55 Sec Time Delay in Assembly Language

Thread Starter

pauliewalnuts

Joined Mar 9, 2008
5
Hi,
I'm new to assembly language, I have read a couple of tutorials and am beginning to get the fundamentals.
I have a fairly simple project to do, whereby I am using a PIC and need it to produce a time delay between 55-60 sec when an input goes high. after this delay, an output will go high.
I would really appreciate some ideas, as I am unsure how to produce a delay of this length.
Many thanks in advance for any info.
Cheers, Paulie.
 

John Luciani

Joined Apr 3, 2007
477
Use a counter-timer and an interrupt routine.

For example in one of my uC boards I have 32768Hz crystal. The counter is set to interrupt
when the count = 32768 (which is one second). You could change the counter
to interrupt at a 60 second interval by using different settings for the counter
clock or a larger counter.

(* jcl *)
 

Papabravo

Joined Feb 24, 2006
12,497
I'll give you the carpenter's answer. If you want to drive a BAN(big a** nail), you use a BAH(big a** hammer). If you want to time a long delay with a fast clock you need a LAC (long a** counter). In other words you keep making the counter longer until it has enough bits to exceed the interval of interest.

Example: How many cycles of an 8 MHz. clock do you need to count in one day?

Answer: 8e6 cycles/sec * 60 seconds/min * 60 Minutes/hour * 24 hours/day = 6.912 e 11

Now log(6.912 e 11)/log(2) = 39.33 which means a 40 bit or 5 byte counter will do the trick. Just to check my math 2^40 = 1.099 e 12 which is > 6.912 e 11

Wasn't that a dizzying mental math exercise?

To implement the multibyte or multiword counter in the assembly language of your choice you need to locate and understand the instructions which produce and use the carry flag. For the least significant part of an addition you do an ordinary "ADD" instruction. For each of the higher order parts you use the "ADD with Carry" instruction that adds two things together AND adds the carry bit from the previous operation. With the carry flag you can make the number of bytes or words anything you desire within the limits of memory on your particular machine.

Your machine might also have subtract and subtract with borrow instructions where the borrow flag is the complement of the carry flag.
 

0xFF

Joined Feb 26, 2008
12
I think the answer depends on what you're trying to do with it, and how accurate you need your count to be. For me, I have a project where I use Timer1 to set a 10ms interrupt. Accordingly, I set a counter at 100 and decrement it with each interrupt. Once the counter = 0, 1 second has passed (10ms x 100 = 1000ms == 1 Second). Add that to a seconds counter and you can keep track of the seconds.

However, this is an ESTIMATED interrupt rollover! It WILL NOT be accurate over time (ie. if you plan on using it for a clock, etc.). If you need the accuracy, set up your Timer to use an external crystal (32768Hz as pointed out above), which will keep accurate time.

