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.
I used Tim4 for generate 1 Hz frequency PWM. My clock Speed on this project 168MHz. it is imported for calculate PWM frequency.
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).
/* USER CODE BEGIN 2 */ /*TIM2 PWM gönderimi için channellar ayarlanir */ HAL_TIM_PWM_Start(&htim4,TIM_CHANNEL_1); HAL_TIM_PWM_Start(&htim4,TIM_CHANNEL_2); HAL_TIM_PWM_Start(&htim4,TIM_CHANNEL_3); HAL_TIM_PWM_Start(&htim4,TIM_CHANNEL_4); /* 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); /* USER CODE END 2 */
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:
#define RIGHT_FRONT_PWM TIM3->CCR2
RIGHT_FRONT_PWM = pid_out;
... 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.