I had recently sorted out using SPI from the SDK to interface with the 74HC595 in the forum post [SOLVED] Bewildered by SPI and 74HC595 used to drive 8 LED. All is well. So as the next exercise I thought I would use a 74HC165 to read the output of the 74HC595 and work out the clock delay between setting the parallel-out pins on the 595 and reading that information back in though the 165. And things didn't go as expected. The pinout and the shift, load sequences are fairly straight forward.
The problem I'm having is trying to figure out the correct use of SPI_RX, SPI_TX and SPI_CS when working with spi_read_blocking(). The prototype for the function is:
The problem is what is being done with the uint8_t repeated_tx_data parameter and pinout? The very short description in the SDK docs say that 0 should usually work, but some applications like talking to SD cards require 0xff instead. All pins are configured as GPIO_FUNC_SPI (though I have tried SPI_CS as normal gpio out and tried manually pulling SH/LD low before the read - no change. I validate the return from spi_read_blocking() and it always returns 1-byte telling me it did read 8-bits of data (but what that data was was suspect, normally 0 or 255, but with 3, 7, 15, 31, 63, 127, etc.. just basic 255 shifted by x number of bits one way or the other)
As far as the pinout goes, does this mean the SPI_TX pin is connected to the SH/LD pin on the 74165 to handle pulling SH/LD low to capture the inputs? Or does the SPI_CS pin handle pulling SH/LD low before the spi_read_blocking() call read the data shifted out on Qh?
The spi_write_blocking() used with the 74HC595 linked above worked fine that way so I was expecting the treatment of the 165 chip would be similar. The key difference is the 165 isn't a true tri-state chip where the 595 is. For single-chip, non-chained used, that shouldn't matter, but with the Pi Zero 2W using ioctl access to the spi /dev/ filesystem nodes, you do need to use both spi0 and spi1 placing an extra read on spi1 before the read on spi0 to clock the SH/LD low then high so inputs are transferred to the buffer for reading out. I don't believe the pico lib needs this (but I tried it anyway - no change)
The code I'm attempting is shown below. The 165 is read on SPI1. The SPI0 config was there for driving the 74HC595, and also served as the test as was done on the Pi Zero 2W, so it is just boilerplate at this point. The SPI1 code can be configured to us the SPI_CS as GPIO_FUNC_SPI or as a normal gpio depending on whether define CS1nSHLD as it is now.
So my big question is "What triggers the SH/LD pin on the 165 low transferring the inputs to the internal buffer"? Is the purpose of the uint8_t repeated_tx_data intended for you to use the SPI_TX to connect to the 165 SH/LD pin to handle that, or do you connect SPI_CS to that to handle it similar to the 74HC595 setup. Or, do you need to use the SPI_CS pin as a general gpio out pin and manually pull SH/LD low and then high before the read? That's where I'm stuck.
The code I'm currently using along with 2-button to test read of the 74hc165 is:
I've obviously wrapped myself around the axle on this one and need a bit of guidance who has figured out how to read from the 74HC165 using SPI.
The problem I'm having is trying to figure out the correct use of SPI_RX, SPI_TX and SPI_CS when working with spi_read_blocking(). The prototype for the function is:
Code:
int spi_read_blocking (spi_inst_t * spi, uint8_t repeated_tx_data, uint8_t * dst, size_t len)As far as the pinout goes, does this mean the SPI_TX pin is connected to the SH/LD pin on the 74165 to handle pulling SH/LD low to capture the inputs? Or does the SPI_CS pin handle pulling SH/LD low before the spi_read_blocking() call read the data shifted out on Qh?
The spi_write_blocking() used with the 74HC595 linked above worked fine that way so I was expecting the treatment of the 165 chip would be similar. The key difference is the 165 isn't a true tri-state chip where the 595 is. For single-chip, non-chained used, that shouldn't matter, but with the Pi Zero 2W using ioctl access to the spi /dev/ filesystem nodes, you do need to use both spi0 and spi1 placing an extra read on spi1 before the read on spi0 to clock the SH/LD low then high so inputs are transferred to the buffer for reading out. I don't believe the pico lib needs this (but I tried it anyway - no change)
The code I'm attempting is shown below. The 165 is read on SPI1. The SPI0 config was there for driving the 74HC595, and also served as the test as was done on the Pi Zero 2W, so it is just boilerplate at this point. The SPI1 code can be configured to us the SPI_CS as GPIO_FUNC_SPI or as a normal gpio depending on whether define CS1nSHLD as it is now.
So my big question is "What triggers the SH/LD pin on the 165 low transferring the inputs to the internal buffer"? Is the purpose of the uint8_t repeated_tx_data intended for you to use the SPI_TX to connect to the 165 SH/LD pin to handle that, or do you connect SPI_CS to that to handle it similar to the 74HC595 setup. Or, do you need to use the SPI_CS pin as a general gpio out pin and manually pull SH/LD low and then high before the read? That's where I'm stuck.
The code I'm currently using along with 2-button to test read of the 74hc165 is:
Code:
#include <stdio.h>#include <stdint.h>#include <stdlib.h>#include "pico/stdlib.h"#include "hardware/spi.h"#define USERT 1 /* use repeating timer for output to stdout */#define RTDELAY 100 /* stdout updated every 100 ms (10Hz) */ /*------------------------------------------*/#define SPI_PORT spi0 /* 74HC595 pinout: */#define SPI_CLK 2 /* GP2 SRCLK shift-register clock */#define SPI_TX 3 /* GP3 SER serial data */#define SPI_RX 4 /* GP4 SH/LD shift/load latch for read */#define SPI_CS 5 /* GP5 RCLK latch clock */ /* */#define SRCLR_PIN 10 /* GP10 SRCLR clear (pull hi/active low) */#define OE_PIN 11 /* GP11 OE output enable (pull low) */ /* */#define CSnSPIFN 1 /* SPI_CS using GPIO_FUNC_SPI (optional) */ /*------------------------------------------*/#define CS1nSHLD /*------------------------------------------*/#define SPI1_PORT spi1 /* 74HC165 pinout: */#define SPI1_CLK 6 /* GP6 CLK shift-register clock */#define SPI1_TX 7 /* GP7 SH/LD shift/load latch for read */#define SPI1_RX 8 /* GP8 QH serial data out */#define SPI1_CS 9 /* GP9 SH/LD shift/load latch for read */ /*------------------------------------------*/static volatile uint8_t rxval = 0; /* value read from 74hc165 input */#define DELAY 400 /* max delay (ms) betweed RCLK latch for 74HC595 */#define DELAYFIRST 800 /* for first count 0-255 if longer dealy wanted */static volatile uint16_t delay = DELAYFIRST;/* reverse the bits in byte, LSB to MSB or vice versa */uint8_t bitorderswap (uint8_t byte){ byte = ((byte & 0xf0) >> 4) | ((byte & 0x0f) << 4); byte = ((byte & 0xcc) >> 2) | ((byte & 0x33) << 2); return ((byte & 0xaa) >> 1) | ((byte & 0x55) << 1);}/** callback for stdout repeating_timer */bool timer_repeat_ms (struct repeating_timer *rt){ /* output current value and loop deay to stdout (in-place) */ printf (" 74hc165 : %3hhu\033[0K\r", rxval); return true;}/* binary count 0-255 on LED attached as parallel outputs to the 74HC595 * shift-register, in between counts, the LEDs are traversed in sequence from * Qa to Qh illuminatng each LED, the delay for the count which loops in * decreasing manner from 400 ms to as low as 20 ms, is adjusted and the next * count begins. the output LED brightness can be controlled by PWM and the * GP26 ADC input captures the value for LED intensity and the LEDs brightness * is automatically adjusted with max-bright at 0-duty cycle and faint/off for * a 100% duty cycle provided by the SPI_OE (output enabled pin). * * the 74hc165 inputs are attached to each of the 74hc595 outputs to read the * value currently being output. the 74hc165 is driven by spi1. currently it * reads L (0) or H (255) on each read with other bit patterns 7, 15, 31, 63, * 127, 192, 240 and 254 at random intervals (those are 255 left/right shifted) */int main (void) { uint8_t rxfailcnt = 0; unsigned baudrate = 0; int ret = 0; stdio_init_all(); /* set SPI function on SPI_CLK, SPI_TX and optionally SPI_CS for 74hc595 */ gpio_set_function (SPI_CLK, GPIO_FUNC_SPI); gpio_set_function (SPI_TX, GPIO_FUNC_SPI); gpio_set_function (SPI_RX, GPIO_FUNC_SPI); gpio_set_function (SPI_CS, GPIO_FUNC_SPI); /* SPI initialization (56KHz requested) */ baudrate = spi_init (SPI_PORT, 128 * 1000); printf ("\n\nSPI baudrate : %u\033[?25l\n", baudrate); /* set SPI function for SPI CLK, QH and SH/LD for 74hc165 */ gpio_set_function (SPI1_CLK, GPIO_FUNC_SPI); gpio_set_function (SPI1_TX, GPIO_FUNC_SPI); gpio_set_function (SPI1_RX, GPIO_FUNC_SPI);#ifndef CS1nSHLD gpio_set_function (SPI1_CS, GPIO_FUNC_SPI);#else gpio_init (SPI1_CS); gpio_set_dir (SPI1_CS, GPIO_OUT); gpio_put (SPI1_CS, 1);#endif baudrate = spi_init (SPI1_PORT, 128 * 1000); printf ("SPI1 baudrate : %u\033[?25l\n\n", baudrate); /* test read with waveform polarity reversed */ // spi_set_format (SPI1_PORT, 8, 1, 0, SPI_MSB_FIRST); /* add timer_repeat_ms for 5Hz consule text update */ repeating_timer_t rt = { .delay_us = 0 }; void *data = NULL; add_repeating_timer_ms (RTDELAY, timer_repeat_ms, data, &rt); while (1) { uint8_t rxtmp = 0; /* raw value from 74hc165 */ sleep_ms (delay); /* trigger shift/load with spi0 */ // ret = spi_read_blocking (SPI_PORT, 0, &rxtmp, sizeof rxtmp);#ifdef CS1nSHLD gpio_put (SPI1_CS, 0); sleep_ms (2); gpio_put (SPI1_CS, 1); sleep_ms (1);#endif /* read data from 74hc165, validate return */ ret = spi_read_blocking (SPI1_PORT, 0, &rxtmp, sizeof rxtmp); if (ret == sizeof rxtmp) { rxval = rxtmp; // rxval = bitorderswap (rxtmp); /* MSB first to LSB first */ } else { /* handle read error */ rxfailcnt += 1; printf ("\n\033[0Kerror: spi_read_blocking() returned : %d " "(%3hhu)\033[2A\n", ret, rxfailcnt); } }}Statistics: Posted by drankinatty — Mon Aug 04, 2025 8:14 am — Replies 2 — Views 58