NANO130_H2Press/User_RS485Up(PB9冲突)/rtc_wakeup.c

561 lines
13 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.

#include "includes.h"
// 当前工作模式
uint8_t Wakeup_workMode = WORK_MODE_NORMAL;
// 电量极低标志
uint8_t bat_veryLow = 0;
// 休眠标识
volatile uint8_t Wakeup_Sleeping = 0;
// 实际启动时间和INITIAL_YEAR相差的秒数当GPS定位或GPRS连接基站以后设置
volatile uint32_t RTC_offsetSeconds = 0;
void Wakeup_ShowTime(char *preStr, S_RTC_TIME_DATA_T *pRTC)
{
S_RTC_TIME_DATA_T sRTC;
uint32_t totalSeconds = Calc_SecondsFromYear(INITIAL_YEAR, pRTC->u32Year, pRTC->u32Month, pRTC->u32Day,
pRTC->u32Hour, pRTC->u32Minute, pRTC->u32Second);
totalSeconds += RTC_offsetSeconds;
Wakeup_CalcUTCTime(totalSeconds, &sRTC);
printf("\n%s%04d-%02d-%02d %02d:%02d:%02d\n", preStr, sRTC.u32Year, sRTC.u32Month,
sRTC.u32Day, sRTC.u32Hour, sRTC.u32Minute, sRTC.u32Second);
}
// 判断是否闰年
uint8_t IsLeapYear(uint16_t year)
{
if(year % 4)
return 0;
if(year % 100 == 0 && year % 400)
return 0;
return 1;
}
// 计算从某年开始以来经过的秒数
uint32_t Calc_SecondsFromYear(uint16_t startYear, uint16_t year, uint8_t mon, uint8_t day, uint8_t hour, uint8_t min, uint8_t sec)
{
uint32_t totalSeconds = 0ul;
uint16_t i;
// 计算整年的
for(i = startYear; i < year; i++)
{
totalSeconds += 31536000ul; // 365 * 86400
if(IsLeapYear(i)) // 闰年+1天
totalSeconds += 86400ul;
}
// 计算整月的
for(i = 1; i < mon; i++)
{
totalSeconds += 2678400ul; // 31 * 86400
if(i == 4 || i == 6 || i == 9 || i == 11)
totalSeconds -= 86400ul;
else if(i == 2)
{
totalSeconds -= 259200ul; // 3 * 86400
if(IsLeapYear(year)) // 闰年+1天
totalSeconds += 86400ul;
}
}
// 计算整天的
totalSeconds += (day - 1) * 86400ul;
// 计算时分秒的
totalSeconds += hour * 3600ul;
totalSeconds += min * 60ul;
totalSeconds += sec;
return totalSeconds;
}
uint16_t get_year_days(uint16_t year)
{
if(IsLeapYear(year))
return 366;
return 365;
}
uint16_t get_month_days(uint16_t year, uint8_t month)
{
if(month == 4 || month == 6 || month == 9 || month == 11)
return 30;
if(month == 2)
{
if(IsLeapYear(year))
return 29;
return 28;
}
return 31;
}
// 将从INITIAL_YEAR开始的描述转换成年月日
void Wakeup_CalcUTCTime(uint32_t totalSeconds, S_RTC_TIME_DATA_T *pRTC)
{
uint32_t totalDays, days;
// 计算下次唤醒的年、月、日
pRTC->u32Year = INITIAL_YEAR;
pRTC->u32Month = 1;
pRTC->u32Day = 1;
totalDays = totalSeconds / 86400ul;
while(totalDays >= 1)
{
// 计算整年的
days = get_year_days(pRTC->u32Year);
if(totalDays >= days)
{
pRTC->u32Year++;
totalDays -= days;
continue;
}
// 计算整月的
days = get_month_days(pRTC->u32Year, pRTC->u32Month);
if(totalDays >= days)
{
pRTC->u32Month++;
totalDays -= days;
continue;
}
// 计算整天
pRTC->u32Day += totalDays;
break;
}
// 计算下次唤醒的时、分、秒
totalSeconds %= 86400ul;
pRTC->u32Hour = totalSeconds / 3600ul;
totalSeconds %= 3600;
pRTC->u32Minute = totalSeconds / 60ul;
totalSeconds %= 60ul;
pRTC->u32Second = totalSeconds;
}
uint32_t Wakeup_CalcPeriod(uint8_t charging)
{
uint32_t period = 300;
if(dcBuff.configData.intervalTrans > 0 && period > dcBuff.configData.intervalTrans)
period = dcBuff.configData.intervalTrans;
if(dcBuff.configData.intervalSample > 0 && period > dcBuff.configData.intervalSample)
period = dcBuff.configData.intervalSample;
if(charging && period > 60)
period = 60;
return period;
}
// 计算下次唤醒的时间
void Wakeup_CalcWakeupTime(S_RTC_TIME_DATA_T *pRTC, uint8_t charging)
{
uint32_t period = Wakeup_CalcPeriod(charging);
uint32_t sec;
// 先计算从初始时间以来的秒数
uint32_t totalSeconds = Calc_SecondsFromYear(INITIAL_YEAR, pRTC->u32Year, pRTC->u32Month, pRTC->u32Day,
pRTC->u32Hour, pRTC->u32Minute, pRTC->u32Second);
// 双路切换显示
if(MeterNeedRoll() && period > 20)
period = 20;
// 计算下次唤醒时间的秒数
// 将这个秒数对齐到唤醒周期的倍数
// 这是因为:每次复位以后虽然时间是继续的,但闹钟需要重新设置
sec = totalSeconds + period;
sec -= sec % period;
// 防止闹钟时间太近
if(sec <= totalSeconds + 2)
totalSeconds = sec + period;
else
totalSeconds = sec;
// 计算下次唤醒的年、月、日
Wakeup_CalcUTCTime(totalSeconds, pRTC);
}
// 设定下次闹钟唤醒时间
void Wakeup_SetAlarm(uint8_t charging)
{
S_RTC_TIME_DATA_T sRTC;
// 设定下次闹钟唤醒时间
RTC_GetDateAndTime(&sRTC);
// 显示当前时间
Wakeup_ShowTime("RTC time is: ", &sRTC);
Wakeup_CalcWakeupTime(&sRTC, charging);
RTC_SetAlarmDateAndTime(&sRTC);
// 显示唤醒时间
Wakeup_ShowTime("***********************************************\nWakeup time is: ", &sRTC);
}
void RTC_IRQHandler(void)
{
S_RTC_TIME_DATA_T sRTC;
static uint32_t gpsSeconds = 0;
static uint32_t rollTime = 0;
static uint32_t semTime = 0;
uint32_t totalSeconds;
uint32_t period = Wakeup_CalcPeriod(dcBuff.sampleData.charging);
if(CLK->WK_INTSTS & CLK_WK_INTSTS_PD_WK_IS_Msk)
{
// 重新打开看门狗计数器
WDT->CTL |= WDT_CTL_WTE_Msk;
}
// 本中断用于唤醒MCU
if(RTC_GET_ALARM_INT_FLAG() == 1)
{
/* Clear RTC alarm interrupt flag */
RTC_CLEAR_ALARM_INT_FLAG();
// 获取当前时间
RTC_GetDateAndTime(&sRTC);
// 计算自上次gps定位以来的时间
totalSeconds = Calc_SecondsFromYear(INITIAL_YEAR, sRTC.u32Year, sRTC.u32Month, sRTC.u32Day,
sRTC.u32Hour, sRTC.u32Minute, sRTC.u32Second);
// 如果超过6个发送周期最小2小时最大1天未发送成功复位
if((totalSeconds >= DTU_succTime + 7200 && totalSeconds >= DTU_succTime + dcBuff.configData.intervalTrans * 6) || totalSeconds >= DTU_succTime + 86400)
NVIC_SystemReset();
// 每隔24小时定位一次(3秒的容错
if(totalSeconds + 3 >= gpsSeconds + 86400ul)
{
// 重新定位
GPS_Located = 0;
GPS_Locate = 1;
// 重新计时
gpsSeconds = totalSeconds;
}
if(CLK->WK_INTSTS & CLK_WK_INTSTS_PD_WK_IS_Msk)
{
delay_ms(10);
// 清除中断唤醒标志
// CLK->WK_INTSTS |= CLK_WK_INTSTS_PD_WK_IS_Msk;
Vcc_Enable();
delay_ms(40);
Wakeup_Sleeping = 0;
// printf("\nTickCount: %d\n", GetTickCount());
printf("\n\nWake up by RTC.\n\n");
}
if(totalSeconds >= semTime + period)
{
semTime = totalSeconds;
if(CLK->WK_INTSTS & CLK_WK_INTSTS_PD_WK_IS_Msk)
DTU_semSync = 1;
else
DTU_semGPRS = 1;
}
// 切换双压力滚动显示序号
if(MeterInMeterPage() && MeterNeedRoll() && totalSeconds >= rollTime + 20)
{
rollTime = totalSeconds;
Form_rollIdx = 1 - Form_rollIdx;
Form_Refresh();
}
// 设定下次闹钟唤醒时间
Wakeup_SetAlarm(dcBuff.sampleData.charging);
}
}
void GPABC_IRQHandler(void)
{
// 如果是发送按键中断
if(GPIO_GET_INT_FLAG(PB, BIT9))
{
// 处理按键
Key_Handler();
}
// 处理充电唤醒中断
if(!GPIO_GET_INT_FLAG(PC, BIT6))
return;
if(CLK->WK_INTSTS & CLK_WK_INTSTS_PD_WK_IS_Msk)
{
// 重新打开看门狗计数器
WDT->CTL |= WDT_CTL_WTE_Msk;
}
// 清除中断标志
GPIO_CLR_INT_FLAG(PC, BIT6);
if(CLK->WK_INTSTS & CLK_WK_INTSTS_PD_WK_IS_Msk)
{
delay_ms(10);
// 清除中断唤醒标志
// CLK->WK_INTSTS |= CLK_WK_INTSTS_PD_WK_IS_Msk;
Vcc_Enable();
delay_ms(40);
Wakeup_Sleeping = 0;
// 判断是否为低电平,排除干扰
if(!VCC_POWER_STATUS())
{
printf("\nWakeup by DISTURB ...\n");
// 由干扰引起的按键中断,发信号休眠
return;
}
printf("\n\nWake up by POWER.\n\n");
// 采集数据
if(!Sample_Busy())
NVIC_SetPendingIRQ(TMR1_IRQn);
}
}
void Wakeup_Init()
{
CLK->PWRCTL |= CLK_PWRCTL_PD_WK_IE_Msk;
// 用外部32768Hz低速时钟在掉电模式依然有效
CLK_EnableModuleClock(RTC_MODULE);
/* Set GPC as VCC_POWER_STATUS */
SYS->PC_L_MFP &= ~SYS_PC_L_MFP_PC6_MFP_Msk;
SYS->PC_L_MFP |= SYS_PC_L_MFP_PC6_MFP_GPC6;
GPIO_SetMode(PC, 1 << 6, GPIO_PMD_INPUT);
/* Interrupt Type: Rising Edge */
GPIO_EnableInt(PC, 6, GPIO_INT_RISING);
/* Enable interrupt de-bounce function and select de-bounce sampling cycle time is 512 clocks of LIRC clock */
GPIO_SET_DEBOUNCE_TIME(GPIO_DBCLKSRC_IRC10K, GPIO_DBCLKSEL_256);
// GPIO_ENABLE_DEBOUNCE(PC, BIT6);
}
void Wakeup_InitializeTime(S_RTC_TIME_DATA_T *pRTC)
{
pRTC->u32Year = INITIAL_YEAR;
pRTC->u32Month = 1;
pRTC->u32Day = 1;
pRTC->u32DayOfWeek = RTC_FRIDAY;
pRTC->u32Hour = 0;
pRTC->u32Minute = 0;
pRTC->u32Second = 0;
pRTC->u32TimeScale = RTC_CLOCK_24;
}
void Wakeup_Open()
{
S_RTC_TIME_DATA_T sRTC;
NVIC_SetPriority (RTC_IRQn, 3); /* set Priority for RTC Interrupt */
NVIC_EnableIRQ(RTC_IRQn);
RTC->INIR = RTC_INIT_KEY;
while(RTC->INIR != RTC_INIR_ACTIVE_Msk);
/* Set RTC date and time */
Wakeup_InitializeTime(&sRTC);
RTC_SetDateAndTime(&sRTC);
/* Waiting for RTC settings stable */
while((RTC->AER & RTC_AER_ENF_Msk) == RTC_AER_ENF_Msk);
printf("\n*******************************\n");
printf("system reseted ...\n");
// 设定下次闹钟唤醒时间
Wakeup_SetAlarm(dcBuff.sampleData.charging);
/* Enable RTC alarm interrupt and wake-up function will be enabled also */
RTC_EnableInt(RTC_RIER_AIER_Msk);
/* Enable GPIO IRQ */
NVIC_SetPriority (GPABC_IRQn, 3);
NVIC_EnableIRQ(GPABC_IRQn);
}
// 检查是否进入掉电模式
void Wakeup_Powerdown()
{
uint32_t curr_time;
S_RTC_TIME_DATA_T sRTC;
static uint8_t last_PowerStatus = 0;
uint8_t powerStatus;
#if 0
// 模拟充电和放电每隔10分钟切换一次
if(dcBuff.configDisplay.op_SEND_GPS_DATA)
{
RTC_GetDateAndTime(&sRTC);
curr_time = Calc_SecondsFromYear(INITIAL_YEAR, sRTC.u32Year, sRTC.u32Month, sRTC.u32Day,
sRTC.u32Hour, sRTC.u32Minute, sRTC.u32Second);
powerStatus = (curr_time / 600 % 2 == 0);
}
else
#endif
{
// 记录关电的时间
powerStatus = VCC_POWER_STATUS();
}
if(last_PowerStatus != powerStatus && !powerStatus)
{
RTC_GetDateAndTime(&sRTC);
Poweroff_time = Calc_SecondsFromYear(INITIAL_YEAR, sRTC.u32Year, sRTC.u32Month, sRTC.u32Day,
sRTC.u32Hour, sRTC.u32Minute, sRTC.u32Second);
}
last_PowerStatus = powerStatus;
// 如果是槽车,且有外部供电,不休眠
if(powerStatus && dcBuff.configDisplay.op_SEND_GPS_DATA)
return;
// 定位未成功且未超时,不允许休眠
if(!dcBuff.configDisplay.op_SEND_GPS_DATA && Wakeup_GetWorkMode() == WORK_MODE_NORMAL && GPS_Waiting)
return;
// 采集任务未完成,不允许休眠
if(Sample_Busy())
return;
// 槽车刚断电的时候延迟20分钟再休眠
if(dcBuff.configDisplay.op_SEND_GPS_DATA)
{
// 正常模式的情况下,延迟休眠
if(!powerStatus && Wakeup_GetWorkMode() == WORK_MODE_NORMAL)
{
// 获取当前时间
RTC_GetDateAndTime(&sRTC);
curr_time = Calc_SecondsFromYear(INITIAL_YEAR, sRTC.u32Year, sRTC.u32Month, sRTC.u32Day,
sRTC.u32Hour, sRTC.u32Minute, sRTC.u32Second);
if(Poweroff_time > 0 && curr_time >= Poweroff_time && curr_time < Poweroff_time + 1200)
// if(Poweroff_time > 0 && curr_time >= Poweroff_time && curr_time < Poweroff_time + 60)
{
if((curr_time - Poweroff_time) % 10 == 0)
printf("\ndelay sleep time: %d\n", curr_time - Poweroff_time);
return;
}
}
}
// 关闭DTU
if(Wakeup_GetWorkMode() != WORK_MODE_NORMAL || LCD_Disabled)
DTU_PowerOff();
// 如果显示屏在操作,不休眠
if(Wakeup_GetWorkMode() == WORK_MODE_NORMAL && !LCD_Disabled)
return;
// 有外部供电,不休眠
if(powerStatus)
return;
// 禁止按键功能
NVIC_DisableIRQ(GPDEF_IRQn);
// 非罐箱版,禁止加速度中断
if(!dcBuff.configDisplay.op_BOX_VER)
GPIO_DisableInt(PF, 5);
printf("\nEnter to Power-Down ......\n");
// printf("\nTickCount: %d\n", GetTickCount());
/* To check if all the debug messages are finished */
while(IsDebugFifoEmpty() == 0);
// 防止休眠意外(不喂狗)
WDT_RESET_COUNTER();
Wakeup_Sleeping = 1;
VCC_SFLASH_FRAM_OFF();
Vcc_Disable();
// 允许按键唤醒,先清除中断标志
PE->ISRC = PE->ISRC;
NVIC_EnableIRQ(GPDEF_IRQn);
// 看门狗时钟为LIRC在休眠下也有效故先停止计数器
WDT->CTL &= ~WDT_CTL_WTE_Msk;
do
{
// 进入休眠状态
CLK_PowerDown();
}while(!(CLK->WK_INTSTS & CLK_WK_INTSTS_PD_WK_IS_Msk));
CLK->WK_INTSTS |= CLK_WK_INTSTS_PD_WK_IS_Msk;
// 罐箱或槽车版,允许加速度中断
if(dcBuff.configDisplay.op_BOX_VER || dcBuff.configDisplay.op_SEND_GPS_DATA)
{
// 读取运动状态
Accelero_ReadStatus(PF5);
GPIO_EnableInt(PF, 5, GPIO_INT_BOTH_EDGE);
}
delay_ms(20);
SFlash_UnlockBPR();
}
// 读取工作模式
uint8_t Wakeup_GetWorkMode()
{
return Wakeup_workMode;
}
// 设置工作模式
void Wakeup_SetWorkMode()
{
// 默认为正常工作模式
uint8_t mode = WORK_MODE_NORMAL;
// 放电状态
if(!VCC_POWER_STATUS())
{
// 进入电池保护模式
if(bat_veryLow)
mode = WORK_MODE_PROTECT;
// 进入充电提示模式
else if(dcBuff.dtuData.batLow)
mode = WORK_MODE_CHARGE;
}
printf("\nchange work mode from %d to %d\n", Wakeup_workMode, mode);
// 电池保护模式
if(mode == WORK_MODE_PROTECT)
{
// 关闭DTU和传感器供电
DTU_PowerOff();
// VCC_SENSOR_24V_OFF();
// 保存模式
Wakeup_workMode = mode;
return;
}
// 解除电池保护模式
if(Wakeup_workMode == WORK_MODE_PROTECT)
{
}
// 充电提示模式
if(mode == WORK_MODE_CHARGE)
{
// 关闭DTU和传感器供电
DTU_PowerOff();
// VCC_SENSOR_24V_OFF();
}
else
{
// VCC_SENSOR_24V_ON();
}
// 保存模式
Wakeup_workMode = mode;
}