timer.c 15.5 KB
Newer Older
1 2 3
/*
 * File      : timer.c
 * This file is part of RT-Thread RTOS
D
dzzxzz 已提交
4
 * COPYRIGHT (C) 2006 - 2012, RT-Thread Development Team
5
 *
B
Bernard Xiong 已提交
6 7 8 9 10 11 12 13 14 15 16 17 18
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 20 21 22 23 24 25 26
 *
 * Change Logs:
 * Date           Author       Notes
 * 2006-03-12     Bernard      first version
 * 2006-04-29     Bernard      implement thread timer
 * 2006-06-04     Bernard      implement rt_timer_control
 * 2006-08-10     Bernard      fix the periodic timer bug
 * 2006-09-03     Bernard      implement rt_timer_detach
27
 * 2009-11-11     LiJin        add soft timer
28
 * 2010-05-12     Bernard      fix the timer check bug.
29
 * 2010-11-02     Charlie      re-implement tick overflow issue
30
 * 2012-12-15     Bernard      fix the next timeout issue in soft timer
31 32 33 34 35
 */

#include <rtthread.h>
#include <rthw.h>

36
/* hard timer list */
37
static rt_list_t rt_timer_list = RT_LIST_OBJECT_INIT(rt_timer_list);
38

39
#ifdef RT_USING_TIMER_SOFT
40
#ifndef RT_TIMER_THREAD_STACK_SIZE
D
dzzxzz@gmail.com 已提交
41
#define RT_TIMER_THREAD_STACK_SIZE     512
42 43 44
#endif

#ifndef RT_TIMER_THREAD_PRIO
D
dzzxzz@gmail.com 已提交
45
#define RT_TIMER_THREAD_PRIO           0
46 47
#endif

48
/* soft timer list */
49
static rt_list_t rt_soft_timer_list;
50 51 52
static struct rt_thread timer_thread;
ALIGN(RT_ALIGN_SIZE)
static rt_uint8_t timer_thread_stack[RT_TIMER_THREAD_STACK_SIZE];
53
#endif
54

55
#ifdef RT_USING_HOOK
D
dzzxzz 已提交
56 57 58
extern void (*rt_object_take_hook)(struct rt_object *object);
extern void (*rt_object_put_hook)(struct rt_object *object);
static void (*rt_timer_timeout_hook)(struct rt_timer *timer);
59 60 61 62

/**
 * @addtogroup Hook
 */
D
dzzxzz 已提交
63

64 65 66 67 68
/*@{*/

/**
 * This function will set a hook function, which will be invoked when timer
 * is timeout.
69
 *
70 71
 * @param hook the hook function
 */
D
dzzxzz 已提交
72
void rt_timer_timeout_sethook(void (*hook)(struct rt_timer *timer))
73
{
74
    rt_timer_timeout_hook = hook;
75 76 77 78 79
}

/*@}*/
#endif

80
static void _rt_timer_init(rt_timer_t timer,
D
dzzxzz@gmail.com 已提交
81 82 83 84
                           void (*timeout)(void *parameter),
                           void      *parameter,
                           rt_tick_t  time,
                           rt_uint8_t flag)
85
{
86 87
    /* set flag */
    timer->parent.flag  = flag;
88

89 90
    /* set deactivated */
    timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
91

92 93
    timer->timeout_func = timeout;
    timer->parameter    = parameter;
94

95 96
    timer->timeout_tick = 0;
    timer->init_tick    = time;
97

98 99
    /* initialize timer list */
    rt_list_init(&(timer->list));
100 101
}

D
dzzxzz@gmail.com 已提交
102
static rt_tick_t rt_timer_list_next_timeout(rt_list_t *timer_list)
103
{
104
    struct rt_timer *timer;
D
dzzxzz@gmail.com 已提交
105

106 107
    if (rt_list_isempty(timer_list))
        return RT_TICK_MAX;
B
Bernard Xiong 已提交
108

109
    timer = rt_list_entry(timer_list->next, struct rt_timer, list);
D
dzzxzz@gmail.com 已提交
110

111
    return timer->timeout_tick;
112 113
}

