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


von Max H. (hartl192)


Angehängte Dateien:

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:
1
#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:
1
  CBLOCK 0x00
2
    tx_cnt
3
    tx_buf
4
    rgb_byte: number_rgb_bytes
5
  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:
1
  CBLOCK 0x20
2
    rgb_byte: number_rgb_bytes
3
  ENDC
4
  tx_cnt equ 0x70
5
  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:
1
  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
von Max H. (hartl192)


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
von Max H. (hartl192)


Angehängte Dateien:

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:
1
.equ number_rgb_bytes, 9
-Es wird Speicher im RAM für die RGB-Bytes reservier:
1
.section  MAINRAM, bss, address(0x850)
2
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:
1
.equ number_rgb_bytes, 36
2
3
.section  MAINRAM, bss, address(0x850)
4
RGB_bytes:    .space number_rgb_bytes
5
6
.
7
.
8
.
9
10
mov #0x0F,w0
11
mov #RGB_bytes,w1
12
repeat #35
13
mov.b w0,[w1++]
14
15
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
von Max H. (hartl192)


Angehängte Dateien:

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:
1
void WS2812_Init(void);
Diese Funktion muss am Anfang aufgerufen werden um das SPI Modul zu 
Initialisieren.
1
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.

von Roland D. (roland_d92)


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

von Max H. (hartl192)


Lesenswert?

Und was hat das mit WS2812 für PIC zu tu?

von Max H. (hartl192)


Angehängte Dateien:

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
1
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
von Volker S. (vloki)


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
1
   #if _XTAL_FREQ == 64000000
2
        #define D0H()  Nop();Nop();Nop()
3
        #define D0L()  Nop();Nop();Nop();Nop()
4
        #define D1H()  Nop();Nop();Nop();Nop();Nop();Nop();Nop();Nop()
5
        #define D1L()  Nop();Nop();Nop()
6
    #elif _XTAL_FREQ == 32000000
7
        #define D0H()  Nop()
8
        #define D0L()  Nop();Nop()
9
        #define D1H()  Nop();Nop();Nop();Nop()
10
        #define D1L()  Nop()
11
    #elif _XTAL_FREQ == 16000000
12
        #define D0H()
13
        #define D0L()
14
        #define D1H()   Nop();Nop()
15
        #define D1L()   Nop()
16
    #endif
17
18
void WS2812_wr(uint8_t * ptrColors, uint8_t nrLEDs)
19
{
20
    uint8_t color;
21
22
    for(uint8_t i=0; i<(nrLEDs*3); i++){
23
        color = *ptrColors++;
24
/* bit 7 */
25
        if(!(color & 0x80)){        // (bit == 0)
26
            WS2812_DAT = 1; D0H();
27
            WS2812_DAT = 0; D0L();
28
        } else {                    // (bit == 1)
29
            WS2812_DAT = 1; D1H();
30
            WS2812_DAT = 0; D1L();
31
        }
32
/* 6 */ if(!(color & 0x40)){ WS2812_DAT = 1; D0H(); WS2812_DAT = 0; D0L(); }
33
          else { WS2812_DAT = 1; D1H(); WS2812_DAT = 0; D1L(); }
34
/* 5 */ if(!(color & 0x20)){ WS2812_DAT = 1; D0H(); WS2812_DAT = 0; D0L(); }
35
          else { WS2812_DAT = 1; D1H(); WS2812_DAT = 0; D1L(); }
36
/* 4 */ if(!(color & 0x10)){ WS2812_DAT = 1; D0H(); WS2812_DAT = 0; D0L(); }
37
          else { WS2812_DAT = 1; D1H(); WS2812_DAT = 0; D1L(); }
38
/* 3 */ if(!(color & 0x08)){ WS2812_DAT = 1; D0H(); WS2812_DAT = 0; D0L(); }
39
          else { WS2812_DAT = 1; D1H(); WS2812_DAT = 0; D1L(); }
40
/* 2 */ if(!(color & 0x04)){ WS2812_DAT = 1; D0H(); WS2812_DAT = 0; D0L(); }
41
          else { WS2812_DAT = 1; D1H(); WS2812_DAT = 0; D1L(); }
42
/* 1 */ if(!(color & 0x02)){ WS2812_DAT = 1; D0H(); WS2812_DAT = 0; D0L(); }
43
          else { WS2812_DAT = 1; D1H(); WS2812_DAT = 0; D1L(); }
44
/* 0 */ if(!(color & 0x01)){ WS2812_DAT = 1; D0H(); WS2812_DAT = 0; D0L(); }
45
          else { WS2812_DAT = 1; D1H(); WS2812_DAT = 0; D1L(); }
46
    }
47
}

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
von Mitihi (Gast)


