1
0
mirror of https://bitbucket.org/librepilot/librepilot.git synced 2025-02-05 21:52:10 +01:00

PIOS SPI: Added a function to detect if the channel is busy

git-svn-id: svn://svn.openpilot.org/OpenPilot/trunk@1829 ebee16cc-31ac-478f-84a7-5cbb03baadba
This commit is contained in:
les 2010-10-01 16:52:20 +00:00 committed by les
parent d2c1014e29
commit 498f3d0fa6
2 changed files with 547 additions and 511 deletions

View File

@ -1,452 +1,487 @@
/** /**
****************************************************************************** ******************************************************************************
* @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) 2010.
* Parts by Thorsten Klose (tk@midibox.org) (tk@midibox.org) * Parts by Thorsten Klose (tk@midibox.org) (tk@midibox.org)
* @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 struct pios_spi_dev *find_spi_dev_by_id(uint8_t spi) static struct pios_spi_dev *find_spi_dev_by_id(uint8_t spi)
{ {
if (spi >= pios_spi_num_devices) { if (spi >= pios_spi_num_devices) {
/* Undefined SPI port for this board (see pios_board.c) */ /* Undefined SPI port for this board (see pios_board.c) */
return NULL; return NULL;
} }
/* Get a handle for the device configuration */ /* Get a handle for the device configuration */
return &(pios_spi_devs[spi]); return &(pios_spi_devs[spi]);
} }
/** /**
* 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(void) int32_t PIOS_SPI_Init(void)
{ {
struct pios_spi_dev *spi_dev; struct pios_spi_dev *spi_dev;
uint8_t i; uint8_t i;
for (i = 0; i < pios_spi_num_devices; i++) { for (i = 0; i < pios_spi_num_devices; i++) {
/* Get a handle for the device configuration */ /* Get a handle for the device configuration */
spi_dev = find_spi_dev_by_id(i); spi_dev = find_spi_dev_by_id(i);
PIOS_DEBUG_Assert(spi_dev); PIOS_DEBUG_Assert(spi_dev);
/* Disable callback function */ /* Disable callback function */
spi_dev->callback = NULL; spi_dev->callback = NULL;
/* Set rx/tx dummy bytes to a known value */ /* Set rx/tx dummy bytes to a known value */
spi_dev->rx_dummy_byte = 0xFF; spi_dev->rx_dummy_byte = 0xFF;
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);
/* Since we're driving the SSEL pin in software, ensure that the slave is deselected */ /* Since we're driving the SSEL pin in software, ensure that the slave is deselected */
GPIO_SetBits(spi_dev->cfg->ssel.gpio, spi_dev->cfg->ssel.init.GPIO_Pin); GPIO_SetBits(spi_dev->cfg->ssel.gpio, spi_dev->cfg->ssel.init.GPIO_Pin);
GPIO_Init(spi_dev->cfg->ssel.gpio, &(spi_dev->cfg->ssel.init)); GPIO_Init(spi_dev->cfg->ssel.gpio, &(spi_dev->cfg->ssel.init));
} 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:
/* FIXME: Should this also call SPI_SSOutputCmd()? */ /* FIXME: Should this also call SPI_SSOutputCmd()? */
GPIO_Init(spi_dev->cfg->ssel.gpio, &(spi_dev->cfg->ssel.init)); GPIO_Init(spi_dev->cfg->ssel.gpio, &(spi_dev->cfg->ssel.init));
break; break;
default: default:
PIOS_DEBUG_Assert(0); PIOS_DEBUG_Assert(0);
} }
/* Initialize the GPIO pins */ /* Initialize the GPIO pins */
GPIO_Init(spi_dev->cfg->sclk.gpio, &(spi_dev->cfg->sclk.init)); GPIO_Init(spi_dev->cfg->sclk.gpio, &(spi_dev->cfg->sclk.init));
GPIO_Init(spi_dev->cfg->mosi.gpio, &(spi_dev->cfg->mosi.init)); GPIO_Init(spi_dev->cfg->mosi.gpio, &(spi_dev->cfg->mosi.init));
GPIO_Init(spi_dev->cfg->miso.gpio, &(spi_dev->cfg->miso.init)); GPIO_Init(spi_dev->cfg->miso.gpio, &(spi_dev->cfg->miso.init));
/* Enable the associated peripheral clock */ /* Enable the associated peripheral clock */
switch ((uint32_t) spi_dev->cfg->regs) { switch ((uint32_t) spi_dev->cfg->regs) {
case (uint32_t) SPI1: case (uint32_t) SPI1:
/* Enable SPI peripheral clock (APB2 == high speed) */ /* Enable SPI peripheral clock (APB2 == high speed) */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
break; break;
case (uint32_t) SPI2: case (uint32_t) SPI2:
/* Enable SPI peripheral clock (APB1 == slow speed) */ /* Enable SPI peripheral clock (APB1 == slow speed) */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);
break; break;
case (uint32_t) SPI3: case (uint32_t) SPI3:
/* Enable SPI peripheral clock (APB1 == slow speed) */ /* Enable SPI peripheral clock (APB1 == slow speed) */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI3, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI3, ENABLE);
break; break;
} }
/* Enable DMA clock */ /* Enable DMA clock */
RCC_AHBPeriphClockCmd(spi_dev->cfg->dma.ahb_clk, ENABLE); RCC_AHBPeriphClockCmd(spi_dev->cfg->dma.ahb_clk, ENABLE);
/* Configure DMA for SPI Rx */ /* Configure DMA for SPI Rx */
DMA_Cmd(spi_dev->cfg->dma.rx.channel, DISABLE); DMA_Cmd(spi_dev->cfg->dma.rx.channel, DISABLE);
DMA_Init(spi_dev->cfg->dma.rx.channel, &(spi_dev->cfg->dma.rx.init)); DMA_Init(spi_dev->cfg->dma.rx.channel, &(spi_dev->cfg->dma.rx.init));
/* Configure DMA for SPI Tx */ /* Configure DMA for SPI Tx */
DMA_Cmd(spi_dev->cfg->dma.tx.channel, DISABLE); DMA_Cmd(spi_dev->cfg->dma.tx.channel, DISABLE);
DMA_Init(spi_dev->cfg->dma.tx.channel, &(spi_dev->cfg->dma.tx.init)); DMA_Init(spi_dev->cfg->dma.tx.channel, &(spi_dev->cfg->dma.tx.init));
/* Initialize the SPI block */ /* Initialize the SPI block */
SPI_Init(spi_dev->cfg->regs, &(spi_dev->cfg->init)); SPI_Init(spi_dev->cfg->regs, &(spi_dev->cfg->init));
/* Configure CRC calculation */ /* Configure CRC calculation */
if (spi_dev->cfg->use_crc) { if (spi_dev->cfg->use_crc) {
SPI_CalculateCRC(spi_dev->cfg->regs, ENABLE); SPI_CalculateCRC(spi_dev->cfg->regs, ENABLE);
} else { } else {
SPI_CalculateCRC(spi_dev->cfg->regs, DISABLE); SPI_CalculateCRC(spi_dev->cfg->regs, DISABLE);
} }
/* Enable SPI */ /* Enable SPI */
SPI_Cmd(spi_dev->cfg->regs, ENABLE); SPI_Cmd(spi_dev->cfg->regs, ENABLE);
/* Enable SPI interrupts to DMA */ /* Enable SPI interrupts to DMA */
SPI_I2S_DMACmd(spi_dev->cfg->regs, SPI_I2S_DMAReq_Tx | SPI_I2S_DMAReq_Rx, ENABLE); SPI_I2S_DMACmd(spi_dev->cfg->regs, SPI_I2S_DMAReq_Tx | SPI_I2S_DMAReq_Rx, ENABLE);
/* Configure DMA interrupt */ /* Configure DMA interrupt */
NVIC_Init(&(spi_dev->cfg->dma.irq.init)); NVIC_Init(&(spi_dev->cfg->dma.irq.init));
} }
return 0; return 0;
} }
/** /**
* (Re-)initialises SPI peripheral clock rate * (Re-)initialises SPI peripheral clock rate
* *
* \param[in] spi SPI number (0 or 1) * \param[in] spi SPI number (0 or 1)
* \param[in] spi_prescaler configures the SPI speed: * \param[in] spi_prescaler configures the SPI speed:
* <UL> * <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) * <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)
* <LI>PIOS_SPI_PRESCALER_4: sets clock rate 55.5~ nS @ 72 MHz (18 MBit/s) * <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) * <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_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_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) * <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) * <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) * <LI>PIOS_SPI_PRESCALER_256: sets clock rate 3.5~ nS @ 72 MHz (0.281 MBit/s)
* </UL> * </UL>
* \return 0 if no error * \return 0 if no error
* \return -1 if disabled SPI port selected * \return -1 if disabled SPI port selected
* \return -2 if unsupported SPI port selected * \return -2 if unsupported SPI port selected
* \return -3 if invalid spi_prescaler selected * \return -3 if invalid spi_prescaler selected
*/ */
int32_t PIOS_SPI_SetClockSpeed(uint8_t spi, SPIPrescalerTypeDef spi_prescaler) int32_t PIOS_SPI_SetClockSpeed(uint8_t spi, SPIPrescalerTypeDef spi_prescaler)
{ {
struct pios_spi_dev *spi_dev; struct pios_spi_dev *spi_dev;
SPI_InitTypeDef SPI_InitStructure; SPI_InitTypeDef SPI_InitStructure;
/* Get a handle for the device configuration */ /* Get a handle for the device configuration */
spi_dev = find_spi_dev_by_id(spi); spi_dev = find_spi_dev_by_id(spi);
if (!spi_dev) { if (!spi_dev) {
/* Undefined SPI port for this board (see pios_board.c) */ /* Undefined SPI port for this board (see pios_board.c) */
return -2; return -2;
} }
if (spi_prescaler >= 8) { if (spi_prescaler >= 8) {
/* Invalid prescaler selected */ /* Invalid prescaler selected */
return -3; return -3;
} }
/* Start with a copy of the default configuration for the peripheral */ /* Start with a copy of the default configuration for the peripheral */
SPI_InitStructure = spi_dev->cfg->init; SPI_InitStructure = spi_dev->cfg->init;
/* Adjust the prescaler for the peripheral's clock */ /* Adjust the prescaler for the peripheral's clock */
SPI_InitStructure.SPI_BaudRatePrescaler = ((uint16_t) spi_prescaler & 7) << 3; SPI_InitStructure.SPI_BaudRatePrescaler = ((uint16_t) spi_prescaler & 7) << 3;
/* Write back the new configuration */ /* Write back the new configuration */
SPI_Init(spi_dev->cfg->regs, &SPI_InitStructure); SPI_Init(spi_dev->cfg->regs, &SPI_InitStructure);
PIOS_SPI_TransferByte(spi, 0xFF); PIOS_SPI_TransferByte(spi, 0xFF);
return 0; return 0;
} }
/** /**
* Controls the RC (Register Clock alias Chip Select) pin of a SPI port * Controls the RC (Register Clock alias Chip Select) pin of a SPI port
* \param[in] spi SPI number (0 or 1) * \param[in] spi SPI number (0 or 1)
* \param[in] pin_value 0 or 1 * \param[in] pin_value 0 or 1
* \return 0 if no error * \return 0 if no error
* \return -1 if disabled SPI port selected * \return -1 if disabled SPI port selected
* \return -2 if unsupported SPI port selected * \return -2 if unsupported SPI port selected
*/ */
int32_t PIOS_SPI_RC_PinSet(uint8_t spi, uint8_t pin_value) int32_t PIOS_SPI_RC_PinSet(uint8_t spi, uint8_t pin_value)
{ {
struct pios_spi_dev *spi_dev; struct pios_spi_dev *spi_dev;
/* Get a handle for the device configuration */ /* Get a handle for the device configuration */
spi_dev = find_spi_dev_by_id(spi); spi_dev = find_spi_dev_by_id(spi);
if (!spi_dev) { if (!spi_dev) {
/* Undefined SPI port for this board (see pios_board.c) */ /* Undefined SPI port for this board (see pios_board.c) */
return -2; return -2;
} }
if (pin_value) { if (pin_value) {
GPIO_SetBits(spi_dev->cfg->ssel.gpio, spi_dev->cfg->ssel.init.GPIO_Pin); GPIO_SetBits(spi_dev->cfg->ssel.gpio, spi_dev->cfg->ssel.init.GPIO_Pin);
} else { } else {
GPIO_ResetBits(spi_dev->cfg->ssel.gpio, spi_dev->cfg->ssel.init.GPIO_Pin); GPIO_ResetBits(spi_dev->cfg->ssel.gpio, spi_dev->cfg->ssel.init.GPIO_Pin);
} }
return 0; return 0;
} }
/** /**
* Transfers a byte to SPI output and reads back the return value from SPI input * Transfers a byte to SPI output and reads back the return value from SPI input
* \param[in] spi SPI number (0 or 1) * \param[in] spi SPI number (0 or 1)
* \param[in] b the byte which should be transfered * \param[in] b the byte which should be transfered
*/ */
int32_t PIOS_SPI_TransferByte(uint8_t spi, uint8_t b) int32_t PIOS_SPI_TransferByte(uint8_t spi, uint8_t b)
{ {
struct pios_spi_dev *spi_dev; struct pios_spi_dev *spi_dev;
uint8_t dummy; uint8_t dummy;
uint8_t rx_byte; uint8_t rx_byte;
/* Get a handle for the device configuration */ /* Get a handle for the device configuration */
spi_dev = find_spi_dev_by_id(spi); spi_dev = find_spi_dev_by_id(spi);
PIOS_DEBUG_Assert(spi_dev); PIOS_DEBUG_Assert(spi_dev);
/* /*
* Procedure taken from STM32F10xxx Reference Manual section 23.3.5 * Procedure taken from STM32F10xxx Reference Manual section 23.3.5
*/ */
/* Make sure the RXNE flag is cleared by reading the DR register */ /* Make sure the RXNE flag is cleared by reading the DR register */
dummy = spi_dev->cfg->regs->DR; dummy = spi_dev->cfg->regs->DR;
/* Start the transfer */ /* Start the transfer */
spi_dev->cfg->regs->DR = b; spi_dev->cfg->regs->DR = b;
/* Wait until there is a byte to read */ /* Wait until there is a byte to read */
while (!(spi_dev->cfg->regs->SR & SPI_I2S_FLAG_RXNE)) ; while (!(spi_dev->cfg->regs->SR & SPI_I2S_FLAG_RXNE)) ;
/* Read the rx'd byte */ /* Read the rx'd byte */
rx_byte = spi_dev->cfg->regs->DR; rx_byte = spi_dev->cfg->regs->DR;
/* Wait until the TXE goes high */ /* Wait until the TXE goes high */
while (!(spi_dev->cfg->regs->SR & SPI_I2S_FLAG_TXE)) ; while (!(spi_dev->cfg->regs->SR & SPI_I2S_FLAG_TXE)) ;
/* Wait for SPI transfer to have fully completed */ /* Wait for SPI transfer to have fully completed */
while (spi_dev->cfg->regs->SR & SPI_I2S_FLAG_BSY) ; while (spi_dev->cfg->regs->SR & SPI_I2S_FLAG_BSY) ;
/* Return received byte */ /* Return received byte */
return rx_byte; return rx_byte;
} }
/** /**
* Transfers a block of bytes via DMA. * Transfers a block of bytes via DMA.
* \param[in] spi SPI number (0 or 1) * \param[in] spi SPI number (0 or 1)
* \param[in] send_buffer pointer to buffer which should be sent.<BR> * \param[in] send_buffer pointer to buffer which should be sent.<BR>
* If NULL, 0xff (all-one) will be sent. * If NULL, 0xff (all-one) will be sent.
* \param[in] receive_buffer pointer to buffer which should get the received values.<BR> * \param[in] receive_buffer pointer to buffer which should get the received values.<BR>
* If NULL, received bytes will be discarded. * If NULL, received bytes will be discarded.
* \param[in] len number of bytes which should be transfered * \param[in] len number of bytes which should be transfered
* \param[in] callback pointer to callback function which will be executed * \param[in] callback pointer to callback function which will be executed
* from DMA channel interrupt once the transfer is finished. * from DMA channel interrupt once the transfer is finished.
* If NULL, no callback function will be used, and PIOS_SPI_TransferBlock() will * If NULL, no callback function will be used, and PIOS_SPI_TransferBlock() will
* block until the transfer is finished. * block until the transfer is finished.
* \return >= 0 if no error during transfer * \return >= 0 if no error during transfer
* \return -1 if disabled SPI port selected * \return -1 if disabled SPI port selected
* \return -2 if unsupported SPI port selected * \return -2 if unsupported SPI port selected
* \return -3 if function has been called during an ongoing DMA transfer * \return -3 if function has been called during an ongoing DMA transfer
*/ */
int32_t PIOS_SPI_TransferBlock(uint8_t spi, const uint8_t * send_buffer, uint8_t * receive_buffer, uint16_t len, void *callback) int32_t PIOS_SPI_TransferBlock(uint8_t spi, const uint8_t * send_buffer, uint8_t * receive_buffer, uint16_t len, void *callback)
{ {
struct pios_spi_dev *spi_dev; struct pios_spi_dev *spi_dev;
DMA_InitTypeDef dma_init; DMA_InitTypeDef dma_init;
/* Get a handle for the device configuration */ /* Get a handle for the device configuration */
spi_dev = find_spi_dev_by_id(spi); spi_dev = find_spi_dev_by_id(spi);
if (!spi_dev) { if (!spi_dev) {
/* Undefined SPI port for this board (see pios_board.c) */ /* Undefined SPI port for this board (see pios_board.c) */
return -2; return -2;
} }
/* Exit if ongoing transfer */ /* Exit if ongoing transfer */
if (DMA_GetCurrDataCounter(spi_dev->cfg->dma.rx.channel)) { if (DMA_GetCurrDataCounter(spi_dev->cfg->dma.rx.channel)) {
return -3; return -3;
} }
/* Disable the SPI peripheral */ /* Disable the SPI peripheral */
SPI_Cmd(spi_dev->cfg->regs, DISABLE); SPI_Cmd(spi_dev->cfg->regs, DISABLE);
/* Disable the DMA channels */ /* Disable the DMA channels */
DMA_Cmd(spi_dev->cfg->dma.rx.channel, DISABLE); DMA_Cmd(spi_dev->cfg->dma.rx.channel, DISABLE);
DMA_Cmd(spi_dev->cfg->dma.tx.channel, DISABLE); DMA_Cmd(spi_dev->cfg->dma.tx.channel, DISABLE);
/* Set callback function */ /* Set callback function */
spi_dev->callback = callback; spi_dev->callback = callback;
/* /*
* Configure Rx channel * Configure Rx channel
*/ */
/* Start with the default configuration for this peripheral */ /* Start with the default configuration for this peripheral */
dma_init = spi_dev->cfg->dma.rx.init; dma_init = spi_dev->cfg->dma.rx.init;
if (receive_buffer != NULL) { if (receive_buffer != NULL) {
/* Enable memory addr. increment - bytes written into receive buffer */ /* Enable memory addr. increment - bytes written into receive buffer */
dma_init.DMA_MemoryBaseAddr = (uint32_t) receive_buffer; dma_init.DMA_MemoryBaseAddr = (uint32_t) receive_buffer;
dma_init.DMA_MemoryInc = DMA_MemoryInc_Enable; dma_init.DMA_MemoryInc = DMA_MemoryInc_Enable;
} else { } else {
/* Disable memory addr. increment - bytes written into dummy buffer */ /* Disable memory addr. increment - bytes written into dummy buffer */
spi_dev->rx_dummy_byte = 0xFF; spi_dev->rx_dummy_byte = 0xFF;
dma_init.DMA_MemoryBaseAddr = (uint32_t) & spi_dev->rx_dummy_byte; dma_init.DMA_MemoryBaseAddr = (uint32_t) & spi_dev->rx_dummy_byte;
dma_init.DMA_MemoryInc = DMA_MemoryInc_Disable; dma_init.DMA_MemoryInc = DMA_MemoryInc_Disable;
} }
if (spi_dev->cfg->use_crc) { if (spi_dev->cfg->use_crc) {
/* Make sure the CRC error flag is cleared before we start */ /* Make sure the CRC error flag is cleared before we start */
SPI_I2S_ClearFlag(spi_dev->cfg->regs, SPI_FLAG_CRCERR); SPI_I2S_ClearFlag(spi_dev->cfg->regs, SPI_FLAG_CRCERR);
} }
dma_init.DMA_BufferSize = len; dma_init.DMA_BufferSize = len;
DMA_Init(spi_dev->cfg->dma.rx.channel, &(dma_init)); DMA_Init(spi_dev->cfg->dma.rx.channel, &(dma_init));
/* /*
* Configure Tx channel * Configure Tx channel
*/ */
/* Start with the default configuration for this peripheral */ /* Start with the default configuration for this peripheral */
dma_init = spi_dev->cfg->dma.tx.init; dma_init = spi_dev->cfg->dma.tx.init;
if (send_buffer != NULL) { if (send_buffer != NULL) {
/* Enable memory addr. increment - bytes written into receive buffer */ /* Enable memory addr. increment - bytes written into receive buffer */
dma_init.DMA_MemoryBaseAddr = (uint32_t) send_buffer; dma_init.DMA_MemoryBaseAddr = (uint32_t) send_buffer;
dma_init.DMA_MemoryInc = DMA_MemoryInc_Enable; dma_init.DMA_MemoryInc = DMA_MemoryInc_Enable;
} else { } else {
/* Disable memory addr. increment - bytes written into dummy buffer */ /* Disable memory addr. increment - bytes written into dummy buffer */
spi_dev->tx_dummy_byte = 0xFF; spi_dev->tx_dummy_byte = 0xFF;
dma_init.DMA_MemoryBaseAddr = (uint32_t) & spi_dev->tx_dummy_byte; dma_init.DMA_MemoryBaseAddr = (uint32_t) & spi_dev->tx_dummy_byte;
dma_init.DMA_MemoryInc = DMA_MemoryInc_Disable; dma_init.DMA_MemoryInc = DMA_MemoryInc_Disable;
} }
if (spi_dev->cfg->use_crc) { if (spi_dev->cfg->use_crc) {
/* The last byte of the payload will be replaced with the CRC8 */ /* The last byte of the payload will be replaced with the CRC8 */
dma_init.DMA_BufferSize = len - 1; dma_init.DMA_BufferSize = len - 1;
} else { } else {
dma_init.DMA_BufferSize = len; dma_init.DMA_BufferSize = len;
} }
DMA_Init(spi_dev->cfg->dma.tx.channel, &(dma_init)); DMA_Init(spi_dev->cfg->dma.tx.channel, &(dma_init));
/* Enable DMA interrupt if callback function active */ /* Enable DMA interrupt if callback function active */
DMA_ITConfig(spi_dev->cfg->dma.rx.channel, DMA_IT_TC, (callback != NULL) ? ENABLE : DISABLE); DMA_ITConfig(spi_dev->cfg->dma.rx.channel, DMA_IT_TC, (callback != NULL) ? ENABLE : DISABLE);
/* Flush out the CRC registers */ /* Flush out the CRC registers */
SPI_CalculateCRC(spi_dev->cfg->regs, DISABLE); SPI_CalculateCRC(spi_dev->cfg->regs, DISABLE);
(void)SPI_GetCRC(spi_dev->cfg->regs, SPI_CRC_Rx); (void)SPI_GetCRC(spi_dev->cfg->regs, SPI_CRC_Rx);
SPI_I2S_ClearFlag(spi_dev->cfg->regs, SPI_FLAG_CRCERR); SPI_I2S_ClearFlag(spi_dev->cfg->regs, SPI_FLAG_CRCERR);
/* Make sure to flush out the receive buffer */ /* Make sure to flush out the receive buffer */
(void)SPI_I2S_ReceiveData(spi_dev->cfg->regs); (void)SPI_I2S_ReceiveData(spi_dev->cfg->regs);
if (spi_dev->cfg->use_crc) { if (spi_dev->cfg->use_crc) {
/* Need a 0->1 transition to reset the CRC logic */ /* Need a 0->1 transition to reset the CRC logic */
SPI_CalculateCRC(spi_dev->cfg->regs, ENABLE); SPI_CalculateCRC(spi_dev->cfg->regs, ENABLE);
} }
/* Start DMA transfers */ /* Start DMA transfers */
DMA_Cmd(spi_dev->cfg->dma.rx.channel, ENABLE); DMA_Cmd(spi_dev->cfg->dma.rx.channel, ENABLE);
DMA_Cmd(spi_dev->cfg->dma.tx.channel, ENABLE); DMA_Cmd(spi_dev->cfg->dma.tx.channel, ENABLE);
/* Reenable the SPI device */ /* Reenable the SPI device */
SPI_Cmd(spi_dev->cfg->regs, ENABLE); SPI_Cmd(spi_dev->cfg->regs, ENABLE);
if (callback) { if (callback) {
/* User has requested a callback, don't wait for the transfer to complete. */ /* User has requested a callback, don't wait for the transfer to complete. */
return 0; return 0;
} }
/* Wait until all bytes have been transmitted/received */ /* Wait until all bytes have been transmitted/received */
while (DMA_GetCurrDataCounter(spi_dev->cfg->dma.rx.channel)) ; while (DMA_GetCurrDataCounter(spi_dev->cfg->dma.rx.channel)) ;
/* Wait for the final bytes of the transfer to complete, including CRC byte(s). */ /* 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))) ; 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). */ /* 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)) ; while (SPI_I2S_GetFlagStatus(spi_dev->cfg->regs, SPI_I2S_FLAG_BSY)) ;
/* Check the CRC on the transfer if enabled. */ /* Check the CRC on the transfer if enabled. */
if (spi_dev->cfg->use_crc) { if (spi_dev->cfg->use_crc) {
/* Check the SPI CRC error flag */ /* Check the SPI CRC error flag */
if (SPI_I2S_GetFlagStatus(spi_dev->cfg->regs, SPI_FLAG_CRCERR)) { if (SPI_I2S_GetFlagStatus(spi_dev->cfg->regs, SPI_FLAG_CRCERR)) {
return -4; return -4;
} }
} }
/* No error */ /* No error */
return 0; return 0;
} }
void PIOS_SPI_IRQ_Handler(uint8_t spi) /**
{ * Check if a transfer is in progress
struct pios_spi_dev *spi_dev; * \param[in] spi SPI number (0 or 1)
* \return >= 0 if no transfer is in progress
spi_dev = find_spi_dev_by_id(spi); * \return -1 if disabled SPI port selected
PIOS_DEBUG_Assert(spi_dev); * \return -2 if unsupported SPI port selected
* \return -3 if function has been called during an ongoing DMA transfer
DMA_ClearFlag(spi_dev->cfg->dma.irq.flags); */
int32_t PIOS_SPI_Busy(uint8_t spi)
/* 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))) ; struct pios_spi_dev * spi_dev;
/* Wait for the final bytes of the transfer to complete, including CRC byte(s). */ /* Get a handle for the device configuration */
while (SPI_I2S_GetFlagStatus(spi_dev->cfg->regs, SPI_I2S_FLAG_BSY)) ; spi_dev = find_spi_dev_by_id(spi);
if (spi_dev->callback != NULL) { if (!spi_dev) {
bool crc_ok = TRUE; /* Undefined SPI port for this board (see pios_board.c) */
uint8_t crc_val; return -2;
}
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(uint8_t spi)
{
struct pios_spi_dev *spi_dev;
spi_dev = find_spi_dev_by_id(spi);
PIOS_DEBUG_Assert(spi_dev);
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

@ -1,59 +1,60 @@
/** /**
****************************************************************************** ******************************************************************************
* @addtogroup PIOS PIOS Core hardware abstraction layer * @addtogroup PIOS PIOS Core hardware abstraction layer
* @{ * @{
* @addtogroup PIOS_SPI SPI Functions * @addtogroup PIOS_SPI SPI Functions
* @{ * @{
* *
* @file pios_spi.h * @file pios_spi.h
* @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010. * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
* Parts by Thorsten Klose (tk@midibox.org) * Parts by Thorsten Klose (tk@midibox.org)
* @brief SPI functions header. * @brief SPI functions header.
* @see The GNU Public License (GPL) Version 3 * @see The GNU Public License (GPL) Version 3
* *
*****************************************************************************/ *****************************************************************************/
/* /*
* 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
*/ */
#ifndef PIOS_SPI_H #ifndef PIOS_SPI_H
#define PIOS_SPI_H #define PIOS_SPI_H
/* Global types */ /* Global types */
typedef enum { typedef enum {
PIOS_SPI_PRESCALER_2 = 0, PIOS_SPI_PRESCALER_2 = 0,
PIOS_SPI_PRESCALER_4 = 1, PIOS_SPI_PRESCALER_4 = 1,
PIOS_SPI_PRESCALER_8 = 2, PIOS_SPI_PRESCALER_8 = 2,
PIOS_SPI_PRESCALER_16 = 3, PIOS_SPI_PRESCALER_16 = 3,
PIOS_SPI_PRESCALER_32 = 4, PIOS_SPI_PRESCALER_32 = 4,
PIOS_SPI_PRESCALER_64 = 5, PIOS_SPI_PRESCALER_64 = 5,
PIOS_SPI_PRESCALER_128 = 6, PIOS_SPI_PRESCALER_128 = 6,
PIOS_SPI_PRESCALER_256 = 7 PIOS_SPI_PRESCALER_256 = 7
} SPIPrescalerTypeDef; } SPIPrescalerTypeDef;
/* Public Functions */ /* Public Functions */
extern int32_t PIOS_SPI_Init(void); extern int32_t PIOS_SPI_Init(void);
extern int32_t PIOS_SPI_SetClockSpeed(uint8_t spi, SPIPrescalerTypeDef spi_prescaler); extern int32_t PIOS_SPI_SetClockSpeed(uint8_t spi, SPIPrescalerTypeDef spi_prescaler);
extern int32_t PIOS_SPI_RC_PinSet(uint8_t spi, uint8_t pin_value); extern int32_t PIOS_SPI_RC_PinSet(uint8_t spi, uint8_t pin_value);
extern int32_t PIOS_SPI_TransferByte(uint8_t spi, uint8_t b); extern int32_t PIOS_SPI_TransferByte(uint8_t spi, uint8_t b);
extern int32_t PIOS_SPI_TransferBlock(uint8_t spi, const uint8_t * send_buffer, uint8_t * receive_buffer, uint16_t len, void *callback); extern int32_t PIOS_SPI_TransferBlock(uint8_t spi, const uint8_t * send_buffer, uint8_t * receive_buffer, uint16_t len, void *callback);
extern void PIOS_SPI_IRQ_Handler(uint8_t spi); extern int32_t PIOS_SPI_Busy(uint8_t spi);
extern void PIOS_SPI_IRQ_Handler(uint8_t spi);
#endif /* PIOS_SPI_H */
#endif /* PIOS_SPI_H */
/**
* @} /**
* @} * @}
*/ * @}
*/