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 » A binary-to-decimal (0-15) decoder using shift registers

April 01, 2012
by SpaceGhost
SpaceGhost's Avatar

Hello all, I've been working on a 4 bit binary-to-decimal "decoder." The idea is to turn on or off one or more of the 16 outputs of two shift registers in relationship to the binary code inputted. I use a "5th bit" (VT) in order to input "0".

The code I have wrote so far works. The only problem is that as I turn a shift register output from off to on, or from on to off, if there are other outputs that have already been turned on they "blink" to off briefly.

What I am trying to do now is to eliminate this "blink" so that an output that is already on will remain in a sustained on-state while other outputs are being turned on or off.

I think it could have something to do with the way I'm using my delays. I've tried a number of different things but have not had much luck with the results. I've been stuck on this for several days, lol.

Right now I am only working with four outputs until I can get this part figured out. I'm hoping that perhaps someone will see something glaringly obvious, or offer some suggestions how I might better code this project.

// 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 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>

#define DISP_ZERO   0b11111111
#define DISP_ONE    0b01111111
#define DISP_TWO    0b10111111
#define DISP_THREE  0b11011111
#define DISP_FOUR   0b11101111
#define DISP_FIVE   0b11110111
#define DISP_SIX    0b11111011
#define DISP_SEVEN  0b11111101
#define DISP_EIGHT  0b11111110

void SPI_Write(uint8_t dataout) {

  SPDR = dataout;

  while(!(SPSR & (1<<SPIF)));

}

void SPI_Latch(void) {

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

  delay_us(200); // Hold pulse -

  PORTB &= ~(1<<PB2); // Disable Latch

}

int main(void) {

  DDRC &= ~(1<<PC1); // VT input

  DDRC &= ~(1<<PC2); // "1's input (From rcvr)
  DDRC &= ~(1<<PC3); // 2's 
  DDRC &= ~(1<<PC4); // 4's
  DDRC &= ~(1<<PC5); // 8's

  // turn on the internal resistor for input pins

  PORTC |= (1<<PC1);

  PORTC |= (1<<PC2);
  PORTC |= (1<<PC3);
  PORTC |= (1<<PC4);
  PORTC |= (1<<PC5);

    uint8_t a0;
    uint8_t a1;
    uint8_t a2;
    uint8_t a3;
    uint8_t a4;
    uint8_t a5;
    uint8_t a6;
    uint8_t a7;
    uint8_t a8;
    uint8_t a9;
    uint8_t a10;
    uint8_t a11;
    uint8_t a12;
    uint8_t a13;
    uint8_t a14;
    uint8_t a15;

  DDRB |=  ( (1<<PB5)|(1<<PB2)|(1<<PB3) );

  PORTB &= ~(1<<PB2);

  SPCR |= ( (1<<SPE) | (1<<MSTR) );

  SPCR &= ~ ( (1<<SPR1) | (1<<SPR0) );

  SPSR |= (1<<SPI2X);

    SPI_Write(DISP_ZERO); // 2nd,
    SPI_Write(DISP_ZERO); // 1st shift register

    a0 = 0;
    a1 = 0;
    a2 = 0;
    a3 = 0;
    a4 = 0;
    a5 = 0;
    a6 = 0;
    a7 = 0;
    a8 = 0;
    a9 = 0;
    a10 = 0;
    a11 = 0;
    a12 = 0;
    a13 = 0;
    a14 = 0;
    a15 = 0;

  while(1) {

    if (!(PINC & (1<<PC1))) ;

    if ((PINC & (1<<PC1))) { //sourcing input VT

    delay_ms(200);

    if (((PINC & (1<<PC2))) && ((PINC & (1<<PC3))) && ((PINC & (1<<PC4))) && ((PINC & (1<<PC5)))) {

    // 0

    a0 = a0 + 1;

    if (a0 > 1)

    a0 = 0;

    if (a0 == 0) {

    SPI_Write(a0 ^ 0xFF);

    }

    }

    if ((!(PINC & (1<<PC2))) && ((PINC & (1<<PC3))) && ((PINC & (1<<PC4))) && ((PINC & (1<<PC5)))) {

    // 1

    a1 = a1 + 1;

    if (a1 > 1)

    a1 = 0;

    if (a1 == 0) {

    SPI_Write(a1 ^ 0xFF);

    }

    }

    if (((PINC & (1<<PC2))) && (!(PINC & (1<<PC3))) && ((PINC & (1<<PC4))) && ((PINC & (1<<PC5)))) {

    // 2

    a2 = a2 + 1;

    if (a2 > 1)

    a2 = 0;

    if (a2 == 0) {

    SPI_Write(a2 ^ 0xFF);

    }

    }

    if ((!(PINC & (1<<PC2))) && (!(PINC & (1<<PC3))) && ((PINC & (1<<PC4))) && ((PINC & (1<<PC5)))) {

    // 3

    a3 = a3 + 1;

    if (a3 > 1)

    a3 = 0;

    if (a3 == 0) {

    SPI_Write(a3 ^ 0xFF);

    }

    }

    }

    if (a0 == 1)

    SPI_Write(DISP_ONE); // 2nd,
    SPI_Write(DISP_ZERO); // 1st shift register

    SPI_Latch();

    if (a1 == 1)

    SPI_Write(DISP_TWO); // 2nd,
    SPI_Write(DISP_ZERO); // 1st shift register

    SPI_Latch();

    if (a2 == 1)

    SPI_Write(DISP_THREE); // 2nd,
    SPI_Write(DISP_ZERO); // 1st shift register

    SPI_Latch();

    if (a3 == 1)

    SPI_Write(DISP_FOUR); // 2nd,
    SPI_Write(DISP_ZERO); // 1st shift register

    SPI_Latch();

  }

  return 0;
}
April 02, 2012
by SpaceGhost
SpaceGhost's Avatar

