Defective ADC operation on Attiny861 or software issue !!!

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

I want to make a software using Atmel Studio 7 (C++) that will lead to the activation of PB01, PB1 and PB2 pins in 1 logic (5V) when the ADC3 input is varied in the range 0V ... 1.1V.  Also, I want pins PB4, PB5 and PB6 to be activated based on the voltage applied to ADC4 all in the 0V ... 1.1V line. 

Of course, to do this I will use Vref = 1.1V and calibrate the internal oscillator on 8 MHz.

So, I made the following C++ code:

#define F_CPU 8000000UL // ATtiny861A la 8 MHZ

#include <avr/io.h>
#include <util/delay.h>

void adc_init(void);
uint8_t adc_read(uint8_t ch);

uint8_t m, n, ch, th0, th1, th2;

void adc_init()
{
	ADMUX|=(1<<REFS1)	//VREF=1.1V
	| (1<<ADLAR);		//Move the result to the left (the most significant 8 bits in ADHC)
	ADCSRA|=(1<<ADPS2)|(1<<ADPS1) //Prescaler: F_CPU/64  == 125kHz
	| (1<<ADEN);		//Enable ADC
}

uint8_t adc_read(uint8_t ch)
{
	ADMUX &= 0xE0; //Delete the old ADC channel
	ADMUX |= ch; //Sets the new ADC channel
	ADCSRA|=(1<<ADSC); //Starts conversion
	while(!(ADCSRA&(1<<ADIF))); //Awaiting the end of the conversion
	return ADCH; //Returns the result (8-bit)
}

int main(void)
{
	DDRB|=(1<<DDB0)|(1<<DDB1)|(1<<DDB2)|(1<<DDB4)|(1<<DDB5)|(1<<DDB6); // Set the PB0, PB1, PB2, PB4, PB5, PB6 pins as outputs
	
	adc_init(); // ADC Init
	
	ch = 3;  // ADC conversion set it to start from channel 3
	th0 = 65; th1 = 105; th2 = 150; // We define the value of comparison constants
	
	do  {
		for (ch=3;ch<=4;ch++)
		
		if(ch == 3)
			{
				m = 0; 
				m = adc_read(ch);
				
				if (th0<m && th1>=m) 
				{
					PORTB|=(1<<PB1);				// Turn ON PORTB pin 1 = 5V
					PORTB&=~(1<<PB0);				//Turn OFF PORTB pin 0 & 2
					PORTB&=~(1<<PB2);
				}
				else
				if (th1<m && th2>=m) 
				{
					PORTB|=(1<<PB2);				// Turn ON PORTB pin 2 = 5V
					PORTB&=~(1<<PB1);				//Turn OFF PORTB pin 1 & 0
					PORTB&=~(1<<PB0);
				}
				else  
				{
					PORTB|=(1<<PB0);				// Turn ON PORTB pin 0 = 5V
					PORTB&=~(1<<PB2);				//Turn OFF PORTB pin 1 & 2
					PORTB&=~(1<<PB1);
				}
			}
			
		if(ch == 4)
			{
				n = 0; 
				n = adc_read(ch);
				
				if (th0<n && th1>=n) 
				{
					PORTB|=(1<<PB5);				// Turn ON PORTB pin 5 = 5V
					PORTB&=~(1<<PB4);				//Turn OFF PORTB pin 4 & 6
					PORTB&=~(1<<PB6);
				}
				else
				if (th1<n && th2>=n) 
				{
					PORTB|=(1<<PB6);				// Turn ON PORTB pin 6 = 5V
					PORTB&=~(1<<PB4);				//Turn OFF PORTB pin 4 & 5
					PORTB&=~(1<<PB5);
				}
				else  
				{
					PORTB|=(1<<PB4);				// Turn ON PORTB pin 4 = 5V
					PORTB&=~(1<<PB5);				//Turn OFF PORTB pin 5 & 6
					PORTB&=~(1<<PB6);
				}
			}	
			
			_delay_ms(100);
		
	} while(1);
}

I have tested the code above and I have one issue. Normally only PB0, PB1 and PB2 outputs should be activated when I modify the voltage on ADC3 input but, in my case, the PB4, PB5 and PB6 outputs also work (which is not exactly right).

So, if the ADC4 = 0V and I vary the voltage applied to ADC3 (PA4), it will activate according to the program both if's  where ch = 3 and ch = 4, although ADC4 = 0V.  If I set ADC3 = 0V and vary ADC4, nothing happens. 

Why when I vary the voltage on ADC3 all the outputs are activated? And why when I vary the voltage at the input of ADC4 nothing happens? Are ADC settings wrong or software structure?

Some advice, please!

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

You don't reset the ADIF bit. It is easier just to poll the ADSC bit for completion and not concern yourself with the ADIF bit.

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

Thanks for the answer, but I did not fully understand your words, can you show me how to change the code ???

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
while(!(ADCSRA&(1<<ADIF)));

->

while(ADCSRA&(1<<ADSC));

Stefan Ernst

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
    for (ch=3;ch<=4;ch++)

        if(ch == 3)
            ...

        if(ch == 4)
            ...

There is a pair of braces missing.

Stefan Ernst

Last Edited: Sun. Jun 18, 2017 - 09:54 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yes, we seem to have omitted parentheses for the "for" statement. Sorry.

Now code work very well but which is the difference between the two codes:

while(!(ADCSRA&(1<<ADIF)));
while(ADCSRA&(1<<ADSC));

Note that you have given up the "!" and instead of "ADIF", we used "ADSC" !!!! I did not understand this difference! Can you explain to me, please?

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

Both are fairly unreadable.   Buy a keyboard that has a SPACE key.

 

Regarding operation.   Open the datasheet in your PDF reader.   Search for ADSC.  Use ctrl-F to find "ADSC"

Learning how to find information in datasheets is really important.    (next best thing after learning what the SPACE key is for)

 

David. 

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

David, I read the datasheet, but if we take the datasheet, we can not always make the right decisions (see my case). For this reason, another way of speaking is also readable.

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

What precisely has the datasheet not told you about ADSC and ADIF?
My guess is you've lifted some suspect code from some site. As to why one would use ADIF in a polled environment is beyond me.
Nevertheless, the ADSC is the adc completed flag. When it is 0, the conversion has completed. This explains the absence of the not!

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

Go on.   You were given clear answers in #2 and #4.

 

My answer in #7 was mainly pointed to your "typing style".

 

I typed #7 without looking at the datasheet myself.

 

I have subsequently followed my own advice (on my ATmega48P/88P/168P/328P datasheet)

1.  I always find the register description the best place to start.  e.g.

• Bit 6 – ADSC: ADC Start Conversion
In Single Conversion mode, write this bit to one to start each conversion. In Free Running mode,
write this bit to one to start the first conversion. The first conversion after ADSC has been written
after the ADC has been enabled, or if ADSC is written at the same time as the ADC is enabled,
will take 25 ADC clock cycles instead of the normal 13. This first conversion performs initialization
of the ADC.
ADSC will read as one as long as a conversion is in progress. When the conversion is complete,
it returns to zero. Writing zero to this bit has no effect.

2.   The next place is any timing diagram e.g. 

 Figure 23-5. ADC Timing Diagram, Single Conversion

 

As you have noticed,   there are several references to ADSC.

 

It is very difficult to know how to reply to some messages.    Just saying RTFM is not very polite.

Re-writing the documentation in my own words might be ignored.

Copy-pasting paragraphs (as above) might be ignored too.

 

You can use the ADIF bit instead.  But it is more complicated to program.

And punters find the concept of "clearing by writing 1 to a bit" very difficult.

 

I repeat.    The best way to understand any code statements is with "sensible indentation and whitespace around operators"

 

David.

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

david.prentice wrote:
It is very difficult to know how to reply to some messages.

Especially given the title implications: "defective AVR operations".

 

Indeed from time to time "interesting AVR behaviour" has been uncovered here on these forums.  Nearly all the time the "interesting" part turns out to be the person behind the keyboard.

 

 

 

 

 

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

I understand your answers.
I used ADIF for one Attiny85 without any problems. I did not understand why in the case of Attiny861 I had to go to the ADSC, that's why I asked. Now, I hope you don't mind me !!!

And of course, maybe the title of the topic is not very suggestive, but my English does not excel - I hope to find understanding from yours!

Thanks for the tips.

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

the_rock wrote:
I used ADIF for one Attiny85 without any problems.

Please show the code, and explain the "not any problems".

 

Note that the ADC code still "works" in that you start a conversion and get a result.  The difference is that the problem code posted here does not properly wait for completion.  I suppose it could appear to somewhat work depending on the circumstances.

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.

Last Edited: Sun. Jun 18, 2017 - 02:28 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

theusch, the code below work very well on Attiny85:

#define F_CPU 8000000UL // ATtiny85 = 8 MHZ

#include <avr/io.h>
#include <util/delay.h>

void adc_init(void);
uint8_t adc_read(uint8_t ch);

uint8_t i, j, ch;

void adc_init()
{
	ADMUX|=(1<<REFS1)	
	| (1<<ADLAR);		
	ADCSRA|=(1<<ADPS2)|(1<<ADPS1) 
	| (1<<ADEN);		
}

uint8_t adc_read(uint8_t ch)
{
	ADMUX &= 0xE0; 
	ADMUX |= ch; 
	ADCSRA|=(1<<ADSC); 
	while(!(ADCSRA&(1<<ADIF))); 
	return ADCH; 
}

int main(void)
{
	DDRB|=(1<<DDB0)|(1<<DDB1)|(1<<DDB3)|(1<<DDB5);
		
	TCCR0A|=(1<<COM0A1)|(1<<COM0A0)|(1<<COM0B1)|(1<<COM0B0)|(1<<WGM01)|(1<<WGM00);  
	OCR0A=127; 	
	OCR0B=127;
		
	TCCR0B|= (1<<CS00);
		
	adc_init(); //  init ADC
	ch = 1;
	_delay_ms(3000);
	
   do
    {	
		for (ch=1;ch<=2;ch++)
		{
			
		if(ch == 1)	
			{
				i = 0;
				i = adc_read(ch);
			
				if(i > 200)
					{
						OCR0A = 127; 
					}
					else
						if(i > 152 && i <= 200)
							{
								OCR0A = 145; 
							}
							else
								if(i > 117 && i <= 152)
									{
										OCR0A = 180; 
									}
									else
										if(i > 90 && i <= 117)
											{
												OCR0A = 205; 
											}
											else
											{
												OCR0A = 250; 
											} 
			}

		if(ch == 2)
			{
				j = 0;
				j = adc_read(ch);

				if(j > 200)
					{
						OCR0B = 127; 
					}
					else
						if(j > 152 && j <= 200)
							{
								OCR0B = 145; 
							}
							else
								if(j > 117 && j <= 152)
									{
										OCR0B = 180; 
									}
									else
										if(j > 90 && j <= 117)
											{
												OCR0B = 205; 
											}
											else
											{
												OCR0B = 250; 
											}

			}
					
		_delay_ms(100);
		}
	} while(1);
}

 

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

Seriously.   Do you expect anyone to read stuff like that?

 

If you can't make the effort to format code nicely,   I am not prepared to waste my time.

 

It takes a fraction of a second to press ctrl-T in the Arduino IDE.   Or ctrl-K, ctrl-D in the AS7 editor.

Or you can add a custom "external command" to most IDEs.

 

David.

Last Edited: Sun. Jun 18, 2017 - 04:08 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

the_rock wrote:
the code below work very well on Attiny85
By accident.

It works because of this:

So the line starting the ADC "ADCSRA|=(1<<ADSC);" also clears ADIF, even if it is compiled into a SBI instruction.

Stefan Ernst

Last Edited: Sun. Jun 18, 2017 - 04:28 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

sternst wrote:
The Tiny841 doesn't have that "SBI and CBI" part.

That's what I wanted to read. Thanks for clarification.

 

Now, I want to go back to the first code I posted with Attiny861. In the last few hours as they leaked from the conversation here, I have just completed the first code (see first post):

#define F_CPU 8000000UL // ATtiny861A la 8 MHZ

#include <avr/io.h>
#include <util/delay.h>

void adc_init(void);
uint8_t adc_read(uint8_t ch);

uint8_t m, n, ch, th0, th1, th2;

void adc_init()
{
	ADMUX|=(1<<REFS1)	//VREF=1.1V
	| (1<<ADLAR);		//Move the result to the left (the most significant 8 bits in ADHC)
	ADCSRA|=(1<<ADPS2)|(1<<ADPS1) //Prescaler: F_CPU/64  == 125kHz
	| (1<<ADEN);		//Enable ADC
}

uint8_t adc_read(uint8_t ch)
{
	ADMUX &= 0xE0; //Delete the old ADC channel
	ADMUX |= ch; //Sets the new ADC channel
	ADCSRA|=(1<<ADSC); //Starts conversion
	while(!(ADCSRA&(1<<ADIF))); //Awaiting the end of the conversion
	return ADCH; //Returns the result (8-bit)
}

int main(void)
{
	DDRB|=(1<<DDB0)|(1<<DDB1)|(1<<DDB2)|(1<<DDB4)|(1<<DDB5)|(1<<DDB6)|(1<<DDB3)|(1<<DDA7); // Set the PB0, PB1, PB2, PB4, PB5, PB6, PB3 and PA7 pins as outputs

	adc_init(); // ADC Init

	ch = 3;  // ADC conversion set it to start from channel 3
	th0 = 65; th1 = 105; th2 = 150; // We define the value of comparison constants

	do  {
		for (ch=3;ch<=4;ch++)
		{
		if(ch == 3)
			{
				m = 0;
				m = adc_read(ch);

				if (th0<m && th1>=m)
				{
					PORTB|=(1<<PB1);				// Turn ON PORTB pin 1 = 5V
					PORTB&=~(1<<PB0);				//Turn OFF PORTB pin 0 & 2
					PORTB&=~(1<<PB2);
				}
				else
				if (th1<m && th2>=m)
				{
					PORTB|=(1<<PB2);				// Turn ON PORTB pin 2 = 5V
					PORTB&=~(1<<PB1);				//Turn OFF PORTB pin 1 & 0
					PORTB&=~(1<<PB0);
				}
				else
				{
					PORTB|=(1<<PB0);				// Turn ON PORTB pin 0 = 5V
					PORTB&=~(1<<PB2);				//Turn OFF PORTB pin 1 & 2
					PORTB&=~(1<<PB1);
				}
			}

		if(ch == 4)
			{
				n = 0;
				n = adc_read(ch);

				if (th0<n && th1>=n)
				{
					PORTB|=(1<<PB5);				// Turn ON PORTB pin 5 = 5V
					PORTB&=~(1<<PB4);				//Turn OFF PORTB pin 4 & 6
					PORTB&=~(1<<PB6);
				}
				else
				if (th1<n && th2>=n)
				{
					PORTB|=(1<<PB6);				// Turn ON PORTB pin 6 = 5V
					PORTB&=~(1<<PB4);				//Turn OFF PORTB pin 4 & 5
					PORTB&=~(1<<PB5);
				}
				else
				{
					PORTB|=(1<<PB4);				// Turn ON PORTB pin 4 = 5V
					PORTB&=~(1<<PB5);				//Turn OFF PORTB pin 5 & 6
					PORTB&=~(1<<PB6);
				}
			}
		}	

		th3=th2+60;

		if (m>th3)
			{
				PORTB|=(1<<PB3);
			}
                        else
                                {
                                       PORTB&=~(1<<PB3);
                                }

		if (n>th3)
			{
				PORTA|= (1<<PA7);
			}
                         else
                                {
                                       PORTA&=~(1<<PA7);
                                }		

    _delay_ms(100);

	} while(1);
}

