alarm.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609
  1. /*
  2. * Copyright (c) 2006-2018, RT-Thread Development Team
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. *
  6. * Change Logs:
  7. * Date Author Notes
  8. * 2012-10-27 heyuanjie87 first version.
  9. * 2013-05-17 aozima initial alarm event & mutex in system init.
  10. */
  11. #include <rtthread.h>
  12. #include <rtdevice.h>
  13. #define RT_RTC_YEARS_MAX 137
  14. #define RT_ALARM_DELAY 2
  15. #define RT_ALARM_STATE_INITED 0x02
  16. #define RT_ALARM_STATE_START 0x01
  17. #define RT_ALARM_STATE_STOP 0x00
  18. #if (defined(RT_USING_RTC) && defined(RT_USING_ALARM))
  19. static struct rt_alarm_container _container;
  20. rt_inline rt_uint32_t alarm_mkdaysec(struct tm *time)
  21. {
  22. rt_uint32_t sec;
  23. sec = time->tm_sec;
  24. sec += time->tm_min * 60;
  25. sec += time->tm_hour * 3600;
  26. return (sec);
  27. }
  28. static rt_err_t alarm_set(struct rt_alarm *alarm)
  29. {
  30. rt_device_t device;
  31. struct rt_rtc_wkalarm wkalarm;
  32. rt_err_t ret;
  33. device = rt_device_find("rtc");
  34. if (device == RT_NULL)
  35. {
  36. return (RT_ERROR);
  37. }
  38. if (alarm->flag & RT_ALARM_STATE_START)
  39. wkalarm.enable = RT_TRUE;
  40. else
  41. wkalarm.enable = RT_FALSE;
  42. wkalarm.tm_sec = alarm->wktime.tm_sec;
  43. wkalarm.tm_min = alarm->wktime.tm_min;
  44. wkalarm.tm_hour = alarm->wktime.tm_hour;
  45. ret = rt_device_control(device, RT_DEVICE_CTRL_RTC_SET_ALARM, &wkalarm);
  46. if ((ret == RT_EOK) && wkalarm.enable)
  47. {
  48. ret = rt_device_control(device, RT_DEVICE_CTRL_RTC_GET_ALARM, &wkalarm);
  49. if (ret == RT_EOK)
  50. {
  51. /*
  52. some RTC device like RX8025,it's alarms precision is 1 minute.
  53. in this case,low level RTC driver should set wkalarm->tm_sec to 0.
  54. */
  55. alarm->wktime.tm_sec = wkalarm.tm_sec;
  56. alarm->wktime.tm_min = wkalarm.tm_min;
  57. alarm->wktime.tm_hour = wkalarm.tm_hour;
  58. }
  59. }
  60. return (ret);
  61. }
  62. static void alarm_wakeup(struct rt_alarm *alarm, struct tm *now)
  63. {
  64. rt_uint32_t sec_alarm, sec_now;
  65. rt_bool_t wakeup = RT_FALSE;
  66. time_t timestamp;
  67. sec_alarm = alarm_mkdaysec(&alarm->wktime);
  68. sec_now = alarm_mkdaysec(now);
  69. if (alarm->flag & RT_ALARM_STATE_START)
  70. {
  71. switch (alarm->flag & 0xFF00)
  72. {
  73. case RT_ALARM_ONESHOT:
  74. {
  75. sec_alarm = mktime(&alarm->wktime);
  76. sec_now = mktime(now);
  77. if (((sec_now - sec_alarm) <= RT_ALARM_DELAY) && (sec_now >= sec_alarm))
  78. {
  79. /* stop alarm */
  80. alarm->flag &= ~RT_ALARM_STATE_START;
  81. alarm_set(alarm);
  82. wakeup = RT_TRUE;
  83. }
  84. }
  85. break;
  86. case RT_ALARM_DAILY:
  87. {
  88. if (((sec_now - sec_alarm) <= RT_ALARM_DELAY) && (sec_now >= sec_alarm))
  89. wakeup = RT_TRUE;
  90. }
  91. break;
  92. case RT_ALARM_WEEKLY:
  93. {
  94. /* alarm at wday */
  95. sec_alarm += alarm->wktime.tm_wday * 24 * 3600;
  96. sec_now += now->tm_wday * 24 * 3600;
  97. if (((sec_now - sec_alarm) <= RT_ALARM_DELAY) && (sec_now >= sec_alarm))
  98. wakeup = RT_TRUE;
  99. }
  100. break;
  101. case RT_ALARM_MONTHLY:
  102. {
  103. /* monthly someday generate alarm signals */
  104. if (alarm->wktime.tm_mday == now->tm_mday)
  105. {
  106. if ((sec_now - sec_alarm) <= RT_ALARM_DELAY)
  107. wakeup = RT_TRUE;
  108. }
  109. }
  110. break;
  111. case RT_ALARM_YAERLY:
  112. {
  113. if ((alarm->wktime.tm_mday == now->tm_mday) && \
  114. (alarm->wktime.tm_mon == now->tm_mon))
  115. {
  116. if ((sec_now - sec_alarm) <= RT_ALARM_DELAY)
  117. wakeup = RT_TRUE;
  118. }
  119. }
  120. break;
  121. }
  122. if ((wakeup == RT_TRUE) && (alarm->callback != RT_NULL))
  123. {
  124. timestamp = time(RT_NULL);
  125. alarm->callback(alarm, timestamp);
  126. }
  127. }
  128. }
  129. static void alarm_update(rt_uint32_t event)
  130. {
  131. struct rt_alarm *alm_prev = RT_NULL, *alm_next = RT_NULL;
  132. struct rt_alarm *alarm;
  133. rt_int32_t sec_now, sec_alarm, sec_tmp;
  134. rt_int32_t sec_next = 24 * 3600, sec_prev = 0;
  135. time_t timestamp;
  136. struct tm now;
  137. rt_list_t *next;
  138. rt_mutex_take(&_container.mutex, RT_WAITING_FOREVER);
  139. if (!rt_list_isempty(&_container.head))
  140. {
  141. /* get time of now */
  142. timestamp = time(RT_NULL);
  143. localtime_r(&timestamp, &now);
  144. for (next = _container.head.next; next != &_container.head; next = next->next)
  145. {
  146. alarm = rt_list_entry(next, struct rt_alarm, list);
  147. /* check the overtime alarm */
  148. alarm_wakeup(alarm, &now);
  149. }
  150. timestamp = time(RT_NULL);
  151. localtime_r(&timestamp, &now);
  152. sec_now = alarm_mkdaysec(&now);
  153. for (next = _container.head.next; next != &_container.head; next = next->next)
  154. {
  155. alarm = rt_list_entry(next, struct rt_alarm, list);
  156. /* calculate seconds from 00:00:00 */
  157. sec_alarm = alarm_mkdaysec(&alarm->wktime);
  158. if ((alarm->flag & RT_ALARM_STATE_START) && (alarm != _container.current))
  159. {
  160. sec_tmp = sec_alarm - sec_now;
  161. if (sec_tmp > 0)
  162. {
  163. /* find alarm after now(now to 23:59:59) and the most recent */
  164. if (sec_tmp < sec_next)
  165. {
  166. sec_next = sec_tmp;
  167. alm_next = alarm;
  168. }
  169. }
  170. else
  171. {
  172. /* find alarm before now(00:00:00 to now) and furthest from now */
  173. if (sec_tmp < sec_prev)
  174. {
  175. sec_prev = sec_tmp;
  176. alm_prev = alarm;
  177. }
  178. }
  179. }
  180. }
  181. /* enable the alarm after now first */
  182. if (sec_next < 24 * 3600)
  183. {
  184. if (alarm_set(alm_next) == RT_EOK)
  185. _container.current = alm_next;
  186. }
  187. else if (sec_prev < 0)
  188. {
  189. /* enable the alarm before now */
  190. if (alarm_set(alm_prev) == RT_EOK)
  191. _container.current = alm_prev;
  192. }
  193. }
  194. rt_mutex_release(&_container.mutex);
  195. }
  196. static rt_uint32_t days_of_year_month(int tm_year, int tm_mon)
  197. {
  198. rt_uint32_t ret, year;
  199. year = tm_year + 1900;
  200. if (tm_mon == 1)
  201. {
  202. ret = 28 + ((!(year % 4) && (year % 100)) || !(year % 400));
  203. }
  204. else if (((tm_mon <= 6) && (tm_mon % 2 == 0)) || ((tm_mon > 6) && (tm_mon % 2 == 1)))
  205. {
  206. ret = 31;
  207. }
  208. else
  209. {
  210. ret = 30;
  211. }
  212. return (ret);
  213. }
  214. static rt_bool_t is_valid_date(struct tm *date)
  215. {
  216. if ((date->tm_year < 0) || (date->tm_year > RT_RTC_YEARS_MAX))
  217. {
  218. return (RT_FALSE);
  219. }
  220. if ((date->tm_mon < 0) || (date->tm_mon > 11))
  221. {
  222. return (RT_FALSE);
  223. }
  224. if ((date->tm_mday < 1) || \
  225. (date->tm_mday > days_of_year_month(date->tm_year, date->tm_mon)))
  226. {
  227. return (RT_FALSE);
  228. }
  229. return (RT_TRUE);
  230. }
  231. static rt_err_t alarm_setup(rt_alarm_t alarm, struct tm *wktime)
  232. {
  233. rt_err_t ret = RT_ERROR;
  234. time_t timestamp;
  235. struct tm *setup, now;
  236. setup = &alarm->wktime;
  237. *setup = *wktime;
  238. timestamp = time(RT_NULL);
  239. localtime_r(&timestamp, &now);
  240. /* if these are a "don't care" value,we set them to now*/
  241. if ((setup->tm_sec > 59) || (setup->tm_sec < 0))
  242. setup->tm_sec = now.tm_sec;
  243. if ((setup->tm_min > 59) || (setup->tm_min < 0))
  244. setup->tm_min = now.tm_min;
  245. if ((setup->tm_hour > 23) || (setup->tm_hour < 0))
  246. setup->tm_hour = now.tm_hour;
  247. switch (alarm->flag & 0xFF00)
  248. {
  249. case RT_ALARM_DAILY:
  250. {
  251. /* do nothing but needed */
  252. }
  253. break;
  254. case RT_ALARM_ONESHOT:
  255. {
  256. /* if these are "don't care" value we set them to now */
  257. if (setup->tm_year == RT_ALARM_TM_NOW)
  258. setup->tm_year = now.tm_year;
  259. if (setup->tm_mon == RT_ALARM_TM_NOW)
  260. setup->tm_mon = now.tm_mon;
  261. if (setup->tm_mday == RT_ALARM_TM_NOW)
  262. setup->tm_mday = now.tm_mday;
  263. /* make sure the setup is valid */
  264. if (!is_valid_date(setup))
  265. goto _exit;
  266. }
  267. break;
  268. case RT_ALARM_WEEKLY:
  269. {
  270. /* if tm_wday is a "don't care" value we set it to now */
  271. if ((setup->tm_wday < 0) || (setup->tm_wday > 6))
  272. setup->tm_wday = now.tm_wday;
  273. }
  274. break;
  275. case RT_ALARM_MONTHLY:
  276. {
  277. /* if tm_mday is a "don't care" value we set it to now */
  278. if ((setup->tm_mday < 1) || (setup->tm_mday > 31))
  279. setup->tm_mday = now.tm_mday;
  280. }
  281. break;
  282. case RT_ALARM_YAERLY:
  283. {
  284. /* if tm_mon is a "don't care" value we set it to now */
  285. if ((setup->tm_mon < 0) || (setup->tm_mon > 11))
  286. setup->tm_mon = now.tm_mon;
  287. if (setup->tm_mon == 1)
  288. {
  289. /* tm_mon is February */
  290. /* tm_mday should be 1~29.otherwise,it's a "don't care" value */
  291. if ((setup->tm_mday < 1) || (setup->tm_mday > 29))
  292. setup->tm_mday = now.tm_mday;
  293. }
  294. else if (((setup->tm_mon <= 6) && (setup->tm_mon % 2 == 0)) || \
  295. ((setup->tm_mon > 6) && (setup->tm_mon % 2 == 1)))
  296. {
  297. /* Jan,Mar,May,Jul,Aug,Oct,Dec */
  298. /* tm_mday should be 1~31.otherwise,it's a "don't care" value */
  299. if ((setup->tm_mday < 1) || (setup->tm_mday > 31))
  300. setup->tm_mday = now.tm_mday;
  301. }
  302. else
  303. {
  304. /* tm_mday should be 1~30.otherwise,it's a "don't care" value */
  305. if ((setup->tm_mday < 1) || (setup->tm_mday > 30))
  306. setup->tm_mday = now.tm_mday;
  307. }
  308. }
  309. break;
  310. default:
  311. {
  312. goto _exit;
  313. }
  314. }
  315. if ((setup->tm_hour == 23) && (setup->tm_min == 59) && (setup->tm_sec == 59))
  316. {
  317. /*
  318. for insurance purposes, we will generate an alarm
  319. signal two seconds ahead of.
  320. */
  321. setup->tm_sec = 60 - RT_ALARM_DELAY;
  322. }
  323. /* set initialized state */
  324. alarm->flag |= RT_ALARM_STATE_INITED;
  325. ret = RT_EOK;
  326. _exit:
  327. return (ret);
  328. }
  329. /** \brief send a rtc alarm event
  330. *
  331. * \param dev pointer to RTC device(currently unused,you can ignore it)
  332. * \param event RTC event(currently unused)
  333. * \return none
  334. */
  335. void rt_alarm_update(rt_device_t dev, rt_uint32_t event)
  336. {
  337. rt_event_send(&_container.event, 1);
  338. }
  339. /** \brief modify the alarm setup
  340. *
  341. * \param alarm pointer to alarm
  342. * \param cmd control command
  343. * \param arg argument
  344. */
  345. rt_err_t rt_alarm_control(rt_alarm_t alarm, int cmd, void *arg)
  346. {
  347. rt_err_t ret = RT_ERROR;
  348. RT_ASSERT(alarm != RT_NULL);
  349. rt_mutex_take(&_container.mutex, RT_WAITING_FOREVER);
  350. switch (cmd)
  351. {
  352. case RT_ALARM_CTRL_MODIFY:
  353. {
  354. struct rt_alarm_setup *setup;
  355. RT_ASSERT(arg != RT_NULL);
  356. setup = arg;
  357. rt_alarm_stop(alarm);
  358. alarm->flag = setup->flag & 0xFF00;
  359. alarm->wktime = setup->wktime;
  360. ret = alarm_setup(alarm, &alarm->wktime);
  361. }
  362. break;
  363. }
  364. rt_mutex_release(&_container.mutex);
  365. return (ret);
  366. }
  367. /** \brief start an alarm
  368. *
  369. * \param alarm pointer to alarm
  370. * \return RT_EOK
  371. */
  372. rt_err_t rt_alarm_start(rt_alarm_t alarm)
  373. {
  374. rt_int32_t sec_now, sec_old, sec_new;
  375. rt_err_t ret = RT_ERROR;
  376. time_t timestamp;
  377. struct tm now;
  378. if (alarm == RT_NULL)
  379. return (ret);
  380. rt_mutex_take(&_container.mutex, RT_WAITING_FOREVER);
  381. if (!(alarm->flag & RT_ALARM_STATE_INITED))
  382. {
  383. if (alarm_setup(alarm, &alarm->wktime) != RT_EOK)
  384. goto _exit;
  385. }
  386. if ((alarm->flag & 0x01) == RT_ALARM_STATE_STOP)
  387. {
  388. timestamp = time(RT_NULL);
  389. localtime_r(&timestamp, &now);
  390. alarm->flag |= RT_ALARM_STATE_START;
  391. /* set alarm */
  392. if (_container.current == RT_NULL)
  393. {
  394. ret = alarm_set(alarm);
  395. }
  396. else
  397. {
  398. sec_now = alarm_mkdaysec(&now);
  399. sec_old = alarm_mkdaysec(&_container.current->wktime);
  400. sec_new = alarm_mkdaysec(&alarm->wktime);
  401. if ((sec_new < sec_old) && (sec_new > sec_now))
  402. {
  403. ret = alarm_set(alarm);
  404. }
  405. else if ((sec_new > sec_now) && (sec_old < sec_now))
  406. {
  407. ret = alarm_set(alarm);
  408. }
  409. else if ((sec_new < sec_old) && (sec_old < sec_now))
  410. {
  411. ret = alarm_set(alarm);
  412. }
  413. else
  414. {
  415. ret = RT_EOK;
  416. goto _exit;
  417. }
  418. }
  419. if (ret == RT_EOK)
  420. {
  421. _container.current = alarm;
  422. }
  423. }
  424. _exit:
  425. rt_mutex_release(&_container.mutex);
  426. return (ret);
  427. }
  428. /** \brief stop an alarm
  429. *
  430. * \param alarm pointer to alarm
  431. * \return RT_EOK
  432. */
  433. rt_err_t rt_alarm_stop(rt_alarm_t alarm)
  434. {
  435. rt_err_t ret = RT_ERROR;
  436. if (alarm == RT_NULL)
  437. return (ret);
  438. rt_mutex_take(&_container.mutex, RT_WAITING_FOREVER);
  439. if (!(alarm->flag & RT_ALARM_STATE_START))
  440. goto _exit;
  441. /* stop alarm */
  442. alarm->flag &= ~RT_ALARM_STATE_START;
  443. if (_container.current == alarm)
  444. {
  445. ret = alarm_set(alarm);
  446. _container.current = RT_NULL;
  447. }
  448. if (ret == RT_EOK)
  449. alarm_update(0);
  450. _exit:
  451. rt_mutex_release(&_container.mutex);
  452. return (ret);
  453. }
  454. /** \brief delete an alarm
  455. *
  456. * \param alarm pointer to alarm
  457. * \return RT_EOK
  458. */
  459. rt_err_t rt_alarm_delete(rt_alarm_t alarm)
  460. {
  461. rt_err_t ret = RT_ERROR;
  462. if (alarm == RT_NULL)
  463. return (ret);
  464. rt_mutex_take(&_container.mutex, RT_WAITING_FOREVER);
  465. /* stop the alarm */
  466. alarm->flag &= ~RT_ALARM_STATE_START;
  467. if (_container.current == alarm)
  468. {
  469. ret = alarm_set(alarm);
  470. _container.current = RT_NULL;
  471. /* set new alarm if necessary */
  472. alarm_update(0);
  473. }
  474. rt_list_remove(&alarm->list);
  475. rt_free(alarm);
  476. rt_mutex_release(&_container.mutex);
  477. return (ret);
  478. }
  479. /** \brief create an alarm
  480. *
  481. * \param flag set alarm mode e.g: RT_ALARM_DAILY
  482. * \param setup pointer to setup infomation
  483. */
  484. rt_alarm_t rt_alarm_create(rt_alarm_callback_t callback, struct rt_alarm_setup *setup)
  485. {
  486. struct rt_alarm *alarm;
  487. if (setup == RT_NULL)
  488. return (RT_NULL);
  489. alarm = rt_malloc(sizeof(struct rt_alarm));
  490. if (alarm == RT_NULL)
  491. return (RT_NULL);
  492. rt_list_init(&alarm->list);
  493. alarm->wktime = setup->wktime;
  494. alarm->flag = setup->flag & 0xFF00;
  495. alarm->callback = callback;
  496. rt_mutex_take(&_container.mutex, RT_WAITING_FOREVER);
  497. rt_list_insert_after(&_container.head, &alarm->list);
  498. rt_mutex_release(&_container.mutex);
  499. return (alarm);
  500. }
  501. /** \brief rtc alarm service thread entry
  502. *
  503. */
  504. static void rt_alarmsvc_thread_init(void *param)
  505. {
  506. rt_uint32_t recv;
  507. _container.current = RT_NULL;
  508. while (1)
  509. {
  510. if (rt_event_recv(&_container.event, 0xFFFF,
  511. RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR,
  512. RT_WAITING_FOREVER, &recv) == RT_EOK)
  513. {
  514. alarm_update(recv);
  515. }
  516. }
  517. }
  518. /** \brief initialize alarm service system
  519. *
  520. * \param none
  521. * \return none
  522. */
  523. void rt_alarm_system_init(void)
  524. {
  525. rt_thread_t tid;
  526. rt_list_init(&_container.head);
  527. rt_event_init(&_container.event, "alarmsvc", RT_IPC_FLAG_FIFO);
  528. rt_mutex_init(&_container.mutex, "alarmsvc", RT_IPC_FLAG_FIFO);
  529. tid = rt_thread_create("alarmsvc",
  530. rt_alarmsvc_thread_init, RT_NULL,
  531. 512, 8, 1);
  532. if (tid != RT_NULL)
  533. rt_thread_startup(tid);
  534. }
  535. #endif