NerdKits - electronics education for a digital generation

You are not logged in. [log in]

NEW: Learning electronics? Ask your questions on the new Electronics Questions & Answers site hosted by CircuitLab.

Microcontroller Programming » Slow refresh rate LED Array

March 31, 2012
by amartinez
amartinez's Avatar

Anyone, I'm trying to pick this code apart and I think the problem is with the column driver but I dare not touch the code because I have no idea what I'm doing with it as of yet.

The LED marquee scrolls fine but the refresh rate is slow, enough to not be able to read things clearly. Here's the code. It's should scroll a simple message on your LED Array. You'll see the refresh rate very slow. I noticed watching the videos that the refresh rate on the given marquees is rather quick and hardly noticeable. Not so with mine. It's very choppy, slow and blinks almost one time per second.

If you copy this code you will have to make sure your header file directory paths reflect your existing file structure.

/ ledarray.c
// ATmega168
// 24 X 5 LED matrix

#include "../avr/include/stdio.h"

#include "../avr/include/avr/io.h"
#include "../avr/include/avr/interrupt.h"
#include "../avr/include/avr/pgmspace.h"
#include "../avr/include/inttypes.h"

#include "../libnerdkits/delay.h"
#include "../libnerdkits/uart.h"

#include "font.h"

// PIN DEFINITIONS:
//
// PB1-5 ROW DRIVERS (0-4)
// PC0-5,PD2-7: COLUMN DRIVERS (0-11)
#define F_CPU 14745600
#define ROWS 5
#define COLS 24

volatile uint8_t la_row, real_row;
volatile uint8_t la_data[COLS];

    inline uint8_t ledarray_get(uint8_t i, uint8_t j) {
  if(i < ROWS && j < COLS) {
    if((la_data[j] & (1<<i)) != 0) {
      return 1;
    } else {
      return 0;
    }
  } else {
    return 0;
  }
}

inline void ledarray_set(uint8_t i, uint8_t j, uint8_t onoff) {
  if(i < ROWS && j < COLS) {
    if(onoff) {
      la_data[j] |= (1<<i);
    } else {
      la_data[j] &= ~(1<<i);
    }
  }
}

inline void ledarray_set_columndriver(uint8_t j, uint8_t onoff, uint8_t sense) {
  // cols 0-5: PC0-5
  // cols 6-11: PD2-7
  if(j < 6) {
    if(onoff) {
      PORTC |= (1 << (PC0 + j));
    } else {
      PORTC &= ~(1<< (PC0 + j));
    }
    if(sense == onoff) {
      DDRC |= (1 << (PC0 + j));
    } else {
      DDRC &= ~(1 << (PC0 + j));
      PORTC &= ~(1 << (PC0 + j));
    }
  } else {
    if(onoff) {
      PORTD |= (1 << (PD2 + (j-6)));
    } else {
      PORTD &= ~(1<< (PD2 + (j-6)));
    }  
    if(sense == onoff) {
      DDRD |= (1 << (PD2 + (j-6)));
    } else {
      DDRD &= ~(1 << (PD2 + (j-6)));
      PORTD &= ~(1 << (PD2 + (j-6)));
    }
  }
}

inline void ledarray_all_off() {
  // turn off all row drivers
  DDRB &= ~( (1<<PB1)|(1<<PB2)|(1<<PB3)|(1<<PB4)|(1<<PB5) );
  PORTB &= ~( (1<<PB1)|(1<<PB2)|(1<<PB3)|(1<<PB4)|(1<<PB5) );

  // turn off all column drivers
  DDRC &= ~( (1<<PC0) | (1<<PC1) | (1<<PC2) | (1<<PC3) | (1<<PC4) | (1<<PC5) );
  PORTC &= ~( (1<<PC0) | (1<<PC1) | (1<<PC2) | (1<<PC3) | (1<<PC4) | (1<<PC5) );
  DDRD &= ~( (1<<PD2) | (1<<PD3) | (1<<PD4) | (1<<PD5) | (1<<PD6) | (1<<PD7) );
  PORTD &= ~( (1<<PD2) | (1<<PD3) | (1<<PD4) | (1<<PD5) | (1<<PD6) | (1<<PD7) );  
}

