1
0
mirror of https://github.com/whowechina/aic_pico.git synced 2024-12-13 13:01:04 +01:00
aic_pico/firmware/src/light.c

242 lines
5.6 KiB
C
Raw Normal View History

2023-11-04 13:30:53 +01:00
/*
2023-11-15 14:57:12 +01:00
* Light (WS2812 + LED) control
2023-11-04 13:30:53 +01:00
* WHowe <github.com/whowechina>
*
*/
2023-11-15 14:57:12 +01:00
#include "light.h"
2023-11-04 13:30:53 +01:00
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include "bsp/board.h"
#include "hardware/pio.h"
#include "hardware/timer.h"
2023-11-15 14:57:12 +01:00
#include "hardware/pwm.h"
2023-11-04 13:30:53 +01:00
#include "ws2812.pio.h"
#include "board_defs.h"
#include "config.h"
static uint32_t rgb_buf[16];
2023-11-15 14:57:12 +01:00
static uint8_t led_gpio[] = LED_DEF;
#define RGB_NUM (sizeof(rgb_buf) / sizeof(rgb_buf[0]))
#define LED_NUM (sizeof(led_gpio))
2023-11-04 13:30:53 +01:00
#define _MAP_LED(x) _MAKE_MAPPER(x)
#define _MAKE_MAPPER(x) MAP_LED_##x
#define MAP_LED_RGB { c1 = r; c2 = g; c3 = b; }
#define MAP_LED_GRB { c1 = g; c2 = r; c3 = b; }
#define REMAP_BUTTON_RGB _MAP_LED(BUTTON_RGB_ORDER)
#define REMAP_TT_RGB _MAP_LED(TT_RGB_ORDER)
static inline uint32_t _rgb32(uint32_t c1, uint32_t c2, uint32_t c3, bool gamma_fix)
{
if (gamma_fix) {
c1 = ((c1 + 1) * (c1 + 1) - 1) >> 8;
c2 = ((c2 + 1) * (c2 + 1) - 1) >> 8;
c3 = ((c3 + 1) * (c3 + 1) - 1) >> 8;
}
return (c1 << 16) | (c2 << 8) | (c3 << 0);
}
uint32_t rgb32(uint32_t r, uint32_t g, uint32_t b, bool gamma_fix)
{
#if BUTTON_RGB_ORDER == GRB
return _rgb32(g, r, b, gamma_fix);
#else
return _rgb32(r, g, b, gamma_fix);
#endif
}
uint32_t rgb32_from_hsv(uint8_t h, uint8_t s, uint8_t v)
{
uint32_t region, remainder, p, q, t;
if (s == 0) {
return v << 16 | v << 8 | v;
}
region = h / 43;
remainder = (h % 43) * 6;
p = (v * (255 - s)) >> 8;
q = (v * (255 - ((s * remainder) >> 8))) >> 8;
t = (v * (255 - ((s * (255 - remainder)) >> 8))) >> 8;
switch (region) {
case 0:
return v << 16 | t << 8 | p;
case 1:
return q << 16 | v << 8 | p;
case 2:
return p << 16 | v << 8 | t;
case 3:
return p << 16 | q << 8 | v;
case 4:
return t << 16 | p << 8 | v;
default:
return v << 16 | p << 8 | q;
}
}
2023-11-05 12:18:30 +01:00
static inline uint32_t apply_level(uint32_t color)
{
unsigned r = (color >> 16) & 0xff;
unsigned g = (color >> 8) & 0xff;
unsigned b = color & 0xff;
r = r * aic_cfg->led.level / 255;
g = g * aic_cfg->led.level / 255;
b = b * aic_cfg->led.level / 255;
return r << 16 | g << 8 | b;
}
/* 6 segment regular hsv color wheel, better color cycle
* https://www.arnevogel.com/rgb-rainbow/
* https://www.instructables.com/How-to-Make-Proper-Rainbow-and-Random-Colors-With-/
*/
#define COLOR_WHEEL_SIZE 256
static uint32_t color_wheel[COLOR_WHEEL_SIZE];
static void generate_color_wheel()
{
static uint8_t old_level = 0;
if (old_level == aic_cfg->led.level) {
return;
}
old_level = aic_cfg->led.level;
for (int i = 0; i < COLOR_WHEEL_SIZE; i++) {
2023-11-05 14:23:28 +01:00
color_wheel[i] = rgb32_from_hsv(i, 208, 255);
2023-11-05 12:18:30 +01:00
color_wheel[i] = apply_level(color_wheel[i]);
}
}
#define RAINBOW_PITCH 37
#define RAINBOW_MIN_SPEED 1
static uint32_t rainbow_speed = RAINBOW_MIN_SPEED;
static void rainbow_update()
2023-11-04 13:30:53 +01:00
{
static uint64_t last = 0;
uint64_t now = time_us_64();
2023-11-05 12:18:30 +01:00
if (now - last < 33333) { // no faster than 30Hz
2023-11-04 13:30:53 +01:00
return;
}
last = now;
2023-11-05 12:18:30 +01:00
static uint32_t rotator = 0;
rotator = (rotator + rainbow_speed) % COLOR_WHEEL_SIZE;
2023-11-15 14:57:12 +01:00
for (int i = 0; i < RGB_NUM; i++) {
2023-11-05 12:18:30 +01:00
uint32_t index = (rotator + RAINBOW_PITCH * i) % COLOR_WHEEL_SIZE;
rgb_buf[i] = color_wheel[index];
2023-11-04 13:30:53 +01:00
}
2023-11-15 14:57:12 +01:00
/* LED just follows rgb */
for (int i = 0; (i < LED_NUM) && (i < RGB_NUM); i++) {
int level = rgb_buf[i] & 0xff;
pwm_set_gpio_level(led_gpio[i], level * level);
}
2023-11-04 13:30:53 +01:00
}
2023-11-15 14:57:12 +01:00
void light_set_rainbow_speed(uint8_t speed)
2023-11-04 13:30:53 +01:00
{
2023-11-05 12:18:30 +01:00
rainbow_speed = speed / 8;
}
2023-11-04 13:30:53 +01:00
2023-11-05 12:18:30 +01:00
static void rainbow_speed_down()
{
static uint64_t last = 0;
uint64_t now = time_us_64();
if (now - last < 200000) {
return;
}
last = now;
2023-11-04 13:30:53 +01:00
2023-11-05 12:18:30 +01:00
if (rainbow_speed > RAINBOW_MIN_SPEED) {
rainbow_speed = rainbow_speed * 95 / 100;
}
}
static void drive_led()
{
static uint64_t last = 0;
uint64_t now = time_us_64();
if (now - last < 4000) { // no faster than 250Hz
return;
}
last = now;
2023-11-15 14:57:12 +01:00
for (int i = 0; i < RGB_NUM; i++) {
2023-11-05 12:18:30 +01:00
pio_sm_put_blocking(pio0, 0, rgb_buf[i] << 8u);
}
2023-11-04 13:30:53 +01:00
}
2023-11-15 14:57:12 +01:00
void light_set_color(unsigned index, uint32_t color)
2023-11-04 13:30:53 +01:00
{
2023-11-15 14:57:12 +01:00
if (index >= RGB_NUM) {
2023-11-04 13:30:53 +01:00
return;
}
rgb_buf[index] = apply_level(color);
}
2023-11-15 14:57:12 +01:00
void light_set_color_all(uint32_t color)
2023-11-04 13:30:53 +01:00
{
2023-11-15 14:57:12 +01:00
for (int i = 0; i < RGB_NUM; i++) {
2023-11-04 13:30:53 +01:00
rgb_buf[i] = apply_level(color);
}
}
2023-11-15 14:57:12 +01:00
void light_set_brg(unsigned index, const uint8_t *brg_array, size_t num)
2023-11-04 13:30:53 +01:00
{
2023-11-15 14:57:12 +01:00
if (index >= RGB_NUM) {
2023-11-04 13:30:53 +01:00
return;
}
2023-11-15 14:57:12 +01:00
if (index + num > RGB_NUM) {
num = RGB_NUM - index;
2023-11-04 13:30:53 +01:00
}
for (int i = 0; i < num; i++) {
uint8_t b = brg_array[i * 3 + 0];
uint8_t r = brg_array[i * 3 + 1];
uint8_t g = brg_array[i * 3 + 2];
rgb_buf[index + i] = apply_level(rgb32(r, g, b, false));
}
}
2023-11-15 14:57:12 +01:00
void light_init()
2023-11-04 13:30:53 +01:00
{
uint pio0_offset = pio_add_program(pio0, &ws2812_program);
gpio_set_drive_strength(RGB_PIN, GPIO_DRIVE_STRENGTH_2MA);
ws2812_program_init(pio0, 0, pio0_offset, RGB_PIN, 800000, false);
2023-11-15 14:57:12 +01:00
for (int i = 0; i < LED_NUM; i++) {
gpio_init(led_gpio[i]);
gpio_set_dir(led_gpio[i], GPIO_OUT);
gpio_set_function(led_gpio[i], GPIO_FUNC_PWM);
int slice = pwm_gpio_to_slice_num(led_gpio[i]);
pwm_config cfg = pwm_get_default_config();
pwm_config_set_clkdiv(&cfg, 4.f);
pwm_init(slice, &cfg, true);
}
2023-11-05 12:18:30 +01:00
generate_color_wheel();
2023-11-04 13:30:53 +01:00
}
2023-11-15 14:57:12 +01:00
void light_update()
2023-11-04 13:30:53 +01:00
{
2023-11-05 12:18:30 +01:00
generate_color_wheel();
rainbow_update();
rainbow_speed_down();
2023-11-04 13:30:53 +01:00
drive_led();
}