STM32低功耗学习-停止模式-(学习笔记)

上次我们学习了睡眠模式,这次我们来看一下停止模式。这也是STM32的一种低功耗模式。

停止模式只能通过外部中断触发进行唤醒。

一.了解

1.课前了解

我们首先复制一下对射红外传感器计次的代码。

起个新名字。

编译一下,方便代码提示。

2.代码前瞻

我们看到这个countsensor总是一直在get。但是如果外部一直没有中断信号的话,还是比较浪费资源,这个get是没有意义的。我们可以让他进入更为省电的停止模式。

在停止模式下1.8V区域的时钟关闭。CPU和外设都没有时钟了。但是外部中断是不需要时钟的。这一点从代码中也可以看出来。

我们初始化的时候,根本就没有开启EXTI外部中断的时钟。这也是EXTI能在时钟关闭的情况下工作的原因。因为它不需要外部时钟。

二.程序

写代码之前我们还是来了解一下库函数。上一次我们讲的睡眠模式其实都只是内核的操作。睡眠模式涉及到的几个寄存器,都是在内核里。跟PWR外设关系不大。所以我们上次没有用到PWR的库函数。现在的停止模式,涉及到内核之外的电路操作了,这就需要用到PWR外设了。

1.停止模式函数了解

(1)复位

恢复缺省配置,给PWR外设复位。

(2)使能后备区域

使能后备区域的访问。开启后后备区域才会上电。

(3)PVD函数

这两个是跟PVD相关的函数。

这个函数是配置PVD的阈值电压。

然后CMD就是使能PVD功能。如果需要使用PVD的话,就先指定阈值,然后使能PVD就可以。

(4)wakeup使能

使能位于PA0位置的WakeUp引脚。

这个是配合待机模式使用的。

(5)进入停止模式

进入停止模式

(6)进入待机模式

进入待机模式,

(7)标志位

获取标志位和清除标志位函数。

2.小测试

我们首先还是添加一个running指示。

在循环下面,加入一个指示。

编译一下。然后下载的时候,需要按住复位键不松开,才可以。下载完成后,松手。

这时候就开始闪烁了running。

然后遮挡传感器的话,数值就会增加。

但是在没有计数的情况下,还是在不断地running,这个操作是非常耗电的。

3.代码编写

首先我们开启PWR的时钟。如果外设没有开启时钟,那么写入寄存器是无效的。时钟开启之后我们需要让他在主循环的最后,进入停止模式。

我们使用这个函数,这个函数有两个参数,我们转到定义看一下。

第一个参数是指定电压调节器在停止模式里的状态。开启或者低功耗。

这两个都可以,我们暂时选择开启。

之后第二个参数是停止模式的入口参数。是选择WFI指令进入停止模式,还是WFE指令进入停止模式。

这里我们选择WFI指令进入停止模式。那么这一段代码干了什么呢,我们可以看一下函数内部。

第一步读取PWR_CR寄存器放在临时变量里面。

第二步清除PDDS和LPDS。其中清除PDDS位就是代表选择停止模式。

也就是这里,PDDS=0。这一步就会进入上面的操作,进入停机模式。

第三步,根据我们给定的参数,设置LPDAS位。如果你这个参数选择电压调节器开启,那么这里LPDS=0。

第四步,把临时变量,写入到PWR_CR寄存器参数生效。最后设置内核里面的SLEEPDEEP位。这里或等于(|=)SLEEPDEEP的掩码就是把SLEEPDEEP置1。

后面就是我们选择执行哪个指令,如果第二个阐述给的是WFI那么就执行WFI,反之执行WFE。

最后一行,就是停止模式唤醒之后,才能执行到。可以看到在退出停止模式后,他很贴心的把我们的SLEEPDEEP位清零了。

4.测试一下

我们编译下载,一定要按住复位键,要不下载不进去。

现在就可以看到在空闲时running就不会闪烁了。这说明我们的主循环时停止运行了。

我们每遮挡一下,runing就会显示一下,然后数值+1。

如果我们按一下复位键。复位之后第一次running闪烁很快。是正常的时间,我们遮挡的话,running闪烁就会变得很慢了,这是为什么呢。

