日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

STM32 之十一 LL 库(low-layer drivers)详解 及 移植说明

發布時間:2024/10/14 编程问答 88 豆豆
生活随笔 收集整理的這篇文章主要介紹了 STM32 之十一 LL 库(low-layer drivers)详解 及 移植说明 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

??最新項目中需要使用 STM32L476 的片子。在選擇片子時,資源的多少成為了一個比較重要的考量。在斟酌一番之后,我決定采用 LL 庫來實現本次的功能。下面就以 STM32L476 為例來介紹一下 LL 庫(low-layer drivers)。下面是ST 中文官網上一篇《關于ST庫函數的代碼性能對比》的文章中對比了各種庫的性能的圖示:

關于 ST 各種庫的介紹,可以參見博文《STM32 之一 HAL庫、標準外設庫、LL庫(STM32 Embedded Software)》

文檔

??LL 庫一直是與 Cube HAL 庫捆綁發布的。我們可以自己從 ST 官網下載對應的 Cube 包 STM32CubeL4 ,也可以直接在 CubeMX 中下載。對應的文檔也是和 HAL 庫在同一個文檔中。名為 UM1884:Description of STM32L4/L4+ HAL and low-layer drivers,這里就不演示如何下載了。本次我們只需要關系文檔中的 LL 庫相關的章節即可。

簡介

??LL 庫旨在提供快速輕巧的面向專家的層,其比 HAL 庫更接近硬件。 與 HAL 相反,LL API 不是提供給優化訪問不是關鍵功能的外圍設備或需要繁重的軟件配置和/或復雜的上層協議棧(例如 FSMC,USB 或 SDMMC)。
??在設計上,LL 庫的 API 旨在用于獨立模式或與 HAL 庫結合使用。 不過它們不能與 HAL庫同時用于相同的外設實例。如果您將 LL API 用于特定的外設實例,那么您仍然可以將 HAL API 用于其他外設實例。注意,LL API 可能會覆蓋一些寄存器,這些寄存器的內容被映射到 HAL 句柄中。

特點

