Skip to content

Instantly share code, notes, and snippets.

@jef-sure
Created May 20, 2025 16:21
Show Gist options
  • Save jef-sure/f5a4c72662b4f0ce9859d43eaa226346 to your computer and use it in GitHub Desktop.
Save jef-sure/f5a4c72662b4f0ce9859d43eaa226346 to your computer and use it in GitHub Desktop.
#include "spi_shiftout.h"
#include "driver/gpio.h"
#include "esp_log.h"
#include <stdlib.h>
#include <string.h>
static const char *TAG = "spi_shiftout";
spi_shiftout_t *spi_shiftout_init(int32_t frequency, gpio_num_t data_pin, gpio_num_t clock_pin, gpio_num_t latch_pin,
gpio_num_t output_enable_pin, spi_host_device_t host, uint8_t shift_out_length)
{
spi_shiftout_t *cfg = (spi_shiftout_t *)calloc(1, sizeof(spi_shiftout_t) + shift_out_length - 1);
if (!cfg) {
ESP_LOGE(TAG, "Failed to allocate memory for spi_shiftout_t");
return NULL;
}
cfg->latch_pin = latch_pin;
cfg->output_enable_pin = output_enable_pin;
cfg->output_enable_value = 255; // Default value for output enable
cfg->shift_out_length = shift_out_length;
cfg->ledc_channel = LEDC_CHANNEL_0; // or another available channel
cfg->ledc_timer = LEDC_TIMER_0; // or another available timer
if (output_enable_pin >= 0) {
// Configure OE pin for PWM (LEDC)
ledc_timer_config_t ledc_timer = {
.speed_mode = LEDC_LOW_SPEED_MODE, //
.timer_num = cfg->ledc_timer, //
.duty_resolution = LEDC_TIMER_8_BIT, //
.freq_hz = 8000, // TODO: test this value
.clk_cfg = LEDC_AUTO_CLK //
};
ledc_timer_config(&ledc_timer);
ledc_channel_config_t ledc_channel = {
.gpio_num = cfg->output_enable_pin, //
.speed_mode = LEDC_LOW_SPEED_MODE, //
.channel = cfg->ledc_channel, //
.timer_sel = cfg->ledc_timer, //
.duty = cfg->output_enable_value, // 100% duty (fully enabled)
.hpoint = 0, //
.flags.output_invert = 0 //
};
ledc_channel_config(&ledc_channel);
}
// Configure the SPI bus
spi_bus_config_t buscfg = {
.miso_io_num = -1, //
.mosi_io_num = data_pin, //
.sclk_io_num = clock_pin, //
.quadwp_io_num = -1, //
.quadhd_io_num = -1, //
.max_transfer_sz = shift_out_length * 8, //
.flags = 0 //
};
spi_device_interface_config_t devcfg = {
.clock_speed_hz = frequency, // Clock out at 2 MHz
.mode = 0, // SPI mode 0
.spics_io_num = -1, // CS pin
.queue_size = 1, //
.pre_cb = 0, //
};
esp_err_t ret;
// Initialize the SPI bus
ret = spi_bus_initialize(host, &buscfg, SPI_DMA_CH_AUTO);
ESP_ERROR_CHECK(ret);
ret = spi_bus_add_device(host, &devcfg, &cfg->spi);
ESP_ERROR_CHECK(ret);
gpio_config_t latch_conf = {
.pin_bit_mask = (1ULL << latch_pin), //
.mode = GPIO_MODE_OUTPUT, //
.pull_up_en = GPIO_PULLUP_ENABLE, //
.pull_down_en = GPIO_PULLDOWN_DISABLE, //
.intr_type = GPIO_INTR_DISABLE //
};
gpio_config(&latch_conf);
gpio_set_level(latch_pin, 1);
return cfg;
}
void spi_shiftout_write(spi_shiftout_t *cfg, uint8_t oe_val)
{
esp_err_t ret;
spi_transaction_t t;
gpio_set_level(cfg->latch_pin, 0);
memset(&t, 0, sizeof(t)); // Zero out the transaction
t.length = 8 * cfg->shift_out_length; // Command is 8 bits
t.tx_buffer = cfg->shift_out; // Data
ret = spi_device_polling_transmit(cfg->spi, &t); // Transmit!
gpio_set_level(cfg->latch_pin, 1);
assert(ret == ESP_OK); // Should have had no issues.
if (cfg->output_enable_pin >= 0 && oe_val != cfg->output_enable_value) {
cfg->output_enable_value = oe_val;
ledc_set_duty(LEDC_LOW_SPEED_MODE, cfg->ledc_channel, oe_val);
ledc_update_duty(LEDC_LOW_SPEED_MODE, cfg->ledc_channel);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment