Tim Barry / mbed_blinky_offset
Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers spi_api.c Source File

spi_api.c

00001 /* mbed Microcontroller Library
00002  * Copyright (c) 2006-2013 ARM Limited
00003  *
00004  * Licensed under the Apache License, Version 2.0 (the "License");
00005  * you may not use this file except in compliance with the License.
00006  * You may obtain a copy of the License at
00007  *
00008  *     http://www.apache.org/licenses/LICENSE-2.0
00009  *
00010  * Unless required by applicable law or agreed to in writing, software
00011  * distributed under the License is distributed on an "AS IS" BASIS,
00012  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00013  * See the License for the specific language governing permissions and
00014  * limitations under the License.
00015  */
00016 #include "spi_api.h"
00017 
00018 #if DEVICE_SPI
00019 #include <math.h>
00020 
00021 #include "cmsis.h"
00022 #include "pinmap.h"
00023 #include "error.h"
00024 
00025 #if defined(TARGET_LPC1768) || defined(TARGET_LPC2368)
00026 
00027 static const PinMap PinMap_SPI_SCLK[] = {
00028     {P0_7 , SPI_1, 2},
00029     {P0_15, SPI_0, 2},
00030     {P1_20, SPI_0, 3},
00031     {P1_31, SPI_1, 2},
00032     {NC   , NC   , 0}
00033 };
00034 
00035 static const PinMap PinMap_SPI_MOSI[] = {
00036     {P0_9 , SPI_1, 2},
00037     {P0_13, SPI_1, 2},
00038     {P0_18, SPI_0, 2},
00039     {P1_24, SPI_0, 3},
00040     {NC   , NC   , 0}
00041 };
00042 
00043 static const PinMap PinMap_SPI_MISO[] = {
00044     {P0_8 , SPI_1, 2},
00045     {P0_12, SPI_1, 2},
00046     {P0_17, SPI_0, 2},
00047     {P1_23, SPI_0, 3},
00048     {NC   , NC   , 0}
00049 };
00050 
00051 static const PinMap PinMap_SPI_SSEL[] = {
00052     {P0_6 , SPI_1, 2},
00053     {P0_11, SPI_1, 2},
00054     {P0_16, SPI_0, 2},
00055     {P1_21, SPI_0, 3},
00056     {NC   , NC   , 0}
00057 };
00058 
00059 #elif defined(TARGET_LPC11U24)
00060 
00061 static const PinMap PinMap_SPI_SCLK[] = {
00062     {P0_6 , SPI_0, 0x02},
00063     {P0_10, SPI_0, 0x02},
00064     {P1_29, SPI_0, 0x01},
00065     {P1_15, SPI_1, 0x03},
00066     {P1_20, SPI_1, 0x02},
00067     {NC   , NC   , 0}
00068 };
00069 
00070 static const PinMap PinMap_SPI_MOSI[] = {
00071     {P0_9 , SPI_0, 0x01},
00072     {P0_21, SPI_1, 0x02},
00073     {P1_22, SPI_1, 0x02},
00074     {NC   , NC   , 0}
00075 };
00076 
00077 static const PinMap PinMap_SPI_MISO[] = {
00078     {P0_8 , SPI_0, 0x01},
00079     {P0_22, SPI_1, 0x03},
00080     {P1_21, SPI_1, 0x02},
00081     {NC   , NC   , 0}
00082 };
00083 
00084 static const PinMap PinMap_SPI_SSEL[] = {
00085     {P0_2 , SPI_0, 0x01},
00086     {P1_19, SPI_1, 0x02},
00087     {P1_23, SPI_1, 0x02},
00088     {NC   , NC   , 0}
00089 };
00090 
00091 #elif defined(TARGET_LPC812)
00092 
00093 static const SWM_Map SWM_SPI_SSEL[] = {
00094     {4, 16},
00095     {5, 16},
00096 };
00097 
00098 static const SWM_Map SWM_SPI_SCLK[] = {
00099     {3, 24},
00100     {4, 24},
00101 };
00102 
00103 static const SWM_Map SWM_SPI_MOSI[] = {
00104     {4, 0},
00105     {5, 0},
00106 };
00107 
00108 static const SWM_Map SWM_SPI_MISO[] = {
00109     {4, 8},
00110     {5, 16},
00111 };
00112 
00113 // bit flags for used SPIs
00114 static unsigned char spi_used = 0;
00115 static int get_available_spi(void) {
00116     int i;
00117     for (i=0; i<2; i++) {
00118         if ((spi_used & (1 << i)) == 0)
00119             return i;
00120     }
00121     return -1;
00122 }
00123 
00124 #endif
00125 
00126 static inline int ssp_disable(spi_t *obj);
00127 static inline int ssp_enable(spi_t *obj);
00128 
00129 void spi_init(spi_t *obj, PinName mosi, PinName miso, PinName sclk, PinName ssel) {
00130 #if defined(TARGET_LPC1768) || defined(TARGET_LPC2368) || defined(TARGET_LPC11U24)
00131     // determine the SPI to use
00132     SPIName spi_mosi = (SPIName)pinmap_peripheral(mosi, PinMap_SPI_MOSI);
00133     SPIName spi_miso = (SPIName)pinmap_peripheral(miso, PinMap_SPI_MISO);
00134     SPIName spi_sclk = (SPIName)pinmap_peripheral(sclk, PinMap_SPI_SCLK);
00135     SPIName spi_ssel = (SPIName)pinmap_peripheral(ssel, PinMap_SPI_SSEL);
00136     SPIName spi_data = (SPIName)pinmap_merge(spi_mosi, spi_miso);
00137     SPIName spi_cntl = (SPIName)pinmap_merge(spi_sclk, spi_ssel);
00138 #if defined(TARGET_LPC1768) || defined(TARGET_LPC2368)
00139     obj->spi = (LPC_SSP_TypeDef*)pinmap_merge(spi_data, spi_cntl);
00140 #elif defined(TARGET_LPC11U24)
00141     obj->spi = (LPC_SSPx_Type*)pinmap_merge(spi_data, spi_cntl);
00142 #endif
00143     if ((int)obj->spi == NC) {
00144         error("SPI pinout mapping failed");
00145     }
00146 #elif defined(TARGET_LPC812)
00147     int spi_n = get_available_spi();
00148     if (spi_n == -1) {
00149         error("No available SPI");
00150     }
00151     obj->spi_n = spi_n;
00152     spi_used |= (1 << spi_n);
00153     
00154     obj->spi = (spi_n) ? (LPC_SPI_TypeDef *)(LPC_SPI1_BASE) : (LPC_SPI_TypeDef *)(LPC_SPI0_BASE);
00155     
00156     const SWM_Map *swm;
00157     uint32_t regVal;
00158     
00159     swm = &SWM_SPI_SCLK[obj->spi_n];
00160     regVal = LPC_SWM->PINASSIGN[swm->n] & ~(0xFF << swm->offset);
00161     LPC_SWM->PINASSIGN[swm->n] = regVal |  (sclk   << swm->offset);
00162     
00163     swm = &SWM_SPI_MOSI[obj->spi_n];
00164     regVal = LPC_SWM->PINASSIGN[swm->n] & ~(0xFF << swm->offset);
00165     LPC_SWM->PINASSIGN[swm->n] = regVal |  (mosi   << swm->offset);
00166     
00167     swm = &SWM_SPI_MISO[obj->spi_n];
00168     regVal = LPC_SWM->PINASSIGN[swm->n] & ~(0xFF << swm->offset);
00169     LPC_SWM->PINASSIGN[swm->n] = regVal |  (miso   << swm->offset);
00170     
00171     swm = &SWM_SPI_SSEL[obj->spi_n];
00172     regVal = LPC_SWM->PINASSIGN[swm->n] & ~(0xFF << swm->offset);
00173     LPC_SWM->PINASSIGN[swm->n] = regVal |  (ssel   << swm->offset);
00174     
00175     // clear interrupts
00176     obj->spi->INTENCLR = 0x3f;
00177 #endif
00178 
00179     // enable power and clocking
00180 #if defined(TARGET_LPC1768) || defined(TARGET_LPC2368)
00181     switch ((int)obj->spi) {
00182         case SPI_0: LPC_SC->PCONP |= 1 << 21; break;
00183         case SPI_1: LPC_SC->PCONP |= 1 << 10; break;
00184     }
00185 #elif defined(TARGET_LPC11U24)
00186     switch ((int)obj->spi) {
00187         case SPI_0:
00188             LPC_SYSCON->SYSAHBCLKCTRL |= 1 << 11;
00189             LPC_SYSCON->SSP0CLKDIV = 0x01;
00190             LPC_SYSCON->PRESETCTRL |= 1 << 0;
00191             break;
00192         case SPI_1:
00193             LPC_SYSCON->SYSAHBCLKCTRL |= 1 << 18;
00194             LPC_SYSCON->SSP1CLKDIV = 0x01;
00195             LPC_SYSCON->PRESETCTRL |= 1 << 2;
00196             break;
00197     }
00198 #elif defined(TARGET_LPC812)
00199     switch (obj->spi_n) {
00200         case 0:
00201             LPC_SYSCON->SYSAHBCLKCTRL |= (1<<11);
00202             LPC_SYSCON->PRESETCTRL &= ~(0x1<<0);
00203             LPC_SYSCON->PRESETCTRL |= (0x1<<0);
00204             break;
00205         case 1:
00206             LPC_SYSCON->SYSAHBCLKCTRL |= (1<<12);
00207             LPC_SYSCON->PRESETCTRL &= ~(0x1<<1);
00208             LPC_SYSCON->PRESETCTRL |= (0x1<<1);
00209             break;
00210     }
00211     
00212 #endif
00213 
00214     // set default format and frequency
00215     if (ssel == NC) {
00216         spi_format(obj, 8, 0, 0);  // 8 bits, mode 0, master
00217     } else {
00218         spi_format(obj, 8, 0, 1);  // 8 bits, mode 0, slave
00219     }
00220     spi_frequency(obj, 1000000);
00221     
00222     // enable the ssp channel
00223     ssp_enable(obj);
00224 
00225 #if defined(TARGET_LPC1768) || defined(TARGET_LPC2368) || defined(TARGET_LPC11U24)
00226     // pin out the spi pins
00227     pinmap_pinout(mosi, PinMap_SPI_MOSI);
00228     pinmap_pinout(miso, PinMap_SPI_MISO);
00229     pinmap_pinout(sclk, PinMap_SPI_SCLK);
00230     if (ssel != NC) {
00231         pinmap_pinout(ssel, PinMap_SPI_SSEL);
00232     }
00233 #endif
00234 }
00235 
00236 void spi_free(spi_t *obj) {}
00237 
00238 void spi_format(spi_t *obj, int bits, int mode, int slave) {
00239     ssp_disable(obj);
00240     
00241 #if defined(TARGET_LPC1768) || defined(TARGET_LPC2368) || defined(TARGET_LPC11U24)
00242     if (!(bits >= 4 && bits <= 16) || !(mode >= 0 && mode <= 3)) {
00243 #elif defined(TARGET_LPC812)
00244     if (!(bits >= 1 && bits <= 16) || !(mode >= 0 && mode <= 3)) {
00245 #endif
00246         error("SPI format error");
00247     }
00248     
00249 
00250     int polarity = (mode & 0x2) ? 1 : 0;
00251     int phase = (mode & 0x1) ? 1 : 0;
00252 
00253     // set it up
00254     int DSS = bits - 1;            // DSS (data select size)
00255     int SPO = (polarity) ? 1 : 0;  // SPO - clock out polarity
00256     int SPH = (phase) ? 1 : 0;     // SPH - clock out phase
00257 
00258 #if defined(TARGET_LPC1768) || defined(TARGET_LPC2368) || defined(TARGET_LPC11U24)
00259     int FRF = 0;                   // FRF (frame format) = SPI
00260     uint32_t tmp = obj->spi->CR0;
00261     tmp &= ~(0xFFFF);
00262     tmp |= DSS << 0
00263         | FRF << 4
00264         | SPO << 6
00265         | SPH << 7;
00266     obj->spi->CR0 = tmp;
00267 
00268     tmp = obj->spi->CR1;
00269     tmp &= ~(0xD);
00270     tmp |= 0 << 0                   // LBM - loop back mode - off
00271         | ((slave) ? 1 : 0) << 2   // MS - master slave mode, 1 = slave
00272         | 0 << 3;                  // SOD - slave output disable - na
00273     obj->spi->CR1 = tmp;
00274 #elif defined(TARGET_LPC812)
00275     uint32_t tmp = obj->spi->CFG;
00276     tmp &= ~((1 << 2) | (1 << 4) | (1 << 5));
00277     tmp |= (SPH << 4) | (SPO << 5) | ((slave ? 0 : 1) << 2);
00278     obj->spi->CFG = tmp;
00279     
00280     // select frame length
00281     tmp = obj->spi->TXDATCTL;
00282     tmp &= ~(0xf << 24);
00283     tmp |= (DSS << 24);
00284     obj->spi->TXDATCTL = tmp;
00285 #endif
00286 
00287     ssp_enable(obj);
00288 }
00289 
00290 void spi_frequency(spi_t *obj, int hz) {
00291     ssp_disable(obj);
00292 
00293     // setup the spi clock diveder to /1
00294 #if defined(TARGET_LPC1768) || defined(TARGET_LPC2368)
00295     switch ((int)obj->spi) {
00296         case SPI_0:
00297             LPC_SC->PCLKSEL1 &= ~(3 << 10);
00298             LPC_SC->PCLKSEL1 |=  (1 << 10);
00299             break;
00300         case SPI_1:
00301             LPC_SC->PCLKSEL0 &= ~(3 << 20);
00302             LPC_SC->PCLKSEL0 |=  (1 << 20);
00303             break;
00304     }
00305 #endif
00306 
00307     uint32_t PCLK = SystemCoreClock ;
00308     
00309 #if defined(TARGET_LPC1768) || defined(TARGET_LPC2368) || defined(TARGET_LPC11U24)
00310     int prescaler;
00311 
00312     for (prescaler = 2; prescaler <= 254; prescaler += 2) {
00313         int prescale_hz = PCLK / prescaler;
00314 
00315         // calculate the divider
00316         int divider = floor(((float)prescale_hz / (float)hz) + 0.5);
00317 
00318         // check we can support the divider
00319         if (divider < 256) {
00320             // prescaler
00321             obj->spi->CPSR = prescaler;
00322 
00323             // divider
00324             obj->spi->CR0 &= ~(0xFFFF << 8);
00325             obj->spi->CR0 |= (divider - 1) << 8;
00326             ssp_enable(obj);
00327             return;
00328         }
00329     }
00330     error("Couldn't setup requested SPI frequency");
00331 #elif defined(TARGET_LPC812)
00332     obj->spi->DIV = PCLK/hz - 1;
00333     obj->spi->DLY = 0;
00334     ssp_enable(obj);
00335 #endif
00336 }
00337 
00338 static inline int ssp_disable(spi_t *obj) {
00339 #if defined(TARGET_LPC1768) || defined(TARGET_LPC2368) || defined(TARGET_LPC11U24)
00340     return obj->spi->CR1 &= ~(1 << 1);
00341 #elif defined(TARGET_LPC812)
00342     return obj->spi->CFG &= ~(1 << 0);
00343 #endif
00344 }
00345 
00346 static inline int ssp_enable(spi_t *obj) {
00347 #if defined(TARGET_LPC1768) || defined(TARGET_LPC2368) || defined(TARGET_LPC11U24)
00348     return obj->spi->CR1 |= (1 << 1);
00349 #elif defined(TARGET_LPC812)
00350     return obj->spi->CFG |= (1 << 0);
00351 #endif
00352 }
00353 
00354 static inline int ssp_readable(spi_t *obj) {
00355 #if defined(TARGET_LPC1768) || defined(TARGET_LPC2368) || defined(TARGET_LPC11U24)
00356     return obj->spi->SR & (1 << 2);
00357 #elif defined(TARGET_LPC812)
00358     return obj->spi->STAT & (1 << 0);
00359 #endif
00360 }
00361 
00362 static inline int ssp_writeable(spi_t *obj) {
00363 #if defined(TARGET_LPC1768) || defined(TARGET_LPC2368) || defined(TARGET_LPC11U24)
00364     return obj->spi->SR & (1 << 1);
00365 #elif defined(TARGET_LPC812)
00366     return obj->spi->STAT & (1 << 1);
00367 #endif
00368 }
00369 
00370 static inline void ssp_write(spi_t *obj, int value) {
00371     while (!ssp_writeable(obj));
00372 #if defined(TARGET_LPC1768) || defined(TARGET_LPC2368) || defined(TARGET_LPC11U24)
00373     obj->spi->DR = value;
00374 #elif defined(TARGET_LPC812)
00375     // end of transfer
00376     obj->spi->TXDATCTL |= (1 << 20);
00377     obj->spi->TXDAT = value;
00378 #endif
00379 }
00380 
00381 static inline int ssp_read(spi_t *obj) {
00382     while (!ssp_readable(obj));
00383 #if defined(TARGET_LPC1768) || defined(TARGET_LPC2368) || defined(TARGET_LPC11U24)
00384     return obj->spi->DR;
00385 #elif defined(TARGET_LPC812)
00386     return obj->spi->RXDAT;
00387 #endif
00388 }
00389 
00390 static inline int ssp_busy(spi_t *obj) {
00391 #if defined(TARGET_LPC1768) || defined(TARGET_LPC2368) || defined(TARGET_LPC11U24)
00392     return (obj->spi->SR & (1 << 4)) ? (1) : (0);
00393 #elif defined(TARGET_LPC812)
00394     // TODO
00395     return 0;
00396 #endif
00397 }
00398 
00399 int spi_master_write(spi_t *obj, int value) {
00400     ssp_write(obj, value);
00401     return ssp_read(obj);
00402 }
00403 
00404 int spi_slave_receive(spi_t *obj) {
00405     return (ssp_readable(obj) && !ssp_busy(obj)) ? (1) : (0);
00406 };
00407 
00408 int spi_slave_read(spi_t *obj) {
00409 #if defined(TARGET_LPC1768) || defined(TARGET_LPC2368) || defined(TARGET_LPC11U24)
00410     return obj->spi->DR;
00411 #elif defined(TARGET_LPC812)
00412     return obj->spi->RXDAT;
00413 #endif
00414 }
00415 
00416 void spi_slave_write(spi_t *obj, int value) {
00417     while (ssp_writeable(obj) == 0) ;
00418 #if defined(TARGET_LPC1768) || defined(TARGET_LPC2368) || defined(TARGET_LPC11U24)
00419     obj->spi->DR = value;
00420 #elif defined(TARGET_LPC812)
00421     obj->spi->TXDAT = value;
00422 #endif
00423 }
00424 
00425 int spi_busy(spi_t *obj) {
00426     return ssp_busy(obj);
00427 }
00428 
00429 #endif