??LL 庫的一大特點就是巧妙的運用 C 語言的靜態、內聯函數來直接操作寄存器。你會在 LL 庫 .h 文件中發現大量的類似的靜態內聯函數。也正是因此,在 LL 庫中,只有少數函數接口是放在 .c 文件中的。
??所有的外設接口都是一個模板,下面以 ADC 為例來簡單說明一下。每個 .h 文件的內容從前到后,大都可以分為四大部分:

  • 代表各寄存器位或者參數的常量值(#define),例如 ADC 通道的定義:

    #define LL_ADC_CHANNEL_0 (ADC_CHANNEL_0_NUMBER | ADC_CHANNEL_0_SMP | ADC_CHANNEL_0_BITFIELD ) /*!< ADC external channel (channel connected to GPIO pin) ADCx_IN0 */
  • 寄存器讀寫宏函數以及一些基本運算宏函數。其中,寄存器讀寫宏函數是所有內聯函數的基礎。

    /*** @brief Write a value in ADC register* @param __INSTANCE__ ADC Instance* @param __REG__ Register to be written* @param __VALUE__ Value to be written in the register* @retval None*/ #define LL_ADC_WriteReg(__INSTANCE__, __REG__, __VALUE__) WRITE_REG(__INSTANCE__->__REG__, (__VALUE__))/*** @brief Read a value in ADC register* @param __INSTANCE__ ADC Instance* @param __REG__ Register to be read* @retval Register value*/ #define LL_ADC_ReadReg(__INSTANCE__, __REG__) READ_REG(__INSTANCE__->__REG__)
  • 函數參數結構體以及靜態內聯函數。LL 絕大多數函數即可都是靜態內聯函數。使用 LL 庫,我們絕大多數都是使用這些內聯函數即可。例如:

    typedef struct {uint32_t Resolution; /*!< Set ADC resolution.This parameter can be a value of @ref ADC_LL_EC_RESOLUTIONThis feature can be modified afterwards using unitary function @ref LL_ADC_SetResolution(). */uint32_t DataAlignment; /*!< Set ADC conversion data alignment.This parameter can be a value of @ref ADC_LL_EC_DATA_ALIGNThis feature can be modified afterwards using unitary function @ref LL_ADC_SetDataAlignment(). */uint32_t LowPowerMode; /*!< Set ADC low power mode.This parameter can be a value of @ref ADC_LL_EC_LP_MODEThis feature can be modified afterwards using unitary function @ref LL_ADC_SetLowPowerMode(). */} LL_ADC_InitTypeDef; /*** @brief Set ADC resolution.* Refer to reference manual for alignments formats* dependencies to ADC resolutions.* @note On this STM32 serie, setting of this feature is conditioned to* ADC state:* ADC must be disabled or enabled without conversion on going* on either groups regular or injected.* @rmtoll CFGR RES LL_ADC_SetResolution* @param ADCx ADC instance* @param Resolution This parameter can be one of the following values:* @arg @ref LL_ADC_RESOLUTION_12B* @arg @ref LL_ADC_RESOLUTION_10B* @arg @ref LL_ADC_RESOLUTION_8B* @arg @ref LL_ADC_RESOLUTION_6B* @retval None*/ __STATIC_INLINE void LL_ADC_SetResolution(ADC_TypeDef *ADCx, uint32_t Resolution) {MODIFY_REG(ADCx->CFGR, ADC_CFGR_RES, Resolution); }/*** @brief Get ADC resolution.* Refer to reference manual for alignments formats* dependencies to ADC resolutions.* @rmtoll CFGR RES LL_ADC_GetResolution* @param ADCx ADC instance* @retval Returned value can be one of the following values:* @arg @ref LL_ADC_RESOLUTION_12B* @arg @ref LL_ADC_RESOLUTION_10B* @arg @ref LL_ADC_RESOLUTION_8B* @arg @ref LL_ADC_RESOLUTION_6B*/ __STATIC_INLINE uint32_t LL_ADC_GetResolution(ADC_TypeDef *ADCx) {return (uint32_t)(READ_BIT(ADCx->CFGR, ADC_CFGR_RES)); }
  • 配合 .c 的函數。這部分主要是一些函數體較長不適合作為內聯函數的函數接口。這部分接口是封裝的一些常用的操作。例如,我們在操作 RTC 時的流程:

  • 將 RTC_ISR 寄存器中的 INIT 位置 1 以進入初始化模式。在此模式下,日歷計數器將停 止工作并且其值可更新。
  • 輪詢 RTC_ISR 寄存器中的 INITF 位。當 INITF 置 1 時進入初始化階段模式。大約需要 2 個 RTCCLK 時鐘周期(由于時鐘同步)。
  • 要為日歷計數器生成 1 Hz 時鐘,應首先編程 RTC_PRER 寄存器中的同步預分頻系數, 然后編程異步預分頻系數。即使只需要更改這兩個字段中之一,也必須對 RTC_PRER 寄存器執行兩次單獨的寫訪問。
  • 在影子寄存器(RTC_TR 和 RTC_DR)中加載初始時間和日期值,然后通過 RTC_CR 寄存器中的 FMT 位配置時間格式(12 或 24 小時制)。
  • 通過清零 INIT 位退出初始化模式。隨后,自動加載實際日歷計數器值,在 4 個 RTCCLK 時鐘周期后重新開始計數。
  • ??我們可以使用 stm32l4xx_ll_rtc.h 中的各內聯函數實現以上流程來字節實現一個寫時間或者日期的接口。同時,stm32l4xx_ll_rtc.c ST也為我們提供封裝好的接口ErrorStatus LL_RTC_DATE_Init(RTC_TypeDef *RTCx, uint32_t RTC_Format, LL_RTC_DateTypeDef *RTC_DateStruct) 和 ErrorStatus LL_RTC_TIME_Init(RTC_TypeDef *RTCx, uint32_t RTC_Format, LL_RTC_TimeTypeDef *RTC_TimeStruct)。至于如何取舍就看自己了!
    ??但是需要注意:這部分接口僅僅封裝了很少的一部分! 大多數我們還是需要自己根據寄存器操作流程來封裝自己的接口!例如,讀取 RTC 時間或者日期,需要先檢查并等待 RTC_ISR 寄存器中的 RSF 位置 1 時才可以讀取(影子寄存器模式下)。

文件結構

??LL 庫圍繞 .h / .c 文件構建,每個受支持的外圍設備一個獨立的文件,外加上五個與某些系統和 Cortex 相關功能的頭文件。具體如下:

除了以上這些文件以外,LL 庫 和 HAL 庫共享一部分文件。這部分文件位于 CMSIS 中。主要是對于 MCU 中寄存器的封裝定義,如下圖所示:

紅色框中的文件是與 HAL 庫共享的。上圖中的其他幾個文件原則上來說數據用戶層文件,不屬于庫文件。下圖顯示了 LL 庫文件的包含關系:

通常來說,其只會包含 CMSIS 中的兩個文件。

移植使用

??LL 庫的使用既可以獨立使用也可以和 HAL 庫混合使用。 但是統一外設不能即使用 HAL 庫,又使用 LL 庫。例如,不可使用 HAL 庫初始化外設,然后使用 LL 庫讀寫外設。配套文檔中有專門的兩個章節介紹:

手動移植

??總體來說,LL 庫的手動移植與標準外設庫基本一致,就時鐘源配置有些區別!先來看看移植之后的最終結果圖:

下面我們一步一步來介紹一下如何手動移植。

第一步:復制庫文件

??首先我們需要從 ST 給的對應的芯片軟件包里提取出需要的庫文件(如果你不在意項目中一堆無用的文件的話,可以不提取),具體需要的庫文件如下:

當然我們只需要復制一些必須的文件,杜絕出現一堆無用的文件,全部庫文件如下:

第二步:復制用戶層文件

??根據 ST 驅動開發架構,我們的用戶層文件中必須包含幾個特定的文件。由于這幾個文件需要根據用戶的功能而變化,因此他們并不屬于庫文件,而屬于用戶層文件。具體如下:

我們只需要從任意例子中查到這幾個文件就可以,后面我們需要更加自己的需要修改里面的內容,首要的是刪除里面的不需要的內容。復制到自己的用戶層目錄中后,我一般會根據我的程序架構,調整里面的內容,刪除不需要的東西。基本我會根據我的程序架構,將這幾個文件的內容全部重整。
??其中需要注意的是 stm32_assert.h 這個文件。這個文件是庫文件源碼目錄中的 stm32_assert_template.h 復制到用戶目錄中并更名的!這是 LL 庫唯一一個需要用戶處理的文件。

第三步:修改配置

??LL 庫相比于 HAL 庫 和 標準外設庫,一個特點就是沒有了庫的配置文件。但是 CMSIS 中要求的配置以及 MCU 需要的配置還是必須的!
??這里有個配置技巧,ST的文件中也都有說明,就是將配置項配置到自己的編譯工具鏈中,從而避免修改各個配置文件導致移植時的麻煩。以 MDK-ARM 為例,我們可以把配置項如下配置:

其他 IDE 的配置基本類似,這里就不一一說明了。下面我們具體來說明一下 LL 庫的使用需要進行哪些配置。

斷言的配置

??在 LL 庫中,默認使用了斷言(assert),斷言的定義位于文件 stm32_assert.h 中。如果要使用默認的斷言,則該文件要求,在自己的編譯工具鏈中定義宏值 USE_FULL_ASSERT。注意,如果與 HAL 庫混合使用,可能會導致重定義(HAL 庫中也存在默認斷言的定義)。使用時自己注意一下!

/* Define to prevent recursive inclusion -------------------------------------*/ #ifndef STM32_ASSERT_H #define STM32_ASSERT_H#ifdef __cplusplusextern "C" { #endif/* Exported types ------------------------------------------------------------*/ /* Exported constants --------------------------------------------------------*/ /* Includes ------------------------------------------------------------------*/ /* Exported macro ------------------------------------------------------------*/ #ifdef USE_FULL_ASSERT /*** @brief The assert_param macro is used for function's parameters check.* @param expr: If expr is false, it calls assert_failed function* which reports the name of the source file and the source* line number of the call that failed.* If expr is true, it returns no value.* @retval None*/#define assert_param(expr) ((expr) ? (void)0U : assert_failed((char *)__FILE__, __LINE__)) /* Exported functions ------------------------------------------------------- */void assert_failed(char *file, uint32_t line); #else#define assert_param(expr) ((void)0U) #endif /* USE_FULL_ASSERT */#ifdef __cplusplus } #endif#endif /* STM32_ASSERT_H */

配置使用的芯片類型

??LL 庫本身支持多個系列的 MCU,我們必須要配置使用的芯片型號。這是 CMSIS 中的 stm32l4xx.h 文件中要求的。如下是未配置之前的原文件部分內容:

/** @addtogroup Library_configuration_section* @{*//*** @brief STM32 Family*/ #if !defined (STM32L4) #define STM32L4 #endif /* STM32L4 *//* Uncomment the line below according to the target STM32L4 device used in yourapplication*/#if !defined (STM32L412xx) && !defined (STM32L422xx) && \!defined (STM32L431xx) && !defined (STM32L432xx) && !defined (STM32L433xx) && !defined (STM32L442xx) && !defined (STM32L443xx) && \!defined (STM32L451xx) && !defined (STM32L452xx) && !defined (STM32L462xx) && \!defined (STM32L471xx) && !defined (STM32L475xx) && !defined (STM32L476xx) && !defined (STM32L485xx) && !defined (STM32L486xx) && \!defined (STM32L496xx) && !defined (STM32L4A6xx) && \!defined (STM32L4P5xx) && !defined (STM32L4Q5xx) && \!defined (STM32L4R5xx) && !defined (STM32L4R7xx) && !defined (STM32L4R9xx) && !defined (STM32L4S5xx) && !defined (STM32L4S7xx) && !defined (STM32L4S9xx)/* #define STM32L412xx */ /*!< STM32L412xx Devices *//* #define STM32L422xx */ /*!< STM32L422xx Devices *//* #define STM32L431xx */ /*!< STM32L431xx Devices *//* #define STM32L432xx */ /*!< STM32L432xx Devices *//* #define STM32L433xx */ /*!< STM32L433xx Devices *//* #define STM32L442xx */ /*!< STM32L442xx Devices *//* #define STM32L443xx */ /*!< STM32L443xx Devices *//* #define STM32L451xx */ /*!< STM32L451xx Devices *//* #define STM32L452xx */ /*!< STM32L452xx Devices *//* #define STM32L462xx */ /*!< STM32L462xx Devices *//* #define STM32L471xx */ /*!< STM32L471xx Devices *//* #define STM32L475xx */ /*!< STM32L475xx Devices *//* #define STM32L476xx */ /*!< STM32L476xx Devices *//* #define STM32L485xx */ /*!< STM32L485xx Devices *//* #define STM32L486xx */ /*!< STM32L486xx Devices *//* #define STM32L496xx */ /*!< STM32L496xx Devices *//* #define STM32L4A6xx */ /*!< STM32L4A6xx Devices *//* #define STM32L4P5xx */ /*!< STM32L4Q5xx Devices *//* #define STM32L4R5xx */ /*!< STM32L4R5xx Devices *//* #define STM32L4R7xx */ /*!< STM32L4R7xx Devices *//* #define STM32L4R9xx */ /*!< STM32L4R9xx Devices *//* #define STM32L4S5xx */ /*!< STM32L4S5xx Devices *//* #define STM32L4S7xx */ /*!< STM32L4S7xx Devices *//* #define STM32L4S9xx */ /*!< STM32L4S9xx Devices */ #endif/* Tip: To avoid modifying this file each time you need to switch between thesedevices, you can define the device in your toolchain compiler preprocessor.*/

通常,我們是把需要的芯片類型定義到自己的編譯工具鏈中,而不是去修改該文件!

配置時鐘源

??STM32 系列的 MCU 都有一個很豐富的時鐘源配置功能,滿足用戶的各種需求。時鐘的配置是用戶層文件 system_stm32l4xx.c 必須的。該文件中有如下內容:

#if !defined (HSE_VALUE)#define HSE_VALUE 8000000U /*!< Value of the External oscillator in Hz */ #endif /* HSE_VALUE */#if !defined (MSI_VALUE)#define MSI_VALUE 4000000U /*!< Value of the Internal oscillator in Hz*/ #endif /* MSI_VALUE */#if !defined (HSI_VALUE)#define HSI_VALUE 16000000U /*!< Value of the Internal oscillator in Hz*/ #endif /* HSI_VALUE */

通常,我們是把使用的時鐘源類型及頻率定義到自己的編譯工具鏈中,而不是去修改該文件!
??選擇了時鐘源之后,我們必須要對 MCU 進行配置,以啟動切換到選擇的時鐘源。通常在 main 函數的一開始就必須要先配置時鐘源。在 ST 給的例子中的 main 函數中,就有配置的函數

/*** @brief System Clock Configuration* The system Clock is configured as follows :* System Clock source = PLL (MSI)* SYSCLK(Hz) = 80000000* HCLK(Hz) = 80000000* AHB Prescaler = 1* APB1 Prescaler = 1* APB2 Prescaler = 1* MSI Frequency(Hz) = 4000000* PLL_M = 1* PLL_N = 40* PLL_R = 2* Flash Latency(WS) = 4* @param None* @retval None*/ void SystemClock_Config(void) {/* MSI configuration and activation */LL_FLASH_SetLatency(LL_FLASH_LATENCY_4);LL_RCC_MSI_Enable();while(LL_RCC_MSI_IsReady() != 1) {};/* Main PLL configuration and activation */LL_RCC_PLL_ConfigDomain_SYS(LL_RCC_PLLSOURCE_MSI, LL_RCC_PLLM_DIV_1, 40, LL_RCC_PLLR_DIV_2);LL_RCC_PLL_Enable();LL_RCC_PLL_EnableDomain_SYS();while(LL_RCC_PLL_IsReady() != 1) {};/* Sysclk activation on the main PLL */LL_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_1);LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_PLL);while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_PLL) {};/* Set APB1 & APB2 prescaler*/LL_RCC_SetAPB1Prescaler(LL_RCC_APB1_DIV_1);LL_RCC_SetAPB2Prescaler(LL_RCC_APB2_DIV_1);/* Set systick to 1ms in using frequency set to 80MHz *//* This frequency can be calculated through LL RCC macro *//* ex: __LL_RCC_CALC_PLLCLK_FREQ(__LL_RCC_CALC_MSI_FREQ(LL_RCC_MSIRANGESEL_RUN, LL_RCC_MSIRANGE_6), LL_RCC_PLLM_DIV_1, 40, LL_RCC_PLLR_DIV_2)*/LL_Init1msTick(80000000);/* Update CMSIS variable (which can be updated also through SystemCoreClockUpdate function) */LL_SetSystemCoreClock(80000000); }

