串口IDLE空闲中断+DMA实现接收不定长数据基于stm32cubemx

引言:对于串口接收一些不定长的数据,必须面对一个问题:怎么判断一帧数据接收是否完成?通常使用RXNE非空中断配合简单的数据协议,在数据中加入帧头、帧尾,在程序中判断是否接收到帧尾来确定数据接收完毕,因此对每个字节都要触发中断进行判断,比较消耗系统资源,尤其是在一些实时性要求较高的场合,而串口空闲中断可以大大简化数据接收过程的判断。本文章介绍基于stm32cubemx使用DMA+IDLE串口空闲中断实现接收不定长数据。

STM32的IDLE空闲中断产生机制

IDLE空闲中断是在监测到数据接收后(即串口的RXNE位被置位)开始检测,当总线上在一个字节对应的周期内未再有新的数据接收时,控制触发空闲中断的IDLE位被硬件置1 便会激发一个空闲中断,在中断处理函数中,我们可以解析接收到的不定长数据。

 使用工具

软件:stm32cubemx ,keil uVision5,串口收发软件(vofa+,

硬件:stm32f103rct6,ST-link下载器,数据线.

STM32CUBEMX初始化配置

打开stm32cubemx,点击红色方框处,开始新建工程

查找芯片型号,选择对应芯片型号处双击

sys进行如下配置,便于进行st-link下载

设置rcc,选择高速时钟(HSE)为外部晶振

配置时钟树,本文使用的是f103系列,HCLK最大为72MHz

配置串口:

1.设置MODE为异步通信(Asynchronous)

2.使用默认配置(波特率为115200 Bits/s,传输数据长度为8 Bit,奇偶检验无,停止位为1 Bit 接收和发送都使能)

3.使能串口中断

 

设置DMA:

  1. 点击add,添加通道
  2. 通道配置均为默认(modenormal,内存地址每次递增1个byte)

工程命名,选择保存位置,选择使用的编译器及版本

 

如下配置,点击GENERATE CODE生成代码

代码编写

注释部分为使用双缓冲区接收数据,可有效防止接收中断间隔时间非常短(即发送数据帧的速率很快),MCU来不及处理此次接收到的数据,又产生中断,导致数据会被覆盖的情况

main.h中添加

#define BUFFER_SIZE  100 
void USAR_UART_IDLECallback(UART_HandleTypeDef *huart,uint8_t rxlen );

main.c

/* USER CODE BEGIN PV */
uint8_t rxbuffer1[BUFFER_SIZE]={0};  //接收数据缓存数组1
//uint8_t rxbuffer2[BUFFER_SIZE]={0};  //接收数据缓存数组2
//uint8_t flag=0;双缓冲区切换标志
/* USER CODE END PV */

 main函数中注意确保DMA初始化函数放在串口初始化之前,

并在串口初始化之后使能IDLE中断、开启串口DMA接收

 /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();//放在串口初始化之前
  MX_USART1_UART_Init();

  /* USER CODE BEGIN 2 */
  __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); //使能IDLE中断
   HAL_UART_Receive_DMA(&huart1,rxbuffer1,BUFFER_SIZE);//开启串口DMA接收
  /* USER CODE END 2 */

在/* USER CODE BEGIN 4 */下添加用户自定义IDLE空闲中断回调函数(注释部分为双缓冲接收)


void USAR_UART_IDLECallback(UART_HandleTypeDef *huart,uint8_t rxlen )
	{
	if(huart == &huart1)  //判断是否为串口1产生中断
	{
 /*		switch(flag)
 	{
			case 0:	flag=1;
                    memset(rxbuffer2,0,BUFFER_SIZE);//清空接收缓存,调用需包含string.h,本例程删去也基本无影响
                    HAL_UART_Receive_DMA(&huart1,rxbuffer2,BUFFER_SIZE);//重新打开DMA接收
				    HAL_UART_Transmit_DMA(&huart1, rxbuffer1,rxlen);//将接收到的不定长数据发送到上位机	               
			        rxlen = 0;//清除数据长度计数
					
 		    break;
			case 1:flag=0;
                   memset(rxbuffer1,0,BUFFER_SIZE);//清空接收缓存,调用需包含string.h,本例程删去也基本无影响
                   HAL_UART_Receive_DMA(&huart1,rxbuffer1,BUFFER_SIZE);//重新打开DMA接收
			       HAL_UART_Transmit_DMA(&huart1, rxbuffer2,rxlen);//将接收到的不定长数据发送到上位机	              
			       rxlen = 0;//清除数据长度计数
				   
			break;
			}*/
		           
         HAL_UART_Transmit_DMA(&huart1, rxbuffer1,rxlen);//将接收到的不定长数据发送到上位机
	     rxlen = 0;//清除数据长度计数
		 HAL_UART_Receive_DMA(&huart1,rxbuffer1,BUFFER_SIZE);//重新打开DMA接收
	}

	}

