WhyCan Forum(哇酷开发者社区)

我们能随心所欲"有问题百度(谷歌)一下", 这得感谢为中文互联网持续输出优质内容的各位老铁们。 QQ: 516333132, 微信(wechat): whycan_cn (哇酷网/挖坑网/填坑网) service@whycan.cn

您尚未登录。

#1 2018-07-06 17:08:00

msr06rr
会员
所在地: 苏州
注册时间: 2018-01-11
累计积分: 163

移植LittlevGL到ESP32

乐鑫的IDF软件写的真的是单片机领域的linux级别,手上正好有一块ESP-WROVER-KIT开发板,自带一块320*240分辨率的TFT液晶屏,虽然ESP32不是为这类显示屏而生,但是人家主频性能好啊,DMA加速一下,跑个简单的界面no problem的,我又不用它来播放视频。
LittlevGL的官方其实已经移植了一个版本,但是在写法上没有充分利用好dma的优势,这里仿照着乐鑫的example写了一个chart demo
上图
2112295671.jpg
上代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

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

#define PIN_NUM_MISO 25
#define PIN_NUM_MOSI 23
#define PIN_NUM_CLK 19
#define PIN_NUM_CS 22

#define PIN_NUM_DC 21
#define PIN_NUM_RST 18
#define PIN_NUM_BCKL 5

typedef struct
{
    uint8_t cmd;
    uint8_t data[16];
    uint8_t databytes; //No of data in data; bit 7 = delay after set; 0xFF = end of cmds.
} lcd_init_cmd_t;

typedef enum
{
    LCD_TYPE_ILI = 1,
    LCD_TYPE_ST,
    LCD_TYPE_MAX,
} type_lcd_t;

static spi_device_handle_t spi;

DRAM_ATTR static const lcd_init_cmd_t st_init_cmds[] = {
    /* Memory Data Access Control, MX=MV=1, MY=ML=MH=0, RGB=0 */
    {0x36, {(1 << 5) | (1 << 6)}, 1},
    /* Interface Pixel Format, 16bits/pixel for RGB/MCU interface */
    {0x3A, {0x55}, 1},
    /* Porch Setting */
    {0xB2, {0x0c, 0x0c, 0x00, 0x33, 0x33}, 5},
    /* Gate Control, Vgh=13.65V, Vgl=-10.43V */
    {0xB7, {0x45}, 1},
    /* VCOM Setting, VCOM=1.175V */
    {0xBB, {0x2B}, 1},
    /* LCM Control, XOR: BGR, MX, MH */
    {0xC0, {0x2C}, 1},
    /* VDV and VRH Command Enable, enable=1 */
    {0xC2, {0x01, 0xff}, 2},
    /* VRH Set, Vap=4.4+... */
    {0xC3, {0x11}, 1},
    /* VDV Set, VDV=0 */
    {0xC4, {0x20}, 1},
    /* Frame Rate Control, 60Hz, inversion=0 */
    {0xC6, {0x0f}, 1},
    /* Power Control 1, AVDD=6.8V, AVCL=-4.8V, VDDS=2.3V */
    {0xD0, {0xA4, 0xA1}, 1},
    /* Positive Voltage Gamma Control */
    {0xE0, {0xD0, 0x00, 0x05, 0x0E, 0x15, 0x0D, 0x37, 0x43, 0x47, 0x09, 0x15, 0x12, 0x16, 0x19}, 14},
    /* Negative Voltage Gamma Control */
    {0xE1, {0xD0, 0x00, 0x05, 0x0D, 0x0C, 0x06, 0x2D, 0x44, 0x40, 0x0E, 0x1C, 0x18, 0x16, 0x19}, 14},
    /* Sleep Out */
    {0x11, {0}, 0x80},
    /* Display On */
    {0x29, {0}, 0x80},
    {0, {0}, 0xff}};

DRAM_ATTR static const lcd_init_cmd_t ili_init_cmds[] = {
    /* Power contorl B, power control = 0, DC_ENA = 1 */
    {0xCF, {0x00, 0x83, 0X30}, 3},
    /* Power on sequence control,
     * cp1 keeps 1 frame, 1st frame enable
     * vcl = 0, ddvdh=3, vgh=1, vgl=2
     * DDVDH_ENH=1
     */
    {0xED, {0x64, 0x03, 0X12, 0X81}, 4},
    /* Driver timing control A,
     * non-overlap=default +1
     * EQ=default - 1, CR=default
     * pre-charge=default - 1
     */
    {0xE8, {0x85, 0x01, 0x79}, 3},
    /* Power control A, Vcore=1.6V, DDVDH=5.6V */
    {0xCB, {0x39, 0x2C, 0x00, 0x34, 0x02}, 5},
    /* Pump ratio control, DDVDH=2xVCl */
    {0xF7, {0x20}, 1},
    /* Driver timing control, all=0 unit */
    {0xEA, {0x00, 0x00}, 2},
    /* Power control 1, GVDD=4.75V */
    {0xC0, {0x26}, 1},
    /* Power control 2, DDVDH=VCl*2, VGH=VCl*7, VGL=-VCl*3 */
    {0xC1, {0x11}, 1},
    /* VCOM control 1, VCOMH=4.025V, VCOML=-0.950V */
    {0xC5, {0x35, 0x3E}, 2},
    /* VCOM control 2, VCOMH=VMH-2, VCOML=VML-2 */
    {0xC7, {0xBE}, 1},
    /* Memory access contorl, MX=MY=0, MV=1, ML=0, BGR=1, MH=0 */
    {0x36, {0x28}, 1},
    /* Pixel format, 16bits/pixel for RGB/MCU interface */
    {0x3A, {0x55}, 1},
    /* Frame rate control, f=fosc, 70Hz fps */
    {0xB1, {0x00, 0x1B}, 2},
    /* Enable 3G, disabled */
    {0xF2, {0x08}, 1},
    /* Gamma set, curve 1 */
    {0x26, {0x01}, 1},
    /* Positive gamma correction */
    {0xE0, {0x1F, 0x1A, 0x18, 0x0A, 0x0F, 0x06, 0x45, 0X87, 0x32, 0x0A, 0x07, 0x02, 0x07, 0x05, 0x00}, 15},
    /* Negative gamma correction */
    {0XE1, {0x00, 0x25, 0x27, 0x05, 0x10, 0x09, 0x3A, 0x78, 0x4D, 0x05, 0x18, 0x0D, 0x38, 0x3A, 0x1F}, 15},
    /* Column address set, SC=0, EC=0xEF */
    {0x2A, {0x00, 0x00, 0x00, 0xEF}, 4},
    /* Page address set, SP=0, EP=0x013F */
    {0x2B, {0x00, 0x00, 0x01, 0x3f}, 4},
    /* Memory write */
    {0x2C, {0}, 0},
    /* Entry mode set, Low vol detect disabled, normal display */
    {0xB7, {0x07}, 1},
    /* Display function control */
    {0xB6, {0x0A, 0x82, 0x27, 0x00}, 4},
    /* Sleep out */
    {0x11, {0}, 0x80},
    /* Display on */
    {0x29, {0}, 0x80},
    {0, {0}, 0xff},
};

static void lcd_cmd(spi_device_handle_t spi, const uint8_t cmd)
{
    esp_err_t ret;
    spi_transaction_t t;
    memset(&t, 0, sizeof(t));           //Zero out the transaction
    t.length = 8;                       //Command is 8 bits
    t.tx_buffer = &cmd;                 //The data is the cmd itself
    t.user = (void *)0;                 //D/C needs to be set to 0
    ret = spi_device_transmit(spi, &t); //Transmit!
    assert(ret == ESP_OK);              //Should have had no issues.
}

