当前位置: 首页 > news >正文

修改主频睡眠模式停止模式待机模式

代码示例:

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

接线图:睡眠模式+串口发送+接收
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;			//闹钟为唤醒后当前时间的后10sRTC_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);		//显示闹钟标志位,显示长度为1OLED_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微安。
在这里插入图片描述

相关文章:

  • 第五章重采样方法
  • 牛顿迭代法(求解整数的近似平方根)
  • 网络爬虫中selenium和requests这两个工具有什么区别呢?
  • 力扣爆刷第153天之TOP100五连刷(接雨水、环形链表、最长上升子序列)
  • Golang笔记:使用serial包进行串口通讯
  • STM32单片机-BKP和RTC
  • 如何级联移位寄存器(74HC595)
  • 【Linux】基础IO——文件描述符,重定向,FILE
  • WordPress 技巧:如何限制或取消自动清空回收站功能
  • 怎样去掉卷子上的答案并打印
  • mac下Xcode在iphone真机上测试运行iOS软件
  • [信号与系统]有关时域信号与频域信号的转换
  • 红队内网攻防渗透:内网渗透之内网对抗:隧道技术篇防火墙组策略FRPNPSChiselSocks代理端口映射C2上线
  • 力扣85.最大矩形
  • 【深度学习驱动流体力学】VTK创建、处理和可视化流体数据
  • Angular 响应式表单 基础例子
  • Git初体验
  • HTTP--网络协议分层,http历史(二)
  • JavaScript 无符号位移运算符 三个大于号 的使用方法
  • NLPIR语义挖掘平台推动行业大数据应用服务
  • Object.assign方法不能实现深复制
  • SQLServer之创建数据库快照
  • Web Storage相关
  • 第三十一到第三十三天:我是精明的小卖家(一)
  • 罗辑思维在全链路压测方面的实践和工作笔记
  • 爬虫进阶 -- 神级程序员:让你的爬虫就像人类的用户行为!
  • 排序(1):冒泡排序
  • 让你成为前端,后端或全栈开发程序员的进阶指南,一门学到老的技术
  • 入门到放弃node系列之Hello Word篇
  • 试着探索高并发下的系统架构面貌
  • 腾讯视频格式如何转换成mp4 将下载的qlv文件转换成mp4的方法
  • 小程序上传图片到七牛云(支持多张上传,预览,删除)
  • 微龛半导体获数千万Pre-A轮融资,投资方为国中创投 ...
  • 新年再起“裁员潮”,“钢铁侠”马斯克要一举裁掉SpaceX 600余名员工 ...
  • ​1:1公有云能力整体输出,腾讯云“七剑”下云端
  • ​LeetCode解法汇总307. 区域和检索 - 数组可修改
  • ​必胜客礼品卡回收多少钱,回收平台哪家好
  • #多叉树深度遍历_结合深度学习的视频编码方法--帧内预测
  • #我与Java虚拟机的故事#连载13:有这本书就够了
  • (13)[Xamarin.Android] 不同分辨率下的图片使用概论
  • (4)(4.6) Triducer
  • (九)One-Wire总线-DS18B20
  • (十)Flink Table API 和 SQL 基本概念
  • (算法二)滑动窗口
  • (一)kafka实战——kafka源码编译启动
  • (一)Spring Cloud 直击微服务作用、架构应用、hystrix降级
  • (转) ns2/nam与nam实现相关的文件
  • (转)关于pipe()的详细解析
  • (转载)Linux 多线程条件变量同步
  • (轉貼) UML中文FAQ (OO) (UML)
  • ./configure、make、make install 命令
  • .bat批处理(四):路径相关%cd%和%~dp0的区别
  • .NET Core、DNX、DNU、DNVM、MVC6学习资料
  • .NET6 命令行启动及发布单个Exe文件
  • .NET框架类在ASP.NET中的使用(2) ——QA