Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 13 of 13

Thread: SN74HC165 shift register revisited

  1. #1
    Senior Member
    Join Date
    Jan 2017
    Location
    Maastricht
    Posts
    152

    SN74HC165 shift register revisited

    The SN74HC165 shift register makes it easy to add digital input to a Teensy/Arduino. On the Arduino playground a description can be found: SN74HC165N, a Sparkfun tutorial relies on the same approach.
    It always seemed strange to me that shifting in would require 4 wires, whereas shifting out can make do with three. Turns out that you only need three wires, the clock inhibit is superfluous. The tutorial mentioned refer to the timing diagram:

    Click image for larger version. 

Name:	sequence.png 
Views:	202 
Size:	67.8 KB 
ID:	12398

    The way I interpret the datasheet this diagram refers to the case where there's a continuously running clock, not when you have full control over the clock. Also in the datasheet is an example of a typical application with a microcontroller:

    Click image for larger version. 

Name:	typicalApplication.png 
Views:	200 
Size:	76.0 KB 
ID:	12399

    In this example the clock inhibit (pin 15) is connected to ground and just three wires are used.

    The tutorials also make use of delay() statements for timing purposes.

    Code:
    /* Width of pulse to trigger the shift register to read and latch.
    */
    #define PULSE_WIDTH_USEC   5
    
    
    BYTES_VAL_T read_shift_regs()
    {
        long bitVal;
        BYTES_VAL_T bytesVal = 0;
    
        /* Trigger a parallel Load to latch the state of the data lines,
        */
        digitalWrite(clockEnablePin, HIGH);
        digitalWrite(ploadPin, LOW);
        delayMicroseconds(PULSE_WIDTH_USEC);
        digitalWrite(ploadPin, HIGH);
        digitalWrite(clockEnablePin, LOW);
    
        /* Loop to read each bit value from the serial out line
         * of the SN74HC165N.
        */
        for(int i = 0; i < DATA_WIDTH; i++)
        {
            bitVal = digitalRead(dataPin);
    
            /* Set the corresponding bit in bytesVal.
            */
            bytesVal |= (bitVal << ((DATA_WIDTH-1) - i));
    
            /* Pulse the Clock (rising edge shifts the next bit).
            */
            digitalWrite(clockPin, HIGH);
            delayMicroseconds(PULSE_WIDTH_USEC);
            digitalWrite(clockPin, LOW);
        }
    
        return(bytesVal);
    }
    Using an oscilloscope I performed some tests to see whether the delay is actually required. The test setup (alle delay statements removed and clock inhibit connected to ground) is shown in the following picture:

    Click image for larger version. 

Name:	shift-test-setup.jpg 
Views:	123 
Size:	158.7 KB 
ID:	12400

    The stripboard contains four 74HC165 shift registers for the buttons and three 74HC595 shift registers for the leds, the other buttons are on two separate boards. Alle the IC sockets have 100nF decoupling caps build in. A Teensy 3.6 is mounted on a Tall Dog extension board, it is mounted on its side for easy access to all the pins.
    I'm no electrical engineer but this looks like a decent clock pulse to me. Here's a screenprint showing the clock measured at the last IC in the chain:

    Click image for larger version. 

Name:	shift-wave-last.png 
Views:	74 
Size:	28.5 KB 
ID:	12401

    The clock frequency is just over 2MHz, pulse width is about 175ns. From the datasheet:

    Click image for larger version. 

Name:	timing.png 
Views:	77 
Size:	65.3 KB 
ID:	12402

    Worst case is a maximum of 6MHz and a minimum pulse width of 80ns so we're well in the clear. No need for any delays in the code.

    The code running in the test:
    Code:
    unsigned long readShiftRegisters()
    {
        int bitVal;
        unsigned long bytesVal = 0;
        // Trigger a parallel Load to latch the state of the data lines
        digitalWrite(ploadPin, LOW);
        digitalWrite(ploadPin, HIGH);
        // Loop to read each bit value from the serial out line of the SN74HC165N.
        for(int i = 0; i < DATA_WIDTH; i++)
        {
            bitVal = digitalRead(dataPin);
            // Set the corresponding bit in bytesVal.
            bytesVal |= (bitVal << ((DATA_WIDTH-1) - i));
            // Pulse the Clock (rising edge shifts the next bit).
            digitalWrite(clockPin, HIGH);
            digitalWrite(clockPin, LOW);
        }
        return(bytesVal);
    }

    Kind regards,

    Gerrit

  2. #2
    Senior Member+ Theremingenieur's Avatar
    Join Date
    Feb 2014
    Location
    Colmar, France
    Posts
    2,296
    What you are doing looks basically like a bit-banging emulation of a slow ancestor of the SPI bus. Great to see that it works!

  3. #3
    Senior Member
    Join Date
    Jan 2017
    Location
    Maastricht
    Posts
    152
    Quote Originally Posted by Theremingenieur View Post
    What you are doing looks basically like a bit-banging emulation of a slow ancestor of the SPI bus. Great to see that it works!
    I had a look at the SPI library documentation, it seems this is intended for more specialised applications. Using shift registers this way is really easy as there is no protocol overhead and all buttons can be debounced simultaneously.
    While browsing the libraries I found a similar case of clock ringing mentioned in the OctoWS2811 documentation so I thought I'd try a putting a series resistor in the clock line. I started with 220Ω and and then tried 100Ω. The resistor is mounted on the cable connector at the Teensy side.

    Click image for larger version. 

