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 // MakingThings - Make Controller Board - 2006 00019 00020 /** \file analogin.c 00021 Functions for reading the analog inputs on the MAKE Application Board. 00022 */ 00023 00024 /* Library includes. */ 00025 #include <string.h> 00026 #include <stdio.h> 00027 00028 /* Scheduler includes. */ 00029 #include "FreeRTOS.h" 00030 #include "task.h" 00031 #include "semphr.h" 00032 00033 /* Hardware specific headers. */ 00034 #include "Board.h" 00035 #include "AT91SAM7X256.h" 00036 00037 #include "config.h" 00038 00039 #include "io.h" 00040 00041 #include "analogin.h" 00042 #include "analogin_internal.h" 00043 00044 #define ANALOGIN_0_IO IO_PB27 00045 #define ANALOGIN_1_IO IO_PB28 00046 #define ANALOGIN_2_IO IO_PB29 00047 #define ANALOGIN_3_IO IO_PB30 00048 00049 #define AUTOSENDSAVE 0xDF 00050 00051 static int AnalogIn_Start( int index ); 00052 static int AnalogIn_Stop( int index ); 00053 00054 static int AnalogIn_Init( void ); 00055 static int AnalogIn_Deinit( void ); 00056 00057 static int AnalogIn_GetIo( int index ); 00058 00059 extern void ( AnalogInIsr_Wrapper )( void ); 00060 00061 struct AnalogIn_* AnalogIn; 00062 00063 /** \defgroup AnalogIn Analog Inputs 00064 The AnalogIn subsystem converts 0-3.3V signals to 10-bit digital values. 00065 The analog to digital converters read incoming signals from 0 - 3.3V. They are rated as 5V tolerant, 00066 and indeed can momentarily withstand higher voltages, but will not return meaningful values for anything 00067 above 3.3V.\n 00068 00069 Converting the 0 - 1023 reading of the AnalogIn channel into a voltage is performed as follows: 00070 \verbatim v = 3.3 * ( a / 1023.0 ) \endverbatim 00071 where a is the AnalogIn value 00072 00073 This is of course a floating point operation (slowish) using a division (slowish). Fixed point 00074 versions may be more suitable for some applications. Where reduced accuracy is acceptable, the following 00075 can be used to get the input as a percentage of 3.3V: 00076 \verbatim p = ( 100 * a ) / 1023 \endverbatim 00077 00078 Initializing the controller's AnalogIn system is pretty involved (see AnalogIn_Init() in AnalogIn->c). There are a lot of 00079 different options - different converter speeds, DMA access, etc. We've chosen something pretty simple here. 00080 More ambitious users may wish to alter the implementation. 00081 00082 \todo Provide multi-channel conversion routines 00083 00084 \ingroup Core 00085 @{ 00086 */ 00087 00088 /** 00089 Sets whether the specified channel is active. 00090 This initializes the AnalogIn system and gets a lock on its IO lines. It only needs to be called once. 00091 @param index An integer specifying the channel (0 - 7). 00092 @param state An integer specifying the active state - 1 (active) or 0 (inactive). 00093 @return Zero on success. 00094 00095 00096 \par Example 00097 \code 00098 if( AnalogIn_SetActive( 0, 1 ) == CONTROLLER_OK ) // set analogin 0 to active 00099 // then continue processing 00100 else 00101 // some error occurred 00102 \endcode 00103 */ 00104 int AnalogIn_SetActive( int index, int state ) 00105 { 00106 if ( index < 0 || index >= ANALOGIN_CHANNELS ) 00107 return CONTROLLER_ERROR_ILLEGAL_INDEX; 00108 00109 if ( state ) 00110 { 00111 if( AnalogIn == NULL ) 00112 { 00113 AnalogIn = MallocWait( sizeof( struct AnalogIn_ ), 100 ); 00114 AnalogIn->users = 0; 00115 int i; 00116 for( i = 0; i < ANALOGIN_CHANNELS; i++ ) 00117 AnalogIn->channelUsers[ i ] = 0; 00118 #ifdef OSC 00119 AnalogIn_AutoSendInit( ); 00120 #endif 00121 } 00122 00123 return AnalogIn_Start( index ); 00124 } 00125 else 00126 { 00127 // TODO: check if any channels are still active and if not, Free( ) the AnalogIn struct 00128 return AnalogIn_Stop( index ); 00129 } 00130 } 00131 00132 /** 00133 Returns the active state of a channel. 00134 @param index An integer specifying the ANALOGIN channel (0 - 7). 00135 @return State - 1/non-zero (active) or 0 (inactive). 00136 00137 \par Example 00138 \code 00139 int active = AnalogIn_GetActive( 0 ) // check whether analogin 0 is active 00140 \endcode 00141 */ 00142 int AnalogIn_GetActive( int index ) 00143 { 00144 if( AnalogIn == NULL ) 00145 return 0; 00146 00147 if ( index < 0 || index >= ANALOGIN_CHANNELS ) 00148 return false; 00149 return AnalogIn->channelUsers[ index ] > 0; 00150 } 00151 00152 /** 00153 Read the value of an analog input. 00154 @param index An integer specifying which input (0-7). 00155 @return The value as an integer (0 - 1023). 00156 00157 \par Example 00158 \code 00159 int analogin1 = AnalogIn_GetValue( 1 ); 00160 \endcode 00161 */ 00162 int AnalogIn_GetValue( int index ) 00163 { 00164 AnalogIn_SetActive( index, 1 ); 00165 if ( index < 0 || index >= ANALOGIN_CHANNELS ) 00166 return CONTROLLER_ERROR_ILLEGAL_INDEX; 00167 00168 if ( AnalogIn->channelUsers[ index ] < 1 ) 00169 { 00170 int status = AnalogIn_Start( index ); 00171 if ( status != CONTROLLER_OK ) 00172 return status; 00173 } 00174 00175 int value; 00176 00177 if ( !xSemaphoreTake( AnalogIn->semaphore, 1000 ) ) 00178 return -1; 00179 00180 /* Third Step: Select the active channel */ 00181 int mask = 1 << index; 00182 AT91C_BASE_ADC->ADC_CHDR = ~mask; 00183 AT91C_BASE_ADC->ADC_CHER = mask; 00184 00185 /* Fourth Step: Start the conversion */ 00186 AT91C_BASE_ADC->ADC_CR = AT91C_ADC_START; 00187 00188 /* Busy wait */ 00189 // while ( !( AT91C_BASE_ADC->ADC_CHSR & ( 1 << index ) ) ); 00190 00191 if ( !xSemaphoreTake( AnalogIn->doneSemaphore, 1000 ) ) 00192 return -1; 00193 00194 value = AT91C_BASE_ADC->ADC_LCDR & 0xFFFF; 00195 00196 xSemaphoreGive( AnalogIn->semaphore ); 00197 00198 return value; 00199 } 00200 00201 /** 00202 Read the value of several of the analog inputs. 00203 Due to the current ISR handling, this isn't too much quicker than making 00204 calls to each of the channels individually. 00205 @param mask A bit mask specifying which channels to read. 00206 @param values A pointer to an int array to be filled with the values. 00207 @return 0 on success, otherwise non-zero. 00208 00209 \par Example 00210 \code 00211 int mask = 0xFF; 00212 int samples[8]; 00213 AnalogIn_GetValueMulti( mask, samples ); // now samples is filled with all the analogin values 00214 \endcode 00215 */ 00216 int AnalogIn_GetValueMulti( int mask, int values[] ) 00217 { 00218 //AnalogIn_SetActive( 1 ); 00219 if ( mask < 0 || mask > 255 ) // check the value is a valid 8-bit mask 00220 return CONTROLLER_ERROR_ILLEGAL_INDEX; 00221 00222 int i; // Is this the best way to make sure everything is started up properly? 00223 for( i = 0; i < 8; i++ ) 00224 { 00225 if( mask >> i & 1 ) 00226 { 00227 if ( AnalogIn->channelUsers[ i ] < 1 ) 00228 { 00229 int status = AnalogIn_Start( i ); 00230 if ( status != CONTROLLER_OK ) 00231 return status; 00232 } 00233 } 00234 } 00235 00236 if ( !xSemaphoreTake( AnalogIn->semaphore, 1000 ) ) 00237 return -1; 00238 00239 /* Third Step: Select the active channels */ 00240 AT91C_BASE_ADC->ADC_CHDR = ~mask; 00241 AT91C_BASE_ADC->ADC_CHER = mask; 00242 00243 /* Fourth Step: Start the conversion */ 00244 AT91C_BASE_ADC->ADC_CR = AT91C_ADC_START; 00245 00246 if ( !xSemaphoreTake( AnalogIn->doneSemaphore, 1000 ) ) 00247 return -1; 00248 00249 //Figure out which of the channels we want to read 00250 volatile uint* reg = &AT91C_BASE_ADC->ADC_CDR0; // the address of the first ADC result register 00251 for( i = 0; i < 8; i++ ) 00252 { 00253 if( mask >> i & 1 ) 00254 values[ i ] = *reg++ & 0xFFFF; 00255 } 00256 00257 xSemaphoreGive( AnalogIn->semaphore ); 00258 00259 return CONTROLLER_OK; 00260 } 00261 00262 /** 00263 Read the value of an analog input without the use of any OS services. 00264 Note that this is not thread safe and shouldn't be used if another 00265 part of the code might be using it or the thread safe versions. 00266 @param index An integer specifying which input (0-7). 00267 @return The value as an integer (0 - 1023). 00268 00269 \par Example 00270 \code 00271 int analogin1 = AnalogIn_GetValueWait( 1 ); 00272 \endcode 00273 */ 00274 int AnalogIn_GetValueWait( int index ) 00275 { 00276 AnalogIn_SetActive( index, 1 ); 00277 if ( index < 0 || index >= ANALOGIN_CHANNELS ) 00278 return CONTROLLER_ERROR_ILLEGAL_INDEX; 00279 00280 if ( AnalogIn->channelUsers[ index ] < 1 ) 00281 { 00282 int status = AnalogIn_Start( index ); 00283 if ( status != CONTROLLER_OK ) 00284 return status; 00285 } 00286 00287 int value = 0; 00288 00289 // Third Step: Select the active channel 00290 int mask = 1 << index; 00291 AT91C_BASE_ADC->ADC_CHDR = ~mask; 00292 AT91C_BASE_ADC->ADC_CHER = mask; 00293 00294 AT91C_BASE_ADC->ADC_IDR = AT91C_ADC_DRDY; 00295 00296 // Fourth Step: Start the conversion 00297 AT91C_BASE_ADC->ADC_CR = AT91C_ADC_START; 00298 00299 value++; 00300 value++; 00301 00302 // Busy wait 00303 while ( !( AT91C_BASE_ADC->ADC_SR & AT91C_ADC_DRDY ) ) 00304 value++; 00305 00306 AT91C_BASE_ADC->ADC_IDR = AT91C_ADC_DRDY; 00307 00308 value = AT91C_BASE_ADC->ADC_LCDR & 0xFFFF; 00309 00310 return value; 00311 } 00312 00313 /** @} 00314 */ 00315 00316 #ifdef OSC 00317 void AnalogIn_AutoSendInit( ) 00318 { 00319 int autosend; 00320 Eeprom_Read( EEPROM_ANALOGIN_AUTOSEND, (uchar*)&autosend, 4 ); 00321 if( !((autosend >> 16) & 0xFF) == AUTOSENDSAVE ) 00322 AnalogIn->autosend = AUTOSENDSAVE << 16; 00323 else 00324 AnalogIn->autosend = autosend; 00325 } 00326 #endif 00327 00328 int AnalogIn_Start( int index ) 00329 { 00330 if ( index < 0 || index >= ANALOGIN_CHANNELS ) 00331 return CONTROLLER_ERROR_ILLEGAL_INDEX; 00332 if ( AnalogIn->channelUsers[ index ]++ == 0 ) 00333 { 00334 int status; 00335 00336 // The lower four channel pins are shared with other subsystems, so no locking 00337 if ( index < 4 ) 00338 { 00339 int io = AnalogIn_GetIo( index ); 00340 status = Io_Start( io, false ); 00341 if ( status != CONTROLLER_OK ) 00342 { 00343 AnalogIn->channelUsers[ index ]--; 00344 return status; 00345 } 00346 00347 Io_SetPullup( io, false ); 00348 } 00349 00350 if ( AnalogIn->users++ == 0 ) 00351 { 00352 AnalogIn_Init(); 00353 } 00354 } 00355 00356 return CONTROLLER_OK; 00357 } 00358 00359 int AnalogIn_Stop( int index ) 00360 { 00361 if ( index < 0 || index >= ANALOGIN_CHANNELS ) 00362 return CONTROLLER_ERROR_ILLEGAL_INDEX; 00363 if ( AnalogIn->channelUsers[ index ] <= 0 ) 00364 return CONTROLLER_ERROR_TOO_MANY_STOPS; 00365 if ( --AnalogIn->channelUsers[ index ] == 0 ) 00366 { 00367 if ( index < 4 ) 00368 { 00369 int io = AnalogIn_GetIo( index ); 00370 Io_Stop( io ); 00371 } 00372 if ( --AnalogIn->users == 0 ) 00373 { 00374 AnalogIn_Deinit(); 00375 } 00376 } 00377 return CONTROLLER_OK; 00378 } 00379 00380 int AnalogIn_Init() 00381 { 00382 // Enable the peripheral clock 00383 AT91C_BASE_PMC->PMC_PCER = 1 << AT91C_ID_ADC; 00384 00385 // Set up 00386 AT91C_BASE_ADC->ADC_MR = 00387 AT91C_ADC_TRGEN_DIS | // Hardware Trigger Disabled 00388 // AT91C_ADC_TRGEN_EN | // Hardware Trigger Disabled 00389 // AT91C_ADC_TRGSEL_ | // Hardware Trigger Disabled 00390 // AT91C_ADC_TRGSEL_TIOA0 | // Trigger Selection Don't Care 00391 AT91C_ADC_LOWRES_10_BIT | // 10 bit conversion 00392 // AT91C_ADC_LOWRES_8_BIT | // 8 bit conversion 00393 AT91C_ADC_SLEEP_NORMAL_MODE | // SLEEP 00394 // AT91C_ADC_SLEEP_MODE | // SLEEP 00395 ( ( 9 << 8 ) & AT91C_ADC_PRESCAL ) | // Prescale rate (8 bits) 00396 ( ( 127 << 16 ) & AT91C_ADC_STARTUP ) | // Startup rate 00397 ( ( 127 << 24 ) & AT91C_ADC_SHTIM ); // Sample and Hold Time 00398 00399 //TODO: Will need to fine-tune these timings. 00400 00401 // Do the OS stuff 00402 00403 vSemaphoreCreateBinary( AnalogIn->semaphore ); 00404 00405 // Create the sempahore that will be used to wake the calling process up 00406 vSemaphoreCreateBinary( AnalogIn->doneSemaphore ); 00407 xSemaphoreTake( AnalogIn->doneSemaphore, 0 ); 00408 00409 // Initialize the interrupts 00410 // WAS AT91F_AIC_ConfigureIt( AT91C_ID_ADC, 3, AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL, ( void (*)( void ) ) AnalogInIsr_Wrapper ); 00411 // Which is defined at the bottom of the AT91SAM7X256.h file 00412 unsigned int mask ; 00413 00414 mask = 0x1 << AT91C_ID_ADC; 00415 00416 /* Disable the interrupt on the interrupt controller */ 00417 AT91C_BASE_AIC->AIC_IDCR = mask ; 00418 /* Save the interrupt handler routine pointer and the interrupt priority */ 00419 AT91C_BASE_AIC->AIC_SVR[ AT91C_ID_ADC ] = (unsigned int)AnalogInIsr_Wrapper; 00420 /* Store the Source Mode Register */ 00421 AT91C_BASE_AIC->AIC_SMR[ AT91C_ID_ADC ] = AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL | 4 ; 00422 /* Clear the interrupt on the interrupt controller */ 00423 AT91C_BASE_AIC->AIC_ICCR = mask ; 00424 00425 AT91C_BASE_ADC->ADC_IER = AT91C_ADC_DRDY; 00426 00427 AT91C_BASE_AIC->AIC_IECR = mask; 00428 00429 return CONTROLLER_OK; 00430 } 00431 00432 int AnalogIn_Deinit() 00433 { 00434 return CONTROLLER_OK; 00435 } 00436 00437 int AnalogIn_GetIo( int index ) 00438 { 00439 int io = -1; 00440 switch ( index ) 00441 { 00442 case 0: 00443 io = ANALOGIN_0_IO; 00444 break; 00445 case 1: 00446 io = ANALOGIN_1_IO; 00447 break; 00448 case 2: 00449 io = ANALOGIN_2_IO; 00450 break; 00451 case 3: 00452 io = ANALOGIN_3_IO; 00453 break; 00454 } 00455 return io; 00456 } 00457 00458 #ifdef OSC 00459 00460 /** 00461 Read whether a particular channel is enabled to check for and send new values automatically. 00462 @param index An integer specifying which input (0-7). 00463 @return True if enabled, false if disabled. 00464 */ 00465 bool AnalogIn_GetAutoSend( int index ) 00466 { 00467 AnalogIn_SetActive( index, 1 ); 00468 return (AnalogIn->autosend >> index) & 0x01; 00469 } 00470 00471 /** 00472 Set whether a particular channel is enabled to check for and send new values automatically. 00473 @param index An integer specifying which input (0-7). 00474 @param onoff A boolean specifying whether to turn atuosending on or off. 00475 */ 00476 void AnalogIn_SetAutoSend( int index, bool onoff ) 00477 { 00478 AnalogIn_SetActive( index, 1 ); 00479 if( ((AnalogIn->autosend >> index) & 0x01) != onoff ) 00480 { 00481 int mask = (1 << index); 00482 if( onoff ) 00483 AnalogIn->autosend |= mask; 00484 else 00485 AnalogIn->autosend &= ~mask; 00486 00487 Eeprom_Write( EEPROM_ANALOGIN_AUTOSEND, (uchar*)&AnalogIn->autosend, 4 ); 00488 } 00489 } 00490 00491 /** \defgroup AnalogInOSC Analog In - OSC 00492 Read the Application Board's Analog Inputs via OSC. 00493 \ingroup OSC 00494 00495 \section devices Devices 00496 There are 8 Analog Inputs on the Make Application Board, numbered 0 - 7. 00497 00498 \section properties Properties 00499 The Analog Ins have three properties: 00500 - value 00501 - active 00502 - autosend 00503 00504 \par Value 00505 The \b value property corresponds to the incoming signal of an Analog In. 00506 The range of values you can expect to get back are from 0 - 1023. 00507 Because you can only ever \em read the value of an input, you'll never 00508 want to include an argument at the end of your OSC message to read the value.\n 00509 To read the sixth Analog In, send the message 00510 \verbatim /analogin/5/value \endverbatim 00511 The board will then respond by sending back an OSC message with the Analog In value. 00512 00513 \par Autosend 00514 The \b autosend property corresponds to whether an analogin will automatically send a message 00515 when its incoming value changes. 00516 To tell the Controller to automatically send messages from analogin 4, send the message 00517 \verbatim /analogin/5/autosend 1 \endverbatim 00518 To have the Controller stop sending messages from analogin 4, send the message 00519 \verbatim /analogin/5/autosend 0 \endverbatim 00520 All autosend messages send at the same interval. You can set this interval, in 00521 milliseconds, by sending the message 00522 \verbatim /system/autosend-interval 10 \endverbatim 00523 so that messages will be sent every 10 milliseconds. This can be anywhere from 1 to 5000 milliseconds. 00524 \par 00525 You also need to select whether the board should send to you over USB or Ethernet. Send 00526 \verbatim /system/autosend-usb 1 \endverbatim 00527 to send via USB, and 00528 \verbatim /system/autosend-udp 1 \endverbatim 00529 to send via Ethernet. Via Ethernet, the board will send messages to the last address it received a message from. 00530 00531 \par Active 00532 The \b active property corresponds to the active state of an Analog In. 00533 If an Analog In is set to be active, no other tasks will be able to 00534 read from it as an Analog In. If you're not seeing appropriate 00535 responses to your messages to the Analog In, check the whether it's 00536 locked by sending a message like 00537 \verbatim /analogin/0/active \endverbatim 00538 \par 00539 You can set the active flag by sending 00540 \verbatim /analogin/0/active 1 \endverbatim 00541 */ 00542 00543 #include "osc.h" 00544 #include "string.h" 00545 #include "stdio.h" 00546 00547 // Need a list of property names 00548 // MUST end in zero 00549 static char* AnalogInOsc_Name = "analogin"; 00550 static char* AnalogInOsc_PropertyNames[] = { "active", "value", "autosend", 0 }; // must have a trailing 0 00551 00552 int AnalogInOsc_PropertySet( int index, int property, int value ); 00553 int AnalogInOsc_PropertyGet( int index, int property ); 00554 00555 // Returns the name of the subsystem 00556 const char* AnalogInOsc_GetName( ) 00557 { 00558 return AnalogInOsc_Name; 00559 } 00560 00561 // Now getting a message. This is actually a part message, with the first 00562 // part (the subsystem) already parsed off. 00563 int AnalogInOsc_ReceiveMessage( int channel, char* message, int length ) 00564 { 00565 int status = Osc_IndexIntReceiverHelper( channel, message, length, 00566 ANALOGIN_CHANNELS, AnalogInOsc_Name, 00567 AnalogInOsc_PropertySet, AnalogInOsc_PropertyGet, 00568 AnalogInOsc_PropertyNames ); 00569 if ( status != CONTROLLER_OK ) 00570 return Osc_SendError( channel, AnalogInOsc_Name, status ); 00571 return CONTROLLER_OK; 00572 00573 } 00574 00575 // Set the index LED, property with the value 00576 int AnalogInOsc_PropertySet( int index, int property, int value ) 00577 { 00578 switch ( property ) 00579 { 00580 case 0: 00581 AnalogIn_SetActive( index, value ); 00582 break; 00583 case 2: // autosend 00584 AnalogIn_SetAutoSend( index, value ); 00585 break; 00586 } 00587 return CONTROLLER_OK; 00588 } 00589 00590 // Get the index LED, property 00591 int AnalogInOsc_PropertyGet( int index, int property ) 00592 { 00593 int value = 0; 00594 switch ( property ) 00595 { 00596 case 0: 00597 value = AnalogIn_GetActive( index ); 00598 break; 00599 case 1: 00600 value = AnalogIn_GetValue( index ); 00601 break; 00602 case 2: // autosend 00603 value = AnalogIn_GetAutoSend( index ); 00604 break; 00605 } 00606 00607 return value; 00608 } 00609 00610 int AnalogInOsc_Async( int channel ) 00611 { 00612 int newMsgs = 0; 00613 char address[ OSC_SCRATCH_SIZE ]; 00614 int i; 00615 int value; 00616 for( i = 0; i < ANALOGIN_CHANNELS; i ++ ) 00617 { 00618 if( !AnalogIn_GetAutoSend( i ) ) 00619 continue; 00620 value = AnalogIn_GetValue( i ); 00621 if( value != AnalogIn->lastValues[i] ) 00622 { 00623 AnalogIn->lastValues[i] = value; 00624 snprintf( address, OSC_SCRATCH_SIZE, "/%s/%d/value", AnalogInOsc_Name, i ); 00625 Osc_CreateMessage( channel, address, ",i", value ); 00626 newMsgs++; 00627 } 00628 } 00629 00630 return newMsgs; 00631 } 00632 00633 #endif // OSC
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.