13.三种低功耗和RTC实时时钟

目录

低功耗

低功耗的目的

如何降低功耗

如何测量功耗

STM32 中的电源系统

后备供电区域如何实现始终有电

 STM32 中的低功耗

        睡眠模式

        停止模式

        待机模式

三种低功耗模式的编程

        睡眠模式

        停止模式

        待机模式

RTC 实时时钟

RTC 实时时钟的作用

实现实时时钟的必要硬件

STM32 内部的 RTC 介绍

STM32 内部的 RTC 时钟源介绍

STM32 内部的 RTC 的接口函数

STM32 内部 RTC 初始化过程

手写算法实现

如何获取准确时间

编程实现 RTC 时钟

        利用 time 接口实现


低功耗

低功耗的目的

        电源对电子设备的重要性不言而喻,它是保证系统稳定运行的基础,而保证系统能稳定运行后,又有低功耗的要求。 在很多应用场合中都对电子设备的功耗要求非常苛刻,如某些传感器信息采集设备,仅靠小型的电池提供电源,要求工作长达数年之久,且期间不需要任何维护;由于智慧穿戴设备的小型化要求,电池体积不能太大导致容量也比较小,所以也很有必要从控制功耗入手,提高设备的续行时间。

如何降低功耗

        1、选择低功耗的芯片

                STM32F103ZET6 F 标准类的

                STM32L051C8T6 L 低功耗类的

        2、降低 CPU 的主频

        3、将不必要的片上外设,关闭,以及对应的时钟也关闭

        4、设备可以设置低功耗模式,可以单片机自带的,睡眠,停止,待机 -- 课程中讲的

        5、DHT11 KQM6600 设计电源的开关电路 直接断电

                KQM6600 F 引脚 低功耗

        6、产品的硬件原理图上的元器件,选择功耗比较低的器件

如何测量功耗

        直流电源,显示设备的电压和电流,测试功耗。

        万用表,电流档位,串联到电路中,测量电流,计算功耗。

STM32 中的电源系统

        ADC 电源及参考电压(VDDA 供电区域)。为了提高转换精度, STM32 的 ADC 配有独立的电源接口,方便进行单独的滤波。ADC 的工作电源使用 VDDA 引脚输入,使用 VSSA 作为独立的地连接, VREF 引脚则为 ADC 提供测量使用的参考电压。

        调压器供电电路(VDD/1.8V 供电区域)。在 STM32 的电源系统中调压器供电的电路是最主要的部分,调压器为备份域及待机电路以外的所有数字电路供电,其中包括内核、数字外设以及 RAM,调压器的输出电压约为 1.8V,因而使用调压器供电的这些电路区域被称为 1.8V 域。调压器可以运行在“运行模式”、“停止模式”以及“待机模式”。在运行模式下, 1.8V 域全功率运行;在停止模式下 1.8V 域运行在低功耗状态, 1.8V 区域的所有时钟都被关闭,相应的外设都停止了工作,但它会保留内核寄存器以及 SRAM 的内容;在待机模式下,整个 1.8V 域都断电,该区域的内核寄存器及 SRAM 内容都会丢失(备份区域的寄存器不受影响)。

        备份域电路(后备供电区域)。STM32 的 LSE 振荡器、 RTC 及备份寄存器这些器件被包含进备份域电路中,这部分的电路可以通过 STM32 的 VBAT 引脚获取供电电源,在实际应用中一般会使用 3V 的钮扣电池对该引脚供电

后备供电区域如何实现始终有电

        如果要实现 备份域电路(后背供电区域)一直有电,需要 VBAT 引脚接纽扣电池

        备份域一直有电,可以实现 RTC 时间掉电不丢失

        有的开发板:VBAT 引脚没有接纽扣电池

 STM32 中的低功耗

        按功耗由高到低排列, STM32 具有运行、睡眠、停止和待机四种工作模式。上电复位后 STM32 处于运行状态时,当内核不需要继续运行,就可以选择进入后面的三种低功耗模式降低功耗,这三种模式中,电源消耗不同、唤醒时间不同、唤醒源不同,用户需要根据应用需求,选择最佳的低功耗模式。

        睡眠模式

