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

基于STM32实战利用空闲中断从串口接收任意长数据经验分享

[复制链接]
攻城狮Melo 发布时间:2023-6-14 16:35

这次我们利用STM32F103的UART内部的空闲中断来实现对串口任意长数据的接收,通过简洁的手段解决了接收端在事前无法得知数据长度的问题。本次教程我们需要一块STM32核心板与一个USB转TTL工具。

一、原理介绍

STM32的异步串口接收寄存器可以存放1个字节,当我们开启接收中断(RXNEIE)时,当串口外设接收到一个字节的数据时 数据接收(RXNE)标志位置1,同时触发串口中断,此时我们可以把接收寄存器RDR中的数据转移至我们自定的缓存区中。

此种方式我们只能一个字节一个字节的接收数据,如果我们事先不知道需要接收的数据长度或未规定帧尾内容,我们便无法判断数据是否已经接收完毕。这时我们可以采用总线空闲标志(IDLE),使能空闲中断(IDLEIE)当检测到线路空闲时会触发串口中断,我们便知道一帧数据接收完成,可以送往上层应用中进行处理了。

二、代码编写

这里我们使用串口1,波特率初始化为115200,因为stm32f1的hal库似乎实现不完全,没有总线空闲相关的函数,我们这里自己实现下。如果使用的是stm32f4及以上可以使用函数:

  1. HAL_StatusTypeDef HAL_UARTEx_ReceiveToIdle_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
复制代码

/ c7 z, t1 N, E/ [+ S1 w0 S& H$ `2 O  f+ X( H

1、IO及UART初始化,并使能空闲中断:

  1. // UART.c
    : \. j4 G; R9 {  t4 ]2 d# l8 |
  2. & S9 y1 Z; ?4 d! x# P2 w
  3. int UARTInit(void)1 R" n% }+ t# S/ F3 f8 \
  4. {) h1 a- z" _* g: d, g& K
  5. GPIO_InitTypeDef GPIO_InitStruct = {0};: Z2 U) N5 R1 v3 e  `! e  h" Q
  6.     $ p% W  B7 H4 J6 L5 z+ e  F  O
  7.     /* IO初始化 */
    7 y2 k. N4 L' f) G) @. j
  8. __HAL_RCC_GPIOA_CLK_ENABLE();5 H/ ]! k: Z: l+ [( G
  9.    
    . k9 x  `6 z4 h/ ]. g5 H# Q
  10. GPIO_InitStruct.Pin = GPIO_PIN_9;0 l, _0 o4 l! @$ p
  11. GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    4 ?( j  b4 x& S, l5 x" P2 z. O, w
  12. GPIO_InitStruct.Pull = GPIO_PULLUP;% G! l; o" |' ]* v9 I3 i6 x  X& {9 h: X, B
  13. GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    8 f" g% D& C# h* y
  14. HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);9 |: `( K; ?) x7 L% X

  15. * z2 M3 X3 N8 ^4 `& ?4 e8 h+ F
  16.     GPIO_InitStruct.Pin = GPIO_PIN_10;1 h7 H" Z, @# W8 h5 t
  17. GPIO_InitStruct.Mode = GPIO_MODE_AF_INPUT;# g, d5 i8 H8 m$ h3 W; F
  18. GPIO_InitStruct.Pull = GPIO_NOPULL;, Z! }6 c- I9 f# Y+ q/ x
  19. GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;  N, H" E, r0 l4 S! l
  20. HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    2 L3 c, S. X+ ^# t
  21.    
    - P/ L0 _" j- H- g5 U
  22.     /* UART1初始化 */( R8 u+ |$ w4 Z: S  E7 o! X  u/ ~
  23.     __HAL_RCC_USART1_CLK_ENABLE();. t% ^9 B* F7 y8 K& R
  24.     3 m3 _7 I  j% V4 P2 _; R; E
  25.     UARTDev1.UART_Handle.Instance = USART1;; k( e( h  R9 a. Y( g) v' w
  26.     UARTDev1.UART_Handle.Init.BaudRate = 115200;
    ( L! T3 t8 d5 e9 g, J! x( M
  27.     UARTDev1.UART_Handle.Init.Parity = UART_PARITY_NONE;
      r; ^5 D$ R" s5 y
  28.     UARTDev1.UART_Handle.Init.WordLength = UART_WORDLENGTH_8B;
    * P4 [5 a4 c+ j3 N- g4 J- Z# F
  29.     UARTDev1.UART_Handle.Init.StopBits = UART_STOPBITS_1;
    . W9 `- S% T' e& ?" L- w% B0 ]
  30.     UARTDev1.UART_Handle.Init.Mode = UART_MODE_TX_RX;- r4 @9 B- Y" K8 Y, L# `& v
  31.     HAL_UART_Init(&UARTDev1.UART_Handle);  ~& F3 r6 F0 w8 H1 E! |
  32. 7 k+ l4 i' k4 b* r* H6 Y" k5 [* v
  33.     /* UART1中断初始化 */
      r, ]/ h/ A0 R- P) r' w
  34.     HAL_NVIC_SetPriority(USART1_IRQn, 0, 0);+ x1 H: \4 m9 V' V
  35.     HAL_NVIC_EnableIRQ(USART1_IRQn);0 t3 z0 ^5 l" y$ {. m  l
  36.     / L8 `4 W/ z  Y8 p; \6 q- A
  37.     8 Z/ F- x4 i1 I) E0 L2 T. K
  38.     /* UART1启动接收 */
      ~5 p" A! K' ~8 w2 s
  39.     HAL_UART_Receive_IT(&UARTDev1.UART_Handle, UARTDev1.RxBuffer, UART_RX_BUFFSIZE);* X5 L3 Q/ y8 ~1 T, a
  40.    
    - J, a8 _. E- l( Q: s: f: @
  41.     /** UART1开启空闲中断 **/
    0 x6 W) |$ d( D2 M: l3 X: Z! v
  42.     __HAL_UART_ENABLE_IT(&UARTDev1.UART_Handle, UART_IT_IDLE);
    , m. n' r, E- A3 i- O
  43.    
      J4 j  P: Y' l( A3 M8 w7 y
  44.     return 0;
    9 R' `! R1 m' ^" W' O- H. z0 M' Q' t
  45. }
