I am new to HAL drivers and STM32 MCUs. Can anyone tell me how to use HAL drivers to update register values of STM32F407 MCU? I have to programatically update the values of ARR and CCR registers of TIM1 to generate PWM waveforms.

  • 4
    \$\begingroup\$ If you're using the HAL drivers, then you typically don't want to directly change the values of these peripheral config registers - that's a big part of what the HAL drivers do for you. You figure what high-level configuration you want and then tell the HAL drivers ' hey, gimme that config', and the driver code takes are of the actual register values. \$\endgroup\$
    – brhans
    Jan 26, 2019 at 18:10
  • \$\begingroup\$ where can i find that config function? \$\endgroup\$
    – scico111
    Jan 26, 2019 at 18:19
  • 1
    \$\begingroup\$ In ST's documentation for the HAL drivers, and in the .h files of the drivers themselves. \$\endgroup\$
    – brhans
    Jan 26, 2019 at 18:20

3 Answers 3


You can configure PWM signals and generate this codes by using prgram interface of CubeMX.
enter image description here

I used Tim4 for generate 1 Hz frequency PWM. My clock Speed on this project 168MHz. it is imported for calculate PWM frequency.

enter image description here

Tim4 depend APB1 Peripheral clock. APB1 Clock frequency is System Clock Frequency / 2. So my clock frequency is 84MHz. I changed presceler value to 42000 and changed my counter periyod to 2000 to set PWM frequency to 1Hz. Because 84MHz/(42k*2k) = 1. Tim1 depend APB2 peripheral clock and this clock frequency is System clock. So when use same values for timer1 its end up 2Hz PWM.

As a result you dont need to change register for generate pwm. You can use cubemx for this. I also share my project main code and github link in order to examine. In my project i use 4 channel of tim4 because i generate 4 different duty cycle values (%25, %50, %75, %100).

/*TIM2 PWM gönderimi için channellar ayarlanir */

Ayarlanmis channellarin duty cycle ayarlari yapilir.
CH1 (D12) için %25
CH2 (D12) için %50
CH3 (D12) için %75
CH4 (D12) için %100
Prescaler = 42000, Counter Period = 2000 toplam çarpimi 84Mhz  dolayisiyla sinyal 1 Hz 
Periyot degerinin duty cyclea göre istedigimiz gibi dagitabiliriz. %25 için 2000*25/100 = 500
__HAL_TIM_SetCompare(&htim4, TIM_CHANNEL_1, 500  - 1);
__HAL_TIM_SetCompare(&htim4, TIM_CHANNEL_2, 1000 - 1);
__HAL_TIM_SetCompare(&htim4, TIM_CHANNEL_3, 1500 - 1);
__HAL_TIM_SetCompare(&htim4, TIM_CHANNEL_4, 2000 - 1);




While it would be tempting to think that if you are going to use the HAL libraries, you should maintain that abstraction and not pierce it, in fact it is often necessary to bypass them.

If you look at the source of stm32f4xx_hal_tim.c (for example in mbed's github, since ST prefers zip files) you will see that the only functions which set the CCRx registers directly (vs via DMA) are the kitchen-sink calls of the TIM_OCx_SetConfig() family. As these are about 60 lines each and pull and manipulate values from a struct, they're a horribly inefficient way to so something ordinary like update the output of a PID control loop over and over.

Instead, either reading through the code or by reading the relevant Programmers Manual, you can learn that after getting the peripheral setup (with the aid of the HAL or by hand), what you likely want to do is directly write to the register, for example here is relevant line buried in one of those kitchen-sink functions:

/* Set the Capture Compare Register value */  
TIMx->CCR3 = OC_Config->Pulse;

Given the HAL just does not offer a practical way to update this register frequently, basically your only choices are to:

1) Modify the HAL to add a function that does the above, possibly trying to set it up to be inlined, this can work but you then get to maintain a private fork of the vendor hal, though in a large enough project you may be doing that for bugfix or code format reasons anyway.

2) Write your own meta-HAL that adds this while wrapping the vendor one

3) Just put the relevant register right in the code, either directly, or via an application-oriented abstraction like:




RIGHT_FRONT_PWM = pid_out[1];

... or you can throw the whole thing out in favor of a 3rd party HAL or write an original HAL that meets your preferences and needs.

  • \$\begingroup\$ So does it mean that its safe to mix the use of HAL drivers and the use of direct register access? And that the HAL calls are stateless? \$\endgroup\$
    – scico111
    Jan 27, 2019 at 16:37
  • 1
    \$\begingroup\$ For anyone interested in this topic, part of the above comment from scico111 has now been asked as a separate question (and therefore should be answered there and not here): "CubeMX HAL Libraries state or stateless". \$\endgroup\$
    – SamGibson
    Jan 28, 2019 at 2:07
  • 1
    \$\begingroup\$ @samgibson the general form of that question is unanswerable as I pointed out there. In this case it is reasonably safe provided one doesn't call the kitchen sink function again with a stale struct, but in others it is not. \$\endgroup\$ Jan 28, 2019 at 2:32

There is a HAL manual on ST website. It explains how the timer HAL works, but sometimes it is just easier to read the actual source code. Specifically, you are looking for CubeF4 manual, and the HAL section for timer functions have a TIM prefix.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.