江科大笔记—修改主频&睡眠模式&停止模式&待机模式

代码示例:

接线图:修改主频
在这里插入图片描述

接线图:睡眠模式+串口发送+接收
CH340 USB转串口模块。GND和stm32共地。RXD接PA9,TXD接PA10。
在这里插入图片描述
接线图:停止模式+对射式红外传感器计次
对射式红外传感器模块的VCC和GND接上供电。DO输出接STM32的PB14引脚。
在这里插入图片描述

接线图:待机模式+实时时钟
PA0引出一根线,目的是,可以手动把PA0接到3.3v或断开。因为PA0有个WKUP,可以唤醒待机模式,WKUP引脚上升沿有效。直接接一个线短接到3.3v,来手动产生一个上升沿,测试WKUP的效果。

在这里插入图片描述
研究system_stm32f10x.h、system_stm32f10x.c文件:

system_stm32f10x.h、system_stm32f10x.c文件是用来配置系统时钟的,也就是配置RCC时钟树。
RCC时钟树
在这里插入图片描述

在这里这个图就是RCC时钟树的全部电路,左边是四个时钟源,HSI、HSE、LSE、LSI用于提供时钟,右边就是各个外设,就是使用时钟的地方,我们用的最多的就是AHB时钟、APB1时钟、APP2时钟。另外还有一些时钟,它们的来源不是AHB和APB,比如I2S的时钟直接来源于system clock,USB的时钟直接来源于PLL。

我们主要关心的就是这个外部的8MHz晶振,它如何进行选择,如何倍频才能得到这个72MHz的SYSCLK系统主频,然后系统主频如何去分配,才能得到AHB、APB1和APB2的时钟频率,

在这里插入图片描述
#if叫做预编译,主要是用来兼容不同型号的设备 。修改主频,把宏解除注释(/*)。

当前带钥匙符号代表文件只读,解除只读的方法:打开工程文件夹或在system_stm32f10x.c右键,点打开包含的文件夹
在这里插入图片描述


在这里插入图片描述
在这里插入图片描述
右键,属性
在这里插入图片描述
把只读的√去掉。
在这里插入图片描述
如想整个文件或整个工程文件夹都取消只读,在外面对整个文件夹操作,点击属性,把只读去掉即可。
在这里插入图片描述

总结:首先是是Systemlnit函数,进来启动HSI,之后就是各种缺省配置,最后调用SetSysClock;SetSysClock是一个分配函数,根据前面解除不同的宏定义,选择执行不同的配置函数:如SetSysClockTo72、To56、To48等。最后在SetSysClockTo72里配置函数,如配置是:选择HSE作为锁相环输入,之后锁相环进行9倍频,再选择锁相环输出,作为主频,这样主频就是72MHZ。
在这里插入图片描述

修改主频

main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"

int main(void)
{
	OLED_Init();							//OLED初始化
	
	OLED_ShowString(1, 1, "SYSCLK:");		//显示静态字符串
	OLED_ShowNum(1, 8, SystemCoreClock, 8);	//显示SystemCoreClock变量
											//SystemCoreClock的值表示当前的系统主频频率
	
	while (1)
	{
		OLED_ShowString(2, 1, "Running");	//闪烁Running,指示当前主循环运行的快慢
		Delay_ms(500);
		OLED_ShowString(2, 1, "       ");
		Delay_ms(500);
	}
}

程序现象:
第一行主频频率是36M。第二行Running闪烁周期是2s。
在这里插入图片描述

睡眠模式

main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Serial.h"

uint8_t RxData;			//定义用于接收串口数据的变量

int main(void)
{
	OLED_Init();		//OLED初始化
	OLED_ShowString(1, 1, "RxData:");	//显示静态字符串
	
	Serial_Init();		//串口初始化
	
	while (1)
	{
		if (Serial_GetRxFlag() == 1)			//检查串口接收数据的标志位
		{
			RxData = Serial_GetRxData();		//获取串口接收的数据
			Serial_SendByte(RxData);			//串口将收到的数据回传回去,用于测试
			OLED_ShowHexNum(1, 8, RxData, 2);	//显示串口接收的数据
		}
		
		OLED_ShowString(2, 1, "Running");		//OLED闪烁Running,指示当前主循环正在运行
		Delay_ms(100);
		OLED_ShowString(2, 1, "       ");
		Delay_ms(100);
		
		__WFI();								//执行WFI指令,CPU睡眠,并等待中断唤醒
		                                        //执行WFI这时CPU会立刻睡眠,程序就停在了WFI指令这里,但是各个                                                                                    外设比如USRT还是工作状态
		//等到我们用串口助手发送数据时,USRT外设收到数据产生中断,唤醒CPU之后程序在暂停的地方继续运行
	}
}

程序现象
串口助手发送数据,每发送一个Running闪烁一次,接收区回传一次数据,说明程序在收到数据之后,可以唤醒工作一次。在不影响功能的前提下,CPU能在空闲时睡眠,节约用电。
在这里插入图片描述

停止模式

停止模式只能通过外部中断触发(唤醒),所以和停止模式相关的代码肯定得用外部中断。

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

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

PWR.h里常用函数

void PWR_DeInit(void);//恢复缺省配置
void PWR_BackupAccessCmd(FunctionalState NewState); // 使能后备区域的访问


// 配置PVD使能电压
void PWR_PVDCmd(FunctionalState NewState);	   //使能PVD功能
void PWR_PVDLevelConfig(uint32_t PWR_PVDLevel);//配置PVD的阈值电压


// 使能WKUP引脚唤醒功能在待机模式下使用
void PWR_WakeUpPinCmd(FunctionalState NewState);//使能位于PA0位置的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); //清除标志位

main .c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "CountSensor.h"

int main(void)
{
	/*模块初始化*/
	OLED_Init();			//OLED初始化
	CountSensor_Init();		//计数传感器初始化
	
	/*开启时钟*/
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);		//开启PWR的时钟
															//停止模式和待机模式一定要记得开启
	
	/*显示静态字符串*/
	OLED_ShowString(1, 1, "Count:");
	
	while (1)
	{
		OLED_ShowNum(1, 7, CountSensor_Get(), 5);			//OLED不断刷新显示CountSensor_Get的返回值
		
		OLED_ShowString(2, 1, "Running");					//OLED闪烁Running,指示当前主循环正在运行
		Delay_ms(100);
		OLED_ShowString(2, 1, "       ");                   //把Running清除
		Delay_ms(100);
		
		PWR_EnterSTOPMode(PWR_Regulator_ON, PWR_STOPEntry_WFI);	//STM32进入停止模式,并等待中断唤醒
		SystemInit();										//唤醒后,要重新配置时钟。重新启动SHE,配置72MHZ
	}                                                       
}

程序现象

在空闲时,Running没有闪烁。每遮挡一次,Running闪烁一下。而复位后第一次的Running闪烁很快,是正常的时间,而遮挡之后,Running闪烁变慢。
退出停止模式时,HSI被选为系统时钟,也就是在我们首次复位后,SystemInit函数里配置的是HSE*9倍频的72M主频。 所以复位后第一次Running闪烁很快,而之后进入停止模式,再退出时默认时钟就变成HSI了,HSI是8M,所以唤醒之后的程序运行就会明显变慢。

在这里插入图片描述

待机模式

主要任务是,第一,设置RTC时钟;第二,进入待机模式;第三,使用闹钟信号,唤醒待机模式。

代码:
main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "MyRTC.h"

