本帖最后由 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等等),以及增加打印前缀等等,让你有选择的输出日志。
|