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

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

+ r' ?' y" \* h- }6 y0 X* z
8 K4 w( f) ?( x/ @* I+ g" V

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

  1. // UART.c! {  Q) d" y+ _5 K/ X! C1 g4 y" ]

  2. 3 m8 i) u! `4 @5 Q8 s  R% T& i; n
  3. int UARTInit(void)
    + H$ T% J1 Y! s8 W+ |+ v
  4. {
    . u' y+ m2 P- \& {/ h
  5. GPIO_InitTypeDef GPIO_InitStruct = {0};
    . _; s4 N) c* P7 A7 \' |/ y, p0 V3 s
  6.     ; a7 T" U4 F4 k) g+ u" T
  7.     /* IO初始化 */
    ' ^! ]& h0 g9 U8 G3 J* e
  8. __HAL_RCC_GPIOA_CLK_ENABLE();
    2 J' C3 n6 c  C/ x, V0 }0 J
  9.     # B: k+ b7 O% Z- Z# y" D. M
  10. GPIO_InitStruct.Pin = GPIO_PIN_9;
    . H1 L3 A: F; A
  11. GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;- F( m4 \* ]8 H+ H
  12. GPIO_InitStruct.Pull = GPIO_PULLUP;
    / h4 w" h. z; o' J% L
  13. GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;& \% Q. {0 P$ z6 B  k
  14. HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    / ~/ Z/ B4 O; U3 D1 r( L& M* S: Z

  15. 8 G8 Z: t; j2 O
  16.     GPIO_InitStruct.Pin = GPIO_PIN_10;
    ' ?9 T4 U3 }; x; d' C* P3 Y, k
  17. GPIO_InitStruct.Mode = GPIO_MODE_AF_INPUT;" I% I3 T+ h9 ^
  18. GPIO_InitStruct.Pull = GPIO_NOPULL;
    * ~9 n0 _! d5 v' h% U6 [7 n
  19. GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;# x0 X( `2 ]$ e- [
  20. HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    6 ?; X. `# D8 x2 O
  21.     * P) D0 o- J! T
  22.     /* UART1初始化 */
    9 l5 i9 H6 w/ p" z: v$ \
  23.     __HAL_RCC_USART1_CLK_ENABLE();& W5 j7 }! H* ?! Y) G* [
  24.     * Q2 j7 N3 j" ^0 A6 {1 Q: \; J
  25.     UARTDev1.UART_Handle.Instance = USART1;% J% ~5 H, d+ \) ?5 M
  26.     UARTDev1.UART_Handle.Init.BaudRate = 115200;6 f( ~+ J  `+ Z9 B  y; `9 q
  27.     UARTDev1.UART_Handle.Init.Parity = UART_PARITY_NONE;  b; j: R5 `+ y6 Z) H
  28.     UARTDev1.UART_Handle.Init.WordLength = UART_WORDLENGTH_8B;! g! e* |8 `% ^; U  T8 [
  29.     UARTDev1.UART_Handle.Init.StopBits = UART_STOPBITS_1;
    8 a) _  C0 I" G8 O# N
  30.     UARTDev1.UART_Handle.Init.Mode = UART_MODE_TX_RX;
    ) `/ G- m1 j( r5 t3 E; R
  31.     HAL_UART_Init(&UARTDev1.UART_Handle);
    3 ?4 y+ i6 R4 f# o8 n- T" |

  32. ( O- K" h: }7 ?% k; p# j- y
  33.     /* UART1中断初始化 */
    + A% Y0 v7 f; [. p
  34.     HAL_NVIC_SetPriority(USART1_IRQn, 0, 0);
    9 d3 H# f  a3 @* }& r+ ]
  35.     HAL_NVIC_EnableIRQ(USART1_IRQn);
    # H/ @, [* [8 e
  36.     3 F3 U' p& U; C) u3 \
  37.     ) l5 Q0 h' i; n: T4 \7 M
  38.     /* UART1启动接收 */
    & \) {9 V/ y! A
  39.     HAL_UART_Receive_IT(&UARTDev1.UART_Handle, UARTDev1.RxBuffer, UART_RX_BUFFSIZE);
    , {' T# [7 y4 \$ U/ r* T/ g- a
  40.     & W+ `6 ?  g) E& z+ ~, k$ z: A* i+ \/ @
  41.     /** UART1开启空闲中断 **/+ o. \5 \9 I# \. u& d* [2 I1 u
  42.     __HAL_UART_ENABLE_IT(&UARTDev1.UART_Handle, UART_IT_IDLE);# w- E& g8 d- Z8 H% a
  43.    
    : P9 ^; M  D( R
  44.     return 0;: e9 b  L5 j* c- b
  45. }
复制代码

+ [* Y7 F9 l" k7 F1 b

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

  1. // UART.c
    : L$ P2 `1 H' d6 i! n! F3 y
  2. void USART1_IRQHandler(void)6 m2 Z: G' c  D4 G6 R5 c
  3. {9 I) l4 M+ t: D* O9 x5 i2 k( w7 w
  4.     UARTDef *huart = &UARTDev1;# [$ k; Y) x) P+ L9 Q. x
  5.     . ?/ n$ @& M- ]- K- x. F$ {7 h
  6. HAL_UART_IRQHandler(&huart->UART_Handle);5 r9 L! v' ^3 s& r, }
  7.     /* F1的HAL库中未实现IDLE相关功能,固自行实现 */
    6 A; A: E0 ?# G7 b- n% Z3 w4 `! B) k$ g
  8.     if(__HAL_UART_GET_FLAG(&huart->UART_Handle, UART_FLAG_IDLE))3 G" z0 ]9 H* @# H. ?
  9.     {   /* 接收到完整一帧数据 */
    " ]1 ?3 t& a" J4 N* u) d
  10.         UARTSendData(huart, UARTDev1.RxBuffer, UART_RX_BUFFSIZE - UARTDev1.UART_Handle.RxXferCount);$ ^) _/ w1 |9 n( h' B6 ^
  11.         
    # W9 v! d* Z: E# J9 V4 c4 C
  12.         /* 重新启动接收 */
    - B+ e. l# ~; R( R$ q
  13.         HAL_UART_AbortReceive_IT(&UARTDev1.UART_Handle);
    " L$ s2 p: T. m- d$ e8 |# l
  14.         HAL_UART_Receive_IT(&UARTDev1.UART_Handle, UARTDev1.RxBuffer, UART_RX_BUFFSIZE);- l* A1 H) ?4 Z) I; L
  15.         /* 清空闲中断 */
    7 @" N+ V9 g' K2 y' z
  16.         __HAL_UART_CLEAR_IDLEFLAG(&huart->UART_Handle);
    5 ]1 i: @* o7 ^7 B5 X8 U
  17.     }  E6 @, ~4 T+ ^9 ^$ u
  18. }
