网站系统改教程,西青天津网站建设,免费做字体的网站,网站建设服务代理商一、前言
最近在准备蓝桥杯比赛#xff08;嵌入式赛道#xff09;#xff0c;研究了以下串口空闲中断DMA接收不定长的数据#xff0c;感觉这个方法的接收效率很高#xff0c;十分好用。方法配置都成功了#xff0c;但是有一个点需要进行考虑#xff0c;就是一般我们需要…一、前言
最近在准备蓝桥杯比赛嵌入式赛道研究了以下串口空闲中断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);// 清除接收缓存}
}值得一提的是若是没有手动开启串口空闲中断那么串口错误中断也不会被开启也就无法进入串口错误回调函数。 四、实验现象
两个方法实现现象一致 应该还有更好的串口接收模式现在来说这个方法应该够用了。