Lesenswert?

Hi, could you please give me Volker S. little explanation about the code 
you added? because I am not sure with what function should I use your 
code exactly like main file etc?  and does this WS2812_DAT define as 
port which I am using?

von Volker S. (vloki)


Lesenswert?

Mitihi schrieb:
> I am not sure with what function should I use your
> code exactly like main file etc?

Sorry for delayed answer. Here an example of usage for 24 LEDs:
1
    #define WS2812_DAT LATCbits.LATC5
2
    #define WS2812_TRI TRISCbits.TRISC5
3
    #define WS2812_ANS ANSELCbits.ANSC5
4
5
void main(void)
6
{
7
    uint8_t ledColors[72], i;
8
    //----------------------------------------------------------------- __init()
9
  #if _XTAL_FREQ == 16000000
10
    OSCCONbits.IRCF = IRCF_16MHZ; OSCTUNEbits.PLLEN = 0;
11
  #elif _XTAL_FREQ == 32000000
12
    OSCCONbits.IRCF = IRCF_8MHZ; OSCTUNEbits.PLLEN = 1;
13
  #elif _XTAL_FREQ == 64000000
14
    OSCCONbits.IRCF = IRCF_16MHZ; OSCTUNEbits.PLLEN = 1;
15
  #else
16
    #error "Please define in _XTAL_FREQ in AddOnBoards.h"
17
  #endif
18
19
    WS2812_TRI = OUTPUT_PIN; WS2812_ANS = DIGITAL_PIN;
20
21
//----------------------------------------------------------------------- main()
22
    while(1){
23
//        INTCONbits.GIE ...
24
        for(i = 0; i<72; i+=3){ ledColors[i] = i*2;}    // green
25
        ledColors[0] = 1;
26
        WS2812_wr(&ledColors[0],24);
27
        __delay_ms(1000);
28
        for(i = 1; i<72; i+=3){ ledColors[i] = i*2;}    // yellow (green + red))
29
        WS2812_wr(&ledColors[0],24);
30
        __delay_ms(1000);
31
        for(i = 0; i<72; i+=3){ ledColors[i] = 0;}      // red  (yellow - green)
32
        WS2812_wr(&ledColors[0],24);
33
        __delay_ms(1000);
34
        for(i = 2; i<72; i+=3){ ledColors[i] = i*2;}    // purple (red+blue)
35
        WS2812_wr(&ledColors[0],24);
36
        __delay_ms(1000);
37
        for(i = 1; i<72; i+=3){ ledColors[i] = 0;}      // blue (purple - red)
38
        WS2812_wr(&ledColors[0],24);
39
        __delay_ms(1000);
40
        for(i = 0; i<72; i++){ ledColors[i] = i*2;}     // white
41
        WS2812_wr(&ledColors[0],24);
42
        __delay_ms(1000);
43
        for(i = 0; i<72; i++){ ledColors[i] = 0;}       // off
44
        WS2812_wr(&ledColors[0],24);
45
        __delay_ms(1000);
46
    }
47
}

: Bearbeitet durch User
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.