(* 说明:全志A20的TWI底层操作封装类。 作者:tjCFeng 邮箱:tjCFeng@163.com 更新日期:2014.12.06 *) unit TWI; {$mode objfpc}{$H+} interface uses SysUtils, A20, Clock, GPIO; type TChannel = (TWI_0, TWI_1, TWI_2, TWI_3, TWI_4); TI2CSpeed = (I2C100K, I2C400K); TTWI = class private FChannel: TChannel; FSpeed: TI2CSpeed; FBus: Boolean; FAutoACK: Boolean; FSCK: TGPIO; FSDA: TGPIO; FTimeOut: LongWord; FTWI_BASE: ^LongWord; protected FTWI_ADDR: ^LongWord; //TWI Slave address FTWI_XADDR: ^LongWord; //TWI Extended slave address FTWI_DATA: ^LongWord; //TWI Data byte FTWI_CNTR: ^LongWord; //TWI Control register FTWI_STAT: ^LongWord; //TWI Status register FTWI_CCR: ^LongWord; //TWI Clock control register FTWI_SRST: ^LongWord; //TWI Software reset FTWI_EFR: ^LongWord; //TWI Enhance Feature register FTWI_LCR: ^LongWord; //TWI Line Control register private procedure SetSpeed(Value: TI2CSpeed); procedure SetBus(Value: Boolean); procedure SetAutoACK(Value: Boolean); function GetState(State: Byte): Boolean; function SendReset: Boolean; function SendStart: Boolean; function SendReStart: Boolean; function SendStop: Boolean; function SetByte(Dat: Byte): Boolean; function GetByte(out Dat: Byte): Boolean; public constructor Create(Channel: TChannel); destructor Destroy; override; function Write(Addr: Byte; Reg: Byte; Dat: Byte): Boolean; function Read(Addr: Byte; Reg: Byte; out Dat: Byte): Boolean; public property Speed: TI2CSpeed read FSpeed write SetSpeed; property Bus: Boolean read FBus write SetBus; property AutoACK: Boolean read FAutoACK write SetAutoACK; end; implementation const TWI_BASE = $01C2AC00; TWI_TIMEOUT = $FFFFFF; const TWI_CNTR_ACK = $04; //($1 shl 2); TWI_CNTR_INT = $08; //($1 shl 3); TWI_CNTR_STP = $10; //($1 shl 4); TWI_CNTR_STA = $20; //($1 shl 5); TWI_CNTR_BUSEN = $40; //($1 shl 6); TWI_CNTR_INTEN = $80; //($1 shl 7); const TWI_SRST_RESET = $1; //($1 shl 0); const TWI_CCR_M = ($F shl 3); TWI_CCR_N = ($7 shl 0); const TWI_STAT_BUS_ERR = $00; // BUS ERROR (* Master mode use only *) TWI_STAT_TX_STA = $08; // START condition transmitted TWI_STAT_TX_RESTA = $10; // Repeated START condition transmitted TWI_STAT_TX_AW_ACK = $18; // Address+Write bit transmitted, ACK received TWI_STAT_TX_AW_NAK = $20; // Address+Write bit transmitted, ACK not received TWI_STAT_TXD_ACK = $28; // data byte transmitted in master mode,ack received TWI_STAT_TXD_NAK = $30; // data byte transmitted in master mode ,ack not received TWI_STAT_ARBLOST = $38; // arbitration lost in address or data byte TWI_STAT_TX_AR_ACK = $40; // Address+Read bit transmitted, ACK received TWI_STAT_TX_AR_NAK = $48; // Address+Read bit transmitted, ACK not received TWI_STAT_RXD_ACK = $50; // data byte received in master mode ,ack transmitted TWI_STAT_RXD_NAK = $58; // date byte received in master mode,not ack transmitted (* Slave mode use only *) TWI_STAT_RXWS_ACK = $60; // Slave address+Write bit received, ACK transmitted TWI_STAT_ARBLOST_RXWS_ACK = $68; TWI_STAT_RXGCAS_ACK = $70; // General Call address received, ACK transmitted TWI_STAT_ARBLOST_RXGCAS_ACK = $78; TWI_STAT_RXDS_ACK = $80; TWI_STAT_RXDS_NAK = $88; TWI_STAT_RXDGCAS_ACK = $90; TWI_STAT_RXDGCAS_NAK = $98; TWI_STAT_RXSTPS_RXRESTAS = $A0; TWI_STAT_RXRS_ACK = $A8; TWI_STAT_ARBLOST_SLAR_ACK = $B0; (* 10bit Address, second part of address *) TWI_STAT_TX_SAW_ACK = $D0; // Second Address byte+Write bit transmitted,ACK received TWI_STAT_TX_SAW_NAK = $D8; // Second Address byte+Write bit transmitted,ACK not received TWI_STAT_IDLE = $F8; // No relevant status infomation,INT_FLAG = 0 const TWI_LCR_SDA_EN = $01; //($1 shl 0); TWI_LCR_SDA_CTL = $02; //($1 shl 1); TWI_LCR_SCL_EN = $04; //($1 shl 2); TWI_LCR_SCL_CTL = $08; //($1 shl 3); TWI_LCR_SDA_STATE = $10; //($1 shl 4); TWI_LCR_SCL_STATE = $20; //($1 shl 5); const TWI_LCR_DEFAULT = $3A; constructor TTWI.Create(Channel: TChannel); var Base: LongWord; begin inherited Create; FChannel:= Channel; FTWI_BASE:= TA20.Instance.GetMMap(TWI_BASE + Ord(FChannel) * $400); Base:= LongWord(FTWI_BASE) + TA20.Instance.BaseOffset(TWI_BASE + Ord(FChannel) * $400); FTWI_ADDR:= Pointer(Base + $0000); FTWI_XADDR:= Pointer(Base + $0004); FTWI_DATA:= Pointer(Base + $0008); FTWI_CNTR:= Pointer(Base + $000C); FTWI_STAT:= Pointer(Base + $0010); FTWI_CCR:= Pointer(Base + $0014); FTWI_SRST:= Pointer(Base + $0018); FTWI_EFR:= Pointer(Base + $001C); FTWI_LCR:= Pointer(Base + $0020); case FChannel of TWI_0: begin FSCK:= TGPIO.Create(PB, 0); FSCK.Fun:= Fun2; FSDA:= TGPIO.Create(PB, 1); FSDA.Fun:= Fun2; end; TWI_1: begin FSCK:= TGPIO.Create(PB, 18); FSCK.Fun:= Fun2; FSDA:= TGPIO.Create(PB, 19); FSDA.Fun:= Fun2; end; TWI_2: begin FSCK:= TGPIO.Create(PB, 20); FSCK.Fun:= Fun2; FSDA:= TGPIO.Create(PB, 21); FSDA.Fun:= Fun2; end; TWI_3: begin FSCK:= TGPIO.Create(PI, 0); FSCK.Fun:= Fun3; FSDA:= TGPIO.Create(PI, 1); FSDA.Fun:= Fun3; end; TWI_4: begin FSCK:= TGPIO.Create(PI, 2); FSCK.Fun:= Fun3; FSDA:= TGPIO.Create(PI, 3); FSDA.Fun:= Fun3; end; end; TCCU.Instance.CLK_SetTWI(Ord(FChannel), True); SetSpeed(I2C100K); SetBus(True); SetAutoACK(True); FTWI_CNTR^:= FTWI_CNTR^ or ($1 shl 7); SendReset; end; destructor TTWI.Destroy; begin SendReset; FSCK.Free; FSDA.Free; TA20.Instance.FreeMMap(FTWI_BASE); inherited Destroy; end; procedure TTWI.SetSpeed(Value: TI2CSpeed); begin FSpeed:= Value; FTWI_CCR^:= 0; case FSpeed of I2C100K: FTWI_CCR^:= FTWI_CCR^ or (3 shl 3) or 3; I2C400K: FTWI_CCR^:= FTWI_CCR^ or (5 shl 3) or 0; end; SendReset; end; function TTWI.SetByte(Dat: Byte): Boolean; begin FTimeOut:= TWI_TIMEOUT; FTWI_DATA^:= Dat; FTWI_CNTR^:= FTWI_CNTR^ and $F7; //Clear INT while ((FTWI_CNTR^ and TWI_CNTR_INT) = 0) and (FTimeOut > 0) do Dec(FTimeOut); Result:= (FTimeOut > 0); end; function TTWI.GetByte(out Dat: Byte): Boolean; begin FTimeOut:= TWI_TIMEOUT; FTWI_CNTR^:= FTWI_CNTR^ and $F7; //Clear INT while ((FTWI_CNTR^ and TWI_CNTR_INT) = 0) and (FTimeOut > 0) do Dec(FTimeOut); Result:= (FTimeOut > 0); Dat:= FTWI_DATA^; end; procedure TTWI.SetBus(Value: Boolean); begin FBus:= Value; case FBus of False: FTWI_CNTR^:= FTWI_CNTR^ and not TWI_CNTR_BUSEN; True: FTWI_CNTR^:= FTWI_CNTR^ or TWI_CNTR_BUSEN; end; end; procedure TTWI.SetAutoACK(Value: Boolean); begin FAutoACK:= Value; case FAutoACK of False: FTWI_CNTR^:= FTWI_CNTR^ and not TWI_CNTR_ACK; True: FTWI_CNTR^:= FTWI_CNTR^ or TWI_CNTR_ACK; end; end; function TTWI.GetState(State: Byte): Boolean; begin Result:= FTWI_STAT^ = State; end; function TTWI.SendReset: Boolean; begin FTimeOut:= TWI_TIMEOUT; FTWI_SRST^:= FTWI_SRST^ or TWI_SRST_RESET; while ((FTWI_SRST^ and TWI_SRST_RESET) = TWI_SRST_RESET) and (FTimeOut > 0) do Dec(FTimeOut); Result:= (FTimeOut > 0); end; function TTWI.SendStart: Boolean; begin SendReset; FTWI_CNTR^:= (FTWI_CNTR^ and $C0) or TWI_CNTR_STA; FTimeOut:= TWI_TIMEOUT; while ((FTWI_CNTR^ and TWI_CNTR_INT) = 0) and (FTimeOut > 0) do Dec(FTimeOut); if (FTimeOut = 0) then Exit(False); Result:= (FTWI_STAT^ = TWI_STAT_TX_STA); end; function TTWI.SendReStart: Boolean; begin FTWI_CNTR^:= (FTWI_CNTR^ and $C0) or TWI_CNTR_STA; FTimeOut:= TWI_TIMEOUT; while ((FTWI_CNTR^ and TWI_CNTR_INT) = 0) and (FTimeOut > 0) do Dec(FTimeOut); if (FTimeOut = 0) then Exit(False); Result:= (FTWI_STAT^ = TWI_STAT_TX_RESTA); end; function TTWI.SendStop: Boolean; begin FTWI_CNTR^:= (FTWI_CNTR^ and $C0) or TWI_CNTR_STP; FTimeOut:= TWI_TIMEOUT; while ((FTWI_CNTR^ and TWI_CNTR_STP) = TWI_CNTR_STP) and (FTimeOut > 0) do Dec(FTimeOut); Result:= (FTimeOut > 0); end; //INT_EN BUS_EN START STOP INT_FLAG ACK NOT NOT //7 6 5 4 3 2 1 0 function TTWI.Write(Addr: Byte; Reg: Byte; Dat: Byte): Boolean; begin try FTWI_EFR^:= 0; FTimeOut:= TWI_TIMEOUT; while (not GetState(TWI_STAT_IDLE)) and (FTimeOut > 0) do Dec(FTimeOut); if (FTimeOut = 0) then Exit(False); if not SendStart then Exit(False); if not SetByte((Addr shl 1) or 0) then Exit(False); if not GetState(TWI_STAT_TX_AW_ACK) then Exit(False); if not SetByte(Reg) then Exit(False); if not GetState(TWI_STAT_TXD_ACK) then Exit(False); if not SetByte(Dat) then Exit(False); if not GetState(TWI_STAT_TXD_ACK) then Exit(False); Result:= True; finally SendStop; end; end; function TTWI.Read(Addr: Byte; Reg: Byte; out Dat: Byte): Boolean; begin try FTWI_EFR^:= 0; FTimeOut:= TWI_TIMEOUT; while (not GetState(TWI_STAT_IDLE)) and (FTimeOut > 0) do Dec(FTimeOut); if (FTimeOut = 0) then Exit(False); if not SendStart then Exit(False); if not SetByte((Addr shl 1) or 0) then Exit(False); if not GetState(TWI_STAT_TX_AW_ACK) then Exit(False); if not SetByte(Reg) then Exit(False); if not GetState(TWI_STAT_TXD_ACK) then Exit(False); if not SendReStart then Exit(False); if not SetByte((Addr shl 1) or 1) then Exit(False); if not GetState(TWI_STAT_TX_AR_ACK) then Exit(False); Result:= GetByte(Dat); //if not GetState($58) then Exit(False); finally SendStop; end; end; end.