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;
}