RTCCLK 一般信号频率一般为32.768kHz
RTC预分频器通过计数实现对输入信号的分频,RTC_DIV实际是一个计数器,RTC_PRL用于设置预分频值( 分频倍数 = RTC_PRL+1 )
RTC_CNT中存放的是UNIX时间戳的秒数
RTC_ALR是RTC的闹钟功能,当RTC_CNT=RTC_ALR时,触发RTC_Alarm中断。若配置了RTC_Alarm中断服务,可在中断服务函数中执行需要的操作
RTC_Overflow但RCT_CNT溢出时会触发该中断,一般不会触发
RTC_Second, 每秒中断(具体时间由输入信号频率与预分频系数配置决定)
值得注意的是: 晶振本身就有百万分之几十的误差(ppm), 因此电容相差不太的情况, 所造成的误差可能也就几百万分之一
电容对频率的影响: 电容越大频率越低,反之越高
这里是一个UP对晶振电容大小和频率的测试视频 【晶振的负载电容到底怎么选择?】
执行以下操作将使能对BKP和RTC的访问:
设置RCC_APB1ENR的PWREN和BKPEN,使能PWR和BKP时钟
设置PWR_CR的DBP,使能对BKP和RTC的访问
若在读取RTC寄存器时,RTC的APB1接口曾经处于禁止状态,则软件首先必须等待RTC_CRL寄存器中的RSF位(寄存器同步标志)被硬件置1 (等待同步)
必须设置RTC_CRL寄存器中的CNF位,使RTC进入配置模式后,才能写入RTC_PRL、RTC_CNT、RTC_ALR寄存器 (进入配置模式, 无需软件操作 )
对RTC任何寄存器的写操作,都必须在前一次写操作结束后进行。可以通过查询RTC_CR寄存器中的RTOFF状态位,判断RTC寄存器是否处于更新中。仅当RTOFF状态位是1时,才可以写入RTC寄存器 (等待写入)
#include "stm32f10x.h"
#include "delay.h"
#include "OLED.h"
int main(void)
{
OLED_Init();
// RCC
RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP, ENABLE); // 使能BKP
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE); // 使能PWR
PWR_BackupAccessCmd(ENABLE); // 配置DBP
BKP_WriteBackupRegister(BKP_DR1, 0x01); // 写入数据 中容量 DR1-DR10 第二次注释该行
OLED_ShowHexNum(1, 1, BKP_ReadBackupRegister(BKP_DR1), 4); // 读BKP DR寄存器
// 在提供备用电源的情况下,断电和复位不会情况BKP里的数据
while(1)
{
}
}
PWR_BackupAccessCmd(ENABLE)
实际配置了CR寄存器的DBP位/**
* @brief Enables or disables access to the RTC and backup registers.
* @param NewState: new state of the access to the RTC and backup registers.
* This parameter can be: ENABLE or DISABLE.
* @retval None
*/
void PWR_BackupAccessCmd(FunctionalState NewState)
{
/* Check the parameters */
assert_param(IS_FUNCTIONAL_STATE(NewState));
*(__IO uint32_t *) CR_DBP_BB = (uint32_t)NewState;
}
#include "stm32f10x.h"
#include
uint16_t MyRTC_Time[] = {2023, 9, 18, 20, 40, 58};
/**
* @brief 设置时间
*/
void MyRTC_SetTime(void)
{
time_t time_cnt;
struct tm time_date;
time_date.tm_year = MyRTC_Time[0] - 1900; // 年
time_date.tm_mon = MyRTC_Time[1] - 1; // 月
time_date.tm_mday = MyRTC_Time[2]; // 日
time_date.tm_hour = MyRTC_Time[3]; // 时
time_date.tm_min = MyRTC_Time[4]; // 分
time_date.tm_sec = MyRTC_Time[5]; // 秒
time_cnt = mktime(&time_date) - 8 * 60 * 60; // 转换为时间戳 减去东八偏移
RTC_SetCounter(time_cnt); // 将数据写入RTC的CNT
RTC_WaitForLastTask(); // 等待写入完成
}
/**
* @brief 读取时间
*/
void MyRTC_ReadTime(void)
{
time_t time_cnt;
struct tm time_date;
time_cnt = RTC_GetCounter() + 8 * 60 * 60; // 加上东八区偏移
time_date = *localtime(&time_cnt);
MyRTC_Time[0] = time_date.tm_year + 1900; // 年
MyRTC_Time[1] = time_date.tm_mon + 1;// 月
MyRTC_Time[2] = time_date.tm_mday; // 日
MyRTC_Time[3] = time_date.tm_hour; // 时
MyRTC_Time[4] = time_date.tm_min ; // 分
MyRTC_Time[5] = time_date.tm_sec ; // 秒
}
/**
* @brief 初始化RTC
*/
void MyRTC_Init(void)
{
// RCC
RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
PWR_BackupAccessCmd(ENABLE); // 设置CR寄存器的DBP位
if(BKP_ReadBackupRegister(BKP_DR1) != 0xA5A5) // 避免时间重复写入
{
RCC_LSEConfig(RCC_LSE_ON); // 开启LSE时钟 外接32.768khz晶振 LSE默认不上电
while(RCC_GetFlagStatus(RCC_FLAG_LSERDY)==RESET); // 等待LSE起振
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); // 设置RTC时钟源为LSE
RCC_RTCCLKCmd(ENABLE); // 使能时钟
RTC_WaitForSynchro(); // 等待同步
RTC_WaitForLastTask(); // 等待写入完成
RTC_SetPrescaler(32768-1); // 设置分频系数
RTC_WaitForLastTask();
MyRTC_SetTime(); // 设置时间
BKP_WriteBackupRegister(BKP_DR1, 0xA5A5);
}
else
{
RTC_WaitForSynchro(); // 等待同步
RTC_WaitForLastTask(); // 等待写入完成
}
}
#ifndef __MYRTC_H
#define __MYRTC_H
extern uint16_t MyRTC_Time[];
void MyRTC_Init(void);
void MyRTC_SetTime(void);
void MyRTC_ReadTime(void);
void MyRTC_Init(void);
#endif
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "MyRTC.h"
int main(void)
{
OLED_Init();
MyRTC_Init();
OLED_ShowString(1, 1, "Date:XXXX-XX-XX");
OLED_ShowString(2, 1, "Time:XX:XX:XX");
OLED_ShowString(3, 1, "CNT :");
OLED_ShowString(4, 1, "DIV :");
while (1)
{
MyRTC_ReadTime();
OLED_ShowNum(1, 6, MyRTC_Time[0], 4);
OLED_ShowNum(1, 11, MyRTC_Time[1], 2);
OLED_ShowNum(1, 14, MyRTC_Time[2], 2);
OLED_ShowNum(2, 6, MyRTC_Time[3], 2);
OLED_ShowNum(2, 9, MyRTC_Time[4], 2);
OLED_ShowNum(2, 12, MyRTC_Time[5], 2);
OLED_ShowNum(3, 6, RTC_GetCounter(), 10);
OLED_ShowNum(4, 6, RTC_GetDivider(), 10); // 余数寄存器
}
}
STM32F10xxx参考手册(中文).pdf
之前代码在RTC配置分配系数用的37268-1(敲错了),正确分频应该是32768-1。自己焊了个RTC的电路,发现比正常一分慢了8s,一直觉得是硬件问题,换了3组电容,都是稳定慢八秒,然后发现是程序分频这里敲错了。2023/10/13