1 IO设备管理层
1.1 基类
在基类rt_object的管理文件object.c中主要实现了基类对象初始化、反初始化、查找基类对象等接口。
object.c中定义了容器,它主要的工作是将各类对象的基类对象放到容器里进行统一的管理。
1.2 设备基类
在设备基类rt_device的管理文件device.c中实现了2类接口:一类是注册等接口,一类是访问自身方法的接口。
1.2.1 设备基类注册/反注册、创建/销毁、查找等接口
rt_device_create/rt_device_destroy
rt_device_register/rt_device_unregister
rt_device_find
主要讲下注册接口 rt_device_register,它 干了2个活:
(1)对上,把父类(基类rt_object)挂到rtt的对象容器中。
(2)对本类,初始化自己的属性。
1.2.2 访问设备基类方法的各接口
rt_device_init 访问设备基类的init方法
rt_device_open 访问设备基类的open方法
rt_device_read 访问设备基类的read方法
rt_device_write 访问设备基类的write方法
rt_device_close 访问设备基类的close方法
rt_device_control 访问设备基类的control方法
rt_device_set_rx_indicate 设置设备基类的rx_indicate方法
rt_device_set_tx_complete 设置设备基类的tx_complete方法
2.设备驱动框架层和设备驱动层
因为设备驱动框架层和设备驱动层的管理接口实现机制基本一致,所以放到一起说。
设备驱动框架层和设备驱动层的管理文件实现了以下几类管理接口:
(1)该类注册接口rt_xxx_register
(2)注册给父类的方法
(3)访问本类方法的接口
2.1 注册接口rt_xxx_register
设备驱动框架层的注册接口干了2个活:
(1)对上,把本类管理接口文件中实现的方法传递给父类方法接口上。
(2)对下,获取子类方法到自己的方法句柄上。
设备驱动层的注册接口是整个流程的起点,因此只干了1个活:对上,把本类管理接口文件中实现的方法传递给父类方法接口上。
设备驱动框架层的注册接口承上启下。
设备驱动层的注册接口是整个流程的起点,所以就没有再下面的方法了,活少了一个,轻松不少——但是实际上这一层是新的BSP要实现的,上面两层rt-thread该实现的已经实现了。
值得关注的是:
设备驱动层的注册接口一旦调用就实现了对象创建及初始化流程——该机制映射到面向对象思想看,就是实现了OOPL(面向对象语言)的创建新对象的机制。
这些注册接口是这个IO设备模型框架的骨架!看这个注册接口就能顺藤摸瓜,知道这个模型的脉络图。
2.2 注册给父类的方法
设备驱动框架层和设备驱动层都实现了注册给父类的方法。这些方法是IO设备模型框架的血肉!
2.2.1 先看对象创建流程
调用设备驱动层的注册接口,就实现了创建对象,在创建对象的这个流程中,可少不了这些方法。你看这过程:从下往上信息传递,把方法传递上去。先是设备驱动层的这些方法传递给上一层——设备驱动框架层。设备驱动框架层又将本层的这些方法再传递到更上一层——IO设备管理层——到了顶层了,在这顶层把基类对象放到对象容器中,对象创建和初始化流程结束。
——这个注册过程映射到面向对象中,是继承父类方法后子类改写父类方法的过程。
2.2.2 再看对象使用
从上往下调用,参见第3节,这里不详细描述了。只是说这些方法在这一过程会被调用,和创建流程相反的方向。
2.3 访问本类方法的接口
2.2 节虽然是注册给父类的方法,但同样可以访问本类的方法。一般2.3节和2.2节是指的是同一个接口。但极个别的子类还是单独实现了这些额外的接口——重复造轮子了,搞不懂。
3 父类调用子类的调用链举例
这套框架最终成果是实现面向对象的创建对象和使用对象的机制。
3.1 各类对象调用链抽象流程
比如创建了一个设备类对象dev(设备驱动层对接自动创建),如何使用?先是调用设备基类管理接口文件device.c中的rt_device_find根据名字找到对象dev的地址。然后调用device.c文件中的访问设备基类方法的接口。比如调用rt_device_init(dev)就是访问dev的init方法,调用链如下:
第1级:调用rt_device_init(dev)即dev->init。而dev->init又是子类即设备驱动框架层管理文件xxx.c里传递过来的rt_xxx_init方法,比如rt_serial_init/rt_can_init/rt_spi_init等。
第2级:dev->ops->init。解释:设备驱动框架层的rt_xxx_init方法调用的是设备驱动层管理文件drv_xxx.c里传递过来的方法,比如stm32_configure等。
第3级:调用sdk接口。解释:设备驱动层各类管理文件实现的注册给父类的方法,其实现是调用对应平台SDK接口的。
第4级:操作硬件。如果不涉及硬件就没有第4级了。
综上所述,调用rt_device_init(dev)发生的调用链,干净一点的形式是:
rt_device_init
——>dev->init
——>dev->ops->init
——>sdk接口
——>硬件
调用device.c的其他访问方法基本上是一样的调用链。
3.2 具体实例
3.1节是针对所有设备对象调用链的一个模板,有点抽象。那么举个具体的例子,对照上面的抽象调用链就好理解了。
以stm32串口为例,假设stm32串口设备注册的名字为"uart1"。
先找到该设备地址:
dev=rt_device_find(“uart1”)
调用rt_device_init(dev)开启访问流程,调用链如下:
rt_device_init(dev)
——> rt_serial_init
——> stm32_configure
——> HAL_UART_Init
——> 操作硬件(操作USART寄存器)
stm32串口设备类分析有道笔记
其他方法的调用链类似。
4 总结
可以看到,设备基类rt_device比较特殊,和其他子类设备不同的是它的父类是基类,基类没有方法。所以设备基类的管理接口device.c中不需要实现父类方法。
设备基类rt_device没有白叫“设备基类”,其管理接口文件实现的方法是访问一切设备子类的调用者,自上而下的起点——也是父类怎么调用子类的入口点。——所以它就是各设备类的老大、部门老大、顶层了。
IO设备管理层管理文件实现的访问自身的各接口(init/open等)、设备驱动框架层管理文件实现的注册给IO设备管理层的各方法里面的实现都是调用本类的方法,但是本类的方法都是子类的管理文件传递过来的方法。也就是你调用本层的方法就是调用下层的方法,调用下层的方法就是调用下下层的方法,下下层的方法调用SDK接口实现的。如此,一共3层。
原作者:yunhuibin