What do you use to delay?

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

What do you use to delay?

8,063 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by ElectroNerd on Sat Nov 24 16:19:05 MST 2012
Suppose you want to create a delay_ms() function for an LPC17xx. Would you use SysTick, a timer, or something else?
11 Replies

4,865 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by ElectroNerd on Mon Nov 26 15:33:44 MST 2012
Very nice! I am going to make use of this method.


Quote: ToBeFrank
I like to make a generic interface to the timer hardware. For example, in the header file:
typedef struct
{
  void (*delayUs)(uint32_t);
  void (*delayMs)(uint32_t);
  bool (*enabled)(void);
  void (*startMs)(uint32_t);
  void (*stop)(void);
  bool (*expired)(void);
} timer_t; 

extern const timer_t g_timer32b0m0;
extern const timer_t g_timer32b0m1;
...
Then in the .c file you implement the functions for the hardware and create the structure for that hardware:
static void Timer32b0m0DelayUs (uint32_t us) { ... }
static void Timer32b0m0DelayMs (uint32_t ms) { Timer32b0m0DelayUs(ms * 1000); }
static bool Timer32b0m0Enabled (void) { ... } 
static void Timer32b0m0StartMs (uint32_t ms) { ... }
static void Timer32b0m0Stop (void) { ... }
static bool Timer32b0m0Expired (void) { ... }

const timer_t g_timer32b0m0 =
{
  Timer32b0m0DelayUs,
  Timer32b0m0DelayMs,
  Timer32b0m0Enabled,
  Timer32b0m0StartMs,
  Timer32b0m0Stop,
  Timer32b0m0Expired
};
...
Then I can use any of the timer hardware with the same interface. For example, delays:

g_timer32b0m0.delayMs(100);
g_timer32b0m1.delayUs(550);
A loop with a delay and a timeout:

g_timer32b0m2.startMs(1000);
while (!checkSomeFlag())
{
  if (g_timer32b0m2.expired())
  {
    break;
  }
  g_timer32b0m3.delayUs(100);
}
g_timer32b0m2.stop();

0 Kudos

4,865 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by fjrg76 on Mon Nov 26 13:09:50 MST 2012
I like it


Quote: ToBeFrank
I like to make a generic interface to the timer hardware. For example, in the header file:
typedef struct
{
  void (*delayUs)(uint32_t);
  void (*delayMs)(uint32_t);
  bool (*enabled)(void);
  void (*startMs)(uint32_t);
  void (*stop)(void);
  bool (*expired)(void);
} timer_t; 

extern const timer_t g_timer32b0m0;
extern const timer_t g_timer32b0m1;
...
Then in the .c file you implement the functions for the hardware and create the structure for that hardware:
static void Timer32b0m0DelayUs (uint32_t us) { ... }
static void Timer32b0m0DelayMs (uint32_t ms) { Timer32b0m0DelayUs(ms * 1000); }
static bool Timer32b0m0Enabled (void) { ... } 
static void Timer32b0m0StartMs (uint32_t ms) { ... }
static void Timer32b0m0Stop (void) { ... }
static bool Timer32b0m0Expired (void) { ... }

const timer_t g_timer32b0m0 =
{
  Timer32b0m0DelayUs,
  Timer32b0m0DelayMs,
  Timer32b0m0Enabled,
  Timer32b0m0StartMs,
  Timer32b0m0Stop,
  Timer32b0m0Expired
};
...
Then I can use any of the timer hardware with the same interface. For example, delays:

g_timer32b0m0.delayMs(100);
g_timer32b0m1.delayUs(550);
A loop with a delay and a timeout:

g_timer32b0m2.startMs(1000);
while (!checkSomeFlag())
{
  if (g_timer32b0m2.expired())
  {
    break;
  }
  g_timer32b0m3.delayUs(100);
}
g_timer32b0m2.stop();

0 Kudos

4,865 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by ToBeFrank on Mon Nov 26 11:16:46 MST 2012
I like to make a generic interface to the timer hardware. For example, in the header file:
typedef struct
{
  void (*delayUs)(uint32_t);
  void (*delayMs)(uint32_t);
  bool (*enabled)(void);
  void (*startMs)(uint32_t);
  void (*stop)(void);
  bool (*expired)(void);
} timer_t; 

