模仿RT_Thread的设备驱动模型,使用C++实现

电子说

1.3w人已加入

描述

现状

由于目前本人就职的公司不允许自由使用操作系统开发,且公司一般为C/C++混合开发的模式,驱动的开发也十分的草率,驱动代码与应用代码杂糅在一起,十分的不清晰。个人又十分喜爱RT_Thread这个系统,也翻阅过一些内核源码,学习到了非常多的有用的开发技能。

于是我想到,为何不能自己建立一套驱动管理机制来进行统一?于是萌发了使用C++来构建RT_Thread的设备驱动框架的想法。当然,我并没有做到完全一致,而是加入了自己的一些想法,并且也是基于了目前的开发现状来进行了一定的修改(不允许贴源码只能截图哈)。

实现过程说明

RT_Thread的内核对象分为很多种类别,线程、信号量、互斥量、设备、定时器…由于借鉴了RT_Thread的源码,故进行了删减,只保留设备部分,且去除了对象类型和动态申请内存使用的size变量(RT_Thread的源码这一部分代码就不贴了),如图:

SPI接口

内核对象类:

SPI接口

RT_Thread是把rt_object这个结构体作为双向链表的元素,这里的话是把CObject这个类作为双向链表的对象(C++的类其实和结构体是一样的)。

对象的操作只保留了object_find、object_init、object_detach,修改了register和unregister的操作。type、name、usr_data保留,其中type(设备的类型)根据实际应用做了一些修改(部分定义):

SPI接口

链表的插入删除操作原封不动“抄”了RT_Thread的源码,如图:

SPI接口

新增驱动的控制与循环

因为删除了open和close,且考虑到有些驱动需要调用循环实现一定的功能,故新增一个驱动控制类CObjCtrl:

SPI接口

该类非常简单,如果驱动未使能则循环退出,且循环的主体(ObjProcess())需要该类被继承后进行虚函数的重写。

新建注册类(基本抽象类)

注册类的作用主要就是继承了CObjCtrl,且替代RT_Thread的register函数和unregister函数,将对象接入双向链表。该类的实现如下:

SPI接口

需要注意的是,不在需要手动调用register和unregister这两个注册卸载函数了,因为在驱动类构造函数执行的时候回自动调用(见构造函数和析构函数)。GetType()和GetName()函数比较常规。构造函数需要传入驱动的类型、名字和私有数据(私有数据的作用下面会有说明)。

因为继承了CObjCtrl类,故需要重写虚函数ObjProcess(),重写的ObjProcess()函数由调用了本类的AbsLayerProcess()纯虚函数,故本注册类依然需要被继承后重写该纯虚函数AbsLayerProcess()。

驱动抽象类

该类主要就是继承了上述注册类,且存在常规的read、write、control接口(虚函数)。

RT_Thread的驱动初始化是在open函数调用的时候被调用的,我去除了open和close,所以驱动的初始化放在find函数内部,通过变量b_IsInitOK来记录是否已经进行过初始化,也可以手动调用函数DriverInitial()来进行驱动的初始化。查看构造函数可以发现,私有数据记录了本驱动抽象类的地址(因为传入了this指针)。

又重写了注册类的AbsLayerProcess()函数,该函数调用的真正需要驱动实现的循环函数DriverProcess()。

SPI接口

至此,设备驱动框架部分代码全部开发完毕。

使用示例

以flash芯片AT45DB为例:

SPI接口

需要实现驱动的read、write、control、init函数(因为该驱动不需要循环,所以不需要重写DriverProcess()循环函数)。构造函数传入SPI相关信息(自己实现的代码,不深究)和驱动类型及名字。

实例化驱动类,自动将“对象”注册至链表内:

SPI接口

使用如下:

SPI接口

至此,基本的设备驱动框架已经形成,当然后面又做了一定的拓展和优化,这里不再赘述。RT_Thread实在是一个宝藏系统!

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分