esp32开发与应用(2.8寸电容触摸屏的驱动)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】

        和电阻屏相比较,电容屏最大的好处就是触摸上去响应比较好。尤其是触摸,异常灵敏。不像电阻屏那样,需要给予一定压力之后,才能产生触摸信号。这次趁着学习电阻触摸屏的机会,也买了一个2.8寸的触摸屏,一起学习下。但是,电容屏价格要比电阻屏贵不少,这一点也需要注意下。

1、屏幕的驱动ic是ILI9341V

        根据搜索资料的说法,ILI9341V和ILI9341是差不多的。所以,一会准备直接拿之前2.8寸电阻屏的驱动代码来用。

2、屏幕连线

        这部分连线还是spi连线,和之前一样的。

3、触摸屏的IC是FT6336G

        这个IC和之前的XPT2046,他们的不同之处有两个地方。第一点,之前其实是量电压,需要自己做换算。这个就是直接给坐标。第二点,前面是spi总线,这个是iic总线。不过也多了一个rst和int,数量上其实差不多。

4、坐标的标定

        这一点都是少不了的,尤其是遇到横屏和竖屏的情形,部分电阻屏,还是电容屏。

5、先解决屏幕的驱动

        在调试屏幕驱动的时候,我们发现之前pin12接到了背光上面是没问题的。但是此时接到电容屏的时候,不但无法烧入,esp32也无法启动,直到后来转到了pin27时才能正常烧入。另外一点,比较奇怪的是,一开始屏幕的背景为黑色,后来设置为black,才能背景变为白色,这一点有点诡异。

#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "esp_timer.h"
#include "driver/spi_master.h"
#include "driver/gpio.h"
#include "lvgl.h"
 
//================================================
// ILI9341 CONFIG
//================================================
#define ILI9341_SPI_HOST    SPI2_HOST
#define ILI9341_SCLK_PIN    14
#define ILI9341_MOSI_PIN    13
#define ILI9341_CS_PIN      15
#define ILI9341_DC_PIN      2
#define ILI9341_RST_PIN     4
#define ILI9341_BL_PIN      27
 
#define ILI9341_WIDTH       320
#define ILI9341_HEIGHT      240
 
#define ILI9341_SWRESET     0x01
#define ILI9341_SLPOUT      0x11
#define ILI9341_NORON       0x13
#define ILI9341_INVOFF      0x20
#define ILI9341_DISPON      0x29
#define ILI9341_CASET       0x2A
#define ILI9341_RASET       0x2B
#define ILI9341_RAMWR       0x2C
#define ILI9341_COLMOD      0x3A
#define ILI9341_MADCTL      0x36
 
static spi_device_handle_t ili9341_spi;
static SemaphoreHandle_t lvgl_mutex;
static lv_obj_t *label_time;
static lv_obj_t *label_date;
 
//================================================
// SPI LOW LEVEL
//================================================
static void ili9341_send_cmd(uint8_t cmd)
{
    gpio_set_level(ILI9341_DC_PIN, 0);
    spi_transaction_t t = {
        .length = 8,
        .tx_buffer = &cmd
    };
    spi_device_transmit(ili9341_spi, &t);
}
 
static void ili9341_send_data(const uint8_t *data, size_t len)
{
    gpio_set_level(ILI9341_DC_PIN, 1);
    spi_transaction_t t = {
        .length = len * 8,
        .tx_buffer = data
    };
    spi_device_transmit(ili9341_spi, &t);
}
 
//================================================
// WINDOW
//================================================
static void ili9341_set_window(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1)
{
    ili9341_send_cmd(ILI9341_CASET);
    uint8_t ca[] = { x0 >> 8, x0 & 0xFF, x1 >> 8, x1 & 0xFF };
    ili9341_send_data(ca, 4);
 
    ili9341_send_cmd(ILI9341_RASET);
    uint8_t ra[] = { y0 >> 8, y0 & 0xFF, y1 >> 8, y1 & 0xFF };
    ili9341_send_data(ra, 4);
 
    ili9341_send_cmd(ILI9341_RAMWR);
}
 
//================================================
// LCD INIT
//================================================
static void ili9341_init(void)
{
    gpio_set_level(ILI9341_RST_PIN, 0);
    vTaskDelay(pdMS_TO_TICKS(100));
    gpio_set_level(ILI9341_RST_PIN, 1);
    vTaskDelay(pdMS_TO_TICKS(120));
 
    ili9341_send_cmd(ILI9341_SWRESET);
    vTaskDelay(pdMS_TO_TICKS(120));
 
    ili9341_send_cmd(ILI9341_SLPOUT);
    vTaskDelay(pdMS_TO_TICKS(120));
 
    ili9341_send_cmd(ILI9341_COLMOD);
    ili9341_send_data((uint8_t[]){0x55}, 1);
 
    ili9341_send_cmd(ILI9341_MADCTL);
    ili9341_send_data((uint8_t[]){0x28}, 1);
 
    ili9341_send_cmd(ILI9341_DISPON);
 
    gpio_set_level(ILI9341_BL_PIN, 1);
}
 
//================================================
// SPI INIT
//================================================
static void ili9341_spi_init(void)
{
    gpio_config_t io = {
        .pin_bit_mask =
            (1ULL << ILI9341_DC_PIN) |
            (1ULL << ILI9341_RST_PIN) |
            (1ULL << ILI9341_BL_PIN),
        .mode = GPIO_MODE_OUTPUT
    };
    gpio_config(&io);
 
    spi_bus_config_t buscfg = {
        .mosi_io_num = ILI9341_MOSI_PIN,
        .miso_io_num = -1,
        .sclk_io_num = ILI9341_SCLK_PIN,
        .max_transfer_sz = ILI9341_WIDTH * ILI9341_HEIGHT * 2
    };
 
    spi_device_interface_config_t devcfg = {
        .clock_speed_hz = 20 * 1000 * 1000,
        .mode = 0,
        .spics_io_num = ILI9341_CS_PIN,
        .queue_size = 7
    };
 
    spi_bus_initialize(ILI9341_SPI_HOST, &buscfg, SPI_DMA_CH_AUTO);
    spi_bus_add_device(ILI9341_SPI_HOST, &devcfg, &ili9341_spi);
}
 
//================================================
// LVGL FLUSH
//================================================
static void ili9341_flush_cb(lv_disp_drv_t *drv,
                             const lv_area_t *area,
                             lv_color_t *color_map)
{
    uint32_t w = area->x2 - area->x1 + 1;
    uint32_t h = area->y2 - area->y1 + 1;
    uint32_t pixels = w * h;
 
    ili9341_set_window(area->x1, area->y1, area->x2, area->y2);
 
    gpio_set_level(ILI9341_DC_PIN, 1);
 
    spi_transaction_t t = {
        .length = pixels * 16,
        .tx_buffer = color_map
    };
 
    spi_device_transmit(ili9341_spi, &t);
    lv_disp_flush_ready(drv);
}
 
//================================================
// UI
//================================================
static void create_ui(void)
{
    lv_obj_set_style_bg_color(lv_scr_act(), lv_color_black(), 0); // lv_color_white()
    lv_obj_set_style_bg_opa(lv_scr_act(), LV_OPA_COVER, 0);
 
    label_time = lv_label_create(lv_scr_act());
    lv_label_set_text(label_time, "00:00:00");
 
    lv_obj_set_style_text_color(label_time,
                                lv_color_hex(0xff0000), // brg
                                0);
 
    lv_obj_set_style_text_font(label_time,
                              &lv_font_montserrat_32,
                              0);
 
    lv_obj_align(label_time, LV_ALIGN_CENTER, 0, -30);
 
    label_date = lv_label_create(lv_scr_act());
    lv_label_set_text(label_date, "2026-06-17");
 
    lv_obj_set_style_text_color(label_date,
                                lv_color_hex(0x00ff00), // brg
                                0);
 
    lv_obj_set_style_text_font(label_date,
                              &lv_font_montserrat_24,
                              0);
 
    lv_obj_align(label_date, LV_ALIGN_CENTER, 0, 35);
}
 
//================================================
// LVGL TICK
//================================================
static void lv_tick_cb(void *arg)
{
    lv_tick_inc(1);
}
 
//================================================
// CLOCK TASK
//================================================
static void clock_task(void *arg)
{
    int h = 0, m = 0, s = 0;
    char buf[32];
 
    while (1)
    {
        vTaskDelay(pdMS_TO_TICKS(1000));
 
        s++;
        if (s >= 60) { s = 0; m++; }
        if (m >= 60) { m = 0; h++; }
        if (h >= 24) { h = 0; }
 
        sprintf(buf, "%02d:%02d:%02d", h, m, s);
 
        if (xSemaphoreTake(lvgl_mutex, portMAX_DELAY))
        {
            lv_label_set_text(label_time, buf);
            xSemaphoreGive(lvgl_mutex);
        }
    }
}
 
//================================================
// MAIN
//================================================
void app_main(void)
{
    printf("ILI9341 START\n");
 
    lvgl_mutex = xSemaphoreCreateMutex();
 
    ili9341_spi_init();
    ili9341_init();
 
    lv_init();
 
    esp_timer_handle_t timer;
    const esp_timer_create_args_t tick_args = {
        .callback = lv_tick_cb,
        .name = "lv_tick"
    };
 
    esp_timer_create(&tick_args, &timer);
    esp_timer_start_periodic(timer, 1000);
 
    static lv_disp_draw_buf_t draw_buf;
    static lv_color_t buf1[ILI9341_WIDTH * 20];
    static lv_color_t buf2[ILI9341_WIDTH * 20];
 
    lv_disp_draw_buf_init(&draw_buf,
                          buf1,
                          buf2,
                          ILI9341_WIDTH * 20);
 
    static lv_disp_drv_t disp_drv;
    lv_disp_drv_init(&disp_drv);
 
    disp_drv.hor_res = ILI9341_WIDTH;
    disp_drv.ver_res = ILI9341_HEIGHT;
    disp_drv.flush_cb = ili9341_flush_cb;
    disp_drv.draw_buf = &draw_buf;
 
    lv_disp_drv_register(&disp_drv);
 
    create_ui();
 
    xTaskCreate(clock_task, "clock_task", 4096, NULL, 5, NULL);
 
    while (1)
    {
        lv_timer_handler();
        vTaskDelay(pdMS_TO_TICKS(5));
    }
}