extern const timer_t g_timer32b0m0;
extern const timer_t g_timer32b0m1;
...
Then in the .c file you implement the functions for the hardware and create the structure for that hardware:
static void Timer32b0m0DelayUs (uint32_t us) { ... }
static void Timer32b0m0DelayMs (uint32_t ms) { Timer32b0m0DelayUs(ms * 1000); }
static bool Timer32b0m0Enabled (void) { ... } 
static void Timer32b0m0StartMs (uint32_t ms) { ... }
static void Timer32b0m0Stop (void) { ... }
static bool Timer32b0m0Expired (void) { ... }

const timer_t g_timer32b0m0 =
{
  Timer32b0m0DelayUs,
  Timer32b0m0DelayMs,
  Timer32b0m0Enabled,
  Timer32b0m0StartMs,
  Timer32b0m0Stop,
  Timer32b0m0Expired
};
...
Then I can use any of the timer hardware with the same interface. For example, delays:

g_timer32b0m0.delayMs(100);
g_timer32b0m1.delayUs(550);
A loop with a delay and a timeout:

g_timer32b0m2.startMs(1000);
while (!checkSomeFlag())
{
  if (g_timer32b0m2.expired())
  {
    break;
  }
  g_timer32b0m3.delayUs(100);
}
g_timer32b0m2.stop();
0 Kudos

4,865 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by fjrg76 on Mon Nov 26 10:06:50 MST 2012

Quote: ElectroNerd
This is the simplest way to create delay - by burning clock cycles. The only problem is that while the CPU is busy, you wouldn't be able to service interrupts if they are triggered.



This is a software delay, so:

- As long as the interrupt system is enabled, all the enabled interrupts will be serviced. Nonetheless, your app will be stuck in the delay() funtion 'til the desired time elapses.

- You cannot guarantee exact timing for the delays 'cause the interrupts' overhead and optimization level will affect them.
0 Kudos

4,865 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by js-nxp on Sun Nov 25 13:55:40 MST 2012

Quote:
you wouldn't be able to service interrupts if they are triggered.

In my case the delay was part of a GLCD init so nothing else is running, but if one doesn't need absolute precision (ie 110ms instead if 100ms) then servicing the occasional ISR would still be possibe.

Anyway not advocating my way, using a timer is better but I'm not very familiar with the hardware, I use timers with other chips. :)
0 Kudos

4,865 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by Ex-Zero on Sun Nov 25 13:19:49 MST 2012
The best delay function is a function which meets your requirements So the main question is: which are your requirements on Mars :confused:

My favorite is a SysTick function, since nearly all of my projects use a 1ms SysTick. To use this function also as timeout, I've added a counter there:
volatile uint32_t delay;                //delay counter
...
//SysTick timer handler
void SysTick_Handler(void)                 //Interrupt handler 1ms
{
//delay counter
 if(delay)delay--;                        //delay counter
....
}
So it's useful as simple delay
delay = 50;                            //set 50 ms delay
while(delay);                        //wait for end of delay
or as sensor data timeout :eek:
delay = 50;                            //set 50 ms timeout
sensor_error =0;                    //reset sensor error
while(!sensor_data_ready)            //wait for sensor data
{
 if(delay == 0)                        //timeout
 {
  sensor_error =1;                     //set sensor error 
  break;
 }                                    //end timeout
}                                    //end wait for sensor data
0 Kudos

4,865 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by gbm on Sun Nov 25 13:04:46 MST 2012
Wrong. SysTick is a periodic timer with period set by the programmer. It can be set to any period. If you use an RTOS, you usually set it to 100 Hz or 1000 Hz.

In one of my applications, SysTick is programmed to generate an interrupt every 22 us. Now a quiz: What is my application?

4,864 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by ElectroNerd on Sun Nov 25 11:36:31 MST 2012

Quote: js-nxp
I used this
void _delay_ms (uint16_t ms)
{
 uint16_t delay;
 volatile uint32_t i;
 for (delay = ms; delay >0 ; delay--)
//1ms loop with -Os optimisation
  {
  for (i=3500; i >0;i--){};
  }
}
Probably not correct but it worked for my needs. (At 48MHz I think)



This is the simplest way to create delay - by burning clock cycles. The only problem is that while the CPU is busy, you wouldn't be able to service interrupts if they are triggered.


Quote: fjrg76
It depends on your project's requeriments (granullarity and resollution).

