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.

Basic Electronics » Daisy chaining MC74MC595 Shift Registers

August 14, 2011
by Ralphxyz
Ralphxyz's Avatar

I am needing to use 32 bit patterns for my Water Curtain.

Or at least at the moment I am thinking that I need to use 32 bit patterns.

But for now I am just trying to use 2 shift registers to light 16 leds.

Anyway I finally was able to decipher my notes to get the pinout and using code based on Rick's Shift Register code I have a single Shift Register working passing in 8bit binary patterns.

I "think" I have the pinout correct but lets start there.

Anyone have a drawing or schematic of the mcu and multiple shift registers?

Here are my single shift register pinout notes:

    /*  74HC595              |      MCU
       Slave                 |     Master                               
    Qh   pin  9              | MISO PB4 pin 18  (optional)                                  
    SCRL pin 10 Clear Vcc(?) |                                          
    SCK  pin 11 Clock        | SCK  PB5 pin 19                                         
    RCK  pin 12 Latch        | SS   PB2 pin 16
    G    pin 13 Enable (low) |
    SER  pin 14 input        | MOSI PB3 pin 17
    Vcc  pin 16              |
    Gnd  pin 8       |      
    */

So you see I am using PB2, PB3 and PB5.

The problem I am having is that with 16 leds:

SPI_WriteRead(0b0000000000000001);
delay_ms(1250);

Turns on led1 as expected but:

SPI_WriteRead(0b0000000000000011);
delay_ms(1250);

Turns on led 1 & 2 but also led 9

Here is the code I am using:

#define F_CPU 14745600
#include <avr/io.h>
#include "../libnerdkits/delay.h"

#define SPI_PORT PORTB
#define SPI_DDR  DDRB
#define SPI_CS   PB2     // CS Slave chip select SS Master slave select PB2 pin 16

//unsigned char SPI_WriteRead(unsigned char dataout)
unsigned char SPI_WriteRead(uint16_t dataout)
{
    unsigned char datain;

    // Start transmission (MOSI)
    SPDR = dataout;                         // SPDR – SPI Data Register  pg 176

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

    // Get return Value;
    datain = SPDR;

    // Latch the Output using rising pulse to the RCK Pin
    SPI_PORT |= (1<<SPI_CS);

    //_delay_us(1);             // Hold pulse for 1 micro second
    delay_us(1);
    // Disable Latch
    SPI_PORT &= ~(1<<SPI_CS);

    // Return Serial In Value (MISO)
    return datain;
}

int main(void)
{
    unsigned char cnt;
    uint8_t i;      
    // Initial the AVR ATMega168 SPI Peripheral     
    // Set MOSI and SCK as output, others as input
    SPI_DDR = (1<<PB3)|(1<<PB5)|(1<<PB2); 
    // Latch Disable (RCK Low)
    SPI_PORT &= ~(1<<SPI_CS);       
    // Enable SPI, Master, set clock rate fck/2 (maximum)
    SPCR = (1<<SPE)|(1<<MSTR);
    SPSR = (1<<SPI2X);    // Reset the 74HC595 register

    for(;;) 
    {
        for(i=0;i<3;i++)
        {
            SPI_WriteRead(0b0000000000000000);
            delay_ms(1250);
            SPI_WriteRead(0b0000000000000001);
            delay_ms(1250);
            SPI_WriteRead(0b0000000000000011);
            delay_ms(1250);
        }
    }

    return 0;
}

I "think" the problem probable has something to do with how I set the SPI ReadWrite function.

unsigned char SPI_WriteRead(uint16_t dataout)

Thanks,

Ralph

August 15, 2011
by Ralphxyz
Ralphxyz's Avatar

Well Paul (Noter) posted a excellant photo of 4 MC74HC595's daisy chained so I can use that to confirm the pinout. I have tried his simplified code with two shift registers, but only led 1 and 9 light (flickering).

So I'll take it apart again, which I need to do anyway as I need to setup 4 shift registers like Paul has.

I sure hope I can make mine look as good as his does.

Once I have this operating I'll post it to the Library so there is a consolidated reference, but first I have to get it to work.

Ralph

August 15, 2011
by Rick_S
Rick_S's Avatar

Ralph,

I'm at work and can't really verify this, but isn't SPDR an 8 bit register. I think if you want to used SPI to send to more than one 8 bit shift register, you'd need to split your 16 bits into an upper 8 and lower 8. It may be easier to do by just bit banging it. Look at my New LED Array project code for those displays we were all grabbing. I have code there for bit-banging the shift registers on those boards and there are 8 of them for a total of 40 bits.

Rick

August 15, 2011
by Ralphxyz
Ralphxyz's Avatar

Thanks Rick that would make sense, but I thought it was passing 8 bits at a time.

Bit Banging we will go!!

I set up a new breadboard with the full 32 leds (4 shift registers) to simulate my water curtain.

I was hoping it was as Noter suggested in another thread a wiring error.

But I guess not.

Here is Paul's code from the other thread that I am also trying I believe he was using 4 shift registers.

#define F_CPU 14745600
#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) {

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

Ralph

August 16, 2011
by Ralphxyz
Ralphxyz's Avatar

Anybody have a clever scheme to break up a 32 bit number into it's 8bit components?

If I were using strings I can see how to do it but storing 0b0001111111111111111111111110000 as a 32bit number takes up less space than storing it as a string.

But breaking it up into:

0b00001111
0b11111111
0b11111111
0b11110000

Does it take more space to store a 32 bit number or 4 8bit numbers?

I think I just need to change my thinking, if I use the 4 8bit numbers then I do not have to worry about breaking down the 32bit number into it's 8 bit components which might not be as efficient in storage does simplify my processing.

Like Rick said the SPDR register is 8 bit so I might just as well leave everything 8 bit and not even think about the 32 bit number.

