timer.c 14.3 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 6 7
 *
 * The license and distribution terms for this file may be
 * found in the file LICENSE in this distribution or at
8
 * http://www.rt-thread.org/license/LICENSE
9 10 11 12 13 14 15 16
 *
 * 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
17
 * 2009-11-11     LiJin        add soft timer
18
 * 2010-05-12     Bernard      fix the timer check bug.
19
 * 2010-11-02     Charlie      re-implement tick overflow issue
20
 * 2012-12-15     Bernard      fix the next timeout issue in soft timer
21 22 23 24 25
 */

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

26
/* hard timer list */
27
static rt_list_t rt_timer_list = RT_LIST_OBJECT_INIT(rt_timer_list);
28

29
#ifdef RT_USING_TIMER_SOFT
30 31 32 33 34 35 36 37
#ifndef RT_TIMER_THREAD_STACK_SIZE
#define RT_TIMER_THREAD_STACK_SIZE 512
#endif

#ifndef RT_TIMER_THREAD_PRIO
#define RT_TIMER_THREAD_PRIO        0
#endif

38
/* soft timer list */
39
static rt_list_t rt_soft_timer_list;
40 41 42
static struct rt_thread timer_thread;
ALIGN(RT_ALIGN_SIZE)
static rt_uint8_t timer_thread_stack[RT_TIMER_THREAD_STACK_SIZE];
43
#endif
44

45
#ifdef RT_USING_HOOK
D
dzzxzz 已提交
46 47 48
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);
49 50 51 52

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

54 55 56 57 58
/*@{*/

/**
 * This function will set a hook function, which will be invoked when timer
 * is timeout.
59
 *
60 61
 * @param hook the hook function
 */
D
dzzxzz 已提交
62
void rt_timer_timeout_sethook(void (*hook)(struct rt_timer *timer))
63
{
64
    rt_timer_timeout_hook = hook;
65 66 67 68 69
}

/*@}*/
#endif

70
static void _rt_timer_init(rt_timer_t timer,
71 72
                           void (*timeout)(void *parameter), void *parameter,
                           rt_tick_t time, rt_uint8_t flag)
73
{
74 75
    /* set flag */
    timer->parent.flag  = flag;
76

77 78
    /* set deactivated */
    timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
79

80 81
    timer->timeout_func = timeout;
    timer->parameter    = parameter;
82

83 84
    timer->timeout_tick = 0;
    timer->init_tick    = time;
85

86 87
    /* initialize timer list */
    rt_list_init(&(timer->list));
88 89
}

D
dzzxzz@gmail.com 已提交
90
static rt_tick_t rt_timer_list_next_timeout(rt_list_t *timer_list)
91
{
92
    struct rt_timer *timer;
D
dzzxzz@gmail.com 已提交
93

94 95 96 97
    if (rt_list_isempty(timer_list))
        return RT_TICK_MAX;
    
    timer = rt_list_entry(timer_list->next, struct rt_timer, list);
D
dzzxzz@gmail.com 已提交
98

99
    return timer->timeout_tick;
100 101
}

102 103 104
/**
 * @addtogroup Clock
 */
D
dzzxzz 已提交
105

106 107 108
/*@{*/

/**
109 110
 * This function will initialize a timer, normally this function is used to
 * initialize a static timer object.
111 112 113 114 115 116 117 118
 *
 * @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
 */
119
void rt_timer_init(rt_timer_t timer,
120 121 122
                   const char *name,
                   void (*timeout)(void *parameter), void *parameter,
                   rt_tick_t time, rt_uint8_t flag)
123
{
124 125
    /* timer check */
    RT_ASSERT(timer != RT_NULL);
126

127 128
    /* timer object initialization */
    rt_object_init((rt_object_t)timer, RT_Object_Class_Timer, name);
129

130
    _rt_timer_init(timer, timeout, parameter, time, flag);
131
}
132
RTM_EXPORT(rt_timer_init);
133

134
/**
135 136 137
 * This function will detach a timer from timer management.
 *
 * @param timer the static timer object
138
 *
139 140 141 142
 * @return the operation status, RT_EOK on OK; RT_ERROR on error
 */
