Timer1 Quadrature Signal Generator - Signals are not the same frequency

Hello! I’m still kind of new to Arduino but I’ve been trying to generate a quadrature signal. I was following the same structure that cattledog had provided in 2015: https://forum.arduino.cc/index.php?topic=342499.0

However, with my implementation of the interrupts, it isn’t generating the same signal frequency once I try to set the frequency above 20kHz. They are still toggling, however i.e signal B would be toggled at a low frequency of 5kHz while signal A would have a frequency of 43kHz. This is through changing the value of ICR1. Any suggestions would help!

const int signalA = 9;                               // Set signal A for pin 9
const int signalB = 10;                              // Set signal B for pin 10

boolean toggleA = 0;                                 // Toggle flag for signal A
boolean toggleB = 0;                                 // Toggle flag for signal B


void setup(){
  
  pinMode(signalA,OUTPUT);                           // Initialize all pins to be outputs
  pinMode(signalB,OUTPUT);
  
  cli();                                             // Stop all interrupts
  
  TCCR1A = 0;                                        // Set timer
  TCCR1B = 0;
  TCNT1 = 0;                                         // Set counter to 0

  ICR1 = 199;                                        // Set ICR1 = [16Mhz / (desired frequency * prescaler)] - 1. This is a full wave of 10kHz
  OCR1A = ICR1-1;                                    // Set Compare Match Register A to 
  OCR1B = OCR1A/2;                                   // set Compare Match Register B to 
  TCCR1B |= (1 << CS11);                             // Set prescaler to 8
  TIMSK1 |= (1 << OCIE1A) | (1 << OCIE1B);           // Enable Timer Compare Interrupt A & B

  TCCR1B |= _BV(WGM13) | _BV(WGM12);                 // CTC mode with ICR1
  TCCR1A = _BV(COM1A0) | _BV(COM1B0);                // Toggle OCR1A/OCR1B on compare match

  
  sei();                                             // Allow interrupts
}

ISR(TIMER1_COMPA_vect){                              // Interrupt handler. Toggle signalA
 if (toggleA){
  digitalWrite(signalA, HIGH);
  toggleA = 0;
 }
 else{
  digitalWrite(signalA, LOW);
  toggleA = 1;
 }
}


ISR(TIMER1_COMPB_vect){                              // Interrupt handler. Toggle signalB
 if (toggleB){
  digitalWrite(signalB, HIGH);
  toggleB = 0;
 }
 else{
  digitalWrite(signalB, LOW);
  toggleB = 1;
 }
}
void loop() {
}
TCCR1A = _BV(COM1A0) | _BV(COM1B0);

You have enabled the hardware toggle of the output pins. You should not need to toggle the output in an interrupt handler. My thinking is that the interrupt handler and the hardware toggle are interfering with each other.

cattledog:

TCCR1A = _BV(COM1A0) | _BV(COM1B0);

You have enabled the hardware toggle of the output pins. You should not need to toggle the output in an interrupt handler. My thinking is that the interrupt handler and the hardware toggle are interfering with each other.

Thank you for the tip (and for writing the code 5 years ago)! Unfortunately, it is still not giving the same frequency. I have attached a result of trying to aim for 50kHz (with ICR1 set as 39)

Edit: I have no idea why the photo won't upload so here is a link instead:

The code posted 5 years ago uses no interrupts. Hardware outputs only. The interrupt latency of several microseconds (even with an empty interrupt handler) may be creating issues at the higher frequencies. Why are you using them?

//Quadrature signal generator two outputs offset 90 degrees
//emulate rotary encoder
//Timer 1 CTC with ICR1 TOP and

void setup() {

  pinMode(9, OUTPUT); //output A
  pinMode(10, OUTPUT); //output B

  TCCR1A = 0; //clear timer registers
  TCCR1B = 0;
  TCNT1 = 0;
  GTCCR |= 1 << PSRASY; //reset prescaler

  //ICR1 and Prescaler sets frequency
  //no prescaler .0625 us per count @ 16mhz
  //prescaler 8 .5 us per count

  TCCR1B |=  _BV(CS11); // prescaler 8
  //TCCR1B |= _BV(CS10); //no prescaler

  //counts are zero indexed 2edges per ICR1 period
  //numerical values for prescaler 8.
  //e.g. 10k period give 20k encoder counts

  ICR1 = 199;//10k ICR1 period  20k encoder counts
  //ICR1 = 99; //20k ICR1 period 40k encoder counts
  //ICR1 = 49; //40K ICR1 period 80k encoder counts
  //ICR1 = 46; //42.5K ICR1 period 85k encoder counts
  //ICR1 = 41; //47.5k ICR1 period 95K encoder counts
  //ICR1 = 39; //50k ICR1 period 100k encoder counts
  //ICR1 = 29; //66.6K ICR1 period 133k encoder counts
  //ICR1 = 19; //100k ICR1 period 200k encoder counts
  
  OCR1A = ICR1 - 1; //two different pulse widths almost 100% duty cycle
  OCR1B = OCR1A / 2; //offset by half period

  TCCR1B |= _BV(WGM13) | _BV(WGM12); //CTC mode with ICR1
  TCCR1A = _BV(COM1A0) | _BV(COM1B0); //Toggle OC1A/OC1B on compare match
}
void loop () {}

