cancel
Showing results for 
Search instead for 
Did you mean: 

ThreadX makes HAL_Delay() & HAL_GetTick() functions highly unreliable

TDJ
Senior III

I have set up ThreadX on my STM32U5A9 board using CubeMX. In such configuration it is recommended to use SysTick for ThreadX and select some other timer as a Timebase Source so I selected TIM7.

Now HAL_Delay() and HAL_GetTick() functions are very unreliable. If the system is a bit busy, 1s delay can easily take 10s and HAL_GetTick() counts much slower than it should. The problem is TIM7 IRQ priority. CubeMX generates code setting it to the lowest available priority 15, instead to the highest allowable - probably 1 would be a good choice so it is a bit lower than SysTick priority 0. Where/how do I modify current HAL_GetTick() timer priority in CubeMX?
The problem is that many HAL functions rely on HAL_Delay() and HAL_GetTick() functions while using LL_mDelay() is not recommended and SysTick is handled in ThreadX assembler code.
I begin to contemplate using DWT counter. It could be used in HAL_Delay() function, but not in HAL_GetTick() since at 160MHz DWT.CYCCNT resets every ~28s. Maybe high resolution 32b TIM2 timer incrementing every 1ms would be a better choice.

ST, why do not you implement a simple 64b DTW hardware clock cycle counter? I think it would perfectly fit HAL_Delay() and HAL_GetTick() needs without using ISR just to increment software counter.
Probably this problem can be seen as a CubeMX bug.

TDJ_0-1700254984449.png

6 REPLIES 6
Pavel A.
Evangelist II

probably 1 would be a good choice so it is a bit lower than SysTick priority 0

I begin to contemplate using DWT counter. 

Using a free-running timer to implement HAL_Delay, HAL_GetTick is ok if your code does not require tick interrupts. There are other 32-bit timers. Get longer time between overflows with resolution lower than 1 ms.

 Where/how do I modify current HAL_GetTick() timer priority in CubeMX?

You do this in stm...hal_conf.h, exactly as on your picture. If the Cube gets in your way when it re-generates the code, fight back and revert undesired changes.

ST, why do not you implement a simple 64b DTW hardware clock cycle counter?

DWT design comes from ARM, so direct this plea to them 🙂

TDJ
Senior III

@PawelP All I need is HAL_Delay(), HAL_GetTick() functions working properly.
Needless to say, fighting with CubeMX and reverting TICK_INT_PRIORITY every time code is generated is error/problem prone and it is precisely what I try to avoid, hence it think lack of way to configure and retain this setting in CubeMX is a bug or at least a substantial shortcoming.
There is no need for HAL_Delay and HAL_GetTick functions to use a timer with ISR just to increment software counter. I believe some hardware counter, e.g. free-running timer, would be much better.

AScha.3
Principal III

well, here we are on RTOS system now (maybe, not because we like or need it, just forced by STM - no other choice.)

so we have some basic new rules now : 1. any real "delay" is a no-go. forbidden. it renders "Rtos" pointless/useless.

so if we want "wait" , for user response or whatever, we have to use the matching function of the rtos, here: 

tx_thread_sleep(xx);  ( = delay for xx *timetick ; standard is 10ms in Azure; then delay for (xx * 10ms). )

the non-rtos call HAL_Delay() shouldnt be used any more.

the big difference is: the _sleep lets wait the program here, but switches immediate to other thread(s), running the multitasking. So every delay has to call _sleep.

Just for calling the HAL-lib, maybe there are still a few HAL_Delay() calls, replace them, if possible. Otherwise they still should work (more or less exactly, this depends on other settings/actions in the rtos and can be tuned - if needed.)

to replace the hal_delay, i just put in main.c :

 

 

 

 

void HAL_Delay(uint32_t Delay)
{
	tx_thread_sleep((TX_TIMER_TICKS_PER_SECOND*Delay)/1000);
}

 

 

 

 

or better in ms - resolution, same as HAL ->

 

 

#include <ux_api.h>
/* USER CODE END Includes */
....
....


void HAL_Delay(uint32_t Delay)
{
   ux_utility_delay_ms(Delay);
}

 

 

/* DESCRIPTION */

/* */

/* This function causes the calling thread to sleep for the */

/* specified number of milliseconds */

/* */

 

If you feel a post has answered your question, please click "Accept as Solution".
TDJ
Senior III

@AScha.3 You seem to be missing my point. tx_thread_sleep(), as the function name suggests, is used to sleep a thread while HAL_Delay() can work in/from ISR, that is, outside of the ThreadX realm.
tx_thread_sleep() description clearly states:
"This service can be called only from an application thread."
> Just for calling the HAL-lib, maybe there are still a few HAL_Delay() calls, replace them, if possible
Have you actually checked and tried that?

Are you suggesting I should start maintaining my own custom version of HAL?

AScha.3
Principal III

ok ok, i would never have come up with the idea, to use a HAL_Delay() in ISR . 🙂

i just use:   (re-defines HAL_Delay) 

 

#include <ux_api.h>
/* USER CODE END Includes */
....
....


void HAL_Delay(uint32_t Delay)
{
   ux_utility_delay_ms(Delay);
}

..se my older post.

If you feel a post has answered your question, please click "Accept as Solution".
TDJ
Senior III

@AScha.3 I still do not quite understand. ux_utility_delay_ms() is USBX, not ThreadX function, which inside uses 

tx_thread_sleep(). However, you got me thinking. Using tx_time_get() may be a viable solution. Per MS specs, tx_time_get() can be called from anywhere, including ISRs. Consequently, one can overwrite 
HAL_GetTick() and that is all what is needed because HAL_Delay() depends on HAL_GetTick().

 

uint32_t HAL_GetTick() {
    return tx_time_get() * 10; // x10 since by default ThreadX timer ticks every 10ms
}

 

There is just one big catch: such overwritten HAL_GetTick() cannot be called before MX_ThreadX_Init() and MX_ThreadX_Init() must be called at the very end of the init process.
Another issue is that inside tx_time_get() interrupts are disabled and then re-enabled. This may cause adverse side effects.