Preiswerte Displays

Ein kleiner OLED-Bildschirm bereichert Android Things

Tam Hanna

© Shutterstock / Twin Design

Egal ob Industriesteuerung oder intelligente Kaffeemaschine: gutaussehende Infografiken sind und bleiben ein exzellenter „Verkaufstreiber“ für allerlei Hardware. Was einst unbezahlbar war, ist – Moore sei Dank – preiswert geworden.

Unser Raspberry Pi hat mit seinem HDMI-Port ein durchaus leistungsfähiges Grafiksystem, das sowohl Videos als auch OpenGL-Grafiken dezenter Komplexität darstellt. Leider sind HDMI-taugliche Bildschirme nach wie vor teuer – selbst dann, wenn man aus China importiert und sich mit sehr kleinen Displays zufriedengibt, kommt man nur schwer unter 30 bis 40 Euro.

Eine Ebene darunter finden sich kleine organische Displays, die klassischerweise 128 x 64 Pixel auflösen. Die mit 128 x 128 Pixel aufgelösten größeren Brüder wollen wir in diesem Artikel nicht betrachten, farbige Displays kommen ebenfalls erst später zur Sprache.

Die Verwendung der auf dem von Solomon Systech entwickelten SSD1306-Controller basierenden Displays ist eine Möglichkeit, Informationen schnell in Richtung Nutzer zu tragen. Zudem lässt sich auf diese Weise mehr über den SPI-Bus herausfinden, der in der Welt der Elektronik ebenfalls eine nicht unbedeutende Rolle spielt.

Der I2C-Bus ist darauf optimiert, möglichst viele Endstellen mit individuell geringen Bandbreitenansprüchen zu befriedigen. SPI wählt einen komplett anderen Ansatz: Das Serial Peripheral Interface wendet sich an all jene, die mit einem oder zwei Peripheriegeräten möglichst schnell kommunizieren wollen. Die Lösung des Problems ist die in Abbildung 1 gezeigte Verbindung zwischen den Endgeräten.

Abb. 1: SPI setzt im Vollausbau vier Leitungen voraus (Quelle: Wikimedia Commons/cburnett)

Abb. 1: SPI setzt im Vollausbau vier Leitungen voraus (Quelle: Wikimedia Commons/cburnett)

Hinter MISO – Master in, Slave out – verbirgt sich eine Leitung, die Informationen vom Sklaven zum Meister überträgt. MOSI, kurz für Master out, Slave in, ist für den Rückkanal verantwortlich. Die eigentliche Datenübertragung erfolgt prinzipiell durch einen vom Master vorgegebenen Takt und wandert zwischen den beiden Kommunikationspartnern über die Leitung SCLK (Serial Clock).

Da jede der Leitungen nur von einem der Endgeräte gesteuert und vom anderen Endgerät gelesen wird, sind hier keine Open-Drain-Treiber erforderlich. Das scheint vernünftig, da das Hochziehen der Leitung in Richtung der Versorgungsspannung mit Leistung erfolgen kann. Die in Abbildung 2 gezeigten und an Kondensatoren erinnernden, langsamen Ladevorgänge sind nicht erforderlich, was SPI wiederum wesentlich höhere Übertragungsraten ermöglicht.

Abb. 2: Die Leitungskapazität verlangsamt den I2C-Bus

Abb. 2: Die Leitungskapazität verlangsamt den I2C-Bus

Problematisch ist, dass diese Verbindung immer nur einen Sklaven pro Port ansprechbar macht. Die CS-Leitung, kurz für Chip Select, ermöglicht das Anschließen mehrerer Geräte an einen Bus.

Motorola vergaß bei der Spezifikation die Zuordnung zwischen den Flanken und den Zuständen. Daraus folgt, dass es vier verschiedene SPI-Betriebsmodi gibt, die als Mode-Null bis Mode-Drei bezeichnet werden. In der Praxis findet sich in den Datenblättern fast immer Information darüber, welchen Mode ein bestimmtes Endgerät voraussetzt. Können diese aus irgendeinem Grund nicht gefunden werden, besteht normalerweise die Möglichkeit, einfach alle vier Betriebszustände durchzuprobieren, um den passenden zu finden.

In die Praxis!

