lovyan03 / LovyanGFX

SPI LCD graphics library for ESP32 (ESP-IDF/ArduinoESP32) / ESP8266 (ArduinoESP8266) / SAMD51(Seeed ArduinoSAMD51)
Other
1.02k stars 187 forks source link

Core Panic'ed on ESP32-S3 board with ILI9488 on 8-bit parallel mode #534

Closed alba-ado closed 2 months ago

alba-ado commented 2 months ago

Environment ( 実行環境 )

Problem Description ( 問題の内容 )

I am using the Parallel8 bus on a custom ESP32-S3 board that I made. When intializing the LovyanGFX library, the processor crashes with the given error:

Guru Meditation Error: Core  0 panic'ed (LoadProhibited). Exception was unhandled.

Core  0 register dump:
PC      : 0x42006ccf  PS      : 0x00060230  A0      : 0x82006ce6  A1      : 0x3fcf3c50  
0x42006ccf: void lgfx::v1::LGFXBase::fillScreen<int>(int const&) at /home/ado/Projects/Espressif/ocm-firmware/components/LovyanGFX/src/lgfx/v1/LGFXBase.hpp:272
 (inlined by) DisplayDriver::init() at /home/ado/Projects/Espressif/ocm-firmware/main/include/TFT/DisplayDriver.h:54

A2      : 0x3fc959e0  A3      : 0x3fcf0518  A4      : 0x00000001  A5      : 0x3fcf3b50  
A6      : 0x00000027  A7      : 0x3fcf3b50  A8      : 0x00000000  A9      : 0x3fcf3c30  
A10     : 0x00000000  A11     : 0x4202b9c8  A12     : 0x00000000  A13     : 0x00000000  
0x4202b9c8: unsigned int lgfx::v1::color_convert<lgfx::v1::rgb888_t, lgfx::v1::bgr888_t>(unsigned int) at /home/ado/Projects/Espressif/ocm-firmware/components/LovyanGFX/src/lgfx/v1/misc/colortype.hpp:550

A14     : 0x3fcf0518  A15     : 0x00000001  SAR     : 0x00000008  EXCCAUSE: 0x0000001c  
EXCVADDR: 0x00000012  LBEG    : 0x40056f5c  LEND    : 0x40056f72  LCOUNT  : 0x00000000  

Backtrace: 0x42006ccc:0x3fcf3c50 0x42006ce3:0x3fcf3c70 0x42006ea4:0x3fcf3ca0 0x42006eaf:0x3fcf3cd0 0x4202e35c:0x3fcf3cf0
0x42006ccc: lgfx::v1::LGFXBase::width() const at /home/ado/Projects/Espressif/ocm-firmware/components/LovyanGFX/src/lgfx/v1/LGFXBase.hpp:284
 (inlined by) void lgfx::v1::LGFXBase::fillScreen<int>(int const&) at /home/ado/Projects/Espressif/ocm-firmware/components/LovyanGFX/src/lgfx/v1/LGFXBase.hpp:272
 (inlined by) DisplayDriver::init() at /home/ado/Projects/Espressif/ocm-firmware/main/include/TFT/DisplayDriver.h:54

0x42006ce3: System::init() at /home/ado/Projects/Espressif/ocm-firmware/main/src/System.h:76

0x42006ea4: systemStartup() at /home/ado/Projects/Espressif/ocm-firmware/main/main.cpp:8

0x42006eaf: app_main at ??:?

0x4202e35c: main_task at /home/ado/Programs/Espressif/esp-idf-v4.4/components/freertos/port/port_common.c:141 (discriminator 1)

Crash occurs when calling fillScreen from anywhere inside the code. The interesting part is that, display actually gets filled with black then it crashes. I am sure with my pin configuration since the same code works with the TFT_eSPI library.

I tried changing frequency etc. but the crash doesn't seem to be related to that. Backtrace shows that the issue comes from the LGFXBase.hpp:284 when calling v1::LGFXBase::width()

Similar issue occurs when I comment the fillscreen and uncomment the createSprite, this time the backtrace becomes:

Guru Meditation Error: Core  0 panic'ed (LoadProhibited). Exception was unhandled.