The reason I choose this method is that I can keep an "estimated" time of how long the unit has been running (and I don't need it to the second -- even days is fine for what I need), but the main reason is, I can handle as many counters as I need. My timers are for time delay -- you must wait a certain amount of time before being able to do a certain task. I, on occasion, have 3 timeout timers running at the same time, all from the same interrupt / timer setup. Here's how I do it;

NOTE: this code was pulled from an 18F series chip -- 18F4620 to be precise. Please make sure to check your settings for your particular chip (ie. INTCON, T1CON, etc. -- just make sure to look them up in your chips datasheet and adjust as required.

First, setup the timer to rollover every 10ms, which creates an interrupt.

Rich (BB code):
	; setup Timer1
		movlw	b'00000000'				; TMR1 prescaler 1:1, internal clock source
		movwf	T1CON					;
		movlw	0xD8					; load timer 1 with 0xD8EF 
		movwf	TMR1H					; 10ms rollover @ 4 MHz
		movlw	0xEF					;
		movwf	TMR1L					;
Next, turn on the interrupts.

Rich (BB code):
	; enable/disable interrupts
		bcf		PIR1, TMR1IF			; clear the TMR1 interrupt flag
		bsf		INTCON, PEIE			; enable peripheral interrupts
		bsf		PIE1, TMR1IE			; enable TMR1 interrupts
		bsf		INTCON, GIE				; enable global interrupts
Then, in your ISR (Interrupt Service Routine);

Rich (BB code):
ISR:
		bcf		INTCON, GIE				; turn off global interrupts
		btfsc	INTCON, GIE				; test the flag to ensure it's off
		bra 	$-4						; still on, try again

ISR_TMR1:
		btfss	PIR1, TMR1IF			;
		bra		IRestore				; jump if not TMR1 interrupt

	; handle the TMR1 event		
		bcf		PIR1, TMR1IF			; clear the TMR1 interrupt flag
		movlw	0xD8					; reload timer 1 with 0xD8EF 
		movwf	TMR1H					; 10ms rollover @ 4 MHz
		movlw	0xEF					;
		movwf	TMR1L					;		

		decfsz	second_count, F			; decrement the seconds count
										; interrupt every 10ms x 100 = 1000ms (1 second)
		bra		IRestore				;

		movlw	.100					; reset the seconds count
		movwf	second_count			;

ITime_Delay:
		decf	delay_md1_sec, F		; decrement seconds 
		incfsz	delay_md1_sec, W		; check for underflow
		bra		ICheck_Zero				; 
		movlw	.59						; reset seconds to 59
		movwf	delay_md1_sec			;
		decf	delay_md1_min, F		; decrement MIN
		incfsz	delay_md1_min, W		; check for underflow
		nop								;

ICheck_Zero:
		movf	delay_md1_sec, F		; test SEC for zero
		bnz		IRestore			; NOT ZERO
		movf	delay_md1_min, F		; test MIN for zero
		bnz		IRestore			; NOT ZERO 

ITimeout:
                {DO WHAT YOU NEED TO DO AFTER A TIMEOUT HERE}

IRestore:
		bcf		PIR1, TMR1IF			; clear the TMR1 interrupt flag
		retfie	FAST					; enable global interrupt (INTCON,GIE)
So, basically, what happens is this -- you set a 10ms interrupt -- with every interrupt, you decrement the count -- when the count hits zero, 1 second has elapsed. When 1 second has elapsed, you send the ISR to the next function, which decrements the time out period you have set (in the example I have provided, delay_md1_sec and delay_md1_min are your seconds and minutes counters (respectively)). For example, if I wanted a 55 second timeout, I would set delay_md1_sec = 55 and delay_md1_min = 0. Each time you decrement the seconds, you check them for zero -- if they are zero, you set them back to 59 (assuming you haven't hit your timeout) and decrement the minutes counter. If they're both = 0, you have hit your timeout.

I have removed some of the other checks for other timeouts (ITime_Delay2, ITime_Delay3, etc.) -- but it's easy to have multiple counts running at the same time.

I have to run, so I don't have much time to proof read this -- hopefully there aren't many mistakes!

If I haven't explained something well, please ask and I'll try to explain it better.
 

SAE140

Joined May 1, 2008
4
Hi,
I'm new to assembly language, I have read a couple of tutorials and am beginning to get the fundamentals.
I have a fairly simple project to do, whereby I am using a PIC and need it to produce a time delay between 55-60 sec when an input goes high. after this delay, an output will go high.
I would really appreciate some ideas, as I am unsure how to produce a delay of this length.
Many thanks in advance for any info.
Cheers, Paulie.
Providing the processor isn't required to do anything else during this delay period, it's possible to achieve this kind of delay without the use of interrupts (to keep things as simple as possible). The key word here is 'nested-delay'.

So - create a loop which keeps checking for your desired input state. On change, it enters a nested loop, then after the long delay it alters the output, then whatever .... (program ends, re-starts ... ?)

The nested loop structure itself takes the form of an innermost loop with a down counter, starting from max (255), and exitting on zero. With a 1 uSec clock period (say), that gives you a delay of 255 or 256 uSecs (depending on whether you decrement the counter before or after the decision to exit) - so you've now created a 255 uSec delay.

If this inner loop is then placed within an outer loop, also with a down-counter set at 255, then you'll have a 255x255x1 uSec delay (approximately). If you need even more delay, then arrange for this lot to sit (nest) within yet another loop, and so on ....

You could make a timer to delay for many years like this.

Couple of points:
a) this is extremely wasteful of processing power, but for a demonstration it doesn't matter. Using interrupts is certainly better, but adds complexity if you've just started programming.
b) consider toggling an LED from an output line when your delay gets up to around the 0.5 - 5 second mark, so that you have some indication that the program is still running and the processor hasn't gone off picking daisies ...
c) In the loops there will probably be a timing difference depending on whether the program keeps looping, or whether it exits. This may be small difference, but is obviously magnified if it occurs in the innermost loop.
Adjustment will be needed if precision is sought.

Hope this helps. Try doing a Google for 'nested delay loops'.

'best
Colin
 
Hi,,,,,
I need a subprogram of 1 SECOND delay.......I want to call this routine frequently..Because i am doing my project traffic light.so i want to CALL the delay of 1 second ,20 times for highway road and 5 times for pocket road. please anyone can help me..I need my delay pgm in assembly language.................


Advanced thanXXX
 

nanovate

Joined May 7, 2007
666
Hi,,,,,
I need a subprogram of 1 SECOND delay.......I want to call this routine frequently..Because i am doing my project traffic light.so i want to CALL the delay of 1 second ,20 times for highway road and 5 times for pocket road. please anyone can help me..I need my delay pgm in assembly language.................


Advanced thanXXX
Thanxxxx for the double post and hijack: http://forum.allaboutcircuits.com/showthread.php?t=12298
 

conanav

Joined Jun 14, 2008
14
Amazing post, I think i learned more from your replays than when i read the chapter on timer control in the outdated Microcontrollers by Hintz.

Thanks
 
Top