ISR(TIMER0_OVF_vect) {
  // turn off old row driver
  DDRB &= ~(1 << (PB1 + real_row));
  PORTB &= ~(1 << (PB1 + real_row));
  ledarray_all_off();

  // increment row number
  if(++la_row == 2*ROWS)
    la_row = 0;

  // set column drivers appropriately
  uint8_t j;
  if(la_row%2 == 0) {
    // even la_row number: fill even columns
    real_row = la_row / 2;
    for(j=0; j<COLS/2; j++) {
      ledarray_set_columndriver(j, ledarray_get(real_row, 2*j), 1);
    }
    // activate row driver SINK
    PORTB &= ~(1 << (PB1 + real_row));
    DDRB |= (1 << (PB1 + real_row));
  } else {
    // odd la_row number: fill odd columns
    real_row = (la_row-1)/2;
    for(j=0; j<COLS/2; j++) {
      ledarray_set_columndriver(j, 1 - ledarray_get(real_row, 2*j + 1), 0);
    }
    // activate row driver SOURCE
    PORTB |= (1 << (PB1 + real_row));
    DDRB |= (1 << (PB1 + real_row));
  }  
}

void ledarray_init() {
  // Timer0 CK/64 (900Hz)
  TCCR0B = (1<<CS01) | (1<<CS00);
  TIMSK0 = (1<<TOIE0);

  // outputs (set row drivers high for off)
  DDRC &= ~( (1<<PC0) | (1<<PC1) | (1<<PC2) | (1<<PC3) | (1<<PC4) | (1<<PC5) );
  DDRD &= ~( (1<<PD2) | (1<<PD3) | (1<<PD4) | (1<<PD5) | (1<<PD6) | (1<<PD7) );
  DDRB &= ~( (1<<PB1)|(1<<PB2)|(1<<PB3)|(1<<PB4)|(1<<PB5) );
}

void ledarray_left_shift() {
  // shift everything one position left
  uint8_t i, j;
  for(i=0; i<ROWS; i++) {
    for(j=0; j<COLS-1; j++) {
      ledarray_set(i,j, ledarray_get(i, j+1));
    }
    ledarray_set(i,23,0);
  }
}

void ledarray_right_shift(){
    // shift everything right one position
    uint8_t i, j;
    for(i=0; i<ROWS; i++) {
        for(j=COLS-1; j>0; j--) {
        ledarray_set(i,j, ledarray_get(i, j-1));
        }
        ledarray_set(i,0,0);
    }
}

void ledarray_shift_up(){
    // shift everything up one position
    uint8_t i,j;
    for(i=0;i<ROWS-1;i++){
        for(j=0;j<COLS;j++){
            ledarray_set(i,j,ledarray_get(i+1,j));
        }
    }
    // blank bottom line to prevent 'smearing'
    for(j=0;j<COLS;j++){
        ledarray_set(4,j,0);
    }
}

void ledarray_shift_down(){
    // shift everything down one position
    uint8_t i,j;
    for(i=4;i>0;i--){
        for(j=0;j<COLS;j++){
            ledarray_set(i,j,ledarray_get(i-1,j));
        }
    }
    // blank top line to prevent 'smearing'
    for(j=0;j<COLS;j++){
        ledarray_set(0,j,0);
    }
}

void ledarray_blank() {
  uint8_t i, j;
  for(i=0; i<ROWS; i++) {
    for(j=0; j<COLS; j++) {
      ledarray_set(i,j,0);
    }
  }
}

void ledarray_testpattern() {
    uint8_t i, j, ct;
    ledarray_blank();
    // set initital pattern (every other LED on)
    for(i=0;i<ROWS;i++) {
        for(j=i%2;j<COLS;j += 2) {
        ledarray_set(i,j, 1 - ledarray_get(i,j));
        }
    }
    //wait 50ms
    delay_ms(5);

    // toggle the pattern turning what was on off then back on again 10 times
    for(ct=0;ct<20;ct++){
        for(i=0;i<ROWS;i++) {
            for(j=0;j<COLS;j++) {
                ledarray_set(i,j, 1 - ledarray_get(i,j));
            }
        }
        delay_ms(5);
        for(i=0;i<ROWS;i++) {
            for(j=0;j<COLS;j++){ 
                ledarray_set(i,j, 1 - ledarray_get(i,j));
            }
        }
        delay_ms(5);
    }
}

void font_get(char match, char *buf) {
  // copies the character "match" into the buffer
  uint8_t i;
  PGM_P p;

  for(i=0; i<FONT_SIZE; i++) {
    memcpy_P(&p, &font[i], sizeof(PGM_P));

    if(memcmp_P(&match, p,1)==0) {
      memcpy_P(buf, p, 7);
      return;
    }
  }

  // NO MATCH?
  font_get('?', buf);
}

uint8_t font_width(char c) {
  char buf[7];
  buf[1] = 0;

  font_get(c, buf);

  return buf[1];
}

