0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

STM32三種開發(fā)方式及其區(qū)別

STM32嵌入式開發(fā) ? 來源:STM32嵌入式開發(fā) ? 作者:STM32嵌入式開發(fā) ? 2022-04-08 10:37 ? 次閱讀

前言

??相比較早幾年使用標(biāo)準(zhǔn)庫開發(fā)來講,最近幾年HAL庫的使用是越來越多,那么我們開發(fā)應(yīng)當(dāng)使用哪一種呢,本文著重介紹常用的幾種開發(fā)方式及相互之間的區(qū)別,白貓也好、黑貓也好,抓到耗子就是好貓。

STM32三種開發(fā)方式

??通常新手在入門STM32的時候,首先都要先選擇一種要用的開發(fā)方式,不同的開發(fā)方式會導(dǎo)致你編程的架構(gòu)是完全不一樣的。一般大多數(shù)都會選用標(biāo)準(zhǔn)庫和HAL庫,而極少部分人會通過直接配置寄存器進(jìn)行開發(fā)。

??網(wǎng)上關(guān)于標(biāo)準(zhǔn)庫、HAL庫的描述相信是數(shù)不勝數(shù)??墒且粋€對于很多剛?cè)腴T的朋友還是沒法很直觀的去真正了解這些不同開發(fā)發(fā)方式彼此之間的區(qū)別,所以筆者想以一種非常直白的方式,用自己的理解去將這些東西表述出來,如果有描述的不對的地方或者是不同意見的也可以大家提出。

1、直接配置寄存器

??不少先學(xué)了51的朋友可能會知道,會有一小部分人或是教程是通過匯編語言直接操作寄存器實(shí)現(xiàn)功能的,這種方法到了STM32就變得不太容易行得通了,因?yàn)镾TM32的寄存器數(shù)量是51單片機(jī)的十?dāng)?shù)倍,如此多的寄存器根本無法全部記憶,開發(fā)時需要經(jīng)常的翻查芯片的數(shù)據(jù)手冊,此時直接操作寄存器就變得非常的費(fèi)力了。但還是會有很小一部分人,喜歡去直接操作寄存器,因?yàn)檫@樣更接近原理,知其然也知其所以然。

2、標(biāo)準(zhǔn)庫

??上面也提到了,STM32有非常多的寄存器,而導(dǎo)致了開發(fā)困難,所以為此ST公司就為每款芯片都編寫了一份庫文件,也就是工程文件里stm32F1xx…之類的。在這些 .c .h文件中,包括一些常用量的宏定義,把一些外設(shè)也通過結(jié)構(gòu)體變量封裝起來,如GPIO口時鐘等。所以我們只需要配置結(jié)構(gòu)體變量成員就可以修改外設(shè)的配置寄存器,從而選擇不同的功能。也是目前最多人使用的方式,也是學(xué)習(xí)STM32接觸最多的一種開發(fā)方式,我也就不多闡述了。

3、HAL庫

??HAL庫是ST公司目前主力推的開發(fā)方式,全稱就是Hardware Abstraction Layer(抽象印象層)。庫如其名,很抽象,一眼看上去不太容易知道他的作用是什么。

??它的出現(xiàn)比標(biāo)準(zhǔn)庫要晚,但其實(shí)和標(biāo)準(zhǔn)庫一樣,都是為了節(jié)省程序開發(fā)的時期,而且HAL庫尤其的有效,如果說標(biāo)準(zhǔn)庫把實(shí)現(xiàn)功能需要配置的寄存器集成了,那么HAL庫的一些函數(shù)甚至可以做到某些特定功能的集成。也就是說,同樣的功能,標(biāo)準(zhǔn)庫可能要用幾句話,HAL庫只需用一句話就夠了。

??并且HAL庫也很好的解決了程序移植的問題,不同型號的stm32芯片它的標(biāo)準(zhǔn)庫是不一樣的,例如在F4上開發(fā)的程序移植到F3上是不能通用的,而使用HAL庫,只要使用的是相通的外設(shè),程序基本可以完全復(fù)制粘貼,注意是相通外設(shè),意思也就是不能無中生有,例如F7比F3要多幾個定時器,不能明明沒有這個定時器卻非要配置,但其實(shí)這種情況不多,絕大多數(shù)都可以直接復(fù)制粘貼。是而且使用ST公司研發(fā)的STMcube軟件,可以通過圖形化的配置功能,直接生成整個使用HAL庫的工程文件,可以說是方便至極,但是方便的同時也造成了它執(zhí)行效率的低下,在各種論壇帖子真的是被吐槽的數(shù)不勝數(shù)。

