历史上的今天
返回首页

历史上的今天

今天是:2024年12月26日(星期四)

正在发生

2018年12月26日 | STM32 RTC闹钟中断和唤醒待机模式

2018-12-26 来源:eefocus

RTC代表的是实时时钟的意思。因为它提供的时钟基准比较准确所以用处还是很多的。本文章主要讲解基于STM32F103上的RTC闹钟中断功能以及用闹钟中断唤醒STM32的待机模式。


需要注意RTC的几个要点:1.RTC的值被设定后它就会一直按照设定的基准时间自己递增,如果你的硬件设备上发现一个纽扣电池,那就是给RTC功能供电用的。因此就算关闭掉设备的主电源,RTC的运行也是不受影响的,当然如果扣掉纽扣电池或者不带纽扣电池它就不工作了。2.每次STM32复位后这个RTC值它重新计数还是继续计数要考虑清楚3.RTC的值是有上限的,它的最大值就是2的32次方减一,这个数字很大因此不用太关心。


本文章的第一个例程是让RTC一次设定值后一秒递增一个单位,每过40秒就触发一个闹钟并进入中断,每经过一秒就将秒数输出到计算机上。下面就是RTC的初始化配置。(第一个程序从正点原子的程序上修改得来)


u8 RTC_Init()

{

u8 temp = 0;

NVIC_InitTypeDef NVIC_InitStructure;

RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP|RCC_APB1Periph_PWR,ENABLE);//电源时钟和背部时钟

PWR_BackupAccessCmd(ENABLE);                    //允许背部区域写


if (BKP_ReadBackupRegister(BKP_DR1) != 0xC0B4)

{

 

BKP_DeInit();

RCC_LSEConfig(RCC_LSE_ON);

while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET)

{

    temp++;

    delay_ms(10);

}

if(temp>=250)return 1;   

RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);    

RCC_RTCCLKCmd(ENABLE);  

RTC_WaitForLastTask();

RTC_WaitForSynchro();

RTC_ITConfig(RTC_IT_SEC|RTC_IT_ALR, ENABLE); //打开RTC的秒中断和闹钟中断

RTC_WaitForLastTask();

RTC_EnterConfigMode();                        //进入配置RTC模式

RTC_SetPrescaler(32767); 


RTC_SetCounter(0);                            //初始值设定为0s

    RTC_WaitForLastTask();

RTC_SetAlarm(40);                            //闹钟值设定为40s

RTC_WaitForLastTask();                        //等待上述配置完成

RTC_ExitConfigMode();                          //退出配置模式

BKP_WriteBackupRegister(BKP_DR1, 0XC0B4);

PWR_BackupAccessCmd(DISABLE);                //不允许背部区域写操作

}

else

{

PWR_BackupAccessCmd(DISABLE);

RTC_WaitForSynchro();

RTC_ITConfig(RTC_IT_SEC|RTC_IT_ALR,ENABLE); //打开RTC的秒中断和闹钟中断

RTC_WaitForLastTask();

}

  

NVIC_InitStructure.NVIC_IRQChannel = RTC_IRQn; //RTC全局中断的中断配置

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;

NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;

NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

NVIC_Init(&NVIC_InitStructure);

 

return 0;

}

//此初始化函数在主函数中的用法

while(RTC_Init())

{

printf("INIT Programing is ERROR!!\r\n");


}


if (BKP_ReadBackupRegister(BKP_DR1) != 0xC0B4)    的意思就是让STM32上电后自检是不是第一次运行这个程序。BKP_ReadBackupRegister(BKP_DR1)代表读取BKP_DR1的值,如果第一次运行这个程序那这个值一定是0X0000,值和0XC0B4不相等就进入配置初始化程序。BKP_WriteBackupRegister(BKP_DR1, 0XC0B4)代表往BKP_DR1这个寄存器中写入0XC0B4,注意BKP_DR1这个值被写入之后就算复位他也不会被清除成0000。这样的话就算复位或者重新上电,初始程序也不会再执行一遍,所以RTC的值就不会再重新设置了。如果想要RTC的值重新从0开始计数,那就可以吧0XC0B4改成一个新的数字,重新下载一次程序就可以了。


接下来的时钟配置选择打开外部低速时钟LSE,它向RTC提供时钟频率。它频率为是32.768KHZ.我们需要分频后使用它。分成1HZ。



根据上图1HZ = 32768/(32767+1)  所以RTC_SetPrescaler(32767)就能明白什么意思了吧。


为RTC提供时钟频率还是有两种选择的,这里我们用一种就够了。


RTC_WaitForLastTask()和RTC_WaitForSynchro()都是等待最近的写操作完成的意思,在RTC配置的时候这两条尤其第一条都是很重要的,如果添加位置不当或缺失程序有时候会卡在一个地方。


