| @@ -3,6 +3,8 @@ | |||||
| cmake_minimum_required(VERSION 3.16) | cmake_minimum_required(VERSION 3.16) | ||||
| include($ENV{IDF_PATH}/tools/cmake/project.cmake) | include($ENV{IDF_PATH}/tools/cmake/project.cmake) | ||||
| include($ENV{IOT_SOLUTION_PATH}/component.cmake) | |||||
| project(blinky) | project(blinky) | ||||
| target_add_binary_data(blinky.elf "main/broker.pem" TEXT) | target_add_binary_data(blinky.elf "main/broker.pem" TEXT) | ||||
| @@ -11,6 +11,8 @@ idf_component_register( | |||||
| "leds.cpp" | "leds.cpp" | ||||
| "presets.cpp" | "presets.cpp" | ||||
| "spi_leds.cpp" | "spi_leds.cpp" | ||||
| "screen_leds.cpp" | |||||
| "display.cpp" | |||||
| "patterns/gradient.cpp" | "patterns/gradient.cpp" | ||||
| "patterns/random.cpp" | "patterns/random.cpp" | ||||
| "patterns/sparkle.cpp" | "patterns/sparkle.cpp" | ||||
| @@ -0,0 +1,80 @@ | |||||
| static const char *TAG = "display"; | |||||
| #include <esp_log.h> | |||||
| #include "display.hpp" | |||||
| Display::Display(size_t w, size_t h) : | |||||
| width(w), height(h), pitch(w), buffer(NULL), | |||||
| bus(NULL), iface_drv(NULL), driver{.deinit = NULL} | |||||
| { | |||||
| esp_err_t ret; | |||||
| spi_config_t bus_cfg = { | |||||
| .miso_io_num = GPIO_NUM_4, | |||||
| .mosi_io_num = GPIO_NUM_35, | |||||
| .sclk_io_num = GPIO_NUM_36, | |||||
| .max_transfer_sz = (int)(height * width * sizeof(*buffer) + 8), | |||||
| }; | |||||
| bus = spi_bus_create(SPI2_HOST, &bus_cfg); | |||||
| if (NULL == bus) { | |||||
| ESP_LOGE(TAG, "Failed to acquire bus"); | |||||
| return; | |||||
| } | |||||
| scr_interface_spi_config_t spi_dev_cfg = { | |||||
| .spi_bus = bus, | |||||
| .pin_num_cs = 34, | |||||
| .pin_num_dc = 37, | |||||
| .clk_freq = 40 * 1000 * 1000, | |||||
| .swap_data = 1, | |||||
| }; | |||||
| ret = scr_interface_create(SCREEN_IFACE_SPI, &spi_dev_cfg, &iface_drv); | |||||
| if (ESP_OK != ret) { | |||||
| ESP_LOGE(TAG, "Failed to create interface"); | |||||
| return; | |||||
| } | |||||
| ret = scr_find_driver(SCREEN_CONTROLLER_ST7789, &driver); | |||||
| if (ESP_OK != ret) { | |||||
| ESP_LOGE(TAG, "Failed to find driver"); | |||||
| return; | |||||
| } | |||||
| scr_controller_config_t lcd_cfg = { | |||||
| .interface_drv = iface_drv, | |||||
| .pin_num_rst = 38, | |||||
| .pin_num_bckl = 33, | |||||
| .rst_active_level = 0, | |||||
| .bckl_active_level = 1, | |||||
| .width = (uint16_t)width, | |||||
| .height = (uint16_t)height, | |||||
| .offset_hor = 52, | |||||
| .offset_ver = 40, | |||||
| .rotate = SCR_DIR_LRTB, | |||||
| }; | |||||
| ret = driver.init(&lcd_cfg); | |||||
| if (ESP_OK != ret) { | |||||
| ESP_LOGE(TAG, "Failed to initialize driver"); | |||||
| return; | |||||
| } | |||||
| buffer = new uint16_t[height * pitch](); | |||||
| refresh(); | |||||
| } | |||||
| Display::~Display() { | |||||
| if (buffer) delete[] buffer; | |||||
| if (driver.deinit) driver.deinit(); | |||||
| if (iface_drv) scr_interface_delete(iface_drv); | |||||
| if (bus) spi_bus_delete(&bus); | |||||
| } | |||||
| void Display::refresh() { | |||||
| driver.draw_bitmap(0, 0, width, height, buffer); | |||||
| } | |||||
| @@ -0,0 +1,28 @@ | |||||
| #pragma once | |||||
| #include <screen_driver.h> | |||||
| class Display { | |||||
| public: | |||||
| Display(size_t w, size_t h); | |||||
| ~Display(); | |||||
| void refresh(); | |||||
| size_t get_width() const { return width; } | |||||
| size_t get_height() const { return height; } | |||||
| size_t get_pitch() const { return pitch; } | |||||
| size_t get_stride() const { return pitch * sizeof(*buffer); } | |||||
| uint16_t* get_buffer() const { return buffer; } | |||||
| private: | |||||
| size_t width; | |||||
| size_t height; | |||||
| size_t pitch; | |||||
| uint16_t *buffer; | |||||
| spi_bus_handle_t bus; | |||||
| scr_interface_driver_t *iface_drv; | |||||
| scr_driver_t driver; | |||||
| }; | |||||
| @@ -16,6 +16,7 @@ static const char *TAG = "blinky"; | |||||
| #include "presets.hpp" | #include "presets.hpp" | ||||
| #include "utils.hpp" | #include "utils.hpp" | ||||
| #include "spi_leds.hpp" | #include "spi_leds.hpp" | ||||
| #include "screen_leds.hpp" | |||||
| // mDNS / NetBIOS | // mDNS / NetBIOS | ||||
| @@ -176,7 +177,7 @@ extern "C" void app_main(void) { | |||||
| } else { | } else { | ||||
| ESP_LOGI(TAG, "Starting animation"); | ESP_LOGI(TAG, "Starting animation"); | ||||
| int period_us = 1000000 / 60; | |||||
| int period_us = 1000000 / 30; | |||||
| int64_t target_us = time_us(); | int64_t target_us = time_us(); | ||||
| int64_t profile_start = target_us; | int64_t profile_start = target_us; | ||||
| #ifdef PROFILE_PERF | #ifdef PROFILE_PERF | ||||
| @@ -0,0 +1,79 @@ | |||||
| static const char *TAG = "screen_leds"; | |||||
| #include <esp_log.h> | |||||
| #include "screen_leds.hpp" | |||||
| static inline void fill(uint16_t *buffer, uint16_t value, int w, int h, int p = -1) { | |||||
| if (p <= 0) p = w; | |||||
| uint16_t *line = buffer; | |||||
| while (--h >= 0) { | |||||
| uint16_t *pixel = line; | |||||
| for (int _w = w; _w > 0; --_w) { | |||||
| *pixel = value; | |||||
| ++pixel; | |||||
| } | |||||
| line += p; | |||||
| } | |||||
| } | |||||
| static inline void box(uint16_t *buffer, uint16_t value, int x, int y, int w, int h, int p = -1) { | |||||
| if (p <= 0) p = w; | |||||
| fill(buffer + x + (y * p), value, w, h, p); | |||||
| } | |||||
| static inline uint16_t rgb16(int r, int g, int b) { | |||||
| return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | ((uint8_t)b >> 3); | |||||
| } | |||||
| static inline uint16_t color_to_565(Color c) { | |||||
| return rgb16(c.r, c.g, c.b); | |||||
| } | |||||
| ScreenLEDs::ScreenLEDs(int length) : LEDStrip(length), display(135, 240) {} | |||||
| void ScreenLEDs::length_changing(int length) { | |||||
| fill(display.get_buffer(), COLOR_BLACK, display.get_width(), display.get_height()); | |||||
| } | |||||
| void ScreenLEDs::show() { | |||||
| const int width = display.get_width(); | |||||
| const int height = display.get_height(); | |||||
| const int pitch = display.get_pitch(); | |||||
| auto buffer = display.get_buffer(); | |||||
| const size_t size = ((width + height) * 2) / (length + 4); | |||||
| int lx = 0, ly = 0; | |||||
| int dx = 0, dy = size; | |||||
| for (size_t l = 0; l < length; ++l) { | |||||
| if (ly + dy > height) { | |||||
| ly -= dy; | |||||
| dx = size; | |||||
| dy = 0; | |||||
| lx += dx; | |||||
| } else if (ly < 0) { | |||||
| ly -= dy; | |||||
| dx = -size; | |||||
| dy = 0; | |||||
| lx += dx; | |||||
| } else if (lx + dx > width) { | |||||
| lx -= dx; | |||||
| dx = 0; | |||||
| dy = -size; | |||||
| ly += dy; | |||||
| } else if (lx < 0) { | |||||
| lx -= dx; | |||||
| dx = 0; | |||||
| dy = size; | |||||
| ly += dy; | |||||
| } | |||||
| box(buffer, color_to_565(pixels[l]), lx, ly, size, size, pitch); | |||||
| lx += dx; | |||||
| ly += dy; | |||||
| } | |||||
| display.refresh(); | |||||
| } | |||||
| @@ -0,0 +1,15 @@ | |||||
| #pragma once | |||||
| #include "leds.hpp" | |||||
| #include "display.hpp" | |||||
| class ScreenLEDs : public LEDStrip { | |||||
| public: | |||||
| ScreenLEDs(int length = 0); | |||||
| void show(); | |||||
| void length_changing(int length); | |||||
| private: | |||||
| Display display; | |||||
| }; | |||||