please dont rip this site

Paul B. Webster VK2BZC

There are *THREE* ways to achieve this functionality. The three ways are:

1} Isosynchronous code - loops performing all functions take a measured time to cycle. Very tedious, virtually impossible in "C".

2} Interrupt-driven. On interrupt, counters are incremented/ decremented and overflow to other counters. The main routine can "peek" at these counters and determine when to perform the necessary task which it does outside of "interrupt time", thus avoiding problems with interrupts overlapping.

To resolve whether the task has been performed at each required interval however, the interrupt routine can set a (series of) flag(s) on certain overflows e.g., each hour. The mainline code then checks if the flag is set *and* the hour is right, if so clears the flag (so it doesn't perform the task twice) and performs the action.

Hint: These flags are all bit flags within a byte; the interrupt routine sets the whole byte. Easier if worked the other way - it clears all the bits at once.

Along this line, it simplifies things a bit if the interrupt routine only deals with say, seconds, setting a flag each second which the main routine uses to advance the second count and count minutes, hours, days etc. But this does lead to the next proposal:

3} Timer-polling. This is the simplest and easiest to code, and can be done without interrupt. That makes it a very *mature* approach!

The basis is to set up the timer and prescaler so that the timer overflows every so many thousand clock cycles, more than enough to perform either the whole of the "other" functions, or a substantial module of those functions. After performing a function group known to take such a time, you wait polling for the overflow, perform the timing count when it happens and go on to the next function group. Thus for one function:

  While TRUE Do
    main_function
    wait_for_rollover
    timer_count
  Loop

and for multiple parts of code each taking less *on average* than the overflow time:

  While TRUE Do
    main_function1
    wait_for_rollover
    timer_count

    main_function2
    wait_for_rollover
    timer_count

    main_function3
    wait_for_rollover
    timer_count

    main_function4
    wait_for_rollover
    timer_count
  Loop

For example, function 1 can take 1½ times the overflow time while function 2 is very short, the first overflow is serviced before another overflow occurs, and the short second function brings the next overflow in on time. You can even cut it slightly finer than that.

The "rollover" code (should be a subroutine) on the devices with an interrupt flag (RTCCOV in T1CNTB, SX48/52) consists of polling for that flag *and clearing it when found*, but note: no interrupt, specific or global is enabled. (Other interrupts can however be enabled as long as they will not make the mainline code modules run significantly over-time as above.)

The timing cycles are each of the form:

timer	macro	unit,unit_max,end_time
	decsz	unit
	jmp	end_time
	mov	W, #unit_max
	mov	unit, W
        endm

;e.g., for seconds, minutes
	decsz	seconds
	jmp	done_time
	mov	W, #60
	mov	seconds, W

	decsz	minutes
	jmp	done_time
	mov	W, #60
	mov	minutes, W

;could be macro coded as:
	timer   seconds,60,done_time
	timer   minutes,60,done_time
;  ..more timers..
;  ..payload; i.e. what you do at the end..
done_time

Further hint on using interrupts and timer in general: IMNSHO don't even *think* of setting/ re-setting/ altering the timer value within the interrupt or poll routine. Always let it count un-impeded, use "nice" numbers. *Far* less heartache that way. KISS!

Øyvind Kaurstad was having a problem with jitter in his timing routine and said:

Immediately after I read RTCC I also clear it, and this will prevent a new overflow for some time.

But Nikolai warns:

There is a slim chance that RTCC overflows in the same instruction when you read it. As I understand from datasheets, read is made in the beginning of instruction cycle, and write - in the end. So actually, there is still some room for error. I don't know if this can matter. Best way is compare with this (INTF part of your routine):

William J. Kitchen says

Make a subroutine that adds the contents of the RTCC to several registers then resets the RTCC. The subroutine has to be called often enough to keep the RTCC from overflowing. This means including a call to it inside all loops. The tricky part is accounting for the time it takes for the subroutine to execute. Here's an example that creates a 24 bit timer:
TIMER
; add RTCC contents to 24 bit value (RTIMEL, RTIMEM, RTIMEH)
; This strange looking addition is designed to execute in exactly
; the same number of cycles regardless of the number of carries
;[ed: this was changed from the original to exclude jumps,
;this is actually the Scott's version below].

TIMER	mov	W, RTCC
	add	RTIMEL, W
	snb	C
	incsz	RTIMEM
	dec	RTIMEH
	inc	RTIMEH
			    ;*  reinitialize RTCC
	mov	W, #$+3-TIMER	; this the number of instruction cycles between
                            ; the time that the RTCC is read and the time when
                            ; it is written, plus 2 more to adjust for the
                            ; time that is required for the RTCC to restart
                            ; after being written
	mov	RTCC, W	;
;*	ret
	retw	#0	;

This routine will always take exactly the same time to execute.

Unfortunately, using a prescaler with this technique will introduce error because subroutine can be called at varying times relative to the prescaler's interval.

I've used this with great success to generate long and accurate timers on PICS without interrupts. It can also be used to get very high resolution (as fine as 0.2 microsecond with a 20mhz pic) measurement of external events on PICS that do have interrupts, while still maintaining long counts.

Scott Dattalo says:

A slightly easier to read (IMO) and slightly faster yet still isochronous is:
	mov	W, RTCC
	add	RTIMEL, W
	mov	W, <<known_zero
	add	RTIMEM, W
	mov	W, <<known_zero
	add	RTIMEH, W

   ;followed by the RTCC fix-up code

where known_zero is a variable that has been initialized to zero.

A slighty more obscure version that doesn't require the known_zero:

	inc	RTIMEH
	mov	W, RTCC
	add	RTIMEL, W
	snb	C
	incsz	RTIMEM
	dec	RTIMEH

I'm not sure if this one is more obscure or not:

	mov	W, RTCC
	add	RTIMEL, W
	snb	C
	inc	RTIMEM
	snb	Z
	inc	RTIMEH
 

Note:

If RTCC = 0 and RTIMEL = 0 then C = 0, Z = 1, which causes increment of RTIMEH! But it's okay, because RTCC will increment before the end of this routine (prescaler isn't used).


file: /Techref/scenix/lib/flow/timenint_sx.htm, 7KB, , updated: 2001/4/17 11:12, local time: 2024/11/17 11:01,
TOP NEW HELP FIND: 
18.119.109.232:LOG IN

 ©2024 These pages are served without commercial sponsorship. (No popup ads, etc...).Bandwidth abuse increases hosting cost forcing sponsorship or shutdown. This server aggressively defends against automated copying for any reason including offline viewing, duplication, etc... Please respect this requirement and DO NOT RIP THIS SITE. Questions?
Please DO link to this page! Digg it! / MAKE!

<A HREF="http://linistepper.com/techref/scenix/lib/flow/timenint_sx.htm"> SX long term timers without interrupts</A>

After you find an appropriate page, you are invited to your to this massmind site! (posts will be visible only to you before review) Just type a nice message (short messages are blocked as spam) in the box and press the Post button. (HTML welcomed, but not the <A tag: Instead, use the link box to link to another page. A tutorial is available Members can login to post directly, become page editors, and be credited for their posts.


Link? Put it here: 
if you want a response, please enter your email address: 
Attn spammers: All posts are reviewed before being made visible to anyone other than the poster.
Did you find what you needed?