[学习笔记] 使用DMA+fifo的方式实现日志打印

[复制链接]
7108|30
手机看帖
扫描二维码
随时随地手机跟帖
JasonLee27|  楼主 | 2019-12-6 11:45 | 显示全部楼层 |阅读模式
本帖最后由 JasonLee27 于 2019-12-6 11:45 编辑

硬件环境:通用开发板,ATC-LINK
软件环境:keil 5.23

突然发现我很多地方都喜欢用fifo了

有时候为了方便调试,我们会在代码里增加很多打印信息。使用官方提供的debug库虽然也能实现串口日志输出,但官方的库日志是采用polling方式输出,一个115200波特率的串口输出10个字节大概就要占用1ms的时间,如果我们的系统对时间要求严格,那么大量的打印必然就会占用我们很高的CPU使用权。所以我这里设计了(也不算我设计了,很早前我们做项目跑系统的时候就是这样用的)一个通过DMA+fifo缓存打印的方式来输出日志,这样可以大大降低日志打印对我们系统运行的影响。

实现的原理嘛也不复杂,就是通过一个vsprintf库函数,将你要输出的格式化字符串定向到一个数组buff中,再将这个buff插入fifo。
/**
* [url=home.php?mod=space&uid=42490]@function[/url] Debug_Print
*
* @param[in] fmt: format data
* [url=home.php?mod=space&uid=266161]@return[/url]    none
*
* [url=home.php?mod=space&uid=247401]@brief[/url]  format data and insert to fifo
*/
void Debug_Print(const char *fmt, ...)
{
    uint8_t printBuffer[MAX_PRINT_LENGTH] = {0};
    uint8_t *pStr = NULL;
    va_list ap = {0};
    if (!s_debugState)
        return;
   
    pStr = printBuffer;
    /* if we use print in interrupt handler,must Disable interrupt
       or use mutex */
    DisableInterrupts      
    /* unnamed_p point to first unnamed argument */
    va_start(ap, fmt);
    vsprintf((char *)pStr, fmt, ap);
    va_end(ap);
   
    if (printBuffer[MAX_PRINT_LENGTH - 1] != '\0')
    {
        /* print length is overflow, maybe a Array transboundary has been happened*/
        
        while(1)
        {
            
        }
    }
   
    pStr = &printBuffer[0];
    FIFO_Insert(&s_printFifo, pStr, strlen((const char *)pStr));
    EnableInterrupts
}
然后在一个周期性运行的debugTask任务中去从fifo中取出打印数据,通过DMA将数据发送出去。
/**
* [url=home.php?mod=space&uid=42490]@function[/url] Debug_Task
*
* @param[in] none
* [url=home.php?mod=space&uid=266161]@return[/url]    none
*
* [url=home.php?mod=space&uid=247401]@brief[/url]  Debug cycle function,execute this in cycle
*/
void Debug_Task(uint8_t args)
{
    uint32_t length;
    uint32_t validLen;
    if (!s_debugState)
        return;
   
    length = FIFO_GetLen(&s_printFifo);
    if (length > 0 && s_dmaTxFlag == 0)
    {
        s_dmaTxFlag = 1;
        validLen = FIFO_Retrieve(&s_printFifo, s_dmaBuffer, DUMP_LENGTH);
        UART_StartDMATransmit(2, 8, (uint32_t)s_dmaBuffer, validLen, DMA_CallbackHandler);
    }
   
}


当然了,这份代码有其局限性以及一定的异常风险,比如格式化字符串重定向到一个数组,这个数组的大小因为是固定的,所以一旦打印的格式化字符串长度超过了数组,就会导致数组操作越界,有跑飞的风险。还有fifo也是有大小的,一旦某一时刻的日志产生速度超过了消耗速度,那也会产生溢出导致打印丢失的问题。这些就需要结合实际情况去设计合适的buff大小了。

但也有很方便的地方,在Debug_Print函数中,你可以设计打印等级(比如error,info等等),以及增加打印前缀等等,让你有选择的输出日志。


游客,如果您要查看本帖隐藏内容请回复

使用特权

评论回复
戊戌变法| | 2020-1-7 08:33 | 显示全部楼层
又发现好东西

使用特权

评论回复
carefull3357dc| | 2020-1-7 09:27 | 显示全部楼层
需要有发送完成中断吗?

使用特权

评论回复
JasonLee27|  楼主 | 2020-1-7 09:31 | 显示全部楼层
carefull3357dc 发表于 2020-1-7 09:27
需要有发送完成中断吗?

没有用发送中断,用的是DMA传输完成中断

使用特权

评论回复
carefull3357dc| | 2020-1-7 09:36 | 显示全部楼层
JasonLee27 发表于 2020-1-7 09:31
没有用发送中断,用的是DMA传输完成中断

这样的话,是不是直接比原来的printf();那种方式要省资源?省很多?我之前一直在STM32上做开发,尤其是开发电机的应用,发现这样定时器定时发送状态,非常的占用资源。

使用特权

评论回复
JasonLee27|  楼主 | 2020-1-7 13:34 | 显示全部楼层
carefull3357dc 发表于 2020-1-7 09:36
这样的话,是不是直接比原来的printf();那种方式要省资源?省很多?我之前一直在STM32上做开发,尤其是 ...

不是的,这种方式不省资源,但是省CPU,等于我通过一个fifo将所需要打印的数据都缓存起来,然后通过DMA发送出去,这样发送就几乎不占用CPU,但是增加了fifo,所以会增加内存的占用。还有就是要考虑打印数据高发的情况下是否会导致fifo不够而造成打印丢失。

使用特权

评论回复
carefull3357dc| | 2020-1-7 15:00 | 显示全部楼层
JasonLee27 发表于 2020-1-7 13:34
不是的,这种方式不省资源,但是省CPU,等于我通过一个fifo将所需要打印的数据都缓存起来,然后通过DMA发 ...

好的!了解了!学习学习!

使用特权

评论回复
weict125| | 2020-9-12 15:23 | 显示全部楼层
点赞

使用特权

评论回复
bird777| | 2020-11-21 13:23 | 显示全部楼层

使用特权

评论回复
GrootBrain| | 2021-1-6 16:29 | 显示全部楼层
好东西,收藏

使用特权

评论回复
zhdm| | 2021-1-23 21:01 | 显示全部楼层
谢谢分享

使用特权

评论回复
jcky001| | 2021-1-27 09:23 | 显示全部楼层

好东西,收藏

使用特权

评论回复
czq2317| | 2021-3-31 00:10 | 显示全部楼层
输出调试信息很好用

使用特权

评论回复
非谢家之宝树| | 2021-3-31 19:30 | 显示全部楼层
我用的是从rtt里面的抠出来的

使用特权

评论回复
ehua| | 2021-4-2 08:21 | 显示全部楼层
如果您要查看本帖隐藏内容请回复

使用特权

评论回复
caigang13| | 2021-4-2 09:18 | 显示全部楼层
C语言把fifo实现好了会很香。

使用特权

评论回复
brightchl| | 2021-4-23 15:32 | 显示全部楼层

学习一下

使用特权

评论回复
sihan| | 2021-5-19 15:26 | 显示全部楼层
学习学习!

使用特权

评论回复
wsnsyy| | 2021-11-3 14:11 | 显示全部楼层
mark

使用特权

评论回复
FBI快开门| | 2021-12-17 11:33 | 显示全部楼层
学习学习

使用特权

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

本版积分规则