I2S-parallel example: Drive a 64x32 display

boilerbots
Posts: 4
Joined: Thu Aug 10, 2017 7:22 am

Re: I2S-parallel example: Drive a 64x32 display

Postby boilerbots » Thu Jan 18, 2018 5:39 am

Vader_Mester wrote:My question is related to the parallel I2S driver in general.

How does the WS signal work in I2S parallel mode?
For example if I configure the bus to be 8bit wide, but the word length is 16bits, then the two half of the 16bit word will be clocked out with BCK, and WS will be 2BCK cycles long, with the high part (1BCK cycle) of the WS cycle signaling the 1st half and low part (1BCK cylce) for the 2nd, if I'm correct. (I know WS can be inverted).

By the way the above is what I want to achieve with the I2S, and reading the technical ref I'm pretty much confused on how BCK and WS are related in parallel and how flexible I2S is.

Generally I just want to achive a general half-duplex 8bit with parallel bus, with 16bit words, where 1 WS cycle is the length of some even number of BCK cycles (like 1WS can be 2, 4 8, etc BCK cycles long)... whichever is easier to set up.

It would be nice if someone gives some insight on how it works, cause you are the ones who know more about the I2S parrallel driver than anyone else.

Thanks in advance!
You can simplify this code as an example and examine he output with a simple scope or logic analyzer to see if it does what you want. There is a function that connects the internal data through the matrix to each output pin, that function has a flag that can invert the logic of any pin, you can inverter the WS clock if you wish.

For me it was easier to clean up the display driver and get a very nice result by just encoding the clock into the DMA data and forget the WS signal. To do it cleanly you need to stop clocking and latch the data and flip the row addressing. I am running a 64x32 panel with this technique and the output is very clean.

User avatar
Vader_Mester
Posts: 300
Joined: Tue Dec 05, 2017 8:28 pm
Location: Hungary
Contact:

Re: I2S-parallel example: Drive a 64x32 display

Postby Vader_Mester » Thu Jan 18, 2018 5:46 am

I realized soon enough that a logic analyzer is needed for further development.
We will see.

I had the same thought, it is actually very easy, to implement these signals into the DMA...
However It's not gonna work in my case since I wanna make this driver into a parallel LCD driver (MIPI-DBI Type B). In that case you occasionally have to read data from the LCD, so controlling the control signals through the function is a must.

Code: Select all

task_t coffeeTask()
{
	while(atWork){
		if(!xStreamBufferIsEmpty(mug)){
			coffeeDrink(mug);
		} else {
			xTaskCreate(sBrew, "brew", 9000, &mug, 1, NULL);
			xSemaphoreTake(sCoffeeRdy, portMAX_DELAY);
		}
	}
	vTaskDelete(NULL);
}

TheNitek
Posts: 2
Joined: Thu Jan 25, 2018 6:03 pm

Re: I2S-parallel example: Drive a 64x32 display

Postby TheNitek » Thu Jan 25, 2018 6:07 pm

Unfortunately the OSH project by BirdTechstep seems to be gone. Is the PCB available somewhere else?

cjsm74x
Posts: 12
Joined: Wed Nov 25, 2015 10:16 pm

Re: I2S-parallel example: Drive a 64x32 display

Postby cjsm74x » Sat Feb 03, 2018 9:42 pm

TheNitek wrote:Unfortunately the OSH project by BirdTechstep seems to be gone. Is the PCB available somewhere else?
It's not gone:
https://oshpark.com/profiles/BIRD-TEHCSTEP

themindfactory
Posts: 65
Joined: Mon Mar 26, 2018 7:57 pm

Re: I2S-parallel example: Drive a 64x32 display

Postby themindfactory » Mon Mar 26, 2018 7:58 pm

Can I2S0 be used instead of I2S1??

I have swapped and it seems not to work...

RichardS

embedded-creations
Posts: 13
Joined: Thu Feb 15, 2018 11:22 am

Re: I2S-parallel example: Drive a 64x32 display

Postby embedded-creations » Wed Mar 28, 2018 11:41 am

It looks like the code was only tested on I2S1. Search I2S_Parallel for

Code: Select all

//Because of... reasons... the 16-bit values for i2s1 appear on d8...d23
I2S0 seems to have the same issue. You can copy the code block that changes the base and apply it to I2S0 as well:

Code: Select all

    if (dev==&I2S0) {
        sig_clk=I2S0O_WS_OUT_IDX;
        if (cfg->bits==I2S_PARALLEL_BITS_32) {
            sig_data_base=I2S0O_DATA_OUT0_IDX;
        } else {
            //Because of... reasons... the 16-bit values for i2s0 appear on d8...d23
            sig_data_base=I2S0O_DATA_OUT8_IDX;
        }
    } else {
        if (cfg->bits==I2S_PARALLEL_BITS_32) {
            sig_data_base=I2S1O_DATA_OUT0_IDX;
        } else {
            //Because of... reasons... the 16-bit values for i2s1 appear on d8...d23
            sig_data_base=I2S1O_DATA_OUT8_IDX;
        }
        sig_clk=I2S1O_WS_OUT_IDX;
    }

embedded-creations
Posts: 13
Joined: Thu Feb 15, 2018 11:22 am

Re: I2S-parallel example: Drive a 64x32 display

Postby embedded-creations » Wed Mar 28, 2018 12:28 pm

Hi, my name's Louis, and I designed the SmartMatrix Shield for Teensy and wrote the SmartMatrix Library which was originally designed to make it easy to add a HUB75 matrix panel to a Teensy Arduino sketch. I've been wanting to add support for a platform with WiFi capability to the SmartMatrix Library for years, and was excited to find this forum thread and example code that makes it possible using the ESP32.

