adc.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2004 by Ole Reinhardt <ole.reinhardt@kernelconcepts.de>,
00003  *                       Kernelconcepts http://www.kernelconcepts.de
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions
00007  * are met:
00008  *
00009  * 1. Redistributions of source code must retain the above copyright
00010  *    notice, this list of conditions and the following disclaimer.
00011  * 2. Redistributions in binary form must reproduce the above copyright
00012  *    notice, this list of conditions and the following disclaimer in the
00013  *    documentation and/or other materials provided with the distribution.
00014  * 3. Neither the name of the copyright holders nor the names of
00015  *    contributors may be used to endorse or promote products derived
00016  *    from this software without specific prior written permission.
00017  *
00018  * THIS SOFTWARE IS PROVIDED BY EGNITE SOFTWARE GMBH AND CONTRIBUTORS
00019  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00020  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00021  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL EGNITE
00022  * SOFTWARE GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00023  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00024  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
00025  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
00026  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
00027  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
00028  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00029  * SUCH DAMAGE.
00030  *
00031  * For additional information see http://www.ethernut.de/
00032  *
00033  */
00034 
00041 /*
00042  * $Log: adc.c,v $
00043  * Revision 1.7  2006/01/25 12:51:40  freckle
00044  * added explicit off mode
00045  *
00046  * Revision 1.6  2006/01/11 16:34:44  freckle
00047  * ADCInit can be called multiple times (makes life easier)
00048  *
00049  * Revision 1.5  2005/10/24 17:59:55  haraldkipp
00050  * Fixes for ATmega103
00051  *
00052  * Revision 1.4  2005/08/25 16:23:42  olereinhardt
00053  * Removed compute intensive % and replaced by an &
00054  *
00055  * Revision 1.3  2005/08/19 21:52:43  freckle
00056  * use 8-bit buffer pointers, removed critical section from ADCRead
00057  *
00058  * Revision 1.2  2005/08/02 17:46:45  haraldkipp
00059  * Major API documentation update.
00060  *
00061  * Revision 1.1  2005/07/26 18:02:27  haraldkipp
00062  * Moved from dev.
00063  *
00064  * Revision 1.3  2005/06/13 12:00:09  olereinhardt
00065  * Removed unnecessary NutExitCritical
00066  *
00067  * Revision 1.2  2005/03/04 11:42:05  olereinhardt
00068  * Added function void ADCStartLowNoiseConversion(void)
00069  * This function enters sleep mode!!! Be shure to read the AVR datasheet before using this function
00070  *
00071  * Revision 1.1  2004/08/02 10:05:25  olereinhardt
00072  * First checkin. ADC driver for ATMega128 / GCC
00073  *
00074  */
00075 
00076 
00082 
00083 /* Not ported. */
00084 #ifdef __GNUC__
00085 
00086 #include <string.h>
00087 #include <avr/sleep.h>
00088 #include <sys/heap.h>
00089 #include <sys/atom.h>
00090 #include <sys/nutconfig.h>
00091 
00092 #include <dev/irqreg.h>
00093 #include <dev/adc.h>
00094 
00095 #ifndef ADC_INITIAL_CHANNEL
00096 #define ADC_INITIAL_CHANNEL ADC0
00097 #endif
00098 
00099 #ifndef ADC_INITIAL_REF
00100 #define ADC_INITIAL_REF AVCC
00101 #endif
00102 
00103 #ifndef ADC_INITIAL_MODE
00104 #define ADC_INITIAL_MODE SINGLE_CONVERSION
00105 #endif
00106 
00107 #ifndef ADC_INITIAL_PRESCALE
00108 #define ADC_INITIAL_PRESCALE ADC_PRESCALE_DIV64
00109 #endif
00110 
00111 #define ADC_BUF_SIZE 16 // this may only be a power of two
00112 
00113 #if defined(__GNUC__) && defined(__AVR_ENHANCED__)
00114 u_char adc_sleep_mode = SLEEP_MODE_ADC;
00115 
00116 /* AT90CAN128 uses a different register to enter sleep mode */
00117 #if defined(SMCR)
00118 #define AVR_SLEEP_CTRL_REG    SMCR
00119 #else
00120 #define AVR_SLEEP_CTRL_REG    MCUCR
00121 #endif
00122 #endif
00123 
00124 
00125 
00126 /********** DRIVER GLOBALS **********/
00127 adc_mode_t current_mode = ADC_OFF;
00128 
00129 // The buffers are FIFO buffers implemented as char
00130 // arrays with head and tail pointers.
00131 
00132 // All read and write functions return a 0 for
00133 // success and a 1 for fail, if a successfull read,
00134 // the character is placed into *read.
00135 
00136 // buffer: [data1 data2 ... datalast head tail]
00137 
00138 #define _adc_buf_head ADC_BUF_SIZE
00139 #define _adc_buf_tail ADC_BUF_SIZE+1
00140 
00141 u_short *ADC_buffer = NULL;
00142 
00143 inline int ADCBufRead(u_short * buf, u_short * read)
00144 {
00145     u_char tail, head;
00146     tail = buf[_adc_buf_tail];
00147     head = buf[_adc_buf_head];
00148     if (head != tail) {
00149         *read = buf[tail];
00150         buf[_adc_buf_tail] = (tail + 1) & (ADC_BUF_SIZE-1);
00151         return 0;
00152     }
00153     return 1;
00154 }
00155 
00156 inline int ADCBufWrite(u_short * buf, u_short * write)
00157 {
00158     u_char tail, head;
00159     tail = buf[_adc_buf_tail];
00160     head = buf[_adc_buf_head];
00161     if ((head + 1) % ADC_BUF_SIZE != tail) {
00162         buf[head] = *write;
00163         buf[_adc_buf_head] = (head + 1) & (ADC_BUF_SIZE-1);
00164         return 0;
00165     }
00166     return 1;
00167 }
00168 
00169 void ADCBufInit(u_short * buf)
00170 {
00171     buf[_adc_buf_head] = 0;
00172     buf[_adc_buf_tail] = 0;
00173 }
00174 
00175 static void ADCInterrupt(void *arg)
00176 {
00177     u_short ADC_value;
00178 
00179     ADC_value = inw(ADCW);
00180 
00181     if (ADCBufWrite(ADC_buffer, &ADC_value) != 0) {
00182         // Send error message
00183     }
00184 
00185 }
00186 
00187 void ADCInit()
00188 {
00189     if (ADC_buffer) return;
00190 
00191     ADCSetChannel(ADC_INITIAL_CHANNEL);
00192     ADCSetRef(ADC_INITIAL_REF);
00193     ADCSetMode(ADC_INITIAL_MODE);
00194     ADCSetPrescale(ADC_INITIAL_PRESCALE);
00195 
00196     ADC_buffer = NutHeapAlloc(sizeof(u_short) * ADC_BUF_SIZE + 2);
00197     ADCBufInit(ADC_buffer);
00198 
00199     if (NutRegisterIrqHandler(&sig_ADC, ADCInterrupt, NULL)) {
00200         // MR 2006-01-11: we do not free buffer as this would cost ROM and is not likely
00201         return;
00202     }
00203     // Enable ADC
00204     sbi(ADCSR, ADEN);
00205 
00206     // Enable ADC interrupt
00207     sbi(ADCSR, ADIE);
00208 }
00209 
00210 void ADCSetRef(adc_ref_t reference)
00211 {
00212     ADCStopConversion();
00213 
00214 #ifdef __AVR_ENHANCED__
00215     switch (reference) {
00216     case AVCC:
00217         cbi(ADMUX, REFS1);
00218         sbi(ADMUX, REFS0);
00219         break;
00220     case AREF:
00221         cbi(ADMUX, REFS1);
00222         cbi(ADMUX, REFS0);
00223         break;
00224     case INTERNAL_256:
00225         sbi(ADMUX, REFS1);
00226         sbi(ADMUX, REFS0);
00227         break;
00228     }
00229 #endif /* __AVR_ENHANCED__ */
00230 
00231 }
00232 
00233 void ADCSetMode(adc_mode_t mode)
00234 {
00235     ADCStopConversion();
00236 
00237     switch (mode) {
00238     case FREE_RUNNING:
00239         sbi(ADCSR, ADFR);
00240         break;
00241     case SINGLE_CONVERSION:
00242         cbi(ADCSR, ADFR);
00243         break;
00244     case ADC_OFF:
00245             break;
00246     }
00247     current_mode = mode;
00248 }
00249 
00250 u_char ADCSetPrescale(u_char prescalar)
00251 {
00252 
00253     ADCStopConversion();
00254 
00255     if (prescalar > 128) {
00256         prescalar = 128;
00257     }
00258 
00259     switch (prescalar) {
00260     case ADC_PRESCALE_DIV2:
00261         cbi(ADCSR, ADPS2);
00262         cbi(ADCSR, ADPS1);
00263         cbi(ADCSR, ADPS0);
00264         break;
00265     case ADC_PRESCALE_DIV4:
00266         cbi(ADCSR, ADPS2);
00267         sbi(ADCSR, ADPS1);
00268         cbi(ADCSR, ADPS0);
00269         break;
00270     case ADC_PRESCALE_DIV8:
00271         cbi(ADCSR, ADPS2);
00272         sbi(ADCSR, ADPS1);
00273         sbi(ADCSR, ADPS0);
00274         break;
00275     case ADC_PRESCALE_DIV16:
00276         sbi(ADCSR, ADPS2);
00277         cbi(ADCSR, ADPS1);
00278         cbi(ADCSR, ADPS0);
00279         break;
00280     case ADC_PRESCALE_DIV32:
00281         sbi(ADCSR, ADPS2);
00282         cbi(ADCSR, ADPS1);
00283         sbi(ADCSR, ADPS0);
00284         break;
00285     case ADC_PRESCALE_DIV64:
00286         sbi(ADCSR, ADPS2);
00287         sbi(ADCSR, ADPS1);
00288         cbi(ADCSR, ADPS0);
00289         break;
00290     case ADC_PRESCALE_DIV128:
00291         sbi(ADCSR, ADPS2);
00292         sbi(ADCSR, ADPS1);
00293         sbi(ADCSR, ADPS0);
00294         break;
00295 
00296     default:
00297         return 1;
00298         break;
00299     }
00300 
00301     return 0;
00302 }
00303 
00304 void ADCSetChannel(adc_channel_t adc_channel)
00305 {
00306     u_char current_admux;
00307 
00308     current_admux = inb(ADMUX) & 0xF8;
00309 
00310     outb(ADMUX, (current_admux | adc_channel));
00311 }
00312 
00313 void ADCBufferFlush(void)
00314 {
00315     ADCBufInit(ADC_buffer);
00316 }
00317 
00318 void ADCStartConversion()
00319 {
00320     sbi(ADCSR, ADSC);
00321 }
00322 
00323 void ADCStartLowNoiseConversion()
00324 {
00325     ADCSetMode(SINGLE_CONVERSION);
00326 
00327 #if defined(__GNUC__) && defined(__AVR_ENHANCED__)
00328     {
00329         u_char sleep_mode = AVR_SLEEP_CTRL_REG & _SLEEP_MODE_MASK;
00330         set_sleep_mode(adc_sleep_mode);
00331         /* Note:  avr-libc has a sleep_mode() function, but it's broken for
00332         AT90CAN128 with avr-libc version earlier than 1.2 */
00333         AVR_SLEEP_CTRL_REG |= _BV(SE);
00334         __asm__ __volatile__ ("sleep" "\n\t" :: );
00335         AVR_SLEEP_CTRL_REG &= ~_BV(SE);
00336         set_sleep_mode(sleep_mode);
00337     }
00338 #else
00339     sbi(ADCSR, ADSC);
00340 #endif    
00341 }
00342 
00343 void ADCStopConversion()
00344 {
00345     if (current_mode != FREE_RUNNING) {
00346         // Send warning message
00347         return;
00348     }
00349 // Terminate and restart the ADC 
00350 // When restarted, start_conversion needs to be
00351 // called again
00352     cbi(ADCSR, ADEN);
00353     cbi(ADCSR, ADSC);
00354     sbi(ADCSR, ADEN);
00355 }
00356 
00357 u_char ADCRead(u_short * value)
00358 {
00359     return ADCBufRead(ADC_buffer, value);
00360 }
00361 
00362 inline adc_mode_t ADCGetMode(void)
00363 {
00364     return (current_mode);
00365 }
00366 #endif
00367 

© 2000-2007 by egnite Software GmbH - visit http://www.ethernut.de/