STM32CubeMX HAL库串口+DMA+IDLE空闲中断不定长度数据接收和发送

📅 发布时间:2026/7/11 22:50:45 👁️ 浏览次数:
STM32CubeMX HAL库串口+DMA+IDLE空闲中断不定长度数据接收和发送
1. STM32发送接收方式在 STM32 中。USART 发送接收有三种基本方式轮询、中断和 DMA。轮询方式为堵塞模式使用超时管理机制。轮训方式它每次接收一个字节在规定时间内接收固定长度的数据。在对于某些数据不固定长度接收的数据轮询的方式有时候不够灵活。中断方式使用中断的方式如每一个字节都中断一次当时比较消耗系统资源。特别是HAL库中从中断到回调函数运行了不少的程序频繁的中断很可能造成数据溢出。为了避免这个问题我们使用指定接收一定长度的数据再调用回调函数这会让我们可以接收大数据但是这种情况则造成了要求每次的包是固定长度。DMA方式为了解决以上一些问题最常用的办法是使用空闲中断即在串口空闲的时候触发一次中断通知内核本次运输完成了。数据传输过程为了尽量不占用CPU的处理数据时间所以就使用DMA接收串口的数据。2. 空闲中断空闲中断介绍空闲中断是接受数据后出现一个byte的高电平(空闲)状态就会触发空闲中断。并不是空闲就会一直中断准确的说应该是上升沿停止位后一个byte如果一直是低电平是不会触发空闲中断的会触发break中断。所以为了减少误进入串口空闲中断串口RX的IO管脚一定设置成Pull-up上拉模式串口空闲中断只是接收的数据时触发发送时不触发。空闲中断使用串口空闲中断的判定是当串口开始接收数据后检测到1字节数据的时间内没有数据发生则认为串口空闲了进入相应的串口中断。在中断内清除空闲中断标志位和调用串口回调函数在回调函数内处理读取判断处理接收的一帧数据。3. DMA半满和完成中断DMA的半满Half Transfer中断和满Transfer Complete中断是用于处理DMA传输过程中的半满和满的事件的中断。半满中断:半满中断是当DMA传输的一半数据已经传输完成时触发的中断。这个中断可以用来通知CPU表示已经传输了一部分数据可以进行相关的处理操作。满中断满中断是当整个DMA传输完成时触发的中断。这个中断用来通知CPU表示整个数据传输已经完成可以进行后续的处理。通过使用半满和满中断可以及时获得DMA传输的状态并根据需要进行相应的处理以实现高效的数据传输和处理。DMA半满中断的理解DMA设置成传输到一半有个中断这样缓存数组如果设成100那么存满50个就会有个中断这样的好处就是相当于有两个箩筐一个用来接收采集的萝卜另一个倒萝卜两个箩筐轮流使用但是这样有个问题就是两个箩筐要配合倒萝卜的速度要跟得上采萝卜的速度也就是说到萝卜的速度要采萝卜的速度main.c int main(void) { /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_DMA_Init(); MX_TIM3_Init(); MX_USART1_UART_Init(); /* USER CODE BEGIN 2 */ printf(Uart1 init is ok); __HAL_UART_ENABLE_IT(huart1, UART_IT_IDLE); //使能串UART1 IDLE(空闲)中断 HAL_UART_Receive_DMA(huart1, RX1_Buf, DMA_BUF_SIZE); //设置DMA传输将uart1的数据搬运到RX1_Buf中 /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */ } usart.h /* USER CODE BEGIN Private defines */ #define DMA_BUF_SIZE 256 extern uint8_t RX1_Offset ; // 计算偏移量 , 用来计算接收数据长度的 extern uint8_t RX1_Buf[]; // 接收数据存放的数组 /* USER CODE END Private defines */ usart.c /* USER CODE BEGIN 0 */ #include stdio.h uint8_t RX1_Offset 0 ; // 计算偏移量 , 用来计算接收数据长度的 uint8_t RX1_Buf[DMA_BUF_SIZE]; // 接收数据存放的数组 /* USER CODE END 0 */ stm32f1xx_it.c /* Private function prototypes -----------------------------------------------*/ /* USER CODE BEGIN PFP */ void USER_UART_IRQHandler(UART_HandleTypeDef *huart); /* USER CODE END PFP */ /** * brief This function handles USART1 global interrupt. */ 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 */ USER_UART_IRQHandler(huart1); /* USER CODE END USART1_IRQn 1 */ } /* USER CODE BEGIN 1 */ // DMA 接收到一半的中断 void HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart) { if(huart-Instance USART1) { uint8_t Length DMA_BUF_SIZE/2 - RX1_Offset ; //printf(HLength%d\n,Length); HAL_UART_Transmit(huart,RX1_BufRX1_Offset,Length,HAL_MAX_DELAY); RX1_Offset Length; } } // DMA传输完成中断 , 就是接收满了的时候 触发中断 void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if(huart-Instance USART1) { uint8_t Length DMA_BUF_SIZE - RX1_Offset ; HAL_UART_Transmit(huart,RX1_BufRX1_Offset,Length,HAL_MAX_DELAY); //printf(CLength%d\n,Length); RX1_Offset 0 ; // 清空dma 位置基准值 } } // 用户自定义的函数 处理串口空闲中断 void USER_UART_IRQHandler(UART_HandleTypeDef *huart) { if(huart-Instance USART1) //判断是否是串口1 { if(__HAL_UART_GET_FLAG(huart, UART_FLAG_IDLE) ! RESET ) //判断是否是空闲中断 { __HAL_UART_CLEAR_IDLEFLAG(huart); //清除空闲中断标志否则会一直不断进入中断 //计算接收到的数据长度 : BUFFER_SIZE - __HAL_DMA_GET_COUNTER(hdma_usart1_rx) uint8_t Length DMA_BUF_SIZE - __HAL_DMA_GET_COUNTER(hdma_usart1_rx) - RX1_Offset; HAL_UART_Transmit(huart,RX1_BufRX1_Offset,Length,HAL_MAX_DELAY); RX1_Offset Length; //printf(ILength%d\n,Length); } } } /* USER CODE END 1 */