AVR Embedded Tutorial - Software I2C, TWI/de
│
Deutsch (de) │
English (en) │
Zur Übersichtseite AVR Embedded Tutorial/de.
Software-I2C/TWI für ATmega328 / Arduino
Viele AVR Controller bieten eine Hardware-I2C-Schnittstelle. Mitunter benötigt man aber einen I2C auf anderen Pins, oder mehrere I2C. Dann hilft ein Software-I2C.
Zu den Grundlagen von I2C siehe Wikipedia: https://de.wikipedia.org/wiki/I2c
Software TWI Master
TWI Header
unit test_twi;
{$mode objfpc}
{$goto on}
interface
uses
;
procedure twi_start();
procedure twi_stop();
function twi_write(data : uint8) : boolean;
function twi_readbyte(ackn : boolean) : uint8;
function twi_readword(ackn : boolean) : uint16;
const
Psclin = 1 shl 5; // PC5 beliebige Pins, aber auf gleichem Port
Psdain = 1 shl 4; // PC4
var
DDRtwi : byte absolute DDRC; // TWI Pins auf Port C
PRTtwi : byte absolute PORTC;
PINtwi : byte absolute PINC;
implementation
Die Pins für SCL und SDA können beliebig festgelegt werden, müssen aber auf einem Port liegen. Bei Pins auf verschiedenen Ports müssen die Pinzugriffe in den folgenden Routinen angepasst werden.
Pausen
// Pause TWI
procedure delay2();
label
loop;
begin
asm
ldi r16, 9
loop:
dec r16
brne loop
end['r16'];
end;
procedure delay3();
label
loop;
begin
asm
ldi r16, 10
loop:
dec r16
brne loop
end['r16'];
end;
Die Taktrate wird über die Laufzeit der Pausen angepasst, die Zahlen hiner ldi r16. Inline-Assembler läßt hier keine Kommentare zu und aus Timinggründen sollten die Werte direkt eingetragen und nicht als Variablen übergeben werden.
Mit den angegebenen Werten 9 und 10 ergibt sich bei 8MHz Taktfrequenz eine Clockrate von 100kHz, mit 59 und 60 erreicht man 10kHz für lange Datenleitungen.
TWI Start
// Start TWI
procedure twi_start();
begin
DDRtwi := DDRtwi or Psdain; // SDA low
delay3();
DDRtwi := DDRtwi or Psclin; // SCL low
delay3();
end;
Die Startsequenz für eine Übertragung.
TWI Stop
// Stop TWI
procedure twi_stop();
begin
DDRtwi := DDRtwi or Psdain; // SDA low
delay3();
DDRtwi := DDRtwi and (not Psclin); // SCL high
delay3();
DDRtwi := DDRtwi and (not Psdain); // SDA high
delay3();
end;
Die Stopsequenz. Für ein Restart, wie es manche Datenblätter fordern nimmt man einfach eine Stopsequenz gefolgt von einer Startsequenz.
TWI Byte schreiben
// TWI schreiben
function twi_write(data : uint8) : boolean;
var
i : uint8;
begin
for i := 0 to 7 do begin // 8 Bit ausgeben
if (data and (1 shl 7)) <> 0 then
DDRtwi := DDRtwi and (not Psdain) // SDA high, wenn Bit gesetzt
else
DDRtwi := DDRtwi or Psdain; // SDA low, wenn Bit nicht gesetzt
data := data shl 1; // Bit schieben, 7 bis 0
delay2();
DDRtwi := DDRtwi and (not Psclin); // SCL high
delay3();
DDRtwi := DDRtwi or Psclin; // SCL low
end;
DDRtwi := DDRtwi and (not Psdain); // SDA high
delay2();
DDRtwi := DDRtwi and (not Psclin); // SCL high
delay2();
if (PINtwi and Psdain) = 0 then // Testen SDA auf Acknowledge low
twi_write := true // Flag Ack setzen
else
twi_write := false; // sonst Flag Ack löschen
DDRtwi := DDRtwi or Psclin; // SCL low
end;
Byte übergeben und los. Dauer ist etwa 10x Taktdauer, das heisst 100µsec bei 100kHz. Die Routine darf von Interrupts unterbrochen werden, der Slave wartet dann.
TWI Datenbyte lesen
// TWI Byte lesen
function twi_readbyte(ackn : boolean) : uint8;
var
data : uint8 = 0;
i : uint8;
begin
for i := 0 to 7 do begin // 8 Bit ausgeben
data := data shl 1; // Bit schieben, 7 bis 0
delay2();
DDRtwi := DDRtwi and (not Psclin); // SCL high
delay2();
if (PINtwi and (Psdain)) <> 0 then // Testen SDA auf high
data := data or (1 shl 0); // wenn high, Bit 0 setzen
DDRtwi := DDRtwi or Psclin; // SCL low
end;
if ackn then
DDRtwi := DDRtwi or Psdain // SDA low, Acknowledge
else
DDRtwi := DDRtwi and (not Psdain); // SDA high, No Acknowledge
delay2();
DDRtwi := DDRtwi and (not Psclin); // SCL high
delay3();
DDRtwi := DDRtwi or Psclin; // SCL low
DDRtwi := DDRtwi and (not Psdain); // SDA high
twi_readbyte := data; // Daten zurückgeben
end;
TWI Datenword lesen
// TWI Word lesen, MSB + LSB
function twi_readword(ackn : boolean) : uint16;
var
data : uint16 = 0;
i : uint8;
begin
for i := 0 to 7 do begin // 8 Bit ausgeben
data := data shl 1; // Bit schieben, 7 bis 0
delay2();
DDRtwi := DDRtwi and (not Psclin); // SCL high
delay2();
if (PINtwi and (Psdain)) <> 0 then // Testen SDA auf high
data := data or (1 shl 0); // wenn high, Bit 0 setzen
DDRtwi := DDRtwi or Psclin; // SCL low
end;
DDRtwi := DDRtwi or Psdain; // SDA low, Acknowledge
delay2();
DDRtwi := DDRtwi and (not Psclin); // SCL high
delay3();
DDRtwi := DDRtwi or Psclin; // SCL low
DDRtwi := DDRtwi and (not Psdain); // SDA high
for i := 0 to 7 do begin // 8 Bit ausgeben
data := data shl 1; // Bit schieben, 7 bis 0
delay2();
DDRtwi := DDRtwi and (not Psclin); // SCL high
delay2();
if (PINtwi and (Psdain)) <> 0 then // Testen SDA auf high
data := data or (1 shl 0); // wenn high, Bit 0 setzen
DDRtwi := DDRtwi or Psclin; // SCL low
end;
if ackn then
DDRtwi := DDRtwi or Psdain // SDA low, Acknowledge
else
DDRtwi := DDRtwi and (not Psdain); // SDA high, No Acknowledge
delay2();
DDRtwi := DDRtwi and (not Psclin); // SCL high
delay3();
DDRtwi := DDRtwi or Psclin; // SCL low
DDRtwi := DDRtwi and (not Psdain); // SDA high
twi_readword := data; // Daten zurückgeben
end;
End
Mit folgender Zeile ist die Unit komplett.
end.
Siehe auch
- Übersichtseite AVR Embedded Tutorial