STM32F7驱动GT9147电容触摸屏控制芯片,并移植到STemWin

目录

一  电阻驱动与电容驱动原理

二 软件模拟IIC

三 GT9147电容触摸屏控制芯片驱动

四 移植触摸屏驱动到STemWin


一  电阻驱动与电容驱动原理

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内部结构框图如下图所示

引脚说明
AVDD、AVDD18、DVDD12、VDDDIO、GND电源和地
Driving channels激励信号输出的引脚,一共有 0-17 个引脚,它连接到电容屏引出的各个激励信号轴
Sensing channels信号检测引脚,一共有 0-9 个引脚,它连接到电容屏 引出的各个电容量检测信号轴
I2CI2C 通信信号线,包含 SCL与 SDA,外部控制器通过它与 GT9147 芯片通讯
INT中断信号,GB9147 芯片通过它告诉外部控制器有新的触摸事件
/RSTB复位引脚,用于复位 GT9157 芯片;在上电时还与 INT 引脚配合设置 IIC 通讯的设备地址

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坐标值
                x[i] = (((uint16_t)buf[1] << 8) + buf[0]);
                y[i] = (((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[i]=CT_IIC_Read_Byte(i==(len-1)?0:1); //发数据	  
	}
        CT_IIC_Stop();//产生一个停止条件    
}

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[i]);  	//发数据
		ret = CT_IIC_Wait_Ack();
		if(ret)break;  
	}
    CT_IIC_Stop();					//产生一个停止条件	    
	return ret; 
}

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[i];//计算校验和
        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;
}

四 移植触摸屏驱动到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 */
}

 

 

 

 

 

 

 

以下是使用HAL库初始化USB的示例代码: 首先,需要在CubeMX中启用USB功能,然后在main.c文件中添加以下代码: ```c /* USB initialization */ MX_USB_DEVICE_Init(); ``` 接着,在stm32f4xx_hal_conf.h文件中需要启用USB功能: ```c #define USE_USB_FS ``` 最后,在stm32f4xx_hal_msp.c文件中添加以下代码: ```c /** * Initializes the USB device library. * Called by the HAL_Init() function in the beginning of the program. */ void HAL_PCD_MspInit(PCD_HandleTypeDef *hpcd) { GPIO_InitTypeDef GPIO_InitStruct = {0}; if(hpcd->Instance == USB_OTG_FS) { /* Enable peripheral clocks */ __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_GPIOB_CLK_ENABLE(); __HAL_RCC_GPIOC_CLK_ENABLE(); __HAL_RCC_GPIOH_CLK_ENABLE(); __HAL_RCC_GPIOI_CLK_ENABLE(); __HAL_RCC_USB_OTG_FS_CLK_ENABLE(); /* Configure USB FS GPIOs */ GPIO_InitStruct.Pin = GPIO_PIN_11 | GPIO_PIN_12; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; GPIO_InitStruct.Alternate = GPIO_AF10_OTG_FS; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); GPIO_InitStruct.Pin = GPIO_PIN_13; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); GPIO_InitStruct.Pin = GPIO_PIN_9; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; GPIO_InitStruct.Alternate = GPIO_AF10_OTG_FS; HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); GPIO_InitStruct.Pin = GPIO_PIN_5; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; GPIO_InitStruct.Alternate = GPIO_AF10_OTG_FS; HAL_GPIO_Init(GPIOH, &GPIO_InitStruct); GPIO_InitStruct.Pin = GPIO_PIN_10; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; GPIO_InitStruct.Alternate = GPIO_AF10_OTG_FS; HAL_GPIO_Init(GPIOI, &GPIO_InitStruct); /* Enable interrupt */ HAL_NVIC_SetPriority(OTG_FS_IRQn, 0x0F, 0); HAL_NVIC_EnableIRQ(OTG_FS_IRQn); } } /** * DeInitializes the USB device library. * Called by the HAL_DeInit() function in the end of the program. */ void HAL_PCD_MspDeInit(PCD_HandleTypeDef *hpcd) { if(hpcd->Instance == USB_OTG_FS) { /* Disable interrupt */ HAL_NVIC_DisableIRQ(OTG_FS_IRQn); /* Disable peripheral clocks */ __HAL_RCC_USB_OTG_FS_CLK_DISABLE(); __HAL_RCC_GPIOA_CLK_DISABLE(); __HAL_RCC_GPIOB_CLK_DISABLE(); __HAL_RCC_GPIOC_CLK_DISABLE(); __HAL_RCC_GPIOH_CLK_DISABLE(); __HAL_RCC_GPIOI_CLK_DISABLE(); } } ``` 这些代码将初始化USB的GPIO和中断,并启用USB的时钟。 此外,还需要在main.c文件中添加以下中断处理函数: ```c void OTG_FS_IRQHandler(void) { HAL_PCD_IRQHandler(&hpcd_USB_FS); } ``` 这会处理USB的中断并将其传递给HAL库。 完成这些步骤后,USB应该已成功初始化。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值