; All timings / code are with 12Mhz crystal ; LTIME routine produces a delay of 1 x ( value loaded in DPTR ) Milliseconds ; Destroys B Register. LTIME: MOV A,DPL ; 6+DPTR*(15 + TIME0) micros ORL A,DPH ; 6+DPTR*1000 micros JZ SRET2 SETB C MOV A,DPL SUBB A,#0 MOV DPL,A MOV A,DPH SUBB A,#0 MOV DPH,A LCALL TIME0 SJMP LTIME SRET2: RET TIME0: MOV B, #0C4H ; (n1+1)*5 microsec=985 micros NOP TIME0L: NOP NOP NOP DJNZ B,TIME0L RET ;---------------------------------------------------------------------------------------------------------- ;Code for Byte wide output in memory mapped I/O . ADDR_HI EQU 0F0H DOT_1 EQU 00H ; Digital Out 1 mapping DOT_2 EQU 01H DAC1 EQU 02H ; DAC 1 input mapping DAC2 EQU 03H DIGOUT1: CPL A ;Bit to be low for LED to be ON MOV P2, #ADDR_HI MOV R0, #DOT_1 MOVX @R0,A RET DAC1: MOV P2, #ADDR_HI MOV R0, #DAC_1 MOVX @R0, A RET DIGOUT2: CPL A ;Bit to below for LED to be ON MOV P2, #ADDR_HI MOV R0, #DOT_2 MOVX @R0,A RET DAC2: MOV P2, #ADDR_HI MOV R0, #DAC_2 MOVX @R0, A RET ;----------------------------------------------------------------------------------------- ; COMPARE routine is called after loading R0 with address of 16bit #1 and R1 ; with address of 16bit #2. It compares and returns ACC with zero if #1 = #2 or ; with carry set if #1 < #2. It also sets the FLGZERO if result=0. COMPARE: CLR C CLR FLGZERO LCALL SUB16 MOV A, @R0 JNZ EXITCOM ; Even LSB is not zero.Return back INC R0 MOV A, @R0 ; If ACC is not zero exit. JNZ EXITCOM SETB FLGZERO ; If ACC is Zero set the relevant flag. EXITCOM: RET ;------------------------------------------------------------------------------------------- ; This routine is to check if the 16 bits pointed at by R0 ( LSB) are zero. BITS16_0: MOV A, @R0 JNZ EXIT16 ; Quit routine as even LSB <>0 INC R0 ORL A, @R0 EXIT16: RET ;-------------------------------------------------------------------------------------------- ; UTIL_INC24 increments the 3 byte wide Hex digit by 1 count everytime it is called. ; Call with R0 pointing to the LSB of the Hex digit. UTIL_INC24: NOP MOV A, @R0 ; Get Low Byte ADD A, #1 ; Add 1 MOV @R0, A ; Store Back INC R0 ; Point To next Byte MOV A, @R0 ; Get the Byte ADDC A, #0 ; Add CY, If Set MOV @R0, A ; Store Back INC R0 ; Point to High Byte MOV A, @R0 ; Get the byte ADDC A, #0 ; Add CY , if set MOV @R0, A ; Store back RET ;------------------------------------------------------------------------------------------------ ; COMP24 routine is called after loading R0 with address of 24bit #1 and R1 ; with address of 24bit #2. It compares and returns ACC with zero if #1 = #2 ; It also sets the FLGZERO if result=0. COMP24: CLR C CLR FLGZERO MOV A, @R0 SUBB A, @R1 JNZ EXITCOM ; Even Low bytes are not equal.Return back INC R0 INC R1 CLR C MOV A, @R0 SUBB A, @R1 JNZ EXITCOM ; Mid bytes are not equal, exit. INC R0 INC R1 CLR C MOV A, @R0 SUBB A, @R1 JNZ EXITCOM ; High bytes are not equal, exit. SETB FLGZERO ; ACC is Zero set the relevant flag. EXITCOM: RET ;---------------------------------------------------------------------------------------------------- ; INCMENT is for incrementing three bytes with BCD format. VAL3_DIS is used as the ; single digit addend. While VAL0-DIS to VAL2-DIS hold the BCD digit.. INCMENT: INC VAL3_DIS ; Start 3 byte wide BCD increment.... CLR C MOV A, VAL3_DIS ADD A, VAL2_DIS DA A MOV VAL2_DIS, A MOV A, #0 ADDC A, VAL1_DIS DA A MOV VAL1_DIS, A MOV A, #0 ADDC A, VAL0_DIS DA A MOV VAL0_DIS, A MOV VAL3_DIS, #0 RET ;---------------------------------------------------------------------------------------------------------- ;FNDCHNGE is for finding the increment in value of Counter registers since the last call. ; The PREBUF registers are subtracted from CURBUF regsiters and result returned in ; INCRMNT FNDCHNGE: MOV R0, #CURBUFL MOV R1, #PREBUFL CLR C MOV A, @R0 SUBB A, @R1 MOV INCRMNT, A INC R0 INC R1 MOV A, @R0 SUBB A, @R1 MOV INCRMNT+1, A CHECK: JNC EXIT1 ; Return zero in result if -ve . MOV INCRMNT, #0 MOV INCRMNT+1, #0 EXIT1: RET ;-------------------------------------------------------------------------------------------------- ;CHKPARAM checks if a 16bit value loaded in RAM is within specified low and ;high limits which are also loaded in buffers in RAM and then returns FLG1.3 ;set if the parameter is within limits. For this routine, ; DPTR should point to Low byte of parameter & will be incremented once. ; ACC, R0, R1 and FLG1.3 are used. ; Needs 6 buffers in RAM - PARAMLO, PARAMHI, LOLIML, LOLIMH, HILIML ; and HILIMH all loaded with required constants. CHKPARAM: CLR C CLR FLG1.3 ; Initialize PARVALID flag MOVX A,@DPTR ; Load the programmed values from EEPROM... MOV PARAMLO,A INC DPTR MOVX A,@DPTR MOV PARAMHI,A ; Parameters moved to RAM MOV R0, #PARAMLO MOV R1, #LOLIML LCALL rSUB16 ; Subtract LOLIMIT from PARAMETER.. JC RETERR ; Error. PARAMETER is less than LOLIMIT MOV R0, #HILIML MOV R1, #PARAMLO LCALL rSUB16 ; Subtract PARAMETER from HI LIMIT.. JC RETERR ; Error.PARAMETER is greater than HI LIMIT SETB FLG1.3 ; PARAMETER check passed RET RETERR: CLR FLG1.3 ; PARAMETER check failed RET ;---------------------------------------------------------------------------------------------------- ; To call MOV_BLK, DPTR to be loaded with the ROM address of data to be moved. ; It returns after moving 32Bytes and DPTR pointing to the moved location.Use ; this for moving the data in ROM to RAM. For this, ; DPTR to be loaded with required ROM address ; Uses ACC, R3 registers. ; Needs 4 buffers in RAM. DPLORG, DPHORG, DPLCPY, DPHCPY ; Uses 1 variable LCD_BUF MOV_BLK: MOV R3,#32 ; Move 32 bytes.ROM to RAM MOV DPHORG,DPH ; Save fetch address to RAM MOV DPLORG,DPL MOV DPTR, #RAM_ADDR MOV DPHCPY,DPH ; Save copy address to RAM MOV DPLCPY,DPL DPT_LUP: MOV DPH,DPHORG ; Dummy instructions to complete loop... MOV DPL,DPLORG MOV A,#0 MOVC A,@A+DPTR ; MOVC used here as original address INC DPTR ; ...is in ROM when running from 89C51 ! MOV DPHORG,DPH ; Save next fetch address to RAM MOV DPLORG,DPL MOV DPH,DPHCPY ; Point to copy location.. MOV DPL,DPLCPY MOVX @DPTR,A ; First byte transferred to copy location INC DPTR ; Pointer to next copy location. MOV DPHCPY,DPH ; Save next copy address to RAM MOV DPLCPY,DPL DJNZ R3,DPT_LUP ; Go transfer next byte.. MOV DPTR,#RAM_ADDR ; Ready to display from buffer in RAM RET ; 32 bytes moved... ; Note : If you have a derivative with two DPTRs then use them. The above routine will then be very small... ;--------------------------------------------------------------------------------------------------------------- ; These Maths routine have been adapted from a monitor program writtem by the genius Dr.M.Ohsmann ; and commented additionally for clarity. ; Sub routine for Addition ; Parameters: R0, R1 : pointers to 16 bit operands, ; Returns : R0 : pointer to result. The two 16 bit numbers @R0 and @R1 are added. ; R0 returns a pointer to the result. ADD16: NOP ; @R0 + @R1 goes to top MOV A,@R0 ADD A,@R1 MOV top,A INC R0 INC R1 MOV A,@R0 ADDC A,@R1 MOV top+1,A MOV R0,#top RET ; Sub routine for Subtraction. ; Parameters: R0, R1 : pointers to 16 bit operands, ; returns: R0 : pointer to result.The 16 bit @R1 is subtracted from the 16 bit @R0. ; R0 returns a pointer to the result. SUB16: NOP ; @R0 - @R1 goes to top CLR C MOV A,@R0 SUBB A,@R1 MOV top,A INC R0 INC R1 MOV A,@R0 SUBB A,@R1 MOV top+1,A MOV R0,#top RET ; Sub Routine for 16 bit multiplication. ; Parameters : R0, R1 Pointers to the multiplier and multiplicand ; Returns pointer of product LSB in R0. MUL16: NOP ; @R0 * @R1 goes to PROD MOV FAC1+0,@R0 MOV FAC2+0,@R1 INC R0 INC R1 MOV FAC1+1,@R0 MOV FAC2+1,@R1 LCALL MULPLY MOV R0,#PROD ; result pointer in R0 RET MULPLY: MOV A,FAC1+0 ; 16*16 multply -> 32 bit ( approx. 70 microsec ) MOV B,FAC2+0 MUL AB ; low * low byte MOV PROD+0,A MOV PROD+1,B MOV A,FAC1+1 MOV B,FAC2+1 MUL AB ; high * high byte MOV PROD+2,A MOV PROD+3,B MOV A,FAC1+1 ; mixed prod 1 MOV B,FAC2+0 MUL AB ADD A,PROD+1 ; add 3 byte wide MOV PROD+1,A MOV A,PROD+2 ADDC A,B MOV PROD+2,A MOV A,PROD+3 ADDC A,#0 MOV PROD+3,A MOV A,FAC1+0 ; mixed prod 2 MOV B,FAC2+1 MUL AB ADD A,PROD+1 ; add 3 byte wide MOV PROD+1,A MOV A,PROD+2 ADDC A,B MOV PROD+2,A MOV A,PROD+3 ADDC A,#0 MOV PROD+3,A RET ; Sub routine for Division. ; Parameters: R0 : pointer to a 32 bit number ; R1 : pointer to 16 bit operands, ; Returns: R0 : pointer to 16 bit result ; R1 : pointer to 16 bit remainder ; The 32-bit number @R0 is divided by the 16-bit divisor @R1, without a ; sign. R0 returns a pointer to the 16 bit quotient. R1 returns a pointer ; to the 16 bit remainder. ; The quotient must be < 65536 for this routine to operate correctly. rDIV16: MOV PROD+0,@R0 INC R0 MOV PROD+1,@R0 INC R0 MOV PROD+2,@R0 INC R0 MOV PROD+3,@R0 MOV DIVI,@R1 INC R1 MOV DIVI+1,@R1 LCALL DIV16 MOV R0,#QUOT ; Quotient pointer MOV R1,#PROD+2 ; Remainder pointer RET ; 32 / 16 bit division routine DIV16: MOV PROD+4,#00H ; Clear extension approx. 1000 microsec MOV DIVCNT,#16 ; 15 normal steps, last step: remainder ! DIVLP: LCALL SHR5 LCALL TRYSUB ; Try and perform subtraction, results in CY LCALL SHLQ ; Shift CY into QUOTIENT DJNZ DIVCNT,DIVLP RET ; TRYSUB: NOP CLR C ; No borrow MOV A,PROD+2 SUBB A,DIVI+0 MOV A,PROD+3 SUBB A,DIVI+1 MOV A,PROD+4 ; Is ACC extension SUBB A,#0 JNC SUBIT RET SUBIT: MOV A,PROD+2 ; Now really subtract, we have no borrow from before SUBB A,DIVI+0 MOV PROD+2,A MOV A,PROD+3 SUBB A,DIVI+1 MOV PROD+3,A MOV A,PROD+4 ; Is accu extension SUBB A,#0 MOV PROD+4,A RET ; SHLQ: CPL C MOV A,QUOT+0 ; Use carry from before and shift into quotient RLC A MOV QUOT+0,A MOV A, QUOT+1 RLC A MOV QUOT+1,A RET ; SHR5: NOP CLR C ; Shift 5 bytes = product+ACC 17 bit extension MOV A,PROD+0 RLC A MOV PROD+0,A MOV A,PROD+1 RLC A MOV PROD+1,A MOV A,PROD+2 RLC A MOV PROD+2,A MOV A,PROD+3 RLC A MOV PROD+3,A MOV A,PROD+4 RLC A MOV PROD+4,A RET rABS16: INC R0 ; Is ABS set CY if negative MOV A,@R0 JNB ACC.7,noneg1 DEC R0 SJMP rNEG16 noneg1: CLR C RET ; rNEG16: MOV A,@R0 ; Set negative @ R0 XRL A,#0FFH ADD A,#1 MOV @R0,A INC R0 MOV A,@R0 XRL A,#0FFH ADDC A,#0 MOV @R0,A SETB C RET ; ;----------------------------------------------------------------------------------------- ; B2HEX08 converts the BCDvalue in accumulator to HEX B2HEX08: PUSH ACC ; Save value ANL A, #0F0H ; Keep high bits SWAP A ; Get into low nibble MOV B, #10 ; 10 decimal MUL AB ; Multiply MOV B, A ; Store In B POP ACC ; Recover BCD value ANL A, #00FH ; Keep low nybble ADD A, B ; Add In high RET ; Return with Hex equivalent of BCD ;----------------------------------------------------------------------------------------- ; B2HEX20 converts the 20 bit BCD number to HEX . It is assumed that the BCD numbers are in ; packed form like " 09 87 65 " in three bytes. Call with R0 loaded with the address of byte having 09. ; Collect results from ANSWER+0 ( lsb) and ANSWER+1 (msb). Uses R7 register and B register. Even though ; multiplication result is 32bit, addition is only 16 bit and hence can convert only upto 65535 or FFFF. B2HEX20: MOV A, R0 INC A INC A MOV R7, A MOV R0, A MOV A, @R0 ANL A, #0F0H SWAP A MOV B, #10 MUL AB MOV B, A MOV A, R7 MOV R0, A MOV A, @R0 ANL A, #0FH ADD A, B MOV ANSWER, A ; Units + 10s place converted DEC R7 MOV A, R7 MOV R0, A MOV A, @R0 ANL A, #0FH MOV CNVBUF, A MOV CNVBUF+1, #0 MOV CNVBUF+2, #100 MOV CNVBUF+3, #0 MOV R0, #CNVBUF MOV R1, #CNVBUF+2 LCALL MUL16 MOV ANSWER+1, #0 MOV R1, # ANSWER LCALL ADD16 MOV ANSWER, @R0 INC R0 MOV ANSWER+1, @R0 ; 100s place converted MOV A, R7 MOV R0, A MOV A, @R0 ANL A, #0F0H SWAP A MOV CNVBUF, A MOV CNVBUF+1, #0 MOV CNVBUF+2, #0E8H MOV CNVBUF+3, #3H MOV R0, #CNVBUF MOV R1, #CNVBUF+2 LCALL MUL16 MOV R1, #ANSWER LCALL ADD16 MOV ANSWER, @R0 INC R0 MOV ANSWER+1, @R0 ; 1000s place converted DEC R7 MOV A, R7 MOV R0, A MOV A, @R0 ANL A, #0FH MOV CNVBUF, A MOV CNVBUF+1, #0 MOV CNVBUF+2, #10H MOV CNVBUF+3, #27H MOV R0, #CNVBUF MOV R1, #CNVBUF+2 LCALL MUL16 MOV R1, # ANSWER LCALL ADD16 MOV ANSWER, @R0 INC R0 MOV ANSWER+1, @R0 ; 10,000s place converted. But only upto FFFF as ; addition is only 16Bit RET ;-------------------------------------------------------------------------------------------------------- ; H16BCD is for converting a 16 bit hex digit to a packed BCD digit. Since only two ; bytes are reserved for BCD store max Hex digit is only 270FH ; Call with R0 holding the address of HEX bytes to be converted. Returns with result ; stored in BCDSTOR1 ( Thousands+Hundreds ) and BCDSTOR2 ( Tens+Units ) H16BCD: LCALL dCNVa ; Clear/load conversion buffers LCALL dCNVb ; Convert 2 bytes LCALL dCNVb MOV R1,#dACCU+dBYTS ; Pointer to converted bytes LCALL BCD_STOR RET BCD_STOR: MOV R6, #dBYTS ; Store 2 BCD bytes( MSB first) starting @R1 MOV R0,#BCDSTOR1 ; Point to the BCD result area STORL: DEC R1 MOV A,@R1 MOV @R0,A ; Store BCD value INC R0 ; Increment result pointer DJNZ R6,STORL RET dCNVa: MOV A,#0 MOV dACCU+0,A ; Init DEC conversion dACCU:=0 ; dREG1:=1 MOV dACCU+1,A MOV dREG1+0,#1 MOV dREG1+1,A RET dCNVb: MOV A,@R0 ; Convert Byte at R0 , & inc R0 INC R0 DCNVb1: MOV R2,A ; Convert Byte ACC MOV R6,#8 ; Convert R2 using dACCU and dREG1 dCNV1: MOV A,R2 ; Get bit from R2 RRC A MOV R2,A JNC dCNV2 LCALL ADD1 ; If CY add dREG1 to dACCU using BCD dCNV2: LCALL DOUBL1 ; dREG1:=2*dREG1 DJNZ R6,dCNV1 ; Loop until all 8 bits done RET DOUBL1: MOV A,dREG1+0 ; dREG1:=2*dREG1 , BCD coded 5 bytes ADD A,dREG1+0 DA A MOV dREG1+0,A MOV A,dREG1+1 ADDC A,dREG1+1 DA A MOV dREG1+1,A RET ADD1: MOV A,dACCU+0 ; dACCU:=dACCU+dREG1 , bcd coded 5 bytes ADD A,dREG1+0 DA A MOV dACCU+0,A MOV A,dACCU+1 ADDC A,dREG1+1 DA A MOV dACCU+1,A RET ;------------------------------------------------------------------------------------------------------ ; H24BCD is for converting a 24 bit hex digit to a packed BCD digit. Since only three ; bytes are reserved for BCD store max Hex digit is only F423FH ; Call with R0 holding the address of HEX bytes to be converted. Returns with result ; stored in BCDSTOR1 ( Thousands+Hundreds ) and BCDSTOR2 ( Tens+Units ) ; and BCDSTOR3 ( One tenths and One hundredths ) H24CD: LCALL dCNVa ; Clear/load conversion buffers LCALL dCNVb ; Convert 3 bytes LCALL dCNVb LCALL dCNVb MOV R1,#dACCU+dBYTS ; Pointer to converted bytes LCALL BCD_STOR RET BCD_STOR: MOV R6, #dBYTS ; Store 3 BCD bytes( MSB first) starting @R1 MOV R0,#BCDSTOR1 ; Point to the BCD result area STORL: DEC R1 MOV A,@R1 MOV @R0,A ; Store BCD value INC R0 ; Increment result pointer DJNZ R6,STORL RET dCNVa: MOV A,#0 MOV dACCU+0,A ; Init DEC conversion dACCU:=0 ; dREG1:=1 MOV dACCU+1,A MOV dACCU+2,A MOV dREG1+0,#1 MOV dREG1+1,A MOV dREG1+2,A RET dCNVb: MOV A,@R0 ; Convert Byte at R0 , & inc R0 INC R0 DCNVb1: MOV R2,A ; Convert Byte ACC MOV R6,#8 ; Convert R2 using dACCU and dREG1 dCNV1: MOV A,R2 ; Get bit from R2 RRC A MOV R2,A JNC dCNV2 LCALL ADD1 ; If CY add dREG1 to dACCU using BCD dCNV2: LCALL DOUBL1 ; dREG1:=2*dREG1 DJNZ R6,dCNV1 ; Loop until all 8 bits done RET DOUBL1: MOV A,dREG1+0 ; dREG1:=2*dREG1 , BCD coded 3 bytes ADD A,dREG1+0 DA A MOV dREG1+0,A MOV A,dREG1+1 ADDC A,dREG1+1 DA A MOV dREG1+1,A MOV A,dREG1+2 ADDC A,dREG1+2 DA A MOV dREG1+2,A RET ADD1: MOV A,dACCU+0 ; dACCU:=dACCU+dREG1 , bcd coded 3 bytes ADD A,dREG1+0 DA A MOV dACCU+0,A MOV A,dACCU+1 ADDC A,dREG1+1 DA A MOV dACCU+1,A MOV A,dACCU+2 ADDC A,dREG1+2 DA A MOV dACCU+2,A RET ;------------------------------------------------------------------------------------------------- ; R0 should point to the LSB byte of the 32bit number to be converted.BCD ; result is stored starting from BCD0(MSB) to BCD4(LSB). H32BCD: LCALL dCNVa ; Clear/load conversion buffers LCALL dCNVb ; Convert first byte LCALL dCNVb LCALL dCNVb LCALL dCNVb ; Convert fourth byte MOV R1,#dACCU+dBYTS ; Pointer to converted bytes+1 MOV R6,#dBYTS ; Counter for 5 BCD coded bytes LCALL BCD_STOR RET BCD_STOR: NOP ; Store 5 BCD bytes in decreasing order @R1 MOV R0,#BCD0 ; Point to the BCD result area STORL: DEC R1 MOV A,@R1 MOV @R0,A ; Store BCD value INC R0 ; Increment result pointer DJNZ R6,STORL RET dCNVa: MOV A,#0 MOV dACCU+0,A ; Init DEC conversion dACCU:=0 ; dREG1:=1 MOV dACCU+1,A MOV dACCU+2,A MOV dACCU+3,A MOV dACCU+4,A MOV dREG1+0,#1 MOV dREG1+1,A MOV dREG1+2,A MOV dREG1+3,A MOV dREG1+4,A RET dCNVb: MOV A,@R0 ; Convert Byte at R0 , & inc R0 INC R0 DCNVb1: MOV R2,A ; Convert Byte ACC MOV R6,#8 ; Convert R2 using dACCU and dREG1 dCNV1: MOV A,R2 ; Get bit from R2 RRC A MOV R2,A JNC dCNV2 LCALL ADD1 ; If CY add dREG1 to dACCU using BCD dCNV2: LCALL DOUBL1 ; dREG1:=2*dREG1 DJNZ R6,dCNV1 ; Loop until all 8 bits done RET DOUBL1: MOV A,dREG1+0 ; dREG1:=2*dREG1 , BCD coded 5 bytes ADD A,dREG1+0 DA A MOV dREG1+0,A MOV A,dREG1+1 ADDC A,dREG1+1 DA A MOV dREG1+1,A MOV A,dREG1+2 ADDC A,dREG1+2 DA A MOV dREG1+2,A MOV A,dREG1+3 ADDC A,dREG1+3 DA A MOV dREG1+3,A MOV A,dREG1+4 ADDC A,dREG1+4 DA A MOV dREG1+4,A RET ADD1: MOV A,dACCU+0 ; dACCU:=dACCU+dREG1 , bcd coded 5 bytes ADD A,dREG1+0 DA A MOV dACCU+0,A MOV A,dACCU+1 ADDC A,dREG1+1 DA A MOV dACCU+1,A MOV A,dACCU+2 ADDC A,dREG1+2 DA A MOV dACCU+2,A MOV A,dACCU+3 ADDC A,dREG1+3 DA A MOV dACCU+3,A MOV A,dACCU+4 ADDC A,dREG1+4 DA A MOV dACCU+4,A RET ;----------------------------------------------------------------------------------------------------- ; Call BCD7SEG after loading NIBBLE with the BCD digit to be displayed. Returns with ACC holding the 7Segdata BCD7SEG: MOV DPTR, #NUMSEG ; Point to Segment LookUpTable SJMP ZERO BCD7DP: MOV DPTR, #DPSEG ; LUT with decimal point.. ZERO: MOV A, NIBBLE JZ OVER1 ; Check if character is zero LOOP: INC DPTR ; Increment pointer till character=0 DJNZ NIBBLE, LOOP NOP ; DPTR now has address for required 7 seg. OVER1: MOV A, #0 ; Load ACC with required 7 seg information MOVC A, @A+DPTR CPL A ; Segment drive is active low.... RET NUMSEG: DB 3FH, 06H, 5BH, 4FH, 66H, 6DH, 7DH, 07H, 7FH, 6FH DPSEG: DB 0BFH, 86H, 0DBH, 0CFH, 0E6H, 0EDH, 0FDH, 87H, 0FFH, 0EFH ; ( Check this for Active Low type and use) ;------------------------------------------------------------------------------------------------------- ; Following routines pertain to the I2C interface : ; BitDly; SCLHigh; SendStop; SendByte; GoMaster; SendData; RcvByte; RcvData; ; I2CSave; I2CRcall. ; BitDly - insures minimum high and low clock times on I2C bus. ; This routine must be tuned for the actual oscilator frequency used, shown ; here tuned for a 12MHz clock. Note that the CALL instruction that invokes ; BitDly already uses 2 machine cycles. BitDly: NOP ; NOPs to delay 5 microseconds (minus 4 ; machine cycles for CALL and RET). RET ; SCLHigh - sends SCL pin high and waits for any clock stretching peripherals. SCLHigh: SETB SCLPin ;Set SCL from our end. JNB SCLPin,$ ;Wait for pin to actually go high. RET ; SendStop - sends an I2C stop, releasing the bus. SendStop: CLR SDAPin ;Get SDA ready for stop. ACALL SCLHigh ;Set clock for stop. ACALL BitDly SETB SDAPin ;Send I2C stop. ACALL BitDly CLR I2CBusy ;Clear I2C busy status. RET ;Bus should now be released. ; SendByte - sends one byte of data to an I2C slave device. ; Enter with: ACC = data byte to be sent. SendByte: MOV BitCnt,#8 ;Set bit count. SBLoop: RLC A ;Send one data bit. MOV SDAPin,C ;Put data bit on pin. ACALL SCLHigh ;Send clock. ACALL BitDly CLR SCLPin ACALL BitDly DJNZ BitCnt, SBLoop ;Repeat until all bits sent. SETB SDAPin ;Release data line for acknowledge. ACALL SCLHigh ;Send clock for acknowledge. ACALL BitDly JNB SDAPin,SBEX ;Check for valid acknowledge bit. SETB NoAck ;Set status for no acknowledge. SBEX: CLR SCLPin ;Finish acknowledge bit. ACALL BitDly RET ; GoMaster - sends an I2C start and slave address. ; Enter with: SlvAdr = slave address. GoMaster: SETB I2CBusy ;Indicate that I2C frame is in progress. CLR NoAck ;Clear error status flags. CLR BusFault JNB SCLPin,Fault ;Check for bus clear. JNB SDAPin,Fault CLR SDAPin ;Begin I2C start. ACALL BitDly CLR SCLPin ACALL BitDly ;Complete I2C start. MOV A, SlvAdr ;Get slave address. ACALL SendByte ;Send slave address. RET Fault: SETB BusFault ;Set fault status LJMP ERROR3 ; SendData - sends one or more bytes of data to an I2C slave device. ; Enter with: ; ByteCnt = count of bytes to be sent. ; SlvAdr = slave address. ; @R0 = data to be sent (the first data byte will be the ; subaddress, if the I2C device expects one). SendData: ACALL GoMaster ;Acquire bus and send slave address. JB NoAck,SFLT ;Check for slave not responding. SDLoop: MOV A,@R0 ;Get data byte from buffer. ACALL SendByte ;Send next data byte. INC R0 ;Advance buffer pointer. JB NoAck,SDEX ;Check for slave not responding. DJNZ ByteCnt,SDLoop ;All bytes sent? SJMP SDEX SFLT: LJMP ERROR1 SDEX: ACALL SendStop ;Done, send an I2C stop. RET ;RcvByte - receives one byte of data from an I2C slave device. ; Returns: ; ACC = data byte received. RcvByte: MOV BitCnt,#8 ;Set bit count. RBLoop: ACALL SCLHigh ;Read one data bit. ACALL BitDly MOV C,SDAPin ;Get data bit from pin. RLC A ;Rotate bit into result byte. CLR SCLPin ACALL BitDly DJNZ BitCnt,RBLoop ;Repeat until all bits received. PUSH ACC ;Save accumulator MOV A,ByteCnt CJNE A,#1,RBAck ;Check for last byte of frame. SETB SDAPin ;Send no acknowledge on last byte. SJMP RBAClk RBAck: CLR SDAPin ;Send acknowledge bit. RBAClk: ACALL SCLHigh ;Send acknowledge clock. POP ACC ;Restore accumulator ACALL BitDly CLR SCLPin SETB SDAPin ;Clear acknowledge bit. ACALL BitDly RET ;RcvData - receives sends one or more bytes of data from an I2C slave device. ; Enter with: ; ByteCnt = count of bytes to be sent. ; SlvAdr = slave address. ; Returns: ; @R0 = data received. ; Note: to receive with a subaddress, use SendData to set the subaddress ; first (no provision for repeated start). RcvData: INC SlvAdr ;Set for READ of slave. ACALL GoMaster ;Acquire bus and send slave address. JB NoAck, RFLT ;Check for slave not responding. RDLoop: ACALL RcvByte ;Recieve next data byte. MOV @R0,A ;Save data byte in buffer. INC R0 ;Advance buffer pointer. DJNZ ByteCnt, RDLoop ;Repeat untill all bytes received. SJMP RDEX RFLT: LJMP ERROR2 RDEX: ACALL SendStop ;Done, send an I2C stop. RET ;I2CSave is to save two bytes from IRAM to NV Ram in PCF8583 ;Call with PRGVAL0 & PRGVAL1 with bytes to save I2CSave: MOV ByteCnt, #2 MOV SlvAdr, #NVRam MOV XmtDat+0, #0 MOV XmtDat+1, #80H ;Control word for halting count. MOV R0, #XmtDat CLR IE.7 ; Stop interrupt LCALL SendData ; Initialize PCF8583 MOV ByteCnt, #3 MOV SlvAdr, #NVRam MOV XmtDat+0, #10H ; Address to save.... MOV XmtDat+1, PRGVAL0 MOV XmtDat+2, PRGVAL1 MOV R0, #XmtDat LCALL SendData ; Save the data SETB IE.7 ; Resume interrupt RET ;I2CRcall is to read the saved value and load it to the IRAM locations PRGVAL0 & ; PRGVAL1. I2CRcall: MOV ByteCnt, #1 MOV SlvAdr, #NVRam MOV XmtDat, #10H MOV R0, #XmtDat CLR IE.7 ; Dont interrupt please LCALL SendData ; Write the recall address to PCF8583 MOV ByteCnt, #2 MOV SlvAdr, #NVRam MOV R0, #RcvDat LCALL RcvData MOV PRGVAL0, RcvDat+0 MOV PRGVAL1, RcvDat+1 SETB IE.7 ; OK to interrupt now. RET ;----------------------------------------------------------------------------------------------