HAL庫固件庫安裝與 用戶手冊

1、首先設(shè)置讓Cube可以自動聯(lián)網(wǎng)下載相關(guān)固件庫選擇updater Settings

ad69790a-b65c-11ec-aa7f-dac502259ad0.png??設(shè)置如下

ad81863a-b65c-11ec-aa7f-dac502259ad0.png

2、根據(jù)芯片選擇所需固件

??版本是向下兼容的,可以直接選擇最新版。但如果覺得最新版太大,可以閱讀下面的Main Changes.能夠支持你目前的芯片就好。

ad967284-b65c-11ec-aa7f-dac502259ad0.png??

選好了,點(diǎn)擊Install Now就行,過程可能有點(diǎn)長。建議直接官網(wǎng)下載到本地,再安裝文件會被下載到如下位置,建議更改此目錄,不要選在C盤?。?!

adb71ec6-b65c-11ec-aa7f-dac502259ad0.png

3、尋找用戶幫助手冊

??進(jìn)入固件所在文件夾,里面包含很多內(nèi)容。

adcf87fe-b65c-11ec-aa7f-dac502259ad0.png比如說 官方提供的開發(fā)板程序,每個型號下面都有對應(yīng)功能的實(shí)現(xiàn),用戶手冊就在Drivers文件夾下面。

ade8f2d4-b65c-11ec-aa7f-dac502259ad0.pngae033252-b65c-11ec-aa7f-dac502259ad0.png

STM32 HAL庫與標(biāo)準(zhǔn)庫的區(qū)別

1、句柄

??句柄(handle),有多種意義,其中第一種是指程序設(shè)計(jì),第二種是指Windows編程。現(xiàn)在大部分都是指程序設(shè)計(jì)/程序開發(fā)這類。

  • 第一種解釋:句柄是一種特殊的智能指針 。當(dāng)一個應(yīng)用程序要引用其他系統(tǒng)(如數(shù)據(jù)庫、操作系統(tǒng))所管理的內(nèi)存塊或?qū)ο髸r,就要使用句柄。

  • 第二種解釋:整個Windows編程的基礎(chǔ)。一個句柄是指使用的一個唯一的整數(shù)值,即一個4字節(jié)(64位程序中為8字節(jié))長的數(shù)值,來標(biāo)識應(yīng)用程序中的不同對象和同類中的不同的實(shí)例,諸如,一個窗口,按鈕,圖標(biāo),滾動條,輸出設(shè)備,控件或者文件等。應(yīng)用程序能夠通過句柄訪問相應(yīng)的對象的信息,但是句柄不是指針,程序不能利用句柄來直接閱讀文件中的信息。如果句柄不在I/O文件中,它是毫無用處的。句柄是Windows用來標(biāo)志應(yīng)用程序中建立的或是使用的唯一整數(shù),Windows大量使用了句柄來標(biāo)識對象。

STM32的標(biāo)準(zhǔn)庫中,句柄是一種特殊的指針,通常指向結(jié)構(gòu)體!

??在STM32的標(biāo)準(zhǔn)庫中,假設(shè)我們要初始化一個外設(shè)(這里以USART為例),我們首先要初始化他們的各個寄存器。在標(biāo)準(zhǔn)庫中,這些操作都是利用固件庫結(jié)構(gòu)體變量+固件庫Init函數(shù)實(shí)現(xiàn)的:

USART_InitTypeDef USART_InitStructure;
USART_InitStructure.USART_BaudRate = bound;//串口波特率USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字長為8位數(shù)據(jù)格式USART_InitStructure.USART_StopBits = USART_StopBits_1;//一個停止位USART_InitStructure.USART_Parity = USART_Parity_No;//無奇偶校驗(yàn)位USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//無硬件數(shù)據(jù)流控制USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收發(fā)模式
USART_Init(USART3, &USART_InitStructure); //初始化串口1

可以看到,要初始化一個串口,需要:

  • 1、對六個位置進(jìn)行賦值
  • 2、然后引用Init函數(shù)

??USART_InitStructure并不是一個全局結(jié)構(gòu)體變量,而是只在函數(shù)內(nèi)部的局部變量,初始化完成之后,USART_InitStructure就失去了作用。而在HAL庫中,同樣是USART初始化結(jié)構(gòu)體變量,我們要定義為全局變量。

UART_HandleTypeDef UART1_Handler;

結(jié)構(gòu)體成員

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;

