;****************************************************************************** ; ; BINCLOCK.asm ; Practical Binary Digit Clock - built to implement the ; "Black Standard" for human interface binary digit clocks. ; ; Open source - anyone can use as they choose. ; Note! See text: www.RomanBlack.com/binclock.htm ; ; Note! Uses the (no ints) zero-error clock routine as seen; ; www.romanblack.com/one_sec.htm ; ; Ver 1.1 - started 14 March 2007 - Roman Black ; ver 1.2 - 15 June 2009 - Roman Black, with thanks to tester Art Loya ; (v1.2 fixed bugs and improved clock setting speed.) ;****************************************************************************** ;============================================================================== ; MPLAB stuff here ERRORLEVEL -224 ; suppress annoying message because of option/tris ERRORLEVEL -302 ; suppress message because of bank select in setup ports LIST b=5, n=97, t=ON, st=OFF ; absolute listing tabs=5, lines=97, trim long lines=ON, symbol table=OFF ; config for 16F628 __CONFIG _CP_OFF & _WDT_OFF & _PWRTE_ON & _XT_OSC & _MCLRE_ON & _BODEN_ON & _LVP_OFF ; oscillator is XT ready for 4 MHz crystal or resonator. ; (use _HS_OSC (not _XT_OSC)if you are using 8MHz or higher.) ;============================================================================== ; processor defined ; include ;============================================================================== ; Variables here CBLOCK 0x20 ; start of ram in 16F628 bres_hi ; hi byte of our 24bit variable bres_mid ; mid byte bres_lo ; lo byte (all used for Bres) sec ; 3 vars, binary clock data min ; hour ; buttons ; holds input buttons bit6 bit7 debounce ; counter to time debounce ENDC ; My custom instructions! #define incw addlw d'1' ; add 1 to w #define comw xorlw 0xFF ; complement w #define skpwne skpnz ; after subxx, uses zero #define skpweq skpz ; after subxx, uses zero #define skpwle skpc ; after subxx, uses carry #define skpwgt skpnc ; after subxx, uses carry #define skp1 goto $+1+1 ; skips next 1 instruction. #define skp2 goto $+1+2 ; skips next 2 instructions. #define skp3 goto $+1+3 ; skips next 3 instructions. #define back1 goto $-1 ; loops back 1 instruction. #define nop2 goto $+1 ; = 2 nops (wait) ;============================================================================== ; constants for the Bresenham 24bit 1second timer algorithm (in hex) #define BHI 0x0F ; msb value (change these to suit xtal) #define BMID 0x42 ; mid value #define BLO 0x40 ; lsb value ; You can use ANY xtal frequency! some samples below; ; 32.768kHz = 8,192 = 00 20 00 ; 4MHz xtal = 1,000,000 = 0F 42 40 *using ; 6MHz xtal = 1,500,000 = 16 E3 60 ; 8MHz xtal = 2,000,000 = 1E 84 80 ; 16MHz xtal = 4,000,000 = 3D 09 00 ; 20MHz xtal = 5,000,000 = 4C 4B 40 ;============================================================================== ; Code here org 0x000 ; Set program memory base at reset vector 0x000 reset goto setup ; set up ints and port stuff ; Note! No interrupts are used in this code. ;============================================================================== ;****************************************************************************** ; SETUP (runs this only once at startup) ;****************************************************************************** ; ;------------------ setup ; goto label ;------------------ ;------------------------------------------------- ; Note! 16F628 version. ; Note! here we set up peripherals and port directions. ; this will need to be changed for different PICs. ;------------------------------------------------- ; OPTION setup movlw b'00001000' ; ; x------- ; 7, 0=enable, 1=disable, portb pullups ; -x------ ; 6, 1=/, int edge select bit ; --x----- ; 5, timer0 source, 0=internal clock ; ---x---- ; 4, timer0 ext edge, 1=\ ; ----x--- ; 3, prescaler assign, 1=wdt, 0=timer0 ; -----x-- ; 2,1,0, timer0 prescaler rate select ; ------x- ; 000=2, 001=4, 010=8, 011=16, etc. ; -------x ; ; Note! We set timer0 prescaler to 1:1, ; this is explained later in MAIN. ; banksel OPTION_REG ; movwf OPTION_REG ; load data into OPTION_REG banksel 0 ; ;------------------------------------------------- ; extra setup for 16F628 banksel VRCON ; do bank 1 stuff clrf PIE1 ; disable pie etc clrf VRCON ; disable Vref banksel 0 ; clrf T2CON ; disable timer2 clrf CCP1CON ; disable CCP module movlw b'00000111' ; disable comparators movwf CMCON ; ;------------------------------------------------- ; TIMER1 setup ; prescale 1:1, timer OFF. movlw b'00000000' ; ; --x----- ; 5,4; TMR1 prescale; 11=8 ; ---x---- ; 10=4, 01=2, 00=1 ; ----x--- ; T1OSCEN; 0=osc off ; -----x-- ; T1SYNC; 0=external clock sync ; ------x- ; TMR1CS; clock source, 1=ext, 0=int ; -------x ; TMR1ON; 1=on, 0=off movwf T1CON ; ;------------------------------------------------- ; PORTB pins direction setup ; 1=input, 0=output clrf PORTB ; ; movlw b'11000000' ; 6,7 are inputs ; banksel TRISB ; movwf TRISB ; send mask to portb banksel 0 ; ;------------------------------------------------- ; PORTA pins direction setup ; 1=input, 0=output clrf PORTA ; ; movlw b'00000000' ; all 5 porta are outputs, ; (with 16F628 porta only has lower 5 bits) ; banksel TRISB ; movwf TRISA ; send mask to porta banksel 0 ; ;------------------------------------------------- ; INTCON setup ; ; for this code example, all we need to do is ; make sure all interrupts are off. ; movlw b'00000000' ; GIE=off TOIE=off (timer0 overflow int) movwf INTCON ; set up ints. ;------------------------------------------------- ; Now set up the variables ;------------------------------------------------- clrf bres_hi ; set up the 24bit bres variable clrf bres_lo ; ready to roll straight away... clrf bres_mid ; incf bres_mid,f ; mid MUST be >0 at start!! clrf sec ; setup the 3 clock vars clrf min ; movlw d'4' ; 4= 1oclock movwf hour ; clrf PORTA ; safe, set all leds OFF to start clrf PORTB ; ;------------------------------------------------- goto main ; start main program ;------------------------------------------------------------------------------ ;****************************************************************************** ; MAIN (main program loop) ;****************************************************************************** ; ;------------------ main ; goto label ;------------------ ;------------------------------------------------- ; Note! This example does not use interrupts. ; This can very handy, if using smaller PICs like the 12c508, ; or in designs where interrupts would be a problem ; but where you still need a reliable period event. ; ; However we must poll the "fake" interrupt and manually ; check timer0. This system is quite fast, which does not ; use up too much timeslice. ; ; The "fake" interrupt is done by polling timer0 int flag. ; This is set after 256 instructions, and stays ; set for a safe period of 256 instructions, during this last ; 256 instructions we MUST poll and detect it. ; ; So the MAX delay between polls is 256 to 512 instructions. ; Suggest safe value of 250 instructions between polls. ; ; Make sure your main program code never makes more than ; 250 instructions before the next polling of the "fake" int. ;------------------------------------------------- main_loop ; ; Note! here you have your main program code, ; or calls to the main program pieces. ; Remember, nothing should take longer than ; 256 instructions total or we might miss a ; "fake" interrupt. ;------------------------------------------------- ; Poll (check) for our "fake" int here! ;------------------------------------------------- ; all we do is check if TMR0 int flag is set, ; if so we generate a "fake" interrupt. ; btfss INTCON,T0IF ; test if overflow flag is set goto main_loop ; clr, no fake int, keep looping. ; set, we have reached 256 counts since last ; "fake" interrupt. ;------------------------------------------------- ; If it gets here we have detected a "fake" interrupt! ;------------------------------------------------- ; Note! This will occur every 256 instructions, we ; can now do our special one second timing system. ; This consists of three main steps; ; * subtract 256 counts from our 24bit variable ; * test if we reached the setpoint ; * if so, add 1,000,000 counts to 24bit variable and generate event. ;------------------------------------------------- ; first clear timer0 int flag ; this never resets timer0 so it stays accurate, ; as it still contains the lower bits and will keep ; perfect time. bcf INTCON,T0IF ; ;------------------------------------------------- ; * optimised 24 bit subtract here ; This is done with the minimum instructions. ; We subtract 256 from the 24bit variable ; by just decrementing the mid byte. tstf bres_mid ; first test for mid==0 skpnz ; nz = no underflow needed decf bres_hi,f ; z, so is underflow, so dec the msb decfsz bres_mid,f ; dec the mid byte (subtract 256) ; now the full 24bit optimised subtract is done! ; this is almost 4 times faster than a "proper" ; 24bit subtract. goto main_loop ; nz, so definitely not one second yet. ; in most cases the entire 'fake" int takes ; only 9 instructions. ;------------------------ ; * test if we have reached one second. ; only gets here when mid==0, it MAY be one second. ; only gets to here 1 in every 256 times. ; (this is our best optimised test) ; it gets here when bres_mid ==0. call change_time ; test buttons every 65536 timer0 ticks ; with 4MHz xtal that is 15Hz tstf bres_hi ; test hi for zero too skpz ; z = both hi and mid are zero, is one second! goto main_loop ; nz, so not one second yet. ;------------------------------------------------- ; Only gets to here if we have reached one second. ; now we can generate our one second event, ; the other thing we need to do is add X counts ; to our 24bit variable and start all over again. ;------------------------------------------------- ; Add the X counts first. ; As we know hi==0 and mid==0 this makes it very fast. ; This is an optimised 24bit add, because we can ; just load the top two bytes and only need to do ; a real add on the bottom byte. This is much quicker ; than a "proper" 24bit add. movlw BHI ; get msb value movwf bres_hi ; load in msb movlw BMID ; get mid value movwf bres_mid ; load in mid movlw BLO ; lsb value to add addwf bres_lo,f ; add it to the remainder already in lsb skpnc ; nc = no overflow, so mid is still ok incf bres_mid,f ; c, so lsb overflowed, so inc mid ; this is optimised and relies on mid being known ; and that mid