123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675 |
- /****************************************************************************
- * include/nuttx/spi/spi_bitbang.c
- *
- * Copyright (C) 2013 Gregory Nutt. All rights reserved.
- * Author: Gregory Nutt <gnutt@nuttx.org>
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * 3. Neither the name NuttX nor the names of its contributors may be
- * used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
- * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- *
- ****************************************************************************/
- /****************************************************************************
- * Included Files
- ****************************************************************************/
- #include <nuttx/config.h>
- #include <nuttx/spi/spi_bitbang.h>
- /****************************************************************************
- * Pre-processor Definitions
- ****************************************************************************/
- /* Usage ********************************************************************/
- /* To use this logic, you should provide a C file that does the following:
- *
- * - Defines SPI_SETSCK and SPI_CLRSCK to set and clear the SCK signal
- * - Defines SPI_SETMOSI and SPI_CLRMOSI to set and clear the MISO signal
- * - Defines SPI_GETMISO to sample the MISO state
- * - Defines SPI_PERBIT_NSEC which is the minimum time to transfer one bit.
- * This determines the maximum frequency.
- * - Other configuration options:
- * SPI_BITBANG_LOOPSPERMSEC - Delay loop calibration
- * SPI_BITBANG_DISABLEMODE0 - Define to disable Mode 0 support
- * SPI_BITBANG_DISABLEMODE1 - Define to disable Mode 1 support
- * SPI_BITBANG_DISABLEMODE2 - Define to disable Mode 2 support
- * SPI_BITBANG_DISABLEMODE3 - Define to disable Mode 3 support
- * - Provide implementations of spi_select(), spi_status() and spi_cmddata().
- * - Then include this file
- * - Provide an initialization function that initializes the GPIO pins used
- * in the bit bang interface and calls spi_create_bitbang().
- */
- /****************************************************************************
- * Private Function Prototypes
- ****************************************************************************/
- static void spi_delay(uint32_t holdtime);
- static void spi_select(FAR struct spi_bitbang_s *priv,
- uint32_t devid, bool selected);
- static uint32_t spi_setfrequency(FAR struct spi_bitbang_s *priv,
- uint32_t frequency);
- static void spi_setmode(FAR struct spi_bitbang_s *priv,
- enum spi_mode_e mode);
- #ifndef SPI_BITBANG_DISABLEMODE0
- static uint16_t spi_bitexchange0(uint16_t dataout, uint32_t holdtime);
- #endif
- #ifndef SPI_BITBANG_DISABLEMODE1
- static uint16_t spi_bitexchange1(uint16_t dataout, uint32_t holdtime);
- #endif
- #ifndef SPI_BITBANG_DISABLEMODE2
- static uint16_t spi_bitexchange2(uint16_t dataout, uint32_t holdtime);
- #endif
- #ifndef SPI_BITBANG_DISABLEMODE3
- static uint16_t spi_bitexchange3(uint16_t dataout, uint32_t holdtime);
- #endif
- static uint16_t spi_exchange(FAR struct spi_bitbang_s *priv,
- uint16_t dataout);
- static uint8_t spi_status(FAR struct spi_bitbang_s *priv,
- uint32_t devid);
- #ifdef CONFIG_SPI_CMDDATA
- static int spi_cmddata(FAR struct spi_bitbang_s *priv,
- uint32_t devid, bool cmd);
- #endif
- /****************************************************************************
- * Private Data
- ****************************************************************************/
- static const struct spi_bitbang_ops_s g_spiops =
- {
- spi_select, /* select */
- spi_setfrequency, /* setfrequency */
- spi_setmode, /* setmode */
- spi_exchange, /* exchange */
- spi_status, /* status */
- #ifdef CONFIG_SPI_CMDDATA
- spi_cmddata, /* cmddata */
- #endif
- };
- /****************************************************************************
- * Private Functions
- ****************************************************************************/
- /****************************************************************************
- * Name: spi_delay
- *
- * Description:
- * Delay for a specified number of loops
- *
- * Input Parameters:
- * holdtime - The number of loops
- *
- * Returned Value:
- * None.
- *
- ****************************************************************************/
- static void spi_delay(uint32_t holdtime)
- {
- volatile int i;
- for (i = 0; i < holdtime; i++);
- }
- /****************************************************************************
- * Name: spi_setfrequency
- *
- * Description:
- * Set the SPI frequency.
- *
- * Input Parameters:
- * priv - Device-specific state data
- * frequency - The SPI frequency requested
- *
- * Returned Value:
- * Returns the actual frequency selected
- *
- ****************************************************************************/
- static uint32_t spi_setfrequency(FAR struct spi_bitbang_s *priv,
- uint32_t frequency)
- {
- uint32_t pnsec;
- /* SPI frequency cannot be precisely controlled with a bit-bang interface.
- * Frequency corresponds to delay in toggle the SPI clock line: Set high,
- * wait, set low, wait, set high, wait, etc.
- *
- * Here we calcalute the half period of the frequency in nanoseconds (i.e.,
- * the amount of time that the clock should remain in the high or low
- * state).
- *
- * frequency = psec / 1 sec
- * psec = full period in seconds
- * psec = 1 sec / frequency
- * pnsec = 1000000000 nsec / (2 * frequency)
- * pnsec = full period in nsec
- *
- * As examples:
- * 1) frequency = 400KHz; SPI_PERBIT_NSEC = 100
- * pnsec = (2500 - 100) / 2 = 1200
- * 2) frequency = 20MHz; SPI_PERBIT_NSEC = 100
- * pnsec = (50 - 100( / 2 -> 0
- */
- pnsec = (1000000000ul + (frequency >> 1)) / frequency;
- /* Minus the bit transfer overhead */
- if (pnsec > SPI_PERBIT_NSEC)
- {
- pnsec -= SPI_PERBIT_NSEC;
- }
- else
- {
- pnsec = 0;
- }
- /* The hold time in nanoseconds is then half this */
- pnsec = (pnsec + 1) >> 1;
- /* But what we really want is the hold time in loop counts. We know that
- * SPI_BITBANG_LOOPSPERMSEC is the number of times through a delay loop
- * to get 1 millisecond.
- *
- * SPI_BITBANG_LOOPSPERMSEC / 1000000 is then the number of counts
- * to get 1 nanosecond. In reality, this is a number less than zero. But
- * then we can use this to calculate:
- *
- * holdtime loops/hold = pnsec nsec/hold * (SPI_BITBANG_LOOPSPERMSEC /
- * 1000000) loops/nsec
- *
- * As examples:
- * 1) frequency = 400KHz; SPI_PERBIT_NSEC = 100; pnsec = 1200;
- * SPI_BITBANG_LOOPSPERMSEC = 5000
- * holdtime = (1200 * 5000 + 500000) / 1000000 = 6
- * 2) frequency = 20MHz; SPI_PERBIT_NSEC = 100; pnsec = 0;
- * SPI_BITBANG_LOOPSPERMSEC = 5000
- * holdtime = (0 * 5000 + 500000) / 1000000 = 0
- */
- priv->holdtime = (pnsec * SPI_BITBANG_LOOPSPERMSEC + 500000) / 1000000;
- /* Let's do our best to calculate the actual frequency
- *
- * As examples:
- * 1) frequency = 400KHz; SPI_PERBIT_NSEC = 100;
- * SPI_BITBANG_LOOPSPERMSEC = 5000; holdtime = 6
- * pnsec = 2 * 1000000 * 6 / 5000 + 100 = 2500 nsec
- * frequency = 400KHz
- * 2) frequency = 20MHz; SPI_PERBIT_NSEC = 100; holdtime = 0
- * SPI_BITBANG_LOOPSPERMSEC = 5000; holdtime = 0
- * pnsec = 2 * 0 * 6 / 5000 + 100 = 100 nsec
- * frequency = 10MHz
- */
- pnsec = 2 * 1000000 * priv->holdtime / SPI_BITBANG_LOOPSPERMSEC +
- SPI_PERBIT_NSEC;
- frequency = 1000000000ul / pnsec;
- return frequency;
- }
- /****************************************************************************
- * Name: spi_setmode
- *
- * Description:
- * Select the current SPI mode
- *
- * Input Parameters:
- * priv - Device-specific state data
- * mode - The new SPI mode
- *
- * Returned Value:
- * None
- *
- ****************************************************************************/
- static void spi_setmode(FAR struct spi_bitbang_s *priv,
- enum spi_mode_e mode)
- {
- spiinfo("mode=%d\n", mode);
- switch (mode)
- {
- case SPIDEV_MODE0: /* CPOL=0; CPHA=0 */
- #ifndef SPI_BITBANG_DISABLEMODE0
- SPI_CLRSCK; /* Resting level of the clock is low */
- priv->exchange = spi_bitexchange0;
- #else
- DEBUGPANIC();
- #endif
- break;
- case SPIDEV_MODE1: /* CPOL=0; CPHA=1 */
- #ifndef SPI_BITBANG_DISABLEMODE1
- SPI_CLRSCK; /* Resting level of the clock is low */
- priv->exchange = spi_bitexchange1;
- #else
- DEBUGPANIC();
- #endif
- break;
- case SPIDEV_MODE2: /* CPOL=1; CPHA=0 */
- #ifndef SPI_BITBANG_DISABLEMODE2
- SPI_SETSCK; /* Resting level of the clock is high */
- priv->exchange = spi_bitexchange2;
- #else
- DEBUGPANIC();
- #endif
- break;
- case SPIDEV_MODE3: /* CPOL=1; CPHA=1 */
- #ifndef SPI_BITBANG_DISABLEMODE3
- SPI_SETSCK; /* Resting level of the clock is high */
- priv->exchange = spi_bitexchange3;
- #else
- DEBUGPANIC();
- #endif
- break;
- default:
- DEBUGPANIC();
- break;
- }
- }
- /****************************************************************************
- * Name: spi_bitexchange0
- *
- * Description:
- * Exchange one bit in mode 0
- *
- * MODE 0: CPOL=0 and CPHA = 0
- * The base value of the clock is zero. Data is captured on the clock's
- * rising edge and data is propagated on the falling edge (high->low
- * transition).
- *
- * /CS --+
- * |
- * +-----------------------------------------------------------------
- * <-hold time-> <-hold time-><-hold time-><-hold time-><-hold time->
- * +------------+ +------------+
- * | | | |
- * SCLK ---------------+ +------------+ +------------
- * `- Set MOSI | `- Set MOSI | `- Set MOSI
- * `- Sample MISO `- Sample MISO
- *
- * MISO /-------------X-----------\/------------X-------------\/-----------
- * MOSO \-------------X-----------/\------------X-------------/\-----------
- *
- * Input Parameters:
- * dev - Device-specific state data
- * lock - true: Lock spi bus, false: unlock SPI bus
- *
- * Returned Value:
- * None
- *
- ****************************************************************************/
- #ifndef SPI_BITBANG_DISABLEMODE0
- static uint16_t spi_bitexchange0(uint16_t dataout, uint32_t holdtime)
- {
- uint16_t datain;
- /* Here the clock is is in the resting set (low). Set MOSI output and wait
- * for the hold time
- */
- if (dataout != 0)
- {
- SPI_SETMOSI;
- }
- else
- {
- SPI_CLRMOSI;
- }
- spi_delay(holdtime);
- /* Set the clock high and sample MISO */
- SPI_SETSCK;
- datain = (uint16_t)SPI_GETMISO;
- /* Wait the required amount of hold time then put the clock back in the
- * resting state.
- */
- spi_delay(holdtime);
- SPI_CLRSCK;
- return datain;
- }
- #endif
- /****************************************************************************
- * Name: spi_bitexchange1
- *
- * Description:
- * Exchange one bit in mode 1
- *
- * MODE 1: CPOL=0 and CPHA = 1
- * The base value of the clock is zero. Data is captured on the clock's
- * falling edge and data is propagated on the rising edge
- *
- * /CS --+
- * |
- * +-----------------------------------------------------------------
- * <-hold time-> <-hold time-><-hold time-><-hold time-><-hold time->
- * +------------+ +------------+
- * | | | |
- * SCLK -+ +------------+ +------------
- * `- Set MOSI | `- Set MOSI |
- * `- Sample MISO `- Sample MISO
- *
- * MISO /-----------X------------\/-----------X-------------\/------------
- * MOSO \-----------X------------/\-----------X-------------/\------------
- *
- * Input Parameters:
- * dev - Device-specific state data
- * lock - true: Lock spi bus, false: unlock SPI bus
- *
- * Returned Value:
- * None
- *
- ****************************************************************************/
- #ifndef SPI_BITBANG_DISABLEMODE1
- static uint16_t spi_bitexchange1(uint16_t dataout, uint32_t holdtime)
- {
- uint16_t datain;
- /* The clock should be in the resting state (low). Set the clock to the
- * high state and set MOSI.
- */
- SPI_SETSCK;
- if (dataout != 0)
- {
- SPI_SETMOSI;
- }
- else
- {
- SPI_CLRMOSI;
- }
- /* Wait for the required hold time then put the clock back into the
- * resting (low) state.
- */
- spi_delay(holdtime);
- SPI_CLRSCK;
- /* Sample MISO on the falling edge and wait for the hold time again */
- datain = (uint16_t)SPI_GETMISO;
- spi_delay(holdtime);
- return datain;
- }
- #endif
- /****************************************************************************
- * Name: spi_bitexchange2
- *
- * Description:
- * Exchange one bit in mode 2
- *
- * MODE 2: CPOL=1 and CPHA = 0
- * The base value of the clock is one. Data is captured on the clock's
- * falling edge and data is propagated on the rising edge.
- *
- * /CS --+
- * |
- * +-----------------------------------------------------------------
- * <-hold time-> <-hold time-><-hold time-><-hold time-><-hold time->
- * ---------------+ +------------+ +------------
- * | | | |
- * SCLK +------------+ +------------+
- * `- Set MOSI | `- Set MOSI | `- Set MOSI
- * `- Sample MISO `- Sample MISO
- *
- * MISO /-------------X------------\/-----------X-------------\/-----------
- * MOSO \-------------X------------/\-----------X-------------/\-----------
- *
- * Input Parameters:
- * dev - Device-specific state data
- * lock - true: Lock spi bus, false: unlock SPI bus
- *
- * Returned Value:
- * None
- *
- ****************************************************************************/
- #ifndef SPI_BITBANG_DISABLEMODE2
- static uint16_t spi_bitexchange2(uint16_t dataout, uint32_t holdtime)
- {
- uint16_t datain;
- /* Here the clock is is in the resting set (high). Set MOSI output and
- * wait for the hold time
- */
- if (dataout != 0)
- {
- SPI_SETMOSI;
- }
- else
- {
- SPI_CLRMOSI;
- }
- spi_delay(holdtime);
- /* Set the clock low and sample MISO */
- SPI_CLRSCK;
- datain = (uint16_t)SPI_GETMISO;
- /* Wait the required amount of hold time then put the clock back in the
- * resting state (high).
- */
- spi_delay(holdtime);
- SPI_SETSCK;
- return datain;
- }
- #endif
- /****************************************************************************
- * Name: spi_bitexchange3
- *
- * Description:
- * Exchange one bit in mode 3
- *
- * MODE 3: CPOL=1 and CPHA = 1
- * The base value of the clock is one. Data is captured on the clock's
- * rising edge and data is propagated on the falling edge.
- *
- * /CS --+
- * |
- * +-----------------------------------------------------------------
- * <-hold time-> <-hold time-><-hold time-><-hold time-><-hold time->
- * -+ +------------+ +------------
- * | | | |
- * SCLK +------------+ +------------+
- * ` Set MOSI | `- Set MOSI |
- * `- Sample MISO `- Sample MISO
- *
- * MISO /-----------X------------\/-----------X-------------\/------------
- * MOSO \-----------X------------/\-----------X-------------/\------------
- *
- * Input Parameters:
- * dev - Device-specific state data
- * lock - true: Lock spi bus, false: unlock SPI bus
- *
- * Returned Value:
- * None
- *
- ****************************************************************************/
- #ifndef SPI_BITBANG_DISABLEMODE3
- static uint16_t spi_bitexchange3(uint16_t dataout, uint32_t holdtime)
- {
- uint16_t datain;
- /* The clock should be in the resting state (high). Set the clock to the
- * low state and set MOSI.
- */
- SPI_CLRSCK; /* Clock transition before setting MOSI */
- if (dataout != 0)
- {
- SPI_SETMOSI; /* Set MOSI if the bit is set */
- }
- else
- {
- SPI_CLRMOSI; /* Clear MOSI if the bit is not set */
- }
- /* Wait for the required hold time then put the clock back into the
- * resting (high) state.
- */
- spi_delay(holdtime);
- SPI_SETSCK;
- /* Sample MISO on the rising edge and wait for the hold time again */
- datain = (uint16_t)SPI_GETMISO;
- spi_delay(holdtime);
- return datain;
- }
- #endif
- /****************************************************************************
- * Name: spi_exchange
- *
- * Description:
- * Exahange one word of data on SPI
- *
- * Input Parameters:
- * priv - Device-specific state data
- * data - The TX data to be exchanged with the slave
- *
- * Returned Value:
- * The RX data received from the slave
- *
- ****************************************************************************/
- #ifdef CONFIG_SPI_BITBANG_VARWIDTH
- static uint16_t spi_exchange(FAR struct spi_bitbang_s *priv,
- uint16_t dataout)
- {
- bitexchange_t exchange = priv->exchange;
- uint32_t holdtime = priv->holdtime;
- uint16_t datain;
- uint16_t bit;
- int shift;
- /* Transfer each bit. This might be better done with straight-line
- * logic because the loop overhead will limit our maximum transfer
- * rate.
- */
- shift = priv->nbits - 1;
- for (bit = 1 << shift; bit != 0; bit >>= 1)
- {
- /* Shift to make space for the next, less significant bit.
- * Then exchange bits with the slave an OR in the new, returned
- * bit.
- */
- datain <<= 1;
- datain |= exchange(dataout & bit, holdtime);
- }
- return datain;
- }
- #else
- static uint16_t spi_exchange(FAR struct spi_bitbang_s *priv,
- uint16_t dataout)
- {
- bitexchange_t exchange = priv->exchange;
- uint32_t holdtime = priv->holdtime;
- uint8_t datain;
- /* Transfer each bit. This is better done with straight-line logic
- * when possible because the loop overhead will limit our maximum transfer
- * rate.
- */
- /* Exchange bit 7 with the slave */
- datain = priv->exchange(dataout & (1 << 7), holdtime);
- /* Exchange bit 6 with the slave */
- datain <<= 1;
- datain |= priv->exchange(dataout & (1 << 6), holdtime);
- /* Exchange bit 5 with the slave */
- datain <<= 1;
- datain |= priv->exchange(dataout & (1 << 5), holdtime);
- /* Exchange bit 4 with the slave */
- datain <<= 1;
- datain |= priv->exchange(dataout & (1 << 4), holdtime);
- /* Exchange bit 3 with the slave */
- datain <<= 1;
- datain |= priv->exchange(dataout & (1 << 3), holdtime);
- /* Exchange bit 2 with the slave */
- datain <<= 1;
- datain |= priv->exchange(dataout & (1 << 2), holdtime);
- /* Exchange bit 1 with the slave */
- datain <<= 1;
- datain |= priv->exchange(dataout & (1 << 1), holdtime);
- /* Exchange bit 0 with the slave */
- datain <<= 1;
- datain |= priv->exchange(dataout & (1 << 0), holdtime);
- return datain;
- }
- #endif
|