STM32串口空闲中断+DMA不定长收发数据(HAL库)

目录

一、STM32 IDLE和RXNE中断

1.1.STM32的IDLE功能:

1.2.STM32 的RXNE功能:

二、USART+DMA接收数据

2.1.DMA计算实际接收数据字节数

2.2.串口IDLE空闲中断的方式接收一帧数据原理

三、软件设计

3.1.接收数据的流程:

3.2数据接收完成的流程:

3.3.while循环主程序流程:

四、注意事项


一、STM32 IDLE和RXNE中断

1.1.STM32的IDLE功能:

在使用串口接受字符串时,可以使用空闲中断(IDLEIE置1,即可使能空闲中断),这样在接收完一个字符串,进入空闲状态时(IDLE置1)便会激发一个空闲中断。在中断处理函数,我们可以解析这个字符串。

中断产生条件:在串口无数据接收的情况下,不会产生,当清除IDLE标志位后,必须有接收到第一个数据后,才开始触发,一但接收的数据断流,没有接收到数据,即产生IDLE中断。

1.2.STM32 的RXNE功能:

当串口接收到一个bit的数据时,(读取到一个停止位) 便会触发 RXNE接收数据中断,接受到一个字节的数据,触发中断。比如给上位机给单片机一次性发送了8个字节,就会产生8次RXNE中断,1次IDLE中断。当串口接收到数据时,bit5 RXNE就会自动变成1,当接收完一帧数据后,bit4就会变成1。

图1.串口CR1寄存器

二、USART+DMA接收数据

2.1.DMA计算实际接收数据字节数

图2.1.DMA返回接收空间剩余字节宏定义

DMA接收时该宏将返回当前接收空间剩余字节。其本质就是读取NTDR寄存器,DMA通道结构体中定义了NDTR寄存器,读取该寄存器即可得到未传输的数据数。

实际接收的字节= 预先定义的接收总字节 - __HAL_DMA_GET_COUNTER()

图2.2 NTDR寄存器

2.2.串口IDLE空闲中断的方式接收一帧数据原理

采用STM32F103的串口1,并配置成空闲中断IDLE模式且使能DMA接收,并同时设置接收缓冲区和初始化DMA。那么初始化完成之后,当外部给单片机发送数据的时候,假设这次接受的数据长度是100个字节,那么在单片机接收到一个字节的时候并不会产生串口中断,而是DMA在后台把数据默默地搬运到你指定的缓冲区数组里面。当整帧数据发送完毕之后串口才会产生一次中断,此时可以利用__HAL_DMA_GET_COUNTER(&hdma_usart1_rx);函数计算出当前DMA接收存储空间剩余字节。

例如,本次串口接收100个字节,通过上位机发送了 Usart_Test 6个字节的数据长度,那么此时 GET_COUNTER函数读出来接收存储空间剩余字节就是94个字节,即:

实际接收的字节(6) = 预先定义的接收总字节(100) -__HAL_DMA_GET_COUNTER()(94)

9 = 100-94

图2.3.串口+DMA接收数据的变量


volatile uint8_t Array_number = 100;          //暂存数组个数         
uint8_t Rx_date[100]={0};                     //数据接收暂存数组
HAL_UART_Receive_DMA(&huart1,Rx_date,Array_number);//重启DM

三、软件设计

本例程将使用DMA+串口接受空闲中断,实现不定长数据完整接收,并将接收到的数据发送至上位机的功能。

图3.1.例程逻辑图

3.1.接收数据的流程:

首先在初始化的时候打开DMA接收,当MCU通过USART接收外部发来的数据时,在进行第①②③步的时候,DMA直接将接收到的数据写入缓存rx_buffer[100] //接收数据缓存数组,程序此时也不会进入接收中断,在软件上无需做任何事情,要在初始化配置的时候设置好配置就可以了。

3.2数据接收完成的流程:

当一帧数据接收完成之后IDLE置1,产生接收空闲中断④,在中断服务函数中做这几件事:

  • 判断是否为IDLE接收空闲中断。

②在中断服务函数中将接收完成标志位置1。

③关闭DMA防止在处理数据时候接收数据,产生干扰。

④计算出接收缓存中的数据长度,清除中断标志位。

图3.2.中断服务函数代码

