MakingThings
  • 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.