本篇文章提供两种方法:
6 U# E; L) ~# \" S# O. ]3 J一种是 :IDLE 接收空闲中断+DMA) k- w9 |: ^. d: {
一种是: IDLE 接收空闲中断+RXNE接收数据中断) s$ C* q: K* h- }* c4 l- ~
. z6 V \/ C8 r# @' o/ j- i
都可完成串口数据的收发/ b7 g8 ?4 @. b0 D" l
5 z2 G+ ^3 c/ l! Y1 g( K8 O' P( j知识点介绍:& C: g- U9 b" r6 g8 x
STM32 IDLE 接收空闲中断! J5 e2 y% c: r E2 x
功能:
, [1 R+ X- c- e5 `9 }在使用串口接受字符串时,可以使用空闲中断(IDLEIE置1,即可使能空闲中断),这样在接收完一个字符串,进入空闲状态时(IDLE置1)便会激发一个空闲中断。在中断处理函数,我们可以解析这个字符串。
; e; I- k# c/ h3 b# J3 m ?
+ | A# I+ `- C& b6 b# [接受完一帧数据,触发中断
7 i8 \* W8 x. ^7 l7 _! Y7 @
1 f3 t1 u6 B% Y6 j4 ASTM32的IDLE的中断产生条件:
9 P, x2 |6 S5 m |( B在串口无数据接收的情况下,不会产生,当清除IDLE标志位后,必须有接收到第一个数据后,才开始触发,一但接收的数据断流,没有接收到数据,即产生IDLE中断$ W/ h o- T$ x# O: Y( t
: k/ Q3 V1 I+ {
STM32 RXNE接收数据中断
3 {% [$ p2 O6 J" q) D功能:* ]: E( R5 ]: G& S+ X
当串口接收到一个bit的数据时,(读取到一个停止位) 便会触发 RXNE接收数据中断5 {7 {: P1 Z" ^: r* j
! s& k9 \% z$ C' h! c2 Q
接受到一个字节的数据,触发中断
' F X/ }% }: @, o6 g2 x8 g% G z2 H( G- Y" i+ i# |& d- Q
比如给上位机给单片机一次性发送了8个字节,就会产生8次RXNE中断,1次IDLE中断。; i8 V! H& h- d1 S* E4 J8 H
]1 ^6 X) j% a* D- Q
串口CR1寄存器; ^9 c* ?5 y* a3 W i
5 r+ O6 A e" t0 y
2 U" X9 d5 |0 o& d
) W5 I* F6 Y. j: E
对bit4写1开启IDLE接受空闲中断
5 }: z% v N% }/ D4 ]( ]8 u,对bit5写1开启RXNE接收数据中断。9 y5 W! @+ _0 g
7 X$ t7 C" C3 s& v- M串口ISR寄存器1 `6 Z* S; b {8 X/ C/ [8 K; U% |
" `" J: W: u# ]8 @
$ u% l( K/ A; S' y: r
, }+ D6 ]7 n! ^( T" I, U
此寄存器为串口状态查询寄存器$ u, ]8 V9 K6 R4 N8 e0 }2 Q
3 y. I1 E" `0 `当串口接收到数据时,bit5 RXNE就会自动变成1,当接收完一帧数据后,bit4就会变成1.5 Q. H5 d& S4 C% P6 G6 o
( u! |+ Z8 J9 x2 y/ T7 k! ?( E
清除RXNE中断标志位的方法为:0 Z- e# f3 ^. f( L+ h
0 c% `6 \& d: e. W
只要把接收到的一个字节读出来,就会清除这个中断! {, g# R% K: y+ E
9 i- J" } Y6 D( {
在STM32F1 /STM32F4 系列中 清除IDLE中断标志位的方法为:4 u: D @" y+ k+ M. i
% {# k; x* u% ?* _# ?* P1.先读SR寄存器,
' T8 {3 J( [4 F9 }: w2.再读DR寄存器。. ^3 j2 T" ?0 y9 x
) I! L/ B; @. T. U% o
: {2 b7 i( v l/ p5 l$ G0 V
" a2 u) {& S& k: p) D5 d
memset()函数( P) V: [8 W( D: _5 _6 N
3 j( P5 {9 ?) E9 A5 d6 w0 W9 G. n
- extern void *memset(void *buffer, int c, int count)
复制代码
% |, Z9 c) q' Xbuffer:为指针或是数组
! k$ r0 L0 ?3 |c:是赋给buffer的值
$ R* f3 H$ y$ z5 E8 Gcount:是buffer的长度.
' F- q0 P& N0 z# sUSART采用DMA接收时,如何读取当前接收字节数?9 e# Y F, R+ P2 f
- #define __HAL_DMA_GET_COUNTER(__HANDLE__) ((__HANDLE__)->Instance->CNDTR);
复制代码
3 \0 Y& A4 Q5 V2 S( hDMA接收时该宏将返回当前接收空间剩余字节
# `( d8 Q u& k V, b- V9 Z! o1 ^3 _+ b* e: R1 u; _6 z
实际接受的字节= 预先定义的接收总字节 - __HAL_DMA_GET_COUNTER()7 X' w: m* Y8 z% f& I2 G: ~ q- E/ D
0 i! a6 J3 Y/ a2 ~- C
其本质就是读取NTDR寄存器,DMA通道结构体中定义了NDTR寄存器,读取该寄存器即可得到未传输的数据数呢,9 s) L4 t/ w% r" M0 S8 s
4 u$ W: x. @* h" m' Q4 C% sNTDR寄存器
0 U: l& Z8 E; {2 w5 H+ M n4 T# c
% g4 f* K! K1 R, e6 O
* {( W2 b$ b, x" u$ T- S5 V
; M) |+ O) F6 v9 w9 ]实现方法: _4 H T1 Y( h$ v( F
两种利用串口IDLE空闲中断的方式接收一帧数据,方法如下:
5 [4 U) O% z3 {4 j! e* o4 p( Z. U# M& m/ @0 ^
方法1:实现思路:采用STM32F103的串口1,并配置成空闲中断IDLE模式且使能DMA接收,并同时设置接收缓冲区和初始化DMA。那么初始化完成之后,当外部给单片机发送数据的时候,假设这次接受的数据长度是200个字节,那么在单片机接收到一个字节的时候并不会产生串口中断,而是DMA在后台把数据默默地搬运到你指定的缓冲区数组里面。当整帧数据发送完毕之后串口才会产生一次中断,此时可以利用__HAL_DMA_GET_COUNTER(&hdma_usart1_rx);函数计算出当前DMA接收存储空间剩余字节% \" E2 B$ c/ V& P: M$ }2 B
5 m; k5 K1 R# l本次的数据接受长度=预先定义的接收总字节-接收存储空间剩余字节* m$ [7 s# o! b: H
, C, L ^, s, l k4 J+ ?, G
比方说 本次串口接受200个字节,
7 W7 Z' q* n6 f! |9 q4 ~- HAL_UART_Receive_DMA(&huart1,rx_buffer,200);//打开DMA接收
复制代码
$ L+ }" F( V: y9 a然后我发送了 Zxiaoxuan 9个字节的数据长度9 E! h4 c: h3 _$ O9 }! f
& d$ o; i) x3 c4 l1 U! d
那么此时 GET_COUNTER函数读出来 接收存储空间剩余字节 就是191个字节
3 p- Q% Z( l% Z! B7 D1 z5 k9 D: w2 e) S; W
实际接受的字节(9) = 预先定义的接收总字节(200) - __HAL_DMA_GET_COUNTER()(191) F5 o& O' l% P, R
9 = 200-191% t" c' L3 x3 T0 O
% F& t: }5 r# D- {/ @0 c! Z) ^: @
应用对象:适用于各种串口相关的通信协议,如:MODBUS,PPI ;还有类似于GPS数据接收解析,串口WIFI的数据接收等,都是很好的应用对象。
/ E) J) ^" `8 @, P( e
9 J& @9 w P% c; p方法2:实现思路:直接利用stm32的RXNE和IDLE中断进行接收不定字节数据。 每次接收到一个字节的数据,触发RXNE中断 将该字节数据存放到数组里,传输完成之后,触发一次IDLE中断,对已经获取到的数据进行处理9 {$ Y3 D2 s5 U; }' ^) H; K5 U
0 d$ `# b" G, U$ s# X, J
例程1
: _2 J4 {/ x- D) n; M" Y本例程功能:
/ I4 E# w0 C# ^* z- O) W
/ L* W: d1 F" l* z. o使用DMA+串口接受空闲中断 实现将接收的数据完整发送到上位机的功能' b) r. x( q8 ^. ~1 n) W5 r
6 [: A* O! |. j- w( M
0 B: R4 r: q8 }6 M
- A( ?& G1 {0 S, b, J2 Y, e" B" e/ A; G接收数据的流程:
' L4 [/ {) V. u8 f: D0 R) o7 v
; T+ I" R5 _) B- T首先在初始化的时候打开DMA接收,当MCU通过USART接收外部发来的数据时,在进行第①②③步的时候,DMA直接将接收到的数据写入缓存rx_buffer[100] //接收数据缓存数组,程序此时也不会进入接收中断,在软件上无需做任何事情,要在初始化配置的时候设置好配置就可以了。
+ o0 A6 X/ f& M' D! B2 s
, U7 \* V& e, n; w+ @$ P数据接收完成的流程:
1 S" K* k1 Q; O( K. D0 y/ h
3 v/ v/ a+ A0 y. [3 l当数据接收完成之后产生接收空闲中断④( R! a' R( Q. }) p5 w
+ @! p5 \. i# m0 f$ `: i. p在中断服务函数中做这几件事:9 ?2 _! g2 ` i" F3 J
判断是否为IDLE接受空闲中断
W0 B9 P W- H( Z- N3 @- d在中断服务函数中将接收完成标志位置19 \% S" ? }$ l/ R
关闭DMA防止在处理数据时候接收数据,产生干扰。
# J- E3 j0 [/ e( m计算出接收缓存中的数据长度,清除中断位,# y9 B( ~' Z { l
/ {' g$ i; F( g
while循环 主程序流程:( f! W3 A$ |: n
主程序中检测到接收完成标志被置1 l+ X: e s7 E$ y: P
进入数据处理程序,现将接收完成标志位置0,2 N* r! C+ F, C$ h2 q
将接收到的数据重新发送到上位机3 Z1 u4 ^4 |! U9 `5 j& }, E) `
重新设置DMA下次要接收的数据字节数,使能DMA进入接收数据状态。9 B8 `3 ~% U8 o
7 x8 u a! j' n
例程代码:! w1 r+ c* H) ~, {: B
uart.c5 e. c+ s, f9 R+ i( x3 @
- volatile uint8_t rx_len = 0; //接收一帧数据的长度
/ f# e/ c; K8 H# D8 p. o! t5 {4 b - volatile uint8_t recv_end_flag = 0; //一帧数据接收完成标志
X! }& k: |3 r5 d( B# | - uint8_t rx_buffer[100]={0}; //接收数据缓存数组
复制代码- void MX_USART1_UART_Init(void)
4 s, ~; j2 p5 F- M+ q) x! x - {6 C' I9 Y- o0 `$ R1 M
- . m$ s: ] k9 r; s# d+ L
- huart1.Instance = USART1;
+ P/ ^, @& d9 \; T1 L - huart1.Init.BaudRate = 115200;
& n( q6 m0 a4 e$ s4 ?% W - huart1.Init.WordLength = UART_WORDLENGTH_8B; G! L1 i8 L$ S/ P
- huart1.Init.StopBits = UART_STOPBITS_1;
! S9 ^' t4 ]1 F& e/ ~ - huart1.Init.Parity = UART_PARITY_NONE;
6 z! e4 b y# D$ f1 j6 i5 S( { - huart1.Init.Mode = UART_MODE_TX_RX;
8 x0 K% L# ]- ^6 x+ I: _/ H - huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;3 `% u6 A* P4 Q, ~4 {5 P& x2 q
- huart1.Init.OverSampling = UART_OVERSAMPLING_16;
7 [" R+ X/ n' P# p; G - if (HAL_UART_Init(&huart1) != HAL_OK)2 _# O* Q2 Z. O# \: P0 E$ f8 e
- {
. V- i7 \$ J/ h Z$ s/ X5 P2 ]" W7 P - Error_Handler();
. i; N$ _5 R: u9 t& T' I - }
8 {0 G. y5 K. S" J5 \ - //下方为自己添加的代码
. J* q2 B1 a0 g - __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); //使能IDLE中断
2 U8 b2 T5 y( o$ ?% d* J8 } - 6 {; x3 z) ^" s1 v
- //DMA接收函数,此句一定要加,不加接收不到第一次传进来的实数据,是空的,且此时接收到的数据长度为缓存器的数据长度+ n7 f% C( E( I" ]
- HAL_UART_Receive_DMA(&huart1,rx_buffer,BUFFER_SIZE); i, x& C8 e1 W3 M1 |
! f0 Y7 {0 R2 n, K! K# k- + i* L( H- t$ o
- }
复制代码 6 e0 i% p; s6 L4 y/ o$ Z
uart.h' y ^8 Q! z5 j$ y7 ]
- extern UART_HandleTypeDef huart1;! T) Y. l0 c7 \* ~6 D1 y5 k
- extern DMA_HandleTypeDef hdma_usart1_rx;- C* P. W, P$ T$ r0 N
- extern DMA_HandleTypeDef hdma_usart1_tx;
) [) F2 b4 @8 N6 G# a - /* USER CODE BEGIN Private defines */! [% \ o+ Y8 N8 z1 W
. ~: m7 r; I" y' B$ p! d W
9 M7 j9 u+ b. a$ }- #define BUFFER_SIZE 100
$ `5 J# H" s) A% ~4 ~+ X: G' I6 q - extern volatile uint8_t rx_len ; //接收一帧数据的长度: W; ~& }5 ^& ]% `( h) S0 Z
- extern volatile uint8_t recv_end_flag; //一帧数据接收完成标志
, H2 u/ y5 M" D, e: L$ `* Y - extern uint8_t rx_buffer[100]; //接收数据缓存数组
复制代码
E$ U7 B i% Z; nmain.c. e, w. D- B* ^; [: v# N
' `0 e4 `7 ?% i ^5 K; e# G ?
- /*
& p% H) b- `- g* ^- u6 v - *********************************************************************************************************5 w/ f) ?4 j \, k$ s9 R
- * 函 数 名: DMA_Usart_Send
& F) X& _3 j. B- ?/ O2 Q3 H - * 功能说明: 串口发送功能函数
6 @+ M( W6 t1 \$ n. y - * 形 参: buf,len
& V. k G3 [- u' x, c, ~ - * 返 回 值: 无/ y3 `+ z( A5 f @( z, M
- *********************************************************************************************************
5 W: _* j0 I7 C/ N9 L5 K - */- `6 Z4 @, a! I2 M: {" _8 q- E
- void DMA_Usart_Send(uint8_t *buf,uint8_t len)//串口发送封装
_5 l1 g* j9 Y8 C7 K9 {- w - {0 W, ^3 {( M0 v+ m! A6 X. L
- if(HAL_UART_Transmit_DMA(&huart1, buf,len)!= HAL_OK) //判断是否发送正常,如果出现异常则进入异常中断函数/ }; @! c8 i4 m
- {
- W6 t3 |+ s5 h! T/ F - Error_Handler();
" b2 B' X( v8 `7 j" m* P& v% ?3 ` - }
! A# E: t6 w" a5 M ]
5 U n7 ^- ]) \6 Z- }7 \8 D# b) r2 |& y
) N2 n# u$ n' Q+ c! d9 c3 ^- 5 [" z/ [6 R2 p
- / ?: l1 Q; K8 w. t0 x2 ^
- /*
( r3 _3 [* l1 j% l. G7 B - *********************************************************************************************************
0 P" I& o" ?/ [% D0 v* T: x+ r) t - * 函 数 名: DMA_Usart1_Read& {$ c9 M ^ a/ ^# m
- * 功能说明: 串口接收功能函数
! }" n% [/ d4 P7 K0 Z: u; B - * 形 参: Data,len
7 j4 o/ x$ N7 Z7 Y. A( Q9 E8 B - * 返 回 值: 无
3 W0 v7 N# j, s! n. m+ b - *********************************************************************************************************
5 f( f7 k8 O5 k3 {" x7 E/ J - */9 p, ]* G5 n' p0 I0 F) Z& ]
- void DMA_Usart1_Read(uint8_t *Data,uint8_t len)//串口接收封装
! g% a$ ^! r; S$ f; L" n8 | - {
8 i3 G3 \) d% K8 Q6 Q2 N0 | - HAL_UART_Receive_DMA(&huart1,Data,len);//重新打开DMA接收
* j) w) s# Q+ Z/ Y - }
; X ?1 A# I/ |0 P: Y9 w1 \
复制代码
2 D1 d$ y( G4 P& [- C4 N3 Xwhile循环
9 |% L$ e0 V x2 ~# O4 F% b! R* \+ \ u
- while (1)
% ?7 h: J* K* P# O - {5 ^7 D- V) D B" [8 D0 L4 Q; H
- /* USER CODE END WHILE */4 U8 n2 f- f# `
- " y* G: d, d: y, U/ o% s
- /* USER CODE BEGIN 3 */
1 Z' u+ J5 m6 @ - if(recv_end_flag == 1) //接收完成标志
" s+ v& I0 h% M8 k( X - {
4 @2 x# N- X& N% U - * d7 h( M" g# b( e* h, A
- ! R5 C! R5 o, \+ T" p
- DMA_Usart_Send(rx_buffer, rx_len);
4 X) z) i* y1 [% q8 f9 I, A - rx_len = 0;//清除计数
$ V; `1 m" E9 L! d! p! O# q8 a Q - recv_end_flag = 0;//清除接收结束标志位
! k$ o4 W* _) M3 ]3 [6 X - // for(uint8_t i=0;i<rx_len;i++)
0 X' p6 q- M7 C! J1 e+ U8 |. G: {$ z* f - // {9 U/ z! v6 Q" R( B" z0 ^
- // rx_buffer<i>=0;//清接收缓存</i>
& ?' n h' q2 }0 \, u2 \+ i - // }
* ]7 ^2 }6 o8 N$ F, [6 k - memset(rx_buffer,0,rx_len);* x. W6 \$ a* b# r+ k3 n! S2 V+ ?
- }
2 o" q7 n% q; u9 F+ W! X - HAL_UART_Receive_DMA(&huart1,rx_buffer,BUFFER_SIZE);//重新打开DMA接收
s7 z! y. k+ }/ P - }9 {6 R2 L# a9 l3 x) ?
复制代码
) p: ]' t5 V4 l5 j/ `: M( q+ Mstm32f1xx_it.c中
. I9 s8 S5 o* S* V+ u& n; H/ J* _$ N% ~; w+ F/ y" z8 b( p3 G
- #include "usart.h"0 ^# C( z% P: I# g& N4 g) a
3 \, y) Q, z7 o, e3 T1 f5 W6 ?- void USART1_IRQHandler(void)9 g e$ l9 M' k. Y ]: d4 h6 Z! v9 w
- {8 A" p% M6 m- G. e
- uint32_t tmp_flag = 0;- z7 w8 [- i) i6 l; u$ w% E! U6 ^
- uint32_t temp;
; B0 k' r: I1 l5 {# y/ q - tmp_flag =__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE); //获取IDLE标志位
) U+ F# Z. O$ _" V O D - if((tmp_flag != RESET))//idle标志被置位8 A% e- `( O$ T* E5 X# {; _
- {
; [' X6 V4 v' T9 p - __HAL_UART_CLEAR_IDLEFLAG(&huart1);//清除标志位( O7 U) \/ ]' m& q
- //temp = huart1.Instance->SR; //清除状态寄存器SR,读取SR寄存器可以实现清除SR寄存器的功能
" _5 N. i, @% ] - //temp = huart1.Instance->DR; //读取数据寄存器中的数据6 k+ R# x8 l# f5 p$ \6 f `
- //这两句和上面那句等效( q! O& h& p3 v. J9 M- C0 o" N& Q: [+ D
- HAL_UART_DMAStop(&huart1); // 停止DMA传输,防止" F& y7 b. a: F0 ^
- temp = __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);// 获取DMA中未传输的数据个数
+ G. F. o, e6 d; S - //temp = hdma_usart1_rx.Instance->NDTR;// 读取NDTR寄存器,获取DMA中未传输的数据个数, k8 u; E* B: W; }; Z9 X1 _' f
- rx_len = BUFFER_SIZE - temp; //总计数减去未传输的数据个数,得到已经接收的数据个数 j: j5 ~% `1 i" G
- recv_end_flag = 1; // 接受完成标志位置1 / ?4 x# Z/ n0 j$ `2 R
- }
) a7 p4 t0 {4 L6 c" r - HAL_UART_IRQHandler(&huart1);9 b V1 V1 G$ G" X0 L' x* D
. h8 I9 P [. @0 S8 X; D0 t. T- }
0 Q0 Q# O9 F; i' g- m4 t
复制代码
3 N+ J0 v5 U- V/ \注释详解: ~( O4 H+ b' p2 T! X, F
注释1:
' ], P5 ?5 V! T' I' t- i, H, l- temp = UartHandle.Instance->SR; //清除状态寄存器SR,读取SR寄存器可以实现清除SR寄存器的功能
& O) O/ G9 l; I+ ~3 M - temp = UartHandle.Instance->DR; //读取数据寄存器中的数据
复制代码
9 Z. }) ^' Y- G# \ w) E. U这两句被屏蔽的原因是它们实现的功能和这下面串口IDLE状态寄存器SR标志位清零的宏定义实现的功能是一样的:8 o& Z4 s* p( t( T
( E- h) {; D) Y1 P1 y z5 W
__HAL_UART_CLEAR_IDLEFLAG(&huart1);//清除标志位 t1 v3 N& N* `$ h1 Q
. P( k$ ^% s: m我们可以点击这个宏定义进去看看,它实现的功能和上面两句是一样的:
# C) @! B8 `$ Q: C. J& k5 @1 _1 I. J* U: o0 P+ }1 B4 a
#define __HAL_UART_CLEAR_IDLEFLAG(HANDLE) __HAL_UART_CLEAR_PEFLAG(HANDLE)
5 T* A% k+ P! W% K4 L. ]& C, M9 V$ @! v
; y6 \2 X. K9 T% r$ A: i3 y( [4 k
注释2:% u2 a7 U( D- c9 Q
' \7 c( i0 C6 l1 U, K( c
- temp = hdma_usart1_rx.Instance->NDTR;// 获取DMA中未传输的数据个数,NDTR寄存器分析见下面
复制代码
3 z' D* w/ ~/ e5 [1 N同理, 这句被屏蔽的原因是因为他和上面的__HAL_DMA_GET_COUNTER的作用也是一样的,都可以获取DMA中未传输的数据个数4 [7 J+ d! k/ E0 _& ]7 f2 v
& Q! E# u6 D+ C0 V2 w. n: K. j) m
" S8 u9 V0 o: F( @$ M }) P& G( B2 X$ Q* s: L7 I
测试正常:% M! ]; O: p$ p, `* Y
0 N9 k2 ~2 _4 ~$ c4 H4 v
7 f7 D! b( \( ]* R/ K! Z3 N% ? z
% A+ ?+ V( G, ~$ P6 H/ m! M5 \# U此程序可以进行接收不定长的数据帧,不需像RXNE每次接收到一个字节就进一次中断。
b, ^; C; p1 R0 Q同时开启DMA传输速率也会加快 S |, e$ q9 D0 v |
例程2
2 ^+ O# ~' r8 Y; a7 K4 [2 bUART中断使能函数9 ?; }1 M. L! w
- __HAL_UART_ENABLE_IT(__HANDLE__, __INTERRUPT__)
复制代码 " j8 g) _5 p5 ?; b5 R/ n y, p
功能: 该函数的作用是使能对应的UART中断
' C! F% c6 K" t4 U
$ T2 Z3 G: j/ B$ F在stm32f1xx_hal_uart.h中被宏定义
6 g( I" c7 P, m+ p! g1 ^" h$ {& u5 {+ P
, [5 T) K5 @: ^. S
, k! J1 Y+ e& X9 E6 R可以使能的中断一共有这些:1 O, M! _/ r" f$ K) \ g
1 f p) Q: }: Y: m9 J
# Y( d: E8 r9 m. s ~! ~3 \$ @" B$ U4 ]- m u. W# Q
我们现在所用到的为:
/ s- N1 p4 J/ K; e- * @arg UART_IT_RXNE: Receive Data register not empty interrupt3 W: D2 @$ p. @
- * @arg UART_IT_IDLE: Idle line detection interrupt
复制代码
2 {2 F( }( b% o% G9 gRXNE接收数据中断
. s& @' h9 q# i7 U) ^" G4 R) c- r; _# \IDLE 接收空闲中断
3 a, r8 |4 o& k. k! @# S7 B+ L3 a" D2 \
7 y. p# i* X1 v3 F
" Z* Z1 S* T7 A' d$ o' e* X
本例程功能:3 s2 g& c: @. x# d2 Z
0 t3 Y" W" t3 q# J1 _; m7 I5 o使用RXNE接收数据中断+IDLE串口接受空闲中断 实现将接收的数据完整发送到上位机的功能
+ ]# N5 [( f! T) w% J# t; H: e4 |5 S" H9 c% v4 |
接收数据的流程:
+ q3 |( z& a: c. Q' f0 Z8 c: y& Y; \8 F# ~6 O9 H' f' b
首先在初始化的时候打开DMA接收,当MCU通过USART接收外部发来的数据时,在进行第①②③步的时候,DMA直接将接收到的数据写入缓存rx_buffer[100] //接收数据缓存数组,同样初始化配置的时候设置好配置就可以了。
6 P v" N9 S4 w% Y* o
+ s9 L! ^1 |- I" x; o G接收到一个字节数据:7 i2 _3 v4 ?8 W* E+ E: }: M1 G1 S
接收到一个字节的数据之后,便会进入RXNE接收数据中断,
* d; z) A, j/ i( ]4 g2 k0 f) E( d8 w! p" C
接受完一帧数据之后,便会进入IDLE 接收空闲中断% }3 ` o8 w, [+ H$ n7 `8 m
# c, g1 Z6 p( y9 N3 k, B' A# f
在uart.c中添加中断函数
0 D. {9 U' w/ k# j
$ w+ @0 [& q( V4 ]% Y# P% D- void MX_USART1_UART_Init(void)
! u: d, X* ?9 |* H - {7 z- H2 h8 E S( }# R5 l% {
- ) j" m8 s( C+ m! ?! s$ P* b* B1 u: Q6 B
- huart1.Instance = USART1;. @( T7 Z- x. o v( O8 E( w
- huart1.Init.BaudRate = 115200;
P, \; o* c3 g+ L' L# r% G+ y* E - huart1.Init.WordLength = UART_WORDLENGTH_8B;
) ^7 S; ^% n# c% v5 k) a - huart1.Init.StopBits = UART_STOPBITS_1;$ T, }: T3 w4 r0 ~; A4 t2 F
- huart1.Init.Parity = UART_PARITY_NONE;
) ]. Z' `1 Y- I: z. r7 ~ - huart1.Init.Mode = UART_MODE_TX_RX;* @$ R+ ]3 ]. x1 P4 a
- huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
( A" C: r3 w6 \5 ?6 @) u - huart1.Init.OverSampling = UART_OVERSAMPLING_16;
z: Z2 O6 g4 C - if (HAL_UART_Init(&huart1) != HAL_OK)
0 s' L/ i4 c, ~ - {6 L8 }5 e0 W- k8 Q# ]* }3 H
- Error_Handler();
" M" M( b: J5 I5 [2 D! ]1 l; S - }
( B# ~; ?3 P; J6 W4 d3 h
# n& g" Y6 B( O0 [* Q$ K: l& n/ f6 r3 C- __HAL_UART_ENABLE_IT(&huart1, UART_IT_RXNE); //使能IDLE中断# B6 b( T% D! g# [! C2 F1 e1 x E
- __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); //使能IDLE中断1 s. {2 Z: v; g. g8 J
- ( H5 ^, P. N& k/ F- e
- //DMA接收函数,此句一定要加,不加接收不到第一次传进来的实数据,是空的,且此时接收到的数据长度为缓存器的数据长度# x! ^" X5 U; r
- HAL_UART_Receive_DMA(&huart1,rx_buffer,BUFFER_SIZE);& }, v- m! W! ?7 O# v' T" n
- E; \1 B) b& [7 i0 ~7 {2 j9 [/ B# |
- + u; I3 A, ]* i l
- }( e, l5 n0 X/ b i6 l
复制代码 其核心代码就是下面的两句
4 W4 q9 Y$ u' i% G
% g& _! P8 `% L; E* q q8 G
1 Q/ J( @+ f0 D: N6 K. n- |% s5 P
' D; s' {, a6 J, _/ E* @. I5 J2 L第一条语句用来判断是否接收到1个字节,第二条语句用来判断是否接收到1帧数据; T6 J; q9 B* C# m M- h+ ~
- * w8 D8 @. u' D8 ~& X
- if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE)!= RESET) //如果接收到了一个字节的数据
1 J* W4 o1 O! \- q3 i1 B3 Y - {
( u) ` m. q# Y, w( f - HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_5); //反转LED
, e, {# F9 X1 O4 u - }
4 s+ @) H& I3 _ -
6 m0 k9 o' m# b1 ]/ s. h6 k# o - if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE)!= RESET)//如果接受到了一帧数据 @ v [( a; l+ Q' Q! p. }9 ^7 }# @
- {
" }+ f+ d' ~% y& J- P1 ` - __HAL_UART_CLEAR_IDLEFLAG(&huart1);//清除标志位
复制代码
, A+ F! h+ l3 Y: S其余部分于上方例程1基本相同: z# Z+ w- r4 S( @5 y) w. f7 B9 J* q7 d
) Z: t) |5 v3 q* d# `本次测试是检测接收到一个字节就反转LED
. M! U A6 o# v. o# D# W& T+ m. J7 w经测试,历程正常:1 ^+ `! J( Q3 ~
" `) s5 k% b) r" g8 s. }$ s& H: V: ? % V0 O. v8 G: U$ T0 ]6 F1 a9 {
% E# z9 v- x% y$ p+ Z8 ^6 Z( a
+ m8 _) _% `0 @, i; {# c+ d
# z5 [) b& U3 ^$ H* J3 W' G5 Y
|