本篇核心讲解的2个图,如下所示。图1是官网文档中的设备各类关系图,图2是IO设备框架图,它们两个都取自rtt官方文档
图1 rtt设备各类关系图
图2 rtt的IO设备框架图
概述
本篇以面向对象的角度来解读rtt的IO设备框架。
先澄清下概念,这些概念是进行新的定义或者表达名词在本文中的意思,防止有二义。然后介绍各类关系,IO设备框架对应的类。再接着用伪代码举出了面向对象语言的对象创建与初始化,然后开始解读rtt的c的面向对象的对象创建及其启动初始化流程,最后总结面向对象语言与c面向对象的区别。
目的:
(1)以面向对象的角度表达自己对rtt设备框架的理解
(2)给框图找朋友。这个找朋友就像划线一样,记录下自己的牵线过程——把图1与图2的层次进行对应、划线,然后再给它们找对应的源代码——每个层次每类大致对应的c和h文件。不至于说看了这个不知道各层对应的c和h文件在哪。
(3)是我对官方IO设备框架文档的阅读笔记。——把抽象的rtt设备框架变成具体的——怎么具体?把IO框架层次对应到各c文件,使得官方设备框架文档不再那么抽象。
1 概念澄清
子类设备类 :设备基类派生出的子类。比如上面图1最下层的串口设备类、PIN设备类、SPI设备类、I2C总线设备类等类。
子类设备对象 :由子类设备类创建的对象。比如图1最下层的串口设备类等类创建的对象。
多态 :不同子类(同一父类派生出的各子类)调用同一父类方法时的表现不一样是为多态。——多态出现的根因在于,子类可以覆盖父类的方法或者拓展父类的方法,从而不同子类调用同一父类的方法时表现不同。在此重点列出
2 rtt的设备各类关系图与IO设备框架的对应
2.1 rtt的设备各类关系图
如图1,从上往下看依次是基类、设备基类、子类设备类。
它们的关系,
从上往下看是派生的关系;
从下往上看,是继承关系。——这是面向对象的思想。
rtt基类,管理接口在object.c
rtt设备基类,管理接口在device.c
rtt子类设备类。
2.2 IO设备框架分层
图3 IO设备框架分层
如图3,官方的设备框架层从上往下共5层——第0-第4层。
最上层第4层应用app。
第3层设备层 ,管理接口在device.c、object.c。
第2层设备驱动框架层 ,比如例如serial.c的rt_hw_serial_register等。
第1层设备驱动层,实际上可以叫设备框架对接层
对接rtt的设备框架——需要客户以芯片厂家sdk(比如stm32的hal库)为基础写出对接接口,实质就是创建子类设备对象以及调用设备框架的启动入口——注册子类设备对象的函数(比如rt_hw_serial_register)。
另外,这层有些芯片平台的rtt已写好,比如stm32。
第0层 .硬件层。比如stm32芯片。
其他说明:
(1)第2-3层是rtt的IO设备对象框架层——rtt官方已写好,只需写好第1层。
(2)第1-3层实现了rtt对象的创建与初始化等面向对象语言创建对象的机制。
(3)rtt已经分好类并创建好类了,我们要做的事,只是拿rtt创建好的类来创建对象并启动对象初始化流程——这就是第1层要干的事。
2.2 rtt的设备各类关系图与IO设备框架的对应
如下图:
图4 rtt的设备各类关系图与IO设备框架的对应
自己连线如如图4所示:
基类、设备基类对应IO设备框架的第3层I/O设备管理层(device.c、object.c)。
rtt子类设备类对应IO设备框架的第1层和第2层(比如drv_usart.c、serial.c等)。
这样的话,把各层对应的c文件找出来从抽象来到具体。
3 面向对象语言创建对象
既然以面向对象的思想看rtt那么就说下面向对象的一些东西。
不管是面向对象语言还是c面向对象,其对象创建很相似,分两步:第一步,创建类,第二步创建对象并初始化对象。只是两者形式不同。
3.1面向对象的创建对象伪代码
3.1.1创建类
(1)创建类ClassA,继承于基类object。伪代码如下:
class ClassA(object):
def 属性
def 方法
(2)创建类ClassB,继承于ClassA,伪代码如下:
class ClassB(ClassA):
def 属性
def 方法
3.1.2 创建对象
创建2个对象:
objectA = ClassA(a, b, c);
objectB = ClassB(a, b, c);
面向对象语言的创建对象、继承父类,写代码很简单,而c面向对象则需要把这一套机制给实现。
4 rtt的对象创建与初始化过程
c面向对象,创建对象需要解决的问题:
(1)怎么初始化对象的属性和方法?
(2)怎么继承父类的属性和方法?
这个问题面向对象语言不需要程序员自己解决,面向对象语言它自身有这个机制,而因为c面向对象方法的实现在外部,所以必须得把这一套机制实现下——实现对对象的初始化、继承等管理接口——以解决上面问题。
而rtt做了一套对象初始化的接口来实现了面向对象语言创建对象的那套机制——先创建对象,然后从低层到高层,实现了对对象方法、属性、父类方法和父类属性继承等的初始化。
4.1 rtt创建类
如图1所示,大多数类,rtt已创建完成,我们所做的是拿rtt创建好的类去创建所需的对象得了——当然你也可以再派生出新类。
这些类在哪创建的呢?基类、设备基类、定时器类、线程类、ipc基类、ipc各子类(信号量类、互斥量类、邮箱类、消息队列类等)等类在rtdef.h中定义。
rtt的整体类关系图,如图5,取自官方文档内核基础:
图5 官方整体的大类关系图
而本篇解读的只是图5中设备类这一分支,但以点攻面,其他类似。
然后接着说图1的子类设备类在哪定义的?比如sensor设备类、PIN设备类分别在sensor.h、pin.h中定义的,其他子类设备同理。
这就是rtt已经写好的类——当然可以根据需求派生出新类。
4.2 rtt创建对象并初始化
以IO设备框架图为参照,从第1层为开始、为起点的,直至第3层为结束——是从底层到高层的过程——其初始化流程如下:
第1层 ,起点层,IO框架图中的rtt设备驱动层,我想了下也可叫rtt设备框架对接层。此层是拿rtt创建好的类来创建子类设备对象或者自己派生出新类创建子类设备类子类的对象,调用子类设备对象注册函数传递驱动的属性和方法到上一层——第2层,从而启动对象的初始化流程——所以说此层是子类设备对象初始化的推送者也是起点与开端。
此层的工作量在于编写子类设备对象的属性和方法。——这就需要编写硬件的能力——硬件驱动了。
第2层 ,设备驱动框架层,对应到图1类图的类是子类设备类,所做的事:
(1)初始化子类设备对象的属性和方法
(2)初始化子类设备对象的父类(设备基类)的属性和方法——其中,对父类方法的“继承”是通过函数指针重新映射的——这也是多态的体现(子类调用同一父类的方法表现不同)。
第3层 ,IO设备管理层,对应到图1类图的类是设备基类,这层按照顺序做了2件事:
(1)对子类设备的父类(设备基类)的属性进行初始化,并调用基类对象注册函数。
(2)初始化子类设备对象的父类的父类对象——即基类对象——并放到内核对象容器对应类型的链表中进行管理。
小结 :rtt是以IO框架图的第1层为起点,创建对象并启动对象初始化,然后第2-3层是实现对象的初始化,同时对应到类图,可以参看图1和图2,或者图4.
5 rtt的c面向对象与面向对象语言总结
5.1 rtt的c面向对象的体现
第1-3层是rtt子类设备对象初始化过程——比如串口设备对象初始化过程,初始化过程是从低层到高层的过程——告诉上层底层硬件有什么能力,同时这一过程也体现了面向对象的三大特性:
(1)封装性:把属性和方法封装到各自的类中。 (2)继承的特性:子类继承了父类的属性与方法 (3)多态的特性:子类继承父类的方法是通过指针实现了对父类方法的重映射或者说修改,从而实现了多态的特性——子类调用同一父类的方法而表现不同。
5.2 c面向对象与面向面向对象语言的区别
c面向对象 : (1)不管子类还是父类的方法都是在外部实现的,通过指针“继承”过来。 (2)对父类方法的继承是对父类方法的重写:正是因为c面向对象的方法是在外部实现的,子类对父类方法的继承实质是通过指针指向不同的函数——不同子类给其父类链接的函数不同,所以对父类方法继承是对父类方法的重写,不存在对父类方法的拓展这一说。
面向对象的语言 : (1)子类、父类方法的实现都是封装在类的内部,在类的内部实现的。 (2)子类可以对父类的方法进行重写或者拓展以实现多态的特性——正是因为它们的方法是在类的内部实现而不是在外部实现的,所以子类可以对父类的方法进行重写或者拓展。
所以给我的感觉c面向对象和面向对象的语言最大不同是方法的实现不同,一个在类的外部实现,一个在类的内部实现,这就导致了c面向对象对父类方法的继承只能重写、不能拓展,而面向对象的语言除了重写父类方法的能力外,还比c面向对象要多出拓展父类方法这个能力。
原作者: yhb1206