nkolban / esp32-snippets

Sample ESP32 snippets and code fragments
https://leanpub.com/kolban-ESP32
Apache License 2.0
2.37k stars 710 forks source link

SPI CLOCK error between ST7920 <-> ESP32 WROOM32 #1170

Open willianaraujo opened 1 year ago

willianaraujo commented 1 year ago

I'm using an ESP32 WROOM32 DEVKIT board, a 128x64 display with ST7920 chip. My development environment is ESP-IDF in VSCode. I created a project from scratch, added only the U8G2 library directly from the official repository on GitHub following the official documentation. I also added Kolban's component, the u8g2_esp32_hal to interface with the main u8g2 library.

The first time I used this combination of libraries, still last year, it worked perfectly.... after approximately 6 months without running the development environment, I returned to work on the project, reinstalled VSCode, ESP-IDF, U8G2, and U8G2_esp32_hal, all from the official repositories in their current versions. However, now my CPU started to restart repeatedly when I try to start SPI communication between the ESP32 and the ST7920. I even suspected the hardware, so I got a new DEVKIT board, a new display, and connected the display directly to the board, without any peripherals, just display-esp, and the board keeps resetting...

My boot log is as follows:

0x40080400: _init at ??:?

load:0x40080404,len:3876
entry 0x4008064c
.I (32) boot: ESP-IDF v5.1-dirty 2nd stage bootloader
I (33) boot: compile time Jul 21 2023 15:13:26
I (33) boot: Multicore bootloader
I (37) boot: chip revision: v3.1
I (41) boot.esp32: SPI Speed      : 40MHz
I (46) boot.esp32: SPI Mode       : DIO
I (50) boot.esp32: SPI Flash Size : 2MB
I (55) boot: Enabling RNG early entropy source...
I (60) boot: Partition Table:
I (64) boot: ## Label            Usage          Type ST Offset   Length
I (71) boot:  0 nvs              WiFi data        01 02 00009000 00006000
I (79) boot:  1 phy_init         RF data          01 01 0000f000 00001000
I (86) boot:  2 factory          factory app      00 00 00010000 00100000
I (94) boot: End of partition table
I (98) esp_image: segment 0: paddr=00010020 vaddr=3f400020 size=0b9d0h ( 47568) map
I (124) esp_image: segment 1: paddr=0001b9f8 vaddr=3ffb0000 size=0226ch (  8812) load
I (127) esp_image: segment 2: paddr=0001dc6c vaddr=40080000 size=023ach (  9132) load
I (133) esp_image: segment 3: paddr=00020020 vaddr=400d0020 size=17db0h ( 97712) map
I (173) esp_image: segment 4: paddr=00037dd8 vaddr=400823ac size=0b2c8h ( 45768) load
I (199) boot: Loaded app from partition at offset 0x10000
I (199) boot: Disabling RNG early entropy source...
I (211) cpu_start: Multicore app
I (211) cpu_start: Pro cpu up.
I (211) cpu_start: Starting app cpu, entry point is 0x4008119c
0x4008119c: call_start_cpu1 at /home/wka/esp/esp-idf/components/esp_system/port/cpu_start.c:154

I (198) cpu_start: App cpu up.
I (229) cpu_start: Pro cpu start user code
I (229) cpu_start: cpu freq: 160000000 Hz
I (229) cpu_start: Application information:
I (234) cpu_start: Project name:     ST7920
I (239) cpu_start: App version:      1
I (243) cpu_start: Compile time:     Jul 21 2023 15:12:36
I (249) cpu_start: ELF file SHA256:  71d26c1d383068f3...
I (255) cpu_start: ESP-IDF:          v5.1-dirty
I (261) cpu_start: Min chip rev:     v0.0
I (265) cpu_start: Max chip rev:     v3.99 
I (270) cpu_start: Chip rev:         v3.1
I (275) heap_init: Initializing. RAM available for dynamic allocation:
I (282) heap_init: At 3FFAE6E0 len 00001920 (6 KiB): DRAM
I (288) heap_init: At 3FFB2F18 len 0002D0E8 (180 KiB): DRAM
I (294) heap_init: At 3FFE0440 len 00003AE0 (14 KiB): D/IRAM
I (301) heap_init: At 3FFE4350 len 0001BCB0 (111 KiB): D/IRAM
I (307) heap_init: At 4008D674 len 0001298C (74 KiB): IRAM
I (315) spi_flash: detected chip: generic
I (318) spi_flash: flash io: dio
W (322) spi_flash: Detected size(4096k) larger than the size in the binary image header(2048k). Using the size in the binary image header.
I (336) app_start: Starting scheduler on CPU0
I (340) app_start: Starting scheduler on CPU1
I (340) main_task: Started on CPU0
I (350) main_task: Calling app_main()
I (350) gpio: GPIO[5]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 1| Intr:0 
I (360) gpio: GPIO[22]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 1| Intr:0 
E (370) esp_clk_tree: esp_clk_tree_src_get_freq_hz(21): unknown clk src
E (370) spi_master: spi_bus_add_device(368): invalid sclk speed
E (380) err: esp_err_t = 258

