2015/06/22

How to use shift register (74HC165)

Below is an example on how to use 74HC165 shift register with Arduino Uno. I made this post to record my findings while following Sparkfun's tutorial on shift registers.

The Sparkfun tutorial website on shift registers is here: https://learn.sparkfun.com/tutorials/shift-registers

Wiring Diagram:


Waveform:

This is taken from the 74HC165 datasheet, observe that !PL needs to be LOW for certain time period.


Observe that "tw" is the time period that !PL must remain LOW.


The minimum tw is 120ns (0.12ms). In the program, we add 5ms delay by putting "delayMicroseconds(5);" after pulling !PL to LOW "(digitalWrite(shld_pin, LOW);" to ensure this requirement is met.


The Code:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
// HARDWARE CONNECTIONS
// Connect the following pins between your Arduino and the 74HC165 chip
// Connect pins D7 - D0 to 5V or GND or switches or whatever

const int data_pin = 11; // Connect Pin 11 to SER_OUT (serial data out) - Pin 9 (Q7) of 74HC165
const int shld_pin = 8;  // Connect Pin 8 to SH/!LD (shift or active low load) - Pin 1 (!PL) of 74HC165
const int clk_pin = 12;  // Connect Pin 12 to CLK (the clock that times the shifting) - Pin 2 (CP) of 74HC165
const int ce_pin = 9;    // Connect Pin 9 to !CE (clock enable, active low) - Pin 15 (!CE) of 74HC165

byte incoming; // Variable to store the 8 values loaded from the shift register

// The part that runs once

void setup() {                
  // Initialize serial to gain the power to obtain relevant information, 9600 baud
  Serial.begin(9600);

  // Initialize each digital pin to either output or input
  // We are commanding the shift register with each pin with the exception of the serial
  // data we get back on the data_pin line.
  pinMode(shld_pin, OUTPUT);
  pinMode(ce_pin, OUTPUT);
  pinMode(clk_pin, OUTPUT);
  pinMode(data_pin, INPUT);

  // Required initial states of these two pins according to the datasheet timing diagram
  digitalWrite(clk_pin, HIGH);
  digitalWrite(shld_pin, HIGH);
}

// The part that runs to infinity and beyond

void loop() {
  incoming = read_shift_regs(); // Read the shift register, it likes that

  // Print out the values being read from the shift register
  Serial.println("\nThe incoming values of the shift register are: ");
  Serial.println("D7 D6 D5 D4 D3 D2 D1 D0:");
  print_byte(incoming); // Print every 1 and 0 that correlates with 8 through 1 of the dip switch

  //Serial.println(incoming, BIN); // **This way works too but leaves out the leading zeros**
  delay(2000); // Wait for some arbitrary amount of time
}

// This code is intended to trigger the shift register to grab values from its D7 - D0 inputs

byte read_shift_regs(){

  byte the_shifted = 0;  // An 8 bit number to carry each bit value of D7 - D0

  // Trigger loading the state of the A-H data lines into the shift register
  digitalWrite(shld_pin, LOW);
  delayMicroseconds(5); // Requires a delay here according to the datasheet timing diagram
  digitalWrite(shld_pin, HIGH);
  delayMicroseconds(5);

  // Required initial states of these two pins according to the datasheet timing diagram
  digitalWrite(clk_pin, HIGH);
  digitalWrite(ce_pin, LOW); // Enable the clock

  // Get D7 ~ D0 values
  // The function shiftIn() shifts in a byte (8-bit) of data one bit at a time staring from either the most (leftmost) or the least (rightmost) significant bit.

  the_shifted = shiftIn(data_pin, clk_pin, MSBFIRST);
  digitalWrite(ce_pin, HIGH); // Disable the clock

  return the_shifted;
}

