Forum: Projekte & Code Raspberry Pi und AVR verbinden mittels I2C/TWI


von McLovin (Gast)


Lesenswert?

Um Daten zwischen einem Raspberry Pi und einem AVR-Microcontroller 
auszutauschen, kann man das I2C/TWI-Protokoll benutzen. Es braucht nur 
zwei Leitungen und ist relativ simpel.

- ATmega8 (weil der Hardware-I2C/TWI hat, es gehen auch andere von AVR, 
im Datenblatt sollte aber unter Features "Byte-oriented Two-wire Serial 
Interface" aufgeführt sein)
- Raspberry Pi

*ACHTUNG: Beim Verbinden des AVR mit dem RPi darauf achten, dass der AVR 
mit 3,3V läuft! Ansonsten wird der RPi beschädigt!*

Zur Verbindung muss man einfach SDA und SCL des Raspberry (siehe [3], 
also Pin 3 und 5) jeweils mit SDA und SCL am AVR verbinden, beim ATmega8 
sind das Pin 27 und 28. Die Leitungen müssen außerdem jeweils mittels 
Pull-up-Widerstand mit Vcc verbunden werden.

Beim ATmega der Code ist einfach die Bibliothek von [1].

Beim Raspberry benutze ich die I2C-Bibliothek von [2] und den folgenden 
Code:
1
#include <stdio.h>
2
#include <wiringPiI2C.h>
3
4
int main(void)
5
{
6
        printf("OK.\n");
7
8
        int fd = wiringPiI2CSetup(7);
9
        if (fd == -1) {
10
                printf("Error!\n");
11
        }
12
13
        while (1) {
14
                wiringPiI2CWriteReg8(fd, 0, 0);
15
                for (int i = 0; i < 8; i++) {
16
                        int read = wiringPiI2CRead(fd);
17
                        if (read != -1) {
18
                                printf("Read: %d\n", read);
19
                        }
20
                }
21
        }
22
23
        return 0;
24
}
(Datei main.c)

Als Makefile kann man dann noch
1
testi2c: main.c
2
        gcc -std=c99 -o testi2c -lwiringPi main.c
(Datei Makefile)

nehmen.

Die Ausgabe ist dann:
1
# ./testi2c | head
2
OK.
3
Read: 0
4
Read: 3
5
Read: 4
6
Read: 5
7
Read: 6
8
Read: 0
9
Read: 0
10
Read: 0
11
Read: 0

[1] http://www.jtronics.de/avr-projekte/library-i2c-twi-slave.html
[2] https://projects.drogon.net/raspberry-pi/wiringpi/i2c-library/
[3] 
http://www.hobbytronics.co.uk/image/data/tutorial/raspberry-pi/gpio-pinout.jpg

von Christian R. (saedelaere)


Lesenswert?

Du kannst mittels eines Levelshifters wie dem hier 
http://www.watterott.com/de/Level-Shifter natürlich auch einen 5V AVR 
verwenden. Außerdem, warum braucht man PullUP Widerstände für SDA und 
SCL?
Hier zum Beispiel wird erklärt der RPI habe bereits PullUp Widerstände.
http://binerry.de/post/26685647322/raspberry-pi-and-i2c

Wenn du weitere Widerstände hinzufügst hast du die parallel geschaltet 
Widerstände was im Endeffekt dazu führen kann, dass der PullUp zu gering 
ist.

Ansonsten danke für deinen Beitrag, genau das habe ich gesucht!

von Marco Oklitz (Gast)


Lesenswert?

Hallo !

kurze Info falls das wirklich jemand (so wie ich) probiert und dann 
stundenlang den Fehler sucht !

Der Raspberry Pi unterstützt kein clock stretching !!

Die (SCL) Taktleitung wird nur maskiert und demaskiert wodurch spikes 
entstehen die beide Kommunikationspartner stören kann.

http://www.advamation.de/technik/raspberrypi/rpi-i2c-bug.html

von Hopper (Gast)


Lesenswert?

Marco Oklitz schrieb:
> Der Raspberry Pi unterstützt kein clock stretching !!

hi, in den application notes von Atmel zum TWI heisst es, dass der Takt 
des Slave mindestens 16x so schnell sein muss, wie der Takt des TWI vom 
Master. also bei standartmäßigen 100KHz wären das dann min 1,6MHz. Mit 
dem internen Takt kommt ma da nicht hin.

Nochwas: die vorgeschlagene lib für den Atmega unterstützt, so wie man 
sie downloaden kann, keine Rückmeldung für den "general call". Um darauf 
zu reagieren ist die lib wie unten in den Kommentaren vermerkt zu 
ergänzen.

Ebenso die Adresse: diese umfasst, zumindest beim Atmega 8, 16 und 32, 
eine Größe von 7Bit. Der Rpi liest sogar nur Adressen bis 0x77 aus. die 
Adresse muss also eine Stelle nach links geschoben werden und zusätzlich 
das niederwertigste Bit gesetzt werden um auf einen "general call" zu 
reagieren.

Ich kämpfe auch schon die ganze Zeit damit rum, heut mittag werd ich das 
mal testen.

von Jeti (Gast)


Lesenswert?

Hallo,
ich bin neu hier und versuche jetzt schon seit einigen Tagen eine 
Kommunikation zwischen meinem Raspberry und ATMEGA8 herzustellen. Ohne 
Erfolg. Versuche es auch wie oben von  "McLovin" beschrieben mit der 
Libary von jtronics. Ich habe aber keine Ahnung wie ich die einbinden 
muss. Vlt. kann mir ja hier einer helfen. Benutze ATMEL Studio6.

von (prx) A. K. (prx)


Lesenswert?

Hab grad die umgekehrte Richtung in Arbeit, also den RasPi als I2C 
Slave. Sinn: Ersatz eines bisher auf AVR basierenden Datenloggers mit 
Webinterface durch den RasPi. Da der RasPi aber von sich aus keinen I2C 
Slave unterstützt, setzt ein ATtiny841 das I2C in UART um. UART kann der 
RasPi besser.

So ist der RasPi nicht nur wesentlich besser als Datenlogger und kann 
die Daten auch selbst auswerten und präsentieren. Er kann ausserdem auch 
die eigentliche Steuerung programmieren. Was den Firmware-Update 
deutlich erleichtert, indem das SPI des RasPi für Atmel-ISP genutzt 
wird. Bisher lief das über Turnschuhnetzwerk, dann kann man direkt vom 
Entwicklungsrechner aus per Ethernet aktualisieren.

: Bearbeitet durch User
von Jeti (Gast)


Lesenswert?

Ich will den Atmega zur Temperaturregelung benutzen. Dazu soll der Rpi 
dem Atmega den Sollwert und den Istwert senden. Das bekomme ich nicht 
hin irgendwie. Oder hat jemand nen Tip wie das noch anders geht?

von Christian R. (0x2a)


Lesenswert?

Du kannst auch einen EEPROM dazwischen hängen. Das mache ich. Allerdings 
nur weil ich mit dem Microcntroller größere Datenmengen sammle. In der 
Messpause werden die Daten dann vom Pi ausgelesen und in einer DB 
gespeichert.

Der Pi kann übrigens die meisten Sensoren auch selbst auslesen. 
Möglcihrerweise brauchst du den zusätzlichen Microcontroller gar nicht.

von Jeti (Gast)


Lesenswert?

Ja, da hast Du recht, das würde auch nur mit dem Pi gehen. Ich würde 
trotzdem gerne die Kommunikation über TWI hinbekommen. Hat denn evtl. 
irgend jemand eine Ahnung wie "McLovin" das gemacht hat und wie ich die 
Libary von jtronics korrekt einbinden kann. Wie gesagt ich benutze Atmel 
Studio6. Habe die Header-Datei in das entsprechende Verzeichnis kopiert 
und versuche nun irgendwie zumindest mal was hinzubekommen, dass ich den 
Atmel mit i2c detect vom Raspberry aus sehen kann. Ich weiß aber nicht 
wie das gehen soll. Und wozu jeweils die main- und twislave c-dateien 
sind. Wenn jemand schonmal sowas gemacht hat und mir weiter helfen kann 
wäre ich echt sehr dankbar.

von Birger Z. (nohelp)


Lesenswert?

Man sollte auch nochmal darauf hinweisen, dass der I2C-Bus low-aktiv ist 
und es daher hinreichend ist, dass nur der RPI den Pull-Up Richtung 3,3V 
macht. Der Atmel darf dann ohne eigenen Pull-UP durchaus mit 5V 
betrieben werden. Ein Levelshifter ist überflüssig. In vielen 
Schaltungen sieht man zur Strombegrenzung im Fehlerfall häufig 
Serienwiderstände von ca. 100 Ohm in den Leitungen von SCL und SDA.

von (prx) A. K. (prx)


Lesenswert?

Marco Oklitz schrieb:
> Der Raspberry Pi unterstützt kein clock stretching !!

Worauf ich aus gegebenem Anlass nochmal hinweisen will.

Ein ATtiny841, mit echtem Hardware-Slave ausgestattet, war bei mir bei 
7,37 Mhz nicht in der Lage, einem kein Clock Stretching unterstützenden 
100 kHz Master (*) zu folgen, weil durch das Clock Stretching 
Nadelimpulse auf SCL auftraten. Bei 50 kHz I2C-Takt funktionierte es. 
Dabei ist zudem zu bedenken, dass dieser Effekt direkt von der 
Interrupt-Latenz abhängt. Ein mit vielen Interrupt-Quellen versehener 
AVR wird eine dementsprechend hohe maximale Reaktionszeit auf 
I2C-Interrupts haben, dass auch bei einem Takt von 50 kHz sporadische 
Probleme auftreten werden.

M.a.W: Wer einen µC als Slave an einen RPi hängen will, der sollte 
entweder den I2C-Takt extrem niedrig legen, oder einen µC mit sehr 
kurzer worst case Reaktion auf Interrupts verwenden (wie etwa STM32 mit 
Priorisierung). Auch sollte die Kommunikation mit einer CRC abgesichert 
werden.

Hopper schrieb:
> hi, in den application notes von Atmel zum TWI heisst es, dass der Takt
> des Slave mindestens 16x so schnell sein muss, wie der Takt des TWI vom
> Master. also bei standartmäßigen 100KHz wären das dann min 1,6MHz. Mit
> dem internen Takt kommt ma da nicht hin.

Andere Baustelle. Das hat damit nichts zu tun.

Clock Stretching ist ein Teil der I2C Definition und wird von jedem 
mir bekannten per Mikrocontroller implementierten I2C-Slave aktiv 
verwendet. Es wirkt sich immer dann aus, wenn ein I2C-Interrupt im Slave 
erst quittiert wird, wenn der Master bereits versucht, den Takt 
loszulassen, d.h. auf High zu legen. Der Slave hält den Takt dann unten 
und wenn der Master darauf nicht korrekt reagiert, dann gibts u.U. einen 
zu kurzen SCL.

Die meisten nicht per Mikrocontroller implementierte I2C-Slaves, wie 
etwa EEPROMs und viele Sensoren, verwenden kein Clock Stretching, so 
dass das Problem dabei nicht auftritt. So ist ein FRAM vom RPi auch bei 
1MHz problemlos ansprechbar (steht dort auch drin, dass Clock Stretching 
nicht auftritt).

*: Das war bei dem Test zwar kein RPi, sondern ein Bus Pirate, aber das 
ändert reinweg nichts am zu Grunde liegenden Problem.

: Bearbeitet durch User
von Gonzo (Gast)


Lesenswert?

Hallo,
für alle die noch mit der Lib kämpfen: Die Slave-Adresse in der jtronics 
Lib muss man noch um ein Bit nach Links verschieben => Für eine Slave 
Adresse von z.B. 3 (0011) muss man hier eine 7 (0111) oder eine 6 (0110) 
vorgeben je nachdem ob man auch einen General-Call zulassen will! => 
Dies hat auch schon Hopper weiter oben geschrieben, aber man überliest 
es leicht ...ging zumindests mir so...

von Michael B. (mb_)


Lesenswert?

Marco Oklitz schrieb:
> Der Raspberry Pi unterstützt kein clock stretching !!

Das ist so nicht ganz richtig.
Wenn das Clock-stretching mindestens eine Clock-periode dauert, macht 
der Raspi alles richtig.
Außerdem scheint stretching nur nach dem jeweiligen ACK zu 
funktionieren. Das ist beim Empfang etwas ungünstig, da man erst ACK-en 
muss und dann erst verarbeiten kann.

Jedenfalls ist es nicht richtig, dass der Raspi die Clock nur maskiert 
oder überhaupt kein Clock-stretching unterstützt.

von Henrik H. (Firma: TU Chemnitz) (heha)


Lesenswert?

Hallo,
das mit dem Clock-Stretch-Bug des Raspberry Pi (bei mir RPi3b) löst sich 
in Luft auf, wenn man das betreffende Timeout-Register auf 0 setzt:

     sudo busybox devmem 0x3F80401C 32 0

Erklärung: Laut "BCM2835 ARM Peripherals.pdf" Seite 35 darf das Register 
TOUT Null enthalten, um das Timeout des BSC-Moduls (a.k.a. I²C oder TWI) 
zu deaktivieren. Standardmäßig enthält es einen von 0 verschiedenen 
Wert. Damit verschwindet auch das Problem mit den SCL-Spikes. Ohne 
Timeout gibt es bei mir mit dem (recht stark beschäftigten) AVR 
keinerlei Probleme mehr.

Ich habe hier eine Konfiguration (in /boot/config.txt) die kein 
"dtoverlay = i2c_irgendwas" enthält sondern nur diese beiden Zeilen mit 
i2c:

    dtparam=i2c1=on
    dtparam=i2c_arm=on,i2c_arm_baudrate=400000

Anscheinend lädt das System einen (unbekannten?) Standardtreiber der das 
Timeout-Register auf 0 setzt. 400 kHz sind übrigens kein Problem, selbst 
mit einer 1 m langen Leitung dazwischen.

Zu der o.a. Zeile mit "sudo busybox":
busybox ist ein Werkzeugsammelsurium genau für Raspberrys und sonstiges 
Embedded Linux. Mit der Option "devmem" öffnet es "/dev/mem" auf 
funktionierende Weise, mit mmap(). "hexdump -C /dev/mem" geht nämlich 
nicht.
Genau wie alle anderen Prozesse benötigt es zum Zugriff Root-Rechte, 
deshalb "sudo" davor.
Mit der Adressangabe 0x3F80401C liest man von diesem Offset 
(defaultmäßig) einen 32-Bit-Wert. Dabei ist "3F" vom System abhängig (wo 
Linux die Hardwareadresse 0x2000'0000 - siehe Datenblatt - einblendet), 
die "8040" ist die BSC-Basisadresse aus dem Datenblatt, und "1C" der 
Register-Offset von TOUT.
Der nachfolgende Parameter "32" legt die Bitbreite fest, danach die "0" 
zum Schreiben derselben (sonst lesen). Die ARM-Hardware weigert sich, 
etwas anderes als 32-Bit-Quantitäten entgegenzunehmen.

Schließlich kann man das Byte "3F" der Datei 
/proc/device_tree/soc/ranges am Offset 4 entnehmen; beim Raspberry 4 
sollte es "FE" lauten. Wer es genau will liest einen 32-Bit-Addierwert 
ab Offset 4 und muss den mit ntohl() byte-swappen. Wer auch immer auf 
die Idee gekommen sein mag, in dieser Datei Big-Endian unterzubringen: 
Ist doch schon lange ausgestorben.
Die ganze (recht kurze) Datei kann man mit "hexdump -C 
/proc/device_tree/soc/ranges" angucken.

henni

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.