Nach dieser eher theoretischen Vorstellung des Busses stellt sich die Frage, wie sich das Display mit dem Prozessrechner verbinden lässt. Der im letzten Teil der Serie verwendete Temperatursensor soll abermals mit von der Partie sein, da wir realistische Informationen auf den Bildschirm bringen wollen. Die resultierende Schaltung präsentiert sich wie in Abbildung 3 gezeigt.

Abb. 3: Langsam wird es auf der Steckplatine kuschelig

Abb. 3: Langsam wird es auf der Steckplatine kuschelig

Aufmerksame Beobachter fragen sich, warum hier eine zusätzliche, mit DC beschriftete Leitung verläuft. Der Grund dafür ist eine kleine Optimierung, die für Displaycontroller aus dem Hause Solomon Systech charakteristisch ist.

Zwischen dem Prozessrechner und dem Display übertragene Informationen können zwei Typen haben: einerseits können es Steuerbefehle sein, die den Controller beispielsweise bei der Auswahl der Spannungsversorgung beeinflussen. Andererseits können die eigentlichen Displayinformationen (Framebuffer) zu übertragen sein. Um nicht bei jedem einzelnen Datenwort eine zusätzliche Richtungsinformation übertragen zu müssen, setzt man stattdessen auf das Data-Control-Pin. Ist es low, wandern die Informationen in den Kommandospeicher. Zieht man es stattdessen high, rechnet der Controller mit der Übertragung von Bilddaten.

An dieser Stelle sind wir dazu bereit, unser Display mit dem Prozessrechner zu verbinden und es zu aktivieren. Realisieren Sie die in Abbildung 3 gezeigte Schaltung auf einem Steckbrett und öffnen Sie die aus dem vorigen Teil übrig gebliebene Solution. Wie im letzten Teil müssen Sie den Raspberry Pi auch diesmal mit dem PC verbinden, da Android Studio per Internet ansprechbare Geräte nicht automatisch erkennt:

tamhan@TAMHAN14:~/Android/Sdk/platform-tools$ ./adb connect 192.168.1.103
* daemon not running; starting now at tcp:5037
* daemon started successfully connected to 192.168.1.103:5555

Führen Sie an dieser Stelle einen Test durch, um sich davon zu überzeugen, dass die Erfassung der Temperaturwerte wie erwartet funktioniert. Steckplatinen haben die unangenehme Eigenschaft, im Laufe der Zeit zu korrodieren [1].

Android-Things-erfahrene Entwickler wundern sich sicher nicht darüber, dass auch die SPI-Busse über Gerätestrings identifiziert werden. Zu ihrer Suche wird der Code aus Listing 1 ausgeführt.

