feat(boards): Add Waveshare ESP32-C6-Touch-AMOLED-2.16 support
- Add complete board support for Waveshare ESP32-C6-Touch-AMOLED-2.16 with SH8601 display driver - Implement board initialization with AXP2101 PMU rail configuration for LCD and touch power - Add display driver for SH8601 QSPI panel (480×480 resolution) - Add CST9217 touch controller support via I2C - Add QMI8658 IMU initialization and sensor reading - Add AXP2101 power management and battery monitoring - Add input handling for BOOT button (GPIO 9) - Configure PlatformIO environment with C6-specific build flags and 16 MB flash layout - Update capability flags documentation to clarify BOARD_HAS_PSRAM build-flag macro usage - C6 has no external PSRAM; shared code gates on this flag to use internal SRAM and reduce LVGL buffer sizes - Screenshot capture disabled on this board due to internal SRAM constraints
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
|
||||
// Waveshare ESP32-C6-Touch-AMOLED-2.16.
|
||||
//
|
||||
// Despite the matching "2.16" model number, this board is hardware-
|
||||
// distinct from the S3 AMOLED-2.16: it uses an SH8601 panel (same driver
|
||||
// family as the AMOLED-1.8), a CST9217 touch controller, and the C6 SoC's
|
||||
// own GPIO map. There is no PSRAM. AXP2101 PMU and QMI8658 IMU carry over.
|
||||
//
|
||||
// Pin assignments verified against the official Waveshare XiaoZhi BSP at
|
||||
// waveshareteam/ESP32-C6-Touch-AMOLED-2.16 (XiaoZhi config.h) and the
|
||||
// ESP-IDF example user_config.h files in the same repo.
|
||||
|
||||
#define BOARD_NAME "Waveshare AMOLED 2.16 (C6)"
|
||||
|
||||
#define LCD_WIDTH 480
|
||||
#define LCD_HEIGHT 480
|
||||
|
||||
// ---- QSPI display pins (SH8601) ----
|
||||
#define LCD_CS 15
|
||||
#define LCD_SCLK 0
|
||||
#define LCD_SDIO0 1
|
||||
#define LCD_SDIO1 2
|
||||
#define LCD_SDIO2 3
|
||||
#define LCD_SDIO3 4
|
||||
// LCD reset is not wired to a MCU GPIO on this board — the SH8601 relies
|
||||
// on its internal power-on reset. The Arduino_GFX driver gets
|
||||
// GFX_NOT_DEFINED for reset.
|
||||
|
||||
// ---- I2C bus (touch + PMU + IMU all share one bus) ----
|
||||
#define IIC_SDA 8
|
||||
#define IIC_SCL 7
|
||||
|
||||
// ---- Touch (CST9217 via TouchDrvCST92xx library) ----
|
||||
// CST9217 is register-compatible with CST9220 in the relevant subset; the
|
||||
// SensorLib CST92xx driver works against both. I2C address is the same.
|
||||
#define TP_INT 5
|
||||
#define TP_RST 11
|
||||
#define CST9220_ADDR 0x5A
|
||||
|
||||
// ---- PMU ----
|
||||
#define AXP2101_ADDR 0x34
|
||||
|
||||
// ---- Buttons ----
|
||||
// Only one user GPIO button (BOOT). The "PWR" side button is the AXP2101
|
||||
// PKEY input — already serviced by power.cpp via the PKEY_SHORT_IRQ path.
|
||||
#define BTN_BACK_GPIO 9 // BOOT — primary, Space (PTT)
|
||||
|
||||
// ---- Capability flags ----
|
||||
#define BOARD_HAS_SECONDARY_BUTTON 0
|
||||
#define BOARD_HAS_ROTATION 0 // C6 has no PSRAM headroom for the rotation strip
|
||||
#define BOARD_HAS_IMU 1 // present + initialized for I2C bus health
|
||||
#define BOARD_HAS_BATTERY 1
|
||||
#define BOARD_HAS_IO_EXPANDER 0 // TCA9554 exists on board but only services audio
|
||||
@@ -0,0 +1,51 @@
|
||||
#include "board.h"
|
||||
#include <Arduino.h>
|
||||
#include <Wire.h>
|
||||
#include <XPowersLib.h>
|
||||
|
||||
// On this C6 board the SH8601 LCD + CST9217 touch are powered from the
|
||||
// AXP2101's ALDO rails, not directly from 3V3. The display init must run
|
||||
// AFTER the rails are up, so we bring the PMU up here in board_init()
|
||||
// (before display_hal_init) instead of in power_hal_init() which runs
|
||||
// later. power.cpp re-uses the same XPowersPMU handle for battery polling.
|
||||
//
|
||||
// Rail config mirrors the Waveshare XiaoZhi BSP for this board:
|
||||
// DC1 = 3.3V (system rail)
|
||||
// ALDO1..4 = 3.3V, all enabled (LCD, touch, sensors)
|
||||
|
||||
XPowersPMU board_pmu; // shared instance — power.cpp uses extern reference
|
||||
|
||||
extern "C" void board_init(void) {
|
||||
Wire.begin(IIC_SDA, IIC_SCL);
|
||||
|
||||
if (!board_pmu.begin(Wire, AXP2101_ADDR, IIC_SDA, IIC_SCL)) {
|
||||
Serial.println("AXP2101 init failed (board_init)");
|
||||
return;
|
||||
}
|
||||
|
||||
// Set all four ALDOs to 3.3V.
|
||||
board_pmu.setALDO1Voltage(3300);
|
||||
board_pmu.setALDO2Voltage(3300);
|
||||
board_pmu.setALDO3Voltage(3300);
|
||||
board_pmu.setALDO4Voltage(3300);
|
||||
|
||||
// ALDO1, 2, 4 just need to be enabled.
|
||||
board_pmu.enableALDO1();
|
||||
board_pmu.enableALDO2();
|
||||
board_pmu.enableALDO4();
|
||||
|
||||
// ALDO3 doubles as the LCD reset line on this board (the SH8601's RST
|
||||
// pin isn't wired to a MCU GPIO). The Waveshare BSP pulses it
|
||||
// HIGH → LOW → HIGH with 100 ms holds to issue a proper reset before
|
||||
// the panel sees its first SPI command. Without this pulse the SH8601
|
||||
// stays in an indeterminate state and the screen is black even though
|
||||
// QSPI init and brightness writes succeed.
|
||||
board_pmu.enableALDO3();
|
||||
delay(100);
|
||||
board_pmu.disableALDO3();
|
||||
delay(100);
|
||||
board_pmu.enableALDO3();
|
||||
delay(100);
|
||||
|
||||
Serial.println("AXP2101 rails up (ALDO1-4 @ 3.3V, LCD reset pulsed)");
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#include "../../hal/board_caps.h"
|
||||
#include "board.h"
|
||||
|
||||
static const BoardCaps caps = {
|
||||
.name = BOARD_NAME,
|
||||
.width = LCD_WIDTH,
|
||||
.height = LCD_HEIGHT,
|
||||
.button_count = 1, // BOOT only; PWR is via AXP PKEY IRQ
|
||||
.has_rotation = (bool)BOARD_HAS_ROTATION,
|
||||
.has_battery = (bool)BOARD_HAS_BATTERY,
|
||||
.has_imu = (bool)BOARD_HAS_IMU,
|
||||
};
|
||||
|
||||
const BoardCaps& board_caps(void) { return caps; }
|
||||
@@ -0,0 +1,77 @@
|
||||
#include "../../hal/display_hal.h"
|
||||
#include "board.h"
|
||||
#include <Arduino.h>
|
||||
#include <Arduino_GFX_Library.h>
|
||||
|
||||
// C6 AMOLED-2.16 uses an SH8601 panel — same driver family as the
|
||||
// AMOLED-1.8 port. LCD reset is not wired to any MCU GPIO; the SH8601
|
||||
// boots from its internal POR. Rotation is disabled (no PSRAM headroom
|
||||
// for the strip buffer).
|
||||
|
||||
static Arduino_DataBus* bus = nullptr;
|
||||
static Arduino_SH8601* gfx = nullptr;
|
||||
|
||||
void display_hal_init(void) {
|
||||
bus = new Arduino_ESP32QSPI(
|
||||
LCD_CS, LCD_SCLK, LCD_SDIO0, LCD_SDIO1, LCD_SDIO2, LCD_SDIO3);
|
||||
gfx = new Arduino_SH8601(
|
||||
bus, GFX_NOT_DEFINED, 0, LCD_WIDTH, LCD_HEIGHT);
|
||||
}
|
||||
|
||||
// Vendor-specific init commands from the Waveshare C6-2.16 BSP
|
||||
// (02_Example/Arduino-v3.3.3/09_LVGL_V9_Test/bsp_lvgl_port.cpp in the
|
||||
// waveshareteam/ESP32-C6-Touch-AMOLED-2.16 repo). The stock Arduino_GFX
|
||||
// SH8601 init does SLPOUT + NORON + INVOFF + PIXFMT + DISPON + brightness,
|
||||
// which is enough for the AMOLED-1.8 panel but leaves this 2.16 panel
|
||||
// dark. The page-switch sequence (0xFE 0x20 ... 0xFE 0x00) writes two
|
||||
// panel-specific manufacturer registers (0x19 and 0x1C) that gate the
|
||||
// driving voltages — without them the panel stays black even with the
|
||||
// rails up and the reset pulse applied.
|
||||
static void send_vendor_init(Arduino_DataBus* b) {
|
||||
b->beginWrite();
|
||||
b->writeC8D8(0xFE, 0x20); // enter manufacturer command page 0x20
|
||||
b->writeC8D8(0x19, 0x10); // panel driving
|
||||
b->writeC8D8(0x1C, 0xA0); // panel driving
|
||||
b->writeC8D8(0xFE, 0x00); // back to user command page
|
||||
b->writeC8D8(0xC4, 0x80); // SPI mode control
|
||||
b->writeC8D8(0x36, 0x30); // MADCTL (BSP value)
|
||||
b->writeC8D8(0x53, 0x20); // CTRL display 1 (brightness control on)
|
||||
b->writeC8D8(0x51, 0xFF); // brightness = max
|
||||
b->writeC8D8(0x63, 0xFF); // HBM brightness = max
|
||||
b->writeCommand(0x29); // DISPON (idempotent — stock init already did this)
|
||||
b->endWrite();
|
||||
delay(20);
|
||||
}
|
||||
|
||||
void display_hal_begin(void) {
|
||||
gfx->begin();
|
||||
send_vendor_init(bus); // patch up panel-specific regs the stock init misses
|
||||
gfx->fillScreen(0x0000);
|
||||
gfx->setBrightness(200);
|
||||
}
|
||||
|
||||
void display_hal_set_brightness(uint8_t level) {
|
||||
if (gfx) gfx->setBrightness(level);
|
||||
}
|
||||
|
||||
void display_hal_fill_screen(uint16_t color) {
|
||||
if (gfx) gfx->fillScreen(color);
|
||||
}
|
||||
|
||||
void display_hal_draw_bitmap(int32_t x, int32_t y, int32_t w, int32_t h,
|
||||
const uint16_t* pixels) {
|
||||
if (gfx) gfx->draw16bitRGBBitmap(x, y, (uint16_t*)pixels, w, h);
|
||||
}
|
||||
|
||||
void display_hal_tick(void) {
|
||||
// No rotation cycle on this board.
|
||||
}
|
||||
|
||||
// Mirrors the CO5300/SH8601 even-alignment pattern from the other ports.
|
||||
// Harmless on SH8601, kept for consistency.
|
||||
void display_hal_round_area(int32_t* x1, int32_t* y1, int32_t* x2, int32_t* y2) {
|
||||
*x1 = *x1 & ~1;
|
||||
*y1 = *y1 & ~1;
|
||||
*x2 = *x2 | 1;
|
||||
*y2 = *y2 | 1;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
#include "../../hal/imu_hal.h"
|
||||
#include "board.h"
|
||||
#include <Arduino.h>
|
||||
#include <Wire.h>
|
||||
#include <SensorQMI8658.hpp>
|
||||
|
||||
// QMI8658 is populated on the 2.16 carrier PCB but rotation is disabled
|
||||
// on the C6 build (BOARD_HAS_ROTATION=0). We still initialize the device
|
||||
// so the shared I2C bus stays healthy and so a future build can flip
|
||||
// rotation on without changing this file. Always reports quadrant 0.
|
||||
|
||||
static SensorQMI8658 imu;
|
||||
|
||||
void imu_hal_init(void) {
|
||||
if (!imu.begin(Wire, QMI8658_L_SLAVE_ADDRESS, IIC_SDA, IIC_SCL)) {
|
||||
Serial.println("QMI8658 init failed");
|
||||
return;
|
||||
}
|
||||
Serial.println("QMI8658 init OK (rotation disabled on this board)");
|
||||
}
|
||||
|
||||
void imu_hal_tick(void) {
|
||||
// No-op — rotation is disabled.
|
||||
}
|
||||
|
||||
uint8_t imu_hal_rotation_quadrant(void) { return 0; }
|
||||
@@ -0,0 +1,17 @@
|
||||
#include "../../hal/input_hal.h"
|
||||
#include "board.h"
|
||||
#include <Arduino.h>
|
||||
|
||||
// Only BOOT is wired to a MCU GPIO. The "PWR" side button is the AXP2101
|
||||
// PKEY, handled in power.cpp.
|
||||
|
||||
void input_hal_init(void) {
|
||||
pinMode(BTN_BACK_GPIO, INPUT_PULLUP);
|
||||
}
|
||||
|
||||
bool input_hal_is_held(InputButton btn) {
|
||||
if (btn == INPUT_BTN_PRIMARY) {
|
||||
return digitalRead(BTN_BACK_GPIO) == LOW;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
#include "../../hal/power_hal.h"
|
||||
#include "board.h"
|
||||
#include <Arduino.h>
|
||||
#include <Wire.h>
|
||||
#include <XPowersLib.h>
|
||||
|
||||
// AXP2101 PMU — identical chip and protocol to the S3 variant.
|
||||
|
||||
#define BATTERY_POLL_MS 2000
|
||||
#define CHARGING_POLL_MS 500
|
||||
#define PWR_POLL_MS 50
|
||||
|
||||
// The PMU instance is owned by board_init.cpp on this board — it has to
|
||||
// come up before display_hal_init() to enable the LCD power rails. We
|
||||
// reuse the same handle here for battery polling and PKEY IRQ wiring.
|
||||
extern XPowersPMU board_pmu;
|
||||
#define pmu board_pmu
|
||||
|
||||
static int cached_pct = -1;
|
||||
static bool cached_charging = false;
|
||||
static bool cached_vbus = false;
|
||||
static bool pwr_pressed_flag = false;
|
||||
static uint32_t last_battery_ms = 0;
|
||||
static uint32_t last_charging_ms = 0;
|
||||
static uint32_t last_pwr_ms = 0;
|
||||
|
||||
void power_hal_init(void) {
|
||||
// pmu.begin() already ran in board_init(); just configure battery +
|
||||
// IRQ wiring here.
|
||||
pmu.enableBattDetection();
|
||||
pmu.enableBattVoltageMeasure();
|
||||
|
||||
// Mirror the Waveshare XiaoZhi BSP charging config so the on-chip
|
||||
// fuel gauge has the right reference numbers. Without these,
|
||||
// getBatteryPercent() returns -1 / shows "---" on the UI.
|
||||
pmu.setChargeTargetVoltage(XPOWERS_AXP2101_CHG_VOL_4V1);
|
||||
pmu.setChargerConstantCurr(XPOWERS_AXP2101_CHG_CUR_400MA);
|
||||
pmu.setChargerTerminationCurr(XPOWERS_AXP2101_CHG_ITERM_25MA);
|
||||
pmu.setPrechargeCurr(XPOWERS_AXP2101_PRECHARGE_50MA);
|
||||
|
||||
pmu.disableIRQ(XPOWERS_AXP2101_ALL_IRQ);
|
||||
pmu.clearIrqStatus();
|
||||
pmu.enableIRQ(XPOWERS_AXP2101_PKEY_SHORT_IRQ);
|
||||
|
||||
cached_charging = pmu.isCharging();
|
||||
cached_vbus = pmu.isVbusIn();
|
||||
cached_pct = pmu.getBatteryPercent();
|
||||
}
|
||||
|
||||
void power_hal_tick(void) {
|
||||
uint32_t now = millis();
|
||||
|
||||
if (now - last_charging_ms >= CHARGING_POLL_MS) {
|
||||
last_charging_ms = now;
|
||||
cached_charging = pmu.isCharging();
|
||||
cached_vbus = pmu.isVbusIn();
|
||||
}
|
||||
if (now - last_battery_ms >= BATTERY_POLL_MS) {
|
||||
last_battery_ms = now;
|
||||
cached_pct = pmu.getBatteryPercent();
|
||||
}
|
||||
if (now - last_pwr_ms >= PWR_POLL_MS) {
|
||||
last_pwr_ms = now;
|
||||
pmu.getIrqStatus();
|
||||
if (pmu.isPekeyShortPressIrq()) {
|
||||
pwr_pressed_flag = true;
|
||||
}
|
||||
pmu.clearIrqStatus();
|
||||
}
|
||||
}
|
||||
|
||||
int power_hal_battery_pct(void) { return cached_pct; }
|
||||
bool power_hal_is_charging(void) { return cached_charging; }
|
||||
bool power_hal_is_vbus_in(void) { return cached_vbus; }
|
||||
|
||||
bool power_hal_pwr_pressed(void) {
|
||||
if (pwr_pressed_flag) {
|
||||
pwr_pressed_flag = false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
#include "../../hal/touch_hal.h"
|
||||
#include "board.h"
|
||||
#include <Arduino.h>
|
||||
#include <Wire.h>
|
||||
#include <TouchDrvCSTXXX.hpp>
|
||||
|
||||
static TouchDrvCST92xx touch;
|
||||
|
||||
static volatile bool touch_data_ready = false;
|
||||
static volatile bool touch_pressed = false;
|
||||
static volatile uint16_t touch_x = 0;
|
||||
static volatile uint16_t touch_y = 0;
|
||||
|
||||
static void IRAM_ATTR touch_isr(void) {
|
||||
touch_data_ready = true;
|
||||
}
|
||||
|
||||
void touch_hal_init(void) {
|
||||
touch.setPins(TP_RST, TP_INT);
|
||||
if (!touch.begin(Wire, CST9220_ADDR, IIC_SDA, IIC_SCL)) {
|
||||
Serial.println("Touch init failed");
|
||||
return;
|
||||
}
|
||||
touch.setMaxCoordinates(LCD_WIDTH, LCD_HEIGHT);
|
||||
// C6 2.16 panel mapping (verified empirically): the CST9217's raw
|
||||
// axes are swapped relative to the SH8601 raster AND X is mirrored.
|
||||
// Matches the Waveshare BSP, which reads y = raw_byte1, x = W - raw_byte2.
|
||||
touch.setSwapXY(true);
|
||||
touch.setMirrorXY(true, false);
|
||||
pinMode(TP_INT, INPUT_PULLUP);
|
||||
attachInterrupt(TP_INT, touch_isr, FALLING);
|
||||
Serial.println("Touch init OK");
|
||||
}
|
||||
|
||||
void touch_hal_read(uint16_t* x, uint16_t* y, bool* pressed) {
|
||||
if (touch_data_ready) {
|
||||
touch_data_ready = false;
|
||||
int16_t tx[5], ty[5];
|
||||
uint8_t n = touch.getPoint(tx, ty, touch.getSupportTouchPoint());
|
||||
if (n > 0) {
|
||||
touch_pressed = true;
|
||||
touch_x = (uint16_t)tx[0];
|
||||
touch_y = (uint16_t)ty[0];
|
||||
} else {
|
||||
touch_pressed = false;
|
||||
}
|
||||
}
|
||||
*x = touch_x;
|
||||
*y = touch_y;
|
||||
*pressed = touch_pressed;
|
||||
}
|
||||
+19
-3
@@ -21,8 +21,17 @@
|
||||
|
||||
static UsageData usage = {};
|
||||
|
||||
// ---- LVGL draw buffers (PSRAM, partial render mode) ----
|
||||
// ---- LVGL draw buffers (partial render mode) ----
|
||||
// PSRAM-equipped boards (S3) can comfortably hold larger strips. PSRAM-free
|
||||
// boards (e.g. ESP32-C6) allocate from internal SRAM, so we shrink the strip
|
||||
// — 480×20 RGB565 = 19 KB × 2 buffers = 38 KB, fits beside everything else.
|
||||
#ifdef BOARD_HAS_PSRAM
|
||||
#define BUF_LINES 40
|
||||
#define LV_BUF_CAPS (MALLOC_CAP_SPIRAM)
|
||||
#else
|
||||
#define BUF_LINES 20
|
||||
#define LV_BUF_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)
|
||||
#endif
|
||||
static uint16_t* buf1 = nullptr;
|
||||
static uint16_t* buf2 = nullptr;
|
||||
|
||||
@@ -111,6 +120,12 @@ static char cmd_buf[CMD_BUF_SIZE];
|
||||
static int cmd_pos = 0;
|
||||
|
||||
static void send_screenshot() {
|
||||
#ifndef BOARD_HAS_PSRAM
|
||||
// A full RGB565 framebuffer doesn't fit in internal SRAM on PSRAM-free
|
||||
// boards (e.g. 480×480×2 = 460 KB). Capture is unsupported there.
|
||||
Serial.println("SCREENSHOT_UNSUPPORTED");
|
||||
return;
|
||||
#else
|
||||
const uint32_t w = board_caps().width;
|
||||
const uint32_t h = board_caps().height;
|
||||
const uint32_t row_bytes = w * 2;
|
||||
@@ -139,6 +154,7 @@ static void send_screenshot() {
|
||||
Serial.println();
|
||||
Serial.println("SCREENSHOT_END");
|
||||
heap_caps_free(sbuf);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void check_serial_cmd() {
|
||||
@@ -182,8 +198,8 @@ void setup() {
|
||||
lv_init();
|
||||
lv_tick_set_cb(my_tick);
|
||||
|
||||
buf1 = (uint16_t*)heap_caps_malloc(W * BUF_LINES * 2, MALLOC_CAP_SPIRAM);
|
||||
buf2 = (uint16_t*)heap_caps_malloc(W * BUF_LINES * 2, MALLOC_CAP_SPIRAM);
|
||||
buf1 = (uint16_t*)heap_caps_malloc(W * BUF_LINES * 2, LV_BUF_CAPS);
|
||||
buf2 = (uint16_t*)heap_caps_malloc(W * BUF_LINES * 2, LV_BUF_CAPS);
|
||||
|
||||
lv_display_t* disp = lv_display_create(W, H);
|
||||
lv_display_set_color_format(disp, LV_COLOR_FORMAT_RGB565);
|
||||
|
||||
+16
-2
@@ -103,11 +103,25 @@ void splash_init(lv_obj_t *parent) {
|
||||
int min_dim = (c.width < c.height) ? c.width : c.height;
|
||||
cell = min_dim / GRID; // fits within the smaller display dimension
|
||||
if (cell < 4) cell = 4;
|
||||
|
||||
#ifdef BOARD_HAS_PSRAM
|
||||
const uint32_t canvas_caps = MALLOC_CAP_SPIRAM;
|
||||
#else
|
||||
// Without PSRAM the full 480×480 RGB565 canvas (460 KB) won't fit. Cap
|
||||
// the canvas so the buffer stays under ~80 KB, leaving the rest of
|
||||
// internal SRAM free for LVGL, NimBLE, and the audio/PMU stacks. The
|
||||
// canvas is centered, so the cost is extra black border around the
|
||||
// pixel art — not cropping.
|
||||
const uint32_t canvas_caps = MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT;
|
||||
const int MAX_CELL_NO_PSRAM = 10; // 10*20=200; 200*200*2=78 KB
|
||||
if (cell > MAX_CELL_NO_PSRAM) cell = MAX_CELL_NO_PSRAM;
|
||||
#endif
|
||||
|
||||
canvas_w = GRID * cell;
|
||||
canvas_h = GRID * cell;
|
||||
|
||||
canvas_buf = (uint16_t*)heap_caps_malloc(canvas_w * canvas_h * 2, MALLOC_CAP_SPIRAM);
|
||||
row_buf = (uint16_t*)heap_caps_malloc(canvas_w * 2, MALLOC_CAP_SPIRAM);
|
||||
canvas_buf = (uint16_t*)heap_caps_malloc(canvas_w * canvas_h * 2, canvas_caps);
|
||||
row_buf = (uint16_t*)heap_caps_malloc(canvas_w * 2, canvas_caps);
|
||||
if (!canvas_buf || !row_buf) {
|
||||
Serial.println("splash: failed to alloc canvas buffer");
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user