mirror of
https://github.com/betaflight/betaflight.git
synced 2025-07-24 00:35:39 +03:00
359 lines
9.4 KiB
C
359 lines
9.4 KiB
C
/*
|
|
* This file is part of Cleanflight.
|
|
*
|
|
* Cleanflight is free software: you can redistribute it and/or modify
|
|
* it 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 is distributed in the hope that it 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 Cleanflight. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
|
|
#include <platform.h>
|
|
|
|
#include "bus_spi.h"
|
|
#include "exti.h"
|
|
#include "io.h"
|
|
#include "io_impl.h"
|
|
#include "rcc.h"
|
|
|
|
/* for F30x processors */
|
|
#if defined(STM32F303xC)
|
|
#ifndef GPIO_AF_SPI1
|
|
#define GPIO_AF_SPI1 GPIO_AF_5
|
|
#endif
|
|
#ifndef GPIO_AF_SPI2
|
|
#define GPIO_AF_SPI2 GPIO_AF_5
|
|
#endif
|
|
#ifndef GPIO_AF_SPI3
|
|
#define GPIO_AF_SPI3 GPIO_AF_6
|
|
#endif
|
|
#endif
|
|
|
|
#ifndef SPI1_SCK_PIN
|
|
#define SPI1_NSS_PIN PA4
|
|
#define SPI1_SCK_PIN PA5
|
|
#define SPI1_MISO_PIN PA6
|
|
#define SPI1_MOSI_PIN PA7
|
|
#endif
|
|
|
|
#ifndef SPI2_SCK_PIN
|
|
#define SPI2_NSS_PIN PB12
|
|
#define SPI2_SCK_PIN PB13
|
|
#define SPI2_MISO_PIN PB14
|
|
#define SPI2_MOSI_PIN PB15
|
|
#endif
|
|
|
|
#ifndef SPI3_SCK_PIN
|
|
#define SPI3_NSS_PIN PA15
|
|
#define SPI3_SCK_PIN PB3
|
|
#define SPI3_MISO_PIN PB4
|
|
#define SPI3_MOSI_PIN PB5
|
|
#endif
|
|
|
|
#ifndef SPI1_NSS_PIN
|
|
#define SPI1_NSS_PIN NONE
|
|
#endif
|
|
#ifndef SPI2_NSS_PIN
|
|
#define SPI2_NSS_PIN NONE
|
|
#endif
|
|
#ifndef SPI3_NSS_PIN
|
|
#define SPI3_NSS_PIN NONE
|
|
#endif
|
|
|
|
static spiDevice_t spiHardwareMap[] = {
|
|
#if defined(STM32F1)
|
|
{ .dev = SPI1, .nss = IO_TAG(SPI1_NSS_PIN), .sck = IO_TAG(SPI1_SCK_PIN), .miso = IO_TAG(SPI1_MISO_PIN), .mosi = IO_TAG(SPI1_MOSI_PIN), .rcc = RCC_APB2(SPI1), .af = 0, false },
|
|
{ .dev = SPI2, .nss = IO_TAG(SPI2_NSS_PIN), .sck = IO_TAG(SPI2_SCK_PIN), .miso = IO_TAG(SPI2_MISO_PIN), .mosi = IO_TAG(SPI2_MOSI_PIN), .rcc = RCC_APB1(SPI2), .af = 0, false },
|
|
#else
|
|
{ .dev = SPI1, .nss = IO_TAG(SPI1_NSS_PIN), .sck = IO_TAG(SPI1_SCK_PIN), .miso = IO_TAG(SPI1_MISO_PIN), .mosi = IO_TAG(SPI1_MOSI_PIN), .rcc = RCC_APB2(SPI1), .af = GPIO_AF_SPI1, false },
|
|
{ .dev = SPI2, .nss = IO_TAG(SPI2_NSS_PIN), .sck = IO_TAG(SPI2_SCK_PIN), .miso = IO_TAG(SPI2_MISO_PIN), .mosi = IO_TAG(SPI2_MOSI_PIN), .rcc = RCC_APB1(SPI2), .af = GPIO_AF_SPI2, false },
|
|
#endif
|
|
#if defined(STM32F3) || defined(STM32F4)
|
|
{ .dev = SPI3, .nss = IO_TAG(SPI3_NSS_PIN), .sck = IO_TAG(SPI3_SCK_PIN), .miso = IO_TAG(SPI3_MISO_PIN), .mosi = IO_TAG(SPI3_MOSI_PIN), .rcc = RCC_APB1(SPI3), .af = GPIO_AF_SPI3, false }
|
|
#endif
|
|
};
|
|
|
|
SPIDevice spiDeviceByInstance(SPI_TypeDef *instance)
|
|
{
|
|
if (instance == SPI1)
|
|
return SPIDEV_1;
|
|
|
|
if (instance == SPI2)
|
|
return SPIDEV_2;
|
|
|
|
if (instance == SPI3)
|
|
return SPIDEV_3;
|
|
|
|
return SPIINVALID;
|
|
}
|
|
|
|
void spiInitDevice(SPIDevice device)
|
|
{
|
|
spiDevice_t *spi = &(spiHardwareMap[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, RESOURCE_SPI_SCK, device + 1);
|
|
IOInit(IOGetByTag(spi->miso), OWNER_SPI, RESOURCE_SPI_MISO, device + 1);
|
|
IOInit(IOGetByTag(spi->mosi), OWNER_SPI, RESOURCE_SPI_MOSI, device + 1);
|
|
|
|
#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);
|
|
|
|
if (spi->nss) {
|
|
IOConfigGPIOAF(IOGetByTag(spi->nss), SPI_IO_CS_CFG, spi->af);
|
|
}
|
|
#endif
|
|
#if 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);
|
|
|
|
if (spi->nss) {
|
|
IOConfigGPIO(IOGetByTag(spi->nss), SPI_IO_CS_CFG);
|
|
}
|
|
#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);
|
|
|
|
if (spi->nss) {
|
|
// Drive NSS high to disable connected SPI device.
|
|
IOHi(IOGetByTag(spi->nss));
|
|
}
|
|
}
|
|
|
|
bool spiInit(SPIDevice device)
|
|
{
|
|
switch (device)
|
|
{
|
|
case SPIINVALID:
|
|
return false;
|
|
case SPIDEV_1:
|
|
#ifdef USE_SPI_DEVICE_1
|
|
spiInitDevice(device);
|
|
return true;
|
|
#else
|
|
break;
|
|
#endif
|
|
case SPIDEV_2:
|
|
#ifdef USE_SPI_DEVICE_2
|
|
spiInitDevice(device);
|
|
return true;
|
|
#else
|
|
break;
|
|
#endif
|
|
case SPIDEV_3:
|
|
#if defined(USE_SPI_DEVICE_3) && (defined(STM32F303xC) || defined(STM32F4))
|
|
spiInitDevice(device);
|
|
return true;
|
|
#else
|
|
break;
|
|
#endif
|
|
}
|
|
return false;
|
|
}
|
|
|
|
uint32_t spiTimeoutUserCallback(SPI_TypeDef *instance)
|
|
{
|
|
SPIDevice device = spiDeviceByInstance(instance);
|
|
if (device == SPIINVALID)
|
|
return -1;
|
|
spiHardwareMap[device].errorCount++;
|
|
return spiHardwareMap[device].errorCount;
|
|
}
|
|
|
|
// return uint8_t value or -1 when failure
|
|
uint8_t spiTransferByte(SPI_TypeDef *instance, uint8_t data)
|
|
{
|
|
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, data);
|
|
#else
|
|
SPI_I2S_SendData(instance, data);
|
|
#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, uint8_t *out, const uint8_t *in, int len)
|
|
{
|
|
uint16_t spiTimeout = 1000;
|
|
|
|
uint8_t b;
|
|
instance->DR;
|
|
while (len--) {
|
|
b = in ? *(in++) : 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 (out)
|
|
*(out++) = b;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void spiSetDivisor(SPI_TypeDef *instance, uint16_t divisor)
|
|
{
|
|
#define BR_CLEAR_MASK 0xFFC7
|
|
|
|
uint16_t tempRegister;
|
|
|
|
SPI_Cmd(instance, DISABLE);
|
|
|
|
tempRegister = instance->CR1;
|
|
|
|
switch (divisor) {
|
|
case 2:
|
|
tempRegister &= BR_CLEAR_MASK;
|
|
tempRegister |= SPI_BaudRatePrescaler_2;
|
|
break;
|
|
|
|
case 4:
|
|
tempRegister &= BR_CLEAR_MASK;
|
|
tempRegister |= SPI_BaudRatePrescaler_4;
|
|
break;
|
|
|
|
case 8:
|
|
tempRegister &= BR_CLEAR_MASK;
|
|
tempRegister |= SPI_BaudRatePrescaler_8;
|
|
break;
|
|
|
|
case 16:
|
|
tempRegister &= BR_CLEAR_MASK;
|
|
tempRegister |= SPI_BaudRatePrescaler_16;
|
|
break;
|
|
|
|
case 32:
|
|
tempRegister &= BR_CLEAR_MASK;
|
|
tempRegister |= SPI_BaudRatePrescaler_32;
|
|
break;
|
|
|
|
case 64:
|
|
tempRegister &= BR_CLEAR_MASK;
|
|
tempRegister |= SPI_BaudRatePrescaler_64;
|
|
break;
|
|
|
|
case 128:
|
|
tempRegister &= BR_CLEAR_MASK;
|
|
tempRegister |= SPI_BaudRatePrescaler_128;
|
|
break;
|
|
|
|
case 256:
|
|
tempRegister &= BR_CLEAR_MASK;
|
|
tempRegister |= SPI_BaudRatePrescaler_256;
|
|
break;
|
|
}
|
|
|
|
instance->CR1 = tempRegister;
|
|
|
|
SPI_Cmd(instance, ENABLE);
|
|
}
|
|
|
|
uint16_t spiGetErrorCounter(SPI_TypeDef *instance)
|
|
{
|
|
SPIDevice device = spiDeviceByInstance(instance);
|
|
if (device == SPIINVALID)
|
|
return 0;
|
|
return spiHardwareMap[device].errorCount;
|
|
}
|
|
|
|
void spiResetErrorCounter(SPI_TypeDef *instance)
|
|
{
|
|
SPIDevice device = spiDeviceByInstance(instance);
|
|
if (device != SPIINVALID)
|
|
spiHardwareMap[device].errorCount = 0;
|
|
}
|