0
\$\begingroup\$

I want to read from potentiometers via Attiny24A a single time. So far I managed to read the potentiometer at the start once but I want to read the pot values in a random place of the code. I've written the test code below which blinks an LED according to the potentiometer position:

#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 (Pot is not connected to VCC but a pin to save power)
  delay(100);
  ADMUX |= (1 << MUX0); // ADC1 (PA1) channel 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) | (1 << ADPS1) | (1 << ADPS0); //start convertion, lowers ADC clock by 1/8 of CPU clock

}

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

void loop() {

  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<<ADIF); // clear flag
  ADCSRA |= (1 << ADEN);
  ADCSRA |= (1 << ADSC);
}

The code actually first enables free running mode, then reads the analog value, then disables the ADC when the conversion is done. This is a hard way of doing things and ADC reads with a gigantic delay. I turn the potentiometer, but it affects the LED blink rate seconds later. I would be glad if you could tell me a proper way to read from rhe potentiometer a single time.

Datasheet: Attiny24A datasheet

Thanks.

Edit: I tried the code below which is without interrupts but it only reads the pot for a single time at the start. I can't reread.

#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);

}

Edit 2: I fixed minor issues in Edit1 but no help, it still does a single conversion only:

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

Note: I want to turn off ADC, I am building a battery powered system. But even I commented that part out, ADC still only does a single conversion.

\$\endgroup\$
12
  • \$\begingroup\$ Why can't you just start a conversion and wait when it is ready and return with a result? Why do you even use ADC interrupts for such a simple thing, the code must be copied from some more complex program? Have you read the datasheet how to use ADC? At least the simplest solution is to start conversion and wait until it is done, i.e. the interrupt happens. Also your code is missing the main function so it won't work. Unless you are using Arduino, but then why don't you read the ADC using Arduino libraries? \$\endgroup\$
    – Justme
    Commented Oct 12, 2022 at 13:58
  • \$\begingroup\$ This is the only way I know to read from potentiometer, by using interrupt. I copied this code (not %100 but still) from a Toutube video where the guy aims to make a continuous conversion. After I start a conversion and wait for random time (long enough to read from ADC, ex. few milliseconds). Can I read the ADCH and ADCL registers? Do you advice something like that? Can I remove the interrupt enabling or flag clearance by doing that? \$\endgroup\$ Commented Oct 12, 2022 at 14:03
  • \$\begingroup\$ "Some time later" is probably: after all your delays have happened! That could be up to 5 seconds later because there are 5 seconds of delay. Possibly even 10 seconds because I don't know if the ADC will be done converting before the next loop. I think you need to think about which order you want the computer to do things in \$\endgroup\$ Commented Oct 12, 2022 at 14:13
  • \$\begingroup\$ There is no main function in ATtiny I guess. This code works. I didn't use Arduino library because analogRead() didn't work in ATtiny at all. I added my new code to the first post but still no help. \$\endgroup\$ Commented Oct 12, 2022 at 14:14
  • \$\begingroup\$ Look at the Arduino core code for analogRead(). As mentioned by others, interrupts are not needed. Simply start the conversion and wait until The ADSC flag is cleared. No need for delays. You can also simply write val = ADC; to read the result. If in doubt, read the datasheet. The AVR would have to be one of the simplest adcs to use. You don’t need to fiddle with ADEN unless you want to save power. \$\endgroup\$
    – Kartman
    Commented Oct 12, 2022 at 14:33

2 Answers 2

1
\$\begingroup\$

I have find out the problem, it's VERY tricky. Conversion work just single time because of analogresult = (ADCH << 8) | ADCL;

Just replace by analogresult = ADC; that's all !

I guess that reading high register before low register cause a mess and the ADC is screwed until reset.

\$\endgroup\$
1
  • \$\begingroup\$ I solved the problem by using default Arduino function "analogRead()" long time ago. Thanks anyway, I will consider what you've said for a future project :) \$\endgroup\$ Commented Apr 16, 2023 at 9:58
0
\$\begingroup\$

I turn the potentiometer, but it affects the LED blink rate seconds later. ...

To make the blink rate respond quickly to the pot you have to read the ADC frequently to see if it changed.

Here's an idea based on how DDS signal generators work

byte phase = 0;

void loop() {
    phase += analogRead() >> 3;
    digitalWrite(LED, phase & 0x80);
    delay(1);  // play with this delay
}

Note you will delay only a short time between analog reads which makes the loop responsive to changes in the pot.

Each time through the loop the phase variable will increase by the ADC reading. When it reaches 256 it will roll over back to 0. Each time through the loop the phase variable can increase in value from 0 to 128. The LED is set according to the high bit of the phase variable.

If the ADC voltage is low the phase variable increases slowly. If the ADC voltage is high it cycles quickly.

You can also put this code in ADC_vect ISR and trigger it on a regular basis.

\$\endgroup\$
1
  • \$\begingroup\$ The latency lasts for 5-6 seconds that's why it is not about the ADC sampling, it's more than that. My final goal is not blinking led. My goal is to set a counter variable which the Timer1 will count down from. So, let's consider I use no interrupts as in the second code I posted, why am I only able to do only one conversion even though I call the function in each loop? \$\endgroup\$ Commented Oct 12, 2022 at 22:23

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.