一、背景说明
模块是对业务的抽象,组件是对功能的抽象,组件化的目的是为了将高频使用的功能点抽象成独立的插件,供开发者调用,提高开发效率;跨平台组件通过对不同芯片平台的兼容适配,提供给应用开发者无差异化的无感接口;解决以往同一个功能组件,各芯片端/业务线需要分开维护,造成人力浪费的问题;跨平台组件示意图如下:
二、设计原则
2.1 功能独立
组件内部不能互相显式调用,上层组件可以依赖下层组件接口,不同组件之间可以隐式调用;
2.2 功能单一
一个组件实现一个功能;
2.3 组件分类
组件按照功能可以分为4种:通用组件、基础组件、业务组件、应用组件,各组件说明见下表:
类别 | 功能 | 备注 |
---|---|---|
平台组件 | 平台相关的硬件抽象层,抽象硬件基础能力,比如PWM,FLASH、UART、TIMER等,兼容不同厂家SDK,向上层业务提供统一接口,连接业务和不同芯片平台,其它组件可以显式调用; 说明:不同协议,不同芯片平台驱动实现有差异,建议分芯片平台抽象组件,比如ST的芯片是一个组件,乐鑫的硬件抽象层放到一个组件;虽然不同芯片类型的驱动能力放到不同组件,但是各芯片的sal接口命名是统一的,可以由上层业务统一显式调用。 | |
通用组件 | 集成通用工具,比如:字符转换接口,加解密接口等 | |
基础组件 | 提供基础服务/能力,比如:红外解码,按键事件等 | |
业务组件 | 品类通用业务功能,通常相对稳定; | |
应用组件 | 是对高频定制业务的抽象,需要适应本地业务变化,持续集成,比如:遥控器/按钮对应的功能库,大客户定制业务等。 问题:考虑后续集成功能规模较大,Flash资源可能不够。 暂行办法:拆分成颗粒度适当的组件,通过配置项配置; 优点:不使用的组件不占用硬件资源; 缺点:颗粒度太小,组件数量太多,不好管理。 |
2.4 组件下沉
将相对稳定的业务下沉,对于那些由于不同品类导致的差异化部分通过注册的方式向开发者提供接口,处理执行结果;比如倒计时功能,照明品类是开关灯,电工品类是开关插座,开关灯/开关插座/其它操作 就是执行结果,应该放到应用层由开发者处理;
2.5 方便剪裁
每个组件都可以单独编译,不能出现不编译某个组件编译报错的情况(个别基础组件除外);每个组件只提供给开发者一个初始化接口;
2.6 兼容接口
组件接口分为:兼容性接口,非兼容性接口两种,实际情况也是如此,很难做到所有接口兼容;但是原则上讲,尽量做成兼容接口;
三、平台适配
针对不同芯片平台的差异处理,基本思路是增加适配层,主要是对硬件驱动的接口适配,有无操作系统适配。具体内容如下:
3.1 硬件适配
对不同硬件的适配,通过增加一个软件抽象层实现,格式类似 manufacturer_sal_xx(),一般芯片厂商都提供了硬件抽象层manufacturer_hal_xx(),但是有些厂商的原厂SDK接口没有抽象hal层。所以如果是已经提供了manufacturer_hal_xx() 接口那么只需要再抽一个manufacturer_sal_xx.h头文件接口即可,头文件中的内容如下:(manufacturer 一般是厂商名缩写)
#define manufacturer_sal_xx() esp_hal_xx()
如果芯片平台调用的原厂SDK没有提供hal层,那么需要新增两个文件作为软件适配层,分别是:manufacturer_sal_xx.h,manufacturer_sal_xx.c;manufacturer_sal_xx.h中主要是函数声明,比如:
extern VOID manufacturer_sal_xx(VOID arg);
manufacturer_sal_xx.c中则是对原厂接口的抽象层封装,比如:
VOID manufacturer_sal_xx(VOID arg)
{
// to do
}
为什么所有芯片不对标某些厂商的manufacturer_hal_xx()接口,那么原来hal接口就可以直接使用;这里有个顾虑,主要是担心有些入参无法适配或者不好适配,或者原来接口不符合我们预期,于是单独拉个软件抽象层;
3.2 系统适配
由于目前很多WiFi芯片是基于RTOS进行应用开发,BT和ZigBee更多是基于裸机开发,那么跨平台组件就涉及到是否支持OS的适配问题,RTOS内核主要包括几大模块:线程管理,任务调度,线程通信,内存管理、时钟管理,中断管理;但是由于有些模块在裸机上实现比较复杂,考虑到当前业务场景的使用频率,暂时只实现部分OS接口适配,分为兼容性适配层和非兼容性适配层,其中裸机实现比较复杂的放到非兼容性适配层。针对OS和裸机接口需要再做一个适配层,这个适配层和上述硬件适配层的sal功能类似。
3.2.1 兼容性适配层
- 定时器
- 内存管理
- 消息队列
3.2.2 非兼容性适配层
裸机实现比较复杂的OS功能放到非兼容性适配层,代码越复杂,出错的概率就越高,按照实际情况做取舍,以下内容暂时不做适配:
- 线程管理
- 互斥锁
- ... ...
3.3 通信适配
通信能力主要是指上下行数据,对于设备端而言,也就是指令接收和数据上报,不同芯片平台的上报接口也不同。由于三种芯片平台的通信能力不同,数据链路不同,数据格式也不同,为了抽象不同平台的通信能力,将数据上报抽象成句柄的方式,句柄包括WIFI,BT、ZigBee设备的上报能力;不同芯片平台只需要给句柄赋值,然后调用接口即可,如下图伪代码:
typdef struct{
PT_TYPE_E pt_type; // 协议类型
union{
WIFI_HANDLE_S wifi_handle;
BT_HANDLE_S bt_handle;
ZIGBEE_HANDLE_S zigbee_hanle;
}REPORT_HANDLE;
}MANU_REPORT_HANDLE_S;
RET_E manufacturer_data_report(MAUN_REPORT_HANDLE_S* manu_report_handle)
{
//to do
}
example:
MAUN_REPORT_HANDLE_S manu_report_handle;
//to do
RET_E ret = manu_data_report(&manu_report_handle);
if(RET_OK != ret){
PR_ERR("manufacturer report data err, %d", ret);
return ret;
}
3.3.5 指令接收
不同芯片平台收到的数据也不同,那么对于组件而言,如何适配这些不同格式的数据?答案是:组件不适配。不同的芯片平台,数据格式不同,这些数据在业务逻辑层转换成组件支持的数据格式,比如收到的是压缩数据,就先将数据解压,然后才能被组件解析。
3.4 功耗适配
关于低功耗实现,很多芯片是放在sdk上处理,如果应用上有显式调用就抽出来;
平台有用到就抽出来,没用到就空函数。
四、设计规范
4.1 编码规范
4.2 组件规范