timer.c 12.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 21 22 23 24
 */

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

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

28
#ifdef RT_USING_TIMER_SOFT
29
/* soft timer list */
30
static rt_list_t rt_soft_timer_list;
31 32 33
static struct rt_thread timer_thread;
ALIGN(RT_ALIGN_SIZE)
static rt_uint8_t timer_thread_stack[RT_TIMER_THREAD_STACK_SIZE];
34
#endif
35

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

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

45 46 47 48 49
/*@{*/

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

/*@}*/
#endif

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

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

	timer->timeout_func = timeout;
D
dzzxzz 已提交
72
	timer->parameter    = parameter;
73

D
dzzxzz 已提交
74 75
	timer->timeout_tick = 0;
	timer->init_tick    = time;
76

77
	/* initialize timer list */
78 79 80
	rt_list_init(&(timer->list));
}

D
dzzxzz@gmail.com 已提交
81
static rt_tick_t rt_timer_list_next_timeout(rt_list_t *timer_list)
82
{
D
dzzxzz@gmail.com 已提交
83 84 85 86
	struct rt_timer *timer;

	if (rt_list_isempty(timer_list))
		return RT_TICK_MAX;
87 88
	
	timer = rt_list_entry(timer_list->next, struct rt_timer, list);
D
dzzxzz@gmail.com 已提交
89

90 91 92
	return timer->timeout_tick;
}

93 94 95
/**
 * @addtogroup Clock
 */
D
dzzxzz 已提交
96

97 98 99
/*@{*/

/**
100 101
 * This function will initialize a timer, normally this function is used to
 * initialize a static timer object.
102 103 104 105 106 107 108 109
 *
 * @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
 */
110
void rt_timer_init(rt_timer_t timer,
D
dzzxzz 已提交
111 112
				   const char *name,
				   void (*timeout)(void *parameter), void *parameter,
113
				   rt_tick_t time, rt_uint8_t flag)
114 115 116 117
{
	/* timer check */
	RT_ASSERT(timer != RT_NULL);

118
	/* timer object initialization */
119 120 121 122
	rt_object_init((rt_object_t)timer, RT_Object_Class_Timer, name);

	_rt_timer_init(timer, timeout, parameter, time, flag);
}
123
RTM_EXPORT(rt_timer_init);
124

125
/**
126 127 128
 * This function will detach a timer from timer management.
 *
 * @param timer the static timer object
129
 *
130 131 132 133 134
 * @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;
135

136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
	/* 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;
}
152
RTM_EXPORT(rt_timer_detach);
153 154 155 156 157 158 159 160 161 162 163 164 165

#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
 */
166 167
rt_timer_t rt_timer_create(const char *name, void (*timeout)(void *parameter),
		void *parameter, rt_tick_t time, rt_uint8_t flag)
168
{
D
dzzxzz 已提交
169
	struct rt_timer *timer;
170 171

	/* allocate a object */
D
dzzxzz 已提交
172
	timer = (struct rt_timer *)rt_object_allocate(RT_Object_Class_Timer, name);
173 174 175 176 177 178 179 180 181
	if (timer == RT_NULL)
	{
		return RT_NULL;
	}

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

	return timer;
}
182
RTM_EXPORT(rt_timer_create);
183 184 185 186 187 188 189 190 191 192 193

/**
 * 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;
194

195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
	/* 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;
}
211
RTM_EXPORT(rt_timer_delete);
212 213 214 215 216 217 218
#endif

/**
 * This function will start the timer
 *
 * @param timer the timer to be started
 *
B
bernard.xiong@gmail.com 已提交
219
 * @return the operation status, RT_EOK on OK, -RT_ERROR on error
220 221 222
 */
