Skip to content

IO模型

驱动一般在drivers目录下面

image-20240128191745924

提供了一套简单的 I/O 设备模型框架,如下图所示,它位于硬件和应用程序之间,共分成三层,从上到下分别是 I/O 设备管理层、设备驱动框架层、设备驱动层。

image-20240128162158303

  • 应用程序通过 I/O 设备管理接口获得正确的设备驱动,然后通过这个设备驱动与底层 I/O 硬件设备进行交互。
  • I/O 设备管理层实现了对设备驱动程序的封装
  • 设备驱动框架层是对同类硬件设备驱动的抽象,将不同厂家的同类硬件设备驱动中相同的部分抽取出来,将不同部分留出接口,由驱动程序实现。
  • 设备驱动层是一组驱使硬件设备工作的程序,实现访问硬件设备的功能。

简单设备的注册不经过设备驱动框架层,直接将设备注册到I/O设备管理器中

  • 设备驱动根据设备模型定义,创建出具备硬件访问能力的设备实例,将该设备通过rt_device_register()接口注册到 I/O 设备管理器中
  • 应用程序通过 rt_device_find()接口查找到设备,然后使用 I/O 设备管理接口来访
image-20240128162659825

对于一些复杂设备,需要使用到对应的设备驱动框架层,进行注册,如:看门狗定时器

  • 看门狗设备驱动程序根据看门狗设备模型定义,创建出具备硬件访问能力的看门狗设备实例,并将该看门狗设备通过 rt_hw_watchdog_register()接口注册到看门狗设备驱动框架中
  • 看门狗设备驱动框架通过 rt_device_register()接口将看门狗设备注册到 I/O 设备管理器中
  • 应用程序通过 I/O 设备管理接口来访问看门狗设备硬件
image-20240128162838121

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设备(用户层)

image-20240128182148339

查找一个设备

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 个变量没有在代码中定义

c
static 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;
}
image-20240128201940159

image-20240128205057888