这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » DIY与开源设计 » 电子DIY » [ESP-IDF]使用ESP32-S31完成画板功能

共5条 1/1 1 跳转至

[ESP-IDF]使用ESP32-S31完成画板功能

高工
2026-08-10 04:16:24     打赏

简介

最近在 ESP32-S31 上折腾了一个触摸画板,界面用的是 LVGL,目前已经能完成自由绘画、切换颜色、调整画笔粗细、橡皮擦、撤销/重做、清空画布,这些操作。代码不算特别长,但里面同时碰到了触摸坐标、Canvas 绘制、PSRAM、历史记录和异步文件保存,还是有不少值得聊的地方。下面按我自己读代码的思路拆开说,也顺手把目前存在的几个坑记下来,方便后面继续改。

37d6bf2621b5479fcfa0b671c0641746.jpg

  1. 项目平台:ESP32-S31 Korvo-1  (上次张总送的那个)

  2. 图形库:LVGL 9.x  

  3. 开发框架:ESP-IDF


先说一下整体思路

1. 触摸屏按下时,新建一条笔画;2. 手指移动时,不断记录坐标点,同时把相邻点连起来;3. 手指松开时,把整条笔画放进历史记录;4. 撤销或重做时,清空 Canvas,再根据历史记录把有效笔画全部重画一遍;


一开始想得挺简单:屏幕能跟着手指画线,再放几个颜色按钮,差不多就算完成了。真写起来才发现,这类项目麻烦的地方不在画一条线,而在触摸坐标、PSRAM、历史记录、LVGL 状态样式这些细节上。目前这个版本已经可以正常使用,支持: 触摸自由绘画、 黑、红、绿、蓝四种颜色、通过底部滑条调整笔画粗细、撤销和重做,最多保留 20 条笔画、 一键清空画布。


画布主体尺寸是 608 × 350,放在屏幕中间偏右的位置。左侧是颜色和工具栏,底部单独留了一条区域调节笔画粗细。现在颜色没有堆很多,只保留了最常用的四个:

static lv_color_t color_palette[4];

static void init_color_palette(void)
{
    color_palette[0] = lv_color_black();
    color_palette[1] = lv_color_make(0xFF, 0x00, 0x00);  // Red
    color_palette[2] = lv_color_make(0x00, 0xC0, 0x40);  // Green
    color_palette[3] = lv_color_make(0x00, 0x60, 0xFF);  // Blue
}

之前颜色比较多,按钮又是纵向排列,结果工具栏很容易放不下。现在四个色块改成 28 × 28,并按 2 × 2 排列,看起来更清楚,手指点击也比原来的小色块舒服。工具按钮目前保留 Undo、Redo 和 Clear。


Canvas 为什么要放到 PSRAM:

这里需要注意的是Canvas是放到了PSRAM里,所以需要再menuconfig中配置好psram。画布使用 RGB565,一个像素占 2 字节。只算原始像素数据,大概就需要:608 × 350 × 2  = 416 KiB 。就这还没有算内存对齐和 LVGL 自己的数据。对 ESP32 来说,这种大块缓冲区显然更适合放到 PSRAM。

uint32_t stride = lv_draw_buf_width_to_stride(
    CANVAS_WIDTH,
    LV_COLOR_FORMAT_RGB565
);
uint32_t buf_size = stride * CANVAS_HEIGHT;

void *buf = heap_caps_aligned_alloc(
    64,
    buf_size,
    MALLOC_CAP_SPIRAM
);
这里我觉得有两个点比较重要。第一,图像每行实际占用的内存不一定永远等于 `width × 2`,使用 LVGL 提供的 stride 接口更稳。第二,缓冲区做了 64 字节对齐,可以减少 Cache 一致性方面的麻烦。画布创建完成之后,直接绑定这块缓冲区:
lv_canvas_set_buffer(
    app->canvas,
    buf,
    CANVAS_WIDTH,
    CANVAS_HEIGHT,
    LV_COLOR_FORMAT_RGB565
);
最后填充成白色,整个绘画区域就准备好了。


触摸坐标不能拿来直接画

触摸驱动给出的是屏幕坐标,而画线使用的是 Canvas 内部坐标。Canvas 并不是从屏幕左上角 `(0, 0)` 开始,所以需要先拿到它在屏幕上的位置,再把触摸点转换成本地坐标:

lv_indev_get_point(indev, &point);
lv_obj_get_coords(canvas, &canvas_area);

int32_t x = point.x - canvas_area.x1;
int32_t y = point.y - canvas_area.y1;
这一步如果漏掉,手指和笔迹之间就会出现固定偏移,看起来就像触摸校准错了一样(会出现偏移的情况)。转换之后还要做一次边界判断:
if (x < 0 || x >= CANVAS_WIDTH ||
    y < 0 || y >= CANVAS_HEIGHT) {
    return;
}

否则手指从画布拖到外面时,错误坐标可能继续参与绘制。整个绘画过程对应三个 LVGL 事件:

  1. LV_EVENT_PRESSED:手指刚按下,开始一条新笔画;
  2. LV_EVENT_PRESSING:手指移动,不断追加坐标点

  3. LV_EVENT_RELEASED`:手指松开,把这条笔画提交到历史记录。

笔画结构很简单,只保存点、颜色和粗细:
typedef struct {
    lv_point_precise_t points[MAX_POINTS_PER_STROKE];
    uint32_t point_count;
    lv_color_t color;
    uint8_t width;
} stroke_t;
手指移动时,每得到一个新坐标,就把它和上一个坐标连起来:
line_dsc.p1 = stroke->points[stroke->point_count - 2];
line_dsc.p2 = stroke->points[stroke->point_count - 1];
line_dsc.color = stroke->color;
line_dsc.width = stroke->width;

lv_draw_line(&layer, &line_dsc);

这样每次只画最新的一小段,不需要在手指移动时反复重画整个 Canvas,实际响应会快很多。对于笔画的历史记录则是最高保存了20条,因为保存的太多的花也并没有实际的意义。

```c
stroke_t *stroke = &history.strokes[history.stroke_count];
```

当当前的手触摸到屏幕的时候会将当前的坐标写入下述的结构体中的active_stroke,然后当松下的时候则会将当前的状态保存history 中,这样便可以完成两个状态的控制。

typedef struct {
    /* 界面和绘画状态…… */
    stroke_history_t history;
    stroke_t active_stroke;
    bool is_drawing;
} draw_app_t;

而Undo和Redo, 撤销和重做则是从保存的的数组内的索引进行调整,这样的话就实现了历史记录的功能。而如果超过了20个的话则最先的数据会被覆盖。


遇见的坑

这次遇见最折腾的坑就是底部调整笔画粗细的这个滑条。原本加上去滑条之后都是正常的,但是调整完笔画的粗细的时候滑条会出现花屏类似的这种情况。自己尝试了好久都修复不了。然后便开始借助AI工具,使用Claude搞不定。后来使用了GPT5.5开了高等级的推理最终找到了原因是因为:手松手的时候触发了动画的事件。也就是说按下的时候滑块是正常的,但是等松手的时候触发了缩放所以导致没有清除之前的像素就留下了残影。


总结

做到这里,这个小画板也算是基本成型了,从一开始只是能在屏幕上画几条线,到现在已经有点真正画板的感觉了。Canvas 的缓冲区放到了 PSRAM,触摸坐标也做了转换,画的时候会记录每一笔的轨迹,所以 Undo 和 Redo 也能正常用了,下面的 Slider 还可以随时调画笔粗细。中间其实也踩了不少坑,尤其是 Slider 松手之后偶尔花屏这个问题,查了半天,最后才发现居然是 LVGL 默认主题在松手的时候会让旋钮缩小,改成自己的静态样式之后总算解决了。至于后续的更新可能会专注于怎么增加上橡皮擦的功能和保存到SD卡然后读取的功能。橡皮擦的话还没有好的灵感,暂时短期内应该不会做。 而保存到SD卡的话相对简单。只需要序列化数组即可。


附件:烧录可以直接使用,记得开启PSRAM和确认分区大小

drawing.zip




关键词: ESP-IDF     ESP32-S31     画板    

专家
2026-08-10 09:04:21     打赏
2楼

谢谢分享


院士
2026-08-10 13:25:41     打赏
3楼

帖子写得真好。

有内容,还有实验。

赞一下


专家
2026-08-10 16:30:28     打赏
4楼
点赞

助工
2026-08-11 10:59:06     打赏
5楼

我使用一块小资源的MCU,搭配PSRAM,是不是也可以玩图形GUI了啊!


共5条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]