	NOLIST
;	This file is part of picide, ATA(PI) interface to PIC18 family MCUs.
;	Copyright (C) 2004-5 Toby Thain, toby@telegraphics.com.au

;	This program is free software; you can redistribute it and/or modify
;	it under the terms of the GNU General Public License as published by  
;	the Free Software Foundation; either version 2 of the License, or
;	(at your option) any later version.

;	This program is distributed in the hope that it will be useful,
;	but WITHOUT ANY WARRANTY; without even the implied warranty of
;	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;	GNU General Public License for more details.

;	You should have received a copy of the GNU General Public License  
;	along with this program; if not, write to the Free Software
;	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
	LIST

	TITLE picide
	SUBTITLE "Timers and delays"
	
;; Version history
;; 16-Oct-2004: 0.1 - started by Toby
;; 23-Feb-2005: 1.0 - public release under GPL

; contents of this module:
	; *** all code assumes Fosc 40MHz ***
	; various synchronous delays using Timer0
	global delay1500ms,delay100us,delay1ms,delay150ms
	global delay2ms	; uses Timer2 (just to be different)
	
	; setup async timeouts on Timer0 (poll TMR0IF, used by IDE code)
	global time400ms,time1500ms
	
	; interrupt driven "stopwatch" timer, 3 byte tick count @ 1kHz
	; used for benchmark timings
	global init_tick,start_time,end_time

#include "defs.inc"

	extern tick1khz,decstr,puthex,dec16,putdec16,putch

#define TMR_1500MS	(65536-58593) ; x 256 = 14999808 = ~1.5 second
#define TMR_1SEC	(65536-39062) ; x 256 = 9999872 = ~1 second
#define TMR_400MS	(65536-15625) ; x 256 = 4000000 = ~400ms

	udata_acs
sttime	res 2

	code

init_tick:
	; set up 1000Hz heartbeat interrupt on Timer2
	; Fosc/4 = 10,000,000 Hz
	; /16 prescale = 625,000 Hz
	; /125 period = 5,000 Hz
	; /5 postscale = 1,000 Hz
	movlw b'00100010' ; 1:5 postscale,timer off,1:16 prescale
	movwf T2CON
	clrf TMR2
	movlw 125	; period
	movwf PR2
	clrf tick1khz
	clrf tick1khz+1
	clrf tick1khz+2
	bcf PIR1,TMR2IF
	bsf PIE1,TMR2IE	; enable Timer2 match period interrupt
	bsf T2CON,TMR2ON ; start it!
	return
	
	; stash stopwatch value at start of timing interval
start_time:	
	bcf INTCON,GIEH	; disable interrupts while reading ticks
	movff tick1khz,sttime
	movff tick1khz+1,sttime+1
	;movff tick1khz+2,sttime+2
	bsf INTCON,GIEH	; re-enable interrupts
	return	

	; subtract starting time from 1kHz timer
	; and print result in hexadecimal
end_time:
	bcf INTCON,GIEH	; disable interrupts while reading ticks
	movf sttime,w
	subwf tick1khz,w
	movwf dec16
	movf sttime+1,w
	subwfb tick1khz+1,w
	movwf dec16+1
	bsf INTCON,GIEH	; re-enable interrupts
	call putdec16
	_putchar '.'
	return	

delay25us: ; set up Timer0
	movlw b'01001000' ; disabled,8-bit,timer,low-to-high,not prescaled
	movwf T0CON
	movlw -250+11+2 ; 250 cycles = ~25us, less subroutine and call overhead
delay:	movwf TMR0L
delay2:	bcf INTCON,TMR0IE ; disable Timer0 interrupt
	bcf INTCON,TMR0IF ; clear Timer0 overflow
	bsf T0CON,TMR0ON ; start it!
	  btfss INTCON,TMR0IF ; check for timer overflow
	  bra $-2
	return
delay100us: ; set up Timer0
	movlw b'01000001' ; disabled,8-bit,timer,low-to-high,prescaled 1:4
	movwf T0CON
	movlw -250+(13+2)/4 ; 250x4 cycles = ~100us, less subroutine and call overhead
	bra delay
delay1ms: ; set up Timer0
	movlw b'01000101' ; disabled,8-bit,timer,low-to-high,prescaled 1:64
	movwf T0CON
	movlw -156 ; 156x64 cycles = ~998.4us
	bra delay
delay150ms: ; set up Timer0
	movlw b'00000100' ; disabled,16 bit,timer,low-to-high,prescaled 1:32
	movwf T0CON
	_tmr 0, 46875	; x 32 = 1500000 cycles = ~150ms
	bra delay2
delay1500ms: ; set up Timer0
	movlw b'00000111' ; disabled,16 bit,timer,low-to-high,prescaled 1:256
	movwf T0CON
	_tmr 0, TMR_1500MS
	bra delay2
delay2ms_: ; set up Timer0
	movlw b'01000110' ; disabled,8-bit,timer,low-to-high,prescaled 1:128
	movwf T0CON
	movlw -156 ; 156x128 cycles = ~1996.8us
	bra delay
; alternative version using Timer2
; Timer2 input = Fosc/4 = 10,000,000 Hz
; divide by 1/16 prescale = 625,000 Hz
; divide by period (125) = 5000 Hz
; divide by 1/10 postscale = 500 Hz = 2ms
delay2ms: ; set up Timer2
	movlw b'01001010' ; 1:10 postscale,timer off,1:16 prescale
	movwf T2CON
	clrf TMR2
	movlw 125
	movwf PR2
	bcf PIR1,TMR2IF
	bsf T2CON,TMR2ON ; start it!
	  btfss PIR1,TMR2IF ; check for timer overflow
	  bra $-2
	return
;---------------------------------------------------------
time1500ms: ; set up Timer0
	movlw b'00000111' ; disabled,16 bit,timer,low-to-high,prescaled 1:256
	movwf T0CON
	_tmr 0, TMR_1500MS
tmrgo:	bcf INTCON,TMR0IE
	bcf INTCON,TMR0IF
	bsf T0CON,TMR0ON ; start timer!
	; the caller should poll TMR0IF
	return
time400ms: ; set up Timer0
	movlw b'00000111' ; disabled,16 bit,timer,low-to-high,prescaled 1:256
	movwf T0CON
	_tmr 0, TMR_400MS
	bra tmrgo
;---------------------------------------------------------
	end
