打印
[STM32F3]

基于STM32通过RTC唤醒低功耗模式

[复制链接]
400|10
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
t61000|  楼主 | 2022-8-31 15:50 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
ST, tc
一、低功耗模式
1.简介
通俗的来讲低功耗模式就是降低单片机的运行功耗

STM32F10xxx有三种低功耗模式:
(1)睡眠模式 (Cortex™-M3 内核停止,所有外设包括 Cortex-M3 核心的外设,如 NVIC 、系统时
钟 (SysTick) 等仍在运行 )
(2)停止模式 ( 所有的时钟都已停止 )
(3)待机模式 (1.8V 电源关闭 )

使用特权

评论回复
沙发
t61000|  楼主 | 2022-8-31 15:53 | 只看该作者
此外,在运行模式下,可以通过以下方式中的一种降低功耗:
(1)降低系统时钟
(2)关闭 APB 和 AHB 总线上未被使用的外设时钟。
这里以停止模式为例来讲解

使用特权

评论回复
板凳
t61000|  楼主 | 2022-8-31 15:54 | 只看该作者
进入STOP模式
要想进入停止模式,我们只需调用固件库的函数:
/*
    入口参数:
        PWR_Regulator_LowPower:低功耗模式进入
        PWR_STOPEntry_WFI     : WFI进入,任意外部中断退出
*/
PWR_EnterSTOPMode(PWR_Regulator_LowPower,PWR_STOPEntry_WFI)

使用特权

评论回复
地板
t61000|  楼主 | 2022-8-31 15:55 | 只看该作者
执行完这一句后就能进入停止模式,进入STOP模式后程序就会停在这个位置不会再往下跑了,当我们通过外部中断来退出STOP模式后,程序开始从当前位置的下一句运行起来。

使用特权

评论回复
5
t61000|  楼主 | 2022-8-31 15:55 | 只看该作者
退出STOP模式
     这里我们通过一个外部中断来退出STOP模式,我们采用串口的接收引脚通过发送数据产生引脚的电平变化来触发外部中断产生,从而退出STOP模式

使用特权

评论回复
6
t61000|  楼主 | 2022-8-31 15:57 | 只看该作者
具体配置代码如下
u8 UART1_RECEIVE_OK = 0;                //UART1中断接收完成标志位          
u8 UART1_RECEIVE_DATA[200]; //UART1接收缓存
u8 UART1_SEND_DATA[200];                //UART1发送缓存
u8 R_COUNT = 0;
/*
    串口1初始化
*/
void USART1_Init(u32 bound)
{
        //GPIO端口设置
        GPIO_InitTypeDef GPIO_InitStructure;
        USART_InitTypeDef USART_InitStructure;
        NVIC_InitTypeDef NVIC_InitStructure;

        RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);        //使能USART1,GPIOA时钟

        //USART1_TX   GPIOA.9
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;        //复用推挽输出
        GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9

        //USART1_RX          GPIOA.10初始化
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
        GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10  

        //Usart1 NVIC 配置
        NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;                //子优先级3
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                        //IRQ通道使能
        NVIC_Init(&NVIC_InitStructure);        //根据指定的参数初始化VIC寄存器

        //USART 初始化设置

        USART_InitStructure.USART_BaudRate = bound;//串口波特率
        USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
        USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
        USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
        USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
        USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;        //收发模式

    USART_Init(USART1, &USART_InitStructure); //初始化串口1
    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断
    USART_Cmd(USART1, ENABLE);                    //使能串口1

}

/*
    串口1中断
*/
void USART1_IRQHandler(void)                        //串口1中断服务程序
{

        if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)//判断是否为接收中断
        {
                UART1_RECEIVE_DATA[R_COUNT] =  USART_ReceiveData(USART1);
                //USART_SendData(USART1,UART1_RECEIVE_DATA[R_COUNT]);
                R_COUNT++;
       
        }
        if(R_COUNT == 199)
        {
                R_COUNT = 0;
        }
        UART1_RECEIVE_OK = 1;
}

/*
        外部中断初始化
*/
void exti_init(void)
{
        EXTI_InitTypeDef EXTI_InitStructure;
        NVIC_InitTypeDef NVIC_InitStructure;
       
          RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);                            //使能复用功能时钟       
       
          GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource10);
          EXTI_InitStructure.EXTI_Line=EXTI_Line10;                                            //串口1 RX
          EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;                       
          EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;                    //下降沿触发
          EXTI_InitStructure.EXTI_LineCmd = ENABLE;
          EXTI_Init(&EXTI_InitStructure);         

    NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;                        //外部中断线10
          NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;        //抢占优先级2,
          NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;                        //子优先级2
          NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                                        //使能外部中断通道
          NVIC_Init(&NVIC_InitStructure);       
       
}

/*
        外部中断
*/
void EXTI15_10_IRQHandler(void)
{
        EXTI_ClearITPendingBit(EXTI_Line10); //清除LINE 10 上的中断标志位

}

使用特权

评论回复
7
t61000|  楼主 | 2022-9-29 23:07 | 只看该作者
这里我们初始化了串口1,然后初始化了外部中断的线10 对应串口1的接收引脚PA10,当我们通过串口工具发送数据时就会产生一个外部中断,从而来退出STOP模式
二、RTC实时时钟
1.简介
实时时钟是一个独立的定时器。RTC模块和时钟配置系统(RCC_BDCR寄存器)处于后备区域,即在系统复位或从待机模式唤醒后,RTC的设置和时间维持不变。
● 可编程的预分频系数:分频系数最高为 2 20 。
● 32 位的可编程计数器,可用于较长时间段的测量。
● 2 个分离的时钟:用于 APB1 接口的 PCLK1 和 RTC 时钟 (RTC 时钟的频率必须小于 PCLK1 时钟
频率的四分之一以上 ) 。
● 可以选择以下三种 RTC 的时钟源:
─ HSE 时钟除以 128 ;
─ LSE 振荡器时钟;
─ LSI 振荡器时钟 ( 详见 6.2.8
308/754
节 RTC 时钟 ) 。
● 2 个独立的复位类型:
─ APB1 接口由系统复位;
─ RTC 核心 ( 预分频器、闹钟、计数器和分频器 ) 只能由后备域复位 ( 详见 6.1.3 节 ) 。
● 3 个专门的可屏蔽中断:
─ 闹钟中断,用来产生一个软件可编程的闹钟中断。
─ 秒中断,用来产生一个可编程的周期性中断信号 ( 最长可达 1 秒 ) 。
─ 溢出中断,指示内部可编程计数器溢出并回转为 0 的状态。

使用特权

评论回复
8
t61000|  楼主 | 2022-9-29 23:08 | 只看该作者
2.RTC实时时钟的使用

RTC可以使用外部时钟源,即在低功耗模式下依然能够正常运行

在第一节中讲了外部中断来退出STOP模式,那是需要我们主动去操作来产生一个外部中断,而RTC可以定时来自动产生外部中断从而退出STOP模式

从简介中我们知道RTC有三个专门的中断,我们常用的就是秒中断和闹钟中断,这里闹钟中断发生时会产生一个外部中断,我们可以从STM32中文参考手册中外部中断事件章节得知=

使用特权

评论回复
9
t61000|  楼主 | 2022-9-29 23:08 | 只看该作者

使用特权

评论回复
10
t61000|  楼主 | 2022-9-29 23:14 | 只看该作者
3.配置RTC

下面直接上代码

rtc.c
#include "rtc.h"
#include "sys.h"
                    

/*
        RTC初始化
*/
u8 RTC_Init(void)
{
        //检查是不是第一次配置时钟
        u8 temp=0;
       
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);        //使能PWR和BKP外设时钟   
        PWR_BackupAccessCmd(ENABLE);        //使能后备寄存器访问  
                       
        BKP_DeInit();        //复位备份区域        
        RCC_LSEConfig(RCC_LSE_ON);        //设置外部低速晶振(LSE),使用外设低速晶振
        while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET)        //检查指定的RCC标志位设置与否,等待低速晶振就绪
        {
                temp++;
                delay_ms(10);
        }
        if(temp>=250)
                return 1;//初始化时钟失败,晶振有问题          
               
        RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);                //设置RTC时钟(RTCCLK),选择LSE作为RTC时钟   
        RCC_RTCCLKCmd(ENABLE);                                                //使能RTC时钟  
        RTC_WaitForSynchro();                //等待RTC寄存器同步
        RTC_WaitForLastTask();        //等待最近一次对RTC寄存器的写操作完成       
       
        RTC_ITConfig(RTC_IT_ALR, ENABLE);                //使能RTC时钟中断
       
        RTC_WaitForLastTask();        //等待最近一次对RTC寄存器的写操作完成
        RTC_EnterConfigMode();/// 允许配置       
       
        RTC_SetPrescaler(32767); //设置RTC预分频的值
        RTC_SetCounter(0);       // 这里的设置,每设置一次就要等待写操作完成
        RTC_WaitForLastTask();         //等待最近一次对RTC寄存器的写操作完成       
        RTC_SetAlarm(5);         // 设置闹钟的时间要加1哦,也就是说现在是5S
        RTC_WaitForLastTask();        //等待最近一次对RTC寄存器的写操作完成       
        RTC_ExitConfigMode();   //退出配置模式  
        RTC_NVIC_Config();      //RCT中断分组设置                                                         
       
        return 0; //ok
}       

