Momentary pushbutton as toggling switches with shift registers

Hi,

I just want to check if anyone knows if there is a farly simple, maybe even standard way of making momentary pusbutton to act as toggling switches when using shift registers as input and outputs for the Arduino?

I have many inputs and outputs and with below code I can control one input (physical toggle switch) to one output in a large scale via the Arduino and I can also "pre-select" with the physical toggle switches which outputs shall be turned on simultaniously by holding the on switching with a separate toggle switch that switches the Output Enable pin between gnd and 5V for all the daisy chained output shift registers.

If I could use pushbuttons instead of physical toggle switches to toggle the I/O's I could software control for example shutdowns in a better way, but if there is no fairly straight forward way for that I might skip it.

Heres the loop I use for now to make 74HC165 inputs control 74HC595 outputs. Code is when only one input shift register and one output shift register to not complicate the example.

void loop() {

  // ********INPUT PART********

  // Reading the PISO Shift Register
  
  // Pulse the Input Latch Pin to load the parallell
  // data in the PISO Shift Register to the serial outputs

  digitalWrite(inLatchPin, LOW);
  delayMicroseconds(5);
  digitalWrite(inLatchPin, HIGH);
  delayMicroseconds(5);  

  // Shift the bits out from the PISO Shift Register

  digitalWrite(inClockPin, HIGH);
  digitalWrite(inEnablePin, LOW);

  shiftByte = shiftIn(inDataPin, inClockPin, MSBFIRST);

  digitalWrite(inEnablePin, HIGH);

  // ********OUTPUT PART******** 

  // Pull Output Latch Pin LOW before sending data
  
  digitalWrite(outLatchPin, LOW);

  // Shift the bits out to the SIPO Shift Register
  
  shiftOut(outDataPin, outClockPin, MSBFIRST, shiftByte);

  // Turn on the Output Latch Pin to move the values to 
  // the output stage of the Shift Register
  
  digitalWrite(outLatchPin, HIGH);

  // Give the process a 0,1 sec break

  delay(100);

}

I just want to check if anyone knows if there is a farly simple, maybe even standard way of making momentary pusbutton to act as toggling switches

Have a look at the StateChangeDetection example in the IDE

UKHeliBob:
Have a look at the StateChangeDetection example in the IDE

Thank you, and I did actually. I just cant figure out if there is an easy way of coding this when there is a state change within one of the bits in the shiftByte, ie for example 00101100 goes 10100101 or likewise.

From the example when reading input states:

if (buttonState != lastButtonState) {
// if the state has changed, increment the counter
if (buttonState == HIGH) {
// if the current state is HIGH then the button went from off to on:

Save the value read form the shift register then each time you read it use a for loop to read each bit in the received byte and compare it with the equivalent bit of the byte previously received.

Something like (untested)

for (int theBit = 0; theBit < 8; theBit++)
{
  if (bitRead(previousByte, theBit) != bitRead(currentByte, theBit))
  {
    //bit has changed
  }
}

It worked like a charm, thank you!

Here is the code for two daisy chained 74HC165 and two daisy chained 74HC595. I did some stress testing, pushing several buttos at once, holding down buttons etc, and it just chewed.

Maybe I could do more tuning?

// Give each respective Input Pin a named variable
// Each pin is to connect to the serial inputs of
// the PISO Shift Register

const byte inLatchPin = 5;
const byte inClockPin = 6;
const byte inEnablePin = 8;
const byte inDataPin = 7;

// Give each respective Output Pin a named variable
// Each pin is to connect to the serial inputs of
// the SIPO Shift Register

const byte outLatchPin = 2;
const byte outClockPin = 13;
const byte outDataPin = 4;

// Initialize a variable for storing the byte that
// is going to be sent to the Shift Register

byte prevShiftByte1 = 00000000;
byte prevShiftByte2 = 00000000;

byte currShiftByte1 = 00000000;
byte currShiftByte2 = 00000000;

byte outShiftByte1 = 00000000;
byte outShiftByte2 = 00000000;

void setup() {
  
  // Initialize the Input Pins to work in Output  
  // Mode to the PISO Shift Register Latch Pin
  // and Clock Pin, and in Input Mode to the
  // inDataPin
  
  pinMode(inLatchPin, OUTPUT);
  pinMode(inClockPin, OUTPUT);
  pinMode(inEnablePin, OUTPUT);
  pinMode(inDataPin, INPUT);

  // Initialize the Output Pins to work in Output
  // Mode to the SIPO Shift Register input pins
  
  pinMode(outLatchPin, OUTPUT);
  pinMode(outClockPin, OUTPUT);
  pinMode(outDataPin, OUTPUT);

  // Required initial states of these two pins
  // according to the datasheet timing diagram

  digitalWrite(inLatchPin,HIGH);
  digitalWrite(inEnablePin,HIGH);
}

void loop() {

  // ********INPUT PART********

  // Reading the PISO Shift Register
  
  // Pulse the Input Latch Pin to load the parallell
  // data in the PISO Shift Register to the serial outputs

  digitalWrite(inLatchPin, LOW);
  delayMicroseconds(5);
  digitalWrite(inLatchPin, HIGH);
  delayMicroseconds(5);  

  // Shift the bits out from the PISO Shift Register

  digitalWrite(inClockPin, HIGH);
  digitalWrite(inEnablePin, LOW);

  currShiftByte1 = shiftIn(inDataPin, inClockPin, MSBFIRST);
  currShiftByte2 = shiftIn(inDataPin, inClockPin, MSBFIRST);

  //************FIRST BYTE**************

for (int theBit = 0; theBit < 8; theBit++)
{
  if ((bitRead(currShiftByte1, theBit) != bitRead(prevShiftByte1, theBit)) && (bitRead(currShiftByte1, theBit) == HIGH) && (bitRead(outShiftByte1, theBit) == LOW))
    {
    bitWrite(outShiftByte1, theBit, HIGH);
    }
  else if ((bitRead(currShiftByte1, theBit) != bitRead(prevShiftByte1, theBit)) && (bitRead(currShiftByte1, theBit) == HIGH) && (bitRead(outShiftByte1, theBit) == HIGH))
    {  
    bitWrite(outShiftByte1, theBit, LOW);
    }
}

  //************SECOND BYTE**************

for (int theBit = 0; theBit < 8; theBit++)
{
  if ((bitRead(currShiftByte2, theBit) != bitRead(prevShiftByte2, theBit)) && (bitRead(currShiftByte2, theBit) == HIGH) && (bitRead(outShiftByte2, theBit) == LOW))
    {
    bitWrite(outShiftByte2, theBit, HIGH);
    }
  else if ((bitRead(currShiftByte2, theBit) != bitRead(prevShiftByte2, theBit)) && (bitRead(currShiftByte2, theBit) == HIGH) && (bitRead(outShiftByte2, theBit) == HIGH))
    {  
    bitWrite(outShiftByte2, theBit, LOW);
    }
}
  {  
  digitalWrite(inEnablePin, HIGH);  
  }

  // ********OUTPUT PART******** 

  // Pull Output Latch Pin LOW before sending data
  
  digitalWrite(outLatchPin, LOW);

  // Shift the bits out to the SIPO Shift Register
  
  shiftOut(outDataPin, outClockPin, MSBFIRST, outShiftByte2);
  shiftOut(outDataPin, outClockPin, MSBFIRST, outShiftByte1);

  // Turn on the Output Latch Pin to move the values to 
  // the output stage of the Shift Register
  
  digitalWrite(outLatchPin, HIGH);

  // Give the process a 0,1 sec break

  delay(100);

  prevShiftByte1 = currShiftByte1;
  prevShiftByte2 = currShiftByte2;
}

I'm pondering if I could do the same with the shifBytes, like a for-loop or a switch case?

stajo: I'm pondering if I could do the same with the shifBytes, like a for-loop or a switch case?

Since for loops and switch/case statements do completely different things, that's like wondering if you could go camping with a bicycle or a chainsaw.

The answer is that you can. The results are likely to be disappointing if you don't use what you have correctly.

stajo:
I’m pondering if I could do the same with the shifBytes, like a for-loop or a switch case?

I am not clear what you mean by this, but your code could be improved by checking whether the new data byte as a whole is the same as the previous byte. If so then there is no need to check each bit

Another possible change is this portion of the code

  for (int theBit = 0; theBit < 8; theBit++)
  {
    if ((bitRead(currShiftByte1, theBit) != bitRead(prevShiftByte1, theBit)) && (bitRead(currShiftByte1, theBit) == HIGH) && (bitRead(outShiftByte1, theBit) == LOW))
    {
      bitWrite(outShiftByte1, theBit, HIGH);
    }
    else if ((bitRead(currShiftByte1, theBit) != bitRead(prevShiftByte1, theBit)) && (bitRead(currShiftByte1, theBit) == HIGH) && (bitRead(outShiftByte1, theBit) == HIGH))
    {
      bitWrite(outShiftByte1, theBit, LOW);
    }
  }

There is no need to check 4 conditions (twice). If the current bit being tested is not the same as its previous value, whatever they were, then invert the bit by using

      bitWrite(outShiftByte1, theBit, !bitRead(outShiftByte1, theBit));

Another improvement would be to put the code for this test in a function and call it with the bytes to be tested as parameters or, given the clues given by the names outShiftByte1, outShiftByte2 put the data in arrays and test them inside a for loop.

Thank you UKHeliBob,

Hehe I was pondering all day besides work on my desk on how to write those If else lines:)

This was a real good shortcut, and I dont keep on nervously doublechecking everything over and over.

 //************FIRST BYTE**************

for (int theBit = 0; theBit < 8; theBit++)
{
  if ((bitRead(currShiftByte1, theBit) != bitRead(prevShiftByte1, theBit)) && (bitRead(currShiftByte1, theBit)))
    {
    bitWrite(outShiftByte1, theBit, !bitRead(outShiftByte1, theBit));  
    }
}

  //************SECOND BYTE**************

for (int theBit = 0; theBit < 8; theBit++)
{
  if ((bitRead(currShiftByte2, theBit) != bitRead(prevShiftByte2, theBit)) && (bitRead(currShiftByte2, theBit)))
    {
    bitWrite(outShiftByte2, theBit, !bitRead(outShiftByte2, theBit));
    }
}

So I think I understood your first point. About

Another improvement would be to put the code for this test in a function and call it with the bytes to be tested as parameters or, given the clues given by the names outShiftByte1, outShiftByte2 put the data in arrays and test them inside a for loop.

it makes me curious, but I have to think about what it could mean. Thanks alot, appreciate your lectures!