Yahoo Groups archive

Lpc2000

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

Message

RE: [lpc2100] Delay routines

2003-12-20 by Curt Powell

Robert,
 
I note you are using IOSET/IOCLR on 0x100 (P0.08).  It doesn't apear to
be used by timer 0.  Was it included for some other purpose?
 
Curt

-----Original Message-----
From: Robert Adsett [mailto:radsett@...] 
Sent: Saturday, December 06, 2003 4:39 PM
To: lpc2100@yahoogroups.com
Subject: RE: [lpc2100] Delay routines


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



Yahoo! Groups Sponsor	

ADVERTISEMENT
 
<http://rd.yahoo.com/SIG=12cbf8iq2/M=267637.4116730.5333196.1261774/D=eg
roupweb/S=1706554205:HM/EXP=1070843978/A=1853618/R=0/*http://www.netflix
.com/Default?mqso=60178338&partid=4116730> click here	
 
<http://us.adserver.yahoo.com/l?M=267637.4116730.5333196.1261774/D=egrou
pmail/S=:HM/A=1853618/rand=419195907> 	

To unsubscribe from this group, send an email to:
lpc2100-unsubscribe@yahoogroups.com



Your use of Yahoo! Groups is subject to the Yahoo! Terms of Service
<http://docs.yahoo.com/info/terms/> .

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.