diff --git a/platform/drivers/GPIO/gpio_shiftReg.c b/platform/drivers/GPIO/gpio_shiftReg.c new file mode 100644 index 00000000..e6946da5 --- /dev/null +++ b/platform/drivers/GPIO/gpio_shiftReg.c @@ -0,0 +1,115 @@ +/*************************************************************************** + * Copyright (C) 2024 by Silvano Seva IU2KWO * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 3 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, see * + ***************************************************************************/ + +#include +#include +#include +#include +#include +#include "gpio_shiftReg.h" + +void gpioShiftReg_init(const struct gpioDev *dev) +{ + const struct gpioShiftRegPriv *cfg = (const struct gpioShiftRegPriv *) dev->priv; + const size_t nBytes = (cfg->numOutputs + 8 - 1) / 8; + + // Setup strobe line + gpioDev_setMode(cfg->strobe.port, cfg->strobe.pin, OUTPUT); + gpioDev_clear(cfg->strobe.port, cfg->strobe.pin); + + // Clear all outputs + memset(cfg->outData, 0x00, nBytes); + spi_send(cfg->spi, cfg->outData, nBytes); + gpioDev_set(cfg->strobe.port, cfg->strobe.pin); +} + +void gpioShiftReg_terminate(const struct gpioDev *dev) +{ + (void) dev; +} + +static int gpioShiftReg_mode(const struct gpioDev *dev, const uint8_t pin, + const uint16_t mode) +{ + (void) dev; + (void) pin; + (void) mode; + + return -ENOTSUP; +} + +static void gpioShiftReg_set(const struct gpioDev *dev, const uint8_t pin) +{ + const struct gpioShiftRegPriv *cfg = (const struct gpioShiftRegPriv *) dev->priv; + const size_t nBytes = (cfg->numOutputs + 8 - 1) / 8; + const size_t byte = (cfg->numOutputs - 1 - pin) / 8; + const size_t bit = pin % 8; + + if(pin > cfg->numOutputs) + return; + + __disable_irq(); + + cfg->outData[byte] |= (1 << bit); + + gpioDev_clear(cfg->strobe.port, cfg->strobe.pin); + spi_send(cfg->spi, cfg->outData, nBytes); + gpioDev_set(cfg->strobe.port, cfg->strobe.pin); + + __enable_irq(); +} + +static void gpioShiftReg_clear(const struct gpioDev *dev, const uint8_t pin) +{ + const struct gpioShiftRegPriv *cfg = (const struct gpioShiftRegPriv *) dev->priv; + const size_t nBytes = (cfg->numOutputs + 8 - 1) / 8; + const size_t byte = (cfg->numOutputs - 1 - pin) / 8; + const size_t bit = pin % 8; + + if(pin > cfg->numOutputs) + return; + + __disable_irq(); + + cfg->outData[byte] &= ~(1 << bit); + + gpioDev_clear(cfg->strobe.port, cfg->strobe.pin); + spi_send(cfg->spi, cfg->outData, nBytes); + gpioDev_set(cfg->strobe.port, cfg->strobe.pin); + + __enable_irq(); +} + +static bool gpioShiftReg_read(const struct gpioDev *dev, const uint8_t pin) +{ + const struct gpioShiftRegPriv *cfg = (const struct gpioShiftRegPriv *) dev->priv; + const size_t byte = (cfg->numOutputs - 1 - pin) / 8; + const size_t bit = pin % 8; + + if(pin > cfg->numOutputs) + return false; + + return ((cfg->outData[byte] & (1 << bit)) != 0) ? true : false; +} + +const struct gpioApi gpioShiftReg_ops = +{ + .mode = &gpioShiftReg_mode, + .set = &gpioShiftReg_set, + .clear = &gpioShiftReg_clear, + .read = &gpioShiftReg_read +}; diff --git a/platform/drivers/GPIO/gpio_shiftReg.h b/platform/drivers/GPIO/gpio_shiftReg.h new file mode 100644 index 00000000..2622a1b6 --- /dev/null +++ b/platform/drivers/GPIO/gpio_shiftReg.h @@ -0,0 +1,85 @@ +/*************************************************************************** + * Copyright (C) 2024 by Silvano Seva IU2KWO * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 3 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, see * + ***************************************************************************/ + +#ifndef GPIO_SHIFTREG_H +#define GPIO_SHIFTREG_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Driver to use a chain of shift registers (e.g. 74HC595) as GPIO outputs. + * It requires an SPI interface, of which only the MOSI and SCK lines are used + * and an MCU gpio for shift register strobe signal. + */ + +/** + * Private data of shift register gpio driver. + */ +struct gpioShiftRegPriv +{ + const struct spiDevice *spi; ///< SPI interface device driver + const struct gpioPin strobe; ///< Gpio for output strobe line + const size_t numOutputs; ///< Number of actual outputs + uint8_t *const outData; ///< Pointer to gpio state storage +}; + +/** + * Instantiate a shift register gpio device driver. + * + * @param name: device name. + * @param spiDev: pointer to device driver managing the SPI bus. + * @param strb: gpioPin struct for strobe line management. + * @param numOuts: number of gpio outputs. + */ +#define GPIO_SHIFTREG_DEVICE_DEFINE(name, spiDev, strb, numOuts) \ +extern const struct gpioApi gpioShiftReg_ops; \ +static uint8_t srData_##name[(numOuts + 8 - 1) / 8]; \ +static const struct gpioShiftRegPriv srGpioPriv_##name = \ +{ \ + .spi = spiDev, \ + .strobe = strb, \ + .numOutputs = numOuts, \ + .outData = srData_##name \ +}; \ +const struct gpioDev name = \ +{ \ + .api = &gpioShiftReg_ops, \ + .priv = &srGpioPriv_##name \ +}; + +/** + * Initialize the GPIO shift register driver. + */ +void gpioShiftReg_init(const struct gpioDev *dev); + +/** + * Shut down the GPIO shift register driver. Shift register outputs keep the last + * assigned state. + */ +void gpioShiftReg_terminate(const struct gpioDev *dev); + +#ifdef __cplusplus +} +#endif + +#endif /* GPIO_SHIFTREG_H */