本篇文章提供两种方法:3 I& S/ B* h' n. ]
一种是 :IDLE 接收空闲中断+DMA! g! g* f3 A2 P5 _7 M4 c
一种是: IDLE 接收空闲中断+RXNE接收数据中断& q. F$ C4 g$ A8 A. a
+ q- {/ f3 H; W+ n- E" G都可完成串口数据的收发# |0 ?3 J5 \; ?1 O* |; T2 g
" k# e' Y& k. m6 F. @# L知识点介绍:
5 D6 A/ h/ q0 I" ]( X( y' cSTM32 IDLE 接收空闲中断) `7 k, r% a- Y n7 Q
功能:1 s8 T N+ e9 g9 p1 _$ O
在使用串口接受字符串时,可以使用空闲中断(IDLEIE置1,即可使能空闲中断),这样在接收完一个字符串,进入空闲状态时(IDLE置1)便会激发一个空闲中断。在中断处理函数,我们可以解析这个字符串。, l/ T3 Q U6 V' i5 V# h2 K
( a& I& \7 [6 J" A( l: [. G接受完一帧数据,触发中断
. H* }- h b- p5 Z' k, k- X1 j1 e
: {1 X4 s3 a: L8 g' P9 ^" D3 b/ ~7 y/ QSTM32的IDLE的中断产生条件:
& J; |/ F4 ^/ E" T% q' _4 U8 o在串口无数据接收的情况下,不会产生,当清除IDLE标志位后,必须有接收到第一个数据后,才开始触发,一但接收的数据断流,没有接收到数据,即产生IDLE中断7 [3 L, N9 m4 B7 {
& M' V H+ Y5 `$ ?/ A: o" \9 w
STM32 RXNE接收数据中断7 i$ O6 v( E. s2 A# v* C5 b
功能:
$ z& P' z. v( \) S当串口接收到一个bit的数据时,(读取到一个停止位) 便会触发 RXNE接收数据中断
5 p5 R* ~6 Z0 G: U
# g) ~2 E* ?' h接受到一个字节的数据,触发中断
! ?; o) K* p3 |2 F+ g7 Z% A
* @" g$ w' p( g! @+ E1 c比如给上位机给单片机一次性发送了8个字节,就会产生8次RXNE中断,1次IDLE中断。3 h7 u: {) O$ q+ M) j
* P- r5 j. M6 }$ v串口CR1寄存器
+ I# A# h3 e8 h! C' \ X! E2 j: |. y6 G; d
9 C, _7 d) o! _9 o! d w* x
- s/ h0 ^5 a6 {# P; B
对bit4写1开启IDLE接受空闲中断
' n; m: [! T5 W% j7 B9 T$ n,对bit5写1开启RXNE接收数据中断。
: b- A1 ~5 h" x9 [- p5 h
1 W- K# Q4 d7 S6 _' b r1 Y9 O串口ISR寄存器
( b) l: T# P/ D$ k/ l! C ^. C
% F @. F/ [0 y7 s4 M
& ^. J5 d& G5 k2 a1 m A H" ^& |
+ }7 }2 ]6 H+ q1 @9 H% p3 s
此寄存器为串口状态查询寄存器# C* ]. T8 G+ ?, V. r4 P0 d
; h- S; |. U& _) M3 x: T6 A/ m当串口接收到数据时,bit5 RXNE就会自动变成1,当接收完一帧数据后,bit4就会变成1.) v ~ g1 I. G0 O
' |" z' A1 ?5 Q' _1 C% K
清除RXNE中断标志位的方法为:7 |' M, s+ M, K# J$ g- x0 I6 d0 H
/ Z" T3 H! N% d& \ t" c只要把接收到的一个字节读出来,就会清除这个中断4 x6 E2 K* q' `5 H+ {+ H% f
& g0 c' ?& C b. Q( V
在STM32F1 /STM32F4 系列中 清除IDLE中断标志位的方法为:
6 r* L: K6 E) }% R! P# h4 ^* f; @% p# B0 G) Z1 h K) v6 P
1.先读SR寄存器,9 D% B: O; U1 {
2.再读DR寄存器。. X: T/ h/ }2 }; ]! q
/ c. Z3 D: x8 \- g T! e
% m E* q4 \4 n. ~
; e, e; Q! E7 Amemset()函数5 ]) F3 \" ^1 y& n- r. G6 F' S
# h% _- ]# M3 p8 a$ o% e. x- extern void *memset(void *buffer, int c, int count)
复制代码 ; A7 v7 D! I6 M! f4 O
buffer:为指针或是数组
! f9 w O! g* G8 X( k* f) mc:是赋给buffer的值4 g/ L( I; p0 v; G
count:是buffer的长度.; Y( u2 F, f" O9 u
USART采用DMA接收时,如何读取当前接收字节数?
6 R" `! W+ l. B6 \+ d- #define __HAL_DMA_GET_COUNTER(__HANDLE__) ((__HANDLE__)->Instance->CNDTR);
复制代码 # r: m @) S8 p& \3 M" m
DMA接收时该宏将返回当前接收空间剩余字节
6 t7 f: T! D6 b, ?: l+ N3 m+ ^; b' e7 k3 D$ U
实际接受的字节= 预先定义的接收总字节 - __HAL_DMA_GET_COUNTER()
. F# `/ h3 t' K0 J. t8 ]% }1 g6 p# K: V1 E1 j
其本质就是读取NTDR寄存器,DMA通道结构体中定义了NDTR寄存器,读取该寄存器即可得到未传输的数据数呢,
6 f' @# x& G- b0 v; \* s1 p4 G/ Z. y, j0 M- {& n" h
NTDR寄存器- W% {3 k$ p, n# k& u8 [
4 x3 y" E# p! S m, c+ A4 f
) j! G$ B- O7 J8 z, D* p% z" |7 ]( {$ i! [- P
实现方法:
3 P- z* E8 J" ]' C4 e1 s! _0 {) e5 e两种利用串口IDLE空闲中断的方式接收一帧数据,方法如下:; {# {' e; |: P- O5 R% c, L
; l: G& G4 o" l3 {方法1:实现思路:采用STM32F103的串口1,并配置成空闲中断IDLE模式且使能DMA接收,并同时设置接收缓冲区和初始化DMA。那么初始化完成之后,当外部给单片机发送数据的时候,假设这次接受的数据长度是200个字节,那么在单片机接收到一个字节的时候并不会产生串口中断,而是DMA在后台把数据默默地搬运到你指定的缓冲区数组里面。当整帧数据发送完毕之后串口才会产生一次中断,此时可以利用__HAL_DMA_GET_COUNTER(&hdma_usart1_rx);函数计算出当前DMA接收存储空间剩余字节
0 _3 U( E% Y- ]+ Q+ h
$ a3 ?. b3 t( u% a ?+ g本次的数据接受长度=预先定义的接收总字节-接收存储空间剩余字节
s2 t6 M- k X. E) d1 N$ ]: q1 R5 D/ E2 o
比方说 本次串口接受200个字节,5 n% E- G8 X6 T) r5 ^1 u
- HAL_UART_Receive_DMA(&huart1,rx_buffer,200);//打开DMA接收
复制代码
- `- G: d$ [3 L: |然后我发送了 Zxiaoxuan 9个字节的数据长度
. T. H, b* D, _/ Q' i2 h" [) u n3 B& z* k; }: g; ^
那么此时 GET_COUNTER函数读出来 接收存储空间剩余字节 就是191个字节( C( V/ p u5 ^
1 Z) F1 V, n* P2 r+ `实际接受的字节(9) = 预先定义的接收总字节(200) - __HAL_DMA_GET_COUNTER()(191)' k, f% L0 W5 F+ f
9 = 200-1912 K F- R$ h+ \7 O. l
* w, _7 J, C' Z! y. r0 j0 c# p应用对象:适用于各种串口相关的通信协议,如:MODBUS,PPI ;还有类似于GPS数据接收解析,串口WIFI的数据接收等,都是很好的应用对象。9 ^! U! f" i% k7 U. g, ^
, {& z) A& p& p- o5 s% X0 p6 p) s% o方法2:实现思路:直接利用stm32的RXNE和IDLE中断进行接收不定字节数据。 每次接收到一个字节的数据,触发RXNE中断 将该字节数据存放到数组里,传输完成之后,触发一次IDLE中断,对已经获取到的数据进行处理
$ G3 ^; ^+ k& j# y7 O- v0 p9 R, T2 m- z. l5 ^6 b7 i
例程1; u# j; b' f8 Z( ?7 j, J Q
本例程功能:
5 a: }- [: b5 m, O( o7 y4 L* Y" f9 f7 B4 k1 Q/ E
使用DMA+串口接受空闲中断 实现将接收的数据完整发送到上位机的功能
: q' p5 _" f0 V8 |
& @9 C: v1 o/ D/ x9 W( ?
+ z0 B- c9 [" S
" i t/ b4 c" x/ y. g7 j; o$ F接收数据的流程:
p) F! v1 N" C. ?$ Q2 m5 O+ i0 e' Q7 u; U
首先在初始化的时候打开DMA接收,当MCU通过USART接收外部发来的数据时,在进行第①②③步的时候,DMA直接将接收到的数据写入缓存rx_buffer[100] //接收数据缓存数组,程序此时也不会进入接收中断,在软件上无需做任何事情,要在初始化配置的时候设置好配置就可以了。
" r5 p7 l9 |% b2 C5 H. t4 O+ G1 o7 Y5 E7 E' o0 I& B% E* {
数据接收完成的流程:5 E+ A! L& Y) X; n9 @. l& \; R
, y6 `5 y4 v. y% a8 j# C7 L当数据接收完成之后产生接收空闲中断④
l; i2 `; Q5 ^0 c& ?' [7 R4 \* m( J% h2 U/ Y+ ?
在中断服务函数中做这几件事:4 f) H, e, j# Y
判断是否为IDLE接受空闲中断0 M$ s& D8 L. p# N( g) _/ R
在中断服务函数中将接收完成标志位置1
2 A0 v. n6 q6 K+ X u关闭DMA防止在处理数据时候接收数据,产生干扰。1 G- |, X1 t$ w* o
计算出接收缓存中的数据长度,清除中断位,
/ s! ?+ ^% O; P! n# j+ n# Z; k* J" h9 u
0 B4 k: q2 Y. \) n6 B2 Q4 {while循环 主程序流程:3 Y" f% g; r* C6 I
主程序中检测到接收完成标志被置1/ p* b! }- ?2 x( X! [( f
进入数据处理程序,现将接收完成标志位置0,
1 G/ {9 x6 `7 z: ~! `, a: _) q5 \) H/ |将接收到的数据重新发送到上位机
) D( N" e! P' L8 a9 W重新设置DMA下次要接收的数据字节数,使能DMA进入接收数据状态。% y0 h4 [% ?1 X% D2 L5 T
% d @& G% e( z- A例程代码:" C) w4 J( x( ?2 B8 V% Q
uart.c+ A5 c" h" X$ f+ A1 C
- volatile uint8_t rx_len = 0; //接收一帧数据的长度+ b. p6 g* ~3 |; |* B
- volatile uint8_t recv_end_flag = 0; //一帧数据接收完成标志+ B* M0 N$ \, T# A7 {$ [: c! a( U$ [
- uint8_t rx_buffer[100]={0}; //接收数据缓存数组
复制代码- void MX_USART1_UART_Init(void)
$ t7 }4 c, k: F( J2 A8 b3 C q - {
2 H4 ]. ]) z+ V; t4 W
3 ?, w8 I" U9 p, |9 B% ~' r3 J4 [- huart1.Instance = USART1;
* K# o: L7 b2 d: ^6 g2 k2 L - huart1.Init.BaudRate = 115200;, |" O7 u6 W7 h' V0 O a1 ~
- huart1.Init.WordLength = UART_WORDLENGTH_8B; d* V% I1 k7 _4 ]. d
- huart1.Init.StopBits = UART_STOPBITS_1;
5 a/ {' F& H: T) o) w& |5 g - huart1.Init.Parity = UART_PARITY_NONE;
# C" U+ n+ |; s9 C, T6 {$ x - huart1.Init.Mode = UART_MODE_TX_RX;" t a3 k3 m% f3 @7 [
- huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
+ b8 [7 z* Q- c9 G0 w - huart1.Init.OverSampling = UART_OVERSAMPLING_16;
7 Y) I8 v: {9 D* W - if (HAL_UART_Init(&huart1) != HAL_OK)
0 J3 ~9 [+ a) g/ n# ^8 X - {( K" R* N7 V% f/ i( z( |
- Error_Handler();) A) e0 T' _- j$ ^% c+ G. L
- }8 @8 q$ n/ Y F# v8 F L; {
- //下方为自己添加的代码
! u2 J% u3 B6 g& B! B' K! s - __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); //使能IDLE中断; G$ m+ o p" k3 V8 R3 g6 N% R c5 O1 }
- 0 P' [) ~) |' ]% b6 g
- //DMA接收函数,此句一定要加,不加接收不到第一次传进来的实数据,是空的,且此时接收到的数据长度为缓存器的数据长度' V7 d; J) y. ~8 I% D
- HAL_UART_Receive_DMA(&huart1,rx_buffer,BUFFER_SIZE);
; H; \* O: \5 g4 n0 q2 e7 w
- _7 x/ W& V) p9 [, z5 t- - J# ^) S5 X. _7 t$ n* D X. q
- }
复制代码
' C# t$ h8 r. K$ T1 Wuart.h9 ~/ }5 D$ I" l; F
- extern UART_HandleTypeDef huart1;- T: d5 |2 s% M1 t' N3 I/ X
- extern DMA_HandleTypeDef hdma_usart1_rx;( ]7 E9 ^' @8 `* P
- extern DMA_HandleTypeDef hdma_usart1_tx;
3 d& q) j$ ~7 {$ T% m - /* USER CODE BEGIN Private defines */5 t$ P0 i" h" _" f) a: P- z
4 z1 X" n6 m% {6 O" q- D2 M- 6 O0 B7 s; T, d+ {7 Y9 D1 O
- #define BUFFER_SIZE 100 ) G) Q, f2 s# H- O8 q
- extern volatile uint8_t rx_len ; //接收一帧数据的长度& Z5 O0 b+ A' w
- extern volatile uint8_t recv_end_flag; //一帧数据接收完成标志6 D" |7 A) R- r
- extern uint8_t rx_buffer[100]; //接收数据缓存数组
复制代码
$ Z2 n5 I2 p( J4 B8 U9 Z9 @4 {" cmain.c
* `; p: `1 b4 G$ b8 Z8 G
7 ?4 n; y8 ^8 f9 H- /*: d' }9 b2 Y% n7 u) S! |% a
- *********************************************************************************************************) f/ k4 m/ \8 v1 N4 ~# u# Z
- * 函 数 名: DMA_Usart_Send( ]; N! u! u. u8 A
- * 功能说明: 串口发送功能函数
, g% {2 t m4 r" D; Q% `5 t! T - * 形 参: buf,len; f7 X% w: j! P0 O1 O
- * 返 回 值: 无% |9 t4 G; k9 e* d( H$ v
- *********************************************************************************************************
# R* y) M. t+ z' F - */: k" [# c6 [; Z' @
- void DMA_Usart_Send(uint8_t *buf,uint8_t len)//串口发送封装
- R: `- X$ q' B0 P - {
0 B* x$ z" _ ` - if(HAL_UART_Transmit_DMA(&huart1, buf,len)!= HAL_OK) //判断是否发送正常,如果出现异常则进入异常中断函数, i: Z9 ~7 W, c% N! r" p/ }1 \
- {
9 M6 M8 n7 E# e6 f3 e7 k& F6 v - Error_Handler();
$ p! f7 F# u* |2 t9 R5 ]$ Q - }: K ], M0 L7 d- S
- - d& \$ k- l/ D
- }
7 e/ D7 F5 d4 w
9 {0 O3 d$ z& i
, K0 o. m+ u- Z0 Q5 X2 |$ ~
; m- w1 q z" R1 A1 `8 P ]5 U+ t- /*
1 j- v6 j3 d' O" Q. c1 m - *********************************************************************************************************7 g2 E7 \7 y" @, s, o% | ?( c
- * 函 数 名: DMA_Usart1_Read+ H2 t* a* o; q$ G* [8 S
- * 功能说明: 串口接收功能函数. A" D7 A: h5 X4 R
- * 形 参: Data,len$ @0 m1 B5 L: v' Y
- * 返 回 值: 无
6 A" C: M9 ~5 s7 m9 }1 C8 ~ v - *********************************************************************************************************
& ^; F' i+ d/ k6 L - */+ F. H1 D# m/ J9 @
- void DMA_Usart1_Read(uint8_t *Data,uint8_t len)//串口接收封装/ ?2 Y7 U2 m. ^2 @4 M
- {
7 `' p ^5 V. T1 ]% B7 a$ l( s- @- ` - HAL_UART_Receive_DMA(&huart1,Data,len);//重新打开DMA接收( A3 S2 d0 J! l# e
- }% y+ M- o) e" ?; S5 ^! c; R
复制代码
- @' h7 U1 Y) [( B, cwhile循环
3 ~& Z4 g$ n/ U+ E: Y& O- P7 u; D" ?3 h8 X( x
- while (1)2 I* c9 L/ W9 L. N3 y8 p# n) ~
- {
- g- k% M' Q: A$ R" J& ?/ A A* b - /* USER CODE END WHILE */3 p* r% ^3 Q! }+ x. _
- 7 l9 [; @% B7 d$ a7 k3 X$ c7 @3 q
- /* USER CODE BEGIN 3 */
, q9 J/ Z$ A9 s, D0 K h0 b" ^7 E - if(recv_end_flag == 1) //接收完成标志& s2 I/ P5 c [1 x; A0 L
- {
0 e0 l- z/ c$ R2 z9 S, H' d - 9 i3 X, r$ l6 i5 ?' m# D* k
-
5 @ C; F, l, @' j' B - DMA_Usart_Send(rx_buffer, rx_len);8 x4 ^ a! G1 p1 ^! b E- }0 _
- rx_len = 0;//清除计数$ K+ M" z, s, b
- recv_end_flag = 0;//清除接收结束标志位8 n% A4 A' f. F2 u; m8 j
- // for(uint8_t i=0;i<rx_len;i++)& E4 {4 h! s, Z6 J6 P" D$ G' ]' p
- // {0 O5 i, o! ?$ h6 k0 [6 b
- // rx_buffer<i>=0;//清接收缓存</i>
4 ?+ g7 z+ M1 j; Q% A - // }
% A) U( U8 m6 e3 b3 B# N/ E - memset(rx_buffer,0,rx_len);
, O! ]! y3 j9 j- G% s% C( M x - }
9 U& S/ i0 C/ p* a. v - HAL_UART_Receive_DMA(&huart1,rx_buffer,BUFFER_SIZE);//重新打开DMA接收6 C' ]3 i! @2 w- B/ i5 c2 l
- }
. ] l8 z H& y% _% _2 e0 P4 c! ^
复制代码
2 a, h' s9 T n2 d7 z# `stm32f1xx_it.c中" s2 R! ?7 T! E0 Q7 P
9 Z- j) F, D. ?% O
- #include "usart.h"8 {, S$ G0 C. y6 ~- H, e
- 7 s g' N4 m% V" A8 i
- void USART1_IRQHandler(void)
) `& m9 R. T6 i i - {4 [6 ?8 q( d4 ^# ^$ ^ f" y
- uint32_t tmp_flag = 0;2 n9 u* @% K4 y$ [8 U. B+ n/ \
- uint32_t temp;
+ i7 l. @+ e/ u4 C& W8 D* p; b3 \ - tmp_flag =__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE); //获取IDLE标志位
8 d7 E/ G8 |. W4 `' R - if((tmp_flag != RESET))//idle标志被置位9 f8 r& T/ P3 m) G
- {
" U; q) @ H6 Z( C$ b9 @, t - __HAL_UART_CLEAR_IDLEFLAG(&huart1);//清除标志位4 \8 h, i; c, X# D: Q
- //temp = huart1.Instance->SR; //清除状态寄存器SR,读取SR寄存器可以实现清除SR寄存器的功能! V3 A ~$ o$ v9 o: \( m* F
- //temp = huart1.Instance->DR; //读取数据寄存器中的数据5 y7 b6 G) L2 K0 h" h% K* ~& u
- //这两句和上面那句等效
8 m) D7 r. k" k8 d( Z6 A. U - HAL_UART_DMAStop(&huart1); // 停止DMA传输,防止7 c* W- A& o ~
- temp = __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);// 获取DMA中未传输的数据个数
% S6 ?3 H* s# [ - //temp = hdma_usart1_rx.Instance->NDTR;// 读取NDTR寄存器,获取DMA中未传输的数据个数,
0 u1 R [- n# a3 g; I/ j: D) u" F - rx_len = BUFFER_SIZE - temp; //总计数减去未传输的数据个数,得到已经接收的数据个数
) t3 ^, \- Y1 ` - recv_end_flag = 1; // 接受完成标志位置1
$ F2 I( z# m( n1 t, s3 W - }0 K e" _! g# f* M
- HAL_UART_IRQHandler(&huart1);
) K8 r {6 D o( R) G O/ |
4 v3 [" e! S2 X+ o9 o2 ?- }
3 `! o, ^* g( E3 \( i. z2 R
复制代码
7 k# X0 d" W9 Y' g" S8 h注释详解:
L6 @; N$ d) T" i3 z注释1:5 U2 }4 D* r- x3 d$ Z% {% Y
- temp = UartHandle.Instance->SR; //清除状态寄存器SR,读取SR寄存器可以实现清除SR寄存器的功能
' D# `7 s- P; y$ I+ |; t - temp = UartHandle.Instance->DR; //读取数据寄存器中的数据
复制代码
1 A7 F$ z0 S, c1 t' m6 h! a这两句被屏蔽的原因是它们实现的功能和这下面串口IDLE状态寄存器SR标志位清零的宏定义实现的功能是一样的:
! u, e# U1 }( @8 i: Q. `+ g8 J
* I0 u$ J6 u" S0 u7 C; W, `__HAL_UART_CLEAR_IDLEFLAG(&huart1);//清除标志位4 n" H1 ~$ I& s: {
/ K; ^- C6 Z& d/ k% O6 I; f6 B我们可以点击这个宏定义进去看看,它实现的功能和上面两句是一样的:
+ S8 M. g4 o# E
" q6 Z1 a" i* i n#define __HAL_UART_CLEAR_IDLEFLAG(HANDLE) __HAL_UART_CLEAR_PEFLAG(HANDLE)
) W) V" y. k5 A, T! o- M
: C+ D" r6 _$ ]0 c/ R
- z/ \7 Z, U5 m6 H7 _! Z- P9 a
注释2:% N. G2 F7 a# V0 F6 ?
, Y) I6 H$ Z: ]- temp = hdma_usart1_rx.Instance->NDTR;// 获取DMA中未传输的数据个数,NDTR寄存器分析见下面
复制代码 * m/ n$ ]( E ]2 w' Y% n+ \
同理, 这句被屏蔽的原因是因为他和上面的__HAL_DMA_GET_COUNTER的作用也是一样的,都可以获取DMA中未传输的数据个数
6 w! O, R; U, i1 \* V* b, q' K# ^$ ~
R; u# U# B. }
. m: }7 U: i }
测试正常:* R0 X. X+ n- e' k- S
/ }! G8 v" A: X' w0 w% L( _3 w
! T' K7 w* d b8 O
, D, Z# B! o( U4 ]此程序可以进行接收不定长的数据帧,不需像RXNE每次接收到一个字节就进一次中断。, J5 a6 L. W) }/ I5 O" O8 }
同时开启DMA传输速率也会加快$ A4 p8 E9 T; t P2 s5 j9 g
例程2 e& c/ e, c2 ]0 }' n( S z' q
UART中断使能函数+ \6 U g6 G9 L$ S
- __HAL_UART_ENABLE_IT(__HANDLE__, __INTERRUPT__)
复制代码 & [6 [9 z H' S- e7 j0 B
功能: 该函数的作用是使能对应的UART中断. f/ q# y* B2 c( q+ u. N! X
6 M# l+ I) _ ~+ B! [) n( B在stm32f1xx_hal_uart.h中被宏定义
7 K( E% G9 o: `+ f P' b1 v* A8 G( A1 [
0 H) w% D# U* h2 K+ q' f8 E
6 i3 q* }3 [& M
可以使能的中断一共有这些:
) {6 T+ X9 [$ v$ C6 t3 e5 y% }" l( g5 L" W5 [: x$ \ X7 O
$ R$ O# K4 O0 e% U5 `( ]6 T) s" h, {7 ~5 R" x
我们现在所用到的为:. Y6 C; B+ V' S c' q
- * @arg UART_IT_RXNE: Receive Data register not empty interrupt
# l3 b; }3 v. m - * @arg UART_IT_IDLE: Idle line detection interrupt
复制代码
+ b3 O, F1 Z4 R/ E) `( j: Q7 {RXNE接收数据中断
+ @, M7 U: y" z6 R# jIDLE 接收空闲中断
! Z# j9 |- V) s: Z7 h! s6 x z
0 b; O. V- [6 b- _( B0 H/ W1 t9 ]
8 v, T7 s- S% Y
/ A ^6 ?/ a0 D5 g) U
本例程功能:
' S# v- a5 V' d c( A. c$ m
( h. l) f0 h' t1 Y' N2 b" P5 F. A使用RXNE接收数据中断+IDLE串口接受空闲中断 实现将接收的数据完整发送到上位机的功能
7 C7 v) x: J _% U" c! }* z4 x7 o- a" m5 ^ v5 Q: v$ f1 H
接收数据的流程:; c& B. s7 {, g- W
6 l% G+ a+ d M J首先在初始化的时候打开DMA接收,当MCU通过USART接收外部发来的数据时,在进行第①②③步的时候,DMA直接将接收到的数据写入缓存rx_buffer[100] //接收数据缓存数组,同样初始化配置的时候设置好配置就可以了。
. K- A; }& U0 \" @3 W! B2 J4 N. u* p. a. g( x
接收到一个字节数据:
* j6 p! }5 t) o- T5 J7 u! g接收到一个字节的数据之后,便会进入RXNE接收数据中断,- T; M; D3 H) q J
' K9 t- D! @1 f b3 M4 a
接受完一帧数据之后,便会进入IDLE 接收空闲中断7 @8 C/ ]6 s) s% g" l
5 c0 x* s! B; Q' d
在uart.c中添加中断函数$ I" o) W9 a5 e6 f$ r
! L: b' z; C, M7 x; N% s" i P
- void MX_USART1_UART_Init(void)' g" I7 k" h0 w T( y
- {
. A1 W2 m/ e, h4 b! D6 g T+ T
+ g$ A# b1 {/ y; R- huart1.Instance = USART1;0 U" R. m( P6 ]/ D+ ]2 E8 S
- huart1.Init.BaudRate = 115200; Y) {- P# [+ d1 V4 v; ]2 ?
- huart1.Init.WordLength = UART_WORDLENGTH_8B;- {# n! u d- d
- huart1.Init.StopBits = UART_STOPBITS_1;
6 R* Q! l' u% ]9 y3 k5 Q# {0 c" s - huart1.Init.Parity = UART_PARITY_NONE;
$ t( o1 P5 \8 j+ Z& X- h8 Z0 y - huart1.Init.Mode = UART_MODE_TX_RX;) J" N4 [5 v( \
- huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;( |" d! v8 h/ \5 {+ O$ |
- huart1.Init.OverSampling = UART_OVERSAMPLING_16;3 o! {8 ^5 `' i9 u: d4 f v4 ?# Z. \
- if (HAL_UART_Init(&huart1) != HAL_OK)
' k4 k3 A" x. ^+ v* U" U8 S, K' l$ d - {( S5 o% @& K8 Z$ P0 Z
- Error_Handler();$ _ V+ }; V b" o6 q
- }
j- x# w* x0 K& o1 L) Z - 5 N( P" _8 ?/ g7 R* h
- __HAL_UART_ENABLE_IT(&huart1, UART_IT_RXNE); //使能IDLE中断
: M3 L& m5 H% f2 \. x" e+ }8 [ - __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); //使能IDLE中断
% C- i9 d7 H* T- m; W
: r, M' [6 V2 y4 y- //DMA接收函数,此句一定要加,不加接收不到第一次传进来的实数据,是空的,且此时接收到的数据长度为缓存器的数据长度* _' U! E4 b* I( Z
- HAL_UART_Receive_DMA(&huart1,rx_buffer,BUFFER_SIZE);+ K- U% J0 s; x* N0 n) o
* `% k9 `# k8 z9 g, y-
% l+ ] d b' Y7 x; I) z - }# t- \& W# Z6 e/ ]$ x/ f
复制代码 其核心代码就是下面的两句
, X) K' I. q& C; k- H4 y6 [+ h2 g' ` G% u; L" m
6 E/ R. P% ~! S: \, D8 U+ }
# Y0 f/ w6 D0 v第一条语句用来判断是否接收到1个字节,第二条语句用来判断是否接收到1帧数据
; H9 l7 v, ?5 Z- ) o/ d' a T; \. k2 @- P& ^ u
- if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE)!= RESET) //如果接收到了一个字节的数据
, I/ S( o0 `4 B( X+ D1 }' X0 q# Y - {5 y% g/ h' y. Y+ N
- HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_5); //反转LED* j9 t- s8 ]/ F8 D2 ]( S, b
- }- S' m: T) B A O) c h5 s
-
2 o6 |* ~ D4 V5 I+ |& H - if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE)!= RESET)//如果接受到了一帧数据
2 i, @) V5 `- d( t1 A% h6 A6 F - {
( n7 S |; L9 `1 d' _* L - __HAL_UART_CLEAR_IDLEFLAG(&huart1);//清除标志位
复制代码
8 h& x# N# y0 M9 b+ j其余部分于上方例程1基本相同
4 Y" r7 L% |, {" I- ^# p6 m) o4 \0 W1 A% ~. S) N
本次测试是检测接收到一个字节就反转LED. U- j, B! O& S- P
经测试,历程正常:9 y J" l2 F6 w& i% I' n
& i* H6 J6 w. W
1 } U) d) {2 ]: j) s7 U: k7 G& z& ^! g& G% W
& x6 ?+ Z# ]7 |3 x
/ x- a( `! y/ c# q |