Help with a2d converter on atmega16

Go To Last Post
8 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

About a year ago I built the $50 dollar robot, the tutorial is on the society of robots site. Since then, I have done several modifications to the code and robot. I made a maze navigation robot that used sharp IR sensors to find it's way through a maze, but I gave that robot away some time ago.

Now, I'm trying to use same model sharp IR sensor and hardware to recreate the maze robot and I'm stuck. Unfortunately, I don't have a copy of the working code from my previous robot :x

Here's what I have going. I'm trying to set up a threshold voltage input to analog sensors act like switches. My test setup is a voltage divider to send 0-5vdc to my a2d port on the atmega16, and am using an LED to indicate when I've met the threshold. But I do not get anything close to meaningful results. The LED randomly flickers even if NOTHING is connected to the a2d port. I've checked all my wiring with a meter and all the power, VCC, AREF,AVCC etc are all good. So, it must be a coding issue? Here is what I have:

#include "SoR_Utils.h" 

int main(void)
	{
	//declare variables here
	int Front_IR=0;//Front IR sensor

	/****************INITIALIZATIONS*******************/
	configure_ports(); // configure which ports are analog, digital, etc.
	a2dInit(); // initialize analog to digital converter (ADC)
	a2dSetPrescaler(ADC_PRESCALE_DIV128); // configure ADC scaling
	a2dSetReference(ADC_REFERENCE_AVCC); // configure ADC reference voltage
		
		while(1)
		{

 		Front_IR=a2dConvert8bit(0);
		if (Front_IR > 120) {
		PORT_ON(PORTD, 4);
		}
		else {
		PORT_OFF(PORTD, 4);
		}  

	    }//end infinite while loop
	return 0;
	}

Any insight into what I'm doing wrong would be greatly appreciated!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

We can only guess at the implementation of a2dInit, a2dSetPrescaler, a2dSetReference and a2dConvert8bit. Show us the whole code.

Regards,
Steve A.

The Board helps those that help themselves.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I guess all the code would help!

This is the section that contains configure_ports()

//AVR includes
#include 		    // include I/O definitions (port names, pin names, etc)
#include 	// include interrupt support

//AVRlib includes
#include "global.h"		// include global settings
#include "a2d.h"		// include A/D converter function library

//define port functions; example: PORT_ON( PORTD, 6);
#define PORT_ON( port_letter, number )			port_letter |= (1<<number)
#define PORT_OFF( port_letter, number )			port_letter &= ~(1<<number)
#define PORT_ALL_ON( port_letter, number )		port_letter |= (number)
#define PORT_ALL_OFF( port_letter, number )		port_letter &= ~(number)
#define FLIP_PORT( port_letter, number )		port_letter ^= (1<<number)
#define PORT_IS_ON( port_letter, number )		( port_letter & (1<<number) )
#define PORT_IS_OFF( port_letter, number )		!( port_letter & (1<<number) )

//************CONFIGURE PORTS************
//configure ports for input or output
void configure_ports(void)
	{
	DDRA = 0x00;  //configure all A ports for input
	PORTA = 0x00; //make sure pull-up resistors are turned off
	DDRC = 0x00;  //configure all C ports for input
	PORTC = 0xFF; //make sure pull-up resistors are turned on
	DDRD = 0xFF;  //configure all D ports for output
	DDRB = 0xFF;  //configure all B ports for output
	}

//************DELAY FUNCTIONS************
//wait for X amount of cycles (23 cycles is about .992 milliseconds)
//to calculate: 23/.992*(time in milliseconds) = number of cycles
void delay_cycles(unsigned long int cycles)
	{
	while(cycles > 0)
		cycles--;
	}
//***************************************

//*********SIMPLIFIED FUNCTIONS**********
//functions to make coding easier for a beginner
void servo_left(signed long int speed)
	{
	PORT_ON(PORTD, 0);
	delay_cycles(speed);
	PORT_OFF(PORTD, 0);//keep off
	delay_cycles(200);
	}
void servo_right(signed long int speed)
	{
	PORT_ON(PORTD, 1);
	delay_cycles(speed);		
	PORT_OFF(PORTD, 1);//keep off
	delay_cycles(200);
	}

