FaceMoo项目:基于ESP32S3开发板和RGB屏幕实现智能可触屏显示屏

目录

一、制作ESP32S3转接板

1.原理图设计

2. PCB制版布线铺铜

3.输出文件与下单

二、安装VMware ubuntu搭建ESP32S3开发环境

1.安装VMware

 2.创建虚拟机

3.安装ESP-IDF

4.在vscode上搭建ESP-IDF

三、Ubuntu上ESP32 MicroPython

1.ESP32 MicroPython固件下载

 2.使用esptool刷入固件

四、测试

五、移植LVGL

1.下载LVGL

 2.创建屏幕文件及CMake设置


选取器件:ESP32S3+RGB接口 4寸 480*480屏幕

一、制作ESP32S3转接板

1.原理图设计

根据480*480屏幕手册设置屏幕的40个引脚

480*480屏幕的属性 

设计原理图

2. PCB制版布线铺铜

3.输出文件与下单

检查无误后,点击文件(File)-制造输出里的gerber files 

会得到如图所示的对话框:
首先在通用选择,单位选择英寸即可,格式可以选择最高精度2:5 

然后选择层,点击左下角绘制层(P),然后选择为(选择使用的),勾选旁边的包括未连接的中间层焊盘,右半边的机械层全部不选。

 点击钻孔图层,这一栏的所有项都不勾选。

 

点击光圈嵌入的孔径(RS274X)需要勾选 

 

点击高级

设置完成,点击右下角确定。文件会自动生成,等待生成结束。 

 

 点击文件-制造输出-gerber files通用一栏保持不变,点击,点击绘制层,选择全部去掉,不勾选未连接的中间层焊盘,右侧机械层选择Mechanical 1

点击钻孔图层,选择输出所有的钻孔对,和输出所有的钻孔。

光圈高级保持和第一次相同。

点击确认后,会自动出现预览文件,直接关闭,不需要保存,这一不是用来定位PCB班上孔的位置。 

钻孔文件的输出:
点击
文件
-制造输出-NC Drill Files

单位和格式的选择和Gerber文件一样,前导和尾数零选择第二项摈弃前导零,坐标位置选择第二项参考相对原点,设置完成后,点击确定 

点击确认后会一次弹出两个确认参数设置对话框,点击确认即可。

最后,文件夹中生成的文件如下: 

把该文件夹压缩上传到嘉立创下单助手即可。 

二、安装VMware ubuntu搭建ESP32S3开发环境

1.安装VMware

可以去vmware官网下载:vmware官网跳转

安装VMware比较简单,在此不赘述。

需要注意:

到下面图片中的这一个步骤,可以点击许可证,输入密钥就可以使用了,

密钥可以去某度或者其他地方搜索一个拿来用就好

如果直接点击完成的话,可以有试用的时间,也可以使用

 2.创建虚拟机

VMware安装完成后如下图

点击创建新的虚拟机

 这里就按照推荐下一步就行

安装映像: 

这里的路径,需要选择一个虚拟机的光盘映像文件,

我这里选择的是ubuntu的,可以去ubuntu下载:官网跳转链接

之后的安装就按照自己需求安装就可以了

3.安装ESP-IDF

参考文档:Standard Toolchain Setup for Linux and macOS - ESP32 - — ESP-IDF Programming Guide latest documentation

在ubuntu上安装需要的工具链

在Terminal终端输入命令:

sudo apt-get install git wget flex bison gperf python3 python3-pip python3-venv cmake ninja-build ccache libffi-dev libssl-dev dfu-util libusb-1.0-0

 查看python版本号:

python3 --version

下载ESP-IDF:

mkdir -p ~/esp
cd ~/esp
git clone --recursive https://github.com/espressif/esp-idf.git

安装ESP-IDF:

cd esp-idf/
./install.sh all  #安装所有版本
. ./export.sh  

ESP-IDF已经安装成功

但是当你再一次打开Terminal终端时,输出idf.py命令时会出现

 找不到此命令,此时我们需要添加一个环境变量

echo $SHELL  #查看我们运行的是什么SHELL

 然后我们输入

nano ~/.bashrc

在打开的bashrc文件中任意一行添加

alias get_idf='. $HOME/esp/esp-idf/export.sh'

打开Terminal终端,输入get_idf命令,即可正常运行

编译并上传hello_world到ESP32S3

cd ~/esp

cp -r $IDF_PATH/examples/get-started/hello_world .

cd hello_world/ #进入hello_world文件夹

idf.py set-target ESP32S3  #设置开发板型号



配置flash大小,输入命令

idf.py menuconfig  #对ESP32进行配置

 选择flasher config

 选择flash size

改成4MB即可。

接下来就是编译和烧入

idf.py build  #编译



#新开终端

cd /dev

ls ttyUSB*  #查看ESP端口号  如果不显示端口号,需要安装对应ESP版本的串口驱动


sudo chmod a+rw /dev/端口号  #设置串口读写权限 注意一定要给权限 不然下面烧入不成功

idf.py flash monitoe -p /dev/端口号 #烧入开发板


以上就是ESP-IDF的安装。

4.在vscode上搭建ESP-IDF

 首先我们需要安装一个python 后面需要用到。

  python下载官网:python官网下载

 下载完成后是个安装包

  利用以下代码解压:

tar -zxvf xxxx  #xxxx代表你的安装包名称

   解压后需要安装一个包:

sudo apt install zlib1g-dev

   编译python:

mkdir ~/python #创建一个文件夹,放入编译好的文件

./configure --prefix=xxxxx  #xxxxx代表你建的文件夹路径

make   #make编译

make install  #安装

 安装完python后,要记住python的路径,下面配置ESP-IDF需要用到。

 vscode官网下载:vscode官网下载

 注意下载对应系统的版本

下载完成后打开vscode

 在扩展中安装这两个扩展

安装完成后

进入ESP-IDF:Configure ESP-IDF extension 配置

 

 在选择python环境时不能选择前两个,前两个没有权限,此时我们选择第三个,然后把上面安装好的python路径粘贴下面加上/python3即可。 

点击install 安装即可。

出现此图片代表安装成功。

三、Ubuntu上ESP32 MicroPython

1.ESP32 MicroPython固件下载

下载地址:MicroPython - Python for microcontrollers

 进入下载地址后,选择相应型号的开发板

下载 .bin文件,下载完成后就可以开始配置了。

获取PIP

在终端中,安装版本型号输入下列命令。

sudo pacman -S python3-pip  #Arch Linux版本

sudo apt-get install python3-pip  #Ubuntu

获取esptool

我们需要使用Python的包管理器pip来获取esptool,在命令行窗口中输入如下命令进行安装:

sudo pip3 install esptool

 2.使用esptool刷入固件

在此之前,我们需要知道插入到电脑上的ESP32设备,在Linux中的端口号是多少。

Linux通常以ttyUSB+数字编号的方式为这些设备命名。可以使用如下的命令来查看:

ls -l /dev/ttyUSB* 

 擦除flash

为了保证固件刷入的成功率,我们先要对ESP32的flash进行清除。代码如下:

sudo esptool.py --port <你的端口号> erase_flash

使用esptool刷入固件

找到第一步下载的ESP32 MicroPython固件的路径,开始使用esptool刷入固件,命令如下:

sudo esptool.py --chip esp32 --port <你的端口号> write_flash -z  0x1000 <你的固件的完整路径> 

至此,Ubuntu下 ESP32 MicroPython固件就烧入结束了。

3.安装MicroPython库文件

git clone -b v1.19.1 --single-branch https://github.com/micropython/micropython.git

四、测试

git完MicroPython库文件后,在micropython-ports文件夹中,复制一个esp32文件夹,重命名为自己的项目名称,我这里取名为facemoo。

在我们复制的文件夹中,新建一个partitions-32MiB.csv文件,里面存放下面代码:

# Notes: the offset of the partition table itself is set in
# $IDF_PATH/components/partition_table/Kconfig.projbuild.
# Name,   Type, SubType, Offset,  Size, Flags
nvs,      data, nvs,     0x9000,  0x6000,
phy_init, data, phy,     0xf000,  0x1000,
factory,  app,  factory, 0x10000, 0x1F0000,
vfs,      data, fat,     0x200000, 0x1E00000,

修改mpconfigport.h文件,修改如下:

接下来就像就可以进行测试了。

首先进入esp-idf文件夹下,在终端输入:

. ./export.sh

打开esp-idf运行环境,进入到facemoo文件夹下,打开终端输入:

idf.py clean #进行清楚

make submodules #如果clean 失败,输入此行代码 之后再次运行idf.py clean

idf.py set-target esp32s3 #设置开发板型号

idf.py menuconfig #打开配置界面进行配置

 配置界面如下,首先进入Serial flasher config,进行如下配置。

 

 配置完后,返回上一界面,配置Partition Table,如下图所示:

 

 配置完后,进行Component config配置,如下:

 

 

 配置完成即可退出,记得最后要保存。

idf.py build    #编译 

sudo chmod 777 /dev/ttyUSB0   #设置串口权限

idf.py flash monitor -p /dev/ttyUSB0  #烧入和监听

 烧入完成后,打开Thonny,会显示如下:

五、移植LVGL

1.下载LVGL

在项目工程中的main文件夹下创建一个名为”idf_component.yml“的文件,在该文件中输入代码:

dependencies:
  idf : ">=4.4"
  lvgl/lvgl : "~8.2.0"

注意:代码的格式和空格千万不能出错! 

该代码的作用是在编译的过程中,下载LVGL,创建完此文件后编译会出现managed_components文件夹

 2.创建屏幕文件及CMake设置

在项目工程目录下创建”driver“文件夹,该文件夹下存放两个屏幕文件:

分别为屏幕的.c文件和.h文件。

.c文件代码如下:

#include "rgb_lcd_480x480.h"
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "esp_timer.h"
#include "esp_lcd_panel_ops.h"
#include "esp_lcd_panel_rgb.h"
#include "driver/gpio.h"
#include "esp_err.h"
#include "esp_log.h"
// #include "managed_components/lvgl__lvgl/src/lvgl.h"
#include "lvgl.h"
#include <driver/i2c.h>
// #include "managed_components/lvgl__lvgl/demos/lv_demos.h"
#include "demos/lv_demos.h"
static const char *TAG = "example";

#define RGB_LCD_PIN_NUM_BK_LIGHT 1
#define RGB_LCD_PIN_NUM_HSYNC 48
#define RGB_LCD_PIN_NUM_VSYNC 45
#define RGB_LCD_PIN_NUM_DE 38
#define RGB_LCD_PIN_NUM_PCLK 2

