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 * 2006-03-12 Bernard first version 9 * 2006-04-29 Bernard implement thread timer 10 * 2006-06-04 Bernard implement rt_timer_control 11 * 2006-08-10 Bernard fix the periodic timer bug 12 * 2006-09-03 Bernard implement rt_timer_detach 13 * 2009-11-11 LiJin add soft timer 14 * 2010-05-12 Bernard fix the timer check bug. 15 * 2010-11-02 Charlie re-implement tick overflow issue 16 * 2012-12-15 Bernard fix the next timeout issue in soft timer 17 * 2014-07-12 Bernard does not lock scheduler when invoking soft-timer 18 * timeout function. 19 */ 20 21 #include <rtthread.h> 22 #include <rthw.h> 23 24 /* hard timer list */ 25 static rt_list_t rt_timer_list[RT_TIMER_SKIP_LIST_LEVEL]; 26 27 #ifdef RT_USING_TIMER_SOFT 28 #ifndef RT_TIMER_THREAD_STACK_SIZE 29 #define RT_TIMER_THREAD_STACK_SIZE 512 30 #endif 31 32 #ifndef RT_TIMER_THREAD_PRIO 33 #define RT_TIMER_THREAD_PRIO 0 34 #endif 35 36 /* soft timer list */ 37 static rt_list_t rt_soft_timer_list[RT_TIMER_SKIP_LIST_LEVEL]; 38 static struct rt_thread timer_thread; 39 ALIGN(RT_ALIGN_SIZE) 40 static rt_uint8_t timer_thread_stack[RT_TIMER_THREAD_STACK_SIZE]; 41 #endif 42 43 #ifdef RT_USING_HOOK 44 extern void (*rt_object_take_hook)(struct rt_object *object); 45 extern void (*rt_object_put_hook)(struct rt_object *object); 46 static void (*rt_timer_enter_hook)(struct rt_timer *timer); 47 static void (*rt_timer_exit_hook)(struct rt_timer *timer); 48 49 /** 50 * @addtogroup Hook 51 */ 52 53 /**@{*/ 54 55 /** 56 * This function will set a hook function, which will be invoked when enter 57 * timer timeout callback function. 58 * 59 * @param hook the hook function 60 */ 61 void rt_timer_enter_sethook(void (*hook)(struct rt_timer *timer)) 62 { 63 rt_timer_enter_hook = hook; 64 } 65 66 /** 67 * This function will set a hook function, which will be invoked when exit 68 * timer timeout callback function. 69 * 70 * @param hook the hook function 71 */ 72 void rt_timer_exit_sethook(void (*hook)(struct rt_timer *timer)) 73 { 74 rt_timer_exit_hook = hook; 75 } 76 77 /**@}*/ 78 #endif 79 80 static void _rt_timer_init(rt_timer_t timer, 81 void (*timeout)(void *parameter), 82 void *parameter, 83 rt_tick_t time, 84 rt_uint8_t flag) 85 { 86 int i; 87 88 /* set flag */ 89 timer->parent.flag = flag; 90 91 /* set deactivated */ 92 timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED; 93 94 timer->timeout_func = timeout; 95 timer->parameter = parameter; 96 97 timer->timeout_tick = 0; 98 timer->init_tick = time; 99 100 /* initialize timer list */ 101 for (i = 0; i < RT_TIMER_SKIP_LIST_LEVEL; i++) 102 { 103 rt_list_init(&(timer->row[i])); 104 } 105 } 106 107 /* the fist timer always in the last row */ 108 static rt_tick_t rt_timer_list_next_timeout(rt_list_t timer_list[]) 109 { 110 struct rt_timer *timer; 111 112 if (rt_list_isempty(&timer_list[RT_TIMER_SKIP_LIST_LEVEL - 1])) 113 return RT_TICK_MAX; 114 115 timer = rt_list_entry(timer_list[RT_TIMER_SKIP_LIST_LEVEL - 1].next, 116 struct rt_timer, row[RT_TIMER_SKIP_LIST_LEVEL - 1]); 117 118 return timer->timeout_tick; 119 } 120 121 rt_inline void _rt_timer_remove(rt_timer_t timer) 122 { 123 int i; 124 125 for (i = 0; i < RT_TIMER_SKIP_LIST_LEVEL; i++) 126 { 127 rt_list_remove(&timer->row[i]); 128 } 129 } 130 131 #if RT_DEBUG_TIMER 132 static int rt_timer_count_height(struct rt_timer *timer) 133 { 134 int i, cnt = 0; 135 136 for (i = 0; i < RT_TIMER_SKIP_LIST_LEVEL; i++) 137 { 138 if (!rt_list_isempty(&timer->row[i])) 139 cnt++; 140 } 141 return cnt; 142 } 143 144 void rt_timer_dump(rt_list_t timer_heads[]) 145 { 146 rt_list_t *list; 147 148 for (list = timer_heads[RT_TIMER_SKIP_LIST_LEVEL - 1].next; 149 list != &timer_heads[RT_TIMER_SKIP_LIST_LEVEL - 1]; 150 list = list->next) 151 { 152 struct rt_timer *timer = rt_list_entry(list, 153 struct rt_timer, 154 row[RT_TIMER_SKIP_LIST_LEVEL - 1]); 155 rt_kprintf("%d", rt_timer_count_height(timer)); 156 } 157 rt_kprintf("\n"); 158 } 159 #endif 160 161 /** 162 * @addtogroup Clock 163 */ 164 165 /**@{*/ 166 167 /** 168 * This function will initialize a timer, normally this function is used to 169 * initialize a static timer object. 170 * 171 * @param timer the static timer object 172 * @param name the name of timer 173 * @param timeout the timeout function 174 * @param parameter the parameter of timeout function 175 * @param time the tick of timer 176 * @param flag the flag of timer 177 */ 178 void rt_timer_init(rt_timer_t timer, 179 const char *name, 180 void (*timeout)(void *parameter), 181 void *parameter, 182 rt_tick_t time, 183 rt_uint8_t flag) 184 { 185 /* timer check */ 186 RT_ASSERT(timer != RT_NULL); 187 188 /* timer object initialization */ 189 rt_object_init((rt_object_t)timer, RT_Object_Class_Timer, name); 190 191 _rt_timer_init(timer, timeout, parameter, time, flag); 192 } 193 RTM_EXPORT(rt_timer_init); 194 195 /** 196 * This function will detach a timer from timer management. 197 * 198 * @param timer the static timer object 199 * 200 * @return the operation status, RT_EOK on OK; RT_ERROR on error 201 */ 202 rt_err_t rt_timer_detach(rt_timer_t timer) 203 { 204 register rt_base_t level; 205 206 /* timer check */ 207 RT_ASSERT(timer != RT_NULL); 208 RT_ASSERT(rt_object_get_type(&timer->parent) == RT_Object_Class_Timer); 209 RT_ASSERT(rt_object_is_systemobject(&timer->parent)); 210 211 /* disable interrupt */ 212 level = rt_hw_interrupt_disable(); 213 214 _rt_timer_remove(timer); 215 216 /* enable interrupt */ 217 rt_hw_interrupt_enable(level); 218 219 rt_object_detach((rt_object_t)timer); 220 221 return RT_EOK; 222 } 223 RTM_EXPORT(rt_timer_detach); 224 225 #ifdef RT_USING_HEAP 226 /** 227 * This function will create a timer 228 * 229 * @param name the name of timer 230 * @param timeout the timeout function 231 * @param parameter the parameter of timeout function 232 * @param time the tick of timer 233 * @param flag the flag of timer 234 * 235 * @return the created timer object 236 */ 237 rt_timer_t rt_timer_create(const char *name, 238 void (*timeout)(void *parameter), 239 void *parameter, 240 rt_tick_t time, 241 rt_uint8_t flag) 242 { 243 struct rt_timer *timer; 244 245 /* allocate a object */ 246 timer = (struct rt_timer *)rt_object_allocate(RT_Object_Class_Timer, name); 247 if (timer == RT_NULL) 248 { 249 return RT_NULL; 250 } 251 252 _rt_timer_init(timer, timeout, parameter, time, flag); 253 254 return timer; 255 } 256 RTM_EXPORT(rt_timer_create); 257 258 /** 259 * This function will delete a timer and release timer memory 260 * 261 * @param timer the timer to be deleted 262 * 263 * @return the operation status, RT_EOK on OK; RT_ERROR on error 264 */ 265 rt_err_t rt_timer_delete(rt_timer_t timer) 266 { 267 register rt_base_t level; 268 269 /* timer check */ 270 RT_ASSERT(timer != RT_NULL); 271 RT_ASSERT(rt_object_get_type(&timer->parent) == RT_Object_Class_Timer); 272 RT_ASSERT(rt_object_is_systemobject(&timer->parent) == RT_FALSE); 273 274 /* disable interrupt */ 275 level = rt_hw_interrupt_disable(); 276 277 _rt_timer_remove(timer); 278 279 /* enable interrupt */ 280 rt_hw_interrupt_enable(level); 281 282 rt_object_delete((rt_object_t)timer); 283 284 return RT_EOK; 285 } 286 RTM_EXPORT(rt_timer_delete); 287 #endif 288 289 /** 290 * This function will start the timer 291 * 292 * @param timer the timer to be started 293 * 294 * @return the operation status, RT_EOK on OK, -RT_ERROR on error 295 */ 296 rt_err_t rt_timer_start(rt_timer_t timer) 297 { 298 unsigned int row_lvl; 299 rt_list_t *timer_list; 300 register rt_base_t level; 301 rt_list_t *row_head[RT_TIMER_SKIP_LIST_LEVEL]; 302 unsigned int tst_nr; 303 static unsigned int random_nr; 304 305 /* timer check */ 306 RT_ASSERT(timer != RT_NULL); 307 RT_ASSERT(rt_object_get_type(&timer->parent) == RT_Object_Class_Timer); 308 309 /* stop timer firstly */ 310 level = rt_hw_interrupt_disable(); 311 /* remove timer from list */ 312 _rt_timer_remove(timer); 313 /* change status of timer */ 314 timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED; 315 rt_hw_interrupt_enable(level); 316 317 RT_OBJECT_HOOK_CALL(rt_object_take_hook, (&(timer->parent))); 318 319 /* 320 * get timeout tick, 321 * the max timeout tick shall not great than RT_TICK_MAX/2 322 */ 323 RT_ASSERT(timer->init_tick < RT_TICK_MAX / 2); 324 timer->timeout_tick = rt_tick_get() + timer->init_tick; 325 326 /* disable interrupt */ 327 level = rt_hw_interrupt_disable(); 328 329 #ifdef RT_USING_TIMER_SOFT 330 if (timer->parent.flag & RT_TIMER_FLAG_SOFT_TIMER) 331 { 332 /* insert timer to soft timer list */ 333 timer_list = rt_soft_timer_list; 334 } 335 else 336 #endif 337 { 338 /* insert timer to system timer list */ 339 timer_list = rt_timer_list; 340 } 341 342 row_head[0] = &timer_list[0]; 343 for (row_lvl = 0; row_lvl < RT_TIMER_SKIP_LIST_LEVEL; row_lvl++) 344 { 345 for (; row_head[row_lvl] != timer_list[row_lvl].prev; 346 row_head[row_lvl] = row_head[row_lvl]->next) 347 { 348 struct rt_timer *t; 349 rt_list_t *p = row_head[row_lvl]->next; 350 351 /* fix up the entry pointer */ 352 t = rt_list_entry(p, struct rt_timer, row[row_lvl]); 353 354 /* If we have two timers that timeout at the same time, it's 355 * preferred that the timer inserted early get called early. 356 * So insert the new timer to the end the the some-timeout timer 357 * list. 358 */ 359 if ((t->timeout_tick - timer->timeout_tick) == 0) 360 { 361 continue; 362 } 363 else if ((t->timeout_tick - timer->timeout_tick) < RT_TICK_MAX / 2) 364 { 365 break; 366 } 367 } 368 if (row_lvl != RT_TIMER_SKIP_LIST_LEVEL - 1) 369 row_head[row_lvl + 1] = row_head[row_lvl] + 1; 370 } 371 372 /* Interestingly, this super simple timer insert counter works very very 373 * well on distributing the list height uniformly. By means of "very very 374 * well", I mean it beats the randomness of timer->timeout_tick very easily 375 * (actually, the timeout_tick is not random and easy to be attacked). */ 376 random_nr++; 377 tst_nr = random_nr; 378 379 rt_list_insert_after(row_head[RT_TIMER_SKIP_LIST_LEVEL - 1], 380 &(timer->row[RT_TIMER_SKIP_LIST_LEVEL - 1])); 381 for (row_lvl = 2; row_lvl <= RT_TIMER_SKIP_LIST_LEVEL; row_lvl++) 382 { 383 if (!(tst_nr & RT_TIMER_SKIP_LIST_MASK)) 384 rt_list_insert_after(row_head[RT_TIMER_SKIP_LIST_LEVEL - row_lvl], 385 &(timer->row[RT_TIMER_SKIP_LIST_LEVEL - row_lvl])); 386 else 387 break; 388 /* Shift over the bits we have tested. Works well with 1 bit and 2 389 * bits. */ 390 tst_nr >>= (RT_TIMER_SKIP_LIST_MASK + 1) >> 1; 391 } 392 393 timer->parent.flag |= RT_TIMER_FLAG_ACTIVATED; 394 395 /* enable interrupt */ 396 rt_hw_interrupt_enable(level); 397 398 #ifdef RT_USING_TIMER_SOFT 399 if (timer->parent.flag & RT_TIMER_FLAG_SOFT_TIMER) 400 { 401 /* check whether timer thread is ready */ 402 if ((timer_thread.stat & RT_THREAD_STAT_MASK) == RT_THREAD_SUSPEND) 403 { 404 /* resume timer thread to check soft timer */ 405 rt_thread_resume(&timer_thread); 406 rt_schedule(); 407 } 408 } 409 #endif 410 411 return RT_EOK; 412 } 413 RTM_EXPORT(rt_timer_start); 414 415 /** 416 * This function will stop the timer 417 * 418 * @param timer the timer to be stopped 419 * 420 * @return the operation status, RT_EOK on OK, -RT_ERROR on error 421 */ 422 rt_err_t rt_timer_stop(rt_timer_t timer) 423 { 424 register rt_base_t level; 425 426 /* timer check */ 427 RT_ASSERT(timer != RT_NULL); 428 RT_ASSERT(rt_object_get_type(&timer->parent) == RT_Object_Class_Timer); 429 430 if (!(timer->parent.flag & RT_TIMER_FLAG_ACTIVATED)) 431 return -RT_ERROR; 432 433 RT_OBJECT_HOOK_CALL(rt_object_put_hook, (&(timer->parent))); 434 435 /* disable interrupt */ 436 level = rt_hw_interrupt_disable(); 437 438 _rt_timer_remove(timer); 439 440 /* enable interrupt */ 441 rt_hw_interrupt_enable(level); 442 443 /* change stat */ 444 timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED; 445 446 return RT_EOK; 447 } 448 RTM_EXPORT(rt_timer_stop); 449 450 /** 451 * This function will get or set some options of the timer 452 * 453 * @param timer the timer to be get or set 454 * @param cmd the control command 455 * @param arg the argument 456 * 457 * @return RT_EOK 458 */ 459 rt_err_t rt_timer_control(rt_timer_t timer, int cmd, void *arg) 460 { 461 /* timer check */ 462 RT_ASSERT(timer != RT_NULL); 463 RT_ASSERT(rt_object_get_type(&timer->parent) == RT_Object_Class_Timer); 464 465 switch (cmd) 466 { 467 case RT_TIMER_CTRL_GET_TIME: 468 *(rt_tick_t *)arg = timer->init_tick; 469 break; 470 471 case RT_TIMER_CTRL_SET_TIME: 472 timer->init_tick = *(rt_tick_t *)arg; 473 break; 474 475 case RT_TIMER_CTRL_SET_ONESHOT: 476 timer->parent.flag &= ~RT_TIMER_FLAG_PERIODIC; 477 break; 478 479 case RT_TIMER_CTRL_SET_PERIODIC: 480 timer->parent.flag |= RT_TIMER_FLAG_PERIODIC; 481 break; 482 } 483 484 return RT_EOK; 485 } 486 RTM_EXPORT(rt_timer_control); 487 488 /** 489 * This function will check timer list, if a timeout event happens, the 490 * corresponding timeout function will be invoked. 491 * 492 * @note this function shall be invoked in operating system timer interrupt. 493 */ 494 void rt_timer_check(void) 495 { 496 struct rt_timer *t; 497 rt_tick_t current_tick; 498 register rt_base_t level; 499 500 RT_DEBUG_LOG(RT_DEBUG_TIMER, ("timer check enter\n")); 501 502 current_tick = rt_tick_get(); 503 504 /* disable interrupt */ 505 level = rt_hw_interrupt_disable(); 506 507 while (!rt_list_isempty(&rt_timer_list[RT_TIMER_SKIP_LIST_LEVEL - 1])) 508 { 509 t = rt_list_entry(rt_timer_list[RT_TIMER_SKIP_LIST_LEVEL - 1].next, 510 struct rt_timer, row[RT_TIMER_SKIP_LIST_LEVEL - 1]); 511 512 /* 513 * It supposes that the new tick shall less than the half duration of 514 * tick max. 515 */ 516 if ((current_tick - t->timeout_tick) < RT_TICK_MAX / 2) 517 { 518 RT_OBJECT_HOOK_CALL(rt_timer_enter_hook, (t)); 519 520 /* remove timer from timer list firstly */ 521 _rt_timer_remove(t); 522 523 /* call timeout function */ 524 t->timeout_func(t->parameter); 525 526 /* re-get tick */ 527 current_tick = rt_tick_get(); 528 529 RT_OBJECT_HOOK_CALL(rt_timer_exit_hook, (t)); 530 RT_DEBUG_LOG(RT_DEBUG_TIMER, ("current tick: %d\n", current_tick)); 531 532 if ((t->parent.flag & RT_TIMER_FLAG_PERIODIC) && 533 (t->parent.flag & RT_TIMER_FLAG_ACTIVATED)) 534 { 535 /* start it */ 536 t->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED; 537 rt_timer_start(t); 538 } 539 else 540 { 541 /* stop timer */ 542 t->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED; 543 } 544 } 545 else 546 break; 547 } 548 549 /* enable interrupt */ 550 rt_hw_interrupt_enable(level); 551 552 RT_DEBUG_LOG(RT_DEBUG_TIMER, ("timer check leave\n")); 553 } 554 555 /** 556 * This function will return the next timeout tick in the system. 557 * 558 * @return the next timeout tick in the system 559 */ 560 rt_tick_t rt_timer_next_timeout_tick(void) 561 { 562 return rt_timer_list_next_timeout(rt_timer_list); 563 } 564 565 #ifdef RT_USING_TIMER_SOFT 566 /** 567 * This function will check timer list, if a timeout event happens, the 568 * corresponding timeout function will be invoked. 569 */ 570 void rt_soft_timer_check(void) 571 { 572 rt_tick_t current_tick; 573 rt_list_t *n; 574 struct rt_timer *t; 575 576 RT_DEBUG_LOG(RT_DEBUG_TIMER, ("software timer check enter\n")); 577 578 current_tick = rt_tick_get(); 579 580 /* lock scheduler */ 581 rt_enter_critical(); 582 583 for (n = rt_soft_timer_list[RT_TIMER_SKIP_LIST_LEVEL - 1].next; 584 n != &(rt_soft_timer_list[RT_TIMER_SKIP_LIST_LEVEL - 1]);) 585 { 586 t = rt_list_entry(n, struct rt_timer, row[RT_TIMER_SKIP_LIST_LEVEL - 1]); 587 588 /* 589 * It supposes that the new tick shall less than the half duration of 590 * tick max. 591 */ 592 if ((current_tick - t->timeout_tick) < RT_TICK_MAX / 2) 593 { 594 RT_OBJECT_HOOK_CALL(rt_timer_enter_hook, (t)); 595 596 /* move node to the next */ 597 n = n->next; 598 599 /* remove timer from timer list firstly */ 600 _rt_timer_remove(t); 601 602 /* not lock scheduler when performing timeout function */ 603 rt_exit_critical(); 604 /* call timeout function */ 605 t->timeout_func(t->parameter); 606 607 /* re-get tick */ 608 current_tick = rt_tick_get(); 609 610 RT_OBJECT_HOOK_CALL(rt_timer_exit_hook, (t)); 611 RT_DEBUG_LOG(RT_DEBUG_TIMER, ("current tick: %d\n", current_tick)); 612 613 /* lock scheduler */ 614 rt_enter_critical(); 615 616 if ((t->parent.flag & RT_TIMER_FLAG_PERIODIC) && 617 (t->parent.flag & RT_TIMER_FLAG_ACTIVATED)) 618 { 619 /* start it */ 620 t->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED; 621 rt_timer_start(t); 622 } 623 else 624 { 625 /* stop timer */ 626 t->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED; 627 } 628 } 629 else break; /* not check anymore */ 630 } 631 632 /* unlock scheduler */ 633 rt_exit_critical(); 634 635 RT_DEBUG_LOG(RT_DEBUG_TIMER, ("software timer check leave\n")); 636 } 637 638 /* system timer thread entry */ 639 static void rt_thread_timer_entry(void *parameter) 640 { 641 rt_tick_t next_timeout; 642 643 while (1) 644 { 645 /* get the next timeout tick */ 646 next_timeout = rt_timer_list_next_timeout(rt_soft_timer_list); 647 if (next_timeout == RT_TICK_MAX) 648 { 649 /* no software timer exist, suspend self. */ 650 rt_thread_suspend(rt_thread_self()); 651 rt_schedule(); 652 } 653 else 654 { 655 rt_tick_t current_tick; 656 657 /* get current tick */ 658 current_tick = rt_tick_get(); 659 660 if ((next_timeout - current_tick) < RT_TICK_MAX / 2) 661 { 662 /* get the delta timeout tick */ 663 next_timeout = next_timeout - current_tick; 664 rt_thread_delay(next_timeout); 665 } 666 } 667 668 /* check software timer */ 669 rt_soft_timer_check(); 670 } 671 } 672 #endif 673 674 /** 675 * @ingroup SystemInit 676 * 677 * This function will initialize system timer 678 */ 679 void rt_system_timer_init(void) 680 { 681 int i; 682 683 for (i = 0; i < sizeof(rt_timer_list) / sizeof(rt_timer_list[0]); i++) 684 { 685 rt_list_init(rt_timer_list + i); 686 } 687 } 688 689 /** 690 * @ingroup SystemInit 691 * 692 * This function will initialize system timer thread 693 */ 694 void rt_system_timer_thread_init(void) 695 { 696 #ifdef RT_USING_TIMER_SOFT 697 int i; 698 699 for (i = 0; 700 i < sizeof(rt_soft_timer_list) / sizeof(rt_soft_timer_list[0]); 701 i++) 702 { 703 rt_list_init(rt_soft_timer_list + i); 704 } 705 706 /* start software timer thread */ 707 rt_thread_init(&timer_thread, 708 "timer", 709 rt_thread_timer_entry, 710 RT_NULL, 711 &timer_thread_stack[0], 712 sizeof(timer_thread_stack), 713 RT_TIMER_THREAD_PRIO, 714 10); 715 716 /* startup */ 717 rt_thread_startup(&timer_thread); 718 #endif 719 } 720 721 /**@}*/ 722