diff options
Diffstat (limited to 'platforms/chibios/drivers/ws2812.c')
| -rw-r--r-- | platforms/chibios/drivers/ws2812.c | 114 | 
1 files changed, 114 insertions, 0 deletions
| diff --git a/platforms/chibios/drivers/ws2812.c b/platforms/chibios/drivers/ws2812.c new file mode 100644 index 0000000000..0d12e2fb79 --- /dev/null +++ b/platforms/chibios/drivers/ws2812.c @@ -0,0 +1,114 @@ +#include "quantum.h" +#include "ws2812.h" +#include <ch.h> +#include <hal.h> + +/* Adapted from https://github.com/bigjosh/SimpleNeoPixelDemo/ */ + +#ifndef NOP_FUDGE +#    if defined(STM32F0XX) || defined(STM32F1XX) || defined(STM32F3XX) || defined(STM32F4XX) || defined(STM32L0XX) +#        define NOP_FUDGE 0.4 +#    else +#        error("NOP_FUDGE configuration required") +#        define NOP_FUDGE 1  // this just pleases the compile so the above error is easier to spot +#    endif +#endif + +// Push Pull or Open Drain Configuration +// Default Push Pull +#ifndef WS2812_EXTERNAL_PULLUP +#    define WS2812_OUTPUT_MODE PAL_MODE_OUTPUT_PUSHPULL +#else +#    define WS2812_OUTPUT_MODE PAL_MODE_OUTPUT_OPENDRAIN +#endif + +#define NUMBER_NOPS 6 +#define CYCLES_PER_SEC (STM32_SYSCLK / NUMBER_NOPS * NOP_FUDGE) +#define NS_PER_SEC (1000000000L)  // Note that this has to be SIGNED since we want to be able to check for negative values of derivatives +#define NS_PER_CYCLE (NS_PER_SEC / CYCLES_PER_SEC) +#define NS_TO_CYCLES(n) ((n) / NS_PER_CYCLE) + +#define wait_ns(x)                                  \ +    do {                                            \ +        for (int i = 0; i < NS_TO_CYCLES(x); i++) { \ +            __asm__ volatile("nop\n\t"              \ +                             "nop\n\t"              \ +                             "nop\n\t"              \ +                             "nop\n\t"              \ +                             "nop\n\t"              \ +                             "nop\n\t");            \ +        }                                           \ +    } while (0) + +// These are the timing constraints taken mostly from the WS2812 datasheets +// These are chosen to be conservative and avoid problems rather than for maximum throughput + +#define T1H 900           // Width of a 1 bit in ns +#define T1L (1250 - T1H)  // Width of a 1 bit in ns + +#define T0H 350           // Width of a 0 bit in ns +#define T0L (1250 - T0H)  // Width of a 0 bit in ns + +// The reset gap can be 6000 ns, but depending on the LED strip it may have to be increased +// to values like 600000 ns. If it is too small, the pixels will show nothing most of the time. +#define RES (1000 * WS2812_TRST_US)  // Width of the low gap between bits to cause a frame to latch + +void sendByte(uint8_t byte) { +    // WS2812 protocol wants most significant bits first +    for (unsigned char bit = 0; bit < 8; bit++) { +        bool is_one = byte & (1 << (7 - bit)); +        // using something like wait_ns(is_one ? T1L : T0L) here throws off timings +        if (is_one) { +            // 1 +            writePinHigh(RGB_DI_PIN); +            wait_ns(T1H); +            writePinLow(RGB_DI_PIN); +            wait_ns(T1L); +        } else { +            // 0 +            writePinHigh(RGB_DI_PIN); +            wait_ns(T0H); +            writePinLow(RGB_DI_PIN); +            wait_ns(T0L); +        } +    } +} + +void ws2812_init(void) { palSetLineMode(RGB_DI_PIN, WS2812_OUTPUT_MODE); } + +// Setleds for standard RGB +void ws2812_setleds(LED_TYPE *ledarray, uint16_t leds) { +    static bool s_init = false; +    if (!s_init) { +        ws2812_init(); +        s_init = true; +    } + +    // this code is very time dependent, so we need to disable interrupts +    chSysLock(); + +    for (uint8_t i = 0; i < leds; i++) { +        // WS2812 protocol dictates grb order +#if (WS2812_BYTE_ORDER == WS2812_BYTE_ORDER_GRB) +        sendByte(ledarray[i].g); +        sendByte(ledarray[i].r); +        sendByte(ledarray[i].b); +#elif (WS2812_BYTE_ORDER == WS2812_BYTE_ORDER_RGB) +        sendByte(ledarray[i].r); +        sendByte(ledarray[i].g); +        sendByte(ledarray[i].b); +#elif (WS2812_BYTE_ORDER == WS2812_BYTE_ORDER_BGR) +        sendByte(ledarray[i].b); +        sendByte(ledarray[i].g); +        sendByte(ledarray[i].r); +#endif + +#ifdef RGBW +        sendByte(ledarray[i].w); +#endif +    } + +    wait_ns(RES); + +    chSysUnlock(); +} | 