#define RGB_LCD_PIN_NUM_DATA0 47 // B0
#define RGB_LCD_PIN_NUM_DATA1 21 // B1
#define RGB_LCD_PIN_NUM_DATA2 14 // B2
#define RGB_LCD_PIN_NUM_DATA3 13 // B3
#define RGB_LCD_PIN_NUM_DATA4 12 // B4

#define RGB_LCD_PIN_NUM_DATA5 11  // G0
#define RGB_LCD_PIN_NUM_DATA6 10  // G1
#define RGB_LCD_PIN_NUM_DATA7 9   // G2
#define RGB_LCD_PIN_NUM_DATA8 46  // G3
#define RGB_LCD_PIN_NUM_DATA9 20  // G4
#define RGB_LCD_PIN_NUM_DATA10 19 // G5

#define RGB_LCD_PIN_NUM_DATA11 8  // R0
#define RGB_LCD_PIN_NUM_DATA12 18 // R1
#define RGB_LCD_PIN_NUM_DATA13 17 // R2
#define RGB_LCD_PIN_NUM_DATA14 16 // R3
#define RGB_LCD_PIN_NUM_DATA15 15 // R4
#define RGB_LCD_PIN_NUM_DISP_EN -1
// #define RGB_LCD_PIN_NUM_DISP_EN        39

// The pixel number in horizontal and vertical
#define RGB_LCD_LCD_H_RES 480
#define RGB_LCD_LCD_V_RES 480

#define RGB_LCD_LVGL_TICK_PERIOD_MS 2


#define TOUCH_FT5624_I2C_SDA 6
#define TOUCH_FT5624_I2C_SCL 5
#define TOUCH_FT5624_I2C_INT 7
#define TOUCH_FT5624_I2C_RST 4
#define TOUCH_FT5624_I2C_FREQ_HZ 400000

#define LV_I2C_TOUCH_PORT 0

#define FT5426_I2C_SLAVE_ADDR 0X38 // 设备地址

// I2C读写命令
#define FT_CMD_WR 0X70 // 写命令
#define FT_CMD_RD 0X71 // 读命令

// FT5206 部分寄存器定义
#define FT_DEVIDE_MODE 0x00    // FT5206模式控制寄存器
#define FT_REG_NUM_FINGER 0x02 // 触摸状态寄存器

#define FT_TP1_REG 0X03 // 第一个触摸点数据地址
#define FT_TP2_REG 0X09 // 第二个触摸点数据地址
#define FT_TP3_REG 0X0F // 第三个触摸点数据地址
#define FT_TP4_REG 0X15 // 第四个触摸点数据地址
#define FT_TP5_REG 0X1B // 第五个触摸点数据地址

#define FT_ID_G_LIB_VERSION 0xA1  // 版本
#define FT_ID_G_MODE 0xA4         // FT5206中断模式控制寄存器
#define FT_ID_G_THGROUP 0x80      // 触摸有效值设置寄存器
#define FT_ID_G_PERIODACTIVE 0x88 // 激活状态周期设置寄存器

#define I2C_ADDR_10 (1 << 15)
#define I2C_REG_16 (1 << 31)
#define I2C_NO_REG (1 << 30)
static const uint8_t ACK_CHECK_EN = 1;

typedef struct
{
    bool inited;
    uint8_t i2c_dev_addr;
} ft5426_status_t;

ft5426_status_t ft5426;

static SemaphoreHandle_t I2C_0_mutex = NULL;


esp_err_t I2C_init();
esp_err_t I2C_read(uint16_t addr, uint32_t reg, uint8_t *buffer, uint16_t size);
esp_err_t I2C_write(uint16_t addr, uint32_t reg, const uint8_t *buffer, uint16_t size);
esp_err_t I2C_lock();
// static esp_err_t I2C_close();
esp_err_t I2C_lock();
esp_err_t I2C_unlock();
esp_err_t I2C_force_unlock();

void i2c_send_address(i2c_cmd_handle_t cmd, uint16_t addr, i2c_rw_t rw)
{
    if (addr & I2C_ADDR_10)
    {
        i2c_master_write_byte(cmd, 0xF0 | ((addr & 0x3FF) >> 7) | rw, ACK_CHECK_EN);
        i2c_master_write_byte(cmd, addr & 0xFF, ACK_CHECK_EN);
    }
    else
    {
        i2c_master_write_byte(cmd, (addr << 1) | rw, ACK_CHECK_EN);
    }
}

void i2c_send_register(i2c_cmd_handle_t cmd, uint32_t reg)
{
    if (reg & I2C_REG_16)
    {
        i2c_master_write_byte(cmd, (reg & 0xFF00) >> 8, ACK_CHECK_EN);
    }
    i2c_master_write_byte(cmd, reg & 0xFF, ACK_CHECK_EN);
}

/**
 * @brief I2C设备初始化
 *
 * @param port
 * @return esp_err_t
 */
