STM32 RTC闹钟 定时唤醒 STOP模式 完整工程 库函数 Ineternal command error

记录,分享,进步,快乐。

#项目内容介绍

前段时间在做一个低功耗的室内定位系统,遇到了几个尴尬的问题,
看了官方给的文档,也在网上看了前辈们对自己实际项目运用。根据功耗以及实用性,选择了Stop模式(20uA),用RTC的闹钟定时唤醒。现在将自己这两天学的东西整理一下,供大家讨论,谢谢。如果对我的分享感兴趣,也可以加我建的讨论群,556456460。

#为什么要用Stop模式

  • 停机模式是次低功耗的,其典型的电流消耗在 20uA 左右。
  • 进入Stop模式,所有的IO口仍然维持进入低功耗之前的状态。
  • 停机模式可以通过任意管脚的外部中断唤醒(可选性大),RTC闹钟中断(EXTI17)也是一种外部中断哦。
  • STOP模式唤醒后从stop进入的下一条语句执行,若是待机模式,则因为掉电,RAM等其他寄存器丢失,唤醒后处于复位状态,因此从程序最开始执行!

#程序的主要思路

由于我之前没有接触过rtc和stop模式,我把定时唤醒分为了两部分来实现,从而进一步降低本次写程序的难度。这里感谢原子哥,我的程序修改于原子的哥的rtc demo。 配合stm32的中文参考手册,以及万能的前辈们,让我大概了解了啥是rtc,经过修改,我的闹钟中断就可以执行(这里面遇到一个大坑,程序里面再说)。Stop模式等下附一个前辈的链接,我就不细说了。感谢梦中_破前辈的分享,谢谢。https://blog.csdn.net/lmx11040101/article/details/85785336

STM32 RTC闹钟 定时唤醒 STOP模式 完整工程 库函数 Ineternal command error_第1张图片
#代码部分

好了,废话不多说了,直接上代码。

// main.c
#include "delay.h"
#include "sys.h"
#include "usart.h"
#include "rtc.h"

 int main(void)
 { 
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);// 设置中断优先级分组2
	delay_init();	    	 //延时函数初始化	  
	uart_init(9600);	 	//串口初始化为9600			 	
	printf("start\r\n");
	
	while(RTC_Init())		//RTC初始化	,一定要初始化成功
	{
		printf("rtc error\r\n");
		delay_ms(800);
	}		    						
	
	printf("start2\r\n");	
	
	while(1)
	{		
		SysTickEnableOrDisable(DISABLE);      // 每1ms产生中断,可能导致Stop模式进入被忽略,从而进不去stop模式。
		
		RTC_ClearITPendingBit(RTC_IT_OW | RTC_IT_ALR);		//清闹钟中断
		PWR_EnterSTOPMode(PWR_Regulator_LowPower, PWR_STOPEntry_WFI);     // 进入stop模式
		
		/**************被唤醒后*******************/
		RCC_HSEConfig(RCC_HSE_ON);	//由于唤醒后,系统时钟源变成了HSI,导致了系统时间紊乱,其他外设不能正常工作,所以要配置HSE.==
		uart_init(9600);	 	    //串口初始化为9600 由于上面的原因,要用到的外设都需要重新配置一下。
		SysTickEnableOrDisable(ENABLE);   // 要用到delay_ms函数
		
		printf("123\r\n");		  // 唤醒后的工作就是打印一下	  
		delay_ms(1);
	} 											    
}	
//rtc.c
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "rtc.h" 		    

//实时时钟配置
//初始化RTC时钟,同时检测时钟是否工作正常
//返回0:正常
//其他:错误代码
u8 RTC_Init(void)
{
	//检查是不是第一次配置时钟
	u8 temp=0;
	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);	//使能PWR和BKP外设时钟   
	PWR_BackupAccessCmd(ENABLE);	//使能后备寄存器访问  
			
	BKP_DeInit();	//复位备份区域 	
	RCC_LSEConfig(RCC_LSE_ON);	//设置外部低速晶振(LSE),使用外设低速晶振
	while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET)	//检查指定的RCC标志位设置与否,等待低速晶振就绪
	{
		temp++;
		delay_ms(10);
	}
	if(temp>=250)return 1;//初始化时钟失败,晶振有问题	   
		
	RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);		//设置RTC时钟(RTCCLK),选择LSE作为RTC时钟    
	RCC_RTCCLKCmd(ENABLE);	//使能RTC时钟  
	RTC_WaitForSynchro();		//等待RTC寄存器同步 
	RTC_WaitForLastTask();	//等待最近一次对RTC寄存器的写操作完成	
	
	RTC_ITConfig(RTC_IT_ALR, ENABLE);		//使能RTC时钟中断
 	
	RTC_WaitForLastTask();	//等待最近一次对RTC寄存器的写操作完成
	RTC_EnterConfigMode();/// 允许配置	
	
	RTC_SetPrescaler(32767); //设置RTC预分频的值
	RTC_SetCounter(0);       // 这里的设置,每设置一次就要等待写操作完成 这是个大坑啊,坑了我将近一天的时间
	RTC_WaitForLastTask();	 //等待最近一次对RTC寄存器的写操作完成	
	RTC_SetAlarm(2);         // 设置闹钟的时间要加1哦,也就是说现在是3S
	RTC_WaitForLastTask();	//等待最近一次对RTC寄存器的写操作完成	
	RTC_ExitConfigMode();   //退出配置模式  
	RTC_NVIC_Config();      //RCT中断分组设置		    				     
	
	return 0; //ok
}	

