velxio/test/test_custom_chips/sdk/examples/sn74hc595.c

98 lines
3.1 KiB
C

/*
* sn74hc595.c — 8-bit SPI shift register with output latches.
*
* Pins: SER (data in, MOSI), SRCLK (shift clock, SCK), RCLK (latch / CS),
* SRCLR (active-low clear), OE (output enable), QH' (cascade out, MISO),
* Q0..Q7 (parallel outputs).
*
* Wired as an SPI slave: SER=MOSI, SRCLK=SCK, RCLK acts as our chip-select
* (rising edge latches the shift register to the output stage).
*
* Behavior:
* - On every SPI byte, 8 bits shift into the internal register.
* - On RCLK rising edge, the register copies to Q0..Q7.
* - SRCLR LOW resets the register and outputs to 0.
* - QH' echoes the bit being shifted out the high end (cascade).
*/
#include "velxio-chip.h"
#include <stdlib.h>
#include <string.h>
static const char* Q_NAMES[8] = {"Q0","Q1","Q2","Q3","Q4","Q5","Q6","Q7"};
typedef struct {
vx_pin SER, SRCLK, RCLK, SRCLR, OE, QH;
vx_pin Q[8];
vx_spi spi;
uint8_t spi_buf[1];
uint8_t shift_reg; /* shift register (volatile) */
uint8_t latch_reg; /* latched outputs */
} chip_state_t;
static void update_outputs(chip_state_t* s) {
for (int i = 0; i < 8; i++) {
vx_pin_write(s->Q[i], (s->latch_reg >> i) & 1 ? VX_HIGH : VX_LOW);
}
}
static void on_spi_done(void* ud, uint8_t* buffer, uint32_t count) {
chip_state_t* s = (chip_state_t*)ud;
/* Each received byte was shifted MSB-first; the resulting register equals
* the last byte received (8-bit register, no cascade-in). */
if (count > 0) {
s->shift_reg = buffer[0];
}
/* Re-arm the SPI for the next byte (real 74HC595 has no CS — it shifts on
* every SCK edge as long as data flows). */
vx_spi_start(s->spi, s->spi_buf, 1);
}
static void on_rclk(void* ud, vx_pin pin, int value) {
chip_state_t* s = (chip_state_t*)ud;
/* RCLK rising edge → latch shift register to outputs. */
s->latch_reg = s->shift_reg;
update_outputs(s);
}
static void on_srclr(void* ud, vx_pin pin, int value) {
chip_state_t* s = (chip_state_t*)ud;
/* SRCLR is active LOW: when it goes LOW, clear the shift register.
* Outputs only update on the next RCLK rising edge. */
if (value == VX_LOW) {
s->shift_reg = 0;
}
}
void chip_setup(void) {
chip_state_t* s = (chip_state_t*)calloc(1, sizeof(chip_state_t));
s->SER = vx_pin_register("SER", VX_INPUT);
s->SRCLK = vx_pin_register("SRCLK", VX_INPUT);
s->RCLK = vx_pin_register("RCLK", VX_INPUT);
s->SRCLR = vx_pin_register("SRCLR", VX_INPUT_PULLUP);
s->OE = vx_pin_register("OE", VX_INPUT);
s->QH = vx_pin_register("QH", VX_OUTPUT_LOW);
for (int i = 0; i < 8; i++) {
s->Q[i] = vx_pin_register(Q_NAMES[i], VX_OUTPUT_LOW);
}
vx_spi_config cfg = {
.sck = s->SRCLK,
.mosi = s->SER,
.miso = s->QH,
.cs = s->RCLK, /* runtime ignores cs, chip drives via RCLK watch */
.mode = 0,
.on_done = on_spi_done,
.user_data = s,
};
s->spi = vx_spi_attach(&cfg);
/* Tell the host we want to receive 1 byte at a time on the SPI bus. */
vx_spi_start(s->spi, s->spi_buf, 1);
vx_pin_watch(s->RCLK, VX_EDGE_RISING, on_rclk, s);
vx_pin_watch(s->SRCLR, VX_EDGE_FALLING, on_srclr, s);
vx_log("74HC595 shift register ready");
}