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

基于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);
复制代码

$ i5 _( ~  i9 n& O; r/ x7 H9 b- F6 X. L" N" O; m7 g8 d& b3 m

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

  1. // UART.c) Z, j9 d4 q, d/ e$ m; I# N

  2. 7 _0 ^8 a& b  k0 I: _$ ]
  3. int UARTInit(void)
    , d: I$ v( Z) f& T7 Y! V
  4. {
    ; M  u6 K+ }  ]* R' z
  5. GPIO_InitTypeDef GPIO_InitStruct = {0};7 C2 v* a' N. B, d
  6.     7 j' M% E4 J# c# T" h4 }
  7.     /* IO初始化 */* b3 ]5 Y( D" j7 v2 S
  8. __HAL_RCC_GPIOA_CLK_ENABLE();
    6 Z3 @3 s' ~, ], p* f' m
  9.     7 N; ~9 g3 d0 y8 w% ~: C
  10. GPIO_InitStruct.Pin = GPIO_PIN_9;
    $ {2 I7 h  O/ R7 q- g. T/ F8 T
  11. GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;8 @2 v! f" G1 j/ q) w& E
  12. GPIO_InitStruct.Pull = GPIO_PULLUP;
    5 R5 X" }4 c) G! [! ]  Q, f
  13. GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    2 M9 F. Y7 a4 G& I3 Y
  14. HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    0 g, d8 q2 b0 ~9 \( k

  15. 9 I" w8 f3 B# t2 ?- m
  16.     GPIO_InitStruct.Pin = GPIO_PIN_10;7 J/ E  f9 P- b* {) q7 C
  17. GPIO_InitStruct.Mode = GPIO_MODE_AF_INPUT;6 Y! M+ w* l4 ]3 i
  18. GPIO_InitStruct.Pull = GPIO_NOPULL;
    9 j' n) d# M' k/ C& d/ ~
  19. GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    ; Y& p' u9 B( H4 p
  20. HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    4 \. ?3 Y7 l: Q: d+ b
  21.     5 ^) ~, p* |. `/ r% p$ g+ n  J
  22.     /* UART1初始化 */
    ; R, h! j) {- X; w
  23.     __HAL_RCC_USART1_CLK_ENABLE();
    8 \: y7 A$ M# t, ^; Q9 c% g/ Q
  24.    
    : J. p& x; Y/ n
  25.     UARTDev1.UART_Handle.Instance = USART1;
    2 [8 e2 `6 A2 |6 A& i9 V8 t: f  V
  26.     UARTDev1.UART_Handle.Init.BaudRate = 115200;
    4 y& i0 x& _7 s- W3 x# N5 K
  27.     UARTDev1.UART_Handle.Init.Parity = UART_PARITY_NONE;4 Z) k! ]( D" `' Z! B
  28.     UARTDev1.UART_Handle.Init.WordLength = UART_WORDLENGTH_8B;) B! d3 m+ |) v& p
  29.     UARTDev1.UART_Handle.Init.StopBits = UART_STOPBITS_1;' O# Q4 U; K8 ^8 S9 M
  30.     UARTDev1.UART_Handle.Init.Mode = UART_MODE_TX_RX;
    * O1 j- n" H; R/ I+ }; r
  31.     HAL_UART_Init(&UARTDev1.UART_Handle);
    ; y9 Y* G! p7 B- L  x. r
  32. # I& z  B+ a( r9 `9 h/ [* T
  33.     /* UART1中断初始化 */
    8 a# P# `, ?8 i/ i# x- F
  34.     HAL_NVIC_SetPriority(USART1_IRQn, 0, 0);
    2 f$ S% _  ]4 [& k5 c: c  O
  35.     HAL_NVIC_EnableIRQ(USART1_IRQn);
    % r+ V1 T/ a) @. X7 O6 s
  36.     4 ~1 z) {' F9 k1 k3 h( e
  37.     1 D. l& c% ?. o) j" S
  38.     /* UART1启动接收 */
    1 M: f# \9 r) P% P2 Q7 i4 g& J
  39.     HAL_UART_Receive_IT(&UARTDev1.UART_Handle, UARTDev1.RxBuffer, UART_RX_BUFFSIZE);% P* h: s! k$ l- N* U
  40.     4 G: ]  f* K- F% X4 B0 }
  41.     /** UART1开启空闲中断 **/6 h$ \3 h" C) P, D' \7 m
  42.     __HAL_UART_ENABLE_IT(&UARTDev1.UART_Handle, UART_IT_IDLE);
    , O/ @- J: C4 [
  43.     3 P( ]0 k  q4 X  V5 B/ h
  44.     return 0;
    - ]- n- L( M0 u
  45. }