rt_err_t rt_timer_start(rt_timer_t timer)
{
D
dzzxzz 已提交
223
	struct rt_timer *t;
224
	register rt_base_t level;
225
	rt_list_t *n, *timer_list;
226 227 228

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

232
	RT_OBJECT_HOOK_CALL(rt_object_take_hook, (&(timer->parent)));
233

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

238 239 240
	/* disable interrupt */
	level = rt_hw_interrupt_disable();

241
#ifdef RT_USING_TIMER_SOFT
242
	if (timer->parent.flag & RT_TIMER_FLAG_SOFT_TIMER)
243
	{
244 245
		/* insert timer to soft timer list */
		timer_list = &rt_soft_timer_list;
246 247
	}
	else
248
#endif
249
	{
250
		/* insert timer to system timer list */
251 252
		timer_list = &rt_timer_list;
	}
253

254 255 256 257 258
	for (n = timer_list->next; n != timer_list; n = n->next)
	{
		t = rt_list_entry(n, struct rt_timer, list);
		
		/*
259 260
		 * It supposes that the new tick shall less than the half duration of
		 * tick max.
261 262
		 */
		if ((t->timeout_tick - timer->timeout_tick) < RT_TICK_MAX/2)
263 264
		{
			rt_list_insert_before(n, &(timer->list));
265
			break;
266
		}
267
	}
268 269 270 271 272
	/* no found suitable position in timer list */
	if (n == timer_list)
	{
		rt_list_insert_before(n, &(timer->list));
	}
273

274 275 276 277 278
	timer->parent.flag |= RT_TIMER_FLAG_ACTIVATED;

	/* enable interrupt */
	rt_hw_interrupt_enable(level);

279 280 281 282 283 284 285 286 287 288 289 290 291
#ifdef RT_USING_TIMER_SOFT
	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();
		}
	}
#endif

292 293
	return -RT_EOK;
}
294
RTM_EXPORT(rt_timer_start);
295 296 297 298 299 300

/**
 * This function will stop the timer
 *
 * @param timer the timer to be stopped
 *
B
bernard.xiong@gmail.com 已提交
301
 * @return the operation status, RT_EOK on OK, -RT_ERROR on error
302 303 304 305 306 307 308
 */
rt_err_t rt_timer_stop(rt_timer_t timer)
{
	register rt_base_t level;

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

312
	RT_OBJECT_HOOK_CALL(rt_object_put_hook, (&(timer->parent)));
313 314 315 316 317 318 319 320 321 322

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

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

	/* enable interrupt */
	rt_hw_interrupt_enable(level);

323 324 325
	/* change stat */
	timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;

326 327
	return RT_EOK;
}
328
RTM_EXPORT(rt_timer_stop);
329 330 331 332 333 334 335 336

/**
 * 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 已提交
337
 * @return RT_EOK
338
 */
D
dzzxzz 已提交
339
rt_err_t rt_timer_control(rt_timer_t timer, rt_uint8_t cmd, void *arg)
340 341 342 343 344 345 346
{
	/* timer check */
	RT_ASSERT(timer != RT_NULL);

	switch (cmd)
	{
	case RT_TIMER_CTRL_GET_TIME:
D
dzzxzz 已提交
347
		*(rt_tick_t *)arg = timer->init_tick;
348 349 350
		break;

	case RT_TIMER_CTRL_SET_TIME:
D
dzzxzz 已提交
351
		timer->init_tick = *(rt_tick_t *)arg;
352 353 354
		break;

	case RT_TIMER_CTRL_SET_ONESHOT:
355
		timer->parent.flag &= ~RT_TIMER_FLAG_PERIODIC;
356 357 358
		break;

	case RT_TIMER_CTRL_SET_PERIODIC:
359
		timer->parent.flag |= RT_TIMER_FLAG_PERIODIC;
360 361 362 363 364
		break;
	}

	return RT_EOK;
}
365
RTM_EXPORT(rt_timer_control);
366 367

/**
368
 * This function will check timer list, if a timeout event happens, the
369 370
 * corresponding timeout function will be invoked.
 *
B
bernard.xiong@gmail.com 已提交
371
 * @note this function shall be invoked in operating system timer interrupt.
372
 */