@Override
protected void onCreate(Bundle savedInstanceState) { . . .
  PeripheralManager manager = PeripheralManager.getInstance();
  try {
    List<String> deviceList = manager.getSpiBusList();
    if (deviceList.isEmpty()) {
      Log.w("SUS", "Kein SPI-Gerät gefunden");
    } else {
      Log.i("SUS", "List of available devices: " + deviceList);
    }

An sich handelt es sich hier nicht um Raketenwissenschaft – wir bitten den Peripheral Manager darum, alle SPI-Busse zurückzuliefern. Danach senden wir die Resultate in Richtung der LogCat-Konsole. Wer das Programm ausführt, sieht das in Abbildung 4 gezeigte Resultat.

Abb. 4: Android Things findet am Raspberry Pi keine SPI-Interfaces

Abb. 4: Android Things findet am Raspberry Pi keine SPI-Interfaces

Die Ursache für dieses seltsame Verhalten liegt in der im letzten Teil durchgeführten Onlinekonfiguration der Peripheriegeräte. Wer im Backend „Hardware“ einstellt, darf zur Laufzeit nur Peripheriegeräte verwenden, die er vorher als „relevant“ markiert hat.

Da muss Chrome geöffnet und in die Android-Things-Konsole zurückgekehrt werden. Anschließend muss das Modell skaliert und die Liste der Build-Konfigurationen geöffnet werden. Nun klickt man auf den grünen New-Knopf und entscheidet sich für die Option Start from Scratch. Die Konsole legt nun eine komplett neue Konfiguration an, die parametriert werden kann.

Entscheiden Sie sich abermals für die Betriebssystemversion OIM1.180327.070, und klicken Sie die restlichen Einstellungen durch. Im fünften Schritt klicken wir diesmal auf Next, um den Hardwarekonfigurationsassistenten zu überspringen. Legen Sie den neuen Build danach wie gewohnt an, brennen ihn auf eine SD-Karte und starten Sie den Prozessrechner neu.

Hier noch ein kleiner Hinweis: Wenn Sie Ihren Raspberry Pi schon jetzt ohne Monitor betreiben (headless), können Sie den Prozessrechner unter Nutzung von nmap finden. Befindet sich Ihre Workstation beispielsweise auf der IP-Adresse 192.168.1.2, lautet der korrekte Befehl wie in Listing 2 dargestellt.



tamhan@tamhan-thinkpad:~$ sudo nmap -sP 192.168.1.0/24
nbsp;
Starting Nmap 6.40 ( http://nmap.org ) at 2019-01-13 11:42 CET
. . .
Nmap scan report for 192.168.1.102
Host is up (0.00060s latency).
MAC Address: B8:27:EB:87:80:71 (Raspberry Pi Foundation)

 

Das Voranstellen von sudo ist unbedingt erforderlich, da aktuelle Versionen von nmap bei einem normalen Aufruf keine MAC-Adressen in die Kommandozeile emittieren. Die Korrektheit der ausgegebenen IP-Adresse wird danach durch Aufrufen von adb connect überprüft – wir müssen den Prozessrechner wie immer von Hand verbinden. Nach der abermaligen Ausführung sehen wir, dass der SPI-Bus Null zwei Endgeräte anspricht:

I/SUS: List of available devices: [SPI0.0, SPI0.1]

Das lässt sich übrigens auch im Pinout des Raspberry Pi überprüfen, wo wir zwei CS-Pins finden.

Bereitstellung des Grafiktreibers

Da die MainActivity mit dem Code für den Temperatursensor „voll“ ist, realisieren wir für das SSD1306 eine neue Klasse. Ihr Korpus beginnt mit der Deklaration eines SPIdevice und einer Gruppe von GPIO-Klassen:

public class SSD1306Driver {
  SpiDevice myDevice;
  Gpio myPinReset, myPinDataCommand;

Google setzt die Vorgehensweise der Deklaration von „Akzessorklassen“ auch im SPI-Treiber rigoros um. Das Betriebssystem repräsentiert die Verbindung zu unserem Display durch eine Instanz der Klasse SpiDevice. Die eigentliche Initialisierung erfolgt anschließend im Rahmen des Konstruktors, der aus der Haupt-Activity im Rahmen von onCreate aufgerufen wird (Listing 3).

public SSD1306Driver(){
  PeripheralManager manager = PeripheralManager.getInstance();
  try {
    myPinReset = manager.openGpio("BCM18");
    myPinReset.setDirection(Gpio.DIRECTION_OUT_INITIALLY_LOW);
    myPinReset.setValue(false);
    myDevice = manager.openSpiDevice("SPI0.0");

Nach der Initialisierung des Reset-Pins beginnen wir mit der Erzeugung der SPIDevice-Abstraktionsklasse. Aufgrund der oben genannten Unschärfen in Motorolas Standarddefinition müssen wir im ersten Schritt den Modus festlegen, der für die Kommunikation mit dem Display zum Einsatz kommt.

Von besonderem Interesse ist der Aufruf von setFrequency, der für das Festlegen der Arbeitsfrequenz des Busses verantwortlich ist. Der Autor treibt seine Displays mit 4 MHz an – allerdings funktioniert bei nicht allzu langen Leitungen in der Praxis auch 8 MHz ohne Probleme. An dieser Stelle sei angemerkt, dass Need for Speed im Elektronikbereich nichts zu suchen hat. Wer unnötig hohe Taktraten oder unnötig schnelle Bauteile nutzt, provoziert Probleme im Bereich EMV. Mehr als ein Design lässt sich durch Reduktion der Taktrate beruhigen (Listing 4).





    myDevice.setMode(SpiDevice.MODE0);
    myDevice.setFrequency(4000000);
    myDevice.setBitsPerWord(8);
    myDevice.setBitJustification(BIT_JUSTIFICATION_MSB_FIRST);
    Thread.sleep(10);
    myPinReset.setValue(true);
  }
  catch (Exception e) { . . .
}

Eine kleine Nettigkeit findet sich in der Einrichtung des Reset-Pins. Direkt nach seiner Aktivierung ziehen wir ihn auf low, um den Controller zum Durchführen eines Reset-Vorgangs zu animieren. Am Ende des Konstruktors muss dann nur noch einige Millisekunden abgewartet und das Pin high gezogen werden, um den Chip in einen definierten Zustand zu bringen.

Diese auf den ersten Blick pedantisch erscheinende Vorgehensweise rentiert sich in der Praxis – man weiß nie, in welchem Betriebszustand ein Chip nach dem erstmaligen Anlegen der Versorgungsspannung hochfährt. Erledigt man den Reset während der Einrichtung sonstiger Peripheriegeräte, spart man Zeit und umgeht Probleme mit seltsamen Ausgaben im Rahmen der Konfiguration.

Auf die Initialisierung des Data-Control-Pins wird an dieser Stelle nicht eingegangen. Wie im letzten Artikel gilt auch diesmal, dass die angelegten Abstraktionsklassen vor dem Schließen des Programms zu eliminieren sind. Im Interesse der Bequemlichkeit setzen wir diesmal auf die finalize-Methode – in einem produktiven Programm sollten Sie eine robustere Art der Beseitigung wählen:

protected void finalize( ) throws Throwable{
  myDevice.close();
  myPinDataCommand.close();
  myPinReset.close();
}

Für die Kommunikation mit dem Displaycontroller benötigen wir einerseits ein Array, das die Framebuffer-Informationen vorhält. Andererseits benötigen wir eine Gruppe von Kommandosequenzen, die das Aktivieren bestimmter Module im Chip erlauben. Wir legen beide Gruppen als Member der Treiberklasse an (Listing 5).




byte[] myFramebuffer=new byte[128*64/8];
byte[] C_DISPLAY_ON = { (byte)0xAF };
byte[] C_CHARGEPUMP_ON = { (byte)0x8D, (byte)0x14 };
byte[] C_MEMADDRMODE = { (byte)0x20, (byte)0x00 };
byte[] C_RESETCOLADDR = { (byte)0x21, (byte)0x00, (byte)0x7F };
byte[] C_RESETPAGEADDR = { (byte)0x22, (byte)0x00, (byte)0x07 };

Die hier gezeigte Verwendung von Byte-Arrays zur Sequenzierung längerer Initialisierungssequenzen erweist sich unter Android Things als Design Pattern, das sich auch in von Google bereitgestelltem Samplecode immer wieder findet. Wer C-Code konvertiert, kann die zusätzlich notwendige Syntax zudem leicht mit einem Editor einpflegen.

Als Nächstes folgt die Methode SendCommand, die für das Übertragen eines Kommandos verantwortlich ist (Listing 6).



void SendCommand(byte[] _what)
{
  try {
    myPinDataCommand.setValue(false);
    myDevice.write(_what, _what.length);
  } . . .

Ihre erste Amtshandlung besteht darin, den Data-Command-Pin auf low zu ziehen. Dadurch erfährt der SSD1306, dass die folgenden Befehle in den Kommandospeicher wandern. Darauf fehlt nur noch ein Aufruf von write. Die Methode animiert den SPI-Treiber des Raspberry Pi dazu, die übergebene Bytesequenz in Richtung des Sklaven zu schicken – dabei vom Sklaven in Richtung des Meisters gesendete Informationen würden kommentarlos verworfen.

Die Übertragung des Framebuffers erfolgt im Prinzip analog. SendData unterscheidet sich nur dadurch von ihrem Kollegen, dass das Data-Command-Pin vor der Übertragung auf True gesetzt wird (Listing 7).



void SendData(){
  try {
    myPinDataCommand.setValue(true);
    myDevice.write(myFramebuffer, myFramebuffer.length);
  } . . .

Im Interesse der Vollständigkeit sei angemerkt, dass unser Framebuffer aus Sicht des großen Ganzen keine besonders große Datenmenge darstellt. Im nächsten Heft sehen wir Situationen, in denen zur Übertragung eine etwas andere Vorgehensweise erforderlich ist.

OLEDs sind bösartig

Der Autor dieser Zeilen unterstützte Samsung vor vielen Jahren im Rahmen des SDA-Programms: Man sollte Entwicklern die Erzeugung von Programmen für Bada schmackhaft machen. Das als Entwicklerkit dienende Wave I war mit einem sehr hochwertigen OLED-Display ausgestattet, das dem Autor und seinen Kollegen in der Praxis allerdings Arbeit machte.

Andere Smartphones der damaligen Zeit – denken Sie an Treo und Co. – hatten ein klassisches LCD, dass auch im leeren Zustand durch die Hintergrundbeleuchtung aktiv erschien. Ein organisches Display unterscheidet sich davon radikal – ist sein Bildschirminhalt schwarz, ist es von einem ausgeschalteten Display visuell nicht zu unterscheiden.

Daraus folgt, dass man bei der Arbeit mit organischen Displays bestrebt sein sollte, einen variablen Framebuffer anzuliefern, der mit verschiedenen Bildsequenzen gefüllt ist.

Die Initialisierung unseres Bildschirms beginnt mit der Übertragung einiger Steuerkommandos, die die Stromversorgung des OLED-Elements sicherstellen (Listing 8).


public void initDisplay()
{
  try {
    Thread.sleep(100);
    SendCommand(C_CHARGEPUMP_ON);
    SendCommand(C_MEMADDRMODE);
    SendCommand(C_DISPLAY_ON);

Da das GPIO-API alles andere als schnell ist, bietet es sich an, über ein Flag unnötige Schaltvorgänge des DC-Pins zu vermeiden. Da wir allerdings pro Sekunde nur einen Frame zu übertragen haben, wollen wir auf diese Optimierung verzichten und uns stattdessen der Ausgabe von Informationen zuwenden.

Die Aufgabe der for-Schleife besteht darin, den Framebuffer in einen definierten Zustand zu bringen. Wir entscheiden uns hier für das Wort 127, weil es aus gesetzten und nicht gesetzten Bits besteht (Listing 9).


for(int i=0;i<myFramebuffer.length;i++){
      myFramebuffer[i]=(byte)127;
    }
    SendData();
  }
  catch(Exception x){Log.e("SUS",x.getMessage());}
}

Der Sinn dieser pedantisch wirkenden Vorgehensweise ist, dass wir Rückschlüsse auf die Zusammenschaltung des Framebuffers und der eigentlichen Pixel erhalten. Die praktische Erfahrung lehrt, dass die physikalische Anordnung nicht immer mit der übereinstimmt, die man im Speicher des Display-Controllers findet.

Für die Einbindung der Display-Methode ist nur wenig Aufwand erforderlich. Adaptieren Sie einfach onCreate:

protected void onCreate(Bundle savedInstanceState) {
  . . .
  mySSD1306 = new SSD1306Driver();
  mySSD1306.initDisplay();
}

An dieser Stelle ist unser Programm zur Ausführung bereit. Schicken Sie es auf den Prozessrechner, um sich am in Abbildung 5 gezeigten Ergebnis zu erfreuen.

Abb. 5: Der Framebuffer ist vertikal ausgerichtet

Abb. 5: Der Framebuffer ist vertikal ausgerichtet

Eine Frage der Anzeige

Unsere nächste Amtshandlung ist das Anzeigen des Temperaturwerts, der bisher nur am HDMI-Display zu sehen war. Wer bisher mit klassischen Mikrocontrollern gearbeitet hat, lernt an dieser Stelle mehrsprachig fluchen – die Bereitstellung von TrueType-Fonds über Bitmaps artet in Arbeit aus. Erfreulicherweise erleichtert uns das zugrunde liegende Android-Betriebssystem die Arbeit, weshalb renderString beginnt wie in Listing 10 dargestellt.

public void renderString(String _x){
  Bitmap returnedBitmap = Bitmap.createBitmap(128, 64,Bitmap.Config.ARGB_8888);
  Canvas canvas = new Canvas(returnedBitmap);
  Paint aPaint=new Paint();
  aPaint.setTextSize(22);
  aPaint.setColor(Color.BLACK);
  canvas.drawText(_x,2,60, aPaint);

Im ersten Schritt nutzen wir die Methode createBitmap, um ein im Arbeitsspeicher lebendes Bitmap zu erzeugen. Es dient danach als Quelle für ein Canvas-Element, mit dem wir den angelieferten String in das Bitmap schreiben.

Das offline Zusammenstellen von Frames außerhalb des Framebuffers ist übrigens ein durchaus geläufiges Verfahren in der Programmierung und insbesondere in der Welt der Spiele als Double Buffering bekannt.

Im nächsten Schritt haben wir das Problem, dass wir die Informationen von Bitmap übertragen müssen. Aufgrund des in Abbildung 5 gezeigten Zusammenhangs können wir diese nicht direkt kopieren. Stattdessen benötigen wir eine Gruppe von Schleifen, die das Bitmap umgekehrt einliest (Listing 11).

  int counter=0;
  int store=0;
  int counter2=0;
  for(int ctr=0;ctr<8;ctr++) {
    for (int x = 0; x < returnedBitmap.getWidth(); x++) {
      for (int y = ctr * 8; y < (ctr + 1) * 8; y++) {
        int myVal = returnedBitmap.getPixel(x, y);
        if (myVal == 0) {
          //makes it white
          store = (store | ( (1 << (counter))));
        }

Die Situation wird dadurch erschwert, dass unser Framebuffer pro Byte acht Pixel beschreibt. Der vorliegende Code löst das Problem dadurch, dass er eine als Store bezeichnete Variable anlegt. Sie wird von einem Zähler überwacht – wenn 8 Bits zusammengefasst sind, wandern die Informationen in den Framebuffer (Listing 12).

counter++;
        if (counter == 8) {
          myFramebuffer[counter2] = (byte)store;
          store = 0;
          counter = 0;
          //Byte raus
          counter2++;
        }
      }
    }
  }
  SendCommand(C_RESETCOLADDR);
  SendCommand(C_RESETPAGEADDR);
  SendData();
}

Vor der eigentlichen Übertragung senden wir die Kommandos C_RESETCOLADDR und C_RESETPAGEADDR. Sie sorgen dafür, dass der Schreibzeiger an den Anfang des Felds zurückwandert und die eingehenden Informationen genau am Bildschirm landen.

Zur Ausgabe der Temperaturwerte müssen wir das Runnable anpassen (Listing 13).

private Runnable runnable = new Runnable(){
  public void run() {
    try {
      . . .
      myView.setText(c.toString() +  " °C");
      mySSD1306.renderString(c.toString() +  " °C");
      handler.postDelayed(runnable, 1000);
    }catch (Exception e) {
      Log.w("SUS", "Runnable-Fehler!", e);
    }
  }
};

An dieser Stelle ist auch diese Version des Programms zur Ausführung bereit. Abbildung 6 zeigt das Resultat, das Sie auf der Steckplatine des Autors erwarten können.

Abb. 6: Die ausgegebene Schrift steht Kopf

Abb. 6: Die ausgegebene Schrift steht Kopf

In der Theorie könnten wir den Framebuffer im RAM umdrehen, um eine für uns angenehmere Darstellung zu erreichen. In der Praxis ist das nicht erforderlich – adaptieren Sie die Methode initDisplay, um das Verhalten des Display-Controllers zu adaptieren. Im Datenblatt zum Bauteil findet sich eine Gruppe von Kommandos, mit denen sich das Problem beheben lässt.

Fazit

Die Darstellung von TrueType-Schriftarten auf Displays ist eine Situation, in der Android Things brilliert. Dank des leistungsfähigen Betriebssystems müssen Sie sich um das Rendering keine Sorgen machen.

Noch interessanter wäre es, statt Texten ganze Diagramme rendern zu können. Wer häufig für Android programmiert, weiß mit Sicherheit über die Verfügbarkeit verschiedener Diagrammbibliotheken Bescheid. Im nächsten Heft testen wir eine dieser Diagrammibliotheken und schalten zudem eine vollfarbige Version unseres Displays ein. Unsere Gefechte in der Welt des Internet of Things bleiben also spannend – bleiben Sie uns gewogen und bis zum nächsten Mal.

Geschrieben von
Tam Hanna
Tam Hanna
Tam 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 unter tamhan@tamoggemon.com für Fragen, Trainings und Vorträge gern zur Verfügung.
Kommentare

Hinterlasse einen Kommentar

avatar
4000
  Subscribe  
Benachrichtige mich zu: