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 <greg@chown.ath.cx>
This commit is contained in:
Grigori Goronzy 2018-07-02 23:17:28 +02:00 committed by KevinOConnor
parent b0ee323e2e
commit 5c7c8c984b
5 changed files with 122 additions and 2 deletions

View File

@ -22,7 +22,8 @@ Fixed pins
The UART used for communication with the host is fixed to pins PA9 (TX) and PA10 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 (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 Digital I/O
=========== ===========
@ -44,4 +45,5 @@ PC0-PC5).
SPI 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.

View File

@ -7,6 +7,7 @@ config STM32F1_SELECT
default y default y
select HAVE_GPIO select HAVE_GPIO
select HAVE_GPIO_ADC select HAVE_GPIO_ADC
select HAVE_GPIO_SPI
select HAVE_USER_INTERFACE select HAVE_USER_INTERFACE
config BOARD_DIRECTORY config BOARD_DIRECTORY

View File

@ -11,8 +11,10 @@
#include "compiler.h" // ARRAY_SIZE #include "compiler.h" // ARRAY_SIZE
#include "gpio.h" // gpio_out_setup #include "gpio.h" // gpio_out_setup
#include "stm32f1xx.h" #include "stm32f1xx.h"
#include "stm32f1xx_ll_rcc.h"
#include "stm32f1xx_ll_gpio.h" #include "stm32f1xx_ll_gpio.h"
#include "stm32f1xx_ll_adc.h" #include "stm32f1xx_ll_adc.h"
#include "stm32f1xx_ll_spi.h"
#include "sched.h" // sched_shutdown #include "sched.h" // sched_shutdown
#include "board/irq.h" #include "board/irq.h"
#include "board/io.h" #include "board/io.h"
@ -219,3 +221,102 @@ gpio_adc_cancel_sample(struct gpio_adc g)
LL_ADC_ClearFlag_EOS(ADC1); 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);
}

View File

@ -30,4 +30,11 @@ uint32_t gpio_adc_sample(struct gpio_adc g);
uint16_t gpio_adc_read(struct gpio_adc g); uint16_t gpio_adc_read(struct gpio_adc g);
void gpio_adc_cancel_sample(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 #endif // gpio.h

View File

@ -14,6 +14,7 @@
#include "stm32f1xx_ll_iwdg.h" #include "stm32f1xx_ll_iwdg.h"
#include "stm32f1xx_ll_gpio.h" #include "stm32f1xx_ll_gpio.h"
#include "stm32f1xx_ll_adc.h" #include "stm32f1xx_ll_adc.h"
#include "stm32f1xx_ll_spi.h"
#include "sched.h" // sched_main #include "sched.h" // sched_main
DECL_CONSTANT(MCU, "stm32f103"); DECL_CONSTANT(MCU, "stm32f103");
@ -108,6 +109,13 @@ void adc_config(void)
LL_ADC_REG_SetSequencerLength(ADC1, LL_ADC_REG_SEQ_SCAN_DISABLE); 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) void io_config(void)
{ {
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_AFIO); LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_AFIO);
@ -131,6 +139,7 @@ main(void)
clock_config(); clock_config();
adc_config(); adc_config();
io_config(); io_config();
spi_config();
sched_main(); sched_main();
return 0; return 0;
} }