本篇文章主要是记录下自己在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的简要回答:
完结,撒花!