I made a 16-bit hardware driver for a 4-digit 7-segment common cathode display. It consists of two daisy-chained 74HC595 ICs. Most significant byte (specifically bits 15, 13, 11 and 9) control 4 transistors that switch characters (1 - 4) on and off. Least significant byte forms "byte masks" which correspond to digits to be displayed.
When I use this driver with normal Arduino outputs, if works fine, even when multiplexing. But then all of a sudden I began to wonder: "Can I control my driver with another 595's outputs? I know it's completely unnecessary, but just... Can I? As far as I understand, I can treat 595's outputs more or less like an MCU output register, say, PORTB. Therefore I can use 595's outputs to control another 595s".
So I wrote this sketch to give it a try:
// Arduino outputs
#define DATA_PIN 6
#define LATCH_PIN 7
#define CLOCK_PIN 8
// "Primary" 595's "digital outputs" which control other 595's inputs.
#define _595_DATA_PIN 2
#define _595_LATCH_PIN 1
#define _595_CLOCK_PIN 0
// Pin for a button I used for debugging.
#define TOGGLE_BUTTON_PIN A0
void _digital_write(uint8_t pin_num, bool level) {
static uint8_t output = 0;
if (level) {
output ^= (1 << pin_num);
} else {
output &= ~(1 << pin_num);
}
// Arduino's outputs refresh "output register", i. e. "primary" 595's outputs.
digitalWrite(LATCH_PIN, 0);
shiftOut(DATA_PIN, CLOCK_PIN, MSBFIRST, output);
digitalWrite(LATCH_PIN, 1);
}
// My version of Arduino shiftOut function, but using "primary" 595's outputs instead of Arduino's own outputs.
void _shift_out(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder, uint8_t val)
{
uint8_t i;
for (i = 0; i < 8; ++i) {
if (bitOrder == LSBFIRST) {
_digital_write(dataPin, val & 1);
val >>= 1;
} else {
_digital_write(dataPin, (val & 128) != 0);
val <<= 1;
}
_digital_write(clockPin, 1);
_digital_write(clockPin, 0);
}
}
uint8_t output_character; // Global variable for switching characters on and off.
// "Bit map": which bit control which transistors (characters) and segments.
// 10203040 E.CGAFBD
#define D1_CHARACTER 1
#define D2_CHARACTER 3
#define D3_CHARACTER 5
#define D4_CHARACTER 7
#define DIGIT_BYTE_MASK_0 0b10101111 // 0
#define DIGIT_BYTE_MASK_1 0b00100010 // 1
#define DIGIT_BYTE_MASK_2 0b10011011 // 2
#define DIGIT_BYTE_MASK_3 0b00111011 // 3
#define DIGIT_BYTE_MASK_4 0b00110110 // 4
#define DIGIT_BYTE_MASK_5 0b00111101 // 5
#define DIGIT_BYTE_MASK_6 0b10111101 // 6
#define DIGIT_BYTE_MASK_7 0b00101010 // 7
#define DIGIT_BYTE_MASK_8 0b10111111 // 8
#define DIGIT_BYTE_MASK_9 0b00111111 // 9
void display_digit(uint8_t current_character, uint8_t digit_to_display) {
uint8_t output_matrix[] = {
DIGIT_BYTE_MASK_0, // 0
DIGIT_BYTE_MASK_1, // 1
DIGIT_BYTE_MASK_2, // 2
DIGIT_BYTE_MASK_3, // 3
DIGIT_BYTE_MASK_4, // 4
DIGIT_BYTE_MASK_5, // 5
DIGIT_BYTE_MASK_6, // 6
DIGIT_BYTE_MASK_7, // 7
DIGIT_BYTE_MASK_8, // 8
DIGIT_BYTE_MASK_9 // 9
};
output_character = 0;
output_character ^= (1 << current_character);
_digital_write(_595_LATCH_PIN, 0);
_shift_out(_595_DATA_PIN, _595_CLOCK_PIN, MSBFIRST, output_character);
_shift_out(_595_DATA_PIN, _595_CLOCK_PIN, MSBFIRST, output_matrix[digit_to_display]);
_digital_write(_595_LATCH_PIN, 1);
// Code I had used with normal Arduino outputs.
/*
digitalWrite(LATCH_PIN, 0);
shiftOut(DATA_PIN, CLOCK_PIN, MSBFIRST, output_character);
shiftOut(DATA_PIN, CLOCK_PIN, MSBFIRST, output_matrix[digit_to_display]);
digitalWrite(LATCH_PIN, 1);
*/
}
void setup() {
pinMode(DATA_PIN, OUTPUT);
pinMode(LATCH_PIN, OUTPUT);
pinMode(CLOCK_PIN, OUTPUT);
pinMode(TOGGLE_BUTTON_PIN, INPUT_PULLUP);
Serial.begin(9600);
}
void loop() {
static uint8_t i = 0;
delay(800);
++i;
if (i > 9) i = 0;
display_digit(D1_CHARACTER, i);
}
Issues arised. Sketch turned characters on/off correctly, but byte masks were distorted: when I tried to output 1 or 7, it worked properly, but attempting to display other digits lead to ligthing up wrong segments. Through debugging I found that removing static from output variable in _digital_output function solves the issue, and bytes are displayed correctly once again.
Can you help me understand that? My reasoning after adding static to output variable was that turning certain bit on or off should not affect other bits which were turned on previously.