diff --git a/stm-firmware/include/stm-periph/spi.h b/stm-firmware/include/stm-periph/spi.h index d7541f3..a2655ce 100644 --- a/stm-firmware/include/stm-periph/spi.h +++ b/stm-firmware/include/stm-periph/spi.h @@ -21,4 +21,42 @@ #ifndef __SPI_H__ #define __SPI_H__ +#include +#include + +enum stm_spi_prescaler { + SPI_PRSC_DIV2 = 0, + SPI_PRSC_DIV4 = 1, + SPI_PRSC_DIV8 = 2, + SPI_PRSC_DIV16 = 3, + SPI_PRSC_DIV32 = 4, + SPI_PRSC_DIV64 = 5, + SPI_PRSC_DIV128 = 6, + SPI_PRSC_DIV256 = 7, +}; + +struct stm_spi_settings { + void (*cs_activate)(void); + void (*cs_deactivate)(void); + bool cpol; + bool cpha; + bool master; + bool msb_first; + enum stm_spi_prescaler prescaler; +}; + +struct stm_spi_dev { + uint32_t magic; + SPI_TypeDef *spi_regs; + struct stm_spi_settings settings; +}; + +typedef void * stm_spi_handle; + +stm_spi_handle spi_init(struct stm_spi_dev *spi_dev_struct, SPI_TypeDef *spi_regs, const struct stm_spi_settings *settings); + +void spi_deinit(stm_spi_handle handle); + +int spi_transfer(stm_spi_handle handle, const uint8_t *tx, uint8_t *rx, uint32_t count, bool handle_cs); + #endif /* __SPI_H__ */ diff --git a/stm-firmware/stm-periph/spi.c b/stm-firmware/stm-periph/spi.c index 2143c25..881004e 100644 --- a/stm-firmware/stm-periph/spi.c +++ b/stm-firmware/stm-periph/spi.c @@ -19,4 +19,146 @@ */ #include -#include +#include +#include +#include + +#define STM_SPI_DEV_MAGIC 0x5A678F5CUL + +struct spi_rcc_lookup_pair { + SPI_TypeDef *spi_regs; + volatile uint32_t *rcc_reg; + uint32_t bit_no; +}; + +const struct spi_rcc_lookup_pair rcc_lookup[3] = { + {SPI1, &RCC->APB2ENR, BITMASK_TO_BITNO(RCC_APB2ENR_SPI1EN)}, + {SPI2, &RCC->APB1ENR, BITMASK_TO_BITNO(RCC_APB1ENR_SPI2EN)}, + {SPI3, &RCC->APB1ENR, BITMASK_TO_BITNO(RCC_APB1ENR_SPI3EN)}, +}; + +static const struct spi_rcc_lookup_pair *spi_find_rcc_reg_and_bit(SPI_TypeDef *spi_regs) +{ + uint32_t i; + const struct spi_rcc_lookup_pair *ret = NULL; + + for (i = 0; i < COUNT_OF(rcc_lookup); i++) { + if (rcc_lookup[i].spi_regs == spi_regs) { + ret = &rcc_lookup[i]; + break; + } + } + + return ret; +} + +static struct stm_spi_dev *spi_handle_to_struct(stm_spi_handle handle) +{ + struct stm_spi_dev *dev; + + dev = (struct stm_spi_dev *)handle; + if (dev == NULL) + return NULL; + if (dev->magic != STM_SPI_DEV_MAGIC) + return NULL; + + return dev; +} + +stm_spi_handle spi_init(struct stm_spi_dev *spi_dev_struct, SPI_TypeDef *spi_regs, const struct stm_spi_settings *settings) +{ + stm_spi_handle ret_handle = NULL; + uint32_t reg_val; + const struct spi_rcc_lookup_pair *rcc_pair; + + if (!spi_dev_struct || !spi_regs || !settings) + goto exit; + if (!settings->cs_activate || !settings->cs_deactivate) + goto exit; + + rcc_pair = spi_find_rcc_reg_and_bit(spi_regs); + if (!rcc_pair) + goto exit; + + memcpy(&spi_dev_struct->settings, settings, sizeof(struct stm_spi_settings)); + spi_dev_struct->spi_regs = spi_regs; + + rcc_manager_enable_clock(rcc_pair->rcc_reg, rcc_pair->bit_no); + + reg_val = 0; + if (settings->cpha) + reg_val |= SPI_CR1_CPHA; + if (settings->cpol) + reg_val |= SPI_CR1_CPOL; + if (settings->master) + reg_val |= SPI_CR1_MSTR; + reg_val |= (settings->prescaler & 0x7) << 3; + reg_val |= SPI_CR1_SPE | SPI_CR1_SSI | SPI_CR1_SSM; + + if (!settings->msb_first) + reg_val |= SPI_CR1_LSBFIRST; + + spi_regs->CR2 = 0UL; + spi_regs->CR1 = reg_val; + + ret_handle = spi_dev_struct; + + spi_dev_struct->settings.cs_deactivate(); + +exit: + return ret_handle; +} + +void spi_deinit(stm_spi_handle handle) +{ + const struct spi_rcc_lookup_pair *rcc_pair; + struct stm_spi_dev *dev; + + dev = spi_handle_to_struct(handle); + if (!dev) + return; + + dev->spi_regs->CR1 = 0; + dev->spi_regs->CR2 = 0; + + rcc_pair = spi_find_rcc_reg_and_bit(dev->spi_regs); + if (!rcc_pair) + rcc_manager_disable_clock(rcc_pair->rcc_reg, rcc_pair->bit_no); +} + +static uint8_t transfer_byte(uint8_t byte, struct stm_spi_dev *dev) +{ + while (dev->spi_regs->SR & SPI_SR_BSY); + dev->spi_regs->DR = (uint16_t)byte; + __DSB(); + while((dev->spi_regs->SR & SPI_SR_BSY) || !(dev->spi_regs->SR & SPI_SR_TXE)); + + return (uint8_t)dev->spi_regs->DR; +} + +int spi_transfer(stm_spi_handle handle, const uint8_t *tx, uint8_t *rx, uint32_t count, bool handle_cs) +{ + struct stm_spi_dev *dev; + uint8_t tx_byte; + uint8_t rx_byte; + uint32_t idx; + + dev = spi_handle_to_struct(handle); + if (!dev) + return -1001; + + if (handle_cs) + dev->settings.cs_activate(); + + for (idx = 0; idx < count; idx++) { + tx_byte = (tx ? tx[idx] : 0U); + rx_byte = transfer_byte(tx_byte, dev); + if (rx) + rx[idx] = rx_byte; + } + + if (handle_cs) + dev->settings.cs_deactivate(); + + return 0; +}