Public Access
Set up PlatformIO project for EL512.256-H on ESP32
PlatformIO skeleton (esp32dev, Arduino, Adafruit GFX) with a local EL512 library: EL512Panel (1 bpp framebuffer, two-bit-mode row encoder, I2S DMA refresh stub) and EL512_GFX (Adafruit_GFX subclass). Adds a test sketch, pin map, README with wiring and timing, and moves the datasheet to docs/datasheet/. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
commit
873bdcd23e
16 files changed
+555
No files matched your search
@@ -0,0 +1,2 @@
|
|||||||
|
* text=auto
|
||||||
|
*.pdf binary
|
||||||
+26
@@ -0,0 +1,26 @@
|
|||||||
|
# PlatformIO
|
||||||
|
.pio/
|
||||||
|
.pioenvs/
|
||||||
|
.piolibdeps/
|
||||||
|
|
||||||
|
# VS Code (PlatformIO generates these per machine)
|
||||||
|
.vscode/.browse.c_cpp.db*
|
||||||
|
.vscode/c_cpp_properties.json
|
||||||
|
.vscode/launch.json
|
||||||
|
.vscode/ipch
|
||||||
|
|
||||||
|
# Windows
|
||||||
|
Thumbs.db
|
||||||
|
ehthumbs.db
|
||||||
|
Desktop.ini
|
||||||
|
$RECYCLE.BIN/
|
||||||
|
|
||||||
|
# macOS
|
||||||
|
.DS_Store
|
||||||
|
.AppleDouble
|
||||||
|
.LSOverride
|
||||||
|
._*
|
||||||
|
|
||||||
|
# Editors
|
||||||
|
*.swp
|
||||||
|
*~
|
||||||
Vendored
+11
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
// See http://go.microsoft.com/fwlink/?LinkId=827846
|
||||||
|
// for the documentation about the extensions.json format
|
||||||
|
"recommendations": [
|
||||||
|
"platformio.platformio-ide"
|
||||||
|
],
|
||||||
|
"unwantedRecommendations": [
|
||||||
|
"ms-vscode.cpptools-extension-pack",
|
||||||
|
"pioarduino.pioarduino-ide"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
# Planar EL512.256-H on ESP32
|
||||||
|
|
||||||
|
Drive a Planar EL512.256-H (512 × 256, monochrome electroluminescent) panel from an
|
||||||
|
ESP-WROOM-32 (classic ESP32), with an [Adafruit GFX](https://github.com/adafruit/Adafruit-GFX-Library)
|
||||||
|
compatible driver so the usual drawing, text and font APIs work.
|
||||||
|
|
||||||
|
The panel has no frame memory and needs a continuous video stream. The ESP32's I2S0
|
||||||
|
peripheral in 8-bit parallel (LCD) mode streams a precomputed frame from DMA, so the
|
||||||
|
CPU is free while the panel refreshes.
|
||||||
|
|
||||||
|
**Status:** project skeleton. Framebuffer, GFX layer and row encoder are in place; the
|
||||||
|
I2S/DMA start-up (`EL512Panel::startI2S()`) is still a stub.
|
||||||
|
|
||||||
|
## Hardware
|
||||||
|
|
||||||
|
- ESP-WROOM-32 dev board (`esp32dev`)
|
||||||
|
- Planar EL512.256-H, configured for **two-bit parallel mode** (mode jumper OFF, see datasheet)
|
||||||
|
- Datasheet: [docs/datasheet/EL512.256-H3 FRB.pdf](docs/datasheet/EL512.256-H3%20FRB.pdf)
|
||||||
|
- Check the panel's logic-level and supply requirements in the datasheet before wiring.
|
||||||
|
|
||||||
|
## Wiring
|
||||||
|
|
||||||
|
| Panel signal | Function | ESP32 GPIO | DMA byte bit |
|
||||||
|
|--------------|-----------------------------------------|-----------:|-------------:|
|
||||||
|
| VCLK | Video clock (rising edge) | 18 | I2S clock |
|
||||||
|
| VID | Pixel data, even columns | 19 | 0 |
|
||||||
|
| TVID | Pixel data, odd columns (two-bit mode) | 21 | 1 |
|
||||||
|
| HS | Horizontal sync | 22 | 2 |
|
||||||
|
| VS | Vertical sync | 23 | 3 |
|
||||||
|
| ENABLE | Display enable | 25 | — |
|
||||||
|
| _LOWPWR | Low-power mode (active low) | 26 | — |
|
||||||
|
| GND | Ground | GND | — |
|
||||||
|
|
||||||
|
The pin map lives in [include/pins.h](include/pins.h).
|
||||||
|
|
||||||
|
## Timing
|
||||||
|
|
||||||
|
| Parameter | Value |
|
||||||
|
|----------------------|-----------------------------------------|
|
||||||
|
| Pixel clock (VCLK) | 5 MHz, 2 pixels per clock |
|
||||||
|
| Clocks per row | 272 (256 data + 16 blanking) |
|
||||||
|
| Lines per frame | 261 (256 visible + 5 blanking) |
|
||||||
|
| Refresh | 5 MHz / (272 × 261) ≈ 70.4 Hz |
|
||||||
|
|
||||||
|
## Layout
|
||||||
|
|
||||||
|
```
|
||||||
|
platformio.ini build config (esp32dev, Arduino, Adafruit GFX)
|
||||||
|
include/pins.h pin map
|
||||||
|
src/main.cpp test sketch: test pattern + text
|
||||||
|
lib/EL512/ local library
|
||||||
|
src/EL512Panel.* framebuffer, row encoder, I2S DMA refresh (stub)
|
||||||
|
src/EL512_GFX.* Adafruit_GFX subclass
|
||||||
|
test/ unit tests (placeholder)
|
||||||
|
docs/ datasheet and notes
|
||||||
|
```
|
||||||
|
|
||||||
|
## Build and flash
|
||||||
|
|
||||||
|
Requires [PlatformIO](https://platformio.org/) (CLI or the VS Code extension).
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pio run
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pio run -t upload
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pio device monitor
|
||||||
|
```
|
||||||
|
|
||||||
|
## Using the driver
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#include <EL512.h>
|
||||||
|
#include "pins.h"
|
||||||
|
|
||||||
|
EL512Panel panel({el512pins::VCLK, el512pins::VID, el512pins::TVID, el512pins::HS,
|
||||||
|
el512pins::VS, el512pins::ENABLE, el512pins::LOWPWR_N});
|
||||||
|
EL512_GFX display(panel);
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
display.begin();
|
||||||
|
display.setTextColor(1);
|
||||||
|
display.print("Hello");
|
||||||
|
display.display(); // push the drawing buffer to the refresh stream
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
See [docs/README.md](docs/README.md) for design notes and the plan.
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
# Notes
|
||||||
|
|
||||||
|
- Plan: https://claude.ai/code/artifact/3b9eda11-a87e-4eea-bbb9-e6454e558991
|
||||||
|
- Datasheet: [datasheet/EL512.256-H3 FRB.pdf](datasheet/EL512.256-H3%20FRB.pdf)
|
||||||
|
|
||||||
|
## Approach
|
||||||
|
|
||||||
|
I2S0 in 8-bit parallel (LCD) mode with a circular DMA descriptor chain. Each DMA byte
|
||||||
|
is one VCLK period:
|
||||||
|
|
||||||
|
| Bit | Signal |
|
||||||
|
|-----|--------|
|
||||||
|
| 0 | VID (even columns) |
|
||||||
|
| 1 | TVID (odd columns) |
|
||||||
|
| 2 | HS |
|
||||||
|
| 3 | VS |
|
||||||
|
|
||||||
|
The I2S clock output drives VCLK. ENABLE (GPIO 25) and _LOWPWR (GPIO 26) are plain GPIO.
|
||||||
|
|
||||||
|
## From the datasheet
|
||||||
|
|
||||||
|
- Two-bit parallel mode is selected with the mode jumper OFF. Even columns on VID,
|
||||||
|
odd columns on TVID; 256 VCLKs per row.
|
||||||
|
- VCLK is active on the rising edge and should be kept running.
|
||||||
|
- HS is high while the pixels of a row are clocked in. The number of VCLKs during HS
|
||||||
|
high should be even.
|
||||||
|
- The topmost row is the first HS high time ending after the rising edge of VS.
|
||||||
|
|
||||||
|
## Open items
|
||||||
|
|
||||||
|
- [ ] Implement `EL512Panel::startI2S()` (I2S0 LCD mode, clock dividers for 5 MHz,
|
||||||
|
GPIO matrix routing, circular `lldesc_t` chain).
|
||||||
|
- [ ] Handle the 8-bit LCD-mode byte-pair swap in `encodeRow()`.
|
||||||
|
- [ ] Check VS/HS setup and hold (T4/T5) and VCLK polarity on a scope.
|
||||||
|
- [ ] Tear-free updates (double buffer or swap at VS).
|
||||||
|
- [ ] Unit test for `encodeRow()`.
|
||||||
Binary file not shown.
@@ -0,0 +1,22 @@
|
|||||||
|
// Pin map for the EL512.256-H panel on an ESP-WROOM-32 (esp32dev).
|
||||||
|
//
|
||||||
|
// VCLK, VID, TVID, HS and VS are driven by I2S0 in 8-bit parallel (LCD) mode,
|
||||||
|
// so their levels come from a DMA byte stream. ENABLE and _LOWPWR are plain GPIO.
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
namespace el512pins {
|
||||||
|
|
||||||
|
// I2S-driven signals
|
||||||
|
constexpr int8_t VCLK = 18; // I2S0 WS/clock out -> panel VCLK
|
||||||
|
constexpr int8_t VID = 19; // byte bit 0
|
||||||
|
constexpr int8_t TVID = 21; // byte bit 1
|
||||||
|
constexpr int8_t HS = 22; // byte bit 2
|
||||||
|
constexpr int8_t VS = 23; // byte bit 3
|
||||||
|
|
||||||
|
// Static control lines
|
||||||
|
constexpr int8_t ENABLE = 25; // high = display enabled
|
||||||
|
constexpr int8_t LOWPWR_N = 26; // _LOWPWR, active low
|
||||||
|
|
||||||
|
} // namespace el512pins
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"name": "EL512",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"description": "Planar EL512.256-H electroluminescent panel driver for ESP32 (I2S parallel DMA) with Adafruit GFX support",
|
||||||
|
"keywords": "display, electroluminescent, planar, el512, esp32, i2s, gfx",
|
||||||
|
"frameworks": "arduino",
|
||||||
|
"platforms": "espressif32",
|
||||||
|
"dependencies": {
|
||||||
|
"adafruit/Adafruit GFX Library": "*"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
// Umbrella header for the EL512 library.
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "EL512Panel.h"
|
||||||
|
#include "EL512_GFX.h"
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
#include "EL512Panel.h"
|
||||||
|
|
||||||
|
#include <esp_heap_caps.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
EL512Panel::EL512Panel(const EL512Pins& pins) : _pins(pins) {}
|
||||||
|
|
||||||
|
EL512Panel::~EL512Panel() { end(); }
|
||||||
|
|
||||||
|
bool EL512Panel::begin() {
|
||||||
|
if (_running) return true;
|
||||||
|
|
||||||
|
pinMode(_pins.enable, OUTPUT);
|
||||||
|
pinMode(_pins.lowpwr_n, OUTPUT);
|
||||||
|
setEnabled(false);
|
||||||
|
setLowPower(false);
|
||||||
|
|
||||||
|
_fb = (uint8_t*)calloc(FRAMEBUFFER_BYTES, 1);
|
||||||
|
// Full-frame stream: 261 * 272 = ~71 KB of DMA-capable internal RAM.
|
||||||
|
// (A line ring buffer refilled from the DMA EOF interrupt would cut this
|
||||||
|
// to a few KB if RAM gets tight.)
|
||||||
|
_stream = (uint8_t*)heap_caps_malloc(STREAM_BYTES, MALLOC_CAP_DMA | MALLOC_CAP_8BIT);
|
||||||
|
if (!_fb || !_stream) {
|
||||||
|
end();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
show();
|
||||||
|
|
||||||
|
if (!startI2S()) {
|
||||||
|
end();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
_running = true;
|
||||||
|
setEnabled(true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EL512Panel::end() {
|
||||||
|
setEnabled(false);
|
||||||
|
stopI2S();
|
||||||
|
_running = false;
|
||||||
|
free(_fb);
|
||||||
|
_fb = nullptr;
|
||||||
|
heap_caps_free(_stream);
|
||||||
|
_stream = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EL512Panel::setEnabled(bool on) { digitalWrite(_pins.enable, on ? HIGH : LOW); }
|
||||||
|
|
||||||
|
void EL512Panel::setLowPower(bool on) { digitalWrite(_pins.lowpwr_n, on ? LOW : HIGH); }
|
||||||
|
|
||||||
|
void EL512Panel::setPixel(int16_t x, int16_t y, bool on) {
|
||||||
|
if (!_fb || x < 0 || y < 0 || x >= WIDTH || y >= HEIGHT) return;
|
||||||
|
uint8_t& b = _fb[(y * WIDTH + x) >> 3];
|
||||||
|
const uint8_t mask = 0x80 >> (x & 7);
|
||||||
|
if (on)
|
||||||
|
b |= mask;
|
||||||
|
else
|
||||||
|
b &= ~mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EL512Panel::getPixel(int16_t x, int16_t y) const {
|
||||||
|
if (!_fb || x < 0 || y < 0 || x >= WIDTH || y >= HEIGHT) return false;
|
||||||
|
return _fb[(y * WIDTH + x) >> 3] & (0x80 >> (x & 7));
|
||||||
|
}
|
||||||
|
|
||||||
|
void EL512Panel::clear(bool on) {
|
||||||
|
if (_fb) memset(_fb, on ? 0xFF : 0x00, FRAMEBUFFER_BYTES);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EL512Panel::encodeRow(uint16_t line, uint8_t* out) const {
|
||||||
|
// VS is held high during the last blanking line so that the first HS high
|
||||||
|
// time ending after its rising edge is row 0 (datasheet: video input timing).
|
||||||
|
// TODO: verify VS/HS setup and hold (T4/T5) on hardware.
|
||||||
|
const uint8_t vs = (line == LINES_PER_FRAME - 1) ? BIT_VS : 0;
|
||||||
|
|
||||||
|
if (line >= HEIGHT) {
|
||||||
|
memset(out, vs, CLOCKS_PER_ROW);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint8_t* src = _fb + (size_t)line * (WIDTH / 8);
|
||||||
|
for (uint16_t c = 0; c < DATA_CLOCKS_PER_ROW; c++) {
|
||||||
|
// Clock c carries columns 2c (VID, even) and 2c+1 (TVID, odd).
|
||||||
|
const uint8_t bits = (uint8_t)(src[c >> 2] << ((c & 3) * 2));
|
||||||
|
uint8_t v = BIT_HS | vs;
|
||||||
|
if (bits & 0x80) v |= BIT_VID;
|
||||||
|
if (bits & 0x40) v |= BIT_TVID;
|
||||||
|
out[c] = v;
|
||||||
|
}
|
||||||
|
memset(out + DATA_CLOCKS_PER_ROW, vs, BLANK_CLOCKS_PER_ROW);
|
||||||
|
|
||||||
|
// NOTE: in 8-bit I2S LCD mode the ESP32 emits bytes in swapped pairs
|
||||||
|
// (byte 1, byte 0, byte 3, byte 2, ...). Swap here once the DMA path is live.
|
||||||
|
}
|
||||||
|
|
||||||
|
void EL512Panel::show() {
|
||||||
|
if (!_fb || !_stream) return;
|
||||||
|
// TODO: double-buffer or sync to VS to avoid tearing.
|
||||||
|
for (uint16_t line = 0; line < LINES_PER_FRAME; line++) {
|
||||||
|
encodeRow(line, _stream + (size_t)line * CLOCKS_PER_ROW);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EL512Panel::startI2S() {
|
||||||
|
// TODO: configure I2S0 for 8-bit parallel LCD mode:
|
||||||
|
// - periph_module_enable(PERIPH_I2S0_MODULE), reset TX / FIFO / DMA
|
||||||
|
// - I2S0.conf2.lcd_en = 1, 8-bit TX, clock dividers -> PIXEL_CLOCK_HZ
|
||||||
|
// - route stream bits 0..3 to vid/tvid/hs/vs with gpio_matrix_out()
|
||||||
|
// (I2S0O_DATA_OUT8_IDX.. for 8-bit mode - verify on the core version)
|
||||||
|
// - route the I2S clock (I2S0O_WS_OUT_IDX) to vclk
|
||||||
|
// - build a circular lldesc_t chain over _stream (<= 4092 bytes each)
|
||||||
|
// - start TX via out_link.start
|
||||||
|
log_w("EL512Panel: I2S DMA refresh not implemented yet (stub)");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EL512Panel::stopI2S() {
|
||||||
|
// TODO: stop I2S0 TX and release the DMA descriptors.
|
||||||
|
}
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
// EL512Panel - low-level refresh layer for the Planar EL512.256-H.
|
||||||
|
//
|
||||||
|
// The panel has no frame memory of its own: it must be refreshed continuously.
|
||||||
|
// We drive it from I2S0 in 8-bit parallel (LCD) mode with a circular DMA
|
||||||
|
// descriptor chain, one byte per VCLK:
|
||||||
|
//
|
||||||
|
// byte bit 0 = VID (even columns)
|
||||||
|
// byte bit 1 = TVID (odd columns)
|
||||||
|
// byte bit 2 = HS (high while the 256 data clocks of a row are sent)
|
||||||
|
// byte bit 3 = VS (marks the top row)
|
||||||
|
// I2S clock -> VCLK (panel samples data on the rising edge)
|
||||||
|
//
|
||||||
|
// Panel in two-bit parallel mode (jumper OFF, see datasheet), 5 MHz VCLK:
|
||||||
|
// 272 clocks per row (256 data + 16 blanking), 261 rows per frame
|
||||||
|
// (256 visible + 5 blanking) -> 5e6 / (272 * 261) ~= 70.4 Hz.
|
||||||
|
//
|
||||||
|
// STATUS: the framebuffer and the row encoder are implemented; the I2S/DMA
|
||||||
|
// start-up in startI2S() is still a TODO (see EL512Panel.cpp).
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
struct EL512Pins {
|
||||||
|
int8_t vclk;
|
||||||
|
int8_t vid;
|
||||||
|
int8_t tvid;
|
||||||
|
int8_t hs;
|
||||||
|
int8_t vs;
|
||||||
|
int8_t enable;
|
||||||
|
int8_t lowpwr_n;
|
||||||
|
};
|
||||||
|
|
||||||
|
class EL512Panel {
|
||||||
|
public:
|
||||||
|
static constexpr uint16_t WIDTH = 512;
|
||||||
|
static constexpr uint16_t HEIGHT = 256;
|
||||||
|
|
||||||
|
// Timing (two-bit parallel mode)
|
||||||
|
static constexpr uint32_t PIXEL_CLOCK_HZ = 5000000;
|
||||||
|
static constexpr uint16_t DATA_CLOCKS_PER_ROW = WIDTH / 2; // 2 px per VCLK
|
||||||
|
static constexpr uint16_t CLOCKS_PER_ROW = 272;
|
||||||
|
static constexpr uint16_t BLANK_CLOCKS_PER_ROW = CLOCKS_PER_ROW - DATA_CLOCKS_PER_ROW;
|
||||||
|
static constexpr uint16_t LINES_PER_FRAME = 261;
|
||||||
|
static constexpr uint16_t BLANK_LINES = LINES_PER_FRAME - HEIGHT;
|
||||||
|
|
||||||
|
// DMA stream byte bits
|
||||||
|
static constexpr uint8_t BIT_VID = 1 << 0;
|
||||||
|
static constexpr uint8_t BIT_TVID = 1 << 1;
|
||||||
|
static constexpr uint8_t BIT_HS = 1 << 2;
|
||||||
|
static constexpr uint8_t BIT_VS = 1 << 3;
|
||||||
|
|
||||||
|
static constexpr size_t FRAMEBUFFER_BYTES = (size_t)WIDTH * HEIGHT / 8;
|
||||||
|
static constexpr size_t STREAM_BYTES = (size_t)LINES_PER_FRAME * CLOCKS_PER_ROW;
|
||||||
|
|
||||||
|
explicit EL512Panel(const EL512Pins& pins);
|
||||||
|
~EL512Panel();
|
||||||
|
|
||||||
|
// Allocates buffers, sets up control GPIOs and (TODO) starts I2S DMA refresh.
|
||||||
|
bool begin();
|
||||||
|
void end();
|
||||||
|
|
||||||
|
// Display on/off (ENABLE) and low-power mode (_LOWPWR, active low).
|
||||||
|
void setEnabled(bool on);
|
||||||
|
void setLowPower(bool on);
|
||||||
|
|
||||||
|
// 1 bpp framebuffer, row-major, MSB = leftmost pixel.
|
||||||
|
uint8_t* framebuffer() { return _fb; }
|
||||||
|
void setPixel(int16_t x, int16_t y, bool on);
|
||||||
|
bool getPixel(int16_t x, int16_t y) const;
|
||||||
|
void clear(bool on = false);
|
||||||
|
|
||||||
|
// Encode the framebuffer into the DMA stream (call after drawing).
|
||||||
|
void show();
|
||||||
|
|
||||||
|
// Encode one scan line (0..LINES_PER_FRAME-1) into CLOCKS_PER_ROW bytes.
|
||||||
|
// Lines >= HEIGHT produce blanking. Public so it can be unit-tested.
|
||||||
|
void encodeRow(uint16_t line, uint8_t* out) const;
|
||||||
|
|
||||||
|
// Frame refresh rate implied by the timing constants.
|
||||||
|
static constexpr float refreshHz() {
|
||||||
|
return (float)PIXEL_CLOCK_HZ / ((float)CLOCKS_PER_ROW * LINES_PER_FRAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool startI2S();
|
||||||
|
void stopI2S();
|
||||||
|
|
||||||
|
EL512Pins _pins;
|
||||||
|
uint8_t* _fb = nullptr; // 1 bpp drawing buffer
|
||||||
|
uint8_t* _stream = nullptr; // STREAM_BYTES, DMA-capable
|
||||||
|
bool _running = false;
|
||||||
|
};
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
#include "EL512_GFX.h"
|
||||||
|
|
||||||
|
EL512_GFX::EL512_GFX(EL512Panel& panel)
|
||||||
|
: Adafruit_GFX(EL512Panel::WIDTH, EL512Panel::HEIGHT), _panel(panel) {}
|
||||||
|
|
||||||
|
void EL512_GFX::drawPixel(int16_t x, int16_t y, uint16_t color) {
|
||||||
|
if (x < 0 || y < 0 || x >= width() || y >= height()) return;
|
||||||
|
|
||||||
|
int16_t t;
|
||||||
|
switch (getRotation()) {
|
||||||
|
case 1:
|
||||||
|
t = x;
|
||||||
|
x = WIDTH - 1 - y;
|
||||||
|
y = t;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
x = WIDTH - 1 - x;
|
||||||
|
y = HEIGHT - 1 - y;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
t = x;
|
||||||
|
x = y;
|
||||||
|
y = HEIGHT - 1 - t;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
_panel.setPixel(x, y, color != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EL512_GFX::fillScreen(uint16_t color) { _panel.clear(color != 0); }
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
// EL512_GFX - Adafruit_GFX front-end for the EL512.256-H panel.
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Adafruit_GFX.h>
|
||||||
|
|
||||||
|
#include "EL512Panel.h"
|
||||||
|
|
||||||
|
class EL512_GFX : public Adafruit_GFX {
|
||||||
|
public:
|
||||||
|
explicit EL512_GFX(EL512Panel& panel);
|
||||||
|
|
||||||
|
bool begin() { return _panel.begin(); }
|
||||||
|
|
||||||
|
// Any non-zero color lights the pixel.
|
||||||
|
void drawPixel(int16_t x, int16_t y, uint16_t color) override;
|
||||||
|
void fillScreen(uint16_t color) override;
|
||||||
|
|
||||||
|
// Encode the drawing buffer into the panel refresh stream.
|
||||||
|
void display() { _panel.show(); }
|
||||||
|
|
||||||
|
EL512Panel& panel() { return _panel; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
EL512Panel& _panel;
|
||||||
|
};
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
; Planar EL512.256-H electroluminescent panel driver for ESP32 (ESP-WROOM-32)
|
||||||
|
; https://docs.platformio.org/page/projectconf.html
|
||||||
|
|
||||||
|
[env:esp32dev]
|
||||||
|
platform = espressif32@6.10.0
|
||||||
|
board = esp32dev
|
||||||
|
framework = arduino
|
||||||
|
monitor_speed = 115200
|
||||||
|
lib_deps =
|
||||||
|
adafruit/Adafruit GFX Library
|
||||||
|
build_flags =
|
||||||
|
-Wall
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
// Test sketch: bring up the EL512.256-H and draw a test pattern with Adafruit GFX.
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <EL512.h>
|
||||||
|
|
||||||
|
#include "pins.h"
|
||||||
|
|
||||||
|
static EL512Panel panel({
|
||||||
|
el512pins::VCLK,
|
||||||
|
el512pins::VID,
|
||||||
|
el512pins::TVID,
|
||||||
|
el512pins::HS,
|
||||||
|
el512pins::VS,
|
||||||
|
el512pins::ENABLE,
|
||||||
|
el512pins::LOWPWR_N,
|
||||||
|
});
|
||||||
|
static EL512_GFX display(panel);
|
||||||
|
|
||||||
|
static void drawTestPattern() {
|
||||||
|
display.fillScreen(0);
|
||||||
|
display.drawRect(0, 0, display.width(), display.height(), 1);
|
||||||
|
display.drawLine(0, 0, display.width() - 1, display.height() - 1, 1);
|
||||||
|
display.drawLine(display.width() - 1, 0, 0, display.height() - 1, 1);
|
||||||
|
|
||||||
|
// Checkerboard strip to check VID/TVID column ordering.
|
||||||
|
for (int16_t y = 8; y < 24; y++)
|
||||||
|
for (int16_t x = 8; x < 136; x++)
|
||||||
|
if ((x ^ y) & 1) display.drawPixel(x, y, 1);
|
||||||
|
|
||||||
|
display.fillCircle(display.width() / 2, display.height() / 2, 40, 1);
|
||||||
|
display.setTextColor(1);
|
||||||
|
display.setTextSize(2);
|
||||||
|
display.setCursor(8, 32);
|
||||||
|
display.print("EL512.256-H");
|
||||||
|
display.setTextSize(1);
|
||||||
|
display.setCursor(8, 56);
|
||||||
|
display.printf("%ux%u %.1f Hz", EL512Panel::WIDTH, EL512Panel::HEIGHT,
|
||||||
|
EL512Panel::refreshHz());
|
||||||
|
display.display();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Serial.begin(115200);
|
||||||
|
Serial.println("EL512.256-H test");
|
||||||
|
|
||||||
|
if (!display.begin()) {
|
||||||
|
Serial.println("panel init failed (out of DMA memory?)");
|
||||||
|
while (true) delay(1000);
|
||||||
|
}
|
||||||
|
drawTestPattern();
|
||||||
|
Serial.printf("free heap: %u\n", ESP.getFreeHeap());
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
static uint32_t last = 0;
|
||||||
|
static uint32_t frames = 0;
|
||||||
|
if (millis() - last >= 1000) {
|
||||||
|
last = millis();
|
||||||
|
display.fillRect(8, 72, 120, 8, 0);
|
||||||
|
display.setCursor(8, 72);
|
||||||
|
display.printf("uptime %lus", (unsigned long)frames++);
|
||||||
|
display.display();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
Unit tests (PlatformIO Unit Testing / Unity) go here.
|
||||||
|
|
||||||
|
Planned first test: EL512Panel::encodeRow() - check that HS is high for exactly
|
||||||
|
256 clocks per visible row, VS appears only on the last blanking line, and that
|
||||||
|
even/odd columns land on VID/TVID.
|
||||||
|
|
||||||
|
Run with: pio test -e esp32dev
|
||||||
|
|
||||||
|
More info: https://docs.platformio.org/en/latest/advanced/unit-testing/index.html
|
||||||
Reference in new issue
Block a user