Analogread and interrupts

Hi, I am new here so I am sorry if this isn’t the right section for this answer. Sorry for my shitty English also :frowning:

I am trying to make a system which reads a temperature with analogRead() -function and a moisture with interrupts. I know that interrupts and analogRead have a confliction but I don’t know how to fix it.

Here is my code. Sorry it is in Finnish but main problem is with reading = analogRead(sensorPin); and interrupts. Thanks in advance I am really losing it with this :confused:

If it helps I can translate the code to english

//Kosteusmittaus, esimerkki H25K5
//Esivastus megaohmin luokkaa
//Taajuuden generointi PVM:llä nastaan 5, taajuus 980Hz
//AD-muuntimen kanava 0
//AD-muuntimella luetaan kkeskeytyksin 125 näytettä taulukkoon(jakso),joista haetaan maksimi.
//Maksimi on verrannollinen kosteuteen
//Kalibrointitaulukkon on otettu 4 arvoparia, joista lasketaan kosteus
volatile int analogVal;
int sensorPin = 1;
int taul[125];
int cnt=0;
int maxval=0;
float arvo; // voltit jotka saadaan analogista
//float arvoresistori = 0; //RH: jännite / virralla, tunnettu
float lampo1;
float lampo2;
volatile int readFlag;
int y = 0;
float vertailuR1 = 0;
float vertailuR2 = 0;
//float temperature = 24.65; //tunnettu
int i;
int k; //kosteusarvoon
int reading =0;
float AsteetJaVastukset[] = {
  5,   0,   10000, 4800, 2100,980,480,250,130,73,41,23, 13,7.20,4,2.20,   // 16 välein osutaan lämpöarvoon  
  10,15000, 7400, 3400, 1500, 700, 350, 190, 100, 57, 32, 19, 11, 6.4, 3.7, 2.2,  // if x > 1, kosteusarvot[x/17+jakojäännös] = kohdan kosteusarvo
  15,11000,5300,2400,1100,520,360,140,80,46,27,16,9.5,5.8,3.5,2.1,
  20,8200,3900,1800,820,400,200,110,64,37,22,14,8.4,5.2,3.3,2,
  25,6300,3000,1400,630,310,160,87,49,31,19,11.8,7.5,4.8,3,2,
  30,4900,2300,1100,490,240,120,74,43,26,16,10.2,6.7,4.4,2.9,1.9,
  35,4000,1800,870,390,190,100,61,36,22,14,9.1,6.1,4.1,2.8,1.8,
  40,3300,1500,710,320,160,86,51,30,19,12,8.2,5.6,3.8,2.6,1.8,
  45,2800,1300,600,270,130,73,44,26,17,11,7.5,5.2,3.6,2.5,1.7,
  50,2500,1100,510,230,110,63,38,23,15,10,6.9,4.9,3.4,2.4,1.7,
  55,2200,1000,450,200,100,55,34,21,14,9.2,6.5,4.6,3.3,2.4,1.7,
  60,2100,920,410,180,91,50,30,19,13,8.6,6.1,4.4,3.2,2.3,1.6
  };
int kosteusarvot[] = {0,20,25,30,35,40,45,50,55,60,65,70,75,80,85,90};  //kosteusarvot

//int ad[1025]; //ad muuntimelta tulee tänne arvo kohtaa admuunnin arvo = ad[admuunnin arvo]
float kosteus;
int x = 0; // counteri
void setup()
{
 Serial.begin(9600);
 analogWrite(5,128); //980Hz PWM, pin 5
 noInterrupts();                              // MOISTURE SENSOR READ
 ADMUX &= B11011111;
 ADMUX |= B01000000;
 ADMUX &= B11110000;
 //ADMUX |= 8;
 ADCSRA |= B10000000;
 ADCSRA |= B00100000;
 ADCSRB &= B11111000;
 ADCSRA |= B00000111;
 ADCSRA |= B00001000;
 ADCSRA |=B01000000;
 interrupts();
}

void loop()

