当前位置: 首页 > news >正文

RTT操作系统(3)

RTT操作系统(3)

本笔记为作者再学习RTT操作系统的一些心得体会,如有不对的地方,请包含与谅解!

​ ————by wsoz

本章开始我们就深入到对I/O口等外设的使用,下面进入我们的学习

I/O口设备

I/O设备框架是RT-Thread应用开发的核心基础,掌握了它就能轻松操作各种硬件外设。学习I/O框架不是为了重复造轮子,而是为了更好地使用现有的轮子,并在需要时能够造出自己的轮子

下图为I/O设备框架,它位于硬件和应用程序之间,共分成三层,从上到下分别是 I/O 设备管理层设备驱动框架层设备驱动层

在这里插入图片描述

在这里插入图片描述

理解:学习到现在我们目前也需要有一个大致的实时操作系统的编程思维和框架在RTT里对于框架的开发和裸机开发最大的区别就是在这个的基础上加了一个实时操作系统框架,但是原本裸机的代码依旧需要使用就是直接使用到驱动层里,然后通过I/O设备管理直接在应用层调用相同的接口,实现高内聚、低耦合,对于不同的芯片我们只需要设备驱动层去修改我们的硬件代码即可。

分层介绍

层次主要职责关键接口/操作对上/对下接口
I/O设备管理层对应用层提供统一的操作接口,管理设备驱动程序,屏蔽底层细节rt_device_find(), rt_device_open(), rt_device_read(), rt_device_write(), rt_device_control()向上对应用层提供标准API
设备驱动框架层抽象同类硬件设备的共同特性,定义标准操作接口,提供通用功能各类设备驱动框架的注册函数(如 rt_hw_watchdog_register())、标准操作集(如 struct rt_can_ops向下为驱动层提供框架,向上对接I/O设备管理层
设备驱动层直接操作硬件,实现设备驱动框架层定义的具体操作,提供硬件驱动能力实现设备操作方法(如 init, open, close, read, write, control向上对接设备驱动框架层,向下直接操作硬件

I/O设备管理层

I/O设备管理层是整个设备框架的最上层,直接面向应用程序。它的核心目标是为上层应用提供一套统一、标准的设备操作接口(如 rt_device_openrt_device_read 等)。

这意味着应用程序开发者无需关心底层具体是哪个型号的硬件,只需通过这套统一的API就能访问设备。这层还负责管理所有注册的设备,比如设备的查找、初始化计数(ref_count)等。

设备驱动框架层

备驱动框架层位于中间,它的主要工作是对同类硬件设备驱动进行抽象。这一层就将这些共同的特征提取出来,定义成统一的操作接口。

设备驱动层

设备驱动层是真正与硬件打交道的部分,由驱动开发者实现。它包含了一组直接驱使硬件设备工作的程序,负责初始化硬件、读写数据、处理中断等具体操作。

使用的是原本芯片sdk来实现对于特定设备的驱动,包括片上外设与外接的传感器通常利用的就是裸机中的代码,只需要将原本的接口流出来封装到驱动函数中。

下图为使用I/O设备框架的一个简单关系:

在这里插入图片描述

上图就是没有通过设备驱动管理层的简单例子,如果驱动中直接调用了 rt_device_register(),它很可能绕过了框架层。如果调用的是 rt_hw_xxx_register()xxx是设备类型),那它一定是通过框架层注册的。

对于像看门口这种就会通过设备管理驱动层:

在这里插入图片描述

I/O 设备模型

RT-Thread 的设备模型是建立在内核对象模型基础之上的,设备被认为是一类对象,被纳入对象管理器的范畴。每个设备对象都是由基对象派生而来,每个具体设备都可以继承其父类对象的属性,并派生出其私有属性

在这里插入图片描述

struct rt_device
{struct rt_object          parent;        /* 内核对象基类 */enum rt_device_class_type type;          /* 设备类型 */rt_uint16_t               flag;          /* 设备参数 */rt_uint16_t               open_flag;     /* 设备打开标志 */rt_uint8_t                ref_count;     /* 设备被引用次数 */rt_uint8_t                device_id;     /* 设备 ID,0 - 255 *//* 数据收发回调函数 */rt_err_t (*rx_indicate)(rt_device_t dev, rt_size_t size);rt_err_t (*tx_complete)(rt_device_t dev, void *buffer);const struct rt_device_ops *ops;    /* 设备操作方法 *//* 设备的私有数据 */void *user_data;
};
typedef struct rt_device *rt_device_t;

I/O设备类型

设备类型主要有

RT_Device_Class_Char             /* 字符设备       */
RT_Device_Class_Block            /* 块设备         */
RT_Device_Class_NetIf            /* 网络接口设备    */
RT_Device_Class_MTD              /* 内存设备       */
RT_Device_Class_RTC              /* RTC 设备        */
RT_Device_Class_Sound            /* 声音设备        */
RT_Device_Class_Graphic          /* 图形设备        */
RT_Device_Class_I2CBUS           /* I2C 总线设备     */
RT_Device_Class_USBDevice        /* USB device 设备  */
RT_Device_Class_USBHost          /* USB host 设备   */
RT_Device_Class_SPIBUS           /* SPI 总线设备     */
RT_Device_Class_SPIDevice        /* SPI 设备        */
RT_Device_Class_SDIO             /* SDIO 设备       */
RT_Device_Class_Miscellaneous    /* 杂类设备        */

按功能分类

存储类设备

  • RT_Device_Class_Block - 块设备:硬盘、SD卡、eMMC等大容量存储
  • RT_Device_Class_MTD - 内存技术设备:Flash、NAND Flash等

通信接口设备

  • RT_Device_Class_Char - 字符设备:串口(UART)、控制台等按字节传输
  • RT_Device_Class_NetIf - 网络接口:以太网、WiFi、蓝牙等网络设备
  • RT_Device_Class_I2CBUS - I2C总线:I2C控制器
  • RT_Device_Class_SPIBUS - SPI总线:SPI控制器
  • RT_Device_Class_SPIDevice - SPI设备:挂载在SPI总线上的具体设备

USB设备

  • RT_Device_Class_USBDevice - USB从设备:作为USB设备连接到主机
  • RT_Device_Class_USBHost - USB主机:可以连接USB设备的主控制器

多媒体设备

  • RT_Device_Class_Sound - 音频设备:扬声器、麦克风、音频编解码器
  • RT_Device_Class_Graphic - 图形设备:LCD显示屏、摄像头

功能设备

  • RT_Device_Class_RTC - 实时时钟:RTC芯片
  • RT_Device_Class_SDIO - SDIO接口:WiFi模块、蓝牙模块等
  • RT_Device_Class_Miscellaneous - 杂项:GPIO、PWM、ADC、看门狗等

看设备的数据传输特性和用途:

  • 按字节传输 → Char 数据传输采用串行的形式,每次一个字节
  • 按块传输 → Block 每次传输一个数据块,例如每次传输 512 个字节数据(由硬件设备要求)
  • 网络通信 → NetIf
  • 时间相关 → RTC
  • 不好归类 → Miscellaneous

I/O框架的使用

使用包括创建和注册I/O设备以及访问I/O设备

创建和注册I/O设备

作用:设备驱动开发者的工作

  • 创建设备对象:分配rt_device结构体,初始化设备属性
  • 实现操作函数:编写具体的open、close、read、write等函数
  • 注册到系统:调用rt_device_register()将设备加入对象管理器

访问I/O设备

作用:应用程序开发者的工作

  • 查找设备:通过设备名找到设备对象
  • 打开设备:获取设备使用权
  • 读写数据:进行实际的I/O操作
  • 关闭设备:释放设备资源
创建和注册I/O设备
创建I/O设备

原型:

rt_device_t rt_device_create(int type, int attach_size);	//设备类型  用户数据大小

该函数分配rt_device结构体,初始化设备属性

attach_size是额外的私有数据(指每个具体设备实例自己独有的、特定的数据,不同设备之间不共享),当不需要额外数据时为0即可。

返回值:

返回描述
设备句柄创建成功
RT_NULL创建失败,动态内存分配失败

设备被创建后,我们需要实现的是访问硬件的操作方法

struct rt_device_ops
{/* common device interface */rt_err_t  (*init)   (rt_device_t dev);rt_err_t  (*open)   (rt_device_t dev, rt_uint16_t oflag);rt_err_t  (*close)  (rt_device_t dev);rt_size_t (*read)   (rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size);rt_size_t (*write)  (rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size);rt_err_t  (*control)(rt_device_t dev, int cmd, void *args);
};
方法名称方法描述
init初始化设备。设备初始化完成后,设备控制块的 flag 会被置成已激活状态 (RT_DEVICE_FLAG_ACTIVATED)。如果设备控制块中的 flag 标志已经设置成激活状态,那么再运行初始化接口时会立刻返回,而不会重新进行初始化。
open打开设备。有些设备并不是系统一启动就已经打开开始运行,或者设备需要进行数据收发,但如果上层应用还未准备好,设备也不应默认已经使能并开始接收数据。所以建议在写底层驱动程序时,在调用 open 接口时才使能设备。
close关闭设备。在打开设备时,设备控制块会维护一个打开计数,在打开设备时进行 + 1 操作,在关闭设备时进行 - 1 操作,当计数器变为 0 时,才会进行真正的关闭操作。
read从设备读取数据。参数 pos 是读取数据的偏移量,但是有些设备并不一定需要指定偏移量,例如串口设备,设备驱动应忽略这个参数。而对于块设备来说,pos 以及 size 都是以块设备的数据块大小为单位的。例如块设备的数据块大小是 512,而参数中 pos = 10, size = 2,那么驱动应该返回设备中第 10 个块 (从第 0 个块做为起始),共计 2 个块的数据。这个接口返回的类型是 rt_size_t,即读到的字节数或块数目。正常情况下应该会返回参数中 size 的数值,如果返回零请设置对应的 errno 值。
write向设备写入数据。参数 pos 是写入数据的偏移量。与读操作类似,对于块设备来说,pos 以及 size 都是以块设备的数据块大小为单位的。这个接口返回的类型是 rt_size_t,即真实写入数据的字节数或块数目。正常情况下应该会返回参数中 size 的数值,如果返回零请设置对应的 errno 值。
control根据 cmd 命令控制设备。命令往往是由底层各类设备驱动自定义实现。例如参数 RT_DEVICE_CTRL_BLK_GETGEOME,意思是获取块设备的大小信息。
销毁I/O设备

原型:

void rt_device_destroy(rt_device_t device);	//设备句柄

当一个动态创建的设备不再需要使用时可以通过如下函数来销毁,调用了这个函数会消除分配给该I/O设备的内存

注册I/O设备管理器

原型:

rt_err_t rt_device_register(rt_device_t dev, const char* name, rt_uint8_t flags);//设备句柄 设备名 设备标志

设备标志可以选择

#define RT_DEVICE_FLAG_RDONLY       0x001 /* 只读 */
#define RT_DEVICE_FLAG_WRONLY       0x002 /* 只写  */
#define RT_DEVICE_FLAG_RDWR         0x003 /* 读写  */
#define RT_DEVICE_FLAG_REMOVABLE    0x004 /* 可移除  */
#define RT_DEVICE_FLAG_STANDALONE   0x008 /* 独立   */
#define RT_DEVICE_FLAG_SUSPENDED    0x020 /* 挂起  */
#define RT_DEVICE_FLAG_STREAM       0x040 /* 流模式  */
#define RT_DEVICE_FLAG_INT_RX       0x100 /* 中断接收 */
#define RT_DEVICE_FLAG_DMA_RX       0x200 /* DMA 接收 */
#define RT_DEVICE_FLAG_INT_TX       0x400 /* 中断发送 */
#define RT_DEVICE_FLAG_DMA_TX       0x800 /* DMA 发送 */

返回值:

返回描述
RT_EOK注册成功
-RT_ERROR注册失败,dev 为空或者 name 已经存在
解除注册I/O设备

原型:

rt_err_t rt_device_unregister(rt_device_t dev);	//设备句柄

调用解除注册I/O设备后,该设备的内存并不会被清除,只有彻底销毁I/O设备才会清除内存

返回:

返回描述
RT_EOK成功
访问I/O设备

应用程序通过 I/O 设备管理接口来访问硬件设备,当设备驱动实现后,应用程序就可以访问该硬件。I/O 设备管理接口与 I/O 设备的操作方法的映射关系下图所示:

在这里插入图片描述

查找设备

原型:

rt_device_t rt_device_find(const char* name);	//设备名

调用该函数后,会进行查找若找到设备就会返回该设备控制块的句柄

返回值:

返回描述
设备句柄查找到对应设备将返回相应的设备句柄
RT_NULL没有找到相应的设备对象
初始化设备

原型:

rt_err_t rt_device_init(rt_device_t dev);	//设备句柄

注意:当一个设备已经初始化成功后,调用这个接口将不再重复做初始化 0。

返回值:

返回描述
RT_EOK设备初始化成功
错误码设备初始化失败
打开和设备

原型:

rt_err_t rt_device_open(rt_device_t dev, rt_uint16_t oflags);	//设备句柄 设备打开模式

oflags标志位可以取下面的宏:

#define RT_DEVICE_OFLAG_CLOSE 0x000   /* 设备已经关闭(内部使用)*/
#define RT_DEVICE_OFLAG_RDONLY 0x001  /* 以只读方式打开设备 */
#define RT_DEVICE_OFLAG_WRONLY 0x002  /* 以只写方式打开设备 */
#define RT_DEVICE_OFLAG_RDWR 0x003    /* 以读写方式打开设备 */
#define RT_DEVICE_OFLAG_OPEN 0x008    /* 设备已经打开(内部使用)*/
#define RT_DEVICE_FLAG_STREAM 0x040   /* 设备以流模式打开 */
#define RT_DEVICE_FLAG_INT_RX 0x100   /* 设备以中断接收模式打开 */
#define RT_DEVICE_FLAG_DMA_RX 0x200   /* 设备以 DMA 接收模式打开 */
#define RT_DEVICE_FLAG_INT_TX 0x400   /* 设备以中断发送模式打开 */
#define RT_DEVICE_FLAG_DMA_TX 0x800   /* 设备以 DMA 发送模式打开 */

注意:如果上层应用程序需要设置设备的接收回调函数,则必须以 RT_DEVICE_FLAG_INT_RX 或者 RT_DEVICE_FLAG_DMA_RX 的方式打开设备,否则不会回调函数。

返回值:

返回描述
RT_EOK设备打开成功
-RT_EBUSY如果设备注册时指定的参数中包括 RT_DEVICE_FLAG_STANDALONE 参数,此设备将不允许重复打开
其他错误码设备打开失败
关闭设备

原型:

rt_err_t rt_device_close(rt_device_t dev);	//设备句柄

当应用完成读写后就要关闭设备,关闭设备接口和打开设备接口需配对使用,打开一次设备对应要关闭一次设备,这样设备才会被完全关闭,否则设备仍处于未关闭状态。

返回值:

返回描述
RT_EOK关闭设备成功
-RT_ERROR设备已经完全关闭,不能重复关闭设备
其他错误码关闭设备失败
控制设备

原型:

rt_err_t rt_device_control(rt_device_t dev, rt_uint8_t cmd, void* arg);//设备句柄 设备命令 控制参数

cmd控制宏定义:

#define RT_DEVICE_CTRL_RESUME           0x01   /* 恢复设备 */
#define RT_DEVICE_CTRL_SUSPEND          0x02   /* 挂起设备 */
#define RT_DEVICE_CTRL_CONFIG           0x03   /* 配置设备 */
#define RT_DEVICE_CTRL_SET_INT          0x10   /* 设置中断 */
#define RT_DEVICE_CTRL_CLR_INT          0x11   /* 清中断 */
#define RT_DEVICE_CTRL_GET_INT          0x12   /* 获取中断状态 */

返回值:

返回描述
RT_EOK函数执行成功
-RT_ENOSYS执行失败,dev 为空
其他错误码执行失败
读设备

原型:

rt_size_t rt_device_read(rt_device_t dev, rt_off_t pos,void* buffer, rt_size_t size);//设备句柄 偏移量 接收缓存 读取数据大小

调用这个函数,会从 dev 设备中读取数据,并存放在 buffer 缓冲区中,这个缓冲区的最大长度是 size,pos 根据不同的设备类别有不同的意义。

返回值:

返回描述
读到数据的实际大小如果是字符设备,返回大小以字节为单位,如果是块设备,返回的大小以块为单位
0需要读取当前线程的 errno 来判断错误状态
写设备

原型:

rt_size_t rt_device_write(rt_device_t dev, rt_off_t pos,const void* buffer, rt_size_t size);//设备句柄 偏移量 写入数据缓存区 数据大小

调用这个函数,会把缓冲区 buffer 中的数据写入到设备 dev 中,写入数据的最大长度是 size,pos 根据不同的设备类别存在不同的意义。

返回值:

返回描述
写入数据的实际大小如果是字符设备,返回大小以字节为单位;如果是块设备,返回的大小以块为单位
0需要读取当前线程的 errno 来判断错误状态
数据接收回调函数

原型:

rt_err_t rt_device_set_rx_indicate(rt_device_t dev, rt_err_t (*rx_ind)(rt_device_t dev,rt_size_t size));//设备句柄 接收回调函数

当硬件设备收到数据时,可以通过如下函数回调另一个函数来设置数据接收指示,通知上层应用线程有数据到达,会回调这个函数并把收到的数据长度放在 size 参数中传递给上层应用。上层应用线程应在收到指示后,立刻从设备中读取数据

返回值:

返回描述
RT_EOK设置成功
数据发送回调函数

原型:

rt_err_t rt_device_set_tx_complete(rt_device_t dev, rt_err_t (*tx_done)(rt_device_t dev,void *buffer));//设备句柄 接收回调函数

在应用程序调用 rt_device_write() 写入数据时,如果底层硬件能够支持自动发送,那么上层应用可以设置一个回调函数。

返回值:

返回描述
RT_EOK设置成功

分析PIN设备理解I/O设备框架

首先我们拿rt_pin_read(pin)函数作为切入口

rt_pin_read(pin);[应用层API]->
int rt_pin_read(rt_base_t pin)
{RT_ASSERT(_hw_pin.ops != RT_NULL);return _hw_pin.ops->pin_read(&_hw_pin.parent, pin);
}
->>_hw_pin.ops->pin_read(&_hw_pin.parent, pin);[驱动抽象层API]->
struct rt_pin_ops
{void (*pin_mode)(struct rt_device *device, rt_base_t pin, rt_base_t mode);void (*pin_write)(struct rt_device *device, rt_base_t pin, rt_base_t value);int (*pin_read)(struct rt_device *device, rt_base_t pin);rt_err_t (*pin_attach_irq)(struct rt_device *device, rt_int32_t pin,rt_uint32_t mode, void (*hdr)(void *args), void *args);rt_err_t (*pin_detach_irq)(struct rt_device *device, rt_int32_t pin);rt_err_t (*pin_irq_enable)(struct rt_device *device, rt_base_t pin, rt_uint32_t enabled);rt_base_t (*pin_get)(const char *name);
};    
->>static int stm32_pin_read(rt_device_t dev, rt_base_t pin);[设备驱动层API]->
const static struct rt_pin_ops _stm32_pin_ops =
{stm32_pin_mode,stm32_pin_write,stm32_pin_read,stm32_pin_attach_irq,stm32_pin_dettach_irq,stm32_pin_irq_enable,stm32_pin_get,
};
static int stm32_pin_read(rt_device_t dev, rt_base_t pin)
{GPIO_TypeDef *gpio_port;uint16_t gpio_pin;int value = PIN_LOW;if (PIN_PORT(pin) < PIN_STPORT_MAX){gpio_port = PIN_STPORT(pin);gpio_pin = PIN_STPIN(pin);value = HAL_GPIO_ReadPin(gpio_port, gpio_pin);}return value;
}

I/O设备创建

使用OPS操作集绑定(**RT_USING_DEVICE_OPS**已经宏定义了)

底层驱动层

#include <board.h>
#include <rtdevice.h> // 必须包含,用于设备框架
#define DBG_TAG "drv.test"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>//ops操作集
static rt_err_t test_device_init(rt_device_t dev)
{LOG_I("This is device_init\r\n");return RT_EOK;
}static rt_err_t test_device_open(rt_device_t dev, rt_uint16_t oflag)
{LOG_I("This is device_open\r\n");return RT_EOK;
}static rt_err_t test_device_close(rt_device_t dev)
{LOG_I("This is device_close\r\n");return RT_EOK;
}static rt_size_t test_device_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
{LOG_I("This is device_read\r\n");return 0; // 返回 0 表示没有读到任何数据
}static rt_size_t test_device_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)
{LOG_I("This is device_write\r\n");return size; // 返回实际写入的字节数
}static rt_err_t test_device_control(rt_device_t dev, int cmd, void *args)
{LOG_I("This is device_control\r\n");return RT_EOK;
}// 使用 .成员名 = 函数名 的方式初始化,更清晰,不易出错
const static struct rt_device_ops test_device_pos =
{.init    = test_device_init,.open    = test_device_open,.close   = test_device_close,.read    = test_device_read,.write   = test_device_write,.control = test_device_control,
};//设备初始化
int mytest_device_init(void) // 入口函数通常返回 int
{rt_device_t test_device = rt_device_create(RT_Device_Class_Char, 0);if (test_device == RT_NULL){LOG_E("Failed to create 'test' device!");return -RT_ENOMEM;}test_device->ops = &test_device_pos;rt_err_t result = rt_device_register(test_device, "test", RT_DEVICE_FLAG_RDWR);if (result != RT_EOK){rt_device_destroy(test_device);LOG_E("Failed to register 'test' device, error: %d", result);return result;}LOG_I("Device 'test' registered successfully.");// 3. 补上返回值return RT_EOK;
}// 使用自动初始化宏
INIT_DEVICE_EXPORT(mytest_device_init);

