前言
阅读rtt官网文档的IO设备模型框架图后,有个疑惑:它具体指的是什么呢?如何对应到代码中去?这始终是一个困惑——让我感觉空落落的,悬在半空中。——近日,为了自我解惑,参照rtt源码和官方的IO设备模型框架图绘制了另一个图来解释和剖析官方的IO设备模型框图,姑且称之为IO设备模型框架补充图——端详良久,比较满意,终于自我解惑了。于是分享出来,希望给更多的人解惑。——对照该图,再看官方IO设备模型框图就能看懂了——感觉终于落地了,不再悬在半空中了——就像装了个望远镜,转了下按钮从而加深了景深。
1.官方IO设备模型框架图
2.我绘的IO设备模型框架补充图
惊喜,该图已更新到rt thread官网文档《IO设备模型》中了。
该图可以作为官方I/O设备模型框架图的补充图,两图对照看,就能很好理解官方的IO设备模型框架了。
2.1 补充图绘图要点
(1)类图结构。
(2)类图里.c文件说明。
各类图里的c文件是该类的管理接口所在的c文件。
(a)IO设备管理层和设备驱动框架层各类图中的c文件名没有带路径,因为是rtt自己写好的,就没写路径了。写路径的代表是设备驱动层——因为设备驱动层可以交给各BSP开发者来开发。
补充下:
IO设备管理层各类图中的c文件是在rtt源码根目录/src下。
设备驱动框架层各类图中的c文件在rtt源码根目录/components/drivers下找寻。
(b)设备驱动层的各类图中的c文件路径和名字只是个示意而已,不要当真——只是说“有”这个文件。——具体名字和路径由各BSP开发维护者自己定的。比如stm32串口设备类图中画的drv_usart.c所在路径是bsp/stm32/drv_usart.c,但实际路径(当前)是在rtt源码下的bsp/stm32/libraries/HAL_Drivers/drv_usart.c。比如有的bsp下起的名字不叫drv_usart.c而叫drv_uart.c等等。随着rtt源码的版本的变化,各bsp的驱动路径和名字可能会发生变化(我建议统一命名规则)。如果按照类图中的c文件路径找不到c文件的话,就不要奇怪了。
(2)类图中的“xxx”说明。
(a)设备驱动框架层各类图中的“xxx”说明。 比如“rt_xxx_device”中的“xxx”,是代表省略的rtt已经写好的各类,不再一一列出了——可以去rtt源码的components目录下找寻,比如components/drivers下有serial/i2c/spi/sensor/can等,比如drivers下的include下定义的各设备类。
(b)设备驱动层的类图里的“xxx”说明。 比如图中最后的类名叫“xxx xxx”,第一个“xxx”是代表省略的各BSP平台,不再一一列出了——可以去rtt源码的bsp目录下找寻,比如stm32/gd32/at32/avr32/k210等等。第二个“xxx”代表的是省略的硬件模块,比如CAN/SPI/I2C等等。
2.2 补充图解说官方IO设备模型框架图
该补充绘图,帮助你看懂并理解rtt官方的IO设备模型框架图:
(1)看补充图就能知道官方图的各层里面文字的背后代表了什么。
(2)各类继承和派生关系清楚明了。
(3)体现了面向接口编程 的思想。子类受到父类接口的约束,子类想办法各自实现父类提供的统一接口。这是属于接口继承 (以父类为角度,创建子类调用其父类方法),接口继承用于多态。
(4)体现了5大设计原则(SOLID)的单一职责原则(S)——请查阅相关博客 ——图中每层的每类只有一套管理接口。比如设备驱动层,碰到的第一个类是stm32串口设备类(stm32_uart),类图中写了该类管理接口在stm32_usart.c中,同理其他厂家比如gd32的串口设备类也类似。此外其他硬件模块比如CAN、ADC等设备也一样。以此类推,设备驱动框架层和IO设备管理层也一样,不再展开讲了。
(5)体现了5大设计原则(SOLID)的开放封闭原则(O)——请查阅相关博客 ——两种情况,第一种情况,新增全新的rtt没有写的类。只需在设备驱动框架层和设备驱动层这两层新增对应的全新的设备类型就行了——这是变化的地方,O原则是提取变化的部分成为扩展类。IO设备管理层的类及其管理方法永远不变——这是O原则不变的类,不用做任何修改。第二种情况,要支持rtt已拥有的设备类。比如有的BSP只支持串口设备类,现在我想让它支持CAN/SPI等设备类型,怎么办呢?——只需在设备驱动层新增该BSP对应的设备类即可。
总之,两种情形都只是扩展新类而不修改IO设备管理层的接口(方法),符合五大设计原则的开放封闭原则(开放扩展,封闭修改)。
(6)从下往上不断抽象、统一和屏蔽硬件差异。
(7)OOPC的思想贯穿其中,细细品味。体现面向对象的抽象、封装、继承、多态的特性。 是的,rtt整个内核源码都是用c语言写的,但采用面向对象的思想来构建整个程序框架的。面向对象用于宏观把控庞大的代码规模或者程序框架,适用于构建大型模型,面向过程则关注于细微,所以一般都是互相结合,集各家之长——为了更好的软件设计。
(8)该补充图,横向看能看到分层思想,纵向看能看到各类派生继承关系,从中可看面向对象c语言是如何实现的。
把设备驱动层的同一父类的类都统一对接到设备驱动框架层的父类接口上。比如不管是stm32的串口设备类、还是gd32的串口设备类或是其他芯片的串口设备类(见上图设备驱动层的相关串口类图),都对接到设备驱动框架层的串口设备类(见上图设备驱动框架层的类图rt_serial_device)——由不同厂商的相同硬件模块对接到同一个父类接口上,多对一,屏蔽了低层差异,体现了面向对象的抽象的威力。再往上,从设备驱动框架层到IO设备管理接口这一层,又是多对一,又是再一次的屏蔽差异,再一次的抽象,最后统一操作了。
(9)父类方法是在子类的管理接口里实现的。总体印象:下层实现上层的方法,上层调用下层的方法。 图中每层的每类都是有且只有一套管理接口,每套管理接口都会实现父类方法,这样父类约束并统一了子类的方法。
先从设备驱动层看。stm32串口设备类stm32_uart的父类是串口设备类rt_serial_device,而串口设备类有个方法:const struct rt_uart_ops *ops;这个方法约束了所有设备驱动层的串口设备。因此stm32串口设备类在drv_usart.c中实现了父类这些方法,然后注册给它(指针方式)。然后gd32等等的串口都要实现这些方法,这样就实现了多态。
从设备驱动框架层到IO设备管理层也是这样的。比如串口设备类rt_serial_device的父类是设备基类rt_device(rtdef.h定义),而设备基类里定义了如下方法:
#ifdef RT_USING_DEVICE_OPS
const struct rt_device_ops ops;
#else
/ common device interface */
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
那么就约束了它的子类,比如串口设备类rt_serial_device、CAN设备类rt_can_device等都要各自实现这些方法,所以不同子类的父类方法是不一样的——这又是多态了。
(10)父类(基类)如何调用子类对象的方法? 前面几条的分析基本都是从下往上的分析——都是从设备驱动层的rt_hw_xxx_register讲起,从下往上看是各子类对象的注册初始化流程(把父类方法一层一层的注册上去)——那么初始化完了,是如何使用的呢?这就要从上往下看了。最好结合实例来讲,后面给个链接,懒得再搬过来了。
父类(基类)是如何调用子类对象的方法的——从IO设备管理层看起,当设备注册到容器后,在各个地方都可以使用它了,在finsh线程中会调用IO设备管理层的管理接口device.c中的方法——init/open/read/write等(没错rtt也和linux一样——“一切皆文件”)。这些方法会调用注册时设备驱动框架层对应的init/open/read/write等方法(名字不重要表达意思),然后在调用设备驱动层对应的init/open/read/write等方法(名字不重要表达意思)。比较抽象,其实画个图就好理解了——就能理解何为接口继承了!这个后续再说。——不过有几年前写的一个串口设备类怎么被使用的实例分析——finsh线程分析——可以参考理解子类方法是如何被父类调用的。
tips:
(1)给BSP新增设备驱动对接到rtt框架时,BSP开发者所做的工作就只是开发驱动层就行了。上面2层(设备驱动框架层和IO设备管理层)rtt都写好了,无需改动,除非发现BUG,或者新增设备类型(但这块就应该是rtt官方来实现了)。
3.模型的思考
模型的概念之前的博客已写过,“模型是描述输入输出关系的东西”。rtt的IO设备模型也是模型,用模型的角度如何理解其输入输出呢?——输入一个设备类,输出该设备类的能力(属性和方法),用以控制。嗯,就这么理解!
原作者:yunhuibin