Bug in pwmout_api.c

03 Feb 2014

I believe I've found a bug in pwmout_api.c. This applies to the LPC1768 and possibly others that use similar code. I searched for other reports of this issue on mbed.org, but didn't find anything.

While using the PwmOut driver, I noticed that if I changed the PWM period, the duty cycle also changed (because the pulse width did not change). According to the PwmOut class documentation, the duty cycle should be maintained at its previous value whenever the period is changed. It's possible that this hasn't been reported due to the likelihood that PWM period is rarely updated dynamically. So, users may not be looking for this type of issue.

I tracked this problem down to the following code in pwmout_api.c:

// Set the PWM period, keeping the duty cycle the same.
void pwmout_period_us(pwmout_t* obj, int us) {
    // calculate number of ticks
    uint32_t ticks = pwm_clock_mhz * us;

    // set reset
    LPC_PWM1->TCR = TCR_RESET;

    // set the global match register
    LPC_PWM1->MR0 = ticks;

    // Scale the pulse width to preserve the duty ratio
    if (LPC_PWM1->MR0 > 0) {
        *obj->MR = (*obj->MR * ticks) / LPC_PWM1->MR0;
    }

    // set the channel latch to update value at next period start
    LPC_PWM1->LER |= 1 << 0;

    // enable counter and pwm, clear reset
    LPC_PWM1->TCR = TCR_CNT_EN | TCR_PWM_EN;
}

Note that on entry, ticks is set to the new period in usec. This is then used to set the new match register value (the new period), LPC_PWM1->MR0. Subsequently, in the if() {...} multiply/divide calculation, ticks is equal to LPC_PWM1->MR0. So, the resulting value of *obj->MR, the pulse width, is unchanged from its previous value, in this calculation:

        *obj->MR = (*obj->MR * ticks) / LPC_PWM1->MR0;

At a minimum, I believe the assignment LPC_PWM1->MR0 = ticks; should be moved just after the if() {...} statement.

If anyone else has time, could you try to confirm my findings?

Also, all PWM channel's MR (pulse width) would need to be changed whenever any PWM channel period is changed. That is because all PWM channels share the same period. I wanted to add that I created an issue at the following link:

https://mbed.org/users/mbed_official/code/mbed-src/issues/3

Any discussion should occur at that location. In the future, I will not be updating the discussion at this location in the Bugs & Suggestions forum.