当前位置:   article > 正文

使用电容触摸屏做画板——软件IIC(二)_触摸iic

触摸iic

使用电容触摸屏时发现硬件IIC会使电容触摸屏卡死,经过调试发现软件IIC更加好用,那么下面就了解一下软件IIC叭。

IIC协议

  1. //先定义引脚的高低电平
  2. #define IIC_SDA_1 GPIO_SetBits(EEPROM_I2C_SDA_GPIO_PORT,EEPROM_I2C_SDA_PIN)
  3. #define IIC_SDA_0 GPIO_ResetBits(EEPROM_I2C_SDA_GPIO_PORT,EEPROM_I2C_SDA_PIN)
  4. #define IIC_SCL_1 GPIO_SetBits(EEPROM_I2C_SCL_GPIO_PORT,EEPROM_I2C_SCL_PIN)
  5. #define IIC_SCL_0 GPIO_ResetBits(EEPROM_I2C_SCL_GPIO_PORT,EEPROM_I2C_SCL_PIN)
  6. //由于是在IIC读写EEPROM那边改的,所以引脚的宏并没有改

那么就模拟起始信号写 一个引脚电平的变换:

  1. static void IIC_Start(void)
  2. {
  3. IIC_SDA_1; //保持高电平
  4. IIC_SCL_1;
  5. IIC_Delay(); //延迟
  6. IIC_SDA_0; //SDA低电平,起始信号
  7. IIC_Delay();
  8. IIC_SCL_0; //SCL低电平,开始工作
  9. IIC_Delay();
  10. }

其中的延时函数:

  1. void IIC_Delay(void)
  2. {
  3. uint8_t i ;
  4. for(i=0;i<50;i++);
  5. }

(2)模拟停止信号:SCL为高电平,SDA由低电平变为高电平。

  1. static void IIC_Stop(void)
  2. {
  3. IIC_SDA_0;
  4. IIC_SCL_1;
  5. IIC_Delay();
  6. IIC_SDA_1;
  7. IIC_Delay();
  8. IIC_SCL_1;
  9. IIC_Delay();
  10. }

(3)数据有效时读取数据:SCL高电平、读取SDA数据

每次采样一个字节一共八位,当SCL高电平时,SDA为高电平时数据有效。

  1. //定义一个读取引脚的宏
  2. #define IIC_READ_SDA() GPIO_ReadInputDataBit(EEPROM_I2C_SDA_GPIO_PORT,EEPROM_I2C_SDA_PIN)

 读取数据函数:

  1. static uint8_t IIC_ReadByte(void)
  2. {
  3. uint8_t i;
  4. uint8_t value = 0;
  5. for(i=0;i<8;i++)
  6. {
  7. //要将高位的数据往左移
  8. value <<= 1 ;
  9. IIC_SCL_1;
  10. IIC_Delay();
  11. if(IIC_READ_SDA()) //SDA为高电平时数据有效
  12. {
  13. value++;
  14. }
  15. else
  16. {
  17. }
  18. //读取一个字节后要变为低电平
  19. IIC_SCL_0;
  20. IIC_Delay();
  21. }
  22. return value;
  23. }

(4)发送一个数据:收发的电平信号与读取一样。

  1. static void IIC_SendByte(uint8_t data)
  2. {
  3. uint8_t i;
  4. for(i=0;i<8;i++)
  5. {
  6. //每一次先发高位数据
  7. if(data &0x80)
  8. {
  9. IIC_SDA_1;
  10. }
  11. else
  12. {
  13. IIC_SDA_0;
  14. }
  15. IIC_Delay();
  16. //拉高一段时间
  17. IIC_SCL_1;
  18. IIC_Delay();
  19. //再拉低
  20. IIC_SCL_0;
  21. IIC_Delay();
  22. //将数据左移使其置为高位
  23. data <<= 1 ;
  24. //释放总线
  25. if(i==7)
  26. {
  27. //相当于产生一个停止信号
  28. IIC_SDA_1;
  29. }
  30. }
  31. }

(5)发送非应答/应答信号:当SCL处于一个高电平的时钟里面,等待响应,当SDA表现为高电平时,表现为非应答信号。

  1. static void IIC_NACK(void)
  2. {
  3. IIC_SDA_1;
  4. IIC_Delay();
  5. IIC_SCL_1;
  6. IIC_Delay();
  7. IIC_SCL_0;
  8. IIC_Delay();
  9. }

当SCL处于一个高电平的时钟里面,等待响应,当SDA表现为低电平时,表现为应答信号。

  1. static void IIC_ACK(void)
  2. {
  3. IIC_SDA_0;
  4. IIC_Delay();
  5. IIC_SCL_1;
  6. IIC_Delay();
  7. IIC_SCL_0;
  8. IIC_Delay();
  9. //释放总线
  10. IIC_SDA_1;
  11. }

 (6)等待应答信号

  1. static uint8_t IIC_Wait_ACK(void)
  2. {
  3. uint8_t ack_value;
  4. //释放控制权
  5. IIC_SDA_1;
  6. IIC_Delay();
  7. //拉高进行读取
  8. IIC_SCL_1;
  9. IIC_Delay();
  10. //判断应答还是非应答
  11. if(IIC_READ_SDA())
  12. ack_value = 1;
  13. else
  14. ack_value = 0;
  15. IIC_SCL_0;
  16. IIC_Delay();
  17. return ack_value;
  18. }

接下来使用软件IIC控制液晶屏:

