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 » 4 Shift Registers in Daisy Chain with Atmega168 for RGB PWM

June 16, 2011
by lavared
lavared's Avatar

Hi,

I have been trying to do a PWM on a 8x8 RGB LED matrix using an Atmega168 and 4 74H595 Shift Registers. Is there any code snippet for how to do the spi transfer for the 4 shift registers (each representing red, blue, green and common cathode) when the 4 shift registers are connected in daisy chain and get the input from the Atmega168 set as Master.

Thanks

June 16, 2011
by lavared
lavared's Avatar

the arrangement for it is similar to http://francisshanahan.com/index.php/2009/how-to-build-a-8x8x3-led-matrix-with-pwm-using-an-arduino/

but without the arduino board. I'm using my own nerdkit board and everything seems to be working except that i'm not getting distinct color combinations but see the red, blue, green lights on with some flickering on green leds. I think i'm not doingthe spi transfer correctly.

June 16, 2011
by Ralphxyz
Ralphxyz's Avatar

lavared, lets have a look at your code.

Ralph

June 16, 2011
by lavared
lavared's Avatar

Raplh,

Here it is..I'm not able to include full code due to formatting issues.

Lavared

/********

define __spi_clock 13 // SCK - hardware SPI

define __spi_latch 10

define __spi_data 11 // MOSI - hardware SPI

define __spi_data_in 12 // MISO - hardware SPI (unused)

define __display_enable 9

define __rows 8

define max_row rows-1

define __leds_per_row 8

define max_led leds_per_row-1

define __brightness_levels 32 // 0...15 above 28 is bad for ISR ( move to timer1, lower irq freq ! )

define max_brightness brightness_levels-1

define __fade_delay 4

define __TIMER1_MAX 0xFFFF // 16 bit CTR

define __TIMER1_CNT 0x0020 // 32 levels --> 0x0130; 38 --> 0x0157 (flicker)

define __TIMER2_MAX 0xFF // 8 bit CTR

define __TIMER2_CNT 0x1D // max 28 levels !

include <avr/interrupt.h>

include <avr/io.h>

include <stdint.h>

include <inttypes.h>

include "../libnerdkits/delay.h"

include "../libnerdkits/uart.h"

include <stdio.h>

include <stdlib.h>

volatile uint8_t brightness_red[leds_per_row][rows]; volatile uint8_t brightness_green[leds_per_row][rows]; volatile uint8_t brightness_blue[leds_per_row][rows];

uint8_t spi_transfer(volatile uint8_t data) { unsigned char datain; // Start transmission (MOSI) SPDR = data; while (!(SPSR & (1<<SPIF))) // Wait the end of the transmission { // Get return Value; datain = SPDR; // Latch the Output using rising pulse to the RCK Pin PORTB |= (1<<PB2); // Hold pulse for 1 micro second delay_ms(1); // Disable Latch PORTB &= ~(1<<PB2); }; // Return Serial In Value (MISO) return SPDR; // return the received byte, we don't need that }

Rest all code (expect main() program ) is same as given here... http://blog.spitzenpfeil.org/wordpress/wp-content/uploads/2008/10/matrix_code.pde

the main() code is given below: int main(void) { setup(); while(1){ loop(); } return 0; }

June 16, 2011
by Noter
Noter's Avatar

That's pretty hard to follow. Maybe you could post again as a code block?

June 16, 2011
by lavared
lavared's Avatar

Here it is again.

Lavared

/** #define spi_clock 13 // SCK - hardware SPI #define spi_latch 10 #define spi_data 11 // MOSI - hardware SPI #define spi_data_in 12 // MISO - hardware SPI (unused) #define display_enable 9 #define rows 8 #define max_row rows-1 #define leds_per_row 8 #define max_led leds_per_row-1 #define brightness_levels 32 // 0...15 above 28 is bad for ISR ( move to timer1, lower irq freq ! ) #define max_brightness brightness_levels-1 #define __fade_delay 4

#define __TIMER1_MAX 0xFFFF // 16 bit CTR
#define __TIMER1_CNT 0x0020 // 32 levels --> 0x0130; 38 --> 0x0157 (flicker)
#define __TIMER2_MAX 0xFF // 8 bit CTR
#define __TIMER2_CNT 0x1D // max 28 levels ! //**LK -Changed to 28 in Hexadec 0x1D from 0xFF

#include <avr/interrupt.h>   
#include <avr/io.h>
#include <stdint.h>
#include <inttypes.h>
#include "../libnerdkits/delay.h"
#include "../libnerdkits/uart.h"
#include <stdio.h>
#include <stdlib.h>

volatile uint8_t brightness_red[__leds_per_row][__rows]; 
volatile uint8_t brightness_green[__leds_per_row][__rows];
volatile uint8_t brightness_blue[__leds_per_row][__rows];

uint8_t spi_transfer(volatile uint8_t data) {
  unsigned char datain;
  // Start transmission (MOSI)
  SPDR = data;
  while (!(SPSR & (1<<SPIF)))     // Wait the end of the transmission
  {
  // Get return Value;
  datain = SPDR;
  // Latch the Output using rising pulse to the RCK Pin
  PORTB |= (1<<PB2);
  // Hold pulse for 1 micro second
  delay_ms(1);
  // Disable Latch
  PORTB &= ~(1<<PB2);
  };
  // Return Serial In Value (MISO)
  return SPDR;                    // return the received byte, we don't need that
}

Rest all code (expect main() program ) is same as given here... http://blog.spitzenpfeil.org/wordpress/wp-content/uploads/2008/10/matrix_code.pde

int main(void) {
  setup(); 
  while(1){
    loop();
  }
  return 0;
}
June 16, 2011
by lavared
lavared's Avatar
#define __spi_clock 13   // SCK - hardware SPI
#define __spi_latch 10
#define __spi_data 11    // MOSI - hardware SPI
#define __spi_data_in 12 // MISO - hardware SPI (unused)
#define __display_enable 9
#define __rows 8
#define __max_row __rows-1
#define __leds_per_row 8
#define __max_led __leds_per_row-1
#define __brightness_levels 32 // 0...15 above 28 is bad for ISR ( move to timer1, lower irq freq ! )
#define __max_brightness __brightness_levels-1
#define __fade_delay 4

#define __TIMER1_MAX 0xFFFF // 16 bit CTR
#define __TIMER1_CNT 0x0020 // 32 levels --> 0x0130; 38 --> 0x0157 (flicker)
#define __TIMER2_MAX 0xFF // 8 bit CTR
#define __TIMER2_CNT 0x1D // max 28 levels ! //**LK -Changed to 28 in Hexadec 0x1D from 0xFF

#include <avr/interrupt.h>   
#include <avr/io.h>
#include <stdint.h>
#include <inttypes.h>
#include "../libnerdkits/delay.h"
#include "../libnerdkits/uart.h"
#include <stdio.h>
#include <stdlib.h>

volatile uint8_t brightness_red[__leds_per_row][__rows]; 
volatile uint8_t brightness_green[__leds_per_row][__rows];
volatile uint8_t brightness_blue[__leds_per_row][__rows];

uint8_t spi_transfer(volatile uint8_t data) {
  unsigned char datain;
  // Start transmission (MOSI)
  SPDR = data;
  while (!(SPSR & (1<<SPIF)))     // Wait the end of the transmission
  {
  // Get return Value;
  datain = SPDR;
  // Latch the Output using rising pulse to the RCK Pin
  PORTB |= (1<<PB2);
  // Hold pulse for 1 micro second
  delay_ms(1);
  // Disable Latch
  PORTB &= ~(1<<PB2);
  };
  // Return Serial In Value (MISO)
  return SPDR;                    // return the received byte, we don't need that
}

int main(void) {
  setup(); 
  while(1){
    loop();
  }
  return 0;
}
June 16, 2011
by lavared
lavared's Avatar
Full Code given below:

    #define __spi_clock 13   // SCK - hardware SPI
    #define __spi_latch 10
    #define __spi_data 11    // MOSI - hardware SPI
    #define __spi_data_in 12 // MISO - hardware SPI (unused)
    #define __display_enable 9
    #define __rows 8
    #define __max_row __rows-1
    #define __leds_per_row 8
    #define __max_led __leds_per_row-1
    #define __brightness_levels 32 // 0...15 above 28 is bad for ISR ( move to timer1, lower irq freq ! )
    #define __max_brightness __brightness_levels-1
    #define __fade_delay 4

    #define __TIMER1_MAX 0xFFFF // 16 bit CTR
    #define __TIMER1_CNT 0x0020 // 32 levels --> 0x0130; 38 --> 0x0157 (flicker)
    #define __TIMER2_MAX 0xFF // 8 bit CTR
    #define __TIMER2_CNT 0x1D // max 28 levels !

    #include <avr/interrupt.h>   
    #include <avr/io.h>
    #include <stdint.h>
    #include <inttypes.h>
    #include "../libnerdkits/delay.h"
    #include "../libnerdkits/uart.h"
    #include <stdio.h>
    #include <stdlib.h>

    volatile uint8_t brightness_red[__leds_per_row][__rows]; 
    volatile uint8_t brightness_green[__leds_per_row][__rows];
    volatile uint8_t brightness_blue[__leds_per_row][__rows];

    uint8_t spi_transfer(volatile uint8_t data) {
      //unsigned char datain;

      // Start transmission (MOSI)
      SPDR = data;

      while (!(SPSR & (1<<SPIF)))     // Wait the end of the transmission
      {
      // Get return Value;
      datain = SPDR;
      // Latch the Output using rising pulse to the RCK Pin
      PORTB |= (1<<PB2);
      // Hold pulse for 1 micro second
      delay_ms(1);
      // Disable Latch
      PORTB &= ~(1<<PB2);
      }
      // Return Serial In Value (MISO)
      return SPDR;                    // return the received byte, we don't need that
    }
    void set_led_red(uint8_t row, uint8_t led, uint8_t red) {
      brightness_red[row][led] = red;
    }
    void set_led_green(uint8_t row, uint8_t led, uint8_t green) {
      brightness_green[row][led] = green;
    }
    void set_led_blue(uint8_t row, uint8_t led, uint8_t blue) {
      brightness_blue[row][led] = blue;
    }
    void set_led_rgb(uint8_t row, uint8_t led, uint8_t red, uint8_t green, uint8_t blue) {
      set_led_red(row,led,red);
      set_led_green(row,led,green);
      set_led_blue(row,led,blue);
    }
    void set_matrix_rgb(uint8_t red, uint8_t green, uint8_t blue) {
      uint8_t ctr1;
      uint8_t ctr2;
      for(ctr2 = 0; ctr2 <= __max_row; ctr2++) {
        for(ctr1 = 0; ctr1 <= __max_led; ctr1++) {
          set_led_rgb(ctr2,ctr1,red,green,blue);
        }
      }
    }
    void set_row_rgb(uint8_t row, uint8_t red, uint8_t green, uint8_t blue) {
      uint8_t ctr1;
      for(ctr1 = 0; ctr1 <= __max_led; ctr1++) {
          set_led_rgb(row,ctr1,red,green,blue);
      }
    }
    void set_column_rgb(uint8_t column, uint8_t red, uint8_t green, uint8_t blue) {
      uint8_t ctr1;
      for(ctr1 = 0; ctr1 <= __max_row; ctr1++) {
          set_led_rgb(ctr1,column,red,green,blue);
      }
    }
    void set_led_hue(uint8_t row, uint8_t led, int hue) {
      // see wikipeda: HSV
      float S=100.0,V=100.0,s=S/100.0,v=V/100.0,h_i,f,p,q,t,R,G,B;

        hue = hue%360;
        h_i = hue/60;            
        f = (float)(hue)/60.0 - h_i;
        p = v*(1-s);
        q = v*(1-s*f);
        t = v*(1-s*(1-f));

        if      ( h_i == 0 ) { 
          R = v; 
          G = t; 
          B = p;
        }
        else if ( h_i == 1 ) { 
          R = q; 
          G = v; 
          B = p;
        }
        else if ( h_i == 2 ) { 
          R = p; 
          G = v; 
          B = t;
        }
        else if ( h_i == 3 ) { 
          R = p; 
          G = q; 
          B = v;
        }
        else if ( h_i == 4 ) { 
          R = t; 
          G = p; 
          B = v;
        }
        else                   { 
          R = v; 
          G = p; 
          B = q;
        }
        set_led_rgb(row,led,(R*(float)(__max_brightness)),(G*(float)(__max_brightness)),(B*(float)(__max_brightness)));  
    }
    void set_row_hue(uint8_t row, int hue) {
      uint8_t ctr1;
      for(ctr1 = 0; ctr1 <= __max_led; ctr1++) {
          set_led_hue(row,ctr1,hue);
      }
    }
    void set_column_hue(uint8_t column, int hue) {
      uint8_t ctr1;
      for(ctr1 = 0; ctr1 <= __max_row; ctr1++) {
          set_led_hue(ctr1,column,hue);
      }
    }
    void set_matrix_hue(int hue) {
      uint8_t ctr1;
      uint8_t ctr2;
      for(ctr2 = 0; ctr2 <= __max_row; ctr2++) {
        for(ctr1 = 0; ctr1 <= __max_led; ctr1++) {
          set_led_hue(ctr2,ctr1,hue);
        }
      }
    }
    void fader(void) {
      uint8_t ctr1;
      uint8_t row;
      uint8_t led;

      for(ctr1 = 0; ctr1 <= __max_brightness; ctr1++) {
        for(row = 0; row <= __max_row; row++) {
          for(led = 0; led <= __max_led; led++) {
            set_led_rgb(row,led,ctr1,ctr1,ctr1);
          }
        }
        delay_ms(__fade_delay);
      } 
      for(ctr1 = __max_brightness; (ctr1 >= 0) & (ctr1 != 255); ctr1--) {
        for(row = 0; row <= __max_row; row++) {
          for(led = 0; led <= __max_led; led++) {
            set_led_rgb(row,led,ctr1,ctr1,ctr1);
          }
        }
        delay_ms(__fade_delay);
      }
    }
    void fader_hue(void) {
      int ctr1;

      for(ctr1 = 0; ctr1 < 360; ctr1=ctr1+3) {
        set_matrix_hue((float)(ctr1));
        delay_ms(__fade_delay);
      }
    }
    void no_irq_pwm(void) {  
      uint8_t cycle;
      for(cycle = 0; cycle < __max_brightness; cycle++) {
        uint8_t led;
        uint8_t row = 0b00000000;    // row: current source. on when (1)
        uint8_t red;    // current sinker when on (0)
        uint8_t green;  // current sinker when on (0)
        uint8_t blue;   // current sinker when on (0)
        for(row = 0; row <= __max_row; row++) {
          red = 0b11111111;    // off
          green = 0b11111111;  // off
          blue = 0b11111111;   // off     
          for(led = 0; led <= __max_led; led++) {
            if(cycle < brightness_red[row][led]) {
              red &= ~(1<<led);
            }
            if(cycle < brightness_green[row][led]) {
              green &= ~(1<<led);
            }
            if(cycle < brightness_blue[row][led]) {
              blue &= ~(1<<led);
            }
          }
          // *** Modify from Arduino code to suit my Atmega168 board *** 
          PORTB &= ~(1<<PB2) ;// digitalWrite(__spi_latch,LOW);// Disable Latch
          spi_transfer(blue);
          spi_transfer(green);
          spi_transfer(red);
          spi_transfer(0b00000001<<row);
          PORTB |= (1<<PB2) ;//digitalWrite(__spi_latch,HIGH);// Latch the Output using rising pulse to the RCK Pin
          PORTB &= ~(1<<PB2) ;//digitalWrite(__spi_latch,LOW);// Disable Latch
          // *** End Modify ***
        }    
      }
    }
    void no_irq_fader(void) {
      uint8_t ctr1;
      uint8_t row;
      uint8_t led;
      uint8_t ctr2;

      for(ctr1 = 0; ctr1 <= __max_brightness; ctr1++) {
        for(row = 0; row <= __max_row; row++) {
          for(led = 0; led <= __max_led; led++) {
            set_led_rgb(row,led,ctr1,ctr1,ctr1);
          }
        }
        for(ctr2 = 0; ctr2 <= __fade_delay; ctr2++) {
          no_irq_pwm();
        }
      }   
      for(ctr1 = __max_brightness; (ctr1 >= 0) & (ctr1 != 255); ctr1--) {
        for(row = 0; row <= __max_row; row++) {
          for(led = 0; led <= __max_led; led++) {
            set_led_rgb(row,led,ctr1,ctr1,ctr1);
          }
        }  
        for(ctr2 = 0; ctr2 <= __fade_delay; ctr2++) {
          no_irq_pwm();
        }
      }
    }
    void set_row_byte_hue(uint8_t row, uint8_t data_byte, int hue) {
      uint8_t led;
      for(led = 0; led <= __max_led; led++) {
        if( (data_byte>>led)&(0b00000001) ) {
          set_led_hue(row,led,hue);
        }
        else {
          set_led_rgb(row,led,0,0,0);
        }
      }
    }
    void matrix_test(void) {
      uint8_t ctr1;
      uint8_t ctr2;
      int hue;

      for(hue = 0; hue < 360; hue=hue+32) {
        for(ctr2 = 0; ctr2 <= __max_row; ctr2++) {
          for(ctr1 = 0; ctr1 <= __max_led; ctr1++) {
            set_led_hue(ctr2,ctr1,hue);
            delay_ms(5);
          }
        }
      }  
    }
    void matrix_heart_2(void) {
      int hue;
      for(hue = 0; hue < 360; hue=hue+16) {
        set_row_byte_hue(1,0b00110110,hue);
        set_row_byte_hue(2,0b01111111,hue);
        set_row_byte_hue(3,0b01111111,hue);
        set_row_byte_hue(4,0b00111110,hue);
        set_row_byte_hue(5,0b00011100,hue);
        set_row_byte_hue(6,0b00001000,hue);
        delay_ms(3*__fade_delay);  
      }
    }
    void rainbow(void) {
      uint8_t column;
      for(column = 0; column <= __max_led; column++) {
        set_column_hue(column,column*50);
      } 
    }
    void color_wave(uint8_t width) {
      uint8_t column;
      static uint16_t shift = 0;
      for(column = 0; column <= __max_led; column++) {
        set_column_hue(column,column*width+shift);
      } 
      shift++;
    }
    void random_leds(void) {
      set_led_hue((uint8_t)(rand()%(__rows)),(uint8_t)(rand()%(__leds_per_row)),(int)(rand()%(360)));
    }
    void smile_on(int hue) {
      set_row_byte_hue(0,0b00000000,hue);
      set_row_byte_hue(1,0b01100110,hue);
      set_row_byte_hue(2,0b01100110,hue);
      set_row_byte_hue(3,0b00000000,hue);
      set_row_byte_hue(4,0b00011000,hue);
      set_row_byte_hue(5,0b10011001,hue);
      set_row_byte_hue(6,0b01000010,hue);
      set_row_byte_hue(7,0b00111100,hue);
    }
    void smile_off(int hue) {
      set_row_byte_hue(0,0b00000000,hue);
      set_row_byte_hue(1,0b00000000,hue);
      set_row_byte_hue(2,0b01100110,hue);
      set_row_byte_hue(3,0b00000000,hue);
      set_row_byte_hue(4,0b00011000,hue);
      set_row_byte_hue(5,0b10011001,hue);
      set_row_byte_hue(6,0b01000010,hue);
      set_row_byte_hue(7,0b00111100,hue);
    }
    void smile_blink(int hue, uint8_t times, int pause) {
     uint8_t ctr;
     for(ctr = 0; ctr < times; ctr++) {
       delay_ms(pause);
       smile_on(hue);
       delay_ms(pause);
       smile_off(hue);
       delay_ms(pause);
       smile_on(hue);
     }
    }
    void explode(int hue, uint8_t pause) {
      set_row_byte_hue(0,0b00000000,hue);
      set_row_byte_hue(1,0b00000000,hue);
      set_row_byte_hue(2,0b00000000,hue);
      set_row_byte_hue(3,0b00011000,hue);
      set_row_byte_hue(4,0b00011000,hue);
      set_row_byte_hue(5,0b00000000,hue);
      set_row_byte_hue(6,0b00000000,hue);
      set_row_byte_hue(7,0b00000000,hue);
      delay_ms(pause);
      set_row_byte_hue(0,0b00000000,hue);
      set_row_byte_hue(1,0b00000000,hue);
      set_row_byte_hue(2,0b00111100,hue);
      set_row_byte_hue(3,0b00100100,hue);
      set_row_byte_hue(4,0b00100100,hue);
      set_row_byte_hue(5,0b00111100,hue);
      set_row_byte_hue(6,0b00000000,hue);
      set_row_byte_hue(7,0b00000000,hue);
      delay_ms(pause);
      set_row_byte_hue(0,0b00000000,hue);
      set_row_byte_hue(1,0b01111110,hue);
      set_row_byte_hue(2,0b01000010,hue);
      set_row_byte_hue(3,0b01000010,hue);
      set_row_byte_hue(4,0b01000010,hue);
      set_row_byte_hue(5,0b01000010,hue);
      set_row_byte_hue(6,0b01111110,hue);
      set_row_byte_hue(7,0b00000000,hue);
      delay_ms(pause);
      set_row_byte_hue(0,0b11111111,hue);
      set_row_byte_hue(1,0b10000001,hue);
      set_row_byte_hue(2,0b10000001,hue);
      set_row_byte_hue(3,0b10000001,hue);
      set_row_byte_hue(4,0b10000001,hue);
      set_row_byte_hue(5,0b10000001,hue);
      set_row_byte_hue(6,0b10000001,hue);
      set_row_byte_hue(7,0b11111111,hue);
      delay_ms(pause);
      set_matrix_rgb(0,0,0);
    }
    void setup_hardware_spi(void) {
      uint8_t clr;
      SPCR |= ( (1<<SPE) | (1<<MSTR) ); // enable SPI as master
      //SPCR |= ( (1<<SPR1) ); // set prescaler bits
      SPCR &= ~ ( (1<<SPR1) | (1<<SPR0) ); // clear prescaler bits
      clr=SPSR; // clear SPI status reg
      clr=SPDR; // clear SPI data reg
      SPSR |= (1<<SPI2X); // set prescaler bits
      //SPSR &= ~(1<<SPI2X); // clear prescaler bits
    }
    void setup_timer2_ovf(void) {
      // My Atmega168 runs at 14745600 hz...
      // Timer Settings, for the Timer Control Register etc. , thank you internets. ATmega168 !
      // Timer2 (8bit) Settings:
      // Timer2 affects delay() !
      // prescaler (frequency divider) values:   CS22    CS21   CS20
      //                                           0       0      0    stopped
      //                                           0       0      1      /1      62500 Hz
      //                                           0       1      0      /8       7813 Hz
      //                                           0       1      1      /32      1953 Hz
      //                                           1       0      0      /64       977 Hz
      //                                           1       0      1      /128      488 Hz
      //                                           1       1      0      /256      244 Hz
      //                                           1       1      1      /1024      61 Hz
      // irq_freq = 14745600Hz / ( 256 * prescaler )
      //
      // set irq to 56.25 Hz: CS22-bit = 1, CS21-bit = 1, CS20-bit = 1
      TCCR2B |= ( (1<<CS22) | (1<<CS21) | (1<<CS20));      
      //TCCR2B &= ~( (1<<CS20) );                        
      // Use normal mode  
      TCCR2A &= ~( (1<<WGM21) | (1<<WGM20) );
      TCCR2B &= ~( (1<<WGM22) );  
      //Timer2 Overflow Interrupt Enable  
      TIMSK2 |= (1<<TOIE2);
      TCNT2 = __TIMER2_MAX - __TIMER2_CNT;                 
      // enable all interrupts
      sei(); 
    }
    void setup_timer1_ovf(void) {
      // Arduino runs at 16 Mhz...My Atmega168 runs at 14745600 hz...
      // Timer1 (16bit) Settings:
      // prescaler (frequency divider) values:   CS12    CS11   CS10
      //                                           0       0      0    stopped
      //                                           0       0      1      /1  
      //                                           0       1      0      /8  
      //                                           0       1      1      /64
      //                                           1       0      0      /256 
      //                                           1       0      1      /1024
      //                                           1       1      0      external clock on T1 pin, falling edge
      //                                           1       1      1      external clock on T1 pin, rising edge
      //
      TCCR1B &= ~ ( (1<<CS11) );
      TCCR1B |= ( (1<<CS12) | (1<<CS10) );      
      //normal mode
      TCCR1B &= ~ ( (1<<WGM13) | (1<<WGM12) );
      TCCR1A &= ~ ( (1<<WGM11) | (1<<WGM10) );
      //Timer1 Overflow Interrupt Enable  
      TIMSK1 |= (1<<TOIE1);
      TCNT1 = __TIMER1_MAX - __TIMER1_CNT;
      // enable all interrupts
      sei(); 
    }
    ISR(TIMER1_OVF_vect) {
     uint8_t cycle; 
      // TCNT2 = __TIMER2_MAX - __TIMER2_CNT; // precharge TIMER2 to maximize ISR time --> max led brightness
      TCNT1 = __TIMER1_MAX - __TIMER1_CNT; 
      // *** Modify from Arduino code to suit my Atmega168 board ***  
      PORTB &= ~(1<<PB1) ;//digitalWrite(__display_enable,LOW); // enable display inside ISR
      // *** End Modify *** 
      for(cycle = 0; cycle < __max_brightness; cycle++) {
        uint8_t led;
        uint8_t row = 0b00000000;    // row: current source. on when (1)
        uint8_t red;    // current sinker when on (0)
        uint8_t green;  // current sinker when on (0)
        uint8_t blue;   // current sinker when on (0)
        for(row = 0; row <= __max_row; row++) {      
          red = 0b11111111;    // off
          green = 0b11111111;  // off
          blue = 0b11111111;   // off      
          for(led = 0; led <= __max_led; led++) {
            if(cycle < brightness_red[row][led]) {
              red &= ~(1<<led);
            }
            if(cycle < brightness_green[row][led]) {
              green &= ~(1<<led);
            }
            if(cycle < brightness_blue[row][led]) {
              blue &= ~(1<<led);
            }
          }    
        // *** Modify from Arduino code to suit my Atmega168 board ***  
          PORTB &= ~(1<<PB2) ;//digitalWrite(__spi_latch,LOW);// Disable Latch    
          spi_transfer(blue);
          spi_transfer(green);
          spi_transfer(red);
          spi_transfer(0b00000001<<row);
          PORTB |= (1<<PB2) ;//  digitalWrite(__spi_latch,HIGH);// Latch the Output using rising pulse to the RCK Pin
          PORTB &= ~(1<<PB2) ;//  digitalWrite(__spi_latch,LOW);// Disable Latch
        }
      } 
      PORTB |= (1<<PB1) ;//digitalWrite(__display_enable,HIGH);    // disable display outside ISR
      // *** End Modify *** 
    }
    void setup(void) {
      srand(555); 
      // *** Modify from Arduino code to suit my Atmega168 board *** 
      // pinMode(__spi_clock,OUTPUT);pinMode(__spi_latch,OUTPUT);pinMode(__spi_data,OUTPUT);pinMode(__display_enable,OUTPUT);
      DDRB |=  ( (1<<PB5)|(1<<PB2)|(1<<PB3)|(1<<PB1) );
      DDRB &= ~(1<<PB4) ;// pinMode(__spi_data_in,INPUT);
      // digitalWrite(__spi_latch,LOW);digitalWrite(__spi_data,LOW);digitalWrite(__spi_clock,LOW);
      PORTB &= ~( (1<<PB2)|(1<<PB3)|(1<<PB5) );
      // *** End Modify *** 
      setup_hardware_spi();
      delay_ms(10);
      set_matrix_rgb(0,0,0);  
      setup_timer1_ovf();
      // display enable/disable is done inside the ISR !
    }
    void loop(void) {  
      int ctr;
      for(ctr=0; ctr < 4; ctr++) { 
        fader();
      }
      for(ctr=0; ctr < 2; ctr++) { 
        fader_hue();
      }
      for(ctr=0; ctr < 1000; ctr++) { 
        color_wave(30);
      }
      for(ctr=0; ctr < 100; ctr++) { 
        rainbow();
      }
      for(ctr=0; ctr < 10; ctr++) { 
        set_matrix_hue(80);
      }
      for(ctr=0; ctr < 1; ctr++) { 
        matrix_test();
      }
      set_matrix_rgb(0,0,0);
      for(ctr=0; ctr < 250; ctr++) { 
        matrix_heart(0);
      }
      for(ctr=0; ctr < 4; ctr++) { 
        matrix_heart_2();
      }
      for(ctr=0; ctr < 10000; ctr++) { 
        random_leds();
      }
      smile_blink(200,8,100);
      delay_ms(2500);
      explode(300,150);
    }

    int main(void) {
      setup(); 
      while(1){
        loop();
      }
      return 0;
    }
June 17, 2011
by Noter
Noter's Avatar

I haven't looked at all of it yet but I think there is a problem here in the spi_transfer function. The while should loop doing nothing until it's done and then do the latch. Here I have taken the code block out of the while loop. Also, return datain since you already read it from SPDR.

uint8_t spi_transfer(volatile uint8_t data) {
  //unsigned char datain;

  // Start transmission (MOSI)
  SPDR = data;

  while (!(SPSR & (1<<SPIF)));     // Wait the end of the transmission

  // Get return Value;
  datain = SPDR;
  // Latch the Output using rising pulse to the RCK Pin
  PORTB |= (1<<PB2);
  // Hold pulse for 1 micro second
  delay_ms(1);
  // Disable Latch
  PORTB &= ~(1<<PB2);

  // Return Serial In Value (MISO)
  return datain;                    // return the received byte, we don't need that
}
June 17, 2011
by lavared
lavared's Avatar

Thanks Noter, thats what I thought. But how do you latch data to send to 4 shift registers...? If you look at lines 207-213 and 460-466, the spi function is transferring data related to red, blue, green and cathode in the reverse order. I want it to read all the 4 shift register data and then send it once it done reading from all.

June 17, 2011
by Noter
Noter's Avatar

I think all you need to do is remove the latch logic from the spi_transfer function so you can send all 4 bytes and then latch only once after the 4th byte is sent. Assuming all your shift registers have the latch lines common and they overflow from one to the next. Have you tried that?

June 17, 2011
by lavared
lavared's Avatar

yeah Noter,

I have tried that but it did'nt work. Hence i tried it this way.

June 17, 2011
by Noter
Noter's Avatar

Ok, it's been a while since I've used 74HC595 so I had to look at the datasheet again but that's the way it works. You can shift data thru them as much as you like and when you latch, the current shifted data is put to the output pins. How about making a test program with minimum code to just work the spi and shift register with a simple pattern that blinks the led's. That should be easier to get working and then you can work it back into your big program.

June 17, 2011
by Noter
Noter's Avatar

How are you driving your led's? Max current for the 74HC595 is 70ma which is enough current to drive about 3 led's. When I used the 74HC595 I had it drive transistors which in turn drove the load.

June 18, 2011
by Rick_S
Rick_S's Avatar

In the link he gave above, assuming he copied the circuit design, it looks as if they drove an 8 x 8 RGB matrix directly. I'm guessing they get away with it like the NK guys do on the LED-Array project, due to a low duty cycle.

Rick

June 18, 2011
by lavared
lavared's Avatar

Yes Noter, i'm driving them directly. I'll try to run a small sample code to test the shift register transfer.

June 18, 2011
by lavared
lavared's Avatar

yeah it is the spi transfer function that needs reworking. I'm now trying to figure out how to latch the red, blue and green leds simultaneously on the 4 shift registers.

June 18, 2011
by lavared
lavared's Avatar

can somebody point me to a code snippet for latching data on multiple shift registers?

June 19, 2011
by Noter
Noter's Avatar

Sorry but I can't find any of my old shift register code. It went away when I changed over to multiple mpu's using I2C. I still have a tube of 74HC595's and can probably wire up a few to duplicate your results if you would post your small test program so I don't have to start from scratch.

June 19, 2011
by Rick_S
Rick_S's Avatar

Here is a link to a grouping of 4 595's I used to drive a 4 digit 7 segment LED display to demonstrate a simple how to for another member who was building a Photography Club rating project. There is sample code and some photo's of the setup.

Don't know if it'll help out or not... Wink

Rick

June 19, 2011
by lavared
lavared's Avatar

Noter, here it is. I'm actually trying to send some bits for red, blue and green leds on different rows and running them. Its similar to Rick's code on 595's.

#define __spi_clock 13   // SCK - hardware SPI
#define __spi_latch 10
#define __spi_data 11    // MOSI - hardware SPI
#define __spi_data_in 12 // MISO - hardware SPI (unused)

#include <avr/interrupt.h>   
#include <avr/io.h>
#include <stdint.h>
#include <inttypes.h>
#include "../libnerdkits/delay.h"
#include "../libnerdkits/uart.h"
#include <stdio.h>
#include <stdlib.h>

void SPI_Write(uint8_t dataout)
{

  // Start transmission (MOSI)
  SPDR = dataout;

  // Wait for transmission to finish
  while(!(SPSR & (1<<SPIF)));

}

void SPI_Latch(void)
{
  // Latch the Output using rising pulse to the RCK Pin
  PORTB |= (1<<PB2);

   // Hold pulse for 1 micro second
   delay_us(1);

  // Disable Latch
  PORTB &= ~(1<<PB2);
}

int main(void) {

  uint8_t dataRED = 0b01000000;
  uint8_t dataGREEN = 0b00010000;
  uint8_t dataBLUE = 0b00000100;
  int cnt;
  int i_row =4;

  DDRB |=  ( (1<<PB5)|(1<<PB2)|(1<<PB3) );

  PORTB &= ~(1<<PB2);

  SPCR |= ( (1<<SPE) | (1<<MSTR) ); // enable SPI as master
  //SPCR |= ( (1<<SPR1) ); // set prescaler bits
  SPCR &= ~ ( (1<<SPR1) | (1<<SPR0) ); // clear prescaler bits
  //clr=SPSR; // clear SPI status reg
  //clr=SPDR; // clear SPI data reg
  SPSR |= (1<<SPI2X); // set prescaler bits

  while(1){

      for (cnt=1;cnt<1000;cnt++) {

      SPI_Write(dataBLUE);

      SPI_Write(dataGREEN);

      SPI_Write(dataRED);

      SPI_Write(0b00000001<<i_row);

      SPI_Latch();
      delay_ms(250);

      SPI_Write(0b00000000);

      SPI_Write(0b00000000);

      SPI_Write(0b00000000);

      SPI_Write(0b00000001<<i_row);

      SPI_Latch();
      delay_ms(250);

      if (i_row>7) { 
      i_row=1;
      }

      }

  }
  return 0;
}
June 19, 2011
by lavared
lavared's Avatar

my USB-Serial cable stopped working while testing. Is there any other way (any other cable locally available) to get it to work, so that I'll be able to flash my MCU?. Ordering from the Nerkits store might set me back by a week or so depending on the shipment of the cable.

June 20, 2011
by Noter
Noter's Avatar

Ok, I'll give it a try later this evening.

If you have a serial port you may be able to get the parts for this circuit quicker than a new usb cable. TTL_RS232

June 20, 2011
by Noter
Noter's Avatar

I made a few minor changes for my setup but otherwise your program works fine. I use the util/delay.h because my F_CPU is 8000000 vs the nerdkit's 14745600 and I don't have any led's connected to the outputs so I changed for a stable pattern that made it easy to test one output pin at a time. See if this blinks your led's in a static pattern. If not maybe a wiring problem?

#define __spi_clock 13   // SCK - hardware SPI
#define __spi_latch 10
#define __spi_data 11    // MOSI - hardware SPI
#define __spi_data_in 12 // MISO - hardware SPI (unused)

#include <avr/interrupt.h>   
#include <avr/io.h>
#include <stdint.h>
#include <inttypes.h>
//#include "../libnerdkits/delay.h"
#include "../libnerdkits/uart.h"
#include <stdio.h>
#include <stdlib.h>

#include <util/delay.h>

void SPI_Write(uint8_t dataout)
{

  // Start transmission (MOSI)
  SPDR = dataout;

  // Wait for transmission to finish
  while(!(SPSR & (1<<SPIF)));

}

void SPI_Latch(void)
{
  // Latch the Output using rising pulse to the RCK Pin
  PORTB |= (1<<PB2);

   // Hold pulse for 1 micro second
   _delay_us(1);

  // Disable Latch
  PORTB &= ~(1<<PB2);
}

int main(void) {

//  uint8_t dataRED = 0b01000000;
//  uint8_t dataGREEN = 0b00010000;
//  uint8_t dataBLUE = 0b00000100;
  int cnt;
  int i_row =4;

  DDRB |=  ( (1<<PB5)|(1<<PB2)|(1<<PB3) );

  PORTB &= ~(1<<PB2);

  SPCR |= ( (1<<SPE) | (1<<MSTR) ); // enable SPI as master
  //SPCR |= ( (1<<SPR1) ); // set prescaler bits
  SPCR &= ~ ( (1<<SPR1) | (1<<SPR0) ); // clear prescaler bits
  //clr=SPSR; // clear SPI status reg
  //clr=SPDR; // clear SPI data reg
  SPSR |= (1<<SPI2X); // set prescaler bits

  while(1){

      for (cnt=1;cnt<1000;cnt++) {

//      SPI_Write(dataBLUE);

//      SPI_Write(dataGREEN);

//      SPI_Write(dataRED);

//      SPI_Write(0b00000001<<i_row);

        SPI_Write(0b00001000); // 4th
        SPI_Write(0b00000100); // 3rd
        SPI_Write(0b00000010); // 2nd
        SPI_Write(0b00000001); // 1st shift register

      SPI_Latch();

      _delay_ms(250);

      SPI_Write(0b00000000);

      SPI_Write(0b00000000);

      SPI_Write(0b00000000);

      SPI_Write(0b00000000);
//      SPI_Write(0b00000001<<i_row);

      SPI_Latch();
      _delay_ms(250);

      if (i_row>7) { 
      i_row=1;
      }

      }

  }
  return 0;
}

test circuit

June 21, 2011
by lavared
lavared's Avatar

thanks Noter. Will try it once I receive my USB-Serial cable this weekend. Unfortunately I don't have have the parts to the wiring diagram you sent earlier.

June 26, 2011
by lavared
lavared's Avatar

Noter,

I modified the example and its is working for me too with 2 shift registers. I chained the two shift registers in a daisy chain and for each shift register I attached 8 leds (16 in total) and I lit them one after the other sequentially in a chain. It worked for me.

Thanks for helping me out. Now I need to figure out my actual problem.

July 13, 2011
by lavared
lavared's Avatar

All,

I have one question and was hoping somebody would clear it....In my code above, i'm doing the PWM on PB1 pin but it is not connected to any shift registers. The only connections I have for the 4 shift registers is MOSI, SCK, Latch pins of MCU. Is PB1 pin supposed to be empty ? Or does it need to be connected to any pins (since theoretically PWM is happening on it).

Thanks

August 16, 2011
by Ralphxyz
Ralphxyz's Avatar

Hey Paul, I tried your code in two different setups but all I get is led 1, 9, 19 and led 28 to flash (using 4 shift registers with 32 leds).

Any idea what might be happening?

Ralph

August 16, 2011
by Ralphxyz
Ralphxyz's Avatar

Duh, darn sorry I've got so many things I am looking at besides the actual code.

It is doing exactly what your program says to do.

Well now to feed it some patterns to see what happens.

Ralph

Post a Reply

Please log in to post a reply.

Did you know that negative numbers are represented in two's complement notation in binary? Learn more...