[STM32 HAL库]串口中断编程思路
一、前言
最近在准备蓝桥杯比赛(嵌入式赛道),研究了以下串口空闲中断+DMA
接收不定长的数据,感觉这个方法的接收效率很高,十分好用。方法配置都成功了,但是有一个点需要进行考虑,就是一般我们需要对串口接收的数据进行处理,这个数据处理是在中断的回调函数
里面处理还是在主函数
里面处理好呢?以下就这两个方法进行分析:
二、方法分析
目前我想到的有两种方法:
方法一
在回调函数里直接处理数据
优点:
缺点:
方法二
在回调函数中设置标志位,在主函数里读取标志位再进行数据处理
优点:
缺点:
总结
三、实际操作
配置的方法可以看之前写的文章
链接: [STM32 HAL库]串口空闲中断+DMA接收不定长数据
实验现象:将电脑发来的数据,原封不到的发送回去。特别注意
BUFF_SIZE
的大小,太小会造成接收数据的丢失。
方法一
在这个方法中,在中断的回调函数
里直接发送回去数据,并手动开启下一次的中断。
#define BUFF_SIZE 128
uint8_t rx_buffer[BUFF_SIZE]; // 创建接收缓存,大小为BUF_SIZE
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_DMA_Init();
MX_USART1_UART_Init();
HAL_UARTEx_ReceiveToIdle_DMA(&huart1,rx_buffer,BUFF_SIZE);//手动开启串口DMA模式接收数据
__HAL_DMA_DISABLE_IT(&hdma_usart1_rx, DMA_IT_HT); //手动关闭DMA_IT_HT中断
while (1)
{
}
}
void SystemClock_Config(void)
{
//...
}
/* 串口接收完成回调函数 */
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
if (huart->Instance == USART1)
{
HAL_UART_Transmit(&huart1, rx_buffer, Size, 0xffff);// 将接收到的数据再发出
HAL_UARTEx_ReceiveToIdle_DMA(&huart1, rx_buffer, BUFF_SIZE); // 接收完毕后重启串口DMA模式接收数据
__HAL_DMA_DISABLE_IT(&hdma_usart1_rx, DMA_IT_HT);// 手动关闭DMA_IT_HT中断
memset(rx_buffer, 0, BUFF_SIZE);// 清除接收缓存
}
}
/* 串口错误回调函数 */
void HAL_UART_ErrorCallback(UART_HandleTypeDef * huart)
{
if(huart->Instance == USART1)
{
HAL_UARTEx_ReceiveToIdle_DMA(&huart1, rx_buffer, BUFF_SIZE); // 接收完毕后重启串口DMA模式接收数据
__HAL_DMA_DISABLE_IT(&hdma_usart1_rx, DMA_IT_HT);// 手动关闭DMA_IT_HT中断
memset(rx_buffer, 0, BUFF_SIZE);// 清除接收缓存
}
}
方法二
在这个方法中,在串口接收完成的回调函数
置接收完成的标志位,然后在主函数
中进行判断。判断成立则进行数据的发送,并手动开启下一次的中断和清除标志位。
需要注意的是,不要在
回调函数
里面手动开启下一次的中断,因为有可能会出现主函数
数据还未处理完成,下一个串口数据就到来而覆盖上一次的串口数据。
所以,这里程序的处理方法是:程序处理完本次数据,则开启下一次中断接收;程序未处理完本次数据,则不开启下一次中断接收。
#define BUFF_SIZE 128
uint8_t rx_buffer[BUFF_SIZE]; // 创建接收缓存,大小为BUF_SIZE
_Bool u1_rx_end_flag = 0; //USART1接收数据完成标志位 1:接收完成
uint16_t u1_rx_size; //USART1接收数据实际长度
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_DMA_Init();
MX_USART1_UART_Init();
HAL_UARTEx_ReceiveToIdle_DMA(&huart1,rx_buffer,BUFF_SIZE);//手动开启串口DMA模式接收数据
__HAL_DMA_DISABLE_IT(&hdma_usart1_rx, DMA_IT_HT); //手动关闭DMA_IT_HT中断
while (1)
{
/* 判断接收是否完成 */
if(u1_rx_end_flag == 1)
{
/* 对接收的数据进行处理 */
HAL_UART_Transmit(&huart1, rx_buffer, u1_rx_size, 0xffff);// 将接收到的数据再发出
memset(rx_buffer, 0, BUFF_SIZE); // 清除接收缓存
/* 开启下一次中断 */
HAL_UARTEx_ReceiveToIdle_DMA(&huart1,rx_buffer,BUFF_SIZE);//手动开启串口DMA模式接收数据
__HAL_DMA_DISABLE_IT(&hdma_usart1_rx, DMA_IT_HT);//手动关闭DMA_IT_HT中断
/* 清除标志位 */
u1_rx_end_flag = 0;
}
}
void SystemClock_Config(void)
{
//...
}
/* 串口接收完成回调函数 */
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
if (huart->Instance == USART1)
{
u1_rx_end_flag = 1; //置标志位
u1_rx_size = Size; //获取接收数据长度
}
}
/* 串口错误回调函数 */
void HAL_UART_ErrorCallback(UART_HandleTypeDef * huart)
{
if(huart->Instance == USART1)
{
HAL_UARTEx_ReceiveToIdle_DMA(&huart1, rx_buffer, BUFF_SIZE);//手动开启串口DMA模式接收数据
__HAL_DMA_DISABLE_IT(&hdma_usart1_rx, DMA_IT_HT);// 手动关闭DMA_IT_HT中断
memset(rx_buffer, 0, BUFF_SIZE);// 清除接收缓存
}
}
值得一提的是,若是没有手动开启
串口空闲中断
,那么串口错误中断
也不会被开启,也就无法进入串口错误回调函数
。
四、实验现象
两个方法实现现象一致
应该还有更好的串口接收模式,现在来说,这个方法应该够用了。
作者:生活最重要(ง •̀_•́)ง