- Main Page
- Related Pages
- Modules
- Data Structures
- Files

- File List
- Globals

# spi.c

Go to the documentation of this file.

    00001 /*********************************************************************************
    00002
    00003  Copyright 2006-2008 MakingThings
    00004
    00005  Licensed under the Apache License,
    00006  Version 2.0 (the "License"); you may not use this file except in compliance
    00007  with the License. You may obtain a copy of the License at
    00008
    00009  http://www.apache.org/licenses/LICENSE-2.0
    00010
    00011  Unless required by applicable law or agreed to in writing, software distributed
    00012  under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
    00013  CONDITIONS OF ANY KIND, either express or implied. See the License for
    00014  the specific language governing permissions and limitations under the License.
    00015
    00016 *********************************************************************************/
    00017
    00018 /** \file spi.c
    00019   SPI - Serial Peripheral Interface Controller.
    00020   Methods for communicating with devices via the SPI protocol.
    00021   //ToDo: need to do so locking around the Start code to make it thread safe
    00022   //ToDo: make the naming scheme consistent for the SetActive() business
    00023 */
    00024
    00025 /* Library includes. */
    00026 #include <string.h>
    00027 #include <stdio.h>
    00028
    00029 /* Scheduler includes. */
    00030 #include "FreeRTOS.h"
    00031 #include "task.h"
    00032 #include "semphr.h"
    00033
    00034 /* Hardware specific headers. */
    00035 #include "Board.h"
    00036 #include "AT91SAM7X256.h"
    00037
    00038 #include "spi.h"
    00039 #include "io.h"
    00040 #include "config.h"
    00041
    00042 // Define the SPI select lines
    00043 // and which peripheral they are on that line
    00044 #if ( CONTROLLER_VERSION == 50 )
    00045   #define SPI_SEL0_IO           IO_PA12
    00046   #define SPI_SEL0_PERIPHERAL_A 1
    00047   #define SPI_SEL1_IO           IO_PA13
    00048   #define SPI_SEL1_PERIPHERAL_A 1
    00049   #define SPI_SEL2_IO           IO_PA08
    00050   #define SPI_SEL2_PERIPHERAL_A 0
    00051   #define SPI_SEL3_IO           IO_PA09
    00052   #define SPI_SEL3_PERIPHERAL_A 0
    00053 #elif ( CONTROLLER_VERSION == 90 )
    00054   #define SPI_SEL0_IO           IO_PA12
    00055   #define SPI_SEL0_PERIPHERAL_A 1
    00056   #define SPI_SEL1_IO           IO_PA13
    00057   #define SPI_SEL1_PERIPHERAL_A 1
    00058   #define SPI_SEL2_IO           IO_PB14
    00059   #define SPI_SEL2_PERIPHERAL_A 0
    00060   #define SPI_SEL3_IO           IO_PB17
    00061   #define SPI_SEL3_PERIPHERAL_A 0
    00062 #elif ( CONTROLLER_VERSION == 95 || CONTROLLER_VERSION == 100 || CONTROLLER_VERSION == 200 )
    00063   #define SPI_SEL0_IO           IO_PA12
    00064   #define SPI_SEL0_PERIPHERAL_A 1
    00065   #define SPI_SEL1_IO           IO_PA13
    00066   #define SPI_SEL1_PERIPHERAL_A 1
    00067   #define SPI_SEL2_IO           IO_PA08
    00068   #define SPI_SEL2_PERIPHERAL_A 0
    00069   #define SPI_SEL3_IO           IO_PA09
    00070   #define SPI_SEL3_PERIPHERAL_A 0
    00071 #endif
    00072
    00073 struct Spi_
    00074 {
    00075   int users;
    00076   int channels;
    00077   xSemaphoreHandle semaphore;
    00078 } Spi;
    00079
    00080 static int  Spi_Init( void );
    00081 static void Spi_Deinit( void );
    00082 static int  Spi_GetChannelIo( int channel );
    00083 static int  Spi_GetChannelPeripheralA( int channel );
    00084
    00085 /** \defgroup Spi SPI
    00086    The SPI subsystem allows the MAKE Controller to communicate with peripheral devices via SPI.
    00087    See \ref Eeprom module for an example use.
    00088
    00089    \ingroup Core
    00090 * @{
    00091 */
    00092
    00093 /**
    00094   Configure the SPI channel.
    00095   This function locks the I/O lines associated with the SPI channel so they cannot be
    00096   used by another device on the same lines.
    00097   @param channel An integer specifying the SPI channel to configure.
    00098   @param bits An integer specifying the number of bits to transfer (8-16).
    00099   @param clockDivider An integer specifying the desired divider from the serial clock (0 - 255).
    00100   @param delayBeforeSPCK An integer specifying the delay, in ms, before the clock starts (0 - 255).
    00101   @param delayBetweenTransfers An integer specifying how long, in ms, to wait between transfers (0 - 255).
    00102   @return 0 on success.
    00103 */
    00104 int Spi_Configure( int channel, int bits, int clockDivider, int delayBeforeSPCK, int delayBetweenTransfers )
    00105 {
    00106   // Make sure the channel is locked (by someone!)
    00107   int c = 1 << channel;
    00108   if ( !( c & Spi.channels ) )
    00109     return CONTROLLER_ERROR_NOT_LOCKED;
    00110
    00111   // Check parameters
    00112   if ( bits < 8 || bits > 16 )
    00113     return CONTROLLER_ERROR_ILLEGAL_PARAMETER_VALUE;
    00114
    00115   if ( clockDivider < 0 || clockDivider > 255 )
    00116     return CONTROLLER_ERROR_ILLEGAL_PARAMETER_VALUE;
    00117
    00118   if ( delayBeforeSPCK < 0 || delayBeforeSPCK > 255 )
    00119     return CONTROLLER_ERROR_ILLEGAL_PARAMETER_VALUE;
    00120
    00121   if ( delayBetweenTransfers < 0 || delayBetweenTransfers > 255 )
    00122     return CONTROLLER_ERROR_ILLEGAL_PARAMETER_VALUE;
    00123
    00124   // Set the values
    00125   AT91C_BASE_SPI0->SPI_CSR[ channel ] =
    00126       AT91C_SPI_NCPHA | // Clock Phase TRUE
    00127       ( ( ( bits - 8 ) << 4 ) & AT91C_SPI_BITS ) | // Transfer bits
    00128       ( ( clockDivider << 8 ) & AT91C_SPI_SCBR ) | // Serial Clock Baud Rate Divider (255 = slow)
    00129       ( ( delayBeforeSPCK << 16 ) & AT91C_SPI_DLYBS ) | // Delay before SPCK
    00130       ( ( delayBetweenTransfers << 24 ) & AT91C_SPI_DLYBCT ); // Delay between transfers
    00131
    00132   return CONTROLLER_OK;
    00133 }
    00134
    00135 /**
    00136   Read/Write a block of data via SPI.
    00137   @param channel An integer specifying the device to communicate with.
    00138   @param buffer A pointer to the buffer to read to, or write from.
    00139   @param count An integer specifying the length in bytes of the buffer to read/write.
    00140   @return 0 on success.
    00141 */
    00142 int Spi_ReadWriteBlock( int channel, unsigned char* buffer, int count )
    00143 {
    00144   int r;
    00145
    00146   int address = ~( 1 << channel );
    00147
    00148   // Make sure the unit is at rest before we re-begin
    00149   if ( !( AT91C_BASE_SPI0->SPI_SR & AT91C_SPI_TXEMPTY ) )
    00150   {
    00151     while( !( AT91C_BASE_SPI0->SPI_SR & AT91C_SPI_TXEMPTY ) )
    00152       ;
    00153     while( !( AT91C_BASE_SPI0->SPI_SR & AT91C_SPI_RDRF ) )
    00154       r = AT91C_BASE_SPI0->SPI_SR;
    00155     r = AT91C_BASE_SPI0->SPI_RDR;
    00156   }
    00157
    00158   if ( AT91C_BASE_SPI0->SPI_SR & AT91C_SPI_RDRF )
    00159     r = AT91C_BASE_SPI0->SPI_RDR;
    00160
    00161   // Make the CS line hang around
    00162   AT91C_BASE_SPI0->SPI_CSR[ channel ] |= AT91C_SPI_CSAAT;
    00163
    00164   int writeIndex = 0;
    00165
    00166   unsigned char* writeP = buffer;
    00167   unsigned char* readP = buffer;
    00168
    00169   while ( writeIndex < count )
    00170   {
    00171     // Do the read write
    00172     writeIndex++;
    00173     AT91C_BASE_SPI0->SPI_TDR = ( *writeP++ & 0xFF ) |
    00174                                ( ( address << 16 ) &  AT91C_SPI_TPCS ) |
    00175                                (int)( ( writeIndex == count ) ? AT91C_SPI_LASTXFER : 0 );
    00176
    00177     while ( !( AT91C_BASE_SPI0->SPI_SR & AT91C_SPI_RDRF ) )
    00178     ;
    00179
    00180     *readP++ = (unsigned char)( AT91C_BASE_SPI0->SPI_RDR & 0xFF );
    00181   }
    00182
    00183   AT91C_BASE_SPI0->SPI_CSR[ channel ] &= ~AT91C_SPI_CSAAT;
    00184
    00185   return 0;
    00186 }
    00187
    00188 /** @}
    00189 */
    00190
    00191 int Spi_Lock( void )
    00192 {
    00193   return xSemaphoreTake( Spi.semaphore, 1000 );
    00194 }
    00195
    00196 void Spi_Unlock( void )
    00197 {
    00198   xSemaphoreGive( Spi.semaphore );
    00199 }
    00200
    00201 int Spi_Start( int channel )
    00202 {
    00203   int status;
    00204
    00205   if ( channel < 0 || channel > 3 )
    00206     return CONTROLLER_ERROR_ILLEGAL_INDEX;
    00207
    00208   // Make sure the channel isn't already being used
    00209   int c = 1 << channel;
    00210   if ( c & Spi.channels )
    00211     return CONTROLLER_ERROR_CANT_LOCK;
    00212
    00213   // lock the correct select line
    00214   int io = Spi_GetChannelIo( channel );
    00215   // Try to lock the pin
    00216   status = Io_Start( io, true );
    00217   if ( status != CONTROLLER_OK )
    00218     return status;
    00219
    00220   // Disable the PIO for the IO Line
    00221   Io_SetPio( io, false );
    00222
    00223   // Select the correct peripheral for the IO line
    00224   int peripheralA = Spi_GetChannelPeripheralA( channel );
    00225   if ( peripheralA )
    00226     Io_SetPeripheralA( io );
    00227   else
    00228     Io_SetPeripheralB( io );
    00229
    00230   if ( Spi.users == 0 )
    00231   {
    00232     status = Spi_Init();
    00233     if ( status != CONTROLLER_OK )
    00234     {
    00235       // Couldn't init for some reason, need to undo the IO lock
    00236       Io_Stop( io );
    00237       // now return with the reason for the failure
    00238       return status;
    00239     }
    00240     Spi.users++;
    00241   }
    00242
    00243   // Set the channel to locked
    00244   Spi.channels |= c;
    00245
    00246   return CONTROLLER_OK;
    00247 }
    00248
    00249 int Spi_Stop( int channel )
    00250 {
    00251   if ( channel < 0 || channel > 3 )
    00252     return CONTROLLER_ERROR_ILLEGAL_INDEX;
    00253
    00254   if ( Spi.users <= 0 )
    00255     return CONTROLLER_ERROR_TOO_MANY_STOPS;
    00256
    00257   int c = 1 << channel;
    00258   if ( !( c & Spi.channels ) )
    00259     return CONTROLLER_ERROR_NOT_LOCKED;
    00260
    00261   // release the IO
    00262   int io = Spi_GetChannelIo( channel );
    00263   // release the pin
    00264   Io_Stop( io );
    00265
    00266   Spi.channels &= ~c;
    00267
    00268   if ( --Spi.users == 0 )
    00269     Spi_Deinit();
    00270
    00271   return CONTROLLER_OK;
    00272 }
    00273
    00274 int Spi_Init()
    00275 {
    00276     int status;
    00277     status = Io_StartBits( IO_PA16_BIT | IO_PA17_BIT | IO_PA18_BIT, true );
    00278     if ( status != CONTROLLER_OK )
    00279       return status;
    00280
    00281     // Reset it
    00282     AT91C_BASE_SPI0->SPI_CR = AT91C_SPI_SWRST;
    00283
    00284       // Must confirm the peripheral clock is running
    00285     AT91C_BASE_PMC->PMC_PCER = 1 << AT91C_ID_SPI0;
    00286
    00287     // DON'T USE FDIV FLAG - it makes the SPI unit fail!!
    00288
    00289     AT91C_BASE_SPI0->SPI_MR =
    00290          AT91C_SPI_MSTR | // Select the master
    00291          AT91C_SPI_PS_VARIABLE |
    00292          AT91C_SPI_PCS | // Variable Addressing - no address here
    00293       // AT91C_SPI_PCSDEC | // Select address decode
    00294       // AT91C_SPI_FDIV | // Select Master Clock / 32 - PS DON'T EVER SET THIS>>>>  SAM7 BUG
    00295          AT91C_SPI_MODFDIS | // Disable fault detect
    00296       // AT91C_SPI_LLB | // Enable loop back test
    00297          ( ( 0x0 << 24 ) & AT91C_SPI_DLYBCS ) ;  // Delay between chip selects
    00298
    00299     AT91C_BASE_SPI0->SPI_IDR = 0x3FF; // All interupts are off
    00300
    00301     // Set up the IO lines for the peripheral
    00302
    00303     // Disable their peripherality
    00304     AT91C_BASE_PIOA->PIO_PDR =
    00305         AT91C_PA16_MISO0 |
    00306         AT91C_PA17_MOSI0 |
    00307         AT91C_PA18_SPCK0;
    00308
    00309     // Kill the pull up on the Input
    00310     AT91C_BASE_PIOA->PIO_PPUDR = AT91C_PA16_MISO0;
    00311
    00312     // Make sure the input isn't an output
    00313     AT91C_BASE_PIOA->PIO_ODR = AT91C_PA16_MISO0;
    00314
    00315     // Select the correct Devices
    00316     AT91C_BASE_PIOA->PIO_ASR =
    00317         AT91C_PA16_MISO0 |
    00318         AT91C_PA17_MOSI0 |
    00319         AT91C_PA18_SPCK0;
    00320
    00321     // Elsewhere need to do this for the select lines
    00322     // AT91C_BASE_PIOB->PIO_BSR =
    00323     //    AT91C_PB17_NPCS03;
    00324
    00325     // Fire it up
    00326     AT91C_BASE_SPI0->SPI_CR = AT91C_SPI_SPIEN;
    00327
    00328     if ( !Spi.semaphore )
    00329       vSemaphoreCreateBinary( Spi.semaphore );
    00330
    00331     return CONTROLLER_OK;
    00332 }
    00333
    00334 void Spi_Deinit()
    00335 {
    00336   // Shut down the SPI
    00337   AT91C_BASE_SPI0->SPI_CR = AT91C_SPI_SPIDIS;
    00338
    00339   // Kill the semaphore
    00340   // vSemaphoreDestroyBinary( Spi.semaphore );
    00341   // Spi.semaphore = null;
    00342
    00343   Io_StopBits( IO_PA16_BIT | IO_PA17_BIT | IO_PA18_BIT );
    00344 }
    00345
    00346 int Spi_GetChannelIo( int channel )
    00347 {
    00348   int io;
    00349   switch ( channel )
    00350   {
    00351     case 0:
    00352       io = SPI_SEL0_IO;
    00353       break;
    00354     case 1:
    00355       io = SPI_SEL1_IO;
    00356       break;
    00357     case 2:
    00358       io = SPI_SEL2_IO;
    00359       break;
    00360     case 3:
    00361       io = SPI_SEL3_IO;
    00362       break;
    00363     default:
    00364       io = 0;
    00365       break;
    00366   }
    00367   return io;
    00368 }
    00369
    00370 int Spi_GetChannelPeripheralA( int channel )
    00371 {
    00372   int peripheralA;
    00373   switch ( channel )
    00374   {
    00375     case 0:
    00376       peripheralA = SPI_SEL0_PERIPHERAL_A;
    00377       break;
    00378     case 1:
    00379       peripheralA = SPI_SEL1_PERIPHERAL_A;
    00380       break;
    00381     case 2:
    00382       peripheralA = SPI_SEL2_PERIPHERAL_A;
    00383       break;
    00384     case 3:
    00385       peripheralA = SPI_SEL3_PERIPHERAL_A;
    00386       break;
    00387     default:
    00388       peripheralA = -1;
    00389       break;
    00390   }
    00391   return peripheralA;
    00392 }

The Make Controller Kit is an open source project maintained by MakingThings.
MakingThings code is released under the Apache 2.0 license.
Bug tracker, development wiki and status can be found at http://dev.makingthings.com.
This document was last updated on 18 May 2009.