add ballboard commands on mainboard
[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 SIGNAL(SIG_ADC)
86 {
87         int16_t result;
88
89         if (!adc_event)
90                 return;
91
92         result = ADC;
93         
94         /* sign extension to fill the 16 bits when negative
95          * (for the 16 bits output format, the output is
96          * already right.) */
97         if ( ( g_adc_previous_config & ADC_RESULT_SIGNED)
98              && !(g_adc_previous_config & ADC_MODE_16_BITS)
99              && (result & 0x0200) )
100                 result |= 0xFE00;
101         
102         adc_event(result);
103 }
104
105
106 /**
107  * Launch a conversion : this function launches a conversion with the
108  * specified configuration.  The conversion_config is casted to an
109  * int.
110 */
111 void adc_launch(uint16_t conversion_config)
112 {
113         /* configure multiplexer : done first, so the maximum time is
114          * left before the real conversion launch */
115         ADMUX = conversion_config & 0xFF ;
116         /**
117          * This disables and reenables the ADC when a different
118          * channel is selected AND the new channel is a differential
119          * one. Using this trick, the ADC recalibrates, and the time
120          * for this allows the amplifier to settle.
121          *
122          * Datasheet says :
123          * When switching to a differential gain channel, the first
124          * conversion result may have a poor accuracy due to the
125          * required settling time for the automatic offset
126          * cancellation circuitry.  The user should preferably
127          * disregard the first conversion result.
128          */
129         if ( (conversion_config & ADC_RESULT_SIGNED) &&
130              (g_adc_previous_config != conversion_config) ) {
131                 cbi(ADCSRA,ADEN);
132                 sbi(ADCSRA,ADEN);
133         }
134
135         /* save config */
136         g_adc_previous_config = conversion_config;
137
138         /* for some devices, one additionnal MUX bit is in ADCSRB */
139 #ifdef MUX5_IN_ADCSRB
140         if (conversion_config & MUX5_MASK_IN_CONFIG)
141                 sbi(ADCSRB, MUX5);
142         else
143                 cbi(ADCSRB, MUX5);
144 #endif // MUX5_IN_ADCSRB
145
146         /* Enable free run or not (triggered mode) */
147         if (conversion_config  & ADC_MODE_TRIGGERED)
148                 sbi(ADCSRA, ADFR);
149         else
150                 cbi(ADCSRA, ADFR);
151
152         /* Start conversion, with or without enabling interrupts */
153         if (conversion_config & ADC_MODE_INT) {
154                 /* Clear flag from previous intr (in case of previous
155                  * conversion was not using intr), and enable
156                  * interrupt */
157                 ADCSRA |=  (1 << ADSC) | (1 << ADIF) | (1 << ADIE);
158         }
159         else {
160                 /* clear flag from previous int, used as a 'conversion
161                    finished' flag */
162                 cbi(ADCSRA, ADIE);
163                 ADCSRA |=  (1 << ADSC) | (1 << ADIF);
164         }
165 }
166
167 /**
168  * This function gets an ADC value. If a conversion has been
169  * previously started, with EXACTLY the same config (or specifying
170  * ADC_NO_CONFIG) then it waits for it to finish. Else it launches a
171  * new one with the given config, and polls the result.
172  *
173  * This function should not be used if you use interrupts, but only
174  * can be used with triggered (or free run mode)
175 */
176 int16_t adc_get_value(uint16_t conversion_config)
177 {
178         int16_t result;
179
180         /* conversion has previously been launched, or no change */
181         if ((conversion_config == ADC_NO_CONFIG) ||
182             (conversion_config == g_adc_previous_config)) {
183                 if (bit_is_clear(ADCSRA, ADSC) &&
184                     bit_is_clear(ADCSRA, ADIF)) {
185                         /* no result is available now and no
186                            conversion is pending -> launch it... */
187                         adc_launch(g_adc_previous_config);
188                 }
189         }
190         /* The previous conversion had a different configuration : we
191          * must launch a new one */
192         else {
193                 /* Cancel previous triggered mode, if it was set and
194                  * reset interrupt mask */
195                 cbi(ADCSRA, ADFR);
196                 cbi(ADCSRA, ADIE);
197
198                 /* waiting for the previous conv to finish, result will
199                  * be lost */
200                 while (bit_is_set(ADCSRA, ADSC));
201
202                 /* launch new one */
203                 adc_launch(conversion_config);
204         }
205
206         /* waiting for the result, works even in triggered mode, then
207          * clear the flag. */
208         while (bit_is_clear(ADCSRA, ADIF));
209         sbi(ADCSRA, ADIF);
210
211         result = ADC;
212
213         /* If we are in SIGNED_MODE + 10 bits format, and if the
214          * result is negative, set the 7 first MSB bits to 1 */
215         if(  ( g_adc_previous_config & ADC_RESULT_SIGNED ) &&
216              !(g_adc_previous_config & ADC_MODE_16_BITS) &&
217              (result & 0x0200) ) {
218                 result |= 0xFE00;
219         }
220
221         return result;
222 }
223
224
225 /**
226  * Just a int32_t version for compatibility with control_system
227  * function prototypes.
228  */
229 int32_t adc_get_value32(void *conversion_config)
230 {
231         return adc_get_value((uint16_t)conversion_config);
232 }
233