接下来打开RTC的秒中断和闹钟中断。在接下来就是对RTC的一些具体配置比如初始值和计数频率以及闹钟数值的配置。按照ST的说法:



要用到RTC_WaitForLastTask()和RTC_EnterConfigMode(进入配置)和RTC_ExitConfigMode(退出配置); 这三个库函数。


最后往BKP_DR1这个寄存器中写入0XC0B4代表第一次RTC配置已经完成。当程序复位或者设备重新上电后它都不会在进入这个配置程序了。还要关闭背部寄存器的写操作允许位。


else的程序就是第二次运行这个程序的时候执行的命令。主要是打开RTC的两个中断。


之后的程序就是配置RTC全局中断。这个中断包括了闹钟和秒中断。还有一个返回值要在主程序里面才能明白他的意义,简单来说返回0代表RTC的配置没有问题,返回1则相反。可以不用返回值的方法,因为一般没问题。


下面就是RTC全局中断的介绍:


void RTC_IRQHandler()

{



if(RTC_GetITStatus(RTC_IT_ALR)!=RESET) //是否闹钟中断发生

{


printf("THE ALARM  READY =%d \r\n",RTC_GetCounter());//输出此时的秒数

RTC_ClearITPendingBit(RTC_IT_ALR);

PWR_BackupAccessCmd(ENABLE);

RTC_EnterConfigMode();

    RTC_WaitForLastTask();

RTC_SetAlarm(40+RTC_GetCounter());   //配置下次闹钟为40s后

RTC_WaitForLastTask();

RTC_ExitConfigMode();   

PWR_BackupAccessCmd(DISABLE);

}

    if(RTC_GetITStatus(RTC_IT_SEC)!=RESET)  //是否秒中断发生

{


printf("Time is  =%d \r\n",RTC_GetCounter()); //输出此时的秒数


}

RTC_ClearITPendingBit(RTC_IT_SEC|RTC_IT_OW); //清除秒中断标志位和溢出位

RTC_WaitForLastTask();

}

下面就是程序运行的结果。


要注意的一点就是在中断函数里面如果把判断秒中断的函数写在了判断闹钟中断的前面,就会得到THE ALARM READY=41。。。还有一点就是就算没有写反,下一次闹钟的时间是81秒,如图


下面就是待机唤醒:


待机模式是功耗最低的一种模式。外部复位(NRST 引脚)、 IWDG 复位、 WKUP 引脚上的上升沿或 RTC 闹钟事件

发生时,STM32从待机模式退出。其中外部复位和WKUP的方法比较简单。主要讲一下RTC闹钟唤醒。


这两个程序一个是让STM32的一个管脚接按键,按键按下的时候STM32进入待机状态,当40秒的闹钟到来时唤醒STM32.


另一个是当按键按下时候进入待机状态,闹钟设置为5S秒后,闹钟来临时唤醒STM32.


void RTC_Init()

{


NVIC_InitTypeDef NVIC_InitStructure;

RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP|RCC_APB1Periph_PWR,ENABLE);

PWR_WakeUpPinCmd(ENABLE);

PWR_BackupAccessCmd(ENABLE);

    BKP_DeInit();

RCC_LSEConfig(RCC_LSE_ON);

while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET)

{

 

}

RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);

RCC_RTCCLKCmd(ENABLE);

RTC_WaitForLastTask();

RTC_WaitForSynchro();

RTC_ITConfig(RTC_IT_SEC, ENABLE);

RTC_WaitForLastTask();

RTC_EnterConfigMode(); 

RTC_SetCounter(0);    //设置计数初始值

 

RTC_WaitForLastTask();

RTC_SetPrescaler(32767);

RTC_WaitForLastTask();

RTC_SetAlarm(40);      //闹钟定时40S

RTC_WaitForLastTask();

RTC_ExitConfigMode();


NVIC_InitStructure.NVIC_IRQChannel = RTC_IRQn; //RTC全局中断  它的优先级要小于按键

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;

NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;

NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

NVIC_Init(&NVIC_InitStructure);

 

}

void EXTIX_Init(void)

{

 

    EXTI_InitTypeDef EXTI_InitStructure;

    NVIC_InitTypeDef NVIC_InitStructure;

  GPIO_InitTypeDef GPIO_InitStructure;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOC,ENABLE);


GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_5;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; 

  GPIO_Init(GPIOC, &GPIO_InitStructure);

    GPIO_EXTILineConfig(GPIO_PortSourceGPIOC,GPIO_PinSource5);//配置按键PC5为外部中断

 

  EXTI_InitStructure.EXTI_Line=EXTI_Line5;

  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 = EXTI9_5_IRQn; //外部中断的优先级高于RTC

  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x00;

  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x00;

  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

  NVIC_Init(&NVIC_InitStructure); 


 

}

