[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [avr-gcc-list] searching for scalable software delays
From: |
Matthias G?ppner |
Subject: |
Re: [avr-gcc-list] searching for scalable software delays |
Date: |
Fri Feb 2 15:23:05 2001 |
Hello Thomas,
I have developed a module to register a timeout. When the timeout expires, a
callback function will be called. Many timeouts can run at same time.
Initialisation: timeoutInit()
registering timeout: timeout()
removing timeout: untimeout()
The resolution is 256 us, the maximum time range is 12 days at clock frequency
8 MHz. If you define CLOCK_4MHZ, it will run with 4MHz. For other frequencies
you must change the code yourself. The module uses 16 Bit-Counter T1 and
extends it to 32 Bit. Load is reduced by making use of compare interrupt A.
If you don't need many timers, why don't you just set a compare interrupt of
Timer 1?
Matthias
---------------------------------------------------------
/*
============================================================================
M O D U L: timeout.c
Copyright (C) TSP Cadolzburg 2001 All Rights Reserved
============================================================================
Job:
set/clear timeouts
============================================================================
*/
/*============================================================================*/
/* system includes */
/*============================================================================*/
#include <stdlib.h>
#include <io.h>
#include <sig-avr.h>
#include <interrupt.h>
#include <utils.h>
#include <timeout.h>
/*============================================================================*/
/* constants */
/*============================================================================*/
#ifdef MYMALLOC
# define TOUT_MAX 30
#endif
/*============================================================================*/
/* macros */
/*============================================================================*/
#define currentTime() (((avr_time_t) currentTimeHigh << 16) |
__inw_atomic(TCNT1L))
#ifdef MYMALLOC
# define free(ptr) ptr->free = 1
#endif
/*============================================================================*/
/* types */
/*============================================================================*/
typedef unsigned long avr_time_t; /* limit 12.7 days at resolution 256
useconds */
/*
Struct to hold timeout events in an ordered list
*/
typedef struct timeoutevent toutEvent;
struct timeoutevent
{
int number; /* identification */
avr_time_t callTime; /* timeout expiration time */
void (*callbackFunction)(); /* function to handle timeout */
char *arg; /* function arguments */
toutEvent *next; /* next entry in timeout list */
#ifdef MYMALLOC
char free; /* malloc()-emulation: mark free entries in pool */
#endif
};
/*============================================================================*/
/* statics */
/*============================================================================*/
static short currentTimeHigh = 0;
static int interrupt_state = 0;
static /*volatile */ toutEvent *anchor = NULL;
#ifdef MYMALLOC
static toutEvent toutPool[TOUT_MAX];
#endif
/*============================================================================*/
/* function declaration */
/*============================================================================*/
static void setTimeout(void);
static void removeTimeout(toutEvent *tout);
#ifdef MYMALLOC
static toutEvent* toutMalloc(int dummySize);
#endif
/*============================================================================
NAME
toutInit
DESCRIPTION
toutInit configures interrupts for timeout module.
============================================================================*/
void
toutInit(void)
{
#ifdef MYMALLOC
{
toutEvent *tout;
int i;
/* mark all entries as free in malloc()-emulation */
for (i = 0, tout = toutPool; i < TOUT_MAX; i++)
{
tout->free = 1;
tout++;
}
}
#endif
/* disable PWM and OC1X output */
outp(0x00, TCCR1A);
/*
bit 7: Input Capture Noice Canceller: off
bit 6: Input Capture Edge select: falling
bit 3: Clear Timer/Counter1 on Compare Match: off
*/
#ifdef CLOCK_4MHZ
/* bit 2..0: Clock1 Prescale select: 256 = 100b */
outp(0x04, TCCR1B);
#else
/* bit 2..0: Clock1 Prescale select: 1024 = 101b */
outp(0x05, TCCR1B);
#endif
/* timer 1 initial value */
__outw_atomic(0x0000, TCNT1L);
/* timer 1 overflow interrupt enable */
setBit(TIMSK, TOIE1);
/* timer 1 output compare A disable (while no timeout is set) */
clearBit(TIMSK, OCIE1A);
/* Global enable interrupts */
sei();
} /* end of toutInit() */
/*============================================================================
NAME
timeout
DESCRIPTION
timeout() registers function callbackFunction, which will be called in
diffTime * 256 useconds with argument arg.
RETURN VALUE
timeout() returns an index of the timeout wich can be used to delete the
timeout or -1 on error (if no memory could be allocated).
============================================================================*/
int
timeout(void (*callbackFunction)(char *), /* function to be called after
diffTime */
char *arg, /* function argument
*/
unsigned long diffTime) /* timeout * 256 useconds
*/
{
toutEvent *newTimeout;
toutEvent loopStartTout;
toutEvent *i = &loopStartTout;
static int nTimeout = 0;
int overflow = 0;
if ((newTimeout = malloc(sizeof(toutEvent))) == NULL)
return -1;
#ifdef CLOCK_4MHZ /* the maximum timeout for 4 MHz will only be 0x7fff ffff
(6.36 days) */
diffTime <<= 1;
#endif
/* fill elements of new entry */
newTimeout->callTime = currentTime() + diffTime;
/* sort absolute times correctly despite of overflow in addition */
if (newTimeout->callTime < currentTime())
overflow = 1;
newTimeout->callbackFunction = callbackFunction;
newTimeout->arg = arg;
newTimeout->next = NULL;
/* avoid negative numbers because -1 is reserved for error */
newTimeout->number = (++nTimeout < 0 ? 0: nTimeout);
if (!interrupt_state)
cli();
/* sort new timeout chronological into list */
for (i->next = anchor;
i->next != NULL;
i = i->next)
{
/* on overflow of absolute time walk first to timeout after wraparound */
if (overflow && (i->next->callTime > currentTime()))
continue;
if (i->next->callTime > newTimeout->callTime)
{
/* append rest of list to new entry */
newTimeout->next = i->next;
break;
}
}
if (i->next == anchor)
{
/* insert before first element, overwrite timer */
anchor = newTimeout;
if(!interrupt_state)
/* in interrupt timeout will be set after execution of callback
function */
setTimeout();
}
else
{
/* change link of previous entry to new timeout */
i->next = newTimeout;
}
if (!interrupt_state)
sei();
return nTimeout;
} /* end of timeout() */
/*============================================================================
NAME
untimeout
DESCRIPTION
This function removes the timeout with index n from timeout list. This will
be needed if a timeout becomes obsolete.
============================================================================*/
void
untimeout(int n)
{
toutEvent *i;
if (!interrupt_state)
cli();
for (i = anchor; i != NULL; i = i->next)
if (i->number == n)
{
removeTimeout(i);
setTimeout();
break;
}
if (!interrupt_state)
sei();
}
/*============================================================================
NAME
setTimeout
DESCRIPTION
- call all expired timeouts, starting with *anchor.
- If next timeout will expire in this cycle of T1 set an interrupt for it,
otherwise disable timeout interrupt.
============================================================================*/
static void
setTimeout(void)
{
unsigned short timeHigh;
unsigned short timeLow;
again:
if (anchor == NULL)
{
/* disable compare interrupt */
clearBit(TIMSK, OCIE1A);
return;
}
timeHigh = HIWORD(anchor->callTime);
timeLow = LOWORD(anchor->callTime);
/*
Check for expired timeouts:
Overflow doesn't matter because of checking for equal, not for smaller
*/
if ((timeHigh == currentTimeHigh) && (timeLow <= (unsigned short)
(__inw_atomic(TCNT1L)))
/*
Check also if currentTimeHigh has been incremented due to
very long timeout callback functions.
It is unlikely that callback functions last longer than 5 seconds.
*/
|| (timeHigh + 1 == currentTimeHigh))
{
/* time already expired ? */
if (timeLow <= (unsigned short) (__inw_atomic(TCNT1L)))
{
toutEvent oldAnchor = *anchor;
/*
timeout is already expired; call callback function directly
and check next timeout in list
*/
removeTimeout(anchor);
oldAnchor.callbackFunction(oldAnchor.arg);
goto again;
}
/* set timeout */
__outw(timeLow, OCR1AL);
/* clear pending compare A interrupts according spec */
setBit(TIFR, OCF1A);
/* enable Output Compare Register A Interrupt */
setBit(TIMSK, OCIE1A);
}
else
{
/*
/* timeout isn't in range of T1; disable Output Compare Register A
Interrupt */
clearBit(TIMSK, OCIE1A);
}
return;
}
/*============================================================================
NAME
removeTimeout
DESCRIPTION
Delete timeout from timeout list.
============================================================================*/
static void
removeTimeout(toutEvent *tout)
{
toutEvent *i;
if (tout == anchor)
{
anchor = tout->next;
}
else
{
for (i = anchor; i != NULL; i = i->next)
if (i->next == tout)
{
i->next = tout->next;
break;
}
}
free(tout);
}
/*============================================================================
NAME
(Timer 1 compare interrupt A service routine)
DESCRIPTION
call timeout callback function of expired interrupt and set new timeout
interrupt
============================================================================*/
SIGNAL(SIG_OUTPUT_COMPARE1A)
{
#ifdef AVR_TESTLED
/* indicate interrupt on LED */
outp( inp(PORTB) ^ 0x02, PORTB);
#endif
/* only for security */
if (anchor == NULL)
{
/* oops - if this matters an interrupt has corrupted timeout list */
return;
}
/* prevents calling setTimeout() twice if a new timeout is set in callback
function */
interrupt_state++;
{
toutEvent oldAnchor = *anchor;
removeTimeout(anchor);
/* DO the callback */
(oldAnchor.callbackFunction)(oldAnchor.arg);
/* set new timeout */
setTimeout();
}
interrupt_state--;
}
/*============================================================================
NAME
(Timer 1 overflow interrupt service routine)
DESCRIPTION
Increment software counter and set timeout interrupts in current cycle of T1.
============================================================================*/
SIGNAL(SIG_OVERFLOW1)
{
#ifdef AVR_TESTLED
/* indicate interrupt on LED */
outp( inp(PORTB) ^ 0x04, PORTB);
#endif
interrupt_state++;
currentTimeHigh++;
/*
previously set alarms (T1CompareInterrupt) will expire before this
overflow interrupt occurs; there is no need to test if an alarm is active
*/
setTimeout();
interrupt_state--;
}
/*============================================================================*/
/* helper functions */
/*============================================================================*/
#ifdef MYMALLOC
/*============================================================================
NAME
malloc
DESCRIPTION
malloc() allocates a structure to hold a timeout.
RETURN VALUE
pointer to allocated structure
============================================================================*/
static toutEvent *
malloc(int dummySize)
{
int i;
toutEvent *tout;
for (i = 0, tout = toutPool; i < TOUT_MAX; i++, tout++)
{
if(tout->free)
{
tout->free = 0;
return tout;
}
}
return NULL;
}
#endif
---------------------------------------------------------
Am Mit, 31 Jan 2001 schrieben Sie:
> Hi there !
>
> I'm searching for some reliable working software delay routines that can
> be scaled in usec and msec - range. Code should be "independend" from
> cpu xtal. (just define frequency and routine computes delay value ...)
>
> Anybody has an idea ?
>
> Thomas
>
>
>
> _______________________________________________
> avr-gcc-list mailing list
> address@hidden
> http://avr.jpk.co.nz/mailman/listinfo/avr-gcc-list
--
TSP - Teleprocessing Systems & Products mbH
Matthias Goeppner address@hidden
Altenreuth 5 95326 Kulmbach - Germany
phone: +49 9221 804253 fax : +49 9221 8219015