复制代码
. i% L9 W. N7 O" v

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

  1. // UART.c
    2 A4 G4 V% S# s. o8 Z+ h
  2. void USART1_IRQHandler(void)
    4 V+ v; |! Y* c' l
  3. {
    " y2 w0 M/ F6 ]5 Q: o, P' r
  4.     UARTDef *huart = &UARTDev1;" F8 V; f! L# t3 ?  k
  5.     9 ~2 T! [$ B9 l& W
  6. HAL_UART_IRQHandler(&huart->UART_Handle);! f. g% U5 @& v7 B1 i+ s7 n3 Y
  7.     /* F1的HAL库中未实现IDLE相关功能,固自行实现 */
    3 W- w$ P- ]- j1 ]$ }7 [% X: ~
  8.     if(__HAL_UART_GET_FLAG(&huart->UART_Handle, UART_FLAG_IDLE))
    , e4 Z4 e  \* |. Z
  9.     {   /* 接收到完整一帧数据 */
    7 a. a- [6 J4 ]" B0 }1 ?
  10.         UARTSendData(huart, UARTDev1.RxBuffer, UART_RX_BUFFSIZE - UARTDev1.UART_Handle.RxXferCount);
    $ _+ O6 h5 X. D" x
  11.         
    3 f/ {2 a  [$ o2 L$ T5 a- v
  12.         /* 重新启动接收 */4 S4 Q/ A& R$ o; ?
  13.         HAL_UART_AbortReceive_IT(&UARTDev1.UART_Handle);+ l( U$ |6 M$ w' [& p' N' _
  14.         HAL_UART_Receive_IT(&UARTDev1.UART_Handle, UARTDev1.RxBuffer, UART_RX_BUFFSIZE);
    . H# k  _7 `# E/ q$ J; @
  15.         /* 清空闲中断 */
    ' v8 h' f' C8 G. K' r; D/ m
  16.         __HAL_UART_CLEAR_IDLEFLAG(&huart->UART_Handle);3 Q& ]3 D" B" ?' W( Z$ I
  17.     }
    , E8 K* ^: t2 [# ^# f  ~: m
  18. }
复制代码
0 O2 e. z- G- h; H! y3 G# s4 Y' o

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

  1. int main()
    8 k3 n* O; D6 m! p% Q. D
  2. {4 e/ B$ `/ @) Z' V
  3. HAL_Init();
    1 g* f2 ?; L" R8 c7 o7 C  u; L
  4.     SystemClockInit();
      @) w$ S2 K8 L( X9 u
  5.     GPIOInit();& F. V( C, a* }/ W* ?+ @, U, Q
  6.     UARTInit();
    # L& l2 x& |% P
  7.    
    2 X8 q8 x# t. z! d
  8. while(1)9 I  @' n( C) _# Z; B$ Q1 B
  9. {, n7 F% t5 j7 @# ]2 i
  10.         HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_8);6 @$ r% B6 o8 y% K' y
  11.         HAL_Delay(500);
      w' W% J# j8 l. b
  12. }. J. F; u; h  s9 s4 a
  13. }
复制代码
& O" d6 {2 O3 _
三、效果演示

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


3 b+ J; K' E; d& i7 O

微信图片_20230614163425.png


# H; S6 `7 I, v) z; f% X: j

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

7 A* a6 `; P, h- Y

转载自:嵌入式技术栈

如有侵权请联系删除


. Z4 G8 c: D9 ?4 \% e4 z0 a! z1 L( E
赞 收藏 评论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 手机版