Difference between revisions of "AVR Embedded Tutorial - Software I2C, TWI/de"

From Lazarus wiki
Jump to navigationJump to search
m (Added page template; removed categories in template)
 
(17 intermediate revisions by 4 users not shown)
Line 1: Line 1:
==Software-I2C/TWI für ATmega328 / Arduino==
+
{{I2CS}}
 +
 
 +
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.
 
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
 
Zu den Grundlagen von I2C siehe Wikipedia: https://de.wikipedia.org/wiki/I2c
 +
 +
==Software TWI Master==
  
 
===TWI Header===
 
===TWI Header===
Line 9: Line 15:
 
<syntaxhighlight lang="pascal">unit test_twi;
 
<syntaxhighlight lang="pascal">unit test_twi;
  
{$mode objfpc}{$H+}
+
{$mode objfpc}
 
{$goto on}
 
{$goto on}
  
Line 80: Line 86:
 
procedure twi_start();
 
procedure twi_start();
 
begin
 
begin
  DDRtwi := DDRtwi or Psdain;  // SDA low
+
DDRtwi := DDRtwi or Psdain;  // SDA low
  delay3();
+
delay3();
  DDRtwi := DDRtwi or Psclin;  // SCL low
+
DDRtwi := DDRtwi or Psclin;  // SCL low
  delay3();
+
delay3();
 
end;
 
end;
 
</syntaxhighlight>
 
</syntaxhighlight>
Line 96: Line 102:
 
procedure twi_stop();
 
procedure twi_stop();
 
begin
 
begin
  DDRtwi := DDRtwi or Psdain;  // SDA low
+
DDRtwi := DDRtwi or Psdain;  // SDA low
 
delay3();
 
delay3();
 
DDRtwi := DDRtwi and (not Psclin);  // SCL high
 
DDRtwi := DDRtwi and (not Psclin);  // SCL high
Line 116: Line 122:
 
   i : uint8;
 
   i : uint8;
 
begin
 
begin
  for i := 0 to 7 do begin  // 8 Bit ausgeben
+
for i := 0 to 7 do begin  // 8 Bit ausgeben
 
if (data and (1 shl 7)) <> 0 then
 
if (data and (1 shl 7)) <> 0 then
 
DDRtwi := DDRtwi and (not Psdain)  // SDA high, wenn Bit gesetzt
 
DDRtwi := DDRtwi and (not Psdain)  // SDA high, wenn Bit gesetzt
 
else
 
else
 
DDRtwi := DDRtwi or Psdain;  // SDA low, wenn Bit nicht gesetzt
 
DDRtwi := DDRtwi or Psdain;  // SDA low, wenn Bit nicht gesetzt
    data := data shl 1; // Bit schieben, 7 bis 0
+
data := data shl 1; // Bit schieben, 7 bis 0
  delay2();
+
delay2();
  DDRtwi := DDRtwi and (not Psclin);  // SCL high
+
DDRtwi := DDRtwi and (not Psclin);  // SCL high
  delay3();
+
delay3();
    DDRtwi := DDRtwi or Psclin;  // SCL low
+
DDRtwi := DDRtwi or Psclin;  // SCL low
 
end;
 
end;
  
Line 134: Line 140:
  
 
if (PINtwi and Psdain) = 0 then  // Testen SDA auf Acknowledge low
 
if (PINtwi and Psdain) = 0 then  // Testen SDA auf Acknowledge low
    twi_write := true  // Flag Ack setzen
+
twi_write := true  // Flag Ack setzen
 
else
 
else
 
twi_write := false;  // sonst Flag Ack löschen
 
twi_write := false;  // sonst Flag Ack löschen
  
  DDRtwi := DDRtwi or Psclin;  // SCL low
+
DDRtwi := DDRtwi or Psclin;  // SCL low
 
end;
 
end;
 
</syntaxhighlight>
 
</syntaxhighlight>
 +
 +
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 Datenbyte lesen===
Line 149: Line 157:
 
function twi_readbyte(ackn : boolean) : uint8;
 
function twi_readbyte(ackn : boolean) : uint8;
 
var
 
var
data : uint8 = 0;
+
  data : uint8 = 0;
   i : uint8;
+
   i   : uint8;
 
begin
 
begin
  for i := 0 to 7 do begin  // 8 Bit ausgeben
+
for i := 0 to 7 do begin  // 8 Bit ausgeben
 
data := data shl 1; // Bit schieben, 7 bis 0
 
data := data shl 1; // Bit schieben, 7 bis 0
  delay2();
+
  delay2();
  DDRtwi := DDRtwi and (not Psclin);  // SCL high
+
DDRtwi := DDRtwi and (not Psclin);  // SCL high
  delay2();
+
  delay2();
 
if (PINtwi and (Psdain)) <> 0 then  // Testen SDA auf high
 
if (PINtwi and (Psdain)) <> 0 then  // Testen SDA auf high
 
data := data or (1 shl 0);  // wenn high, Bit 0 setzen
 
data := data or (1 shl 0);  // wenn high, Bit 0 setzen
    DDRtwi := DDRtwi or Psclin;  // SCL low
+
DDRtwi := DDRtwi or Psclin;  // SCL low
 
end;
 
