;; ;; $Id: uptime.asm,v 1.1 2001/03/05 03:42:44 james Exp $ ;; ;; uptime.asm, a PIC 16F877-04P with LCD power on timer. ;; Copyright (C) 2001 James Cameron (quozl@us.netrek.org) ;; ;; 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 ;; ;; ;; A simple power on timer. Counts seconds, minutes, hours and days of ;; uptime and displays it on an LCD panel. Updates once a second. ;; ;; Demonstrates a stack based argument passing infrastructure, functions to ;; handle RETLW data tables within this infrastructure, LCD display and ;; binary to decimal and hexadecimal conversion. ;; ;; ;; Hardware assumptions ;; ;; LCD panel connected via Dontronics DT106 PCB ;; 4MHz crystal oscillator ;; processor 16f877 list f=inhx8m include "p16f877.inc" __config _lvp_off & _wdt_off & _hs_osc ;; symbol naming conventions ;; p_ port address of ;; t_ tris address of ;; m_ mask bits of ;; b_ bit number ;; c_ command mask ;; file register allocations base equ 0x20 ; first free file register address stack equ 0x7f+1 ; end address of stack ;; port bit allocations (a Dontronics DT106 PCB) p_lcdc equ portb ; lcd control port address t_lcdc equ trisb ; lcd control tris address b_lcd_rs equ 1 ; lcd register select (pin 4) b_lcd_rw equ 2 ; lcd read/write (pin 5) b_lcd_e equ 3 ; lcd enable (pin 6) m_lcdc equ b'00001110' ; lcd control mask bits p_lcdd equ portb ; lcd data port address t_lcdd equ trisb ; lcd data tris address b_lcd_db4 equ 4 b_lcd_db5 equ 5 b_lcd_db6 equ 6 b_lcd_db7 equ 7 m_lcdd equ b'11110000' ; lcd data mask bits cblock base r_ephemeral ; use in lowest level functions only r_temporary ; use in lowest level functions only r_trisb ; mirror of trisb flag ; isr execution flags endc #define flag_wake flag,0 include "stack.inc" ; stack macros ;; reset vector org 0x0 goto main ; go to main program ;; interrupt vector org 0x4 isr cblock saved_w ; saved w register saved_status ; saved status register (swapped) saved_fsr ; saved fsr register endc movwf saved_w ; save w register swapf status,w ; save status register movwf saved_status btfss intcon,t0if ; test timer zero interrupt flag goto isr_skip_clock isr_clock bcf intcon,t0if ; clear timer zero interrupt flag cblock u0 ; microsecond counter low u1 ; microsecond counter middle u2 ; microsecond counter high endc ;; add 65536us to the microsecond counter incf u2,f cblock t0 ; temporary copy of u0-u2 t1 t2 endc ;; save a copy of the counter mov24 u0,t0 ;; subtract 1s from copy of counter movlw d'1000000'&0xff subwf t0,f btfss status,c decf t1,f movlw d'1000000'>>d'8'&0xff subwf t1,f btfss status,c decf t2,f movlw d'1000000'>>d'16'&0xff subwf t2,f btfss status,c goto isr_clock_end ; underflow, ignore the subtraction ;; did not underflow, save the result, occurs once per second mov24 t0,u0 cblock ss ; seconds mm ; minutes hh ; hours dl ; days dh endc ;; save fsr movf fsr,w movwf saved_fsr ;; point to counter movlw ss movwf fsr incf indf,f ; increment second counter movlw d'60' subwf indf,w bnz isr_kick_end ; hasn't reached a minute clrf indf ; clear the second counter incf fsr,f ; point to minute counter incf indf,f ; increment minute counter movlw d'60' subwf indf,w bnz isr_kick_end ; hasn't reached an hour clrf indf ; clear the minute counter incf fsr,f ; point to hour counter incf indf,f ; increment hour counter movlw d'24' subwf indf,w bnz isr_kick_end ; hasn't reached an hour clrf indf ; clear the hour counter incf fsr,f ; point to day counter incf indf,f ; increment day counter bnz isr_kick_end incf fsr,f incf indf,f isr_kick_end ;; restore fsr movf saved_fsr,w movwf fsr ;; wake mainline bsf flag_wake isr_clock_end isr_skip_clock swapf saved_status,w ; restore registers saved movwf status swapf saved_w,f swapf saved_w,w retfie table macro popw ; grab offset addwf pcl,f ; vector to appropriate retlw endm t_text table dt "Days HH MM SS" retlw 0 ; terminating zero for caller include "table.asm" ; table handler include "lcd.asm" ; lcd display functions include "bcd.asm" ; binary to decimal conversion include "hex.asm" ; binary to hexadecimal conversion include "stack.asm" ; stack functions ;;; ;;; main program ;;; main ;; clear stack movlw stack movwf fsr ;; clear output port clrf porta clrf portb clrf portc ;; set tristate latches movlw ~(m_lcdd|m_lcdc) ; set lcd data and control to output movwf r_trisb bsf status,rp0 movwf trisb movlw b'11111111' ; inputs so we can use analog movwf trisa movlw b'00000000' ; outputs for debugging movwf trisc bcf status,rp0 ;; clear counters clr24 u0 clrf ss clrf mm clrf hh clr16 dl clrf flag ; clear synchronisation flags ;; initialise option register bsf status,rp0 bcf option_reg,t0cs ; set timer to use oscillator/4 bcf option_reg,psa ; set prescaler to TMR0 bsf option_reg,ps2 ; set prescaler to 1:256 bsf option_reg,ps1 bsf option_reg,ps0 bcf status,rp0 ;; lastly, enable interrupts bsf intcon,t0ie ; enable timer interrupt bsf intcon,gie ; enable all interrupts ;; initialise lcd call lcd_initialise pers lcd_put,t_text loop btfss flag_wake ; wait until isr is executed goto $-1 bcf intcon,gie ; disable interrupts btfsc intcon,gie ; test we suceeded [paranoia] goto $-2 ; loop until it worked pushf ss ; grab a copy of the counters pushf mm pushf hh pushf16 dl bsf intcon,gie ; enable interrupts pushl lcd_row_1 call lcd_move call sb_bcd ; dl call lcd_put_hex call lcd_put_hex call lcd_put_hex pushl ' ' call lcd_put pushl d'0' ; hh call sb_bcd pop pop call lcd_put_hex pushl ':' call lcd_put pushl d'0' ; mm call sb_bcd pop pop call lcd_put_hex pushl ':' call lcd_put pushl d'0' ; ss call sb_bcd pop pop call lcd_put_hex goto loop end