mikrocontroller.net

Forum: Projekte & Code [ASM & C] PIC12/PIC18/PIC24 WS2812 SPI Library


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
Autor: Max H. (hartl192)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Ich habe eine Funktion für den PIC18(F45k22) und eine für den 
PIC12(F1840) geschrieben um WS2812 LED streifen anzusteuern. Der PIC 
muss mit 32MHz laufen.
Weiter Unten ist auch ein Code für PIC24 in C und ASM.

Im Anhang befinden sich zwei Programme, die die erste LED Gelb, die 
Zweite Cyan und dritte Magenta einschalten.

Die Funktion funktioniert ungefähr so:
1. Es wird die Anzahl der LEDs*3 definiet:
#define number_rgb_bytes 9   ; 3 LEDs
2. Die Variablen, in denen die RGB Bytes gespeichert werden und zwei 
Variablen, di die Sendefunktion als Zwischenspeicher braucht, werden mit 
CBLOCK (und equ) definier.
 -PIC18:
  CBLOCK 0x00
    tx_cnt
    tx_buf
    rgb_byte: number_rgb_bytes
  ENDC
  Die Variablen  tx_cnt und tx_buf müssen alle in der access Bank sein,
  die rgb_bytes alle in der selben Bank.
 -PIC12:
  CBLOCK 0x20
    rgb_byte: number_rgb_bytes
  ENDC
  tx_cnt equ 0x70
  tx_buf equ 0x71
  Hier gild ähnliches: tx_cnt und tx_buf müssen im Common RAM
  (0x70 - 0x7F) liegen und die rgb_bytes alle in der selben bank.
3. Der PIC muss mit 32MHz internem oder externen oszillator laufen
4. Das SPI Modul muss initalisier werten, die LEDs kommen an den SDO
   Ausgang.
5. Die gewünschten Werden in die rgb_byte geschrieben
6. Die Sendefunktion wir aufgerugen:
  call transmit_WS2812

Ich hoffe ich konnte verständlich genug erklären, wie die Funktion 
angewendet wird…
Viel Spaß mit der WS2812 Funktion.

Gruß Max

: Bearbeitet durch User
Autor: Max H. (hartl192)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich habe den Code für den PIC12 erfolgreich mit 10 LEDs eingesetzt.
Ich habe keinen PIC16 hier um es zu testen, der Code für den PIC12 
könnte auch auf einem PIC16 funktionieren, wenn man das moviw ++FSR0 
durch incf FSR0,f mowf INDF,w ersetzt.

Der Code sollte auch für WS2812B LEDs passen:
    | WS2812 | WS2812B | Programm
T1H | 0.7µs  |  0.8µs  | 0.75µs
T1L | 0.6µs  |  0.45µs | 0.625µs
    |        |         |
T0H | 0.35µs |  0.4µs  | 0.375µs
T0L | 0.8µs  |  0.85µs | 1µs

: Bearbeitet durch User
Autor: Max H. (hartl192)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Wie einige vielleicht mitbekommen haben, habe ich vor kurzem angefangen 
PIC24 (ASM) zu programmieren. Bei meiner Übung zum SPI Modul ist eine 
Funktion für die WS2812 LEDs entstanden. Geschrieben wurde der Code für 
einen PIC24FV32KA302 mit 32MHz.
Er ist ähnlich anzuwenden, wie der für den 8bit PIC:
-Die Anzahl der RGB Bytes (LEDs*3) wir definier:
.equ number_rgb_bytes, 9
-Es wird Speicher im RAM für die RGB-Bytes reservier:
.section  MAINRAM, bss, address(0x850)
RGB_bytes:    .space number_rgb_bytes
Variblem wie tx_cnt/tx_buf brauch es nicht, das der PIC24 16 
Arbeitsregister hat.
-Dann wird das SPI Modul initialisiert.
-Dann erden die RGB Werte byteweise mit mov.b in die Register 
geschrieben.
-Danach kann die Senderoutine mit call transmit_WS2812 aufgerufen 
werden.

Die Senderoutine verwende die Arbeitsregister W0, W1, W2, W3 und W4. Wer 
will kann sie auch vorher auf den Stack legen.

Ich habe den Streifen mit 12 LEDs getestet:
.equ number_rgb_bytes, 36

.section  MAINRAM, bss, address(0x850)
RGB_bytes:    .space number_rgb_bytes

.
.
.

mov #0x0F,w0
mov #RGB_bytes,w1
repeat #35
mov.b w0,[w1++]

call transmit_WS2812
Alle 12 LEDs leuchten "Dunkelweiß"


@Mods: Es wäre sehr nett, wenn einer von euch den PIC24 im Betreff 
hinzufügen könnte.

: Bearbeitet durch User
Autor: Max H. (hartl192)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Ich habe noch eine WS2812 Funktion für den PIC24F(V32KA302) in C 
(XC16) geschrieben. Der PIC läuft mit 32MHz und das SPI Modul mit 
5.333MHz (Secondary presvaler 1:3)

In der Datei sind, zusätzlich zur main, zwei WS2812-Funktionen:
void WS2812_Init(void);
Diese Funktion muss am Anfang aufgerufen werden um das SPI Modul zu 
Initialisieren.
void WS2812_Send(unsigned char *data, unsigned char nuber_led);
Diese Funktion sendet die Daten an die LEDs. Sie hat als 
Übergabeparameter die Anzahl der LEDs und ein unsigned char array, in 
dem die zu Bytes stehen, die an die gesendet werden. Sie werden in der 
reihenfolde data[0], data[1], data[2], ..., data[nuber_led*3] gesendet.

Autor: Roland D. (roland_d92)
Datum:

Bewertung
-2 lesenswert
nicht lesenswert
ich weiß der thread ist was älter
aber falls noch jemand weiter auf der suche danach ist:
https://github.com/benwilliam/equinox_clock/tree/master/WS2812b_SPI

Autor: Max H. (hartl192)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Und was hat das mit WS2812 für PIC zu tu?

Autor: Max H. (hartl192)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Im Anhang noch ein C Code, getestet auf dem PIC12F1840 und PIC16F1825, 
kompiliert mit dem XC8. In der Main wird einfach die erste LED grün, die 
Zweite rot und die Dritte blau eingeschalten.

Der Code ist eigentlich ganz einfach zu verwenden:
In der WS2812.h wird der Pin an dem die LEDs hängen definiert.
In der main muss die WS2812_Init(); aufgerufen werden, diese stellt 
eigentlich nur den Pin auf Output und initialisiert den Pin mit low.
Um die Daten an die LEDs zu senden wird die Funktion
void WS2812_Write(unsigned char *data, int number_leds);
aufgerufen. Als Übergrabe Parameter brauch die Funktion:
 *data : Ein Array aus unsigned char in dem die RGB werte in der 
Reihenfolge G1,R1,B1,G2,R2,B2,G3,R3,B3,G4,R4,B4,... stehen
 number_leds : Die Anzahl der LEDs an die die Daten gesendet werden 
sollen

Das Timing ist für Fosc=32MHz geschrieben. Wenn jemand eine andere 
Taktfrequenz verwenden will muss das Timing in der WS2812_WriteBit 
Funktion angepasst werden.



Hier noch was für den PIC16F1509, ist zwar nicht von mir, passt aber gut 
zum Thema WS2812 mit PIC. Der Vorteil dieses Codes ist, dass er die CPU 
beim Schreiben nicht zu 100% auslastet.
http://ww1.microchip.com/downloads/en/AppNotes/00001606A.pdf

: Bearbeitet durch User
Autor: Volker S. (vloki)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Thread ist uralt, aber das folgende ist nur eine Variation des Codes für 
16, 32 und 64MHz und dieser Thread ist schon im WS2812 Artikel 
verlinkt...

<edit>getestet mit einem PIC18FxxK22. Compiler XC8 Free
   #if _XTAL_FREQ == 64000000
        #define D0H()  Nop();Nop();Nop()
        #define D0L()  Nop();Nop();Nop();Nop()
        #define D1H()  Nop();Nop();Nop();Nop();Nop();Nop();Nop();Nop()
        #define D1L()  Nop();Nop();Nop()
    #elif _XTAL_FREQ == 32000000
        #define D0H()  Nop()
        #define D0L()  Nop();Nop()
        #define D1H()  Nop();Nop();Nop();Nop()
        #define D1L()  Nop()
    #elif _XTAL_FREQ == 16000000
        #define D0H()
        #define D0L()
        #define D1H()   Nop();Nop()
        #define D1L()   Nop()
    #endif

void WS2812_wr(uint8_t * ptrColors, uint8_t nrLEDs)
{
    uint8_t color;

    for(uint8_t i=0; i<(nrLEDs*3); i++){
        color = *ptrColors++;
/* bit 7 */
        if(!(color & 0x80)){        // (bit == 0)
            WS2812_DAT = 1; D0H();
            WS2812_DAT = 0; D0L();
        } else {                    // (bit == 1)
            WS2812_DAT = 1; D1H();
            WS2812_DAT = 0; D1L();
        }
/* 6 */ if(!(color & 0x40)){ WS2812_DAT = 1; D0H(); WS2812_DAT = 0; D0L(); }
          else { WS2812_DAT = 1; D1H(); WS2812_DAT = 0; D1L(); }
/* 5 */ if(!(color & 0x20)){ WS2812_DAT = 1; D0H(); WS2812_DAT = 0; D0L(); }
          else { WS2812_DAT = 1; D1H(); WS2812_DAT = 0; D1L(); }
/* 4 */ if(!(color & 0x10)){ WS2812_DAT = 1; D0H(); WS2812_DAT = 0; D0L(); }
          else { WS2812_DAT = 1; D1H(); WS2812_DAT = 0; D1L(); }
/* 3 */ if(!(color & 0x08)){ WS2812_DAT = 1; D0H(); WS2812_DAT = 0; D0L(); }
          else { WS2812_DAT = 1; D1H(); WS2812_DAT = 0; D1L(); }
/* 2 */ if(!(color & 0x04)){ WS2812_DAT = 1; D0H(); WS2812_DAT = 0; D0L(); }
          else { WS2812_DAT = 1; D1H(); WS2812_DAT = 0; D1L(); }
/* 1 */ if(!(color & 0x02)){ WS2812_DAT = 1; D0H(); WS2812_DAT = 0; D0L(); }
          else { WS2812_DAT = 1; D1H(); WS2812_DAT = 0; D1L(); }
/* 0 */ if(!(color & 0x01)){ WS2812_DAT = 1; D0H(); WS2812_DAT = 0; D0L(); }
          else { WS2812_DAT = 1; D1H(); WS2812_DAT = 0; D1L(); }
    }
}

Timing bei 16MHz Clock
T0H  250us
T1H  750us
TxL
- zwischen zwei Bytes sind es        31 Cycles → 7,75us (bei 16MHz Takt)
- zwischen zwei kurzen High (0 code)  5 Cycles→ 1,25us
- zwischen „0“ und folgender „1“      6 Cycles→ 1,50us
- zwischen „1“ und folgender „0“      4 Cycles→ 1,00us
- zwischen zwei langen High (1 code)  5 Cycles→ 1,25us

Die "inline" Version hätte mir schon auch gefallen und hat auch mit 
16MHz funktioniert. Allerdings verlängert sie die Übertragungszeit um 
mehr als 50%. Bei 24 WS2812 von ~1,4ms auf ~2,3ms

: Bearbeitet durch User

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.