在睡眠模式中,仅关闭了内核时钟,内核停止运行,但其片上外设, CM3 核心的外设全都还照常运行。两种方式进入睡眠模式,它的进入方式决定了从睡眠唤醒的方式,分别是 WFI(wait for interrupt)和 WFE(wait for event),即由等待“中断”唤醒和由“事件”唤醒。

        停止模式

        在停止模式中,进一步关闭了其它所有的时钟,于是所有的外设都停止了工作,但由于其 1.8V 区域的部分电源没有关闭,还保留了内核的寄存器、内存的信息,所以从停止模式唤醒,并重新开启时钟后,还可以从上次停止处继续执行代码。停止模式可以由任意一个外部中断(EXTI)唤醒,在停止模式中可以选择电压调节器为开模式或低功耗模式。

        待机模式

        待机模式,它除了关闭所有的时钟,还把 1.8V 区域的电源也完全关闭了,也就是说,从待机模式唤醒后,由于没有之前代码的运行记录,只能对芯片复位,重新检测 boot 条件,从头开始执行程序。它有四种唤醒方式,分别是 WKUP(PA0)引脚的上升沿, RTC 闹钟事件, NRST 引脚的复位和 IWDG(独立看门狗)复位。

三种低功耗模式的编程

        进入各种低功耗模式时都需要调用 WFI 或 WFE 命令,它们实质上都是内核指令,在库文件

core_cm3.h 中把这些指令封装成了函数

        睡眠模式

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
UART1_Config();
LED_Config();
EXTI_Config();
printf("进入睡眠模式前\r\n");
__WFI();
printf("退出睡眠模式后\r\n");
while(1)
{
    
}
/*
三种低功耗:睡眠
如何进入:调用__WFI,等待中断
如何退出:任一中断
谁的功耗最低:三者中最高的
退出之后,代码从哪里运行:从原来位置继续运行
IO 的状态:对 IO 口没有影响
使用场景:几乎没啥用
*/

        停止模式

        直接调用 WFI 和 WFE 指令可以进入睡眠模式,而进入停止模式则还需要在调用指令前设置一些寄存器位,STM32 标准库把这部分的操作封装到 PWR_EnterSTOPMode 函数中了。这个函数有两个输入参数,分别用于控制调压器的模式及选择使用 WFI 或 WFE 停止,代码中先是根据调压器的模式配置 PWR_CR 寄存器,再把内核寄存器的 SLEEPDEEP 位置 1,这样再调用 WFI 或 WFE 命令时, STM32 就不是睡眠,而是进入停止模式了。函数结尾处的语句用于复位 SLEEPDEEP 位的状态,由于它是在 WFI 及 WFE 指令之后的,所以这部分代码是在 STM32 被唤醒的时候才会执行。

        要注意的是进入停止模式后, STM32 的所有 I/O 都保持在停止前的状态,而当它被唤醒时, STM32 使用 HSI 作为系统时钟(8MHz)运行,由于系统时钟会影响很多外设的工作状态,所以一般我们在唤醒后会重新开启 HSE,把系统时钟设置回原来的状态。

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
UART1_Config();
LED_Config();
EXTI_Config();
printf("进入停止模式前\r\n");
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR,ENABLE);
PWR_EnterSTOPMode(PWR_Regulator_LowPower,PWR_STOPEntry_WFI);
SystemInit();
printf("退出停止模式后\r\n");
while(1)
{
    
}
/*
三种低功耗:停止
如何进入:PWR_EnterSTOPMode
如何退出:任一外部中断
谁的功耗最低:三者中居中
退出之后,代码从哪里运行:从原来位置继续运行
IO 的状态:对 IO 口没有影响
使用场景:
注意:
1.退出停止之后,选择 HSI 作为系统时钟,需要重新调用时钟初始化,选择 HSE(8M)*9 的 PLLCLK
作为系统时钟
2.设置 PWR 的寄存器,需要开启 PWR 的时钟
*/

        待机模式

        STM32 标准库也提供了控制进入待机模式的函数:PWR_EnterSTANDBYMode该函数中先配置了 PDDS 寄存器位及 SLEEPDEEP 寄存器位,接着调用__force_stores 函数确保存储操作完毕后再调用 WFI 指令,从而进入待机模式。这里值得注意的是,待机模式也可以使用 WFE 指令进入的,如果您有需要可以自行修改。在进入待机模式后,除了被使能了的用于唤醒的 I/O,其余 I/O 都进入高阻态,而从待机模式唤醒后,相当于复位 STM32 芯片,程序重新从头开始执行。

