ADC's on ATTINY85

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

I tried reading through the ADC setup tutorial, but I wasn't able to make it work, at least not the way I want it to. I am using the ATTINY85.

#include      

int     set_PORTB_bit(int position, int value);

int main(void)
{
int trig;
  // Set Port Bit 0 as output, 4 as input 
  DDRB = 0b00000001;

  // i.e. loop while '1' equals '1'
  for ( ; 1==1 ; )
  {
 	if(ADC

This is what I have come up with so far, what I need to do is read voltage levels from inputs, which all my PB's except PB0 are set to inputs. I want to be able to read different voltage levels from different inputs and set a value, such as trig to 1 or 0 based on an input reaching a certain voltage level. I have photo diodes that are .8v active high, a hall affect sensor that's 4.5v active high, and a push button that's 5v active high. I don't understand how to set up voltage level detection for the I/O.

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

Well, you have a long mile to go.

1> configure your ADC. I don't see your ADC configuration.
2> In which mode do you want to use the ADC?
3> Then your code, assuming that works, is doing some sort of IO handling and setting some output out. This is completely different functionally that what you are trying to accomplish... maybe you copy the wrong code, or something??!?!

By the way, you can change the for sentence with something like this:

for (;;)
{
    /* you code goes here*/
}

It is just matter of style because for(;1==1;).. look really weird.

---
ARod

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

int main(void)
{
	DDRB |= (1 << 0) | (0 << 4);	//0 = input, 1 = output, PB0 is output, PB4 is input

  	ADCSRA |= (1 << ADPS2) | (1 << ADPS1) | (0 << ADPS0);		//ADC Prescalar set to 64 - 125kHz@8MHz

 	ADMUX |= (0 << REFS0) | (0 << REFS1);		//Sets ref. voltage to VCC +5v
  	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 << ADEN);  // Enable ADC 
   	ADCSRA |= (1 << ADSC);  // Start A2D Conversions  	
	ADCSRA |= (1 << ADFR);	//Sets ADC to Free Running Mode

  for (;;)	//Loop forever
  {
  if(ADCH < 128) 
      { 
         PORTB |= (1 << 0); // Turn on PB4
      } 

      else 
      { 
         PORTB &= ~(0 << 0); // Turn off PB4
	  } 
  }
}

This is what I have so far, however I get errors, ../FCM1.c:16: error: 'ADFR' undeclared (first use in this function)
../FCM1.c:16: error: (Each undeclared identifier is reported only once
../FCM1.c:16: error: for each function it appears in.)
make: *** [FCM1.o] Error 1
Build failed with 3 errors and 0 warnings...

I don't understand why. Any ideas?

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

If you look at the datasheet, you will find that ADCSRA has no bit named "ADFR," hence your error. To get into the free-running mode, you must first set the ADATE bit in ADCSRA and the ADTS bits in ADCSRB.

I suggest going through the datasheet, giving extra attention to the register descriptions if you want to figure things out.

-Amrit

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

ADATE I already set right after posting, I realized it wasn't in the register. Now what I don't understand is what ADTS has to do with what I am trying to do. ADTS reads only an analog input signal from what I understand on the data sheet. I am using PB0(AIN0) as an output as its a configurable I/O. What I want to do is use PB2,3,4 and 5 to read analog signal inputs and use them to turn a bit on an off, so lets say PB2<2.6v then a = 1 and if PB2>2.4 then a = 0. I don't understand how to set up the the 4 ADC's to do that.

#include      

int main(void)
{
	DDRB |= (1 << 0) | (0 << 4);	//0 = input, 1 = output, PB0 is output, PB4 is input

  	ADCSRA |= (1 << ADPS2) | (1 << ADPS1) | (0 << ADPS0);		//ADC Prescalar set to 64 - 125kHz@8MHz

 	ADMUX |= (0 << REFS0) | (0 << REFS1);		//Sets ref. voltage to VCC +5v
  	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 << ADEN);  // Enable ADC 
   	ADCSRA |= (1 << ADSC);  // Start A2D Conversions  	
	ADCSRA |= (1 << ADATE);	// Enable ADC Auto Trigger Mode

  for (;;)	//Loop forever
  {
  if(ADCH < 128) 
      { 
         PORTB |= (1 << 0); // Turn on PB4
      } 

      else 
      { 
         PORTB &= ~(0 << 0); // Turn off PB4
	  } 
  }
}

In the if statement, where it says ADCH, can I not select a single input to read the analog voltage from? I only have one output, but I have 4 inputs all of which are analog voltage signals. They are all contributing to states that must occur for my one output to be active.

Quote:

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

You have to do that one by one. So you want to set the ADC up for the first port PB1, get a conversion, store the value, set PB2, store the value and so on. You cannot use all the ADC's at once since your ADCH and ADCL is only limited to one conversion at a time.

-Amrit

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

Ok, that's what I was starting to realize. Right now, what I need to do is channel selection then? For example, PB4 is a certain channel, so in order to read the value from it, I need to switch to that channel, then when I need to read from PB2 for example I switch to that channel? Is that how I would accomplish this?

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

Yes. You want to read the ADC part on the datasheet to know your limits on channel-switching and stuff...

-Amrit

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

int main(void)
{
	DDRB |= (1 << 0) | (0 << 4);	//0 = input, 1 = output, PB0 is output, PB4 is input

  	ADCSRA |= (1 << ADPS2) | (1 << ADPS1) | (0 << ADPS0);		//ADC Prescalar set to 64 - 125kHz@8MHz

 	ADMUX |= (0 << REFS0) | (0 << REFS1);		//Sets ref. voltage to VCC +5v
  	ADMUX |= (1 << ADLAR); 		// Left adjust ADC result to allow easy 8 bit reading 
	ADMUX |= (0 << MUX3) | (1 << MUX2) | (0 << MUX1) | (0 << MUX0);	//Selects channel ADC2 (PB4) with 1x Gain

   	ADCSRA |= (1 << ADEN);  // Enable ADC 
   	ADCSRA |= (1 << ADSC);  // Start A2D Conversions  	
	ADCSRA |= (1 << ADATE);	// Enable ADC Auto Trigger Mode

  for (;;)	//Loop forever
  {
  if(ADCH < 128) 
      { 
         PORTB |= (1 << 0); // Turn on PB0
      } 

      else 
      { 
         PORTB &= ~(0 << 0); // Turn off PB0
	  } 
  }
}

Alright, here is where I'm stuck, I have everything set, however there are two things I'm unsure of, I don't quite understand the table for the channel select, it has Single Ended, Positive Differential Input, Negative Differential Input, and of course Gain. Now if I'm using free running mode, and I want to select PB4, obviously I wouldn't use single ended as that's one ADC scan. But I'm not quite sure how their Positive and Negative Diff Inp's work or are set up. I only see PB4 listed under the negative column, so mux 0100 would select PB4 with 1x gain, but why is there only two PB's for the positive? I only want to use my 5V push button to be read by PB4.

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

A single ended conversion is when only one value is being compared to ground. A differential is when you are measuring between two points, neither of which is ground. +ive and -ive refers to how the ADC can handle negative differences. Thus, Diff ADC requires two pins and single ended require 1 pin.

If the pin is going to be 5V or 0V with nothing inbetween, it would be easier just to read that pin value with something like this:

if ( PINB & ( 1 << PB4)== 0 ) set value

Using ADC to measure an output high or output low is a waste of resource. Also, I have found that you do not want to use a 5 V reference on the ADC as it leads to highly inaccurate results. The 2.56V or 1.1V references are much better.

-Amrit

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

Ok, that makes sense, perhaps I've been reading through tis so long that I'm just confusing my self. But what I am going to use the ADC's for is to read input signals which are not at 5v, one is .8v high, one is 3.7v high and the last is plain 5v high. That's why I'm trying to use the ADC to measure it. Is it possible to read lets say 3.7v logic high using single ended inputs? Essentially I want to set a voltage trip point and have store a high or low bit in lets say a, so if PB4>3.7v then a = 1 else a = 0. Would I then just write the following:

if(ADCH < (whatever value corresponds to 3.7v))) 
      { 
         a = 1
      } 

      else 
      { 
         a = 0
      }

Then I would set the MUX to 0010 which is single ended PB4.

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

Yes, that is how you want to do it.

-Amrit

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

For simplicities sake I am trying to turn the light directly on and off with the following, I just want to get the inputs working first:

#include      

int main(void)
{
	DDRB |= (1 << 0) | (0 << 4);	//0 = input, 1 = output, PB0 is output, PB4 is input

  	ADCSRA |= (1 << ADPS2) | (1 << ADPS1) | (0 << ADPS0);		//ADC Prescalar set to 64 - 125kHz@8MHz

 	ADMUX |= (1 << REFS2) | (1 << REFS1) | (0 << REFS0);		//Sets ref. voltage to 2.56v internal reference
  	ADMUX |= (1 << ADLAR); 		// Left adjust ADC result to allow easy 8 bit reading 
	ADMUX |= (0 << MUX3) | (0 << MUX2) | (1 << MUX1) | (0 << MUX0);	//Selects channel ADC2 (PB4)

   	ADCSRA |= (1 << ADEN);  // Enable ADC 
	ADCSRA |= (1 << ADATE);	// Enable ADC Auto Trigger Mode

  for (;;)	//Loop forever
  {
  if(ADCH < 64) 
      { 
         PORTB |= (0 << 0); // Turn off PB0
      } 

      else 
      { 
         PORTB &= ~(1 << 0); // Turn on PB0
	  } 
  }
}

However it does not work, I know PB4 selection is correct, however I am a bit confused about selection between single ended and free running, I can't seem to find anything in the data sheet about enabling single ended or free running as the Atmega128 example had. I was thinking that it was done through MUX, because the table lists single and free running modes seperately, that by using 0010, listed as single ended PB4 it would be single ended. I have my ADCH <64, so it should easily read a 5V input from my PB.

Last Edited: Tue. Mar 3, 2009 - 01:53 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You haven't started any conversions. Rather than doing free running, I would suggest you turn on the ADC and then in the while loop have

while (1)
{
ADCSRA |= (1 <<ADSC);
while (ADCSRA & (1 << ADSC) ); // wait till conversion complete
if ( ADCH...) ....
}

-Amrit

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
 for (;;)	//Loop forever
  while (1) 
  { 
  ADCSRA |= (1 <<ADSC); 
  while (ADCSRA & (1 << ADSC) );
  if(ADCH < 64) 
      { 
         PORTB |= (0 << 0); // Turn off PB0
      } 

      else 
      { 
         PORTB &= ~(1 << 0); // Turn on PB0
	  } 
  }
}

That is what I added, still nothing, I know I'm getting 5V at the pin because I measured it with my meter, but still not turning on the LED :(. This is very confusing to me, I'm studying to be an EE, so dealing with software is not my forte, however I need to in this situation, so I'm learning C as I go. Thanks for your patience. Also if you have IM my screen name is S8 The Thing, it would a little easier to talk in real time.

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

For a start I can say that even when you are studying to be an Electrical Engineer software is getting more and more important. I whish that during my school years I had a little more software practice at school.

I myself am now learning the do it your self way and that costs a lot more time then when you have a teacher to now and then have a good discussion with.

having said that....
You are never turning the LED on.

PORTB |= (0 << 0); // Turn off PB0 

does not change portb at all
you OR the portB value with 0. that means that no change is made

PORTB &= ~(1 << 0); // Turn on PB0 

this makes PortB pin0 low

so the only thing you ever do is make it low and never make it high again correct ??

note that (0<<x) does absolutely nothing. shifting a 0 still gives a 0. The only thing you can shift is a 1

re-read your code and keep that in mind. I have the suspicious feeling that you think you clear a bit at some place, but actualy dont. and thus that the chip is doing what you tolled it to, but that is not what you would like it to do.

x |= (1<<b)

makes bit b in register x high

x &= ~(1<<b)

makes bit b in register x low

I suggest to read the following tutorial:
https://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=37871

it will be very helpfull

regards

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

Ok, that makes sense, I misunderstood what it meant.

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

I do have a question about voltage trip points I want to set, since I am using internal Vref = 2.56v, then wouldn't 2.56v/256 = 10mV resolution in 8-Bit mode? So if I am using (ADCH > 128), then that is equal to (ADCH > 1.28v). So if my prescalar is set to 64, can I use a value x in (ADCH > x) which is greater than 64? I'm not sure which is the limiting factor, my prescalar or the Vref(meaning if Vref is 2.56v then I cannot read signals greater than 2.56v)>

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

The pre-scalar you are talking about I am assuming is for the ADC clock. That just slows down the conversion rate. It shouldn't effect the outcome of the ADC conversion.

-Amrit