world leader in high performance signal processing
Trace: » touch_advanced

ADSP-BF548 EZ-KIT Quick Start: Peripheral Demos: touch_advanced

www.analog.com_static_imported-files_images_product_descriptions_680755565image1a.jpg

What is touch_advanced

An application that uses SDL_gfx to draw a box on the screen, when the user touches the box and drags it on the screen with a finger, the box will follow the finger. This program will do so until it is terminated.

This demo program will show how you can utilise the SDL libraries to handle the touchscreen as if it were a mouse. SDL is a very large topic, but it is well documented and you can learn more at www.libsdl.org. Please note that SDL is not the only way to go about utilising the touchscreen as any library that can use tslib can also be used. You can also just read events directly from the input event buffer which will provide x and y coordinates as well as touch pressure readings. To learn more about reading from the event buffer please look into the push buttons demo as it would be a similar methodology to that demo.

Note that the kernel option 'AD7877 based touchscreens' must be enabled for this to work.

Device Drivers ->
  Input device support ->
    Touchscreens ->
      [X] AD7877 based touchscreens

Download touch_advanced

The demo comes with the customised Ubuntu ISO and can be found in the folder /home/Blackfin/demos. Alternatively it is already installed on the demo uClinux image (demo-uclinux.img) that you can get on ADSP-BF548 EZ-KIT Quick Start: Booting uClinux.

If you just want to download the demos on their own and get their source code you can download the compressed archive (demos-R1.tar.gz) here bf548-quick-start .

How to run the touch_advanced program

  • Change directory to /demos/touch

    root:/> cd /demos/touch
  • Make sure LCD + TS environment settings enabled

    root:/> export TSLIB_FBDEVICE=/dev/fb0
    root:/> export TSLIB_CONSOLEDEVICE=none
    root:/> export TSLIB_CONFFILE=/etc/ts.conf
    root:/> export TSLIB_CALIBFILE=/etc/pointercal
    root:/> export TSLIB_TSDEVICE=/dev/input/event2
    root:/> export QWS_MOUSE_PROTO=tslib
    root:/> export SDL_NOMOUSE=1
    root:/> export SDL_MOUSEDEV=/dev/input/event2
    root:/> export SDL_MOUSEDRV=TSLIB

    Note: You only need to do this once per uClinux boot. Also this is assuming the touch screen device is reporting to event2, it may report to another event id. Run event_test to find out the details of each event node to find the touch screen, eg. “event_test /dev/input/event2”.

  • Make sure touch screen has been calibrated. Run ts_calibrate and follow the instructions.

    root:/> ts_calibrate

    Note: You only need to do this once

  • Run touch_advanced

    root:/> ./touch_advanced
    Loading... 
    Done!
    Drawing boxes... 
    Displayed. Try dragging boxes around. Interrupt to exit.
  • Drag the box on the screen with your finger
  • Exit (Ctrl + C)

How the touch_advanced demo works

Required header files

#include <stdio.h>
#include <stdlib.h>

// Necessary Drawing Libraries
#include <SDL_gfxPrimitives.h>

As you can see, this program only grabs SDL_gfxPrimitives.h really for the low level drawing API. It doesn't explicitly grab SDL.h as that will be done by SDL_gfxPrimitives.

Struct definitions

typedef struct BoxStruct 
{
  int x;
  int y;
  int w;
  int h;
  int colour;
} BoxStructType;

This is just a struct in C to simplify storage of the box dimensions and coordinates and colours. This shows simple constructs such as structs work fine in uclibc just like on the full C.

Box & Drawing functions

void draw_box(SDL_Surface *screen, BoxStructType *box)
{
  // Draw box
  boxColor(screen, box->x, box->y, box->x + box->w, box->y + box->h, box->colour);
}

void draw_screen(SDL_Surface *screen, BoxStructType *box1)
{
  // Draw white background
  SDL_Rect rect0 = {0,0,480,272};
  SDL_FillRect(screen, &rect0, 0xFFFFFF);
  // Draw box 1
  draw_box(screen, box1);

  // display surface on LCD
  SDL_Flip(screen);
}

