天天看點

STM32F407進入低功耗模式以及喚醒(RTC+中斷)概述開發過程結論

概述

由于現在晶片價格以及供貨問題,使得晶片的選擇受到了很大的影響,有一個項目用到了STM32F407這個MCU(雖然價格不便宜,但是出貨量大,購買比較容易寫),功能要求需要做低功耗,想到之前做過STM32F103的低功耗(發表過一個部落格:https://blog.csdn.net/zsj2016o/article/details/85141738),想來F407也不會有太多問題,結果實際操作的時候發現,兩者差別還不小(主要是F4與F1相同功能的函數表達不一樣,還有就是F4比F1功能強大,一些配置發生了改變),是以隻能找到正點原子的例程,參照F1的模式從新寫。

開發過程

我是首先使用的實驗17 待機喚醒實驗(利用的是STM32F4探索者),它使用的是待機模式,待機模式喚醒條件相對比較單一,項目需要多個中斷喚醒以及定時喚醒,是以用到了STOP模式,進入STOP模式的程式為:PWR_EnterSTOPMode(PWR_LowPowerRegulator_ON,PWR_STOPEntry_WFI);在進入休眠前,需要配置外部中斷,采用的是PA4,休眠後采用PA4電平變化喚醒MAU,完成第一階段外部中斷喚醒STOP模式休眠。

第二部是RTC喚醒休眠,采用的是正點原子曆程中的實驗15 RTC實時實驗實驗,我的方式為修改程式中RTC喚醒間隔,即修改這個函數RTC_Set_WakeUp(RTC_WakeUpClock_CK_SPRE_16bits,0); //配置WAKE UP中斷,1秒鐘中斷一次,其中0改為其他值比如10,然後測試是否是10喚醒一次,在這裡測試的時候,我犯了一個錯誤,或者是誤區,RTC是實時的,我用斷電進行測試,測試連續兩次到達斷電的時間間隔,結果發現一個奇怪的現象,那就是有時候時間是正确的,有時間時間不正确,調的我很煩,在網上查了很多資料,結果沒發現什麼有用資訊,然後我就放棄了定時周期喚醒,嘗試了鬧鐘喚醒,鬧鐘喚醒顧名思義就是定一個時間,然後時間到了就進入中斷,喚醒MCU,我要的效果是一定時間後喚醒,比如5S,我的方式為先擷取目前的時間然後在這兒時間上+5,設定鬧鐘即可,由于我還是用的之前的測試方法,采用打斷的方式用手機記錄時間,結果跟周期喚醒效果一樣,有時可以成功有時不行;

發現這個困擾以後,我就在CSDN上找相應的資料,就是STM32F407RTC喚醒STOP模式,我找着找着就很生氣,網上找資料,是為了快速定位問題,最不濟是問題讨論,哪怕是沒有結果,我在搜尋的時候,絕絕絕絕大部分的部落格以及下載下傳都是針對于正點原子例程的說明,或者是說明都算不上,說明至少還有一個了解吸收,他們僅僅是搬運,感覺就是在騙積分,着實氣憤。

後來不知道咋的了,想到的RTC時鐘是不會随着斷點的出現而停止的,是以之前的測試方式是有問題的,故放棄斷點加手機計時的方式,采用了喚醒進行序列槽列印,然後利用序列槽接收工具的時間戳來判斷,最後完成STOP以及中斷+RTC喚醒的功能。

結論

1、使用該函數RTC_Set_WakeUp(RTC_WakeUpClock_CK_SPRE_16bits,timertimer); 設定定時喚醒時間,寫入時間時有可能出現寫入不成功

解決辦法:在該函數前後添加函數RTC_WaitForSynchro(); RTC_WaitForSynchro();

RTC_Set_WakeUp(RTC_WakeUpClock_CK_SPRE_16bits,timertimer); //配置WAKE

UP中斷,7秒鐘中斷一次 RTC_WaitForSynchro();

2、使用RTC_Set_WakeUp函數成功設定時間,但是喚醒不準确

說明:如果測試采用加斷點采用手機計時的方式,在程式運作斷點後,程式停止(完成鬧鐘設定,5秒),

在你操作過程中,RTC一直在運作,是以可能你手機計時的時間會小于設定值,最好采用列印的方式計時。

3、正點原子RTC例程中,庫版本裡面有一個BUG,如下:

retry有初始值,如果retry++, if(retry==0)return 1;永遠不能return 1,應修改為retry–;

u8 My_RTC_Init(void) 		
 { 			
 			RTC_InitTypeDef RTC_InitStructure; 			u16 retry=0X1FFF; 
   		    RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);//使能PWR時鐘 			PWR_BackupAccessCmd(ENABLE);	//使能後備寄存器通路 
			if(RTC_ReadBackupRegister(RTC_BKP_DR0)!=0x5050)		//是否第一次配置? 			
 			{
   				RCC_LSEConfig(RCC_LSE_ON);//LSE 開啟    
   				while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET)	//檢查指定的RCC标志位設定與否,等待低速晶振就緒
   				{
   					retry--;      //注:原來這裡是++,程式BUG,已修改
   					delay_ms(10);
   				}
   				if(retry==0)return 1;		//LSE 開啟失敗. 
   					
   				RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);		//設定RTC時鐘(RTCCLK),選擇LSE作為RTC時鐘
   
   				RCC_RTCCLKCmd(ENABLE);	//使能RTC時鐘 
   		
   		    RTC_InitStructure.RTC_AsynchPrediv = 0x7F;//RTC異步分頻系數(1~0X7F)
   		    RTC_InitStructure.RTC_SynchPrediv  = 0xFF;//RTC同步分頻系數(0~7FFF)
   		    RTC_InitStructure.RTC_HourFormat   = RTC_HourFormat_24;//RTC設定為,24小時格式
   		    RTC_Init(&RTC_InitStructure);
   		 
   				RTC_Set_Time(23,59,56,RTC_H12_AM);	//設定時間
   				RTC_Set_Date(14,5,5,1);		//設定日期
   			 
   				RTC_WriteBackupRegister(RTC_BKP_DR0,0x5050);	//标記已經初始化過了 			
			} 
   		  			return 0; 		
	}

           

