cancel
Showing results for 
Search instead for 
Did you mean: 

HAL_TIM_PWM_PulseFinishedCallback never called but HAL_TIM_PeriodElapsedCallback is...

RPG
Senior

I am very new to embedded programming and I have to modify some code from other developers.

I have to disable the outputs for a PWM and add a callback to get the state of the pulse instead of sending the pulse to the outputs.

I was able to disable the output on PIN 6 but the callback HAL_TIM_PWM_PulseFinishedCallback is never called. HAL_TIM_PeriodElapsedCallback instead is called all times.

I have found the reason why HAL_TIM_PWM_PulseFinishedCallback is never called is because this condition from stm32g4xx_hal_tim.c is always returning RESET:

    if (__HAL_TIM_GET_IT_SOURCE(htim, TIM_IT_CC1) != RESET)

Could somebody help me out?

Thanks in advance.

8 REPLIES 8

> add a callback to get the state of the pulse

What do you mean exactly by "callback to get the state of the pulse"? Draw a diagram.

> the callback HAL_TIM_PWM_PulseFinishedCallback is never called.

You cannot expect a random function/callback to be called just based on its name. As you've found out, that callback is called upon CC interrupt, which therefore must be enabled, and by further reading Cube code, it is called only when the given channel is set as Output Compare.

Read the TIM chapter in RM. You may want to get rid of Cube/HAL and write your own code.

JW

RPG
Senior

I need to generate pulses with a given duty cycle and need to detect when the pulse is rising and falling before and after the duty cycle.

The idea is that the PWM was controlling the strobe of a lamp by turning on and off the output. But that's not possible anymore and the lamp must be have voltage continuously and the strobe must be controlled my changing the brightness of the lamp instead. What I did is to disable the output at the PIN number 6 for channel 1. But I would like to get the rise and fall events at the beginning and the end of the duty cycle so that I can change the brightness of the lamp between 0 and 99 to perform the strobe.

0693W00000NqsVOQAZ.png 

I have set this on my code:

HAL_TIM_OC_Start_IT(&htim3,TIM_CHANNEL_1);
HAL_TIM_PWM_Start_IT(&htim3,TIM_CHANNEL_1);

And this is how the channel is setup:

  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();
  }

Thank you for your help. I am quite lost with this. I am a total beginner and just found out as you said that HAL is kind of complicated and not super well documented. I also have to learn about the these microcontrollers.

I forgot to ask: which STM32?

> I am quite lost with this. I am a total beginner

Try this: set up the PWM to output say 50% duty onto the pin, observe it using oscilloscope/logic analyzer. Then open the reference manual in the Timer chapter, at the end where the registers are described, and in the debugger, stop program execution, and play with the given timer's registers, observing what are the effects of what you do.

> sConfigOC.Pulse = 0;

By doing this, you effectively disable the PWM entirely, as you set 0% duty cycle.

If you want to run the timer's channel in PWM mode and not have it output onto its pin, simply disable given channel by clearing its respective TIMx_CCER.CCxE bit. You can also change the given pin's setting, e.g. in its respective GPIOx_MODER field change it to Input. In both cases the timer continues to run and the channel continues to generate the PWM, except that it is not propagated to the pin.

The Update interrupt is triggered by one edge of the pulse, and the CCx interrupt is triggered by the other. So enable the CC1 interrupt by setting TIMx_DIER.CCxIE.

For everything I wrote above there is certainly a Cube/HAL macro, but I don't use Cube/HAL so I don't know them. You can look them easily up yourself, Cube is open source.

> you said that HAL is kind of complicated

Yes and no. Cube/HAL is primarily a vehicle for the CubeMX-generated code. That means, that by clicking and perhaps slightly modifying the generated code you can rapidly generate certain types of applications, and they are said to be easily portable to other STM32. But as soon as you depart from the intentions of Cube authors, you'll struggle, as you'll need to learn not only what you wanted to avoid at the first place, i.e. how the "nasty" hardware works, but also how Cube exactly uses that nasty hardware, and that's double the work. You may get lucky to never need that, though, or you may find that easy and refreshing.

> not super well documented

Well, there are those thousand-page long doxygen-autogenerated pdfs... Have you read the one belonging to your STM32?

JW

Thank you again.

You are being of great help. It starts making sense.

I am using the STM32G474.

sConfigOC.Pulse = 0; is used initially I have seen some code in another part of the app where the CCR1 and ARR get updated.

I have read many documents and watched many tutorials and code but nothing worked so far. But with all that and your explanation I am starting to understand. Maybe I was unable to find the right documentation.

I will try all you have suggested tomorrow morning when I am fresh again. And will be back with some feedback.

RPG
Senior

I have been able to disable the output by:

GPIO_InitStruct.Mode = GPIO_MODE_INPUT;

But still not luck with the interrupts. I am using this code to enable them but HAL_TIM_PWM_PulseFinishedCallback still not called:

__HAL_TIM_ENABLE_IT(&htim3, TIM_IT_UPDATE);
__HAL_TIM_ENABLE_IT(&htim3, TIM_IT_CC1);
__HAL_TIM_ENABLE_IT(&htim3, TIM_IT_CC2);
__HAL_TIM_ENABLE_IT(&htim3, TIM_IT_CC3);
__HAL_TIM_ENABLE_IT(&htim3, TIM_IT_CC4);

 HAL_TIM_PeriodElapsedCallback is called constantly.

Read out and check/post the TIM registers content.

JW

I could manage by editing with the HAL tool. I had to change the output channel to Generate No Output in CubeMX and started working. Also had to place the above code in the right place.

Thank you for your help 🙂

GreenGuy
Lead

Sorry for answering this so late, but I just now came across the post and thought there is a simple answer if you really want to use HAL. I think what you were asking is very straight forward regardless the series of the processor.

It goes like this:

First, in CubeMX set up the timer (In my case I used Timer3)

In the Mode window set:

Slave Mode - Disabled

Trigger Source - Disable

Internal clock - checked (guess this could use external as well)

Channel1 - PWM Generation No Output (or output it if disired)

Channel 2 - Channel 3 same or disable depending on how many PWMs are needed.

One Pulse Mode - unchecked

Also set the default Counter period and prescaler as needed to meet your resolution requirements.  set the auto-reload preload to Enable.

Then in the Project Manager tab, advanced settings, over the right side Enable the TIM Register Callback.

In the Main.c, find the static void MX_TIM3_Init(void) routine and at the end, after the line

/* USER CODE BEGIN TIM3_Init 2 */

insert the following:

TIM3->CCR1 = [default start value];

HAL_TIM_PWM_Start_IT(&htim3,TIM_CHANNEL_1);

HAL_TIM_Base_Start_IT(&htim3);

you can add other CCRx defaults and timer channels as needed.

Then in the /* USER CODE BEGIN 4 */ section (or where ever you want to put the callbacks) put:

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim3)

{

[your code to do what is needed when the period is done]

}

 

void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim3)

{

[your code to do what is needed when the active period is done]

}

 

I did this with just one channel so I am not checking to see which channel triggered the event. If you use other channels you will need code that determines which channel fired.

Also, before the Start_IT calls you may want to clear the Status Register bits otherwise when the calls to Start_IT are made the Pulse Finished Callback will fire a couple of times before the PWM action kicks in.