我记得最开始和驱动打交道是做2015年6月份的时候,那时候燕姐让我移植一个串口驱动,那时候的我真的很蠢,只能说当时我计算机基础,软硬件基础实在是太差了,后面就没怎么接触驱动相关的东西,所以驱动对我来说还是具有神秘的吸引力的。毕竟它是很接近硬件的东西,而且做嵌入式开发,哪能不接触驱动呢?
刚好,2017年6月中旬,很高兴接手了一个把我们的deltaOS移植到arm上去的活儿。这里面就涉及到了驱动的开发,虽然我是做ta相关的工作,但由于驱动停留在ta的驻留层里,都是通过桥接的方式给os的,所以我现在也做了部分驱动。
我现在只做了些简单的驱动,包括串口驱动,GPIO驱动,以及定时器驱动。
完成这些驱动,我觉得确实我学会了一些驱动的移植。但是还是缺少一些大型驱动移植的经验,比如网卡驱动。但我也有很多疑惑,一部分是对原理的求知,一部分是觉得驱动移植是否太过简单?
好像就是在某种功能的寄存器基址上加上偏移,然后往该指定的寄存器上控制各个位的0或1即可,最后我们在主程序中,调用该驱动的初始化函数对该驱动初始化即可。
那么我是怎么移植驱动的呢?
移植驱动最不可缺少的是什么,是芯片手册,里面写明了寄存器基址和偏移,以及各个寄存器的作用,而驱动代码就是按照手册,写出来具有各个功能的函数。
而对某个寄存器的某一位,若置1,代表一个功能;若置0,代表另一个功能,这些都是硬件所决定的。
先说最简单的gpio驱动吧,gpio有个寄存器基址BASE_ADDR_GPIO,通过该寄存器基址+偏移,衍生出了GPIO_SWPORTA_DR,GPIO_SWPORTA_DDR,GPIO_SWPORTA_CTL等寄存器,这些寄存器都可以保存32位bit。这些都是从芯片手册上了解的。
好,现在就要写gpio驱动代码,确定gpio驱动功能主要有设置gpio方向和gpio数据,先在方向寄存器中设置好方向,然后往数据寄存器里面写入数据,就可以。。。
定时器timer也是类似的,在初始化timer的时候需要注意分频系数和每秒响应多少次中断的设置。
串口其实也差不多,但是串口更复杂的是一个相同的偏移可能代表不同功能的寄存器,这就需要区分开来。有的是当读该偏移时,代表一个寄存器;当写该偏移时,代表另一个寄存器;而当该偏移需要代表3个功能的寄存器时,则需要线控制寄存器LCR中的一位DLAB来区分开来。