Single/Auto ADC conversion in ATtiny

Hi, I've written the test code below to blink a led with the desired interval which is set by a potentiometer. I want to learn about both free running and single conversion modes:

(ATtiny24A)

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

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

volatile int analogResult;
void setup() {

  DDRA &= ~(1 <<  pot_DUR_pin);
  DDRA |= ((1 << LEDButtonPin) | (1 << PotEnablePin));
  PORTA |=  (1 << PotEnablePin); // power up POTs
  delay(200);
  ADMUX |= (1 << MUX0); // ADC1 (PA1) cahnnel is selected
  ADCSRB &= ~((1 << ADTS2) | (1 << ADTS1) | (1 << ADTS0)); // Free running mode
  DIDR0 |= (1 << ADC1D); // Digital input disable to save power
  ADCSRA |= (1 << ADEN) | (1 << ADIE) | (1 << ADATE); // ADEN: ADC Enable, ADIE: ADC Interrupt Enable, ADATE: ADC Auto Trigger Enable
  sei(); // enable interrupts
  ADCSRA |= (1 << ADSC); //start convertion

}

ISR(ADC_vect) {
  analogResult = (ADCH << 8) | ADCL;
}

void loop() {

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

Currently the ADC converts automatically, but it is rather slow. I must stop moving the potentiometer and wait for some time (some time that a human can measure, one second or so) for the changed to be visible. It is not because of the delay functions I am sure. I may need rapid conversions in the future so I am asking:

Question 1: Why is ADC conversion occur with a delay in free running mode, why does it need a steady input before converting?

When I delete (1 << ADATE) part it makes a single reading at start and never reads again. I want to be able to read pot value whenever I want with a single line of code. I don't want free running mode at all. My priority is single conversion for now.

Question 2: How can I make a single ADC reading whenever I want?

Thanks.

What makes you so sure? The A/D returns a number between 0 and 1023, and you have two delays depending on where the pot is set, so that is up to two seconds before you get an update.

Why are you doing such a bazaar thing?

What else do you think what it would do?

Because the lag occurs for 5-6 seconds, that's too long

Well you are accessing a 16 bIt number while the interrupts are enabled. That can cause these values to be corrupted as there could be an interrupt between one byte being fetched and another.

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

[https://ww1.microchip.com/downloads/en/DeviceDoc/ATtiny24A-44A-84A-DataSheet-DS40002269A.pdf](https://ATtiny24A datasheet)

Can you please write the setup and loop parts of this simple program in registers? Thanks.

That is not generally how the forum works unless you are willing to pay for someone to write it for you

If not, then try to write the code yourself and come back with any problems that you encounter

I did but nobody answered my question. The original topic: Single/Auto ADC conversion in ATtiny

Also I've written another one without interrupts but it still only reads potentiometer at start:
Second version:

#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;
void setup() {

  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

}

void loop() {
  
  ADCSRA |= (1 << ADSC); //start convertion
  
  while (ADCSRA & (1 << ADSC) ); //wait  
  analogResult = (ADCH << 8) | ADCL; // read val
  ADCSRA &= ~(1<<ADIF); // clear flag

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

  ADCSRA &= ~(1 << ADEN);
  delay(3000);
  ADCSRA |= (1 << ADEN);

}

That is no reason to start another topic

I did and you were not very cooperative were you?

I have merged your cross-posts @prince_charming.

Cross-posting is against the Arduino forum rules. The reason is that duplicate posts can waste the time of the people trying to help. Someone might spend a lot of time investigating and writing a detailed answer on one topic, without knowing that someone else already did the same in the other topic.

Repeated cross-posting can result in a suspension from the forum.

In the future, please only create one topic for each distinct subject matter. This is basic forum etiquette, as explained in the "How to get the best out of this forum" guide. It contains a lot of other useful information. Please read it.

Thanks in advance for your cooperation.

First of all I am sorry, I know you answered I just wanted a quick way out. Secondly, I want a single conversion without interrupts, that seems to be a cleaner way so accessing bits during interrupt won't be a problem. The only problem is this: ADC converts only for once but can't do another conversion. Why is that? My code is simple:

#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
}

I am in to do a zoom or discord meeting with you or anybody. I can try the recommendations live. Tell me if you are interested or if you can see why ADC works only once, tell me please. Thanks.

The ADC will do a conversion when you set the appropriate bit in the control register. When the conversion is complete a bit is set saying it is complete and optionally an interrupt is triggered. I am sorry at the moment I can't tell you what bit in what register because I am on my mobile device and don't have access to the data sheet.

When one conversion is complete and sets the finished bit there is another bit that controls if an other conversion will start. This is the continuous conversion mode that you were adamant you did not want to use. This allows the code to do other things while the conversion is on going. When you go back to see if the next conversion is you have to test for the finished flag. If it is not yet set then you have to wait until it is.

Maybe you need to look for those flags, especially the one that starts off a conversion so that it again will do a conversion.

You are still doing this:-

And you will not say why that is.
What do you see as an advantage in this sort of code?
Not answering question like this is considered to be not cooperating. We only know what you tell us we can not read your mind. There are lots of people who do cooperate and those will alway take priority over those who don't.

I suspect that the reason nobody answered your questions was the attitude you showed on you previous posts. You were not very cooperative in those, people vote with their feet here. Theirs is a setting you can make to hide posts from specific members that you want to ignore. Maybe you are on these lists for some members.

ADIF: ADC Interrupt Flag is set to 1 after conversion is complete, since I use no interrupts this is irrelevant.
Also ADSC: ADC Start Conversion is set to 0 when conversion is complete but I already wait for it in the while loop.

Is this ADSC, if it is I already set it in each conversion

I don't need to do other stuff while MCU is converting analog values, I only do it once for each pot so no problem.

This is a test code, I just want to observe if potentiometer changes are understood by the MCU. I will not blink a LED at the end. My final goal is to create a counter variable for Timer1 and equalize it to the potentiometer values, I must be able to do it once in a blue moon whenever I want. That's why I didn't care about the stupid delays which actually slows down the response time. Still doesn't explain why it does only a single conversion.

My final code is this, cleaner version of the above, still doesn't work:

#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() {
  setupPots();
}

