avr-gcc-list
[Top][All Lists]
Advanced

[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



reply via email to

[Prev in Thread] Current Thread [Next in Thread]