Arduino Diecimila Board as Dumb ADC Input
From Catholicpenguin
Overview
In the context of the larger wired golfcart project, we needed an ADC board for monitoring battery voltage, etc. Bill had an Arduino Basic Bare Board (schematic), which for some reason wouldn't work with the Arduino bootloader.
Not wanting to write the board off as a total loss, I wrote a small program to sample the ADC inputs and print them out over serial without the Arduino framework, just using standard avr-libc. The board uses an ATMega168.
The result, which repeats forever about twice a second, is the following output, over 9600 baud serial, where 0 => 0 volts, and 1023 => 5.0 volts.
Chan 0 : 965 Chan 1 : 836 Chan 2 : 0 Chan 3 : 164 Chan 4 : 286 Chan 5 : 1023
Todo: Since we aren't concerned about speed, implement higher resolution ADCs through oversampling.
Code
This code was compiled on 2/22/09 using WinAVR-20081205 under Windows Vista, and programmed using AVR Studio 4.16 b 628 on the same machine.
#include <avr/io.h>
#include <stdio.h>
#include <util/delay.h>
// 103 (serial # for 16MHz, 9600 baud, ATMega168)
// Serial functions taken verbatim from datasheet
void USART_Init( unsigned int ubrr)
{
/*Set baud rate */
UBRR0H = (unsigned char)(ubrr>>8);
UBRR0L = (unsigned char)ubrr;
/*Enable receiver and transmitter */
UCSR0B = (1<<RXEN0)|(1<<TXEN0);
/* Set frame format: 8data, 2stop bit */
UCSR0C = (1<<USBS0)|(3<<UCSZ00);
}
static int USART_Transmit( unsigned char data , FILE *stream )
{
/* Wait for empty transmit buffer */
while ( !( UCSR0A & (1<<UDRE0)) )
;
/* Put data into buffer, sends the data */
UDR0 = data;
return 0;
}
static int USART_Receive( FILE *stream )
{
/* Wait for data to be received */
while ( !(UCSR0A & (1<<RXC0)) )
;
/* Get and return received data from buffer */
return UDR0;
}
static FILE mystdout = FDEV_SETUP_STREAM(USART_Transmit, USART_Receive, _FDEV_SETUP_RW);
int main(void)
{
// I'm not sure if it's needed to set the serial Rx/Tx to input/output,
// but it doesn't hurt.
DDRD &= ~(1<<0); // Input
PORTD &= ~(1<<0); // Tri-state
DDRD |= (1<<1); // Output
USART_Init(103);
stdout = &mystdout;
stdin = &mystdout;
printf("Running.\n");
// Setup ADC inputs
for (int i=0;i<6;i++)
{
DDRC &= ~(1<<i); // Input
PORTC &= ~(1<i); // Tri-state
}
ADMUX |= _BV(REFS0); // REFS1:0 REFS0:1 AVCC with external capacitor at AREF pin
ADCSRA = _BV(ADEN)|_BV(ADPS2)|_BV(ADPS1)|_BV(ADPS0); // ADEN: ADC Enable | ADC Prescaler Selections: 1 1 1 128
// (this is the slowest prescaler; we don't need speed, so we'll take all the accuracy we can get)
while (1)
{
// Simple app to read out ADC value:
for (int i=0;i<6;i++)
{
ADMUX = (ADMUX&0xF0) | i; // Switch chan
ADCSRA |= _BV(ADSC); // Start conv
while (ADCSRA & _BV(ADSC)); // Wait till done
int val = ADCL | ADCH<<8; // Note that the ADCH must be sampled last,
// as after it's sampled, the ADC value is cleared.
// This means you can't write int val = ADCH<<8 | ADCL; like you would expect
// and have it work.
printf("Chan %d : %d\r\n",i,val);
_delay_ms(100);
}
}
return 0;
}