It is just easier to associate the 32bit number with the pattern being generated for the Water Curtain.

Now to see why I cannot get either code set to work!

Ralph

August 16, 2011
by bretm
bretm's Avatar

You can get at the four constituent bytes of a long by using a union of a long and a 4-byte array. A union is basically just like a struct except all the fields share the same memory. This thread shows a union called conver_ which shows how to do this.

August 16, 2011
by Ralphxyz
Ralphxyz's Avatar

So what takes up more space a 32bit number or 4 8bit numbers?

I'm wondering if I should take the time to process a 32 bit pattern breaking it down into it's 8bit components (thanks bretm, for that link the discussion is a"bit" over my head but I "think" I understood the 32bit number processing).

This might happen 50 or more times a second.

Eventually I am going to have a PlayList of all of the patterns I want to display for a session.

The patterns will be stored in EEPROM or might come from a pc using UART or a serial communications.

So dealing with the pattern coming from EEPROM first.

I can store a 32bit number or store four 8bit numbers.

There is a certain overhead no matter which method I use.

I am favoring using the 4 8bit numbers unless someone can point out a simpler method or a compelling reason to use the 32bit method.

Thanks for the help, I just got Noters 4 shift register code to run.

So I have the daisy chain working!!

Ralph

August 18, 2011
by carlhako
carlhako's Avatar

Hi Ralphxyz

I have recently pulled out a couple of my 74HC595's for a new project I am starting for Christmas.

I played around with the simulator here http://conductiveresistance.com/interactive-595-shift-register-simulator/ and worked out my own bit banging code that actually works. Currently I have 2 chained together controlling 16 leds but I will be chaining 30 of them together.

Anyway I was just posting the simulator as it really helped me out.

August 18, 2011
by Rick_S
Rick_S's Avatar

Why don't you set up an array like the NK guys did for the LED-Array project. That way, you create your code for the shift registers does nothing but output array data to the "display" while your other code manipulates the array as if it were a display. That seems to work quite well. Did you take a look at the code I had working with the 4 x 40 display?

Rick

August 18, 2011
by Ralphxyz
Ralphxyz's Avatar

carlhako, thanks that is a nice simulator I wish I had found it before I pulled my hair out, it might have helped.

Now in looking at the simulator I just see the spec sheet, which I have had to read a lot of times.

Rick, your 4 x 40 code was way over my head. As far as programing the 595, Noters code is absolutely the simplest I have found.

Most of the code examples I found were either for an Arduino or just plain didn't work.

I have a new thread about coming up with a schema to incorporate the I2C EEPROM with the Shift Registers to build my Water Curtain display.

Ralph

August 18, 2011
by Rick_S
Rick_S's Avatar

Ralph, I had hoped the code wouldn't be that hard to follow. I tried to comment it as best I could. Hopefully you'll get it working with Noter's code.

Rick

August 18, 2011
by carlhako
carlhako's Avatar

Hi Ralyhxyz

When i open the simulator I see a picture of the chip and on the left are some buttons you can click to pulse those buttons and toggle the SER line.

Here is my code for something else to take a look at.

 #define SER        PC0
 #define CLOCK      PC1
 #define LATCH      PC2
 #define PORT595    PORTC
 #define DDR595     DDRC

 void high_shift() { // shift out a 1
      PORT595 |= (1<<SER);
      PORT595 |= (1<<CLOCK);
      PORT595 &= ~(1<<CLOCK);
      PORT595 &= ~(1<<SER);
 }

 void low_shift() { // shift out a 0
      PORT595 |= (1<<CLOCK);
      PORT595 &= ~(1<<CLOCK);
 }

 void latch() { // latch bits to output on shift register
      PORT595 |= (1<<LATCH);
      PORT595 &= ~(1<<LATCH);
 }

 void shift_out(uint8_t data) { // push out 8 bits passed to shift register
      uint8_t loop;
      for (loop = 0; loop < 8; loop++) {
           if (data & 0x01) high_shift();
           else low_shift();
           data >>= 1; // shift data along by 1
      }
 }

And here is a simple bit of code that simulates 2 leds (a snow flake) falling down the 16 leds. This is pretty much all i had in my main while loop.

uint8_t LED_ARRAYS[2];
LED_ARRAYS[0] = 0;
LED_ARRAYS[1] = 0;
for (i=0;i<18;i++) {
    LED_ARRAYS[0] >>= 1;
    LED_ARRAYS[1] >>= 1;
    if (i==0) LED_ARRAYS[0] = 0x80; // first 2 loops generate the 2 leds to scroll down
    if (i==1) LED_ARRAYS[0] = 0xC0;
    if (i==8) LED_ARRAYS[1] = 0x80; // at loop 9 & 10 is where the bits need to cross over to 2nd variable in array.
    if (i==9) LED_ARRAYS[1] = 0xC0;
    shift_out(LED_ARRAYS[1]);
    shift_out(LED_ARRAYS[0]);
    latch();
    delay_ms(200);
}
delay_ms(200);
August 18, 2011
by Ralphxyz
Ralphxyz's Avatar

Hi carlhako, thanks for the sample code.

Yeah Rick your code is nicely commented, I just have so many things going on I easily confuse myself.

I got Pauls' (Noter) code running. He had really simplified it.

Paul's code is easy (so far) to be modify to pass in variable or array elements in place of the hardcoded 8bit numbers.

So far bretm has given me a detail comment on my new Schema thread.

I need to look at the Nerdkit's LED-Array project again to see how the display was handled.

I sure appreciate any comments or suggestions.

Ralph

Post a Reply

Please log in to post a reply.

Did you know that binary numbers use base 2 to represent numbers, and these are important for understanding microcontroller registers? Learn more...