我們發(fā)現(xiàn),與標(biāo)準(zhǔn)庫不同的是,該成員不僅:

  • 1、包含了之前標(biāo)準(zhǔn)庫就有的六個成員(波特率,數(shù)據(jù)格式等),

  • 2、還包含過采樣、(發(fā)送或接收的)數(shù)據(jù)緩存、數(shù)據(jù)指針、串口 DMA 相關(guān)的變量、各種標(biāo)志位等等要在整個項(xiàng)目流程中都要設(shè)置的各個成員。

    該 UART1_Handler就被稱為串口的句柄,它被貫穿整個USART收發(fā)的流程,比如開啟中斷:

HAL_UART_Receive_IT(&UART1_Handler, (u8 *)aRxBuffer, RXBUFFERSIZE);

比如后面要講到的MSP與Callback回調(diào)函數(shù):

void HAL_UART_MspInit(UART_HandleTypeDef *huart);void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);

在這些函數(shù)中,只需要調(diào)用初始化時定義的句柄UART1_Handler就好。

2、MSP函數(shù)

MSP: MCU Specific Package 單片機(jī)的具體方案

MSP是指和MCU相關(guān)的初始化,引用一下正點(diǎn)原子的解釋,個人覺得說的很明白:

??我們要初始化一個串口,首先要設(shè)置和 MCU 無關(guān)的東西,例如波特率,奇偶校驗(yàn),停止位等,這些參數(shù)設(shè)置和 MCU 沒有任何關(guān)系,可以使用 STM32F1,也可以是 STM32F2/F3/F4/F7上的串口。而一個串口設(shè)備它需要一個 MCU 來承載,例如用 STM32F4 來做承載,PA9 做為發(fā)送,PA10 做為接收,MSP 就是要初始化 STM32F4 的 PA9,PA10,配置這兩個引腳。所以 HAL驅(qū)動方式的初始化流程就是:

  • HAL_USART_Init()—>HAL_USART_MspInit() ,先初始化與 MCU無關(guān)的串口協(xié)議,再初始化與 MCU 相關(guān)的串口引腳。

  • 在 STM32 的 HAL 驅(qū)動中HAL_PPP_MspInit()作為回調(diào),被 HAL_PPP_Init()函數(shù)所調(diào)用。當(dāng)我們需要移植程序到 STM32F1平臺的時候,我們只需要修改 HAL_PPP_MspInit 函數(shù)內(nèi)容而不需要修改 HAL_PPP_Init 入口參數(shù)內(nèi)容。

??在HAL庫中,幾乎每初始化一個外設(shè)就需要設(shè)置該外設(shè)與單片機(jī)之間的聯(lián)系,比如IO口,是否復(fù)用等等,可見,HAL庫相對于標(biāo)準(zhǔn)庫多了MSP函數(shù)之后,移植性非常強(qiáng),但與此同時卻增加了代碼量和代碼的嵌套層級。可以說各有利弊。

同樣,MSP函數(shù)又可以配合句柄,達(dá)到非常強(qiáng)的移植性:

void HAL_UART_MspInit(UART_HandleTypeDef *huart);

3、Callback函數(shù)

??類似于MSP函數(shù),個人認(rèn)為Callback函數(shù)主要幫助用戶應(yīng)用層的代碼編寫。

??還是以USART為例,在標(biāo)準(zhǔn)庫中,串口中斷了以后,我們要先在中斷中判斷是否是接收中斷,然后讀出數(shù)據(jù),順便清除中斷標(biāo)志位,然后再是對數(shù)據(jù)的處理,這樣如果我們在一個中斷函數(shù)中寫這么多代碼,就會顯得很混亂:

void USART3_IRQHandler(void)                 //串口1中斷服務(wù)程序{ u8 Res; if(USART_GetITStatus(USART3, USART_IT_RXNE) != RESET)  //接收中斷(接收到的數(shù)據(jù)必須是0x0d 0x0a結(jié)尾) {  Res =USART_ReceiveData(USART3); //讀取接收到的數(shù)據(jù)  /*數(shù)據(jù)處理區(qū)*/  }           } } 

而在HAL庫中,進(jìn)入串口中斷后,直接由HAL庫中斷函數(shù)進(jìn)行托管:

void USART1_IRQHandler(void)                 {  HAL_UART_IRQHandler(&UART1_Handler); //調(diào)用HAL庫中斷處理公用函數(shù) /***************省略無關(guān)代碼****************/ }

??HAL_UART_IRQHandler這個函數(shù)完成了判斷是哪個中斷(接收?發(fā)送?或者其他?),然后讀出數(shù)據(jù),保存至緩存區(qū),順便清除中斷標(biāo)志位等等操作。

??比如我提前設(shè)置了,串口每接收五個字節(jié),我就要對這五個字節(jié)進(jìn)行處理。在一開始我定義了一個串口接收緩存區(qū):

/*HAL庫使用的串口接收緩沖,處理邏輯由HAL庫控制,接收完這個數(shù)組就會調(diào)用HAL_UART_RxCpltCallback進(jìn)行處理這個數(shù)組*//*RXBUFFERSIZE=5*/u8 aRxBuffer[RXBUFFERSIZE];

在初始化中,我在句柄里設(shè)置好了緩存區(qū)的地址,緩存大?。ㄎ鍌€字節(jié))

/*該代碼在HAL_UART_Receive_IT函數(shù)中,初始化時會引用*/    huart->pRxBuffPtr = pData;//aRxBuffer    huart->RxXferSize = Size;//RXBUFFERSIZE    huart->RxXferCount = Size;//RXBUFFERSIZE

??則在接收數(shù)據(jù)中,每接收完五個字節(jié),HAL_UART_IRQHandler才會執(zhí)行一次Callback函數(shù):

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);

??在這個Callback回調(diào)函數(shù)中,我們只需要對這接收到的五個字節(jié)(保存在aRxBuffer[]中)進(jìn)行處理就好了,完全不用再去手動清除標(biāo)志位等操作。

??所以說Callback函數(shù)是一個應(yīng)用層代碼的函數(shù),我們在一開始只設(shè)置句柄里面的各個參數(shù),然后就等著HAL庫把自己安排好的代碼送到手中就可以了~

??綜上,就是HAL庫的三個與標(biāo)準(zhǔn)庫不同的地方之個人見解。個人覺得從這三個小點(diǎn)就可以看出HAL庫的可移植性之強(qiáng)大,并且用戶可以完全不去理會底層各個寄存器的操作,代碼也更有邏輯性。但與此帶來的是復(fù)雜的代碼量,極慢的編譯速度,略微低下的效率??丛趺慈∩崃?。

STM32 HAL庫結(jié)構(gòu)

??說到STM32的HAL庫,就不得不提STM32CubeMX,其作為一個可視化的配置工具,對于開發(fā)者來說,確實(shí)大大節(jié)省了開發(fā)時間。STM32CubeMX就是以HAL庫為基礎(chǔ)的,且目前僅支持HAL庫及LL庫!首先看一下,官方給出的HAL庫的包含結(jié)構(gòu):

ae1b8154-b65c-11ec-aa7f-dac502259ad0.png
  • 1、stm32f4xx.h主要包含STM32同系列芯片的不同具體型號的定義,是否使用HAL庫等的定義,接著,其會根據(jù)定義的芯片信號包含具體的芯片型號的頭文件:
#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 主要實(shí)現(xiàn)HAL庫的初始化、系統(tǒng)滴答相關(guān)函數(shù)、及CPU的調(diào)試模式配置

  • 3、stm32f4xx_hal_conf.h :該文件是一個用戶級別的配置文件,用來實(shí)現(xiàn)對HAL庫的裁剪,其位于用戶文件目錄,不要放在庫目錄中。

接下來對于HAL庫的源碼文件進(jìn)行一下說明,HAL庫文件名均以stm32f4xx_hal開頭,后面加上_外設(shè)或者模塊名(如:stm32f4xx_hal_adc.c):

  • 4、庫文件:stm32f4xx_hal_ppp.c/.h // 主要的外設(shè)或者模塊的驅(qū)動源文件,包含了該外設(shè)的通用API

    stm32f4xx_hal_ppp_ex.c/.h // 外圍設(shè)備或模塊驅(qū)動程序的擴(kuò)展文件。這組文件中包含特定型號或者系列的芯片的特殊API。以及如果該特定的芯片內(nèi)部有不同的實(shí)現(xiàn)方式,則該文件中的特殊API將覆蓋_ppp中的通用API。

    stm32f4xx_hal.c/.h // 此文件用于HAL初始化,并且包含DBGMCU、重映射和基于systick的時間延遲等相關(guān)的API

  • 5、其他庫文件

    用戶級別文件:

    stm32f4xx_hal_msp_template.c // 只有.c沒有.h。它包含用戶應(yīng)用程序中使用的外設(shè)的MSP初始化和反初始化(主程序和回調(diào)函數(shù))。使用者復(fù)制到自己目錄下使用模板。

    stm32f4xx_hal_conf_template.h // 用戶級別的庫配置文件模板。使用者復(fù)制到自己目錄下使用

    system_stm32f4xx.c // 此文件主要包含SystemInit()函數(shù),該函數(shù)在剛復(fù)位及跳到main之前的啟動過程中被調(diào)用。它不在啟動時配置系統(tǒng)時鐘(與標(biāo)準(zhǔn)庫相反)。時鐘的配置在用戶文件中使用HAL API來完成。startup_stm32f4xx.s // 芯片啟動文件,主要包含堆棧定義,終端向量表等 stm32f4xx_it.c/.h // 中斷處理函數(shù)的相關(guān)實(shí)現(xiàn)

  • 6 main.c/.h //

