怎么在单片机裸机程序中移植EasyLogger?
1、介绍
EasyLogger 是一款超轻量级、高性能的C日志库,非常适合对资源敏感的软件项目。例如:IoT产品、可穿戴设备、智能家居等等。相比log4c、zlog这些知名的C日志库,EasyLogger的功能更加简单,提供给用户的接口更少,但上手会很快,更多实用功能支持以插件形式进行动态扩展。
目前EasyLogger支持以下功能:
-
日志输出方式支持串口、Flash、文件等;
-
日志内容可包含级别、时间戳、线程信息、进程信息等;
-
支持多种操作系统,支持裸机;
-
各级别日志支持不同颜色显示;
EasyLogger的GitHub代码地址:
GitHub - armink/EasyLogger: An ultra-lightweight(ROM<1.6K, RAM<0.3k), high-performance C/C++ log library. | 一款超轻量级(ROM<1.6K, RAM<0.3k)、高性能的 C/C++ 日志库https://github.com/armink/EasyLogger
2、移植EasyLogger过程
2.1整体的移植思路
(1)添加源码到裸机工程中;
(2)实现需要的接口;
2.2 具体添加源码到工程的操作
在移植的时候,可以参考从上面的链接中下载的ZIP文件解压出来的EasyLogger-master项目的readme文档和demo工程。
通过这些可以对EasyLogger有一个大致的了解。docs中的API和port中的kernel.md有对于EasyLogger 核心功能移植说明和EasyLogger 核心功能 API 说明。重点看一下这部分!!!
然后准备一份裸机工程文件,将printf重定向到串口打印,准备好之后开始移植easylogger。
首先是将下载好的开源文件中的easylogger复制到裸机工程中。(本人使用的是STM32F407芯片的板子,仅有LED和串口两部分的代码)
将这个部分的源码,复制到准备好的裸机工程的third_lib文件夹下。然后打开keil工程,进行添加easylogger组件的源码文件。
-
port/elog_port.c
:elog移植接口文件; -
src/elog.c
: elog核心功能源码; -
src/elog_utils.c
:elog所用到的一些c库工具函数实现; -
src/elog_buf.c
(可选添加):elog缓冲输出模式源码; -
src/elog_async.c
(可选添加):elog异步输出模式源码;
在像上图中添加了以后,再将
easylogger/inc的
头文件路径添加到keil中。如下图:
对于串口的重定向部分的代码如下:(后续的输出日志信息要使用printf函数,所以需要进行添加这个串口重定向代码!)
int fputc(int ch, FILE *stream)
{/* 堵塞判断串口是否发送完成 */while((USART1->ISR & 0X40) == 0);/* 串口发送完成,将该字符发送 */USART1->TDR = (uint8_t) ch;return ch;
}
我将这部分代码放到了elog_port.c文件中,如下图:
2.3 实现elog移植接口
elog的移植接口都已经写好了,在elog_port.c
文件中,只需要在函数体中添加代码即可。
① elog初始化接口
ElogErrCode elog_port_init(void);
如果涉及到后续elog使用资源的初始化,比如动态申请分配缓冲区内存,可以放在此接口中,本文中保持默认。这个暂时不需要调用,因为该接口会在调用elog_init函数中被调用。
② elog日志输出接口(重点)
//开头添加
#include <stdio.h>
....void elog_port_output(const char *log, size_t size) {/* add your code here *///日志使用printf输出,printf已经重定向到串口USART1printf("%.*s", size, log); //%s表示字符串输出,.<十进制数>是精度控制格式符,输出字符时表示输出字符的位数
}
③ 日志输出上锁/解锁接口
该接口可以对日志输出接口进行上锁/解锁,以保证日志在并发输出时的正确性,本文中使用的是裸机程序,所以在此使用关闭全局中断来加锁,打开全局中断来解锁(STM32开关全局中断的方式很多,这里是直接操作 PRIMASK 寄存器来快速的屏蔽/打开全局中断):
//开头添加
#include "stm32f4xx.h"void elog_port_output_lock(void) {/* add your code here *///关闭全局中断__set_PRIMASK(1); //使用的是裸机程序,所以在此使用关闭全局中断来加锁,打开全局中断来解锁:
}/*** output unlock*/
void elog_port_output_unlock(void) {/* add your code here *///开启全局中断__set_PRIMASK(0);
}
④ 系统信息获取接口
elog提供了三个接口用来获取当前时间、获取进程号、获取线程号,因为本文中移植到裸机工程中,并且没有提供时间支持,所以这三个接口都返回空字符串。
/*** get current time interface** @return current time*/
const char *elog_port_get_time(void) { //elog提供了三个接口用来获取当前时间、获取进程号、获取线程号,因为本文中移植到裸机工程中,并且没有提供时间支持,所以这三个接口都返回空字符串/* add your code here */return "";}/*** get current process name interface** @return current process name*/
const char *elog_port_get_p_info(void) {/* add your code here */return "";
}/*** get current thread name interface** @return current thread name*/
const char *elog_port_get_t_info(void) {/* add your code here */return "";}
2.4 配置elog
elog的核心功能开启宏定义和核心参数宏定义都在配置文件elog_cfg.h
中,下面讲述其中重要的宏定义。 这三个地方要特别的注意,第一个是日志输出的总开关,该宏必须要在这里进行定义。第二个是换行符宏定义,修改成\r\n。第三个是带有颜色的日志输出开关,在这里进行定义。
移植时并没有添加异步输出和缓冲区输出的源码,所以将这两个功能关掉:到此为止,就算移植并且配置完成了,下面就可以进行使用了。
3、使用EasyLogger
3.1. 初始化elog
elog使用之前需要初始化,过程有三步:
① 初始化elog
/* 初始化elog */
void elog_init(void);
② 设置日志输出格式
void elog_set_fmt(uint8_t level, size_t set);
其中第一个参数表示设置哪个日志输出级别对应的输出格式,其二个参数是日志输出格式,枚举给出,可以自由组合搭配。
③ 启动elog
void elog_start(void);
接下来在main函数中的串口初始化函数之后,while(1)之前编写elog初始化代码:
/* 初始化elog */elog_init();elog_set_text_color_enabled(true); //要想五彩缤纷的日志,仅在elog_cfg.h中使能颜色输出还不够,还需要使用API开启输出/* 设置每个级别的日志输出格式 *///输出所有内容elog_set_fmt(ELOG_LVL_ASSERT, ELOG_FMT_ALL);//输出日志级别信息和日志TAGelog_set_fmt(ELOG_LVL_ERROR, ELOG_FMT_LVL | ELOG_FMT_TAG);elog_set_fmt(ELOG_LVL_WARN, ELOG_FMT_LVL | ELOG_FMT_TAG);elog_set_fmt(ELOG_LVL_INFO, ELOG_FMT_LVL | ELOG_FMT_TAG);//除了时间、进程信息、线程信息之外,其余全部输出elog_set_fmt(ELOG_LVL_DEBUG, ELOG_FMT_ALL & ~(ELOG_FMT_TIME | ELOG_FMT_P_INFO | ELOG_FMT_T_INFO));//输出所有内容elog_set_fmt(ELOG_LVL_VERBOSE, ELOG_FMT_ALL);/* 启动elog */elog_start();
3.2. elog日志输出
elog中每种级别都有一种完整方式,两种简化方式,使用时自行选择:
#define elog_assert(tag, ...)
#define elog_a(tag, ...) //简化方式1,每次需填写 LOG_TAG
#define log_a(...) //简化方式2,LOG_TAG 在文件顶部定义,使用前无需填写 LOG_TAG
比如上面这个elog_assert函数就有两种表达方法,我选择的是简化方式2。前两种在使用的时候只需要包含<elog.h>
头文件即可,第三种方式除了包含头文件之外,还需要在文件开始定义TAG宏定义,使用起来和printf相同。
首先在main.c文件开始定义TAG宏,包含头文件:
#define LOG_TAG "main" //使用简化方式2,LOG_TAG 在文件顶部定义,使用前无需填写 LOG_TAG(必须是在下面包含的头文件前进行定义,因为程序顺序执行)
#include "elog.h"
然后在main函数中编写的elog初始化代码之后,继续添加代码,测试elog的使用:
log_a("Hello EasyLogger!");log_e("Hello EasyLogger!");log_w("Hello EasyLogger!");log_i("Hello EasyLogger!");log_d("Hello EasyLogger!");log_v("Hello EasyLogger!");
然后再进行编译,烧写,使用串口助手查看串口输出:
另外串口助手无法显示颜色,如果想要输出有颜色的日志,请使用使用串口终端(Mobaxterm)查看串口输出!!
相关文章:

怎么在单片机裸机程序中移植EasyLogger?
1、介绍 EasyLogger 是一款超轻量级、高性能的C日志库,非常适合对资源敏感的软件项目。例如:IoT产品、可穿戴设备、智能家居等等。相比log4c、zlog这些知名的C日志库,EasyLogger的功能更加简单,提供给用户的接口更少,但…...
C/C++解析文件名和目录路径
文章目录 主要函数使用注意事项示例程序总结 #include <libgen.h> 是一个 C/C 语言的头文件,主要用于字符串处理,特别是在处理文件路径时。它提供了一些函数来帮助你解析文件名和目录路径。 主要函数 以下是 libgen.h 中一些常见的函数ÿ…...
Git 基本命令行操作
Git是一个开源的分布式版本控制系统,用于管理源代码和文档的版本。以下是Git的基本命令行操作: 一、配置 安装完成后,需要配置Git的用户名和邮箱,以便在提交记录时记录操作者的信息。 配置全局用户名:git config --g…...
【Rust练习】17.泛型
练习题来自:https://practice-zh.course.rs/generics-traits/generics.html 函数 1 // 填空 struct A; // 具体的类型 A. struct S(A); // 具体的类型 S. struct SGen<T>(T); // 泛型 SGen.fn reg_fn(_s: S) {}fn gen_spec_t(_s: SGen<A&…...

java脚手架系列4--测试用例、拦截器
异常处理、拦截器、数据库连接 1 测试用例 单元测试是一个老生常谈的问题,无论是后端对自己的代码质量把的第一道关也好,也是对测试减缓压力。这里就不过多讲述测试用例的重要性,但是有2个框架我们必须了解一下。 1.1 JUnit和mockito 我们…...
论文推荐 |【Agent】自动化Agent设计系统
论文标题: Automated Design of Agentic Systems 论文地址: https://arxiv.org/abs/2408.08435 GitHub地址: https://github.com/ShengranHu/ADAS 自动化代理设计在性能和通用性方面显著超越了手动方法。 • 引入了自动化代理系统设计&am…...
Linux操作系统提供了五种主要的IO(输入/输出)模型
Linux操作系统提供了五种主要的IO(输入/输出)模型,这些模型旨在优化应用程序对输入输出操作的管理和处理。以下是关于这五种IO模型的详细介绍。 一、阻塞IO(Blocking IO) 阻塞IO是最常见、最传统的IO模型。在这种模型…...

基于深度学习的花卉识别系统
简介: 基于Python的花卉识别分类系统利用深度学习和计算机视觉技术,能够准确识别和分类各种花卉,如玫瑰、郁金香和向日葵等。这种系统不仅有助于植物学研究和园艺管理,还在生态保护、智能农业和市场销售等领域展现广泛应用前景。随…...

【斯坦福CS144】Lab0
一、实验目的 1.初步了解计算机网络,准备实验所需的材料和环境; 2.掌握基础实验方法; 3.动手实现网络功能。 二、实验内容 1.下载实验所需的资料,安装虚拟机,配置环境; 2.获取一个网页; …...

关于Mybatis中,IPage<PO>转换成IPage<VO>的问题
以下是一个比较常见通用的一个查询并且为单表查询,在开发初期,或者项目不是很复杂的时候,或者一开始项目框架就规划好的情况下,通常我们都会封装。 在我们的项目中,这部分代码其实是自动生成的,足以满足大…...

使用idea和vecode创建vue项目并启动(超详细)
一、idea创建vue项目 创建项目之前先下载好插件 新建项目找到vue生成器 写好名称,找到自己需要存放的地址,node解释器安装方式可以看我上一个博客,vueCLI是选择vue的版本,我们可以使用idea自带的vue版本默认是vue3,创…...

C#|.net core 基础 - 删除字符串最后一个字符的七大类N种实现方式
今天想通过和大家分享如何删除字符串最后一个字符的N种实现方法,来回顾一些基础知识点。 01第一类、字符串方式 这类方法是通过string类型自身方法直接实现。 1、Substring方法 相信大多数人第一个想到的可能就是这个方法。Substring方法是字符串内置方法&#…...

成都睿明智科技有限公司怎么样靠谱吗?
随着短视频与直播的深度融合,抖音电商凭借其强大的流量入口、精准的算法推荐以及便捷的购物体验,迅速崛起。对于传统企业和新兴品牌而言,这无疑是一个不可多得的机遇。然而,如何在这片红海中脱颖而出,就需要借助专业的…...

docker简述
1.安装dockers,配置docker软件仓库 安装,可能需要开代理,这里我提前使用了下好的包安装 启动docker systemctl enable --now docker查看是否安装成功 2.简单命令 拉取镜像,也可以提前下载使用以下命令上传 docker load -i imag…...
第27周:Transformer实战:文本分类
目录 前言 一、前期准备 1.1 环境安装 1.2 加载数据 二、数据预处理 2.1 构建词典 2.2 生成数据批次和迭代器 2.3 构建数据集 三、模型构建 3.1 定义位置编码器 3.2 定义Transformer模型 3.3 初始化模型 3.4 定义训练函数 3.5 定义评估函数 四、训练模型 4.1 模…...

在QT中将Widget提升为自定义的Widget后,无法设置Widget的背景颜色问题解决方法
一、问题 在Qt中将QWidget组件提升为自定义的QWidget后,Widget设置的样式失效,例如设置背景颜色为白色失效。 二、解决方法 将已经提升的QWidget实例对象,脱离父窗体的样式,然后再重新设置自己的样式。...

【学习笔记】手写一个简单的 Spring IOC
目录 一、什么是 Spring IOC? 二、IOC 的作用 1. IOC 怎么知道要创建哪些对象呢? 2. 创建出来的对象放在哪儿? 3. 创建出来的对象如果有属性,如何给属性赋值? 三、实现步骤 1. 创建自定义注解 2. 创建 IOC 容器…...

日记学习小迪安全27
感觉复制粘贴没有意思,而且还有点浪费时间,主要是学习,不是复制,那就复制别人的吧 第27关就参考这篇文章吧,以下大部分内容都是参考以下文章(侵权删除) 第27天:WEB攻防-通用漏洞&a…...
【React】类组件和函数组件
构建组件的方式 函数式组件(function)createElement(不建议使用)类组件形式创建(不建议使用) 对于 React 的理解 React, 用于构建用户界面的JavaScript库,本身只提供了Ul层面的解决方案。&am…...
Spring Boot应用开发
Spring Boot是一个基于Spring框架的开源Java框架,旨在简化新Spring应用的初始化和开发过程。它通过提供各种默认配置,减少了繁琐的配置,使开发者能够专注于业务逻辑的实现。本文将介绍Spring Boot的基本概念、优点、关键特性以及如何构建一个…...

C++初阶-list的底层
目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...

从WWDC看苹果产品发展的规律
WWDC 是苹果公司一年一度面向全球开发者的盛会,其主题演讲展现了苹果在产品设计、技术路线、用户体验和生态系统构建上的核心理念与演进脉络。我们借助 ChatGPT Deep Research 工具,对过去十年 WWDC 主题演讲内容进行了系统化分析,形成了这份…...
【Linux】C语言执行shell指令
在C语言中执行Shell指令 在C语言中,有几种方法可以执行Shell指令: 1. 使用system()函数 这是最简单的方法,包含在stdlib.h头文件中: #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...

蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练
前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1):从基础到实战的深度解析-CSDN博客,但实际面试中,企业更关注候选人对复杂场景的应对能力(如多设备并发扫描、低功耗与高发现率的平衡)和前沿技术的…...
在Ubuntu中设置开机自动运行(sudo)指令的指南
在Ubuntu系统中,有时需要在系统启动时自动执行某些命令,特别是需要 sudo权限的指令。为了实现这一功能,可以使用多种方法,包括编写Systemd服务、配置 rc.local文件或使用 cron任务计划。本文将详细介绍这些方法,并提供…...

