LPC1114 32bit timer resolution

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

LPC1114 32bit timer resolution

1,219 Views
lpcware
NXP Employee
NXP Employee
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
Labels (1)
0 Kudos
12 Replies

1,072 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by starblue on Wed Nov 27 10:01:13 MST 2013

Quote: deevee
I didn't know dithering was supported in hardware, thanks for the tip! I'll look in to that.



Only on the LPC43xx parts with flash.

But in your case on the LPC1114 you could adjust the value by software, e.g. in an interrupt.
0 Kudos

1,072 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by deevee on Wed Nov 27 03:11:51 MST 2013
I didn't know dithering was supported in hardware, thanks for the tip! I'll look in to that.

I do actually have an opto-reflector under the disk and a white stripe to get a rotation time but it isn't working reliably at the moment. It's mounted *just* too far away from the platter to pick up properly so until I re-mount it I can't get sensible timing data out of the thing.

Thanks again!
D
0 Kudos

1,072 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by ksdoubleshooter on Tue Nov 26 12:37:04 MST 2013
If you want to make your display stable, you will have to get an index signal from the motor to base your timing off of. Two systems with 20ppm crystals will drift over time.

Jeff
0 Kudos

1,072 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by starblue on Tue Nov 26 01:39:11 MST 2013

Quote: deevee

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...



You can use dithering.
I.e. use 1480 for one cycle and 1481 for three cycles and repeat.

The flash based LPC43xx parts even supports that in hardware for their SCT.
0 Kudos

1,072 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by deevee on Mon Nov 25 16:03:13 MST 2013
Hey guys,

My project documentation is now up: astrarium.go2dev.co.uk - check it out!

Thanks again for your help
Best
D
0 Kudos

1,072 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by deevee on Wed Oct 30 11:42:43 MST 2013
Never too late! Version 2 will have one of those on them I think, built straight on to the PCB. The 72Mhz clock will help with timing things as well. Just need to figure out when I can get around to doing it...

Figured that's what the green was about - trying to see things which aren't there.

Thanks again!
D
0 Kudos

1,072 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by R2D2 on Wed Oct 30 07:17:35 MST 2013

Unfortunately M0 has no cycle counter (too late to change to a nice little M3 -> LPC1317 :quest: ).

Instruction set summary: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0432c/CHDCICDF.html


Quote:

Can you explain what the green tinted hi lighting means in the disassembly view?


The lighter green lines are the instructions which have been executed before...
0 Kudos

1,072 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by deevee on Wed Oct 30 06:10:33 MST 2013
Turns out that my optimisation was already set to -O0 so something else might be going on here.

Having a go with this disassembly view and counting instructions.

Can you explain what the green tinted hi lighting means in the disassembly view? The darkest one (I assume) is the instruction currently being executed. The trail of lighter green instructions behind it are confusing and I can't find reference to them in the documentation. Is it something to do with the instruction pipeline or is it just cosmetic?

If I'm using a M0 core, which version of ARM Assembly am I working with (I have a reference document here but I want to check its the correct one). I know some instructions take more clock cycles to execute than others.

Thanks again!
D

0 Kudos

1,072 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by R2D2 on Tue Oct 29 10:12:21 MST 2013
Declaring volatiles is more important for optimization. Without optimization 'useless stuff' (like your roxeltime++;roxeltime--;) shouldn't be optimized out. Anyway, to avoid surprises (if you use optimization later) it's useful to declare your variables volatile. With a little bit more experience you'll learn if that's really necessary or not.

A simple way to set a local optimization is to use 'optimize' function attribute:

__attribute__((optimize("Os"))) void function(void)
 

See also: http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html
0 Kudos

1,072 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by deevee on Tue Oct 29 09:42:45 MST 2013
Thanks and thanks for taking the time to answer my questions.

I am indeed using the LPCXpresso RedCode compiler/IDE etc so that link was great.

I'll have a go with displaying assembly and no optimisation when I get back to my desk later this evening.

How would I use volatiles (if possible) to make the time critical for loop compile without optimisations? Do I need to declare all the variables involved (the frame buffer, the loop counter etc) as volatile or is there a better way of doing it? Can I mark a section of code to have -O0 optimisation whilst the rest remains at whatever level the compiler is generally running at.

I've seen lines of code like this in the datasheet (with reference to interrupt handlers)

__asm volatile ("nop");


but is this subject to the compiler optimisation level or is it like a volatile variable?

Thanks again
Best
D
0 Kudos

1,072 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by deevee on Mon Oct 28 06:57:29 MST 2013
There has been some progress! But I fear that I have found a much wider and more subtle problem with what I'm trying to do.

As you can see from these videos, I've got the display showing the positions of the planets in the solar system on a particular date.
https://www.dropbox.com/s/hvnhwiqp93hf20l/2013-10-27%2022.33.06.mov
https://www.dropbox.com/s/xe0cmxfv4xcj4rf/2013-10-27%2022.29.02.mov


