29. 电容触摸屏 您所在的位置:网站首页 电容触摸屏原理详解图解 29. 电容触摸屏

29. 电容触摸屏

2024-07-09 03:24| 来源: 网络整理| 查看: 265

29.3. 电容触摸屏—触摸画板实验¶

本小节讲解如何驱动电容触摸屏,并利用触摸屏制作一个简易的触摸画板应用。

学习本小节内容时,请打开配套的“电容触摸屏—触摸画板”工程配合阅读。

29.3.1. 硬件设计¶

我们这个触摸屏出厂时就与GT9157芯片通过柔性电路板连接在一起了,柔性电路板从GT9157芯片引出VCC、GND、SCL、SDA、RSTN及INT引脚, 再通过FPC座子引出到屏幕的PCB电路板中,PCB电路板加了部分电路,如I2C的上拉电阻,然后把这些引脚引出到屏幕右侧的排针处,方便整个屏幕与外部器件相连。

以上是我们STM32F429实验板使用的5寸屏原理图,它通过屏幕上的排针接入到实验板的液晶排母接口, 与STM32芯片的引脚相连,连接见图 屏幕与实验板的引脚连接 。

图 屏幕与实验板的引脚连接 35-38号引脚即电容触摸屏相关的控制引脚。

以上原理图可查阅《LCD5.0-黑白原理图》及《野火F429开发板黑白原理图》文档获知,若您使用的液晶屏或实验板不一样,请根据实际连接的引脚修改程序。

29.3.2. 软件设计¶

本工程中的GT9157芯片驱动主要是从官方提供的Linux驱动修改过来的,我们把这部分文件存储到“gt9xx.c”及“gt9xx.h”文件中, 而这些驱动的底层I2C通讯接口我们存储到了“bsp_i2c_touch.c”及“bsp_i2c_touch.h”文件中,这些文件也可根据您的喜好命名, 它们不属于STM32标准库的内容,是由我们自己根据应用需要编写的。在我们提供的资料《gt9xx_1.8_drivers.zip》压缩包里有官方的原Linux驱动, 感兴趣的读者可以对比这些文件,了解如何移植驱动。

29.3.2.1. 编程要点¶

(1) 分析官方的gt9xx驱动,了解需要提供哪些底层接口;

(2) 编写底层驱动接口;

(3) 利用gt9xx驱动,获取触摸坐标;

(4) 编写测试程序检验驱动。

29.3.2.2. 代码分析¶

触摸屏硬件相关宏定义

根据触摸屏与STM32芯片的硬件连接,我们把触摸屏硬件相关的配置都以宏的形式定义到 “bsp_i2c_touch.h”文件中, 见 代码清单:触摸-1 。

代码清单:触摸-1 触摸屏硬件配置相关的宏(bsp_i2c_touch.h文件)¶ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37/*设定使用的电容屏IIC设备地址*/ #define GTP_ADDRESS 0xBA #define I2CT_FLAG_TIMEOUT ((uint32_t)0x1000) #define I2CT_LONG_TIMEOUT ((uint32_t)(10 * I2CT_FLAG_TIMEOUT)) /*I2C引脚*/ #define GTP_I2C I2C2 #define GTP_I2C_CLK RCC_APB1Periph_I2C2 #define GTP_I2C_CLK_INIT RCC_APB1PeriphClockCmd #define GTP_I2C_SCL_PIN GPIO_Pin_4 #define GTP_I2C_SCL_GPIO_PORT GPIOH #define GTP_I2C_SCL_GPIO_CLK RCC_AHB1Periph_GPIOH #define GTP_I2C_SCL_SOURCE GPIO_PinSource4 #define GTP_I2C_SCL_AF GPIO_AF_I2C2 #define GTP_I2C_SDA_PIN GPIO_Pin_5 #define GTP_I2C_SDA_GPIO_PORT GPIOH #define GTP_I2C_SDA_GPIO_CLK RCC_AHB1Periph_GPIOH #define GTP_I2C_SDA_SOURCE GPIO_PinSource5 #define GTP_I2C_SDA_AF GPIO_AF_I2C2 /*复位引脚*/ #define GTP_RST_GPIO_PORT GPIOI #define GTP_RST_GPIO_CLK RCC_AHB1Periph_GPIOI #define GTP_RST_GPIO_PIN GPIO_Pin_8 /*中断引脚*/ #define GTP_INT_GPIO_PORT GPIOD #define GTP_INT_GPIO_CLK RCC_AHB1Periph_GPIOD #define GTP_INT_GPIO_PIN GPIO_Pin_13 #define GTP_INT_EXTI_PORTSOURCE EXTI_PortSourceGPIOD #define GTP_INT_EXTI_PINSOURCE EXTI_PinSource13 #define GTP_INT_EXTI_LINE EXTI_Line13 #define GTP_INT_EXTI_IRQ EXTI15_10_IRQn /*中断服务函数*/ #define GTP_IRQHandler EXTI15_10_IRQHandler

以上代码根据硬件的连接,把与触摸屏通讯使用的引脚号、引脚源以及复用功能映射都以宏封装起来。在这里还定义了与GT9157芯片通讯的I2C设备地址, 该地址是一个8位的写地址,它是由我们的上电时序决定的。

初始化触摸屏控制引脚

利用上面的宏,编写触摸屏控制引脚的初始化函数,见 代码清单:触摸-2 。

代码清单:触摸-2 触摸屏控制引脚的GPIO初始化函数(bsp_i2c_touch.c文件)¶ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51/** * @brief 触摸屏 I/O配置 * @param 无 * @retval 无 */ static void I2C_GPIO_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; /*使能I2C时钟 */ GTP_I2C_CLK_INIT(GTP_I2C_CLK, ENABLE); /*使能触摸屏使用的引脚的时钟*/ RCC_AHB1PeriphClockCmd(GTP_I2C_SCL_GPIO_CLK | GTP_I2C_SDA_GPIO_CLK| GTP_RST_GPIO_CLK|GTP_INT_GPIO_CLK, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE); /* 配置I2C_SCL源*/ GPIO_PinAFConfig(GTP_I2C_SCL_GPIO_PORT, GTP_I2C_SCL_SOURCE, GTP_I2C_SCL_AF); /* 配置I2C_SDA 源*/ GPIO_PinAFConfig(GTP_I2C_SDA_GPIO_PORT, GTP_I2C_SDA_SOURCE, GTP_I2C_SDA_AF); /*配置SCL引脚 */ GPIO_InitStructure.GPIO_Pin = GTP_I2C_SCL_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_OType = GPIO_OType_OD; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_Init(GTP_I2C_SCL_GPIO_PORT, &GPIO_InitStructure); /*配置SDA引脚 */ GPIO_InitStructure.GPIO_Pin = GTP_I2C_SDA_PIN; GPIO_Init(GTP_I2C_SDA_GPIO_PORT, &GPIO_InitStructure); /*配置RST引脚,下拉推挽输出 */ GPIO_InitStructure.GPIO_Pin = GTP_RST_GPIO_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN; GPIO_Init(GTP_RST_GPIO_PORT, &GPIO_InitStructure); /*配置 INT引脚,下拉推挽输出,方便初始化 */ GPIO_InitStructure.GPIO_Pin = GTP_INT_GPIO_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN; //设置为下拉,方便初始化 GPIO_Init(GTP_INT_GPIO_PORT, &GPIO_InitStructure); }

以上函数初始化了触摸屏用到的I2C信号线,并且把RST及INT引脚也初始化成了下拉推挽输出模式,以便刚上电的时候输出上电时序,设置触摸屏的I2C设备地址。

配置I2C的模式

接下来需要配置I2C的工作模式,GT9157芯片使用的是标准7位地址模式的I2C通讯,所以I2C这部分的配置跟我们在EEPROM实验中的是一样的, 不了解这部分内容的请阅读EEPROM章节,见 代码清单:触摸-3 。

代码清单:触摸-3 配置I2C工作模式(bsp_i2c_touch.c文件)¶ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28/* STM32 I2C 快速模式 */ #define I2C_Speed 400000 /* 这个地址只要与STM32外挂的I2C器件地址不一样即可 */ #define I2C_OWN_ADDRESS7 0x0A /** * @brief I2C 工作模式配置 * @param 无 * @retval 无 */ static void I2C_Mode_Config(void) { I2C_InitTypeDef I2C_InitStructure; /* I2C 模式配置 */ I2C_InitStructure.I2C_Mode = I2C_Mode_I2C; I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2; I2C_InitStructure.I2C_OwnAddress1 =I2C_OWN_ADDRESS7; I2C_InitStructure.I2C_Ack = I2C_Ack_Enable ; I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; /* I2C的寻址模式 */ I2C_InitStructure.I2C_ClockSpeed = I2C_Speed; /* 通信速率 */ I2C_Init(GTP_I2C, &I2C_InitStructure); /* I2C1 初始化 */ I2C_Cmd(GTP_I2C, ENABLE); /* 使能 I2C1 */ I2C_AcknowledgeConfig(GTP_I2C, ENABLE); }

使用上电时序设置触摸屏的I2C地址

注意

因硬件I2C在实际驱动时存在无法成功发送信号的情况,我们的范例程序中关于I2C的底层驱动已改成使用软件I2C,其原理类似,硬件I2C的驱动在范例程序中有保留,可使用bsp_i2c_touch.h头文件中的宏来切换。

在上面配置完成STM32的引脚后,就可以开始控制这些引脚对触摸屏进行控制了,为了使用I2C通讯, 首先要根据GT9157芯片的上电时序给它设置I2C设备地址,见 代码清单:触摸-4 。

代码清单:触摸-4使用上电时序设置触摸屏的I2C地址(bsp_i2c_touch.c文件)¶ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34/** * @brief 对GT91xx芯片进行复位 * @param 无 * @retval 无 */ void I2C_ResetChip(void) { GPIO_InitTypeDef GPIO_InitStructure; /*配置 INT引脚,下拉推挽输出,方便初始化 */ GPIO_InitStructure.GPIO_Pin = GTP_INT_GPIO_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN; //设置为下拉,方便初始化 GPIO_Init(GTP_INT_GPIO_PORT, &GPIO_InitStructure); /*初始化GT9157,rst为高电平,int为低电平,则gt9157的设备地址被配置为0xBA*/ /*复位为低电平,为初始化做准备*/ GPIO_ResetBits (GTP_RST_GPIO_PORT,GTP_RST_GPIO_PIN); Delay(0x0FFFFF); /*拉高一段时间,进行初始化*/ GPIO_SetBits (GTP_RST_GPIO_PORT,GTP_RST_GPIO_PIN); Delay(0x0FFFFF); /*把INT引脚设置为浮空输入模式,以便接收触摸中断信号*/ GPIO_InitStructure.GPIO_Pin = GTP_INT_GPIO_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_Init(GTP_INT_GPIO_PORT, &GPIO_InitStructure); }

这段函数中控制RST引脚由低电平改变至高电平,且期间INT一直为低电平,这样的上电时序可以控制触控芯片的I2C写地址为0xBA, 读地址为0xBB,即(0xBA|0x01)。输出完上电时序后,把STM32的INT引脚模式改成浮空输入模式,使它可以接收触控芯片输出的触摸中断信号。 接下来我们在I2C_GTP_IRQEnable函数中使能INT中断,见 代码清单:触摸-5 。

代码清单:触摸-5 使能INT中断(bsp_i2c_touch.c文件)¶ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37/** * @brief 使能触摸屏中断 * @param 无 * @retval 无 */ void I2C_GTP_IRQEnable(void) { EXTI_InitTypeDef EXTI_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; GPIO_InitTypeDef GPIO_InitStructure; /*配置 INT 为浮空输入 */ GPIO_InitStructure.GPIO_Pin = GTP_INT_GPIO_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_Init(GTP_INT_GPIO_PORT, &GPIO_InitStructure); /* 连接 EXTI 中断源 到INT 引脚 */ SYSCFG_EXTILineConfig(GTP_INT_EXTI_PORTSOURCE, GTP_INT_EXTI_PINSOURCE); /* 选择 EXTI 中断源 */ EXTI_InitStructure.EXTI_Line = GTP_INT_EXTI_LINE; EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; EXTI_InitStructure.EXTI_LineCmd = ENABLE; EXTI_Init(&EXTI_InitStructure); /* 配置中断优先级 */ NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); /*使能中断*/ NVIC_InitStructure.NVIC_IRQChannel = GTP_INT_EXTI_IRQ; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); }

这个INT引脚我们配置为上升沿触发,是跟后面写入到触控芯片的配置参数一致的。

初始化封装

利用以上函数,我们把信号引脚及I2C设备地址初始化的过程都封装到函数I2C_Touch_Init中,见 代码清单:触摸-6 。

代码清单:触摸-6 封装引脚初始化及上电时序(bsp_i2c_touch.c文件)¶ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15/** * @brief I2C 外设(GT91xx)初始化 * @param 无 * @retval 无 */ void I2C_Touch_Init(void) { I2C_GPIO_Config(); I2C_Mode_Config(); I2C_ResetChip(); I2C_GTP_IRQEnable(); }

I2C基本读写函数

为了与上层“gt9xx.c”驱动文件中的函数对接,本实验中的I2C读写函数与EEPROM实验中的有稍微不同,见 代码清单:触摸-7 。

代码清单:触摸-7 I2C基本读写函数(bsp_i2c_touch.c文件)¶ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145__IO uint32_t I2CTimeout = I2CT_LONG_TIMEOUT; /** * @brief IIC等待超时调用本函数输出调试信息 * @param None. * @retval 返回0xff,表示IIC读取数据失败 */ static uint32_t I2C_TIMEOUT_UserCallback(uint8_t errorCode) { /* Block communication and all processes */ GTP_ERROR("I2C 等待超时!errorCode = %d",errorCode); return 0xFF; } /** * @brief 使用IIC读取数据 * @param * @arg ClientAddr:从设备地址 * @arg pBuffer:存放由从机读取的数据的缓冲区指针 * @arg NumByteToRead:读取的数据长度 * @retval 无 */ uint32_t I2C_ReadBytes(uint8_t ClientAddr,uint8_t* pBuffer, uint16_t NumByteToRead) { I2CTimeout = I2CT_LONG_TIMEOUT; while (I2C_GetFlagStatus(GTP_I2C, I2C_FLAG_BUSY)) { if ((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(0); } /* Send STRAT condition */ I2C_GenerateSTART(GTP_I2C, ENABLE); I2CTimeout = I2CT_FLAG_TIMEOUT; /* Test on EV5 and clear it */ while (!I2C_CheckEvent(GTP_I2C, I2C_EVENT_MASTER_MODE_SELECT)) { if ((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(1); } /* Send GT91xx address for read */ I2C_Send7bitAddress(GTP_I2C, ClientAddr, I2C_Direction_Receiver); I2CTimeout = I2CT_FLAG_TIMEOUT; /* Test on EV6 and clear it */ while (!I2C_CheckEvent(GTP_I2C, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)) { if ((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(2); } /* While there is data to be read */ while (NumByteToRead) { if (NumByteToRead == 1) { /* Disable Acknowledgement */ I2C_AcknowledgeConfig(GTP_I2C, DISABLE); /* Send STOP Condition */ I2C_GenerateSTOP(GTP_I2C, ENABLE); } I2CTimeout = I2CT_LONG_TIMEOUT; while (I2C_CheckEvent(GTP_I2C, I2C_EVENT_MASTER_BYTE_RECEIVED)==0) { if ((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(3); } { /* Read a byte from the device */ *pBuffer = I2C_ReceiveData(GTP_I2C); /* Point to the next location where the byte read will be saved */ pBuffer++; /* Decrement the read bytes counter */ NumByteToRead--; } } /* Enable Acknowledgement to be ready for another reception */ I2C_AcknowledgeConfig(GTP_I2C, ENABLE); return 0; } /** * @brief 使用IIC写入数据 * @param * @arg ClientAddr:从设备地址 * @arg pBuffer:缓冲区指针 * @arg NumByteToWrite:写的字节数 * @retval 无 */ uint32_t I2C_WriteBytes(uint8_t ClientAddr,uint8_t* pBuffer, uint8_t NumByteToWrite) { I2CTimeout = I2CT_LONG_TIMEOUT; while (I2C_GetFlagStatus(GTP_I2C, I2C_FLAG_BUSY)) { if ((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(4); } /* Send START condition */ I2C_GenerateSTART(GTP_I2C, ENABLE); I2CTimeout = I2CT_FLAG_TIMEOUT; /* Test on EV5 and clear it */ while (!I2C_CheckEvent(GTP_I2C, I2C_EVENT_MASTER_MODE_SELECT)) { if ((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(5); } /* Send GT91xx address for write */ I2C_Send7bitAddress(GTP_I2C, ClientAddr, I2C_Direction_Transmitter); I2CTimeout = I2CT_FLAG_TIMEOUT; /* Test on EV6 and clear it */ while(!I2C_CheckEvent(GTP_I2C,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) { if ((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(6); } /* While there is data to be written */ while (NumByteToWrite--) { /* Send the current byte */ I2C_SendData(GTP_I2C, *pBuffer); /* Point to the next byte to be written */ pBuffer++; I2CTimeout = I2CT_FLAG_TIMEOUT; /* Test on EV8 and clear it */ while (!I2C_CheckEvent(GTP_I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTED)) { if ((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(7); } } /* Send STOP condition */ I2C_GenerateSTOP(GTP_I2C, ENABLE); return 0; }

为了与后面的gt9xx.c文件的读写接口兼容,这里的读写函数都是很纯粹的I2C通讯过程,即读函数只有读过程,不包含发送寄存器地址的过程, 而写函数也是只有写过程,没有包含寄存器的地址,大家可以对比一下它们与前面EEPROM实验中的差别。这两个函数都只包含从I2C的设备地址、缓冲区指针以及数据量。

Linux的I2C驱动接口

使用前面的基本读写函数,主要是为了对接原“gt9xx.c”驱动里使用的Linux I2C接口函数I2C_Transfer,实现了这个函数后, 移植时就可以减少“gt9xx.c”文件的修改量。I2C_Transfer函数见 代码清单:触摸-8 。

代码清单:触摸-8 Linux的I2C驱动接口(gt9xx.c文件)¶ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51/* 表示读数据 */ #define I2C_M_RD 0x0001 /* * 存储I2C通讯的信息 * @addr: 从设备的I2C设备地址 * @flags: 控制标志 * @len: 读写数据的长度 * @buf: 存储读写数据的指针 **/ struct i2c_msg { uint8_t addr; /*从设备的I2C设备地址 */ uint16_t flags; /*控制标志*/ uint16_t len; /*读写数据的长度 */ uint8_t *buf; /*存储读写数据的指针 */ }; /** * @brief 使用IIC进行数据传输 * @param * @arg i2c_msg:数据传输结构体 * @arg num:数据传输结构体的个数 * @retval 正常完成的传输结构个数,若不正常,返回0xff */ static int I2C_Transfer( struct i2c_msg *msgs,int num) { int im = 0; int ret = 0; //输出调试信息,可忽略 GTP_DEBUG_FUNC(); for (im = 0; ret == 0 && im != num; im++) { //根据flag判断是读数据还是写数据 if ((msgs[im].flags&I2C_M_RD)) { //IIC读取数据 ret = I2C_ReadBytes(msgs[im].addr, msgs[im].buf, msgs[im].len); } else { //IIC写入数据 ret = I2C_WriteBytes(msgs[im].addr, msgs[im].buf, msgs[im].len); } } if (ret) return ret; return im; //正常完成的传输结构个数 }

I2C_Transfer的主要输入参数是i2c_msg结构体的指针以及要传输多少个这样的结构体。i2c_msg结构体包含以下几个成员:

addr

这是从机的I2C设备地址,通讯时无论是读方向还是写方向,给这个成员赋值为写地址即可(本实验中为0xBA)。

flags

这个成员存储了控制标志,它用于指示本i2c_msg结构体要求以什么方式来传输。在原Linux驱动中有很多种控制方式,在我们这个工程中, 只支持读或写控制标志,flags被赋值为I2C_M_RD宏的时候表示读方向,其余值表示写方向。

len

本成员存储了要读写的数据长度。

buf

本成员存储了指向读写数据缓冲区的指针。

利用这个结构体,我们再来看I2C_Transfer函数做了什么工作。

(1) 输入参数中可能包含有多个要传输的i2c_msg结构体, 利用for循环把这些结构体一个个地传输出去;

(2) 传输的时候根据i2c_msg结构体中的flags标志,确定应该调用I2C读函数还是写函数, 这些函数即前面定义的I2C基本读写函数。调用这些函数的时候,以i2c_msg结构体的成员作为参数。

I2C复合读写函数

理解了I2C_Transfer函数的代码,我们发现它还是什么都没做,只是对I2C基本读写函数封装了比较特别的调用形式而已, 而我们知道GT9157触控芯片都有很多不同的寄存器,如果我们仅用上面的函数,如何向特定寄存器写入参数或读取特定寄存器的内容呢? 这就需要再利用I2C_Transfer函数编写具有I2C通讯复合时序的读写函数了。Linux驱动进行这样的封装是为了让它的核心层与具体设备独立开来, 对于这个巨型系统,这样写代码是很有必要的,上述的I2C_Transfer函数属于Linux内部的驱动层,它对外提供接口,而像GT9157、 EEPROM等使用I2C的设备,都利用这个接口编写自己具体的驱动文件, GT9157的这些I2C复合读写函数见 代码清单:触摸-9 。

代码清单:触摸-9 I2C复合读写函数(gt9xx.c文件)¶ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91//寄存器地址的长度 #define GTP_ADDR_LENGTH 2 /** * @brief 从IIC设备中读取数据 * @param * @arg client_addr:设备地址 * @arg buf[0~1]: 读取数据寄存器的起始地址 * @arg buf[2~len-1]: 存储读出来数据的缓冲buffer * @arg len: GTP_ADDR_LENGTH + read bytes count( 寄存器地址长度+读取的数据字节数) * @retval i2c_msgs传输结构体的个数,2为成功,其它为失败 */ static int32_t GTP_I2C_Read(uint8_t client_addr, uint8_t *buf, int32_t len) { struct i2c_msg msgs[2]; int32_t ret=-1; int32_t retries = 0; //输出调试信息,可忽略 GTP_DEBUG_FUNC(); /*一个读数据的过程可以分为两个传输过程: * 1. IIC 写入 要读取的寄存器地址 * 2. IIC 读取 数据 * */ msgs[0].flags = !I2C_M_RD; //写入 msgs[0].addr = client_addr; //IIC设备地址 msgs[0].len = GTP_ADDR_LENGTH; //寄存器地址为2字节(即写入两字节的数据) msgs[0].buf = &buf[0]; //buf[0~1]存储的是要读取的寄存器地址 msgs[1].flags = I2C_M_RD; //读取 msgs[1].addr = client_addr; //IIC设备地址 msgs[1].len = len - GTP_ADDR_LENGTH; //要读取的数据长度 msgs[1].buf = &buf[GTP_ADDR_LENGTH]; //buf[GTP_ADDR_LENGTH]之后的缓冲区存储读出的数据 while (retries = 5)) { //发送失败,输出调试信息 GTP_ERROR("I2C Read Error"); } return ret; } /** * @brief 向IIC设备写入数据 * @param * @arg client_addr:设备地址 * @arg buf[0~1]: 要写入的数据寄存器的起始地址 * @arg buf[2~len-1]: 要写入的数据 * @arg len: GTP_ADDR_LENGTH + write bytes count( 寄存器地址长度+写入的数据字节数) * @retval i2c_msgs传输结构体的个数,1为成功,其它为失败 */ static int32_t GTP_I2C_Write(uint8_t client_addr,uint8_t *buf, int32_t len) { struct i2c_msg msg; int32_t ret = -1; int32_t retries = 0; //输出调试信息,可忽略 GTP_DEBUG_FUNC(); /*一个写数据的过程只需要一个传输过程: * 1. IIC连续 写入 数据寄存器地址及数据 * */ msg.flags = !I2C_M_RD; //写入 msg.addr = client_addr; //从设备地址 msg.len = len; //长度直接等于(寄存器地址长度+写入的数据字节数) msg.buf = buf; //直接连续写入缓冲区中的数据(包括了寄存器地址) while (retries = 5)) { //发送失败,输出调试信息 GTP_ERROR("I2C Write Error"); } return ret; }

可以看到,复合读写函数都包含有client_addr、buf及len输入参数,其中client_addr表示I2C的设备地址,buf存储了要读写的寄存器地址及数据,len表示buf的长度。 在函数的内部处理中,复合读写过程被分解成两个基本的读写过程,输入参数被转化存储到i2c_msg结构体中,每个基本读写过程使用一个i2c_msg结构体来表示, 见表 复合读过程的步骤分解 和表 复合写过程的步骤分解 。

复合过程的分解主要是针对寄存器地址传输和实际数据传输来划分的,调用这两个复合读写过程的时候, 我们需要注意buf的前两个字节为寄存器地址,且len的长度为buf的整体长度。

读取触控芯片的产品ID及版本号

利用上述复合读写函数,我们就可以使用I2C控制触控芯片了,首先是最简单的读取版本函数,见 代码清单:触摸-10 。

代码清单:触摸-10读取触控芯片的产品ID及版本号(gt9xx.c文件)¶ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42/*设定使用的电容屏IIC设备地址*/ #define GTP_ADDRESS 0xBA //芯片版本号地址 #define GTP_REG_VERSION 0x8140 /******************************************************* Function: Read chip version. Input: client: i2c device version: buffer to keep ic firmware version Output: read operation return. 2: succeed, otherwise: failed *******************************************************/ int32_t GTP_Read_Version(void) { int32_t ret = -1; //寄存器地址 uint8_t buf[8] = {GTP_REG_VERSION >> 8, GTP_REG_VERSION & 0xff}; //输出调试信息,可忽略 GTP_DEBUG_FUNC(); ret = GTP_I2C_Read(GTP_ADDRESS, buf, sizeof(buf)); if (ret > 8, GTP_REG_CONFIG_DATA & 0xff}; TOUCH_IC touchIC; /******************************************************* Function: Initialize gtp. Input: ts: goodix private data Output: Executive outcomes. 0: succeed, otherwise: failed *******************************************************/ int32_t GTP_Init_Panel(void) { int32_t ret = -1; int32_t i = 0; uint8_t check_sum = 0; int32_t retry = 0; uint8_t* cfg_info; uint8_t cfg_info_len ; uint8_t cfg_num =0x80FE-0x8047+1 ; //需要配置的寄存器个数 GTP_DEBUG_FUNC(); I2C_Touch_Init(); ret = GTP_I2C_Test(); if (ret 8, GTP_READ_COOR_ADDR & 0xFF, 0}; uint8_t point_data[2 + 1 + 8 * GTP_MAX_TOUCH + 1]= {GTP_READ_COOR_ADDR >> 8, GTP_READ_COOR_ADDR & 0xFF }; uint8_t touch_num = 0; uint8_t finger = 0; static uint16_t pre_touch = 0; static uint8_t pre_id[GTP_MAX_TOUCH] = {0}; uint8_t client_addr=GTP_ADDRESS; uint8_t* coor_data = NULL; int32_t input_x = 0; int32_t input_y = 0; int32_t input_w = 0; uint8_t id = 0; int32_t i = 0; int32_t ret = -1; GTP_DEBUG_FUNC(); ret = GTP_I2C_Read(client_addr, point_data, 12);//10字节寄存器加2字节地址 if (ret GTP_MAX_TOUCH) { goto exit_work_func;//大于最大支持点数,错误退出 } if (touch_num > 1)//不止一个点 { uint8_t buf[8 * GTP_MAX_TOUCH] = {(GTP_READ_COOR_ADDR + 10) >> 8, (GTP_READ_COOR_ADDR + 10) & 0xff}; ret = GTP_I2C_Read(client_addr, buf, 2 + 8 * (touch_num - 1)); //复制其余点数的数据到point_data memcpy(&point_data[12], &buf[2], 8 * (touch_num - 1)); } if (pre_touch>touch_num) //pre_touch>touch_num,表示有的点释放了 { for (i = 0; i


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有