Blog der Heimetli Software AG

Ansteuerung von LED-Pixeln mit dem Raspberry PI

Nur so zum Spass habe ich mir eine LED-Kette mit 25 RGB-Pixeln gekauft. Jedes Pixel wird durch einen WS2801 kontrolliert, der die Farbe und die Helligkeit des Pixels bestimmt.

Alle Chips sind durch einem seriellen Bus verbunden der für eine einfache Ansteuerung der Pixel sorgt.

Anschluss an den Raspberry

Eigentlich müsste die Kette mit einen 5V-Pegel angesteuert werden. Im Internet findet man aber viele Darstellungen wie die Kette direkt an den PI angeschlossen wird. Also habe ich die Kette auch direkt an den Raspberry angeschlossen (vorsichtshalber mit einem Widerstand in Serie) was bisher problemlos funktioniert.

Beim Anschluss an den PI ist allerdings grösste Vorsicht geboten denn anscheinend sind viele Varianten dieser Ketten im Umlauf. Die Farben der Litzen, die Farben und Beschriftungen der Prints in den Pixeln sind alle unterschiedlich, was mich recht nervös machte.

Schauen Sie dreimal alles genau an, bevor Sie die Pixel anschliessen !

Die Applikation

Wie schon gesagt, sind die Pixel einfach anzusteuern. Es gibt nur ein Data- und ein Clock-Pin, die bedient werden müssen.

Noch besser: das Timing der beiden Pins ist kompatibel mit dem SPI-Bus. Weil der Raspberry bekanntlich einen Driver für das SPI enthält, wird das Programm unkompliziert.

Nach den ersten Versuchen wird es aber recht mühsam, das Programm immer wieder zu compilieren, deshalb habe ich mir eine Applikaton gebaut die ein Steuerfile liest und interpretiert.

Die Syntax ist einfach:

R 1 G 2 B 3

Das bedeutet: die erste LED ist rot, die zweite Grün und die dritte Blau. Es ist aber auch möglich, mehrere Pixel mit der gleichen Farbe anzusteuern:

R 1 2 G 2 3 B 4 5

Das ergibt ein rotes, ein gelbes, ein grünes, und zwei blaue Pixel. Das zweite Pixel ist gelb, weil sowohl der rote als auch der grüne Teil aktiv sind.

Der Parser ist recht flexibel, die Reihenfolge der Farb-Angaben ist vollkommen egal.

Das Eingabefile wird zeilenweise abgearbeitet und angezeigt. Eine Leerzeile bedeutet dass alle LEDs dunkel sind.

Kommentare sind bisher nicht vorgesehen. Das wäre sicher eine sinnvolle Erweiterung denn es ist nicht so einfach, die Uebersicht zu behalten.

Das Programm nimmt den Namen des Steuerfiles von der Kommandozeile. Wenn mehrere Files auf der Kommandozeile stehen, wird eins nach dem andern abgespielt.

Der Zugriff auf die SPI-Schnittstelle erfordert root-Rechte, deshalb starte ich das Programm mit sudo:

sudo ./ledstrip test

Link zum Steuerfile aus dem Beispiel.

Die Datenstruktur

Die Datenstruktur besteht hauptsächlich aus zwei Arrays die als Sende- und Empfangsbuffer dienen. Der Empfangsbuffer wird vom ioctl-Aufruf beschrieben, aber das Programm sieht die Daten nie an. Sie sind sowieso undefiniert, weil ich den Eingangspin gar nicht angeschlossen habe.

Für jedes Pixel braucht es drei Bytes, um die Leuchtstärke für den Rot-, Grün- und Blau-Anteil zu bestimmen. Multipliziert mit 25 Pixeln ergibt das die Magic-Number 75. (Ja, ich weiss, das sollte besser eine Konstante im Programm sein)

Dazu kommt eine Struktur mit den Angaben für den System-Call. Sie enthält Pointer auf die beiden Buffer und weitere Parameter für den Transfer. Wie diese Struktur aufgebaut ist sehen Sie im Code für die Initialisierung.

static unsigned char           tx[75] ;
static unsigned char           rx[75] ;

static struct spi_ioc_transfer parameters[1];

Der Code

Die Initialisierung

Die Initialisierung der Schnittstelle ist der aufwendigste Teil bei der Ansteuerung der Pixel.

// Device /dev/spidev0.0 oeffnen
spi = open( device, O_RDWR ) ;
if( spi < 0 )
{
   fprintf( stderr, "can't open device\n" ) ;
   return 1 ;
}

// Das Device konfigurieren
int ret = ioctl( spi, SPI_IOC_WR_MODE, &mode ) ;
if( ret == -1 )
{
   close( spi ) ;
   fprintf( stderr, "can't set mode\n" ) ;
   return 2 ;
}

ret = ioctl( spi, SPI_IOC_WR_BITS_PER_WORD, &bits ) ;
if( ret == -1 )
{
   close( spi ) ;
   fprintf( stderr, "can't set bits\n" ) ;
   return 3 ;
}

ret = ioctl( spi, SPI_IOC_WR_MAX_SPEED_HZ, &speed ) ;
if( ret == -1 )
{
   close( spi ) ;
   fprintf( stderr, "can't set speed\n" ) ;
   return 4 ;
}

// Struktur für den Transfer aufsetzen
memset( &parameters, 0, sizeof(spi) ) ;
 
parameters[0].tx_buf        = (unsigned long)tx ;
parameters[0].rx_buf        = (unsigned long)rx ;
parameters[0].len           = 75 ;
parameters[0].delay_usecs   = 0 ; 
parameters[0].speed_hz      = speed ;
parameters[0].bits_per_word = bits ;
parameters[0].cs_change     = 0 ;

Die Farbe der Pixel setzen

Der Sendebuffer (tx) wird jetzt mit den gewünschten Daten gefüllt, und dann schicke ich den ganzen Block am Stück an die Pixel.

if( ioctl(spi,SPI_IOC_MESSAGE(1),&parameters) == -1 )
   fprintf( stderr, "can't transfer data\n" ) ;

Die Chips in den Pixeln übernehmen die Daten erst wenn die Steuerleitungen eine kurze Zeit stabil bleiben. Es ist also nötig, nach dem Transfer eine kurze Zeit zu warten.

Senden und warten kann beliebig oft wiederholt werden.

Device wieder freigeben

Am Schluss muss natürlich wieder aufgeräumt werden, und das ist in diesem Fall wirklich einfach.

close( spi ) ;

Hier ist ein Link zum Sourcefile des Programms.

Build

Wer schon einmal ein Programm für Linux compiliert hat, müsste eigentlich wissen wie es geht:

g++ -o ledstrip ledstrip.cpp