void print_byte(byte val){
// This part is a bit different from the Sparkfun code.
// Reference:  The Quick Reference section of Bit Math Tutorial http://playground.arduino.cc/Code/BitMath on how to isolate individual bit.

      Serial.print(val >> 7 & 1, BIN); // print 7th bit of val
      Serial.print("  ");
      Serial.print(val >> 6 & 1, BIN); // print 6th bit of val
      Serial.print("  ");
      Serial.print(val >> 5 & 1, BIN); // print 5th bit of val
      Serial.print("  ");
      Serial.print(val >> 4 & 1, BIN); // print 4th bit of val
      Serial.print("  ");
      Serial.print(val >> 3 & 1, BIN); // print 3th bit of val
      Serial.print("  ");
      Serial.print(val >> 2 & 1, BIN); // print 2th bit of val
      Serial.print("  ");
      Serial.print(val >> 1 & 1, BIN); // print 1th bit of val
      Serial.print("  ");
      Serial.println(val >> 0 & 1, BIN); // print 0th bit of val 

// the above code can be written as follow

      //int i;
      for (int i = 8; i > 0; i--){
          Serial.print(val >> (i - 1) & 1, BIN);
          Serial.print("  ");
      }
      Serial.print("\n");  
}

The Result:


Note:

D7 ~ D0 are corresponding to the pinout of 74HC165, not the label on the dip switch.

References:

- Arduino - print(), http://www.arduino.cc/en/Serial/Print
- Arduino - bitshift right (>>), http://www.arduino.cc/en/Reference/Bitshift
- Arduino - bitwise AND (&), http://www.arduino.cc/en/Reference/BitwiseAnd
- 74HC165 datasheet - http://www.nxp.com/documents/data_sheet/74HC_HCT165.pdf

--------------------------------------------------------------------------------------------------------------------------

Working with ESP8266 and 74HC165 on 3.3V

The section below is added on Jan. 17, 2020 to show how to interface ESP8266 (WeMos D1 Mini) with 74HC165 and have them working on 3.3V.

The Schematic


The Code

It's basically the same as the one above with minor changes to the pin assignment.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
// HARDWARE CONNECTIONS
// Connect the following pins between your Arduino and the 74HC165 chip
// Connect pins D7 - D0 to 5V or GND or switches or whatever

const int data_pin = 4; // Connect WeMos D1 Mini D2 pin (GPIO4) to SER_OUT (serial data out) - Pin 9 (Q7) of 74HC165
const int shld_pin = 2; // Connect WeMos D1 Mini D4 pin (GPIO2) to SH/!LD (shift or active low load) - Pin 1 (!PL) of 74HC165
const int clk_pin = 5;  // Connect WeMos D1 Mini D1 pin (GPIO5) to CLK (the clock that times the shifting) - Pin 2 (CP) of 74HC165
const int ce_pin = 0;   // Connect WeMos D1 Mini D3 (GPIO0) to !CE (clock enable, active low) - Pin 15 (!CE) of 74HC165

byte incoming;          // Variable to store the 8 values loaded from the shift register

// The code below triggers the shift register to grab values from its D7 - D0 inputs
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
byte read_shift_regs(){

  byte the_shifted = 0;  // An 8-bit number to carry each bit value of D7 - D0

  // Trigger loading the state of the A-H data lines into the shift register

  digitalWrite(shld_pin, LOW);
  delayMicroseconds(5); // Requires a delay here according to the datasheet timing diagram
  digitalWrite(shld_pin, HIGH);
  delayMicroseconds(5);

  // Required initial states of these two pins according to the datasheet timing diagram

  digitalWrite(clk_pin, HIGH);
  digitalWrite(ce_pin, LOW); // Enable the clock

  // Get D7 ~ D0 values
  // The function shiftIn() shifts in a byte (8-bit) of data one bit at a time staring from either the most (leftmost) or the least (rightmost) significant bit.
  
  the_shifted = shiftIn(data_pin, clk_pin, MSBFIRST);
  digitalWrite(ce_pin, HIGH); // Disable the clock

  return the_shifted;
}
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