end;
  
 
if ackn then
 
if ackn then
  DDRtwi := DDRtwi or Psdain  // SDA low, Acknowledge
+
  DDRtwi := DDRtwi or Psdain  // SDA low, Acknowledge
 
else
 
else
  DDRtwi := DDRtwi and (not Psdain);  // SDA high, No Acknowledge
+
DDRtwi := DDRtwi and (not Psdain);  // SDA high, No Acknowledge
 
delay2();
 
delay2();
 
DDRtwi := DDRtwi and (not Psclin);  // SCL high
 
DDRtwi := DDRtwi and (not Psclin);  // SCL high
 
delay3();
 
delay3();
  DDRtwi := DDRtwi or Psclin;  // SCL low
+
DDRtwi := DDRtwi or Psclin;  // SCL low
 
DDRtwi := DDRtwi and (not Psdain);  // SDA high
 
DDRtwi := DDRtwi and (not Psdain);  // SDA high
  
Line 183: Line 191:
 
function twi_readword(ackn : boolean) : uint16;
 
function twi_readword(ackn : boolean) : uint16;
 
var
 
var
data : uint16 = 0;
+
  data : uint16 = 0;
   i : uint8;
+
   i   : uint8;
 
begin
 
begin
  for i := 0 to 7 do begin  // 8 Bit ausgeben
+
for i := 0 to 7 do begin  // 8 Bit ausgeben
 
data := data shl 1; // Bit schieben, 7 bis 0
 
data := data shl 1; // Bit schieben, 7 bis 0
  delay2();
+
  delay2();
  DDRtwi := DDRtwi and (not Psclin);  // SCL high
+
DDRtwi := DDRtwi and (not Psclin);  // SCL high
  delay2();
+
  delay2();
 
if (PINtwi and (Psdain)) <> 0 then  // Testen SDA auf high
 
if (PINtwi and (Psdain)) <> 0 then  // Testen SDA auf high
 
data := data or (1 shl 0);  // wenn high, Bit 0 setzen
 
data := data or (1 shl 0);  // wenn high, Bit 0 setzen
    DDRtwi := DDRtwi or Psclin;  // SCL low
+
DDRtwi := DDRtwi or Psclin;  // SCL low
 
end;
 
end;
  
Line 200: Line 208:
 
DDRtwi := DDRtwi and (not Psclin);  // SCL high
 
DDRtwi := DDRtwi and (not Psclin);  // SCL high
 
delay3();
 
delay3();
  DDRtwi := DDRtwi or Psclin;  // SCL low
+
DDRtwi := DDRtwi or Psclin;  // SCL low
 
DDRtwi := DDRtwi and (not Psdain);  // SDA high
 
DDRtwi := DDRtwi and (not Psdain);  // SDA high
  
  for i := 0 to 7 do begin  // 8 Bit ausgeben
+
for i := 0 to 7 do begin  // 8 Bit ausgeben
 
data := data shl 1; // Bit schieben, 7 bis 0
 
data := data shl 1; // Bit schieben, 7 bis 0
  delay2();
+
  delay2();
  DDRtwi := DDRtwi and (not Psclin);  // SCL high
+
DDRtwi := DDRtwi and (not Psclin);  // SCL high
  delay2();
+
  delay2();
 
if (PINtwi and (Psdain)) <> 0 then  // Testen SDA auf high
 
if (PINtwi and (Psdain)) <> 0 then  // Testen SDA auf high
 
data := data or (1 shl 0);  // wenn high, Bit 0 setzen
 
data := data or (1 shl 0);  // wenn high, Bit 0 setzen
    DDRtwi := DDRtwi or Psclin;  // SCL low
+
DDRtwi := DDRtwi or Psclin;  // SCL low
 
end;
 
end;
  
 
if ackn then
 
if ackn then
  DDRtwi := DDRtwi or Psdain  // SDA low, Acknowledge
+
  DDRtwi := DDRtwi or Psdain  // SDA low, Acknowledge
 
else
 
else
  DDRtwi := DDRtwi and (not Psdain);  // SDA high, No Acknowledge
+
DDRtwi := DDRtwi and (not Psdain);  // SDA high, No Acknowledge
 
delay2();
 
delay2();
 
DDRtwi := DDRtwi and (not Psclin);  // SCL high
 
DDRtwi := DDRtwi and (not Psclin);  // SCL high
 
delay3();
 
delay3();
  DDRtwi := DDRtwi or Psclin;  // SCL low
+
DDRtwi := DDRtwi or Psclin;  // SCL low
 
DDRtwi := DDRtwi and (not Psdain);  // SDA high
 
DDRtwi := DDRtwi and (not Psdain);  // SDA high
  
Line 227: Line 235:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
===Fertsch===
+
===End===
 +
 
 +
Mit folgender Zeile ist die Unit komplett.
  
 
<syntaxhighlight lang="pascal">
 
<syntaxhighlight lang="pascal">
 
end.  
 
end.  
 
</syntaxhighlight>
 
</syntaxhighlight>
 +
 +
== Siehe auch ==
 +
 +
* Übersichtseite [[AVR Embedded Tutorial/de|AVR Embedded Tutorial]]

Latest revision as of 03:35, 7 March 2020

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