Capacitors with Integrated Circuits

Hey guys, I'm playing around with shift registers to read data from 13 buttons. I'm following the multiple shift register portion of tutorial that uses the older CD4021 PISO registers. I'll be adjusting my design to use SN74CH165 registers when I get them in the mail.

My wiring is setup exactly like the Fritzing diagram, and included 10Kohm pull-down resistors on all of the registers' input pins, including the 3 I wasn't using for safe measure.

I am running into some problems though:
-I'm getting this weird issue where the first register fluctuates between 0 and 1 frequently
-It also seems that my shift registers' values are shifted up by one bit, so the the first button that's hooked up to the first register's P1 is actually 0b10, and the second button that's hooked up that same register's P2 is actually 0x100 when HIGH, etc. Then, it's the 8th button yields 0b10000001, which is weird...

I've checked my wiring multiple times, but I'm going to reduce it back down to a single register, verify it works, and move back up to two registers (I've already done this too).

I read somewhere online that the Arduino library's shiftIn() code doesn't work as well with the CD4021 due to how it times things in cases where many CD4021s are are daisy-chained together, but I'm only using 2. I also removed shiftIn() call in my code, and manually shift the data in.

I've also read other resources online such as this forum topic here, and other users suggest using a 0.1uf ceramic capacitor between the registers' +5V and GND pins. I ordered some ceramic capacitors because I have any, but this did bring up a few questions:

  1. Are capacitors necessary in this case (I'm only using 2 CD4021 registers)? Is it good practice to have them anyway to provide repeatable, expected behavior?
  2. Should I use capacitors in general when working with integrated circuits?
  3. Do both registers need capacitors on their VCC and GND pins for a total of 4 capacitors?
  4. I understand that capacitors smooth current out by providing closing-aligned plates for electronics to jump between, but will this fix the floating issue I'm having?
  1. Pretty much all digital IC's need 0.1uF ceramic decoupling cap for reliable operation. This is often given a very brief mention in the datasheet (since designers know to expect one anyway, it doesn't need to be called out loudly)
  2. Yes - and digital IC gets 0.1uF between Vcc and Gnd right next to the chip unless datasheet spec's otherwise.
  3. 2 capacitors, one per chip, between Vcc and Gnd (I'm not sure what you're imagining the other lead of the caps connected to in your question)
  4. Don't know, you have not posted the schematic detailing how you have things connected.

Why not using the 74HC165, a more modern PISO shift register?

Capacitors needed for every logic chip, without them many weird and strange behaviours
can happen causing complete confusion and head-scratching - it might work one day and fail the next, etc.
Particularly likely to misbehave is any clock input, which can register double pulses or even jump the
circuit into an unexpected state if the supply isn't solid and decoupled at high frequencies. All of these
phenomena happen on timescales of a few nanoseconds where the layout is crucial and dominated
by inductance of the wiring.

Try to keep the leads much shorter than shown in the tutorial, particularly the ground wiring which should be
as direct as possible from board to breadboard and between breadboards. Try to run ground wires alongside
signal wires to provide their high speed current return path, otherwise the return path simply jumps to other signal wires which is not ideal.

Distance between chip and its decoupling capacitors should be as short as possible, which on a breadboard
would typically mean the cap stretches diagonally across the top of the chip.

All of these layout tips increase the chance the circuit will work as intended without having to resort to
expensive test equipment to figure out what's happening.

  1. I understand that capacitors smooth current out by providing closing-aligned plates for electronics to jump between,

No. It smooths the voltage variation caused by changing current by supplying a small reservoir of charge to top up any voltage changes.

If any electrons jump between a capacitor's plates then the capacitor is, to use a technical term, fucked.

@DrAzzy: Thanks, I'll keep in mind that ICs need capacitors to keep current smooth. I've only been working on circuits for 2 weeks, so I'm still pretty new to things. I've learned a few things a series circuits, but not much on parallel circuits yet.

Here's the Fritzing diagram I'm following (taken from the tutorial I linked in the original post):

I'm using CD4021 shift registers because that's what the Arduino tutorial I found uses. I didn't learned about the SN74CH165 until after the fact. I ordered some SN74CH165 shift registers last night, so I'll update my circuit to work with those following this tutorial once they're in.

I'll also use only 2 capacitors instead of 4 when I get them. Could I use 1uf in the mean time? I'm 3 days behind development, and I need to at least get started writing the software drivers for Linux today. I can move my breadboard back to the Arduino tomorrow when I get the SN74HC165 registers to make sure the circuit still works.

My shift registers are wired up exactly as shown in the Fritzing diagram above, so each shift register's +V and GND have their own jumper wire connecting them to the appropriate +V/GND rails. It sounds like I should run a jumper wire between both registers' +V pins, and another jumper wire between both of their GND pins. Then, run a capacitor between one of the +V leads to the +V rail on the breadboard, and then do the same with GND.

Regarding how the chips are hooked up: I run each chip's +V and GND pins to the +V and GND rails on my breadboard separately. It sounds like I should run a jumper wire directly between both chips' V+ and GND pins linking them up in series on the breadboard, and then connect one of each lead to the corresponding +V/GND rails with a capacitor.

@MarkT: I'll keep my wires shorter than the example diagram. The wires between +V and GND are as direct as possible between all the components and the power rails on the side of the board. I link both sides of the power rails together with wires that are as direct as possible as well. I do use pre-made jumper cables for all connections between the Arduino and breadboard (+V, GND, DATA, CLK, LATCH). I also use pre-made wires to connect the buttons to the correct pins, but I've thought about using custom-cut wires because it's pretty messy.

I'll also make sure to connect the capacitor directly between the shift registers and the corresponding power rail.

@Grump_Mike: Good call, and it makes sense to regulate the voltage rather than current.

Hi,
1uF will help, so its okay to use them.

Regarding how the chips are hooked up: I run each chip's +V and GND pins to the +V and GND rails on my breadboard separately.

That is the best way to connect, daisy chaining will only compound the supply glitch problem.

Tom... :slight_smile:

Alright, sounds good. I'll use 1uf until my capacitors come in the mail today. I bought an assortment pack of ceramic capacitors since I really don't have any.

If running each +V/GND lead to their power rails is the best way to go about it instead of daisy-chaining them together before connecting to the power rails, then should I be using 4 capacitors?

I got my SN74HC165N shift registers in the mail too, so I'm swapping them out. I was comparing the pins to the CD4021, and the pins seem identical except the layout is a little more organized (each side of the package has 4 Dx input pins, and they're all lined up). I'm looking at the Arduino tutorial that utilizes these shift registers, and this one actually has an output pin running from the Arduino to the clock enable pin. It looks like it works similarly to the parallel load pin in the sense that it must be activated before performing loads, and then it can be deactivated after the load is finished. This seems redundant because performing the load is roughly the same: change the PL signal voltage, wait, change voltage back. The only difference is that the clock enable pin's voltage is opposite of the latch. Here's what I've got so far:

void read_data()
{
  // signal registers to capture input pins
  digitalWrite(EnablePin, HIGH);
  digitalWrite(LatchPin, LOW);
  delayMicroseconds(PulseDelay);
  digitalWrite(LatchPin, HIGH);
  digitalWrite(EnablePin, LOW);

  // proceed to shift data in by pulsing the clock pin within a loop, and reading the data pin
  ...
}

Should the clock enable pin (called EnablePin in my code) be set to HIGH until I finish shifting data in? I've also read somewhere else that it's good to keep the PL pin (called LatchPin in my code) LOW while shifting data in. This leads me to think that these registers don't actually copy whatever the current state of its input pins are into a separate internal register for reading, but I dunno. Tutorials vary online, and I'm not sure how reliable it is.

EDIT: I just realized a bug in my code, and could explains the weird behavior I've described. When I go to shift data in, I do it manually by first pulsing the clock pin, and then reading the data from the pin. This is incorrect because my data pin (connected to the last register's serial-out pin) is set to whatever the PL register's D7 pin is by default. By doing this, I'm grabbing the second value as my first bit, and then end up trying to read an "imaginary" 9th bit's state, which could be anything (which could explain why I get a somewhat frequent floating behavior).

This doesn't explain why I was getting the same behavior when using shiftIn(), but I'll report back one I've finished updating my code.

EDIT: I was able to update my circuit with SN74HC165N chips, and fix my code bugs. Everything seems to work now. Btw, can I set the clock enable pin to HIGH all the time? Here's my code:

// constants
#define PIN_DATA      7
#define PIN_ENABLE    8
#define PIN_LATCH     9
#define PIN_CLOCK    10

#define DELAY_PULSE   5
#define DELAY_REPORT 500

#define NUM_REGISTERS 2
#define NUM_BITS      16

// global variables
static unsigned char bit = 0;
static unsigned char data[NUM_REGISTERS]; // bit field meant to hold all registers' states
static unsigned char lastData[NUM_REGISTERS]; // state of the previous registers' value

void read_data()
{
  // signal registers to capture input pins
  digitalWrite(PIN_ENABLE, HIGH);
  digitalWrite(PIN_LATCH, LOW);
  delayMicroseconds(DELAY_PULSE);
  digitalWrite(PIN_LATCH, HIGH);
  digitalWrite(PIN_ENABLE, LOW);

  // cache the last state, and reset data
  memcpy(lastData, data, NUM_REGISTERS);
  memset(data, 0, NUM_REGISTERS);

  // shift the data in
  for (int i = (NUM_BITS - 1); i >= 0; --i)
  {
    bit = digitalRead(PIN_DATA);
    data[i / 8] |= (bit << (i % 8) );

    digitalWrite(PIN_CLOCK, HIGH);
    delayMicroseconds(DELAY_PULSE);
    digitalWrite(PIN_CLOCK, LOW);
  }
}

void report_data()
{
  // print bitfield if input has changed
  if (memcmp(data, lastData, NUM_REGISTERS))
  {
    Serial.print("Data:");
    for (int i = (NUM_BITS - 1); i >= 0; --i)
    {
      if ( (i - (NUM_BITS - 1) ) % 4 == 0)
        Serial.print(" ");
        
      if ( (i - (NUM_BITS - 1) ) % 8 == 0)
        Serial.print(" ");

      bit = (data[i / 8] & (1 << (i % 8)));
      Serial.print(bit ? "1" : "0");
    }
    Serial.print("\n");
  }
}

void setup()
{
  // define pins used to connect to the last SN74HC165N shift register
  pinMode(PIN_DATA, INPUT);
  pinMode(PIN_ENABLE, OUTPUT);
  pinMode(PIN_CLOCK, OUTPUT);
  pinMode(PIN_LATCH, OUTPUT);

  digitalWrite(PIN_CLOCK, LOW);
  digitalWrite(PIN_LATCH, HIGH);
  Serial.begin(9600);

  memset(data, 0, NUM_REGISTERS);
  memset(lastData, 0, NUM_REGISTERS);
}

void loop()
{
  // read and report the data
  read_data();
  report_data();
}