Mobile Technology - Digital Volume 35 - Gib mir zwei


Preis: 7,80 €

Erhältlich ab:  April 2019

Umfang:  45

Autoren / Autorinnen: Carsten Sandtner, Tam Hanna, Veikko Krypczyk, Elena Bochkor, Veikko Krypczyk, Elena Bochkor

Um zu verstehen, dass man mit Android Things keine Rakete ins Ziel bringen wird, genügt ein Blick in die Modulationsebene. Das Betriebssystem brilliert nur dann, wenn wir Zeitkritisches an einen hardwaregesteuerten Bus auslagern.

Android Things ist in unserem Fall auf das Entgegennehmen der Informationen beschränkt – eine wenig zeitkritische Aufgabe. Im letzten Teil der Serie hatten wir den SPI-Bus vorgestellt und ein kleines Display verwendet. Diese Vorgehensweise war sinnvoll, weil das Display nicht unerheblich Bandbreite konsumiert – die farbige Variante ist übrigens noch in der Post.

In der Praxis gibt es immer wieder Situationen, in denen es auf Bandbreite nicht ankommt und man sich stattdessen freut, wenn eine Vielzahl verschiedener Sensoren an ein und demselben Prozessrechner hängt. An diesem Punkt schlägt die Stunde des als I2C bezeichneten Busses, den man in der Literatur aufgrund von Patentstreitigkeiten mit Philips auch unter dem Namen Two Wire Interface (TWI) findet. Damit ist die Rolle des Busses eigentlich auch schon beschrieben: Zwei Leitungen (plus die prinzipiell nicht mitgezählte Masse) verbinden Sensoren nach dem in Abbildung 1 gezeigten Schema mit dem Prozessrechner.

image

Abb. 1: Der Sinn des Begriffs Two Wire Interface erschließt sich bei einem Blick auf dieses Diagramm (Bildquelle: I2C-Spezifikation)

Die beiden oben eingezeichneten Widerstände Rp, in der Fachsprache als Pull-ups bezeichnet, haben die Aufgabe, die als SCL (Serial Clock) und SDA (Serial Data) bezeichneten Leitungen leicht in Richtung der Versorgungsspannung zu ziehen. Wer kommunizieren möchte, zieht die Leitungen über einen Transistor auf Masse. Das Hochlimitieren zur Versorgungsspannung erfolgt in I2C prinzipiell selbsttätig (Kasten: „Wo sind sie?“).

Wo sind sie?

Die verwendeten Schaltungen kommen ohne Pull-up-Widerstände aus. Die Ursache dafür ist, dass die meisten Prozessrechner auf ihren I2C-Pins schon Widerstände mitbringen, die das Hochziehen erledigen. In manchen Fällen kann es allerdings wünschenswert sein, einen kleinen zusätzlichen Widerstand hinzuzufügen, um längere Leitungen effizient betreiben zu können. Dabei dürfen Sie es allerdings nicht übertreiben – denken Sie daran, dass der gesamte Strom ausschließlich von den gerade kommunizierenden ISs abgeleitet werden muss.

Im nächsten Schritt wollen wir I2C auf logischer Ebene betrachten (Abb. 2). Kanal 1 zeigt hierbei die vom als Master bezeichneten Gerät generierte „Arbeitstaktrate“. Ein I2C-System arbeitet immer mit der Frequenz, die der Master am Pin SCL ausgibt. In der Literatur sind hier zwei Betriebsmodi bekannt: erstens der Standardmodus mit 100 KBit/s, zweitens der Schnellmodus mit 400 KBit/s. Die Spezifikation kennt zudem Fast Mode mit 1 MBit/s und Highspeed mit 3,2 MBit/s – diese Sondermodi sind allerdings nicht universell implementiert. Viele Bauteile unterstützen auch schnellere, langsamere oder dazwischenliegende Werte – eine Situation, die Sie in der Praxis ausprobieren müssen.

image

Abb. 2: I2C-Bus plus LeCroy 9354 AM = futuristisches Schirmbild

Die eigentliche Kommunikation erfolgt über die Leitung SDA. Interessant ist in diesem Zusammenhang die 7 Bit lange Adresse jedes Teils; es handelt sich dabei um eine im gesamten Bus einzigartige numerische ID, die dem Master das Ansprechen einzelner Zielgeräte erlaubt.

