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

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 8 Jan 2009.