STM32使用串口空闲中断(IDLE)和 DMA接收一串数据流

STM32使用串口空闲中断(IDLE)和 DMA接收不定长数据

方法一、使用宏定义判断IDLE标志位

空闲的定义是总线上在一个字节的时间内没有再接收到数据,USART_IT_IDLE空闲中断是检测到有数据被接收后,总线上在一个字节的时间内没有再接收到数据的时候发生的。

串口空闲中断(UART_IT_IDLE):STM32的IDLE的中断在串口无数据接收的情况下,是不会一直产生的,当清除IDLE标志位后,必须有接收到第一个数据后,才开始触发,一但接收的数据断流,没有接收到数据,即产生IDLE中断。IDLE位不会再次被置高直到RXNE位被置起(即又检测到一次空闲总线)。RXNE接收中断可以不用开启,减少进中断的次数。

IDLE置1之后它不会自动清0,也不会因为状态位是1而一直产生中断,它只有0跳变到1时才会产生,也可以理解为上升沿触发。所以,为确保下次空闲中断正常进行,需要在中断服务函数发送任意数据来清除标志位。

在这里插入图片描述

清除IDLE标志位是通过先读USART_SR,再读USART_DR寄存器来完成的,在HAL库中,提供了一个用于清除IDLE标志位的宏定义,该宏定义在stm32f1xx_hal_uart.h头文件中,__ HAL_UART_CLEAR_IDLEFLAG(__ HANDLE__ )是宏名,__ HAL_UART_CLEAR_PEFLAG(__ HANDLE__)是宏体

/** @brief  Clears the UART IDLE pending flag.
  * @param  __HANDLE__ specifies the UART Handle.
  *         UART Handle selects the USARTx or UARTy peripheral
  *         (USART,UART availability and x,y values depending on device).
  * @retval None
  */
#define __HAL_UART_CLEAR_IDLEFLAG(__HANDLE__) __HAL_UART_CLEAR_PEFLAG(__HANDLE__)

而宏体又是一个宏定义,转到底层代码就可以看到先读SR再读DR寄存器的操作

/** @brief  Clears the UART PE pending flag.
  * @param  __HANDLE__ specifies the UART Handle.
  *         UART Handle selects the USARTx or UARTy peripheral
  *         (USART,UART availability and x,y values depending on device).
  * @retval None
  */
#define __HAL_UART_CLEAR_PEFLAG(__HANDLE__)     \
  do{                                           \
    __IO uint32_t tmpreg = 0x00U;               \
    tmpreg = (__HANDLE__)->Instance->SR;        \
    tmpreg = (__HANDLE__)->Instance->DR;        \
    UNUSED(tmpreg);                             \
  } while(0U)

所以在用HAL库编写串口空闲中断相关代码时,可以用该宏定义来清除IDLE标志位

示例:用宏定义判断IDLE标志位是否置位

/**
  * @brief This function handles USART1 global interrupt.
  */
void USART1_IRQHandler(void)
{
  /* USER CODE BEGIN USART1_IRQn 0 */
	//检测串口空闲中断
	if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE) != 0x00u)	//判断IDLE标志位是否被置位
	{
		__HAL_UART_CLEAR_IDLEFLAG(&huart1);		//清除IDLE标志位
		HAL_UART_IdleCallback(&huart1);			//调用自己编写的空闲中断回调函数
	}
	
  /* USER CODE END USART1_IRQn 0 */
  HAL_UART_IRQHandler(&huart1);
  /* USER CODE BEGIN USART1_IRQn 1 */
	
  /* USER CODE END USART1_IRQn 1 */
}

当串口产生中断时,会进入USART1_IRQHandler这个函数,判断IDLE标志位是否被置位,是就先清除标志位,再调用自己写的空闲中断回调函数

USART_IT_IDLE和USART_IT_RXNE区别

当接收到1个字节,会产生USART_IT_RXNE中断

当接收到一帧数据,就会产生USART_IT_IDLE中断

DMA

直接存储器存取(DMA)用来提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。无须CPU干预,数据可以通过DMA快速地移动,这就节省了CPU的资源来做其他操作。 两个DMA控制器有12个通道(DMA1有7个通道,DMA2有5个通道),每个通道专门用来管理来自于一个或多个外设对存储器访问的请求。还有一个仲裁器来协调各个DMA请求的优先权。

在程序中就可以使用DMA搬运外设通过串口发送的数据,放到接收缓存中,在串口空闲中断的回调函数中先关闭DMA,再处理数据,待数据处理完后,再开启DMA搬运

