Raspberry Pi und Attiny 84V über I2C verbinden

Will man die Funktionalität seines Raspberry Pi um einen Atmel Mikrocontroller, z.B. Atmega oder Attiny erweitern, braucht man irgendeine Kommunikationsstrategie. Ich habe mich hierbei für eine I2C-Schnittstelle entschieden, da eine eigene Implementierung doch sehr umständlich ist (man muss immer auf einen Takt o.Ä. achten und somit doch ein komplettes Protokoll implementieren).

I2C

Für I2C hingegen existieren schon diverse Implementierungen sowohl für Master- als auch für Slave-Systeme. I2C ist ein Protokoll, welches zwei Busleitungen benötigt: Adress- und Datenleitung. Über diese kommunizieren Master und Slaves miteinander. Die Slaves reagieren jedoch nur auf Befehle des Masters und agieren nicht von sich aus. Der Vorteil von I2C ist jedoch, dass man viele Slaves an dieselbe Leitung hängen kann. Man benötigt am Master trotzdem nur zwei Pins.

Manche Mikrocontroller von Atmel unterstützen bereits selbst I2C. Die Attiny-Reihe hat häufig eine sogenannte USI-Schnittstelle (Universal Serial Interface). Über diese können mit Software relativ gut verschiedene serielle Protokolle implementiert werden.

Ich habe mich bei meinem Projekt entschieden, den Raspberry Pi als Master zu betreiben und den Attiny als Slave. Hierbei soll der Attiny Benutzereingaben einlesen und später gesammelt an den Pi zurückgeben. Später sollen noch weitere Mikrocontroller für Ausgabeanzeigen hinzukommen (z.B. Display oder LEDs). Deshalb wird der Raspberry Pi als Master betrieben.

Raspberry Pi vorbereiten

Der Raspberry Pi lädt in seiner Standardeinstellung das Modul für I2C noch nicht. Unter ArchlinuxARM kann man dies laden, indem man i2c-tools installiert und die Kernelmodule i2c-dev und i2c-bcm2708 lädt. Wichtig ist außerdem (und das stand in keinem Tutorial, das ich gelesen habe), dass man die Baudrate korrekt einstellt. Bei mir war diese für den Attiny viel zu hoch, sodass dieser mit der Kommunikation nicht hinerher kam.

  • i2c-tools installieren
  • in /etc/modules-load.d/raspberrypi.conf die beiden Module i2c-bcm2708 und i2c-dev eintragen
  • Datei /etc/modprobe.d/i2c-bcm2708.conf anlegen und dort options i2c-bcm2708 baudrate=4000 eintragen (4000 ist so ziemlich der geringste mögliche Wert, den kann man sicherlich noch höher stellen)
  • Raspberry neustarten oder die Module per modprobe alle noch laden

I2C auf dem Attiny

Für den Attiny existieren auch verschiedene Implementierungen von I2C-Slave. Ich habe mich mit i2cslave für eine entschieden, bei der man mit Speicherbereichen/Registern arbeitet, die sowohl geschrieben als auch gelesen werden. Dies scheint mir eher dem Prinzip von I2C zu entsprechen, wie ich es in den i2c-tools kennengelernt habe.

Jedoch gibt es auch Implementierungen, die mit zwei getrennten In- und Out-Buffern arbeiten. Nicht alle Implementierungen sind jedoch für jeden Attiny-Typ geeignet. In der obigen sieht man z.B. in i2cslave.h Regeln für verschiedene Attiny. Hierbei wird für jeden Attiny angegeben, welcher PIN welche Funktion übernimmt. Solche Regeln muss man in anderen Implementierungen evtl. für den eigenen Attiny-Typ ergänzen.

Um die gewählte Implementierung zu verwenden, muss man dann die Header-Datei im eigenen Programm inkludieren und die c-Datei zusammen mit dem eigenen Quelltext kompilieren.

Attiny und Raspberry Pi verkabeln

Verbindung von Pi und Attiny. Die Pin-Nummern im Bild stimmen nicht mit den eigentlichen Pin-Nummern überein.

Verbindung von Pi und Attiny. Die Pin-Nummern im Bild stimmen nicht mit den eigentlichen Pin-Nummern überein.


Die Verkabelung beider Bausteine ist dann grundsätzlich einfach. Man muss die beiden SLC- und SDA-Pins verbinden und den Attiny korrekt an die Stromquelle anschließen. Hierbei empfiehlt es sich, den Attiny mit nur 3,3V Versorgungsspannung zu verbinden (sofern er es unterstützt). Somit benötigt man keine Spannungswandler, weil der Attiny dann auch 3,3V als HIGH-Pegel verwendet.

