Der Logic Analyzer ist ein kleines, preiswertes Gerät, mit dem man die Datensignale verschiedener Bussysteme sichtbar machen kann. Wie im eBook beschrieben, lade ich das Analyseprogramm aus dem Internet herunter. Nach der Installation schließe ich den Logic Analyzer an einen USB-Port an; dieser wird von der Software erkannt. Mit Mausklicks der rot gekennzeichneten Schaltflächen wähle ich I2C auf Kanal 0 und 1 und starte die Datenaufzeichnung mit dem weißen Dreieck auf grünem Kreis.
(Sie haben sicher schon gesehen, dass die Nummerierung der Anschlüsse am Logic Analyzer bei 1 beginnt, die typische Falle: also CH1 wird Channel 0 in der Software.)
Als Erstes mache ich mich an die Auswertung des I2C-Bus-Signals am Beispiel der Real Time Clock DS3231.
Zunächst lade ich den Sketch I2C-Scanner auf meinen Micro Controller, um die I2C-Adresse(n) der angeschlossenen Real Time Clock (RTC) auszulesen:
Im Wesentlichen „ruft“ der Master mit der for-Schleife
for (address = 1; address < 127; address++ )
alle möglichen I2C-Geräte und wartet auf die Antwort NAK (=not acknowledged=kein Gerät mit der Adresse) oder ACK (=acknowledged=Gerät an der angegebenen Adresse gefunden).
Kurze Wiederholung zu I2C: I²C ist als Master-Slave-Bus konzipiert. Ein Datentransfer wird immer durch einen Master (Micro Controller) durch das Senden eines Bytes initiiert. Der über eine Adresse angesprochene Slave (z.B. Sensor oder RTC) reagiert darauf. Die ersten 7 Bit des Bytes kennzeichnen die Adresse des Slave (also theoretisch 2 hoch 7 = 128 Adressen), das letzte Bit signalisiert dem Slave, ob er Daten empfangen oder senden soll.
Hier die Ausgabe für die angeschlossene RTC im Seriellen Monitor; ein Gerät – zwei Adressen? Das klären wir später.
Interessant das Bild der übertragenen Bits and Bytes, ausgewertet vom Logic Analyzer und der dazu empfohlenen Software. Hier der Ausschnitt für die Adressen 0x63 bis 0x68:
Die Adressen 0x63 bis 0x67 liefern NAK, die Übertragung der nächsten Adresse wird sofort fortgesetzt. Die Adresse 0x68 liefert wie erwartet ACK. Die Übertragung auf dem I2C-Bus wird kurz unterbrochen, weil der Sketch die gefundene Adresse im Seriellen Monitor ausgibt, und dann fortgesetzt.
Das gleiche Bild hatte sich bei der Adresse 0x57 gezeigt. Auf der kleinen Platine gibt es also ein weiteres I2C-Gerät. Das englische eBook sagt dazu: The module consists of a DS3231 RTC Clock chip and Atmel AT24C32 EEPROM chip. The AT24C32 has memory storage capacity of 32kB and uses the I2C bus interface with 0x57 address which can be modified.
Also: Der EEPROM Chip, den wir hier nicht weiter benutzen, hat die Adresse 0x57.
Nun kommt die Real Time Clock zum Einsatz. Ich habe die Programm-Bibliothek RTClib von Adafruit bei mir installiert. Die kommt u.a. mit mehreren Dateien mit der Endung .cpp für verschiedene Real Time Clocks sowie einem Beispiel-Sketch zur RTC DS3231.
Hier zunächst beispielhaft ein Screenshot vom Seriellen Monitor mit der Ausgabe der Daten des Beispiel-Sketches alle drei Sekunden (übrigens bei 57600 Baud):
Um eine eindeutige Zuordnung des Programmlaufs zum Messergebnis des Logic Analyzers vornehmen zu können, verlängere ich die Zeit zwischen zwei Abfragen der RTC.
Die Auswertesoftware liefert sowohl Bild des Signalverlaufs als auch Tabelle der gesendeten Bytes.
Mal sehen, ob wir die folgenden Daten aus dem seriellen Monitor der Arduino IDE „wiederfinden“:
19:21:19.225 -> 2021/11/22 (Monday) 19:21:10 |
19:21:19.225 -> since midnight 1/1/1970 = 1637608870s = 18953d |
19:21:19.225 -> now + 7d + 12h + 30m + 6s: 2021/11/30 7:51:16 |
19:21:19.225 -> Temperature: 22.00 C |
Das gesamte Signal dauert 14 ms; man erkennt jedoch noch nicht viel.
Also drehe ich am Rollrad meiner Maus und vergrößere die einzelnen Abschnitte:
Nun kann man sehr schön den Signalverlauf von SDA und SCL erkennen. Mit rechtem Maus-Click in die farblich unterlegten Daten oberhalb des SDA-Signals kann man übrigens wählen, ob das Ausgabeformat Binär, Dezimal oder Hexadezimal sein soll. Entsprechend ändert sich auch das Zahlenformat in der Tabelle am rechten Rand des Programmfensters.
In beiden Bildern signalisiert der Master dem Slave 0x68 (=RTC), dass er etwas schreiben (Write) wird. Im oberen Bild sendet er die Registeradresse 0x00, im unteren 0x11. Dann signalisiert der Master dem Slave 0x68, dass er Daten erwartet (Read). Im oberen Bild werden sieben Bytes übertragen, im unteren zwei Bytes.
Aus der Library und dem Datenblatt ersehen wir, dass die Registeradresse 0x00 und die folgenden sechs Register die aktuelle Zeit und das Datum, die Registeradressen 0x11 und 0x12 die Temperatur übermitteln.
Auszug aus Library RTC_DS3231.cpp
Zeile 1 - 13:
///< I2C address for DS3231
///< Time register
///< Alarm 1 register
///< Alarm 2 register
///< Control register
///< Status register
///< Temperature register (high byte - low byte is at 0x12), 10-bit
///< temperature value
/**************************************************************************/
Zeile 69ff:
DateTime RTC_DS3231::now() {
uint8_t buffer[7];
buffer[0] = 0;
i2c_dev->write_then_read(buffer, 1, buffer, 7);
Auszug aus Datenblatt:
Hier die übertragenen Daten in tabellarischer Form
name |
type |
address |
Data_HEX |
Data_BIN |
aus Register |
I2C |
start |
||||
I2C |
address |
0x68 |
|||
I2C |
data |
0x00 |
0b00000000 |
Registeradresse |
|
I2C |
start |
||||
I2C |
address |
0x68 |
|||
I2C |
data |
0x10 |
0b00010000 |
0x00 |
|
I2C |
data |
0x21 |
0b00100001 |
0x01 |
|
I2C |
data |
0x19 |
0b00011001 |
0x02 |
|
I2C |
data |
0x01 |
0b00000001 |
0x03 |
|
I2C |
data |
0x22 |
0b00100010 |
0x04 |
|
I2C |
data |
0x11 |
0b00010001 |
0x05 |
|
I2C |
data |
0x21 |
0b00100001 |
0x06 |
|
I2C |
stop |
||||
I2C |
start |
||||
I2C |
address |
0x68 |
|||
I2C |
data |
0x11 |
0b00010001 |
Registeradresse |
|
I2C |
start |
||||
I2C |
address |
0x68 |
|||
I2C |
data |
0x16 |
0b00010110 |
0x11 |
|
I2C |
data |
0x00 |
0b00000000 |
0x12 |
|
I2C |
stop |
Die Auswertung anhand der Tabelle aus dem Datenblatt ist verblüffend einfach:
ADDRESS |
BIT 7 MSB |
BIT 6 |
BIT 5 |
BIT 4 |
BIT 3 |
BIT 2 |
BIT 1 |
BIT 0 LSB |
Auswertung |
00h |
0 |
10 Seconds |
Seconds |
||||||
0b00010000 |
0 |
0 |
0 |
1 |
0 |
0 |
0 |
0 |
10 |
01h |
0 |
10 Minutes |
Minutes |
||||||
0b00100001 |
0 |
0 |
1 |
0 |
0 |
0 |
0 |
1 |
21 |
02h |
0 |
12/24 |
AM/PM 20 Hour |
10 Hour |
Hour |
||||
0b00011001 |
0 |
0 |
0 |
1 |
1 |
0 |
0 |
1 |
19 |
Die Auswertung der Bits in den Register 0x02, 0x01 und 0x00 ergibt die Uhrzeit 19:21:10. Bingo!
Für die Temperatur schauen wir noch einmal in die Library RTC_DS3231.cpp und in das Datenblatt:
Zeile 114ff:
float RTC_DS3231::getTemperature() {
uint8_t buffer[2] = {DS3231_TEMPERATUREREG, 0};
i2c_dev->write_then_read(buffer, 1, buffer, 2);
return (float)buffer[0] + (buffer[1] >> 6) * 0.25f;
ADDRESS |
BIT 7 MSB |
BIT 6 |
BIT 5 |
BIT 4 |
BIT 3 |
BIT 2 |
BIT 1 |
BIT 0 LSB |
Auswertung |
|
11h |
Sign |
Data |
Data |
Data |
Data |
Data |
Data |
Data |
||
0b00010110 |
0 |
0 |
0 |
1 |
0 |
1 |
1 |
0 |
22 |
|
12h |
Data |
Data |
0 |
0 |
0 |
0 |
0 |
0 |
||
0b00100001 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
Während die Auswertung der Vorkommastellen der Temperatur aus Register 0x11 einfach ist (22°C), muss man die beiden Daten-Bits für die Nachkommastellen zunächst sechs Stellen nach rechts verschieben (>>6) und den Wert der verbliebenen zwei Bits mit 0,25 multiplizieren.
Die Grundlagen dafür findet man im Datenblatt auf Seite 15:
Temperature Registers (11h–12h)
Temperature is represented as a 10-bit code with a resolution of 0.25°C and is accessible at location 11h and 12h. The temperature is encoded in two’s complement format. The upper 8 bits, the integer portion, are at location 11h and the lower 2 bits, the fractional portion, are in the upper nibble at location 12h.
Wie schön, dass die Programm-Bibliothek uns diese Arbeit abnimmt. Aber: Wir haben alle Werte der Datenübertragung im SDA-Signal wiederfinden können.
Das ist nicht immer so einfach. Ich hatte meine Versuche mit meinem Lieblings-Sensor, dem BME280, begonnen. I2C-Adresse und die Register-Adressen für Temperatur, Luftdruck und Rel. Feuchtigkeit habe ich identifizieren können. Jedoch ist die Berechnung dieser Werte aus den übertragenen Bytes leider äußerst komplex. Die Formeln aus dem Datenblatt von Bosch sind zum Glück in der Programmbibliothek benutzerfreundlich umgesetzt.
Deshalb hier nur ein Bild vom ersten Aufruf des BME280 mit der Adresse 0x76=0b0111 0110:
Fazit: Wenn man bei der Logic Analyzer Software das richtige Datenprotokoll (hier I2C) auswählt und den Datenbus mit den richtigen Kanälen verbindet, erhält man die Aufzeichnung der Datenübertragung, wahlweise als Dezimal-, Hexadezimal- oder Binärzahl.
Beim nächsten Mal werde ich weitere Bussysteme untersuchen.
1 commentaire
Gerhard Chvatal
Hallo!
Prinzipiell ein sehr interessanter Beitrag, den ich gerne um den folgenden Aspekt ergänzen möchte.
Für Ihren und viele andere Logikanalysatoren, gibt es eine sehr gute OpenSource Analysator-Software, die auf allen gängigen Betriebssystemen läuft.
Diese Software heißt Sigrok und das zugehörige graphische Frontend heißt PulseView und bietet eine Vielzahl (130+) von implementierten Decodern (I²C, SPI, I²S …) an. Diese können dazu noch selbst erweitert werden.
Vielleicht eine interessante Open-Source Alternative für den Logikanalysator.
https://sigrok.org/wiki/Main_Page
Mit freundlichem Gruß,
Gerhard Chvatal, OE1GCA