#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; }