目录
1、简介
在嵌入式的开发过程中,难免会有 debug 的过程,STM32的生态比较健全,有一些很好用的调试工具,可以支持在线调试,在线调试的优势是显而易见的,但是在定位时序相关的问题时,它的不足之处也就显现出来了,通信时序一旦被中断,会导致通信异常,所以这时候日志是一个比较好分析问题的工具。
日志应用定位问题、跟踪数据、采集数据等多个方面,比如定位一些时序性较强的 bug、分析系统运行时的性能、对系统产生的数据进行保存分析等等,这些都是在线调试不能解决的。所以日志在一个系统的扮演一个很重要的角色,一般在开发不支持在线调试的单片机是,第一个要实现在功能就是日志了。
2、日志系统的设计
设计和实现大体上参考https://docs.oracle.com/cd/E23095_01/Platform.93/ 和 LVGL 日志的实现。日志系统具有环形队列缓冲区、日志等级过滤、日志输出颜色控制、将日志信息保存到文件的接口。
2.1 日志的结构
日志系统的结构如下图所示。系统主要分为两个部分,一部分在 Lib 层,另一部分在 Drv 部分。
Lib 层的主要功能是搜集工程中的 log,并对其做一个管理,最后做一个 Terminal Display 终端输出(使用的是 printf输出的)。Drv 层的部分用于将日志信息保存到本地,使用前需要注册一个写回调函数,Extral Memory 可以是单片机的 flash、e2prom等等掉电存储介质,也可以将其保存到 PC 中的某一个文件中。
Lib 层和 Drv 层相互独立,Register 注册动作发生在 Drv 层,所以只有 Lib 部分日志也是可以正常使用的。
2.2 日志系统的运行机制
日志的缓存结构 Queue Buffer,采用的是环形队列的方式进行一个缓冲。Queue Buffer 的结构如下图。初始化一个 Array 的空间,最小是一条日志 item 的最大长度(比如 item 最大100,那么 Array 的大小要大于100)。push 一个 item 指针 last 前移,pop 一个 item 指针 frist 前移,绿色为可用空间。item 的大小不固定。
日志的存储机制如下图。系统每产生一个 log,就会在缓冲队列的队尾 push 一个 item,当数组剩余的空间无法存放当前 item 的时候,就会从队列的对头 pop 一个 item,直到剩余的空间可以存放当前的 item。
对 pop 出来的 item 的处理如下图。弹出的 item 会从终端上打印出来,如果还注册了写日志函数,则可以保存到相应的存储介质中。
日志的 item 的结构如下。注意成员 Buf 是一个大小为零的数组,其使用方法见(参考资料:有效长度为零的数组)。
2.3 日志系统的代码实现
具体的实现过程:https://github.com/ShallowGreen123/lvgl_mydemo/lib
可以通过默认日志过滤等级来屏蔽不想看到的日志,如下图。如默认过滤等级是 LV_ERR 那么从日志中打印出来的只有系统运行时出错的信息,其它非错误信息会被屏蔽。
如下图。左边的过滤等级是 Debug 级别,右边的过滤等级是 error 级别。
开启使能输出颜色使能宏,就可以打印不同颜色的日志了,就像上面看到的。
上面介绍的是 lib 层的日志使用,而 Drv 层的需要注册写回调函数,这个部分可以自己试试,上面分享的 lib 源码中已经提供了相应的接口。如下图,上面是注册写函数,下面是注销写函数。
日志系统的设计与现实的方法就分享完了。
参考资料
关于代码规范:关于C代码规范,如何制定自己的编码规范_ShallowGreen的博客-CSDN博客
有效长度为零的数组:Variable Length (Using the GNU Compiler Collection (GCC))
LVGL官方源码:lvgl/lv_log.h at master · lvgl/lvgl · GitHub
日志实现参考:https://docs.oracle.com/cd/E23095_01/Platform.93/