RT_threadのIO设备学习笔记

总结
RT_thread有一套I/O设备模型框架,如下图:
在这里插入图片描述

图1 I/O设备模型框架
 设备驱动通过函数rt_device_register将自己注册到IO设备管理器。应用程序可以使用函数rt_device_find查找设备;使用函数rt_device_open打开设备;使用函数rt_device_read读取设备数据;使用函数rt_device_close关闭设备。  RT_thread的设备模型是建立在内核对象模型基础之上的,每个设备都有继承关系,如图:

在这里插入图片描述

图2 设备继承关系

IO设备内核代码解析

 在rtdef.h文件里面列出了RT_thread的IO设备类型:

/**
 * device (I/O) class type
 */
enum rt_device_class_type
{
    RT_Device_Class_Char = 0,                           /**字符设备 */
    RT_Device_Class_Block,                              /**块设备 */
    RT_Device_Class_NetIf,                              /**网络接口设备 */
    RT_Device_Class_MTD,                                /**内存设备 */
    RT_Device_Class_CAN,                                /**< CAN设备 */
    RT_Device_Class_RTC,                                /**< RTC设备*/
    RT_Device_Class_Sound,                              /**声音设备 */
    RT_Device_Class_Graphic,                            /**图形设备 */
    RT_Device_Class_I2CBUS,                             /**I2C总线设备*/
    RT_Device_Class_USBDevice,                          /**USB设备 */
    RT_Device_Class_USBHost,                            /**USB host总线 */
    RT_Device_Class_SPIBUS,                             /**SPI总线设备*/
    RT_Device_Class_SPIDevice,                          /**SPI device */
    RT_Device_Class_SDIO,                               /**SDIO总线设备 */
    RT_Device_Class_PM,                                 /**PM pseudo device */
    RT_Device_Class_Pipe,                               /**Pipe device */
    RT_Device_Class_Portal,                             /**Portal device */
    RT_Device_Class_Timer,                              /**Timer device */
    RT_Device_Class_Miscellaneous,                      /**杂类设备 */
    RT_Device_Class_Sensor,                             /**传感设备 */
    RT_Device_Class_Unknown                             /**unknown device */
};

 在学习RT_thread的IO设备之前,有必要了解下设备对象的相关结构体:
 在rtdef.h文件里面,定义了这样的内核对象:

/**
 * Base structure of Kernel object
 */
struct rt_object
{
    char       name[RT_NAME_MAX];                       /*内核对象名称 */
    rt_uint8_t type;                                    /*内核对象类型*/
    rt_uint8_t flag;                                    /*内核对象标志*/

#ifdef RT_USING_MODULE
    void      *module_id;                               /**< id of application module */
#endif
    rt_list_t  list;                                    /*内核对象使用的双向链表 */
};
typedef struct rt_object *rt_object_t;  

接下来跟着上面的图,看看继承了内核对象的设备对象,rt_device定义如下(同样也是在rtdef.h里面定义):

/**
 * Device structure
 */
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);

#ifdef RT_USING_DEVICE_OPS
    const struct rt_device_ops *ops;
#else
    /* 通用的设备访问接口 */
    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);
#endif

#if defined(RT_USING_POSIX)
    const struct dfs_file_ops *fops;
    struct rt_wqueue wait_queue;
#endif

    void                     *user_data;                /*设备私有数据*/
};

下面是动态创建设备对象的函数:

rt_device_t rt_device_create(int type, int attach_size)
{
    int size;
    rt_device_t device;

    size = RT_ALIGN(sizeof(struct rt_device), RT_ALIGN_SIZE);
    attach_size = RT_ALIGN(attach_size, RT_ALIGN_SIZE);
    /* use the totoal size */
    size += attach_size;

    device = (rt_device_t)rt_malloc(size);
    if (device)
    {
        rt_memset(device, 0x0, sizeof(struct rt_device));
        device->type = (enum rt_device_class_type)type;
    }

    return device;
}

  函数的传参是设备的类型type,和attach_size(该参数是用于user_data指向的缓存)。利用rt_malloc实例化一个设备对象,如果实例化成功的话就初始化实例,并给设备对象定义类型。
  设备创建成功之后,就可以将它注册到IO设备管理器中,应用程序才能访问,注册设备的函数如下:

/**
 * This function registers a device driver with specified name.
 *
 * @param dev the pointer of device driver structure
 * @param name the device driver's name
 * @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)
{
    if (dev == RT_NULL)
        return -RT_ERROR;

    if (rt_device_find(name) != RT_NULL)
        return -RT_ERROR;

    rt_object_init(&(dev->parent), RT_Object_Class_Device, name);
    dev->flag = flags;
    dev->ref_count = 0;
    dev->open_flag = 0;

    return RT_EOK;
}

  传参中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_device_register函数,首先检查设备句柄是否有效,接着查找是否有名为*name的设备,有的话接着用函数rt_object_init初始化对象,接下来是对设备的标志、计数值、打开标志初始化。
下面看看函数rt_object_init的定义:

void rt_object_init(struct rt_object         *object,
                    enum rt_object_class_type type,
                    const char               *name)
{
    register rt_base_t temp;					//存放中断状态					
    struct rt_list_node *node = RT_NULL;		//定义一个双向链表指针
    struct rt_object_information *information;  //定义对象信息指针

    /* 获取对象信息 */
    information = rt_object_get_information(type);
    RT_ASSERT(information != RT_NULL);

    /* 检查对象类型避免重复初始化*/

    /* 进入临界区 */
    rt_enter_critical();
    /* 尝试查找对象 */
    for (node  = information->object_list.next;
            node != &(information->object_list);
            node  = node->next)
    {
        struct rt_object *obj;

        obj = rt_list_entry(node, struct rt_object, list);
        RT_ASSERT(obj != object);
    }
    /* 退出临界区 */
    rt_exit_critical();

    /* 初始化对象参数 */
    /* 设置对象类型为Static */
    object->type = type | RT_Object_Class_Static;
    /* 赋值名字 */
    rt_strncpy(object->name, name, RT_NAME_MAX);
	/*调用对象勾子函数*/
    RT_OBJECT_HOOK_CALL(rt_object_attach_hook, (object));

    /* 失能中断,返回之前的中断状态 */
    temp = rt_hw_interrupt_disable();

    {
        /* 插入对象信息到对象列表 */
        rt_list_insert_after(&(information->object_list), &(object->list));
    }

    /* 使能中断 */
    rt_hw_interrupt_enable(temp);
}

  先来看看其中的调用函数1:rt_object_get_information

/**
 * 此函数将返回指定类型的对象容器信息。
 *
 * @param type 枚举,对象的类型
 * @return 成功返回对象容器句柄
 */
struct rt_object_information *
rt_object_get_information(enum rt_object_class_type type)
{
    int index;

    for (index = 0; index < RT_Object_Info_Unknown; index ++)//RT_Object_Info_Unknown = 10
        if (rt_object_container[index].type == type) return &rt_object_container[index];

    return RT_NULL;
}

  结构体变量rt_object_container[RT_Object_Info_Unknown] *的数据类型是:

struct rt_list_node
{
    struct rt_list_node *next;                          /**指向下一个节点*/
    struct rt_list_node *prev;                          /**指向前一个节点*/
};
typedef struct rt_list_node rt_list_t;                  

struct rt_object_information
{
    enum rt_object_class_type type;                     /*对象类型 */
    rt_list_t                 object_list;              /*对象链表句柄 */
    rt_size_t                 object_size;              /*对象占用字节大小 */
};

  这里只贴出rt_object_container[RT_Object_Info_Unknown]初始化时的一项,如:

static struct rt_object_information rt_object_container[RT_Object_Info_Unknown] =
{
    /* 初始化对象容器 - thread */
    {RT_Object_Class_Thread, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Thread), sizeof(struct rt_thread)},...
};

  主要看第二个参数,我们知道struct rt_object_information的第二个成员是一个双向链表,所以第二个参数就是链表的值,是个宏,传递参数是一个枚举成员变量,我们看看宏展开是什么样子的,就是给链表初始化,直接前趋节点和后继节点都指向同一个节点。

#define _OBJ_CONTAINER_LIST_INIT(c)     \
    {&(rt_object_container[c].object_list), &(rt_object_container[c].object_list)}

  那么前面的函数rt_object_get_information实现的就是根据类型寻找到对应的对象容器。假设寻找到对象容器,那么接着继续执行rt_object_init代码,for循环实现了查找容器里面是否有相同的内核对象,RT_ASSERT(obj != object);如果为真表示即将初始化的object之前未存在。函数rt_list_entry返回的是某个成员变量所在结构体的地址,它是一个宏,展开之后比较好理解。
接着继续看下rt_object_init函数中的调用函数rt_list_insert_after,函数定义如下:

rt_inline void rt_list_insert_after(rt_list_t *l, rt_list_t *n)
{
    l->next->prev = n;
    n->next = l->next;

    l->next = n;
    n->prev = l;
}

  很明显,这个实现的是将节点插入到链表中,在函数rt_object_init中是将新实例化的对象的链表节点插入到对象容器链表中,实现注册。
  至此,对函数rt_err_t rt_device_register的学习到此结束,了解到设备是怎么注册的。
打开文件pin.h,可以看到IO引脚有这样一个结构体:

/* pin device and operations for RT-Thread */
struct rt_device_pin
{
    struct rt_device parent;
    const struct rt_pin_ops *ops;
};

  其中就继承了struct rt_device属性,这个struct rt_device_pin就是PIN设备,是具备硬件访问能力的,定义于设备驱动框架层,当然设备驱动框架层不止有PIN,还有很多接口驱动。当设备注册成功之后,就可以在FinSH命令行使用 list_device 命令查看系统中所有的设备信息,包括设备名称、设备类型和设备被打开次数。比如:
在这里插入图片描述

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值