一、do_min_word
void do_min_work(void)
{timer_used_min = app_easy_timer(APP_PERIPHERAL_CTRL_TIMER_DELAY_MINUTES, do_min_work);current_unix_time += time_offset;time_offset = 60;// if (isconnected == 1)// {// GPIO_SetActive(GPIO_LED_PORT, GPIO_LED_PIN);// GPIO_SetInactive(GPIO_LED_PORT, GPIO_LED_G_PIN);// DEV_Delay_ms(5);// }arch_printf("current_unix_time:%d\n", current_unix_time);do_time_show();if (step == 0){// do_img_save();step = 1;display();}// time_refresh_count++;// if ((time_refresh_count >= Time_To_Refresh) && (g_tm.tm_min == 0 && g_tm.tm_hour == 0))// {// if (step == 0)// {// // do_img_save();// step = 1;// display();// }// }// GPIO_SetActive(GPIO_LED_PORT, GPIO_LED_G_PIN);// GPIO_SetInactive(GPIO_LED_PORT, GPIO_LED_PIN);
}
這段代碼是一個定時器回調函數,用于每分鐘執行一次系統時間更新和顯示刷新操作。以下是對代碼的詳細解釋:
void do_min_work(void)
{// 1. 重新設置定時器,確保每分鐘執行一次timer_used_min = app_easy_timer(APP_PERIPHERAL_CTRL_TIMER_DELAY_MINUTES, do_min_work);// 2. 時間更新邏輯current_unix_time += time_offset; // 累加當前時間(秒)time_offset = 60; // 重置偏移量為60秒(1分鐘)// 3. 調試輸出當前時間arch_printf("current_unix_time:%d\n", current_unix_time);// 4. 顯示時間do_time_show();// 5. 狀態機控制(僅在step=0時執行一次)if (step == 0){step = 1; // 切換狀態display(); // 更新顯示內容}
}
其中:APP_PERIPHERAL_CTRL_TIMER_DELAY_MINUTES是一個宏,其定義如下 :
#define APP_PERIPHERAL_CTRL_TIMER_DELAY_MINUTES 6000,這時是否少了一個0,不得而知。
代碼關鍵點解釋
-
定時器機制:
- 通過
app_easy_timer
函數設置一個每分鐘觸發的定時器 - 每次回調執行時會重新設置定時器,形成循環調用
APP_PERIPHERAL_CTRL_TIMER_DELAY_MINUTES
應定義為 60000ms(1 分鐘)
- 通過
-
時間維護:
current_unix_time
存儲當前的 Unix 時間戳(秒)time_offset
初始為 60,每次累加后重置,確保每分鐘遞增 60 秒- 這種設計允許系統在無法獲取 RTC 時通過軟件維護時間
-
顯示控制:
do_time_show()
:更新時間顯示display()
:刷新整個顯示內容- 使用
step
變量實現狀態機控制,確保某些操作只執行一次
-
注釋代碼分析:
- 被注釋的 LED 控制代碼表明系統可能通過 LED 指示連接狀態
do_img_save()
可能用于保存屏幕截圖或圖像數據- 時間刷新條件檢查(午夜 0 點)被注釋,可能用于每日特定操作
二、app_easy_timer
函數
timer_hnd app_easy_timer(const uint32_t delay, timer_callback fn)
{// Sanity checksASSERT_ERROR(delay > 0); // Delay should not be zeroASSERT_ERROR(delay <= KE_TIMER_DELAY_MAX); // Delay should not be more than maximum allowedtimer_hnd timer_id = set_callback(fn);if (timer_id == EASY_TIMER_INVALID_TIMER){return EASY_TIMER_INVALID_TIMER; //No timers available}// Create timercreate_timer(timer_id, delay);return timer_id;
}
其中timer_hnd定義如下 :typedef uint8_t timer_hnd;
app_easy_timer 函數用于創建一個定時器,在指定的延時后執行回調函數。它提供了參數檢查、資源分配和定時器初始化的功能。
timer_hnd app_easy_timer(const uint32_t delay, timer_callback fn)
{// 1. 參數有效性檢查ASSERT_ERROR(delay > 0); // 延時必須大于0ASSERT_ERROR(delay <= KE_TIMER_DELAY_MAX); // 延時不能超過系統最大限制// 2. 分配定時器資源并注冊回調函數timer_hnd timer_id = set_callback(fn);if (timer_id == EASY_TIMER_INVALID_TIMER){return EASY_TIMER_INVALID_TIMER; // 沒有可用的定時器資源}// 3. 創建并啟動定時器create_timer(timer_id, delay);// 4. 返回定時器句柄供后續操作使用return timer_id;
}
核心功能分析 1. ?參數檢查:
? delay > 0:確保延時時間有效
? delay <= KE_TIMER_DELAY_MAX:防止過大的延時值導致系統異常
? ASSERT_ERROR 通常是斷言宏,參數不合法時會觸發錯誤處理 ?
2. ?資源分配:
? set_callback(fn) 函數: ? 分配一個定時器 ID
? 將傳入的回調函數 fn 與該 ID 關聯
? 返回 EASY_TIMER_INVALID_TIMER 表示資源不足 ? ?
3. ?定時器初始化:
? create_timer(timer_id, delay) 函數:
? 使用分配的 ID 配置硬件 / 軟件定時器
? 設置延時值 delay(單位通常為毫秒)
? 啟動定時器開始倒計時 ? ?
4. ?錯誤處理:
? 當無可用定時器資源時,函數會立即返回錯誤碼
? 上層應用需要檢查返回值以處理錯誤情況
5.典型使用流程
1. 調用示例:
? // 設置一個1000ms后執行的定時器
timer_hnd my_timer = app_easy_timer(1000, my_callback_function);
if (my_timer == EASY_TIMER_INVALID_TIMER) {
? ? // 處理定時器創建失敗的情況
}
? ? ?2. 回調函數定義:
?void my_callback_function(void) {
? ? // 定時器到期時執行的代碼
? ? // 例如更新狀態、觸發事件等
}
6.注意事項
1). ?定時器資源限制:
? 系統通常有最大定時器數量限制
? 未釋放的定時器會持續占用資源 ?
2). ?延時精度:
? 實際延時可能受系統負載影響
? 依賴底層定時器硬件的精度 ?
3). ?回調函數約束:
? 避免在回調中執行耗時操作
? 確保回調函數可重入(如果支持多定時器并發) ?
4. ) 資源釋放:
? 通常需要配套的定時器刪除函數(如 delete_timer(timer_hnd))
? 不再使用的定時器應及時釋放 ? ?
這個函數提供了嵌入式系統中常用的定時器功能抽象,通過分離資源分配和定時器操作,使上層應用可以更方便地使用定時服務。
7.其中set_callback函數定義如下:
***************************************************************************************** @brief Place a callback in the first available position in the timer callback array.* @param[in] fn The callback to be added* @return The handler of the timer for future reference. EASY_TIMER_INVALID_TIMER if* there is no timer available*****************************************************************************************/
static timer_hnd set_callback(timer_callback fn)
{for (int i = 0; i < APP_TIMER_MAX_NUM; i++){if (timer_callbacks[i] == NULL){timer_callbacks[i] = fn;return APP_EASY_TIMER_IDX_TO_HND(i);}}return EASY_TIMER_INVALID_TIMER;
}
這段代碼實現了一個內部函數 set_callback,用于在定時器回調數組中分配一個空閑位置并注冊回調函數。以下是對代碼的詳細解釋:set_callback 是一個靜態函數,用于從回調數組中分配一個未使用的索引,并將傳入的回調函數 fn 存儲在該位置。如果找不到空閑位置(即所有定時器都已被占用),則返回錯誤碼。
static timer_hnd set_callback(timer_callback fn)
{
? ? // 遍歷定時器回調數組,查找空閑位置
? ? for (int i = 0; i < APP_TIMER_MAX_NUM; i++)
? ? {
? ? ? ? // 如果發現某個位置為空(NULL),說明該位置未被使用
? ? ? ? if (timer_callbacks[i] == NULL)
? ? ? ? {
? ? ? ? ? ? // 將回調函數存儲到該位置
? ? ? ? ? ? timer_callbacks[i] = fn;
? ? ? ? ? ??
? ? ? ? ? ? // 將數組索引轉換為定時器句柄并返回,
? ? ? ? ? ? return APP_EASY_TIMER_IDX_TO_HND(i);
? ? ? ? }
? ? }
? ??
? ? // 如果遍歷完所有位置都沒有找到空閑位置,返回錯誤碼
? ? return EASY_TIMER_INVALID_TIMER;
}
p
其中:timer_calback 定義如下 :
//?Timer callback function type definition
typedef void (* timer_callback)(void);
此代碼解析:這段代碼定義了一個函數指針類型 timer_callback,用于表示定時器回調函數的原型。
類型定義:
? timer_callback 是一個函數指針類型,指向 無參數、無返回值 的函數。 ?
函數原型要求:
? 回調函數必須符合 void function_name(void) 的形式。
? 例如:
?void my_timer_handler(void) {
? ? // 定時器觸發時執行的代碼
}
? ? ?
? ??用途:
? 作為參數傳遞給定時器 API(如 app_easy_timer),用于注冊定時執行的任務。
? 系統在定時器到期時,會通過該函數指針調用對應的回調函數。 ? ?
典型應用場景
? ? ? ? // 定義回調函數
void update_led(void) {
? ? // 更新LED狀態
}
// 創建定時器,注冊回調
timer_hnd timer = app_easy_timer(1000, update_led); // 1秒后執行
? ? ?技術要點
1. ?函數指針語法:
? void (*)(void) 表示指向 “無參數、無返回值函數” 的指針。
? typedef 將該指針類型命名為 timer_callback,簡化后續使用。 ?
2. ?與定時器系統的關聯:
? 結合之前分析的 set_callback 函數,timer_callbacks 數組實際存儲的就是 timer_callback 類型的函數指針。
? 當定時器到期時,系統會直接調用 timer_callbacks[index]()。 ?
3. ?兼容性要求: ? 所有注冊的回調函數必須嚴格遵循 void(void) 原型,否則可能導致棧破壞或參數傳遞異常。 ? ?
注意事項
1. ?回調函數的執行環境:
? 回調函數可能在中斷上下文或低優先級任務中執行,應避免耗時操作(如阻塞 IO)。 ?
2. ?參數傳遞限制:
? 由于函數原型固定為無參數,若需傳遞上下文,可通過全局變量或閉包技術(如 C++ lambda 捕獲)。 ?
3. ?錯誤處理:
? 回調函數內部應包含必要的錯誤檢查,避免因異常導致系統崩潰。
其中:timer_callbacks數組定義如下 :
// Array that holds the callback function of the active timers
static timer_callback timer_callbacks[APP_TIMER_MAX_NUM] ? ? ? ? ?__SECTION_ZERO("retention_mem_area0");
這段代碼聲明了一個靜態數組 timer_callbacks,用于存儲系統中所有活躍定時器的回調函數。以下是詳細解釋:
數據結構:
? timer_callbacks 是一個靜態數組,大小為 APP_TIMER_MAX_NUM,表示系統最多支持的并發定時器數量。
? 數組元素類型為 timer_callback(即函數指針,指向 void (*)(void) 類型的函數)。 ?
存儲屬性:
? static 關鍵字確保數組的作用域僅限于當前文件,避免外部訪問。
? __SECTION_ZERO("retention_mem_area0") 是一個特殊的編譯器指令,
用于:
? 將數組放置在名為 "retention_mem_area0" 的內存區域。
? __SECTION_ZERO 通常表示該區域在系統復位時不會被清零(即 “保留內存”),適用于需要在睡眠 / 喚醒周期中保持狀態的場景。 ? ?
初始化:
? 由于位于 __SECTION_ZERO 區域,數組初始值為全零(即所有元素初始化為 NULL),表示無活躍定時器。 ? ?
關鍵技術點
1. ?回調函數管理:
? 數組索引對應定時器 ID,例如 timer_callbacks[0] 對應第一個定時器的回調函數。
? set_callback 函數通過線性搜索找到第一個 NULL 位置,將回調函數存入并返回索引。 ?
2. ?保留內存的作用:
? 在低功耗系統中,部分內存區域可配置為 “保留” 狀態,即使系統進入睡眠模式也不會斷電。
? 定時器回調數組位于保留區,可確保:
? 睡眠喚醒后定時器狀態不丟失。
? 無需重新初始化定時器配置。 ? ?
3. ?與硬件的關聯:
? 實際硬件定時器可能通過 ID 與數組索引映射,
例如:
?// 定時器觸發時,通過索引調用對應回調
void hardware_timer_isr(uint8_t timer_idx) {
? ? if (timer_callbacks[timer_idx] != NULL) {
? ? ? ? timer_callbacks[timer_idx](); // 執行回調
? ? }
}
? ? ?
? ? 應用場景
1. ?嵌入式系統中的定時任務:
? 周期性數據采集(如傳感器讀數)。
? 狀態機超時處理。
? 通信協議中的定時響應(如心跳包)。 ?
2. ?低功耗設計:
? 配合 RTC(實時時鐘)實現長時間定時喚醒。
? 在休眠模式下保持關鍵定時器狀態。 ?
?注意事項
1. ?內存限制:
? APP_TIMER_MAX_NUM 受保留內存區域大小限制,需根據實際需求調整。 ?
2. ?線程安全:
? 多任務環境中修改數組需加鎖保護,避免競態條件。 ?
3. ?兼容性:
? __SECTION_ZERO 是特定編譯器的擴展(如 GCC、ARMCC),不同平臺語法可能不同。 ?
?總結 該數組是定時器系統的核心數據結構,通過將回調函數存儲在保留內存區域,確保系統在休眠 / 喚醒周期中能持續管理定時任務。這種設計在資源受限的嵌入式系統中尤為重要,既能高效利用內存,又能保證關鍵狀態不丟失。
其中宏定義如下 :
#define APP_EASY_TIMER_HND_TO_MSG_ID(timer_id) (timer_id - 1 + APP_TIMER_API_MES0)
#define APP_EASY_TIMER_MSG_ID_TO_HND(timer_msg) (timer_msg - APP_TIMER_API_MES0 + 1)
#define APP_EASY_TIMER_HND_TO_IDX(timer_id) (timer_id - 1)
#define APP_EASY_TIMER_IDX_TO_HND(timer_id) (timer_id + 1)
#define APP_EASY_TIMER_HND_IS_VALID(timer_id) ((timer_id > 0) && (timer_id <= APP_TIMER_MAX_NUM))