SPI master problem when driving ST7789,get puzzled between spi_device_polling_transmit and spi_device_queue_trans差异?

geokai
Posts: 6
Joined: Sat Mar 27, 2021 1:33 pm

SPI master problem when driving ST7789,get puzzled between spi_device_polling_transmit and spi_device_queue_trans差异?

Postby geokai » Sat Mar 27, 2021 2:55 pm

I was doing ST7789 240*240 LCD driver, which is modified from official example in "SPI_MASTER/LCD" . And the main modification is below. The LCD is working well, but the Timing Diagram puzzled me.

Code: Select all

引脚部分
#ifdef CONFIG_IDF_TARGET_ESP32
#define LCD_HOST    HSPI_HOST
#define DMA_CHAN    2

#define PIN_NUM_MISO -1
#define PIN_NUM_MOSI 25
#define PIN_NUM_CLK  12
#define PIN_NUM_CS   27

#define PIN_NUM_DC   33
#define PIN_NUM_RST  14
#define PIN_NUM_BCKL -1

一次刷新120行
#define PARALLEL_LINES 120

主函数

Code: Select all

void app_main(void)
{
    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,
        .max_transfer_sz=PARALLEL_LINES*240*2+8
    };
    spi_device_interface_config_t devcfg={
        .clock_speed_hz=25*1000*1000,           //Clock out at 10 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, DMA_CHAN);
    ESP_ERROR_CHECK(ret);
    //Attach the LCD to the SPI bus
    ret=spi_bus_add_device(LCD_HOST, &devcfg, &spi);
    ESP_ERROR_CHECK(ret);
    //Initialize the LCD
    lcd_init(spi);
    //Go do nice stuff.
    display_pretty_colors(spi);
}

Code: Select all

static void display_pretty_colors(spi_device_handle_t spi)
{
    uint16_t *lines[2];
    uint8_t pix[240];
    //Allocate memory for the pixel buffers
    ESP_LOGI(TAG,"start heap caps malloc");
    for (int i=0; i<2; i++) {
        lines[i]=heap_caps_malloc(240*PARALLEL_LINES*sizeof(uint16_t), MALLOC_CAP_DMA);
        assert(lines[i]!=NULL);
    }
    uint16_t frame=0xFFFF;
    //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--;
        if (frame==0) frame = 0xFFFF;
        for (int y=0; y<240; y+=PARALLEL_LINES) {
            //Calculate a line.
            // pretty_effect_calc_lines(lines[calc_line], y, frame, PARALLEL_LINES);

            for (int j=0; j<240*PARALLEL_LINES; j++) lines[0][j] = (frame<<8) + (frame>>8);
            //Finish up the sending process of the previous line, if any
            ESP_LOGI(TAG,"fill color %2x", frame);
            if (sending_line!=-1) send_line_finish(spi);
            ESP_LOGI(TAG,"send line");

            send_lines(spi, y, lines[0]);
            ESP_LOGI(TAG,"finish send line");
        }
    }


}

Code: Select all

static void send_lines(spi_device_handle_t spi, int ypos, uint16_t *linedata)
{
    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.
    DRAM_ATTR static spi_transaction_t trans[6];
    DRAM_ATTR static uint8_t data_command=0x2c;
    static uint8_t ydata_pos_command[5];

    //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]=((uint16_t)239)>>8;       //End Col High
    trans[1].tx_data[3]=((uint16_t)239)&0xff;     //End Col Low
    
    trans[2].tx_data[0]=0x2B;           //Page address set

    ydata_pos_command[0] = ypos>>8;
    ydata_pos_command[1] = ypos&0xff; 
    ydata_pos_command[2] = (ypos+PARALLEL_LINES)>>8; 
    ydata_pos_command[3] = (ypos+PARALLEL_LINES)&0xff;
    trans[3].tx_buffer = ydata_pos_command;
    trans[3].length=4*8;          //Data length, in bits
    trans[3].flags=0; //undo SPI_TRANS_USE_TXDATA flag


    trans[4].tx_buffer=&data_command;        //finally send the line data
    trans[4].length = 8;
    trans[4].flags=0;

    trans[5].tx_buffer = linedata;
    trans[5].length=240*2*8*PARALLEL_LINES;          //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);
        // ret=spi_device_polling_transmit(spi, &trans[x]);
        
        assert(ret==ESP_OK);
    }

}

I get the Timing Diagram below

Image


Image


Image

1) ST7789 init part,

2a) the first 120 line of the first frame. Represent function send_lines()
including 1cmd+4data+1cmd+4data+1cmd+240*120*2data

2b) the rest 120 line of the first frame. Represent function send_lines()
including 1cmd+4data+1cmd+4data+1cmd+240*120*2data

my question is
why cmd or data transfer interval between stage 1) 2a) and 3) are different , from 8ms ->4ms->20us. as there is no delay() function between cmd or data transfer.
is it cause by spi_device_polling_transmit() and spi_device_queue_trans(), or something else?


geokai
Posts: 6
Joined: Sat Mar 27, 2021 1:33 pm

Re: SPI master problem when driving ST7789,get puzzled between spi_device_polling_transmit and spi_device_queue_trans

Postby geokai » Mon Mar 29, 2021 2:46 pm

I re-upload the image
Image
Image
Image

ESP_Sprite
Posts: 9727
Joined: Thu Nov 26, 2015 4:08 am

Re: SPI master problem when driving ST7789,get puzzled between spi_device_polling_transmit and spi_device_queue_trans

Postby ESP_Sprite » Tue Mar 30, 2021 4:33 am

Interesting. I can't explain it. Unless you messed with the example, the main loop calls send_line_finish() to finish up any SPI transfers before starting a new one, so you'd expect 2 and 3 to take the same amount of time.

geokai
Posts: 6
Joined: Sat Mar 27, 2021 1:33 pm

Re: SPI master problem when driving ST7789,get puzzled between spi_device_polling_transmit and spi_device_queue_trans

Postby geokai » Tue Mar 30, 2021 6:53 am

Lastest progress
I tried to change PARALLEL_LINES lower than 80 lines at a time. Which means a frame should be sent with send_lines() func called 4times than the previous 2times. And each time when send_lines() function called it represented like the 2a) stage. Interval between cmd never below 4ms any more.

I then tried the same code with pin modification on ESP32S2. Strange, problem solved, cmd interval while sending frame data all about 20us.

Now, I am even more pullzed. I think I need to get deeper to the idf source code, and take some study on the DMA and ISR code. Or any one can give some advise on which thing I should do next.

ESP_Sprite
Posts: 9727
Joined: Thu Nov 26, 2015 4:08 am

Re: SPI master problem when driving ST7789,get puzzled between spi_device_polling_transmit and spi_device_queue_trans

Postby ESP_Sprite » Wed Mar 31, 2021 3:51 am

The difference between the ESP32 and the -S2 might be that the -S2 supports something called 'segmented transfer', which means multiple transactions can be queued and executed consecutively without CPU interference. In this case, the commands and data can be queued up and sent in one go. The ESP32 does not have that, so after each transfer it needs an IRQ to send the next transaction to the SPI hardware. That could be why the -S2 does not have these delays.

Who is online

Users browsing this forum: No registered users and 88 guests