6、再解决触摸屏的问题

        电容触摸屏其实没有什么,主要就是这个表面异常敏感。当我们一次按下去的时候,可能有好几个中断,这个时候就要做好同一个坐标的确认。如果发现是连续同一个坐标,直接skip即可。只有不同坐标的时候,才留下来。这里做的其实比较简单,就是起了一个任务,有手指按下来的时候,循环打印坐标。

#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "esp_timer.h"
#include "driver/spi_master.h"
#include "driver/gpio.h"
#include "driver/i2c.h"
#include "lvgl.h"

//================================================
// FT6336G CONFIG
//================================================
#define FT6336G_I2C_ADDR     0x38
#define FT6336G_I2C_PORT     I2C_NUM_0
#define FT6336G_SDA_PIN      21
#define FT6336G_SCL_PIN      22
#define FT6336G_INT_PIN      34  // Interrupt pin (input only)
#define FT6336G_RST_PIN      18  // Reset pin

// FT6336G registers
#define FT6336G_DEV_MODE     0x00
#define FT6336G_NUM_TOUCH    0x02
#define FT6336G_TOUCH1_XH    0x03
#define FT6336G_TOUCH1_YH    0x05
#define FT6336G_TOUCH1_XL    0x04
#define FT6336G_TOUCH1_YL    0x06

//================================================
// ILI9341 CONFIG
//================================================
#define ILI9341_SPI_HOST    SPI2_HOST
#define ILI9341_SCLK_PIN    14
#define ILI9341_MOSI_PIN    13
#define ILI9341_CS_PIN      15
#define ILI9341_DC_PIN      2
#define ILI9341_RST_PIN     4
#define ILI9341_BL_PIN      27

#define ILI9341_WIDTH       320
#define ILI9341_HEIGHT      240

#define ILI9341_SWRESET     0x01
#define ILI9341_SLPOUT      0x11
#define ILI9341_NORON       0x13
#define ILI9341_INVOFF      0x20
#define ILI9341_DISPON      0x29
#define ILI9341_CASET       0x2A
#define ILI9341_RASET       0x2B
#define ILI9341_RAMWR       0x2C
#define ILI9341_COLMOD      0x3A
#define ILI9341_MADCTL      0x36

static spi_device_handle_t ili9341_spi;
static SemaphoreHandle_t lvgl_mutex;
static lv_obj_t *label_time;
static lv_obj_t *label_date;

//================================================
// FT6336G I2C Functions
//================================================
static esp_err_t ft6336g_i2c_write(uint8_t reg, uint8_t data)
{
    i2c_cmd_handle_t cmd = i2c_cmd_link_create();
    i2c_master_start(cmd);
    i2c_master_write_byte(cmd, FT6336G_I2C_ADDR << 1, I2C_MASTER_ACK);
    i2c_master_write_byte(cmd, reg, I2C_MASTER_ACK);
    i2c_master_write_byte(cmd, data, I2C_MASTER_ACK);
    i2c_master_stop(cmd);
    esp_err_t ret = i2c_master_cmd_begin(FT6336G_I2C_PORT, cmd, 1000 / portTICK_PERIOD_MS);
    i2c_cmd_link_delete(cmd);
    return ret;
}

static esp_err_t ft6336g_i2c_read(uint8_t reg, uint8_t *data, size_t len)
{
    i2c_cmd_handle_t cmd = i2c_cmd_link_create();
    i2c_master_start(cmd);
    i2c_master_write_byte(cmd, FT6336G_I2C_ADDR << 1, I2C_MASTER_ACK);
    i2c_master_write_byte(cmd, reg, I2C_MASTER_ACK);
    i2c_master_start(cmd);
    i2c_master_write_byte(cmd, (FT6336G_I2C_ADDR << 1) | 0x01, I2C_MASTER_ACK);
    
    if (len > 1) {
        i2c_master_read(cmd, data, len - 1, I2C_MASTER_ACK);
    }
    i2c_master_read_byte(cmd, data + len - 1, I2C_MASTER_NACK);
    
    i2c_master_stop(cmd);
    esp_err_t ret = i2c_master_cmd_begin(FT6336G_I2C_PORT, cmd, 1000 / portTICK_PERIOD_MS);
    i2c_cmd_link_delete(cmd);
    return ret;
}

//================================================
// FT6336G INIT
//================================================
static void ft6336g_init(void)
{
    printf("Initializing FT6336G...\n");
    
    // Configure RST pin (output)
    gpio_config_t rst_io = {
        .pin_bit_mask = 1ULL << FT6336G_RST_PIN,
        .mode = GPIO_MODE_OUTPUT,
        .pull_up_en = GPIO_PULLUP_DISABLE,
        .pull_down_en = GPIO_PULLDOWN_DISABLE,
    };
    gpio_config(&rst_io);
    
    // Reset FT6336G
    gpio_set_level(FT6336G_RST_PIN, 0);
    vTaskDelay(pdMS_TO_TICKS(10));
    gpio_set_level(FT6336G_RST_PIN, 1);
    vTaskDelay(pdMS_TO_TICKS(50));
    
    // Initialize I2C
    i2c_config_t conf = {
        .mode = I2C_MODE_MASTER,
        .sda_io_num = FT6336G_SDA_PIN,
        .scl_io_num = FT6336G_SCL_PIN,
        .sda_pullup_en = GPIO_PULLUP_ENABLE,
        .scl_pullup_en = GPIO_PULLUP_ENABLE,
        .master.clk_speed = 100000,  // Use lower speed for better reliability
    };
    
    esp_err_t ret = i2c_param_config(FT6336G_I2C_PORT, &conf);
    if (ret != ESP_OK) {
        printf("I2C param config failed: %d\n", ret);
        return;
    }
    
    ret = i2c_driver_install(FT6336G_I2C_PORT, conf.mode, 0, 0, 0);
    if (ret != ESP_OK) {
        printf("I2C driver install failed: %d\n", ret);
        return;
    }
    
    // Check if FT6336G is present by reading register
    uint8_t data;
    ret = ft6336g_i2c_read(FT6336G_NUM_TOUCH, &data, 1);
    if (ret == ESP_OK) {
        printf("FT6336G detected successfully!\n");
        
        // Set device to normal mode
        ft6336g_i2c_write(FT6336G_DEV_MODE, 0x00);
        printf("FT6336G set to normal mode\n");
    } else {
        printf("FT6336G not detected, error: %d\n", ret);
    }
}

