Skip to main content
GunkutA
Associate III
May 27, 2020
Solved

Prescaler and period values for PWM in STM32

  • May 27, 2020
  • 17 replies
  • 9960 views

I am using STM32F103C8Tx and I am trying to create a PID adjustable PWM. In PID I will be giving values to PWM from 0 to 100 to set the duty cycle. That means my counter period needs to be 100 in Cubemx. Because as I have learnt while I am changing the dutch cycle it changes the counter period. However, I need to create PWM with 200 KHz frequency and the clock of the timer is 72 MHz.In order to achieve a value of 100 in counter period I need to have a 3.6 prescaler. Am I following a wrong way or did I get this PWM duty cycle thing wrong in stm32?

Note: I am changing duty cycle from the code as

htimx.Instance->CCRx =Duty_Cycle;

 I think I cannot put codes as a reply so I will put them here:

static void MX_TIM3_Init(void)
{
 
 /* USER CODE BEGIN TIM3_Init 0 */
 
 /* USER CODE END TIM3_Init 0 */
 
 TIM_MasterConfigTypeDef sMasterConfig = {0};
 TIM_OC_InitTypeDef sConfigOC = {0};
 
 /* USER CODE BEGIN TIM3_Init 1 */
 
 /* USER CODE END TIM3_Init 1 */
 htim3.Instance = TIM3;
 htim3.Init.Prescaler = 1;
 htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
 htim3.Init.Period = 359;
 htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
 htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
 if (HAL_TIM_PWM_Init(&htim3) != HAL_OK)
 {
 Error_Handler();
 }
 sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
 sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
 if (HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig) != HAL_OK)
 {
 Error_Handler();
 }
 sConfigOC.OCMode = TIM_OCMODE_PWM1;
 sConfigOC.Pulse = 0;
 sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
 sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
 if (HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
 {
 Error_Handler();
 }
 if (HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_2) != HAL_OK)
 {
 Error_Handler();
 }
 /* USER CODE BEGIN TIM3_Init 2 */
 
 /* USER CODE END TIM3_Init 2 */
 HAL_TIM_MspPostInit(&htim3);
 
}
 
 
void SystemClock_Config(void)
{
 RCC_OscInitTypeDef RCC_OscInitStruct = {0};
 RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
 RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
 
 /** Initializes the CPU, AHB and APB busses clocks 
 */
 RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
 RCC_OscInitStruct.HSEState = RCC_HSE_ON;
 RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
 RCC_OscInitStruct.HSIState = RCC_HSI_ON;
 RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
 RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
 RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
 if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
 {
 Error_Handler();
 }
 /** Initializes the CPU, AHB and APB busses clocks 
 */
 RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
 |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
 RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
 RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
 RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
 RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
 
 if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
 {
 Error_Handler();
 }
 PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC|RCC_PERIPHCLK_USB;
 PeriphClkInit.AdcClockSelection = RCC_ADCPCLK2_DIV6;
 PeriphClkInit.UsbClockSelection = RCC_USBCLKSOURCE_PLL_DIV1_5;
 if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
 {
 Error_Handler();
 }
}

This topic has been closed for replies.
Best answer by berendi

The prescaler value is an integer. Check the timer feature description and register documentation in the reference manual, do not rely on libraries that leave important aspects undocumented.

You can either change the PID algorithm to output a value between 0 and 120 or 360, change the system clock to 60 MHz, or not use the prescaler at all and live with the 0.14 % error.

17 replies

berendi
berendiBest answer
Principal
May 27, 2020

The prescaler value is an integer. Check the timer feature description and register documentation in the reference manual, do not rely on libraries that leave important aspects undocumented.

You can either change the PID algorithm to output a value between 0 and 120 or 360, change the system clock to 60 MHz, or not use the prescaler at all and live with the 0.14 % error.

GunkutA
GunkutAAuthor
Associate III
May 27, 2020

I guess the easiest way to change PID output to 0 and 360 range. Set Prescaler to 2 and Counter Period to 179. So it will give me 200 kHz PWM while using 72 MHz clock. However, since program adds +1 to counter period. I will never have a %0 duty cycle. Is that correct?

waclawek.jan
Super User
May 27, 2020

> However, since program adds +1 to counter period. I will never have a %0 duty cycle. Is that correct?

No.

If CCRx = 0, you have 0% duty cycle; if CCR = ARR CCR > ARR you have 100% duty cycle (depending on the particular PWM mode you are using, this may be the other way round).

Read the TIM chapter in RM.

JW

berendi
Principal
May 27, 2020

Reference manual, TIMx capture/compare mode register 1 (TIMx_CCMR1) description:

110: PWM mode 1 - In upcounting, channel 1 is active as long as TIMx_CNT<TIMx_CCR1 else inactive.

When you want a counter period of 100 cycles, you set ARR = 99. CNT counts from 0 to 99, then starts again at 0.

