mirror of
https://github.com/betaflight/betaflight.git
synced 2025-07-24 16:55:36 +03:00
196 lines
5.5 KiB
C
196 lines
5.5 KiB
C
/*
|
|
* This file is part of Cleanflight and Betaflight.
|
|
*
|
|
* Cleanflight and Betaflight are free software. You can redistribute
|
|
* this software and/or modify this software under the terms of the
|
|
* GNU General Public License as published by the Free Software
|
|
* Foundation, either version 3 of the License, or (at your option)
|
|
* any later version.
|
|
*
|
|
* Cleanflight and Betaflight are distributed in the hope that they
|
|
* will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
|
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
* See the GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this software.
|
|
*
|
|
* If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
|
|
#include "platform.h"
|
|
|
|
#ifdef USE_SPI
|
|
|
|
#include "drivers/bus.h"
|
|
#include "drivers/bus_spi.h"
|
|
#include "drivers/bus_spi_impl.h"
|
|
#include "drivers/exti.h"
|
|
#include "drivers/io.h"
|
|
#include "drivers/rcc.h"
|
|
|
|
spiDevice_t spiDevice[SPIDEV_COUNT];
|
|
|
|
void spiInitDevice(SPIDevice device)
|
|
{
|
|
spiDevice_t *spi = &(spiDevice[device]);
|
|
|
|
#ifdef SDCARD_SPI_INSTANCE
|
|
if (spi->dev == SDCARD_SPI_INSTANCE) {
|
|
spi->leadingEdge = true;
|
|
}
|
|
#endif
|
|
#ifdef RX_SPI_INSTANCE
|
|
if (spi->dev == RX_SPI_INSTANCE) {
|
|
spi->leadingEdge = true;
|
|
}
|
|
#endif
|
|
|
|
// Enable SPI clock
|
|
RCC_ClockCmd(spi->rcc, ENABLE);
|
|
RCC_ResetCmd(spi->rcc, ENABLE);
|
|
|
|
IOInit(IOGetByTag(spi->sck), OWNER_SPI_SCK, RESOURCE_INDEX(device));
|
|
IOInit(IOGetByTag(spi->miso), OWNER_SPI_MISO, RESOURCE_INDEX(device));
|
|
IOInit(IOGetByTag(spi->mosi), OWNER_SPI_MOSI, RESOURCE_INDEX(device));
|
|
|
|
#if defined(STM32F3) || defined(STM32F4)
|
|
IOConfigGPIOAF(IOGetByTag(spi->sck), SPI_IO_AF_CFG, spi->af);
|
|
IOConfigGPIOAF(IOGetByTag(spi->miso), SPI_IO_AF_CFG, spi->af);
|
|
IOConfigGPIOAF(IOGetByTag(spi->mosi), SPI_IO_AF_CFG, spi->af);
|
|
#elif defined(STM32F10X)
|
|
IOConfigGPIO(IOGetByTag(spi->sck), SPI_IO_AF_SCK_CFG);
|
|
IOConfigGPIO(IOGetByTag(spi->miso), SPI_IO_AF_MISO_CFG);
|
|
IOConfigGPIO(IOGetByTag(spi->mosi), SPI_IO_AF_MOSI_CFG);
|
|
#else
|
|
#error Undefined MCU architecture
|
|
#endif
|
|
|
|
// 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;
|
|
|
|
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;
|
|
}
|
|
|
|
#ifdef STM32F303xC
|
|
// Configure for 8-bit reads.
|
|
SPI_RxFIFOThresholdConfig(spi->dev, SPI_RxFIFOThreshold_QF);
|
|
#endif
|
|
|
|
SPI_Init(spi->dev, &spiInit);
|
|
SPI_Cmd(spi->dev, ENABLE);
|
|
}
|
|
|
|
// return uint8_t value or -1 when failure
|
|
uint8_t spiTransferByte(SPI_TypeDef *instance, uint8_t txByte)
|
|
{
|
|
uint16_t spiTimeout = 1000;
|
|
|
|
while (SPI_I2S_GetFlagStatus(instance, SPI_I2S_FLAG_TXE) == RESET)
|
|
if ((spiTimeout--) == 0)
|
|
return spiTimeoutUserCallback(instance);
|
|
|
|
#ifdef STM32F303xC
|
|
SPI_SendData8(instance, txByte);
|
|
#else
|
|
SPI_I2S_SendData(instance, txByte);
|
|
#endif
|
|
spiTimeout = 1000;
|
|
while (SPI_I2S_GetFlagStatus(instance, SPI_I2S_FLAG_RXNE) == RESET)
|
|
if ((spiTimeout--) == 0)
|
|
return spiTimeoutUserCallback(instance);
|
|
|
|
#ifdef STM32F303xC
|
|
return ((uint8_t)SPI_ReceiveData8(instance));
|
|
#else
|
|
return ((uint8_t)SPI_I2S_ReceiveData(instance));
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* Return true if the bus is currently in the middle of a transmission.
|
|
*/
|
|
bool spiIsBusBusy(SPI_TypeDef *instance)
|
|
{
|
|
#ifdef STM32F303xC
|
|
return SPI_GetTransmissionFIFOStatus(instance) != SPI_TransmissionFIFOStatus_Empty || SPI_I2S_GetFlagStatus(instance, SPI_I2S_FLAG_BSY) == SET;
|
|
#else
|
|
return SPI_I2S_GetFlagStatus(instance, SPI_I2S_FLAG_TXE) == RESET || SPI_I2S_GetFlagStatus(instance, SPI_I2S_FLAG_BSY) == SET;
|
|
#endif
|
|
|
|
}
|
|
|
|
bool spiTransfer(SPI_TypeDef *instance, const uint8_t *txData, uint8_t *rxData, int len)
|
|
{
|
|
uint16_t spiTimeout = 1000;
|
|
|
|
uint8_t b;
|
|
instance->DR;
|
|
while (len--) {
|
|
b = txData ? *(txData++) : 0xFF;
|
|
while (SPI_I2S_GetFlagStatus(instance, SPI_I2S_FLAG_TXE) == RESET) {
|
|
if ((spiTimeout--) == 0)
|
|
return spiTimeoutUserCallback(instance);
|
|
}
|
|
#ifdef STM32F303xC
|
|
SPI_SendData8(instance, b);
|
|
#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);
|
|
}
|
|
#ifdef STM32F303xC
|
|
b = SPI_ReceiveData8(instance);
|
|
#else
|
|
b = SPI_I2S_ReceiveData(instance);
|
|
#endif
|
|
if (rxData)
|
|
*(rxData++) = b;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void spiSetDivisor(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
|
|
}
|
|
#endif
|
|
|
|
SPI_Cmd(instance, DISABLE);
|
|
|
|
const uint16_t tempRegister = (instance->CR1 & ~BR_BITS);
|
|
instance->CR1 = tempRegister | (divisor ? ((ffs(divisor | 0x100) - 2) << 3) : 0);
|
|
|
|
SPI_Cmd(instance, ENABLE);
|
|
|
|
#undef BR_BITS
|
|
}
|
|
#endif
|