--- /dev/null
+#include "Adafruit_LEDBackpack.h"
+
+#define _countof(x) (sizeof(x) / sizeof (x[0]))
+
+// Using Adafruit's four-digit 7-segment display backpack
+Adafruit_7segment segs = Adafruit_7segment();
+
+
+enum test_op {END, FORCE, ASSERT, DEBUG};
+
+
+struct test_command
+{
+ enum test_op command;
+ const char *right;
+ const char *left;
+};
+
+
+struct chip_entry
+{
+ int part_number;
+ struct test_command *sequence;
+};
+
+
+// -----------------------------------------------------------------------------
+// Test sequences
+
+
+const struct test_command test_74_04[] = {
+ // Test case: ~0 == 1
+ {FORCE, "10x0x0x", "0x0x0x0"},
+ {ASSERT, "xx1x1x1", "x1x1x1x"},
+
+ // Test case: ~1 == 0
+ {FORCE, "11x1x1x", "1x1x1x0"},
+ {ASSERT, "xx0x0x0", "x0x0x0x"},
+
+ {END, "", ""},
+};
+
+
+const struct test_command test_74_37[] = {
+ // Test case: 0 nand 0 == 1
+ {FORCE, "100x00x", "00x00x0"},
+ {ASSERT, "xxx1xx1", "xx1xx1x"},
+
+ // Test case: 0 nand 1 == 1
+ {FORCE, "101x01x", "01x01x0"},
+ {ASSERT, "xxx1xx1", "xx1xx1x"},
+
+ // Test case: 1 nand 0 == 1
+ {FORCE, "110x10x", "10x10x0"},
+ {ASSERT, "xxx1xx1", "xx1xx1x"},
+
+ // Test case: 1 nand 1 == 0
+ {FORCE, "111x11x", "11x11x0"},
+ {ASSERT, "xxx0xx0", "xx0xx0x"},
+
+ {END, "", ""},
+};
+
+
+const struct test_command test_74_109[] = {
+ // Async preset
+ {FORCE, "110100xx", "10100xx0"}, // Assert preset
+ {ASSERT, "xxxxxx10", "xxxxx10x"},
+ {FORCE, "110101xx", "10101xx0"}, // Deassert preset
+
+ // Async clear
+ {FORCE, "100101xx", "00101xx0"}, // Assert clear
+ {ASSERT, "xxxxxx01", "xxxxx01x"},
+ {FORCE, "110101xx", "10101xx0"}, // Deassert clear
+
+ // J sets on clock edge
+ {FORCE, "111101xx", "11101xx0"}, // Assert J
+ {FORCE, "111111xx", "11111xx0"}, // Raise clock
+ {ASSERT, "xxxxxx10", "xxxxx10x"},
+ {FORCE, "111101xx", "11101xx0"}, // Lower clock
+
+ // No input has no change on clock edge
+ {FORCE, "110101xx", "10101xx0"}, // Deassert J and K
+ {FORCE, "110111xx", "10111xx0"}, // Raise clock
+ {ASSERT, "xxxxxx10", "xxxxx10x"},
+ {FORCE, "110101xx", "10101xx0"}, // Lower clock
+
+ // K resets on clock edge
+ {FORCE, "110001xx", "10001xx0"}, // Assert K
+ {FORCE, "110011xx", "10011xx0"}, // Raise clock
+ {ASSERT, "xxxxxx01", "xxxxx01x"},
+ {FORCE, "110001xx", "10001xx0"}, // Lower clock
+
+ // No input has no change on clock edge
+ {FORCE, "110101xx", "10101xx0"}, // Deassert J and K
+ {FORCE, "110111xx", "10111xx0"}, // Raise clock
+ {ASSERT, "xxxxxx01", "xxxxx01x"},
+ {FORCE, "110101xx", "10101xx0"}, // Lower clock
+
+ // JK toggles on clock edge
+ {FORCE, "111001xx", "11001xx0"}, // Assert J and K
+ {FORCE, "111011xx", "11011xx0"}, // Raise clock
+ {ASSERT, "xxxxxx10", "xxxxx10x"},
+ {FORCE, "111001xx", "11001xx0"}, // Lower clock
+
+ // JK toggles on clock edge
+ {FORCE, "111001xx", "11001xx0"}, // Assert J and K
+ {FORCE, "111011xx", "11011xx0"}, // Raise clock
+ {ASSERT, "xxxxxx01", "xxxxx01x"},
+ {FORCE, "111001xx", "11001xx0"}, // Lower clock
+
+ {END, "", ""},
+};
+
+
+const struct test_command test_74_534[] = {
+ {FORCE, "1x00xx00x0", "0x00xx00x0"}, // Input zeroes
+ {FORCE, "1x00xx00x1", "0x00xx00x0"}, // Clock rise
+ {FORCE, "1x00xx00x0", "0x00xx00x0"}, // Clock fall
+ {ASSERT, "x1xx11xx1x", "x1xx11xx1x"}, // Check for ones (inverted inputs) at outputs
+ {FORCE, "1x11xx11x0", "0x11xx11x0"}, // Input ones
+ {ASSERT, "x1xx11xx1x", "x1xx11xx1x"}, // Check for ones (previous output) at outputs
+ {FORCE, "1x11xx11x1", "0x11xx11x0"}, // Clock rise
+ {FORCE, "1x11xx11x0", "0x11xx11x0"}, // Clock fall
+ {ASSERT, "x0xx00xx0x", "x0xx00xx0x"}, // Check for zeroes (inverted inputs) at outputs
+ {FORCE, "1x10xx10x0", "0x01xx01x0"}, // Input mix of ones and zeroes
+ {FORCE, "1x10xx10x1", "0x01xx01x0"}, // Clock rise
+ {FORCE, "1x10xx10x0", "0x01xx01x0"}, // Clock fall
+ {ASSERT, "x0xx10xx1x", "x1xx01xx0x"}, // Check for inverted inputs at outputs
+ {FORCE, "1x10xx10x0", "1x01xx01x0"}, // Disable output
+ {ASSERT, "x1xx11xx1x", "x1xx11xx1x"}, // Outputs should float, pulled up
+ {FORCE, "1x10xx10x0", "0x01xx01x0"}, // Enable output
+ {ASSERT, "x0xx10xx1x", "x1xx01xx0x"}, // Values should still be present
+
+ {END, "", ""},
+};
+
+
+// -----------------------------------------------------------------------------
+// Mapping part numbers to test sequences
+
+
+const struct chip_entry tests[] = {
+ {4, test_74_04},
+ {37, test_74_37},
+ {109, test_74_109},
+ {534, test_74_534},
+};
+
+
+// -----------------------------------------------------------------------------
+
+
+void force_test_vector(const char *left, const char *right)
+{
+ // Convert the string masks to bit masks
+ uint32_t set_high = 0, outputs = 0;
+
+ for (int i = 0; right[i] != '\0'; i++)
+ {
+ if (right[i] == '1')
+ {
+ set_high |= 1 << (19-i);
+ outputs |= 1 << (19-i);
+ }
+ else if (right[i] == '0')
+ {
+ outputs |= 1 << (19-i);
+ }
+ else
+ {
+ // Pin is an input, set bit in set_high to enable pull-up
+ set_high |= 1 << (19-i);
+ }
+ }
+
+ for (int i = 0; left[i] != '\0'; i++)
+ {
+ if (left[i] == '1')
+ {
+ set_high |= 1ul << i;
+ outputs |= 1ul << i;
+ }
+ else if (left[i] == '0')
+ {
+ outputs |= 1ul << i;
+ }
+ else
+ {
+ // Pin is an input, set bit in set_high to enable pull-up
+ set_high |= 1ul << i;
+ }
+ }
+
+ force_bit_vector(set_high, outputs);
+}
+
+
+void force_bit_vector(uint32_t set_high, uint32_t outputs)
+{
+ uint8_t port_b = 0, port_c = 0, port_d = 0;
+ uint8_t ddr_b = 0, ddr_c = 0, ddr_d = 0;
+
+ // Map bits from test vector to GPIO register bits
+ if (set_high & 0x80000) port_d |= 0x80;
+ if (set_high & 0x40000) port_d |= 0x40;
+ if (set_high & 0x20000) port_d |= 0x20;
+ if (set_high & 0x10000) port_d |= 0x10;
+ if (set_high & 0x08000) port_c |= 0x08;
+ if (set_high & 0x04000) port_c |= 0x04;
+ if (set_high & 0x02000) port_b |= 0x80;
+ if (set_high & 0x01000) port_b |= 0x40;
+ if (set_high & 0x00800) port_b |= 0x20;
+ if (set_high & 0x00400) port_b |= 0x10;
+ if (set_high & 0x00200) port_d |= 0x01;
+ if (set_high & 0x00100) port_d |= 0x02;
+ if (set_high & 0x00080) port_d |= 0x04;
+ if (set_high & 0x00040) port_d |= 0x08;
+ if (set_high & 0x00020) port_c |= 0x01;
+ if (set_high & 0x00010) port_c |= 0x02;
+ if (set_high & 0x00008) port_b |= 0x01;
+ if (set_high & 0x00004) port_b |= 0x02;
+ if (set_high & 0x00002) port_b |= 0x04;
+ if (set_high & 0x00001) port_b |= 0x08;
+
+ if (outputs & 0x80000) ddr_d |= 0x80;
+ if (outputs & 0x40000) ddr_d |= 0x40;
+ if (outputs & 0x20000) ddr_d |= 0x20;
+ if (outputs & 0x10000) ddr_d |= 0x10;
+ if (outputs & 0x08000) ddr_c |= 0x08;
+ if (outputs & 0x04000) ddr_c |= 0x04;
+ if (outputs & 0x02000) ddr_b |= 0x80;
+ if (outputs & 0x01000) ddr_b |= 0x40;
+ if (outputs & 0x00800) ddr_b |= 0x20;
+ if (outputs & 0x00400) ddr_b |= 0x10;
+ if (outputs & 0x00200) ddr_d |= 0x01;
+ if (outputs & 0x00100) ddr_d |= 0x02;
+ if (outputs & 0x00080) ddr_d |= 0x04;
+ if (outputs & 0x00040) ddr_d |= 0x08;
+ if (outputs & 0x00020) ddr_c |= 0x01;
+ if (outputs & 0x00010) ddr_c |= 0x02;
+ if (outputs & 0x00008) ddr_b |= 0x01;
+ if (outputs & 0x00004) ddr_b |= 0x02;
+ if (outputs & 0x00002) ddr_b |= 0x04;
+ if (outputs & 0x00001) ddr_b |= 0x08;
+
+ // Apply all changes as rapidly as possible, port D first because it has power and ground pins
+ PORTD = port_d;
+ PORTC = port_c;
+ PORTB = port_b;
+ DDRD = ddr_d;
+ DDRC = ddr_c;
+ DDRB = ddr_b;
+
+ delay(5);
+}
+
+
+bool assert_values(const char *left, const char *right)
+{
+ // Convert the string masks to bit masks
+ uint32_t check_high = 0, check_low = 0;
+
+ for (int i = 0; right[i] != '\0'; i++)
+ {
+ if (right[i] == '1')
+ {
+ check_high |= 1ul << (19-i);
+ }
+ else if (right[i] == '0')
+ {
+ check_low |= 1ul << (19-i);
+ }
+ }
+
+ for (int i = 0; left[i] != '\0'; i++)
+ {
+ if (left[i] == '1')
+ {
+ check_high |= 1ul << i;
+ }
+ else if (left[i] == '0')
+ {
+ check_low |= 1ul << i;
+ }
+ }
+
+ // Read input pins
+ uint8_t pin_b = PINB;
+ uint8_t pin_c = PINC;
+ uint8_t pin_d = PIND;
+
+ // Map GPIO register bits to test vector bits
+ uint32_t vector = 0;
+
+ if (pin_d & 0x80) vector |= 0x80000;
+ if (pin_d & 0x40) vector |= 0x40000;
+ if (pin_d & 0x20) vector |= 0x20000;
+ if (pin_d & 0x10) vector |= 0x10000;
+ if (pin_c & 0x08) vector |= 0x08000;
+ if (pin_c & 0x04) vector |= 0x04000;
+ if (pin_b & 0x80) vector |= 0x02000;
+ if (pin_b & 0x40) vector |= 0x01000;
+ if (pin_b & 0x20) vector |= 0x00800;
+ if (pin_b & 0x10) vector |= 0x00400;
+ if (pin_d & 0x01) vector |= 0x00200;
+ if (pin_d & 0x02) vector |= 0x00100;
+ if (pin_d & 0x04) vector |= 0x00080;
+ if (pin_d & 0x08) vector |= 0x00040;
+ if (pin_c & 0x01) vector |= 0x00020;
+ if (pin_c & 0x02) vector |= 0x00010;
+ if (pin_b & 0x01) vector |= 0x00008;
+ if (pin_b & 0x02) vector |= 0x00004;
+ if (pin_b & 0x04) vector |= 0x00002;
+ if (pin_b & 0x08) vector |= 0x00001;
+
+ // Check assertions
+ if ((vector & check_high) != check_high) return false;
+ if ((~vector & check_low) != check_low) return false;
+
+ return true;
+}
+
+
+void debug()
+{
+ uint8_t port_b, pin_b, ddr_b;
+ uint8_t port_c, pin_c, ddr_c;
+ uint8_t port_d, pin_d, ddr_d;
+
+ port_b = PORTB;
+ pin_b = PINB;
+ ddr_b = DDRB;
+
+ port_c = PORTC;
+ pin_c = PINC;
+ ddr_c = DDRC;
+
+ port_d = PORTD;
+ pin_d = PIND;
+ ddr_d = DDRD;
+
+ char hex[] = "0123456789ABCdEF";
+
+ segs.clear();
+ segs.writeDigitAscii(0, 'B');
+ segs.writeDigitAscii(1, 'O');
+ segs.writeColon();
+ segs.writeDigitAscii(3, hex[(port_b >> 4) & 0xf]);
+ segs.writeDigitAscii(4, hex[(port_b >> 0) & 0xf]);
+ segs.writeDisplay();
+ delay(1000);
+
+ segs.clear();
+ segs.writeDigitAscii(0, 'B');
+ segs.writeDigitAscii(1, 'I');
+ segs.writeColon();
+ segs.writeDigitAscii(3, hex[(pin_b >> 4) & 0xf]);
+ segs.writeDigitAscii(4, hex[(pin_b >> 0) & 0xf]);
+ segs.writeDisplay();
+ delay(1000);
+
+ segs.clear();
+ segs.writeDigitAscii(0, 'B');
+ segs.writeDigitAscii(1, 'D');
+ segs.writeColon();
+ segs.writeDigitAscii(3, hex[(ddr_b >> 4) & 0xf]);
+ segs.writeDigitAscii(4, hex[(ddr_b >> 0) & 0xf]);
+ segs.writeDisplay();
+ delay(1000);
+
+
+ segs.clear();
+ segs.writeDigitAscii(0, 'C');
+ segs.writeDigitAscii(1, 'O');
+ segs.writeColon();
+ segs.writeDigitAscii(3, hex[(port_c >> 4) & 0xf]);
+ segs.writeDigitAscii(4, hex[(port_c >> 0) & 0xf]);
+ segs.writeDisplay();
+ delay(1000);
+
+ segs.clear();
+ segs.writeDigitAscii(0, 'C');
+ segs.writeDigitAscii(1, 'I');
+ segs.writeColon();
+ segs.writeDigitAscii(3, hex[(pin_c >> 4) & 0xf]);
+ segs.writeDigitAscii(4, hex[(pin_c >> 0) & 0xf]);
+ segs.writeDisplay();
+ delay(1000);
+
+ segs.clear();
+ segs.writeDigitAscii(0, 'C');
+ segs.writeDigitAscii(1, 'D');
+ segs.writeColon();
+ segs.writeDigitAscii(3, hex[(ddr_c >> 4) & 0xf]);
+ segs.writeDigitAscii(4, hex[(ddr_c >> 0) & 0xf]);
+ segs.writeDisplay();
+ delay(1000);
+
+
+ segs.clear();
+ segs.writeDigitAscii(0, 'D');
+ segs.writeDigitAscii(1, 'O');
+ segs.writeColon();
+ segs.writeDigitAscii(3, hex[(port_d >> 4) & 0xf]);
+ segs.writeDigitAscii(4, hex[(port_d >> 0) & 0xf]);
+ segs.writeDisplay();
+ delay(1000);
+
+ segs.clear();
+ segs.writeDigitAscii(0, 'D');
+ segs.writeDigitAscii(1, 'I');
+ segs.writeColon();
+ segs.writeDigitAscii(3, hex[(pin_d >> 4) & 0xf]);
+ segs.writeDigitAscii(4, hex[(pin_d >> 0) & 0xf]);
+ segs.writeDisplay();
+ delay(1000);
+
+ segs.clear();
+ segs.writeDigitAscii(0, 'D');
+ segs.writeDigitAscii(1, 'D');
+ segs.writeColon();
+ segs.writeDigitAscii(3, hex[(ddr_d >> 4) & 0xf]);
+ segs.writeDigitAscii(4, hex[(ddr_d >> 0) & 0xf]);
+ segs.writeDisplay();
+ delay(1000);
+}
+
+
+void make_safe()
+{
+ // Disconnect all pins by turning them into inputs
+ DDRD = 0x00;
+ DDRC = 0x00;
+ DDRB = 0x00;
+
+ // Assign all outputs to zero to prevent enabling pullups
+ PORTD = 0x00;
+ PORTC = 0x00;
+ PORTB = 0x00;
+}
+
+
+void run_test(int n) {
+ for (int i = 0; i < _countof(tests); i++)
+ {
+ if (tests[i].part_number == n)
+ {
+ test_command *sequence = tests[i].sequence;
+
+ for (int j = 0; sequence[j].command != END; j++)
+ {
+ if (sequence[j].command == FORCE)
+ {
+ force_test_vector(sequence[j].left, sequence[j].right);
+ }
+ if (sequence[j].command == ASSERT)
+ {
+ if (!assert_values(sequence[j].left, sequence[j].right))
+ {
+ make_safe();
+ segs.clear();
+ segs.println("FAIL");
+ segs.writeDisplay();
+ delay(1000);
+ return;
+ }
+ }
+ if (sequence[j].command == DEBUG)
+ {
+ debug();
+ }
+ }
+ make_safe();
+ segs.clear();
+ segs.println("PASS");
+ segs.writeDisplay();
+ delay(1000);
+ return;
+ }
+ }
+
+ // Test not found
+ segs.clear();
+ segs.println(" NO ");
+ segs.writeDisplay();
+ delay(500);
+ segs.clear();
+ segs.println("TEST");
+ segs.writeDisplay();
+ delay(500);
+}
+
+
+int n0, n1, n2, n3;
+int last_key, key;
+uint32_t test_vec;
+
+
+void setup() {
+ make_safe();
+ segs.begin(0x70);
+ n0 = n1 = n2 = n3 = 0;
+ last_key = key = 0;
+ test_vec = 1;
+}
+
+
+void loop() {
+ int key_pin_value = analogRead(A6);
+ last_key = key;
+ if (key_pin_value < 858 + 20 && key_pin_value > 858 - 20) {
+ key = 6;
+ } else if (key_pin_value < 686 + 20 && key_pin_value > 686 - 20) {
+ key = 5;
+ } else if (key_pin_value < 521 + 20 && key_pin_value > 521 - 20) {
+ key = 4;
+ } else if (key_pin_value < 352 + 20 && key_pin_value > 352 - 20) {
+ key = 3;
+ } else if (key_pin_value < 177 + 20 && key_pin_value > 177 - 20) {
+ key = 2;
+ } else if (key_pin_value < 0 + 20) {
+ key = 1;
+ } else {
+ key = 0;
+ }
+
+ if (last_key == 0 && key != 0) {
+ switch (key) {
+ case 1:
+ n0 = n1 = n2 = n3 = 0;
+ break;
+ case 2:
+ n0 = (n0 + 1) % 10;
+ break;
+ case 3:
+ n1 = (n1 + 1) % 10;
+ break;
+ case 4:
+ n2 = (n2 + 1) % 10;
+ break;
+ case 5:
+ n3 = (n3 + 1) % 10;
+ break;
+ case 6:
+ int n = (n3 * 1000) + (n2 * 100) + (n1 * 10) + (n0 * 1);
+ run_test(n);
+ break;
+ }
+ }
+
+ segs.clear();
+ segs.writeDigitNum(4, n0);
+ segs.writeDigitNum(3, n1);
+ if (n2 > 0 || n3 > 0)
+ segs.writeDigitNum(1, n2);
+ if (n3 > 0)
+ segs.writeDigitNum(0, n3);
+ segs.writeDisplay();
+ delay(50);
+}