If you need a delay in the order of microseconds then you have to use one of the several timers (TIMx) in your CPU.

In the other hand, if your delays are going to be in the order of hundreds of milliseconds, the SysTick would be ok (remember that the SysTick generally runs from 1ms to 10 ms)



Agreed. Although, it seems that using a timer would be more applicable in every situation:
[LIST]
[*]High Resolution (in the order of µ's)
[*]Wide delay range possibilities (32-bit)
[*]Interrupt configurable for the wide delay range (32-bit)
[/LIST]

The nice thing about having the timer interrupt configurable for the wide delay range is that, while the timer is running, you can have the CPU busy with other tasks (handling other interrupts, etc.).


Quote: ub3r
My delay below..
It doesn't use a timer.. and puts CPU to sleep until an interrupt or delay is over.

volatile uint32_t msTicks;/* counts 1ms timeTicks */

void SysTick_Handler(void)
{
msTicks++;/* increment counter necessary in Delay() */
}

__inline static void _delay_ms(uint32_t del)
{
uint32_t curTicks;
curTicks = msTicks;

while ((msTicks - curTicks) < del)
{
__WFI();
}
}

void setSYSTICK(void)// Setup Systick!
{
if (SysTick_Config(SystemCoreClock / 100))
{ /* Setup SysTick Timer for 1 msec interrupts  */
while (1);                                  /* Capture error */
}

if ( !(SysTick->CTRL & (1<<SysTick_CTRL_CLKSOURCE_Msk)) )
{
LPC_SYSCON->SYSTICKCLKDIV = 0x08;
}
}

int main(void)
{
    SystemInit();
    setSYSTICK();

 LPC_PMU->PCON |= (1<<11);
SCB->SCR &= ~(1<<2);

while(1)
{
_delay_ms(100);
                //Do Something
}
return 0 ;
}



This was a method I was interested in. In abstract terms, SysTick is basically a [I]timer[/I] but only within the range of 1 to 10 ms as fjrg76 pointed out, correct? Putting the CPU to sleep while this is occurring is an interesting idea &#8211; but it could also allow for other interrupts to be serviced.

Personally, I am thinking that using a timer would be best. What do you guys think?
0 Kudos

4,864 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by ub3r on Sun Nov 25 00:28:15 MST 2012
My delay below..
It doesn't use a timer.. and puts CPU to sleep until an interrupt or delay is over.

volatile uint32_t msTicks;/* counts 1ms timeTicks */

void SysTick_Handler(void)
{
msTicks++;/* increment counter necessary in Delay() */
}

__inline static void _delay_ms(uint32_t del)
{
uint32_t curTicks;
curTicks = msTicks;

while ((msTicks - curTicks) < del)
{
__WFI();
}
}

void setSYSTICK(void)// Setup Systick!
{
if (SysTick_Config(SystemCoreClock / 100))
{ /* Setup SysTick Timer for 1 msec interrupts  */
while (1);                                  /* Capture error */
}

if ( !(SysTick->CTRL & (1<<SysTick_CTRL_CLKSOURCE_Msk)) )
{
LPC_SYSCON->SYSTICKCLKDIV = 0x08;
}
}

int main(void)
{
    SystemInit();
    setSYSTICK();

 LPC_PMU->PCON |= (1<<11);
SCB->SCR &= ~(1<<2);

while(1)
{
_delay_ms(100);
                //Do Something
}
return 0 ;
}
0 Kudos

4,864 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by fjrg76 on Sat Nov 24 21:52:43 MST 2012
It depends on your project's requeriments (granullarity and resollution).

If you need a delay in the order of microseconds then you have to use one of the several timers (TIMx) in your CPU.

In the other hand, if your delays are going to be in the order of hundreds of milliseconds, the SysTick would be ok (remember that the SysTick generally runs from 1ms to 10 ms)
0 Kudos

4,865 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by js-nxp on Sat Nov 24 19:56:16 MST 2012
I used this
void _delay_ms (uint16_t ms)
{
 uint16_t delay;
 volatile uint32_t i;
 for (delay = ms; delay >0 ; delay--)
//1ms loop with -Os optimisation
  {
  for (i=3500; i >0;i--){};
  }
}
Probably not correct but it worked for my needs. (At 48MHz I think)
0 Kudos