本文主要介绍AliOS Things新增单板支持,相关board/platform目录、文件的部署规范,以及相关接口定义和使用的标准。相关代码合入AliOS Things代码仓库,都需要遵循此文档规范。
代码提交和回流过程中,都会按照本规范进行代码检查,不符合的代码不予入库,请务必遵循。
AliOS Things参考版本:AOS-R-3.1.0
参考示例规范单板:
board: aaboard_demo ;
mcu: aamcu_demo;
arch: Cortex-M4
说明:
如果使用其他版本,请参考:AliOS Things板级支持目录规范
和历史版本的差异,主要包括:
a、 原根目录board目录归纳到platform下,对应platform/board目录;
b、 board下面重新梳理了几个典型单板的初始化代码样例,部分board及其hal的抽象定义和实现,主要是将主任务的入口移动到app里面实现,由app来决定board的初始化内容,而非默认在board内全初始化;相关初始化代码和流程与对应的app关联修改,原有board实现放在board_legacy目录下;
c、 对应上述新规范board目录,相应的app实现在application/example目录下;原有的app实现移入application/example/example_legacy目录。
d、板级board初始化代码按照模块统一梳理。
1、目录结构规范
下述为新增一个单板支持,必须关注的几个目录项:
目录名 | 介绍 |
app/example | 通用用户运行实例,如helloworld实例,可直接使用,无特殊情况不修改 |
platform/board | 用户需要适配、可配置board级代码,系统启动相关代码 |
platform/arch | 该CPU架构内核调度适配接口,可直接使用 |
platform/mcu | 该MCU通用SDK以及对应的hal适配层 |
1.1、example新增规范
原则上不建议新增example,除非目前的example不能满足功能需求。app/example下为通用运行实例,如果新增example,需要具有通用性,而不是为了某个特殊,或者临时性的修改。
1.1.1、目录功能
针对上层用户需要运行的具体实例出发,抽象出具体代表某个功能的实例工程,如基本的定时输出功能:helloworld_demo,
上云通道实例功能:mqttapp。app/example目录下,为已经抽象的通用运行实例,原则上用户不需要修改,直接使用现行实例。
其中app/example/example_legacy下为3.1版本之前规范的app示例,其不包括maintask.c主任务实现,其相关主任务的板级实现统一在跳转app前做全初始化,即不同的app,底层初始化的模块是一样的;
app/example目录非example_legacy下为3.1版本要求的规范app示例,其包括maintask.c主任务实现,对板级初始化做了模块区分,以期望实现不同app按照具体需求来初始化不同的board模块。
其中,app/example/example_legacy对应platform/board/board_legacy目录的单板;
其他app/example对应platform/board下非board_legacy实现。
另,platform/board/board_legacy原有单板只能支持老的example_legacy实现;新的platform/board单板,兼容app/example下所有app。
1.1.2、命名规范
运行实例取名简洁、直观,需要和具体运行的功能对应。
1.1.3、目录结构规范
文件部署规范如下,以helloworld_demo示例,
helloworld_demo
|-- maintask.c # main task entry "aos_maintask"
|-- appdemo.c # helloworld source code, including app entry ”application_start”
|-- Config.in # menuconfig config file
|-- aos.mk # aos build system file(for make)
|-- k_app_config.h # aos app config file, has higher priority than k_config.h
|-- README.md
1.1.4、函数命名规范
主函数入口统一使用aos_maintask函数。用户可以在该接口内添加板级初始化代码。
运行实例入口统一使用application_start函数。用户可以在该接口内添加具体实现。
1.1.5、mk编写规范(aos.mk)
NAME := helloworld_demo #example名,和目录统一
$(NAME)_MBINS_TYPE := app #在多bin情况下,归属kernel还是app
$(NAME)_VERSION := 1.0.0 #menuconfig组件版本号
$(NAME)_SUMMARY := Hello World #描述
$(NAME)_SOURCES += #example.c文件
$(NAME)_COMPONENTS += osal_aos #依赖其他组件名
GLOBAL_INCLUDES += #全局头文件
GLOBAL_DEFINES += #全局宏定义
1.1.6、config.in编写规范
以helloworld_demo为例:
config AOS_APP_HELLOWORLD_DEMO # 定义组件配置选项
bool "Helloworld Demo" # 配置选项类型,双引号定义该选项显示名称
help
Hello World # 配置选项帮助
if AOS_APP_HELLOWORLD
# Configurations for app helloworld_demo # 如有必要,定义更多组件内配置选项
endif
组件配置选项命名规范:使用前缀“AOS_APP_” + 组件NAME
将新增example加入系统配置菜单:
如果新增example在“app/exampe”目录下,编辑“app/exampe/Config.in”
如果新增example在“app/profile”目录下,编辑“app/profile/Config.in”
例如:
source "app/example/helloworld_demo/Config.in" # 引用example配置文件
if AOS_APP_HELLOWORLD_DEMO # 如果example组件被启用
config AOS_BUILD_APP
default "helloworld_demo" # 为AOS_BUILD_APP赋值,与example目录一致
endif
注意:AOS_BUILD_APP默认值必须与配置命令行(AliOS Thing v3.1及以后,aos create project -b board -t app myapp
;3.1以前版本,aos make app@board -c config )输入的app保持一致。
1.2、board新增规范
1.2.1、目录功能
board下统一放置板级相关启动、配置、初始化代码,以及板级外设驱动。
其中platform/board/board_legacy下为3.1版本之前规范的board示例,其对应app/example/example_legacy下app实现;在跳转到app入口application_start前完成了board的全部初始化;
platform/board下非board_legacy目录为3.1版本要求的规范board示例,其对应app/example目录下非example_legacy实现;其进入main函数后,完成最基本的初始化,并创建主任务后,直接跳转到app内主任务入口aos_maintask,并最终跳转到application_start入口。
platform/board/board_legacy原有单板只能支持老的example_legacy实现;新的platform/board单板,兼容app/example下所有app。
1.2.2、命名规范
board取名需要使用标准通用名,能方便检索获取到相关单板信息。
1.2.3、目录结构规范
board目录下文件结构部署和命名需要遵循下面布局规则,以aaboard_demo单板为例
Dir\File Description Necessary for kernel run
|-- drivers # board peripheral driver N
|-- config
| |-- board.h # board config file, define for user, such as uart port num Y
| |-- k_config.c # user's kernel hook and mm memory region define Y
| |-- k_config.h # kernel config file .h Y
| |-- partition_conf.c # board flash config file N
|-- startup
| |-- board.c # board_init implement Y
| |-- startup.c # main entry file Y
| |-- startup_gcc.s # board startup assember for gcc Y
| |-- startup_iar.s # board startup assember for iar Y
| |-- startup_keil.s # board startup assember for keil Y
|-- aaboard_demo.icf # linkscript file for iar Y
|-- aaboard_demo.ld # linkscript file for gcc Y
|-- aaboard_demo.sct # linkscript file for sct Y
|-- aos.mk # board makefile Y
|-- Config.in # menuconfig component config Y
|-- ucube.py # aos build system file N
|-- README.md Y
注:gcc、keil、iar任意支持一种即可,无需全部支持,即startup_xx.s和链接文件只需实现一套即可
1.2.4、函数命名规范
文件 | 函数名 |
k_config.c | 实现样例单板aaboard_demo该文件内所有对接接口 |
partition_conf.c | 统一分区初始化接口:flash_partition_init |
board.c | 统一单板初始化接口:board_init |
startup.c | 无特殊情况统一C程序主入口为main; 内部调用单板初始化board_init; 内部调用krhino接口初始化内核; 内部创建主任务入口sys_init。 (具体见初始化流程规范) |
1.2.5、mk编写规范(aos.mk)
NAME := board_aaboard_demo #board_+单板名
$(NAME)_MBINS_TYPE := kernel #在多bin情况下,归属kernel还是app
$(NAME)_VERSION := 1.0.1 #组件版本号
$(NAME)_SUMMARY := #描述
MODULE := 1062 #固定
HOST_ARCH := Cortex-M4 #CPU arch
HOST_MCU_FAMILY := mcu_aamcu_demo #归属MCU系列,需要对应platform/mcu下aos.mk组件
SUPPORT_MBINS := no #是否支持app/kernel的bin分离
HOST_MCU_NAME := aamcu1_demo # MCU子系列类型
ENABLE_VFP := 1 #是否支持浮点数
$(NAME)_SOURCES += #board组件包含.c文件
$(NAME)_COMPONENTS += $(HOST_MCU_FAMILY) #依赖其他组件名
GLOBAL_INCLUDES += #头文件
GLOBAL_CFLAGS += #c文件编译选项
GLOBAL_ASMFLAGS += #汇编编译选项
GLOBAL_LDFLAGS += #链接选项
GLOBAL_DEFINES += #用户自定义宏
注意:
(1)、其中HOST_MCU_FAMILY的定义需要对应platform/mcu某子目录下aos.mk中的组件名NAME,一般是mcu_+“mcu名”。HOST_MCU_NAME表示具体的mcu子系列。
(2)、用户可以通过GLOBAL_DEFINES定义宏,如GLOBAL_DEFINES += CONFIG_AOS_CLI_BOARD或者GLOBAL_DEFINES += CONFIG_AOS_KV_BLK_BITS=14。当然也可以直接在编译选项 GLOBAL_CFLAGS使用-D定义。
(3)、原则上在3.1版本中,mk只用来增加定义的组件components,其他配置和宏定义统一在Config.in定义。具体参考组件化指导文档。
1.2.6、config.in编写规范
以aaboard_demo为例:
config AOS_BOARD_AABOARD_DEMO # 定义组件配置选项
bool "AABOARD_DEMO" # 配置选项类型,双引号定义该选项显示名称
help
... # 配置选项帮助
if AOS_BOARD_AABOARD_DEMO
# Configurations for board aaboard_demo
# "BSP SUPPORT FEATURE" # 硬件支持的能力
config BSP_SUPPORT_UART
bool
default y
...
endif
board组件配置选项命名规范:使用前缀“AOS_BOARD_” + 组件NAME。
1.3、arch子目录新增规范
目前arch下已经适配了主要的CPU架构,原则上只需要直接使用。如果需要新增,需要关注下述章节。
1.3.1、目录功能
arch目录下主要是基本的CPU架构相关的porting,主要包括开关中断实现、任务切换、中断上下文切换等功能。
1.3.2、命名规范
必须使用业界通用CPU架构名,从名字可以清晰了解是哪种CPU、哪种processs系列。
1.3.3、目录结构规范
新增CPU架构规范,以ARM体系为例。
三级和四级目录按照具体情况可选。对于三级目录,如果此架构只会使用gcc,则不需要分此目录;如果二级目录可以区分不同的处理器系列或架构类型,则按照具体情况不需要添加。
一级目录 CPU arch | 二级目录 Process arch | 三级目录(具体情况可选) Compiler Type | 四级目录(具体情况可选) Process series |
ARM | armv5 | armcc/gcc/iccarm | |
armv6m | armcc/gcc/iccarm | m0 | |
armv7m | armcc/gcc/iccarm | m3 | |
m4 | |||
m7 | |||
armv7a | armcc/gcc/iccarm | a5 | |
a7 | |||
a9 |
1.3.4、函数命名规范
CPU arch统一对接下述接口
CPU Porting接口 | 说明 |
cpu_intrpt_save | 关中断 |
cpu_intrpt_restore | 开中断 |
cpu_intrpt_switch | 中断退出切换(在中断处理函数尾部使用,需要确保被打断的上下文正确保存,中断退出后,回到当前最高优先级任务) |
cpu_task_switch | 任务切换(需要保存老任务上下文、获取最高优先级任务、恢复新任务上下文) |
cpu_first_task_start | 进入第一个任务调度 |
cpu_task_stack_init | 任务栈初始化 |
cpu_cur_get | 获取当前核号 |
1.3.5、mk编写规范(aos.mk)
没有例外情况,统一在二级Process arch目录添加对应的编译mk文件。
arch mk添加规范如下(以armv7m为例):
NAME := arch_armv7m #arch_+架构名
$(NAME)_MBINS_TYPE := kernel #多bin情况下,归属kernel还是app
$(NAME)_VERSION := 1.0.2 #menuconfig版本号
$(NAME)_SUMMARY := arch for armv7m #描述
$(NAME)_SOURCES += #组件包含.c文件
GLOBAL_INCLUDES += #包含头文件
ifeq ($(COMPILER),armcc) #区分编译器
ifeq ($(HOST_ARCH),Cortex-M4) #区分Process series
1.3.6、config.in编写规范
以armv7m为例:
config AOS_ARCH_ARMV7M # 定义组件配置选项
bool # 配置选项类型
help
arch for armv7m # 配置选项帮助
if AOS_ARCH_ARMV7M
# Configurations for arch armv7m # 如有必要,定义更多组件内配置选项
endif
Arch组件配置选项命名规范:使用前缀“AOS_ARCH_” + 组件NAME
1.4、mcu子目录新增规范
1.4.1、目录功能
Mcu目录存放其原始SDK驱动文件,以及hal驱动对接层。
其中的SDK文件原则上直接使用厂商的驱动包,除了License或bug修复等,原则上不做修改。板级相关的配置代码统一放入board目录。
1.4.2、命名规范
Mcu命令需要使用业界通用名,能直观方便检索到相关信息为准。
1.4.3、目录结构规范
Dir\File Description Necessary for kernel run
|-- drivers # board peripheral driver Y
|-- hal # hal API layer, hal uart is necessary Y
|-- aos.mk # mcu makefile Y
|-- Config.in # menuconfig component config Y
|-- README.md Y
1.4.4、函数命名规范
统一按照样例aamcu_demo/hal下列出的各模块hal API实现。
1.4.5、mk编写规范(aos.mk)
mcu的mk文件,其描述了当前mcu组件需要的编译文件和编译选项。
如果该系列MCU能实现一个通用mk文件则使用一个即可;如果该MCU体系下存在多种MCU子系列,那么需要添加子mcu的mk文件,在其中放置不同的属性定义。aos.mk作为主mk,主要放置公共的属性配置,并使用HOST_MCU_NAME来分别引用对应的子mcu。
示例:
aamcu_demo #mcu主目录
|-- aos.mk # 该mcu主mk
|-- aamcu1_demo.mk # aamcu1_demo
|-- aamcu2_demo.mk # aamcu2_demo
在对应board如aaboard_demo的aos.mk文件引用此mcu模块名时,使用格式:
示例:
HOST_MCU_FAMILY := mcu_aamcu_demo
HOST_MCU_NAME := aamcu1_demo
在mcu的主aos.mk中需要分别对子mcu进行引用,使用格式:
include $($(NAME)_LOCATION)/$(HOST_MCU_NAME).mk
aos.mk其他包含项:
NAME := mcu_aamcu_demo #主MCU名,一般是mcu_+当前mcu目录名
$(NAME)_MBINS_TYPE := kernel #多bin情况下,归属kernel还是app
$(NAME)_VERSION := 1.0.2 #menuconfig组件版本号
$(NAME)_SUMMARY := driver & sdk #描述
$(NAME)_SOURCES += #MCU组件包含.c文件
$(NAME)_COMPONENTS += #依赖其他组件名
GLOBAL_INCLUDES += #头文件
GLOBAL_CFLAGS += #c文件编译选项
GLOBAL_ASMFLAGS += #汇编编译选项
GLOBAL_LDFLAGS += #链接选项
GLOBAL_DEFINES += #用户自定义宏
如,需要指定该MCU的CPU类型,则如下定义,将对应CPU调度代码加入编译体系:
$(NAME)_COMPONENTS += arch_armv7m
同样的,在3.1版本中,mk建议只定义组件化的模块components,其他配置和选项需要在Config.in中定义。
1.4.6、config.in编写规范
以aamcu_demo为例:
config AOS_MCU_AAMCU_DEMO # 定义组件配置选项
bool # 配置选项类型
select ... # 依赖其他组件
help
driver & sdk for platform/mcu aamcu_demo # 配置选项帮助
if AOS_MCU_AAMCU_DEMO
# Configurations for mcu aamcu_demo # 如有必要,定义更多组件内配置选项
endif
mcu组件配置选项命名规范:使用前缀“AOS_MCU_” + 组件NAME.
2、接口定义使用规范
2.1、内核接口使用规范
规范:对于纯内核系统,或者内核本身、bsp相关代码使用krhino接口;对于上层连接协议栈、app统一使用aos接口。
2.2、HAL定义规范
规范:hal相关接口的命名和声明统一参照目录aamcu_demo/hal下提供的样例demo实现。
特殊说明:
Flash相关hal接口实现,对OTA功能有影响,包括不限于:
相关hal函数返回值需要规范,统一为正确返回0,错误返回负值;
相关含有出参的接口,如off_set需要正确赋值;
具体细节以OTA相关实现要求为准。
3、初始化流程规范
系统从复位启动到main函数入口的流程一般使用该单板通用的汇编程序来实现,进入main函数后需要遵循下面规范。下面规范一方面为了使用接口统一,另一方面避免发生一些已知的流程问题。
main函数位置规范:
按照1.2
章节board目录结构描述,统一放在startup.c中实现,并统一跳转到app中的主任务入口aos_maintask,由其跳转到app入口application_start。
单板驱动初始化规范:
在初始化流程中,对于板级的初始化,按照模块功能划分,统一了初始化的接口实现,参考aaboard_demo/config/board_api.h,具体实现在board.c中。
主要包括:
void board_basic_init(void) # 基础的堆、时钟初始化
void board_stduart_init(void) # 标准输出
void board_tick_init(void)
void board_flash_init(void)
void board_network_init(void)
void board_gpio_init(void)
void board_wdg_init(void)
void board_ota_init(void)
void board_dma_init(void)
用户需要按照具体运行的app需求,来实现对应的模块初始化代码;并且在app主任务中将上述接口封装在board_init里面统一调用,不同的app中board_init封装的board模块不同。
对于board_basic_init函数,需要在进入main函数内,内核启动前调用;
其他的模块初始化按需封装在board_init内,在主函数入口aos_maintask调用。
board_basic_init此接口调用时,内核尚未初始化,此初始化阶段不能激活中断处理,否则会触发中断调度。
内核初始化调用:
统一使用krhino接口,如krhino_init和krhino_start。
主任务创建规范:
内核初始化本身只会创建内部任务,如idle/timer任务;初始化流程中需要创建主任务,供用户app运行。统一通过krhino接口如krhino_task_dyn_create来创建主任务;主任务的入口统一为aos_maintask。
接口使用限制说明:
krhino_init前不调用malloc、printf函数。原因是此类库函数被内核重定向,会调用内核接口aos_malloc,依赖内核的初始化。
系统初始化流程规范如下:
主任务会在krhino_start开始调度后进入,如果不创建主任务,则系统会默认进入OS自身创建的其他任务运行,比如idle任务。
主任务入口aos_maintask实现:
在aos_maintask中,统一调用board_init实现板级初始化,内部按照需求来添加board的具体组件;
如果需要初始化相关中间件和协议栈模块,使用aos_components_init接口;
最后,在非多bin的情况下,统一调用application_start进入上层app入口;多bin情况下,由aos_components_init内部分发处理。
(1)、系统初始化示例:
参考代码(platform/board/aaboard_demo/startup/startup.c):
int main(void)
{
/*irq initialized is approved here.But irq triggering is forbidden, which will enter CPU scheduling.
Put them in sys_init which will be called after aos_start.
Irq for task schedule should be enabled here, such as PendSV for cortex-M4.
*/
/* board basic init: Base CLK, heap, define in board\aaboard_demo\startup\board.c */
board_basic_init();
/* kernel init, malloc can use after this! */
krhino_init();
/* main task to run */
krhino_task_dyn_create(&g_main_task, "main_task", 0, AOS_MAIN_TASK_PRI, 0,
AOS_MAIN_TASK_STACK_SIZE, (task_entry_t)aos_maintask, 1);
/* kernel start schedule! */
krhino_start();
/* never run here */
return 0;
}
(2)、主任务初始化示例:
参考代码(application/example/helloworld_demo/maintask.c):
void board_init(void)
{
board_tick_init();
board_stduart_init();
board_dma_init();
board_gpio_init();
}
void aos_maintask(void* arg)
{
board_init();
/*内核参数初始化,一般使用默认*/
board_kinit_init(&kinit);
/*中间件模块初始化,按宏开关*/
aos_components_init(&kinit);
/*跳转到app用户入口*/
#ifndef AOS_BINS
application_start(kinit.argc, kinit.argv); /* jump to app entry */
#endif
}
(3)、用户app入口示例:
参考代码(application/example/helloworld_demo/appdemo.c):
int application_start(int argc, char *argv[])
{
int count = 0;
printf("nano entry here!\r\n");
while(1) {
printf("hello world! count %d \r\n", count++);
aos_msleep(1000);
};
}
4、内核认证
AliOS
Things提供了基本的内核测试用例集,用于内核移植后的测试验证,所有移植的平台都需要运行该测试样例,确保内核功能的正确性。
内核测试集目录:test/testcase/certificate/certificate_test
在上面目录下提供了两个测试文件rhino_test.c和aos_test.c。其中rhino_test.c针对于纯内核的移植,aos_test.c针对于至少包含AOS API层的移植。
目前主要的认证项都会通过aos层,如果只关注rhino_test.c相关纯内核的验证,需要做以下修改:
- 修改rhino_test.c配置项,如:
/*以下字符定义可任取名字,不能为空*/
#define SYSINFO_ARCH "unknown"
#define SYSINFO_MCU "unknown"
#define SYSINFO_DEVICE_NAME "unknown"
#define SYSINFO_APP_VERSION "3.1.0"
/*kv不属于纯krhino模块,需要关闭*/
#define TEST_CONFIG_KV_ENABLED (0)
- 将rhino_test.c和cut.c/cut.h加入编译体系
可以将test/testcase/certificate/certificate_test目录下此三个直接拷贝到对应mcu下,新建一个test目录并加入到makefile;其他IDE直接添加编译文件。
- 在主任务中调用test_certificate执行测试用例认证直到用例通过即可。
上面属于纯krhino内核的测试方式,如果带aos接口层的测试请参考,
AliOS Things Kernel
测试指南:https://github.com/alibaba/AliOS-Things/wiki/Manual-API
5、代码合入整体原则
5.1、公共代码修改
公共代码原则上避免修改,以影响其他单板。通用文件修改后,需要确认不影响其他工程的编译和运行。如果影响公共代码,需要清晰说明:是修复bug、增加新特性、或是改进功能,并介绍如何完成的。
公共代码范围:目前除新增board目录、新增mcu目录,其他目录或者文件都视为公共文件,包括app/example目录。修改后,都可能影响其他单板。
5.2、编译链接选项限制
在编译选项中,严禁加-w选项关闭编译告警,以控制上传代码质量。
5.3、License准则
- 原创为主尊重版权,请作者标明自己的 copyright 信息,并签署CLA
- 禁止合入这些 license 的代码: AGPL, CPAL, OSL 等严格开源许可证
- 谨慎处理 GPL/LGPL 等强制开源许可证的代码,考虑以下替代方案
(1)将 GPL/LGPL 或类似许可证源码编译为二进制程序,作为独立软件使用
(2)将 LGPL 或类似许可证源码编译为动态连接库,并以动态连接方式调用
(3)将 LGPL
或类似许可证源码编译为静态链接库与应用程序相结合发布,但同时提供整个应用程序(含
LGPL 静态连接库)的目标代码和 LGPL 库源码
- 允许使用的 license: BSD, MIT, Apache License Version 2.0, Zlib, CDDL 等宽松开源许可证
5.4、CI验证通过
- Build: 代码 autobuild, PV build 通过
- Test: PV 测试通过(目前暂无此步骤)
- Agreement: CLA 已签署
在文档使用中是否遇到以下问题
更多建议
匿名提交