How to achieve Single ADC Conversion in ATtiny

Status
Not open for further replies.

Prince Charming

Member level 2
Joined
Jul 10, 2022
Messages
45
Helped
0
Reputation
0
Reaction score
2
Trophy points
8
Activity points
575
Hello, I've struggled for hours but I couldn't manage to succeed. I need a good coders help.

ATtiny24A demo program:

Requests:
  1. There will be two potentiometers (ADC0 and ADC1) that is going to be read in an order for a single time, no constant conversions.
  2. This is a power saved system so the ADC will turn off after each conversion.
  3. In the "loop" part, potentiometers will be read for a single time. A led (on PA3 pin) will blink in the rate which is according to the read values.
  4. The sequence of loop:
// Turn on ADC
// Select ADC0 channel, Read ADC0
// Select ADC1 channel, Read ADC1
//Turn off ADC
// Turn on LED
// Wait as long as ADC0 value in milliseconds
// Turn off LED
// Wait as long as ADC1 value in milliseconds
// Turn on LED
// Wait as long as ADC0 value in milliseconds
// Turn off LED
// Wait as long as ADC1 value in milliseconds
// Wait for 3 seconds
--> Loop starts over from here

The code I've written so far is below. It reads pot for once but doesn't update in the next loops. Can you help me answer: Why does ADC is able to do only a single conversion?

my code:

C:
#include <avr/io.h>
#include <avr/interrupt.h>

#define LEDButtonPin PA3
#define PotEnablePin PA2
#define pot_DEL_pin PA0
#define pot_DUR_pin PA1

volatile int analogResult=200;
void setup() {
}

void loop() {

  PORTA |=  (1 << LEDButtonPin);
  delay(analogResult);
  PORTA &=  ~(1 << LEDButtonPin);
  delay(analogResult);
  PORTA |=  (1 << LEDButtonPin);
  delay(analogResult);
  PORTA &=  ~(1 << LEDButtonPin);

  delay(3000);
  readPots();
}

void readPots() {
  DDRA &= ~(1 <<  pot_DUR_pin);
  DDRA |= ((1 << LEDButtonPin) | (1 << PotEnablePin));
  PORTA |=  (1 << PotEnablePin); // power up POTs
  delay(100);
  ADMUX |= (1 << MUX0); // ADC1 (PA1) channel is selected
  DIDR0 |= (1 << ADC1D); // Digital input disable to save power
  ADCSRA |= (1 << ADPS1) | (1 << ADPS0); // lowers ADC clock by 1/8 of CPU clock

  ADCSRA |= (1 << ADEN); // Turn on ADC
  delay(100);
  ADCSRA |= (1 << ADSC); //start convertion
 
  while (ADCSRA & (1 << ADSC) ); //wait for conversion
 
  analogResult = (ADCH << 8) | ADCL; // read val 
  ADCSRA &= ~(1 << ADEN); // Turn off ADC
  ADCSRA &= ~(1 << ADIF); // clear flag
  PORTA &=  ~(1 << PotEnablePin); // power down POTs
}
 

Solution
What makes you think this is true. I can not find any information that prooves it .. neither can I see how it is tested.
I turn the pots, the leds blink rate is always same from the start, if I turn off and on the system then the blink rate updates for once, that's the only way.
As soon as you care about supply current you also have to care about floating pins.
I cared about it by pulling unused pins high with input pullup

Are the LEDs only for testing or are they used in the real circuit?
LED is used in real circuit but the pots mission is not to set the blink rate but instead it's mission is to determine the value of a variable called "counter" which the Timer1 will countdown from.


I see some...
Hi,

