cancel
Showing results for 
Search instead for 
Did you mean: 

LL_mDelay taking an extra millisecond?

Sam Walsh
Associate
Posted on August 15, 2017 at 15:12

Hi, I am using a Nucleo F401RE running at 84 MHz (8 MHz XTAL PLL'd up to 84 MHz) and then using Timer 2 as a simple counter to count how long it takes to run the delays.

Timer 2 is set to run at 84 MHz with no prescaler and I am using LL_mDelay for the delays. I have a function which converts the counts to milliseconds. You can see in the timer2 function I am simply reading the count, delaying and resetting the count. However every one of my LL_mDelays are a millisecond over what they should be.

Eg LL_mDelay(1) takes 2 milliseconds, LL_mDelay(0) takes 1 millisecond.

Any ideas why this is? I have attached the watch window output and the project as a zip.

#include 'main.h'
void SystemClock_Config(void);
void Configure_Timer2(void);
float Counts_To_Ms(uint32_t);
volatile uint32_t Counts[10];
volatile float Counts_ms[10];
int main(void)
{
/* Configure the system clock to 84 MHz */
SystemClock_Config();
/* Configure Timer 2 as a software start stop timer */
Configure_Timer2();

/* Infinite loop */
while (1)
{
}
}
float Counts_To_Ms(uint32_t count){
return ((1.0/84000000) * count) * 1000;
}
void Configure_Timer2(void){
// Timer 2 peripheral Configuration
LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM2); // Enable clock to Timer 2 peripheral
/* Enable counter */
LL_TIM_EnableCounter(TIM2);
//First Count
LL_mDelay(0);
Counts[0] = LL_TIM_GetCounter(TIM2);
Counts_ms[0] = Counts_To_Ms(Counts[0]);
LL_TIM_SetCounter(TIM2, 0);
//Second Count
LL_mDelay(1);
Counts[1] = LL_TIM_GetCounter(TIM2);
Counts_ms[1] = Counts_To_Ms(Counts[1]);
LL_TIM_SetCounter(TIM2, 0);
//Third Count
LL_mDelay(2);
Counts[2] = LL_TIM_GetCounter(TIM2);
Counts_ms[2] = Counts_To_Ms(Counts[2]);
LL_TIM_SetCounter(TIM2, 0);
//Fourth Count
LL_mDelay(3);
Counts[3] = LL_TIM_GetCounter(TIM2); 
Counts_ms[3] = Counts_To_Ms(Counts[3]);
LL_TIM_SetCounter(TIM2, 0);
//Fifth Count
LL_mDelay(4);
Counts[4] = LL_TIM_GetCounter(TIM2);
Counts_ms[4] = Counts_To_Ms(Counts[4]);
LL_TIM_SetCounter(TIM2, 0);
//Sixth Count
LL_mDelay(5);
Counts[5] = LL_TIM_GetCounter(TIM2);
Counts_ms[5] = Counts_To_Ms(Counts[5]);
LL_TIM_SetCounter(TIM2, 0);
//Seventh Count
LL_mDelay(6);
Counts[6] = LL_TIM_GetCounter(TIM2);
Counts_ms[6] = Counts_To_Ms(Counts[6]);
LL_TIM_SetCounter(TIM2, 0);
__nop();
}
/**
* The system Clock is configured as follow :
* System Clock source = PLL (HSE)
* SYSCLK(Hz) = 84000000
* HCLK(Hz) = 84000000
* AHB Prescaler = 1
* APB1 Prescaler = 1
* APB2 Prescaler = 2
* HSE Frequency(Hz) = 8000000
* PLL_M = 8
* PLL_N = 336
* PLL_P = 4
* VDD(V) = 3.3
* Main regulator output voltage = Scale2 mode
* Flash Latency(WS) = 2
*/
void SystemClock_Config(void)
{
/* Enable HSE oscillator */
LL_RCC_HSE_EnableBypass();
LL_RCC_HSE_Enable();
while(LL_RCC_HSE_IsReady() != 1){};
/* Set FLASH latency */
LL_FLASH_SetLatency(LL_FLASH_LATENCY_2);
/* Main PLL configuration and activation */
LL_RCC_PLL_ConfigDomain_SYS(LL_RCC_PLLSOURCE_HSE, LL_RCC_PLLM_DIV_8, 336, LL_RCC_PLLP_DIV_4);
LL_RCC_PLL_Enable();
while(LL_RCC_PLL_IsReady() != 1){};
/* Sysclk activation on the main PLL */
LL_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_1);
LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_PLL);
while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_PLL){};
/* Set APB1 & APB2 prescaler */
LL_RCC_SetAPB1Prescaler(LL_RCC_APB1_DIV_2);
LL_RCC_SetAPB2Prescaler(LL_RCC_APB2_DIV_1);
/* Set systick to 1us */
SysTick_Config(84000000 / 1000);
/* Update CMSIS variable (which can be updated also through SystemCoreClockUpdate function) */
SystemCoreClock = 84000000;
}�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?

#nucleo #ll #low-layer #stm32f4
4 REPLIES 4
Posted on August 15, 2017 at 15:36

Please use a 'Syntax highlighter' section for pasting code.

Looking at LL_mDelay function's code it seems there is a bug there:

#define LL_MAX_DELAY 0xFFFFFFFFU�?�?
void LL_mDelay(uint32_t Delay)
{
 __IO uint32_t tmp = SysTick->CTRL; /* Clear the COUNTFLAG first */
 /* Add this code to indicate that local variable is not used */
 ((void)tmp);
 // !!! BUG?: This condition will pretty much always be true, so we will get one more millisecond than we desire
 /* Add a period to guaranty minimum wait */
 if(Delay < LL_MAX_DELAY)
 {
 Delay++;
 }
 while (Delay)
 {
 if((SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk) != 0U)
 {
 Delay--;
 }
 }
}�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?

See my comment starting with '// !!! BUG'.

Posted on August 15, 2017 at 16:25

You are totally correct, I commented the lines and the delays are all correct. I hope ST can fix this, seems a large oversight seems as it is the only official delay function provided.

void LL_mDelay(uint32_t Delay)
{
 __IO uint32_t tmp = SysTick->CTRL; /* Clear the COUNTFLAG first */
 /* Add this code to indicate that local variable is not used */
 ((void)tmp);
 /* Add a period to guaranty minimum wait */
 //if(Delay <= LL_MAX_DELAY)
 //{
 // Delay++;
 //}
 while (Delay)
 {
 if((SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk) != 0U)
 {
 Delay--;
 }
 }
}�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?

Posted on August 15, 2017 at 17:17

You are totally correct

Giving it another thought, maybe not so. I think this function is not intended to be very precise. If you need precise delays use a hardware timer with interrupts.

The thing is, when entering into this function the time may already be, for example, on 900-th microsecond of a millisecond, so the next millisecond will tick just after 0.1 milliseconds.

That condition that you commented out moves the possible skew to from 'taking up to millisecond less time' to 'taking up to milliseconds more time'. Maybe the developers decided that it's less desirable the delay take a little less time than a little more time.

Posted on August 15, 2017 at 19:43

That, and it's not interrupt/thread safe either, so expect situations where one or multiple milliseconds get thrown in the mix.

>>I hope ST can fix this, seems a large oversight seems as it is the only official delay function provided.

Moral: Don't be tied to ST's half-baked solutions, use a free TIM and free-run it at some better granularity which you can then delta safely.

Anything you have to reset, or uses self-clearing flags, or relies on interrupts, is going to give you a head-ache at some point. You are in a much better position to pick/chose free resources in your own design or board implementation.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..