int main(void)
{
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    SysTick_Config(SystemCoreClock/1000); //core_cm3.h 1827 行
    UART1_Config();
    KEY_Config();
    LED_Config();
    printf("准备进入主循环\r\n");
    while(1)
    {
        if(KEY_Period[0]>=KEY_Period[1])
        {
            KEY_Period[0]=0;
            KEY_Handle();
        }
        if(LED_Period[0]>=LED_Period[1])
        {
            LED_Period[0]=0;
            printf("主循环正在执行\r\n");
        }
    }
}
void KEY_Handle(void)
{
	uint8_t KEY_State = 0;
	KEY_State = KEY_SCAN();
	switch(KEY_State)
	{
		case 0: //无按键按下
			break;
		case 1: //按键 1 短按并松手
			break;
		case 4: //按键 4 短按并松手
			printf("进入待机模式前\r\n");
			RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR,ENABLE);
			PWR_WakeUpPinCmd(ENABLE); //使能 WakeUp 唤醒功能
			PWR_EnterSTANDBYMode();//进入待机模式
			break;
		case 5: 
			break;
		case 6: 
				
			break;
		case 7:
				
			break;
		case 8: 
				
			break;
		default:
			break;
	}
}
/*
三种低功耗:待机
如何进入:PWR_EnterSTANDBYMode
如何退出:WKUP 引脚的上升沿、 RTC 闹钟事件的上升沿、 NRST 引脚上外部复位、 IWDG 复位。
谁的功耗最低:三者中最低
退出之后,代码从哪里运行:从头运行
IO 的状态:将 IO 设置成高阻态(浮空输入)
使用场景:额温枪,把 PA0 按键配置成唤醒按键,当设备 2 分钟无人使用,进入待机模式
户外设备,配置成 RTC 闹钟事件唤醒,唤醒之后,采集数据,上报平台,上报成功,
继续进入待机模式,等待闹钟唤醒。
注意:
1.开 PWR 的时钟
2.使能 PA0 唤醒功能
如何解决代码无法下载:
1.退出低功耗模式,就可以下载
2.一手按复位键,一手点下载,点击下载之后松开复位键,下载成功
*/

RTC 实时时钟

RTC 实时时钟的作用

        RTC--实时时钟

        本质上:依然是定时器,为什么必须有它,因为它可以实现掉电不丢失,时间能继续运行。

实现实时时钟的必要硬件

        做一个日历:让整个设备断电之后,时间不丢失,使用单片机内部的 RTC 功能做实时时钟,必须 VBAT 引脚有电池

STM32 内部的 RTC 介绍

        注释:当 VDD 有电的情况下,后备供电区域由 VDD 提供电源,当 VDD 断电之后,由 VBAT 给后备供电区域提供电源。如果 VBAT 引脚没有接电池,VDD 掉电之后,后备供电区域也停止工作了。意味着时间从头运行了。

        实时时钟是一个独立的定时器。 RTC 模块拥有一组连续计数的计数器,在相应软件配置下,可提供时钟日历的功能。修改计数器的值可以重新设置系统当前的时间和日期。

        RTC 模块和时钟配置系统(RCC_BDCR 寄存器)处于后备区域,即在系统复位或从待机模式唤醒后, RTC 的设置和时间维持不变。

        系统复位后,对后备寄存器和 RTC 的访问被禁止,这是为了防止对后备区域(BKP)的意外写操作。执行以下操作将使能对后备寄存器和 RTC 的访问:

        ● 设置寄存器 RCC_APB1ENR 的 PWREN 和 BKPEN 位,使能电源和后备接口时钟。

        ● 设置寄存器 PWR_CR 的 DBP 位,使能对后备寄存器和 RTC 的访问。

        RTC 由两个主要部分组成(参见下图)。第一部分(APB1 接口)用来和 APB1 总线相连。此单元还包含一组 16位寄存器,可通过 APB1 总线对其进行读写操作(参见 16.4 节)。APB1 接口由 APB1 总线时钟驱动,用来与 APB1总线接口。另一部分(RTC 核心)由一组可编程计数器组成,分成两个主要模块。第一个模块是 RTC 的预分频模块,它可编程产生最长为 1 秒的 RTC 时间基准 TR_CLK。RTC 的预分频模块包含了一个 20 位的可编程分频器(RTC 预分频器)。如果在 RTC_CR 寄存器中设置了相应的允许位,则在每个 TR_CLK 周期中 RTC 产生一个中断(秒中断)。第二个模块是一个 32 位的可编程计数器,可被初始化为当前的系统时间。系统时间按 TR_CLK 周期累加并与存储在 RTC_ALR 寄存器中的可编程时间相比较,如果 RTC_CR 控制寄存器中设置了相应允许位,比较匹配时将产生一个闹钟中断。

STM32 内部的 RTC 时钟源介绍

STM32 内部的 RTC 的接口函数

涉及到四章 参考手册 PWR BKP RTC RCC

