历史上的今天
返回首页

历史上的今天

今天是:2025年08月14日(星期四)

正在发生

2018年08月14日 | STM32F103ZET6 — RTC

2018-08-14 来源:eefocus

简介

"RTC"是Real Time Clock 的简称,意为实时时钟。即,提供类似于 PC 上的时间记录信息的功能。既然是实时时钟,则至少应该有秒、分、时等信息。也可以直观的把他理解成为一个计数器,一直累加。但又不同于 CPU 上电后的那些计数器,对于 RTC ,需要支持的是掉电后的继续计数(存在备用电源)。所谓掉电,是指电源Vpp断开的情况下,为了RTC外设掉电可以继续运行,必须给STM32芯片通过VBAT引脚街上锂电池.当主电源VDD有效时,由VDD给RTC外设供电.当VDD掉电后,由VBAT给RTC外设供电.无论由什么电源供电,RTC中的数据始终都保存在属于RTC的备份域中,如果主电源和VBA都掉电,那么备份域中保存的所有数据都将丢失.(备份域除了RTC模块的寄存器,还有42个16位的寄存器可以在VDD掉电的情况下保存用户程序的数序,系统复位或电源复位时,这些数据也不会被复位). 
 

STM32 的 RTC 操作,主要功能由三部分组成:

电源配置

后备(BKP配置)

RTC 配置

 

时钟

RTC 时钟可以选择三种时钟源的输入:

1. 高速外部时钟 HSE 的 128 分频

2. 低速外部时钟 LSE

3. 低速内部时钟 LSI


针对单板上,我们选择 LSE 32.768kHz 的 LSE 作为 RTC 的时钟输入。

当主电源掉电的时候,由Vbat进行供电:

工作原理

灰色部分为备用区域,系统掉电后,由后备电源持续供电,寄存器相关的值会持续存在并持续工作。同时,此部分的复位也单独存在 BKP 复位部分,即通过对 BKP 的复位来进行此部分的复位。

 

可以看到, RTC CLK 供给 RTC 模块时,首先通过一个分频器,进行分频,经过分频器后的时钟 TR_CLK 用于后续的计数器使用。RTC 支持闹钟功能,即,在 RTC_ALR 寄存器设置一个期望的值,RTC_CNT 在 TR_CLK 下进行计数,当计数器的值到达了 RTC_ALR ,则产生闹钟事件。

注:普通情况下,通过计算,使得 RTC 计数器能以 1s 一次的方式进行计数,这样能够满足基本使用。

除了RTC_PRL、RTC_ALR、RTC_CNT和RTC_DIV寄存器外,所有的系统寄存器都由系统复位或电源复位进行异步复位。
RTC_PRL、RTC_ALR、RTC_CNT和RTC_DIV寄存器仅能通过备份域复位信号复位。

 

中断

RTC 支持 3 个中断:

1. 秒中断:即经过分频器的时钟 TR_CLK 每次计数产生的中断

2. 溢出中断 : 计数器是 32bits 的,当计数器达到上限的时候产生的中断

3. 闹钟中断:当计数器的值达到配置的期望值 (RTC_ALR) 的时候产生闹钟

这三个中断均需要配置对应的中断使能位,进行使能后,方可使用。

 

访问限制

由于 RTC 处于 STM32 的备用区域(BKP),同时 RTC  的核心独立于 APB1,同时又是使用了 APB1 进行访问,此处,在访问 RTC 相关寄存器的时候,硬件有跨时钟域的同步操作,需要有几点注意的地方:

A. 访问 RTC 相关寄存器之前,首先需要开启 BKP 和 PWR 相关的时钟

B. 设置 PWR 的 DBP 位,使能对后备寄存器和RTC的访问

C. 若在读取 RTC 寄存器时,RTC 的 APB1 接口曾经处于禁止状态,则软件首先必须等待 RTC_CRL 寄存器中的 RSF 位(寄存器同步标志)被硬件置’1’。

