Author Topic: Why is my microsecond delay function suddenly malfunctioning on my STM32?  (Read 4404 times)

0 Members and 1 Guest are viewing this topic.

Offline TC2Topic starter

  • Newbie
  • Posts: 9
  • Country: us
Hi all! Hope everything is going well. I'm honestly at a loss here, hoping you guys can help. I am returning to a project from a few months ago I was working on and was trying to get some video of it. For example, I have a ultrasonic sensor that I wanted to show working in my video. It wasn't working, and I had no idea why so I did some debugging. Keep in mind this use to work fine for the past year, I don't get what's wrong.

I narrowed it down to what seems to be my microsecond delay function. It would produce a delay that I didn't specify. I tried reconfiguring the clocks so it didn't work, so I opened an older test project and it still is acting up.

Some info
My board is an STM32F746ZG
I'm using TIM3 for the delay function
My code for the delay function
Code: [Select]
void delay_us(uint16_t us){
__HAL_TIM_SET_COUNTER(&htim3,0); // Set counter to 0
while(__HAL_TIM_GET_COUNTER(&htim3) < us);
}

TIM3 according to the datasheet is connected to APB1. Here is what it's set to

TIM3 itself has a prescalar of 4.5-1, because 4.5Mhz/4.5 = 1Mhz, 1/1Mhz = 1uS for microseconds. The period is 0xFFFF-1.

Below is some example test code
Code: [Select]
HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_11);
delay_us(10);

Here is the result on the oscillscope. So instead of 10uS, using cursors for measurements (sorry it's not in this photo), it's around 14.7uS instead. Or if I do something like 100uS, I get 94uS instead.


Am I missing something? I don't know if I'm forgetting something since I took a break, or something else. Thanks
 

Offline abyrvalg

  • Frequent Contributor
  • **
  • Posts: 851
  • Country: es
Re: Why is my microsecond delay function suddenly malfunctioning on my STM32?
« Reply #1 on: September 16, 2021, 11:27:10 pm »
TIMx prescaler is integer, so your 4.5 gets truncated to 4, resulting in 88us delay for 100 counts and 8.8us for 10. Additional ~6us in both cases must be coming from bloated HAL inefficiency and slow APB1 register accesses at such low bus frequency.
« Last Edit: September 16, 2021, 11:31:08 pm by abyrvalg »
 

Offline thm_w

  • Super Contributor
  • ***
  • Posts: 7835
  • Country: ca
  • Non-expert
Re: Why is my microsecond delay function suddenly malfunctioning on my STM32?
« Reply #2 on: September 17, 2021, 01:12:24 am »
They are macro defines, not bloated:

Code: [Select]
__HAL_TIM_SET_COUNTER(__HANDLE__, __COUNTER__)   ((__HANDLE__)->Instance->CNT = (__COUNTER__))
  Sets the TIM Counter Register value on runtime.

Code: [Select]
__HAL_TIM_GET_COUNTER(__HANDLE__)   ((__HANDLE__)->Instance->CNT)
  Gets the TIM Counter Register value on runtime.
Profile -> Modify profile -> Look and Layout ->  Don't show users' signatures
 
The following users thanked this post: newbrain

Offline gnif

  • Administrator
  • *****
  • Posts: 1720
  • Country: au
  • Views and opinions are my own
    • AMD
Re: Why is my microsecond delay function suddenly malfunctioning on my STM32?
« Reply #3 on: September 17, 2021, 01:44:39 am »
They are macro defines, not bloated:

Code: [Select]
__HAL_TIM_SET_COUNTER(__HANDLE__, __COUNTER__)   ((__HANDLE__)->Instance->CNT = (__COUNTER__))
  Sets the TIM Counter Register value on runtime.

Code: [Select]
__HAL_TIM_GET_COUNTER(__HANDLE__)   ((__HANDLE__)->Instance->CNT)
  Gets the TIM Counter Register value on runtime.

`HAL_GPIO_TogglePin` is in also use... that said though it's not a complex function so shouldn't add much latency.

Since you're running at a fixed frequency wouldn't it be better to simply use a timed nop loop for such short delays? This is what I have done in the past

Code: [Select]
#define _NOP_LOOP(x) \                                                           
  __asm__( \                                                                     
      "mov r0,#" #x "\n" \                                                       
      "1:\n" \                                                                   
      "sub r0,#1\n" \                                                             
      "cmp r0,#0\n" \                                                             
      "bge 1b\n" \                                                               
      ::: "r0" \                                                                 
  )                                                                               
                                                                                 
#define NOP() __asm__("nop");
#define NOP_LOOP(x ) _NOP_LOOP(((x) / 6) - 1)                                     
#define DELAY_NS(ns) NOP_LOOP((ns * 1000) / (1000000000/72000000))

Note: I am not a professional firmware developer, the above may be terrible :)
« Last Edit: September 17, 2021, 01:53:35 am by gnif »
 