Core  0 register dump:
PC      : 0x42006cdf  PS      : 0x00060230  A0      : 0x82006e52  A1      : 0x3fcf3c50  
0x42006cdf: lgfx::v1::LGFX_Sprite::createSprite(int, int) at /home/ado/Projects/Espressif/ocm-firmware/components/LovyanGFX/src/lgfx/v1/LGFX_Sprite.hpp:174
 (inlined by) DisplayDriver::init() at /home/ado/Projects/Espressif/ocm-firmware/main/include/TFT/DisplayDriver.h:55

A2      : 0x3fc95888  A3      : 0x00000000  A4      : 0x3fc95984  A5      : 0x3fc958bc  
A6      : 0x00000027  A7      : 0x3fcf3b50  A8      : 0x3fc95988  A9      : 0x3fcf3c30  
A10     : 0x3fc959e0  A11     : 0x4202bb34  A12     : 0x00000018  A13     : 0x00000000  
0x4202bb34: unsigned int lgfx::v1::color_convert<lgfx::v1::rgb888_t, lgfx::v1::bgr888_t>(unsigned int) at /home/ado/Projects/Espressif/ocm-firmware/components/LovyanGFX/src/lgfx/v1/misc/colortype.hpp:550

A14     : 0x00000000  A15     : 0x00000001  SAR     : 0x00000008  EXCCAUSE: 0x0000001c  
EXCVADDR: 0x00000012  LBEG    : 0x40056f5c  LEND    : 0x40056f72  LCOUNT  : 0x00000000  

Backtrace: 0x42006cdc:0x3fcf3c50 0x42006e4f:0x3fcf3c70 0x42007010:0x3fcf3ca0 0x4200701b:0x3fcf3cd0 0x4202e4c8:0x3fcf3cf0
0x42006cdc: lgfx::v1::LGFX_Sprite::createSprite(int, int) at /home/ado/Projects/Espressif/ocm-firmware/components/LovyanGFX/src/lgfx/v1/LGFX_Sprite.hpp:174
 (inlined by) DisplayDriver::init() at /home/ado/Projects/Espressif/ocm-firmware/main/include/TFT/DisplayDriver.h:55

0x42006e4f: System::init() at /home/ado/Projects/Espressif/ocm-firmware/main/src/System.h:76

0x42007010: systemStartup() at /home/ado/Projects/Espressif/ocm-firmware/main/main.cpp:8

0x4200701b: app_main at ??:?

0x4202e4c8: main_task at /home/ado/Programs/Espressif/esp-idf-v4.4/components/freertos/port/port_common.c:141 (discriminator 1)

Expected Behavior ( 期待される動作 )

To not crash

Actual Behavior ( 実際の動作 )

Code crashes

Steps to reproduce ( 再現のための前提条件 )

  1. Using esp-idf v4.4 and LGFX as a component, compile a basic demo code.
  2. Upload the code to a ESP32-S3 target with ILI9488 display attached.

// If possible, attach a picture of your setup/wiring here.

#ifndef __ILI9488_TFT_LCD_LovyanGFX_LIBRARY_SETUP__
#define __ILI9488_TFT_LCD_LovyanGFX_LIBRARY_SETUP__

#include <LovyanGFX.hpp>
#include "BoardDefinition.h"

class LGFX : public lgfx::LGFX_Device {
    // Prepare an instance that matches the type of panel to be connected.
    lgfx::Panel_ILI9488 _panel_instance;

    // Prepare an instance that matches the type of bus that connects the panel.
    lgfx::Bus_Parallel8 _bus_instance;

