;================================================================================================== ; PCF8584 I2C CLOCK DRIVER ;================================================================================================== ; PCF_BASE .EQU PCFBASE PCF_ID .EQU 0AAH ; PCF_RS0 .EQU PCF_BASE PCF_RS1 .EQU PCF_BASE+1 PCF_OWN .EQU (PCF_ID >> 1) ; PCF'S ADDRESS IN SLAVE MODE ; ;T4LC512D .EQU 10100000B ; DEVICE IDENTIFIER ;T4LC512A1 .EQU 00000000B ; DEVICE ADDRESS ;T4LC512A2 .EQU 00001110B ; DEVICE ADDRESS ;T4LC512A3 .EQU 00000010B ; DEVICE ADDRESS ;T4LC512W .EQU 00000000B ; DEVICE WRITE ;T4LC512R .EQU 00000001B ; DEVICE READ ; ;I2CDEV1W .EQU (T4LC512D+T4LC512A1+T4LC512W) ;I2CDEV1R .EQU (T4LC512D+T4LC512A1+T4LC512R) ; ;I2CDEV2W .EQU (T4LC512D+T4LC512A2+T4LC512W) ;I2CDEV2R .EQU (T4LC512D+T4LC512A2+T4LC512R) ; ;I2CDEV3W .EQU (T4LC512D+T4LC512A3+T4LC512W) ;I2CDEV3R .EQU (T4LC512D+T4LC512A3+T4LC512R) ; ; CONTROL REGISTER BITS ; PCF_PIN .EQU 10000000B PCF_ES0 .EQU 01000000B PCF_ES1 .EQU 00100000B PCF_ES2 .EQU 00010000B PCF_EN1 .EQU 00001000B PCF_STA .EQU 00000100B PCF_STO .EQU 00000010B PCF_ACK .EQU 00000001B ; PCF_START_ .EQU (PCF_PIN | PCF_ES0 | PCF_STA | PCF_ACK) PCF_STOP_ .EQU (PCF_PIN | PCF_ES0 | PCF_STO | PCF_ACK) PCF_REPSTART_ .EQU (PCF_ES0 | PCF_STA | PCF_ACK) PCF_IDLE_ .EQU (PCF_PIN | PCF_ES0 | PCF_ACK) ; ; STATUS REGISTER BITS ; ;PCF_PIN .EQU 10000000B PCF_INI .EQU 01000000B ; 1 if not initialized PCF_STS .EQU 00100000B PCF_BER .EQU 00010000B PCF_AD0 .EQU 00001000B PCF_LRB .EQU 00001000B PCF_AAS .EQU 00000100B PCF_LAB .EQU 00000010B PCF_BB .EQU 00000001B ; ; THE PCF8584 TARGETS A TOP I2C CLOCK SPEED OF 90KHZ AND SUPPORTS DIVIDERS FOR ; 3, 4.43, 6, 8 AND 12MHZ TO ACHEIVE THIS. ; ; +--------------------------------------------------------------------------------------------+ ; | div/clk | 2MHz | 4MHz | 6MHz | 7.38Mhz | 10MHz | 12MHz | 16MHz | 18.432Mhz | 20MHz | ; +----------------------------------------------------------------------------------+---------+ ; | 3MHz | 60Khz | 120Khz | | | | | | | | ; | 4.43MHz | | 81Khz | | | | | | | | ; | 6MHz | | | 90Khz | 110Khz | | | | | | ; | 8MHz | | | | 83Khz | 112Khz | | | | | ; | 12MHz | | | | | | 90Khz | 120Khz | 138Khz | 150Khz | ; +----------------------------------------------------------------------------------+---------+ ; ; CLOCK CHIP FREQUENCIES ; PCF_CLK3 .EQU 000H PCF_CLK443 .EQU 010H PCF_CLK6 .EQU 014H PCF_CLK8 .EQU 018H PCF_CLK12 .EQU 01CH ; ; TRANSMISSION FREQUENCIES ; PCF_TRNS90 .EQU 000H ; 90 kHz */ PCF_TRNS45 .EQU 001H ; 45 kHz */ PCF_TRNS11 .EQU 002H ; 11 kHz */ PCF_TRNS15 .EQU 003H ; 1.5 kHz */ ; ; BELOW VARIABLES CONTROL PCF CLOCK DIVISOR PROGRAMMING ; HARD-CODED FOR NOW ; PCF_CLK .EQU PCF_CLK12 PCF_TRNS .EQU PCF_TRNS90 ; ; TIMEOUT AND DELAY VALUES (ARBITRARY) ; PCF_PINTO .EQU 65000 PCF_ACKTO .EQU 65000 PCF_BBTO .EQU 65000 PCF_LABDLY .EQU 65000 ; .ECHO "PCF: IO=" .ECHO PCF_BASE .ECHO "\n" ; ; DATA PORT REGISTERS ; PCF_INIT: CALL NEWLINE ; Formatting PRTS("PCF: IO=0x$") LD A, PCF_BASE CALL PRTHEXBYTE CALL PC_SPACE CALL PCF_INITDEV ;CALL NEWLINE RET ; ; LINUX DRIVER BASED CODE ; ; I2C_INB = IN A,(PCF_RS0) ; I2C_OUTB = LD A,* | OUT (PCF_RS0),A ; SET_PCF -= LD A,* | OUT (PCF_RS1),A ; GET_PCF = IN A,(PCF_RS1) ; ;----------------------------------------------------------------------------- PCF_START: LD A,PCF_START_ OUT (PCF_RS1),A RET ; ;----------------------------------------------------------------------------- PCF_REPSTART: LD A,PCF_REPSTART_ OUT (PCF_RS1),A RET ; ;----------------------------------------------------------------------------- PCF_STOP: LD A,PCF_STOP_ OUT (PCF_RS1),A RET ; ;----------------------------------------------------------------------------- ; PCF_INITDEV: LD A,PCF_PIN ; S1=80H: S0 SELECTED, SERIAL OUT (PCF_RS1),A ; INTERFACE OFF NOP IN A,(PCF_RS1) ; CHECK TO SEE S1 NOW USED AS R/W ;CALL PC_SPACE ;CALL PRTHEXBYTE AND 07FH ; CTRL. PCF8584 DOES THAT WHEN ESO JR NZ,PCF_FAIL ; IS ZERO ; LD A,PCF_OWN ; LOAD OWN ADDRESS IN S0, OUT (PCF_RS0),A ; EFFECTIVE ADDRESS IS (OWN <<1) NOP IN A,(PCF_RS0) ; CHECK IT IS REALLY WRITTEN ;CALL PC_SPACE ;CALL PRTHEXBYTE CP PCF_OWN JP NZ,PCF_SETERR ; LD A,+(PCF_PIN | PCF_ES1) ; S1=0A0H OUT (PCF_RS1),A ; NEXT BYTE IN S2 NOP IN A,(PCF_RS1) ;CALL PC_SPACE ;CALL PRTHEXBYTE AND 07FH CP PCF_ES1 JP NZ,PCF_REGERR ; LD A,PCF_CLK | PCF_TRNS ; LOAD CLOCK REGISTER S2 OUT (PCF_RS0),A NOP IN A,(PCF_RS0) ; CHECK IT'S REALLY WRITTEN, ONLY ;CALL PC_SPACE ;CALL PRTHEXBYTE AND 1FH ; THE LOWER 5 BITS MATTER CP PCF_CLK JP NZ,PCF_CLKERR ; LD A,PCF_IDLE_ OUT (PCF_RS1),A NOP IN A,(PCF_RS1) ;CALL PC_SPACE ;CALL PRTHEXBYTE CP +(PCF_PIN | PCF_BB) JP NZ,PCF_IDLERR ; XOR A RET ; PCF_FAIL: CALL PCF_INIERR LD A,ERR_NOHW LD (PCF_FAIL_FLAG),A RET ; PCF_FAIL_FLAG: .DB 0 ; ;----------------------------------------------------------------------------- PCF_HANDLE_LAB: ; LD A,PCF_PIN OUT (PCF_RS1),A LD A,PCF_ES0 OUT (PCF_RS1),A ; LD HL,PCF_LABDLY PCF_LABLP: LD A,H OR L DEC HL JR NZ,PCF_LABLP ; IN A,(PCF_RS1) RET ; ;----------------------------------------------------------------------------- ; ; RETURN A=00/Z IF SUCCESSFULL ; RETURN A=FF/NZ IF TIMEOUT ; RETURN A=01/NZ IF LOST ARBITRATION ; PCF_STATUS HOLDS LAST PCF STATUS ; PCF_WAIT_FOR_PIN: PUSH HL LD HL,PCF_PINTO ; SET TIMEOUT VALUE PCF_WFP0: IN A,(PCF_RS1) ; GET BUS LD (PCF_STATUS),A ; STATUS LD B,A DEC HL ; HAVE WE LD A,H ; TIMED OUT OR L JR Z,PCF_WFP1 ; YES WE HAVE, GO ACTION IT LD A,B ; AND PCF_PIN ; IS TRANSMISSION COMPLETE? JR NZ,PCF_WFP0 ; KEEP ASKING IF NOT OR POP HL ; YES COMPLETE (PIN=0) RETURN WITH ZERO RET PCF_WFP1: LD A,B ; DID WE LOSE ARBITRATION? AND PCF_LAB ; IF A=0 THEN NO CPL JR NZ,PCF_WFP2 ; NO CALL PCF_HANDLE_LAB ; YES GO HANDLE IT LD (PCF_STATUS),A XOR A ; RETURN NZ, A=01H INC A PCF_WFP2: POP HL ; RET NZ, A=FF IF TIMEOUT RET ; PCF_STATUS .DB 00H ;-------------------------------------------------------------------------------- ; ; RETURN NZ/FF IF TIMEOUT ERROR ; RETURN NZ/01 IF FAILED TO RECEIVE ACKNOWLEDGE ; RETURN Z/00 IF RECEIVED ACKNOWLEDGE ; PCF_WAIT_FOR_ACK: PUSH HL LD HL,PCF_ACKTO ; PCF_WFA0: IN A,(PCF_RS1) ; READ PIN LD (PCF_STATUS),A ; STATUS LD B,A ; DEC HL ; SEE IF WE HAVE TIMED LD A,H ; OUT WAITING FOR PIN OR L ; EXIT IF JR Z,PCF_WFA1 ; WE HAVE ; LD A,B ; OTHERWISE KEEP LOOPING AND PCF_PIN ; UNTIL WE GET PIN JR NZ,PCF_WFA0 ; OR TIMEOUT ; LD A,B ; WE GOT PIN SO NOW AND PCF_LRB ; CHECK WE HAVE LD A,1 JR Z,PCF_WFA2 ; RECEIVED ACKNOWLEDGE XOR A JR PCF_WFA2 PCF_WFA1: CPL ; TIMOUT ERROR PCF_WFA2: POP HL ; EXIT WITH NZ = FF RET ; ;-------------------------------------------------------------------------------- ; ; HL POINTS TO DATA ; DE = COUNT ; A = 0 LAST A=1 NOT LAST ; ; ;PCF_READBYTES: ; NOT FUNCTIONAL YET LD (PCF_LBF),A ; SAVE LAST BYTE FLAG ; INC DE ; INCREMENT NUMBER OF BYTES TO READ BY ONE -- DUMMY READ BYTE LD BC,0 ; SET BYTE COUNTER ; PCF_RBL:PUSH BC CALL PCF_WAIT_FOR_PIN ; DO WE HAVE THE BUS? POP BC JR Z,PCF_RB1 ; YES CP 01H JR Z,PCF_RB3 ; NO - LOST ARBITRATION JR PCF_RB2 ; NO - TIMEOUT ; PCF_RB1: LD A,(PCF_STATUS) AND PCF_LRB ; IS THIS THE SECOND TO LAST BYTE TO GO? PUSH DE ; SAVE COUNT DEC DE ; COUNT (DE) = NUMBER OF BYTES TO READ LESS 1 EX DE,HL ; SAVE POINTER, PUT COUNT IN DE XOR A ; CLEAR CARRY FLAG SBC HL,BC ; DOES BYTE COUNTER = HL (NUMBER OF BYTES TO READ LESS 1) EX DE,HL ; RESTORE POINTER POP DE ; RESTORE COUNT ; Z = YES IT IS ; NZ = NO IT ISN'T JR NZ,PCF_RB4 ; PCF_RB4:LD A,B ; IF FIRST READ DO A DUMMY OR C ; READ OTHERWISE READ AND SAVE JR NZ,PCF_RB5 IN A,(PCF_RS0) ; DUMMY READ JR PCF_RB6 PCF_RB5:IN A,(PCF_RS0) ; READ AND SAVE LD (HL),A ; PCF_RB6: ; HAVE WE DONE ALL? PUSH DE ; SAVE COUNT EX DE,HL ; SAVE POINTER, PUT COUNT IN DE XOR A ; CLEAR CARRY FLAG SBC HL,BC ; DOES BYTE COUNTER = HL (NUMBER OF BYTES TO READ) EX DE,HL ; RESTORE POINTER POP DE ; RESTORE COUNT ; INC HL ; BUFFER POINTER INC BC ; COUNT ; JR NZ,PCF_RBL ; REPEAT UNTIL COUNTS MATCH RET ; PCF_RB2: ; TIMEOUT CALL PCF_STOP CALL PCF_TOERR RET ; PCF_RB3: ; LOST ARBITRATION CALL PCF_ARBERR RET ; PCF_LBF: .DB 0 ; LAST BYTE FLAG ; ;----------------------------------------------------------------------------- ; READ ONE BYTE FROM I2C ; RETURNS DATA IN A ; Z FLAG SET IS ACKNOWLEDGE RECEIVED (CORRECT OPERATION) ; PCF_READI2C: IN A,(PCF_RS1) ; READ S1 REGISTER BIT 7,A ; CHECK PIN STATUS JP NZ,PCF_READI2C BIT 3,A ; CHECK LRB=0 JP NZ,PCF_RDERR IN A,(PCF_RS0) ; GET DATA RET ;----------------------------------------------------------------------------- ; ; POLL THE BUS BUSY BIT TO DETERMINE IF BUS IS FREE. ; RETURN WITH A=00H/Z STATUS IF BUS IS FREE ; RETURN WITH A=FFH/NZ STATUS IF BUS IS BUSY ; ; AFTER RESET THE BUS BUSY BIT WILL BE SET TO 1 I.E. NOT BUSY ; PCF_WAIT_FOR_BB: LD HL,PCF_BBTO PCF_WFBB0: IN A,(PCF_RS1) AND PCF_BB RET Z ; BUS IS FREE RETURN ZERO DEC HL LD A,H OR L JR NZ,PCF_WFBB0 ; REPEAT IF NOT TIMED OUT CPL ; RET NZ IF TIMEOUT RET ; ;----------------------------------------------------------------------------- ; DISPLAY ERROR MESSAGES ; PCF_RDERR: PUSH HL LD HL,PCF_RDFAIL JR PCF_PRTERR ; PCF_INIERR: PUSH HL LD HL,PCF_NOPCF JR PCF_PRTERR ; PCF_SETERR: PUSH HL LD HL,PCF_WRTFAIL JR PCF_PRTERR ; PCF_REGERR: PUSH HL LD HL,PCF_REGFAIL JR PCF_PRTERR ; PCF_CLKERR: PUSH HL LD HL,PCF_CLKFAIL JR PCF_PRTERR ; PCF_IDLERR: PUSH HL LD HL,PCF_IDLFAIL JR PCF_PRTERR ; PCF_ACKERR: PUSH HL LD HL,PCF_ACKFAIL JR PCF_PRTERR ; PCF_RDBERR: PUSH HL LD HL,PCF_RDBFAIL JR PCF_PRTERR ; PCF_TOERR: PUSH HL LD HL,PCF_TOFAIL JR PCF_PRTERR ; PCF_ARBERR: PUSH HL LD HL,PCF_ARBFAIL JR PCF_PRTERR ; PCF_PINERR: PUSH HL LD HL,PCF_PINFAIL JR PCF_PRTERR ; PCF_BBERR: PUSH HL LD HL,PCF_BBFAIL JR PCF_PRTERR ; PCF_PRTERR: CALL PRTSTR ; CALL NEWLINE POP HL RET ; ;----------------------------------------------------------------------------- ; DEBUG HELPER ; #IF (1) PCF_DBG: PUSH AF PUSH DE PUSH HL LD A,'[' CALL COUT LD HL,PCF_DBGF LD A,(HL) ADD A,'0' INC (HL) CALL COUT LD A,']' CALL COUT POP HL POP DE POP AF RET PCF_DBGF: .DB 0 ; DEBUG STAGE COUNTER #ENDIF ; PCF_NOPCF .DB "NOT PRESENT$" PCF_WRTFAIL .DB "SETTING DEVICE ID FAILED$" PCF_REGFAIL .DB "CLOCK REGISTER SELECT ERROR$" PCF_CLKFAIL .DB "CLOCK SET FAIL$" PCF_IDLFAIL .DB "BUS IDLE FAILED$" PCF_ACKFAIL .DB "FAILED TO RECEIVE ACKNOWLEDGE$" PCF_RDFAIL .DB "READ FAILED$" PCF_RDBFAIL .DB "READBYTES FAILED$" PCF_TOFAIL .DB "TIMEOUT ERROR$" PCF_ARBFAIL .DB "LOST ARBITRATION$" PCF_PINFAIL .DB "PIN FAIL$" PCF_BBFAIL .DB "BUS BUSY$"