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.