BES SDK 软件架构开发简介

Hello ,大家好!上次写了基于恒玄模块的 单线串口通讯模块,本文在这里对恒玄蓝牙SDK 做简单的介绍。

一 .软件系统构架

1.BES采用的是RXT RTOS(嵌入式实时操作系统),并且用了ARM的CMSIS_RTOS API接口

2. 我们知道程序运行开始的地方是RTX_CM_LIB.H里面的_main_init(),主要是进行内核初始化、堆栈的设置、main线程的创建和开启内核等。

3.第一个线程os_thread_def_main就是main,接着看,在main.cpp文件中。

然后再是 debug 端口的初始化 (一把我们用 debug=1)默认开启UART0  RX的 打印输出。

在 uart 初始化完成后 会打印输出 当前软件、芯片、flash分区大小等一些信息  hal_trace.c 中:

打印输出大致如下 :

[18:35:14.391]CHIP=best1305
[18:35:14.391]KERNEL=RTX5
[18:35:14.391]CRASH_DUMP_SIZE=0
[18:35:14.391]AUD_SEC_SIZE=0x10000
[18:35:14.391]USER_SEC_SIZE=0x1000
[18:35:14.391]FACT_SEC_SIZE=0x1000
[18:35:14.391]NV_REC_DEV_VER=2
[18:35:14.391]FLASH_BASE=0x38000000
[18:35:14.391]FLASH_SIZE=0x400000
[18:35:14.391]OTA_CODE_OFFSET=0x28000
[18:35:14.391]CRC32_OF_IMAGE=0x00000000
[18:35:14.391]BUILD_DATE=Mar 10 2021 18:21:32
[18:35:14.391]REV_INFO=:best2500i_JBL_T230
[18:35:14.391]
[18:35:14.391]
[18:35:14.391]------
[18:35:14.391]METAL_ID: 0
[18:35:14.391]------

随后 软件运行会判断当前flash实际大小与软件定义是否匹配,如果软件定义的flash size 超过芯片自带flash大小,就会打印输出

“Wrong FLASH_SIZE defined in target.mk!”

“FLASH_SIZE is defined as 0x20000 while the actual chip flash size is 0x40000” 随后死机。

如果flash无异常 后续就会启动  硬件IO 配置和模拟输入输出IO 配置

随后 跑入app_init

最后 如果初始化完成 就在一直死循环执行 线程任务

 

如果app_init里面初始化提前结束并且返回 非0  则直接关机

4. BES 的线程/任务消息 /定时器创建和使用。

首先说个概念理解 :1.线程是基于RTOS的一个任务调度处理 (模拟多核 占用时间片的)

                                  2.每个线程都是独立并且不会被同一时间执行的 

                                  3.一个线程里面可以包含多个 消息模块处理.

A.线程

第一个线程os_thread_def_main就是main,接着看,在main.cpp文件中:

app_os_init()里有线程创建,这是CMSIS的风格,开发过STM32应该比较熟悉,用来定义线程、定时器和邮箱通讯等;
app_thread_tid = osThreadCreate(osThread(app_thread), NULL);

app_thread为线程loop函数,OSThread其实是一个宏,就是取地址而已
#define osThread(name) &os_thread_def_##name

所以OSThreadDef是设置线程名、优先级以及堆栈大小,通过osThread获取配置的结构体变量的指针,然后作为参数传入OSThreadCreate()。

一般在文件开头会看到这样的定义:osThreadDef其实也是一个宏
osThreadDef(app_thread, osPriorityHigh, 1, 1024, “app_thread”);

PS:早期的线程定义是这样的:

/* thread */
static osThreadId uartrxtx_tid; 
uint32_t os_thread_def_stack_uartrxtx [UART_STACK_SIZE/ sizeof(uint32_t)];
osThreadDef_t os_thread_def_app_uartrxtx = {(app_uartrxtx),(osPriorityHigh),(UART_STACK_SIZE),(os_thread_def_stack_uartrxtx)};

这里就是给一个结构体变量初始化赋值,然后把os_thread_def_app_thread的地址传给os_thread()。结构体类型是:

/  ==== Thread Management Functions ====