STM32 内部 RTC 初始化过程

        1. 开 BKP 和 PWR 的时钟 -- 参考手册 16.1

        2. 允许对后背区域和 RTC 的访问操作 -- 参考手册 16.1

        3. 将 RTC 的时钟配置成 LSE

        4. 使能 RTC 时钟,并等待 APB1 和 RTC 同步完成 -- 参考手册 16.3.3

        5. 对 RTC 进行分频,计数频率设置成 1HZ

        6. 设置 RTC 的初始值,把当前时间给他

        7. 需要使用秒中断,就配置秒中断

        8. 需要使用闹钟,就配置闹钟 -- 不使用

手写算法实现

        通过平年闰年手动计算。

如何获取准确时间

        通过串口发送给单片机,单片机解析出来时间,跟新到 RTC 中

        设备连接网络,获取当前时间,更新到 RTC 中

        注意:正常情况下如果 VBAT 引脚接电池,RTC 时间掉电不丢失,不接电池,掉电丢失。

编程实现 RTC 时钟

        利用 time 接口实现

                mktime();//根据结构体转成秒数

                localtime();//将秒数转换成本地时间结构体
                时间戳(Unix timestamp)转换工具 - 在线工具

#include "RTC.h"
#include "time.h"

struct tm Set_Time = {0, 15, 14, 22, 5-1, 2025-1900};//待设置的时间
uint32_t Set_Time_Cnt;//待设置的秒数
struct tm *Now_Time = NULL;//结构体指针,当前时间
uint32_t Now_Time_Cnt;//当前时间秒数

uint8_t RTC_Sec_Flag = 0;//秒标志位

void RTC_Config(void)
{
	//开启PWR,BKP时钟,位置27,28置1
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);

	//使能PWR_CR的DBP位8置1
	PWR_BackupAccessCmd(ENABLE);

	//启用低速外部晶振(LSE),它通常用于提供一个稳定的32.768 kHz时钟源,用于RTC的计时。
	RCC_LSEConfig(RCC_LSE_ON);
	//等待LSE(低速外部晶振)稳定并就绪。RCC_FLAG_LSERDY标志位在LSE稳定时会被置位,代码会等待直到该标志位为1。
	while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET)
	{}
	//选择LSE为RTC的时钟源32.768khz
	RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);
	//使能RTC时钟
	RCC_RTCCLKCmd(ENABLE);

	//等待RTC寄存器与APB时钟同步。由于RTC模块是一个独立的外设,它的寄存器更新可能需要一些时间。
	RTC_WaitForSynchro();

	//检测上次是否操作完成
	RTC_WaitForLastTask();

	//对RTC分频,内部默认累加1,配置RTC_CRL_CNF
	/* 
		RTC period = RTCCLK/RTC_PR = (32.768 KHz)/(32767+1) 
		f=32768/32768=1hz
		t=1/f=1s
	*/
	RTC_SetPrescaler(32767); 
	//检测上次是否操作完成
	RTC_WaitForLastTask();
	
	NVIC_InitTypeDef   NVIC_InitStructure = {0};
	NVIC_InitStructure.NVIC_IRQChannel = RTC_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;//抢占优先级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;//次级优先级
	NVIC_Init(&NVIC_InitStructure);
	//使能秒中断,CRH
	RTC_ITConfig(RTC_IT_SEC, ENABLE);
	//等待操作完成
	RTC_WaitForLastTask();
	//更新待设置的时间到RTC中
	Time_Adjust();
}


void Time_Adjust(void)
{
	//1.检测上一次是否操作完成  参考手册16.3.4描述  判断参考手册16.4.2的位5是否置1
	RTC_WaitForLastTask();
	//2.将时间结构体转换成秒数
	Set_Time_Cnt = mktime(&Set_Time);//根据结构体转换成秒数
	//3.将当前时间戳更新到计数器中   参考手册16.4.5
	RTC_SetCounter(Set_Time_Cnt);
	//4.检测上一次是否操作完成  参考手册16.3.4描述  判断参考手册16.4.2的位5是否置1
	RTC_WaitForLastTask();
}


void RTC_IRQHandler(void)
{
	//1.检测中断标志位   判断参考手册16.4.2的位0是否置1	
	if (RTC_GetITStatus(RTC_IT_SEC) != RESET)
	{
		//2.执行中断服务函数具体内容
		RTC_Sec_Flag = 1;
		//3.清除中断标志位  参考手册16.4.2的位0清0	
		RTC_ClearITPendingBit(RTC_IT_SEC);
		//4.检测上一次是否操作完成  参考手册16.3.4描述  判断参考手册16.4.2的位5是否置1	
		RTC_WaitForLastTask();
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

努力做小白

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值