ESP32开发---驱动触摸屏

采用I2C驱动触摸屏。

I2C多用于主控制器和从器件间的主从通信,在小数据量场合使用,传输距离短,任意时刻只能有一个主机等特性。

它有两条线,一条是SCL(串行时钟总线),另外一条是SDA(串行数据线),这两条数据需要接上拉电阻,总线空闲的时候SCL和SDA处于高电平。

 图来自正点原子linux驱动开发教程

I2C主要有起始位、停止位、数据传输、应答信号等。

I2C写时序

I2C的写时序相较于读时序是比较简单的,大概分以下几个步骤。

  1. 开始信号
  2. 发送 I2C设备地址,其中高七位是设备地址,最后一位是读写地址。
  3. 从机发送应答信号
  4. 重新发送开始信号
  5. 发送要写入数据的寄存器地址
  6. 从机发送应答信号
  7. 发送要写入寄存器的数据
  8. 从机发送应答信号
  9. 停止信号

 图来自正点原子linux驱动开发教程

I2C读时序

读时序相对于写时序来说复杂了一点,结束时多了一个非应答信号,以及写入寄存器地址之后要重新发从机的地址

总体的分为四步,跟写时序差不多。

  1. 发送设备地址
  2. 发送要读取的寄存器地址
  3. 重新发送设备地址
  4. 读取数据

  图来自正点原子linux驱动开发教程

OK,了解了基本的之后,回到驱动触摸屏上来。

我们驱动一个触摸屏需要使用到TP,这里使用的是FT6236。

查询芯片手册,可以发现FT6236有四根线。

两根是I2C需要的线,还有两根分别是INT(输入),RSTN(输出)。

到这里,就很简洁明了了。

我们需要用到GPIOI2C,以及TP的驱动。

首先,将RSTINT利用GPIO进行初始化,并进行相应的一些设置。、

GPIO

GPIO的使用已经很熟悉了,这里就不多说了。

简单的来说就是定义硬件图所使用的IO口,然后利用寄存器进行相应的初始化,实现我们的功能。

#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;//强转
    xQueueSendFromISR(gpio_evt_queue,&gpio_num,NULL);
}

//触摸屏GPIO口初始化
void ds_touch_gpio_init(void){
    gpio_config_t io_conf;
    //rst不需要中断,关闭中断
    io_conf.intr_type = GPIO_PIN_INTR_DISABLE;
    //设置模式为输出
    io_conf.mode = GPIO_MODE_OUTPUT;
    //设置引脚5
    io_conf.pin_bit_mask = GPIO_OUTPUT_PIN_SEL;
    //设置上拉和下拉
    io_conf.pull_down_en = 0;
    io_conf.pull_up_en = 0;
    //配置GPIO
    gpio_config(&io_conf);

    //无论上升沿还是下降沿都触发
    io_conf.intr_type = GPIO_INTR_ANYEDGE;
    //模式为输入,根据手册判定的
    io_conf.mode = GPIO_MODE_INPUT;
    //设置为引脚4
    io_conf.pin_bit_mask = GPIO_INTPUT_PIN_SEL;
    //设置为上拉,默认低电平有效
    io_conf.pull_up_en = 1;
    //配置GPIO
    gpio_config(&io_conf);

    //创建处理gpio事件的消息队列
    gpio_evt_queue = xQueueCreate(10,sizeof(uint32_t));

    //安装gpio中断服务
    gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT);
    gpio_isr_handler_add(GPIO_INTPUT_IO_0,gpio_isr_handler,(void*)GPIO_INTPUT_IO_0);

}
//复位
void ds_gpio_set_touch_rst(uint32_t level){

    gpio_set_level(GPIO_OUTPUT_IO_0,level);
}

GPIO比较简单。

I2C程序编写

I2C的程序编写尽量要参考一下ESP32官方给出的手册,包括读写时序的编写,避免出错。

相关API可以在官方文档里看。

这是官方例程里带寄存器读的例子,可以看到是先设置寄存器的地址,然后再重新写入从机地址的,接下来就可以模仿这个开始写。

首先设置读取地址。

//设置读取地址
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);//设置从机地址
    i2c_master_write_byte(cmd,u8Cmd,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_set_addr error\n");
    }
    return ret;

}

然后进行读时序。

//读取数据

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);//设置要读取的寄存器地址
vTaskDelay(30 / portTICK_RATE_MS);//延时24ms
//然后再次写入从机地址,并加上独标志位
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);//发送非应答信号
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;

}

写时序就比较简单了。

//写入数据,不需要发非应答信号
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的引脚重新定义一下,适合我们的开发板就行。

//初始化
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就已经可以了。

然后开始写最重要的一步,TP的驱动,这个是需要我们自己查芯片手册,然后一步步编写的。

TP驱动

观察芯片手册。

 圈出来的这里,就是我们用I2C写入或者读取的寄存器了。

在头文件里进行定义。

//由FT6236芯片手册查询得到各部分寄存器地址
#define FI_DEVIDE_MODE      0x00       //FT6236模式控制寄存器
#define FI_REG_NUM_FINGER   0x02       //触摸状态寄存器

#define FI_TP1_REG          0x03       //第一个触摸点数据地址
#define FI_TP2_REG          0x09       //第一个触摸点数据地址
#define FI_TP3_REG          0x0F       //第一个触摸点数据地址
#define FI_TP4_REG          0x15       //第一个触摸点数据地址
#define FI_TP5_REG          0x1B       //第一个触摸点数据地址

#define FI_ID_G_LIB_VERSION   0xA1       //版本
#define FI_ID_G_MODE          0xA4       //FT6236中断模式控制寄存器
#define FI_ID_G_THGROUP       0x80       //触摸有效值设置寄存器
#define FI_ID_G_PERIODACTIVE  0x88       //激活状态周期设置寄存器
#define Chip_Vendor_ID        0xA3       //芯片ID(0x36)
#define ID_G_FT6236ID         0xA8       //0x11

然后定义触摸点的结构体。

//触摸点相关数据结构定义
typedef struct
{
    //bit7:按下1/松开0
    //bit6: 没有按键按下0/有按键按下1
    //bit5:保留
    //bit4-bit0:触摸点按下有效标志,有效为1,对应五个触摸点
    uint8_t touch_sta; //触摸点的情况
    uint8_t touch_count; //触摸点数
    uint16_t x[5];
    uint16_t y[5];
    bool updata;
}TouchPoint_T;

定义一下触摸屏被按下或松开的标记。

#define TP_PRESS_DOWN    0x80  //触摸屏被按下,0x10000000,第七位为1
#define TP_COORD_UD      0x40  //触摸屏坐标更新,第六位为1

其他的就看自己的用途定义了,基本就是这些了。

然后就可以开始编写驱动了,主要参考ESP里的相关例程,以及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组触摸点,FT6236最大支持双触
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;

//扫描触摸屏寄存器状态、数据
static void scan_ft6236(void)
{
    uint8_t i=0;
    uint8_t sta = 0;//触摸点状态
    uint8_t buf[4] = {0};//这里是获取四个字节,分别是xH、xL、yH、yL
    uint8_t gestid = 0;//手势
    i2c_master_read_slave(0x02,&sta,1);//读取寄存器状态,读取的是个数!
    gTPS.touch_count = sta;
    i2c_master_read_slave(0x01,&gestid,1);//读取触摸点的状态

    if(sta&0x0f)//判断有无触摸点按下
    {

        gTPS.touch_sta = ~(0xFF << (sta & 0x0F));//将有效触摸点的个数转换为对应的标记
        for (i = 0; i < 2; i++)//最多同时两个触摸点
        {
            if (gTPS.touch_sta & (1 << i))
            {
                i2c_master_read_slave(FT6236_TPX_TBL[i], buf, 4); // 读取触摸点坐标
                gTPS.x[i] = (uint16_t)(((buf[0]&0x0F)<<8)+buf[1]);//清空XH的高四位,并左移8位与XL组成坐标
                gTPS.y[i] = (uint16_t)(((buf[2]&0x0F)<<8)+buf[3]);
            }
        }
        gTPS.touch_sta |= TP_PRESS_DOWN; //按下标记,置1

    }
    else //如果判断无触摸点按下,那么检查一下之前的标记
    {
        if(gTPS.touch_sta & TP_PRESS_DOWN)//如果之前被按下了
        {
            gTPS.touch_sta &= ~0x80; //清楚按下标记
        }
        else//
        {
            gTPS.x[0]=0;
            gTPS.y[0]=0;
            gTPS.touch_sta &= 0xe0;//将后五位清0,这一块还是有点疑虑
        }

    }
    
}

//转换为实际位置
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("触摸点的个数=%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("触摸点个数::%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中断和复位引脚
    ds_touch_gpio_init();
    //复位初始化,拉低
    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;
    //设置正常操作模式
    i2c_master_write_slave(FI_DEVIDE_MODE,&w_data,1);
    w_data = 22;
    //设置触摸有效值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);
    //设置激活周期 不能小于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;
	//中断产生方式 持续电平
	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);
}

结合芯片手册去看,更能够理解,包括细节性的东西我都在代码里注释了,也是给自己留一个记录。

实验

OK,这些都写好了之后,在主函数里面进行调用。

 编译一下,成功之后上开发板进行测试!

可以看到,实验是成功了!

触摸屏初级的使用就已经到这里完成了。

虽然写的不多,但是花的时间是真的多啊。


以上参考正点原子linux驱动开发教程、FT6x36芯片手册,野火STM32库文件开发教程等...

  • 2
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值