Did some modifying of the code - it's a little closer. The (DISP_FOUR) LED stays lit when the other LEDs toggle. I was really hoping there'd be a simple solution to keeping all "on-state" LEDs lit (latched?) while the other LEDs toggle on or off. It's probably something that's been explained before, lol. Or maybe I'm over-complicating things, again.

// 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 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>

#define DISP_ZERO   0b11111111
#define DISP_ONE    0b01111111
#define DISP_TWO    0b10111111
#define DISP_THREE  0b11011111
#define DISP_FOUR   0b11101111
#define DISP_FIVE   0b11110111
#define DISP_SIX    0b11111011
#define DISP_SEVEN  0b11111101
#define DISP_EIGHT  0b11111110

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_ms(1);

   PORTB &= ~(1<<PB2); // Disable Latch
}

int main(void) {

  DDRC &= ~(1<<PC1); // VT input

  DDRC &= ~(1<<PC2); // "1's input (From rcvr)
  DDRC &= ~(1<<PC3); // 2's 
  DDRC &= ~(1<<PC4); // 4's
  DDRC &= ~(1<<PC5); // 8's

  // turn on the internal resistor for input pins

  PORTC |= (1<<PC1);

  PORTC |= (1<<PC2);
  PORTC |= (1<<PC3);
  PORTC |= (1<<PC4);
  PORTC |= (1<<PC5);

    uint8_t a;

    uint8_t a0;
    uint8_t a1;
    uint8_t a2;
    uint8_t a3;
    uint8_t a4;
    uint8_t a5;
    uint8_t a6;
    uint8_t a7;
    uint8_t a8;
    uint8_t a9;
    uint8_t a10;
    uint8_t a11;
    uint8_t a12;
    uint8_t a13;
    uint8_t a14;
    uint8_t a15;

  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

    SPI_Write(DISP_ZERO); // 2nd
    SPI_Write(DISP_ZERO); // 1st shift register

    a = 0;

    a0 = 0;
    a1 = 0;
    a2 = 0;
    a3 = 0;
    a4 = 0;
    a5 = 0;
    a6 = 0;
    a7 = 0;
    a8 = 0;
    a9 = 0;
    a10 = 0;
    a11 = 0;
    a12 = 0;
    a13 = 0;
    a14 = 0;
    a15 = 0;

  while(1){

    if (!(PINC & (1<<PC1))) {

    a = 0;

    }

    if ((PINC & (1<<PC1))) { //sourcing input

    a = 1;

    }

    if (a == 1) {

    delay_ms(200);

    // 0 thru 4

    if (((PINC & (1<<PC2))) && ((PINC & (1<<PC3))) && ((PINC & (1<<PC4))) && ((PINC & (1<<PC5)))) {

    // 0

    a0 = a0 + 1;

    if (a0 > 1)

    a0 = 0;

    if (a0 == 0) {

    SPI_Write(a0 ^ 0xFF);

    }

  }

    if ((!(PINC & (1<<PC2))) && ((PINC & (1<<PC3))) && ((PINC & (1<<PC4))) && ((PINC & (1<<PC5)))) {

    // 1

    a1 = a1 + 1;

    if (a1 > 1)

    a1 = 0;

    if (a1 == 0) {

    SPI_Write(a1 ^ 0xFF);

    }

  }

    if (((PINC & (1<<PC2))) && (!(PINC & (1<<PC3))) && ((PINC & (1<<PC4))) && ((PINC & (1<<PC5)))) {

    // 2

    a2 = a2 + 1;

    if (a2 > 1)

    a2 = 0;

    if (a2 == 0) {

    SPI_Write(a2 ^ 0xFF);

    }

  }

    if ((!(PINC & (1<<PC2))) && (!(PINC & (1<<PC3))) && ((PINC & (1<<PC4))) && ((PINC & (1<<PC5)))) {

    // 3

    a3 = a3 + 1;

    if (a3 > 1)

    a3 = 0;

    if (a3 == 0) {

    SPI_Write(a3 ^ 0xFF);

    }

  }

}

    if (a == 0) {

    SPI_Latch();

    }

    if (a0 == 1) {

    SPI_Write(DISP_ONE); // 2nd
    SPI_Write(DISP_ZERO); // 1st shift register

    SPI_Latch();

    }

    if (a1 == 1) {

    SPI_Write(DISP_TWO); // 2nd
    SPI_Write(DISP_ZERO); // 1st shift register

    SPI_Latch();

    }

    if (a2 == 1) {

    SPI_Write(DISP_THREE); // 2nd
    SPI_Write(DISP_ZERO); // 1st shift register

    SPI_Latch();

    }

    if (a3 == 1) {

    SPI_Write(DISP_FOUR); // 2nd
    SPI_Write(DISP_ZERO); // 1st shift register

    SPI_Latch();

    }

  }
  return 0;
}
April 03, 2012
by pcbolt
pcbolt's Avatar

