Close

74HC595 Shift Register, I misunderstood you

A project log for Arduino (ESP32) Standalone Accordion

Polyphonic Piano Accordion made from a cheap Melodica, some buttons and an ESP32 microcontroller

bruno-campidelliBruno Campidelli 08/29/2024 at 00:040 Comments

To use fewer ESP32 pins, I need to multiplex the inputs as I have been doing already (using a matrix scan) but I can go further and add shift registers to save more pins!

However, the subject can be quite confusing and intimidating, at least to me. I thought I could use a single 74HC595 to scan both the COLUMNS and ROWS of my matrix, but it turns out I can't. That is because the 74HC595 is a serial IN and parallel OUT shift register. To scan a matrix I need to write to the columns (or rows, pick one) and read the rows (or columns if you have picked the rows before).

I bought a very nice module that has three 74HC595 chained, allowing you to expand your ports to a whopping 24 outputs! But I still need a way to read several inputs, and for that, there is another shift register called 74HC165.

This module has three chained 74HC595

Since I am still waiting for my 74HC165 to arrive from China, I decided to use a small 74HC595 I had here to test the matrix scanning, using it to output signal to 4 columns and read the 2 rows using the ESP32 input pins.

Neat, right?

Well, it didn't work. So I had to check if my shift register was working and for that, I had to learn more about it. So I checked this tutorial here (https://dronebotworkshop.com/shift-registers/) and it is amazing. Here is the Hello World example:

#include <Arduino.h>

// ST_CP pin 22
const int latchPin = 22;
// SH_CP pin 21
const int clockPin = 21;
// DS pin 23
const int dataPin = 23;

void setup () {
  // Setup pins as Outputs
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
}

void loop() {
  // Count from 0 to 255 and display in binary
  for (int numberToDisplay = 0; numberToDisplay < 256; numberToDisplay++) {
    // ST_CP LOW to keep LEDs from changing while reading serial data
    digitalWrite(latchPin, LOW);
    // Shift out the bits
    shiftOut(dataPin, clockPin, MSBFIRST, numberToDisplay);
    // ST_CP HIGH change LEDs
    digitalWrite(latchPin, HIGH);
    delay(500);
  }
}

And the demo!

Now that I now that my shift register IS working, I had to recheck the wiring and my program, it turns out that I had misread the data pin GPIO number and set it to 27 instead of 23. Now it is working! And it supports multi-pressed buttons! Here is the sketch:

#include <Arduino.h>

// Pin definitions
const int DATA_PIN  = 23; // Data (SER) -> GPIO 23
const int CLOCK_PIN = 21; // Clock (SRCLK) -> GPIO 21
const int LATCH_PIN = 22; // Latch (RCLK) -> GPIO 22

// Matrix dimensions
const int COLS = 4;
const int ROWS = 2;

// Row pins
const int rowPins[ROWS] = {12, 14}; // Row 1 -> GPIO 12, Row 2 -> GPIO 14

// Column to 74HC595 mapping
const int colPins[COLS] = {0, 1, 2, 3}; // Q0 to Q3 on the 74HC595

// Array to keep track of button states (0 = not pressed, 1 = pressed)
bool buttonStates[ROWS][COLS] = {false};

// Set a specific column LOW by controlling the 74HC595
void setColumn(int col) {
  uint8_t colValue = ~(1 << col); // Set only the selected column LOW
  shiftOut(DATA_PIN, CLOCK_PIN, MSBFIRST, colValue);

  // Latch to transfer the data to output pins
  digitalWrite(LATCH_PIN, LOW);
  digitalWrite(LATCH_PIN, HIGH);

  // Small delay to allow the shift register output to stabilize
  delayMicroseconds(10);
}

void onButtonPress(int button) {
  Serial.print("Button ");
  Serial.print(button);
  Serial.println(" pressed");
}

void onButtonRelease(int button) {
  Serial.print("Button ");
  Serial.print(button);
  Serial.println(" released");
}

void setup() {
  // Initialize the Serial
  Serial.begin(115200);

  // Set up the row pins as inputs with pull-up resistors
  for (int i = 0; i < ROWS; i++) {
    pinMode(rowPins[i], INPUT_PULLUP);
  }

  // Set up the control pins for the 74HC595
  pinMode(DATA_PIN, OUTPUT);
  pinMode(CLOCK_PIN, OUTPUT);
  pinMode(LATCH_PIN, OUTPUT);
}

void loop() {
  for (int col = 0; col < COLS; col++) {
    // Set the current column to LOW
    setColumn(col);

    // Check each row for a key press
    for (int row = 0; row < ROWS; row++) {
      int currentState = digitalRead(rowPins[row]);

      // Determine the button number (1 to 8 in this case)
      int button = row * COLS + col + 1;

      if (currentState == LOW && !buttonStates[row][col]) {
        // Button was just pressed
        buttonStates[row][col] = true;
        onButtonPress(button);
      } else if (currentState == HIGH && buttonStates[row][col]) {
        // Button was just released
        buttonStates[row][col] = false;
        onButtonRelease(button);
      }
    }
  }

  delay(50); // Debounce delay
}

The next step is to make sounds with this setup!

Discussions