Friday, September 5, 2014

Experimenting with BeagleBone Black and a userspace TFT library

This post is part of a series of posts I have done about the Adafruit 2.2" TFT display. You can check out those previous posts here and here. In those previous posts I was using this display with an Arduino and ran into issues with the refresh rate. To get a decent refresh rate I had to do some extra coding to determine what data needed updating and then only redraw the sections of the screen that actually needed to change. In this post I am going to take a look at using this display with the BeagleBone Black single board computer. My hope going into this is that the CPU horsepower of the BBB will allow quicker screen redraws which would eliminate the need to selectively redraw sections of the screen.

Since the BBB is a full computer there is a Linux operating system sitting between the code and the hardware. This quite a bit different from an Arduino where you have direct access to the hardware. My BBB came with Angstrom Linux on it but I am more familiar with Ubuntu and the Adafruit tutorials were written assuming you have Ubuntu installed. So I swapped the OS to Ubuntu 14.04 following Adafruit's tutorial. To control the TFT display with a BBB Adafruit created a user space library in Python. To install the library and wire up the display I followed the instructions on learn.adafruit.com. The instructions were written for the 2.8 inch display but they worked just fine for my 2.2 inch display. I think these instructions would probably work for most ILI9341 based TFT displays.

The wiring for the 2.2" display is slightly different than the 2.8" display. Here is how I wired it up.

  • BeagleBone Black SCLK pin P9_22 to TFT SCK.
  • BeagleBone Black MOSI pin P9_18 to TFT MOSI.
  • BeagleBone Black CE0 pin P9_17 to TFT CS.
  • BeagleBone Black pin P9_12 to TFT RST.
  • BeagleBone Black pin P9_15 to TFT D/C.
  • BeagleBone Black 3.3V power pin P9_3 to TFT Vin.
  • BeagleBone Black ground pin P9_2 to TFT GND.

Then I ran the sample image.py application to make sure everything worked correctly



Now that we have all that stuff out of the way let's get to the real point of this post, how fast can the BBB update this display! To test it I took one of Adafruit's example scripts that came with the library and modified it so it read the value of an analog pin (P9_40) and then display that value on the TFT screen with a custom font. I connected a 10k pot to the analog pin so I could adjust the value.

The first time I ran the test script I ran into problem with the bottom the text being clipped off. Turns out it was a bug with a Python library. I detailed the fix for that here.

In the script I also recorded a time stamp before drawing the TFT screen and immediately after to measure how long it took to actually draw the screen.

Here is a video of the testing



If you made it to the end of the video you saw that it was taking approximately 0.6 seconds for the draw command to complete but the screen itself appears to update nearly instantly. This is a huge improvement compared to when I tested this display with an Arduino. On the Arduino you could see the screen slowly painting the image onto the screen.

Here is a video of the same exact test with an Arduino:



I'm not sure what is taking 0.6 seconds with the BBB. The screen redraws seem to happen faster than that. Maybe it's the data transfer over SPI plus the screen redraw (Update: See this post to learn how to speed up the redraws). In any case I am quite pleased with it. Another advantage of using the BBB is I'm able to use any TrueType font to display text. The library on the Arduino only had one font which became very blocky when using large characters. As you saw in the first video I used an Arial font which looked very smooth.

I originally wanted to use this TFT display for my speedometer project but abandoned it for a 7 segment display because the refresh rate was so slow with the Arduino. Based on these BBB results I think I'm going to make a v2.0 digital speedometer using both an Arduino and a BBB. The BBB isn't great at doing real time things like counting pulses so I think I will continue to use the Arduino to count the VSS pulses and then feed the speed data into the BBB over I2C or CAN. I'm already brainstorming other data to display on the screen...

Oh here is the python code I used to test this display if you are interested:





Helpful Links

https://learn.adafruit.com/user-space-spi-tft-python-library-ili9341-2-8/
https://learn.adafruit.com/beaglebone-black-installing-operating-systems
http://elinux.org/Beagleboard:Ubuntu_On_BeagleBone_Black


[Updated 2014-09-05]
This is in reference to my statement about about the BBB not being great at real time tasks. I've been doing some reading and the BBB does have a hardware counter on it but it looks like you have to be a NASA engineer to get it to work. This post discusses the PRU on the BBB. When he started talking about having to write Assembly code I tuned out. I'll just use an Arduino to do the accurate pulse counting, thanks.

[Updated 2014-10-04]
Added Fritzing wiring diagram and pin outs.

[Updated 2014-10-08]
Added link to post that shows how to speed up display redraws.



7 comments:

  1. Good stuff Matt!
    Loved your post on the python lib bug.

    it would be great if you could hang around at the BeagleFu forums( forum.beaglefu.com). It is still slow, but it would be nice to have you there.

    Anyways.. keep up your writeups!

    ReplyDelete
  2. Thanks for posting this, it's extremely helpful!

    I wanted to get a faster update, so poked around a bit in the code. The slow updating seems to come almost entirely from the image_to_data call in Adafruit_ILI9341. It's probably worth a devoted c routine at some point, but even swapping it to numpy seems to help, got my redraws down to 0.2s. If you just edit the library, it's a quick change:


    import numpy as np

    def image_to_data(image):
    # """Generator function to convert a PIL image to 16-bit 565 RGB bytes."""
    # pixels = image.convert('RGB').load()
    # width, height = image.size
    # for y in range(height):
    # for x in range(width):
    # r,g,b = pixels[(x,y)]
    # color = color565(r, g, b)
    # yield (color >> 8) & 0xFF
    # yield color & 0xFF
    pb = np.array(image.convert('RGB')).astype('uint16')
    color = ((pb[:,:,0] & 0xF8) << 8) | ((pb[:,:,1] & 0xFC) << 3) | (pb[:,:,2] >> 3)
    return np.dstack(((color >> 8) & 0xFF, color & 0xFF)).flatten().tolist()

    ReplyDelete
    Replies
    1. Wow thanks for this tip! I'm going to try it out asap. Would you mind if I used this info in new blog post?

      Delete
  3. Maybe you can use xenomai for real time task

    ReplyDelete
  4. Please, i've followed your tutorial, but every .py example i run, i get this error:

    root@beaglebone:~/src/Adafruit_Python_ILI9341/examples# sudo python image.py
    Traceback (most recent call last):
    File "image.py", line 41, in
    disp = TFT.ILI9341(DC, rst=RST, spi=SPI.SpiDev(SPI_PORT, SPI_DEVICE, max_speed_hz=64000000))
    File "build/bdist.linux-armv7l/egg/Adafruit_GPIO/SPI.py", line 42, in __init__
    IOError: [Errno 2] No such file or directory

    Any idea?

    ReplyDelete
    Replies
    1. One possibility is that your SPI pins aren't setup. Run the command 'ls /dev/spi*' and you should see the devices /dev/spidev1.0 and /dev/spidev1.1
      If not, you need to get that setup first. Adafruit has a nice tutorial on how to do that here:
      https://learn.adafruit.com/setting-up-io-python-library-on-beaglebone-black/using-the-bbio-library

      Delete
    2. The problem is I'm Using python3.2.3.
      No module named spidev
      But I can see /devspidev 0.0 /dev/spidev0.1

      Any ideas?

      Delete

Please note all comments are moderated by me before they appear on the site. It may take a day or so for me to get to them. Thanks for your feedback.