;************************************************
;* Project: Fiat Seicento RPM counter *
;* Author: Nebojsa Mrmak *
;* Date: 04 Feb 2008 *
;* *
;* Note: Check out www.fiatforum.com *
;* for more information *
;************************************************
ORG 0000h
MOV SP,#030h ;stack pointer vector
JMP INIT
ORG 013h ;interrupt that occurs when a pulse is detected
JMP PULSE
ORG 01Bh ;Timer 1 interrupt vector
JMP INT_T1 ;Jump to the timer1 interrupt routine
INIT: ;initialize our stuff
ORG 100h ;program start vector
SETB PX1
MOV TMOD,#00010001b ;establish T1 as a 16 bit timer - at 12MHz ~ 0.1s
MOV IE,#10001100b ;enable ints, enable T1, INT0 int.
MOV TH1,#11111101b ;reset the timer
MOV TL1,#11110000b ; -||-
;init some values here:
MOV P1,#1
MOV R0,#0
MOV R1,#0
MOV R2,#0
MOV R3,#0
MOV R4,#0
MOV R5,#0
MOV R6,#0
MOV R7,#0
MOV 0018h,#0
MOV 0019h,#0
MOV 001Ah,#0
MOV 001Bh,#0
MOV TCON,#01010000b ;start the timer
MAIN:
;The following four blocks are used to display the numbers on four 7-seg displays.
MOV A,#0
ACALL MASK
MOV P1,#0
MOV P0,A
MOV P1,#1
MOV A,0018h
ACALL MASK
MOV P1,#0
MOV P0,A
MOV P1,#2
MOV A,0019h
ACALL MASK
MOV P1,#0
MOV P0,A
MOV P1,#4
MOV A,001Ah
ACALL MASK
MOV P1,#0
MOV P0,A
MOV P1,#8
;and repeat.
JMP MAIN
INT_T1:
;timer overflow interrupt!
PUSH ACC
PUSH PSW
MOV TH1,#11111101b ;reset the timer
MOV TL1,#11110000b ; -||-
CPL P3.0 ;just flash a debug LED, safe to remove this line
MOV A,R0
ADD A,#10
JNC NO_OVERFLOW
INC R1
NO_OVERFLOW:
;if the low byte of the timer is still less than 255
MOV R0,A
POP PSW
POP ACC
RETI ;end timer overflow interrupt here
PULSE:
;ecu LT pulse interrupt start!
MOV 80A7h,#10101011b ;reset the watchdog timer.
;delay a little, debouncing while i was testing it with a button.
;Possibly safe to remove when in a car, ecu shouldn't have bounce.
MOV R7,#10
LOOP: MOV R6,#10
DJNZ R6,$
DJNZ R7,LOOP
;end delay
JB P3.3,NEXT ;if button is still pressed, do the interrupt
RETI ;else return from interrupt
NEXT:
MOV TCON,#00000000b ;stop the timer
PUSH ACC
PUSH PSW
MOV P2,R0 ;debug, outputs R0 value to port 2, safe to remove this line
MOV A,R0 ;push R0 and R1 onto the stack
PUSH ACC ;R0 and R1 hold the number of times the timer overflowed x10, and since it overflows once each
MOV A,R1 ;1ms, then it's the number of ms passed between 2 revs
PUSH ACC
;the following block practically divides the timer by 66
;(max. value the timer can achieve is 666, so you get 1/10 of that value, and the max is 0.001s)
MOV A,TL1
MOV R2,A ;move TL1 to r2 (TL1 - timer 1 lower byte)
MOV A,TH1
MOV R3,A ;move TH1 to r3 (TH1 - timer 1 higher byte)
MOV R1,#66 ;move 66 to R1 and 0 to R0 so we can divide
MOV R0,#0
LCALL div16_16 ;call the division routine (borrowed from someone else)
MOV A,R2 ;put the lower byte of the result into 0018h
MOV 0018h,A
POP ACC ;pop R1 and R0 from the stack
MOV R1,A
POP ACC
MOV R0,A
MOV A,R0 ;add the calculated number of ms on the timer to the number of overflows
ADD A,0018h
JNC NO_OVERFLOW1
INC R1
NO_OVERFLOW1:
TEST0: ;a small test to see if we're going to divide by 0 (we don't want that)
CJNE R1,#0,TEST_END ;if either register is non-zero, carry on
CJNE R0,#0,TEST_END
POP PSW ;if both are zero, pop the values from the stack and return from interrupt
POP ACC ;better to give up here than divide by 0, we'll get the rpm on the next 2 pulses
RETI
TEST_END:
MOV A,R0
MOV R2,A
MOV A,R1
MOV R3,A
MOV R1,#11101010b
MOV R0,#01100000b
LCALL div16_16 ;get the rpm using the formula: 60000/10* ms passed
MOV A,R3
MOV R1,A
MOV A,R2
MOV R0,A
;the following few blocks divide by 10, and get the individual digits that we're to output on our 7-segment displays
;for example, 1234 rpm:
;1234 div 10 = 123, mod = 4, least significant digit is 4
;123 div 10 = 12, mod = 3, second digit is 3
;12 div 10 = 1, mod = 2, third is 2, fourth is 1
;save each digit to it's designated ram address, which will be read and displayed in the main loop.
;note that the least significant digit is always zero, to make the display more readable (less fluctuations, easier code)
;and why the hell would you care if you're at 3501 or 3500 rmm anyway? :)
MOV R3,#0
MOV R2,#10
LCALL div16_16
MOV A,R0
MOV 0018h,A
MOV A,R3
MOV R1,A
MOV A,R2
MOV R0,A
MOV R3,#0
MOV R2,#10
LCALL div16_16
MOV A,R0
MOV 0019h,A
MOV A,R3
MOV R1,A
MOV A,R2
MOV R0,A
MOV R3,#0
MOV R2,#10
LCALL div16_16
MOV A,R0
MOV 001Ah,A
MOV A,R3
MOV R1,A
MOV A,R2
MOV R0,A
MOV R3,#0
MOV R2,#10
LCALL div16_16
MOV A,R0
MOV 001Bh,A
CPL P2.1 ;debug, safe to remove this line
MOV TH1,#11111101b ;reset the timer
MOV TL1,#11110000b ; -||-
MOV R0,#0 ;reset the overflow counter
MOV R1,#0
POP PSW
POP ACC
MOV TCON,#01010000b ;re-start the timer
RETI ;return from interrupt
MASK: ; Subroutine that translates digits to 7seg
INC A
MOVC A,@A+PC
RET
DB 0C0H ; Mask for 0
DB 0F9H ; Mask for 1
DB 0A4H ; Mask for 2
DB 0B0H ; Mask for 3
DB 099H ; Mask for 4
DB 092H ; Mask for 5
DB 082H ; Mask for 6
DB 0F8H ; Mask for 7
DB 080H ; Mask for 8
DB 090H ; Mask for 9
; **** the following routine is NOT mine, i borrowed it from www.8052.com since i couldn't have been bothered to write my own.
; **** it's very well documented, so you should have no problems with it if you need to know how it works anyway
; **** it divides R1R0 by R3R2 in 16 bit and stores the result in R3/R2 and the rest in R1/R0
div16_16:
CLR C ;Clear carry initially
MOV R4,#00h ;Clear R4 working variable initially
MOV R5,#00h ;CLear R5 working variable initially
MOV B,#00h ;Clear B since B will count the number of left-shifted bits
div1:
INC B ;Increment counter for each left shift
MOV A,R2 ;Move the current divisor low byte into the accumulator
RLC A ;Shift low-byte left, rotate through carry to apply highest bit to high-byte
MOV R2,A ;Save the updated divisor low-byte
MOV A,R3 ;Move the current divisor high byte into the accumulator
RLC A ;Shift high-byte left high, rotating in carry from low-byte
MOV R3,A ;Save the updated divisor high-byte
JNC div1 ;Repeat until carry flag is set from high-byte
div2: ;Shift right the divisor
MOV A,R3 ;Move high-byte of divisor into accumulator
RRC A ;Rotate high-byte of divisor right and into carry
MOV R3,A ;Save updated value of high-byte of divisor
MOV A,R2 ;Move low-byte of divisor into accumulator
RRC A ;Rotate low-byte of divisor right, with carry from high-byte
MOV R2,A ;Save updated value of low-byte of divisor
CLR C ;Clear carry, we don't need it anymore
MOV 07h,R1 ;Make a safe copy of the dividend high-byte
MOV 06h,R0 ;Make a safe copy of the dividend low-byte
MOV A,R0 ;Move low-byte of dividend into accumulator
SUBB A,R2 ;Dividend - shifted divisor = result bit (no factor, only 0 or 1)
MOV R0,A ;Save updated dividend
MOV A,R1 ;Move high-byte of dividend into accumulator
SUBB A,R3 ;Subtract high-byte of divisor (all together 16-bit substraction)
MOV R1,A ;Save updated high-byte back in high-byte of divisor
JNC div3 ;If carry flag is NOT set, result is 1
MOV R1,07h ;Otherwise result is 0, save copy of divisor to undo subtraction
MOV R0,06h
div3:
CPL C ;Invert carry, so it can be directly copied into result
MOV A,R4
RLC A ;Shift carry flag into temporary result
MOV R4,A
MOV A,R5
RLC A
MOV R5,A
DJNZ B,div2 ;Now count backwards and repeat until "B" is zero
MOV R3,05h ;Move result to R3/R2
MOV R2,04h ;Move result to R3/R2
RET
END