void USART1_IRQHandler(void)
{
  /* USER CODE BEGIN USART1_IRQn 0 */
	uint32_t temp;                                           //未传输数据的个数
	uint8_t IDLE_flag = 0;                                   //空闲中断标志位
	IDLE_flag =__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE);  //获取IDLE标志位
	if(IDLE_flag == 1)                                       //判断是否进入空闲中断
	{ 
		__HAL_UART_CLEAR_IDLEFLAG(&huart1);                  //清除标志位
		HAL_UART_DMAStop(&huart1);                           //停止DMA传输,防止搬错
		temp = __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);       //获取DMA中未传输的数据个数   
		Rx_length = Array_number - temp;                     //总计数减去未传输的数据个数,得到已经接收的数据个数
		Rx_end_flag = 1;	                                 //接收完成标志位置1	
	 }

  /* USER CODE END USART1_IRQn 0 */
	
  /* USER CODE BEGIN USART1_IRQn 1 */
  /* USER CODE END USART1_IRQn 1 */
	 
	HAL_UART_IRQHandler(&huart1);
	 
}

3.3.while循环主程序流程:

  • 主程序中检测到接收完成标志被置1。
  • 进入数据处理程序,将接收到的数据重新发送到上位机。
  • 将接收完成标志位置0,并清除计数。
  • 重新设置DMA下次要接收的数据字节数,使能DMA进入接收数据状态。

图3.1.while循环代码


  while (1)
  {
    /* USER CODE END WHILE */
	 if(Rx_end_flag == 1)                                                                 //判断数据是否接受完成
		{			
		    HAL_UART_Transmit_DMA(&huart1,Rx_date,Rx_length);                                 //将接收的数据返回上位机			
			Rx_length = 0;                                                                    //清除计数
			Rx_end_flag = 0;                                                                  //清除接收标志位			
        }
	 HAL_UART_Receive_DMA(&huart1,Rx_date,Array_number);                                 //重启DMA
    /* USER CODE BEGIN 3 */
  }

四、注意事项

1、一定要初始化空闲中断和DMA,空闲中断未初始化会导致无法进入空闲中断,DMA未初始化会导致第一次搬运数据为空数据。

图4.1.空闲中断和DMA初始化函数

2、while每次循环结束时,要重启DMA(HAL_UART_Receive      _DMA();),否则DMA搬运的数据不会更新,即DMA只搬运一次,Rx_date[ ]中的数据永远为第一次存进去的数据。

图4.2

3、接收数据时,DMA搬运来的数据将暂存至Rx_date[ ]数组中,但是将接收到的数据处理后,用HAL_UART_Transmit_DMA(&huart1,Rx_date,Rx_length)发回上位机时,数据的长度应该为Rx_length,如果用Array_number,会出现下图情况。

图4.3

4、__HAL_DMA_GET_COUNTER();函数获取的是DMA中未传输的数据个数,若要获取实际接收数据的长度需进一步处理。

图4.4 实际接收数据长度的处理

