Project

General

Profile

Statistics
| Revision:

root / branches / simulator / projects / libdragonfly / analog.c @ 891

History | View | Annotate | Download (5.88 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
 * code mostly taken from fwr analog file (author: Tom Lauwers)
34
 **/
35

    
36
#include <util/delay.h>
37
#include <avr/interrupt.h>
38
#include "analog.h"
39
#include "serial.h"
40

    
41
// Internal Function Prototypes
42
void set_adc_mux(int which);
43

    
44
#define ADMUX_OPT 0x60
45

    
46
/** @brief Struct to hold the value of a particular analog port **/
47
typedef struct {
48
  uint8_t adc8;
49
  uint16_t adc10;
50
} adc_t;
51

    
52
int adc_loop_running = 0;
53
int adc_current_port = 0;
54
adc_t an_val[11];
55

    
56
void analog_init(int start_conversion) {
57
        for (int i = 0; i < 11; i++) {
58
                an_val[i].adc10 = 0;
59
                an_val[i].adc8 = 0;
60
        }
61

    
62
        // ADMUX register
63
        // Bit 7,6 - Set voltage reference to AVcc (0b01)
64
        // Bit 5 - ADLAR set to simplify moving from register
65
        // Bit 4 - X
66
        // Bit 3:0 - Sets the current channel
67
        // Initializes to read from AN1 first (AN0 is reservered for the BOM)
68
        ADMUX = 0;
69
        ADMUX |= ADMUX_OPT | _BV(MUX0);
70

    
71
        // ADC Status Register A
72
        // Bit 7 - ADEN is set (enables analog)
73
        // Bit 6 - Start conversion bit is set (must be done once for free-running mode)
74
        // Bit 5 - Enable Auto Trigger (for free running mode) NOT DOING THIS RIGHT NOW
75
        // Bit 4 - ADC interrupt flag, 0
76
        // Bit 3 - Enable ADC Interrupt (required to run free-running mode)
77
        // Bits 2-0 - Set to create a clock divisor of 128, to make ADC clock = 8,000,000/64 = 125kHz
78
        ADCSRA = 0;
79
        ADCSRA |= _BV(ADEN) | _BV(ADIE) | _BV(ADPS2) | _BV(ADPS1) | _BV(ADPS0);
80
        
81
        // Set external mux lines to outputs
82
        DDRG |= 0x1C;
83
        
84
        // Set up first port for conversions
85
        set_adc_mux(0x00);
86
        adc_current_port = AN1;
87

    
88
        //Start the conversion if requested
89
        if (start_conversion)
90
                analog_start_loop();
91
        else
92
                analog_stop_loop();
93
}        
94

    
95
unsigned int analog8(int which) {
96
        if (which == BOM_PORT) {
97
                return 0;
98
        } else {
99
                return an_val[which - 1].adc8;
100
        }
101
}
102

    
103
unsigned int analog10(int which) {
104
        if (which == BOM_PORT) {
105
                return 0;
106
        } else {
107
                return an_val[which - 1].adc10;
108
        }
109
}
110

    
111
void analog_start_loop(void) {
112
        //Start the conversion
113
        ADCSRA |= _BV(ADSC);
114
        adc_loop_running = 0x1;
115
}
116

    
117
void analog_stop_loop(void) {
118
        //Stop the conversion
119
        adc_loop_running = 0x0;
120
}
121

    
122
unsigned int analog_get8(int which) {        
123
        // Let any previous conversion finish
124
        while (ADCSRA & _BV(ADSC));
125
        
126
        if(which < EXT_MUX) {
127
                ADMUX = ADMUX_OPT + which;
128
        } else {
129
                ADMUX = ADMUX_OPT + EXT_MUX;
130
                set_adc_mux(which - 8);
131
        }
132
        
133
        // Start the conversion
134
        ADCSRA |= _BV(ADSC);
135

    
136
        // Wait for the conversion to finish
137
        while (ADCSRA & _BV(ADSC));
138

    
139
        return ADCH; //since we left aligned the data, ADCH is the 8 MSB.
140
}
141

    
142
unsigned int analog_get10(int which) {
143
        int adc_h;
144
        int adc_l;
145
        
146
        // Let any previous conversion finish
147
        while (ADCSRA & _BV(ADSC));
148

    
149
        if(which < EXT_MUX) {
150
                ADMUX = ADMUX_OPT + which;
151
        } else {
152
                ADMUX = ADMUX_OPT + EXT_MUX;
153
                set_adc_mux(which - 8);
154
        }
155
        
156
        // Start the conversion
157
        ADCSRA |= _BV(ADSC);
158

    
159
        // Wait for the conversion to finish
160
        while (ADCSRA & _BV(ADSC));
161
        
162
        adc_l = ADCL;
163
        adc_h = ADCH;
164

    
165
        return ((adc_h << 2) | (adc_l >> 6));
166
}
167

    
168
int wheel(void) {
169
        return analog8(WHEEL_PORT);
170
}
171

    
172

    
173
/**
174
 * Sets the value of the external analog mux. Values are read
175
 *         on AN7 physical port. (AN8 - AN15 are "virtual" ports).
176
 *
177
 * @param which which analog mux port (0-7) which corresponds
178
 *                   to AN8-AN15.
179
 *
180
 * @bug FIX THIS IN THE NEXT BOARD REVISION:
181
 *                ADDR2 ADDR1 ADDR0
182
 *                G2.G4.G3 set mux to port 0-7 via vinary selection
183
 *                math would be much cleaner if it was G4.G3.G2
184
 *
185
 * @see analog_init
186
 **/
187
void set_adc_mux(int which) {  
188
  // mask so only proper bits are possible.  
189
  PORTG = (PORTG & 0xE3) | ((which & 0x03) << 3) | (which & 0x04);
190
}
191

    
192
/**@}**/ //end defgroup
193

    
194

    
195
ISR(ADC_vect) {
196
        static volatile int adc_prev_loop_running = 0; 
197
        int adc_h = 0;
198
        int adc_l = 0;
199

    
200
        //Store the value only if this read isn't for the BOM
201
        if (ADMUX != BOM_PORT) {
202
                adc_l = ADCL;
203
                adc_h = ADCH;
204
        
205
                an_val[adc_current_port - 1].adc10 = (adc_h << 2) | (adc_l >> 6);
206
                an_val[adc_current_port - 1].adc8 = adc_h;
207
        }
208
        
209
        //Save the result only if we just turned off the loop
210
        if (!adc_loop_running && !adc_prev_loop_running)
211
                return;
212
        
213
        adc_prev_loop_running = adc_loop_running;
214
        
215
        //Skip AN7 because it is not a real port
216
        if (adc_current_port == AN6) {
217
                ADMUX = ADMUX_OPT | EXT_MUX;
218
                set_adc_mux(AN8 - 8);
219
                adc_current_port = AN8;
220
        //Wrap around
221
        } else if (adc_current_port == AN11) {
222
                adc_current_port = AN1;
223
                ADMUX = ADMUX_OPT | adc_current_port;
224
        //Normal increment
225
        } else {
226
                adc_current_port++;
227
        
228
                if(adc_current_port < EXT_MUX) {
229
                        ADMUX = ADMUX_OPT | adc_current_port;
230
                } else {
231
                        ADMUX = ADMUX_OPT | EXT_MUX;
232
                        set_adc_mux(adc_current_port - 8);
233
                }
234
        }
235

    
236
        //Initiate next conversion only if we are running a loop
237
        if (!adc_loop_running) {
238
                return;
239
    } else {
240
            ADCSRA |= _BV(ADSC);
241
        }
242
                
243
        return;
244
}
245