//================================================
// Touch Task (Polling mode)
//================================================
static void touch_task(void *arg)
{
    printf("Touch task started\n");
    
    // Store last touch coordinates to filter duplicates
    uint16_t last_x = 0;
    uint16_t last_y = 0;
    bool first_touch = true;
    
    while (1) {
        uint8_t num_touch;
        esp_err_t ret = ft6336g_i2c_read(FT6336G_NUM_TOUCH, &num_touch, 1);
        
        if (ret == ESP_OK) {
            if (num_touch > 0 && num_touch <= 5) {  // FT6336G supports up to 5 touches
                uint8_t touch_data[4];
                ret = ft6336g_i2c_read(FT6336G_TOUCH1_XH, touch_data, 4);
                
                if (ret == ESP_OK) {
                    uint16_t x = ((touch_data[0] & 0x0F) << 8) | touch_data[1];
                    uint16_t y = ((touch_data[2] & 0x0F) << 8) | touch_data[3];

                    uint16_t t = x; // add by feixiaoxing
                    x = y;
                    y = 240 - t;
                    
                    // Valid touch coordinates are typically 0-320 for X and 0-240 for Y
                    if (x > 0 && y > 0) {
                        // Only print if coordinates changed or it's the first touch
                        if (first_touch || (x != last_x || y != last_y)) {
                            printf("Touch detected: X=%d, Y=%d\n", x, y);
                            last_x = x;
                            last_y = y;
                            first_touch = false;
                        }
                    }
                } else {
                    printf("Failed to read touch data: %d\n", ret);
                }
            } else if (num_touch == 0) {
                // Reset when no touch detected
                first_touch = true;
            }
        } else {
            printf("Failed to read touch status: %d\n", ret);
        }
        
        vTaskDelay(pdMS_TO_TICKS(50));  // Poll every 50ms
    }
}

//================================================
// SPI LOW LEVEL
//================================================
static void ili9341_send_cmd(uint8_t cmd)
{
    gpio_set_level(ILI9341_DC_PIN, 0);
    spi_transaction_t t = {
        .length = 8,
        .tx_buffer = &cmd
    };
    spi_device_transmit(ili9341_spi, &t);
}

static void ili9341_send_data(const uint8_t *data, size_t len)
{
    gpio_set_level(ILI9341_DC_PIN, 1);
    spi_transaction_t t = {
        .length = len * 8,
        .tx_buffer = data
    };
    spi_device_transmit(ili9341_spi, &t);
}

//================================================
// WINDOW
//================================================
static void ili9341_set_window(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1)
{
    ili9341_send_cmd(ILI9341_CASET);
    uint8_t ca[] = { x0 >> 8, x0 & 0xFF, x1 >> 8, x1 & 0xFF };
    ili9341_send_data(ca, 4);

    ili9341_send_cmd(ILI9341_RASET);
    uint8_t ra[] = { y0 >> 8, y0 & 0xFF, y1 >> 8, y1 & 0xFF };
    ili9341_send_data(ra, 4);

    ili9341_send_cmd(ILI9341_RAMWR);
}

//================================================
// LCD INIT
//================================================
static void ili9341_init(void)
{
    gpio_set_level(ILI9341_RST_PIN, 0);
    vTaskDelay(pdMS_TO_TICKS(100));
    gpio_set_level(ILI9341_RST_PIN, 1);
    vTaskDelay(pdMS_TO_TICKS(120));

    ili9341_send_cmd(ILI9341_SWRESET);
    vTaskDelay(pdMS_TO_TICKS(120));

    ili9341_send_cmd(ILI9341_SLPOUT);
    vTaskDelay(pdMS_TO_TICKS(120));

    ili9341_send_cmd(ILI9341_COLMOD);
    ili9341_send_data((uint8_t[]){0x55}, 1);

    ili9341_send_cmd(ILI9341_MADCTL);
    ili9341_send_data((uint8_t[]){0x28}, 1);

    ili9341_send_cmd(ILI9341_DISPON);

    gpio_set_level(ILI9341_BL_PIN, 1);
}