SpaceGhost -

I'm not exactly sure what you need to have happen here. If you could answer a few questions I might be able to help.

  • Let's say you have only LED3 lit and you input 3, does LED3 toggle off?
  • If only LED3 is lit and you input "4", does LED3 stay on and LED4 turn on?
  • Does the input only effect one LED at a time?

It may be overcomplicated a little, but it should not be impossible to do.

April 03, 2012
by SpaceGhost
SpaceGhost's Avatar

Hi pcbolt. The answer to all three of your questions is yes. I want binary-coded inputs to "toggle" a corresponding LED, to on or off. I want the shift register outputs that had previously been turned on to remain in a steady on state.

I'm using two shift registers for the decoding display. Binary "0" is a number that I'm using as an input too, hence the "5th bit" (PC1) - the "VT" input (active high). As it's written, inputting a "0" (just PC1) turns on "SPI_Write(DISP_ONE);", etc. VT is inputted (sourced) when any other binary number is inputted (sink) as well.

I wrote the code so that inputting the binary number once turns the output of the shift register on, inputting it again turns that corresponding output off. That part works okay, except that I get a "blink" from the other outputs that are on, if I turn on or off another output.

I can explain a little more about my project this afternoon, right now I'm on my way to work.

Dave

April 03, 2012
by pcbolt
pcbolt's Avatar

Dave -

I think all you need is to keep your pin initializations and SPI functions then use something like:

uint8_t old_VT_state = 0, new_VT_state = 0, input_value;
uint16_t old_output = 0, new_output = 0;

while(1){
  if (PINC & (1<<PC1))
    new_VT_state = 1;
  else
    old_VT_state = 0;

  if ((new_VT_state == 1) && (old_VT_state == 0)){

  input_value = 0x0F & (PINC >> 2);
  new_output = old_output ^ (1 << input_value);

  SPI_Write(0xff & new_Output);    // low byte
  SPI_Write(new_Output >> 8);      // high byte

  SPI_Latch();
  old_output = new_output;

  old_VT_state = 1;
  }
}
April 03, 2012
by SpaceGhost
SpaceGhost's Avatar

Hmmm, I might need to hit the books... I'm not quite following your suggestions.

For one,

  if ((new_VT_state == 1) && (old_VT_state == 0)){

has me a little confused. How can "new_VT_state = 1" AND "old_VT_state = 0" both be true at the same time?

And, could you explain a little what this is doing -

  input_value = 0x0F & (PINC >> 2);
  new_output = old_output ^ (1 << input_value);

And, if by using this -

  SPI_Write(0xff & new_Output);    // low byte
  SPI_Write(new_Output >> 8);      // high byte

Does this mean that I wouldn't need or use these output definitions?

#define DISP_ONE    0b01111111
#define DISP_TWO    0b10111111
etc..

I guess what I'm trying to say is that I'm having some trouble trying to interpret this stuff well enough to incorporate it into my code.

Thank you very much for your input.

April 03, 2012
by pcbolt
pcbolt's Avatar

"new_VT_state" and "old_VT_state" are two separate variables. They can be different. When the program starts out they are both 0, so the

if ((new_VT_state == 1) && (old_VT_state == 0)){

code block does not execute. Once PC1 goes high, "new_VT_state" will be assigned 1 and the code block will execute. To make sure the code block only executes once, "old_VT_state" gets assigned 1 at the end of the code block. Once PC1 goes low, we reset "old_VT_state" to 0 so the next time PC1 goes high, the code block executes again.

As for this code,

input_value = 0x0F & (PINC >> 2);

If, for example, you wanted to turn on LED9. You would set PC5 to 1 (high) and PC2 to 1 (high). This means the register PINC has the value of 0bxx1001xx (where x can be 1 or 0). By shifting this value right 2 places (PINC >> 2), we get 0b00xx1001. By taking this value and "ANDING" it with 0x0F (0b00001111) we will "mask off" the first four digits to get 0b00001001 which is the number 9 in decimal. Next we take that "input_value" and use;

new_output = old_output ^ (1 << input_value);

Here we take 1 (or 0b0000000000000001) and shift it over 9 places to get 0b0000001000000000 (we are using 16-bit values here). Next we use the XOR operator with the "old_output" to calculate a "new_output". This will toggle only the 9th place bit and leave everything else the same. Now as for;

SPI_Write(0xff & new_Output);    // low byte
SPI_Write(new_Output >> 8);      // high byte

Since "new_output" (I messed up the capital on Output...that has to change) is a 16-bit number and SPI_Write takes 8-bits, we need to break it into two 8-bit numbers. "ANDING" new_output with 0xFF (0b0000000011111111) masks the output just like we did before and leaves only the lower 8-bits (low byte). "new_output >> 8" shifts the hight 8-bits over into where the lower bits were and writes them to SPI.

As far as your defines go...I don't think you will need them. You may need to "negate" the output (~ operator) so 1's become 0's and 0's become 1's, but since you are just toggling...it should not matter.

April 03, 2012
by SpaceGhost
SpaceGhost's Avatar

I might be getting somewhere... With this code for my while loop, the outputs turn on without the "blink". But, they don't turn off right -

while(1){

  if (PINC & (1<<PC1))

    new_VT_state = 1;

  else

    old_VT_state = 0;

  if ((new_VT_state == 1) && (old_VT_state == 0)){

    if (((PINC & (1<<PC2))) && ((PINC & (1<<PC3))) && ((PINC & (1<<PC4))) && ((PINC & (1<<PC5)))) {

    // 0

    a0 = a0 + 1;

    if (a0 > 1)

    a0 = 0;

    if (a0 == 0) {

    SPI_Write(a0 ^ 0xFF);

    }

    }

    if ((!(PINC & (1<<PC2))) && ((PINC & (1<<PC3))) && ((PINC & (1<<PC4))) && ((PINC & (1<<PC5)))) {

    // 1

    a1 = a1 + 1;

    if (a1 > 1)

    a1 = 0;

    if (a1 == 0) {

    SPI_Write(a1 ^ 0xFF);

    }

    }

    if (((PINC & (1<<PC2))) && (!(PINC & (1<<PC3))) && ((PINC & (1<<PC4))) && ((PINC & (1<<PC5)))) {

    // 2

    a2 = a2 + 1;

    if (a2 > 1)

    a2 = 0;

    if (a2 == 0) {

    SPI_Write(a2 ^ 0xFF);

    }

    }

    if ((!(PINC & (1<<PC2))) && (!(PINC & (1<<PC3))) && ((PINC & (1<<PC4))) && ((PINC & (1<<PC5)))) {

    // 3

    a3 = a3 + 1;

    if (a3 > 1)

    a3 = 0;

    if (a3 == 0) {

    SPI_Write(a3 ^ 0xFF);

    }

    }

    }

  input_value = 0x0F & (PINC >> 2);
  new_output = old_output ^ (1 << input_value);

    if (a0 == 1) {

    SPI_Write(DISP_ONE); // 2nd,
    SPI_Write(DISP_ZERO); // 1st shift register

  SPI_Latch();
  old_output = new_output;

  old_VT_state = 1;

  }

    if (a1 == 1) {

    SPI_Write(DISP_TWO); // 2nd,
    SPI_Write(DISP_ZERO); // 1st shift register

  SPI_Latch();
  old_output = new_output;

  old_VT_state = 1;

  }

    if (a2 == 1) {

    SPI_Write(DISP_THREE); // 2nd,
    SPI_Write(DISP_ZERO); // 1st shift register

   SPI_Latch();
  old_output = new_output;

  old_VT_state = 1;

  }

    if (a3 == 1) {

    SPI_Write(DISP_FOUR); // 2nd,
    SPI_Write(DISP_ZERO); // 1st shift register

   SPI_Latch();
  old_output = new_output;

  old_VT_state = 1;

  }

  }

  return 0;
}

I'd like to be able to use my output definitions as they are defined. They do activate the outputs as I expect, and easier for me to tell what they are at a glance.

I realize that I'm being kind of thick headed. I may need to just step back from this for a while, then come back and study this some more. I would appreciate any suggestions that you may have, to at this stage modify the code so that the outputs will turn off properly.

Thank you for your help, it is appreciated.

April 03, 2012
by pcbolt
pcbolt's Avatar

Use what you are comfortable with and more importantly, what you understand. A heck of a lot easier to debug that way :-)

