juj / fbcp-ili9341

A blazing fast display driver for SPI-based LCD displays for Raspberry Pi A, B, 2, 3, 4 and Zero
MIT License
1.59k stars 265 forks source link

Pi zero wh and oled 128x128 #87

Closed ghost closed 3 years ago

ghost commented 5 years ago

Hello,

I need help for my project.

I try your framebuffer but I have some trouble.

I use a Raspberry Pi zero wh and a ssd1351 OLED 128x128

It work but sometimes the updated zone is shifted. And at the bottom something erase the line (cursor may be)

oled oled

This is my config

cmake -DCMAKE_VERBOSE_MAKEFILE=ON -DSSD1351=ON -DGPIO_TFT_DATA_CONTROL=24 -DGPIO_TFT_RESET_PIN=25 -DSPI_BUS_CLOCK_DIVISOR=12 -DSTATISTICS=OFF ..

This is the trace :

bcm_host_get_peripheral_address: 0x20000000, bcm_host_get_peripheral_size: 33554432, bcm_host_get_sdram_address: 0x40000000 BCM core speed: current: 400000000hz, max turbo: 400000000hz. SPI CDIV: 12, SPI max frequency: 33333333hz Allocated DMA channel 7 Allocated DMA channel 1 Enabling DMA channels Tx:7 and Rx:1 DMA hardware register file is at ptr: 0xb4b99000, using DMA TX channel: 7 and DMA RX channel: 1 DMA hardware TX channel register file is at ptr: 0xb4b99700, DMA RX channel register file is at ptr: 0xb4b99100 Resetting DMA channels for use DMA all set up Initializing display Resetting display at reset GPIO pin 25 InitSPI done Relevant source display area size with overscan cropped away: 128x128. Source GPU display is 128x128. Output SPI display is 128x128 with a drawable area of 128x128. Applying scaling factor horiz=1.00x & vert=1.00x, xOffset: 0, yOffset: 0, scaledWidth: 128, scaledHeight: 128 Creating dispmanX resource of size 128x128 (aspect ratio=1.000000). GPU grab rectangle is offset x=0,y=0, size w=128xh=128, aspect ratio=1.000000 All initialized, now running main loop...

This is my change in code :

`

define DISPLAY_NATIVE_WIDTH 128

define DISPLAY_NATIVE_HEIGHT 128

`

And the initialisation :

`
BEGIN_SPI_COMMUNICATION(); { SPI_TRANSFER(0xFD/Set Command Lock/, 0x12); SPI_TRANSFER(0xFD/Set Command Lock/, 0xB1); SPI_TRANSFER(0xAE/Sleep Mode On (Display OFF)/);

SPI_TRANSFER(0xB3/*Set Front Clock Divider/Oscillator Frequency*/, 0xF1/*Divide Ratio=1, Oscillator Frequency=0xF*/); // This controls frame rate -> set to fastest

//SAM SPI_TRANSFER(0xCA/*Set Multiplex Ratio*/, 95); // This effectively sets the pixel height of the display, set this to 127 for 128x128 OLED, and to 95 for 128x96 OLED. It looks like even the 128x96 OLED has 128x128 bytes worth of internal memory (for hardware scrolling?)
SPI_TRANSFER(0xCA/*Set Multiplex Ratio*/, 127); // This effectively sets the pixel height of the display, set this to 127 for 128x128 OLED, and to 95 for 128x96 OLED. It looks like even the 128x96 OLED has 128x128 bytes worth of internal memory (for hardware scrolling?)

//SAM SPI_TRANSFER(0xA0/*Set Remap*/, 0x34/*0x04=BGR<->RGB Swap | 0x10=Vertical swap | 0x20=Enable COM split odd even (this makes pixel addressing sane as one'd expect)*/);
SPI_TRANSFER(0xA0/*Set Remap*/, 0x74/*0x04=BGR<->RGB Swap | 0x10=Vertical swap | 0x20=Enable COM split odd even (this makes pixel addressing sane as one'd expect)*/);

SPI_TRANSFER(0xA1/*Set Display Start Line*/, 0);
//SPI_TRANSFER(0xA1/*Set Display Start Line*/, 128);

SPI_TRANSFER(0xA2/*Set Display Offset*/, 0);
SPI_TRANSFER(0xAB/*Set Function Select*/, 0x01/*16bpp colors*/);
SPI_TRANSFER(0xB5/*Set GPIO0 and GPIO1 pin*/, 0);
SPI_TRANSFER(0xC1/*Set Contrast Current for Color A,B,C*/, 0xC8, 0x80, 0xC8); // These three seem to be first for red, second for green and third for blue, 0x00-0xFF
SPI_TRANSFER(0xC7/*Master Contrast Current Control*/, 0x0F); // 0x0F=max contrast, smaller valuers=dimmer and less power consumption

// Some voltage settings from the spec sheet to try out, although power on defaults seem to work fine as well: SPI_TRANSFER(0xB1/Set Phase Length/, 0x32/Phase1=0, Phase2=4/); //SSD1351_CMD_PRECHARGE SPI_TRANSFER(0xBE/Set VCOMH Voltage/, 0x05); //SSD1351_CMD_VCOMH SPI_TRANSFER(0xB4/Set Segment Low Voltage/, 0xA0, 0xB5, 0x55); //SSD1351_CMD_SETVSL SPI_TRANSFER(0xB6/Set Second Precharge Period/, 0x01/1 DCLK/); //SSD1351_CMD_PRECHARGE2

SPI_TRANSFER(0xA6/*Set Display Normal*/);
SPI_TRANSFER(0xAF/*Sleep Mode OFF/Display ON*/);
ClearScreen();

} `

juj commented 5 years ago

Looks good, unfortunately nothing stands out that might be wrong.

If the bug might be something related to subrectangle diffing, you could try to diagnose/workaround by trying what happens if you enable one of the config items

https://github.com/juj/fbcp-ili9341/blob/0b46b04f1d05a5f5088e750bfde28b2099b8da7d/config.h#L102 or https://github.com/juj/fbcp-ili9341/blob/0b46b04f1d05a5f5088e750bfde28b2099b8da7d/config.h#L111 by removing the comments // in front of those configs. (Only remove one of them at a time, they are mutually exclusive)

ghost commented 5 years ago

You are right about the diff.

With #define UPDATE_FRAMES_IN_SINGLE_RECTANGULAR_DIFF I have the same trouble in one block.

With #define UPDATE_FRAMES_WITHOUT_DIFFING, I have no shifting, just the cursor

oled

I will make a simpler program, just a square changing color.

Could you give me some hint to debug it ?

juj commented 5 years ago

One way to debug is to uncomment https://github.com/juj/fbcp-ili9341/blob/0b46b04f1d05a5f5088e750bfde28b2099b8da7d/spi.cpp#L18 to dump all the written bytes to the display. The SET_CURSOR_X/Y and pixel write commands are defined at https://github.com/juj/fbcp-ili9341/blob/0b46b04f1d05a5f5088e750bfde28b2099b8da7d/ssd1351.h#L16-L18

Perhaps the coordinate locations to update get sent wrong or similar in the bad updates.

Google search suggests setterm --cursor off might hide the cursor.

ghost commented 5 years ago

Thanks for hints.

So I make a simple test. A square at pos 0,0, size 50x50 and it change of color every second.

I let the initialisation and then I launch my program.

This is the output for 4 changes :

75 117
00 0
7F 127
75 117
7F 127
7F 127
75 117
00 0
7F 127
75 117
0E 14
7F 127
75 117
7F 127
7F 127
75 117
00 0
7F 127
75 117
7F 127
7F 127
75 117
00 0
7F 127
75 117
7F 127
7F 127

I am surprised to not see any 0x15 command.

I have make the test with and without #define DISPLAY_WRITE_PIXELS_CMD_DOES_NOT_RESET_WRITE_CURSOR

I make another test, I start it after a reboot so the screen display the console

After initialisation, I have this, we can see the console refresh the screen (0,2) to (0,127)

75 117
02 2
7F 127
15 21
00 0
7F 127
75 117
7F 127
7F 127

I launch my test, we can see the DISPLAY_SET_CURSOR_X is sent only one time, and the DISPLAY_SET_CURSOR_Y is sent for each change. Is there a way to force the DISPLAY_SET_CURSOR_X ?

75 117
00 0
7F 127
75 117
7F 127
7F 127
75 117
00 0
7F 127
15 21
00 0
3F 63
75 117
7F 127
7F 127
75 117
00 0
7F 127
75 117
7F 127
7F 127
75 117
00 0
7F 127
75 117
7F 127
7F 127
75 117
00 0
7F 127
75 117
7F 127
7F 127
75 117
00 0
7F 127
75 117
7F 127
7F 127
juj commented 5 years ago

I am surprised to not see any 0x15 command.

Oh hmm, it looks like that debug #define needs DMA to be disabled, otherwise it will not print out the writes that go out via DMA (by default large writes go via DMA, and short writes via CPU, for fastest performance/best bus utilization). So re-run with DMA disabled to see the debug prints for pixel writes as well.

I launch my test, we can see the DISPLAY_SET_CURSOR_X is sent only one time, and the DISPLAY_SET_CURSOR_Y is sent for each change. Is there a way to force the DISPLAY_SET_CURSOR_X ?

The default driver behavior is to only update the cursors when needed, to avoid sending excessive bytes if the cursor coordinates do not change from one task to the next. fbcp-ili9341.cpp keeps track of the current write cursor position and write window that the display hardware should be at. It is possible that the bug is related to quirks/discrepancies between how the display hardware actually updates the cursor, and how fbcp-ili9341.cpp is tracking it. (That is what the different #defines DISPLAY_WRITE_PIXELS_CMD_DOES_NOT_RESET_WRITE_CURSOR and MUST_SEND_FULL_CURSOR_WINDOW are about - different displays behave subtly differently with respect to how the write cursor updates after commands)

To make fbcp-ili9341 drop tracking of the write cursors and windows altogether, but instead always before each task it would reset the window for the task (even if it thinks it would be redundant to do so), you can try something like the following:

diff --git a/fbcp-ili9341.cpp b/fbcp-ili9341.cpp
index a7d324f..8bb0b51 100644
--- a/fbcp-ili9341.cpp
+++ b/fbcp-ili9341.cpp
@@ -395,7 +395,10 @@ int main()
         ++i->size;
       }
 #endif
-      // Update the write cursor if needed
+      // Before each pixel write, update the write cursor to the subrectangle of the specified pixel write task
+      QUEUE_SET_WRITE_WINDOW_TASK(DISPLAY_SET_CURSOR_X, displayXOffset + i->x, displayXOffset + i->endX - 1);
+      QUEUE_SET_WRITE_WINDOW_TASK(DISPLAY_SET_CURSOR_Y, displayYOffset + i->y, displayYOffset + gpuFrameHeight - 1);
+/*
 #ifndef DISPLAY_WRITE_PIXELS_CMD_DOES_NOT_RESET_WRITE_CURSOR
       if (spiY != i->y)
 #endif
@@ -458,7 +461,7 @@ int main()
         }
 #endif
       }
-
+*/
       // Submit the span pixels
       SPITask *task = AllocTask(i->size*SPI_BYTESPERPIXEL);
       task->cmd = DISPLAY_WRITE_PIXELS;

(did not test this in practice, but should be something like that)

juj commented 3 years ago

Closing out old issues.