就在这里。也就是我们在首次复位后,systeminit函数配置就是HSE*9倍频的72Mhz的主频。所以复位后,第一次runinig闪烁的很快。而之后进入停止模式在退出之后默认时钟就变为HSI了,HSI是8M的低速时钟。所以唤醒后的程序,就会变得很慢。

我们只需要进行在退出停止模式之后,再次调用一次systeminit函数就可以了。

我们再来测试一下。

可以看到这时running闪烁是正常的,着我们就解决了退出停止模式之后,主频变为8M的问题了。

到这里我们就完成了停止模式代码。

三.所有代码

1.main.c

cs 复制代码
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "LED.h"
#include "KEY.h"
#include "OLED.h"
#include "chuanganqi.h"

//uint8_t Keynum;

int main(void)
{	
    //开启PWR时钟
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
    
    OLED_Init();
    chuan_init();//外部中断初始化
    OLED_ShowString(1, 1, "count:");
    while(1)
    {
        OLED_ShowNum(1, 7, chuan_get(), 5);
        
        //running指示
        OLED_ShowString(2, 1,"Running");
        Delay_ms(100);
        OLED_ShowString(2, 1,"       ");
        Delay_ms(100);
        
        //开启停止模式
        PWR_EnterSTOPMode(PWR_Regulator_ON, PWR_STOPEntry_WFI);
        SystemInit();
    }
}

2.chuanganqi.c

cs 复制代码
#include "stm32f10x.h"                  // Device header

uint16_t chuan_count;

void chuan_init(void)
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);//开启GPIOB时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);//开启AFIO时钟
    
    //GPIO配置
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    
    //AFIO配置
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14);
    
    //EXTI配置
    EXTI_InitTypeDef EXTI_InitStructure;
    EXTI_InitStructure.EXTI_Line = EXTI_Line14;//指定中断线
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;//指定选择中断线的新状态,使能,失能
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;//指定外部中断线的模式,分为中断模式,事件模式
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;//指定触发信号的有效边缘,上升沿,下降沿,边沿
    EXTI_Init(&EXTI_InitStructure);
    
    //NVIC配置
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//芯片只能分一组,一次,所有的模块都要写一样的。
    NVIC_InitTypeDef NVIC_InitStructure;
    NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;//指定中断通道开启或关闭。EXTI的15-10通道集成在这里。
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//指定通道使能or失能。
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;//指定抢占优先级
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;//指定响应优先级
    NVIC_Init(&NVIC_InitStructure);
}

//中断函数编写,因为中断函数是固定的,我们去文件夹复制一下
void EXTI15_10_IRQHandler(void)
{
    //因为这函数EXTI15-10都可以进来我们需要判断是不是我们需要的中断通道。
    if(EXTI_GetITStatus(EXTI_Line14) == SET)//返回值是set或reset
    {
        chuan_count++;
        //要清除中断标志位,不清除就会一直置1从而一直中断
        EXTI_ClearITPendingBit(EXTI_Line14);
    }
}

int16_t chuan_get(void)
{
    return chuan_count;
}

3.chuanganqi.h

cs 复制代码
#include "stdint.h"
#ifndef __CHUAN_H    //防止重定义
#define __CHUAN_H    //那么就定义这个

void chuan_init(void);
int16_t chuan_get(void);

#endif
相关推荐
找了一圈尾巴2 小时前
LLM-as-a-Judge-论文学习(下)
学习·模型评估
@游子2 小时前
Python学习笔记-Day5
笔记·python·学习
MARIN_shen2 小时前
Marin说PCB之电容物料的替换经验总计--01
单片机·嵌入式硬件·硬件工程·信号处理·pcb工艺
摇滚侠2 小时前
2025最新 SpringCloud 教程,网关功能、创建网关,笔记51、笔记52
java·笔记·spring cloud
漏洞文库-Web安全2 小时前
Linux逆向学习记录
linux·运维·学习·安全·web安全·网络安全·逆向
又是忙碌的一天2 小时前
Socket学习
java·学习·socket
windfantasy19902 小时前
青少年编程考级:如何避免过度负担,让考级助力学习?
学习·青少年编程
浓墨染彩霞2 小时前
Java-----多线路
java·经验分享·笔记
Zero_Era3 小时前
LKT6850安全MCU应用场景介绍
单片机·嵌入式硬件