STM32CubeMX—串口空闲中断+DMA接收

一.串口中断通信
串口中断方式的特点
- 发送数据时,将一字节数据放入数据寄存器DR;接收数据时,将DR的内容存放到用户存储区;
- 中断方式不必等待数据的传输过程,只需要在每字节数据收发完成后,由中断标志位触发中断,在中断服务程序中放入新的一字数据或者读取接收到 的一字节数据;
- 在传输数据量较大,且通信波特率较高(大于38400)时,如果采用中断方式,每收发一个字节的数据,CPU都会被打断,造成CPU无法处理其他事务。因此在批量数据传输,通信波特率较高时,建议采用DMA方式。
中断代码如下
void USART1_IRQHandler(void)
{/* USER CODE BEGIN USART1_IRQn 0 */if (__HAL_UART_GET_FLAG(&huart1, UART_FLAG_RXNE) != RESET){if (rxReceiveCount1 < 20){rxBuffer1[rxReceiveCount1]=(uint8_t)(huart1.Instance->DR);++rxReceiveCount1;}else{rxReceiveCount1 = 0;}__HAL_UART_CLEAR_FLAG(&huart1, UART_FLAG_RXNE);}else if (__HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE) != RESET){__HAL_UART_CLEAR_IDLEFLAG(&huart1);uint32_t tmp1;tmp1 = huart1.Instance->SR;tmp1 = huart1.Instance->DR;rxFrameFlag1 = 1;}/* USER CODE END USART1_IRQn 0 *//* USER CODE BEGIN USART1_IRQn 1 *//* USER CODE END USART1_IRQn 1 */
}/**
HAL_UART_Receive_IT(&huart1, (uint8_t *)&rc_data, 1)的作用是:
-
启动UART1的中断接收模式:该代码将启动UART1的中断接收模式,使UART1开始等待接收数据。
-
指定接收缓冲区:将
rc_data变量的地址作为接收数据的缓冲区地址。这意味着当UART1接收到数据时,数据将存储在rc_data变量中。 -
指定缓冲区大小:
1表示接收数据的大小为1个字节。这意味着当UART1接收到1个字节的数据时,将触发一个接收中断,并将数据存储在rc_data变量中。
需要注意的是,在初始化UART1时,需要先调用一次该函数,来启动UART1的中断接收模式;再接受完一个数据后,也需要再调用该函数来重新启动UART1的中断接收模式,以等待接收下一个字节的数据。
二.串口空闲中断+DMA
1.空闲中断接收
空闲中断接收,当一帧数据接收完成之后,串口会进入到空闲中断中去,然后在空闲中断中处理收到的数据。这种模式对处理不定长数据帧带来很大的便利,我们不必频繁的进入接收中断处理数据,但是弊端也是明显的,由于每次都要接收完一个完整的数据帧后才空闲中断,所以当一帧数据出错时,我们也不得不接收这帧错误的数据。在通讯可靠的场合,使用空闲中断接收模式接收串口数据,将会大大提高系统的性能。
2.DMA
1.简介
DMA(Direct Memory Access)—直接存储器存取,是单片机的一个外设,它的主要功能是用来搬数 据,但是不需要占用 CPU ,即在传输数据的时候, CPU 可以干其他的事情,好像是多线程一样。2、使用场景
DMA用在只需要传输数据,不需要处理数据的地方,有三种传输方式:
- 外设→存储器(例:从串口RDR寄存器写入某数据buf)
- 存储器→外设(例:从某数据buf写入串口TDR寄存器)
- 存储器→存储器(例:复制某特别大的数据buf)
3、指针递增
在传输数据时,可以配置指向传输双方数据的指针是否自动向后递增。通过单个寄存器访问外设源或目标数据时,禁止递增模式十分有用。
通常如下图配置:
Cubemx配置
串口设置
DMA设置
开启全局中断
最后直接生成文件
代码编写
1.在usart.c文件中添加
定义变量
/* USER CODE BEGIN 0 */
volatile uint8_t rx_len=0; //接收到的数据长度
volatile uint8_t recv_end_flag=0;//接收完成标志
uint8_t rx_buffer[200];//缓存数组
/* USER CODE END 0 */
开启空闲中断和dma接收
/* USER CODE BEGIN USART1_Init 2 */__HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE); //使能空闲中断HAL_UARTEx_ReceiveToIdle_DMA(&huart1,rx_buffer,200); //开启DMA接收/* USER CODE END USART1_Init 2 */
重定义fputc,是为了能使用printf函数
/* USER CODE BEGIN 1 */
//重定向fputc函数
int fputc(int ch, FILE *f)
{HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xFFFF);return ch;
}
/* USER CODE END 1 */
2.在usart.h文件中添加
#include /* USER CODE BEGIN Includes */
extern volatile uint8_t rx_len ; //接收一帧数据的长度
extern volatile uint8_t recv_end_flag; //一帧数据接收完成标志
extern uint8_t rx_buffer[200]; //接收数据缓存数组
注意这里的#include
3.在中断函数中添加
void USART1_IRQHandler(void)
{/* USER CODE BEGIN USART1_IRQn 0 */ uint32_t tmp_flag = 0;uint32_t temp;tmp_flag =__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE); //获取IDLE标志位if((tmp_flag != RESET)){ __HAL_UART_CLEAR_IDLEFLAG(&huart1);//temp = huart1.Instance->SR; temp = huart1.Instance->DR; HAL_UART_DMAStop(&huart1); temp = hdma_usart1_rx.Instance->CNDTR;//获取此时DMA中未传输的数据个数rx_len = 200 - temp; //总计数减去未传输的数据个数,得到此次接收到的数据个数recv_end_flag = 1; }HAL_UART_IRQHandler(&huart1); /* USER CODE END USART1_IRQn 0 *//* USER CODE BEGIN USART1_IRQn 1 *//* USER CODE END USART1_IRQn 1 */
}
这里不使用回调函数
4.在main.c中添加
while (1){/* USER CODE END WHILE */if(recv_end_flag ==1) { printf("接收到的数据长度为%d\r\n",rx_len);HAL_UART_Transmit(&huart1,rx_buffer, rx_len,200);for(uint8_t i=0;i
这里只是测试代码,将接收到的数据再利用串口输出到串口助手
5.结果

当数据接收间隔时间很短的情况下,这里是1ms,数据接收会出错;在10ms后就正常
注意事项
1.程序下载到板子上没反应,只有在一步一步调试时才有输出。最后勾选了Use MicoLIB后程序就正常运行了

2.输出中文时串口助手上显示乱码,代码格式要改为GB 2312,但直接在keil上修改没什么用,我这里是在vscode上修改格式后保存,在keil上运行就行了。

本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