esp_err_t I2C_init()
{
    esp_err_t ret = ESP_OK;
    if (I2C_0_mutex == 0)
    {

        ESP_LOGI(TAG, "Starting I2C master at port %d.", (int)LV_I2C_TOUCH_PORT);

        I2C_0_mutex = xSemaphoreCreateMutex();

        i2c_config_t conf = {0};
        conf.clk_flags = 0;
        conf.sda_io_num = TOUCH_FT5624_I2C_SDA;
        conf.scl_io_num = TOUCH_FT5624_I2C_SCL;
        conf.sda_pullup_en = GPIO_PULLUP_DISABLE;
        conf.scl_pullup_en = conf.sda_pullup_en;
        conf.master.clk_speed = TOUCH_FT5624_I2C_FREQ_HZ;
        conf.mode = I2C_MODE_MASTER;
        ret = i2c_param_config(LV_I2C_TOUCH_PORT, &conf);
        ret |= i2c_driver_install(LV_I2C_TOUCH_PORT, conf.mode, 0, 0, 0);
        if (ret != ESP_OK)
        {
            ESP_LOGE(TAG, "Failed to initialise I2C port %d.", (int)LV_I2C_TOUCH_PORT);
            ESP_LOGW(TAG, "If it was already open, we'll use it with whatever settings were used "
                          "to open it. See I2C Manager README for details.");
        }
        else
        {
            ESP_LOGI(TAG, "Initialised port %d (SDA: %d, SCL: %d, speed: %d Hz.)",
                     LV_I2C_TOUCH_PORT, conf.sda_io_num, conf.scl_io_num, conf.master.clk_speed);
        }
    }
    return ret;
}

esp_err_t I2C_read(uint16_t addr, uint32_t reg, uint8_t *buffer, uint16_t size)
{
    esp_err_t result;

    TickType_t timeout = 2;

    if (I2C_lock() == ESP_OK)
    {
        i2c_cmd_handle_t cmd = i2c_cmd_link_create();
        if (!(reg & I2C_NO_REG))
        {
            /* When reading specific register set the addr pointer first. */
            i2c_master_start(cmd);
            i2c_send_address(cmd, addr, I2C_MASTER_WRITE);
            i2c_send_register(cmd, reg);
        }
        /* Read size bytes from the current pointer. */
        i2c_master_start(cmd);
        i2c_send_address(cmd, addr, I2C_MASTER_READ);
        i2c_master_read(cmd, buffer, size, I2C_MASTER_LAST_NACK);
        // printf("i2c_master_start: %d, size:%d, buffer: %X, %X\r\n",result, size, buffer[0], buffer[1]);
        i2c_master_stop(cmd);
        result = i2c_master_cmd_begin(LV_I2C_TOUCH_PORT, cmd, timeout);
        i2c_cmd_link_delete(cmd);
        I2C_unlock();
    }
    else
    {
        ESP_LOGE(TAG, "Lock could not be obtained for port %d.", (int)LV_I2C_TOUCH_PORT);
        return ESP_ERR_TIMEOUT;
    }

    if (result != ESP_OK)
    {
        ESP_LOGW(TAG, "Touch Error: %d", result);
    }

    ESP_LOG_BUFFER_HEX_LEVEL(TAG, buffer, size, ESP_LOG_VERBOSE);

    return result;
}

esp_err_t I2C_write(uint16_t addr, uint32_t reg, const uint8_t *buffer, uint16_t size)
{

    esp_err_t result;

    ESP_LOGV(TAG, "Writing port %d, addr 0x%03x, reg 0x%04x", LV_I2C_TOUCH_PORT, addr, reg);

    TickType_t timeout = 20;

    if (I2C_lock() == ESP_OK)
    {
        i2c_cmd_handle_t cmd = i2c_cmd_link_create();
        i2c_master_start(cmd);
        i2c_send_address(cmd, addr, I2C_MASTER_WRITE);
        if (!(reg & I2C_NO_REG))
        {
            i2c_send_register(cmd, reg);
        }
        i2c_master_write(cmd, (uint8_t *)buffer, size, ACK_CHECK_EN);
        i2c_master_stop(cmd);
        result = i2c_master_cmd_begin(LV_I2C_TOUCH_PORT, cmd, timeout);
        i2c_cmd_link_delete(cmd);
        I2C_unlock();
    }
    else
    {
        ESP_LOGE(TAG, "Lock could not be obtained for port %d.", (int)LV_I2C_TOUCH_PORT);
        return ESP_ERR_TIMEOUT;
    }

    if (result != ESP_OK)
    {
        ESP_LOGW(TAG, "Error: %d", result);
    }

    ESP_LOG_BUFFER_HEX_LEVEL(TAG, buffer, size, ESP_LOG_VERBOSE);

    return result;
}

// static esp_err_t I2C_close() {
//     vSemaphoreDelete(I2C_0_mutex);
//     I2C_0_mutex= NULL;
//     ESP_LOGI(TAG, "Closing I2C master at port 0.");
//     return i2c_driver_delete(LV_I2C_TOUCH_PORT);
// }

esp_err_t I2C_lock()
{
    ESP_LOGV(TAG, "Mutex lock set for 0.");
    TickType_t timeout = 2; // 原设置是20/10
    if (xSemaphoreTake(I2C_0_mutex, timeout) == pdTRUE)
    {
        return ESP_OK;
    }
    else
    {
        ESP_LOGE(TAG, "Removing stale mutex lock from port 0.");
        I2C_force_unlock();
        return (xSemaphoreTake(I2C_0_mutex, timeout) == pdTRUE ? ESP_OK : ESP_FAIL);
    }
}

