I have defined my code to generate an interrupt over each 5μs on CTC mode using timer1 but the output shows a time of ~15μs.
Following the equation to set the counting number to achieve the desired time
(# timer counts + 1) = (target time) / (timer resolution)
I've got OCR1A = 79 for a desired time of 5μs using no prescaler and a source clock of 16MHz on Arduino Uno.
My code is as follows:
// avr-libc library includes
#include <avr/io.h>
#include <avr/interrupt.h>
#define LEDPIN 9
void setup()
{
pinMode(LEDPIN, OUTPUT);
digitalWrite(LEDPIN, HIGH);
// initialize Timer1
cli(); // disable global interrupts
TCCR1A = 0; // set entire TCCR1A register to 0
TCCR1B = 0; // same for TCCR1B
// set compare match register to desired timer count:
OCR1A = 79;
// turn on CTC mode:
TCCR1B |= (1 << WGM12);
// Set CS10 bit for no prescaler:
TCCR1B |= _BV(CS10);
// enable timer compare interrupt:
TIMSK1 |= (1 << OCIE1A);
sei(); // enable global interrupts
}
void loop () {}
ISR(TIMER1_COMPA_vect)
{
digitalWrite(LEDPIN, !digitalRead(LEDPIN));
}
This is the output from my logic analyzer:
Output From Logic Analyzer
Is it a limitation of the Arduino or am I missing something?
My idea is to generate a PWM manually to control a servo motor using timer1 and precisely increase the period by 10μs during "on" time and get the desired angle.
After some experiments, I have discovered that my motor (MG996R) has a range from 0.6ms to 2.1ms during "on" time to move from 0 to 180 degrees.
I have tried the Arduino's Servo library but it goes far than 180 degrees (I don't know why). That's why I am building my own code for that purpose.
-
79 clock cycles might no be enough to execute all the ISR code.Gerben– Gerben04/27/2015 20:35:33Commented Apr 27, 2015 at 20:35
-
@Gerben, do you any idea how to solve it instead?bpinhosilva– bpinhosilva04/27/2015 20:52:56Commented Apr 27, 2015 at 20:52
1 Answer 1
I got it working just analyzing time consumption of digitalWrite and digitalRead functions. They took around 13us to execute (each one), so this added up a few extra undesired microseconds to the final period.
Instead, I set up digital output using DDRD for configuring and PORTD for setting the desired output, which results in approximately 1us to set the output value (HIGH or LOW).
I changed the prescaler to 8 and the code below shows how to get a 100kHz wave output with 5us duration of "on" time using timer1 CTC mode.
void setup()
{
DDRD = B00001000; // pin 3 as output
PORTD &= ~_BV(PORTD3); // set 3 as LOW
// initialize Timer1
cli(); // disable global interrupts
TCCR1A = 0; // set entire TCCR1A register to 0
TCCR1B = 0; // same for TCCR1B
// set compare match register to desired timer count:
OCR1A = 9;
// turn on CTC (Clear Timer on Compare Match) mode:
TCCR1B |= (1 << WGM12);
// Set CS10 bit for clk/8 prescaler:
TCCR1B |= _BV(CS11);
// Output Compare A Match Interrupt Enable
TIMSK1 |= (1 << OCIE1A);
sei(); // enable global interrupts
}
ISR(TIMER1_COMPA_vect)
{
PORTD = ~PORTD;
}