Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ESP32-S2] How do I use the I2S to control a 8-bit parallel LCD? (IDFGH-3242) #5238

Closed
csobrinho opened this issue May 4, 2020 · 17 comments
Closed
Labels
Awaiting Response awaiting a response from the author

Comments

@csobrinho
Copy link
Contributor

Environment

  • Development Kit: [ESP32-S2-SAOLA]
  • Kit version (for WroverKit/PicoKit/DevKitC): [??]
  • Module or chip used: [ESP32-S2]
  • IDF version (run git describe --tags to find it):
    // v4.2-dev-1126-gd85d3d969
  • Build System: [idf.py]
  • Compiler version (run xtensa-esp32-elf-gcc --version to find it):
    // xtensa-esp32s2-elf-gcc (crosstool-NG esp-2020r1) 8.2.0
  • Operating System: [macOS]
  • Using an IDE?: [Yes, CLion]
  • Power Supply: [USB]

Problem Description

I'm trying to use the I2S to control a RM68090 8-bit parallel LCD with the ESP32-S2 based on the existing code that I already have for the ESP32. My code is uses most of the esp-iot-solution lcd_common example with some custom additions/fixes.

I see the RS toggling but no WR toggling. If I try to use the FIFO the same happens.
This is my config:

.i2s_lcd_conf = {
        .data_width = 8,
        .data_io_num = {
                GPIO_NUM_2, GPIO_NUM_1, GPIO_NUM_8, GPIO_NUM_7,
                GPIO_NUM_6, GPIO_NUM_5, GPIO_NUM_4, GPIO_NUM_3,
        },
        .ws_io_num = GPIO_NUM_38,
        .rs_io_num = GPIO_NUM_45,
},
.i2s_port = I2S_NUM_0,

I've tried to search the docs but not a lot of info about LCD mode. From what I can see on Table 102: Peripherals and FIFO Address, the FIFO location changed:

#define I2S0_FIFO_ADD 0x6000f000

to

#define I2S0_FIFO_ADD 0x6000f004

I tested with and without this change and I still can't use the DMA and/or the FIFO and that's why I'm requesting some support. I remember reading on previous tickets [esp-iot-solution/issues/71] and [esp-iot-solution/issues/19] that 8 bit was rather cumbersome on the ESP32 but was improved for the ESP32-S2 (remember reading something about this on the forum).

Thank you!

@github-actions github-actions bot changed the title [ESP32-S2] How do I use the I2S to control a 8-bit parallel LCD? [ESP32-S2] How do I use the I2S to control a 8-bit parallel LCD? (IDFGH-3242) May 4, 2020
@koobest
Copy link
Contributor

koobest commented May 6, 2020

esp32-s2-parallel-master.tar.gz

Hi, There is no I2S LCD supports for ESP32-S2 chip in IOT-solution currently, but the example in the attachment is how to configure the I2S LCD mode for the ESP32-S2 chip.

@koobest
Copy link
Contributor

koobest commented May 12, 2020

Hi, @csobrinho
Have you tried the example code?

@csobrinho
Copy link
Contributor Author

csobrinho commented May 14, 2020 via email

@koobest
Copy link
Contributor

koobest commented May 14, 2020

Well, you may have misunderstood what I meant. I mean the compression package I sent you (esp32-s2-parallel-master.tar.gz) . Have you tried it?

@csobrinho
Copy link
Contributor Author

csobrinho commented May 19, 2020 via email

@csobrinho
Copy link
Contributor Author

Hi @koobest, I tried this adapted version but only saw the WS toggling, not the D0-D7 lines.

I adapted the current example for 8 bits instead of 24 but also tried the 24 bits version with same result.

Please find attached the modified version (some headers fixes and different data pins).
esp32-s2-parallel8-master.tar.gz

Here's the logic analyzer: logic analyzer

And the log output:

/* Hello World 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 "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "esp_system.h"
#include "i2s_parallel.h"

/*   I2S i2s_parallel interface configure part  */

#define  CLK  GPIO_NUM_38

#define  D0  GPIO_NUM_2
#define  D1  GPIO_NUM_1
#define  D2  GPIO_NUM_8
#define  D3  GPIO_NUM_7
#define  D4  GPIO_NUM_6
#define  D5  GPIO_NUM_5
#define  D6  GPIO_NUM_4
#define  D7  GPIO_NUM_3