esp_err_t I2C_unlock()
{
    ESP_LOGV(TAG, "Mutex lock removed for 0.");
    return (xSemaphoreGive(I2C_0_mutex) == pdTRUE) ? ESP_OK : ESP_FAIL;
}

esp_err_t I2C_force_unlock()
{
    if (I2C_0_mutex)
    {
        vSemaphoreDelete(I2C_0_mutex);
    }
    I2C_0_mutex = xSemaphoreCreateMutex();
    return ESP_OK;
}

/**
 * @brief I2C读数据
 *
 * @param register_addr
 * @param data_buf
 * @param len
 * @return esp_err_t
 */
esp_err_t ft5462_i2c_read(uint16_t register_addr, uint8_t *data_buf, uint8_t len)
{
    return I2C_read(ft5426.i2c_dev_addr, register_addr, data_buf, len);
}

/**
 * @brief I2C写数据
 *
 * @param register_addr
 * @param data_buf
 * @param len
 * @return esp_err_t
 */
esp_err_t ft5462_i2c_write(uint16_t register_addr, uint8_t *data_buf, uint8_t len)
{
    return I2C_write(ft5426.i2c_dev_addr, register_addr, data_buf, len);
}
/**
 * @brief 触摸扫描
 *
 * @param drv
 * @param data
 * @return true
 * @return false
 */
static void touch_driver_read(lv_indev_drv_t *drv, lv_indev_data_t *data)
{
    // printf("touch_driver_read\r\n");
    uint8_t temp[4];
    ft5462_i2c_read(FT_REG_NUM_FINGER, temp, 1);
    if (temp[0] != 1)
    {
        // 如果触点不是1个,或者没有按下的时候
        data->point.x = 0;
        data->point.y = 0;
        data->state = LV_INDEV_STATE_REL;
        return;
    }

    // 如果只有一个触点的时候
    ft5462_i2c_read(FT_TP1_REG, temp, 4);
    data->point.y = ((uint16_t)(temp[0] & 0X0F) << 8) + temp[1];
    data->point.x = ((uint16_t)(temp[2] & 0X0F) << 8) + temp[3];
    data->state = LV_INDEV_STATE_PR;
    data->continue_reading = false;
    return;
}

// static void ft5462_i2c_read_touch_point(uint16_t * x, uint16_t * y)
// {
//     uint8_t temp[4];
//     ft5462_i2c_read(FT_REG_NUM_FINGER, temp, 1);
//     printf("FT_REG_NUM_FINGER: %d \r\n", temp[0]);
//     ft5462_i2c_read(FT_TP1_REG, temp, 4);
//     *y = ((uint16_t)(temp[0]&0X0F)<<8) + temp[1];
//     *x = ((uint16_t)(temp[2]&0X0F)<<8) + temp[3];
// }

/**
 * @brief 初始化FT5426
 *
 * @param dev_addr
 */
void ft5426_init(uint8_t dev_addr)
{
    if (!ft5426.inited)
    {
        I2C_init(dev_addr);
        ft5426.i2c_dev_addr = dev_addr;
        // 设置RES移交
        gpio_config_t gpio_configure = {
            .intr_type = GPIO_INTR_DISABLE,
            .mode = GPIO_MODE_OUTPUT,
            .pin_bit_mask = 1ULL << TOUCH_FT5624_I2C_RST,
            .pull_down_en = 0,
            .pull_up_en = 0};
        gpio_config(&gpio_configure);

        // 复位
        gpio_set_level(TOUCH_FT5624_I2C_RST, 0);
        vTaskDelay(20 / portTICK_PERIOD_MS);
        gpio_set_level(TOUCH_FT5624_I2C_RST, 1);
        vTaskDelay(50 / portTICK_PERIOD_MS);

        // 读取版本号
        uint8_t temp[5];
        memset(temp, 0, 5);
        ft5462_i2c_read(FT_ID_G_LIB_VERSION, temp, 2);

        // 进入正常操作模式
        ft5462_i2c_write(FT_DEVIDE_MODE, temp, 1);
        // 查询模式
        ft5462_i2c_write(FT_ID_G_MODE, temp, 1);
        // 设置触摸有效值
        temp[0] = 22;
        ft5462_i2c_write(FT_ID_G_THGROUP, temp, 1);
        // 设置激活周期
        temp[0] = 12;
        ft5462_i2c_write(FT_ID_G_PERIODACTIVE, temp, 1);
        ft5426.inited = true;

        // while (1)
        // {
        //     uint16_t x=0,y=0;
        //     ft5462_i2c_read_touch_point(&x, &y);
        //     printf("POINT: (%d, %d)\r\n", x, y);
        //     vTaskDelay(1000 / portTICK_PERIOD_MS);
        // }
    }
}
/**
 * @brief 触摸驱动初始化
 *
 */
void RGB_LCD_touch_init()
{
    ft5426_init(FT5426_I2C_SLAVE_ADDR);
    // 触摸驱动控制
    lv_indev_drv_t *touch_drv;
    touch_drv = pvPortMalloc(sizeof(lv_indev_drv_t));
    lv_indev_drv_init(touch_drv);
    touch_drv->type = LV_INDEV_TYPE_POINTER;
    touch_drv->read_cb = touch_driver_read;
    lv_indev_drv_register(touch_drv);
}



void example_lvgl_demo_ui(lv_obj_t *scr);

/**
 * @brief LVGL刷新回调函数
 * @param drv LVGL显示驱动对象
 * @param area 需要刷新的区域
 * @param color_map 颜色映射表
 */
static void rgb_lcd_lvgl_flush_cb(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map)
{
    esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t)drv->user_data;
    // 将缓冲区的内容复制到显示器的特定区域
    esp_lcd_panel_draw_bitmap(panel_handle, area->x1, area->y1, area->x2 + 1, area->y2 + 1, color_map);
    // 通知LVGL库,显示器已经刷新完毕
    lv_disp_flush_ready(drv);
}

/**
 * @brief LVGL延时回调函数
 */
static void rgb_lcd_increase_lvgl_tick(void *arg)
{
    // 通知LVGL库,经过了一段时间(2ms)
    lv_tick_inc(RGB_LCD_LVGL_TICK_PERIOD_MS);
}


void rgb_lcd_init(void){
    static lv_disp_draw_buf_t disp_buf; // 显示缓冲区对象,用于存储绘图数据
    static lv_disp_drv_t disp_drv;      // 显示配置结构体,其中包含绘图缓冲区对象和显示回调函数

    // 设置背光GPIO,这里应该通过PWM控制,或者通过三极管控制
    ESP_LOGI(TAG, "Turn off LCD backlight");
    gpio_config_t bk_gpio_config = {
        .mode = GPIO_MODE_OUTPUT,
        .pin_bit_mask = 1ULL << RGB_LCD_PIN_NUM_BK_LIGHT};
    ESP_ERROR_CHECK(gpio_config(&bk_gpio_config));

    // 安装RGB面板驱动
    ESP_LOGI(TAG, "Install RGB panel driver");
    esp_lcd_panel_handle_t panel_handle = NULL;
    esp_lcd_rgb_panel_config_t panel_config = {
        .data_width = 16,                         // 使用 RGB565 配色方案
        .psram_trans_align = 64,                  // PSRAM 传输对齐
        .clk_src = LCD_CLK_SRC_PLL240M,           // 设置时钟源
        .disp_gpio_num = RGB_LCD_PIN_NUM_DISP_EN, // 设置显示使能引脚
        .pclk_gpio_num = RGB_LCD_PIN_NUM_PCLK,    // 设置像素时钟引脚
        .vsync_gpio_num = RGB_LCD_PIN_NUM_VSYNC,  // 设置垂直同步引脚
        .hsync_gpio_num = RGB_LCD_PIN_NUM_HSYNC,  // 设置水平同步引脚
        .de_gpio_num = RGB_LCD_PIN_NUM_DE,        // 设置数据使能引脚
        .data_gpio_nums = {
            // 设置数据引脚
            RGB_LCD_PIN_NUM_DATA0,  // B0
            RGB_LCD_PIN_NUM_DATA1,  // B1
            RGB_LCD_PIN_NUM_DATA2,  // B2
            RGB_LCD_PIN_NUM_DATA3,  // B3
            RGB_LCD_PIN_NUM_DATA4,  // B4
            RGB_LCD_PIN_NUM_DATA5,  // G0
            RGB_LCD_PIN_NUM_DATA6,  // G1
            RGB_LCD_PIN_NUM_DATA7,  // G2
            RGB_LCD_PIN_NUM_DATA8,  // G3
            RGB_LCD_PIN_NUM_DATA9,  // G4
            RGB_LCD_PIN_NUM_DATA10, // G5
            RGB_LCD_PIN_NUM_DATA11, // R0
            RGB_LCD_PIN_NUM_DATA12, // R1
            RGB_LCD_PIN_NUM_DATA13, // R2
            RGB_LCD_PIN_NUM_DATA14, // R3
            RGB_LCD_PIN_NUM_DATA15, // R4
        },
        .timings = {
            // 设置时序参数
            .pclk_hz = 18*1000*1000,        // 设置像素时钟频率
            .h_res = RGB_LCD_LCD_H_RES, // 设置水平分辨率
            .v_res = RGB_LCD_LCD_V_RES, // 设置垂直分辨率
            .hsync_back_porch = 0,//43
            .hsync_front_porch = 8,
            .hsync_pulse_width = 2,
            .vsync_back_porch = 0, //15
            .vsync_front_porch = 12,//12
            .vsync_pulse_width = 10,//10
            .flags.pclk_active_neg = false,
        },
        .flags.fb_in_psram = 1, // allocate frame buffer in PSRAM
    };
    // 创建RGB面板,并将其句柄保存到 panel_handle 变量中,panel_handle用于传入显示回调函数中,在回调函数中进行显示操作
    ESP_ERROR_CHECK(esp_lcd_new_rgb_panel(&panel_config, &panel_handle));

    // 初始化RGB面板
    ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_handle));
    ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle));

    // 打开背光
    ESP_LOGI(TAG, "Turn on LCD backlight");
    gpio_set_level(RGB_LCD_PIN_NUM_BK_LIGHT, 1);

    // 初始化LVGL库
    ESP_LOGI(TAG, "Initialize LVGL library");
    lv_init();

    // 初始化LVGL显示驱动
    // 首先在 SPIRAM 中分配双缓冲区,然后将其传递给 lv_disp_draw_buf_init 函数,以初始化显示缓冲区对象。
    // 最后,将显示缓冲区对象传递给 lv_disp_drv_init 函数,以初始化显示驱动对象。
    static size_t BUF_SIZE = RGB_LCD_LCD_H_RES * RGB_LCD_LCD_V_RES * sizeof(lv_color_t);
    lv_color_t *buf1 = heap_caps_malloc(BUF_SIZE, MALLOC_CAP_SPIRAM);
    lv_color_t *buf2 = heap_caps_malloc(BUF_SIZE, MALLOC_CAP_SPIRAM);
    // lv_color_t *buf1 = heap_caps_malloc(RGB_LCD_LCD_H_RES * BUF_SIZE * sizeof(lv_color_t), MALLOC_CAP_DMA);
    // lv_color_t *buf2 = heap_caps_malloc(RGB_LCD_LCD_H_RES * BUF_SIZE * sizeof(lv_color_t), MALLOC_CAP_DMA);
    assert(buf1);
    assert(buf2);

    /* 初始化LVGL显示缓冲区
     * LVGL 使用双缓冲技术来实现平滑的屏幕刷新。它需要两个显示缓冲区来进行绘制和显示操作。当一个缓冲区用于显示时,另一个缓冲区可以同时进行绘制和更新操作,以提高渲染效率。
     * 具体来说,buf1 和 buf2 是两个指针变量,它们分别指向两个不同的显示缓冲区。每个缓冲区都是一块内存区域,用于存储屏幕上每个像素的颜色值或绘图元素的数据。
     * 在初始化显示缓冲区对象时,你需要为 buf1 和 buf2 提供相应的内存地址。这些内存地址应该是事先分配和配置好的,以便 LVGL 在运行时可以向这些缓冲区中写入绘制数据。
     * 在屏幕刷新时,LVGL 会根据需要在这两个缓冲区之间进行切换,使绘制和显示操作能够同时进行,并实现平滑的屏幕更新效果。
     * 需要注意的是,在使用 lv_disp_draw_buf_init 初始化显示缓冲区对象之前,你需要先确保已经正确分配和配置好 buf1 和 buf2 所需的内存空间。
     */
    lv_disp_draw_buf_init(&disp_buf, buf1, buf2, RGB_LCD_LCD_H_RES * RGB_LCD_LCD_V_RES);

    // uint8_t *img_buf = malloc(RGB_LCD_LCD_H_RES * RGB_LCD_LCD_V_RES * sizeof(lv_color_t));
    // lv_disp_draw_buf_init(&disp_buf, img_buf, NULL, RGB_LCD_LCD_H_RES * RGB_LCD_LCD_V_RES);
    // ESP_LOGI(TAG, "buf1 @%p, buf2 @%p", buf1, buf2);
    // ESP_LOGI(TAG, "disp_buf @%p", disp_buf);

    // 初始化LVGL显示驱动
    ESP_LOGI(TAG, "Register display driver to LVGL");
    lv_disp_drv_init(&disp_drv);
    disp_drv.hor_res = RGB_LCD_LCD_H_RES;      // 设置水平分辨率
    disp_drv.ver_res = RGB_LCD_LCD_V_RES;      // 设置垂直分辨率
    disp_drv.flush_cb = rgb_lcd_lvgl_flush_cb; // 设置刷新回调函数
    disp_drv.draw_buf = &disp_buf;             // 设置显示缓冲区
    disp_drv.user_data = panel_handle;         // 设置用户数据,这里设置为RGB面板句柄,用于在回调函数中的显示操作
    /* lv_disp_drv_t disp_drv 结构体配置项如下:
     * hor_res          :水平分辨率
     * ver_res          :垂直分辨率
     * physical_hor_res :物理显示的水平分辨率
     * physical_ver_res :物理显示的垂直分辨率
     * offset_x         :水平偏移量
     * offset_y         :垂直偏移量
     * draw_buf         :用于绘制屏幕内容的缓冲区指针
     * direct_mode      :是否使用以屏幕大小为基础的缓冲区和绘制绝对坐标(默认 是)
     * full_refresh     :是否总是重新绘制整个屏幕(默认 是)
     * sw_rotate        :是否使用软件旋转(速度较慢)(默认 是)
     * antialiasing     :是否启用抗锯齿(默认 是)
     * rotated          :是否旋转显示屏90度(默认 1,将显示器旋转90度)
     * screen_transp    :如果屏幕没有固定的不透明背景,则处理透明屏幕(默认 是)
     * dpi              :显示屏的DPI(每英寸点数)(默认 10)
     * flush_cb         :将内部缓冲区的内容写入显示屏的回调函数
     * rounder_cb       :根据显示驱动程序的要求调整无效区域的回调函数
     * set_px_cb        :根据显示的特殊要求,在缓冲区中设置像素的回调函数
     * clear_cb         :清除缓冲区的回调函数
     * monitor_cb       :在刷新周期后调用,用于监控渲染和刷新时间以及刷新的像素数
     * wait_cb          :在lvgl等待操作完成时定期调用的回调函数
     * clean_dcache_cb  :在需要清除影响渲染的CPU缓存时调用的回调函数
     * drv_update_cb    : 当驱动程序参数更新时调用的回调函数
     * color_chroma_key :在 CHROMA_KEYED 图像中,该颜色将是透明的
     * draw_ctx、draw_ctx_init、draw_ctx_deinit、draw_ctx_size :绘制上下文相关的成员变量
     *
     * 可以尝试调整 full_refresh、antialiasing 和 sw_rotate 参数,观察屏幕刷新效果。
     * 可以尝试调整 dpi 参数,观察屏幕显示效果。
     */

    /* 注册显示驱动
     * lv_disp_drv_register 是 LittlevGL(LVGL)图形库中的一个函数,用于注册显示驱动器并将其与一个显示设备(屏幕)关联起来。
     * 在使用 LVGL 进行图形界面开发时,需要将图形库与底层的硬件显示设备进行连接和协调。lv_disp_drv_register 函数的作用就是创建一个显示驱动器,并将其与一个特定的显示设备进行关联。
     * LVGL 操作屏幕的时候,根据该参数进行操作,如果需要显示到屏幕上,将会回调 disp_drv.flush_cb 与硬件进行实际交互,届时会把 disp_drv.user_data  作为参数传入
     */
    lv_disp_t *disp = lv_disp_drv_register(&disp_drv);

    // 初始化LVGL触摸驱动(IIC)
    RGB_LCD_touch_init();

    ESP_LOGI(TAG, "Install LVGL tick timer");
    /* LVGL的Tick接口(使用esp_timer生成2ms周期性事件),每过2ms更新一次数据
     * 在每次调用 lv_tick_inc 函数时,LVGL 会根据传入的时间间隔(period),将内部时钟计数值增加相应的量。
     * 这样,LVGL 就可以根据自己的时钟计数来进行事件处理和界面更新,保证了图形界面的实时性和流畅性。
     */
    const esp_timer_create_args_t lvgl_tick_timer_args = {
        .callback = &rgb_lcd_increase_lvgl_tick,
        .name = "lvgl_tick"};
    esp_timer_handle_t lvgl_tick_timer = NULL;
    ESP_ERROR_CHECK(esp_timer_create(&lvgl_tick_timer_args, &lvgl_tick_timer));                     // 创建定时器
    ESP_ERROR_CHECK(esp_timer_start_periodic(lvgl_tick_timer, RGB_LCD_LVGL_TICK_PERIOD_MS * 1000)); // 启动定时器

    // 获取当前活动的屏幕(Screen)对象
    ESP_LOGI(TAG, "Display LVGL Scatter Chart...");
    // lv_obj_t *scr = lv_disp_get_scr_act(disp);
    // example_lvgl_demo_ui(lv_disp_get_scr_act(disp));
    // example_lvgl_demo_ui(lv_scr_act());
    // test();
    // lv_demo_music();
    // lv_demo_widgets();
    ESP_LOGI(TAG, "LV_USE_DEMO_WIDGETS %d", LV_USE_DEMO_WIDGETS);

    while (1)
    {
        // 提高LVGL的任务优先级和/或缩短处理程序周期可以提高性能
        vTaskDelay(pdMS_TO_TICKS(10));
        /* 在使用 LVGL 进行图形界面开发时,定时器可以用于实现定时刷新、动画效果、延时操作等功能。lv_timer_handler 函数的作用是处理定时器事件,并执行与定时器相关的操作。
         *
         * 通过调用 lv_timer_handler 函数,可以触发 LVGL 的定时器机制,使得注册的定时器回调函数得到调用。在这些定时器回调函数中,可以编写相应的逻辑代码,实现定时执行特定任务的功能。
         * 一般来说,lv_timer_handler 函数会被周期性地调用,以处理过期的定时器事件。在每次调用 lv_timer_handler 时,LVGL 会检查所有已注册的定时器,若有定时器超过设定的时间,则会触发相应的回调函数。
         * 可以通过提升任务优先级,或者修改上面的延时时间加速处理,但一般来讲 运行lv_timer_handler的任务的优先级应低于运行“lv_tick_inc”的任务`
         */
        lv_timer_handler();
    }
}