void print_byte(byte val){

// This part is a bit different from the Sparkfun code.
// Reference:  The Quick Reference section of Bit Math Tutorial http://playground.arduino.cc/Code/BitMath on how to isolate individual bit.

      /*
      Serial.print(val >> 7 & 1, BIN); // print 7th bit of val
      Serial.print("  ");
      Serial.print(val >> 6 & 1, BIN); // print 6th bit of val
      Serial.print("  ");
      Serial.print(val >> 5 & 1, BIN); // print 5th bit of val
      Serial.print("  ");
      Serial.print(val >> 4 & 1, BIN); // print 4th bit of val
      Serial.print("  ");
      Serial.print(val >> 3 & 1, BIN); // print 3th bit of val
      Serial.print("  ");
      Serial.print(val >> 2 & 1, BIN); // print 2th bit of val
      Serial.print("  ");
      Serial.print(val >> 1 & 1, BIN); // print 1th bit of val
      Serial.print("  ");
      Serial.println(val >> 0 & 1, BIN); // print 0th bit of val
      */

// the above code can be written as follow
      
      //int i;
      for (int i = 8; i > 0; i--) {
          Serial.print(val >> (i - 1) & 1, BIN);
          Serial.print("  ");
        }
      Serial.print("\n");  
}

void setup() {                
  Serial.begin(115200);

  // Initialize each digital pin to either output or input
  // We are commanding the shift register with each pin with the exception of the serial
  // data we get back on the data_pin line.

  pinMode(shld_pin, OUTPUT);
  pinMode(ce_pin, OUTPUT);
  pinMode(clk_pin, OUTPUT);
  pinMode(data_pin, INPUT);

  // Initialize the below two pins according to the datasheet timing diagram
  digitalWrite(clk_pin, HIGH);
  digitalWrite(shld_pin, HIGH);
}

void loop(){

  incoming = read_shift_regs(); // Read the shift register, it likes that

  // Print out the values being read from the shift register

  Serial.println("\nThe incoming values of the shift register are: ");
  Serial.println("D7 D6 D5 D4 D3 D2 D1 D0:");
  print_byte(incoming); // Print every 1 and 0 that correlates with 8 through 1 of the dip switch
  
  //Serial.println(incoming, BIN); // **This way works too but leaves out the leading zeros**

  delay(2000); // Wait for some arbitrary amount of time
}

The Result


25 comments:

  1. Many thanks for the tutorial. Do you know how to implement this into a uno in HID mode? I already have HID mode working.

    ReplyDelete
  2. What do you want to achieve in the HID mode? What's the application?

    ReplyDelete
    Replies
    1. Many thanks for your quick reply Wei-Hsiung. It's to add extra buttons to a custom collective / joystick to mainly use with DCS world. I'm using the Oculus DK2 so i'm trying to avoid using the keyboard blindly. A mega would be easier but I'm limited for space and I have plenty of shift registers.

      Delete
    2. I think it's possible. The only issue that I could think of is how fast can this extended keyboard sends out the keystroke (need to test it out). How many keys do you currently have on your HID keyboard and how many more keys do you want?

      Delete
  3. I have two thumb sticks on a1 - a4. The other two analog pins will be used as digital. This at the moment gives me 14 digital. 8 of these will have two dpads. 9 pins will be used for rotary encoders, then a push button and about 8 swithes, so I would be looking at two daisy chained registers.

    ReplyDelete
  4. I've posted up both sets of code i've got so far on the arduino forums. The 74hc165 code has been taken from your site.

    ReplyDelete
    Replies
    1. Great!! Let me know how did it work out for you..

      Delete
  5. I've still not found a way to link the two codes together. Both work independently but not sure where to start adding the 74hc165 to the hid code.

    ReplyDelete
    Replies
    1. This comment has been removed by the author.

      Delete
    2. Could you paste the link to your code on Arduino Forum for me to take a look?

      Delete
    3. My apolgies, I thought I had. Will try again:

      https://forum.arduino.cc/index.php?topic=405837.0

      Delete
    4. Hi Wei-Hsiung. I posted up the link again yesterday but it doesn't seem to be showing.

      Delete
    5. Sorry. It's my fault. I didn't publish that post (now it's polished)...

      Delete
    6. I've taken a look at the code made by Darran Hunt and I think he only implemented the sending of joystick data to the PC, not the keystrokes. This can be seemed from the sentence "Here's a sketch demonstrating how to send joystick data to the host." that he put right between the "Example Sketch" and his code. In order to send the keystroke to the PC, the keystroke needs to be added into the "Keyboard Report Buffer", which is the joyReport structure defined in Darran's code and it's sent out by the sendJoyReport function.

      I think what you need to do is to first combine the shift register code that you got from my blog with the HID keyboard example at http://mitchtech.net/arduino-usb-hid-keyboard/ to implement a HID keyboard based on shift register. After that, you could merge this code with Darran's code to come out with what you want to achieve.

      Delete
    7. I've done some research on HID keyboard and HID joystick and will post my findings on my blog in a few minutes (it's not finished yet as I want to make a tutorial about it). Hope the preliminary version will help you to carry out your project.

      Delete
  6. Many thanks for you input, i'll give it a go. I presume I need to work out how to send the binary variable/keystrock to a virtual pin so it can be sent to the joy_report as a button?

    ReplyDelete
    Replies
    1. Yes! That's correct. And you could find reference about the keystroke codes, the Keyboard Report Buffer, and other useful info. in my latest post that I've just posted on my blog...

      Delete
  7. Hiya Wei-Hsiung. How would I put the 8 bit binary into an array?

    ReplyDelete
    Replies
    1. There are several ways to do it. For example, you could use a For or While loop and bitwise AND (https://www.arduino.cc/en/Reference/BitwiseAnd) to check each bit. If it's a 1 then put 1 into the corresponding spot in an array, if it's a 0 then put 0.

      Delete
  8. I've managed to get the array filled and it works fine on the shift register code. I have merged the two codes and when I plug in as a HID device the correct buttons are lit, however on button change it all goes manic. Can you please look at the code and see if you can see where I am going wrong.

    https://forum.arduino.cc/index.php?topic=405837.0

    Many thanks

    Les

    ReplyDelete
    Replies
    1. Could you send me your schematic and let me know how you test it (do you use any software to help you do the test)? I haven't looked at you code, but my guess is that there may be problem in your schematic (how you wire up the buttons and the unused pin(s)).

      Delete
    2. Have sent to your email as i'm not sure how to add attachments here.

      Delete
    3. I've browsed through your code and the schematic (but I don't have time to actually duplicate your settings and trace the code), below are some issues that you might want to check:

      1. Make sure the pins are properly wired.

      In your schematic, A2, A3, and A5 are not wired, but they are used in your code.

      //joyReport.axis[0] = (-32768 to 32767)
      joyReport.axis[0] = map(analogRead(A0), 0, 1023, -32768,32767 );
      joyReport.axis[1] = map(analogRead(A1), 0, 1023, -32768,32767 );
      joyReport.axis[2] = map(analogRead(A2), 0, 1023, -32768,32767 );
      joyReport.axis[3] = map(analogRead(A3), 0, 1023, -32768,32767 );
      joyReport.axis[4] = 0;
      joyReport.axis[5] = 0;

      -------------------------------------------------------------

      //Pinos analogicos 4 e 5
      analogicPinAsButton(A4,13);
      analogicPinAsButton(A5,14);

      2. Make sure the pins used in your code but not wired up are properly terminated (ground or pull up)

      Fail to do so will cause these pins to give out random signal and causes chaotic results (which may explain why the correct buttons are lit but then they go manic.

      Delete
  9. Hiya Wei-Hsiung. Firstly I hope your Father is OK. I have finally got this to work. I must of changed one digit when stripping out the code. Where bytesVal |= (bitVal << ((8) - i)); .. Should be bytesVal |= (bitVal << ((7) - i));
    My next step is to put back in the code removed for the UNO digital pins and then add another 74HC165 daisy chained, but I think the hard part is now done. I really have appreciated your input. I will post the full working code once tidied up.

    Many thanks

    Les

    ReplyDelete
    Replies
    1. My Father is doing well. Thank you for your concern. I am happy that you've solved the problem. Congratulations!! :-)

      Delete