Author Topic: need 16 channel ADC code for ATmega2560  (Read 10237 times)

0 Members and 1 Guest are viewing this topic.

Offline AdminTopic starter

  • Administrator
  • Supreme Robot
  • *****
  • Posts: 11,703
  • Helpful? 173
    • Society of Robots
need 16 channel ADC code for ATmega2560
« on: January 10, 2008, 03:02:47 PM »
I've been struggling trying to get my ADC to work on my ATmega2560 (which has 16 analog to digital converters).

The avrlib a2d.h file was only written for 8 channels, but with slight modification (see below code), works fine for channels 0->7. But for channels 8->15 its just not working. :(

Also, in a2d.c, I see this line:
Code: [Select]
return (inb(ADCL) | (inb(ADCH)<<8)); // read ADC (full 10 bits);what does that << 8 do?

Ideas on how to fix it?

Code: [Select]
/*! \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
//
// modified by bobgardner on avrfreaks.com to handle 16 channels of ADC
// - required for ATmega2560, 1280
// - define mistakes found and corrected by www.societyofrobots.com
//
//*****************************************************************************
//additions with //BG by Bob Gardner for mega2560 16 ch a/d (untested!)

#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_ADC8 0x08 //BG add 8 more chans
#define ADC_CH_ADC9 0x09
#define ADC_CH_ADC10 0x0a
#define ADC_CH_ADC11 0x0b
#define ADC_CH_ADC12 0x0c
#define ADC_CH_ADC13 0x0d
#define ADC_CH_ADC14 0x0e
#define ADC_CH_ADC15 0x0f

#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

Offline klaxxon

  • Jr. Member
  • **
  • Posts: 7
  • Helpful? 0
Re: need 16 channel ADC code for ATmega2560
« Reply #1 on: January 11, 2008, 10:36:59 AM »
I don't know the solution off the top of my head, but I can shed some light on the return statement you posted.

(n << 8) means "shift the value of n 8 bits to the left." It's eqivilant to saying (n * (2^8)) meaning "n times two to the power of eight." Gotta love binary math.

The pipe character is a bitwise-OR operator. When combining values with bitwise-OR, it means if a given bit in A is set OR if a given bit of B is set, the result will also be set. It performs that operation for each bit in the value.

The return statement is essentially combining the low (least significant) and high (most significant) bytes together to make a complete 16-bit word.

I hope my rambling explanation was more-or-less clear, and at least of some help.

Cheers,
- Adam

Offline klaxxon

  • Jr. Member
  • **
  • Posts: 7
  • Helpful? 0
Re: need 16 channel ADC code for ATmega2560
« Reply #2 on: January 11, 2008, 10:39:09 AM »
Ahhhh -- my math text got turned into a smiley!

It should read:
( n << 8 ) means "shift the value of n 8 bits to the left." ....

Offline AdminTopic starter

  • Administrator
  • Supreme Robot
  • *****
  • Posts: 11,703
  • Helpful? 173
    • Society of Robots
Re: need 16 channel ADC code for ATmega2560
« Reply #3 on: January 11, 2008, 02:33:31 PM »
yea I knew what the bit shift operation was, but couldn't figure out what the purpose was for it in the code.

talking to people, I found out that the avrlib a2d code is really outdated.

this was the original line of code:
return (inb(ADCL) | (inb(ADCH)<<8 ));   // read ADC (full 10 bits);

but was told this is the new way to do it:
return ADC;

which basically means <<8 has nothing to do with my problem . . .

Offline brijesh

  • Full Member
  • ***
  • Posts: 55
  • Helpful? 0
Re: need 16 channel ADC code for ATmega2560
« Reply #4 on: January 11, 2008, 05:28:51 PM »
I am leaving for the airport in few minutes so don't have time to dig into full details. I did manage to look at the data sheet for ATmega2560.
The way the channel is selected is slightly different in ATMega250. For channels 0 to 7 its same as the old device for which the code was written. But for channels 8 to 15 bit 3 in ADCRB register has to be set.

Look at table 26-4 for the bit settings.

brijesh



Offline AdminTopic starter

  • Administrator
  • Supreme Robot
  • *****
  • Posts: 11,703
  • Helpful? 173
    • Society of Robots
Re: need 16 channel ADC code for ATmega2560
« Reply #5 on: January 14, 2008, 08:44:06 AM »
wait, its the 5th bit or the 3rd bit? (not sure which way to start counting)

0->7
010000
010001
010010

8->15
100000
100001
100010

Ok so I'm assuming it has something to do with that . . . but I can't figure what what to change in code . . .

oh this is the a2d.c code just for reference:
Code: [Select]
/*! \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 <avr/io.h>
#include <avr/interrupt.h>

#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

/*if (bit_is_set(ch, 5))
    ADCSRB |= _BV(MUX5);
else
    ADCSRB ^= _BV(MUX5);*/

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) //old version
return ADC;//new version code
}

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


Offline AdminTopic starter

  • Administrator
  • Supreme Robot
  • *****
  • Posts: 11,703
  • Helpful? 173
    • Society of Robots
Re: need 16 channel ADC code for ATmega2560
« Reply #6 on: January 14, 2008, 12:28:47 PM »
Problem solved.

This code:
Code: [Select]
outb(ADMUX, (inb(ADMUX) & ~ADC_MUX_MASK) | (ch & ADC_MUX_MASK)); // set channel

Should be this:
Code: [Select]
if (ch >= 8)
    ADCSRB |= _BV(MUX5);
else
    ADCSRB &= ~_BV(MUX5);

   outb(ADMUX, (inb(ADMUX) & ~7) | (ch & 7));   // set channel

 


Get Your Ad Here