a2d.h

/*! \file a2d.h \brief Analog-to-Digital converter function library. */
//*****************************************************************************
//
// File Name	: 'a2d.h'
// Title		: Analog-to-digital converter functions
// Author		: Pascal Stang - Copyright (C) 2002
// Created		: 4/08/2002
// Revised		: 4/30/2002
// Version		: 1.1
// Target MCU	: Atmel AVR series
// Editor Tabs	: 4
//
// This code is distributed under the GNU Public License
//		which can be found at http://www.gnu.org/licenses/gpl.txt
//
///	\ingroup driver_avr
/// \defgroup a2d A/D Converter Function Library (a2d.c)
/// \code #include "a2d.h" \endcode
/// \par Overview
///		This library provides an easy interface to the analog-to-digital
///		converter available on many AVR processors.  Updated to support
///		the ATmega128.
//
//****************************************************************************
//@{

#ifndef A2D_H
#define A2D_H

// defines

// A2D clock prescaler select
//		*selects how much the CPU clock frequency is divided
//		to create the A2D clock frequency
//		*lower division ratios make conversion go faster
//		*higher division ratios make conversions more accurate
#define ADC_PRESCALE_DIV2		0x00	///< 0x01,0x00 -> CPU clk/2
#define ADC_PRESCALE_DIV4		0x02	///< 0x02 -> CPU clk/4
#define ADC_PRESCALE_DIV8		0x03	///< 0x03 -> CPU clk/8
#define ADC_PRESCALE_DIV16		0x04	///< 0x04 -> CPU clk/16
#define ADC_PRESCALE_DIV32		0x05	///< 0x05 -> CPU clk/32
#define ADC_PRESCALE_DIV64		0x06	///< 0x06 -> CPU clk/64
#define ADC_PRESCALE_DIV128		0x07	///< 0x07 -> CPU clk/128
// default value
#define ADC_PRESCALE			ADC_PRESCALE_DIV64
// do not change the mask value
#define ADC_PRESCALE_MASK		0x07

// A2D voltage reference select
//		*this determines what is used as the
//		full-scale voltage point for A2D conversions
#define ADC_REFERENCE_AREF		0x00	///< 0x00 -> AREF pin, internal VREF turned off
#define ADC_REFERENCE_AVCC		0x01	///< 0x01 -> AVCC pin, internal VREF turned off
#define ADC_REFERENCE_RSVD		0x02	///< 0x02 -> Reserved
#define ADC_REFERENCE_256V		0x03	///< 0x03 -> Internal 2.56V VREF
// default value
#define ADC_REFERENCE			ADC_REFERENCE_AVCC
// do not change the mask value
#define ADC_REFERENCE_MASK		0xC0

// bit mask for A2D channel multiplexer
#define ADC_MUX_MASK			0x1F

// channel defines (for reference and use in code)
// these channels supported by all AVRs with A2D
#define ADC_CH_ADC0				0x00
#define ADC_CH_ADC1				0x01
#define ADC_CH_ADC2				0x02
#define ADC_CH_ADC3				0x03
#define ADC_CH_ADC4				0x04
#define ADC_CH_ADC5				0x05
#define ADC_CH_ADC6				0x06
#define ADC_CH_ADC7				0x07
#define ADC_CH_122V				0x1E	///< 1.22V voltage reference
#define ADC_CH_AGND				0x1F	///< AGND
// these channels supported only in ATmega128
// differential with gain
#define ADC_CH_0_0_DIFF10X		0x08
#define ADC_CH_1_0_DIFF10X		0x09
#define ADC_CH_0_0_DIFF200X		0x0A
#define ADC_CH_1_0_DIFF200X		0x0B
#define ADC_CH_2_2_DIFF10X		0x0C
#define ADC_CH_3_2_DIFF10X		0x0D
#define ADC_CH_2_2_DIFF200X		0x0E
#define ADC_CH_3_2_DIFF200X		0x0F
// differential
#define ADC_CH_0_1_DIFF1X		0x10
#define ADC_CH_1_1_DIFF1X		0x11
#define ADC_CH_2_1_DIFF1X		0x12
#define ADC_CH_3_1_DIFF1X		0x13
#define ADC_CH_4_1_DIFF1X		0x14
#define ADC_CH_5_1_DIFF1X		0x15
#define ADC_CH_6_1_DIFF1X		0x16
#define ADC_CH_7_1_DIFF1X		0x17

