当前位置: 首页 > news >正文

【CanMV K210】显示交互 触摸屏画图与 LCD 轨迹绘制

在智能硬件项目中,触摸屏经常承担“输入”和“显示”两个角色。电子画板、设备配置面板、手写签名、交互式控制台、工业设备调试界面,都需要把手指触摸的位置转换成程序能够处理的数据,再通过屏幕反馈成可见图形。对于 Python 硬件编程入门而言,触摸屏画图实验很适合作为图形交互的起点,因为它能直观看到坐标读取、状态判断、图像绘制和屏幕刷新之间的关系。

本实验基于 CanMV K210 开发板实现一个简单的触摸屏画图 Demo。程序会初始化 LCD 显示屏和 I2C 触摸屏,手指按下或移动时,屏幕上会绘制红色触摸点和白色连续轨迹。按下 BOOT_KEY 后,程序会清空画布并重新绘制顶部状态栏。整个实验不是简单显示一张图片,而是让代码持续读取触摸状态,把真实手势转化成屏幕上的线条。

学习目标说明
理解图形交互认识触摸输入、图像绘制和 LCD 显示之间的关系
掌握触摸坐标读取通过 I2C 触摸屏读取触摸状态、X 坐标和 Y 坐标
学会 LCD 画布绘制使用image.Image()创建画布,并绘制文字、圆点和线条
理解轨迹绘制逻辑通过保存上一帧坐标,把离散触摸点连接成连续轨迹
掌握按键清屏使用 BOOT_KEY 作为清空画布的输入按钮
建立交互项目思路为触摸菜单、参数面板、手写输入和硬件控制界面打基础

这个实验的重点不只是“能在屏幕上画线”,而是建立触摸屏交互的基本流程:触摸屏负责输入坐标,程序负责判断状态和绘制图形,LCD 负责显示结果,BOOT_KEY 负责触发清屏操作。后续的触摸菜单、屏幕按钮、参数调节界面和 AI 识别结果标注,都可以复用这套结构。

文章目录

  • 理论基础
  • 硬件设施
  • 软件代码
  • 扩展应用
  • 总结

理论基础

触摸屏实验同时包含输入和输出两个方向。输入来自触摸屏,程序需要读取手指是否按下、是否移动,以及当前触摸坐标;输出来自 LCD,程序需要把点、线、文字和背景画到图像画布上,再刷新到屏幕显示。这个过程比普通 LED 或按键实验更接近真实的人机交互系统。

LCD 本身主要负责显示,不能直接理解触摸动作。触摸屏则通过 I2C 总线向 K210 提供触摸状态和坐标数据。程序读取到坐标后,并不是直接把坐标显示出来,而是根据状态判断是否需要绘制点或线。当触摸状态为按下或移动时,程序会在当前位置画一个红色小点;如果已经存在上一帧坐标,就把上一帧坐标和当前坐标连成白色线段,从而形成连续轨迹。

BOOT_KEY 在本实验中作为清屏按钮使用。它不参与触摸轨迹绘制,只负责触发clear_canvas()。按键按下后,程序会清空当前画布,然后重新绘制顶部提示栏。这个设计让触摸屏负责绘图输入,BOOT_KEY 负责功能控制,LCD 负责统一显示,形成一个完整的交互闭环。

手指触摸屏幕
按下 / 移动 / 松开

I2C 触摸屏
读取状态与坐标

CanMV K210
IO30=SCL / IO31=SDA

ts.read()
返回 status, x, y

状态判断
PRESS / MOVE / IDLE

image 画布绘制
点 / 线 / 顶部提示栏

lcd.display(img)
刷新到 LCD

BOOT_KEY
IO16

GPIO1 输入检测

clear_canvas()
清空画布并重绘提示栏

这张流程图展示的是触摸屏画图实验的控制链路。触摸屏通过 I2C 把状态和坐标传给 K210,程序根据坐标在image画布上绘制图形,再通过 LCD 显示出来。BOOT_KEY 作为额外输入,负责触发清屏逻辑。整个实验体现了“输入事件 -> 程序处理 -> 图形反馈”的交互过程。

轨迹绘制的关键是“上一点”和“当前点”。如果每次触摸只画一个圆点,手指快速移动时屏幕上会出现一串分散的点,轨迹不够连续。代码中使用last_xlast_yhas_last_point记录上一帧触摸位置,当新的触摸点到来时,程序用draw_line()把两个点连接起来。这样手指移动时,屏幕上就会形成连续线条。

硬件设施

本实验围绕 LCD 显示屏、I2C 触摸屏和 BOOT_KEY 按键展开。LCD 负责显示画布和触摸轨迹,触摸屏通过 I2C 总线向 K210 提供触摸状态与坐标数据,BOOT_KEY 作为清空画布的输入按钮。软件侧主要使用lcdimagetouchscreenmachine.I2Cmaix.GPIOfpioa_manager.fm,分别负责显示刷新、图像绘制、触摸读取、I2C 通信、按键输入和引脚功能映射。

实验接线和运行效果可以通过下面的图片建立整体印象。触摸屏负责输入坐标,LCD 负责显示轨迹,BOOT_KEY 用于清空画布。检查硬件时重点关注触摸屏 I2C 通信、LCD 显示是否正常,以及 BOOT_KEY 是否能被程序读取。

硬件 / 软件作用说明
CanMV K210 开发板实验运行平台负责执行 Python 程序,连接 LCD、触摸屏和 BOOT_KEY
LCD 显示屏图像输出设备显示顶部提示信息、触摸点和连续轨迹
I2C 触摸屏坐标输入设备通过触摸状态、X 坐标和 Y 坐标反馈手指操作
BOOT_KEY清空画布按钮按下后触发clear_canvas(),用于重新开始绘制
IO30I2C 时钟线根据代码和注释推导,对应触摸屏 SCL
IO31I2C 数据线根据代码和注释推导,对应触摸屏 SDA
IO16按键输入引脚根据代码推导,注册为 GPIO1 后读取 BOOT_KEY 状态
lcd屏幕控制模块用于初始化屏幕、清屏和显示图像
image图像绘制模块创建画布,并绘制矩形、文字、圆点和线条
touchscreen触摸屏模块初始化触摸屏并读取触摸状态和坐标
machine.I2CI2C 通信模块创建触摸屏通信总线
maix.GPIOGPIO 控制模块用于读取 BOOT_KEY 的按下状态
fpioa_manager.fm引脚映射模块将物理引脚 IO16 注册为 GPIO1

接线关系可以从代码中的 I2C 配置、按键注册和注释说明中推导出来。触摸屏通过 I2C 与 K210 通信,SCL 对应 IO30,SDA 对应 IO31。BOOT_KEY 使用 IO16,并通过fm.register(CLEAR_KEY_PIN, CLEAR_KEY_GPIO, force=True)注册为 GPIO1。LCD 的具体硬件连接没有在代码中展开,程序通过lcd.init()lcd.display(img)直接调用显示接口。

物理引脚 / 接口功能引脚 / 总线代码变量对应硬件说明
IO30I2C SCLI2C_SCL_PIN触摸屏 SCL用作触摸屏 I2C 时钟线
IO31I2C SDAI2C_SDA_PIN触摸屏 SDA用作触摸屏 I2C 数据线
IO16GPIO1btn_clearBOOT_KEY按下后用于清空画布
LCD 显示接口LCD 控制接口lcdimgLCD 屏幕通过 LCD 模块接口完成显示
I2C0触摸通信总线TOUCH_I2C_ID触摸屏通信用于读取触摸状态和坐标
VCC / GND供电与地线LCD 与触摸屏保证屏幕和触摸通信正常工作

物理引脚可以理解成开发板连接真实硬件的入口,功能引脚则是程序对这些入口赋予的角色。IO30 和 IO31 被用于 I2C 通信,负责让 K210 与触摸屏交换坐标数据;IO16 被注册成 GPIO1,负责读取按键是否被按下;LCD 显示部分由lcd模块统一管理,程序只需要把绘制好的img画布刷新到屏幕上。

