Project

General

Profile

Revision 1345

Added by Rich Hong over 11 years ago

Final spline code for master/slave

updated outdated libdragonfly and libwireless

View differences:

analog.c
1
/*
2
 * analog.c - Contains the function implementations for manipulating the ADC
3
 * on the firefly+ board
4
 * 
5
 * author:  CMU Robotics Club, Colony Project
6
 * code mostly taken from fwr analog file (author: Tom Lauwers)
7
 */
8

  
9
#include <util/delay.h>
10
#include <avr/interrupt.h>
11
#include "analog.h"
12

  
13
// Internal Function Prototypes
14
void set_adc_mux(int which);
15

  
16
/**
17
 * @defgroup analog Analog
18
 * Functions for manipulation the ADC on the dragonfly board.
19
 * All definitions may be found in analog.h.
20
 *
21
 * @{
22
 **/
23

  
24

  
25
/**
26
 * Initializes the ADC.
27
 * Call analog_init before reading from the analog ports.
28
 *
29
 * @see analog8, analog10
30
 **/
31
void analog_init(void)
32
{
33
	// ADC Status Register A
34
	// Bit 7 - ADEN is set (enables analog)
35
	// Bit 6 - Start conversion bit is set (must be done once for free-running mode)
36
	// Bit 5 - Enable Auto Trigger (for free running mode)
37
	// Bit 4 - ADC interrupt flag, 0
38
	// Bit 3 - Enable ADC Interrupt (required to run free-running mode)
39
	// Bits 2-0 - Set to create a clock divisor of 128, to make ADC clock = 8,000,000/128
40
	ADCSRA |= 0xEF;
41

  
42
	// ADC Status Register B
43
	// Bit 7, 5-3 - Must be cleared
44
	// Bit 2:0 - Set mode, currently cleared for free running operation
45
	// Bit 6 - Analog comparator mode - cleared
46
//	ADCSRB = 0x00;
47
	
48
	// ADMUX register
49
	// Bit 7,6 - Set voltage reference to AVcc (0b01)
50
	// Bit 5 - Set ADLAR bit for left adjust to do simple 8-bit reads
51
	// Bit 4 - X
52
	// Bit 3:0 - Sets the current channel, set to ADC7 (the external mux)
53
	ADMUX = 0x67;
54

  
55
	// Set external mux lines to outputs
56
	DDRG |= 0x1C;
57
	set_adc_mux(0x07);
58
}	
59

  
60

  
61
/**
62
 * Reads an eight bit number from an analog port.
63
 * analog_init must be called before using this function.
64
 * 
65
 * @param which the analog port to read from. One of
66
 * the constants AN0 - AN7.
67
 *
68
 * @return the eight bit input to the specified port
69
 *
70
 * @see analog_init, analog10
71
 **/
72
unsigned int analog8(int which)
73
{
74
	if(which < EXT_MUX)
75
		ADMUX = 0x60 + which;
76
	else if(which == EXT_MUX)
77
		return 0;
78
	else
79
	{
80
		ADMUX = 0x60 + EXT_MUX;
81
		set_adc_mux(which - 8);
82
		_delay_ms(1);
83
	}
84
	
85
	_delay_ms(1); // need at least 130 us between conversions
86
	return ADCH;
87
}
88

  
89
/**
90
 * Reads a ten bit number from the specified port.
91
 * analog_init must be called before using this function.
92
 *
93
 * @param which the analog port to read from. Typically
94
 * a constant, one of AN0 - AN7.
95
 *
96
 * @return the ten bit number input to the specified port
97
 * 
98
 * @see analog_init, analog8
99
 **/
100
unsigned int analog10(int which)
101
{
102
	unsigned int adc_h = 0;
103
	unsigned int adc_l = 0;
104

  
105
	if(which < EXT_MUX)
106
		ADMUX = 0x60 + which;
107
	else if(which == EXT_MUX)
108
		return 0;
109
	else
110
	{
111
		ADMUX = 0x60 + EXT_MUX;
112
		set_adc_mux(which - 8);
113
		_delay_ms(1);
114
	}
115

  
116
	_delay_ms(1);
117
	adc_l = ADCL; /* highest 2 bits of ADCL -> least 2 bits of analog val */
118
	adc_h = ADCH; /* ADCH -> 8 highest bits of analog val */
119
	
120
	return (adc_h << 2) | (adc_l >> 6);
121
}
122

  
123
/**
124
 * Returns the current position of the wheel, as an integer
125
 * in the range 0 - 255.
126
 * analog_init must be called before using this function.
127
 *
128
 * @return the orientation of the wheel, as an integer in
129
 * the range 0 - 255.
130
 *
131
 * @see analog_init
132
 **/
133
int wheel(void)
134
{
135
	return analog8(WHEEL_PORT);
136
}
137

  
138
/**@}**/ //end defgroup
139

  
140
SIGNAL (SIG_ADC)
141
{
142
	// This is just here to catch ADC interrupts because ADC is free running.  
143
	// No code needs to be in here.
144
}
145

  
146
void set_adc_mux(int which)
147
{
148
  // FIX THIS IN NEXT REVISION
149
  // ADDR2 ADDR1 ADDR0
150
  // G2.G4.G3 set mux to port 0-7 via binary selection
151
  // math would be much cleaner if it was G4.G3.G2
152
  
153
  // mask so only proper bits are possible.  
154
  PORTG = (PORTG & 0xE3) | ((which & 0x03) << 3) | (which & 0x04);
155
}
156

  
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
// Internal Function Prototypes
41
void set_adc_mux(int which);
42

  
43
/**
44
 * @defgroup analog Analog
45
 * Functions for manipulation the ADC on the dragonfly board.
46
 * All definitions may be found in analog.h.
47
 *
48
 * @{
49
 **/
50

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

  
55
/**
56
 * Initializes the ADC.
57
 * Call analog_init before reading from the analog ports.
58
 *
59
 * @see analog8, analog10, analog_get8, analog_get10
60
 **/
61
void analog_init(int start_conversion) {
62
	for (int i = 0; i < 11; i++) {
63
		an_val[i].adc10 = 0;
64
		an_val[i].adc8 = 0;
65
	}
66

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

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

  
93
	//Start the conversion if requested
94
	if (start_conversion)
95
		analog_start_loop();
96
	else
97
		analog_stop_loop();
98
}	
99

  
100
/**
101
 * Returns the 8-bit analog conversion of which from
102
 * the lookup table. If the requested port is the BOM_PORT
103
 * you will get an automatic 0 since the BOM_PORT is not
104
 * read in the loop and not stored. If you need that port
105
 * you should use the functions in bom.c. There is an analog_get8
106
 * function which for instant lookups but should be avoided unless
107
 * you know what you're doing.
108
 *
109
 * @param which the port that you want to read
110
 *
111
 * @bug may cause a seg fault if which is a larger value
112
 * than exists in an_val table. Not sure if we should fix
113
 * this or not since it would add overhead.
114
 *
115
 * @return 8-bit analog value for the which port requested
116
 *
117
 * @see analog10, analog_get8, analog_get10
118
 **/
119
unsigned int analog8(int which) {
120
	if (which == BOM_PORT) {
121
		return 0;
122
	} else {
123
		return an_val[which - 1].adc8;
124
	}
125
}
126

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

  
154
/**
155
 * Starts the analog update loop. Will continue to run
156
 * until analog_stop_loop is called.
157
 *
158
 * @see analog_stop_loop
159
 **/
160
void analog_start_loop(void) {
161
	//Start the conversion
162
	ADCSRA |= _BV(ADSC);
163
	adc_loop_running = 0x1;
164
}
165

  
166
/**
167
 * Stops the analog update loop. If there is a current
168
 * read, it will finish up and be stored before the loop
169
 * is interrupted. No further updates will be made until
170
 * the loop is started again.
171
 *
172
 * @see analog_start_loop
173
 **/
174
void analog_stop_loop(void) {
175
	//Stop the conversion
176
	adc_loop_running = 0x0;
177
}
178
/**
179
 * Reads an 8-bit number from an analog port.
180
 * analog_init must be called before using this function.
181
 * The analog loop must also be stopped before using this
182
 * function or you will mess up the lookup table. You
183
 * must also reenabled the loop when you are done unless
184
 * you are doing more instant reads. See analog_stop_loop
185
 * and analog_start_loop for more information about the loop.
186
 * 
187
 * @param which the analog port to read from. One of
188
 * the constants AN0 - AN7.
189
 *
190
 * @return the 8-bit input to the specified port
191
 *
192
 * @see analog_init, analog_get10, analog8, analog_stop_loop,
193
 * analog_start_loop
194
 **/
195
unsigned int analog_get8(int which) {	
196
	// Let any previous conversion finish
197
	while (ADCSRA & _BV(ADSC));
198
	
199
	if(which < EXT_MUX) {
200
		ADMUX = ADMUX_OPT + which;
201
	} else {
202
		ADMUX = ADMUX_OPT + EXT_MUX;
203
		set_adc_mux(which - 8);
204
	}
205
	
206
	// Start the conversion
207
	ADCSRA |= _BV(ADSC);
208

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

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

  
215
/**
216
 * Reads an 10-bit number from an analog port.
217
 * analog_init must be called before using this function.
218
 * The analog loop must also be stopped before using this
219
 * function or you will mess up the lookup table. You
220
 * must also reenabled the loop when you are done unless
221
 * you are doing more instant reads. See analog_stop_loop
222
 * and analog_start_loop for more information about the loop.
223
 * 
224
 *
225
 * @param which the analog port to read from. Typically
226
 * a constant, one of AN0 - AN7.
227
 *
228
 * @return the 10-bit number input to the specified port
229
 * 
230
 * @see analog_init, analog_get8, analog10, analog_stop_loop,
231
 * analog_start_loop
232
 **/
233
unsigned int analog_get10(int which) {
234
	int adc_h;
235
	int adc_l;
236
	
237
	// Let any previous conversion finish
238
	while (ADCSRA & _BV(ADSC));
239

  
240
	if(which < EXT_MUX) {
241
		ADMUX = ADMUX_OPT + which;
242
	} else {
243
		ADMUX = ADMUX_OPT + EXT_MUX;
244
		set_adc_mux(which - 8);
245
	}
246
	
247
	// Start the conversion
248
	ADCSRA |= _BV(ADSC);
249

  
250
	// Wait for the conversion to finish
251
	while (ADCSRA & _BV(ADSC));
252
	
253
	adc_l = ADCL;
254
	adc_h = ADCH;
255

  
256
	return ((adc_h << 2) | (adc_l >> 6));
257
}
258

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

  
273

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

  
293
/**@}**/ //end defgroup
294

  
295

  
296
ISR(ADC_vect) {
297
	static volatile int adc_prev_loop_running = 0; 
298
	int adc_h = 0;
299
	int adc_l = 0;
300

  
301
	//Store the value only if this read isn't for the BOM
302
	if (ADMUX != BOM_PORT) {
303
		adc_l = ADCL;
304
		adc_h = ADCH;
305
	
306
		an_val[adc_current_port - 1].adc10 = (adc_h << 2) | (adc_l >> 6);
307
		an_val[adc_current_port - 1].adc8 = adc_h;
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
    } else {
341
    	ADCSRA |= _BV(ADSC);
342
	}
343
		
344
	return;
345
}
346

  

Also available in: Unified diff