返回列表 发新帖我要提问本帖赏金: 50.00元(功能说明)

[APM32F4] 在APM32F4上实现实现一个简易的log打印调试模块

[复制链接]
2643|7
 楼主| luobeihai 发表于 2024-12-29 18:08 | 显示全部楼层 |阅读模式

#技术资源# #申请原创# @21小跑堂

平时写代码时,总是需要用到打印调试输出,之前每次都是直接使用 printf 函数直接打印的,但是越来越发现很不方便。比如有时我想屏蔽打印调试信息时,总不能回去一点点的把原来的打印代码删除了吧。有时我需要分级别打印时,直接使用 printf 函数打印也不好操作。

不过,在网上我发现了一个打印调试的日志库 EasyLogger ,这个 log 库的功能非常强大,可以满足各种需求了(到时我写文章记录下该库的使用心得)。

但是,我其实又不用这么强大的功能,只需要最基本的功能就能满足我的需求。虽然 EasyLogger 移植使用起来也不难,但自己所需要的功能也不用那么多。于是就自己捣鼓一下 log 的调试输出代码。

1. log打印调试代码实现

我大概需要的功能就是:

  • 可以打开、关闭调试输出信息
  • 输出信息有过滤机制,比如只打印报错、警告信息,过滤掉一些打印调试信息。输出过滤机制可以使用分级打印,这种方法很常用的,比如 linux 内核的打印信息也是有分级打印的。
  • 做的漂亮点,可以根据输出信息级别,打印信息显示的颜色不一样。

我自己参考了 RT-Thread 写了这部分代码。

其中核心代码就是这部分分级打印代码:

image-20220830184931797.png

dbg_log_line 宏定义如下:

image-20220830185043754.png

这个宏的第一行代码就是首先打印输出 log 信息的前缀以及设置打印信息的显示颜色,打印的前缀我们可以根据需要自己设置,一般都是设置打印出文件名、行数等等。接着就是打印我们真正的 log 信息,最后一行接着打印换行。

