Hi, I'm new on Arduino and the forum, but not on programming microcontrollers
I've an Arduino mini and I want to trigger the adc and read the value when the Timer0 reaches the compare value.
I've found one way which is:
interrupt generated by Timer0
inside the ISR of timer 0 read the value of the adc and do my operations
the thing I don't like is that I need to wait (stop the processor) until the ADC finishes the conversion (with something like a while(true) )
but I think there is another way which is:
trigger from timer0 (set by default on the register of the adc)
interrupt generated by the adc when finishes the conversion
read the value inside the ISR of the adc
first of all, is it possible?
last but not least, how do I write code inside the post?
See the datasheet - you'll have to do this manually (ie, writing your own ISR), but it doesn't look hard.
You can make it auto-trigger off of Timer0 or Timer1 overflow or compare matches - I recommend Timer1 so you don't have to break millis() and delay(). This also doesn't require touching the ISR for the timers.
You write the ADC ISR (I forget what the vector is named) and read the ADC values in there. You would set up the auto trigger source to be the overflow/compare match event you want in ADCSRB, and set the ADIE bit in ADCSRA to enable the ADC interrupt, which fires on completed conversion.
This what I use in a project for setting up the ADC in timer triggered mode for a few AVR types, but using timer1. Maybe you can adapt it for your purpose.
. . .
ISR(ADC_vect) {
// handle register ADC here
}
. . .
// initialisation
cli() ;
// Initialise Timer1
TCCR1A = 0;
TCCR1B = _BV(CS10) | // Bit 2:0 – CS12:0: Clock Select = no prescaler
_BV(WGM13) | // WGM 12 = CTC ICR1 Immediate MAX
_BV(WGM12); // WGM 12 ditto
ICR1 = (((F_CPU + FREQUENCY_CORRECTION)) / 19200) - 1; // was 9600
#if defined(_xATTINY84)
{
ADMUX = 0x00 ; // input ADC0 (PA0) Vref=Vcc
DIDR0 |= _BV(0); // DIDR0 Digital Input Disable Register 0
ADCSRB = _BV(ADTS2) | // Bit 2:0 – ADTS[2:0]: ADC Auto Trigger Source
_BV(ADTS1) | // Timer/Counter1 Capture Event
_BV(ADTS0) | //
_BV(ADLAR) ; // Left adjust
}
#elif defined(_xATTINY841)
{
ADMUXA = 0x00 ; // input ADC0 (PA0)
ADMUXB = 0x00 ; // Vref = Vcc, gain = 1
DIDR0 |= _BV(0); // DIDR0 Digital Input Disable Register 0
ADCSRB = _BV(ADTS2) | // Bit 2:0 – ADTS[2:0]: ADC Auto Trigger Source
_BV(ADTS1) | // Timer/Counter1 Capture Event
_BV(ADTS0) | //
_BV(ADLAR) ; // Left adjust
}
#elif defined(_xATMEGA328)
{
// Analog Port PC0 (pin A0 )
// V0_02 ADLAR and prepare also for reading A1
ADMUX = _BV(REFS0) | _BV(ADLAR) ; // Fixed AVcc reference voltage for ATMega328P !!
DIDR0 |= _BV(ADC0D); // DIDR0 Digital Input Disable Register 0
DIDR0 |= _BV(ADC1D); // DIDR0 Digital Input Disable Register 1
ADCSRB = _BV(ADTS2) | // Bit 2:0 ADTS[2:0]: ADC Auto Trigger Source
_BV(ADTS1) | // Timer/Counter1 Capture Event
_BV(ADTS0); //
}
#else
# error unknown processor type
#endif
ADCSRA = _BV(ADEN) | // Bit 7 ADEN: ADC Enable
_BV(ADSC) | // Bit 6 ADSC: ADC Start Conversion
_BV(ADATE) | // Bit 5 ADATE: ADC Auto Trigger Enable
_BV(ADIE) | //
_BV(ADPS1); // Bits 2:0 ADPS[2:0]: ADC Prescaler Select Bits (div 4 ) // V0_02 div2 NOK, div4 OK, div16 OK
sei() ;
antoniominighin:
last but not least, how do I write code inside the post?
Create your program (sketch) in the Arduino IDE, and then carry out the following steps: (1) Select the entire codes of the IDE by pressing Cntrl+A. (2) Copy the selected codes of Step-1. (3) Click on the symbol </> (called code tags) of the Toolbar of your posting window. Press Cntrl+V.
void setup()
{
// put your setup code here, to run once:
}
void loop()
{
// put your main code here, to run repeatedly:
}
DrAzzy:
See the datasheet - you'll have to do this manually (ie, writing your own ISR), but it doesn't look hard.
You can make it auto-trigger off of Timer0 or Timer1 overflow or compare matches - I recommend Timer1 so you don't have to break millis() and delay(). This also doesn't require touching the ISR for the timers.
You write the ADC ISR (I forget what the vector is named) and read the ADC values in there. You would set up the auto trigger source to be the overflow/compare match event you want in ADCSRB, and set the ADIE bit in ADCSRA to enable the ADC interrupt, which fires on completed conversion.
void adc_setup()
{
cli();
ADMUX |= (1 << REFS0); // use AVcc as the reference
ADMUX |= (1 << ADLAR); // left aligned, better for 8 bit resolution
ADCSRA |= (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0); // 128 prescale for 16Mhz
ADCSRA |= (1 << ADATE); // Set this bit to enable trigger source
ADCSRB |= (1 << ADTS0) | (1 << ADTS1); // Triggered by Timer0 compare match A
PRR |= (0 << PRADC); // Power Reduction Register's PRADC bit must be disabled before starting the conversion
ADCSRA |= (1 << ADEN); // Enable the ADC
ADCSRA |= (1 << ADIE); // Enable Interrupts
sei();
}
void timer0_setup()
{
TCCR0A |= (1 << WGM01); // Set the Timer Mode to CTC
OCR0A = 0xF9; // Set the value that you want to count to
TIMSK0 |= (1 << OCIE0A); // Set the ISR COMPA_vect. "NOTE!"
sei(); // Enable interrupts
TCCR0B |= (1 << CS01); // Set prescaler to 8 and start the timer
}
Fisrt:
(inside the ISR of the ADC_vect) do i have to clear both the interrupt flags OCF0A of the TIFR and the ADIF of the adc?
Second:
I've marked a line in the timer_setup() with "NOTE!" because I'm a bit confuse on the use of the 2 register TIFR and TIMSK
You said that I don't need to enable the interrupt of the timer, but does the adc trigger recive the OCF0A edge also if the OCEI0A of TIMSK is not 1?
does this mean that the TIMSK only enable the interrupt for the processor?
hope are not silly doubts and thanks to everyone for the time spent helping me
You have to clear only the timer interrupt flag in ISR(ADC_vect). Something like this from an old example of mine:
ISR(ADC_vect) {
. . .
// handle register ADCH here (you are left adjusting the result)
. . .
#if defined(_xATTINY85)
{
TIFR = _BV(OCF0B) ; // reset interrupt flag
}
#elif defined(_xATMEGA328) || defined(_xATTINY84)
{
TIFR1 = _BV(ICF1);
}
#else
# error unknown processor type
#endif
}
I struggled for some time using timer0 for this (actually with ATTiny85 where I was forced to use timer 0 for auto triggering the ADC) before moving millis() etc. to timer 1 and echo what DrAzzy said about not recommending it. Although you are using an ATMega328p you'll face similar issues.
This may also help if you continue to use timer 0: Reseting ATTiny85 timer so millis and delay will work properly - Programming Questions - Arduino Forum
Other than that, just try it and see what happens.
DrAzzy:
As I read the datasheet, no, you don't need to enable the interrupt, unless you're doing something in an ISR tied to it. And you're not.
okay I've tried different time to trigger ADC from Timer/Counter1.
I failed.
I didn't know if the issue was DUE to the TIMER or the ADC.
So I've tried to do some test.
The first is on Timer1 and is a simple blink led test triggered by CTC mode.
I've found something strange on TCCR1B
TCCR1B |= (1 << CS11)|(1 << CS10); this works
TCCR1B |= (1 << CS12); this doesn't!!
Can some one tries with different tests?
boolean on = 1;
int waitstates = 20;
int i = 0;
void setup() {
// put your setup code here, to run once:
timer1_setup();
pinMode(LED_BUILTIN, OUTPUT);
}
void loop() {
// put your main code here, to run repeatedly:
}
ISR(TIMER1_COMPA_vect)
{
TIFR1 &= ~(1 << OCF1A);
if(i == waitstates)
{
i = 0;
digitalWrite(LED_BUILTIN, on);
on = !on;
}
else i++;
}
void timer1_setup()
{
cli(); // Disable interrups
OCR1AH = 0xFF; // Set the MSBvalue that you want to count to
OCR1AL = 0xFF; // Set the LSBvalue that you want to count to
TCCR1B |= (1 << WGM12); // Set the Timer Mode to CTC on OCR2A
TIMSK1 |= (1 << OCIE1A); // Set the ISR TIMER1_COMPA_vect
sei(); // Enable interrupts
TCCR1B |= (1 << CS12); // Set prescaler to 1024 and start the timer
}
I continue by my self to take update this topic, yeah!
I've found in the datasheet that Timer0 and Timer1 SHARE the same PRESCALER
maybe its a problem.. but I don't know if this could generate an error in my code.
I've tried to blink one time the led inside the ISR of TIMER1_COMPA_vect.
it seems that with cs12 bit on it does't reach the interrupt
You seem to be having some basic difficulty even with the timer configuration.
At what frequency do you want to trigger the ADC (ie samples per second) a and which channel do you want to read ? Is 8 bit resolution enough or do you need the full 10 bit ? Maybe also say what the application is.
I could quickly put something together which you can test in isolation but the code I supplied in #2 was more or less complete apart from the content of the ISR which also needs the reset of TIFR1 when you have processed the sample.
In your last example, you should declare the variables which are set in the ISR as volatile otherwise the compiler may optimise them out of existence. Also use unsigned long instead of int for 'i' if the timer is running at any speed.
For testing, it is probably better to increment counters in the ISR and occasionally print out their values in the loop() rather than using a LED.
6v6gt:
You seem to be having some basic difficulty even with the timer configuration.
At what frequency do you want to trigger the ADC (ie samples per second) a and which channel do you want to read ? Is 8 bit resolution enough or do you need the full 10 bit ? Maybe also say what the application is.
I could quickly put something together which you can test in isolation but the code I supplied in #2 was more or less complete apart from the content of the ISR which also needs the reset of TIFR1 when you have processed the sample.
In your last example, you should declare the variables which are set in the ISR as volatile otherwise the compiler may optimise them out of existence. Also use unsigned long instead of int for 'i' if the timer is running at any speed.
For testing, it is probably better to increment counters in the ISR and occasionally print out their values in the loop() rather than using a LED.
I've found that Arduino initialize the timer1 in PWM mode setting WGM1n so I need to reset TCCR1B before setting my WGM mode.
before I try and report some test on the adc can u explain me something more on when to use volatile variabile?
OCR1B = ticks; // compare value
OCR1A = ticks; // Set CTC TOP value, must be >= OCR1B
// start timer, give it a clock
TCCR1B |= (( 1 << CS10 ) | ( 1 << CS12 )) ; // Fcpu/1024, 64us tick @ 16 MHz
} // TimerOneInit
ISR( ADC_vect ) // ADC conversion complete
{
ADCresult = ADC; // Read the ADC
adcFlag = true; // set flag
TIFR1 = ( 1<<OCF1B ); // clear Compare Match B Flag
} // ISR
Hi thanks for your help.
in the ISR why do u set TIFR1 OCF1B bit to 1 instead of clear it?
Thinking about what I really need to do, it's impossible to trigger the adc by the timer at high speed, because the adc must be clocked at 125 or 250 khz, it takes at least 13 clock cycles so the timer can run at a maximum frequency of 10 or 20khz.
maybe 6v6gt was right, I should have thought about the frequency of the timer before
in the ISR why do u set TIFR1 OCF1B bit to 1 instead of clear it?
Thinking about what I really need to do, it's impossible to trigger the adc by the timer at high speed, because the adc must be clocked at 125 or 250 khz, it takes at least 13 clock cycles so the timer can run at a maximum frequency of 10 or 20khz.
maybe 6v6gt was right, I should have thought about the frequency of the timer before
For higher speeds than are possible with the AVR inbuilt ADCs, start looking at external SPI ADCs like MCP3002 (or better) which can handle sample rates of up to 200K SPS. I have used this is in a ESP8266 project where the inbuilt ADC is too weak for my purpose.