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.