处理数据之前关闭DMA是防止在处理过程中DMA又将新数据搬运到接收缓存,覆盖掉原来的数据

方法二、使用HAL库提供的库函数

使能DMA接收,并于接收完后进入空闲中断函数

函数作用:在DMA模式下接收一定数量的数据,直到接收到预期数量的数据或发生空闲事件。

参数Size:接收数据的长度,一般大于不定长数据长度,避免遗漏数据

HAL_StatusTypeDef HAL_UARTEx_ReceiveToIdle_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)

空闲中断回调函数

__weak void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)

使用方法:

先在主函数中调用HAL_UARTEx_ReceiveToIdle_DMA函数初始化

/*
先调用HAL_UARTEx_ReceiveToIdle_DMA函数(比如说在main中调用),产生空闲中断后进入中断服务函数,中断服务函数会调用回调函数。
*/
int main(void)
{
	...
	HAL_UARTEx_ReceiveToIdle_DMA(&huart3,DATA_BUFF,BUFF_SIZE);
	...
}

再在空闲中断回调函数中处理数据

void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
	if(huart->Instance == USART3)
	{
	//这里的DMA为Normal模式
	//HAL_UART_DMAStop(huart)//如果DMA为Circular模式,回调函数加上stop函数
		USER_FNC();//用户自定义函数
		HAL_UARTEx_ReceiveToIdle_DMA(&huart3,DATA_BUFF,BUFF_SIZE);//继续接收数据
	}
}

参考:http://t.csdn.cn/DJmHQ

注意

看教程时别人使用的是HAL库是STM32Cube_FW_F1_V1.8.0,是1.8.0版本的,没有启动DMA和空闲中断的函数,也没有空闲中断回调函数,所以教程使用的是宏定义判断IDLE标志位的方法;而现在最新的HAL库是1.8.4版本,可在CubeMX中查看,在使用过程中发现是有DMA和空闲中断启动函数HAL_UARTEx_ReceiveToIdle_DMA的,也有空闲中断回调函数HAL_UARTEx_RxEventCallback

不知道从1.8.1到1.8.3哪个版本开始就有了,不过现在最新的有这些函数,也可以尝试着使用一下

在这里插入图片描述

STM32Cube_FW_F1_V1.8.0

在这里插入图片描述

STM32Cube_FW_F1_V1.8.4

在这里插入图片描述

  • 18
    点赞
  • 173
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
使用STM32串口空闲中断DMA可以实现接收不定长度数据的功能,具体步骤如下: 1. 配置串口DMA 首先需要配置串口DMA,使其能够正常工作。具体配置方法可以参考STM32的官方文档或者其他相关资料。 2. 配置接收数组和接收计数器 在代码中定义一个接收数组和一个接收计数器,用于存储接收到的数据和记录接收到的数据长度。 3. 配置空闲中断空闲中断中判断接收数据是否完成,如果完成则将接收到的数据发送出去。具体实现方法如下: ```c void HAL_UART_IDLECallback(UART_HandleTypeDef *huart) { if(huart->Instance == USART1) { uint32_t tmp_flag = 0; uint32_t temp; tmp_flag = __HAL_UART_GET_FLAG(huart, UART_FLAG_IDLE); //获取空闲中断标志 if((tmp_flag != RESET)) //判断是否是空闲中断 { __HAL_UART_CLEAR_IDLEFLAG(&huart1); //清除空闲中断标志 HAL_UART_DMAStop(&huart1); //停止DMA传输 temp = huart1.hdmarx->Instance->CNDTR; //获取DMA缓存区剩余数据量 uart1_rx_len = UART_RCV_BUF_SIZE - temp; //计算接收到的数据长度 HAL_UART_Transmit(&huart1, uart1_rx_buf, uart1_rx_len, 0xffff); //将接收到的数据发送出去 HAL_UART_Receive_DMA(&huart1, uart1_rx_buf, UART_RCV_BUF_SIZE); //重新开启DMA传输 } } } ``` 4. 启动DMA传输 在代码中启动DMA传输,将串口接收到的数据存储到接收数组中。具体实现方法如下: ```c HAL_UART_Receive_DMA(&huart1, uart1_rx_buf, UART_RCV_BUF_SIZE); ``` 以上就是使用STM32串口空闲中断DMA实现接收不定长度数据的方法。如果您有任何问题,请随时提出。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值