int main(void)
{
	/*模块初始化*/
	OLED_Init();		//OLED初始化
	MyRTC_Init();		//RTC初始化
	
	/*开启时钟*/
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);		//开启PWR的时钟
															//停止模式和待机模式一定要记得开启  虽然MyRTC_Init里开启了,多次开启无所谓,防止其他没调用MyRTC_Init的场景   但时钟没开启外设就不会工作。
	
	/*显示静态字符串*/
	OLED_ShowString(1, 1, "CNT :");              //显示秒计数器
	OLED_ShowString(2, 1, "ALR :");              //显示闹钟值
	OLED_ShowString(3, 1, "ALRF:");              //闹钟标志位
	 
	/*使能WKUP引脚*/
	PWR_WakeUpPinCmd(ENABLE);						//使能位于PA0的WKUP引脚,WKUP引脚上升沿唤醒待机模式
	
	/*设定闹钟*/
	uint32_t Alarm = RTC_GetCounter() + 10;			//闹钟为唤醒后当前时间的后10s
	RTC_SetAlarm(Alarm);							//写入闹钟值到RTC的ALR寄存器 ,这个寄存器只写不可读,所以使用变量Alarm显示到OLED上
	OLED_ShowNum(2, 6, Alarm, 10);					//显示闹钟值
	
	while (1)
	{
		OLED_ShowNum(1, 6, RTC_GetCounter(), 10);	//显示32位的秒计数器
		OLED_ShowNum(3, 6, RTC_GetFlagStatus(RTC_FLAG_ALR), 1);		//显示闹钟标志位,显示长度为1
		
		OLED_ShowString(4, 1, "Running");			//OLED闪烁Running,指示当前主循环正在运行
		Delay_ms(100);
		OLED_ShowString(4, 1, "       ");           //Running清除
		Delay_ms(100);
		
		OLED_ShowString(4, 9, "STANDBY");			//OLED闪烁STANDBY,指示即将进入待机模式
		Delay_ms(1000);
		OLED_ShowString(4, 9, "       ");
		Delay_ms(100);
		
		OLED_Clear();								//OLED清屏,模拟关闭外部所有的耗电设备,以达到极度省电
		
		PWR_EnterSTANDBYMode();						//STM32进入停止模式,并等待指定的唤醒事件(WKUP上升沿或RTC闹钟)
		/*待机模式唤醒后,程序会重头开始运行*/
	}   //待机模式之后的代码执行不到,下次继续从头开始 在程序刚开始的时候自动调用SystemInit初始化时钟,所以待机模式我们就不用像停止模式那样,自己调用SystemInit了
	    //并且这个while循环,实际上也只有执行一遍的机会,把这个while循环去掉也是可以的
}

程序现象:

复位后,Running闪烁一次,进入待机,当然CNT也不会进行刷新了,然后需等一下,闹钟触发,待机模式唤醒 ,CNT和闹钟值刷新一下,Running闪烁一次。并且,每次唤醒后,闹钟值都会重新设定,通过这一现象,确定待机模式唤醒后,程序是从头开始执行。stm32进入待机模式之前,要把外部接的模块全部关掉。

在这里插入图片描述
最开始显示信息,进入待机,OLED清屏,等待一会,闹钟触发之后,唤醒一次。
还有wkup引脚的唤醒功能:wkup引脚默认下拉,引脚悬空,就是低电平,把线插到高电平,待机模式唤醒。
在这里插入图片描述
在这里插入图片描述

小实验:验证待机模式省电

手册说待机模式的电流只有3微安左右,为此进行了测试,

  • 这里我单独找了个stm32板子,把这个电源供电线正极给剪断,串联一个万用表测电流。
    当然最开始我直接测试哈,待机模式的电流高达一点多毫安,远超手册里说的3微安,但首先很明显这个电源指示灯肯定是耗电的,所以我先把这个电源指示灯去掉了,再测试电流仍然有几百微安。

    那说明还有别的东西耗电,之后我就把板子背面的这个LDO稳压器去掉了,这个LDO虽然我们并没有用到哈,但接上它就会有电流损耗,最后去掉电源指示灯和LDO之后,待机模式的电流就下降到3微安了。

首先,万用表接到200mA电流档,最开始看到正常工作的电流
在这里插入图片描述
之后等一会,进入待机模式,这个供电电流瞬间变为0,万用表换到200微安档,可以看到,目前待机的供电电流是3.3微安。
在这里插入图片描述

  • 22
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
科大STM32笔记是关于STM32单片机的学习笔记,其中涵盖了一些关于按键初始化和按键读取的代码示例。在这些代码中,通过引用中的Key_Init函数来对按键进行初始化,然后通过引用中的Key_GetNum函数来获取按键按下的键码值。代码中使用了STM32的GPIO模块来配置引脚的工作模式和读取引脚的电平状态。此外,引用中提到STM32内部集成了硬件收发电路,可以通过写入控制寄存器CR和数据寄存器DR来实现与外设的通信,并通过读取状态寄存器SR来了解外设电路的当前状态。这些寄存器的使用可以实现对外设的控制和监测,减轻CPU的负担。因此,科大STM32笔记主要是介绍了STM32单片机的相关知识和编程技巧。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [STM32学习笔记 -- I2C(科大)](https://blog.csdn.net/weixin_61244109/article/details/131002266)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [STM32科大学习笔记](https://blog.csdn.net/weixin_38647099/article/details/128337708)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值