为什么要实现接口

接口,可以是硬件与硬件之间连接的桥梁,可以是软件与软件之间沟通的窗户,也可以是硬件与软件之间通信的枢纽。其中硬件与硬件之间的桥梁,比如I2C、UART、CANFD、等等,软件与软件之间沟通的窗户,比如操作系统提供的各种API、等等。我实现的这套“接口”,连接了硬件与软件,它可以实现软硬件的完全解耦合,引入了平台无关性,提供了尽可能简化的逻辑,这有点类似于开汽车,一个人一旦学会了开汽车,那开什么牌子的汽车都基本是一个道理,实现了一套驱动,一套接口,对于不同系统、硬件平台的支持,这无疑会大大提高代码的易用性、复用性,大大减少重复的造轮子。

我所实现的接口

  • 首先,需要一个结构体来存放系统接口,不妨叫system_interface_t,它用来存放系统的函数实现,是我们将驱动对接到不同平台的关键
1
2
3
4
5
6
7
8
9
typedef struct {
generic_err_t (*send)(uint8_t *data, size_t len);

generic_err_t (*receive)(uint8_t *data, size_t len);

generic_err_t (*delay_ms)(uint32_t nms);

generic_err_t (*delay_us)(uint32_t nus);
} system_interface_t;

推荐将上述代码放在一个.h文件里,这样不同的驱动只需要这一套接口即可

  • 接下来,需要一个结构体来存放system_interface_t(这样做的目的是实现不同驱动可以复用同一套接口),以及驱动内部实现的各种函数,比如对于aht20温度计而言,则除了对接系统的system_interface_t结构体,还应存放用于读取温度的函数
1
2
3
4
5
6
7
8
9
10
typedef struct {
float temperature;
float humidity;

system_interface_t *pfsystem_interface;

generic_err_t (*pfinit)(void *pfdev);

generic_err_t (*pfmeasure)(void *pfdev);
} generic_sensor_aht20_driver_interface_t;
  • 然后可以构建一个whoami函数,用于对底层驱动的对接写入,以及对设备调用函数的暴露
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
generic_err_t generic_sensor_aht20_whoami(generic_sensor_aht20_driver_interface_t *pfdev,
generic_err_t (*generic_send_func)(uint8_t *data, size_t len),
generic_err_t (*generic_receive_func)(uint8_t *data, size_t len),
generic_err_t (*delay_ms_func)(uint32_t nms),
generic_err_t (*delay_us_func)(uint32_t nus)) {
pfdev->pfsystem_interface = malloc(sizeof(system_interface_t));
pfdev->pfsystem_interface->send = generic_send_func;
pfdev->pfsystem_interface->receive = generic_receive_func;
pfdev->pfsystem_interface->delay_ms = delay_ms_func;
pfdev->pfsystem_interface->delay_us = delay_us_func;
pfdev->pfinit = generic_sensor_aht20_init;
pfdev->pfmeasure = generic_sensor_aht20_measure;

return GENERIC_OK;
}

注意这里要先给generic_sensor_aht20_driver_interface_t下的system_interface_t申请一块内存,否则直接调用pfdev->pfsystem_interface会指向非法内存,造成段错误

关于调用

可以在初始化的时候进行驱动程序的注册,不要忘记申请内存

1
generic_sensor_aht20_driver_interface_t *aht20_device = malloc(sizeof(generic_sensor_aht20_driver_interface_t));

然后调用whoami函数,对接系统底层到驱动层

1
2
3
4
5
generic_sensor_aht20_whoami(aht20_device,
i2c_master_send,
i2c_master_receive,
HAL_Delay,
NULL);

接着,便可以通过下面这种方式调用驱动暴露出来的API

1
aht20_device->pfinit(aht20_device); // 如pfinit(void *pfdev)

删除设备

我们前面通过malloc为设备的系统驱动以及设备暴露的接口分配了内存,下面通过释放这两块内存来删除设备

1
2
3
4
5
6
7
generic_err_t generic_sensor_aht20_del(generic_sensor_aht20_driver_interface_t *pfdev) {
free(pfdev->pfsystem_interface); // 先释放系统驱动
pfdev->pfsystem_interface = NULL; // 避免悬挂指针
free(pfdev); // 再释放暴露的接口
pfdev = NULL; // 避免悬挂指针
return GENERIC_OK;
}

驱动编写

驱动编写中,注意每个驱动内部的API都需要一个void *类型的指针,用来调用generic_sensor_aht20_driver_interface_t的驱动接口