Appearance
IO模型
驱动一般在drivers目录下面
提供了一套简单的 I/O 设备模型框架,如下图所示,它位于硬件和应用程序之间,共分成三层,从上到下分别是 I/O 设备管理层、设备驱动框架层、设备驱动层。
- 应用程序通过 I/O 设备管理接口获得正确的设备驱动,然后通过这个设备驱动与底层 I/O 硬件设备进行交互。
- I/O 设备管理层实现了对设备驱动程序的封装
- 设备驱动框架层是对同类硬件设备驱动的抽象,将不同厂家的同类硬件设备驱动中相同的部分抽取出来,将不同部分留出接口,由驱动程序实现。
- 设备驱动层是一组驱使硬件设备工作的程序,实现访问硬件设备的功能。
简单设备的注册不经过设备驱动框架层,直接将设备注册到I/O设备管理器中
- 设备驱动根据设备模型定义,创建出具备硬件访问能力的设备实例,将该设备通过rt_device_register()接口注册到 I/O 设备管理器中
- 应用程序通过 rt_device_find()接口查找到设备,然后使用 I/O 设备管理接口来访

对于一些复杂设备,需要使用到对应的设备驱动框架层,进行注册,如:看门狗定时器
- 看门狗设备驱动程序根据看门狗设备模型定义,创建出具备硬件访问能力的看门狗设备实例,并将该看门狗设备通过 rt_hw_watchdog_register()接口注册到看门狗设备驱动框架中
- 看门狗设备驱动框架通过 rt_device_register()接口将看门狗设备注册到 I/O 设备管理器中
- 应用程序通过 I/O 设备管理接口来访问看门狗设备硬件

