bsp_rtc.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457
  1. /*
  2. * Copyright (c) 2006-2021, RT-Thread Development Team
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. *
  6. * Change Logs:
  7. * Date Author Notes
  8. * 2021-09-15 19813 the first version
  9. */
  10. /* 包含头文件 ----------------------------------------------------------------*/
  11. #include "bsp_rtc.h"
  12. #include "main.h"
  13. #include "wwdg.h"
  14. #define DBG_TAG "bsp_rtc"
  15. #define DBG_LVL DBG_LOG
  16. #include <rtdbg.h>
  17. #include "bsp_date.h"
  18. #include "loar.h"
  19. #include <easyflash.h>
  20. #define RTC_Alarm_Time_BCK RTC_BKP_DR0
  21. #define RTC_Alarm_RETRY RTC_BKP_DR2
  22. extern WWDG_HandleTypeDef hwwdg;
  23. extern uint8_t wwdg_state;
  24. extern RTC_HandleTypeDef hrtc;
  25. extern RTC_TimeTypeDef GetTime;
  26. struct rtc_time systmtime;
  27. extern int _EXFUN(getchar, (void));
  28. int Compensation_time; //补偿时间(单位秒) 减去每次开机的运行时间 以使上报数据维持在设定值
  29. uint8_t Time_Correced = 0; //标记时间是否已补偿
  30. /**
  31. * 函数功能: 从串口调试助手获取数字值(把ASCII码转换为数字)
  32. * 输入参数: value 用户在超级终端中输入的数值
  33. * 返 回 值: 输入字符的ASCII码对应的数值
  34. * 说 明:本函数专用于RTC获取时间,若进行其它输入应用,要修改一下
  35. */
  36. // uint8_t USART_Scanf(uint32_t value)
  37. //{
  38. // uint32_t index = 0;
  39. // uint32_t tmp[2] = {0, 0};
  40. // while (index < 2)
  41. // {
  42. // /* 等待直到串口接收到数据 */
  43. // tmp[index++] = getchar();
  44. // if ((tmp[index - 1] < 0x30) || (tmp[index - 1] > 0x39)) /*数字0到9的ASCII码为0x30至0x39*/
  45. // {
  46. // rt_kprintf("请输入 0 到 9 之间的数字 -->:\n");
  47. // index--;
  48. // }
  49. // }
  50. // /* 计算输入字符的ASCII码转换为数字*/
  51. // index = (tmp[1] - 0x30) + ((tmp[0] - 0x30) * 10);
  52. //
  53. // /* 检查数据有效性 */
  54. // if (index > value)
  55. // {
  56. // rt_kprintf("请输入 0 到 %d 之间的数字\n", value);
  57. // return 0xFF;
  58. // }
  59. // return index;
  60. // }
  61. RTC_DateTypeDef yTime; //年份
  62. RTC_TimeTypeDef nTime; //时 分 秒 时间
  63. RTC_AlarmTypeDef aTime; //闹钟定时
  64. extern uint8_t Wakeup_alarm;
  65. extern uint8_t RTC_Check;
  66. extern void Recode_e_code(uint8_t code , uint8_t para);
  67. extern void Trigger_report(uint8_t type);
  68. uint8_t RTC_Alarm_Flag = Zero;
  69. void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *nhrtc)
  70. {
  71. //从闹钟唤醒了 以此触发警戒线程
  72. // uint16_t RTC_Check_Tigger = 0x6400; //此值左移8位==100
  73. RTC_Alarm_Flag = One;
  74. rt_kprintf("--------- HAL_RTC_Alarm ----------\r\n");
  75. if (RTC_Check == Zero /*&& Node_Type != Monitor*/)
  76. {
  77. // rt_kprintf("--------- RTC forced reset ----------\r\n");
  78. // __HAL_RCC_PWR_CLK_ENABLE(); //使能电源时钟PWR
  79. // HAL_PWR_EnableBkUpAccess(); //取消备份区域写保护
  80. // HAL_RTCEx_BKUPWrite(&hrtc, RTC_Alarm_RETRY, RTC_Check_Tigger);
  81. // HAL_PWR_DisableBkUpAccess(); //开启备份区域写保护
  82. //
  83. // Recode_e_code(11 , 1);
  84. //
  85. // RESET_MCU();
  86. Trigger_report(1);
  87. }
  88. }
  89. //获取当前时间戳——加上需要定时的时间(单位秒)——定时闹钟的时间戳转换为rtc_time结构体格式——给RTC_AlarmTypeDef结构体变量赋值时分秒——写入闹钟寄存器
  90. //进入待机模式,输入参数单位为分钟,范围0-1000
  91. extern RTC_TimeTypeDef GetTime; //获取时间结构体
  92. extern RTC_HandleTypeDef hrtc;
  93. extern void RESET_MCU(void);
  94. extern uint8_t RTC_Shotdown;
  95. extern uint16_t read_bcktime;
  96. extern void rt_hw_cpu_reset(void);
  97. extern LOAR_Parameter loar_par;
  98. extern Revise_par rev_par;
  99. extern uint8_t Double_INR;
  100. extern Trans_para Trans_p;
  101. uint8_t ReFlash_RTC(void);
  102. void Set_Alarm_A_Wakeup(uint16_t standbytime)
  103. {
  104. uint32_t TimeVar;
  105. struct rtc_time alarmtime;
  106. uint16_t backup_time = 0;
  107. uint8_t count = 0, Correction_time = 0, i = 0; //提前整点时刻开机的修正参数 Compensation_time 单位秒
  108. rt_thread_mdelay(10);
  109. ReFlash_RTC();
  110. if (Double_INR)
  111. {
  112. sys_info.interval *= 2;
  113. }
  114. /*
  115. *
  116. * 此处加上整点上报的控制逻辑
  117. *
  118. * */
  119. Compensation_time += sys_info.Sensor_num * 9; // 为传感器的读取预留时间
  120. Compensation_time += 15; // 4G版提前开机所需固定时间
  121. if(Node_Type == LTE)
  122. {
  123. LOG_I("This time took %d sec", Compensation_time);
  124. }
  125. if(Trans_p.en == One && Node_Type == LTE) //保护模式 间隔改为2小时
  126. {
  127. standbytime = 120;
  128. LOG_W("Trans_p > INR = 120");
  129. }
  130. if (standbytime != 60 && Trans_p.en != One) //若是保护模式 不再进行整点匹配
  131. {
  132. Correction_time = GetTime.Minutes;
  133. Correction_time++;
  134. for (i = 0; i <= sys_info.interval; i++) //使用for循环 寻找匹配的时间点
  135. {
  136. Correction_time++;
  137. if (Correction_time % sys_info.interval == 0) //判断是否整除时间间隔
  138. {
  139. if ((Correction_time - (Compensation_time / 60 + 1)) <= GetTime.Minutes) //校验当前时间是否已晚于设定开机时间
  140. {
  141. Correction_time += sys_info.interval;
  142. }
  143. if (Correction_time % 30 == 0)
  144. {
  145. Correction_time += 1;
  146. }
  147. if (Correction_time >= 0 && Correction_time < 120) //判定区间
  148. {
  149. if (Correction_time < 60) //分情况处理
  150. {
  151. GetTime.Minutes = Correction_time;
  152. }
  153. else
  154. {
  155. GetTime.Minutes = Correction_time % 60;
  156. GetTime.Hours = (GetTime.Hours + 1) % 24;
  157. }
  158. GetTime.Seconds = 0;
  159. Time_Correced = 1;
  160. // LOG_I("time corr succ %d:%d", GetTime.Hours, GetTime.Minutes);
  161. break;
  162. }
  163. else
  164. {
  165. // LOG_W("Out of range!");
  166. break;
  167. }
  168. }
  169. if (count < sys_info.interval)
  170. {
  171. count++;
  172. }
  173. else
  174. {
  175. LOG_W("corr ×");
  176. count = 0;
  177. Time_Correced = 0;
  178. break;
  179. }
  180. }
  181. }
  182. /*
  183. *
  184. * 此处加上整点上报的控制逻辑
  185. *
  186. * */
  187. TimeVar = GetTime.Seconds + GetTime.Minutes * 60 + GetTime.Hours * 3600;
  188. if (Time_Correced == 0)
  189. {
  190. TimeVar = TimeVar + standbytime * 60; //加上需要闹钟时间。
  191. }
  192. TimeVar = TimeVar + 24 * 3600 - Compensation_time; //在设定的时间中减去每次开机的运行时间·
  193. to_tm(TimeVar, &alarmtime); /*把时间戳转换为闹钟的时分秒*/
  194. if (RTC_Shotdown == One && RTC_Check != One) //如果本次被迫关机 不再根据interval重新计算
  195. {
  196. // LOG_W("bcktime %d" , read_bcktime);
  197. alarmtime.tm_hour = (int)read_bcktime / 360;
  198. alarmtime.tm_min = (int)(read_bcktime - alarmtime.tm_hour * 360) / 6;
  199. alarmtime.tm_sec = (int) (read_bcktime - alarmtime.tm_hour * 360 - alarmtime.tm_min * 6) * 10;
  200. }
  201. aTime.Alarm = RTC_ALARM_A;
  202. aTime.AlarmTime.Hours = alarmtime.tm_hour;
  203. aTime.AlarmTime.Minutes = alarmtime.tm_min;
  204. aTime.AlarmTime.Seconds = alarmtime.tm_sec;
  205. aTime.AlarmTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
  206. aTime.AlarmTime.StoreOperation = RTC_STOREOPERATION_RESET;
  207. aTime.AlarmMask = RTC_ALARMMASK_DATEWEEKDAY;
  208. aTime.AlarmSubSecondMask = RTC_ALARMSUBSECONDMASK_ALL;
  209. aTime.AlarmDateWeekDaySel = RTC_ALARMDATEWEEKDAYSEL_DATE;
  210. if (HAL_RTC_SetAlarm_IT(&hrtc, &aTime, RTC_FORMAT_BIN) == HAL_OK)
  211. {
  212. if (wwdg_state == One) //初始化之后再喂
  213. HAL_WWDG_Refresh(&hwwdg); //喂狗也是每秒一次
  214. LOG_I("alarm-t :%d:%d:%d\n", aTime.AlarmTime.Hours, aTime.AlarmTime.Minutes, aTime.AlarmTime.Seconds);
  215. /******************
  216. *
  217. *
  218. * 使用一个uint16_t 存放当前时刻总秒数/10
  219. *
  220. */
  221. backup_time = aTime.AlarmTime.Hours * 360 + aTime.AlarmTime.Minutes * 6 + aTime.AlarmTime.Seconds / 10;
  222. __HAL_RCC_PWR_CLK_ENABLE(); //使能电源时钟PWR
  223. HAL_PWR_EnableBkUpAccess(); //取消备份区域写保护
  224. HAL_RTCEx_BKUPWrite(&hrtc, RTC_Alarm_Time_BCK, backup_time); //此处减去5秒 为boot loader留下时间
  225. HAL_PWR_DisableBkUpAccess(); //开启备份区域写保护
  226. LOG_I("BCK_T %d\n", backup_time);
  227. }
  228. HAL_RTCEx_BKUPWrite(&hrtc, RTC_Alarm_Time_BCK, 0);
  229. }
  230. extern SPI_HandleTypeDef hspiflash;
  231. extern void SPI_Flash_PowerDown(void);
  232. extern void Enadble_module(uint8_t en);
  233. extern void rt_hw_cpu_reset(void);
  234. extern uint8_t Valve_Mode;
  235. int inter_standby(uint16_t standbytime)
  236. {
  237. if (standbytime > 1000)
  238. return -1;
  239. if (standbytime == 0)
  240. return -1;
  241. if (wwdg_state == One) //初始化之后再喂
  242. HAL_WWDG_Refresh(&hwwdg); //喂狗也是每秒一次
  243. //将当前的定时值转化为参数 设置到Alarm_A中
  244. Set_Alarm_A_Wakeup(standbytime);//就是将形参 保存到RTC的备份寄存器0中
  245. if (wwdg_state == One) //初始化之后再喂
  246. HAL_WWDG_Refresh(&hwwdg); //喂狗也是每秒一次
  247. #if 1 //关闭休眠唤醒
  248. //mode1
  249. // LOG_I("check wake up\r\n");
  250. if (__HAL_PWR_GET_FLAG(PWR_FLAG_SB) != RESET) //检查并检查系统是否从待机模式恢复
  251. {//单片机刚刚从待机模式唤醒
  252. /* Clear Standby flag */
  253. __HAL_PWR_CLEAR_FLAG(PWR_FLAG_SB);
  254. }
  255. rt_thread_mdelay(10);
  256. // PWR_WAKEUP_PIN2 唤醒引脚无用
  257. HAL_PWR_DisableWakeUpPin(PWR_WAKEUP_PIN2); //禁用所有使用的唤醒源
  258. HAL_PWR_DisableWakeUpPin(PWR_WAKEUP_PIN6);
  259. __HAL_PWR_CLEAR_FLAG(PWR_FLAG_WUF); //清除所有相关的唤醒标志
  260. /**************************** 各种关闭耗电源 *********************************/
  261. Enadble_module(0); //去使能各个组件
  262. wwdg_state = Zero;//看门狗
  263. rt_kprintf("--------------- All sleep ----------------\n");
  264. __HAL_RCC_APB2_FORCE_RESET(); //复位所有IO口
  265. __HAL_RCC_PWR_CLK_ENABLE(); //使能PWR时钟
  266. /**************************** 各种关闭耗电源 *********************************/
  267. if (Valve_Mode == 1)
  268. {
  269. HAL_PWR_EnableWakeUpPin(PWR_WAKEUP_PIN2); //启用连接到WakeUp Pin2 PC13
  270. }
  271. HAL_PWR_EnableWakeUpPin(PWR_WAKEUP_PIN6); //启用连接到WakeUp Pin6 PB5
  272. ///
  273. ///
  274. /// 现已证实,连接到PWR_WAKEUP_PIN1 必须保持该IO保持低电平
  275. /// 设置低电平 然后浮空也不行 必须加入弱下拉(目前是100K到GND)
  276. /// 没有下拉会导致 进入休眠后立即唤醒 还有就是功耗增加
  277. ///
  278. ///
  279. /*
  280. *
  281. *
  282. * 实测当前各区域耗能比例
  283. *
  284. * 单片机 standby 模式 1.6uA 该部分调试主要是设置了standby和关闭所有io
  285. * XC62FP 静态模式占用 <2uA 从662K换成了XC62FP 进来电流低至2uA
  286. * falsh 断电 0uA 休眠时flash地线将IO配置成高阻断电
  287. * lora 低功耗模式 2uA 使用AT+EL=0000配置为长期休眠(波特率需要>9600)
  288. * TVS SMBJ6.8(C)A 0-5uA TVS 从3.3-4.2V,漏电流会随电压在0-5uA线性变化
  289. *
  290. *
  291. * 当前待机模式下整机功耗 4-12uA浮动
  292. * 去除TVS下 Loar版4-6uA LTE版3-5uA
  293. *
  294. */
  295. // 进入待机模式, 此时所有1.8V域的时钟都关闭,HIS和HSE的振荡器关闭, 电压调节器关闭.
  296. // 只有WKUP引脚上升沿,RTC警告事件,NRST引脚的外部复位,IWDG复位.
  297. HAL_PWR_EnterSTANDBYMode(); //进入待机模式 按道理立即进入休眠模式 类似断电
  298. //低功耗进入失败? 复位试试吧
  299. RESET_MCU();
  300. rt_hw_cpu_reset();
  301. rt_thread_mdelay(2000);
  302. #endif
  303. return 0;
  304. }
  305. // void run_standby(void)
  306. //{
  307. // inter_standby(2);
  308. // }
  309. //
  310. // MSH_CMD_EXPORT(run_standby, run_standby);
  311. /***************** 警戒线程 ********************/
  312. /*
  313. 不论终端进行任何操作
  314. 只要运行在低功耗模式且通过闹钟中断回调函数唤醒
  315. 3分钟后必然进入低功耗模式
  316. 确保终端出现问题时无法进入低功耗而耗尽电量下线
  317. */
  318. /***************** 警戒线程 ********************/
  319. extern void All_standby(uint16_t stime, uint8_t level);
  320. extern uint8_t Start_LTE;
  321. volatile uint8_t Expand_Guard_T = Zero; //扩容警戒线程时间
  322. char guard_head[] = "Guard:%d/%d\n";
  323. static void Guard_thread_entry(void *param)
  324. {
  325. static uint16_t count = 0; // while计数
  326. static uint16_t timeout = 0;
  327. static uint8_t ET_EN = 1; //1:允许扩容 0:扩容完成
  328. static uint8_t Add_Sen_Time = 1; // 加入一次要素延时
  329. //rt_thread_mdelay(200);
  330. if (wwdg_state == Zero)
  331. {
  332. MX_WWDG_Init(); //初始化看门狗
  333. wwdg_state = One;
  334. rt_kprintf("wwdg start\n");
  335. HAL_WWDG_Refresh(&hwwdg);
  336. }
  337. timeout = 1020; //加入了定位这个时间+60秒
  338. while (1) // Guard
  339. {
  340. if (wwdg_state == One)
  341. {
  342. HAL_WWDG_Refresh(&hwwdg);//喂狗
  343. }
  344. if(sys_info.Sensor_num <= MAX_SENSOR_NUM && sys_info.Sensor_num > 0) // 给读传感器留些时间 有传感器
  345. {
  346. if(Add_Sen_Time == 1)
  347. {
  348. timeout = timeout + (60 * sys_info.Sensor_num); //820 + 60*num
  349. Add_Sen_Time = 0;
  350. //rt_kprintf("Guard timeout: %d\n",timeout);
  351. }
  352. }
  353. if (sys_info.run_mode == Zero)//供电正常
  354. {
  355. count += 1; //
  356. if (count > timeout)
  357. {
  358. LOG_E("boot time ↓");
  359. Recode_e_code(20 , count/100);
  360. All_standby(sys_info.interval, 3);//满足条件进行1.关闭4G供电 2.保存参数3.立即休眠
  361. }
  362. }
  363. // 喂狗
  364. if (wwdg_state == One)
  365. {
  366. HAL_WWDG_Refresh(&hwwdg);
  367. }
  368. //打印信息
  369. if (count % 100 == 0 && count != 0 && count >= timeout / 2 /*&& Node_Type != Monitor*/)
  370. {
  371. if (count * 2 >= timeout * 3)
  372. LOG_W(guard_head, count, timeout);// 说明没正常进入休眠
  373. else
  374. LOG_I(guard_head, count, timeout);// count >= timeout / 2 and count是一百的倍数
  375. }
  376. rt_thread_mdelay(300); //每秒一次 让出CPU
  377. //喂狗
  378. if (wwdg_state == One)
  379. {
  380. HAL_WWDG_Refresh(&hwwdg);
  381. }
  382. if(Expand_Guard_T == One && ET_EN == 1)//也就第一次可能进入
  383. {
  384. ET_EN = 0;
  385. Expand_Guard_T = Zero;
  386. timeout += 6600;
  387. }
  388. }
  389. }
  390. //
  391. int Guard_thread(void)
  392. {
  393. rt_thread_t tid1 = RT_NULL;
  394. tid1 = rt_thread_create("Guard",
  395. Guard_thread_entry, RT_NULL,
  396. 1024,
  397. 6, 5); //给足优先级
  398. // 如果获得线程控制块,启动这个线程
  399. if (tid1 != RT_NULL)
  400. rt_thread_startup(tid1);
  401. return 1;
  402. }
  403. INIT_ENV_EXPORT(Guard_thread); //警戒线程 保证进入低功耗的最后一道防线