ard_newbie:
Page 985 of Sam3x datasheet you will find an explanation to update duty cycle for synchronous channels. There are 3 methods, the 3rd one (IMO the most interesting one) is the automatic update via a PDC DMA.
An example sketch with 3 PWM phase shifted and automatic duty cycle update via DMA (to easely see the result, select Menu>Tools>Serial Plotter and select 57600 for baud rate):
/*******************************************************************************************/
/* Synchro channel PWML0, PWML1 and PWML2 with PDC DMA trigger on Compare /
/******************************************************************************************/
#define sinsize (360)
#define Wavesize (360)
#define PERIOD_VALUE (62500) // ****** For a 1 Hz sinwave
#define NbCh (3) // If Only channel 0 —> Number of channels = 1, etc…
#define DUTY_BUFFER_LENGTH (Wavesize * NbCh) // Half words
uint16_t Duty_Buffer[DUTY_BUFFER_LENGTH];
#define UpdatePeriod_Msk (0b1111)
#define UpdatePeriod (UpdatePeriod_Msk & 0b0000) //Defines the time between each duty cycle update of the synchronous channels
//This time equals to (UpdatePeriod + 1) periods of the Reference channel 0
uint16_t Sin_Duty[sinsize];
uint16_t Wave_Duty[Wavesize];
void setup () {
Serial.begin(57600);
PMC->PMC_PCER1 |= PMC_PCER1_PID36; // PWM controller power ON
// PWML0 on PC2, peripheral type B
PIOC->PIO_PDR |= PIO_PDR_P2; // Set PWM pin to peripheral
PIOC->PIO_ABSR |= PIO_PC2B_PWML0;
// PWML1 on PC4 ; Peripheral=B
PIOC->PIO_PDR |= PIO_PDR_P4;
PIOC->PIO_ABSR |= PIO_PC4B_PWML1;
// PWML2 on PC6 ; Perpipheral type B
PIOC->PIO_PDR |= PIO_PDR_P6;
PIOC->PIO_ABSR |= PIO_PC6B_PWML2;
// Set synchro channels list : Channel 0, channel 1 and channel 2
PWM->PWM_DIS = PWM_DIS_CHID0 | PWM_DIS_CHID1 | PWM_DIS_CHID2;
PWM->PWM_SCM = PWM_SCM_SYNC0 // Add SYNCx accordingly, at least SYNC0, plus SYNC1 & SYNC2
| PWM_SCM_SYNC1
| PWM_SCM_SYNC2
| PWM_SCM_UPDM_MODE2; //Automatic write of duty-cycle update registers by the PDC DMA
// Set duty cycle update period
PWM->PWM_SCUP = PWM_SCUP_UPR(UpdatePeriod);
// Set the PWM Reference channel 0 i.e. : Clock/Frequency/Alignment
PWM->PWM_CLK = PWM_CLK_PREA(0b0000) | PWM_CLK_DIVA(3); // Set the PWM clock rate for 84 MHz/3
PWM->PWM_CH_NUM[0].PWM_CMR = PWM_CMR_CPRE_CLKA; // The period is left aligned, clock source as CLKA on channel 0
PWM->PWM_CH_NUM[0].PWM_CPRD = PERIOD_VALUE; // Set the PWM frequency (84MHz/3)/PERIOD_VALUE Hz
/**** Final frequency = MCK/DIVA/PRES/CPRD/(UPR + 1)/Wavesize ****/
// Set Interrupt events
PWM->PWM_IER2 = PWM_IER2_WRDY; //Write Ready for Synchronous Channels Update Interrupt Enable
//synchro with ENDTX End of TX Buffer Interrupt Enable
// Fill duty cycle buffer for channels 0, x, y …
// Duty_Buffer is a buffer of Half Words(H_W) composed of N lines whose structure model for each duty cycle update is :
// [ H_W: First synchro channel 0 duty cycle Mandatory ]/[ H_W: Second synchro channel duty cycle ] … and so on
/* PWML0 waveform between 0° and 120° /
for (int i = 0; i <= sinsize / 3; i++) {
Sin_Duty[i] = (62500 * (sinf( i * 2 * PI / sinsize ) + 0));
}
/ PWML0 waveform between 120° and 240° */
for (int i = sinsize / 3; i <= 2 * sinsize / 3; i++) {
Sin_Duty[i] = Sin_Duty[240 - i];
}
/* PWML0 waveform between 240° and 360° */
for (int i = 2 * sinsize / 3; i <= sinsize; i++) {
Sin_Duty[i] = 0;
}
/* Fill Duty_Buffer for all synchro channels */
for (uint32_t i = 0; i < sinsize; i++) {
Duty_Buffer[i * NbCh + 0] = Sin_Duty[i];
Duty_Buffer[i * NbCh + 1] = Sin_Duty[(i + sinsize / 3) % sinsize];
Duty_Buffer[i * NbCh + 2] = Sin_Duty[(i + 2 * sinsize / 3) % sinsize];
}
PWM->PWM_ENA = PWM_ENA_CHID0; // Enable PWM for all channels, channel 0 Enable is sufficient
PWM->PWM_TPR = (uint32_t)Duty_Buffer; // FIRST DMA buffer
PWM->PWM_TCR = DUTY_BUFFER_LENGTH; // Number of Half words
PWM->PWM_TNPR = (uint32_t)Duty_Buffer; // Next DMA buffer
PWM->PWM_TNCR = DUTY_BUFFER_LENGTH;
PWM->PWM_PTCR = PWM_PTCR_TXTEN; // Enable PDC Transmit channel request
NVIC_EnableIRQ(PWM_IRQn);
}
void loop() {
// For debugging only :
// Check correct updates of CDTY via CDTYUPD by DMA
// Menu > Tools > Serial Plotter – 57600 baud
printf(
“%d,%d,%d\n”,
PWM->PWM_CH_NUM[0].PWM_CDTY,
PWM->PWM_CH_NUM[1].PWM_CDTY,
PWM->PWM_CH_NUM[2].PWM_CDTY
);
// If a PWM frequency update of PWM0 (and therefore PWM1 and PWM2)
// Do it with PWM_CPRDUPD in loop()
}
void PWM_Handler() { // move PDC DMA pointers to next buffer
PWM->PWM_ISR2; // Clear status register
PWM->PWM_TNPR = (uint32_t)Duty_Buffer;
PWM->PWM_TNCR = DUTY_BUFFER_LENGTH;
}
Can I somehow change this code to just have two 90-degree phase shifted signals? I’m sorry might be a noob question but I’ve been trying to do that since a long while cant seem to make it work.