RT-Thread对象register与find

        本篇文章主要是记录下自己在RT-Thread中探索的收获,因为弄清楚一个事物的根本性原理本来就是个很有成就感的事。

        首先,为什么要去探索这部分内容呢?

        因为在实际项目中经常对i2c进行读写操作,却并不知道在RT-Thread中i2c是怎么回事,仅仅只是用前辈写好的读写函数去做事。自己并不知道到那一层函数后面到底是怎么进行操作的,本篇先不讨论数据的读写,仅说明RT-Thread是如何“认识”i2c的,在项目中通常都是用下面的函数去获取i2c对象,如下所示:

static i2c_dev_t* device =  sdrv_i2c_init(BUS_NAME, freq);

        这里经常会让人感到好奇,今天终于是借着sdrv_i2c_init这个契机踏入RT-Thread的大门了。

进入sdrv_i2c_init函数之后,我看到的是这样的代码:

// @brief 初始化
i2c_dev_t* sdrv_i2c_init(const char* p_name, uint32_t freq)
{
    int32_t ret;
    rt_device_t i2c_bus;
    i2c_dev_t* p_i2c_dev = NULL;
    
    ASSERT(p_name);
    i2c_bus = rt_device_find(p_name);
    ASSERT(i2c_bus != NULL);
    ret = rt_device_open(i2c_bus, RT_DEVICE_FLAG_RDWR);
    ASSERT(ret == RT_EOK);
    
    p_i2c_dev = (struct rt_i2c_bus_device*)i2c_bus;
    struct rt_i2c_configuration i2c_config;
    i2c_config.mode = 0;
    i2c_config.addr = 0;
    i2c_config.timeout = 100;
    i2c_config.max_hz = freq;
    ret = rt_i2c_configure(p_i2c_dev, &i2c_config);
    ASSERT(ret == RT_EOK);
    return p_i2c_dev;
}

        很显然,这段代码我最想要的是 i2c_bus = rt_device_find(p_name)这句,因为其他部分基本都是对rt_device_find的结果进行一些配置(初始化),所以i2c_bus的“真身”还在rt_device_find里面。

在rt_device_find中试一下这段代码:

/**
 * 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.
 */
__ROM_USED rt_device_t rt_device_find(const char *name)
{
    struct rt_object *object;
    struct rt_list_node *node;
    struct rt_object_information *information;

    /* enter critical */
    if (rt_thread_self() != RT_NULL)
        rt_enter_critical();

    /* try to find device object */
    information = rt_object_get_information(RT_Object_Class_Device);
    RT_ASSERT(information != RT_NULL);
    for (node  = information->object_list.next;
            node != &(information->object_list);
            node  = node->next)
    {
        object = rt_list_entry(node, struct rt_object, list);
        if (rt_strncmp(object->name, name, RT_NAME_MAX) == 0)
        {
            /* leave critical */
            if (rt_thread_self() != RT_NULL)
                rt_exit_critical();

            return (rt_device_t)object;
        }
    }

    /* leave critical */
    if (rt_thread_self() != RT_NULL)
        rt_exit_critical();

    /* not found */
    return RT_NULL;
}
RTM_EXPORT(rt_device_find);

        哈哈哈哈哈哈,突然感觉自己像在堆代码。但是没办法,咱所有的头绪都是从代码出发的呀。

        那么在rt_device_find中是怎么获得这个i2c_bus对象的呢,接下来我们可以好好分析下这里面的代码了。

首先,是一个条件判断

if (rt_thread_self() != RT_NULL)
    rt_enter_critical();

rt_thread_self() 用于获取当前正在运行的线程对象指针,

  • 如果 在线程上下文中调用,它会返回当前线程对象;

  • 如果 在中断上下文或尚未启动调度器时调用,返回的是 RT_NULL

        在这里,它返回的肯定不是空(RT_NULL),所以执行rt_enter_critical()方法,rt_enter_critical():通常用于关闭中断,防止中断服务或其他线程打断当前正在执行的重要操作(比如链表修改、对象注册等)。

所以这只是为我们开始真正的操作做一个保护措施。

接着,我们来到了

information = rt_object_get_information(RT_Object_Class_Device);

        在调用这个方法之前我们需要知道一个东西,这也是我多方查询才得知的。在RT-Thread中,任何对象(object)都有一个类型,而每种类型的对象都根据其类型被放在一个链表里进行统一管理,而这里RT_Object_Class_Device就是其中的一个类型,这里是“设备”类型,当然还有其他类型,在rtdef.h文件中我们可以看到一个枚举

