Using shift register

Go To Last Post
15 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

For some experiments I want to use a 74HC595N shift register.
I have to send my bits to the register and activate them. That's not very difficult, but I don't like the code:

...
// Shift in  1
   PORTB  = (1<<PINB3);
   PORTB  = (1<<PINB1);
 
   // Shift in  0
   PORTB  = (1>>PINB3);
   PORTB  = (1<<PINB1);

   // Shift in  1
   PORTB  = (1<<PINB3);
   PORTB  = (1<<PINB1);

   // Shift in 0
   PORTB  = (1>>PINB3);
   PORTB  = (1<<PINB1); 

...

How I can make a function like shiftIn(bits); So I can send it with a function.
Example:

...
shiftIn(0xF0); //11110000 
....

How do I make such function?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks, why I could find that page? :?:

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Search on this site is broken. I knew the page was there, so I went to Google and did "site:www.avrfreaks.net 74hc595" and got the link for you.

Lee

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

A simple shift routine for the MAX7219:

// Hardware Definitions

#define MAX7219_DDR     DDRB
#define MAX7219_PORT    PORTB
#define MAX7219_SCK     PB2
#define MAX7219_DO      PB1
#define MAX7219_STB     PB3

void display_write( unsigned int dc )
{
  unsigned char i;

  MAX7219_DDR |= 1<<MAX7219_DO;         // set all outputs
  MAX7219_DDR |= 1<<MAX7219_SCK;
  MAX7219_DDR |= 1<<MAX7219_STB;
  MAX7219_PORT &= ~(1<<MAX7219_STB);    // strobe = 0

  for( i = 12; i; i-- ){                // shift 12 bits out, msb first

    MAX7219_PORT &= ~(1<<MAX7219_SCK);  // clk = 0
    MAX7219_PORT |= 1<<MAX7219_DO;
    if( (dc & (1<<11)) == 0 )
      MAX7219_PORT &= ~(1<<MAX7219_DO); // data out = bit 11
    dc <<= 1;                           // prepare next bit
    MAX7219_PORT |= 1<<MAX7219_SCK;     // clk = 1
  }

  MAX7219_PORT |= 1<<MAX7219_STB;       // strobe = 1: latch data
}

You must only change two numbers to adapt it for 8 or 16 bit.

Peter

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Here is a function I wrote for the butterfly that has been working fine. This was written for AVR-gcc. I am new to C programming so if you see a way for me to improve this function please let me know.

#define sbiBF(port,bit)  (port |= (1<<bit))   //set bit in port
#define cbiBF(port,bit)  (port &= ~(1<<bit))  //clear bit in port

/******************************************************************************
*
*   Function name:  SR_Out
*
*   returns:        none
*
*   parameters:     unsigned int SRData - data to send to the Shift Register
*
*   Purpose:        Send data to one or two 74HC595 shift registers
*                   
*
*******************************************************************************/

 // Latches data in the 74HC595 (pin 12) on rising edge
#define   SR_RCK_HIGH      sbiBF(PORTB,PB3) 
#define   SR_RCK_LOW       cbiBF(PORTB,PB3) 
// Serial output to 74HC595 pin 14
#define   SR_SER_HIGH       sbiBF(PORTB,PB2)
#define   SR_SER_LOW        cbiBF(PORTB,PB2)
// Clock output to 74HC595 pin 11
#define   SR_SCK_HIGH       sbiBF(PORTB,PB1)
#define   SR_SCK_LOW        cbiBF(PORTB,PB1)

void SR_Out (unsigned int SRData)
{
	unsigned int i = 0x80; // to send 16 bits use 0x8000
	SR_SCK_LOW;             // set the clock low.
	SR_RCK_LOW;             // set the latch low.

	while(i > 0x00)
	{
		if (SRData & i)		// Look at just one data bit
		{					// Starting with the most significant
			SR_SER_HIGH;	//if bit is high set SER high
		}
		else
		{
			SR_SER_LOW;		//if bit is low set SER low
		}
		SR_SCK_HIGH;        // set the clock high to send the bit to the SR
		i >>= 1;			// shift i so we look at the next data bit when we loop
		SR_SCK_LOW;         // set the clock low
	}
	SR_RCK_HIGH;            // set RCK high to latch SR data.
}
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