实验现象正常表现异常提示
程序启动LCD 显示顶部提示栏,画布区域为空无显示时检查 LCD 初始化和程序运行状态
手指按下触摸位置出现红色小点无触摸响应时检查 I2C 触摸屏初始化
手指移动屏幕显示白色连续轨迹轨迹断续时检查刷新频率和触摸采样速度
手指松开轨迹停止延伸,下一次触摸重新记录起点松开后仍乱画时检查触摸状态读取
按下 BOOT_KEY画布清空,顶部提示栏保留无法清屏时检查 IO16 和按键电平
顶部区域无法画线y 坐标小于等于 30 时不绘制轨迹这是为了保留状态栏,不属于故障

软件代码

本实验代码围绕触摸屏画图功能展开。程序先配置 LCD 尺寸、I2C 引脚、清屏按键、颜色参数和防抖时间,再分别初始化 LCD、触摸屏、BOOT_KEY 和画布。主循环中不断读取触摸状态,触摸移动时绘制连续线条,按键按下时清空画布,并持续刷新 LCD 显示。

软件环境作用检查重点
CanMV IDE编辑、运行和调试 K210 程序能识别开发板串口,并能正常运行基础测试程序
CanMV 固件提供lcdimagetouchscreenmachine.I2C等模块固件环境需要支持 LCD 和触摸屏相关接口
LCD 显示模块显示画布和轨迹程序启动后能看到顶部提示栏
I2C 触摸屏模块返回触摸状态和坐标手指按下或移动时坐标会变化
BOOT_KEY清空画布输入按下后能触发清屏
串口终端查看触摸状态变化状态变化时能看到Touch Status输出
#!/usr/bin/env python3# -*- coding: utf-8 -*-""" CanMV K210 触摸屏画图实验 Demo 功能说明: 1. 初始化 LCD 显示屏 2. 初始化 I2C 触摸屏 3. 手指按下或移动时,在屏幕上绘制连续轨迹 4. 按下 BOOT_KEY 清空画布 5. 屏幕顶部显示简单状态提示 接线说明: 触摸屏通常通过 I2C 与 K210 通信 SCL -> IO30 SDA -> IO31 BOOT_KEY -> IO16 """importtimeimportlcdimportimageimporttouchscreenastsfrommachineimportI2CfrommaiximportGPIOfromfpioa_managerimportfm# =========================# 硬件配置区# =========================LCD_WIDTH=320LCD_HEIGHT=240I2C_SCL_PIN=30I2C_SDA_PIN=31CLEAR_KEY_PIN=16CLEAR_KEY_GPIO=fm.fpioa.GPIO1 TOUCH_I2C_ID=I2C.I2C0 TOUCH_I2C_FREQ=400000PEN_COLOR=(255,255,255)TEXT_COLOR=(0,255,0)POINT_COLOR=(255,0,0)BG_COLOR=(0,0,0)CLEAR_DEBOUNCE_MS=300# =========================# 全局状态# =========================img=Nonebtn_clear=Nonelast_x=0last_y=0last_status=ts.STATUS_IDLE has_last_point=Falselast_clear_time=0# =========================# 初始化函数# =========================definit_lcd():"""初始化 LCD 显示屏"""lcd.init()lcd.clear()definit_touchscreen():"""初始化触摸屏"""i2c=I2C(TOUCH_I2C_ID,freq=TOUCH_I2C_FREQ,scl=I2C_SCL_PIN,sda=I2C_SDA_PIN)ts.init(i2c)# 首次使用触摸屏位置不准确时,可以取消下面这一行注释进行校准# ts.calibrate()definit_clear_button():"""初始化清空按钮"""globalbtn_clear fm.register(CLEAR_KEY_PIN,CLEAR_KEY_GPIO,force=True)btn_clear=GPIO(GPIO.GPIO1,GPIO.IN)defcreate_canvas():"""创建画布"""globalimg img=image.Image(size=(LCD_WIDTH,LCD_HEIGHT))clear_canvas()definit_hardware():"""统一初始化硬件"""init_lcd()init_touchscreen()init_clear_button()create_canvas()# =========================# 画布函数# =========================defdraw_header():"""绘制顶部提示信息"""img.draw_rectangle(0,0,LCD_WIDTH,28,color=(20,20,20),fill=True)img.draw_string(8,8,"Touch Draw Demo",color=TEXT_COLOR,scale=1)img.draw_string(180,8,"BOOT: Clear",color=TEXT_COLOR,scale=1)defclear_canvas():"""清空画布"""globalhas_last_point img.clear()draw_header()has_last_point=Falselcd.display(img)defdraw_touch_point(x,y):"""绘制当前触摸点"""ify>30:img.draw_circle(x,y,2,color=POINT_COLOR,fill=True)defdraw_touch_line(x1,y1,x2,y2):"""绘制触摸轨迹线"""ify1>30andy2>30:img.draw_line((x1,y1,x2,y2),color=PEN_COLOR)# =========================# 触摸处理函数# =========================defis_touching(status):"""判断当前是否处于触摸状态"""returnstatus==ts.STATUS_PRESSorstatus==ts.STATUS_MOVEdefhandle_touch():"""读取触摸数据并绘制轨迹"""globallast_x,last_y,last_status,has_last_point status,x,y=ts.read()ifis_touching(status):ifhas_last_point:draw_touch_line(last_x,last_y,x,y)else:has_last_point=Truedraw_touch_point(x,y)last_x=x last_y=yelse:has_last_point=Falseifstatus!=last_status:print("Touch Status:",status,"X:",x,"Y:",y)last_status=status# =========================# 按键处理函数# =========================defhandle_clear_button():"""检测 BOOT_KEY 是否被按下,按下后清空画布"""globallast_clear_time now=time.ticks_ms()ifbtn_clear.value()==0:iftime.ticks_diff(now,last_clear_time)>CLEAR_DEBOUNCE_MS:print("Canvas cleared")clear_canvas()last_clear_time=now# =========================# 主循环# =========================defloop():"""主循环"""whileTrue:handle_touch()handle_clear_button()lcd.display(img)time.sleep_ms(10)# =========================# 程序入口# =========================if__name__=='__main__':try:init_hardware()loop()exceptKeyboardInterrupt:lcd.clear()print("Program stopped")

这段程序可以分成硬件配置、全局状态、初始化函数、画布函数、触摸处理函数、按键处理函数和主循环。硬件配置区集中保存 LCD 尺寸、I2C 引脚、清屏按键、颜色参数和防抖时间;初始化函数负责准备 LCD、触摸屏、BOOT_KEY 和画布;画布函数负责绘制顶部提示栏、清空画面、绘制点和线;触摸处理函数负责读取坐标并更新轨迹;按键处理函数负责检测清屏操作;主循环负责持续刷新。

代码中所有图形都先绘制到img画布中,再通过lcd.display(img)显示到屏幕。这样做的好处是绘制逻辑比较集中,后续增加文字、按钮、边框或状态提示时,都可以继续在同一张画布上操作。顶部提示栏通过draw_header()绘制,画图区域通过y > 30限制,避免手指在顶部区域绘图时覆盖提示文字。

函数名功能对应现象
init_lcd()初始化 LCD 显示屏屏幕进入可显示状态,并清理旧画面
init_touchscreen()初始化 I2C 触摸屏触摸屏可以返回触摸状态和坐标
init_clear_button()初始化 BOOT_KEY按键可以被程序读取,用于清空画布
create_canvas()创建图像画布生成一张 320×240 的绘图区域
init_hardware()统一初始化硬件程序启动后完成屏幕、触摸和按键准备
draw_header()绘制顶部提示栏屏幕顶部显示 Demo 名称和清屏提示
clear_canvas()清空画布轨迹被清除,顶部提示栏重新出现
draw_touch_point()绘制触摸点手指触摸位置出现红色小点
draw_touch_line()绘制触摸轨迹手指移动路径形成白色连续线条
is_touching()判断触摸状态区分按下、移动和空闲状态
handle_touch()读取触摸并绘图手指按下或移动时,屏幕显示轨迹
handle_clear_button()检测清屏按键按下 BOOT_KEY 后清空画布
loop()持续执行主循环程序不断读取触摸、检测按键并刷新屏幕

handle_touch()是轨迹绘制的核心。ts.read()返回触摸状态、X 坐标和 Y 坐标,当状态为按下或移动时,程序会判断是否已经有上一个触摸点。如果有,就把上一点和当前点连成线;如果没有,就从当前点开始记录。手指松开后,has_last_point会被重置,下一次触摸会重新开始新的轨迹,避免不同笔画之间被错误连线。

handle_clear_button()使用了按键防抖逻辑。BOOT_KEY 按下时,程序先判断距离上一次清屏是否超过CLEAR_DEBOUNCE_MS。只有超过 300 毫秒,才执行清屏操作。这样可以减少机械按键抖动造成的重复清屏。主循环每 10 毫秒运行一次,既能保持较好的触摸响应速度,也能避免程序无意义地高速空转。

扩展应用

触摸屏画图实验同时涉及显示、I2C 通信、触摸坐标、按键输入和刷新频率。排查时需要把问题拆开看,屏幕不显示优先检查 LCD 初始化,触摸无反应优先检查 I2C 和触摸屏初始化,清屏无效优先检查 BOOT_KEY 引脚和电平逻辑。

问题现象可能原因处理思路
LCD 没有显示内容LCD 初始化失败、屏幕连接异常、程序没有运行检查lcd.init()是否正常执行,确认程序已经进入主循环
触摸没有轨迹I2C 引脚不匹配、触摸屏没有初始化成功、触摸模块异常核对 IO30 和 IO31 是否对应 SCL、SDA,检查ts.init(i2c)是否报错
轨迹位置偏移触摸屏校准数据不准确取消ts.calibrate()注释进行校准,再重新测试坐标位置
顶部区域无法画线代码限制y > 30才允许绘图这是为了保留顶部提示栏,属于程序设计逻辑
按下 BOOT_KEY 没有清屏IO16 映射不正确、按键电平逻辑不匹配、按键没有接通检查fm.register()GPIO.GPIO1是否一致,观察btn_clear.value()的返回值
按键触发多次清屏按键抖动或防抖时间过短适当增大CLEAR_DEBOUNCE_MS,例如从 300 调整到 500
画线不连续循环刷新太慢、触摸采样间隔过大、手指移动速度太快适当减小time.sleep_ms(10),或降低手指移动速度进行测试
画面刷新有闪烁感LCD 刷新频率与绘图频率不匹配保持画布绘制后统一刷新,避免在多个函数里频繁刷新屏幕
串口持续输出较少代码只在触摸状态变化时打印状态这是正常设计,持续移动时主要观察 LCD 轨迹变化

触摸屏画图实验的价值不只在于做一个简易电子画板,更重要的是建立图形交互思维。程序从触摸屏读取坐标,再把坐标转换成图形、线条和状态变化,这个过程可以迁移到很多实际项目中。设备参数设置、手写签名、交互菜单、坐标调试、轨迹采集和屏幕控制面板,本质上都可以建立在同样的触摸读取与图像绘制逻辑之上。

应用场景实现思路可扩展能力
电子画板继续使用触摸坐标绘制点和线可增加画笔颜色、线宽、橡皮擦和保存图片功能
手写签名采集将触摸轨迹记录为坐标序列或图像可扩展为签名确认、轨迹存档和身份验证实验
设备触控面板把屏幕区域划分为不同按钮可扩展菜单切换、参数设置和模式选择
触摸坐标调试在屏幕上显示触摸点位置可用于校准屏幕、检查坐标方向和识别触摸区域
简易白板工具在 LCD 上绘制文字、线条和提示信息可扩展为教学演示、会议草图或调试标记工具
轨迹采集实验保存手指移动路径的坐标变化可扩展为手势识别、运动轨迹分析和交互实验
智能硬件控制入口使用触摸区域触发不同动作后续可扩展蜂鸣器、LED、电机或传感器联动
AI 识别结果标注在摄像头画面上叠加触摸标记可扩展为目标选择、区域标注和交互式识别