//================================================
// SPI INIT
//================================================
static void ili9341_spi_init(void)
{
    gpio_config_t io = {
        .pin_bit_mask =
            (1ULL << ILI9341_DC_PIN) |
            (1ULL << ILI9341_RST_PIN) |
            (1ULL << ILI9341_BL_PIN),
        .mode = GPIO_MODE_OUTPUT
    };
    gpio_config(&io);

    spi_bus_config_t buscfg = {
        .mosi_io_num = ILI9341_MOSI_PIN,
        .miso_io_num = -1,
        .sclk_io_num = ILI9341_SCLK_PIN,
        .max_transfer_sz = ILI9341_WIDTH * ILI9341_HEIGHT * 2
    };

    spi_device_interface_config_t devcfg = {
        .clock_speed_hz = 20 * 1000 * 1000,
        .mode = 0,
        .spics_io_num = ILI9341_CS_PIN,
        .queue_size = 7
    };

    spi_bus_initialize(ILI9341_SPI_HOST, &buscfg, SPI_DMA_CH_AUTO);
    spi_bus_add_device(ILI9341_SPI_HOST, &devcfg, &ili9341_spi);
}

//================================================
// LVGL FLUSH
//================================================
static void ili9341_flush_cb(lv_disp_drv_t *drv,
                             const lv_area_t *area,
                             lv_color_t *color_map)
{
    uint32_t w = area->x2 - area->x1 + 1;
    uint32_t h = area->y2 - area->y1 + 1;
    uint32_t pixels = w * h;

    ili9341_set_window(area->x1, area->y1, area->x2, area->y2);

    gpio_set_level(ILI9341_DC_PIN, 1);

    spi_transaction_t t = {
        .length = pixels * 16,
        .tx_buffer = color_map
    };

    spi_device_transmit(ili9341_spi, &t);
    lv_disp_flush_ready(drv);
}

//================================================
// UI
//================================================
static void create_ui(void)
{
    lv_obj_set_style_bg_color(lv_scr_act(), lv_color_black(), 0);
    lv_obj_set_style_bg_opa(lv_scr_act(), LV_OPA_COVER, 0);

    label_time = lv_label_create(lv_scr_act());
    lv_label_set_text(label_time, "00:00:00");

    lv_obj_set_style_text_color(label_time,
                                lv_color_hex(0xff0000),
                                0);

    lv_obj_set_style_text_font(label_time,
                              &lv_font_montserrat_32,
                              0);

    lv_obj_align(label_time, LV_ALIGN_CENTER, 0, -30);

    label_date = lv_label_create(lv_scr_act());
    lv_label_set_text(label_date, "2026-06-17");

    lv_obj_set_style_text_color(label_date,
                                lv_color_hex(0x00ff00),
                                0);

    lv_obj_set_style_text_font(label_date,
                              &lv_font_montserrat_24,
                              0);

    lv_obj_align(label_date, LV_ALIGN_CENTER, 0, 35);
}

//================================================
// LVGL TICK
//================================================
static void lv_tick_cb(void *arg)
{
    lv_tick_inc(1);
}

//================================================
// CLOCK TASK
//================================================
static void clock_task(void *arg)
{
    int h = 0, m = 0, s = 0;
    char buf[32];

    while (1)
    {
        vTaskDelay(pdMS_TO_TICKS(1000));

        s++;
        if (s >= 60) { s = 0; m++; }
        if (m >= 60) { m = 0; h++; }
        if (h >= 24) { h = 0; }

        sprintf(buf, "%02d:%02d:%02d", h, m, s);

        if (xSemaphoreTake(lvgl_mutex, portMAX_DELAY))
        {
            lv_label_set_text(label_time, buf);
            xSemaphoreGive(lvgl_mutex);
        }
    }
}

//================================================
// MAIN
//================================================
void app_main(void)
{
    printf("ILI9341 START\n");

    lvgl_mutex = xSemaphoreCreateMutex();

    ili9341_spi_init();
    ili9341_init();
    
    // Initialize FT6336G touch screen
    ft6336g_init();
    xTaskCreate(touch_task, "touch_task", 2048, NULL, 5, NULL);

    lv_init();

    esp_timer_handle_t timer;
    const esp_timer_create_args_t tick_args = {
        .callback = lv_tick_cb,
        .name = "lv_tick"
    };

    esp_timer_create(&tick_args, &timer);
    esp_timer_start_periodic(timer, 1000);

    static lv_disp_draw_buf_t draw_buf;
    static lv_color_t buf1[ILI9341_WIDTH * 20];
    static lv_color_t buf2[ILI9341_WIDTH * 20];

    lv_disp_draw_buf_init(&draw_buf,
                          buf1,
                          buf2,
                          ILI9341_WIDTH * 20);

    static lv_disp_drv_t disp_drv;
    lv_disp_drv_init(&disp_drv);

    disp_drv.hor_res = ILI9341_WIDTH;
    disp_drv.ver_res = ILI9341_HEIGHT;
    disp_drv.flush_cb = ili9341_flush_cb;
    disp_drv.draw_buf = &draw_buf;

    lv_disp_drv_register(&disp_drv);

    create_ui();

    xTaskCreate(clock_task, "clock_task", 4096, NULL, 5, NULL);

    while (1)
    {
        lv_timer_handler();
        vTaskDelay(pdMS_TO_TICKS(5));
    }
}

7、坐标处理中的小trick

        前面说过,触摸ic会直接给坐标信息,但它还是解决不了横屏、竖屏的问题。所以这一点,我们需要注意两个事情。第一,找规律;第二,自己写匹配算法。看看四个点上,x和y要如何正确去匹配换算,比如像这样,

