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.

Project Help and Ideas » LED Array Assistance

October 11, 2010
by bpenglase
bpenglase's Avatar

Ok, So I decided to buy a ton of LEDs on EBay, one purpose for building an LED Array, and just to have LEDs around. Anywho, I spent pretty much all afternoon Sunday making, IMHO, a really nice looking board.

Fast forward to after work today, I connect everything up, and realize I looked at the diagram wrong, and looking at the LEDs, R0/C0 was on the upper Right, instead of the Upper Left. I then realized the diagram was actually already looking at the LEDs, allwell.

So, I rewired some stuff, however everything is one column off. i.e. R0/C2 lights, then R0/C1, then R0/C4, then R0/C3, repeat during the test pattern.

What I would like to know is if there is a way to switch the polarity, in order to correct the switch in the columns? It would be nice if I can do it in software, but I know sometimes there just isn't anything you can fix in software, and you have to fix it on the hardware.

Image Sorry for the crappy shot from my cellphone, but I figure it was worth including :)

October 12, 2010
by Rick_S
Rick_S's Avatar

I haven't played around with the software for the LED Array project in quite a while but you should definiately be able to 'fix' that in software.

The section you will want to focus on is in the interrupt function (SIGNAL(SIG_OVERFLOW0))

This would be the section of code.

  // 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));
  }

I haven't tried it but if you swap the activate row driver SOURCE and activate row driver SINK sections like this below, it may work. It's early though so you may have to do some more tweaking.

  // 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 SOURCE
    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 SINK
    PORTB &= ~(1 << (PB1 + real_row));
    DDRB |= (1 << (PB1 + real_row));
 }

Hope that works,

Rick

October 13, 2010
by hevans
(NerdKits Staff)

hevans's Avatar

Hi bpenglase,

I think Rick is very much on the right track. You also need to change the direction of the column drier thought. Notice how each section calls ledarray_set_columndriver() for each LED in the row. The last parameter tells it whether that set of columns is a forward or a backward LED. If you flip the 1 and 0 on those two calls, and do the change Rick suggested I think your array should work.

Humberto

October 16, 2010
by bpenglase
bpenglase's Avatar

Sorry I didn't reply sooner, been swamped at work, and having to study for an upcoming certification exam.

Anywho, I did get it working combing what you both said. Changing what Rick said, and instead of putting 0's in, I just switched the functions from OR to AND NOT, or vice-versa in the columndriver() section. However, when I did this on the PORTx lines throughout the function, it worked, but the LEDs that were suppose to be off, still were lit slightly dim --

Picture

So while I played with that block, I either got it to how it was, nothing was lighting, some were lighting very dim, or the odd-rows were lit, and were turn off during the init (reversed, and only half working). Then I had a thought, and on the if sense==onoff block, under the else, to match PORT and DDR, as it was originally. Soon as I did that it worked. So here is the final code: ColumnDriver():

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)));
    }
  }
}

And the rows:

  // 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 SOURCE
    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 SINK
    PORTB &= ~(1 << (PB1 + real_row));
    DDRB |= (1 << (PB1 + real_row));
  }

Thanks for the help on this one! Before when I tried changing stuff, I just figured I had to flip all of the bits, but I see thats wrong. And of course as I've been working with the code, I've understood it more.

And a picture of the final product: Fixed The "light" on the bottom is just Lens flare on on the camera, as you can see, there are no lights down there, and the ones which are suppose to be off, are off :)

October 17, 2010
by Rick_S
Rick_S's Avatar

Looks good! I'm glad to see you got it working. The LED Array project is one that gave me quite a sense of accomplishment when I finished.

I made mine a stand alone display that just displays pre-programmed text. You can see the different effects I added in this Video. Here's a Link to my code on the forums. You can find the code about half way down the page in case you want to add any of the effects to your project.

Enjoy your display...

Rick

January 02, 2011
by TonyPat
TonyPat's Avatar

Hello,

I am new in LED project, following is my code then I cannot compile, MAKE complains as the following then I do not know how to correct in 2 lines: line 112 and line 332 which are: SIGNAL(SIG_OVERFLOW0) { and the last line of main function which is only the closing bracket }.

Could any body help. Thank you very much. Hong

