Last Updated: 11/03/2024

#71 CD4021BE Shift Register

Projects >> #71 CD4021BE Shift Register

CD4021BE Shift Register - Adding Extra inputs

What happens when you run out of pins on your UNO, simple answer buy an Arduino Mega. What if you still need more pins?

One of the simplest and cheapest ways is to use a shift register. This allows for almost limitless inputs from 3 wires.

Also the good news is they are very cheap. A pack of 10 is avaialable for under £5. That would add an extra 80 input pins!!

CD4021BE Shift Register

Introduction

Whiole working on a project for model railways I needed a way of adding a lot of button inputs for a control panel. Asd the project I'm working on can be used on any layout I needed a flexible system and so required a minimum of 100 button inputs. For anyone who has seen control panels on large layouts this number is easily reached.

I also wanted something cheap and easily scalable and the CD4021 shift register fits the requirements nicely.

Normally when using buttons they are each attached to an individual poin with a PULL_DOWN of PULL_UP resistor using digitalRead() to check the state of the pins.
This requires a pin per button.

UNO Standard buttons
In the example below we see the CD4021BE shift register is controlled by just 3 pins but has 8 inputs I have only put a single button, in practice a button would be placed in the same style on each of the CD4021BE registers.

CD4021BE Shift register - UNO

The great thing about shift registers is that they can be daisly chained with no addressing system in the style shown below.We now have 16 inputs using the same 3 Arduino pins.

More shift registers can be daisly chained as required.

WARNING: Although there is no real technical limit to the number of shift registers added there is a limit to the current draw on the Arduino.
If more than 3 are added it would be best to supply the 5v from a seperate power supply and just share a common GND with the Arduino.

Multiple CD4021BE Arduino UNo

How does it work?

This is a rather simplistic explanation of how the system works.

Each register on the CD4021BE storesd a 1 or 0 deopending if the pin is HIGH or LOW (from button press).
When the latch Pin opens the lock the data can be requested 1 byte at a time, each byte of data carrying the pins states on the register,
The clock pin prepares for more data if another CD4021BE is connected.

How CD4021BE works

If multiple CD4021BE shift registers are connected we can repeate the data call and each connected boards pin states will be received. A bit like sucking the data down a hose pipe.
We just need to call the number of boarda attached.

 

The problem?

I thought this would be quite straightforward as there is even an example at https://www.arduino.cc/reference/en/language/functions/advanced-io/shiftin/

However when I tried it I wasn't getting anything on register 0 and the other registers seem to have the wrong values. After taking everything apart and rebuilding a few trimes, changing chips etc I started to think I had bought a bad set of chips.

Searching online for the problem I found a reference to someone who had exactly the same problem and thankfully it had been solved by someone called runaway_pancake (Thanks). I did some tests with his code and it worked great but was not ideal for multiple calls so using his code as a basis I rewrote it to create a simple system that was easily configurable and could be connected to as many CD4021BE chips as I would need. Also as I hate delay() I reduced the delayMicroseconds() to the lowest value I could get away with.
The main loop is also timed using millis() to enable the code to interact with other code without any blocking.

The system puts all the data into a byte array, each CD4021BE putting it's data in it's own segment of the array.

The code uses the circuit in the images above.

Example 1: ESP-NOW ESP32 example

Please download the code as the HTML can distort the code in places

Click to Download code: cd4021V3.ino

Rather than reinventing I have written a code example based on the tutorials at:

The system is set up by defining the number of CD4021BE in the system in the line

#define NUMCD4021CHIPS 2

In case I am just using 2 for testing. This then defines the size of the array needed to store all the data.
 
/*  cd4021Vv3
 11/03/2024

 This version will work with as many CD4021's as needed.
 All data is stored in the myButtons[] array

 Many thanks to runaway_pancake for writing some code
that got me pointed in the right direction

//        Demo4021
//         
//        A   --1    16--   +
//        x   --2    15--   B
  dataPin/SO  --3    14--   C
//        E   --4    13--   D
//        F   --5    12--   x
//        G   --6    11--   +
//        H   --7    10--   clk
//      gnd   --8     9--   latch
*/

#define NUMCD4021CHIPS 2
byte myButtons[NUMCD4021CHIPS];//sets the array to the number of chips

int latchPin = 8;
int clockPin = 7;
int dataPin = 9;

unsigned long currentMillis;
unsigned long delayMillis;
int delayTimer = 2000;//2 seconds




void setup (){
  Serial.begin(9600);
  Serial.println("cd4021Vv3");
  pinMode (latchPin, OUTPUT);
  pinMode (clockPin, OUTPUT);
  pinMode (dataPin, INPUT);
 
}

void loop (){
  int q;
  int w;
  currentMillis = millis();
  if(currentMillis - delayMillis >= delayTimer){
    delayMillis = currentMillis;
    
    //This sets the system up to read the data
    latch4021(latchPin);//This function MUST be called before the get4021Byte() is called
    for(q=0;q < NUMCD4021CHIPS;q++){
      myButtons[q] = get4021Byte(dataPin,clockPin); //Put the bytes into the myButtons[] array  
    }
    //At this point all the data is in the myButtons[] array and can be used as required
    

    //This just shows the results
    
    for(w=0;w < NUMCD4021CHIPS;w++){
      Serial.print("myButtons[w] : ");
      Serial.print(w);
      Serial.print(" = ");
      Serial.println(myButtons[w],BIN);
      for(q=0;q<8;q++){//work though the bytes
        if(bitRead(myButtons[w],q) == 1){//if a bit is set
          Serial.print("Button Pressed : ");//tells us the button pressed 0-7
          Serial.println(q);
        }
      }
      Serial.println(" ");
    }
  }
}

void latch4021(int funcPin){
  digitalWrite (funcPin, HIGH);
  delayMicroseconds(5); //works... I want the smallest stable value possible
  digitalWrite (funcPin, LOW);
}

byte get4021Byte(int funcDataPin, int funcClockPin){
  byte registerContent;
  int bitContent;
  for (int idx = 0; idx < 8; idx++)
  {
    bitContent = digitalRead (funcDataPin);
    if (bitContent == 1)
    {
      bitWrite (registerContent,idx,1);
    }
    else
    {
      bitWrite (registerContent,idx,0);
    }
    digitalWrite (funcClockPin, LOW);
    delayMicroseconds(5); //works... I want the smallest stable value possible
    digitalWrite (funcClockPin, HIGH);
  }
  return registerContent;
}

 

Comments


For help or suggestions on new projects, please email the address in this image: and use #71 CD4021BE Shift Register as a reference.