Electrical – 74HC595 Shift Register LED Array

atmegaavrc

I am kind of stuck with my shift register understanding whats happening. I uploaded an empty program to test the connection and what I see is 8 LEDs in my array all lighting up. I don't call any functions and still everything is lighting up. How is this possible?

enter image description here

MAIN

// AVR
#include <avr/io.h>
#include <util/delay.h>

// CUSTOM
#include <systemDefinitions.h>
#include <pinDefinitions.h>
#include <HC595.h>


int main(void)
{   
    while (1) {
    }

    return (0);
}

HC595.h

#define HC595_PORT PORTB
#define HC595_DDR DDRB
#define HC595_DS_POS PB0
#define HC595_SH_CP_POS PB1
#define HC595_ST_CP_POS PB2

#define HC595DataHigh() (HC595_PORT |= (1 << HC595_DS_POS))
#define HC595DataLow() (HC595_PORT &= (~(1<<HC595_DS_POS)))

void HC595Init() {
    // Make DS, SHCP and STCP output
    HC595_DDR |= ((1 << HC595_SH_CP_POS) | (1 << HC595_ST_CP_POS) | (1 << HC595_DS_POS));
}

// Sends a clock pulse on SHCP Line
void HC595Pulse() {
    // Pulse shift clock
    HC595_PORT |= (1 << HC595_SH_CP_POS); // HIGH
    HC595_PORT &= (~(1<<HC595_SH_CP_POS)); // LOW
}

// Sends a clock pulse on STCP Line
void HC595Latch() {
    // Pulse the store clock
    HC595_PORT |= (1 << HC595_SH_CP_POS); // HIGH
    _delay_loop_1(1);

    HC595_PORT &= (~(1<<HC595_ST_CP_POS)); // LOW
    _delay_loop_1(1);
}

/*

Main High level function to write a single byte to
Output shift register 74HC595. 

Arguments:
   single byte to write to the 74HC595 IC

Returns:
   NONE

Description:
   The byte is serially transfered to 74HC595
   and then latched. The byte is then available on
   output line Q0 to Q7 of the HC595 IC.

*/

void HC595Write(uint8_t data) {
    // Send each 8 bits serially
    // Order is MSB first
    for (uint8_t i = 0; i < 8; i++) {
        // Output the data on DS line according to the value of MSB
        if (data & 0b10000000) {
            // MSB is 1 so output HIGH
            HC595DataHigh();
        } else {
            // MSB is 0 so output LOW
            HC595DataLow();
        }
        HC595Pulse(); // Pulse the clock line
        data = data << 1; // Now bring the next bit at MSB Position
    }

    // Now all 8 bits have been transferred to shift register and move them to output latch at ones
    HC595Latch();
}

I understand how it should work, but I don't see how my setup is wrong. I tried now this code:

int main(void)
{
    uint8_t led = 0b100000001;
    HC595Init();    

    HC595Write(led);

    return (0);
}

Even for a basic setup, lighting up all 8 LEDs, the logic should be simple but I cannot associate the output to my input.

As an example:

  • Data: 0b11111111
  • I would expect that the 585 reads in every bit accoring to the function below
  • The LEDs that light up are 0b10101010

As far as I see, my 595Write function works correclty reading each bit per pulse and after 8 pulses, latch the data. So I see no connection to the LED pattern (11111111 -> 10101010)

Best Answer

Some issues:

  • "I don't call any functions and still everything is lighting up. How is this possible?"

    Probably because the DDR register is input as default, so that if you don't set it to outputs, the pins are left in a floating state. If it is important that this never happens, you should consider adding external pull resistors on these pins, to always hold them in a known state.

  • According to the data sheet of 74HC595: "±6-mA Output Drive at 5 V". You are driving 5/220 = 22mA per pin. 22mA * 8 = 181 mA. This is well beyond the absolute maximum ratings of the IC. It is pretty safe to assume that you have fried the chip.

    Consider adding some buffer IC with more drive strength in between the shift register and the LEDs. Quad channel MOSFET driver or similar.

  • Bare metal microcontroller applications ("freestanding") do not return from main(). Return to where? Should you return from main() you program is pretty much guaranteed to go completely haywire. You always need an eternal loop such as for(;;). The correct form of main is probably void main (void), consult your compiler manual.

Some general things:

  • Binary integer literals are non-standard. As such, it is not clear which type they default to. Meaning that you could get various implicit type promotion issues in your code. To solve this, stick to pure standard C.
  • You should put a 100nF decoupling cap close to the Vcc pin.
  • The 1uF cap is very fishy. Does it serve a purpose? Are you trying to get some slope on the binary data or what?