本篇文章提供两种方法:! R: b# Q( w/ H
一种是 :IDLE 接收空闲中断+DMA
% ]' Y: k0 J3 F1 N7 l一种是: IDLE 接收空闲中断+RXNE接收数据中断
+ ^" W% @8 }" K7 z" z# j
3 T% e! T# b$ u# {, l* f! E都可完成串口数据的收发" |8 m/ H* E" y6 c3 K
8 Q% A1 g) w+ W% P: o知识点介绍:
* _3 j" M: j3 FSTM32 IDLE 接收空闲中断
: I5 D" P% A5 i( ?功能:
+ E" t' ?, H$ k+ F, e+ V在使用串口接受字符串时,可以使用空闲中断(IDLEIE置1,即可使能空闲中断),这样在接收完一个字符串,进入空闲状态时(IDLE置1)便会激发一个空闲中断。在中断处理函数,我们可以解析这个字符串。7 t/ O( v1 X8 P# i( o7 B
; t, x& c! x" l2 b2 `接受完一帧数据,触发中断1 H9 X. ?6 I( s( F9 M
3 J8 V5 G* X* J4 G7 B Z% e+ o
STM32的IDLE的中断产生条件:9 q7 D9 g6 q4 m
在串口无数据接收的情况下,不会产生,当清除IDLE标志位后,必须有接收到第一个数据后,才开始触发,一但接收的数据断流,没有接收到数据,即产生IDLE中断
6 z4 f0 w* S0 w9 n$ \
: O3 R/ W+ i$ ]! S; cSTM32 RXNE接收数据中断
; S' W/ ]0 ~! C) D( Y. T3 F- r功能:
. g- Z$ t% m4 T. K- |当串口接收到一个bit的数据时,(读取到一个停止位) 便会触发 RXNE接收数据中断2 ^) a" Q# f: D K! C% p' K+ a
1 i3 A9 i' _+ M# M! M3 u, K接受到一个字节的数据,触发中断
. l8 N! c# L" G, `
* t: ]. {( D/ I. ~' w- ]4 [7 }2 o# r. {比如给上位机给单片机一次性发送了8个字节,就会产生8次RXNE中断,1次IDLE中断。: T9 ]3 L0 m# d, a
# C* b: a3 P3 v' K4 e; y/ }, V
串口CR1寄存器( c& [7 ^+ s: Q
) h% a0 h$ l: m/ @3 l# U: h
5 k: Q! @* f* s0 [5 P5 W8 [- H) L6 ]' i
* s* U6 |- K' R1 x! q对bit4写1开启IDLE接受空闲中断
/ H/ u/ e9 k6 k2 s! ^; {& [,对bit5写1开启RXNE接收数据中断。
. P+ w* {5 Q5 N" G. H1 ]3 D& c% {8 d" s# Y; s# e
串口ISR寄存器
5 p3 A8 [# Q v4 a
0 K; j+ f" m9 F" A0 A8 N
8 `# Z _( ~7 ^$ Y. N) {, b
! e: b& j7 N3 ~% j; A! K! Y0 ~此寄存器为串口状态查询寄存器
^$ E& v) ~) _3 o- |
. O- q3 w* u& I( l- I/ S) J! c当串口接收到数据时,bit5 RXNE就会自动变成1,当接收完一帧数据后,bit4就会变成1.
6 }; d/ h% ?* _+ n" e* R @: s
4 y$ p% Q6 b6 |1 P8 F# x! A清除RXNE中断标志位的方法为:* F5 N; x1 k) r1 g
* x2 k5 g9 ?& Y6 Q: F
只要把接收到的一个字节读出来,就会清除这个中断- n+ b, w' ?9 D+ O; E9 V2 S' e
4 `+ [/ o( `/ z2 F1 ^/ G1 N! }在STM32F1 /STM32F4 系列中 清除IDLE中断标志位的方法为:
1 d+ V9 h7 ]6 s L: T* T$ R5 z- Y, r0 L! N0 ^& U
1.先读SR寄存器,7 h6 v/ r2 W5 ?( u$ v
2.再读DR寄存器。
& a2 L& r5 e# V* y1 }9 p2 W" }/ ?9 e1 a( {' A: t B, O
# ]8 i6 R% ^: K. F$ o1 c0 h7 O8 ?' ~* l$ O _6 a
memset()函数" c! N( I$ U6 Y+ H! a$ v
3 M* v/ [6 ?' M- p9 B- extern void *memset(void *buffer, int c, int count)
复制代码 % p: Q% D1 e: H5 R) [1 s s
buffer:为指针或是数组
; O# W5 O/ a/ I) k: Lc:是赋给buffer的值
; M2 h3 Y5 k8 ^! d3 A qcount:是buffer的长度.
/ V3 h# K5 ?* P, S( P* HUSART采用DMA接收时,如何读取当前接收字节数?6 D: g [! t0 v$ l2 @
- #define __HAL_DMA_GET_COUNTER(__HANDLE__) ((__HANDLE__)->Instance->CNDTR);
复制代码
7 }" P; g0 {3 Y- bDMA接收时该宏将返回当前接收空间剩余字节1 g+ r% J/ _! Z0 e( u& i' O
# L( H+ d. { S% O
实际接受的字节= 预先定义的接收总字节 - __HAL_DMA_GET_COUNTER()
( x& X; b+ X" A/ ~; J% ^1 E) }4 d; Q* e4 K% t2 N
其本质就是读取NTDR寄存器,DMA通道结构体中定义了NDTR寄存器,读取该寄存器即可得到未传输的数据数呢,; d7 k8 C. ~, _' H) [) R1 c" V0 S
% U' C2 j( m, U. }6 z6 R
NTDR寄存器
) T; N; z7 n6 K' U7 [# d% _+ Y8 ^+ m$ x
/ R, _/ C- f- T2 h6 d6 d- ~% r1 D1 O6 x. c* _% n/ @
实现方法:
: r& }! K' s+ m% P8 I4 P1 i两种利用串口IDLE空闲中断的方式接收一帧数据,方法如下:( `7 j5 G- ^/ W2 l# _0 ?# A+ T
+ r- m- R: z7 D% Z方法1:实现思路:采用STM32F103的串口1,并配置成空闲中断IDLE模式且使能DMA接收,并同时设置接收缓冲区和初始化DMA。那么初始化完成之后,当外部给单片机发送数据的时候,假设这次接受的数据长度是200个字节,那么在单片机接收到一个字节的时候并不会产生串口中断,而是DMA在后台把数据默默地搬运到你指定的缓冲区数组里面。当整帧数据发送完毕之后串口才会产生一次中断,此时可以利用__HAL_DMA_GET_COUNTER(&hdma_usart1_rx);函数计算出当前DMA接收存储空间剩余字节2 [; u0 ^# I- v. c/ ?1 f
J, `: F/ K4 k* t! L本次的数据接受长度=预先定义的接收总字节-接收存储空间剩余字节
: `) n5 v U2 t# t/ H/ v
1 `# A& d: d1 U5 ~ t* r比方说 本次串口接受200个字节,
4 l% k7 f" v; _/ C" r/ S+ b. X- HAL_UART_Receive_DMA(&huart1,rx_buffer,200);//打开DMA接收
复制代码
2 q* A6 Z9 O9 [6 [0 m g然后我发送了 Zxiaoxuan 9个字节的数据长度+ _) i4 w( e3 \% j' \
l9 u1 Q: C# {2 g! G那么此时 GET_COUNTER函数读出来 接收存储空间剩余字节 就是191个字节! `) s0 _1 g6 d6 T/ x, A" k7 w
8 C3 F6 t4 f3 h/ r. `9 I/ R$ Y实际接受的字节(9) = 预先定义的接收总字节(200) - __HAL_DMA_GET_COUNTER()(191)8 W+ `# _" N9 p, V% Y1 f
9 = 200-191# F0 H: j2 | {8 r4 F
, N4 T' D/ \) c0 K7 n; h应用对象:适用于各种串口相关的通信协议,如:MODBUS,PPI ;还有类似于GPS数据接收解析,串口WIFI的数据接收等,都是很好的应用对象。
k. w$ X& R- O; i X( B" f5 Q! G9 e/ ~: l* e
方法2:实现思路:直接利用stm32的RXNE和IDLE中断进行接收不定字节数据。 每次接收到一个字节的数据,触发RXNE中断 将该字节数据存放到数组里,传输完成之后,触发一次IDLE中断,对已经获取到的数据进行处理# q+ Z4 A* S) a' M% a; t2 a; I
7 e6 ]' V4 W3 c& x
例程1
7 e Q3 _/ ~3 J+ T) K& J5 z. [本例程功能:
/ }" }2 V' S: ?! z4 \9 J) w+ F7 Z2 K5 P) z4 x8 D0 U
使用DMA+串口接受空闲中断 实现将接收的数据完整发送到上位机的功能
. Z8 L8 L7 Q0 F7 }3 a1 Y% {: C& w8 g# {" g' _6 R5 m. o
/ M7 L+ \; p6 E1 j3 V. B x
& O( c \' {* E2 i7 M
接收数据的流程:
- J, ?5 c( I9 q7 @7 H& n9 @. b& M5 K
首先在初始化的时候打开DMA接收,当MCU通过USART接收外部发来的数据时,在进行第①②③步的时候,DMA直接将接收到的数据写入缓存rx_buffer[100] //接收数据缓存数组,程序此时也不会进入接收中断,在软件上无需做任何事情,要在初始化配置的时候设置好配置就可以了。( ^! I1 \" W2 o
9 J+ b, [, L$ f+ D4 c
数据接收完成的流程:
# F" f6 j. l. ~. M) Z$ Y
8 L7 p4 G: U' X# T5 L( M3 |+ | l当数据接收完成之后产生接收空闲中断④% p; N% _7 X3 Y% m( [7 ^- Y
' e; L3 _5 n: m. T* w在中断服务函数中做这几件事:& H2 Q. k# W/ k; v3 b, c6 v6 Y7 i
判断是否为IDLE接受空闲中断
7 N) K! I5 _7 V: u( q' v, p" _ j在中断服务函数中将接收完成标志位置1
4 U6 @9 p- M" ?* j% c关闭DMA防止在处理数据时候接收数据,产生干扰。
4 @, W" b0 `) p! b: ^5 V. Y) k计算出接收缓存中的数据长度,清除中断位,( a2 i! R4 c) F+ u
/ m k. T3 s5 {6 x3 q4 bwhile循环 主程序流程:
: K) O! y% i- G/ e3 z6 U& T$ t( n3 @主程序中检测到接收完成标志被置1. Q+ [8 k! ~2 D
进入数据处理程序,现将接收完成标志位置0,) E; P* q5 T& ]9 G- q0 o/ I* y5 }4 v
将接收到的数据重新发送到上位机, y/ c: X" y, l
重新设置DMA下次要接收的数据字节数,使能DMA进入接收数据状态。3 a; d: Y! c; t$ v7 N- w, H2 [) a
% W! z% R4 G! x" Z5 r
例程代码:
1 l0 y' J, M! V3 t( Fuart.c( T6 M. N0 w4 q% f3 [% R* s/ u
- volatile uint8_t rx_len = 0; //接收一帧数据的长度
% i8 Q8 F6 v" C E e - volatile uint8_t recv_end_flag = 0; //一帧数据接收完成标志7 V2 q# _+ N; M, h" C
- uint8_t rx_buffer[100]={0}; //接收数据缓存数组
复制代码- void MX_USART1_UART_Init(void)
9 g+ G/ M* ?# T8 g8 Q7 x3 K# k - {
5 \/ M! L" {0 A
3 W$ P- l/ v( C# H4 Q- huart1.Instance = USART1;6 y4 ]2 r) [& f: x+ U
- huart1.Init.BaudRate = 115200;# W+ g6 x, b4 p% ^$ G8 B# s6 _
- huart1.Init.WordLength = UART_WORDLENGTH_8B;, k/ V$ I6 B: {. _: K( j0 N2 w
- huart1.Init.StopBits = UART_STOPBITS_1;
6 t8 n0 R1 C5 H* c( z - huart1.Init.Parity = UART_PARITY_NONE; X9 G( T; x+ I5 ]) {2 b0 u, T
- huart1.Init.Mode = UART_MODE_TX_RX;
3 k3 g* V; ^9 c/ D8 C - huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;. b3 p8 Q# z2 h
- huart1.Init.OverSampling = UART_OVERSAMPLING_16;
. U* B: ?$ _# c( P% N& f; t, V# j - if (HAL_UART_Init(&huart1) != HAL_OK)
; t: V: V S2 Q% i7 R- W - {$ b/ j |5 {( ~* `4 H4 T3 f
- Error_Handler(); ?1 r6 s8 }5 n' @9 T
- }
$ {7 z1 v& u5 ]1 k2 _# P e - //下方为自己添加的代码1 a' Z* C9 G. a1 G% U" D
- __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); //使能IDLE中断
2 M6 ~; |' [0 o. M$ Y$ V) f; v - ) H2 h1 E+ V0 h9 o: Y# c) X% I
- //DMA接收函数,此句一定要加,不加接收不到第一次传进来的实数据,是空的,且此时接收到的数据长度为缓存器的数据长度( [( c) N k5 }# Z- g0 }
- HAL_UART_Receive_DMA(&huart1,rx_buffer,BUFFER_SIZE);
4 g5 Q S5 G+ \' k - + W4 C! E; U- s! h# `! R( p6 A
-
& G. k; {3 p. B6 A" q - }
复制代码 2 r# H- F7 {% D
uart.h
! s$ N$ w2 g% \8 a9 y- extern UART_HandleTypeDef huart1;9 G3 [( Z) s, o! L4 o
- extern DMA_HandleTypeDef hdma_usart1_rx;" c( Y; _# ?5 d) v4 q. Y" \ `
- extern DMA_HandleTypeDef hdma_usart1_tx;6 y* C0 d# b w4 ^ p0 Y1 g
- /* USER CODE BEGIN Private defines */
& `2 a$ N W0 n4 E4 m/ o6 r - 3 y" T. x6 x; T: ^1 }! ]
: X% I9 z/ B0 }) T- #define BUFFER_SIZE 100
5 n. x0 N: K, S- q# |0 \, ~ - extern volatile uint8_t rx_len ; //接收一帧数据的长度
6 i) ?6 s2 n) b# d2 I" G9 @1 S - extern volatile uint8_t recv_end_flag; //一帧数据接收完成标志
0 v" I6 u8 O5 p' G6 M' G% S" w - extern uint8_t rx_buffer[100]; //接收数据缓存数组
复制代码
- n5 r. M! g- _0 Gmain.c3 |( f6 V/ {8 a4 u. ? R n7 H: r% B
: t0 D D$ a( p! C# n, m- /*
: \+ _' W8 K' C# |2 c0 u" s3 Z - *********************************************************************************************************
/ ~: @2 Z$ ]( e! o H9 M - * 函 数 名: DMA_Usart_Send
, e: R; k+ | @6 f3 E - * 功能说明: 串口发送功能函数
1 I+ u" e% K# A! F! y+ J4 K' E - * 形 参: buf,len5 N' z* A: D2 {+ j- J2 [
- * 返 回 值: 无8 @! ]% Q1 k* i. _2 S$ x7 _, a5 q
- *********************************************************************************************************
9 J$ `3 I# ~) H# a8 h0 ^% v0 y4 e' P& W - */- ]; v; T! q. n6 J3 E
- void DMA_Usart_Send(uint8_t *buf,uint8_t len)//串口发送封装
# i5 F$ D4 A$ }, V$ Y$ H6 K; t! N - {3 q& l/ Q) Y1 B
- if(HAL_UART_Transmit_DMA(&huart1, buf,len)!= HAL_OK) //判断是否发送正常,如果出现异常则进入异常中断函数
, g, w* f, D$ f' ?0 ?+ @+ ? - {
9 C9 U& K( ~( f+ o - Error_Handler();
' p& b+ _2 Q% f. m4 p8 c# I* ?/ e - }5 L) T/ t' S1 f
# W& ]* ? |8 L3 N' X v+ |) `- }
8 ~( z# w( R. x
8 s; ^9 `- t2 J) d1 e: B- 9 P9 I6 J# M8 X* P# L
- ; J! ^8 D7 U$ V& a. V
- /*
/ { d6 G5 |) ^ - *********************************************************************************************************- g$ y5 ~; P6 F! O4 y" D! H
- * 函 数 名: DMA_Usart1_Read
( ^; I& _4 C- y7 t - * 功能说明: 串口接收功能函数6 Z1 Z7 ]( v( |% ~8 m i$ w6 c
- * 形 参: Data,len
5 ^- C2 x9 k2 H1 ? - * 返 回 值: 无' |- B1 b2 J1 v- Z0 a
- *********************************************************************************************************( ~1 V- f0 F: e9 c I) F, _
- */, s% Q- V$ Y* m4 I
- void DMA_Usart1_Read(uint8_t *Data,uint8_t len)//串口接收封装2 _* y5 r' l% X
- {) A9 }, ~$ @) U0 q6 J
- HAL_UART_Receive_DMA(&huart1,Data,len);//重新打开DMA接收
! W6 p. g: V/ w3 b, D - }
$ O2 s6 G& `8 I+ v- ~+ {
复制代码
2 y2 d! x6 h- @9 [6 M7 m+ jwhile循环
$ E2 s' b2 r% A5 x# Y, p
* U9 p6 |. ?4 v- while (1)/ w, ]7 C2 B0 Y& r, o
- {
# c5 ?* R9 r0 |1 q! @" v - /* USER CODE END WHILE */
$ ?! d8 C( w8 Y
8 }3 e4 B" [, I: I5 @; v. v _8 t- /* USER CODE BEGIN 3 */
$ r/ c8 F( k- P3 s8 o R - if(recv_end_flag == 1) //接收完成标志
# R7 M0 F+ X8 z& h# M5 ~ S+ q - {: `9 s! h2 M' `9 y
-
5 g+ ^! M" t) ^ - 9 D) X& g2 t: X M0 `
- DMA_Usart_Send(rx_buffer, rx_len);$ ?9 p) K0 p' \0 u. h" E- T
- rx_len = 0;//清除计数
0 a& }4 T2 L2 s2 M [ - recv_end_flag = 0;//清除接收结束标志位2 G# b8 I5 D4 ^1 Y
- // for(uint8_t i=0;i<rx_len;i++)/ x5 Y) N% W$ k9 n4 O4 s
- // {# e$ j# p8 a% o0 @: S# @/ M
- // rx_buffer<i>=0;//清接收缓存</i>
0 m9 s. u; Q1 v* C: f; s& Q* j: K. o - // }
; g7 z- V6 W7 c& {$ r' t6 ` - memset(rx_buffer,0,rx_len);/ _- c- Q; E2 e+ d' J
- }- l$ L0 V# Z. D7 b& m5 L- C8 S" Y, ^/ r
- HAL_UART_Receive_DMA(&huart1,rx_buffer,BUFFER_SIZE);//重新打开DMA接收
% h8 } t9 Z2 |0 h' i8 Q - }& a1 H2 U$ g% w3 ~- b- \
复制代码 8 }$ ]' E4 D. l$ S7 N$ H5 r' i
stm32f1xx_it.c中
: C* @ N) I. i* @! S1 W: L8 l/ a+ G' ?: H+ | f7 o
- #include "usart.h"
6 N9 @( T" p y& F9 ~ - : j% ^( O3 c& o- @( f
- void USART1_IRQHandler(void)8 l8 m% m1 E( i+ W) |1 ~
- {9 p6 D2 r9 I4 w% i
- uint32_t tmp_flag = 0;3 ~( l. S& t" q7 h2 _+ p$ y
- uint32_t temp;
+ w) B: \1 d" c' D - tmp_flag =__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE); //获取IDLE标志位
% f( ]& M. [$ \% e - if((tmp_flag != RESET))//idle标志被置位6 w9 b, u3 I9 W* z& }% A
- { : l! C. G t) X3 y
- __HAL_UART_CLEAR_IDLEFLAG(&huart1);//清除标志位! z9 I2 X) A* V3 }. h2 H
- //temp = huart1.Instance->SR; //清除状态寄存器SR,读取SR寄存器可以实现清除SR寄存器的功能
\, `2 A, F- k: i4 L - //temp = huart1.Instance->DR; //读取数据寄存器中的数据* T, M* n. m- R, i( T7 J. x
- //这两句和上面那句等效+ ]9 a- |3 e) I. t7 s0 p# \( u4 }/ r
- HAL_UART_DMAStop(&huart1); // 停止DMA传输,防止
1 Z7 d, U& ]# Y+ d: N8 j2 V - temp = __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);// 获取DMA中未传输的数据个数 2 u8 Z1 D# M8 f" X" Q7 B/ a# q
- //temp = hdma_usart1_rx.Instance->NDTR;// 读取NDTR寄存器,获取DMA中未传输的数据个数,
0 E8 b8 S; \8 a" V0 ^3 n - rx_len = BUFFER_SIZE - temp; //总计数减去未传输的数据个数,得到已经接收的数据个数" q+ f# \1 e. D# W+ Q
- recv_end_flag = 1; // 接受完成标志位置1
! q" ^% U$ A( t( g( v - }) [% D* u: C+ M( s* s2 d+ ?, D
- HAL_UART_IRQHandler(&huart1);0 l. @; n$ P9 |- g5 A" Y$ h
- : v0 L7 z# {# E
- }
1 C- w/ V. I8 B
复制代码
# t& x: u i" q% s+ E注释详解:3 }& s9 `: O" Y6 v, P( c8 H
注释1:! s$ L( _5 a2 j8 Y! H* `" b
- temp = UartHandle.Instance->SR; //清除状态寄存器SR,读取SR寄存器可以实现清除SR寄存器的功能
* B) M5 o. _$ f5 q" ]/ \0 }/ A# h - temp = UartHandle.Instance->DR; //读取数据寄存器中的数据
复制代码 6 o4 K" I) w. R) |$ ?5 f1 f3 U3 u
这两句被屏蔽的原因是它们实现的功能和这下面串口IDLE状态寄存器SR标志位清零的宏定义实现的功能是一样的:
, A, w# N; P0 o( j6 d# M+ F; ~3 Y' a n5 r- V8 k
__HAL_UART_CLEAR_IDLEFLAG(&huart1);//清除标志位; G6 I$ r( z! \8 H* f
, w: R0 H1 l+ N/ e( V9 E( O3 @我们可以点击这个宏定义进去看看,它实现的功能和上面两句是一样的:
- c- q7 _* x5 C0 }5 ~3 P$ Y
$ L5 t0 |8 A \4 z* r" ^6 \#define __HAL_UART_CLEAR_IDLEFLAG(HANDLE) __HAL_UART_CLEAR_PEFLAG(HANDLE)0 f% b8 j' P R: b
) `5 l* C- n% |7 f. I2 w
2 M0 ?) H% T/ `- h( f7 P3 X& y8 |$ m* n
注释2:
. Z/ v" ~" \: j4 U) ^
8 _! e4 c; V. W& M- temp = hdma_usart1_rx.Instance->NDTR;// 获取DMA中未传输的数据个数,NDTR寄存器分析见下面
复制代码
% `( L" b3 Y: a+ c同理, 这句被屏蔽的原因是因为他和上面的__HAL_DMA_GET_COUNTER的作用也是一样的,都可以获取DMA中未传输的数据个数
% c3 W& W3 Y8 w4 L3 r
V& a' b u5 O) c
+ d) ?# Y5 I& c0 ]7 @
5 w% \( [7 Z& S* B1 u: q测试正常:4 o- J( D$ c4 `' P9 z: {' @
& D1 R& {. a( c
' i% j3 I& g H
5 b1 s m7 J0 [1 E7 Q& ^此程序可以进行接收不定长的数据帧,不需像RXNE每次接收到一个字节就进一次中断。
1 f- F& x4 U, X+ G) C7 Y1 [( J同时开启DMA传输速率也会加快
5 d/ s* K" U e0 ?# {: ]$ Z例程2# O, d* s/ N# d
UART中断使能函数8 p8 y8 }$ M2 t1 U
- __HAL_UART_ENABLE_IT(__HANDLE__, __INTERRUPT__)
复制代码
+ I8 ^# S+ Y3 W6 @8 x: D功能: 该函数的作用是使能对应的UART中断
# a, s, Y O4 k; \; B( O' S! [
8 I, ^4 b/ p$ q1 p8 f$ ?, w在stm32f1xx_hal_uart.h中被宏定义9 F0 s: u: h+ G4 h2 r e8 k1 f
1 [* l1 N! Z0 Y+ e; c: _( y6 P
+ T5 J% I4 L* A! A
: Y, s( Z" T: y可以使能的中断一共有这些:; T8 z8 j3 H0 {0 }6 N6 c; y7 I
% \- V2 L3 @! a* c" Q
, ^- g1 P G3 Z7 W) r6 L+ h
1 ]- A' m% ~5 ~
我们现在所用到的为:
" }9 x* \6 w2 R( m* P0 R- * @arg UART_IT_RXNE: Receive Data register not empty interrupt
" i# ^- o# m# _, L0 H0 G7 i9 |, @ - * @arg UART_IT_IDLE: Idle line detection interrupt
复制代码
2 K" K! Q7 h# [9 ERXNE接收数据中断
0 g" x; q3 A: b' aIDLE 接收空闲中断2 e' C8 L, Y. f& F) W
( c7 |, m$ M t) B! A$ p& V
7 w A) s o& Q) h6 H7 O$ I
+ K" I, S. Z2 S; i本例程功能:( Z$ H+ c9 B! j8 n! X5 {8 W- c
8 i7 k1 g( b: z* g: f7 X% i
使用RXNE接收数据中断+IDLE串口接受空闲中断 实现将接收的数据完整发送到上位机的功能+ C/ D9 W6 i! |) M* N; d
( h8 u* H+ {( t/ I/ _5 G
接收数据的流程:, [, X- d* j) d( ]; l) Z
; g5 c, S# u# m9 E首先在初始化的时候打开DMA接收,当MCU通过USART接收外部发来的数据时,在进行第①②③步的时候,DMA直接将接收到的数据写入缓存rx_buffer[100] //接收数据缓存数组,同样初始化配置的时候设置好配置就可以了。, T4 q5 A3 K& O, X5 U
. X% t+ w2 O! I' `, t: x
接收到一个字节数据:* ?2 K: d+ O3 B3 P
接收到一个字节的数据之后,便会进入RXNE接收数据中断,
' S0 b* C2 {) h9 W0 m* K) M
% J3 r' R2 O/ D Q7 c接受完一帧数据之后,便会进入IDLE 接收空闲中断
" v5 y, H3 x0 f7 s- { ?/ j/ w7 U8 n+ K/ m n
在uart.c中添加中断函数
% l2 a8 q, A0 h% G; j4 B' n2 }( _$ T
- void MX_USART1_UART_Init(void)
4 f2 b# K& T8 j& Y9 O4 T+ z - {
, X: a6 \) _( e% i+ f% [; O
O9 J2 y3 }7 |- huart1.Instance = USART1;
4 ?( n9 G1 r# I - huart1.Init.BaudRate = 115200;
6 [3 N: Q6 Z9 S" K1 u% h" `) } - huart1.Init.WordLength = UART_WORDLENGTH_8B;
. l) L8 W" o$ Y# H - huart1.Init.StopBits = UART_STOPBITS_1;
! b& E+ r& h, k$ c U0 W) Z: A - huart1.Init.Parity = UART_PARITY_NONE;
+ y3 j! R' Z2 V. P) G9 J6 g& W - huart1.Init.Mode = UART_MODE_TX_RX;6 M6 }1 }$ o2 }: |# l
- huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
$ Q* d0 b6 t5 M, Y; Z5 n - huart1.Init.OverSampling = UART_OVERSAMPLING_16;
1 b# [" d0 Y) q; y4 i1 X, V( i - if (HAL_UART_Init(&huart1) != HAL_OK). Z$ R2 j6 c: ^# O. C3 O$ {
- {
9 l) S& y% P( N; t2 l - Error_Handler();
. P$ b( V# D% ^% M - }4 H5 D6 t9 }$ s5 x& ]2 U" b' C: L
/ I4 A. Y q% c( X$ P* |- __HAL_UART_ENABLE_IT(&huart1, UART_IT_RXNE); //使能IDLE中断
A2 L8 O) B/ z2 P: k! s - __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); //使能IDLE中断* e5 x5 K0 ~3 d0 G0 K' I$ O
- 9 w3 ^3 I4 r% E
- //DMA接收函数,此句一定要加,不加接收不到第一次传进来的实数据,是空的,且此时接收到的数据长度为缓存器的数据长度
2 M5 a) e3 O" E% l9 K - HAL_UART_Receive_DMA(&huart1,rx_buffer,BUFFER_SIZE);
4 E5 w! M: A2 G a5 P$ l - 7 [, j7 w' _! `, P t2 I6 }3 }' e
-
% A' H1 R& p# M T" n. g - }
# U" R1 G& s5 B. H% t
复制代码 其核心代码就是下面的两句
5 `; C2 L" H9 H& ?6 ?& n. U* T( e! v3 v( I Z: c- {
; n1 D' Q5 `/ V8 q/ i' E/ B$ r
3 q& Z1 M. T6 U: i第一条语句用来判断是否接收到1个字节,第二条语句用来判断是否接收到1帧数据
+ H- A' l" a/ e- 7 `- h" A+ n, ~( E+ M
- if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE)!= RESET) //如果接收到了一个字节的数据
% h/ L8 }. V; b! f0 v5 L* j& w - {6 G) G% C1 d# f ?. r" e
- HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_5); //反转LED
i3 E' N, `( A5 Z - }. o2 W3 ^7 z; j
- 1 y# J4 B3 J, O6 ~% L: s! q
- if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE)!= RESET)//如果接受到了一帧数据2 g8 i/ Q6 B) f; B+ x
- { 4 o( e# @: y% _" ?- C
- __HAL_UART_CLEAR_IDLEFLAG(&huart1);//清除标志位
复制代码 # r3 I0 b# ~( P W
其余部分于上方例程1基本相同
# c$ o3 x, `% e9 A2 O
8 g( q J, O2 L" j7 _! b- u& Y本次测试是检测接收到一个字节就反转LED% u8 v2 E" z/ L5 a+ N, Q, V
经测试,历程正常:" H* u( _& N; C2 {5 ` [6 M: I; A
4 [7 w! O$ x2 C2 t1 B9 a! O
* D$ {: S( `8 x& b+ J1 D. U N$ n! c7 p5 }
- s6 t% E! T3 U
) X1 q4 D4 A- t$ [: U |