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

庐山派K230开发板实战:3.1寸电容屏触摸坐标获取与手画板应用(Python API详解)

庐山派K230开发板实战:3.1寸电容屏触摸坐标获取与手画板应用(Python API详解)

最近在玩庐山派K230开发板,想给它接个屏幕做个交互应用,发现官方配套的3.1寸电容触摸屏用起来挺方便的。很多刚开始接触的朋友可能会觉得触摸屏驱动有点复杂,其实用K230的CanMV固件,几行Python代码就能搞定。今天我就带大家从零开始,一步步实现触摸坐标的读取,并最终完成一个简单的手绘板应用。

咱们这篇教程会分成几个部分:先聊聊电容屏和电阻屏有啥区别,然后详细讲解K230上操作触摸屏的Python API怎么用,接着写个程序实时打印你手指点的位置,最后把这些知识整合起来,做一个能画画、能换颜色、能清屏的趣味手绘板。

1. 硬件准备与触摸屏原理

在写代码之前,咱们得先把硬件连好,并了解一下屏幕是怎么“感觉”到你的触摸的。

1.1 连接你的屏幕

首先,确保你的庐山派K230开发板和3.1寸屏幕已经通过排线正确连接。连接口一般在开发板边缘,对准防呆口插紧就行。这是后续所有操作的基础,没连好屏幕可是啥都看不到的。

1.2 电容触摸 vs 电阻触摸:它们怎么工作?

咱们用的这块3.1寸屏幕,和现在智能手机的屏幕一样,是电容触摸屏。它和以前一些POS机、工业设备上常见的电阻屏工作原理完全不同。了解这个区别,能帮你更好地理解代码里的某些行为(比如为什么戴手套可能没反应)。

你可以把电容触摸屏想象成一层透明的、对电场特别敏感的材料。当你的手指(导体)靠近或接触屏幕时,就会轻微改变屏幕那一点的电场。屏幕内部的控制器非常灵敏,能检测到这种微小的电容变化,从而精确计算出你手指的位置。

而电阻触摸屏更像是两层可以轻微挤压的透明薄膜。当你用力按压(用指甲、触笔甚至戴手套的手指都行)时,上下两层薄膜在按压点接触,电路接通,控制器通过测量电压变化来确定位置。

为了让你更直观地看清它们的区别,我整理了一个对比表格:

特性电容触摸屏电阻触摸屏
工作原理检测人体引起的电容变化检测压力导致的薄膜接触
触摸方式手指轻触即可,支持多点触控需要施加压力,一般只支持单点
能否戴手套不能,手套绝缘可以,只要力度够
响应速度快,适合滑动等手势相对较慢
耐用性表面玻璃硬度高,但怕潮湿薄膜表面易划伤,但适应恶劣环境
精度高,适合手指操作高,适合触笔精细操作
常见用途手机、平板、消费电子产品工业控制、医疗设备、户外终端

所以,记住咱们的屏幕是电容式的,这意味着它反应快、支持多点(虽然驱动层可能有限制),但千万别戴着手套去点它。

2. 核心API:machine.TOUCH类详解

K230的CanMV固件已经把触摸屏的底层驱动封装好了,我们通过machine模块里的TOUCH类来操作它。这部分是重中之重,咱们把每个函数和参数都掰开揉碎了讲。

2.1 导入与初始化

首先,在任何使用触摸屏的脚本开头,都需要导入这个类。

from machine import TOUCH

然后,创建一个触摸屏对象。目前K230只支持一个触摸设备,所以索引号固定是0。

tp = TOUCH(0) # 实例化触摸屏设备

这里有个重要的可选参数rotate,如果你的屏幕安装方向(比如竖屏变横屏)导致触摸坐标和显示坐标对不上,可以用它来旋转触摸坐标。

