目錄
一 電阻驅動與電容驅動原理
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 個引腳,它連接到電容屏 引出的各個電容量檢測信號軸 |
I2C | I2C 通信信號線,包含 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 */
}