assert failed: u8g2_esp32_spi_byte_cb u8g2_esp32_hal.c:89 (0 && "spi_bus_add_device(HOST, &dev_config, &handle_spi)")

Backtrace: 0x400817be:0x3ffb4eb0 0x40086291:0x3ffb4ed0 0x4008c201:0x3ffb4ef0 0x400d5586:0x3ffb5010 0x400d5f10:0x3ffb5090 0x400d57fe:0x3ffb50b0 0x400d613d:0x3ffb50d0 0x400d621c:0x3ffb5100 0x400e62d9:0x3ffb5120 0x400d5421:0x3ffb5140 0x400e7620:0x3ffb5210 0x40088941:0x3ffb5240
0x400817be: panic_abort at /home/wka/esp/esp-idf/components/esp_system/panic.c:452

0x40086291: esp_system_abort at /home/wka/esp/esp-idf/components/esp_system/port/esp_system_chip.c:84

0x4008c201: __assert_func at /home/wka/esp/esp-idf/components/newlib/assert.c:81

0x400d5586: u8g2_esp32_spi_byte_cb at /home/wka/ST7920/components/u8g2-hal-esp-idf/src/u8g2_esp32_hal.c:89 (discriminator 5)

0x400d5f10: u8x8_cad_001 at /home/wka/ST7920/components/u8g2/csrc/u8x8_cad.c:355

0x400d57fe: u8x8_d_helper_display_init at /home/wka/ST7920/components/u8g2/csrc/u8x8_display.c:68

0x400d613d: u8x8_d_st7920_common at /home/wka/ST7920/components/u8g2/csrc/u8x8_d_st7920.c:94

0x400d621c: u8x8_d_st7920_128x64 at /home/wka/ST7920/components/u8g2/csrc/u8x8_d_st7920.c:326

0x400e62d9: u8x8_InitDisplay at /home/wka/ST7920/components/u8g2/csrc/u8x8_display.c:137

0x400d5421: app_main at /home/wka/ST7920/main/main.c:55

0x400e7620: main_task at /home/wka/esp/esp-idf/components/freertos/app_startup.c:208 (discriminator 13)

0x40088941: vPortTaskWrapper at /home/wka/esp/esp-idf/components/freertos/FreeRTOS-Kernel/portable/xtensa/port.c:162

ELF file SHA256: 71d26c1d383068f3

Rebooting...
ets Jul 29 2019 12:21:46

rst:0xc (SW_CPU_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:2
load:0x3fff0030,len:7084
ho 0 tail 12 room 4
load:0x40078000,len:15576
ho 0 tail 12 room 4
load:0x40080400,len:4
.0x40080400: _init at ??:?

The reason for the reset is specifically the lines below:

E (370) esp_clk_tree: esp_clk_tree_src_get_freq_hz(21): unknown clk src
E (370) spi_master: spi_bus_add_device(368): invalid sclk speed
E (380) err: esp_err_t = 258

assert failed: u8g2_esp32_spi_byte_cb u8g2_esp32_hal.c:89 (0 && "spi_bus_add_device(HOST, &dev_config, &handle_spi)")

I realized that the error is related to the speed of my SPI, but I remember not having changed anything in relation to the SPI speed the last time I was able to make everything work. And this time it also doesn't work, even with a clean project, with all the default settings.

I tried adjusting the clock speed to 1MHz, 10MHz, and 530KHz when invoking the spi_bus_add_device( function within the u8g2_esp32_hal component, but any speed I put the cyclic reset continues.

Below my complete main code:

#include <stdio.h>
#include <driver/gpio.h>
#include <driver/spi_master.h>
#include <esp_log.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <stdio.h>
#include <string.h>
#include <u8g2.h>

#include "sdkconfig.h"
#include "u8g2_esp32_hal.h"

// CLK - GPIO 18
#define PIN_CLK 18

// MOSI - GPIO 23
#define PIN_MOSI 23

// RESET - GPIO 22
#define PIN_RESET 22

// DC - GPIO -1
#define PIN_DC U8G2_ESP32_HAL_UNDEFINED

// CS - GPIO 05
#define PIN_CS 05
static char tag[] = "test_SST7920";
void app_main(void)
{
  u8g2_esp32_hal_t u8g2_esp32_hal = U8G2_ESP32_HAL_DEFAULT;
  u8g2_esp32_hal.bus.spi.clk = PIN_CLK;
  u8g2_esp32_hal.bus.spi.mosi = PIN_MOSI;
  u8g2_esp32_hal.bus.spi.cs = PIN_CS;
  u8g2_esp32_hal.dc = PIN_DC;
  u8g2_esp32_hal.reset = PIN_RESET;
  u8g2_esp32_hal_init(u8g2_esp32_hal);

  u8g2_t u8g2;  // a structure which will contain all the data for one display
    u8g2_Setup_st7920_128x64_f(
        &u8g2,
        U8G2_R0,
        u8g2_esp32_spi_byte_cb,
        u8g2_esp32_gpio_and_delay_cb);  // init u8g2 structure

    u8g2_esp32_hal_init(u8g2_esp32_hal);

  u8g2_InitDisplay(&u8g2);  // send init sequence to the display, display is in
                            // sleep mode after this,

  u8g2_SetPowerSave(&u8g2, 0);  // wake up display
  u8g2_ClearBuffer(&u8g2);
  u8g2_DrawBox(&u8g2, 10, 20, 20, 30);
  u8g2_SetFont(&u8g2, u8g2_font_ncenB14_tr);
  u8g2_DrawStr(&u8g2, 0, 15, "Hello World!");
  u8g2_SendBuffer(&u8g2);

  ESP_LOGD(tag, "All done!");
}

and below my code of the u8g2_esp32_spi_byte_cb() function of the u8g2_esp32_hal component, it is in this function that I tried those different speeds for the SPI clock:

uint8_t u8g2_esp32_spi_byte_cb(u8x8_t* u8x8,
                               uint8_t msg,
                               uint8_t arg_int,
                               void* arg_ptr) {
  ESP_LOGD(TAG, "spi_byte_cb: Received a msg: %d, arg_int: %d, arg_ptr: %p",
           msg, arg_int, arg_ptr);
  switch (msg) {
    case U8X8_MSG_BYTE_SET_DC:
      if (u8g2_esp32_hal.dc != U8G2_ESP32_HAL_UNDEFINED) {
        gpio_set_level(u8g2_esp32_hal.dc, arg_int);
      }
      break;

    case U8X8_MSG_BYTE_INIT: {
      if (u8g2_esp32_hal.bus.spi.clk == U8G2_ESP32_HAL_UNDEFINED ||
          u8g2_esp32_hal.bus.spi.mosi == U8G2_ESP32_HAL_UNDEFINED ||
          u8g2_esp32_hal.bus.spi.cs == U8G2_ESP32_HAL_UNDEFINED) {
        break;
      }

      spi_bus_config_t bus_config;
      memset(&bus_config, 0, sizeof(spi_bus_config_t));
      bus_config.sclk_io_num = u8g2_esp32_hal.bus.spi.clk;   // CLK
      bus_config.mosi_io_num = u8g2_esp32_hal.bus.spi.mosi;  // MOSI
      bus_config.miso_io_num = GPIO_NUM_NC;                  // MISO
      bus_config.quadwp_io_num = GPIO_NUM_NC;                // Not used
      bus_config.quadhd_io_num = GPIO_NUM_NC;                // Not used
      // ESP_LOGI(TAG, "... Initializing bus.");
      ESP_ERROR_CHECK(spi_bus_initialize(HOST, &bus_config, 1));

      spi_device_interface_config_t dev_config;
      dev_config.address_bits = 0;
      dev_config.command_bits = 0;
      dev_config.dummy_bits = 0;
      dev_config.mode = 0;
      dev_config.duty_cycle_pos = 0;
      dev_config.cs_ena_posttrans = 0;
      dev_config.cs_ena_pretrans = 0;
      dev_config.clock_speed_hz = 530000;
      dev_config.spics_io_num = u8g2_esp32_hal.bus.spi.cs;
      dev_config.flags = 0;
      dev_config.queue_size = 200;
      dev_config.pre_cb = NULL;
      dev_config.post_cb = NULL;
      // ESP_LOGI(TAG, "... Adding device bus.");
      ESP_ERROR_CHECK(spi_bus_add_device(HOST, &dev_config, &handle_spi));

      break;
    }

    case U8X8_MSG_BYTE_SEND: {
      spi_transaction_t trans_desc;
      trans_desc.addr = 0;
      trans_desc.cmd = 0;
      trans_desc.flags = 0;
      trans_desc.length = 8 * arg_int;  // Number of bits NOT number of bytes.
      trans_desc.rxlength = 0;
      trans_desc.tx_buffer = arg_ptr;
      trans_desc.rx_buffer = NULL;

      // ESP_LOGI(TAG, "... Transmitting %d bytes.", arg_int);
      ESP_ERROR_CHECK(spi_device_transmit(handle_spi, &trans_desc));
      break;
    }
  }
  return 0;
}  // u8g2_esp32_spi_byte_cb

Why can't I even run the example codes anymore, whereas last year everything worked perfectly, I even developed several applications with screens, menus, and animations on this same hardware...

willianaraujo commented 1 year ago

I kept looking for the issue and found the root cause, but I'm not exactly sure why the problem started happening. I believe it could be due to an update in ESP-IDF or FreeRTOS, someone with more experience in embedded systems might know...

Basically, in the u8g2_esp32_spi_byte_cb() function when filling out the fields of the dev_config variable to send to the spi_bus_add_device() function, none of the documentation I found instructed to pass the dev_config.clock_source field, but from what I saw in the functions that are called from here, this information is needed.

So, the error above was caused because the dev_config.clock_source field is not being filled in the u8g2_esp32_spi_byte_cb function before passing the dev_config as a parameter in the line ESP_ERROR_CHECK(spi_bus_add_device(HSPI_HOST, &dev_config, &handle_spi));

This way the initialization for the dev_config parameter would look like this:


          spi_device_interface_config_t dev_config;
          dev_config.address_bits     = 0;
          dev_config.command_bits     = 0;
          dev_config.dummy_bits       = 0;
          dev_config.mode             = 0;
          dev_config.duty_cycle_pos   = 0;
          dev_config.cs_ena_posttrans = 0;
          dev_config.cs_ena_pretrans  = 0;
          dev_config.clock_speed_hz   = 10000;
          dev_config.clock_source     = SPI_CLK_SRC_DEFAULT; //SOC_MOD_CLK_APB;
          dev_config.spics_io_num     = u8g2_esp32_hal.cs;
          dev_config.flags            = 0;
          dev_config.queue_size       = 200;
          dev_config.pre_cb           = NULL;
          dev_config.post_cb          = NULL;
          //ESP_LOGI(TAG, "... Adding device bus.");
          ESP_ERROR_CHECK(spi_bus_add_device(HSPI_HOST, &dev_config, &handle_spi));

That's it. Looking at the current ESP-IDF code, I can't understand how my previous code worked without this field, as browsing through previous versions of ESP-IDF on GitHub, this information was always mandatory.

I won't mark this thread as resolved since, as I mentioned, I don't understand why it worked before. Therefore, my solution might not be the correct one, or everything I said may not make sense, but as I'm behind schedule with my project, I won't delve further into this investigation.

Cheers!

William-Irvine commented 1 year ago

I have noticed this issue as well.

Using Kolban's HAL I get the same crash after updating from 4.2 to 5.1, but only when I have logging set to INFO or higher.

Otherwise I get the same crash.

Going to try the your solution now.

William-Irvine commented 1 year ago

I added the clock source and it built with logging set to none.

Was still flaky telling me the argument was invalid, so I put a 2 second delay before my call to "u8g2_Setup_st7920_128x64_f();" and now it seems to run perfectly.

I am wondering if it has to do with how fast the device is booting now, and thinking maybe its trying to set that clock source before the chip can initialize it?

Either way its interesting.

Thanks for the initial post, helped me a lot.

fherrera124 commented 4 months ago

the bug is in the line:

spi_device_interface_config_t dev_config;

in u8g2_esp32_hal.c. Members that are not set, have garbage values.

the solution is to use memset, or simply initialize the variable with:

spi_device_interface_config_t dev_config = {0};

that assures that all members of dev_config are intialized with 0; Then spi_bus_add_device() will detect that the member .clock_source is cero and set the value SPI_CLK_SRC_DEFAULT.