复制代码

. J' q- B5 I* X

2、中断处理函数,这里接收完一帧数据后会原样发出去:

  1. // UART.c" i+ b6 I+ [. e/ e
  2. void USART1_IRQHandler(void)4 O) x4 L. m) Y% a$ u$ @: Y2 u
  3. {
    0 a  Y! S8 a( }; O8 @
  4.     UARTDef *huart = &UARTDev1;
    ' H" p$ u7 I# [2 W% K1 o4 L
  5.    
    & H; U  J0 A0 j) |: J3 M
  6. HAL_UART_IRQHandler(&huart->UART_Handle);9 X# I" {5 u/ }0 ~, c4 m- g3 q
  7.     /* F1的HAL库中未实现IDLE相关功能,固自行实现 */+ n8 ~% R0 ?: p# D7 i! S6 W
  8.     if(__HAL_UART_GET_FLAG(&huart->UART_Handle, UART_FLAG_IDLE))7 ?0 @8 o& K8 w8 |3 X
  9.     {   /* 接收到完整一帧数据 */5 _! q2 q  L# u+ t
  10.         UARTSendData(huart, UARTDev1.RxBuffer, UART_RX_BUFFSIZE - UARTDev1.UART_Handle.RxXferCount);' q: J9 X* R2 W0 _4 M
  11.         
    9 p6 J1 F% c9 L: m8 ~0 n
  12.         /* 重新启动接收 */
    2 r( e, t$ F$ h7 W! w, E& v8 a
  13.         HAL_UART_AbortReceive_IT(&UARTDev1.UART_Handle);
    / L6 w  G0 q0 \3 n
  14.         HAL_UART_Receive_IT(&UARTDev1.UART_Handle, UARTDev1.RxBuffer, UART_RX_BUFFSIZE);
    . P! O. a# r6 |! _9 f; u, m
  15.         /* 清空闲中断 */
    : ~, m' `/ x  k: ?
  16.         __HAL_UART_CLEAR_IDLEFLAG(&huart->UART_Handle);
    & e7 ^3 |$ |3 ~" {
  17.     }- A6 s% T. _% `! M& b  z
  18. }
复制代码

1 F0 z" E8 ~3 b* ]) ]" b3 A; ~

3、主函数里别忘了添加初始化函数,因为所有处理都在中断中完成了,这里主循环里就放了个led闪烁的代码:

  1. int main()1 D- R$ u6 ]1 v/ d
  2. {7 Q& n" K' @5 ^
  3. HAL_Init();' b, }* y4 G5 k
  4.     SystemClockInit();
    - Y" S# O/ I; N  f
  5.     GPIOInit();! W" |) Q: l8 ]- i3 N
  6.     UARTInit();! F- I. H! Y: o
  7.    
      t" \5 R; n, p
  8. while(1)1 r" D9 e3 t2 U; X
  9. {
    , Z0 ]  J* x3 W: y, m
  10.         HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_8);4 m) G5 m, f# W& n; g6 Y
  11.         HAL_Delay(500);
    ( K4 L9 D* c) ~8 W9 s/ d
  12. }
    7 E  h" ~* b2 g
  13. }
复制代码

+ c3 N( h# @4 J: M0 r1 F9 n三、效果演示

我们当前实现了从串口1接收了一帧数据后原样返回的功能,我们拿出USB转TTL模块,把RXD、TXD分别与板子的PA9、PA10连接,打开串口调试助手,设置好对应波特率,发送一条信息可以看到我们收到了相同的字符。

0 h0 M3 H: I$ t5 m! l

微信图片_20230614163425.png

3 Z$ x1 Q9 |" v- l/ Y% m

完整代码可以从文章最后方下载,不同文章的代码在不同branch。下次我们讨论下SPI的使用。


1 g2 n+ D# K+ e3 |+ B* N1 k

转载自:嵌入式技术栈

如有侵权请联系删除

. ~7 `9 I7 _% p, H5 `

9 d/ D! @% i% I  H! j* p  m
赞 收藏 评论1 发布时间:2023-6-14 16:35

举报

1个回答
bitterheart 回答时间:2023-6-15 11:27:22

学习了

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版