   public:
    // Create a constructor and configure various settings here.
    LGFX(void) {
        {                                       // Configure bus control settings.
            auto cfg = _bus_instance.config();  // Get the bus configuration structure.

            // 8-bit parallel bus settings
            cfg.port = 0;               // Select the I2S port to use (I2S_NUM_0 or I2S_NUM_1) (Uses I2S LCD mode of ESP32)
            cfg.freq_write = 16000000;  // Transmission clock (maximum 20MHz, rounded to 80MHz divided by an integer)
            cfg.freq_read = 8000000;
            cfg.pin_wr = PIN_LCD_WR;    // Pin number connecting WR
            cfg.pin_rd = PIN_LCD_RD;    // Pin number connecting RD
            cfg.pin_rs = PIN_LCD_DC;    // Pin number connecting RS(D/C)
            cfg.pin_d0 = PIN_LCD_D0;    // Pin number connecting D0
            cfg.pin_d1 = PIN_LCD_D1;    // Pin number connecting D1
            cfg.pin_d2 = PIN_LCD_D2;    // Pin number connecting D2
            cfg.pin_d3 = PIN_LCD_D3;    // Pin number connecting D3
            cfg.pin_d4 = PIN_LCD_D4;    // Pin number connecting D4
            cfg.pin_d5 = PIN_LCD_D5;    // Pin number connecting D5
            cfg.pin_d6 = PIN_LCD_D6;    // Pin number connecting D6
            cfg.pin_d7 = PIN_LCD_D7;    // Pin number connecting D7

            _bus_instance.config(cfg);               // Apply the settings to the bus.
            _panel_instance.setBus(&_bus_instance);  // Set the bus to the panel.
        }

        {                                         // Set display panel control.
            auto cfg = _panel_instance.config();  // Get the structure for display panel settings.

            cfg.pin_cs = -1;            // Pin number to which CS is connected (-1 = disable)
            cfg.pin_rst = PIN_LCD_RST;  // Pin number to which RST is connected (-1 = disable)
            cfg.pin_busy = -1;          // Pin number to which BUSY is connected (-1 = disable)

            cfg.panel_width = 320;     // Actual displayable width
            cfg.panel_height = 480;    // Actual display height
            cfg.offset_x = 0;          // Panel X direction offset amount
            cfg.offset_y = 0;          // Panel Y direction offset amount
            cfg.offset_rotation = 0;   // Offset of rotation direction value 0~7 (4~7 are upside down)
            cfg.dummy_read_pixel = 8;  // Number of bits for dummy read before pixel reading
            cfg.dummy_read_bits = 1;   // Number of bits for dummy read before reading data other than pixels
            cfg.readable = true;       // Set to true if data can be read
            cfg.invert = false;        // Set to true if the brightness of the panel is inverted
            cfg.rgb_order = false;     // Set to true if the red and blue colors of the panel are swapped
            cfg.dlen_16bit = false;    // Set to true for panels that transmit data length in 16-bit units using 16-bit parallel or SPI
            cfg.bus_shared = false;     // Set to true if the bus is shared with the SD card (control the bus with drawJpgFile, etc.)

            _panel_instance.config(cfg);
        }

        setPanel(&_panel_instance);  // Set the panel to use.
    }
};

#endif

Code to reproduce this issue ( 再現させるためのコード )

#ifndef __ILI9488_TFT_LCD_CONTROL_INTERFACE__
#define __ILI9488_TFT_LCD_CONTROL_INTERFACE__

//#include <TFT_eSPI.h>

#include <LovyanGFX.hpp>
#include "LovyanGFX_setup.h"

#include <PNGdec.h>
#include <qrcode.h>

//Display Constants
#define MAXIMUM_IMAGE_WIDTH     320

//PNG Decoder Instance
PNG png = PNG();

//Display Instance (TFT_eSPI)
//static TFT_eSPI tft = TFT_eSPI();
//static TFT_eSprite display = TFT_eSprite(&tft);

//Display Instance (LovyanGFX)
static LGFX tft;
static LGFX_Sprite display(&tft);

//Class Definition/////////////////////////////////////////////////////////////////////////////////
class DisplayDriver {
    public:
        //Init Function
        static void init( void );

        //Image Drawing Function
        static int drawPNG( uint8_t * imageFile, const uint32_t imageSize, const uint16_t xPos = 0, const uint16_t yPos = 0 );
        static void drawQR( QRCode * inputQR, const uint16_t xPos, const uint16_t yPos, const uint8_t scalingFactor );

    private:
        //Image Offset
        static inline uint16_t xStart = 0, yStart = 0;

        //Line Buffers
        static inline uint16_t lineBuffer[ MAXIMUM_IMAGE_WIDTH + 10 ];
        static inline uint8_t maskBuffer[( 1 + ( MAXIMUM_IMAGE_WIDTH / 8) )];

        //PNG Draw Handler
        static void drawHandler( PNGDRAW * pDraw );
};

//Init Function////////////////////////////////////////////////////////////////////////////////////
void DisplayDriver::init( void ) {
    tft.init();
    tft.setRotation(2);
    tft.setColorDepth(16);
    tft.fillScreen(TFT_BLACK);
    //display.createSprite(tft.width(), tft.height());
}

//Drawing Functions///////////////////////////////////////////////////////////////////////////
int DisplayDriver::drawPNG( uint8_t * imageFile, const uint32_t imageSize, const uint16_t xPos, const uint16_t yPos ) {
    //Open the PNG File
    int32_t pngResult = png.openFLASH(imageFile, imageSize, drawHandler);
    if( pngResult != PNG_SUCCESS ) return pngResult;

    //Set the Image Offset   
    xStart = xPos;
    yStart = yPos;

    //Draw the PNG
    display.startWrite();
    pngResult = png.decode(NULL, 0);
    display.endWrite();

    //Return the Result
    return pngResult;
}

void DisplayDriver::drawQR( QRCode * inputQR, uint16_t xPos, uint16_t yPos, uint8_t scalingFactor ) {
    //Calculate the size parameters
    const uint16_t qrSize = inputQR->size;
    const uint16_t qrSilent = round( inputQR->size * 0.15 );
    const uint16_t qrZoneWidth = ( ( qrSize + ( qrSilent * 2 ) ) * scalingFactor );

    //Fill in the silent zone
    display.fillSmoothRoundRect( xPos, yPos, qrZoneWidth, qrZoneWidth, ( qrSilent * scalingFactor ), TFT_WHITE );

    //Fil in the modules
    for( uint16_t y = 0; y < inputQR->size; y++ ) {
        for( uint16_t x = 0; x < inputQR->size; x++ ) {
            uint32_t pixelColor = qrcode_getModule( inputQR, x, y ) ? TFT_BLACK : TFT_WHITE;
            uint16_t calcX = xPos + ( ( x + qrSilent ) * scalingFactor );
            uint16_t calcY = yPos + ( ( y + qrSilent ) * scalingFactor );

            if( scalingFactor > 1 ) {
                display.fillRect( calcX, calcY, scalingFactor, scalingFactor, pixelColor );
            }
            else {
                display.drawPixel( calcX, calcY, pixelColor );
            }
        }
    }
}

//PNG Draw Handlers////////////////////////////////////////////////////////////////////////////////
void DisplayDriver::drawHandler( PNGDRAW * pDraw ) {
    png.getLineAsRGB565(pDraw, lineBuffer, PNG_RGB565_BIG_ENDIAN, 0xFFFFFFFF);
    display.pushImage(xStart, yStart + pDraw->y, pDraw->iWidth, 1, lineBuffer);
}

#endif
alba-ado commented 2 months ago

Update:

I tried to fix the screen resolution by hand just by trying this in the Panel.hpp file (IPanel object)

uint16_t width(void) const { return 320; }
uint16_t height(void) const { return 480; }

instead of the

uint16_t width(void) const { return _width; }
uint16_t height(void) const { return _height; }

and it doesn't crash now. It works kinda normally. I managed to write "hello world" to the screen but the background color doesn't change to green for example. Some other weird things as well (such as creating full-screen sized sprites also crashes). But it doesn't crash now. I don't understand why it crashes when it tries to access the _width and _height variables inside the struct.

I use -std=gnu++2a by the way.

lovyan03 commented 2 months ago

私はあなたのプログラムのデバッグ担当者ではないので、このような情報を渡されても検証できません。 私の時間を自由に奪えると思わないでください。

alba-ado commented 2 months ago

I only call the init function and then draw a blank screen. There is nothing to look inside the program. I just posted here because you require it. The library crashes, when I call the fillScreen. Same code works with TFT_eSPI...

Backtrace shows that the issue comes from the LGFXBase.hpp:284 when calling v1::LGFXBase::width()

lovyan03 commented 2 months ago

現象が再現できるビルド可能な最小のプログラムを用意してください。

alba-ado commented 2 months ago

Here is the minimal code: https://github.com/alba-ado/lovyan-demo

I also write it here the two files here so you can see:

LovyanGFX_setup.h

#ifndef __ILI9488_TFT_LCD_LovyanGFX_LIBRARY_SETUP__
#define __ILI9488_TFT_LCD_LovyanGFX_LIBRARY_SETUP__

#include <LovyanGFX.hpp>

#define PIN_LCD_D0              15
#define PIN_LCD_D1              16
#define PIN_LCD_D2              17
#define PIN_LCD_D3              18
#define PIN_LCD_D4              12
#define PIN_LCD_D5              11
#define PIN_LCD_D6              10
#define PIN_LCD_D7              9

#define PIN_LCD_RST             4
#define PIN_LCD_DC              5
#define PIN_LCD_WR              6
#define PIN_LCD_RD              7

class LGFX : public lgfx::LGFX_Device {
    // Prepare an instance that matches the type of panel to be connected.
    lgfx::Panel_ILI9488 _panel_instance;

    // Prepare an instance that matches the type of bus that connects the panel.
    lgfx::Bus_Parallel8 _bus_instance;

   public:
    // Create a constructor and configure various settings here.
    LGFX(void) {
        {                                       // Configure bus control settings.
            auto cfg = _bus_instance.config();  // Get the bus configuration structure.

            // 8-bit parallel bus settings
            //cfg.port = 0;   //LCD_CAM peripheral number. No need to change (only 0 for ESP32-S3.)
            //cfg.freq_write = 16000000;  // Transmission clock (maximum 20MHz, rounded to 80MHz divided by an integer)
            //cfg.freq_read = 8000000;
            cfg.pin_wr = PIN_LCD_WR;    // Pin number connecting WR
            cfg.pin_rd = PIN_LCD_RD;    // Pin number connecting RD
            cfg.pin_rs = PIN_LCD_DC;    // Pin number connecting RS(D/C)

            cfg.pin_d0 = PIN_LCD_D0;    // Pin number connecting D0
            cfg.pin_d1 = PIN_LCD_D1;    // Pin number connecting D1
            cfg.pin_d2 = PIN_LCD_D2;    // Pin number connecting D2
            cfg.pin_d3 = PIN_LCD_D3;    // Pin number connecting D3
            cfg.pin_d4 = PIN_LCD_D4;    // Pin number connecting D4
            cfg.pin_d5 = PIN_LCD_D5;    // Pin number connecting D5
            cfg.pin_d6 = PIN_LCD_D6;    // Pin number connecting D6
            cfg.pin_d7 = PIN_LCD_D7;    // Pin number connecting D7

            _bus_instance.config(cfg);               // Apply the settings to the bus.
            _panel_instance.setBus(&_bus_instance);  // Set the bus to the panel.
        }

        {                                         // Set display panel control.
            auto cfg = _panel_instance.config();  // Get the structure for display panel settings.

            cfg.pin_cs = -1;            // Pin number to which CS is connected (-1 = disable)
            cfg.pin_rst = PIN_LCD_RST;  // Pin number to which RST is connected (-1 = disable)
            cfg.pin_busy = -1;          // Pin number to which BUSY is connected (-1 = disable)

            cfg.panel_width = 320;     // Actual display width
            cfg.panel_height = 480;    // Actual display height
            cfg.offset_x = 0;          // Panel X direction offset amount
            cfg.offset_y = 0;          // Panel Y direction offset amount
            cfg.offset_rotation = 0;   // Offset of rotation direction value 0~7 (4~7 are upside down)
            cfg.dummy_read_pixel = 8;  // Number of bits for dummy read before pixel reading
            cfg.dummy_read_bits = 1;   // Number of bits for dummy read before reading data other than pixels
            cfg.readable = true;       // Set to true if data can be read
            cfg.invert = false;        // Set to true if the brightness of the panel is inverted
            cfg.rgb_order = false;     // Set to true if the red and blue colors of the panel are swapped
            cfg.dlen_16bit = false;    // Set to true for panels that transmit data length in 16-bit units using 16-bit parallel or SPI
            cfg.bus_shared = false;     // Set to true if the bus is shared with the SD card (control the bus with drawJpgFile, etc.)

            _panel_instance.config(cfg);
        }

        setPanel(&_panel_instance);  // Set the panel to use.
    }
};

#endif

main.cpp

#include <LovyanGFX.hpp>
#include "LovyanGFX_setup.h"

//Display Instance (LovyanGFX)
static LGFX tft = LGFX();

void lovyanTest (void) {
    Serial.begin(115200);

    tft.init();
    tft.setRotation(2);
    tft.setColorDepth(16);
    tft.fillScreen(TFT_BLACK);
}

extern "C" void app_main() {
    lovyanTest();
}
alba-ado commented 2 months ago

Here is the stack trace for that code:

Guru Meditation Error: Core  0 panic'ed (LoadProhibited). Exception was unhandled.

Core  0 register dump:
PC      : 0x42005f43  PS      : 0x00060930  A0      : 0x82005f5a  A1      : 0x3fcf3b60  
0x42005f43: void lgfx::v1::LGFXBase::fillScreen<int>(int const&) at /home/ado/Projects/Espressif/lovyan-demo/components/LovyanGFX/src/lgfx/v1/LGFXBase.hpp:272
 (inlined by) lovyanTest() at /home/ado/Projects/Espressif/lovyan-demo/main/main.cpp:13

A2      : 0x3fc93e68  A3      : 0x00000001  A4      : 0x3fc939ac  A5      : 0x0000cdcd  
A6      : 0x00060023  A7      : 0x00060020  A8      : 0x00000000  A9      : 0x3fcf3b40  
A10     : 0x00000000  A11     : 0x42020fdc  A12     : 0x00000000  A13     : 0x0000002c  
0x42020fdc: unsigned int lgfx::v1::color_convert<lgfx::v1::rgb888_t, lgfx::v1::bgr888_t>(unsigned int) at /home/ado/Projects/Espressif/lovyan-demo/components/LovyanGFX/src/lgfx/v1/misc/colortype.hpp:550

A14     : 0x00000078  A15     : 0x00000000  SAR     : 0x00000008  EXCCAUSE: 0x0000001c  
EXCVADDR: 0x00000012  LBEG    : 0x40056f5c  LEND    : 0x40056f72  LCOUNT  : 0x00000000  

Backtrace: 0x42005f40:0x3fcf3b60 0x42005f57:0x3fcf3b90 0x42022bfc:0x3fcf3bb0
0x42005f40: lgfx::v1::LGFXBase::width() const at /home/ado/Projects/Espressif/lovyan-demo/components/LovyanGFX/src/lgfx/v1/LGFXBase.hpp:284
 (inlined by) void lgfx::v1::LGFXBase::fillScreen<int>(int const&) at /home/ado/Projects/Espressif/lovyan-demo/components/LovyanGFX/src/lgfx/v1/LGFXBase.hpp:272
 (inlined by) lovyanTest() at /home/ado/Projects/Espressif/lovyan-demo/main/main.cpp:13

0x42005f57: app_main at /home/ado/Projects/Espressif/lovyan-demo/main/main.cpp:17

0x42022bfc: main_task at /home/ado/Programs/Espressif/esp-idf-v4.4/components/freertos/port/port_common.c:141 (discriminator 1)
lovyan03 commented 2 months ago

why use Serial ?? you using Arduino ?? ESP-IDF ??

lovyan03 commented 2 months ago

https://github.com/lovyan03/LovyanGFX/blob/master/CMakeLists.txt#L36-L37

image

alba-ado commented 2 months ago

Yes, I was using Arduino as a component. It works now! Why is this required? I don't understand.

lovyan03 commented 2 months ago

There is an ifdef branch inside LovyanGFX depending on whether Arduino is used or not. In particular, the shape of the LGFX_Base type class changes depending on whether or not it inherits from Arduino's Printable. This can lead to memory address inconsistencies and crashes easily if the Arduino is not specified correctly.