Stm32F103&Rt_Thread系列开发——03 日志管理
一、前言:
本系列教程教大家如何从0开始,在Stm32F1系列芯片上使用Rt_Thread实时操作系统进行程序开发,本教程选择的开发板为:正点原子Mini STM32F103RCT6开发板。
在进行程序开发之前,先要建立日志管理,这样在程序开发过程中,才能根据日志信息准确定位程序所出现的问题。本章将介绍RT_Thread 的日志管理系统。
二、普通打印
1、打印函数
学过stm32裸机的朋友,都知道裸机中是将C语言中的printf()函数进行重定向后,就可以在裸机代码中使用,在RT thread实时操作系统中,也提供了一个打印函数 rt_kprintf(); 该函数原型的路径在:/rt-thread/src/kservice.c中,其使用方法与printf()一致,并且官方推荐使用该函数,因为其效率高于printf();。唯一的缺点是,不支持浮点数打印。
使用案例:
//测试打印
const int number = 12;
const int hex = 0xa;
const char str[] = "this is test string!";
rt_kprintf("orange \n");
rt_kprintf("number: %d\n", number);
rt_kprintf("hex: %x\n", hex);
rt_kprintf("string: %s\n", str);
rt_kprintf("float: %f\n", numf);
//打印结果
orange
number: 12
hex: a
string: this is test string!
float: %f
2、浮点数打印
前一节提到,rt_kprintf(); 不支持浮点数打印,那么当需要打印浮点数时,如何操作?笔者搜集了三种处理方法。
测试代码如下:
#include <rtthread.h>
#include <rtdevice.h>
#include <board.h>
#include <stdio.h>
#define DBG_BUFF_MAX_LEN 256
/* debug print : support float double */
int dbg_printf(const char *fmt, ...)
{
va_list args;
static char rt_log_buf[DBG_BUFF_MAX_LEN] = { 0 };
va_start(args, fmt);
int length = vsnprintf(rt_log_buf, sizeof(rt_log_buf) - 1, fmt, args);
rt_kputs(rt_log_buf);
return length;
}
int main(void)
{
//测试浮点打印
const float pi = 3.14f;
//方法1
rt_kprintf("No1 pi = %d.%03d\n", (int)pi, (int)(pi*1000)%1000);
//方法2
dbg_printf("No2 pi = %f\n", pi);
//方法3
char tempchar[10];
sprintf(tempchar, "No3 pi = %f\n", pi);
rt_kprintf(tempchar);
while (1)
{
rt_thread_mdelay(500);
}
}
//测试结果
No1 pi = 3.140
No2 pi = 3.140000
No3 pi = 3.140000
方法1:应用了计算的方法,将浮点数的整数部分与小数部分单独打印。
方法2:运用vsnprintf()函数,重新封装了一个打印函数,进行打印。
方法3:使用:sprintf()函数,将格式化字符串,保存到一段char数组中,之后用rt_kprinf() 打印。
当然,还可以使用c库自带的prinf()函数,但是需要重定向串口。
3、番外,多格式打印
在做工程开发中,常用到有格式要求的数字输出,演示如下:
十进制输出使用%d,十六进制输出使用%x。而%2d,中的2表示输出宽度。%.2d与%02d类似,都表示宽度不足2时,向前方补0。%-2d表示后补空格。十六进制类似。
//输出
int a = 5;
rt_kprintf("%d\n", a);
rt_kprintf("%2d\n", a);
rt_kprintf("%.2d\n", a);
rt_kprintf("%-2d\n", a);
rt_kprintf("%02d\n", a);
//输出
5
5
05
5
05
//代码
int b = 0xf;
rt_kprintf("%x\n", b);
rt_kprintf("%2x\n", b);
rt_kprintf("%.2x\n", b);
rt_kprintf("%-2x\n", b);
rt_kprintf("%02x\n", b);
//输出
f
f
0f
f
0f
浮点数中,%.2f,表示,保留两位小数。
//代码
const float pi = 3.14159f;
dbg_printf("%f\n", pi);
dbg_printf("%.2f\n", pi);
dbg_printf("%.3f\n", pi);
// 输出
3.141590
3.14
3.142
三、日志打印
平时使用日志时,需要区分日志等级,日志等级一般分为四个,分别是:错误(ERROR),警告(WARNING),信息(INFO),调试(LOG)。
其中优先级为 错误 > 警告 > 信息 > 调试。我们可以在指定单个.c文件中日志的打印等级,指定打印等级后,高于该等级的日志会被打印,低于该等级的日志不会被打印。例如,指定打印等级为警告,则该文件中的错误与警告会被打印,而信息与调试不会被打印。
Rt thread 有自带的打印日志,也可以配置轻量级日志ulog,本文介绍自带打印日志信息。
1、日志打印
打印日志的头文件为 ,该文件在/rt-thread/include/rtdbg.h中。
使用时,需要添加段名与打印等级,并包含头文件,示例如下:
#include <rtthread.h>
#include <rtdevice.h>
#include <board.h>
#define DBG_SECTION_NAME "main"
#define DBG_COLOR
#define DBG_LEVEL DBG_LOG //调试
// #define DBG_LEVEL DBG_INFO //信息
// #define DBG_LEVEL DBG_WARNING //警告
// #define DBG_LEVEL DBG_ERROR //错误
#include <rtdbg.h>
int mian()
{
LOG_D("this is debug");
LOG_I("this is information");
LOG_W("this is working");
LOG_E("this is error");
}
输出示例:
值得注意的是:由于头文件的包含具有顺序性,故在文件中,先包含其余模块头文件,再定义DBG_SECTION_NAME与DBG_LEVEL宏,最后再包含头文件。
四、结语
日志打印就介绍到这里了,下期分享更多精彩内容。
最后欢迎关注我的公众号,会定期推送嵌入式相关教程。