static void lcd_data(spi_device_handle_t spi, const uint8_t *data, int len)
{
    esp_err_t ret;
    spi_transaction_t t;
    if (len == 0)
        return;                         //no need to send anything
    memset(&t, 0, sizeof(t));           //Zero out the transaction
    t.length = len * 8;                 //Len is in bytes, transaction length is in bits.
    t.tx_buffer = data;                 //Data
    t.user = (void *)1;                 //D/C needs to be set to 1
    ret = spi_device_transmit(spi, &t); //Transmit!
    assert(ret == ESP_OK);              //Should have had no issues.
}

static void lcd_spi_pre_transfer_callback(spi_transaction_t *t)
{
    int dc = (int)t->user;
    gpio_set_level(PIN_NUM_DC, dc);
}

static uint32_t lcd_get_id(spi_device_handle_t spi)
{
    //get_id cmd
    lcd_cmd(spi, 0x04);

    spi_transaction_t t;
    memset(&t, 0, sizeof(t));
    t.length = 8 * 3;
    t.flags = SPI_TRANS_USE_RXDATA;
    t.user = (void *)1;

    esp_err_t ret = spi_device_transmit(spi, &t);
    assert(ret == ESP_OK);

    return *(uint32_t *)t.rx_data;
}

static void my_disp_flush(int32_t x1, int32_t y1, int32_t x2, int32_t y2, const lv_color_t *color_map)
{
    esp_err_t ret;
    spi_transaction_t trans[6];
    for (int x = 0; x < 6; x++)
    {
        memset(&trans[x], 0, sizeof(spi_transaction_t));
        if ((x & 1) == 0)
        {
            //Even transfers are commands
            trans[x].length = 8;
            trans[x].user = (void *)0;
        }
        else
        {
            //Odd transfers are data
            trans[x].length = 8 * 4;
            trans[x].user = (void *)1;
        }
        trans[x].flags = SPI_TRANS_USE_TXDATA;
    }

    uint32_t size = (x2 - x1 + 1) * (y2 - y1 + 1);
    uint8_t *color_u8 = (uint8_t *)color_map;
    for (uint32_t i = 0; i < size * 2; i += 2)
    {
        color_u8[i] ^= color_u8[i + 1];
        color_u8[i + 1] ^= color_u8[i];
        color_u8[i] ^= color_u8[i + 1];
    }

    trans[0].tx_data[0] = 0x2A;             //Column Address Set
    trans[1].tx_data[0] = (x1 >> 8) & 0xFF; //Start Col High
    trans[1].tx_data[1] = x1 & 0xFF;        //Start Col Low
    trans[1].tx_data[2] = (x2 >> 8) & 0xFF; //End Col High
    trans[1].tx_data[3] = x2 & 0xFF;        //End Col Low
    trans[2].tx_data[0] = 0x2B;             //Page address set
    trans[3].tx_data[0] = (y1 >> 8) & 0xFF; //Start page high
    trans[3].tx_data[1] = y1 & 0xFF;        //start page low
    trans[3].tx_data[2] = (y2 >> 8) & 0xFF; //end page high
    trans[3].tx_data[3] = y2 & 0xFF;        //end page low
    trans[4].tx_data[0] = 0x2C;             //memory write
    trans[5].tx_buffer = color_map;
    trans[5].flags = 0;
    trans[5].length = size * 2 * 8;

    //Queue all transactions.
    for (int x = 0; x < 6; x++)
    {
        ret = spi_device_queue_trans(spi, &trans[x], portMAX_DELAY);
        assert(ret == ESP_OK);
    }
    spi_transaction_t *rtrans;
    for (int x = 0; x < 6; x++)
    {
        ret = spi_device_get_trans_result(spi, &rtrans, portMAX_DELAY);
        assert(ret == ESP_OK);
    }

    lv_flush_ready();
}

static void spi_spec_init()
{
    esp_err_t ret;
    spi_bus_config_t buscfg = {
        .miso_io_num = PIN_NUM_MISO,
        .mosi_io_num = PIN_NUM_MOSI,
        .sclk_io_num = PIN_NUM_CLK,
        .quadwp_io_num = -1,
        .quadhd_io_num = -1,
        .max_transfer_sz = 12800};
    spi_device_interface_config_t devcfg = {
        .clock_speed_hz = 26 * 1000 * 1000,
        .mode = 0,                               //SPI mode 0
        .spics_io_num = PIN_NUM_CS,              //CS pin
        .queue_size = 7,                         //We want to be able to queue 7 transactions at a time
        .pre_cb = lcd_spi_pre_transfer_callback, //Specify pre-transfer callback to handle D/C line
    };
    //Initialize the SPI bus
    ret = spi_bus_initialize(HSPI_HOST, &buscfg, 1);
    ESP_ERROR_CHECK(ret);
    //Attach the LCD to the SPI bus
    ret = spi_bus_add_device(HSPI_HOST, &devcfg, &spi);
    ESP_ERROR_CHECK(ret);
}

static void lcd_init(spi_device_handle_t spi)
{
    int cmd = 0;
    const lcd_init_cmd_t *lcd_init_cmds;

    //Initialize non-SPI GPIOs
    gpio_set_direction(PIN_NUM_DC, GPIO_MODE_OUTPUT);
    gpio_set_direction(PIN_NUM_RST, GPIO_MODE_OUTPUT);
    gpio_set_direction(PIN_NUM_BCKL, GPIO_MODE_OUTPUT);

    //Reset the display
    gpio_set_level(PIN_NUM_RST, 0);
    vTaskDelay(100 / portTICK_PERIOD_MS);
    gpio_set_level(PIN_NUM_RST, 1);
    vTaskDelay(100 / portTICK_PERIOD_MS);

    //detect LCD type
    uint32_t lcd_id = lcd_get_id(spi);
    int lcd_detected_type = 0;
    int lcd_type;

    printf("LCD ID: %08X\n", lcd_id);
    if (lcd_id == 0)
    {
        //zero, ili
        lcd_detected_type = LCD_TYPE_ILI;
        printf("ILI9341 detected.\n");
    }
    else
    {
        // none-zero, ST
        lcd_detected_type = LCD_TYPE_ST;
        printf("ST7789V detected.\n");
    }

    lcd_type = lcd_detected_type;

    if (lcd_type == LCD_TYPE_ST)
    {
        printf("LCD ST7789V initialization.\n");
        lcd_init_cmds = st_init_cmds;
    }
    else
    {
        printf("LCD ILI9341 initialization.\n");
        lcd_init_cmds = ili_init_cmds;
    }

    //Send all the commands
    while (lcd_init_cmds[cmd].databytes != 0xff)
    {
        lcd_cmd(spi, lcd_init_cmds[cmd].cmd);
        lcd_data(spi, lcd_init_cmds[cmd].data, lcd_init_cmds[cmd].databytes & 0x1F);
        if (lcd_init_cmds[cmd].databytes & 0x80)
        {
            vTaskDelay(100 / portTICK_PERIOD_MS);
        }
        cmd++;
    }

    ///Enable backlight
    gpio_set_level(PIN_NUM_BCKL, 0);
}

static void lv_tick_task(void)
{
    lv_tick_inc(1);
}

static lv_obj_t *chart = NULL;
static lv_chart_series_t *series = NULL;
static void gui_init(void)
{
    //Initialize LittlevGL Library
    lv_init();
    //Register display driver
    lv_disp_drv_t disp;
    lv_disp_drv_init(&disp);
    disp.disp_flush = my_disp_flush;
    lv_disp_drv_register(&disp);
    //Bind littlevgl tick counter to FreeRTOS
    esp_register_freertos_tick_hook(lv_tick_task);

    //Create a new Screen
    lv_obj_t *scr = lv_obj_create(NULL, NULL);
    lv_scr_load(scr);

    //Use alien theme
    lv_theme_t *th = lv_theme_alien_init(100, NULL);
    lv_theme_set_current(th);

    chart = lv_chart_create(scr, NULL);
    lv_obj_set_size(chart, 300, 200);
    lv_obj_align(chart, NULL, LV_ALIGN_CENTER, 0, 0);
    lv_chart_set_type(chart, LV_CHART_TYPE_POINT | LV_CHART_TYPE_LINE);
    lv_chart_set_series_opa(chart, LV_OPA_70);
    lv_chart_set_series_width(chart, 4);
    lv_chart_set_range(chart, 0, 100);
    series = lv_chart_add_series(chart, LV_COLOR_RED);
}

static void gui_turbo_task(void *pvParameter)
{
    while (1)
    {
        vTaskDelay(100 / portTICK_PERIOD_MS);
        lv_task_handler();
    }
}

static void user_task(void *pvParameter)
{
    uint8_t value = 0;
    while (1)
    {
        value = esp_random() % 100;
        lv_chart_set_next(chart, series, value);
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
}

void app_main()
{
    spi_spec_init();
    lcd_init(spi);
    gui_init();

    xTaskCreate(
        gui_turbo_task,   //Task Function
        "gui_turbo_task", //Task Name
        2048,             //Stack Depth
        NULL,             //Parameters
        1,                //Priority
        NULL);            //Task Handler
    xTaskCreate(
        user_task,   //Task Function
        "user_task", //Task Name
        512,         //Stack Depth
        NULL,        //Parameters
        1,           //Priority
        NULL);       //Task Handler
}

离线

#2 2018-07-06 17:16:34

sindy
会员
注册时间: 2017-09-09
累计积分: 146

Re: 移植LittlevGL到ESP32

厉害了,学习!

离线

#3 2018-07-06 19:53:52

晕哥
管理员
注册时间: 2017-09-06
累计积分: 9,188

Re: 移植LittlevGL到ESP32

感谢分享!

离线

#4 2018-07-06 22:07:20

arphone
会员
注册时间: 2017-11-03
累计积分: 68

Re: 移植LittlevGL到ESP32

感谢分享!收藏了

离线

#5 2018-07-06 22:26:35

超级萌新
会员
注册时间: 2018-05-04
累计积分: 406

Re: 移植LittlevGL到ESP32

感谢分享!收藏了

离线

#6 2018-07-10 13:02:13

dgtg
会员
注册时间: 2017-11-08
累计积分: 166

Re: 移植LittlevGL到ESP32

厉害!屏是SPI接口的吗?刷一帧,速度快不?

离线

#7 2018-07-10 13:12:38

msr06rr
会员
所在地: 苏州
注册时间: 2018-01-11
累计积分: 163

Re: 移植LittlevGL到ESP32

dgtg 说:

厉害!屏是SPI接口的吗?刷一帧,速度快不?

1. 是SPI接口的屏幕
2. 跑demo的时候没有发现卡顿

离线

#8 2018-08-02 20:41:43

sblpp
会员
注册时间: 2018-02-14
累计积分: 91

Re: 移植LittlevGL到ESP32

谢谢分享,楼主能分享下整个工程吗?谢谢啦。

离线

#9 2020-07-18 07:46:02

selected
会员
注册时间: 2020-04-10
累计积分: 3

Re: 移植LittlevGL到ESP32

在mircopython里有大神移植过LittlevGL没?

离线

#10 2020-07-31 17:23:01

torusww
会员
注册时间: 2020-04-07
累计积分: 2

Re: 移植LittlevGL到ESP32

感谢分享!收藏了

离线

页脚

工信部备案:粤ICP备20025096号-1 Powered by FluxBB