rt_err_t rt_timer_detach(rt_timer_t timer)
{
143
    register rt_base_t level;
144

145 146
    /* timer check */
    RT_ASSERT(timer != RT_NULL);
147

148 149
    /* disable interrupt */
    level = rt_hw_interrupt_disable();
150

151 152
    /* remove it from timer list */
    rt_list_remove(&(timer->list));
153

154 155
    /* enable interrupt */
    rt_hw_interrupt_enable(level);
156

157
    rt_object_detach((rt_object_t)timer);
158

159
    return -RT_EOK;
160
}
161
RTM_EXPORT(rt_timer_detach);
162 163 164 165 166 167 168 169 170 171 172 173 174

#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
 */
175
rt_timer_t rt_timer_create(const char *name, void (*timeout)(void *parameter),
176
        void *parameter, rt_tick_t time, rt_uint8_t flag)
177
{
178
    struct rt_timer *timer;
179

180 181 182 183 184 185
    /* allocate a object */
    timer = (struct rt_timer *)rt_object_allocate(RT_Object_Class_Timer, name);
    if (timer == RT_NULL)
    {
        return RT_NULL;
    }
186

187
    _rt_timer_init(timer, timeout, parameter, time, flag);
188

189
    return timer;
190
}
191
RTM_EXPORT(rt_timer_create);
192 193 194 195 196 197 198 199 200 201

/**
 * 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)
{
202
    register rt_base_t level;
203

204 205
    /* timer check */
    RT_ASSERT(timer != RT_NULL);
206

207 208
    /* disable interrupt */
    level = rt_hw_interrupt_disable();
209

210 211
    /* remove it from timer list */
    rt_list_remove(&(timer->list));
212

213 214
    /* enable interrupt */
    rt_hw_interrupt_enable(level);
215

216
    rt_object_delete((rt_object_t)timer);
217

218
    return -RT_EOK;
219
}
220
RTM_EXPORT(rt_timer_delete);
221 222 223 224 225 226 227
#endif

/**
 * This function will start the timer
 *
 * @param timer the timer to be started
 *
B
bernard.xiong@gmail.com 已提交
228
 * @return the operation status, RT_EOK on OK, -RT_ERROR on error
229 230 231
 */
rt_err_t rt_timer_start(rt_timer_t timer)
{
232 233 234
    struct rt_timer *t;
    register rt_base_t level;
    rt_list_t *n, *timer_list;
235

236 237 238 239
    /* timer check */
    RT_ASSERT(timer != RT_NULL);
    if (timer->parent.flag & RT_TIMER_FLAG_ACTIVATED)
        return -RT_ERROR;
240

241
    RT_OBJECT_HOOK_CALL(rt_object_take_hook, (&(timer->parent)));
242

243 244 245
    /* get timeout tick, the max timeout tick shall not great than RT_TICK_MAX/2 */
    RT_ASSERT(timer->init_tick < RT_TICK_MAX/2);
    timer->timeout_tick = rt_tick_get() + timer->init_tick;
246

247 248
    /* disable interrupt */
    level = rt_hw_interrupt_disable();
249

250
#ifdef RT_USING_TIMER_SOFT
251 252 253 254 255 256
    if (timer->parent.flag & RT_TIMER_FLAG_SOFT_TIMER)
    {
        /* insert timer to soft timer list */
        timer_list = &rt_soft_timer_list;
    }
    else
257
#endif
258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286
    {
        /* 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);
        
        /*
         * It supposes that the new tick shall less than the half duration of
         * tick max.
         */
        if ((t->timeout_tick - timer->timeout_tick) < RT_TICK_MAX/2)
        {
            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);
287

288
#ifdef RT_USING_TIMER_SOFT
289 290 291 292 293 294 295 296 297 298
    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();
        }
    }
299 300
#endif

301
    return -RT_EOK;
302
}
303
RTM_EXPORT(rt_timer_start);
304 305 306 307 308 309

/**
 * This function will stop the timer
 *
 * @param timer the timer to be stopped
 *
B
bernard.xiong@gmail.com 已提交
310
 * @return the operation status, RT_EOK on OK, -RT_ERROR on error
311 312 313
 */