D. 对RTC任何寄存器的写操作,都必须在前一次写操作结束后进行。可以通过查询RTC_CR寄存器中的RTOFF状态位,判断RTC寄存器是否处于更新中。仅当RTOFF状态位是’1’时,才可以写入RTC寄存器。

C. 使能 RTC 进入配置模式,在配置模式下进行寄存器更新,设置完后,退出配置模式

 

针对最后一项D,访问过程如下:

    D1. 查询RTOFF位,直到RTOFF的值变为’1’

    D2. 置 CNF 值为1,进入配置模式

    D3. 对一个或多个RTC寄存器进行写操作

    D4. 清除CNF标志位,退出配置模式

    D5. 查询RTOFF,直至RTOFF位变为’1’以确认写操作已经完成。

 

配置过程

1. 开启 PWR,BKP 区域的时钟

2. 写 PWR 的 DBP 位,使能后备区域的访问权限

3. Deinit RTC 模块

4. 配置 LSE 的时钟开启,并等待 LSE 稳定(32.768kHz)

5. 选择 RTC 的时钟为 LSE(注意,此部分不在 RTC 寄存器,故,无需满足”访问限制”章节的限制)

6. 使能 RTC

7. 由于是上电配置,故 APB1 之前出于禁止状态,故需要等待 RSF 置位

8. 等待 RTOFF 置位,即等待 RTC 出于 no busy

9. 设置分频系数为 32768 - 1,并等待 RTOFF 置位(等待写完成)

10. 配置使能秒中断,并等待 RTOFF 置位(等待写完成)

11. 初始化当前时间(这个可以随意设置)

12. 配置 NVIC

 

注意:为了使上电后,RTC 只被配置一次,这里使用了一个备用寄存器来作为 RTC 是否被配置过的标志(如果使用软件的一个变量,掉电后,变量的值会丢掉)。每次上电的时候进行 check, 如果配置过 RTC,则不再配置。

这里测试的时候,使用一个全局变量进行读数据,每次在秒中断中,将数据读出并解析。(因为寄存器中的计数器的单位是s秒)

当然,在中断处理程序 RTC_IRQHandler ,对 clear 中断 pending 的标志,也是需要等待写完成的 (RTOFF 置位)

代码如下:

#define LSE_CLK_32768KHZ     32768

#define RTC_CFG_DONE        0xAAAA

#define LEAP_YEAR_SEC       31622400

#define NORMAL_YEAR_SEC     31536000

#define A_DAY_SEC           86400

#define A_HOUR_SEC          3600

#define A_MIN_SEC           60

 

#define MAX_DAY             10

#define ONE_DAY_HOURS       24

#define ONE_HOUR_MIN        60

#define ONE_MIN_SEC         60

#define MAX_SEC             (MAX_DAY * ONE_DAY_HOURS * ONE_HOUR_MIN * ONE_MIN_SEC)

 

typedef struct {

    uint32_t day;

    uint32_t hour;

    uint32_t min;

    uint32_t sec;

} SK_TIME_t;

 

SK_TIME_t g_stCurrentTime;

 

static void _setCurrentTime(SK_TIME_t *cur_time)

{

    // Test RTC At 2018.7.13 -- 00:26:00

    RTC_SetCounter(0x00);

    RTC_WaitForLastTask();

}

 

void SK_getCurrentTime(SK_TIME_t *cur_time)

{

    uint32_t secCount = RTC_GetCounter();

    uint32_t sec = secCount % A_DAY_SEC;

 

    cur_time->day   = secCount / A_DAY_SEC ;

    cur_time->hour  = sec / 3600;

    cur_time->min   = (sec % 3600) / 60;

    cur_time->sec   = (sec % 3600) % 60;

}

 

static uint8_t SK_RTCIsConfiged(void)

{

    return ((BKP_ReadBackupRegister(BKP_DR1) == RTC_CFG_DONE) ? 1 : 0);

}

 

static void SK_RTCNVICConfig(void)

