前言
??相比較早幾年使用標準庫開發來講,最近幾年HAL庫的使用是越來越多,那么我們開發應當使用哪一種呢,本文著重介紹常用的幾種開發方式及相互之間的區別,白貓也好、黑貓也好,抓到耗子就是好貓。
STM32三種開發方式
??通常新手在入門STM32的時候,首先都要先選擇一種要用的開發方式,不同的開發方式會導致你編程的架構是完全不一樣的。一般大多數都會選用標準庫和HAL庫,而極少部分人會通過直接配置寄存器進行開發。
??網上關于標準庫、HAL庫的描述相信是數不勝數。可是一個對于很多剛入門的朋友還是沒法很直觀的去真正了解這些不同開發發方式彼此之間的區別,所以筆者想以一種非常直白的方式,用自己的理解去將這些東西表述出來,如果有描述的不對的地方或者是不同意見的也可以大家提出。
1、直接配置寄存器
??不少先學了51的朋友可能會知道,會有一小部分人或是教程是通過匯編語言直接操作寄存器實現功能的,這種方法到了STM32就變得不太容易行得通了,因為STM32的寄存器數量是51單片機的十數倍,如此多的寄存器根本無法全部記憶,開發時需要經常的翻查芯片的數據手冊,此時直接操作寄存器就變得非常的費力了。但還是會有很小一部分人,喜歡去直接操作寄存器,因為這樣更接近原理,知其然也知其所以然。
2、標準庫
??上面也提到了,STM32有非常多的寄存器,而導致了開發困難,所以為此ST公司就為每款芯片都編寫了一份庫文件,也就是工程文件里stm32F1xx…之類的。在這些 .c .h文件中,包括一些常用量的宏定義,把一些外設也通過結構體變量封裝起來,如GPIO口時鐘等。所以我們只需要配置結構體變量成員就可以修改外設的配置寄存器,從而選擇不同的功能。也是目前最多人使用的方式,也是學習STM32接觸最多的一種開發方式,我也就不多闡述了。
3、HAL庫
??HAL庫是ST公司目前主力推的開發方式,全稱就是Hardware Abstraction Layer(抽象印象層)。庫如其名,很抽象,一眼看上去不太容易知道他的作用是什么。 ??
它的出現比標準庫要晚,但其實和標準庫一樣,都是為了節省程序開發的時期,而且HAL庫尤其的有效,如果說標準庫把實現功能需要配置的寄存器集成了,那么HAL庫的一些函數甚至可以做到某些特定功能的集成。也就是說,同樣的功能,標準庫可能要用幾句話,HAL庫只需用一句話就夠了。 ??
并且HAL庫也很好的解決了程序移植的問題,不同型號的stm32芯片它的標準庫是不一樣的,例如在F4上開發的程序移植到F3上是不能通用的,而使用HAL庫,只要使用的是相通的外設,程序基本可以完全復制粘貼,注意是相通外設,意思也就是不能無中生有,例如F7比F3要多幾個定時器,不能明明沒有這個定時器卻非要配置,但其實這種情況不多,絕大多數都可以直接復制粘貼。是而且使用ST公司研發的STMcube軟件,可以通過圖形化的配置功能,直接生成整個使用HAL庫的工程文件,可以說是方便至極,但是方便的同時也造成了它執行效率的低下,在各種論壇帖子真的是被吐槽的數不勝數。
STM32 HAL庫與標準庫的區別
1、句柄
??句柄(handle),有多種意義,其中第一種是指程序設計,第二種是指Windows編程。現在大部分都是指程序設計/程序開發這類。
第一種解釋:句柄是一種特殊的智能指針 。當一個應用程序要引用其他系統(如數據庫、操作系統)所管理的內存塊或對象時,就要使用句柄。
第二種解釋:整個Windows編程的基礎。一個句柄是指使用的一個唯一的整數值,即一個4字節(64位程序中為8字節)長的數值,來標識應用程序中的不同對象和同類中的不同的實例,諸如,一個窗口,按鈕,圖標,滾動條,輸出設備,控件或者文件等。應用程序能夠通過句柄訪問相應的對象的信息,但是句柄不是指針,程序不能利用句柄來直接閱讀文件中的信息。如果句柄不在I/O文件中,它是毫無用處的。句柄是Windows用來標志應用程序中建立的或是使用的唯一整數,Windows大量使用了句柄來標識對象。
STM32的標準庫中,句柄是一種特殊的指針,通常指向結構體! ??在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
可以看到,要初始化一個串口,需要:
1、對六個位置進行賦值
2、然后引用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;我們發現,與標準庫不同的是,該成員不僅:
1、包含了之前標準庫就有的六個成員(波特率,數據格式等),
2、還包含過采樣、(發送或接收的)數據緩存、數據指針、串口 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函數
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、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庫的可移植性之強大,并且用戶可以完全不去理會底層各個寄存器的操作,代碼也更有邏輯性。但與此帶來的是復雜的代碼量,極慢的編譯速度,略微低下的效率。看怎么取舍了。
STM32 HAL庫結構
??說到STM32的HAL庫,就不得不提STM32CubeMX,其作為一個可視化的配置工具,對于開發者來說,確實大大節省了開發時間。相關推薦:STM32CubeMX安裝教程。STM32CubeMX就是以HAL庫為基礎的,且目前僅支持HAL庫及LL庫!首先看一下,官方給出的HAL庫的包含結構:
1、stm32f4xx.h主要包含STM32同系列芯片的不同具體型號的定義,是否使用HAL庫等的定義,接著,其會根據定義的芯片信號包含具體的芯片型號的頭文件:
#if defined(STM32F405xx) #include "stm32f405xx.h" #elif defined(STM32F415xx) #include "stm32f415xx.h" #elif defined(STM32F407xx) #include "stm32f407xx.h" #elif defined(STM32F417xx) #include "stm32f417xx.h" #else #error "Please select first the target STM32F4xx device used in your application (in stm32f2xx.h file)" #endif緊接著,其會包含stm32f4xx_hal.h。
2、stm32f4xx_hal.h:stm32f4xx_hal.c/h 主要實現HAL庫的初始化、系統滴答相關函數、及CPU的調試模式配置
3、stm32f4xx_hal_conf.h :該文件是一個用戶級別的配置文件,用來實現對HAL庫的裁剪,其位于用戶文件目錄,不要放在庫目錄中。
接下來對于HAL庫的源碼文件進行一下說明,HAL庫文件名均以stm32f4xx_hal開頭,后面加上_外設或者模塊名(如:stm32f4xx_hal_adc.c):
4、庫文件:stm32f4xx_hal_ppp.c/.h // 主要的外設或者模塊的驅動源文件,包含了該外設的通用API stm32f4xx_hal_ppp_ex.c/.h // 外圍設備或模塊驅動程序的擴展文件。這組文件中包含特定型號或者系列的芯片的特殊API。以及如果該特定的芯片內部有不同的實現方式,則該文件中的特殊API將覆蓋_ppp中的通用API。 stm32f4xx_hal.c/.h // 此文件用于HAL初始化,并且包含DBGMCU、重映射和基于systick的時間延遲等相關的API
5、其他庫文件 用戶級別文件: stm32f4xx_hal_msp_template.c // 只有.c沒有.h。它包含用戶應用程序中使用的外設的MSP初始化和反初始化(主程序和回調函數)。使用者復制到自己目錄下使用模板。 stm32f4xx_hal_conf_template.h // 用戶級別的庫配置文件模板。使用者復制到自己目錄下使用 system_stm32f4xx.c // 此文件主要包含SystemInit()函數,該函數在剛復位及跳到main之前的啟動過程中被調用。它不在啟動時配置系統時鐘(與標準庫相反)。時鐘的配置在用戶文件中使用HAL API來完成。startup_stm32f4xx.s // 芯片啟動文件,主要包含堆棧定義,終端向量表等 stm32f4xx_it.c/.h // 中斷處理函數的相關實現
6 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
處理各種回調函數
相關知識如下:
1、外設句柄定義
??用戶代碼的第一大部分:對于外設句柄的處理。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 地址以及其初始化參數即可。
2、 三種編程方式
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 __):檢查中斷源
3、 三大回調函數
??在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為外設名):
1、外設系統級初始化/解除初始化回調函數(用戶代碼的第二大部分:對于MSP的處理):
HAL_PPP_MspInit()和 HAL_PPP_MspDeInit** 例如: __weak void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi)。 在HAL_PPP_Init() 函數中被調用,用來初始化底層相關的設備(GPIOs, clock, DMA, interrupt)
2、處理完成回調函數: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)*
3、當外設或者DMA出現錯誤時,觸發終端,該回調函數會在外設中斷處理函數或者DMA的中斷處理函數中被調用
錯誤處理回調函數:
HAL_PPP_ErrorCallback
例如:
__weak void HAL_SPI_ErrorCallback(SPI_HandleTypeDef hspi)*
??當外設或者DMA出現錯誤時,觸發終端,該回調函數會在外設中斷處理函數或者DMA的中斷處理函數中被調用。
??絕大多數用戶代碼均在以上三大回調函數中實現。
??HAL庫結構中,在每次初始化前(尤其是在多次調用初始化前),先調用對應的反初始化(DeInit)函數是非常有必要的。
某些外設多次初始化時不調用返回會導致初始化失敗。完成回調函數有多中,例如串口的完成回調函數有
HAL_UART_TxCpltCallback HAL_UART_TxHalfCpltCallback
??(用戶代碼的第三大部分:對于上面第二點和第三點的各種回調函數的處理)在實際使用中,發現HAL仍有不少問題,例如在使用USB時,其庫配置存在問題。
HAL庫移植使用
基本步驟:
1、復制stm32f2xx_hal_msp_template.c,參照該模板,依次實現用到的外設的HAL_PPP_MspInit()和 HAL_PPP_MspDeInit。
2、復制stm32f2xx_hal_conf_template.h,用戶可以在此文件中自由裁剪,配置HAL庫。
3、在使用HAL庫時,必須先調用函數:HAL_StatusTypeDef HAL_Init(void)(該函數在stm32f2xx_hal.c中定義,也就意味著第一點中,必須首先實現HAL_MspInit(void)和HAL_MspDeInit(void))
4、HAL庫與STD庫不同,HAL庫使用RCC中的函數來配置系統時鐘,用戶需要單獨寫時鐘配置函數(STD庫默認在system_stm32f2xx.c中)
5、關于中斷,HAL提供了中斷處理函數,只需要調用HAL提供的中斷處理函數。用戶自己的代碼,不建議先寫到中斷中,而應該寫到HAL提供的回調函數中。
6、對于每一個外設,HAL都提供了回調函數,回調函數用來實現用戶自己的代碼。整個調用結構由HAL庫自己完成。
例如: Uart中,HAL提供了
void HAL_UART_IRQHandler(UART_HandleTypeDef *huart);
函數,用戶只需要觸發中斷后,用戶只需要調用該函數即可,同時,自己的代碼寫在對應的回調函數中即可!如下:
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart); void HAL_UART_TxHalfCpltCallback(UART_HandleTypeDef *huart); void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart); void HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart); void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart);使用了哪種就用哪個回調函數即可! 基本結構 綜上所述,使用HAL庫編寫程序(針對某個外設)的基本結構(以串口為例)如下:
1、 配置外設句柄 例如,建立UartConfig.c,在其中定義串口句柄 UART_HandleTypeDef huart;接著使用初始化句柄(HAL_StatusTypeDef HAL_UART_Init(UART_HandleTypeDef huart))
2、編寫Msp 例如,建立UartMsp.c,在其中實現void HAL_UART_MspInit(UART_HandleTypeDef huart) 和 void HAL_UART_MspDeInit(UART_HandleTypeDef* huart)
3、實現對應的回調函數 例如,建立UartCallBack.c,在其中實現上文所說明的三大回調函數中的完成回調函數和錯誤回調函數
審核編輯:湯梓紅
-
單片機
+關注
關注
6037文章
44558瀏覽量
635235 -
mcu
+關注
關注
146文章
17148瀏覽量
351186 -
寄存器
+關注
關注
31文章
5343瀏覽量
120348 -
STM32
+關注
關注
2270文章
10900瀏覽量
355985 -
HAL庫
+關注
關注
1文章
121瀏覽量
6234
原文標題:搞懂STM32開發用的HAL庫
文章出處:【微信號:mcu168,微信公眾號:硬件攻城獅】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論