【STM32H7教程】第37章 STM32H7的LPTIM低功耗定時器應用之PWM


完整教程下載地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980

第37章       STM32H7的LPTIM低功耗定時器應用之PWM

本章教程為大家講解低功耗定時器的PWM輸出。使用LPTIM的好處是系統處於睡眠、停機狀態依然可以正常工作(除了待機模式)。實際項目中對於功耗有要求的場合,可以使用這種方式,可以一定程度上較低功耗。

37.1 初學者重要提示

37.2 低功耗定時器PWM驅動設計

37.3 低功耗定時器板級支持包(bsp_lptim_pwm.c)

37.4 低功耗定時器驅動移植和使用

37.5 實驗例程設計框架

37.6 實驗例程說明(MDK)

37.7 實驗例程說明(IAR)

37.8 總結

 

 

37.1 初學者重要提示

  1.   學習本章節前,務必優先學習第36章,HAL庫的幾個常用API均作了講解和舉例。
  2.   使用LPTIM的好處是系統處於睡眠、停機狀態依然可以正常工作(除了待機模式)。停機狀態可以正常工作的關鍵是LSE,LSI時鍾不會被關閉,同時也可以選擇使用外部時鍾源。
  3.   STM32H7從停機模式喚醒后要重新配置系統時鍾,這點跟F1,F4系列一樣。

37.2 低功耗定時器PWM驅動設計

低功耗定時器LPTIM1 – LPTIM5均支持PWM輸出。

37.2.1 低功耗定時器PWM輸出支持的引腳

STM32H7的低功耗定時器LPTIM1 - LPTIM5可以輸出到GPIO的TIM通道整理:

LPTIM1_IN1   PD12  PG12
LPTIM1_IN2   PH2   PE1
LPTIM1_OUT   PG13
LPTIM1_OUT   PD13
LPTIM1_ETR   PG14  PE0

LPTIM2_IN1   PB10  PD12
LPTIM2_IN2   PD11
LPTIM2_OUT   PB13
LPTIM2_ETR   PB11 PE0

LPTIM3_OUT   PA1
LPTIM4_OUT   PA2
LPTIM5_OUT   PA3

 

37.2.2 低功耗定時器時鍾選擇

由前面的第36章節,我們知道LPTIM1的時鍾可以由LSE,LSI,APB或者外部輸入時鍾提供。使用LSE,LSI或者外部輸入的好處是停機狀態下,LPTIM1也可以正常工作。

  •   V7開發板使用的LSE晶振是32768Hz。
  •   STM32H743的LSI頻率約32KHz。
  •   LPTIM1 – LPTIM5的頻率都是100MHz。
System Clock source       = PLL (HSE)
SYSCLK(Hz)                = 400000000 (CPU Clock)
HCLK(Hz)                  = 200000000 (AXI and AHBs Clock)
AHB Prescaler             = 2
D1 APB3 Prescaler         = 2 (APB3 Clock  100MHz)
D2 APB1 Prescaler         = 2 (APB1 Clock  100MHz)
D2 APB2 Prescaler         = 2 (APB2 Clock  100MHz)
D3 APB4 Prescaler         = 2 (APB4 Clock  100MHz)

因為APB1 prescaler != 1, 所以 APB1上的TIMxCLK = APB1 x 2 = 200MHz; 不含這個總線下的LPTIM1
因為APB2 prescaler != 1, 所以 APB2上的TIMxCLK = APB2 x 2 = 200MHz;

APB4上面的TIMxCLK沒有分頻,所以就是100MHz;

APB1 定時器有 TIM2, TIM3 ,TIM4, TIM5, TIM6, TIM7, TIM12, TIM13, TIM14,LPTIM1
APB2 定時器有 TIM1, TIM8 , TIM15, TIM16,TIM17

APB4 定時器有 LPTIM2,LPTIM3,LPTIM4,LPTIM5

 

下面為大家講解下使用LSE,LSI或者APB時鍾的配置方法。

  •   選擇LSE的配置如下:
#define LPTIM_CLOCK_SOURCE_LSE     /* LSE 時鍾32768Hz */

RCC_PeriphCLKInitTypeDef   RCC_PeriphCLKInitStruct = {0};
RCC_OscInitTypeDef RCC_OscInitStruct = {0};

RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSE;
RCC_OscInitStruct.LSEState = RCC_LSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;

if (HAL_RCC_OscConfig(&RCC_OscInitStruct)!= HAL_OK)
{
    Error_Handler(__FILE__, __LINE__);        
}

RCC_PeriphCLKInitStruct.PeriphClockSelection = RCC_PERIPHCLK_LPTIM1;
RCC_PeriphCLKInitStruct.Lptim1ClockSelection = RCC_LPTIM1CLKSOURCE_LSE;
HAL_RCCEx_PeriphCLKConfig(&RCC_PeriphCLKInitStruct);

 

特別注意程序中置紅的地方,這幾個地方很容易配置錯。配置后LPTIM1就會將LSE作為系統時鍾。

  •   選擇LSI的配置如下:
//#define LPTIM_CLOCK_SOURCE_LSI    /* LSI 時鍾約32KHz */
RCC_PeriphCLKInitTypeDef   RCC_PeriphCLKInitStruct = {0};
RCC_OscInitTypeDef RCC_OscInitStruct = {0};

RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSI;
RCC_OscInitStruct.LSIState = RCC_LSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;

if (HAL_RCC_OscConfig(&RCC_OscInitStruct)!= HAL_OK)
{
    Error_Handler(__FILE__, __LINE__);        
}

RCC_PeriphCLKInitStruct.PeriphClockSelection = RCC_PERIPHCLK_LPTIM1;
RCC_PeriphCLKInitStruct.Lptim1ClockSelection = RCC_LPTIM1CLKSOURCE_LSI;
HAL_RCCEx_PeriphCLKConfig(&RCC_PeriphCLKInitStruct);

 

使用LSI作為LPTIM1的系統是要注意兩點:

1、LSI的實現有一定的誤差,具體范圍在數據手冊有給出,由於不支持溫補,溫度對其也是有影響的。

 

2、特別注意程序中置紅的地方,這幾個地方很容易跟LSE搞混淆(復制粘貼的時候容易搞錯)。

  •   選擇APB時鍾的配置如下:
RCC_PeriphCLKInitTypeDef   RCC_PeriphCLKInitStruct = {0};

RCC_PeriphCLKInitStruct.PeriphClockSelection = RCC_PERIPHCLK_LPTIM1;
RCC_PeriphCLKInitStruct.Lptim1ClockSelection = RCC_LPTIM1CLKSOURCE_D2PCLK1;
HAL_RCCEx_PeriphCLKConfig(&RCC_PeriphCLKInitStruct);

 

使用APB作為LPTIM系統時鍾注意以下兩點:

1、   LPTIM1 – LPTIM5的最高主頻都是100MHz。

2、   注意參數RCC_LPTIM1CLKSOURCE_D2PCLK1。

LPTIM1使用的RCC_LPTIM1CLKSOURCE_D2PCLK1。

LPTIM2使用的RCC_LPTIM2CLKSOURCE_D3PCLK1。

LPTIM3-LPTIM5使用的RCC_LPTIM345CLKSOURCE_D3PCLK1。

37.2.3 低功耗定時器的PWM配置

下面通過低功耗定時器實現一個頻率為1024Hz,占空比50%,使用LSE做系統時鍾的配置。PWM輸出引腳采用PD13。

1.    /* 選擇LPTIM的時鍾源 */
2.    #define LPTIM_CLOCK_SOURCE_LSE     /* LSE 時鍾32768Hz */
3.    //#define LPTIM_CLOCK_SOURCE_LSI   /* LSI 時鍾約32KHz */ 
4.    //#define LPTIM_CLOCK_SOURCE_PCLK  /* PCLK 時鍾100MHz */
5.    /*
6.    ******************************************************************************************************
7.    *    函 數 名: bsp_InitTIMOutPWM
8.    *    功能說明: LPTIM1時鍾默認選擇的LSE,而PWM輸出使用的PD13引腳,頻率1024Hz。
9.    *    形    參: 無
10.    *    返 回 值: 無
11.    ******************************************************************************************************
12.    */
13.    void bsp_InitTIMOutPWM(void)
14.    {
15.        LPTIM_HandleTypeDef        LptimHandle = {0};    
16.        RCC_PeriphCLKInitTypeDef   RCC_PeriphCLKInitStruct = {0};
17.        GPIO_InitTypeDef           GPIO_InitStruct = {0};
18.    
19.        /* ## - 1 - 使能LPTIM時鍾和GPIO時鍾 ####################################### */
20.        __HAL_RCC_LPTIM1_CLK_ENABLE();
21.    
22.        __HAL_RCC_GPIOD_CLK_ENABLE();
23.    
24.        /* ## - 2 - 配置PD13做PWM輸出 ############################################ */    
25.        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
26.        GPIO_InitStruct.Pull = GPIO_PULLUP;
27.        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
28.        GPIO_InitStruct.Alternate = GPIO_AF1_LPTIM1;
29.        GPIO_InitStruct.Pin = GPIO_PIN_13;
30.        HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
31.    
32.        /* ## - 3 - 配置LPTIM時鍾,可以選擇LSE,LSI或者PCLK ######################## */        
33.    #if defined (LPTIM_CLOCK_SOURCE_LSE)
34.        {
35.            RCC_OscInitTypeDef RCC_OscInitStruct = {0};
36.    
37.            RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSE;
38.            RCC_OscInitStruct.LSEState = RCC_LSE_ON;
39.            RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
40.    
41.            if (HAL_RCC_OscConfig(&RCC_OscInitStruct)!= HAL_OK)
42.            {
43.                Error_Handler(__FILE__, __LINE__);        
44.            }
45.            
46.            RCC_PeriphCLKInitStruct.PeriphClockSelection = RCC_PERIPHCLK_LPTIM1;
47.            RCC_PeriphCLKInitStruct.Lptim1ClockSelection = RCC_LPTIM1CLKSOURCE_LSE;
48.            HAL_RCCEx_PeriphCLKConfig(&RCC_PeriphCLKInitStruct);
49.        }
50.    #elif defined (LPTIM_CLOCK_SOURCE_LSI)
51.        {
52.            RCC_OscInitTypeDef RCC_OscInitStruct = {0};
53.    
54.            RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSI;
55.            RCC_OscInitStruct.LSIState = RCC_LSI_ON;
56.            RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
57.    
58.            if (HAL_RCC_OscConfig(&RCC_OscInitStruct)!= HAL_OK)
59.            {
60.                Error_Handler(__FILE__, __LINE__);        
61.            }
62.            
63.            RCC_PeriphCLKInitStruct.PeriphClockSelection = RCC_PERIPHCLK_LPTIM1;
64.            RCC_PeriphCLKInitStruct.Lptim1ClockSelection = RCC_LPTIM1CLKSOURCE_LSI;
65.            HAL_RCCEx_PeriphCLKConfig(&RCC_PeriphCLKInitStruct);
66.        }
67.    #elif defined (LPTIM_CLOCK_SOURCE_PCLK)
68.        RCC_PeriphCLKInitStruct.PeriphClockSelection = RCC_PERIPHCLK_LPTIM1;
69.        RCC_PeriphCLKInitStruct.Lptim1ClockSelection = RCC_LPTIM1CLKSOURCE_D2PCLK1;
70.        HAL_RCCEx_PeriphCLKConfig(&RCC_PeriphCLKInitStruct);
71.    #else
72.        #error Please select the LPTIM Clock source inside the bsp_lptim_pwm.c file
73.    #endif
74.    
75.        /* ## - 4 - 配置LPTIM ######################################################## */        
76.        LptimHandle.Instance = LPTIM1;
77.         /* 對應寄存器CKSEL,選擇內部時鍾源 */
78.        LptimHandle.Init.Clock.Source    = LPTIM_CLOCKSOURCE_APBCLOCK_LPOSC; 
79.         /* 設置LPTIM時鍾分頻 */
80.        LptimHandle.Init.Clock.Prescaler = LPTIM_PRESCALER_DIV1;  
81.         /* LPTIM計數器對內部時鍾源計數 */     
82.        LptimHandle.Init.CounterSource   = LPTIM_COUNTERSOURCE_INTERNAL
83.         /* 軟件觸發 */
84.        LptimHandle.Init.Trigger.Source  = LPTIM_TRIGSOURCE_SOFTWARE;   
85.         /* 計數器計數到比較寄存器和ARR自動重載寄存器之間數值,輸出高電平 */
86.        LptimHandle.Init.OutputPolarity  = LPTIM_OUTPUTPOLARITY_HIGH;   
87.         /* 比較寄存器和ARR自動重載寄存器選擇更改后立即更新 */
88.        LptimHandle.Init.UpdateMode      = LPTIM_UPDATE_IMMEDIATE; 
89.         /* 外部輸入1,本配置未使用 */     
90.        LptimHandle.Init.Input1Source    = LPTIM_INPUT1SOURCE_GPIO;  
91.         /* 外部輸入2,本配置未使用 */   
92.        LptimHandle.Init.Input2Source    = LPTIM_INPUT2SOURCE_GPIO;    
93.    
94.        if (HAL_LPTIM_Init(&LptimHandle) != HAL_OK)
95.        {
96.            Error_Handler(__FILE__, __LINE__);
97.        }
98.        
99.    
100.        /* ## - 5 - 啟動LPTIM的PWM模式 ######################################################## */    
101.        /*
102.           ARR是自動重裝寄存器,對應函數HAL_LPTIM_PWM_Start的第2個參數
103.           Compare是比較寄存器,對應函數HAL_LPTIM_PWM_Start的第3個參數
104.    
105.           ---------------------
106.           LSE = 32768Hz
107.           分頻設置為LPTIM_PRESCALER_DIV1,即未分頻
108.           ARR自動重載寄存器 = 31
109.           那么PWM頻率 = LSE / (ARR + 1) = 32768Hz / (31 + 1) = 1024Hz
110.        
111.           占空比 = 1 - (Comprare + 1)/ (ARR + 1)
112.                  = 1 - (15 + 1)/(31 + 1)
113.                  = 50%
114.        
115.           占空比這里為什么要1減操作,而不是直接的(Comprare + 1)/ (ARR + 1),這是因為前面的配置中
116.           計數器計數到比較寄存器和ARR自動重載寄存器之間數值,輸出高電平。
117.        */
118.        if (HAL_LPTIM_PWM_Start(&LptimHandle, 31, 15) != HAL_OK)
119.        {
120.            Error_Handler(__FILE__, __LINE__);
121.        }
122.    }

 

這里把幾個關鍵的地方闡釋下:

  •   第2行,LPTIM1的系統時鍾選項LSE,頻率32768Hz。
  •   第15 – 17行,HAL庫的這個結構體變量要初始化為0,此問題在第36章的4.1小節有專門說明。
  •   第76 – 97行,第36章的3.2小節對這些參數成員有詳細描述。
  •   第118行,啟動PWM輸出,特別注意PWM的頻率和占空比的計算,在前面的注釋中已經講解的比較清楚。

37.2.4 低功耗定時器待機模式下正常運行

這里先補充三個知識點。

  •   LPTIM的好處是系統處於睡眠,停機狀態依然可以正常工作,但停機模式不能再正常工作。
  •   對於睡眠模式,任何受NVIC控制的中斷都可以喚醒休眠模式。進入睡眠模式調用函數HAL_PWR_EnterSLEEPMode即可。
  •   在系統停止模式下,1.2V供電域中的所有時鍾都停止,PLL,HSI和HSE RC振盪器被禁用。內部SRAM和寄存器內容保留。而LSE和LSI是可以正常工作的,所以LPTIM系統時鍾使用LSE或者LSI依然可以在停機模式下工作。

進入停機模式調用函數HAL_PWR_EnterSTOPMode即可。

 

對於停機模式,本章節配套的例子是通過GPIO的EXTI Event喚醒。配套如下:

/*
*********************************************************************************************************
*    函 數 名: PwrExitStopMode
*    功能說明: 起初按鍵K3的GPIO配置為輸入模式用於按鍵后,再次配置EXTI Event模式后,按鍵功能依然可以正常用。
*              配置為EXTI Event是因為可以喚醒停機模式。
*    形    參: 無
*    返 回 值: 無
*********************************************************************************************************
*/
static void PwrExitStopMode(void)
{
    GPIO_InitTypeDef              GPIO_InitStruct = {0};
        
    GPIO_InitStruct.Pin = GPIO_PIN_4;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Mode = GPIO_MODE_EVT_FALLING; /* 配置為下降沿觸發Event */     
    HAL_GPIO_Init(GPIOH, &GPIO_InitStruct);
}

 

37.3 低功耗定時器板級支持包(bsp_lptim_pwm.c)

低功耗定時器驅動文件bsp_lptim_pwm.c供用戶調用的只有如下一個函數:

  •   bsp_InitLPTIMOutPWM

 

下面將這個函數的使用為大家做個說明。

37.3.1 函數bsp_InitLPTIMOutPWM

函數原型:

void bsp_InitLPTIMOutPWM(void)

函數描述:

使用低功耗定時器LPTIM1實現一個頻率為1024Hz,占空比50%,使用LSE做LPTIM1的系統時鍾。

注意事項:

  1. 關於此函數的相關注意事項在本章的37.2小節有詳細說明。

使用舉例:

初始化函數在bsp.c文件的bsp_Init函數里面調用。

37.4 低功耗定時器驅動移植和使用

低功耗定時器的移植比較簡單:

  •   第1步:復制bsp_lptim_pwm.c和bsp_lptim_pwm.h到自己的工程目錄,並添加到工程里面。
  •   第2步:這幾個驅動文件主要用到HAL庫的GPIO和LPTIM驅動文件,簡單省事些可以添加所有HAL庫.C源文件進來。
  •   第3步,應用方法看本章節配套例子即可。如果用到按鍵喚醒的話,看main.c文件里面的函數PwrExitStopMode即可。

37.5 實驗例程設計框架

通過程序設計框架,讓大家先對配套例程有一個全面的認識,然后再理解細節,本次實驗例程的設計框架如下:

  第1階段,上電啟動階段:

這部分在第14章進行了詳細說明。

  第2階段,進入main函數:

  •   第1步,硬件初始化,主要是MPU,Cache,HAL庫,系統時鍾,滴答定時器,LED和串口。
  •   第2步,借助按鍵消息實現低功耗定時器的效果測試。

37.6 實驗例程說明(MDK)

配套例子:

V7-021_低功耗定時器PWM輸出

實驗目的:

  1. 學習低功耗定時器PWM輸出。

實驗內容:

  1. 使用LPTIM的好處是系統處於睡眠,停機狀態依然可以正常工作(除了待機模式)。停機狀態可以正常工作的關鍵是LSE,LSI時鍾不會被關閉,同時也可以選擇使用外部時鍾源。
  2. 例子默認用的LSE時鍾供LPTIM1使用,大家可以通過bsp_lptim_pwm.c文件開頭宏定義切換到LSI或者PLCK。
  3. PWM輸出引腳采用的PD13,輸出頻譜1024Hz,占空比50%。

實驗操作:

  1. K1鍵按下,進入低功耗的停機模式,LED2停止閃爍。
  2. K3鍵按下,退出停機模式,LED2繼續閃爍。

PD13的位置:

 

上電后串口打印的信息:

波特率 115200,數據位 8,奇偶校驗位無,停止位 1

 

程序設計:

  系統棧大小分配:

 

  RAM空間用的DTCM:

 

  硬件外設初始化

硬件外設的初始化是在 bsp.c 文件實現:

/*
*********************************************************************************************************
*    函 數 名: bsp_Init
*    功能說明: 初始化所有的硬件設備。該函數配置CPU寄存器和外設的寄存器並初始化一些全局變量。只需要調用一次
*    形    參:無
*    返 回 值: 無
*********************************************************************************************************
*/
void bsp_Init(void)
{
    /* 配置MPU */
    MPU_Config();
    
    /* 使能L1 Cache */
    CPU_CACHE_Enable();

    /* 
       STM32H7xx HAL 庫初始化,此時系統用的還是H7自帶的64MHz,HSI時鍾:
       - 調用函數HAL_InitTick,初始化滴答時鍾中斷1ms。
       - 設置NVIV優先級分組為4。
     */
    HAL_Init();

    /* 
       配置系統時鍾到400MHz
       - 切換使用HSE。
       - 此函數會更新全局變量SystemCoreClock,並重新配置HAL_InitTick。
    */
    SystemClock_Config();

    /* 
       Event Recorder:
       - 可用於代碼執行時間測量,MDK5.25及其以上版本才支持,IAR不支持。
       - 默認不開啟,如果要使能此選項,務必看V7開發板用戶手冊第xx章
    */    
#if Enable_EventRecorder == 1  
    /* 初始化EventRecorder並開啟 */
    EventRecorderInitialize(EventRecordAll, 1U);
    EventRecorderStart();
#endif
    
    bsp_InitKey();        /* 按鍵初始化,要放在滴答定時器之前,因為按鈕檢測是通過滴答定時器掃描 */
    bsp_InitTimer();      /* 初始化滴答定時器 */
    bsp_InitUart();    /* 初始化串口 */
    bsp_InitExtIO();    /* 初始化FMC總線74HC574擴展IO. 必須在 bsp_InitLed()前執行 */    
    bsp_InitLed();        /* 初始化LED */    
}

 

  MPU配置和Cache配置:

數據Cache和指令Cache都開啟。配置了AXI SRAM區(本例子未用到AXI SRAM)和FMC的擴展IO區。

