mbed library sources

Dependents:   Encrypted my_mbed lklk CyaSSL_DTLS_Cellular ... more

Issue: pwmout_api.c wrong period/frequency for STM43F4 series

Issue:- When using the pwmout_api as below the instead of a 1KHz for the 1ms period then it results in 500Hz or PWM1/1 and 2KHz for PWM3/1 (Checked with PWM2 and PWM4 both same as PWM3) for the NUCLEO-STM32F401RE but since this is used for the NUCLEO STM32F4 I suspect others may be affected also.

#include "mbed.h"

PwmOut mypwm(PA_6);     //PWM3/1
PwmOut mypwm2(PA_7);    //PWM1/1N
DigitalOut myled(LED1);

int main() {

    mypwm.period_ms(1);
    mypwm.pulsewidth_us(500);
  
    mypwm2.period_ms(1);
    mypwm2.pulsewidth_us(500);
  
    while(1) {
        myled = !myled;
        wait(1);
    }
}

Source:-

mbed-src/targets/hal/TARGET_STM/TARGET_STM32F4/pwmout_api.c

Correction Required:- Divisor incorrectly applied to above API on Lines 244 and 246 Change required at * lines

 /* mbed Microcontroller Library
 *******************************************************************************
 * Copyright (c) 2014, STMicroelectronics
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * 3. Neither the name of STMicroelectronics nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *******************************************************************************
 */
#include "pwmout_api.h"

#if DEVICE_PWMOUT

#include "cmsis.h"
#include "pinmap.h"
#include "mbed_error.h"
#include "PeripheralPins.h"

static TIM_HandleTypeDef TimHandle;

void pwmout_init(pwmout_t* obj, PinName pin)
{
    // Get the peripheral name from the pin and assign it to the object
    obj->pwm = (PWMName)pinmap_peripheral(pin, PinMap_PWM);

    // Get the functions (timer channel, (non)inverted) from the pin and assign it to the object
    uint32_t function = pinmap_function(pin, PinMap_PWM);
    MBED_ASSERT(function != (uint32_t)NC);
    obj->channel  = STM_PIN_CHANNEL(function);
    obj->inverted = STM_PIN_INVERTED(function);

    if (obj->pwm == (PWMName)NC) {
        error("PWM error: pinout mapping failed.");
    }

    // Enable TIM clock
    if (obj->pwm == PWM_1) __TIM1_CLK_ENABLE();
    if (obj->pwm == PWM_2) __TIM2_CLK_ENABLE();
    if (obj->pwm == PWM_3) __TIM3_CLK_ENABLE();
    if (obj->pwm == PWM_4) __TIM4_CLK_ENABLE();
#if defined(TIM8_BASE)
    if (obj->pwm == PWM_8) __TIM8_CLK_ENABLE();
#endif
    if (obj->pwm == PWM_9) __TIM9_CLK_ENABLE();
    if (obj->pwm == PWM_10) __TIM10_CLK_ENABLE();
    if (obj->pwm == PWM_11) __TIM11_CLK_ENABLE();
#if defined(TIM13_BASE)
    if (obj->pwm == PWM_13) __TIM13_CLK_ENABLE();
#endif
#if defined(TIM14_BASE)
    if (obj->pwm == PWM_14) __TIM14_CLK_ENABLE();
#endif

    // Configure GPIO
    pinmap_pinout(pin, PinMap_PWM);

    obj->pin = pin;
    obj->period = 0;
    obj->pulse = 0;

    pwmout_period_us(obj, 20000); // 20 ms per default
}

void pwmout_free(pwmout_t* obj)
{
    // Configure GPIO
    pin_function(obj->pin, STM_PIN_DATA(STM_MODE_INPUT, GPIO_NOPULL, 0));
}

void pwmout_write(pwmout_t* obj, float value)
{
    TIM_OC_InitTypeDef sConfig;
    int channel = 0;
    int complementary_channel = 0;

    TimHandle.Instance = (TIM_TypeDef *)(obj->pwm);

    if (value < (float)0.0) {
        value = 0.0;
    } else if (value > (float)1.0) {
        value = 1.0;
    }

    obj->pulse = (uint32_t)((float)obj->period * value);

    // Configure channels
    sConfig.OCMode       = TIM_OCMODE_PWM1;
    sConfig.Pulse        = obj->pulse;
    sConfig.OCPolarity   = TIM_OCPOLARITY_HIGH;
    sConfig.OCNPolarity  = TIM_OCNPOLARITY_HIGH;
    sConfig.OCFastMode   = TIM_OCFAST_DISABLE;
    sConfig.OCIdleState  = TIM_OCIDLESTATE_RESET;
    sConfig.OCNIdleState = TIM_OCNIDLESTATE_RESET;

    complementary_channel = obj->inverted;
    switch (obj->channel) {

        case 1:
            channel = TIM_CHANNEL_1;
            break;

        case 2:
            channel = TIM_CHANNEL_2;
            break;

        case 3:
            channel = TIM_CHANNEL_3;
            break;

        case 4:
            channel = TIM_CHANNEL_4;
            break;

        default:
            return;
    }

    HAL_TIM_PWM_ConfigChannel(&TimHandle, &sConfig, channel);
    if (complementary_channel) {
        HAL_TIMEx_PWMN_Start(&TimHandle, channel);
    } else {
        HAL_TIM_PWM_Start(&TimHandle, channel);
    }
}

