1. 背景
GDK8和幽兰中的刘姥姥驱动程序是一个经典的小例子。第一个版本写在十年前,没有硬件依赖,2021年,移植到GDK8时,增加了一些针对GDK8硬件的功能,比如观察系统寄存器,读芯片温度等。今年移植到幽兰代码本时,再次进行改进,增加了对RK3588芯片的支持。
2. 条件编译方法
在从GDK8移植到幽兰时,为了处理硬件差异,引入了编译宏,通过宏来隔离两套代码。
宏具有许多优点,比如代码复用和模块化:宏允许你将一段重复使用的代码片段抽象为一个宏,并在多个地方进行调用,从而避免了代码的重复书写,并提高了代码的复用性和模块化程度。简化代码:使用宏可以减少冗长或复杂的表达式,在编码过程中能够以更简洁和易读的方式来描述逻辑和操作,等等。同时也有许多缺点,比如难以调试和跟踪:由于宏在编译时被展开,它们往往不具备标准函数的调试能力。在调试过程中,很难直接跟踪宏内部的执行流程和变量值。错误定位困难:宏展开后代码的行号可能与原始代码不一致,当出现编译错误时,很难根据错误信息快速定位到具体的源代码位置。
具体来说,下面是使用预定义宏方式的代码。通过判断该宏是否定义,条件选择定义哪一种硬件所需要的宏。
从上面的代码可以看出来,我们只能在编译时通过宏来选择一套代码。如果在使用驱动时定义错了宏,那么在不同硬件中会就有严重的错误,因为访问无效的外设地址而导致整个系统挂死。
同时,宏的方式也非常不灵活,可拓展性很低,每添加一种硬件,就需要添加一种硬件的宏定义,同时执行函数中也需要添加一种宏定义,导致代码冗长累赘。
3. 动态检查方法
针对上述问题,我们对症下药,设计了一种在运行期动态检查硬件,选择代码逻辑的方法。下面是这种方法的细节。
首先,定义了一个结构体,归纳硬件的特征,如下:
它是所有硬件的通用体(成员可以添加),可以赋予不同的值代表不同的硬件。
接下来,定义一个全局变量,实例化上述结构体。在模块进行初始化时,检查硬件,给这个去全局变量赋予合适的值。
之后进行各种操作时,只需将它传入即可。用它的成员变量值去计算,即可完成所有操作,又免去了多个if,else的条件编译及判断。
这是选择动态选择方法改进后的代码:
从中我们可以看出来执行函数完全不用在进行选择了,只需应用传入的结构体变量成员即可,不仅方便函数调用,而且减少了代码量,对于以后更多的硬件,也只需在初始化的时候多加一种硬件即可,可拓展性极强。
4. 总结
最后我们看看刘姥姥模块在不同硬件中的执行效果。
a. 在ULAN版本中,使用刘姥姥模块执行温度函数。效果如下:
b. 在ULAN版本中,使用刘姥姥模块执行温度函数。效果如下:
可以看到执行模块初始化的时候也会打印出当前的版本,同时它也可以流畅的进行温度函数执行。
经过改进,在代码数量和可拓展性两方面都是优于条件编译方法的。
回过头看,虽然两种方法都可以解决硬件差异的问题,但是我们对于代码质量,应该要精益求精。一种问题总是可以找到多种解决方法,我们应该选择最优,最简洁明了的方法。
苏轼曾说:横看成岭侧成峰,远近高低各不同。这两句诗告诉我们,从不同的角度去看事和解决问题,会有不同的结果!处理硬件差异是软件工程中的一个典型任务,动态监测的方法具有更好的动态适应性和可维护性。
最后编辑:zuoyingying 更新时间:2024-12-20 17:01