Go Down

Topic: Complementary PWM channels Output (Read 93 times) previous topic - next topic

jonnygainz

I'm using the following code to synchronize three channels and output a pair of complementary outputs per channel.

Code: [Select]
void setup() {

  PMC->PMC_PCER1 |= PMC_PCER1_PID36; //Enable PWM (Power On)
 
  PWM->PWM_DIS = PWM_DIS_CHID0;       //Disable PWM on Channel 0
  //PWM->PWM_DIS = PWM_DIS_CHID1;

  PIOC->PIO_PDR |= PIO_PDR_P3 | PIO_PDR_P5 | PIO_PDR_P7; // Setting pins 3,5,7 (DUE Pins 35, 37, 39) to PWM Peripheral, not GPIO
  PIOC->PIO_ABSR |= PIO_PC3B_PWMH0 | PIO_PC5B_PWMH1 | PIO_PC7B_PWMH2; // Setting pins to Peripheral B

  PIOC->PIO_PDR |= PIO_PDR_P2 | PIO_PDR_P4 | PIO_PDR_P6; // Setting pins 2,4,6 (DUE Pins 34, 36, 38) to PWM Peripheral, not GPIO
  PIOC->PIO_ABSR |= PIO_PC2B_PWML0 | PIO_PC4B_PWML1 | PIO_PC6B_PWML2; // Setting pins to Peripheral B

  PWM->PWM_CLK = PWM_CLK_PREA(0) | PWM_CLK_DIVA(42); //Set PWM clock rate to 2MHz (84MHz/42)
  PWM->PWM_CH_NUM[0].PWM_CMR = PWM_CMR_CPRE_CLKA; // Period is left aligned,clock source is CLKA on Channel 0

  REG_PWM_SCM |= PWM_SCM_SYNC0 | PWM_SCM_SYNC1 | PWM_SCM_SYNC2; // Synchronizing of Channels 0, 1 and 2

  //PWM->PWM_CLK = PWM_CLK_PREA(1) | PWM_CLK_DIVA(42);
  //PWM->PWM_CH_NUM[1].PWM_CMR = PWM_CMR_CPRE_CLKA;

  REG_PWM_CPRD0 = 1000000; //Channel 0 Period f = 2MHz/(2*CPRD)
  REG_PWM_CDTY0 = 200000; //Channel 0 Duty Cycle x% = (CDTY/ CPRD)*100%
  REG_PWM_CPRD1 = 1000000;
  REG_PWM_CDTY1 = 1000000;
  REG_PWM_CPRD2 = 1000000;
  REG_PWM_CDTY2 = 0;
 
  PWM->PWM_ENA = PWM_ENA_CHID0; // Enable PWM on Channel 0
  //PWM->PWM_ENA = PWM_ENA_CHID1;

  //NVIC_EnableIRQ(TC0_IRQn); // enable TC0 interrupts
}

void loop() {
  // put your main code here, to run repeatedly:

}


When the duty cycle is set to 0%, 20%, 40%, 60%, 80% and 100% (0, 200000, 400000, 600000, 800000, 1000000) it outputs correctly. However any other value just gives me the output for 100% duty cycle. Can anyone explain this to me and offer some guidance on how to rectify this problem? 

MartinL

#1
Jan 12, 2019, 06:04 pm Last Edit: Jan 12, 2019, 07:56 pm by MartinL
Hi jonnygainz,

To generate synchronous, complementary PWM using method 1 (mode0): manual duty-cycle and trigger update, it's necessary to set the update unlock bit (PWM_SCUC_UPDULOCK) in the Sync Channels Update Control register (SCUC), to trigger once the duty-cycle registers have been changed:

Code: [Select]
PWM->PWM_SCUC = PWM_SCUC_UPDULOCK;      // Set the update unlock bit to trigger an update at the end of the next PWM period
Here's an example of using 3 synchronous, complementary channels on channel 0 (D34, D35), 1 (D36, D37) and 2 (D38, D39). The PWM is at 50Hz and switches between 25% and 75% duty-cycle every 1/2 second:

Code: [Select]
// Enable synchronous, complementary output, single-slope PWM at 50Hz on 3 channels (0 to 2), at 50% duty cycle
void setup() {
  // Set-up 6 PWM outputs on 3 complementary channels on on pins D34, D35, D36, D37, D38, and D39 for channels 0 through to 2 respectively
  PMC->PMC_PCER1 |= PMC_PCER1_PID36;                                               // Enable PWM   
  PIOC->PIO_ABSR |= PIO_ABSR_P7 | PIO_ABSR_P6 | PIO_ABSR_P5 | PIO_ABSR_P4 |        // Set the port C PWM pins to peripheral type B
                    PIO_ABSR_P3 | PIO_ABSR_P2;
  PIOC->PIO_PDR |= PIO_PDR_P7 | PIO_PDR_P6 | PIO_PDR_P5 | PIO_PDR_P4 |             // Set the port C PWM pins to outputs
                   PIO_PDR_P3 | PIO_PDR_P2;
  PWM->PWM_CLK = PWM_CLK_PREA(0) | PWM_CLK_DIVA(84);                               // Set the PWM clock A rate to 1MHz (84MHz/84)
  PWM->PWM_SCM |= PWM_SCM_SYNC2 | PWM_SCM_SYNC1 | PWM_SCM_SYNC0;                   // Set the PWM channels as synchronous                 
  PWM->PWM_CH_NUM[0].PWM_CMR = PWM_CMR_CPRE_CLKA;      // Enable single slope PWM and set the clock source as CLKA for all synchronous channels
  PWM->PWM_CH_NUM[0].PWM_CPRD = 19999;                 // Set the PWM frequency 1MHz/(19999 + 1) = 50Hz for all synchronous channels
  for (uint8_t i = 0; i < 3; i++)                      // Loop for each PWM channel (8 in total)
  {
    PWM->PWM_CH_NUM[i].PWM_CDTY = 9999;                // Set the PWM duty cycle to 50%
  }
  PWM->PWM_ENA = PWM_ENA_CHID0;                        // Enable all synchronous PWM channels
  PWM->PWM_SCUC = PWM_SCUC_UPDULOCK;      // Set the update unlock bit to trigger an update at the end of the next PWM period
}

void loop()
{
  for (uint8_t i = 0; i < 3; i++)                      // Loop for each PWM channel (3 in total)
  {
    PWM->PWM_CH_NUM[i].PWM_CDTYUPD = 4999;             // Set the PWM duty cycle to 25%
  }
  PWM->PWM_SCUC = PWM_SCUC_UPDULOCK;      // Set the update unlock bit to trigger an update at the end of the next PWM period
  delay(500);
  for (uint8_t i = 0; i < 3; i++)                      // Loop for each PWM channel (3 in total)
  {
    PWM->PWM_CH_NUM[i].PWM_CDTYUPD = 14999;            // Set the PWM duty cycle to 75%
  }
  PWM->PWM_SCUC = PWM_SCUC_UPDULOCK;      // Set the update unlock bit to trigger an update at the end of the next PWM period
  delay(500);
}

Also, the PWM controller's timers and duty-cycle registers (PWM_CDTY and PWM_CDTYUPD) are essentially only 16-bit, therefore it's not possible to load them with values over 65535 (2^16 - 1).

jonnygainz

Thank you very much for the help, I'll let you know if I get through with what I'm doing.

Go Up