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

Add support for TTGO T-Display #8

Closed
wants to merge 5 commits into from

Conversation

IhorNehrutsa
Copy link
Contributor

@IhorNehrutsa
Copy link
Contributor Author

The height and width are height=280, width=240 but not as expected height=240, width=135.
#ssd = SSD(spi, height=240, width=135, dc=pdc, cs=pcs, rst=prst) #, disp_mode=PORTRAIT | USD)
ssd = SSD(spi, height=(135+5)*2, width=240, dc=pdc, cs=pcs, rst=prst) #, disp_mode=PORTRAIT | USD)

There is an invisible area outside the screen.
Cyan and gray squares go off-screen.
The top-left corner has coordinates (41, 53).
ssd.fill_rect(0, 0, ssd.width//2, 41, CYAN) # Cyan square at top left
ssd.fill_rect(0, 0, 53, ssd.height//2, GREY) # Grey square at top left

P10421-152435

@peterhinch
Copy link
Owner

peterhinch commented Apr 21, 2021

It's hard for me to support hardware I don't possess. From the URL's you posted it should be capable of working, and your code shows you are setting about investigating this in the same way that I would. Further, given your progress so far I'm confident we can fix this.
[EDITED]
That said, height and width values should match the display hardware otherwise it will break things in CWriter and nano-gui.

It seems that the principal problem is an offset such that (0, 0) is off screen. This is common, and I was planning to support it, however the hardware I possess has zero offsets so I didn't progress this. I have posted an update with a final optional constructor arg offset=(0,0) which allows you to enter an (x, y) tuple. Here x and y (positive integers) represent the origin of a window in the device's internal RAM where the framebuf is to be rendered.

Determining these values is likely to be an iterative process unless you have someone else's code or docs to study. My simple test script helps with getting pixel-perfect values. I have had to do this with other displays.

To add a few general comments. Firstly, thank you for your excellent initial post which provided everything needed to offer support (see below). Secondly it's a great product to support and I'm keen to progress this and offer any help I can. Please keep me posted on progress.

Cheers, Pete

Beats "I installed this on a Chinese clone of the Scam Products Inc TotallyNackered V0.01 alpha and it doesn't work. Can u help?". I exaggerate only slightly...

@peterhinch
Copy link
Owner

Please see above edit.

@IhorNehrutsa
Copy link
Contributor Author

Hello, Peter.
Still unsuccessful with the offset parameter.
You mentioned a datasheet here

# mapping between chip RAM and LCD. Datasheet section 8.12 p124.

I can't find it.
Could you please give a link or datasheet.pdf
Thanks for your work.

@peterhinch
Copy link
Owner

I'm damned if I can find where I got it. I'm pretty sure it was from the Adafruit site but I can't find it there. I've posted it here.

@IhorNehrutsa
Copy link
Contributor Author

Thanks for the pdf.
Why here grayscale mode is used instead of framebuf.RGB565?
Is it faster?

mode = framebuf.GS4_HMSB # Use 4bit greyscale.

@peterhinch
Copy link
Owner

peterhinch commented Apr 22, 2021

The ST7789 driver can support displays up to 320x240. The driver was designed around the Adafruit 240x240 display. With RGB565 the buffer size would be 240x240x2 = 115200 bytes. This would be unusable on most platforms. By using a 4-bit greyscale buffer this reduces to 240x240/2 = 28800 bytes which is big but usable on Pyboards, Pico and ESP32 without SPIRAM.

The driver uses a lookup table (LUT) to map the 4-bit value onto a 12-bit color which is a mode that the hardware supports. The LUT is user configurable so the GUI can use a wide range of colors, but only from a palette of 16 user selectable colors. I chose 12-bit mode to minimise transfer time between the framebuf and the hardware.

This is a compromise: transfer time and color selection are offset against a big reduction in RAM usage (transfer time because of the real time mapping from the LUT),

When you use the offset values, what is the outcome? Does the image alter its position on screen? I'm trying to figure out if I made a mistake implementing it or whether the problem is something more subtle.

[EDIT] Answered my own question here.

I looked at the header file you posted. I suggest for a trial we set the window as the manufacturers do. This means commenting out lines 164 and 168 and entering

        self._wcd(b'\x2a', b'\x00\x00\x00\xe5')  # h says 239 (\xef) but it's actually 229
        self._wcd(b'\x2b', b'\x00\x00\x01\x3f')  # 319

[EDIT]
You'll need to set height=320 and width=230 (or 240) to match these values.

This is a hack but it may point to where we're going wrong. If this doesn't work, try changing the e5 to ef - it's possible that the header file comment is right and the code is wrong.

I'm puzzled, though. These values in the header seem to imply a zero offset which is where we started.

Tomorrow I'll study the header file in more detail to see if I can find any relevant issues.

@peterhinch
Copy link
Owner

I have now studied the TTGO header files in detail. In general they make sense. Many of the settings replicate the software reset defaults - I'm not sure why they explicitly set them to default values. They send one 0xb6 command which is not in the datasheet so I have no idea what that does. There are a couple of cases where their commands slightly alter voltages applied to the display. I've seen this kind of thing in other code - I'm not sure of the consequences but it is not going to cause your specific problem.

They do gamma setting. Adafruit don't, and nor do I. This is not necessary unless you are going to display images - which you're not with a 4-bit driver. Your colors look fine.

What continues to baffle me is their setting of the display window which assumes a 240x320 display. It seems inevitable that, with these settings, some content will be off-screen, which is what you're experiencing. Their Arduino code is complex and I really can't follow it. However I suspect that they later override the default window settings to match the physical display size.

I still therefore think that the problem should be fixable with the right display window settings. If the physical display is smaller than the chip's onboard RAM it must be mapped onto a subset of the RAM. The display window enables the driver to write just to that subset. Getting these settings right is tricky but should fix the problem.

I've ordered one of these from a local supplier so I will experiment. Until it arrives and I spend time with it I'm afraid I've run out of ideas.

@IhorNehrutsa
Copy link
Contributor Author

  1. I understand about the
    mode = framebuf.GS4_HMSB
    Thanks.

  2. Offset works right.
    Thanks again.

  3. You tell about the 12-bit mode, but here is set 16-bit mode.

    wcd(b'\x3a', b'\x55') # _COLMOD 16 bit/pixel, 64Kib color space

as described at
p224 9.1.32 COLMOD (3Ah): Interface Pixel Format
and it correspondent to
p105 8.8.42 Write data for 16-bit/pixel (RGB-5-6-5-bit input), 65K-Colors, 3Ah=”05h”

@peterhinch
Copy link
Owner

I'm sorry, I was thinking of another driver. It is 16-bit.

I received my board this morning and have produced color_setup_ttgo.py which is in the directory with the driver. This appears to be pixel-perfect and runs demos such as aclock and color15. It produces a landscape mode display. You'll need to update the driver as my handling of offset was wrong.

Hopefully you want a landscape mode display.

Currently only PORTRAIT mode works and produces a landscape display. I'll have to think that one through so there may be some changes.

@IhorNehrutsa
Copy link
Contributor Author

Thank you,
color_setup_ttgo.py is not pushed to Github.

Also, I found TTGO, which has offsets modes
https://github.com/jikegong/TTGO-Esp32-ST7789-Display-MicroPython.git

@peterhinch
Copy link
Owner

It is now. I don't know what happened to that git push - the driver update probably didn't happen either. Anyway, both look correct now.

That link will be very helpful. First thing I noticed is that in their "inverted landscape" mode they use the same offsets as me. I'll look at this further over the next day or two and hopefully produce an update that supports all possible rotations.

The next release of the docs will include a credit to you for your help with supporting this device - it is much appreciated!

@peterhinch
Copy link
Owner

I have pushed a new release which now supports all display modes.

There was a nasty bug in the driver which prevented portrait mode from working. I had not catered for the case where the physical width of a display was not divisible by 2. I have tested all modes on TTGO and Adafruit. Hopefully this has fixed all outstanding problems. The setup file has commented out code showing how to invoke each mode.

You'll observe that the setup file transposes PORTRAIT and LANDSCAPE constants. This is because the TTGO seems to be designed as a portrait mode display. My driver design assumed that asymmetrical displays would be landscape displays.

The relevant part of the driver doc has an acknowledgement of your work on this.

Please let me know if you find any issues or have any comments.

@LDmicroGitHub
Copy link

Hello, Peter.
Please move micropython-nano-gui\drivers\st7789\color_setup_ttgo.py
to micropython-nano-gui\color_setup\

Don't merge this pull request, but live it open, please.

@peterhinch
Copy link
Owner

I'm currently considering where the various color_setup files should go. It seems rather confusing to have them all in one directory, but I haven't thought of a satisfactory alternative. There are two variables, host microcontroller and display adaptor. In the case of TTGO there is currently a third variable - display type - but I hope to remove that.

I'm modifying the driver to handle the mapping of the various orientation options so that they work properly on any device. So if you choose PORTRAIT | USD you will get that on TTGO or Adafruit.

@LDmicroGitHub
Copy link

Little note.
LANDSCAPE constant is redundant.

if (mode & PORTRAIT) == 0:
     this is a LANDSCAPE
ssd = SSD(spi, height=135, width=240, dc=pdc, cs=pcs, rst=prst, offset=OFFSET)  # disp_mode==0==LANDSCAPE, 

# Portrait configs.
# Normal portrait display
ssd = SSD(spi, height=240, width=135, dc=pdc, cs=pcs, rst=prst, disp_mode=PORTRAIT, offset=OFFSET)  

@peterhinch
Copy link
Owner

I appreciate that LANDSCAPE is redundant, however I thought it might be clearer for some users to explicitly specify it. It's arguable either way.

I've moved the setup file as you requested but I'm still wondering how best to organise them.

The code now handles display_mode options correctly on TTGO and Adafruit. So if you specify PORTRAIT | USD that's what you'll get. You'll need to check your color_setup.py as the swapping of modes is now done in the driver and the offset constructor arg has an extra element.

Hopefully that is the end of the API changes.

@IhorNehrutsa
Copy link
Contributor Author

The offset[2] will confuse users. They will think
offset[0] - x
offset[1] - y
offset[2] - z ???
This is unclear.

disp_mode=0, init_spi=False, offset=(0, 0, 0)):

@IhorNehrutsa
Copy link
Contributor Author

IhorNehrutsa commented Apr 26, 2021

Sorry for my tediousness, and thank you for your patience.

The problem is that the PORTRAIT constant may be the portrait or landscape for different manufacturers in real world
(depending on the sequence of soldering the ST7789 and LEDs)

More correct name is
PORTRAIT = 0x20 # X-Y Exchange
REFLECT = 0x40 # X-Mirror
USD = 0x80 # Y-Mirror
see Page 125 in pdf

PORTRAIT is a name in an absolute coordinate system, X-Y Exchange is a name in a relative coordinate system

@peterhinch
Copy link
Owner

I agree with you about offset and have replaced the offset arg with display. This accepts an object defined in the driver which is either GENERIC (the default) or TDISPLAY. The user does not need to know the data type of this object. This gives me the option to change its content or its data type in the future without affecting the API. This might be to accommodate some new device. This change also simplifies color_setup.py and gets rid of the tuple of arbitrary numbers.

If a new device has a different connection (as you suggest above) I'll just need to create a new object for the display arg, ensuring that PORTRAIT, USD and REFLECT work correctly.

Regarding naming I think USD is much more descriptive than X-Mirror partly because it isn't visually a mirror image, and also because X is the long axis on a landscape display and the short one on a portrait. As you know the chip itself uses that terminology but my ageing brain finds it quite confusing :) Which is why I chose terminology which deliberately ignores X, Y, rows and columns:

Landscape: text runs parallel to the long axis.
Portrait: text parallel to short axis.
USD: Upside down, text is legible.
Reflect: You'll need a mirror to read it.
I find this easy to grasp. It would take a lot of persuasion to change my mind.

I've pushed an update - barring any bugs I think I'm happy with the API.

I do like the TTGO unit. It's nicely made and is surely the cheapest way to get a decent display on a MicroPython host.

@IhorNehrutsa
Copy link
Contributor Author

IhorNehrutsa commented Apr 26, 2021

"entities should not be multiplied without necessity" https://en.wikipedia.org/wiki/Occam%27s_razor
GENERIC and TDISPLAY are new entities and increase the level of code abstraction.
This is maybe good or maybe not.
Your will, the developer.
Thank you.

@IhorNehrutsa
Copy link
Contributor Author

IhorNehrutsa commented Apr 26, 2021

A little bit.
Please rename
micropython-nano-gui\color_setup\color_setup_ttgo.py
to
micropython-nano-gui\color_setup\ssd7789_ttgo.py

ssd7789_ttgo.py and ssd7789.py files will be located side by side in the browser.
image

There are two mentions in DRIVERS.md also.
Thank you.

@IhorNehrutsa
Copy link
Contributor Author

Hello, Peter.
It seems that I was in a hurry to close the PR.
Have you seen my last message about rename?

I think that the up-group level should be a display controller, and the next group level may be a display(or board) or manufacturer.

I have tested TTGO and I am glad.

@peterhinch
Copy link
Owner

peterhinch commented Apr 27, 2021

GENERIC and TDISPLAY are new entities and increase the level of code abstraction.

The added abstraction is the reason for doing it :) It allows me to change the data type without changing the API. Further, there is no reason for the user to think about numeric offset values: these are an aspect of the driver and should be encapsulated in that file. In my opinion, of course.
[EDIT]
I renamed the files, but called them st7789* rather than ssd7789* in line with the chip.

Thank you again for your work on this. It has been an enjoyable and fruitful collaboration. I'd be interested to know what application you plan for the TTGO.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants