STM32進入STOP模式並喚醒實驗總結
專案需求,需要實現裝置低功耗功能,實現過程中遇到幾個問題,以此記錄總結。(stm32f103ret6)
問題一:執行PWR_EnterSTOPMode(PWR_Regulator_LowPower,PWR_STOPEntry_WFI);後,程式繼續執行,看到的現象像是沒有進入休眠。
除錯步驟:
1.關閉自己的時鐘配置,呼叫啟動函式中預設的SystemInit()
2.關閉相關外設初始化,只開啟串列埠一以追蹤程式執行流程和一個IO中斷以喚醒
現象:能進入終端並且正常喚醒
3.換為呼叫自己的時鐘,休眠不了,一步步開啟相關時鐘配置沒問題直到開啟了
SysTick_Config(SystemCoreClock / 1000 ) //1ms定時器
就休眠失敗
原始碼貼圖如下:
原因:開始以為把所有的外設中斷關閉就沒事了,其實系統時鐘開了個1Ms的系統定時中斷(功能需要)
【解決方式】:
方式一:.註釋SysTick_Config(SystemCoreClock / 1000)
方式二:.在進入休眠之前,關閉系統定時器,清空計數值
SysTick->CTRL = 0x00;//關閉定時器
SysTick->VAL = 0x00;//清空val,清空定時器
功能需要不能關閉,選擇方式二
【補充說明】:
SysTick系統定時器是屬於CM3核心中的一個外設,內嵌在NUIV中。它是一個24bit的向下遞減計數器,每計數一次的時間為1/SYSCLK。當重灌載數值暫存器地見到0的時候,系統定時器就產生一次中斷,一次迴圈反覆。我的累計值是SystemCoreClock / 1000,所以中斷就是1ms一次。
相比定時器中斷,產生之後需要清中斷標誌位,但是系統時鐘中斷是沒有的,開始懷疑是不是中斷產生之後標誌位是不會復位的,導致停止模式進入被忽略,程式繼續執行。後面找資料發現每次systick溢位後會置位計數標誌位和中斷標誌位,計數標誌位在計數器重灌載後被清除,而中斷標誌位也會隨著中斷服務程式的響應被清除,所以這兩個標誌位是不需要手動清除的。
具體為什麼開啟系統定時器之後不能進入停止模式還需要查詢原因,在此也請各位網友賜教。
問題二:成功進入休眠並退出後,串列埠列印失敗
原因:
退出停止模式後,其他時鐘還是保持原來的配置(原來我的配置是以外部高速時鐘HSE不分頻作為PLL時鐘源,再經過9倍頻得到,即72MHz,然後PLL作為系統時鐘源,系統時鐘一分頻得到AHB匯流排時鐘,AHB再 一分頻作為高速匯流排時鐘PCLK2,也就是72MHz,USART1用的就是高速匯流排時鐘),這些配置是不會變的,但是退出停止模式後,內部高速時鐘(HSI)預設變成了系統時鐘源,造成時鐘紊亂。
【其實不僅僅是串列埠,其他在進入停止模式之前的外設在退出停止模式之後都會工作不正常,重新配置系統時鐘即可】
問題三:進入停止模式後整個電流還是很大
1.根據裝置原理圖檢視IO外部引腳連線電路,閒置狀態為低電平時,設定為下拉輸入;閒置狀態為高電平時,設定為上拉輸入;閒置狀態為懸空時設定為模擬輸入;輸出引腳根據功能需要設定就行
原因:當IO通過外圍電路電阻接地被拉低時,如果設定為上拉輸入,則在晶片內部的上拉電阻和外圍的下拉電阻構成迴路,電流損耗取決於這兩個電阻;當IO通過外圍電路電阻接電源被拉高時,則在晶片內部的下拉電阻和外圍的上拉電阻構成迴路,電流損耗也取決於這兩個電阻;當懸空時,斯密特觸發器是開啟的,要判斷輸入的是高電平還是低電平,需要一點電流損耗,但是裝置模擬輸入,這個觸發器是關閉的
2.排查外圍電路,某些驅動、電源轉換IC如果是由軟體使能,如果進入停止模式之後不需要則關閉,因為本身就有工作電流;如果是由硬體使能,則需要檢視晶片手冊看靜態工作電流是多少,如果比較高,只能修改硬體電路或者用低耗ic代替
3.外圍是否有一些閉合迴路,比如電壓採集,雖然ADC已經關閉,但是這個分壓電路還是有損耗的,大小一般取決於電阻和電源電壓
4.另外如果線上除錯時,進入停止模式後,除錯失去作用,看看是不是SWDIO、SWCLK兩個引腳也直接設定成了模擬輸入,還關閉了除錯功能:GPIO_PinRemapConfig(GPIO_Remap_SWJ_Disable, ENABLE);
RTC喚醒停止模式
外部中斷功能已經實現,專案需求中另外還需要定時喚醒,現在選用RTC喚醒
EXTI有20箇中斷/事件線,每個GPIO都能被設定為輸入線,佔用EXTI0-EXTI15,有4根用於特定的外設事件觸發
所以RTC的鬧鐘事件就能觸發EXTI17中斷(這就是為什麼進入停止模式設定的是中斷喚醒,但還能用RTC鬧鐘事件喚醒的原因,本質還是被外部中斷喚醒)
全功能程式碼不需要改,在進入停止模式之前配置EXTI17中斷,配置鬧鐘事件
/**************************************1.配置EXTI17中斷*********************************/
EXTI_ClearITPendingBit(EXTI_Line17); //注意先清掉標誌位,要不然可能會出現停止模式被忽略進 //入不了的問題,上面問題一截圖有說明
EXTI_InitStructure.EXTI_Line = EXTI_Line17;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
/**************************************2.配置中斷控制器鬧鐘事件優先順序*********************************/
NVIC_InitStructure.NVIC_IRQChannel = RTCAlarm_IRQn; //RTC在最開始初始化的時候是秒中斷,這裡新增鬧鐘事件
//中斷,此時RTC秒鐘秒中斷和鬧鐘事件中斷同時開啟
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
/**************************************3.設定鬧鐘時間,sec即想要喚醒的時間間隔*********************************/
RTC_SetAlarm(RTC_GetCounter() + sec); //用RTC_SetAlarm()函式配置鬧鐘時間,當計數器的值與鬧鐘暫存器的
//值相等時,可產生鬧鐘事件或中斷
RTC_WaitForLastTask(); //等待確保已成功修改RTC暫存器
RTC_ITConfig(RTC_IT_ALR, ENABLE); //使能鬧鐘事件
RTC_WaitForLastTask();
以上三步在將要進入停止模式之前執行。
/**************************************RTC中斷函式*********************************/
void RTC_IRQHandler(void)
{
if (RTC_GetITStatus(RTC_IT_SEC) != RESET) //秒鐘中斷
{
RTC_GetTime(&SystemTime); //更新時間(專案需要)
RTC_ClearITPendingBit(RTC_IT_SEC);
RTC_WaitForLastTask();
}
if(RTC_GetITStatus(RTC_IT_ALR) != RESET) //鬧鐘中斷
{
RTC_ClearITPendingBit(RTC_IT_ALR); //清中斷
RTC_WaitForLastTask();
}
}
void RTCAlarm_IRQHandler(void)
{
EXTI_ClearITPendingBit(EXTI_Line17); //鬧鐘事件發生,會產生一個EXTI_17外部中斷,此標誌位要清除,否 //則下次停止模式進入失敗,原因見問題一貼圖
}
【補充】:1.每次進入停止模式之前,都重新執行最開始的三步配置,否則可能出現前幾次鬧鐘事件會觸發,後面觸發失敗的問 題。
2.停止模式喚醒之後,需要重新初始化系統時鐘,這時候可以把鬧鐘中斷關閉(因為進入之前會重新開啟,避免沒有 進入 停止模式時一直有鬧鐘事件產生)
以上為個人實驗總結,不對之處還希望指正
原創文章,轉載請註明出處:https://mp.csdn.net/postedit/85785336