#define ADC_CH_0_2_DIFF1X		0x18
#define ADC_CH_1_2_DIFF1X		0x19
#define ADC_CH_2_2_DIFF1X		0x1A
#define ADC_CH_3_2_DIFF1X		0x1B
#define ADC_CH_4_2_DIFF1X		0x1C
#define ADC_CH_5_2_DIFF1X		0x1D

// compatibility for new Mega processors
// ADCSR hack apparently no longer necessary in new AVR-GCC
#ifdef ADCSRA
#ifndef ADCSR
	#define ADCSR	ADCSRA
#endif
#endif
#ifdef ADATE
	#define ADFR	ADATE
#endif

// function prototypes

//! Initializes the A/D converter.
/// Turns ADC on and prepares it for use.
void a2dInit(void);

//! Turn off A/D converter
void a2dOff(void);

//! Sets the division ratio of the A/D converter clock.
/// This function is automatically called from a2dInit()
/// with a default value.
void a2dSetPrescaler(unsigned char prescale);

//! Configures which voltage reference the A/D converter uses.
/// This function is automatically called from a2dInit()
/// with a default value.
void a2dSetReference(unsigned char ref);

//! sets the a2d input channel
void a2dSetChannel(unsigned char ch);

//! start a conversion on the current a2d input channel
void a2dStartConvert(void);

//! return TRUE if conversion is complete
u08 a2dIsComplete(void);

//! Starts a conversion on A/D channel# ch,
/// returns the 10-bit value of the conversion when it is finished.
unsigned short a2dConvert10bit(unsigned char ch);

//! Starts a conversion on A/D channel# ch,
/// returns the 8-bit value of the conversion when it is finished.
unsigned char a2dConvert8bit(unsigned char ch);

#endif
//@}

a2d.c

/*! \file a2d.c \brief Analog-to-Digital converter function library. */
//*****************************************************************************
//
// File Name	: 'a2d.c'
// Title		: Analog-to-digital converter functions
// Author		: Pascal Stang - Copyright (C) 2002
// Created		: 2002-04-08
// Revised		: 2002-09-30
// Version		: 1.1
// Target MCU	: Atmel AVR series
// Editor Tabs	: 4
//
// This code is distributed under the GNU Public License
//		which can be found at http://www.gnu.org/licenses/gpl.txt
//
//*****************************************************************************

#include 
#include 

#include "global.h"
#include "a2d.h"

// global variables

//! Software flag used to indicate when
/// the a2d conversion is complete.
volatile unsigned char a2dCompleteFlag;

// functions

// initialize a2d converter
void a2dInit(void)
{
	sbi(ADCSR, ADEN);				// enable ADC (turn on ADC power)
	cbi(ADCSR, ADFR);				// default to single sample convert mode
	a2dSetPrescaler(ADC_PRESCALE);	// set default prescaler
	a2dSetReference(ADC_REFERENCE);	// set default reference
	cbi(ADMUX, ADLAR);				// set to right-adjusted result

	sbi(ADCSR, ADIE);				// enable ADC interrupts

	a2dCompleteFlag = FALSE;		// clear conversion complete flag
	sei();							// turn on interrupts (if not already on)
}

// turn off a2d converter
void a2dOff(void)
{
	cbi(ADCSR, ADIE);				// disable ADC interrupts
	cbi(ADCSR, ADEN);				// disable ADC (turn off ADC power)
}

// configure A2D converter clock division (prescaling)
void a2dSetPrescaler(unsigned char prescale)
{
	outb(ADCSR, ((inb(ADCSR) & ~ADC_PRESCALE_MASK) | prescale));
}

// configure A2D converter voltage reference
void a2dSetReference(unsigned char ref)
{
	outb(ADMUX, ((inb(ADMUX) & ~ADC_REFERENCE_MASK) | (ref<<6)));
}

// sets the a2d input channel
void a2dSetChannel(unsigned char ch)
{
	outb(ADMUX, (inb(ADMUX) & ~ADC_MUX_MASK) | (ch & ADC_MUX_MASK));	// set channel
}

// start a conversion on the current a2d input channel
void a2dStartConvert(void)
{
	sbi(ADCSR, ADIF);	// clear hardware "conversion complete" flag 
	sbi(ADCSR, ADSC);	// start conversion
}

// return TRUE if conversion is complete
u08 a2dIsComplete(void)
{
	return bit_is_set(ADCSR, ADSC);
}

// Perform a 10-bit conversion
// starts conversion, waits until conversion is done, and returns result
unsigned short a2dConvert10bit(unsigned char ch)
{
	a2dCompleteFlag = FALSE;				// clear conversion complete flag
	outb(ADMUX, (inb(ADMUX) & ~ADC_MUX_MASK) | (ch & ADC_MUX_MASK));	// set channel
	sbi(ADCSR, ADIF);						// clear hardware "conversion complete" flag 
	sbi(ADCSR, ADSC);						// start conversion
	//while(!a2dCompleteFlag);				// wait until conversion complete
	//while( bit_is_clear(ADCSR, ADIF) );		// wait until conversion complete
	while( bit_is_set(ADCSR, ADSC) );		// wait until conversion complete

	// CAUTION: MUST READ ADCL BEFORE ADCH!!!
	return (inb(ADCL) | (inb(ADCH)<<8));	// read ADC (full 10 bits);
}

// Perform a 8-bit conversion.
// starts conversion, waits until conversion is done, and returns result
unsigned char a2dConvert8bit(unsigned char ch)
{
	// do 10-bit conversion and return highest 8 bits
	return a2dConvert10bit(ch)>>2;			// return ADC MSB byte
}

//! Interrupt handler for ADC complete interrupt.
SIGNAL(SIG_ADC)
{
	// set the a2d conversion flag to indicate "complete"
	a2dCompleteFlag = TRUE;
}

That's all for external code files other then the AVR library stuff.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Curious code. The conversion synchronously blocks on the ADSC bit and yet they set ADIE and provide an ISR that doesn't seem to have much purpose?!?

The whole thing looks like a complete mish-mash of started/aborted experiments.

It's all very well using "driver code" to prevent the need to understand how a peripheral works but when that code is "suspect" I don't see what's to be gained? It's very easy to write a single channel synchronous conversion routine yourself in about 5 lines of C. Doing it yourself you will totally understand the peripheral rather than peering into the murky depths of someone else's "black box".

Their two register reading code (why not ADLAR for 8bit??) is a bit suspicious too:

// CAUTION: MUST READ ADCL BEFORE ADCH!!! 
   return (inb(ADCL) | (inb(ADCH)<<8));   // read ADC (full 10 bits); 

the comment notes the requirement but wouldn't

return ADCW;

be simpler/safer than relying on the compiler's code ordering?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

inb(ADCL)

#define inb(xyz) (xyz)

Right?

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ditch it and make your own using the ADC tutorial, searched out posts on ADC and the d.sht. Or start from the working code at society.

1) Studio 4.18 build 716 (SP3)
2) WinAvr 20100110
3) PN, all on Doze XP... For Now
A) Avr Dragon ver. 1
B) Avr MKII ISP, 2009 model
C) MKII JTAGICE ver. 1

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Guess starting from the ground up was the right way to go. I have everything working properly with the voltage divider. I do have one more question.

The tutorial code works and makes sense, with the exception of line

ADCSRA |= (1 << ADFR);  // Set ADC to Free-Running Mode

According to my manual there is no ADFR register in ADCSRA, I would have to modify registers in SFIOR to get to free running mode. However, I get errors when I try to do this. Just wondering what I missed there.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

My spec sheet says SFIOR bits ADTS2:0 which happen to default to free-run.

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net