The one part I didn't understand though was the following code block;

if (((PINC & (1<<PC2))) && (!(PINC & (1<<PC3))) && ((PINC & (1<<PC4))) && ((PINC & (1<<PC5)))) {
   // 2
   a2 = a2 + 1;
   if (a2 > 1)
     a2 = 0;
   if (a2 == 0) {
     SPI_Write(a2 ^ 0xFF);
   }
 }

I can see that you are toggling "a2" when 2 is input (i.e. if "a2" was 0 it becomes 1 and if it was 1 it becomes 0....BTW "a2 ^= 1;" will do this too). What puzzles me is the next part;

if (a2 == 0){
   SPI_Write(a2 ^ 0xFF)
}

This will always evaluate to "SPI_Write(0xFF)" when a2 is 0. Also, it doesn't "latch" afterward. Not sure what the outcome is supposed to be here.

April 06, 2012
by SpaceGhost
SpaceGhost's Avatar

Okay, now I'm starting to get it (I think). I went back over your explanations and they are beginning to make sense. I have the outputs turning on and off without the blink (although I'm having some slight "bounce" issues).

Although I am following you in your description about how the number "9" output should work, the code goes back to the number "1" output (of the shift register) when binary 9 is inputted.

Thanks for getting me this far! I have been driving myself nuts over this. Here's the code that I'm using now - Could you offer any suggestions as to why, as the code is written, the outputs of the other shift register are not outputting?

Any other explanations as to how to improve the code would also be greatly appreciated.

// 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 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_ms(1);

  PORTB &= ~(1<<PB2); // Disable Latch
}

int main(void) {

  DDRC &= ~(1<<PC1); // VT input

  DDRC &= ~(1<<PC2); // "1's input (From rcvr)
  DDRC &= ~(1<<PC3); // 2's 
  DDRC &= ~(1<<PC4); // 4's
  DDRC &= ~(1<<PC5); // 8's

  // turn on the internal resistor for input pins

  PORTC |= (1<<PC1);

  PORTC |= (1<<PC2);
  PORTC |= (1<<PC3);
  PORTC |= (1<<PC4);
  PORTC |= (1<<PC5);

uint8_t a = 0, old_VT_state = 0, new_VT_state = 0, input_value;
uint16_t old_output = 0, new_output = 0;

  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){

  if ((PINC & (1<<PC1))) {

    a = a + 1;

    if (a > 1)

    a = 0;

    delay_ms(100);

}
    if (a == 1) {

    new_VT_state = 0;

    }

    if (a == 0) {

    new_VT_state = 1;

    }

  else

    old_VT_state = 0;

  if ((new_VT_state == 1) && (old_VT_state == 0)) {

  input_value = 0x0F & (PINC >> 2);

  new_output = old_output ^ (1 << input_value);

  SPI_Write(new_output >> 8);      // high byte
  SPI_Write(new_output & 0xff);    // low byte

  SPI_Latch();

  old_output = new_output;

  old_VT_state = 1;

  }

  }
  //return 0;
}
April 06, 2012
by SpaceGhost
SpaceGhost's Avatar

Actually the "bounce" issue isn't really about "switch bounce" it is more so with the transition time(s) between PC1 being high or low. It seems to work a little better with the while loop modified like this -

while(1){

  if ((PINC & (1<<PC1))) {

    a = a + 1;

    if (a > 1)

    a = 0;

    delay_ms(200);

}
    if (a == 1) {

    new_VT_state = 0;

    }

    if (a == 0) {

    new_VT_state = 1;

    }

  else {

    if (a == 1) {

    old_VT_state = 0;

    }

    if (a == 0) {

    old_VT_state = 1;

    }

    }

    //old_VT_state = 0;

  if ((new_VT_state == 1) && (old_VT_state == 0)) {

  input_value = 0x0F & (PINC >> 2);

  new_output = old_output ^ (1 << input_value);

  SPI_Write(new_output >> 8);      // high byte
  SPI_Write(new_output & 0xff);    // low byte

  SPI_Latch();

  old_output = new_output;

  old_VT_state = 1;

  }

  }
  //return 0;
}

This of course hasn't fixed the other shift register of not "registering" 9 - 16.

Dave

April 06, 2012
by pcbolt
pcbolt's Avatar

Dave -

Are you able to "Latch" the second shift register? What I mean is, does PB2 connect to both shift registers? The shift registers I've used don't have the latch feature (which is a good feature)...they just shift bits across the pins in real time.

April 07, 2012
by SpaceGhost
SpaceGhost's Avatar

Yes, PB2 connects to pin 12 of both shift registers. I'm using two 74HC595N shift registers.

April 08, 2012
by SpaceGhost
SpaceGhost's Avatar

So, the second shift register should "latch" too, right?

April 08, 2012
by SpaceGhost
SpaceGhost's Avatar

I think that I might need some kind of conditional statements around the "SPI_Write" functions. I've tried a number of different things, but nothing seems to work quite right. Any suggestions?

April 08, 2012
by SpaceGhost
SpaceGhost's Avatar

