mirror of
https://github.com/openstenoproject/qmk
synced 2024-11-22 08:24:41 +00:00
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:
parent
b9610091f5
commit
1ea0cac998
14 changed files with 162 additions and 7 deletions
|
@ -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`:
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
17
drivers/arm/ws2812.h
Normal 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);
|
|
@ -25,3 +25,5 @@
|
|||
#define BACKLIGHT_PIN A0
|
||||
#define BACKLIGHT_PWM_DRIVER PWMD2
|
||||
#define BACKLIGHT_PWM_CHANNEL 1
|
||||
|
||||
#define RGB_DI_PIN A1
|
||||
|
|
4
keyboards/handwired/onekey/keymaps/rgb/config.h
Normal file
4
keyboards/handwired/onekey/keymaps/rgb/config.h
Normal file
|
@ -0,0 +1,4 @@
|
|||
#pragma once
|
||||
|
||||
#define RGBLED_NUM 9
|
||||
#define RGBLIGHT_ANIMATIONS
|
11
keyboards/handwired/onekey/keymaps/rgb/keymap.c
Normal file
11
keyboards/handwired/onekey/keymaps/rgb/keymap.c
Normal 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);
|
||||
}
|
1
keyboards/handwired/onekey/keymaps/rgb/rules.mk
Normal file
1
keyboards/handwired/onekey/keymaps/rgb/rules.mk
Normal file
|
@ -0,0 +1 @@
|
|||
RGBLIGHT_ENABLE = yes
|
|
@ -26,3 +26,5 @@
|
|||
#define BACKLIGHT_PWM_DRIVER PWMD4
|
||||
#define BACKLIGHT_PWM_CHANNEL 3
|
||||
#define BACKLIGHT_PAL_MODE 2
|
||||
|
||||
#define RGB_DI_PIN A1
|
||||
|
|
|
@ -26,3 +26,5 @@
|
|||
#define BACKLIGHT_PWM_DRIVER PWMD3
|
||||
#define BACKLIGHT_PWM_CHANNEL 3
|
||||
#define BACKLIGHT_PAL_MODE 0
|
||||
|
||||
#define RGB_DI_PIN B15
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue