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