#技术资源# #申请原创# @21小跑堂
平时写代码时,总是需要用到打印调试输出,之前每次都是直接使用 printf 函数直接打印的,但是越来越发现很不方便。比如有时我想屏蔽打印调试信息时,总不能回去一点点的把原来的打印代码删除了吧。有时我需要分级别打印时,直接使用 printf 函数打印也不好操作。
不过,在网上我发现了一个打印调试的日志库 EasyLogger ,这个 log 库的功能非常强大,可以满足各种需求了(到时我写文章记录下该库的使用心得)。
但是,我其实又不用这么强大的功能,只需要最基本的功能就能满足我的需求。虽然 EasyLogger 移植使用起来也不难,但自己所需要的功能也不用那么多。于是就自己捣鼓一下 log 的调试输出代码。
1. log打印调试代码实现
我大概需要的功能就是:
我自己参考了 RT-Thread 写了这部分代码。
其中核心代码就是这部分分级打印代码:
dbg_log_line 宏定义如下:
这个宏的第一行代码就是首先打印输出 log 信息的前缀以及设置打印信息的显示颜色,打印的前缀我们可以根据需要自己设置,一般都是设置打印出文件名、行数等等。接着就是打印我们真正的 log 信息,最后一行接着打印换行。
完整的代码如下:
- #ifndef __LOG_H__
- #define __LOG_H__
- #ifdef __cplusplus
- extern "C" {
- #endif
- #include <stdio.h>
- /* 日志输出总开关 */
- #define DBG_ENABLE
- /* 颜色输出开关 */
- #define DBG_COLOR
- /* 日志打印级别 */
- #define DBG_ERROR 0
- #define DBG_WARNING 1
- #define DBG_INFO 2
- #define DBG_LOG 3
- /* 设置日志打印级别,级别越高输出的日志信息越多 */
- #define DBG_LEVEL DBG_LOG
- #ifdef DBG_ENABLE
- /*
- * The color for terminal (foreground)
- * BLACK 30
- * RED 31
- * GREEN 32
- * YELLOW 33
- * BLUE 34
- * PURPLE 35
- * CYAN 36
- * WHITE 37
- */
- #ifdef DBG_COLOR
- #define _DBG_COLOR(n) printf("\033["#n"m")
- #define _DBG_LOG_HDR(lvl_name, color_n) \
- printf("\033["#color_n"m[" lvl_name "/" "<%s:%d>" "] ", __FILE__, __LINE__)
- #define _DBG_LOG_X_END \
- printf("\033[0m\n")
- #else
- #define _DBG_COLOR(n)
- #define _DBG_LOG_HDR(lvl_name, color_n) \
- printf("[" lvl_name "/" "%s:%d" "] ", __FILE__, __LINE__)
- #define _DBG_LOG_X_END \
- printf("\n")
- #endif /* DBG_COLOR */
- #define dbg_log_line(lvl, color_n, fmt, ...) \
- do \
- { \
- _DBG_LOG_HDR(lvl, color_n); \
- printf(fmt, ##__VA_ARGS__); \
- _DBG_LOG_X_END; \
- } \
- while (0)
- #else
- #define dbg_log_line(lvl, color_n, fmt, ...)
- #endif /* DBG_ENABLE */
- /* debug */
- #if (DBG_LEVEL >= DBG_LOG)
- #define LOG_D(fmt, ...) dbg_log_line("D", 0, fmt, ##__VA_ARGS__)
- #else
- #define LOG_D(...)
- #endif
- /* info */
- #if (DBG_LEVEL >= DBG_INFO)
- #define LOG_I(fmt, ...) dbg_log_line("I", 32, fmt, ##__VA_ARGS__)
- #else
- #define LOG_I(...)
- #endif
- /* warn */
- #if (DBG_LEVEL >= DBG_WARNING)
- #define LOG_W(fmt, ...) dbg_log_line("W", 33, fmt, ##__VA_ARGS__)
- #else
- #define LOG_W(...)
- #endif
- /* error */
- #if (DBG_LEVEL >= DBG_ERROR)
- #define LOG_E(fmt, ...) dbg_log_line("E", 31, fmt, ##__VA_ARGS__)
- #else
- #define LOG_E(...)
- #endif
- #ifdef __cplusplus
- }
- #endif
- #endif /* __LOG_H__ */
2. 在 ubuntu 环境下测试 log 打印
由于输出的信息颜色,有些终端可能不支持,所以在 ubuntu 环境下测试。
测试代码其实很简单,就是直接打印信息就行。
- #include <stdio.h>
- #include "log.h"
- int main(void)
- {
- LOG_D("***********************log test start***********************");
- LOG_D("hello world...");
- LOG_I("hello world...");
- LOG_W("hello world...");
- LOG_E("hello world...");
-
- LOG_D("***********************log test end***********************");
- return 0;
- }
打印输出效果如下:
可以看到有不同的颜色效果。而且,如果是不支持颜色显示的话,可以关闭颜色显示功能。
3. 在 APM32 上测试 log 打印
3.1 重定向 printf
在 MCU 上测试的话,需要有串口的支持。我使用的测试开发板是 APM32F407ZGT6 芯片,关于串口的初始化相关的代码,参考使用官方的SDK即可,这里不多讲。如果你使用的是其他芯片,自己添加初始化串口相关的代码。
初始化完串口之后,我们要想使用 printf 打印函数,需要重定向这个函数是向串口打印输出信息的。
我们只要在工程代码中,重新实现 fputc 这个函数即可。我使用 HAL 库编写的代码如下:
- int fputc(int ch, FILE* f)
- {
- if (ch == '\n')
- {
- while (USART_ReadStatusFlag(USART1, USART_FLAG_TXBE) == RESET);
- USART_TxData(USART1, '\r');
- }
- /* wait for the data to be send */
- while (USART_ReadStatusFlag(USART1, USART_FLAG_TXBE) == RESET);
-
- /* send a byte of data to the serial port */
- USART_TxData(USART1, (uint8_t)ch);
- return (ch);
- }
3.2 在 MDK 中配合使用 MicroLIB 库
重新实现了 fputc 函数之后,我们还需要配合使用 MDK 提供的 MicroLIB ,这个库其实就是标准的 C 库高度优化而来的,占用资源更少。
在配置串口配置如下:
在配置框中勾上 Use MicroLib 。一定要选择这个,不选择的话,会导致输出不成功的。
3.3 不勾选 MicroLIB 库的方法
如果我们不勾选 Use MicroLib 也想正常打印输出调试信息的话,那就要我们多添加一些代码了。
因为 printf 函数使用了半主机模式,所以直接使用这个函数会导致程序无法运行的,我们需要提前告诉编译器不要使用半主机模式。
不勾选 Use MicroLib 库需要添加的完整代码如下:
- #if 1
- /* 告知连接器不从C库链接使用半主机的函数 */
- #pragma import(__use_no_semihosting)
- /* 标准库需要的支持数据类型 */
- struct __FILE
- {
- int handle;
- };
-
- FILE __stdout;
- /**
- * [url=home.php?mod=space&uid=247401]@brief[/url] 定义_sys_exit()以避免使用半主机模式
- * @param void
- * [url=home.php?mod=space&uid=266161]@return[/url] void
- */
- void _sys_exit(int x)
- {
- x = x;
- }
- int fputc(int ch, FILE* f)
- {
- if (ch == '\n')
- {
- while (USART_ReadStatusFlag(USART1, USART_FLAG_TXBE) == RESET);
- USART_TxData(USART1, '\r');
- }
- /* wait for the data to be send */
- while (USART_ReadStatusFlag(USART1, USART_FLAG_TXBE) == RESET);
-
- /* send a byte of data to the serial port */
- USART_TxData(USART1, (uint8_t)ch);
- return (ch);
- }
- #endif
这样添加了上述代码之后,我们就算不够选 Use MicroLib 库也可以正常打印输出日志信息了。
3.4 log模块打印测试效果
在 main 函数中,我们添加在 ubuntu 下测试的那几行打印测试代码,然后打开 MobaXterm 终端软件,可以看到如下打印效果:
可以看到有不同颜色的打印效果,如果不想打印某些信息的级别,修改打印日志输出级别的宏定义就行。
注意:如果是使用常用的串口助手测试工具的话,可能不支持输出信息带颜色的,而且反而会看到一些不需要的颜色编码,这个时候我们可以把颜色输出的宏定义关闭就可以不用输出颜色了。
|