Need sample code for ILI9488 LCD on SPI Interface

Ritesh
Posts: 1383
Joined: Tue Sep 06, 2016 9:37 am
Location: India
Contact:

Need sample code for ILI9488 LCD on SPI Interface

Postby Ritesh » Tue Apr 18, 2017 6:48 am

Hi,

I am working on ESP32 chip and there is one requirement in which I need to driver one LCD which ILI9488 based MircroTech TFT LCD.

I have checked into ESP32 SPI Master Example and found that there is one example of ILI9341 which is different than ILI9488 LCD.

So, Please let me know if anyone has ported any example based on ILI9488 LCD for ESP32 chip on SPI Interface.
Regards,
Ritesh Prajapati

Ritesh
Posts: 1383
Joined: Tue Sep 06, 2016 9:37 am
Location: India
Contact:

Re: Need sample code for ILI9488 LCD on SPI Interface

Postby Ritesh » Tue Apr 18, 2017 2:00 pm

Hi,

I have checked ILI9341-based spi_master example and changed initialization sequence for ILI9488 module. I have also checked that initialization process was completed successfully without any issue.

But, I am not getting any color bars which are mentioned into display_pretty_colors API.

So, Please let me know if anyone has checked spi_master example over ILI9488 320 X 480 LCD.
Regards,
Ritesh Prajapati

Ritesh
Posts: 1383
Joined: Tue Sep 06, 2016 9:37 am
Location: India
Contact:

Re: Need sample code for ILI9488 LCD on SPI Interface

Postby Ritesh » Wed Apr 19, 2017 1:14 am

Anyone has any update on this?
Regards,
Ritesh Prajapati

User avatar
loboris
Posts: 514
Joined: Wed Dec 21, 2016 7:40 pm

Re: Need sample code for ILI9488 LCD on SPI Interface

Postby loboris » Wed Apr 19, 2017 7:10 am

Ritesh wrote:Anyone has any update on this?
I have one ILI9488 based LCD Display module and will test it this weekend or early next week...

Ritesh
Posts: 1383
Joined: Tue Sep 06, 2016 9:37 am
Location: India
Contact:

Re: Need sample code for ILI9488 LCD on SPI Interface

Postby Ritesh » Wed Apr 19, 2017 11:10 am

loboris wrote:
Ritesh wrote:Anyone has any update on this?
I have one ILI9488 based LCD Display module and will test it this weekend or early next week...
Hi,

But for that, do you have sample code to start development for ILI9488 LCD as I have checked into spi_master code and found sample code for ILI9341 LCD which is different then ILI9488 LCD.

So, Please provide me sample source code for ILI9488 if you have
Regards,
Ritesh Prajapati

User avatar
kolban
Posts: 1683
Joined: Mon Nov 16, 2015 4:43 pm
Location: Texas, USA

Re: Need sample code for ILI9488 LCD on SPI Interface

Postby kolban » Thu Apr 20, 2017 12:55 am

Looking here:

http://www.instructables.com/id/Arduino ... 88-SPI-In/

It would appear that the ILI9488 is close to the ILI9341. Looks like you could just about follow this instructables tutorial mapping from Arduino to ESP32.
Free book on ESP32 available here: https://leanpub.com/kolban-ESP32

Ritesh
Posts: 1383
Joined: Tue Sep 06, 2016 9:37 am
Location: India
Contact:

Re: Need sample code for ILI9488 LCD on SPI Interface

Postby Ritesh » Thu Apr 20, 2017 1:19 am

kolban wrote:Looking here:

http://www.instructables.com/id/Arduino ... 88-SPI-In/

It would appear that the ILI9488 is close to the ILI9341. Looks like you could just about follow this instructables tutorial mapping from Arduino to ESP32.
Hi,

Thanks for quick reply.

First of all, I have check SPI Master code which is provided for ILI9341 LCD and updated it for ILI9488 with it's initialization sequence by taking one reference code in which ILI9488 has been verified for ESP8266 using Adrafruit.

So, it should be worked but somehow not getting anything over LCD.

So, I have checked Reset,DC, CS and MOSI pin which are toggling while communication is going on.

Let me know your suggestion for that. I will also share my code as well.
Regards,
Ritesh Prajapati

Ritesh
Posts: 1383
Joined: Tue Sep 06, 2016 9:37 am
Location: India
Contact:

Re: Need sample code for ILI9488 LCD on SPI Interface

Postby Ritesh » Thu Apr 20, 2017 3:59 pm

Hi,

If anyone has developed and check ILI9488 using ESP32-IDF on ESP32 board then provide me sample code for that.
Regards,
Ritesh Prajapati

Ritesh
Posts: 1383
Joined: Tue Sep 06, 2016 9:37 am
Location: India
Contact:

Re: Need sample code for ILI9488 LCD on SPI Interface