根據(jù)HAL庫的命名規(guī)則,其API可以分為以下三大類:

  • 初始化/反初始化函數(shù):
 HAL_PPP_Init(), HAL_PPP_DeInit()
  • IO 操作函數(shù):
HAL_PPP_Read(),HAL_PPP_Write(),HAL_PPP_Transmit(), HAL_PPP_Receive()
  • 控制函數(shù):
HAL_PPP_Set (), HAL_PPP_Get ().
  • 狀態(tài)和錯誤:
 ** HAL_PPP_GetState (),   HAL_PPP_GetError ().
  • 注意:

??目前LL庫是和HAL庫捆綁發(fā)布的,所以在HAL庫源碼中,還有一些名為 stm32f2xx_ll_ppp的源碼文件,這些文件就是新增的LL庫文件。使用CubeMX生產(chǎn)項(xiàng)目時,可以選擇LL庫。

??HAL庫最大的特點(diǎn)就是對底層進(jìn)行了抽象。在此結(jié)構(gòu)下,用戶代碼的處理主要分為三部分:

  • 處理外設(shè)句柄(實(shí)現(xiàn)用戶功能)
  • 處理MSP
  • 處理各種回調(diào)函數(shù)

相關(guān)知識如下:

1、外設(shè)句柄定義

??用戶代碼的第一大部分:對于外設(shè)句柄的處理。HAL庫在結(jié)構(gòu)上,對每個外設(shè)抽象成了一個稱為ppp_HandleTypeDef的結(jié)構(gòu)體,其中ppp就是每個外設(shè)的名字。*所有的函數(shù)都是工作在ppp_HandleTypeDef指針之下。

  1. 多實(shí)例支持:每個外設(shè)/模塊實(shí)例都有自己的句柄。因此,實(shí)例資源是獨(dú)立的
  • 下面,以ADC為例

  1. 外圍進(jìn)程相互通信:該句柄用于管理進(jìn)程例程之間的共享數(shù)據(jù)資源。
