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分你觉得自己配么???

个人观点,非喜勿喷(不接收反驳,哈哈哈)

你可能感兴趣的:(低功耗,stm32,rtc)