一、初始化引脚

 

 对所有要使用的引脚进行初始化:

  1. /*设定使用的电容屏 IIC 设备地址*/
  2. #define GTP_ADDRESS 0xBA
  3. #define I2CT_FLAG_TIMEOUT ((uint32_t)0x1000)
  4. #define I2CT_LONG_TIMEOUT ((uint32_t)(10 * I2CT_FLAG_TIMEOUT))
  5. /*I2C 引脚*/
  6. #define GTP_I2C I2C2
  7. #define GTP_I2C_CLK RCC_APB1Periph_I2C2
  8. #define GTP_I2C_CLK_INIT RCC_APB1PeriphClockCmd
  9. #define GTP_I2C_SCL_PIN GPIO_Pin_4
  10. #define GTP_I2C_SCL_GPIO_PORT GPIOH
  11. #define GTP_I2C_SCL_GPIO_CLK RCC_AHB1Periph_GPIOH
  12. #define GTP_I2C_SCL_SOURCE GPIO_PinSource4
  13. #define GTP_I2C_SCL_AF GPIO_AF_I2C2
  14. #define GTP_I2C_SDA_PIN GPIO_Pin_5
  15. #define GTP_I2C_SDA_GPIO_PORT GPIOH
  16. #define GTP_I2C_SDA_GPIO_CLK RCC_AHB1Periph_GPIOH
  17. #define GTP_I2C_SDA_SOURCE GPIO_PinSource5
  18. #define GTP_I2C_SDA_AF GPIO_AF_I2C2
  19. /*复位引脚*/
  20. #define GTP_RST_GPIO_PORT GPIOI
  21. #define GTP_RST_GPIO_CLK RCC_AHB1Periph_GPIOI
  22. #define GTP_RST_GPIO_PIN GPIO_Pin_8
  23. /*中断引脚*/
  24. #define GTP_INT_GPIO_PORT GPIOD
  25. #define GTP_INT_GPIO_CLK RCC_AHB1Periph_GPIOD
  26. #define GTP_INT_GPIO_PIN GPIO_Pin_13
  27. #define GTP_INT_EXTI_PORTSOURCE EXTI_PortSourceGPIOD
  28. #define GTP_INT_EXTI_PINSOURCE EXTI_PinSource13
  29. #define GTP_INT_EXTI_LINE EXTI_Line13
  30. #define GTP_INT_EXTI_IRQ EXTI15_10_IRQn
  31. /*中断服务函数*/
  32. #define GTP_IRQHandler EXTI15_10_IRQHandler
  33. //初始化触摸屏使用的I2C信号线,并且把RET与INT引脚也初始化为下拉推挽输出模式,以便刚上电的时候输出上电时序,设置触摸屏的I2C设备地址
  34. static void I2C_GPIO_Config(void)
  35. {
  36. GPIO_InitTypeDef GPIO_InitStructure;
  37. /*使能IIC时钟 */
  38. RCC_APB1PeriphClockCmd(GTP_I2C_CLK, ENABLE);
  39. /*使能触摸屏使用的引脚的时钟 */
  40. RCC_AHB1PeriphClockCmd(GTP_I2C_SCL_GPIO_CLK | GTP_I2C_SDA_GPIO_CLK|GTP_RST_GPIO_CLK|GTP_INT_GPIO_CLK, ENABLE);
  41. RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
  42. /* 配置I2C_SCL源*/
  43. GPIO_PinAFConfig(GTP_I2C_SCL_GPIO_PORT,
  44. GTP_I2C_SCL_SOURCE, GTP_I2C_SCL_AF);
  45. /* 配置I2C_SDA 源*/
  46. GPIO_PinAFConfig(GTP_I2C_SDA_GPIO_PORT,
  47. GTP_I2C_SDA_SOURCE, GTP_I2C_SDA_AF);
  48. /*配置SCL引脚 */
  49. GPIO_InitStructure.GPIO_Pin = GTP_I2C_SCL_PIN;
  50. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
  51. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  52. GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
  53. GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
  54. GPIO_Init(GTP_I2C_SCL_GPIO_PORT, &GPIO_InitStructure);
  55. /*配置SDA引脚 */
  56. GPIO_InitStructure.GPIO_Pin = GTP_I2C_SDA_PIN;
  57. GPIO_Init(GTP_I2C_SDA_GPIO_PORT, &GPIO_InitStructure);
  58. /*!< Configure RST */
  59. GPIO_InitStructure.GPIO_Pin = GTP_RST_GPIO_PIN;
  60. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
  61. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  62. GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  63. GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;
  64. GPIO_Init(GTP_RST_GPIO_PORT, &GPIO_InitStructure);
  65. /*!< Configure INT */
  66. GPIO_InitStructure.GPIO_Pin = GTP_INT_GPIO_PIN;
  67. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
  68. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  69. GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  70. GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN; //设置为下拉,方便初始化
  71. GPIO_Init(GTP_INT_GPIO_PORT, &GPIO_InitStructure);

通过RST引脚与INT引脚确定设备地址以及配置IIC模式:最开始这INT引脚要配置为输出模式,后面要输入,实现对液晶屏的上电时序控制。

  1. void I2C_ResetChip(void)
  2. {
  3. GPIO_InitTypeDef GPIO_InitStructure;
  4. /*!< Configure INT */
  5. GPIO_InitStructure.GPIO_Pin = GTP_INT_GPIO_PIN;
  6. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
  7. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  8. GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  9. GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN; //设置为下拉,方便初始化
  10. GPIO_Init(GTP_INT_GPIO_PORT, &GPIO_InitStructure);
  11. /*初始化GT9157,rst为高电平,int为低电平,则gt9157的设备地址被配置为0xBA*/
  12. //这段函数中控制RST引脚由低电平改变至高电平,且期间按INT引脚一直为低电平这样的上电时序使控制芯片的I2C写地址为0xBA,读地址为0xBB,可以写为(0xBA|0x01)
  13. /*复位为低电平,为初始化做准备*/
  14. GPIO_ResetBits (GTP_RST_GPIO_PORT,GTP_RST_GPIO_PIN);
  15. Delay(0x0FFFFF);
  16. /*拉高一段时间,进行初始化*/
  17. GPIO_SetBits (GTP_RST_GPIO_PORT,GTP_RST_GPIO_PIN);
  18. Delay(0x0FFFFF);
  19. /*把INT引脚设置为浮空输入模式*/
  20. /*!< Configure INT */
  21. //使其可以接收触控芯片输出的触摸中断信号
  22. GPIO_InitStructure.GPIO_Pin = GTP_INT_GPIO_PIN;
  23. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
  24. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  25. GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
  26. GPIO_Init(GTP_INT_GPIO_PORT, &GPIO_InitStructure);
  27. }

配置中断:

  1. //INT引脚配置为上升沿触发
  2. void I2C_GTP_IRQEnable(void)
  3. {
  4. EXTI_InitTypeDef EXTI_InitStructure;
  5. NVIC_InitTypeDef NVIC_InitStructure;
  6. /*配置 INT 为浮空输入 */
  7. GPIO_InitStructure.GPIO_Pin = GTP_INT_GPIO_PIN;
  8. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
  9. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  10. GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
  11. GPIO_Init(GTP_INT_GPIO_PORT, &GPIO_InitStructure);
  12. /* 连接 EXTI 中断源 到 INT 引脚 */
  13. SYSCFG_EXTILineConfig(GTP_INT_EXTI_PORTSOURCE, GTP_INT_EXTI_PINSOURCE);
  14. /* 选择 EXTI 中断源 */
  15. EXTI_InitStructure.EXTI_Line = GTP_INT_EXTI_LINE;
  16. EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
  17. EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
  18. EXTI_InitStructure.EXTI_LineCmd = ENABLE;
  19. EXTI_Init(&EXTI_InitStructure);
  20. /* 配置中断优先级 */
  21. NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1)
  22. /*使能中断*/
  23. NVIC_InitStructure.NVIC_IRQChannel = GTP_INT_EXTI_IRQ;
  24. NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
  25. NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
  26. NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  27. NVIC_Init(&NVIC_InitStructure);
  28. }
  1. void SysTick_Handler(void)
  2. {
  3. static uint8_t timecount=0;
  4. if(timecount>=10)
  5. {
  6. timecount=0;
  7. GTP_TouchProcess();
  8. }
  9. TimingDelay_Decrement();
  10. timecount++;
  11. }

