mirror of
https://github.com/usatiuk/cardboy.git
synced 2025-10-28 23:27:49 +01:00
138 lines
5.0 KiB
C++
138 lines
5.0 KiB
C++
// Simplified display implementation (no async memcpy) ---------------------------------
|
|
|
|
#include "display.hpp"
|
|
#include <cstring>
|
|
#include <driver/gpio.h>
|
|
#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 (!_inFlight || uxSemaphoreGetCount(s_clearSem)) {
|
|
// 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<SMDSurface, BwPixel>(),
|
|
EventQueue<SMDSurface, KeyboardEvent, SurfaceEvent, SurfaceResizeEvent>(loop, this) {}
|
|
SMDSurface::~SMDSurface() {}
|