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

基于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);
复制代码
5 j8 ~; R4 l$ q  T& _) D

0 E: X* }6 o) j& O

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

  1. // UART.c8 {4 g: f8 s9 s4 ~& C
  2. 6 |1 n8 j1 }9 p" k+ Y
  3. int UARTInit(void)
    ) P' v5 [" u; f$ L% r2 i
  4. {
    6 a9 ]" I$ Z! F+ ^- P
  5. GPIO_InitTypeDef GPIO_InitStruct = {0};4 e, M! y& O5 y" O
  6.    
    * e% p  w3 d' M; B( m; ^+ z
  7.     /* IO初始化 */
    " {8 z8 {& W" E' U$ p2 y2 R& A
  8. __HAL_RCC_GPIOA_CLK_ENABLE();
    2 x' j. X, O; h' f
  9.    
    ( \7 t1 a+ Z5 h, M% I
  10. GPIO_InitStruct.Pin = GPIO_PIN_9;
    ( [' t3 A9 k7 i3 \* a/ P1 ~
  11. GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    # V8 j: F/ a) d" V9 x
  12. GPIO_InitStruct.Pull = GPIO_PULLUP;
    ( `$ V: o2 ^0 r1 q8 e
  13. GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;" H0 z1 _' J7 U0 h
  14. HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    " h% p& R7 o9 C2 r+ m' E( a
  15. ; U, \  ~4 o" F4 t) ^& h
  16.     GPIO_InitStruct.Pin = GPIO_PIN_10;/ k9 l1 n' g& F
  17. GPIO_InitStruct.Mode = GPIO_MODE_AF_INPUT;
    * }$ j6 I3 `2 p/ f) b) H+ `
  18. GPIO_InitStruct.Pull = GPIO_NOPULL;  T0 Y3 g- E, y- q
  19. GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    6 j4 s. m  k& {1 y
  20. HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);. x( R& Z  x  d6 m/ w
  21.     . Z  ^1 s. G0 _+ o0 T3 L
  22.     /* UART1初始化 */+ B# k/ O& A  d: G  M" [/ w9 K
  23.     __HAL_RCC_USART1_CLK_ENABLE();' J5 f  m  |8 Q6 k
  24.     ! g8 q% P0 D0 B5 r
  25.     UARTDev1.UART_Handle.Instance = USART1;1 w. Y( x% Y7 e8 l% b4 B# x9 ~
  26.     UARTDev1.UART_Handle.Init.BaudRate = 115200;
    ! h6 U& m- ~) \) |, D
  27.     UARTDev1.UART_Handle.Init.Parity = UART_PARITY_NONE;
      o) A. l. r# h( @( N
  28.     UARTDev1.UART_Handle.Init.WordLength = UART_WORDLENGTH_8B;
    0 ]  N' C9 G; d6 l- H* O% L
  29.     UARTDev1.UART_Handle.Init.StopBits = UART_STOPBITS_1;! i6 ]; C8 B' K( X0 ~
  30.     UARTDev1.UART_Handle.Init.Mode = UART_MODE_TX_RX;
    & W, V. i2 g' i" n+ Z' S% w
  31.     HAL_UART_Init(&UARTDev1.UART_Handle);
    5 c; L% g) t% I# v6 a9 Q: k3 F$ y
  32. " _) R$ ^. K" @; _0 {. C& A& h
  33.     /* UART1中断初始化 */* f0 ^% R" z% q- \0 T
  34.     HAL_NVIC_SetPriority(USART1_IRQn, 0, 0);* g2 x. m. M. f4 i/ Y3 X
  35.     HAL_NVIC_EnableIRQ(USART1_IRQn);5 Q0 Z0 l$ D# @9 |! L% U' S3 S
  36.     , U* e" E1 R3 _( S& M' n4 M) K* d
  37.     ' M% I  v8 X5 o" `: V6 ]5 G$ ]! s! a
  38.     /* UART1启动接收 */& \& c# k* C3 g; n- r5 H
  39.     HAL_UART_Receive_IT(&UARTDev1.UART_Handle, UARTDev1.RxBuffer, UART_RX_BUFFSIZE);# s- w! f4 d7 L8 B% B9 t
  40.     - j0 j! }  b4 g' ^0 u, i
  41.     /** UART1开启空闲中断 **/
    0 q5 z" s* ?( K! ~5 J. B
  42.     __HAL_UART_ENABLE_IT(&UARTDev1.UART_Handle, UART_IT_IDLE);/ j' T5 h5 u; E# A" q' U
  43.     : d0 h0 h9 s2 G' o
  44.     return 0;/ [6 ]% C( k6 ?$ e$ ?% g
  45. }
