../
Looking for a senior C++ dev? I'm looking for work. Hire me!

Summary

Seeed Studio and 52pi got together and announced 2 touch-capable LCDs in May 2016. The first is a 5" display, the other is a 7" display. I purchased one of the 5" models, and have now figured out how to access it from Linux on my BeagleBone.

Features

The 5" display has 800x480 resolution, 5 buttons (left, right, up, down, enter) and as a bonus is a touch screen. When I first hooked it up to my BeagleBone Green Wireless and booted, it showed me the console with a typical login prompt. Hooking up a keyboard to the BeagleBone's USB port, I could login as if it was a normal desktop computer.

Display

I wanted to access the LCD as a graphics device, not just as a text console. I'm certain there are multiple ways to do this, but I discovered I could do what I needed by accessing the frame buffer in /dev/. The simple explanation is to open up /dev/fb0, perform some ioctl(2) calls, memory map /dev/fb0, then write some bytes to indicate various shades of red, green, or blue. Repeat this 800x480 times, and you get an entire screen of colourful pixels.

The LCD uses 16 bits of colour for every pixel. This means RED, GREEN, and BLUE must all fit in 16 bits. Through trial-and-error, I finally figured out the high 5 bits are for red, the middle 6 bits are for green, and the low 5 bits are for blue. So if you have a 24-bit image, with 8 bits for each channel, you have to discard the lower 2 bits of green, and the lower 3 bits of both red & blue. Then pack all 3 colour values into a uint16_t.

R = 0b1111100000000000
G = 0b0000011111100000
B = 0b0000000000011111
    ------------------
    0bRRRRRGGGGGGBBBBB

In summary:

As a quick example, this is how you'd clear the entire screen:

// where length is likely 800x480x2 (times 2 because each pixel is 16 bits aka 2 bytes) memset( ptr, 0, length );

From a command prompt the screen can be drawn and then cleared with the following commands:

sudo bash # /dev/fb0 is only accessible to root cat /dev/urandom > /dev/fb0 # put random colour on the screen cat /dev/zero > /dev/fb0 # clear the entire screen

Touch Screen

The touch screen provides the following information:

  1. the X coordinate
  2. the Y coordinate
  3. the pressure
  4. key press down (finger touches the screen)
  5. key press up (finger no longer touches the screen)
This information is available as events in /dev/input/by-path/platform-TI-am335x-tsc-event or the corresponding symbolic link in /dev/input/. For example:

sudo apt-get install evtest sudo evtest /dev/input/by-path/platform-TI-am335x-tsc-event Input driver version is 1.0.1 Input device ID: bus 0x0 vendor 0x0 product 0x0 version 0x0 Input device name: "ti-tsc" ... (touch the screen at this point) ... Event: time 1467930869.488798, -------------- EV_SYN ------------ Event: time 1467930869.490523, type 3 (EV_ABS), code 0 (ABS_X), value 2064 Event: time 1467930869.490523, type 3 (EV_ABS), code 1 (ABS_Y), value 903 Event: time 1467930869.490523, type 3 (EV_ABS), code 24 (ABS_PRESSURE), value 184

Some details on Linux's input events is available from event-codes.txt.

Reading the touch screen events requires opening the input event file, doing a few ioctl() calls, and then repeatedly reading struct input_event which is a small 16-byte structure.

Ignoring error checks, this is how to initialize reading the touch screen information:

#include <linux/input.h> // ... int fd = -1; int rc = 0; struct input_absinfo abs_info_x; struct input_absinfo abs_info_y; struct input_absinfo abs_info_pressure; fd = open( "/dev/input/by-path/platform-TI-am335x-tsc-event", O_RDWR | O_CLOEXEC ); rc = ioctl( fd, EVIOCGABS(ABS_X), &abs_info_x ); rc = ioctl( fd, EVIOCGABS(ABS_Y), &abs_info_y ); rc = ioctl( fd, EVIOCGABS(ABS_PRESSURE), &abs_info_pressure );

The three ioctl() calls are mostly to obtain the minimum and maximum ranges. The X and Y coordinates returned by the touch screen do NOT correspond to pixels, so you'll have to convert them to make meaningful sense.

For example, if the X range is 0-4095, then a value of 2048 will be 50% of the screen, or 400 pixels on the 800-pixel wide 5" LCD.

Once you've opened the event file and know the ranges, then you start a loop to read from fd:

struct input_event e; const size_t len = sizeof(e); while (true) { const ssize_t bytes_read = read( fd, &e, len ); if (bytes_read == len) { // ... interpret and process event here } }
Or open the file in non-blocking IO mode with O_NONBLOCK and then use select() to determine when an event is available.

The event structure in linux/input.h is very simple. There are 4 fields to each event: a timestamp, a type, a code, and a value. The events I found of particular interest were EV_KEY and EV_ABS.

EV_KEY is generated when a finger first hits the touch screen, and again when the finger is removed from the touch screen. It acts as if a button was pushed. The code field should be BTN_TOUCH, and a value of 0 indicates the "button up" event, while a value of 1 indicates the "button down" event.

EV_ABS indicates an absolute value. (Versus EV_REL which is a relative move event generated by a mouse.) The code field is set to ABS_X, ABS_Y, or ABS_PRESSURE. The value field is the actual location or pressure.

Note there is a single value field. This means when a EV_KEY event is sent with a value indicating if it was "push up" or "push down" event, there is no room left in the event to indicate the X and Y coordinates. So you'll need to combine the ABS_X and ABS_Y coordinates with the BTN_TOUCH event to determine where the touch event actually occurred. Or ignore the EV_KEY events completely and only use EV_ABS to determine where the screen was touched.

Buttons

The LEFT, RIGHT, UP, DOWN, and ENTER buttons at the bottom of the LCD are problematic. I'm not yet certain if this is a bad board I have, or if something is preventing it from working correctly. I've asked Seeed Studio for some assistance.

At the moment, I can only get the RIGHT and UP buttons to work. This is the command I ran:

sudo apt-get install evtest sudo evtest /dev/input/by-path/platform-gpio_keys-event Input driver version is 1.0.1 Input device ID: bus 0x19 vendor 0x1 product 0x1 version 0x100 Input device name: "gpio_keys" Supported events: Event type 0 (EV_SYN) Event type 1 (EV_KEY) Event code 28 (KEY_ENTER) Event code 103 (KEY_UP) Event code 105 (KEY_LEFT) Event code 106 (KEY_RIGHT) Event code 108 (KEY_DOWN)

So while it obviously knows about the 5 buttons located on the bottom of the LCD, only 2 of them seem to generate any events:

Event: time 1467931504.899499, type 1 (EV_KEY), code 106 (KEY_RIGHT), value 1 Event: time 1467931504.899499, -------------- EV_SYN ------------ Event: time 1467931505.095462, type 1 (EV_KEY), code 106 (KEY_RIGHT), value 0 Event: time 1467931505.095462, -------------- EV_SYN ------------ Event: time 1467931505.981331, type 1 (EV_KEY), code 103 (KEY_UP), value 1 Event: time 1467931505.981331, -------------- EV_SYN ------------ Event: time 1467931506.147099, type 1 (EV_KEY), code 103 (KEY_UP), value 0 Event: time 1467931506.147099, -------------- EV_SYN ------------

2016-07-12: I've since discovered that all 5 keys work on BBG. It is only on the BBGW that some of the keys aren't working correctly. I'm hoping someone at Seeed Studio will get back to me to say why some of the keys don't work on BBGW.

Last modified: 2016-07-12
Stéphane Charette, stephanecharette@gmail.com
../