void font_display(char c, uint8_t offset) {
  char buf[7]; 
  font_get(c, buf);

  uint8_t width = buf[1];
  uint8_t i, j;
  for(i=0; i<ROWS; i++) {
    for(j=0; j<width; j++) {
      if((offset + j) < COLS) {
        if( (buf[2+j] & (1<<i)) != 0) {
          ledarray_set(i,offset + j,1);
        } else {
          ledarray_set(i,offset + j,0);
        }
      }
    }
  }

  // blank the next column to the right
  for(i=0; i<ROWS; i++) {
    ledarray_set(i, offset+width, 0);
  }
}

void font_display_ud(char c, uint8_t offset, uint8_t up) {
  char buf[7]; 
  // get font data for the current character and place it into the buffer

  font_get(c, buf);

  uint8_t width = buf[1];
  uint8_t i, j, s,n;

  if(up){
  for(i=0; i<ROWS; i++) {
    n=0;
    s=4-i;
        while(s<ROWS){
            for(j=0; j<width; j++) {
                if((offset + j) < COLS) {
                    if( (buf[2+j] & (1<<n)) != 0) {
                        ledarray_set(s,offset + j,1);
                    } else {
                        ledarray_set(s,offset + j,0);
                    }
                }
            }   
            delay_ms(2);
            //  AL... THE DELAY HERE WAS 7 I CHANGED IT TO 2
            n++;
            s++;
        }
    }
    } else{
    for(i=0; i<ROWS; i++) {
    n=4-i;
    s=0;
        while(s<=i){
            for(j=0; j<width; j++) {
                if((offset + j) < COLS) {
                    if( (buf[2+j] & (1<<n)) != 0) {
                        ledarray_set(s,offset + j,1);
                    } else {
                        ledarray_set(s,offset + j,0);
                    }
                }
            }   
            delay_ms(7);
            n++;
            s++;
        }
    }
    }
 // blank the next column to the right
  for(i=0; i<ROWS; i++) {
    ledarray_set(i, offset+width, 0);
  }
}

void scroll_display(const char *s,uint8_t speed) {
  // clear display
  ledarray_blank();
  int8_t offset = 0 , next_offset = 0;
  uint8_t is_started = 0;
  uint8_t i=0;
      char x=' ';
  if(speed==0)
        speed=40;

  // begin loop and continue until nul character at end of string
  while(pgm_read_byte(s) != 0x00){

        // have we read a character? 
        if(is_started) {
            // if so, place and shift the character until it is clear of column 24
            while(next_offset>23){
                delay_ms(speed);
                // shift the display one place
                ledarray_left_shift(); 
                // check the end of the currently displayed     characters     if it's greater than zero, decrement 
                // both the offset (character display position), and     next_offset(character end position)
                if(next_offset > 0) {
                    offset -= 1;
                    next_offset -= 1;
                }
                // display the character at the new position
                font_display(x, offset); 
            }
        } else {
            // if not, set offset to # of columns -1 (23)
            offset = COLS-1;
        }
        // read the next character in the string
        x = pgm_read_byte(s++);

        // if we have already started, set the current display position to where the last character ended
        if(is_started)
            offset = next_offset;
        // display the character
        font_display(x, offset);
        // create the new character end position
        next_offset = offset + font_width(x)+1;
        // set flag to show we have been through the loop
        is_started = 1;
    }
    // Process the last character.  This is neccessary since the while bails as soon as it reads
    // the null character.  At that point, the last read character has not yet     shifted into full
    // view.  The following while loop shifts the final string character into full view.
    while(next_offset>23){
        delay_ms(speed);
        ledarray_left_shift(); 
        if(next_offset > 0) {
            offset -= 1;
            next_offset -= 1;
        }
        font_display(x, offset); 
        }

   delay_ms(speed);
   // this final loop shifts the display all the way off.
   for(i=0;i<COLS;i++){
        ledarray_left_shift();
        delay_ms(speed);
    }

    return;
}

void do_testpattern() {

  int8_t offset=0;
  int8_t i=0;
  while(1) {

// Simple scrolling message

ledarray_blank();

scroll_display(PSTR("BAD REFRESH RATE"),10);

ledarray_blank();

}

return;

  }

// }

int main() {
  ledarray_init();

  // activate interrupts
  sei();

/*
  // init serial port
  uart_init();
  FILE uart_stream = FDEV_SETUP_STREAM(uart_putchar, uart_getchar, _FDEV_SETUP_RW);
  stdin = stdout = &uart_stream;
*/

  // do test pattern has been re-written 
  do_testpattern();

  return 0;
}

Post a Reply

Please log in to post a reply.

Did you know that you can make a huge, multi-panel LED display? Learn more...