“一点五编程”–以函数指针override重写 以结构体作为传参 实现多态
将应用逻辑层和硬件层通过构建接口层来实现隔离 使代码不拘于硬件、平台,只面向功能,以获得代码的复用性、移植性 且增加了代码的易读性
io_led.light_i.open(struct led* self,int flags) 由于self结构体内参数不同 实现多态效果
面向对象
将任务经过面向对象进行抽象,比如将led灯、pwm灯、rgb灯抽象为 “灯”
在"灯"中有着统一的硬件层接口 如开灯open 关灯close.应用逻辑层上 无论是点亮led灯还是pwm灯 统一调用"灯"接口中的open函数
open函数会根据传参的不同来override重写接口函数 得到C++中多态的效果
函数指针
typedef void (*p)(int args);
typedef int (*painter_draw_point_fn_t)(void *self, struct point pos, int color);
通过typedef定义一个函数指针,该函数指针指向的是某一个抽象出来的功能,如open开灯 close关灯
结构体
struct p_i
{
fn_t
}
struct painter_i {
painter_draw_point_fn_t draw_point;
};
自顶向下 丢掉嵌入式开发自底向上的刻板思维 或者理解为当前功能已经实现,只需设计应用逻辑
先设计应用层 一路往下设计 直到驱动层
比如我们项目要求是led闪烁 pwm灯随着每一次led闪烁渐明渐暗以实现呼吸灯
可以先写出应用层逻辑
io_led_open();
pwm_led_open();
delay(100);
io_led_close();
pwm_led_close();
delay(100);
上面即为应用层逻辑代码 以往的嵌入式开发思维 会认为这是两个不同的模块 其根据是通过硬件功能实现决定的
抛弃刻板思维 通过功能属性来进行模块化编程! 将io_led和pwm_led根据底层功能划分为同一个模块–“灯”
那么如何设计"灯"的接口呢 秘诀在于–函数指针和结构体
通过函数指针定义接口函数
在灯的设计中 要用到的功能有开灯(led_open) 关灯(led_close)
所以typedef函数指针
typedef void (*led_open_fn_t)(void *self, struct point pos, int color);
模块化编程
传统的嵌入式开发流程:只对功能划分 如led灯 pwm灯 oled显示 超声波模块 蓝牙模块等进行分文件分模块的设计
一点五编程:不仅对功能进行划分 同时对于具有相同抽象属性的功能进行接口设计
在应用层设计中没有iic等硬件接口的概念 只关注对象接口例如灯、传感器、显示器
如灯类功能(核心操作为开灯、关灯) 显示类功能(核心功能为打点函数)
虽然io_led和pwm_led实际驱动的方式不同 但可以通过设计相同的软件接口 通过override重写接口函数来适配硬件接口
以实现软件层面的设计统一 代码更能体现功能的特性 以及更好的可读性和修改性
通过以功能属性抽象划分的接口设计 在软件代码逻辑不变 硬件改变的情况下 只需要修改接口函数中的硬件接口实现即可
如果在当前代码基础上增加一些具有相同功能 但硬件接口的不同的模块时 比如iic_led通过iic来发送数据以控制led亮灭的功能 也能快速适配到代码
流程
接口实现(灯为例)
typedef int (*light_open_fn_t)(void *self);
typedef int (*light_close_fn_t)(void *self);
struct light_i {
light_open_fn_t light_open;