应用测试层

#include <rtthread.h>#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>int main(void)
{rt_kprintf("||main start!||\r\n");//查找rt_device_t device=NULL;device=rt_device_find("test");if(device!=RT_NULL)LOG_D("Find device ok\r\n");//测试rt_device_open(device, 0);rt_device_close(device);return RT_EOK;
}
原始旧方法绑定
#include <board.h>
#include <rtdevice.h> // 必须包含,用于设备框架
#define DBG_TAG "drv.test"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>//ops操作集
static rt_err_t test_device_init(rt_device_t dev)
{LOG_I("This is device_init\r\n");return RT_EOK;
}static rt_err_t test_device_open(rt_device_t dev, rt_uint16_t oflag)
{LOG_I("This is device_open\r\n");return RT_EOK;
}static rt_err_t test_device_close(rt_device_t dev)
{LOG_I("This is device_close\r\n");return RT_EOK;
}static rt_size_t test_device_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
{LOG_I("This is device_read\r\n");return 0; // 返回 0 表示没有读到任何数据
}static rt_size_t test_device_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)
{LOG_I("This is device_write\r\n");return size; // 返回实际写入的字节数
}static rt_err_t test_device_control(rt_device_t dev, int cmd, void *args)
{LOG_I("This is device_control\r\n");return RT_EOK;
}//设备初始化
int mytest_device_init(void) // 入口函数通常返回 int
{rt_device_t test_device = rt_device_create(RT_Device_Class_Char, 0);if (test_device == RT_NULL){LOG_E("Failed to create 'test' device!");return -RT_ENOMEM;}//手动绑定操作test_device->init=test_device_init;test_device->open=test_device_open;test_device->close=test_device_close;test_device->read=test_device_read;test_device->write=test_device_write;test_device->control=test_device_control;rt_err_t result = rt_device_register(test_device, "test", RT_DEVICE_FLAG_RDWR);if (result != RT_EOK){rt_device_destroy(test_device);LOG_E("Failed to register 'test' device, error: %d", result);return result;}LOG_I("Device 'test' registered successfully.");// 3. 补上返回值return RT_EOK;
}// 使用自动初始化宏
INIT_DEVICE_EXPORT(mytest_device_init);

