打印

记群内因串口DMA发送而引发的讨论

[复制链接]
331|8
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
雨果喝水|  楼主 | 2023-9-30 19:37 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

本文主要介绍串口+DMA的发送过程。

起因

起初我的发送代码写的是下面这种实现:


void USART0_printf(const char*format , ...)
{
        int tx_len;
        va_list args;
       
    //将要发送的数据写入到DMA发送缓冲区USART0_tx_dma_buf
        va_start(args,format);
        tx_len = vsnprintf((char*)USART0_tx_dma_buf,USART0_TX_DMA_BUF_SIZE,format,args);
        va_end(args);
       
        if(tx_len>0)
        {
                dma_channel_disable(USART0_TX_DMA_CH);     //关闭DMA通道,这样才能设置传输长度
                dma_transfer_number_config(USART0_TX_DMA_CH,tx_len); //设置本次DMA传输长度
                dma_channel_enable(USART0_TX_DMA_CH);     //使能USART0_TX使用的DMA通道,开始DMA传输               
        }
   
    //等待传输完成TC
    while(!usart_flag_get(USART0,USART_FLAG_TC));
    usart_flag_clear(USART_FLAG_TC);
}

使用特权

评论回复
沙发
雨果喝水|  楼主 | 2023-9-30 19:38 | 只看该作者
但是群友提出,不能只等TC标志,原因是在串口DMA发送过程中,可能有比串口发送DMA优先级更高的其他通道开启传输过程,这样就会怠慢串口发送过程,使得TC标志置位,但是实际上发送过程还没有完成。于是增加对传输完成的检查,如下代码。
void USART0_printf(const char*format , ...)
{
        int tx_len;
        va_list args;
       
    //将要发送的数据写入到DMA发送缓冲区USART0_tx_dma_buf
        va_start(args,format);
        tx_len = vsnprintf((char*)USART0_tx_dma_buf,USART0_TX_DMA_BUF_SIZE,format,args);
        va_end(args);
       
        if(tx_len>0)
        {
                dma_channel_disable(USART0_TX_DMA_CH);     //关闭DMA通道,这样才能设置传输长度
                dma_transfer_number_config(USART0_TX_DMA_CH,tx_len); //设置本次DMA传输长度
                dma_channel_enable(USART0_TX_DMA_CH);     //使能USART0_TX使用的DMA通道,开始DMA传输               
        }
   
        //等待DMA传输完成
        while(!dma_flag_get(USART0_TX_DMA_CH,DMA_FLAG_FTF));  
        dma_flag_clear(USART0_TX_DMA_CH,DMA_FLAG_FTF);      
       
    //等待传输完成TC
    while(!usart_flag_get(USART0,USART_FLAG_TC));
    usart_flag_clear(USART_FLAG_TC);
}

使用特权

评论回复
板凳
雨果喝水|  楼主 | 2023-9-30 19:38 | 只看该作者
争论1:使用串口发送的时候,是等串口的TC标志还是TXE标志

结论:一般情况下等TXE标志就行,进入睡眠模式,关闭串口,RS485换向情况下要等TC。

TXE置位说明发送缓冲寄存器空,但不说明串口发送已经完成,因为可能还有数据在移位寄存器中进行移位发送。而TC则代表发送缓冲寄存器空,且移位寄存器也发送完了,发送电路中没有任何残留的数据,它更彻底。所以我的观点是TC标志更加侧重于发送数据的完整性和安全性。尤其是在有睡眠模式,或者需要在运行时关闭串口,以及使用gpio切换RS485收发器芯片的方向时,就必须要等TC标志,而且它的影响只是让下一包数据的发送延迟一个串口帧的间隔(约10bit)。而赞成等TXE标志的人则认为,这样使得发送过程更加高效,即便是包和包之间都可以做到无间隔。当串口需要被关闭或者需要进入睡眠模式时,才去等TC标志,这样的观点我也赞同。但是使用GPIO驱动RS485换向的情况只能等TC,这个没得商量,除非硬件上实现了自动方向切换。

使用特权

评论回复
地板
雨果喝水|  楼主 | 2023-9-30 19:38 | 只看该作者
争论2:串口DMA发送的时候,是否需要等TXE标志

结论:不需要

使用DMA发送串口数据时,我们将N个字节的数据写入到DMA发送缓冲区,设置传输长度,然后使能DMA通道开始传输,直到传输完成。在传输过程中,DMA和串口发送器之所以配合的天衣无缝,就是因为只有当TXE标志置位的时候,串口才会受理DMA的发送请求,也就是说,DMA发送的内部实现本身就依靠了TXE标志,所以,我们在写DMA发送程序的时候,根本不需要软件等待TXE标志。

使用特权

评论回复
5
雨果喝水|  楼主 | 2023-9-30 19:39 | 只看该作者
争论3:使用DMA发送的时候,等待标志的代码应该是在发送前还是发送后

结论:放在发送的前

在每次开始一次发送过程前,都需要确保本次的发送不会影响上一次的发送过程。那么有两种实现方案:①每次发送后,都检查并等待相关标志,确保发送完成而不会被下一次发送影响 ②每次发送前,先检查标志,确保达到可以发送而不影响上一次发送的条件,然后再开始发送。

使用特权

评论回复
6
雨果喝水|  楼主 | 2023-9-30 19:39 | 只看该作者
理论上两种方案都可以,但是实际开发过程中,第二种更高效。因为第二种方案,将数据丢给DMA后,就立马退出发送函数了,可以让CPU去干别的事情,而不是干等着,更加符合DMA发送的初衷。

最终,我的串口DMA发送函数实现如下。

使用特权

评论回复
7
雨果喝水|  楼主 | 2023-9-30 19:39 | 只看该作者
需要注意的是,不能仅判断DMA的发送完成标志是否置位,因为DMA初始化后,发送完成标志是清零状态,这样的话第一个发送动作***卡在了等待DMA发送完成的条件上。所以附加了一个DMA通道使能的条件,初始化后,DMA发送通道是禁用的。

使用特权

评论回复
8
雨果喝水|  楼主 | 2023-9-30 19:39 | 只看该作者
 
void USART0_printf(const char*format , ...)
{
        int tx_len;
        va_list args;
       
        //如果dma传输通道是使能的,且上一次的传输还没完成
        while((DMA_CHCTL(USART0_TX_DMA_CH)&0x01) &&  (!dma_flag_get(USART0_TX_DMA_CH,DMA_FLAG_FTF)));  //等待DMA传输完成
        dma_flag_clear(USART0_TX_DMA_CH,DMA_FLAG_FTF);       //清除传输完成标志
       
        va_start(args,format);
        tx_len = vsnprintf((char*)USART0_tx_dma_buf,USART0_TX_DMA_BUF_SIZE,format,args);
        va_end(args);
       
        if(tx_len>0)
        {
                dma_channel_disable(USART0_TX_DMA_CH);     //关闭DMA通道,这样才能设置传输长度
                dma_transfer_number_config(USART0_TX_DMA_CH,tx_len); //设置本次DMA传输长度
                dma_channel_enable(USART0_TX_DMA_CH);     //使能USART0_TX使用的DMA通道,开始DMA传输               
        }
}

使用特权

评论回复
9
xia00| | 2023-10-22 10:16 | 只看该作者
在实现DMA传输时,是由DMA控制器直接掌管总线,因此,存在着一个总线控制权转移问题。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

71

主题

1071

帖子

0

粉丝