复制代码

/ }* R7 q8 @9 M3 X

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

  1. // UART.c
      j  o/ w  G; N, p+ i4 ?
  2. void USART1_IRQHandler(void)
    3 j* u6 R$ E/ a. \
  3. {
    - P# C: k+ h2 d0 a' r( a
  4.     UARTDef *huart = &UARTDev1;/ ?" Y6 T+ `0 Q
  5.    
    3 Q$ m9 W% g) E
  6. HAL_UART_IRQHandler(&huart->UART_Handle);7 F4 d( j, i2 M9 h1 [7 B
  7.     /* F1的HAL库中未实现IDLE相关功能,固自行实现 */0 W' s: x" _6 R1 Z0 S
  8.     if(__HAL_UART_GET_FLAG(&huart->UART_Handle, UART_FLAG_IDLE))
    & O" f2 ^! `$ f+ K/ |& v7 [
  9.     {   /* 接收到完整一帧数据 */
    0 R5 Y& D" t0 `+ W. U; u/ ]' H1 V
  10.         UARTSendData(huart, UARTDev1.RxBuffer, UART_RX_BUFFSIZE - UARTDev1.UART_Handle.RxXferCount);. T- D' s8 s% ~0 a1 @; f
  11.         
    6 h$ @0 \# I1 C' E
  12.         /* 重新启动接收 */2 ^  W$ G/ t! j, S2 B' Y
  13.         HAL_UART_AbortReceive_IT(&UARTDev1.UART_Handle);
    ( a: O7 P! i! {4 b0 L# N- J: x
  14.         HAL_UART_Receive_IT(&UARTDev1.UART_Handle, UARTDev1.RxBuffer, UART_RX_BUFFSIZE);. p8 l+ s, H3 W7 Q& H, }6 U  j
  15.         /* 清空闲中断 */
    8 [: v2 \: `* I" U
  16.         __HAL_UART_CLEAR_IDLEFLAG(&huart->UART_Handle);
    4 M% F5 s* A# ~* E
  17.     }
    - c3 j6 ]  B+ _2 U  T
  18. }
复制代码
/ Y* z* N( v7 t6 n3 a! Q* z. b

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

  1. int main()
      w# F% v/ R1 H
  2. {+ i! R$ C7 O4 t# F
  3. HAL_Init();
    # h8 K" @9 Q+ V4 Z  ]
  4.     SystemClockInit();+ ^+ f* ]$ e7 Y0 e
  5.     GPIOInit();! w. k0 T* f3 H6 V
  6.     UARTInit();* m' }' Z" ?, n
  7.    
    / V1 ?( A, J3 N  ?' O
  8. while(1)1 L$ E# @, y9 O- [8 B. j2 g: h
  9. {% N. g" x8 S, t: ]4 W6 D) w
  10.         HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_8);" N0 p+ U5 E. p1 O5 c# y5 I
  11.         HAL_Delay(500);
    ( f7 L+ `* N( G) \! @# B
  12. }
    ( |+ X+ V7 F8 x6 i; i8 v
  13. }
复制代码

% ^7 J; z5 K1 Z) S" @# y8 e1 j三、效果演示

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


2 b, ~2 N: W! L, U8 y: n" r

微信图片_20230614163425.png


# e- k! u5 e. G# P2 }4 i

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


, ^7 j. H7 G& u" l2 |

转载自:嵌入式技术栈

如有侵权请联系删除

0 ]4 b% v: k& \% w* F8 d# T

, d; h" S% v1 ]6 G
赞 收藏 评论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 手机版