Nachdem ich die Baudrate heruntergesetzt hatte (siehe oben), habe ich gute Erfahrungen sowohl mit eigenen Pullup-Widerständen (1,5kOhm) als auch mit den internen Pullup-Widerständen des Raspberry Pi alleine gemacht. Hier kann ich also im Moment keine klare Empfehlung geben. Da an die Widerstände jedoch nach der Länge der Leitung und der Anzahl an Slaves berechnen soll, muss man hier eventuell externe Widerstände einplanen, wenn man mehrere Slaves anschließen will.

Ein-Aus-Schalter für den Raspberry Pi

Im Internet gibt es bereits einige Tutorials, wie man den Raspberry Pi sauber herunterfahren und danach auch wieder hochfahren kann. Diese verwenden jedoch alle zwei getrennte Knöpfe zum Herunterfahren und zum Hochfahren.

Dies ist grundsätzlich einfacher, da beide Vorgänge komplett unterschiedlich funktionieren, jedoch nicht sehr intuitiv. Will man das Board nur zum Entwickeln nutzen, ist diese Variante möglicherweise sinnvoll, bei einem Endprodukt jedoch nicht (z.B. mein Internetradio).

Hoch- und Runterfahren

Das Herunterfahren des Raspberry Pi muss aus dem Betriebssystem heraus durch das shutdown-Kommando erfolgen. D.h. man muss mit einem GPIO-Pin auf eingehende Signale lauschen und prüfen, ob ein Taster gedrückt wurde oder nicht. Sobald der Taster gedrückt wurde, kann man shutdown -h now aufrufen.

Um den Pi anschließend wieder hochzufahren, muss man die RESET-Pins des Raspberry Pi verwenden. Verbindet man diese, führt der Raspberry Pi ein Reset durch. Da er bereits heruntergefahren ist, entspricht dies einem Hochfahren.

Anstatt die beiden Pins zu verbinden, kann man jedoch auch nur den weiter am Rand des Boards liegenden Pin (ich vermute Pin 1) auf 0V ziehen.

Vorbereitung

Standardmäßig existieren für diese beiden Pins jedoch noch keine Steckstifte. Man muss sich selbst eine 2-Pin-Stiftleiste auf die entsprechenden Anschlüsse des P6 löten.

IMG_6080

Hierzu führt man die Stiftleiste erst durch die Löcher hindurch und lötet sie dann von unten her fest.

Schaltung

Wie oben geschrieben, sollen beide Kommandos durch nur einen Knopf möglich sein.

  • Ist der Pi hochgefahren: Herunterfahren
  • Ist der Pi heruntergefahren: Hochfahren

Hierzu wird von irgendwoher ein Signal benötigt, in welchem Zustand sich der Raspberry Pi im Moment befindet. Da ich nicht herausfinden konnte, ob der Pi von sich aus so ein Signal anbietet, habe ich einen GPIO-Ausgang eingeplant. Dieser steht auf HIGH, wenn der Raspberry Pi läuft. Beim Herunterfahren schaltet er dann auf LOW um.

Wie in den Beschreibungen zum Hoch- und Runterfahren erläutert, erfolgt das Herunterfahren durch Signal an einem GPIO-Pin. Das Hochfahren durch Signal am P6-Anschluss. Zusammengefasst gibt es also zwei Pins, die Signale empfangen sollen – einer bei laufendem Pi, einer bei ruhendem Pi.

IMG_6083

Für die Eingabe des Shutdown-Signals habe ich GPIO-Pin 9 gewählt. Die Ausgabe, ob der Pi hoch- oder runtergefahren ist, wird auf GPIO-Pin 10 gesendet.

Der Shutdown-Pin kann jedoch auch im heruntergefahrenen Zustand Signale erhalten – er kann sowieso noch nicht reagieren. Insofern ist nur wichtig, dass RESET nicht ausgeführt wird, wenn der Pi läuft.

Ob RESET ein Signal erhalten soll, kann dann durch einen Transistor gesteuert werden.

Raspberry-Pi-Ein-Aus-Schalter

Grundsätzlich liegt bei dieser Schaltung am GPIO-9 über einen Pulldown-Widerstand Ground an. Wird der Taster betätigt, liegt am GPIO-9 HIGH an.

Die beiden Transistoren kümmern sich um die korrekte Beschaltung von RESET. Solange GPIO-9 auf HIGH steht, schaltet der linke Transistor durch. Dadurch liegen im gesamten Bereich unten rechts 0V an. Das Gate des rechten Transistors steht also auch auf 0V und schaltet nicht durch.