C:\Users\Hong\Documents\Learning\Nerdkit\Code\Code\ledarray_educational>make
make -C ../libnerdkits
make[1]: Entering directory `C:/Users/Hong/Documents/Learning/Nerdkit/Code/Code/
libnerdkits'
make[1]: Nothing to be done for `all'.
make[1]: Leaving directory `C:/Users/Hong/Documents/Learning/Nerdkit/Code/Code/l
ibnerdkits'
avr-gcc -g -Os -Wall -mmcu=atmega168  -Wl,-u,vfprintf -lprintf_flt -Wl,-u,vfscan
f -lscanf_flt -lm -o ledarray.o ledarray.c ../libnerdkits/delay.o ../libnerdkits
/lcd.o ../libnerdkits/uart.o
ledarray.c: In function 'ledarray_set_columndriver':
ledarray.c:112: error: static declaration of '__vector_16' follows non-static de
claration
ledarray.c:112: error: previous declaration of '__vector_16' was here
ledarray.c:317: warning: 'main' is normally a non-static function
ledarray.c:332: error: expected declaration or statement at end of input
make: *** [ledarray.hex] Error 1

====================

#include <stdio.h>

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <inttypes.h>

#include "../libnerdkits/delay.h"
#include "../libnerdkits/uart.h"
// #include "../libnerdkits/io_328p.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];

// in array la_data[COLS], each data is one 8 bit integer, represent one column, we need only 5 bit to represent 5 rows, example:
// la_data[2] = 00001000 meaning led at section row 3 column 2 is on ( 1 at row 3, we have only 5 rows: row0 to row4)
inline uint8_t ledarray_get(uint8_t i, uint8_t j) {

//01-02-11 Hong return (la_data[j] ~ (1<<i)) >> i;  //read column then mask bit i, 1 data in la_data array is 1 column, we use only 5 bits then waist 3 bits
// shift right data to i bit to get the data bit we want to bit 0 so when we read we have only 2 states 0 or 1 at bit 0

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 (onoff = 1){ 
la_data[j] |= 1 << i;
  }else {la_data[j] &  = ~(1 << i);
    }
 */

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

}

//sense variable indicates direction of LED: sense == 1 indicates COL wire must be
//hight for LED to turn on. sense == 0, COL wire must be low to turn LED on
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){ //led on
      DDRC |= (1 << (PC0 + j));
      if(sense) {
        PORTC |= (1 << (PC0 + j));
      } else {
        PORTC &= ~(1<< (PC0 + j));
      }
    } else { // led off, pins to high impedance
      DDRC &= ~(1 << (PC0 + j));
      PORTC &= ~(1 << (PC0 + j));
    }
  } else {

    if(onoff){ //led on
      DDRD |= (1 << (PD2 + (j-6)));

      if(sense) {
        PORTD |= (1 << (PD2 + (j-6)));
      } else {
        PORTD &= ~(1<< (PD2 + (j-6)));
      }
    } else { // led off, pins to high impedance
      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) );  
}

SIGNAL(SIG_OVERFLOW0) {

  // 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 / 2;
    for(j=0; j<COLS/2; j++) {
      ledarray_set_columndriver(j, 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 low 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));
    }
  }
}

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;
  ledarray_blank();

  for(i=0;i<ROWS;i++) {
    for(j=0;j<COLS;j++) {
      ledarray_set(i,j, 1 - ledarray_get(i,j));
      delay_ms(30);
    }
  }

  for(i=0;i<ROWS;i++) {
    for(j=0;j<COLS;j++) {
      ledarray_set(i,j, 1 - ledarray_get(i,j));
      delay_ms(30);
    }
  }
}

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) {
    // set the row of led_array at the right offset.
            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 do_scrolling_display() {
  ledarray_blank();
  int8_t offset = 0, next_offset = 0;
  uint8_t is_started = 0;
  char x=' ';

  while(1) {
    if(is_started) {
      delay_ms(125);
      ledarray_left_shift(); 
      if(next_offset > 0) {
        offset -= 1;
        next_offset -= 1;
      } else {
        is_started = 0;
      }
      font_display(x, offset); 
    } else {
      offset = COLS-1;
    }
    // if we can now accept a new character, tell the computer
    if(next_offset == COLS)
      uart_write('n');

    while(uart_char_is_waiting()) {
      if(is_started)
        offset = next_offset;

      x = uart_read(); 
      if(x=='a') {
        ledarray_blank();
        return;
      }
      font_display(x, offset);
      next_offset = offset + font_width(x)+1;
      is_started = 1;  
      // if we can now accept a new character, tell the computer
      if(next_offset <= COLS)
        uart_write('n');
    }
  }
}

void do_simple_display() {
  ledarray_blank();
  uint8_t offset = 0;
  char x = ' ';

  while(1){
    x = uart_read();
    if(x == 'z'){
      ledarray_blank();
      offset = 0;
    } else {

        //fill in code to render character here
        font_display(x, 2); delay_ms(100);
    }
  }

}

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;

  while(1){
    do_simple_display();
    //do_scrollint_display();
  }
}
January 02, 2011
by hevans
(NerdKits Staff)

hevans's Avatar

Hi TonyPat,

It looks like you are missing a closing bracket } that should be on line 90 of the code above. Notice on the error you are getting the compiler is telling you it is confused about something the ledarray_set_columndriver() function. That was the first clue about where to look.

Humberto

Post a Reply

Please log in to post a reply.

Did you know that there are Power MOSFETs for switching big loads on and off? Learn more...