potion_party_leds/main/leds.h

178 lines
4.5 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 <stdint.h>
#include <stddef.h>
#include <unistd.h>
#include "esp8266/gpio_register.h"
#include "esp_system.h"
#include "rom/ets_sys.h"
#include "shared.h"
#include "driver/gpio.h"
// 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
};
// buffer that will be written out to the led strip over serial
uint32_t g_serial_out_buffer[62];
// 60-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);
#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);
os_delay_us(2);
// set clock to low, triggering a falling edge, shifting the LEDs shift register
gpio_set_level(CLOCK, 0);
}
static
void send_leds() {
LOGLN("entering 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;
LOGLN("Setting clock low");
gpio_set_level(CLOCK, 0);
LOGLN("Writing to leds");
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_gradient(struct gradient_point_t* points, size_t points_len, int send) {
struct gradient_point_t from = points[0];
struct gradient_point_t to;
// set_led_range(0, points[0].offset, points[0].led);
// set_led_range(points[points_len-1].offset, 60, points[points_len-1].led);
for(int i = 1; i < points_len; ++i) {
to = points[i];
lerp_points_between(from, to);
from = to;
}
if(send) {
send_leds();
}
}
static
void leds_init() {
g_serial_out_buffer[0] = 0u;
g_serial_out_buffer[61] = ~0u;
set_led_range(0, 60,
(union led_t){.components =
(struct led_components_t) {
.red = 0,
.green = 0,
.blue = 0,
.global = GLOBAL(5)
}}
);
leds_config_gpio();
}
#endif // !_leds_h