void RTC_NVIC_Config(void)
{       
    NVIC_InitTypeDef NVIC_InitStructure;
        EXTI_InitTypeDef EXTI_InitStructure;
       
        //EXTI17配置
        EXTI_InitStructure.EXTI_Line = EXTI_Line17;         // RTC闹钟为外部中断17
        EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
        EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
        EXTI_InitStructure.EXTI_LineCmd = ENABLE;
        EXTI_Init(&EXTI_InitStructure);
       
        // 时钟中断配置
        NVIC_InitStructure.NVIC_IRQChannel = RTCAlarm_IRQn;                //RTC全局中断
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;        //先占优先级1位,从优先级3位
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;        //先占优先级0位,从优先级4位
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                //使能该通道中断
        NVIC_Init(&NVIC_InitStructure);                //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
}



/*
        RTC闹钟中断函数,当RTC计数器的值和闹钟设定的一样时,触发这个函数
*/
void RTCAlarm_IRQHandler(void)
{
        printf("RTC ALARM INTERRUPT\r\n");
        if(RTC_GetITStatus(RTC_IT_ALR)!= RESET)//闹钟中断
        {
                RTC_SetCounter(0);            // 清除RTC计数器,从新开始计数
                RTC_WaitForLastTask();
                RTC_SetAlarm(10);            // 设置闹钟 设置闹钟的时间要加1哦,,也就是说现在是11S
                RTC_WaitForLastTask();          
        }
       
        RTC_ClearITPendingBit(RTC_IT_ALR);                //清闹钟中断
        EXTI_ClearITPendingBit(EXTI_Line17);     //闹钟事件发生,会产生一个EXTI_17外部中断,此标志位要清除,否则下次停止模式进入失败
        RTC_ClearITPendingBit(RTC_IT_OW);                //清闹钟中断
        RTC_WaitForLastTask();                                     
}

使用特权

评论回复
11
t61000|  楼主 | 2022-9-29 23:15 | 只看该作者
本帖最后由 t61000 于 2022-9-29 23:18 编辑

这里我们配置了RTC的初始化和闹钟中断,每次产生闹钟中断我们便打印一句话来测试

main.c
#include "stm32f10x.h"
#include "sys.h"

extern u8 UART1_RECEIVE_OK;
extern u8 UART1_RECEIVE_DATA[200];
extern u8 R_COUNT;

/*
        进入STOP模式
*/
void Enter_Stop_Mode()
{
        printf("enter stop mode\r\n");
        PWR_EnterSTOPMode(PWR_Regulator_LowPower,PWR_STOPEntry_WFI);  //进入STOP停机模式
        SystemInit();        //系统初始化
        delay_ms(10);
        printf("wake up\r\n");
}

int main(void)
{        

         USART1_Init(115200); //初始化串口
         exti_init();                  //初始化外部中断
         delay_init();                  //初始化延时
         delay_ms(1000);
         RTC_Init();                  //RTC初始化

         Enter_Stop_Mode();

         while(1)
         {
                delay_ms(1000);
                printf("running now\r\n");
               
                if(UART1_RECEIVE_OK) //发送串口中断
                {
                        UART1_RECEIVE_OK = 0;
                        if(UART1_RECEIVE_DATA[0] == 0xaa) //如果收到0xaa则进入stop模式
                        {
                                Enter_Stop_Mode();
                                UART1_RECEIVE_OK = 0;
                        }
                        R_COUNT = 0;
                }
         }
         
}

在主函数中我们上电就进入STOP模式,这时候程序不会进到while里去而是停在了Enter_Stop_Mode(); 这一行,我们配置了RTC闹钟中断为10s发送一次,也就是说等待10s后就退出STOP模式进到while里去

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

19

主题

124

帖子

0

粉丝