此次做一个项目出现了一个令我很不解的问题,就是RTC时钟,代码是提前写好的,当时是用的STM32F103ZET6最小系统板,所有功能都是没有问题的。但是最终我画好的PCB芯片用的是STM32F103C8T6为了节省成本嘛,经过不断的移植和调试过后,到了RTC这一步,外部低速晶振怎么都不起振,由于没有示波器没法测试晶振是否起振,我只能用万用表来测,测不出来啥,但是使用外部高速晶振就没有问题,当时以为是代码写错了,但是经过不断的检查发现没问题,难道是晶振坏了?于是就买的一个新的晶振,等换好晶振好发现还是不行,用debug调试的时候发现代码死在了 while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET)这里,真是见鬼了,一头雾水啊,最后在网上查找,说电容问题,最后我又换了电容,还是不行,最后我发现我的电路图好像有问题,PC15和PC14这两个引脚连接了晶振
但是PC15被我用到ESP8266的复位引脚上了,然后我修改代码,直接把ESP8266的代码给屏蔽了,经过测试发现还是不行,还是死在了RTC初始化,最后我把代码下载到了其他的板子上,发现RTC正常了,看来我的最小系统也是没问题的,到这里我就明白了,好家伙PC15引脚就算你不去用不初始化它,也不能接其他的模块引脚,如果接了,晶振就会不起振。我不知到这个引脚是不是可以用其他的办法可以使用。
另外你改了时间发现没有修改成功,这时你把 BKP_ReadBackupRegister(BKP_DR1)这个改成不一致就可以了,仔细看代码就明白了
RTC代码
#include "rtc.h"
#include "sys.h"
#include "stm32f10x.h"
#include "usart.h"
#include "delay.h"
_calendar_obj calendar; //时钟结构体
static void RTC_NVIC_Config(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);
}
u8 RTC_initConfig(void)
{
//检查是不是第一次配置时钟
u8 temp=0;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE); //使能PWR和BKP外设时钟
PWR_BackupAccessCmd(ENABLE); //使能后备寄存器访问
if (BKP_ReadBackupRegister(BKP_DR1) != 0x5053) //从指定的后备寄存器中读出数据:读出了与写入的指定数据不相乎
{
BKP_DeInit(); //复位备份区域
RCC_LSEConfig(RCC_LSE_ON); //设置外部低速晶振(LSE),使用外设低速晶振
while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET && temp<250) //检查指定的RCC标志位设置与否,等待低速晶振就绪
{
temp++;
delay_ms(10);
}
if(temp>=250)return 1;//初始化时钟失败,晶振有问题
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); //设置RTC时钟(RTCCLK),选择LSE作为RTC时钟
RCC_RTCCLKCmd(ENABLE); //使能RTC时钟
RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
RTC_WaitForSynchro(); //等待RTC寄存器同步
RTC_ITConfig(RTC_IT_SEC, ENABLE); //使能RTC秒中断
RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
RTC_EnterConfigMode();/// 允许配置
RTC_SetPrescaler(32767); //设置RTC预分频的值
RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
RTC_Set(2023,4,4,9,14,55); //设置时间
RTC_ExitConfigMode(); //退出配置模式
BKP_WriteBackupRegister(BKP_DR1, 0X5050); //向指定的后备寄存器中写入用户程序数据
}
else//系统继续计时
{
RTC_WaitForSynchro(); //等待最近一次对RTC寄存器的写操作完成
RTC_ITConfig(RTC_IT_SEC, ENABLE); //使能RTC秒中断
RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
}
RTC_NVIC_Config();//RCT中断分组设置
RTC_Get();//更新时间
return 0; //ok
}
// RTC时钟中断
//每秒触发一次
// extern u16 tcnt;
void RTC_IRQHandler(void)
{
if (RTC_GetITStatus(RTC_IT_SEC) != RESET) //秒钟中断
{
RTC_Get(); //更新时间
}
if (RTC_GetITStatus(RTC_IT_ALR) != RESET) //闹钟中断
{
RTC_ClearITPendingBit(RTC_IT_ALR); //清闹钟中断
RTC_Get(); //更新时间
printf("Alarm Time:%d-%d-%d %d:%d:%d\n", calendar.w_year, calendar.w_month, calendar.w_date, calendar.hour, calendar.min, calendar.sec); //输出闹铃时间
}
RTC_ClearITPendingBit(RTC_IT_SEC | RTC_IT_OW); //清闹钟中断
RTC_WaitForLastTask();
}
//判断是否是闰年函数
//月份 1 2 3 4 5 6 7 8 9 10 11 12
//闰年 31 29 31 30 31 30 31 31 30 31 30 31
//非闰年 31 28 31 30 31 30 31 31 30 31 30 31
//输入:年份
//输出:该年份是不是闰年.1,是.0,不是
u8 Is_Leap_Year(u16 year)
{
if (year % 4 == 0) //必须能被4整除
{
if (year % 100 == 0)
{
if (year % 400 == 0)
return 1; //如果以00结尾,还要能被400整除
else
return 0;
}
else
return 1;
}
else
return 0;
}
//设置时钟
//把输入的时钟转换为秒钟
//以1970年1月1日为基准
// 1970~2099年为合法年份
//返回值:0,成功;其他:错误代码.
//月份数据表
u8 const table_week[12] = {0, 3, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5}; //月修正数据表
//平年的月份日期表
const u8 mon_table[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
u8 RTC_Set(u16 syear, u8 smon, u8 sday, u8 hour, u8 min, u8 sec)
{
u16 t;
u32 seccount = 0;
if (syear < 1970 || syear > 2099)
return 1;
for (t = 1970; t < syear; t++) //把所有年份的秒钟相加
{
if (Is_Leap_Year(t))
seccount += 31622400; //闰年的秒钟数
else
seccount += 31536000; //平年的秒钟数
}
smon -= 1;
for (t = 0; t < smon; t++) //把前面月份的秒钟数相加
{
seccount += (u32)mon_table[t] * 86400; //月份秒钟数相加
if (Is_Leap_Year(syear) && t == 1)
seccount += 86400; //闰年2月份增加一天的秒钟数
}
seccount += (u32)(sday - 1) * 86400; //把前面日期的秒钟数相加
seccount += (u32)hour * 3600; //小时秒钟数
seccount += (u32)min * 60; //分钟秒钟数
seccount += sec; //最后的秒钟加上去
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE); //使能PWR和BKP外设时钟
PWR_BackupAccessCmd(ENABLE); //使能RTC和后备寄存器访问
RTC_SetCounter(seccount); //设置RTC计数器的值
RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
return 0;
}
//初始化闹钟
//以1970年1月1日为基准
// 1970~2099年为合法年份
// syear,smon,sday,hour,min,sec:闹钟的年月日时分秒
//返回值:0,成功;其他:错误代码.
u8 RTC_Alarm_Set(u16 syear, u8 smon, u8 sday, u8 hour, u8 min, u8 sec)
{
u16 t;
u32 seccount = 0;
if (syear < 1970 || syear > 2099)
return 1;
for (t = 1970; t < syear; t++) //把所有年份的秒钟相加
{
if (Is_Leap_Year(t))
seccount += 31622400; //闰年的秒钟数
else
seccount += 31536000; //平年的秒钟数
}
smon -= 1;
for (t = 0; t < smon; t++) //把前面月份的秒钟数相加
{
seccount += (u32)mon_table[t] * 86400; //月份秒钟数相加
if (Is_Leap_Year(syear) && t == 1)
seccount += 86400; //闰年2月份增加一天的秒钟数
}
seccount += (u32)(sday - 1) * 86400; //把前面日期的秒钟数相加
seccount += (u32)hour * 3600; //小时秒钟数
seccount += (u32)min * 60; //分钟秒钟数
seccount += sec; //最后的秒钟加上去
//设置时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE); //使能PWR和BKP外设时钟
PWR_BackupAccessCmd(ENABLE); //使能后备寄存器访问
//上面三步是必须的!
RTC_SetAlarm(seccount);
RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
return 0;
}
//得到当前的时间
//返回值:0,成功;其他:错误代码.
u8 RTC_Get(void)
{
static u16 daycnt = 0;
u32 timecount = 0;
u32 temp = 0;
u16 temp1 = 0;
timecount = RTC_GetCounter();
temp = timecount / 86400; //得到天数(秒钟数对应的)
if (daycnt != temp) //超过一天了
{
daycnt = temp;
temp1 = 1970; //从1970年开始
while (temp >= 365)
{
if (Is_Leap_Year(temp1)) //是闰年
{
if (temp >= 366)
temp -= 366; //闰年的秒钟数
else
{
temp1++;
break;
}
}
else
temp -= 365; //平年
temp1++;
}
calendar.w_year = temp1; //得到年份
temp1 = 0;
while (temp >= 28) //超过了一个月
{
if (Is_Leap_Year(calendar.w_year) && temp1 == 1) //当年是不是闰年/2月份
{
if (temp >= 29)
temp -= 29; //闰年的秒钟数
else
break;
}
else
{
if (temp >= mon_table[temp1])
temp -= mon_table[temp1]; //平年
else
break;
}
temp1++;
}
calendar.w_month = temp1 + 1; //得到月份
calendar.w_date = temp + 1; //得到日期
}
temp = timecount % 86400; //得到秒钟数
calendar.hour = temp / 3600; //小时
calendar.min = (temp % 3600) / 60; //分钟
calendar.sec = (temp % 3600) % 60; //秒钟
calendar.week = RTC_Get_Week(calendar.w_year, calendar.w_month, calendar.w_date); //获取星期
return 0;
}
//获得现在是星期几
//功能描述:输入公历日期得到星期(只允许1901-2099年)
//输入参数:公历年月日
//返回值:星期号
u8 RTC_Get_Week(u16 year, u8 month, u8 day)
{
u16 temp2;
u8 yearH, yearL;
yearH = year / 100;
yearL = year % 100;
// 如果为21世纪,年份数加100
if (yearH > 19)
yearL += 100;
// 所过闰年数只算1900年之后的
temp2 = yearL + yearL / 4;
temp2 = temp2 % 7;
temp2 = temp2 + day + table_week[month - 1];
if (yearL % 4 == 0 && month < 3)
temp2--;
return (temp2 % 7);
}