Arduino Mega 2560 - Interrupt issue CTC mode

Hello,

I have an issue with the implementation of an interuption.
In order to generate a PWM at a frequency of 1Khz I decided to do it through an interuption in TIMER 4 in CTC mode, see code bellow.

void setup() {   
  Serial.begin(115200);
  cli();
    /* initialize Timer/counter 4 and register value to 0*/
  OCR4A=0;  
  OCR4B=0;  
  OCR4C=0;  
  TCCR4A = 0;
  TCCR4B = 0;
  TCCR4C = 0;
  TCNT4 = 0;


 // TCCR4A = B00010000;
 // TCCR4B = B00001011;

  /* Toggle OC4A on compare match */
  TCCR4A |= (1 << COM4B0);
  
  /* Turn on CTC mode*/
  TCCR4B |= (1 << WGM42);
    
  /* Set Prescaler to 64*/
  TCCR4B |= (1 << CS40);
  TCCR4B |= (1 << CS41);
  
  
OCR4B=116;  

  TIMSK4 |= (1 << OCIE4B);

  sei();
  Serial.print("TCCR4A : ");
  Serial.println(TCCR4A, BIN);
  Serial.print("TCCR4B : ");
  Serial.println(TCCR4B, BIN);
  pinMode(7, OUTPUT);
}

void loop() {
}

ISR(TIMER4_COMPB_vect) {
  TCNT4 = 0;
}

My issue is the fact that when the Timer 4 TCNT4 match the Comparator register OCR4B the interupt is triggered, but the Timer register’4 TCNT4 isn’t reset! This is why I had to do it manually through an interupt routine.

Can someone tell me why it didn’t worked as expected in CTC mode?

Thanks in advance,

MBG60

Up

If you are trying to generate a PWM signal, why is it you are not using one of the many PWM modes?!?

I needed to get a PWM at a frequency of 1Khz, this is why I didn't used any of the PWM modes.

My question is relaed to the deployement of the interuption in CTC mode. Why isn't the register reset to zero automatically when the interupt is triggered?

In order to make it work I had to use a interupt routine and do it manually.

Thanks in advance,

BEN-GELOUNE Mouad

/* Turn on CTC mode*/
  TCCR4B |= (1 << WGM42);

You have set mode 4 which is CTC to OCR4A.

Then you set up a compare interrupt on OCR4B

OCR4B=116; 
TIMSK4 |= (1 << OCIE4B);

The comment here is wrong, this sets a generic toggle for A/B/C

/* Toggle OC4A on compare match */
  TCCR4A |= (1 << COM4B0);

quote from the data sheet

Toggle OCnA/OCnB/OCnC on compare match

It looks like you toggle outputB on compare match, but because you do not set an OCR4A compare match value for the CTC mode the TCNT4 value keeps rising.

In CTC mode, OCRnA is used to set TOP or the freq. OCRnB is then used for duty cycle. With OCR4A/B/C all set to zero you're looping zero to zero with matching at zero, duty cycle at zero, counter reset at zero and interrupt at zero. You need to be a bit more creative with your values. OCR4A needs to be set to give you x counts at your pre-scale in one millisecond. The OCR4B and C and be used to trigger duty cycle interrupts of toggle the OC pins.

If you want PWM at 1 kHz on a 16 MHz Arduino, set TOP to 15999.

/*
  PWM16Begin(): Set up Timer4 for PWM.
  PWM16EnableA(): Start the PWM output on Pin 6
  PWM16EnableB(): Start the PWM output on Pin 7
  PWM16EnableC(): Start the PWM output on Pin 8
  PWM16A(unsigned int value): Set the PWM value for Pin 6.
  PWM16B(unsigned int value): Set the PWM value for Pin 7.
  PWM16B(unsigned int value): Set the PWM value for Pin 8.
*/


// Set 'TOP' for PWM resolution.  Assumes 16 MHz clock.
// const unsigned int TOP = 0xFFFF; // 16-bit resolution.   244 Hz PWM
// const unsigned int TOP = 0x7FFF; // 15-bit resolution.   488 Hz PWM
// const unsigned int TOP = 0x3FFF; // 14-bit resolution.   976 Hz PWM
// const unsigned int TOP = 0x1FFF; // 13-bit resolution.  1953 Hz PWM
// const unsigned int TOP = 0x0FFF; // 12-bit resolution.  3906 Hz PWM
// const unsigned int TOP = 0x07FF; // 11-bit resolution.  7812 Hz PWM
// const unsigned int TOP = 0x03FF; // 10-bit resolution. 15624 Hz PWM
const unsigned int TOP = 15999; // 1 kHz PWM


void PWM16Begin()
{
  // Stop Timer/Counter1
  TCCR4A = 0;  // Timer/Counter1 Control Register A
  TCCR4B = 0;  // Timer/Counter1 Control Register B
  TIMSK1 = 0;   // Timer/Counter1 Interrupt Mask Register
  TIFR4 = 0;   // Timer/Counter1 Interrupt Flag Register
  ICR4 = TOP;
  OCR4A = 0;  // Default to 0% PWM
  OCR4B = 0;  // Default to 0% PWM
  OCR4C = 0;  // Default to 0% PWM


  // Set clock prescale to 1 for maximum PWM frequency
  TCCR4B |= (1 << CS40);


  // Set to Timer/Counter1 to Waveform Generation Mode 14: Fast PWM with TOP set by ICR1
  TCCR4A |= (1 << WGM41);
  TCCR4B |= (1 << WGM43) | (1 << WGM42) ;
}


void PWM16EnableA()
{
  // Enable Fast PWM on Pin 9: Set OC1A at BOTTOM and clear OC1A on OCR4A compare
  TCCR4A |= (1 << COM4A1);
  pinMode(6, OUTPUT);
}


void PWM16EnableB()
{
  // Enable Fast PWM on Pin 10: Set OC1B at BOTTOM and clear OC1B on OCR4B compare
  TCCR4A |= (1 << COM4B1);
  pinMode(7, OUTPUT);
}


void PWM16EnableC()
{
  // Enable Fast PWM on Pin 10: Set OC1B at BOTTOM and clear OC1B on OCR4B compare
  TCCR4A |= (1 << COM4C1);
  pinMode(8, OUTPUT);
}


inline void PWM16A(unsigned int PWMValue)
{
  OCR4A = constrain(PWMValue, 0, TOP);
}


inline void PWM16B(unsigned int PWMValue)
{
  OCR4B = constrain(PWMValue, 0, TOP);
}


inline void PWM16C(unsigned int PWMValue)
{
  OCR4C = constrain(PWMValue, 0, TOP);
}


void setup()
{
  Serial.begin(9600);
  PWM16Begin();


  // On the Arduino MEGA, T4A is Pin 6, T4B is Pin 7, and T4C is Pin 8


  PWM16A(0);  // Set initial PWM value for Pin 6
  PWM16EnableA();  // Turn PWM on for Pin 6


  PWM16B(0);  // Set initial PWM value for Pin 7
  PWM16EnableB();  // Turn PWM on for Pin 7


  PWM16C(0);  // Set initial PWM value for Pin 8
  PWM16EnableC();  // Turn PWM on for Pin 8
}


void loop()
{
  PWM16A(map(analogRead(A0), 0, 1023, 0, TOP));  // Update the PWM at the end of the current cycle
  PWM16B(map(analogRead(A1), 0, 1023, 0, TOP));  // Update the PWM at the end of the current cycle
  PWM16C(map(analogRead(A2), 0, 1023, 0, TOP));  // Update the PWM at the end of the current cycle
}