Files
2026-08-25 17:30:40 +08:00

794 lines
20 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
******************************************************************************
* @file gpio.c
* @author Jerry Cai
* @version V2.1
* @date 19-April-2022
* @brief gpio program body.
******************************************************************************
* @attention
*
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "stm32f10x.h"
#include "rtc.h"
#include "global.h"
#define sleepEnd 60*(paraMem.sleep_min_disable&0x7FFF) //休眠1等待时长min,单位1s
#define sleep2End 60*(paraMem.sleep2_min_disable&0x7FFF) //休眠2等待时长min,单位1s
#define sleep2Vol paraMem.sleep2_vol //休眠方案2对应休眠电压,单位1mV
#define uvoffEnd 300 //强制欠压复位等待时长5min,单位1s
#define ocvEnd 60*(paraMem.ocv_min_disable&0x7FFF) //开路电压校准等待时长min,单位1s
#define fcccaliEnd 60*(paraMem.cali_min_disable&0x7FFF) //校准满充容量等待时长min,单位1s
_calendar_obj calendar; //时钟结构体
_calendar_obj calendar_WRITE;
_calendar_obj calendar_BACKUP;
uint8_t const table_week[12]={0,3,3,6,1,4,6,2,5,0,3,5}; //平年的月修正数据表(基准是1900年1月1日),闰年其他相同只是1.2月要-1
const u8 mon_table[12]={31,28,31,30,31,30,31,31,30,31,30,31}; //平年的月份日期表.闰年的2月有29天
uint32_t timecount; //当前计时s
uint32_t oldtimecnt; //上一秒的计时s
uint8_t LSEErrFlag; //外部低频晶振有问题的标志,需要让屏幕的时间不再显示
uint8_t LSEErrCount;//时间不走的计数,满足了会把时间清零,标志置1
uint8_t sleep_flag; //休眠执行标志位
uint8_t sleep_enableflag;//开启休眠功能标志位
uint32_t sleeptimecount; //休眠1的计时起点s
uint32_t sleeptime; //休眠启动的倒计时数
uint32_t sleep2timecount; //休眠2的计时起点s
uint32_t sleep2time; //休眠2启动的倒计时数
uint32_t uvofftimecount; //关闭欠压功能后的计时起点
uint16_t uvofftime; //关闭欠压功能的倒计时数(会在屏幕显示故只设uint16)
uint32_t ocvtimecount; //开路电压法的计时起点
uint32_t ocvtime; //开路电压法的倒计时数
uint32_t fcc_Calitimecount; //校准满充容量的计时起点
uint32_t fcc_Calitime; //校准满充容量的倒计时数,超过则不可更新
uint8_t RTC_UpdateFlag; //起始点刷新标志位,用以刷新休眠和欠压强制复位的时间
/*
* 函数名:Is_Leap_Year
* 描述 :判断是否为闰年
月份 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,不是
* 调用
*/
uint8_t Is_Leap_Year(uint8_t yed)
{
uint8_t year;
year=2000+yed;
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;
}
}
/*
* 函数名:RTC_Get_Week
* 描述 :输入公历日期得到星期
* 输入 :公历年月日
* 输出 :星期号
* 调用
*/
uint8_t RTC_Get_Week(uint8_t ye, uint8_t month, uint8_t day)
{
uint16_t temp2;
uint16_t year;
uint8_t yearH,yearL;
ye=ye/16*10+ye%16; //十六进制转十进制
month=month/16*10+month%16;
day=day/16*10+day%16;
year=2000+ye; //(输入格式决定了必定是21世纪)
yearH=year/100;//用于判断是21世纪
yearL=year%100;//用于计算多的年数
//计算自1900年1月1日以来积累的多出来的天数
temp2=yearL+yearL/4; //平年365%7=1 闰年366%7=2 (自动把可以略去的7的倍数略掉了)(不用计算/100和/400)
temp2=temp2%7;
temp2=temp2+day+table_week[month-1];
// 21世纪需要加6(因为1900年1月1日是星期一,2000年1月1日是星期六)
if(yearH == 20)
{
temp2+=6;
}
//若日期是闰年的1.2月,星期修正要-1
if(yearL%4==0&&month<3)
{
temp2--;
}
return(temp2%7!=0)?temp2%7:7; //周一到周日=1~7
}
/*
* 函数名:RTC_Get
* 描述 :根据RTC计算器值计算当前年/月/日/时/分/秒/星期放入calendar结构体
* 输入 :无
* 输出 :0,成功;其他:错误代码
* 调用
*/
uint8_t RTC_Get(void)
{
static uint16_t daycnt=0;
uint32_t temp=0;
uint16_t temp1=0;
uint16_t tempppy;
timecount=RTC_GetCounter();
/*时分秒的计算*/
#if LTE_Conn
temp=(timecount+28800)/86400; //得到(总秒钟数对应的)天数 //在计算年月日时,中国时区-8h
#else
temp=timecount/86400; //得到(总秒钟数对应的)天数
#endif
if(daycnt!=temp)//超过一天了
{
daycnt=temp;
temp1=1970; //计算年份,从1970年开始
while(temp>=365)
{
if(Is_Leap_Year(temp1))//闰年-366
{
if(temp>=366)temp-=366;
else {temp1++;break;}
}
else temp-=365; //平年-365
temp1++;
}
tempppy=temp1;
temp1=0; //计算月份,最后的temp就是日期
while(temp>=28)
{
if(Is_Leap_Year(tempppy)&&temp1==1)//闰年且是2月份-29
{
if(temp>=29)temp-=29;
else break;
}
else
{
if(temp>=mon_table[temp1])temp-=mon_table[temp1];//其他都按表上来
else break;
}
temp1++;
}
tempppy=tempppy-2000;//得到年份
calendar.w_year =(tempppy/10)*16+(tempppy%10);
temp1=temp1+1; //得到月份
calendar.w_month=(temp1/10)*16+(temp1%10);
temp=temp+1; //得到日期
calendar.w_date=(temp/10)*16+(temp%10);
}
#if LTE_Conn
temp=(timecount+28800)%86400; //得到(去掉天数后 当天的)秒钟数 //在计算年月日时,中国时区-8h
#else
temp=timecount%86400; //得到(去掉天数后 当天的)秒钟数
#endif
tempppy=temp/3600; //得到小时
calendar.hour=(tempppy/10)*16+(tempppy%10);
tempppy=(temp%3600)/60;//得到分钟
calendar.min=(tempppy/10)*16+(tempppy%10);
tempppy=(temp%3600)%60;//得到秒钟
calendar.sec=(tempppy/10)*16+(tempppy%10);
calendar.week=RTC_Get_Week(calendar.w_year,calendar.w_month,calendar.w_date);//获取星期
/*判断合法*/
//在初始化后的第一次调用时,oldtimecnt还未赋值,此时将合法的当前时间写入EEPROM
if(oldtimecnt==0)
{
oldtimecnt = timecount;
if(calendar.w_month != 0)
{
uint8_t tmpRd[6];
tmpRd[0] = calendar.w_year;
tmpRd[1] = calendar.w_month;
tmpRd[2] = calendar.w_date;
tmpRd[3] = calendar.hour;
tmpRd[4] = calendar.min;
tmpRd[5] = calendar.sec;
EEPROM_WrMulByte(EE_TIME_BACKUP,tmpRd);
delay_ms(5);
}
}
//当时钟一直不走,判定有问题
if(timecount==oldtimecnt)
{
LSEErrCount++;
if(LSEErrCount>10)
{
LSEErrFlag=1;
sleep_Moni_Count=SLEEP_MON_CNT;
sleep2_Moni_Count = SLEEP2_MON_CNT;
calendar.w_year = 0; //结构体清零,方便此时的报警记录计入的时间为全0无效值
calendar.w_month = 0;
calendar.w_date = 0;
calendar.week = 0;
calendar.hour = 0;
calendar.min = 0;
calendar.sec = 0;
return 1; //数据异常,返回1
}
}
else
{
LSEErrCount=0;
oldtimecnt = timecount;
}
//休眠功能-RTC,比较当前秒与开始秒的差别来定时
//方案1
if((sleep_flag == 0) && ((sleep_enableflag & 0x01) != 0)) //未进入休眠+启用休眠方案1
{
if(sleeptimecount != 0)
{
if(RTC_UpdateFlag == 1)
{
sleeptimecount = timecount - (sleepEnd - sleeptime); //更新起始点=当前时间-已消耗时间,此时剩余时间保持原值
}
else
{
sleeptime = sleepEnd - (timecount - sleeptimecount); //更新剩余时间
}
if((sleeptime == 0) || (sleeptime > sleepEnd))
{
sleep_flag = 1;
#if LTE_Conn
pre_sleep_flag = 2;
pre_sleep_waitCnt = 0;
sleepOn_time=timecount;
#endif
}
}
else
{
sleeptimecount = timecount;
}
}
//方案2
if((sleep_flag == 0) && ((sleep_enableflag & 0x02) != 0)) //未进入休眠+启用休眠方案2
{
//满足休眠电压,无充电电流
if((cellVoltageMin <= sleep2Vol) && (bmsMem.packCurrent < 500))
{
if(sleep2timecount != 0)
{
if(RTC_UpdateFlag == 1)
{
sleep2timecount = timecount - (sleep2End - sleep2time); //更新起始点=当前时间-已消耗时间,此时剩余时间保持原值
}
else
{
sleep2time = sleep2End - (timecount - sleep2timecount); //更新剩余时间
}
if((sleep2time == 0) || (sleep2time > sleep2End))
{
#if Key_PressLong
power_old = 4; //写入关机
power_state = 0;
#else
sleep_flag = 1;
#if LTE_Conn
pre_sleep_flag = 2;
pre_sleep_waitCnt = 0;
sleepOn_time=timecount;
#endif
#endif
}
}
else
{
sleep2timecount = timecount;
}
}
//否则停止计时
else
{
sleep2timecount = 0;
}
}
//不启用任何休眠方案
if(sleep_enableflag == 0)
{
sleep_flag = 0;
}
//屏幕手动关欠压启动了
if((bmsMem.balanceStatus & 0x20) != 0)
{
if(RTC_UpdateFlag == 1)
{
uvofftimecount = timecount - (uvoffEnd - uvofftime); //更新起始点=当前时间-已消耗时间,此时剩余时间保持原值
}
else
{
uvofftime = uvoffEnd - (timecount - uvofftimecount);
}
if((uvofftime == 0) || (uvofftime > uvoffEnd)) //防止反向溢出
{
bmsMem.balanceStatus &= 0xffdf;
}
}
//开路电压法校准SOC启动了
if(OCV_Wait_flag == 1)
{
if(RTC_UpdateFlag == 1)
{
ocvtimecount = timecount - (ocvEnd - ocvtime); //更新起始点=当前时间-已消耗时间,此时剩余时间保持原值
}
else
{
ocvtime = ocvEnd - (timecount - ocvtimecount);
}
if((ocvtime == 0) || (ocvtime > ocvEnd)) //防止反向溢出
{
OCV_CaliSOC_flag = 1;
}
}
//充电校准满充容量的倒计时启动了
if(fcc_CaliStartFlag == 1)
{
if(RTC_UpdateFlag == 1)
{
fcc_Calitimecount = timecount - (fcccaliEnd - fcc_Calitime); //更新起始点=当前时间-已消耗时间,此时剩余时间保持原值
}
else
{
fcc_Calitime = fcccaliEnd - (timecount - fcc_Calitimecount);
}
if((fcc_Calitime == 0) || (fcc_Calitime > fcccaliEnd)) //防止反向溢出
{
fcc_CaliStartFlag = 0;
EEPROM_WrMulByte(EE_FCC_TIME,ClearEE);
delay_ms(5);
}
}
//该刷新的都刷新后,标志置0
if(RTC_UpdateFlag != 0)
{
RTC_UpdateFlag = 0;
}
return 0;
}
/*
* 函数名:RTC_Set
* 描述 :把输入的年/月/日/时/分/秒转换为秒钟 写入RTC计数器
以1970年1月1日为基准
* 输入 :无
* 输出 :0,成功;1:错误代码
* 调用
*/
uint8_t RTC_Set(uint8_t ear,uint8_t smon,uint8_t sday,uint8_t hour,uint8_t min,uint8_t sec)
{
uint16_t t,syear;
uint32_t seccount=0;
ear=(ear/16)*10+ear%16;
smon=(smon/16)*10+smon%16;
sday=(sday/16)*10+sday%16;
hour=(hour/16)*10+hour%16;
min=(min/16)*10+min%16;
sec=(sec/16)*10+sec%16;
syear=2000+ear;
if(syear<1970||syear>2099) //1970~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; //最后的秒钟加上去
#if LTE_Conn
//4G的timecount是世界时间,中国对应显示需+8h,存储就要比电脑写入的少8h=28800s
seccount -= 28800;
#endif
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE); //使能PWR和BKP外设时钟
PWR_BackupAccessCmd(ENABLE); //使能RTC和后备寄存器访问
RTC_SetCounter(seccount); //设置RTC计数器的值
RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
//RTC_Get();
return 0;
}
/*
* 函数名:RTC_GetSynchro
* 描述 :由函数RTC_WaitForSynchro()改得,增加超时判断说明晶振出现问题
* 输入 :无
* 输出 :0,成功;1:错误代码
* 调用
*/
uint8_t RTC_GetSynchro(void)
{
uint16_t temp=0;
/* Clear RSF flag */
RTC->CRL &= (uint16_t)~RTC_FLAG_RSF;
/* Loop until RSF flag is set */
while ((RTC->CRL & RTC_FLAG_RSF) == (uint16_t)RESET)
{
temp++;
if(temp>=2000) return 1; //初始化时钟失败,晶振有问题
delay_ms(1); //1ms延时,总共2s
}
return 0;
}
/*
* 函数名:uf_RTC_Init
* 描述 :RTC初始化配置.第一次配置时会写入2023-6-25 14:00:00.
* 输入 :无
* 输出 :0,成功;1:错误代码
* 调用
*/
//开机执行一次
//第一次配置写入2023-6-25 14:00:00,此后正常读RTC后备寄存器的存值。若RTC值意外丢失则读EEPROM存值使用
uint8_t uf_RTC_Init(void)
{
uint16_t temp=0;
uint8_t tmpRd[6];
//上电读备份的时间信息
EEPROM_RdMulByte(EE_TIME_BACKUP,tmpRd);
if((tmpRd[0]!=0xff) && (tmpRd[1]!=0xff) && (tmpRd[2]!=0xff) && (tmpRd[3]!=0xff) && (tmpRd[4]!=0xff) && (tmpRd[5]!=0xff)) //时分秒格式存储,不可能存在0xff
{
calendar_BACKUP.w_year = tmpRd[0];
calendar_BACKUP.w_month = tmpRd[1];
calendar_BACKUP.w_date = tmpRd[2];
calendar_BACKUP.hour = tmpRd[3];
calendar_BACKUP.min = tmpRd[4];
calendar_BACKUP.sec = tmpRd[5];
}
else
{
//0x23,0x06,0x25,0x14,0x00,0x00
calendar_BACKUP.w_year = 0x23;
calendar_BACKUP.w_month = 0x06;
calendar_BACKUP.w_date = 0x25;
calendar_BACKUP.hour = 0x14;
calendar_BACKUP.min = 0x00;
calendar_BACKUP.sec = 0x00;
}
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE); //使能PWR和BKP外设时钟
PWR_BackupAccessCmd(ENABLE); //使能后备寄存器访问
if(BKP_ReadBackupRegister(BKP_DR1) != 0x5050) //检查是不是第一次配置时钟
{
// /*使用内部低速晶振*/
// BKP_DeInit(); //复位备份区域
// RCC_LSICmd(ENABLE); //使能LSI时钟
// while (RCC_GetFlagStatus(RCC_FLAG_LSIRDY) == RESET) //等待LSI晶振就绪
// {
// temp++;
// if(temp>=5000) return 1; //持续5s无法起振,初始化时钟失败,晶振有问题
// delay_ms(1); // 1ms延时
// }
// RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI); //设置RTC时钟(RTCCLK),选择LSI作为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(40000 - 1); //设置RTC预分频的值40kHz
// RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
// /*使用内部低速晶振*/
/*使用外部低速晶振*/
BKP_DeInit(); //复位备份区域
RCC_LSEConfig(RCC_LSE_ON); //设置外部低速晶振(LSE),使用外设低速晶振
while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET) //检查指定的RCC标志位设置与否,等待低速晶振就绪
{
temp++;
if(temp>=5000) return 1; //持续5s无法起振,初始化时钟失败,晶振有问题
delay_ms(1); // 1ms延时
}
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(0x23,0x06,0x25,0x14,0x00,0x00);
RTC_Set(calendar_BACKUP.w_year,calendar_BACKUP.w_month,calendar_BACKUP.w_date,calendar_BACKUP.hour,calendar_BACKUP.min,calendar_BACKUP.sec);
RTC_ExitConfigMode(); //退出配置模式
BKP_WriteBackupRegister(BKP_DR1, 0x5050); //向指定的后备寄存器中写入指定数据
}
else//系统继续计时
{
// /*使用内部低速晶振*/
// RCC_LSICmd(ENABLE); //使能LSI时钟
// while (RCC_GetFlagStatus(RCC_FLAG_LSIRDY) == RESET) //等待LSI晶振就绪
// {
// temp++;
// if(temp>=5000) return 1; //持续5s无法起振,初始化时钟失败,晶振有问题
// delay_ms(1); // 1ms延时
// }
// RCC_RTCCLKCmd(ENABLE); //使能RTC时钟
// /*使用内部低速晶振*/
if(RTC_GetSynchro() == 1)return 1; //等待RTC寄存器同步 //10.8 晶振坏了这里会卡死,所以增加超时失败退出
RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
}
/**更多功能**/
//更新当前时间
timecount=RTC_GetCounter();
//若启用休眠1,则以当前秒数为起始点
if((paraMem.sleep_min_disable & 0x8000) == 0) //0代表启用休眠
{
sleep_enableflag |= 0x01;
sleeptimecount=timecount;
}
else
{
sleep_enableflag &= 0xfe;
}
//若启用休眠2,则判断此时最低电压是否满足条件,满足则以当前秒数为起始点
if((paraMem.sleep2_min_disable & 0x8000) == 0) //0代表启用休眠
{
sleep_enableflag |= 0x02;
//满足休眠电压,无均衡,无电流
if((cellVoltageMin <= sleep2Vol) && ((bmsMem.balanceStatus & 0x0001) == 0) && (bmsMem.packCurrent > (-200)) && (bmsMem.packCurrent < 200))
{
//无除过压以外的保护
if(((bmsMem.bStatus1 & 0x067e) == 0) && ((bmsMem.bStatus2 & 0x00ff) == 0) && ((bmsMem.bStatus3 & 0x0008) == 0) && ((bmsMem.temperaStatus & 0x0f7f) == 0))
{
sleep2timecount=timecount;
}
}
}
else
{
sleep_enableflag &= 0xfd;
}
return 0; //ok
}
/*
* 函数名:uf_RTC_Update
* 描述 :将calendar_WRITE结构体的值写入RTC时间
* 输入 :无
* 输出 :0,成功;1:错误代码
* 调用
*/
uint8_t uf_RTC_Update(void)
{
if(calendar_WRITE.w_month != 0) //有写入的数组
{
uint16_t temp=0;
uint8_t Wrtime[6];
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE); //使能PWR和BKP外设时钟
PWR_BackupAccessCmd(ENABLE); //使能后备寄存器访问
// /*使用内部低速晶振*/
// BKP_DeInit(); //复位备份区域
// RCC_LSICmd(ENABLE); //使能LSI时钟
// while (RCC_GetFlagStatus(RCC_FLAG_LSIRDY) == RESET) //等待LSI晶振就绪
// {
// temp++;
// if(temp>=5000) return 1; //持续5s无法起振,初始化时钟失败,晶振有问题
// delay_ms(1); // 1ms延时
// }
// RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI); //设置RTC时钟(RTCCLK),选择LSI作为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(40000 - 1); //设置RTC预分频的值40kHz
// RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
// /*使用内部低速晶振*/
/*使用外部低速晶振*/
BKP_DeInit(); //复位备份区域
RCC_LSEConfig(RCC_LSE_ON); //设置外部低速晶振(LSE),使用外设低速晶振
while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET) //检查指定的RCC标志位设置与否,等待低速晶振就绪
{
temp++;
if(temp>=5000) return 1; //持续5s无法起振,初始化时钟失败,晶振有问题
delay_ms(1); // 1ms延时
}
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(calendar_WRITE.w_year,calendar_WRITE.w_month,calendar_WRITE.w_date,calendar_WRITE.hour,calendar_WRITE.min,calendar_WRITE.sec);
RTC_ExitConfigMode(); //退出配置模式
BKP_WriteBackupRegister(BKP_DR1, 0x5050); //向指定的后备寄存器中写入用户程序数据
//刷新休眠和欠压复位的时间
RTC_UpdateFlag = 1;
//存储时间备份
Wrtime[0] = calendar_WRITE.w_year;
Wrtime[1] = calendar_WRITE.w_month;
Wrtime[2] = calendar_WRITE.w_date;
Wrtime[3] = calendar_WRITE.hour;
Wrtime[4] = calendar_WRITE.min;
Wrtime[5] = calendar_WRITE.sec;
EEPROM_WrMulByte(EE_TIME_BACKUP,Wrtime);
delay_ms(5);
//清零
calendar_WRITE.w_year = 0;
calendar_WRITE.w_month = 0;
calendar_WRITE.w_date = 0;
calendar_WRITE.week = 0;
calendar_WRITE.hour = 0;
calendar_WRITE.min = 0;
calendar_WRITE.sec = 0;
}
return 0; //ok
}
//用于处理因各种原因需要的时间备份
int32_t oldCur;
void RTC_BackUp(void)
{
uint8_t WrFlag=0;
//当正在充放电,开始时记录一次,之后每5分钟记录一次
if(bCHGING == 1)
{
if(oldCur <= 0) //原来在放电或待机
{
WrFlag = 1;
oldCur = bmsMem.packCurrent;
}
if((calendar.min%5 == 0x00) && (calendar.sec == 0x00))
{
WrFlag = 1;
}
}
else if(bDSGING == 1)
{
if(oldCur >= 0) //原来在充电或待机
{
WrFlag = 1;
oldCur = bmsMem.packCurrent;
}
if((calendar.min%5 == 0x00) && (calendar.sec == 0x00))
{
WrFlag = 1;
}
}
//在待机状态,每1h记录一次
else
{
oldCur = 0;
if((calendar.min == 0x00) && (calendar.sec == 0x00))
{
WrFlag = 1;
}
}
//需要写入备份时间,执行
if(WrFlag == 1)
{
if(calendar.w_month != 0)
{
uint8_t tmpRd[6];
tmpRd[0] = calendar.w_year;
tmpRd[1] = calendar.w_month;
tmpRd[2] = calendar.w_date;
tmpRd[3] = calendar.hour;
tmpRd[4] = calendar.min;
tmpRd[5] = calendar.sec;
EEPROM_WrMulByte(EE_TIME_BACKUP,tmpRd);
delay_ms(5);
}
}
}