ARM(15) - LCD(2)显示字母数字+touch

一、配置

参考手册

二、程序

(一)、显示字母和数字

调用

(二)、触屏功能实现

1、整体框架与核心数据结构

首先明确代码的核心目标:通过 I2C 与触摸芯片通信,利用中断捕获触摸事件,读取触摸坐标并在 LCD 上显示

1. 隐含的数据结构(关键!)

代码中未显式定义touch_data_tpoint_t,但通过逻辑可反推其结构(通常在touch.h中声明),是触摸数据存储的基础:

cpp 复制代码
// 触摸点坐标结构体(单个触摸点的x/y坐标)
typedef struct {
    unsigned short x;  // x轴坐标(16位,适配常见LCD分辨率)
    unsigned short y;  // y轴坐标(16位)
} point_t;

// 触摸数据全局结构体(存储所有触摸信息,供中断与主函数共享)
typedef struct {
    unsigned char flag_valid;       // 数据有效标志(1=有新触摸数据,0=无)
    unsigned char num_point;        // 当前触摸点数量(0~5,FT5x06支持最多5点触摸)
    point_t point_data[5];          // 5个触摸点的坐标数据(对应最多5点)
} touch_data_t;

// 全局变量:中断处理函数与主函数通过该变量传递触摸数据
touch_data_t touch_data;

2. 核心外设依赖

代码依赖 MCIMX6Y2 的多个外设,需先初始化才能保证触屏功能正常:

  • I2C2 :与触摸芯片通信(触摸芯片为 I2C 从设备,地址0x14);
  • GPIO:2 个关键引脚(复位引脚 GPIO5_IO09、中断引脚 GPIO1_IO09);
  • GIC(通用中断控制器):管理 GPIO 中断,使能中断响应;
  • LCD :显示触摸坐标(依赖lcd_show_string函数)。

2、模块 1:触摸硬件初始化(touch_init

touch_init是触屏功能的 "启动入口",负责完成引脚配置、触摸芯片复位、中断配置三大核心工作,确保触摸芯片进入就绪状态。

1. 步骤 1:引脚复用与电气属性配置

MCIMX6Y2 的引脚需通过IOMUXC(引脚复用控制器)配置为目标功能:

cpp 复制代码
// 1. 配置复位引脚(GPIO5_IO09):复用为GPIO功能
IOMUXC_SetPinMux(IOMUXC_SNVS_SNVS_TAMPER9_GPIO5_IO09, 0);
// 2. 配置中断引脚(GPIO1_IO09):复用为GPIO功能
IOMUXC_SetPinMux(IOMUXC_GPIO1_IO09_GPIO1_IO09, 0);

// 3. 配置引脚电气属性(拉电阻、驱动能力、 slew rate等)
// 0x10B0:通常表示"下拉电阻+100KΩ+驱动能力适中"(具体需查IMX6Y2手册)
IOMUXC_SetPinConfig(IOMUXC_SNVS_SNVS_TAMPER9_GPIO5_IO09, 0x10B0);
// 0xF0B0:中断引脚可能配置为"上拉电阻"(匹配触摸芯片中断输出电平)
IOMUXC_SetPinConfig(IOMUXC_GPIO1_IO09_GPIO1_IO09, 0xF0B0);

2. 步骤 2:触摸芯片复位(关键时序)

触摸芯片上电后需复位才能正常工作,通过 GPIO5_IO09 输出高低电平实现复位时序:

cpp 复制代码
gpio_pin_t pin;
pin.dir = gpio_output;  // 复位引脚设为输出模式
pin.def_val = 0;
gpio_init(GPIO5, 9, &pin);  // 初始化GPIO5_IO09(复位引脚)
gpio_init(GPIO1, 9, &pin);  // 先将中断引脚设为输出(临时拉低复位?)

// 复位时序:拉低10ms(硬件复位)→ 拉高100ms(等待芯片启动)
gpio_write(GPIO5, 9, 0);  // 复位引脚拉低
gpio_write(GPIO1, 9, 0);  // 中断引脚临时拉低(可选,看芯片要求)
delay_ms(10);             // 保持拉低10ms(满足芯片复位时序)
gpio_write(GPIO5, 9, 1);  // 复位引脚拉高(结束复位)
delay_ms(100);            // 等待芯片初始化完成

3. 步骤 3:触摸芯片寄存器初始化(可选调试与配置)

代码中包含读取芯片状态、配置工作模式的逻辑(部分注释掉,用于调试):

cpp 复制代码
unsigned char buf[20] = {0};
// 读0x8056寄存器:获取触摸触发模式(0x3可能表示"中断触发模式")
touch_read(0x8056, buf, 1); 
sprintf(buf,"triger mode:%d",buf[0] & 0x3);  // 格式化模式值(调试用)

// 写0x8040寄存器:配置触摸芯片工作模式(如"中断使能""多点触摸使能")
buf[0] = 0;  // 具体值需查芯片手册(如0=默认模式)
touch_write(0x8040, buf, 1);
delay_ms(100);  // 等待配置生效

4. 步骤 4:GPIO 中断配置(捕获触摸事件)

触摸芯片在检测到触摸时,会拉低中断引脚(GPIO1_IO09),需配置 GPIO 中断以捕获该事件:

cpp 复制代码
// 1. 中断引脚改为输入模式(接收触摸芯片的中断信号)
pin.dir = gpio_input;
gpio_init(GPIO1, 9, &pin);

// 2. 配置中断触发方式(GPIO1->ICR1:中断配置寄存器1)
// (3 << 18):配置GPIO1_IO09的触发方式(3=下降沿触发,因触摸芯片中断为低电平脉冲)
// 注:ICR1的bit18~19对应IO9(IMX6 GPIO中断配置规则:bit[2n+1:n]对应IOn)
GPIO1->ICR1 |= (3 << 18); 

// 3. 解除中断屏蔽(GPIO1->IMR:中断屏蔽寄存器)
// (1 << 9):允许GPIO1_IO09产生中断
GPIO1->IMR |= (1 << 9);

// 4. 注册中断处理函数并使能GIC中断
// 把"GPIO1_Combined_0_15_IRQn"中断与"touch_screen_interrupt_handler"绑定
system_interrupt_register(GPIO1_Combined_0_15_IRQn, touch_screen_interrupt_handler);
// 在GIC中使能该中断(Cortex-A7必须通过GIC管理中断)
GIC_EnableIRQ(GPIO1_Combined_0_15_IRQn);

3、模块 2:I2C 通信层(touch_write/touch_read

触摸芯片通过 I2C 协议与 MCU 通信,touch_writetouch_read是封装好的 I2C 读写接口,负责与触摸芯片的寄存器交互。

1. I2C 写函数(touch_write

功能:向触摸芯片的指定寄存器写入数据(如配置寄存器、清除标志)。

cpp 复制代码
void touch_write(unsigned short reg_addr, unsigned char *data, unsigned short len)
{
    // I2C消息结构体(定义在i2c.h中,描述一次I2C传输的所有参数)
    struct I2C_Msg msg = {
        .dev_addr = 0x14,        // 触摸芯片的I2C从设备地址(FT5x06默认0x14)
        .reg_addr = reg_addr,    // 目标寄存器地址(如0x8040、0x814E)
        .reg_len = 2,            // 寄存器地址长度(2字节,因reg_addr是unsigned short)
        .data = data,            // 要写入的数据缓冲区
        .len = len,              // 写入数据的长度(字节数)
        .dir = I2C_write         // 传输方向:写
    };
    // 调用I2C底层驱动,执行一次I2C写传输(I2C2总线)
    i2c_transfer(I2C2, &msg);
}

I2C 写流程:MCU(I2C 主设备)发送「设备地址 + 写标志」→ 发送「2 字节寄存器地址」→ 发送「数据」→ 停止信号。

2. I2C 读函数(touch_read

功能:从触摸芯片的指定寄存器读取数据(如触摸点数量、坐标)。

cpp 复制代码
void touch_read(unsigned short reg_addr, unsigned char *data, unsigned short len)
{
    struct I2C_Msg msg = {
        .dev_addr = 0x14,        //  same as write
        .reg_addr = reg_addr,    // 目标寄存器地址
        .reg_len = 2,            // 寄存器地址长度(2字节)
        .data = data,            // 存储读取数据的缓冲区
        .len = len,              // 要读取的数据长度
        .dir = I2C_read          // 传输方向:读
    };
    i2c_transfer(I2C2, &msg);  // 执行I2C读传输
}

I2C 读流程(关键:需 "两次启动"):

  1. 第一次启动:MCU 发送「设备地址 + 写标志」→ 发送「2 字节寄存器地址」→ 发送「重复启动信号」;
  2. 第二次启动:MCU 发送「设备地址 + 读标志」→ 接收「数据」→ 发送「NACK + 停止信号」。

4、模块 3:中断处理(touch_screen_interrupt_handler

当有触摸时,触摸芯片拉低 GPIO1_IO09,触发中断,中断处理函数负责读取触摸数据并存储到全局变量,是触屏功能的 "数据采集核心"。

中断处理流程

cpp 复制代码
void touch_screen_interrupt_handler(void)
{
    // 1. 确认中断源:是否为GPIO1_IO09的中断(避免误触发)
    if (GPIO1->ISR & (1 << 9)) 
    {
        unsigned char num;        // 触摸点数量
        unsigned char i = 0;
        unsigned char point[20] = {0};  // 存储单个触摸点的4字节数据(x低8、x高8、y低8、y高8)

        // 2. 读0x814E寄存器:获取当前触摸点数量(FT5x06的"触摸点数量寄存器")
        touch_read(0x814E, &num, 1);
        touch_data.num_point = num & 0x0F;  // 低4位有效(0~5,最多5点)

        // 3. 若有触摸点,读取每个点的坐标
        if (0 != touch_data.num_point)
        {
            touch_data.flag_valid = 1;  // 置"数据有效"标志,通知主函数有新数据

            // 循环读取每个触摸点的坐标(array_point是坐标寄存器地址表)
            // array_point[0] = 0x8150(第1点)、0x8158(第2点)... 0x8170(第5点)
            for (i = 0; i < touch_data.num_point; i++)
            {
                // 读4字节数据(每个触摸点坐标占4字节)
                touch_read(array_point[i], point, 4);
                // 拼接x坐标(point[0]=x低8位,point[1]=x高8位 → 16位x)
                touch_data.point_data[i].x = point[1] << 8 | point[0];
                // 拼接y坐标(point[2]=y低8位,point[3]=y高8位 → 16位y)
                touch_data.point_data[i].y = point[3] << 8 | point[2];
            }
        }

        // 4. 写0x814E寄存器为0:清除触摸点数量标志(避免重复触发中断)
        num = 0;
        touch_write(0x814E, &num, 1);

        // 5. 清除GPIO中断标志(IMX6 GPIO的ISR寄存器"写1清中断")
        GPIO1->ISR |= (1 << 9);
    }
}

关键注意点

  • 中断处理函数需 "快进快出",但此处读取 I2C 数据为阻塞操作(若触摸芯片响应快,影响可忽略);
  • 坐标拼接逻辑需与触摸芯片的寄存器格式匹配(FT5x06 的坐标寄存器格式就是 "低 8 位 + 高 8 位")。

5、模块 4:数据交互接口(get_touch_screen_data

主函数不直接操作中断或 I2C,而是通过get_touch_screen_data获取触摸数据 ------ 该函数是 "中断层" 与 "应用层" 的隔离接口,降低耦合。

cpp 复制代码
unsigned char get_touch_screen_data(point_t *data)
{
    unsigned char i = 0;
    // 若数据有效(中断已采集到新触摸数据)
    if (touch_data.flag_valid != 0)
    {
        // 把全局变量的触摸点数据复制到传入的data数组(主函数的point数组)
        for (i = 0; i < touch_data.num_point; i++)
        {
            data[i].x = touch_data.point_data[i].x;
            data[i].y = touch_data.point_data[i].y;
        }
        touch_data.flag_valid = 0;  // 清"数据有效"标志(避免重复读取)
        return touch_data.num_point;  // 返回触摸点数量(0~5)
    }
    return 0;  // 无有效数据,返回0
}

6、模块 5:主函数逻辑(main

主函数是触屏功能的 "应用入口",负责初始化所有外设,然后循环读取触摸数据并在 LCD 上显示。

主函数核心流程

cpp 复制代码
int main(void)
{
    // 1. 初始化基础外设(时钟、中断控制器、LED/蜂鸣器/按键等)
    clock_init();          // 初始化系统时钟(IMX6Y2核心时钟、外设时钟)
    system_interrupt_init();// 初始化中断控制器(GIC)
    led_init();            // 初始化LED(可选,用于触摸反馈)
    beep_init();           // 初始化蜂鸣器(可选)
    key_init();            // 初始化按键(可选)

    // 2. 初始化定时器、UART、I2C等通信/定时外设
    epit1_init();          // EPIT定时器(可选)
    gpt1_init();           // GPT定时器(可选,用于delay_ms)
    uart1_init();          // UART1(可选,用于串口打印调试)
    i2c1_init();           // I2C1(其他设备,如LM75温度传感器)
    i2c2_init();           // I2C2(关键!触摸芯片通信总线)

    // 3. 初始化模拟外设与显示外设
    adc1_init();           // ADC(可选)
    pwm1_init();           // PWM(可选,如背光控制)
    pwm1_set_g_f(1);       // 设置PWM参数(可选)
    lcd_init();            // 初始化LCD(关键!显示触摸坐标)
    delay_ms(50);          // 等待LCD启动
    lcd_clear();           // 清屏(避免残留画面)

    // 4. 初始化触摸功能(关键!前面讲解的touch_init)
    touch_init();

    // 5. 主循环:读取触摸数据并显示
    while (1)
    {
        point_t point[5];  // 存储最多5个触摸点的坐标
        int i = 0;
        char buf[20] = {0};// 格式化坐标字符串

        // 读取触摸数据(返回触摸点数量,0=无触摸)
        unsigned char ret = get_touch_screen_data(point);
        if (ret != 0)  // 若有触摸数据
        {
            // 循环显示每个触摸点的坐标(x,y)
            for (i = 0; i < ret; i++)
            {
                // 格式化字符串:"0: 123, 456"(第0个点,x=123,y=456)
                sprintf(buf, "%d: %d, %d", i, point[i].x, point[i].y);
                // 在LCD上显示:位置(100, 100+32*i),字体32x32
                // 32*i:每个点占32像素高度,避免文字重叠
                lcd_show_string(100, 100 + 32 * i, strlen(buf)*16, 32, 32, buf);
            }
        }
    }
}
相关推荐
小猪写代码2 小时前
Linux-ARM-裸机开发-开发环境搭建
linux·arm开发
2301_1472583693 小时前
STM32 单片机 - ADC
stm32·单片机·嵌入式硬件
wei-dong-183797540084 小时前
嵌入式硬件笔记:三种滤波电路的对比
笔记·嵌入式硬件·算法
点灯小铭4 小时前
基于STM32单片机的OneNet物联网粉尘烟雾检测系统
stm32·单片机·物联网·毕业设计·课程设计
Skylar_.5 小时前
ARM(14) - LCD(1)清屏和画图形
arm开发
点灯小铭5 小时前
基于51单片机水塔水箱液水位WIFI监控报警设计
单片机·嵌入式硬件·mongodb·毕业设计·51单片机·课程设计
szxinmai主板定制专家5 小时前
基于RK3588与ZYNQ7045的ARM+FPGA+AI实时系统解决方案
arm开发·人工智能·嵌入式硬件·fpga开发
自激振荡器5 小时前
2,GUI Guider的安装与导入STM32裸机工程
stm32·单片机·嵌入式硬件·lvgl·gui guider
<man>6 小时前
STM32_08_中断(☆☆☆)
stm32·单片机·嵌入式硬件