The way of refreshing the panels in Sprite_tm's example code makes a lot of sense for the ESP32's architecture, but highlights some problems with these HUB75 panels. After charging up a row by setting the address lines and then releasing that row by moving to a new row, the row drivers needs to be discharged through the column drivers and LEDs. This lights up pixels that may not have been intended to be lit on the current row. This shows up every time the address line changes, and the method of refreshing the panels in the example code is switching rows as frequently as possible, leading to the maximum number of ghost pixels showing up.

Here's a photo of the example code refreshing a panel with CLK set to 20MHz, and a test pattern displayed. The diagonal line maximizes the ghost pixels as each row has only a single unique column lit, forcing previous rows to discharge through a pixel that wasn't intended to be lit.
2018-03-28 12.47.59_preview copy.jpg
2018-03-28 12.47.59_preview copy.jpg (204.48 KiB) Viewed 48163 times
Here's some other references describing the issue and solutions.

http://fw.hardijzer.nl/?p=223
https://forums.adafruit.com/viewtopic.php?f=47&t=47243
https://www.maximintegrated.com/en/app- ... vp/id/4111

The best solution - that doesn't involve modifying the matrix panels themselves - seems to be to minimize the number of row switches when refreshing, so shift out all the color bits for a given row at once, before moving to the next row.

I modified the example code to do just that. Here's the same panel and test pattern refreshed with CLK set to 20MHz.
2018-03-28 12.49.06_preview.jpeg
2018-03-28 12.49.06_preview.jpeg (198.56 KiB) Viewed 48163 times
It looks much better, at a distance you may not even notice the ghost pixels. They are still there, but dimmer and limited to the row above each lit column. (There's another even dimmer green pixel lit below the row, but I think this is an unrelated bug I haven't yet tracked down)
2018-03-28 12.50.02_preview.jpeg
2018-03-28 12.50.02_preview.jpeg (128.1 KiB) Viewed 48163 times
This method of refreshing has some downsides. Because the binary time division is applied to each row, not each frame, it requires more linked list entries, using up more RAM. The simplest way of applying the technique would require 16x the RAM for the linked lists (32 rows / 2 pixels refreshed in parallel per row). I'm reducing the RAM required in two ways.

First, I sweep from color LSB to MSB, instead of a color bit at a time, which divides the RAM required in half.

Second, I added a new LSBMSB_TRANSITION_BIT definition, which defines the color bit that is refreshed once per frame, with the same brightness as the bits above it. Every color bit below it is refreshed only once, with fractional brightness. If LSBMSB_TRANSITION_BIT is 2 and brightness is 50%, color bit 1 will be refreshed only once with brightness 25%, and color bit 0 refreshed only once with brightness 12.5%. The color bits from LSBMSB_TRANSITION_BIT to the MSB are refreshed with normal brightness 1x, 2x, 4, … 2^n. This divides the RAM required for linked lists in half each time LSBMSB_TRANSITION_BIT increases by one. It also speeds up the refresh rate as there is less data to shift out per row. The cost is less resolution in brightness, decreased maximum brightness, and at dimmer brightness levels, some lower color bits will not be visible.

I made the above changes to the code, and cleaned some of it up (fewer magic numbers), and fixed a few bugs. The modified example code is up on GitHub. (If you want to change the CLK speed, you'll need to edit that in i2s_parallel.c, the code using clkspeed_hz is broken and I haven't tried fixing it yet.)

https://github.com/pixelmatix/esp32_I2s ... aLedMatrix

embedded-creations
Posts: 13
Joined: Thu Feb 15, 2018 11:22 am

Re: I2S-parallel example: Drive a 64x32 display

Postby embedded-creations » Fri Apr 20, 2018 8:57 am

I have the SmartMatrix Library ESP32 port far enough along that other people can start using it. I'm able to do up to 48-bit color refresh while keeping the refresh rate high. Here's a demo video showing refreshing a 64x32 panel with 36-bit color, >120Hz refresh (I think it's around 200Hz), and generating patterns using the FastLED Library, and playing Animated GIFs from a SD card.

Image
Full Video on YouTube

I'll be discussing the ESP32 port in more detail here:

https://community.pixelmatix.com/t/smar ... 2-port/272

You can find code, circuits, hardware files and BOMs, and a README with more info and a list of items still in progress at GitHub:

https://github.com/pixelmatix/SmartMatrix/tree/teensylc

embedded-creations
Posts: 13
Joined: Thu Feb 15, 2018 11:22 am

Re: I2S-parallel example: Drive a 64x32 display

Postby embedded-creations » Thu May 17, 2018 11:09 am

I got I2S Parallel working in 8-bit mode so the SmartMatrix Library can refresh the panel with I2S data stored in uint8_t instead of uint16_t, cutting the RAM needed to hold the refresh buffer in half. I made the modifications inside SmartMatrix Library and not Sprite_TM's I2S Parallel example, but for anyone that needs it, hopefully this GitHub commit gets you going in the right direction (it’s not well documented in the reference manual and what I have working seems like a bit of a hack!):

https://github.com/pixelmatix/SmartMatr ... 05546c0e78

BuddyCasino
Posts: 263
Joined: Sun Jun 19, 2016 12:00 am

Re: I2S-parallel example: Drive a 64x32 display

Postby BuddyCasino » Thu May 17, 2018 11:21 am

Props for figuring all this stuff out. Its appreciated.

Who is online

Users browsing this forum: No registered users and 9 guests