[CC2538-SPI] Implement SPI_FLUSH, spi mode, !CS

Because the CC2538 has a multi-byte SPI RX FIFO, flushing the buffer
requires more than just a single read. This adds a loop that empties the
entire RX buffer on a FLUSH().

Different SPI chips needs different SPI settings. This commit adds a
function that allows chip drivers to configure the SPI peripheral before
using it.

The frame pin the driver was using as a chip select does not work as
most devices expect it to. It toggles after every byte, and most chips
interpret that as end of message. To make drivers more reliable, each
chip driver should setup a GPIO and assert it as needed.
This commit is contained in:
Brad Campbell 2014-03-18 10:11:49 -04:00
parent a96dc90250
commit af27d2d252
4 changed files with 115 additions and 59 deletions

View file

@ -48,23 +48,6 @@
#define SPI_MOSI_PIN_MASK GPIO_PIN_MASK(SPI_MOSI_PIN)
#define SPI_MISO_PORT_BASE GPIO_PORT_TO_BASE(SPI_MISO_PORT)
#define SPI_MISO_PIN_MASK GPIO_PIN_MASK(SPI_MISO_PIN)
#define SPI_SEL_PORT_BASE GPIO_PORT_TO_BASE(SPI_SEL_PORT)
#define SPI_SEL_PIN_MASK GPIO_PIN_MASK(SPI_SEL_PIN)
/* Default: Motorola mode 3 with 8-bit data words */
#ifndef SPI_CONF_PHASE
#define SPI_CONF_PHASE SSI_CR0_SPH
#endif
#ifndef SPI_CONF_POLARITY
#define SPI_CONF_POLARITY SSI_CR0_SPO
#endif
#ifndef SPI_CONF_DATA_SIZE
#define SPI_CONF_DATA_SIZE 8
#endif
#if SPI_CONF_DATA_SIZE < 4 || SPI_CONF_DATA_SIZE > 16
#error SPI_CONF_DATA_SIZE must be set between 4 and 16 inclusive.
#endif
/**
* \brief Initialize the SPI bus.
@ -73,12 +56,11 @@
* SPI_CLK_PORT SPI_CLK_PIN
* SPI_MOSI_PORT SPI_MOSI_PIN
* SPI_MISO_PORT SPI_MISO_PIN
* SPI_SEL_PORT SPI_SEL_PIN
*
* This sets the mode to Motorola SPI with the following format options:
* SPI_CONF_PHASE: 0 or SSI_CR0_SPH
* SPI_CONF_POLARITY: 0 or SSI_CR0_SPO
* SPI_CONF_DATA_SIZE: 4 to 16 bits
* Clock phase: 1; data captured on second (rising) edge
* Clock polarity: 1; clock is high when idle
* Data size: 8 bits
*/
void
spi_init(void)
@ -95,31 +77,42 @@ spi_init(void)
ioc_set_sel(SPI_CLK_PORT, SPI_CLK_PIN, IOC_PXX_SEL_SSI0_CLKOUT);
ioc_set_sel(SPI_MOSI_PORT, SPI_MOSI_PIN, IOC_PXX_SEL_SSI0_TXD);
REG(IOC_SSIRXD_SSI0) = (SPI_MISO_PORT * 8) + SPI_MISO_PIN;
ioc_set_sel(SPI_SEL_PORT, SPI_SEL_PIN, IOC_PXX_SEL_SSI0_FSSOUT);
/* Put all the SSI gpios into peripheral mode */
GPIO_PERIPHERAL_CONTROL(SPI_CLK_PORT_BASE, SPI_CLK_PIN_MASK);
GPIO_PERIPHERAL_CONTROL(SPI_MOSI_PORT_BASE, SPI_MOSI_PIN_MASK);
GPIO_PERIPHERAL_CONTROL(SPI_MISO_PORT_BASE, SPI_MISO_PIN_MASK);
GPIO_PERIPHERAL_CONTROL(SPI_SEL_PORT_BASE, SPI_SEL_PIN_MASK);
/* Disable any pull ups or the like */
ioc_set_over(SPI_CLK_PORT, SPI_CLK_PIN, IOC_OVERRIDE_DIS);
ioc_set_over(SPI_MOSI_PORT, SPI_MOSI_PIN, IOC_OVERRIDE_DIS);
ioc_set_over(SPI_MISO_PORT, SPI_MISO_PIN, IOC_OVERRIDE_DIS);
ioc_set_over(SPI_SEL_PORT, SPI_SEL_PIN, IOC_OVERRIDE_DIS);
/* Configure the clock */
REG(SSI0_BASE + SSI_CPSR) = 2;
/* Put the ssi in Motorola SPI mode using the provided format options */
REG(SSI0_BASE + SSI_CR0) = SPI_CONF_PHASE | SPI_CONF_POLARITY | (SPI_CONF_DATA_SIZE - 1);
/* Configure the default SPI options.
* mode: Motorola frame format
* clock: High when idle
* data: Valid on rising edges of the clock
* bits: 8 byte data
*/
REG(SSI0_BASE + SSI_CR0) = SSI_CR0_SPH | SSI_CR0_SPO | (0x07);
/* Enable the SSI */
REG(SSI0_BASE + SSI_CR1) |= SSI_CR1_SSE;
}
/*---------------------------------------------------------------------------*/
void
spi_cs_init(uint8_t port, uint8_t pin)
{
GPIO_SOFTWARE_CONTROL(GPIO_PORT_TO_BASE(port), GPIO_PIN_MASK(pin));
ioc_set_over(port, pin, IOC_OVERRIDE_DIS);
GPIO_SET_OUTPUT(GPIO_PORT_TO_BASE(port), GPIO_PIN_MASK(pin));
GPIO_SET_PIN(GPIO_PORT_TO_BASE(port), GPIO_PIN_MASK(pin));
}
/*---------------------------------------------------------------------------*/
void
spi_enable(void)
{
/* Enable the clock for the SSI peripheral */
@ -132,4 +125,16 @@ spi_disable(void)
/* Gate the clock for the SSI peripheral */
REG(SYS_CTRL_RCGCSSI) &= ~1;
}
/*---------------------------------------------------------------------------*/
void spi_set_mode(uint32_t frame_format, uint32_t clock_polarity, uint32_t clock_phase, uint32_t data_size)
{
/* Disable the SSI peripheral to configure it */
REG(SSI0_BASE + SSI_CR1) = 0;
/* Configure the SSI options */
REG(SSI0_BASE + SSI_CR0) = clock_phase | clock_polarity | frame_format | (data_size - 1);
/* Re-enable the SSI */
REG(SSI0_BASE + SSI_CR1) |= SSI_CR1_SSE;
}
/** @} */

View file

@ -74,98 +74,106 @@
*/
#define SSI_CR0_SCR_M 0x0000FF00 /**< Serial clock rate mask */
#define SSI_CR0_SCR_S 8 /**< Serial clock rate shift */
#define SSI_CR0_SPH 0x00000080 /**< Serial clock phase (H) */
#define SSI_CR0_SPH_M 0x00000080 /**< Serial clock phase (H) mask */
#define SSI_CR0_SPH_S 7 /**< Serial clock phase (H) shift */
#define SSI_CR0_SPO 0x00000040 /**< Serial clock phase (O) */
#define SSI_CR0_SPO_M 0x00000040 /**< Serial clock phase (O) mask */
#define SSI_CR0_SPO_S 6 /**< Serial clock phase (O) shift */
#define SSI_CR0_FRF_M 0x00000030 /**< Frame format select mask */
#define SSI_CR0_FRF_S 4 /**< Frame format select shift */
#define SSI_CR0_DSS_M 0x0000000F /**< Data size select mask */
#define SSI_CR0_DSS_S 0 /**< Data size select shift */
#define SSI_CR1_SOD 0x00000008 /**< Slave mode output disable */
#define SSI_CR1_SOD_M 0x00000008 /**< Slave mode output disable mask */
#define SSI_CR1_SOD_S 3 /**< Slave mode output disable shift */
#define SSI_CR1_MS 0x00000004 /**< Master and slave select */
#define SSI_CR1_MS_M 0x00000004 /**< Master and slave select mask */
#define SSI_CR1_MS_S 2 /**< Master and slave select shift */
#define SSI_CR1_SSE 0x00000002 /**< Synchronous serial port enable */
#define SSI_CR1_SSE_M 0x00000002 /**< Synchronous serial port enable mask */
#define SSI_CR1_SSE_S 1 /**< Synchronous serial port enable shift */
#define SSI_CR1_LBM 0x00000001 /**< Loop-back mode */
#define SSI_CR1_LBM_M 0x00000001 /**< Loop-back mode mask */
#define SSI_CR1_LBM_S 0 /**< Loop-back mode shift */
#define SSI_DR_DATA_M 0x0000FFFF /**< FIFO data mask */
#define SSI_DR_DATA_S 0 /**< FIFO data shift */
#define SSI_SR_BSY 0x00000010 /**< Busy bit */
#define SSI_SR_BSY_M 0x00000010 /**< Busy bit mask */
#define SSI_SR_BSY_S 4 /**< Busy bit shift */
#define SSI_SR_RFF 0x00000008 /**< Receive FIFO full */
#define SSI_SR_RFF_M 0x00000008 /**< Receive FIFO full mask */
#define SSI_SR_RFF_S 3 /**< Receive FIFO full shift */
#define SSI_SR_RNE 0x00000004 /**< Receive FIFO not empty */
#define SSI_SR_RNE_M 0x00000004 /**< Receive FIFO not empty mask */
#define SSI_SR_RNE_S 2 /**< Receive FIFO not empty shift */
#define SSI_SR_TNF 0x00000002 /**< Transmit FIFO not full */
#define SSI_SR_TNF_M 0x00000002 /**< Transmit FIFO not full mask */
#define SSI_SR_TNF_S 1 /**< Transmit FIFO not full shift */
#define SSI_SR_TFE 0x00000001 /**< Transmit FIFO empty */
#define SSI_SR_TFE_M 0x00000001 /**< Transmit FIFO empty mask */
#define SSI_SR_TFE_S 0 /**< Transmit FIFO empty shift */
#define SSI_CPSR_CPSDVSR_M 0x000000FF /**< Clock prescale divisor mask */
#define SSI_CPSR_CPSDVSR_S 0 /**< Clock prescale divisor shift */
#define SSI_IM_TXIM 0x00000008 /**< Transmit FIFO interrupt mask */
#define SSI_IM_TXIM_M 0x00000008 /**< Transmit FIFO interrupt mask mask */
#define SSI_IM_TXIM_S 3 /**< Transmit FIFO interrupt mask shift */
#define SSI_IM_RXIM 0x00000004 /**< Receive FIFO interrupt mask */
#define SSI_IM_RXIM_M 0x00000004 /**< Receive FIFO interrupt mask mask */
#define SSI_IM_RXIM_S 2 /**< Receive FIFO interrupt mask shift */
#define SSI_IM_RTIM 0x00000002 /**< Receive time-out interrupt mask */
#define SSI_IM_RTIM_M 0x00000002 /**< Receive time-out interrupt mask mask */
#define SSI_IM_RTIM_S 1 /**< Receive time-out interrupt mask shift */
#define SSI_IM_RORIM 0x00000001 /**< Receive overrun interrupt mask */
#define SSI_IM_RORIM_M 0x00000001 /**< Receive overrun interrupt mask mask */
#define SSI_IM_RORIM_S 0 /**< Receive overrun interrupt mask shift */
#define SSI_RIS_TXRIS 0x00000008 /**< SSITXINTR raw state */
#define SSI_RIS_TXRIS_M 0x00000008 /**< SSITXINTR raw state mask */
#define SSI_RIS_TXRIS_S 3 /**< SSITXINTR raw state shift */
#define SSI_RIS_RXRIS 0x00000004 /**< SSIRXINTR raw state */
#define SSI_RIS_RXRIS_M 0x00000004 /**< SSIRXINTR raw state mask */
#define SSI_RIS_RXRIS_S 2 /**< SSIRXINTR raw state shift */
#define SSI_RIS_RTRIS 0x00000002 /**< SSIRTINTR raw state */
#define SSI_RIS_RTRIS_M 0x00000002 /**< SSIRTINTR raw state mask */
#define SSI_RIS_RTRIS_S 1 /**< SSIRTINTR raw state shift */
#define SSI_RIS_RORRIS 0x00000001 /**< SSIRORINTR raw state */
#define SSI_RIS_RORRIS_M 0x00000001 /**< SSIRORINTR raw state mask */
#define SSI_RIS_RORRIS_S 0 /**< SSIRORINTR raw state shift */
#define SSI_MIS_TXMIS 0x00000008 /**< SSITXINTR masked state */
#define SSI_MIS_TXMIS_M 0x00000008 /**< SSITXINTR masked state mask */
#define SSI_MIS_TXMIS_S 3 /**< SSITXINTR masked state shift */
#define SSI_MIS_RXMIS 0x00000004 /**< SSIRXINTR masked state */
#define SSI_MIS_RXMIS_M 0x00000004 /**< SSIRXINTR masked state mask */
#define SSI_MIS_RXMIS_S 2 /**< SSIRXINTR masked state shift */
#define SSI_MIS_RTMIS 0x00000002 /**< SSIRTINTR masked state */
#define SSI_MIS_RTMIS_M 0x00000002 /**< SSIRTINTR masked state mask */
#define SSI_MIS_RTMIS_S 1 /**< SSIRTINTR masked state shift */
#define SSI_MIS_RORMIS 0x00000001 /**< SSIRORINTR masked state */
#define SSI_MIS_RORMIS_M 0x00000001 /**< SSIRORINTR masked state mask */
#define SSI_MIS_RORMIS_S 0 /**< SSIRORINTR masked state shift */
#define SSI_ICR_RTIC 0x00000002 /**< Receive time-out interrupt clear */
#define SSI_ICR_RTIC_M 0x00000002 /**< Receive time-out interrupt clear mask */
#define SSI_ICR_RTIC_S 1 /**< Receive time-out interrupt clear shift */
#define SSI_ICR_RORIC 0x00000001 /**< Receive overrun interrupt clear */
#define SSI_ICR_RORIC_M 0x00000001 /**< Receive overrun interrupt clear mask */
#define SSI_ICR_RORIC_S 0 /**< Receive overrun interrupt clear shift */
#define SSI_DMACTL_TXDMAE 0x00000002 /**< Transmit DMA enable */
#define SSI_DMACTL_TXDMAE_M 0x00000002 /**< Transmit DMA enable mask */
#define SSI_DMACTL_TXDMAE_S 1 /**< Transmit DMA enable shift */
#define SSI_DMACTL_RXDMAE 0x00000001 /**< Receive DMA enable */
#define SSI_DMACTL_RXDMAE_M 0x00000001 /**< Receive DMA enable mask */
#define SSI_DMACTL_RXDMAE_S 0 /**< Receive DMA enable shift */
#define SSI_CC_CS_M 0x00000007 /**< Baud and system clock source mask */
#define SSI_CC_CS_S 0 /**< Baud and system clock source shift */
/** @} */
/*---------------------------------------------------------------------------*/
/** \name SSI Register Values
* @{
*/
#define SSI_CR0_SPH 0x00000080 /**< Serial clock phase (H) */
#define SSI_CR0_SPO 0x00000040 /**< Serial clock phase (O) */
#define SSI_CR0_FRF_MOTOROLA 0x00000000 /**< Motorola frame format */
#define SSI_CR0_FRF_TI 0x00000010 /**< Texas Instruments frame format */
#define SSI_CR0_FRF_MICROWIRE 0x00000020 /**< National Microwire frame format */
#define SSI_CR1_SOD 0x00000008 /**< Slave mode output disable */
#define SSI_CR1_MS 0x00000004 /**< Master and slave select */
#define SSI_CR1_SSE 0x00000002 /**< Synchronous serial port enable */
#define SSI_CR1_LBM 0x00000001 /**< Loop-back mode */
#define SSI_SR_BSY 0x00000010 /**< Busy bit */
#define SSI_SR_RFF 0x00000008 /**< Receive FIFO full */
#define SSI_SR_RNE 0x00000004 /**< Receive FIFO not empty */
#define SSI_SR_TNF 0x00000002 /**< Transmit FIFO not full */
#define SSI_SR_TFE 0x00000001 /**< Transmit FIFO empty */
#define SSI_IM_TXIM 0x00000008 /**< Transmit FIFO interrupt mask */
#define SSI_IM_RXIM 0x00000004 /**< Receive FIFO interrupt mask */
#define SSI_IM_RTIM 0x00000002 /**< Receive time-out interrupt mask */
#define SSI_IM_RORIM 0x00000001 /**< Receive overrun interrupt mask */
#define SSI_RIS_TXRIS 0x00000008 /**< SSITXINTR raw state */
#define SSI_RIS_RXRIS 0x00000004 /**< SSIRXINTR raw state */
#define SSI_RIS_RTRIS 0x00000002 /**< SSIRTINTR raw state */
#define SSI_RIS_RORRIS 0x00000001 /**< SSIRORINTR raw state */
#define SSI_MIS_TXMIS 0x00000008 /**< SSITXINTR masked state */
#define SSI_MIS_RXMIS 0x00000004 /**< SSIRXINTR masked state */
#define SSI_MIS_RTMIS 0x00000002 /**< SSIRTINTR masked state */
#define SSI_MIS_RORMIS 0x00000001 /**< SSIRORINTR masked state */
#define SSI_ICR_RTIC 0x00000002 /**< Receive time-out interrupt clear */
#define SSI_ICR_RORIC 0x00000001 /**< Receive overrun interrupt clear */
#define SSI_DMACTL_TXDMAE 0x00000002 /**< Transmit DMA enable */
#define SSI_DMACTL_RXDMAE 0x00000001 /**< Receive DMA enable */
/** @} */
#endif
/**

View file

@ -41,27 +41,51 @@
#ifndef SPI_ARCH_H_
#define SPI_ARCH_H_
#include "contiki.h"
#include "dev/ssi.h"
#define SPI_WAITFORTxREADY() do { \
while(!(REG(SSI0_BASE + SSI_SR) & SSI_SR_TNF)); \
} while (0)
} while(0)
#define SPI_TXBUF REG(SSI0_BASE + SSI_DR)
#define SPI_RXBUF REG(SSI0_BASE + SSI_DR)
#define SPI_WAITFOREOTx() do { \
while(REG(SSI0_BASE + SSI_SR) & SSI_SR_BSY); \
} while (0)
} while(0)
#define SPI_WAITFOREORx() do { \
while(!(REG(SSI0_BASE + SSI_SR) & SSI_SR_RNE)); \
} while (0)
} while(0)
#ifdef SPI_FLUSH
#error "You must include spi-arch.h before spi.h for the CC2538."
#endif
#define SPI_FLUSH() do { \
SPI_WAITFOREORx(); \
while (REG(SSI0_BASE + SSI_SR) & SSI_SR_RNE) { \
SPI_RXBUF; \
} \
} while(0)
#define SPI_CS_CLR(port, pin) do { \
GPIO_CLR_PIN(GPIO_PORT_TO_BASE(port), GPIO_PIN_MASK(pin)); \
} while(0)
#define SPI_CS_SET(port, pin) do { \
GPIO_SET_PIN(GPIO_PORT_TO_BASE(port), GPIO_PIN_MASK(pin)); \
} while(0)
/*---------------------------------------------------------------------------*/
/** \name Arch-specific SPI functions
* @{
*/
/**
* \brief Configure a GPIO to be the chip select pin
*/
void spi_cs_init(uint8_t port, uint8_t pin);
/** \brief Enables the SPI peripheral
*/
void spi_enable(void);
@ -71,6 +95,27 @@ void spi_enable(void);
*/
void spi_disable(void);
/**
* \brief Configure the SPI data and clock polarity and the data size.
*
* This function configures the SSI peripheral to use a particular SPI
* configuration that a slave device requires. It should always be called
* before using the SPI bus as another driver could have changed the settings.
*
* See section 19.4.4 in the CC2538 user guide for more information.
*
* \param frame_format Set the SSI frame format. Use SSI_CR0_FRF_MOTOROLA,
* SSI_CR0_FRF_TI, or SSI_CR0_FRF_MICROWIRE.
* \param clock_polarity In Motorola mode, set whether the clock is high or low
* when idle. Use SSI_CR0_SPO or 0.
* \param clock_phase In Motorola mode, select whether data is valid on the
* first or second edge of the clock. Use SSI_CR0_SPH or 0.
* \param data_size The number of bits in each "byte" of data. Must be
* between 4 and 16, inclusive.
*/
void spi_set_mode(uint32_t frame_format, uint32_t clock_polarity,
uint32_t clock_phase, uint32_t data_size);
/** @} */
#endif /* SPI_ARCH_H_ */

View file

@ -193,8 +193,6 @@
#define SPI_MOSI_PIN 4
#define SPI_MISO_PORT GPIO_A_NUM
#define SPI_MISO_PIN 5
#define SPI_SEL_PORT GPIO_B_NUM
#define SPI_SEL_PIN 5
/** @} */
/*---------------------------------------------------------------------------*/
/**