266 lines
7.1 KiB
C++
266 lines
7.1 KiB
C++
// Ovladani signalizacni chytre LED diody
|
|
#include "wsLED.h"
|
|
#if defined(ARDUINO_ARCH_ESP32)
|
|
//# include "esp_task.h"
|
|
// https://github.com/JSchaenzle/ESP32-NeoPixel-WS2812-RMT/blob/master/ws2812_control.c
|
|
# include <driver/rmt.h>
|
|
# include <set>
|
|
# ifndef LED_RMT_TX_CHANNEL
|
|
# define LED_RMT_TX_CHANNEL RMT_CHANNEL_0
|
|
# endif
|
|
# define BITS_PER_LED_CMD 24
|
|
# define LED_BUFFER_ITEMS ((1 * BITS_PER_LED_CMD))
|
|
// These values are determined by measuring pulse timing with logic analyzer and adjusting to match datasheet.
|
|
# define T0H 14 // 0 bit high time
|
|
# define T1H 52 // 1 bit high time
|
|
# define TL 52 // low time for either bit
|
|
#endif
|
|
#if defined(ARDUINO_ARCH_ESP8266)
|
|
// due to linker overriding the ICACHE_RAM_ATTR for cpp files, these methods are
|
|
// moved into a C file so the attribute will be applied correctly
|
|
extern "C" void ICACHE_RAM_ATTR send_pixels_800(uint8_t* pixel, size_t count, uint8_t pin);
|
|
#endif
|
|
// TODO: Proverit ESP32 s https://github.com/adafruit/Adafruit_NeoPixel/blob/master/esp.c
|
|
// ... a s https://github.com/Freenove/Freenove_WS2812_Lib_for_ESP32
|
|
|
|
#define countof(a) (sizeof(a) / sizeof(a[0]))
|
|
|
|
static const uint16_t LB[] PROGMEM = {
|
|
0x201 , 0x202 , 0x103 , 0x104 , 0x105 , 0x106 , 0x107 , 0x109 , 0x10a , 0x10c , 0x10e , 0x110 , 0x112 , 0x114 , 0x116 , 0x119 , 0x11c , 0x11e , 0x122 , 0x125 , 0x128 , 0x12c , 0x130 , 0x134,
|
|
0x139 , 0x13d , 0x142 , 0x147 , 0x14d , 0x152 , 0x158 , 0x15e , 0x165 , 0x16b , 0x172 , 0x179 , 0x180 , 0x187 , 0x18e , 0x196 , 0x19d , 0x1a5 , 0x1ac , 0x1b3 , 0x1bb , 0x1c2 , 0x1c9 , 0x1cf,
|
|
0x1d6 , 0x1dc , 0x1e2 , 0x1e7 , 0x1ec , 0x1f0 , 0x1f4 , 0x1f7 , 0x1fa , 0x1fc , 0x1fd , 0x3fe , 0x1fd , 0x1fb , 0x1f8 , 0x1f5 , 0x1f2 , 0x1ed , 0x1e9 , 0x1e4 , 0x1de , 0x1d8 , 0x1d2 , 0x1cb,
|
|
0x1c4 , 0x1bd , 0x1b6 , 0x1af , 0x1a7 , 0x1a0 , 0x198 , 0x191 , 0x18a , 0x182 , 0x17b , 0x174 , 0x16d , 0x167 , 0x161 , 0x15a , 0x154 , 0x14f , 0x149 , 0x144 , 0x13f , 0x13a , 0x136 , 0x131,
|
|
0x12d , 0x12a , 0x126 , 0x123 , 0x11f , 0x11c , 0x11a , 0x117 , 0x115 , 0x112 , 0x110 , 0x10e , 0x10c , 0x10b , 0x109 , 0x108 , 0x107 , 0x105 , 0x104 , 0x203 , 0x102 , 0x201 , 0x2a00
|
|
};
|
|
|
|
inline bool wsLED::canShow(void)
|
|
{
|
|
|
|
return (micros() - _endtime) >= 70L;
|
|
}
|
|
|
|
void wsLED::show(void)
|
|
{
|
|
uint8_t color[3];
|
|
|
|
for (uint8_t i = 0; i < 3; i++) {
|
|
// https://github.com/FastLED/FastLED/wiki/FastLED-Color-Correction
|
|
uint16_t c = (uint16_t)_color[i] * (1 + (uint16_t)_scale);
|
|
color[i] = (uint8_t)(c >> 8);
|
|
}
|
|
while (!canShow())
|
|
__asm__ volatile ("nop");
|
|
#if defined(ARDUINO_ARCH_ESP32)
|
|
rmt_item32_t led_data_buffer[LED_BUFFER_ITEMS];
|
|
|
|
uint32_t bits_to_send = ((uint32_t)color[0] << 16) | ((uint32_t)color[1] << 8) | (uint32_t)color[2];
|
|
uint32_t mask = 1 << (BITS_PER_LED_CMD - 1);
|
|
for (uint32_t bit = 0; bit < BITS_PER_LED_CMD; bit++) {
|
|
uint32_t bit_is_set = bits_to_send & mask;
|
|
led_data_buffer[bit] = bit_is_set ? (rmt_item32_t) {{{T1H, 1, TL, 0}}} : (rmt_item32_t) {{{T0H, 1, TL, 0}}};
|
|
mask >>= 1;
|
|
}
|
|
ESP_ERROR_CHECK(rmt_write_items(LED_RMT_TX_CHANNEL, led_data_buffer, LED_BUFFER_ITEMS, false));
|
|
ESP_ERROR_CHECK(rmt_wait_tx_done(LED_RMT_TX_CHANNEL, portMAX_DELAY));
|
|
#else
|
|
noInterrupts();
|
|
send_pixels_800(color, 1, _pin);
|
|
interrupts();
|
|
#endif
|
|
_endtime = micros();
|
|
}
|
|
|
|
|
|
wsLED::wsLED(int pin, int order) : _pin(pin), _order(order), _ledState(LED_STOP)
|
|
{
|
|
}
|
|
|
|
wsLED::wsLED(void) : _pin(NOT_A_PIN), _order(RGB), _ledState(LED_STOP)
|
|
{
|
|
}
|
|
|
|
void wsLED::begin(int pin)
|
|
{
|
|
|
|
_pin = pin;
|
|
begin();
|
|
}
|
|
|
|
void wsLED::begin(void)
|
|
{
|
|
|
|
if (NOT_A_PIN != _pin) {
|
|
#if defined(ARDUINO_ARCH_ESP8266)
|
|
pinMode(_pin, OUTPUT);
|
|
#endif
|
|
#if defined(ARDUINO_ARCH_ESP32)
|
|
// TODO: mozna nejak zkusit najit volny RMT kanal?
|
|
rmt_config_t config;
|
|
|
|
config.rmt_mode = RMT_MODE_TX;
|
|
config.channel = LED_RMT_TX_CHANNEL;
|
|
config.gpio_num = (gpio_num_t)_pin;
|
|
config.mem_block_num = 3;
|
|
config.tx_config.loop_en = false;
|
|
config.tx_config.carrier_en = false;
|
|
config.tx_config.idle_output_en = true;
|
|
config.tx_config.idle_level = RMT_IDLE_LEVEL_LOW;
|
|
config.clk_div = 2;
|
|
|
|
ESP_ERROR_CHECK(rmt_config(&config));
|
|
ESP_ERROR_CHECK(rmt_driver_install(config.channel, 0, 0));
|
|
#endif
|
|
_color[0] = _color[1] = _color[2] = 0;
|
|
show();
|
|
}
|
|
}
|
|
|
|
void wsLED::setColor(LEDRGB color)
|
|
{
|
|
|
|
_color[(_order >> 6) & 7] = color.r;
|
|
_color[(_order >> 3) & 7] = color.g;
|
|
_color[_order & 7] = color.b;
|
|
}
|
|
|
|
void ICACHE_RAM_ATTR wsLED::rtLed(void)
|
|
{
|
|
|
|
switch (_ledState) {
|
|
case LED_BLINK:
|
|
_handler.detach();
|
|
if (_subState) {
|
|
_subState = 0;
|
|
setColor(_color2);
|
|
} else {
|
|
_subState = 1;
|
|
setColor(_color1);
|
|
}
|
|
show();
|
|
_handler.attach_ms(_blinkSpeed, lh, this);
|
|
break;
|
|
|
|
|
|
case LED_BREATH: {
|
|
_handler.detach();
|
|
++_subState;
|
|
_subState %= countof(LB);
|
|
uint16_t val = pgm_read_word(&LB[_subState]);
|
|
_scale = (uint8_t)val;
|
|
show();
|
|
val >>= 8;
|
|
uint32_t timing = 30ul * val;
|
|
_handler.attach_ms(timing, lh, this);
|
|
break;
|
|
}
|
|
|
|
case LED_PULSE: {
|
|
_handler.detach();
|
|
_ledState = _savedState;
|
|
_subState = 0;
|
|
setColor(_color1);
|
|
show();
|
|
_handler.attach_ms(1, lh, this); // co nejrychleji do predchoziho stavu
|
|
break;
|
|
}
|
|
|
|
case LED_PULSE_START:
|
|
_handler.detach();
|
|
setColor(_pulseColor);
|
|
show();
|
|
_ledState = LED_PULSE;
|
|
_handler.attach_ms(_pulseDuration, lh, this);
|
|
break;
|
|
|
|
default:
|
|
_handler.detach();
|
|
break;
|
|
}
|
|
}
|
|
|
|
void ICACHE_RAM_ATTR wsLED::lh(wsLED *ptr)
|
|
{
|
|
|
|
wsLED *pled = ptr;
|
|
pled->rtLed();
|
|
}
|
|
|
|
void wsLED::setColors(LEDRGB color1, LEDRGB color2)
|
|
{
|
|
|
|
_color1 = color1;
|
|
_color2 = color2;
|
|
if (LED_BREATH == _ledState) {
|
|
setColor(_color1);
|
|
}
|
|
}
|
|
|
|
void wsLED::blink(uint32_t speed)
|
|
{
|
|
|
|
if (NOT_A_PIN != _pin) {
|
|
if (LED_BLINK != _ledState) {
|
|
_handler.detach();
|
|
_ledState = LED_BLINK;
|
|
_scale = 0xff;
|
|
_subState = 1;
|
|
_blinkSpeed = speed;
|
|
_handler.attach_ms(1, lh, this);
|
|
}
|
|
}
|
|
}
|
|
|
|
void wsLED::breath(void)
|
|
{
|
|
|
|
if (NOT_A_PIN != _pin) {
|
|
if (LED_BREATH != _ledState) {
|
|
_handler.detach();
|
|
_ledState = LED_BREATH;
|
|
_subState = 0;
|
|
setColor(_color1);
|
|
_handler.attach_ms(1, lh, this);
|
|
}
|
|
}
|
|
}
|
|
|
|
void wsLED::setOrder(int neworder)
|
|
{
|
|
|
|
_order = neworder;
|
|
}
|
|
|
|
void wsLED::pulse(LEDRGB color, uint32_t duration)
|
|
{
|
|
|
|
if (NOT_A_PIN != _pin) {
|
|
_handler.detach();
|
|
_scale = 0xff;
|
|
_pulseColor = color;
|
|
if (LED_PULSE != _ledState) {
|
|
_savedState = _ledState;
|
|
}
|
|
_pulseDuration = duration;
|
|
_ledState = LED_PULSE_START;
|
|
_handler.attach_ms(5, lh, this);
|
|
}
|
|
}
|
|
|
|
ledstate_t wsLED::getState(void)
|
|
{
|
|
|
|
return _ledState;
|
|
}
|
|
|
|
void wsLED::stop(void)
|
|
{
|
|
|
|
if (NOT_A_PIN != _pin) {
|
|
_handler.detach();
|
|
_ledState = LED_STOP;
|
|
_scale = 0xff;
|
|
setColor(LEDRGB(0, 0, 0));
|
|
show();
|
|
}
|
|
}
|