I have mad an timer interrupt where ISR should when activated read analog value on analog pin 5 and if the value is in a certain range "return" 5 and if it is not, the it should "return" 0. In a way it should only "return" 5 only if the value went from some NUMBR to the number in range. In my case it is always returning 0. I'm controlling the value via push buttons and resistors. I'm certain that there is a mistake in code.
#include <TimerOne.h>
volatile int c;
volatile int old_reading = 0;
volatile int reading;
volatile int printaj;
void setup () {
pinMode (A5, INPUT_PULLUP);
Timer1.initialize(125);
Timer1.attachInterrupt(function);
Serial.begin(9600);
}
void function () {
c = analogRead(5);
if (c > 251 && c < 254) {
reading = 5;
} else {
reading = 0;
}
if (reading == old_reading) {
old_reading = reading;
printaj = 0;
} else {
old_reading = reading;
printaj = reading;
}
}
void loop () {
Serial.println(printaj);
}
125us is too short a time I think, analogRead takes nearly that long and IIRC twice as long
on the first call. You should call analogRead once before installing the interrupt, and try a
more conservative period for timer1.
You'll find that you are spending most time in the ISR and the length of time in the ISR
will prevent the Serial library from working properly.
You could try re-enabling interrupts in your ISR before you call analogRead(), although
this risks a re-entrant call to the ISR.
[ on reflection this is a situation where you could just start an analog conversion in the
timer1 ISR and use the ADC completion interrupt to process the result - would take
some reading up ]
MarkT:
125us is too short a time I think, analogRead takes nearly that long and IIRC twice as long
on the first call. You should call analogRead once before installing the interrupt, and try a
more conservative period for timer1.
You'll find that you are spending most time in the ISR and the length of time in the ISR
will prevent the Serial library from working properly.
You could try re-enabling interrupts in your ISR before you call analogRead(), although
this risks a re-entrant call to the ISR.
[ on reflection this is a situation where you could just start an analog conversion in the
timer1 ISR and use the ADC completion interrupt to process the result - would take
some reading up ]
Could you please point me out to the topics which I should investigate.
Look at the source to analogRead - this sets up a conversion and then busy-waits
for it to finish - the first part is what you need to do on the timer interrupts.
look at the chip datasheet for how to configure conversion-complete interrupts on the ADC,
and write a handler to pull the result out at that point and use it.
The ATmega328 datasheet says 15kSPS at Maximum Resolution, up to 76.9kSPS at lower resolutions. 125µs should be possible, that'd be 8kSPS. Here is a sketch that samples at 200µs intervals, it should be easily modified: http://forum.arduino.cc/index.php?topic=171226.msg1276869#msg1276869
Timer1 can be used to trigger an analog conversion when the timer overflows. The TimerOne library makes it easy to set up Timer1's overflow interrupt, but it doesn't do anything with the ADC. Here's a way to use that feature:, using the TimerOne library:
Select the ADC input pin you want to use. You can do that by writing to register ADMUX, and you'll have to be careful to set the REFS0 and REFS1 bits to select the proper analog reference. Looking at the code for analogRead, it looks like you can select that pin by executing analogRead() on the pin before you start acquiring data. It looks like the ADC multiplexer is set in that routine and left alone. If you leave it alone afterward, it shouldn't change.
Execute an analogRead(), to get past the extra-long first conversion of the ADC. That can be the same analogRead() that sets the ADC input pin, if you like.
Create and attach a Timer1 interrupt service routine that doesn't do anything. That ISR's only purpose will be to reset the Timer1 overflow flag, bit TOV1 in register TIFR1.
Create an ISR for the ADC that collects the ADC reading, stores it in a golbal variable, sets a global flag, and exits. ISR's are best that do the least possible amount of work.
Enable the ADC, enable its auto-trigger, enable its interrupt, and select Timer1 overflow as the trigger source. That'll involve writing to the ADC control and status registers ADCSRA and ADCSRB.
I'd recommend waiting for the first ADC conversion to complete, by waiting for the flag from the ADC, and then resetting the flag. You might even want to wait for it to complete two conversions, to be sure that the Timer has fully reacted to the change of its rollover value, and the ADC has synced with it.
in loop(), watch the ADC flag. When it goes active, reset it, process the ADC data, and wait for the next one.
You'll have to be sure to set the Timer1 rollover time high enough to allow for the ADC conversion to complete, and for the ADC ISR to execute afterward. The Timer1 interrupt will execute while the ADC conversion is going on, so, unless you make it a complex ISR, it shouldn't interfere with reading the ADC. You should also allow time for the Timer0 overflow ISR to execute, too, since it may be active when the ADC completes its conversion, and the ADC ISR won't execute until that ISR completes.
You can find out how to do all those things in the datasheet, in the descriptions of Timer1 and the ADC.
An alternative is to refrain from enabling the ADC interrupt, don't provide an ADC ISR, and read the ADC and set the flag in the Timer1 ISR. You still need to consider the relative timing between the ADC and Timer1, since the ADC will write over the result of the last conversion when the new conversion completes. You'll have an ADC conversion time to let other ISR's complete, respond to the Timer1 overflow interrupt, and read the ADC; after the conversion time elapses, the previous reading will be gone.
The code that Jack Christensen references in Reply #4 manipulates Timer1 directly, without help from the TimerOne library. I like that better, since it gives you precise control over the Timer1 functions. You may prefer to let the TimerOne library handle those details for you.