Arduino Uno + WS2801 Pixel Wall

Hallo zusammen,

ich bin gerade dabei meinen Partykeller zu sanieren…und dabi entstand die Idee eine Pixelwand zu bauen. Die Wand ist eine 24x12 matrix und besteht aus 288 WS2801 Pixeln. Soweit so gut. Angesteuert werden soll diese matrix durch einen PC auf dem Glediator (www.solderlab.de) läuft.
Für diese Software gibt es bereits einen Arduino Sketch, der wie folgt aussieht:

//Change this to YOUR matrix size!!
#define Num_Pixels 16


#define CMD_NEW_DATA 1

int SDI = 13; 
int CKI = 12;

unsigned char display_buffer[Num_Pixels * 3];

static unsigned char *ptr;
static unsigned char pos = 0;

volatile unsigned char go = 0;

void setup() 
{
  pinMode(SDI, OUTPUT);
  pinMode(CKI, OUTPUT); 
 
  //UART Initialisation
  UCSR0A |= (1<<U2X0);                                
  UCSR0B |= (1<<RXEN0)  | (1<<TXEN0) | (1<<RXCIE0);   
  UCSR0C |= (1<<UCSZ01) | (1<<UCSZ00)             ; 
  UBRR0H = 0;
  UBRR0L = 3; //Baud Rate 0.5 MBit   --> 0% Error at 16MHz :-)
  
  ptr=display_buffer;
  
  //Enable global interrupts
  sei();
}

void loop() 
{
  if (go==1) {shift_out_data(); go=0;}
}

//############################################################################################################################################################
// UART-Interrupt-Prozedur (called every time one byte is compeltely received)                                                                               #
//############################################################################################################################################################

ISR(USART_RX_vect) 
{
  unsigned char b;
  
  b=UDR0;
  
  if (b == CMD_NEW_DATA)  {pos=0; ptr=display_buffer; return;}    
  if (pos == (Num_Pixels*3)) {} else {*ptr=b; ptr++; pos++;}  
  if (pos == ((Num_Pixels*3)-1)) {go=1;}
}


//############################################################################################################################################################
// Shift out Data                                                                                                                                            #
//############################################################################################################################################################

void shift_out_data()
{

  for (byte i=0; i<Num_Pixels; i++)
  {
    byte r = display_buffer[i*3+0];
    byte g = display_buffer[i*3+1];
    byte b = display_buffer[i*3+2];
    
    for (byte j=0; j<8; j++)
    {
       digitalWrite(CKI, LOW);
       if (b & (byte)(1<<(7-j))) {digitalWrite(SDI, HIGH);} else {digitalWrite(SDI, LOW);}     
       digitalWrite(CKI, HIGH);
    }
    
    for (byte j=0; j<8; j++)
    {
       digitalWrite(CKI, LOW);
       if (r & (1<<(7-j))) {digitalWrite(SDI, HIGH);} else {digitalWrite(SDI, LOW);}     
       digitalWrite(CKI, HIGH); 
    }
    
    for (byte j=0; j<8; j++)
    {
       digitalWrite(CKI, LOW);
       if (g & (1<<(7-j))) {digitalWrite(SDI, HIGH);} else {digitalWrite(SDI, LOW);}     
       digitalWrite(CKI, HIGH);
    }
    
  }
  
  digitalWrite(CKI, LOW);
  delayMicroseconds(800); //Latch Data

}

Die Ansteuerung funktioniert auch wunderbar…jedoch nicht mit voller Pixelzahl. Setze ich Num_Pixels auf 288 passiert gar nichts mehr.
Da sich meine “programmierkenntnisse” auf ein wenig java/php/batch beschränken habe ich leider keine ahnung warum das so ist. Mit 99px funktioniert es noch, gebe ich 100px an findet nach upload keine ausgabe mehr statt.

Ist der Arduino UNO ausgelastet? Läuft die Schnittstelle über? Oder ist es ein Softwarefehler?
Kann jemand helfen? Habe schon Tage mit der Fehlersuche zugebracht bin aber mit meinem Latein am Ende…wie schön wäre jetzt ein Bier in der Kellerbar :astonished: :astonished:

Wo hast du den Code geklaut? für “nur ein bisschen Erfahrung mit Java/PHP/Batch” sehen mir folgende Zeilen ein wenig hochgegriffen aus:

UCSR0A |= (1<<U2X0);                                
  UCSR0B |= (1<<RXEN0)  | (1<<TXEN0) | (1<<RXCIE0);   
  UCSR0C |= (1<<UCSZ01) | (1<<UCSZ00)             ; 
  UBRR0H = 0;
  UBRR0L = 3; //Baud Rate 0.5 MBit   --> 0% Error at 16MHz :-)

Außerdem sehe ich nirgends eine Definition dieser Variablen, und die Methode ISR hat keinen Datentyp. display_buffer gibt es nicht. Was im Interrupt passieren soll sehe ich auch nicht… Wenn sich der Sketch so kompilieren lassen würde, bedeutet es entweder, dass du auf bereits intern vorhandene Funktionen und Variablen zurückgreifst - was nicht nachvollziehbar für uns wäre, oder dass etwas deines Codes fehlt.

Dass der Arduino überlastet ist, kann ich mir nicht vorstellen, eher dass irgentwo ein falscher (zu kleiner) Datentyp eingesetzt wurde.

Leider sieht man aus dem Sketch nicht, was das Ding eigentlich machen soll -so wie er da steht, macht er wenig sinn, da er garnix tut… “go” ist immer 0, der loop loopt sich zu tode.

Das sind direkte Zugriffe auf die Register des ATmega und somit korrekt auch wo ich mich frage wieso der Programmierer das so umständlich auf low level programmiert hat.

Der ATmega auf dem Arduino UNO hat 2kByte RAM. Bei 288 pixel sind das schon 864 byte für den Buffer. Ich tippe mal daß das RAM nicht ausreicht. Hast Du versucht den Kode auf einem Arduino MEGA laufen zu lassen?

Grüße Uwe

Danke erst einmal für die Antworten...vielleicht hatte ich mich falsch ausgedrückt. Den Code habe ich wie geschrieben von den Jungs von Solderlab übernommen und versucht ihn nachzuvollziehen...aber unter anderem an der genannten Stelle bin ich ein wenig überfordert. Der Sketch lässt sich auch compilieren und funktioniert einwandfrei...halt nur mit maximal 99pixeln. Nein auf einem Mega hab ich es noch nicht probiert, das wäre aber mein nächster Ansatz gewesen. Wollte bevor ich mir einen bestelle jedoch erstmal ausschließen, dass es vielleicht ja nur ein Programmierfehler ist. @Marcus: Was meinst du mit zu kleinem Datentyp? @uwefed: Du meinst dass der Code eigentlich so richtig wäre? oder kannst du da irgendetwas "kritisches" erkennen?

Mfg Holly

die Methode ISR hat keinen Datentyp…Was im Interrupt passieren soll sehe ich auch nicht… Wenn sich der Sketch so kompilieren lassen würde, bedeutet es entweder, dass du auf bereits intern vorhandene Funktionen und Variablen zurückgreifst - was nicht nachvollziehbar für uns wäre, oder dass etwas deines Codes fehlt.

Leider sieht man aus dem Sketch nicht, was das Ding eigentlich machen soll -so wie er da steht, macht er wenig sinn, da er garnix tut… “go” ist immer 0, der loop loopt sich zu tode.

Wenn man mal “glaubt”, dass der UART - Teil im Prinzip funktioniert, kann ich folgendes beitragen:

ISR(USART_RX_vect)  // ISR ist ein macro, das das Folgende als Interrupt Handler definiert
{
  unsigned char b;
  b=UDR0;               // lies das byte vom UART
  
  if (b == CMD_NEW_DATA)  {pos=0; ptr=display_buffer; return;}    // Start
  if (pos == (Num_Pixels*3)) {} else {*ptr=b; ptr++; pos++;}     // wenn der buffer voll ist mach nix, sonst kopier das byte in den buffer
  if (pos == ((Num_Pixels*3)-1)) {go=1;}          // Ende : go = 1 für die loop()
}

edit: minor format change

Holly:

//Change this to YOUR matrix size!!

#define Num_Pixels 16

static unsigned int pos = 0;  // geht bis Num_Pixels*3

unsigned char display_buffer[Num_Pixels * 3];
//…
void shift_out_data()
{
 for (byte i=0; i<Num_Pixels; i++)
 {
   byte r = display_buffer[i*3+0]; // byte * 3 bleibt byte !!!

Probiers mal mit for (int i = 0; i < Num_Pixels; i++)
oder mach jedesmal … (int)i*3 … wenn du i mit 3 multiplizierst.

pos (im IR handler verwendet) muss übrigens auch ein int werden!

Mal ausprobieren:

#define Num_Pixels 99  // geht ok bis 85 ?
static unsigned char pos = 0;
void setup() {test();}
void loop() { }
void test() {
  if (pos == ((Num_Pixels*3)-1)) return;
  Serial.println (pos++);
  delay(100);
}

Oder ist es ein Softwarefehler?

In Software ist nie ein Fehler. Macht immer genau das was programmiert wurde, Wenn nicht, ist es ein Hardware-Fehler.
Die Erwartungen sind in der Regel andere als das was die Software macht.

//Change this to YOUR matrix size!!

Wer sagt denn, dass du eine Zahl GRÖSSER als 16 nehmen kannst ? :wink:

edit: pos muss ein int sein !!!

So nach ein wenig hin und her sieht der Code nun so aus

//Change this to YOUR matrix size!!
#define Num_Pixels 288


#define CMD_NEW_DATA 1

int SDI = 13; 
int CKI = 12;

unsigned char display_buffer[Num_Pixels * 3];

static unsigned char *ptr;
static unsigned int pos = 0;

volatile unsigned char go = 0;

void setup() 
{
  pinMode(SDI, OUTPUT);
  pinMode(CKI, OUTPUT); 
 
  //UART Initialisation
  UCSR0A |= (1<<U2X0);                                
  UCSR0B |= (1<<RXEN0)  | (1<<TXEN0) | (1<<RXCIE0);   
  UCSR0C |= (1<<UCSZ01) | (1<<UCSZ00)             ; 
  UBRR0H = 0;
  UBRR0L = 3; //Baud Rate 0.5 MBit   --> 0% Error at 16MHz :-)
  
  ptr=display_buffer;
  
  //Enable global interrupts
  sei();
}

void loop() 
{
  if (go==1) {shift_out_data(); go=0;}
}

//############################################################################################################################################################
// UART-Interrupt-Prozedur (called every time one byte is compeltely received)                                                                               #
//############################################################################################################################################################

ISR(USART_RX_vect) 
{
  unsigned char b;
  
  b=UDR0;
  
  if (b == CMD_NEW_DATA)  {pos=0; ptr=display_buffer; return;}    
  if (pos == (Num_Pixels*3)) {} else {*ptr=b; ptr++; pos++;}  
  if (pos == ((Num_Pixels*3)-1)) {go=1;}
}


//############################################################################################################################################################
// Shift out Data                                                                                                                                            #
//############################################################################################################################################################

void shift_out_data()
{

  for (int i=0; i<Num_Pixels; i++)
  {
    byte r = display_buffer[i*3+0];
    byte g = display_buffer[i*3+1];
    byte b = display_buffer[i*3+2];
    
    for (byte j=0; j<8; j++)
    {
       digitalWrite(CKI, LOW);
       if (r & (byte)(1<<(7-j))) {digitalWrite(SDI, HIGH);} else {digitalWrite(SDI, LOW);}     
       digitalWrite(CKI, HIGH);
    }
    
    for (byte j=0; j<8; j++)
    {
       digitalWrite(CKI, LOW);
       if (g & (1<<(7-j))) {digitalWrite(SDI, HIGH);} else {digitalWrite(SDI, LOW);}     
       digitalWrite(CKI, HIGH); 
    }
    
    for (byte j=0; j<8; j++)
    {
       digitalWrite(CKI, LOW);
       if (b & (1<<(7-j))) {digitalWrite(SDI, HIGH);} else {digitalWrite(SDI, LOW);}     
       digitalWrite(CKI, HIGH);
    }
    
  }
  
  digitalWrite(CKI, LOW);
  delayMicroseconds(800); //Latch Data

}

//############################################################################################################################################################
//############################################################################################################################################################
//############################################################################################################################################################

Hatte gleichzeitig mit den Entwicklern von Glediator (Solderlab) Kontakt aufgenommen…Diese haben das Problem nun auch behoben…Und ich kann euch sagen IHR hattet recht!
DANKE an michael_x :wink:
Mit diesem Code ist es jetzt möglich mit dem Arduino UNO bis zu 333 WS2801 Pixel anzusteuern, falls noch jemand bock drauf hat. Wenn die Wand das Plexiglas vor hat poste ich noch mal ein Foto/Video

Vielen Dank für eure Mühen und bis zum nächsten Projekt!!!

mfg
#Holly

uwefed: Das sind direkte Zugriffe auf die Register des ATmega und somit korrekt auch wo ich mich frage wieso der Programmierer das so umständlich auf low level programmiert hat. Grüße Uwe

Nachtrag: In der Zwischenzeit habe ich verstanden wieso die Entwickler direkt auf die Register des UART zugreifen. Weil sie eine sehr hohe baudrate einstellen die mit Serial.begin() nicht möglich wäre. Grüße Uwe

Ich habe mal ne ganz dumme Frage: Verstehe ich das richtig das dieses IC (WS2801) im Prinzip eine Schieberegister ist mit einem BIT/BYTE und ein bissel LED Elektronik? Weil ich war am überlegen wie ich sage welche LED ich denn nun in der Reihe ansteuern will. Ich dachte zunächst immer man müsse die irgendwie Adressieren. Sieht aber so aus als ob ich nur immer alle LED-Werte durch schiebe. Will ich also die LED in der mitte ändern muss ich die ganze Reihe neu rein schieben, Richtig?

Richtig. Du mußt immer die Daten für alle WS2801 ausgeben.

Es sei denn Du programmierst ein Lauflicht.

Grüße Uwe

uwefed: Es sei denn Du programmierst ein Lauflicht.

Vermutlich sogar dann, denn wenn ich das richtig verstehe, hat der WS2801 ein Latch Signal, mit dem die hereingeschobenen Daten von den Schieberegistern übernommen werden. Du müßtest dann aber die Daten jeweils nur so weit in die Kette schieben, wie der letzte "Leuchtpunkt" entfernt ist, also nicht immer alle Daten.

Guten Tag,
Dieser Code läuft nur auf einem Arduiono Uno? Auf dem Mega 2560 geht es nämlich nicht.
Ist es wegen diesem Assemblerteil?

//UART Initialisation
  UCSR0A |= (1<<U2X0);                                
  UCSR0B |= (1<<RXEN0)  | (1<<TXEN0) | (1<<RXCIE0);   
  UCSR0C |= (1<<UCSZ01) | (1<<UCSZ00)             ; 
  UBRR0H = 0;
  UBRR0L = 3; //Baud Rate 0.5 MBit   --> 0% Error at 16MHz :-)

Kann den jemand freundlicherweise anpassen? Besten Dank.

LG
voguecocktail

Das ist nicht Assembler sondern direkter Zugiff auf die register des Microcontrollers. Der ATmeha2560 wird andere Register haben da er 4 UART hat und darum funktioniert das nicht. Grüße Uwe

Die Register sind laut Doc gleich, nur unterscheiden sie sich durch UCSR*0A,UCSR1*A usw. Habe da schon alle von 0-3 durchprobiert :( Auch beim Aufruf

 ISR(USART_RX_vect)

Habe ich USART0-3_RX_vect probiert. Weitere Idee?

LG voguecocktail

Wie versprochen hier noch schnell ein Link zum Projekt!

http://www.youtube.com/watch?v=IBS2aUCtVko

Grüße Holly

Toll!!