Offline lucazader

  • Regular Contributor
  • *
  • Posts: 221
  • Country: au
Re: Why is my microsecond delay function suddenly malfunctioning on my STM32?
« Reply #4 on: September 17, 2021, 06:37:34 am »
There is anopther topic around here that was looking at using the CYCCNT register (which is present in your F7) as a timer and then using that to do timing.

I generally dont choose this route and choose something similar to you.
However instead of setting up the timer like you do. I start a free-running timer with a 1us tick rate that just counts up forever (usually use tim6).
But functionally it does similar with a while loop waiting to check if the timer tick if above the time I wish.

As others have said; I would suggest clocking the timer peripheral to an integer value (say 4, 8 or something like that)
And then setting the pre scaler appropriately. This works super well in all my projects.

Another quick way that you can sanity check the timer is giving you the correct time is to compare it against the HAL tick in HAL_GetTick().
If you delay for say 16000us, and then either using a debugger or printf find out the Hal time before and after you entered your custom delay. and check that the value makes sense.
 

Offline TC2Topic starter

  • Newbie
  • Posts: 9
  • Country: us
Re: Why is my microsecond delay function suddenly malfunctioning on my STM32?
« Reply #5 on: September 17, 2021, 07:30:03 pm »
TIMx prescaler is integer, so your 4.5 gets truncated to 4,
Sorry I should of been more specific, this is just one out of many examples I tried. I've used 36Mhz for example, with 36 prescalar. Still same issue.

Another quick way that you can sanity check the timer is giving you the correct time is to compare it against the HAL tick in HAL_GetTick().
If you delay for say 16000us, and then either using a debugger or printf find out the Hal time before and after you entered your custom delay. and check that the value makes sense.
I did some sanity checks like you said. If I do 16000us for example, the reported number from the debugger is 16ms. 20000us is 20ms, etc.  :-\
 

Offline bson

  • Supporter
  • ****
  • Posts: 2519
  • Country: us
Re: Why is my microsecond delay function suddenly malfunctioning on my STM32?
« Reply #6 on: September 17, 2021, 10:37:50 pm »
Are you optimizing the code with inlining enabled?  If not, you might have significant function call overhead.  At what speed is the core running?

Regardless, consider moving the delay_us() function to a header file and define it there as:
Code: [Select]
static void __attribute__((always_inline, optimize)) delay_us(uint16_t us) {
        __HAL_TIM_SET_COUNTER(&htim3, 0); // Set counter to 0
while (__HAL_TIM_GET_COUNTER(&htim3) < us)
                            ;
}
It should only produce a few inline instructions.  But the drawback of getting more accurate timing in debug builds is you won't be able step through it with the debugger, although that's not particularly useful here.
« Last Edit: September 17, 2021, 10:57:02 pm by bson »
 

Offline westfw

  • Super Contributor
  • ***
  • Posts: 4383
  • Country: us
Re: Why is my microsecond delay function suddenly malfunctioning on my STM32?
« Reply #7 on: September 18, 2021, 06:01:12 am »
Quote
wouldn't it be better to simply use a timed nop loop for such short delays?
It gets very tricky on the newer, faster, ARM chips with caches and "flash accelerators" and shared buses and such.
 

Online SiliconWizard

  • Super Contributor
  • ***
  • Posts: 16136
  • Country: fr
Re: Why is my microsecond delay function suddenly malfunctioning on my STM32?
« Reply #8 on: September 18, 2021, 04:39:52 pm »
Using the CYCCNT register on ARM is the easiest way of doing this really. And it has the benefit of being accessed at the core clock's frequency, thus there is no extra synchronization latency compared to accessing a peripheral register through an APB.

All you need is enable it (as was already shown). Which is even easier to do than setting up a peripheral timer.

In any case, getting µs-accuracy with waiting loops obviously implies running the core at a frequency that is significantly higher than 1 MHz to make up for the waiting loop code overhead, even if it's just a couple instructions.

It may still be hard to get accurate, very short delays, depending on cache considerations, branch prediction, and such, so you should also consider the underlying architecture.

Thus using a timer or cycle counter will get you a delay of the form a.n + b (with a, the resolution, being much more accurate than with any ugly nop-based hack), but there will always be a b delay offset (software overhead) which becomes significant for very small a and n.

 

Offline abyrvalg

  • Frequent Contributor
  • **
  • Posts: 851
  • Country: es
Re: Why is my microsecond delay function suddenly malfunctioning on my STM32?
« Reply #9 on: September 18, 2021, 06:53:28 pm »
And you can compensate that b by reducing the count limit by some fixed offset. Or just use that offset as the initial timer value instead of 0 - this way you’ll avoid subtraction (which could roll over if the input value is smaller than offset).
 

Offline bson

  • Supporter
  • ****
  • Posts: 2519
  • Country: us
Re: Why is my microsecond delay function suddenly malfunctioning on my STM32?
« Reply #10 on: September 19, 2021, 11:17:59 pm »
In general, just personally speaking, I never rely on bit banging accurate timing.  When I do bit bang it's generally not important from a performance perspective, doesn't require super accurate timing, and the device doesn't care if the timing is stretched.  Meaning if there's an interrupt in the middle of the delay loop that's fine and will just slow things down an itty bit once in a while.  While you can count cycles and bang out accurate timing, you won't be doing much of anything else, like process data simultaneously.

For accurate timing, I'd rely on timers, either to generate it to timestamp events (timer capture).  For performance, a suitable bus - like FSMC, an LCD controller, DMA, carefully laying out memory on different AHBs to reduce memory contention, etc.
 

Online SiliconWizard

  • Super Contributor
  • ***
  • Posts: 16136
  • Country: fr
Re: Why is my microsecond delay function suddenly malfunctioning on my STM32?
« Reply #11 on: September 20, 2021, 12:34:49 am »
And you can compensate that b by reducing the count limit by some fixed offset. Or just use that offset as the initial timer value instead of 0 - this way you’ll avoid subtraction (which could roll over if the input value is smaller than offset).

Problem is, this offset, at least on some targets, is neither fixed nor really predictable. Trying to compensate it is thus pretty similar to implementing delays with cycle counting instructions. So your best bet is just to use your delays so that this offset is negligible.

Very small *and* accurate delays are rarely useful anyway - if you use them, there's probably a better way of achieving the same thing using the appropriate peripherals.
 

Offline viperidae

  • Frequent Contributor
  • **
  • Posts: 306
  • Country: nz
Re: Why is my microsecond delay function suddenly malfunctioning on my STM32?
« Reply #12 on: September 20, 2021, 03:05:26 am »
Are you trying to generate a single pulse to send to the ultrasonic module?
The timers can be configured to single pulse mode and automatically do the pin toggling for you.

For timing a response, that's exactly what the capture function of timers is for.

You should be able to get all the timing critical stuff done with the peripheral without having to get the CPU involved
 

Offline peter-h

  • Super Contributor
  • ***
  • Posts: 4522
  • Country: gb
  • Doing electronics since the 1960s...
Re: Why is my microsecond delay function suddenly malfunctioning on my STM32?
« Reply #13 on: September 20, 2021, 11:28:18 am »
If you want to generate a pulse of an accurate width then this is not possible unless you disable interrupts (and DMA if used) around the whole piece of code.

And even then it won't work usefully because interrupts will generate a variable latency i.e. the point in time when the pulse starts will move about.

It could be made to work on a CPU which is not running anything else and just runs this tight loop. OTOH we don't know the OP's algorithm; it could be that the pulse needs to be generated in response to some input and a latency is not important but the width is.

I would definitely try doing this with a timer, or even with external hardware.

If one can do it with polled code, the CYCCNT method seems very solid and very accurate. See last post:
https://www.eevblog.com/forum/microcontrollers/32f4-a-timer-without-interrupts-cyccnt/


Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 

Offline westfw

  • Super Contributor
  • ***
  • Posts: 4383
  • Country: us
Re: Why is my microsecond delay function suddenly malfunctioning on my STM32?
« Reply #14 on: September 21, 2021, 04:24:01 am »
Quote
If you want to generate a pulse of an accurate width then this is not possible
One has to be careful about defining "accurate."Accurate to within +/- 5 clock cycles: very difficult.Accurate to within +/- 100 clock cycles: not so difficult.(and keeping in mind that 100 clock cycles is about 1us on many current chips.)
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf