esp32开发与应用(3.5寸屏幕+触摸驱动)

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

实际开发的时候,我们发现节奏还是要尽量慢一点。一定要按照屏幕驱动、触摸驱动、lvgl、触摸标定、触摸进入lvgl,不能想着一步梭哈,就能得到最终的结果。不然,往往是错误套着错误,很容易把人搞得非常疲惫。有必要一点一点修改代码,稳扎稳打、步步为营,这样用ai写代码比较稳妥一点。

1、添加触摸驱动

驱动芯片还是之前说过的xpt2046。本身是电阻屏,虽然消费领域用的一般,但是工业系统用得很多。比如说,电阻屏用手套按下去的时候,也会有效果,这是电容屏不具备的。

2、总线

总线部分还是spi,用到的是miso,但一般mosi也连接上。另外除了spi四根线之外,还有一个irq连线,可以用来做中断。

3、两个spi

电阻触摸屏,一般需要两个spi,这有点吃亏。毕竟其他ic一般都是用iic来记录 ,这样至少还可以用spi来做tf卡、或者是以太网连接。使用的时候,需要注意下。

4、ai编写代码

编写代码的时候,不要从0到1编写代码,要在上一次3.5寸屏幕ok的基础之上修改,这样成功的概率高很多。这也算是一个小的trick。

复制代码
#include <stdio.h>
#include <string.h>

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/spi_master.h"
#include "driver/gpio.h"
#include "esp_log.h"

// ================= CONFIG =================
#define LCD_W 480
#define LCD_H 320

#define PIN_MOSI 13
#define PIN_CLK  14
#define PIN_CS   15
#define PIN_DC   2
#define PIN_RST  4
#define PIN_BL   12

// ================= TOUCH (XPT2046) =================
#define TP_MOSI  23
#define TP_MISO  19
#define TP_CLK   18
#define TP_CS    5
#define TP_IRQ   27

static spi_device_handle_t spi_lcd;
static spi_device_handle_t spi_tp;

static const char *TAG = "ILI9488_AUTO";

// ================= LCD MODE =================
typedef enum {
    MODE_RGB565 = 0,
    MODE_RGB666 = 1,
} lcd_mode_t;

static lcd_mode_t lcd_mode = MODE_RGB565;

// ================= GPIO =================
static inline void dc_cmd(void)  { gpio_set_level(PIN_DC, 0); }
static inline void dc_data(void) { gpio_set_level(PIN_DC, 1); }

static void lcd_reset(void)
{
    gpio_set_level(PIN_RST, 0);
    vTaskDelay(pdMS_TO_TICKS(100));
    gpio_set_level(PIN_RST, 1);
    vTaskDelay(pdMS_TO_TICKS(150));
}

// ================= SPI LCD =================
static void lcd_cmd(uint8_t cmd)
{
    spi_transaction_t t = {
        .length = 8,
        .tx_buffer = &cmd,
    };
    dc_cmd();
    spi_device_transmit(spi_lcd, &t);
}

static void lcd_data(const void *data, int len)
{
    spi_transaction_t t = {
        .length = len * 8,
        .tx_buffer = data,
    };
    dc_data();
    spi_device_transmit(spi_lcd, &t);
}

// ================= INIT =================
static void ili9488_init(lcd_mode_t mode)
{
    lcd_reset();

    lcd_cmd(0x01);
    vTaskDelay(pdMS_TO_TICKS(120));

    lcd_cmd(0x11);
    vTaskDelay(pdMS_TO_TICKS(150));

    lcd_cmd(0x3A);
    uint8_t pix = (mode == MODE_RGB565) ? 0x55 : 0x66;
    lcd_data(&pix, 1);

    lcd_cmd(0x36);
    uint8_t mad = 0x28;
    lcd_data(&mad, 1);

    lcd_cmd(0x29);
    vTaskDelay(pdMS_TO_TICKS(50));

    lcd_mode = mode;

    ESP_LOGI(TAG, "LCD init done");
}

// ================= TOUCH (XPT2046) =================
static uint16_t tp_read_axis(uint8_t cmd)
{
    uint8_t tx[3] = {cmd, 0x00, 0x00};
    uint8_t rx[3] = {0};

    spi_transaction_t t = {
        .length = 24,
        .tx_buffer = tx,
        .rx_buffer = rx,
    };

    spi_device_transmit(spi_tp, &t);

    int val = ((rx[1] << 8) | rx[2]) >> 3; // 12bit
    return val & 0x0FFF;
}

