Yahoo Groups archive

Lpc2000

Index last updated: 2026-04-28 23:31 UTC

Message

RE: [lpc2100] Delay routines

2003-12-07 by Robert Adsett

At 05:02 PM 12/5/03 -0500, you wrote:
>At 09:26 AM 12/5/03 -0800, you wrote:
> >I had looked at the timers before but it was unclear if you would have to
> >give up GPIO pins by putting them into alternate function mode (we're
> >already pin-constricted).  I'm hoping for something with this type of
> >functionality but that doesn't require giving up such vital (to us)
> >resources as i/o pins.  Care to guess whether the match registers could be
> >used without putting the i/o pins into alternate function mode?
>
>I would be surprised it they couldn't be used that way.  I see two ways of
>doing it.  One is to use interrupts on match.  The other is to poll the
>status bit in the external match register (That's what I'm trying first).

A timer using timer 0.  This is a first pass and still could use some 
refinement and verification but it does show a method that works.  Using 
the instrumented set and clear in the wait routine (last routine) I get an 
11.4 uS delay when asking for 10uS which fits with the bit toggle time I 
measured earlier.  It works fine down to 1uS and fails at 0.1uS.  I haven't 
delved further to determine just what the minimum wait time is.  If you 
want to time really short event (< a few microseconds) you would probably 
want to perform the wait operation in-line rather than make a function call 
otherwise the overhead becomes significant.

The real guts are in wait_ns, everything else is setup or support.

This is a polling version (no interrupts) that should keep time as long as 
a wait or time gathering call is made every minute and a half or so.  Other 
than that there is no extra runtime overhead.

This code is forming a portion of a I/O library I'm building up.  I suspect 
I'll back off on the resolution but I haven't decided yet.


#define CLOCK_SPEED     (10000000)      /*  Nominal speed for internal  */
                                         /* clock (timer not CPU) in 
Hz.         */

         /*  Used for converting between counts and nS for time function.*/
         /* An unsigned long long so overflows are not possible during   */
         /* the conversion.                                              */
#define NS_PER_COUNT    (100uLL)

#define COUNTER_RESET   ((unsigned char)2)
#define COUNTER_ENABLE  ((unsigned char)1)

static unsigned long timing_scale_factor;

static int start_clock(
     struct _reent *r)
{
  unsigned long rate, divider;

  rate = VPB_rate( r);           /*  Get the clock rate of the           */
                                 /* peripheral bus.                      */
  if( rate == 0) {
     return -1;
     }

         /* Set clock to reset.  This will keep them at zero.            */
  T0TCR = COUNTER_RESET;

         /*  Calculate a divider that will cause the internal clock to   */
         /* approximate the target clock speed.  This should give a      */
         /* clock counting rate between the desired rate and twice the   */
         /* desired rate.                                                */
  divider = rate/ CLOCK_SPEED;
  if( divider == 0) {    /*  Sanity check on the divider.  If we have a  */
     divider = 1uL;      /* pclk slower than our desired rate we won't   */
     }                   /* be able to reach the desired rate so just    */
                         /* run as fast as we can.                       */
  T0PR = divider - 1;    /*  Set prescaler to give us our target rate.   */

         /*  Since the actual clock rate we have and our target rate may */
         /* not be the same, calculate a corrective factor for routines  */
         /* that make use of it.  This value will be divided by          */
         /* CLOCK_SPEED when dealing with time and so should produce a   */
         /* result precise to 1 part in CLOCK_SPEED.                     */
  timing_scale_factor = rate / divider;

  T0MCR &= ~7u;          /*lint !e915 */
  T0EMR &= ~0x31u;       /*lint !e915 */
  T0EMR |= 0x20u;        /*lint !e915 */

  T0TCR = COUNTER_ENABLE;        /* Enable clocks.                       */
  return 0;
}

static unsigned long long counts_to_ns( unsigned long counts)
{
  unsigned long long ns;

         /*  Convert the counts to nanoseconds taking the actual clock   */
         /* rate into account.  Note: NS_PER_COUNT * CLOCK_SPEED should  */
         /* always be 1,000,000,000.  The expanded form just shows the   */
         /* intent more clearly.                                         */
  ns = ( counts * NS_PER_COUNT * CLOCK_SPEED)/timing_scale_factor;

  return ns;
}

static unsigned long ns_to_counts( unsigned long ns)
{
  unsigned long long counts;

         /*  Convert the nanoseconds to counts taking the actual clock   */
         /* rate into account.  Note: NS_PER_COUNT * CLOCK_SPEED should  */
         /* always be 1,000,000,000.  The expanded form just shows the   */
         /* intent more clearly.  Note 2:  This can only result in a     */
         /* value larger than an unsigned long if ther are multiple      */
         /* counts in a nS, i.e. a rate > ~4GHz                          */
  counts = ((unsigned long long)ns * timing_scale_factor)/(CLOCK_SPEED * 
NS_PER_COUNT);

  return (unsigned long)counts;
}

static unsigned long high_counts = 0uL;

static void accumulate_time( void)
{
  static unsigned long last_time = 0uL;
  unsigned long current_time;

         /*  A simple polled rollover check.  This should work as long as*/
         /* this routine is called at least once every time the counter  */
         /* has incremented 1/2 of its maximum count.                    */
  current_time = T0TC;                   /*  Get the current count       */
  if(current_time < last_time) {         /*  If we have rolled over,     */
     high_counts++;                      /* increment the high oder      */
     }                                   /* accumulator.                 */
  last_time = current_time;              /*  Record last read value for  */
                                         /* the next check.              */
}

static unsigned long long get_full_counts( void)
{
  unsigned long low;
  unsigned long long full_count;

  accumulate_time();             /*  Check for rollover of HW clock.     */

         /*  Read in the clock counts.  It is necessary to perform a     */
         /* rollover check so we don't end up with the low order value   */
         /* that of after the rollover and the high order count from     */
         /* before the rollover.  If that were to happen our time would  */
         /* not increase in a monotonic fashion.                         */
  do {
     low = T0TC;         /*  Read current count.                         */

         /*  Form low order and high order counts into a single value.   */
     full_count = ((unsigned long long)high_counts << 32) | low;

     } while( low > T0TC);       /*  Check for rollover and redo if one  */
                                 /* has happened.                        */
  return full_count;             /*  All done.                           */
}

static void wait_ns(
     const unsigned long *ptr)
{
  unsigned long counts;

  accumulate_time();             /*  Check for rollover of HW clock.     */
  counts = ns_to_counts( *ptr);  /*  Convert from requested wait in nS   */
                                 /* to counts used by the HW counter.    */

         /*  If counts is at zero then the wait interval is too small    */
         /* and we should just return.  The smallest count we can deal   */
         /* with is the count that occurs between reading the HW clock   */
         /* and setting the match register.  It is quite possible there  */
         /* is a small non-zero count that will be missed because it is  */
         /* smaller than the time taken to perform those operations. The */
         /* value of this minimum will depend on the timer rate and the  */
         /* CPU execution speed.  In addition any wait will have a fixed */
         /* overhead for setup and tear down.  It may be possible to     */
         /* compensate for one or both of these on startup in a future   */
         /* revision.                                                    */
  if( counts > 0) {

     IOSET = 0x100u;
     T0EMR &= ~0x31;/*lint !e915*//*  Set external match 0 output to 0   */
                                 /* and operation on match to do nothing.*/

     T0MR0 = T0TC + counts;      /*  Set match to current time + delay   */
                                 /* time.                                */
     T0EMR |= 0x20;/*lint !e915*//*  Set operation on match to set to 1. */

     while( (T0EMR & 0x1) == 0) {        /*  Wait for match to set output*/
          }                              /* to 1.                        */
     T0EMR &= ~0x31;/*lint !e915*//*  Set external match 0 output to 0   */
                                 /* and operation on match to do nothing */
                                 /* so we don't trigger next wait early  */
                                 /* by accident.                         */
     IOCLR = 0x100u;
     }
}



Robert

" 'Freedom' has no meaning of itself.  There are always restrictions,
be they legal, genetic, or physical.  If you don't believe me, try to
chew a radio signal. "

                         Kelvin Throop, III

Attachments

Move to quarantaine

This moves the raw source file on disk only. The archive index is not changed automatically, so you still need to run a manual refresh afterward.