void loop() {

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

  delay(3000);
  readPots();

}

void setupPots() {
  DDRA &= ~(1 <<  pot_DUR_pin);
  DDRA |= ((1 << LEDButtonPin) | (1 << PotEnablePin));
  DIDR0 |= (1 << ADC1D); // Digital input disable to save power
  ADCSRA |= (1 << ADPS1) | (1 << ADPS0); // lowers ADC clock by 1/8 of CPU clock
}

void readPots() {

  PORTA |=  (1 << PotEnablePin); // power up POTs
  delay(100);
  ADMUX |= (1 << MUX0); // ADC1 (PA1) channel is selected
  ADCSRA |= (1 << ADEN); // Turn on ADC
  ADCSRA |= (1 << ADSC); //start convertion

  while (ADCSRA & (1 << ADSC) ); //wait for conversion

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

I didn't realize my bad attitude in other threads, now I am just frustrated because I can't do a very basic thing. I may try to create a new account in the future. Also I will try to calm down. Thanks for warnings.

Please don't do that. Some of the people here are very uptight about this sort of thing and it will only make work for the moderators to deal with cleaning up the multiple accounts.

OK can I ask one more question?
Why are you not just using the analogRead() instruction to do what you want? Why are you wanting to do it through register bit manipulation?
The analogRead() is supported on the ATtiny24.

I tried it, it didn't work. I'll give you another example that's irrelevant from ADC problem:

Also, I tried to use "digitalRead()" to determine rising trigger from pinchange interrupt, it also didn't work. I used something like:

ISR(PCINT1_vect) { // sensor interrupt
  if ( ((PINB & 0b00000100) != 0) && state==3){
    flagPIR = true;
  }
}

And it worked. I am convinced that some Arduino functions are unnecessarily complex and problematic. I understand and am able to control what is going on if I use registers. Still, if you make analogRead() work, I am in.

OMG, brooooooo. It worked. I am not dumb ofc I tried to do it with analogRead() before but it ddiN't work in the past, now magically it is working. It is probably about the other mistakes I corrected in the code after I tried analogRead(). Now that the only missing part was ADC read, every other part is correct in the code and Arduino function worked. It's funny but I will mark your answer as solution as it literally is :smiley:

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.