My PalmOS port is making progress on a desktop computer, with keyboard, mouse and a 1920×1080 monitor for display. While that was interesting, I wanted to try a different form factor, one that is closer to the original PalmOS experience. Up to now I have been calling it “PalmOS on x86”, but there is nothing that prevents it from running on different architectures, so I grabbed a brand new 4GB Raspberry PI 4B.
For the display I chose one of those small TFT SPI based touch screens. I picked a 3.5″ model supporting a resolution of 320×480, which is perfect for this use case since later Palm handhelds with virtual graffiti used exactly that resolution. Now it is just a matter of installing the display driver and call it quits, right ?
About that display… during the last couple weeks I found at least three different repositories containing more or less the same drivers. None seemed official, and none was found on the manufacturer site. All of them required installation of closed-source binaries and some would even overwrite critical files on the OS, like /boot/cmdline.txt, /boot/config.txt, /etc/rc.local and some others I can’t remember now. Eventually I settled on this one: https://github.com/goodtft/LCD-show ; but instead of running the installer, I manually copied just tft35a.dtbo (the device tree overlay binary I thought matched my display) onto /boot/overlays and added a line to /boot/config.txt:
The rotate=0 parameter means the display will be set to portrait mode: 320 columns by 480 rows, which is the correct aspect for Palm devices . I am not going to use the display as the primary framebuffer, so no need to mess with X.org, fbcp, etc.
After rebooting, to my surprise, the Pi console (tty1) appeared on the TFT display! I did not have a HDMI cable connected at the time, so I guess the display driver created a framebuffer and mapped it to /dev/fb0, which was picked by the kernel as the primary display.
Just to check, I attached a cable to the first HDMI port on the Pi and rebooted. This time /dev/fb0 was assigned to HDMI and /dev/fb1 to the TFT display. Anyway, now that I had a working framebuffer device it was just a matter of writing bytes at specific locations and see the results on the screen (Funny note: on Linux, hardware devices are mapped to files inside /dev, so that you can use standard I/O functions to interact with it. Framebuffers are essentially memory blocks storing pixels, so it is more natural to read and write from memory to access the device. So Linux maps memory to a file, just for us to map the file back to memory using the mmap() system call).
The display depth is 16-bits (5 red, 6 green, 5 blue). Every pixel requires 2 bytes for storage, so the whole screen requires 320x480x2 bytes which is a little over 300 KB. I was not sure if the SPI protocol would be fast enough for frequent screen updates. In the end I optimized screen access so that only areas that have changed were written to the display memory. A small change to the display management code made this possible:
The image is surprisingly sharp (it is better in person than in picture). Performance is good, but could be improved further. Right now I am testing the touch functionality. In the same way that /dev/fb1 is created for the display, a /dev/input/event1 device is created for the touch screen. You just need to open this device and read from it. Whenever a pen event is generated (touch or movement), bytes encoding the event are sent through the file descriptor. I found good information on event devices on this article. I will post an update when everything is properly integrated.