??????? 对mcu的开发调试过程中,通过串口打印运行过程中的一些信息,有时候比调试器好用。以及在产品使用中,通过对关键运行状态的输出,对于运行故障的判断分析,具有很高的价值。如何用printf或者类似这样函数来实现呢?
??????? 先讲原理:printf本身不具备输出功能,只是式样化输出函数, 一般用于向准则输出设备按规定式样输出消息。例如语句:
uint8_t hour = 10, minute = 25, second = 3;
Printf("Now Time %02d:%02d:%02d\n", hour, minute, second);
只是转化为语句:Now Time 10:25:03 [换行],这个语句去哪了?由函数fputc处理了。
注:函数printf和fputc都是C语言的标准库函数(stdio.h)。
那么,如何实现串口的格式化输出呢?
方法1,重定向函数fputc
??????? 上面提到,printf是格式化数据,然后通过fputc实现输出。可以输出到文件,显示器,串口等等,取决于fputc函数的功能。
??????? 重新写一个fputc函数,将会自动替代标准库函数。(华大代码)
???????
int fputc(int ch, FILE *f)
{
(void)f; /* Prevent unused argument compilation warning */
return (Ok == UartPutChar(m_PrintfDevice, (char)ch)) ? ch: -1;
}
ch就是需要发送的一个字符,FILE *f这个参数不用管(将字符写入文件用的,文件描述符)。
根据mcu底层,将数据写入发送寄存器或者缓冲器。注意的是,一次发送一个字符,一个printf会频繁调用fputc,必须要串口的缓冲器为空才能写入。
可以看到,这种方法效率太低。等待前一个数据发出后,才能发送下一个。
另外一种方法就是在fputc函数中,把字符放到ringbuffer,再ringbuffer的数据通过串口发送出去,这样效率就非常高了。
方法2,写一个函数,格式化加串口发送
??????? 根据以上的描述,我们了解了串口格式化输出的原理,自己写一个函数来实现。
void LogPrintfUser(const char *fmt, ...)
{
va_list ap;
int len = 0;
uint8_t buffer[UART_LOG_MAX_LEN];
if (fmt == NULL) return;
memset(buffer, 0, UART_LOG_MAX_LEN);
va_start(ap, fmt);
len = vsnprintf((char *)buffer, UART_LOG_MAX_LEN, fmt, ap);
va_end(ap);
RingBuffer_InsertMult(&RB_Debug_TX, buffer, len);
DBG_TxStart();
}
|