world leader in high performance signal processing
Trace: » touch_simple

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

www.analog.com_static_imported-files_images_product_descriptions_680755565image1a.jpg

What is touch_simple

An application that uses SDL_gfx to draw two boxes on the screen, when a user touches a box on the screen, that box will change colours. 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_simple

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_simple 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_simple

    root:/> ./touch_simple
    Loading... 
    Done!
    Drawing boxes... 
    Displayed. Try touching the boxes. Interrupt to exit.
  • Touch the boxes on the screen with your finger
  • Exit (Ctrl + C)

How the touch_simple 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, BoxStructType *box2)
{
  // Draw white background
  SDL_Rect rect0 = {0, 0, 480, 272};
  SDL_FillRect(screen, &rect0, 0xFFFFFF);
  // Draw box 1
  draw_box(screen, box1);

  // Draw box 2
  draw_box(screen, box2);

  // 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;
}

int update_box_colour(int colour)
{
  if (colour == 0x000000FF)
  {
    return 0xFF0000FF;
  }
  else if (colour == 0xFF0000FF)
  {
    return 0xFFFF00FF;
  }
  else if (colour == 0xFFFF00FF)
  {
    return 0x00FF00FF;
  }
  else if (colour == 0x00FF00FF)
  {
    return 0x00FFFFFF;
  }
  else if (colour == 0x00FFFFFF)
  {
    return 0x0000FFFF;
  }
  else
  {
    return 0x000000FF;
  }
}

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. update_box_colour will be called if a user touches a box, and will cycle through that boxes colour.

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;

  BoxStructType box1;
  box1.x = 100;
  box1.y = 120;
  box1.w = 100;
  box1.h = 40;
  box1.colour = 0x000000FF;

  BoxStructType box2;
  box2.x = 300;
  box2.y = 120;
  box2.w = 100;
  box2.h = 40;
  box2.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, &box2);
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)
    {
      // Update box1 colour & redraw screen
      box1.colour = update_box_colour(box1.colour);
      draw_screen(screen, &box1, &box2);
    }
    else if (check_box_touch(&box2, input_event.button.x, input_event.button.y) == 1)
    {
      // Update box2 colour & redraw screen
      box2.colour = update_box_colour(box2.colour);
      draw_screen(screen, &box1, &box2);
    }
  }
}

This main loop performs the touch detection. It will look for any mouse button down events as the touch screen appears as a mouse to SDL. When it does get an event, it will use the check_box_touch function to compare the x & y coordinates to the boxes on the screen. If they are within bounds, it will update that boxes colour.

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_simple

First make sure that your kernel has the libraries libSDL, SDL_ttf, 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_simple.c -o touch_simple

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_simple.c” is the source file while ”-o touch_simple” indicates the output file is “touch_simple”.

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

root:/> make touch_simple

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.