// static void btn_event_cb(lv_event_t * e)
// {
//     printf("click button : %d \r\n", (int)e->user_data);
// }

void example_lvgl_demo_ui(lv_obj_t *scr)
{
    // 添加一组按钮
    // for(int i=0; i<6; i++){
    //     lv_obj_t * btn = lv_btn_create(scr);
    //     lv_obj_set_pos(btn, 15+i*130, 15);
    //     lv_obj_set_size(btn, 120,120);
    //     lv_obj_add_event_cb(btn, btn_event_cb,LV_EVENT_CLICKED,(void *)i);
    //     lv_obj_t *lab =lv_label_create(btn);
    //     char name[6];
    //     sprintf(name, "BTN_%d", i);
    //     lv_label_set_text(lab, name);
    //     lv_obj_align(lab, LV_ALIGN_CENTER, 0, 0);
    // }

    // 添加obj
    lv_obj_t * obj = lv_obj_create(scr);
    lv_obj_set_pos(obj, 10,10);
    lv_obj_set_size(obj,120,120);
}

.h文件代码如下:

#pragma once

void rgb_lcd_init(void);

 设置完成后,开始进行CMake设置,注意是main文件夹下的CMakeLists.txt文件。

首先要定义要编译的文件,如下:

 将屏幕的源文件和头文件加入到编译队列。

 除此之外,还有一步:

 这样,我们的LVGL就移植完成了,之后根据项目和屏幕的需求,在进行更改就好了。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值