一 电阻驱动与电容驱动原理
1. 电阻屏等效电路如下所示, 当产生按压时:
X-接地,X+接电源,Y+接ADC输入.通过读取Y+的电压以及电阻分压原理,可以得出触点的x坐标
Y-接地,Y+接电源,X+接ADC输入.通过读取X+的电压以及电阻分压原理,可以得出触点的y坐标
2. 电容屏等效图如下所示, 检测原理类似矩阵按键, 当产生按压时:
X轴产生从上至下依次产生激励信号(AC交流信号), Y轴电极同时接受信号 (该激励信号是交流电,可以通过电容穿到Y轴).
假设有如下两个按压点(分别位于(1,1)与(2,2)的位置),检测过程如下:
当X1产生激励信号时,Y1接受到的交流信号产生了变化, 所以可以确认(1,1)处有触摸.
接着当当X2产生激励信号时,Y2接受到的交流信号产生了变化, 所以可以确认(2,2)处也有有触摸.
二 软件模拟IIC
由于stm32的硬件iic有各种各样的问题,所以采用软件模拟IIC的方式,主要实现IIC起始信号函数、IIC停止信号函数、等待ACK函数、产生ACK应答函数、不产生ACK应答函数、发送字节函数、读字节函数
1. 起始信号与停止信号
下图是IIC起始信号与停止信号时序图,通过观察该图编写起始信号与停止信号函数。
起始信号:当SCL为高期间,SDA由高到低的跳变;启动信号是一种电平跳变时序信号,而不是一个电平信号。
- /***************************************************************************************
- * @brief 产生IIC起始信号,当SCL为高期间,SDA由高到低的跳变
- ***************************************************************************************/
- void CT_IIC_Start(void)
- {
- CT_SDA_OUT(); //sda线输出
- CT_IIC_SCL(1);
- CT_IIC_SDA(1);
- CT_Delay();
- CT_IIC_SDA(0);
- }
复制代码
停止信号:当SCL为高期间,SDA由低到高的跳变;停止信号也是一种电平跳变时序信号,而不是一个电平信号。
- /***************************************************************************************
- * @brief 产生IIC停止信号,当SCL为高期间,SDA由低到高的跳变
- ***************************************************************************************/
- void CT_IIC_Stop(void)
- {
- CT_SDA_OUT();//sda线输出
- CT_IIC_SCL(1);
- CT_IIC_SDA(0);//STOP:when CLK is high DATA change form low to high
- CT_Delay();
- CT_IIC_SDA(1);
- }
复制代码
在GT9147数据手册中可以发现如下截图所示内容,SCL在低电平期间至少保持1.3us,其他电平最小保持0.6
所以将IIC延时函数定时为2us可以满足所有时序。(一般支持最高速度400k的IIC设备,基本都可以使用2us延时。)
- //控制I2C速度的延时
- void CT_Delay(void)
- {
- delay_us(2);
- }
复制代码
2. 数据传输 与 ACK时序
下图是ACK时序与 传输数据0的时序图,这两种时序图是一样的,区别在于ACK是在8个数据时序后产生,且SDA电平是由接受数据端拉低的。
产生ACK时序:在SCL高电平期间,SDA为低电平状态。
- /***************************************************************************************
- * @brief 产生ACK应答,
- ***************************************************************************************/
- void CT_IIC_Ack(void)
- {
- CT_IIC_SCL(0);
- CT_Delay();
- CT_SDA_OUT();
- CT_IIC_SDA(0);
- CT_IIC_SCL(1);
- CT_Delay();
- CT_IIC_SCL(0);
- }
复制代码
等待ACK时序:拉高SCL与SDA后, 然后设置SDA为输入检测状态,等待接受数据端拉低SDA
- /***************************************************************************************
- * @brief 等待应答信号到来,等待接受数据端拉低SDA
- * @input
- * @return 1,接收应答失败 ; 0,接收应答成功
- ***************************************************************************************/
- uint8_t CT_IIC_Wait_Ack(void)
- {
- uint8_t ucErrTime=0;
- CT_IIC_SDA(1);
- CT_IIC_SCL(1);
- CT_SDA_IN(); //SDA设置为输入
- while(CT_READ_SDA)
- {
- ucErrTime++;
- if(ucErrTime>250){
- CT_IIC_Stop();
- return 1;
- }
- CT_Delay();
- }
- CT_IIC_SCL(0);
- return 0;
- }
复制代码
下图是nACK时序与 传输数据1的时序图,这两种时序图是一样的,区别在于ACK是在8个数据时序后产生,且SDA电平是由接受数据端拉低的。
产生ACK时序:在SCL高电平期间,SDA为高电平状态。
- /***************************************************************************************
- * @brief 不产生ACK应答
- ***************************************************************************************/
- void CT_IIC_NAck(void)
- {
- CT_IIC_SCL(0);
- CT_Delay();
- CT_SDA_OUT();
- CT_IIC_SDA(1);
- CT_IIC_SCL(1);
- CT_Delay();
- CT_IIC_SCL(0);
- }
复制代码
发送一个字节函数,优先发送高位,即从bit7开始发送。
- /***************************************************************************************
- * @brief IIC发送一个字节,
- * @input
- * @return
- ***************************************************************************************/
- void CT_IIC_Send_Byte(uint8_t txd)
- {
- uint8_t t;
- CT_SDA_OUT();
- CT_IIC_SCL(0);//拉低时钟开始数据传输
- CT_Delay();
- for(t=0;t<8;t++)
- {
- CT_IIC_SDA((txd&0x80)>>7);//发送bit7位
- txd<<=1; //将txd的次高位左移到最高位
- CT_IIC_SCL(1);
- CT_Delay();
- CT_IIC_SCL(0);
- CT_Delay();
- }
- }
复制代码
读字一个字节函数,接受到1bit数据后,将该bit放在bit0位置(通过receive++实现),如果还要继续接受数据,则将之前接受到的数据左移一位(通过receive<<=1实现),留出bit0位置接受新的数据。
- /***************************************************************************************
- * @brief 读1个字节
- * @input ack=1时,发送ACK,ack=0,发送nACK
- * @return
- ***************************************************************************************/
- uint8_t CT_IIC_Read_Byte(unsigned char ack)
- {
- uint8_t i,receive=0;
- CT_SDA_IN();//SDA设置为输入
- CT_Delay();
- for(i=0;i<8;i++ )
- {
- CT_IIC_SCL(0);
- CT_Delay();
- CT_IIC_SCL(1);
- receive<<=1;
- if(CT_READ_SDA)receive++;
- }
- if (!ack)
- CT_IIC_NAck();//发送nACK
- else
- CT_IIC_Ack(); //发送ACK
- return receive;
- }
复制代码
三 GT9147电容触摸屏控制芯片驱动
1. GT914内部结构框图如下图所示
2. 上电流程
GT9147上电初始化流程如下所示:
配置RST输出低电平,INT输出低(或者高)电平后,位置100us以上,此阶段配置IIC地址为 0xBA/0xBB(或者0x28/0x29)
接着配置RST输出高电平并维持5ms以上后,可以配置INT引脚为悬浮输入模式。
等待50ms以上后,可以发送配置信息。
代码参考原子例程编写,贴上代码:
- uint8_t GT9147_Init(void)
- {
- uint8_t temp[5];
- GPIO_InitTypeDef GPIO_Initure;
- GPIO_Initure.Pin = GPIO_PIN_7; //PH7
- GPIO_Initure.Mode = GPIO_MODE_INPUT; //输出
- GPIO_Initure.Pull = GPIO_PULLUP; //上拉
- GPIO_Initure.Speed = GPIO_SPEED_HIGH; //高速
- HAL_GPIO_Init(GPIOH, &GPIO_Initure); //初始化
- GPIO_Initure.Pin = GPIO_PIN_8; //PI8
- GPIO_Initure.Mode = GPIO_MODE_OUTPUT_PP; //推挽输出
- HAL_GPIO_Init(GPIOI, &GPIO_Initure); //初始化
- CT_IIC_Init(); //初始化电容屏的I2C总线
- GT_RST(0); //复位
- delay_ms(1); //INT引脚电平维持100us以上
- GT_RST(1); //释放复位
- delay_ms(10); //释放复位后维持5ms以上,设置INT为悬浮输入
- GPIO_Initure.Pin = GPIO_PIN_7;
- GPIO_Initure.Mode = GPIO_MODE_IT_RISING;
- GPIO_Initure.Pull = GPIO_NOPULL;
- HAL_GPIO_Init(GPIOH, &GPIO_Initure); //初始化
- delay_ms(60);
- GT9147_RD_Reg(GT_PID_REG, temp, 4); //读取产品ID
- temp[4] = 0;
- printf("CTP ID:0x%s\r\n", temp); //打印ID
- GT9147_Send_Cfg(1);//更新并保存配置
- return 1;
- }
复制代码
3. 读取坐标流程
使用中断方式读取坐标,需要注意的是,如果不及时读取坐标信息会一直产生中断。GT9147的中断脚与STM32的LINE7中断线相连,中断回调函数如下所示。当按下触摸屏时,touch_x与touch_y分别保存触摸时的xy坐标;当松开触摸屏时,touch_x,touch_y赋值0xffff
- void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
- {
- uint8_t buf[4];
- uint8_t i = 0, mode = 0, temp = 0;
- uint16_t x[5], y[5];
- if(GPIO_Pin == GPIO_PIN_7) { //读取坐标, 否则会一直有INT脉冲
- GT9147_RD_Reg(GT_GSTID_REG, &mode, 1); //读取触摸点的状态
- if(mode & 0X80 && ((mode & 0XF) < 6)) //有坐标可读取
- {
- temp = 0;
- GT9147_WR_Reg(GT_GSTID_REG, &temp, 1);//清标志
- }
- if( (mode & 0xF) && ((mode & 0xF) < 6)) //判断触摸点个数
- {
- for(i = 0; i < (mode & 0xF); i++)
- {
- GT9147_RD_Reg(GT9147_TPX_TBL<i>, buf, 4); //读取XY坐标值
- </i>x = (((uint16_t)buf[1] << 8) + buf[0]);
- y = (((uint16_t)buf[3] << 8) + buf[2]);
- }
- touch_x = x[0];
- touch_y = y[0];
- }
- if((mode&0x8F)==0x80)//无触摸点按下,xy赋值为0xffff
- {
- touch_x = 0xffff;
- touch_y = 0xffff;
- }
- }
- }
复制代码 4.主机 对 GT9147 进行读操作时序
根据该时序图编写读寄存器函数如下:
- /***************************************************************************************
- * @brief 从GT9147读出一次数据
- * @input reg:起始寄存器地址
- buf:数据缓缓存区
- len:读数据长度
- * @return
- ***************************************************************************************/
- void GT9147_RD_Reg(uint16_t reg,uint8_t *buf,uint8_t len)
- {
- uint8_t i;
- CT_IIC_Start();
- CT_IIC_Send_Byte(GT_CMD_WR); //发送写命令
- CT_IIC_Wait_Ack();
- CT_IIC_Send_Byte(reg>>8); //发送高8位地址
- CT_IIC_Wait_Ack();
- CT_IIC_Send_Byte(reg&0XFF); //发送低8位地址
- CT_IIC_Wait_Ack();
- CT_IIC_Start();
- CT_IIC_Send_Byte(GT_CMD_RD); //发送读命令
- CT_IIC_Wait_Ack();
- for(i=0;i<len;i++)
- {
- buf<span style="font-style: italic;"><span style="font-style: normal;">=CT_IIC_Read_Byte(i==(len-1)?0:1); //发数据
- }
- CT_IIC_Stop();//产生一个停止条件
- }</span></span>
复制代码
5.主机对 GT9147 进行写操作时序
通过该时序图,编写写寄存器函数如下
- /***************************************************************************************
- * @brief 向GT9147写入一次数据
- * @input reg:起始寄存器地址;
- buf:数据缓缓存区
- len:写数据长度
- * @return 0,成功; 1,失败.
- ***************************************************************************************/
- uint8_t GT9147_WR_Reg(uint16_t reg,uint8_t *buf,uint8_t len)
- {
- uint8_t i;
- uint8_t ret=0;
- CT_IIC_Start();
- CT_IIC_Send_Byte(GT_CMD_WR); //发送写命令
- CT_IIC_Wait_Ack();
- CT_IIC_Send_Byte(reg>>8); //发送高8位地址
- CT_IIC_Wait_Ack();
- CT_IIC_Send_Byte(reg&0XFF); //发送低8位地址
- CT_IIC_Wait_Ack();
- for(i=0;i<len;i++)
- {
- CT_IIC_Send_Byte(buf<span style="font-style: italic;"><span style="font-style: normal;">); //发数据
- ret = CT_IIC_Wait_Ack();
- if(ret)break;
- }
- CT_IIC_Stop(); //产生一个停止条件
- return ret;
- }</span></span>
复制代码
6. GT9147配置函数如下,GT9147_CFG_TBL针对分辨率480×272的触摸屏。
配置寄存器时是要注意以下几点:
新的版本号大于等于GT9147内部flash原有版本号,才会更新配置.
写完GT9147_CFG_TBL中的配置后,还需要往0x80FF寄存器中写入校验,0x8100寄存器中写1。
关于配置更详细的信息可以参考GT9147编程参考手册
- const uint8_t GT9147_CFG_TBL[]=
- {
- 0x60,0xe0,0x01,0x10,0x01,0x05,0x0C,0x00,0x01,0x08,
- 0x28,0x05,0x50,0x32,0x03,0x05,0x00,0x00,0xff,0xff,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x89,0x28,0x0a,
- 0x17,0x15,0x31,0x0d,0x00,0x00,0x02,0x9b,0x03,0x25,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x32,0x00,0x00,
- 0x00,0x0f,0x94,0x94,0xc5,0x02,0x07,0x00,0x00,0x04,
- 0x8d,0x13,0x00,0x5c,0x1e,0x00,0x3c,0x30,0x00,0x29,
- 0x4c,0x00,0x1e,0x78,0x00,0x1e,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x08,0x0a,0x0c,0x0e,0x10,0x12,0x14,0x16,
- 0x18,0x1a,0x00,0x00,0x00,0x00,0x1f,0xff,0xff,0xff,
- 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
- 0xff,0xff,0x00,0x02,0x04,0x05,0x06,0x08,0x0a,0x0c,
- 0x0e,0x1d,0x1e,0x1f,0x20,0x22,0x24,0x28,0x29,0xff,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,
- 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
- 0xff,0xff,0xff,0xff,
- };
- uint8_t GT9147_Send_Cfg(uint8_t mode)
- {
- uint8_t buf[2];
- uint8_t i=0;
- buf[0]=0;
- buf[1]=mode; //是否写入到GT9147 FLASH? 即是否掉电保存
- for(i=0;i<sizeof(GT9147_CFG_TBL);i++)
- buf[0]+=GT9147_CFG_TBL<span style="font-style: italic;"><span style="font-style: normal;">;//计算校验和
- buf[0]=(~buf[0])+1;
- GT9147_WR_Reg(GT_CFGS_REG,(uint8_t*)GT9147_CFG_TBL,sizeof(GT9147_CFG_TBL));//发送寄存器配置
- GT9147_WR_Reg(GT_CHECK_REG,buf,2);//写入校验和,和配置更新标记
- return 0;
- }</span></span>
复制代码
四 移植触摸屏驱动到STemWin
这里假设已经成功移植了STemWin到STM32F7工程。
1. 在GUIConf打开STemWin触摸屏驱动宏
- #define GUI_SUPPORT_TOUCH (1) // Support touchscreen
复制代码
2. 新建GUI_X_Touch_Analog.c文件实现以下四个函数,在第三章第3小节我们知道touch_x,touch_y表示当前触摸的xy坐标。整个文件的内容如下所示。
- void GUI_TOUCH_X_ActivateX(void)
- {
- }
- void GUI_TOUCH_X_ActivateY(void)
- {
- }
- /*获取x坐标*/
- int GUI_TOUCH_X_MeasureX (void)
- {
- return touch_x;
- }
- /*获取y坐标*/
- int GUI_TOUCH_X_MeasureY (void)
- {
- return touch_y;
- }
复制代码
3. 使用GUI_TOUCH_Calibrate函数校准x与y值,然后才能调用GUI_TOUCH_Exec()函数处理触摸事件。
- void StartTouchTask(void const * argument)
- {
- /* USER CODE BEGIN StartTouchTask */
- GUI_CURSOR_Show();//显示鼠标指针
- GUI_TOUCH_Calibrate(GUI_COORD_X,0,LCD_WIDTH,0,LCD_WIDTH);
- GUI_TOUCH_Calibrate(GUI_COORD_Y,0,LCD_HEIGHT,0,LCD_HEIGHT);
- /* Infinite loop */
- for(;;)
- {
- GUI_TOUCH_Exec();
- osDelay(5);
- }
- /* USER CODE END StartTouchTask */
- }
复制代码
|