timer.c 11.7 KB
Newer Older
1 2 3
/*
 * File      : timer.c
 * This file is part of RT-Thread RTOS
D
dzzxzz 已提交
4
 * COPYRIGHT (C) 2006 - 2011, 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 21 22 23 24 25 26
 */

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

#include "kservice.h"

27
/* hard timer list */
28
static rt_list_t rt_timer_list;
29

30
#ifdef RT_USING_TIMER_SOFT
31
/* soft timer list */
32
static rt_list_t rt_soft_timer_list;
33
#endif
34

35
#ifdef RT_USING_HOOK
D
dzzxzz 已提交
36 37 38
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);
39 40 41 42 43 44 45 46 47

/**
 * @addtogroup Hook
 */
/*@{*/

/**
 * This function will set a hook function, which will be invoked when timer
 * is timeout.
48
 *
49 50
 * @param hook the hook function
 */
D
dzzxzz 已提交
51
void rt_timer_timeout_sethook(void (*hook)(struct rt_timer *timer))
52 53 54 55 56 57 58
{
	rt_timer_timeout_hook = hook;
}

/*@}*/
#endif

59
static void _rt_timer_init(rt_timer_t timer,
D
dzzxzz 已提交
60
						   void (*timeout)(void *parameter), void *parameter,
61
						   rt_tick_t time, rt_uint8_t flag)
62 63
{
	/* set flag */
D
dzzxzz 已提交
64
	timer->parent.flag  = flag;
65 66 67 68 69

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

	timer->timeout_func = timeout;
D
dzzxzz 已提交
70
	timer->parameter    = parameter;
71

D
dzzxzz 已提交
72 73
	timer->timeout_tick = 0;
	timer->init_tick    = time;
74

75
	/* initialize timer list */
76 77 78 79 80 81 82 83 84
	rt_list_init(&(timer->list));
}

/**
 * @addtogroup Clock
 */
/*@{*/

/**
85
 * This function will initialize a timer, normally this function is used to initialize
86 87 88 89 90 91 92 93 94
 * a static timer object.
 *
 * @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
 */
95
void rt_timer_init(rt_timer_t timer,
D
dzzxzz 已提交
96 97
				   const char *name,
				   void (*timeout)(void *parameter), void *parameter,
98
				   rt_tick_t time, rt_uint8_t flag)
99 100 101 102
{
	/* timer check */
	RT_ASSERT(timer != RT_NULL);

103
	/* timer object initialization */
104 105 106 107 108
	rt_object_init((rt_object_t)timer, RT_Object_Class_Timer, name);

	_rt_timer_init(timer, timeout, parameter, time, flag);
}

109
/**
110 111 112
 * This function will detach a timer from timer management.
 *
 * @param timer the static timer object
113
 *
114 115 116 117 118
 * @return the operation status, RT_EOK on OK; RT_ERROR on error
 */
rt_err_t rt_timer_detach(rt_timer_t timer)
{
	register rt_base_t level;
119

120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
	/* timer check */
	RT_ASSERT(timer != RT_NULL);

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

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

	/* enable interrupt */
	rt_hw_interrupt_enable(level);

	rt_object_detach((rt_object_t)timer);

	return -RT_EOK;
}

#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 已提交
149
rt_timer_t rt_timer_create(const char *name, void (*timeout)(void *parameter), void *parameter, rt_tick_t time, rt_uint8_t flag)
150
{
D
dzzxzz 已提交
151
	struct rt_timer *timer;
152 153

	/* allocate a object */
D
dzzxzz 已提交
154
	timer = (struct rt_timer *)rt_object_allocate(RT_Object_Class_Timer, name);
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
	if (timer == RT_NULL)
	{
		return RT_NULL;
	}

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

	return timer;
}

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

177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199
	/* timer check */
	RT_ASSERT(timer != RT_NULL);

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

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

	/* enable interrupt */
	rt_hw_interrupt_enable(level);

	rt_object_delete((rt_object_t)timer);

	return -RT_EOK;
}
#endif

/**
 * This function will start the timer
 *
 * @param timer the timer to be started
 *
B
bernard.xiong@gmail.com 已提交
200
 * @return the operation status, RT_EOK on OK, -RT_ERROR on error
201 202 203 204
 *
 */
rt_err_t rt_timer_start(rt_timer_t timer)
{
D
dzzxzz 已提交
205
	struct rt_timer *t;
206
	register rt_base_t level;
207
	rt_list_t *n, *timer_list;
208 209 210 211 212

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

213
	RT_OBJECT_HOOK_CALL(rt_object_take_hook, (&(timer->parent)));
214 215 216

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

B
bernard.xiong@gmail.com 已提交
218 219
	/* get timeout tick, the max timeout tick shall not great than RT_TICK_MAX/2 */
	RT_ASSERT(timer->init_tick < RT_TICK_MAX/2);
220 221
	timer->timeout_tick = rt_tick_get() + timer->init_tick;

222
#ifdef RT_USING_TIMER_SOFT
223
	if (timer->parent.flag & RT_TIMER_FLAG_SOFT_TIMER)
224
	{
225 226
		/* insert timer to soft timer list */
		timer_list = &rt_soft_timer_list;
227 228
	}
	else
229
#endif
230
	{
231
		/* insert timer to system timer list */
232 233
		timer_list = &rt_timer_list;
	}
234

235 236 237 238 239 240 241 242
	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)
