267 lines
7.2 KiB
C
267 lines
7.2 KiB
C
///
|
|
//
|
|
// leds.h defines structs and functions for addressing an HD107s ledstrip over serial using the GPIO pins.
|
|
//
|
|
///
|
|
|
|
#ifndef _potion_leds_h
|
|
#define _potion_leds_h
|
|
|
|
#include <FreeRTOS.h>
|
|
#include <stdint.h>
|
|
#include <stddef.h>
|
|
#include <unistd.h>
|
|
#include "shared.h"
|
|
#include "esp8266/gpio_register.h"
|
|
#include "esp_system.h"
|
|
#include "rom/ets_sys.h"
|
|
#include "driver/gpio.h"
|
|
|
|
enum leds_send_state_t {
|
|
LEDS_SEND_WAITING,
|
|
LEDS_SEND_REQUESTED,
|
|
LEDS_SENDING,
|
|
};
|
|
|
|
// pack the struct to match exactly 8 * 4 = 32bits
|
|
struct __attribute__((__packed__)) led_components_t {
|
|
// RGB component values
|
|
uint8_t red;
|
|
uint8_t green;
|
|
uint8_t blue;
|
|
uint8_t global; // global baseline brightness, highest 3 bits should always be ones
|
|
};
|
|
|
|
// union of components and their representation as a u32
|
|
// allows for easier sending of data over serial
|
|
union led_t {
|
|
struct led_components_t components;
|
|
uint32_t bits;
|
|
};
|
|
|
|
// point on a gradient
|
|
struct gradient_point_t {
|
|
union led_t led; // value of the led at this point
|
|
size_t offset; // offset (measured in leds) from the beginning
|
|
short movement; // direction of movement over time
|
|
};
|
|
|
|
struct gradient_t {
|
|
struct gradient_point_t points[16]; // array of gradient points, support at most 16 points in a gradient
|
|
size_t points_len; // number of used gradient points
|
|
float duration; // amount of time to allow this gradient to last
|
|
// positive means an amount in second 0 or negative means indefinitely until further notice
|
|
};
|
|
|
|
// buffer that will be written out to the led strip over serial
|
|
uint32_t g_serial_out_buffer[122];
|
|
// 120-long slice of the out buffer that represents the first few leds
|
|
union led_t* g_leds = ((union led_t*)g_serial_out_buffer + 1);
|
|
|
|
struct gradient_t g_default_gradient;
|
|
struct gradient_t g_current_gradient;
|
|
int g_leds_are_default = 1;
|
|
enum leds_send_state_t g_leds_send_state = LEDS_SEND_WAITING;
|
|
|
|
SemaphoreHandle_t g_led_mutex; // mutex governing access to data for leds
|
|
// use for all global data defined in this header
|
|
|
|
#define CLOCK 4
|
|
#define DATA 5
|
|
|
|
static
|
|
void leds_config_gpio() {
|
|
gpio_config_t config = {
|
|
.intr_type = GPIO_INTR_DISABLE,
|
|
.mode = GPIO_MODE_OUTPUT,
|
|
.pin_bit_mask = 0x18030,
|
|
.pull_up_en = 0,
|
|
.pull_down_en = 0,
|
|
};
|
|
gpio_config(&config);
|
|
|
|
}
|
|
|
|
static
|
|
void serial_write(int high) {
|
|
// set clock out to high, triggering a rising edge
|
|
gpio_set_level(CLOCK, 1);
|
|
// write bit to data out
|
|
gpio_set_level(DATA, high);
|
|
|
|
ets_delay_us(1);
|
|
|
|
// set clock to low, triggering a falling edge, shifting the LEDs shift register
|
|
gpio_set_level(CLOCK, 0);
|
|
}
|
|
|
|
static
|
|
void send_leds() {
|
|
// index of the bit being written
|
|
// fixed point number where the first 5 bits are the bit of a 32bit integger, and the rest is the integer
|
|
int write_bit = 0;
|
|
int write_next = 0;
|
|
|
|
gpio_set_level(CLOCK, 0);
|
|
|
|
while(write_bit < sizeof(g_serial_out_buffer) * 8) {
|
|
// fetch the bit being addressed
|
|
write_next = 0x1 & (g_serial_out_buffer[write_bit >> 5] >> (32 - (write_bit & 0x1F)));
|
|
|
|
serial_write(write_next);
|
|
|
|
write_bit++;
|
|
}
|
|
|
|
gpio_set_level(CLOCK, 0);
|
|
gpio_set_level(DATA, 0);
|
|
|
|
}
|
|
|
|
static inline
|
|
uint8_t lerp_uint8(uint8_t a, uint8_t b, float t) {
|
|
if(t <= 0) return a;
|
|
else if(t >= 1.0) return b;
|
|
else {
|
|
int dir = b - a;
|
|
return a + dir * t;
|
|
}
|
|
}
|
|
|
|
static inline
|
|
void lerp_led(union led_t* out, const union led_t* from, const union led_t* to, float t) {
|
|
out->components.red = lerp_uint8(from->components.red, to->components.red, t);
|
|
out->components.green = lerp_uint8(from->components.green, to->components.green, t);
|
|
out->components.blue = lerp_uint8(from->components.blue, to->components.blue, t);
|
|
uint8_t glob_from = from->components.global & ~0xE0;
|
|
uint8_t glob_to = to->components.global & ~0xE0;
|
|
out->components.global = GLOBAL(lerp_uint8(glob_from, glob_to, t));
|
|
}
|
|
|
|
static inline
|
|
void lerp_points_between(const struct gradient_point_t from, const struct gradient_point_t to) {
|
|
const int dif = to.offset - from.offset;
|
|
float t = 0.f;
|
|
for(int led = from.offset; led <= to.offset; ++led) {
|
|
t = (float)(led - from.offset) / (float)dif;
|
|
lerp_led(g_leds + led, &from.led, &to.led, t);
|
|
}
|
|
}
|
|
|
|
static
|
|
void set_led_range(int start, int end, union led_t value) {
|
|
for(int i = start; i < end; ++i) {
|
|
g_leds[i] = value;
|
|
}
|
|
}
|
|
|
|
static
|
|
void leds_set_default_gradient(const struct gradient_t* gradient) {
|
|
g_default_gradient = *gradient;
|
|
}
|
|
|
|
static
|
|
void leds_set_current_gradient(const struct gradient_t* gradient, int defer_send) {
|
|
struct gradient_point_t from = gradient->points[0];
|
|
struct gradient_point_t to;
|
|
|
|
set_led_range(0, gradient->points[0].offset, gradient->points[0].led);
|
|
set_led_range(gradient->points[gradient->points_len-1].offset, 120, gradient->points[gradient->points_len-1].led);
|
|
|
|
g_current_gradient = *gradient;
|
|
|
|
for(int i = 1; i < gradient->points_len; ++i) {
|
|
to = gradient->points[i];
|
|
lerp_points_between(from, to);
|
|
from = to;
|
|
}
|
|
|
|
if(!defer_send) {
|
|
send_leds();
|
|
}
|
|
}
|
|
|
|
void memswap(void* d, void* s, size_t n) {
|
|
void* tmp = malloc(n);
|
|
memcpy(tmp, s, n);
|
|
memcpy(s, d, n);
|
|
memcpy(d, tmp, n);
|
|
free(tmp);
|
|
}
|
|
|
|
void leds_animate() {
|
|
for(size_t i = 0; i < g_current_gradient.points_len; ++i) {
|
|
struct gradient_point_t* point = g_current_gradient.points + i;
|
|
if(point->movement > 0) {
|
|
point->offset = min(point->offset + 1, 120);
|
|
LOGLN("move result %i", point->offset);
|
|
LOGLN("next %d", (point+1)->offset);
|
|
|
|
if(point->offset > (point+1)->offset) {
|
|
memswap(point, point+1, sizeof(struct gradient_point_t));
|
|
LOGLN("swap down");
|
|
}
|
|
} else if(point->movement < 0) {
|
|
point->offset = max(0, point->offset - 1);
|
|
LOGLN("move result %i", point->offset);
|
|
LOGLN("next %d", (point-1)->offset);
|
|
|
|
if(point->offset < (point-1)->offset) {
|
|
memswap(point, point-1, sizeof(struct gradient_point_t));
|
|
LOGLN("swap up");
|
|
}
|
|
}
|
|
}
|
|
leds_set_current_gradient(&g_current_gradient, 1);
|
|
}
|
|
|
|
struct led_thread_data_t {
|
|
TaskHandle_t task;
|
|
TaskFunction_t func;
|
|
};
|
|
|
|
static void leds_thread();
|
|
|
|
static struct led_thread_data_t _thread_data = {
|
|
.task = 0,
|
|
.func = &leds_thread
|
|
};
|
|
|
|
static
|
|
void leds_thread() {
|
|
send_leds();
|
|
for(;;) {
|
|
vTaskDelay(10);
|
|
xSemaphoreTake(g_led_mutex, portMAX_DELAY);
|
|
send_leds();
|
|
leds_animate();
|
|
xSemaphoreGive(g_led_mutex);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static
|
|
void leds_init() {
|
|
g_serial_out_buffer[0] = 0u;
|
|
g_serial_out_buffer[61] = ~0u;
|
|
set_led_range(0, 120,
|
|
(union led_t){.components =
|
|
(struct led_components_t) {
|
|
.red = 0,
|
|
.green = 0,
|
|
.blue = 0,
|
|
.global = GLOBAL(5)
|
|
}}
|
|
);
|
|
|
|
g_led_mutex = xSemaphoreCreateMutex();
|
|
|
|
xTaskCreate(_thread_data.func, "Leds", 1024, NULL, 1, &_thread_data.task);
|
|
|
|
// initialize mutex for leds data
|
|
leds_config_gpio();
|
|
}
|
|
|
|
#endif // !_leds_h
|