Project

General

Profile

Revision 322

Copied new analog code into recharging branch.

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
/** @brief Struct to hold the value of a particular analog port */
44
typedef struct {
45
	uint8_t adc8;
46
	uint16_t adc10;
47
} adc_t;
48

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

  
57
int adc_loop_running = 0;
58
int adc_current_port = AN1;
59
adc_t an_val[11];
60

  
61
/**
62
 * Initializes the ADC.
63
 * Call analog_init before reading from the analog ports.
64
 *
65
 * @see analog8, analog10
66
 **/
67
void analog_init(int start_conversion)
68
{
69
	for (int i = 0; i < 11; i++) {
70
		an_val[i].adc10 = 0;
71
		an_val[i].adc8 = 0;
72
	}
73

  
74
	//cli();
75
	// ADMUX register
76
	// Bit 7,6 - Set voltage reference to AVcc (0b01)
77
	// Bit 5 - ADLAR set to simplify moving from register
78
	// Bit 4 - X
79
	// Bit 3:0 - Sets the current channel
80
	// Initializes to read from AN1 first (AN0 is reservered for the BOM)
81
	ADMUX = 0;
82
	ADMUX |= ADMUX_OPT | _BV(MUX0);
83

  
84

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

  
102
	//Start the conversion if requested
103
	if (start_conversion)
104
		analog_start_loop();
105
	else
106
		analog_stop_loop();
107
	//sei();
108
	
109
}	
110

  
111
unsigned int analog8(int which) {
112
	if (which == BOM_PORT) {
113
		return 0;
114
	} else {
115
		return an_val[which - 1].adc8;
116
	}
117
}
118

  
119
unsigned int analog10(int which) {
120
	if (which == BOM_PORT) {
121
		return 0;
122
	} else {
123
		return an_val[which - 1].adc10;
124
	}
125
}
126

  
127
void analog_start_loop(void) {
128
	//Start the conversion
129
	ADCSRA |= _BV(ADSC);
130
	adc_loop_running = 0x1;
131
}
132

  
133
//will stop after current conversion finishes
134
void analog_stop_loop(void) {
135
	//Stop the conversion
136
	adc_loop_running = 0x0;
137
}
138
/**
139
 * Reads an eight bit number from an analog port.
140
 * analog_init must be called before using this function.
141
 * 
142
 * @param which the analog port to read from. One of
143
 * the constants AN0 - AN7.
144
 *
145
 * @return the eight bit input to the specified port
146
 *
147
 * @see analog_init, analog10
148
 **/
149
unsigned int analog_get8(int which)
150
{	
151
	// Let any previous conversion finish
152
	while (ADCSRA & _BV(ADSC));
153
	
154
	if(which < EXT_MUX) {
155
		ADMUX = ADMUX_OPT + which;
156
	} else {
157
		ADMUX = ADMUX_OPT + EXT_MUX;
158
		set_adc_mux(which - 8);
159
	}
160
	
161
	// Start the conversion
162
	ADCSRA |= _BV(ADSC);
163

  
164
	// Wait for the conversion to finish
165
	while (ADCSRA & _BV(ADSC));
166

  
167
	return ADCH; //since we left aligned the data, ADCH is the 8 MSB.
168
}
169

  
170
/**
171
 * Reads a ten bit number from the specified port.
172
 * analog_init must be called before using this function.
173
 * 
174
 *
175
 * @param which the analog port to read from. Typically
176
 * a constant, one of AN0 - AN7.
177
 *
178
 * @return the ten bit number input to the specified port
179
 * 
180
 * @see analog_init, analog8
181
 **/