But there was another problem, I noticed that pin PB3 is flashing and does not light when the ADC3 input is equal to 0V.  If I slightly increase the voltage on the ADC3 pin, the led connected to the PB3's output starts flashing louder and louder.  I don't understand where this perturbation occurs ??? I use an EasyAVR6 development board. 

On the other hand, the LED connected to the PA7 output does not have this behavior but the minimum output on this LED is about 0.5V instead of 0V. 

So, why are the new PB3 and PA7 outputs not working properly? !!!

Last Edited: Sun. Jun 18, 2017 - 04:39 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I tried for two days all sorts of solutions, but without success, so the led on the PB3 flashes when the ADC3 input is higher than 0 and I don't know why? 

Normally the PB3 should swing into 5V when the ADC3 input gets a certain value. Does anyone have a solution to this issue?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
DDRB|=(1<<DDB0)|(1<<DDB1)|(1<<DDB2)|(1<<DDB4)|(1<<DDB5)|(1<<DDB6)|(1<<DDB3)|(1<<DDA7); // Set the PB0, PB1, PB2, PB4, PB5, PB6, PB3 and PA7 pins as outputs

Why on earth you set PA7 in DDRB?

 

You haven't define your th3 in your code. And you could compile it?

I don't know why I'm still doing this hobby

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

I forgot to specifically th3 because: I think when I copied the text it was not the final form of the code. Sorry.

I set PA7 in DDRB because in void main there are:

if (n>th3)
			{
				PORTA|= (1<<PA7);
			}
                         else
                                {
                                       PORTA&=~(1<<PA7);
                                }	

I have tried not to declare PA7 as output, even PB3, but the problem persists. I'm really thinking of reproducing the circuit on a PCB separate from the EasyAVR6 board but until I get to do that, I was thinking of asking for your's opinions!

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

I don't have the hardware so I tested your code in simulator.

 

 

Maybe it looks weird / stupid but try this:

uint8_t mask=0;

...

         if(ch == 3)
         {
                //m = 0; --> not needed
                m = adc_read(ch);

                if (th0<m && th1>=m)
                {
                  PORTB = mask | 0x02;
                  mask  = 0x02;
                }
                else
                if (th1<m && th2>=m)
                {
                  PORTB = mask | 0x04;
                  mask  = 0x04;
                }
                else
                {
                  PORTB = mask | 0x01;
                  mask  = 0x01;
                }
         }

         if(ch == 4)
         {
                //n = 0; --> not needed
                n = adc_read(ch);

                if (th0<n && th1>=n)
                {
                  PORTB = mask | 0x20;
                  mask  = 0x20;
                }
                else
                if (th1<n && th2>=n)
                {
                  PORTB = mask | 0x40;
                  mask  = 0x40;
                }
                else
                {
                  PORTB = mask | 0x10;
                  mask  = 0x10;
                }
          }

 

 

I'm not sure why but it maybe has something todo with pull-up resistor, datasheet noted this :

wrote:
If PORTxn is written logic one when the pin is configured as an input pin, the pull-up resistor is activated.

To switch the pull-up resistor off, PORTxn has to be written logic zero or the pin has to be configured as an output pin.

I tried it but has no effect.

 

Perhaps someone can give you better answer.

 

 

 

 

MG

I don't know why I'm still doing this hobby

Last Edited: Thu. Jun 22, 2017 - 09:18 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
uint8_t adc_read(uint8_t ch)
{
	ADMUX &= 0xE0; //Delete the old ADC channel
	ADMUX |= ch; //Sets the new ADC channel
	ADCSRA|=(1<<ADSC); //Starts conversion
	while(!(ADCSRA&(1<<ADIF))); //Awaiting the end of the conversion
	return ADCH; //Returns the result (8-bit)
}

You are always making the same mistake.

If you test the ADIF bit, you MUST clear it after testing.

Otherwise it remains set and the next conversion can start while the previous one is still not completed.

 

You were advised to use a simplier way

ADCSRA |= (1<<ADSC);         //Starts conversion
while(ADCSRA & (1<<ADSC));   // bit ADSC goes back to "0" after conversion completed
return ADCH;                 //Returns the result (8-bit)