So I have found my next problem which is pretty strange. As written in other questions I am trying to sample the ADC with the timer 2.
All of that works fine and the results are as expected. However now there is another problem that I do not understand. I use interrupts to switch on the external hardware a few clock cycles before measuring, since it takes about 4µs to settle. Then I start an ADC conversion through entering the ADC sleep mode. After the sample & hold circuit was activated (13.5 ADC clocks) I deactivate the external hardware with another interrupt from the timer.
After the conversation is over I then enter the sleep mode PWR_SAFE in between the measurements.
However that causes the ADC to read low at random intervals as you can see in the picture.
If I use a lighter sleep mode, for example ADC sleep mode with the ADC disabled, or no sleep it works fine and those glitches do not happen.
Has anyone experienced that before? Any ideas how to fix it? I can not find anything in the datasheet that would explain this behavior. I also tried to not having the interrupt enabled to deactivate the hardware but deactivate it in the ADC interrupt however that did not change the behavior.
Edit:
I measured the input with an oscilloscope so I know it is a measurement error. Also lowering the ADC prescaler does not help.
pylon
February 6, 2020, 6:40pm
2
Don't you think you should post your code? (and don't forget to use code tags!)
Well if it helps. But I thought maybe someone has experienced the same problem before.
Right now it is written that it uses the IDLE and disables everything else that is unnecessary.
#include <Arduino.h>
#include <avr/sleep.h>
#include <avr/power.h>
volatile uint8_t _val;
volatile uint8_t _curVal = 0;
volatile uint8_t _sampleNow = 0,
_adcSleep = 0;
volatile uint8_t _selectedDetector;
void sampleData();
void enableDetector();
void disableDetector();
ISR(ADC_vect)
{
_val = ADCH;
_curVal = 1;
}
ISR(TIMER2_OVF_vect)
{
disableDetector();
if (_selectedDetector)
{
TCNT2 = 130;
}
else
{
TCNT2 = 101;
}
}
ISR(TIMER2_COMPA_vect) //New Measurement
{
enableDetector();
}
ISR(TIMER2_COMPB_vect) //Sample & Hold already happend
{ //Deactivate to reduce power
sampleData();
}
void setupADC()
{
if (_selectedDetector == 1)
ADMUX = bit(REFS0) | bit(ADLAR) | bit(MUX2) | bit(MUX1); //Detector 1 = ADC Chanel 6, Left Adjusted, AVcc as Ref
else
ADMUX = bit(REFS0) | bit(ADLAR) | bit(MUX2) | bit(MUX1) | bit(MUX0); //Detector 2 = ADC Chanel 7, Left Adjusted, AVcc as Ref
ADCSRA = bit(ADEN) | bit(ADIE) | bit(ADPS1) | bit(ADPS0); //Interrupt & ADC Enable & Prescaler 8
}
void setupTimer2()
{
cli(); //Disable Interrupts globally
TCCR2B = 0; //Set control register to 0
TCCR2A = 0;
if (_selectedDetector)
{
OCR2A = 150;
OCR2B = 251;
TCCR2B = 1 << CS21 | 1 << CS20; //Perscaler 32 -> 8MHz / 32 = 250kHz
}
else
{
OCR2A = 150;
400Hz = 156:
OCR2B = 253;
TCCR2B = 1 << CS22 | 1 << CS20; //Perscaler 128 -> 8MHz / 64 = 62,5kHz
}
sei();
}
void sampleData()
{
if (_selectedDetector)
{
_adcSleep = 1;
}
else if (_sampleNow) //Measure every second interrupt
{
_sampleNow = 0;
_adcSleep = 1;
}
else
{
_sampleNow = 1;
_adcSleep = 2;
}
}
void enableDetector()
{
if (_selectedDetector)
{
PORTD = PORTD | 1<<PD3; //Detector 1 Enable
}
else if (_sampleNow) //Measure every second interrupt
{
PORTC = PORTC | 1<<PC0; //Detector 2 Enable
PORTB = PORTB | 1<<PB1; //IR LED Enable
}
_adcSleep = 2;
}
void disableDetector()
{
if (_selectedDetector == 1)
{
PORTD &= ~ (1<<PD3); //Detector 1 Disable
}
else
{
PORTC &= ~ (1<<PC0); //Detector 2 Disable
PORTB &= ~ (1<<PB1); //IR LED Disable
}
_adcSleep = 2;
}
void startSampleing(void)
{
if (_selectedDetector)
{
TCNT2 = 130;
}
else
{
TCNT2 = 99;
}
TIMSK2 = 1<<OCIE2A | 1<<OCIE2B | 1<<TOIE2; //Enable Comp A & Comp B Interrupt
}
void enterSleepADC(void)
{
ADCSRA |= bit(ADEN);
set_sleep_mode(SLEEP_MODE_ADC);
power_spi_disable();
power_twi_disable();
power_timer0_disable();
power_timer1_disable();
sleep_enable();
sleep_mode();
sleep_disable();
power_all_enable();
}
void enterSleepDeep(void)
{
ADCSRA &= ~bit(ADEN);
set_sleep_mode(SLEEP_MODE_IDLE);
power_adc_disable();
power_spi_disable();
power_twi_disable();
power_timer0_disable();
power_timer1_disable();
sleep_enable();
sleep_mode();
sleep_disable();
power_all_enable();
}
void setup()
{
/* Port Setup */
pinMode(9,OUTPUT); //IR LED
pinMode(3,OUTPUT); //Detector 1 Enable
pinMode(A0,OUTPUT); //Detector 2 Enable
pinMode(A6,INPUT); //Detector 1 Input
pinMode(A7,INPUT); //Detector 2 Input
pinMode(A2,OUTPUT); //CS Poti
pinMode(A1,OUTPUT); //Shutdown Poti
pinMode(2,OUTPUT); //Writecontrol EEPROM
pinMode(A3,OUTPUT); //Select EEPROM
digitalWrite(A1,HIGH);
/* ADC Setup */
_selectedDetector = 0;
setupADC();
/* Timer 2 Setup */
setupTimer2();
startSampleing();
}
void loop()
{
uint16_t i;
if (_adcSleep == 1)
{
_adcSleep = 0;
enterSleepADC();
} else if (_adcSleep == 2)
{
_adcSleep = 0;
enterSleepDeep();
}
else if (_curVal)
{
Serial.begin(500000);
Serial.println(_val);
_curVal = 0;
for(i=0; i < 100;i++)
asm("nop");
enterSleepDeep();
}
}
pylon
February 7, 2020, 5:39pm
4
I guess that code:
for(i=0; i < 100;i++)
asm("nop");
is just a bad replacement for
Serial.flush();
Your code doesn't compile, so I doubt that it shows the described behavior.
Have you tried disabling timer 0 completely? You don't use it and it might have precedence over the ADC interrupt influencing the measured value.