uint16_t t = x; // add by feixiaoxing
x = y;
y = 240 - t;

        自己有找规律的能力,遇到什么情况,都不会心慌了。

8、把触摸功能加进来

        我们发现,屏幕开启后,时间是一直更新的。这个时候就可以加一个flag。当触摸一下,时间停止更新。再触摸一下,继续更新。通过这个小demo,可以加深一下自己对触摸屏的认识。

#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "esp_timer.h"
#include "driver/spi_master.h"
#include "driver/gpio.h"
#include "driver/i2c.h"
#include "lvgl.h"

//================================================
// FT6336G CONFIG
//================================================
#define FT6336G_I2C_ADDR     0x38
#define FT6336G_I2C_PORT     I2C_NUM_0
#define FT6336G_SDA_PIN      21
#define FT6336G_SCL_PIN      22
#define FT6336G_INT_PIN      34  // Interrupt pin (input only)
#define FT6336G_RST_PIN      18  // Reset pin

// FT6336G registers
#define FT6336G_DEV_MODE     0x00
#define FT6336G_NUM_TOUCH    0x02
#define FT6336G_TOUCH1_XH    0x03
#define FT6336G_TOUCH1_YH    0x05
#define FT6336G_TOUCH1_XL    0x04
#define FT6336G_TOUCH1_YL    0x06

//================================================
// ILI9341 CONFIG
//================================================
#define ILI9341_SPI_HOST    SPI2_HOST
#define ILI9341_SCLK_PIN    14
#define ILI9341_MOSI_PIN    13
#define ILI9341_CS_PIN      15
#define ILI9341_DC_PIN      2
#define ILI9341_RST_PIN     4
#define ILI9341_BL_PIN      27

#define ILI9341_WIDTH       320
#define ILI9341_HEIGHT      240

#define ILI9341_SWRESET     0x01
#define ILI9341_SLPOUT      0x11
#define ILI9341_NORON       0x13
#define ILI9341_INVOFF      0x20
#define ILI9341_DISPON      0x29
#define ILI9341_CASET       0x2A
#define ILI9341_RASET       0x2B
#define ILI9341_RAMWR       0x2C
#define ILI9341_COLMOD      0x3A
#define ILI9341_MADCTL      0x36

static spi_device_handle_t ili9341_spi;
static SemaphoreHandle_t lvgl_mutex;
static lv_obj_t *label_time;
static lv_obj_t *label_date;
static int update_flag = 1;

//================================================
// FT6336G I2C Functions
//================================================
static esp_err_t ft6336g_i2c_write(uint8_t reg, uint8_t data)
{
    i2c_cmd_handle_t cmd = i2c_cmd_link_create();
    i2c_master_start(cmd);
    i2c_master_write_byte(cmd, FT6336G_I2C_ADDR << 1, I2C_MASTER_ACK);
    i2c_master_write_byte(cmd, reg, I2C_MASTER_ACK);
    i2c_master_write_byte(cmd, data, I2C_MASTER_ACK);
    i2c_master_stop(cmd);
    esp_err_t ret = i2c_master_cmd_begin(FT6336G_I2C_PORT, cmd, 1000 / portTICK_PERIOD_MS);
    i2c_cmd_link_delete(cmd);
    return ret;
}

static esp_err_t ft6336g_i2c_read(uint8_t reg, uint8_t *data, size_t len)
{
    i2c_cmd_handle_t cmd = i2c_cmd_link_create();
    i2c_master_start(cmd);
    i2c_master_write_byte(cmd, FT6336G_I2C_ADDR << 1, I2C_MASTER_ACK);
    i2c_master_write_byte(cmd, reg, I2C_MASTER_ACK);
    i2c_master_start(cmd);
    i2c_master_write_byte(cmd, (FT6336G_I2C_ADDR << 1) | 0x01, I2C_MASTER_ACK);
    
    if (len > 1) {
        i2c_master_read(cmd, data, len - 1, I2C_MASTER_ACK);
    }
    i2c_master_read_byte(cmd, data + len - 1, I2C_MASTER_NACK);
    
    i2c_master_stop(cmd);
    esp_err_t ret = i2c_master_cmd_begin(FT6336G_I2C_PORT, cmd, 1000 / portTICK_PERIOD_MS);
    i2c_cmd_link_delete(cmd);
    return ret;
}

