Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

Custom delay in c

Status
Not open for further replies.

Makaram

Member
Hey,
Wanting to make a custom delay function for c so that I can delay using a variable rather than just using constants as __delay_us(x) only allows.
Many of you would probably have come across this issue in the past and triumphed, if so could you please point me in the right direction or perhaps link some code.

I've tried functions such as this

void ServoDelay (long delay){
while(--delay != 0);
}

with varying amounts of offsets etc to allow for the clock cycles involved in the while loop,
But surely there is a more elegant (and accurate) solution?!

Thanks
 
If you use a timer you can get an accurate delay, but that requires you dedicate a timer to it.

You can also use a watchdog timer or systick timer if you have one to use as a countdown. I'm assuming your using a PIC based on the mention of the __delay_us(x) macro. They don't have a systick and watchdog isn't used much, but if you set a timer to interrupt every uS you can decrement a variable within it. So in your timer interrupt function you'll have:
Code:
if( timer_counter) timer_counter--;

In your delay function you have:
Code:
  timer_counter = {delay time};
  while( timer_counter);

With this you can have several delay counters that can be polled separately and are non-blocking, meaning you can still do stuff and check back every once in a while to see if the timer has completed. In this case it's possible to use the timer for other things as well. I've used this method and had the timer also multiplexing LEDs at the same time. Make sure your interrupt code doesn't get bigger than the time it's allotted.
 
Last edited:
Forgive me, I'm new. How could an interrupt with a loop be more efficient?!
also when you say "while(timer_counter)" or "if(timer_counter)" how does this work? doesn't it compare it to 1? eg while(timer_counter==1)
 
You don't need an interrupt loop.. Just set the timer going and wait for the interrupt flag. You can still access the flags outside an interrupt.

I always create my own delays.
If you use the Nop() definition, the while loop is 4 cycles so 5 x 200 = 1000.

Code:
unsigned char x=200;
while(x--)
   Nop();

should be 1000 clock cycles ( 1mS on a 4MHz xtal.)


BTW __delay_us() can be used in a delay function to give a better usage...
 
Last edited:
Forgive me, I'm new. How could an interrupt with a loop be more efficient?!
If you just count the cycles in a nop loop like Ian did, a regular loop will be just as accurate as a timer. Putting a countdown on a timer just has some advantages. You can have a non-blocking timer, like I mentioned earlier, and you can put the chip to sleep between timer interrupts if you are just waiting for something to happen.

also when you say "while(timer_counter)" or "if(timer_counter)" how does this work? doesn't it compare it to 1? eg while(timer_counter==1)

In C "if(timer_counter)" would translate to "if( timer_counter != 0)" so the timer interrupt decrements timer_counter until it reaches 0, then stops. Any non-zero value evaluates to true.
 
I chopped some code out of a project I have under MPLABX/XC8. I have not actually tested this code snippet other than building it without error, but the larger program works. If there's any errors, let me know, but the program doesn't do anything anyway. It's strictly an example of how to use the timer and a decrementing counter within your program to check timing of events. All the program does is wait for a long button press and then a short button press over and over again. The original program was for a PIC16F1933.

Code:
#define _XTAL_FREQ 8000000

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <xc.h>

__CONFIG(FOSC_INTOSC & WDTE_OFF & PWRTE_OFF & MCLRE_ON & CP_OFF & CPD_OFF & BOREN_OFF & CLKOUTEN_OFF & IESO_OFF & FCMEN_OFF);
__CONFIG(WRT_OFF & PLLEN_OFF & STVREN_ON & LVP_OFF);
__IDLOC(0000);

#define BTN_LONG_COUNT         2000
#define BTN_TIMEOUT_COUNT      10000
#define BTN_DEBOUNCE_DELAY     10
#define BTN_SHORT              1
#define BTN_LONG               2
#define BTN_TIMEOUT            3
#define IN_MODE_PIN    (1<<2)  //PORTB.2 = input mode

volatile uint16_t delay_count = 0;

uint8_t get_button( void);

/*
 * 
 */
void main( void)
{
    OSCCON				= 0b01110000;	//8MHz clock
    TRISA				= 0b00000000;
    TRISB				= 0b00010110;   //RB1 is throttle input
                                                        //RB2 is button input
                                                        //RB4 is voltage input
    TRISC				= 0b00000000;

    //Timer2 Setup
    T2CON   = 0b00101110;
    PR2     = 0x20;
    TMR2IE  = 1;
    TMR2ON  = 1;

    PEIE = 1; // Enable Peripheral Interrupt
    GIE = 1;  // Enable Global Interrupt

    while(1)
    {
        while( get_button() != BTN_LONG);   //wait for long press
        while( get_button() != BTN_SHORT);  //wait for short press
    }

}

uint8_t get_button( void)
{
    delay_count = BTN_TIMEOUT_COUNT;
    while ( ( ( PORTB & IN_MODE_PIN) == IN_MODE_PIN) && delay_count);
    if( !delay_count) return( BTN_TIMEOUT);                //Button is on already and is just being held down.
    
    delay_count = BTN_DEBOUNCE_DELAY;                      //Button is off and waiting for press
    while( delay_count);                                   //Wait minimum time for debounce

    delay_count = BTN_LONG_COUNT;
    while( delay_count && ( ( PORTB & IN_MODE_PIN) != IN_MODE_PIN));  //Hold until button is released.
    if( !delay_count)
        return( BTN_LONG);
    return( BTN_SHORT);
}

void interrupt isr(void)
{
   if (TMR2IF)  // Timer2 to PR2 Interrupt Flag bit
   {
        TMR2IF = 0;
	if( delay_count > 0) delay_count--;
   }
}
 
Really appreciate the replies Ian and Mark, will check it out tomorrow, Bit heavy for 4am :S

Thanks again
 
should be 1000 clock cycles ( 1mS on a 4MHz xtal.)
. So I take it that you go 4000000/4 >> 1second/1000000 >> 1000cycles x ans?
Im using the 32Mhz intosc xtal in the 16f1824 so 32000000/4 >> 1second/8000000 == clock time?

Also I'm getting error at compile when I use Nop();. Is there something I need to include? There's no underline in mplabx simply ":0: error: undefined symbol:"
 
Last edited:
What compiler are you using?
 
XC8. See my problem is all these delay functions seem really unaccurate. If I use
Code:
void delay(int d){
while(d--){  
  NOP();
  NOP();
  NOP();
  NOP();
    }
}

I'm assuming that this is 8 clock cycles (4 for the loop and 1 for each NOP()). If I'm using a 32Mhz input. That'll be a 8Mhz main clock so 125 nano seconds a clock pulse.
125 nano * 8 should be 1uS.

Yet when I run the code for 1000000 us (so I can visibly time the accuracy) I get around 2.5seconds?!? Is this way wrong or does it have something to do with the size of the number I'm using?

I'll try the timer2 method next.
 
Did you look at the code I posted (the links in post #13). They are for different compiler (Hi-Tech), but should be easy to port. Good chance is that they work as they are.

Below is a "cleaned up code" for 8 Mhz clock. I hope somebody more experienced with PICs can check the inline assembly for XC8 compiler and your PIC.

Code:
#define PIC_CLK 8000000

/* Change these macros for different clock speeds */
#define DelayDivisor 2
#define WaitFor1Us asm("nop")
#define Jumpback asm("goto $ - 2")

unsigned char delayus_variable;


/* Macro to delay microseconds up to 255 */
#define DelayUs(x) { \
			delayus_variable=(unsigned char)(x/DelayDivisor); \
			WaitFor1Us; } \
			asm("decfsz _delayus_variable,f"); \
			Jumpback;


/* Delay microseconds (up to 65535) */
void DelayBigUs(unsigned int cnt)
{
	unsigned char	i;

	i = (unsigned char)(cnt>>8);
	while(i>=1)
	{
		i--;
		DelayUs(253);
		CLRWDT(); /* Clear watchdog timer */
	}
	DelayUs((unsigned char)(cnt & 0xFF));
}


/* Delay milliseconds (up to 65535) */
void DelayBigMs(unsigned int cnt)
{
	unsigned char	i;
	do {
		i = 4;
		do {
			DelayUs(250);
			CLRWDT();
		} while(--i);
	} while(--cnt);
}


If you use 32 Mhz clock, make these changes:
Code:
#define PIC_CLK 32000000

/* Change these macros for different clock speeds */
#define DelayDivisor 1
#define WaitFor1Us asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop")
#define Jumpback asm("goto $ - 6")
 
Last edited:
Code:
void delay(int d){
while(d--){  
  NOP();
  NOP();
  NOP();
  NOP();
    }
}

AH!! Now that's different I use a char in my delay... You have used an int..... that can be up to 13+ clock cycles


Here is the assembled output

Code:
    417  0792  2F98               	goto	l5249
   418                           	
   419  0793                     l2834:	
   420                           	line	15
   421                           ;main.c: 14: {
   422                           ;main.c: 15: _nop();
   423  0793  0000               	nop
   424                           	line	16
   425                           ;main.c: 16: _nop();
   426  0794  0000               	nop
   427                           	line	17
   428                           ;main.c: 17: _nop();
   429  0795  0000               	nop
   430                           	line	18
   431                           ;main.c: 18: _nop();
   432  0796  0000               	nop
   433  0797  2F98               	goto	l5249
   434                           	line	19
   435                           	
   436  0798                     l2833:	
   437                           	line	13
   438                           	
   439  0798                     l5249:	
   440  0798  30FF               	movlw	low(-1)
   441  0799  07F8               	addwf	(main@d),f
   442  079A  1803               	skipnc
   443  079B  0AF9               	incf	(main@d+1),f
   444  079C  30FF               	movlw	high(-1)
   445  079D  07F9               	addwf	(main@d+1),f
   446  079E  30FF               	movlw	high(-1)
   447  079F  0679               	xorwf	((main@d+1)),w
   448  07A0  1D03               	skipz
   449  07A1  2FA4               	goto	u2735
   450  07A2  30FF               	movlw	low(-1)
   451  07A3  0678               	xorwf	((main@d)),w
   452  07A4                     u2735:
   453                           
   454  07A4  1D03               	skipz
   455  07A5  2FA7               	goto	u2731
   456  07A6  2FA8               	goto	u2730
   457  07A7                     u2731:
   458  07A7  2F93               	goto	l2834
   459  07A8                     u2730:
   460  07A8  2FA9               	goto	l2836
   461

As you can see while(int d--) has a lot more functionality....
 
Last edited:
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top