#define  D8   GPIO_NUM_9
#define  D9   GPIO_NUM_10
#define  D10  GPIO_NUM_11
#define  D11  GPIO_NUM_12
#define  D12  GPIO_NUM_13
#define  D13  GPIO_NUM_14
#define  D14  GPIO_NUM_15
#define  D15  GPIO_NUM_16

#define  D16   GPIO_NUM_17
#define  D17   GPIO_NUM_18
#define  D18  GPIO_NUM_19
#define  D19  GPIO_NUM_20
#define  D20  GPIO_NUM_21
#define  D21  GPIO_NUM_26
#define  D22  GPIO_NUM_27
#define  D23  GPIO_NUM_28

i2s_parallel_pin_config_t i2s_parallel_bus = {
    .bit_width = 8,
    .pin_clk = CLK,
    .data = {D0, D1, D2, D3, D4, D5, D6, D7}, // D8, D9, D10, D11, D12, D13, D14, D15, D16, D17, D18, D19, D20, D21, D22, D23},
};

typedef struct {
    uint8_t bits_7_0;
    // uint8_t bits_15_8;
    // uint8_t bits_23_16;
} parallel_data_t;

parallel_data_t data[4096];

void app_main(void)
{
    printf("Hello world!\n");

    for (uint32_t x = 0; x < 4096; x++) {
        // data[x].bits_23_16 =  x;
        // data[x].bits_15_8 =   x;
        data[x].bits_7_0 =    x;
    }

    i2s_parallel_init(&i2s_parallel_bus, 4);// clk_fre = 40MHz / clk_div

    while (1) {
        i2s_parallel_write_data((uint8_t *)data, sizeof(parallel_data_t) * 4096);
        vTaskDelay(10 / portTICK_RATE_MS);
    }
}

And the log output:

-- Configuring done
-- Generating done
-- Build files have been written to: /Users/sobrinho/Desktop/esp32-s2-parallel8-master/build
Running ninja in directory /Users/sobrinho/Desktop/esp32-s2-parallel8-master/build
Executing "ninja app-flash"...
[915/924] Building C object esp-idf/i2s_parallel/CMakeFiles/__idf_i2s_parallel.dir/i2s_parallel.c.obj
../components/i2s_parallel/i2s_parallel.c: In function 'i2s_parallel_dma_write':
../components/i2s_parallel/i2s_parallel.c:136:26: warning: assignment to 'uint32_t' {aka 'volatile unsigned int'} from 'lldesc_t *' {aka 'struct lldesc_s *'} makes integer from pointer without a cast [-Wint-conversion]
         __dma[cnt].empty = &__dma[cnt+1];
                          ^
../components/i2s_parallel/i2s_parallel.c:142:24: warning: assignment to 'uint32_t' {aka 'volatile unsigned int'} from 'void *' makes integer from pointer without a cast [-Wint-conversion]
     __dma[cnt-1].empty = NULL;
                        ^
[923/924] Generating binary image from built executable
esptool.py v3.0-dev
Generated /Users/sobrinho/Desktop/esp32-s2-parallel8-master/build/parallel.bin
[923/924] cd /Users/sobrinho/esp/esp-idf/components/esptool_py && /User... -P /Users/sobrinho/esp/esp-idf/components/esptool_py/run_esptool.cmake
esptool.py --chip esp32s2 -p /dev/cu.SLAB_USBtoUART -b 3000000 --before=default_reset --after=hard_reset write_flash --flash_mode dio --flash_freq 40m --flash_size 2MB 0x10000 parallel.bin
esptool.py v3.0-dev
Serial port /dev/cu.SLAB_USBtoUART
Connecting....
Chip is ESP32-S2
Features: WiFi
Crystal is 40MHz
MAC: 7c:df:a1:00:39:b6
Uploading stub...
Running stub...
Stub running...
Changing baud rate to 3000000
Changed.
Configuring flash size...
Compressed 145472 bytes to 76475...
Writing at 0x00010000... (20 %)
Writing at 0x00014000... (40 %)
Writing at 0x00018000... (60 %)
Writing at 0x0001c000... (80 %)
Writing at 0x00020000... (100 %)
Wrote 145472 bytes (76475 compressed) at 0x00010000 in 0.7 seconds (effective 1594.0 kbit/s)...
Hash of data verified.

