一个关于STM32串口的问题。他想实现接收无限制字符数量的串口信息。
) s5 }% @) h5 y) Q, N
# E' C/ P* W p# i( N4 U但是他遇到了一个问题,他在主函数中发送循环发送内容(500ms的延时)串口一中断回调函数中如果收到了串口内容,就利用串口发送。
/ w2 v( H% ]( T+ [! Y2 u
7 t+ T% ~3 W6 E N$ W+ U# W! g但是他的串口一旦接收到了信息就会导致主循环中的串口发送极快。他的主代码如下
/ |/ [3 i4 G+ n8 v, u s) b1 v) B- unsigned char RecieveBuffer;
- F7 @" _6 e) L# @6 Q) a0 P - void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
, A5 x7 n/ G" f4 G5 S - {
8 [9 g' |# ]. ~ Z7 t! G - UNUSED(huart);7 O& D1 i, _6 X3 v( a; v
- HAL_UART_Transmit(&huart1,&RecieveBuffer,8 ,100); // 将接收到的数据再通过串口发送出去( X4 V3 K! b& Z5 m Q, E
- HAL_UART_Receive_IT(&huart1,&RecieveBuffer,8); //使能接收中断,RecieveBuffer的类型是uint8_t1 w' h- ^. b# u1 @& e0 I2 S/ s
- }
复制代码
- s. d* P; X' ?( w3 u n5 d# u看了他的代码,就这么短短的7行代码,让我汗流浃背。
% i, u6 f4 E# r! J6 G& _7 r! m- l2 }/ d/ E n3 E2 @/ A; Q
他的发送和接收中有一个参数是8,这意味着当串口接收的寄存器中有8个数据时就会触发中断回调。' s3 |1 ~9 d4 @+ a z3 L* W* f
+ [, s' a% t7 z8 n7 n. d
我们需要注意的是,串口寄存器的数据会被存储到缓存区中,这里的缓存区相当于一个队列先进先出,当我们调用HAL_UART_Receive_IT(&huart1,&RecieveBuffer,8);$ T& h- }, l4 P4 D4 k# f: G
. Q8 {5 Z: Y- {0 P会将RecieveBuffer作为缓存区,将串口接收的信息存入缓存区。
: j$ w' R. w1 q; c5 g" e* F2 ]+ d: T9 t) r \ R8 d3 g
那么让我们来试一下这段代码。
! r+ k9 H: X+ l4 @! `1 b' _* b [) s9 p% S. C' j9 [& ~' U5 p& M4 E4 P
6 i# l1 T2 c& @1 g* d( R! ?+ u1 |
6 u6 E. A& [& Z" d) W; s4 @可以看到,代码死住了。其实不难理解,我们发送数据的时候,由于缓存区只有一个字符的空间,我们发送八个字符的时候会导致缓存区溢出。所以我朋友的代码可以“正常运行”也是一件非常奇怪的事情。$ B8 _; ~1 b6 C% i+ x2 ]
7 `5 f" N& J- k1 s; A, i% L) h所以正确的做法应该是:设置和缓存区一样长的读取字符。; w2 t5 ^. Q+ ^- M! A/ x& {
* d4 _# j" y9 h! C/ ~- unsigned char RecieveBuffer[8];# ?( {3 i5 f3 q" v0 P9 B
- void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
" U2 x5 l/ }, u - {* d( G. N% h" f" ]/ x9 `
- UNUSED(huart);9 e B+ x+ ` W5 [
- HAL_UART_Transmit(&huart1,&RecieveBuffer,8 ,100); // 将接收到的数据再通过串口发送出去
* G# _# L+ X1 T6 I2 N/ w R) e' g5 C - HAL_UART_Receive_IT(&huart1,&RecieveBuffer,8); //使能接收中断,RecieveBuffer的类型是uint8_t
) V; z, R8 Y- R) z5 U4 Z) J- r - }
复制代码 : z$ L7 r2 K |1 g/ f4 S) l4 _
这样子就可以实现每接收8个字符就调用中断一次, \1 Y" H! Z) l7 e
3 V+ p! Y; J' P8 q; b
% ^) c# [3 J4 S1 L) M4 T
# Q4 N" R6 \5 e' M. a* V- A" d可以看到,我们正常的发送了8个字符。
0 N% K+ L! t. V5 j1 f* W7 O2 A. M7 h" p
但是这样子有一个非常非常致命的缺点!: v. N4 t$ x$ ]3 K! ~
1 `; y- o( N8 c3 U5 L" f! K由于我们的代码每8个才会接收一次,所以当我们的发送的数量小于8时,必须发送多次才能会触发一次调用。9 D: U* Q+ h0 b% ?, ~0 X, P* X' v& U
, D# v5 T) B1 ?: \% M5 h# X' R注意,这里的每次我们都是先发送再接收,因为初始化的时候是启用了接收。6 z- X6 ]9 t! s! w
; C! K& X; r; H8 _. P% Z
3 G& G- E# b# w3 L' ~
$ ~! j/ o! U8 D: s8 l当我们发送3次“123”时才能让接收的字符>=8,我们把前八个发送了出去,此时缓存区剩下了一个字符是“3”' D( @' d3 ], f
7 o! D& C; g9 c& k$ z0 l" D+ D* v
0 r' t# V) g8 K# W, J' f4 ] }
+ i6 Q X; t, d7 [0 U
第二次我们发送了三次“123123123”,再次让字符数量>=8,这时候把缓存区的八个“31231231”发送出去再接收后面的“23”,这时候缓存区的数据为:"23 "。
7 a- w& \* M7 e. _; T q0 h `/ [& ]% P! E- z. M
: P! w! D6 b' X% d/ [! \; P! _4 R# ^6 i8 j
最后我们第三次发送“123”的时候,由于23 + 123 + 123 这时候,虽然这里按理来说正好八个,但是实际测下来,当这时候接收的时候系统就会卡死。
* e% B( B1 F# Y$ C( X& }8 P" D
1 U7 o8 k5 G" E所以,实际上这种方法必须保证发送端的数据是完完整整的八个八个发送,多一个少一个都不行。
/ L, _; A8 U- _7 C0 D, ^0 F! x g
% V$ l' k, f7 Y% ]8 b e2 G7 {# y6 ?& J& S( z5 o/ a/ a5 N
解法4 b! a6 S- K. }( D z! y3 h
事实上最标准的做法是单个字符单个字符处理。定义一个大的缓存区,用来存储接收到的字符并且确立一个结束符来确定数据流的结束。- if((USART_RX_STA&0x8000)==0)//接收未完成" s1 @" O: s0 y3 V% v/ C
- {
0 i/ V, Z6 ~0 r0 [8 r - if(USART_RX_STA&0x4000)//接收到了0x0d6 ~9 T$ B% S i: T9 o
- {8 B4 f/ k$ C+ H/ K
- if(Res!=0x0a)USART_RX_STA=0;//接收错误,重新开始5 T3 e" E& [3 {* g$ N0 G' R$ v& B; N
- else USART_RX_STA|=0x8000; //接收完成了 ; k& @/ ]. C# w' f9 ?
- }
. R+ B1 \- j& P( k% Z% m - else //还没收到0X0D
% E, H8 N8 s) G, U' f* A - { 8 k k( V6 b4 `1 |' s
- if(Res==0x0d)USART_RX_STA|=0x4000;* S [' }! o: u4 X' c7 j8 W
- else) V, b6 q# C" Y0 I4 q! v0 l
- {
8 f3 Q) o" V T - USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;9 e0 R2 r) O. f6 g, f
- USART_RX_STA++;
. M! f# n; J# W( d% D8 o - if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收 ! m5 C& ^1 ?' q4 T4 i& A' |
- }
8 D ]) r& E/ U1 |% |0 w3 J - }
3 `( m% j Q4 \+ G; v% @; P% D3 j - }
复制代码 : _" o# j3 R1 b7 n6 c% s4 l
* m! h+ _; d) U* G+ p& y
9 P" p9 f0 q" R: P* T上述是正点原子的官方例程,其中USART_RX_STA是接收到的字符,即是确定一个结束符\r\n来作为一串字符的结尾并且检测长度是否超出长度。2 f. |0 m1 l' j; }) K4 H! b9 B) U
5 |& G4 e2 L6 k# S这样子的代码容错率非常高,也确定了一个规范,并且避免了缓存区溢出的情况。
8 i$ V3 p* t& D7 j9 e& C \. h! s" L
7 y# G& ~4 t, s+ v
转载自:电路小白+ P$ V1 T( `7 O3 \5 y1 W
如有侵权请联系删除+ [& |) c% \1 Y3 t& J' H5 t
0 x7 p* K, R- l/ c& c
|