1. 程式人生 > >stm32-hal庫開發入門

stm32-hal庫開發入門

很久之前就聽說st出了一個新版本的庫,用於代替原來的標準庫,非常好奇,但是一直沒有機會去體驗。這次藉著做畢設的機會,嘗試著切換到新庫。

官網介紹說,hal(hardware abstract layer)是一層硬體的抽象,看到這裡,我非常激動,看來st終於意識到原來標準庫的問題了,原來的標準庫非常依賴於具體硬體細節,很難體現出使用庫的優勢,而且很難移植。同時我也非常好奇,st到底是如何把不同系列mcu的操作給封裝起來的,是不是足夠抽象,方便移植。

話不多說,直接上官網下下來再說。

Paste_Image.png

上圖就是hal庫的全部內容,其中STM32F1xx_HAL_Driver中屬於hal庫的內容。

拿到庫第一步需要做的就是配置一個簡單的hello world,我在配置的時候,出現了非常多的問題。最開始非常自信,只從資料夾裡挑選自認為有用的檔案加入到工程中,結果出現了各種問題,裡面各種庫的依賴關係比較複雜,如果不是很熟悉整個架構的話,還是老老實實拷貝整個資料夾吧。

配置之前,首先要了解一下整個庫的框架,官方給的框架圖如下:

捕獲.PNG

個人認為這幅圖和我理解的有些許出入,故重新畫了一張:

Paste_Image.png

有幾點區別:

  • cmsis我放在了驅動層的最底層,因為cmsis庫中包含的內容都是和具體cpu核心相關的東西,還有一些地址定義,這些都是非常底層的東西了,而且hal層確實是依賴於cmsis。
  • hal底層我增加了一層msp,類似於bsp,全稱是mcu support package,這一層相當於hal的驅動層,與硬體相關的部分比如最終的時鐘配置,gpio配置等等提取出來,交給使用者配置。

瞭解了架構,下面我們就來配置一個簡單的工程吧。

  1. 首先拷貝整個Driver目錄到工程中。
  2. 新建user資料夾,新建main.c檔案。
    找到stm32f1xx_hal_conf_template.h,stm32f1xx_hal_msp_template.c,去掉"_template"放入user資料夾。
    找到stm32f1xx_it.c和stm32f1xx_it.h放入user資料夾。
  3. 新建工程
    新增原始檔:
Paste_Image.png

配置工程:

  • 勾選Use MicroLib,因為hal使用了c標準庫。
  • 新增全域性巨集定義:USE_HAL_DRIVER,STM32F103xB。關於晶片選擇,有如下表格:


    捕獲1.PNG
  • 勾選c99支援,因為hal採用的是c99標準編寫,不勾選的話,會出現類似於uint32_t等型別不存在的編譯錯誤。
  • 新增包含目錄,如下圖:
Paste_Image.png

4.編寫程式碼:

配置stm32f1xx_hal_conf.h:
這裡面有許多用於配置的巨集,比如用於精準延時的晶振頻率,還有各個外設模組的開關等等。

main.c

#include "stm32f1xx_hal.h"
int main()
{
    HAL_Init();
    
    __HAL_RCC_GPIOC_CLK_ENABLE();
    
    GPIO_InitTypeDef gpio_initstruct;
    gpio_initstruct.Mode=GPIO_MODE_OUTPUT_PP;
    gpio_initstruct.Speed=GPIO_SPEED_FREQ_HIGH;
    gpio_initstruct.Pull=GPIO_NOPULL;
    gpio_initstruct.Pin=GPIO_PIN_13;
    HAL_GPIO_Init(GPIOC,&gpio_initstruct);

    while(1)
    {
        HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,0);
        HAL_Delay(150);
        HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,1);
        HAL_Delay(150);
        HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,0);
        HAL_Delay(150);
        HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,1);
        HAL_Delay(1000);
    }
    return 0;
}

stm32f1xx_hal_msp.c

#include "stm32f1xx_hal.h"
void SystemClock_Config(void);
void HAL_MspInit(void)
{
    SystemClock_Config();
}
void SystemClock_Config(void)
{
  RCC_ClkInitTypeDef clkinitstruct = {0};
  RCC_OscInitTypeDef oscinitstruct = {0};
  
  /* Configure PLL ------------------------------------------------------*/
  /* PLL configuration: PLLCLK = (HSI / 2) * PLLMUL = (8 / 2) * 16 = 64 MHz */
  /* PREDIV1 configuration: PREDIV1CLK = PLLCLK / HSEPredivValue = 64 / 1 = 64 MHz */
  /* Enable HSI and activate PLL with HSi_DIV2 as source */
  oscinitstruct.OscillatorType  = RCC_OSCILLATORTYPE_HSE;//RCC_OSCILLATORTYPE_HSI;
  oscinitstruct.HSEState        = RCC_HSE_ON;//RCC_HSE_OFF;
  oscinitstruct.LSEState        = RCC_LSE_OFF;
  oscinitstruct.HSIState        = RCC_HSI_OFF;//RCC_HSI_ON;
  oscinitstruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
  oscinitstruct.HSEPredivValue    = RCC_HSE_PREDIV_DIV1;
  oscinitstruct.PLL.PLLState    = RCC_PLL_ON;
  oscinitstruct.PLL.PLLSource   = RCC_PLLSOURCE_HSE;//RCC_PLLSOURCE_HSI_DIV2;
  oscinitstruct.PLL.PLLMUL      = RCC_PLL_MUL9;//RCC_PLL_MUL16;
  if (HAL_RCC_OscConfig(&oscinitstruct)!= HAL_OK)
  {
    /* Initialization Error */
    while(1); 
  }

  /* Select PLL as system clock source and configure the HCLK, PCLK1 and PCLK2 
     clocks dividers */
  clkinitstruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2);
  clkinitstruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  clkinitstruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  clkinitstruct.APB2CLKDivider = RCC_HCLK_DIV1;
  clkinitstruct.APB1CLKDivider = RCC_HCLK_DIV2;  
  if (HAL_RCC_ClockConfig(&clkinitstruct, FLASH_LATENCY_2)!= HAL_OK)
  {
    /* Initialization Error */
    while(1); 
  }
}

整個程式設計步驟就是,hal庫初始化->開外設時鐘->外設初始化->使用者程式,然後在msp.c檔案裡實現其他平臺相關的雜七雜八的操作,需要呼叫的時候會自動呼叫,我這裡只實現了一個點亮led的功能,故只實現了HAL_MspInit()函式。如果我們要使用uart、adc等其他更復雜的外設,我們需要在msp.c檔案裡重寫HAL_UART_MspInit()、HAL_ADC_MspInit()等函式,當我們呼叫HAL_PPP_Init()時,他們都會自動被呼叫。

說到這裡,我要說一下這裡其實使用了一個c語言的技巧,實現了類似於c++的過載功能。比如我們來看UART的原始檔:

/**
  * @brief  USART MSP Init.
  * @param  husart: Pointer to a USART_HandleTypeDef structure that contains
  *                 the configuration information for the specified USART module.
  * @retval None
  */
 __weak void HAL_USART_MspInit(USART_HandleTypeDef *husart)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(husart);
  /* NOTE: This function should not be modified, when the callback is needed,
           the HAL_USART_MspInit can be implemented in the user file
   */ 
}

函式被__weak修飾了,意思就是,如果別處沒定義,這個函式就是他,如果別處重定義了,就用新的函式,這樣就實現了過載。這有一個很大的好處,就是實現oo思想中的差異化程式設計,hal實現所有硬體通用的功能,而把不通用的部分通過可過載的函式開放給使用者修改。

體現oo的還有個地方,每個函式中,都會接收到一個handle指標,這其實和this指標非常類似,每個函式都不用知道自己到底是在操作某一個具體的物件,只需要根據handle的指向來操作就可以了。

回到上面的過載。在hal庫中有一點比較大的改變是,中斷都是通過回撥函式來開放給使用者的,具體使用方式也是過載相關回調函式,不像標準庫是直接在stm32fxxx_it.c裡填寫相關中斷處理函式。這樣做的好處是,hal幫我們處理了一些中斷來臨時的雜務,只把我們感興趣的事件開放給使用者。