由于hal库中没有定义IDLE空闲中断的中断处理函数,需要用户自行定义

打开stm32f1xx_it.c,找到void USART1_IRQHandler(void)函数,并添加如下代码:

void USART1_IRQHandler(void)
{
  /* USER CODE BEGIN USART1_IRQn 0 */

  /* USER CODE END USART1_IRQn 0 */
  HAL_UART_IRQHandler(&huart1);
  /* USER CODE BEGIN USART1_IRQn 1 */
    uint8_t rxlen ;  //接收一帧数据的长度
 if(RESET != __HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE)) //判断idle标志被置位
	{ 
		__HAL_UART_CLEAR_IDLEFLAG(&huart1);//清除标志位
		HAL_UART_DMAStop(&huart1); //  停止DMA传输
		rxlen =  BUFFER_SIZE - __HAL_DMA_GET_COUNTER(&hdma_usart1_rx); //总计数减去未传输的数据个数,得到已经接收的数据个数
        USAR_UART_IDLECallback(&huart1,rxlen); 	 // 调用用户定义空闲中断回调函数
	 }

  /* USER CODE END USART1_IRQn 1 */
}

下载程序测试

蓝色为上位机发送数据,绿色为上位机接收数据

测试正常

  • 10
    点赞
  • 55
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
STM32CubeMX中配置STM32F103C8T6的串口空闲中断结合DMA接收不定数据的方法如下: 1. 打开STM32CubeMX软件,选择相应的芯片型号(STM32F103C8T6)。 2. 在配置器中选择相应的外设及其参数。首先配置串口一(USART1),选择波特率、数据位、停止位、校验位等参数根据需求进行配置。 3. 在配置UART设置下拉菜单中选择“Asynchronous”,并勾选“Enable DMA Reception”选项。同时,在“DMA Settings”中选择合适的DMA通道和传输方向(从外设到内存)。 4. 在“Interrupt Settings”中勾选“USART1 global interrupt”选项,并勾选“USART1 interrupt enable”选项,以使能串口一的中断功能。 5. 在Pinout & Configuration选项卡中,单击USART1的引脚图标,选择相应的引脚,如PA9作为USART1的TX引脚,PA10作为USART1的RX引脚。 6. 点击“Project”菜单下的“Generate Code”选项生成代码并导入到工程中。 7. 在生成的代码中找到USART1_IRQHandler函数,使用DMA接收数据的方法如下: ```c void USART1_IRQHandler(void) { if(USART1->SR & USART_SR_IDLE) // 判断空闲中断标志位 { uint32_t temp; // 记录DMA传输的度 temp = USART1->DR; // 读取USART1数据寄存器 DMA1_Channel5->CNDTR = BUFFER_SIZE; // 设置DMA通道传输DMA1_Channel5->CCR |= DMA_CCR_EN; // 使能DMA1通道5 } } ``` 8. 在代码中定义一个缓冲区数组来存放接收到的数据,定义一个缓冲区大小的常量(BUFFER_SIZE),并初始化DMA传输相关的寄存器,如下: ```c #define BUFFER_SIZE 1024 // 定义缓冲区大小为1024 uint8_t rx_buffer[BUFFER_SIZE]; // 定义接收缓冲区 int main(void) { // ... DMA1_Channel5->CPAR = (uint32_t)&USART1->DR; // 设置DMA通道外设地址为USART1数据寄存器地址 DMA1_Channel5->CMAR = (uint32_t)rx_buffer; // 设置DMA通道存储器地址为接收缓冲区地址 DMA1_Channel5->CNDTR = BUFFER_SIZE; // 设置DMA通道传输DMA1_Channel5->CCR |= DMA_CCR_MINC | DMA_CCR_CIRC | DMA_CCR_TCIE; // 使能DMA通道5、存储器增量模式、循环模式、传输完成中断 NVIC_EnableIRQ(USART1_IRQn); // 使能USART1中断 // ... } ``` 通过以上配置,当串口接收数据并产生空闲中断时,中断服务程序会启动DMA传输,将接收到的数据存储在缓冲区中。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值