Interessant wird es, wenn GPIO-9 auf LOW steht – d.h. der Pi heruntergefahren ist. Dann sperrt der linke Transistor. Solange der Taster nicht gedrückt ist, gelangt über zwei 10kOhm-Widerstände jedoch immer noch GND an das Gate des rechten Transistors. Erst wenn der Taster betätigt wird, überwiegt die Spannung von 3,3V und der rechte Transistor schaltet durch. RESET wird auf GND gezogen und der Pi fährt hoch.

Software

Die Ein- und Ausgaben müssen natürlich auch noch in Software gesteuert werden. Dazu habe ich ein kleines Script erstellt, welches beim Booten als Daemon gestartet wird.

Als Programmiersprache verwende ich Python. Im Grunde setze ich beim Starten des Skripts nur GPIO-10 auf HIGH, um anzuzeigen, dass der Pi nun läuft.

Dann warte ich auf Signale am GPIO-9. Wichtig ist hierbei auf die fallende Flanke zu achten, also wenn der Knopf losgelassen wird. Ansonsten fährt der Raspberry Pi beim Herunterdrücken schon runter und setzt GPIO-10 auf LOW. Der Benutzer hält den Knopf noch gedrückt, der RESET-Transistor schaltet durch (da GPIO-10 ja auf LOW steht) und der Pi führt einen Reset durch statt eines sauberen Shutdowns.

Um zusätzlich noch Reset-Fehler durch Bouncing (also mehrmaliges Springen des Signals, wenn man einen Knopf drückt oder loslässt) zu verhindern, warte ich beim Herunterfahren noch eine Sekunde ab, bevor GPIO-10 auf LOW gesetzt wird.

#!/usr/bin/python2
 
import RPIO
import subprocess
import time
 
RPIO.setup(9, RPIO.IN)
RPIO.setup(10, RPIO.OUT, initial=RPIO.HIGH)
 
def shutdown(gpio_id, val):
        time.sleep(1) # wait a while to avoid resetting due to signal bouncing
        RPIO.output(10, False)
        subprocess.call(['shutdown', '-h', 'now'])
 
RPIO.add_interrupt_callback(9, shutdown, edge='falling')
 
RPIO.wait_for_interrupts()

Dieses Skript kann man unter ArchlinuxARM mittels systemd automatisch starten. Hierzu muss man sich eine Datei unter /etc/systemd/system erstellen, z.B. /etc/systemd/system/piboot.service.

[Unit]
Description=Control and listen to boot and shutdown

[Service]
ExecStart=/path/to/python/script.py

[Install]
WantedBy=multi-user.target

Unter ExecStart muss man als absoluten Pfad eintragen, wo die obige Python-Datei liegt. Diese Datei muss ausführbar sein (also muss man ggfs. chmod +x ausführen).

Dann kann man den Dienst mittels systemctl start piboot starten und mittels systemctl enable piboot für jeden Start aktivieren.

Anmerkung

Gestern traten bei mir selten Signalsprünge auf, die zum Herunterfahren führten, obwohl ich den Knopf nicht betätigt hatten. Diese lassen sich vermutlich durch einene Kondensator im Schaltkreis oder aber durch eine Zeitprüfung in der Software verhindern.

Internetradio mit dem Raspberry Pi

IMG_6075

Der Raspberry Pi eignet sich hervorragend, um ein eigenes Internetradio zu bauen. Er bietet selbst ein komplettes Betriebssystem (mit z.B. Internet-Verbindung und Audio-Decoding) und auch GPIO-Pins, um Signale von anderen Bausteinen zu empfangen und zu senden.

Ich will hier nicht genau auf die einzelnen Bausteine eingehen, sondern werde die groben Bestandteile vorstellen und auf die detaillierten Anleitungen verweisen.

Lautsprecher

Zunächst einmal ist klar, dass irgendwelche Lautsprecher benötigt werden. Will man die Ummantelung des Radios (also z.B. eine Kiste) selbst bauen und die Lautsprecher direkt integrieren, wählt man hier Einbaulautsprecher. Dabei sollte man dann darauf achten, dass es sich um Breitbandlautsprecher handelt. Diese decken den gesamten Frequenzbereich ab. Hochwertige Lautsprecherboxen werden nämlich oftmals aus zwei oder drei verschiedenen Lautsprechern gefertigt, welche dann jeweils nur hohe, mittlere oder niedrige Frequenzen wiedergeben können. Kauft man fälschlicherweise solch einen Lautsprecher, wird das Klangerlebnis nicht so toll sein.

Geliefert werden diese Einbaulautsprecher meiner Erfahrung nach ohne Anschlusskabel. Sie haben einen Plus- und einen Minuspol, an denen man das Kabel selbst verdrillen oder noch besser verlöten kann. D.h. man muss sich auch ein Klinkenkabel kaufen. Diese gibt es praktischerweise ebenfalls als Einbauvariante, wo bereits ein Stück abisoliert ist.

Grundsätzlich sollte man jedoch auch ein existierendes Kabel aufschneiden können. Allerdings sind meiner Erfahrung nach dort die Adern sehr viel dünner, sodass sie sich vielleicht schlecht abisolieren lassen oder leicht brechen.

Verstärker

Dann muss man sich überlegen, wie man den Lautsprecher ansteuern will. Der Raspberry Pi hat bereits einen Audioanschluss, dieser alleine direkt am Lautsprecher ist jedoch relativ leise.

Man kann auch eine USB-Soundkarte verwenden. Bei meinem Test war der Ton dann lauter, jedoch immer noch verbesserbar.

Daher benötigt man einen Verstärker. Hierfür wird vielfach der LM386-1 empfohlen, weil er besonders günstig ist. Es existieren auch bereits komplette Schaltpläne für einen Verstärker mit dem Baustein.

Wichtig bei diesem Schaltplan ist es, darauf zu achten, den Widerstand zur Rauschunterdrückung korrekt zu dimensionieren. Sonst wird entweder der Ton sehr leise oder das Rauschen sehr hoch. Ich habe hierzu viele Widerstände durchprobiert, man könnte aber auch ein Potentiometer verwenden.

Sollte der Ton dennoch sehr leise sein, kann es daran liegen, dass irgendwelche Anschlüsse nicht stabil sitzen.

Meine Erfahrung ist auch, dass das Rauschen auf dem Steckbrett mit langen, dünnen Jumperkabeln höher ist als später auf einer Lochrasterplatine mit Drahtbrücken.

IMG_6076

Eingabe

Weiterhin müssen die Eingabemethoden für das Radio festgelegt werden. Zur Lautstärkeregulierung bietet sich natürlich ein Drehknopf mit Potentiometer an (welches direkt in die Verstärkerschaltung eingebaut wird). Die restliche Interaktion ist sicherlich sehr flexibel.

Ich habe beispielsweise fünf Knöpfe für Favoritenprogramme gewählt. Alles andere muss noch über einen zweiten PC gesteuert werden. Manche Leute werden vielleicht eine Steuerung komplett über ihr Smartphone implementieren wollen.

Um die Knöpfe nicht alle direkt an die GPIO-Pins anschließen zu müssen, kann man einen zwischengeschalteten Mikrocontroller (bei mir Attiny) und/oder ein Schieberegister verwenden. Beim Schieberegister werden alle Eingänge abgefragt und dann mit einem bestimmten Takt über einen einzelnen GPIO-Pin durchgereicht. Indem man den Takt mitzählt, kann man dann ermitteln, welcher Knopf gedrückt wurde.

Wichtig hierbei ist allerdings, dass die Taktzeit nicht zu lange ist. Dauert das Abfragen zu lange, könnte es sein, dass ein kürzerer Tastendruck nicht erkannt wird.

Ausgabe

Außerdem kann man sich überlegen, ob man eine Ausgabe haben möchte und was hierauf angezeigt werden soll. Einige Online-Streams bieten z.B. Daten zum Songtitel an. Ich habe bisher selbst noch kein Display gekauft, will aber später eines einbauen.

Internetanbindung

Für die Internetverbindung muss man sich zwischen Ethernet und WLAN entscheiden. Für ein Radio scheint mir WLAN praktischer, allerdings benötigt man dann einen USB-Adapter. Hier scheint der EDIMAX EW-7811UN gute Dienste zu leisten.

Ethernet hingegen kann man direkt an die Buchse des Pi anschließen.

Softwarefunktionen

Letztlich muss man sich natürlich auch überlegen, welche Funktionen das Radio unterstützen soll. Die Hauptfunktion ist wohl die Musikwiedergabe, aber lohnenswert können auch Erweiterungen sein:

  • Wecker mit Cronjobs (beliebig flexibel im Gegensatz zu käuflichen Weckern)
  • Wiedergabe von Youtube-Playlists (evtl. sogar mit Video im Display?)
  • Wiedergabe von mp3-Dateien