Shimering effect with SSD1963
I'm using this display with an ESP32 adapter board (available on the same page) which has an SSD1963Q9: https://www.aliexpress.us/item/3256805880921100.html
Working sample code.zip - In LCDWIKI_KBV.cpp see Set_1963_PWM at line 726.
(Removed old irrelevant comments)
The original issue here was largely missing backlight control and confusion from an "RS" pin mislabeled as "CD", but with the goal of this GH issue being a well-functioning display, there's still a shimmering effect that happens when using LGFX but not when using the attached sample code.
(One of the original issues with backlight control was weird since LGFX init for SSD1963 does include commands to set backlight, not sure why it didn't seem to be working. Maybe a larger compatibility issue with command set?)
~My LGFX_Device definition:~
New LGFX config here
hi,
cfg.freq_write = 200000;
200 KHz seems rather slow for a parallel display, have you tried in the MHz range ?
[edit] also LightPWM isn't configured, so no backlight
I set it to 20MHz to start with, and since I wasn't getting any output, I tried lowering it as one of the troubleshooting steps. In order to test output I had to manually and indirectly connect to a backlight pin which is normally not exposed to the ESP32
(The ESP32 connects to an adapter board which connects to the display via one of two FPC cables, the display has two FPC ports and I happened to have an FPC breakout which allowed me to manually apply 3V3 on the backlight pin for the display using the 2nd FPC port. However the FPC is raw 24-bit RGB, bypassing the SSD1963, so I can't use this long term)
The adapter board does not expose a backlight pin to the ESP32, and I didn't see a suitable alternative impl. for controlling backlight with SSD1963, so I left that commented out
The sample code I attached shows a command sent to SSD1963 for controlling backlight. Is there an alternative LGFX_Light impl. which can work with this?
sorry I didn't read your full post before replying :blush:
you can probably take the Set_1963_PWM() function from the vendor and implement it inside your LGFX custom class, this is untested code, and the function should be called after display.init():
class LGFX : public lgfx::LGFX_Device
{
// ... ( your existing config )
void Set_1963_PWM(uint8_t value)
{
uint8_t pwm=value*2.55;
_bus_instance.writeCommand(0xBE, 8);
_bus_instance.wait();
_bus_instance.writeData( 0xBE, 8 );
_bus_instance.writeData( 0x05, 8 );
_bus_instance.writeData( pwm, 8 );
_bus_instance.writeData( 0x01, 8 );
_bus_instance.writeData( 0xFF, 8 )
_bus_instance.writeData( 0x00, 8 )
_bus_instance.writeData( 0x00, 8 );
_bus_instance.wait();
}
}
if this solves the issue for you then we'll think about a way to include this in the driver without affecting the other SSD1963 variants
Sounds good, I'll give that a try later!
Do you happen to know anything about that cd pin that's referenced? I'm currently seeing shimmering in the output when using LGFX and I'm wondering if that's related. I don't see that behavior when using the sample code
(The pin is just used to digitalWrite(HIGH) in the LCDWIKI_KBV ctor, but it's also referenced with CD_COMMAND/CD_DATA macros from mcu_8bit_magic.h)
New LGFX config:
{ // バス制御の設定を行います。
auto cfg = _bus_instance.config(); // バス設定用の構造体を取得します。
// 8ビットパラレルバスの設定
// cfg.i2s_port = I2S_NUM_0; // 使用するI2Sポートを選択 (I2S_NUM_0 or I2S_NUM_1) (ESP32のI2S LCDモードを使用します)
cfg.freq_write = 12000000; // 送信クロック (最大20MHz, 80MHzを整数で割った値に丸められます)
cfg.pin_wr = DISPLAY_SSD1963_WR; // WR を接続しているピン番号
cfg.pin_rd = DISPLAY_SSD1963_RD; // RD を接続しているピン番号
cfg.pin_rs = DISPLAY_SSD1963_RS; // RS(D/C)を接続しているピン番号
cfg.pin_d0 = DISPLAY_SSD1963_D0; // D0を接続しているピン番号
cfg.pin_d1 = DISPLAY_SSD1963_D1; // D1を接続しているピン番号
cfg.pin_d2 = DISPLAY_SSD1963_D2; // D2を接続しているピン番号
cfg.pin_d3 = DISPLAY_SSD1963_D3; // D3を接続しているピン番号
cfg.pin_d4 = DISPLAY_SSD1963_D4; // D4を接続しているピン番号
cfg.pin_d5 = DISPLAY_SSD1963_D5; // D5を接続しているピン番号
cfg.pin_d6 = DISPLAY_SSD1963_D6; // D6を接続しているピン番号
cfg.pin_d7 = DISPLAY_SSD1963_D7; // D7を接続しているピン番号
//*/
_bus_instance.config(cfg); // 設定値をバスに反映します。
_panel_instance.setBus(&_bus_instance); // バスをパネルにセットします。
}
{ // 表示パネル制御の設定を行います。
auto cfg = _panel_instance.config(); // 表示パネル設定用の構造体を取得します。
cfg.pin_cs = DISPLAY_SSD1963_CS; // CSが接続されているピン番号 (-1 = disable)
cfg.pin_rst = DISPLAY_SSD1963_RST; // RSTが接続されているピン番号 (-1 = disable)
cfg.pin_busy = -1; // BUSYが接続されているピン番号 (-1 = disable)
// ※ 以下の設定値はパネル毎に一般的な初期値が設定されていますので、不明な項目はコメントアウトして試してみてください。
cfg.panel_width = 800; // 実際に表示可能な幅
cfg.panel_height = 480; // 実際に表示可能な高さ
cfg.offset_x = 0; // パネルのX方向オフセット量
cfg.offset_y = 0; // パネルのY方向オフセット量
cfg.offset_rotation = 0; // 回転方向の値のオフセット 0~7 (4~7は上下反転)
cfg.dummy_read_pixel = 8; // ピクセル読出し前のダミーリードのビット数
cfg.dummy_read_bits = 1; // ピクセル以外のデータ読出し前のダミーリードのビット数
cfg.readable = false; // データ読出しが可能な場合 trueに設定
cfg.invert = false; // パネルの明暗が反転してしまう場合 trueに設定
cfg.rgb_order = false; // パネルの赤と青が入れ替わってしまう場合 trueに設定
cfg.dlen_16bit = false; // 16bitパラレルやSPIでデータ長を16bit単位で送信するパネルの場合 trueに設定
cfg.bus_shared = false; // SDカードとバスを共有している場合 trueに設定(drawJpgFile等でバス制御を行います)
_panel_instance.config(cfg);
}
maybe _bus_instance.config().cfg.pin_ctrl[3] can be used for pulling CD high ? e.g. cfg.pin_ctrl = { DISPLAY_SSD1963_CD, -1, -1 }; ?
(The pin is just used to digitalWrite(HIGH) in the LCDWIKI_KBV ctor, but it's also referenced with CD_COMMAND/CD_DATA macros from mcu_8bit_magic.h)
apparently CD_COMMAND uses that DC pin too in LCDWIKI_KBV::reset(), but I don't know Panel_SSD1963 well enough to take a guess at how LGFX should handle it, maybe this needs a custom implementation of Bus_Parallel8 too
Oh whoops, looks like "CD" was a typo that made its way throughout the sample codebase:
#define CD_PIN 15 // Data Command control pin - must use a pin in the range 0-31
D/C i.e. RS pin is 15, which is mapped correctly
So it looks like all of the pins are assigned as expected, I'm just not sure why I'm getting shimmering
I tried comparing the init command bytes which are similar but different enough that I can't make a good comparison. I did notice differences in timing values, and changing xtal_clock affects the shimmering, so it's likely a timing issue
I have LGFX working great on the same 5" display noted by @tylercamp but have spent the last 6 hours pulling my hair out trying to get the backlight to work properly. I have the same sample code (In LCDWIKI_KBV.cpp see Set_1963_PWM at line 726) that properly adjust the backlight PWM, but for the life of me can not get it to work within LGFX,
The example function above provided by @tobozo seems to be close but does not work. I noticed a few things that I attempted to correct:
This line seems to be unnecessary: _bus_instance.writeData( 0xBE, 8 );
The writeCommand() should 16 bit length
There's a few missing ;
But nonetheless still no luck. I have subsequently dug deeper into the Dynamic Backlight Control and the SSD1963 datasheet. It seems that after setting the PWM value, you also have to turn on DBC. Here's the pseudo code from the datasheet:
The hardware pin, PWM is the output signal from SSD1963 to the system backlight driver. So it should configure PWM module before enable DBC. WRITE COMMAND “0xBE” WRITE DATA “0x0E” (set PWM frequency) WRITE DATA “0xFF” (dummy value if DBC is used) WRITE DATA “0x09” (enable PWM controlled by DBC) WRITE DATA “0xFF” WRITE DATA “0x00” WRITE DATA “0x00” WRITE COMMAND “0xD4” (Define the threshold value) WRITE DATA ..... WRITE COMMAND “0xD0” WRITE DATA “0x0D” (Enable DBC with Aggressive mode
Any suggestions on how to proceed?
edit: here is the datasheet, section 9.46 outlines the PWM functionality and section 9.63 outlines DBC https://pdf1.alldatasheet.com/datasheet-pdf/view/1179028/ETC2/SSD1963.html
@dekesone Could you provide your LGFX config?
@dekesone thanks for the reference, I found that I was missing this:
cfg.panel_width = cfg.memory_width = 800;
cfg.panel_height = cfg.memory_height = 480;
I thought I saw init of panel dimensions in the SSD1963 ctor so it seemed unnecessary, but this was the cause of the shimmering I saw.
For the PWM backlight:
I have subsequently dug deeper into the Dynamic Backlight Control and the SSD1963 datasheet. It seems that after setting the PWM value, you also have to turn on DBC. Here's the pseudo code from the datasheet: ...
The last two lines for enabling DBC are present in the SSD1963 fixed_cmds:
...
0xD0, 1, 0x0D, // <-- here (L133)
...
... so whatever the underlying backlight issue is, it doesn't seem like it's due to missing DBC init.
@dekesone Got it! The general commands were correct but there were underlying operations that were missing (e.g. toggling CS pin) since we were interacting with _bus_instance directly. I updated the commands to use the same functions as Panel_SSD1963::init and command_list:
void Set_1963_PWM(uint8_t value)
{
startWrite();
writeCommand16(0xBE);
_bus_instance.flush();
writeData( 0x05 );
writeData( value );
writeData( 0x01 );
writeData( 0xFF );
writeData( 0x00 );
writeData( 0x00 );
endWrite();
}
@tobozo Since this type of backlight control is specific to the particular Panel_LCD, I wonder if any of these minor refactors would be appropriate:
// additional class to be mixed in when appropriate and manually called by the user if needed
class IPanel_LCD_InteractiveBacklight {
// (1) panel is capable of creating an ILight instance which references this panel for commands
virtual ILight* createLight() = 0;
// ... or ...
// (2) panel directly exposes a `setBrightness`, and a new `Light_InteractivePanel` class
// would simply defer to this
virtual void setBrightness(...) = 0;
};
// ... or ...
class Panel_SSD1963 {
public:
// (3) panel defines its own `ILight` impl to be explicitly created by the user and assigned to `LGFX_Device`
class Light : ILight { ... };
};
// ... or ...
class Panel_Device {
// ...
// (4) all panels may directly offer a `ILight`; panels like SSD1963 will always have backlight control
//
// if no `ILight` was specifically assigned to `LGFX_Device`, then it would defer to
// the panel
virtual ILight* getLight() { return nullptr; };
// ...
};
(2) seems most in line with current LGFX patterns
EDIT: I'll make a new issue rather than re-scoping this for the third time
To summarize:
The shimmering effect I saw was due to incomplete display init. Assigning panel_width/height and memory_width/height fixed this problem.
We've figured out how to manage backlight control with LGFX and some customizations, but there should be an update to LGFX to properly support this case. #586
Thanks @tylercamp, great work !
@tylercamp thanks for breaking out the different suggested solutions :+1: , I have forwarded the new issue link to lovyan03 as I honestly don't know which one is better since this is beyond my skills
however he's been very busy lately so don't expect a lightning fast answer :wink:
@dekesone An update was pushed to develop branch by @lovyan03, are you able to test the update? I'm away from home and don't have a device to test with right now
@dekesone proper fix merged to 'develop' branch, call 'myLgfx->setBrightness' instead of 'myLgfx->light()->setBrightness' (which is apparently the preferred method in general but the examples get the light directly instead)