I am trying to have ~10-MHz output clock from the Due board for my sensor boards.
Is there way to divide the main clock, and use the divided clock as an output.
Since the clock for the Due 84 MHz, divide-by 8 will give 10.5MHz (84M/8).
(I guess PWM frequency is much lower than 10MHz.)
don't change anything in the variant.h file.. all can be done in your sketch code.
To use PWM on the DUE there are two ways. First one is using the general purpose timer counter (TC) channels and the second one is using the internal PWM functionality of the SAM3X8E. Both of them work equally fine but you need to modify different registers in the two cases. Here is some code that uses the internal PWM channels:
int32_t mask_PWM_pin = digitalPinToBitMask(7);
REG_PMC_PCER1 = 1<<4; // activate clock for PWM controller
REG_PIOC_PDR |= mask_PWM_pin; // activate peripheral functions for pin (disables all PIO functionality)
REG_PIOC_ABSR |= mask_PWM_pin; // choose peripheral option B
REG_PWM_CLK = 0; // choose clock rate, 0 -> full MCLK as reference 84MHz
REG_PWM_CMR6 = 0<<9; // select clock and polarity for PWM channel (pin7) -> (CPOL = 0)
REG_PWM_CPRD6 = 8; // initialize PWM period -> T = value/84MHz (value: up to 16bit), value=8 -> 10.5MHz
REG_PWM_CDTY6 = 4; // initialize duty cycle, REG_PWM_CPRD6 / value = duty cycle, for 8/4 = 50%
REG_PWM_ENA = 1<<6; // enable PWM on PWM channel (pin 7 = PWML6)
The code enables PWM with a frequency of 10.5MHz and a duty cycle of 50% on digital pin number 7. The code like it is only works for this pin. If you want to change the pin, you have to check in the pinout diagramm for the pins labeled PWMHx or PWMLx and go through the section for PWM in the datasheet to set the good registers. I can help you with that if needed.
It works like a charm !
Would you mind telling me which documents should I dig up for all those registers and settings?
The code I googled below won't work when I change the value of REG_PWM_CPRD0 from 5000 (works OK), to 500.
Many thanks..
// Let's say we want to configure PWM channel 0, PWML0 which is on pin34 Due board, and corresponds to the port C.2
// First the peripheral must be activated instead off the general GPIO
REG_PIOC_PDR = 4;
// Moreover it's peripheral B (not A), which means we have to change PIO_ABSR[2]
REG_PIOC_ABSR = REG_PIOC_ABSR | 4;
// Now concerning PWM settings...
// activation of the clock dedicated to PWM peripheral (id36), which is fifth bit of PMC_PCSR1
REG_PMC_PCER1 = REG_PMC_PCER1 | 16;
// activation of PWM_ENA register with channel 0
REG_PWM_ENA = REG_PWM_SR | 1;
// setting the period (which can take any 16 bit value...)
REG_PWM_CPRD0 = 500;
// note : with this 5000 setting, we have thus 1/5000 resol, with a frequency of 16khz, if we use the defaut main clock : that's good
// Finally, setting the duty cycle to the value you want (between 0 and the period, of course)
REG_PWM_CDTY0 = 1000;
// And here we can check at the oscilloscope that we get a nice PWM on pin 34, with a period of 60usec... nice!
First of all, when you want to change the values in the registers REG_PWM_CPRD0 and REG_PWM_CDTY0 during your programm code, you need to use REG_PWM_CPRDUPD0 and REG_PWM_CDTYUPD0 to update the values (the direct write to the registers works only once, for initialization).
The way PWM works in principle is that you have one register that counts clock cycles. It counts up until a cerntain value is hit that is written in a compare register (here REG_PWM_CDTY0), then the state of your output pin gets inverted. Then the counter continues to count until the value of a second register is hit (here REG_PWM_CPRD0), inverts again the state of the output pin and resets your counter. Then everything starts again.
This creates a signal that has a total period given by the value in the REG_PWM_CPRD0 register. During this full period, there is the choice after how many clock cycles the output state should toggle (which determines the duty cycle), that is given by the value in REG_PWM_CDTY0. Therefore, if the value in REG_PWM_CDTY0 is larger than the one in REG_PWM_CPRD0, the output state never toggles and you have a constant signal.
This is why setting "REG_PWM_CPRD0 from 5000 to 500." doesnt work as long as you dont change the duty cycle register to something below 500 (to 250 for 50% duty cycle). So if you want a 50% duty cycle PWM the value in CPRD should always be twice as large as the one in CDTY. With the value in CPRD you set the PWM period because it simply determines how many clock cycles should be counted during one PWM period.
schwingkopf:
int32_t mask_PWM_pin = digitalPinToBitMask(7);
REG_PMC_PCER1 = 1<<4; // activate clock for PWM controller
REG_PIOC_PDR |= mask_PWM_pin; // activate peripheral functions for pin (disables all PIO functionality)
REG_PIOC_ABSR |= mask_PWM_pin; // choose peripheral option B
REG_PWM_CLK = 0; // choose clock rate, 0 -> full MCLK as reference 84MHz
REG_PWM_CMR6 = 0<<9; // select clock and polarity for PWM channel (pin7) -> (CPOL = 0)
REG_PWM_CPRD6 = 8; // initialize PWM period -> T = value/84MHz (value: up to 16bit), value=8 -> 10.5MHz
REG_PWM_CDTY6 = 4; // initialize duty cycle, REG_PWM_CPRD6 / value = duty cycle, for 8/4 = 50%
REG_PWM_ENA = 1<<6; // enable PWM on PWM channel (pin 7 = PWML6)
I would like to use this in my code, but I'm not quite sure how to integrate. Should I put it in setup(), loop(), global? Simply put, I just want to get a 10 MHz PWM on a pin (preferably one of 10-13). I just don't quite understand how to bring this code snippet into my code in order to get that PWM signal. Could someone help me with this?