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

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

8 W7 L/ \% r7 Z! @2 C: L3 M$ p; Q- P& _

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

  1. // UART.c/ o" p5 b6 c) T% D

  2. 1 q# g0 T: N- j
  3. int UARTInit(void)
    ; v: e/ Z$ [# p# O5 h+ ~: ^
  4. {, B+ ]- o$ Z' }" d( R& u
  5. GPIO_InitTypeDef GPIO_InitStruct = {0};, w  ^2 B, |/ P4 e$ Q
  6.     & ^! `9 Y  }0 f& \5 j- j1 n/ Z
  7.     /* IO初始化 */4 }8 j8 ~6 q. v9 ~& R- {( b. s
  8. __HAL_RCC_GPIOA_CLK_ENABLE();
    ! x. [, ?: E5 J- p, f
  9.    
    5 [1 E# K. r8 z! ]
  10. GPIO_InitStruct.Pin = GPIO_PIN_9;
    5 z& }8 M# h: t8 j- a9 U+ Y
  11. GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    1 M/ e5 }$ z0 T  F: ~1 [" i8 e0 T
  12. GPIO_InitStruct.Pull = GPIO_PULLUP;3 `* i5 C/ \: k: @. X) E8 Y2 ^7 g
  13. GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;( R: J6 W+ Z5 ^. B( m
  14. HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);, z0 ?2 U: C2 i; k$ O; d
  15. - S* d+ w5 o6 ~# @
  16.     GPIO_InitStruct.Pin = GPIO_PIN_10;; H/ ~, g" w1 w  y+ D* n) h: \1 R( K
  17. GPIO_InitStruct.Mode = GPIO_MODE_AF_INPUT;
    % F- `- F  |/ r9 T
  18. GPIO_InitStruct.Pull = GPIO_NOPULL;
    ) f- Z( P# {5 h6 I6 G2 n- o7 j9 j% b* @
  19. GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    $ |7 w5 S3 S! J! L* U- s8 K
  20. HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    7 a% @% g) d. G- P) S
  21.    
    0 ~1 V9 Y  I& k' I+ r
  22.     /* UART1初始化 */
    - o" V2 Z1 S! |2 a- ?: x
  23.     __HAL_RCC_USART1_CLK_ENABLE();6 y8 \% P  C. v) w: ^6 G5 S- g+ b3 s
  24.     ; v* Q: ?, P) i) P& X- E) u
  25.     UARTDev1.UART_Handle.Instance = USART1;: m+ k* C- ]6 Z
  26.     UARTDev1.UART_Handle.Init.BaudRate = 115200;8 \& ^; s3 T( P) K2 |
  27.     UARTDev1.UART_Handle.Init.Parity = UART_PARITY_NONE;
    2 X/ S9 `( N8 ~# |4 W
  28.     UARTDev1.UART_Handle.Init.WordLength = UART_WORDLENGTH_8B;/ T; }) m* h" h2 n
  29.     UARTDev1.UART_Handle.Init.StopBits = UART_STOPBITS_1;
    - Y, |" M- n% s0 K
  30.     UARTDev1.UART_Handle.Init.Mode = UART_MODE_TX_RX;- ?( v; ?" Z, a" L/ H0 K2 @4 l- U; c
  31.     HAL_UART_Init(&UARTDev1.UART_Handle);2 x& q) z: v4 k: j, u4 X) {
  32. ' X& g: v5 K: M# W4 o& d2 m
  33.     /* UART1中断初始化 */
    ) A9 M6 H. d/ u. ?9 b
  34.     HAL_NVIC_SetPriority(USART1_IRQn, 0, 0);
    # N+ c2 V. |* d: n  I8 F
  35.     HAL_NVIC_EnableIRQ(USART1_IRQn);
    ( p5 M+ M- n; x6 }; b' ]( {
  36.    
    / \( ]7 l4 s1 f  ?7 r
  37.     $ [: W$ o9 g9 U
  38.     /* UART1启动接收 */
    * a9 o8 @$ @# P& E7 p
  39.     HAL_UART_Receive_IT(&UARTDev1.UART_Handle, UARTDev1.RxBuffer, UART_RX_BUFFSIZE);
    & W: ^/ H: ]* z) b& }) h
  40.    
    ! }- C6 D6 J- j% m6 ?: S4 e
  41.     /** UART1开启空闲中断 **/7 O. x' Z* r* N- z! v
  42.     __HAL_UART_ENABLE_IT(&UARTDev1.UART_Handle, UART_IT_IDLE);
    2 B! ~1 W6 Q7 d  I3 L& r
  43.    
    + v! L* m1 [; S1 D/ I6 ~0 {
  44.     return 0;$ v* |* e, S& C7 r1 U$ J4 P
  45. }
复制代码

5 o9 o) q4 X4 z* O

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

  1. // UART.c& q4 b' P; I2 a" M3 y' |) p- E1 H) m
  2. void USART1_IRQHandler(void)
    0 z7 i4 z" b$ S3 _8 ]$ [+ \
  3. {6 b( e( a* D6 N4 R% r, j3 j
  4.     UARTDef *huart = &UARTDev1;5 e7 H6 g9 B# X$ u; j
  5.    
    : f) ?6 K; j$ N- T0 D
  6. HAL_UART_IRQHandler(&huart->UART_Handle);
    ( N2 B9 |0 L+ F- _2 y  b
  7.     /* F1的HAL库中未实现IDLE相关功能,固自行实现 */
    / S# A) z3 N; P7 F) R
  8.     if(__HAL_UART_GET_FLAG(&huart->UART_Handle, UART_FLAG_IDLE))
    ! x/ F. `9 J% J; F, E
  9.     {   /* 接收到完整一帧数据 */
    5 T7 q% i- A" U* G0 _
  10.         UARTSendData(huart, UARTDev1.RxBuffer, UART_RX_BUFFSIZE - UARTDev1.UART_Handle.RxXferCount);4 j+ \- \4 w  a6 N
  11.         
    5 T0 a2 ~" `7 j5 e) M/ h
  12.         /* 重新启动接收 */
    ) k; \' b! ~( C9 a# G6 n" }9 [, ~
  13.         HAL_UART_AbortReceive_IT(&UARTDev1.UART_Handle);2 r  b* G6 k5 H+ W
  14.         HAL_UART_Receive_IT(&UARTDev1.UART_Handle, UARTDev1.RxBuffer, UART_RX_BUFFSIZE);
    & M  T0 h8 p( G. B3 {1 R5 t
  15.         /* 清空闲中断 */0 Z- X' V& }4 `# K, i5 M6 o
  16.         __HAL_UART_CLEAR_IDLEFLAG(&huart->UART_Handle);
    9 d( A0 H9 i4 i- E! @! p  G4 H4 ?
  17.     }% I9 G  E$ v, ~! n% N$ @. ]
  18. }