但是個人覺得這個改變需要再徹底一點,因為這並沒有解決程式碼耦合性的痛點,每一次我們需要寫中斷函式的時候,總是要去改底層程式碼,而如果st給我們實現一個註冊回撥的介面,那麼上層和下層之前就完全分離了,應用層各個模組之間也不會產生耦合。

總結:
總體而言,hal相比於標準庫,層次架構更加清晰了,對平臺更加抽象,但是還遠遠不夠,依然非常依賴於具體的硬體,如果能實現Qt的那種抽象就完美了。使用者使用的時候,只用包含hal.h而不用去管是hal_f1還是hal_f2或是什麼其他系列的標頭檔案,所有系列的程式碼打包在一起,通過條件編譯來實現真正的跨平臺,而如果需要使用某款mcu的特色功能時,就再包含一個hal_f1extend.h。如果這些st都實現了,那麼微控制器程式設計將會變得和應用程式設計一樣簡單方便!

相關推薦

stm32-hal開發入門

很久之前就聽說st出了一個新版本的庫,用於代替原來的標準庫,非常好奇,但是一直沒有機會去體驗。這次藉著做畢設的機會,嘗試著切換到新庫。 官網介紹說,hal(hardware abstract layer)是一層硬體的抽象,看到這裡,我非常激動,看來st終於意識到原來標準庫的問題了,原來的標準庫非常依賴於具體

【書籍連載】《STM32 HAL 開發實戰指南—基於F7》-第一章

第三部分 http 使用方法 參考 必須 並不會 簡介 過程 學習過程 從今天起,每天開始連載一章《STM32 HAL 庫開發實戰指南—基於F7》。歡迎各位閱讀、點評、學習。 第1章 如何使用本書 1.1 本書的參考資料 本書參考資料為:《STM32F76xxx參考手冊

stm32 HAL筆記(一)——串口的操作

可能 硬件 字節 tro define 數據位 tst flow hand   昨天分析了普通io口的使用,和初始化代碼流程,回顧一下,首先定義一個配置io口功能的結構體,然後開啟時鐘,再去配置這個結構體裏面的各個成員變量,每個成員變量都有很多種選擇,可以看各個成員變量 後

STM32 HAL、標準外設、LL

轉自 http://www.stmcu.org.cn/module/forum/thread-612445-1-1.html 工作以來一直使用ST的STM32系列晶片,ST為開發者提供了非常方便的開發庫。到目前為止,有標準外設庫(STD庫)、HAL庫、LL庫 三種。前兩者都是常用的庫,

STM32 HAL學習(五)RCC時鐘樹分析

開發板採用STM32F070RB,最大時鐘速率為48MHZ,在STM32Cube MX中可以選擇對應的晶片,得到它的時鐘樹如下所示: 分析時鐘樹,幾種主要的時鐘源分析如下 1、SYSCLK系統時鐘的時鐘源       &n

STM32 HAL描述

官方下載的HAL庫內容包括:STM32Cube HAL庫檔案、中介軟體(RTOS、USB、TCP/IP、Graphics)、一系列的外設應用例程。 HAL驅動建立在一套通用的體系結構之上,主要提供一套API介面以便更好地與上層應用進行通訊;HAL驅動函式嚴格按照ANSI-C標準編寫,因此可獨立於

STM32 HAL學習(四) SPI查詢傳送與接收

又是花了兩天時間調SPI......細心細心還是需要細心啊,還是用的上次的SPI Flash晶片mx25l04600E,主要是測試晶片的初始化和讀取晶片ID是否成功。 STM32F070晶片只有一個SPI,但可用作SPI訊號管腳的引腳卻不只一組,建議通訊前先連線MOSI和MISO測試自發自收是否

STM32 HAL學習(三)ADC取樣以及printf的使用