LOG打印补充

#define DBG_TAG "drv.test"

这个相当于是一个标签主要是在shell面板中可以看出是哪个地方打印的调试

深入讲解:DBG_LVL 对打印的影响

首先,RT-Thread 定义了几个日志级别,从高到低(重要性从高到低) 排列如下:

级别定义对应宏含义
DBG_ERRORLOG_E(...)错误 (Error): 致命错误,导致程序无法正常运行。必须处理!
DBG_WARNINGLOG_W(...)警告 (Warning): 潜在的问题,程序还能继续跑,但可能存在风险。
DBG_INFOLOG_I(...)信息 (Info): 程序运行过程中的一些状态信息,比如初始化成功、连接建立等。
DBG_DEBUGLOG_D(...)调试 (Debug): 最详细的调试信息,用于开发阶段定位问题,比如变量值、函数入口等。
DBG_LOGLOG_RAW(...)原始日志:不带任何前缀,直接打印。

核心规则:
你为 DBG_LVL 设置的那个级别,就是这个文件的**“最低打印门槛”**

只有级别等于或高于这个门槛的日志,才会被编译和打印出来。

打印出来的示例

[I/drv.test] This is device_init      // <-- 一眼看出是 drv.test 文件打印的
[I/main] Find device 'test' successfully. // <-- 一眼看出是 main 文件打印的
[E/drv.spi] SPI bus lock failed!      // <-- 一眼看出是 drv.spi 文件打印的

