Close

Software Update

A project log for Persistence of Vision LED Matrix

Because every project needs a twist.

Matthew James BellafaireMatthew James Bellafaire 01/20/2019 at 23:380 Comments

Until now I've avoided mentioning software as much as possible, since for the most part it's been in flux throughout this project. This time around, the software is more-or-less complete with some tweaks coming here and there.

the basic format of the code hasn't changed much since last time I talked about it, but here it is: 

  1. take array (used to represent image) from program memory and move it into ram
  2. take array and split it up into multiple different color arrays
  3. shift out those color arrays to the anode-side shift registers
  4. shift the cathode-side shift registers to pulse those LED's on
  5. repeat 2-4 until a new image is loaded in.

Additionally I created an interrupt subroutine which handles the rotation calculations by interpreting the rotation rate and adjusting the frame display time. Since the matrix divides it's rotation into 5 frames we can take the total time passed between each interrupt and calculate how long it should take to be at that same point again then divide it out to determine each frame's time. 

ISR (PCINT1_vect) {
  interruptFlag = true;
}

The interrupt used is shown above, it  just flips the interrupt flag which will be handled every time the changeImage() function is called. I had to use the PCINT1 interrupt since the pins for INT0 and INT1 were carelessly used by me when I was creating the PCB. 

void changeImage() {
  /*if interruptFlag and a interrupt hasn't been triggered in the last 50ms and PC2 reads a high value */
  if (interruptFlag && micros() > 50000 + lastInterrupt && PINC & (1 << PC2)) {

    interruptFlag = false;

    //we know when the last rotation was and we know when now is
    //so lets make sure the frame displays for the right amount of
    //time for our current speed

    frameDisplayTime =  recalcAnimationTime(lastInterrupt, micros());

    //store again
    lastInterrupt = micros();

  } else {
    interruptFlag = false; //because that was a false positive (probably noise)
  }

  if (micros() > Time + frameDisplayTime) {
    Time = micros();
    currentPositionInArrays++;
    if (currentPositionInArrays >= 5) {
      currentPositionInArrays = 0;
    }

    //move from flash storage to ram
    memcpy_PF(RenderImage, array0[currentPositionInArrays] , 110);

  }
}

the code above only gets called a single time in the main loop of the program. For the most part this is sufficient since I've done a ton of optimizations recently that significantly reduced the execution time of the loop. There is a small drift in the image as a result of this method to handle the interrupt, but I think I’ll leave it as is for now. The interrupt is checked each time it's called and has to meet 2 other conditions if the flag is written true

the first condition is just to eliminate some weird behavior I observed and the second is just to try and make sure the interrupt is executed only when the opto-interrupter is actually detecting the rotation marker. then the frame display time is calculated by the function below and the new time is stored. If all the conditions aren't met however the flag gets written false since it can be assumed the interrupt was premature or just attributable to noise. 

unsigned long recalcAnimationTime(unsigned long lastTime, unsigned long currentTime) {
  return (unsigned long)((currentTime - lastTime) / 10);
}

the animation is calculated by this simple function, since everything's timed in microseconds all variables to do with frame time are unsigned longs

other than these changes the code is more-or-less the same with some bloat removed and comments added. if you have any questions feel free to comment!

that just about wraps up the recent updates to this project, functionally it's done and just needs some animations to make it come alive!

Discussions