BMS STM32 V4.0.0.0

This commit is contained in:
2026-08-25 17:30:40 +08:00
commit 3ae7bcb054
313 changed files with 115734 additions and 0 deletions
+793
View File
@@ -0,0 +1,793 @@
/**
******************************************************************************
* @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);
}
}
}