总结:
RT_thread有一套I/O设备模型框架,如下图:
在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 命令查看系统中所有的设备信息,包括设备名称、设备类型和设备被打开次数。比如: