I have a little experiment going. The aim is to collect a number of bits (likely including real world I/O) into one byte and then process them en masse for debouncing, state change detection, etc. and get corresponding individual conditioned bits out the other end for program use.
I suspected my first effort – below – violated the proscription on type punning so I went out on the webs and got wrapped around the axle with all the c++ language spec. chapters and paragraphs and subparagraphs. Went down the arcana rabbit hole with trap representations, common initial sequences, and am still not much more knowledgeable.
So, does this constitute type punning? Does it invoke undefined behavior? The compiler gives no warnings (set to ‘all’). My interpretation says it’s not a trap representation but, what do I know?
Thanks for looking.
// debounce, one-shot, and toggle eight bits at once.
struct eightBits { // gives individual bit access
bool bit0: 1; bool bit1: 1;
bool bit2: 1; bool bit3: 1;
bool bit4: 1; bool bit5: 1;
bool bit6: 1; bool bit7: 1;
};
union { // allow mass manipulation and individual testing/setting
byte allBits;
eightBits individual;
} inputImage, inputsPreviousState, inputsXORed, inputsANDed, toggledBits, inputRisingOneShots;
const byte buttonOne = A3; // generic small tactile switch
byte counter;
void setup() {
Serial.begin(115200);
pinMode(buttonOne, INPUT_PULLUP);
pinMode(LED_BUILTIN, OUTPUT);
char shortTitle[30] = "multiple buttons struct/union";
Serial.println(shortTitle);
delay(3000);
}
void loop() {
inputImage.individual.bit2 = !digitalRead(buttonOne);
inputsXORed.allBits = inputsPreviousState.allBits ^ inputImage.allBits; // detect changed bits
inputsANDed.allBits = inputsXORed.allBits & inputImage.allBits; // isolate the changed bit(s)
toggledBits.allBits = toggledBits.allBits ^ inputsANDed.allBits; // if bit went true, toggle
inputRisingOneShots.allBits = inputsXORed.allBits & inputsANDed.allBits;
inputsPreviousState.allBits = inputImage.allBits; //update last state so changes can be detected
if (inputRisingOneShots.individual.bit2) {
counter++;
}
digitalWrite(LED_BUILTIN, toggledBits.individual.bit2);
Serial.print(inputImage.individual.bit2);
Serial.print(" ");
Serial.print(toggledBits.individual.bit2);
Serial.print(" ");
Serial.print(counter);
Serial.println();
}
I next tried using memcpy since that seems to be the approved way of avoiding punning. This version also works and I prefer the aesthetics of it but it uses ~70 bytes more flash and 2 bytes more RAM than the union version. Is there any way to have my cake and eat it too?
// gather eight bits and manipulate as a byte
// using 'address of' operator & makes it work
struct eightBits { // gives individual bit access via bit fields
bool bit0: 1; bool bit1: 1;
bool bit2: 1; bool bit3: 1;
bool bit4: 1; bool bit5: 1;
bool bit6: 1; bool bit7: 1;
} inputImage, outputImage, oneShotsOut, inputsANDedOut, toggleBitsOut, temp;
byte internal, inputsXORed, inputsPreviousState, toggledBits, inputsANDed, byteTemp, inputRisingOneShots;
const byte buttonOne = A3; // generic small tactile switch
int counter;
void setup() {
Serial.begin(115200);
pinMode(buttonOne, INPUT_PULLUP);
pinMode(LED_BUILTIN, OUTPUT);
char shortTitle[30]="multiple buttons memcpy";
Serial.println(shortTitle);
delay(3000);
}
void loop() {
inputImage.bit2 = !digitalRead(buttonOne);
memcpy(&internal, &inputImage, 1); // copy struct inputs to bytes for manipulation
inputsXORed = inputsPreviousState ^ internal; // detect changed bits
inputsANDed = inputsXORed & internal; // isolate the changed bit(s)
toggledBits = toggledBits ^ inputsANDed; // if bit went true, toggle
inputRisingOneShots = inputsXORed & inputsANDed;
memcpy(&oneShotsOut, &inputRisingOneShots, 1); // copy one-shots back to legal bit representation
// byteTemp = inputsANDed ^ toggledBits;
// memcpy(&inputsANDedOut, &byteTemp, 1);
memcpy(&toggleBitsOut, &toggledBits, 1); // copy toggles back to legal bit representation
inputsPreviousState = internal; // update last state so changes can be detected
if (oneShotsOut.bit2) {
counter++;
digitalWrite(LED_BUILTIN, toggleBitsOut.bit2);
}
Serial.print(inputImage.bit2);
Serial.print("\t");
Serial.print(toggleBitsOut.bit2);
Serial.print("\t");
Serial.println(counter);
}