【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 K210IO30SCL / IO31SDAts.read()返回 status, x, y状态判断PRESS / MOVE / IDLEimage 画布绘制点 / 线 / 顶部提示栏lcd.display(img)刷新到 LCDBOOT_KEYIO16GPIO1 输入检测clear_canvas()清空画布并重绘提示栏这张流程图展示的是触摸屏画图实验的控制链路。触摸屏通过 I2C 把状态和坐标传给 K210程序根据坐标在image画布上绘制图形再通过 LCD 显示出来。BOOT_KEY 作为额外输入负责触发清屏逻辑。整个实验体现了“输入事件 - 程序处理 - 图形反馈”的交互过程。轨迹绘制的关键是“上一点”和“当前点”。如果每次触摸只画一个圆点手指快速移动时屏幕上会出现一串分散的点轨迹不够连续。代码中使用last_x、last_y和has_last_point记录上一帧触摸位置当新的触摸点到来时程序用draw_line()把两个点连接起来。这样手指移动时屏幕上就会形成连续线条。硬件设施本实验围绕 LCD 显示屏、I2C 触摸屏和 BOOT_KEY 按键展开。LCD 负责显示画布和触摸轨迹触摸屏通过 I2C 总线向 K210 提供触摸状态与坐标数据BOOT_KEY 作为清空画布的输入按钮。软件侧主要使用lcd、image、touchscreen、machine.I2C、maix.GPIO和fpioa_manager.fm分别负责显示刷新、图像绘制、触摸读取、I2C 通信、按键输入和引脚功能映射。实验接线和运行效果可以通过下面的图片建立整体印象。触摸屏负责输入坐标LCD 负责显示轨迹BOOT_KEY 用于清空画布。检查硬件时重点关注触摸屏 I2C 通信、LCD 显示是否正常以及 BOOT_KEY 是否能被程序读取。硬件 / 软件作用说明CanMV K210 开发板实验运行平台负责执行 Python 程序连接 LCD、触摸屏和 BOOT_KEYLCD 显示屏图像输出设备显示顶部提示信息、触摸点和连续轨迹I2C 触摸屏坐标输入设备通过触摸状态、X 坐标和 Y 坐标反馈手指操作BOOT_KEY清空画布按钮按下后触发clear_canvas()用于重新开始绘制IO30I2C 时钟线根据代码和注释推导对应触摸屏 SCLIO31I2C 数据线根据代码和注释推导对应触摸屏 SDAIO16按键输入引脚根据代码推导注册为 GPIO1 后读取 BOOT_KEY 状态lcd屏幕控制模块用于初始化屏幕、清屏和显示图像image图像绘制模块创建画布并绘制矩形、文字、圆点和线条touchscreen触摸屏模块初始化触摸屏并读取触摸状态和坐标machine.I2CI2C 通信模块创建触摸屏通信总线maix.GPIOGPIO 控制模块用于读取 BOOT_KEY 的按下状态fpioa_manager.fm引脚映射模块将物理引脚 IO16 注册为 GPIO1接线关系可以从代码中的 I2C 配置、按键注册和注释说明中推导出来。触摸屏通过 I2C 与 K210 通信SCL 对应 IO30SDA 对应 IO31。BOOT_KEY 使用 IO16并通过fm.register(CLEAR_KEY_PIN, CLEAR_KEY_GPIO, forceTrue)注册为 GPIO1。LCD 的具体硬件连接没有在代码中展开程序通过lcd.init()和lcd.display(img)直接调用显示接口。物理引脚 / 接口功能引脚 / 总线代码变量对应硬件说明IO30I2C SCLI2C_SCL_PIN触摸屏 SCL用作触摸屏 I2C 时钟线IO31I2C SDAI2C_SDA_PIN触摸屏 SDA用作触摸屏 I2C 数据线IO16GPIO1btn_clearBOOT_KEY按下后用于清空画布LCD 显示接口LCD 控制接口lcd、imgLCD 屏幕通过 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 固件提供lcd、image、touchscreen、machine.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_WIDTH320LCD_HEIGHT240I2C_SCL_PIN30I2C_SDA_PIN31CLEAR_KEY_PIN16CLEAR_KEY_GPIOfm.fpioa.GPIO1 TOUCH_I2C_IDI2C.I2C0 TOUCH_I2C_FREQ400000PEN_COLOR(255,255,255)TEXT_COLOR(0,255,0)POINT_COLOR(255,0,0)BG_COLOR(0,0,0)CLEAR_DEBOUNCE_MS300# # 全局状态# imgNonebtn_clearNonelast_x0last_y0last_statusts.STATUS_IDLE has_last_pointFalselast_clear_time0# # 初始化函数# definit_lcd():初始化 LCD 显示屏lcd.init()lcd.clear()definit_touchscreen():初始化触摸屏i2cI2C(TOUCH_I2C_ID,freqTOUCH_I2C_FREQ,sclI2C_SCL_PIN,sdaI2C_SDA_PIN)ts.init(i2c)# 首次使用触摸屏位置不准确时可以取消下面这一行注释进行校准# ts.calibrate()definit_clear_button():初始化清空按钮globalbtn_clear fm.register(CLEAR_KEY_PIN,CLEAR_KEY_GPIO,forceTrue)btn_clearGPIO(GPIO.GPIO1,GPIO.IN)defcreate_canvas():创建画布globalimg imgimage.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),fillTrue)img.draw_string(8,8,Touch Draw Demo,colorTEXT_COLOR,scale1)img.draw_string(180,8,BOOT: Clear,colorTEXT_COLOR,scale1)defclear_canvas():清空画布globalhas_last_point img.clear()draw_header()has_last_pointFalselcd.display(img)defdraw_touch_point(x,y):绘制当前触摸点ify30:img.draw_circle(x,y,2,colorPOINT_COLOR,fillTrue)defdraw_touch_line(x1,y1,x2,y2):绘制触摸轨迹线ify130andy230:img.draw_line((x1,y1,x2,y2),colorPEN_COLOR)# # 触摸处理函数# defis_touching(status):判断当前是否处于触摸状态returnstatusts.STATUS_PRESSorstatusts.STATUS_MOVEdefhandle_touch():读取触摸数据并绘制轨迹globallast_x,last_y,last_status,has_last_point status,x,yts.read()ifis_touching(status):ifhas_last_point:draw_touch_line(last_x,last_y,x,y)else:has_last_pointTruedraw_touch_point(x,y)last_xx last_yyelse:has_last_pointFalseifstatus!last_status:print(Touch Status:,status,X:,x,Y:,y)last_statusstatus# # 按键处理函数# defhandle_clear_button():检测 BOOT_KEY 是否被按下按下后清空画布globallast_clear_time nowtime.ticks_ms()ifbtn_clear.value()0:iftime.ticks_diff(now,last_clear_time)CLEAR_DEBOUNCE_MS:print(Canvas cleared)clear_canvas()last_clear_timenow# # 主循环# 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 识别交互等方向。触摸屏不只是显示模块而是智能硬件项目中非常重要的人机交互入口。