This tutorial shows you how to control 16 LEDs with just 3 control lines. We do this by daisy chaining 74HC595 shift registers
The 74HC595 shift register has an 8 bit storage register and an 8 bit shift register. Data is written to the shift register serially, then latched onto the storage register. The storage register then controls 8 output lines. The figure below shows the 74HC595 pinout.
Pin 14 (DS) is the Data pin. On some datasheets it is referred to as "SER".
When pin 11 (SH_CP or SRCLK on some datasheets) goes from Low to High the value of DS is stored into the shift register and the existing values of the register are shifted to make room for the new bit.
Pin 12 (ST_CP or RCLK on some datasheets) is held low whilst data is being written to the shift register. When it goes High the values of the shift register are latched to the storage register which are then outputted to pins Q0-Q7.
The timing diagram below demonstrates how you would set the Q0-Q7 output pins to 11000011, assuming starting values of 00000000.
The circuit we are building is showed below, followed by the build steps
We will start with an Atmega8 breadboard circuit which we have used for many of our other tutorials. We add 2 extra breadboards and route power to these.
We add the Shift Register and connect it to +5V and Ground.
We now run the following control lines between the microcontroller and Shift Register
These are the blue wires shown below.
Next we connect up the LEDs and resistors. I used 510 Ohm resistors, but a range of other sizes are acceptable. ohmslawcalculator.com has a great LED Resistor Calculator that you can use.
To demonstrate the circuit, I wrote a small bit of code which produces a knight rider pattern on the 8 LEDs.
This is all very impressive, but didn’t I say we were controlling 16 LEDs? To do this we need to add another 74HC595 shift register, more LEDs, more resistors and more orange and blue wires.
We use the Q7’ pin to daisy chain the shift registers together.
The modified circuit is shown below.
To code to produce the 16 LED knight rider pattern is
We just stopped at 16 LEDs, but we can continue daisy chaining more shift registers. This technique is not just limited to LEDs of course and we can use it to multiply output ports to drive many other kinds of devices.
One word of warning regarding this technique. When you power on the circuit, the output lines are set to some arbitrary value. Now it takes less than a microsecond to set them to your desired values, but for some circuits this may cause problems. In that case you can use to MR and OE pins to reset the storage registers.
The 74HC595 shift register has an 8 bit storage register and an 8 bit shift register. Data is written to the shift register serially, then latched onto the storage register. The storage register then controls 8 output lines. The figure below shows the 74HC595 pinout.
Pin 14 (DS) is the Data pin. On some datasheets it is referred to as "SER".
When pin 11 (SH_CP or SRCLK on some datasheets) goes from Low to High the value of DS is stored into the shift register and the existing values of the register are shifted to make room for the new bit.
Pin 12 (ST_CP or RCLK on some datasheets) is held low whilst data is being written to the shift register. When it goes High the values of the shift register are latched to the storage register which are then outputted to pins Q0-Q7.
The timing diagram below demonstrates how you would set the Q0-Q7 output pins to 11000011, assuming starting values of 00000000.
The circuit we are building is showed below, followed by the build steps
We will start with an Atmega8 breadboard circuit which we have used for many of our other tutorials. We add 2 extra breadboards and route power to these.
We add the Shift Register and connect it to +5V and Ground.
We now run the following control lines between the microcontroller and Shift Register
- PC0 to DS
- PC1 to ST_CP
- Pc2 to SH_CP
These are the blue wires shown below.
Next we connect up the LEDs and resistors. I used 510 Ohm resistors, but a range of other sizes are acceptable. ohmslawcalculator.com has a great LED Resistor Calculator that you can use.
To demonstrate the circuit, I wrote a small bit of code which produces a knight rider pattern on the 8 LEDs.
Code:
#include
#include
#define DS_PORT PORTC
#define DS_PIN 0
#define ST_CP_PORT PORTC
#define ST_CP_PIN 1
#define SH_CP_PORT PORTC
#define SH_CP_PIN 2
#define DS_low() DS_PORT&=~_BV(DS_PIN)
#define DS_high() DS_PORT|=_BV(DS_PIN)
#define ST_CP_low() ST_CP_PORT&=~_BV(ST_CP_PIN)
#define ST_CP_high() ST_CP_PORT|=_BV(ST_CP_PIN)
#define SH_CP_low() SH_CP_PORT&=~_BV(SH_CP_PIN)
#define SH_CP_high() SH_CP_PORT|=_BV(SH_CP_PIN)
//Define functions
//======================
void ioinit(void);
void output_led_state(unsigned char __led_state);
//======================
int main (void)
{
ioinit(); //Setup IO pins and defaults
while(1)
{
/*
Output a knight rider pattern
10000000
01000000
00100000
00010000
00001000
00000100
00000010
00000001
00000010
00000100
00001000
00010000
00100000
01000000
*/
for (int i=7;i>=0;i--)
{
output_led_state(_BV(i));
_delay_ms(100);
}
for (int i=1;i<7;i++)
{
output_led_state(_BV(i));
_delay_ms(100);
}
}
}
void ioinit (void)
{
DDRC = 0b00000111; //1 = output, 0 = input
PORTC = 0b00000000;
}
void output_led_state(unsigned char __led_state)
{
SH_CP_low();
ST_CP_low();
for (int i=0;i<8;i++)
{
if (bit_is_set(__led_state, i))
DS_high();
else
DS_low();
SH_CP_high();
SH_CP_low();
}
ST_CP_high();
}
#include
#define DS_PORT PORTC
#define DS_PIN 0
#define ST_CP_PORT PORTC
#define ST_CP_PIN 1
#define SH_CP_PORT PORTC
#define SH_CP_PIN 2
#define DS_low() DS_PORT&=~_BV(DS_PIN)
#define DS_high() DS_PORT|=_BV(DS_PIN)
#define ST_CP_low() ST_CP_PORT&=~_BV(ST_CP_PIN)
#define ST_CP_high() ST_CP_PORT|=_BV(ST_CP_PIN)
#define SH_CP_low() SH_CP_PORT&=~_BV(SH_CP_PIN)
#define SH_CP_high() SH_CP_PORT|=_BV(SH_CP_PIN)
//Define functions
//======================
void ioinit(void);
void output_led_state(unsigned char __led_state);
//======================
int main (void)
{
ioinit(); //Setup IO pins and defaults
while(1)
{
/*
Output a knight rider pattern
10000000
01000000
00100000
00010000
00001000
00000100
00000010
00000001
00000010
00000100
00001000
00010000
00100000
01000000
*/
for (int i=7;i>=0;i--)
{
output_led_state(_BV(i));
_delay_ms(100);
}
for (int i=1;i<7;i++)
{
output_led_state(_BV(i));
_delay_ms(100);
}
}
}
void ioinit (void)
{
DDRC = 0b00000111; //1 = output, 0 = input
PORTC = 0b00000000;
}
void output_led_state(unsigned char __led_state)
{
SH_CP_low();
ST_CP_low();
for (int i=0;i<8;i++)
{
if (bit_is_set(__led_state, i))
DS_high();
else
DS_low();
SH_CP_high();
SH_CP_low();
}
ST_CP_high();
}
This is all very impressive, but didn’t I say we were controlling 16 LEDs? To do this we need to add another 74HC595 shift register, more LEDs, more resistors and more orange and blue wires.
We use the Q7’ pin to daisy chain the shift registers together.
The modified circuit is shown below.
To code to produce the 16 LED knight rider pattern is
Code:
#include
#include
#define DS_PORT PORTC
#define DS_PIN 0
#define ST_CP_PORT PORTC
#define ST_CP_PIN 1
#define SH_CP_PORT PORTC
#define SH_CP_PIN 2
#define DS_low() DS_PORT&=~_BV(DS_PIN)
#define DS_high() DS_PORT|=_BV(DS_PIN)
#define ST_CP_low() ST_CP_PORT&=~_BV(ST_CP_PIN)
#define ST_CP_high() ST_CP_PORT|=_BV(ST_CP_PIN)
#define SH_CP_low() SH_CP_PORT&=~_BV(SH_CP_PIN)
#define SH_CP_high() SH_CP_PORT|=_BV(SH_CP_PIN)
//Define functions
//===============================================
void ioinit(void);
void output_led_state(unsigned int __led_state);
//===============================================
int main (void)
{
ioinit(); //Setup IO pins and defaults
while(1)
{
// Output a knight rider pattern
for (int i=15;i>=0;i--)
{
output_led_state(_BV(i));
_delay_ms(50);
}
for (int i=1;i<15;i++)
{
output_led_state(_BV(i));
_delay_ms(50);
}
}
}
void ioinit (void)
{
DDRC = 0b00000111; //1 = output, 0 = input
PORTC = 0b00000000;
}
void output_led_state(unsigned int __led_state)
{
SH_CP_low();
ST_CP_low();
for (int i=0;i<16;i++)
{
if ((_BV(i) & __led_state) == _BV(i)) //bit_is_set doesn’t work on unsigned int so we do this instead
DS_high();
else
DS_low();
SH_CP_high();
SH_CP_low();
}
ST_CP_high();
}
#include
#define DS_PORT PORTC
#define DS_PIN 0
#define ST_CP_PORT PORTC
#define ST_CP_PIN 1
#define SH_CP_PORT PORTC
#define SH_CP_PIN 2
#define DS_low() DS_PORT&=~_BV(DS_PIN)
#define DS_high() DS_PORT|=_BV(DS_PIN)
#define ST_CP_low() ST_CP_PORT&=~_BV(ST_CP_PIN)
#define ST_CP_high() ST_CP_PORT|=_BV(ST_CP_PIN)
#define SH_CP_low() SH_CP_PORT&=~_BV(SH_CP_PIN)
#define SH_CP_high() SH_CP_PORT|=_BV(SH_CP_PIN)
//Define functions
//===============================================
void ioinit(void);
void output_led_state(unsigned int __led_state);
//===============================================
int main (void)
{
ioinit(); //Setup IO pins and defaults
while(1)
{
// Output a knight rider pattern
for (int i=15;i>=0;i--)
{
output_led_state(_BV(i));
_delay_ms(50);
}
for (int i=1;i<15;i++)
{
output_led_state(_BV(i));
_delay_ms(50);
}
}
}
void ioinit (void)
{
DDRC = 0b00000111; //1 = output, 0 = input
PORTC = 0b00000000;
}
void output_led_state(unsigned int __led_state)
{
SH_CP_low();
ST_CP_low();
for (int i=0;i<16;i++)
{
if ((_BV(i) & __led_state) == _BV(i)) //bit_is_set doesn’t work on unsigned int so we do this instead
DS_high();
else
DS_low();
SH_CP_high();
SH_CP_low();
}
ST_CP_high();
}
We just stopped at 16 LEDs, but we can continue daisy chaining more shift registers. This technique is not just limited to LEDs of course and we can use it to multiply output ports to drive many other kinds of devices.
One word of warning regarding this technique. When you power on the circuit, the output lines are set to some arbitrary value. Now it takes less than a microsecond to set them to your desired values, but for some circuits this may cause problems. In that case you can use to MR and OE pins to reset the storage registers.
Last edited by protostackadmin on Sat May 29, 2010 2:14 am, edited 6 times in total. |
Postar um comentário