Name:	clock-100ohm-220ohm.jpg 
Views:	59 
Size:	57.3 KB 
ID:	12423

    220Ω is a bit too much, the corners are rounded off and the rise time increases, 100Ω is just fine. The little blips in front of the first pulse are the result of crosstalk from the parallel load line, adding a series resistor cleans it up a little. Here's a before-after comparison of the clock signal without any series resistors and with 100Ω series resistors in the clock an parallel load lines:

    Click image for larger version. 

Name:	shift-wave-before-after.jpg 
Views:	57 
Size:	58.8 KB 
ID:	12424

    I'm quite satisfied with this result. A nice, clean clock signal and 32 buttons scanned in about 15Ás.

    Kind regards,

    Gerrit

  4. #4
    Senior Member
    Join Date
    Jan 2017
    Location
    Maastricht
    Posts
    152
    The finished button panel:

    Click image for larger version. 

Name:	button-panel-board.jpg 
Views:	87 
Size:	156.1 KB 
ID:	12461

    Real old school wiring, it looks a little messy but remember that with shift registers the high frequency clock and data signals are confined to the ICs so there's no risk of RF interference. This is about as far as I'd go without designing a PCB but at least when using strip board like this it is easy to get exact dimensions for a front panel design by counting holes. Mounted on the front panel it look like this:

    Click image for larger version. 

Name:	button-panel.jpg 
Views:	82 
Size:	112.2 KB 
ID:	12462

    The board is mounted on 8 M3 threaded standoffs on the back of the frontpanel, fortunately the measurements were correct so all the buttons are perfectly aligned In case you're wondering, this is part of a DAW (Digital Audio Workstation) plugin controller project I'm working on.

    Kind regards,

    Gerrit

  5. #5
    Senior Member+ KurtE's Avatar
    Join Date
    Jan 2014
    Posts
    4,202
    Looks good!

    Wonder if you could use the Arduino function shiftin (https://www.arduino.cc/reference/en/...ed-io/shiftin/)
    To do each byte... But since you already have it working...

  6. #6
    Senior Member
    Join Date
    Jan 2017
    Location
    Maastricht
    Posts
    152
    Quote Originally Posted by KurtE View Post
    Looks good!

    Wonder if you could use the Arduino function shiftin (https://www.arduino.cc/reference/en/...ed-io/shiftin/)
    To do each byte... But since you already have it working...
    Thanks.

    I thought about using shiftIn() but, as you said, it's working now and there's still a lot to do. When my project is finished I'll take another look at this, it could well be that shiftIn() is faster.
    In the meantime I've got to the point where all the buttons and leds are operational with the DAW software (Logic Pro X).

    Kind regards,

    Gerrit

  7. #7
    I would like to know where you got the switches with leds in them? Part number and supplier?

    Thanks.

  8. #8
    Senior Member
    Join Date
    Jan 2017
    Location
    Maastricht
    Posts
    152
    Quote Originally Posted by mark.winger View Post
    I would like to know where you got the switches with leds in them? Part number and supplier?

    Thanks.
    The switches are from the C&K Digitast series. I can't make heads or tails of the pricing which varies a lot per supplier and type. I got them from RS Components and Mouser, here's a link to the black ones with the green leds

    Here's the datasheet: DIGITAST.pdf

    These switches have a very nice feel and precise pressure point, I selected the wide version because they're almost exactly the width of a finger. I also have some no name imitations but they're definitely not as refined as the C&K.

    Kind regards,

    Gerrit

  9. #9
    Member
    Join Date
    Sep 2014
    Location
    South Dundas, Ontario, Canada
    Posts
    27
    I'm interested in how you made/cut the panel for the switches and the labelling. I often don't get past doing "a proof of concept" because of a lack finishing details and equipment. Thanks.

    Peter.

  10. #10
    Senior Member
    Join Date
    Jan 2017
    Location
    Maastricht
    Posts
    152
    Quote Originally Posted by KOC62 View Post
    I'm interested in how you made/cut the panel for the switches and the labelling. I often don't get past doing "a proof of concept" because of a lack finishing details and equipment. Thanks.

    Peter.
    The panel was made by Schaeffer using their free front panel designer software. It's not cheap but the result looks great and you can check the price at anytime in the design software, when finished the panel can be ordered from within the application.

    Kind regards,

    Gerrit

  11. #11
    Senior Member oddson's Avatar
    Join Date
    Feb 2013
    Location
    Isle in the Salish Sea
    Posts
    1,052
    It really is a beautiful build.

    But why a shift-register instead of a mux or GIPO expander? Seems like a lot of effort was expended to get this working, presumably not to save a few cents on the chips.

    Also, what's with the TRS connectors? Is this thing going to be an audio interface too?

    I hope you'll do a write up of the full project when complete.

  12. #12
    Senior Member
    Join Date
    Jan 2017
    Location
    Maastricht
    Posts
    152
    Quote Originally Posted by oddson View Post
    It really is a beautiful build.

    But why a shift-register instead of a mux or GIPO expander? Seems like a lot of effort was expended to get this working, presumably not to save a few cents on the chips.

    Also, what's with the TRS connectors? Is this thing going to be an audio interface too?

    I hope you'll do a write up of the full project when complete.
    Thanks

    Here's a picture with the display and rotary encoder panel:

    Click image for larger version. 

Name:	Zeus-DPC-Lexicon-front.jpg 
Views:	301 
Size:	90.3 KB 
ID:	13516

    For study I once took apart a Behringer controller and the thing is full of them (and the 595) so I figured I'll do it like this too. I also like the fact that there no multiplexing of the buttons, i.e. high frequency switching all over the pcb.

    Here's the rear view of the display and rotary panel:

    Click image for larger version. 

Name:	Zeus-DPC-rear.jpg 
Views:	67 
Size:	130.3 KB 
ID:	13520

    Level shifters for the encoders and two more shift registers for the encoder buttons.

    The trs jacks are for future expansion with foot pedals and other stuff, I thought I'd put them in now so I don't need to drill holes in the chassis later on. No audio functionality is planned, this is purely a (Mackie control compatible) controller.
    As soon as I find the time to create a little video demonstrating the controller I'll start a thread on the subject.

    Kind regards,

    Gerrit

  13. #13
    Senior Member
    Join Date
    Jan 2017
    Location
    Maastricht
    Posts
    152
    Shifting up...

    The step & pattern sequencer I'm working on has 96 switches so stripboard is definitely not an option anymore. I created schematics and pcb boards for the control board with 32 switches and the step/pattern matrix with 64 switches. In hindsight it probably would have been smarter to invest the time to learn using pcb layout software in stead of building on stripboard but at the time it seemed like stripboard building was simpler but it isn't actually.
    The matrix pcb without the switches:

    Click image for larger version. 

Name:	matrix_pcb.jpg 
Views:	15 
Size:	116.0 KB 
ID:	15567

    The control pcb with mounted switches:

    Click image for larger version. 

Name:	Control_pcb_buttons.jpg 
Views:	11 
Size:	151.6 KB 
ID:	15568

    To prevent any issues due to the length of the cables and the amount of shift registers driven I used 74LS244 buffers on all the outputs and 74LVC244 buffer/level shifters on the inputs.

    Here's a render of the main board with the buffers and the series resistors for driving the cables:

    Click image for larger version. 

Name:	Zeus-SPS-16_main_render.jpg 
Views:	12 
Size:	153.0 KB 
ID:	15571

    The matrix is split into two groups of 32 switches sharing clock and latch but with a separate data line, together with the control board they are processed simultaneously.

    Code:
    void readButtonShiftRegisters(){
      int bitVal;
      unsigned long bytesValControl = 0;
      unsigned long bytesValMatrix1 = 0;
      unsigned long bytesValMatrix2 = 0;
      // Trigger a parallel Load to latch the state of the data lines
      digitalWrite(PLOAD_PIN, LOW);
      digitalWrite(PLOAD_PIN, HIGH);
      // Loop to read each bit value from the serial out line of the SN74HC165N.
      for(int i = 31; i >=0; i--){
        bitVal = digitalReadFast(CONTROL_BUTTON_DATA_PIN);
        // Set the corresponding bit.
        bytesValControl |= bitVal << i;
        newPanelButtonStates[0] =bytesValControl;
        
        bitVal = digitalReadFast(MATRIX1_BUTTON_DATA_PIN);
        bytesValMatrix1 |= bitVal << i;
        newPanelButtonStates[1] =bytesValMatrix1;
        
        bitVal = digitalReadFast(MATRIX2_BUTTON_DATA_PIN);
        bytesValMatrix2 |= bitVal << i;
        newPanelButtonStates[2] =bytesValMatrix2;
        // Pulse the Clock (rising edge shifts the next bit).
        digitalWriteFast(CLOCK_PIN, HIGH);
        digitalWriteFast(CLOCK_PIN, LOW);
      }
    }
    By using digitalReadFast() and digitalWriteFast() the speed can be increased significantly. Reading the state of 96 switches takes just 5Ás, setting the 96 LEDs takes also just 5Ás. The clock signal doesn't like like much but it's all working perfectly. The shift in clock, just over 7MHz, at the last shift register of the matrix board:

    Click image for larger version. 

Name:	Matrix_button_clock.png 
Views:	7 
Size:	33.5 KB 
ID:	15569

    The shift out clock, 10MHz, (74HC595) also at the shift register which is furthest away from the main board:

    Click image for larger version. 

Name:	Matrix_led_clock.png 
Views:	10 
Size:	32.5 KB 
ID:	15570

    There're no glitches with the LEDs or the switches so I guess as long as the criteria (rise time, duration) for the clock and other signals are met the waveform does not matter that much.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •