65.9K
CodeProject 正在变化。 阅读更多。
Home

使用链表创建多个独立定时器

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.64/5 (8投票s)

2009年4月4日

CPOL

2分钟阅读

viewsIcon

41468

一个简单的 C 程序,用于从单个定时器中断创建 16 个定时器例程,适用于嵌入式系统。

引言

本文档提供了一种在不使用操作系统的情况下为固件开发实现定时器例程的方法。定时器是编程中一个非常重要的工具,它可以简化生活。对于高级编程语言,我们可以根据系统的能力使用多个定时器。

对于一些小型和中型应用,将操作系统移植到设备中会显著增加成本,因为它需要复杂的硬件。对于大多数小型和中型应用,硬件不支持操作系统的移植。因此,在本文中,我将尝试给出基本概念,使用 C 语言作为编程语言,从单个定时器中断例程创建多个独立计时器。

所有处理器至少支持一个定时器中断作为低优先级中断,通常用作应用程序的时间基准。

目标读者

本文对那些希望他们的二进制输出(.bin)文件直接在硬件上执行的人很有用。当今,不使用操作系统进行固件开发在小型和中型硬件上很流行。

定时器例程代码

考虑一个链表,包含两个元素:定时器 ID 和相应的时间周期,如下所示

/* Timer record for each timer */
typedef struct Timer
{
	unsigned short TimerId;
	unsigned short Period;
	struct Timer *pNext;
}tTimer;

static tTimer *mpTimerList = NULL;

/* Global variable for reference */
static unsigned short gTimer;

其中 TimerID 是一个 16 位变量,用于标识,而 Period 是毫秒内的间隔。在这里,我们可以创建最多 16 个定时器,并且可以这样定义它们

#define TIMER_1	            (unsigned short)0x0001
#define TIMER_2	            (unsigned short)0x0002
#define TIMER_3	            (unsigned short)0x0004
..........
#define TIMER_16	            (unsigned short)0x8000

现在,编写一个函数 AddTimer() 来填充链表。请注意,只有当 Period > 0 时,定时器才是有效的。

bool AddTimer(unsigned short TimerId, unsigned short Period)
{
	tTimer *pTimer;
	tTimer *pNewTimer = NULL;
	bool ReturnValue = FALSE;

    /* Look for the timer – if already exists */
	pTimer = FindTimer(TimerId);

    /* Check if the timer was found */
	if((pTimer == NULL) || (pTimer->TimerId != TimerId))
    {
        /* Create a new timer */
    	pNewTimer = malloc(sizeof(tTimer));

    	if(pNewTimer != NULL)
        {
        	pNewTimer->TimerId = TimerId;
        	pNewTimer->pNext = NULL;

            /* Check if the list is empty */
        	if(pTimer == NULL)
            {
                /* Store the address of this timer as a first element in the list */
            	mpTimerList = pNewTimer;
            }
        	else
            {
                /* Add the new timer to the end of the list */
            	pTimer = pNewTimer;
            }
        }

        /* Select the new timer */
    	pTimer = pNewTimer;
    }

	if(pTimer != NULL)
    {
        /* Set the timer interval */
    	pTimer->Period = Period;
    	ReturnValue = TRUE;
    }

	return ReturnValue;
}

在上面的代码中,函数 FindTimer 用于避免在链表中重复 TimerID。只有当列表为空时,它才会返回 NULL,否则如果列表中找不到 TimerID,它将返回最后一个节点。如果 TimerID 已经存在,则它返回相应节点的指针。函数的体如下所示

static tTimer * FindTimer(unsigned short TimerId)
{
	tTimer *pTimer;

    /* Move to the start of the list */
	pTimer = mpTimerList;

    /* Check if list is empty */
	if(pTimer != NULL)
    {
        /* Move through the list until the timer id is found */
    	while((pTimer->Next != NULL) && (pTimer->TimerId != TimerId))
        {
            /* Timer ids - not match, Move onto the next timer */
        	pTimer = pTimer->Next;
        }
    }
	return (pTimer);
}

现在,配置中断级别 0,即定时器中断,使中断每毫秒发生一次。相应寄存器的值取决于处理器时钟速度。因此,请根据指令设置正确的值。

#pragma interrupt_level 0
void interrupt TimerBase(void)
{
    /* Check the bit for a timer 0 interrupt */
	if(TMRFLAG == 1)
    {
        /* Reset corresponding bit */
    	TMRFLAG = 0;

        /* Reload the corresponding register value */
    	RELOAD_TIMER0();

        /* Process timer event */
    	SystemTickEvent();

        /* Enable the timer0 again */
        ......
    }
}

在上面的例子中,编译器负责栈相关的活动。SystemTickEvent() 函数负责通过为每个 TimerID 减小周期来生成定时器事件。这里,该函数是内联的,以尽可能地最小化执行时间。

#pragma inline SystemTickEvent
void SystemTickEvent(void)
{
	tTimer *pTimer;

    /* Update the timers */
	pTimer = mpTimerList;

	while(pTimer != NULL)
    {
    	if(pTimer->Period != 0)
        {
            /* Decrement the timer period */
        	pTimer->Period--;

            /* Check if the timer has reached zero */
        	if(pTimer->Period == 0)
            {
                /* Set the corresponding bit when the timer reaches zero */
            	gTimer = gTimer | pTimer->TimerID;
            }
        }
        /* Move to the next timer in the list */
    	pTimer = pTimer->pNext;
    }
}

主例程应该在无限循环中调用 CheckTimerEvent() 函数,以及其他任务。

void main(void)
{
    /* Initialize all registers & variables*/
	InitMain ();
    ........
    
    /* Initial value of gTimer = 0 */
	gTimer = 0;

    /* Add a Timer1 with 1000 ms interval*/
	AddTimer(TIMER_1, 1000);

    /* Add a Timer2 with 500 ms interval*/
	AddTimer(TIMER_1, 500);

    ........
	while(1)
    {
        /* Check the Timer Events */
    	CheckTimerEvent();
        .......
        /* Write other task here */
    }
}

CheckTimerEvent() 始终检查是否有任何定时器完成间隔并调用相应的例程。

void CheckTimerEvent()
{
	undigned short nTimer;

    /* Read the global variable gTimer and reset the value */
	DisableInterrupt()
	nTimer = gTimer; 
	gTimer = 0;
	EnableInterrupt()

    /* Check for TimerId */
	if( nTimer != 0)
    {
    	if(nTimer & TIMER_1)
        {
            /* Call Timer 1 routine */
        	Timer1Function();
        }
    	if(TimerID & TIMER_2)
        {
            /* Call Timer 2 routine */
        	Timer2Function();
        }
        ........
    }
}

这是一个简单的例子,用于从单个时间基准创建多个独立计时器。希望它能帮助程序员。如有任何建议,请给我发邮件。

© . All rights reserved.