文章目录
-
- 前言
- [1. WS63 平台与典型软件栈](#1. WS63 平台与典型软件栈)
- [2. EasyLogger 简介](#2. EasyLogger 简介)
-
- [2.1 项目背景与许可](#2.1 项目背景与许可)
- [2.2 架构分层(理解移植要改哪里)](#2.2 架构分层(理解移植要改哪里))
- [3. 本仓库目录与编译单元](#3. 本仓库目录与编译单元)
- [4. WS63 移植层实现要点(`elog_port.c`)](#4. WS63 移植层实现要点(
elog_port.c)) -
- [4.1 为何使用 CMSIS-RTOS2](#4.1 为何使用 CMSIS-RTOS2)
- [4.2 `printf` 与 UART](#4.2
printf与 UART) - [4.3 时间戳与内核 Tick](#4.3 时间戳与内核 Tick)
- [5. 配置说明(`elog_cfg.h`)](#5. 配置说明(
elog_cfg.h)) -
- [5.1 输出总开关与静态级别](#5.1 输出总开关与静态级别)
- [5.2 缓冲区](#5.2 缓冲区)
- [5.3 缓冲模式(`elog_buf`)](#5.3 缓冲模式(
elog_buf)) - [5.4 颜色与格式](#5.4 颜色与格式)
- [5.5 不要在本平台打开的宏](#5.5 不要在本平台打开的宏)
- [6. 编译系统集成](#6. 编译系统集成)
-
- [6.1 CMake(本仓库已集成示例)](#6.1 CMake(本仓库已集成示例))
- [6.2 GN(OpenHarmony 风格)](#6.2 GN(OpenHarmony 风格))
- [7. 初始化与运行期 API](#7. 初始化与运行期 API)
-
- [7.1 推荐启动顺序](#7.1 推荐启动顺序)
- [7.2 常用输出方式](#7.2 常用输出方式)
- [7.3 过滤与格式(运行期)](#7.3 过滤与格式(运行期))
- [8. RAM、线程与实时性建议](#8. RAM、线程与实时性建议)
- [9. 可选插件:Flash / 文件后端](#9. 可选插件:Flash / 文件后端)
- [10. 常见问题排查](#10. 常见问题排查)
- [11. 参考与延伸](#11. 参考与延伸)
- 总结
前言
在 海思 WS63 一类 Wi-Fi / 星闪 SoC 上跑应用时,调试与现场排障高度依赖 串口日志 :既要能在多线程环境下 安全打印 ,又希望 尽量少占 RAM 、不把 UART 写操作拖慢业务线程。EasyLogger 是嵌入式领域常用的轻量级日志库,通过 可裁剪的配置 与 异步输出 能较好平衡这些需求。
本文面向在本仓库(xiaohong + WS63 SDK)或同类 OpenHarmony / LiteOS + CMSIS-RTOS2 工程中的集成者,说明:
- EasyLogger 是什么、适合什么场景;
- 如何在本工程形态下完成移植与编译集成;
elog_cfg.h与elog_port.c在 WS63 上的注意点;- 初始化顺序、常用 API、RAM/线程/UART 与常见问题。
文中路径以本仓库为准:src/easylogger/。若你使用其他目录,请自行替换。
1. WS63 平台与典型软件栈
WS63 为海思面向 IoT 连接场景的芯片平台,常见配套包括:
- 实时内核:LiteOS-M / 兼容层;
- RTOS API :多通过 CMSIS-RTOS2 (
cmsis_os2.h)封装信号量、线程等; - 日志出口 :调试阶段多为 UART +
printf重定向,或 SDK 提供的打印接口。
本仓库的 BUILD.gn 通过 ws63_sdk.gni 引入 SDK 头文件与驱动;板级示例为 xiaohong_ws63_v1(宏 BOARD_XH_WS63_V1)。EasyLogger 的移植层必须与当前工程实际使用的 RTOS API 一致 ------本工程采用的是 CMSIS-RTOS2 ,而不是 POSIX pthread。
2. EasyLogger 简介
2.1 项目背景与许可
EasyLogger (简称 elog )由 Armink 维护,源码采用 MIT 许可,仓库与文档可在 GitHub / Gitee 检索 EasyLogger 获取原版说明。其核心目标是在 资源受限 MCU 上提供:
- 分级日志(Assert / Error / Warn / Info / Debug / Verbose);
- 按标签(tag)、关键字、级别过滤;
- 可选颜色(ANSI 转义序列,终端支持时便于区分级别);
- 可选异步输出(业务线程只写缓冲,专用线程或回调做 UART 输出);
- 可选缓冲输出(合并多次写入,减少碎片输出)。
本仓库内置版本在 elog.h 中标记为 ELOG_SW_VERSION "2.2.99"(与上游版本号可能随合并略有差异,以头文件为准)。
github地址 :https://github.com/armink/EasyLogger

2.2 架构分层(理解移植要改哪里)
| 层次 | 作用 | WS63 上谁实现 |
|---|---|---|
核心 elog.c |
格式化、过滤、调用输出链路 | 一般不改 |
工具 elog_utils.c |
字符串与内存辅助 | 一般不改 |
异步 elog_async.c |
环形缓冲、异步策略 | 一般不改(通过宏选 pthread 或自定义 notice) |
缓冲 elog_buf.c |
缓冲刷新策略 | 一般不改 |
移植层 elog_port.c |
锁、输出、时间、线程名、异步线程 | 必须按平台实现 |
所有与板级相关的行为都集中在 elog_port.c (以及可选插件的 *_port.c)。
3. 本仓库目录与编译单元
建议保持如下布局(与当前工程一致):
text
src/easylogger/
├── inc/
│ ├── elog.h # 对外 API 与 log_x 宏
│ └── elog_cfg.h # 功能开关与缓冲区尺寸(移植时最常改)
├── port/
│ └── elog_port.c # WS63 / CMSIS-RTOS2 移植实现
├── src/
│ ├── elog.c
│ ├── elog_utils.c
│ ├── elog_async.c
│ └── elog_buf.c
└── plugins/ # 可选:Flash / 文件后端(默认未编入本工程 CMake)
├── flash/
└── file/
建议编入的 C 文件 (与 CMakeLists.txt 中一致):
elog_port.celog.celog_utils.celog_buf.celog_async.c
头文件搜索路径需包含:src/easylogger/inc。
说明 :当前仓库的
BUILD.gn静态库xiaohong未列出上述 EasyLogger 源文件 ;若你仅在 GN 构建 下编译,需要在sources中自行追加同等列表,并保证能包含cmsis_os2.h与stdio.h(printf)。
4. WS63 移植层实现要点(elog_port.c)
本工程 elog_port.c 已实现下列 移植接口(函数名由 EasyLogger 约定,不可随意改名):
| 接口 | 职责 | 本工程实现摘要 |
|---|---|---|
elog_port_init |
初始化锁、异步所需信号量与线程 | osSemaphoreNew;异步模式下 osThreadNew(async_output, ...) |
elog_port_deinit |
释放信号量 | 释放 output_lock / output_notice;未终止异步线程(若需完整 deinit 可自行扩展) |
elog_port_output |
将已格式化的日志写出 | printf("%.*s", (int)size, log) |
elog_port_output_lock / unlock |
多线程互斥 | 二值信号量 osSemaphoreAcquire / Release |
elog_port_get_time |
时间戳字符串 | osKernelGetTickCount() 换算为 秒.毫秒 形式 |
elog_port_get_p_info |
进程信息 | 固定返回 "app" |
elog_port_get_t_info |
线程信息 | osThreadGetName 或回退 t<id> |
elog_async_output_notice |
通知异步输出线程取日志 | osSemaphoreRelease(output_notice)(仅 ELOG_ASYNC_OUTPUT_ENABLE 时) |
4.1 为何使用 CMSIS-RTOS2
WS63 SDK 工程通常已链接 CMSIS-RTOS2 适配层,与 LiteOS 线程模型一致。EasyLogger 官方示例里异步模式常见 POSIX pthread (ELOG_ASYNC_OUTPUT_USING_PTHREAD);在 WS63 上 不要打开该宏,而应像本工程一样:
- 在
elog_port.c内实现elog_async_output_notice; - 使用
osThreadNew跑async_output循环,从elog_async_get_line_log/elog_async_get_log取数据再printf。
这样避免引入 pthread、semaphore.h 等与当前 SDK 不完全一致的依赖。
4.2 printf 与 UART
elog_port_output 使用 printf 。请确保在 WS63 工程中 printf 已重定向到所用调试 UART (例如通过 SDK 的 console 或 uart 适配)。若 printf 未初始化或阻塞时间过长,会表现为 日志卡顿或早期启动无输出。
4.3 时间戳与内核 Tick
elog_port_get_time 中当前实现假设 Tick 周期为 1 ms (ticks/1000 为秒)。若你的 osKernelGetTickFreq() 与 1000 Hz 不一致,应改为按 实际 Tick 频率 换算,否则时间戳会偏差。
5. 配置说明(elog_cfg.h)
以下为 本仓库当前配置 的语义说明(具体数值以文件为准,移植时可按 RAM 预算调整)。
5.1 输出总开关与静态级别
ELOG_OUTPUT_ENABLE:关闭后所有elog_*/log_*宏为空操作。ELOG_OUTPUT_LVL:编译期静态裁剪,高于该级别的宏不生成代码(例如设为ELOG_LVL_INFO则 Debug/Verbose 宏为空)。
5.2 缓冲区
ELOG_LINE_BUF_SIZE:单行上限(含前缀);本工程可设为 128 省 RAM,超长截断。开目录/颜色时前缀易占满,可关ELOG_COLOR_ENABLE、ELOG_FMT_USING_DIR或加大本值。ELOG_ASYNC_OUTPUT_ENABLE:启用异步环形缓冲与后台输出。ELOG_ASYNC_OUTPUT_BUF_SIZE:异步环缓大小;本工程由ELOG_ASYNC_RING_LINE_SLOTS(默认 3 )×ELOG_LINE_BUF_SIZE得出,RAM 极紧时槽位过小会在日志突发时更易丢尾。ELOG_ASYNC_LINE_OUTPUT:按行从环缓取出,便于一行一行printf。ELOG_ASYNC_OUTPUT_LVL:与elog_async.c中level >= OUTPUT_LVL走异步 的逻辑配合使用(级别数值越小表示越"严重":Assert 为 0,Verbose 为 5)。设为ELOG_LVL_ASSERT时,所有级别 均满足>= 0,一般会 全部走异步 ;若希望低级别同步、高级别异步,可改为例如ELOG_LVL_INFO(需对照业务理解同步/异步划分)。
5.3 缓冲模式(elog_buf)
ELOG_BUF_OUTPUT_ENABLE与ELOG_BUF_OUTPUT_BUF_SIZE:在异步之外再叠一层缓冲,减少零碎写;RAM 紧张时可评估是否关闭。
5.4 颜色与格式
ELOG_COLOR_ENABLE:ANSI 颜色;部分串口终端支持,纯 UART 工具可能显示转义字符,可按需关闭。ELOG_FMT_USING_DIR/FUNC/LINE:控制是否输出文件名、函数名、行号(会增大日志体积与格式化开销)。
5.5 不要在本平台打开的宏
ELOG_ASYNC_OUTPUT_USING_PTHREAD:应保持注释;WS63 本工程使用 CMSIS 线程 +elog_async_output_notice方案。
6. 编译系统集成
6.1 CMake(本仓库已集成示例)
在父级 CMakeLists.txt 中已包含类似片段(路径按仓库实际为准):
PRIVATE_SOURCES增加:elog_port.c、elog.c、elog_utils.c、elog_buf.c、elog_async.c;include增加:src/easylogger/inc。
6.2 GN(OpenHarmony 风格)
若目标产物通过 BUILD.gn 构建:
- 在
static_library("xiaohong")(或你的模块名)的sources中 加入与 CMake 相同的 5 个.c文件; - 在
include_dirs中加入src/easylogger/inc(或//vendor/.../xiaohong/src/easylogger/inc等 GN 绝对路径写法); - 确认
cmsis_os2.h所在路径已被该模块引用(本仓库BUILD.gn已包含kernel/osal等目录时可与现有模块对齐)。
7. 初始化与运行期 API
7.1 推荐启动顺序
在 内核与线程环境已就绪 、UART/printf 可用 之后调用:
c
#include "elog.h"
void app_log_init(void)
{
if (elog_init() != ELOG_NO_ERR) {
/* 处理错误:信号量/线程创建失败等 */
return;
}
elog_start(); /* 打开输出,启用 async/buf 等 */
}
elog_init():内部调用elog_port_init()、elog_async_init(),并设置默认过滤与格式。elog_start():使能输出及异步/缓冲模块(与elog_cfg.h宏相关)。
结束或重启前可调用 elog_stop() 、elog_deinit()(注意异步线程与资源释放策略,见下文「常见问题」)。
7.2 常用输出方式
方式 A:带 tag 的宏(推荐)
c
#define LOG_TAG "wifi"
#define LOG_LVL ELOG_LVL_INFO
#include "elog.h"
void foo(void)
{
log_i("connected, rssi=%d", rssi);
log_e("assoc failed, code=%u", code);
}
方式 B:显式 tag
c
elog_i("net", "ip=" IPSTR, IP2STR(&ip));
原始输出(不经完整格式前缀时可用)
c
elog_raw_output("boot stage %d\r\n", stage);
十六进制 dump
c
elog_hexdump("buf", 16, data, len);
7.3 过滤与格式(运行期)
c
elog_set_filter_lvl(ELOG_LVL_WARN); /* 全局级别 */
elog_set_filter_tag("noisy_mod"); /* 只输出某 tag */
elog_set_filter_tag_lvl("spi", ELOG_LVL_INFO); /* 按 tag 设级别 */
elog_set_fmt(ELOG_LVL_INFO, ELOG_FMT_LVL | ELOG_FMT_TAG | ELOG_FMT_TIME);
8. RAM、线程与实时性建议
| 项目 | 说明 |
|---|---|
| 异步线程栈 | ELOG_PORT_ASYNC_STACK_SIZE (默认 768 );poll_get_buf 为 static。栈不够再加大。 |
| 环缓大小 | ELOG_ASYNC_OUTPUT_BUF_SIZE 过小易丢日志或阻塞格式化路径,过大占 RAM;WS63 小 RAM 场景建议实测峰值打印速率。 |
| 锁与优先级 | 输出互斥使用信号量;异步线程优先级为 osPriorityBelowNormal,避免抢占关键实时任务,但若日志洪峰仍可能占用 CPU,需配合 ELOG_OUTPUT_LVL 与过滤。 |
| 启动早期 | 在 osKernel 未就绪时调用 osKernelGetTickCount / 创建线程可能失败;应将 elog_init 放在 RTOS 已启动之后 的应用入口。 |
9. 可选插件:Flash / 文件后端
仓库 plugins/flash、plugins/file 提供 将日志写入 Flash 或文件系统 的扩展,需额外:
- 编译对应
elog_flash.c/elog_file.c及各自*_port.c; - 在
elog_flash_cfg.h/elog_file_cfg.h中配置分区或路径; - 在业务中调用插件初始化与 flush 策略。
默认 CMake 列出的 5 个文件不包含插件 ;若 WS63 上已使用 LittleFS,可在此基础上评估 文件日志 是否与现有 FS 线程安全策略一致。
10. 常见问题排查
-
编译找不到
cmsis_os2.h检查 GN/CMake 的
include_dirs是否包含 OSAL / CMSIS-RTOS2 头路径。 -
无串口输出
确认
printf重定向、UART 初始化顺序早于elog_start,以及线波特率与 PC 终端一致。 -
异步模式无输出
确认
elog_port_init中osThreadNew成功 ,且elog_async_output_notice在elog_async.c中被调用(ELOG_ASYNC_OUTPUT_ENABLE打开)。 -
颜色乱码
关闭
ELOG_COLOR_ENABLE,或换支持 ANSI 的终端。 -
时间戳不准
按
osKernelGetTickFreq()修正elog_port_get_time的换算。 -
elog_port_deinit与异步线程已在
elog_port_deinit中osThreadTerminate(async_thread_id)后再删除信号量;elog_deinit时请先elog_stop,避免业务线程与析构竞态。
11. 参考与延伸
- 上游 EasyLogger 官方文档与示例(检索
armink EasyLogger)。 - 本仓库 RTOS 相关 :
BUILD.gn中ws63_sdk_path、kernel/osal等 include,便于对齐与你 SDK 版本一致的头文件。 - 同仓库内 另一套日志方案 :
src/ulog/及《ulog 日志库移植到海思 WS63 平台》类文档,可按项目规范 二选一或分层使用(避免混用两套宏导致风格分裂)。
总结
EasyLogger 在 WS63 上的移植核心是:用 CMSIS-RTOS2 实现 elog_port.c 的锁、时间与异步通知线程 ,保证与 LiteOS 线程模型一致;通过 elog_cfg.h 在 RAM 与实时性之间做裁剪 ;依赖已就绪的 printf/UART 作为最终输出 。本仓库已提供 可直接参考的 elog_port.c 与 CMake 集成列表 ;若使用 GN 构建,只需将相同源文件与头路径补进模块即可。
按本文完成集成后,建议在 多线程压力场景 下做一次 日志洪峰测试 ,根据是否丢日志、栈是否溢出,微调 ELOG_ASYNC_OUTPUT_BUF_SIZE、异步栈大小与输出级别 ,使 WS63 小内存环境下的日志既 可用 又 可控。