qmk_firmware

QMK firmware for my keyboards (Corne, Sweep Ferris) and trackball (Ploopy Adept)
Log | Files | Refs | Submodules | LICENSE

ws2812_bitbang.c (4344B)


      1 #include "ws2812.h"
      2 
      3 #include "gpio.h"
      4 #include "chibios_config.h"
      5 
      6 // DEPRECATED - DO NOT USE
      7 #if defined(NOP_FUDGE)
      8 #    define WS2812_BITBANG_NOP_FUDGE NOP_FUDGE
      9 #endif
     10 
     11 /* Adapted from https://github.com/bigjosh/SimpleNeoPixelDemo/ */
     12 
     13 #ifndef WS2812_BITBANG_NOP_FUDGE
     14 #    if defined(STM32F0XX) || defined(STM32F1XX) || defined(GD32VF103) || defined(STM32F3XX) || defined(STM32F4XX) || defined(STM32L0XX) || defined(WB32F3G71xx) || defined(WB32FQ95xx) || defined(AT32F415)
     15 #        define WS2812_BITBANG_NOP_FUDGE 0.4
     16 #    else
     17 #        if defined(RP2040)
     18 #            error "Please use `vendor` WS2812 driver for RP2040"
     19 #        else
     20 #            error "WS2812_BITBANG_NOP_FUDGE configuration required"
     21 #        endif
     22 #        define WS2812_BITBANG_NOP_FUDGE 1 // this just pleases the compile so the above error is easier to spot
     23 #    endif
     24 #endif
     25 
     26 // Push Pull or Open Drain Configuration
     27 // Default Push Pull
     28 #ifndef WS2812_EXTERNAL_PULLUP
     29 #    define WS2812_OUTPUT_MODE PAL_MODE_OUTPUT_PUSHPULL
     30 #else
     31 #    define WS2812_OUTPUT_MODE PAL_MODE_OUTPUT_OPENDRAIN
     32 #endif
     33 
     34 // The reset gap can be 6000 ns, but depending on the LED strip it may have to be increased
     35 // to values like 600000 ns. If it is too small, the pixels will show nothing most of the time.
     36 #ifndef WS2812_RES
     37 #    define WS2812_RES (1000 * WS2812_TRST_US) // Width of the low gap between bits to cause a frame to latch
     38 #endif
     39 
     40 #define NUMBER_NOPS 6
     41 #define CYCLES_PER_SEC (CPU_CLOCK / NUMBER_NOPS * WS2812_BITBANG_NOP_FUDGE)
     42 #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
     43 #define NS_PER_CYCLE (NS_PER_SEC / CYCLES_PER_SEC)
     44 #define NS_TO_CYCLES(n) ((n) / NS_PER_CYCLE)
     45 
     46 #define wait_ns(x)                                  \
     47     do {                                            \
     48         for (int i = 0; i < NS_TO_CYCLES(x); i++) { \
     49             __asm__ volatile("nop\n\t"              \
     50                              "nop\n\t"              \
     51                              "nop\n\t"              \
     52                              "nop\n\t"              \
     53                              "nop\n\t"              \
     54                              "nop\n\t");            \
     55         }                                           \
     56     } while (0)
     57 
     58 void sendByte(uint8_t byte) {
     59     // WS2812 protocol wants most significant bits first
     60     for (unsigned char bit = 0; bit < 8; bit++) {
     61         bool is_one = byte & (1 << (7 - bit));
     62         // using something like wait_ns(is_one ? T1L : T0L) here throws off timings
     63         if (is_one) {
     64             // 1
     65             gpio_write_pin_high(WS2812_DI_PIN);
     66             wait_ns(WS2812_T1H);
     67             gpio_write_pin_low(WS2812_DI_PIN);
     68             wait_ns(WS2812_T1L);
     69         } else {
     70             // 0
     71             gpio_write_pin_high(WS2812_DI_PIN);
     72             wait_ns(WS2812_T0H);
     73             gpio_write_pin_low(WS2812_DI_PIN);
     74             wait_ns(WS2812_T0L);
     75         }
     76     }
     77 }
     78 
     79 ws2812_led_t ws2812_leds[WS2812_LED_COUNT];
     80 
     81 void ws2812_init(void) {
     82     palSetLineMode(WS2812_DI_PIN, WS2812_OUTPUT_MODE);
     83 }
     84 
     85 void ws2812_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {
     86     ws2812_leds[index].r = red;
     87     ws2812_leds[index].g = green;
     88     ws2812_leds[index].b = blue;
     89 #if defined(WS2812_RGBW)
     90     ws2812_rgb_to_rgbw(&ws2812_leds[index]);
     91 #endif
     92 }
     93 
     94 void ws2812_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {
     95     for (int i = 0; i < WS2812_LED_COUNT; i++) {
     96         ws2812_set_color(i, red, green, blue);
     97     }
     98 }
     99 
    100 void ws2812_flush(void) {
    101     // this code is very time dependent, so we need to disable interrupts
    102     chSysLock();
    103 
    104     for (int i = 0; i < WS2812_LED_COUNT; i++) {
    105         // WS2812 protocol dictates grb order
    106 #if (WS2812_BYTE_ORDER == WS2812_BYTE_ORDER_GRB)
    107         sendByte(ws2812_leds[i].g);
    108         sendByte(ws2812_leds[i].r);
    109         sendByte(ws2812_leds[i].b);
    110 #elif (WS2812_BYTE_ORDER == WS2812_BYTE_ORDER_RGB)
    111         sendByte(ws2812_leds[i].r);
    112         sendByte(ws2812_leds[i].g);
    113         sendByte(ws2812_leds[i].b);
    114 #elif (WS2812_BYTE_ORDER == WS2812_BYTE_ORDER_BGR)
    115         sendByte(ws2812_leds[i].b);
    116         sendByte(ws2812_leds[i].g);
    117         sendByte(ws2812_leds[i].r);
    118 #endif
    119 
    120 #ifdef WS2812_RGBW
    121         sendByte(ws2812_leds[i].w);
    122 #endif
    123     }
    124 
    125     wait_ns(WS2812_RES);
    126 
    127     chSysUnlock();
    128 }