ardupilot/libraries/AP_HAL_F4Light/hardware/hal/spi.c

288 lines
7.7 KiB
C

/*
(c) 2017 night_ghost@ykoctpa.ru
based on: datasheet and logical analyser
*/
#pragma GCC optimize ("O2")
#include <spi.h>
#include <hal.h>
/*
* SPI devices
*/
static spi_state spi1_state IN_CCM;
static spi_state spi2_state IN_CCM;
static spi_state spi3_state IN_CCM;
static const spi_dev spi1 = {
.SPIx = SPI1,
.afio = GPIO_AF_SPI1,
.irq = SPI1_IRQn,
.clock = RCC_APB2Periph_SPI1,
.dma = { DMA_CR_CH3, DMA2_STREAM2, DMA2_STREAM3 }, // SPI1, see Errata: DMA2 transfers can spoils data
.state = &spi1_state,
};
/** SPI device 1 */
const spi_dev * const _SPI1 = &spi1;
static const spi_dev spi2 = {
.SPIx = SPI2,
.afio = GPIO_AF_SPI2,
.irq = SPI2_IRQn,
.clock = RCC_APB1Periph_SPI2,
.dma = { DMA_CR_CH0, DMA1_STREAM3, DMA1_STREAM4 }, // SPI2
.state = &spi2_state,
};
/** SPI device 2 */
const spi_dev * const _SPI2 = &spi2;
static const spi_dev spi3 = {
.SPIx = SPI3,
.afio = GPIO_AF_SPI3,
.irq = SPI3_IRQn,
.clock = RCC_APB1Periph_SPI3,
.dma = { DMA_CR_CH0, DMA1_STREAM2, DMA1_STREAM5 }, // SPI3
.state = &spi3_state,
};
/** SPI device 3 */
const spi_dev * const _SPI3 = &spi3;
void spi_init(const spi_dev *dev) {
if (dev->SPIx == SPI1) {
RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1, ENABLE);
RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1, DISABLE);
} else if (dev->SPIx == SPI2) {
RCC_APB1PeriphResetCmd(RCC_APB1Periph_SPI2, ENABLE);
RCC_APB1PeriphResetCmd(RCC_APB1Periph_SPI2, DISABLE);
} else if (dev->SPIx == SPI3) {
RCC_APB1PeriphResetCmd(RCC_APB1Periph_SPI3, ENABLE);
RCC_APB1PeriphResetCmd(RCC_APB1Periph_SPI3, DISABLE);
} else if (dev->SPIx == SPI4) {
RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI4, ENABLE);
RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI4, DISABLE);
} else if (dev->SPIx == SPI5) {
RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI5, ENABLE);
RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI5, DISABLE);
} else if (dev->SPIx == SPI6) {
RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI6, ENABLE);
RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI6, DISABLE);
}
}
/**
* @brief Call a function on each SPI port
* @param fn Function to call.
*/
void spi_foreach(void (*fn)(const spi_dev*)) {
fn(_SPI1);
fn(_SPI2);
fn(_SPI3);
}
void spi_gpio_master_cfg(const spi_dev *dev,
const gpio_dev *comm_dev,
uint8_t sck_bit,
uint8_t miso_bit,
uint8_t mosi_bit) {
/* Configure SCK pin */
gpio_set_mode(comm_dev, sck_bit, GPIO_AF_OUTPUT_PP);
gpio_set_af_mode(comm_dev, sck_bit, dev->afio);
gpio_set_speed(comm_dev, sck_bit, GPIO_speed_100MHz);
/* Configure MISO pin */
gpio_set_mode(comm_dev, miso_bit, GPIO_AF_OUTPUT_OD_PU);
gpio_set_af_mode(comm_dev, miso_bit, dev->afio);
gpio_set_speed(comm_dev, miso_bit, GPIO_speed_100MHz);
/* Configure MOSI pin */
gpio_set_mode(comm_dev, mosi_bit, GPIO_AF_OUTPUT_PP);
gpio_set_af_mode(comm_dev, mosi_bit, dev->afio);
gpio_set_speed(comm_dev, mosi_bit, GPIO_speed_100MHz);
}
void spi_gpio_slave_cfg(const spi_dev *dev,
const gpio_dev *comm_dev,
uint8_t sck_bit,
uint8_t miso_bit,
uint8_t mosi_bit) {
/* Configure SCK pin */
gpio_set_mode(comm_dev, sck_bit, GPIO_INPUT_FLOATING);
/* Configure MISO pin */
gpio_set_mode(comm_dev, miso_bit, GPIO_AF_OUTPUT_PP);
/* Configure MOSI pin */
gpio_set_mode(comm_dev, mosi_bit, GPIO_INPUT_FLOATING);
}
/*
* SPI auxiliary routines
*/
void spi_reconfigure(const spi_dev *dev, uint8_t ismaster, uint16_t baudPrescaler, uint16_t bitorder, uint8_t mode) {
SPI_InitTypeDef SPI_InitStructure;
memset(dev->state, 0, sizeof(spi_state));
spi_disable_irq(dev, SPI_INTERRUPTS_ALL);
spi_init(dev);
spi_peripheral_disable(dev);
/* Enable the SPI clock */
if (dev->SPIx == SPI1)
RCC_APB2PeriphClockCmd(dev->clock, ENABLE);
else if (dev->SPIx == SPI2)
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);
else
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI3, ENABLE);
/* SPI configuration */
SPI_StructInit(&SPI_InitStructure);
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
switch(mode) {
case SPI_MODE_0:
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
break;
case SPI_MODE_1:
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
break;
case SPI_MODE_2:
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
break;
case SPI_MODE_3:
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
break;
default:
break;
}
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
SPI_InitStructure.SPI_BaudRatePrescaler = baudPrescaler;
if (bitorder == LSBFIRST)
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_LSB;
else
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStructure.SPI_CRCPolynomial = 7;
if (ismaster)
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
else
SPI_InitStructure.SPI_Mode = SPI_Mode_Slave;
SPI_Init(dev->SPIx, &SPI_InitStructure);
SPI_Cmd(dev->SPIx, ENABLE);
uint32_t dly=1000;
while (SPI_I2S_GetFlagStatus(dev->SPIx, SPI_BIT_TXE) == RESET) {
dly--;
if(dly==0) break;
}
(void) dev->SPIx->DR;
}
void spi_set_speed(const spi_dev *dev, uint16_t baudPrescaler) {
#define BR_CLEAR_MASK 0xFFC7
SPI_Cmd(dev->SPIx, DISABLE);
dev->SPIx->CR1 = (dev->SPIx->CR1 & BR_CLEAR_MASK) | baudPrescaler;
SPI_Cmd(dev->SPIx, ENABLE);
}
// Transmit command and/or receive result in bidirectional master mode
int spimaster_transfer(const spi_dev *dev,
const uint8_t *txbuf,
uint16_t txcount,
uint8_t *rxbuf,
uint16_t rxcount)
{
uint16_t txc_in=txcount;
// Transfer command data out
while (txcount--){
while (!(dev->SPIx->SR & SPI_BIT_TXE)){ // just for case
if(!spi_is_busy(dev) ) break;
}
dev->SPIx->DR = *txbuf++;
uint16_t dly=1000; // ~20uS so byte already transferred
while (!(dev->SPIx->SR & SPI_BIT_RXNE)) {
if(--dly==0) break;
}
(void) dev->SPIx->DR; // read out unneeded data
}
if(txc_in && rxcount) delay_ns100(5); // small delay between TX and RX, to give the chip time to think over domestic affairs
// Transfer response data in
while (rxcount--){
while (!(dev->SPIx->SR & SPI_BIT_TXE));
dev->SPIx->DR = 0xFF;
uint16_t dly=1000;
while (!(dev->SPIx->SR & SPI_BIT_RXNE)) {
if(--dly==0) break;
}
*rxbuf++ = dev->SPIx->DR;
}
// Wait until the transfer is complete - to not disable CS too early
uint32_t dly=3000;
while (dev->SPIx->SR & SPI_BIT_BSY){ // but datasheet prohibits this usage
dly--;
if(dly==0) break;
}
return 0;
}
static void isr_handler(const spi_dev *dev){
NVIC_ClearPendingIRQ(dev->irq);
if(dev->state->handler) revo_call_handler(dev->state->handler, dev->SPIx->SR);
else { // disable interrupts
spi_disable_irq(dev, SPI_INTERRUPTS_ALL);
}
}
void SPI1_IRQHandler();
void SPI2_IRQHandler();
void SPI3_IRQHandler();
void SPI1_IRQHandler() {
isr_handler(&spi1);
}
void SPI2_IRQHandler() {
isr_handler(&spi2);
}
void SPI3_IRQHandler() {
isr_handler(&spi3);
}