File syn_soft_spi.c¶
File List > drivers > syn_soft_spi.c
Go to the documentation of this file
#if __has_include("syn_config.h")
#include "syn_config.h"
#endif
#if !defined(SYN_USE_SOFT_SPI) || SYN_USE_SOFT_SPI
#include "../util/syn_assert.h"
#include "syn_gpio.h"
#include "syn_soft_spi.h"
static void spi_delay(const SYN_SoftSPI *spi)
{
for (volatile uint32_t i = 0; i < spi->delay_loops; i++) {
// NOP loop
}
}
void syn_soft_spi_init(SYN_SoftSPI *spi, SYN_GPIO_Pin sck, SYN_GPIO_Pin mosi, SYN_GPIO_Pin miso,
SYN_SPIMode mode, uint32_t delay_loops)
{
SYN_ASSERT(spi != NULL);
spi->sck = sck;
spi->mosi = mosi;
spi->miso = miso;
spi->mode = mode;
spi->delay_loops = delay_loops;
syn_gpio_init(spi->sck, SYN_GPIO_OUTPUT);
syn_gpio_init(spi->mosi, SYN_GPIO_OUTPUT);
syn_gpio_init(spi->miso, SYN_GPIO_INPUT);
bool cpol = (spi->mode == SYN_SPI_MODE_2 || spi->mode == SYN_SPI_MODE_3);
spi->cpha = (spi->mode == SYN_SPI_MODE_1 || spi->mode == SYN_SPI_MODE_3);
spi->idle_state = cpol ? SYN_GPIO_HIGH : SYN_GPIO_LOW;
spi->active_state = cpol ? SYN_GPIO_LOW : SYN_GPIO_HIGH;
// Set idle clock state based on CPOL
syn_gpio_write(spi->sck, spi->idle_state);
syn_gpio_write(spi->mosi, SYN_GPIO_LOW);
spi->cs_pin = (SYN_GPIO_Pin)-1; /* No CS by default */
}
uint8_t syn_soft_spi_transfer(const SYN_SoftSPI *spi, uint8_t data)
{
uint8_t rx_data = 0;
SYN_GPIO_State idle = spi->idle_state;
SYN_GPIO_State active = spi->active_state;
for (uint8_t mask = 0x80; mask != 0; mask >>= 1) {
if (!spi->cpha) {
// Mode 0 or 2: Set data on idle-to-active edge
syn_gpio_write(spi->mosi, (data & mask) ? SYN_GPIO_HIGH : SYN_GPIO_LOW);
spi_delay(spi);
syn_gpio_write(spi->sck, active);
spi_delay(spi);
if (syn_gpio_read(spi->miso) == SYN_GPIO_HIGH) {
rx_data |= mask;
}
syn_gpio_write(spi->sck, idle);
spi_delay(spi);
} else {
// Mode 1 or 3: Set data on active-to-idle edge
syn_gpio_write(spi->sck, active);
syn_gpio_write(spi->mosi, (data & mask) ? SYN_GPIO_HIGH : SYN_GPIO_LOW);
spi_delay(spi);
syn_gpio_write(spi->sck, idle);
spi_delay(spi);
if (syn_gpio_read(spi->miso) == SYN_GPIO_HIGH) {
rx_data |= mask;
}
spi_delay(spi);
}
}
return rx_data;
}
void syn_soft_spi_transfer_bulk(SYN_SoftSPI *spi, const uint8_t *tx, uint8_t *rx, size_t len)
{
SYN_ASSERT(spi != NULL);
SYN_ASSERT(tx != NULL || rx != NULL);
for (size_t i = 0; i < len; i++) {
uint8_t tx_byte = tx ? tx[i] : 0xFF;
uint8_t rx_byte = syn_soft_spi_transfer(spi, tx_byte);
if (rx) {
rx[i] = rx_byte;
}
}
}
void syn_soft_spi_set_cs(SYN_SoftSPI *spi, SYN_GPIO_Pin cs_pin, bool active_low)
{
SYN_ASSERT(spi != NULL);
spi->cs_pin = cs_pin;
spi->cs_active_low = active_low;
syn_gpio_init(cs_pin, SYN_GPIO_OUTPUT);
/* Start deasserted */
syn_gpio_write(cs_pin, active_low ? SYN_GPIO_HIGH : SYN_GPIO_LOW);
}
void syn_soft_spi_select(SYN_SoftSPI *spi)
{
SYN_ASSERT(spi != NULL);
if (spi->cs_pin == (SYN_GPIO_Pin)-1)
return;
syn_gpio_write(spi->cs_pin, spi->cs_active_low ? SYN_GPIO_LOW : SYN_GPIO_HIGH);
}
void syn_soft_spi_deselect(SYN_SoftSPI *spi)
{
SYN_ASSERT(spi != NULL);
if (spi->cs_pin == (SYN_GPIO_Pin)-1)
return;
syn_gpio_write(spi->cs_pin, spi->cs_active_low ? SYN_GPIO_HIGH : SYN_GPIO_LOW);
}
#endif /* SYN_USE_SOFT_SPI */