void EXTI9_5_IRQHandler()    //按键对应的外部中断

{

EXTI_ClearITPendingBit(EXTI_Line5);

 

PWR_EnterSTANDBYMode();    //进入待机模式

 

}

void RTC_IRQHandler()        //RTC的中断

{


if(RTC_GetITStatus(RTC_IT_SEC)!=RESET)

{

printf("TIME IS =%d \r\n",RTC_GetCounter());

}


RTC_ClearITPendingBit(RTC_IT_SEC|RTC_IT_OW);

RTC_WaitForLastTask();

}


以上的例程就是让STM32运行模式下每秒都讲时间显示到计算机上,在按下连接PC5的按键后STM32进入待机模式,计时40秒后闹钟触发,STM32退出待机模式。要注意的是本程序没有设置保存RTC的值,因此每次从待机退出后时间从零开始重新计数。而且按键触发待机模式要在STM32上电40S之前,否则40S之后闹钟错过没法唤醒STM32.



13秒时候按下按键等待计数到40S的时候都是在待机过程中所以STM32串口没法发送数据。40s闹钟来临时计数又从0开始,退出待机模式相当于复位。


第二个例程当按键按下时候进入待机状态,闹钟设置为5S秒后,闹钟来临时唤醒STM32.运行的时候将秒数显示到计算机。本程序在上面那个上更改少些地方即可。


void RTC_Init()

{


NVIC_InitTypeDef NVIC_InitStructure;

RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP|RCC_APB1Periph_PWR,ENABLE);

PWR_WakeUpPinCmd(ENABLE);

PWR_BackupAccessCmd(ENABLE);

if(PWR_GetFlagStatus(PWR_FLAG_SB)!=RESET) //如果现在处于待机模式

{

PWR_ClearFlag(PWR_FLAG_SB);            //清除待机模式

RTC_ITConfig(RTC_IT_SEC, ENABLE);     //打开RTC中断

RTC_WaitForSynchro();

}

else

{

BKP_DeInit();

RCC_LSEConfig(RCC_LSE_ON);

while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET)

{


}

RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);

RCC_RTCCLKCmd(ENABLE);

RTC_WaitForLastTask();

RTC_WaitForSynchro();

RTC_ITConfig(RTC_IT_SEC, ENABLE);

RTC_WaitForLastTask();

RTC_EnterConfigMode(); 

RTC_SetCounter(0);

 

RTC_WaitForLastTask();

RTC_SetPrescaler(32767);

RTC_WaitForLastTask();

//RTC_SetAlarm(35);        //取消这个闹钟设置

RTC_WaitForLastTask();

RTC_ExitConfigMode();

}

NVIC_InitStructure.NVIC_IRQChannel = RTC_IRQn;

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;

NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;

NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

NVIC_Init(&NVIC_InitStructure);

 

}

void EXTI9_5_IRQHandler()

{

EXTI_ClearITPendingBit(EXTI_Line5);

RTC_ClearFlag(RTC_FLAG_SEC);

while(RTC_GetFlagStatus(RTC_FLAG_SEC)==RESET);

RTC_SetAlarm(RTC_GetCounter()+5);  //闹钟在此时刻加上5秒

RTC_WaitForLastTask();

PWR_EnterSTANDBYMode();        //进入待机模式

 

}

其他的地方不需要更改。


这个程序的运行结果,可以看到在450s的时候进入待机模式5S之后程序接着运行。


时间并不是从0开始。

推荐阅读

史海拾趣

普芯达电子(Chipswinner)公司的发展小趣事

随着公司规模的不断扩大和产品质量的不断提升,普芯达电子开始积极拓展市场,寻求更广阔的发展空间。公司通过与国内外知名企业和机构建立合作关系,共同开展技术研发和市场推广等活动,进一步提高了公司的知名度和影响力。同时,普芯达电子还积极参与国际展览和交流活动,与全球同行交流学习,不断提升自身的技术水平和市场竞争力。

Electroswitch公司的发展小趣事

随着市场竞争的加剧,Electroswitch意识到仅仅依靠产品质量已经不足以赢得市场。因此,公司开始注重提升服务质量,为客户提供更加全面和专业的支持。通过加强售前咨询、售后服务以及技术支持等方面的投入,Electroswitch成功赢得了客户的信任和忠诚。这也使得公司在电子行业中的地位得到了进一步提升。

First Switchtech公司的发展小趣事

为了应对原材料成本上涨和市场竞争加剧的挑战,First Switchtech公司(或类似公司)实施了供应链优化策略。公司通过与上游供应商建立长期合作关系,实现了原材料的稳定供应和成本的有效控制。同时,公司还引入了先进的生产管理系统,提高了生产效率和产品质量。这些措施不仅降低了产品的生产成本,还增强了公司的市场竞争力。

