1
0
mirror of https://bitbucket.org/librepilot/librepilot.git synced 2025-02-27 16:54:15 +01:00

Update the F1 SPI library to support multiple NSS lines

This commit is contained in:
James Cotton 2012-01-23 23:27:02 -06:00
parent 5fd458426a
commit aaf1c5dfdd
5 changed files with 597 additions and 583 deletions

View File

@ -1,543 +1,575 @@
/** /**
****************************************************************************** ******************************************************************************
* @addtogroup PIOS PIOS Core hardware abstraction layer * @addtogroup PIOS PIOS Core hardware abstraction layer
* @{ * @{
* @addtogroup PIOS_SPI SPI Functions * @addtogroup PIOS_SPI SPI Functions
* @brief PIOS interface to read and write from SPI ports * @brief PIOS interface to read and write from SPI ports
* @{ * @{
* *
* @file pios_spi.c * @file pios_spi.c
* @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010. * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2012.
* @brief Hardware Abstraction Layer for SPI ports of STM32 * @brief Hardware Abstraction Layer for SPI ports of STM32
* @see The GNU Public License (GPL) Version 3 * @see The GNU Public License (GPL) Version 3
* @notes * @notes
* *
* Note that additional chip select lines can be easily added by using * Note that additional chip select lines can be easily added by using
* the remaining free GPIOs of the core module. Shared SPI ports should be * the remaining free GPIOs of the core module. Shared SPI ports should be
* arbitrated with (FreeRTOS based) Mutexes to avoid collisions! * arbitrated with (FreeRTOS based) Mutexes to avoid collisions!
* *
*****************************************************************************/ *****************************************************************************/
/* /*
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or * the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, but * This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details. * for more details.
* *
* You should have received a copy of the GNU General Public License along * You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., * with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/ */
#include <pios.h> #include <pios.h>
#if defined(PIOS_INCLUDE_SPI) #if defined(PIOS_INCLUDE_SPI)
#include <pios_spi_priv.h> #include <pios_spi_priv.h>
static bool PIOS_SPI_validate(struct pios_spi_dev * com_dev) static bool PIOS_SPI_validate(struct pios_spi_dev * com_dev)
{ {
/* Should check device magic here */ /* Should check device magic here */
return(true); return(true);
} }
#if defined(PIOS_INCLUDE_FREERTOS) #if defined(PIOS_INCLUDE_FREERTOS)
static struct pios_spi_dev * PIOS_SPI_alloc(void) static struct pios_spi_dev * PIOS_SPI_alloc(void)
{ {
return (pvPortMalloc(sizeof(struct pios_spi_dev))); return (pvPortMalloc(sizeof(struct pios_spi_dev)));
} }
#else #else
static struct pios_spi_dev pios_spi_devs[PIOS_SPI_MAX_DEVS]; static struct pios_spi_dev pios_spi_devs[PIOS_SPI_MAX_DEVS];
static uint8_t pios_spi_num_devs; static uint8_t pios_spi_num_devs;
static struct pios_spi_dev * PIOS_SPI_alloc(void) static struct pios_spi_dev * PIOS_SPI_alloc(void)
{ {
if (pios_spi_num_devs >= PIOS_SPI_MAX_DEVS) { if (pios_spi_num_devs >= PIOS_SPI_MAX_DEVS) {
return (NULL); return (NULL);
} }
return (&pios_spi_devs[pios_spi_num_devs++]); return (&pios_spi_devs[pios_spi_num_devs++]);
} }
#endif #endif
/** /**
* Initialises SPI pins * Initialises SPI pins
* \param[in] mode currently only mode 0 supported * \param[in] mode currently only mode 0 supported
* \return < 0 if initialisation failed * \return < 0 if initialisation failed
*/ */
int32_t PIOS_SPI_Init(uint32_t * spi_id, const struct pios_spi_cfg * cfg) int32_t PIOS_SPI_Init(uint32_t * spi_id, const struct pios_spi_cfg * cfg)
{ {
PIOS_Assert(spi_id); uint32_t init_ssel = 0;
PIOS_Assert(cfg);
PIOS_Assert(spi_id);
struct pios_spi_dev * spi_dev; PIOS_Assert(cfg);
spi_dev = (struct pios_spi_dev *) PIOS_SPI_alloc(); struct pios_spi_dev * spi_dev;
if (!spi_dev) goto out_fail;
spi_dev = (struct pios_spi_dev *) PIOS_SPI_alloc();
/* Bind the configuration to the device instance */ if (!spi_dev) goto out_fail;
spi_dev->cfg = cfg;
/* Bind the configuration to the device instance */
#if defined(PIOS_INCLUDE_FREERTOS) spi_dev->cfg = cfg;
vSemaphoreCreateBinary(spi_dev->busy);
xSemaphoreGive(spi_dev->busy); #if defined(PIOS_INCLUDE_FREERTOS)
#else vSemaphoreCreateBinary(spi_dev->busy);
spi_dev->busy = 0; xSemaphoreGive(spi_dev->busy);
#endif #else
spi_dev->busy = 0;
/* Disable callback function */ #endif
spi_dev->callback = NULL;
/* Disable callback function */
/* Set rx/tx dummy bytes to a known value */ spi_dev->callback = NULL;
spi_dev->rx_dummy_byte = 0xFF;
spi_dev->tx_dummy_byte = 0xFF; /* Set rx/tx dummy bytes to a known value */
spi_dev->rx_dummy_byte = 0xFF;
switch (spi_dev->cfg->init.SPI_NSS) { spi_dev->tx_dummy_byte = 0xFF;
case SPI_NSS_Soft:
if (spi_dev->cfg->init.SPI_Mode == SPI_Mode_Master) { switch (spi_dev->cfg->init.SPI_NSS) {
/* We're a master in soft NSS mode, make sure we see NSS high at all times. */ case SPI_NSS_Soft:
SPI_NSSInternalSoftwareConfig(spi_dev->cfg->regs, SPI_NSSInternalSoft_Set); if (spi_dev->cfg->init.SPI_Mode == SPI_Mode_Master) {
/* Since we're driving the SSEL pin in software, ensure that the slave is deselected */ /* We're a master in soft NSS mode, make sure we see NSS high at all times. */
GPIO_SetBits(spi_dev->cfg->ssel.gpio, spi_dev->cfg->ssel.init.GPIO_Pin); SPI_NSSInternalSoftwareConfig(spi_dev->cfg->regs, SPI_NSSInternalSoft_Set);
GPIO_Init(spi_dev->cfg->ssel.gpio, &(spi_dev->cfg->ssel.init)); /* Init as many slave selects as the config advertises. */
} else { init_ssel = spi_dev->cfg->slave_count;
/* We're a slave in soft NSS mode, make sure we see NSS low at all times. */ } else {
SPI_NSSInternalSoftwareConfig(spi_dev->cfg->regs, SPI_NSSInternalSoft_Reset); /* We're a slave in soft NSS mode, make sure we see NSS low at all times. */
} SPI_NSSInternalSoftwareConfig(spi_dev->cfg->regs, SPI_NSSInternalSoft_Reset);
break; }
case SPI_NSS_Hard: break;
/* FIXME: Should this also call SPI_SSOutputCmd()? */ case SPI_NSS_Hard:
GPIO_Init(spi_dev->cfg->ssel.gpio, &(spi_dev->cfg->ssel.init)); /* only legal for single-slave config */
break; PIOS_Assert(spi_dev->cfg->slave_count == 1);
default: init_ssel = 1;
PIOS_Assert(0); /* FIXME: Should this also call SPI_SSOutputCmd()? */
} break;
/* Initialize the GPIO pins */ default:
GPIO_Init(spi_dev->cfg->sclk.gpio, &(spi_dev->cfg->sclk.init)); PIOS_Assert(0);
GPIO_Init(spi_dev->cfg->mosi.gpio, &(spi_dev->cfg->mosi.init)); }
GPIO_Init(spi_dev->cfg->miso.gpio, &(spi_dev->cfg->miso.init));
/* Initialize the GPIO pins */
/* Enable the associated peripheral clock */ GPIO_Init(spi_dev->cfg->sclk.gpio, &(spi_dev->cfg->sclk.init));
switch ((uint32_t) spi_dev->cfg->regs) { GPIO_Init(spi_dev->cfg->mosi.gpio, &(spi_dev->cfg->mosi.init));
case (uint32_t) SPI1: GPIO_Init(spi_dev->cfg->miso.gpio, &(spi_dev->cfg->miso.init));
/* Enable SPI peripheral clock (APB2 == high speed) */ for (uint32_t i = 0; i < init_ssel; i++) {
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE); /* Since we're driving the SSEL pin in software, ensure that the slave is deselected */
break; /* XXX multi-slave support - maybe have another SPI_NSS_ mode? */
case (uint32_t) SPI2: GPIO_SetBits(spi_dev->cfg->ssel[i].gpio, spi_dev->cfg->ssel[i].init.GPIO_Pin);
/* Enable SPI peripheral clock (APB1 == slow speed) */ GPIO_Init(spi_dev->cfg->ssel[i].gpio, (GPIO_InitTypeDef*)&(spi_dev->cfg->ssel[i].init));
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE); }
break;
case (uint32_t) SPI3: /* Enable the associated peripheral clock */
/* Enable SPI peripheral clock (APB1 == slow speed) */ switch ((uint32_t) spi_dev->cfg->regs) {
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI3, ENABLE); case (uint32_t) SPI1:
break; /* Enable SPI peripheral clock (APB2 == high speed) */
} RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
break;
/* Enable DMA clock */ case (uint32_t) SPI2:
RCC_AHBPeriphClockCmd(spi_dev->cfg->dma.ahb_clk, ENABLE); /* Enable SPI peripheral clock (APB1 == slow speed) */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);
/* Configure DMA for SPI Rx */ break;
DMA_Cmd(spi_dev->cfg->dma.rx.channel, DISABLE); case (uint32_t) SPI3:
DMA_Init(spi_dev->cfg->dma.rx.channel, &(spi_dev->cfg->dma.rx.init)); /* Enable SPI peripheral clock (APB1 == slow speed) */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI3, ENABLE);
/* Configure DMA for SPI Tx */ break;
DMA_Cmd(spi_dev->cfg->dma.tx.channel, DISABLE); }
DMA_Init(spi_dev->cfg->dma.tx.channel, &(spi_dev->cfg->dma.tx.init));
/* Enable DMA clock */
/* Initialize the SPI block */ RCC_AHBPeriphClockCmd(spi_dev->cfg->dma.ahb_clk, ENABLE);
SPI_Init(spi_dev->cfg->regs, &(spi_dev->cfg->init));
/* Configure DMA for SPI Rx */
/* Configure CRC calculation */ DMA_Cmd(spi_dev->cfg->dma.rx.channel, DISABLE);
if (spi_dev->cfg->use_crc) { DMA_Init(spi_dev->cfg->dma.rx.channel, &(spi_dev->cfg->dma.rx.init));
SPI_CalculateCRC(spi_dev->cfg->regs, ENABLE);
} else { /* Configure DMA for SPI Tx */
SPI_CalculateCRC(spi_dev->cfg->regs, DISABLE); DMA_Cmd(spi_dev->cfg->dma.tx.channel, DISABLE);
} DMA_Init(spi_dev->cfg->dma.tx.channel, &(spi_dev->cfg->dma.tx.init));
/* Enable SPI */ /* Initialize the SPI block */
SPI_Cmd(spi_dev->cfg->regs, ENABLE); SPI_Init(spi_dev->cfg->regs, &(spi_dev->cfg->init));
/* Enable SPI interrupts to DMA */ /* Configure CRC calculation */
SPI_I2S_DMACmd(spi_dev->cfg->regs, SPI_I2S_DMAReq_Tx | SPI_I2S_DMAReq_Rx, ENABLE); if (spi_dev->cfg->use_crc) {
SPI_CalculateCRC(spi_dev->cfg->regs, ENABLE);
/* Configure DMA interrupt */ } else {
NVIC_Init(&(spi_dev->cfg->dma.irq.init)); SPI_CalculateCRC(spi_dev->cfg->regs, DISABLE);
}
*spi_id = (uint32_t)spi_dev;
return(0); /* Enable SPI */
SPI_Cmd(spi_dev->cfg->regs, ENABLE);
out_fail:
return(-1); /* Enable SPI interrupts to DMA */
} SPI_I2S_DMACmd(spi_dev->cfg->regs, SPI_I2S_DMAReq_Tx | SPI_I2S_DMAReq_Rx, ENABLE);
/** /* Configure DMA interrupt */
* (Re-)initialises SPI peripheral clock rate NVIC_Init(&(spi_dev->cfg->dma.irq.init));
*
* \param[in] spi SPI number (0 or 1) *spi_id = (uint32_t)spi_dev;
* \param[in] spi_prescaler configures the SPI speed: return(0);
* <UL>
* <LI>PIOS_SPI_PRESCALER_2: sets clock rate 27.7~ nS @ 72 MHz (36 MBit/s) (only supported for spi==0, spi1 uses 4 instead) out_fail:
* <LI>PIOS_SPI_PRESCALER_4: sets clock rate 55.5~ nS @ 72 MHz (18 MBit/s) return(-1);
* <LI>PIOS_SPI_PRESCALER_8: sets clock rate 111.1~ nS @ 72 MHz (9 MBit/s) }
* <LI>PIOS_SPI_PRESCALER_16: sets clock rate 222.2~ nS @ 72 MHz (4.5 MBit/s)
* <LI>PIOS_SPI_PRESCALER_32: sets clock rate 444.4~ nS @ 72 MHz (2.25 MBit/s) /**
* <LI>PIOS_SPI_PRESCALER_64: sets clock rate 888.8~ nS @ 72 MHz (1.125 MBit/s) * (Re-)initialises SPI peripheral clock rate
* <LI>PIOS_SPI_PRESCALER_128: sets clock rate 1.7~ nS @ 72 MHz (0.562 MBit/s) *
* <LI>PIOS_SPI_PRESCALER_256: sets clock rate 3.5~ nS @ 72 MHz (0.281 MBit/s) * \param[in] spi SPI number (0 or 1)
* </UL> * \param[in] spi_prescaler configures the SPI speed:
* \return 0 if no error * <UL>
* \return -1 if disabled SPI port selected * <LI>PIOS_SPI_PRESCALER_2: sets clock rate 27.7~ nS @ 72 MHz (36 MBit/s) (only supported for spi==0, spi1 uses 4 instead)
* \return -3 if invalid spi_prescaler selected * <LI>PIOS_SPI_PRESCALER_4: sets clock rate 55.5~ nS @ 72 MHz (18 MBit/s)
*/ * <LI>PIOS_SPI_PRESCALER_8: sets clock rate 111.1~ nS @ 72 MHz (9 MBit/s)
int32_t PIOS_SPI_SetClockSpeed(uint32_t spi_id, SPIPrescalerTypeDef spi_prescaler) * <LI>PIOS_SPI_PRESCALER_16: sets clock rate 222.2~ nS @ 72 MHz (4.5 MBit/s)
{ * <LI>PIOS_SPI_PRESCALER_32: sets clock rate 444.4~ nS @ 72 MHz (2.25 MBit/s)
struct pios_spi_dev * spi_dev = (struct pios_spi_dev *)spi_id; * <LI>PIOS_SPI_PRESCALER_64: sets clock rate 888.8~ nS @ 72 MHz (1.125 MBit/s)
* <LI>PIOS_SPI_PRESCALER_128: sets clock rate 1.7~ nS @ 72 MHz (0.562 MBit/s)
bool valid = PIOS_SPI_validate(spi_dev); * <LI>PIOS_SPI_PRESCALER_256: sets clock rate 3.5~ nS @ 72 MHz (0.281 MBit/s)
PIOS_Assert(valid) * </UL>
* \return 0 if no error
SPI_InitTypeDef SPI_InitStructure; * \return -1 if disabled SPI port selected
* \return -3 if invalid spi_prescaler selected
if (spi_prescaler >= 8) { */
/* Invalid prescaler selected */ int32_t PIOS_SPI_SetClockSpeed(uint32_t spi_id, SPIPrescalerTypeDef spi_prescaler)
return -3; {
} struct pios_spi_dev * spi_dev = (struct pios_spi_dev *)spi_id;
/* Start with a copy of the default configuration for the peripheral */ bool valid = PIOS_SPI_validate(spi_dev);
SPI_InitStructure = spi_dev->cfg->init; PIOS_Assert(valid)
/* Adjust the prescaler for the peripheral's clock */ SPI_InitTypeDef SPI_InitStructure;
SPI_InitStructure.SPI_BaudRatePrescaler = ((uint16_t) spi_prescaler & 7) << 3;
if (spi_prescaler >= 8) {
/* Write back the new configuration */ /* Invalid prescaler selected */
SPI_Init(spi_dev->cfg->regs, &SPI_InitStructure); return -3;
}
PIOS_SPI_TransferByte(spi_id, 0xFF);
return 0; /* Start with a copy of the default configuration for the peripheral */
} SPI_InitStructure = spi_dev->cfg->init;
/** /* Adjust the prescaler for the peripheral's clock */
* Claim the SPI bus semaphore. Calling the SPI functions does not require this SPI_InitStructure.SPI_BaudRatePrescaler = ((uint16_t) spi_prescaler & 7) << 3;
* \param[in] spi SPI number (0 or 1)
* \return 0 if no error /* Write back the new configuration */
* \return -1 if timeout before claiming semaphore SPI_Init(spi_dev->cfg->regs, &SPI_InitStructure);
*/
int32_t PIOS_SPI_ClaimBus(uint32_t spi_id) PIOS_SPI_TransferByte(spi_id, 0xFF);
{ return 0;
#if defined(PIOS_INCLUDE_FREERTOS) }
struct pios_spi_dev * spi_dev = (struct pios_spi_dev *)spi_id;
/**
bool valid = PIOS_SPI_validate(spi_dev); * Claim the SPI bus semaphore. Calling the SPI functions does not require this
PIOS_Assert(valid) * \param[in] spi SPI number (0 or 1)
* \return 0 if no error
if (xSemaphoreTake(spi_dev->busy, 0xffff) != pdTRUE) * \return -1 if timeout before claiming semaphore
return -1; */
#else int32_t PIOS_SPI_ClaimBus(uint32_t spi_id)
struct pios_spi_dev * spi_dev = (struct pios_spi_dev *)spi_id; {
uint32_t timeout = 0xffff; #if defined(PIOS_INCLUDE_FREERTOS)
while((PIOS_SPI_Busy(spi_id) || spi_dev->busy) && --timeout); struct pios_spi_dev * spi_dev = (struct pios_spi_dev *)spi_id;
if(timeout == 0) //timed out
return -1; bool valid = PIOS_SPI_validate(spi_dev);
PIOS_Assert(valid)
PIOS_IRQ_Disable();
if(spi_dev->busy) if (xSemaphoreTake(spi_dev->busy, 0xffff) != pdTRUE)
return -1; return -1;
spi_dev->busy = 1; #else
PIOS_IRQ_Enable(); struct pios_spi_dev * spi_dev = (struct pios_spi_dev *)spi_id;
#endif uint32_t timeout = 0xffff;
return 0; while((PIOS_SPI_Busy(spi_id) || spi_dev->busy) && --timeout);
} if(timeout == 0) //timed out
return -1;
/**
* Release the SPI bus semaphore. Calling the SPI functions does not require this PIOS_IRQ_Disable();
* \param[in] spi SPI number (0 or 1) if(spi_dev->busy)
* \return 0 if no error return -1;
*/ spi_dev->busy = 1;
int32_t PIOS_SPI_ReleaseBus(uint32_t spi_id) PIOS_IRQ_Enable();
{ #endif
#if defined(PIOS_INCLUDE_FREERTOS) return 0;
struct pios_spi_dev * spi_dev = (struct pios_spi_dev *)spi_id; }
bool valid = PIOS_SPI_validate(spi_dev); /**
PIOS_Assert(valid) * Claim the SPI bus semaphore from an ISR. Has no timeout.
* \param[in] spi SPI number (0 or 1)
xSemaphoreGive(spi_dev->busy); * \return 0 if no error
#else * \return -1 if timeout before claiming semaphore
struct pios_spi_dev * spi_dev = (struct pios_spi_dev *)spi_id; */
PIOS_IRQ_Disable(); int32_t PIOS_SPI_ClaimBusISR(uint32_t spi_id)
spi_dev->busy = 0; {
PIOS_IRQ_Enable(); #if defined(PIOS_INCLUDE_FREERTOS)
struct pios_spi_dev * spi_dev = (struct pios_spi_dev *)spi_id;
#endif
return 0; bool valid = PIOS_SPI_validate(spi_dev);
} PIOS_Assert(valid)
/** if (xQueueGenericReceive(( xQueueHandle ) spi_dev->busy, NULL, 0x0000 , pdFALSE ) != pdTRUE)
* Controls the RC (Register Clock alias Chip Select) pin of a SPI port return -1;
* \param[in] spi SPI number (0 or 1) #endif
* \param[in] pin_value 0 or 1 return 0;
* \return 0 if no error }
*/
int32_t PIOS_SPI_RC_PinSet(uint32_t spi_id, uint8_t pin_value) /**
{ * Release the SPI bus semaphore. Calling the SPI functions does not require this
struct pios_spi_dev * spi_dev = (struct pios_spi_dev *)spi_id; * \param[in] spi SPI number (0 or 1)
* \return 0 if no error
bool valid = PIOS_SPI_validate(spi_dev); */
PIOS_Assert(valid) int32_t PIOS_SPI_ReleaseBus(uint32_t spi_id)
{
if (pin_value) { #if defined(PIOS_INCLUDE_FREERTOS)
GPIO_SetBits(spi_dev->cfg->ssel.gpio, spi_dev->cfg->ssel.init.GPIO_Pin); struct pios_spi_dev * spi_dev = (struct pios_spi_dev *)spi_id;
} else {
GPIO_ResetBits(spi_dev->cfg->ssel.gpio, spi_dev->cfg->ssel.init.GPIO_Pin); bool valid = PIOS_SPI_validate(spi_dev);
} PIOS_Assert(valid)
return 0; xSemaphoreGive(spi_dev->busy);
} #else
struct pios_spi_dev * spi_dev = (struct pios_spi_dev *)spi_id;
/** PIOS_IRQ_Disable();
* Transfers a byte to SPI output and reads back the return value from SPI input spi_dev->busy = 0;
* \param[in] spi SPI number (0 or 1) PIOS_IRQ_Enable();
* \param[in] b the byte which should be transfered
*/ #endif
int32_t PIOS_SPI_TransferByte(uint32_t spi_id, uint8_t b) return 0;
{ }
struct pios_spi_dev * spi_dev = (struct pios_spi_dev *)spi_id;
/**
bool valid = PIOS_SPI_validate(spi_dev); * Controls the RC (Register Clock alias Chip Select) pin of a SPI port
PIOS_Assert(valid) * \param[in] spi SPI number (0 or 1)
* \param[in] pin_value 0 or 1
uint8_t dummy; * \return 0 if no error
uint8_t rx_byte; */
int32_t PIOS_SPI_RC_PinSet(uint32_t spi_id, uint32_t slave_id, uint8_t pin_value)
/* {
* Procedure taken from STM32F10xxx Reference Manual section 23.3.5 struct pios_spi_dev * spi_dev = (struct pios_spi_dev *)spi_id;
*/
bool valid = PIOS_SPI_validate(spi_dev);
/* Make sure the RXNE flag is cleared by reading the DR register */ PIOS_Assert(valid)
dummy = spi_dev->cfg->regs->DR; PIOS_Assert(slave_id <= spi_dev->cfg->slave_count)
/* Start the transfer */ /* XXX multi-slave support? */
spi_dev->cfg->regs->DR = b; if (pin_value) {
GPIO_SetBits(spi_dev->cfg->ssel[slave_id].gpio, spi_dev->cfg->ssel[slave_id].init.GPIO_Pin);
/* Wait until there is a byte to read */ } else {
while (!(spi_dev->cfg->regs->SR & SPI_I2S_FLAG_RXNE)) ; GPIO_ResetBits(spi_dev->cfg->ssel[slave_id].gpio, spi_dev->cfg->ssel[slave_id].init.GPIO_Pin);
}
/* Read the rx'd byte */
rx_byte = spi_dev->cfg->regs->DR; return 0;
}
/* Wait until the TXE goes high */
while (!(spi_dev->cfg->regs->SR & SPI_I2S_FLAG_TXE)) ; /**
* Transfers a byte to SPI output and reads back the return value from SPI input
/* Wait for SPI transfer to have fully completed */ * \param[in] spi SPI number (0 or 1)
while (spi_dev->cfg->regs->SR & SPI_I2S_FLAG_BSY) ; * \param[in] b the byte which should be transfered
*/
/* Return received byte */ int32_t PIOS_SPI_TransferByte(uint32_t spi_id, uint8_t b)
return rx_byte; {
} struct pios_spi_dev * spi_dev = (struct pios_spi_dev *)spi_id;
/** bool valid = PIOS_SPI_validate(spi_dev);
* Transfers a block of bytes via DMA. PIOS_Assert(valid)
* \param[in] spi SPI number (0 or 1)
* \param[in] send_buffer pointer to buffer which should be sent.<BR> uint8_t dummy;
* If NULL, 0xff (all-one) will be sent. uint8_t rx_byte;
* \param[in] receive_buffer pointer to buffer which should get the received values.<BR>
* If NULL, received bytes will be discarded. /*
* \param[in] len number of bytes which should be transfered * Procedure taken from STM32F10xxx Reference Manual section 23.3.5
* \param[in] callback pointer to callback function which will be executed */
* from DMA channel interrupt once the transfer is finished.
* If NULL, no callback function will be used, and PIOS_SPI_TransferBlock() will /* Make sure the RXNE flag is cleared by reading the DR register */
* block until the transfer is finished. dummy = spi_dev->cfg->regs->DR;
* \return >= 0 if no error during transfer
* \return -1 if disabled SPI port selected /* Start the transfer */
* \return -3 if function has been called during an ongoing DMA transfer spi_dev->cfg->regs->DR = b;
*/
int32_t PIOS_SPI_TransferBlock(uint32_t spi_id, const uint8_t *send_buffer, uint8_t *receive_buffer, uint16_t len, void *callback) /* Wait until there is a byte to read */
{ while (!(spi_dev->cfg->regs->SR & SPI_I2S_FLAG_RXNE)) ;
struct pios_spi_dev * spi_dev = (struct pios_spi_dev *)spi_id;
/* Read the rx'd byte */
bool valid = PIOS_SPI_validate(spi_dev); rx_byte = spi_dev->cfg->regs->DR;
PIOS_Assert(valid)
/* Wait until the TXE goes high */
DMA_InitTypeDef dma_init; while (!(spi_dev->cfg->regs->SR & SPI_I2S_FLAG_TXE)) ;
/* Exit if ongoing transfer */ /* Wait for SPI transfer to have fully completed */
if (DMA_GetCurrDataCounter(spi_dev->cfg->dma.rx.channel)) { while (spi_dev->cfg->regs->SR & SPI_I2S_FLAG_BSY) ;
return -3;
} /* Return received byte */
return rx_byte;
/* Disable the SPI peripheral */ }
SPI_Cmd(spi_dev->cfg->regs, DISABLE);
/**
/* Disable the DMA channels */ * Transfers a block of bytes via DMA.
DMA_Cmd(spi_dev->cfg->dma.rx.channel, DISABLE); * \param[in] spi SPI number (0 or 1)
DMA_Cmd(spi_dev->cfg->dma.tx.channel, DISABLE); * \param[in] send_buffer pointer to buffer which should be sent.<BR>
* If NULL, 0xff (all-one) will be sent.
/* Set callback function */ * \param[in] receive_buffer pointer to buffer which should get the received values.<BR>
spi_dev->callback = callback; * If NULL, received bytes will be discarded.
* \param[in] len number of bytes which should be transfered
/* * \param[in] callback pointer to callback function which will be executed
* Configure Rx channel * from DMA channel interrupt once the transfer is finished.
*/ * If NULL, no callback function will be used, and PIOS_SPI_TransferBlock() will
* block until the transfer is finished.
/* Start with the default configuration for this peripheral */ * \return >= 0 if no error during transfer
dma_init = spi_dev->cfg->dma.rx.init; * \return -1 if disabled SPI port selected
* \return -3 if function has been called during an ongoing DMA transfer
if (receive_buffer != NULL) { */
/* Enable memory addr. increment - bytes written into receive buffer */ int32_t PIOS_SPI_TransferBlock(uint32_t spi_id, const uint8_t *send_buffer, uint8_t *receive_buffer, uint16_t len, void *callback)
dma_init.DMA_MemoryBaseAddr = (uint32_t) receive_buffer; {
dma_init.DMA_MemoryInc = DMA_MemoryInc_Enable; struct pios_spi_dev * spi_dev = (struct pios_spi_dev *)spi_id;
} else {
/* Disable memory addr. increment - bytes written into dummy buffer */ bool valid = PIOS_SPI_validate(spi_dev);
spi_dev->rx_dummy_byte = 0xFF; PIOS_Assert(valid)
dma_init.DMA_MemoryBaseAddr = (uint32_t) &spi_dev->rx_dummy_byte;
dma_init.DMA_MemoryInc = DMA_MemoryInc_Disable; DMA_InitTypeDef dma_init;
}
if (spi_dev->cfg->use_crc) { /* Exit if ongoing transfer */
/* Make sure the CRC error flag is cleared before we start */ if (DMA_GetCurrDataCounter(spi_dev->cfg->dma.rx.channel)) {
SPI_I2S_ClearFlag(spi_dev->cfg->regs, SPI_FLAG_CRCERR); return -3;
} }
dma_init.DMA_BufferSize = len; /* Disable the SPI peripheral */
DMA_Init(spi_dev->cfg->dma.rx.channel, &(dma_init)); SPI_Cmd(spi_dev->cfg->regs, DISABLE);
/* /* Disable the DMA channels */
* Configure Tx channel DMA_Cmd(spi_dev->cfg->dma.rx.channel, DISABLE);
*/ DMA_Cmd(spi_dev->cfg->dma.tx.channel, DISABLE);
/* Start with the default configuration for this peripheral */ /* Set callback function */
dma_init = spi_dev->cfg->dma.tx.init; spi_dev->callback = callback;
if (send_buffer != NULL) { /*
/* Enable memory addr. increment - bytes written into receive buffer */ * Configure Rx channel
dma_init.DMA_MemoryBaseAddr = (uint32_t) send_buffer; */
dma_init.DMA_MemoryInc = DMA_MemoryInc_Enable;
} else { /* Start with the default configuration for this peripheral */
/* Disable memory addr. increment - bytes written into dummy buffer */ dma_init = spi_dev->cfg->dma.rx.init;
spi_dev->tx_dummy_byte = 0xFF;
dma_init.DMA_MemoryBaseAddr = (uint32_t) &spi_dev->tx_dummy_byte; if (receive_buffer != NULL) {
dma_init.DMA_MemoryInc = DMA_MemoryInc_Disable; /* Enable memory addr. increment - bytes written into receive buffer */
} dma_init.DMA_MemoryBaseAddr = (uint32_t) receive_buffer;
dma_init.DMA_MemoryInc = DMA_MemoryInc_Enable;
if (spi_dev->cfg->use_crc) { } else {
/* The last byte of the payload will be replaced with the CRC8 */ /* Disable memory addr. increment - bytes written into dummy buffer */
dma_init.DMA_BufferSize = len - 1; spi_dev->rx_dummy_byte = 0xFF;
} else { dma_init.DMA_MemoryBaseAddr = (uint32_t) &spi_dev->rx_dummy_byte;
dma_init.DMA_BufferSize = len; dma_init.DMA_MemoryInc = DMA_MemoryInc_Disable;
} }
if (spi_dev->cfg->use_crc) {
DMA_Init(spi_dev->cfg->dma.tx.channel, &(dma_init)); /* Make sure the CRC error flag is cleared before we start */
SPI_I2S_ClearFlag(spi_dev->cfg->regs, SPI_FLAG_CRCERR);
/* Enable DMA interrupt if callback function active */ }
DMA_ITConfig(spi_dev->cfg->dma.rx.channel, DMA_IT_TC, (callback != NULL) ? ENABLE : DISABLE);
dma_init.DMA_BufferSize = len;
/* Flush out the CRC registers */ DMA_Init(spi_dev->cfg->dma.rx.channel, &(dma_init));
SPI_CalculateCRC(spi_dev->cfg->regs, DISABLE);
(void)SPI_GetCRC(spi_dev->cfg->regs, SPI_CRC_Rx); /*
SPI_I2S_ClearFlag(spi_dev->cfg->regs, SPI_FLAG_CRCERR); * Configure Tx channel
*/
/* Make sure to flush out the receive buffer */
(void)SPI_I2S_ReceiveData(spi_dev->cfg->regs); /* Start with the default configuration for this peripheral */
dma_init = spi_dev->cfg->dma.tx.init;
if (spi_dev->cfg->use_crc) {
/* Need a 0->1 transition to reset the CRC logic */ if (send_buffer != NULL) {
SPI_CalculateCRC(spi_dev->cfg->regs, ENABLE); /* Enable memory addr. increment - bytes written into receive buffer */
} dma_init.DMA_MemoryBaseAddr = (uint32_t) send_buffer;
dma_init.DMA_MemoryInc = DMA_MemoryInc_Enable;
/* Start DMA transfers */ } else {
DMA_Cmd(spi_dev->cfg->dma.rx.channel, ENABLE); /* Disable memory addr. increment - bytes written into dummy buffer */
DMA_Cmd(spi_dev->cfg->dma.tx.channel, ENABLE); spi_dev->tx_dummy_byte = 0xFF;
dma_init.DMA_MemoryBaseAddr = (uint32_t) &spi_dev->tx_dummy_byte;
/* Reenable the SPI device */ dma_init.DMA_MemoryInc = DMA_MemoryInc_Disable;
SPI_Cmd(spi_dev->cfg->regs, ENABLE); }
if (callback) { if (spi_dev->cfg->use_crc) {
/* User has requested a callback, don't wait for the transfer to complete. */ /* The last byte of the payload will be replaced with the CRC8 */
return 0; dma_init.DMA_BufferSize = len - 1;
} } else {
dma_init.DMA_BufferSize = len;
/* Wait until all bytes have been transmitted/received */ }
while (DMA_GetCurrDataCounter(spi_dev->cfg->dma.rx.channel));
DMA_Init(spi_dev->cfg->dma.tx.channel, &(dma_init));
/* Wait for the final bytes of the transfer to complete, including CRC byte(s). */
while (!(SPI_I2S_GetFlagStatus(spi_dev->cfg->regs, SPI_I2S_FLAG_TXE))); /* Enable DMA interrupt if callback function active */
DMA_ITConfig(spi_dev->cfg->dma.rx.channel, DMA_IT_TC, (callback != NULL) ? ENABLE : DISABLE);
/* Wait for the final bytes of the transfer to complete, including CRC byte(s). */
while (SPI_I2S_GetFlagStatus(spi_dev->cfg->regs, SPI_I2S_FLAG_BSY)); /* Flush out the CRC registers */
SPI_CalculateCRC(spi_dev->cfg->regs, DISABLE);
/* Check the CRC on the transfer if enabled. */ (void)SPI_GetCRC(spi_dev->cfg->regs, SPI_CRC_Rx);
if (spi_dev->cfg->use_crc) { SPI_I2S_ClearFlag(spi_dev->cfg->regs, SPI_FLAG_CRCERR);
/* Check the SPI CRC error flag */
if (SPI_I2S_GetFlagStatus(spi_dev->cfg->regs, SPI_FLAG_CRCERR)) { /* Make sure to flush out the receive buffer */
return -4; (void)SPI_I2S_ReceiveData(spi_dev->cfg->regs);
}
} if (spi_dev->cfg->use_crc) {
/* Need a 0->1 transition to reset the CRC logic */
/* No error */ SPI_CalculateCRC(spi_dev->cfg->regs, ENABLE);
return 0; }
}
/* Start DMA transfers */
/** DMA_Cmd(spi_dev->cfg->dma.rx.channel, ENABLE);
* Check if a transfer is in progress DMA_Cmd(spi_dev->cfg->dma.tx.channel, ENABLE);
* \param[in] spi SPI number (0 or 1)
* \return >= 0 if no transfer is in progress /* Reenable the SPI device */
* \return -1 if disabled SPI port selected SPI_Cmd(spi_dev->cfg->regs, ENABLE);
* \return -2 if unsupported SPI port selected
* \return -3 if function has been called during an ongoing DMA transfer if (callback) {
*/ /* User has requested a callback, don't wait for the transfer to complete. */
int32_t PIOS_SPI_Busy(uint32_t spi_id) return 0;
{ }
struct pios_spi_dev * spi_dev = (struct pios_spi_dev *)spi_id;
/* Wait until all bytes have been transmitted/received */
bool valid = PIOS_SPI_validate(spi_dev); while (DMA_GetCurrDataCounter(spi_dev->cfg->dma.rx.channel));
PIOS_Assert(valid)
/* Wait for the final bytes of the transfer to complete, including CRC byte(s). */
/* DMA buffer has data or SPI transmit register not empty or SPI is busy*/ while (!(SPI_I2S_GetFlagStatus(spi_dev->cfg->regs, SPI_I2S_FLAG_TXE)));
if (DMA_GetCurrDataCounter(spi_dev->cfg->dma.rx.channel) ||
!SPI_I2S_GetFlagStatus(spi_dev->cfg->regs, SPI_I2S_FLAG_TXE) || /* Wait for the final bytes of the transfer to complete, including CRC byte(s). */
SPI_I2S_GetFlagStatus(spi_dev->cfg->regs, SPI_I2S_FLAG_BSY)) while (SPI_I2S_GetFlagStatus(spi_dev->cfg->regs, SPI_I2S_FLAG_BSY));
{
return -3; /* Check the CRC on the transfer if enabled. */
} if (spi_dev->cfg->use_crc) {
/* Check the SPI CRC error flag */
return(0); if (SPI_I2S_GetFlagStatus(spi_dev->cfg->regs, SPI_FLAG_CRCERR)) {
} return -4;
}
}
void PIOS_SPI_IRQ_Handler(uint32_t spi_id)
{ /* No error */
struct pios_spi_dev * spi_dev = (struct pios_spi_dev *)spi_id; return 0;
}
bool valid = PIOS_SPI_validate(spi_dev);
PIOS_Assert(valid) /**
* Check if a transfer is in progress
DMA_ClearFlag(spi_dev->cfg->dma.irq.flags); * \param[in] spi SPI number (0 or 1)
* \return >= 0 if no transfer is in progress
/* Wait for the final bytes of the transfer to complete, including CRC byte(s). */ * \return -1 if disabled SPI port selected
while (!(SPI_I2S_GetFlagStatus(spi_dev->cfg->regs, SPI_I2S_FLAG_TXE))) ; * \return -2 if unsupported SPI port selected
* \return -3 if function has been called during an ongoing DMA transfer
/* Wait for the final bytes of the transfer to complete, including CRC byte(s). */ */
while (SPI_I2S_GetFlagStatus(spi_dev->cfg->regs, SPI_I2S_FLAG_BSY)) ; int32_t PIOS_SPI_Busy(uint32_t spi_id)
{
if (spi_dev->callback != NULL) { struct pios_spi_dev * spi_dev = (struct pios_spi_dev *)spi_id;
bool crc_ok = TRUE;
uint8_t crc_val; bool valid = PIOS_SPI_validate(spi_dev);
PIOS_Assert(valid)
if (SPI_I2S_GetFlagStatus(spi_dev->cfg->regs, SPI_FLAG_CRCERR)) {
crc_ok = FALSE; /* DMA buffer has data or SPI transmit register not empty or SPI is busy*/
SPI_I2S_ClearFlag(spi_dev->cfg->regs, SPI_FLAG_CRCERR); if (DMA_GetCurrDataCounter(spi_dev->cfg->dma.rx.channel) ||
} !SPI_I2S_GetFlagStatus(spi_dev->cfg->regs, SPI_I2S_FLAG_TXE) ||
crc_val = SPI_GetCRC(spi_dev->cfg->regs, SPI_CRC_Rx); SPI_I2S_GetFlagStatus(spi_dev->cfg->regs, SPI_I2S_FLAG_BSY))
spi_dev->callback(crc_ok, crc_val); {
} return -3;
} }
#endif return(0);
}
/**
* @}
* @} void PIOS_SPI_IRQ_Handler(uint32_t spi_id)
*/ {
struct pios_spi_dev * spi_dev = (struct pios_spi_dev *)spi_id;
bool valid = PIOS_SPI_validate(spi_dev);
PIOS_Assert(valid)
DMA_ClearFlag(spi_dev->cfg->dma.irq.flags);
/* Wait for the final bytes of the transfer to complete, including CRC byte(s). */
while (!(SPI_I2S_GetFlagStatus(spi_dev->cfg->regs, SPI_I2S_FLAG_TXE))) ;
/* Wait for the final bytes of the transfer to complete, including CRC byte(s). */
while (SPI_I2S_GetFlagStatus(spi_dev->cfg->regs, SPI_I2S_FLAG_BSY)) ;
if (spi_dev->callback != NULL) {
bool crc_ok = TRUE;
uint8_t crc_val;
if (SPI_I2S_GetFlagStatus(spi_dev->cfg->regs, SPI_FLAG_CRCERR)) {
crc_ok = FALSE;
SPI_I2S_ClearFlag(spi_dev->cfg->regs, SPI_FLAG_CRCERR);
}
crc_val = SPI_GetCRC(spi_dev->cfg->regs, SPI_CRC_Rx);
spi_dev->callback(crc_ok, crc_val);
}
}
#endif
/**
* @}
* @}
*/

