你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

基于STM32串口中断之缓存区溢出卡死经验分享

[复制链接]
攻城狮Melo 发布时间:2024-5-29 19:53
一个关于STM32串口的问题。他想实现接收无限制字符数量的串口信息。9 r9 K* i. k2 ]% D5 F

6 a+ D, y3 M1 J! S
但是他遇到了一个问题,他在主函数中发送循环发送内容(500ms的延时)串口一中断回调函数中如果收到了串口内容,就利用串口发送。' N5 x! F( k& n; S0 D; b7 N4 i
5 j0 l0 h+ f& d( U2 D. E1 R5 L9 y) b
但是他的串口一旦接收到了信息就会导致主循环中的串口发送极快。他的主代码如下
/ D, x1 u; `5 X2 _( Y7 B" T8 ?
  1. unsigned char RecieveBuffer;. ~2 c' Q( ~2 D0 Y' n) g2 S
  2. void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
    , W+ I% U8 ~% ]( @/ R4 l; _
  3. {
    0 V4 {4 Y; o- C9 b, F
  4.   UNUSED(huart);0 M, A/ G9 h- H. W  C
  5.   HAL_UART_Transmit(&huart1,&RecieveBuffer,8 ,100); // 将接收到的数据再通过串口发送出去
    9 w2 c" t: K  [
  6.   HAL_UART_Receive_IT(&huart1,&RecieveBuffer,8); //使能接收中断,RecieveBuffer的类型是uint8_t3 A2 b: L& A# a% r+ x, g$ O1 Q. e
  7. }
复制代码
6 G" ?( }1 ^% _
看了他的代码,就这么短短的7行代码,让我汗流浃背。4 y5 f- n6 }, g+ d4 D- f

# v3 u$ N" o9 [0 {" r
他的发送和接收中有一个参数是8,这意味着当串口接收的寄存器中有8个数据时就会触发中断回调。
4 X5 C+ g& F' N! Z) [5 B2 t( k5 Z( `0 ~2 D. \5 m
我们需要注意的是,串口寄存器的数据会被存储到缓存区中,这里的缓存区相当于一个队列先进先出,当我们调用HAL_UART_Receive_IT(&huart1,&RecieveBuffer,8);! I* U" ^+ x0 p: L6 A, T
9 ]; K. M( l4 A; P
会将RecieveBuffer作为缓存区,将串口接收的信息存入缓存区。
* M( I  ~, ]! Y) }) `7 i8 j9 ?3 U+ p, `( L1 K; U; y  G3 x  u# r0 s! p
那么让我们来试一下这段代码。
% |8 l9 a6 c. l7 j& {2 o3 N$ `7 N) _) F! g# l
微信图片_20240529195314.png
! p- s% e  i4 }9 q# c/ @& `$ [! d! J
9 y, l, z8 X- H& R
可以看到,代码死住了。其实不难理解,我们发送数据的时候,由于缓存区只有一个字符的空间,我们发送八个字符的时候会导致缓存区溢出。所以我朋友的代码可以“正常运行”也是一件非常奇怪的事情。
3 l7 s9 o: T/ ?9 B/ h6 l" }; o% N# D9 h  q* {
所以正确的做法应该是:设置和缓存区一样长的读取字符。
/ h3 |' [. ?# H
( O- `5 V  v; @( p. r, O; J
  1. unsigned char RecieveBuffer[8];
    . E: O6 s( [  ]& s& K
  2. void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)0 v& ^3 G* e$ o% t  V9 F
  3. {/ R) |. p+ Q* u9 }# ^
  4.   UNUSED(huart);
    1 ~3 |; j0 m6 j
  5.   HAL_UART_Transmit(&huart1,&RecieveBuffer,8 ,100); // 将接收到的数据再通过串口发送出去
    1 f! H0 c* `" S
  6.   HAL_UART_Receive_IT(&huart1,&RecieveBuffer,8); //使能接收中断,RecieveBuffer的类型是uint8_t5 j, V  s  @5 K" k( N' d0 p. s
  7. }
复制代码
+ M5 I% y/ K- m; X# [. {) |7 r
这样子就可以实现每接收8个字符就调用中断一次5 R* o, z* p% F" |5 }8 R

; V$ A' t. @' A2 v* F
微信图片_20240529195311.png 0 _4 Z; J7 E' p8 X# ?

, B2 O( d4 t  m( E9 ^( Z' h1 S- V可以看到,我们正常的发送了8个字符。
: Z6 s1 b3 V% F2 ?* p- M
! f3 l% \- S1 j% P8 k$ c0 v0 J$ `但是这样子有一个非常非常致命的缺点!" U. ~6 I& u7 o

5 u- Z$ w+ }; I5 `2 p由于我们的代码每8个才会接收一次,所以当我们的发送的数量小于8时,必须发送多次才能会触发一次调用。& b6 ?7 t+ M* L( {3 }
- H  W! k9 }' {) b
注意,这里的每次我们都是先发送再接收,因为初始化的时候是启用了接收。( w) {" D# u4 ]7 P  F- l
3 v# B1 u3 Y, e  f. W
微信图片_20240529195307.png % e! s" h, G1 |# i
) C7 Q: K5 j; L1 ^! u
当我们发送3次“123”时才能让接收的字符>=8,我们把前八个发送了出去,此时缓存区剩下了一个字符是“3”
6 V4 S* m1 V! ?, H5 l; x* b. N
, m* |2 I8 Q7 ~- o9 d0 l8 l
微信图片_20240529195305.png + |3 y6 h* ]  A) v7 Q! i

5 O1 r+ ?- I5 y% X  Y第二次我们发送了三次“123123123”,再次让字符数量>=8,这时候把缓存区的八个“31231231”发送出去再接收后面的“23”,这时候缓存区的数据为:"23 "。
) `4 J  ^( }7 b* Y% G8 d. c' s, q8 f5 d6 P8 D
微信图片_20240529195302.png ! k6 P5 j  E0 W- b( g- c7 L
  Q; a0 @0 R8 h
最后我们第三次发送“123”的时候,由于23 + 123 + 123 这时候,虽然这里按理来说正好八个,但是实际测下来,当这时候接收的时候系统就会卡死。
0 S8 s+ E, P1 a6 Z* g/ I( J  K8 W, H; ^9 I- p* ?$ v8 u% [  d
所以,实际上这种方法必须保证发送端的数据是完完整整的八个八个发送,多一个少一个都不行。
+ E" i. g6 `7 ~
' H) m: {& Z% C. J% N2 V
3 x" G! e7 P5 A* e
解法$ t4 D$ e# e! K) I1 q- F
事实上最标准的做法是单个字符单个字符处理。定义一个大的缓存区,用来存储接收到的字符并且确立一个结束符来确定数据流的结束。
  1. if((USART_RX_STA&0x8000)==0)//接收未完成
    / f& D1 \# J9 {0 m# j/ K0 `
  2. {
    # l2 e/ D  t1 Y9 s8 g+ Y" Z+ s& v
  3.   if(USART_RX_STA&0x4000)//接收到了0x0d
    0 e  p9 d4 @0 i
  4.   {- R  m3 E& @! X3 a9 c, X
  5.     if(Res!=0x0a)USART_RX_STA=0;//接收错误,重新开始6 h! w" o2 G  R& _8 G
  6.     else USART_RX_STA|=0x8000;  //接收完成了
    ; q" ]* _- o) f# x- o4 D# Y+ ]. ]
  7.   }
    " X0 F1 k; [: {2 g6 a' S1 C3 P
  8.   else //还没收到0X0D
    , b+ [  S, B& e
  9.   {  
    , f( Y* E1 s$ [2 C5 W+ o
  10.     if(Res==0x0d)USART_RX_STA|=0x4000;# J$ }) n( ^9 T' e
  11.     else
    ( G1 ?, q9 {) U: X% o
  12.     {
    6 R" a2 h4 }5 R- O5 l
  13.       USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;8 T+ |5 v) \- G5 \$ K
  14.       USART_RX_STA++;
    ' n) z! e9 Z# \: I1 u
  15.       if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收   
    ' l# s5 r2 b' ^2 T
  16.     }     
    6 _5 G6 G- R+ e5 Q* C0 y
  17.   }& S$ a3 d/ ^" D' g
  18. }
复制代码

! P5 |3 p! L1 y8 @; h) U2 }5 }; W. }8 K! @+ e' t3 W- {7 V
; n7 K* x4 Z# A; l; U  [
上述是正点原子的官方例程,其中USART_RX_STA是接收到的字符,即是确定一个结束符\r\n来作为一串字符的结尾并且检测长度是否超出长度。
! f, m. o" c3 h6 c# U' f  w

) [1 e4 y. Q0 B  h这样子的代码容错率非常高,也确定了一个规范,并且避免了缓存区溢出的情况。
0 s$ j4 g4 Q8 i$ R4 I
) G* E* K7 m2 I3 L  Q8 H# }$ b. [2 |7 w' Z4 }! }
转载自:电路小白
$ L5 V6 j# Z: s$ s4 a* p$ S) y如有侵权请联系删除
9 C' |+ V8 A& G% n* X0 Q% v- g: X
收藏 评论0 发布时间:2024-5-29 19:53

举报

0个回答
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32Cube扩展软件包
意法半导体边缘AI套件
ST - 理想汽车豪华SUV案例
ST意法半导体智能家居案例
STM32 ARM Cortex 32位微控制器
关注我们
st-img 微信公众号
st-img 手机版