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.
3 Answers
You can configure PWM signals and generate this codes by using prgram interface of CubeMX.
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 */
https://github.com/Bisbiliroglu/STM32F4_CubeMX_HAL/tree/master/PWM
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:
board.h
#define RIGHT_FRONT_PWM TIM3->CCR2
pilot.c
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\$– scico111Commented 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 ♦Commented 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\$ Commented 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.
.h
files of the drivers themselves. \$\endgroup\$