/// Create a Thread Definition with function, priority, and stack requirements.
/// \param         name          name of the thread function.
/// \param         priority      initial priority of the thread function.
/// \param         instances     number of possible thread instances.
/// \param         stacksz       stack size (in bytes) requirements for the thread function.
/// \note CAN BE CHANGED: The parameters to \b osThreadDef shall be consistent but the
///       macro body is implementation specific in every CMSIS-RTOS.

#define osThreadDef(name, priority, instances, stacksz, task_name) \
uint64_t os_thread_def_stack_##name [(8*((stacksz+7)/8)) / sizeof(uint64_t)]; \
const osThreadDef_t os_thread_def_##name = \
{ (name), \
  { task_name, osThreadDetached, NULL, 0U, os_thread_def_stack_##name, 8*((stacksz+7)/8), (priority), 1U, 0U } }
下面是关于线程的创建:

我们再看下线程的执行:

下面我们看一下app_thread线程里面的具体做了什么:
if (mod_handler[msg_p->mod_id])
int ret = mod_handler[msg_p->mod_id] (&(msg_p->msg_body));

我们可以看到在app_thread线程中,通过app_mailbox_get()反复的在获取邮箱信息,并传入mod_handler[]里面。
static APP_MOD_HANDLER_T mod_handler[APP_MODUAL_NUM];
在这里插入图片描述
看其数据类型是一个函数指针数组,再看其数组下标的定义:

关于线程的创建使用实际代码连接如下 (包含新旧版本):

https://share.weiyun.com/GUat9L7n

B:消息任务:

消息任务是基于 线程下一级的处理 

目前通用的都是基于 app_thread  线程的消息任务处理 ,当然也可以在其余线程中增加任务处理 。

由上面线程讲解我们知道各个模块会注册自己的回调函数,然后app_thread()会根据get的消息回调模块对应的API进行处理。

所以任务在注册之前需要在app_os_init 之后 然后再去注册消息执行函数。

消息处理函数通用接口:

返回值为  int 类型数据  携带 APP_MESSAGE_BOY *的 结构体参数指针。

这里以我写的NTC 消息处理做讲解:

C 定时器:

定时器分为软件定时器和硬件定时器,本质上都是时间到了后执行中断处理。

区别是硬件定时器以 硬件时钟为基准 所以相对来说会更准确 测量精度更小,且硬件定时器是一次性的 。

定时器 可以重载 但不能在中断里面使用

C1:硬件定时器

头文件: #include "hwtimer_list.h"

static HWTIMER_ID app_box_det_debounce_tid = NULL;  //硬件定时器ID 定义  一般定义为全部变量

app_box_det_debounce_tid = hwtimer_alloc(app_box_det_debounce_timehanlder, NULL); //关联定时器中断执行函数。app_box_det_debounce_timehanlder会携带 void*类型参数。

hwtimer_start(app_box_det_debounce_tid,MS_TO_TICKS(200));// 启动定时器  需要注意后面不能直接填写时间,需要转为时钟

hwtimer_stop(app_box_det_debounce_tid); // 定时器可以被提前停止,移除队列 改定时器就当此就不再执行。

C2: 软件定时器:

头文件:

#include "cmsis_os.h"
#include "hal_timer.h"

typedef enum {
  osTimerOnce               = 0,          ///< One-shot timer.
  osTimerPeriodic           = 1           ///< Repeating timer.
} osTimerType_t;

static osTimerId app_battery_timer = NULL; //软件定时器ID  
static void app_battery_timer_handler(void const *param); //定时器 中断执行函数 ,可以携带 void* 类型参数
osTimerDef (APP_BATTERY, app_battery_timer_handler); //软件定时器 需要用宏关联定时器 和 执行函数

app_battery_timer = osTimerCreate (osTimer(APP_BATTERY), osTimerPeriodic, NULL); //软件定时器创建 APP_BATTERY为定时器寻址ID 

第二个参数 是 定时器是否为周期性的,如果是一次性的 那么以后时间到了 执行完后就不会再被执行 ,但是ID 还是在  可以再次start.

 osTimerStop(app_battery_timer); //停止该软件定时器
 osTimerStart(app_battery_timer,5000); // 重新开启软件定时器 ,并在5S后执行 定时器中断。

这一篇的系统架构 暂时说到这里  后续 子模块详细分析  请大家继续订阅 查看 ,谢谢!

QQ:1902026113

猜你喜欢

转载自blog.csdn.net/qq_34990604/article/details/114928487