450 lines
12 KiB
C
450 lines
12 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 <string.h>
|
|
|
|
#include <platform.h>
|
|
|
|
#ifdef USE_SPI
|
|
|
|
#include "common/utils.h"
|
|
|
|
#include "drivers/bus.h"
|
|
#include "drivers/bus_spi.h"
|
|
#include "drivers/bus_spi_impl.h"
|
|
#include "drivers/dma.h"
|
|
#include "drivers/io.h"
|
|
#include "drivers/nvic.h"
|
|
#include "drivers/rcc.h"
|
|
|
|
spiDevice_t spiDevice[SPIDEV_COUNT];
|
|
|
|
#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 SPI4_SCK_PIN
|
|
#define SPI4_NSS_PIN PA15
|
|
#define SPI4_SCK_PIN PB3
|
|
#define SPI4_MISO_PIN PB4
|
|
#define SPI4_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
|
|
#ifndef SPI4_NSS_PIN
|
|
#define SPI4_NSS_PIN NONE
|
|
#endif
|
|
|
|
#define SPI_DEFAULT_TIMEOUT 10
|
|
|
|
SPIDevice spiDeviceByInstance(SPI_TypeDef *instance)
|
|
{
|
|
#ifdef USE_SPI_DEVICE_1
|
|
if (instance == SPI1)
|
|
return SPIDEV_1;
|
|
#endif
|
|
|
|
#ifdef USE_SPI_DEVICE_2
|
|
if (instance == SPI2)
|
|
return SPIDEV_2;
|
|
#endif
|
|
|
|
#ifdef USE_SPI_DEVICE_3
|
|
if (instance == SPI3)
|
|
return SPIDEV_3;
|
|
#endif
|
|
|
|
#ifdef USE_SPI_DEVICE_4
|
|
if (instance == SPI4)
|
|
return SPIDEV_4;
|
|
#endif
|
|
|
|
return SPIINVALID;
|
|
}
|
|
|
|
SPI_HandleTypeDef* spiHandleByInstance(SPI_TypeDef *instance)
|
|
{
|
|
return &spiDevice[spiDeviceByInstance(instance)].hspi;
|
|
}
|
|
|
|
SPI_TypeDef *spiInstanceByDevice(SPIDevice device)
|
|
{
|
|
if (device >= SPIDEV_COUNT) {
|
|
return NULL;
|
|
}
|
|
|
|
return spiDevice[device].dev;
|
|
}
|
|
|
|
DMA_HandleTypeDef* dmaHandleByInstance(SPI_TypeDef *instance)
|
|
{
|
|
return &spiDevice[spiDeviceByInstance(instance)].hdma;
|
|
}
|
|
|
|
void SPI1_IRQHandler(void)
|
|
{
|
|
HAL_SPI_IRQHandler(&spiDevice[SPIDEV_1].hspi);
|
|
}
|
|
|
|
void SPI2_IRQHandler(void)
|
|
{
|
|
HAL_SPI_IRQHandler(&spiDevice[SPIDEV_2].hspi);
|
|
}
|
|
|
|
void SPI3_IRQHandler(void)
|
|
{
|
|
HAL_SPI_IRQHandler(&spiDevice[SPIDEV_3].hspi);
|
|
}
|
|
|
|
void SPI4_IRQHandler(void)
|
|
{
|
|
HAL_SPI_IRQHandler(&spiDevice[SPIDEV_4].hspi);
|
|
}
|
|
|
|
|
|
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
|
|
#ifdef MPU6500_SPI_INSTANCE
|
|
if (spi->dev == MPU6500_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(STM32F7)
|
|
if (spi->leadingEdge == true)
|
|
IOConfigGPIOAF(IOGetByTag(spi->sck), SPI_IO_AF_SCK_CFG_LOW, spi->sckAF);
|
|
else
|
|
IOConfigGPIOAF(IOGetByTag(spi->sck), SPI_IO_AF_SCK_CFG_HIGH, spi->sckAF);
|
|
IOConfigGPIOAF(IOGetByTag(spi->miso), SPI_IO_AF_MISO_CFG, spi->misoAF);
|
|
IOConfigGPIOAF(IOGetByTag(spi->mosi), SPI_IO_AF_CFG, spi->mosiAF);
|
|
#endif
|
|
#if defined(STM32F3) || defined(STM32F4)
|
|
if (spi->leadingEdge == true)
|
|
IOConfigGPIOAF(IOGetByTag(spi->sck), SPI_IO_AF_SCK_CFG_LOW, spi->af);
|
|
else
|
|
IOConfigGPIOAF(IOGetByTag(spi->sck), SPI_IO_AF_SCK_CFG_HIGH, spi->af);
|
|
IOConfigGPIOAF(IOGetByTag(spi->miso), SPI_IO_AF_MISO_CFG, spi->af);
|
|
IOConfigGPIOAF(IOGetByTag(spi->mosi), SPI_IO_AF_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);
|
|
#endif
|
|
spiDevice[device].hspi.Instance = spi->dev;
|
|
// Init SPI hardware
|
|
HAL_SPI_DeInit(&spiDevice[device].hspi);
|
|
|
|
spiDevice[device].hspi.Init.Mode = SPI_MODE_MASTER;
|
|
spiDevice[device].hspi.Init.Direction = SPI_DIRECTION_2LINES;
|
|
spiDevice[device].hspi.Init.DataSize = SPI_DATASIZE_8BIT;
|
|
spiDevice[device].hspi.Init.NSS = SPI_NSS_SOFT;
|
|
spiDevice[device].hspi.Init.FirstBit = SPI_FIRSTBIT_MSB;
|
|
spiDevice[device].hspi.Init.CRCPolynomial = 7;
|
|
spiDevice[device].hspi.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_256;
|
|
spiDevice[device].hspi.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
|
|
spiDevice[device].hspi.Init.TIMode = SPI_TIMODE_DISABLED;
|
|
|
|
if (spi->leadingEdge) {
|
|
spiDevice[device].hspi.Init.CLKPolarity = SPI_POLARITY_LOW;
|
|
spiDevice[device].hspi.Init.CLKPhase = SPI_PHASE_1EDGE;
|
|
}
|
|
else {
|
|
spiDevice[device].hspi.Init.CLKPolarity = SPI_POLARITY_HIGH;
|
|
spiDevice[device].hspi.Init.CLKPhase = SPI_PHASE_2EDGE;
|
|
}
|
|
|
|
if (HAL_SPI_Init(&spiDevice[device].hspi) == HAL_OK)
|
|
{
|
|
}
|
|
}
|
|
|
|
bool spiInit(SPIDevice device)
|
|
{
|
|
switch (device) {
|
|
case SPIINVALID:
|
|
return false;
|
|
case SPIDEV_1:
|
|
#if defined(USE_SPI_DEVICE_1)
|
|
spiInitDevice(device);
|
|
return true;
|
|
#else
|
|
break;
|
|
#endif
|
|
case SPIDEV_2:
|
|
#if defined(USE_SPI_DEVICE_2)
|
|
spiInitDevice(device);
|
|
return true;
|
|
#else
|
|
break;
|
|
#endif
|
|
case SPIDEV_3:
|
|
#if defined(USE_SPI_DEVICE_3)
|
|
spiInitDevice(device);
|
|
return true;
|
|
#else
|
|
break;
|
|
#endif
|
|
case SPIDEV_4:
|
|
#if defined(USE_SPI_DEVICE_4)
|
|
spiInitDevice(device);
|
|
return true;
|
|
#else
|
|
break;
|
|
#endif
|
|
}
|
|
return false;
|
|
}
|
|
|
|
uint32_t spiTimeoutUserCallback(SPI_TypeDef *instance)
|
|
{
|
|
SPIDevice device = spiDeviceByInstance(instance);
|
|
if (device == SPIINVALID) {
|
|
return -1;
|
|
}
|
|
spiDevice[device].errorCount++;
|
|
return spiDevice[device].errorCount;
|
|
}
|
|
|
|
/**
|
|
* Return true if the bus is currently in the middle of a transmission.
|
|
*/
|
|
bool spiIsBusBusy(SPI_TypeDef *instance)
|
|
{
|
|
SPIDevice device = spiDeviceByInstance(instance);
|
|
if (spiDevice[device].hspi.State == HAL_SPI_STATE_BUSY)
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
bool spiTransfer(SPI_TypeDef *instance, const uint8_t *txData, uint8_t *rxData, int len)
|
|
{
|
|
SPIDevice device = spiDeviceByInstance(instance);
|
|
HAL_StatusTypeDef status;
|
|
|
|
if (!rxData) { // Tx only
|
|
status = HAL_SPI_Transmit(&spiDevice[device].hspi, txData, len, SPI_DEFAULT_TIMEOUT);
|
|
} else if (!txData) { // Rx only
|
|
status = HAL_SPI_Receive(&spiDevice[device].hspi, rxData, len, SPI_DEFAULT_TIMEOUT);
|
|
} else { // Tx and Rx
|
|
status = HAL_SPI_TransmitReceive(&spiDevice[device].hspi, txData, rxData, len, SPI_DEFAULT_TIMEOUT);
|
|
}
|
|
if (status != HAL_OK) {
|
|
spiTimeoutUserCallback(instance);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool spiBusReadBuffer(const busDevice_t *bus, uint8_t *out, int len)
|
|
{
|
|
const HAL_StatusTypeDef status = HAL_SPI_Receive(bus->busdev_u.spi.handle, out, len, SPI_DEFAULT_TIMEOUT);
|
|
if (status != HAL_OK) {
|
|
spiTimeoutUserCallback(bus->busdev_u.spi.instance);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
uint8_t spiTransferByte(SPI_TypeDef *instance, uint8_t txByte)
|
|
{
|
|
uint8_t rxByte;
|
|
|
|
spiTransfer(instance, &txByte, &rxByte, 1);
|
|
return rxByte;
|
|
}
|
|
|
|
static uint8_t spiBusTransferByte(const busDevice_t *bus, uint8_t in)
|
|
{
|
|
const HAL_StatusTypeDef status = HAL_SPI_TransmitReceive(bus->busdev_u.spi.handle, &in, &in, 1, SPI_DEFAULT_TIMEOUT);
|
|
if (status != HAL_OK) {
|
|
spiTimeoutUserCallback(bus->busdev_u.spi.instance);
|
|
}
|
|
return in;
|
|
}
|
|
|
|
bool spiBusTransfer(const busDevice_t *bus, const uint8_t *txData, uint8_t *rxData, int len)
|
|
{
|
|
IOLo(bus->busdev_u.spi.csnPin);
|
|
const HAL_StatusTypeDef status = HAL_SPI_TransmitReceive(bus->busdev_u.spi.handle, txData, rxData, len, SPI_DEFAULT_TIMEOUT);
|
|
IOHi(bus->busdev_u.spi.csnPin);
|
|
if (status != HAL_OK) {
|
|
spiTimeoutUserCallback(bus->busdev_u.spi.instance);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void spiSetDivisor(SPI_TypeDef *instance, uint16_t divisor)
|
|
{
|
|
SPIDevice device = spiDeviceByInstance(instance);
|
|
if (HAL_SPI_DeInit(&spiDevice[device].hspi) == HAL_OK)
|
|
{
|
|
}
|
|
|
|
spiDevice[device].hspi.Init.BaudRatePrescaler = (uint8_t []) {
|
|
0, 0,
|
|
SPI_BAUDRATEPRESCALER_2, SPI_BAUDRATEPRESCALER_4,
|
|
SPI_BAUDRATEPRESCALER_8, SPI_BAUDRATEPRESCALER_16,
|
|
SPI_BAUDRATEPRESCALER_32, SPI_BAUDRATEPRESCALER_64,
|
|
SPI_BAUDRATEPRESCALER_128, SPI_BAUDRATEPRESCALER_256
|
|
}[ffs(divisor | 0x100)];
|
|
|
|
if (HAL_SPI_Init(&spiDevice[device].hspi) == HAL_OK)
|
|
{
|
|
}
|
|
}
|
|
|
|
uint16_t spiGetErrorCounter(SPI_TypeDef *instance)
|
|
{
|
|
SPIDevice device = spiDeviceByInstance(instance);
|
|
if (device == SPIINVALID) {
|
|
return 0;
|
|
}
|
|
return spiDevice[device].errorCount;
|
|
}
|
|
|
|
void spiResetErrorCounter(SPI_TypeDef *instance)
|
|
{
|
|
SPIDevice device = spiDeviceByInstance(instance);
|
|
if (device != SPIINVALID) {
|
|
spiDevice[device].errorCount = 0;
|
|
}
|
|
}
|
|
|
|
bool spiBusWriteRegister(const busDevice_t *bus, uint8_t reg, uint8_t data)
|
|
{
|
|
IOLo(bus->busdev_u.spi.csnPin);
|
|
spiBusTransferByte(bus, reg);
|
|
spiBusTransferByte(bus, data);
|
|
IOHi(bus->busdev_u.spi.csnPin);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool spiBusReadRegisterBuffer(const busDevice_t *bus, uint8_t reg, uint8_t *data, uint8_t length)
|
|
{
|
|
IOLo(bus->busdev_u.spi.csnPin);
|
|
spiBusTransferByte(bus, reg | 0x80); // read transaction
|
|
spiBusReadBuffer(bus, data, length);
|
|
IOHi(bus->busdev_u.spi.csnPin);
|
|
|
|
return true;
|
|
}
|
|
|
|
uint8_t spiBusReadRegister(const busDevice_t *bus, uint8_t reg)
|
|
{
|
|
uint8_t data;
|
|
IOLo(bus->busdev_u.spi.csnPin);
|
|
spiBusTransferByte(bus, reg | 0x80); // read transaction
|
|
spiBusReadBuffer(bus, &data, 1);
|
|
IOHi(bus->busdev_u.spi.csnPin);
|
|
|
|
return data;
|
|
}
|
|
|
|
void spiBusSetInstance(busDevice_t *bus, SPI_TypeDef *instance)
|
|
{
|
|
bus->busdev_u.spi.instance = instance;
|
|
bus->busdev_u.spi.handle = spiHandleByInstance(instance);
|
|
}
|
|
|
|
void dmaSPIIRQHandler(dmaChannelDescriptor_t* descriptor)
|
|
{
|
|
SPIDevice device = descriptor->userParam;
|
|
if (device != SPIINVALID) {
|
|
HAL_DMA_IRQHandler(&spiDevice[device].hdma);
|
|
}
|
|
}
|
|
|
|
|
|
DMA_HandleTypeDef* spiSetDMATransmit(DMA_Stream_TypeDef *Stream, uint32_t Channel, SPI_TypeDef *Instance, const uint8_t *pData, uint16_t Size)
|
|
{
|
|
SPIDevice device = spiDeviceByInstance(Instance);
|
|
|
|
spiDevice[device].hdma.Instance = Stream;
|
|
spiDevice[device].hdma.Init.Channel = Channel;
|
|
spiDevice[device].hdma.Init.Direction = DMA_MEMORY_TO_PERIPH;
|
|
spiDevice[device].hdma.Init.PeriphInc = DMA_PINC_DISABLE;
|
|
spiDevice[device].hdma.Init.MemInc = DMA_MINC_ENABLE;
|
|
spiDevice[device].hdma.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
|
|
spiDevice[device].hdma.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
|
|
spiDevice[device].hdma.Init.Mode = DMA_NORMAL;
|
|
spiDevice[device].hdma.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
|
|
spiDevice[device].hdma.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_1QUARTERFULL;
|
|
spiDevice[device].hdma.Init.PeriphBurst = DMA_PBURST_SINGLE;
|
|
spiDevice[device].hdma.Init.MemBurst = DMA_MBURST_SINGLE;
|
|
spiDevice[device].hdma.Init.Priority = DMA_PRIORITY_LOW;
|
|
|
|
HAL_DMA_DeInit(&spiDevice[device].hdma);
|
|
HAL_DMA_Init(&spiDevice[device].hdma);
|
|
|
|
__HAL_DMA_ENABLE(&spiDevice[device].hdma);
|
|
__HAL_SPI_ENABLE(&spiDevice[device].hspi);
|
|
|
|
/* Associate the initialized DMA handle to the spi handle */
|
|
__HAL_LINKDMA(&spiDevice[device].hspi, hdmatx, spiDevice[device].hdma);
|
|
|
|
// DMA TX Interrupt
|
|
dmaSetHandler(spiDevice[device].dmaIrqHandler, dmaSPIIRQHandler, NVIC_BUILD_PRIORITY(3, 0), (uint32_t)device);
|
|
|
|
//HAL_CLEANCACHE(pData,Size);
|
|
// And Transmit
|
|
HAL_SPI_Transmit_DMA(&spiDevice[device].hspi, CONST_CAST(uint8_t*, pData), Size);
|
|
|
|
return &spiDevice[device].hdma;
|
|
}
|
|
#endif
|