A while ago on www.controlsystemslab.com
, we present an article on DC motor open-loop speed control
, using a simple voltage command from ADC to drive the H-bridge driver. The PWM output is generated from output compare module of the dsPIC30F2010. That 16-bit digital signal processor is now quite dated. The reader would want to implement the scheme on a newer microcontroller.
At the time of this writing, PIC24EP (and dsPIC33E) series from Microchip is the latest product in the 16-bit range that could run at 70 MIPS performance. For those who want to port the code to PIC24EP, unfortunately, you have to modify the ADC and PWM routines. In this article we focus on the latter.
dsPIC30F PWM Generation Scheme
In the previous article DC motor open-loop speed control
, the output compare module OC1 is used together with timer 3 to generate the PWM signal. Older 16-bit PIC and dsPIC have only one control register per output compare module. For example, OC1 is configured by OC1CON1. So we simply write 0x000E to OC1CON1 to achieve the desired functionality. Please refer to Microchip datasheet for detail.
After the OC1 module is set up properly, the duty cycle of PWM signal is adjusted by the following manner. The value corresponding to maximum period is loaded into PR3, and the value for current duty cycle is written to OC1RS, which is then copied to OC1R by the hardware.
To make a concrete example, for a 10-bit PWM a value of 1024 is loaded to PR3. Then the duty cycle is computed from the ratio of OC1RS and PR3; i.e., writing 256, 512, 1024 to OC1RS generate PWM signals with duty cycle 25%, 50%, and 100%, respectively.
Migration to PIC24E
On our experiment board, we use PIC24EP256MC202 and configure it to run at its maximum performance as described in our previous article Configure PIC24E to run at 70 MIPS
. The PWM signal from OC1 module is sent out of pin 16 (RP39/RB7). Note that the OC1 output can be mapped to an RP pin. Beware that PIC24E has RPI pins, which can be used only as inputs.
Here is a C-code example to remap OC1 to RP39 pin.
Now for the OC1 setup. Note that PIC24E has 2 control registers for each of the OCx module. For OC1 they are OC1CON1 and OC1CON2. Another difference (that took me a day to figure out) is the functionality of OC1R and OC1RS registers. We no longer have to waste a timer resource (unless you need a specified PWM frequency that cannot be derived from standard peripheral clock).
First, we clear all the contents of two control registers
OC1CON1 = 0x0000;
OC1CON2 = 0x0000;
In OC1CON1, we need to set the clock source. To use the peripheral clock, for example,
OC1CON1bits.OCTSEL = 0b111;
Then, the Output Compare Mode (OCM) is set to edge-aligned PWM mode
For OC1CON2, the Trigger/Synchronization Source is set to 0x1F.
OC1CON2bits.SYNCSEL = 0x1F;
This tells the module that OC1RS compare event is used for synchronization. The required setup for our purpose is now completed.
The real surprise happens when I try to update the PWM by writing to OC2RS and expect that OC1R would be updated automatically. It does not work that way anymore. Instead, we have to use OC1RS as the timer PR register in the dsPIC30F setup, and write directly to OC1R.
For example, suppose we set maximum PWM count as 1024. Write this value to OC1RS
Then, to generate 50% duty cycle, a value of 512 is written to OC1R
For good structure programming practice, it is recommended that the PWM update be constructed as a function
void setPWM(int pwmval)
OC1R = pwmval;
Note that OC1R and OC1RS are 16-bit registers. So in practice we could set a value of 65535 to OC1RS to achieve highest resolution. This in effect reduces the PWM frequency. Some tradeoffs might have to be made.