STM32串口DMA连续发送两帧,导致数据部分覆盖的问题

发布者:森绿企鹅最新更新时间:2018-06-02 来源: eefocus关键字:STM32  串口 手机看文章 扫描二维码
随时随地手机看文章

问题描述


使用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 ok\r\n");

          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 ok\r\n”和”$Mickey\r\n“,为什是第二次发送的内容的后半部覆盖了第一次发送的内容,一般不应该是前半部分”(美元符号,此处会排版出错)Mic”吗?问题的原因可能与状态位无关。于是我再审视了一下send_to_router函数:void send_to_router(unsigned char *buffer,unsigned int length)突然间想到,入参只是一个指针,发送缓存区在log_printf函数中


static char tx_buffer[256]={""};

1

整理一下,整个发送过程流程如下:


log_printf(“Get ok\r\n”);时,“Get ok\r\n”被装进了tx_buffer,附带一个发送长度8字节。

send_to_router函数中,HAL_UART_Transmit_DMA(&huart1,buffer,length);开启了这个8个字节的发送。

8个字节可能只完成了“Get”的发送, log_printf(“%s”,router_rx_buffer);(即log_printf(“$Mickey\r\n“);)已经开始执行。

”$Mickey\r\n“被装进tx_buffer,附带一个发送长度9字节。

send_to_router函数中,因为上一次数据还没有发送完全,进入DMA状态等待循环。但是DMA发送指针char *buffer原本指向的那个地址的内容” ok\r\n“已经被”ckey\r\n“代替,所以就变成了”Getckey\r“。由于显示原因,只看到”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来创建工程。届时,再用一个例程完整复现和解决这个问题。


关键字:STM32  串口 引用地址:STM32串口DMA连续发送两帧,导致数据部分覆盖的问题

上一篇:LPC17xx Uart0,Uart1,Uart2,Uart3的区别
下一篇:STM32串口使用心得(二)——串口溢出中断(ORE)

推荐阅读最新更新时间:2024-03-16 16:04

stm32的IO 8种基本类型设置
GPIO_Mode_AIN //模拟输入 一般用作ADC等 GPIO_Mode_IN_FLOATING //浮空输入 可以用作按键 GPIO_Mode_IPD //下拉输入 GPIO_Mode_IPU //上垃输入 GPIO_Mode_Out_OD //开漏输出 GPIO_Mode_Out_PP //推挽输出 GPIO_Mode_AF_OD //复用开漏输出 GPIO_Mode_AF_PP //复用推挽输出 IO的输出类型设置主要还是查询芯片手册或者根据自己应用设置对应的类型。
[单片机]
STM32的理解
现谈一点单片机的简单知识及STM32相比于其他单片机的优势。单片机最小系统需要有晶振电路,复位电路,电源供电以及接地,简单来说单片机就是通过芯片来控制外围电路的一个微型控制器。相比较于Arduino,AVR,K60,STM32拥有更丰富的外设,这就意味着它有更强大的功能,完成更多的实验。同时STM32拥有更快的图像采集速度。再来随便讲一些有关单片机的简单知识:晶振——一个用于给整个单片机计时的元器件,同时STM32其他部位上还有一些时钟,用于控制该部分是否工作。一些其他程序:看门狗——用于检测程序的运行是否正常,在自己的程序“跑飞”的时候能够及时将程序复位。中断——在自己的程序运行的时候突然有另一个程序介入,自己的程序中断,运行介
[单片机]
STM32的RTC晶振不起振的原因及解决方法
STM32的RTC晶振经常出现不起振的问题,这已经是“业界共识”了。很多人在各种电子论坛上求助类似于“求高手指点!RTC晶振不起振怎么办”的问题,而其答案基本可以概括为“这次高手帮不了你了”   更有阴谋论者提出让人啼笑皆非的解释——STM32的RTC晶振不起振是ST与晶振厂商串通后故意搞出来的,目的是提高某晶振厂商高端晶振的销量。   最近做的几块板子也用到了STM32的RTC,前后两版一共做了大概6片,幸运的是并未遇到晶振不起振的现象。而我采用的是3毛钱一个的普通晶振,并未选用传说中低负载高精度晶振。后来在另外一片实验性质的板子上首次遇到了晶振不起振的问题,而且做了2片都不起振,这才让我意识到这个问题的严重性。   从上述现象
[单片机]
<font color='red'>STM32</font>的RTC晶振不起振的原因及解决方法
基于TMS320DM642的红外监控系统设计与实现
    红外技术自诞生以来,红外探测器作为一种在常温下即可将任何物体发出的红外辐射转换为电信号的热辐射转换器,以其被动探测隐蔽性强、体积小巧易携带等诸多优势,广泛应用于红外侦查与跟踪、雷达制导、电力监测、安防监控、医疗诊断等军事及民用领域。红外监控技术集红外热成像技术与传统的视频监控技术的优势于一体,有效地弥补了传统视频监控系统的不足,是未来安防监控领域的重要发展方向。本文在客户端/服务器的系统架构基础上提出了以TMS320DM642为核心的红外监控系统,前端采用FLIR公司的红外热成像仪,红外图像数据经过H.264技术压缩后通过以太网传输至客户端PC机,利用由Visual C++平台开发的客户端解码器解压缩后还原红外图像并显示。整
