单片机开发---ESP32S3移植lvgl+触摸屏

2023-11-13

书接上文

《单片机开发—ESP32-S3模块上手》

本章内容

熟悉一下ESP32S3的开发,修改范例程序的lvgl,使之能够匹配现在的显示屏。
具体工作大概为通过SPI接口连接一块SPI串口屏幕,并且适配lvgl,最后加上触摸屏作为输入。
在这里插入图片描述

屏幕

用了这块SPI屏幕,带触摸和SD卡插槽。下面有显示部分引脚功能说明
在这里插入图片描述

软件工程

工程有一个lvgl的demo程序,位于Espressif\frameworks\esp-idf-v4.4.3\examples\peripherals\lcd\lvgl路径下,不过默认是支持并口8080的方式进行显示的。
所以要改为SPI方式驱动,进行屏幕显示。原理很简单,开头加上屏幕初始化,显示的时候,将内容输入到显示屏即可。
在这里插入图片描述

相关重点

屏幕,触摸和移植。

屏幕驱动

代码参考Espressif\frameworks\esp-idf-v4.4.3\examples\peripherals\spi_master\lcd\main下面对于spi lcd的控制。
引脚配置如下
在这里插入图片描述
注意s3能用的spi有两个SPI2和SPI3,SPI2通常引脚固定,速度快,SPI3可以选择复用GPIO进行配置,速度稍慢,后面的触摸屏会用到。

屏幕SPI配置

屏幕驱动函数中,注意这里的配置
在这里插入图片描述

时钟频率可以选择最高到80Mhz,不过到了80M,我的屏幕就失真了,可能是屏幕自身不支持这么大的速率。
在这里插入图片描述

并行绘制

例子中注意这个宏定义,注释意思就是

为了加快传输速度,每个SPI传输都会发送一组显示的线。 此定义指定数量。更多意味着更多的内存使用,
但设置/完成传输的开销较小。确保240可以除以这个。

//To speed up transfers, every SPI transfer sends a bunch of lines. This define specifies how many. More means more memory use,
//but less overhead for setting up / finishing transfers. Make sure 240 is dividable by this.
#define PARALLEL_LINES 40

就是将一副图片,按照line的方式进行显示,每次传输40行数据。

众所周知,ili9341芯片,是支持直接写入显存的,大概意思就是,屏幕有一块显存,对应了每个像素点,通过2A和2B命令,配置好写入位置,
2A表示修改列范围
在这里插入图片描述
2B表示修改行范围
在这里插入图片描述
再执行2C命令,就可以直接写入显存数据了。

这个位置数据都是矩形,那么我们就可以以40行图像作为一个矩形,写入显存。

那么问题来了,既然有这么大的显存。那么我就要一帧一帧的数据写入可以吗?

答案是不可以,将PARALLEL_LINES调整超过50之后,就只能显示一部分画面了,因为SPI的一次传输,是有限制的

#define LCD_SPI_MAX_DATA_SIZE (SPI_LL_DATA_MAX_BIT_LEN / 8)
#define SPI_LL_DATA_MAX_BIT_LEN (1 << 18)

最多传输32768个字节,对于宽度320像素的屏幕,每行是320*2个字节,所以经过计算,每次最多传输
51行。

那么在小于51中,最大被240整除的数,那自然就是48了,所以设置为48,是效率最高的时候。
在这里插入图片描述

完整驱动

参考spi驱动lcd,将其封装为一个文件,对外提供一个初始化及绘图函数即可

/*
lcd_ili9341.c
  */

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

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

static const char *TAG = "lcd_ili9341";


#define LCD_HOST    SPI2_HOST

#define PIN_NUM_MISO SPI2_IOMUX_PIN_NUM_MISO
#define PIN_NUM_MOSI SPI2_IOMUX_PIN_NUM_MOSI
#define PIN_NUM_CLK  SPI2_IOMUX_PIN_NUM_CLK
#define PIN_NUM_CS   SPI2_IOMUX_PIN_NUM_CS

#define PIN_NUM_DC   9
#define PIN_NUM_RST  4
#define PIN_NUM_BCKL 5

#define PARALLEL_LINES 48

/*
 The LCD needs a bunch of command/argument values to be initialized. They are stored in this struct.
*/
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;

//Place data into DRAM. Constant data gets placed into DROM by default, which is not accessible by DMA.
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_spi_pre_transfer_callback(spi_transaction_t *t);

void lcd_spi_init(spi_device_handle_t* spi)
{
	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=PARALLEL_LINES*320*2+8
	};
	
	spi_device_interface_config_t devcfg=
	{
		.clock_speed_hz=SPI_MASTER_FREQ_40M,	//26*1000*1000, 		  //Clock out at 26 MHz
		.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(LCD_HOST, &buscfg, SPI_DMA_CH_AUTO);
	ESP_ERROR_CHECK(ret);

	//Attach the LCD to the SPI bus
	ret=spi_bus_add_device(LCD_HOST, &devcfg, spi);
	ESP_ERROR_CHECK(ret);
}

/* Send a command to the LCD. Uses spi_device_polling_transmit, which waits
 * until the transfer is complete.
 *
 * Since command transactions are usually small, they are handled in polling
 * mode for higher speed. The overhead of interrupt transactions is more than
 * just waiting for the transaction to complete.
 */
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_polling_transmit(spi, &t);  //Transmit!
    assert(ret==ESP_OK);            //Should have had no issues.
}

/* Send data to the LCD. Uses spi_device_polling_transmit, which waits until the
 * transfer is complete.
 *
 * Since data transactions are usually small, they are handled in polling
 * mode for higher speed. The overhead of interrupt transactions is more than
 * just waiting for the transaction to complete.
 */
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_polling_transmit(spi, &t);  //Transmit!
    assert(ret==ESP_OK);            //Should have had no issues.
}

//This function is called (in irq context!) just before a transmission starts. It will
//set the D/C line to the value indicated in the user field.
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_polling_transmit(spi, &t);
    assert( ret == ESP_OK );

    return *(uint32_t*)t.rx_data;
}

//Initialize the display
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_RATE_MS);
    gpio_set_level(PIN_NUM_RST, 1);
    vTaskDelay(100 / portTICK_RATE_MS);

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

    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");
    }

#ifdef CONFIG_LCD_TYPE_AUTO
    lcd_type = lcd_detected_type;
#elif defined( CONFIG_LCD_TYPE_ST7789V )
    printf("kconfig: force CONFIG_LCD_TYPE_ST7789V.\n");
    lcd_type = LCD_TYPE_ST;
#elif defined( CONFIG_LCD_TYPE_ILI9341 )
    printf("kconfig: force CONFIG_LCD_TYPE_ILI9341.\n");
    lcd_type = LCD_TYPE_ILI;
#endif
    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_RATE_MS);
        }
        cmd++;
    }

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


/* To send a set of lines we have to send a command, 2 data bytes, another command, 2 more data bytes and another command
 * before sending the line data itself; a total of 6 transactions. (We can't put all of this in just one transaction
 * because the D/C line needs to be toggled in the middle.)
 * This routine queues these commands up as interrupt transactions so they get
 * sent faster (compared to calling spi_device_transmit several times), and at
 * the mean while the lines for next transactions can get calculated.
 */
void send_lines(spi_device_handle_t spi, int x1pos,int x2pos,int y1pos,int y2pos, uint16_t *linedata,int length)
{
    esp_err_t ret;
    int x;
    //Transaction descriptors. Declared static so they're not allocated on the stack; we need this memory even when this
    //function is finished because the SPI driver needs access to it even while we're already calculating the next line.
    static spi_transaction_t trans[6];

    //In theory, it's better to initialize trans and data only once and hang on to the initialized
    //variables. We allocate them on the stack, so we need to re-init them each call.
    for (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;
    }
    trans[0].tx_data[0]=0x2A;           //Column Address Set
    trans[1].tx_data[0]=x1pos>>8;              //Start Col High
    trans[1].tx_data[1]=x1pos&0xff;              //Start Col Low
    trans[1].tx_data[2]=x2pos>>8;       //End Col High
    trans[1].tx_data[3]=x2pos&0xff;     //End Col Low
   
    trans[2].tx_data[0]=0x2B;           //Page address set
    trans[3].tx_data[0]=y1pos>>8;        //Start page high
    trans[3].tx_data[1]=y1pos&0xff;      //start page low
    trans[3].tx_data[2]=y2pos>>8;    //end page high
    trans[3].tx_data[3]=y2pos&0xff;  //end page low
   
    trans[4].tx_data[0]=0x2C;           //memory write
    trans[5].tx_buffer=linedata;        //finally send the line data

    trans[5].length=length*8;          //Data length, in bits
    trans[5].flags=0; //undo SPI_TRANS_USE_TXDATA flag

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

    //When we are here, the SPI driver is busy (in the background) getting the transactions sent. That happens
    //mostly using DMA, so the CPU doesn't have much to do here. We're not going to wait for the transaction to
    //finish because we may as well spend the time calculating the next line. When that is done, we can call
    //send_line_finish, which will wait for the transfers to be done and check their status.
}

