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

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


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

  1. // UART.c

  2. int UARTInit(void)
  3. {
  4. GPIO_InitTypeDef GPIO_InitStruct = {0};
  5.    
  6.     /* IO初始化 */
  7. __HAL_RCC_GPIOA_CLK_ENABLE();
  8.    
  9. GPIO_InitStruct.Pin = GPIO_PIN_9;
  10. GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  11. GPIO_InitStruct.Pull = GPIO_PULLUP;
  12. GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  13. HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  14.     GPIO_InitStruct.Pin = GPIO_PIN_10;
  15. GPIO_InitStruct.Mode = GPIO_MODE_AF_INPUT;
  16. GPIO_InitStruct.Pull = GPIO_NOPULL;
  17. GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  18. HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
  19.    
  20.     /* UART1初始化 */
  21.     __HAL_RCC_USART1_CLK_ENABLE();
  22.    
  23.     UARTDev1.UART_Handle.Instance = USART1;
  24.     UARTDev1.UART_Handle.Init.BaudRate = 115200;
  25.     UARTDev1.UART_Handle.Init.Parity = UART_PARITY_NONE;
  26.     UARTDev1.UART_Handle.Init.WordLength = UART_WORDLENGTH_8B;
  27.     UARTDev1.UART_Handle.Init.StopBits = UART_STOPBITS_1;
  28.     UARTDev1.UART_Handle.Init.Mode = UART_MODE_TX_RX;
  29.     HAL_UART_Init(&UARTDev1.UART_Handle);

  30.     /* UART1中断初始化 */
  31.     HAL_NVIC_SetPriority(USART1_IRQn, 0, 0);
  32.     HAL_NVIC_EnableIRQ(USART1_IRQn);
  33.    
  34.    
  35.     /* UART1启动接收 */
  36.     HAL_UART_Receive_IT(&UARTDev1.UART_Handle, UARTDev1.RxBuffer, UART_RX_BUFFSIZE);
  37.    
  38.     /** UART1开启空闲中断 **/
  39.     __HAL_UART_ENABLE_IT(&UARTDev1.UART_Handle, UART_IT_IDLE);
  40.    
  41.     return 0;
  42. }
复制代码

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

  1. // UART.c
  2. void USART1_IRQHandler(void)
  3. {
  4.     UARTDef *huart = &UARTDev1;
  5.    
  6. HAL_UART_IRQHandler(&huart->UART_Handle);
  7.     /* F1的HAL库中未实现IDLE相关功能,固自行实现 */
  8.     if(__HAL_UART_GET_FLAG(&huart->UART_Handle, UART_FLAG_IDLE))
  9.     {   /* 接收到完整一帧数据 */
  10.         UARTSendData(huart, UARTDev1.RxBuffer, UART_RX_BUFFSIZE - UARTDev1.UART_Handle.RxXferCount);
  11.         
  12.         /* 重新启动接收 */
  13.         HAL_UART_AbortReceive_IT(&UARTDev1.UART_Handle);
  14.         HAL_UART_Receive_IT(&UARTDev1.UART_Handle, UARTDev1.RxBuffer, UART_RX_BUFFSIZE);
  15.         /* 清空闲中断 */
  16.         __HAL_UART_CLEAR_IDLEFLAG(&huart->UART_Handle);
  17.     }
  18. }
复制代码

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

  1. int main()
  2. {
  3. HAL_Init();
  4.     SystemClockInit();
  5.     GPIOInit();
  6.     UARTInit();
  7.    
  8. while(1)
  9. {
  10.         HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_8);
  11.         HAL_Delay(500);
  12. }
  13. }
复制代码

三、效果演示

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


微信图片_20230614163425.png


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


转载自:嵌入式技术栈

如有侵权请联系删除



收藏 评论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 手机版