1
0

alarm.c 16 KB

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