View File

@ -50,7 +50,7 @@ static bool PIOS_SPI_validate(struct pios_spi_dev * com_dev)
return(true); return(true);
} }
#if defined(PIOS_INCLUDE_FREERTOS) && 0 #if defined(PIOS_INCLUDE_FREERTOS)
static struct pios_spi_dev * PIOS_SPI_alloc(void) static struct pios_spi_dev * PIOS_SPI_alloc(void)
{ {
return (malloc(sizeof(struct pios_spi_dev))); return (malloc(sizeof(struct pios_spi_dev)));
@ -101,27 +101,27 @@ int32_t PIOS_SPI_Init(uint32_t * spi_id, const struct pios_spi_cfg * cfg)
spi_dev->tx_dummy_byte = 0xFF; spi_dev->tx_dummy_byte = 0xFF;
switch (spi_dev->cfg->init.SPI_NSS) { switch (spi_dev->cfg->init.SPI_NSS) {
case SPI_NSS_Soft: case SPI_NSS_Soft:
if (spi_dev->cfg->init.SPI_Mode == SPI_Mode_Master) { if (spi_dev->cfg->init.SPI_Mode == SPI_Mode_Master) {
/* We're a master in soft NSS mode, make sure we see NSS high at all times. */ /* We're a master in soft NSS mode, make sure we see NSS high at all times. */
SPI_NSSInternalSoftwareConfig(spi_dev->cfg->regs, SPI_NSSInternalSoft_Set); SPI_NSSInternalSoftwareConfig(spi_dev->cfg->regs, SPI_NSSInternalSoft_Set);
/* Init as many slave selects as the config advertises. */ /* Init as many slave selects as the config advertises. */
init_ssel = spi_dev->cfg->slave_count; init_ssel = spi_dev->cfg->slave_count;
} else { } else {
/* We're a slave in soft NSS mode, make sure we see NSS low at all times. */ /* We're a slave in soft NSS mode, make sure we see NSS low at all times. */
SPI_NSSInternalSoftwareConfig(spi_dev->cfg->regs, SPI_NSSInternalSoft_Reset); SPI_NSSInternalSoftwareConfig(spi_dev->cfg->regs, SPI_NSSInternalSoft_Reset);
} }
break; break;
case SPI_NSS_Hard: case SPI_NSS_Hard:
/* only legal for single-slave config */ /* only legal for single-slave config */
PIOS_Assert(spi_dev->cfg->slave_count == 1); PIOS_Assert(spi_dev->cfg->slave_count == 1);
init_ssel = 1; init_ssel = 1;
/* FIXME: Should this also call SPI_SSOutputCmd()? */ /* FIXME: Should this also call SPI_SSOutputCmd()? */
break; break;
default: default:
PIOS_Assert(0); PIOS_Assert(0);
} }
/* Initialize the GPIO pins */ /* Initialize the GPIO pins */

View File

@ -44,11 +44,7 @@ typedef enum {
/* Public Functions */ /* Public Functions */
extern int32_t PIOS_SPI_SetClockSpeed(uint32_t spi_id, SPIPrescalerTypeDef spi_prescaler); extern int32_t PIOS_SPI_SetClockSpeed(uint32_t spi_id, SPIPrescalerTypeDef spi_prescaler);
#if defined(STM32F2XX) || defined(STM32F4XX) /* XXX harmonise these */
extern int32_t PIOS_SPI_RC_PinSet(uint32_t spi_id, uint32_t slave_id, uint8_t pin_value); extern int32_t PIOS_SPI_RC_PinSet(uint32_t spi_id, uint32_t slave_id, uint8_t pin_value);
#else
extern int32_t PIOS_SPI_RC_PinSet(uint32_t spi_id, uint8_t pin_value);
#endif
extern int32_t PIOS_SPI_TransferByte(uint32_t spi_id, uint8_t b); extern int32_t PIOS_SPI_TransferByte(uint32_t spi_id, uint8_t b);
extern int32_t PIOS_SPI_TransferBlock(uint32_t spi_id, const uint8_t *send_buffer, uint8_t *receive_buffer, uint16_t len, void *callback); extern int32_t PIOS_SPI_TransferBlock(uint32_t spi_id, const uint8_t *send_buffer, uint8_t *receive_buffer, uint16_t len, void *callback);
extern int32_t PIOS_SPI_Busy(uint32_t spi_id); extern int32_t PIOS_SPI_Busy(uint32_t spi_id);

View File

@ -34,8 +34,6 @@
#include <pios.h> #include <pios.h>
#include <pios_stm32.h> #include <pios_stm32.h>
/* XXX these two should be reconciled - separate for now to avoid breaking other targets */
#if defined(STM32F2XX) || defined(STM32F4XX)
struct pios_spi_cfg { struct pios_spi_cfg {
SPI_TypeDef *regs; SPI_TypeDef *regs;
uint32_t remap; /* GPIO_Remap_* or GPIO_AF_* */ uint32_t remap; /* GPIO_Remap_* or GPIO_AF_* */
@ -48,18 +46,6 @@ struct pios_spi_cfg {
uint32_t slave_count; uint32_t slave_count;
struct stm32_gpio ssel[]; struct stm32_gpio ssel[];
}; };
#else
struct pios_spi_cfg {
SPI_TypeDef *regs;
SPI_InitTypeDef init;
bool use_crc;
struct stm32_dma dma;
struct stm32_gpio ssel;
struct stm32_gpio sclk;
struct stm32_gpio miso;
struct stm32_gpio mosi;
};
#endif
struct pios_spi_dev { struct pios_spi_dev {
const struct pios_spi_cfg * cfg; const struct pios_spi_cfg * cfg;