ARM - ws2812 bitbang (#7173)

* 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
This commit is contained in:
Joel Challis 2019-11-07 13:10:29 +00:00 committed by GitHub
parent b9610091f5
commit 1ea0cac998
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 162 additions and 7 deletions

View 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`:

View 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

View 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.

View 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:

View File

@ -1 +1,100 @@
#error("NOT SUPPORTED")
#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
}

17
drivers/arm/ws2812.h Normal file
View File

@ -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<EFBFBD>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);

View File

@ -25,3 +25,5 @@
#define BACKLIGHT_PIN A0
#define BACKLIGHT_PWM_DRIVER PWMD2
#define BACKLIGHT_PWM_CHANNEL 1
#define RGB_DI_PIN A1

View File

@ -0,0 +1,4 @@
#pragma once
#define RGBLED_NUM 9
#define RGBLIGHT_ANIMATIONS

View File

@ -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);
}

View File

@ -0,0 +1 @@
RGBLIGHT_ENABLE = yes

View 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

View 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

View 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)

View File

@ -32,6 +32,11 @@
#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
}
}