ADC輸出的電壓值經轉換後一般為一個浮點數,要將其列印到串列埠則需要對串列埠輸出函式做一個調整。 首先是printf函式的重定向,將一下程式碼加入到usart.c中,完成後便可以用printf函式輸出字串到串列埠中,而要輸出浮點數,則還需在“專案/建立設定/C Linker/Miscellane

stm32 hal串列埠通訊資料彙集

串列埠的傳送接收函式: HAL_UART_Transmit();串列埠輪詢模式傳送,使用超時管理機制。 HAL_UART_Receive();串列埠輪詢模式傳送,使用超時管理機制。 HAL_UART_Transmit_IT();串列埠中斷模式傳送, HAL_UART_Receive_IT();串

STM32 HAL串列埠 填坑

CUBEMAX生成的usart.c檔案修改如下,沒有效率強迫症的人可以參考 /** ****************************************************************************** * File Name

STM32 HALRTC模組列印時間不準的問題

正點原子 STM32F7阿波羅開發板,CubeMx生成工程,使用HAL庫的RTC模組時,發現了列印時間不準的問題,現記錄查證過程。 1、CubeMx的配置反覆查證過,沒有問題。  2、main函式中加入測試程式碼如下:     RTC_DateTypeDef stRt

STM32 HAL學習(一) STM32CubeMX和TRUEStudio的使用

最近開始學習STM32的HAL庫,之前有用過std庫函式做STM32開發的基礎,因此學習HAL庫會更容易上手一些,但對於新的程式設計、下載工具以及HAL庫仍有許多不清晰的地方。經過初步的實驗實現點亮LED燈和串列埠的傳送資料之後,對所用到的幾個工具以及HAL庫進行一個總結。

STM32 HAL使用中斷實現串列埠接收不定長資料

  以前用DMA實現接收不定長資料,DMA的方法接收串列埠助手的資料,全部沒問題,不過如果接收模組返回的資料,而這些資料如果包含回車換行的話就會停止接收,例如接收:AT\r\nOK\r\n,就只能接收到AT\r,導致沒有接收完成,具體原因還沒搞懂,有了解的,希望可以告知一下,DMA不定長接收方法傳輸門:htt

FreeRTOS移植,基於STM32 HAL

一、硬體準備 一個STM32開發板(STM32F429IGT6),及其電源線等; 一個ST-Link下載器及其連線線等。 二、軟體準備 FreeRTOS原始碼(V9.0.0); 一個基於STM32 HAL庫的基礎例程(跑馬燈例程)。 三、移植FreeRT

STM32 HAL

STM32標準外設庫http://www.st.com/content/st_com/en/products/embedded-software/mcus-embedded-software/stm32-embedded-software/stm32-standard-per

STM32 HAL學習系列第3篇 常使用的幾種延時方式

1   自帶的hal_delay 函式    毫秒級延遲void HAL_Delay(__IO uint32_t Delay) { uint32_t tickstart = HAL_GetTick(

STM32 HAL學習系列第10篇---串列埠空閒中斷接收不定長資料

串列埠重定向配置: 可以直接複製使用 /************************************************* * 函式功能: 重定向c庫函式printf到DEBUG

STM32 HAL的一些思考(一)SPI通訊的資料格式問題

眾所周知,STM32是一款價效比比較高的ARM晶片,並且它擁有極為豐富的外設,方便實現大部分的功能。2014年,意法半導體公司推出HAL(Hardware Abstracted Library)和配套的STM32CubeMX,更是讓STM32的開發變得易如反掌,

STM32 HAL 串列埠DMA(收發)和STM32串列埠中斷接收(接收時間管理機制)+ESP8266 wifi模組通訊問題

一、HAL庫 串列埠 DMA+ESP8266模組通訊問題 用STM32 HAL庫串列埠的DMA傳送和空閒中斷接收處理資料,單片機發送AT指令給ESP8266 wifi模組問題:微控制器連續幾次給wifi模組傳送AT指令,wifi模組總是少一次的應答,在無線通訊過程中是不方便

STM32 HAL標頭檔案包含關係

如圖,從左到右下的順序寫:   -----------------------------------------------------------------------------------------------------------------------