114 115 116
/**
 * @addtogroup Clock
 */
D
dzzxzz 已提交
117

118 119 120
/*@{*/

/**
121 122
 * This function will initialize a timer, normally this function is used to
 * initialize a static timer object.
123 124 125 126 127 128 129 130
 *
 * @param timer the static timer object
 * @param name the name of timer
 * @param timeout the timeout function
 * @param parameter the parameter of timeout function
 * @param time the tick of timer
 * @param flag the flag of timer
 */
D
dzzxzz@gmail.com 已提交
131
void rt_timer_init(rt_timer_t  timer,
132
                   const char *name,
D
dzzxzz@gmail.com 已提交
133 134 135 136
                   void (*timeout)(void *parameter),
                   void       *parameter,
                   rt_tick_t   time,
                   rt_uint8_t  flag)
137
{
138 139
    /* timer check */
    RT_ASSERT(timer != RT_NULL);
140

141 142
    /* timer object initialization */
    rt_object_init((rt_object_t)timer, RT_Object_Class_Timer, name);
143

144
    _rt_timer_init(timer, timeout, parameter, time, flag);
145
}
146
RTM_EXPORT(rt_timer_init);
147

148
/**
149 150 151
 * This function will detach a timer from timer management.
 *
 * @param timer the static timer object
152
 *
153 154 155 156
 * @return the operation status, RT_EOK on OK; RT_ERROR on error
 */
rt_err_t rt_timer_detach(rt_timer_t timer)
{
157
    register rt_base_t level;
158

159 160
    /* timer check */
    RT_ASSERT(timer != RT_NULL);
161

162 163
    /* disable interrupt */
    level = rt_hw_interrupt_disable();
164

165 166
    /* remove it from timer list */
    rt_list_remove(&(timer->list));
167

168 169
    /* enable interrupt */
    rt_hw_interrupt_enable(level);
170

171
    rt_object_detach((rt_object_t)timer);
172

173
    return -RT_EOK;
174
}
175
RTM_EXPORT(rt_timer_detach);
176 177 178 179 180 181 182 183 184 185 186 187 188

#ifdef RT_USING_HEAP
/**
 * This function will create a timer
 *
 * @param name the name of timer
 * @param timeout the timeout function
 * @param parameter the parameter of timeout function
 * @param time the tick of timer
 * @param flag the flag of timer
 *
 * @return the created timer object
 */
D
dzzxzz@gmail.com 已提交
189 190 191 192 193
rt_timer_t rt_timer_create(const char *name,
                           void (*timeout)(void *parameter),
                           void       *parameter,
                           rt_tick_t   time,
                           rt_uint8_t  flag)
194
{
195
    struct rt_timer *timer;
196

197 198 199 200 201 202
    /* allocate a object */
    timer = (struct rt_timer *)rt_object_allocate(RT_Object_Class_Timer, name);
    if (timer == RT_NULL)
    {
        return RT_NULL;
    }
203

204
    _rt_timer_init(timer, timeout, parameter, time, flag);
205

206
    return timer;
207
}
208
RTM_EXPORT(rt_timer_create);
209 210 211 212 213 214 215 216 217 218

/**
 * This function will delete a timer and release timer memory
 *
 * @param timer the timer to be deleted
 *
 * @return the operation status, RT_EOK on OK; RT_ERROR on error
 */
rt_err_t rt_timer_delete(rt_timer_t timer)
{
219
    register rt_base_t level;
220

221 222
    /* timer check */
    RT_ASSERT(timer != RT_NULL);
223

224 225
    /* disable interrupt */
    level = rt_hw_interrupt_disable();
226

227 228
    /* remove it from timer list */
    rt_list_remove(&(timer->list));
229

230 231
    /* enable interrupt */
    rt_hw_interrupt_enable(level);
232

233
    rt_object_delete((rt_object_t)timer);
234

235
    return -RT_EOK;
236
}
237
RTM_EXPORT(rt_timer_delete);
238 239 240 241 242 243 244
#endif

