;----------------------------------------------------------------------; ; bike3.asm Combines: Time , Dist, Av velocity, Inst Vel ; Fred Maher 1st March 2002 ; BASIC BICYCLE COMPUTER WITH THESE FUNCTIONS ;----------------------------------------------------------------------; ; 1 Trip Time HRS:MIN:SEC ; 2 Trip Dist 000km 00m ; 3 Trip AvSpd 00.00km/hr ; 4 Speed 00.00km/hr ;--------------------------------------------------------------------- ;------- HEADER ---------- ;--------------------------------------------------------------------- ; LCD MESSAGES ; position at beginning of 1st line col 0 movlw H'80 ; position at beginning of second line movlw H'C0' LIST P=16F84 ; 16F84 Runs at 4.096 MHz INCLUDE "p16f84A.inc" __CONFIG _CP_OFF & _WDT_OFF & _XT_OSC & _PWRTE_ON ERRORLEVEL -224 ; suppress annoying message because of tris ERRORLEVEL -302 ; suppress message because of page change ; Define Information #DEFINE RS PORTA, 2 #DEFINE E PORTA, 3 ;#DEFINE TOGGLESW PORTB, 6 ; not used at the moment ;#DEFINE LED PORTB, 5 ; Macro ;--------------------------------------------------------------- CBLOCK 0CH ; from 0C to 4F = 67 ;--------------------------------------------------------------- ; MATH ROUTINES ; 16 X16 Mult , --> 32 ; mah mal x mbh mbl = mq4 mq3 mq2 mq1 ( mq4 highest) mq4 mq3 mq2 mq1 mbh mbl mah mal ; 16 / 16 Div --> 16 denlo denhi numlo numhi rmdrlo rmdrhi reshi reslo ;temp also used in nybble ; Addition 16+16 out max 65536 (no carry) ; Subtraction q1_16-q2_16 out r_16; neg numbers not allowed q1hi q1lo q2hi q2lo rhi rlo ;------------ Distance ----------------------- dtotlo ; total trip distance in metres dtothi ; dtothi/lo hold a max of 65km 536m, dtothi2 ; with this added max now 16777km dist54 ; corrects +2m every 54m (27x2.0747 =56) dm dm10 dm100 dm1000 dm10000 dm100000 ;------------ Average velocity Vav ------------------- ;vavhi ;vavlo ;divrat ;stemphi ;second info passed to stemphi/lo ;stemplo ;dtemphi ;dist info passed to dtemphi/lo ;dtemplo m m10 m100 m1000 ;--------- Time and trip time -------------------- ; sectotlo ; total trip seconds sectothi ; max 65536 = aprox 18hrs sec ; seconds digit sec10 ; 10's of second digit min ; minutes digit min10 ; 10's of minutes digit hr ; hours digit hr10 ; 10's of hours digit oldsec ; holds last value of sec cntmsec ; count ms TMR18 ; TMR0 217* TMR18 18 = 1 SEC ;---- isr interrupt service routine push pop ------- w_temp ; W isr var status_temp ; STATUS isr var fsr_temp ; FSR isr var ; --------- Instantaneous Velocity Vin -------------------- vinhi vinlo sectemp oldtemp totmslo totmshi totms2lo totms2hi mshi mslo spdflg ; 1st pass =0 2nd pass =1, diff is time between pulses ;-------- Miscellaneous ------------------ temp ; a temporary used in divide and nybble count menu ENDC ; end of definition block ;------------------------------------------------------------------- ORG 0 ; start at location 0 goto main ; jump over to main routine ORG 4 goto Isr ; jump to interrupt routine ;----------------------------------------------------------------------; ; The Main routine ; ;----------------------------------------------------------------------; ; -------- THE MAIN MODULES --------- ; Isr stores elapsed TRIP time and generates hr:min:sec ; Disptime TRIP time, to max 99:59:59 ; Dist Calculates total TRIP distance ; Dispdist TRIP distance to a max of 999km 999m ; Velav Average velocity, Totdist/totsecs max 99km/hr ; Dispvav Average TRIP speed up to 99.99 km/hr ; Velocity Speed from 2m/(time diff of pulse2-pulse1) ; Dispd Speed up to 99.99km/hr ; NOTE. Speed is your calculated velocity every second ; -------- more to come??? NO, running out of space --------- ;----------------------------------------------------------------------- ; M A I N ;----------------------------------------------------------------------- main: call Init ; Initialize ports, set up timer call Initlcd ; Initialize the LCD DUMMY Initend: ;------------------------------------------------------------------ ; Changesec Changewhlpulse wait for interrupts from newsec or newwheelpulse ; ----------------------------------------------------------------- clrf menu ; initially set to 0 menu loop clrf spdflg ; Initially = 0 2nd = sub for time from zero ; every new sec, set to zero ; CHANGESEC LOOPS TILL SEC CHANGES ;clrw ;DUMMY ;xorwf sec,w ;DUMMY ;btfsc STATUS,2 ;DUMMY ;incf sec,f ;DUMMY Changesec: ;loops checking new sec and new wheel pulse movf oldsec, w xorwf sec,w ; if equal, w= 0 and Z bit=1 btfsc STATUS,2 ; test Z bit goto Changesec ; no change, loop movf sec,w ; sec has increased, update movwf oldsec ; sec and oldsec are = again incf menu,f ; increases menu every sec ; Note1, sectotlo/hi are updated every sec in ISR module ; Note2, Decimal time also created in ISR module ; but time display for LCD controlled from ; the automatic menu below call Disptime ;TRIP time, will appear on LCD top line Changewhp: ; 1st detected wheelpulse starts menu sequence clrf PORTB bcf PORTB,5 ; start with green LED off loop: btfss PORTB, 4 ;(PUT BTFSC FOR TEST); i/p hi,?DUMMY goto loop ; not yet ; i/p hi detected ;green LED flashes with every wheel pulse bsf PORTB, 5 ; LED on ; wait a while to make sure switch has ; settled movlw D'10' ; wait about 10 msec call nmsec btfsc PORTB, 4 ; will be lo (0) when finished goto $ -1 ; still low ; now must wait a make sure bouncing stopped movlw D'10' ; 10 milliseconds call nmsec ; and check again btfsc PORTB, 4 ; if set, not finished goto $ -5 ; still hi start debounce wait again Tp1: ; green LED ready for next wheel pulse bcf PORTB,5 ; i.e. LED off movf PORTB,w ; reading to clear ; AUTOMATIC MENU: (LCD Top line always time) ; -------------- ; menu selects at 10sec intervals in rotation ; Speed menu 1 to 10 ; Dist ance menu 11 to 20 ; Velav Avg vel menu 21 to 30 ; MENU 0 TO 10 ; as speed ( Speed) is stand alone, no sense in calculating it ; if it is not going to be displayed when menu is >10. ; BUT if SPEED AVERAGING IS TO BE USED, kill off the lines below that bypass ; the SPEED calc module Menucheck: movlw 0x0A ;check menu > 10 (to meet >10 AND =<20) subwf menu,w btfss STATUS,C ;compare with 10 goto $+2 ;menucount =< 10 not yet got to 11 jump to Speed goto Distblk call Speed ; Calculates instant speed,(time between pulses) movlw 0x0A ; DUMMY, was movlw 0xA subwf menu,w btfss STATUS,C ;compare with 10 call Spdflgcheck ;menucount =< 10 ; also see 0-1 spdflg goto Distblk Spdflgcheck: clrw xorwf spdflg,w btfsc STATUS,Z call Dispd ;menucount =< 10 AND spdflg =1 Return Distblk: call Dist ;menucount > 10 skip Dispd update Dist ; MENU 11 TO 20 movlw 0x0A ;check menu > 10 (to meet >10 AND =<20) subwf menu,w btfss STATUS,C ;compare with 10 goto $+5 ;menucount =< 10 not yet got to 11 jump to velav movlw 0x14 subwf menu,w btfss STATUS,C ;compare with 20 call Dispdist ;menucount =< 20 call Velav ;menucount > 20 skip dispdist update Velav ; MENU 20 TO 30 movlw 0x14 ;check menu > 20 (to meet >20 AND =<30) subwf menu,w btfss STATUS,C ;compare with 20 goto $+5 ;menucount =< 20 not yet got to 21 jump to NEXT movlw 0x1E subwf menu,w btfss STATUS,C ;compare with 30 call Dispvav ;menucount =< 30 movlw 0x1F ;if it has reached 31 reset menu, before return xorwf menu,w btfss STATUS,Z goto $ +2 ; not 31 goto changewhp1 direct clrf menu ; is 31, reset menu before changewhp1 Menuend: ; before changewhp jump, check that sec has not updated ; if it has , jump back to changesec routine movf oldsec, w xorwf sec,w ; if equal, w= 0 and Z bit=1 btfsc STATUS,2 ; test Z bit goto Changewhp ; no sec change, check whpulse change goto Changesec ; a new sec starts measurements ; loop while NOT newsec ; --------------------------------------------------------------- ; End Main ; ---------------------------------------------------------------- ;----------------------------------------------------------------------; ; ISR, increments TMR0 by 1 every 256 µsec. Basically just ; ; reset the INTCON and TMR0 bits. The FSR,w, STATUS push pop kept ; ;----------------------------------------------------------------------; Isr: movwf w_temp ; save W swapf STATUS,W ; save status movwf status_temp ; without changing flags swapf FSR,W ; save FSR movwf fsr_temp ; without changing flags ;256us * 217 * 18 = 0.999936 sec. ;1hr_err = 0.23sec or 1 sec in 4hr ;The time loop starts with TMR0 loaded with (256-217)=39 After 217 ;steps interrupt is set, TMR0 rolls to zero and the TMR18 inc +1 movlw D'39' ;39 = 0x27 movwf TMR0 incf TMR18,f movlw D'18' ; DUMMY18 = 0x12 xorwf TMR18,w ; if equal, w= 0 and Z bit=1 btfss STATUS,2 ; test Z bit goto restore ; NOT 18 so pop stack and return to main Firstsec: clrf TMR18 ; is 18. set TMR18 to zero and INC sec incf sec,f ; also inc sectot (eventually lo and hi) incf sectotlo,f ; check sectot lo 00->inc hi clrw xorwf sectotlo,w ; if equal, w= 0 and Z bit=1 btfsc STATUS,2 incf sectothi,f ; increases every 256 x sectotlo movlw 0xA ; check if =10 xorwf sec,w ; if equal, w= 0 and Z bit=1 btfss STATUS,2 ; test Z bit goto restore ; NOT 10 so pop stack and return to main clrf sec ; is 10. set sec to zero and inc sec10 incf sec10,f movlw 0x6 ; = 0x6 xorwf sec10,w ; if equal, w= 0 and Z bit=1 btfss STATUS,2 ; test Z bit goto restore ; NOT 6 so pop stack and return to main clrf sec10 ; is 6. set sec10 to zero and INC min incf min,f movlw 0xA ; check if =10 xorwf min,w ; if equal, w= 0 and Z bit=1 btfss STATUS,2 ; test Z bit goto restore ; NOT 10 so pop stack and return to main clrf min ; is 10. set sec to zero and inc min10 incf min10,f movlw 0x6 ;= 0x6 xorwf min10,w ; if equal, w= 0 and Z bit=1 btfss STATUS,2 ; test Z bit goto restore ; NOT 6 so pop stack and return to main clrf min10 ; is 6. set min10 to zero and INC hr incf hr,f movlw 0xA ;check if =10 xorwf hr,w ; if equal, w= 0 and Z bit=1 btfss STATUS,2 ; test Z bit goto restore ; NOT 10 so pop stack and return to main clrf hr ; is 10. set hr to zero and inc hr10 incf hr10,f movlw 0xA ;= 10 xorwf hr10,w ; if equal, w= 0 and Z bit=1 btfss STATUS,2 ; test Z bit goto restore ; NOT 10 so pop stack and return to main clrf sec ; is ten (99hr 59min 59sec +1sec) we are at MAX clrf sec10 ;all reset to zero and start again clrf min clrf min10 clrf hr clrf hr10 clrf TMR18 clrf oldsec goto restore restore: swapf status_temp,W ; get original status back movwf STATUS ; into status register swapf fsr_temp,W ; get original fsr back movwf FSR ; into status register swapf w_temp,f ; old no flags trick again swapf w_temp,W ; to restore W bcf INTCON,T0IF ; clear the TMR0 interrupt flag retfie ; finished, reset GIE ;----------------------------------------------------------------------; ; Initialize the ports ; ;----------------------------------------------------------------------; Init: clrf PORTA clrf PORTB movlw B'00000000' ; Porta all outputs tris PORTA movlw B'01010000' ; 7o 6i-Wire 5o-LED 4i-multivib, tris PORTB ; 0to3lcd movlw B'00000111' ; opt pull-ups enabled ; opt prescaler assigned to TMR18 ; opt prescaler set to 1:256 option ; opt rolls over each 125th second movlw 0 ; zero out all registers clrf m clrf m10 clrf m100 clrf m1000 clrf dm clrf dm10 clrf dm100 clrf dm1000 clrf dm10000 clrf dm100000 clrf totmshi clrf totmslo clrf totms2hi clrf totms2lo clrf dist54 clrf hr10 clrf hr clrf min10 clrf min clrf oldsec clrf sec10 clrf sec clrf sectotlo clrf dtotlo clrf dtothi clrf sectotlo clrf sectothi clrf TMR18 ; when this reaches 18, 1 sec has elapsed ; TMR0 has to start at 256-217 :39, so that the first rollover ; with prescaler 256, happens in 217*256us =0.055552sec ; The TMR18 incs each roll and after 18 1 sec has elapsed movlw 0x27 ; D39 movwf TMR0 ; set to D39 217 counts later 256, ; TMR0 resets and inc TMR18 in Isr loop clrf INTCON ; START WITH ALL AT 0 ; movlw B'10100000' ; GIE set T0IE set, T0IF cleared movlw B'10100000' ; Set:GIE,T0IE,RBIE Clrd:T0IF,RBIF movwf INTCON ; ready to detect interrupts in ISR return ;----------------------------------------------------------------------; ; Initialize the LCD ; ;----------------------------------------------------------------------; Initlcd: movlw D'40' call nmsec ; Wait 40 msecs before Reset bcf RS ; send an 8 bit instruction movlw 0x03 ; Reset Command call NybbleOut ; Send the Nybble call Dlay5 ; Wait 5 msecs before Sending Again call EStrobe call Dlay160 ; Wait 160 usecs before Sending 2nd Time call EStrobe call Dlay160 ; Wait 160 usecs before Sending 3rd Time bcf RS ; send an 8 bit instruction movlw 0x02 ; Set 4 Bit Mode call NybbleOut call Dlay160 movlw 0x028 ; 4 bit, 2 Line, 5x7 font call SendINS movlw 0x010 ; display shift off call SendINS movlw 0x001 ; Clear the Display RAM call SendINS call Dlay5 ; Note, Can take up to 4.1 msecs movlw 0x006 ; increment cursor call SendINS movlw 0x00C ; display on cursor off call SendINS return ;----------------------------------------------------------------------; ; Send the character in W out to the LCD ; ;----------------------------------------------------------------------; SendASCII addlw '0' ; Send nbr as ASCII character SendCHAR: ; Send the Character to the LCD movwf temp ; Save the temporary Value swapf temp, w ; Send the High Nybble bsf RS ; RS = 1 call NybbleOut movf temp, w ; Send the Low Nybble bsf RS call NybbleOut return ;------------------------------------------------------------- ; ES strobe ;------------------------------------------------------------- EStrobe: ; Strobe the "E" Bit bsf E bcf E return ;----------------------------------------------------------------------; ; Send an instruction in W out to the LCD ; ;----------------------------------------------------------------------; SendINS: ; Send the Instruction to the LCD movwf temp ; Save w swapf temp, w ; send Hi Nybble bcf RS ; RS to 0 call NybbleOut movf temp, w ; Send Lo Nybble bcf RS call NybbleOut return ;----------------------------------------------------------------------; ; Send the nibble in W out to the LCD ; ;----------------------------------------------------------------------; NybbleOut: ; Send a Nybble to the LCD movwf PORTB call EStrobe ; Strobe out the LCD Data bsf E call Dlay160 ; delay for 160 usec return ;----------------------------------------------------------------------; ; Output the message on the LCD ; ;----------------------------------------------------------------------; OutMessage1: movwf FSR ; Point at first letter OutLoop: movf FSR, w ; Get pointer into W incf FSR, f ; Set up for next letter call Dispmsg1 ; Get character to output iorlw 0 ; At the End of the Message? btfsc STATUS, Z ; Skip if not at end return ; Yes - Equal to Zero call SendCHAR ; Output the ASCII Character goto OutLoop ; Get the next character ;----------------------------------------------------------------------; ; Data for message to be output ; ;----------------------------------------------------------------------; Dispmsg1: ; Message to Output addwf PCL, f ; Output the Characters dt "Bike Computer", 0 Dispmsg2: ; Message to Output addwf PCL, f ; Output the Characters dt "Dist", 0 Dispmsg3: addwf PCL,f ; message to output dt " Av speed:",0 ; output characters ;----------------------------------------------------------------------; ; time delay routines ; ;----------------------------------------------------------------------; ;Note . The original application needed precise times from the delay code that ; follows. But the Simple Bike application does NOT use the routines for ; any CRITICAL time measurement. Dlay160: movlw D'41' ; delay about 160 usec micro4: addlw H'FF' ; subtract 1 from 'W' btfss STATUS,Z ; skip when you reach zero goto micro4 ; more loops return Dlay5: movlw 5 ; delay for 5 milliseconds goto $ + 2 msec250: movlw D'250' ; delay for 250 milliseconds ; --- N millisecond delay routine --- nmsec: movwf cntmsec ; delay for N (you put in W) millisec msecloop: movlw D'254' ; load takes .9765625 microsec call micro4 ; by itself CALL takes ... ; about 1ms nop ; 1usec decfsz cntmsec, f ; .98 skip not taken, else 1.95 goto msecloop ; 1.95 here: total ~1000 / loop return ; final time through ~999 to here ; overhead in and out ignored ; this block is functional for tests, but not used in bike Dlay1sec: ; this is a marker routine to see where call msec250 ; the prg is at. ; bsf LED call msec250 ;flash led on portb pin6 ;bcf LED call msec250 ; bsf LED call msec250 ; finishes with green led lit return ;----------------------------------------------------------------------; ; Display the Time ; ;----------------------------------------------------------------------; Disptime: movlw H'80' ; position at beginning of first line call SendINS movf hr10, W ; tens of hours call SendASCII movf hr, W ; hours call SendASCII movlw ":" call SendCHAR movf min10, W ; tens of minutes call SendASCII movf min, W ; minutes call SendASCII movlw ":" call SendCHAR movf sec10, W ; tens of seconds call SendASCII movf sec, W ; seconds call SendASCII movlw " " call SendCHAR ; the h m s really not needed movlw "h" call SendCHAR movlw "m" call SendCHAR movlw "s" call SendCHAR movlw " " call SendCHAR Dispend: RETURN ;---------------------------------------------------- ; End of display time ;---------------------------------------------------- ;----------------------------------------------------------------------; ; Display the Distance , (trip distance at moment) ; ;----------------------------------------------------------------------; Dispdist: movlw H'C0' ; position at beginning of second line call SendINS movf dm100000, W ; 0 of 065km 536m call SendASCII movf dm10000, W ; 6 of 065km 536m call SendASCII movf dm1000, W ; 5 of 065km 536m call SendASCII movlw "k" ; k of 065km 536m call SendCHAR movlw "m" ; m of 065km 536m call SendCHAR movlw " " ; " " of 065km 536m call SendCHAR movf dm100,W ; 5 of 065km 536m call SendASCII movf dm10,W ; 3 of 065km 536m call SendASCII movf dm, W ; 6 of 065km 536m call SendASCII movlw "m" ; m of 065km 536m call SendCHAR movlw " " ; " " call SendCHAR movlw "D" ; "D " call SendCHAR movlw "i" ; " i" call SendCHAR movlw "s" ; "s " call SendCHAR movlw "t" ; " t" call SendCHAR movlw " " ; " " ( to wipe screen) call SendCHAR Distend: RETURN ; ;---------------------------------------------------------------------* ; Multiplication 16x16 Out32 ;---------------------------------------------------------------------* Mult16x16: clrf mq4 clrf mq3 clrf mq2 clrf mq1 bsf mq2, 7 Mu1: rrf mah, f rrf mal, f skpc goto Mu2 movf mbl, w addwf mq3, f movf mbh, w skpnc incfsz mbh, w addwf mq4, f Mu2: rrf mq4, f rrf mq3, f rrf mq2, f rrf mq1, f skpc goto Mu1 Endmult16: clrf mbh clrf mbl clrf mah clrf mal Return ;---------------------------------------------------------------------* ; End Multiplication 16x16 Out32 ;---------------------------------------------------------------------* ;---------------------------------------------------------------------* ; New routine Division 16/16 Out 16 ; ---------------------------------------------------------------------* ; Finally:- Result 000AH in reshi/lo, remainder 0096H in rmdrhi/lo ; so, checking end of division A=10 and 96H/E1H= 150/225 =0.666 OK Div: call D_divS ; remainder in Rmdr. Divend: nop return D_divS: call setup clrf rmdrhi clrf rmdrlo dloop: bcf STATUS,C rlf reslo, f rlf reshi, f rlf rmdrlo, f rlf rmdrhi, f movf denhi,w subwf rmdrhi,w ;check if a>c btfss STATUS,Z goto nochk movf denlo,w subwf rmdrlo,w ;if msb equal then check lsb nochk: btfss STATUS,C ;carry set if c>a goto nogo movf denlo,w ;c-a into c subwf rmdrlo, f btfss STATUS,C decf rmdrhi, f movf denhi,w subwf rmdrhi, f bsf STATUS,C ;shift a 1 into b (result) nogo: rlf numlo,f rlf numhi,f decfsz temp, f ;loop untill all bits checked goto dloop setup: movlw .16 ; for 16 shifts movwf temp movf numhi,w ;move Num to Res movwf reshi movf numlo,w movwf reslo clrf numhi clrf numlo retlw 0 ;--------------------------End Div 16 by 16---------------------------- return ;---------------------------------------------------------------------* ; END DIVISION 16/16 OUT 16 ;--------------------------------------------------------------- ;---------------------------------------------------------------------* ; START Addition 16-16 OUT 16 ;--------------------------------------------------------------- Add: ; R = q1 + q2 movf q1lo, W addwf q2lo, W movwf rlo movf q1hi, W btfsc STATUS, C addlw .1 ; If A Carry Occurred, Add 1 addwf q2hi, W movwf rhi ;clrf q1hi ; added this to stop next user summing; ;clrf q1lo ;clrf q2hi ;clrf q2lo Return ;---------------------------------------------------------------------* ; END Addition 16-16 OUT 16 ;--------------------------------------------------------------- ;---------------------------------------------------------------------* ; START Subtraction 16-16 OUT 16 ;--------------------------------------------------------------- Sub: ; R = q1 - q2 movf q2lo, W subwf q1lo, W ; W = q1lo - q2lo movwf rlo btfss STATUS, C Goto Borrow Goto Sub1 Borrow: Decf q1hi, F Sub1: movf q2hi, W subwf q1hi, W ; W = q1hi - q2hi movwf rhi Return ;--------------------------------------------------------------- ; END Subtraction 16-16 OUT 16 ;--------------------------------------------------------------- ;--------------------------------------------------------------- ; Dist = counting wheel pulses ;--------------------------------------------------------------- Dist: ; whlcirc = 2.0747, for initial test =2 ; ; movlw 0x2 ; <-- don't forget, this is just for 26"wheel ; movwf whlcirc ; start count at 0 and inc till same as whlcirc ; (range will be 1 to 4 m) clrf count ; count can be 2 or 4 in this 26" case Whloop: ; although the distance pulses ( in this example) jump in steps of 2m ; the m counter has to increase in 1's to catch the ; decimal rollover from 10 to 0 ; so count, here, is stepped from 0 to 2 (whlcirc) ; Also, when the dist54 var increases 54m, COUNT adds an extra 2m ; to the hex and decimal totals, before resetting to zero. ; Note that as dist54 is in the loop it will also receive ; the 2m increase so it really counts to 56 (54 +2). movlw 0x2 ; wheel circumference of 26 inch xorwf count,w ; if equal, w= 0 and Z bit=1 btfsc STATUS,2 ; test Z bit goto DistanceEnd ; they are the same, so jump distanceEnd and return goto Mcount ; NOT yet = whlcirc, so inc mcount by 1 and return Mcount: ; Distance to decimal for display , i/p m, o/p 000km 000m ; starts with m, m10, m100, m1000 , m10000, m100000 = 0 ; Later correct m every 54m (add 2m ???? ) incf dm,f movlw D'10' xorwf dm,w ; if equal, w= 0 and Z bit=1 btfss STATUS,2 ; test Z bit goto Mcountend ; NOT 0 so return clrf dm incf dm10,f movlw D'10' xorwf dm10,w ; if equal, w= 0 and Z bit=1 btfss STATUS,2 ; test Z bit goto Mcountend ; NOT 0 so return clrf dm10 incf dm100,f movlw D'10' xorwf dm100,w ; if equal, w= 0 and Z bit=1 btfss STATUS,2 ; test Z bit goto Mcountend ; NOT 0 so return clrf dm100 incf dm1000,f movlw D'10' xorwf dm1000,w ; if equal, w= 0 and Z bit=1 btfss STATUS,2 ; test Z bit goto Mcountend ; NOT 0 so return clrf dm1000 incf dm10000,f movlw D'10' xorwf dm10000,w ; if equal, w= 0 and Z bit=1 btfss STATUS,2 ; test Z bit goto Mcountend ; NOT 0 so return clrf dm10000 incf dm100000,f movlw D'10' xorwf dm100000,w ; if equal, w= 0 and Z bit=1 btfss STATUS,2 ; test Z bit goto Mcountend ; NOT 0 so return clrf dm ; is distmax, reset to 0 clrf dm10 clrf dm100 clrf dm1000 clrf dm10000 clrf dm100000 goto Mcountend ; see if whpl update is now finished Mcountend: ;update the hex counters dtot /lo/hi/hi2 incf dtotlo,f clrw xorwf dtotlo,w ; if equal, w= 0 and Z bit=1 btfss STATUS,2 ; test Z bit goto Dtotend ; dtotlo NOT 0 incf dtothi,f ; add 1, dtotlo is 0 clrw xorwf dtothi,w ; if equal, w= 0 and Z bit=1 btfss STATUS,2 ; test Z bit goto Dtotend ; dtothi NOT 0 incf dtothi2,f ; add 1, dtothi is 0 Dtotend: movlw D'54' movwf numhi ; not in use here, use to store 54 movf dist54,w subwf numhi,w btfsc STATUS,C goto Noextra ; dist54=< 53, don't freeze COUNT movlw D'56' ; dist54>53 add 2 extra counts movwf numhi ; not in use here to store 56 movf dist54,w subwf numhi,w btfsc STATUS,C goto Extra ; dist54=< 56, freeze COUNT clrf dist54 goto Noextra Noextra: incf dist54,f incf count,f goto Whloop Extra: ; an extra loop BUT count is NOT increased incf dist54,f goto Whloop DistanceEnd: ; All conversion loops have executed and ; only left to display in decimal, when selected ; return ; BACK TO MAIN ;-------------------------------------------------------------------------- ; End of Trip Distance calculation Module ;-------------------------------------------------------------------------- ;---------------------------------------------------------- ; Calculate Average velocity from Velav = totdist/totsec ;----------------------------------------------------------- ; 1st time round distto = 0 and sectot =0. ; Disttot lo/hi zero is prob OKbut test sec=0. If zero return Velav: clrw xorwf sectotlo,w btfss STATUS,Z goto Spdstart ; not 0, onto Avspeed calc clrw ; lo was 0, but hi byte may not be xorwf sectothi,w btfss STATUS,Z goto Spdstart ; was not 0, onto Avspeed calc ;incf sectotlo,f ; DUMMY ;goto Spdstart ; DUMMY return ; was also 0, abort module wait for sec inc ; to slow down the number of whpul in the test we add Dummies Spdstart: ; for test delay incf sec and sectot, gives 7.2km/hr ;incf sec,f ; DUMMY ;incf sectotlo,f ; DUMMY ;Lpstart: movlw 0x07 ; 1800 comp hi bytes subwf dtothi,w btfsc STATUS,C goto Divratio ; dtothi is > 1800hi and needs dividing ; we suppose that the hibyte test is sufficient, ; it may not be true. e.g hi's the same but lo's diff ; pass dtot to m1000,m100 movf dtothi,w ; dtothi <1800 movwf m1000 movf dtotlo,w movwf m100 ; m1000,m100 hold dtothi/lo ( no div needed) movf sectothi,w movwf m10 movf sectotlo,w movwf m ; m10,m hold sectothi/lo ( no div needed) ; note, this is dtot to m1000,m100 not needing division goto Ratend ; if divratio is needed, then m1000,m100 is used there Divratio: ; enter variables and 1800 movf dtothi,w movwf numhi movf dtotlo,w movwf numlo movlw 0x07 movwf denhi movlw 0x08 movwf denlo call Div incf reslo,w ; reslo holds INT of Divratio +1 movwf count ; not used at the moment movf dtothi,w ; divide dtothi/lo by count movwf numhi movf dtotlo,w movwf numlo clrf denhi movf count,w ; e.g. D 37 movwf denlo call Div ; we need parking space for results, REuse the m1000 etc movf reshi,w movwf m1000 ; movf reslo,w movwf m100 ; m1000, m100 =dtot/count ; repeat for sectot movf sectothi,w movwf numhi movf sectotlo,w movwf numlo clrf denhi movf count,w ; e.g D 37 movwf denlo call Div movf reshi,w movwf m10 movf reslo,w movwf m ; m10, m =sectot/count Ratend: ; WE CAN NOW DO DTOT/SECTOT x3.6 as we have SCALED TO AVOID OVERFLOW ; m1000,m100 x 36 (top line) ; Vav= ------------------------- = Average speed in km/hr ; m10,m x 10 (bottom line) ; 36(24H), 10(AH) movf m1000,w ; MULT dtot, top line, by 24H movwf mah movf m100,w movwf mal clrf mbh movlw 0x24 movwf mbl call Mult16x16 ; ( store top line result in the same variables) movf mq2,w movwf m1000 movf mq1,w movwf m100 ; dtot x 36 movf m10,w ; MULT sectot, bottom line, by AH movwf mah movf m,w movwf mal clrf mbh movlw 0xA movwf mbl call Mult16x16 ; ( store bottom line result in the same variables) movf mq2,w movwf m10 movf mq1,w movwf m ; sectot x 10 Tp3: ; As we are reusing variables, trying to show where the results to here are being held ; m1000,m100 ; We now have as Average speed ----- --------- km/hr ; m10,m ;the AVERAGE SPEED AT LAST movf m1000,w ; dtotx36/sectotx10 movwf numhi movf m100,w movwf numlo movf m10,w movwf denhi ; needed below for decimal place movf m,w movwf denlo ; needed below for decimal place call Div ; denhi/lo can chanfge as the div routine reuses bits ; so we reestablish previous denom values before losing movf m10,w movwf denhi movf m,w movwf denlo ; we can now write over m and m10 movf reshi,w ;will be zero ;(bikes don't exceed 256km/hr normally) movf reslo,w ; reuse variables m10 INT and later m FRAC movwf m10 ;we have integer part of Vav Average speed ; rmdrhi/lo holds decimal fraction.we will only use 1 decimal place. ; so mult the rmdrhi,lo x10 before dividing by denhi/lo movf rmdrhi,w ; mult top by 10 movwf mah movf rmdrlo,w movwf mal ; rmdr in tot storage clrf mbh movlw 0xA movwf mbl call Mult16x16 ; rmdrhi,lo x 10 ; top line is now mq2,mq1 ( nothing in mq4,mq3) ; Bottom line..remember we already have loaded ; the denominator above which says ; " needed below for decimal place" Tp4: movf mq2,w ;rmdrhi,w movwf numhi movf mq1,w ;rmdrlo,w movwf numlo call Div ; div for the decimal( just 1 place), in reslo ; reshi,reslo holds decimal place results ; m10+m are av speed as INT+DEC but in hex ; now change to decimal. ; m10 and m are going to be wiped, but there is no ;divide routine so for INT +FRAC we use reshi reslo movf m10,w movwf reshi ; OK? yes reshi +reslo = INT+FRAC in hex, convert Vavh2d: clrf m clrf m10 clrf count Vavloh2d: ; m is 2nd dec, use m10 movf count,w xorwf reslo,w btfsc STATUS,Z goto Vavloend incf count,f incf m10,f movlw 0xA xorwf m10,w btfss STATUS,Z goto Vavloh2d clrf m10 incf m,f goto Vavloh2d Vavloend: clrf m100 clrf m1000 clrf count Vavhih2d: movf count,w xorwf reshi,w btfsc STATUS,Z goto Vavhiend incf count,f incf m100,f movlw 0xA xorwf m100,w btfss STATUS,Z goto Vavhih2d clrf m100 incf m1000,f goto Vavhih2d Vavhiend: ; We have Vav as: INT m1000,m100 and FRAC m10,m with Frac Vavend: nop return ;----------------------------------------------------------------------; ; Display the Average velocity ; ;----------------------------------------------------------------------; Dispvav: movlw H'C0' ; position at beginning of second line call SendINS movf m1000, W ; x0 tens of km call SendASCII movf m100,W ; 0x ones of km call SendASCII movlw "." ; "." punto decimal call SendCHAR movf m10,W ; "0.x0" decimal call SendASCII movf m, W ; "0.0x" decimal call SendASCII movlw "k" ; k call SendCHAR movlw "m" ; m call SendCHAR movlw "/" ; / call SendCHAR movlw "h" ; h call SendCHAR movlw "r" ; r call SendCHAR movlw " " ; " " call SendCHAR movlw "V" ; V call SendCHAR movlw "e" ; e call SendCHAR movlw "l" ; l call SendCHAR movlw "A" ; A call SendCHAR movlw "v" ; v call SendCHAR movlw "g" ; g call SendCHAR movlw " " ; " " call SendCHAR Dispvavend: Return ; to change seconds for next second ;--------------END OF AV SPEED------------------- ; --------------------------------------------------------------- ; Instantaneous Speed ;----------------------------------------------------------------- ; Previous time values are subtracted from the present ; sec, TMR18, TMR0 values. ; This time difference is the time between ; wheel pulses. This in turn for 26" wheel is a distance of 2.0747m ; is covered in totms ; --------------------------------------------------------------- ; Instantaneous Speed ;----------------------------------------------------------------- ; Note sec can only move between 0 and 9, 10 is a new 0 ; We start by reading sec, TMR18, TMR0 TO GET START TIME Speed: Spdflg0: ; FIRST TIMEwith spdflg=0, second time with spdflg=1 ; First time captures timezero 0 in ms in totms clrw xorwf spdflg,w btfss STATUS ,Z goto Spdflg1 ; spdflg is not 0, this is the second time call Summs movf mshi,w movwf totmshi movf mslo,w movwf totmslo incf spdflg,f ; (spdflg = 1 for SECOND TIME) return ;jump back to main menu Summs: ; this routine is common to the first and second time movf sec,w ; spdflg = 0 movwf mbl ; sec can only be in range 0 to 9 clrf mbh movlw 0x03 ; 1000 = 03E8 movwf mah movlw 0xE8 ; 1000 = 03E8 movwf mal call Mult16x16 ; movf mq2,w ; hi byte movwf mshi movf mq1,w ; lo byte movwf mslo movf TMR18,w movwf mal clrf mah movlw D'56' movwf mbl clrf mbh call Mult16x16 ; after themult we add movf mshi,w movwf q1hi movf mslo,w movwf q1lo movf mq2,w movwf q2hi movf mq1,w movwf q2lo call Add movf rhi,w movwf mshi movf rlo,w movwf mslo ; sum (sec +TMR18) ;TMR0 is a little more complex as we have to subtract39 ; before we can calculate ms in TMR0 movf TMR0,w movwf q1lo clrf q1hi movlw D'39' movwf q2lo clrf q2hi call Sub movf rlo, w movwf numlo clrf numhi movlw 0x4 movwf denlo clrf denhi call Div movf mshi,w movwf q1hi movf mslo,w movwf q1lo movf reshi,w movwf q2hi movf reslo,w movwf q2lo call Add movf rhi,w movwf mshi movf rlo,w movwf mslo ; ( SEC +TMR18+ TMR0) in ms mshi/lo RETURN ; Summs Summsend: Spdflg1: ; SECOND TIME is with spdflg =1 ; Second time captures timezero to 2nd Pulse ; in ms, store in totms2 ; for test we add 250ms to simulate time between ; wheel pulses ; 250ms delay ;movlw D'63' ; DUMMY FOR TEST ;call nmsec ; DUMMY FOR TEST call Summs movf mshi,w ; movwf totms2hi ; movf mslo,w ; movwf totms2lo ; ; totms2 should be larger than totms,however if totms2 has just rolled, ; then add 10000 to tot2 Testtot2: movf totms2hi,w subwf totmshi,w btfss STATUS,C ;compare for greater goto Diffms ;totms2 > totms no add 10000 needed movlw 0xA ;totms2 < totms, add 10000, hex 2710 movlw 0x27 movwf q1hi movlw 0x10 movwf q1lo movf totms2hi,w movwf q2hi movf totms2lo,w movwf q2lo call Add ; tot2 +10000 movf rhi,w movwf totms2hi movf rlo,w movwf totms2lo ; tot2 = tot2+10000 Diffms: ; sub (tot2-tot1)for time between wheel pulses movf totms2hi,w ; movwf q1hi movf totms2lo,w movwf q1lo movf totmshi,w movwf q2hi movf totmslo,w movwf q2lo call Sub ; result is time diff, stored in rhi, rlo tp2: ; we are almost ready to divide dist/time for speed ; complete equation is dist/time x3.6 ; to avoid as far as possible fractions we multiply before divide ; but this time, better divide first. ; 2000 36 <--- this is more than 65536 ; ---- x -------- ; denhi/lo 10 ( denhi/lo is sub result rhi/lo) ; so we can divide by 10 and x by 36 ( always,) ; so top line is simply 7200. ; we finally have 7200/(denhi/lo) , SPEED IN HEX movf rhi,w movwf denhi movf rlo,w movwf denlo ; denom is time in hex movlw 0x1C ; 7200, hex 1C20 movwf numhi movlw 0x20 ; 7200, hex 1C20 movwf numlo call Div ; the result , res, is more than 1 ;and a remainder rmdr/den ;we now have 6 free variables ; mshi/lo for INT hex speed lo is enough ; totmshi/lo ; totms2hi/lo ;we will reuse to convert from hex to decimal movf reslo,w movwf mshi ; INT part of hex speed ( hi, because FRAC will be in lo) ;we will only use 1 decimal of ;the hex speed remainder, so 1st rmdrx10 ; so next calculation is ; 10 x rmdrhi/lo ; --------------- ; denhi/lo movf rmdrhi,w movwf mah movf rmdrlo,w movwf mal clrf mbh movlw 0x0A movwf mbl call Mult16x16 ;( ten times top) movf mq2,w movwf numhi movf mq1,w movwf numlo ; the den is previously loaded so divide for ; decimal part of speed ( still in hex) call Div ; we only use 1 decimal, so just use lo of Div result movf reslo,w movwf mslo ; FRAC of speed in hex ; now convert speed to decimal form ; i.e. mshi, mslo --> 99.9 km/hr Spdh2d: clrf m clrf m10 clrf m100 clrf m1000 clrf count Spdlo: movf count,w xorwf mslo,w btfsc STATUS,Z goto Spdhicnt incf count,f incf m10,f movlw 0xA xorwf m10,w btfss STATUS,Z goto Spdlo clrf m10 incf m,f goto Spdlo Spdhicnt: clrf m100 clrf m1000 clrf count Spdhi: movf count,w xorwf mshi,w btfsc STATUS,Z goto Spdhiend incf count,f incf m100,f movlw 0xA xorwf m100,w btfss STATUS,Z goto Spdhi clrf m100 incf m1000,f goto Spdhi Spdhiend:; We have filled m1000,m100 with INT and m10,m with Frac, of Vin. ; Note we only need m10 ( 1 decimal place) clrf spdflg ; reset to 0 for next pair of pulses ; incf spdflg,f ; (spdflg = 1 for SECOND TIME) return ; ---------------------------------------------------------------** ; End of Instantaneous Speed ; ---------------------------------------------------------------** Dispd: ;----------------------------------------------------------------------; ; Display the Instantaneous Speed (Speed) ; ;----------------------------------------------------------------------; movlw H'C0' ; position at beginning of second line call SendINS movf m1000, W ; x0 tens of km call SendASCII movf m100,W ; 0x ones of km call SendASCII movlw "." ; "." punto decimal call SendCHAR movf m10,W ; "0.x0" decimal call SendASCII movf m, W ; "0.0x" decimal call SendASCII movlw "k" ; k call SendCHAR movlw "m" ; m call SendCHAR movlw "/" ; / call SendCHAR movlw "h" ; h call SendCHAR movlw "r" ; r call SendCHAR movlw " " ; " " call SendCHAR movlw "S" ; S call SendCHAR movlw "p" ; p call SendCHAR movlw "e" ; e call SendCHAR movlw "e" ; e call SendCHAR movlw "d" ; d call SendCHAR Dispdend: Return ; to change seconds for next second ;--------------END OF Inst SPEED------------------- end ;----------------------------------------------------- ; bike computer program end ....enjoy ;----------------------------------------------------- ; WORK IN HAND ; CLEAN UP SPEED, readout still a little jumpy, ; can try integrating but need more space. Next version ; Coventions used in the program text. ;Variables all small letters , exceptions, those defined ; by microchip. e.g. STATUS ;Labels: First letter always a Capital, exceptions imported ; routines ; Test points. These have a comment DUMMY, the value of the code ; has/had been changed for testing or simulating. If YOU want ;to polish these points should be useful for value changes ; example. In ISR the D'18' value to reach a sec is very slow. ;Use for test , say 2, and you get going about 10 times faster. ;Also LCD INIT: can be bypassed to debug quicker ;---------------------------------------------------------------------------------------------------- ; End of the Simple Bike Computer ;----------------------------------------------------------------------------------------------------
Just a final word about the the skills we have learned up to here-
We said in the introduction, that most people maintain the hardest part of working with ucontrollers is writing the source code.
So we decided to start with the easy part, the hardware.
However after learning how to make our own PCB maybe we don't agree.
The people who say the hardware is EASY, either lead a very secluded life (someome else does the layout, makes the PCB etc ), or perhaps they have never even thought that such things exist.
Then the source code side is the easier? No, simply it is different activity which can be as formidable as the hardware integration we have learned.
A final thought. Computers get faster and faster,... thanks to ?
Yes, you guessed it.. thanks to ongoing hardware improvements.
The factor which limits the computer today 2002 is the need to connect the bits together inside the computer box. It is now very important to consider and design the PCB, in the high speed parts, using microstrip technology.