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

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

[复制链接]
攻城狮Melo 发布时间:2024-5-29 19:53
一个关于STM32串口的问题。他想实现接收无限制字符数量的串口信息。
3 x; G5 \% D/ f: G1 Y2 O' W6 a; x. e: V7 `2 u7 l* c
但是他遇到了一个问题,他在主函数中发送循环发送内容(500ms的延时)串口一中断回调函数中如果收到了串口内容,就利用串口发送。
! `: a7 E2 s, R4 v7 p* J" ~9 S+ W6 X- b0 U1 |  M2 i- s) c
但是他的串口一旦接收到了信息就会导致主循环中的串口发送极快。他的主代码如下
. m; I: K6 t2 E. a
  1. unsigned char RecieveBuffer;9 O' J. W& M" k- y5 r* x. ~
  2. void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart): U3 t. }  ?8 F: e
  3. {% o5 u* i2 U  s8 p! t, j8 n
  4.   UNUSED(huart);- o" @, z- l# u4 k, o1 D5 f5 Y
  5.   HAL_UART_Transmit(&huart1,&RecieveBuffer,8 ,100); // 将接收到的数据再通过串口发送出去
    , \7 ]8 i6 h- r% ^  Q! H( J& i
  6.   HAL_UART_Receive_IT(&huart1,&RecieveBuffer,8); //使能接收中断,RecieveBuffer的类型是uint8_t9 i+ e) z6 [  C
  7. }
复制代码
+ }! l1 W* N, P& E! K
看了他的代码,就这么短短的7行代码,让我汗流浃背。- v: y+ i7 Y- j( m3 X4 q. E
# l  B6 `- p2 L( L8 K' L4 }6 @
他的发送和接收中有一个参数是8,这意味着当串口接收的寄存器中有8个数据时就会触发中断回调。( ^. y: n, A, }
6 z& x8 W5 z1 @, ~+ E2 q
我们需要注意的是,串口寄存器的数据会被存储到缓存区中,这里的缓存区相当于一个队列先进先出,当我们调用HAL_UART_Receive_IT(&huart1,&RecieveBuffer,8);& q) f$ @. R6 S1 |% h, l

7 O& p$ U, Q5 o) _3 X
会将RecieveBuffer作为缓存区,将串口接收的信息存入缓存区。
3 ~- v$ a4 J) M; o: L8 }$ N; N5 c7 c/ \1 F/ t5 V+ c2 O
那么让我们来试一下这段代码。
5 [. c3 w; v9 v$ }
* p1 O* @6 g* k
微信图片_20240529195314.png
+ k$ S+ b) M0 g. B8 B! d
8 h& C: g2 {- z9 D8 O, q
可以看到,代码死住了。其实不难理解,我们发送数据的时候,由于缓存区只有一个字符的空间,我们发送八个字符的时候会导致缓存区溢出。所以我朋友的代码可以“正常运行”也是一件非常奇怪的事情。9 r& R# G3 x" O* `5 _  C! k2 `

! I- P/ p% U6 Z* ^所以正确的做法应该是:设置和缓存区一样长的读取字符。$ N* m1 D) `; b% i( `
2 m* p9 ?3 U2 {' y* F6 Z0 J
  1. unsigned char RecieveBuffer[8];1 e" h. u5 `- t3 y9 Y
  2. void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
    9 c2 _8 n  }7 i
  3. {
    2 I5 M2 \" J! n- }. B" E# w# l
  4.   UNUSED(huart);7 q  }+ s' E* M& D
  5.   HAL_UART_Transmit(&huart1,&RecieveBuffer,8 ,100); // 将接收到的数据再通过串口发送出去+ w  c5 o* S4 C7 E1 a  q; H9 W
  6.   HAL_UART_Receive_IT(&huart1,&RecieveBuffer,8); //使能接收中断,RecieveBuffer的类型是uint8_t! P' h. y" i" C/ }; V) w, _* y* o
  7. }
复制代码

