cancel
Showing results for 
Search instead for 
Did you mean: 

How to set a glitch free 100% duty cycle PWM output

WSpar.1
Associate III

To control a DC motor driver I need to set one pin permanently high and the other is inverse PWM.

If I want to reverse the motor direction I need to swap these settings.

So I configured both pins as PWM output.

But if I want to set one output pin high the following code is ignored:

HAL_GPIO_WritePin(GPIOA, MOTOR_IN1_Pin, GPIO_PIN_SET);

The pin stays low, maybe because the pin is officially not in GPIO mode?

So I thought ok setting 100% duty has the same effect, but when I measure on the scope you see small glitches to zero volt.

So my question can be solved two ways:

How do I make my PWM output pin accept also WritePin commands?

OR

How can I get rid of the small glitches in my 100% duty cycle?

1 ACCEPTED SOLUTION

Accepted Solutions
KnarfB
Principal III

> So I thought ok setting 100% duty has the same effect

It should work without glitches. Lets say TIM1 counts up with 1 kHz frequency (prescaler 7999 at 8 MHz clock) and you want a period of 100ms. So you set up a counter period of 99. With a PWM pulse setting of 0 you get 0% PWM and with a PWM pulse setting of 100 you get 100% PWM.

Anyway, you can toggle the mode of the pin between PWM and GPIO. Using HAL this is a bit of overhead, as GPIO init (for another pin) is

/*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET);
 
  /*Configure GPIO pin : LED_Pin */
  GPIO_InitStruct.Pin = LED_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(LED_GPIO_Port, &GPIO_InitStruct);

whereas PWM output mode init is (code in HAL_TIM_MspPostInit):

GPIO_InitStruct.Pin = GPIO_PIN_9|GPIO_PIN_10;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    GPIO_InitStruct.Alternate = GPIO_AF2_TIM1;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

the only difference is the .Mode which can be switched easily at the register level (GPIOx->MODER).

If already at register level: Once you have PM mode running, there is a forced output mode for each channel, by setting the OCxM bits in the TIMx_CCMRx register to to 100 rsp. 101. See reference manual.

View solution in original post

3 REPLIES 3
KnarfB
Principal III

> So I thought ok setting 100% duty has the same effect

It should work without glitches. Lets say TIM1 counts up with 1 kHz frequency (prescaler 7999 at 8 MHz clock) and you want a period of 100ms. So you set up a counter period of 99. With a PWM pulse setting of 0 you get 0% PWM and with a PWM pulse setting of 100 you get 100% PWM.

Anyway, you can toggle the mode of the pin between PWM and GPIO. Using HAL this is a bit of overhead, as GPIO init (for another pin) is

/*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET);
 
  /*Configure GPIO pin : LED_Pin */
  GPIO_InitStruct.Pin = LED_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(LED_GPIO_Port, &GPIO_InitStruct);

whereas PWM output mode init is (code in HAL_TIM_MspPostInit):

GPIO_InitStruct.Pin = GPIO_PIN_9|GPIO_PIN_10;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    GPIO_InitStruct.Alternate = GPIO_AF2_TIM1;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

the only difference is the .Mode which can be switched easily at the register level (GPIOx->MODER).

If already at register level: Once you have PM mode running, there is a forced output mode for each channel, by setting the OCxM bits in the TIMx_CCMRx register to to 100 rsp. 101. See reference manual.

dhch
Associate

For anybody stumbling upon the same behaviour:

The glitching at 100% duty-cycle is probably due to calculating the duty-cycle to be a % of TIMx->ARR. The calculated value probably is just not the real 100%, but something a bit below. So when setting the duy-cycle, just make an extra if-statement that handles those 100% to be the actual value of ARR

if (duty == 100) {
  TIMx->CCRx = TIMx->ARR;
}

 

The code above guarantees the glitch. To get full duty, set CCR to ARR + 1.

The problem does not exist in a properly written code. If you want to express duty in percent, set the period to 100 (so set ARR to 99), then load your percent value to CCR - no arithmetics needed. You may regulate the PWM frequency by setting the prescaler in PSC register.

My STM32 stuff on github - compact USB device stack and more: https://github.com/gbm-ii/gbmUSBdevice