Leaving...
Hard resetting via RTS pin...
Done
ESP-ROM:esp32s2-rc4-20191025
Build:Oct 25 2019
rst:0x1 (POWERON),boot:0x8 (SPI_FAST_FLASH_BOOT)
SPIWP:0xee
mode:DIO, clock div:2
load:0x3ffe8100,len:0x4
load:0x3ffe8104,len:0x1814
load:0x40050000,len:0x14b0
load:0x40054000,len:0x210c
entry 0x400502d8
I (48) boot: ESP-IDF v4.2-dev-1575-ga3520970f 2nd stage bootloader
I (48) boot: compile time 20:21:11
I (48) boot: chip revision: 0
I (51) boot.esp32s2: SPI Speed      : 40MHz
I (56) boot.esp32s2: SPI Mode       : DIO
I (61) boot.esp32s2: SPI Flash Size : 2MB
I (66) boot: Enabling RNG early entropy source...
I (71) boot: Partition Table:
I (75) boot: ## Label            Usage          Type ST Offset   Length
I (82) boot:  0 nvs              WiFi data        01 02 00009000 00006000
I (89) boot:  1 phy_init         RF data          01 01 0000f000 00001000
I (97) boot:  2 factory          factory app      00 00 00010000 00100000
I (104) boot: End of partition table
I (109) esp_image: segment 0: paddr=0x00010020 vaddr=0x3f000020 size=0x04ffc ( 20476) map
I (123) esp_image: segment 1: paddr=0x00015024 vaddr=0x3ffbcd00 size=0x01eb8 (  7864) load
I (129) esp_image: segment 2: paddr=0x00016ee4 vaddr=0x40024000 size=0x00404 (  1028) load
0x40024000: _WindowOverflow4 at /Users/sobrinho/esp/esp-idf/components/freertos/xtensa/xtensa_vectors.S:1730

I (136) esp_image: segment 3: paddr=0x000172f0 vaddr=0x40024404 size=0x088f8 ( 35064) load
I (156) esp_image: segment 4: paddr=0x0001fbf0 vaddr=0x00000000 size=0x00428 (  1064)
I (156) esp_image: segment 5: paddr=0x00020020 vaddr=0x40080020 size=0x137f8 ( 79864) map
0x40080020: _stext at ??:?

I (191) boot: Loaded app from partition at offset 0x10000
I (191) boot: Disabling RNG early entropy source...
I (191) cache: Instruction cache 	: size 8KB, 4Ways, cache line size 32Byte
I (199) cpu_start: Pro cpu up.
I (203) cpu_start: Application information:
I (208) cpu_start: Project name:     parallel
I (213) cpu_start: App version:      1
I (217) cpu_start: Compile time:     May 19 2020 20:57:49
I (223) cpu_start: ELF file SHA256:  96cc017c371dbeb0...
I (229) cpu_start: ESP-IDF:          v4.2-dev-1575-ga3520970f
I (235) cpu_start: Single core mode
I (240) heap_init: Initializing. RAM available for dynamic allocation:
I (247) heap_init: At 3FFC0F48 len 0003B0B8 (236 KiB): DRAM
I (253) heap_init: At 3FFFC000 len 00003A10 (14 KiB): DRAM
I (259) cpu_start: Pro cpu start user code
I (319) spi_flash: detected chip: generic
I (319) spi_flash: flash io: dio
W (319) spi_flash: Detected size(4096k) larger than the size in the binary image header(2048k). Using the size in the binary image header.
I (330) cpu_start: Starting scheduler on PRO CPU.
Hello world!
--------I2S version  19052500, bit_width: 8

@koobest
Copy link
Contributor

koobest commented May 22, 2020

Hi, @csobrinho

Sorry, the GPIO configuration we gave is incorrect.

8bit mode: I2S0O_DATA_OUT16_IDX ~I2S0O_DATA_OUT23_IDX
16bit mode: I2S0O_DATA_OUT8_IDX ~I2S0O_DATA_OUT23_IDX
24bit mode: I2S0O_DATA_OUT0_IDX ~I2S0O_DATA_OUT23_IDX

please use this one:

static void i2s_parallel_set_pin(void)
{
    PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[i2s_parallel_bus.pin_clk], PIN_FUNC_GPIO);
    gpio_set_direction(i2s_parallel_bus.pin_clk, GPIO_MODE_OUTPUT);
    gpio_set_pull_mode(i2s_parallel_bus.pin_clk,GPIO_PULLUP_ONLY);
    gpio_matrix_out(i2s_parallel_bus.pin_clk, I2S0O_WS_OUT_IDX, true, 0);

    for(int i = 0; i < i2s_parallel_bus.bit_width; i++) {
        PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[i2s_parallel_bus.data[i]], PIN_FUNC_GPIO);
        gpio_set_direction(i2s_parallel_bus.data[i], GPIO_MODE_OUTPUT);
        gpio_set_pull_mode(i2s_parallel_bus.data[i],GPIO_PULLUP_ONLY);
        gpio_matrix_out(i2s_parallel_bus.data[i], I2S0O_DATA_OUT16_IDX + i, false, 0);
    }
} 

csobrinho pushed a commit to csobrinho/hydroponics that referenced this issue May 26, 2020
@koobest
Copy link
Contributor

koobest commented May 27, 2020

@csobrinho
Have you solved this issue?
thanks !!

@csobrinho
Copy link
Contributor Author

Yes, I believe so. It seems to be working but somehow the framerate doesn't seem that good. I'll capture some logic analyzer timings and post here.

One of the things I had to do was swap the low/high byte to make sure the order was good. Is there anything on the hardware that could do this?

My main code is here:
https://github.com/csobrinho/hydroponics/blob/master/components/i2s_parallel/i2s_parallel.c

I also added a new parameter (buffer policy) to the

static inline void i2s_parallel_dma_write(const uint8_t *buf, size_t length, buffer_policy_t policy)

So that you can reuse the same buffer. This is very useful when clearing a screen with a specific color. With this change, you can set a small buffer with the color and just setup the necessary lldesc_t structures until you reach the desirable length.

@koobest
Copy link
Contributor

koobest commented May 29, 2020

The hardware don't support this feature.

@Alvin1Zhang Alvin1Zhang added the Awaiting Response awaiting a response from the author label Jun 2, 2020
@csobrinho
Copy link
Contributor Author

csobrinho commented Jun 3, 2020

Sorry, haven't been able to capture the logic analyser timings yet. This week for sure!

On a different note, I saw in the ESP32-S2 datasheet that there is some magical support for serial 8080 LCD interface using the SPI 2.

Would this allow me to add a shift register to the SPI port and have WR (and optionally R/D and CS) to toggle and latch the output? That and FIFO (for small register writes) and DMA (for big fills) support would be a killer option to save some precious GPIOs.

Thanks for your help dealing with this, it has completely unblocked and helped me a lot!

@koobest
Copy link
Contributor

koobest commented Jun 3, 2020

In SPI 8080 LCD mode, the data bus is 8 bits wide. Therefore, we cannot control the WR signal through the shift register. But in the CMD phase and DATA phase, we can control the CD signal by setting or clearing the registers SPI2.misc.cd_cmd_set and SPI2.misc.cd_data_set

@csobrinho
Copy link
Contributor Author

Bummer. I was planning to convert the SPI serial data into parallel using a shift register but I need a good way to signal the 8 bit shift is done, latch the data and toggle WR. If I can't do this with the hardware I might need to add:
a) some sort of decade counter attached to the SPI clock, when 8 is reached, reset and toggle wr.
b) some sort of delay line attached to the SPI clk to toggle WR after some ns have passed and then auto reset itself.
c) some other alternative like an SPI LCD but I already have four displays here ready to use.

Any suggestions? Thanks!

@koobest
Copy link
Contributor

koobest commented Jun 3, 2020

Sorry, I don't understand why you need convert the SPI serial data into parallel.
. what is your LCD interface, SPI(serial) or 8080(parallel 8bit)?
can you provide your LCD datasheet?

@csobrinho
Copy link
Contributor Author

The LCD is parallel 8080 (RM68090) but I wanted to save some precious GPIOs so was thinking of using the SPI connected to a shift register to convert to parallel and connect the LCD D0-7 to the shift register. The problem is identifying when the 8th clock is shifted and trigger the WR.

@csobrinho
Copy link
Contributor Author

If I use the SPI at 40 MHz, I could (at most) shift about 32fps (40M / 8 / (2403202))

@Alvin1Zhang
Copy link
Collaborator

Thanks for reporting and updates. Feel free to reopen if the issue still happens. Thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Awaiting Response awaiting a response from the author
Projects
None yet
Development

No branches or pull requests

3 participants