4、RTC鬧鐘喚醒模式設定

我的測試代碼隻用到了秒中斷,因為我将日期周末、小時、分都屏蔽了,隻比較秒,方式為設定RTC_AlarmMask,如下:

RTC_AlarmTypeInitStructure.RTC_AlarmMask = RTC_AlarmMask_DateWeekDay|RTC_AlarmMask_Hours|RTC_AlarmMask_Minutes;//屏蔽 日期還有星期

void RTC_Set_AlarmA(u8 week,u8 hour,u8 min,u8 sec)
		{ 
			EXTI_InitTypeDef   EXTI_InitStructure;
			RTC_AlarmTypeDef RTC_AlarmTypeInitStructure;
			RTC_TimeTypeDef RTC_TimeTypeInitStructure;
			
			RTC_AlarmCmd(RTC_Alarm_A,DISABLE);//關閉鬧鐘A 
			
		    RTC_TimeTypeInitStructure.RTC_Hours   = hour;//小時
			RTC_TimeTypeInitStructure.RTC_Minutes = min;//分鐘
			RTC_TimeTypeInitStructure.RTC_Seconds = sec;//秒
			RTC_TimeTypeInitStructure.RTC_H12			= 0;
		  
			RTC_AlarmTypeInitStructure.RTC_AlarmDateWeekDay    = 0;//星期
			RTC_AlarmTypeInitStructure.RTC_AlarmDateWeekDaySel = RTC_AlarmDateWeekDaySel_WeekDay;//按星期鬧
			RTC_AlarmTypeInitStructure.RTC_AlarmMask           = 			 		RTC_AlarmMask_DateWeekDay|RTC_AlarmMask_Hours|RTC_AlarmMask_Minutes;//屏蔽   日期還有星期
			RTC_AlarmTypeInitStructure.RTC_AlarmTime           = RTC_TimeTypeInitStructure;
		    RTC_SetAlarm(RTC_Format_BIN,RTC_Alarm_A,&RTC_AlarmTypeInitStructure);
		 
			
			RTC_ClearITPendingBit(RTC_IT_ALRA);//清除RTC鬧鐘A的标志
		    EXTI_ClearITPendingBit(EXTI_Line17);//清除LINE17上的中斷标志位 
			
			RTC_ITConfig(RTC_IT_ALRA,ENABLE);//開啟鬧鐘A中斷
			RTC_AlarmCmd(RTC_Alarm_A,ENABLE);//開啟鬧鐘A 
			
			EXTI_InitStructure.EXTI_Line = EXTI_Line17;//LINE17
		    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;//中斷事件
		    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; //上升沿觸發 
		    EXTI_InitStructure.EXTI_LineCmd = ENABLE;//使能LINE17
		    EXTI_Init(&EXTI_InitStructure);//配置
		
			NVIC_InitStructure.NVIC_IRQChannel = RTC_Alarm_IRQn; 
		    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;//搶占優先級1
		    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;//子優先級2
		    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能外部中斷通道
		    NVIC_Init(&NVIC_InitStructure);//配置
		}
           

5、擷取RTC時間指令

RTC_GetTime(RTC_Format_BIN,&TestTimer);//擷取目前時間,用于計算寫入寄存器的值

定義一個結構體,然後采用上述指令,指令定義在stm32f4xx_rtc.c這個檔案中。

附一個例程:https://download.csdn.net/download/zsj2016o/20347018(經常線上,有問題可以私信我)

最後發表一下自己的見解,大家來到一個喜歡的論壇或者其他集合,無論是查閱資料、查閱問題,都是以一種交流學習的态度來的,是以,如果要寫就好好寫,要發就發一些有意義的東西,你直接搬運手冊上的東西,長篇大論,或者直接拿一個正點原子的例程,也不加上自己的了解,或者總結,直接就發出來,這不就是在騙積分麼,為了解決問題,花點積分也無可厚非,畢竟誰的工作也不是白來的,但是花那麼多積分都是一些正點原子的例程或者是官網的例程,你們不覺得慚愧麼,一個50分,直接搬運你就直接定一個5分意思一下就行了,50分你覺得自己配麼???

個人觀點,非喜勿噴(不接收反駁,哈哈哈)

繼續閱讀