{

    NVIC_InitTypeDef NVIC_InitStructure;

    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); 

    NVIC_InitStructure.NVIC_IRQChannel = RTC_IRQn;

    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;

    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;

    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

    NVIC_Init(&NVIC_InitStructure);

}

 

static void SK_RTC_Configuration(void)

{

    // Step 1 : Open Power & Backup zone Clock

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);

 

    // Step 2 : Set Power register to allow to access backup domain

    PWR_BackupAccessCmd(ENABLE);

 

    // Step 3 : Rest BackUp domain

    BKP_DeInit();

 

    // Step 4 : Enable LSE Clock and wait for ready

    RCC_LSEConfig(RCC_LSE_ON);

    while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET);

 

    // Step 5 : Configure the LSE as RTC Clock input

    RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);

 

    // Step 6 : Enable RTC Clock

    RCC_RTCCLKCmd(ENABLE);

 

    // Step 7 : Because APB1 was reset when power on, follow datasheet, must wait RSF

    RTC_WaitForSynchro();

 

    // Step 8 : Wait until last write was finished

    RTC_WaitForLastTask();

 

    // Step 9 : Set prescaler as 32767

    // RTC period = RTCCLK/RTC_PR = (32.768 KHz)/(32767+1)

    RTC_SetPrescaler(LSE_CLK_32768KHZ - 1);

    RTC_WaitForLastTask();

 

    // Step 10 : Enable second interrupt

    RTC_ITConfig(RTC_IT_SEC, ENABLE);

    RTC_WaitForLastTask();

}

 

 

void SK_RTCInit(void)

{

    // First Configure RTC

    if (!SK_RTCIsConfiged())

    {

        SK_RTC_Configuration();

        _setCurrentTime(&stTestRTCTime);

        BKP_WriteBackupRegister(BKP_DR1, RTC_CFG_DONE);

    }

    else

    {

        RTC_WaitForSynchro();

        RTC_ITConfig(RTC_IT_SEC, ENABLE);

        RTC_WaitForLastTask();

    }

    SK_RTCNVICConfig();

    SK_getCurrentTime(&g_stCurrentTime);

}

 

void SK_RTCDeInit(void)

{

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);

    PWR_BackupAccessCmd(ENABLE);

    BKP_DeInit();

}

 

void RTC_IRQHandler(void)

{

    if (RTC_GetITStatus(RTC_IT_SEC) != RESET)

    {

        if (RTC_GetCounter() >= MAX_SEC)

        {

            RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);

            PWR_BackupAccessCmd(ENABLE);

            RTC_WaitForLastTask();

            RTC_SetCounter(0x0);

            RTC_WaitForLastTask();

        }

        SK_getCurrentTime(&g_stCurrentTime);

    }

    if(RTC_GetITStatus(RTC_IT_ALR)!= RESET)

    {

        RTC_ClearITPendingBit(RTC_IT_ALR);

    }

    RTC_ClearITPendingBit(RTC_IT_SEC | RTC_IT_OW);

    RTC_WaitForLastTask();

}

 


推荐阅读

史海拾趣

德力康(DLK)公司的发展小趣事

德力康(DLK)公司自1985年成立以来,最初专注于电视机用CRT插座的生产。凭借其卓越的产品质量和稳定的性能,逐渐在市场中树立了良好的口碑。随着电子行业的快速发展,DLK公司敏锐地捕捉到了连接器市场的巨大潜力,并开始逐步扩大产品线,涵盖D-SUB、DVI、HDMI、USB等多种连接器类型。通过不断的技术创新和市场拓展,DLK逐渐成为了国内连接器行业的重要供应商之一。

Fenghua (HK) Electronics Ltd公司的发展小趣事

人才是企业发展的重要保障。Fenghua (HK) Electronics Ltd高度重视人才队伍建设,通过校园招聘、社会招聘等多种渠道吸引优秀人才加入公司。公司提供了完善的培训体系和晋升机会,激发了员工的积极性和创造力。同时,公司注重营造良好的企业文化氛围,让员工在工作中感受到归属感和成就感。

Captive Fastener公司的发展小趣事