----------------------------------------------------- |
| DBG_ERROR | LOG_E(...) | 错误 (Error): 致命错误,导致程序无法正常运行。必须处理! |
| DBG_WARNING | LOG_W(...) | 警告 (Warning): 潜在的问题,程序还能继续跑,但可能存在风险。 |
| DBG_INFO | LOG_I(...) | 信息 (Info): 程序运行过程中的一些状态信息,比如初始化成功、连接建立等。 |
| DBG_DEBUG | LOG_D(...) | 调试 (Debug): 最详细的调试信息,用于开发阶段定位问题,比如变量值、函数入口等。 |
| DBG_LOG | LOG_RAW(...) | 原始日志:不带任何前缀,直接打印。 |

核心规则:
你为 DBG_LVL 设置的那个级别,就是这个文件的**“最低打印门槛”**

只有级别等于或高于这个门槛的日志,才会被编译和打印出来。

打印出来的示例

[I/drv.test] This is device_init      // <-- 一眼看出是 drv.test 文件打印的
[I/main] Find device 'test' successfully. // <-- 一眼看出是 main 文件打印的
[E/drv.spi] SPI bus lock failed!      // <-- 一眼看出是 drv.spi 文件打印的
http://www.dtcms.com/a/388793.html

相关文章:

  • DNS服务管理
  • IDA Pro配置与笔记
  • 虚函数表在单继承与多继承中的实现机制
  • 矿石生成(1)
  • Linux 线程的概念
  • Unity学习之资源管理(Resources、AssetDatabase、AssetBundle、Addressable)
  • LG P5138 fibonacci Solution
  • 删除UCPD监控服务或者监控驱动
  • 日语学习-日语知识点小记-构建基础-JLPT-N3阶段(33):文法運用第10回1+(考え方14)
  • 向量技术研究报告:从数学基础到AI革命的支柱
  • 802.1x和802.1Q之间关联和作用
  • 基于大模型多模态的人体体型评估:从“尺码测量”到“视觉-感受”范式
  • 更符合人类偏好的具身导航!HALO:面向机器人导航的人类偏好对齐离线奖励学习
  • Transformer多头注意力机制
  • git 分支 error: src refspec sit does not match any`
  • VN1640 CH5 I/O通道终极指南:【VN1630 I/O功能在电源电压时间精确度测试中的深度应用】
  • qt QHorizontalBarSeries详解
  • 半导体制造的芯片可靠性测试的全类别
  • MySQL 索引详解:原理、类型与优化实践
  • AI 重塑就业市场:哪些岗位将被替代?又会催生哪些新职业赛道?
  • mysql表分区备份太慢?如何精准“狙击”所需数据?
  • InVEST实践及在生态系统服务供需、固碳、城市热岛、论文写作等实际项目中应用
  • 数据库视图详解
  • C#并行处理CPU/内存监控:用PerformanceCounter实时监控,避免资源过载(附工具类)
  • 数据结构初阶——红黑树的实现(C++)
  • PS练习1:将风景图放到相框中
  • Seedream 4.0深度评测:新一代AI图像创作的革命性突破
  • Python中的异常和断言
  • java求职学习day32
  • 内存一致性模型(Memory Consistency Model)及其核心难度