Ok if you want to exactly set the duty cycle all you have to do is, to calculate the value you are gonna store into the capture compare register.
The thing is that you have your timer which counts up to the value you have specified in the auto reload register (ARR). If the timer reaches this value it restarts counting from zero.
Now if you are using PWM mode, this counter speciefies your PWM signal. Means if the value of the counter is smaller than the value stored in the capture compare register, the PWM signal is on a high level. If the counter is higher than the value stored in the capture compare register the PWM signal is on a low level. If the counter reaches the value of the ARR it resets to zero.
This is how your PWM works. Same as in the picture.

OCxREF is the PWM output signal and the TIM_ARR value is 8.
Now to calculate the PWM in percentage you have to do it like that:
CCR = (ARR * dutyCycle) / 100 → dutyCycle is here in percent (e.g 25 for 25%)
It is quite easy. The only thing you need is your duty cycle you want to set and the value you have configured in the auto reload register (ARR)
Here is also the modified code that allows you to set the duty cycle in percentage:
#include "main.h"
TIM_HandleTypeDef htim12;
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_TIM12_Init(void);
enum {
TIM_MAX_CNT_VAL = 65535, // the maximum value the PWM timer will count to (TIMx_ARR)
MAX_DUTY_CYCLE_PERCENT = 100,
MIN_DUTY_CYCLE_PERCENT = 0
};
void setPWMDutyCycle (TIM_HandleTypeDef *handler, uint32_t channel, uint32_t dutyCycle)
{
if (dutyCycle > (uint32_t)MAX_DUTY_CYCLE_PERCENT)
{
return;
}
const uint32_t captureCompareVal = (dutyCycle * (uint32_t)TIM_MAX_CNT_VAL) / 100; // 100 because 100 %
TIM_OC_InitTypeDef channelConfig = {
.Pulse = captureCompareVal,
.OCMode = TIM_OCMODE_PWM1
};
HAL_TIM_PWM_Stop(handler, channel);
HAL_TIM_PWM_ConfigChannel(handler, &channelConfig, channel);
HAL_TIM_PWM_Start(handler, channel);
}
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_TIM12_Init();
TIM_Base_InitTypeDef config = {
.Period = TIM_MAX_CNT_VAL,
.CounterMode = TIM_COUNTERMODE_UP,
.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE
};
HAL_TIM_PWM_Init(&htim12);
TIM_Base_SetConfig(htim12.Instance, &config);
const uint32_t delayMs = 100U;
for(;;)
{
for(int dutyCycle = MIN_DUTY_CYCLE_PERCENT; dutyCycle <= MAX_DUTY_CYCLE_PERCENT; ++dutyCycle)
{
setPWMDutyCycle(&htim12, TIM_CHANNEL_1, (uint32_t)dutyCycle);
HAL_Delay(delayMs);
}
for(int dutyCycle = MAX_DUTY_CYCLE_PERCENT; dutyCycle >= MIN_DUTY_CYCLE_PERCENT; --dutyCycle)
{
setPWMDutyCycle(&htim12, TIM_CHANNEL_1, (uint32_t)dutyCycle);
HAL_Delay(delayMs);
}
}
}
I really hope this helps you. And as always, if something is not clear please ask.
Wish you a good night (depends on your local time ;))