完整的代码如下:

  1. #ifndef __LOG_H__
  2. #define __LOG_H__

  3. #ifdef __cplusplus
  4. extern "C" {
  5. #endif

  6. #include <stdio.h>

  7. /* 日志输出总开关 */
  8. #define DBG_ENABLE

  9. /* 颜色输出开关 */
  10. #define DBG_COLOR

  11. /* 日志打印级别 */
  12. #define DBG_ERROR           0
  13. #define DBG_WARNING         1
  14. #define DBG_INFO            2
  15. #define DBG_LOG             3

  16. /* 设置日志打印级别,级别越高输出的日志信息越多 */
  17. #define DBG_LEVEL         DBG_LOG


  18. #ifdef DBG_ENABLE

  19. /*
  20. * The color for terminal (foreground)
  21. * BLACK    30
  22. * RED      31
  23. * GREEN    32
  24. * YELLOW   33
  25. * BLUE     34
  26. * PURPLE   35
  27. * CYAN     36
  28. * WHITE    37
  29. */
  30. #ifdef DBG_COLOR
  31. #define _DBG_COLOR(n)        printf("\033["#n"m")
  32. #define _DBG_LOG_HDR(lvl_name, color_n)                    \
  33.     printf("\033["#color_n"m[" lvl_name "/" "<%s:%d>" "] ", __FILE__, __LINE__)
  34. #define _DBG_LOG_X_END                                     \
  35.     printf("\033[0m\n")
  36. #else
  37. #define _DBG_COLOR(n)
  38. #define _DBG_LOG_HDR(lvl_name, color_n)                    \
  39.     printf("[" lvl_name "/" "%s:%d" "] ", __FILE__, __LINE__)
  40. #define _DBG_LOG_X_END                                     \
  41.     printf("\n")
  42. #endif /* DBG_COLOR */


  43. #define dbg_log_line(lvl, color_n, fmt, ...)                \
  44.     do                                                      \
  45.     {                                                       \
  46.         _DBG_LOG_HDR(lvl, color_n);                         \
  47.         printf(fmt, ##__VA_ARGS__);                             \
  48.         _DBG_LOG_X_END;                                     \
  49.     }                                                       \
  50.     while (0)

  51. #else
  52. #define dbg_log_line(lvl, color_n, fmt, ...)
  53. #endif /* DBG_ENABLE */


  54. /* debug */
  55. #if (DBG_LEVEL >= DBG_LOG)
  56. #define LOG_D(fmt, ...)      dbg_log_line("D", 0, fmt, ##__VA_ARGS__)
  57. #else
  58. #define LOG_D(...)
  59. #endif

  60. /* info */
  61. #if (DBG_LEVEL >= DBG_INFO)
  62. #define LOG_I(fmt, ...)      dbg_log_line("I", 32, fmt, ##__VA_ARGS__)
  63. #else
  64. #define LOG_I(...)
  65. #endif

  66. /* warn */
  67. #if (DBG_LEVEL >= DBG_WARNING)
  68. #define LOG_W(fmt, ...)      dbg_log_line("W", 33, fmt, ##__VA_ARGS__)
  69. #else
  70. #define LOG_W(...)
  71. #endif

  72. /* error */
  73. #if (DBG_LEVEL >= DBG_ERROR)
  74. #define LOG_E(fmt, ...)      dbg_log_line("E", 31, fmt, ##__VA_ARGS__)
  75. #else
  76. #define LOG_E(...)
  77. #endif


  78. #ifdef __cplusplus
  79. }
  80. #endif

  81. #endif /* __LOG_H__ */

2. 在 ubuntu 环境下测试 log 打印

由于输出的信息颜色,有些终端可能不支持,所以在 ubuntu 环境下测试。

测试代码其实很简单,就是直接打印信息就行。

  1. #include <stdio.h>
  2. #include "log.h"

  3. int main(void)
  4. {
  5.         LOG_D("***********************log test start***********************");

  6.         LOG_D("hello world...");
  7.         LOG_I("hello world...");
  8.         LOG_W("hello world...");
  9.         LOG_E("hello world...");
  10.        
  11.         LOG_D("***********************log test end***********************");

  12.         return 0;
  13. }

打印输出效果如下:

image-20220830185651454.png

可以看到有不同的颜色效果。而且,如果是不支持颜色显示的话,可以关闭颜色显示功能。

3. 在 APM32 上测试 log 打印

3.1 重定向 printf

在 MCU 上测试的话,需要有串口的支持。我使用的测试开发板是 APM32F407ZGT6 芯片,关于串口的初始化相关的代码,参考使用官方的SDK即可,这里不多讲。如果你使用的是其他芯片,自己添加初始化串口相关的代码。

初始化完串口之后,我们要想使用 printf 打印函数,需要重定向这个函数是向串口打印输出信息的。

我们只要在工程代码中,重新实现 fputc 这个函数即可。我使用 HAL 库编写的代码如下:

  1. int fputc(int ch, FILE* f)
  2. {
  3.     if (ch == '\n')
  4.     {
  5.         while (USART_ReadStatusFlag(USART1, USART_FLAG_TXBE) == RESET);

  6.         USART_TxData(USART1, '\r');
  7.     }

  8.     /* wait for the data to be send */
  9.     while (USART_ReadStatusFlag(USART1, USART_FLAG_TXBE) == RESET);
  10.    
  11.     /* send a byte of data to the serial port */
  12.     USART_TxData(USART1, (uint8_t)ch);

  13.     return (ch);
  14. }

3.2 在 MDK 中配合使用 MicroLIB 库

重新实现了 fputc  函数之后,我们还需要配合使用 MDK 提供的 MicroLIB ,这个库其实就是标准的 C 库高度优化而来的,占用资源更少。

在配置串口配置如下:

image-20220830212110430.png

在配置框中勾上 Use MicroLib 。一定要选择这个,不选择的话,会导致输出不成功的。

3.3 不勾选 MicroLIB 库的方法

如果我们不勾选 Use MicroLib 也想正常打印输出调试信息的话,那就要我们多添加一些代码了。

因为 printf 函数使用了半主机模式,所以直接使用这个函数会导致程序无法运行的,我们需要提前告诉编译器不要使用半主机模式。

不勾选  Use MicroLib 库需要添加的完整代码如下:

  1. #if 1
  2. /* 告知连接器不从C库链接使用半主机的函数 */
  3. #pragma import(__use_no_semihosting)

  4. /* 标准库需要的支持数据类型 */
  5. struct __FILE
  6. {
  7.     int handle;
  8. };

  9. FILE __stdout;

  10. /**
  11. * [url=home.php?mod=space&uid=247401]@brief[/url] 定义_sys_exit()以避免使用半主机模式
  12. * @param void
  13. * [url=home.php?mod=space&uid=266161]@return[/url]  void
  14. */
  15. void _sys_exit(int x)
  16. {
  17.     x = x;
  18. }

  19. int fputc(int ch, FILE* f)
  20. {
  21.     if (ch == '\n')
  22.     {
  23.         while (USART_ReadStatusFlag(USART1, USART_FLAG_TXBE) == RESET);

  24.         USART_TxData(USART1, '\r');
  25.     }

  26.     /* wait for the data to be send */
  27.     while (USART_ReadStatusFlag(USART1, USART_FLAG_TXBE) == RESET);
  28.    
  29.     /* send a byte of data to the serial port */
  30.     USART_TxData(USART1, (uint8_t)ch);

  31.     return (ch);
  32. }
  33. #endif

这样添加了上述代码之后,我们就算不够选  Use MicroLib 库也可以正常打印输出日志信息了。

3.4 log模块打印测试效果

在 main 函数中,我们添加在 ubuntu 下测试的那几行打印测试代码,然后打开 MobaXterm 终端软件,可以看到如下打印效果:

image-20220830214255460.png

可以看到有不同颜色的打印效果,如果不想打印某些信息的级别,修改打印日志输出级别的宏定义就行。

注意:如果是使用常用的串口助手测试工具的话,可能不支持输出信息带颜色的,而且反而会看到一些不需要的颜色编码,这个时候我们可以把颜色输出的宏定义关闭就可以不用输出颜色了。






打赏榜单

21小跑堂 打赏了 50.00 元 2025-01-09
理由:恭喜通过原创审核!期待您更多的原创作品~

评论

灵活裁剪,实现一个自定义的log调试输出代码,有效提升log输出效率,代码调试信息更加直观简洁。  发表于 2025-1-9 16:38
菜鸟的第一步 发表于 2025-1-9 17:23 | 显示全部楼层
分级打印代码写的很规整,


dukedz 发表于 2025-1-10 08:43 | 显示全部楼层
跨平台支持 windows 彩色串口调试的开源上位机可以试试:
https://bbs.21ic.com/icview-3113306-1-1.html

且最大的好处是:可以走用户串口进行调试,不用拆产品外壳
 楼主| luobeihai 发表于 2025-1-10 22:53 | 显示全部楼层
dukedz 发表于 2025-1-10 08:43
跨平台支持 windows 彩色串口调试的开源上位机可以试试:
https://bbs.21ic.com/icview-3113306-1-1.html

哇 好用吗?我试一下,谢谢了!
 楼主| luobeihai 发表于 2025-1-17 12:35 | 显示全部楼层
菜鸟的第一步 发表于 2025-1-9 17:23
分级打印代码写的很规整,

还有个EasyLogger的功能更强大,如果需要可以使用这个
天体书记 发表于 2025-5-31 18:51 | 显示全部楼层
这么小的资源,实现这个丰富的日志打印。感觉好是浪费,有点华而不实
GalaxyStroll 发表于 2025-6-1 20:43 | 显示全部楼层
调试输出,关键是实时性和可重入的问题的吧!
您需要登录后才可以回帖 登录 | 注册

本版积分规则

23

主题

101

帖子

4

粉丝
快速回复 在线客服 返回列表 返回顶部