2.44 修改主频&低功耗模式

修改主频

修改主频就是要研究一下system_stm32f10x.c和system_stm32f10x.h这两个文件。

这两个文件实用配置系统时钟的,也就是配置RCC时钟树。

RCC时钟树

这节我们主要了解,外部的8MHz晶振,他如何进行选择,如何倍频,才能得到72MHz的SYSCLK,系统主频。系统主频如何去分配,才能得到AHB、APB1和APB2的时钟频率。

在我们之前的课程里,一直保持了默认的配置,就是晶振接的是8MHz HSE,主频是72MHz,AHB和APB2是72MHz,APB1是36MHz。但其实,这些都不是绝对的,可以根据自己的需求进行更改。(一般建议不要改,绝大多数程序都是按照默认的配置来写的)

system这两个文件,提供了两个外部可调用的函数和一个外部可调用的变量:

两个外部可调用的函数是:SystemInit()和SystemCoreClockUpdata().

一个外部可调用的变量是:SystemCoreClock.

SystemInit()这个函数是用来配置时钟树的,也是这个文件最重要的部分,使用HSE配置主频为72MHz,就是这个函数干的活,并且这个函数,在复位后,执行main函数之前,在启动文件里自动调用了。

SystemCoreClock表示主频频率的值,我们想知道目前的主频是多少,直接显示一下这个变量就行了。

SystemCoreClockUpdata(),这个是用来更新SystemCoreClock这个变量的,因为这个变量只有最开始的一次赋值,之后如果我们改变的主频频率,这个值不会跟着变换。

在 system_stm32f10x.c 文件中 , 更改这里的define,会修改不同的主频。

睡眠模式/串口发送接收

对于靠中断触发,没有中断的时候就没什么事的代码,我们就可以给它加入低功耗模式,没事的时候就低功耗,中断来了,再醒来干活就行了。

睡眠模式是CPU时钟关闭,程序不再执行,但是外设的时钟不会关,USART硬件电路还是可以接收数据的,USART收到数据后,产生中断,唤醒CPU。

睡眠程序逻辑:

程序正常运行,走到__WFI()函数这里,程序进入睡眠状态,如果串口上有信息,那么就会终止睡眠模式正常运行,先进入中断程序执行中断函数中的代码,然后再进入主程序,显示接收到的数据,然后就会进入到新的睡眠模式。

int main(void){
	OLED_Init();
	Serial_Init();
	OLED_ShowString(1,1,"RxData:");
	
	while(1)
	{
		if(Serial_GetRxFlag() == 1)
		{	
			RxData = Serial_GetRxData();
			Serial_SendByte(RxData);
			OLED_ShowHexNum(1,8,RxData,2);
		}
		
		OLED_ShowString(2,1,"Running");
		Delay_ms(100);
		OLED_ShowString(2,1,"        ");
		Delay_ms(100);
		
		__WFI();//中断唤醒
	}
}

停止模式/对射式红外传感器计次

停止模式只能通过外部中断来触发,所以和停止模式相关的代码,肯定就要用外部中断。

对于“对射式红外传感器计次”,只有当有东西遮挡红外线时,模块会给STM32发送一个外部中断信号,所以这个程序是可以使用停止模式来省电的。

在停止模式下,1.8V区域的时钟关闭,CPU和外设都没有时钟了,但是外部中断的工作是不需要时钟的,这点也可以在代码中看出,在初始化的时候根本没有开启EXTI时钟的参数,这也是EXTI能在时钟关闭的情况下工作的原因,因为它不需要时钟。

刚才写的睡眠模式其实都是内核的操作,睡眠模式涉及的几个寄存器,也都是在内核里,跟PWR外设关系不大,所以刚才开启睡眠模式都没有用到PWR的库函数。不过现在停止模式,涉及到内核之外的电路操作,这就需要用到PWR外设了。

PWR库函数

void PWR_DeInit(void);//恢复缺省配置
void PWR_BackupAccessCmd(FunctionalState NewState);//使能后备区域的访问
void PWR_PVDCmd(FunctionalState NewState);//使能PVD功能
void PWR_PVDLevelConfig(uint32_t PWR_PVDLevel);//配置PVD的阈值电压
void PWR_WakeUpPinCmd(FunctionalState NewState);//使能位于PA0位置的WKUP引脚(待机模式可以用WKUP的上升沿唤醒)
void PWR_EnterSTOPMode(uint32_t PWR_Regulator, uint8_t PWR_STOPEntry);//进入停止模式
void PWR_EnterSTANDBYMode(void);//进入待机模式
FlagStatus PWR_GetFlagStatus(uint32_t PWR_FLAG);//获取标志位
void PWR_ClearFlag(uint32_t PWR_FLAG);//清除标志位

停止模式和待机模式都需要PWR外设干活,所以我们首先要开启PWR的时钟。

int main(void)
{
	/*模块初始化*/
	OLED_Init();			//OLED初始化
	CountSensor_Init();		//计数传感器初始化
	
	/*显示静态字符串*/
	OLED_ShowString(1, 1, "Count:");	//1行1列显示字符串Count:
	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR,ENABLE);
	
	while (1)
	{
		OLED_ShowNum(1, 7, CountSensor_Get(), 5);		//OLED不断刷新显示CountSensor_Get的返回值
		
		OLED_ShowString(2,1,"Running");
		Delay_ms(100);
		OLED_ShowString(2,1,"        ");
		Delay_ms(100);
		
		PWR_EnterSTOPMode(PWR_Regulator_ON,PWR_STOPEntry_WFI);
	}
}

可以看到的现象是,Running在没有东西遮挡红外传感器的情况下不再闪烁,证明进入到了停止模式,但同时,我们也可以发现,Running的闪烁速度明显变慢了,这是因为退出停止模式时,HSI被选为系统时钟,而HSI是8MHz,所以唤醒之后的程序,运行就会明显变慢,解决方法也很简单,我们只需要在停止模式之后,重新调用SystemInit()函数,重新启动HSE配置72MHz主频就行了。

 

待机模式/实时时钟

这个程序我们的主要任务就是:

1.设置RTC闹钟。

2.进入待机模式。

3.使用闹钟信号,唤醒待机模式。

进入待机模式后再被唤醒,程序是从头开始运行的,所以PWR_EnterSTANDBYMode()函数,进入待机模式之后的代码也不会再被运行。

 

int main(void)
{
	OLED_Init();
	MyRTC_Init();
	
	//开启PWR时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR,ENABLE);
	
	OLED_ShowString(1,1,"CNT :");//秒计数器
	OLED_ShowString(2,1,"ALR :");//闹钟值
	OLED_ShowString(3,1,"ALRF:");//闹钟值标志位
	
	uint32_t Alarm = RTC_GetCounter()+10;
	
	//在每次复位后设置闹钟值,闹钟值是10s后
	RTC_SetAlarm(Alarm);
	OLED_ShowNum(2,6,Alarm,10);
	
	while(1)
	{	
		OLED_ShowNum(1,6,RTC_GetCounter(),10);
		OLED_ShowNum(3,6,RTC_GetFlagStatus(RTC_FLAG_ALR),1);
		
		OLED_ShowString(4,1,"Running");
		Delay_ms(100);
		OLED_ShowString(4,1,"       ");
		Delay_ms(100);
		
		PWR_EnterSTANDBYMode();
	}
	
}