-- extp.lua - 触摸系统模块 --[[ @module extp @summary 触摸系统拓展库 @version 1.1.1 @date 2025.11.20 @author 江访 @usage 本文件为触摸系统拓展库,核心业务逻辑为: 1、初始化触摸设备,支持多种触摸芯片 2、处理原始触摸数据并解析为各种手势事件 3、通过统一消息接口发布触摸事件 4、提供消息发布控制功能 5、提供滑动和长按阈值配置功能 支持的触摸事件类型包括: RAW_DATA、TOUCH_DOWN、MOVE_X、MOVE_Y、SWIPE_LEFT、SWIPE_RIGHT、 SWIPE_UP、SWIPE_DOWN、SINGLE_TAP、LONG_PRESS 本文件的对外接口有5个: 1、extp.init(param):触摸设备初始化函数 2、extp.set_publish_enabled(msg_type, enabled):设置消息发布状态 3、extp.get_publish_enable(msg_type):获取消息发布状态 4、extp.set_swipe_threshold(threshold):设置滑动判定阈值 5、extp.set_long_press_threshold(threshold):设置长按判定阈值 所有触摸事件均通过sys.publish("BASE_TOUCH_EVENT", event_type, ...)发布 ]] local extp = {} -- 触摸状态变量 local state = "IDLE" -- 当前状态:IDLE(空闲), DOWN(按下), MOVE(移动) local touch_down_x = 0 -- 按下时的X坐标 local touch_down_y = 0 -- 按下时的Y坐标 local touch_down_time = 0 -- 按下时的时间戳 local swipe_threshold = 45 -- 滑动判定阈值(像素) local long_press_threshold = 500 -- 长按判定阈值(毫秒) local swipe_direction = nil -- 滑动方向(用于MOVE状态) -- 消息发布控制表,默认全部打开 local publish_control = { RAW_DATA = false, -- 原始触摸数据 TOUCH_DOWN = false, -- 按下事件 MOVE_X = false, -- 水平移动 MOVE_Y = false, -- 垂直移动 SWIPE_LEFT = false, -- 向左滑动 SWIPE_RIGHT = false, -- 向右滑动 SWIPE_UP = false, -- 向上滑动 SWIPE_DOWN = false, -- 向下滑动 SINGLE_TAP = true, -- 单击 LONG_PRESS = true -- 长按 } -- 定义支持的触摸芯片配置 local tp_configs = { cst820 = { i2c_speed = i2c.FAST, tp_model = "cst820" }, gt9157 = { i2c_speed = i2c.FAST, tp_model = "gt9157" }, cst9220 = { i2c_speed = i2c.SLOW, tp_model = "cst9220" }, jd9261t = { i2c_speed = i2c.FAST, tp_model = "jd9261t" }, gt911 = { i2c_speed = i2c.SLOW, tp_model = "gt911" }, AirLCD_1010 = { i2c_speed = i2c.SLOW, tp_model = "gt911" }, Air780EHM_LCD_4 = { i2c_speed = i2c.SLOW, tp_model = "gt911" }, AirLCD_1020 = { i2c_speed = i2c.SLOW, tp_model = "gt911" } } -- 特殊型号的默认配置 local special_tp_configs = { Air780EHM_LCD_4 = { i2c_id = 1, pin_rst = 1, pin_int = 22 }, AirLCD_1010 = { i2c_id = 0, pin_rst = 20, pin_int = gpio.WAKEUP0 }, AirLCD_1020 = { i2c_id = i2c.createSoft(0, 1), pin_rst = 28, pin_int = 7 } } --[[ 设置消息发布状态 @api extp.set_publish_enabled(msg_type, enabled) @string msg_type 消息类型,支持"ALL"或具体事件类型 @bool enabled 是否启用发布 @return bool 操作成功返回true,失败返回false @usage -- 启用单击事件 extp.set_publish_enabled("SINGLE_TAP", true) -- 禁用所有消息发布 extp.set_publish_enabled("ALL", false) ]] function extp.set_publish_enabled(msg_type, enabled) if msg_type == "ALL" then for k, _ in pairs(publish_control) do publish_control[k] = enabled end log.info("extp", "所有消息发布", enabled and "启用" or "禁用") return true elseif publish_control[msg_type] ~= nil then publish_control[msg_type] = enabled log.info("extp", msg_type, "消息发布", enabled and "启用" or "禁用") return true else log.error("extp", "未知的消息类型:", msg_type) return false end end --[[ 获取消息发布状态 @api extp.get_publish_enable(msg_type) @string msg_type 消息类型,"ALL"或具体事件类型 @return bool|table 发布状态或所有状态表 @usage -- 获取单击事件状态 local enabled = extp.get_publish_enable("SINGLE_TAP") -- 获取所有状态 local all_status = extp.get_publish_enable("ALL") ]] function extp.get_publish_enable(msg_type) if msg_type == "ALL" then -- 返回完整的发布控制表 return publish_control elseif publish_control[msg_type] ~= nil then return publish_control[msg_type] else log.error("extp", "未知的消息类型:", msg_type) return nil end end --[[ 设置滑动判定阈值 @api extp.set_swipe_threshold(threshold) @number threshold 滑动判定阈值(像素) @return bool 设置成功返回true,失败返回false @usage -- 设置滑动阈值为50像素 extp.set_swipe_threshold(50) ]] function extp.set_swipe_threshold(threshold) if type(threshold) == "number" and threshold > 0 then swipe_threshold = threshold log.info("extp", "滑动判定阈值设置为:", threshold) return true else log.error("extp", "无效的滑动阈值:", threshold) return false end end --[[ 设置长按判定阈值 @api extp.set_long_press_threshold(threshold) @number threshold 长按判定阈值(毫秒) @return bool 设置成功返回true,失败返回false @usage -- 设置长按阈值为800毫秒 extp.set_long_press_threshold(800) ]] function extp.set_long_press_threshold(threshold) if type(threshold) == "number" and threshold > 0 then long_press_threshold = threshold log.info("extp", "长按判定阈值设置为:", threshold) return true else log.error("extp", "无效的长按阈值:", threshold) return false end end -- 触摸回调函数 -- 参数: tp_device-触摸设备对象, tp_data-触摸数据 local function tp_callback(tp_device, tp_data) -- 发布原始数据 if publish_control.RAW_DATA then sys.publish("BASE_TOUCH_EVENT", "RAW_DATA", tp_device, tp_data) end if type(tp_data[1]) ~= "table" then return end local event_type = tp_data[1].event local x = tp_data[1].x local y = tp_data[1].y -- 获取高精度时间戳 local _, ms_l = mcu.ticks2(1) -- 使用系统时间戳或触摸数据中的时间戳 local timestamp = ms_l or tp_data[1].timestamp if not event_type then return end if event_type == 2 then -- 抬手事件 if state == "DOWN" or state == "MOVE" then local moveX = x - touch_down_x local moveY = y - touch_down_y if moveX < -swipe_threshold then if publish_control.SWIPE_LEFT then sys.publish("BASE_TOUCH_EVENT", "SWIPE_LEFT", moveX, 0) end elseif moveX > swipe_threshold then if publish_control.SWIPE_RIGHT then sys.publish("BASE_TOUCH_EVENT", "SWIPE_RIGHT", moveX, 0) end elseif moveY < -swipe_threshold then if publish_control.SWIPE_UP then sys.publish("BASE_TOUCH_EVENT", "SWIPE_UP", 0, moveY) end elseif moveY > swipe_threshold then if publish_control.SWIPE_DOWN then sys.publish("BASE_TOUCH_EVENT", "SWIPE_DOWN", 0, moveY) end else -- 计算按下时间 local press_time = timestamp - touch_down_time -- 判断是单击还是长按 if press_time < long_press_threshold then if publish_control.SINGLE_TAP then sys.publish("BASE_TOUCH_EVENT", "SINGLE_TAP", touch_down_x, touch_down_y) end else if publish_control.LONG_PRESS then sys.publish("BASE_TOUCH_EVENT", "LONG_PRESS", touch_down_x, touch_down_y) end end end state = "IDLE" end elseif event_type == 1 or event_type == 3 then -- 按下或移动事件 if state == "IDLE" and event_type == 1 then -- 从空闲状态接收到按下事件 state = "DOWN" touch_down_x = x touch_down_y = y touch_down_time = timestamp swipe_direction = nil -- 发布按下事件 if publish_control.TOUCH_DOWN then sys.publish("BASE_TOUCH_EVENT", "TOUCH_DOWN", x, y) end elseif state == "DOWN" and event_type == 3 then -- 在按下状态下接收到移动事件 if math.abs(x - touch_down_x) >= swipe_threshold or math.abs(y - touch_down_y) >= swipe_threshold then state = "MOVE" -- 确定滑动方向 if math.abs(x - touch_down_x) > math.abs(y - touch_down_y) then -- 水平滑动 if x - touch_down_x < 0 then swipe_direction = "LEFT" else swipe_direction = "RIGHT" end else -- 垂直滑动 if y - touch_down_y < 0 then swipe_direction = "UP" else swipe_direction = "DOWN" end end end elseif state == "MOVE" and event_type == 3 then -- 在移动状态下接收到移动事件 -- 根据滑动方向发布相应的移动事件 if swipe_direction == "LEFT" or swipe_direction == "RIGHT" then -- 水平滑动,发布MOVE_X事件 if publish_control.MOVE_X then sys.publish("BASE_TOUCH_EVENT", "MOVE_X", x - touch_down_x, 0) end else -- 垂直滑动,发布MOVE_Y事件 if publish_control.MOVE_Y then sys.publish("BASE_TOUCH_EVENT", "MOVE_Y", 0, y - touch_down_y) end end end end end --[[ 初始化触摸设备 @api extp.init(param) @table param 触摸芯片配置参数,参考库的说明及demo用法 @return bool 初始化成功返回true,失败返回false @usage -- 基础触摸初始化 extp.init({ tp_model = "gt911", i2c_id = 0, pin_rst = 20, pin_int = 21 }) -- 使用预定义配置 extp.init({tp_model = "AirLCD_1010"}) -- 带屏幕尺寸的初始化 extp.init({ tp_model = "gt911", i2c_id = 0, pin_rst = 20, pin_int = 21, w = 480, h = 320 }) ]] function extp.init(param) if type(param) ~= "table" then log.error("extp", "参数必须为表") return false end -- 检查必要参数 if not param.tp_model then log.error("extp", "缺少必要参数: tp_model") return false end local tp_model = param.tp_model -- 检查是否支持该型号 local config = tp_configs[tp_model] if not config then log.error("extp", "不支持的触摸型号:", tp_model) return false end -- 特殊型号参数处理 local final_param = {} final_param.tp_model = tp_model -- 处理特殊型号的默认配置 if special_tp_configs[tp_model] then local default_config = special_tp_configs[tp_model] if tp_model == "Air780EHM_LCD_4" then -- Air780EHM_LCD_4: 强制使用默认配置,忽略传入的其他参数 final_param.i2c_id = default_config.i2c_id final_param.pin_rst = default_config.pin_rst final_param.pin_int = default_config.pin_int log.info("extp", "Air780EHM_LCD_4使用固定配置") else -- AirLCD_1010 和 AirLCD_1020: 使用传入参数,如果未传入则使用默认配置 final_param.i2c_id = param.i2c_id or default_config.i2c_id final_param.pin_rst = param.pin_rst or default_config.pin_rst final_param.pin_int = param.pin_int or default_config.pin_int -- 记录使用的配置来源 if param.i2c_id then log.info("extp", tp_model, "使用传入的i2c_id") else log.info("extp", tp_model, "使用默认i2c_id") end if param.pin_rst then log.info("extp", tp_model, "使用传入的pin_rst") else log.info("extp", tp_model, "使用默认pin_rst") end if param.pin_int then log.info("extp", tp_model, "使用传入的pin_int") else log.info("extp", tp_model, "使用默认pin_int") end end else -- 其他型号:直接使用传入参数 final_param.i2c_id = param.i2c_id final_param.pin_rst = param.pin_rst final_param.pin_int = param.pin_int end local tp_i2c_id, tp_pin_rst, tp_pin_int = final_param.i2c_id, final_param.pin_rst or 255, final_param.pin_int -- 构建tp.init的参数表,增加w和h可选参数 local tp_init_params = { port = tp_i2c_id, pin_rst = tp_pin_rst, pin_int = tp_pin_int } -- 如果传入了w和h参数,则添加到参数表中 if param.w then tp_init_params.w = param.w log.info("extp", "设置屏幕宽度:", param.w) end if param.h then tp_init_params.h = param.h log.info("extp", "设置屏幕高度:", param.h) end -- 统一初始化流程 if type(tp_i2c_id) ~= "userdata" and config.i2c_speed ~= nil then i2c.setup(tp_i2c_id, config.i2c_speed) end -- 使用包含w和h参数的table调用tp.init local tp_device = tp.init(config.tp_model, tp_init_params, tp_callback) if tp_device ~= nil then return true end if tp_device == nil then -- 如果第一次初始化失败,尝试不带pin_rst的初始化 tp_init_params.pin_rst = 255 local tp_device = tp.init(config.tp_model, tp_init_params, tp_callback) if tp_device ~= nil then return true end end -- 若硬件触摸初始化失败,尝试PC触摸回退 log.warn("extp", "触摸初始化失败,尝试PC触摸回退") local ok_pc, dev_pc = pcall(tp.init, "pc", { port = 0 }, tp_callback) if ok_pc and dev_pc then log.info("extp", "PC触摸回退成功") return true end log.error("extp", "PC触摸回退失败") return false end return extp