【单片机期末】单片机系统设计
主要内容:系统状态机,系统时基,系统需求分析,系统构建,系统状态流图 一、题目要求 二、绘制系统状态流图 题目:根据上述描述绘制系统状态流图,注明状态转移条件及方向。 三、利用定时器产生时…...

论文浅尝 | 基于判别指令微调生成式大语言模型的知识图谱补全方法(ISWC2024)
笔记整理:刘治强,浙江大学硕士生,研究方向为知识图谱表示学习,大语言模型 论文链接:http://arxiv.org/abs/2407.16127 发表会议:ISWC 2024 1. 动机 传统的知识图谱补全(KGC)模型通过…...

基于Docker Compose部署Java微服务项目
一. 创建根项目 根项目(父项目)主要用于依赖管理 一些需要注意的点: 打包方式需要为 pom<modules>里需要注册子模块不要引入maven的打包插件,否则打包时会出问题 <?xml version"1.0" encoding"UTF-8…...

k8s业务程序联调工具-KtConnect
概述 原理 工具作用是建立了一个从本地到集群的单向VPN,根据VPN原理,打通两个内网必然需要借助一个公共中继节点,ktconnect工具巧妙的利用k8s原生的portforward能力,简化了建立连接的过程,apiserver间接起到了中继节…...
06 Deep learning神经网络编程基础 激活函数 --吴恩达
深度学习激活函数详解 一、核心作用 引入非线性:使神经网络可学习复杂模式控制输出范围:如Sigmoid将输出限制在(0,1)梯度传递:影响反向传播的稳定性二、常见类型及数学表达 Sigmoid σ ( x ) = 1 1 +...