Getting the SPI display to work
Section titled Getting the SPI display to workFor my build I'm using a 2.0" 320x240 color IPS TFT Display with a ST7789 driver chip and 4-wire SPI communication. “IPS” (In-Plane Switching) promises beautiful, consistent colors with a large viewing angle, high contrast, and fast response time (according to some descriptions).
There are several solutions to display content on the TFT with Raspberry Pi. These are the ones I tried:
Method 1: Drawing images with Python
Section titled Method 1: Drawing images with PythonUsing the Python Imaging Library (PIL)
Back when I had received the TFT display, I had temporarily connected it to the SPI0 GPIOs of the Raspberry Pi for an initial test (wiring example here). I followed the setup instructions and sample codes to draw images with Python. The frame refresh rate seemed to be slow with this method.
Although it worked, I didn't like this method of defining shapes in Python or displaying static images. I wanted my TR-590 to play short animations in a loop (prepared as MP4 video files or similar), with the ability to switch between different clips at the touch of a button. So I looked for other options.
Method 2: Program “fbcp-ili9341”
Section titled Method 2: Program “fbcp-ili9341”TFT displays with up to 60 fps via SPI
fbcp-ili9341 is a great open source project, supporting different types of TFT driver chips (like ILI9341, ST7735, ST7789 and others). The short description is “A blazing fast display driver for SPI-based LCD displays for Raspberry Pi A, B, 2, 3, 4 and Zero”.
Although the display is connected via the SPI GPIOs, this program does not utilize the default SPI driver, neither the notro/fbtft framebuffer driver (both must be disabled – at least that was the case at the time I tested it). The functionality of the program is documented in great detail on Github:
https://github.com/juj/fbcp-ili9341
I followed the installation steps and tried several settings (described on Github) for my screen, like
cmake -DSPI_BUS_CLOCK_DIVISOR=8 -DST7789=ON -DGPIO-TFT-DATA-CONTROL=25 -DGPIO-TFT-RESET-PIN=24 -DUSE_DMA_TRANSFERS=OFF DSINGLE-CORE-BOARD=ON ..
... or ...
cmake -DSPI_BUS_CLOCK_DIVISOR=24 -DST7789=ON -DGPIO-TFT-DATA-CONTROL=25 -DGPIO-TFT-RESET-PIN=24 -DSINGLE-CORE-BOARD=ON -DDMAX-RX-CHANNEL=1 -DDMAX-TX-CHANEL=11 ..
Some tests worked, the TFT displayed the Raspi desktop with a high refresh rate and test videos played fine with VLC player (unfortunately, I did not make any further notes on the tests at the time).
But I had a problem: As already written, the default SPI driver must be disabled. This makes it impossible to use other SPI devices, like the TLC5947 breakout I wanted to use for LEDs.
I asked on Github about the compatibility with other SPI devices. Theoretically, using SPI could be possible. But the method by which fbcp-ili9341 operates can potentially interfere with the standard SPI driver. Testing or fixing this compatibility issue is way beyond my capabilities.
Method 3: “PiTFT helper” installer script
Section titled Method 3: “PiTFT helper” installer scriptExtending the Open Source script
I had ordered my display from Adafruit and while browsing their website and learning guides I noticed that they provide Open Source Raspberry Pi Installer Scripts for many of their products, including the TFT screens they sell as “PiTFT” (shields placed on a Raspberry Pi).
At the time of my test, the 2.0" 320x240 ST7789 display was not supported by the installation script adafruit-pitft.py. BUT they had just added support for two other displays with the ST7789 driver chip, with 240x240 and 240x135 resolution. This made me curious and I took a closer look at the script.
I duplicated the code snippets used for the 240x240 TFT and changed the variable names. I found the Sitronix ST7789 datasheet online and tried to understand the fb-init sequence, changed the MADCTL definition (avoid cropping), corrected gamma values and other settings...
After lots of debugging and testing, it finally worked and I proposed my solution on Github and asked if the 2.0" 320x240 ST7789 display could be included in the install script. Although they didn't adopt my changes 1:1, they did includ the TFT. The script now supports this display.
Below is the code of my version of the installer script. Note: This script is out of date now, do not use it for current projects. Just use the latest script from Adafruit as described below, their version is up to date.
#!/bin/bash
# (C) Adafruit Industries, Creative Commons 3.0 - Attribution Share Alike
#
# Instructions!
# cd ~
# wget https://raw.githubusercontent.com/adafruit/Raspberry-Pi-Installer-Scripts/master/adafruit-pitft.sh
# chmod +x adafruit-pitft.sh
# sudo ./adafruit-pitft.sh
if [ $(id -u) -ne 0 ]; then
echo "Installer must be run as root."
echo "Try 'sudo bash $0'"
exit 1
fi
UPDATE_DB=false
############################ CALIBRATIONS ############################
# For TSLib
POINTERCAL_28r0="4232 11 -879396 1 5786 -752768 65536"
POINTERCAL_28r90="33 -5782 21364572 4221 35 -1006432 65536"
POINTERCAL_28r180="-4273 61 16441290 4 -5772 21627524 65536"
POINTERCAL_28r270="-9 5786 -784608 -4302 19 16620508 65536"
POINTERCAL_35r0="5724 -6 -1330074 26 8427 -1034528 65536"
POINTERCAL_35r90="5 8425 -978304 -5747 61 22119468 65536"
POINTERCAL_35r180="-5682 -1 22069150 13 -8452 32437698 65536"
POINTERCAL_35r270="3 -8466 32440206 5703 -1 -1308696 65536"
POINTERCAL_28c="320 65536 0 -65536 0 15728640 65536"
# for PIXEL desktop
TRANSFORM_28r0="0.988809 -0.023645 0.060523 -0.028817 1.003935 0.034176 0 0 1"
TRANSFORM_28r90="0.014773 -1.132874 1.033662 1.118701 0.009656 -0.065273 0 0 1"
TRANSFORM_28r180="-1.115235 -0.010589 1.057967 -0.005964 -1.107968 1.025780 0 0 1"
TRANSFORM_28r270="-0.033192 1.126869 -0.014114 -1.115846 0.006580 1.050030 0 0 1"
TRANSFORM_35r0="-1.098388 0.003455 1.052099 0.005512 -1.093095 1.026309 0 0 1"
TRANSFORM_35r90="-0.000087 1.094214 -0.028826 -1.091711 -0.004364 1.057821 0 0 1"
TRANSFORM_35r180="1.102807 0.000030 -0.066352 0.001374 1.085417 -0.027208 0 0 1"
TRANSFORM_35r270="0.003893 -1.087542 1.025913 1.084281 0.008762 -0.060700 0 0 1"
TRANSFORM_28c0="-1 0 1 0 -1 1 0 0 1"
TRANSFORM_28c90="0 1 0 -1 0 1 0 0 1"
TRANSFORM_28c180="1 0 0 0 1 0 0 0 1"
TRANSFORM_28c270="0 -1 1 1 0 0 0 0 1"
ROTATE_28c0="rotate=0,touch-invx=true,touch-invy=true"
ROTATE_28c90="rotate=90,touch-swapxy=true,touch-invx=true"
ROTATE_28c180="rotate=180"
ROTATE_28c270="rotate=270,touch-swapxy=true,touch-invy=true"
MADCTL_st7789_240x32090="0x36,0x60,-1,0x37,0x00,0x00,-1"
MADCTL_st7789_240x320180="0x36,0x00,-1,0x37,0x00,0x00,-1"
MADCTL_st7789_240x320270="0x36,0xA0,-1,0x37,0x00,0x00,-1"
MADCTL_st7789_240x3200="0x36,0xC0,-1,0x37,0x00,0x00,-1"
MADCTL_st7789_240x2400="0x36,0x60,-1,0x37,0x00,0x00,-1"
MADCTL_st7789_240x24090="0x36,0x00,-1,0x37,0x00,0x00,-1"
MADCTL_st7789_240x240180="0x36,0xA0,-1,0x37,0x00,0x50,-1"
MADCTL_st7789_240x240270="0x36,0xC0,-1,0x37,0x00,0x50,-1"
warning() {
echo WARNING : $1
}
############################ Script assisters ############################
# Given a list of strings representing options, display each option
# preceded by a number (1 to N), display a prompt, check input until
# a valid number within the selection range is entered.
selectN() {
for ((i=1; i<=$#; i++)); do
echo $i. ${!i}
done
echo
REPLY=""
while :
do
echo -n "SELECT 1-$#: "
read
if [[ $REPLY -ge 1 ]] && [[ $REPLY -le $# ]]; then
return $REPLY
fi
done
}
function print_version() {
echo "Adafruit PiTFT Helper v2.1.0"
exit 1
}
function print_help() {
echo "Usage: $0 "
echo " -h Print this help"
echo " -v Print version information"
echo " -u [homedir] Specify path of primary user's home directory (defaults to /home/pi)"
exit 1
}
group=ADAFRUIT
function info() {
system="$1"
group="${system}"
shift
FG="1;32m"
BG="40m"
echo -e "[\033[${FG}\033[${BG}${system}\033[0m] $*"
}
function bail() {
FG="1;31m"
BG="40m"
echo -en "[\033[${FG}\033[${BG}${group}\033[0m] "
if [ -z "$1" ]; then
echo "Exiting due to error"
else
echo "Exiting due to error: $*"
fi
exit 1
}
function ask() {
# http://djm.me/ask
while true; do
if [ "${2:-}" = "Y" ]; then
prompt="Y/n"
default=Y
elif [ "${2:-}" = "N" ]; then
prompt="y/N"
default=N
else
prompt="y/n"
default=
fi
# Ask the question
read -p "$1 [$prompt] " REPLY
# Default?
if [ -z "$REPLY" ]; then
REPLY=$default
fi
# Check if the reply is valid
case "$REPLY" in
Y*|y*) return 0 ;;
N*|n*) return 1 ;;
esac
done
}
function has_repo() {
# Checks for the right raspbian repository
# http://mirrordirector.raspbian.org/raspbian/ stretch main contrib non-free rpi firmware
if [[ $(grep -h ^deb /etc/apt/sources.list /etc/apt/sources.list.d/* | grep "mirrordirector.raspbian.org") ]]; then
return 0
else
return 1
fi
}
progress() {
count=0
until [ $count -eq $1 ]; do
echo -n "..." && sleep 1
((count++))
done
echo
}
sysupdate() {
if ! $UPDATE_DB; then
# echo "Checking for correct software repositories..."
# has_repo || { warning "Missing Apt repo, please add deb http://mirrordirector.raspbian.org/raspbian/ stretch main contrib non-free rpi firmware to /etc/apt/sources.list.d/raspi.list" && exit 1; }
echo "Updating apt indexes..." && progress 3 &
sudo apt update 1> /dev/null || { warning "Apt failed to update indexes!" && exit 1; }
sudo apt-get update 1> /dev/null || { warning "Apt failed to update indexes!" && exit 1; }
echo "Reading package lists..."
progress 3 && UPDATE_DB=true
fi
}
# Given a filename, a regex pattern to match and a replacement string,
# perform replacement if found, else append replacement to end of file.
# (# $1 = filename, $2 = pattern to match, $3 = replacement)
reconfig() {
grep $2 $1 >/dev/null
if [ $? -eq 0 ]; then
# Pattern found; replace in file
sed -i "s/$2/$3/g" $1 >/dev/null
else
# Not found; append (silently)
echo $3 | sudo tee -a $1 >/dev/null
fi
}
############################ Sub-Scripts ############################
function softwareinstall() {
echo "Installing Pre-requisite Software...This may take a few minutes!"
apt-get install -y libts0 1> /dev/null 2>&1 || apt-get install -y tslib 1> /dev/null 2>&1 || { warning "Apt failed to install TSLIB!" && exit 1; }
apt-get install -y bc fbi git python-dev python-pip python-smbus python-spidev evtest libts-bin device-tree-compiler 1> /dev/null || { warning "Apt failed to install software!" && exit 1; }
pip install evdev 1> /dev/null || { warning "Pip failed to install software!" && exit 1; }
}
# update /boot/config.txt with appropriate values
function update_configtxt() {
if grep -q "adafruit-pitft-helper" "/boot/config.txt"; then
echo "Already have an adafruit-pitft-helper section in /boot/config.txt."
echo "Removing old section..."
cp /boot/config.txt /boot/configtxt.bak
sed -i -e "/^# --- added by adafruit-pitft-helper/,/^# --- end adafruit-pitft-helper/d" /boot/config.txt
fi
# remove any old flexfb/fbtft stuff
rm -f /etc/modprobe.d/fbtft.conf
sed -i 's/spi-bcm2835//g' "/etc/modules"
sed -i 's/flexfb//g' "/etc/modules"
sed -i 's/fbtft_device//g' "/etc/modules"
if [ "${pitfttype}" == "22" ]; then
overlay="dtoverlay=pitft22,rotate=${pitftrot},speed=64000000,fps=30"
fi
if [ "${pitfttype}" == "28r" ]; then
overlay="dtoverlay=pitft28-resistive,rotate=${pitftrot},speed=64000000,fps=30"
fi
if [ "${pitfttype}" == "28c" ]; then
rotateparams=$(eval echo "\$ROTATE_$pitfttype$pitftrot")
overlay=$(printf "dtoverlay=pitft28-capacitive,speed=64000000,fps=30\ndtoverlay=pitft28-capacitive,${rotateparams}")
fi
if [ "${pitfttype}" == "35r" ]; then
overlay="dtoverlay=pitft35-resistive,rotate=${pitftrot},speed=20000000,fps=20"
fi
if [ "${pitfttype}" == "st7789_240x320" ]; then
madctl=$(eval echo "\$MADCTL_$pitfttype$pitftrot")
if [ "${pitftrot}" == "90" ] || [ "${pitftrot}" == "270" ]; then
fbtftdevicerotate="rotate=90"
else
fbtftdevicerotate=""
fi
# -1,0x26,0x01 = gamma curve 2 (G2.2)
# -1,0x26,0x02 = gamma curve 2 (G1.8) = darker
# -1,0x26,0x04 = gamma curve 3 (G2.5) = lighter
cat >> /etc/modprobe.d/fbtft.conf <<EOF
# --- added by adafruit-pitft-helper $date ---
options fbtft_device name=flexfb gpios=dc:25,cs:8,led:12 speed=40000000 bgr=1 fps=60 $fbtftdevicerotate
options flexfb setaddrwin=0 width=240 height=320 init=-1,0x11,-2,120,-1,$madctl,0x3A,0x05,-1,0x26,0x04,-1,0xBA,0x01,-1,0xB2,0x0C,0x0C,0x00,0x33,0x33,-1,0xB7,0x35,-1,0xBB,0x1A,-1,0xC0,0x2C,-1,0xC2,0x01,-1,0xC3,0x0B,-1,0xC4,0x20,-1,0xC6,0x0F,-1,0xD0,0xA4,0xA1,-1,0x21,-1,0xE0,0x00,0x19,0x1E,0x0A,0x09,0x15,0x3D,0x44,0x51,0x12,0x03,0x00,0x3F,0x3F,-1,0xE1,0x00,0x18,0x1E,0x0A,0x09,0x25,0x3F,0x43,0x52,0x33,0x03,0x00,0x3F,0x3F,-1,0x29,-3
# --- end adafruit-pitft-helper $date ---
EOF
echo "spi-bcm2835" >> /etc/modules
echo "flexfb" >> /etc/modules
echo "fbtft_device" >> /etc/modules
overlay=""
fi
if [ "${pitfttype}" == "st7789_240x240" ]; then
madctl=$(eval echo "\$MADCTL_$pitfttype$pitftrot")
cat >> /etc/modprobe.d/fbtft.conf <<EOF
# --- added by adafruit-pitft-helper $date ---
options fbtft_device name=flexfb gpios=dc:25,cs:8,led:26 speed=40000000 bgr=1 fps=60
options flexfb setaddrwin=0 width=240 height=240 init=-1,0x11,-2,120,-1,$madctl,0x3A,0x05,-1,0xB2,0x0C,0x0C,0x00,0x33,0x33,-1,0xB7,0x35,-1,0xBB,0x1A,-1,0xC0,0x2C,-1,0xC2,0x01,-1,0xC3,0x0B,-1,0xC4,0x20,-1,0xC6,0x0F,-1,0xD0,0xA4,0xA1,-1,0x21,-1,0xE0,0x00,0x19,0x1E,0x0A,0x09,0x15,0x3D,0x44,0x51,0x12,0x03,0x00,0x3F,0x3F,-1,0xE1,0x00,0x18,0x1E,0x0A,0x09,0x25,0x3F,0x43,0x52,0x33,0x03,0x00,0x3F,0x3F,-1,0x29,-3
# --- end adafruit-pitft-helper $date ---
EOF
echo "spi-bcm2835" >> /etc/modules
echo "flexfb" >> /etc/modules
echo "fbtft_device" >> /etc/modules
overlay=""
fi
if [ "${pitfttype}" == "st7789_240x135" ]; then
dtc -@ -I dts -O dtb -o /boot/overlays/drm-minipitft114.dtbo overlays/minipitft114-overlay.dts
echo "############# UPGRADING KERNEL ###############"
sudo apt update || { warning "Apt failed to update itself!" && exit 1; }
sudo apt-get upgrade || { warning "Apt failed to install software!" && exit 1; }
apt-get install -y raspberrypi-kernel-headers 1> /dev/null || { warning "Apt failed to install software!" && exit 1; }
[ -d /lib/modules/$(uname -r)/build ] || { warning "Kernel was updated, please reboot now and re-run script!" && exit 1; }
cd st7789_module
make -C /lib/modules/$(uname -r)/build M=$(pwd) modules || { warning "Apt failed to compile ST7789V driver!" && exit 1; }
mv /lib/modules/$(uname -r)/kernel/drivers/gpu/drm/tinydrm/mi0283qt.ko /lib/modules/$(uname -r)/kernel/drivers/gpu/drm/tinydrm/mi0283qt.BACK
mv st7789v_ada.ko /lib/modules/$(uname -r)/kernel/drivers/gpu/drm/tinydrm/mi0283qt.ko
overlay="dtoverlay=drm-minipitft114,rotation=${pitftrot}"
fi
date=`date`
cat >> /boot/config.txt <<EOF
# --- added by adafruit-pitft-helper $date ---
dtparam=spi=on
dtparam=i2c1=on
dtparam=i2c_arm=on
$overlay
# --- end adafruit-pitft-helper $date ---
EOF
}
function update_udev() {
cat > /etc/udev/rules.d/95-touchmouse.rules <<EOF
SUBSYSTEM=="input", ATTRS{name}=="touchmouse", ENV{DEVNAME}=="*event*", SYMLINK+="input/touchscreen"
EOF
cat > /etc/udev/rules.d/95-ftcaptouch.rules <<EOF
SUBSYSTEM=="input", ATTRS{name}=="EP0110M09", ENV{DEVNAME}=="*event*", SYMLINK+="input/touchscreen"
EOF
cat > /etc/udev/rules.d/95-stmpe.rules <<EOF
SUBSYSTEM=="input", ATTRS{name}=="*stmpe*", ENV{DEVNAME}=="*event*", SYMLINK+="input/touchscreen"
EOF
}
function update_pointercal() {
if [ "${pitfttype}" == "28r" ] || [ "${pitfttype}" == "35r" ]; then
echo $(eval echo "\$POINTERCAL_$pitfttype$pitftrot") > /etc/pointercal
fi
if [ "${pitfttype}" == "28c" ]; then
echo $(eval echo "\$POINTERCAL_$pitfttype") > /etc/pointercal
fi
}
function install_console() {
echo "Set up main console turn on"
if ! grep -q 'fbcon=map:10 fbcon=font:VGA8x8' /boot/cmdline.txt; then
echo "Updating /boot/cmdline.txt"
sed -i 's/rootwait/rootwait fbcon=map:10 fbcon=font:VGA8x8/g' "/boot/cmdline.txt"
else
echo "/boot/cmdline.txt already updated"
fi
echo "Turning off console blanking"
# pre-stretch this is what you'd do:
if [ -e /etc/kbd/config ]; then
sed -i 's/BLANK_TIME=.*/BLANK_TIME=0/g' "/etc/kbd/config"
fi
# as of stretch....
# removing any old version
sed -i -e '/^# disable console blanking.*/d' /etc/rc.local
sed -i -e '/^sudo sh -c "TERM=linux setterm -blank.*/d' /etc/rc.local
sed -i -e "s|^exit 0|# disable console blanking on PiTFT\\nsudo sh -c \"TERM=linux setterm -blank 0 >/dev/tty0\"\\nexit 0|" /etc/rc.local
reconfig /etc/default/console-setup "^.*FONTFACE.*$" "FONTFACE=\"Terminus\""
reconfig /etc/default/console-setup "^.*FONTSIZE.*$" "FONTSIZE=\"6x12\""
echo "Setting raspi-config to boot to console w/o login..."
(cd "$target_homedir" && raspi-config nonint do_boot_behaviour B2)
# remove fbcp
sed -i -e "/^.*fbcp.*$/d" /etc/rc.local
}
function uninstall_console() {
echo "Removing console fbcon map from /boot/cmdline.txt"
sed -i 's/rootwait fbcon=map:10 fbcon=font:VGA8x8/rootwait/g' "/boot/cmdline.txt"
echo "Screen blanking time reset to 10 minutes"
if [ -e "/etc/kbd/config" ]; then
sed -i 's/BLANK_TIME=0/BLANK_TIME=10/g' "/etc/kbd/config"
fi
sed -i -e '/^# disable console blanking.*/d' /etc/rc.local
sed -i -e '/^sudo sh -c "TERM=linux.*/d' /etc/rc.local
}
function install_fbcp() {
echo "Installing cmake..."
apt-get --yes --allow-downgrades --allow-remove-essential --allow-change-held-packages install cmake 1> /dev/null || { warning "Apt failed to install software!" && exit 1; }
echo "Downloading rpi-fbcp..."
cd /tmp
#curl -sLO https://github.com/tasanakorn/rpi-fbcp/archive/master.zip
curl -sLO https://github.com/adafruit/rpi-fbcp/archive/master.zip
echo "Uncompressing rpi-fbcp..."
rm -rf /tmp/rpi-fbcp-master
unzip master.zip 1> /dev/null || { warning "Failed to uncompress fbcp!" && exit 1; }
cd rpi-fbcp-master
mkdir build
cd build
echo "Building rpi-fbcp..."
echo -e "\nset (CMAKE_C_FLAGS \"-std=gnu99 ${CMAKE_C_FLAGS}\")" >> ../CMakeLists.txt
cmake .. 1> /dev/null || { warning "Failed to cmake fbcp!" && exit 1; }
make 1> /dev/null || { warning "Failed to make fbcp!" && exit 1; }
echo "Installing rpi-fbcp..."
install fbcp /usr/local/bin/fbcp
cd ~
rm -rf /tmp/rpi-fbcp-master
# Start fbcp in the appropriate place, depending on init system:
if [ "$SYSTEMD" == "0" ]; then
# Add fbcp to /etc/rc.local:
echo "We have sysvinit, so add fbcp to /etc/rc.local..."
grep fbcp /etc/rc.local >/dev/null
if [ $? -eq 0 ]; then
# fbcp already in rc.local, but make sure correct:
sed -i "s|^.*fbcp.*$|/usr/local/bin/fbcp \&|g" /etc/rc.local >/dev/null
else
# Insert fbcp into rc.local before final 'exit 0':
sed -i "s|^exit 0|/usr/local/bin/fbcp \&\\nexit 0|g" /etc/rc.local >/dev/null
fi
else
# Install fbcp systemd unit, first making sure it's not in rc.local:
uninstall_fbcp_rclocal
echo "We have systemd, so install fbcp systemd unit..."
install_fbcp_unit || bail "Unable to install fbcp unit file"
sudo systemctl enable fbcp.service
fi
# if there's X11 installed...
if [ -e /etc/lightdm ]; then
echo "Setting raspi-config to boot to desktop w/o login..."
raspi-config nonint do_boot_behaviour B4
fi
# Disable overscan compensation (use full screen):
raspi-config nonint do_overscan 1
# Set up HDMI parameters:
echo "Configuring boot/config.txt for forced HDMI"
reconfig /boot/config.txt "^.*hdmi_force_hotplug.*$" "hdmi_force_hotplug=1"
reconfig /boot/config.txt "^.*hdmi_group.*$" "hdmi_group=2"
reconfig /boot/config.txt "^.*hdmi_mode.*$" "hdmi_mode=87"
# if there's X11 installed...
if [ -e /etc/lightdm ]; then
if [ "${pitfttype}" == "35r" ]; then
echo "Using x1.5 resolution"
SCALE=1.5
else
echo "Using x2 resolution"
SCALE=2.0
fi
else
echo "Using native resolution"
SCALE=1
fi
if [[ "${pitfttype}" == "st7789_240x320" && "${pitftrot}" == "180" ]] || [[ "${pitfttype}" == "st7789_240x320" && "${pitftrot}" == "0" ]]; then
# swap width/height for portrait display rotation when using st7789_240x320
WIDTH=`python -c "print(int(${HEIGHT_VALUES[PITFT_SELECT-1]} * ${SCALE}))"`
HEIGHT=`python -c "print(int(${WIDTH_VALUES[PITFT_SELECT-1]} * ${SCALE}))"`
else
WIDTH=`python -c "print(int(${WIDTH_VALUES[PITFT_SELECT-1]} * ${SCALE}))"`
HEIGHT=`python -c "print(int(${HEIGHT_VALUES[PITFT_SELECT-1]} * ${SCALE}))"`
fi
reconfig /boot/config.txt "^.*hdmi_cvt.*$" "hdmi_cvt=${WIDTH} ${HEIGHT} 60 1 0 0 0"
if [ "${pitfttype}" == "st7789_240x320" ]; then
# dont rotate HDMI when using st7789_240x320 (only display will be rotated)
reconfig /boot/config.txt "^.*display_hdmi_rotate.*$" ""
else
if [ "${pitftrot}" == "90" ] || [ "${pitftrot}" == "270" ]; then
# dont rotate HDMI on 90 or 270
reconfig /boot/config.txt "^.*display_hdmi_rotate.*$" ""
fi
if [ "${pitftrot}" == "0" ]; then
reconfig /boot/config.txt "^.*display_hdmi_rotate.*$" "display_hdmi_rotate=1"
# this is a hack but because we rotate HDMI we have to 'unrotate' the TFT!
pitftrot=90
update_configtxt || bail "Unable to update /boot/config.txt"
pitftrot=0
fi
if [ "${pitftrot}" == "180" ]; then
reconfig /boot/config.txt "^.*display_hdmi_rotate.*$" "display_hdmi_rotate=3"
this is a hack but because we rotate HDMI we have to 'unrotate' the TFT!
pitftrot=90
update_configtxt || bail "Unable to update /boot/config.txt"
pitftrot=180
fi
fi
}
function install_fbcp_unit() {
cat > /etc/systemd/system/fbcp.service <<EOF
[Unit]
Description=Framebuffer copy utility for PiTFT
After=network.target
[Service]
Type=simple
ExecStartPre=/bin/sleep 10
ExecStart=/usr/local/bin/fbcp
[Install]
WantedBy=multi-user.target
EOF
}
function uninstall_fbcp() {
uninstall_fbcp_rclocal
# Enable overscan compensation
raspi-config nonint do_overscan 0
# Set up HDMI parameters:
echo "Configuring boot/config.txt for default HDMI"
reconfig /boot/config.txt "^.*hdmi_force_hotplug.*$" "hdmi_force_hotplug=0"
sed -i -e '/^hdmi_group=2.*$/d' /boot/config.txt
sed -i -e '/^hdmi_mode=87.*$/d' /boot/config.txt
sed -i -e '/^hdmi_cvt=.*$/d' /boot/config.txt
}
function uninstall_fbcp_rclocal() {
# Remove fbcp from /etc/rc.local:
echo "Remove fbcp from /etc/rc.local, if it's there..."
sed -i -e '/^.*fbcp.*$/d' /etc/rc.local
}
function update_xorg() {
if [ "${pitfttype}" == "28r" ] || [ "${pitfttype}" == "35r" ]; then
matrix=$(eval echo "\$TRANSFORM_$pitfttype$pitftrot")
transform="Option \"TransformationMatrix\" \"${matrix}\""
cat > /usr/share/X11/xorg.conf.d/20-calibration.conf <<EOF
Section "InputClass"
Identifier "STMPE Touchscreen Calibration"
MatchProduct "stmpe"
MatchDevicePath "/dev/input/event*"
Driver "libinput"
${transform}
EndSection
EOF
fi
if [ "${pitfttype}" == "28c" ]; then
matrix=$(eval echo "\$TRANSFORM_$pitfttype$pitftrot")
transform="Option \"TransformationMatrix\" \"${matrix}\""
cat > /usr/share/X11/xorg.conf.d/20-calibration.conf <<EOF
Section "InputClass"
Identifier "FocalTech Touchscreen Calibration"
MatchProduct "EP0110M09"
MatchDevicePath "/dev/input/event*"
Driver "libinput"
${transform}
EndSection
EOF
fi
}
####################################################### MAIN
target_homedir="/home/pi"
clear
echo "This script downloads and installs"
echo "PiTFT Support using userspace touch"
echo "controls and a DTO for display drawing."
echo "one of several configuration files."
echo "Run time of up to 5 minutes. Reboot required!"
echo
echo "Select configuration:"
selectN "PiTFT 2.4\", 2.8\" or 3.2\" resistive (240x320)" \
"PiTFT 2.2\" no touch (240x320)" \
"PiTFT 2.8\" capacitive touch (240x320)" \
"PiTFT 3.5\" resistive touch (320x480)" \
"PiTFT 2.0\" no touch (240x320)" \
"PiTFT Mini 1.3\" or 1.54\" display (240x240)" \
"MiniPiTFT 1.14\" display (240x135) - WARNING! CUTTING EDGE! WILL UPGRADE YOUR KERNEL TO LATEST" \
"Quit without installing"
PITFT_SELECT=$?
if [ $PITFT_SELECT -gt 7 ]; then
exit 1
fi
echo "Select rotation:"
selectN "90 degrees (landscape)" \
"180 degrees (portait)" \
"270 degrees (landscape)" \
"0 degrees (portait)"
PITFT_ROTATE=$?
if [ $PITFT_ROTATE -gt 4 ]; then
exit 1
fi
PITFT_ROTATIONS=("90" "180" "270" "0")
PITFT_TYPES=("28r" "22" "28c" "35r" "st7789_240x320" "st7789_240x240" "st7789_240x135")
WIDTH_VALUES=(320 320 320 480 320 240)
HEIGHT_VALUES=(240 240 240 320 240 240)
HZ_VALUES=(64000000 64000000 64000000 32000000 32000000 64000000)
args=$(getopt -uo 'hvri:o:b:u:' -- $*)
[ $? != 0 ] && print_help
set -- $args
for i
do
case "$i"
in
-h)
print_help
;;
-v)
print_version
;;
-u)
target_homedir="$2"
echo "Homedir = ${2}"
shift
shift
;;
esac
done
# check init system (technique borrowed from raspi-config):
info PITFT 'Checking init system...'
if command -v systemctl > /dev/null && systemctl | grep -q '\-\.mount'; then
echo "Found systemd"
SYSTEMD=1
elif [ -f /etc/init.d/cron ] && [ ! -h /etc/init.d/cron ]; then
echo "Found sysvinit"
SYSTEMD=0
else
bail "Unrecognised init system"
fi
if grep -q boot /proc/mounts; then
echo "/boot is mounted"
else
echo "/boot must be mounted. if you think it's not, quit here and try: sudo mount /dev/mmcblk0p1 /boot"
if ask "Continue?"; then
echo "Proceeding."
else
bail "Aborting."
fi
fi
if [[ ! -e "$target_homedir" || ! -d "$target_homedir" ]]; then
bail "$target_homedir must be an existing directory (use -u /home/foo to specify)"
fi
pitfttype=${PITFT_TYPES[$PITFT_SELECT-1]}
pitftrot=${PITFT_ROTATIONS[$PITFT_ROTATE-1]}
if [ "${pitfttype}" != "28r" ] && [ "${pitfttype}" != "28c" ] && [ "${pitfttype}" != "35r" ] && [ "${pitfttype}" != "22" ] && [ "${pitfttype}" != "st7789_240x320" ] && [ "${pitfttype}" != "st7789_240x240" ] && [ "${pitfttype}" != "st7789_240x135" ]; then
echo "Type must be one of:"
echo " '28r' (2.8\" resistive, PID 1601)"
echo " '28c' (2.8\" capacitive, PID 1983)"
echo " '35r' (3.5\" Resistive)"
echo " '22' (2.2\" no touch)"
echo " 'st7789_240x320' (2.0\" no touch)"
echo " 'st7789_240x240' (1.54\" or 1.3\" no touch)"
echo " 'st7789_240x135' 1.14\" no touch)"
echo
print_help
fi
info PITFT "System update"
sysupdate || bail "Unable to apt-get update"
info PITFT "Installing Python libraries & Software..."
softwareinstall || bail "Unable to install software"
info PITFT "Updating /boot/config.txt..."
update_configtxt || bail "Unable to update /boot/config.txt"
if [ "${pitfttype}" == "28r" ] || [ "${pitfttype}" == "35r" ] || [ "${pitfttype}" == "28c" ] ; then
info PITFT "Updating SysFS rules for Touchscreen..."
update_udev || bail "Unable to update /etc/udev/rules.d"
info PITFT "Updating TSLib default calibration..."
update_pointercal || bail "Unable to update /etc/pointercal"
fi
# ask for console access
if ask "Would you like the console to appear on the PiTFT display?"; then
info PITFT "Updating console to PiTFT..."
uninstall_fbcp || bail "Unable to uninstall fbcp"
install_console || bail "Unable to configure console"
else
info PITFT "Making sure console doesn't use PiTFT"
uninstall_console || bail "Unable to configure console"
if ask "Would you like the HDMI display to mirror to the PiTFT display?"; then
info PITFT "Adding FBCP support..."
install_fbcp || bail "Unable to configure fbcp"
if [ -e /etc/lightdm ]; then
info PITFT "Updating X11 default calibration..."
update_xorg || bail "Unable to update calibration"
fi
fi
fi
#info PITFT "Updating X11 setup tweaks..."
#update_x11profile || bail "Unable to update X11 setup"
#if [ "${pitfttype}" != "35r" ]; then
# # ask for 'on/off' button
# if ask "Would you like GPIO #23 to act as a on/off button?"; then
# info PITFT "Adding GPIO #23 on/off to PiTFT..."
# install_onoffbutton || bail "Unable to add on/off button"
# fi
#fi
# update_bootprefs || bail "Unable to set boot preferences"
info PITFT "Success!"
echo
echo "Settings take effect on next boot."
echo
echo -n "REBOOT NOW? [y/N] "
read
if [[ ! "$REPLY" =~ ^(yes|y|Y)$ ]]; then
echo "Exiting without reboot."
exit 0
fi
echo "Reboot started..."
reboot
exit 0
Mirroring HDMI with fbcp to the TFT screen
Section titled Mirroring HDMI with fbcp to the TFT screenThe installation with the adafruit-pitft.py PiTFT helper installation script is quite simple:
cd ~
sudo pip3 install --upgrade adafruit-python-shell click
# Copy the latest version from Github:
git clone https://github.com/adafruit/Raspberry-Pi-Installer-Scripts.git
# Change to the installer directory:
cd Raspberry-Pi-Installer-Scripts
# Run the PiTFT installer script:
sudo python3 adafruit-pitft.py
Then you can
- Enter the number for your display type (in my case “ST7789V 2.0 no touch”)
- Select the display rotation (0°, 90°, 180°, 270°)
- Answer the question “Would you like the console to appear on the PiTFT display?” with NO
- Answer the question “Would you like the HDMI display to mirror to the PiTFT display?” with YES
- Then fbcp gets installed. Finally answer the question “Reboot now?” with YES
That's all. Et voila, the TFT display shows the Raspi Dektop and all applications as on the HDMI port. Videos are played flawlessly and smoothly with VLC or mplayer. In the init sequence, the frame rate is set to 60 fps, so this PiTFT driver should give a similar result to the fbcp-ili9341 driver above.
For more options and explanations, see the detailed installation guide for PiTFT.
Notes
During the installation, the desktop resolution is changed and reduced to the TFT resolution. It may be difficult to use some applications in this low resolution mode. Therefore, I would recommend changing the settings of the programs you want to use before installing the TFT driver.
I haven't tested it, but I think that the installer script also supports displays with ILI9340, ILI9341 and HX8357-D00/D01 driver chips (those are used in some of the PiTFT displays).