复制代码
: M* L6 \8 m, P( D  }2 a5 Y, `# ^

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

  1. int main()6 S# {+ a# e* p; N- j- B: C
  2. {
    ) [* b0 ]; r! ?: S5 S
  3. HAL_Init();
    ) y0 {1 `3 t/ {# |1 Y7 w6 E  x% A
  4.     SystemClockInit();
    ; r' z! j* F1 t. ^* r3 h( k! N( r
  5.     GPIOInit();% l1 E' e4 [& I( w' d* ^
  6.     UARTInit();3 r! l" s8 f& _( i+ g
  7.     . a% B1 W) X* q- S
  8. while(1)
    9 |$ _2 V8 A2 H* f$ A7 a0 e
  9. {+ ]7 U0 G& R6 {0 d, r3 S" t
  10.         HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_8);
    1 a$ I$ ^0 {% N0 k
  11.         HAL_Delay(500);
    % i7 s/ X) ^3 D+ M1 C( W' R' U
  12. }* _5 m6 x* a" U
  13. }
复制代码
  c9 g  Z* U# D! L9 T; D
三、效果演示

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

3 x4 n6 |4 g8 m) ~( U' e

微信图片_20230614163425.png

% a0 G4 |# E/ E  m, Q' R3 C0 O

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


  U* A7 N- M! w

转载自:嵌入式技术栈

如有侵权请联系删除

6 e6 U7 M" V& c+ ^5 _2 c" l' A
+ o9 c2 ~! ]- Q' m
收藏 评论1 发布时间:2023-6-14 16:35

举报

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

学习了

所属标签

相似技术帖

官网相关资源

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