243 244
		{
			rt_list_insert_before(n, &(timer->list));
245
			break;
246
		}
247
	}
248 249 250 251 252 253
	/* no found suitable position in timer list */
	if (n == timer_list)
	{
		rt_list_insert_before(n, &(timer->list));
	}
		
254 255 256 257 258 259 260 261 262 263 264 265 266
	timer->parent.flag |= RT_TIMER_FLAG_ACTIVATED;

	/* enable interrupt */
	rt_hw_interrupt_enable(level);

	return -RT_EOK;
}

/**
 * This function will stop the timer
 *
 * @param timer the timer to be stopped
 *
B
bernard.xiong@gmail.com 已提交
267
 * @return the operation status, RT_EOK on OK, -RT_ERROR on error
268 269 270 271 272 273 274 275
 *
 */
rt_err_t rt_timer_stop(rt_timer_t timer)
{
	register rt_base_t level;

	/* timer check */
	RT_ASSERT(timer != RT_NULL);
D
dzzxzz 已提交
276
	if (!(timer->parent.flag & RT_TIMER_FLAG_ACTIVATED)) return -RT_ERROR;
277

278
	RT_OBJECT_HOOK_CALL(rt_object_put_hook, (&(timer->parent)));
279 280 281 282

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

283 284 285
	/* change stat */
	timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;

286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301
	/* remove it from timer list */
	rt_list_remove(&(timer->list));

	/* enable interrupt */
	rt_hw_interrupt_enable(level);

	return RT_EOK;
}

/**
 * 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 已提交
302
 * @return RT_EOK
303 304
 *
 */
D
dzzxzz 已提交
305
rt_err_t rt_timer_control(rt_timer_t timer, rt_uint8_t cmd, void *arg)
306 307 308 309 310 311 312
{
	/* timer check */
	RT_ASSERT(timer != RT_NULL);

	switch (cmd)
	{
	case RT_TIMER_CTRL_GET_TIME:
D
dzzxzz 已提交
313
		*(rt_tick_t *)arg = timer->init_tick;
314 315 316
		break;

	case RT_TIMER_CTRL_SET_TIME:
D
dzzxzz 已提交
317
		timer->init_tick = *(rt_tick_t *)arg;
318 319 320 321 322 323 324 325 326 327 328 329 330 331 332
		break;

	case RT_TIMER_CTRL_SET_ONESHOT:
		timer->parent.flag &= ~(1 << RT_TIMER_FLAG_PERIODIC);
		break;

	case RT_TIMER_CTRL_SET_PERIODIC:
		timer->parent.flag |= (1 << RT_TIMER_FLAG_PERIODIC);
		break;
	}

	return RT_EOK;
}

/**
333
 * This function will check timer list, if a timeout event happens, the
334 335
 * corresponding timeout function will be invoked.
 *
B
bernard.xiong@gmail.com 已提交
336
 * @note this function shall be invoked in operating system timer interrupt.
337
 */
338
#ifdef RT_USING_TIMER_SOFT
D
dzzxzz 已提交
339
void rt_soft_timer_tick_increase(void);
340
#endif
341
void rt_timer_check(void)
342 343
{
	struct rt_timer *t;
344
	rt_tick_t current_tick;
345 346
	register rt_base_t level;

D
dzzxzz 已提交
347
	RT_DEBUG_LOG(RT_DEBUG_TIMER, ("timer check enter\n"));
348 349 350 351 352 353

	current_tick = rt_tick_get();

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

354
	while (!rt_list_isempty(&rt_timer_list))
355
	{
356
		t = rt_list_entry(rt_timer_list.next, struct rt_timer, list);
D
dzzxzz 已提交
357

358 359 360 361
		/*
		 * 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)
362
		{
363
			RT_OBJECT_HOOK_CALL(rt_timer_timeout_hook, (t));
364 365 366 367 368 369 370

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

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

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

D
dzzxzz 已提交
374
			RT_DEBUG_LOG(RT_DEBUG_TIMER, ("current tick: %d\n", current_tick));
375

376 377
			if ((t->parent.flag & RT_TIMER_FLAG_PERIODIC) &&
					(t->parent.flag & RT_TIMER_FLAG_ACTIVATED))
378 379
			{
				/* start it */
380
				t->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
381 382
				rt_timer_start(t);
			}
383 384 385 386 387
			else
			{
				/* stop timer */
				t->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
			}
388 389 390 391 392 393 394
		}
		else break;
	}

	/* enable interrupt */
	rt_hw_interrupt_enable(level);