Postby Ritesh » Fri Apr 21, 2017 4:59 pm

Any example base on ILI9488 LCD over SPI Interface?
Regards,
Ritesh Prajapati

Ritesh
Posts: 1383
Joined: Tue Sep 06, 2016 9:37 am
Location: India
Contact:

Re: Need sample code for ILI9488 LCD on SPI Interface

Postby Ritesh » Sat Apr 22, 2017 11:04 am

Hi,

Please find my below whole code which I have shared here.

Code: Select all


/* SPI Master example

   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "driver/spi_master.h"
#include "soc/gpio_struct.h"
#include "driver/gpio.h"
#include "esp_log.h"

static const char *TAG = "example_lcd";

/*
 This code displays some fancy graphics on the ILI9341-based 320x240 LCD on an ESP-WROVER_KIT board.
 It is not very fast, even when the SPI transfer itself happens at 8MHz and with DMA, because
 the rest of the code is not very optimized. Especially calculating the image line-by-line
 is inefficient; it would be quicker to send an entire screenful at once. This example does, however,
 demonstrate the use of both spi_device_transmit as well as spi_device_queue_trans/spi_device_get_trans_result
 as well as pre-transmit callbacks.

 Some info about the ILI9341: It has an C/D line, which is connected to a GPIO here. It expects this
 line to be low for a command and high for data. We use a pre-transmit callback here to control that
 line: every transaction has as the user-definable argument the needed state of the D/C line and just
 before the transaction is sent, the callback will set this line to the correct state.
*/

//#define PIN_NUM_MISO 25
//#define PIN_NUM_MISO 18
#define PIN_NUM_MISO 12

//#define PIN_NUM_MOSI 23
//#define PIN_NUM_MOSI 20
#define PIN_NUM_MOSI 13

//#define PIN_NUM_CLK  19
//#define PIN_NUM_CLK  17
#define PIN_NUM_CLK  14

//#define PIN_NUM_CS   22
//#define PIN_NUM_CS   21
#define PIN_NUM_CS   15

//#define PIN_NUM_DC   21
#define PIN_NUM_DC   33

//#define PIN_NUM_RST  18
//#define PIN_NUM_RST  14
#define PIN_NUM_RST  25

#define PIN_NUM_BCKL 5


/*
 The ILI9341 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.
} ili_init_cmd_t;

#if 0 /// This is for ILI9341-based 320x240 LCD SPI Init LCD Sequence
static const ili_init_cmd_t ili_init_cmds[]={
    {0xCF, {0x00, 0x83, 0X30}, 3},
    {0xED, {0x64, 0x03, 0X12, 0X81}, 4},
    {0xE8, {0x85, 0x01, 0x79}, 3},
    {0xCB, {0x39, 0x2C, 0x00, 0x34, 0x02}, 5},
    {0xF7, {0x20}, 1},
    {0xEA, {0x00, 0x00}, 2},
    {0xC0, {0x26}, 1},
    {0xC1, {0x11}, 1},
    {0xC5, {0x35, 0x3E}, 2},
    {0xC7, {0xBE}, 1},
    {0x36, {0x28}, 1},
    {0x3A, {0x55}, 1},
    {0xB1, {0x00, 0x1B}, 2},
    {0xF2, {0x08}, 1},
    {0x26, {0x01}, 1},
    {0xE0, {0x1F, 0x1A, 0x18, 0x0A, 0x0F, 0x06, 0x45, 0X87, 0x32, 0x0A, 0x07, 0x02, 0x07, 0x05, 0x00}, 15},
    {0XE1, {0x00, 0x25, 0x27, 0x05, 0x10, 0x09, 0x3A, 0x78, 0x4D, 0x05, 0x18, 0x0D, 0x38, 0x3A, 0x1F}, 15},
    {0x2A, {0x00, 0x00, 0x00, 0xEF}, 4},
    {0x2B, {0x00, 0x00, 0x01, 0x3f}, 4}, 
    {0x2C, {0}, 0},
    {0xB7, {0x07}, 1},
    {0xB6, {0x0A, 0x82, 0x27, 0x00}, 4},
    {0x11, {0}, 0x80},
    {0x29, {0}, 0x80},
    {0, {0}, 0xff},
};
#endif

#if 0
/// ESP8266 based SPI Init LCD Sequence
static const ili_init_cmd_t ili_init_cmds[]={
	{0x28, {0}, 0x80},
	{0xB0, {0x00}, 1},
	{0xB1, {0xA0}, 1},
	{0xB4, {0x02}, 1},
	{0xB6, {0x02, 0x02, 0x3B}, 3},
	{0xB7, {0xC6}, 1},
	{0xC0, {0x17, 0x15}, 2},
	{0xC1, {0x41}, 1},
	{0xC5, {0x00, 0x12, 0x80}, 3},
	{0x36, {0x48}, 1},
	{0x3A, {0x66}, 1},
    {0xE0, {0x00, 0x03, 0x09, 0x08, 0x16, 0x0A, 0x3F, 0X78, 0x4C, 0x09, 0x0A, 0x08, 0x16, 0x1A, 0x0F}, 15},
    {0XE1, {0x00, 0x16, 0x19, 0x03, 0x0F, 0x05, 0x32, 0x45, 0x46, 0x04, 0x0E, 0x0D, 0x35, 0x37, 0x0F}, 15},
    {0xE9, {0x00}, 1},
    {0x51, {0xFF}, 1},
    {0xF7, {0xA9, 0x51, 0x2C, 0x82}, 4},
    {0x36, {0x48}, 1},
    {0x11, {0}, 0x80},
    {0x29, {0}, 0x80},
    {0, {0}, 0xff},
};
#endif

#if 0
/// 8051 based SPI Init LCD Sequence
static const ili_init_cmd_t ili_init_cmds[]={
	{0xE0, {0x00, 0x07, 0x0F, 0x07, 0x15, 0x09, 0x3C, 0X99, 0x4B, 0x09, 0x10, 0x0D, 0x1C, 0x1E, 0x0F}, 15},
    {0XE1, {0x00, 0x20, 0x23, 0x02, 0x0F, 0x06, 0x34, 0x45, 0x43, 0x04, 0x0A, 0x08, 0x30, 0x37, 0x0F}, 15},
    {0xC0, {0x17, 0x15}, 2},
    {0xC1, {0x41}, 1},
    {0xC5, {0x00, 0x12, 0x80}, 3},
    {0x36, {0x48}, 1},
    {0x3A, {0x66}, 1},
    {0xB0, {0x08}, 1},
	{0xB1, {0xA0, 0x11}, 2},
	{0xB4, {0x02}, 1},
	{0xB6, {0x30, 0x02}, 2},
	{0xBE, {0x00, 0x04}, 2},
    {0xE9, {0x00}, 1},
    {0xF7, {0xA9, 0x51, 0x2C, 0x82}, 4},
    {0x11, {0}, 0x80},
    {0x29, {0}, 0x80},
    {0, {0}, 0xff},
};
#endif

#if 1
/// SPI Init LCD Sequence provided by Microteck
static const ili_init_cmd_t ili_init_cmds[]={
	{0xE0, {0x00, 0x07, 0x0F, 0x07, 0x15, 0x09, 0x3C, 0X99, 0x4B, 0x09, 0x10, 0x0D, 0x1C, 0x1E, 0x0F}, 15},
    {0XE1, {0x00, 0x20, 0x23, 0x02, 0x0F, 0x06, 0x34, 0x45, 0x43, 0x04, 0x0A, 0x08, 0x30, 0x37, 0x0F}, 15},
    {0xC0, {0x10, 0x10}, 2},
    {0xC1, {0x41}, 1},
    {0xC5, {0x00, 0x20, 0xD0}, 3},
    {0x36, {0x08}, 1},
    {0x3A, {0x60}, 1},
    {0xB0, {0x08}, 1},
	{0xB1, {0xA0, 0x11}, 2},
	{0xB4, {0x02}, 1},
	{0xB6, {0x30, 0x02}, 2},
	{0xBE, {0x00, 0x04}, 2},
    {0xE9, {0x00}, 1},
    {0xF7, {0xA9, 0x51, 0x2C, 0x82}, 4},
    {0x11, {0}, 0x80},
    {0x29, {0}, 0x80},
    {0, {0}, 0xff},
};
#endif
//Send a command to the ILI9341. Uses spi_device_transmit, which waits until the transfer is complete.
void ili_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.
}

//Send data to the ILI9341. Uses spi_device_transmit, which waits until the transfer is complete.
void ili_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.
}

//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.
void ili_spi_pre_transfer_callback(spi_transaction_t *t) 
{
    int dc=(int)t->user;
    gpio_set_level(PIN_NUM_DC, dc);
}

//Initialize the display
void ili_init(spi_device_handle_t spi) 
{
    int cmd=0, i = 0;
    //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, 1);
    vTaskDelay(100 / portTICK_RATE_MS);
    gpio_set_level(PIN_NUM_RST, 0);
    vTaskDelay(100 / portTICK_RATE_MS);
    gpio_set_level(PIN_NUM_RST, 1);
    vTaskDelay(120 / portTICK_RATE_MS);

    //Send all the commands
    while (ili_init_cmds[cmd].databytes!=0xff) {
        ili_cmd(spi, ili_init_cmds[cmd].cmd);
        
        //ili_data(spi, ili_init_cmds[cmd].data, ili_init_cmds[cmd].databytes&0x1F);
        if (ili_init_cmds[cmd].databytes&0x80) {
            vTaskDelay(120 / portTICK_RATE_MS);
        }
        else
        {
			ili_data(spi, ili_init_cmds[cmd].data, ili_init_cmds[cmd].databytes&0x1F);
        
			#if 0
				ESP_LOGI(TAG, "ili_init_cmds[cmd].cmd :: 0x%x\r\n", ili_init_cmds[cmd].cmd);
				for(i = 0; i < ili_init_cmds[cmd].databytes; i++)
				{
					ESP_LOGI(TAG, "ili_init_cmds[cmd].data[%d] :: 0x%x\r\n", i, ili_init_cmds[cmd].data[i]);
				}
				ESP_LOGI(TAG, "ili_init_cmds[cmd].databytes :: 0x%x\r\n", ili_init_cmds[cmd].databytes);
			#endif	
		}
        
        cmd++;
    }

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


//To send a line 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 so they get sent as quickly as possible.
void send_line(spi_device_handle_t spi, int ypos, uint16_t *line) 
{
    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]=0;              //Start Col High
    trans[1].tx_data[1]=0;              //Start Col Low
    trans[1].tx_data[2]=(320)>>8;       //End Col High
    trans[1].tx_data[3]=(320)&0xff;     //End Col Low
    trans[2].tx_data[0]=0x2B;           //Page address set
    trans[3].tx_data[0]=ypos>>8;        //Start page high
    trans[3].tx_data[1]=ypos&0xff;      //start page low
    trans[3].tx_data[2]=(ypos+1)>>8;    //end page high
    trans[3].tx_data[3]=(ypos+1)&0xff;  //end page low
    trans[4].tx_data[0]=0x2C;           //memory write
    trans[5].tx_buffer=line;            //finally send the line data
    trans[5].length=320*2*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.
    }
}


//Simple routine to generate some patterns and send them to the LCD. Don't expect anything too
//impressive. Because the SPI driver handles transactions in the background, we can calculate the next line 
//while the previous one is being sent.
void display_pretty_colors(spi_device_handle_t spi) 
{
    uint16_t line[2][320];
    int x, y, frame=0;
    //Indexes of the line currently being sent to the LCD and the line we're calculating.
    int sending_line=-1;
    int calc_line=0;
    
    while(1) {
        frame++;
        for (y=0; y<480; y++) {
            //Calculate a line.
            for (x=0; x<320; x++) {
                line[calc_line][x]=((x<<3)^(y<<3)^(frame+x*y));
            }
            
            //ESP_LOGI(TAG, "sending_line :: 0x%x\r\n", sending_line);
            //Finish up the sending process of the previous line, if any
            if (sending_line!=-1) 
				send_line_finish(spi);
            //Swap sending_line and calc_line
            sending_line=calc_line;
            calc_line=(calc_line==1)?0:1;
            
            //ESP_LOGI(TAG, "calc_line :: 0x%x\r\n", calc_line);
            
            ESP_LOGI(TAG, "y :: 0x%x & line[sending_line :: 0x%x\r\n", y, *line[sending_line]);
            //Send the line we currently calculated.
            send_line(spi, y, line[sending_line]);
            //The line is queued up for sending now; the actual sending happens in the
            //background. We can go on to calculate the next line as long as we do not
            //touch line[sending_line]; the SPI sending process is still reading from that.
        }
    }
}


void app_main()
{
	ESP_LOGI(TAG, "LCD app_main started");
	
    esp_err_t ret;
    spi_device_handle_t spi;
    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
    };
    
    spi_device_interface_config_t devcfg={
        //.clock_speed_hz=10000000,               //Clock out at 10 MHz
        //.clock_speed_hz=1000000,               //Clock out at 1 MHz
        .clock_speed_hz=500000,               //Clock out at 500 KHz
        .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=ili_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_LOGI(TAG, "spi_bus_initialize calling");
    assert(ret==ESP_OK);
    
    //Attach the LCD to the SPI bus
    ret=spi_bus_add_device(HSPI_HOST, &devcfg, &spi);
    ESP_LOGI(TAG, "spi_bus_add_device calling");
    
    assert(ret==ESP_OK);
    
    //Initialize the LCD
    ili_init(spi);
    ESP_LOGI(TAG, "ili_init calling");
    
    //Go do nice stuff.
    display_pretty_colors(spi);
    ESP_LOGI(TAG, "display_pretty_colors calling");
}

So, Can anyone review that code and check that is there any wrong configuration inside code?

As I am using HSPI Interface in master mode and want to access ILI9488 based LCD which 320X480. I have also checked SPI Clock which is not getting proper after running above example as set 500 KHz, 1 MHz, 10 MHz.

Please let me know if anything wrong in that code.
Regards,
Ritesh Prajapati

Who is online

Users browsing this forum: No registered users and 104 guests