I had a problem with programming Due PWM channels and managed to write a code that seems to solve it, but I am not sure about its correctness. The problem is described below, and my questions are at the very end.
I need two PWM channels with a fix and rather high frequency 20 β 25 kHz. The duty factors should be variable and the phases of two channels should be shifted by half a period.
I programmed Leonardo to achieve this, but due to its frequency got a limited, around 8-bit, resolution. And I would like to have it better, because I want to use it in a programmable heating controller, where speed and precision matter.
I turned then to Due with more than 5 times higher frequency. The Due has a different system of PWM channels, more complicated and I did not find the Atmel datasheet very helpful.
Anyway, after looking for a solution in a number of places and after many experiments, I managed to write a code that seems to fit my needs.
The code is like this:
#include <Arduino.h>
void setupPWM();
void setup() {
Serial.begin(115200);
delay(10);
setupPWM();
}
int i = 0;
void loop() {
delay(5000);
i++;
uint32_t duty = i*200ul;
PWM->PWM_CH_NUM[4].PWM_CDTY = duty;
PWM->PWM_CH_NUM[5].PWM_CDTY = 4000ul - duty;
Serial.println("duty set to " + String(i) + "/10 of max");
if (i == 9) i = 0;
}
void setupPWM() {
// Set up PWM on digital pins D9, D8 for channels 4, 5 respectively
PMC->PMC_PCER1 |= PMC_PCER1_PID36; // enable PWM through peripheral clock enable register (p. 563)
PIOC->PIO_ABSR |= PIO_ABSR_P21 | PIO_ABSR_P22; // select port C pins 21 and 22 for PWM through its peripheral select register (p. 656)
PIOC->PIO_PDR |= PIO_PDR_P21 | PIO_PDR_P22; // PIO disable register enables peripheral control of the pins (p. 634)
PWM->PWM_CLK = PWM_CLK_PREA(0) | PWM_CLK_DIVA(1); // configure PWM clock A with no prescaler and no divider resulting in 84 MHz
// Arduino Due pin 9
PWM->PWM_CH_NUM[4].PWM_CMR |= PWM_CMR_CALG | PWM_CMR_CPRE_CLKA;
PWM->PWM_CH_NUM[4].PWM_CPRD = 3999;
PWM->PWM_CH_NUM[4].PWM_CDTY = 500;
// Arduino Due pin 8
PWM->PWM_CH_NUM[5].PWM_CMR |= PWM_CMR_CPOL | PWM_CMR_CALG | PWM_CMR_CPRE_CLKA;
PWM->PWM_CH_NUM[5].PWM_CPRD = 3999;
PWM->PWM_CH_NUM[5].PWM_CDTY = 3500;
// Enable PWM channels
PWM->PWM_ENA = PWM_ENA_CHID4 | PWM_ENA_CHID5;
}
`setupPWM()` function enables the PWM device, sets two pins for PWM output, and programs PWM clock A for the maximum frequency 84 MHz. I selected Due pins D9 & D8 and PWM channels 4 & 5. This is done by the code in the first 4 lines of the function. Then it configures the two selected channels, setting them to the center aligned waveform and to use the PWM clock A, sets the period and duty cycle. The only difference is that channel 5 has reversed polarity and duty cycles. This results in positive polarity pulses of the same length and shifted by half a period. At least it looks like that when I use the oscilloscope.
The `loop()` function changes gradually the duty factor and seemingly the whole code works as expected.
But I am not sure if it is true. So, I have questions and would appreciate help in answering them.
Is the code safe? I mean, may changing the duty factor in the way used in the loop() cause any harm either to Due or to a connected circuit? Should the update registers be used and how?
Are pulses in the two channels shifted exactly by half a period or is it an accident?
If the code needs to be corrected, in which way, then?
The connected circuit will be switching transistors and then the primary windings of the transformer, so they are probably safe, but I rather think of spikes that may produce some interference.
Well, I saw that method but it is listed under synchronous channels, which they are not. I tried synchronous channels but couldn't get the shifted phase.
It is not the whole program. setupPWM() sets the clock and prescaler to 1 Hz PWM frequency (for testing purposes). setFast() changes that to 25 kHz at the expense of resolution, though. dutyFactor(double) changes only the duty factor of both channels. Note that duty factors of both channels are complimentary, they sum up to the whole period. This is because the waveform is center aligned and polarity of one channel is reversed.
180 phase shift and reversed complimetary means the same. And I needed exactly 180 phase shift fixed.
They are probably aligned by some internal mechanism. I selected the center aligned waveform and this probably fixes it. This mode is called "Phase Correct PWM Mode" in the manual.