/*
*********************************************************************************************************
*    函 數 名: MPU_Config
*    功能說明: 配置MPU
*    形    參: 無
*    返 回 值: 無
*********************************************************************************************************
*/
static void MPU_Config( void )
{
    MPU_Region_InitTypeDef MPU_InitStruct;

    /* 禁止 MPU */
    HAL_MPU_Disable();

    /* 配置AXI SRAM的MPU屬性為Write back, Read allocate,Write allocate */
    MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    MPU_InitStruct.BaseAddress      = 0x24000000;
    MPU_InitStruct.Size             = MPU_REGION_SIZE_512KB;
    MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
    MPU_InitStruct.IsCacheable      = MPU_ACCESS_CACHEABLE;
    MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    MPU_InitStruct.Number           = MPU_REGION_NUMBER0;
    MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;
    MPU_InitStruct.SubRegionDisable = 0x00;
    MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;

    HAL_MPU_ConfigRegion(&MPU_InitStruct);
    
    
    /* 配置FMC擴展IO的MPU屬性為Device或者Strongly Ordered */
    MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    MPU_InitStruct.BaseAddress      = 0x60000000;
    MPU_InitStruct.Size             = ARM_MPU_REGION_SIZE_64KB;    
    MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
    MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;    
    MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    MPU_InitStruct.Number           = MPU_REGION_NUMBER1;
    MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;
    MPU_InitStruct.SubRegionDisable = 0x00;
    MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;
    
    HAL_MPU_ConfigRegion(&MPU_InitStruct);

    /*使能 MPU */
    HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
}

/*
*********************************************************************************************************
*    函 數 名: CPU_CACHE_Enable
*    功能說明: 使能L1 Cache
*    形    參: 無
*    返 回 值: 無
*********************************************************************************************************
*/
static void CPU_CACHE_Enable(void)
{
    /* 使能 I-Cache */
    SCB_EnableICache();

    /* 使能 D-Cache */
    SCB_EnableDCache();
}

 

  主功能:

主程序實現如下操作:

  •   K1鍵按下,進入低功耗的停機模式,LED2停止閃爍。
  •   K3鍵按下,退出停機模式,LED2繼續閃爍。
/*
*********************************************************************************************************
*    函 數 名: main
*    功能說明: c程序入口
*    形    參: 無
*    返 回 值: 錯誤代碼(無需處理)
*********************************************************************************************************
*/
int main(void)
{
    uint8_t ucKeyCode;        /* 按鍵代碼 */
    

    bsp_Init();           /* 硬件初始化 */
    PwrExitStopMode(); /* 配置K3按鍵用於喚醒停機模式 */
    
    PrintfLogo();    /* 打印例程名稱和版本等信息 */
    PrintfHelp();    /* 打印操作提示 */

    bsp_StartAutoTimer(0, 100);    /* 啟動1個100ms的自動重裝的定時器 */
    
    /* 進入主程序循環體 */
    while (1)
    {
        bsp_Idle();        /* 這個函數在bsp.c文件。用戶可以修改這個函數實現CPU休眠和喂狗 */

        /* 判斷定時器超時時間 */
        if (bsp_CheckTimer(0))    
        {
            /* 每隔100ms 進來一次 */  
            bsp_LedToggle(2);
        }

        /* 按鍵濾波和檢測由后台systick中斷服務程序實現,我們只需要調用bsp_GetKey讀取鍵值即可。 */
        ucKeyCode = bsp_GetKey();    /* 讀取鍵值, 無鍵按下時返回 KEY_NONE = 0 */
        if (ucKeyCode != KEY_NONE)
        {
            switch (ucKeyCode)
            {
                case KEY_DOWN_K1:            /* K1鍵按下,進入停機模式 */
                    printf("--進入停機模式\r\n");
                    HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
                    SystemClock_Config();   /* 特別注意,退出后要重新配置系統時鍾 */
                    break;
                
                case KEY_DOWN_K3:            /* K3鍵按下,喚醒停機模式 */
                    printf("--退出停機模式\r\n");
                    break;

                default:
                    /* 其它的鍵值不處理 */
                    break;
            }
        }
    }
}

 

通過GPIO為EXTI Event可以喚醒停機模式:

/*
*********************************************************************************************************
*    函 數 名: PwrExitStopMode
*    功能說明: 起初按鍵K3的GPIO配置為輸入模式用於按鍵后,再次配置EXTI Event模式后,按鍵功能依然可以正常使*              用。配置為EXTI Event是因為可以喚醒停機模式。
*    形    參: 無
*    返 回 值: 無
*********************************************************************************************************
*/
static void PwrExitStopMode(void)
{
    GPIO_InitTypeDef              GPIO_InitStruct = {0};
        
    GPIO_InitStruct.Pin = GPIO_PIN_4;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Mode = GPIO_MODE_EVT_FALLING; /* 配置為下降沿觸發Event */     
    HAL_GPIO_Init(GPIOH, &GPIO_InitStruct);
}

 

37.7 實驗例程說明(IAR)

配套例子:

V7-021_低功耗定時器PWM輸出

實驗目的:

  1. 學習低功耗定時器PWM輸出。

實驗內容:

  1. 使用LPTIM的好處是系統處於睡眠,停機狀態依然可以正常工作(除了待機模式)。停機狀態可以正常工作的關鍵是LSE,LSI時鍾不會被關閉,同時也可以選擇使用外部時鍾源。
  2. 例子默認用的LSE時鍾供LPTIM1使用,大家可以通過bsp_lptim_pwm.c文件開頭宏定義切換到LSI或者PLCK。
  3. PWM輸出引腳采用的PD13,輸出頻譜1024Hz,占空比50%。

實驗操作:

  1. K1鍵按下,進入低功耗的停機模式,LED2停止閃爍。
  2. K3鍵按下,退出停機模式,LED2繼續閃爍。

PD13的位置:

 

上電后串口打印的信息:

波特率 115200,數據位 8,奇偶校驗位無,停止位 1

 

程序設計:

  系統棧大小分配:

 

  RAM空間用的DTCM:

 

  硬件外設初始化

硬件外設的初始化是在 bsp.c 文件實現:

/*
*********************************************************************************************************
*    函 數 名: bsp_Init
*    功能說明: 初始化所有的硬件設備。該函數配置CPU寄存器和外設的寄存器並初始化一些全局變量。只需要調用一次
*    形    參:無
*    返 回 值: 無
*********************************************************************************************************
*/
void bsp_Init(void)
{
    /* 配置MPU */
    MPU_Config();
    
    /* 使能L1 Cache */
    CPU_CACHE_Enable();

    /* 
       STM32H7xx HAL 庫初始化,此時系統用的還是H7自帶的64MHz,HSI時鍾:
       - 調用函數HAL_InitTick,初始化滴答時鍾中斷1ms。
       - 設置NVIV優先級分組為4。
     */
    HAL_Init();

    /* 
       配置系統時鍾到400MHz
       - 切換使用HSE。
       - 此函數會更新全局變量SystemCoreClock,並重新配置HAL_InitTick。
    */
    SystemClock_Config();

    /* 
       Event Recorder:
       - 可用於代碼執行時間測量,MDK5.25及其以上版本才支持,IAR不支持。
       - 默認不開啟,如果要使能此選項,務必看V7開發板用戶手冊第xx章
    */    
#if Enable_EventRecorder == 1  
    /* 初始化EventRecorder並開啟 */
    EventRecorderInitialize(EventRecordAll, 1U);
    EventRecorderStart();
#endif
    
    bsp_InitKey();        /* 按鍵初始化,要放在滴答定時器之前,因為按鈕檢測是通過滴答定時器掃描 */
    bsp_InitTimer();      /* 初始化滴答定時器 */
    bsp_InitUart();    /* 初始化串口 */
    bsp_InitExtIO();    /* 初始化FMC總線74HC574擴展IO. 必須在 bsp_InitLed()前執行 */    
    bsp_InitLed();        /* 初始化LED */    
}

 

  MPU配置和Cache配置:

數據Cache和指令Cache都開啟。配置了AXI SRAM區(本例子未用到AXI SRAM)和FMC的擴展IO區。