//================================================
// FT6336G INIT
//================================================
static void ft6336g_init(void)
{
    printf("Initializing FT6336G...\n");
    
    // Configure RST pin (output)
    gpio_config_t rst_io = {
        .pin_bit_mask = 1ULL << FT6336G_RST_PIN,
        .mode = GPIO_MODE_OUTPUT,
        .pull_up_en = GPIO_PULLUP_DISABLE,
        .pull_down_en = GPIO_PULLDOWN_DISABLE,
    };
    gpio_config(&rst_io);
    
    // Reset FT6336G
    gpio_set_level(FT6336G_RST_PIN, 0);
    vTaskDelay(pdMS_TO_TICKS(10));
    gpio_set_level(FT6336G_RST_PIN, 1);
    vTaskDelay(pdMS_TO_TICKS(50));
    
    // Initialize I2C
    i2c_config_t conf = {
        .mode = I2C_MODE_MASTER,
        .sda_io_num = FT6336G_SDA_PIN,
        .scl_io_num = FT6336G_SCL_PIN,
        .sda_pullup_en = GPIO_PULLUP_ENABLE,
        .scl_pullup_en = GPIO_PULLUP_ENABLE,
        .master.clk_speed = 100000,  // Use lower speed for better reliability
    };
    
    esp_err_t ret = i2c_param_config(FT6336G_I2C_PORT, &conf);
    if (ret != ESP_OK) {
        printf("I2C param config failed: %d\n", ret);
        return;
    }
    
    ret = i2c_driver_install(FT6336G_I2C_PORT, conf.mode, 0, 0, 0);
    if (ret != ESP_OK) {
        printf("I2C driver install failed: %d\n", ret);
        return;
    }
    
    // Check if FT6336G is present by reading register
    uint8_t data;
    ret = ft6336g_i2c_read(FT6336G_NUM_TOUCH, &data, 1);
    if (ret == ESP_OK) {
        printf("FT6336G detected successfully!\n");
        
        // Set device to normal mode
        ft6336g_i2c_write(FT6336G_DEV_MODE, 0x00);
        printf("FT6336G set to normal mode\n");
    } else {
        printf("FT6336G not detected, error: %d\n", ret);
    }
}

//================================================
// Touch Task (Polling mode)
//================================================
static void touch_task(void *arg)
{
    printf("Touch task started\n");
    
    // Store last touch coordinates to filter duplicates
    uint16_t last_x = 0;
    uint16_t last_y = 0;
    bool first_touch = true;
    
    while (1) {
        uint8_t num_touch;
        esp_err_t ret = ft6336g_i2c_read(FT6336G_NUM_TOUCH, &num_touch, 1);
        
        if (ret == ESP_OK) {
            if (num_touch > 0 && num_touch <= 5) {  // FT6336G supports up to 5 touches
                uint8_t touch_data[4];
                ret = ft6336g_i2c_read(FT6336G_TOUCH1_XH, touch_data, 4);
                
                if (ret == ESP_OK) {
                    uint16_t x = ((touch_data[0] & 0x0F) << 8) | touch_data[1];
                    uint16_t y = ((touch_data[2] & 0x0F) << 8) | touch_data[3];

                    uint16_t t = x; // add by feixiaoxing
                    x = y;
                    y = 240 - t;
                    
                    // Valid touch coordinates are typically 0-320 for X and 0-240 for Y
                    if (x > 0 && y > 0) {
                        // Only print if coordinates changed or it's the first touch
                        if (first_touch || (x != last_x || y != last_y)) {
                            printf("Touch detected: X=%d, Y=%d\n", x, y);
                            last_x = x;
                            last_y = y;
                            first_touch = false;
                            
                            update_flag = 1 - update_flag; // add by feixiaoxing
                        }
                    }
                } else {
                    printf("Failed to read touch data: %d\n", ret);
                }
            } else if (num_touch == 0) {
                // Reset when no touch detected
                first_touch = true;
            }
        } else {
            printf("Failed to read touch status: %d\n", ret);
        }
        
        vTaskDelay(pdMS_TO_TICKS(50));  // Poll every 50ms
    }
}

//================================================
// SPI LOW LEVEL
//================================================
static void ili9341_send_cmd(uint8_t cmd)
{
    gpio_set_level(ILI9341_DC_PIN, 0);
    spi_transaction_t t = {
        .length = 8,
        .tx_buffer = &cmd
    };
    spi_device_transmit(ili9341_spi, &t);
}

static void ili9341_send_data(const uint8_t *data, size_t len)
{
    gpio_set_level(ILI9341_DC_PIN, 1);
    spi_transaction_t t = {
        .length = len * 8,
        .tx_buffer = data
    };
    spi_device_transmit(ili9341_spi, &t);
}

//================================================
// WINDOW
//================================================
static void ili9341_set_window(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1)
{
    ili9341_send_cmd(ILI9341_CASET);
    uint8_t ca[] = { x0 >> 8, x0 & 0xFF, x1 >> 8, x1 & 0xFF };
    ili9341_send_data(ca, 4);

    ili9341_send_cmd(ILI9341_RASET);
    uint8_t ra[] = { y0 >> 8, y0 & 0xFF, y1 >> 8, y1 & 0xFF };
    ili9341_send_data(ra, 4);

    ili9341_send_cmd(ILI9341_RAMWR);
}

//================================================
// LCD INIT
//================================================
static void ili9341_init(void)
{
    gpio_set_level(ILI9341_RST_PIN, 0);
    vTaskDelay(pdMS_TO_TICKS(100));
    gpio_set_level(ILI9341_RST_PIN, 1);
    vTaskDelay(pdMS_TO_TICKS(120));

    ili9341_send_cmd(ILI9341_SWRESET);
    vTaskDelay(pdMS_TO_TICKS(120));

    ili9341_send_cmd(ILI9341_SLPOUT);
    vTaskDelay(pdMS_TO_TICKS(120));

    ili9341_send_cmd(ILI9341_COLMOD);
    ili9341_send_data((uint8_t[]){0x55}, 1);

    ili9341_send_cmd(ILI9341_MADCTL);
    ili9341_send_data((uint8_t[]){0x28}, 1);

    ili9341_send_cmd(ILI9341_DISPON);

    gpio_set_level(ILI9341_BL_PIN, 1);
}

