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.

Support Forum » Can you store a message to be displayed on the LED board?

October 13, 2011
by Gianni
Gianni's Avatar

I'd like to have several lines of text appear on the 5x24 kit, one line after another, regularly timed. (not scrolling) Do I have to have a computer hooked up continuously?

If I can, what is the number of commands it could store? It is basically 24bytes per display, if I'm understanding this correctly.

Also, what is the minimum recommended spacing of the LEDs? Can I have them as close as 7.5mm without worrying about them overheating? (I know soldering them that close will be tough.)

For example, if a # sign is an "on" led, I'd like to have the following blocks on for 2 seconds each, then change to the next one.

# #  #  ### ### # #                                  
# # # # # # # # # #                                     
### ### ### ### ###                                 
# # # # #   #     #                               
# # # # #   #   ###

##    ##   #  # # 
# #   # # # # # #
### # # # ### ### 
# #   # # # #   #
##    ##  # # ###

### ###  # # ### # #                            
 #  # #  # # # # # #                           
 #  # #  ### # # # #                           
 #  # #    # # # # #                         
 #  ###  ### ### ###
October 14, 2011
by Rick_S
Rick_S's Avatar

I did this a couple of years ago. Here is a link to a discussion about its beginnings and the final code I made is about half way thru (if the post id doesn't take you directly to it). My code will allow for static display, scrolling display, text scroll up from bottom or down from top as individual characters.

Here is another forum link for my build using blue Christmas lights.

These are you tube links to what my finished array looked like.

Link 1 Final Version Link 2 Link 3

Rick

October 14, 2011
by Gianni
Gianni's Avatar

Thank you very much Rick, I will let you know how it comes out! If anyone has an input on the spacing or memory capacity let me know!

October 25, 2011
by Gianni
Gianni's Avatar

Rick, I finally got back to this, but am having trouble getting your code to run. Can you or anyone else give me a pointer to how to go from copying your chunk of code to running it? While I'd like to go through and learn from the beginning, I'd also like to just mod your code for the time being.

I can get the ledarray.c to load in the ledarray_template folder, and just tried to replace the code in the ledarray.c file with yours, save, and run the make file, but the command window says:

make -C ../libnerdkits
make: *** ../libnerdkits: No such file or directory. Stop.
make: *** [ledarray.hex] Error 2

I know the above isn't code but I indented it so it's easier to read. The code of your's I'm referring to is:

// ledarray.c
// for NerdKits with ATmega168
// mrobbins@mit.edu
// Modified by Rick Shear

#define F_CPU 14745600
#include "../libnerdkits/io_328p.h"

#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 "font.h"

// PIN DEFINITIONS:
//
// PB1-5 ROW DRIVERS (0-4)
// PC0-5,PD2-7: COLUMN DRIVERS (0-11)
#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(50);

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

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(7);
            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=90;

  // 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) {

//ledarray_testpattern();

ledarray_blank();
offset=0;
font_display_ud('H', 2,1); delay_ms(100);
font_display_ud('A', 6,0); delay_ms(100);
font_display_ud('P', 10,1); delay_ms(100);
offset=14;
font_display_ud('P', offset,0); offset += font_width('P')+1; delay_ms(100);
font_display_ud('Y', offset,1); offset += font_width('Y')+1; delay_ms(1500);
for(i=0;i<5;i++){
    ledarray_shift_up();
    delay_ms(80);
}
  // for(i=0;i<COLS;i++){
//  ledarray_left_shift();
//  delay_ms(50);
//}

    ledarray_blank();

scroll_display(PSTR("BIRTHDAY"),80);

ledarray_blank();
offset=1;
font_display_ud('D', offset,1); offset += font_width('D')+1; //delay_ms(1000);
font_display_ud('O', offset,1); offset += font_width('O')+1; //delay_ms(1000);
font_display_ud('N', offset,1); offset += font_width('N')+1; //delay_ms(1000);
font_display_ud('N', offset,1); offset += font_width('N')+1; //delay_ms(1000);
font_display_ud('A', offset,1); offset += font_width('A')+1; delay_ms(1500);

for(i=0;i<5;i++){
    ledarray_shift_down();
    delay_ms(80);
}

  }
}

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;
*/

  // mode 1: test pattern
  do_testpattern();

  return 0;
}
October 26, 2011
by Rick_S
Rick_S's Avatar

You have to have the folder that you put the file into in the nerdkit code folder. The program has dependencies that depend on the files in the libnerkits folder being where they would be in a typical NK setup.

October 26, 2011
by Gianni
Gianni's Avatar

Thank you Rick, I've successfully wished a Happy Birthday to Donna. I know enough/have enough time to change the message. In case someone else tries this in the future, I also needed to have the io_328p.h file in my "libnerdkits" folder. I downloaded it from:

www.nerdkits.com/files/Downloads/io_328p.h

October 26, 2011
by Rick_S
Rick_S's Avatar

LOL I'm sure she'd appreciate it. BigGrin_Green

Glad you got it to work. Have fun with it.

Rick

Post a Reply

Please log in to post a reply.

Did you know that NerdKits has been featured on Slashdot, Hack A Day, Hacked Gadgets, the MAKE blog, and other DIY-oriented websites? Learn more...