Über die Frage, wie man Hardwarebusse am besten analysiert, lässt sich viel diskutieren. Viele Entwickler bevorzugen als MSO bezeichnete Oszillographen, die das Dekodieren direkt am Bildschirm erledigen. Allerdings lassen sich die analoge und digitale Domäne auch als voneinander getrennt betrachten. Zur Analyse des digitalen Teils benutzen wir in unserem Fall Analysatoren von Saleae, deren Benutzerinterface sich in Abbildung 3 zeigt.

image

Abb. 3: Die Transaktionen werden von der Analysesoftware durch Blasen hervorgehoben

Der Vorteil dieser Vorgehensweise, die sich übrigens auch in aus China preiswert erhältlichen Klonen wiederfindet, ist, dass Sie den Code des Treibers und seine Ausgabe am I2C-Bus gleichzeitig auf der Workstation sehen können und nicht permanent zwischen Messtisch und Rechner herumlaufen. Die Consultingpraxis hat gezeigt, dass nicht funktionierende Busse so gut wie immer ein Problem der Software waren.

Was nun?

Nach diesen einführenden Überlegungen zur Elektronik wollen wir damit beginnen, per I2C zu kommunizieren. Als Gegenstelle dient dabei das Breakout-Board. Der Anschluss an einen Raspberry Pi 3 erfolgt, indem Sie Vcc mit 3V3 verbinden, SDA und SCL anschließen sowie eine Masseverbindung herstellen. Beginnen wir mit der Implementierung der Software. Öffnen Sie wie gewohnt Android Studio und spielen Sie die diversen Updates ein. Achten Sie insbesondere darauf, ein aktuelles Android Things zu benutzen. Google ändert das API von Android Things mitunter zwischen einzelnen Releases derartig, dass sich Programme nicht mehr kompilieren lassen. An dieser Stelle sollten Sie auch die unter https://partner.android.com/things/console/ bereitstehende Android-Things-Konsole aufsuchen und eine neue SD-Karte brennen: Android Things ist weder aufwärts- noch abwärtskompatibel. Immerhin verspricht Google, dass die nun vorliegende Developer Preview 8 bis inklusive des Erscheinens von Version 1.0 keine weiteren Änderungen erfährt.

Ein wichtiger Unterschied zu früheren Versionen von Android Things ist, dass die neueste Version DP8 im Rahmen der Erstellung einer Build-Konfiguration erlaubt, die Größe der OEM-Partition zu verändern. Dies erfolgt über den in Abbildung 4 gezeigten Assistenten.

image

Abb. 4: Bisher musste man diesen Wert beim erstmaligen Anliegen des Projekts bestimmen

Von besonderem Interesse ist der ganz links am Bildschirm eingeblendete Selektor, über den sich die Größe des zu erzeugenden Images festlegen lässt. Sie können auf diese Art und Weise erstmals unter 4 GB gehen. In der Theorie wäre es sogar möglich, ein 2 GB großes Image zu erzeugen. Interessanterweise scheint das Backend diese Einstellung nicht ganz ernst zu nehmen: In Tests ergab das Einstellen von 4 GB beispielsweise eine rund 400 MB große Archivdatei, die im entpackten Zustand allerdings ein 4,6 GB großes Image ausspuckte.

Ein weiteres Ärgernis ist, dass Google nun Development und Production Builds aufteilt. In der Configuration List müssen wir über die Combobox auswählen, welche Art von Image wir begehren. Das Backend beginnt dann mit der Erzeugung des bekannten SD-Karten-Images, das Sie wie gewohnt an den Raspberry Pi weitergeben. Das mit Android Marshmallow eingeführte Permission-Management findet sich in Android Things in ähnlicher Form: In der Konsole können Sie festlegen, welche Berechtigungen Ihr Programm nicht verwenden darf.

Leider ist dies noch nicht genug. Wer für die aktuellste Version von Android Things entwickeln möchte, muss zwangsweise auf eine Betaversion von Android Studio setzen. Bis dato gibt Google die Version Android Studio 3.2 Canary 11 als Minimalanforderung an. Wer mit einer normalen Version der IDE arbeitet, muss im ersten Schritt den URL https://developer.android.com/studio/preview/index.html aufrufen. Unter https://developer.android.com/studio/preview/install-preview.html finden sich zudem Installationsanweisungen, die erklären, wie man zwei Versionen von Android Studio nebeneinander installieren kann. Da sich die verschiedenen Versionen der IDE eine gemeinsame SDK-Installation teilen können, liegt der dabei auftretende Speicherbedarf im Bereich von 2 GB.

Im nächsten Schritt lässt sich die IDE starten und ein neues Projekt anlegen. Wie im Fall von DP7 gilt, dass die aktuellste Version der Android-Things-Programmierumgebung auf Version 8.1 des Betriebssystems basiert. Achten Sie darauf, im Projektgeneratorassistenten von Android Studio die Option Android Things zu selektieren, um die passende Version von Bibliothek, Projektskelett und Co. zu laden.

Mann gegen Metall

Der erste Unterschied ist die Art, wie der Start der Applikation erfolgt. Ein Android-Things-Gerät kommt ohne klassischen Programmstarter aus und lädt nach dem Hochfahren das Programm, das für die Erledigung der MSR-Aufgabe vorgesehen ist. Bisher wurde dies über einen speziellen Intent angezeigt. Ab DP8 wird dieses System nicht mehr genutzt. Stattdessen kommt ein gewöhnlicher Intent vom Typ CATEGORY_HOME zum Einsatz. Die neue Version der Deklaration der Startaktivität sieht folgendermaßen aus. Beachten Sie, dass das Projektskelett die betreffenden Activity-Attribute selbst nicht einschreibt, um den „Debug-Desktop“ von Android Things nicht zu deaktivieren:

<activity android:name=".HomeActivity">
...
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.HOME"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>

Änderung zwei ist, dass der Zugriff auf Hardware nun eine zusätzliche Permission voraussetzt. Sie lässt sich in der Manifestdatei auf die gewohnte Art und Weise deklarieren und ist nur insofern lästig, als ihr Fehlen zu einem nicht funktionierenden Programm führt:

<uses-permission android:name="com.google.android.things.permission.USE_PERIPHERAL_IO" />

Als Android-Things-erfahrene Entwickler wissen Sie, was an dieser Stelle als Nächstes kommt. Verbinden Sie ADB mit dem Prozessrechner und versuchen Sie danach den folgenden Code, der eine Liste aller I2C-Busse ausgibt (hier stark gekürzt), interessant ist nur die geänderte Beschaffung der PeripheralManager-Instanz:

PeripheralManager manager = PeripheralManager.getInstance();
List<String> deviceList = manager.getI2cBusList();
if (deviceList.isEmpty()) {
Log.i("SUS", "No I2C bus available on this device.");
}

Anders als andere Prozessrechner bietet der Raspberry Pi nur einen I2C-Bus an, der auf den Namen [I2C1] hört.

Das Ausgeben des Strings ist an dieser Stelle kein Selbstzweck, er wird für den eigentlichen Verbindungsaufbau benötigt. Hierzu ist zusätzlich eine numerische Adresse erforderlich, die das Gerät identifiziert:

try {
myDevice = manager.openI2cDevice(deviceList.get(0), 0x68);

}
catch (IOException e) { . . .

Android Things kennt bis jetzt keinen Weg, um die weiter oben besprochene Anpassung der Bus-Arbeitsfrequenz durchzuführen. Unter https://issuetracker.google.com/issues/65046298 findet sich immerhin ein Funktionsanforderungsthread, in dem die Android-Things-Entwickler auf das Fehlen hingewiesen werden.

Zudem taucht eine weitere Frage auf: Wie kommen wir an die Adresse beziehungweise wie lässt sich diese ändern? Eines der klassischen Probleme bei der Arbeit mit dem I2C-Bus ist, dass Halbleiterhersteller bei ihren Sensoren oft nur einen sehr eingeschränkten Bereich für die Vergabe der ID festlegen. Als Beispiel dafür dient Abbildung 5, die unseren hier verwendeten Sensor physikalisch zeigt.

image

Abb. 5: Jeder Pin kostet Chipfläche, Boarding-Draht und somit echtes Geld

Durch das Einsparen einzelner Pins lassen sich Kosten sparen, andererseits nimmt aber die Flexibilität ab. Unser Chip hat nur einen Adresspin, weshalb es nur zwei mögliche Adressen gibt. Transaktionen über I2C erfolgen registerweise. Hinter diesem Begriff verbirgt sich, dass wir eine Registeradresse schicken und danach einen Wert übertragen oder entgegennehmen, die im jeweiligen Ziel vorliegen muss. Unser hier verwendetes IS exponiert ein Register, in dem ein vom Hersteller fix vorgegebener Code zu liegen kommt. Als erste Aufgabe bietet es sich an, diesen auszulesen und so sicherzustellen, ob wir es auch wirklich mit einem MPU-9250 zu tun haben.

In AliExpress und Co. wird dasselbe Brett laut Board nämlich auch mit anderen Familienmitgliedern bestückt, die weniger Sensoren aufweisen und etwas anders zu programmieren sind:

public class MainActivity extends Activity {
int REG_WHO_AM_I = 0x75;
. . .
myDevice = manager.openI2cDevice(deviceList.get(0), 0x68);
int whoAmI = myDevice.readRegByte(REG_WHO_AM_I);
if(whoAmI==0x73) {
Log.i("SUS","MPU gefunden");
}

Spätestens an dieser Stelle ist es empfehlenswert, den weiter oben genannten Analyzer in der Hinterhand zu haben. Im Test kam es immer wieder zu merkwürdigem Verhalten, bei dem der Raspberry Pi die vom Sensor ausgegebenen Signale kurzfristig nicht interpretieren konnte.

Zur Erfassung der Daten reicht es im Allgemeinen aus, bei SCL auf eine steigende Flanke zu triggern (Kasten: „Analyse wider Willen“). Der I2C-Bus wird von Android Things selbst „in Ruhe gelassen“, weshalb alle Bewegungen des Clock-Pins automatisch auf eine Willensäußerung des von uns erzeugten Codes zurückzuführen sind.

Analyse wider Willen

Wenn ein I2C-System partout nicht tut, was es soll, so halten Sie eine zweite SD-Karte mit Raspbian vor. Starten Sie das Betriebssystem, installieren Sie das Paket „i2c-tools“ und führen Sie dann den Befehl i2cdetect aus. Er „scannt“ den angeschlossenen I2C-Bus und meldet die Adressen gefundener Gegenstellen in die Kommandozeile.

Daten auswerten

Das Authentifizieren des Chips mag eine reife Leistung sein, führt aber nicht wirklich zum Ziel. Wir wollen im Sensor befindliche Informationen auswerten. Eine komplette Auswertung sprengt den Rahmen des Artikels: Die Korrektur der Sensorwerte ist immens aufwendig. Wir wollen uns hier deshalb darauf beschränken, ein grundlegendes Framework anzulegen, mit dem Sie dann weitere Versuche durchführen können.

Als Erstes müssen wir dafür sorgen, dass wir die Ausführung des Kommunikationsprogramms anhalten können. Sensoren haben die unangenehme Eigenschaft, dass sie nicht jedes Kommando sofort verarbeiten können. Manche Schreibbefehle lösen eine Totzeit aus, während derer keine weiteren Orders eingehen können. Als erste Aufgabe müssen wir die Kommunikation deshalb in einem dedizierten Thread auslagern, der aus der Hauptmethode aufgerufen werden kann:

private void worker() throws InterruptedException, IOException {
myDevice.writeRegByte(PWR_MGMT_1, (byte) 0x00);
Thread.sleep(100);
myDevice.writeRegByte(PWR_MGMT_1, (byte) 0x01);
Thread.sleep(200);
myDevice.writeRegByte(CONFIG, (byte) 0x03);
myDevice.writeRegByte(SMPLRT_DIV, (byte) 0x04);

Sensoren starten meist inert. Im Fall unseres MPU besteht die erste Aufgabe unseres Programms darin, den Sensor darüber zu informieren, dass er nun Informationen bereitstellen muss. Neben dem eigentlichen Einschalten – es nimmt etwas Zeit in Anspruch, weshalb sich hier ein Aufruf von Delay befindet – sorgen wir für die Festlegung der Taktquelle und einiger anderer Parameter.

Besonders interessant ist, dass die im Datenblatt beschriebene Einrichtung stufenweise abläuft. Die Konfiguration erfolgt beispielsweise, indem man im ersten Schritt die bereits vom Hersteller angelegten Werte einliest, mit den gewünschten Parametern versieht und das Ganze dann in Richtung des Prozessrechners zurückschreibt:

  byte c = myDevice.readRegByte(GYRO_CONFIG);
c = (byte)(c & ~0xE0);
c = (byte)(c & ~0x03);
c = (byte)(c & ~0x18);
c = (byte)(c | 0 << 3);
myDevice.writeRegByte(GYRO_CONFIG, c );
Thread.sleep(100);

Nach getaner Arbeit geht unser Thread in eine Endlosschleife über, die den Inhalt des Statusregisters überprüft. In I2C ist es normalerweise so, dass der Slave den Master nur auf Wunsch des Masters kontaktieren kann. Aus diesem Grund haben viele Sensoren ein oder mehrere Register, die sich schnell abfragen lassen und darüber informieren, ob sich an anderer Stelle wertvolle Informationen befinden:

  for(int i=0;i<100;i=i){
if (0!=(myDevice.readRegByte(INT_STATUS) & 0x01)) {
Log.i("SUS", "New Data in!");
}
else
{
Log.i("SUS", "Nothing found!");
}
}
}

Im Fall unseres MPU gäbe es außerdem den Pin XXX, der nach der Fertigstellung eines Konversionsprozesses „getoggelt“ wird und theoretisch ebenfalls als Lesequelle dienen könnte.

Wer das vorliegende Programm ausführt, sieht in der Konsole eine Vielzahl abwechselnder Statusmeldungen vorbeiscrollen. Es ist offensichtlich, dass Android Things den hier platzierten Lesecode schneller abarbeitet, als der Sensor die Daten bereitstellen kann.

Die Praxis müssen Sie an dieser Stelle damit beginnen, die in den verschiedenen Registern befindlichen Werte einzusammeln und mit den vom Hersteller in den jeweiligen Chip eingeplanten Korrekturkonstanten sowie weiteren Kalkulationsdaten zu verarbeiten. Hierbei handelt es sich allerdings um höhere Mathematik, die mit Android Things selbst nichts zu tun hat.

Verschiedene Spannungen

Zu guter Letzt sei noch ein kleiner Exkurs erlaubt. Wir haben insofern Glück, als unser MPU auf dem Eval-Board mit 3,3 V arbeitet. In der Praxis kann es immer wieder erforderlich sein, I2C-Geräte miteinander zu verbinden, die in verschiedenen Spannungswelten leben. In diesem Fall ist es erforderlich, auf einen Spannungskonverter zu setzen. Erfreulicherweise ist sein Aufbau nicht besonders kompliziert. Abbildung 6 zeigt ein weit verbreitetes Beispiel, das seine Arbeit unter Nutzung von zwei Feldeffekttransistoren bewerkstelligt.

image

Abb. 6: Diese Schaltung könnte einfacher nicht sein (Bildquelle: NXP)

Fazit

Wir haben festgestellt, dass der I2C-Bus in Android Things unterstützt wird. I2C ist ein durchaus gutmütiges Protokoll, das im Allgemeinen keinerlei Probleme verursacht. Unsere Reise durch die Welt der von Android Things unterstützten Hardwareprotokolle ist damit allerdings noch nicht am Ende angelangt. Im nächsten Heft wenden wir uns klassischen seriellen Bussen zu und beschäftigen uns mit der Entgegennahme von Informationen, die auf RFID-Chipkarten vorliegen. In einem darauffolgenden Teil wenden wir uns abermals I2C zu, um Stromverbrauch und Echtzeiteignung zu verbessern. Kurzum: Es bleibt spannend. Bleiben Sie uns gewogen!

imageTam Hanna befasst sich seit der Zeit des Palm IIIc mit der Programmierung und Anwendung von Handcomputern. Er entwickelt Programme für diverse Plattformen, betreibt Onlinenewsdienste zum Thema und steht für Fragen, Trainings und Vorträge gern zur Verfügung.
Mail: tamhan@tamoggemon.com

Neugierig geworden?


   
Loading...

Angebote für Teams

Für Firmen haben wir individuelle Teamlizenzen. Wir erstellen Ihnen gerne ein passendes Angebot.

Das Library-Modell:
IP-Zugang

Das Company-Modell:
Domain-Zugang