从工程结构看,当前代码已经具备较好的扩展基础。硬件初始化被单独封装,画布操作和触摸处理相互独立,清屏按键也被拆成单独函数。新增功能时,可以围绕handle_touch()增加触摸区域判断,也可以围绕draw_touch_line()扩展画笔样式,还可以在clear_canvas()中加入更多界面元素。后续接入 LED、蜂鸣器、电机或传感器时,触摸屏就可以从“画图工具”升级成“硬件控制入口”。

总结

本实验通过 CanMV K210 完成了一个触摸屏画图 Demo,核心能力包括 LCD 初始化、I2C 触摸屏通信、GPIO 按键输入、图像画布创建、坐标读取、状态判断、线条绘制、屏幕刷新和按键防抖。代码把真实触摸动作转换成坐标数据,再通过image模块绘制成可见轨迹,完整展示了 Python 程序处理硬件交互的过程。

这类实验非常适合作为图形化硬件交互入门案例。触摸状态对应程序分支,坐标变化对应屏幕图形,按键输入对应画布清空,主循环负责把这些动作持续串联起来。理解这套流程后,后续可以继续扩展到触摸菜单、参数配置界面、手势识别、传感器数据显示、摄像头图像标注和 AI 识别交互等方向。触摸屏不只是显示模块,而是智能硬件项目中非常重要的人机交互入口。

http://www.jsqmd.com/news/816913/

相关文章:

  • Python MongoDB客户端实战:PyMongo深度解析
  • 米立特国产移液器全系解析:覆盖科研与工业领域的精准移液工具 - 品牌推荐大师
  • WechatDecrypt终极指南:安全高效解密微信聊天记录的完整方案
  • 避坑指南:STM32的OSCIN/OSCOUT引脚配置为GPIO后,如何保证系统时钟稳定运行?
  • 桥接模式和NAT模式
  • 2026北京婚姻纠纷找律师事务所:专业靠谱怎么选?这份参考请收好 - 产业观察网
  • 【逻辑设计】卡诺图化简实战 | 从真值表到最简电路 | 利用无关项优化设计
  • 北京翡翠变现攻略:翡翠手镯、挂件回收,专业鉴定无隐形扣费 - 奢侈品回收测评
  • AGV机器人48V锂电池选型指南:特种定制能力决定供应商质量 - 新闻快传
  • 从模拟信号到云端可视化:光敏电阻物联网项目全链路实践
  • 量子通信与6G融合:探索未来通信新维度
  • 新闻发布行业核心服务商技术盘点 多维度拆解适配逻辑 - 奔跑123
  • AntiDupl.NET:智能图片去重工具,轻松释放硬盘空间
  • 谷歌发布AI语音听写功能Rambler,集成Gboard支持语码切换,今夏率先登陆部分安卓机
  • 《Java面试85题图解版(三)》上篇:高阶架构设计篇
  • 【亲测门店】兴化市别墅品牌对比,哪家更靠谱? - 花开富贵112
  • 运维人会被 AI 淘汰吗?未来的机房,可能连值班都不需要了
  • 探索Taotoken模型广场如何帮助我根据任务选择合适的大模型
  • 2026年餐饮品牌扩张发展背景下的适配性餐饮SaaS服务商专业分析与推荐 - 产业观察网
  • 仓储物流机器人48V电池定制周期多久?哪家厂家值得合作?——以浩博电池为例 - 新闻快传
  • 视频硬字幕提取:本地化AI如何破解87种语言的视频转录难题
  • 别只盯着张雪峰的公关团队了,2026年品牌公关的胜负手其实是它
  • ESP32-S3物联网开发实战:从Bootloader到云端数据交互全流程指南
  • Photoshop图层批量导出终极指南:10倍速免费脚本让你的设计工作流飞起来
  • 鸣潮自动化工具终极指南:3步解放双手的智能助手
  • 如何让dnSpyEx完美支持.NET 8调试?完整兼容性解决方案指南
  • 为小型创业团队搭建统一的大模型开发与测试环境
  • 2026年5月辣椒酱生产厂家最新推荐:复合调味酱、食用油产品优选指南 - 海棠依旧大
  • 如何彻底卸载OneDrive:Windows 10/11系统完全清理专业指南
  • 怎么用WordPress做企业网站 专业WordPress网站建设服务商 - 麦麦唛