Project

General

Profile

Statistics
| Revision:

root / branches / analog / trunk / code / projects / libdragonfly / analog.c @ 324

History | View | Annotate | Download (8.84 KB)

1
/**
2
 * Copyright (c) 2007 Colony Project
3
 * 
4
 * Permission is hereby granted, free of charge, to any person
5
 * obtaining a copy of this software and associated documentation
6
 * files (the "Software"), to deal in the Software without
7
 * restriction, including without limitation the rights to use,
8
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
9
 * copies of the Software, and to permit persons to whom the
10
 * Software is furnished to do so, subject to the following
11
 * conditions:
12
 * 
13
 * The above copyright notice and this permission notice shall be
14
 * included in all copies or substantial portions of the Software.
15
 * 
16
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
18
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
20
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23
 * OTHER DEALINGS IN THE SOFTWARE.
24
 **/
25

    
26
/**
27
 * @file analog.c
28
 * @brief Analog input and output
29
 *
30
 * Contains functions for manipulating the ADC on the Dragonfly board.
31
 * It operates by running throug all available analog ports (set by AN_MAX,
32
 * see analog.h for details on that constant). It performs a conversion for
33
 * each port and stores the 8bit and 10bit value. It then increments the 
34
 * port that it is looking at and then procedes to do another read.
35
 *
36
 * analog8 and analog10 are changed to use the lookup table. To use the
37
 * historic functions, use analog_get8 and analog_get10. If you do use those,
38
 * it is advised to turn off the loop by calling analog_stop_loop and restarting
39
 * the loop when done with analog_start_loop. This is especially important if you
40
 * are planning on doing a series of analog_getX reads.
41
 *
42
 * This function pretty intensive on the interrupts as it will be called quite
43
 * often. Plan ahead for this.
44
 * 
45
 * @author Colony Project, CMU Robotics Club, Kevin Woo
46
 * Originally based on fwr code by Tom Lauwers
47
 **/
48

    
49
#include <util/delay.h>
50
#include <avr/interrupt.h>
51
#include "analog.h"
52
#include "serial.h"
53
// Internal Function Prototypes
54
void set_adc_mux(int which);
55

    
56
/** @brief Struct to hold the value of a particular analog port */
57
typedef struct {
58
        uint8_t adc8;
59
        uint16_t adc10;
60
} adc_t;
61

    
62
/**
63
 * @defgroup analog Analog
64
 * Functions for manipulation the ADC on the dragonfly board.
65
 * All definitions may be found in analog.h.
66
 *
67
 * @{
68
 **/
69

    
70
int adc_loop_running = 0;
71
int adc_current_port = 0;
72
adc_t an_val[11];
73

    
74
/**
75
 * Initializes the ADC.
76
 * Call analog_init before reading from the analog ports.
77
 *
78
 * @see analog8, analog10, analog_get8, analog_get10, analog_start_loop
79
 *      analog_stop_loop
80
 **/
81
void analog_init(int start_conversion)
82
{
83
    //Initialize the array to 0
84
        for (int i = 0; i < (AN_MAX + 1); i++) {
85
                an_val[i].adc10 = 0;
86
                an_val[i].adc8 = 0;
87
        }
88

    
89
        // ADMUX register
90
        // The following bits are set
91
        // Bit 7,6 - Set voltage reference to AVcc (0b01)
92
        // Bit 5 - ADLAR set to simplify moving from register
93
        // Bit 4 - X
94
        // Bit 3:0 - Sets the current channel
95
        // Initializes to read from AN1 first (AN0 is reservered for the BOM)
96
        ADMUX = 0;
97
        ADMUX |= ADMUX_OPT | _BV(MUX0);
98

    
99

    
100
        // ADC Status Register A
101
        // The following bits are set
102
        // Bit 7 - ADEN is set (enables analog)
103
        // Bit 3 - Enable ADC Interrupt (required to run free-running mode)
104
        // Bits 2-0 - Set to create a clock divisor of 128, to make ADC clock = 8,000,000/64 = 125kHz
105
        ADCSRA = 0;
106
        ADCSRA |= _BV(ADEN) | _BV(ADIE) | _BV(ADPS2) | _BV(ADPS1) | _BV(ADPS0);
107
        
108
        // Set external mux lines to outputs
109
        DDRG |= 0x1C;
110
        
111
        // Set up first port for conversions
112
        set_adc_mux(0x00);
113
        adc_current_port = AN1;
114

    
115
        // Start the conversion if requested
116
        if (start_conversion)
117
                analog_start_loop();
118
        else
119
                analog_stop_loop();
120
        
121
}        
122

    
123
/**
124
 * Reads an analog value from the look-up table. Values are not
125
 * good unless the analog loop is running.
126
 *
127
 * @param which is the analog port you wish to read.
128
 *
129
 * @return returns an 8 bit value which represents the analog value
130
 *
131
 * @see analog8, analog_start_loop, analog_stop_loop, analog_init
132
 **/
133
unsigned int analog_get8(int which) {
134
        if (which == BOM_PORT) {
135
                return 0;
136
        } else {
137
                return an_val[which - 1].adc8;
138
        }
139
}
140

    
141
/**
142
 * Reads an analog value from the look-up table. Values are not
143
 * good unless the analog loop is running.
144
 *
145
 * @param which is the analog port you wish to read.
146
 *
147
 * @return returns an 10 bit value which represents the analog value
148
 *
149
 * @see analog8, analog_start_loop, analog_stop_loop, analog_init
150
 **/
151
unsigned int analog_get10(int which) {
152
        if (which == BOM_PORT) {
153
                return 0;
154
        } else {
155
                return an_val[which - 1].adc10;
156
        }
157
}
158

    
159
/**
160
 * Starts the analog loop to put values into the lookup table. Need
161
 * to have the loop running before analog_get8 and analog_get10 are
162
 * valid.
163
 *
164
 * @see analog_stop_loop, analog_init, analog_get8, analog_get10
165
 **/
166
void analog_start_loop(void) {
167
        //Start the conversion
168
        ADCSRA |= _BV(ADSC);
169
        adc_loop_running = 0x1;
170
}
171

    
172
/**
173
 * Starts the analog loop to put values into the lookup table. You
174
 * should disable to loop to use analog8 and analog10 reliably.
175
 *
176
 * @see analog_stop_loop, analog_init, analog8, analog10
177
 **/
178
void analog_stop_loop(void) {
179
        //Stop the conversion
180
        adc_loop_running = 0x0;
181
}
182
/**
183
 * Reads an eight bit number from an analog port.
184
 * analog_init must be called before using this function.
185
 * 
186
 * @param which the analog port to read from. One of
187
 * the constants AN0 - AN15.
188
 *
189
 * @return the eight bit input to the specified port
190
 *
191
 * @see analog_init, analog10
192
 **/
193
unsigned int analog8(int which)
194
{        
195
        // Let any previous conversion finish
196
        while (ADCSRA & _BV(ADSC));
197
        
198
        if(which < EXT_MUX) {
199
                ADMUX = ADMUX_OPT + which;
200
        } else {
201
                ADMUX = ADMUX_OPT + EXT_MUX;
202
                set_adc_mux(which - 8);
203
        }
204
        
205
        // Start the conversion
206
        ADCSRA |= _BV(ADSC);
207

    
208
        // Wait for the conversion to finish
209
        while (ADCSRA & _BV(ADSC));
210

    
211
        return ADCH; //since we left aligned the data, ADCH is the 8 MSB.
212
}
213

    
214
/**
215
 * Reads a ten bit number from the specified port.
216
 * analog_init must be called before using this function.
217
 * 
218
 *
219
 * @param which the analog port to read from. Typically
220
 * a constant, one of AN0 - AN15.
221
 *
222
 * @return the ten bit number input to the specified port
223
 * 
224
 * @see analog_init, analog8
225
 **/
