{*********************************************************************
*                                                                    *
*         Projekt Name : WUDU2                                       *
*            Main Unit : -                                           *
*            Unit Name : I2CUnit                                     *
*     Edition.Revision : 1.0                                         *
*               System : Windows                                     *
*   besondere Umgebung : I2C-Bus Interface                           *
*                                                                    *
*            Komponist : Uwe Mnich                                   *
*            Copyright : Uwe Mnich                                   *
*             Erstellt : 01.09.2002                                  *
*     letzte Aenderung : 07.09.2002 I2CInit und I2CRelease impl.     *
*                                                                    *
*          Eigenschaft : Unit                                        *
*         benutzt Unit : Windows, SysUtils                           *
*     wird benutzt von : -                                           *
*  benötigte Libraries : -                                           *
*    Extra Komponenten : -                                           *
*              Dateien : -                                           *
*    Datenbank Dateien : -                                           *
*                                                                    *
*               Syntax : -                                           *
*            Argumente : -                                           *
*              Returns : -                                           *
* Def Globale Variable :                                             *
*                                                                    *
* Zweck: Funktionen zur Bedienung des I2C-Bus Interfaces an der      *
*        seriellen Schnittstelle.                                    *
*                                                                    *
**********************************************************************}
unit I2CUnit;

interface

Uses
  Windows, SysUtils;

  Function  ComOpen(ComStr:String):Boolean;
  Procedure ComClose;
  Function  DTR(State:Boolean):Boolean;
  Function  RTS(State:Boolean):Boolean;
  Function  DSR:Boolean;
  Function  CTS:Boolean;
  Function  Ring:Boolean;
  Procedure I2CStartBit;
  Procedure I2CStopBit;
  Function  I2CInit(ComPort:String):Boolean;
  Procedure I2CRelease;
  Function  I2CPutByte(b1:Byte):Boolean;
  Function  I2CGetByte(Ack:Byte):Byte;

implementation

Uses Unit1;

Const
  high = false;
  low = true;

Var
  CHandle: THandle;
  CEnabled: Boolean = false;
  t0,t1,t2: TLargeInteger;
  r0,r1,r2: Extended;

// Verzögerungszeit in us
Procedure Delay(td:Integer);
Begin
  QueryPerformanceCounter(t1);
  r1 := t1.QuadPart;
  Repeat
    QueryPerformanceCounter(t2);
    r2 := t2.QuadPart;
  Until Trunc(1000000*(r2-r1)/r0) >= td;
End;

// ------------------------------------------------------------------
// Level 1

Function ComOpen(ComStr:String):Boolean;
Begin
  Result := true;
  CHandle := CreateFile(PCHAR(ComStr),GENERIC_READ or GENERIC_WRITE,
             0,nil,OPEN_EXISTING,FILE_FLAG_OVERLAPPED,0);
  If CHandle = INVALID_HANDLE_VALUE Then Result := false;
  CEnabled := Result;
  // Delay initialisieren
  QueryPerformanceFrequency(t0);
  r0 := t0.QuadPart;
End;

Procedure ComClose;
Begin
  If CEnabled Then Begin
    CloseHandle(CHandle);
    CHandle := INVALID_HANDLE_VALUE;
  End;
End;

// Setzt die DTR Leitung
// State = True = +5V..+12V
// State = False = -12V..-5V
Function DTR(State:Boolean):Boolean;
Const DTR: Array[Boolean] of Integer = (CLRDTR,SETDTR);
Begin
  Result := false;
  If CEnabled Then Begin
    Result := EscapeCommFunction(CHandle,DTR[State]);
  End;
End;

// Setzt die RTS Leitung
// State = True = +5V..+12V
// state = False = -12V..-5V
Function RTS(State:Boolean):Boolean;
Const RTS: Array[Boolean] of Integer = (CLRRTS,SETRTS);
Begin
  Result := false;
  If CEnabled Then Begin
    Result := EscapeCommFunction(CHandle,RTS[State]);
  End;