we don`t know what IDE and compiler you use. Please tell us.

I think the biggest problem is that your "loop" has the name "loop" but indeed it is no loop.
Depending on compiler I´d expect a

Code:
while (1) {
.. this really is a loop
.. put your loop code in here
}

Some other things that came to my mind: (may depend on task, schematic, personal taste..)
* do the port setup in the setup section (all things that have to be done only once)
* do the ADC setup in the setup section (all things that have to be done only once)
* there is no need for anlogResult to be "volatile", since it is not updated within an ISR. But it doesn´t hurt to be volatile.
* in most cases you don´t need to change DDRx during the main loop. An input stays an input and an output stays an output unless you are doing bidirectional communication on one pin.
Mind: don´t leave any pin floating. Used or not used. Analog or digital.
* also you usually don´t need to switch ON/OFF the ADC in a loop. You may say it´s to save power, but the ADC consumes only microamperes, while the LEDs consume some milliamperes.
* you only use one ADC channel, the other is never read.
* you only use 3 variable delays in the loop, while it should be 4

Klaus
 

    johnny78

    Points: 2
    Helpful Answer Positive Rating
Thank you for your concerns. First of all this is a test code, I don't care about the tidyness of the code or floating pins. I am building a system which must run with no supply for a year that's why I must turn off ADC, at least I must try it in the test code. I already apply internal pull up for the unused pins in original code.
* you only use one ADC channel, the other is never read.
If I manage to read one, I'll try the other. I actually did try, it only reads the first pot but second one can't be read.

As I said my problem is, this code can only read one potentiometer for a single time. I want to be able to read two pots whenever I want.

I am using Arduino IDE, there is a hidden main C function divided into Setup() and Loop(). So this is correct. See for details: https://forum.arduino.cc/t/void-setup-vs-int-main/365632/6

Still, I noticed Klaus observes the threads with a great care, I may ask if he sees any problems in any project of mine, even if I have no errors. So thanks. But for now let's only focus on the ADC.

You can make me do it with an interrupt, I am in, I just found it unnecessary for a single pot read.
 

Hi,
The code I've written so far is below. It reads pot for once but doesn't update in the next loops. Can you help me answer: Why does ADC is able to do only a single conversion?
What makes you think this is true. I can not find any information that prooves it .. neither can I see how it is tested.

Please describe how you tested it, what you expect amd what you see instead. Sketches of schematic and timing diagrams surely are of great help..
Currently we don´t know whether your LED ever is ON, or OFF, whether you see any change in delay...

I don't care about the tidyness of the code or floating pins. I am building a system which must run with no supply for a year that's why I must turn off ADC, at least I must try it in the test code.
This contradicts itself: As soon as you care about supply current you also have to care about floating pins.

This also leads me to the next question: Are the LEDs only for testing or are they used in the real circuit?

I see some "requirements" in your text, but I miss them in your code. So I don´t know what is "important" for now and what is not important for me to check.

ADC:
when I follow the datasheet chapter 16.3, I see
* PRADC bit: Is cleared by default. .. You never use it. So I guess you rely on the default state
* ADEN bit: I see you set it and you clear it.
* ADC REF: This is important for ADC function.
* ADMUX is set
* AD conversion started ..
--> I miss the prescaler setup... we neither know what your microcontroller frequency is...
--> I miss ADCSR setup, or any mentioning that you want to use default values...
--> I miss ADCSRB setup, or any mentioning that you want to use default values...

I stop to further go through the code and datasheet

I read the ADC chapter of the datasheet, you have to do the same ... and follow it step by step.

***
ADCSR, ADCSRA, ADCSRB ...
I recommend to write them, especially YOUR default setup, and I recommend to do this "default" in the SETUP section.
If you don´twant to do this, then I still recommend to add some comment about it in the code.
This is good for us, so we see you did check and care about it.
But it´s also good for you .. if you want to modify your own code after some months or years.

Klaus
 

If you are using the Arduino setup, I would expect there to be code in the 'setup()' function that configures the pins. Also without knowing the specific MCU, are you sure that the pins are set up as inputs and outputs by default as you seem to assume?
Personally I would first check that the 'loop()' is working as you expect. Make the initial value of 'analogResult' something more like 1000 (i.e. delay for 1 second between each toggle of the LED) and comment out the delay(3000) and call to 'readPots()'. That will make the LED flash on fort 1 second and off for 1 second repeatedly.
At least that will let you know if your means of determining 'success' (i.e. watching the LED flash) works.
Next, try printing out the 'analogResult' value in the serial port to make sure that it is being set to something reasonable in the 'readPots()' function. It could be set to (say) 0 in which case you might currently be seeing the LED flash twice (fairly fast) and then if 'analogResult' is a very low number, the flashes will be too short to physically see (you eye needs about 20mS to respond to changes in light - this is why movies don't appear to flash).
Susan
 

What makes you think this is true. I can not find any information that prooves it .. neither can I see how it is tested.
I turn the pots, the leds blink rate is always same from the start, if I turn off and on the system then the blink rate updates for once, that's the only way.
As soon as you care about supply current you also have to care about floating pins.
I cared about it by pulling unused pins high with input pullup

Are the LEDs only for testing or are they used in the real circuit?
LED is used in real circuit but the pots mission is not to set the blink rate but instead it's mission is to determine the value of a variable called "counter" which the Timer1 will countdown from.


I see some "requirements" in your text, but I miss them in your code. So I don´t know what is "important" for now and what is not important for me to check.
Forget the requirements, I want to read two potentiometers whenever I want for a single time literally that's the only goal, I don't care about LEDs, I can modify the code as I desire after I figure out how to comprehend ADC.

Wait a sec, It seems like it does the same thing with ADEN, do they both turn on/off the ADC?
ADC REF: This is important for ADC function.
Default value of REFS1 and REFS0 is 0 and this means VCC will be the reference, that's what I want.
I miss the prescaler setup... we neither know what your microcontroller frequency is...
ADCSRA |= (1 << ADPS1) | (1 << ADPS0); // lowers ADC clock by 1/8 of CPU clock
this line is what you are looking for. ATtiny24A's default frequency is 1MHZ and I use it.
I miss ADCSR setup, or any mentioning that you want to use default values...
What? It is at around bottom lines.
I miss ADCSRB setup, or any mentioning that you want to use default values...
There are only ADTS bits in this register that are relevant and their default value is zero so I didn't need to make them zero again to enable Free Running Mode, but okay as you wanted sir.

This is good for us, so we see you did check and care about it.
I obeyed this. I've written a cleaner version of the code, I included the commands that equalizes registers to their default value for cleaner reading. This doesn't mean I've solved anything about the problem yet.

Code Version 2:
C:
#include <avr/io.h>
#include <avr/interrupt.h>

#define LEDButtonPin PA3
#define PotEnablePin PA2
#define pot_DEL_pin PA0
#define pot_DUR_pin PA1

volatile int analogResult1;
volatile int analogResult2;

void setup() {
  DDRA &= ~((1 <<  pot_DUR_pin) | (1 <<  pot_DEL_pin));
  DDRA |= ((1 << LEDButtonPin) | (1 << PotEnablePin));
  DIDR0 |= (1 << ADC1D); // Digital input disable to save power
  DIDR0 |= (1 << ADC0D); // Digital input disable to save power
  ADCSRA |= (1 << ADPS1) | (1 << ADPS0); // lowers ADC clock by 1/8 of CPU clock
  ADMUX &= ~((1<<REFS1) | (1<<REFS0)); // Chose VCC as ADC_ref
  ADCSRB &= ~((1<<ADTS2) | (1<<ADTS1) | (1<<ADTS0)); // Choose Free running mode
}

void loop() {

  PORTA |=  (1 << LEDButtonPin);
  delay(analogResult1);
  PORTA &=  ~(1 << LEDButtonPin);
  delay(analogResult2);
  PORTA |=  (1 << LEDButtonPin);
  delay(analogResult1);
  PORTA &=  ~(1 << LEDButtonPin);
  delay(analogResult2);
  PORTA |=  (1 << LEDButtonPin);
  delay(analogResult1);
  PORTA &=  ~(1 << LEDButtonPin);

  delay(3000);
  readPots();
}

void readPots() {

  PORTA |=  (1 << PotEnablePin); // power up POTs
  delay(100);
  ADMUX &= ~(1 << MUX0); // ADC0 (PA0) channel is selected
  ADCSRA |= (1 << ADEN); // Turn on ADC
  ADCSRA |= (1 << ADSC); //start convertion
  while (ADCSRA & (1 << ADSC) ); //wait for conversion

  analogResult1 = (ADCH << 8) | ADCL; // read val
 
  ADMUX |= (1 << MUX0); // ADC1 (PA1) channel is selected
  ADCSRA |= (1 << ADSC); //start convertion
  while (ADCSRA & (1 << ADSC) ); //wait for conversion

  analogResult2 = (ADCH << 8) | ADCL; // read val
 
  //ADCSRA &= ~(1 << ADEN); // Turn off ADC
 // PORTA &=  ~(1 << PotEnablePin); // power down POTs
}
--- Updated ---

Everyone stop, thank you for your concerns. I tried to use "analogRead()" once again and this time it magically worked. I believed in magic with the MCUs
 
Last edited:

Solution
Hi,

The code is much cleaner now.

Still I miss PORTB setup, you said you pulled HIGH all unused pins.

According ADCSR (without -A and without -B): Here I was confused by a typo in the datasheet page 150

Maybe the problem was caused by this: Pages 140, 154
ADCL must be read first, then ADCH, to ensure that the content of the
data registers belongs to the same conversion.
so maybe this solves the problem:
Code:
analogResult1 =  ADCL; // read val
analogResult1 |=( uint16_t)(ADCH << 8)  // read val
same with result2..

Don´t believe in magic. Even if it´s hard, but always try to find out why somehting does not work as expected.

Klaus
 

Oh, I didn't know the order of the reading matters. Well, I' cant check it right now but if I do a test I will inform this thread.

I was joking about magic, anyway thanks..
 

Status
Not open for further replies.

Similar threads

Cookies are required to use this site. You must accept them to continue using the site. Learn more…