From 343e9b3a672072ce172b02dd94f8c0b5ff2f9cd6 Mon Sep 17 00:00:00 2001 From: jflyper Date: Sat, 6 Jan 2018 04:50:19 +0900 Subject: [PATCH] Allow mixed speed and mode on a SPI bus by CR1 caching --- src/main/drivers/barometer/barometer_bmp280.c | 8 +- src/main/drivers/barometer/barometer_lps.c | 6 +- src/main/drivers/barometer/barometer_ms5611.c | 6 +- .../drivers/barometer/barometer_qmp6988.c | 6 +- src/main/drivers/bus.c | 10 ++ src/main/drivers/bus.h | 7 ++ src/main/drivers/bus_spi.c | 46 ++++++- src/main/drivers/bus_spi.h | 25 ++++ src/main/drivers/bus_spi_impl.h | 3 + src/main/drivers/bus_spi_ll.c | 101 +++++++++++++--- src/main/drivers/bus_spi_stdperiph.c | 113 ++++++++++++++---- src/main/drivers/compass/compass_ak8963.c | 8 +- src/main/drivers/compass/compass_hmc5883l.c | 6 +- src/main/drivers/flash.c | 8 ++ src/main/drivers/flash_m25p16.c | 29 ++++- src/main/drivers/flash_w25m.c | 4 + src/main/drivers/max7456.c | 13 +- src/main/drivers/rx/rx_spi.c | 13 +- src/main/drivers/sdcard_spi.c | 27 ++++- src/main/target/common_pre.h | 2 + src/test/unit/baro_bmp280_unittest.cc | 5 +- src/test/unit/baro_ms5611_unittest.cc | 2 +- 22 files changed, 390 insertions(+), 58 deletions(-) diff --git a/src/main/drivers/barometer/barometer_bmp280.c b/src/main/drivers/barometer/barometer_bmp280.c index b67822f19..4d660b5c6 100644 --- a/src/main/drivers/barometer/barometer_bmp280.c +++ b/src/main/drivers/barometer/barometer_bmp280.c @@ -75,7 +75,11 @@ void bmp280BusInit(busDevice_t *busdev) IOHi(busdev->busdev_u.spi.csnPin); // Disable IOInit(busdev->busdev_u.spi.csnPin, OWNER_BARO_CS, 0); IOConfigGPIO(busdev->busdev_u.spi.csnPin, IOCFG_OUT_PP); - spiSetDivisor(busdev->busdev_u.spi.instance, SPI_CLOCK_STANDARD); // XXX +#ifdef USE_SPI_TRANSACTION + spiBusTransactionInit(busdev, SPI_MODE0_POL_LOW_EDGE_1ST, SPI_CLOCK_STANDARD); // BMP280 supports Mode 0 or 3 +#else + spiBusSetDivisor(busdev, SPI_CLOCK_STANDARD); +#endif } #else UNUSED(busdev); @@ -93,6 +97,8 @@ void bmp280BusDeinit(busDevice_t *busdev) #endif } +#include "drivers/time.h" + bool bmp280Detect(baroDev_t *baro) { delay(20); diff --git a/src/main/drivers/barometer/barometer_lps.c b/src/main/drivers/barometer/barometer_lps.c index 592a62e33..0e902f902 100644 --- a/src/main/drivers/barometer/barometer_lps.c +++ b/src/main/drivers/barometer/barometer_lps.c @@ -255,7 +255,11 @@ bool lpsDetect(baroDev_t *baro) IOInit(busdev->busdev_u.spi.csnPin, OWNER_BARO_CS, 0); IOConfigGPIO(busdev->busdev_u.spi.csnPin, IOCFG_OUT_PP); IOHi(busdev->busdev_u.spi.csnPin); // Disable - spiSetDivisor(busdev->busdev_u.spi.instance, SPI_CLOCK_STANDARD); // Baro can work only on up to 10Mhz SPI bus +#ifdef USE_SPI_TRANSACTION + spiBusTransactionInit(busdev, SPI_MODE3_POL_HIGH_EDGE_2ND, SPI_CLOCK_STANDARD); // Baro can work only on up to 10Mhz SPI bus +#else + spiBusSetDivisor(busdev, SPI_CLOCK_STANDARD); // Baro can work only on up to 10Mhz SPI bus +#endif uint8_t temp = 0x00; lpsReadCommand(&baro->busdev, LPS_WHO_AM_I, &temp, 1); diff --git a/src/main/drivers/barometer/barometer_ms5611.c b/src/main/drivers/barometer/barometer_ms5611.c index 084f1f981..867dfce3c 100644 --- a/src/main/drivers/barometer/barometer_ms5611.c +++ b/src/main/drivers/barometer/barometer_ms5611.c @@ -71,7 +71,11 @@ void ms5611BusInit(busDevice_t *busdev) IOHi(busdev->busdev_u.spi.csnPin); // Disable IOInit(busdev->busdev_u.spi.csnPin, OWNER_BARO_CS, 0); IOConfigGPIO(busdev->busdev_u.spi.csnPin, IOCFG_OUT_PP); - spiSetDivisor(busdev->busdev_u.spi.instance, SPI_CLOCK_STANDARD); // XXX +#ifdef USE_SPI_TRANSACTION + spiBusTransactionInit(busdev, SPI_MODE3_POL_HIGH_EDGE_2ND, SPI_CLOCK_STANDARD); +#else + spiBusSetDivisor(busdev, SPI_CLOCK_STANDARD); // XXX +#endif } #else UNUSED(busdev); diff --git a/src/main/drivers/barometer/barometer_qmp6988.c b/src/main/drivers/barometer/barometer_qmp6988.c index 60aa80c63..451a0b301 100755 --- a/src/main/drivers/barometer/barometer_qmp6988.c +++ b/src/main/drivers/barometer/barometer_qmp6988.c @@ -104,7 +104,11 @@ void qmp6988BusInit(busDevice_t *busdev) IOHi(busdev->busdev_u.spi.csnPin); IOInit(busdev->busdev_u.spi.csnPin, OWNER_BARO_CS, 0); IOConfigGPIO(busdev->busdev_u.spi.csnPin, IOCFG_OUT_PP); - spiSetDivisor(busdev->busdev_u.spi.instance, SPI_CLOCK_STANDARD); +#ifdef USE_SPI_TRANSACTION + spiBusTransactionInit(busdev, SPI_MODE3_POL_HIGH_EDGE_2ND, SPI_CLOCK_STANDARD); +#else + spiBusSetDivisor(busdev, SPI_CLOCK_STANDARD); +#endif } #else UNUSED(busdev); diff --git a/src/main/drivers/bus.c b/src/main/drivers/bus.c index 075bfedf9..40cbaafcc 100644 --- a/src/main/drivers/bus.c +++ b/src/main/drivers/bus.c @@ -36,8 +36,13 @@ bool busWriteRegister(const busDevice_t *busdev, uint8_t reg, uint8_t data) switch (busdev->bustype) { #ifdef USE_SPI case BUSTYPE_SPI: +#ifdef USE_SPI_TRANSACTION + // XXX Watch out fastpath users, if any + return spiBusTransactionWriteRegister(busdev, reg & 0x7f, data); +#else return spiBusWriteRegister(busdev, reg & 0x7f, data); #endif +#endif #ifdef USE_I2C case BUSTYPE_I2C: return i2cBusWriteRegister(busdev, reg, data); @@ -57,8 +62,13 @@ bool busReadRegisterBuffer(const busDevice_t *busdev, uint8_t reg, uint8_t *data switch (busdev->bustype) { #ifdef USE_SPI case BUSTYPE_SPI: +#ifdef USE_SPI_TRANSACTION + // XXX Watch out fastpath users, if any + return spiBusTransactionReadRegisterBuffer(busdev, reg | 0x80, data, length); +#else return spiBusReadRegisterBuffer(busdev, reg | 0x80, data, length); #endif +#endif #ifdef USE_I2C case BUSTYPE_I2C: return i2cBusReadRegisterBuffer(busdev, reg, data, length); diff --git a/src/main/drivers/bus.h b/src/main/drivers/bus.h index 1dad07800..f10e919d7 100644 --- a/src/main/drivers/bus.h +++ b/src/main/drivers/bus.h @@ -33,11 +33,18 @@ typedef enum { BUSTYPE_GYRO_AUTO // Only used by acc/gyro bus auto detection code } busType_e; +struct spiDevice_s; + typedef struct busDevice_s { busType_e bustype; union { struct deviceSpi_s { SPI_TypeDef *instance; +#ifdef USE_SPI_TRANSACTION + struct SPIDevice_s *device; // Back ptr to controller for this device. + // Cached SPI_CR1 for spiBusTransactionXXX + uint16_t modeCache; // XXX cr1Value may be a better name? +#endif #if defined(USE_HAL_DRIVER) SPI_HandleTypeDef* handle; // cached here for efficiency #endif diff --git a/src/main/drivers/bus_spi.c b/src/main/drivers/bus_spi.c index b5d6b3456..d0ac9d407 100644 --- a/src/main/drivers/bus_spi.c +++ b/src/main/drivers/bus_spi.c @@ -174,7 +174,6 @@ bool spiBusWriteRegister(const busDevice_t *bus, uint8_t reg, uint8_t data) } bool spiBusRawReadRegisterBuffer(const busDevice_t *bus, uint8_t reg, uint8_t *data, uint8_t length) - { IOLo(bus->busdev_u.spi.csnPin); spiTransferByte(bus->busdev_u.spi.instance, reg); @@ -218,4 +217,49 @@ void spiBusSetInstance(busDevice_t *bus, SPI_TypeDef *instance) bus->bustype = BUSTYPE_SPI; bus->busdev_u.spi.instance = instance; } + +void spiBusSetDivisor(busDevice_t *bus, uint16_t divisor) +{ + spiSetDivisor(bus->busdev_u.spi.instance, divisor); + // bus->busdev_u.spi.modeCache = bus->busdev_u.spi.instance->CR1; +} + +#ifdef USE_SPI_TRANSACTION +// Separate set of spiBusTransactionXXX to keep fast path for acc/gyros. + +void spiBusTransactionBegin(const busDevice_t *bus) +{ + spiBusTransactionSetup(bus); + IOLo(bus->busdev_u.spi.csnPin); +} + +void spiBusTransactionEnd(const busDevice_t *bus) +{ + IOHi(bus->busdev_u.spi.csnPin); +} + +bool spiBusTransactionTransfer(const busDevice_t *bus, const uint8_t *txData, uint8_t *rxData, int length) +{ + spiBusTransactionSetup(bus); + return spiBusTransfer(bus, txData, rxData, length); +} + +bool spiBusTransactionWriteRegister(const busDevice_t *bus, uint8_t reg, uint8_t data) +{ + spiBusTransactionSetup(bus); + return spiBusWriteRegister(bus, reg, data); +} + +uint8_t spiBusTransactionReadRegister(const busDevice_t *bus, uint8_t reg) +{ + spiBusTransactionSetup(bus); + return spiBusReadRegister(bus, reg); +} + +bool spiBusTransactionReadRegisterBuffer(const busDevice_t *bus, uint8_t reg, uint8_t *data, uint8_t length) +{ + spiBusTransactionSetup(bus); + return spiBusReadRegisterBuffer(bus, reg, data, length); +} +#endif // USE_SPI_TRANSACTION #endif diff --git a/src/main/drivers/bus_spi.h b/src/main/drivers/bus_spi.h index 9b9d1f4cb..6fe825dc1 100644 --- a/src/main/drivers/bus_spi.h +++ b/src/main/drivers/bus_spi.h @@ -69,6 +69,21 @@ typedef enum { #endif } SPIClockDivider_e; +// De facto standard mode +// See https://en.wikipedia.org/wiki/Serial_Peripheral_Interface +// +// Mode CPOL CPHA +// 0 0 0 +// 1 0 1 +// 2 1 0 +// 3 1 1 +typedef enum { + SPI_MODE0_POL_LOW_EDGE_1ST = 0, + SPI_MODE1_POL_LOW_EDGE_2ND, + SPI_MODE2_POL_HIGH_EDGE_1ST, + SPI_MODE3_POL_HIGH_EDGE_2ND +} SPIMode_e; + typedef enum SPIDevice { SPIINVALID = -1, SPIDEV_1 = 0, @@ -124,6 +139,16 @@ void spiBusWriteRegisterBuffer(const busDevice_t *bus, uint8_t reg, const uint8_ uint8_t spiBusRawReadRegister(const busDevice_t *bus, uint8_t reg); uint8_t spiBusReadRegister(const busDevice_t *bus, uint8_t reg); void spiBusSetInstance(busDevice_t *bus, SPI_TypeDef *instance); +void spiBusSetDivisor(busDevice_t *bus, SPIClockDivider_e divider); + +void spiBusTransactionInit(busDevice_t *bus, SPIMode_e mode, SPIClockDivider_e divider); +void spiBusTransactionSetup(const busDevice_t *bus); +void spiBusTransactionBegin(const busDevice_t *bus); +void spiBusTransactionEnd(const busDevice_t *bus); +bool spiBusTransactionWriteRegister(const busDevice_t *bus, uint8_t reg, uint8_t data); +uint8_t spiBusTransactionReadRegister(const busDevice_t *bus, uint8_t reg); +bool spiBusTransactionReadRegisterBuffer(const busDevice_t *bus, uint8_t reg, uint8_t *data, uint8_t length); +bool spiBusTransactionTransfer(const busDevice_t *bus, const uint8_t *txData, uint8_t *rxData, int length); struct spiPinConfig_s; void spiPinConfigure(const struct spiPinConfig_s *pConfig); diff --git a/src/main/drivers/bus_spi_impl.h b/src/main/drivers/bus_spi_impl.h index 379263fd8..46a702e13 100644 --- a/src/main/drivers/bus_spi_impl.h +++ b/src/main/drivers/bus_spi_impl.h @@ -70,6 +70,9 @@ typedef struct SPIDevice_s { DMA_HandleTypeDef hdma; uint8_t dmaIrqHandler; #endif +#ifdef USE_SPI_TRANSACTION + uint16_t cr1SoftCopy; // Copy of active CR1 value for this SPI instance +#endif } spiDevice_t; extern spiDevice_t spiDevice[SPIDEV_COUNT]; diff --git a/src/main/drivers/bus_spi_ll.c b/src/main/drivers/bus_spi_ll.c index 573a29c61..3c4698b39 100644 --- a/src/main/drivers/bus_spi_ll.c +++ b/src/main/drivers/bus_spi_ll.c @@ -73,6 +73,18 @@ #define SPI_DEFAULT_TIMEOUT 10 +static LL_SPI_InitTypeDef defaultInit = +{ + .TransferDirection = SPI_DIRECTION_2LINES, + .Mode = SPI_MODE_MASTER, + .DataWidth = SPI_DATASIZE_8BIT, + .NSS = SPI_NSS_SOFT, + .BaudRate = SPI_BAUDRATEPRESCALER_8, + .BitOrder = SPI_FIRSTBIT_MSB, + .CRCPoly = 7, + .CRCCalculation = SPI_CRCCALCULATION_DISABLE, +}; + void spiInitDevice(SPIDevice device) { spiDevice_t *spi = &(spiDevice[device]); @@ -81,6 +93,7 @@ void spiInitDevice(SPIDevice device) return; } +#ifndef USE_SPI_TRANSACTION #ifdef SDCARD_SPI_INSTANCE if (spi->dev == SDCARD_SPI_INSTANCE) { spi->leadingEdge = true; @@ -90,6 +103,7 @@ void spiInitDevice(SPIDevice device) if (spi->dev == RX_SPI_INSTANCE) { spi->leadingEdge = true; } +#endif #endif // Enable SPI clock @@ -110,22 +124,20 @@ void spiInitDevice(SPIDevice device) LL_SPI_Disable(spi->dev); LL_SPI_DeInit(spi->dev); - LL_SPI_InitTypeDef init = +#ifndef USE_SPI_TRANSACTION + if (spi->leadingEdge) { + defaultInit.ClockPolarity = SPI_POLARITY_LOW; + defaultInit.ClockPhase = SPI_PHASE_1EDGE; + } else +#endif { - .TransferDirection = SPI_DIRECTION_2LINES, - .Mode = SPI_MODE_MASTER, - .DataWidth = SPI_DATASIZE_8BIT, - .ClockPolarity = spi->leadingEdge ? SPI_POLARITY_LOW : SPI_POLARITY_HIGH, - .ClockPhase = spi->leadingEdge ? SPI_PHASE_1EDGE : SPI_PHASE_2EDGE, - .NSS = SPI_NSS_SOFT, - .BaudRate = SPI_BAUDRATEPRESCALER_8, - .BitOrder = SPI_FIRSTBIT_MSB, - .CRCPoly = 7, - .CRCCalculation = SPI_CRCCALCULATION_DISABLE, - }; + defaultInit.ClockPolarity = SPI_POLARITY_HIGH; + defaultInit.ClockPhase = SPI_PHASE_2EDGE; + } + LL_SPI_SetRxFIFOThreshold(spi->dev, SPI_RXFIFO_THRESHOLD_QF); - LL_SPI_Init(spi->dev, &init); + LL_SPI_Init(spi->dev, &defaultInit); LL_SPI_Enable(spi->dev); } @@ -217,7 +229,7 @@ bool spiTransfer(SPI_TypeDef *instance, const uint8_t *txData, uint8_t *rxData, return true; } -void spiSetDivisor(SPI_TypeDef *instance, uint16_t divisor) +static uint16_t spiDivisorToBRbits(SPI_TypeDef *instance, uint16_t divisor) { #if !(defined(STM32F1) || defined(STM32F3)) // SPI2 and SPI3 are on APB1/AHB1 which PCLK is half that of APB2/AHB2. @@ -225,12 +237,71 @@ void spiSetDivisor(SPI_TypeDef *instance, uint16_t divisor) if (instance == SPI2 || instance == SPI3) { divisor /= 2; // Safe for divisor == 0 or 1 } +#else + UNUSED(instance); #endif divisor = constrain(divisor, 2, 256); + return (ffs(divisor) - 2) << SPI_CR1_BR_Pos; +} + +void spiSetDivisor(SPI_TypeDef *instance, uint16_t divisor) +{ LL_SPI_Disable(instance); - LL_SPI_SetBaudRatePrescaler(instance, (ffs(divisor) - 2) << SPI_CR1_BR_Pos); + LL_SPI_SetBaudRatePrescaler(instance, spiDivisorToBRbits(instance, divisor)); LL_SPI_Enable(instance); } + +#ifdef USE_SPI_TRANSACTION +void spiBusTransactionInit(busDevice_t *bus, SPIMode_e mode, SPIClockDivider_e divisor) +{ + switch (mode) { + case SPI_MODE0_POL_LOW_EDGE_1ST: + defaultInit.ClockPolarity = SPI_POLARITY_LOW; + defaultInit.ClockPhase = SPI_PHASE_1EDGE; + break; + case SPI_MODE1_POL_LOW_EDGE_2ND: + defaultInit.ClockPolarity = SPI_POLARITY_LOW; + defaultInit.ClockPhase = SPI_PHASE_2EDGE; + break; + case SPI_MODE2_POL_HIGH_EDGE_1ST: + defaultInit.ClockPolarity = SPI_POLARITY_HIGH; + defaultInit.ClockPhase = SPI_PHASE_1EDGE; + break; + case SPI_MODE3_POL_HIGH_EDGE_2ND: + defaultInit.ClockPolarity = SPI_POLARITY_HIGH; + defaultInit.ClockPhase = SPI_PHASE_2EDGE; + break; + } + + LL_SPI_Disable(bus->busdev_u.spi.instance); + LL_SPI_DeInit(bus->busdev_u.spi.instance); + + LL_SPI_Init(bus->busdev_u.spi.instance, &defaultInit); + LL_SPI_SetBaudRatePrescaler(bus->busdev_u.spi.instance, spiDivisorToBRbits(bus->busdev_u.spi.instance, divisor)); + + // Configure for 8-bit reads. XXX Is this STM32F303xC specific? + LL_SPI_SetRxFIFOThreshold(bus->busdev_u.spi.instance, SPI_RXFIFO_THRESHOLD_QF); + + LL_SPI_Enable(bus->busdev_u.spi.instance); + + bus->busdev_u.spi.device = &spiDevice[spiDeviceByInstance(bus->busdev_u.spi.instance)]; + bus->busdev_u.spi.modeCache = bus->busdev_u.spi.instance->CR1; +} + +void spiBusTransactionSetup(const busDevice_t *bus) +{ + // We rely on MSTR bit to detect valid modeCache + + if (bus->busdev_u.spi.modeCache && bus->busdev_u.spi.modeCache != bus->busdev_u.spi.device->cr1SoftCopy) { + bus->busdev_u.spi.instance->CR1 = bus->busdev_u.spi.modeCache; + bus->busdev_u.spi.device->cr1SoftCopy = bus->busdev_u.spi.modeCache; + + // SCK seems to require some time to switch to a new initial level after CR1 is written. + // Here we buy some time in addition to the software copy save above. + __asm__("nop"); + } +} +#endif // USE_SPI_TRANSACTION #endif diff --git a/src/main/drivers/bus_spi_stdperiph.c b/src/main/drivers/bus_spi_stdperiph.c index de7911ea5..5795c86f8 100644 --- a/src/main/drivers/bus_spi_stdperiph.c +++ b/src/main/drivers/bus_spi_stdperiph.c @@ -34,6 +34,16 @@ #include "drivers/io.h" #include "drivers/rcc.h" +static SPI_InitTypeDef defaultInit = { + .SPI_Mode = SPI_Mode_Master, + .SPI_Direction = SPI_Direction_2Lines_FullDuplex, + .SPI_DataSize = SPI_DataSize_8b, + .SPI_NSS = SPI_NSS_Soft, + .SPI_FirstBit = SPI_FirstBit_MSB, + .SPI_CRCPolynomial = 7, + .SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8, +}; + void spiInitDevice(SPIDevice device) { spiDevice_t *spi = &(spiDevice[device]); @@ -42,6 +52,7 @@ void spiInitDevice(SPIDevice device) return; } +#ifndef USE_SPI_TRANSACTION #ifdef SDCARD_SPI_INSTANCE if (spi->dev == SDCARD_SPI_INSTANCE) { spi->leadingEdge = true; @@ -51,6 +62,7 @@ void spiInitDevice(SPIDevice device) if (spi->dev == RX_SPI_INSTANCE) { spi->leadingEdge = true; } +#endif #endif // Enable SPI clock @@ -76,21 +88,15 @@ void spiInitDevice(SPIDevice device) // Init SPI hardware SPI_I2S_DeInit(spi->dev); - SPI_InitTypeDef spiInit; - spiInit.SPI_Mode = SPI_Mode_Master; - spiInit.SPI_Direction = SPI_Direction_2Lines_FullDuplex; - spiInit.SPI_DataSize = SPI_DataSize_8b; - spiInit.SPI_NSS = SPI_NSS_Soft; - spiInit.SPI_FirstBit = SPI_FirstBit_MSB; - spiInit.SPI_CRCPolynomial = 7; - spiInit.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8; - +#ifndef USE_SPI_TRANSACTION if (spi->leadingEdge) { - spiInit.SPI_CPOL = SPI_CPOL_Low; - spiInit.SPI_CPHA = SPI_CPHA_1Edge; - } else { - spiInit.SPI_CPOL = SPI_CPOL_High; - spiInit.SPI_CPHA = SPI_CPHA_2Edge; + defaultInit.SPI_CPOL = SPI_CPOL_Low; + defaultInit.SPI_CPHA = SPI_CPHA_1Edge; + } else +#endif + { + defaultInit.SPI_CPOL = SPI_CPOL_High; + defaultInit.SPI_CPHA = SPI_CPHA_2Edge; } #ifdef STM32F303xC @@ -98,7 +104,7 @@ void spiInitDevice(SPIDevice device) SPI_RxFIFOThresholdConfig(spi->dev, SPI_RxFIFOThreshold_QF); #endif - SPI_Init(spi->dev, &spiInit); + SPI_Init(spi->dev, &defaultInit); SPI_Cmd(spi->dev, ENABLE); } @@ -158,7 +164,9 @@ bool spiTransfer(SPI_TypeDef *instance, const uint8_t *txData, uint8_t *rxData, #else SPI_I2S_SendData(instance, b); #endif + spiTimeout = 1000; + while (SPI_I2S_GetFlagStatus(instance, SPI_I2S_FLAG_RXNE) == RESET) { if ((spiTimeout--) == 0) return spiTimeoutUserCallback(instance); @@ -175,27 +183,86 @@ bool spiTransfer(SPI_TypeDef *instance, const uint8_t *txData, uint8_t *rxData, return true; } -void spiSetDivisor(SPI_TypeDef *instance, uint16_t divisor) +static uint16_t spiDivisorToBRbits(SPI_TypeDef *instance, uint16_t divisor) { -#define BR_BITS ((BIT(5) | BIT(4) | BIT(3))) - #if !(defined(STM32F1) || defined(STM32F3)) // SPI2 and SPI3 are on APB1/AHB1 which PCLK is half that of APB2/AHB2. if (instance == SPI2 || instance == SPI3) { divisor /= 2; // Safe for divisor == 0 or 1 } +#else + UNUSED(instance); #endif divisor = constrain(divisor, 2, 256); - SPI_Cmd(instance, DISABLE); + return (ffs(divisor) - 2) << 3; // SPI_CR1_BR_Pos +} +static void spiSetDivisorBRreg(SPI_TypeDef *instance, uint16_t divisor) +{ +#define BR_BITS ((BIT(5) | BIT(4) | BIT(3))) const uint16_t tempRegister = (instance->CR1 & ~BR_BITS); - instance->CR1 = tempRegister | ((ffs(divisor) - 2) << 3); - - SPI_Cmd(instance, ENABLE); - + instance->CR1 = tempRegister | spiDivisorToBRbits(instance, divisor); #undef BR_BITS } + +void spiSetDivisor(SPI_TypeDef *instance, uint16_t divisor) +{ + SPI_Cmd(instance, DISABLE); + spiSetDivisorBRreg(instance, divisor); + SPI_Cmd(instance, ENABLE); +} + +#ifdef USE_SPI_TRANSACTION + +void spiBusTransactionInit(busDevice_t *bus, SPIMode_e mode, SPIClockDivider_e divider) +{ + switch (mode) { + case SPI_MODE0_POL_LOW_EDGE_1ST: + defaultInit.SPI_CPOL = SPI_CPOL_Low; + defaultInit.SPI_CPHA = SPI_CPHA_1Edge; + break; + case SPI_MODE1_POL_LOW_EDGE_2ND: + defaultInit.SPI_CPOL = SPI_CPOL_Low; + defaultInit.SPI_CPHA = SPI_CPHA_2Edge; + break; + case SPI_MODE2_POL_HIGH_EDGE_1ST: + defaultInit.SPI_CPOL = SPI_CPOL_High; + defaultInit.SPI_CPHA = SPI_CPHA_1Edge; + break; + case SPI_MODE3_POL_HIGH_EDGE_2ND: + defaultInit.SPI_CPOL = SPI_CPOL_High; + defaultInit.SPI_CPHA = SPI_CPHA_2Edge; + break; + } + + // Initialize the SPI instance to setup CR1 + + SPI_Init(bus->busdev_u.spi.instance, &defaultInit); + spiSetDivisorBRreg(bus->busdev_u.spi.instance, divider); +#ifdef STM32F303xC + // Configure for 8-bit reads. + SPI_RxFIFOThresholdConfig(bus->busdev_u.spi.instance, SPI_RxFIFOThreshold_QF); +#endif + + bus->busdev_u.spi.modeCache = bus->busdev_u.spi.instance->CR1; + bus->busdev_u.spi.device = &spiDevice[spiDeviceByInstance(bus->busdev_u.spi.instance)]; +} + +void spiBusTransactionSetup(const busDevice_t *bus) +{ + // We rely on MSTR bit to detect valid modeCache + + if (bus->busdev_u.spi.modeCache && bus->busdev_u.spi.modeCache != bus->busdev_u.spi.device->cr1SoftCopy) { + bus->busdev_u.spi.instance->CR1 = bus->busdev_u.spi.modeCache; + bus->busdev_u.spi.device->cr1SoftCopy = bus->busdev_u.spi.modeCache; + + // SCK seems to require some time to switch to a new initial level after CR1 is written. + // Here we buy some time in addition to the software copy save above. + __asm__("nop"); + } +} +#endif // USE_SPI_TRANSACTION #endif diff --git a/src/main/drivers/compass/compass_ak8963.c b/src/main/drivers/compass/compass_ak8963.c index 5a6cbb27c..d4250489f 100644 --- a/src/main/drivers/compass/compass_ak8963.c +++ b/src/main/drivers/compass/compass_ak8963.c @@ -366,7 +366,7 @@ static bool ak8963Init(magDev_t *mag) return true; } -void ak8963BusInit(const busDevice_t *busdev) +void ak8963BusInit(busDevice_t *busdev) { switch (busdev->bustype) { #ifdef USE_MAG_AK8963 @@ -380,7 +380,11 @@ void ak8963BusInit(const busDevice_t *busdev) IOHi(busdev->busdev_u.spi.csnPin); // Disable IOInit(busdev->busdev_u.spi.csnPin, OWNER_COMPASS_CS, 0); IOConfigGPIO(busdev->busdev_u.spi.csnPin, IOCFG_OUT_PP); - spiSetDivisor(busdev->busdev_u.spi.instance, SPI_CLOCK_STANDARD); +#ifdef USE_SPI_TRANSACTION + spiBusTransactionInit(busdev, SPI_MODE3_POL_HIGH_EDGE_2ND, SPI_CLOCK_STANDARD); +#else + spiBusSetDivisor(busdev, SPI_CLOCK_STANDARD); +#endif break; #endif diff --git a/src/main/drivers/compass/compass_hmc5883l.c b/src/main/drivers/compass/compass_hmc5883l.c index 8f24db3cb..c6152467f 100644 --- a/src/main/drivers/compass/compass_hmc5883l.c +++ b/src/main/drivers/compass/compass_hmc5883l.c @@ -190,7 +190,11 @@ static void hmc5883SpiInit(busDevice_t *busdev) IOInit(busdev->busdev_u.spi.csnPin, OWNER_COMPASS_CS, 0); IOConfigGPIO(busdev->busdev_u.spi.csnPin, IOCFG_OUT_PP); - spiSetDivisor(busdev->busdev_u.spi.instance, SPI_CLOCK_STANDARD); +#ifdef USE_SPI_TRANSACTION + spiBusTransactionInit(busdev, SPI_MODE3_POL_HIGH_EDGE_2ND, SPI_CLOCK_STANDARD); +#else + spiBusSetDivisor(busdev, SPI_CLOCK_STANDARD); +#endif } #endif diff --git a/src/main/drivers/flash.c b/src/main/drivers/flash.c index 40533ef27..e1d2eed2b 100644 --- a/src/main/drivers/flash.c +++ b/src/main/drivers/flash.c @@ -68,10 +68,14 @@ bool flashInit(const flashConfig_t *flashConfig) IOConfigGPIO(busdev->busdev_u.spi.csnPin, SPI_IO_CS_CFG); IOHi(busdev->busdev_u.spi.csnPin); +#ifdef USE_SPI_TRANSACTION + spiBusTransactionInit(busdev, SPI_MODE3_POL_HIGH_EDGE_2ND, SPI_CLOCK_FAST); +#else #ifndef FLASH_SPI_SHARED //Maximum speed for standard READ command is 20mHz, other commands tolerate 25mHz //spiSetDivisor(busdev->busdev_u.spi.instance, SPI_CLOCK_FAST); spiSetDivisor(busdev->busdev_u.spi.instance, SPI_CLOCK_STANDARD*2); +#endif #endif flashDevice.busdev = busdev; @@ -87,7 +91,11 @@ bool flashInit(const flashConfig_t *flashConfig) in[1] = 0; // Clearing the CS bit terminates the command early so we don't have to read the chip UID: +#ifdef USE_SPI_TRANSACTION + spiBusTransactionTransfer(busdev, out, in, sizeof(out)); +#else spiBusTransfer(busdev, out, in, sizeof(out)); +#endif // Manufacturer, memory type, and capacity uint32_t chipID = (in[1] << 16) | (in[2] << 8) | (in[3]); diff --git a/src/main/drivers/flash_m25p16.c b/src/main/drivers/flash_m25p16.c index f39b1474c..b05f83d2f 100644 --- a/src/main/drivers/flash_m25p16.c +++ b/src/main/drivers/flash_m25p16.c @@ -80,6 +80,7 @@ STATIC_ASSERT(M25P16_PAGESIZE < FLASH_MAX_PAGE_SIZE, M25P16_PAGESIZE_too_small); const flashVTable_t m25p16_vTable; +#ifndef USE_SPI_TRANSACTION static void m25p16_disable(busDevice_t *bus) { IOHi(bus->busdev_u.spi.csnPin); @@ -91,12 +92,17 @@ static void m25p16_enable(busDevice_t *bus) __NOP(); IOLo(bus->busdev_u.spi.csnPin); } +#endif static void m25p16_transfer(busDevice_t *bus, const uint8_t *txData, uint8_t *rxData, int len) { +#ifdef USE_SPI_TRANSACTION + spiBusTransactionTransfer(bus, txData, rxData, len); +#else m25p16_enable(bus); spiTransfer(bus->busdev_u.spi.instance, txData, rxData, len); m25p16_disable(bus); +#endif } /** @@ -104,11 +110,13 @@ static void m25p16_transfer(busDevice_t *bus, const uint8_t *txData, uint8_t *rx */ static void m25p16_performOneByteCommand(busDevice_t *bus, uint8_t command) { +#ifdef USE_SPI_TRANSACTION + m25p16_transfer(bus, &command, NULL, 1); +#else m25p16_enable(bus); - spiTransferByte(bus->busdev_u.spi.instance, command); - m25p16_disable(bus); +#endif } /** @@ -269,13 +277,20 @@ static void m25p16_pageProgramContinue(flashDevice_t *fdevice, const uint8_t *da m25p16_writeEnable(fdevice); +#ifdef USE_SPI_TRANSACTION + spiBusTransactionBegin(fdevice->busdev); +#else m25p16_enable(fdevice->busdev); +#endif spiTransfer(fdevice->busdev->busdev_u.spi.instance, command, NULL, fdevice->isLargeFlash ? 5 : 4); - spiTransfer(fdevice->busdev->busdev_u.spi.instance, data, NULL, length); +#ifdef USE_SPI_TRANSACTION + spiBusTransactionEnd(fdevice->busdev); +#else m25p16_disable(fdevice->busdev); +#endif fdevice->currentWriteAddress += length; } @@ -327,12 +342,20 @@ static int m25p16_readBytes(flashDevice_t *fdevice, uint32_t address, uint8_t *b return 0; } +#ifdef USE_SPI_TRANSACTION + spiBusTransactionBegin(fdevice->busdev); +#else m25p16_enable(fdevice->busdev); +#endif spiTransfer(fdevice->busdev->busdev_u.spi.instance, command, NULL, fdevice->isLargeFlash ? 5 : 4); spiTransfer(fdevice->busdev->busdev_u.spi.instance, NULL, buffer, length); +#ifdef USE_SPI_TRANSACTION + spiBusTransactionEnd(fdevice->busdev); +#else m25p16_disable(fdevice->busdev); +#endif return length; } diff --git a/src/main/drivers/flash_w25m.c b/src/main/drivers/flash_w25m.c index 89faca129..f4fa271a8 100644 --- a/src/main/drivers/flash_w25m.c +++ b/src/main/drivers/flash_w25m.c @@ -69,7 +69,11 @@ static void w25m_dieSelect(busDevice_t *busdev, int die) uint8_t command[2] = { W25M_INSTRUCTION_SOFTWARE_DIE_SELECT, die }; +#ifdef SPI_BUS_TRANSACTION + spiBusTransactionTransfer(busdev, command, NULL, 2); +#else spiBusTransfer(busdev, command, NULL, 2); +#endif activeDie = die; } diff --git a/src/main/drivers/max7456.c b/src/main/drivers/max7456.c index 86905bc5f..bdbf3601e 100644 --- a/src/main/drivers/max7456.c +++ b/src/main/drivers/max7456.c @@ -174,8 +174,12 @@ // On shared SPI buss we want to change clock for OSD chip and restore for other devices. +#ifdef USE_SPI_TRANSACTION + #define __spiBusTransactionBegin(busdev) spiBusTransactionBegin(busdev) + #define __spiBusTransactionEnd(busdev) spiBusTransactionEnd(busdev) +#else #ifdef MAX7456_SPI_CLK - #define __spiBusTransactionBegin(busdev) {spiSetDivisor((busdev)->busdev_u.spi.instance, max7456SpiClock);IOLo((busdev)->busdev_u.spi.csnPin);} + #define __spiBusTransactionBegin(busdev) {spiBusSetDivisor(busdev, max7456SpiClock);IOLo((busdev)->busdev_u.spi.csnPin);} #else #define __spiBusTransactionBegin(busdev) IOLo((busdev)->busdev_u.spi.csnPin) #endif @@ -185,6 +189,7 @@ #else #define __spiBusTransactionEnd(busdev) IOHi((busdev)->busdev_u.spi.csnPin) #endif +#endif busDevice_t max7456BusDevice; busDevice_t *busdev = &max7456BusDevice; @@ -476,7 +481,11 @@ bool max7456Init(const max7456Config_t *max7456Config, const vcdProfile_t *pVcdP UNUSED(cpuOverclock); #endif - spiSetDivisor(busdev->busdev_u.spi.instance, max7456SpiClock); +#ifdef USE_SPI_TRANSACTION + spiBusTransactionInit(busdev, SPI_MODE3_POL_HIGH_EDGE_2ND, max7456SpiClock); +#else + spiBusSetDivisor(busdev, max7456SpiClock); +#endif // force soft reset on Max7456 __spiBusTransactionBegin(busdev); diff --git a/src/main/drivers/rx/rx_spi.c b/src/main/drivers/rx/rx_spi.c index 26259b55b..c82751c7b 100644 --- a/src/main/drivers/rx/rx_spi.c +++ b/src/main/drivers/rx/rx_spi.c @@ -54,11 +54,13 @@ void rxSpiDevicePreInit(const rxSpiConfig_t *rxSpiConfig) bool rxSpiDeviceInit(const rxSpiConfig_t *rxSpiConfig) { - if (!rxSpiConfig->spibus) { + SPI_TypeDef *instance = spiInstanceByDevice(SPI_CFG_TO_DEV(rxSpiConfig->spibus)); + + if (!instance) { return false; } - spiBusSetInstance(busdev, spiInstanceByDevice(SPI_CFG_TO_DEV(rxSpiConfig->spibus))); + spiBusSetInstance(busdev, instance); const IO_t rxCsPin = IOGetByTag(rxSpiConfig->csnTag); IOInit(rxCsPin, OWNER_RX_SPI_CS, 0); @@ -66,8 +68,11 @@ bool rxSpiDeviceInit(const rxSpiConfig_t *rxSpiConfig) busdev->busdev_u.spi.csnPin = rxCsPin; IOHi(rxCsPin); - - spiSetDivisor(busdev->busdev_u.spi.instance, SPI_CLOCK_STANDARD); +#ifdef USE_SPI_TRANSACTION + spiBusTransactionInit(busdev, SPI_MODE0_POL_LOW_EDGE_1ST, SPI_CLOCK_STANDARD); +#else + spiBusSetDivisor(busdev, SPI_CLOCK_STANDARD); +#endif return true; } diff --git a/src/main/drivers/sdcard_spi.c b/src/main/drivers/sdcard_spi.c index 4aa1ef9ba..b0a4eb767 100644 --- a/src/main/drivers/sdcard_spi.c +++ b/src/main/drivers/sdcard_spi.c @@ -55,6 +55,9 @@ /* Operational speed <= 25MHz */ #define SDCARD_SPI_FULL_SPEED_CLOCK_DIVIDER SPI_CLOCK_FAST +#define SDCARD_SPI_MODE SPI_MODE0_POL_LOW_EDGE_1ST +//#define SDCARD_SPI_MODE SPI_MODE3_POL_HIGH_EDGE_2ND + /* Break up 512-byte SD card sectors into chunks of this size when writing without DMA to reduce the peak overhead * per call to sdcard_poll(). */ @@ -71,7 +74,11 @@ static bool sdcardSpi_isFunctional(void) static void sdcard_select(void) { +#ifdef USE_SPI_TRANSACTION + spiBusTransactionBegin(&sdcard.busdev); +#else IOLo(sdcard.busdev.busdev_u.spi.csnPin); +#endif } static void sdcard_deselect(void) @@ -82,7 +89,12 @@ static void sdcard_deselect(void) while (spiBusIsBusBusy(&sdcard.busdev)) { } + delayMicroseconds(10); +#ifdef USE_SPI_TRANSACTION + spiBusTransactionEnd(&sdcard.busdev); +#else IOHi(sdcard.busdev.busdev_u.spi.csnPin); +#endif } /** @@ -99,7 +111,11 @@ static void sdcard_reset(void) } if (sdcard.state >= SDCARD_STATE_READY) { +#ifdef USE_SPI_TRANSACTION + spiBusTransactionInit(&sdcard.busdev, SDCARD_SPI_MODE, SDCARD_SPI_INITIALIZATION_CLOCK_DIVIDER); +#else spiSetDivisor(sdcard.busdev.busdev_u.spi.instance, SDCARD_SPI_INITIALIZATION_CLOCK_DIVIDER); +#endif } sdcard.failureCount++; @@ -535,14 +551,18 @@ static void sdcardSpi_init(const sdcardConfig_t *config, const spiPinConfig_t *s } // Max frequency is initially 400kHz + +#ifdef USE_SPI_TRANSACTION + spiBusTransactionInit(&sdcard.busdev, SDCARD_SPI_MODE, SDCARD_SPI_INITIALIZATION_CLOCK_DIVIDER); +#else spiSetDivisor(sdcard.busdev.busdev_u.spi.instance, SDCARD_SPI_INITIALIZATION_CLOCK_DIVIDER); +#endif // SDCard wants 1ms minimum delay after power is applied to it delay(1000); // Transmit at least 74 dummy clock cycles with CS high so the SD card can start up IOHi(sdcard.busdev.busdev_u.spi.csnPin); - spiBusRawTransfer(&sdcard.busdev, NULL, NULL, SDCARD_INIT_NUM_DUMMY_BYTES); // Wait for that transmission to finish before we enable the SDCard, so it receives the required number of cycles: @@ -700,7 +720,12 @@ static bool sdcardSpi_poll(void) } // Now we're done with init and we can switch to the full speed clock (<25MHz) + +#ifdef USE_SPI_TRANSACTION + spiBusTransactionInit(&sdcard.busdev, SDCARD_SPI_MODE, SDCARD_SPI_FULL_SPEED_CLOCK_DIVIDER); +#else spiSetDivisor(sdcard.busdev.busdev_u.spi.instance, SDCARD_SPI_FULL_SPEED_CLOCK_DIVIDER); +#endif sdcard.multiWriteBlocksRemain = 0; diff --git a/src/main/target/common_pre.h b/src/main/target/common_pre.h index 776342dc5..9abdc6cb9 100644 --- a/src/main/target/common_pre.h +++ b/src/main/target/common_pre.h @@ -64,6 +64,7 @@ #if defined(STM32F40_41xxx) || defined(STM32F411xE) #define USE_OVERCLOCK +#define USE_SPI_TRANSACTION #endif #endif // STM32F4 @@ -83,6 +84,7 @@ #define USE_PERSISTENT_MSC_RTC #define USE_MCO #define USE_DMA_SPEC +#define USE_SPI_TRANSACTION #endif // STM32F7 #if defined(STM32F4) || defined(STM32F7) diff --git a/src/test/unit/baro_bmp280_unittest.cc b/src/test/unit/baro_bmp280_unittest.cc index 676d7ec96..cd9b2b425 100644 --- a/src/test/unit/baro_bmp280_unittest.cc +++ b/src/test/unit/baro_bmp280_unittest.cc @@ -147,7 +147,10 @@ void delay(uint32_t) {} bool busReadRegisterBuffer(const busDevice_t*, uint8_t, uint8_t*, uint8_t) {return true;} bool busWriteRegister(const busDevice_t*, uint8_t, uint8_t) {return true;} -void spiSetDivisor() { +void spiBusSetDivisor() { +} + +void spiBusTransactionInit() { } void spiPreinitByIO() { diff --git a/src/test/unit/baro_ms5611_unittest.cc b/src/test/unit/baro_ms5611_unittest.cc index 559f0f972..0b7c1e830 100644 --- a/src/test/unit/baro_ms5611_unittest.cc +++ b/src/test/unit/baro_ms5611_unittest.cc @@ -149,7 +149,7 @@ void delayMicroseconds(uint32_t) {} bool busReadRegisterBuffer(const busDevice_t*, uint8_t, uint8_t*, uint8_t) {return true;} bool busWriteRegister(const busDevice_t*, uint8_t, uint8_t) {return true;} -void spiSetDivisor() { +void spiBusSetDivisor() { } void spiPreinitByIO() {