Using Timer Interrupts

I have been playing around with Timer Interrupts to generate a square wave on a pin. I have got that working, but now I want to be able to vary the length of the pulse but keep the frequency the same. For example, I want to create a pulse of 100-200us, and have a frequency of 1000Hz. Here is my code:

int channels = 0;

#define REFRESH 16000 //1000us/0.0625us = 16,000

void setup() {
  // Reset Timer 3 Control Register to 0
  TCCR3A = 0;

  // Set prescaler to 1
  TCCR3B &= ~(1 << CS32); //0
  TCCR3B &= ~(1 << CS31); //0
  TCCR3B |= (1 << CS30);  //1

  //Reset Timer 3 and set compare value
  TCNT3 = 0;

  // Enable Timer 3 compare interrupt
  TIMSK3 = (1 << OCIE3A);

  //Enable global interrupts
  sei();

  pinMode(4, OUTPUT);
}

void loop() {
  delay(100);
}

ISR(TIMER3_COMPA_vect) {

  if (channels == 1) {
    digitalWrite(4, HIGH);
    OCR3A = 100 / 0.0625;
    channels = 0;
  }

  if (channels == 0) {
    digitalWrite(4, LOW);
    OCR3A = REFRESH;
    channels = 1;
    TCNT3 = 0;
  }
}

The servo library does something similar, and I found this is the code for the library:

static inline void handle_interrupts(timer16_Sequence_t timer, volatile uint16_t *TCNTn, volatile uint16_t* OCRnA)
{
  if( Channel[timer] < 0 )
    *TCNTn = 0; // channel set to -1 indicated that refresh interval completed so reset the timer
  else{
    if( SERVO_INDEX(timer,Channel[timer]) < ServoCount && SERVO(timer,Channel[timer]).Pin.isActive == true )
      digitalWrite( SERVO(timer,Channel[timer]).Pin.nbr,LOW); // pulse this channel low if activated
  }

  Channel[timer]++;    // increment to the next channel
  if( SERVO_INDEX(timer,Channel[timer]) < ServoCount && Channel[timer] < SERVOS_PER_TIMER) {
    *OCRnA = *TCNTn + SERVO(timer,Channel[timer]).ticks;
    if(SERVO(timer,Channel[timer]).Pin.isActive == true)     // check if activated
      digitalWrite( SERVO(timer,Channel[timer]).Pin.nbr,HIGH); // its an active channel so pulse it high
  }
  else {
    // finished all channels so wait for the refresh period to expire before starting over
    if( ((unsigned)*TCNTn) + 4 < usToTicks(REFRESH_INTERVAL) )  // allow a few ticks to ensure the next OCR1A not missed
      *OCRnA = (unsigned int)usToTicks(REFRESH_INTERVAL);
    else
      *OCRnA = *TCNTn + 4;  // at least REFRESH_INTERVAL has elapsed
    Channel[timer] = -1; // this will get incremented at the end of the refresh period to start again at the first channel
  }
}

It seems like they are only using OCRnA, and just redefining it for the next servo pulse. Once all the servo pulses are sent it then resets the timer to 0, and starts the process again.

When I look at pin 4 on the oscilloscope it does a frequency of 1000Hz, but the pulse is only about 4us. I am obviously doing something wrong in my code and could use a little guidance.

Which of the many processors are you using?

I am using an Arduino Micro which uses the ATMega32u4, which has a 16-bit timer on Timer 3.

Using the timer in the normal mode is not correct.

You are better off in a clear timer compare mode which is CTC to OCR3A, which will set the timer repeat frequency.

Then use the A compare match isr at the top value to the output pin turn on and set the value of the OCR3B compare match. Use the OCR3B compare match to turn the pin off. Increasing the OCR3B compare match value will increase the duty cycle.

#define REFRESH 16000 //1000us/0.0625us = 16,000

void setup() {
  // Reset Timer 3 Control Registers clear ide presets
  TCCR3A = 0;
  TCCR3B = 0;
  TCCR3B |= (1 << CS30);  //Set prescaler 1
  TCCR3B |= (1 << WGM32); //Set Mode4 CTC to OCR3A
  OCR3A = REFRESH; //1ms 1000Hz period
  OCR3B = REFRESH / 2; // initial setting at 50 %
  TCNT3 = 0;
  // Enable Timer 3 A and B compare interrupts
  TIMSK3 = (1 << OCIE3A) | (1 << OCIE3B);
  //Enable global interrupts
  sei();

  pinMode(4, OUTPUT);
}

void loop() {
}

ISR(TIMER3_COMPA_vect) {
  digitalWrite(4, HIGH);//turn output pin on at TOP
  OCR3B = REFRESH / 4; //set turn off on B match value, e.g. 25% duty cycle
}

ISR(TIMER3_COMPB_vect) { //increasing duty cycle with OCR3B
  digitalWrite(4, LOW);
}