From ab45ef0378d3afe461bbbec0d2c77b56fce52e5f Mon Sep 17 00:00:00 2001 From: Laurent MEUNIER Date: Tue, 28 Jun 2016 18:19:58 +0200 Subject: [PATCH] [STM32F4] Handle higher range pwm periods As first reported on STM32F3 family in #1682, we need to cope with periods in the seconds range as well. This is fixed here in the same way as was done for STM32F3 by using the pre-scaler. --- .../TARGET_STM32F4/common_objects.h | 1 + .../TARGET_STM/TARGET_STM32F4/pwmout_api.c | 39 ++++++++++++++----- 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/hal/targets/hal/TARGET_STM/TARGET_STM32F4/common_objects.h b/hal/targets/hal/TARGET_STM/TARGET_STM32F4/common_objects.h index 69a0cb6add..a3c3410cb3 100644 --- a/hal/targets/hal/TARGET_STM/TARGET_STM32F4/common_objects.h +++ b/hal/targets/hal/TARGET_STM/TARGET_STM32F4/common_objects.h @@ -42,6 +42,7 @@ extern "C" { struct pwmout_s { PWMName pwm; PinName pin; + uint32_t prescaler; uint32_t period; uint32_t pulse; uint8_t channel; diff --git a/hal/targets/hal/TARGET_STM/TARGET_STM32F4/pwmout_api.c b/hal/targets/hal/TARGET_STM/TARGET_STM32F4/pwmout_api.c index 6bddceda80..b908c5a0af 100644 --- a/hal/targets/hal/TARGET_STM/TARGET_STM32F4/pwmout_api.c +++ b/hal/targets/hal/TARGET_STM/TARGET_STM32F4/pwmout_api.c @@ -94,6 +94,7 @@ void pwmout_init(pwmout_t* obj, PinName pin) obj->pin = pin; obj->period = 0; obj->pulse = 0; + obj->prescaler = 1; pwmout_period_us(obj, 20000); // 20 ms per default } @@ -121,7 +122,7 @@ void pwmout_write(pwmout_t* obj, float value) // Configure channels sConfig.OCMode = TIM_OCMODE_PWM1; - sConfig.Pulse = obj->pulse; + sConfig.Pulse = obj->pulse / obj->prescaler; sConfig.OCPolarity = TIM_OCPOLARITY_HIGH; sConfig.OCNPolarity = TIM_OCNPOLARITY_HIGH; sConfig.OCFastMode = TIM_OCFAST_DISABLE; @@ -240,26 +241,45 @@ void pwmout_period_us(pwmout_t* obj, int us) default: return; } - - TimHandle.Init.Period = us - 1; + + /* To make it simple, we use to possible prescaler values which lead to: + * pwm unit = 1us, period/pulse can be from 1us to 65535us + * or + * pwm unit = 500us, period/pulse can be from 500us to ~32.76sec + * Be careful that all the channels of a PWM shares the same prescaler + */ + if (us > 0xFFFF) { + obj->prescaler = 500; + } else { + obj->prescaler = 1; + } + // TIMxCLK = PCLKx when the APB prescaler = 1 else TIMxCLK = 2 * PCLKx if (APBxCLKDivider == RCC_HCLK_DIV1) - TimHandle.Init.Prescaler = (uint16_t)((PclkFreq) / 1000000) - 1; // 1 us tick + TimHandle.Init.Prescaler = (uint16_t)(((PclkFreq) / 1000000) * obj->prescaler) - 1; // 1 us tick else - TimHandle.Init.Prescaler = (uint16_t)((PclkFreq * 2) / 1000000) - 1; // 1 us tick + TimHandle.Init.Prescaler = (uint16_t)(((PclkFreq * 2) / 1000000) * obj->prescaler) - 1; // 1 us tick + + if (TimHandle.Init.Prescaler > 0xFFFF) + error("PWM: out of range prescaler"); + + TimHandle.Init.Period = (us - 1) / obj->prescaler; + if (TimHandle.Init.Period > 0xFFFF) + error("PWM: out of range period"); + TimHandle.Init.ClockDivision = 0; TimHandle.Init.CounterMode = TIM_COUNTERMODE_UP; - + if (HAL_TIM_PWM_Init(&TimHandle) != HAL_OK) { error("Cannot initialize PWM\n"); } - // Set duty cycle again - pwmout_write(obj, dc); - // Save for future use obj->period = us; + // Set duty cycle again + pwmout_write(obj, dc); + __HAL_TIM_ENABLE(&TimHandle); } @@ -276,6 +296,7 @@ void pwmout_pulsewidth_ms(pwmout_t* obj, int ms) void pwmout_pulsewidth_us(pwmout_t* obj, int us) { float value = (float)us / (float)obj->period; + printf("pwmout_pulsewidth_us: period=%d, us=%d, dc=%d%%\r\n", obj->period, us, (int) (value*100)); pwmout_write(obj, value); }