1. IMX6ULL与GT9147触摸屏驱动开发实战
在嵌入式Linux开发中,触摸屏驱动是一个常见但颇具挑战性的任务。最近我在IMX6ULL平台上调试4.3寸800×480分辨率的LCD屏幕,搭配GT9147触摸芯片,实现了触摸坐标数据的采集和显示。这个过程中遇到了一些典型问题,也积累了不少实战经验,今天就来详细分享一下完整的实现过程。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 硬件环境搭建与原理分析
2.1 硬件组件选型与连接
我们使用的核心硬件配置如下:
- 主控芯片:NXP i.MX6ULL Cortex-A7处理器
- 触摸芯片:Goodix GT9147电容触摸控制器
- 显示屏:4.3寸TFT LCD,分辨率800×480
- 通信接口:I2C用于触摸数据传输,LCD使用RGB接口
GT9147通过I2C接口与IMX6ULL通信,硬件连接需要注意几个关键点:
- I2C引脚配置:使用IMX6ULL的I2C2接口(UART5_TXD作为SCL,UART5_RXD作为SDA)
- 中断引脚:GPIO1_IO09用于触摸中断信号
- 复位引脚:GPIO5_IO09用于触摸芯片复位控制
2.2 GT9147工作原理
GT9147是一款支持多点触控的电容式触摸屏控制器,其主要特性包括:
- 支持最多5点同时触控
- 内置12位ADC,坐标分辨率高
- 通过I2C接口通信,最高速率400kHz
- 可配置的扫描频率和灵敏度
工作时序分为三个阶段:
- 初始化阶段:配置I2C接口,复位芯片,加载配置参数
- 中断响应:当触摸发生时产生中断信号
- 数据读取:通过I2C读取触摸点坐标数据
3. 驱动开发关键实现
3.1 I2C接口初始化
IMX6ULL的I2C控制器需要正确配置引脚复用和电气特性。在我们的实现中,关键配置如下:
c复制/* 1、初始化IIC2 IO
* I2C2_SCL -> UART5_TXD
* I2C2_SDA -> UART5_RXD
*/
IOMUXC_SetPinMux(IOMUXC_UART5_TX_DATA_I2C2_SCL,1);
IOMUXC_SetPinMux(IOMUXC_UART5_RX_DATA_I2C2_SDA,1);
/* 配置I2C2 IO属性 */
IOMUXC_SetPinConfig(IOMUXC_UART5_TX_DATA_I2C2_SCL, 0x70B0);
IOMUXC_SetPinConfig(IOMUXC_UART5_RX_DATA_I2C2_SDA, 0X70B0);
这里的0x70B0配置值对应以下电气特性:
- 47K上拉电阻使能
- 100MHz信号速度
- 中等驱动强度(R0/6)
- 高转换率使能
3.2 GT9147初始化流程
完整的初始化过程包括以下几个关键步骤:
c复制void gt9147_init(void)
{
/* 1. 初始化I2C接口 */
i2c_init(I2C2);
/* 2. 配置中断和复位GPIO */
gpio_init(GPIO1, 9, &ctintpin_config); // 中断引脚
gpio_init(GPIO5, 9, &ctretpin_config); // 复位引脚
/* 3. 复位GT9147 */
gpio_pinwrite(GPIO5, 9, 0); // 拉低复位
delayms(10);
gpio_pinwrite(GPIO5, 9, 1); // 释放复位
delayms(10);
/* 4. 读取芯片ID验证通信 */
gt9147_read_len(GT9147_ADDR, GT_PID_REG, 6, (unsigned char *)temp);
/* 5. 配置中断模式 */
switch(temp[0]) {
case 0x0: ctintpin_config.interruptMode = kGPIO_IntRisingEdge; break;
// 其他模式处理...
}
/* 6. 使能中断 */
GIC_EnableIRQ(GPIO1_Combined_0_15_IRQn);
system_register_irqhandler(GPIO1_Combined_0_15_IRQn, gt9147_irqhandler, NULL);
gpio_enableint(GPIO1, 9);
}
3.3 触摸坐标读取实现
当触摸事件发生时,GT9147会触发中断,我们在中断服务函数中读取坐标数据:
c复制void gt9147_irqhandler(void)
{
if(gt9147_dev.initfalg == GT9147_INIT_FINISHED) {
unsigned char touch_status = gt9147_read_byte(GT9147_ADDR, GT_GSTID_REG);
if(touch_status & 0x80) { // 检查有效触摸标志
gt9147_read_tpcoord(); // 读取所有触摸点坐标
// 在LCD上显示坐标数据
lcd_shownum(50 + 72, 110, gt9147_dev.point_num , 1, 16);
lcd_shownum(50 + 72, 130, gt9147_dev.x[0], 5, 16);
// 其他坐标显示...
// 在触摸位置绘制点
lcd_draw_big_point(gt9147_dev.x[0],gt9147_dev.y[0],LCD_BLACK);
// 其他点绘制...
}
gt9147_write_byte(GT9147_ADDR, GT_GSTID_REG, 0x00); // 清除状态
}
gpio_clearintflags(GPIO1, 9); // 清除中断标志
}
坐标读取函数gt9147_read_tpcoord()的实现如下:
c复制void gt9147_read_tpcoord(void)
{
u8 buf[4];
u8 regvalue = gt9147_read_byte(GT9147_ADDR, GT_GSTID_REG);
gt9147_dev.point_num = regvalue & 0xF; // 获取触摸点数量
for(u8 i = 0; i < gt9147_dev.point_num; i++) {
gt9147_read_len(GT9147_ADDR, GT9147_TPX_TBL[i], 4, buf);
gt9147_dev.x[i] = ((u16)buf[1] << 8) + buf[0];
gt9147_dev.y[i] = (((u16)bu