void RTC_NVIC_Config(void)
{	
    NVIC_InitTypeDef NVIC_InitStructure;
	EXTI_InitTypeDef EXTI_InitStructure;
	
	//EXTI17配置
	EXTI_InitStructure.EXTI_Line = EXTI_Line17;         // RTC闹钟为外部中断17
	EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
	EXTI_InitStructure.EXTI_LineCmd = ENABLE;
	EXTI_Init(&EXTI_InitStructure);
	
	// 时钟中断配置
	NVIC_InitStructure.NVIC_IRQChannel = RTCAlarm_IRQn;		//RTC全局中断
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;	//先占优先级1位,从优先级3位
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;	//先占优先级0位,从优先级4位
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;		//使能该通道中断
	NVIC_Init(&NVIC_InitStructure);		//根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
}

/*
	函数:滴答定时器开关
	输入参数:status 滴答定时器工作状态 EABLE打开  DISABLE关闭
	输出参数:无
*/
void SysTickEnableOrDisable(u8 status)
{
	SysTick->CTRL = status;    // 关闭滴答定时器
	SysTick->VAL = 0x00;     // 清空val,清空定时器
}

/*
	函数:RTC闹钟中断函数,当RTC计数器的值和闹钟设定的一样时,触发这个函数
*/
void RTCAlarm_IRQHandler(void)
{
	printf("22\r\n");
	if(RTC_GetITStatus(RTC_IT_ALR)!= RESET)//闹钟中断
	{
		RTC_SetCounter(0);	    // 清除RTC计数器,从新开始计数
		RTC_WaitForLastTask();
		RTC_SetAlarm(2);	    // 设置闹钟 设置闹钟的时间要加1哦,,也就是说现在是3S
		RTC_WaitForLastTask();	  
	}
	
	RTC_ClearITPendingBit(RTC_IT_ALR);		//清闹钟中断
	EXTI_ClearITPendingBit(EXTI_Line17);     //闹钟事件发生,会产生一个EXTI_17外部中断,此标志位要清除,否则下次停止模式进入失败
	RTC_ClearITPendingBit(RTC_IT_OW);		//清闹钟中断
	RTC_WaitForLastTask();	  	    		
}
#ifndef __RTC_H
#define __RTC_H	    

#include "sys.h"

u8 RTC_Init(void);        //初始化RTC,返回0,失败;1,成功;

void SysTickEnableOrDisable(u8 status);
void RTC_NVIC_Config(void);

#endif

程序的结果,22是进入闹钟中断函数后打印的,123是从stop模式唤醒后打印的。两次123打印时间间隔是3s.符合我的程序预期。
STM32 RTC闹钟 定时唤醒 STOP模式 完整工程 库函数 Ineternal command error_第2张图片

#现在说一下我遇到的问题

##配了闹钟中断,外部中断线也配了,闹钟的值也设置了,rtc计数器也清零了,但是进不去闹钟中断函数。

  • RTC_SetCounter(0); // 这里的设置,每设置一次就要等待写操作完成 这是个大坑啊,坑了我将近一天的时间
    RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
    RTC_SetAlarm(2);
    RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成

##闹钟中断函数和全局中断RTC_IRQHandler() 的区别

  • 这里我就不说了,放一个前辈(langwanglx)在openedv上链接,http://www.openedv.com/posts/list/22055.htm,再次感谢这位前辈的分享。

##低功耗-stm32引脚配置

这里我就不说了,放一个前辈(langwanglx)在电子发烧友上链接,http://bbs.elecfans.com/jishu_1635024_1_1.html,再次感谢这位前辈的分享。

##最后说一下下载程序过程会遇到的问题

由于程序中采用的是stop模式,stm32会进入到低功耗模式,所以32运行之后再下载程序,有很大的可能是下载不进去的(stm32大部分时间是睡眠),而我用的是ST-LINK下载器下载程序,会报Ineternal command error.此时断电,上电就点下载按钮就可以(点快一点哦,否则又会进入到stop模式)。

STM32 RTC闹钟 定时唤醒 STOP模式 完整工程 库函数 Ineternal command error_第3张图片
点赞,给我点个赞哦!
以上为个人实验总结,不对之处还希望指正
原创文章,转载请注明出处:https://editor.csdn.net/md?articleId=104466243

你可能感兴趣的:(低功耗,单片机,嵌入式)