[TUT] [C] Newbie's Guide to the AVR ADC

Go To Last Post
324 posts / 0 new

Pages

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

A friend of mine tells me that my range of adc can be a multiple of the reference voltage...so that if im using Vcc as my reference voltage, which is going to be 2V, i should be able to measure more than 4V.

i checked the documentation on my attiny13, but i didnt see anything about that, although im probably not looking for the right thing.

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

Quote:

A friend of mine

I'd put more faith in the datasheets than a "friend". Generally the 0..255/0..1023 range of readings will be split between Gnd and the reference voltage.

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

thats what i thought, the reference voltage is the MAX voltage you can measure.

the problem is that i wanted to use an attiny to measure battery voltages, while running off of the batterys. but if its running off the batters, the attiny has to be able to run when the batterys are at there discharge point, which is 2.7V so its possible, but then if Vcc is my reference i wouldnt be able to measure more than that...

edit:
i could probably just use a couple of resistors so that the attiny gest 1.8V while the batterys are at 2.7V, then have it calculate for full etc...

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

The method to measure battery voltage using the ADC has been widely documented here a number of times. A search should find a suitable thread (I'm guessing the words "ADC" and "battery" are going to be involved! ;-))

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

Placing a resistor divider from Vbat to ground to measure battery voltage is ok, but you will waste current in the divider. If the device is supposed to run for a long time, this is a problem

You can solve that by adding a mosfet on the bottom of the divider, using an I/O pin to turn the mosfet on before you make your measurement. This way, the divider is disconnected when not in use. The mosfet draws no current in the on or off state, and its on resistance is so low that it won't be noticable.

How long you need to delay between turning on the mosfet and making the reading depends on the resistor values and whatever filter capacitance you're using.

If the battery voltage is much higher than VCC, then you'll want to switch it on the high side, using a Pfet and an open collector or open drain inverter.

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

dbvanhorn wrote:
Placing a resistor divider from .... *trimmed*

since im using an attiny13, my adc' reference voltage is the attiny13's source voltage, which means i have to be able to power the attiny13 at the same time the batterys are virtually dead.

right now the resistor divider is the only way i can think it would work right now, although a bit wasteful.

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

You use the internal bandgap 1.1V not Vref when measuring the battery. I DID suggest you search for this above. When I tried it "internal bandgap adc battery" hit a load of very useful looking threads. Just a few of those:

https://www.avrfreaks.net/index.p...
https://www.avrfreaks.net/index.p...
https://www.avrfreaks.net/index.p...
https://www.avrfreaks.net/index.p...

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

clawson wrote:
You use the internal bandgap 1.1V not Vref when measuring the battery. I DID suggest you search for this above. When I tried it "internal bandgap adc battery" hit a load of very useful looking threads. Just a few of those

i did do several searches, and didnt find any results anywhere close to that good :oops:

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

has anyone tried my code in a avr mega16?

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

Very nice tutorial, I never fully understood ADC but this tutorial made the difference.
How about considering an PWM and timers tutorial? :roll:

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

Quote:
in the datasheet they say that the Interrupt flag is set when the conversion ends, so why do i have to set it manually?

But that line does not set the flag, it clears it. Read the datasheet for why this is so and why you need it.

Regards,
Steve A.

The Board helps those that help themselves.

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

hi guys,

I am new to Microcontrollers and i am wrting a program for ADC for atmega128.my aim is to display two analog inputs ADCO and ADC7 and display it on LCD.it shpuld be updated evry 5second.I tried running the program but there are few errors.Please look into it.Thanks

#include

int ADC0=0;
int ADC7=0;
int main (void)
{

DDRE |= (1 << 2); // Set LED1 as output
DDRG |= (1 << 0); // Set LED2 as output

ADCSRA |= (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0); // Set ADC prescalar to 128 - 125KHz sample rate @ 16MHz
//set pin1 as ADCO
ADMUX &= ~( ( 1 << MUX0 ) | ( 1 << MUX1 ) | ( 1 << MUX2 ) | ( 1 << MUX3
ADMUX |= (1 << REFS0); // Set ADC reference to AVCC
ADMUX |= (1 << ADLAR); // Left adjust ADC result to allow easy 8 bit reading

//set pin1 as ADC7
ADMUX &= ~( ( 1 << MUX0 ) | ( 1 << MUX1 ) | ( 1 << MUX2 ) | ( 1 << MUX3
ADCSRA |= (1 << ADEN); // Enable ADC
ADCSRA |= (1 << ADSC); // Start A2D Conversions

for(;;) // Loop Forever
{
lcd_gotoxy(0,0); // Display ADC0
lcd_puts("ADC0: ");
num = adc0;
itoa(num , buffer, 10);
lcd_puts(buffer);
lcd_puts(" ");

lcd_gotoxy(0,1); // Display ADC7
lcd_puts("ADC7: ");
num = adc7;
itoa(num , buffer, 10);
lcd_puts(buffer);
lcd_puts(" ");
}
}

ISR(ADC_vect)
{
if (adcport==0)
{
if (ADCH==0)
adc0 = ADCL;
else adc0 = ADCH * 256;
adcport = 1;
ADMUX |= (1<<MUX0); // Switch to ADC0
}
if (adcport==1)
{
if (ADCH==0)
adc1 = ADCL;
else adc1 = ADCH * 256;
adcport = 0;
ADMUX &= ~(1<<MUX0); // Switch to ADC7
}
}

}

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

Dear agop by double posting your request all you are doing is to annoy people!! And less likely to recieve help.

John Samperi

Ampertronics Pty. Ltd.

https://www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

I am sorry.I have recently joined this site and I wasnt aware of it.it will not happen again.regarding the code i posted above,i badly need help.could anyone guide me?

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

Thanks for your Outstanding Tutorial
I expect a Tutorial on PWM with ATMEGA16

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

guito2005 wrote:
I used the tutorial in a Mega16A, off course i had to do a little modification, and i have the ADC working, but... I don´t understand why!

here is the code:


#define F_CPU 1000000UL  //1MHz

#include 

//Ports

#define LED1 PA0
#define LED2 PA1
#define POT ADC2

int main(void){
   
   DDRA|=_BV(LED1)|_BV(LED2);  //LEds input,

//ADC CONF

   
   //1MHz/8=125kHz   limit Prescaler [50 200] kHz

   ADCSRA|=_BV(ADPS1)|_BV(ADPS0);   //Define prescaler  8=(0b011)
   ADMUX|=_BV(MUX1);                //input ADC2
   ADMUX|=_BV(REFS0);               //Ref AVCC

   
   //Free-running mode 000
   ADCSRA|=_BV(ADATE); //set Auto Triguer

   ADMUX|=_BV(ADLAR); //use only ADCH

   ADCSRA|=_BV(ADEN); //ADC on

   ADCSRA|=_BV(ADSC);  //start ADC

//end ACD CONF
   
   
   while(1){
      if(ADCH<128){  // Led1 on
         PORTA|=_BV(LED2);
         PORTA&=~_BV(LED1);
      }
      else{           //led2 on
         PORTA|=_BV(LED1);
         PORTA&=~_BV(LED2);
      }

      ADCSRA|=_BV(ADIF); // if i remove this line it stops working
   }
   return (0);
}

in the datasheet they say that the Interrupt flag is set when the conversion ends, so why do i have to set it manually?

the code is correct?

Thanks for your help


This is because there is no ADFR bit in ADCSRA of MEGA16 to set it into free-running mode .
Try to Start ADC in A loop so that it it starts another conversion after finishing one
Like the one i did...

#include
#include
#include
int main(void)
{
DDRB=0XFF;    //To set all pins of port b as outputs
ADCSRA|=(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);//prescaling
ADMUX|=(1<<ADLAR);
ADMUX|=(1<<MUX1);
ADCSRA=(1<<ADEN);
ADCSRA|=(1<<ADIE);
sei();
for(;;)
{
ADCSRA|=(1<<ADSC);
_delay_ms(400);                //must required as adc needs 13 or 25 cpu cycles to convert

}
}
ISR(ADC_vect)
{
if(ADCH<123)
{
PORTB=0XF0;
}
else if(ADCH>130)
{
PORTB=0X0F;
}
}
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

guito2005 wrote:
I used the tutorial in a Mega16A, off course i had to do a little modification, and i have the ADC working, but... I don´t understand why!

here is the code:


#define F_CPU 1000000UL  //1MHz

#include 

//Ports

#define LED1 PA0
#define LED2 PA1
#define POT ADC2

int main(void){
   
   DDRA|=_BV(LED1)|_BV(LED2);  //LEds input,

//ADC CONF

   
   //1MHz/8=125kHz   limit Prescaler [50 200] kHz

   ADCSRA|=_BV(ADPS1)|_BV(ADPS0);   //Define prescaler  8=(0b011)
   ADMUX|=_BV(MUX1);                //input ADC2
   ADMUX|=_BV(REFS0);               //Ref AVCC

   
   //Free-running mode 000
   ADCSRA|=_BV(ADATE); //set Auto Triguer

   ADMUX|=_BV(ADLAR); //use only ADCH

   ADCSRA|=_BV(ADEN); //ADC on

   ADCSRA|=_BV(ADSC);  //start ADC

//end ACD CONF
   
   
   while(1){
      if(ADCH<128){  // Led1 on
         PORTA|=_BV(LED2);
         PORTA&=~_BV(LED1);
      }
      else{           //led2 on
         PORTA|=_BV(LED1);
         PORTA&=~_BV(LED2);
      }

      ADCSRA|=_BV(ADIF); // if i remove this line it stops working
   }
   return (0);
}

in the datasheet they say that the Interrupt flag is set when the conversion ends, so why do i have to set it manually?

the code is correct?

Thanks for your help

This is because there is no ADFR bit in ADCSRA of MEGA16 to set it into free-running mode .

Try to Start ADC in A loop so that it it starts another conversion after finishing one

Like the one i did...

#include
#include
#include
int main(void)
{
DDRB=0XFF;    //To set all pins of port b as outputs
ADCSRA|=(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);//prescaling
ADMUX|=(1<<ADLAR);
ADMUX|=(1<<MUX1);
ADCSRA=(1<<ADEN);
ADCSRA|=(1<<ADIE);
sei();
for(;;)
{
ADCSRA|=(1<<ADSC);
_delay_ms(400);                //must required as adc needs 13 or 25 cpu cycles to convert

}
}
ISR(ADC_vect)
{
if(ADCH<123)
{
PORTB=0XF0;
}
else if(ADCH>130)
{
PORTB=0X0F;
}
}

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

Quote:

I expect a Tutorial on PWM

https://www.avrfreaks.net/index.p...
Quote:

with ATMEGA16

Although not identical, different timers on "modern" AVRs are fairly similar, patterns will re-occur through the different timer implementations. Even when narrowing down to the ATmega16 that specific AVR model has several timers that differ (eg in resolution, there are both 8-bit and 16-bit timers).

Start by reading Deans tutorial. If you have problems understanding that, then it is not unlikely that you need to read up on his "general" timer tutorial. Then proceed with a simple PWM mode on one of the ATmega16 timers. Use it to eg vary the brightness of a LED. Then move in small learning steps toward what you ultimately need (you say nothing about your ultimate goal). When you get into obstacles or troubles, search for previous threads here at AVRfreaks, and if not successful with that, ask a question. Be prepared to be pointed to the data sheet for specific details.

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

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

Thank u Tilak, the code finally worked by your method. I just started with AVRs and I was having problem trying to make the code in this tutorial work in my ATMega32. This free running mode is mentioned in the datasheet but i didn't understand what to do. Isn't there any other way avoid using any code in that for(;;) loop. I mean from hardware itself like in Mega128 in this tutorial?

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

This is a pdf conversion of this for anyone to download. If his violates any rule, sorry, and please delete my post if necessary. Also, great tutorial :)

Attachment(s): 

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

Hi, it's my first post in the forum, and in a word, it's a perfect newbie tut.

tnxs .

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

Quote:

This is a pdf conversion of this for anyone to download. If his violates any rule [...]

Rules, or no rules - when the original tutorial gets updated this PDF will be out-dated. Who will promise now to take care of that then?

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

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

Hi everyone.
I red a discussion you had posted here about the sampling frequency, I also red the ADC section in the data sheet, but it isn't very clear to me how to calculate the sampling frequency.

I'm working with some audio signal (1kHz-15kHz), I would like to use the ADC in the Mega32, so according to the data sheet, the ADC clock source requires a frequency between 50kHz and 200 kHz to get maximum resolution, and it takes 13.5 conversion cycles for auto triggered conversion.

So if i have the internal clock at 8Mhz, program the prescaler CK/8:

8Mhz/8 = 1Mhz ; 1Mhz/13.5 = 74.074 KHz

And 74 kHz complies with the requirement for maximum resolution, but it also complies with the Nyquist Theorem for my signal Fs >> 2*Fmax = 30 kHz.

I have tested this with a sinusoidal signal, but when the signal is reconstructed with a DAC, seems like the sampling frequency is incorrect because at 10kHz the signal is barely reconstructed, and with 15kHz the signal is not reconstructed.

Does anyone knows why?? :roll:
Is it noise, or the Sampling Frequency, or my conversion is incorrect???

Attachment(s): 

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

Quote:
So if i have the internal clock at 8Mhz, program the prescaler CK/8:

8Mhz/8 = 1Mhz ; 1Mhz/13.5 = 74.074 KHz

And 74 kHz complies with the requirement for maximum resolution


No, the 50kHz to 200kHz refers to the ADC clock frequency, not the sample rate. At 8MHz you need at least a /64 prescaler to get within the range needed. Also, the sample takes 13 clocks, not 13.5.

If you don't need 10 bit resolution, then you could get away with a 32 or even 16 prescaler. The 16 prescaler would give you ~38.5kHz sample rate.

Regards,
Steve A.

The Board helps those that help themselves.

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

Just to note that you might actually want to DROP your F_CPU to get a higher sample rate. At 8MHz to get a 50kHz..200kHz ADC clock you have to use /64 which actually gets you 125kHz ADC clock but if you drop to 6.4MHz then you could use /32 to get the fastest ADC clock of 200kHz

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

https://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&p=644489#644489

I was having only 0xFF output from ADCH, hope someone finds this helpful.

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

An absolutely brilliant tutorial! Thanks alot for making life easier for all of us!

Here is the code for Atmega8: connect long pin (+) of LED1 to Pin 14 (=PB0) and the short pin (-) to a 300 ohm resistance, which goes to ground. Do the same for LED2 but here, the long pin goes to pin 28. Then you can cut and paste the following code.

#include 

int main (void)
{
   DDRC |= (1 << 4); // Set LED1 as output
   DDRB |= (1 << 1); // Set LED2 as output

   ADCSRA |= (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0); // Set ADC prescalar to 128 - 125KHz sample rate @ 16MHz

   ADMUX |= (1 << REFS0); // Set ADC reference to AVCC
   ADMUX |= (1 << ADLAR); // Left adjust ADC result to allow easy 8 bit reading

   // No MUX values needed to be changed to use ADC0

   ADCSRA |= (1 << ADFR);  // Set ADC to Free-Running Mode
   ADCSRA |= (1 << ADEN);  // Enable ADC
   ADCSRA |= (1 << ADSC);  // Start A2D Conversions

   for(;;)  // Loop Forever
   {
      if(ADCH < 128)
      {
         PORTC |= (1 << 4); // Turn on LED1
         PORTB &= ~(1 << 1); // Turn off LED2
      }

      else
      {
         PORTC &= ~(1 << 4); // Turn off LED1
         PORTB |= (1 << 1); // Turn on LED2
      }

   }

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

Many thanks for the excellent tutorial. I modified the bit settings for ADMUX using REFS0 and REFS1 == 0 to set the Vref to AVCC as I'm using an attiny85

Probably a newbie question, but...

Is it guaranteed that all status register bits are set to zero unless otherwise set with (1<<BitName)?

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

On power up reset, the values of the registers are guaranteed to be what the datasheet says they will be (in the case of some registers this is not zero).

Regards,
Steve A.

The Board helps those that help themselves.

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

Thanks Koshchi and Clawson.

I've been miscalculating the ADC frequency. but I think I got it all wrong still.
I'm still using the internal 8MHz frequency, and 500KHz to the ADC in free running mode:

500kHz/13 ~ 38kHz

So my sampling rate should be around this value, but when I use the "read_adc" command in Codevision looks like my frequency drops down, I dont know why it takes to long for the ADC to convert, because I use one pin of the chip as output and XOR'it every time the ADC finish the conversion. The frequency on this pin is around 18kHz,

#include 
#include 
#define ADC_VREF_TYPE 0x60

unsigned char read_adc(unsigned char adc_input)
{
   ADMUX=adc_input | (ADC_VREF_TYPE & 0xff);
   // Delay needed for the stabilization of the ADC input voltage
   delay_us(10);
   // Start the AD conversion
   ADCSRA|=0x40;
   // Wait for the AD conversion to complete
   while ((ADCSRA & 0x10)==0);
   ADCSRA|=0x10;
   return ADCH;
}

void main(void)
{
   #pragma optsize-
   CLKPR=0x80;
   CLKPR=0x00;
   #ifdef _OPTIMIZE_SIZE_
   #pragma optsize+
   #endif

   DDRB=0x00;
   DDRC=0x02;
   DDRD=0xFF;

   DIDR0=0x01;
   ADMUX=ADC_VREF_TYPE & 0xff;
   ADCSRA=0xA4;
   ADCSRB&=0xF8;

   while (1)
      {
        PORTC^=0x02;
        PORTD=read_adc(PORTC.0);
      };
}

If I dont call read_adc, the frequency on the pin is 600kHz.
Why is this frequency so low, even if I don use the ADC???, I already disable the CLKDIV8 fuse, It must but up to Mhz at least.

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

Hi,
Thanks for the excellent tutorial.

I compiled both programs using the Simulator in AVR studio (picking the atmega128 chip). The 1st example works fine, but the second example there is a problem as the interrupt only happens once.
As an experiment I put the line

ADCSRA |= (1 << ADSC);

in the interrupt section and the interrupt triggers continuously, but I assume I shouldn't be doing this in free running mode.
Any ideas as to why the interrupt only triggers once in the ADC interrupt example with the simulator?

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

Quote:
Any ideas as to why the interrupt only triggers once in the ADC interrupt example with the simulator?

Simulator user manual wrote:
General Simulator Issues
The issues in this section apply to all AVR devices.

Unsupported modules
Simulation of TWI, USI and analog peripheral is not yet implemented.

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

clawson wrote:
Quote:
Any ideas as to why the interrupt only triggers once in the ADC interrupt example with the simulator?

Simulator user manual wrote:
General Simulator Issues
The issues in this section apply to all AVR devices.

Unsupported modules
Simulation of TWI, USI and analog peripheral is not yet implemented.


Thanks :)

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

Thank you for the tutorial!

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

CAN SOME ONE ALSO HELP IN PROVIDING THE ASM CODE TOO...

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

Quote:

PROVIDING THE ASM CODE TOO

C compiler says:

000000c4 
: #include #include int main (void) { DDRE |= (1 << 2); // Set LED1 as output c4: 12 9a sbi 0x02, 2 ; 2 DDRG |= (1 << 0); // Set LED2 as output c6: 80 91 64 00 lds r24, 0x0064 ca: 81 60 ori r24, 0x01 ; 1 cc: 80 93 64 00 sts 0x0064, r24 ADCSRA |= (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0); // Set ADC prescaler to 128 - 125KHz sample rate @ 16MHz d0: 86 b1 in r24, 0x06 ; 6 d2: 87 60 ori r24, 0x07 ; 7 d4: 86 b9 out 0x06, r24 ; 6 ADMUX |= (1 << REFS0); // Set ADC reference to AVCC d6: 3e 9a sbi 0x07, 6 ; 7 ADMUX |= (1 << ADLAR); // Left adjust ADC result to allow easy 8 bit reading d8: 3d 9a sbi 0x07, 5 ; 7 // No MUX values needed to be changed to use ADC0 ADCSRA |= (1 << ADFR); // Set ADC to Free-Running Mode da: 35 9a sbi 0x06, 5 ; 6 ADCSRA |= (1 << ADEN); // Enable ADC dc: 37 9a sbi 0x06, 7 ; 6 ADCSRA |= (1 << ADIE); // Enable ADC Interrupt de: 33 9a sbi 0x06, 3 ; 6 sei(); // Enable Global Interrupts e0: 78 94 sei ADCSRA |= (1 << ADSC); // Start A2D Conversions e2: 36 9a sbi 0x06, 6 ; 6 e4: ff cf rjmp .-2 ; 0xe4 000000e6 <__vector_21>: { } } ISR(ADC_vect) { e6: 1f 92 push r1 e8: 0f 92 push r0 ea: 0f b6 in r0, 0x3f ; 63 ec: 0f 92 push r0 ee: 11 24 eor r1, r1 f0: 8f 93 push r24 if(ADCH < 128) f2: 2f 99 sbic 0x05, 7 ; 5 f4: 05 c0 rjmp .+10 ; 0x100 <__vector_21+0x1a> { PORTE |= (1 << 2); // Turn on LED1 f6: 1a 9a sbi 0x03, 2 ; 3 PORTG &= ~(1 << 0); // Turn off LED2 f8: 80 91 65 00 lds r24, 0x0065 fc: 8e 7f andi r24, 0xFE ; 254 fe: 04 c0 rjmp .+8 ; 0x108 <__vector_21+0x22> } else { PORTE &= ~(1 << 2); // Turn off LED1 100: 1a 98 cbi 0x03, 2 ; 3 PORTG |= (1 << 0); // Turn on LED2 102: 80 91 65 00 lds r24, 0x0065 106: 81 60 ori r24, 0x01 ; 1 108: 80 93 65 00 sts 0x0065, r24 } } 10c: 8f 91 pop r24 10e: 0f 90 pop r0 110: 0f be out 0x3f, r0 ; 63 112: 0f 90 pop r0 114: 1f 90 pop r1 116: 18 95 reti

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

Got 2 questions. :)
(note: im using attiny861)

1) If I prescale my clock from 8M to 125kHz, do I need to prescale the ADC aswell?

2) I need to take 10 samples with a delay between, lets say 100ms, every sample. How should I do this?

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

1) No, the only requirement is that F_CPU is divided by the ADC prescaler into the 50kHz to 200kHz range. If F_CPU is already in that range then the ADC prescale can be set to /1

2) Most usual solution would be a timer interrupt. Just set up a timer to interrupt every 100ms and when the interrupt occurs take the previous ADC reading and set ADSC again to start another. If the ADC is clocked at 125kHz and it takes 13 ADC clocks to make a conversion then the next reading will be ready after just 13/125000 seconds ~= 0.1ms so it'll be ready LONG before the next interrupt occurs. I guess one thing you could do is use the 100ms to make quite a few readings and then take the average at the 100ms point.

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

Quote:
If I prescale my clock from 8M to 125kHz, do I need to prescale the ADC aswell?

That depends on what frequency you want to clock the ADC at. But I suppose you already understood that and you're actually asking "what frequency should I clock the ADC with?".

One clue lies in
with a delay between, lets say 100ms
Going from memory, the ADC needs 13 clocks for a conversion. 100ms is 10 Hz, so it would be nice if the ADC was clocked with at least 130 Hz. And that is well below 125 KHz so that's no problem.

Other comments:
I'd take a look in the data sheet for the limitations and requirements on the ADC clock frequency. Have you?

What does your input signal look like? It to might influence the selection of ADC frequency. If it's at a steady level at time spans like a full conversion, then no problem there. But if it is a signal that varies with a certain frequency w.r.t. the ADC sampling frequency you might get "interesting" results. The people here that live in the time domain, and worship Nyquist every morning, can tell you more.

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

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

