嵌入式技术论坛
直播中

刘埃生

7年用户 1678经验值
私信 关注
[经验]

详解IO设备管理之父类调用子类方法的过程

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

更多回帖

发帖
×
20
完善资料,
赚取积分