/**  * @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可能出現(xiàn)的所有定義,對于用戶想要使用ADC只要定義一個ADC_HandleTypeDef的變量,給每個變量賦好值,對應(yīng)的外設(shè)就抽象完了。接下來就是具體使用了。

???當(dāng)然,對于那些共享型外設(shè)或者說系統(tǒng)外設(shè)來說,他們不需要進(jìn)行以上這樣的抽象,這些部分與原來的標(biāo)準(zhǔn)外設(shè)庫函數(shù)基本一樣。例如以下外設(shè):

  • GPIO

  • SYSTICK

  • NVIC

  • RCC

  • FLASH

??以GPIO為例,對于HAL_GPIO_Init() 函數(shù),其只需要GPIO 地址以及其初始化參數(shù)即可。

2、 三種編程方式

HAL庫對所有的函數(shù)模型也進(jìn)行了統(tǒng)一。在HAL庫中,支持三種編程模式:輪詢模式、中斷模式、DMA模式(如果外設(shè)支持)。其分別對應(yīng)如下三種類型的函數(shù)(以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庫架構(gòu)下統(tǒng)一采用宏的形式對各種中斷等進(jìn)行配置(原來標(biāo)準(zhǔn)外設(shè)庫一般都是各種函數(shù))。針對每種外設(shè)主要由以下宏:

__HAL_PPP_ENABLE_IT(HANDLE, INTERRUPT): 使能一個指定的外設(shè)中斷__HAL_PPP_DISABLE_IT(HANDLE, INTERRUPT):失能一個指定的外設(shè)中斷__HAL_PPP_GET_IT (HANDLE, __ INTERRUPT __):獲得一個指定的外設(shè)中斷狀態(tài)__HAL_PPP_CLEAR_IT (HANDLE, __ INTERRUPT __):清除一個指定的外設(shè)的中斷狀態(tài)__HAL_PPP_GET_FLAG (HANDLE, FLAG):獲取一個指定的外設(shè)的標(biāo)志狀態(tài)__HAL_PPP_CLEAR_FLAG (HANDLE, FLAG):清除一個指定的外設(shè)的標(biāo)志狀態(tài)__HAL_PPP_ENABLE(HANDLE) :使能外設(shè)__HAL_PPP_DISABLE(HANDLE) :失能外設(shè)__HAL_PPP_XXXX (HANDLE, PARAM) :指定外設(shè)的宏定義_HAL_PPP_GET IT_SOURCE (HANDLE, __ INTERRUPT __):檢查中斷源

3、 三大回調(diào)函數(shù)

??在HAL庫的源碼中,到處可見一些以__weak開頭的函數(shù),而且這些函數(shù),有些已經(jīng)被實(shí)現(xiàn)了,比如:

__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;}

有些則沒有被實(shí)現(xiàn),例如:

__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關(guān)鍵字的函數(shù)表示,就可以由用戶自己來實(shí)現(xiàn)。如果出現(xiàn)了同名函數(shù),且不帶__weak關(guān)鍵字,那么連接器就會采用外部實(shí)現(xiàn)的同名函數(shù)。

??通常來說,HAL庫負(fù)責(zé)整個處理和MCU外設(shè)的處理邏輯,并將必要部分以回調(diào)函數(shù)的形式給出到用戶,用戶只需要在對應(yīng)的回調(diào)函數(shù)中做修改即可。HAL庫包含如下三種用戶級別回調(diào)函數(shù)(PPP為外設(shè)名):

  • 1、外設(shè)系統(tǒng)級初始化/解除初始化回調(diào)函數(shù)(用戶代碼的第二大部分:對于MSP的處理):

HAL_PPP_MspInit()和 HAL_PPP_MspDeInit**

例如:

__weak void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi)。

在HAL_PPP_Init() 函數(shù)中被調(diào)用,用來初始化底層相關(guān)的設(shè)備(GPIOs, clock, DMA, interrupt)

  • 2、處理完成回調(diào)函數(shù):HAL_PPP_ProcessCpltCallback*(Process指具體某種處理,如UART的Tx),

例如:

__weak void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi)

當(dāng)外設(shè)或者DMA工作完成后時,觸發(fā)中斷,該回調(diào)函數(shù)會在外設(shè)中斷處理函數(shù)或者DMA的中斷處理函數(shù)中被調(diào)用錯誤處理回調(diào)函數(shù):

HAL_PPP_ErrorCallback

例如:

__weak void HAL_SPI_ErrorCallback(SPI_HandleTypeDef hspi)*
  • 3、當(dāng)外設(shè)或者DMA出現(xiàn)錯誤時,觸發(fā)終端,該回調(diào)函數(shù)會在外設(shè)中斷處理函數(shù)或者DMA的中斷處理函數(shù)中被調(diào)用

錯誤處理回調(diào)函數(shù):

HAL_PPP_ErrorCallback

例如:

__weak void HAL_SPI_ErrorCallback(SPI_HandleTypeDef hspi)*

??當(dāng)外設(shè)或者DMA出現(xiàn)錯誤時,觸發(fā)終端,該回調(diào)函數(shù)會在外設(shè)中斷處理函數(shù)或者DMA的中斷處理函數(shù)中被調(diào)用。

??絕大多數(shù)用戶代碼均在以上三大回調(diào)函數(shù)中實(shí)現(xiàn)。

??HAL庫結(jié)構(gòu)中,在每次初始化前(尤其是在多次調(diào)用初始化前),先調(diào)用對應(yīng)的反初始化(DeInit)函數(shù)是非常有必要的。

某些外設(shè)多次初始化時不調(diào)用返回會導(dǎo)致初始化失敗。完成回調(diào)函數(shù)有多中,例如串口的完成回調(diào)函數(shù)有

HAL_UART_TxCpltCallback HAL_UART_TxHalfCpltCallback

??(用戶代碼的第三大部分:對于上面第二點(diǎn)和第三點(diǎn)的各種回調(diào)函數(shù)的處理)在實(shí)際使用中,發(fā)現(xiàn)HAL仍有不少問題,例如在使用USB時,其庫配置存在問題。

HAL庫移植使用

基本步驟:

  • 1、復(fù)制stm32f2xx_hal_msp_template.c,參照該模板,依次實(shí)現(xiàn)用到的外設(shè)的HAL_PPP_MspInit()和 HAL_PPP_MspDeInit。

  • 2、復(fù)制stm32f2xx_hal_conf_template.h,用戶可以在此文件中自由裁剪,配置HAL庫。

  • 3、在使用HAL庫時,必須先調(diào)用函數(shù):HAL_StatusTypeDef HAL_Init(void)(該函數(shù)在stm32f2xx_hal.c中定義,也就意味著第一點(diǎn)中,必須首先實(shí)現(xiàn)HAL_MspInit(void)和HAL_MspDeInit(void))

  • 4、HAL庫與STD庫不同,HAL庫使用RCC中的函數(shù)來配置系統(tǒng)時鐘,用戶需要單獨(dú)寫時鐘配置函數(shù)(STD庫默認(rèn)在system_stm32f2xx.c中)

  • 5、關(guān)于中斷,HAL提供了中斷處理函數(shù),只需要調(diào)用HAL提供的中斷處理函數(shù)。用戶自己的代碼,不建議先寫到中斷中,而應(yīng)該寫到HAL提供的回調(diào)函數(shù)中。

  • 6、對于每一個外設(shè),HAL都提供了回調(diào)函數(shù),回調(diào)函數(shù)用來實(shí)現(xiàn)用戶自己的代碼。整個調(diào)用結(jié)構(gòu)由HAL庫自己完成。

例如:

Uart中,HAL提供了

void HAL_UART_IRQHandler(UART_HandleTypeDef *huart);

函數(shù),用戶只需要觸發(fā)中斷后,用戶只需要調(diào)用該函數(shù)即可,同時,自己的代碼寫在對應(yīng)的回調(diào)函數(shù)中即可!如下:

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);

使用了哪種就用哪個回調(diào)函數(shù)即可!

基本結(jié)構(gòu)

綜上所述,使用HAL庫編寫程序(針對某個外設(shè))的基本結(jié)構(gòu)(以串口為例)如下:

  • 1、 配置外設(shè)句柄 例如,建立UartConfig.c,在其中定義串口句柄 UART_HandleTypeDef huart;接著使用初始化句柄(HAL_StatusTypeDef HAL_UART_Init(UART_HandleTypeDef huart))

  • 2、編寫Msp 例如,建立UartMsp.c,在其中實(shí)現(xiàn)void HAL_UART_MspInit(UART_HandleTypeDef huart) 和 void HAL_UART_MspDeInit(UART_HandleTypeDef* huart)

  • 3、實(shí)現(xiàn)對應(yīng)的回調(diào)函數(shù) 例如,建立UartCallBack.c,在其中實(shí)現(xiàn)上文所說明的三大回調(diào)函數(shù)中的完成回調(diào)函數(shù)和錯誤回調(diào)函數(shù)

原文標(biāo)題:搞懂STM32開發(fā)用的HAL庫

文章出處:【微信公眾號:STM32嵌入式開發(fā)】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
審核編輯:湯梓紅


聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 寄存器
    +關(guān)注

    關(guān)注

    31

    文章

    5268

    瀏覽量

    119646
  • STM32
    +關(guān)注

    關(guān)注

    2263

    文章

    10846

    瀏覽量

    353694
  • 開發(fā)
    +關(guān)注

    關(guān)注

    0

    文章

    359

    瀏覽量

    40769

原文標(biāo)題:搞懂STM32開發(fā)用的HAL庫

文章出處:【微信號:c-stm32,微信公眾號:STM32嵌入式開發(fā)】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    STM32三種開發(fā)方式及其區(qū)別

    ??相比較早幾年使用標(biāo)準(zhǔn)庫開發(fā)來講,最近幾年HAL庫的使用是越來越多,那么我們開發(fā)應(yīng)當(dāng)使用哪一呢,本文著重介紹常用的幾種開發(fā)方式及相互之間的區(qū)別
    發(fā)表于 09-20 09:38 ?3952次閱讀

    STM32的幾種開發(fā)方式,你都知道嗎?

    功能,比如你想要以一個什么樣的方式通訊,還是需要你自己去完善的。如下圖是圖形化開發(fā)的界面:上面我們以簡潔明了的描述了三種編程方式的使用方式
    發(fā)表于 07-06 13:38

    STM32三種開發(fā)方式

    1 STM32三種開發(fā)方式通常新手在入門STM32的時候,首先都要先選擇一要用的開發(fā)方式,不
    發(fā)表于 08-05 06:56

    STM32三種啟動方式

    STM32三種啟動方式依靠BOOT0和BOOT1兩個引腳的電平來決定,ST官方推薦的是串聯(lián)10k電阻然后在接高電平或接地。我用0R直接接地的,沒有串聯(lián)10k電阻,造成STM32的硬件
    發(fā)表于 08-11 06:33

    STM32三種開發(fā)方式分享

    STM32三種開發(fā)方式通常新手在入門STM32的時候,首先都要先選擇一要用的開發(fā)方式,不同的
    發(fā)表于 12-01 07:59

    STM32三種boot模式介紹

    淺識STM32三種boot模式文章目錄淺識STM32三種boot模式任務(wù)摘要一、認(rèn)識boot1.三種BOOT模式介紹2.
    發(fā)表于 12-10 07:46

    STM32三種啟動方式是什么

    STM32三種啟動方式是什么
    發(fā)表于 12-15 07:16

    STM32三種BOOT啟動方式的設(shè)置與應(yīng)用

    如何在不同的下載方式中選擇STM32的啟動模式呢?STM32三種BOOT啟動方式的設(shè)置與應(yīng)用分別是什么?
    發(fā)表于 01-18 07:01

    STM32L4系列開發(fā)記錄(1)——使用STM32CubeMX建立工程

    目錄1.三種開發(fā)方式2.安裝STM32CubeMX3.使用STM32CubeMX建立工程1.三種開發(fā)方式
    發(fā)表于 11-14 09:06 ?14次下載
    <b class='flag-5'>STM32</b>L4系列<b class='flag-5'>開發(fā)</b>記錄(1)——使用<b class='flag-5'>STM32</b>CubeMX建立工程

    機(jī)智云三種APP開發(fā)方式介紹

    機(jī)智云針對不同開發(fā)者的不同需求提供三種APP開發(fā)方式,包括集成SDK、使用app開源框架、使用app自動生成,幫助開發(fā)者更加快速開發(fā)自己的A
    的頭像 發(fā)表于 11-21 15:27 ?2722次閱讀
    機(jī)智云<b class='flag-5'>三種</b>APP<b class='flag-5'>開發(fā)方式</b>介紹

    STM32的HAL庫分析及使用

    STM32三種開發(fā)方式通常新手在入門STM32的時候,首先都要先選擇一要用的開發(fā)方式,不同的
    發(fā)表于 11-22 11:21 ?115次下載
    <b class='flag-5'>STM32</b>的HAL庫分析及使用

    STM32三種開發(fā)方式 HAL庫固件庫安裝與用戶手冊

    ??相比較早幾年使用標(biāo)準(zhǔn)庫開發(fā)來講,最近幾年HAL庫的使用是越來越多,那么我們開發(fā)應(yīng)當(dāng)使用哪一呢,本文著重介紹常用的幾種開發(fā)方式及相互之間的區(qū)別
    的頭像 發(fā)表于 07-04 14:35 ?3735次閱讀

    先楫hpm_sdk開發(fā)方式的優(yōu)缺點(diǎn) 與單片機(jī)傳統(tǒng)開發(fā)方式的不同點(diǎn)

    最近在跟一些開發(fā)者交流過程中,或者開發(fā)者群里反饋,感覺先楫單片機(jī)開發(fā)方式不同于以往的單片機(jī)開發(fā)方式,或者開發(fā)方式沒接觸過導(dǎo)致無從下手,或者是
    的頭像 發(fā)表于 09-25 09:16 ?1142次閱讀
    先楫hpm_sdk<b class='flag-5'>開發(fā)方式</b>的優(yōu)缺點(diǎn) 與單片機(jī)傳統(tǒng)<b class='flag-5'>開發(fā)方式</b>的不同點(diǎn)

    嵌入式Linux開發(fā)三種方式

    嵌入式Linux開發(fā)主要有三種方式:裸機(jī)開發(fā)、SDK開發(fā)和驅(qū)動開發(fā)。
    的頭像 發(fā)表于 01-22 14:22 ?865次閱讀

    簡述三種esp32的開發(fā)方式是什么

    ESP32是一款由樂鑫(Espressif)推出的低功耗、高性能的Wi-Fi和藍(lán)牙雙模無線通信芯片,廣泛應(yīng)用于物聯(lián)網(wǎng)、智能家居、智能硬件等領(lǐng)域。本文將詳細(xì)介紹三種ESP32的開發(fā)方式:Arduino
    的頭像 發(fā)表于 08-20 09:11 ?1255次閱讀