IO设备类型
c
RT_Device_Class_Char = 0, /**< character device 字符设备 */ RT_Device_Class_Block, /**< block device 块设备*/ RT_Device_Class_NetIf, /**< net interface 网络设备*/ RT_Device_Class_MTD, /**< memory device 内存设备*/ RT_Device_Class_CAN, /**< CAN device */ RT_Device_Class_RTC, /**< RTC device */ RT_Device_Class_Sound, /**< Sound device 声音 */ RT_Device_Class_Graphic, /**< Graphic device 图形 */ RT_Device_Class_I2CBUS, /**< I2C bus device */ RT_Device_Class_USBDevice, /**< USB slave device */ RT_Device_Class_USBHost, /**< USB host bus */ RT_Device_Class_SPIBUS, /**< SPI bus device */ RT_Device_Class_SPIDevice, /**< SPI device */ RT_Device_Class_SDIO, /**< SDIO bus device */ RT_Device_Class_Timer, /**< Timer device */ RT_Device_Class_Miscellaneous, /**< misc device 杂项设备*/ RT_Device_Class_Sensor, /**< Sensor device */ RT_Device_Class_Touch, /**< Touch device */ RT_Device_Class_Unknown /**< unknown device */
字符设备、块设备是常用的设备类型,它们的分类依据是设备数据与系统之间的传输处理方式。字符模式设备允许非结构的数据传输,即通常数据传输采用串行的形式,每次一个字节。字符设备通常是一些简单设备,如串口、按键。
块设备每次传输一个数据块,例如每次传输 512 个字节数据。这个数据块是硬件强制性的,数据块可能使用某类数据接口或某些强制性的传输协议,否则就可能发生错误。因此,有时块设备驱动程序对读或写操作必须执行附加的工作
I/O设备的注册
创建以及注册
c
/**
* This function creates a device object with user data size.
*
* @param type, the kind type of this device object.
* @param attach_size, the size of user data.
*
* @return the allocated device object, or RT_NULL when failed.
*/
rt_device_t rt_device_create(int type, int attach_size)
c
/**
* This function will initialize the specified device
*
* @param dev the pointer of device driver structure
*
* @return the result
*/
rt_err_t rt_device_init(rt_device_t dev)
注册函数
c
/**
* This function registers a device driver with specified name.
*
* @param dev the pointer of device driver structure
* @param name the device driver's name 这一个名字是find函数使用的
* @param flags the capabilities flag of device 设备模式标志
*
* @return the error code, RT_EOK on initialization successfully.
*/
rt_err_t rt_device_register(rt_device_t dev,
const char *name,
rt_uint16_t flags)
c#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发送*/
注销以及销毁
c
/**
* This function removes a previously registered device driver
*
* @param dev the pointer of device driver structure
*
* @return the error code, RT_EOK on successfully.
*/
rt_err_t rt_device_unregister(rt_device_t dev)
这一个函数不会销毁他的管理模块, 但是不可以再通过搜索找到
c
/**
* This function destroy the specific device object.
*
* @param dev, the specific device object.
*/
void rt_device_destroy(rt_device_t dev)
需要实现的访问的操作方法集
c
/**
* operations set for device object
*/
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);
};
这一些函数会被放在rt_device_t里面
访问I/O设备(用户层)
查找一个设备
c
/**
* This function finds a device driver by specified name.
*
* @param name the device driver's name
*
* @return the registered device driver on successful, or RT_NULL on failure.
*/
rt_device_t rt_device_find(const char *name)
打开一个设备
c
/**
* This function will open a device
*
* @param dev the pointer of device driver structure
* @param oflag the flags for device open
*
* @return the result
*/
rt_err_t rt_device_open(rt_device_t dev, rt_uint16_t oflag)
c#define RT_DEVICE_FLAG_INT_RX 0x100 /**< INT mode on Rx 中断接收*/ #define RT_DEVICE_FLAG_DMA_RX 0x200 /**< DMA mode on Rx DMA接收*/ #define RT_DEVICE_FLAG_INT_TX 0x400 /**< INT mode on Tx 中断发送*/ #define RT_DEVICE_FLAG_DMA_TX 0x800 /**< DMA mode on Tx DMA发送*/ #define RT_DEVICE_OFLAG_CLOSE 0x000 /**< device is closed 设备已关闭(内部使用)*/ #define RT_DEVICE_OFLAG_RDONLY 0x001 /**< read only access 只读的方式打开*/ #define RT_DEVICE_OFLAG_WRONLY 0x002 /**< write only access 只写的方式打开*/ #define RT_DEVICE_OFLAG_RDWR 0x003 /**< read and write 读写的方式打开*/ #define RT_DEVICE_OFLAG_OPEN 0x008 /**< device is opened 已经打开了(内部使用)*/ #define RT_DEVICE_OFLAG_MASK 0xf0f /**< mask of open flag*/ #define RT_DEVICE_FLAG_RDONLY 0x001 /**< read only */ #define RT_DEVICE_FLAG_WRONLY 0x002 /**< write only */ #define RT_DEVICE_FLAG_RDWR 0x003 /**< read and write */ #define RT_DEVICE_FLAG_REMOVABLE 0x004 /**< removable device */ #define RT_DEVICE_FLAG_STANDALONE 0x008 /**< standalone device */ #define RT_DEVICE_FLAG_ACTIVATED 0x010 /**< device is activated */ #define RT_DEVICE_FLAG_SUSPENDED 0x020 /**< device is suspended */ #define RT_DEVICE_FLAG_STREAM 0x040 /**< stream mode 流设备*/
RT_DEVICE_FLAG_STREAM:流模式用于向串口终端输出字符串:当输出的字符是 "\n"(对应 16 进制值为 0x0A)时,自动在前面输出一个 "\r"(对应 16 进制值为 0x0D)做分行。
关闭设备
c
/**
* This function will close a device
*
* @param dev the pointer of device driver structure
*
* @return the result
*/
rt_err_t rt_device_close(rt_device_t dev)
控制设备
c
/**
* This function will perform a variety of control functions on devices.
*
* @param dev the pointer of device driver structure
* @param cmd the command sent to device
* @param arg the argument of command
*
* @return the result
*/
rt_err_t rt_device_control(rt_device_t dev, int cmd, void *arg)
可以使用的参数
c#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 /* 获取中断状态 */
读写设备
c
/**
* This function will read some data from a device.
*
* @param dev the pointer of device driver structure
* @param pos the position of reading
* @param buffer the data buffer to save read data
* @param size the size of buffer
*
* @return the actually read size on successful, otherwise negative returned.
*
* @note since 0.4.0, the unit of size/pos is a block for block device.
*/
rt_size_t rt_device_read(rt_device_t dev,
rt_off_t pos,
void *buffer,
rt_size_t size)
/**
* This function will write some data to a device.
*
* @param dev the pointer of device driver structure
* @param pos the position of written
* @param buffer the data buffer to be written to device
* @param size the size of buffer
*
* @return the actually written size on successful, otherwise negative returned.
*
* @note since 0.4.0, the unit of size/pos is a block for block device.
*/
rt_size_t rt_device_write(rt_device_t dev,
rt_off_t pos,
const void *buffer,
rt_size_t size)
数据的收发回调函数
当硬件设备收到数据时,可以通过如下函数回调另一个函数来设置数据接收指示,通知上层应用线程有数据到达
c
/**
* This function will set the reception indication callback function.
* This callback function
* is invoked when this device receives data.
*
* @param dev the pointer of device driver structure
* @param rx_ind the indication callback function
*
* @return RT_EOK
*/
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))
/**
* This function will set the indication callback function when device has
* written data to physical hardware.
*
* @param dev the pointer of device driver structure
* @param tx_done the indication callback function
*
* @return 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))
实际使用
注册
在drivers目录下面添加一个文件, 在工程里面进行刷新
需要使用一个宏定义用来把这一个函数进行注册
c
INIT_BOARD_EXPORT(rt_wdt_init);
//进行展开
/***************1**************/
#define INIT_BOARD_EXPORT(fn) INIT_EXPORT(fn, "1")
INIT_EXPORT(rt_wdt_init, "1")
/***************2*************/
#define INIT_EXPORT(fn, level) \
RT_USED const init_fn_t __rt_init_##fn SECTION(".rti_fn." level) = fn
RT_USED const init_fn_t __rt_init_rt_wdt_init SECTION(".rti_fn." "1") = rt_wdt_init
/***************3*************/
#define RT_USED __attribute__((used))
#define SECTION(x) __attribute__((section(x)))
__attribute__((used)) const init_fn_t __rt_init_rt_wdt_init __attribute__((section(".rti_fn.1"))) = rt_wdt_init
/*
attribute((used)) 其作用是告诉编译器避免被链接器因为未用过而被优化掉。
attribute((section(“name”))) 其作用是将作用的函数或数据放入指定名为"section_name"对应的段中
*/
宏替换完之后,就是定义了一个指向函数的指针变量
__rt_init_rt_hw_spi_init
,该变量值为rt_hw_spi_init
,同时该变量位于.rti_fn.1
段, 该符号段位于内存分配的 RO 段中。
c
/**
* @brief Onboard components initialization. In this function, the board-level
* initialization function will be called to complete the initialization
* of the on-board peripherals.
*/
void rt_components_board_init(void)
{
volatile const init_fn_t *fn_ptr;
//会在这里被调用
for (fn_ptr = &__rt_init_rti_board_start; fn_ptr < &__rt_init_rti_board_end; fn_ptr++)
{
(*fn_ptr)();
}
#endif /* RT_DEBUGING_INIT */
}
__rt_init_rti_board_start
和__rt_init_rti_board_end
这 2 个变量没有在代码中定义cstatic int rti_start(void) { return 0; } INIT_EXPORT(rti_start, "0"); static int rti_board_start(void) { return 0; } INIT_EXPORT(rti_board_start, "0.end"); static int rti_board_end(void) { return 0; } INIT_EXPORT(rti_board_end, "1.end"); static int rti_end(void) { return 0; } INIT_EXPORT(rti_end, "6.end");
通过
c__rt_init_start = .; KEEP(*(SORT(.rti_fn*))) __rt_init_end = .;KEEP(*(SORT(.rti_fn*)))
语句将所有的
.rti_fn*
的段,排序后放在 rt_init_start 和 rt_init_end 之间,KEEP 关键字强制链接器保留某些特定部分。
实际实现框架
c
#include <rtdevice.h>
#include <rtdbg.h>
rt_err_t demo_init (rt_device_t dev){
rt_kprintf("demo_init\n");
return 0;
}
rt_err_t demo_open (rt_device_t dev, rt_uint16_t oflag){
rt_kprintf("demo_open\n");
return 0;
}
rt_err_t demo_close (rt_device_t dev){
rt_kprintf("demo_close\n");
return 0;
}
int rt_demo_init(void)
{
rt_device_t demo_dev;
demo_dev = rt_device_create(RT_Device_Class_Char, 32);
if(demo_dev == RT_NULL){
LOG_E("rt_device demo create fail...\n");
return -ENOMEM;
}
demo_dev->init = demo_init;
demo_dev->open = demo_open;
demo_dev->close = demo_close;
rt_device_register(demo_dev, "demo", RT_DEVICE_FLAG_RDWR);
return 0;
}
INIT_BOARD_EXPORT(rt_demo_init);
c
rt_device_t dev;
int main(void)
{
dev = rt_device_find("demo");
if(dev == RT_NULL)
{
LOG_E("dev find err\n");
return -EINVAL;
}
rt_device_init(dev);
rt_device_open(dev, RT_DEVICE_OFLAG_RDWR);
rt_device_close(dev);
return 0;
}