To have 0% duty cycle, set CCR1 = 0. Because CNT can't be less than 0, the output is always inactive.

To have 100% duty cycle, set CCR1 = 100. Now CNT is always less than CCR1, because CNT is always in the 0..99 range, so the output is always active.

GunkutA
GunkutAAuthor
Associate III
May 27, 2020

Now eventhough APB1 Timer Clock is 72 MHz. Prescaler is 0 and Counter Period is 359. The frequency of the PWM is 12 Hz. I use blue pill STM32. What might be the cause . I know it is a little bit irrelevant from the topic but I didn't want to open a new topic for that.

berendi
Principal
May 27, 2020

Please post the contents of the RCC and timer registers.

waclawek.jan
Super User
May 27, 2020

> The frequency of the PWM is 12 Hz

How do you know?

JW

GunkutA
GunkutAAuthor
Associate III
May 27, 2020

I look it with oscillscope. here is the view:

0693W000001pY69QAE.jpg

GunkutA
GunkutAAuthor
Associate III
May 27, 2020

Here they are from CubeMX

RCC0693W000001pY2qQAE.png

Clock Config Screen

0693W000001pY30QAE.png

Timer 30693W000001pY35QAE.png

berendi
Principal
May 27, 2020

No, the contents of the hardware registers. Cube screenshots and and source code containing HAL function calls are useless for troubleshooting.

GunkutA
GunkutAAuthor
Associate III
May 27, 2020

I don't know how to check them... You mean these?

void SystemClock_Config(void)
{
 RCC_OscInitTypeDef RCC_OscInitStruct = {0};
 RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
 RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
 
 /** Initializes the CPU, AHB and APB busses clocks 
 */
 RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
 RCC_OscInitStruct.HSEState = RCC_HSE_ON;
 RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
 RCC_OscInitStruct.HSIState = RCC_HSI_ON;
 RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
 RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
 RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
.
.
.
}
 
 
static void MX_TIM3_Init(void)
{
 
 /* USER CODE BEGIN TIM3_Init 0 */
 
 /* USER CODE END TIM3_Init 0 */
geçe
 TIM_MasterConfigTypeDef sMasterConfig = {0};
 TIM_OC_InitTypeDef sConfigOC = {0};
 
 /* USER CODE BEGIN TIM3_Init 1 */
 
 /* USER CODE END TIM3_Init 1 */
 htim3.Instance = TIM3;
 htim3.Init.Prescaler = 1-1;
 htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
 htim3.Init.Period = 360-1;
 htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
 htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
.
.
.
}

GunkutA
GunkutAAuthor
Associate III
May 27, 2020

You mean these?

void SystemClock_Config(void)
{
 RCC_OscInitTypeDef RCC_OscInitStruct = {0};
 RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
 RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
 
 /** Initializes the CPU, AHB and APB busses clocks 
 */
 RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
 RCC_OscInitStruct.HSEState = RCC_HSE_ON;
 RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
 RCC_OscInitStruct.HSIState = RCC_HSI_ON;
 RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
 RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
 RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
 if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
 {
 Error_Handler();
 }
 /** Initializes the CPU, AHB and APB busses clocks 
 */
 RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
 |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
 RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
 RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
 RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
 RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
 
 if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
 {
 Error_Handler();
 }
 PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC|RCC_PERIPHCLK_USB;
 PeriphClkInit.AdcClockSelection = RCC_ADCPCLK2_DIV6;
 PeriphClkInit.UsbClockSelection = RCC_USBCLKSOURCE_PLL_DIV1_5;
 if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
 {
 Error_Handler();
 }
}
 
 
 
 
static void MX_TIM3_Init(void)
{
 
 /* USER CODE BEGIN TIM3_Init 0 */
 
 /* USER CODE END TIM3_Init 0 */
geçe
 TIM_MasterConfigTypeDef sMasterConfig = {0};
 TIM_OC_InitTypeDef sConfigOC = {0};
 
 /* USER CODE BEGIN TIM3_Init 1 */
 
 /* USER CODE END TIM3_Init 1 */
 htim3.Instance = TIM3;
 htim3.Init.Prescaler = 1-1;
 htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
 htim3.Init.Period = 360-1;
 htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
 htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
 if (HAL_TIM_PWM_Init(&htim3) != HAL_OK)
 {
 Error_Handler();
 }
 sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
 sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
 if (HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig) != HAL_OK)
 {
 Error_Handler();
 }
 sConfigOC.OCMode = TIM_OCMODE_PWM1;
 sConfigOC.Pulse = 0;
 sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
 sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
 if (HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
 {
 Error_Handler();
 }
 if (HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_2) != HAL_OK)
 {
 Error_Handler();
 }
 /* USER CODE BEGIN TIM3_Init 2 */
 
 /* USER CODE END TIM3_Init 2 */
 HAL_TIM_MspPostInit(&htim3);
 
}