]> git.donarmstrong.com Git - qmk_firmware.git/commitdiff
ARM - ws2812 bitbang (#7173)
authorJoel Challis <git@zvecr.com>
Thu, 7 Nov 2019 13:10:29 +0000 (13:10 +0000)
committerGitHub <noreply@github.com>
Thu, 7 Nov 2019 13:10:29 +0000 (13:10 +0000)
* Initial ARM bitbang ws2812 driver

* Unify chibios platform to run rgblight_task

* Remove 'avr only' comments from ws2812 docs

* Remove 'avr only' comments from ws2812 docs

* Unify chibios platform to run rgblight_task - review comments

* Remove debug flags from keymap

* Add comments from review

* Add defines for STM32L0XX

* Attempt to get arm ws2812 working on multiple gcc versions

14 files changed:
docs/feature_rgb_matrix.md
docs/feature_rgblight.md
docs/hardware_drivers.md
docs/ws2812_driver.md
drivers/arm/ws2812.c
drivers/arm/ws2812.h [new file with mode: 0644]
keyboards/handwired/onekey/bluepill/config.h
keyboards/handwired/onekey/keymaps/rgb/config.h [new file with mode: 0644]
keyboards/handwired/onekey/keymaps/rgb/keymap.c [new file with mode: 0644]
keyboards/handwired/onekey/keymaps/rgb/rules.mk [new file with mode: 0644]
keyboards/handwired/onekey/proton_c/config.h
keyboards/handwired/onekey/stm32f0_disco/config.h
quantum/stm32/proton_c.mk
tmk_core/protocol/chibios/main.c

index b2850c3cff22c3c411c6e34f5b92177acaaabdf4..3e69361fbdaf87fbde2a939007356a89680132fb 100644 (file)
@@ -107,7 +107,7 @@ Where `X_Y` is the location of the LED in the matrix defined by [the datasheet](
 
 ---
 
-### WS2812 (AVR only)
+### WS2812
 
 There is basic support for addressable RGB matrix lighting with a WS2811/WS2812{a,b,c} addressable LED strand. To enable it, add this to your `rules.mk`:
 
index be4ddfa72956992161e61c1cf97a4eff3b7c7294..397dde587df234a82614bf018e496dc969dee865 100644 (file)
@@ -6,7 +6,7 @@ QMK has the ability to control RGB LEDs attached to your keyboard. This is commo
 
 Some keyboards come with RGB LEDs preinstalled. Others must have them installed after the fact. See the [Hardware Modification](#hardware-modification) section for information on adding RGB lighting to your keyboard.
 
-Currently QMK supports the following addressable LEDs on AVR microcontrollers (however, the white LED in RGBW variants is not supported):
+Currently QMK supports the following addressable LEDs (however, the white LED in RGBW variants is not supported):
 
  * WS2811, WS2812, WS2812B, WS2812C, etc.
  * SK6812, SK6812MINI, SK6805
index 321ba2a4bcb536c784a60bdf69b12e9520a57233..16518779570a133605d8577a1088ea572114efa5 100644 (file)
@@ -22,7 +22,7 @@ Support for SSD1306 based OLED displays. For more information see the [OLED Driv
 
 You can make use of uGFX within QMK to drive character and graphic LCDs, LED arrays, OLED, TFT, and other display technologies. This needs to be better documented, if you are trying to do this and reading the code doesn't help please [open an issue](https://github.com/qmk/qmk_firmware/issues/new) and we can help you through the process.
 
-## WS2812 (AVR Only)
+## WS2812
 
 Support for WS2811/WS2812{a,b,c} LED's. For more information see the [RGB Light](feature_rgblight.md) page.
 
index 6fa5d324cfbff02918d8f9fa8010d78566f28734..67481c458db68cf6011a8aa42484d15c703ba244 100644 (file)
@@ -1,13 +1,22 @@
 # WS2812 Driver
 This driver powers the [RGB Lighting](feature_rgblight.md) and [RGB Matrix](feature_rgb_matrix.md) features.
 
-Currently QMK supports the following addressable LEDs on AVR microcontrollers (however, the white LED in RGBW variants is not supported):
+Currently QMK supports the following addressable LEDs (however, the white LED in RGBW variants is not supported):
 
     WS2811, WS2812, WS2812B, WS2812C, etc.
     SK6812, SK6812MINI, SK6805
 
 These LEDs are called "addressable" because instead of using a wire per color, each LED contains a small microchip that understands a special protocol sent over a single wire. The chip passes on the remaining data to the next LED, allowing them to be chained together. In this way, you can easily control the color of the individual LEDs.
 
+## Supported Driver Types
+
+|          | AVR                | ARM                |
+|----------|--------------------|--------------------|
+| bit bang | :heavy_check_mark: | :heavy_check_mark: |
+| I2C      | :heavy_check_mark: |                    |
+| SPI      |                    | Soon™              |
+| PWM      |                    | Soon™              |
+
 ## Driver configuration
 
 ### Bitbang
@@ -17,7 +26,7 @@ Default driver, the absence of configuration assumes this driver. To configure i
 WS2812_DRIVER = bitbang
 ```
 
-!> ARM does not yet support WS2182. Progress is being made, but we are not quite there, yet.
+!> This driver is not hardware accelerated and may not be performant on heavily loaded systems.
 
 ### I2C
 Targeting boards where WS2812 support is offloaded to a 2nd MCU. Currently the driver is limited to AVR given the known consumers are ps2avrGB/BMC. To configure it, add this to your rules.mk:
index 2094e50098afafe77ef35ef77d4f9f0f012eb9b6..b076eff3300293f39fe35f817ec5ab5453517eaa 100644 (file)
@@ -1 +1,100 @@
-#error("NOT SUPPORTED")
\ No newline at end of file
+#include "quantum.h"
+#include "ws2812.h"
+#include "ch.h"
+#include "hal.h"
+
+/* Adapted from https://github.com/bigjosh/SimpleNeoPixelDemo/ */
+
+#ifndef NOP_FUDGE
+#    if defined(STM32F1XX) || defined(STM32F1xx) || defined(STM32F0XX) || defined(STM32F0xx) || defined(STM32F3XX) || defined(STM32F3xx) || defined(STM32L0XX) || 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
+
+#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 10000  // 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) { setPinOutput(RGB_DI_PIN); }
+
+// 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
+        sendByte(ledarray[i].g);
+        sendByte(ledarray[i].r);
+        sendByte(ledarray[i].b);
+    }
+
+    wait_ns(RES);
+
+    chSysUnlock();
+}
+
+// Setleds for SK6812RGBW
+void ws2812_setleds_rgbw(LED_TYPE *ledarray, uint16_t leds) {
+// not supported - for now error out if its enabled
+#ifdef RGBW
+#    error "RGBW not supported"
+#endif
+}
diff --git a/drivers/arm/ws2812.h b/drivers/arm/ws2812.h
new file mode 100644 (file)
index 0000000..bf5c9fd
--- /dev/null
@@ -0,0 +1,17 @@
+#pragma once
+
+#include "quantum/color.h"
+
+/* User Interface
+ *
+ * Input:
+ *         ledarray:           An array of GRB data describing the LED colors
+ *         number_of_leds:     The number of LEDs to write
+ *
+ * The functions will perform the following actions:
+ *         - Set the data-out pin as output
+ *         - Send out the LED data
+ *         - Wait 50�s to reset the LEDs
+ */
+void ws2812_setleds(LED_TYPE *ledarray, uint16_t number_of_leds);
+void ws2812_setleds_rgbw(LED_TYPE *ledarray, uint16_t number_of_leds);
index 81282ae1ffc358d0c02546810fdef1a1d8ca9ba5..01555e43152f27fefae253e72066e9727527d8de 100644 (file)
@@ -25,3 +25,5 @@
 #define BACKLIGHT_PIN           A0
 #define BACKLIGHT_PWM_DRIVER    PWMD2
 #define BACKLIGHT_PWM_CHANNEL   1
+
+#define RGB_DI_PIN A1
diff --git a/keyboards/handwired/onekey/keymaps/rgb/config.h b/keyboards/handwired/onekey/keymaps/rgb/config.h
new file mode 100644 (file)
index 0000000..89e7632
--- /dev/null
@@ -0,0 +1,4 @@
+#pragma once
+
+#define RGBLED_NUM 9
+#define RGBLIGHT_ANIMATIONS
diff --git a/keyboards/handwired/onekey/keymaps/rgb/keymap.c b/keyboards/handwired/onekey/keymaps/rgb/keymap.c
new file mode 100644 (file)
index 0000000..a96c6f3
--- /dev/null
@@ -0,0 +1,11 @@
+#include QMK_KEYBOARD_H
+
+const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
+  LAYOUT( RGB_MOD )
+};
+
+void keyboard_post_init_user(void) {
+  rgblight_enable_noeeprom();
+  rgblight_sethsv_noeeprom_cyan();
+  rgblight_mode_noeeprom(RGBLIGHT_MODE_RAINBOW_SWIRL);
+}
diff --git a/keyboards/handwired/onekey/keymaps/rgb/rules.mk b/keyboards/handwired/onekey/keymaps/rgb/rules.mk
new file mode 100644 (file)
index 0000000..1e3cebb
--- /dev/null
@@ -0,0 +1 @@
+RGBLIGHT_ENABLE = yes
index d4fb9c829975e4c5ce43be19fde5761d78b81efb..fe34f94add3f96d4f8e0c1241512e5a83e429e86 100644 (file)
@@ -26,3 +26,5 @@
 #define BACKLIGHT_PWM_DRIVER    PWMD4
 #define BACKLIGHT_PWM_CHANNEL   3
 #define BACKLIGHT_PAL_MODE      2
+
+#define RGB_DI_PIN A1
index 4024ee1caa6bd68e91908c50d9057b2632a506cf..637ed65d3fa3c8f93d12cc409db4a24555e065da 100644 (file)
@@ -26,3 +26,5 @@
 #define BACKLIGHT_PWM_DRIVER    PWMD3
 #define BACKLIGHT_PWM_CHANNEL   3
 #define BACKLIGHT_PAL_MODE      0
+
+#define RGB_DI_PIN B15
index 193e09ca1e09702e4ea9f12f77c7cf8e03a12ed0..ff28a4cb5d585694283a0aeeb4ee74fdce081e0d 100644 (file)
@@ -2,7 +2,7 @@
 
 # These are defaults based on what has been implemented for ARM boards
 AUDIO_ENABLE = yes
-RGBLIGHT_ENABLE = no
+WS2812_DRIVER = bitbang
 
 # Force task driven PWM until ARM can provide automatic configuration
 ifneq ($(strip $(BACKLIGHT_ENABLE)), no)
index de2b493b8418ce12397ab07d7b4c5ac06dc01673..c304f4d795c2b7b1bcd1bf2d6a997c14e9d5fc76 100644 (file)
 #include "sendchar.h"
 #include "debug.h"
 #include "printf.h"
+#include "rgblight_reconfig.h"
+
+#if (defined(RGB_MIDI) || defined(RGBLIGHT_ANIMATIONS)) && defined(RGBLIGHT_ENABLE)
+#    include "rgblight.h"
+#endif
 #ifdef SLEEP_LED_ENABLE
 #    include "sleep_led.h"
 #endif
@@ -214,6 +219,9 @@ int main(void) {
 #endif
 #ifdef RAW_ENABLE
         raw_hid_task();
+#endif
+#if defined(RGBLIGHT_ANIMATIONS) && defined(RGBLIGHT_ENABLE)
+        rgblight_task();
 #endif
     }
 }