End;

// Holt den Status der DSR Leitung
// Result = True = +5V..12V
// Result = False = -12V..0V oder Offener Eingang
Function DSR:Boolean;
Var Flag:DWord;
Begin
  Result := false;
  If CEnabled Then Begin
    If GetCommModemStatus(CHandle,Flag) Then Begin
      Result := (Flag and MS_DSR_ON > 0);
    End;
  End;
End;

// Holt den Status der CTS Leitung
// True = +5V..12V
// False = -12V..0V oder Offener Eingang
Function CTS:Boolean;
Var Flag:DWord;
Begin
  Result := false;
  If CEnabled Then Begin
    If GetCommModemStatus(CHandle,Flag) Then Begin
      Result := (Flag and MS_CTS_ON > 0);
    End;
  End;
End;

// Holt den Status der Ring Leitung
// True = +5V..12V
// False = -12V..0V oder Offener Eingang
Function Ring:Boolean;
Var Flag:DWord;
Begin
  Result := false;
  If CEnabled Then Begin
    If GetCommModemStatus(CHandle,Flag) Then Begin
      Result := (Flag and MS_RING_ON > 0);
    End;
  End;
End;

// ------------------------------------------------------------------
// Level 2

Procedure SCL(b1:Boolean);
Begin
  DTR(not b1);
End;

Procedure SDA(b1:Boolean);
Begin
  RTS(not b1);
End;

Function GetSCL:Boolean;
Begin
  If DSR Then Result := low Else Result := high;
End;

Function GetSDA:Boolean;
Begin
  If CTS Then Result := low Else Result := high;
End;

// ------------------------------------------------------------------
// Level 3

// Sendet ein Startbit zum Slave
Procedure I2CStartBit;
Begin
  // Wenn CLK high ist, wechselt SDA von high nach low
  SDA(high);
  SCL(high);
  SDA(low);
  SCL(low);
End;

// Sendet ein Stopbit zum Slave
Procedure I2CStopBit;
Begin
  // Wenn CLK high ist, wechselt SDA von low nach high
  SDA(low);
  SCL(high);
  SDA(high);
End;

// Schnittstelle öffnen und Leitungen initialiseren
Function I2CInit(ComPort:String):Boolean;
Begin
  Result := false;
  If ComOpen(ComPort) Then Begin
    DTR(false);
    RTS(false);
    Result := true;
  End;
End;

// Schnittstelle freigeben
Procedure I2CRelease;
Begin
  ComClose;
End;

// Sendet ein Byte zum Slave
// b1 ist das Byte, welches zum Slave gesendet wird
Function I2CPutByte(b1:Byte):Boolean;
Var i:Integer; j:Byte;
Begin
  Result := false;
  j := $80;
  For i := 1 to 8 do Begin
    If (j and b1) = j Then Begin
      SDA(high);
    End Else Begin
      SDA(low);
    End;
    SCL(high);
    SCL(low);
    SDA(low);
    j := j div 2;
  End;
  // Acknowledge empfangen
  SDA(high);
  SCL(high);
  Result := GetSDA;
  SCL(low);
  SDA(high);
End;

// Empfängt ein Byte vom Slave
// Ack ist das zum Slave gesendete Acknowledge Bit.
// Ist bei einigen Bausteinen notwendig um einen
// Autoincrement Modus zu beenden.
Function I2CGetByte(Ack:Byte):Byte;
Var i:Integer; b1:Byte;
Begin
  Result := 0;
  SDA(high);
  b1 := $00;
  For i := 1 to 8 do Begin
    b1 := b1 * 2;
    SCL(high);
    If GetSDA = high Then b1 := b1 or $01;
    SCL(low);
  End;
  // Acknowledge senden
  If Ack = 0 Then SDA(low) Else SDA(high);
  SCL(high);
  SCL(low);
  SDA(high);
  Result := b1;
End;

end.