Quote:

if the ADC was clocked with at least 130 Hz

But the datasheet says it should be 50kHz minimum? (I presume the charge on the sample/hold capacitor will not remain long enough for slower ADC conversions?)

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

clawson wrote:
2)I guess one thing you could do is use the 100ms to make quite a few readings and then take the average at the 100ms point.

Thats the plan. :)
Thank you for the help.

JohanEkdahl wrote:

What does your input signal look like? It to might influence the selection of ADC frequency. If it's at a steady level at time spans like a full conversion, then no problem there.

Im hoping steady but the sensor is new and uniqe so we are not so sure of the result. :/

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

Quote:
2)I guess one thing you could do is use the 100ms to make quite a few readings and then take the average at the 100ms point.

But if the ADC Clock rate is 62.5kHz (the only choice available with a 125kHz system clock), that would mean close to 500 readings every 100ms. Not very practical.
Quote:
Most usual solution would be a timer interrupt. Just set up a timer to interrupt every 100ms and when the interrupt occurs take the previous ADC reading and set ADSC again to start another.

A much better idea, especially since the ADC can be auto-triggered by timer 0 or timer 1.

Regards,
Steve A.

The Board helps those that help themselves.

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

Quote:

But the datasheet says it should be 50kHz minimum?

Which in my world is "at least 130 Hz" :wink:, and
I wrote:

I'd take a look in the data sheet for the limitations and requirements on the ADC clock frequency. Have you?

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

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

Thanks for the tutorial. I'm having a little problem: I made thermometer based on Atmega8 with TC1047(Precision Temperature-to-Voltage Converter) so this tutorial was very helpful, but when Atmega8 ran out of memory(full program takes over 13k) I decide move to Atmega16. And there is problem: I don't know how to set free-runing mode ADC, there is no ADFR bit in ADCSRA to set free-running mode.

Structure of code:

int main(void)
{
ADCSRA|=(1<<ADPS2)|(1<<ADPS1)|(0<<ADPS0);//prescaling of 64. Internal 8MHz
//No MUX values needed to be changed to use ADC0
ADMUX|=(0<<ADLAR); //for all 10bit
ADCSRA|=(1<<ADATE); //set Auto Triguer
ADMUX|=(1<<REFS1)|(1<<REFS0); //Internal 2,56V
ADCSRA=(1<<ADEN); //Enable ADC
ADCSRA|=(1<<ADSC); //Start ADC

while(1)
{
ADC_L = ADCL;
ADC_H = ADCH;
kodas = ( ADC_H<<8 ) | ADC_L;
....
....
}
ADCSRA|=_BV(ADIF);
}

Thanks for advises

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

Set the ADTSx bits in SFIOR to the mode that you want, then set the ADATE bit in ADCSRA.

Regards,
Steve A.

The Board helps those that help themselves.

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

I found the problem :) Code is working in free-runing mode. Mistake was wrong pin :D :D :D

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

Hi this is my code

#include  
#include  
#define F_CPU 8000000UL
#include 

int main(void)

{

void pwm() {

CLKPR	=	0x80;		//enabling CPU CLOCK scaling : clk_io
CLKPR	=	0x00;		//setting CPU clock's prescaler to 1	


//Phase Correct PWMMODE 5 utilizing 0CR0A as TOP storage register. 
//This therefore inhibits the usage of OC0A pin; 
//We will use OC0B as the output compare action pin. 

//This requires PB1(OC0B) to be setup as an output pin:

PORTB	=	0x00;
DDRB	=	0x02;	//setting PB1 to O/P mode and rest I/P?

//SETUP OF TCCR0A:

TCCR0A	=	(1<<WGM00)|(0<<WGM01)|(0<<COM0B0)|(1<<COM0B1)
			|(0<<COM0A0)|(0<<COM0A1); //{PC_PWM Mode 5 ; OC0B = noninvert OC0A = off}

TCCR0B	=	(1<<CS00)|(0<<CS01)|(1<<CS02)
			|(1<<WGM02)|(0<<FOC0B)|(0<<FOC0A); //{No prescaling; PC_PWM = mode5; Force comp = off}

//STARTING TIMER:
TCNT0		=	0;

//DEFINING TOP & D VALUEs:

OCR0A		=	255; // TOP = FF
OCR0B		=	128; // D = .5
}

// Set Port B pins for 3 and 4 as outputs 
    DDRB = ( 1 << DDB3 );
    
    //set the reference voltage for the ATTINY45 ADC to be VCC 
    ADMUX |=  ((0 << REFS0) | (0 << REFS1)); 
    
    //set pin #1 as ADC0 
    ADMUX |=  ( ( 0 << MUX0 ) | ( 0 << MUX1 ) | ( 0 << MUX2 ) | ( 0 << MUX3 ) ); 
    
    //left allign the adc value 
    ADMUX |= (1 << ADLAR); 
    
    //set the division factor to 128 (see the datasheet) 
    ADCSRA |= (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0); 
    
    //auto triger enable 
    ADCSRA |= (1 << ADATE); 

    //enable the ADC 
    ADCSRA |= (1 << ADEN); 
    
    //start the adc measurments 
    ADCSRA |= (1 << ADSC); 
    
    for ( ; 1==1 ; ) { 
        
        if(ADCH < 128) { 
            PORTB = 0x08; 
        } else { 
            pwm();
        } 
        
        //optional 
        //PORTB = 0x00;
        
    } 
    
    return 1; 





}


I am trying to use vcc as vref, adc input upto 5 volts, if voltage less than 2.1 nothing happens, when higher than that it should turn on led at portb3 when volt higher than 2.5 then it should turn on the pwm output. I am inputting the pwm into n-channel mosfet to control another led.

The problem is when i power on at 2.1volt led turns on nicely but i keep going say up until 5 volts the mosfet led doesn't turn on ,then i switch back to 2.5 volts or something like that then the mosfet led nicely turns on and off like it supposed to(blinks as 50% duty cycle),but then when i drop to 2.1 volts the other led does not turn on, but the mosfet led turns off, then when i pull the volt again to 2.2 led turns on at high volt the mosfet doesn't work, have to go down then only it works!!

Why?

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

Why are you defining pwm() inside of main()?

Why are you changing the clock pre-scaler inside of pwm()?

Why are you changing DDRB in pwm() (and to something different than what you set it to in main())?

If you want TOP to be 255, then why not use mode 1?

But then, why not use CTC mode for a 50% duty cycle?

When do you ever turn PWM off after it has been turned on?

#include  

Do not include this file. The AVR is specified in the makefile or project options.

Quote:
then the mosfet led nicely turns on and off like it supposed to(blinks as 50% duty cycle)
You can see the LED blink at 30Hz?

Regards,
Steve A.

The Board helps those that help themselves.

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

Koshchi wrote:
Why are you defining pwm() inside of main()?

Why are you changing the clock pre-scaler inside of pwm()?

Why are you changing DDRB in pwm() (and to something different than what you set it to in main())?

If you want TOP to be 255, then why not use mode 1?

But then, why not use CTC mode for a 50% duty cycle?

When do you ever turn PWM off after it has been turned on?

#include  

Do not include this file. The AVR is specified in the makefile or project options.

Quote:
then the mosfet led nicely turns on and off like it supposed to(blinks as 50% duty cycle)
You can see the LED blink at 30Hz?

I changed the DDRB in pwm cause i want pwm out of PB1.
I am changing the prescaler inside pwm to control the pwm.

I am defining my duty cycle cause later i will vary the duty cycle depending on the ADC input.

thnx for the reply, i am trying to fix all that you said, but i think i am getting a 15Hz pwm frequency and the mosfet is switching the led and its blinking.

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

Quote:
I changed the DDRB in pwm cause i want pwm out of PB1.
But you also want output to PB3. You don't want to keep switching between input and output for these pins. If you want PB1 off when the PWM is enabled, then turn it off using PORTB, don't change it to an input. The same goes for PWM, don't change it to input, disable PWM. (By the way, 0x02 is PB2, not PB1).
Quote:
I am changing the prescaler inside pwm to control the pwm.
But why change it there? Either the rest of the app doesn't care, or worse, it does care and you are changing the pre-scaler without warning. Either way, the best place to do this is at the beginning of main. And since you have:

#define F_CPU 8000000UL

You are telling the compiler that you are running at a specific speed. Any code relying on this will have no clue that you change the speed at a random time during runtime.

Regards,
Steve A.

The Board helps those that help themselves.

Pages

Topic locked