rt_err_t rt_timer_stop(rt_timer_t timer)
{
314
    register rt_base_t level;
315

316 317 318 319
    /* timer check */
    RT_ASSERT(timer != RT_NULL);
    if (!(timer->parent.flag & RT_TIMER_FLAG_ACTIVATED))
        return -RT_ERROR;
320

321
    RT_OBJECT_HOOK_CALL(rt_object_put_hook, (&(timer->parent)));
322

323 324
    /* disable interrupt */
    level = rt_hw_interrupt_disable();
325

326 327
    /* remove it from timer list */
    rt_list_remove(&(timer->list));
328

329 330
    /* enable interrupt */
    rt_hw_interrupt_enable(level);
331

332 333
    /* change stat */
    timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
334

335
    return RT_EOK;
336
}
337
RTM_EXPORT(rt_timer_stop);
338 339 340 341 342 343 344 345

/**
 * 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 已提交
346
 * @return RT_EOK
347
 */
D
dzzxzz 已提交
348
rt_err_t rt_timer_control(rt_timer_t timer, rt_uint8_t cmd, void *arg)
349
{
350 351
    /* timer check */
    RT_ASSERT(timer != RT_NULL);
352

353 354 355 356 357
    switch (cmd)
    {
    case RT_TIMER_CTRL_GET_TIME:
        *(rt_tick_t *)arg = timer->init_tick;
        break;
358

359 360 361
    case RT_TIMER_CTRL_SET_TIME:
        timer->init_tick = *(rt_tick_t *)arg;
        break;
362

363 364 365
    case RT_TIMER_CTRL_SET_ONESHOT:
        timer->parent.flag &= ~RT_TIMER_FLAG_PERIODIC;
        break;
366

367 368 369 370
    case RT_TIMER_CTRL_SET_PERIODIC:
        timer->parent.flag |= RT_TIMER_FLAG_PERIODIC;
        break;
    }
371

372
    return RT_EOK;
373
}
374
RTM_EXPORT(rt_timer_control);
375 376

/**
377
 * This function will check timer list, if a timeout event happens, the
378 379
 * corresponding timeout function will be invoked.
 *
B
bernard.xiong@gmail.com 已提交
380
 * @note this function shall be invoked in operating system timer interrupt.
381
 */
382
void rt_timer_check(void)
383
{
384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 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
    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) &&
                    (t->parent.flag & RT_TIMER_FLAG_ACTIVATED))
            {
                /* 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"));
439 440
}

441 442 443 444 445 446
/**
 * 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)
447
{
448
    return rt_timer_list_next_timeout(&rt_timer_list);
449 450
}

451
#ifdef RT_USING_TIMER_SOFT
452 453 454 455
/**
 * This function will check timer list, if a timeout event happens, the
 * corresponding timeout function will be invoked.
 */
D
dzzxzz 已提交
456
void rt_soft_timer_check(void)
457
{
458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508
    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.
         */
        if ((current_tick - t->timeout_tick) < RT_TICK_MAX/2)
        {
            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) &&
                    (t->parent.flag & RT_TIMER_FLAG_ACTIVATED))
            {
                /* 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"));
509 510
}

511
/* system timer thread entry */
D
dzzxzz 已提交
512
static void rt_thread_timer_entry(void *parameter)
513
{
514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547
    rt_tick_t next_timeout;
    
    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();
    }
548
}
549
#endif
550

551 552 553
/**
 * @ingroup SystemInit
 *
554
 * This function will initialize system timer
555 556 557
 *
 * @deprecated since 1.1.0, this function does not need to be invoked
 * in the system initialization.
558
 */
D
dzzxzz 已提交
559
void rt_system_timer_init(void)
560
{
B
bernard.xiong 已提交
561
}
562

B
bernard.xiong 已提交
563 564 565
/**
 * @ingroup SystemInit
 *
566
 * This function will initialize system timer thread
B
bernard.xiong 已提交
567
 */
D
dzzxzz 已提交
568
void rt_system_timer_thread_init(void)
B
bernard.xiong 已提交
569 570
{
#ifdef RT_USING_TIMER_SOFT
571
    rt_list_init(&rt_soft_timer_list);
572

573 574 575 576 577 578
    /* start software timer thread */
    rt_thread_init(&timer_thread,
                   "timer",
                   rt_thread_timer_entry, RT_NULL,
                   &timer_thread_stack[0], sizeof(timer_thread_stack),
                   RT_TIMER_THREAD_PRIO, 10);
579

580 581
    /* startup */
    rt_thread_startup(&timer_thread);
582
#endif
583
}
584

585
/*@}*/