STM32F103系列微控制器是基于ARM Cortex-M3内核的高性能微处理器,广泛应用于嵌入式系统设计。在本示例中,我们关注的是如何利用串口(USART)的空闲中断DMA(Direct Memory Access)功能来实现Modbus通信,这是一种在工业自动化领域广泛应用的通信协议。 我们要理解串口通信的基本概念。串口通信是一种点对点的数据传输方式,通常使用UART(通用异步收发传输器)或USART(通用同步/异步收发传输器)接口。在STM32F103中,USART模块提供了串行数据的发送接收功能,包括数据帧的起始位、数据位、奇偶校验位停止位。 串口空闲中断是在串口接收线路上检测到一段无数据传输的空闲时间后触发的中断,这对于检测完整的Modbus报文非常有用。在传统实现中,可能需要设置定时器中断来判断一帧数据是否接收完成,但这种方法可能会增加系统的复杂性功耗。使用串口空闲中断则更为高效,一旦接收到的数据流停止,中断立即被触发,可以立即处理接收到的数据,无需等待额外的定时器中断。 接下来,我们讨论DMADMA是一种允许外部设备直接访问内存的技术,减少了CPU的干预,提高了数据传输效率。在STM32中,我们可以配置DMA通道来处理串口接收数据,这样当数据到达时,DMA会自动将数据串口接收缓冲区转移到指定的内存位置,而CPU可以继续执行其他任务,提升了系统性能。 在实现Modbus通信时,我们需要设置USART的相关参数,如波特率、数据位、停止位奇偶校验,并开启串口接收中断。同时,要配置DMA,选择合适的通道,设置传输方向(从外设到内存),并指定数据缓冲区地址。当串口进入空闲状态并触发中断时,通过DMA已经将完整的一帧数据传输到内存,此时可以进行Modbus报文解析响应。 在提供的文件列表中,`keilkilll.bat`可能是Keil的工程
在使用STM32 HAL进行串口通信时,结合DMA空闲中断(Idle Interrupt)是一种高效接收不定长数据的方法。然而,在实际应用中,可能会遇到各种错误,例如DMA传输错误、溢出错误或帧错误等。为了确保系统的稳定性数据的完整性,需要正确处理错误回调函数并进行相关错误排查。 ### 错误回调函数的处理 在HAL中,错误回调函数 `HAL_UART_ErrorCallback()` 会在发生串口通信错误时被调用。该函数的原型如下: ```c void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart); ``` 当使用DMA空闲中断时,常见的错误包括: - **DMA传输错误**:例如DMA通道配置错误或内存访问错误。 - **帧错误**:当接收到的数据帧格式不正确时发生。 - **溢出错误**:当接收缓冲区已满而仍有数据到来时发生。 在 `HAL_UART_ErrorCallback()` 中,可以通过检查 `huart->ErrorCode` 来判断具体的错误类型。以下是一个示例代码片段,展示如何在错误回调函数中处理这些错误: ```c void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart) { if (huart == &huart1) { if (huart->ErrorCode & HAL_UART_ERROR_DMA) { // 处理DMA传输错误 // 例如:重新启动DMA接收 HAL_UARTEx_ReceiveToIdle_DMA(huart, rx_buffer, RX_BUFFER_SIZE); } else if (huart->ErrorCode & HAL_UART_ERROR_FE) { // 处理帧错误 } else if (huart->ErrorCode & HAL_UART_ERROR_ORE) { // 处理溢出错误 } } } ``` ### 错误排查方法 1. **检查DMA配置** 确保DMA通道的配置与串口外设匹配,包括数据宽度、传输方向缓冲区大小。如果DMA配置错误,可能会导致传输中断数据丢失。 2. **检查空闲中断使能** 在调用 `HAL_UARTEx_ReceiveToIdle_DMA()` 后,需要确保空闲中断已正确启用。可以通过检查寄存器 `CR1` 的 `IDLEIE` 位来确认。 3. **检查DMA缓冲区大小** 如果DMA缓冲区太小,可能会导致溢出错误。根据预期接收数据长度,合理设置缓冲区大小。 4. **关闭不必要的DMA中断** 在某些情况下,关闭DMA的半满中断(如 `DMA_IT_HT`)可以减少中断处理的复杂性,并避免不必要的中断触发 [^2]。 5. **检查串口波特率配置** 波特率配置不正确可能导致帧错误。确保波特率与发送端的波特率一致。 6. **调试工具的使用** 使用调试工具(如ST-Link或J-Link)查看寄存器状态内存数据,可以帮助快速定位问题。 ### 示例代码 以下是一个完整的示例代码片段,展示如何在 `main` 函数中启动DMA空闲中断接收,并处理错误回调函数: ```c /* USER CODE BEGIN 2 */ // 启动串口DMA空闲中断接收 HAL_UARTEx_ReceiveToIdle_DMA(&huart1, rx_buffer, RX_BUFFER_SIZE); // 关闭DMA半满中断 __HAL_DMA_DISABLE_IT(&hdma_usart1_rx, DMA_IT_HT); /* USER CODE END 2 */ // 错误回调函数 void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart) { if (huart == &huart1) { if (huart->ErrorCode & HAL_UART_ERROR_DMA) { // 处理DMA传输错误 HAL_UARTEx_ReceiveToIdle_DMA(huart, rx_buffer, RX_BUFFER_SIZE); } else if (huart->ErrorCode & HAL_UART_ERROR_FE) { // 处理帧错误 } else if (huart->ErrorCode & HAL_UART_ERROR_ORE) { // 处理溢出错误 } } } ``` ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值