/*
*********************************************************************************************************
*    函 數 名: MPU_Config
*    功能說明: 配置MPU
*    形    參: 無
*    返 回 值: 無
*********************************************************************************************************
*/
static void MPU_Config( void )
{
    MPU_Region_InitTypeDef MPU_InitStruct;

    /* 禁止 MPU */
    HAL_MPU_Disable();

    /* 配置AXI SRAM的MPU屬性為Write back, Read allocate,Write allocate */
    MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    MPU_InitStruct.BaseAddress      = 0x24000000;
    MPU_InitStruct.Size             = MPU_REGION_SIZE_512KB;
    MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
    MPU_InitStruct.IsCacheable      = MPU_ACCESS_CACHEABLE;
    MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    MPU_InitStruct.Number           = MPU_REGION_NUMBER0;
    MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;
    MPU_InitStruct.SubRegionDisable = 0x00;
    MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;

    HAL_MPU_ConfigRegion(&MPU_InitStruct);
    
    
    /* 配置FMC擴展IO的MPU屬性為Device或者Strongly Ordered */
    MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    MPU_InitStruct.BaseAddress      = 0x60000000;
    MPU_InitStruct.Size             = ARM_MPU_REGION_SIZE_64KB;    
    MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
    MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;    
    MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    MPU_InitStruct.Number           = MPU_REGION_NUMBER1;
    MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;
    MPU_InitStruct.SubRegionDisable = 0x00;
    MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;
    
    HAL_MPU_ConfigRegion(&MPU_InitStruct);

    /*使能 MPU */
    HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
}

/*
*********************************************************************************************************
*    函 數 名: CPU_CACHE_Enable
*    功能說明: 使能L1 Cache
*    形    參: 無
*    返 回 值: 無
*********************************************************************************************************
*/
static void CPU_CACHE_Enable(void)
{
    /* 使能 I-Cache */
    SCB_EnableICache();

    /* 使能 D-Cache */
    SCB_EnableDCache();
}

 

  主功能:

主程序實現如下操作:

  •   K1鍵按下,進入低功耗的停機模式,LED2停止閃爍。
  •   K3鍵按下,退出停機模式,LED2繼續閃爍。
/*
*********************************************************************************************************
*    函 數 名: main
*    功能說明: c程序入口
*    形    參: 無
*    返 回 值: 錯誤代碼(無需處理)
*********************************************************************************************************
*/
int main(void)
{
    uint8_t ucKeyCode;        /* 按鍵代碼 */
    

    bsp_Init();           /* 硬件初始化 */
    PwrExitStopMode(); /* 配置K3按鍵用於喚醒停機模式 */
    
    PrintfLogo();    /* 打印例程名稱和版本等信息 */
    PrintfHelp();    /* 打印操作提示 */

    bsp_StartAutoTimer(0, 100);    /* 啟動1個100ms的自動重裝的定時器 */
    
    /* 進入主程序循環體 */
    while (1)
    {
        bsp_Idle();        /* 這個函數在bsp.c文件。用戶可以修改這個函數實現CPU休眠和喂狗 */

        /* 判斷定時器超時時間 */
        if (bsp_CheckTimer(0))    
        {
            /* 每隔100ms 進來一次 */  
            bsp_LedToggle(2);
        }

        /* 按鍵濾波和檢測由后台systick中斷服務程序實現,我們只需要調用bsp_GetKey讀取鍵值即可。 */
        ucKeyCode = bsp_GetKey();    /* 讀取鍵值, 無鍵按下時返回 KEY_NONE = 0 */
        if (ucKeyCode != KEY_NONE)
        {
            switch (ucKeyCode)
            {
                case KEY_DOWN_K1:            /* K1鍵按下,進入停機模式 */
                    printf("--進入停機模式\r\n");
                    HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
                    SystemClock_Config();   /* 特別注意,退出后要重新配置系統時鍾 */
                    break;
                
                case KEY_DOWN_K3:            /* K3鍵按下,喚醒停機模式 */
                    printf("--退出停機模式\r\n");
                    break;

                default:
                    /* 其它的鍵值不處理 */
                    break;
            }
        }
    }
}

 

通過GPIO為EXTI Event可以喚醒停機模式:

/*
*********************************************************************************************************
*    函 數 名: PwrExitStopMode
*    功能說明: 起初按鍵K3的GPIO配置為輸入模式用於按鍵后,再次配置EXTI Event模式后,按鍵功能依然可以正常使*              用。配置為EXTI Event是因為可以喚醒停機模式。
*    形    參: 無
*    返 回 值: 無
*********************************************************************************************************
*/
static void PwrExitStopMode(void)
{
    GPIO_InitTypeDef              GPIO_InitStruct = {0};
        
    GPIO_InitStruct.Pin = GPIO_PIN_4;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Mode = GPIO_MODE_EVT_FALLING; /* 配置為下降沿觸發Event */     
    HAL_GPIO_Init(GPIOH, &GPIO_InitStruct);
}

 

37.8 總結

本章節就為大家講解這么多,低功耗定時器在低功耗場合比較有用,如果有低功耗方面的項目需求,可以考慮是這個定時器實現PWM。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM