完整教程下載地址: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 初學者重要提示
- 學習本章節前,務必優先學習第36章,HAL庫的幾個常用API均作了講解和舉例。
- 使用LPTIM的好處是系統處於睡眠、停機狀態依然可以正常工作(除了待機模式)。停機狀態可以正常工作的關鍵是LSE,LSI時鍾不會被關閉,同時也可以選擇使用外部時鍾源。
- 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的系統時鍾。
注意事項:
- 關於此函數的相關注意事項在本章的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輸出
實驗目的:
- 學習低功耗定時器PWM輸出。
實驗內容:
- 使用LPTIM的好處是系統處於睡眠,停機狀態依然可以正常工作(除了待機模式)。停機狀態可以正常工作的關鍵是LSE,LSI時鍾不會被關閉,同時也可以選擇使用外部時鍾源。
- 例子默認用的LSE時鍾供LPTIM1使用,大家可以通過bsp_lptim_pwm.c文件開頭宏定義切換到LSI或者PLCK。
- PWM輸出引腳采用的PD13,輸出頻譜1024Hz,占空比50%。
實驗操作:
- K1鍵按下,進入低功耗的停機模式,LED2停止閃爍。
- 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輸出
實驗目的:
- 學習低功耗定時器PWM輸出。
實驗內容:
- 使用LPTIM的好處是系統處於睡眠,停機狀態依然可以正常工作(除了待機模式)。停機狀態可以正常工作的關鍵是LSE,LSI時鍾不會被關閉,同時也可以選擇使用外部時鍾源。
- 例子默認用的LSE時鍾供LPTIM1使用,大家可以通過bsp_lptim_pwm.c文件開頭宏定義切換到LSI或者PLCK。
- PWM輸出引腳采用的PD13,輸出頻譜1024Hz,占空比50%。
實驗操作:
- K1鍵按下,進入低功耗的停機模式,LED2停止閃爍。
- 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。