Willkommen in unserer Community

Werde Teil unserer Community und registriere dich jetzt kostenlos ...

  1. Diese Seite verwendet Cookies. Wenn du dich weiterhin auf dieser Seite aufhältst, akzeptierst du unseren Einsatz von Cookies. Weitere Informationen

C WS2812 LED Treiber

Dieses Thema im Forum "Software" wurde erstellt von Janiiix3, 17. Oktober 2017.

  1. Janiiix3

    Janiiix3 Mitglied

    Registriert seit:
    28. September 2013
    Beiträge:
    987
    Zustimmungen:
    6
    Ort:
    Hannover
    Sprachen:
    C
    Map
    Einen wunderschönen guten Morgen.

    Bin gerade dabei ein paar LED´s mit dem oben erwähnten Treiber zum leuchten zu bewegen. Klappt zwar auch, nur nicht so wie ich will.
    Kein Wunder. Bekomme auch nicht wirklich mit den "C" Routinen das Timing hin, was laut Datenblatt vorgegebn ist.

    Um eine "0" zu senden möchte der Treiber folgendes Timing.:


    CodeBox C und C++
    void ws2812Low(uint8_t port, uint8_t bit)
    {
       BS(PORTK,PK0); // high
       _delay_us(0.35); // high for 0.35µS
       BC(PORTK,PK0); // low
       _delay_us(0.80); // 0.8µS
    }
    


    Um eine "1" zu mögen, möchte er.:


    CodeBox C und C++
    void ws2812High(uint8_t port, uint8_t bit)
    {
       BS(PORTK,PK0); // high
       _delay_us(0.70); // high for 0.35µS
       BC(PORTK,PK0); // low
       _delay_us(0.60); // 0.8µS
    }
    


    Mit beiden Funktionen, bin ich um ettliche Nanosekunden daneben. Gibt es eine alternative?
    In meiner jetzigen CPU wackelt ein 16MHz Quarz..
     
  2. Dirk

    Dirk Administrator Mitarbeiter

    Registriert seit:
    28. Januar 2007
    Beiträge:
    4.070
    Zustimmungen:
    100
    Ort:
    Mittelhessen, Giessen
    Sprachen:
    C, Assembler, Pascal, C++, PHP, Java
    Map
    Das Timing wirst du mit C nicht hinbekommen!

    Das Übertragungsprotokoll musst du maschinennah in Assembler programmieren.

    Inzwischen gibt es sicher einige Lösungen für AVR Mikrocontroller im Bereich Arduino. Hier würde ich mal schauen.

    Hier mal eine Spielerei von mir mit Mega2560 und Mega32, C mit Assembler ...

     
  3. Janiiix3

    Janiiix3 Mitglied

    Registriert seit:
    28. September 2013
    Beiträge:
    987
    Zustimmungen:
    6
    Ort:
    Hannover
    Sprachen:
    C
    Map
    Hey Dirk!
    Sieht cool aus ;) Kann man sich den Code irgendwo ansehen?
    Ich habe von Asm überhaupt keine Ahnung.
     
  4. Dirk

    Dirk Administrator Mitarbeiter

    Registriert seit:
    28. Januar 2007
    Beiträge:
    4.070
    Zustimmungen:
    100
    Ort:
    Mittelhessen, Giessen
    Sprachen:
    C, Assembler, Pascal, C++, PHP, Java
    Map
    Meinen Source habe ich jetzt nicht hier. Ich schau bei Gelegenheit mal danach.
     
  5. addi

    addi Mitglied

    Registriert seit:
    2. September 2013
    Beiträge:
    79
    Zustimmungen:
    3
    Ort:
    Hamminkeln
    Sprachen:
    BascomAVR, C, Assembler
    Hmmm...eventl. 20mhz quarz rein?...falls der prozessor das mitmacht.
    Addi
     
  6. LotadaC

    LotadaC Sehr aktives Mitglied

    Registriert seit:
    22. Januar 2009
    Beiträge:
    2.698
    Zustimmungen:
    38
    Ort:
    Hennigsdorf
    Sprachen:
    BascomAVR, Assembler
    Map
    ???
    0 -> 200ns < Hi < 500ns, 750ns < Lo < 1050ns
    1-> 750ns < Hi < 1050ns, 200ns < Lo < 500ns

    1100ns < Hi+Lo < 1400ns

    Bei der Null paßt's doch, bei der Eins bist Du eigentlich im nicht definierten Zustand. Eigentlich, weil ein Takt bei 16MHz 62,5ns dauert.
    Schmeißt der Compiler bei dem Delay eigentlich keine Warnung? 350ns kann er aus 16MHz doch gar nicht treffen. 5 Takte sind 312,5ns - 6 Takte sind 375ns. Die nächste Instruktion ist erst einen weiteren Takt später dran - das ist aber keine Makro (oder?) sondern ein Funktionsaufruf (RCALL oder CALL, je nach Controller - 2 bis 5Takte (auch vom Flash abhängig)).
    Aber die 600ns sind eigentlich zu lang.

    Wäre doch 'ne Herausforderung...
    Hmm...
    Wie siehts mit der Verwendung der Hardware aus?
    Prinzipiell sollte sich doch'n Timer im fastPWM nutzen lassen. Das Compare-Register wird beim Überlauf (genauer Bottom) aktualisiert, müßte also beim Compare-IRQ bereits für das nächste Bit gesetzt werden. Beim letzten Bit muß dann der Timer gestoppt werden (und für die nächste Transmission bereitgemacht...).

    Hmm2...
    Ihr kennt ja meine ... ähm ... unkonventionellen Vorschläge...
    Wie siehts mit dem UART aus?
    Die Datenleitung soll im idle low sein, oder?
    Mist, wäre ganau andersrum...
    Ok, könnte man mit einem Transistor/NOT-Gatter (SOT23) invertieren...
    Allerdings spuckt einem da das Oversampling für den Empfang in die Suppe. Mit doubeled Transmission Speed wären bei 16MHz 2Mbaud drin. Wir bräuchten etwa 3.33Mbaud.
    Hmm3...
    USART im SPI-Mode oder SPI selbst?
    Dann sollte jedes Nibble ein LED-Bit repräsentieren können.
    SPI-Bitzeit auf ca. 300ns festlegen, dann dauert ein Nibble 1200ns.
    idle = 0hex = 0000bin wäre idle oder eben nichts senden,
    "0" = 8hex = 1000bin die gesendete Null,
    "1" = Ehex = 1110bin die gesendete Eins.
    Beim echten SPI hatte ich damals keinen lückenlosen Transfer hinbekommen, beim gepufferten UART hingegen schon.
    USART-SPI hab ich noch nie getestet...
     
  7. addi

    addi Mitglied

    Registriert seit:
    2. September 2013
    Beiträge:
    79
    Zustimmungen:
    3
    Ort:
    Hamminkeln
    Sprachen:
    BascomAVR, C, Assembler
    Hmmm...bzgl spi via usart
    Hier ein link
    http://www.gammon.com.au/spi

    Weiter runterblättern ..bei reply #3 steht dazu etwas (arduino)
    Addi
     
  8. Dirk

    Dirk Administrator Mitarbeiter

    Registriert seit:
    28. Januar 2007
    Beiträge:
    4.070
    Zustimmungen:
    100
    Ort:
    Mittelhessen, Giessen
    Sprachen:
    C, Assembler, Pascal, C++, PHP, Java
    Map
    Da kann man sich drehen wie man will. Leider geht auch nicht _delay_ns(1) ;-) IO Port Zugriffe benötigen auch mehr als 0ns.
    Eventuell geht es auch "unkonventionell" via Hardwaremodule. Ich denke aber die einfachste Lösung geht über konventionellen Assemblercode.
     
  9. LotadaC

    LotadaC Sehr aktives Mitglied

    Registriert seit:
    22. Januar 2009
    Beiträge:
    2.698
    Zustimmungen:
    38
    Ort:
    Hennigsdorf
    Sprachen:
    BascomAVR, Assembler
    Map
    Nein, aber delay_ns(62.5) müßte genau ein NOP sein - falls es entsprechend implementiert wurde. Ein purer schleifengenerator, der auch noch erst mit call/rcall und ret aufgerufen/beendet wird, versagt bei so kurzen Zeiten, und da würde ich zumindest einen Hinweis des Compilers erwarten...
    Mit Set/Reset (SBI/CBI) einen Takt, mit direktem Schreibzugriff (OUT) auch einen, da der Wert vorher geladen werden muß (LDS/LDI mindestens einen weiteren, bei ReadModifyWrite-Zugriffen dann entsprechende Takte auch für Read und Modify...
    Diese Verzögerungen (Aufruf und Ausführung des Bein-setzens) verlängern die Phasen. deswegen wird der eigetlich zu kurze Hi-Teil trotzdem als 1 erkannt.
    Meiner Meinung nach ist die Länge des Hi-Pulses relevant, die Low-Länge sollte weniger kritisch sein.
    Hmm... kommt drauf an, was der Controller nebenbei noch macht - da bleibt ja nicht viel Zeit im Hintergrund.
    Wenn der Pin zu Fuß gesetzt werden soll, brauchst Du zwei Zugriffe pro LED-Bit (also ca alle 1,2µs).
    Läßt Du das irgendein Hardware-Modul machen, übernimmt das einen Teil der Zugriffe im Hintergrund - mein Vorschlag erschlägt zwei LED-Bits mit einem gesendeten "Byte", wäre also nur noch ein Zugriff alle 2,4µs. Insbesondere keinen kritischen mit 350ns-Timing.
    So unkonventionell scheint die Idee nicht zu sein, allerdings schafft der AVR@16MHz keine 3+Mbaud. Link
    Hm..
    zumindest im dazugehöriigen Bild sieht man auch hier 'ne Lücke zwischen den Bytes, und die wollen wir ja nicht. Woher die nun kommt, geh ich da jetzt nicht mehr durch - Arduinesisch/C tu ich mir jetzt nicht mehr an...
    beim echten SPI hab ich das mal in diesem Thread durchgespielt.
    Das SPI Data Register ist schreibtechnisch nicht gepuffert.
    Reagiert man auf den Transfer Complete IRQ, verzögert es sich durch den IRQ auf mindestens 4 Takte für den automatischen Sprung in die IVT (mit Returnadresse pushen), dort dann üblicherweise (aber nicht zwingend nötig) ein weiterer Sprung in die ISR. Sieben Takte, bevor man mit beschreiben des SPDR das nächste Byte losschicken kann.
    Etwas schneller gehts im Polling.
    Als letztes hatte ich dann die benötigten Takte gezählt - da mußten es interessanterweise nicht 16 Takte sein sondern 17. Beschreibt man nach 16 Takten das SPDR, werden zwar CLKs generiert, aber es wird immer eine 0x00 rausgetaktet.

    Beim echten UART meine ich mich hingegen an ein lückenloses senden erinnern zu können - das UDR ist ja schreibtechnisch (einfach) gepuffert. Ob das aber auch im USART-SPI-Mode so gilt, weiß ich nicht...
     
  10. Dirk

    Dirk Administrator Mitarbeiter

    Registriert seit:
    28. Januar 2007
    Beiträge:
    4.070
    Zustimmungen:
    100
    Ort:
    Mittelhessen, Giessen
    Sprachen:
    C, Assembler, Pascal, C++, PHP, Java
    Map
    Dieses ...
    hatte ich eigentlich nicht besonders ernst gemeint, deswegen auch das ;-) :D
    Eigentlich wäre es etwas für den nächsten April. ;)

    Das ist klar, weder dies noch ggf. Aufruf der Routinen werden im Code ganz oben berücksichtigt, mal ganz abgesehen davon, dass man ein NOP nicht beliebig "teilen" kann. Es funktioniert so auf keinen Fall!
     
  11. Janiiix3

    Janiiix3 Mitglied

    Registriert seit:
    28. September 2013
    Beiträge:
    987
    Zustimmungen:
    6
    Ort:
    Hannover
    Sprachen:
    C
    Map
    Hey Ho!
    Habe jetzt eine Assembler / C Version im Netz gefunden die Funktioniert. Sehr schade das man mit C keinen knackig kurzen / schnellen Code erzeugen kann :/
     
  12. Dirk

    Dirk Administrator Mitarbeiter

    Registriert seit:
    28. Januar 2007
    Beiträge:
    4.070
    Zustimmungen:
    100
    Ort:
    Mittelhessen, Giessen
    Sprachen:
    C, Assembler, Pascal, C++, PHP, Java
    Map
    Hallo Jan,

    super dass es nun funktioniert :cool:

    Wenn du lust hast, mach doch mal ein Video oder Bilder von deinem Projekt.

    Ist es die Platine mit den kreisförmig angeordneten RGB-LEDs, wo du das Layout unter jeder LED ebenfalls gleichmäßig gedreht haben wolltest?
     
  13. LotadaC

    LotadaC Sehr aktives Mitglied

    Registriert seit:
    22. Januar 2009
    Beiträge:
    2.698
    Zustimmungen:
    38
    Ort:
    Hennigsdorf
    Sprachen:
    BascomAVR, Assembler
    Map
    Hab ich auch schon vermutet...

    Da die eigentliche Frage gelöst zu sein scheint (leider ohne die Lösung hier zu zeigen), können wir ja ein wenig rumspammen...

    So ein LED-Bit kann man sich ja in drei Phasen teilen: Hi, Data, low.
    Jede der Phase soll zwischen 375ns und 467ns dauern, um das Timing des Datenblattes einzuhalten.
    Zur Verwendung des konventionellen UART:
    Ein UART-"Byte beginnt mit einem Startbit(=0), es folgen 5..9 Datenbits LSB first, anschließend ggf ein Paritätsbit und ein Stopbit (=1).
    Das Paritätsbit wird inhaltspezifisch automatisch gebildet - man müßte also jedesmal die Datenbits betrachten, und dann zwischen odd/even wählen - oder es eben nicht nutzen.
    Start=0 und Stop=1 erzwingen eine externe Invertierung. Effektiv kann man so drei LED-Bytes pro UART-"Byte" übertragen - mit sieben Datenbits (+ Start- und Stopbit).

    Der UART verwendet bei doppelter Übertragungsgeschwindigkeit (U2X) acht samples pro Bit - ein 16MHz getakteter Controller käme also (mit UBRR=0) so auf 2Mbaud. Wären 500ns für die LED-bit-Phasen - 1500ns pro LED-bit.
    Ist knapp außerhalb der Datenblatt-Timings (1400ns) - könnte aber trotzdem klappen.
    Bei 20MHz Takt käme man auf 2,5Mbaud (400ns pro Phase, 1200ns pro LED-bit) - das ist sauber drin...

    Pro LED wären acht "Bytes" zu übertragen (1 Uart-Byte = 9 UART-Bits entsprechen 3 LED-Bits. Acht folglich 24 LED-Bits).
    Idee (ATtiny2313A):
    • U2X in UCSRA setzen (Doppeltempo)
    • UCSZ0 in UCSRC löschen (7 Databits)
    • TXEN in UCSRB setzen (Transmitter aktivieren)
    • D1 in DDRD setzen (TxD ist Ausgang)
    Danach je UDRE in UCSRA pollen wenn es frei ist ein Byte nach UDR schreiben...
    Die zu übertragenden Bytes sind (sollte eine LED Grün leuchten (100%) lassen):
    0x12
    0x12
    0x52
    0x5B
    0x5B
    0x5B
    0x5B
    0x5B

    Mein 2312A taktet mit 8MHz (interner RC), entsprechend muß man sich die Timings angepaßt denken, die externe Invertierung ebenso...
    uart_ws2812.png

    @Dirk , @Janiiix3 ...
    kann das mal wer von Euch ausprobieren?
     
  14. Dirk

    Dirk Administrator Mitarbeiter

    Registriert seit:
    28. Januar 2007
    Beiträge:
    4.070
    Zustimmungen:
    100
    Ort:
    Mittelhessen, Giessen
    Sprachen:
    C, Assembler, Pascal, C++, PHP, Java
    Map
    Hallo LotadaC, also mir fehlt leider im Moment total die Zeit :(
     
  15. Janiiix3

    Janiiix3 Mitglied

    Registriert seit:
    28. September 2013
    Beiträge:
    987
    Zustimmungen:
    6
    Ort:
    Hannover
    Sprachen:
    C
    Map
    Moin Jungs^^

    Sorry das ich jetzt erst antworte, bin leider noch Krankgeschrieben und lag die ganze Zeit flach.
    Also, es ist ja kein Projekt, habe auf nem Breadboard einfach mal son China teilchen gesteckt und wollte schauen ob das funktioniert. Sind einfach nur 8 x diese RGB Leds drauf.

    Ich werde jetzt klein anfangen und mir mal den Assembler reinziehen.
     
  16. LotadaC

    LotadaC Sehr aktives Mitglied

    Registriert seit:
    22. Januar 2009
    Beiträge:
    2.698
    Zustimmungen:
    38
    Ort:
    Hennigsdorf
    Sprachen:
    BascomAVR, Assembler
    Map
    Kannst Du trotzdem mal den Vorschlag aus #13 unter C testen?
    Wenn Du den verwendeten Controller angibst, würde ich mal versuchen, daß C-Programm dazu zu ... stricken...
     
  17. Janiiix3

    Janiiix3 Mitglied

    Registriert seit:
    28. September 2013
    Beiträge:
    987
    Zustimmungen:
    6
    Ort:
    Hannover
    Sprachen:
    C
    Map
    Du willst dich jetzt in C Versuchen?
    Das ist ein 2560 er
     
  18. LotadaC

    LotadaC Sehr aktives Mitglied

    Registriert seit:
    22. Januar 2009
    Beiträge:
    2.698
    Zustimmungen:
    38
    Ort:
    Hennigsdorf
    Sprachen:
    BascomAVR, Assembler
    Map
    Ich bereu es jetzt schon...
    zumindest compiliert es fehlerfrei...


    CodeBox C und C++
    /*
     * GccApplication4.cpp
     *
     * Created: 23.10.2017 17:44:37
     *  Author: LotadaC
     */
    
    
    #include <avr/io.h>
    #include <util/delay.h>
    uint8_t data[]={0x12,0x12,0x52,0x5b,0x5b,0x5b,0x5b,0x5b};
    
    int main(void)
    {
     UCSR0A|=(1<<U2X0);
     UCSR0C&=~(1<<UCSZ00);
     UCSR0B|=(1<<TXEN0);
     DDRE|=(1<<PE1);
     
        while(1)
        {
      for (int i=0; i<8;i++)
      {
       while(!(UCSR0A&(1<<UDRE0)))
       {
        asm("nop"); 
       }
       UDR0=data[i];
      }
      _delay_ms(500);
        }
    }
    

    Sollte den UART0 verwenden. TXD0 müßtest Du also über einen externen Transistor invertieren. Sollte die erste LED voll grün leuchten lassen. 16MHz sind allerdings von den Timings her grenzwertig, kannst es ja trotzdem mal versuchen.
     

Diese Seite empfehlen

  • Über uns

    Unsere immer weiter wachsende Community beschäftigt sich mit Themenbereichen rund um Mikrocontroller- und Kleinstrechnersysteme. Neben den Themen Design von Schaltungen, Layout und Software, beschäftigen wir uns auch mit der herkömmlichen Elektrotechnik.

    Du bist noch kein Mitglied in unserer freundlichen Community? Werde Teil von uns und registriere dich in unserem Forum.
  • Coffee Time

    Unser makerconnect-Team arbeitet hart daran sicherzustellen, dass unser Forum permanent online und schnell erreichbar ist, unsere Forensoftware auf dem aktuellsten Stand ist und unser eigener makerconnekt-Server regelmäßig gewartet wird. Wir nehmen das Thema Datensicherung und Datenschutz sehr ernst und sind hier sehr aktiv, auch sorgen wir uns darum, dass alles Drumherum stimmt!

    Dir gefällt das Forum und die Arbeit unseres Teams und du möchtest es unterstützen? Unterstütze uns durch deine Premium-Mitgliedschaft, unser Team freut sich auch über eine Spende für die Kaffeekasse :-)
    Vielen Dank!
    Dein makerconnect-Team

    Spende uns! (Paypal)