1、STM32 之一 HAL庫、標準外設庫、LL庫_ZC·Shou的博客-CSDN博客_ll庫(仔細閱讀)
2、STM32標準外設庫、 HAL庫、LL庫 - King先生 - 博客園??
3、STM32 之 HAL庫_戈 揚的博客(仔細閱讀)
4、STM32 LL 為什么比 HAL 高效?_strongerHuang的博客-CSDN博客
5、標準庫和HAL庫到底有什么不同?怎么選? - 知乎
6、從代碼層面分析STM32 標準庫和HAL庫的差異_嵌入式@hxydj的博客-CSDN博客_hal庫和寄存器版本區別
7、代碼分析【STM32】入門(九):HAL庫學習_郭老二的博客-CSDN博客_stm32 hal庫
ST 為開發者提供了非常方便的開發庫。到目前為止,有
標準外設(SPL 庫)、
硬件抽象層HAL 庫、
低層LL 庫?三種。
前兩者都是常用的庫,后面的 LL 庫是 ST 最近才添加,隨 HAL 源碼包一起提供,目前支持的芯片也偏少。各庫如下所示:?
?
?串口通信實驗包含的文件區別:
如上左邊標準庫、右邊HAL:
具體代碼區別:
主函數
?標準庫
#include "led.h"
#include "delay.h"
#include "key.h"
#include "sys.h"
#include "usart.h"
int main(void)
{
u16 t;
u16 len;
u16 times=0;
delay_init(); //延時函數初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //設置NVIC中斷分組2:2位搶占優先級,2位響應優先級
uart_init(115200); //串口初始化為115200
LED_Init(); //LED端口初始化
KEY_Init(); //初始化與按鍵連接的硬件接口
while(1)
{
if(USART_RX_STA&0x8000)
{
len=USART_RX_STA&0x3fff;//得到此次接收到的數據長度
printf("\r\n您發送的消息為:\r\n\r\n");
for(t=0;t<len;t++)
{
USART_SendData(USART1, USART_RX_BUF[t]);//向串口1發送數據
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);//等待發送結束
}
printf("\r\n\r\n");//插入換行
USART_RX_STA=0;
}else
{
times++;
if(times%5000==0)
{
printf("\r\n精英STM32開發板 串口實驗\r\n");
printf("正點原子@ALIENTEK\r\n\r\n");
}
if(times%200==0)printf("請輸入數據,以回車鍵結束\n");
if(times%30==0)LED0=!LED0;//閃爍LED,提示系統正在運行.
delay_ms(10);
}
}
}
HAL:
-
#include "sys.h"
-
#include "delay.h"
-
#include "usart.h"
-
#include "led.h"
-
#include "key.h"
-
int main(void)
-
{
-
u8 len;
-
u16 times=0;
-
HAL_Init(); //初始化HAL庫
-
Stm32_Clock_Init(RCC_PLL_MUL9); //設置時鐘,72M
-
delay_init(72); //初始化延時函數
-
uart_init(115200); //初始化串口
-
LED_Init(); //初始化LED
-
KEY_Init(); //初始化按鍵
-
while(1)
-
{
-
if(USART_RX_STA&0x8000)
-
{
-
len=USART_RX_STA&0x3fff;//得到此次接收到的數據長度
-
printf("\r\n您發送的消息為:\r\n");
-
HAL_UART_Transmit(&UART1_Handler,(uint8_t*)USART_RX_BUF,len,1000); //發送接收到的數據
-
while(__HAL_UART_GET_FLAG(&UART1_Handler,UART_FLAG_TC)!=SET); //等待發送結束
-
printf("\r\n\r\n");//插入換行
-
USART_RX_STA=0;
-
}else
-
{
-
times++;
-
if(times%5000==0)
-
{
-
printf("\r\nALIENTEK 精英STM32開發板 串口實驗\r\n");
-
printf("正點原子@ALIENTEK\r\n\r\n\r\n");
-
}
-
if(times%200==0)printf("請輸入數據,以回車鍵結束\r\n");
-
if(times%30==0)LED0=!LED0;//閃爍LED,提示系統正在運行.
-
delay_ms(10);
-
}
-
}
-
}
?
1?STM32的三種開發方式
通常新手在入門STM32的時候,首先都要先選擇一種要用的開發方式,不同的開發方式會導致你編程的架構是完全不一樣的。一般大多數都會選用標準庫和HAL庫,而極少部分人會通過直接配置寄存器進行開發。網上關于標準庫、HAL庫的描述相信是數不勝數。可是一個對于很多剛入門的朋友還是沒法很直觀的去真正了解這些不同開發發方式彼此之間的區別,所以筆者想以一種非常直白的方式,用自己的理解去將這些東西表述出來,如果有描述的不對的地方或者是不同意見的也可以大家提出。
一、直接配置寄存器
不少先學了51的朋友可能會知道,會有一小部分人或是教程是通過匯編語言直接操作寄存器實現功能的,這種方法到了STM32就變得不太容易行得通了,因為STM32的寄存器數量是51單片機的十數倍,如此多的寄存器根本無法全部記憶,開發時需要經常的翻查芯片的數據手冊,此時直接操作寄存器就變得非常的費力了。但還是會有很小一部分人,喜歡去直接操作寄存器,因為這樣更接近原理,知其然也知其所以然。
二、標準(外設)SPL庫
上面也提到了,STM32有非常多的寄存器,而導致了開發困難,所以為此ST公司就為每款芯片都編寫了一份庫文件,也就是工程文件里stm32F1xx…之類的。在這些 .c .h文件中,包括一些常用量的宏定義,把一些外設也通過結構體變量封裝起來,如GPIO口時鐘等。所以我們只需要配置結構體變量成員就可以修改外設的配置寄存器,從而選擇不同的功能。也是目前最多人使用的方式,也是學習STM32接觸最多的一種開發方式,我也就不多闡述了。
三、HAL庫
HAL庫是ST公司目前主力推的開發方式,全稱就是Hardware Abstraction Layer(硬件抽象層)。庫如其名,很抽象,一眼看上去不太容易知道他的作用是什么。它的出現比標準庫要晚,但其實和標準庫一樣,都是為了節省程序開發的時期,而且HAL庫尤其的有效,如果說標準庫把實現功能需要配置的寄存器集成了,那么HAL庫的一些函數甚至可以做到某些特定功能的集成。也就是說,同樣的功能,標準庫可能要用幾句話,HAL庫只需用一句話就夠了。并且HAL庫也很好的解決了程序移植的問題,不同型號的stm32芯片它的標準庫是不一樣的,例如在F4上開發的程序移植到F3上是不能通用的,而使用HAL庫,只要使用的是相通的外設,程序基本可以完全復制粘貼,注意是相通外設,意思也就是不能無中生有,例如F7比F3要多幾個定時器,不能明明沒有這個定時器卻非要配置,但其實這種情況不多,絕大多數都可以直接復制粘貼。是而且使用ST公司研發的STMcube軟件,可以通過圖形化的配置功能,直接生成整個使用HAL庫的工程文件,可以說是方便至極,但是方便的同時也造成了它執行效率的低下,在各種論壇帖子真的是被吐槽的數不勝數。
個人理解:hal統一了define規范和函數規范,讓移植只需要改幾個參數即可
STM32中HAL庫與標準庫的區別
1.HAL庫就是封裝的比較猛,移植性比較強,標準庫就是將寄存器封裝好,移植性沒有HAL好。
2.HAL庫可以用ST的軟件CUBE生成初始化工程。
3.HAL庫最方便的就是可以用CUBEMX自動生成代碼,動態的調用資源,不會出現地城配置上的沖突。
4.其實就是兩種庫的區別,Hal庫更加全面一點,目前STM32官方也在主推Hal庫,目前STMcubemx軟件可以直接生成HAL庫代碼,非常方便編程,易移植。
5.HAL庫是ST近年來推出的新庫,可以直接在CubeMX下生成例程,并且各個不同型號的STM32之間的函數差異也減少了。
6.HAL庫是現在ST主推的庫,標準庫現在已經不更新了。HAL庫做了更深的封裝,可以很方便的移植在F0/F1/F3/F4/F7的各個系列的芯片上。
7.HAL庫通用型強,但是效率稍微低一點,標準庫效率高。
8.本質上是一樣的,就是配置寄存器,只是HAL庫將應用層與驅動層分的比較明確。
9.HAL庫設計進一步降低了API對硬件的依賴性,它借鑒了OS中驅動程序的思路,使得API的通用性更強。 能使用ST的CubeMX圖形化界面來生成軟件框架,它和CubeMX生成的軟件代碼完全兼容。減少了程序員的負擔,同時代碼也更規范。 至于與原庫函數之間的使用差異, 關鍵還是要掌握內核及外設的工作原理,如果熟悉了硬件的工作原理,這些庫函數還是很好用的。
10.HAL庫似乎是為初學者而制定,但這也許是STM32的未來所在。
11.HAL和STD庫最大的區別是移植性上的區別,HAL相當于在標準庫上在加以封裝了。增強了移植性,STD庫是在寄存器的基礎上封裝了一次。12.hal庫封裝的更想arduino,移植性強,相對效率就低一些,在時間就是金錢的現在,hal庫無疑是很好的選擇。
13.HAL庫和標準庫都是對寄存器操作的封裝,但是這些庫的函數不同在HAL庫每個.c文件的開頭會介紹這個庫里面包含哪些函數,這些函數的用途,可以留意一下。
14.標準庫是STM32最早推出的庫,應用非常廣泛,但是比較新的F7和H7等系列已經不支持了。
HAL庫是官方主推的庫,目前支持所有系列,相對效率沒有標準庫高,但是各個系列之間的兼容性很好,而且能夠配合STM32CubeMX進行使用。由于官方現在不在更新和支持標準庫了,所有精力都放在了HAL庫和LL庫上了,所以建議今后學習和應用還是以HAL庫和LL庫為主要對象。
15.HAL庫和標準庫的最大區別就是減少了不同系列器件之間的庫函數層差異,并且可以直接用cubemx生成。
16.HAL移植性比較強,可以通過cubemx生成代碼,不過效率比較低標準庫更像寄存器的操作,感覺更符合對寄存器使用的理解。
17.HAL庫移植性比較好,操作比較簡單。標準庫移植性沒那么方便,操作比較復雜,直接對寄存器的操作。
18.HAL庫的封裝比較多,大部分都是面向對象的設計,移植比較簡單。
19.HAL的優點就是用API的設計,十分方便移植,而且操作也簡單。
20.HAL的移植性是最好的,但是標準庫不太方便移植,所以ST出了一個HAL庫。
————————————————
版權聲明:本文為CSDN博主「宇豪@」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/qq_44403143/article/details/117650293
MCU的替換就意味著程序的移植。對一個傳感器而言,替換MCU時,其實替換的是底層,而上層的邏輯是保持一致的。只要保證底層能脫離MCU的寄存器那么程序移植起來就非常容易。
HAL庫是st公司為了更方便地進行stm32之間的移植而開發的庫,通用性很強,在不同的兩款stm32芯片之間的移植基本上不需要修改。之前使用的標準庫都是基于寄存器的操作。寄存器版本使用較麻煩,每個設置都要去查看芯片datasheet,好處是可以讓你熟悉芯片的寄存器配置。庫函數是基于寄存器版本進行二次封裝后推出的,它的優勢就是寄存器版本的劣勢,方便了使用,不再需要手動去配置寄存器,使用更方便了。但是它的劣勢就是HAL的優勢,就是每次修改MCU功能,都需要手動去修改功能,而且自己修改也不能保證正確性,程序代碼在不同MCU之間的移植性不強。
HAL是Hardware Abstraction Layer的縮寫,中文名稱是:硬件抽象層,HAL庫工程一般使用Cube軟件來生成工程。HAL庫是ST公司為STM32的MCU最新推出的抽象層嵌入式軟件,更方便的實現跨STM32產品的最大可移植性。優勢就是不需要開發工程師再關注所用MCU型號,只需要專注所以要的功能軟件開發工作。而且是未來主推的方向,正在不斷的推出更新。HAL庫推出的同時,也加入了很多第三方的中間件,有RTOS,USB,TCP / IP和圖形等等。和標準庫對比起來,STM32的HAL庫更加的抽象,ST最終的目的是要實現在STM32系列MCU之間無縫移植,甚至在其他MCU也能實現快速移植。
HAL 庫相對標準庫更加復雜更加繁瑣,但是HAL庫能夠適應不同的st芯片的應用,所以應用能力更加廣泛,但是是直接和硬件聯系的,所以比起標準庫來說,更難上手。
文章下方附學習資源,自助領取。
摘要:通常新手在入門STM32的時候,首先都要先選擇一種要用的開發方式,不同的開發方式會導致你編程的架構是完全不一樣的。一般大多數都會選用標準庫和HAL庫,而極少部分人會通過直接配置寄存器進行開發。網上關于標準庫、HAL庫的描述相信是數不勝數。可是一個對于很多剛入門的朋友還是沒法很直觀的去真正了解這些不同開發發方式彼此之間的區別,所以筆者想以一種非常直白的方式,用自己的理解去將這些東西表述出來,如果有描述的不對的地方或者是不同意見的也可以大家提出。
一、配置寄存器
不少先學了51的朋友可能會知道,會有一小部分人或是教程是通過匯編語言直接操作寄存器實現功能的,這種方法到了STM32就變得不太容易行得通了,因為STM32的寄存器數量是51單片機的十數倍,如此多的寄存器根本無法全部記憶,開發時需要經常的翻查芯片的數據手冊,此時直接操作寄存器就變得非常的費力了。但還是會有很小一部分人,喜歡去直接操作寄存器,因為這樣更接近原理,知其然也知其所以然。
二、標準庫
上面也提到了,STM32有非常多的寄存器,而導致了開發困難,所以為此ST公司就為每款芯片都編寫了一份庫文件,也就是工程文件里stm32F1xx.....
之類的。在這些.c .h
文件中,包括一些常用量的宏定義,把一些外設也通過結構體變量封裝起來,如GPIO
口時鐘等。所以我們只需要配置結構體變量成員就可以修改外設的配置寄存器,從而選擇不同的功能。也是目前最多人使用的方式,也是學習STM32接觸最多的一種開發方式,我也就不多闡述了。
三、HAL庫
HAL庫是ST公司目前主力推的開發方式,全稱就是Hardware Abstraction Layer
(抽象印象層)。庫如其名,很抽象,一眼看上去不太容易知道他的作用是什么。它的出現比標準庫要晚,但其實和標準庫一樣,都是為了節省程序開發的時期,而且HAL庫尤其的有效,如果說標準庫把實現功能需要配置的寄存器集成了,那么HAL庫的一些函數甚至可以做到某些特定功能的集成。也就是說,同樣的功能,標準庫可能要用幾句話,HAL庫只需用一句話就夠了。并且HAL庫也很好的解決了程序移植的問題,不同型號的stm32芯片它的標準庫是不一樣的,例如在F4上開發的程序移植到F3上是不能通用的,而使用HAL庫,只要使用的是相通的外設,程序基本可以完全復制粘貼,注意是相通外設,意思也就是不能無中生有,例如F7比F3要多幾個定時器,不能明明沒有這個定時器卻非要配置,但其實這種情況不多,絕大多數都可以直接復制粘貼。是而且使用ST公司研發的STMcube軟件
,可以通過圖形化的配置功能,直接生成整個使用HAL庫的工程文件,可以說是方便至極,但是方便的同時也造成了它執行效率的低下,在各種論壇帖子真的是被吐槽的數不勝數。
四、總結
綜合上面說的,其實筆者還是強烈推薦HAL庫的,理由有二:第一、 F7系列開始 ST公司就已近開始停止更新標準庫,也就是F7開始包括F7已經不能用標準庫了,公司對于主打HAL庫的目的已經非常明顯了。第二、追求更方便、追求模塊化向來是世界的潮流,更方便的HAL庫一定會迅速發展,低效的短板遲早會被硬件高度集成化所彌補。
當然啦,不能只學習HAL庫,底層的原理必需是要懂的,這是每個學有所成的人都公認的事實,HAL庫也不是萬能的,結合對底層的理解相信一定會讓你的開發水準大大提高。
五、STM32 HAL庫與標準庫的區別
1.句柄
在STM32的標準庫中,假設我們要初始化一個外設(這里以USART為例) 我們首先要初始化他們的各個寄存器。在標準庫中,這些操作都是利用固件庫結構體變量+固件庫Init函數實現的:
-
USART_InitTypeDef USART_InitStructure;
-
USART_InitStructure.USART_BaudRate = bound;//串口波特率
-
USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字長為8位數據格式
-
USART_InitStructure.USART_StopBits = USART_StopBits_1;//一個停止位
-
USART_InitStructure.USART_Parity = USART_Parity_No;//無奇偶校驗位
-
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//無硬件數據流控制
-
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收發模式
-
USART_Init(USART3, &USART_InitStructure); //初始化串口1
可以看到,要初始化一個串口,需要對六個位置進行賦值,然后引用Init函數,并且USART_InitStructure
并不是一個全局結構體變量,而是只在函數內部的局部變量,初始化完成之后,USART_InitStructure
就失去了作用。
而在HAL庫中,同樣是USART初始化結構體變量,我們要定義為全局變量。
UART_HandleTypeDef UART1_Handler;
右鍵查看結構體成員
-
typedef struct
-
{
-
USART_TypeDef *Instance; /*!< UART registers base address */
-
UART_InitTypeDef Init; /*!< UART communication parameters */
-
uint8_t *pTxBuffPtr; /*!< Pointer to UART Tx transfer Buffer */
-
uint16_t TxXferSize; /*!< UART Tx Transfer size */
-
uint16_t TxXferCount; /*!< UART Tx Transfer Counter */
-
uint8_t *pRxBuffPtr; /*!< Pointer to UART Rx transfer Buffer */
-
uint16_t RxXferSize; /*!< UART Rx Transfer size */
-
uint16_t RxXferCount; /*!< UART Rx Transfer Counter */
-
DMA_HandleTypeDef *hdmatx; /*!< UART Tx DMA Handle parameters */
-
DMA_HandleTypeDef *hdmarx; /*!< UART Rx DMA Handle parameters */
-
HAL_LockTypeDef Lock; /*!< Locking object */
-
__IO HAL_UART_StateTypeDef State; /*!< UART communication state */
-
__IO uint32_t ErrorCode; /*!< UART Error code */
-
}UART_HandleTypeDef;
我們發現,與標準庫不同的是,該成員不僅包含了之前標準庫就有的六個成員(波特率,數據格式等),還包含過采樣、(發送或接收的)數據緩存、數據指針、串口 DMA 相關的變量、各種標志位等等要在整個項目流程中都要設置的各個成員。
該UART1_Handler就被稱為串口的句柄 它被貫穿整個USART收發的流程,比如開啟中斷:
HAL_UART_Receive_IT(&UART1_Handler, (u8 *)aRxBuffer, RXBUFFERSIZE);
比如后面要講到的MSP
與Callback
回調函數:
-
void HAL_UART_MspInit(UART_HandleTypeDef *huart);
-
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);
在這些函數中,只需要調用初始化時定義的句柄UART1_Handler
就好。
2.MSP函數
MCU Specific Package
單片機的具體方案
MSP是指和MCU相關的初始化,引用一下正點原子的解釋,個人覺得說的很明白:
“我們要初始化一個串口,首先要設置和 MCU 無關的東西,例如波特率,奇偶校驗,停止位等,這些參數設置和 MCU 沒有任何關系,可以使用 STM32F1,也可以是?STM32F2/F3/F4/F7
上的串口。而一個串口設備它需要一個 MCU 來承載,例如用 STM32F4 來做承載,PA9 做為發送,PA10 做為接收,MSP 就是要初始化 STM32F4 的 PA9,PA10,配置這兩個引腳。所以 HAL驅動方式的初始化流程就是:?HAL_USART_Init()—>HAL_USART_MspInit()
,先初始化與 MCU無關的串口協議,再初始化與 MCU 相關的串口引腳。在 STM32 的 HAL 驅動中HAL_PPP_MspInit()作為回調,被?HAL_PPP_Init()
函數所調用。當我們需要移植程序到 STM32F1平臺的時候,我們只需要修改 HAL_PPP_MspInit 函數內容而不需要修改 HAL_PPP_Init 入口參數內容。
”
在HAL庫中,幾乎每初始化一個外設就需要設置該外設與單片機之間的聯系,比如IO口,是否復用等等,可見,HAL庫相對于標準庫多了MSP函數之后,移植性非常強,但與此同時卻增加了代碼量和代碼的嵌套層級。可以說各有利弊。
同樣,MSP函數又可以配合句柄,達到非常強的移植性:
void HAL_UART_MspInit(UART_HandleTypeDef *huart);
3年嵌入式物聯網學習資源整理分享:C語言、Linux開發、數據結構;軟件開發,STM32單片機、ARM硬件開發、物聯網通信開發、綜合項目開發教程資料;筆試面試真題。點擊下方插件
點我領取?s.pdb2.com/l/CMIsoKcnATFIF4M
3.Callback函數
類似于MSP函數,個人認為Callback函數主要幫助用戶應用層的代碼編寫。還是以USART為例,在標準庫中,串口中斷了以后,我們要先在中斷中判斷是否是接收中斷,然后讀出數據,順便清除中斷標志位,然后再是對數據的處理,這樣如果我們在一個中斷函數中寫這么多代碼,就會顯得很混亂:
-
void USART3_IRQHandler(void) //串口1中斷服務程序
-
{
-
u8 Res;
-
if(USART_GetITStatus(USART3, USART_IT_RXNE) != RESET) //接收中斷(接收到的數據必須是0x0d 0x0a結尾)
-
{
-
Res =USART_ReceiveData(USART3); //讀取接收到的數據
-
/*數據處理區*/
-
}
-
}
而在HAL庫中,進入串口中斷后,直接由HAL庫中斷函數進行托管:
-
void USART1_IRQHandler(void)
-
{
-
HAL_UART_IRQHandler(&UART1_Handler); //調用HAL庫中斷處理公用函數
-
/***************省略無關代碼****************/
-
}
HAL_UART_IRQHandler
這個函數完成了判斷是哪個中斷(接收?發送?或者其他?),然后讀出數據,保存至緩存區,順便清除中斷標志位等等操作。比如我提前設置了,串口每接收五個字節,我就要對這五個字節進行處理。在一開始我定義了一個串口接收緩存區:
-
/*HAL庫使用的串口接收緩沖,處理邏輯由HAL庫控制,接收完這個數組就會調用HAL_UART_RxCpltCallback進行處理這個數組*/
-
/*RXBUFFERSIZE=5*/
-
u8 aRxBuffer[RXBUFFERSIZE];
在初始化中,我在句柄里設置好了緩存區的地址,緩存大小(五個字節)
-
/*該代碼在HAL_UART_Receive_IT函數中,初始化時會引用*/
-
huart->pRxBuffPtr = pData;//aRxBuffer
-
huart->RxXferSize = Size;//RXBUFFERSIZE
-
huart->RxXferCount = Size;//RXBUFFERSIZE
則在接收數據中,每接收完五個字節,HAL_UART_IRQHandler
才會執行一次Callback函數:
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);
在這個Callback
回調函數中,我們只需要對這接收到的五個字節(保存在aRxBuffer[]中)進行處理就好了,完全不用再去手動清除標志位等操作。
所以說Callback函數是一個應用層代碼的函數,我們在一開始只設置句柄里面的各個參數,然后就等著HAL庫把自己安排好的代碼送到手中就可以了~
綜上,就是HAL庫的三個與標準庫不同的地方之個人見解。
個人覺得從這三個小點就可以看出HAL庫的可移植性之強大,并且用戶可以完全不去理會底層各個寄存器的操作,代碼也更有邏輯性。但與此帶來的是復雜的代碼量,極慢的編譯速度,略微低下的效率。看怎么取舍了。
五、HAL庫結構
說到STM32 的HAL庫,就不得不提STM32CubeMX,其作為一個可視化的配置工具,對于開發者來說,確實大大節省了開發時間。STM32CubeMX 就是以 HAL 庫為基礎的,且目前僅支持 HAL 庫及 LL 庫!首先看一下,官方給出的 HAL 庫的包含結構:
stm32f2xx.h 主要包含STM32同系列芯片的不同具體型號的定義,是否使用HAL庫等的定義,接著,其會根據定義的芯片信號包含具體的芯片型號的頭文件:
-
#if defined(STM32F205xx)
-
#include "stm32f205xx.h"
-
#elif defined(STM32F215xx)
-
#include "stm32f215xx.h"
-
#elif defined(STM32F207xx)
-
#include "stm32f207xx.h"
-
#elif defined(STM32F217xx)
-
#include "stm32f217xx.h"
-
#else
-
#error "Please select first the target STM32F2xx device used in your application (in stm32f2xx.h file)"
-
#endif
緊接著,其會包含 stm32f2xx_hal.h。
- stm32f2xx_hal.h:stm32f2xx_hal.c/h 主要實現HAL庫的初始化、系統滴答相關函數、及CPU的調試模式配置
- stm32f2xx_hal_conf.h :該文件是一個用戶級別的配置文件,用來實現對HAL庫的裁剪,其位于用戶文件目錄,不要放在庫目錄中。
接下來對于HAL庫的源碼文件進行一下說明,HAL庫文件名均以stm32f2xx_hal開頭,后面加上_外設或者模塊名(如:stm32f2xx_hal_adc.c):
-
庫文件:
-
stm32f2xx_hal_ppp.c/.h // 主要的外設或者模塊的驅動源文件,包含了該外設的通用API
-
stm32f2xx_hal_ppp_ex.c/.h // 外圍設備或模塊驅動程序的擴展文件。這組文件中包含特定型號或者系列的芯片的特殊API。以及如果該特定的芯片內部有不同的實現方式,則該文件中的特殊API將覆蓋_ppp中的通用API。
-
stm32f2xx_hal.c/.h // 此文件用于HAL初始化,并且包含DBGMCU、重映射和基于systick的時間延遲等相關的API
-
其他庫文件
-
用戶級別文件:
-
stm32f2xx_hal_msp_template.c // 只有.c沒有.h。它包含用戶應用程序中使用的外設的MSP初始化和反初始化(主程序和回調函數)。使用者復制到自己目錄下使用模板。
-
stm32f2xx_hal_conf_template.h // 用戶級別的庫配置文件模板。使用者復制到自己目錄下使用
-
system_stm32f2xx.c // 此文件主要包含SystemInit()函數,該函數在剛復位及跳到main之前的啟動過程中被調用。 **它不在啟動時配置系統時鐘(與標準庫相反)**。 時鐘的配置在用戶文件中使用HAL API來完成。
-
startup_stm32f2xx.s // 芯片啟動文件,主要包含堆棧定義,終端向量表等
-
stm32f2xx_it.c/.h // 中斷處理函數的相關實現
-
main.c/.h
根據HAL庫的命名規則,其API可以分為以下三大類:
- 初始化/反初始化函數:HAL_PPP_Init(), HAL_PPP_DeInit()
- IO 操作函數:HAL_PPP_Read(), HAL_PPP_Write(),HAL_PPP_Transmit(), HAL_PPP_Receive()
- 控制函數:HAL_PPP_Set (), HAL_PPP_Get ().
- 狀態和錯誤:HAL_PPP_GetState (), HAL_PPP_GetError ().
“注意:目前 LL 庫是和 HAL 庫捆綁發布的,所以在 HAL 庫源碼中,還有一些名為 stm32f2xx_ll_ppp 的源碼文件,這些文件就是新增的LL庫文件。使用 CubeMX 生產項目時,可以選擇LL庫。
”
HAL 庫最大的特點就是對底層進行了抽象。在此結構下,用戶代碼的處理主要分為三部分:
處理外設句柄(實現用戶功能) 處理MSP 處理各種回調函數 外設句柄定義 用戶代碼的第一大部分:對于外設句柄的處理。HAL庫在結構上,對每個外設抽象成了一個稱為ppp_HandleTypeDef的結構體,其中ppp就是每個外設的名字。*所有的函數都是工作在ppp_HandleTypeDef指針之下。
- 多實例支持:每個外設/模塊實例都有自己的句柄。因此,實例資源是獨立的
- 外圍進程相互通信:該句柄用于管理進程例程之間的共享數據資源。
下面,以ADC為例
-
/**
-
* @brief ADC handle Structure definition
-
*/
-
typedef struct
-
{
-
ADC_TypeDef *Instance; /*!< Register base address */
-
ADC_InitTypeDef Init; /*!< ADC required parameters */
-
__IO uint32_t NbrOfCurrentConversionRank; /*!< ADC number of current conversion rank */
-
DMA_HandleTypeDef *DMA_Handle; /*!< Pointer DMA Handler */
-
HAL_LockTypeDef Lock; /*!< ADC locking object */
-
__IO uint32_t State; /*!< ADC communication state */
-
__IO uint32_t ErrorCode; /*!< ADC Error code */
-
}ADC_HandleTypeDef;
從上面的定義可以看出,ADC_HandleTypeDef
中包含了ADC可能出現的所有定義,對于用戶想要使用ADC只要定義一個ADC_HandleTypeDef
的變量,給每個變量賦好值,對應的外設就抽象完了。接下來就是具體使用了。
當然,對于那些共享型外設或者說系統外設來說,他們不需要進行以上這樣的抽象,這些部分與原來的標準外設庫函數基本一樣。例如以下外設:
- GPIO
- SYSTICK
- NVIC
- RCC
- FLASH
以GPIO 為例,對于HAL_GPIO_Init()
函數,其只需要GPIO地址以及其初始化參數即可。
1. 三種編程方式
HAL庫對所有的函數模型也進行了統一。在HAL庫中,支持三種編程模式:輪詢模式、中斷模式、DMA模式(如果外設支持)。其分別對應如下三種類型的函數(以ADC為例):
-
HAL_StatusTypeDef HAL_ADC_Start(ADC_HandleTypeDef* hadc);
-
HAL_StatusTypeDef HAL_ADC_Stop(ADC_HandleTypeDef* hadc);
-
HAL_StatusTypeDef HAL_ADC_Start_IT(ADC_HandleTypeDef* hadc);
-
HAL_StatusTypeDef HAL_ADC_Stop_IT(ADC_HandleTypeDef* hadc);
-
HAL_StatusTypeDef HAL_ADC_Start_DMA(ADC_HandleTypeDef* hadc, uint32_t* pData, uint32_t Length);
-
HAL_StatusTypeDef HAL_ADC_Stop_DMA(ADC_HandleTypeDef* hadc);
其中,帶_IT的表示工作在中斷模式下;帶_DMA的工作在DMA模式下(注意:DMA模式下也是開中斷的);什么都沒帶的就是輪詢模式(沒有開啟中斷的)。至于使用者使用何種方式,就看自己的選擇了。
此外,新的HAL庫架構下統一采用宏的形式對各種中斷等進行配置(原來標準外設庫一般都是各種函數)。針對每種外設主要由以下宏:
- __HAL_PPP_ENABLE_IT(HANDLE, INTERRUPT):使能一個指定的外設中斷
- __HAL_PPP_DISABLE_IT(HANDLE, INTERRUPT):失能一個指定的外設中斷
- __HAL_PPP_GET_IT (HANDLE, __ INTERRUPT __):獲得一個指定的外設中斷狀態
- __HAL_PPP_CLEAR_IT (HANDLE, __ INTERRUPT __):清除一個指定的外設的中斷狀態
- __HAL_PPP_GET_FLAG (HANDLE, FLAG):獲取一個指定的外設的標志狀態
- __HAL_PPP_CLEAR_FLAG (HANDLE, FLAG):清除一個指定的外設的標志狀態
- __HAL_PPP_ENABLE(HANDLE) :使能外設
- __HAL_PPP_DISABLE(HANDLE) :失能外設
- __HAL_PPP_XXXX (HANDLE, PARAM) :指定外設的宏定義
- _HAL_PPP_GET?IT_SOURCE (HANDLE, __ INTERRUPT __)檢查中斷源
2. 三大回調函數
在 HAL 庫的源碼中,到處可見一些以__weak
開頭的函數,而且這些函數,有些已經被實現了,比如:
-
__weak HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
-
{
-
/*Configure the SysTick to have interrupt in 1ms time basis*/
-
HAL_SYSTICK_Config(SystemCoreClock/1000U);
-
/*Configure the SysTick IRQ priority */
-
HAL_NVIC_SetPriority(SysTick_IRQn, TickPriority ,0U);
-
/* Return function status */
-
return HAL_OK;
-
}
有些則沒有被實現,例如:
-
__weak void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi)
-
{
-
/* Prevent unused argument(s) compilation warning */
-
UNUSED(hspi);
-
/* NOTE : This function should not be modified, when the callback is needed,the HAL_SPI_TxCpltCallback should be implemented in the user file
-
*/
-
}
所有帶有__weak
關鍵字的函數表示,就可以由用戶自己來實現。如果出現了同名函數,且不帶__weak
關鍵字,那么連接器就會采用外部實現的同名函數。通常來說,HAL庫負責整個處理和MCU外設的處理邏輯,并將必要部分以回調函數的形式給出到用戶,用戶只需要在對應的回調函數中做修改即可。HAL 庫包含如下三種用戶級別回調函數(PPP為外設名):
- 外設系統級初始化/解除初始化回調函數(用戶代碼的第二大部分:對于MSP的處理):
HAL_PPP_MspInit()
和HAL_PPP_MspDeInit
?例如:__weak void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi)
。在HAL_PPP_Init() 函數中被調用,用來初始化底層相關的設備(GPIOs, clock, DMA, interrupt) - 處理完成回調函數:
HAL_PPP_ProcessCpltCallback
(Process指具體某種處理,如UART的Tx),例如:__weak void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi)
。當外設或者DMA工作完成后時,觸發中斷,該回調函數會在外設中斷處理函數或者DMA的中斷處理函數中被調用 - 錯誤處理回調函數:
HAL_PPP_ErrorCallback
例如:__weak void HAL_SPI_ErrorCallback(SPI_HandleTypeDef *hspi)
。當外設或者DMA出現錯誤時,觸發終端,該回調函數會在外設中斷處理函數或者DMA的中斷處理函數中被調用。
參考文檔及網文鏈接
ST-Description of STM32F4 HAL and LL drivers.pdf
ST-en.stm32_embedded_software_offering.pdf
https://mp.weixin.qq.com/s/GxvIvbxagSPvKuYSXcSVTw
轉載自:嵌入式大雜燴 標準庫和HAL庫到底有什么不同?怎么選?