你好,我是吴老师。这份是给你自己读的——不用等我讲,看不懂就整段复制给 AI,加一句"请一次只解释一个概念,讲完问我一个小问题"。
读完之后,你应该能:
- 说清"用触摸画出一条连续的线"是怎么做到的;
- 明白"画笔 = 记住上一帧位置,把上一帧和当前连成一条线";
- 说清颜色、粗细、橡皮为什么都只是"换一个状态";
- 明白大画布为什么用
ps_malloc(PSRAM),而不是放到栈上。用时:约 60 分钟。 准备:烧录
demo01_touch_pen的板子 + 一根 USB 线。
一、让板子变成你的画板
前面几课,我们用板子放音乐、讲绘本、对讲、聊天——都是在用别人做好的东西。今天换个玩法:让板子变成画板,你想画什么,就亲手画出来。
第 5 课你会画一个固定的点,第 8 课你学会用手指告诉板子"我点了哪"。今天把这两样接起来:手指按到哪,笔就画到哪,而且是一条连续的线。
先看现象:烧录后手指在屏幕上拖动,就画出一笔;右上角有个 RST,点一下画板清空。
二、我观察到的现象
| 现象 | 勾选 |
|---|---|
| 手指按到屏幕,那个位置出现一截笔迹 | |
| 手指拖动,笔迹连成一条连续线 | |
| 手抬起来再按别处,不会凭空拉一条长线 | |
笔迹是红或蓝(由 color_mask 决定) | |
| 点右上角 RST,画板清空 |
关键问题:手指明明是"点一下接一下",屏幕怎么画成一条连续的线? 答案:板子记住了你上一笔在哪,再把"上一个位置"和"现在的位置"两个点连起来。
三、一笔一画,走过的路
| 环节 | 谁在干 | 学过 |
|---|---|---|
| 手指在哪 | FT6336 触摸 | 第 8 课 |
| 坐标传进程序 | touch_last_x / touch_last_y | 第 8 课 |
| 记住上一笔在哪 | lastpos[] | 本课 |
| 把两点连成线 | LCD_Draw_Line | 本课 |
| 显示出来 | TFT_eSPI 画一串小圆点 | 第 5 课 |
整支画笔几乎没有新知识——触摸是第 8 课、画点是第 5 课。真正新增的只有"怎么把一个点接到上一个点"。
四、画笔,就像一支墨水笔
画画就像用墨水笔在纸上滑。手指是笔尖,屏幕是纸。
- 手指按到屏幕 = 笔尖落纸,留下一点墨;
- 手指拖着走 = 笔尖不断往前,墨不断留下 → 成一条线;
- 手指抬起来 = 笔尖离开纸,不再留墨;
- 再按到别处 = 从新的地方重新落笔。
⚠️ 比喻有边界:"墨水笔"是帮你理解落笔/拖动/抬笔三个状态,不是真涂了墨水。屏幕上其实是一串小圆点(每帧画一个圆),挨得近才像连续线。手指拖得快,点之间会有空隙。"橡皮"也不是擦掉墨水,是用背景色(比如白色)再盖画一遍。
一句话记住:画笔 = 落笔 → 拖动(把上一帧和当前连成线)→ 抬笔。
五、两个代码名,够了
1. LCD_Draw_Line(x1, y1, x2, y2, Size, colour)——把两点连成线
x1, y1 = 上一帧手指位置
x2, y2 = 现在手指位置
Size = 笔的粗细(demo 里写死成 2)
colour = 笔的颜色(color_mask 数组里的一个)
它做的:用 Bresenham 算法算出两点间的每一个圆点,
再沿这条线一个个 fillCircle 画出来(Size 越大,点越粗)。
别看它叫"画线",其实是在这条线上画一串小圆点。所以"笔的粗细"不是线多宽,而是"每个圆点有多大"。
颜色数组:
color_mask[] = {0xF800, 0x001F},0xF800 是红、0x001F 是蓝。把color_mask[0]改成color_mask[1],笔就变蓝。
2. lastpos + 0xFFFF——记住上一帧在哪、笔抬起来了
lastpos[0], lastpos[1] = 记住"上一帧手指在哪"的两个数
0xFFFF = 一个特殊记号:笔还没落下 / 笔抬起来了
逻辑(loop 里):
手指按着:
刚按下(lastpos==0xFFFF) → 先把 lastpos 设成当前位置
把上一帧和当前连成线(LCD_Draw_Line)
更新 lastpos = 当前
手指抬起:
lastpos[0] = 0xFFFF // 记成"笔抬起来了"
为什么抬手要记
0xFFFF?因为不记,抬起来再按别处,程序会从旧位置凭空画一条长线到新位置——那根本不是你想画的。
最该记住的:画笔 = 记住上一帧位置 lastpos,用 LCD_Draw_Line 把上一帧和当前连起来;0xFFFF 记着"笔抬起来了",抬手后重置,好让下一笔从新位置开始。
六、颜色、粗细、橡皮——都是"状态在变"
demo01 只画红色、粗细固定。想换颜色、变粗、加橡皮,思路一模一样——都只是"换一个状态",画线动作不变。
- 把
color_mask[0]改成color_mask[1]→ 笔变蓝; - 把
2(Size)改成更大的数 → 笔变粗; - 把颜色改成背景色(白色)再画一遍 → 就是"橡皮",把旧笔迹盖掉。
这就是"工具切换":画面怎么画永远不变,变的只是"当前用哪种状态"(颜色、粗细、画笔还是橡皮)。
| 对照思考(可自己写) | 勾选 |
|---|---|
color_mask[1] → 笔变蓝 | |
| Size 2→6 → 笔明显变粗 | |
| 画线颜色改白色 → 等于橡皮 |
⚠️ 注意:demo 里颜色其实是写死的(每帧都画
color_mask[0])。真正让"点一下颜色就变"的,是"点色块 → 改一个全局状态变量",那需要你扩展时自己加上。别把"规律"当"代码里已有"。
七、改一个条件:把"连线"去掉
思想实验:把 LCD_Draw_Line 去掉,改成每帧只画当前这个点,会怎样?
| 我改了什么 | 我预测 | 实际 |
|---|---|---|
| 去掉连线,每帧只画当前点 | 手指挪得快两点之间有缝 → 必须把上一帧和当前连起来 | |
抬手时不把 lastpos 设成 0xFFFF | 下次按下用旧位置连到新位置,画出不该有的直线 → 抬手必须重置 |
"连成一条线"和"记住上一帧位置"是同一件事的两面:要连线就记住上一帧;要每次重新起笔就抬手清掉它。
八、一条完整的"画一笔"链路
手指按下屏幕 → FT6336 读出坐标 (touch_last_x, touch_last_y)
→ 这次是不是刚按下?lastpos==0xFFFF
是(新一笔)→ 先把 lastpos 设成当前位置(落笔)
否(还在同一笔)→ 直接用上次记的 lastpos
→ LCD_Draw_Line(上一个位置, 当前, 粗细2, 颜色)
→ 屏幕沿这条线画一串小圆点(就是笔迹)
→ 把上一个位置更新成当前(lastpos=当前)
→ 手指还按着 → 回去再连下一次,线一直往前画
→ 手指抬起 → lastpos 记成 0xFFFF(笔抬起来了)
→ 下次再按 → 从新位置重新开始
真正要记住的只有一句:画笔 = 记住上一帧位置,把上一帧和当前两个点连成一条线;每动一点就连一点,笔迹就连起来了。
九、过关检查
- 一支画笔,程序靠什么知道"笔现在在哪"?
- 为什么不能每帧只画一个点,非要连成一条线?
- 抬起来再按别处,怎么避免从旧位置拉一条长线?
- 想换颜色、变粗、加橡皮,分别改哪个"状态值"?哪个动作永远不变?
- 大画布为什么要用
ps_malloc放到 PSRAM,而不是放栈上? - 想把画保存/截图,为什么不能用
readRect去读屏幕?
留下三样东西:你画的第一笔的样子、你换颜色后画的那一笔、以及"一支画笔在程序里到底是什么"的一句话。
十、还能玩什么
| 作品方向 | 描述给 AI 的效果 | 再想想 |
|---|---|---|
| 调色盘 | 屏幕上几个色块,点一下换颜色 | 怎么让"点色块"改变颜色下标? |
| 橡皮擦 | 手指擦掉画过的笔迹 | 背景不是白色时,橡皮怎么"盖"? |
| 笔的粗细 | 点按钮换粗细 | LCD_Draw_Line 的 Size 怎么跟着状态变? |
| 保存图片 | 画完存成 PNG / 存进 TF 卡 | 为什么截图用内存画布(ps_malloc)而不是 readRect? |
| 你画我猜 | 画好传给另一块板子,让对方猜 | 怎么把这一串坐标打包发过去? |
下节课预告:第 8 章到此收官,你已经在板子上做出一个又一个能玩的作品。下一章(第 9 章)板子要"更聪明"——第 43 课,让板子走进"AI 生活",把它和你每天的学习、生活真正连在一起。