config: update test config files to current aversive config.in
[aversive.git] / modules / hardware / adc / adc.c
1 /*
2  *  Copyright Droids Corporation, Microb Technology, Eirbot (2005)
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program; if not, write to the Free Software
16  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  *
18  *  Revision : $Id: adc.c,v 1.8.4.8 2009-02-27 21:37:49 zer0 Exp $
19  *
20  */
21
22 #include <stdlib.h>
23
24 #include <aversive.h>
25 #include <adc.h>
26
27  /* keep the previous config */
28 static uint16_t g_adc_previous_config = ADC_NO_CONFIG;
29
30 /* function pointer definition for event */
31 static void (*adc_event)(int16_t) = NULL;
32
33
34 /**
35  * Initialisation of ADC internal registers
36  * Can be called for a wake up after a shutdown command
37  */
38 void adc_init(void)
39 {
40 #if defined(PRADC) && defined(PRR)
41         cbi(PRR, PRADC);
42 #elif defined(PRADC) && defined(PRR0)
43         cbi(PRR0, PRADC);
44 #endif
45
46         ADCSRA =  (1<<ADEN) | (ADC_PRESCALE << ADPS0);
47         ADMUX = 0;
48 #ifdef ADCSRB
49         ADCSRB = 0;
50 #endif
51 }
52
53 /**
54  * Shut down the ADC, for power consumption
55  */
56 void adc_shutdown(void)
57 {
58         ADCSRA = 0; // erases all the register
59
60 #if defined(PRADC) && defined(PRR)
61         sbi(PRR, PRADC);
62 #elif defined(PRADC) && defined(PRR0)
63         sbi(PRR0, PRADC);
64 #endif
65 }
66
67
68 /**
69  * Register callback event. The parameter function is called when the
70  * conversion is finished.
71  */
72 void adc_register_event(void (*f)(int16_t))
73 {
74         uint8_t flags;
75
76         IRQ_LOCK(flags);
77         adc_event = f;
78         IRQ_UNLOCK(flags);
79 }
80
81 /**
82  * Interrupt function, other interrupts are disabled during its
83  * execution.
84  */
85 #ifndef ADC_vect
86 #if defined(SIG_ADC)
87 #define ADC_vect SIG_ADC
88 #endif
89 #endif
90 SIGNAL(ADC_vect)
91 {
92         int16_t result;
93
94         if (!adc_event)
95                 return;
96
97         result = ADC;
98
99         /* sign extension to fill the 16 bits when negative
100          * (for the 16 bits output format, the output is
101          * already right.) */
102         if ( ( g_adc_previous_config & ADC_RESULT_SIGNED)
103              && !(g_adc_previous_config & ADC_MODE_16_BITS)
104              && (result & 0x0200) )
105                 result |= 0xFE00;
106
107         adc_event(result);
108 }
109
110
111 /**
112  * Launch a conversion : this function launches a conversion with the
113  * specified configuration.  The conversion_config is casted to an
114  * int.
115 */
116 void adc_launch(uint16_t conversion_config)
117 {
118         /* configure multiplexer : done first, so the maximum time is
119          * left before the real conversion launch */
120         ADMUX = conversion_config & 0xFF ;
121         /**
122          * This disables and reenables the ADC when a different
123          * channel is selected AND the new channel is a differential
124          * one. Using this trick, the ADC recalibrates, and the time
125          * for this allows the amplifier to settle.
126          *
127          * Datasheet says :
128          * When switching to a differential gain channel, the first
129          * conversion result may have a poor accuracy due to the
130          * required settling time for the automatic offset
131          * cancellation circuitry.  The user should preferably
132          * disregard the first conversion result.
133          */
134         if ( (conversion_config & ADC_RESULT_SIGNED) &&
135              (g_adc_previous_config != conversion_config) ) {
136                 cbi(ADCSRA,ADEN);
137                 sbi(ADCSRA,ADEN);
138         }
139
140         /* save config */
141         g_adc_previous_config = conversion_config;
142
143         /* for some devices, one additionnal MUX bit is in ADCSRB */
144 #ifdef MUX5_IN_ADCSRB
145         if (conversion_config & MUX5_MASK_IN_CONFIG)
146                 sbi(ADCSRB, MUX5);
147         else
148                 cbi(ADCSRB, MUX5);
149 #endif // MUX5_IN_ADCSRB
150
151         /* Enable free run or not (triggered mode) */
152         if (conversion_config  & ADC_MODE_TRIGGERED)
153                 sbi(ADCSRA, ADFR);
154         else
155                 cbi(ADCSRA, ADFR);
156
157         /* Start conversion, with or without enabling interrupts */
158         if (conversion_config & ADC_MODE_INT) {
159                 /* Clear flag from previous intr (in case of previous
160                  * conversion was not using intr), and enable
161                  * interrupt */
162                 ADCSRA |=  (1 << ADSC) | (1 << ADIF) | (1 << ADIE);
163         }
164         else {
165                 /* clear flag from previous int, used as a 'conversion
166                    finished' flag */
167                 cbi(ADCSRA, ADIE);
168                 ADCSRA |=  (1 << ADSC) | (1 << ADIF);
169         }
170 }
171
172 /**
173  * This function gets an ADC value. If a conversion has been
174  * previously started, with EXACTLY the same config (or specifying
175  * ADC_NO_CONFIG) then it waits for it to finish. Else it launches a
176  * new one with the given config, and polls the result.
177  *
178  * This function should not be used if you use interrupts, but only
179  * can be used with triggered (or free run mode)
180 */
181 int16_t adc_get_value(uint16_t conversion_config)
182 {
183         int16_t result;
184
185         /* conversion has previously been launched, or no change */
186         if ((conversion_config == ADC_NO_CONFIG) ||
187             (conversion_config == g_adc_previous_config)) {
188                 if (bit_is_clear(ADCSRA, ADSC) &&
189                     bit_is_clear(ADCSRA, ADIF)) {
190                         /* no result is available now and no
191                            conversion is pending -> launch it... */
192                         adc_launch(g_adc_previous_config);
193                 }
194         }
195         /* The previous conversion had a different configuration : we
196          * must launch a new one */
197         else {
198                 /* Cancel previous triggered mode, if it was set and
199                  * reset interrupt mask */
200                 cbi(ADCSRA, ADFR);
201                 cbi(ADCSRA, ADIE);
202
203                 /* waiting for the previous conv to finish, result will
204                  * be lost */
205                 while (bit_is_set(ADCSRA, ADSC));
206
207                 /* launch new one */
208                 adc_launch(conversion_config);
209         }
210
211         /* waiting for the result, works even in triggered mode, then
212          * clear the flag. */
213         while (bit_is_clear(ADCSRA, ADIF));
214         sbi(ADCSRA, ADIF);
215
216         result = ADC;
217
218         /* If we are in SIGNED_MODE + 10 bits format, and if the
219          * result is negative, set the 7 first MSB bits to 1 */
220         if(  ( g_adc_previous_config & ADC_RESULT_SIGNED ) &&
221              !(g_adc_previous_config & ADC_MODE_16_BITS) &&
222              (result & 0x0200) ) {
223                 result |= 0xFE00;
224         }
225
226         return result;
227 }
228
229
230 /**
231  * Just a int32_t version for compatibility with control_system
232  * function prototypes.
233  */
234 int32_t adc_get_value32(void *conversion_config)
235 {
236         return adc_get_value((uint16_t)conversion_config);
237 }
238