// Simplified display implementation (no async memcpy) --------------------------------- #include "display.hpp" #include #include #include "disp_tools.hpp" #include "driver/spi_master.h" #include "esp_async_memcpy.h" #include "freertos/FreeRTOS.h" #include "freertos/semphr.h" #include "freertos/task.h" DMA_ATTR uint8_t SMD::dma_buf[SMD::kLineDataBytes]{}; DMA_ATTR uint8_t dma_buf_template[SMD::kLineDataBytes]{}; spi_device_handle_t SMD::_spi; static spi_transaction_t _tx{}; static bool _vcom = false; volatile bool _inFlight = false; static TaskHandle_t s_clearTaskHandle = nullptr; static SemaphoreHandle_t s_clearReqSem = nullptr; static SemaphoreHandle_t s_clearSem = nullptr; static async_memcpy_config_t config = ASYNC_MEMCPY_DEFAULT_CONFIG(); // update the maximum data stream supported by underlying DMA engine static async_memcpy_handle_t driver = NULL; static unsigned char reverse_bits3(unsigned char b) { return (b * 0x0202020202ULL & 0x010884422010ULL) % 0x3ff; } static bool IRAM_ATTR my_async_memcpy_cb(async_memcpy_handle_t mcp_hdl, async_memcpy_event_t* event, void* cb_args) { BaseType_t high_task_wakeup = pdFALSE; _inFlight = false; xSemaphoreGiveFromISR(s_clearSem, &high_task_wakeup); // high_task_wakeup set to pdTRUE if some high priority task unblocked return high_task_wakeup == pdTRUE; } static void zero_framebuffer_payload() { ESP_ERROR_CHECK(esp_async_memcpy(driver, SMD::dma_buf, dma_buf_template, 12480, my_async_memcpy_cb, nullptr)); } extern "C" void IRAM_ATTR s_spi_post_cb(spi_transaction_t* /*t*/) { BaseType_t hpw = pdFALSE; xSemaphoreGiveFromISR(s_clearReqSem, &hpw); if (hpw) portYIELD_FROM_ISR(); } static void clear_task(void*) { for (;;) { if (xSemaphoreTake(s_clearReqSem, portMAX_DELAY) == pdTRUE) { printf("Started zeroing\n"); spi_transaction_t* r = nullptr; ESP_ERROR_CHECK(spi_device_get_trans_result(SMD::_spi, &r, 0)); zero_framebuffer_payload(); // printf("Zeroing done\n"); } } } void SMD::ensure_clear_task() { if (!s_clearReqSem) s_clearReqSem = xSemaphoreCreateBinary(); if (!s_clearSem) s_clearSem = xSemaphoreCreateBinary(); xSemaphoreGive(s_clearSem); if (!s_clearTaskHandle) xTaskCreatePinnedToCore(clear_task, "fbclr", 1536, nullptr, tskIDLE_PRIORITY + 1, &s_clearTaskHandle, 0); } void SMD::init() { spi_bus_add_device(SPI_BUS, &_devcfg, &_spi); ensure_clear_task(); ESP_ERROR_CHECK(gpio_reset_pin(SPI_DISP_DISP)); ESP_ERROR_CHECK(gpio_set_direction(SPI_DISP_DISP, GPIO_MODE_OUTPUT)); ESP_ERROR_CHECK(gpio_set_level(SPI_DISP_DISP, 1)); ESP_ERROR_CHECK(gpio_hold_en(SPI_DISP_DISP)); for (uint8_t i = 0; i < DISP_HEIGHT; i++) { dma_buf[kLineMultiSingle * i + 1] = reverse_bits3(i + 1); dma_buf[2 + kLineMultiSingle * i + kLineBytes] = 0; } dma_buf[kLineDataBytes - 1] = 0; for (int y = 0; y < DISP_HEIGHT; ++y) for (int x = 0; x < DISP_WIDTH; ++x) DispTools::set_pixel(x, y, false); std::memcpy(dma_buf_template, dma_buf, sizeof(dma_buf_template)); ESP_ERROR_CHECK(esp_async_memcpy_install(&config, &driver)); // install driver with default DMA engine } bool SMD::async_draw_busy() { return _inFlight; } void SMD::async_draw_start() { assert(!_inFlight); if (!xSemaphoreTake(s_clearSem, portMAX_DELAY)) assert(false); _vcom = !_vcom; _tx = {}; _tx.tx_buffer = dma_buf; _tx.length = SMD::kLineDataBytes * 8; dma_buf[0] = 0b10000000 | (_vcom << 6); _inFlight = true; ESP_ERROR_CHECK(spi_device_queue_trans(_spi, &_tx, 0)); } void SMD::async_draw_wait() { if (uxSemaphoreGetCount(s_clearSem) || !_inFlight) { assert((uxSemaphoreGetCount(s_clearSem) == 0) == _inFlight); return; } if (!xSemaphoreTake(s_clearSem, portMAX_DELAY)) assert(false); if (!xSemaphoreGive(s_clearSem)) assert(false); assert(!_inFlight); } // (clear_in_progress / wait_clear / request_clear removed from public API) // Surface implementation ------------------------------------------------------ void SMDSurface::draw_pixel_impl(unsigned x, unsigned y, const BwPixel& pixel) { if (pixel.on) DispTools::set_pixel(x, y); else DispTools::reset_pixel(x, y); } void SMDSurface::clear_impl() { DispTools::clear(); } int SMDSurface::get_width_impl() const { return DISP_WIDTH; } int SMDSurface::get_height_impl() const { return DISP_HEIGHT; } EventHandlingResult SMDSurface::handle(SurfaceResizeEvent event) { return _window->handle(event); } SMDSurface::SMDSurface(EventLoop* loop) : Surface(), EventQueue(loop, this) {} SMDSurface::~SMDSurface() {}