diff --git a/src/stm32f4/Kconfig b/src/stm32f4/Kconfig index df2036ab..7edf61b5 100644 --- a/src/stm32f4/Kconfig +++ b/src/stm32f4/Kconfig @@ -7,6 +7,8 @@ config STM32F4_SELECT default y select HAVE_GPIO select HAVE_GPIO_ADC + select HAVE_GPIO_SPI + select HAVE_GPIO_BITBANGING config BOARD_DIRECTORY string diff --git a/src/stm32f4/Makefile b/src/stm32f4/Makefile index b1c22cb7..1586a36f 100644 --- a/src/stm32f4/Makefile +++ b/src/stm32f4/Makefile @@ -16,6 +16,7 @@ src-y += generic/crc16_ccitt.c generic/alloc.c src-y += generic/armcm_irq.c generic/armcm_timer.c src-y += ../lib/stm32f4/system_stm32f4xx.c src-$(CONFIG_HAVE_GPIO_ADC) += stm32f4/adc.c +src-$(CONFIG_HAVE_GPIO_SPI) += stm32f4/spi.c src-$(CONFIG_SERIAL) += stm32f4/serial.c generic/serial_irq.c # Add assembler build rules diff --git a/src/stm32f4/gpio.h b/src/stm32f4/gpio.h index 6aaf02c3..01c01a1e 100644 --- a/src/stm32f4/gpio.h +++ b/src/stm32f4/gpio.h @@ -29,4 +29,13 @@ uint32_t gpio_adc_sample(struct gpio_adc g); uint16_t gpio_adc_read(struct gpio_adc g); void gpio_adc_cancel_sample(struct gpio_adc g); +struct spi_config { + void *spidev; + uint32_t spi_cr1; +}; +struct spi_config spi_setup(uint32_t bus, uint8_t mode, uint32_t rate); +void spi_prepare(struct spi_config config); +void spi_transfer(struct spi_config config, uint8_t receive_data + , uint8_t len, uint8_t *data); + #endif // gpio.h diff --git a/src/stm32f4/spi.c b/src/stm32f4/spi.c new file mode 100644 index 00000000..3759f286 --- /dev/null +++ b/src/stm32f4/spi.c @@ -0,0 +1,57 @@ +// SPI functions on STM32F4 +// +// Copyright (C) 2019 Kevin O'Connor +// +// This file may be distributed under the terms of the GNU GPLv3 license. + +#include "command.h" // shutdown +#include "gpio.h" // spi_setup +#include "internal.h" // gpio_peripheral +#include "sched.h" // sched_shutdown + +DECL_ENUMERATION("spi_bus", "spi2", 0); +DECL_CONSTANT_STR("BUS_PINS_spi2", "PB14,PB15,PB13"); + +struct spi_config +spi_setup(uint32_t bus, uint8_t mode, uint32_t rate) +{ + if (bus) + shutdown("Invalid spi bus"); + + // Enable SPI + enable_pclock(SPI2_BASE); + gpio_peripheral(GPIO('B', 14), GPIO_FUNCTION, 5, 1); + gpio_peripheral(GPIO('B', 15), GPIO_FUNCTION, 5, 0); + gpio_peripheral(GPIO('B', 13), GPIO_FUNCTION, 5, 0); + + // Calculate CR1 register + uint32_t pclk = get_pclock_frequency(SPI2_BASE); + uint32_t div = 0; + while ((pclk >> (div + 1)) > rate && div < 7) + div++; + uint32_t cr1 = ((mode << SPI_CR1_CPHA_Pos) | (div << SPI_CR1_BR_Pos) + | SPI_CR1_SPE | SPI_CR1_MSTR | SPI_CR1_SSM | SPI_CR1_SSI); + + return (struct spi_config){ .spi_cr1 = cr1 }; +} + +void +spi_prepare(struct spi_config config) +{ + SPI2->CR1 = config.spi_cr1; +} + +void +spi_transfer(struct spi_config config, uint8_t receive_data, + uint8_t len, uint8_t *data) +{ + while (len--) { + SPI2->DR = *data; + while (!(SPI2->SR & SPI_SR_RXNE)) + ; + uint8_t rdata = SPI2->DR; + if (receive_data) + *data = rdata; + data++; + } +}