Having gotten a single line drawn on my display using the code in my first post I moved on to using an array LED states and cycling through them. 360 bytes, each one representing the state of the 8 LEDs in the display at each 1 degree slice around the display circle.

Something like this:

#define roxeltime 1263
uint8_t frameBuffer[360] = {0};

SomeFunctionWhichPopulatesTheFramebuffer();

while(1){

for (i = 0; i < 359; i++)
{
LPC_GPIO2->DATA &= ~0xff;
LPC_GPIO2->DATA |= frameBuffer;
delay32Rx(roxeltime);
}
}



I had to change the roxeltime delay value because now there are multiple GPIO changes happening, calls to the delay function and the loop to worry about. This is what I expected to happen - but I had to sort of brute force the number out because I don't know how to get an execution time for the loop. Back when I was writing assembly for PICs I could just count the number of operations and get accurate execution times.

To speed to the trail and error process of getting to the correct delay value I wrote the following piece of code to increment the roxeltime value so I could watch and see the image stabilise on the display.


uint32_t roxeltime = 1250
uint8_t frameBuffer[360] = {0};

SomeFunctionWhichPopulatesTheFramebuffer();

while(1){

for (j=0;j<900;j++){    //let the display run for a bit
                       //TIME CRITICAL FROM HERE
for (i = 0; i < 359; i++) //loop through the framebuffer
{
LPC_GPIO2->DATA &= ~0xff;  //clear the LEDs
LPC_GPIO2->DATA |= frameBuffer; //set them to whatever is in this line of the framebuffer
delay32Rx(roxeltime); //delay a set period
}
                       //TIME CRITICAL TO HERE
}


roxeltime++; //increment the delay period

Clear();
DrawString(0, 0, "Running...");
snprintf(tempstring, 8, "%d", roxeltime);
DrawString(0, 1, tempstring);
DrawScreen(); //update the OLED display with the roxeltime so we can see what it actually is while the code is running

}

return 1;
}



So I run the loop for a bit and see that a roxeltime delay value of 1263 gives a stable image that rotates around the display. I don't want it to rotate (see first post) but its close enough for now.

If I then comment out the outside loop and and use the value I just found as the roxeltime delay the image starts bugging out.

I suspect this is because the outside loop is in fact adding a little extra time on to the length of my display loop but because I don't know how long the extra instructions are taking I can't compensate for it.

Strange then that though, if I leave the outside loop in but take out the roxeltime++; line it still bugs out.

This confused me because as far as I can tell that instruction and the stuff below it which updates the display is outside the time critical loop.

Further to all this, I think the compiler is doing some optimisations (or something) which is messing with the timing of things.

For example if I kludge together
roxeltime++;
roxeltime--;


to keep that instruction in place but leave my value the same the image on the display bugs out again. Is the compiler smart enough to know that ++, -- will do nothing and thus not add those lines in?

Further more, in my SomeFunctionWhichPopulatesTheFramebuffer(); if I get it to just draw a straight line

uint8_t frameBuffer[360] = {0};
frameBuffer[0] = 0xFF;


I need a different delay time to get a smooth image then to when I have something different on the display - e.g. the positions of the planets on a particular day:

        uint8_t frameBuffer[360] = {0};
        //4th November 1988
frameBuffer[159] |= 0x01; //mercury
frameBuffer[135] |= 0x02; //venus
frameBuffer[43] |= 0x04; //earth
frameBuffer[29] |= 0x08; //mars
frameBuffer[60] |= 0x10; //jupiter
frameBuffer[274] |= 0x20; //saturn
frameBuffer[271] |= 0x40; // uranus
frameBuffer[280] |= 0x80; //neptune


Again, if the compiler can see that there are no changes to make to the GPIO port from one loop run to the next, is it skipping over the instructions?

How can I ensure that my time critical loop takes a fixed amount of time? I am making some assumptions about what the compiler is doing; it may well be me and my inexperience which is causing this to happen.

Once again, thoughts greatly appreciated
Kind regards
D
0 Kudos

1,072 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by R2D2 on Mon Oct 28 06:24:33 MST 2013
First of all : nice project.

Just a few points:

Your timer resolution problem can't be solved with software. The minimal resolution is 1/main clock = 1/48MHz, prescaler can't reduce that.

A possible solution could be another crystal, which could generate another main clock.

Delays should be generated with timers. As you realized already, modern compiler tend to optimize 'useless' stuff away.

So just adding code isn't working always. I'm not sure, which compiler you are using, LPCXpresso Optimization is described here:

http://www.support.code-red-tech.com/CodeRedWiki/CompilerOptimization

Debugging your code with displayed assembly instructions can help you to see if your code has been optimized away.

http://www.support.code-red-tech.com/CodeRedWiki/DebugViewAsm

Last not least always useful: Volatiles

http://www.support.code-red-tech.com/CodeRedWiki/CompilerOptimization?highlight=%28volatile%29

With no Optimization (-O0) your NOPs shouldn't be optimized out...
0 Kudos