由于现在芯片价格以及供货问题,使得芯片的选择受到了很大的影响,有一个项目用到了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分你觉得自己配么???
个人观点,非喜勿喷(不接收反驳,哈哈哈)