/**
 * This function will start the timer
 *
 * @param timer the timer to be started
 *
B
bernard.xiong@gmail.com 已提交
245
 * @return the operation status, RT_EOK on OK, -RT_ERROR on error
246 247 248
 */
rt_err_t rt_timer_start(rt_timer_t timer)
{
249 250 251
    struct rt_timer *t;
    register rt_base_t level;
    rt_list_t *n, *timer_list;
252

253 254 255 256
    /* timer check */
    RT_ASSERT(timer != RT_NULL);
    if (timer->parent.flag & RT_TIMER_FLAG_ACTIVATED)
        return -RT_ERROR;
257

258
    RT_OBJECT_HOOK_CALL(rt_object_take_hook, (&(timer->parent)));
259

D
dzzxzz@gmail.com 已提交
260 261 262 263 264
    /*
     * get timeout tick,
     * the max timeout tick shall not great than RT_TICK_MAX/2
     */
    RT_ASSERT(timer->init_tick < RT_TICK_MAX / 2);
265
    timer->timeout_tick = rt_tick_get() + timer->init_tick;
266

267 268
    /* disable interrupt */
    level = rt_hw_interrupt_disable();
269

270
#ifdef RT_USING_TIMER_SOFT
271 272 273 274 275 276
    if (timer->parent.flag & RT_TIMER_FLAG_SOFT_TIMER)
    {
        /* insert timer to soft timer list */
        timer_list = &rt_soft_timer_list;
    }
    else
277
#endif
278 279 280 281 282 283 284 285
    {
        /* insert timer to system timer list */
        timer_list = &rt_timer_list;
    }

    for (n = timer_list->next; n != timer_list; n = n->next)
    {
        t = rt_list_entry(n, struct rt_timer, list);
G
Grissiom 已提交
286

287 288
        /*
         * It supposes that the new tick shall less than the half duration of
G
Grissiom 已提交
289 290
         * tick max. And if we have two timers that timeout at the same time,
         * it's prefered that the timer inserted early get called early.
291
         */
G
Grissiom 已提交
292 293 294 295 296 297
        if ((t->timeout_tick - timer->timeout_tick) == 0)
        {
            rt_list_insert_after(n, &(timer->list));
            break;
        }
        else if ((t->timeout_tick - timer->timeout_tick) < RT_TICK_MAX / 2)
298 299 300 301 302 303 304 305 306 307 308 309 310 311 312
        {
            rt_list_insert_before(n, &(timer->list));
            break;
        }
    }
    /* no found suitable position in timer list */
    if (n == timer_list)
    {
        rt_list_insert_before(n, &(timer->list));
    }

    timer->parent.flag |= RT_TIMER_FLAG_ACTIVATED;

    /* enable interrupt */
    rt_hw_interrupt_enable(level);
313

314
#ifdef RT_USING_TIMER_SOFT
315 316 317 318 319 320 321 322 323 324
    if (timer->parent.flag & RT_TIMER_FLAG_SOFT_TIMER)
    {
        /* check whether timer thread is ready */
        if (timer_thread.stat != RT_THREAD_READY)
        {
            /* resume timer thread to check soft timer */
            rt_thread_resume(&timer_thread);
            rt_schedule();
        }
    }
325 326
#endif

327
    return -RT_EOK;
328
}
329
RTM_EXPORT(rt_timer_start);
330 331 332 333 334 335

/**
 * This function will stop the timer
 *
 * @param timer the timer to be stopped
 *
B
bernard.xiong@gmail.com 已提交
336
 * @return the operation status, RT_EOK on OK, -RT_ERROR on error
337 338 339
 */
rt_err_t rt_timer_stop(rt_timer_t timer)
{
340
    register rt_base_t level;
341

342 343 344 345
    /* timer check */
    RT_ASSERT(timer != RT_NULL);
    if (!(timer->parent.flag & RT_TIMER_FLAG_ACTIVATED))
        return -RT_ERROR;
346

347
    RT_OBJECT_HOOK_CALL(rt_object_put_hook, (&(timer->parent)));
348

349 350
    /* disable interrupt */
    level = rt_hw_interrupt_disable();
351

352 353
    /* remove it from timer list */
    rt_list_remove(&(timer->list));
354

355 356
    /* enable interrupt */
    rt_hw_interrupt_enable(level);
357

358 359
    /* change stat */
    timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
360

361
    return RT_EOK;
362
}
363
RTM_EXPORT(rt_timer_stop);
364 365 366 367 368 369 370 371

/**
 * This function will get or set some options of the timer
 *
 * @param timer the timer to be get or set
 * @param cmd the control command
 * @param arg the argument
 *
B
bernard.xiong@gmail.com 已提交
372
 * @return RT_EOK
373
 */
D
dzzxzz 已提交
374
rt_err_t rt_timer_control(rt_timer_t timer, rt_uint8_t cmd, void *arg)
375
{
376 377
    /* timer check */
    RT_ASSERT(timer != RT_NULL);
378

379 380 381 382 383
    switch (cmd)
    {
    case RT_TIMER_CTRL_GET_TIME:
        *(rt_tick_t *)arg = timer->init_tick;
        break;
384

385 386 387
    case RT_TIMER_CTRL_SET_TIME:
        timer->init_tick = *(rt_tick_t *)arg;
        break;
388

389 390 391
    case RT_TIMER_CTRL_SET_ONESHOT:
        timer->parent.flag &= ~RT_TIMER_FLAG_PERIODIC;
        break;
392

393 394 395 396
    case RT_TIMER_CTRL_SET_PERIODIC:
        timer->parent.flag |= RT_TIMER_FLAG_PERIODIC;
        break;
    }
397

398
    return RT_EOK;
399
}
400
RTM_EXPORT(rt_timer_control);
401 402

/**
403
 * This function will check timer list, if a timeout event happens, the
404 405
 * corresponding timeout function will be invoked.
 *
B
bernard.xiong@gmail.com 已提交
406
 * @note this function shall be invoked in operating system timer interrupt.
407
 */
408
void rt_timer_check(void)
409
{
410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444
    struct rt_timer *t;
    rt_tick_t current_tick;
    register rt_base_t level;

    RT_DEBUG_LOG(RT_DEBUG_TIMER, ("timer check enter\n"));

    current_tick = rt_tick_get();

    /* disable interrupt */
    level = rt_hw_interrupt_disable();

    while (!rt_list_isempty(&rt_timer_list))
    {
        t = rt_list_entry(rt_timer_list.next, struct rt_timer, list);

        /*
         * It supposes that the new tick shall less than the half duration of
         * tick max.
         */
        if ((current_tick - t->timeout_tick) < RT_TICK_MAX/2)
        {
            RT_OBJECT_HOOK_CALL(rt_timer_timeout_hook, (t));

            /* remove timer from timer list firstly */
            rt_list_remove(&(t->list));

            /* call timeout function */
            t->timeout_func(t->parameter);

            /* re-get tick */
            current_tick = rt_tick_get();

            RT_DEBUG_LOG(RT_DEBUG_TIMER, ("current tick: %d\n", current_tick));

            if ((t->parent.flag & RT_TIMER_FLAG_PERIODIC) &&
D
dzzxzz@gmail.com 已提交
445
                (t->parent.flag & RT_TIMER_FLAG_ACTIVATED))
446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464
            {
                /* start it */
                t->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
                rt_timer_start(t);
            }
            else
            {
                /* stop timer */
                t->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
            }
        }
        else
            break;
    }

    /* enable interrupt */
    rt_hw_interrupt_enable(level);

    RT_DEBUG_LOG(RT_DEBUG_TIMER, ("timer check leave\n"));
465 466
}

467 468 469 470 471 472
/**
 * This function will return the next timeout tick in the system.
 *
 * @return the next timeout tick in the system
 */
rt_tick_t rt_timer_next_timeout_tick(void)
473
{
474
    return rt_timer_list_next_timeout(&rt_timer_list);
475 476
}

477
#ifdef RT_USING_TIMER_SOFT
478 479 480 481
/**
 * This function will check timer list, if a timeout event happens, the
 * corresponding timeout function will be invoked.
 */
