;****************************************************************************** ; ZERO-ERROR ONE SECOND TIMER ; (Roman Black 2001, public domain, use it as you like) ; ; NO INTERRUPTS VERSION ; ; for PIC 16F84 at 4 MHz (or most PICs) ; (this code has been assembled with MPLAB and hardware tested) ; (for best viewing set TABS=5 in MPLAB editor) ; ; Note! See text: www.RomanBlack.com/one_sec.htm ; ; Generates an event every second (or other period) from any PIC ; with any clock frequency. ; ; This version uses a "fake" interrupt at every 256 instructions. ; Code can be adapted for different clock speeds, period lengths ; and accuracy levels. ; ; May 2004 Errata! Bug fixed in main() where timer0 bit7 was cleared. ; June 2009 update! Changed the TMR0 code to allow more PIC versions ; with PICs that have no interrupts. ;****************************************************************************** ;============================================================================== ; processor defined ; include ;============================================================================== ; MPLAB stuff here LIST b=5, n=97, t=ON, st=OFF ; absolute listing tabs=5, lines=97, trim long lines=ON, symbol table=OFF __CONFIG _CP_OFF & _WDT_OFF & _PWRTE_ON & _XT_OSC ; config for 16F84; code protect OFF, watchdog OFF, powerup timer ON, ; oscillator is XT ready for 4 MHz crystal or resonator. ;============================================================================== ; Variables here CBLOCK 0x20 ; start of ram in 16F84 bres_hi ; hi byte of our 24bit variable bres_mid ; mid byte bres_lo ; lo byte bit7flag ; used to test TMR0 ; (we only need 4 bytes for this system) ENDC ;============================================================================== ; 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! 16F84 version. ; Note! here we set up peripherals and port directions. ; this will need to be changed for different PICs. ;------------------------------------------------- ; OPTION setup movlw b'10000000' ; ; 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:2, ; this is explained later in MAIN. ; banksel OPTION_REG ; go proper reg bank movwf OPTION_REG ; load data into OPTION_REG banksel 0 ; back to normal bank 0 ;------------------------------------------------- ; PORTB pins direction setup ; 1=input, 0=output clrf PORTB ; ; movlw b'00000000' ; all 8 portb are outputs ; banksel TRISB ; go proper reg bank movwf TRISB ; send mask to portb banksel 0 ; back to normal reg bank ;------------------------------------------------- ; PORTA pins direction setup ; 1=input, 0=output clrf PORTA ; ; movlw b'00000000' ; all 5 porta are outputs, ; (with 16F84 porta only has lower 5 bits) ; banksel TRISB ; go proper reg bank movwf TRISA ; send mask to porta banksel 0 ; back to normal reg bank ;------------------------------------------------- ; 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. ;------------------------------------------------- ; Note! Now the hardware is set up we need to load the ; first count for one second into our 24bit bres variable. ;------------------------------------------------- ; Note! This example uses 4 MHz clock, which is ; 1,000,000 counts per second. ; ; We require a 1 second period, so we must load ; 1,000,000 counts each time. ; 1,000,000 = 0F 42 40 (in hex) ; ; We also need to add 256 counts for the first time, ; so we just add 1 to the mid byte. ; Check mid overflow if needed. ; here we load the 24bit variable. movlw 0x0F ; get msb value movwf bres_hi ; put in hi movlw 0x42 +1 ; get mid value (note we added 1 to it) movwf bres_mid ; put in mid movlw 0x40 ; get lsb value movwf bres_lo ; put in mid ; now setup is complete, we can start execution. ;------------------------------------------------- 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 bit7 of timer0, ; which is prescaled at 1:2. ; This means bit7 changes after 256 instructions, and stays ; for a safe period of 256 instructions, during this last ; 256 instructions we MUST poll and detect the next change. ; ; So the MAX delay between polls is 256 to 512 instructions. ;------------------------------------------------- main_loop ; ; Note! here you have your main program code, ; or calls to the main program pieces. ; Remember, nothing should take longer than ; 250 instructions total or we might miss a ; "fake" interrupt. ; stuff ; stuff ; stuff ; stuff ;------------------------------------------------- ; Poll (check) for our "fake" int here! ;------------------------------------------------- ; all we do is check if timer0,bit7 ; has changed, we compare it to our flag. ; if so we generate a "fake" interrupt. ; btfsc TMR0,7 ; if bit7 is lo, force flag lo too goto timer_7hi ; ; TMR0,7 is lo, check if flag lo too btfss bit7flag,7 ; skip if mismatch goto main_loop ; just keep looping ; we detected a mismatch, so do fake int! bcf bit7flag,7 ; fix flag first goto fake_int ; timer_7hi ; TMR0,7 is hi btfsc bit7flag,7 ; test if bit7 hi AND flag lo goto main_loop ; flag also hi, no fake int, keep looping. ; we detected a mismatch, so do fake int! bsf bit7flag,7 ; fix flag first ;------------------------------------------------- fake_int ; 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. ;------------------------------------------------- ; * 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. 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, like add ; one second to our clock or whatever. ; (in this example we toggle a led) ; the other thing we need to do is add 1,000,000 counts ; to our 24bit variable and start all over again. ;------------------------------------------------- ; Add the 1,000,000 counts first. ; One second = 1,000,000 = 0F 42 40 (in hex) ; 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 0x0F ; get msb value movwf bres_hi ; load in msb movlw 0x42 ; get mid value movwf bres_mid ; load in mid movlw 0x40 ; 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 won't overflow from one inc. ; that's it! Our optimised 24bit add is done, ; this is roughly twice as quick as a "proper" ; 24bit add. ;------------------------- ; now we do the "event" that we do every one second. ; Note! for this example we toggle a led, which ; will give a flashing led which is on for a second ; and off for a second. ; Add your own code here for your one second event. ; Note! My led is on porta,3 ; your led may be on a different pin. movlw b'00001000' ; mask for bit 3 xorwf PORTA,f ; toggle PORTA,bit3 (toggle the led) ;------------------------------------------------- ; now our one second event is all done, ; we can keep looping. goto main_loop ; ;------------------------------------------------------------------------------ ;============================================================================== end ; no code after this point. ;==============================================================================