B
bernard.xiong 已提交
395
	/* increase soft timer tick */
396
#ifdef RT_USING_TIMER_SOFT
D
dzzxzz 已提交
397
	rt_soft_timer_tick_increase();
398 399
#endif

D
dzzxzz 已提交
400
	RT_DEBUG_LOG(RT_DEBUG_TIMER, ("timer check leave\n"));
401 402
}

403 404
#ifdef RT_USING_TIMER_SOFT
static struct rt_thread timer_thread;
405
ALIGN(RT_ALIGN_SIZE)
406
static rt_uint8_t timer_thread_stack[RT_TIMER_THREAD_STACK_SIZE];
407 408
static struct rt_semaphore timer_sem;

B
bernard.xiong 已提交
409
static rt_uint16_t timer_ex_cnt;
410

D
dzzxzz 已提交
411
void rt_soft_timer_tick_increase(void)
412
{
D
dzzxzz 已提交
413
	timer_ex_cnt ++;
B
bernard.xiong 已提交
414
	if (timer_ex_cnt >= (RT_TICK_PER_SECOND / RT_TIMER_TICK_PER_SECOND))
415 416
	{
		timer_ex_cnt = 0;
B
bernard.xiong 已提交
417
		rt_sem_release(&timer_sem);
418 419 420 421 422 423 424 425
	}
}

/**
 * This function will check timer list, if a timeout event happens, the
 * corresponding timeout function will be invoked.
 *
 */
D
dzzxzz 已提交
426
void rt_soft_timer_check(void)
427 428 429 430
{
	rt_tick_t current_tick;
	rt_list_t *n;
	struct rt_timer *t;
431

D
dzzxzz 已提交
432
	RT_DEBUG_LOG(RT_DEBUG_TIMER, ("software timer check enter\n"));
433

434 435
	current_tick = rt_tick_get();

D
dzzxzz 已提交
436
	for (n = rt_soft_timer_list.next; n != &(rt_soft_timer_list);)
437 438
	{
		t = rt_list_entry(n, struct rt_timer, list);
D
dzzxzz 已提交
439

440 441 442 443
		/*
		 * 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)
444
		{
445 446
			RT_OBJECT_HOOK_CALL(rt_timer_timeout_hook, (t));

447 448 449 450 451 452 453 454 455
			/* 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);

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

D
dzzxzz 已提交
459
			RT_DEBUG_LOG(RT_DEBUG_TIMER, ("current tick: %d\n", current_tick));
460

461 462
			if ((t->parent.flag & RT_TIMER_FLAG_PERIODIC) &&
					(t->parent.flag & RT_TIMER_FLAG_ACTIVATED))
463 464 465 466 467 468 469 470 471 472 473
			{
				/* start it */
				t->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
				rt_timer_start(t);
			}
			else
			{
				/* stop timer */
				t->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
			}
		}
474
		else break; /* not check anymore */
475 476
	}

D
dzzxzz 已提交
477
	RT_DEBUG_LOG(RT_DEBUG_TIMER, ("software timer check leave\n"));
478 479
}

480
/* system timer thread entry */
D
dzzxzz 已提交
481
static void rt_thread_timer_entry(void *parameter)
482 483 484
{
	while (1)
	{
B
bernard.xiong 已提交
485
		/* take software timer semaphore */
D
dzzxzz 已提交
486
		rt_sem_take(&timer_sem, RT_WAITING_FOREVER);
487

D
dzzxzz 已提交
488
		/* lock scheduler */
489
		rt_enter_critical();
490

D
dzzxzz 已提交
491
		/* check software timer */
492 493
		rt_soft_timer_check();

D
dzzxzz 已提交
494
		/* unlock scheduler */
495
		rt_exit_critical();
496
	}
497
}
498
#endif
499

500 501 502
/**
 * @ingroup SystemInit
 *
503
 * This function will initialize system timer
504 505
 *
 */
D
dzzxzz 已提交
506
void rt_system_timer_init(void)
507 508 509 510 511
{
	rt_list_init(&rt_timer_list);

#ifdef RT_USING_TIMER_SOFT
	rt_list_init(&rt_soft_timer_list);
D
dzzxzz 已提交
512
	rt_sem_init(&timer_sem, "timer", 0, RT_IPC_FLAG_FIFO);
B
bernard.xiong 已提交
513 514
#endif
}
515

B
bernard.xiong 已提交
516 517 518
/**
 * @ingroup SystemInit
 *
519
 * This function will initialize system timer thread
B
bernard.xiong 已提交
520 521
 *
 */
D
dzzxzz 已提交
522
void rt_system_timer_thread_init(void)
B
bernard.xiong 已提交
523 524
{
#ifdef RT_USING_TIMER_SOFT
D
dzzxzz 已提交
525
	/* start software timer thread */
526 527 528 529 530
	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);
531 532

	/* startup */
533 534
	rt_thread_startup(&timer_thread);
#endif
535
}
536

537
/*@}*/