本篇文章提供两种方法:
4 x% g; L1 m8 w; \$ c- [4 S' |一种是 :IDLE 接收空闲中断+DMA& H5 s, f- A b
一种是: IDLE 接收空闲中断+RXNE接收数据中断) e/ X+ @; s1 t9 h! B% [
/ x Z4 f6 T: Y都可完成串口数据的收发
4 b) L- i* l3 C' x' U4 F3 L. N" d1 J5 n& Q: |5 m- g. i3 m
知识点介绍:9 ?/ q! I' w: N+ ` w; o
STM32 IDLE 接收空闲中断
, E* [5 O0 p2 e) @/ [/ r5 O功能:/ w. [1 h4 v" f
在使用串口接受字符串时,可以使用空闲中断(IDLEIE置1,即可使能空闲中断),这样在接收完一个字符串,进入空闲状态时(IDLE置1)便会激发一个空闲中断。在中断处理函数,我们可以解析这个字符串。
0 ?( f( {( Z1 b8 i5 ]: p; Y
, `9 C+ N) O9 n5 _接受完一帧数据,触发中断
& W1 n0 b/ b$ f! D7 u) r3 g' \# [) F9 I A+ a" h' _; i' n8 Q
STM32的IDLE的中断产生条件:
, {+ q* I5 h% h6 m. l% Z在串口无数据接收的情况下,不会产生,当清除IDLE标志位后,必须有接收到第一个数据后,才开始触发,一但接收的数据断流,没有接收到数据,即产生IDLE中断
& [3 J- \' E2 \9 f2 G2 w( b5 x$ Z9 G; t8 e6 ^" x
STM32 RXNE接收数据中断
% D# `! ^0 ]9 A5 R" ?+ x功能:8 l' e7 w1 Q4 W
当串口接收到一个bit的数据时,(读取到一个停止位) 便会触发 RXNE接收数据中断
+ E! f/ P$ }9 z* {9 `# F
/ G& ~! h9 s0 z0 L/ _! M2 K接受到一个字节的数据,触发中断
& v$ ^! D$ |6 F3 f# M0 o0 u1 n, h+ v
& U: N0 E6 {; b k* S比如给上位机给单片机一次性发送了8个字节,就会产生8次RXNE中断,1次IDLE中断。- u' ]6 f/ `1 Y$ R& m; S
# {4 P4 n8 l- i2 f) }3 p7 t9 y/ ~串口CR1寄存器" ]9 Z3 ]9 m5 r' I
" g" k! H) K- _5 [9 q
7 O; U1 W* N: @* D7 }5 q
! y& G0 I( U4 j! [7 t
对bit4写1开启IDLE接受空闲中断
" k. g" w% Y' a2 X6 @6 H8 ~,对bit5写1开启RXNE接收数据中断。& {0 d/ s( Z' d# t: H, J `" P
# _) [( D% j; m" Y7 m, ]串口ISR寄存器9 X- T' b4 m. ?1 h$ r# L0 J
0 C2 [* q! F0 @) Q+ [- r
+ H$ O$ t9 C( W; e: H7 I6 C8 H( P5 K! T, }
此寄存器为串口状态查询寄存器4 e& Z5 E( `/ \# t( J
6 C6 ~/ |& k+ H4 {6 g+ n当串口接收到数据时,bit5 RXNE就会自动变成1,当接收完一帧数据后,bit4就会变成1.- O, b0 q6 c3 S, z, a# O
0 H# |' A& e3 f4 t ~# m清除RXNE中断标志位的方法为:. W, q1 u v( Q5 H5 H1 q( w% F
+ C: Y8 a" p, a3 l
只要把接收到的一个字节读出来,就会清除这个中断
- k5 f1 B+ E, F" _; c+ S: X/ f% H; t, Z
在STM32F1 /STM32F4 系列中 清除IDLE中断标志位的方法为:
1 e; |5 p3 e" s0 i2 p* x8 b; N4 {6 \+ I! Q
1.先读SR寄存器,8 `$ Q$ w6 t& f' D8 |
2.再读DR寄存器。. y( B! u0 w& I7 x& I3 T; D3 W
; B4 c+ l0 r4 b9 ]) [
! p! L& z: j/ Y$ r1 f
8 s$ ^6 v8 x- d. o" ~: i
memset()函数3 {7 s8 ^4 `! U% p* m
* E' @3 F4 v6 Q/ L. B3 z. M8 b
- extern void *memset(void *buffer, int c, int count)
复制代码
& h. _( j7 P3 f1 N+ K2 Z# g8 k4 @buffer:为指针或是数组
; }% b1 m: w6 F2 j' mc:是赋给buffer的值7 i. ^, Q1 z, ~
count:是buffer的长度.
: y& Q/ N6 c- T# {& wUSART采用DMA接收时,如何读取当前接收字节数?
6 R( `0 ~3 B' y3 N- #define __HAL_DMA_GET_COUNTER(__HANDLE__) ((__HANDLE__)->Instance->CNDTR);
复制代码 & Y# v& X7 m) z$ u) W
DMA接收时该宏将返回当前接收空间剩余字节
% E5 K8 @: m1 Q9 B V0 L! J; d1 R( A i/ z4 K
实际接受的字节= 预先定义的接收总字节 - __HAL_DMA_GET_COUNTER() c- J/ L) D& |. p
! Q( j4 y9 S7 p; S( T) e/ B6 Q$ `
其本质就是读取NTDR寄存器,DMA通道结构体中定义了NDTR寄存器,读取该寄存器即可得到未传输的数据数呢,/ l& j& n* D7 ?) T
; _0 Q7 @2 t) B0 w( ?$ }" RNTDR寄存器9 ?% `0 D* \. v+ F+ [+ O
3 ~ V$ l" C# N7 k) w
U- l4 y5 F6 ~* V8 q
$ z' S% O0 V$ v% e' Z3 l" p
实现方法:
+ i1 U( f% | y" L5 K0 W两种利用串口IDLE空闲中断的方式接收一帧数据,方法如下:$ Z+ {) L W0 C7 } f2 w# p( h6 ~
) D5 Q' @* O" y' M4 h2 d方法1:实现思路:采用STM32F103的串口1,并配置成空闲中断IDLE模式且使能DMA接收,并同时设置接收缓冲区和初始化DMA。那么初始化完成之后,当外部给单片机发送数据的时候,假设这次接受的数据长度是200个字节,那么在单片机接收到一个字节的时候并不会产生串口中断,而是DMA在后台把数据默默地搬运到你指定的缓冲区数组里面。当整帧数据发送完毕之后串口才会产生一次中断,此时可以利用__HAL_DMA_GET_COUNTER(&hdma_usart1_rx);函数计算出当前DMA接收存储空间剩余字节+ \3 s6 ^" @+ e0 ?3 A
1 Z' ]& _; ]- q5 t
本次的数据接受长度=预先定义的接收总字节-接收存储空间剩余字节- `4 P# V' L O! q' N9 Q
6 Z8 o1 }2 g8 `
比方说 本次串口接受200个字节,
; H9 g# s; p4 [5 O! F/ w2 H6 U0 F- HAL_UART_Receive_DMA(&huart1,rx_buffer,200);//打开DMA接收
复制代码 1 P) O- U+ _+ C. O% K/ q
然后我发送了 Zxiaoxuan 9个字节的数据长度9 I) K9 R" F7 D; w
! P9 W W* b0 P4 a0 @那么此时 GET_COUNTER函数读出来 接收存储空间剩余字节 就是191个字节- [! t& `# L0 f* u
' p* D6 `" C; Q) U9 U实际接受的字节(9) = 预先定义的接收总字节(200) - __HAL_DMA_GET_COUNTER()(191)2 {+ U1 R" ], u
9 = 200-191
3 e. m z4 w3 \3 m4 [+ x9 G3 K/ k1 m: k* i- H5 L6 l- N/ r7 b9 i1 d
应用对象:适用于各种串口相关的通信协议,如:MODBUS,PPI ;还有类似于GPS数据接收解析,串口WIFI的数据接收等,都是很好的应用对象。
( ~ \; ?+ Y* A7 P# A' U1 S. e, h* J( p9 J4 L* u& E7 O
方法2:实现思路:直接利用stm32的RXNE和IDLE中断进行接收不定字节数据。 每次接收到一个字节的数据,触发RXNE中断 将该字节数据存放到数组里,传输完成之后,触发一次IDLE中断,对已经获取到的数据进行处理
& u2 A: S; L8 I& P
. p- C* T$ F* g3 B- X例程1% E% D# ^+ I$ e
本例程功能:
: ]& I+ Z8 N& D7 F+ A9 |
6 P0 z% Z6 c: y0 s- x使用DMA+串口接受空闲中断 实现将接收的数据完整发送到上位机的功能% d3 B$ a8 `' ~( |! G" h0 K+ t
2 B9 [2 u$ s" R3 D, @) `& a; Y
1 ^, O4 G- O" f$ w+ L3 O, d2 X- J
* x3 t6 j# r, [
接收数据的流程:
Q& Y+ M3 }, V: K
. [. d( t) ?+ V" _/ P; n; B& h6 R首先在初始化的时候打开DMA接收,当MCU通过USART接收外部发来的数据时,在进行第①②③步的时候,DMA直接将接收到的数据写入缓存rx_buffer[100] //接收数据缓存数组,程序此时也不会进入接收中断,在软件上无需做任何事情,要在初始化配置的时候设置好配置就可以了。+ z8 d8 b- e2 Q5 Q
6 }( H+ g( ?2 W. s) q数据接收完成的流程:+ x5 q, K; c0 |% E6 f! e
; I( T( m' ~1 r; P) V9 P" P当数据接收完成之后产生接收空闲中断④4 e. g$ C1 I9 n( M
: |' Z$ Q: V* e: z/ h" @
在中断服务函数中做这几件事:
: p* X% S/ w& I: `( |: |" N判断是否为IDLE接受空闲中断
t1 y& L( v- D在中断服务函数中将接收完成标志位置1
9 @/ [* M; w0 b4 B6 r关闭DMA防止在处理数据时候接收数据,产生干扰。4 U% Z4 f& ~; A; b. S; |
计算出接收缓存中的数据长度,清除中断位,/ p+ \+ x; L4 ]/ ^) ?/ |; e
0 E' N& K/ r p; B' x
while循环 主程序流程:
. g% ~0 v+ e2 A: j) X/ ^主程序中检测到接收完成标志被置1/ U. Z, ^/ ]+ D- H. {8 L4 U1 e
进入数据处理程序,现将接收完成标志位置0,! @: a$ t: I6 L' m) D
将接收到的数据重新发送到上位机
0 z* Y) L4 K$ y- D: P& T4 O$ N重新设置DMA下次要接收的数据字节数,使能DMA进入接收数据状态。
: j- M, @' |6 n
z! ^5 h( v5 Y& @ n: C7 j( n例程代码:
7 M) c1 C7 F. R0 b. f, H% vuart.c
) j1 S1 s' K; X! s- volatile uint8_t rx_len = 0; //接收一帧数据的长度 F1 `( x% L2 y
- volatile uint8_t recv_end_flag = 0; //一帧数据接收完成标志
8 @' B$ P: C& k( } - uint8_t rx_buffer[100]={0}; //接收数据缓存数组
复制代码- void MX_USART1_UART_Init(void)
x U" U6 v, n9 x' H- b4 a/ o - {
g' W; C% a/ j" z
! e# J- F3 ?% n) c2 q- huart1.Instance = USART1;9 n0 ~, Y o* ~6 s3 t" ~0 i
- huart1.Init.BaudRate = 115200;( i2 J3 v5 J) G; q3 K9 [( \- X6 v
- huart1.Init.WordLength = UART_WORDLENGTH_8B;
- h1 k- `" g. W5 w0 i - huart1.Init.StopBits = UART_STOPBITS_1;4 K5 w) z/ z! E: B; f
- huart1.Init.Parity = UART_PARITY_NONE;
7 F. q: K7 d$ C: l9 k - huart1.Init.Mode = UART_MODE_TX_RX; C9 K& d6 s' U8 ^
- huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
1 E4 P* E/ O" }. Y. w/ U - huart1.Init.OverSampling = UART_OVERSAMPLING_16;- Q$ B( g6 D% `" y8 u$ o
- if (HAL_UART_Init(&huart1) != HAL_OK)
; A2 R% j: }; p0 C0 h - {7 Z, {) ]+ {1 E5 v# Y1 D& R4 p
- Error_Handler();
1 ]' q2 G! E3 m - } W- Q2 ~0 T0 q' R
- //下方为自己添加的代码) k: T% F' m) b8 t3 R" ^8 N
- __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); //使能IDLE中断% U( C/ u0 f- t3 P) o, p+ L
- . y5 G5 C( i; ~
- //DMA接收函数,此句一定要加,不加接收不到第一次传进来的实数据,是空的,且此时接收到的数据长度为缓存器的数据长度
9 g& |7 s; @" r; t% N% t- D& z - HAL_UART_Receive_DMA(&huart1,rx_buffer,BUFFER_SIZE);
8 b& `" |( ]" j( a, c5 q P( I - ! \+ s" d" R6 }" P
-
; K/ o! |" u5 {# K7 S$ h$ }/ F - }
复制代码
( m3 p( x5 y+ F5 Cuart.h
6 y- j- I3 o3 c1 c% m, I) L1 u- extern UART_HandleTypeDef huart1;
2 ?; I5 C9 L) G9 Y { - extern DMA_HandleTypeDef hdma_usart1_rx;6 X2 W& j. V" ]$ o" v
- extern DMA_HandleTypeDef hdma_usart1_tx;( X' J6 T# n$ g/ p
- /* USER CODE BEGIN Private defines */# {3 k- N! E1 U& x, `6 `
- ) W2 v' ?4 F) V6 L" q
# U& c2 W. }' P9 n9 z+ G$ T% u- #define BUFFER_SIZE 100 # w& E+ l% ^8 \* k3 @6 p
- extern volatile uint8_t rx_len ; //接收一帧数据的长度 c2 k, r; i2 z8 f+ q& \
- extern volatile uint8_t recv_end_flag; //一帧数据接收完成标志
! `- H$ T0 j, S; p% [9 k& G9 f - extern uint8_t rx_buffer[100]; //接收数据缓存数组
复制代码 / L' g- i% B1 j8 K% S
main.c( G: M) }# \4 j. H
) y- y! [" F: h9 @- /*5 t' _. C, }# b2 o \ b# _
- *********************************************************************************************************3 E, z1 w2 |8 x; R) s4 H- b+ x
- * 函 数 名: DMA_Usart_Send
) E1 Z }# ?9 G9 b: k - * 功能说明: 串口发送功能函数# o8 F5 c, E4 f/ v( L
- * 形 参: buf,len/ W& j# l: Z, {7 A! j9 g1 l
- * 返 回 值: 无
5 }# e t/ f( t8 l3 a& U - *********************************************************************************************************
+ G# _ Q/ Z$ _' U: r/ b3 W# S9 h - */
4 X$ K# P+ e8 a. D) I1 g - void DMA_Usart_Send(uint8_t *buf,uint8_t len)//串口发送封装! B% u) n# s0 I& o+ R# n u8 b f
- {
" m: N" q; t% L - if(HAL_UART_Transmit_DMA(&huart1, buf,len)!= HAL_OK) //判断是否发送正常,如果出现异常则进入异常中断函数$ A5 V, G- T' @, e
- {1 }9 v: A. M: |. k7 b. M0 Q) W
- Error_Handler();
3 v1 P6 I0 I) i$ E* n$ E9 Z/ Q - }
8 y% h S5 ?( {4 X2 p" R% v - ; }5 m8 t0 Q- q2 V& _
- }
0 b! I- B% Z; E0 ]0 } - 0 U* M$ _$ v% }; [# t- a i2 b) ^5 {5 b
% H+ t" t% m8 L: ~0 O$ }4 @1 e4 }- 3 D' X z1 z+ m6 d% W+ n
- /*
$ H% _! g: @; ~/ l3 H - *********************************************************************************************************: J/ G n! V! |3 M' c, ? L
- * 函 数 名: DMA_Usart1_Read5 t; Y1 @3 M0 o, l
- * 功能说明: 串口接收功能函数! K `) b4 K( j/ ]/ Y5 l# A8 f5 S3 [
- * 形 参: Data,len: P& L! L! f% |4 a2 Q; z
- * 返 回 值: 无
* [6 K9 z0 U s+ k3 H& O- c+ n - *********************************************************************************************************
8 Q9 G( F2 L- R: J- w - */* M6 }# ~" | K# N' L5 L
- void DMA_Usart1_Read(uint8_t *Data,uint8_t len)//串口接收封装
. e$ e: o# r+ Z# ]$ F1 t$ r - {
: ^5 c: \8 q0 v) h - HAL_UART_Receive_DMA(&huart1,Data,len);//重新打开DMA接收# L Z3 Y3 Q/ n
- }
. d/ i1 y' \! N$ T9 W0 l$ m
复制代码 ! R: Y6 r$ V7 t; K/ j4 d
while循环9 o- y4 l! D$ U {
0 a! Y: _. P3 T. w, C7 R
- while (1)
9 p, S# Y( }$ g- k" e) \& i - {
( Q |( o0 q; l* s7 t( T - /* USER CODE END WHILE */
5 ]: D4 B, }6 W1 d0 T
9 f/ t( x4 { f+ d% m6 Y- /* USER CODE BEGIN 3 */7 }" _( r* k: N( V2 f0 h: D1 q
- if(recv_end_flag == 1) //接收完成标志
% b4 P3 ?$ P; j/ r* v2 ~, d) D - {. D% m4 s! I1 V/ |1 |
- " ~/ B; u3 ^7 ^: }0 [& m
-
3 Z9 d2 o X+ m R; Q# _( z - DMA_Usart_Send(rx_buffer, rx_len);, _; H1 _. `: r' T* h- @2 U
- rx_len = 0;//清除计数
. B! p0 I6 y* c. H0 }4 ]% B - recv_end_flag = 0;//清除接收结束标志位3 A. D2 J7 V3 |. n9 t" g
- // for(uint8_t i=0;i<rx_len;i++)- q8 Z7 l$ a" I/ V6 N
- // {8 @+ _. r6 X+ E# F4 s
- // rx_buffer<i>=0;//清接收缓存</i>5 t: U9 Z+ b% ?. s: U
- // }
7 m" V& S- [$ U( _- | - memset(rx_buffer,0,rx_len);. q' f" v: c% c: G; _: W7 y( l$ x
- }+ y5 d# ], E" k
- HAL_UART_Receive_DMA(&huart1,rx_buffer,BUFFER_SIZE);//重新打开DMA接收
) s/ @/ I( b1 J. P) c - }
( j$ \, o$ m+ W o
复制代码
+ K% _; c: v/ u" j; v- C9 Hstm32f1xx_it.c中8 H9 h7 o% L$ E6 v ~# k* s
1 X m B" P0 j8 D h& d2 c
- #include "usart.h"
) b) H" ]- c9 C5 u# D- E |1 f
, j( }- c6 O' b! P4 c: z9 S8 V- void USART1_IRQHandler(void)
: e* J% v. S. H4 `# ~# i$ w2 E( K - {. t$ m6 C+ `0 f- D* g6 t
- uint32_t tmp_flag = 0;
$ a; n- T. r% ` - uint32_t temp;8 F+ _6 w3 V% m1 r
- tmp_flag =__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE); //获取IDLE标志位( E# o, M# E, \; @$ g$ @( K
- if((tmp_flag != RESET))//idle标志被置位( a3 }/ i1 Q% ~1 [( g; H2 V
- {
; S% D# Y4 M6 | - __HAL_UART_CLEAR_IDLEFLAG(&huart1);//清除标志位
& q! G- Z% A4 Q - //temp = huart1.Instance->SR; //清除状态寄存器SR,读取SR寄存器可以实现清除SR寄存器的功能0 O# c# P" D; Y$ }( r4 k2 F; f B
- //temp = huart1.Instance->DR; //读取数据寄存器中的数据6 _) Y2 `. W, t9 |+ p6 Z6 |$ C" z
- //这两句和上面那句等效9 k: W* F% E# C% f" b. g
- HAL_UART_DMAStop(&huart1); // 停止DMA传输,防止
7 B F, x& C: J7 Q - temp = __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);// 获取DMA中未传输的数据个数 0 U; _/ K4 t& h- b( s7 W% p
- //temp = hdma_usart1_rx.Instance->NDTR;// 读取NDTR寄存器,获取DMA中未传输的数据个数,
$ ?0 z9 |' M: k - rx_len = BUFFER_SIZE - temp; //总计数减去未传输的数据个数,得到已经接收的数据个数
5 o- A* \4 a2 j4 Y# Q - recv_end_flag = 1; // 接受完成标志位置1 6 Q1 `4 C: o9 M3 \8 [. ?
- }
* n ^7 W8 K" K9 f0 { - HAL_UART_IRQHandler(&huart1);
4 I0 U) m% V1 u% h5 \. x - 0 k/ N7 A( f: n9 R: [
- }
5 C7 K/ p7 [# L$ y% A B
复制代码 . r/ r" G! C. Q7 V0 o6 @) w
注释详解:
; s9 h" ^- g, l3 B m2 ?. d: m2 R注释1:6 R4 r, i/ c+ Z) C* c3 J
- temp = UartHandle.Instance->SR; //清除状态寄存器SR,读取SR寄存器可以实现清除SR寄存器的功能
4 D/ K+ g1 t. h3 m' R - temp = UartHandle.Instance->DR; //读取数据寄存器中的数据
复制代码
( s% |& B% Z1 [* y( o( j- N/ g" J, p9 X4 l这两句被屏蔽的原因是它们实现的功能和这下面串口IDLE状态寄存器SR标志位清零的宏定义实现的功能是一样的:
* G* V4 N' I' V& ~6 M$ \$ q' z
2 L$ \6 Y0 h8 ^; L; m__HAL_UART_CLEAR_IDLEFLAG(&huart1);//清除标志位
( m& `/ V/ e3 h/ M0 {6 T1 k! A6 Y) N6 [3 U+ {& g
我们可以点击这个宏定义进去看看,它实现的功能和上面两句是一样的:
& h/ w) Y4 }7 M+ o+ ?; i# f( c1 m
#define __HAL_UART_CLEAR_IDLEFLAG(HANDLE) __HAL_UART_CLEAR_PEFLAG(HANDLE)
# C Z8 _3 m/ `3 g* e- B8 r8 [* C4 |8 J& f2 \
- u) l+ I F4 j) l% E5 b4 e: o& ]- \8 c! l) b0 S( S
注释2:
( r3 ^5 }$ z/ ^# h3 n, R2 H3 O/ g4 ^% A
- temp = hdma_usart1_rx.Instance->NDTR;// 获取DMA中未传输的数据个数,NDTR寄存器分析见下面
复制代码
8 {, @) I% G2 `- O4 w同理, 这句被屏蔽的原因是因为他和上面的__HAL_DMA_GET_COUNTER的作用也是一样的,都可以获取DMA中未传输的数据个数7 t+ A+ k! w4 C; K2 |
2 M# E6 K2 _2 D4 O0 F
( t4 M+ O. O* d$ _9 t
& |3 W2 T3 t" b0 ]: {. l/ Z测试正常:
; f1 l6 ?9 Z2 O! {5 W ]
1 Q9 g' U0 J5 Z
: j) k6 K2 w( L/ e
* d. ^& q+ E' O2 U( k, r此程序可以进行接收不定长的数据帧,不需像RXNE每次接收到一个字节就进一次中断。
9 Q, b5 K3 a5 l$ {, w% n) X7 M# w同时开启DMA传输速率也会加快4 i8 ^2 {! ]8 N& h( e# m% W
例程2, i. U: Y9 W) c5 T& U! A0 s. W* \# r# ]
UART中断使能函数
7 X/ l! s; a8 E7 r: W* s$ H- __HAL_UART_ENABLE_IT(__HANDLE__, __INTERRUPT__)
复制代码 " w6 U$ L6 r8 k- M0 x0 F
功能: 该函数的作用是使能对应的UART中断1 Y5 J) f8 w$ B a& T5 F$ n
0 z3 x D6 `4 W0 w7 r: z; Y; |$ |/ P) C* A在stm32f1xx_hal_uart.h中被宏定义
: I4 S* C, K% p3 }4 X/ d/ ?( [
/ O& z+ c! H+ e6 d' p) `$ k f
K6 F( D$ s: J6 ^5 a `
% U) l/ x( x" c' J' h+ J \0 O可以使能的中断一共有这些:
" m. V" P+ A* \0 b) a8 [! Z
/ F; A7 C6 ^! U; v. @
4 @1 s" W3 X6 K8 O& t2 J
% I1 f8 n- g5 U! b/ P- ~: z我们现在所用到的为:% A4 o7 ^/ C! l* O5 |, `
- * @arg UART_IT_RXNE: Receive Data register not empty interrupt
3 P( w" v ~; z - * @arg UART_IT_IDLE: Idle line detection interrupt
复制代码
7 G- |* V' O/ e/ F; R0 D8 LRXNE接收数据中断
+ q1 \6 b0 y3 |* E( p2 h* CIDLE 接收空闲中断% l1 Q* w2 I' S$ i+ Q% _) m+ ~
* K1 {' k7 h6 i& Q: d
. r% m- R' O7 y8 P! _6 ]5 z# N, N! q+ c& W
本例程功能:
, N2 D5 s# |1 T$ ~+ z
. o ^: N+ h7 P2 `使用RXNE接收数据中断+IDLE串口接受空闲中断 实现将接收的数据完整发送到上位机的功能
6 B" P( Y; e' I& Q2 o c4 z$ o K! W
接收数据的流程:& v2 n+ V3 ^1 w' p! L
% X' {7 L5 k2 K2 X% l; M
首先在初始化的时候打开DMA接收,当MCU通过USART接收外部发来的数据时,在进行第①②③步的时候,DMA直接将接收到的数据写入缓存rx_buffer[100] //接收数据缓存数组,同样初始化配置的时候设置好配置就可以了。# m1 C2 @% l+ l, f
3 R: q! j' l8 L接收到一个字节数据:
& P/ {% I' S8 \9 i1 w8 h5 `接收到一个字节的数据之后,便会进入RXNE接收数据中断,) m+ F+ r4 Y a }; Y1 O ~
3 t/ `( ?3 u r: c
接受完一帧数据之后,便会进入IDLE 接收空闲中断 W3 p. a: m; N! K
" G6 I6 t. O: ? \
在uart.c中添加中断函数! N: D+ |9 m+ ]5 Y# m' t5 ?
& w4 O4 b$ l7 [( R$ W% d2 u) u- w- void MX_USART1_UART_Init(void)
0 m4 f1 e: C. C - {9 i/ W! ]2 w$ u
- 6 ^/ I5 t# Q3 Z9 b+ x J J
- huart1.Instance = USART1;, F( s+ K6 E6 {& Y( C
- huart1.Init.BaudRate = 115200;
" R. K3 h3 P' O3 o% c8 ` - huart1.Init.WordLength = UART_WORDLENGTH_8B;4 U1 `5 E6 _. Z3 s0 E! Z( b
- huart1.Init.StopBits = UART_STOPBITS_1;
5 N- n1 d. | c6 D8 z4 @ - huart1.Init.Parity = UART_PARITY_NONE;: h4 g# X" Q- q! X- L# f
- huart1.Init.Mode = UART_MODE_TX_RX;- O+ T; q6 f0 n* E* v U
- huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
R. {% T e+ e d$ s - huart1.Init.OverSampling = UART_OVERSAMPLING_16;
+ z) \% i2 [4 q2 Z - if (HAL_UART_Init(&huart1) != HAL_OK)
! C/ k& e) y% o4 V% f1 a4 @% B! `$ K6 s - {
+ I# R% D2 V% R& x1 d - Error_Handler();
+ s( \5 ^3 c/ }' d8 M/ y& M - }
0 x6 [; V) I- f, v" v; T$ B - # y: w2 X! j; R/ Z9 A- c
- __HAL_UART_ENABLE_IT(&huart1, UART_IT_RXNE); //使能IDLE中断
0 @& {# K% h9 u& M3 G5 b" w Q - __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); //使能IDLE中断
- U2 q% L0 {0 J9 r4 k9 G" U
. b' A E( h+ q3 d& L- //DMA接收函数,此句一定要加,不加接收不到第一次传进来的实数据,是空的,且此时接收到的数据长度为缓存器的数据长度+ b6 ^- y) ?$ n: t8 G+ M& O
- HAL_UART_Receive_DMA(&huart1,rx_buffer,BUFFER_SIZE);* Y1 m0 R% a5 W. ?, l- a
7 e C9 I. y5 _- ( `' U# }# z2 I h! n, z2 ?
- }; e* a/ q+ e! K+ L5 i: t
复制代码 其核心代码就是下面的两句
( d7 y& b. i R" Z- g+ s2 F9 _$ y5 u) F2 @: Q- h* C- B' S7 I" f9 i6 f q1 l1 Q
/ N! `' L/ V& i5 z) M5 X
8 z# z$ F4 X" b! ^& d第一条语句用来判断是否接收到1个字节,第二条语句用来判断是否接收到1帧数据. R$ r+ ~7 `. M" ?! a4 x1 s
4 K" L% [- y* y ]9 Z _% f- if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE)!= RESET) //如果接收到了一个字节的数据. R$ C: `# a# P3 k& l
- {
2 f# O' S% U0 I5 x* }9 }6 { - HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_5); //反转LED+ v! c& M+ J! W7 O" M0 E
- }+ B C- ~5 S$ _
- * J% t8 c/ e& ^
- if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE)!= RESET)//如果接受到了一帧数据' _. X! j% Z \; \/ _! i7 L
- { + \2 P4 i) }& g# w; B
- __HAL_UART_CLEAR_IDLEFLAG(&huart1);//清除标志位
复制代码 $ r( ]% K6 ^% X0 T u) }8 l8 v
其余部分于上方例程1基本相同( H0 I6 B% R0 }8 O7 @9 {* p
% I- w2 p. z- K2 ]3 _8 T! I
本次测试是检测接收到一个字节就反转LED
* \5 g! s0 O# A1 Z9 @3 ]( S经测试,历程正常:
" z( @! f% _5 Y- u9 _5 u* h
D: @- A6 ]% U5 ` " c, t9 R0 ]# U" ?0 E) \# {' ~
# U% j" k% o, A& R8 }4 J! T$ N! S! `+ X8 L
) E0 J V# u3 x& N) e5 v7 `
|