{
  
  reading = analogRead(sensorPin);    // THERMISTOR READ
   float voltage = reading * 5.0;
    voltage /= 1024.0;
   float lampo =-14.438*pow(voltage,3)+127.09*pow(voltage,2) -464.13*voltage +616.26;

  if(lampo < AsteetJaVastukset[0]){ // jos lämpötila on alle 5 asteen, kosteutta ei pysty laskemaan tarkemmin
    kosteus = 20;
  }
  while(lampo > AsteetJaVastukset[x])
{
  x = x+16;
}
float arvoresistori = arvo*5/1024/0.005;
y = x;
lampo1 = AsteetJaVastukset[y-16];
lampo2 = AsteetJaVastukset[y];
x++;
while(AsteetJaVastukset[x] > arvoresistori)
{
  x++;
}

Serial.println("value:");
Serial.println(analogVal);
Serial.println(arvo);
Serial.println("arvoresistori");
Serial.println(arvoresistori);
Serial.println("lampo1");
Serial.println(lampo1);
Serial.println("lampo2");
Serial.println(lampo2);
Serial.println("x");
Serial.println(x);
k = x/17 + x%17;
Serial.println("K: ");
Serial.println(k);
Serial.println("          x-15 ja x-1");
Serial.println(AsteetJaVastukset[x+1-16]);
Serial.println( AsteetJaVastukset[x-1]);
Serial.println(AsteetJaVastukset[x]);
vertailuR1 = (lampo2-lampo)/5*AsteetJaVastukset[x-16] + (lampo-lampo1)/5 * AsteetJaVastukset[x];
vertailuR2 = (lampo2-lampo)/5*AsteetJaVastukset[x+1-16] + (lampo-lampo1)/5 * AsteetJaVastukset[x-1];

Serial.println("R1: ");
Serial.println(vertailuR1);
Serial.println("R2: ");
Serial.println(vertailuR2);
kosteus =(vertailuR2-arvoresistori)/(vertailuR2-vertailuR1)*kosteusarvot[k-1]+(arvoresistori-vertailuR1)/(vertailuR2-vertailuR1)*kosteusarvot[k];

Serial.println("lampotila on:");
Serial.println(lampo);
Serial.println("kosteus on: ");
Serial.println(kosteus);
Serial.println("Reading!!!!");  
Serial.println(reading);
x = 0;
delay(5000);
}
ISR(ADC_vect)
{
 
 analogVal = ADCL | (ADCH << 8);
 taul[cnt]=analogVal;
 cnt++;
 if(cnt==125)
 {
 maxval=taul[0];
 for(i=0;i<125;i++)
 {
 if (maxval<taul[i+1]) maxval=taul[i+1];
 }
 
 arvo=maxval;
 cnt=0;
 }
}

Issue isn't interrupts, it's the fact that you're changing the configuration of the ADC and then expecting analogRead() to still work.

analogRead() depends on the ADC registers being in the state they're initialized to in init(); once you change them, all bets are off (the same holds true whenever you start manipulating an on-chip peripheral directly as opposed to via the Arduino wrappers - just like how lots of libraries that need timers break PWM on a pair of pins).

So what can I change/add to make it work?

You’d need to either restore the ADC registers to their original state before running analogRead(), and then change them back after, or write your own function to read the chosen pin. Either way, this will probably include disabling the ADC interrupt, since you don’t want it to run that ISR for the analogRead() on a different pin.

It’s hard for me to give a more precise answer without breaking out the datasheet, (since I can’t tell what you’ve set the ADC up for), which I don’t have time for now. When you write to configuration registers, you should note in comments what bits you’re setting (or, since you’re usually flipping one bit at a time, do something like ADCSRA|=(1<<bit name), for example, ADCSRA|=(1<<ADEN) to flip the ADEN (ADC Enable) bit). Besides, I don’t know the specifics of your use case, which probably matter a lot here.

DrAzzy: Issue isn't interrupts, it's the fact that you're changing the configuration of the ADC and then expecting analogRead() to still work.

But you have to change both the mux bits and the reference voltage in order to read the internal temperature sensor.

Perhaps reading the internal temperature sensor is just not compatible with analog read and Azumiar will have to do it manually. That's how I've always done it anyway.

Azumia, I'll check with some of my code and make sure that your mux and control registers are set properly and post a link of how to read temperature without using analog read. It's pretty easy to do.

Ok. Here is some very basic code that I've written. Note that in this particular sketch I am not using the ADC for anything other than reading the internal temperature sensor. That's why I only need the code to initialize it in setup().

If you are otherwise using the ADC then you will need to change the mux and setup before each read (and possibly restore them to their previous state if Analogread doesn't already do so. Have to check on this).

BTW. If you change the reference voltage before reading then add small delay to allow it to settle. (EDIT: The datasheet just recommends taking and discarding one sample after switching the reference).

In my setup() routine

  ADMUX = 0xC8;         // Set mux for internal temperature and voltage reference.
  ADCSRA = 0x87;        // Configure ADC status (Enable ADC and set clk prescaler).
unsigned readTempC() {
  ADCSRA |= 0x40;       // Issue start convert to ADC.
  byte tmpb=0x40;
  while ((tmpb & 0x40) !=0) {tmpb = ADCSRA;}  // Wait for ADC to finish convertion.
  return ADCW;

Sorry, I think I've misunderstood what you are trying to do.

Azumiar. Can you clarify what you are trying to do. When I saw your code manipulating the MUX and Reference I assumed that you wanted to read the internal temperature sensor (because that's how you normally do that). Now I look at your code in more detail I see that you want to read from analog input A1.

Do you have an external temperature sensor connected to analog input A1? If so then ignore what I said above. Just do what DrAzzy said and use Analogread() without manually altering any of the ADC registers beforehand.

Sorry about confusing and messy code in first post. Yeah I am using a thermistor in A1 and moisture sensor in A0. Maybe this code make it easier to understand:

volatile int MoistureAnalogVal;
int thermistorPin = 1;   // A1, thermistor
int arr[125];   
int count=0;
int maxval=0;   // maxvalue in arr[125]
float moistureVoltage; // Volts from A0 (moisture sensor)
int i;
int Thermistorreading =0;  // sensorPin reading
void setup()
{
 Serial.begin(9600);
 analogWrite(5,128); //980Hz PWM, pin 5
 noInterrupts();
 ADMUX &= B11011111;
 ADMUX |= B01000000;
 ADMUX &= B11110000;
 //ADMUX |= 8;
 ADCSRA |= B10000000;
 ADCSRA |= B00100000;
 ADCSRB &= B11111000;
 ADCSRA |= B00000111;
 ADCSRA |= B00001000;
 ADCSRA |=B01000000;
 interrupts();
}

void loop()

{
  
Thermistorreading = analogRead(thermistorPin);

Serial.println(Thermistorreading);
Serial.println(moistureVoltage);

}
ISR(ADC_vect)
{
 
 MoistureAnalogVal = ADCL | (ADCH << 8);
 arr[count]=MoistureAnalogVal;
 count++;
 if(count==125)
 {
 maxval=arr[0];
 for(i=0;i<125;i++)
 {
 if (maxval<arr[i+1]) maxval=arr[i+1];
 }
 
 moistureVoltage=maxval;
 count=0;
 }
}

Hopefully it’s now easier to understand.

Yeah I am using a thermistor in A1 and moisture sensor in A0.

So, why not names the pins thermistorPin and moisturePin? Jane and Sally just don't cut it.

Why does reading a thermistor or moisture sensor require interrupts?

Well I don't know better way to read that moisturepinvalue 125 times to get maxvalue of it :/

Azumiar: Well I don't know better way to read that moisturepinvalue 125 times to get maxvalue of it :/

Why do you need to read that many times? How fast is the moisture value changing? How fast can the sensor react to changes?

Have to read it many times because maxvalue is correct value for moisture. Maybe that could work with less values but still same problem.

Datasheet for moisture sensor: http://www.farnell.com/datasheets/1355476.pdf

Azumiar: Sorry about confusing and messy code in first post. Yeah I am using a thermistor in A1 and moisture sensor in A0.

Then just use "analogRead(A1)", it's all you need. The reason why analogRead takes a parameter (like A0, A1 etc) is so that it can automatically mux the correct input for you.

Azumiar: ... to read that moisturepinvalue 125 times to get maxvalue of it :/

Just use the main loop to read it as many times as you like, it's much easier. It's a bit over one millisecond per analogRead, but the main loop doesn't care. You'll still get your 125 samples (whatever the need for that is) in under 200 milliseconds.

Azumiar:
Sorry about confusing and messy code in first post. Yeah I am using a thermistor in A1 and moisture sensor in A0. Maybe this code make it easier to understand:

volatile int MoistureAnalogVal;

int thermistorPin = 1;  // A1, thermistor
int arr[125]; 
int count=0;
int maxval=0;  // maxvalue in arr[125]
float moistureVoltage; // Volts from A0 (moisture sensor)
int i;
int Thermistorreading =0;  // sensorPin reading
void setup()
{
Serial.begin(9600);
analogWrite(5,128); //980Hz PWM, pin 5
noInterrupts();
ADMUX &= B11011111;
ADMUX |= B01000000;
ADMUX &= B11110000;
//ADMUX |= 8;
ADCSRA |= B10000000;
ADCSRA |= B00100000;
ADCSRB &= B11111000;
ADCSRA |= B00000111;
ADCSRA |= B00001000;
ADCSRA |=B01000000;
interrupts();
}

void loop()

{
 
Thermistorreading = analogRead(thermistorPin);

Serial.println(Thermistorreading);
Serial.println(moistureVoltage);

}
ISR(ADC_vect)
{

MoistureAnalogVal = ADCL | (ADCH << 8);
arr[count]=MoistureAnalogVal;
count++;
if(count==125)
{
maxval=arr[0];
for(i=0;i<125;i++)
{
if (maxval<arr[i+1]) maxval=arr[i+1];
}

moistureVoltage=maxval;
count=0;
}
}




Hopefully it's now easier to understand.

A big problem here is that you’ve set the ADC to free running mode with an interrupt triggered at the end of each conversion. This means that the ADC is now fully occupied all of the time. The other analogRead for your temperature measurement cannot work.

If you want to go with controlling the ADC yourself then you are going to have to go all the way and collect temperature readings the same way.

What other functions does your main loop have to do while it’s waiting for the moisture MaxValue? Are there other time critical things there that would make analogReads cause some problems?

Think about something simple along these lines (BTW. Code NOT typed from an IDE and syntax NOT debugged)

int finalMax, currentMax=0, sampleCount = 0, maxSampleCount = 125; 
boolean moistureCollecting = true;


void loop() {
  ...

  if (moistureCollecting) {
    sampleCount++;
    int newSample = analogRead(A0);
    if (newSample > currentMax) currentMax = newSample;    // Find maxium value
    if (sampleCount >=  maxSampleCount) {
      sampleCount = 0;
      MoistureCollecting = false;
      finalMax = currentMax;
      currentMax = 0;
    }
  }

  ...
}

No there aren't any other time critical things. Tried to use that main loop for moisture readings but it just gives out very different values everytime.. So how can I change the code to get rid of that freerunning mode and make that analogRead function work? Sorry I am new to this :(

Azumiar: No there aren't any other time critical things. Tried to use that main loop for moisture readings but it just gives out very different values everytime.. So how can I change the code to get rid of that freerunning mode and make that analogRead function work?

Get rid of free running mode by just not altering any of the ADC control registers and letting analogRead do it all for you.

See my previous post for an example of reading the sensor multiple times in the main loop.

BTW. I just noticed that your humidity sensor requires approximately 1kHz AC excitation. Does that mean that you are trying to read a 1kHz AC signal at the analog input? Because that's not going to work.

So is that the reason for the 125 samples and finding the maximum. To try and detect the peak value of a 1kHz signal on A0? If so then you need to start at the beginning on what you're doing and how your sensors are connected.

Well my programs are working fine separately but when I combine them I get this conflict between analogread and interrupts. It's the only problem I have and I would like to know if there is anyway to get this work properly...

MoistureSensor is connected with a 1M ohm resistor and PWM 5, with 980Hz and Thermistor with 1000 ohm and 5v.

Azumiar: Well my programs are working fine separately but when I combine them I get this conflict between analogread and interrupts. It's the only problem I have

Well you can continue to do it that way (free running) for moisture if you want. But you should disable free running mode (ADATE) and mask the interrupt (ADIE) before using analogRead for temperature.

MoistureSensor is connected with a 1M ohm resistor and PWM 5, with 980Hz and Thermistor with 1000 ohm and 5v.

Is that a capacitive or a resistive sensor? That datasheet doesn't give a lot of application information. The datasheet only specifies it's operation for a 1kHz sine wave. Do you even know if it can work properly with a PWM (square wave with DC offset) excitation?

If it's capacitive then it will block the DC so that's not a problem, but you should be trying to get the peak to peak voltage to get the best indication of impedance. And yeah you'd have to measure at just the right time to find that.

If it's a resistive type then it mightn't like having a DC bias and it might degrade over time. The resistive types often specify ac operation because polarization can damage them. It shouldn't be so critical exactly when you sample the voltage for the resistive type, as long as the PWM excitation isn't in/near transition.