Hey!! I am delighted to say that the code that I last posted (with pcbolt's help!), works. I had a loose wire or something going on, the entire circuit had quit working entirely. After jiggling some wires around and then trying out the last code posted again, it worked!

pcbolt, I thank you very much for your excellent help!!

One more thing that I'd like to ask - you mentioned that I could ""negate" the output (~ operator) so 1's become 0's and 0's become 1's..." Um, where would I need to place the tilde(s), in order to do that?

April 08, 2012
by pcbolt
pcbolt's Avatar

Dave -

Love it when a plan comes together.

As far as the negation goes I "think" it would work on line 48:

new_output = old_output ^ ~(1 << input_value);

Glad to help.

April 08, 2012
by SpaceGhost
SpaceGhost's Avatar

Hi pcbolt, yes the plan finally came together. Once again, I thank you for all your help. I apologize for the wiring situation - sure had me trying to "fix" something that wasn't broken for quite a while, lol.

I tried putting the tilde where you suggested that it might go - the outputs really acted weird! Don't think that's it...

By "negating" the outputs, did you mean "inverting" them? That's what I'm actually wanting to do.

The outputs are all on, when I power the circuit. They toggle from on to off, etc. just fine though. I've modified the code a little more, this seems to work the best (stability wise) so far -

// 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

#include <avr/io.h>
#include "../libnerdkits/delay.h"
#include <stdint.h> 
#include <stdio.h>
#include <stdlib.h>

#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>

#

*/

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_ms(1);

  PORTB &= ~(1<<PB2); // Disable Latch
}

int main(void) {

  DDRC &= ~(1<<PC1); // VT input

  DDRC &= ~(1<<PC2); // "1's input (From rcvr)
  DDRC &= ~(1<<PC3); // 2's
  DDRC &= ~(1<<PC4); // 4's
  DDRC &= ~(1<<PC5); // 8's

  // turn on the internal resistor for input pins

  PORTC |= (1<<PC1);

  PORTC |= (1<<PC2);
  PORTC |= (1<<PC3);
  PORTC |= (1<<PC4);
  PORTC |= (1<<PC5);

uint8_t a = 0, b = 0, input_value;
uint16_t old_output = 0, new_output = 0;

  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

  SPI_Write(new_output >> 8);      // high byte
  SPI_Write(new_output & 0xff);    // low byte

  SPI_Latch();

while(1){

  if ((PINC & (1<<PC1))) {

    b = 1;

    a = a + 1;

    if (a > 1)

    a = 0;

   delay_ms(200);

    }

  if (!(PINC & (1<<PC1))) {

  b = 0;

  }

  if (b == 0);

    //old_VT_state = 0;

   if (b == 1) {

    //delay_ms(200);

  input_value = 0x0F & (PINC >> 2);

  new_output = old_output ^ (1 << input_value);

  delay_ms(60);

  SPI_Write(new_output >> 8);      // high byte
  SPI_Write(new_output & 0xff);    // low byte

  SPI_Latch();

  old_output = new_output;

  //old_VT_state = 1;

  }

  }
  //return 0;
}

I realize that I could swap my LEDs around, and it'd work that way (LEDs off on power up). I have the LEDs wired common anodes to the positive rail. It'd be great to know how to invert the outputs, at least for future reference.

April 09, 2012
by SpaceGhost
SpaceGhost's Avatar

Hey, figured it out. To "invert" the outputs, I simply had to XOR the SPI_Write functions -

  SPI_Write(0xff ^ (new_output >> 8));      // high byte
  SPI_Write(0xff ^ (new_output & 0xff));    // low byte

This had been explained to me in an earlier thread that I had started also pertaining to shift registers... Thanks again there, Rick S.!

And thanks to you again too, pcbolt for all of your great help as well.

I needed this long weekend to absorb things - they've got me learning a lot of new stuff at work as well.. A bit overwhelming at times, for my aging brain!

April 09, 2012
by Rick_S
Rick_S's Avatar

Glad to be of help again ... even if indirectly this time 'round. Cheers

Rick

Post a Reply

Please log in to post a reply.

Did you know that the microcontroller's crystal oscillator can be used to keep accurate time? Learn more...