static void touch_task(void *arg)
{
    while (1)
    {
        if (gpio_get_level(TP_IRQ) == 0)   // pressed
        {
            uint16_t x = tp_read_axis(0xD0);
            uint16_t y = tp_read_axis(0x90);

            ESP_LOGI("TOUCH", "PRESSED -> X=%d Y=%d", x, y);

            vTaskDelay(pdMS_TO_TICKS(100));
        }
        vTaskDelay(pdMS_TO_TICKS(20));
    }
}


// ================= WINDOW =================
static void set_window(int x1,int y1,int x2,int y2)
{
    uint8_t d[4];
 
    lcd_cmd(0x2A);  // Column address set
    d[0]=x1>>8; d[1]=x1;
    d[2]=x2>>8; d[3]=x2;
    lcd_data(d,4);
 
    lcd_cmd(0x2B);  // Page address set
    d[0]=y1>>8; d[1]=y1;
    d[2]=y2>>8; d[3]=y2;
    lcd_data(d,4);
 
    lcd_cmd(0x2C);  // Memory write
}

// ================= AUTO SAFE FILL =================
#define BUF_PIXELS 1024
 
static uint8_t buf[BUF_PIXELS * 3]; // Maximum size prepared for RGB666

static void fill_color(uint16_t color)
{
    set_window(0,0,LCD_W-1,LCD_H-1);
 
    uint8_t r = (color >> 11) & 0x1F;
    uint8_t g = (color >> 5)  & 0x3F;
    uint8_t b = color & 0x1F;
 
    r <<= 3;
    g <<= 2;
    b <<= 3;
 
    for (int i=0;i<BUF_PIXELS;i++) {
        buf[i*3+0]=r;
        buf[i*3+1]=g;
        buf[i*3+2]=b;
    }
 
    spi_transaction_t t = {
        .length = BUF_PIXELS * 24,
        .tx_buffer = buf,
    };
 
    int blocks = (LCD_W*LCD_H)/BUF_PIXELS;
    for (int i=0;i<blocks;i++) {
        dc_data();
        spi_device_transmit(spi_lcd,&t);
    }
}

// ================= BACKLIGHT =================
static void backlight_init(void)
{
    gpio_config_t io={
        .pin_bit_mask=1ULL<<PIN_BL,
        .mode=GPIO_MODE_OUTPUT
    };
    gpio_config(&io);
    gpio_set_level(PIN_BL,1);
}

// ================= MAIN =================
void app_main(void)
{
    gpio_set_direction(PIN_DC,GPIO_MODE_OUTPUT);
    gpio_set_direction(PIN_RST,GPIO_MODE_OUTPUT);

    // ================= LCD SPI =================
    spi_bus_config_t bus_lcd={
        .mosi_io_num=PIN_MOSI,
        .miso_io_num=-1,
        .sclk_io_num=PIN_CLK,
        .max_transfer_sz=1024*3
    };

    spi_bus_initialize(SPI2_HOST,&bus_lcd,SPI_DMA_CH_AUTO);

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

    spi_bus_add_device(SPI2_HOST,&dev_lcd,&spi_lcd);

    // ================= TOUCH SPI =================
    spi_bus_config_t bus_tp={
        .mosi_io_num=TP_MOSI,
        .miso_io_num=TP_MISO,
        .sclk_io_num=TP_CLK,
        .max_transfer_sz=32
    };

    spi_bus_initialize(SPI3_HOST,&bus_tp,SPI_DMA_CH_AUTO);

    spi_device_interface_config_t dev_tp={
        .clock_speed_hz=2*1000*1000,
        .mode=0,
        .spics_io_num=TP_CS,
        .queue_size=3,
    };

    spi_bus_add_device(SPI3_HOST,&dev_tp,&spi_tp);

    gpio_set_direction(TP_IRQ, GPIO_MODE_INPUT);

    // ================= INIT LCD =================
    ili9488_init(MODE_RGB666);
    fill_color(0x07e0); // green
    backlight_init(); // backlight on

    // ================= TOUCH TASK =================
    xTaskCreate(touch_task, "touch_task", 4096, NULL, 5, NULL);

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

5、测试和验证

测试的时候,主要就是看两点。第一,看看屏幕的颜色有没有发生变化,比如说变成绿色。第二,就是esp-idf切换成monitor的形式,这个时候就可以看到打印信息。手动按下去的时候,看看有没有打印信息出来,正常是没有打印的,有打印就代表触摸屏起作用了。