Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C

Create multiple independent timers using a linked-list

3.64/5 (8 votes)
4 Apr 2009CPOL2 min read 41K  
A simple C program to create a 16 timers routine from a single timer interrupt for embedded systems.

Introduction

This article gives you an idea of how to implement a timer routine for firmware development without using the Operating System. Timer is a very important tool in programming, and it makes life simple. For high level programming languages, we can use multiple timers depending on the capabilities of the system.

For some small and medium applications, porting the Operating System into a device will increase the cost significantly as its requires complex hardware. For most small and medium applications, the hardware does not support porting of Operating Systems. So in this article, I will try to give the basic concepts to create multiple independent timers from a single timer interrupt routine using C as the programming language.

All processors support at least one timer interrupt as a low priority interrupt, and it is generally used as a time base of the application.

Audience

This article is useful for those who want their binary output (.bin) file to directly execute on the hardware. Firmware development without Operating System is popular today for small and medium footprint hardware.

Code for the Timer Routine

Consider a linked-list having two elements, the timer ID and the corresponding time period, as shown below:

C++
/* 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;

where TimerID is a 16-bit variable for identification, and Period is the interval in milliseconds. Here, we can create a maximum of 16 timers, and they can be defined in the following way:

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

Now, write a function AddTimer() to populate the linked-list. Note that the timer is valid only if Period > 0.

C++
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;
}

In the above code, the function FindTimer is useful to avoid the repetition of TimerID in the linked-list. Its return NULL only when the list is blank, else it returns the last node in case the TimerID is not found within the list. If the TimerID already exists, then it return the pointers of the corresponding node. The body of the function is shown below:

C++
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);
}

Now, configuring the interrupt level 0, i.e., the timer interrupt such that the interrupt occurs every millisecond. The value of the corresponding register depends on the processor clock speed. So, put the proper value as per the instruction.

C++
#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 */
        ......
    }
}

In the above case, the compiler takes care of the stack related activity. The SystemTickEvent() function is responsible to generate the timer event by decrementing the period for each TimerID. Here, the function is inline to minimize the execution time as possible.

C++
#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;
    }
}

The main routine should have the CheckTimerEvent() function call within the infinite loop, along with other tasks.

C++
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() always checks if any timer completes the interval and calls the corresponding routine.

C++
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();
        }
        ........
    }
}

This is a simple example to create multiple independent timers from a single time base. Hope it will help programmers. For any suggestions, please drop me a mail.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)