Measured frequency

A project log for Global View

Persistence of vision is awesome

JarrettJarrett 12/29/2017 at 19:583 Comments

Revisiting this project log, I had some ballpark guesstimates on timing, and it wasn't looking as good as I'd hoped. So I attacked the problem again, this time in the lab.

First of all, I simplified the main loop of code to this:

            LATAbits.LATA5 = 1;
            for(i = 0; i < VPIXELS; i++) {
                setChannel(blob, i, blueMap[i]);
            LATAbits.LATA5 = 0;
            for(i = 0; i < 2; i++) {

 That sets port A5 high while we're actually doing work and gives me something to trigger on.

Remember that setChannel is there to process data from storage into an array of the format that the driver chip takes, and LEDMap sends the data to the LED driver over SPI.

I'm expecting setChannel to take a long time, and LEDMap to be fairly quick, given that SPI is done in hardware at clockspeed/4.

Here's what the whole loop looks like (yellow is PA5, blue is SDO/ SPI data):

That doesn't look right! Why is SDO all the way to the left?

We go to LEDMap, where that data is sent:

void LEDMap(uint8_t *blob)
    uint8_t data = 0;
    XLAT = 0;
    for(int i = TABLESIZE - 1; i >= 0; i--) {
        data = *(blob + i);
    XLAT = 1;
    XLAT = 0;

 Oh yeah, that makes sense. __delay_ms(10) is a little excessive. I meant to tune that down as far as the driver would allow, but it slipped my mind completely.

Putting the scope on XLAT proves that nicely:

So two things come out of this that are very interesting. The more important takeaway is that, if the XLAT timing is reduced to almost nothing, each loop of main() should take around 750us.

The other is that a 10ms delay is nowhere near 10ms. Close to 40ms, in fact. Why is that?

The clue comes from the poor Microchip documentation for built in functions. Historically, before their (pretty excellent) redo of their framework, there was an important constant called _XTAL_FREQ.

Some built-in functions, such as the delay functions, required it to know what speed the crystal was running at, and generate accurate(ish) timings.

With the framework redesign, as far as I can tell, those delay functions are still the recommended method. You need to specify _XTAL_FREQ yourself, however.

In my system.h file, I have written:

#define SYS_FREQ        12000000L
#define FCY             SYS_FREQ/4

Hmmm. So, future note: _XTAL_FREQ must equal FCY, not SYS_FREQ.

Okay, back to the task at hand. Or at least, some fun facts,

Each byte is taking around 5us to send.

The whole block to load up the LED driver with data is about 200us to send.

Okay, so at a 750us period, I can refresh one LED driver 1333 times per second. The final board has two drivers, so I'm looking at 666Hz.

Ideally, as the globe spins, I'd have a horizontal resolution that is equal to my vertical resolution. I have 20 LEDs going up each edge, so multiply that by Pi, and I require 63-ish refreshes required for each revolution of the disc.

Treating it like a movie screen, that's a little over 10FPS at each physical point on the globe. That's not really enough for animation, but for a static light/dark image, I think it's right on the edge of being okay.

I'm not certain I can get my motor spinning that slowly, however.

But there are two optimisations that can still be done. Currently testing one out, and I think it'll be pretty fantastic if it pans out.


Ted Yapo wrote 12/29/2017 at 20:27 point

Which compiler are you using?  The free one, or the pro?

  Are you sure? yes | no

Jarrett wrote 12/29/2017 at 20:39 point

Free version. Looking at the traces again, it could be worth looking at the generated assembly in between data pulses and seeing if there's any optimisation to be done. Could get it done in half the time, potentially. Thanks for the idea!

  Are you sure? yes | no

Ted Yapo wrote 12/29/2017 at 21:16 point

There are examples on the web where the free version allegedly inserts unnecessary instructions to make the pro version worth money.

I have done a few projects with mixed C/asm code for the PIC, and it always makes me nervous.  The rules don't seem to be very explicit for inline assembly, like they are in gcc, for instance.  I'm always afraid I'll clobber something the compiler is using.

  Are you sure? yes | no