Highland Electronics Co Ltd公司的发展小趣事

High Tech Chips Inc深知人才是企业发展的核心竞争力。因此,公司一直将人才战略放在企业发展的重要位置。公司不仅吸引了大量来自国内外顶尖高校的优秀人才加盟,还建立了完善的人才培养体系和激励机制。通过举办技术交流会、设立创新基金等方式激发员工的创新潜能和工作热情。这些举措为公司的发展奠定了坚实的人才基础和技术储备。

台湾町洋(dinkle)公司的发展小趣事

随着电子行业的快速发展,町洋不断投入研发资源,致力于技术创新和产品升级。公司成功开发出了一系列具有高性能、高可靠性和高安全性的接线端子产品,满足了不同客户的需求。同时,町洋还积极引进国际先进技术和管理经验,不断提升自身的竞争力和市场占有率。

Essentra Components公司的发展小趣事

随着环保意识的不断提高,Essentra Components公司积极响应绿色发展的号召,将环保理念融入到产品的设计和生产过程中。公司采用环保材料替代传统材料,减少了对环境的污染;同时,公司还引进了先进的节能减排技术,降低了生产过程中的能耗和排放。这些措施不仅提高了产品的环保性能,还为公司赢得了更多的社会赞誉和市场份额。

问答坊 | AI 解惑

利用运算放大器实现的混频器

混频器常常用一个二极管桥式电路(diodebridge)或一个Gilbert单元(Gilbertcell)来实现。这两类混频器都使用了一个本地振荡器(LO)来跳转射频输入的极性。   当LO为正时,RF输入被混频转换为中频(IF)输出时极性不改变。当LO为负时,RF输入转换为IF时极 ...…

查看全部问答>

新硬盘用ghost装系统后无法引导启动

我的一个新硬盘(笔记本的,笔记本是r60e),在bios里面能够看到这个硬盘,但是用普通的安装盘安装操作系统时硬件检测时不能通过,然后换用gost盘安装,能够安装,但是装完重启时不能从引导从硬盘启动。硬盘可以确认是好的,我把硬盘放到硬盘盒里可 ...…

查看全部问答>

insmod加载模块错误

  各位大侠:    我现在在弄开发板的时候,遇到了加载模块的错误。我是在PC机上把.c文件经过交叉编译后生成一个net_hook.o的模块,编译    时只出现两个警告。    然后通过nfs共享到开发板上,在/mnt文 ...…

查看全部问答>

vc新手问个工程方法的问题

学了一年的VC老用不上,于是想在公司的设备基础上做个工控啥的。 思想是这样,利用串口通讯实现电脑与单片机相连,并且可以在电脑上实时监控和操作。 我想把这些工作分成通讯、数据处理、参数设定、实时监控等模块,然后编一个总界面来调这些模块 ...…

查看全部问答>

STM32串口寄存器与库的关系?不能正常通讯,正在调程序,在

在做串口通讯调试的时候,STM32接收正常,可是只能发送一字节的数据,然后就不再发送的,经查程序发现如下问题。在状态寄存器状态寄存器(USART_SR)中与发送有关的位为位6和位7,还有低三位。如果检测串口是否发送完成或错误,应该用的检测值为 ...…

查看全部问答>

求助我的KEIL的DEBUG中没有Luminary Eva Board,USB仿真器不知道怎么用请大虾帮助

我按照PDF上的资料来配置KEIL却发现DEBUG中没有Luminary Eva Board选项,还有Utilities里面也没有另外USB仿真器怎么用啊???求帮助…

查看全部问答>

光纤收发器 IP175CH调试问题

最近遇到一个问题,基于光纤收发器 IP175CH芯片做了一块板子,IP175CH芯片外围电路都是根据芯片手册给的标准电路设计的,但是在调试的时候遇到问题,就是通过IP175CH芯片转换过来的电信号用网线连接电口,无法上网,通过示波器显示出来的芯片所需的 ...…

查看全部问答>

求指点9B96以太网问题!

小弟用自己做的板子(主芯片是9L97的和9B96相似)从最基本的LWip调试以太网模块,我用论坛里academic的帖子里的第一个程序https://bbs.eeworld.com.cn/thread-222963-1-2.html编译通过后下载到板子里,用交叉网线把开发板和PC相连,程序下载进去以 ...…

查看全部问答>

嵌入式AES加密的应用?

在StellarisWare的软件包中都附带了aes的加解密例程,本人很好奇也研究了一下。程序中只对一个字符串进行了加解密并显示。但是不是很清除AES在嵌入式中有何具体的应用?…

查看全部问答>