完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
问题描述
使用STM32的串口进行DMA发送(Noraml模式),在某个任务中连续调用两次发送函数log_printf(),但是发回的数据在串口调试助手上显示与预期不符。第一次发送的数据有一部分被第二次发送的数据覆盖,如图所示: 任务代码如下: /* Log_Task function */ void Log_Task(void const * argument) { /* USER CODE BEGIN Log_Task */ /* Infinite loop */ for(;;) { if(router_rx_flag == 1) { router_rx_flag = 0; log_printf("Get okrn"); log_printf("%s",router_rx_buffer); } osDelay(100); } /* USER CODE END Log_Task */ } 从代码中可以看出,期望的结果应该是下图这样: log_printf函数代码如下: /* * 名称: log_printf * 功能: 在串口1上打印出日志内容 * 输入: 格式化输出的字符串 * 输出: 无 */ void log_printf(const char *format ,... ) { va_list arg; static char tx_buffer[256]={""}; //把数据处理后放进缓冲区 va_start(arg, format); vsprintf((char *)tx_buffer, format, arg); va_end(arg); //开始发送数据 send_to_router((u8 *)tx_buffer,strlen(tx_buffer)); } send_to_router函数代码如下: void send_to_router(unsigned char *buffer,unsigned int length) { //等待上一次的数据发送完毕 while(HAL_DMA_GetState(&hdma_usart1_tx) == HAL_DMA_STATE_BUSY) osDelay(1); /* 关闭DMA */ __HAL_DMA_DISABLE(&hdma_usart1_tx); //开始发送数据 HAL_UART_Transmit_DMA(&huart1,buffer,length); // while(HAL_DMA_GetState(&hdma_usart1_tx) == HAL_DMA_STATE_BUSY) osDelay(1); /* 放在此处可以保证每次发送完全,但会占用时间 */ } 串口中断接收处理函数如下: /* * 名称: router_parse * 功能: 接收路由器数据的解析,在回调函数中调用 * 输入: 空闲中断时串口1接收的数据长度 * 输出: 无 */ void router_parse(uint16_t buffer_len) { char *p_start = NULL,*p_end = NULL; /* 只提取一帧NMEA数据,$开头,n结尾 */ p_start = strchr(usart1_rx_buffer,'$'); if(p_start != NULL) { p_end = strchr(p_start,'n'); if(p_end != NULL) { memcpy(router_rx_buffer, p_start, (p_end - p_start + 1)); /* 保存数据 */ router_rx_flag = 1; } } } 分析过程 以前一直以为是send_to_router函数中的 //等待上一次的数据发送完毕 while(HAL_DMA_GetState(&hdma_usart1_tx) == HAL_DMA_STATE_BUSY) osDelay(1); 这一句的问题,即由于某种原因导致DMA缓存中数据未发送完全,但DMA状态却被释放了,结果重新开始了新一轮的发送,导致上次数据的后半部分被覆盖。但无论如何调试,都无法证实这个猜想,DMA外设没有出过任何异常。 今天仔细观察了一下,“Getckey”和“Get okrn”和”$Mickeyrn“,为什是第二次发送的内容的后半部覆盖了第一次发送的内容,一般不应该是前半部分”(美元符号,此处会排版出错)Mic”吗?问题的原因可能与状态位无关。于是我再审视了一下send_to_router函数:void send_to_router(unsigned char *buffer,unsigned int length)突然间想到,入参只是一个指针,发送缓存区在log_printf函数中 static char tx_buffer[256]={""}; 整理一下,整个发送过程流程如下: log_printf(“Get okrn”);时,“Get okrn”被装进了tx_buffer,附带一个发送长度8字节。 send_to_router函数中,HAL_UART_Transmit_DMA(&huart1,buffer,length);开启了这个8个字节的发送。 8个字节可能只完成了“Get”的发送, log_printf(“%s”,router_rx_buffer);(即log_printf(“$Mickeyrn“);)已经开始执行。 ”$Mickeyrn“被装进tx_buffer,附带一个发送长度9字节。 send_to_router函数中,因为上一次数据还没有发送完全,进入DMA状态等待循环。但是DMA发送指针char *buffer原本指向的那个地址的内容” okrn“已经被”ckeyrn“代替,所以就变成了”Getckeyr“。由于显示原因,只看到”Getckey“。 解决办法 把while(HAL_DMA_GetState(&hdma_usart1_tx) == HAL_DMA_STATE_BUSY) osDelay(1);这一句放到缓存区tx_buffer装载步骤之前即可: /* * 名称: log_printf * 功能: 在串口1上打印出日志内容 * 输入: 格式化输出的字符串 * 输出: 无 */ void log_printf(const char *format ,... ) { va_list arg; static char tx_buffer[256]={""}; //等待上一次的数据发送完毕 while(HAL_DMA_GetState(&hdma_usart1_tx) == HAL_DMA_STATE_BUSY) osDelay(1); //把数据处理后放进缓冲区 va_start(arg, format); vsprintf((char *)tx_buffer, format, arg); va_end(arg); //开始发送数据 send_to_router((u8 *)tx_buffer,strlen(tx_buffer)); } 至于send_to_router函数中的该代码,保留或删除都可以。 后言 很久以前就开始使用STM32的DMA串口发送功能,套路基本上就是曾经的博文《iar中使用DMA+printf+uart1》所描述的那样。后来开始用STM32CubeMX了,把之前的例程稍微做了一些修改,调试成功之后,就一直沿用至今。期间,这个问题困扰了我很久,虽然在写代码时稍微注意一下就可避免其发生,但做威廉希尔官方网站 的人都明白:千里之堤,溃于蝼蚁,放过任何一个小细节都可能在将来引发重大灾难。很庆幸今天能够找到问题的原因。 再回去看来一遍《iar中使用DMA+printf+uart1》,其实这个问题的答案很早就写在里面了。。。 找个时间,我会专门写一篇使用DMA串口Normal模式发送的博文,还是以Cube来创建工程。届时,再用一个例程完整复现和解决这个问题。 |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1975 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1760 浏览 1 评论
1232 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
819 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1776 浏览 2 评论
2015浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
892浏览 4评论
stm32f4下spi+dma读取数据不对是什么原因导致的?
318浏览 3评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
671浏览 3评论
662浏览 3评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2025-2-24 04:52 , Processed in 0.363162 second(s), Total 43, Slave 37 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191