I was using the code to create 2 signals with one 90 degrees out of phase to simulate a quadrature encoder so that every rising edge would toggle an LED. I haven’t implemented that portion yet since I am still trying to figure out how to simulate the two signals. But I see: I had tried to use other methods but unfortunately it’s the interrupt handler being unable to go to the higher frequencies. I had another code written with only using one compare match register, but I believe that created another issue with the timer interrupt. Here is the other example I had used below:

const int signalA = 9;              // Set Signal A for pin 9
const int signalB = 10;             // Set Signal B for pin 10
volatile byte count = 0;            // Timer1 overflow interrupt vector handler

void setup(){
  
  pinMode(signalA,OUTPUT);          // Initialize all pins to be outputs
  pinMode(signalB,OUTPUT);

  
  cli();                            // Stop all interrupts
  
  TCCR1A = 0;                       // Set timer
  TCCR1B = 0;
  TCNT1 = 0;
  OCR1A = 228;

  TCCR1B  |= (1 << WGM12);          // Turn on CTC Mode
  TCCR1B |= (1 << CS10);            // Set prescaler to 1
  TIMSK1 |= (1 << OCIE1A);           // Enable Timer Compare Interrupt
  
  sei();                            // Allow interrupts
}


ISR(TIMER1_COMPA_vect){
 switch (count){                    // Based on the count, set the channels accordingly to the switch case
  case 0:
    digitalWrite(signalA,HIGH);
    break;
  case 1:
    digitalWrite(signalB,HIGH);
    break;
  case 2:
    digitalWrite(signalA,LOW);
    break;
  case 3:
    digitalWrite(signalB,LOW);
    break;
  }
  count++;                          // Increment count
  if (count >= 4) {                 // Reset count
    count = 0;
  }
}

void loop() {
}

cattledog:
The code posted 5 years ago uses no interrupts. Hardware outputs only. The interrupt latency of several microseconds (even with an empty interrupt handler) may be creating issues at the higher frequencies. Why are you using them?

//Quadrature signal generator two outputs offset 90 degrees

//emulate rotary encoder
//Timer 1 CTC with ICR1 TOP and

void setup() {

pinMode(9, OUTPUT); //output A
 pinMode(10, OUTPUT); //output B

TCCR1A = 0; //clear timer registers
 TCCR1B = 0;
 TCNT1 = 0;
 GTCCR |= 1 << PSRASY; //reset prescaler

//ICR1 and Prescaler sets frequency
 //no prescaler .0625 us per count @ 16mhz
 //prescaler 8 .5 us per count

TCCR1B |=  _BV(CS11); // prescaler 8
 //TCCR1B |= _BV(CS10); //no prescaler

//counts are zero indexed 2edges per ICR1 period
 //numerical values for prescaler 8.
 //e.g. 10k period give 20k encoder counts

ICR1 = 199;//10k ICR1 period  20k encoder counts
 //ICR1 = 99; //20k ICR1 period 40k encoder counts
 //ICR1 = 49; //40K ICR1 period 80k encoder counts
 //ICR1 = 46; //42.5K ICR1 period 85k encoder counts
 //ICR1 = 41; //47.5k ICR1 period 95K encoder counts
 //ICR1 = 39; //50k ICR1 period 100k encoder counts
 //ICR1 = 29; //66.6K ICR1 period 133k encoder counts
 //ICR1 = 19; //100k ICR1 period 200k encoder counts
 
 OCR1A = ICR1 - 1; //two different pulse widths almost 100% duty cycle
 OCR1B = OCR1A / 2; //offset by half period

TCCR1B |= _BV(WGM13) | _BV(WGM12); //CTC mode with ICR1
 TCCR1A = _BV(COM1A0) | _BV(COM1B0); //Toggle OC1A/OC1B on compare match
}
void loop () {}