Captive Fastener公司在电子紧固件领域一直致力于技术创新。某年,公司研发团队成功开发出一种新型的自锁紧固件,这种紧固件能够在高振动环境下保持稳定的连接性能,极大地提高了电子设备的可靠性和耐久性。这一创新产品迅速获得了市场的认可,Captive Fastener公司因此获得了大量订单,实现了业务的快速增长。

EMLSI公司的发展小趣事

随着环保意识的不断提高,电子行业也开始面临环保压力。EMLSI公司积极响应环保号召,致力于开发绿色环保产品。公司投入大量资源进行环保技术研发,成功推出了一系列低能耗、低排放的电子产品。这些产品不仅满足了客户的需求,也为公司赢得了良好的社会声誉。

Gespac Inc公司的发展小趣事

随着电子行业的快速发展,市场竞争也日益激烈。为了保持领先地位,EMLSI公司开始实施全球化战略。公司先后在亚洲、欧洲和北美等地建立了生产基地和研发中心,与当地企业建立了紧密的合作关系。这一战略不仅让EMLSI能够更快地了解市场需求和技术趋势,还为公司带来了更多的商业机会和合作伙伴。

动运科技(DONGWOON)公司的发展小趣事

在电子行业中,技术创新是企业持续发展的关键。动运科技始终将技术创新作为公司的核心竞争力,不断投入研发资源,推动产品升级和技术创新。近年来,公司在自动对焦和光学防抖技术方面取得了重大突破,成功应用于音圈马达驱动芯片中,为智能手机等设备的摄像头模组提供了更加稳定、清晰的成像效果。这一技术的成功应用,不仅提升了动运科技在业界的地位,也为公司带来了广阔的市场前景。

问答坊 | AI 解惑

[资料] HSPICE最新中文使用手册

HSPICE最新中文教程,希望对大家有帮助!…

查看全部问答>

关于dm642 D1 转 Cif的问题

关于dm642 D1 转 Cif的问题 请教高手, 我采集出来的是pal制式 的 D1(720*576)的图像,  但是要做图像处理, 所以想要缩小成Cif的, 请问怎么做啊,我在网上看到说抽出来其中的一部分, 具体怎么做呢,请高手指点!!!…

查看全部问答>

定时器0的中断和串口中断不能同时工作么?

include #include #define _MHZ_ 11    sbit LED1=P0^0; sbit LED2=P0^1; sbit P0_2=P0^2; sbit P0_3=P0^3; sbit K1=P0^4; sbit K2=P0^5; sbit P0_6=P0^6; sbit P0_7=P0^7; sbit LED3=P1^0; sbit LED4=P1^1; sbi ...…

查看全部问答>

LPC114申请

很好奇!:Q :Q…

查看全部问答>

请教STM32的I2C通讯问题

大家好,我最近在弄一个小东西,是用stm32的I2C和一个射频芯片的I2C通讯,主要目的是往我的射频芯片里面写数据。前几天我在网上找到了st的固件库和demo,结果一直没有调通,后来上网看到它的升级版,下下来跑了它的双地址通讯那个demo,跑通了 ...…

查看全部问答>

发本开关电源设计方面的书

这本书在原理性、实践性方面都不错,学习开关电源设计的网友不妨看书学习,包括做逆变器也一样有参考价值,掌握理论自会知道如何实施。     …

查看全部问答>

修改NWK_MAX_DEVICE_LIST定义为70,编译无法通过。请求解决办法。

修改NWK_MAX_DEVICE_LIST定义为70,编译无法通过。请求解决办法。我知道是由于内存够请问有什么办法可以修改?我目前的设置如下…

查看全部问答>

求助

ADC12中的增益误差只会使精度下降,而不会使编码丢失是吗? 如果是,能解释下吗?  …

查看全部问答>

MSP430英文书籍推荐一、Embedded Systems Design using the TI msp430 Series

MSP430英文书籍推荐一、Embedded Systems Design using the TI msp430 Series https://download.eeworld.com.cn/detail/tiankai001/8744 …

查看全部问答>