目录
前言
本文写于TWS耳机项目的研发阶段,主要目的在于分享研发阶段中所遇到的问题以及对bes平台框架的初步理解,文章主要从开发阶段我所接触的模块来讲述各个模块的用法以及注意事项,旨在让后续人员能够以最快速度入手该平台,也为后续bes平台的新项目做好基础。该项目采用恒玄bes2500z超低功耗蓝牙音频SoC,支持蓝牙5.2,集成主动降噪功能,支持三麦克风实现高清通话降噪等,其芯片性能相当强悍。下面我将从以下几个方面,展开对BES平台的初步理解。
一、基本的软件框架
根据SDK的目录可以看出 ,BES采用的是RTX RTOS(嵌入式实时操作系统),并且使用了ARM的CMSIS _RTOS API接口,下面我们从main函数的入口介绍该平台的代码架构。
程序开始运行的地方在RTX_CM_LIB.H里面的_main_init,主要进行内核的初始化、堆栈的设置、main线程的创建和开启内核等(main实际上是一个线程,可以认为其后创建的线程均为该线程的子线程)。
<span style="background-color:#f8f8f8"><span style="color:#333333">void _main_init (void) __attribute__((section(".ARM.Collect$$$$000000FF")));
void _main_init (void) {
osKernelInitialize();
set_main_stack();
osThreadCreate(&os_thread_def_main, NULL);
osKernelStart();
for (;;);
}</span></span>
内核启动的第一个线程就是main,该线程在platform文件加下的main.cpp文件中。该线程主要进行一些基础的初始化配置,包括watchdog、trace、time、norflash以及一些gpio的配置,其最重要的接口为app_init,其接口包含了该平台所有模块功能的初始化。
<span style="background-color:#f8f8f8"><span style="color:#333333">int main(void)
{
...
ret = hlai_init_step1();
if (ret){
TR_INFO(TR_MOD(MAIN), "error, drv init failed!!!");
}
ret = app_init();
}</span></span>
app_init在apps文件夹下的apps.cpp文件中,该接口是BES平台模块功能初始化的核心,初始化了芯片主频、电源管理、蓝牙栈、af以及audio manager等,其中app_os_init函数创建了该平台最主要的线程app_thread。
<span style="background-color:#f8f8f8"><span style="color:#333333">int app_init(void)
{
...
list_init();
nRet = app_os_init();
if (nRet) {
goto exit;
}
...
}</span></span>
app_thread线程在apps的common文件夹下的app_thread.cpp文件中,该线程是继于main线程后的第一个线程,是个模块之间交互与解耦的主要线程,下面看一下线程的创建,该线程也为其他线程的创建提供了参考。
app_os_init接口包含RTX实时操作系统的mailbox的创建以及app线程的创建,特别注意osThreadDef和osThread,这两个其实是两个宏,使用osThreadDef配置线程的名字、优先级以及堆栈大小等,配置完之后该宏就会将配置信息填充到TCB块中,调用osThread宏传入线程的函数名就可以以配置的信息创建该线程(通过函数名获得结构体的头指针)。
<span style="background-color:#f8f8f8"><span style="color:#333333">osThreadDef(app_thread, osPriorityHigh, 1, APP_THREAD_STACK_SIZE, "app_thread");
osMailQDef (app_mailbox, APP_MAILBOX_MAX, APP_MESSAGE_BLOCK);
int app_os_init(void)
{
if (app_mailbox_init())
return -1;
app_thread_tid = osThreadCreate(osThread(app_thread), NULL);
if (app_thread_tid == NULL) {
TRACE(0,"Failed to Create app_thread\n");
return 0;
}
return 0;
}</span></span>
下面看一下app_thread线程里面具体做了哪些工作,如下,该线程主要用于接收各模块发送的消息,以及根据模块的id将此消息转发到注册该消息的对应模块的回调函数中,该线程实现了各模块之间的解耦,同时各个模块之间可以高效且快速的通过mailbox进行消息以及资源上的交互。后续项目app模块的添加也应在此基础上,通过app_mailbox与其他模块进行交互。
<span style="background-color:#f8f8f8"><span style="color:#333333">static void app_thread(void const *argument)
{
while(1){
APP_MESSAGE_BLOCK *msg_p = NULL;
if (!app_mailbox_get(&msg_p)) {
if (msg_p->mod_id < APP_MODUAL_NUM) {
if (mod_handler[msg_p->mod_id]) {
int ret = mod_handler[msg_p->mod_id](&(msg_p->msg_body));
if (ret)
TRACE(2,"mod_handler[%d] ret=%d", msg_p->mod_id, ret);
}
}
app_mailbox_free(msg_p);
}
}
}</span></span>
增加一个模块只需要添加一