02-26-12
Here is what I've been thinking about so far:
I've been pouring over the datasheet and I've come to some degree of understanding of the various registers involved in an Analog to Digital Conversion.
I've found
this file really useful. It takes a second to orient yourself but it lists what every pin and register stands for and what they are defined as. I found the section that defines where in memory each of the registers resides particularly interesting.
Here are the important registers:
ADMUX:
; ADMUX - The ADC multiplexer Selection Register
.equ MUX0 = 0 ; Analog Channel and Gain Selection Bits
.equ MUX1 = 1 ; Analog Channel and Gain Selection Bits
.equ MUX2 = 2 ; Analog Channel and Gain Selection Bits
.equ MUX3 = 3 ; Analog Channel and Gain Selection Bits
.equ REFS2 = 4 ; Reference Selection Bit 2
.equ ADLAR = 5 ; Left Adjust Result
.equ REFS0 = 6 ; Reference Selection Bit 0
.equ REFS1 = 7 ; Reference Selection Bit 1
- REFS[2:0] : Sets the reference voltage for ADCs
- MUX[3:0] : decides which pin is going to be read by the ADC. For our purposes, we only care about the bottom two bits (00**). The top two bits (**00) deal with differential input which isn't useful to me (yet).
- if nothing is set to MUX[3:0] (0000), but ADC is enabled, it will try to use the reset pin (PB5/ADC0) as an input. We don't want that. We will pretend ADC0 doesn't exist.
ADCSRA:
; ADCSRA - The ADC Control and Status register
.equ ADPS0 = 0 ; ADC Prescaler Select Bits
.equ ADPS1 = 1 ; ADC Prescaler Select Bits
.equ ADPS2 = 2 ; ADC Prescaler Select Bits
.equ ADIE = 3 ; ADC Interrupt Enable
.equ ADIF = 4 ; ADC Interrupt Flag
.equ ADATE = 5 ; ADC Auto Trigger Enable
.equ ADSC = 6 ; ADC Start Conversion
.equ ADEN = 7 ; ADC Enable
- ADSC starts an ADC. ADEN must be enabled first before starting an ADC.
- The rest of the bits are not particularly useful yet, Alex may have suggested changing ADPS[2:0] to make the ADC's more accurate.
ADCH and ADCL:
; ADCH - ADC Data Register High Byte
.equ ADCH0 = 0 ; ADC Data Register High Byte Bit 0
.equ ADCH1 = 1 ; ADC Data Register High Byte Bit 1
.equ ADCH2 = 2 ; ADC Data Register High Byte Bit 2
.equ ADCH3 = 3 ; ADC Data Register High Byte Bit 3
.equ ADCH4 = 4 ; ADC Data Register High Byte Bit 4
.equ ADCH5 = 5 ; ADC Data Register High Byte Bit 5
.equ ADCH6 = 6 ; ADC Data Register High Byte Bit 6
.equ ADCH7 = 7 ; ADC Data Register High Byte Bit 7
; ADCL - ADC Data Register Low Byte
.equ ADCL0 = 0 ; ADC Data Register Low Byte Bit 0
.equ ADCL1 = 1 ; ADC Data Register Low Byte Bit 1
.equ ADCL2 = 2 ; ADC Data Register Low Byte Bit 2
.equ ADCL3 = 3 ; ADC Data Register Low Byte Bit 3
.equ ADCL4 = 4 ; ADC Data Register Low Byte Bit 4
.equ ADCL5 = 5 ; ADC Data Register Low Byte Bit 5
.equ ADCL6 = 6 ; ADC Data Register Low Byte Bit 6
.equ ADCL7 = 7 ; ADC Data Register Low Byte Bit 7
- These registers tell the returned value from the last ADC.
Here's my code so far, it doesn't quite work, the duration that the LED blinks changes mysteriously and then holds a single value upon a hard reset (disconnecting then reconnecting ground).
/* analog_input.c
* run '$ make' to check for erors,
* '$ make writeflash' to compile and upload
*/
// Define clock speed for delay.h:
// (8MHz internal crystal; /8 prescaler):
#define F_CPU 1000000L
#include <avr/io.h>
#include <util/delay.h>
#define LED PB0
#define POTPIN PB2
void delay(int ms) {
int i;
for (i=0; i<ms; i++) {
_delay_ms(1);
}
}
int main(void){
// Setup :
//Set LED as output:
DDRB |= (1<<LED);
// Start it high:
PORTB |= (1<<LED);
// Enable ADC:
ADCSRA |= (1<<ADEN);
// Select ADC1 for reading:
ADMUX = 0b00000001;
int val = 0;
//--
// Main:
while (1) {
ADCSRA |= (1<<ADEN);
//example code from http://avrbasiccode.wikispaces.com/ used for reference
//discard first conversion
ADCSRA |= (1<<ADSC);
while (ADCSRA & (1 << ADSC));
//start real conversion
ADCSRA |= (1<<ADSC);
while (ADCSRA & (1 << ADSC));
//shut down ADC
ADCSRA &= ~(1<<ADSC);
//combine the two registers together to get the full 10 bit range.
val = (ADCH<<8)|ADCL;
//with the newly sampled val, turn on the led for val milliseconds
PORTB |= (1<<LED);
delay(val);
// ... then switch off LED pin for n milliseconds
PORTB &= ~(1<<LED);
delay(val);
}
return 0;
}
I think my code is right at this point but I'm not sure about the physical setup. It could be that I'm still doing something sort of silly and it's not doing the thing I think it's doing but somehow I don't think that's the case. I'll ask Alex about it.