最近一直研究阅读RT-Thread的设备模型,把一些感悟做一个笔记,也算是一个学习总结。
对于设备模型涉及到的思想、软件分层 继承 多态 等(可以去RT-Thread的官网文档中心查看,解释的很详细,教程实例很丰富),这里只是个人总结。
一,简单应用
少废话,先看怎么用。下面是发送数据简单演示代码。
/* 定义设备 */
rt_device_t uart1;
/* 定义数据 */
rt_uint8_t buff[] = "Hello RT-Thread\n";
/* 查找设备,并做返回值判断 */
uart1 = rt_device_find("uart1");
if(uart1 == RT_NULL)
{
while(1);
}
/* 打开找到的设备,一般的能找到设备说明在系统中存在 该设备,但是相关的硬件并没有初始化,
open操作就是实现了硬件初始,能先找到说明支持这个设备,再打开说明要用这个设备 */
rt_device_open(uart1, RT_DEVICE_FLAG_INT_RX);
/* 数据发送 */
rt_device_write(uart1, -1, &buff, sizeof(buff));
上电复位,就可以看到串口助手收到的数据了!
一开始感觉很神奇,这样写就能实现了数据的发送了,那么具体是怎么实现的呢?为啥通过这几个函数就能往串口里写数据呢?
原来RT-Thread在软件上将数据收发的过程进行了分层。
应用层 | 用户调用 | rt_device_find等接口 |
---|---|---|
设备驱动层 | 由RTT提供 | 用户不需要更改 |
硬件层 | 驱动人员编写 | 涉及具体硬件,如stm32 |
进行裸机编程的时候 ,我们对数据的收发通常是直接操作芯片的寄存器或者通过库函数调用最终操作寄存器,实际上是和硬件层打交道。应用操作系统之后,操作系统通过封装呈现给用户的是通用的调用接口。通过这样分层之后,就把应用编写和驱动编写的人员进行分割,各司其职,加快研发效率。
二,实现过程
从简单应用中我们直接就通过rt_device_find
函数找到了设备。能够找到的原因是在上电初始化的阶段对该设备进行了注册,通俗的说就是告诉操作系统我有这样一个设备啦,你可以用啦!这样就可以通过上层的通用函数调用,看串口驱动的注册过程。
rt_err_t rt_hw_serial_register(struct rt_serial_device *serial,
const char *name,
rt_uint32_t flag,
void *data)
{
rt_err_t ret;
struct rt_device *device;
/* 获取设备指针 */
device = &(serial->parent);
/* 设备类型为字符设备 */
device->type = RT_Device_Class_Char;
device->rx_indicate = RT_NULL;
device->tx_complete = RT_NULL;
/* 这里将串口特有的操作挂接到设备的方法上,这样上层调用open函数时就会调
用串口特有的操作方法这个在高级语言上叫做多态 */
#ifdef RT_USING_DEVICE_OPS
device->ops = &serial_ops;
#else
device->init = rt_serial_init;
device->open = rt_serial_open;
device->close = rt_serial_close;
device->read = rt_serial_read;
device->write = rt_serial_write;
device->control = rt_serial_control;
#endif
device->user_data = data;
/* 调用公用的设备注册接口 */
ret = rt_device_register(device, name, flag);
#if defined(RT_USING_POSIX)
/* set fops */
device->fops = &_serial_fops;
#endif
return ret;
}
这样注册之后,用户调用通用接口open
read
write
(在RT-Thread中叫做rt_device_open
rt_device_read
rt_device_write
)时,实际上就是调用的rt_serial_open
rt_serial_read
rt_serial_write
可以看出这里就实现了软件上的分层,将内核程序与用户程序分离。
那么Hello RT-Thread\n
这个字符串是如何发送的呢?
rt_size_t rt_device_write(rt_device_t dev,
rt_off_t pos,
const void *buffer,
rt_size_t size)
{
/* 调用对应的接口,在这里就是调用rt_serial_write函数 */
if (device_write != RT_NULL)
{
/* device_write 是个宏,见下文,调用注册的接口 */
return device_write(dev, pos, buffer, size);
}
return