From 5c7c8c984b582304d1f1c36de0f1d9bacbd8d42d Mon Sep 17 00:00:00 2001 From: Grigori Goronzy Date: Mon, 2 Jul 2018 23:17:28 +0200 Subject: [PATCH] stm32f1: add SPI support Add basic SPI support and associated documentation. v2: remove baud rate check, fix baud rate calculations v3: finish transaction with BSY check, disable SPI when not in use Signed-off-by: Grigori Goronzy --- docs/stm32f1.md | 6 ++- src/stm32f1/Kconfig | 1 + src/stm32f1/gpio.c | 101 ++++++++++++++++++++++++++++++++++++++++++++ src/stm32f1/gpio.h | 7 +++ src/stm32f1/main.c | 9 ++++ 5 files changed, 122 insertions(+), 2 deletions(-) diff --git a/docs/stm32f1.md b/docs/stm32f1.md index c75dd304..04868ec6 100644 --- a/docs/stm32f1.md +++ b/docs/stm32f1.md @@ -22,7 +22,8 @@ Fixed pins The UART used for communication with the host is fixed to pins PA9 (TX) and PA10 (RX). SWD pins (PA13/PA14) are enabled for debugging and cannot be used for any -I/O. +I/O. SPI uses pins PB13/PB14/PB15, but the pins can be used as general I/O if +SPI is not used. Digital I/O =========== @@ -44,4 +45,5 @@ PC0-PC5). SPI === -SPI support is currently unimplemented. +SPI uses pin PB13 (SCK), PB14 (MISO) and PB15 (MOSI). The clock speed range is +0.15..18 MHz. Chip select pins do not have any restrictions. diff --git a/src/stm32f1/Kconfig b/src/stm32f1/Kconfig index 53ff6dcf..13d423c6 100644 --- a/src/stm32f1/Kconfig +++ b/src/stm32f1/Kconfig @@ -7,6 +7,7 @@ config STM32F1_SELECT default y select HAVE_GPIO select HAVE_GPIO_ADC + select HAVE_GPIO_SPI select HAVE_USER_INTERFACE config BOARD_DIRECTORY diff --git a/src/stm32f1/gpio.c b/src/stm32f1/gpio.c index caadf277..ed507496 100644 --- a/src/stm32f1/gpio.c +++ b/src/stm32f1/gpio.c @@ -11,8 +11,10 @@ #include "compiler.h" // ARRAY_SIZE #include "gpio.h" // gpio_out_setup #include "stm32f1xx.h" +#include "stm32f1xx_ll_rcc.h" #include "stm32f1xx_ll_gpio.h" #include "stm32f1xx_ll_adc.h" +#include "stm32f1xx_ll_spi.h" #include "sched.h" // sched_shutdown #include "board/irq.h" #include "board/io.h" @@ -219,3 +221,102 @@ gpio_adc_cancel_sample(struct gpio_adc g) LL_ADC_ClearFlag_EOS(ADC1); } } + +/**************************************************************** + * Serial Peripheral Interface (SPI) pins + ****************************************************************/ + +void spi_set_mode(SPI_TypeDef *spi, uint8_t mode) +{ + switch (mode) { + case 0: + LL_SPI_SetClockPolarity(spi, LL_SPI_POLARITY_LOW); + LL_SPI_SetClockPhase(spi, LL_SPI_PHASE_1EDGE); + break; + case 1: + LL_SPI_SetClockPolarity(spi, LL_SPI_POLARITY_LOW); + LL_SPI_SetClockPhase(spi, LL_SPI_PHASE_2EDGE); + break; + case 2: + LL_SPI_SetClockPolarity(spi, LL_SPI_POLARITY_HIGH); + LL_SPI_SetClockPhase(spi, LL_SPI_PHASE_1EDGE); + break; + case 3: + LL_SPI_SetClockPolarity(spi, LL_SPI_POLARITY_HIGH); + LL_SPI_SetClockPhase(spi, LL_SPI_PHASE_2EDGE); + break; + default: + shutdown("Invalid SPI mode"); + } +} + +void spi_set_baudrate(SPI_TypeDef *spi, uint32_t rate) +{ + const uint32_t pclk = __LL_RCC_CALC_PCLK1_FREQ(SystemCoreClock, LL_RCC_GetAPB1Prescaler()); + const uint32_t prescaler = pclk / rate; + + uint32_t setting = LL_SPI_BAUDRATEPRESCALER_DIV256; + if (prescaler <= 2) + setting = LL_SPI_BAUDRATEPRESCALER_DIV2; + else if (prescaler <= 4) + setting = LL_SPI_BAUDRATEPRESCALER_DIV4; + else if (prescaler <= 8) + setting = LL_SPI_BAUDRATEPRESCALER_DIV8; + else if (prescaler <= 16) + setting = LL_SPI_BAUDRATEPRESCALER_DIV16; + else if (prescaler <= 32) + setting = LL_SPI_BAUDRATEPRESCALER_DIV32; + else if (prescaler <= 64) + setting = LL_SPI_BAUDRATEPRESCALER_DIV64; + else if (prescaler <= 128) + setting = LL_SPI_BAUDRATEPRESCALER_DIV128; + + LL_SPI_SetBaudRatePrescaler(spi, setting); +} + +void spi_init_pins(void) +{ + LL_GPIO_SetPinMode(GPIOB, LL_GPIO_PIN_13, LL_GPIO_MODE_ALTERNATE); + LL_GPIO_SetPinMode(GPIOB, LL_GPIO_PIN_14, LL_GPIO_MODE_INPUT); + LL_GPIO_SetPinMode(GPIOB, LL_GPIO_PIN_15, LL_GPIO_MODE_ALTERNATE); + LL_GPIO_SetPinOutputType(GPIOB, LL_GPIO_PIN_13, LL_GPIO_OUTPUT_PUSHPULL); + LL_GPIO_SetPinPull(GPIOB, LL_GPIO_PIN_14, LL_GPIO_PULL_UP); + LL_GPIO_SetPinOutputType(GPIOB, LL_GPIO_PIN_15, LL_GPIO_OUTPUT_PUSHPULL); +} + +struct spi_config +spi_setup(uint32_t bus, uint8_t mode, uint32_t rate) +{ + struct spi_config config; + config.config = *SPI2; + + if (bus > 0 || !rate) + shutdown("Invalid spi_setup parameters"); + + spi_init_pins(); + spi_set_mode(&config.config, mode); + spi_set_baudrate(&config.config, rate); + + return config; +} + +void +spi_transfer(struct spi_config config, uint8_t receive_data, + uint8_t len, uint8_t *data) +{ + *SPI2 = config.config; + LL_SPI_Enable(SPI2); + + while (len--) { + LL_SPI_TransmitData8(SPI2, *data); + while (!LL_SPI_IsActiveFlag_TXE(SPI2)); + if (receive_data) { + while (!LL_SPI_IsActiveFlag_RXNE(SPI2)); + *data = LL_SPI_ReceiveData8(SPI2); + } + data++; + } + + while (LL_SPI_IsActiveFlag_BSY(SPI2)); + LL_SPI_Disable(SPI2); +} diff --git a/src/stm32f1/gpio.h b/src/stm32f1/gpio.h index 52e944f1..8c2a00ca 100644 --- a/src/stm32f1/gpio.h +++ b/src/stm32f1/gpio.h @@ -30,4 +30,11 @@ 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 { + SPI_TypeDef config; +}; +struct spi_config spi_setup(uint32_t bus, uint8_t mode, uint32_t rate); +void spi_transfer(struct spi_config config, uint8_t receive_data, + uint8_t len, uint8_t *data); + #endif // gpio.h diff --git a/src/stm32f1/main.c b/src/stm32f1/main.c index 8c144343..c6839c36 100644 --- a/src/stm32f1/main.c +++ b/src/stm32f1/main.c @@ -14,6 +14,7 @@ #include "stm32f1xx_ll_iwdg.h" #include "stm32f1xx_ll_gpio.h" #include "stm32f1xx_ll_adc.h" +#include "stm32f1xx_ll_spi.h" #include "sched.h" // sched_main DECL_CONSTANT(MCU, "stm32f103"); @@ -108,6 +109,13 @@ void adc_config(void) LL_ADC_REG_SetSequencerLength(ADC1, LL_ADC_REG_SEQ_SCAN_DISABLE); } +void spi_config(void) +{ + LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_SPI2); + LL_SPI_SetNSSMode(SPI2, LL_SPI_NSS_SOFT); + LL_SPI_SetMode(SPI2, LL_SPI_MODE_MASTER); +} + void io_config(void) { LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_AFIO); @@ -131,6 +139,7 @@ main(void) clock_config(); adc_config(); io_config(); + spi_config(); sched_main(); return 0; }