226
unsigned int analog10(int which)
227
{
228
        int adc_h;
229
        int adc_l;
230
        
231
        // Let any previous conversion finish
232
        while (ADCSRA & _BV(ADSC));
233

    
234
        if(which < EXT_MUX) {
235
                ADMUX = ADMUX_OPT + which;
236
        } else {
237
                ADMUX = ADMUX_OPT + EXT_MUX;
238
                set_adc_mux(which - 8);
239
        }
240
        
241
        // Start the conversion
242
        ADCSRA |= _BV(ADSC);
243

    
244
        // Wait for the conversion to finish
245
        while (ADCSRA & _BV(ADSC));
246

    
247
        adc_l = ADCL;
248
        adc_h = ADCH;
249

    
250
        return ((adc_h << 2) | (adc_l >> 6));
251
}
252

    
253
/**
254
 * Returns the current position of the wheel, as an integer
255
 * in the range 0 - 255.
256
 * analog_init must be called before using this function.
257
 *
258
 * @return the orientation of the wheel, as an integer in
259
 * the range 0 - 255.
260
 *
261
 * @see analog_init
262
 **/
263
int wheel(void)
264
{
265
        return analog8(WHEEL_PORT);
266
}
267

    
268

    
269
/**
270
 * Sets the value of the external analog mux. Values are read
271
 *         on AN7 physical port. (AN8 - AN15 are "virtual" ports).
272
 *
273
 * @param which which analog mux port (0-7) which corresponds
274
 *                   to AN8-AN15.
275
 *
276
 * @bug FIX THIS IN THE NEXT BOARD REVISION:
277
 *                ADDR2 ADDR1 ADDR0
278
 *                G2.G4.G3 set mux to port 0-7 via vinary selection
279
 *                math would be much cleaner if it was G4.G3.G2
280
 *
281
 * @see analog_init
282
 **/
283
void set_adc_mux(int which)
284
{  
285
  // mask so only proper bits are possible.  
286
  PORTG = (PORTG & 0xE3) | ((which & 0x03) << 3) | (which & 0x04);
287
}
288

    
289
/**@}**/ //end defgroup
290

    
291

    
292
ISR(ADC_vect) {
293
        static volatile int adc_prev_loop_running = 0;
294

    
295
    // Need these local variables to read ADCH and ADCL in the correct order
296
        int adc_h = 0;
297
        int adc_l = 0;
298

    
299
        //Store the value only if this read isn't for the BOM
300
        if (ADMUX != BOM_PORT) {
301
            // Read ADCL before ADCH otherwise the registers change in the middle
302
                adc_l = ADCL;
303
                adc_h = ADCH;
304
        
305
                an_val[adc_current_port - 1].adc10 = (adc_h << 2) | (adc_l >> 6);
306
                an_val[adc_current_port - 1].adc8 = adc_h;
307

    
308
        }
309
        
310
        //Save the result only if we just turned off the loop
311
        if (!adc_loop_running && !adc_prev_loop_running)
312
                return;
313
        
314
        adc_prev_loop_running = adc_loop_running;
315
        
316
        //Skip AN7 because it is not a real port
317
        if (adc_current_port == AN6) {
318
                ADMUX = ADMUX_OPT | EXT_MUX;
319
                set_adc_mux(AN8 - 8);
320
                adc_current_port = AN8;
321
        //Wrap around
322
        } else if (adc_current_port == AN11) {
323
                adc_current_port = AN1;
324
                ADMUX = ADMUX_OPT | adc_current_port;
325
        //Normal increment
326
        } else {
327
                adc_current_port++;
328
        
329
                if(adc_current_port < EXT_MUX) {
330
                        ADMUX = ADMUX_OPT | adc_current_port;
331
                } else {
332
                        ADMUX = ADMUX_OPT | EXT_MUX;
333
                        set_adc_mux(adc_current_port - 8);
334
                }
335
        }
336

    
337
        //Initiate next conversion only if we are running a loop
338
        if (!adc_loop_running)
339
                return;
340

    
341
        ADCSRA |= _BV(ADSC);
342
        return;
343
}
344