衡山派开发板屏幕选购与驱动适配实战RGB/LVDS接口与GT911/FT6336触摸芯片详解最近在玩衡山派开发板很多朋友问我怎么给它配屏幕还有触摸驱动怎么搞。确实这块板子功能挺全但屏幕选型和驱动适配对新手来说是个坎儿。今天我就结合自己的踩坑经验手把手带你搞定衡山派开发板的屏幕选购和驱动适配。咱们主要解决两个问题一是怎么选屏幕RGB接口还是LVDS接口二是如果屏幕自带的触摸芯片不是默认的GT911比如是FT6336该怎么移植驱动。文章会从硬件选购讲到软件配置最后还有完整的代码移植实战。1. 屏幕选购指南RGB还是LVDS衡山派开发板的屏幕接口是40Pin的支持两种主流接口RGB-40Pin和LVDS-40Pin。官方默认适配的是4.3英寸、分辨率为480*272的RGB565屏幕触摸芯片是GT911。注意官方和屏幕商家没有合作关系不处理任何关于屏幕的售后问题。选购时请自行与商家沟通。1.1 RGB屏幕选购RGB接口是直接驱动屏幕的方式时序控制相对直观。如果你刚开始玩或者项目对成本敏感RGB屏幕是个不错的选择。官方推荐的RGB屏幕购买链接有两个链接1点击跳转链接2点击跳转提示链接2的屏幕最便宜是电容触摸触摸芯片也是GT911的性价比很高。1.2 LVDS屏幕选购LVDS低压差分信号接口传输距离更远抗干扰能力更强常用于大尺寸或高分辨率的屏幕。如果你需要7寸、10寸甚至更大的屏LVDS是更好的选择。这里有一个LVDS屏幕的购买链接注意此链接的屏幕未经实际验证LVDS屏幕链接未验证点击跳转官方文档中的LVDS示例图片使用的不是这个商家的屏幕是原厂的屏幕所以这个商家的LVDS屏幕没有验证过但问题应该不大。选购小结新手入门、小尺寸4.3寸直接选链接2的RGB屏幕便宜又好用。需要大尺寸、高分辨率考虑LVDS屏幕但驱动配置可能稍复杂。2. 屏幕驱动配置选好屏幕硬件接上板子后下一步就是软件配置了。大部分配置工作都在menuconfig菜单中完成。这里有两个非常重要的官方参考文档建议你先通读一遍了解配置框架RTOS-SDK BringUP屏幕配置Display使用指南屏幕适配屏幕适配大部分只需要在menuconfig菜单中进行相关的参数配置即可。简单来说你需要进入scons --menuconfig找到显示相关的配置项根据你的屏幕分辨率、时序参数进行设置。比如对于默认的4.3寸480*272 RGB屏幕就需要配置对应的像素时钟、行同步、场同步等参数。这些参数通常可以从屏幕的数据手册Datasheet中找到。3. 触摸驱动适配以FT6336为例这是本文的重点。开发板正面的6Pin FPC接口是电容触摸接口。官方强烈推荐直接使用GT911触摸芯片因为对该芯片的适配最完善。如果你想使用其他触摸芯片比如FT6336就需要自己移植驱动。别担心跟着下面的步骤走其实并不难。官方触摸驱动适配参考文档 RTOS-SDK 外设移植CTP调试指南下面我以FT6336触摸芯片为例详细说明一下触摸驱动添加的步骤。3.1 创建驱动源码文件首先我们需要在SDK的指定目录下创建驱动文件。找到SDK中的触摸驱动目录bsp\peripheral\touch\在该目录下创建一个名为ft6336的文件夹。在ft6336文件夹内再创建两个子文件夹inc(存放头文件) 和src(存放源文件)。最终的文件结构如下bsp\peripheral\touch\ └── ft6336/ ├── inc/ │ └── ft6336.h └── src/ └── ft6336.c3.2 编写驱动代码接下来我们分别编写ft6336.c和ft6336.h。ft6336.h 头文件这个文件主要定义芯片的I2C地址、关键寄存器地址以及一些配置宏。#ifndef __FT6336_H__ #define __FT6336_H__ #include drivers/touch.h #include aic_hal_gpio.h #include aic_drv_gpio.h // 器件地址 #define FT_SLAVE_ADDR 0X38 // 部分寄存器定义 #define FT_DEVIDE_MODE 0x00 // FT6336 模式控制寄存器 #define FT_REG_NUM_FINGER 0x02 // 触摸状态寄存器 #define FT_TP1_REG 0X03 // 第一个触摸点数据地址 #define FT_TP2_REG 0X09 // 第二个触摸点数据地址 // 芯片和固件信息寄存器 #define FT_ID_G_CIPHER_MID 0x9F // 芯片代号中字节 #define FT_ID_G_CIPHER_LOW 0xA0 // 芯片代号低字节 #define FT_ID_G_LIB_VERSION_H 0xA1 // LIB 版本高8位 #define FT_ID_G_LIB_VERSION_L 0xA2 // LIB 版本低8位 #define FT_ID_G_CIPHER_HIGH 0xA3 // 芯片代号高字节 #define FT_ID_G_MODE 0xA4 // FT6636 中断模式控制寄存器 #define FT_ID_G_FIRMWARE_VERSION 0xA6 // 固件版本寄存器 #define FT_ID_G_FOCALTECH_ID 0xA8 // VENDOR ID #define FT_ID_G_THGROUP 0x80 // 触摸有效值设置寄存器 #define FT_ID_G_PERIODACTIVE 0x88 // 激活状态周期设置寄存器 // 引脚和通道配置这些宏通常在板级配置中定义 #define FT6336_I2C_CHAN AIC_TOUCH_PANEL_I2C_CHAN #define FT6336_RST_PIN AIC_TOUCH_PANEL_RST_PIN #define FT6336_INT_PIN AIC_TOUCH_PANEL_INT_PIN #define AIC_TOUCH_PANEL_FT7411_X_RANGE 480 #define AIC_TOUCH_PANEL_FT7411_Y_RANGE 800 #endif /* __FT6336_H__ */ft6336.c 源文件这是驱动的核心实现了I2C读写、芯片初始化、数据读取等操作。代码较长但关键函数我都加了注释。/* 版权和版本信息省略 */ #include rtthread.h #include rtdevice.h #include touch.h #include ft6336.h #include rtdbg.h #define DBG_TAG ft6336 #define DBG_LVL DBG_INFO // 设备结构体定义其他TP也需要定义这个变量 static struct rt_i2c_client ft6336_client; /** * 写寄存器 * param bus I2C总线设备指针 * param reg 寄存器地址 * param value 要写入的值 * return RT_EOK 成功-RT_ERROR 失败 */ static int ft6336_write_reg(struct rt_i2c_bus_device *bus, rt_uint8_t reg, rt_uint8_t value) { struct rt_i2c_msg msg {0}; rt_uint8_t buf[2]; buf[0] reg; buf[1] value; msg.addr FT_SLAVE_ADDR; msg.flags RT_I2C_WR; msg.buf buf; msg.len 2; if (rt_i2c_transfer(bus, msg, 1) ! 1) { LOG_E([ft6336_write_reg]ft6336 write register error); return -RT_ERROR; } return RT_EOK; } /** * 读寄存器 * param bus I2C总线设备指针 * param reg 要读取的寄存器地址 * param buf 用于保存读取的数据 * param len 读取长度 * return RT_EOK 成功-RT_ERROR 失败 */ static int ft6336_read_reg(struct rt_i2c_bus_device *bus, rt_uint8_t reg, rt_uint8_t *buf, rt_uint8_t len) { struct rt_i2c_msg msg {0}; rt_uint8_t temp_reg; temp_reg reg; msg.addr FT_SLAVE_ADDR; msg.flags RT_I2C_WR; msg.buf temp_reg; msg.len 1; if (rt_i2c_transfer(bus, msg, 1) ! 1) { LOG_E([ft6336_read_reg]ft6336 write register error); return -RT_ERROR; } msg.addr FT_SLAVE_ADDR; msg.flags RT_I2C_RD; msg.buf buf; msg.len len; if (rt_i2c_transfer(bus, msg, 1) ! 1) { LOG_E([ft6336_read_reg]ft6336 read register error); return -RT_ERROR; } return RT_EOK; } /** * 获取芯片ID和固件版本信息 */ static void ft6336_get_id(void) { uint8_t ID 0; uint8_t LIB_Version[2] {0}; uint8_t Firmware_Version 0; int ret 0; // 读ID ret ft6336_read_reg(ft6336_client.bus, FT_ID_G_FOCALTECH_ID, ID, 1); if(RT_EOK ! ret) { LOG_E(FT6336 Read ID Failed !!); } else { LOG_I(FT6636 Read ID 0x%x, ID); } // LIB版本 ret ft6336_read_reg(ft6336_client.bus, FT_ID_G_LIB_VERSION_H, LIB_Version[0], 1); ret ft6336_read_reg(ft6336_client.bus, FT_ID_G_LIB_VERSION_L, LIB_Version[1], 1); if(RT_EOK ! ret) { LOG_E(FT6336 Read LIB Version info Failed !!); } else { LOG_I(FT6636 Read LIB Version info 0x%x, LIB_Version[0]8 | LIB_Version[1]); } // 固件版本号 ret ft6336_read_reg(ft6336_client.bus, FT_ID_G_FIRMWARE_VERSION, Firmware_Version, 1); if(RT_EOK ! ret) { LOG_E(FT6336 Read Firmware Version Failed !!); } else { LOG_I(FT6636 Read Firmware Version 0x%x, Firmware_Version); } } /** * ft6336获取点击手指数量 */ static rt_err_t ft6336_get_info(struct rt_i2c_client *dev, struct rt_touch_info *info) { info-point_num 1; // FT6336支持多点这里示例只配置为单点 return RT_EOK; } /** * ft6336芯片配置 */ static void ft6336_init(struct rt_i2c_bus_device *bus, rt_base_t pin) { // 复位操作 rt_pin_mode(pin, PIN_MODE_OUTPUT); rt_pin_write(pin, 0); rt_thread_mdelay(50); rt_pin_write(pin, 1); rt_thread_mdelay(100); // 配置芯片工作模式和参数 ft6336_write_reg(bus, FT_DEVIDE_MODE, 0); ft6336_write_reg(bus, FT_ID_G_THGROUP, 12); // 触摸阈值 ft6336_write_reg(bus, FT_ID_G_PERIODACTIVE, 12); // 报告速率 rt_thread_mdelay(500); } /** * 读取触摸数据 * param touch 触摸设备 * param buf 需要被赋值的地 * param read_num 传入读取的手指数 * return 触摸手指的数量 */ static rt_size_t ft6336_read_point(struct rt_touch_device *touch, void *buf, rt_size_t read_num) { rt_uint8_t data[4]; uint8_t point_num 0; static rt_uint8_t s_tp_down 0; static uint16_t x_save, y_save; static rt_uint8_t s_count 0; struct rt_touch_data *temp_data; temp_data (struct rt_touch_data *)buf; temp_data-event RT_TOUCH_EVENT_NONE; temp_data-timestamp rt_touch_get_ts(); // 读取第一个触摸点的数据4个字节 ft6336_read_reg(ft6336_client.bus, FT_TP1_REG, data, 4); // 解析坐标数据 temp_data-x_coordinate ((data[0]0X0F)8)data[1]; temp_data-y_coordinate ((data[2]0X0F)8)data[3]; temp_data-timestamp rt_touch_get_ts(); // 判断触摸事件按下、移动、抬起 if ((data[0]0XC0)!0X80) /* up event */ { if (s_tp_down 1) { if (s_count 2) { s_count 0; s_tp_down 0; temp_data-event RT_TOUCH_EVENT_UP; temp_data-timestamp rt_touch_get_ts(); temp_data-x_coordinate x_save; temp_data-y_coordinate y_save; } } return read_num; } s_count 0; if (s_tp_down 0) /* down event */ { s_tp_down 1; temp_data-event RT_TOUCH_EVENT_DOWN; } else /* move event */ { temp_data-event RT_TOUCH_EVENT_MOVE; } x_save temp_data-x_coordinate; y_save temp_data-y_coordinate; return read_num; } /** * ft6336触摸设备控制 * param touch 触摸设备 * param cmd 命令 * param arg 需要被复制的地址 * return RT_EOK成功 */ static rt_err_t ft6336_control(struct rt_touch_device *touch, int cmd, void *arg) { switch(cmd) { case RT_TOUCH_CTRL_GET_ID: ft6336_get_id(); break; case RT_TOUCH_CTRL_GET_INFO: // 必须实现这个命令的处理 ft6336_get_info(ft6336_client, (struct rt_touch_info *)arg); break; case RT_TOUCH_CTRL_SET_MODE: break; case RT_TOUCH_CTRL_SET_X_RANGE: break; case RT_TOUCH_CTRL_SET_Y_RANGE: break; case RT_TOUCH_CTRL_SET_X_TO_Y: break; case RT_TOUCH_CTRL_DISABLE_INT: break; case RT_TOUCH_CTRL_ENABLE_INT: break; default: break; } return RT_EOK; } // 设备接口需要用到的结构体 static const struct rt_touch_ops ft6336_touch_ops { .touch_readpoint ft6336_read_point, .touch_control ft6336_control, }; /** * ft6336触摸设备注册 * param name 触摸设备名 * param cfg 相关的配置 * param pin 复位引脚 * return RT_EOK成功 */ static int rt_hw_ft6336_init(const char *name, struct rt_touch_config *cfg, rt_base_t pin) { struct rt_touch_device *touch_device RT_NULL; touch_device (rt_touch_t)rt_calloc(1, sizeof(struct rt_touch_device)); if (touch_device RT_NULL) { return -RT_ENOMEM; } // 查找并打开I2C总线 ft6336_client.bus rt_i2c_bus_device_find(cfg-dev_name); if (ft6336_client.bus RT_NULL) { return -RT_EIO; } if (rt_device_open((rt_device_t)ft6336_client.bus, RT_DEVICE_FLAG_RDWR) ! RT_EOK) { LOG_E(open %s device failed, cfg-dev_name); return -RT_ERROR; } ft6336_client.client_addr FT_SLAVE_ADDR; // 初始化芯片 ft6336_init(ft6336_client.bus, pin); // 配置触摸设备信息 rt_memcpy(touch_device-config, cfg, sizeof(struct rt_touch_config)); touch_device-info.point_num 1; touch_device-info.type RT_TOUCH_TYPE_CAPACITANCE; touch_device-info.vendor RT_TOUCH_VENDOR_FT; touch_device-info.range_x AIC_TOUCH_PANEL_FT7411_X_RANGE; touch_device-info.range_y AIC_TOUCH_PANEL_FT7411_Y_RANGE; touch_device-ops ft6336_touch_ops; // 注册触摸设备 if (RT_EOK ! rt_hw_touch_register(touch_device, name, RT_DEVICE_FLAG_INT_RX, RT_NULL)) { LOG_E(touch device ft6336 init failed !!!); return -RT_ERROR; } LOG_I(touch device ft6336 init success); return RT_EOK; } /** * GPIO引脚初始化 */ static int rt_ft6336_gpio_cfg() { unsigned int g, p; long pin; // RST 引脚配置为输入通常由上拉电阻控制驱动内部会控制 pin drv_pin_get(AIC_TOUCH_PANEL_RST_PIN); g GPIO_GROUP(pin); p GPIO_GROUP_PIN(pin); hal_gpio_direction_input(g, p); // INT 中断引脚配置为输入 pin drv_pin_get(AIC_TOUCH_PANEL_INT_PIN); g GPIO_GROUP(pin); p GPIO_GROUP_PIN(pin); hal_gpio_direction_input(g, p); hal_gpio_set_irq_mode(g, p, 0); return 0; } /** * FT6336设备端口初始化 */ static int rt_hw_ft6336_port(void) { struct rt_touch_config cfg; rt_base_t rst_pin; rt_ft6336_gpio_cfg(); rst_pin rt_pin_get(AIC_TOUCH_PANEL_RST_PIN); cfg.dev_name AIC_TOUCH_PANEL_I2C_CHAN; // I2C通道名如i2c2 cfg.irq_pin.pin rt_pin_get(AIC_TOUCH_PANEL_INT_PIN); cfg.irq_pin.mode PIN_MODE_INPUT; cfg.user_data rst_pin; rt_hw_ft6336_init(ft6336, cfg, rst_pin); // 读取ID测试 ft6336_get_id(); return 0; } // 自动初始化函数 rt_hw_ft6336_port INIT_DEVICE_EXPORT(rt_hw_ft6336_port);特别需要注意的函数ft6336_read_point形参必须和上面代码一致且返回值需要读取当前触摸屏幕的手指数量如果返回值一直是0则触摸就会没有响应。rt_ft6336_gpio_cfg可以直接复用里面用的都是公共参数。ft6336_control必须实现RT_TOUCH_CTRL_GET_INFO这个选项相关的处理rt_hw_ft6336_port可以直接参考上面代码基本可以复用。且必须要自动初始化INIT_DEVICE_EXPORT。3.3 添加编译路径驱动代码写好了得告诉编译系统去哪里找这些文件。编辑\luban-lite\bsp\peripheral\touch\SConscript文件在合适的位置添加以下内容if GetDepend(AIC_TOUCH_PANEL_FT6336): CPPPATH.append(cwd /ft6336/inc) src Glob(ft6336/src/*.c)这段代码的意思是如果我们在menuconfig中选择了AIC_TOUCH_PANEL_FT6336这个配置项就把ft6336/inc目录加入头文件搜索路径并把ft6336/src/下的所有.c文件加入编译。3.4 添加menuconfig配置项为了让menuconfig里出现 FT6336 的选项我们需要修改 Kconfig 文件。在\luban-lite\bsp\peripheral\touch\Kconfig文件中添加两个语句添加 FT6336 的布尔选择项config AIC_TOUCH_PANEL_FT6336 bool FT6336在默认设备名配置处根据选择项来设置默认名default ft6336 if AIC_TOUCH_PANEL_FT63363.5 进行menuconfig配置现在一切准备就绪我们可以进行图形化配置了。在SDK根目录下执行命令scons --menuconfig进入配置页面后按照以下路径进行配置Board options --- [*] Using I2c2 I2c2 Parameter --- [ ] Using I2C2 10-bit Addr(default 7-bit addr) (400000) Setting I2C2 Transfer Speed # 设置I2C速度400KHz是常用值 [ ] Using I2C2 Slave Mode(default master mode) Rt-Thread options --- RT-Thread Components --- Device Drivers --- [*] Using Touch device drivers [*] touch irq use pin irq # 启用触摸中断 Drivers options --- Peripheral --- Touch Panel Support --- Select touch device (CTP) --- (X) CTP # 选择电容触摸 Select CTP device (FT6336) --- (X) FT6336 # 选择我们刚添加的FT6336驱动 (i2c2) Touch using I2C channel index # 触摸使用的I2C通道如i2c2 (PA.10) Touch reset pin # 触摸复位引脚根据原理图填写 (PA.11) Touch irq pin # 触摸中断引脚根据原理图填写 (480) Touch x coordinate range # 因为目前移植的屏幕是480*800的竖屏所以x和y的触摸范围设定为这样 (800) Touch y coordinate range配置完成后保存退出。3.6 编译与验证最后执行编译命令scons编译成功后将固件烧录到开发板。上电后如果一切正常你应该能在串口日志中看到类似touch device ft6336 init success和芯片ID信息输出的提示。编译然后烧录镜像就能看到相关的屏幕触摸功能正常工作了。至此从屏幕选购到非默认触摸芯片FT6336的驱动移植整个流程就完成了。虽然步骤看起来多但一步步跟着做其实都是体力活。最关键的还是理解驱动框架以及menuconfig的配置逻辑。希望这篇教程能帮你少走弯路。
衡山派开发板屏幕选购与驱动适配实战:RGB/LVDS接口与GT911/FT6336触摸芯片详解
衡山派开发板屏幕选购与驱动适配实战RGB/LVDS接口与GT911/FT6336触摸芯片详解最近在玩衡山派开发板很多朋友问我怎么给它配屏幕还有触摸驱动怎么搞。确实这块板子功能挺全但屏幕选型和驱动适配对新手来说是个坎儿。今天我就结合自己的踩坑经验手把手带你搞定衡山派开发板的屏幕选购和驱动适配。咱们主要解决两个问题一是怎么选屏幕RGB接口还是LVDS接口二是如果屏幕自带的触摸芯片不是默认的GT911比如是FT6336该怎么移植驱动。文章会从硬件选购讲到软件配置最后还有完整的代码移植实战。1. 屏幕选购指南RGB还是LVDS衡山派开发板的屏幕接口是40Pin的支持两种主流接口RGB-40Pin和LVDS-40Pin。官方默认适配的是4.3英寸、分辨率为480*272的RGB565屏幕触摸芯片是GT911。注意官方和屏幕商家没有合作关系不处理任何关于屏幕的售后问题。选购时请自行与商家沟通。1.1 RGB屏幕选购RGB接口是直接驱动屏幕的方式时序控制相对直观。如果你刚开始玩或者项目对成本敏感RGB屏幕是个不错的选择。官方推荐的RGB屏幕购买链接有两个链接1点击跳转链接2点击跳转提示链接2的屏幕最便宜是电容触摸触摸芯片也是GT911的性价比很高。1.2 LVDS屏幕选购LVDS低压差分信号接口传输距离更远抗干扰能力更强常用于大尺寸或高分辨率的屏幕。如果你需要7寸、10寸甚至更大的屏LVDS是更好的选择。这里有一个LVDS屏幕的购买链接注意此链接的屏幕未经实际验证LVDS屏幕链接未验证点击跳转官方文档中的LVDS示例图片使用的不是这个商家的屏幕是原厂的屏幕所以这个商家的LVDS屏幕没有验证过但问题应该不大。选购小结新手入门、小尺寸4.3寸直接选链接2的RGB屏幕便宜又好用。需要大尺寸、高分辨率考虑LVDS屏幕但驱动配置可能稍复杂。2. 屏幕驱动配置选好屏幕硬件接上板子后下一步就是软件配置了。大部分配置工作都在menuconfig菜单中完成。这里有两个非常重要的官方参考文档建议你先通读一遍了解配置框架RTOS-SDK BringUP屏幕配置Display使用指南屏幕适配屏幕适配大部分只需要在menuconfig菜单中进行相关的参数配置即可。简单来说你需要进入scons --menuconfig找到显示相关的配置项根据你的屏幕分辨率、时序参数进行设置。比如对于默认的4.3寸480*272 RGB屏幕就需要配置对应的像素时钟、行同步、场同步等参数。这些参数通常可以从屏幕的数据手册Datasheet中找到。3. 触摸驱动适配以FT6336为例这是本文的重点。开发板正面的6Pin FPC接口是电容触摸接口。官方强烈推荐直接使用GT911触摸芯片因为对该芯片的适配最完善。如果你想使用其他触摸芯片比如FT6336就需要自己移植驱动。别担心跟着下面的步骤走其实并不难。官方触摸驱动适配参考文档 RTOS-SDK 外设移植CTP调试指南下面我以FT6336触摸芯片为例详细说明一下触摸驱动添加的步骤。3.1 创建驱动源码文件首先我们需要在SDK的指定目录下创建驱动文件。找到SDK中的触摸驱动目录bsp\peripheral\touch\在该目录下创建一个名为ft6336的文件夹。在ft6336文件夹内再创建两个子文件夹inc(存放头文件) 和src(存放源文件)。最终的文件结构如下bsp\peripheral\touch\ └── ft6336/ ├── inc/ │ └── ft6336.h └── src/ └── ft6336.c3.2 编写驱动代码接下来我们分别编写ft6336.c和ft6336.h。ft6336.h 头文件这个文件主要定义芯片的I2C地址、关键寄存器地址以及一些配置宏。#ifndef __FT6336_H__ #define __FT6336_H__ #include drivers/touch.h #include aic_hal_gpio.h #include aic_drv_gpio.h // 器件地址 #define FT_SLAVE_ADDR 0X38 // 部分寄存器定义 #define FT_DEVIDE_MODE 0x00 // FT6336 模式控制寄存器 #define FT_REG_NUM_FINGER 0x02 // 触摸状态寄存器 #define FT_TP1_REG 0X03 // 第一个触摸点数据地址 #define FT_TP2_REG 0X09 // 第二个触摸点数据地址 // 芯片和固件信息寄存器 #define FT_ID_G_CIPHER_MID 0x9F // 芯片代号中字节 #define FT_ID_G_CIPHER_LOW 0xA0 // 芯片代号低字节 #define FT_ID_G_LIB_VERSION_H 0xA1 // LIB 版本高8位 #define FT_ID_G_LIB_VERSION_L 0xA2 // LIB 版本低8位 #define FT_ID_G_CIPHER_HIGH 0xA3 // 芯片代号高字节 #define FT_ID_G_MODE 0xA4 // FT6636 中断模式控制寄存器 #define FT_ID_G_FIRMWARE_VERSION 0xA6 // 固件版本寄存器 #define FT_ID_G_FOCALTECH_ID 0xA8 // VENDOR ID #define FT_ID_G_THGROUP 0x80 // 触摸有效值设置寄存器 #define FT_ID_G_PERIODACTIVE 0x88 // 激活状态周期设置寄存器 // 引脚和通道配置这些宏通常在板级配置中定义 #define FT6336_I2C_CHAN AIC_TOUCH_PANEL_I2C_CHAN #define FT6336_RST_PIN AIC_TOUCH_PANEL_RST_PIN #define FT6336_INT_PIN AIC_TOUCH_PANEL_INT_PIN #define AIC_TOUCH_PANEL_FT7411_X_RANGE 480 #define AIC_TOUCH_PANEL_FT7411_Y_RANGE 800 #endif /* __FT6336_H__ */ft6336.c 源文件这是驱动的核心实现了I2C读写、芯片初始化、数据读取等操作。代码较长但关键函数我都加了注释。/* 版权和版本信息省略 */ #include rtthread.h #include rtdevice.h #include touch.h #include ft6336.h #include rtdbg.h #define DBG_TAG ft6336 #define DBG_LVL DBG_INFO // 设备结构体定义其他TP也需要定义这个变量 static struct rt_i2c_client ft6336_client; /** * 写寄存器 * param bus I2C总线设备指针 * param reg 寄存器地址 * param value 要写入的值 * return RT_EOK 成功-RT_ERROR 失败 */ static int ft6336_write_reg(struct rt_i2c_bus_device *bus, rt_uint8_t reg, rt_uint8_t value) { struct rt_i2c_msg msg {0}; rt_uint8_t buf[2]; buf[0] reg; buf[1] value; msg.addr FT_SLAVE_ADDR; msg.flags RT_I2C_WR; msg.buf buf; msg.len 2; if (rt_i2c_transfer(bus, msg, 1) ! 1) { LOG_E([ft6336_write_reg]ft6336 write register error); return -RT_ERROR; } return RT_EOK; } /** * 读寄存器 * param bus I2C总线设备指针 * param reg 要读取的寄存器地址 * param buf 用于保存读取的数据 * param len 读取长度 * return RT_EOK 成功-RT_ERROR 失败 */ static int ft6336_read_reg(struct rt_i2c_bus_device *bus, rt_uint8_t reg, rt_uint8_t *buf, rt_uint8_t len) { struct rt_i2c_msg msg {0}; rt_uint8_t temp_reg; temp_reg reg; msg.addr FT_SLAVE_ADDR; msg.flags RT_I2C_WR; msg.buf temp_reg; msg.len 1; if (rt_i2c_transfer(bus, msg, 1) ! 1) { LOG_E([ft6336_read_reg]ft6336 write register error); return -RT_ERROR; } msg.addr FT_SLAVE_ADDR; msg.flags RT_I2C_RD; msg.buf buf; msg.len len; if (rt_i2c_transfer(bus, msg, 1) ! 1) { LOG_E([ft6336_read_reg]ft6336 read register error); return -RT_ERROR; } return RT_EOK; } /** * 获取芯片ID和固件版本信息 */ static void ft6336_get_id(void) { uint8_t ID 0; uint8_t LIB_Version[2] {0}; uint8_t Firmware_Version 0; int ret 0; // 读ID ret ft6336_read_reg(ft6336_client.bus, FT_ID_G_FOCALTECH_ID, ID, 1); if(RT_EOK ! ret) { LOG_E(FT6336 Read ID Failed !!); } else { LOG_I(FT6636 Read ID 0x%x, ID); } // LIB版本 ret ft6336_read_reg(ft6336_client.bus, FT_ID_G_LIB_VERSION_H, LIB_Version[0], 1); ret ft6336_read_reg(ft6336_client.bus, FT_ID_G_LIB_VERSION_L, LIB_Version[1], 1); if(RT_EOK ! ret) { LOG_E(FT6336 Read LIB Version info Failed !!); } else { LOG_I(FT6636 Read LIB Version info 0x%x, LIB_Version[0]8 | LIB_Version[1]); } // 固件版本号 ret ft6336_read_reg(ft6336_client.bus, FT_ID_G_FIRMWARE_VERSION, Firmware_Version, 1); if(RT_EOK ! ret) { LOG_E(FT6336 Read Firmware Version Failed !!); } else { LOG_I(FT6636 Read Firmware Version 0x%x, Firmware_Version); } } /** * ft6336获取点击手指数量 */ static rt_err_t ft6336_get_info(struct rt_i2c_client *dev, struct rt_touch_info *info) { info-point_num 1; // FT6336支持多点这里示例只配置为单点 return RT_EOK; } /** * ft6336芯片配置 */ static void ft6336_init(struct rt_i2c_bus_device *bus, rt_base_t pin) { // 复位操作 rt_pin_mode(pin, PIN_MODE_OUTPUT); rt_pin_write(pin, 0); rt_thread_mdelay(50); rt_pin_write(pin, 1); rt_thread_mdelay(100); // 配置芯片工作模式和参数 ft6336_write_reg(bus, FT_DEVIDE_MODE, 0); ft6336_write_reg(bus, FT_ID_G_THGROUP, 12); // 触摸阈值 ft6336_write_reg(bus, FT_ID_G_PERIODACTIVE, 12); // 报告速率 rt_thread_mdelay(500); } /** * 读取触摸数据 * param touch 触摸设备 * param buf 需要被赋值的地 * param read_num 传入读取的手指数 * return 触摸手指的数量 */ static rt_size_t ft6336_read_point(struct rt_touch_device *touch, void *buf, rt_size_t read_num) { rt_uint8_t data[4]; uint8_t point_num 0; static rt_uint8_t s_tp_down 0; static uint16_t x_save, y_save; static rt_uint8_t s_count 0; struct rt_touch_data *temp_data; temp_data (struct rt_touch_data *)buf; temp_data-event RT_TOUCH_EVENT_NONE; temp_data-timestamp rt_touch_get_ts(); // 读取第一个触摸点的数据4个字节 ft6336_read_reg(ft6336_client.bus, FT_TP1_REG, data, 4); // 解析坐标数据 temp_data-x_coordinate ((data[0]0X0F)8)data[1]; temp_data-y_coordinate ((data[2]0X0F)8)data[3]; temp_data-timestamp rt_touch_get_ts(); // 判断触摸事件按下、移动、抬起 if ((data[0]0XC0)!0X80) /* up event */ { if (s_tp_down 1) { if (s_count 2) { s_count 0; s_tp_down 0; temp_data-event RT_TOUCH_EVENT_UP; temp_data-timestamp rt_touch_get_ts(); temp_data-x_coordinate x_save; temp_data-y_coordinate y_save; } } return read_num; } s_count 0; if (s_tp_down 0) /* down event */ { s_tp_down 1; temp_data-event RT_TOUCH_EVENT_DOWN; } else /* move event */ { temp_data-event RT_TOUCH_EVENT_MOVE; } x_save temp_data-x_coordinate; y_save temp_data-y_coordinate; return read_num; } /** * ft6336触摸设备控制 * param touch 触摸设备 * param cmd 命令 * param arg 需要被复制的地址 * return RT_EOK成功 */ static rt_err_t ft6336_control(struct rt_touch_device *touch, int cmd, void *arg) { switch(cmd) { case RT_TOUCH_CTRL_GET_ID: ft6336_get_id(); break; case RT_TOUCH_CTRL_GET_INFO: // 必须实现这个命令的处理 ft6336_get_info(ft6336_client, (struct rt_touch_info *)arg); break; case RT_TOUCH_CTRL_SET_MODE: break; case RT_TOUCH_CTRL_SET_X_RANGE: break; case RT_TOUCH_CTRL_SET_Y_RANGE: break; case RT_TOUCH_CTRL_SET_X_TO_Y: break; case RT_TOUCH_CTRL_DISABLE_INT: break; case RT_TOUCH_CTRL_ENABLE_INT: break; default: break; } return RT_EOK; } // 设备接口需要用到的结构体 static const struct rt_touch_ops ft6336_touch_ops { .touch_readpoint ft6336_read_point, .touch_control ft6336_control, }; /** * ft6336触摸设备注册 * param name 触摸设备名 * param cfg 相关的配置 * param pin 复位引脚 * return RT_EOK成功 */ static int rt_hw_ft6336_init(const char *name, struct rt_touch_config *cfg, rt_base_t pin) { struct rt_touch_device *touch_device RT_NULL; touch_device (rt_touch_t)rt_calloc(1, sizeof(struct rt_touch_device)); if (touch_device RT_NULL) { return -RT_ENOMEM; } // 查找并打开I2C总线 ft6336_client.bus rt_i2c_bus_device_find(cfg-dev_name); if (ft6336_client.bus RT_NULL) { return -RT_EIO; } if (rt_device_open((rt_device_t)ft6336_client.bus, RT_DEVICE_FLAG_RDWR) ! RT_EOK) { LOG_E(open %s device failed, cfg-dev_name); return -RT_ERROR; } ft6336_client.client_addr FT_SLAVE_ADDR; // 初始化芯片 ft6336_init(ft6336_client.bus, pin); // 配置触摸设备信息 rt_memcpy(touch_device-config, cfg, sizeof(struct rt_touch_config)); touch_device-info.point_num 1; touch_device-info.type RT_TOUCH_TYPE_CAPACITANCE; touch_device-info.vendor RT_TOUCH_VENDOR_FT; touch_device-info.range_x AIC_TOUCH_PANEL_FT7411_X_RANGE; touch_device-info.range_y AIC_TOUCH_PANEL_FT7411_Y_RANGE; touch_device-ops ft6336_touch_ops; // 注册触摸设备 if (RT_EOK ! rt_hw_touch_register(touch_device, name, RT_DEVICE_FLAG_INT_RX, RT_NULL)) { LOG_E(touch device ft6336 init failed !!!); return -RT_ERROR; } LOG_I(touch device ft6336 init success); return RT_EOK; } /** * GPIO引脚初始化 */ static int rt_ft6336_gpio_cfg() { unsigned int g, p; long pin; // RST 引脚配置为输入通常由上拉电阻控制驱动内部会控制 pin drv_pin_get(AIC_TOUCH_PANEL_RST_PIN); g GPIO_GROUP(pin); p GPIO_GROUP_PIN(pin); hal_gpio_direction_input(g, p); // INT 中断引脚配置为输入 pin drv_pin_get(AIC_TOUCH_PANEL_INT_PIN); g GPIO_GROUP(pin); p GPIO_GROUP_PIN(pin); hal_gpio_direction_input(g, p); hal_gpio_set_irq_mode(g, p, 0); return 0; } /** * FT6336设备端口初始化 */ static int rt_hw_ft6336_port(void) { struct rt_touch_config cfg; rt_base_t rst_pin; rt_ft6336_gpio_cfg(); rst_pin rt_pin_get(AIC_TOUCH_PANEL_RST_PIN); cfg.dev_name AIC_TOUCH_PANEL_I2C_CHAN; // I2C通道名如i2c2 cfg.irq_pin.pin rt_pin_get(AIC_TOUCH_PANEL_INT_PIN); cfg.irq_pin.mode PIN_MODE_INPUT; cfg.user_data rst_pin; rt_hw_ft6336_init(ft6336, cfg, rst_pin); // 读取ID测试 ft6336_get_id(); return 0; } // 自动初始化函数 rt_hw_ft6336_port INIT_DEVICE_EXPORT(rt_hw_ft6336_port);特别需要注意的函数ft6336_read_point形参必须和上面代码一致且返回值需要读取当前触摸屏幕的手指数量如果返回值一直是0则触摸就会没有响应。rt_ft6336_gpio_cfg可以直接复用里面用的都是公共参数。ft6336_control必须实现RT_TOUCH_CTRL_GET_INFO这个选项相关的处理rt_hw_ft6336_port可以直接参考上面代码基本可以复用。且必须要自动初始化INIT_DEVICE_EXPORT。3.3 添加编译路径驱动代码写好了得告诉编译系统去哪里找这些文件。编辑\luban-lite\bsp\peripheral\touch\SConscript文件在合适的位置添加以下内容if GetDepend(AIC_TOUCH_PANEL_FT6336): CPPPATH.append(cwd /ft6336/inc) src Glob(ft6336/src/*.c)这段代码的意思是如果我们在menuconfig中选择了AIC_TOUCH_PANEL_FT6336这个配置项就把ft6336/inc目录加入头文件搜索路径并把ft6336/src/下的所有.c文件加入编译。3.4 添加menuconfig配置项为了让menuconfig里出现 FT6336 的选项我们需要修改 Kconfig 文件。在\luban-lite\bsp\peripheral\touch\Kconfig文件中添加两个语句添加 FT6336 的布尔选择项config AIC_TOUCH_PANEL_FT6336 bool FT6336在默认设备名配置处根据选择项来设置默认名default ft6336 if AIC_TOUCH_PANEL_FT63363.5 进行menuconfig配置现在一切准备就绪我们可以进行图形化配置了。在SDK根目录下执行命令scons --menuconfig进入配置页面后按照以下路径进行配置Board options --- [*] Using I2c2 I2c2 Parameter --- [ ] Using I2C2 10-bit Addr(default 7-bit addr) (400000) Setting I2C2 Transfer Speed # 设置I2C速度400KHz是常用值 [ ] Using I2C2 Slave Mode(default master mode) Rt-Thread options --- RT-Thread Components --- Device Drivers --- [*] Using Touch device drivers [*] touch irq use pin irq # 启用触摸中断 Drivers options --- Peripheral --- Touch Panel Support --- Select touch device (CTP) --- (X) CTP # 选择电容触摸 Select CTP device (FT6336) --- (X) FT6336 # 选择我们刚添加的FT6336驱动 (i2c2) Touch using I2C channel index # 触摸使用的I2C通道如i2c2 (PA.10) Touch reset pin # 触摸复位引脚根据原理图填写 (PA.11) Touch irq pin # 触摸中断引脚根据原理图填写 (480) Touch x coordinate range # 因为目前移植的屏幕是480*800的竖屏所以x和y的触摸范围设定为这样 (800) Touch y coordinate range配置完成后保存退出。3.6 编译与验证最后执行编译命令scons编译成功后将固件烧录到开发板。上电后如果一切正常你应该能在串口日志中看到类似touch device ft6336 init success和芯片ID信息输出的提示。编译然后烧录镜像就能看到相关的屏幕触摸功能正常工作了。至此从屏幕选购到非默认触摸芯片FT6336的驱动移植整个流程就完成了。虽然步骤看起来多但一步步跟着做其实都是体力活。最关键的还是理解驱动框架以及menuconfig的配置逻辑。希望这篇教程能帮你少走弯路。