void send_line_finish(spi_device_handle_t spi)
{
    spi_transaction_t *rtrans;
    esp_err_t ret;
    //Wait for all 6 transactions to be done and get back the results.
    for (int x=0; x<6; x++) 
	{
        ret=spi_device_get_trans_result(spi, &rtrans, portMAX_DELAY);
        assert(ret==ESP_OK);
        //We could inspect rtrans now if we received any info back. The LCD is treated as write-only, though.
    }
}

效果测试

在这里插入图片描述
经过测试,例子大概在每秒23帧左右。也还算可以了,毕竟电影才每秒24帧。

触摸屏使用

这个触摸屏也是SPI接口,引脚说明
在这里插入图片描述

触摸屏引脚定义

这次用SPI3,采用复用GPIO的方式使用,只是在配置上有所区别,使用来是一样的。

在这里插入图片描述
不过注意,这里有个坑,后续会填上。

在这里插入图片描述

触摸屏SPI配置

这块也是研究很久才调试通过了。在这里插入图片描述
这里的配置要重点看一下,尤其是devcfg中,关于命令和地址长度的配置,否则无法正常驱动触摸屏。

还有速率,不适合太高,否则读取数据太快,反而读不到。
在这里插入图片描述

XPT2046的使用

简单来说,就是当IRQ引脚电压为高的时候,就表示有触摸操作,随后读取xy模拟坐标,最后通过边界值,计算出物理坐标。

在这里插入图片描述
根据不同的屏幕大小,模拟坐标变化范围不一样,所以计算坐标时候的数值也不一样。举个例子
我用的屏幕

  1. 得到左上角和右下角的模拟量坐标XY极限值
    x=1960,y=1871
    x=152,y=110

  2. 转换模拟坐标值范围
    x坐标:1960~152 -->1808~0
    y坐标:1871~110 --> 1761~0

  3. 计算斜率,即计算出每个像素对应多少单位模拟量
    x坐标的斜率: 1808/320=5.65
    y坐标的斜率: 1761/240=7.3375

  4. 得到实际的像素坐标
    x坐标: 320-(模拟量-152)/5.65
    y坐标: 240-(模拟量-110)/7.3375

后续可能需要增加一些去抖动。

触摸屏完整驱动

/*
 touch.c
 */

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

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

static const char *TAG = "touch";


#define TOUCH_HOST    SPI3_HOST

#define TOUCH_MISO 15 
#define TOUCH_MOSI 16
#define TOUCH_CLK  17
#define TOUCH_CS   18
#define TOUCH_IRQ  37

static spi_device_handle_t touch_spi;



void touch_spi_init(void)
{
	esp_err_t ret;
	spi_bus_config_t buscfg=
	{
		.miso_io_num=TOUCH_MISO,
		.mosi_io_num=TOUCH_MOSI,
		.sclk_io_num=TOUCH_CLK,
		.quadwp_io_num=-1,
		.quadhd_io_num=-1,
		//.max_transfer_sz=PARALLEL_LINES*320*2+8
		.flags = SPICOMMON_BUSFLAG_GPIO_PINS
	};
	
	spi_device_interface_config_t devcfg=
	{
		.command_bits=8,
		.address_bits=0,
		.clock_speed_hz=1000*1000,//SPI_MASTER_FREQ_10M,	//26*1000*1000, 		  //Clock out at 26 MHz
		.mode=0,								//SPI mode 0
		.spics_io_num=TOUCH_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(TOUCH_HOST, &buscfg, SPI_DMA_CH_AUTO);
	ESP_ERROR_CHECK(ret);

	//Attach the LCD to the SPI bus
	ret=spi_bus_add_device(TOUCH_HOST, &devcfg,&touch_spi);
	ESP_ERROR_CHECK(ret);

    gpio_set_direction(TOUCH_IRQ, GPIO_MODE_INPUT);
}


/*
函数功能:  读2个字节
说明: 读取16位数据,最低4位数据无效,有效数据是高12位

*/
static uint16_t XPT2046_ReadData(uint8_t cmd)
{

	uint16_t data=0;

	spi_transaction_t t = 
	{
	.cmd = cmd,
	.length=16,
	.rxlength = 16,
	.flags = SPI_TRANS_USE_RXDATA,
	};
    esp_err_t ret = spi_device_polling_transmit(touch_spi, &t);
    assert( ret == ESP_OK );

	data=(t.rx_data[0]<<4)+(t.rx_data[1]>>4);
    return data;


}


/*
XPT2046的命令:

10010000  :测试Y的坐标 0x90
11010000  :测试X的坐标 0xD0

返回值:  0表示没有读取到坐标,1表示读取到当前坐标

//1. 得到左上角和右下角的坐标XY极限值
  x=1960,y=1871
  x=152,y=110

//2. 转换坐标值
  x坐标:1960~152 -->1808~0
  y坐标:1871~110 -->  1761~0

//3. 计算斜率
  x坐标的斜率: 1808/320=5.65
  y坐标的斜率: 1761/240=7.3375

//4. 得到实际的像素坐标
  x坐标:  320-(模拟量-152)/5.5
  y坐标:  240-(模拟量-110)/7.3375
*/
uint8_t touch_ReadXY(uint16_t* x,uint16_t* y)
{
	if(gpio_get_level(TOUCH_IRQ)==0)
	{
        vTaskDelay(pdMS_TO_TICKS(10));
		if(gpio_get_level(TOUCH_IRQ)==0)
		{
			vTaskDelay(pdMS_TO_TICKS(10));
			/*1. 得到物理坐标*/
			int x0=XPT2046_ReadData(0x90);
			int y0=XPT2046_ReadData(0xD0);
		
			//printf("%d,%d\n",x0,y0);		
			/*2. 得到像素坐标*/
			*x=320-(x0-152)/5.65;
			*y=240-(y0-110)/7.3375;
			//ESP_LOGI(TAG, "原始 x:%d,y:%d ,转换后 x:%d,y:%d",x0,y0,*x,*y);
			
			return 1;
		}
		else
			return 0;
	}
	else
		return 0;
}


LVGL修改

demo中默认支持的是 并口,所以要修改为通过SPI方式显示
在这里插入图片描述
在显示回调 ,调用见面封装的ili9341显示接口。
在这里插入图片描述
触摸设备同样需要注册
在这里插入图片描述
回调函数中,进行坐标读取。在这里插入图片描述
这里调动额select模块测试了一下,触摸还算可以
在这里插入图片描述

过程问题

随后在性能提升的时候,配置SPI PSRAM的时候,一旦开启PSRAM,就 出现启动卡主的问题,因为之前一直这样开启,也没问题,一度以为自己的模块坏了。
在这里插入图片描述

后来想起来这个关键信息

在这里插入图片描述
原来是37引脚的问题,所以修改了一下触摸引脚IRQ,问题得到了解决。
再次记录一下完整启动打印

ESP-ROM:esp32s3-20210327
Build:Mar 27 2021
rst:0x1 (POWERON),boot:0x8 (SPI_FAST_FLASH_BOOT)
SPIWP:0xee
mode:DIO, clock div:1
load:0x3fce3808,len:0x1664
load:0x403c9700,len:0xbb8
load:0x403cc700,len:0x2e8c
entry 0x403c9954
I (24) boot: ESP-IDF v4.4.3-dirty 2nd stage bootloader
I (25) boot: compile time 11:10:38
I (25) boot: chip revision: 0
I (27) boot.esp32s3: Boot SPI Speed : 80MHz
I (32) boot.esp32s3: SPI Mode       : DIO
I (36) boot.esp32s3: SPI Flash Size : 16MB
I (41) boot: Enabling RNG early entropy source...
I (47) boot: Partition Table:
I (50) boot: ## Label            Usage          Type ST Offset   Length
I (57) boot:  0 nvs              WiFi data        01 02 00009000 00006000
I (65) boot:  1 phy_init         RF data          01 01 0000f000 00001000
I (72) boot:  2 factory          factory app      00 00 00010000 00100000
I (80) boot: End of partition table
I (84) esp_image: segment 0: paddr=00010020 vaddr=3c050020 size=0df08h ( 57096) map
I (103) esp_image: segment 1: paddr=0001df30 vaddr=3fc93730 size=020e8h (  8424) load
I (105) esp_image: segment 2: paddr=00020020 vaddr=42000020 size=435e0h (275936) map
I (159) esp_image: segment 3: paddr=00063608 vaddr=3fc95818 size=00ce0h (  3296) load
I (160) esp_image: segment 4: paddr=000642f0 vaddr=40374000 size=0f730h ( 63280) load
I (178) esp_image: segment 5: paddr=00073a28 vaddr=50000000 size=00010h (    16) load
I (185) boot: Loaded app from partition at offset 0x10000
I (185) boot: Disabling RNG early entropy source...
I (199) opi psram: vendor id : 0x0d (AP)
I (199) opi psram: dev id    : 0x02 (generation 3)
I (199) opi psram: density   : 0x03 (64 Mbit)
I (203) opi psram: good-die  : 0x01 (Pass)
I (207) opi psram: Latency   : 0x01 (Fixed)
I (212) opi psram: VCC       : 0x01 (3V)
I (217) opi psram: SRF       : 0x01 (Fast Refresh)
I (222) opi psram: BurstType : 0x01 (Hybrid Wrap)
I (228) opi psram: BurstLen  : 0x01 (32 Byte)
I (233) opi psram: Readlatency  : 0x02 (10 cycles@Fixed)
I (239) opi psram: DriveStrength: 0x00 (1/1)
W (244) PSRAM: DO NOT USE FOR MASS PRODUCTION! Timing parameters will be updated in future IDF version.
I (254) spiram: Found 64MBit SPI RAM device
I (259) spiram: SPI RAM mode: sram 80m
I (263) spiram: PSRAM initialized, cache is in normal (1-core) mode.
I (270) cpu_start: Pro cpu up.
I (274) cpu_start: Starting app cpu, entry point is 0x40375328
0x40375328: call_start_cpu1 at E:/esp32_new_tools/Espressif/frameworks/esp-idf-v4.4.3/components/esp_system/port/cpu_start.c:148

I (0) cpu_start: App cpu up.
I (700) spiram: SPI SRAM memory test OK
I (709) cpu_start: Pro cpu start user code
I (709) cpu_start: cpu freq: 240000000
I (709) cpu_start: Application information:
I (712) cpu_start: Project name:     lcd_lvgl
I (717) cpu_start: App version:      1
I (721) cpu_start: Compile time:     Jan 30 2023 11:09:49
I (727) cpu_start: ELF file SHA256:  07e942fc9d6734b0...
I (733) cpu_start: ESP-IDF:          v4.4.3-dirty
I (739) heap_init: Initializing. RAM available for dynamic allocation:
I (746) heap_init: At 3FC9F478 len 0004A298 (296 KiB): D/IRAM
I (752) heap_init: At 3FCE9710 len 00005724 (21 KiB): STACK/DRAM
I (759) heap_init: At 3FCF0000 len 00008000 (32 KiB): DRAM
I (765) heap_init: At 600FE000 len 00002000 (8 KiB): RTCRAM
I (772) spiram: Adding pool of 8192K of external SPI memory to heap allocator
I (780) spi_flash: detected chip: gd
I (784) spi_flash: flash io: dio
I (788) sleep: Configure to isolate all GPIO pins in sleep state
I (794) sleep: Enable automatic switching of GPIO sleep configuration
I (802) cpu_start: Starting scheduler on PRO CPU.
I (0) cpu_start: Starting scheduler on APP CPU.
I (822) spiram: Reserving pool of 32K of internal memory for DMA/internal allocations
LCD ID: 00000000
ILI9341 detected.
LCD ILI9341 initialization.
I (1222) s3_lvgl: Initialize LVGL library
I (1222) s3_lvgl: Register display driver to LVGL
I (1222) s3_lvgl: Install LVGL tick timer
I (1222) s3_lvgl: Display LVGL animation

只想说一句。如果不能用,你引出来干嘛?让人看着着急吗?

在这里插入图片描述

代码下载及参考

完整代码下载
使用方法:
首先拷贝Espressif\frameworks\esp-idf-v4.4.3\examples\peripherals\lcd\lvgl作为工程,
然后将下载内容复制进去,即可完成编译,

其实不太推荐,还是需要自己学习一遍。然后参考前面的问题,进行自己实践更明白。

参考资料
《STM32 入门开发:编写 XPT2046 电阻触摸屏驱动 (模拟 SPI)》

在这里插入图片描述

结束语

昨天最大的news大概就是某失踪很久的同学,被发现在校外不远 树林中,一个拉网搜索过的地方,其实感觉最大的可能,就是不是第一现场,100多天,不可能没人看到。所以,真相还没有浮出水面。而且感觉看到的,不一定就是真相,毕竟这么久都没有出来结果。
在这里插入图片描述
就像现在的推送机制的存在,你爱看的内容,认同的内容,就会越来越多出现在你的视野里,久而久之,你认为世界就应该是这样,一旦出现了一些不同的声音,你就会认为是少数,是另类,甚至是极端。这就是信息茧房。我们学校的教育也是一样,教育仅仅是一种福利,因为目前的教育带有浓重的儒家思想,偏向于使人遵守规矩,服从管理……
所以为了保持清醒,要多想多思考,并且要保持理性。
在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

单片机开发---ESP32S3移植lvgl+触摸屏 的相关文章

  • 解决jar包启动关闭窗口后停止项目问题

    项目以jar形式部署到服务器 通常会以这样的形式 java jar zpw 2 2 5 RELEASE jar 问题 当我们一关闭当前窗口就会停止运行项目 解决思路 在后台运行 解决方法 nohup java jar zpw 2 2 5 R
  • 计算次对角线元素平均值、折半查找(二分查找)、冒泡排序

    一 计算次对角线元素平均值 首先从键盘输入方阵的行数n 然后输入n行n列的整数保存到数组a中 最后计算方阵中次对角线元素的平均值并输出 输入格式 第一行输入方阵行数n 以下n行每行输入n个整数 以空格间隔 输出格式 输出平均值 保留两位小数
  • python之赋值、浅拷贝和深拷贝

    概念 python中的对象包含三个属性 id type和value id代表着对象唯一的标识符 是独一无二的 cpython中代表了存放对象的内存地址 type代表着对象的类型 比如说数字1的type就是int 字符串 abc 的type就
  • archetype-catalog.xml下载

    archetype catalog xml下载 archetype catalog xml下载 archetype catalog xml下载 https pan baidu com s 1HPjvvcOPv8vFPq31sANWng 提取
  • AOP-Chap17-Templates

    Chap17 Templates 1 Templated Functions 1 1 Instantiating Templated Functions 2 Templated Classes 2 1 Templates as Templa

随机推荐

  • SQli-labs 进阶 21-38关

    SQli labs 进阶 21 38关 LESS 21 拼接方式 id id 21关和20关本质上没有区别 但是是使用base64进行了编码 使用burp进行抓包 可以很明显看到 uname值为一串编码 利用burp自带的编码工具进行解码
  • SELECT连接查询和嵌套查询

    连接查询 同时涉及到两个及以上表的查询 语句 SELECT lt 列名1 gt lt 列名2 gt FROM lt 表1 gt lt 表2 gt lt 表3 gt WHERE lt 表名1 gt lt 列名1 gt lt 比较运算符 gt
  • mc服务器配置文件leveltype,zimg 服务器配置文件

    1 zimg server config 2 3 server config 4 是否后台运行 5 is daemon 1 6 绑定IP 7 ip 0 0 0 0 8 端口 9 port 4869 10 运行线程数 默认值为服务器CPU数
  • Python爬虫实战 爬取同城艺龙酒店信息

    1 爬虫说明 同城艺龙的反爬做的是非常好的 本博主在与同城艺龙进行了一整天的殊死搏斗才将其完全的爬下来 本博主是以无锡为例 将无锡的所有酒店的相关信息都爬了下来 共3399条酒店数据 当然其他城市也是可以的 只需要修改指定字段即可 本博主是
  • 阿里云视频vod.cn-beijing.aliyuncs.com (Domain name not found)

    今天edusoho网校系统突然爆 An exception has been thrown during the rendering of a template Server unreachable Errno 6 Could not re
  • Dynamics CRM 后台代码如何发邮件《 中秋快乐!》

    前面搞了邮箱的配置 那么就可以借助Dynamics CRM工作流来给用户发邮件了 比如签核提醒 什么的 对于这些收件人简单的固定且存在CRM中 比如用户或者联系人的 通过配置就可以实现 但是当邮件通知的人不在CRM中或者收件人cc复杂时 无
  • 记录xss练习 level1-level5(一)

    练习网址 http test xss tv 本人菜鸟一枚 如有分析不对的地方 望前辈们指正 话不投机 不对 话不多说直接上图 上分析 level1 很简单的一道题 从URL中我们可以分析到 xxx level1 php name test
  • 图像处理学习路线总结 python

    记录一下我的图像处理学习路线 后续有其他想法了再补充 目录 1 图像处理基础知识 1 1 数字图像处理公开课 2 利用opencv进行实现 2 1 在pycharm里安装opencv库 2 2 图像处理基础操作 2 2 1 读取图片 2 2
  • Android 控件最上层显示方法

    view bringToFront 可以讲布局在下层的控件放到上层 不被其他控件挡住
  • rails的一点点认识

    1 会开创新项目 rails new 名称 2 会创建控制类 rails g generate controller 类名 html文件名 3 会创建model rails g generate model 名称 散列属性key value
  • ipsec openswan资料搜集

    http blog csdn net rosetta article category 1190390 http blog chinaunix net uid 127037 list 1 html sid 136058 http blog
  • 大数据面试题(五)----HIVE面试题

    版权声明 本文为CSDN博主 北京小辉 的原创文章 遵循 CC 4 0 BY SA 版权协议 转载请附上原文出处链接及本声明 原文链接 https blog csdn net silentwolfyh article details 103
  • 在进入微信小程序之前显示登录页面的解决方案

    这几天想要在小程序进入之前做个登陆的页面 用户输入账号密码正确才能进入小程序 但是看了因为小程序一启动就是默认进入tabar里的第一个页面 上了百度查也是没有哦好的方式 自己琢磨了以下的几种方式 1 使用路由重定向 小程序中有几个用于重新向
  • Spring整合mybatis完整maven配置

    Spring整合mybatis完整maven配置 Maven配置 2022年1月23 基本都是最新版本
  • HTML 表单标签

    一 什么是表单标签 表单是用来采集用户的输入数据 然后将数据提交给服务器 二 表单的组成 一个表单有三个基本组成部分 表单标签 这里面包含了处理表单数据所用程序的URL以及数据提交到服务器的方法 表单域 包含了文本框 密码框 隐藏域 多行文
  • #1295-D: Deprecated declaration I2C_Start - give arg types

    KEIL5编译器报警告 警告内容如下 1295 D Deprecated declaration I2C Start give arg types 警告原因无参数函数的函数声明没有标明参数为void型 在函数声明中加入void即可 警告代码
  • 1.Kubernetes基础入门学习概述系统架构及组件浅析

    目录 0x00 基础简述 1 发展经历 2 简要介绍 3 系统架构 Borg 系统 Kubernetes 系统 0x01 组件浅析 1 Kubernetes Master 2 Kubernetes Node 3 Kubernetes 插件
  • Keras利用卷积神经网络(CNN)识别手写数字(环境python3.5)

    今天介绍如何利用卷积神经网络进行MNIST数据集的手写数字识别 即将手写数字图像images识别为数字标签labels 目录 数据预处理 模型建立 评估模型准确率 进行预测 建模思路如下图 数据预处理 MNIST数据集共有训练数据60 00
  • json to dart工具

    工具链接https javiercbk github io json to dart
  • 单片机开发---ESP32S3移植lvgl+触摸屏

    书接上文 单片机开发 ESP32 S3模块上手 本章内容 熟悉一下ESP32S3的开发 修改范例程序的lvgl 使之能够匹配现在的显示屏 具体工作大概为通过SPI接口连接一块SPI串口屏幕 并且适配lvgl 最后加上触摸屏作为输入 屏幕 用