Nut/OS  4.10.3
API Reference
spibus0gpio.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2009 by egnite GmbH
00003  *
00004  * All rights reserved.
00005  *
00006  * Redistribution and use in source and binary forms, with or without
00007  * modification, are permitted provided that the following conditions
00008  * are met:
00009  *
00010  * 1. Redistributions of source code must retain the above copyright
00011  *    notice, this list of conditions and the following disclaimer.
00012  * 2. Redistributions in binary form must reproduce the above copyright
00013  *    notice, this list of conditions and the following disclaimer in the
00014  *    documentation and/or other materials provided with the distribution.
00015  * 3. Neither the name of the copyright holders nor the names of
00016  *    contributors may be used to endorse or promote products derived
00017  *    from this software without specific prior written permission.
00018  *
00019  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00020  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00021  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00022  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00023  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00024  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00025  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
00026  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
00027  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
00028  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
00029  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00030  * SUCH DAMAGE.
00031  *
00032  * For additional information see http://www.ethernut.de/
00033  */
00034 
00044 #include <cfg/spi.h>
00045 #include <cfg/arch/gpio.h>
00046 
00047 #include <dev/gpio.h>
00048 #include <dev/spibus_gpio.h>
00049 #include <sys/event.h>
00050 #include <sys/timer.h>
00051 #include <sys/nutdebug.h>
00052 
00053 #include <errno.h>
00054 #include <stdlib.h>
00055 
00056 /* Targets with single ports may not have set the port number. */
00057 #if defined(SBBI0_MISO_BIT) && !defined(SBBI0_MISO_PORT)
00058 #define SBBI0_MISO_PORT NUTGPIO_PORT
00059 #endif
00060 #if defined(SBBI0_MOSI_BIT) && !defined(SBBI0_MOSI_PORT)
00061 #define SBBI0_MOSI_PORT NUTGPIO_PORT
00062 #endif
00063 #if defined(SBBI0_SCK_BIT) && !defined(SBBI0_SCK_PORT)
00064 #define SBBI0_SCK_PORT  NUTGPIO_PORT
00065 #endif
00066 #if defined(SBBI0_CS0_BIT) && !defined(SBBI0_CS0_PORT)
00067 #define SBBI0_CS0_PORT  NUTGPIO_PORT
00068 #endif
00069 #if defined(SBBI0_CS1_BIT) && !defined(SBBI0_CS1_PORT)
00070 #define SBBI0_CS1_PORT  NUTGPIO_PORT
00071 #endif
00072 #if defined(SBBI0_CS2_BIT) && !defined(SBBI0_CS2_PORT)
00073 #define SBBI0_CS2_PORT  NUTGPIO_PORT
00074 #endif
00075 #if defined(SBBI0_CS3_BIT) && !defined(SBBI0_CS3_PORT)
00076 #define SBBI0_CS3_PORT  NUTGPIO_PORT
00077 #endif
00078 
00079 static GSPIREG gspi_reg0;
00080 #if defined(SBBI0_CS1_BIT)
00081 static GSPIREG gspi_reg1;
00082 #endif
00083 #if defined(SBBI0_CS2_BIT)
00084 static GSPIREG gspi_reg2;
00085 #endif
00086 #if defined(SBBI0_CS3_BIT)
00087 static GSPIREG gspi_reg3;
00088 #endif
00089 
00093 static GSPIREG *GpioSpi0ChipSelect(uint_fast8_t cs, uint_fast8_t hi)
00094 {
00095     GSPIREG *rc;
00096 
00097     switch (cs) {
00098     case 0:
00099         /* If CS0 is undefined, we assume permanent selection. */
00100 #if defined(SBBI0_CS0_BIT)
00101         GpioPinSet(SBBI0_CS0_PORT, SBBI0_CS0_BIT, hi);
00102         GpioPinConfigSet(SBBI0_CS0_PORT, SBBI0_CS0_BIT, GPIO_CFG_OUTPUT);
00103 #endif
00104         rc = &gspi_reg0;
00105         break;
00106 #if defined(SBBI0_CS1_BIT)
00107     case 1:
00108         GpioPinSet(SBBI0_CS1_PORT, SBBI0_CS1_BIT, hi);
00109         GpioPinConfigSet(SBBI0_CS1_PORT, SBBI0_CS1_BIT, GPIO_CFG_OUTPUT);
00110         rc = &gspi_reg1;
00111         break;
00112 #endif
00113 #if defined(SBBI0_CS2_BIT)
00114     case 2:
00115         GpioPinSet(SBBI0_CS2_PORT, SBBI0_CS2_BIT, hi);
00116         GpioPinConfigSet(SBBI0_CS2_PORT, SBBI0_CS2_BIT, GPIO_CFG_OUTPUT);
00117         rc = &gspi_reg2;
00118         break;
00119 #endif
00120 #if defined(SBBI0_CS3_BIT)
00121     case 3:
00122         GpioPinSet(SBBI0_CS3_PORT, SBBI0_CS3_BIT, hi);
00123         GpioPinConfigSet(SBBI0_CS3_PORT, SBBI0_CS3_BIT, GPIO_CFG_OUTPUT);
00124         rc = &gspi_reg3;
00125         break;
00126 #endif
00127     default:
00128         errno = EIO;
00129         rc = NULL;
00130         break;
00131     }
00132     return rc;
00133 }
00134 
00135 /* Idle clock is low and data is captured on the rising edge. */
00136 static void SpiMode0Transfer(GSPIREG *gspi, CONST uint8_t *txbuf, uint8_t *rxbuf, int xlen)
00137 {
00138 #if defined(SBBI0_SCK_BIT)
00139     uint_fast8_t mask;
00140 
00141     while (xlen--) {
00142         for (mask = 0x80; mask; mask >>= 1) {
00143 #if defined(SBBI0_MOSI_BIT)
00144             if (txbuf) {
00145                 GpioPinSet(SBBI0_MOSI_PORT, SBBI0_MOSI_BIT, (*txbuf & mask) != 0);
00146             }
00147 #endif /* SBBI0_MOSI_BIT */
00148             NutMicroDelay(gspi->gspi_dly_rate);
00149             GpioPinSetHigh(SBBI0_SCK_PORT, SBBI0_SCK_BIT);
00150 #if defined(SBBI0_MISO_BIT)
00151             if (rxbuf) {
00152                 if (GpioPinGet(SBBI0_MISO_PORT, SBBI0_MISO_BIT)) {
00153                     *rxbuf |= mask;
00154                 }
00155                 else {
00156                     *rxbuf &= ~mask;
00157                 }
00158             }
00159 #endif /* SBBI0_MISO_BIT */
00160             NutMicroDelay(gspi->gspi_dly_rate);
00161             GpioPinSetLow(SBBI0_SCK_PORT, SBBI0_SCK_BIT);
00162         }
00163         if (txbuf) {
00164             txbuf++;
00165         }
00166         if (rxbuf) {
00167             rxbuf++;
00168         }
00169     }
00170 #endif /* SBBI0_SCK_BIT */
00171 }
00172 
00173 /* Idle clock is low and data is captured on the falling edge. */
00174 static void SpiMode1Transfer(GSPIREG *gspi, CONST uint8_t *txbuf, uint8_t *rxbuf, int xlen)
00175 {
00176 #if defined(SBBI0_SCK_BIT)
00177     uint_fast8_t mask;
00178 
00179     while (xlen--) {
00180         for (mask = 0x80; mask; mask >>= 1) {
00181             NutMicroDelay(gspi->gspi_dly_rate);
00182             GpioPinSetHigh(SBBI0_SCK_PORT, SBBI0_SCK_BIT);
00183 #if defined(SBBI0_MOSI_BIT)
00184             if (txbuf) {
00185                 GpioPinSet(SBBI0_MOSI_PORT, SBBI0_MOSI_BIT, (*txbuf & mask) != 0);
00186             }
00187 #endif /* SBBI0_MOSI_BIT */
00188             NutMicroDelay(gspi->gspi_dly_rate);
00189             GpioPinSetLow(SBBI0_SCK_PORT, SBBI0_SCK_BIT);
00190 #if defined(SBBI0_MISO_BIT)
00191             if (rxbuf) {
00192                 if (GpioPinGet(SBBI0_MISO_PORT, SBBI0_MISO_BIT)) {
00193                     *rxbuf |= mask;
00194                 }
00195                 else {
00196                     *rxbuf &= ~mask;
00197                 }
00198             }
00199 #endif /* SBBI0_MISO_BIT */
00200         }
00201         if (txbuf) {
00202             txbuf++;
00203         }
00204         if (rxbuf) {
00205             rxbuf++;
00206         }
00207     }
00208 #endif /* SBBI0_SCK_BIT */
00209 }
00210 
00211 /* Idle clock is high and data is captured on the falling edge. */
00212 static void SpiMode2Transfer(GSPIREG *gspi, CONST uint8_t *txbuf, uint8_t *rxbuf, int xlen)
00213 {
00214 #if defined(SBBI0_SCK_BIT)
00215     uint_fast8_t mask;
00216 
00217     while (xlen--) {
00218         for (mask = 0x80; mask; mask >>= 1) {
00219 #if defined(SBBI0_MOSI_BIT)
00220             if (txbuf) {
00221                 GpioPinSet(SBBI0_MOSI_PORT, SBBI0_MOSI_BIT, (*txbuf & mask) != 0);
00222             }
00223 #endif /* SBBI0_MOSI_BIT */
00224             NutMicroDelay(gspi->gspi_dly_rate);
00225             GpioPinSetLow(SBBI0_SCK_PORT, SBBI0_SCK_BIT);
00226 #if defined(SBBI0_MISO_BIT)
00227             if (rxbuf) {
00228                 if (GpioPinGet(SBBI0_MISO_PORT, SBBI0_MISO_BIT)) {
00229                     *rxbuf |= mask;
00230                 }
00231                 else {
00232                     *rxbuf &= ~mask;
00233                 }
00234             }
00235 #endif /* SBBI0_MISO_BIT */
00236             NutMicroDelay(gspi->gspi_dly_rate);
00237             GpioPinSetHigh(SBBI0_SCK_PORT, SBBI0_SCK_BIT);
00238         }
00239         if (txbuf) {
00240             txbuf++;
00241         }
00242         if (rxbuf) {
00243             rxbuf++;
00244         }
00245     }
00246 #endif /* SBBI0_SCK_BIT */
00247 }
00248 
00249 /* Idle clock is high and data is captured on the rising edge. */
00250 static void SpiMode3Transfer(GSPIREG *gspi, CONST uint8_t *txbuf, uint8_t *rxbuf, int xlen)
00251 {
00252 #if defined(SBBI0_SCK_BIT)
00253     uint_fast8_t mask;
00254 
00255     while (xlen--) {
00256         for (mask = 0x80; mask; mask >>= 1) {
00257             NutMicroDelay(gspi->gspi_dly_rate);
00258             GpioPinSetLow(SBBI0_SCK_PORT, SBBI0_SCK_BIT);
00259 #if defined(SBBI0_MOSI_BIT)
00260             if (txbuf) {
00261                 GpioPinSet(SBBI0_MOSI_PORT, SBBI0_MOSI_BIT, (*txbuf & mask) != 0);
00262             }
00263 #endif /* SBBI0_MOSI_BIT */
00264             NutMicroDelay(gspi->gspi_dly_rate);
00265             GpioPinSetHigh(SBBI0_SCK_PORT, SBBI0_SCK_BIT);
00266 #if defined(SBBI0_MISO_BIT)
00267             if (rxbuf) {
00268                 if (GpioPinGet(SBBI0_MISO_PORT, SBBI0_MISO_BIT)) {
00269                     *rxbuf |= mask;
00270                 }
00271                 else {
00272                     *rxbuf &= ~mask;
00273                 }
00274             }
00275 #endif /* SBBI0_MISO_BIT */
00276         }
00277         if (txbuf) {
00278             txbuf++;
00279         }
00280         if (rxbuf) {
00281             rxbuf++;
00282         }
00283     }
00284 #endif /* SBBI0_SCK_BIT */
00285 }
00286 
00301 int GpioSpiBus0Transfer(NUTSPINODE * node, CONST void *txbuf, void *rxbuf, int xlen)
00302 {
00303     GSPIREG *gspi;
00304 
00305     /* Sanity check. */
00306     NUTASSERT(node != NULL);
00307     NUTASSERT(node->node_stat != NULL);
00308     gspi = (GSPIREG *)node->node_stat;
00309 
00310     /* We use dedicated static routines for each mode in the hope that 
00311     ** this improves the compiler's optimization for the inner loop. */
00312     switch (node->node_mode & SPI_MODE_3) {
00313     case SPI_MODE_0:
00314         SpiMode0Transfer(gspi, txbuf, rxbuf, xlen);
00315         break;
00316     case SPI_MODE_1:
00317         SpiMode1Transfer(gspi, txbuf, rxbuf, xlen);
00318         break;
00319     case SPI_MODE_2:
00320         SpiMode2Transfer(gspi, txbuf, rxbuf, xlen);
00321         break;
00322     case SPI_MODE_3:
00323         SpiMode3Transfer(gspi, txbuf, rxbuf, xlen);
00324         break;
00325     }
00326     return 0;
00327 }
00328 
00339 int GpioSpiBus0NodeInit(NUTSPINODE * node)
00340 {
00341     /* Sanity check. */
00342     NUTASSERT(node != NULL);
00343 
00344     /* Try to deactivate the node's chip select. */
00345     node->node_stat = GpioSpi0ChipSelect(node->node_cs, (node->node_mode & SPI_MODE_CSHIGH) == 0);
00346     if (node->node_stat == NULL) {
00347         /* Chip select not configured. */
00348         return -1;
00349     }
00350     return GpioSpiSetup(node);
00351 }
00352 
00364 int GpioSpiBus0Select(NUTSPINODE * node, uint32_t tmo)
00365 {
00366     int rc;
00367 
00368     /* Sanity check. */
00369     NUTASSERT(node != NULL);
00370     NUTASSERT(node->node_stat != NULL);
00371 
00372     /* Allocate the bus. */
00373     rc = NutEventWait(&node->node_bus->bus_mutex, tmo);
00374     if (rc) {
00375         errno = EIO;
00376     } else {
00377         /* Do the update, if the mode update bit is set. */
00378         if (node->node_mode & SPI_MODE_UPDATE) {
00379             GpioSpiSetup(node);
00380         }
00381         /* Set clock output using the correct idle mode level. */
00382 #if defined(SBBI0_SCK_BIT)
00383         GpioPinSetLow(SBBI0_SCK_PORT, (node->node_mode & SPI_MODE_CPOL) != 0);
00384         GpioPinConfigSet(SBBI0_SCK_PORT, SBBI0_SCK_BIT, GPIO_CFG_OUTPUT);
00385 #endif
00386         /* Enable MOSI output and MISO input. */
00387 #if defined(SBBI0_MOSI_BIT)
00388         GpioPinConfigSet(SBBI0_MOSI_PORT, SBBI0_MOSI_BIT, GPIO_CFG_OUTPUT);
00389 #endif
00390 #if defined(SBBI0_MISO_BIT)
00391         GpioPinConfigSet(SBBI0_MISO_PORT, SBBI0_MISO_BIT, 0);
00392 #endif
00393         /* Activate the node's chip select. */
00394         if (GpioSpi0ChipSelect(node->node_cs, (node->node_mode & SPI_MODE_CSHIGH) != 0) == NULL) {
00395             /* Release the bus in case of an error. */
00396             NutEventPost(&node->node_bus->bus_mutex);
00397             rc = -1;
00398         }
00399     }
00400     return rc;
00401 }
00402 
00411 int GpioSpiBus0Deselect(NUTSPINODE * node)
00412 {
00413     /* Sanity check. */
00414     NUTASSERT(node != NULL);
00415     NUTASSERT(node->node_bus != NULL);
00416 
00417     /* Deactivate the node's chip select. */
00418     GpioSpi0ChipSelect(node->node_cs, (node->node_mode & SPI_MODE_CSHIGH) == 0);
00419 
00420     /* Release the bus. */
00421     NutEventPost(&node->node_bus->bus_mutex);
00422 
00423     return 0;
00424 }
00425 
00429 NUTSPIBUS spiBus0Gpio = {
00430     NULL,                       
00431     NULL,                       
00432     0,                          
00433     NULL,                       
00434     GpioSpiBus0NodeInit,        
00435     GpioSpiBus0Select,          
00436     GpioSpiBus0Deselect,        
00437     GpioSpiBus0Transfer,        
00438     NutSpiBusWait,              
00439     NutSpiBusSetMode,           
00440     NutSpiBusSetRate,           
00441     NutSpiBusSetBits            
00442 };