我們必須要根據自己的需要修改該函數!!!

??注意,在函數的最后,必須要調用一下函數 void SystemCoreClockUpdate(void),否則,必須要手動修改 system_stm32l4xx.c 文件中的如下這些全局變量:

/** @addtogroup STM32L4xx_System_Private_Variables* @{*//* The SystemCoreClock variable is updated in three ways:1) by calling CMSIS function SystemCoreClockUpdate()2) by calling HAL API function HAL_RCC_GetHCLKFreq()3) each time HAL_RCC_ClockConfig() is called to configure the system clock frequencyNote: If you use this function to configure the system clock; then thereis no need to call the 2 first functions listed above, since SystemCoreClockvariable is updated automatically.*/uint32_t SystemCoreClock = 4000000U;const uint8_t AHBPrescTable[16] = {0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, 1U, 2U, 3U, 4U, 6U, 7U, 8U, 9U};const uint8_t APBPrescTable[8] = {0U, 0U, 0U, 0U, 1U, 2U, 3U, 4U};const uint32_t MSIRangeTable[12] = {100000U, 200000U, 400000U, 800000U, 1000000U, 2000000U, \4000000U, 8000000U, 16000000U, 24000000U, 32000000U, 48000000U}; /*** @}*/

配置中斷向量偏移

