Arduino Due SPWM Using Timer Interrupts

The code I've attached below is an attempt at producing two inverted SPWM signals using an RC compare interrupt on Timer Counter Channel 0 (TC0). TC0 is configured to count up at a rate of 4.2MHz, and restart back to 0 once RC is reached (WAVSEL = 2), RC was set to 2100 as to provide SPWM signals with a frequency of 20kHz, and RA and RB were set equal to one another, but with different compare effects (i.e. ACPA = 1, BCPB = 2, etc.) so that TIOA0 and TIOB0 would be inverted from one another. Whenever TC0 reaches RC and an interrupt is fired, the RA and RB registers are updated as to provide a sinusoidally varying duty cycle. The final goal of this code is to be used with a variable frequency drive, which is where values such as "ma" originate.

From what I've found from testing, everything works, except that pins 2 and 13, which should be outputting TIOA0 and TIOB0, are not outputting anything. This was confirmed using an oscilloscope. So it would seem to me that either I have not set up the PIO controller properly, or I'm not writing to the RA and RB registers properly, or maybe what I'm trying to do isn't even possible. Any insight would be greatly appreciated.

//PIO INITIALIZATION
#define PIO_WPEN 0
#define PIO_TIOA0 25
#define PIO_TIOB0 27

//PMC INITIALIZATION
#define PMC_WPEN 0
#define PMC_TC0 27

//HANDLER INITIALIZATION
volatile int const fc = 20000; 
volatile float const VFR = 0.01664;
float const pi = 3.141593;
volatile float ma;
volatile int fm;
volatile float mf;
volatile int i = 0;
volatile int x;
volatile int sine[361];

//TIMER INITIALIZATION
#define TC0_WPEN 0
#define TC0_TCCLKS 0
#define TC0_WAVSEL 13
#define TC0_WAVE 15
#define TC0_ACPA 16
#define TC0_ACPC 18 
#define TC0_BCPB 24
#define TC0_BCPC 26
#define TC0_CPCS 4
#define TC0_CLKEN 0
#define TC0_CLKDIS 1
#define TC0_SWTRG 2
unsigned volatile int Ra,Rb = 0;
unsigned volatile int const Rc = 2100;


void setup() {
  noInterrupts();
  
  //PIO SETUP
  REG_PIOB_WPMR = (1 << PIO_WPEN); 
  REG_PIOB_PDR = (1 << PIO_TIOA0) | (1 << PIO_TIOB0);
  pinMode(13,OUTPUT); 
  pinMode(2,OUTPUT); 
  
  //PMC SETUP
  REG_PMC_WPMR = (1 << PMC_WPEN); 
  REG_PMC_PCER0 = (1 << PMC_TC0);

  //NVIC SETUP
  NVIC_EnableIRQ(TC0_IRQn);

  //HANDLER SETUP
  fm = map(analogRead(A0),0,1023,30,60);
  mf = fc/fm;
  ma = VFR*fm;
  for (int j = 0; j <= 360; j++) {
    sine[j] = 1050*(sin(j*pi/180)+1); 
  }
  
  //TIMER SETUP
  REG_TC0_WPMR = (1 << TC0_WPEN); 
  REG_TC0_CMR0 = (1 << TC0_BCPC) | (2 << TC0_BCPB) | (2 << TC0_ACPC) | 
                 (1 << TC0_ACPA) | (1 << TC0_WAVE) | (2 << TC0_WAVSEL) |
                 (0 << TC0_TCCLKS);             
  REG_TC0_RA0 = Ra; 
  REG_TC0_RB0 = Rb;
  REG_TC0_RC0 = Rc;
  REG_TC0_IER0 = (1 << TC0_CPCS); 
  REG_TC0_IDR0 = (0 << TC0_CPCS);  
  REG_TC0_CCR0 = (1 << TC0_SWTRG) | (0 << TC0_CLKDIS) | (1 << TC0_CLKEN); 

  interrupts();
}

void loop() {
  
}

void TC0_Handler() {
  TC_GetStatus(TC0,0);
  i++;
  fm = map(analogRead(A0),0,1023,30,60);
  x = 360*(i/mf);
  i++;
  if (i == mf) {
    i = 0;
    mf = fc/fm;
    ma = VFR*fm;
  }
  REG_TC0_RA0 = REG_TC0_RB0 = ma*sine[x];
}

Since you want 2 inverted sinewaves, PWM peripheral would be more suited than a Timer Counter. E.g. if PWM channel 0 is selected, PWMH0 is automatically inverted with PWML0. Moreover, since you know the output waveform is a sine wave, fill a buffer with e.g. 360 points for a full sinewave period. There are example sketches in the DUE sub forum.

Thanks for the advice, I've looked into using the PWM peripheral but opted for Timer Counters due to familiarity, however; I'll try out the other method now. Just to confirm though, did I assign the TC pins correctly in my code above? I was trying to "connect" TIOA0 and TIOB0 to pins 2 and 13, to use them as outputs. All I did was disable PIO control for each of the respective pins, thus enabling peripheral control. Would you also need to write to the ABSR register to enable peripheral B functionality?

An example sketch to output a 1 KHz pulse on TIOA8 (pin 11) and TIOB8(pin 12):

/**********************************************************/
/*    Timer Counter TC2 outputs TIOA8 and TIOB8 - 1KHz    */
/**********************************************************/
void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  tc_setup();

}
void loop() {
}
void tc_setup()
{
  PMC->PMC_PCER1 = PMC_PCER1_PID35;                      // TC8 power ON : Timer Counter 2 channel 2 IS TC8

  PIOD->PIO_PDR = PIO_PDR_P7                              //Set TIOA8 and TIOB0 to the peripheral
                  | PIO_PDR_P8;
  PIOD->PIO_ABSR |= PIO_PD7B_TIOA8
                    | PIO_PD8B_TIOB8;

  TC2->TC_CHANNEL[2].TC_CMR = TC_CMR_TCCLKS_TIMER_CLOCK1  //MCK/2, clk on rising edge
                              | TC_CMR_EEVT_XC2           //TIOB in output
                              | TC_CMR_WAVE               //Waveform mode
                              | TC_CMR_WAVSEL_UP_RC       //UP mode with automatic trigger on RC Compare
                              | TC_CMR_BCPB_SET           //Set TIOB on RB compare match
                              | TC_CMR_BCPC_CLEAR         //Clear TIOB on RC compare match
                              | TC_CMR_ACPA_CLEAR         //Clear TIOA on RA compare match
                              | TC_CMR_ACPC_SET;          //Set TIOA on RC compare match

  TC2->TC_CHANNEL[2].TC_RC = 42000;                       //TIOA8 Frequency = Mck/2/TC_RC
  TC2->TC_CHANNEL[2].TC_RA = 21000;                       //Any Duty cycle in between 1 and TC_RC
  TC2->TC_CHANNEL[2].TC_RB = 21000;                       //Any Duty cycle in between 1 and TC_RC

  TC2->TC_CHANNEL[2].TC_IER = TC_IER_CPCS;
  NVIC_EnableIRQ(TC8_IRQn);

  TC2->TC_CHANNEL[2].TC_CCR = TC_CCR_SWTRG | TC_CCR_CLKEN; // Software trigger TC8 counter and enable
}
void TC8_Handler() {

  static uint32_t Count;

  TC2->TC_CHANNEL[2].TC_SR;                       // Read and clear status register
  if (Count++ == 1000) {
    Count = 0;
    PIOB->PIO_ODSR ^= PIO_ODSR_P27;               // Toggle LED every 1 Hz
  }
}

Perfect, that helps a lot! Thank you.

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.