$ {5 G" i+ e" q1 j2 S+ |" @这样子就可以实现每接收8个字符就调用中断一次
% Q- S, o2 [$ n- E
+ L% }& W4 X2 X3 P! `% T
微信图片_20240529195311.png
3 M, j4 p4 }# i" l
+ Z$ b7 b* ^6 l
可以看到,我们正常的发送了8个字符。1 G+ |; `4 @  ~, f  h& k' E

4 U- d; Q2 ]3 _: d6 @$ I但是这样子有一个非常非常致命的缺点!
- @/ `1 j2 U7 c9 J* B4 h9 h" H! Z6 ]/ ?
由于我们的代码每8个才会接收一次,所以当我们的发送的数量小于8时,必须发送多次才能会触发一次调用。
8 y) }' O$ V% x. O5 O
# q0 a9 ^4 t0 G0 d注意,这里的每次我们都是先发送再接收,因为初始化的时候是启用了接收。
0 S% \7 G3 `: G2 ]: s* M& Z! B2 `$ ~9 q; C2 j' \
微信图片_20240529195307.png
; A$ Q, D- I, Q6 u* @! e. y

  }7 ~: v& e/ m/ s当我们发送3次“123”时才能让接收的字符>=8,我们把前八个发送了出去,此时缓存区剩下了一个字符是“3”: L6 x* q& H8 C' T
: b9 f: {8 E" i$ Y5 e7 {- d& A
微信图片_20240529195305.png
, ~5 K2 B( j: d  y# H9 ]9 Y

% g  ?& ?( ~6 |3 G8 o第二次我们发送了三次“123123123”,再次让字符数量>=8,这时候把缓存区的八个“31231231”发送出去再接收后面的“23”,这时候缓存区的数据为:"23 "。
% ^  B4 p* [# f# p3 L( z! k! t) B9 C, t- s6 ], K7 Y0 C0 x! a9 H% W) q9 Q% Y
微信图片_20240529195302.png
0 S4 n$ q, P/ @

! C' s0 |% H  x! c1 f/ ?最后我们第三次发送“123”的时候,由于23 + 123 + 123 这时候,虽然这里按理来说正好八个,但是实际测下来,当这时候接收的时候系统就会卡死。
% `! ~, e+ c# y1 u" S/ M
, r8 @2 \' N* ?: j7 e% V所以,实际上这种方法必须保证发送端的数据是完完整整的八个八个发送,多一个少一个都不行。4 y- e( T( N. ]
2 _  X8 q' N9 j7 i

2 C! @; n5 W5 F0 q) p$ S解法, e; R) b& S" r/ {4 z8 k' ~: \8 Z# D
事实上最标准的做法是单个字符单个字符处理。定义一个大的缓存区,用来存储接收到的字符并且确立一个结束符来确定数据流的结束。
  1. if((USART_RX_STA&0x8000)==0)//接收未完成  a1 v' s% \9 t9 t
  2. {
    % B5 H3 c5 F8 Q' Z: o6 }
  3.   if(USART_RX_STA&0x4000)//接收到了0x0d3 p+ w2 I$ }7 O5 j
  4.   {
    5 j! Y2 S$ A* c' Q; v5 C6 R
  5.     if(Res!=0x0a)USART_RX_STA=0;//接收错误,重新开始. r7 ?0 X, o( e; A8 {5 k9 h& l7 D. _
  6.     else USART_RX_STA|=0x8000;  //接收完成了
    $ l$ X/ N) a9 A
  7.   }
    , n1 Q- l9 D" ]. O! @, V9 v4 L
  8.   else //还没收到0X0D
      M2 Y/ ^/ d4 q* j, C
  9.   {  
    $ m" O; K3 _( H, _  T
  10.     if(Res==0x0d)USART_RX_STA|=0x4000;) v6 j3 ~. v0 d  B+ r
  11.     else
    5 N% n2 |  x+ @0 i. y
  12.     {
    , `  ^" ?( V* \9 f2 W! P
  13.       USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;0 E  i  i1 d9 x8 w. j/ x1 c' \' A
  14.       USART_RX_STA++;
    : c8 i* L( q( O
  15.       if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收    0 P! a% @/ K" g+ T2 }# b# i* q2 m
  16.     }     $ q  J: j" N( b9 o
  17.   }
    4 R6 M% k7 _( U- ]. K+ ^( N
  18. }
复制代码
' S" C3 E/ S+ ]$ O
( H0 a" c+ k% r. B1 W

* K; u. I( j" E上述是正点原子的官方例程,其中USART_RX_STA是接收到的字符,即是确定一个结束符\r\n来作为一串字符的结尾并且检测长度是否超出长度。

7 D6 K! v- E7 O/ l4 H2 Z) u
. H7 |4 v/ ^' J1 s  m4 M这样子的代码容错率非常高,也确定了一个规范,并且避免了缓存区溢出的情况。
4 [3 x" y( x9 w  R8 `
3 w: Y% e; U) P0 R. k  r3 X5 v1 C( F! Q" K6 Z  R$ z# U
转载自:电路小白: Q9 {9 `% z8 M- r2 ^% D2 b% F
如有侵权请联系删除
/ n$ ~! G+ ~1 _! o! w" n" k
. q: H4 U; X* T: T3 F  Z/ N0 o  A
收藏 评论0 发布时间:2024-5-29 19:53

举报

0个回答

所属标签

相似技术帖

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版