lpcware

LPC1114 32bit timer resolution

Discussion created by lpcware Employee on Jun 15, 2016
Latest reply on Jun 15, 2016 by lpcware
Content originally posted in LPCWare by deevee on Sat Oct 26 13:58:25 MST 2013
EDIT: Fixed broken dropbox links

Hey folks, I have a questions about the resolution of timers on the LPC1114 with reference to the rotating persistence of vision display project I'm working on.

I've put POV display together form an old hard drive and an LPCXpresso 1114 devkit, it looks like this:
https://www.dropbox.com/s/g5bpcxe8y17okol/2013-10-26%2020.18.57.mov

I'm having some trouble getting the 'tracking' to work properly - i.e. getting the image that I draw to stick in one place relative to the disk.

If you watch this you'll see what I mean:
https://www.dropbox.com/s/mx92xfvz57dt89d/2013-10-26%2021.13.40.mov

I'm not moving that line in code - it is moving because the 'frame rate' and 'refresh rate' are not quite timed properly.

Think of it like loosing vsync on a CRT display except this display is spinning... so rsync?

I don't have a sensor which tells me how fast the spinning disk is moving, but because the rotor is from a hard drive, its a fairly stable 5400rpm when its moving.

I'm doing the timing using a kludge based on the delay function in the timer32 library.

For reference - the delay32Ms function looks like this:

/*****************************************************************************
** Function name:delay32Ms
**
** Descriptions:Start the timer delay in milo seconds
**until elapsed
**
** parameters:timer number, Delay value in milo second
**
** Returned value:None
**
*****************************************************************************/
void delay32Ms(uint8_t timer_num, uint32_t delayInMs)
{
  if (timer_num == 0)
  {
    /* setup timer #0 for delay */
    LPC_TMR32B0->TCR = 0x02;/* reset timer */
    LPC_TMR32B0->PR  = 0x00;/* set prescaler to zero */
    LPC_TMR32B0->MR0 = delayInMs * ((SystemCoreClock/(LPC_TMR32B0->PR+1)) / 1000);
    LPC_TMR32B0->IR  = 0xff;/* reset all interrrupts */
    LPC_TMR32B0->MCR = 0x04;/* stop timer on match */
    LPC_TMR32B0->TCR = 0x01;/* start timer */
 
    /* wait until delay time has elapsed */
    while (LPC_TMR32B0->TCR & 0x01);
  }
  else if (timer_num == 1)
  {
    /* setup timer #1 for delay */
    LPC_TMR32B1->TCR = 0x02;/* reset timer */
    LPC_TMR32B1->PR  = 0x00;/* set prescaler to zero */
    LPC_TMR32B1->MR0 = delayInMs * ((SystemCoreClock/(LPC_TMR32B0->PR+1)) / 1000);
    LPC_TMR32B1->IR  = 0xff;/* reset all interrrupts */
    LPC_TMR32B1->MCR = 0x04;/* stop timer on match */
    LPC_TMR32B1->TCR = 0x01;/* start timer */
 
    /* wait until delay time has elapsed */
    while (LPC_TMR32B1->TCR & 0x01);
  }
  return;
}


If you change the /1000 to a /1000000 then you have a delay function which works in micro seconds instead of miliseconds.

My delay routine looks like this:

/*****************************************************************************
** Function name:delay32Rx
**
** Descriptions:roxel delay
** 133920 = 90x31uS
** 133920 = (31uS * 90 degrees) * (48000000 / 1) / 1000000
**
** 1488 = 31uS * (48000000/1)/1000000
**
** assuming that all that pre-scalar stuff remains the same the
** back half of that equation boils down to /48
**
** 48 = 1us * [48]
**
** MR0 = 1 = 20.833nS
**
** 1481 = approx 30.86uS
** 1480 is close
**
** parameters:none
**
** Returned value:None
**
*****************************************************************************/
void delay32Rx(uint32_t roxelConstant)
{

    /* setup timer #0 for delay */
    LPC_TMR32B0->TCR = 0x02;/* reset timer */
    LPC_TMR32B0->PR  = 0x00;/* set prescaler to zero */
    LPC_TMR32B0->MR0 = roxelConstant;
    LPC_TMR32B0->IR  = 0xff;/* reset all interrrupts */
    LPC_TMR32B0->MCR = 0x04;/* stop timer on match */
    LPC_TMR32B0->TCR = 0x01;/* start timer */

    /* wait until delay time has elapsed */
    while (LPC_TMR32B0->TCR & 0x01);


  return;
}


Using roxelConstant (roxel -> rotational pixel?) I can manipulate what goes in the timer register with more granularity than by passing a number in and dividing it.

The smallest delay I can achieve (I think) is 20.833 nano seconds.

This is how the delay gets used to draw a straight line:


while(1){
//5400rpm Drive gives a rotation frequency of 90Hz, thus one rotations takes 11.11ms, one roxel (rotational pixel/slice of pie) thus takes 11.11ms/360 = 30.86uS
LPC_GPIO2->DATA &= ~0xff; //switch all the LEDs off
delay32Rx(359*roxeltime);
LPC_GPIO2->DATA |= 0xff; //switch all the LEDs on
delay32Rx(roxeltime);
}


As you can see, the LEDs are on for a single 1 degree 'slice' of the rotation and off for the remaining 359.

Problem is, the line drifts around the display instead of staying still.

If roxeltime is set to 1480 then the line moves clockwise, if it is 1481 it moves (slightly slower) counter clockwise.

Is there a way to achieve a delay time somewhere between those two values? 1480.75 seems like it would work but you can't have 0.75 of a bit...

Can I use the pre-scaler to get a better resolution over a smaller range?
Could I use NOPs or something to stretch the timing a bit?

Thoughts much appreciated
Best
D

Outcomes