Project

General

Profile

Statistics
| Revision:

root / trunk / code / projects / libdragonfly / analog.c @ 1462

History | View | Annotate | Download (10.6 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
 * 
32
 * @author Colony Project, CMU Robotics Club
33
 * originally taken from fwr analog file (author: Tom Lauwers)
34
 * loop code written by Kevin Woo and James Kong
35
 **/
36

    
37
#include <util/delay.h>
38
#include <avr/interrupt.h>
39

    
40

    
41
#include "dragonfly_defs.h"
42
#include "serial.h"
43
#include "analog.h"
44
// Internal Function Prototypes
45
void set_adc_mux(int which);
46

    
47
/**
48
 * @defgroup analog Analog
49
 * Functions for manipulation the ADC on the dragonfly board.
50
 * All definitions may be found in analog.h.
51
 *
52
 * @{
53
 **/
54

    
55
unsigned char analog_initd=0;
56

    
57
volatile int adc_loop_status = ADC_LOOP_STOPPED;
58
volatile int adc_sig_stop_loop = 0;
59
volatile int adc_current_port = 0;
60
volatile adc_t an_val[11];
61

    
62
/**
63
 * Initializes the ADC.
64
 * Call analog_init before reading from the analog ports.
65
 *
66
 * @return returns 0 on success, nonzero on error
67
 *
68
 * @see analog8, analog10, analog_get8, analog_get10
69
 *
70
 * @bug First conversion takes a performance penalty of
71
 * 25 vs. 13 ADC clock cycles of successive conversions.
72
 * Analog_init should run a dummy conversion to pre-empt
73
 * this.
74
 *
75
 * For good 10-bit precision, ACD clock must be between
76
 * 50kHz and 200kHz. Currently, ADC clock is fixed at
77
 * 125kHz using 1/64prescalar. However, most code uses
78
 * 8-bit precision which can work at ADC clock speeds
79
 * higher than 200kHz. Experimental tests needed to
80
 * determine highest clock speed for accurate 8-bit ADC.
81
 *
82
 **/
83
int analog_init(int start_conversion) {
84
  if(analog_initd)
85
    return ERROR_INIT_ALREADY_INITD;
86

    
87
  for (int i = 0; i < 11; i++) {
88
    an_val[i].adc10 = 0;
89
    an_val[i].adc8 = 0;
90
  }
91

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

    
101
  // ADC Status Register A
102
  // Bit 7 - ADEN is set (enables analog)
103
  // Bit 6 - Start conversion bit is set (must be done once for free-running mode)
104
  // Bit 5 - Enable Auto Trigger (for free running mode) NOT DOING THIS RIGHT NOW
105
  // Bit 4 - ADC interrupt flag, 0
106
  // Bit 3 - Enable ADC Interrupt (required to run free-running mode)
107
  // Bits 2-0 - Set to create a clock divisor of 128, to make ADC clock = 8,000,000/64 = 125kHz
108
  ADCSRA = 0;
109
  ADCSRA |= _BV(ADEN) | _BV(ADPS2) | _BV(ADPS1) | _BV(ADPS0);
110
        
111
  // Set external mux lines to outputs
112
  DDRG |= 0x1C;
113
        
114
  // Set up first port for conversions
115
  set_adc_mux(0x00);
116
  adc_current_port = AN1;
117

    
118
  //Start the conversion loop if requested
119
  if (start_conversion)
120
    analog_start_loop();
121
                
122
  //Conversion loop disabled by default
123

    
124
  analog_initd=1;
125
  return 0;
126
}        
127

    
128
/**
129
 * Returns the 8-bit analog conversion of which from
130
 * the lookup table. If the requested port is the BOM_PORT
131
 * you will get an automatic 0 since the BOM_PORT is not
132
 * read in the loop and not stored. If you need that port
133
 * you should use the functions in bom.c. There is an analog_get8
134
 * function which for instant lookups but should be avoided unless
135
 * you know what you're doing.
136
 *
137
 * @param which the port that you want to read
138
 *
139
 * @bug may cause a seg fault if which is a larger value
140
 * than exists in an_val table. Not sure if we should fix
141
 * this or not since it would add overhead.
142
 *
143
 * @return 8-bit analog value for the which port requested
144
 *
145
 * @see analog10, analog_get8, analog_get10
146
 **/
147
unsigned int analog8(int which) {
148
        if (which == BOM_PORT) {
149
                return 0;
150
        } else {
151
                return an_val[which - 1].adc8;
152
        }
153
}
154

    
155
/**
156
 * Returns the 10-bit analog conversion of which from
157
 * the lookup table. If the requested port is the BOM_PORT
158
 * you will get an automatic 0 since the BOM_PORT is not
159
 * read in the loop and not stored. If you need that port
160
 * you should use the functions in bom.c. There is an analog_get10
161
 * function which for instant lookups but should be avoided unless
162
 * you know what you are doing.
163
 *
164
 * @param which the port that you want to read
165
 *
166
 * @bug may cause a seg fault if which is a larger value
167
 * than exists in an_val table. Not sure if we should fix
168
 * this or not since it would add overhead.
169
 *
170
 * @return 10-bit analog value for the which port requested
171
 *
172
 * @see analog8, analog_get8, analog_get10
173
 **/
174
unsigned int analog10(int which) {
175
        if (which == BOM_PORT) {
176
                return 0;
177
        } else {
178
                return an_val[which - 1].adc10;
179
        }
180
}
181

    
182
/**
183
 * Starts the analog update loop. Will continue to run
184
 * until analog_stop_loop is called.
185
 *
186
 * @return returns 0 on success, nonzero on error
187
 *
188
 * @see analog_stop_loop, analog_loop_status
189
 **/
190
int analog_start_loop(void) {
191
  if(!analog_initd)
192
    return ERROR_LIBRARY_NOT_INITD;
193

    
194
  if(adc_loop_status != ADC_LOOP_RUNNING){
195
    //Start the conversion, enable ADC interrupt
196
    ADCSRA |= _BV(ADIE);
197
    ADCSRA |= _BV(ADSC);
198
    adc_loop_status = ADC_LOOP_RUNNING;
199
  }
200

    
201
  return 0;
202
}
203

    
204
/**
205
 * Stops the analog update loop. If there is a current
206
 * read, it will finish up and be stored before the loop
207
 * is interrupted. No further updates will be made until
208
 * the loop is started again.
209
 *
210
 * @return returns 0 on success, nonzero on error
211
 *
212
 * @see analog_start_loop, analog_loop_status
213
 **/
214
int analog_stop_loop() {
215
  if(!analog_initd)
216
    return ERROR_LIBRARY_NOT_INITD;
217

    
218
  //Signal to stop after the next conversion
219
  adc_sig_stop_loop = 1;
220

    
221
  return 0;
222
}
223

    
224
/**
225
 * Returns the status of loop. 0 for stopped.
226
 * 1 for running. 2 for paused.
227
 *
228
 * @see analog_start_loop, analog_stop_loop
229
 **/
230
int analog_loop_status(void) {
231
        return adc_loop_status;
232
}
233

    
234
/**
235
 * Reads an 8-bit number from an analog port.
236
 * analog_init must be called before using this function.
237
 * The analog loop must also be stopped before using this
238
 * function or you will mess up the lookup table. You
239
 * must also reenabled the loop when you are done unless
240
 * you are doing more instant reads. See analog_stop_loop
241
 * and analog_start_loop for more information about the loop.
242
 * 
243
 * @param which the analog port to read from. One of
244
 * the constants AN0 - AN7.
245
 *
246
 * @return the 8-bit input to the specified port
247
 *
248
 * @see analog_init, analog_get10, analog8, analog_stop_loop,
249
 * analog_start_loop
250
 **/
251
unsigned int analog_get8(int which) {        
252
        // Let any previous conversion finish
253
        while (ADCSRA & _BV(ADSC));
254
        
255
        if(which < EXT_MUX) {
256
                ADMUX = ADMUX_OPT + which;
257
        } else {
258
                ADMUX = ADMUX_OPT + EXT_MUX;
259
                set_adc_mux(which - 8);
260
        }
261
        
262
        // Start the conversion
263
        ADCSRA |= _BV(ADSC);
264

    
265
        // Wait for the conversion to finish
266
        while (ADCSRA & _BV(ADSC));
267

    
268
        return ADCH; //since we left aligned the data, ADCH is the 8 MSB.
269
}
270

    
271
/**
272
 * Reads an 10-bit number from an analog port.
273
 * analog_init must be called before using this function.
274
 * The analog loop must also be stopped before using this
275
 * function or you will mess up the lookup table. You
276
 * must also reenabled the loop when you are done unless
277
 * you are doing more instant reads. See analog_stop_loop
278
 * and analog_start_loop for more information about the loop.
279
 * 
280
 *
281
 * @param which the analog port to read from. Typically
282
 * a constant, one of AN0 - AN7.
283
 *
284
 * @return the 10-bit number input to the specified port
285
 * 
286
 * @see analog_init, analog_get8, analog10, analog_stop_loop,
287
 * analog_start_loop
288
 **/
289
unsigned int analog_get10(int which) {
290
        int adc_h;
291
        int adc_l;
292
        
293
        // Let any previous conversion finish
294
        while (ADCSRA & _BV(ADSC));
295

    
296
        if(which < EXT_MUX) {
297
                ADMUX = ADMUX_OPT + which;
298
        } else {
299
                ADMUX = ADMUX_OPT + EXT_MUX;
300
                set_adc_mux(which - 8);
301
        }
302
        
303
        // Start the conversion
304
        ADCSRA |= _BV(ADSC);
305

    
306
        // Wait for the conversion to finish
307
        while (ADCSRA & _BV(ADSC));
308
        
309
        adc_l = ADCL;
310
        adc_h = ADCH;
311

    
312
        return ((adc_h << 2) | (adc_l >> 6));
313
}
314

    
315
/**
316
 * Returns the current position of the wheel, as an integer
317
 * in the range 0 - 255.
318
 * analog_init must be called before using this function.
319
 *
320
 * @return the orientation of the wheel, as an integer in
321
 * the range 0 - 255.
322
 *
323
 * @see analog_init
324
 **/
325
int wheel(void) {
326
        return analog8(WHEEL_PORT);
327
}
328

    
329

    
330
/**
331
 * Sets the value of the external analog mux. Values are read
332
 *         on AN7 physical port. (AN8 - AN15 are "virtual" ports).
333
 *
334
 * @param which which analog mux port (0-7) which corresponds
335
 *                   to AN8-AN15.
336
 *
337
 * @bug FIX THIS IN THE NEXT BOARD REVISION:
338
 *                ADDR2 ADDR1 ADDR0
339
 *                G2.G4.G3 set mux to port 0-7 via vinary selection
340
 *                math would be much cleaner if it was G4.G3.G2
341
 *
342
 * @see analog_init
343
 **/
344
void set_adc_mux(int which) {  
345
  // mask so only proper bits are possible.  
346
  PORTG = (PORTG & 0xE3) | ((which & 0x03) << 3) | (which & 0x04);
347
}
348

    
349
/**@}**/ //end defgroup
350

    
351

    
352
ISR(ADC_vect) {
353
        int adc_h = 0;
354
        int adc_l = 0;
355
        
356
        if(adc_loop_status != ADC_LOOP_RUNNING) return;
357

    
358
        //Store the value only if this read isn't for the BOM
359
        if (ADMUX != BOM_PORT) {
360
                adc_l = ADCL;
361
                adc_h = ADCH;
362
        
363
                an_val[adc_current_port - 1].adc10 = (adc_h << 2) | (adc_l >> 6);
364
                an_val[adc_current_port - 1].adc8 = adc_h;
365
        }
366
        
367
        //Skip AN7 because it is not a real port
368
        if (adc_current_port == AN6) {
369
                ADMUX = ADMUX_OPT | EXT_MUX;
370
                set_adc_mux(AN8 - 8);
371
                adc_current_port = AN8;
372
        //Wrap around
373
        } else if (adc_current_port == AN11) {
374
                adc_current_port = AN1;
375
                ADMUX = ADMUX_OPT | adc_current_port;
376
        //Normal increment
377
        } else {
378
                adc_current_port++;
379
        
380
                if(adc_current_port < EXT_MUX) {
381
                        ADMUX = ADMUX_OPT | adc_current_port;
382
                } else {
383
                        ADMUX = ADMUX_OPT | EXT_MUX;
384
                        set_adc_mux(adc_current_port - 8);
385
                }
386
        }
387

    
388
        //Stop loop if signal is set
389
        if(adc_sig_stop_loop) {
390
                adc_sig_stop_loop = 0;
391
                adc_loop_status = ADC_LOOP_STOPPED;
392
                return;
393
        }
394
        
395
        //Start next conversion
396
        ADCSRA |= _BV(ADSC);
397
}
398