ARM(15) - LCD(2)显示字母数字+touch
一、配置
参考手册
二、程序
(一)、显示字母和数字
调用
(二)、触屏功能实现
1、整体框架与核心数据结构
首先明确代码的核心目标:通过 I2C 与触摸芯片通信,利用中断捕获触摸事件,读取触摸坐标并在 LCD 上显示。
1. 隐含的数据结构(关键!)
代码中未显式定义touch_data_t
和point_t
,但通过逻辑可反推其结构(通常在touch.h
中声明),是触摸数据存储的基础:
// 触摸点坐标结构体(单个触摸点的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
(引脚复用控制器)配置为目标功能:
// 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 输出高低电平实现复位时序:
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:触摸芯片寄存器初始化(可选调试与配置)
代码中包含读取芯片状态、配置工作模式的逻辑(部分注释掉,用于调试):
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 中断以捕获该事件:
// 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_write
和touch_read
是封装好的 I2C 读写接口,负责与触摸芯片的寄存器交互。
1. I2C 写函数(touch_write
)
功能:向触摸芯片的指定寄存器写入数据(如配置寄存器、清除标志)。
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
)
功能:从触摸芯片的指定寄存器读取数据(如触摸点数量、坐标)。
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 读流程(关键:需 “两次启动”):
- 第一次启动:MCU 发送「设备地址 + 写标志」→ 发送「2 字节寄存器地址」→ 发送「重复启动信号」;
- 第二次启动:MCU 发送「设备地址 + 读标志」→ 接收「数据」→ 发送「NACK + 停止信号」。
4、模块 3:中断处理(touch_screen_interrupt_handler
)
当有触摸时,触摸芯片拉低 GPIO1_IO09,触发中断,中断处理函数负责读取触摸数据并存储到全局变量,是触屏功能的 “数据采集核心”。
中断处理流程
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
获取触摸数据 —— 该函数是 “中断层” 与 “应用层” 的隔离接口,降低耦合。
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 上显示。
主函数核心流程
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);}}}
}