完整教程下載地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980
第38章 STM32H7的LPTIM低功耗定時器應用之超時喚醒
本章教程為大家講解定時器應用之超時模式的停機喚醒,實際項目中有一定的使用價值,可以方便的配置系統在停機模式運行一段時間,時間到了可以自動喚醒。
38.1 初學者重要提示
38.2 低功耗定時器超時喚醒驅動設計
38.3 低功耗定時器板級支持包(bsp_lptim_pwm.c)
38.4 低功耗定時器驅動移植和使用
38.5 實驗例程設計框架
38.6 實驗例程說明(MDK)
38.7 實驗例程說明(IAR)
38.8 總結
38.1 初學者重要提示
- 學習本章節前,務必優先學習第36章,HAL庫的幾個常用API均作了講解和舉例。
- 使用LPTIM的好處是系統處於睡眠、停機狀態依然可以正常工作(除了待機模式)。停機狀態可以正常工作的關鍵是LSE,LSI時鍾不會被關閉,同時也可以選擇使用外部時鍾源。
- LPTIM的任何中斷都可以喚醒停機模式。
- STM32H7從停機模式喚醒后要重新配置系統時鍾,這點跟F1,F4系列一樣。
- 測試發現STM32H7的LPTIM1的中斷可以喚醒停機模式,其它幾個LPTIM2-5無法喚醒。詳情記錄看此貼:http://www.armbbs.cn/forum.php?mod=viewthread&tid=91064
38.2 低功耗定時器超時喚醒驅動設計
低功耗定時器超時喚醒驅動設計中有幾個要注意的事項,下面逐一為大家做個說明。
38.2.1 低功耗定時器時鍾選擇
由前面的第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。
38.2.2 低功耗定時器超時模式配置
下面使用LSE做低功耗定時器的系統時鍾,做了8分頻,並開啟LPTIM1的超時中斷。
1. /* 選擇LPTIM的時鍾源 */ 2. #define LPTIM_CLOCK_SOURCE_LSE /* LSE 時鍾32768Hz */ 3. //#define LPTIM_CLOCK_SOURCE_LSI /* LSI 時鍾32768Hz */ 4. //#define LPTIM_CLOCK_SOURCE_PCLK /* PCLK 時鍾100MHz */ 5. 6. LPTIM_HandleTypeDef LptimHandle = {0}; 7. 8. /* 9. ****************************************************************************************************** 10. * 函 數 名: bsp_InitLPTIM 11. * 功能說明: 初始化LPTIM 12. * 形 參: 無 13. * 返 回 值: 無 14. ****************************************************************************************************** 15. */ 16. void bsp_InitLPTIM(void) 17. { 18. RCC_PeriphCLKInitTypeDef RCC_PeriphCLKInitStruct = {0}; 19. 20. 21. /* ## - 1 - 使能LPTIM時鍾和GPIO時鍾 ####################################### */ 22. __HAL_RCC_LPTIM1_CLK_ENABLE(); 23. 24. /* ## - 2 - 配置LPTIM時鍾,可以選擇LSE,LSI或者PCLK ######################## */ 25. #if defined (LPTIM_CLOCK_SOURCE_LSE) 26. { 27. RCC_OscInitTypeDef RCC_OscInitStruct = {0}; 28. 29. RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSE; 30. RCC_OscInitStruct.LSEState = RCC_LSE_ON; 31. RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE; 32. 33. if (HAL_RCC_OscConfig(&RCC_OscInitStruct)!= HAL_OK) 34. { 35. Error_Handler(__FILE__, __LINE__); 36. } 37. 38. RCC_PeriphCLKInitStruct.PeriphClockSelection = RCC_PERIPHCLK_LPTIM1; 39. RCC_PeriphCLKInitStruct.Lptim1ClockSelection = RCC_LPTIM1CLKSOURCE_LSE; 40. HAL_RCCEx_PeriphCLKConfig(&RCC_PeriphCLKInitStruct); 41. } 42. #elif defined (LPTIM_CLOCK_SOURCE_LSI) 43. { 44. RCC_OscInitTypeDef RCC_OscInitStruct = {0}; 45. 46. RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSI; 47. RCC_OscInitStruct.LSIState = RCC_LSI_ON; 48. RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE; 49. 50. if (HAL_RCC_OscConfig(&RCC_OscInitStruct)!= HAL_OK) 51. { 52. Error_Handler(__FILE__, __LINE__); 53. } 54. 55. RCC_PeriphCLKInitStruct.PeriphClockSelection = RCC_PERIPHCLK_LPTIM1; 56. RCC_PeriphCLKInitStruct.Lptim1ClockSelection = RCC_LPTIM1CLKSOURCE_LSI; 57. HAL_RCCEx_PeriphCLKConfig(&RCC_PeriphCLKInitStruct); 58. } 59. #elif defined (LPTIM_CLOCK_SOURCE_PCLK) 60. RCC_PeriphCLKInitStruct.PeriphClockSelection = RCC_PERIPHCLK_LPTIM1; 61. RCC_PeriphCLKInitStruct.Lptim1ClockSelection = RCC_LPTIM1CLKSOURCE_LSE; 62. HAL_RCCEx_PeriphCLKConfig(&RCC_PeriphCLKInitStruct); 63. #else 64. #error Please select the LPTIM Clock source inside the bsp_lptim_pwm.c file 65. #endif 66. 67. /* ## - 3 - 配置LPTIM ######################################################## */ 68. LptimHandle.Instance = LPTIM1; 69. /* 對應寄存器CKSEL,選擇內部時鍾源 */ 70. LptimHandle.Init.Clock.Source = LPTIM_CLOCKSOURCE_APBCLOCK_LPOSC; 71. /* 設置LPTIM時鍾分頻 */ 72. LptimHandle.Init.Clock.Prescaler = LPTIM_PRESCALER_DIV8; 73. /* LPTIM計數器對內部時鍾源計數 */ 74. LptimHandle.Init.CounterSource = LPTIM_COUNTERSOURCE_INTERNAL; 75. /* 軟件觸發 */ 76. LptimHandle.Init.Trigger.Source = LPTIM_TRIGSOURCE_SOFTWARE; 77. /* 超時模式用不到這個配置 */ 78. LptimHandle.Init.OutputPolarity = LPTIM_OUTPUTPOLARITY_HIGH; 79. /* 比較寄存器和ARR自動重載寄存器選擇更改后立即更新 */ 80. LptimHandle.Init.UpdateMode = LPTIM_UPDATE_IMMEDIATE; 81. /* 外部輸入1,本配置未使用 */ 82. LptimHandle.Init.Input1Source = LPTIM_INPUT1SOURCE_GPIO; 83. /* 外部輸入2,本配置未使用 */ 84. LptimHandle.Init.Input2Source = LPTIM_INPUT2SOURCE_GPIO; 85. 86. if (HAL_LPTIM_Init(&LptimHandle) != HAL_OK) 87. { 88. Error_Handler(__FILE__, __LINE__); 89. } 90. 91. /* ## - 4 - 配置LPTIM ######################################################## */ 92. /* 配置中斷優先級並使能中斷 */ 93. HAL_NVIC_SetPriority(LPTIM1_IRQn, 1, 0); 94. HAL_NVIC_EnableIRQ(LPTIM1_IRQn); 95. }
這里把幾個關鍵的地方闡釋下:
- 第2行,LPTIM1的系統時鍾選項LSE,頻率32768Hz。
- 第68 – 89行,第36章的3.2小節對這些參數成員有詳細描述。
- 第76行,低功耗定時器的超時模式使用軟件觸發或者外部觸發均可以正常工作。
- 第93 -94行,配置LPTIM1的中斷優先級並使能中斷。
38.2.3 低功耗定時器超時模式啟動
啟動低功耗定時器:
1. /* 2. ****************************************************************************************************** 3. * 函 數 名: bsp_StartLPTIM 4. * 功能說明: 啟動LPTIM 5. * 形 參: 無 6. * 返 回 值: 無 7. ****************************************************************************************************** 8. */ 9. void bsp_StartLPTIM(void) 10. { 11. /* 12. ARR是自動重裝寄存器,對應函數HAL_LPTIM_TimeOut_Start_IT的第2個參數 13. Compare是比較寄存器,對應函數HAL_LPTIM_TimeOut_Start_IT的第3個參數 14. 15. --------------------- 16. LSE = 32768Hz 17. 分頻設置為LPTIM_PRESCALER_DIV8,即8分頻(函數bsp_InitLPTIM里面做的初始化配置) 18. ARR自動重載寄存器 = 32768 19. 實際測試發現溢出中斷與ARR寄存器無關,全部由第3個參數,Compare寄存器決定 20. 21. LPTIM的計數器計數1次的時間是 1 / (32768 / 8) = 8 /32768。 22. 第三個參數配置的是32767,那么計數到32767就是 (32767 + 1)*(8 /32768) = 8秒,計算的時候要加1。 23. */ 24. if (HAL_LPTIM_TimeOut_Start_IT(&LptimHandle, 0, 32767) != HAL_OK) 25. { 26. Error_Handler(__FILE__, __LINE__); 27. } 28. }
這里把幾個關鍵的地方闡釋下:
- 程序里面的注釋已經比較詳細,特別注意函數HAL_LPTIM_TimeOut_Start_IT的第2個參數在超時模式沒有任何作用。主要是通過第3個參數配置超時時間。
- 函數HAL_LPTIM_TimeOut_Start_IT開啟的是比較匹配中斷,所以實際的超時時間由Compare寄存器決定。
38.2.4 低功耗定時器中斷處理
低功耗定時器中斷的實現如下:
1. /* 2. ****************************************************************************************************** 3. * 函 數 名: LPTIM1_IRQHandler 4. * 功能說明: LPTIM1中斷服務程序 5. * 形 參: 無 6. * 返 回 值: 無 7. ****************************************************************************************************** 8. */ 9. void LPTIM1_IRQHandler(void) 10. { 11. if((LPTIM1->ISR & LPTIM_FLAG_CMPM) != RESET) 12. { 13. /* 清除比較匹配中斷 */ 14. LPTIM1->ICR = LPTIM_FLAG_CMPM; 15. 16. /* 關閉溢出中斷 */ 17. HAL_LPTIM_TimeOut_Stop_IT(&LptimHandle); 18. 19. bsp_LedToggle(4); 20. } 21. }
這里把幾個關鍵的地方闡釋下:
- 程序中沒有使用HAL整理的中斷處理函數HAL_LPTIM_IRQHandler,而是直接使用寄存器判斷的方式,效果高些。
- 第14行,函數HAL_LPTIM_TimeOut_Start_IT開啟的是比較匹配中斷,所以這里要清除對應的標識。
- 第17行,關閉超時中斷,下次使用時再開啟。
38.2.5 低功耗定時器超時喚醒注意事項
這里再強調下低功耗定時器喚醒的三個注意事項。
- LPTIM的任何中斷都可以喚醒停機模式。
- STM32H7從停機模式喚醒后要重新配置系統時鍾,這點跟F1,F4系列一樣。
- 測試發現STM32H7的LPTIM1的中斷可以喚醒停機模式,其它幾個LPTIM2-5無法喚醒。詳情記錄看此貼:http://www.armbbs.cn/forum.php?mod=viewthread&tid=91064
38.3 低功耗定時器板級支持包(bsp_lptim_pwm.c)
低功耗定時器驅動文件bsp_lptim_pwm.c供用戶調用的兩個函數:
- bsp_InitLPTIM
- bsp_StartLPTIM
下面將這兩個函數的使用為大家做個說明。
38.3.1 函數bsp_InitLPTIM
函數原型:
void bsp_InitLPTIM(void)
函數描述:
此函數使用LSE做低功耗定時器的系統時鍾,做了8分頻,並開啟LPTIM1的超時中斷。
注意事項:
- 關於此函數的相關注意事項在本章的38.2.2小節有詳細說明。
使用舉例:
初始化函數在bsp.c文件的bsp_Init函數里面調用。
38.3.2 函數bsp_StartLPTIM
函數原型:
void bsp_StartLPTIM(void)
函數描述:
此函數通過調用函數HAL_LPTIM_TimeOut_Start_IT來啟動低功耗定時器的超時模式,並開啟了相應中斷。
注意事項:
- 關於此函數的相關注意事項和解讀在本章的38.2.3小節有詳細說明。
使用舉例:
調用此函數前優先調用初始化函數bsp_InitLPTIM即可。
38.4 低功耗定時器驅動移植和使用
低功耗定時器的移植比較簡單:
- 第1步:復制bsp_lptim_pwm.c和bsp_lptim_pwm.h到自己的工程目錄,並添加到工程里面。
- 第2步:這幾個驅動文件主要用到HAL庫的GPIO和LPTIM驅動文件,簡單省事些可以添加所有HAL庫.C源文件進來。
- 第3步,應用方法看本章節配套例子即可。
38.5 實驗例程設計框架
通過程序設計框架,讓大家先對配套例程有一個全面的認識,然后再理解細節,本次實驗例程的設計框架如下:
第1階段,上電啟動階段:
- 這部分在第14章進行了詳細說明。
第2階段,進入main函數:
- 第1步,硬件初始化,主要是MPU,Cache,HAL庫,系統時鍾,滴答定時器,LED和串口
- 第2步,借助按鍵消息實現低功耗定時器的效果測試。
38.6 實驗例程說明(MDK)
配套例子:
V7-022_低功耗定時器超時模式的停機喚醒
實驗目的:
- 習低功耗定時器超時模式的停機喚醒。
實驗內容:
- 使用LPTIM的好處是系統處於睡眠,停機狀態依然可以正常工作(除了待機模式)。停機狀態可以正常工作的關鍵是LSE,LSI時鍾不會被關閉,同時也可以選擇使用外部時鍾源。
- 例子默認用的LSE時鍾供LPTIM1使用,大家可以通過bsp_lptim_pwm.c文件開頭宏定義切換到LSI或者PLCK。
- 本例子利用LPTIM的超時功能,配置8秒后喚醒停機模式。
- 板子正常運行時LED2閃爍,進入停機模式后,LED2停止閃爍。
- 進入LPTIM的超時中斷后,LED4翻轉,方便查看是否正常執行。
實驗操作:
- K1鍵按下,進入停機模式, 8秒后自動喚醒。
上電后串口打印的信息:
波特率 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鍵按下,進入停機模式, 8秒后自動喚醒。
/* ********************************************************************************************************* * 函 數 名: main * 功能說明: c程序入口 * 形 參: 無 * 返 回 值: 錯誤代碼(無需處理) ********************************************************************************************************* */ int main(void) { uint8_t ucKeyCode; /* 按鍵代碼 */ bsp_Init(); /* 硬件初始化 */ 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鍵按下,進入停機模式, 8秒后自動喚醒 */ printf("--進入停機模式,8秒后喚醒\r\n"); bsp_StartLPTIM(); HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI); SystemClock_Config(); /* 特別注意,退出后要重新配置系統時鍾 */ printf("--退出停機模式\r\n"); break; default: /* 其它的鍵值不處理 */ break; } } } }
38.7 實驗例程說明(IAR)
配套例子:
V7-022_低功耗定時器超時模式的停機喚醒
實驗目的:
- 習低功耗定時器超時模式的停機喚醒。
實驗內容:
- 使用LPTIM的好處是系統處於睡眠,停機狀態依然可以正常工作(除了待機模式)。停機狀態可以正常工作的關鍵是LSE,LSI時鍾不會被關閉,同時也可以選擇使用外部時鍾源。
- 例子默認用的LSE時鍾供LPTIM1使用,大家可以通過bsp_lptim_pwm.c文件開頭宏定義切換到LSI或者PLCK。
- 本例子利用LPTIM的超時功能,配置8秒后喚醒停機模式。
- 板子正常運行時LED2閃爍,進入停機模式后,LED2停止閃爍。
- 進入LPTIM的超時中斷后,LED4翻轉,方便查看是否正常執行。
實驗操作:
- K1鍵按下,進入停機模式, 8秒后自動喚醒。
上電后串口打印的信息:
波特率 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(); }
主功能:
主功能的實現主要分為兩部分:
- 獲取其它RS485設備發來的命令,並執行相應功能。
通過函數comGetChar(COM3, &ucDataTravel)以查詢、非阻塞方式獲取其它開發板發來的數據。
- 根據不同的按鍵消息,向其它485設備發送命令
通過函數comSendChar(COM3, ucDataTravel)以非阻塞方式向其它485設備發送命令,以此來執行同樣的功能。
/* ********************************************************************************************************* * 函 數 名: main * 功能說明: c程序入口 * 形 參: 無 * 返 回 值: 錯誤代碼(無需處理) ********************************************************************************************************* */ int main(void) { uint8_t ucKeyCode; /* 按鍵代碼 */ bsp_Init(); /* 硬件初始化 */ 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鍵按下,進入停機模式, 8秒后自動喚醒 */ printf("--進入停機模式,8秒后喚醒\r\n"); bsp_StartLPTIM(); HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI); SystemClock_Config(); /* 特別注意,退出后要重新配置系統時鍾 */ printf("--退出停機模式\r\n"); break; default: /* 其它的鍵值不處理 */ break; } } } }
38.8 總結
本章節就為大家講解這么多,低功耗定時器在低功耗場合比較有用,如果有低功耗方面的項目需求,可以考慮使用這個定時器實現低功耗喚醒。