这个中断用于触摸处理。具体的函数是在gt9xx.c文件中,是触摸屏基于Linux给的一个驱动文件,函数作用用于判断现在触摸点于哪个位置。

配置I2C模式,FT9157使用的是标准7位地址模式的I2C通讯:

  1. /* STM32 I2C 快速模式 */
  2. #define I2C_Speed 400000
  3. /* 这个地址只要与STM32外挂的I2C器件地址不一样即可 */
  4. #define I2C_OWN_ADDRESS7 0x0A
  5. static void I2C_Mode_Config(void)
  6. {
  7. I2C_InitTypeDef I2C_InitStructure;
  8. /* I2C 配置 */
  9. I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
  10. I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2; /* 高电平数据稳定,低电平数据变化 SCL 时钟线的占空比 */
  11. I2C_InitStructure.I2C_OwnAddress1 =I2C_OWN_ADDRESS7;
  12. I2C_InitStructure.I2C_Ack = I2C_Ack_Enable ;
  13. I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
  14. /* I2C的寻址模式 */
  15. I2C_InitStructure.I2C_ClockSpeed = I2C_Speed;
  16. /* 通信速率 */
  17. I2C_Init(GTP_I2C, &I2C_InitStructure); /* I2C1 初始化 */
  18. I2C_Cmd(GTP_I2C, ENABLE); /* 使能 I2C1 */
  19. I2C_AcknowledgeConfig(GTP_I2C, ENABLE);
  20. }

 最后封装一下初始化所需要的函数:

  1. void I2C_Touch_Init(void)
  2. {
  3. I2C_GPIO_Config();
  4. I2C_Mode_Config();
  5. I2C_ResetChip();
  6. I2C_GTP_IRQEnable();
  7. }

下面的函数都是移植了Linux(gt9xx.c)中的驱动函数:

在函数中有一个结构体数组,在IIC中有读写复合的方式进行通讯,上面的函数正表示了这一点,在Linux驱动中通过将写入与读取封装在一个结构体数组中,进行了这种复合方式。

  1. //寄存器地址的长度
  2. #define GTP_ADDR_LENGTH 2
  3. /**
  4. * @brief 从IIC设备中读取数据
  5. * @param
  6. * @arg client_addr:设备地址
  7. * @arg buf[0~1]: 读取数据寄存器的起始地址
  8. * @arg buf[2~len-1]: 存储读出来数据的缓冲buffer
  9. * @arg len: GTP_ADDR_LENGTH + read bytes count(寄存器地址长度+读取的数据字节数)
  10. * @retval i2c_msgs传输结构体的个数,2为成功,其它为失败
  11. */
  12. static int32_t GTP_I2C_Read(uint8_t client_addr, uint8_t *buf, int32_t len)
  13. {
  14. struct i2c_msg msgs[2];
  15. int32_t ret=-1;
  16. int32_t retries = 0;
  17. GTP_DEBUG_FUNC();
  18. /*一个读数据的过程可以分为两个传输过程:
  19. * 1. IIC 写入 要读取的寄存器地址
  20. * 2. IIC 读取 数据
  21. * */
  22. msgs[0].flags = !I2C_M_RD; //写入
  23. msgs[0].addr = client_addr; //IIC设备地址
  24. msgs[0].len = GTP_ADDR_LENGTH; //寄存器地址为2字节(即写入两字节的数据)
  25. msgs[0].buf = &buf[0]; //buf[0~1]存储的是要读取的寄存器地址
  26. msgs[1].flags = I2C_M_RD; //读取
  27. msgs[1].addr = client_addr; //IIC设备地址
  28. msgs[1].len = len - GTP_ADDR_LENGTH; //要读取的数据长度
  29. msgs[1].buf = &buf[GTP_ADDR_LENGTH]; //buf[GTP_ADDR_LENGTH]之后的缓冲区存储读出的数据
  30. while(retries < 5)
  31. {
  32. ret = I2C_Transfer( msgs, 2); //调用IIC数据传输过程函数,有2个传输过程
  33. if(ret == 2)break;
  34. retries++;
  35. }
  36. if((retries >= 5))
  37. {
  38. GTP_ERROR("I2C Read: 0x%04X, %d bytes failed, errcode: %d! Process reset.", (((uint16_t)(buf[0] << 8)) | buf[1]), len-2, ret);
  39. }
  40. return ret;
  41. }
  42. //需要注意的是,其中buf的前两个字节表示寄存器地址,且len的长度为buf的整体长度

 复合读过程的步骤:

复合写过程的步骤:

 其中提到的通讯结构体:

  1. /* 表示读数据 */
  2. #define I2C_M_RD 0x0001
  3. /*
  4. * 存储I2C通讯的信息
  5. * @addr: 从设备的I2C设备地址
  6. * @flags: 控制标志
  7. * @len: 读写数据的长度
  8. * @buf: 存储读写数据的指针
  9. **/
  10. struct i2c_msg {
  11. uint8_t addr; /*从设备的I2C设备地址 */
  12. uint16_t flags; /*控制标志*/
  13. uint16_t len; /*读写数据的长度*/
  14. uint8_t *buf; /*存储读写数据的指针 */
  15. };
  • addr:从机的IIC设备地址,通讯时无论是读方向还是写方向,给这个成员赋值为写地址即可(0xBA)。

  • flags:存储了控制标志,用于指示i2c_msg结构体要求以什么方式来传输(读or写)。在原来的Linux驱动中有很多种控制方式,在这里被赋值为I2C_M_RD表示读。

  • len:数据长度。

  • buf:存储了指向读写数据缓冲区的指针。

在读取数据时,使用以下的函数,判断结构体数组中是读取的数据包还是要接收的数据包,这样就使读取与写入都可以封装在一个函数中,牛:I2C_Transfer的主要输入参数是i2c_msg结构体的指针以及要传输多少个这样的结构体,属于Linux内部的驱动层,对外提供接口。

 

  1. /**
  2. * @brief 使用IIC进行数据传输
  3. * @param
  4. * @arg i2c_msg:数据传输结构体
  5. * @arg num:数据传输结构体的个数
  6. * @retval 正常完成的传输结构个数,若不正常,返回0xff
  7. */
  8. static int I2C_Transfer( struct i2c_msg *msgs,int num)
  9. {
  10. int im = 0;
  11. int ret = 0;
  12. GTP_DEBUG_FUNC();
  13. //将结构体一个个地传输出去
  14. for (im = 0; ret == 0 && im != num; im++)
  15. {
  16. if ((msgs[im].flags&I2C_M_RD)) //根据flag判断是读数据还是写数据
  17. {
  18. ret = I2C_ReadBytes(msgs[im].addr, msgs[im].buf, msgs[im].len); //IIC读取数据
  19. } else
  20. {
  21. ret = I2C_WriteBytes(msgs[im].addr, msgs[im].buf, msgs[im].len); //IIC写入数据
  22. }
  23. }
  24. if(ret)
  25. return ret;
  26. return im; //正常完成的传输结构个数
  27. }

后面的读取数据与接收数据都使用软件IIC的方式进行:

  1. /**
  2. * @brief 使用IIC读取数据
  3. * @param
  4. * @arg ClientAddr:从设备地址
  5. * @arg pBuffer:存放由从机读取的数据的缓冲区指针
  6. * @arg NumByteToRead:读取的数据长度
  7. * @retval 无
  8. */
  9. uint32_t I2C_ReadBytes(uint8_t ClientAddr,uint8_t* pBuffer, uint16_t NumByteToRead)
  10. {
  11. /* 第1步:发起I2C总线启动信号 */
  12. i2c_Start();
  13. /* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
  14. i2c_SendByte(ClientAddr | I2C_DIR_RD); /* 此处是读指令 */
  15. /* 第3步:等待ACK */
  16. if (i2c_WaitAck() != 0)
  17. {
  18. goto cmd_fail; /* 器件无应答 */
  19. }
  20. while(NumByteToRead)
  21. {
  22. if(NumByteToRead == 1)
  23. {
  24. i2c_NAck(); /* 最后1个字节读完后,CPU产生NACK信号(驱动SDA = 1) */
  25. /* 发送I2C总线停止信号 */
  26. i2c_Stop();
  27. }
  28. *pBuffer = i2c_ReadByte();
  29. /* 读指针自增 */
  30. pBuffer++;
  31. /*计数器自减 */
  32. NumByteToRead--;
  33. i2c_Ack(); /* 中间字节读完后,CPU产生ACK信号(驱动SDA = 0) */
  34. }
  35. /* 发送I2C总线停止信号 */
  36. i2c_Stop();
  37. return 0; /* 执行成功 */
  38. cmd_fail: /* 命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备 */
  39. /* 发送I2C总线停止信号 */
  40. i2c_Stop();
  41. return 1;
  42. }
  43. /**
  44. * @brief 使用IIC写入数据
  45. * @param
  46. * @arg ClientAddr:从设备地址
  47. * @arg pBuffer:缓冲区指针
  48. * @arg NumByteToWrite:写的字节数
  49. * @retval 无
  50. */
  51. uint32_t I2C_WriteBytes(uint8_t ClientAddr,uint8_t* pBuffer, uint8_t NumByteToWrite)
  52. {
  53. uint16_t m;
  54. /* 第0步:发停止信号,启动内部写操作 */
  55. i2c_Stop();
  56. /* 通过检查器件应答的方式,判断内部写操作是否完成, 一般小于 10ms
  57. CLK频率为200KHz时,查询次数为30次左右
  58. */
  59. for (m = 0; m < 1000; m++)
  60. {
  61. /* 第1步:发起I2C总线启动信号 */
  62. i2c_Start();
  63. /* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
  64. i2c_SendByte(ClientAddr | I2C_DIR_WR); /* 此处是写指令 */
  65. /* 第3步:发送一个时钟,判断器件是否正确应答 */
  66. if (i2c_WaitAck() == 0)
  67. {
  68. break;
  69. }
  70. }
  71. if (m == 1000)
  72. {
  73. goto cmd_fail; /* EEPROM器件写超时 */
  74. }
  75. while(NumByteToWrite--)
  76. {
  77. /* 第4步:开始写入数据 */
  78. i2c_SendByte(*pBuffer);
  79. /* 第5步:检查ACK */
  80. if (i2c_WaitAck() != 0)
  81. {
  82. goto cmd_fail; /* 器件无应答 */
  83. }
  84. pBuffer++; /* 地址增1 */
  85. }
  86. /* 命令执行成功,发送I2C总线停止信号 */
  87. i2c_Stop();
  88. return 0;
  89. cmd_fail: /* 命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备 */
  90. /* 发送I2C总线停止信号 */
  91. i2c_Stop();
  92. return 1;
  93. }