D
dzzxzz 已提交
482
void rt_soft_timer_check(void)
483
{
484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499
    rt_tick_t current_tick;
    rt_list_t *n;
    struct rt_timer *t;

    RT_DEBUG_LOG(RT_DEBUG_TIMER, ("software timer check enter\n"));

    current_tick = rt_tick_get();

    for (n = rt_soft_timer_list.next; n != &(rt_soft_timer_list);)
    {
        t = rt_list_entry(n, struct rt_timer, list);

        /*
         * It supposes that the new tick shall less than the half duration of
         * tick max.
         */
D
dzzxzz@gmail.com 已提交
500
        if ((current_tick - t->timeout_tick) < RT_TICK_MAX / 2)
501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518
        {
            RT_OBJECT_HOOK_CALL(rt_timer_timeout_hook, (t));

            /* move node to the next */
            n = n->next;

            /* remove timer from timer list firstly */
            rt_list_remove(&(t->list));

            /* call timeout function */
            t->timeout_func(t->parameter);

            /* re-get tick */
            current_tick = rt_tick_get();

            RT_DEBUG_LOG(RT_DEBUG_TIMER, ("current tick: %d\n", current_tick));

            if ((t->parent.flag & RT_TIMER_FLAG_PERIODIC) &&
D
dzzxzz@gmail.com 已提交
519
                (t->parent.flag & RT_TIMER_FLAG_ACTIVATED))
520 521 522 523 524 525 526 527 528 529 530 531 532 533 534
            {
                /* start it */
                t->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
                rt_timer_start(t);
            }
            else
            {
                /* stop timer */
                t->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
            }
        }
        else break; /* not check anymore */
    }

    RT_DEBUG_LOG(RT_DEBUG_TIMER, ("software timer check leave\n"));
535 536
}

537
/* system timer thread entry */
D
dzzxzz 已提交
538
static void rt_thread_timer_entry(void *parameter)
539
{
540
    rt_tick_t next_timeout;
B
Bernard Xiong 已提交
541

542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573
    while (1)
    {
        /* get the next timeout tick */
        next_timeout = rt_timer_list_next_timeout(&rt_soft_timer_list);
        if (next_timeout == RT_TICK_MAX)
        {
            /* no software timer exist, suspend self. */
            rt_thread_suspend(rt_thread_self());
            rt_schedule();
        }
        else
        {
            rt_tick_t current_tick;

            /* get current tick */
            current_tick = rt_tick_get();

            if ((next_timeout - current_tick) < RT_TICK_MAX/2)
            {
                /* get the delta timeout tick */
                next_timeout = next_timeout - current_tick;
                rt_thread_delay(next_timeout);
            }
        }

        /* lock scheduler */
        rt_enter_critical();
        /* check software timer */
        rt_soft_timer_check();
        /* unlock scheduler */
        rt_exit_critical();
    }
574
}
575
#endif
576

577 578 579
/**
 * @ingroup SystemInit
 *
580
 * This function will initialize system timer
581 582 583
 *
 * @deprecated since 1.1.0, this function does not need to be invoked
 * in the system initialization.
584
 */
D
dzzxzz 已提交
585
void rt_system_timer_init(void)
586
{
B
bernard.xiong 已提交
587
}
588

B
bernard.xiong 已提交
589 590 591
/**
 * @ingroup SystemInit
 *
592
 * This function will initialize system timer thread
B
bernard.xiong 已提交
593
 */
D
dzzxzz 已提交
594
void rt_system_timer_thread_init(void)
B
bernard.xiong 已提交
595 596
{
#ifdef RT_USING_TIMER_SOFT
597
    rt_list_init(&rt_soft_timer_list);
598

599 600 601
    /* start software timer thread */
    rt_thread_init(&timer_thread,
                   "timer",
D
dzzxzz@gmail.com 已提交
602 603 604 605 606 607
                   rt_thread_timer_entry,
                   RT_NULL,
                   &timer_thread_stack[0],
                   sizeof(timer_thread_stack),
                   RT_TIMER_THREAD_PRIO,
                   10);
608

609 610
    /* startup */
    rt_thread_startup(&timer_thread);
611
#endif
612
}
613

614
/*@}*/