looks like everyones is basically the same :) ill add mine too!

#define SHIFTREG_DDR 	DDRB
#define SHIFTREG_PORT PORTB

#define SER 		PB2
#define RCLK 		PB0
#define SRCLK 		PB1
#define SETBIT(x,y) ((x) |= (1<<(y)))
#define CLEARBIT(x,y) ((x) &= ~(1<<(y)))

void latchValue(uint8_t value)
{
	unsigned int i;
	for(i=0; i<8; i++)
	{
		if((value & (1<<i)) != 0)
		{
			SETBIT(SHIFTREG_PORT,SER);
		}
		else 
		{
			CLEARBIT(SHIFTREG_PORT,SER);
		}
		SETBIT(SHIFTREG_PORT,SRCLK);
		CLEARBIT(SHIFTREG_PORT,SER);
		CLEARBIT(SHIFTREG_PORT,SRCLK);
	}
	SETBIT(SHIFTREG_PORT,RCLK);
	CLEARBIT(SHIFTREG_PORT,RCLK);
} 

anyone know what the QH` is for? it seems like just another QH

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ph0rkeh wrote:

anyone know what the QH` is for? it seems like just another QH

If you mean Pin 9 (it has several different names in several different datasheets, but the functionality is same) it is mean to chain multiple 74HC595:s together, so QH goes to next chips data input. This way you can chain as many chips you like and get tons of outputs with all the same AVR pins.

Note that the 74HC595:s are SPI compatible, so if you would use SPI pins for transmitting the data, you would not have to bit-bang the bits yourself in the code, you could let the SPI hardware to do that for you.

- Jani

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ph0rkeh wrote:

	for(i=0; i<8; i++)
	{
		if((value & (1<<i)) != 0)

A loop inside a loop, not even efficiently.
There ist no instruction on the AVRs to shift more than one step at a time. So shifting n times causes a loop.

Have you looked on an oscilloscope, how crazy it was (every bit need longer time as the predecessor) ?

Also watch that you shift out the LSB first, so you must reverse the pin assignment on the 74HC595 outputs.

Peter

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

like Jani said:

I´d also prefer SPI.
Less software & faster...

Klaus
********************************
Look at: www.megausb.de (German)
********************************

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

danni wrote:
Have you looked on an oscilloscope, how crazy it was (every bit need longer time as the predecessor) ?

I guess a simplistic solution is to use a pre-calculated table of bit masks:

 const uint8_t masks[] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 };
 for(i=0; i<8; i++) 
   { 
      if((value & masks[i]) != 0)

At least that makes for consistent lookup time.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

No, that is huge bloat.

Instead of shifting by constant masks or by the for loop value,
just shift the data once per for loop and set the mask to be same.

like this for example:

void send(uint8_t value)
{
  for (i=0; i<8; i++)
  {
    if (value&0x80) // MSB out first
    {
       // set the data pin to 1
    }
    else
    {
       // set the data pin to 0
    }
    value = value << 1; // shift next bit once to the MSB position
    // clock pin high
    // delay
    // clock pin low
    // delay
  }
  // load pin high
  // delay
  // load pin low
  // delay
}
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Honestly, Feedoh's solution is probably the most optimal. Mask off the MSb, then shift the mask right once per iteration. I had my own code a little while ago that was very similar.

Don't forget to work out if you want to shift out the bytes MSb first or last!

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
There ist no instruction on the AVRs to shift more than one step at a time. So shifting n times causes a loop.

Yes and no.

you can do a shift with using the MUL instruction.
X shift 3 bits left =
X MUL 8
The result is 16 bits...

Klaus
********************************
Look at: www.megausb.de (German)
********************************

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks all! I will make my own code, with your examples