float pwmout_read(pwmout_t* obj)
{
    float value = 0;
    if (obj->period > 0) {
        value = (float)(obj->pulse) / (float)(obj->period);
    }
    return ((value > (float)1.0) ? (float)(1.0) : (value));
}

void pwmout_period(pwmout_t* obj, float seconds)
{
    pwmout_period_us(obj, seconds * 1000000.0f);
}

void pwmout_period_ms(pwmout_t* obj, int ms)
{
    pwmout_period_us(obj, ms * 1000);
}

void pwmout_period_us(pwmout_t* obj, int us)
{
    TimHandle.Instance = (TIM_TypeDef *)(obj->pwm);
    RCC_ClkInitTypeDef RCC_ClkInitStruct;
    uint32_t PclkFreq;
    uint32_t APBxCLKDivider;
    float dc = pwmout_read(obj);

    __HAL_TIM_DISABLE(&TimHandle);

    // Update the SystemCoreClock variable
    SystemCoreClockUpdate();

    HAL_RCC_GetClockConfig(&RCC_ClkInitStruct, &PclkFreq);

    switch (obj->pwm) {

        case PWM_1:
            PclkFreq       = HAL_RCC_GetPCLK2Freq();
            APBxCLKDivider = RCC_ClkInitStruct.APB2CLKDivider;
            break;

        case PWM_2:
            PclkFreq       = HAL_RCC_GetPCLK1Freq();
            APBxCLKDivider = RCC_ClkInitStruct.APB1CLKDivider;
            break;

        case PWM_3:
            PclkFreq       = HAL_RCC_GetPCLK1Freq();
            APBxCLKDivider = RCC_ClkInitStruct.APB1CLKDivider;
            break;

        case PWM_4:
            PclkFreq       = HAL_RCC_GetPCLK1Freq();
            APBxCLKDivider = RCC_ClkInitStruct.APB1CLKDivider;
            break;

#if defined(TIM8_BASE)
        case PWM_8:
            PclkFreq       = HAL_RCC_GetPCLK2Freq();
            APBxCLKDivider = RCC_ClkInitStruct.APB2CLKDivider;
            break;
#endif

        case PWM_9:
            PclkFreq       = HAL_RCC_GetPCLK2Freq();
            APBxCLKDivider = RCC_ClkInitStruct.APB2CLKDivider;
            break;

        case PWM_10:
            PclkFreq       = HAL_RCC_GetPCLK2Freq();
            APBxCLKDivider = RCC_ClkInitStruct.APB2CLKDivider;
            break;

        case PWM_11:
            PclkFreq       = HAL_RCC_GetPCLK2Freq();
            APBxCLKDivider = RCC_ClkInitStruct.APB2CLKDivider;
            break;

#if defined(TIM13_BASE)
        case PWM_13:
            PclkFreq       = HAL_RCC_GetPCLK1Freq();
            APBxCLKDivider = RCC_ClkInitStruct.APB1CLKDivider;
            break;
#endif

#if defined(TIM14_BASE)
        case PWM_14:
            PclkFreq       = HAL_RCC_GetPCLK1Freq();
            APBxCLKDivider = RCC_ClkInitStruct.APB1CLKDivider;
            break;
#endif

        default:
            return;
    }

    TimHandle.Init.Period        = us - 1;
    if (APBxCLKDivider == RCC_HCLK_DIV1)
      TimHandle.Init.Prescaler   = (uint16_t)((PclkFreq) / 1000000) - 1; // 1 s tick was PclkFreq*2 changed to PclkFreq
    else
      TimHandle.Init.Prescaler   = (uint16_t)((PclkFreq*2) / 1000000) - 1; // 1 s tick was PclkFreq changed to PclkFreq*2
    TimHandle.Init.ClockDivision = 0;
    TimHandle.Init.CounterMode   = TIM_COUNTERMODE_UP;
    HAL_TIM_PWM_Init(&TimHandle);

    // Set duty cycle again
    pwmout_write(obj, dc);

    // Save for future use
    obj->period = us;

    __HAL_TIM_ENABLE(&TimHandle);
}

void pwmout_pulsewidth(pwmout_t* obj, float seconds)
{
    pwmout_pulsewidth_us(obj, seconds * 1000000.0f);
}

void pwmout_pulsewidth_ms(pwmout_t* obj, int ms)
{
    pwmout_pulsewidth_us(obj, ms * 1000);
}

void pwmout_pulsewidth_us(pwmout_t* obj, int us)
{
    float value = (float)us / (float)obj->period;
    pwmout_write(obj, value);
}

#endif

After the above was edited as shown then all PWM periods selected were correctly resolved

HAL Clocks are as follows:- PCLK1=42000000 PCLK2=84000000 HCLK=84000000

Kind Regards

Martin

2 comments:

27 Jul 2015

Thanks for reporting and even providing fix. Can you report t his to https://github.com/mbedmicro/mbed/issues ? You can send PR fixing the problem.

I believe this issue tracker will be closed soon, as github issues is the place to report issues in mbed SDK.

27 Jul 2015

Hi Martin Ok will do Regards Martin