上面的两个读写函数都是很纯粹的IIC读写过程,没有包含寄存器的地址,这两个函数都通过调用数据包进行传输设备地址、缓冲区指针以及数据量。

接下来是读取触控芯片的产品ID及其版本号

由前面介绍的,寄存器是存放于0x8140这个寄存器中。

  1. //设定使用的电容屏的IIC设备地址
  2. #define GTP_ADDRESS 0XBA
  3. //芯片版本号地址
  4. #define GTP_REG_VERSION 0X8140
  5. int32_t GTP_Read_Version(void)
  6. {
  7. int32_t ret = -1;
  8. uint8_t buf[8] = {GTP_REG_VERSION >> 8, GTP_REG_VERSION & 0xff}; //寄存器地址
  9. GTP_DEBUG_FUNC();
  10. ret = GTP_I2C_Read(GTP_ADDRESS, buf, sizeof(buf));
  11. if (ret < 0)
  12. {
  13. GTP_ERROR("GTP read version failed");
  14. return ret;
  15. }
  16. if (buf[4] == '1')
  17. {
  18. //GT911芯片
  19. if(buf[2] == '9' && buf[3] == '1' && buf[4] == '1')
  20. {
  21. GTP_INFO("IC1 Version: %c%c%c_%02x%02x", buf[2], buf[3], buf[4], buf[7], buf[6]);
  22. touchIC = GT911;
  23. /* 设置当前的液晶屏类型 */
  24. cur_lcd = INCH_7;
  25. }
  26. //GT9157芯片
  27. else
  28. GTP_INFO("Unknown IC Version: %c%c%c%c_%02x%02x", buf[2], buf[3], buf[4], buf[5], buf[7], buf[6]);
  29. }
  30. else if (buf[4] == '5')
  31. {
  32. if( buf[2] == '9' && buf[3] == '1' && buf[4] == '5' && buf[5] == '7')
  33. {
  34. GTP_INFO("IC2 Version: %c%c%c%c_%02x%02x", buf[2], buf[3], buf[4], buf[5], buf[7], buf[6]);
  35. touchIC = GT9157;
  36. /* 设置当前的液晶屏类型 */
  37. cur_lcd = INCH_5;
  38. }
  39. else
  40. GTP_INFO("Unknown IC Version: %c%c%c%c_%02x%02x", buf[2], buf[3], buf[4], buf[5], buf[7], buf[6]);
  41. }
  42. else if (buf[4] == '8')
  43. {
  44. //GT5688芯片
  45. if(buf[2] == '5' && buf[3] == '6' && buf[4] == '8' && buf[5] == '8')
  46. {
  47. GTP_INFO("IC3 Version: %c%c%c%c_%02x%02x", buf[2], buf[3], buf[4], buf[5], buf[7], buf[6]);
  48. touchIC = GT5688;
  49. /* 设置当前的液晶屏类型 */
  50. cur_lcd = INCH_4_3;
  51. }
  52. else
  53. GTP_INFO("Unknown IC Version: %c%c%c%c_%02x%02x", buf[2], buf[3], buf[4], buf[5], buf[7], buf[6]);
  54. }
  55. else if(buf[4] == '7')
  56. {
  57. //GT917S芯片
  58. GTP_INFO("IC2 Version: %c%c%c%c_%02x%02x", buf[2], buf[3], buf[4], buf[5], buf[7], buf[6]);
  59. if(buf[2] == '9' && buf[3] == '1' && buf[4] == '7' && buf[5] == 'S')
  60. {
  61. touchIC = GT917S;
  62. /* 设置当前的液晶屏类型 */
  63. cur_lcd = INCH_5;
  64. }
  65. }
  66. else
  67. GTP_INFO("Unknown IC Version: %c%c%c%c_%02x%02x", buf[2], buf[3], buf[4], buf[5], buf[7], buf[6]);
  68. return ret;
  69. }

 上面那个函数定义了一个8字节的buf数组,并且向他的第0个和第1个元素写入产品ID寄存器的地址,然后调用复合函数读取数据,之后就可以读取寄存器的信息,利用下面这个宏:

#define GTP_INFO(fmt,arg...)           printf("<<-GTP-INFO->> "fmt"\n",##arg)

之后向触控芯片写入参数

我们识别出是哪一种产品id后,我们就可以往显示屏里面写入配置参数到寄存器中:

 

 在函数中定义了一个枚举,存放了所有的产品型号:

 之后在下面这个函数进行了初始化,也调用了I2C_Touch_Init初始化了STM32的IIC外设,设定触控芯片的IIC设备地址,然后调用了上面的获取触控芯片的版本号。之后将配置参数表写入到触控芯片的配置寄存器中,在传输中包含由checksum寄存器的值,需要利用其来校验数据。

  1. int32_t GTP_Init_Panel(void)
  2. {
  3. int32_t ret = -1;
  4. int32_t i = 0;
  5. uint16_t check_sum = 0;
  6. int32_t retry = 0;
  7. const uint8_t* cfg_info;
  8. uint8_t cfg_info_len ;
  9. uint8_t* config;
  10. uint8_t cfg_num =0 ; //需要配置的寄存器个数
  11. //查错函数
  12. GTP_DEBUG_FUNC();
  13. I2C_Touch_Init();
  14. ret = GTP_I2C_Test();
  15. if (ret < 0)
  16. {
  17. GTP_ERROR("I2C communication ERROR!");
  18. return ret;
  19. }
  20. //获取触摸IC的型号
  21. GTP_Read_Version();
  22. #if UPDATE_CONFIG
  23. config = (uint8_t *)malloc (GTP_CONFIG_MAX_LENGTH + GTP_ADDR_LENGTH);
  24. config[0] = GTP_REG_CONFIG_DATA >> 8;
  25. config[1] = GTP_REG_CONFIG_DATA & 0xff;
  26. //根据IC的型号指向不同的配置
  27. if(touchIC == GT9157)
  28. {
  29. cfg_info = CTP_CFG_GT9157; //指向寄存器配置
  30. cfg_info_len = CFG_GROUP_LEN(CTP_CFG_GT9157);//计算配置表的大小
  31. }
  32. else if(touchIC == GT911)
  33. {
  34. cfg_info = CTP_CFG_GT911;//指向寄存器配置
  35. cfg_info_len = CFG_GROUP_LEN(CTP_CFG_GT911) ;//计算配置表的大小
  36. }
  37. else if(touchIC == GT5688)
  38. {
  39. cfg_info = CTP_CFG_GT5688; //指向寄存器配置
  40. cfg_info_len = CFG_GROUP_LEN(CTP_CFG_GT5688);//计算配置表的大小
  41. }
  42. else if(touchIC == GT917S)
  43. {
  44. cfg_info = CTP_CFG_GT917S; //指向寄存器配置
  45. cfg_info_len = CFG_GROUP_LEN(CTP_CFG_GT917S);//计算配置表的大小
  46. }
  47. memset(&config[GTP_ADDR_LENGTH], 0, GTP_CONFIG_MAX_LENGTH);
  48. memcpy(&config[GTP_ADDR_LENGTH], cfg_info, cfg_info_len);
  49. cfg_num = cfg_info_len;
  50. GTP_DEBUG("cfg_info_len = %d ",cfg_info_len);
  51. GTP_DEBUG("cfg_num = %d ",cfg_num);
  52. GTP_DEBUG_ARRAY(config,6);
  53. /*根据LCD的扫描方向设置分辨率*/
  54. config[GTP_ADDR_LENGTH+1] = LCD_PIXEL_WIDTH & 0xFF;
  55. config[GTP_ADDR_LENGTH+2] = LCD_PIXEL_WIDTH >> 8;
  56. config[GTP_ADDR_LENGTH+3] = LCD_PIXEL_HEIGHT & 0xFF;
  57. config[GTP_ADDR_LENGTH+4] = LCD_PIXEL_HEIGHT >> 8;
  58. /*根据模式设置X2Y交换*/
  59. //不交换
  60. // config[GTP_ADDR_LENGTH+6] &= ~(X2Y_LOC);
  61. //交换
  62. // config[GTP_ADDR_LENGTH+6] |= (X2Y_LOC);
  63. //计算要写入checksum寄存器的值
  64. check_sum = 0;
  65. /* 计算check sum校验值 */
  66. if(touchIC == GT911 || touchIC == GT9157)
  67. {
  68. for (i = GTP_ADDR_LENGTH; i < cfg_num+GTP_ADDR_LENGTH; i++)
  69. {
  70. check_sum += (config[i] & 0xFF);
  71. }
  72. config[ cfg_num+GTP_ADDR_LENGTH] = (~(check_sum & 0xFF)) + 1; //checksum
  73. config[ cfg_num+GTP_ADDR_LENGTH+1] = 1; //refresh 配置更新标志
  74. }
  75. else if(touchIC == GT5688 || touchIC == GT917S)
  76. {
  77. for (i = GTP_ADDR_LENGTH; i < (cfg_num+GTP_ADDR_LENGTH -3); i += 2)
  78. {
  79. check_sum += (config[i] << 8) + config[i + 1];
  80. }
  81. check_sum = 0 - check_sum;
  82. GTP_DEBUG("Config checksum: 0x%04X", check_sum);
  83. //更新checksum
  84. config[(cfg_num+GTP_ADDR_LENGTH -3)] = (check_sum >> 8) & 0xFF;
  85. config[(cfg_num+GTP_ADDR_LENGTH -2)] = check_sum & 0xFF;
  86. config[(cfg_num+GTP_ADDR_LENGTH -1)] = 0x01;
  87. }
  88. //写入配置信息
  89. for (retry = 0; retry < 5; retry++)
  90. {
  91. ret = GTP_I2C_Write(GTP_ADDRESS, config , cfg_num + GTP_ADDR_LENGTH+2);
  92. if (ret > 0)
  93. {
  94. break;
  95. }
  96. }
  97. Delay(0xfffff); //延迟等待芯片更新
  98. #if 1 //读出写入的数据,检查是否正常写入
  99. //检验读出的数据与写入的是否相同
  100. {
  101. uint16_t i;
  102. uint8_t buf[300];
  103. buf[0] = config[0];
  104. buf[1] =config[1]; //寄存器地址
  105. GTP_DEBUG_FUNC();
  106. ret = GTP_I2C_Read(GTP_ADDRESS, buf, sizeof(buf));
  107. GTP_DEBUG("read ");
  108. GTP_DEBUG_ARRAY(buf,cfg_num);
  109. GTP_DEBUG("write ");
  110. GTP_DEBUG_ARRAY(config,cfg_num);
  111. //不对比版本号
  112. for(i=3;i<cfg_num+GTP_ADDR_LENGTH-3;i++)
  113. {
  114. if(config[i] != buf[i])
  115. {
  116. GTP_ERROR("Config fail ! i = %d ",i);
  117. free(config);
  118. return -1;
  119. }
  120. }
  121. if(i==cfg_num+GTP_ADDR_LENGTH-3)
  122. GTP_DEBUG("Config success ! i = %d ",i);
  123. }
  124. #endif
  125. free(config);
  126. #endif
  127. /* emXGUI示例中不使能中断 */
  128. GTP_IRQ_Enable();
  129. GTP_Get_Info();
  130. return 0;
  131. }
  132. #define GTP_INFO(fmt,arg...) printf("<<-GTP-INFO->> "fmt"\n",##arg)
  133. #define GTP_ERROR(fmt,arg...) printf("<<-GTP-ERROR->> "fmt"\n",##arg)
  134. #define GTP_DEBUG(fmt,arg...) do{\
  135. if(GTP_DEBUG_ON)\
  136. printf("<<-GTP-DEBUG->> [%d]"fmt"\n",__LINE__, ##arg);\
  137. }while(0)
  138. #define GTP_DEBUG_ARRAY(array, num) do{\
  139. int32_t i;\
  140. uint8_t* a = array;\
  141. if(GTP_DEBUG_ARRAY_ON)\
  142. {\
  143. printf("<<-GTP-DEBUG-ARRAY->>\n");\
  144. for (i = 0; i < (num); i++)\
  145. {\
  146. printf("%02x ", (a)[i]);\
  147. if ((i + 1 ) %10 == 0)\
  148. {\
  149. printf("\n");\
  150. }\
  151. }\
  152. printf("\n");\
  153. }\
  154. }while(0)
  155. #define GTP_DEBUG_FUNC() do{\
  156. if(GTP_DEBUG_FUNC_ON)\
  157. printf("<<-GTP-FUNC->> Func:%s@Line:%d\n",__func__,__LINE__);\
  158. }while(0)
  159. #define GTP_SWAP(x, y) do{\
  160. typeof(x) z = x;\
  161. x = y;\
  162. y = z;\
  163. }while (0)

INT中断服务函数

  1. void SysTick_Handler(void)
  2. {
  3. static uint8_t timecount=0;
  4. if(timecount>=10)
  5. {
  6. timecount=0;
  7. //此函数的作用是读取触摸坐标
  8. GTP_TouchProcess();
  9. }
  10. TimingDelay_Decrement();
  11. timecount++;
  12. }
  13. void GTP_TouchProcess(void)
  14. {
  15. GTP_DEBUG_FUNC();
  16. Goodix_TS_Work_Func();
  17. }

核心在于下面这个函数:

  1. //状态寄存器地址
  2. #define GTP_READ_COOR_ADDR 0X814E
  3. /**
  4. * @brief 触屏处理函数,轮询或者在触摸中断调用
  5. * @param 无
  6. * @retval 无
  7. */
  8. static void Goodix_TS_Work_Func(void)
  9. {
  10. uint8_t end_cmd[3] = {GTP_READ_COOR_ADDR >> 8, GTP_READ_COOR_ADDR & 0xFF, 0};
  11. uint8_t point_data[2 + 1 + 8 * GTP_MAX_TOUCH + 1]={GTP_READ_COOR_ADDR >> 8, GTP_READ_COOR_ADDR & 0xFF};
  12. uint8_t touch_num = 0;
  13. uint8_t finger = 0;
  14. static uint16_t pre_touch = 0;
  15. static uint8_t pre_id[GTP_MAX_TOUCH] = {0};
  16. uint8_t client_addr=GTP_ADDRESS;
  17. uint8_t* coor_data = NULL;
  18. int32_t input_x = 0;
  19. int32_t input_y = 0;
  20. int32_t input_w = 0;
  21. uint8_t id = 0;
  22. int32_t i = 0;
  23. int32_t ret = -1;
  24. GTP_DEBUG_FUNC();
  25. ret = GTP_I2C_Read(client_addr, point_data, 12);//10字节寄存器加2字节地址
  26. if (ret < 0)
  27. {
  28. GTP_ERROR("I2C transfer error. errno:%d\n ", ret);
  29. return;
  30. }
  31. finger = point_data[GTP_ADDR_LENGTH];//状态寄存器数据
  32. if (finger == 0x00) //没有数据,退出
  33. {
  34. return;
  35. }
  36. if((finger & 0x80) == 0)//判断buffer status位
  37. {
  38. goto exit_work_func;//坐标未就绪,数据无效
  39. }
  40. touch_num = finger & 0x0f;//坐标点数
  41. if (touch_num > GTP_MAX_TOUCH)
  42. {
  43. goto exit_work_func;//大于最大支持点数,错误退出
  44. }
  45. if (touch_num > 1)//不止一个点
  46. {
  47. uint8_t buf[8 * GTP_MAX_TOUCH] = {(GTP_READ_COOR_ADDR + 10) >> 8, (GTP_READ_COOR_ADDR + 10) & 0xff};
  48. ret = GTP_I2C_Read(client_addr, buf, 2 + 8 * (touch_num - 1));
  49. memcpy(&point_data[12], &buf[2], 8 * (touch_num - 1)); //复制其余点数的数据到point_data
  50. }
  51. if (pre_touch>touch_num) //pre_touch>touch_num,表示有的点释放了
  52. {
  53. for (i = 0; i < pre_touch; i++) //一个点一个点处理
  54. {
  55. uint8_t j;
  56. for(j=0; j<touch_num; j++)
  57. {
  58. coor_data = &point_data[j * 8 + 3];
  59. id = coor_data[0] & 0x0F; //track id
  60. if(pre_id[i] == id)
  61. break;
  62. if(j >= touch_num-1) //遍历当前所有id都找不到pre_id[i],表示已释放
  63. {
  64. GTP_Touch_Up( pre_id[i]);
  65. }
  66. }
  67. }
  68. }
  69. if (touch_num)
  70. {
  71. for (i = 0; i < touch_num; i++) //一个点一个点处理
  72. {
  73. coor_data = &point_data[i * 8 + 3];
  74. id = coor_data[0] & 0x0F; //track id
  75. pre_id[i] = id;
  76. input_x = coor_data[1] | (coor_data[2] << 8); //x坐标
  77. input_y = coor_data[3] | (coor_data[4] << 8); //y坐标
  78. input_w = coor_data[5] | (coor_data[6] << 8); //size
  79. {
  80. GTP_Touch_Down( id, input_x, input_y, input_w);//数据处理
  81. }
  82. }
  83. }
  84. else if (pre_touch) //touch_ num=0 且pre_touch!=0
  85. {
  86. for(i=0;i<pre_touch;i++)
  87. {
  88. GTP_Touch_Up(pre_id[i]);
  89. }
  90. }
  91. pre_touch = touch_num;
  92. exit_work_func:
  93. {
  94. ret = GTP_I2C_Write(client_addr, end_cmd, 3);
  95. if (ret < 0)
  96. {
  97. GTP_INFO("I2C write end_cmd error!");
  98. }
  99. }
  100. }

这个函数内容在于,首先读取了状态寄存器,获取当前有多少个触电,然后根据触点数去读取各个点的数据,其中还有包含pre_touch的处理,保存了上一点的触点数据,利用这些数据和触电的track id号,可以确认同一条笔迹。读取后,对状态寄存器的buffer status位写0,结束读取。在这个函数中提供了两个坐标获取接口,只要在这两个接口中修改即可简单地得到了坐标信息。

触点释放和触点按下的坐标接口

  1. /**
  2. * @brief 用于处理或报告触屏检测到按下
  3. * @param
  4. * @arg id: 触摸顺序trackID
  5. * @arg x: 触摸的 x 坐标
  6. * @arg y: 触摸的 y 坐标
  7. * @arg w: 触摸的 大小
  8. * @retval 无
  9. */
  10. /*用于记录连续触摸时(长按)的上一次触摸位置,负数值表示上一次无触摸按下*/
  11. static int16_t pre_x[GTP_MAX_TOUCH] ={-1,-1,-1,-1,-1};
  12. static int16_t pre_y[GTP_MAX_TOUCH] ={-1,-1,-1,-1,-1};
  13. static void GTP_Touch_Down(int32_t id,int32_t x,int32_t y,int32_t w)
  14. {
  15. GTP_DEBUG_FUNC();
  16. /*取x、y初始值大于屏幕像素值*/
  17. GTP_DEBUG("ID:%d, X:%d, Y:%d, W:%d", id, x, y, w);
  18. /* 处理触摸按钮,用于触摸画板 */
  19. Touch_Button_Down(x,y);
  20. /*处理描绘轨迹,用于触摸画板 */
  21. Draw_Trail(pre_x[id],pre_y[id],x,y,&brush);
  22. /************************************/
  23. /*在此处添加自己的触摸点按下时处理过程即可*/
  24. /* (x,y) 即为最新的触摸点 *************/
  25. /************************************/
  26. /*prex,prey数组存储上一次触摸的位置,id为轨迹编号(多点触控时有多轨迹)*/
  27. pre_x[id] = x; pre_y[id] =y;
  28. }
  29. /**
  30. * @brief 用于处理或报告触屏释放
  31. * @param 释放点的id号
  32. * @retval 无
  33. */
  34. static void GTP_Touch_Up( int32_t id)
  35. {
  36. /*处理触摸释放,用于触摸画板*/
  37. Touch_Button_Up(pre_x[id],pre_y[id]);
  38. /*****************************************/
  39. /*在此处添加自己的触摸点释放时的处理过程即可*/
  40. /* pre_x[id],pre_y[id] 即为最新的释放点 ****/
  41. /*******************************************/
  42. /***id为轨迹编号(多点触控时有多轨迹)********/
  43. /*触笔释放,把pre xy 重置为负*/
  44. pre_x[id] = -1;
  45. pre_y[id] = -1;
  46. GTP_DEBUG("Touch id[%2d] release!", id);
  47. }

这两个坐标接口函数都还是在服务函数里面调用的,在实际应用中可以先把这些坐标信息存储起来,等待到系统空闲的时候再处理,就可以减轻中断服务程序的负担。

最后的主函数:

 

  1. int main(void)
  2. {
  3. /* LED 端口初始化 */
  4. LED_GPIO_Config();
  5. Debug_USART_Config();
  6. printf("\r\n野火STM3F429 触摸画板测试例程\r\n");
  7. /* 初始化触摸屏 */
  8. GTP_Init_Panel();
  9. SysTick_Init();
  10. /*初始化液晶屏*/
  11. LCD_Init();
  12. LCD_LayerInit();
  13. LTDC_Cmd(ENABLE);
  14. /*把背景层刷黑色*/
  15. LCD_SetLayer(LCD_BACKGROUND_LAYER);
  16. LCD_Clear(LCD_COLOR_BLACK);
  17. /*初始化后默认使用前景层*/
  18. LCD_SetLayer(LCD_FOREGROUND_LAYER);
  19. /*默认设置不透明 ,该函数参数为不透明度,范围 0-0xff ,0为全透明,0xff为不透明*/
  20. LCD_SetTransparency(0xFF);
  21. LCD_Clear(LCD_COLOR_BLACK);
  22. Delay(0xfff);
  23. while(1);
  24. }

最后关于应用层,放在另外一章吧。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Cpp五条/article/detail/594011
推荐阅读
相关标签
  

闽ICP备14008679号