正点原子 I.MX6ULL MINI 开发板学习笔记:GT9147 电容触摸屏驱动

分享
GT9147 连接示意图

这篇笔记继续正点原子 I.MX6ULL MINI 开发板裸机学习,主题是GT9147 电容触摸屏驱动。代码来自实际工程 /home/zhao/code/2026/touch_screen,通过 I2C2 + 中断读取触摸坐标。

1. 完成什么实验?

驱动 GT9147 电容触摸芯片,通过 I2C2 读取触摸点坐标,并用 GPIO1_IO09 中断触发读取。

2. 电容屏和电阻屏有什么区别?

电容屏靠人体感应电荷工作,支持多点触控,表面覆盖玻璃,寿命长、手感好;电阻屏靠压力使两层导电膜接触,通常只支持单点,表面为软屏,容易磨损。

3. GT9147 使用什么接口?

GT9147 使用 I2C 通信,同时需要 INT 中断引脚和 RST 复位引脚。代码中 INT 接 GPIO1_IO09,RST 接 GPIO5_IO09。

4. GT9147 的 I2C 地址是多少?

代码中使用的设备地址是 0x14。GT9147 在复位时序中采样 INT 引脚电平来选择地址:本代码的复位时序是 INT 与 RST 同时拉低,10ms 后释放 RST,INT 继续保持低电平约 100ms——复位释放期间 INT 保持低电平,选中的就是 0x14;如果复位释放后把 INT 拉高,则选中 0x5D。所以代码的时序和所用地址 0x14 是一致的。

5. 怎么读取坐标?

先读 0x814E 获取触摸点数并清标志位,再从 0x8150、0x8158、0x8160 等寄存器读取每个触摸点的 4 字节坐标(X 低字节、X 高字节、Y 低字节、Y 高字节)。

坐标读取流程

6. 中断方式还是轮询方式?

代码使用 GPIO1_IO09 上升沿中断(ICR1[19:18]=10,I.MX6ULL 的 ICR 编码为 00=低电平/01=高电平/10=上升沿/11=下降沿)。触摸发生时 GT9147 拉高 INT,触发中断服务函数 read_points()。

7. 真实可用的 GT9147 代码

下面是工程中验证过的 gt9147.c 核心代码。

struct GT9147_Device
{
    int num;
    int available;
    unsigned short x[5];
    unsigned short y[5];
};

struct GT9147_Device gt9147_dev;
const unsigned short GT_TP[5] = {0x8150, 0x8158, 0x8160, 0x8168, 0x8170};

void read_gt9147(unsigned short reg_addr, unsigned char *data, unsigned int len)
{
    struct I2C_Msg msg =
    {
        .dev_addr = 0x14,
        .reg_addr = reg_addr,
        .reg_len = 2,
        .data = data,
        .direction = I2C_Read,
        .len = len
    };
    transfer(I2C2, &msg);
}

void write_gt9147(unsigned short reg_addr, unsigned char *data, unsigned int len)
{
    struct I2C_Msg msg =
    {
        .dev_addr = 0x14,
        .reg_addr = reg_addr,
        .reg_len = 2,
        .data = data,
        .direction = I2C_Write,
        .len = len
    };
    transfer(I2C2, &msg);
}
void read_points(void)
{
    unsigned char buffer[24];
    read_gt9147(0x814E, buffer, 1);
    int num = buffer[0] & 0x0F;
    buffer[0] = 0;
    write_gt9147(0x814E, buffer, 1);

    if(num != 0)
    {
        gt9147_dev.available = 1;
        gt9147_dev.num = num;
    }
    int i;
    for(i = 0;i < num;++i)
    {
        read_gt9147(GT_TP[i], buffer, 4);
        gt9147_dev.x[i] = buffer[1] << 8 | buffer[0];
        gt9147_dev.y[i] = buffer[3] << 8 | buffer[2];
    }
}

void touch_screen_interrupt_handler(void)
{
    if((GPIO1->ISR & (1 << 9)) != 0)
    {
        read_points();
        GPIO1->ISR |= (1 << 9);
    }
}
void init_gt9147(void)
{
    IOMUXC_SetPinMux(IOMUXC_GPIO1_IO09_GPIO1_IO09, 0);            // INT
    IOMUXC_SetPinMux(IOMUXC_SNVS_SNVS_TAMPER9_GPIO5_IO09, 0);     // RST
    IOMUXC_SetPinConfig(IOMUXC_GPIO1_IO09_GPIO1_IO09, 0x10B0);
    IOMUXC_SetPinConfig(IOMUXC_SNVS_SNVS_TAMPER9_GPIO5_IO09, 0x10B0);

    gpio_configt_t t =
    {
        .pin_direction = gpio_direction_output,
        .default_vaule = 0
    };
    init_gpio(GPIO1, 9, &t);
    init_gpio(GPIO5, 9, &t);

    write_gpio(GPIO1, 9, 0);
    write_gpio(GPIO5, 9, 0);
    delay_ms(10);
    write_gpio(GPIO5, 9, 1);
    delay_ms(100);

    unsigned char buffer[24] = {0};
    read_gt9147(0x8140, buffer, 4);
    printf("product ID =  %s\n", (char *)buffer);

    read_gt9147(0x804d, buffer, 1);
    printf("interrupt way 0 = %d\r\n", buffer[0] & 0x03);
    read_gt9147(0x8056, buffer, 1);
    printf("interrupt way 1 = %d\r\n", buffer[0] & 0x03);

    t.pin_direction = gpio_direction_input;
    init_gpio(GPIO1, 9, &t);
    IOMUXC_SetPinConfig(IOMUXC_GPIO1_IO09_GPIO1_IO09, 0x80);

    GPIO1->ICR1 &= ~(3 << 18);
    GPIO1->ICR1 |= (2 << 18);
    GPIO1->IMR |= (1 << 9);

    system_interrupt_register(touch_screen_interrupt_handler, GPIO1_Combined_0_15_IRQn);
    GIC_EnableIRQ(GPIO1_Combined_0_15_IRQn);

    buffer[0] = 0;
    write_gt9147(0x8040, buffer, 1);
    delay_ms(200);
}
#include "gt9147.h"
#include "lcd.h"

int main(void)
{
    init_clock();
    system_interrupt_init();
    init_uart1();
    init_lcd();
    init_gt9147();

    while (1)
    {
        if (gt9147_dev.available)
        {
            gt9147_dev.available = 0;
            for (int i = 0; i < gt9147_dev.num; i++)
            {
                printf("point %d: x=%d, y=%d\n", i,
                       gt9147_dev.x[i], gt9147_dev.y[i]);
            }
        }
        delay_ms(50);
    }
    return 0;
}
触摸坐标示例

8. 关键点说明

  • 接口:I2C2 通信 + GPIO1_IO09 中断 + GPIO5_IO09 复位。
  • I2C 地址:代码中使用 0x14(复位释放期间 INT 保持低电平选中 0x14;INT 拉高则选中 0x5D)。
  • 寄存器:0x814E 为状态/点数寄存器;0x8150/58/60... 为各点坐标寄存器;0x804D 低位与 INT 触发方式相关;0x8056 实为刷新率寄存器 Refresh_Rate(代码里打印标签 interrupt way 1 是笔误)。
  • 坐标格式:每个点 4 字节,低高字节组合成 X 和 Y。
  • 中断触发:GPIO1_IO09 配置为上升沿中断,触摸时自动触发读取。
  • 清标志:读 0x814E 后必须写 0 清标志,否则不会再次触发中断。
  • 点数钳位:0x814E 低 4 位给出的点数最大可到 15,而 GT_TP 数组只有 5 项,严谨写法应在 read_points() 里加 if(num > 5) num = 5; 钳位,防止数组越界。

9. 实验现象

程序运行后,用手指触摸屏幕,串口会打印当前触摸点坐标,例如 "point 0: x=312, y=198"。单点和多点触摸都能检测到最多 5 个点的坐标。

总结

问题答案
完成什么实验?驱动 GT9147,I2C2 读取触摸坐标,GPIO1_IO09 中断触发
电容屏 vs 电阻屏?电容屏靠电荷感应,支持多点,寿命长
GT9147 接口?I2C + INT 中断 + RST 复位
I2C 地址?代码中用 0x14(也可能为 0x5D)
怎么读坐标?读 0x814E 获取点数,清标志,再读 0x8150/58/60...
中断还是轮询?GPIO1_IO09 上升沿中断

GT9147 触摸驱动把 I2C、GPIO 中断和 LCD 显示串在了一起,是人机交互的重要一环。

继续阅读

嵌入式开发面试知识手册

嵌入式开发工程师面试知识手册 适用对象:零基础起步、目标是通过嵌入式软件/驱动/BSP 方向面试的读者 编写依据:赵哲简历全部知识点 + 智能车 AGV 控制平台完整项目代码(i.MX6ULL Linux 端 + STM32F407 固件)+ Linux 驱动练习代码包 + embedded-agent 嵌入式开发智能体仓库 + 真实面试手撕代码与追问记录 使用方法:每篇按"通俗解释 → 原理细节 → 命令/API → 代码实例 → 项目结合 → 记忆口诀 → 面试问答"组织;口诀用于快速记忆,问答用于自测。第9篇是讲项目的演练场,第10篇是手撕代码实战题库。 总目录 * 第1篇 C语言与数据结构 * 1.1 编译链接全过程 * 1.2 数据类型与内存

作者:azhezhezhe

正点原子 I.MX6ULL MINI 开发板学习笔记:Linux 驱动架构详解——编译、树内树外与运行时效果

这是 Linux 驱动小系列的总结篇。前三篇把 LED 驱动从内核模块写到 misc 设备,属于“会用”;这篇把背后的架构讲透:驱动到底怎么编译、内核树内和树外两种方式的区别、以及驱动里每个操作在系统跑起来之后到底产生了什么效果。内容整理自我的学习笔记(note.pdf)和实际工程 code/2026/20260803。 1. 驱动代码到底怎么编译的? 不管哪种方式,最终都靠内核的 kbuild 系统。核心就三个文件的关系: * Kconfig:定义 menuconfig 里的选项(如 CONFIG_MYMODULE),三态 y/m/n——y 编进内核,m 编成模块,n 不编; * .config:menuconfig 的选择结果,保存在源码顶层; * Makefile:

作者:azhezhezhe

正点原子 I.MX6ULL MINI 开发板学习笔记:misc 设备——字符设备驱动的快捷方式

上两篇分别用手动 mknod 和 class_create+device_create 注册字符设备,调用链一长串。这篇用 misc(混杂)设备 一步搞定。代码来自实际工程 code/2026/20260803/05_misc_led,已验证可用。 1. 完成什么实验? 用 misc_register 重写 LED 驱动:一步完成设备号分配、字符设备注册、/dev/myled0 节点创建,同时保留上一篇的 sysfs 属性 led_status。 2. 什么是 misc 设备? misc(混杂)设备是 Linux 给“不好归类的小字符设备”

作者:azhezhezhe