目錄
一、硬件抽象層簡介:
(1)HAL 硬件抽象層是什么?
(2)通俗易懂的解釋:
(3)具體例子:
二、硬件抽象層HAL:
(1)HAL文件目錄及工程結構:
(2)工程結構說明:
(3)HAL文件使用:
一、硬件抽象層簡介:
(1)HAL 硬件抽象層是什么?
????????硬件抽象層(Hardware Abstraction Layer,簡稱 HAL) 是一種軟件設計模式,它的目的是將硬件的具體實現細節與軟件的其他部分隔離開來。簡單來說,HAL 就像是一個中間層,它站在軟件和硬件之間,讓軟件可以通過統一的接口來操作不同的硬件,而不需要關心硬件的具體實現細節。
(2)通俗易懂的解釋:
????????想象一下,你是一個廚師,你需要用不同的廚具來烹飪食物。如果你每次都要直接操作各種各樣的鍋、碗、瓢、盆,可能會很麻煩,尤其是當廚具的種類和品牌不同時。于是,你決定用一套統一的工具來操作這些廚具,比如用一個通用的鍋鏟來翻炒,用一個通用的勺子來攪拌。這樣,你就不需要記住每種廚具的具體操作方法,只需要掌握通用工具的使用方法即可。
????????在嵌入式系統中,HAL 就像是這套通用工具。它提供了一組統一的函數接口,讓軟件開發者可以通過這些接口來操作硬件,而不需要直接處理硬件的復雜細節。比如,你要控制一個 LED 燈亮起,你可以通過 HAL 提供的函數來實現,而不需要直接操作硬件寄存器。
(3)具體例子:
????????假設你有一個開發板,上面有多種硬件設備,比如 LED 燈、按鍵、串口等。如果沒有 HAL,你可能需要直接操作每個硬件設備的寄存器來控制它們,這會非常復雜,尤其是當硬件設備的類型和型號不同時。有了 HAL,你可以通過調用 HAL 提供的函數來控制這些設備,比如:
// 通過 HAL 函數點亮 LED 燈
HAL_LED_On(LED1);// 通過 HAL 函數讀取按鍵狀態
uint8_t keyState = HAL_KEY_Read(KEY1);
????????這些函數內部已經封裝了對硬件寄存器的操作,你只需要調用這些函數即可,不需要關心硬件的具體實現。
二、硬件抽象層HAL:
(1)HAL文件目錄及工程結構:
HAL的文件位于 C:\Texas Instruments\Z-Stack 3.0.2\Components\hal 文件夾中。
進入 HAL 文件,如下:
其中:
組名稱 | 說明 |
---|---|
common | 存放公共文件。 |
include | 存放驅動程序接口文件。 |
target | 存放各種類型主板的驅動程序源文件。 |
打開這三個文件夾,有:
文件名 | 說明 |
---|---|
hal_assert.c | 實現條件合法性判斷功能的源代碼文件。 |
hal_drivers.c | 硬件抽象層 HAL 任務初始化及事件處理函數所在文件,是 HAL 的入口源文件。 |
文件名 | 說明 |
---|---|
hal_adc.h | ADC(模擬數字轉換)驅動程序頭文件。 |
hal_assert.h | 條件合法性判斷功能的頭文件。 |
hal_board.h | 各種類型主板的硬件資源配置頭文件。 |
hal_defs.h | 通用定義頭文件。 |
hal_drivers.h | HAL任務初始化及事件處理函數的頭文件。 |
hal_flash.h | FLASH(存儲器)驅動程序頭文件。 |
hal_key.h | 按鍵驅動程序頭文件。 |
hal_lcd.h | 顯示屏驅動程序頭文件。 |
hal_led.h | LED驅動程序頭文件。 |
hal_rpc.h | RPC(Remote Procedure Call,遠程過程調用)驅動程序頭文件。 |
hal_sleep.h | 休眠功能驅動程序頭文件。 |
hal_timer.h | 定時器驅動程序頭文件。 |
hal_uart.h | 串口驅動程序頭文件。 |
文件夾名稱 | 說明 |
---|---|
CC2530EB | 針對芯片主控為CC2530的評估板相關的驅動程序。 |
CC2530USB | 針對芯片主控為CC2530的帶USB轉串口評估板的驅動程序。 |
CC2530ZNP | 針對芯片主控為CC2530的ZNP(ZigBee And Processor)評估板的驅動程序。 |
CC2538 | 針對芯片主控為CC2538的評估板的驅動程序。 |
CC2538ZNP | 針對芯片主控為CC2538的ZNP(ZigBee And Processor)評估板的驅動程序。 |
?以第一個為例,打開CC2530EB有:
對應類型主板的接口源文件和程序驅動源文件。
文件名 | 作用說明 |
---|---|
hal_uart_dma.c | 實現串口通信的直接內存訪問(DMA)功能,用于高效的數據傳輸。 |
hal_uart_isr.c | 包含串口通信的中斷服務例程(ISR),處理串口的中斷事件。 |
hal_adc.c | 提供模數轉換(ADC)功能的實現,用于采集模擬信號并轉換為數字數據。 |
hal_aes.c | 提供高級加密標準(AES)加密算法的實現,用于數據加密和解密操作。 |
hal_board_cfg.h | 包含評估板硬件配置的宏定義和配置參數,用于適配具體的硬件平臺。 |
hal_ccm.h | 提供CCM(Counter with CBC-MAC)加密模式的相關定義和函數聲明。 |
hal_dma.c | 實現直接內存訪問(DMA)功能,用于在不同內存區域或外設之間直接傳輸數據。 |
hal_dma.h | 提供DMA功能的函數聲明和相關宏定義,用于配置和控制DMA操作。 |
hal_flash.c | 提供對存儲器(FLASH)的操作函數,包括讀取、寫入和擦除等操作。 |
hal_key.c | 實現按鍵輸入的相關功能,包括按鍵掃描和狀態檢測。 |
hal_lcd.c | 提供液晶顯示屏(LCD)的驅動函數,用于顯示字符或圖形。 |
hal_led.c | 實現LED控制功能,包括點亮、熄滅和閃爍等操作。 |
hal_led_cfg.h | 包含LED配置的宏定義,如LED引腳定義和初始狀態設置。 |
hal_mac_cfg.h | 包含MAC層配置的宏定義,用于設置無線通信的相關參數。 |
hal_mcu.h | 提供微控制器(MCU)相關的函數聲明和宏定義,如時鐘配置和復位操作。 |
hal_oad.c | 實現無線固件更新(Over-the-Air Download,OAD)功能的源代碼。 |
hal_oad.h | 提供OAD功能的函數聲明和相關宏定義,用于無線固件更新操作。 |
hal_ota.c | 提供OTA(Over-the-Air)升級功能的實現,用于遠程更新設備固件。 |
hal_ota.h | 包含OTA功能的函數聲明和相關定義,用于支持遠程固件升級。 |
hal_sleep.c | 實現低功耗睡眠模式的相關功能,用于節能操作。 |
hal_startup.c | 包含系統啟動相關的代碼,如初始化和主函數入口等。 |
hal_timer.c | 提供定時器功能的實現,用于精確的時間控制和延遲操作。 |
hal_types.h | 定義常用的類型和數據結構,為其他模塊提供統一的數據類型聲明。 |
hal_uart.c | 實現串口通信的基本功能,包括數據發送和接收操作。 |
(2)工程結構說明:
打開 Z-Stack 中的一個示例工程:
?打開HAL目錄,分別展開Common、Include和Target目錄如下:
文件夾路徑 | 作用說明 |
---|---|
HAL/Common | 存放公共文件,包含通用函數實現和工具代碼,可在不同項目和硬件平臺上復用。 |
HAL/Include | 存放各種驅動程序的接口文件,定義函數原型、數據結構和宏等,供其他模塊調用和配置。 |
HAL/Target | 存放針對特定類型主板的驅動程序源文件和配置文件,每個子文件夾對應不同的硬件平臺。 |
HAL/Target/CC2530EB | 針對主控芯片為CC2530的評估板,包含該平臺下的驅動實現和配置文件。 |
HAL/Target/CC2530USB | 針對帶USB轉串口的CC2530評估板,包含該平臺下的驅動實現和配置文件。 |
HAL/Target/CC2530ZNP | 針對CC2530的ZNP評估板,包含該平臺下的驅動實現和配置文件。 |
HAL/Target/CC2538 | 針對主控芯片為CC2538的評估板,包含該平臺下的驅動實現和配置文件。 |
HAL/Target/CC2538ZNP | 針對CC2538的ZNP評估板,包含該平臺下的驅動實現和配置文件。 |
(3)HAL文件使用:
初始化函數:
HAL中的初始化函數位于hal_drivers.c文件為Hal_Init(),如下:
/*************************************************************************************************** @fn Hal_Init** @brief Hal初始化函數。** @param task_id - Hal TaskId** @return None**************************************************************************************************/
void Hal_Init( uint8 task_id )
{/*注冊任務ID */Hal_TaskID = task_id;#ifdef CC2591_COMPRESSION_WORKAROUNDosal_start_reload_timer( Hal_TaskID, PERIOD_RSSI_RESET_EVT, PERIOD_RSSI_RESET_TIMEOUT );
#endif
}
驅動程序初始化:
HAL中的驅動程序初始化函數位于hal_drivers.c文件為HalDriverInit(),如下:
/*************************************************************************************************** @fn Hal_DriverInit** @brief 初始化硬件(HW)- 這些需要在任何人之前初始化。* 該函數負責按正確的順序初始化各個硬件模塊,確保系統正常運行。** @param task_id - Hal TaskId(任務ID),用于標識HAL相關的任務或事件。** @return None**************************************************************************************************/
void HalDriverInit(void)
{/* TIMER(定時器)*/
#if (defined HAL_TIMER) && (HAL_TIMER == TRUE)// 如果定義了HAL_TIMER且其值為TRUE,則初始化定時器模塊// 定時器模塊用于提供精確的時間控制和延遲功能
#endif/* ADC(模數轉換)*/
#if (defined HAL_ADC) && (HAL_ADC == TRUE)// 如果定義了HAL_ADC且其值為TRUE,則初始化ADC模塊// ADC模塊用于將模擬信號轉換為數字信號HalAdcInit();
#endif/* DMA(直接內存訪問)*/
#if (defined HAL_DMA) && (HAL_DMA == TRUE)// 如果定義了HAL_DMA且其值為TRUE,則初始化DMA模塊// DMA模塊允許在外設和內存之間直接傳輸數據,而無需CPU干預,提高效率// 必須在調用任何使用DMA模塊的初始化函數之前調用此函數HalDmaInit();
#endif/* AES(高級加密標準)*/
#if (defined HAL_AES) && (HAL_AES == TRUE)// 如果定義了HAL_AES且其值為TRUE,則初始化AES模塊// AES模塊用于數據加密和解密操作HalAesInit();
#endif/* LCD(液晶顯示屏)*/
#if (defined HAL_LCD) && (HAL_LCD == TRUE)// 如果定義了HAL_LCD且其值為TRUE,則初始化LCD模塊// LCD模塊用于顯示字符或圖形HalLcdInit();
#endif/* LED(發光二極管)*/
#if (defined HAL_LED) && (HAL_LED == TRUE)// 如果定義了HAL_LED且其值為TRUE,則初始化LED模塊// LED模塊用于控制LED的點亮、熄滅和閃爍等操作HalLedInit();
#endif/* UART(通用異步收發傳輸器)*/
#if (defined HAL_UART) && (HAL_UART == TRUE)// 如果定義了HAL_UART且其值為TRUE,則初始化UART模塊// UART模塊用于串行通信,實現數據的發送和接收HalUARTInit();
#endif/* KEY(按鍵)*/
#if (defined HAL_KEY) && (HAL_KEY == TRUE)// 如果定義了HAL_KEY且其值為TRUE,則初始化按鍵模塊// 按鍵模塊用于檢測按鍵輸入和處理按鍵事件HalKeyInit();
#endif/* SPI(串行外設接口)*/
#if (defined HAL_SPI) && (HAL_SPI == TRUE)// 如果定義了HAL_SPI且其值為TRUE,則初始化SPI模塊// SPI模塊用于與外部設備進行高速串行通信HalSpiInit();
#endif/* HID(人機接口設備)*/
#if (defined HAL_HID) && (HAL_HID == TRUE)// 如果定義了HAL_HID且其值為TRUE,則初始化HID模塊// HID模塊用于與人機接口設備(如鍵盤、鼠標等)進行通信usbHidInit();
#endif
}
????????在開發過程中可以需求來使用指定的外設,例如設置 HAL_TIMER = TRUE、HAL_ADC = TRUE、HAL_DMA = TRUE、HAL_AES =?TRUE、HAL_LCD = TRUE、HAL_LED =?TRUE、HAL_UART =?TRUE 等等,指定對應的外設對應的宏。如果不用用到,那么就不用定義。在定義了對應的宏后就會執行對應的初始化。如果需要增加這里沒有的外設,可以按照協議棧的這個架構新增外設。
事件處理:
????????HAL的事件處理函數為位于hal_drivers.c文件中的Hal_ProcessEvent(),它的主要作用是處理HAL層的事件,位置如下:
/*************************************************************************************************** @fn Hal_ProcessEvent** @brief 處理 HAL 相關的事件。* 該函數根據傳入的事件標志,調用相應的處理函數或執行特定的操作。** @param task_id - Hal TaskId(任務ID),用于標識HAL相關的任務。* events - 需要處理的事件標志,每個標志代表一種特定的事件。** @return 返回未處理的事件標志。**************************************************************************************************/
uint16 Hal_ProcessEvent(uint8 task_id, uint16 events)
{uint8 *msgPtr; // 用于接收消息的指針(void)task_id; // 有意未引用的參數,避免編譯器警告// 檢查是否有系統消息事件if (events & SYS_EVENT_MSG){msgPtr = osal_msg_receive(Hal_TaskID); // 接收消息while (msgPtr){/* 在這里做些事情 - 目前,只是釋放消息并繼續 *//* 釋放消息內存 */osal_msg_deallocate(msgPtr);/* 接收下一條消息 */msgPtr = osal_msg_receive(Hal_TaskID);}// 清除 SYS_EVENT_MSG 事件標志,表示該事件已處理return events ^ SYS_EVENT_MSG;}#if (defined HAL_BUZZER) && (HAL_BUZZER == TRUE)// 檢查是否有蜂鳴器事件if (events & HAL_BUZZER_EVENT){HalBuzzerStop(); // 停止蜂鳴器// 清除 HAL_BUZZER_EVENT 事件標志,表示該事件已處理return events ^ HAL_BUZZER_EVENT;}
#endif#ifdef CC2591_COMPRESSION_WORKAROUND// 檢查是否有周期性 RSSI 重置事件if (events & PERIOD_RSSI_RESET_EVT){macRxResetRssi(); // 重置 RSSI// 清除 PERIOD_RSSI_RESET_EVT 事件標志,表示該事件已處理return (events ^ PERIOD_RSSI_RESET_EVT);}
#endif// 檢查是否有 LED 閃爍事件if (events & HAL_LED_BLINK_EVENT){
#if (defined (BLINK_LEDS)) && (HAL_LED == TRUE)HalLedUpdate(); // 更新 LED 狀態
#endif /* BLINK_LEDS && HAL_LED */// 清除 HAL_LED_BLINK_EVENT 事件標志,表示該事件已處理return events ^ HAL_LED_BLINK_EVENT;}// 檢查是否有按鍵事件if (events & HAL_KEY_EVENT){
#if (defined HAL_KEY) && (HAL_KEY == TRUE)/* 檢查按鍵狀態 */HalKeyPoll();/* 如果中斷已禁用,則安排下一次輪詢 */if (!Hal_KeyIntEnable){osal_start_timerEx(Hal_TaskID, HAL_KEY_EVENT, 100);}
#endif// 清除 HAL_KEY_EVENT 事件標志,表示該事件已處理return events ^ HAL_KEY_EVENT;}#if defined POWER_SAVING// 檢查是否有睡眠定時器事件if (events & HAL_SLEEP_TIMER_EVENT){halRestoreSleepLevel(); // 恢復睡眠級別// 清除 HAL_SLEEP_TIMER_EVENT 事件標志,表示該事件已處理return events ^ HAL_SLEEP_TIMER_EVENT;}// 檢查是否有電源管理保持事件if (events & HAL_PWRMGR_HOLD_EVENT){(void)osal_pwrmgr_task_state(Hal_TaskID, PWRMGR_HOLD);(void)osal_stop_timerEx(Hal_TaskID, HAL_PWRMGR_CONSERVE_EVENT);(void)osal_clear_event(Hal_TaskID, HAL_PWRMGR_CONSERVE_EVENT);// 清除 HAL_PWRMGR_HOLD_EVENT 和 HAL_PWRMGR_CONSERVE_EVENT 事件標志return (events & ~(HAL_PWRMGR_HOLD_EVENT | HAL_PWRMGR_CONSERVE_EVENT));}// 檢查是否有電源管理節能事件if (events & HAL_PWRMGR_CONSERVE_EVENT){(void)osal_pwrmgr_task_state(Hal_TaskID, PWRMGR_CONSERVE);// 清除 HAL_PWRMGR_CONSERVE_EVENT 事件標志,表示該事件已處理return events ^ HAL_PWRMGR_CONSERVE_EVENT;}
#endif// 如果沒有處理任何事件,返回 0return 0;
}
HAL輪詢:
??HAL的HAL輪詢函數為位于hal_drivers.c文件中的Hal_ProcessPoll(),它的主要作用為輪詢那些不支持中斷或者需要定期檢查狀態的硬件模塊,位置如下:
/*************************************************************************************************** @fn Hal_ProcessPoll** @brief 該例程將被OSAL調用來輪詢UART、TIMER等硬件模塊的狀態。* 主要用于輪詢那些不支持中斷或者需要定期檢查狀態的硬件模塊。** @param task_id - Hal TaskId(任務ID),用于標識HAL相關的任務。** @return None**************************************************************************************************/
void Hal_ProcessPoll()
{
#if defined(POWER_SAVING)/* 允許在下一個OSAL事件循環之前進入睡眠模式 *//* 這個宏可能用于節能模式,允許系統在空閑時進入低功耗狀態 */ALLOW_SLEEP_MODE();
#endif/* UART輪詢 */
#if (defined HAL_UART) && (HAL_UART == TRUE)/* 如果定義了HAL_UART且其值為TRUE,則調用HalUARTPoll函數輪詢UART狀態 *//* UART輪詢用于檢查串口通信的狀態,如是否有數據可讀取或發送緩沖區是否為空 */HalUARTPoll();
#endif/* SPI輪詢 */
#if (defined HAL_SPI) && (HAL_SPI == TRUE)/* 如果定義了HAL_SPI且其值為TRUE,則調用HalSpiPoll函數輪詢SPI狀態 *//* SPI輪詢用于檢查SPI通信的狀態,如是否有數據傳輸完成或是否有錯誤發生 */HalSpiPoll();
#endif/* HID輪詢 */
#if (defined HAL_HID) && (HAL_HID == TRUE)/* 如果定義了HAL_HID且其值為TRUE,則調用usbHidProcessEvents函數處理HID事件 *//* HID輪詢用于處理人機接口設備(如鍵盤、鼠標等)的事件,如按鍵按下或移動事件 */usbHidProcessEvents();
#endif
}