复制代码

7 ]- Q$ Q7 `+ h5 K3 a1 S" P0 H' A! j

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

  1. int main()- z7 `% Y* y2 E  m- L* f* s4 i
  2. {
    4 |# T  ]( Z1 ~6 J# n! K& c+ z
  3. HAL_Init();
    * r4 q. ^8 r- R7 ?! r, W, h
  4.     SystemClockInit();
    . d0 C( Q/ s( [! L0 j
  5.     GPIOInit();
    1 x% h- n! }, l/ E, e# B1 h
  6.     UARTInit();; \, Q/ A- k4 _
  7.     5 T) ]+ R9 K0 U2 j/ ^6 r
  8. while(1)2 v4 b. O" v% I
  9. {
    % q5 \7 W' D+ @' w3 U8 m# `$ Y; X
  10.         HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_8);: b- {( r) r% }; x
  11.         HAL_Delay(500);9 z' `# ?1 Z% `0 v7 U/ S, ~
  12. }
    6 p5 E# Z8 f1 t2 }% j; {% w
  13. }
复制代码

2 W7 |) e/ d1 l; |% u) Q6 Z三、效果演示

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


& s9 ?* X/ z( t, D

微信图片_20230614163425.png


  z; l1 F3 h3 h" o

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


( ~! ?6 l' m" f1 B" Q

转载自:嵌入式技术栈

如有侵权请联系删除

$ C. ?- v$ z$ {

! O2 J2 F6 Y  ]0 i9 N3 V2 v' H
收藏 评论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 手机版