int check_box_touch(BoxStructType *box, int x, int y)
{
  // Check box
  if (x > box->x && x < box->x + box->w)
  {
    if (y > box->y && y < box->y + box->h)
    {
      return 1;
    }
  }
  return 0;
}

This program does some drawing using SDL_gfx to show output when the user interacts with the touch screen. The functions above are just the drawing functions shifted into their own function wrappers for code cleanliness.

draw_box will draw a BoxStruct based on its properties on the screen. draw_screen will draw the screen (invoking draw_box for each box). check_box_touch is a bounds checker that will take the coordinates of where the user touched the touch screen and see if it interacted with a box.

Main function

int main(int argc, char* argv[])
{
  int screen_width = 480;
  int screen_height = 272;
  int screen_bitdepth = 24;

  SDL_Surface *screen;
  SDL_Event input_event;

  int running = 0;
  int dragging = 0;

  // Define a box
  BoxStructType box1;
  box1.x = 100;
  box1.y = 120;
  box1.w = 100;
  box1.h = 40;
  box1.colour = 0x000000FF;

  printf("Loading... \n");

This section highlights how the programs run are just like normal C programs on your desktop machine. You have your normal main function, and some variable declarations at the top of the function. The screen dimensions provided match up with the LCD on the BF548 EZ-KIT. It also creates the definition of the two boxes to be drawn on the screen.

Initializing the video subsystem

if (SDL_Init(SDL_INIT_VIDEO) < 0)
{
  fprintf(stderr, "Error: Unable to init SDL: %s\n", SDL_GetError());
  exit(1);
}
atexit(SDL_Quit);
SDL_ShowCursor (SDL_DISABLE);

screen = SDL_SetVideoMode(screen_width, screen_height, screen_bitdepth, SDL_HWSURFACE | SDL_DOUBLEBUF);
if (screen == NULL)
{
  fprintf(stderr, "Error: Unable to grab screen\n");
  SDL_Quit();
  exit(1);
}

printf("Done!\n");

This section of code will initialise the video subsystem of SDL so that SDL knows it will have a graphics buffer to write to. If it fails, it will abort the program. The atexit call is added so that when the program closes, SDL will clean itself up.

It also grabs the screen to write to by making a call to SDL_SetVideoMode and provides the screen details, note that width, height and depth are provided at the start of this program and are set to the LCD screens actual dimensions so this creates a full screen window. If this fails, the program will abort.

This section will be quite common for any SDL program using the LCD and the SDL_Init call might have to OR other subsystems as well if you're using things like audio.

Drawing Screen

printf("Drawing boxes... \n");
draw_screen(screen, &box1);
printf("Displayed. Try touching the boxes. Interrupt to exit.\n");

This section merely draws a white rectangle to the screen, then calls the drawing function wrappers discussed previously. For further information on how the drawing works, look at the LCD demos pages, especially shapes and animated.

Input handling loop

memset(&input_event, 0, sizeof(input_event));
while (running == 0)
{
  if (SDL_PollEvent(&input_event))
  {
    if (input_event.type == SDL_QUIT)
    {
      running = 1;
    }
  else if (input_event.type == SDL_MOUSEBUTTONDOWN)
  {
    // Check touch coordinates against the different boxes
    if (check_box_touch(&box1, input_event.button.x, input_event.button.y) == 1)
    {
      // Activate dragging code
      dragging = 1;
    }
  }
  else if (input_event.type == SDL_MOUSEMOTION)
  {
    // Animate dragging rectangle if currently mouse down
    if (dragging == 1)
    {
      box1.x = box1.x + input_event.motion.xrel;
      box1.y = box1.y + input_event.motion.yrel;

      draw_screen(screen, &box1);
    }
  }
  else if (input_event.type == SDL_MOUSEBUTTONUP)
  {
    // Deactivate dragging
    dragging = 0;
  }
}

This main loop performs the touch detection. It will look for any mouse events as the touch screen appears as a mouse to SDL. When it gets a mouse button down event (first touched) the program will set a flag to 1 (int dragging). When it gets a mouse motion event (movement of the finger) it will see if dragging is set, if so it will get the relative change in motion and shift the box by that coordinate change. When it gets a mouse button up event (finger off touch screen) it will turn off the dragging flag. Just to note, you don't need to use the relative motion as in this example, if you look at touch-simple you can see that mouse events provide an x and y coordinate, even mouse motion events.

This section also performs the common input loop that will continuously check for any program input. This one is looking for either an SDL_Quit call (from program termination) or any key presses (from the keypad or rotary) to perform an exit.

Exiting

SDL_Quit();
return 0;

This section just does some basic clean up and exits the program.

Compiling touch_advanced

First make sure that your kernel has the libraries libSDL, SDL_gfx and tslib enabled.

Secondly, make sure your vendor staging install is complete (ADSP-BF548 EZ-KIT Quick Start: Compiling uClinux).

root:/> make vendor_staging_install

Run the following compile command in the same directory as the source code:

root:/> bfin-linux-uclibc-gcc -O2 \
$(~/Blackfin/blackfin-linux-dist/staging/usr/bin/sdl-config --cflags) \
$(~/Blackfin/blackfin-linux-dist/staging/usr/bin/sdl-config --libs) \ 
-lSDL_gfx \ 
touch_advanced.c -o touch_advanced

What this compile command is doing is it is calling the uclibc cross compiler that is set up from the Blackfin toolchains to compile our application.

The calls to sdl-config are a special case for SDL because SDL places its header & library files in non-root locations so this will load them properly.

The parameters prefixed with -l are to indicate we need to load that library, so in this case the library SDL_gfx is loaded. Without this library, our program would not have access to the required graphics rendering functions.

The final parameters match up to standard gcc compiling; “touch_advanced.c” is the source file while ”-o touch_advanced” indicates the output file is “touch_advanced”.

A standard make file is accompanies this demo and you can run that by simply calling

root:/> make touch_advanced

Further Reading

The touch screen library is discussed on the Touchscreen Library page. This page even includes details on setting it up and debugging.

The AD7877 driver page can be found at http://wiki.analog.com/resources/tools-software/linux-drivers/input-touchscreen/ad7877.

Make sure that libsdl has been configured to enable tslib as an input. This can be done by modifying the file “blackfin-linux-dist/lib/libsdl/Makefile” to add the flag ”--enable-input-tslib” for the CONF_OPTS. This has already been done on the custom Ubuntu ISO.

CONF_OPTS = \
        --disable-arts \
        --disable-esd \
        --disable-pth \
        --disable-video-directfb \
        --disable-video-x11 \
        --enable-input-tslib \

If the touch screen is not responding correctly to you, it is possible that tslib was not built correctly with SDL. This makes SDL use the pressure sensor reading as the x coordinate and the y coordinate will be random.

To correct this, you need to ensure tslib is built prior to SDL being built. The uClinux distribution provided on the custom Ubuntu ISO already has this modification permanently fixed in however if you are using a clean distribution, you will need to do two file edits:

  • “blackfin-linux-dist/lib/Kconfig.local”: Add LIB_TSLIB as a dependency for SDL

    ...
    config LIB_LIBSDL
    bool "Build libSDL"
    depends on LIB_TSLIB
    help
    ...
  • “blackfin-linux-dist/lib/Makefile.local”: Shift libsdl make to dir_4

    dir_3_$(CONFIG_LIB_LIBSDL) += libsdl
    
    to dir 4
    
    dir_4_$(CONFIG_LIB_LIBSDL) += libsdl #requires tslib(3)
  • “blackfin-linux-dist/lib/Makefile.local”: shift sdl_gfx to dir_5

    dir_4_$(CONFIG_LIB_SDL_GFX) += SDL_gfx
    
    to dir 5
    
    dir_5_$(CONFIG_LIB_SDL_GFX) += SDL_gfx

After these edits, “make clean” and “make” will correctly build libsdl with tslib support.

If you do not want to edit the files you can just run the following commands after building your uClinux distribution:

root:/> make lib/libsdl_clean
root:/> make lib/tslib-1.0_clean
root:/> make lib/tslib-1.0_only
root:/> make lib/libsdl_only
root:/> make

The new uClinux image built will now have SDL correctly linking to tslib.