The following is a grab bag of notes from an afternoon of playing around with a Elegoo 3.5” 480x320 SPI TFT touch screen
TL:DR
Skip these rubbish screens, they’re a massive pain to set up and have very poor refresh rates (sub 8 FPS)
If you’re a masochist and want to learn more, read on…
These little TFT screens use SPI and are controlled via the
fbtft
framebuffer driver that is part of
the linux kernel.
This article is written testing the screen on a Raspberry Pi 2 loaded with
Raspbian 2017-09-07-raspbian-stretch.zip
LCD-show
)The screen comes with a link to a prebuilt raspbian image setup for use,
however I wanted the latest Raspbian release and didn’t want to reflash my SD
card. The screen also provided a driver folder with a bunch of tar archives called
LCD-show-XXXXXX.tar.gz
and a README specifying the compatibility of each version
of LCD-show with Raspbian versions:
LCD-show version | Min Raspbian version | Max Raspbian version |
---|---|---|
Unmarked | ? | 2015-05-05 Wheezy |
150602 | ? | 2015-05-05 Wheezy |
151020 | 2015-05-05 Wheezy | 2015-09-24 Jessie |
160620 | 2016-05-10 Jessie | Latest (?) |
160701 | 2016-05-10 Jessie (?) | Latest (?) |
An additional version 160701
was provided but not documented, I assume this
is the latest and should be used with the latest version of Raspbian.
Un-tarring the latest LCD-show
yields a bunch of files:
.
├── boot
│ ├── config-28.txt
│ ├── config-32.txt
│ ├── config-35.txt
│ ├── config-397.txt
│ ├── config-43.txt
│ ├── config-5.txt
│ ├── config-7B-800x480.txt
│ ├── config-7C-1024x600.txt
│ └── config-nomal.txt
├── LCD28-show
├── LCD32-show
├── LCD35-show
├── LCD397-show
├── LCD43-show
├── LCD5-show
├── LCD7B-show
├── LCD7C-show
├── LCD-hdmi
├── ReadMe.txt
├── usr
│ ├── 99-calibration.conf-28
│ ├── 99-calibration.conf-32
│ ├── 99-calibration.conf-35
│ ├── 99-calibration.conf-397
│ ├── 99-calibration.conf-43
│ ├── 99-calibration.conf-5
│ ├── 99-fbturbo.conf
│ ├── 99-fbturbo.conf-HDMI
│ ├── cmdline.txt
│ ├── inittab
│ ├── modules-HDMI
│ ├── tft35a-overlay.dtb
│ └── tft9341-overlay.dtb
└── xinput-calibrator_0.7.5-1_armhf.deb
Here’s the README:
[version]
v1.1
[Driver installation]
Step1, Install Raspbian official mirror
1)Download Raspbian official mirror:https://www.raspberrypi.org/downloads/
2)Use SDFormatter.exe to Format your TF Card
3)Use Win32DiskImager.exe Burning mirror to TF Card
Step2, Install LCD Driver
1)Copy LCD-show-160701.tar.gz to the root directory of raspberry pi you can copy it directly to TF card after Step1, or use SFTP to remote copy
2)Landing Raspberry pi system to user command line (Name:pi,Password:raspberry) Execute the following command:
cd /boot
sudo tar zxvf LCD-show-160701.tar.gz
cd LCD-show/
#For 2.8inch RPI LCD excute:
sudo ./LCD28-show
# For 3.2inch RPI LCD excute:
sudo ./LCD32-show
# For 3.5inch RPI LCD excute:
sudo ./LCD35-show
# For 3.97inch RPI LCD excute:
sudo ./LCD397-show
# For 4.3inch RPI LCD excute:
sudo ./LCD43-show
# For 5inch RPI LCD excute:
sudo ./LCD5-show
# For 7inch(B)-800X480 RPI LCD excute:
sudo ./LCD7B-show
# For 7inch(C)-1024X600 RPI LCD excute:
sudo ./LCD7C-show
# If you need to switch back to the traditional HDMI display excute:
Sudo ./LCD-hdmi
3)Wait a few minutes,the system will restart automatically, enjoy with your LCD.
So what’s in these LCDXX-show
scripts? Here’s LCD35-show
for setting up a 3.5” TFT.
sudo mkdir /etc/X11/xorg.conf.d
sudo cp ./usr/tft35a-overlay.dtb /boot/overlays/
sudo cp ./usr/tft35a-overlay.dtb /boot/overlays/tft35a.dtbo
sudo cp -rf ./usr/99-calibration.conf-35 /etc/X11/xorg.conf.d/99-calibration.conf
sudo cp -rf ./usr/99-fbturbo.conf /usr/share/X11/xorg.conf.d/
sudo cp ./usr/cmdline.txt /boot/
sudo cp ./usr/inittab /etc/
sudo cp ./boot/config-35.txt /boot/config.txt
sudo reboot
Hooray, it’s a really shitty shell script! Got changes in your config.txt
?
Kiss goodbye to them! How about cmdline.txt
? Gone as well! Fuck you, puny
user.
Does it work? No. The script reboots the pi and comes up with a white screen.
What now? Googling LCDShow
brings up the LCDShow
repository, let’s try the latest (commit
e6fd2c6abb2195a2b08c856ddeee2ba4c07d7c1f). Hooray, success!
Refresh rate is appalling, as can be seen in the video below.
Let’s dig deeper and find out how it works.
Here’s the working version of LCD35-show
#!/bin/bash
sudo mkdir /etc/X11/xorg.conf.d
sudo cp ./usr/tft35a-overlay.dtb /boot/overlays/
sudo cp ./usr/tft35a-overlay.dtb /boot/overlays/tft35a.dtbo
sudo cp -rf ./usr/99-calibration.conf-35 /etc/X11/xorg.conf.d/99-calibration.conf
sudo cp -rf ./usr/99-fbturbo.conf /usr/share/X11/xorg.conf.d/
sudo cp ./usr/cmdline.txt /boot/
sudo cp ./usr/inittab /etc/
sudo cp ./boot/config-35.txt /boot/config.txt
nodeplatform=`uname -n`
kernel=`uname -r`
version=`uname -v`
if test "$nodeplatform" = "raspberrypi";then
echo "this is raspberrypi kernel"
version=${version%% *}
version=${version#*#}
echo $version
if test $version -lt 970;then
echo "reboot"
else
echo "need to update touch configuration"
if test $version -ge 1023;then
echo "install xserver-xorg-input-evdev_2.10.5-1"
sudo dpkg -i -B xserver-xorg-input-evdev_2.10.5-1_armhf.deb
else
echo "install xserver-xorg-input-evdev_1%3a2.10.3-1"
sudo dpkg -i -B xserver-xorg-input-evdev_1%3a2.10.3-1_armhf.deb
fi
sudo cp -rf /usr/share/X11/xorg.conf.d/10-evdev.conf /usr/share/X11/xorg.conf.d/45-evdev.conf
echo "reboot"
fi
else
echo "this is not raspberrypi kernel, no need to update touch configure, reboot"
fi
sudo reboot
We’ve got
inittab
config.txt
Let’s find out what they do.
First the overlays… What is an overlay? Googling round leads to the
overlays
directory in the Raspberry Pi firmware github repo which has a README
explaining that overlays are device tree overlays and have something to do
with loading modules, they supersede module loading from /etc/modules
. Here’s the device tree spiel:
Device Tree makes it possible to support many hardware configurations with a single kernel and without the need to explicitly load or blacklist kernel modules. Note that this isn’t a “pure” Device Tree configuration (c.f. MACH_BCM2835) - some on-board devices are still configured by the board support code, but the intention is to eventually reach that goal.
On Raspberry Pi, Device Tree usage is controlled from /boot/config.txt. By default, the Raspberry Pi kernel boots with device tree enabled. You can completely disable DT usage (for now) by adding:
device_tree=
to your config.txt which should cause your Pi to revert to the old way of doing things after a reboot.
Are overlays plain text? vi /boot/overlays/tft35a.dtbo
… nope. But they’ve
got a decent amount of ASCII in them: strings /boot/overlays/tft35a.dtbo
lists all the ASCII strings in the file, not hugely useful or informative.
Moving on.
Back to the Raspberry Pi docs:
Overlays are loaded using the “dtoverlay” directive. As an example, consider the popular lirc-rpi module, the Linux Infrared Remote Control driver. In the pre-DT world this would be loaded from /etc/modules, with an explicit “modprobe lirc-rpi” command, or programmatically by lircd. With DT enabled, this becomes a line in config.txt:
dtoverlay=lirc-rpi
. This causes the file /boot/overlays/lirc-rpi.dtbo to be loaded. By default it will use GPIOs 17 (out) and 18 (in), but this can be modified using DT parameters:dtoverlay=lirc-rpi,gpio_out_pin=17,gpio_in_pin=13
.Parameters always have default values, although in some cases (e.g. “w1-gpio”) it is necessary to provided multiple overlays in order to get the desired behaviour. See the list of overlays below for a description of the parameters and their defaults.
So what did LCD35-show
do to the config.txt
, what overlays did it enable?
Well if I’d had the old config.txt
before the script overwrote it I could
diff
it, sigh. How do I get the default config.txt
? Googling doesn’t
yield much–I’ll mount the boot partition from the original
2017-09-07-raspbian-stretch.img
that I wrote to the SD card:
$ fdisk -l 2017-09-07-raspbian-stretch.img
Disk 2017-09-07-raspbian-stretch.img: 4.6 GiB, 4916019200 bytes, 9601600 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x020c3677
Device Boot Start End Sectors Size Id Type
2017-09-07-raspbian-stretch.img1 8192 93814 85623 41.8M c W95 FAT32 (LBA)
2017-09-07-raspbian-stretch.img2 94208 9601599 9507392 4.5G 83 Linux
$ mkdir boot
# Offset is in sectors, each sector is 512 bytes.
$ sudo mount -v -o offset=$((8192 * 512)) -t vfat 2017-09-07-raspbian-stretch.img boot
$ ls boot/config.txt
-rwxr-xr-x 1 root root 1590 Sep 7 16:05 boot/config.txt*
Ta da! Here’s the original in case anyone needs it
# For more options and information see
# http://rpf.io/configtxt
# Some settings may impact device functionality. See link above for details
# uncomment if you get no picture on HDMI for a default "safe" mode
#hdmi_safe=1
# uncomment this if your display has a black border of unused pixels visible
# and your display can output without overscan
#disable_overscan=1
# uncomment the following to adjust overscan. Use positive numbers if console
# goes off screen, and negative if there is too much border
#overscan_left=16
#overscan_right=16
#overscan_top=16
#overscan_bottom=16
# uncomment to force a console size. By default it will be display's size minus
# overscan.
#framebuffer_width=1280
#framebuffer_height=720
# uncomment if hdmi display is not detected and composite is being output
#hdmi_force_hotplug=1
# uncomment to force a specific HDMI mode (this will force VGA)
#hdmi_group=1
#hdmi_mode=1
# uncomment to force a HDMI mode rather than DVI. This can make audio work in
# DMT (computer monitor) modes
#hdmi_drive=2
# uncomment to increase signal to HDMI, if you have interference, blanking, or
# no display
#config_hdmi_boost=4
# uncomment for composite PAL
#sdtv_mode=2
#uncomment to overclock the arm. 700 MHz is the default.
#arm_freq=800
# Uncomment some or all of these to enable the optional hardware interfaces
#dtparam=i2c_arm=on
#dtparam=i2s=on
#dtparam=spi=on
# Uncomment this to enable the lirc-rpi module
#dtoverlay=lirc-rpi
# Additional overlays and parameters are documented /boot/overlays/README
# Enable audio (loads snd_bcm2835)
dtparam=audio=on
Here’s the diff (/boot/config.txt
is the config from LCD-show
):
--- /home/pi/config.txt 2017-09-17 17:57:37.065563044 +0000
+++ /boot/config.txt 2017-09-17 17:20:58.000000000 +0000
@@ -1,5 +1,5 @@
# For more options and information see
-# http://rpf.io/configtxt
+# http://www.raspberrypi.org/documentation/configuration/config-txt.md
# Some settings may impact device functionality. See link above for details
# uncomment if you get no picture on HDMI for a default "safe" mode
@@ -22,7 +22,7 @@
#framebuffer_height=720
# uncomment if hdmi display is not detected and composite is being output
-#hdmi_force_hotplug=1
+hdmi_force_hotplug=1
# uncomment to force a specific HDMI mode (this will force VGA)
#hdmi_group=1
@@ -43,10 +43,10 @@
#arm_freq=800
# Uncomment some or all of these to enable the optional hardware interfaces
-#dtparam=i2c_arm=on
+dtparam=i2c_arm=on
#dtparam=i2s=on
-#dtparam=spi=on
-
+dtparam=spi=on
+enable_uart=1
# Uncomment this to enable the lirc-rpi module
#dtoverlay=lirc-rpi
@@ -54,3 +54,5 @@
# Enable audio (loads snd_bcm2835)
dtparam=audio=on
+dtoverlay=tft35a
+#dtoverlay=ads7846,cs=1,penirq=17,penirq_pull=2,speed=1000000,keep_vref_on=1,swapxy=1,pmax=255,xohms=60,xmin=200,xmax=3900,ymin=200,ymax=3900
In terms of functional changes we’ve only got these:
-#hdmi_force_hotplug=1
+hdmi_force_hotplug=1
...
-#dtparam=i2c_arm=on
+dtparam=i2c_arm=on
...
-#dtparam=spi=on
+dtparam=spi=on
...
+enable_uart=1
+dtoverlay=tft35a
tft35a
overlay addedNot sure why I2C is enabled seeing as this is a SPI screen, moving on… the
main changes are enabling SPI and adding the tft35a
overlay.
What about the cmdline.txt
, how has that changed?
Here’s the original (after adding linebreaks):
dwc_otg.lpm_enable=0
console=serial0,115200
console=tty1
root=PARTUUID=020c3677-02
rootfstype=ext4
elevator=deadline
fsck.repair=yes
rootwait
quiet
init=/usr/lib/raspi-config/init_resize.sh
splash
plymouth.ignore-serial-consoles
After line breaking here’s a diff with the replacement
--- cmdline.txt.orig 2017-09-17 18:05:48.081800539 +0000
+++ cmdline.txt 2017-09-17 18:06:41.511375876 +0000
@@ -1,12 +1,10 @@
dwc_otg.lpm_enable=0
-console=serial0,115200
console=tty1
-root=PARTUUID=020c3677-02
+console=ttyAMA0,115200
+root=/dev/mmcblk0p2
rootfstype=ext4
elevator=deadline
-fsck.repair=yes
rootwait
-quiet
-init=/usr/lib/raspi-config/init_resize.sh
-splash
-plymouth.ignore-serial-consoles
+fbcon=map:10
+fbcon=font:ProFont6x11
+logo.nologo
The following are removed:
fsck.repair
, why!?quiet
, fair enoughinit=/usr/lib/raspi-config/init_resize.sh
, perhaps this is removed after first boot anyway?splash
, if we’re not using quiet, makes sense to get rid of the splash screenThe main functional changes appear to be:
fbcon=map:10
fbcon=font:ProFont6x11
console=serial0,115200
-> console=ttyAMA0,1152001
What do the fbcon
lines do? Once again, the linux kernel
docs come to the
rescue. fbcon
stands for framebuffer console and is a console running on
top of a framebuffer device. What is a framebuffer
device? It’s literally an
abstraction of the underlying framebuffer (a memory buffer that holds the
current video frame). So what are we configuring here? map
specifies which
driver gets mapped to which console, the value is repeated until a string of 64
numbers is produced and the ttys starting from 1 ending at 64 are mapped onto
this string; no idea why this is necessary. The second option is self
explanatory, its simply setting the font of the framebuffer console.
It’s unclear what the console
line change is for. serial0
is represents the
first available serial device, lets try replacing ttyAMA0
with serial0
, on
the RPi 2 I don’t think it should make a difference. reboots, seems to work
fine, no difference. I think ttyAMA0
will break on a RPi 3 since ttyAMA0
will map to the bluetooth
UART.
Back to the device tree overlays. Let’s understand that tft35a.dtbo
file. How
does one make a device tree overlay? Hopefully that’ll help us understand what
its doing. Adafruit has a good tutorial on device overlays on the
beaglebone
Here’s what a device tree overlay sourcefile looks like:
/dts-v1/;
/plugin/;
/ {
compatible = "ti,beaglebone", "ti,beaglebone-black";
/* identification */
part-number = "ADAFRUIT-SPI0";
/* version */
version = "00A0";
fragment@0 {
target = <&am33xx_pinmux>;
__overlay__ {
spi0_pins_s0: spi0_pins_s0 {
pinctrl-single,pins = <
0x150 0x30 /* spi0_sclk, INPUT_PULLUP | MODE0 */
0x154 0x30 /* spi0_d0, INPUT_PULLUP | MODE0 */
0x158 0x10 /* spi0_d1, OUTPUT_PULLUP | MODE0 */
0x15c 0x10 /* spi0_cs0, OUTPUT_PULLUP | MODE0 */
>;
};
};
};
fragment@1 {
target = <&spi0>;
__overlay__ {
#address-cells = <1>;
#size-cells = <0>;
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&spi0_pins_s0>;
spidev@0 {
spi-max-frequency = <24000000>;
reg = <0>;
compatible = "spidev";
};
spidev@1 {
spi-max-frequency = <24000000>;
reg = <1>;
compatible = "spidev";
};
};
};
};
It is compiled with dtc
, turns out you can also decompile dtb
files back to
dts
(source overlay) files; cool! Let’s do that with ours.
$ dtc -O dts -o tft35a.dts -I dtb /boot/overlays/tft35a.d
tft35a.dts: Warning (unit_address_vs_reg): Node /fragment@0 has a unit name, but no reg property
tft35a.dts: Warning (unit_address_vs_reg): Node /fragment@0/__overlay__/spidev@0 has a unit name, but no reg property
tft35a.dts: Warning (unit_address_vs_reg): Node /fragment@0/__overlay__/spidev@1 has a unit name, but no reg property
tft35a.dts: Warning (unit_address_vs_reg): Node /fragment@1 has a unit name, but no reg property
tft35a.dts: Warning (unit_address_vs_reg): Node /fragment@2 has a unit name, but no reg property
Here it the overlay source (decompiled):
/dts-v1/;
/ {
compatible = "brcm,bcm2835", "brcm,bcm2708", "brcm,bcm2709";
fragment@0 {
target = <0xdeadbeef>;
__overlay__ {
status = "okay";
spidev@0 {
status = "disabled";
};
spidev@1 {
status = "disabled";
};
};
};
fragment@1 {
target = <0xdeadbeef>;
__overlay__ {
tft35a_pins {
brcm,pins = <0x11 0x19 0x18>;
brcm,function = <0x0 0x0 0x0>;
linux,phandle = <0x1>;
phandle = <0x1>;
};
};
};
fragment@2 {
target = <0xdeadbeef>;
__overlay__ {
#address-cells = <0x1>;
#size-cells = <0x0>;
tft35a@0 {
compatible = "ilitek,ili9486";
reg = <0x0>;
pinctrl-names = "default";
pinctrl-0 = <0x1>;
spi-max-frequency = <0xf42400>;
txbuflen = <0x8000>;
rotate = <0x5a>;
bgr = <0x0>;
fps = <0x1e>;
buswidth = <0x8>;
regwidth = <0x10>;
reset-gpios = <0xdeadbeef 0x19 0x0>;
dc-gpios = <0xdeadbeef 0x18 0x0>;
debug = <0x0>;
init = <0x10000b0 0x0 0x1000011 0x20000ff 0x100003a 0x55 0x1000036 0x28 0x10000c2 0x44 0x10000c5 0x0 0x0 0x0 0x0 0x10000e0 0xf 0x1f 0x1c 0xc 0xf 0x8 0x48 0x98 0x37 0xa 0x13 0x4 0x11 0xd 0x0 0x10000e1 0xf 0x32 0x2e 0xb 0xd 0x5 0x47 0x75 0x37 0x6 0x10 0x3 0x24 0x20 0x0 0x10000e2 0xf 0x32 0x2e 0xb 0xd 0x5 0x47 0x75 0x37 0x6 0x10 0x3 0x24 0x20 0x0 0x1000036 0x28 0x1000011 0x1000029>;
linux,phandle = <0x2>;
phandle = <0x2>;
};
tft35a-ts@1 {
compatible = "ti,ads7846";
reg = <0x1>;
spi-max-frequency = <0x1e8480>;
interrupts = <0x11 0x2>;
interrupt-parent = <0xdeadbeef>;
pendown-gpio = <0xdeadbeef 0x11 0x0>;
ti,x-plate-ohms = [00 3c];
ti,pressure-max = [00 ff];
linux,phandle = <0x3>;
phandle = <0x3>;
};
};
};
__overrides__ {
speed = <0x2 0x7370692d 0x6d61782d 0x66726571 0x75656e63 0x793a3000>;
txbuflen = [00 00 00 02 74 78 62 75 66 6c 65 6e 3a 30 00];
rotate = [00 00 00 02 72 6f 74 61 74 65 3a 30 00];
fps = [00 00 00 02 66 70 73 3a 30 00];
bgr = [00 00 00 02 62 67 72 3a 30 00];
debug = <0x2 0x64656275 0x673a3000>;
swapxy = <0x3 0x74692c73 0x7761702d 0x78793f00>;
};
__symbols__ {
tft35a_pins = "/fragment@1/__overlay__/tft35a_pins";
tft35a = "/fragment@2/__overlay__/tft35a@0";
tft35a_ts = "/fragment@2/__overlay__/tft35a-ts@1";
};
__fixups__ {
spi0 = "/fragment@0:target:0", "/fragment@2:target:0";
gpio = "/fragment@1:target:0", "/fragment@2/__overlay__/tft35a@0:reset-gpios:0", "/fragment@2/__overlay__/tft35a@0:dc-gpios:0", "/fragment@2/__overlay__/tft35a-ts@1:interrupt-parent:0", "/fragment@2/__overlay__/tft35a-ts@1:pendown-gpio:0";
};
__local_fixups__ {
fixup = "/fragment@2/__overlay__/tft35a@0:pinctrl-0:0", "/__overrides__:speed:0", "/__overrides__:txbuflen:0", "/__overrides__:rotate:0", "/__overrides__:fps:0", "/__overrides__:bgr:0", "/__overrides__:debug:0", "/__overrides__:swapxy:0";
};
};
OK, a little overwhelming, is there a real source file we can use? fbtft
have some for
other screens. It looks like this mostly defines properties of the LCD
and touch layer. Let’s step back up the stack.
Two files are added to /etc/X11/xorg.conf.d/
to configure the X11 display
server.
99-calibration.conf
99-fbturbo.conf
The touch layer configuration is fairly straight forward:
Section "InputClass"
Identifier "calibration"
MatchProduct "ADS7846 Touchscreen"
Option "Calibration" "3936 227 268 3880"
Option "SwapAxes" "1"
EndSection
It matches the ADS7846
device and pre-calibrates it whilst also
swapping X/Y axes (so X becomes Y and vica versa).
Section "Device"
Identifier "Allwinner A10/A13/A20 FBDEV"
Driver "fbturbo"
Option "fbdev" "/dev/fb1"
Option "SwapbuffersWait" "true"
EndSection
This Xorg config specifies the driver to use for the panel;
fbturbo
, the generally
preferred framebuffer driver (over
fbdev
.
fbturbo
is actually based off of fbdev
so acts as a strict superset, it
adds additional optimisations for ARM platforms, in the README it lists the raspberry pi:
Hardware accelerated window moving/scrolling on Raspberry Pi (using the BCM2835 DMA Controller)
Shame it doesn’t seem to be that quicker, maybe its the SPI interface bottlenecking the display updates?
The inittab
spawns a getty
terminal instance on ttyAMA0
at 115200 baud.
Can we speed things up and get a higher refresh rate?
Looks like some people are increasing the speed of the SPI clock:
# config.txt
dtoverlay=pitft28r,rotate=90,speed=80000000,fps=60
Let’s try adding speed=80000000
to the dtoverlay=tft35a
clause in
config.txt
… whitescreen of death. How about with fps=60
too?
Let’s have a look at that device tree overlay file, does it list any options
about fps
or speed
in it? These are the defaults we are trying to override.
Here’s the fragment of interest:
tft35a@0 {
compatible = "ilitek,ili9486";
reg = <0x0>;
pinctrl-names = "default";
pinctrl-0 = <0x1>;
spi-max-frequency = <0xf42400>;
txbuflen = <0x8000>;
rotate = <0x5a>;
bgr = <0x0>;
fps = <0x1e>;
buswidth = <0x8>;
regwidth = <0x10>;
reset-gpios = <0xdeadbeef 0x19 0x0>;
dc-gpios = <0xdeadbeef 0x18 0x0>;
debug = <0x0>;
init = <0x10000b0 0x0 0x1000011 0x20000ff 0x100003a 0x55 0x1000036 0x28 0x10000c2 0x44 0x10000c5 0x0 0x0 0x0 0x0 0x10000e0 0xf 0x1f 0x1c 0xc 0xf 0x8 0x48 0x98 0x37 0xa 0x13 0x4 0x11 0xd 0x0 0x10000e1 0xf 0x32 0x2e 0xb 0xd 0x5 0x47 0x75 0x37 0x6 0x10 0x3 0x24 0x20 0x0 0x10000e2 0xf 0x32 0x2e 0xb 0xd 0x5 0x47 0x75 0x37 0x6 0x10 0x3 0x24 0x20 0x0 0x1000036 0x28 0x1000011 0x1000029>;
linux,phandle = <0x2>;
phandle = <0x2>;
};
The device is an Ilitek ILI9486, cool, that’s good to know, turns out fbtft
supports this :) Hang on, we might already even be using it:
$ dmesg | grep -i tft
[ 25.610311] fbtft: module is from the staging directory, the quality is unknown, you have been warned.
[ 25.630096] fbtft_of_value: regwidth = 16
[ 25.630109] fbtft_of_value: buswidth = 8
[ 25.630119] fbtft_of_value: debug = 0
[ 25.630127] fbtft_of_value: rotate = 90
[ 25.630135] fbtft_of_value: fps = 60
[ 25.630142] fbtft_of_value: txbuflen = 32768
We can also list supported devices:
$ sudo modprobe fbtft_device name=list; dmesg | tail -30
[ 406.017439] fbtft_device: ili9481
[ 406.017443] fbtft_device: itdb24
[ 406.017446] fbtft_device: itdb28
[ 406.017449] fbtft_device: itdb28_spi
[ 406.017452] fbtft_device: mi0283qt-2
[ 406.017455] fbtft_device: mi0283qt-9a
[ 406.017458] fbtft_device: mi0283qt-v2
[ 406.017461] fbtft_device: nokia3310
[ 406.017464] fbtft_device: nokia3310a
[ 406.017467] fbtft_device: nokia5110
[ 406.017470] fbtft_device: piscreen
[ 406.017473] fbtft_device: pitft
[ 406.017476] fbtft_device: pioled
[ 406.017480] fbtft_device: rpi-display
[ 406.017483] fbtft_device: s6d02a1
[ 406.017486] fbtft_device: sainsmart18
[ 406.017489] fbtft_device: sainsmart32
[ 406.017492] fbtft_device: sainsmart32_fast
[ 406.017495] fbtft_device: sainsmart32_latched
[ 406.017499] fbtft_device: sainsmart32_spi
[ 406.017502] fbtft_device: spidev
[ 406.017505] fbtft_device: ssd1331
[ 406.017508] fbtft_device: tinylcd35
[ 406.017511] fbtft_device: tm022hdh26
[ 406.017514] fbtft_device: tontec35_9481
[ 406.017518] fbtft_device: tontec35_9486
[ 406.017521] fbtft_device: upd161704
[ 406.017524] fbtft_device: waveshare32b
[ 406.017527] fbtft_device: waveshare22
[ 406.017530] fbtft_device:
Closest to the seems to be ili9481
, not quite ILI9486
, googling the two
together yields an Aliexpress page selling the 9481 claiming comptability with
the 9486. Let’s try that driver then! First commenting out the dtoverlay
clause in config.txt
then rebooting to use modprobe
to load the driver.
$ sudo modprobe fbtft_device name=ili9481
The screen goes black, woohoo! That means the display has been initialised
(according to the fbtft docs). dmesg
output:
[ 37.977381] fbtft: module is from the staging directory, the quality is unknown, you have been warned.
[ 37.983798] fbtft_device: module is from the staging directory, the quality is unknown, you have been warned.
[ 37.985388] spidev spi0.0: spidev spi0.0 500kHz 8 bits mode=0x00
[ 37.985421] spidev spi0.1: spidev spi0.1 500kHz 8 bits mode=0x00
[ 37.985470] bcm2708_fb soc:fb: soc:fb id=-1 pdata? no
[ 37.985519] spidev spi0.0: Deleting spi0.0
[ 37.986706] fbtft_device: GPIOS used by 'ili9481':
[ 37.986719] fbtft_device: 'reset' = GPIO25
[ 37.986738] fbtft_device: 'dc' = GPIO24
[ 37.986744] fbtft_device: 'led' = GPIO22
[ 37.986765] spidev spi0.1: spidev spi0.1 500kHz 8 bits mode=0x00
[ 37.986779] spi spi0.0: fb_ili9481 spi0.0 32000kHz 8 bits mode=0x00
[ 38.012675] fb_ili9481: module is from the staging directory, the quality is unknown, you have been warned.
[ 38.324103] graphics fb1: fb_ili9481 frame buffer, 320x480, 300 KiB video memory, 4 KiB DMA buffer memory, fps=20, spi0.0 at 32 MHz
Trying to launch Xorg:
$ FRAMEBUFFER=/dev/fb1 startx
X.Org X Server 1.19.2
Release Date: 2017-03-02
X Protocol Version 11, Revision 0
Build Operating System: Linux 4.9.35-v7+ armv7l Raspbian
Current Operating System: Linux raspberrypi 4.9.41-v7+ #1023 SMP Tue Aug 8 16:00:15 BST 2017 armv7l
Kernel command line: bcm2708_fb.fbwidth=640 bcm2708_fb.fbheight=480 bcm2708_fb.fbswap=1 vc_mem.mem_base=0x3ec00000 vc_mem.mem_size=0x40000000 dwc_otg.lpm_enable=0 console=tty1 console=ttyAMA0,115200 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline rootwait fbcon=map:10 fbcon=font:ProFont6x11 logo.nologo
Build Date: 20 July 2017 09:07:00AM
xorg-server 2:1.19.2-1+deb9u1+rpi1 (https://www.debian.org/support)
Current version of pixman: 0.34.0
Before reporting problems, check http://wiki.x.org
to make sure that you have the latest version.
Markers: (--) probed, (**) from config file, (==) default setting,
(++) from command line, (!!) notice, (II) informational,
(WW) warning, (EE) error, (NI) not implemented, (??) unknown.
(==) Log file: "/home/pi/.local/share/xorg/Xorg.0.log", Time: Sun Sep 17 19:34:57 2017
(==) Using config directory: "/etc/X11/xorg.conf.d"
(==) Using system config directory "/usr/share/X11/xorg.conf.d"
(EE)
Fatal server error:
(EE) parse_vt_settings: Cannot open /dev/tty0 (Permission denied)
(EE)
(EE)
Please consult the The X.Org Foundation support
at http://wiki.x.org
for help.
(EE) Please also check the log file at "/home/pi/.local/share/xorg/Xorg.0.log" for additional information.
(EE)
(EE) Server terminated with error (1). Closing log file.
xinit: giving up
xinit: unable to connect to X server: Connection refused
xinit: server error
Couldn't get a file descriptor referring to the console
Not sure why it’s trying to open /dev/tty0, sudo FRAMEBUFFER=/dev/fb1 startx
does start an X11 session! The display is portrait and the colours look all
wrong, I think its a BGR/RGB ordering issue.
According to FBTFT: boot
console, fbcon=map:10
specifies that the console should be output to the display.
Let’s unload and reload the fbtft_device
module to try and fix the colour issue:
$ sudo modprobe -r fbtft_device && sudo modprobe --first-time fbtft_device name=ili9481 bgr=1
I can see part of a flashing cursor! Cool. sudo startx
caused the device to
hang, rebooting again.
Loading fbtft_device
with debug=1
this time to have a look at refresh rate timings
[ 168.718299] fbtft_device: module is from the staging directory, the quality is unknown, you have been warned.
[ 168.719783] spidev spi0.0: spidev spi0.0 500kHz 8 bits mode=0x00
[ 168.719805] spidev spi0.1: spidev spi0.1 500kHz 8 bits mode=0x00
[ 168.719846] bcm2708_fb soc:fb: soc:fb id=-1 pdata? no
[ 168.719890] spidev spi0.0: Deleting spi0.0
[ 168.730169] fbtft_device: GPIOS used by 'ili9481':
[ 168.730187] fbtft_device: 'reset' = GPIO25
[ 168.730194] fbtft_device: 'dc' = GPIO24
[ 168.730200] fbtft_device: 'led' = GPIO22
[ 168.730221] spidev spi0.1: spidev spi0.1 500kHz 8 bits mode=0x00
[ 168.730235] spi spi0.0: fb_ili9481 spi0.0 32000kHz 8 bits mode=0x00
[ 168.747999] fb_ili9481: module is from the staging directory, the quality is unknown, you have been warned.
[ 168.750410] fb_ili9481 spi0.0: fbtft_request_gpios: 'reset' = GPIO25
[ 168.750442] fb_ili9481 spi0.0: fbtft_request_gpios: 'dc' = GPIO24
[ 168.750466] fb_ili9481 spi0.0: fbtft_request_gpios: 'led' = GPIO22
[ 169.006923] graphics fb1: fb_ili9481 frame buffer, 320x480, 300 KiB video memory, 4 KiB DMA buffer memory, fps=20, spi0.0 at 32 MHz
Interesting, reset
and led
look incorrect, I think reset
should be 22
according to the pin definitions in the informational PDF on the CD. Not sure
which pin is LED though.
Starting an X11 session on vt1
caused the display to light up with a mirrored
terminal!
How do we mirror it so that it appears correctly?
$ sudo modprobe -r fbtft_device && sudo modprobe --first-time fbtft_device name=ili9481 debug=1 rotate=90 bgr=1
Produces something that looks reasonable
Oops, made it hang again, it doesn’t like repeated sudo startx
invocations.
Now to make it actually show an xserver rather than running one to show the
terminal! The fbtft
wiki suggest using
con2fbmap
to map a tty to a framebuffer. Trying fbcon2fbmap 1 1
didn’t
produce any results so I tried:
$ fb=1; for tty in {1..10}; do echo "$tty to fb $fb"; con2fbmap $tty $fb; sleep 1; done
1 to fb 1
2 to fb 1
3 to fb 1
4 to fb 1
5 to fb 1
6 to fb 1
7 to fb 1
8 to fb 1
9 to fb 1
10 to fb 1
$ fb=0; for tty in {1..10}; do echo "$tty to fb $fb"; con2fbmap $tty $fb; sleep 1; done
1 to fb 0
2 to fb 0
3 to fb 0
4 to fb 0
5 to fb 0
6 to fb 0
7 to fb 0
8 to fb 0
9 to fb 0
10 to fb 0
When con2fbmap 0 7
was run the blinking cursor on the screen disappeared
suggesting I was already displaying tty7 on /dev/fb1
. Plugging in a keyboard
and spamming the keys didn’t yield any characters on the display. I wonder if
the inittab
changes have messed things up?
Let’s try adding the fbtft_device
options to the /boot/config.txt
and see
how things go.
Found an excellent
post on
configuring these screens. Apparently fbtft
is being transitioned into the kernel
and lots has changed since then…
Not sure whether continuing down the fbtft
approach is worth it, the device
overlay must be using the linux kernel
driver
anyway… going to focus on trying to speed up the LCD now.
There’s a pitft35-resistive
overlay in /boot/overlays
that I thought was
worth a punt, but booted up with a white screen, probably a different
controller.
One of the helpful commands from the forum post was vcdbg log msg
to get the
videocore debug logs
Finally I’ve got debug logs in dmesg
by adding dtparam=debug=7
to config.txt
.
Here’s what the logs look like:
[ 44.554902] fb_ili9486 spi0.0: Display update: 1792 kB/s, fps=4
[ 44.756564] fb_ili9486 spi0.0: fbtft_update_display(start_line=304, end_line=319)
[ 44.756591] fb_ili9486 spi0.0: fbtft_write_reg16_bus8: 002a 0000 0000 0001 00df
[ 44.756610] fb_ili9486 spi0.0: fbtft_write_spi(len=2): 00 2a
[ 44.756678] fb_ili9486 spi0.0: fbtft_write_spi(len=8): 00 00 00 00 00 01 00 df
[ 44.756715] fb_ili9486 spi0.0: fbtft_write_reg16_bus8: 002b 0001 0030 0001 003f
[ 44.756729] fb_ili9486 spi0.0: fbtft_write_spi(len=2): 00 2b
[ 44.756756] fb_ili9486 spi0.0: fbtft_write_spi(len=8): 00 01 00 30 00 01 00 3f
[ 44.756786] fb_ili9486 spi0.0: fbtft_write_reg16_bus8: 002c
[ 44.756798] fb_ili9486 spi0.0: fbtft_write_spi(len=2): 00 2c
[ 44.756824] fb_ili9486 spi0.0: fbtft_write_vmem16_bus8(offset=291840, len=15360)
[ 44.756917] fb_ili9486 spi0.0: fbtft_write_spi(len=15360): 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ...
[ 44.764916] fb_ili9486 spi0.0: Display update: 1792 kB/s, fps=4
FPS=4!? That sounds bad, lets try overriding that in config.txt
with
fps=20
, nope still fps=4
in the logs.
Fiddling with the speed
param with fps=30
speed |
Result | Display update speed (kB/s) |
---|---|---|
16000000 | Colours correct, Tolerable speed | |
20000000 | Colours correct | 2032 |
24000000 | Colours correct | 2325 |
32000000 | Colour corruption, Good speed |
Adafruit
has a good post on the speed
parameter and overclocking
Conclusion: These SPI driven TFT screens suck, don’t bother with them as they all run far too slow to be useful (unless its the expensive Tontec one, might as well get an official 7” screen).
The screen ships with a little CD with some documentation on, frustratingly this is hard to find anywhere else on the internet, so here it is reproduced for my future benefit when I lose the CD/don’t have a CD drive to hand (seriously, who has CD drives in there laptop any more!?)
Pin | Symbol | Description |
---|---|---|
1, 17 | 3.3V |
+ Power rail |
2, 4 | 5V |
+ Power rail |
3, 5, 7, 8, 10, 12, 13, 15, 16 | NV |
NC |
6, 9, 14, 20, 25 | GND |
Ground |
11 | TP_IRQ |
Touch panel interrupt, pulled low during touch |
18 | LCD_RS |
LCD instruction control, Instruction/Data register selection |
19 | LCD_SI/TP_SI |
SPI data input of both LCD & touch panel |
21 | TP_S0 |
SPI data output of touch panel |
22 | RST |
Reset |
23 | LCD_SCK/TP_SCK |
SPI clock for both LCD & touch panel |
24 | LCD_CS |
LCD chip select (active low) |
26 | TP_CS |
Touch panel chip select (active low) |