??在使用了在線升級(IAP)時,通常我們的程序需要分為兩部分,即在實際功能程序前必須有個額外的程序來處理升級。我們的實際功能程序就必須要有偏移。這個配置項也是用戶層文件 system_stm32l4xx.c 必須的。該文件中有如下內容:

/************************* Miscellaneous Configuration ************************/ /*!< Uncomment the following line if you need to relocate your vector Table inInternal SRAM. */ /* #define VECT_TAB_SRAM */ #define VECT_TAB_OFFSET 0x00 /*!< Vector Table base offset field.This value must be a multiple of 0x200. */ /******************************************************************************/

這個項不能配置到編譯工具鏈中,只能修改本文件!

配置 LL 庫

??最后,如果要完整使用 LL 庫,LL 庫要求必須要定義全局宏值 USE_FULL_LL_DRIVER。

至此,使用LL庫的全部文件已經整理完成,接下來就是根據自己的功能修改代碼即可!

CubeMX

??現在 CubeMX 生成代碼時,可以直接選擇 LL 庫。但是根據我之前的測試,其生成的代碼比較簡單,多半還需要自己再進行完善!具體如下:

直接生成沒啥意思,生成的 LL 庫的代碼,仍然需要大量自己修改!

編程事項

??通過 LL 庫的基本架構,我們不難發現,LL 就是通過內聯函數的形式封裝了一下對于寄存器的讀寫。用戶必須要自己掌握各外設的操作流程。例如,在 HAL 庫或者 SPL 庫中,外設的初始化都有專門的接口,用戶甚至不需要關心外設寄存器中,哪個配置項在前哪個在后。但是到了 LL 庫中,用戶必須知道外設配置項的先后操作順序。
??舉個例子來具體說明一下。RTC 時間或者日期的設置流程為:

  • 將 RTC_ISR 寄存器中的 INIT 位置 1 以進入初始化模式。在此模式下,日歷計數器將停止工作并且其值可更新
  • 輪詢 RTC_ISR 寄存器中的 INITF 位。當 INITF 置 1 時進入初始化階段模式。大約需要 2 個 RTCCLK 時鐘周期(由于時鐘同步)。
  • 要為日歷計數器生成 1 Hz 時鐘,應首先編程 RTC_PRER 寄存器中的同步預分頻系數, 然后編程異步預分頻系數。即使只需要更改這兩個字段中之一,也必須對 RTC_PRER 寄存器執行兩次單獨的寫訪問。
  • 在影子寄存器(RTC_TR 和 RTC_DR)中加載初始時間和日期值,然后通過 RTC_CR 寄存器中的 FMT 位配置時間格式(12 或 24 小時制)。
  • 通過清零 INIT 位退出初始化模式。隨后,自動加載實際日歷計數器值,在 4 個 RTCCLK 時鐘周期后重新開始計數。
  • 在 HAL 庫中,寫 RTC 時間函數接口 HAL_StatusTypeDef HAL_RTC_SetTime(RTC_HandleTypeDef *hrtc, RTC_TimeTypeDef *sTime, uint32_t Format); 的實現中已經完整封裝好了以上流程,我們直接調用即可!而使用 LL 庫我們不得不自己實現以上流程(使用 stm32l4xx_ll_rtc.h 中的內聯函數來實現):

    /** 設置時間** @param[in] t 要設置的參數指針* @retval 是否成功 **/ bool STM32RTC_SetTime(RTC_TIME *t) {if(!RTC_IS_BCD(t->ss) || RTC_BCD2DEC(t->ss) > RTC_ss_MAX|| !RTC_IS_BCD(t->mm) || RTC_BCD2DEC(t->mm) > RTC_mm_MAX|| !RTC_IS_BCD(t->hh) || RTC_BCD2DEC(t->hh) > RTC_hh_MAX){return false;}LL_RTC_DisableWriteProtection(RTC);LL_RTC_EnableInitMode(RTC);/* 輪詢 RTC_ISR 寄存器中的 INITF 位。當 INITF 置 1 時進入初始化階段模式。大約需要 2 個 RTCCLK 時鐘周期(由于時鐘同步)。 */while (LL_RTC_IsActiveFlag_INIT(RTC) != 1);LL_RTC_TIME_Config(RTC, LL_RTC_TIME_FORMAT_AM_OR_24, t->hh, t->mm, t->ss);LL_RTC_DisableInitMode(RTC);LL_RTC_EnableWriteProtection(RTC);return true; }

    ??當然,對于某些外設的常用操作,ST 也封裝了一些常用的操作接口。這些接口就是位于LL庫文件對應的 .c 文件中(區別于.h文件中的各內聯函數)的各接口,但是這部分接口很少,大多數還是需要我們自己來封裝需要的接口。例如以上流程,ST 就提供了一個封裝好的函數 LL_RTC_TIME_Init(RTC_TypeDef *RTCx, uint32_t RTC_Format, LL_RTC_TimeTypeDef *RTC_TimeStruct);來直接設置時間。其他外設基本也都還是這樣。

    注意:這部分接口并不可靠!不可靠!不可靠! 舉個例子,如下圖:

    右側為 RTC 中 鬧鈴 A 的初始化函數。乍一看沒啥問題。但是,如果終端復位,鬧鈴仍然是使能的。在使能情況下,無法寫各個鬧鈴相關的寄存器,這就意味著,復位初始化鬧鈴不會成功!對比左側 HAL庫,會先關閉鬧鈴相關的配置。

    參考

  • Description of STM32L4L4+ HAL and low-layer drivers.pdf
  • 總結

    以上是生活随笔為你收集整理的STM32 之十一 LL 库(low-layer drivers)详解 及 移植说明的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。