User space LCD driver for Raspberry pi zero

 I tried to get a raspberry pi zero a couple of months ago but I couldn't get one because of its demand and availability in India. But my friend Michael Kohn from Missouri sent me a pi-zero camera version. Thanks Mike :)

      I have a 160 x 128 TFT (st7735r) and I thought of hooking it up with the pi-zero via SPI. I searched in internet and I found that we can insert some fbtft driver in the latest version of raspbian which can drive the LCD by creating a new frame buffer /dev/fb1.
       Then I thought of making a very simple user space equivalent for the same which should directly work based on the existing frame buffer /dev/fb0. Also it should not depend on any additional kernel modules and peripheral driver dependencies except the default bcm spi driver. Also the code should be as simple as possible so that any one can read and understand it, unlike a kernel module which require a bit of kernel experience.

        As a start, I wrote two source files gpio.c and spi.c based on ioctl (a device specific system call defined by the driver) by referring online (wiringPi) and BCM2835.pdf. I have already used the same tft display on stm32f4, so I ported the same code to st7735.c  and using the ioctl function for spi (in spi.c) I am able to initialise the LCD and able to draw pixels on it. Luckily the ioctl for spi supports DMA transfer. This saves a lot of CPU cycle for frame buffer streaming.
      Next step is set the frame buffer to 160x128 with 16-bit depth which can be done using fbset command or modifying the /boot/config.txt.

Then I opened the frame buffer "/dev/fb0" and mmapped (mmap) it to a pointer. After that I streamed the ' 160x128 - 16 bit ' frame buffer to the LCD via SPI in multiple blocks of 4096 bytes. By default the spi dma maximum payload size seems to be 4096, if I use more than that it doesn't works, I dono how to increase that size but it is okay for saving a lot of CPU cycle. To render a frame I need to repeat the transfer for around 10 times in a loop.
Source code:

But I found an issue, the frame buffer in Pi is little endian and thus the 16 bit 565 colour cannot be displayed properly without swapping the bytes. I searched a lot if it is possible to configure the frame buffer to big-endian but I couldn't find any way method till now, so I manually swapped the bytes in a loop for each frame. 

   Yes, now the tty1 is displayed on LCD and the  CPU usage is only around 3.2%, thanks for the DMA support. !!

 I am able to run the full X GUI on it by typing startx. Till now everything is fine.

Then I tried to play video using mplayer but the video is not playing and it is working fine with fbtft driver. At first I couldn't find the reason for this. I thought may be I need to create a new frame buffer '/dev/fb1' for this purpose as in fbtft. But later I found that when I am playing any video, automatically the frame buffer resolution is changing to 640x480 which is totally against my logic. 
Then I found an easy workaround for this, I dono if it is a good method, but it works fine for me. Before reading every frame from frame buffer I am reading the resolution as well and if the resolution is changed, accordingly I am adjusting a skipping offset for row and column so that the new frame buffer copy will be always 160x120. I made the display resolution to 160x120 instead of 128 for easily scaling it from 640x480. So now the frame buffer is byte swapped dynamically based on the instantaneous frame buffer by using the skipping offsets. 

Yes, now I am able to play video of any resolution on my display using below command!
sudo SDL_VIDEODRIVER=directfb SDL_FBDEV=/dev/fb0 mplayer -vo sdl -framedrop test1.m4v

I am adjusting the frame rate by adding usleep delay in the while loop for frame buffer rendering. Also we can reduce the spi clock speed but it is better to keep highest clock speed supported by LCD.

A simple application can be an IP camera viewer using rtsp protocol. For example download "RTSP server" application in android mobile and stream the camera via rtsp and use mplayer to play that live streaming on the LCD using below command:
sudo SDL_VIDEODRIVER=directfb SDL_FBDEV=/dev/fb0 mplayer -vo sdl -framedrop rtsp:// 
(need to modify the rtsp link accordingly)

Using pygame we can implement our own GUI as well. 

Source code:

The repository contains additional files like sample videos, shell scripts for playing videos, rtsp ip camera etc.


  1. Hey Vinod, this looks like what I need to get my st7735r 128x160 display working with my Pi Zero but I must admit I'm a bit of a begineer and on my first try I screwed up my install and was left with a Pi that wouldnlt boot beyond the initial loading messages.

    I have a Python script that uses Pygame to display an IR thermal camera image through the gui, for simplicity I just want to boot into the GUI and display the small window on the TFT.

    I copied over your files and ran;
    make clean


    sudo ./run

    but after a reboot I was stuck at the loading prompts. Do you have a beginners description of whats required and how to get this screen working from a fresh install? Adafruit have one but it's for Arduino not Pi and I'm unsure of how to port the C++ over for use elsewhere.

    Basically I want to load some software run startx and see something one the TFT, feel free to say "It's not that simple".

    I'm prety sure I have the pinouts correct.

    Any help would be most welcome.

    1. I have a picture now, I did a fresh Raspbian install and also had to chmod the

      I'm now trying to figure out why the picture is smeared over the screen diagonally and not at all correct.

    2. Panic over, I found this in the end and it just worked;

  2. This comment has been removed by a blog administrator.

  3. There are a few confusions about top quality televisions or HDTVs as they apply to the fast development of LCD innovation.Best 24 inch tvs

  4. Hi, I have an ili9488 with touch, but the pins are 15 pins (DB0 ... DB15), it's possible make a port for this? I need use all pins or it's possible using 0 to 7 ? Thanks!