enum rt_object_class_type
{
    RT_Object_Class_Null   = 0,                         /**< The object is not used. */
    RT_Object_Class_Thread,                             /**< The object is a thread. */
    RT_Object_Class_Semaphore,                          /**< The object is a semaphore. */
    RT_Object_Class_Mutex,                              /**< The object is a mutex. */
    RT_Object_Class_Event,                              /**< The object is a event. */
    RT_Object_Class_MailBox,                            /**< The object is a mail box. */
    RT_Object_Class_MessageQueue,                       /**< The object is a message queue. */
    RT_Object_Class_MemHeap,                            /**< The object is a memory heap */
    RT_Object_Class_MemPool,                            /**< The object is a memory pool. */
    RT_Object_Class_Device,                             /**< The object is a device */
    RT_Object_Class_Timer,                              /**< The object is a timer. */
    RT_Object_Class_Module,                             /**< The object is a module. */
    RT_Object_Class_Unknown,                            /**< The object is unknown. */
    RT_Object_Class_Static = 0x80                       /**< The object is a static object. */
};

这里面就是RT-Thread中所有类型的定义了。

        回到函数中,rt_object_get_information是如何根据一个类型就找到了我们所需要的i2c_bus的呢,我们接着看rt_object_get_information中的代码:

rt_object_get_information(enum rt_object_class_type type)
{
    int index;

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

    return RT_NULL;
}

        很简单,他就是做一个数组遍历操作,通过遍历数组中每个元素的类型,来判断,是否是属于“设备”(我们rt_device_find中传给的rt_object_get_information方法的参数是RT_Object_Class_Device),当类型对应上了就返回对应的数组元素。那么这个所谓的数组元素是什么东西呢。

        我们点进rt_object_container数组,发现该数组是一个struct rt_object_information的结构体类型,这个结构体是这样的:

/**
 * The information of the kernel object
 */
struct rt_object_information
{
    enum rt_object_class_type type;                     /**< object class type */
    rt_list_t                 object_list;              /**< object list */
    rt_size_t                 object_size;              /**< object size */
};

这个数组内容是这样的:

#define _OBJ_CONTAINER_LIST_INIT(c)     \
    {&(rt_object_container[c].object_list), &(rt_object_container[c].object_list)}
static struct rt_object_information rt_object_container[RT_Object_Info_Unknown] =
{
    /* initialize object container - thread */
    {RT_Object_Class_Thread, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Thread), sizeof(struct rt_thread)},
#ifdef RT_USING_SEMAPHORE
    /* initialize object container - semaphore */
    {RT_Object_Class_Semaphore, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Semaphore), sizeof(struct rt_semaphore)},
#endif
#ifdef RT_USING_MUTEX
    /* initialize object container - mutex */
    {RT_Object_Class_Mutex, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Mutex), sizeof(struct rt_mutex)},
#endif
#ifdef RT_USING_EVENT
    /* initialize object container - event */
    {RT_Object_Class_Event, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Event), sizeof(struct rt_event)},
#endif
#ifdef RT_USING_MAILBOX
    /* initialize object container - mailbox */
    {RT_Object_Class_MailBox, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_MailBox), sizeof(struct rt_mailbox)},
#endif
#ifdef RT_USING_MESSAGEQUEUE
    /* initialize object container - message queue */
    {RT_Object_Class_MessageQueue, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_MessageQueue), sizeof(struct rt_messagequeue)},
#endif
#ifdef RT_USING_MEMHEAP
    /* initialize object container - memory heap */
    {RT_Object_Class_MemHeap, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_MemHeap), sizeof(struct rt_memheap)},
#endif
#ifdef RT_USING_MEMPOOL
    /* initialize object container - memory pool */
    {RT_Object_Class_MemPool, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_MemPool), sizeof(struct rt_mempool)},
#endif
#ifdef RT_USING_DEVICE
    /* initialize object container - device */
    {RT_Object_Class_Device, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Device), sizeof(struct rt_device)},
#endif
    /* initialize object container - timer */
    {RT_Object_Class_Timer, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Timer), sizeof(struct rt_timer)},
#ifdef RT_USING_MODULE
    /* initialize object container - module */
    {RT_Object_Class_Module, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Module), sizeof(struct rt_dlmodule)},
#endif

};

        所以这里我们返回的是类型为“设备”的数组元素,该元素有三个子元素,分别为{类型,列表,列表长度}。

        那么回到rt_device_find,我们的information得到的就是一个这样的数组元素,随后,再次对这个数组元素中的列表元素进行了遍历,如下:

for (node  = information->object_list.next;
            node != &(information->object_list);
            node  = node->next)
    {
        object = rt_list_entry(node, struct rt_object, list);
        if (rt_strncmp(object->name, name, RT_NAME_MAX) == 0)
        {
            /* leave critical */
            if (rt_thread_self() != RT_NULL)
                rt_exit_critical();

            return (rt_device_t)object;
        }
    }

        在这段遍历里面,遍历的information->object_list就是RT-Thread管理的所有RT_Object_Class_Device(设备)类型的对象了,这里通过rt_strncmp把我们传进来的设备name与系统中的name进行对比,最终就可以找到我们需要的对应的设备了。

.........................................

然而事情到这里就结束了吗?不好奇系统中的name设备是怎么被弄到系统中去的吗?

        这部分就交给各位自己去探索吧,这里稍微提供一下本人在探索过程中问过ChatGPT的简要回答:

完结,撒花!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值