[嵌入式]
STMCU应用过程中与电源相关的案例分享
我们在从事STM32单片机的应用开发及调试过程中,往往会碰到各类异常。其中有不少比例的问题跟电源有关。对于一个电子产品而言,电源部分很关键、很重要,但在实际开发调试中,我们偶尔会有意无意的忽视它。这里分享几个实际案例,以加强刺激,加深印象。 毕竟因为电源问题可能导致的异常很多很多,这里分享几个案例算是抛砖引玉,希望大家在调试中对电源方面加以重视。个人认为,往往电源出问题时导致的异常时并不太好分析。多数时候异常表现得更为诡异或没章法。 注:下面提到的案例中异常原因都与电源有关,但并不是说出现类似异常时一定是电源的原因。 下面主要分享五个基于STM32应用的案例。 案例1:STM32芯片的PLL无法正常工作。 有人使用STM3
[单片机]
STMCU应用过程中与电源相关的案例分享
STM32模拟I2C程序
/******************************************************************************* 测试平台:STM32F103ZET6最小系统 *******************************************************************************/ static void i2cDelay() { volatile int i = 7; while (i) i--; } // SCL高电平期间,SDA出现下降沿为起始信号 static bool i2cStart() { SDA_OUT;
[单片机]
STM32处理器A/D转换输入电阻与采样时间的分析
当经过运算放大器隔离之后的信号输入到STM32处理器的模拟输入口时, 我们需要串联一个电阻,根据规格书的A/D转换电路框图所示, 图1.A/D转换电路框图闪 图中RAIN为外部输入电阻,最大值为50KΩ; RADC为内部采样开关电阻,最大值为1KΩ; CADC为内部采样和保持电容,最大值为8pF. 图2.ADC特性参数 而外部输入电阻的取值与采样时间、采样保持电容有关,如下图: 图3. 外部电容最大取值与采样时间关系 这些取值背后有什么深层的逻辑,外部输入电阻RAIN起到什么作用,应当如何取值? 外部输入电阻的作用 一、限流作用 根据图1所示的框图,处理器的模拟输入口内置了上、下拉的保护二极管。 我们所说的输入口阻抗高
[单片机]
<font color='red'>STM32</font>处理器A/D转换输入电阻与采样时间的分析
STM32单片机知识汇总
1、 AHB系统总线分为APB1(36MHz)和APB2(72MHz),其中2 1,意思是APB2接高速设备 2、 Stm32f10x.h相当于reg52.h(里面有基本的位操作定义),另一个为stm32f10x_conf.h专门控制外围器件的配置,也就是开关头文件的作用 3、 HSE Osc(High Speed External Oscillator)高速外部晶振,一般为8MHz,HSI RC(High Speed InternalRC)高速内部RC,8MHz 4、 LSE Osc(Low Speed External Oscillator)低速外部晶振,一般为32.768KHz,LSI RC(Low Speed Intern
[单片机]
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
随便看看
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved