90-deg phase disappears upon changing the frequency of Timer interrupt

Hi guys,

I am currently working on a project that is supposed to simulate an encoder and be implementable on other microcontrollers than the DUE as well.

The basic idea is to generate two PWM pulses with the same frequency (This frequency remains constant throughout). Then to generate an interrupt which causes the duty cycle of the PWMs to change. The frequency of this interrupt, and when it happens determines the speed of the encoder and the phase shift of the flank B. (from here on denoted as freq_int)

Here is what happens:

  • PWML1 and PWML2 start with a particular duty cycle
  • TC1 interrrupts and changes the duty cycle of PWML1 (with period of 0.1ms)
  • TC3 interrupts and enables TC2 at half the period of TC1. (at 0.05ms)
  • TC2 interrupts and changes the duty cycle of PWML2.

And so on, the duty cyle of both PWMs change with a 90-deg shift from one another. (See the following image for clarification)

Now this all works perfectly fine with my code currently. As you can see from the attached image from the oscilliscope.

However, next task is to change the frequency of the interrupts, which simulates the speed of the encoder.
Everytime I change the frequency (i.e. register value of the timer) my simulation loses the 90-deg phase shift.

Does anybody have any idea as to why this is happening? According to my theory it should all work just fine and it does everytime I restart the microcontroller. The first frequency input works fine, but anything after that causes the 90-deg phase to disappear.

I really appreciate any help that I can get from you guys. Regards.

Below is the complete code.

#include "OOP_timer_PWM_define.h"


void setup () {  

  enable_PWMs();

}
void loop() {
  
  enc_freq = 4200;
  enable_encoder(2, enc_freq);
  delay(2000);
  enc_freq = 840;
  enable_encoder(2, enc_freq);
  delay(2000);
  enc_freq = 42000;
  enable_encoder(2, enc_freq);
  delay(2000);
  
}

void TC2_Handler()
{
  REG_TC1_CCR1 = TC_CCR_SWTRG | TC_CCR_CLKEN;       // Enable the timer TC4
  TC_GetStatus(TC0, 2);
  NVIC_DisableIRQ(TC2_IRQn);
}

void TC3_Handler()
{
  TC3_dutyA();
  TC_GetStatus(TC1, 0);
}

void TC4_Handler()
{
  TC4_dutyB();
  TC_GetStatus(TC1, 1);
}
//This is the file OOP_timer_PWM_define.h

byte counter2A = 0;
byte counter2B = 0;
int x;
int enc_freq = 4200;

/**********************************************************
 * Enables all the PWMs
 **********************************************************/
void enable_PWMs()
{
  PMC->PMC_PCER1 |= PMC_PCER1_PID36;                   // PWM power ON

  // set PC2 = PWML0 & PC4 = PWML1
  PIOC->PIO_PDR = PIO_PDR_P2    //pin 34                      // Set the pin to the peripheral PWM, not the GPIO
                  | PIO_PDR_P4  //pin 36
                  | PIO_PDR_P5  //pin 37
                  | PIO_PDR_P6  //pin 38
                  | PIO_PDR_P8  //pin 40
                  | PIO_PDR_P21 //pin 9
                  | PIO_PDR_P22 //pin 8
                  | PIO_PDR_P23 //pin 7
                  | PIO_PDR_P18 //pin 45
                  | PIO_PDR_P24;//pin 6

  PIOC->PIO_ABSR |= PIO_PC2B_PWML0                     // Set PWM pin perhipheral type B, page 974 datasheet
                    | PIO_PC4B_PWML1
                    | PIO_PC5B_PWMH1
                    | PIO_PC6B_PWML2
                    | PIO_PC8B_PWML3
                    | PIO_PC21B_PWML4
                    | PIO_PC22B_PWML5
                    | PIO_PC23B_PWML6
                    | PIO_PC18B_PWMH6
                    | PIO_PC24B_PWML7;

  

  for (uint8_t i = 1; i < PWMCH_NUM_NUMBER; i++)                      // Loop for each PWM channel (8 in total)
  {
    PWM->PWM_CH_NUM[i].PWM_CMR = PWM_CMR_CPRE_MCK_DIV_2;    // The period is left aligned, clock source Mck/2 on channel 0
    PWM->PWM_CH_NUM[i].PWM_CPRD = 420;                   // Channel i : Set the PWM frequency
    PWM->PWM_CH_NUM[i].PWM_CDTY = 0;                   // Channel i : Set duty cycle
  }

  PWM->PWM_ENA = PWM_ENA_CHID1
                 | PWM_ENA_CHID2
                 | PWM_ENA_CHID3
                 | PWM_ENA_CHID4
                 | PWM_ENA_CHID5
                 | PWM_ENA_CHID6
                 | PWM_ENA_CHID7;

 
}