373
void rt_timer_check(void)
374 375
{
	struct rt_timer *t;
376
	rt_tick_t current_tick;
377 378
	register rt_base_t level;

D
dzzxzz 已提交
379
	RT_DEBUG_LOG(RT_DEBUG_TIMER, ("timer check enter\n"));
380 381 382 383 384 385

	current_tick = rt_tick_get();

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

386
	while (!rt_list_isempty(&rt_timer_list))
387
	{
388
		t = rt_list_entry(rt_timer_list.next, struct rt_timer, list);
D
dzzxzz 已提交
389

390
		/*
391 392
		 * It supposes that the new tick shall less than the half duration of
		 * tick max.
393 394
		 */
		if ((current_tick - t->timeout_tick) < RT_TICK_MAX/2)
395
		{
396
			RT_OBJECT_HOOK_CALL(rt_timer_timeout_hook, (t));
397 398 399 400 401 402 403

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

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

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

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

409 410
			if ((t->parent.flag & RT_TIMER_FLAG_PERIODIC) &&
					(t->parent.flag & RT_TIMER_FLAG_ACTIVATED))
411 412
			{
				/* start it */
413
				t->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
414 415
				rt_timer_start(t);
			}
416 417 418 419 420
			else
			{
				/* stop timer */
				t->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
			}
421
		}
D
dzzxzz 已提交
422 423
		else
			break;
424 425 426 427 428
	}

	/* enable interrupt */
	rt_hw_interrupt_enable(level);

D
dzzxzz 已提交
429
	RT_DEBUG_LOG(RT_DEBUG_TIMER, ("timer check leave\n"));
430 431
}

432 433 434 435 436 437
/**
 * 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)
438
{
439
	return rt_timer_list_next_timeout(&rt_timer_list);
440 441
}

442
#ifdef RT_USING_TIMER_SOFT
443 444 445 446
/**
 * This function will check timer list, if a timeout event happens, the
 * corresponding timeout function will be invoked.
 */
D
dzzxzz 已提交
447
void rt_soft_timer_check(void)
448 449 450 451
{
	rt_tick_t current_tick;
	rt_list_t *n;
	struct rt_timer *t;
452

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

455 456
	current_tick = rt_tick_get();

D
dzzxzz 已提交
457
	for (n = rt_soft_timer_list.next; n != &(rt_soft_timer_list);)
458 459
	{
		t = rt_list_entry(n, struct rt_timer, list);
D
dzzxzz 已提交
460

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

469 470 471 472 473 474 475 476 477
			/* 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);

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

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

483 484
			if ((t->parent.flag & RT_TIMER_FLAG_PERIODIC) &&
					(t->parent.flag & RT_TIMER_FLAG_ACTIVATED))
485 486 487 488 489 490 491 492 493 494 495
			{
				/* start it */
				t->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
				rt_timer_start(t);
			}
			else
			{
				/* stop timer */
				t->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
			}
		}
496
		else break; /* not check anymore */
497 498
	}

D
dzzxzz 已提交
499
	RT_DEBUG_LOG(RT_DEBUG_TIMER, ("software timer check leave\n"));
500 501
}

502
/* system timer thread entry */
D
dzzxzz 已提交
503
static void rt_thread_timer_entry(void *parameter)
504
{
505 506
	rt_tick_t next_timeout;
	
507 508
	while (1)
	{
509 510 511 512 513 514 515 516 517 518
		next_timeout = rt_timer_list_next_timeout(&rt_soft_timer_list);
		if (next_timeout == RT_TICK_MAX)
		{
			rt_thread_suspend(rt_thread_self());
			rt_schedule();
		}
		else
		{
			rt_thread_delay(next_timeout);
		}
519

D
dzzxzz 已提交
520
		/* lock scheduler */
521
		rt_enter_critical();
D
dzzxzz 已提交
522
		/* check software timer */
523
		rt_soft_timer_check();
D
dzzxzz 已提交
524
		/* unlock scheduler */
525
		rt_exit_critical();
526
	}
527
}
528
#endif
529

530 531 532
/**
 * @ingroup SystemInit
 *
533
 * This function will initialize system timer
534 535 536
 *
 * @deprecated since 1.1.0, this function does not need to be invoked
 * in the system initialization.
537
 */
D
dzzxzz 已提交
538
void rt_system_timer_init(void)
539
{
B
bernard.xiong 已提交
540
}
541

B
bernard.xiong 已提交
542 543 544
/**
 * @ingroup SystemInit
 *
545
 * This function will initialize system timer thread
B
bernard.xiong 已提交
546
 */
D
dzzxzz 已提交
547
void rt_system_timer_thread_init(void)
B
bernard.xiong 已提交
548 549
{
#ifdef RT_USING_TIMER_SOFT
550 551
	rt_list_init(&rt_soft_timer_list);

D
dzzxzz 已提交
552
	/* start software timer thread */
553 554 555 556 557
	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);
558 559

	/* startup */
560 561
	rt_thread_startup(&timer_thread);
#endif
562
}
563

564
/*@}*/