//================================================
// SPI INIT
//================================================
static void ili9341_spi_init(void)
{
    gpio_config_t io = {
        .pin_bit_mask =
            (1ULL << ILI9341_DC_PIN) |
            (1ULL << ILI9341_RST_PIN) |
            (1ULL << ILI9341_BL_PIN),
        .mode = GPIO_MODE_OUTPUT
    };
    gpio_config(&io);

    spi_bus_config_t buscfg = {
        .mosi_io_num = ILI9341_MOSI_PIN,
        .miso_io_num = -1,
        .sclk_io_num = ILI9341_SCLK_PIN,
        .max_transfer_sz = ILI9341_WIDTH * ILI9341_HEIGHT * 2
    };

    spi_device_interface_config_t devcfg = {
        .clock_speed_hz = 20 * 1000 * 1000,
        .mode = 0,
        .spics_io_num = ILI9341_CS_PIN,
        .queue_size = 7
    };

    spi_bus_initialize(ILI9341_SPI_HOST, &buscfg, SPI_DMA_CH_AUTO);
    spi_bus_add_device(ILI9341_SPI_HOST, &devcfg, &ili9341_spi);
}

//================================================
// LVGL FLUSH
//================================================
static void ili9341_flush_cb(lv_disp_drv_t *drv,
                             const lv_area_t *area,
                             lv_color_t *color_map)
{
    uint32_t w = area->x2 - area->x1 + 1;
    uint32_t h = area->y2 - area->y1 + 1;
    uint32_t pixels = w * h;

    ili9341_set_window(area->x1, area->y1, area->x2, area->y2);

    gpio_set_level(ILI9341_DC_PIN, 1);

    spi_transaction_t t = {
        .length = pixels * 16,
        .tx_buffer = color_map
    };

    spi_device_transmit(ili9341_spi, &t);
    lv_disp_flush_ready(drv);
}

//================================================
// UI
//================================================
static void create_ui(void)
{
    lv_obj_set_style_bg_color(lv_scr_act(), lv_color_black(), 0);
    lv_obj_set_style_bg_opa(lv_scr_act(), LV_OPA_COVER, 0);

    label_time = lv_label_create(lv_scr_act());
    lv_label_set_text(label_time, "00:00:00");

    lv_obj_set_style_text_color(label_time,
                                lv_color_hex(0xff0000),
                                0);

    lv_obj_set_style_text_font(label_time,
                              &lv_font_montserrat_32,
                              0);

    lv_obj_align(label_time, LV_ALIGN_CENTER, 0, -30);

    label_date = lv_label_create(lv_scr_act());
    lv_label_set_text(label_date, "2026-06-17");

    lv_obj_set_style_text_color(label_date,
                                lv_color_hex(0x00ff00),
                                0);

    lv_obj_set_style_text_font(label_date,
                              &lv_font_montserrat_24,
                              0);

    lv_obj_align(label_date, LV_ALIGN_CENTER, 0, 35);
}

//================================================
// LVGL TICK
//================================================
static void lv_tick_cb(void *arg)
{
    lv_tick_inc(1);
}

//================================================
// CLOCK TASK
//================================================
static void clock_task(void *arg)
{
    int h = 0, m = 0, s = 0;
    char buf[64];

    while (1)
    {
        vTaskDelay(pdMS_TO_TICKS(1000));
        if (update_flag == 0) { continue; }

        s++;
        if (s >= 60) { s = 0; m++; }
        if (m >= 60) { m = 0; h++; }
        if (h >= 24) { h = 0; }

        sprintf(buf, "%02d:%02d:%02d", h, m, s);

        if (xSemaphoreTake(lvgl_mutex, portMAX_DELAY))
        {
            lv_label_set_text(label_time, buf);
            xSemaphoreGive(lvgl_mutex);
        }
    }
}

//================================================
// MAIN
//================================================
void app_main(void)
{
    printf("ILI9341 START\n");

    lvgl_mutex = xSemaphoreCreateMutex();

    ili9341_spi_init();
    ili9341_init();
    
    // Initialize FT6336G touch screen
    ft6336g_init();
    xTaskCreate(touch_task, "touch_task", 2048, NULL, 5, NULL);

    lv_init();

    esp_timer_handle_t timer;
    const esp_timer_create_args_t tick_args = {
        .callback = lv_tick_cb,
        .name = "lv_tick"
    };

    esp_timer_create(&tick_args, &timer);
    esp_timer_start_periodic(timer, 1000);

    static lv_disp_draw_buf_t draw_buf;
    static lv_color_t buf1[ILI9341_WIDTH * 20];
    static lv_color_t buf2[ILI9341_WIDTH * 20];

    lv_disp_draw_buf_init(&draw_buf,
                          buf1,
                          buf2,
                          ILI9341_WIDTH * 20);

    static lv_disp_drv_t disp_drv;
    lv_disp_drv_init(&disp_drv);

    disp_drv.hor_res = ILI9341_WIDTH;
    disp_drv.ver_res = ILI9341_HEIGHT;
    disp_drv.flush_cb = ili9341_flush_cb;
    disp_drv.draw_buf = &draw_buf;

    lv_disp_drv_register(&disp_drv);

    create_ui();

    xTaskCreate(clock_task, "clock_task", 4096, NULL, 5, NULL);

    while (1)
    {
        lv_timer_handler();
        vTaskDelay(pdMS_TO_TICKS(5));
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

嵌入式-老费

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值