/***********************************************************
 *depending on the input value (x) 
 *enables ecoders (timers) 1 or 1&2 both
 **********************************************************/
 
 void enable_encoder(byte x, int y)
{  
  if (x == 2)
  {
    /**********************************************************************************************
    TC2 interrupt to enable flank B
  **********************************************************************************************/

  REG_PMC_PCER0 |= PMC_PCER0_PID29; //Enable peripheral clock for TC2 (pg 38 & 542)

  REG_PIOA_ABSR |= PIO_ABSR_P5;     // TIOA2, PA5
  REG_PIOA_PDR |= PIO_PDR_P5;       //Disable GPIO

  REG_TC0_CMR2 = TC_CMR_ACPC_SET |                  // Set TIOA on counter match with RC0
                 TC_CMR_ACPA_CLEAR |                // Clear TIOA on counter match with RA0
                 TC_CMR_WAVE |                      // Enable wave mode
                 TC_CMR_WAVSEL_UP_RC |              // Count up with automatic trigger on RC compare
                 TC_CMR_TCCLKS_TIMER_CLOCK1;        // Set the timer clock to TCLK1 (MCK/2 = 84MHz/2 = 42MHz)

  REG_TC0_RC2 = y/2;                               // Load the RC1 register
  TC0->TC_CHANNEL[2].TC_IER = TC_IER_CPCS;       // Interrupt on RC compare match
  NVIC_EnableIRQ(TC2_IRQn);                     // Interrupt enable

  TC0->TC_CHANNEL[2].TC_CCR = TC_CCR_SWTRG | TC_CCR_CLKEN; // Software trigger TC0 counter and enable

  /**********************************************************************************************
    TC3 interrupt to change the duty cycle on flank A of the encoder 2
  **********************************************************************************************/

  REG_PMC_PCER0 |= PMC_PCER0_PID30; //Enable peripheral clock for TC3 (pg 38 & 542)

  REG_PIOB_ABSR |= PIO_ABSR_P0;     // TIOA2, PA5
  REG_PIOB_PDR |= PIO_PDR_P0;       //Disable GPIO

  REG_TC1_CMR0 = TC_CMR_ACPC_SET |                  // Set TIOA on counter match with RC0
                 TC_CMR_ACPA_CLEAR |                // Clear TIOA on counter match with RA0
                 TC_CMR_WAVE |                      // Enable wave mode
                 TC_CMR_WAVSEL_UP_RC |              // Count up with automatic trigger on RC compare
                 TC_CMR_TCCLKS_TIMER_CLOCK1;        // Set the timer clock to TCLK1 (MCK/2 = 84MHz/2 = 42MHz)

  REG_TC1_RC0 = y;                               // Load the RC1 register
  TC1->TC_CHANNEL[0].TC_IER = TC_IER_CPCS;       // Interrupt on RC compare match
  NVIC_EnableIRQ(TC3_IRQn);                     // Interrupt enable

  TC1->TC_CHANNEL[0].TC_CCR = TC_CCR_SWTRG | TC_CCR_CLKEN; // Software trigger TC0 counter and enable

  /**********************************************************************************************
    TC4 interrupt to change the duty cycle on flank B of the encoder 2
  **********************************************************************************************/

  REG_PMC_PCER0 |= PMC_PCER0_PID31; //Enable peripheral clock for TC4 (pg 38 & 542)

  REG_PIOB_ABSR |= PIO_ABSR_P2;     // TIOA2, PA5
  REG_PIOB_PDR |= PIO_PDR_P2;       //Disable GPIO

  REG_TC1_CMR1 = TC_CMR_ACPC_SET |                  // Set TIOA on counter match with RC0
                 TC_CMR_ACPA_CLEAR |                // Clear TIOA on counter match with RA0
                 TC_CMR_WAVE |                      // Enable wave mode
                 TC_CMR_WAVSEL_UP_RC |              // Count up with automatic trigger on RC compare
                 TC_CMR_TCCLKS_TIMER_CLOCK1;        // Set the timer clock to TCLK1 (MCK/2 = 84MHz/2 = 42MHz)

  REG_TC1_RC1 = y;                               // Load the RC1 register
  TC1->TC_CHANNEL[1].TC_IER = TC_IER_CPCS;       // Interrupt on RC compare match
  NVIC_EnableIRQ(TC4_IRQn);                     // Interrupt enable

  }
}

void TC3_dutyA()
{
  if (counter2A == 0)
  {
    PWM->PWM_CH_NUM[3].PWM_CDTYUPD = 310;
    counter2A = 1;
  }
  else
  {
    PWM->PWM_CH_NUM[3].PWM_CDTYUPD = 105;
    counter2A = 0;
  }
}

void TC4_dutyB()
{
  if (counter2B == 0)
  {
    PWM->PWM_CH_NUM[4].PWM_CDTYUPD = 310;
    counter2B = 1;
  }
  else
  {
    PWM->PWM_CH_NUM[4].PWM_CDTYUPD = 105;
    counter2B = 0;
  }
}

The image is missing.

Which controller do you use?

Why use PWM and multiple timers if one in ordinary pulse generation mode is sufficient?

Sorry for the late response. I can't figure out why the images won't show up in the post. So I have attached them separately. Please check the previous post again for the images.

I am using the Arduino Due.

What do you mean? The timers are being used for interrupts; so that the duty cycle can be changed in real time.

There is one PWM per channel and one timer interrupt to change its duty cycle.

-One PWM for flank A, one timer interrupt to change its duty cycle.
-One timer interrupt to start flank B with 90deg delay to flank A.
-One PWM for flank B, one timer interrupt to change its duty cycle.

Dunno what timer modes the Due supports. On an Uno a single timer can generate both signals without software intervention.

Update: I figured it on my own

I was disabling the interrupts within the TC2 handler. I thought re-enabling that within my code would work. However, that is not the case. So now I just never disable the interrupts, rather use a condition within that interrupt to decide whether or not the code within the interrupt has any effect.