采用I2C驅(qū)動觸摸屏。
I2C多用于主控制器和從器件間的主從通信,在小數(shù)據(jù)量場合使用,傳輸距離短,任意時刻只能有一個主機等特性。
它有兩條線,一條是SCL(串行時鐘總線),另外一條是SDA(串行數(shù)據(jù)線),這兩條數(shù)據(jù)需要接上拉電阻,總線空閑的時候SCL和SDA處于高電平。
?圖來自正點原子linux驅(qū)動開發(fā)教程
I2C主要有起始位、停止位、數(shù)據(jù)傳輸、應(yīng)答信號等。
I2C寫時序
I2C的寫時序相較于讀時序是比較簡單的,大概分以下幾個步驟。
- 開始信號
- 發(fā)送 I2C設(shè)備地址,其中高七位是設(shè)備地址,最后一位是讀寫地址。
- 從機發(fā)送應(yīng)答信號
- 重新發(fā)送開始信號
- 發(fā)送要寫入數(shù)據(jù)的寄存器地址
- 從機發(fā)送應(yīng)答信號
- 發(fā)送要寫入寄存器的數(shù)據(jù)
- 從機發(fā)送應(yīng)答信號
- 停止信號
?圖來自正點原子linux驅(qū)動開發(fā)教程
I2C讀時序
讀時序相對于寫時序來說復(fù)雜了一點,結(jié)束時多了一個非應(yīng)答信號,以及寫入寄存器地址之后要重新發(fā)從機的地址。
總體的分為四步,跟寫時序差不多。
- 發(fā)送設(shè)備地址
- 發(fā)送要讀取的寄存器地址
- 重新發(fā)送設(shè)備地址
- 讀取數(shù)據(jù)
??圖來自正點原子linux驅(qū)動開發(fā)教程
OK,了解了基本的之后,回到驅(qū)動觸摸屏上來。
我們驅(qū)動一個觸摸屏需要使用到TP,這里使用的是FT6236。
查詢芯片手冊,可以發(fā)現(xiàn)FT6236有四根線。
兩根是I2C需要的線,還有兩根分別是INT(輸入),RSTN(輸出)。
到這里,就很簡潔明了了。
我們需要用到GPIO,I2C,以及TP的驅(qū)動。
首先,將RST和INT利用GPIO進行初始化,并進行相應(yīng)的一些設(shè)置。、
GPIO
GPIO的使用已經(jīng)很熟悉了,這里就不多說了。
簡單的來說就是定義硬件圖所使用的IO口,然后利用寄存器進行相應(yīng)的初始化,實現(xiàn)我們的功能。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/gpio.h"
#include "ds_gpio.h"
#include "ds_system_data_da.h"
//定義IO口,查硬件圖
#define GPIO_OUTPUT_IO_0 5
#define GPIO_OUTPUT_PIN_SEL ((1ULL<<GPIO_OUTPUT_IO_0))
#define GPIO_INTPUT_IO_0 4
#define GPIO_INTPUT_PIN_SEL ((1ULL<<GPIO_INTPUT_IO_0))
#define ESP_INTR_FLAG_DEFAULT 0
static xQueueHandle gpio_evt_queue = NULL;
static void IRAM_ATTR gpio_isr_handler(void *arg){
uint32_t gpio_num = (uint32_t)arg;//強轉(zhuǎn)
xQueueSendFromISR(gpio_evt_queue,&gpio_num,NULL);
}
//觸摸屏GPIO口初始化
void ds_touch_gpio_init(void){
gpio_config_t io_conf;
//rst不需要中斷,關(guān)閉中斷
io_conf.intr_type = GPIO_PIN_INTR_DISABLE;
//設(shè)置模式為輸出
io_conf.mode = GPIO_MODE_OUTPUT;
//設(shè)置引腳5
io_conf.pin_bit_mask = GPIO_OUTPUT_PIN_SEL;
//設(shè)置上拉和下拉
io_conf.pull_down_en = 0;
io_conf.pull_up_en = 0;
//配置GPIO
gpio_config(&io_conf);
//無論上升沿還是下降沿都觸發(fā)
io_conf.intr_type = GPIO_INTR_ANYEDGE;
//模式為輸入,根據(jù)手冊判定的
io_conf.mode = GPIO_MODE_INPUT;
//設(shè)置為引腳4
io_conf.pin_bit_mask = GPIO_INTPUT_PIN_SEL;
//設(shè)置為上拉,默認低電平有效
io_conf.pull_up_en = 1;
//配置GPIO
gpio_config(&io_conf);
//創(chuàng)建處理gpio事件的消息隊列
gpio_evt_queue = xQueueCreate(10,sizeof(uint32_t));
//安裝gpio中斷服務(wù)
gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT);
gpio_isr_handler_add(GPIO_INTPUT_IO_0,gpio_isr_handler,(void*)GPIO_INTPUT_IO_0);
}
//復(fù)位
void ds_gpio_set_touch_rst(uint32_t level){
gpio_set_level(GPIO_OUTPUT_IO_0,level);
}
GPIO比較簡單。
I2C程序編寫
I2C的程序編寫盡量要參考一下ESP32官方給出的手冊,包括讀寫時序的編寫,避免出錯。
相關(guān)API可以在官方文檔里看。
這是官方例程里帶寄存器讀的例子,可以看到是先設(shè)置寄存器的地址,然后再重新寫入從機地址的,接下來就可以模仿這個開始寫。
首先設(shè)置讀取地址。
//設(shè)置讀取地址
static esp_err_t i2c_master_set_addr(uint8_t u8Cmd){
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd,(ESP_SLAVE_ADDR<<1)|WRITE_BIT,ACK_CHECK_EN);//設(shè)置從機地址
i2c_master_write_byte(cmd,u8Cmd,ACK_CHECK_EN);//設(shè)置讀取寄存器地址
i2c_master_stop(cmd);
esp_err_t ret = i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, 1000 / portTICK_RATE_MS);
i2c_cmd_link_delete(cmd);
if (ret != ESP_OK) {
printf("i2c_master_set_addr error\n");
}
return ret;
}
然后進行讀時序。
//讀取數(shù)據(jù)
esp_err_t i2c_master_read_slave(uint8_t u8Cmd, uint8_t *data_rd, size_t size){
if(size == 0){
return ESP_OK;
}
i2c_master_set_addr(u8Cmd);//設(shè)置要讀取的寄存器地址
vTaskDelay(30 / portTICK_RATE_MS);//延時24ms
//然后再次寫入從機地址,并加上獨標(biāo)志位
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd,(ESP_SLAVE_ADDR<<1)|READ_BIT,ACK_CHECK_EN);
for(int index = 0;index<(size-1);index++){
i2c_master_read_byte(cmd,data_rd+index,ACK_VAL);
}
i2c_master_read_byte(cmd,data_rd+size-1,NACK_VAL);//發(fā)送非應(yīng)答信號
i2c_master_stop(cmd);
esp_err_t ret = i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, 1000 / portTICK_RATE_MS);
i2c_cmd_link_delete(cmd);
if (ret != ESP_OK) {
printf("i2c_master_read_slave error\n");
}
return ret;
}
寫時序就比較簡單了。
//寫入數(shù)據(jù),不需要發(fā)非應(yīng)答信號
esp_err_t i2c_master_write_slave(uint8_t u8Cmd, uint8_t *data_wr, size_t size){
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (ESP_SLAVE_ADDR << 1) | WRITE_BIT, ACK_CHECK_EN);
i2c_master_write_byte(cmd, u8Cmd, ACK_CHECK_EN);
i2c_master_write(cmd, data_wr, size, ACK_CHECK_EN);
i2c_master_stop(cmd);
esp_err_t ret = i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, 1000 / portTICK_RATE_MS);
i2c_cmd_link_delete(cmd);
if (ret != ESP_OK) {
printf("i2c_master_write_slave error\n");
}
return ret;
}
在進行這些之前,還需要初始化一下我們的I2C。
這就涉及到一些宏定義,可以采用KConfig進行定義,可以直接寫值,但是這里還是建議用Kconfig進行定義。
初始化的代碼直接參考例程,幾乎不怎么需要修改,只需要把SCL和SDA的引腳重新定義一下,適合我們的開發(fā)板就行。
//初始化
esp_err_t i2c_master_init(void)
{
int i2c_master_port = I2C_MASTER_NUM;
i2c_config_t conf;
conf.mode = I2C_MODE_MASTER;
conf.sda_io_num = I2C_MASTER_SDA_IO;
conf.sda_pullup_en = GPIO_PULLUP_ENABLE;
conf.scl_io_num = I2C_MASTER_SCL_IO;
conf.scl_pullup_en = GPIO_PULLUP_ENABLE;
conf.master.clk_speed = I2C_MASTER_FREQ_HZ;
i2c_param_config(i2c_master_port, &conf);
return i2c_driver_install(i2c_master_port, conf.mode, I2C_MASTER_RX_BUF_DISABLE, I2C_MASTER_TX_BUF_DISABLE, 0);
}
好了,I2C就已經(jīng)可以了。
然后開始寫最重要的一步,TP的驅(qū)動,這個是需要我們自己查芯片手冊,然后一步步編寫的。
TP驅(qū)動
觀察芯片手冊。
?圈出來的這里,就是我們用I2C寫入或者讀取的寄存器了。
在頭文件里進行定義。
//由FT6236芯片手冊查詢得到各部分寄存器地址
#define FI_DEVIDE_MODE 0x00 //FT6236模式控制寄存器
#define FI_REG_NUM_FINGER 0x02 //觸摸狀態(tài)寄存器
#define FI_TP1_REG 0x03 //第一個觸摸點數(shù)據(jù)地址
#define FI_TP2_REG 0x09 //第一個觸摸點數(shù)據(jù)地址
#define FI_TP3_REG 0x0F //第一個觸摸點數(shù)據(jù)地址
#define FI_TP4_REG 0x15 //第一個觸摸點數(shù)據(jù)地址
#define FI_TP5_REG 0x1B //第一個觸摸點數(shù)據(jù)地址
#define FI_ID_G_LIB_VERSION 0xA1 //版本
#define FI_ID_G_MODE 0xA4 //FT6236中斷模式控制寄存器
#define FI_ID_G_THGROUP 0x80 //觸摸有效值設(shè)置寄存器
#define FI_ID_G_PERIODACTIVE 0x88 //激活狀態(tài)周期設(shè)置寄存器
#define Chip_Vendor_ID 0xA3 //芯片ID(0x36)
#define ID_G_FT6236ID 0xA8 //0x11
然后定義觸摸點的結(jié)構(gòu)體。
//觸摸點相關(guān)數(shù)據(jù)結(jié)構(gòu)定義
typedef struct
{
//bit7:按下1/松開0
//bit6: 沒有按鍵按下0/有按鍵按下1
//bit5:保留
//bit4-bit0:觸摸點按下有效標(biāo)志,有效為1,對應(yīng)五個觸摸點
uint8_t touch_sta; //觸摸點的情況
uint8_t touch_count; //觸摸點數(shù)
uint16_t x[5];
uint16_t y[5];
bool updata;
}TouchPoint_T;
定義一下觸摸屏被按下或松開的標(biāo)記。
#define TP_PRESS_DOWN 0x80 //觸摸屏被按下,0x10000000,第七位為1
#define TP_COORD_UD 0x40 //觸摸屏坐標(biāo)更新,第六位為1
其他的就看自己的用途定義了,基本就是這些了。
然后就可以開始編寫驅(qū)動了,主要參考ESP里的相關(guān)例程,以及FT6236的一些源碼。
#include <string.h>
#include <stdio.h>
#include "ds_tp.h"
#include "ds_i2c.h"
#include "ds_gpio.h"
#include "ds_system_data_da.h"
//觸摸芯片最大5組觸摸點,F(xiàn)T6236最大支持雙觸
const uint16_t FT6236_TPX_TBL[5]=
{
FI_TP1_REG,
FI_TP2_REG,
FI_TP3_REG,
FI_TP4_REG,
FI_TP5_REG
};
TouchPoint_T gTPS;
//掃描觸摸屏寄存器狀態(tài)、數(shù)據(jù)
static void scan_ft6236(void)
{
uint8_t i=0;
uint8_t sta = 0;//觸摸點狀態(tài)
uint8_t buf[4] = {0};//這里是獲取四個字節(jié),分別是xH、xL、yH、yL
uint8_t gestid = 0;//手勢
i2c_master_read_slave(0x02,&sta,1);//讀取寄存器狀態(tài),讀取的是個數(shù)!
gTPS.touch_count = sta;
i2c_master_read_slave(0x01,&gestid,1);//讀取觸摸點的狀態(tài)
if(sta&0x0f)//判斷有無觸摸點按下
{
gTPS.touch_sta = ~(0xFF << (sta & 0x0F));//將有效觸摸點的個數(shù)轉(zhuǎn)換為對應(yīng)的標(biāo)記
for (i = 0; i < 2; i++)//最多同時兩個觸摸點
{
if (gTPS.touch_sta & (1 << i))
{
i2c_master_read_slave(FT6236_TPX_TBL[i], buf, 4); // 讀取觸摸點坐標(biāo)
gTPS.x[i] = (uint16_t)(((buf[0]&0x0F)<<8)+buf[1]);//清空XH的高四位,并左移8位與XL組成坐標(biāo)
gTPS.y[i] = (uint16_t)(((buf[2]&0x0F)<<8)+buf[3]);
}
}
gTPS.touch_sta |= TP_PRESS_DOWN; //按下標(biāo)記,置1
}
else //如果判斷無觸摸點按下,那么檢查一下之前的標(biāo)記
{
if(gTPS.touch_sta & TP_PRESS_DOWN)//如果之前被按下了
{
gTPS.touch_sta &= ~0x80; //清楚按下標(biāo)記
}
else//
{
gTPS.x[0]=0;
gTPS.y[0]=0;
gTPS.touch_sta &= 0xe0;//將后五位清0,這一塊還是有點疑慮
}
}
}
//轉(zhuǎn)換為實際位置
static void count_position_ft6236(TP_POSITION_T *position){
switch (gTPS.touch_count)
{
case 1:
if ((gTPS.x[0] != 0) && (gTPS.y[0] != 0)
&& (gTPS.x[0] < 200) && (gTPS.y[0] < 200))
{
position->x = gTPS.x[0];
position->y = gTPS.y[0];
printf("觸摸點的個數(shù)=%d\r\n", gTPS.touch_count);
printf("x0:%d,y0:%d\r\n", gTPS.x[0], gTPS.y[0]);
return;
}
break;
case 2:
if ((gTPS.x[0] != 0) && (gTPS.y[0] != 0)
&& (gTPS.x[0] < 200) && (gTPS.y[0] < 200)
&& (gTPS.x[0] < 200) && (gTPS.y[0] < 200)
&& (gTPS.x[1] < 200) && (gTPS.y[1] < 200))
{
printf("觸摸點個數(shù)::%d\r\n", gTPS.touch_count); // FT6336U最多支持兩點觸控
printf("x0:%d,y0:%d\r\n", gTPS.x[0], gTPS.y[0]);
printf("x1:%d,y1:%d\r\n", gTPS.x[1], gTPS.y[1]);
}
break;
default:
break;
}
for (int i = 0; i < 2; i++)
{
gTPS.x[i] = 0;
gTPS.y[i] = 0;
}
position->status = 0;
position->x = gTPS.x[0];
position->y = gTPS.y[0];
}
void get_ft6236_touch_sta(TP_POSITION_T *position){
scan_ft6236();
count_position_ft6236(position);
}
void init_ft6236(void){
uint8_t w_data,r_data = 0;
memset(&gTPS,0,sizeof(TouchPoint_T));//清0
//GPIO初始化,INT中斷和復(fù)位引腳
ds_touch_gpio_init();
//復(fù)位初始化,拉低
ds_gpio_set_touch_rst(GPIO_RST_LOW);
vTaskDelay(50 / portTICK_PERIOD_MS);
ds_gpio_set_touch_rst(GPIO_RST_HIGH);
vTaskDelay(100 / portTICK_PERIOD_MS);
//I2C初始化
i2c_master_init();
vTaskDelay(100 / portTICK_PERIOD_MS);
w_data = 0;
//設(shè)置正常操作模式
i2c_master_write_slave(FI_DEVIDE_MODE,&w_data,1);
w_data = 22;
//設(shè)置觸摸有效值22,越小越靈敏
i2c_master_write_slave(FI_ID_G_THGROUP,&w_data,1);
i2c_master_read_slave(FI_ID_G_THGROUP,&r_data,1);
printf("init THGROUP = %d \n",r_data);
//設(shè)置激活周期 不能小于12 最大14
i2c_master_write_slave(FI_ID_G_PERIODACTIVE,&w_data,1);
i2c_master_read_slave(FI_ID_G_PERIODACTIVE,&r_data,1);
printf("init PERIODACTIVE = %d \n",r_data);
w_data = 0;
//中斷產(chǎn)生方式 持續(xù)電平
i2c_master_write_slave(FI_ID_G_MODE,&w_data,1);
i2c_master_read_slave(FI_ID_G_MODE,&r_data,1);
printf("init G_MODE = %d \n",r_data);
}
結(jié)合芯片手冊去看,更能夠理解,包括細節(jié)性的東西我都在代碼里注釋了,也是給自己留一個記錄。
實驗
OK,這些都寫好了之后,在主函數(shù)里面進行調(diào)用。
?編譯一下,成功之后上開發(fā)板進行測試!
可以看到,實驗是成功了!
觸摸屏初級的使用就已經(jīng)到這里完成了。
雖然寫的不多,但是花的時間是真的多啊。文章來源:http://www.zghlxwxcb.cn/news/detail-474760.html
以上參考正點原子linux驅(qū)動開發(fā)教程、FT6x36芯片手冊,野火STM32庫文件開發(fā)教程等...文章來源地址http://www.zghlxwxcb.cn/news/detail-474760.html
到了這里,關(guān)于ESP32開發(fā)---驅(qū)動觸摸屏的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!