# rotate 参数说明 # 0: ROTATE_0 - 不旋转 (默认) # 1: ROTATE_90 - 顺时针旋转90度 # 2: ROTATE_180 - 旋转180度 # 3: ROTATE_270 - 顺时针旋转270度(即逆时针90度) # 例如,如果屏幕是横着装的,但触摸坐标是竖着的,可以尝试: tp = TOUCH(0, rotate=1)

注意rotate参数调整的是触摸坐标系统,以适应不同的屏幕物理安装方向。如果触摸位置不准,优先检查硬件连接和屏幕驱动,其次再尝试调整这个参数。

2.2 读取触摸数据:read()方法

初始化之后,就可以在循环里不断读取触摸点了。

# 读取触摸数据,默认只读取一个点 point_data = tp.read() # 也可以指定一次读取最多多少个触摸点(比如我们的屏幕硬件支持最多5点) multi_point_data = tp.read(5)

read()方法返回一个元组,里面包含一个或多个touch_info对象。即使你指定了读取多个点,如果没有那么多手指在屏幕上,返回的元组长度也会相应变短。如果完全没有触摸,它会返回一个空元组()

2.3 触摸点信息:touch_info对象

read()方法返回的元组里,每个元素都是一个touch_info对象。这个对象有几个属性,记录了单个触摸点的详细信息:

  • x,y:这是咱们最常用的,代表触摸点在屏幕上的坐标。原点(0, 0)通常在屏幕的左上角。
  • event:理论上表示触摸事件(如按下、移动、抬起),但请注意:根据原始资料,当前固件版本中这个属性“还不支持”,内部是K230自己维护的一套机制。所以暂时别依赖这个值来判断手指是否抬起。
  • track_id:触点ID,用于在多点触摸中区分不同的手指。同样,资料显示“目前还不支持”。
  • width:触点宽度。资料特别说明,我们这块3.1寸屏的触摸芯片本身不支持获取宽度,所以这个属性当前被用来等效于track_id
  • timestamp:触摸发生的时间戳。

重要提示:关于eventtrack_id的支持情况,原文有明确说明是“请等待嘉楠后续完善”。这意味着在写应用时,我们无法直接通过API判断“手指抬起”这个动作,也无法可靠地区分多个同时触摸的手指。这是当前固件的一个限制,我们在设计手绘板逻辑时需要想办法绕过它。

2.4 资源释放:deinit()方法

当你的程序结束,不再使用触摸屏时,最好显式地释放资源。虽然Python有垃圾回收,但养成好习惯能让程序更稳健。

tp.deinit() # 释放TOUCH设备资源

3. 实战第一步:实时打印触摸坐标

理论讲完了,咱们来点实际的。先写一个简单的程序,在串口终端里实时打印你手指在屏幕上点的位置。这是调试触摸屏最基本也最有效的方法。

# 立创·庐山派-K230-CanMV开发板资料与相关扩展板软硬件资料官网全部开源 # 开发板官网:www.lckfb.com # 技术支持常驻论坛,任何技术问题欢迎随时交流学习 # 立创论坛:www.jlc-bbs.com/lckfb # 关注bilibili账号:【立创开发板】,掌握我们的最新动态! import time from machine import TOUCH # 实例化 TOUCH 设备 0 tp = TOUCH(0) print("触摸坐标读取程序已启动,请触摸屏幕...") while True: # 尝试读取最多5个触摸点(硬件上限) points = tp.read(5) # 如果返回的不是空元组,说明有触摸发生 if points != (): print("--- 检测到触摸 ---") # 使用enumerate遍历,方便给触摸点编号 for i, point in enumerate(points, start=1): print(f" 触摸点 {i}: X = {point.x}, Y = {point.y}") print() # 打印空行分隔 # 短暂延时,避免CPU占用率过高,也方便观察输出 time.sleep(0.01) # 等待10毫秒

代码逐行解析:

  1. tp = TOUCH(0):初始化触摸屏设备。
  2. while True::一个无限循环,持续监听触摸。
  3. points = tp.read(5):每次循环尝试读取最多5个触摸点数据。
  4. if points != ()::这是关键判断。如果points是空元组,说明此刻屏幕没有被触摸。只有非空时才处理。
  5. for i, point in enumerate(points, start=1)::这里用到了Python内置的enumerate函数。它非常方便,可以在遍历points元组的同时,自动给我们生成一个序号i(从1开始计数)。这样打印出来的信息更清晰。
  6. print(f" 触摸点 {i}: X = {point.x}, Y = {point.y}"):打印每个触摸点的编号和其X、Y坐标。
  7. time.sleep(0.01):每次循环后等待10毫秒。这个延时很有必要,一方面降低了CPU使用率,另一方面也让串口输出不至于刷屏太快,方便你看清。

把这段代码保存为main.py并运行,然后用手指触摸屏幕,你就能在CanMV IDE的串行终端里看到实时的坐标输出了。试着用多个手指同时触摸,看看是否能识别出多个点。

4. 综合项目:打造你的简易手绘板

能读到坐标了,咱们就来玩个大的——做一个手绘板!这个程序会显示一个画布,你可以用手指在上面画画,还能切换颜色和清空画布。

这个程序稍微长一点,但结构很清晰。我会把核心逻辑拆开讲。

4.1 程序框架与显示初始化

首先,程序需要初始化显示系统,并创建一个“画布”(图像对象)。

import time, os, gc, sys, urandom from media.display import * from media.media import * from machine import TOUCH try: # 1. 显示模式配置 DISPLAY_MODE = "LCD" # 可选 "VIRT"(IDE虚拟显示), "LCD"(3.1寸屏), "HDMI"(HDMI输出) if DISPLAY_MODE == "LCD": # 我们的3.1寸屏分辨率是800x480 DISPLAY_WIDTH = 800 DISPLAY_HEIGHT = 480 Display.init(Display.ST7701, width=DISPLAY_WIDTH, height=DISPLAY_HEIGHT, to_ide=True) # ... 其他模式(VIRT, HDMI)的初始化代码类似,详见完整代码 # 2. 初始化媒体管理器(管理图像缓冲区等资源) MediaManager.init() # 3. 创建一张空白的RGB565图像作为我们的画布 img = image.Image(DISPLAY_WIDTH, DISPLAY_HEIGHT, image.RGB565) img.clear() # 用黑色清空画布 # 4. 初始化触摸屏 tp = TOUCH(0) # 5. 定义画笔初始状态 current_color = (0, 255, 0) # 初始为绿色,(R, G, B) brush_size = 10 # 画笔粗细 # 进入主循环...

这里有几个关键点:

  • DISPLAY_MODE:非常灵活。即使你没有实物屏幕,设为"VIRT"也可以在CanMV IDE的帧缓冲区里看到画面,方便调试。有屏幕就选"LCD"
  • image.Image():创建了一个图像对象,所有画画的操作(画点、画线、画矩形)都是在这个内存中的图像上进行的。
  • current_color:用一个三元组(R, G, B)表示当前画笔颜色。

4.2 定义交互区域与功能函数

我们在画布上定义两个按钮区域,并写好对应的功能函数。

# 定义按钮在屏幕上的区域 (x, y, width, height) clear_button_area = (DISPLAY_WIDTH - 100, 0, 100, 50) # 清除按钮,在右上角 color_button_area = (0, 0, 130, 50) # 换色按钮,在左上角 def draw_clear_button(): """绘制红色的清除按钮""" img.draw_rectangle(clear_button_area[0], clear_button_area[1], clear_button_area[2], clear_button_area[3], color=(255, 0, 0), fill=True) # 画一个红色填充矩形 img.draw_string_advanced(clear_button_area[0] + 10, clear_button_area[1] + 10, 30, "清除", color=(255, 255, 255), scale=2) # 写上白色文字 def draw_color_buttons(): """绘制黄色的颜色选择按钮""" img.draw_rectangle(color_button_area[0], color_button_area[1], color_button_area[2], color_button_area[3], color=(255, 255, 0), fill=True) # 画一个黄色填充矩形 img.draw_string_advanced(color_button_area[0] + 10, color_button_area[1] + 10, 30, "随机颜色", color=(0, 0, 0), scale=2) # 写上黑色文字 def select_color(x, y): """如果触摸点在颜色按钮区域内,则随机更换画笔颜色""" global current_color cx, cy, cw, ch = color_button_area if cx <= x <= cx + cw and cy <= y <= cy + ch: # 生成随机颜色 current_color = (urandom.getrandbits(8), urandom.getrandbits(8), urandom.getrandbits(8)) print(f"颜色已切换为: {current_color}") def check_clear_button(x, y): """如果触摸点在清除按钮区域内,则清空画布""" cx, cy, cw, ch = clear_button_area if cx <= x <= cx + cw and cy <= y <= cy + ch: img.clear() print("画布已清除")

函数select_colorcheck_clear_button的逻辑是一样的:检查传入的坐标(x, y)是否落在对应按钮的矩形区域内。如果是,就执行相应的动作(换颜色或清屏)。

4.3 核心绘图逻辑与“连线”算法

这是手绘板最核心的部分。我们如何把一个个离散的触摸点变成平滑的线条?这里有个坑:由于固件暂不支持event属性,我们无法准确知道手指何时抬起。如果简单地把所有相邻读取到的点连起来,当手指快速移动或抬起再按下时,可能会把不相关的两点连在一起,画出一条飞线。

原文代码采用了一种巧妙的“距离判断”法来规避这个问题:

last_point = None # 记录上一个有效的触摸点坐标 def draw_line_between_points(last_point, current_point): """在两个触摸点之间绘制连线,通过插值让线条更平滑""" if last_point is not None: # 计算当前点和上一个点的直线距离 dx = current_point.x - last_point.x dy = current_point.y - last_point.y distance = (dx**2 + dy**2) ** 0.5 # **关键判断**:如果距离过大,认为这是两次独立的触摸(比如手指抬起后再次按下),不进行连线 if distance > 30: # 这个阈值30可以根据实际情况调整 return # 如果两点距离适中,则在它们之间插入多个小圆点,让线条看起来是连续的,而不是一串点 min_distance = 10 # 插值密度,值越小线条越平滑 if distance > min_distance: steps = int(distance // min_distance) # 计算需要插入多少个点 for i in range(1, steps + 1): # 计算插入点的坐标 new_x = last_point.x + i * dx / (steps + 1) new_y = last_point.y + i * dy / (steps + 1) # 画出插入的点 img.draw_circle(int(new_x), int(new_y), brush_size, color=current_color, thickness=3, fill=True) # 无论如何,画出当前的这个触摸点 img.draw_circle(current_point.x, current_point.y, brush_size, color=current_color, thickness=3, fill=True)

这个算法的精髓在于if distance > 30: return这一行。它假设如果两次读取的坐标点距离超过30个像素,那么它们很可能不是同一次连续滑动产生的,而是两次独立的触摸动作。因此,放弃在这两点之间画线,只绘制当前的新点作为新线条的起点。

4.4 主循环:把所有部分串起来

最后,我们在一个无限循环里,把所有功能整合起来。

while True: # 1. 读取触摸(一次只读一个点,简化处理) points = tp.read(1) # 2. 如果有触摸 if points != (): point = points[0] # 因为我们只读一个点,所以直接取第一个 # 检查是否点击了功能按钮 select_color(point.x, point.y) check_clear_button(point.x, point.y) # 核心:在上一个点和当前点之间画线(内部会做距离判断) draw_line_between_points(last_point, point) # 更新“上一个点”为当前点,供下次循环使用 last_point = point # 3. 绘制界面元素(按钮) draw_clear_button() draw_color_buttons() # 4. 将内存中的图像(img)显示到屏幕上 Display.show_image(img) # 短暂延时,控制刷新率 time.sleep_ms(1) except KeyboardInterrupt: print("程序被用户中断") except Exception as e: print(f"程序运行出错: {e}") finally: # 程序退出前,务必释放硬件资源 Display.deinit() MediaManager.deinit()

运行效果:将完整代码(即本章开头给出的长代码)运行起来后,你会看到:

  1. 屏幕左上角有一个“随机颜色”的黄色按钮,点击它,画笔颜色会随机变化。
  2. 屏幕右上角有一个红色的“清除”按钮,点击它,整个画布会被清空。
  3. 在屏幕其他区域,你就可以用手指自由作画了。画线时应该是连续平滑的,快速抬起再按下也不会出现奇怪的连线。

通过这个项目,你不仅学会了读取触摸坐标,还掌握了如何基于坐标实现交互逻辑(按钮),并处理了固件限制下的连续绘图问题。这就是嵌入式开发中典型的“发现问题-分析原因-设计解决方案”的过程。希望这篇教程能帮你打开K230触摸屏开发的大门。

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

相关文章:

  • 重新定义实时视觉交互:基于MediaPipe的TouchDesigner零配置解决方案
  • 直流有刷电机H桥控制实战:从原理图到PWM调速(附DR70x驱动芯片详解)
  • 黑群晖+Docker打造怀旧游戏中心:超级玛丽服务器搭建全攻略(附远程访问技巧)
  • 【昇腾实战】MindIE推理框架部署DeepSeek-R1模型全流程解析
  • ESP32-S2硬件密码加速器:RSA与HMAC工程实践指南
  • DDR5 SDRAM可编程前导码与后导码的优化配置与应用场景解析
  • GTE-Chinese-Large语义搜索实战:绕过modelscope pipeline的高性能方案
  • 2026年降AI工具第一梯队出炉,毕业生赶紧收藏 - 还在做实验的师兄
  • ANT+协议在运动健康领域的独特优势:低功耗与多设备互联如何实现?【无线通信小百科】
  • day 41
  • 电动车电源改造指南:用AH7690实现60V电池组降压5V供电(效率92%实测)
  • 立创EDA实战:从原理图到3D打印,打造触摸感应温馨小夜灯
  • Ubuntu下高效配置pip镜像源的两种方法
  • .NET 9云原生升级路径图(含迁移成本测算表+兼容性矩阵):企业级项目零停机迁移的最后窗口期
  • CHORD-X效果实测:生成百页深度行业研究报告的质量与效率评估
  • 翻译大法降AI教程:3步操作把AI率降到15%以下 - 还在做实验的师兄
  • ESP32-C61 AT命令全栈实战:Wi-Fi透传、mDNS、BLE GATT与鲁棒性设计
  • 多语言翻译模型实战:HY-MT1.8B+Chainlit搭建翻译Web界面
  • 视频修复技术全解析:从原理到实战的媒体文件恢复方案
  • AI赋能:让快马智能生成与你项目技术栈精准匹配的安装教程
  • Local Moondream2一键部署教程:VSCode开发环境配置
  • 2026年SCI降AI率用什么好?理工科同学亲测这3款 - 还在做实验的师兄
  • SystemVerilog中local::的5个实际应用场景解析(附代码示例)
  • maven介绍_1
  • Qwen3-TTS-12Hz-1.7B-CustomVoice在播客制作中的应用:自动化内容生成方案
  • 1. 基于ESP32-S3的1.8寸彩色触摸屏(ST7735S+XPT2046)驱动移植与画板应用实战
  • 效率提升:用快马生成mac一键安装配置OpenClaw的自动化脚本
  • 避坑指南:海康威视Linux SDK在Ubuntu 22.04的5个常见编译错误及解决方法
  • 实时手机检测-通用模型常见问题解决:部署与使用全攻略
  • 通义千问1.5-1.8B-Chat-GPTQ-Int4代码助手实战:AI编程辅助工具开发