182
unsigned int analog_get10(int which)
183
{
184
	int adc_h;
185
	int adc_l;
186
	
187
	// Let any previous conversion finish
188
	while (ADCSRA & _BV(ADSC));
189

  
190
	if(which < EXT_MUX) {
191
		ADMUX = ADMUX_OPT + which;
192
	} else {
193
		ADMUX = ADMUX_OPT + EXT_MUX;
194
		set_adc_mux(which - 8);
195
	}
196
	
197
	// Start the conversion
198
	ADCSRA |= _BV(ADSC);
199

  
200
	// Wait for the conversion to finish
201
	while (ADCSRA & _BV(ADSC));
202

  
203
	adc_l = ADCL;
204
	adc_h = ADCH;
205

  
206
	return ((adc_h << 2) | (adc_l >> 6));
207
}
208

  
209
/**
210
 * Returns the current position of the wheel, as an integer
211
 * in the range 0 - 255.
212
 * analog_init must be called before using this function.
213
 *
214
 * @return the orientation of the wheel, as an integer in
215
 * the range 0 - 255.
216
 *
217
 * @see analog_init
218
 **/
219
int wheel(void)
220
{
221
	return analog_get8(WHEEL_PORT);
222
}
223

  
224

  
225
/**
226
 * Sets the value of the external analog mux. Values are read
227
 * 	on AN7 physical port. (AN8 - AN15 are "virtual" ports).
228
 *
229
 * @param which which analog mux port (0-7) which corresponds
230
 * 		  to AN8-AN15.
231
 *
232
 * @bug FIX THIS IN THE NEXT BOARD REVISION:
233
 *		ADDR2 ADDR1 ADDR0
234
 *		G2.G4.G3 set mux to port 0-7 via vinary selection
235
 *		math would be much cleaner if it was G4.G3.G2
236
 *
237
 * @see analog_init
238
 **/
239
void set_adc_mux(int which)
240
{  
241
  // mask so only proper bits are possible.  
242
  PORTG = (PORTG & 0xE3) | ((which & 0x03) << 3) | (which & 0x04);
243
}
244

  
245
/**@}**/ //end defgroup
246

  
247

  
248
ISR(ADC_vect) {
249
	static volatile int adc_prev_loop_running = 0;
250

  
251
	int adc_h = 0;
252
	int adc_l = 0;
253

  
254
	//usb_putc('p');
255
	//usb_putc('r');
256
	//usb_puti(adc_loop_running);
257
	//usb_puts("\n\r");
258

  
259
	//Store the value only if this read isn't for the BOM
260
	if (ADMUX != BOM_PORT) {
261
		adc_l = ADCL;
262
		adc_h = ADCH;
263
	
264
		an_val[adc_current_port - 1].adc10 = (adc_h << 2) | (adc_l >> 6);
265
		an_val[adc_current_port - 1].adc8 = adc_h;
266
		//usb_puti(an_val[adc_current_port - 1].adc10);
267
		//usb_puts("\n\r");
268
		//usb_puti(an_val[adc_current_port - 1].adc8);
269
		//usb_puti(ADCH);
270
		//usb_puts("\n\r");
271
	}
272
	
273
	//Save the result only if we just turned off the loop
274
	if (!adc_loop_running && !adc_prev_loop_running)
275
		return;
276
	
277
	adc_prev_loop_running = adc_loop_running;
278
	
279
	//Skip AN7 because it is not a real port
280
	if (adc_current_port == AN6) {
281
		ADMUX = ADMUX_OPT | EXT_MUX;
282
		set_adc_mux(AN8 - 8);
283
		adc_current_port = AN8;
284
	//Wrap around
285
	} else if (adc_current_port == AN11) {
286
		adc_current_port = AN1;
287
		ADMUX = ADMUX_OPT | adc_current_port;
288
	//Normal increment
289
	} else {
290
		adc_current_port++;
291
	
292
		if(adc_current_port < EXT_MUX) {
293
			ADMUX = ADMUX_OPT | adc_current_port;
294
		} else {
295
			ADMUX = ADMUX_OPT | EXT_MUX;
296
			set_adc_mux(adc_current_port - 8);
297
		}
298
	}
299

  
300
	//Initiate next conversion only if we are running a loop
301
	if (!adc_loop_running)
302
		return;
303

  
304
	ADCSRA |= _BV(ADSC);
305
	
306
	//if (ADCSRA & _BV(ADSC))
307
	//	usb_putc('s');
308
		
309
	return;
310
}
311

  

Also available in: Unified diff