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.

Project Help and Ideas » Binary Counter Using Shift Register

January 24, 2012
by SpaceGhost
SpaceGhost's Avatar

Hello all, I recently got a few 74HC595 shift registers that I have been playing with. I am trying to put together an 8 bit, 0 - 255 binary counter, with the eight shift register outputs representing the binary coded output. (Hope I'm explaining that right!)

I am working off of a modified version of the demo code Rick posted (the really cool blinky LED thingy) in the Photography club rating kit thread. What I have going almost works...

When I tie PB1 low, the counter definitely starts to count - LEDs 1, 2, 4, 8, fire off pretty quickly, then the time increases of course as LEDs 16, 32, 64, and 128 begin to fire off.

The most significant digit LEDs light brightly as the count progresses; however, the lesser significant LED's only dimly flicker. I would like for the lesser significant LED's to become fully lit also, as they are being used. The flickers do seem to be flickering in their proper order.

This leads me to believe that I must be close. But I've been playing with the code for a couple evenings now and whatever it is, it is still eluding me. I think it may be how I'm trying to use

cnt=cnt<<1;

Here is the code that I am working with -

// shiftregtest02

// 8 bit binary counter (0 - 255)

// pin definitions -
//
// (SS)   PB2 - to pin 12 of '595 LATCH (RCK)
// (MOSI) PB3 - to pin 14 of '595 DATA
// (SCK)  PB5 - to pin 11 of '595 CLOCK

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

#define SPI_PORT PORTB
#define SPI_DDR  DDRB
#define SPI_CS   PB2

unsigned char SPI_WriteRead(unsigned char dataout) {

  unsigned char datain;

  // Start transmission (MOSI)
  SPDR = dataout;

  // 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(100);
  // Disable Latch
  SPI_PORT &= ~(1<<SPI_CS);
   //SPI_PORT |= (1<<SPI_CS); // =new/modified

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

int main(void) {

  DDRB &= ~(1<<PB1); // input

  // turn on the internal resistor for input pin
  PORTB |= (1<<PB1);

  unsigned char cnt;
  uint8_t i;

  // Set the PORTD as Output
  //DDRD=0xFF;
  //PORTD=0x00;

  // 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);
  //SPI_PORT |= (1<<SPI_CS); // =new/modified

  // Enable SPI, Master, set clock rate fck/2 (maximum)
  SPCR = (1<<SPE)|(1<<MSTR);
  SPSR = (1<<SPI2X);

  //Reset the 74HC595 register
  cnt=SPI_WriteRead(0); // -

    for(i=0;i<255;i++) {

    SPI_WriteRead(0b11111110);

    SPI_WriteRead(0b11111101);

    SPI_WriteRead(0b11111011);

    SPI_WriteRead(0b11110111);

    SPI_WriteRead(0b11101111);

    SPI_WriteRead(0b11011111);

    SPI_WriteRead(0b10111111);

    SPI_WriteRead(0b01111111);

    }

    PORTD=SPI_WriteRead(0b11111111);

    i = 0;

  while(1) {

    if (!(PINB & (1<<PB1))){

    i = i + 1;

    cnt=cnt<<1;
    //delay_ms(300);

    if (i > 255)  //set 255 as highest allowed count

    i = 0;

    //cnt=cnt>>1;
    //SPI_WriteRead(cnt);

    delay_ms(300); // a bit of a delay so we can see what is happening

    //for(i=0;i<255;i++) {

    SPI_WriteRead(0b11111111);

    //delay_us(100);

    if( i & 1 ) 
    SPI_WriteRead(0b11111110);

    //delay_us(100);

    if( i & (1<<1)) 
    SPI_WriteRead(0b11111101);

    //delay_us(100);

    if( i & (1<<2)) 
    SPI_WriteRead(0b11111011);

    //delay_us(100);

    if( i & (1<<3)) 
    SPI_WriteRead(0b11110111);

    //delay_us(100);

    if( i & (1<<4)) 
    SPI_WriteRead(0b11101111);

    if( i & (1<<5)) 
    SPI_WriteRead(0b11011111);

    if( i & (1<<6)) 
    SPI_WriteRead(0b10111111);

    if( i & (1<<7)) 
    SPI_WriteRead(0b01111111);

    //}

    }

   }

  return 0;
}

As always, I appreciate any suggestions, hints or comments that anyone can offer.

Dave

January 24, 2012
by SpaceGhost
SpaceGhost's Avatar

I just tried something, and got an interesting result - I commented out lines 103 through 115, and put in -

i = 54;

then loaded the code and ran the program again.

Now when I tie PB1 low, all of the proper binary output LEDs light for the number 54. When I remove PB1 from gnd., the most significant LEDs for 54 remains lit, but the other three turn off.. Hmmm...

January 25, 2012
by 6ofhalfdozen
6ofhalfdozen's Avatar

SpaceGhost,

I am not the most familiar with shift registers, but when I read your two posts with their changes a couple things stand out.

a. is it possible that in the original version the "less significant" LEDs are cycling through/counting up so fast that you can not properly see them and they appear dim(ie they act like PWM due to cycling speeds and appear dim due to low current)?? If that is the case, try slowing it down some more, those commented out delays might let you see them better, albeit slower counts. either that or when up in the higher counts (say >64) you only change the LEDs/shift registers for every 6 counts or so which would leave the lower value LEDs on longer between changes.

b. I am thinking that in the second post when you set i to 54, it is all lit because line 101 is true, but when you disconnect PB1, it becomes false and somehow the less significant digits drop out, dunno if thats a code or shift register issue, but I think thats where it is.

hopefully that helps a little

January 25, 2012
by SpaceGhost
SpaceGhost's Avatar

I had considered the same thing - that perhaps the count was cycling too fast. I put the delays back and increased the duration. The count is considerably slower now, of course. I can definitely see that it the LEDs are lighting/counting in their proper order.

The problem seems to be with the "on" time of the lesser significant LEDs. Either the LED "on" time is still too short, or for some reason I am not getting full current to the LEDs. I still am not grasping how to set the durations of the output pulses themselves, just the duration between the pulses (off times). The duration is long, but the "count" is just a quick, dim "blip".

    if (!(PINB & (1<<PB1))){

    //i = 54;
///*

    i = i + 1;

    if (i > 0)

    cnt=cnt<<1;
    delay_ms(1000);

    if (i > 255)    //set 255 as highest allowed count

    i = 0;

    if (i == 0)

    SPI_WriteRead(0b11111111);

//*/

    //delay_us(2000);

    if( i & 1 ) 
    SPI_WriteRead(0b11111110);

    delay_us(2000);

    if( i & (1<<1)) 
    SPI_WriteRead(0b11111101);

    delay_us(2000);

    if( i & (1<<2)) 
    SPI_WriteRead(0b11111011);

    delay_us(2000);

    if( i & (1<<3)) 
    SPI_WriteRead(0b11110111);

    delay_us(2000);

    if( i & (1<<4)) 
    SPI_WriteRead(0b11101111);

    delay_us(2000);

    if( i & (1<<5)) 
    SPI_WriteRead(0b11011111);

    delay_us(1000);

    if( i & (1<<6)) 
    SPI_WriteRead(0b10111111);

    delay_us(1000);

    if( i & (1<<7)) 
    SPI_WriteRead(0b01111111);

    }

In all probability I will probably be able to use what I have (tweeked up a bit) for my intended purpose anyway - what I'm wanting to do is have the code display the binary equivalent of an integer (0 - 255) that I can set with some keypad code that I have written. But since I started out trying to build a counter, I am of course still trying to figure this out.

January 25, 2012
by Rick_S
Rick_S's Avatar

Ok, I'm assuming by the toggling of bits that you are attempting to invert the byte going to the shift register.... Why not just reverse your LED's and then just write the byte to the shift register?

Or, if you need to invert the byte because your LED's are an array with a common anode/cathode, why not just invert the whole byte then send it once. To invert the whole byte just xor it.

i ^= 0xFF;

What you are probably seeing is the issue of writing the byte multiple times.

Rick

January 27, 2012
by SpaceGhost
SpaceGhost's Avatar

Hi Rick, sorry it took me a couple days to answer back. I do not quite understand your answer though..

My LEDs are on a common connection - the eight anodes are connected together to the positive rail.

I think I understand what you are saying about sending the "whole byte", all at once. That would be what happened when I changed the code to display the fixed value integer, right?

How would I go about XOR-ing the whole byte to send it all at once? I am not sure how to use the code snippet that you suggested.

By the way, I have managed to combine the shift register code that I had posted above with my one-wire ADC keypad code - and it's working pretty well! I can enter a number from 000 - 255 with the keypad and store it, then display that number's binary output by pressing another button. Pretty nerdy I know, but I actually have a practical application in mind for the project.

January 27, 2012
by SpaceGhost
SpaceGhost's Avatar

Oh yeah, I meant to say something about "What you are probably seeing is the issue of writing the byte multiple times." That seems plausible... What I'm seeing the lesser significant bits doing does appear as though they are rapidly turning on then back off - like the byte is being written "multiple times." The most significant bit is the only one that does not appear to be changing because it's in use more than the others?

January 28, 2012
by Rick_S
Rick_S's Avatar

I meant replace all this:

if (i == 0)

SPI_WriteRead(0b11111111);

//*/

//delay_us(2000);

if( i & 1 ) 
SPI_WriteRead(0b11111110);

delay_us(2000);

if( i & (1<<1)) 
SPI_WriteRead(0b11111101);

delay_us(2000);

if( i & (1<<2)) 
SPI_WriteRead(0b11111011);

delay_us(2000);

if( i & (1<<3)) 
SPI_WriteRead(0b11110111);

delay_us(2000);

if( i & (1<<4)) 
SPI_WriteRead(0b11101111);

delay_us(2000);

if( i & (1<<5)) 
SPI_WriteRead(0b11011111);

delay_us(1000);

if( i & (1<<6)) 
SPI_WriteRead(0b10111111);

delay_us(1000);

if( i & (1<<7)) 
SPI_WriteRead(0b01111111);

with this:

SPI_WriteRead(i ^ 0xFF);
delay_ms(2000);

The Caret symbol (^) represents XOR (Exclusive OR). An XOR will only show true IF ONLY one of the bits being compared is true NOT both. So if you XOR 0xFF (0b11111111) with any byte, it will invert it. For instance 0x03 ^ 0xFF = 0xFC

0b0000 0011      (0x03 -- 3)
             XOR
0b1111 1111      (0xFF -- 255)
              =
0b1111 1100      (0xFC -- 252)

Rick

January 28, 2012
by SpaceGhost
SpaceGhost's Avatar

Wow, that works great Rick. Thanks again for your help! I appreciate your explanation on using the boolean operator.

"So if you XOR 0xFF (0b11111111) with any byte, it will invert it" .. Okay, I think I'm starting to get it now. Hmmm, and it's got me thinking even more. I don't know why I waited so long to delve into shift registers, these things are great!

I'll be applying this to my keypad project too, sure simplifies the code.

January 28, 2012
by Rick_S
Rick_S's Avatar

No problem. Cheers

Did your ghosting issue go away with that as well?

Rick

January 28, 2012
by SpaceGhost
SpaceGhost's Avatar

Yep. I can slow down the count or speed it up and still get a good solid read out. I left the delay (2000 ms)out. Works perfect, in my opinion. Neat little program.

Post a Reply

Please log in to post a reply.

Did you know that hobby servos are controlled through a particular kind of PWM (Pulse Width Modulation) signal? Learn more...