mirror of
https://github.com/Keychron/qmk_firmware.git
synced 2024-12-12 13:17:38 +06:00
208ebf54a9
* Begin WS2812 API rework * Move RGBW conversion, clean up color.h, fix RGBW for AVR bitbang * Formatting & update PS2AVRGB I2C driver (untested) * Tested ARM bitbang RGB+RGBW * Tested ARM SPI RGB - RGBW not working * Tested ARM PWM RGB+RGBW * Tested RP2040 PIO driver RGB+RGBW * Update RGBLight * Formatting * Fix BM60HSRGB rev2 * Fix oddforge/vea * Fix 1k and XD002 RGBLite * Fix model_m/mschwingen * Fix handwired/promethium * Rename `WS2812_LED_TOTAL` for BM60HSRGB * Fix work_louder boards * Fix dawn60 * Fix rgbkb/pan * Fix neson_design/700e and n6 * Fix ergodox_ez/shine * ergodox_ez/shine: invert indices for left half * Fix matrix/abelx * Fix matrix/m20add * Remove custom rgblight driver for matrix/noah - should be done with lighting layers * Fix LED indexes for RGBLight split * Rename `convert_rgb_to_rgbw()` to `ws2812_rgb_to_rgbw()` * Update WS2812 API docs * `ergodox_ez/shine`: simplify LED index calculation * LED/RGB Matrix: Add weak function for LED index resolution * Bandaid fix for RGB Matrix splits not using WS2812 * `steelseries/prime_plus`: redo custom RGBLight driver * Update keyboards/steelseries/prime_plus/rgblight_custom.c Co-authored-by: Dasky <32983009+daskygit@users.noreply.github.com> --------- Co-authored-by: Dasky <32983009+daskygit@users.noreply.github.com>
222 lines
7.1 KiB
C
222 lines
7.1 KiB
C
#include "ws2812.h"
|
|
#include "gpio.h"
|
|
#include "util.h"
|
|
#include "chibios_config.h"
|
|
|
|
/* Adapted from https://github.com/gamazeps/ws2812b-chibios-SPIDMA/ */
|
|
|
|
// Define the spi your LEDs are plugged to here
|
|
#ifndef WS2812_SPI_DRIVER
|
|
# define WS2812_SPI_DRIVER SPID1
|
|
#endif
|
|
|
|
#ifndef WS2812_SPI_MOSI_PAL_MODE
|
|
# define WS2812_SPI_MOSI_PAL_MODE 5
|
|
#endif
|
|
|
|
#ifndef WS2812_SPI_SCK_PAL_MODE
|
|
# define WS2812_SPI_SCK_PAL_MODE 5
|
|
#endif
|
|
|
|
#ifndef WS2812_SPI_DIVISOR
|
|
# define WS2812_SPI_DIVISOR 16
|
|
#endif
|
|
|
|
// Push Pull or Open Drain Configuration
|
|
// Default Push Pull
|
|
#ifndef WS2812_EXTERNAL_PULLUP
|
|
# if defined(USE_GPIOV1)
|
|
# define WS2812_MOSI_OUTPUT_MODE PAL_MODE_ALTERNATE_PUSHPULL
|
|
# else
|
|
# define WS2812_MOSI_OUTPUT_MODE PAL_MODE_ALTERNATE(WS2812_SPI_MOSI_PAL_MODE) | PAL_OUTPUT_TYPE_PUSHPULL
|
|
# endif
|
|
#else
|
|
# if defined(USE_GPIOV1)
|
|
# define WS2812_MOSI_OUTPUT_MODE PAL_MODE_ALTERNATE_OPENDRAIN
|
|
# else
|
|
# define WS2812_MOSI_OUTPUT_MODE PAL_MODE_ALTERNATE(WS2812_SPI_MOSI_PAL_MODE) | PAL_OUTPUT_TYPE_OPENDRAIN
|
|
# endif
|
|
#endif
|
|
|
|
// Define SPI config speed
|
|
// baudrate should target 3.2MHz
|
|
// F072 fpclk = 48MHz
|
|
// 48/16 = 3Mhz
|
|
#if WS2812_SPI_DIVISOR == 2
|
|
# define WS2812_SPI_DIVISOR_CR1_BR_X (0)
|
|
#elif WS2812_SPI_DIVISOR == 4
|
|
# define WS2812_SPI_DIVISOR_CR1_BR_X (SPI_CR1_BR_0)
|
|
#elif WS2812_SPI_DIVISOR == 8
|
|
# define WS2812_SPI_DIVISOR_CR1_BR_X (SPI_CR1_BR_1)
|
|
#elif WS2812_SPI_DIVISOR == 16 // default
|
|
# define WS2812_SPI_DIVISOR_CR1_BR_X (SPI_CR1_BR_1 | SPI_CR1_BR_0)
|
|
#elif WS2812_SPI_DIVISOR == 32
|
|
# define WS2812_SPI_DIVISOR_CR1_BR_X (SPI_CR1_BR_2)
|
|
#elif WS2812_SPI_DIVISOR == 64
|
|
# define WS2812_SPI_DIVISOR_CR1_BR_X (SPI_CR1_BR_2 | SPI_CR1_BR_0)
|
|
#elif WS2812_SPI_DIVISOR == 128
|
|
# define WS2812_SPI_DIVISOR_CR1_BR_X (SPI_CR1_BR_2 | SPI_CR1_BR_1)
|
|
#elif WS2812_SPI_DIVISOR == 256
|
|
# define WS2812_SPI_DIVISOR_CR1_BR_X (SPI_CR1_BR_2 | SPI_CR1_BR_1 | SPI_CR1_BR_0)
|
|
#else
|
|
# error "Configured WS2812_SPI_DIVISOR value is not supported at this time."
|
|
#endif
|
|
|
|
// Use SPI circular buffer
|
|
#ifdef WS2812_SPI_USE_CIRCULAR_BUFFER
|
|
# define WS2812_SPI_BUFFER_MODE 1 // circular buffer
|
|
#else
|
|
# define WS2812_SPI_BUFFER_MODE 0 // normal buffer
|
|
#endif
|
|
|
|
#if defined(USE_GPIOV1)
|
|
# define WS2812_SCK_OUTPUT_MODE PAL_MODE_ALTERNATE_PUSHPULL
|
|
#else
|
|
# define WS2812_SCK_OUTPUT_MODE PAL_MODE_ALTERNATE(WS2812_SPI_SCK_PAL_MODE) | PAL_OUTPUT_TYPE_PUSHPULL
|
|
#endif
|
|
|
|
#define BYTES_FOR_LED_BYTE 4
|
|
#ifdef WS2812_RGBW
|
|
# define WS2812_CHANNELS 4
|
|
#else
|
|
# define WS2812_CHANNELS 3
|
|
#endif
|
|
#define BYTES_FOR_LED (BYTES_FOR_LED_BYTE * WS2812_CHANNELS)
|
|
#define DATA_SIZE (BYTES_FOR_LED * WS2812_LED_COUNT)
|
|
#define RESET_SIZE (1000 * WS2812_TRST_US / (2 * WS2812_TIMING))
|
|
#define PREAMBLE_SIZE 4
|
|
|
|
static uint8_t txbuf[PREAMBLE_SIZE + DATA_SIZE + RESET_SIZE] = {0};
|
|
|
|
/*
|
|
* As the trick here is to use the SPI to send a huge pattern of 0 and 1 to
|
|
* the ws2812b protocol, we use this helper function to translate bytes into
|
|
* 0s and 1s for the LED (with the appropriate timing).
|
|
*/
|
|
static uint8_t get_protocol_eq(uint8_t data, int pos) {
|
|
uint8_t eq = 0;
|
|
if (data & (1 << (2 * (3 - pos))))
|
|
eq = 0b1110;
|
|
else
|
|
eq = 0b1000;
|
|
if (data & (2 << (2 * (3 - pos))))
|
|
eq += 0b11100000;
|
|
else
|
|
eq += 0b10000000;
|
|
return eq;
|
|
}
|
|
|
|
static void set_led_color_rgb(ws2812_led_t color, int pos) {
|
|
uint8_t* tx_start = &txbuf[PREAMBLE_SIZE];
|
|
|
|
#if (WS2812_BYTE_ORDER == WS2812_BYTE_ORDER_GRB)
|
|
for (int j = 0; j < 4; j++)
|
|
tx_start[BYTES_FOR_LED * pos + j] = get_protocol_eq(color.g, j);
|
|
for (int j = 0; j < 4; j++)
|
|
tx_start[BYTES_FOR_LED * pos + BYTES_FOR_LED_BYTE + j] = get_protocol_eq(color.r, j);
|
|
for (int j = 0; j < 4; j++)
|
|
tx_start[BYTES_FOR_LED * pos + BYTES_FOR_LED_BYTE * 2 + j] = get_protocol_eq(color.b, j);
|
|
#elif (WS2812_BYTE_ORDER == WS2812_BYTE_ORDER_RGB)
|
|
for (int j = 0; j < 4; j++)
|
|
tx_start[BYTES_FOR_LED * pos + j] = get_protocol_eq(color.r, j);
|
|
for (int j = 0; j < 4; j++)
|
|
tx_start[BYTES_FOR_LED * pos + BYTES_FOR_LED_BYTE + j] = get_protocol_eq(color.g, j);
|
|
for (int j = 0; j < 4; j++)
|
|
tx_start[BYTES_FOR_LED * pos + BYTES_FOR_LED_BYTE * 2 + j] = get_protocol_eq(color.b, j);
|
|
#elif (WS2812_BYTE_ORDER == WS2812_BYTE_ORDER_BGR)
|
|
for (int j = 0; j < 4; j++)
|
|
tx_start[BYTES_FOR_LED * pos + j] = get_protocol_eq(color.b, j);
|
|
for (int j = 0; j < 4; j++)
|
|
tx_start[BYTES_FOR_LED * pos + BYTES_FOR_LED_BYTE + j] = get_protocol_eq(color.g, j);
|
|
for (int j = 0; j < 4; j++)
|
|
tx_start[BYTES_FOR_LED * pos + BYTES_FOR_LED_BYTE * 2 + j] = get_protocol_eq(color.r, j);
|
|
#endif
|
|
#ifdef WS2812_RGBW
|
|
for (int j = 0; j < 4; j++)
|
|
tx_start[BYTES_FOR_LED * pos + BYTES_FOR_LED_BYTE * 3 + j] = get_protocol_eq(color.w, j);
|
|
#endif
|
|
}
|
|
|
|
ws2812_led_t ws2812_leds[WS2812_LED_COUNT];
|
|
|
|
void ws2812_init(void) {
|
|
palSetLineMode(WS2812_DI_PIN, WS2812_MOSI_OUTPUT_MODE);
|
|
|
|
#ifdef WS2812_SPI_SCK_PIN
|
|
palSetLineMode(WS2812_SPI_SCK_PIN, WS2812_SCK_OUTPUT_MODE);
|
|
#endif // WS2812_SPI_SCK_PIN
|
|
|
|
// TODO: more dynamic baudrate
|
|
static const SPIConfig spicfg = {
|
|
#ifndef HAL_LLD_SELECT_SPI_V2
|
|
// HAL_SPI_V1
|
|
# if SPI_SUPPORTS_CIRCULAR == TRUE
|
|
WS2812_SPI_BUFFER_MODE,
|
|
# endif
|
|
NULL, // end_cb
|
|
PAL_PORT(WS2812_DI_PIN),
|
|
PAL_PAD(WS2812_DI_PIN),
|
|
# if defined(WB32F3G71xx) || defined(WB32FQ95xx)
|
|
0,
|
|
0,
|
|
WS2812_SPI_DIVISOR
|
|
# else
|
|
WS2812_SPI_DIVISOR_CR1_BR_X,
|
|
0
|
|
# endif
|
|
#else
|
|
// HAL_SPI_V2
|
|
# if SPI_SUPPORTS_CIRCULAR == TRUE
|
|
WS2812_SPI_BUFFER_MODE,
|
|
# endif
|
|
# if SPI_SUPPORTS_SLAVE_MODE == TRUE
|
|
false,
|
|
# endif
|
|
NULL, // data_cb
|
|
NULL, // error_cb
|
|
PAL_PORT(WS2812_DI_PIN),
|
|
PAL_PAD(WS2812_DI_PIN),
|
|
WS2812_SPI_DIVISOR_CR1_BR_X,
|
|
0
|
|
#endif
|
|
};
|
|
|
|
spiAcquireBus(&WS2812_SPI_DRIVER); /* Acquire ownership of the bus. */
|
|
spiStart(&WS2812_SPI_DRIVER, &spicfg); /* Setup transfer parameters. */
|
|
spiSelect(&WS2812_SPI_DRIVER); /* Slave Select assertion. */
|
|
#ifdef WS2812_SPI_USE_CIRCULAR_BUFFER
|
|
spiStartSend(&WS2812_SPI_DRIVER, ARRAY_SIZE(txbuf), txbuf);
|
|
#endif
|
|
}
|
|
|
|
void ws2812_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {
|
|
ws2812_leds[index].r = red;
|
|
ws2812_leds[index].g = green;
|
|
ws2812_leds[index].b = blue;
|
|
#if defined(WS2812_RGBW)
|
|
ws2812_rgb_to_rgbw(&ws2812_leds[index]);
|
|
#endif
|
|
}
|
|
|
|
void ws2812_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {
|
|
for (int i = 0; i < WS2812_LED_COUNT; i++) {
|
|
ws2812_set_color(i, red, green, blue);
|
|
}
|
|
}
|
|
|
|
void ws2812_flush(void) {
|
|
for (int i = 0; i < WS2812_LED_COUNT; i++) {
|
|
set_led_color_rgb(ws2812_leds[i], i);
|
|
}
|
|
|
|
// Send async - each led takes ~0.03ms, 50 leds ~1.5ms, animations flushing faster than send will cause issues.
|
|
// Instead spiSend can be used to send synchronously (or the thread logic can be added back).
|
|
#ifndef WS2812_SPI_USE_CIRCULAR_BUFFER
|
|
# ifdef WS2812_SPI_SYNC
|
|
spiSend(&WS2812_SPI_DRIVER, ARRAY_SIZE(txbuf), txbuf);
|
|
# else
|
|
spiStartSend(&WS2812_SPI_DRIVER, ARRAY_SIZE(txbuf), txbuf);
|
|
# endif
|
|
#endif
|
|
}
|