Pointer Input Devices

emWin provides support for pointer-input-devices. Pointer input devices can be touch-screen, mouse or joystick. The basic emWin package includes a driver for analog touch-screens, a PS2 mouse driver, as well as an example joystick driver. Other types of touch-panel and mouse devices can also be used with the appropriate drivers.

The software for input devices is located in the subdirectory GUI\Core.

Description

Pointer input devices are devices such as mice, touch-screens and joysticks. Multiple pointer input devices can be used in a single application to enable simultaneous mouse/touch-screen/joystick use. Basically all a PID driver does is calling the routine GUI_PID_StoreState() whenever an event (such as a moved mouse, or a pressed touch screen) has been detected.

PID events are stored in a FIFO which is processed by the window manager. If the window manager is not used (respectively deactivated), the application is responsible for reacting on PID events.

Pointer input device API

The table below lists the pointer input device routines in alphabetical order. Detailed descriptions follow.

Note: This API is used by the PID-driver; if you use a PID-driver shipped with emWin, your code does not need to call these routines.

Routine Description
GUI_PID_GetState() Return the current state of the PID.
GUI_PID_StoreState() Store the current state of the PID.
Data structure

The structure of type GUI_PID_STATE referenced by the parameter pState is filled by the routine with the current values. The structure is defined as follows:

typedef struct {
  int x, y;
  U8  Pressed;
  U8  Layer;
} GUI_PID_STATE;
Elements of GUI_PID_STATE
Data type Element Description
int x X position of pointer input device.
int y Y position of pointer input device.
U8 Pressed If using a touch screen this value can be 0 (unpressed) or 1 (pressed).
If using a mouse bit 0 is used for the pressed state of the left button and bit 1 for the right button.
The bits are 1 if the button is pressed and 0 if not.
U8 Layer Describes the layer from which the PID state has been received.

Mouse driver

Mouse support consists of two "layers": a generic layer and a mouse driver layer. Generic routines refer to those functions which always exist, no matter what type of mouse driver you use. The available mouse driver routines, on the other hand, will call the appropriate generic routines as necessary, and may only be used with the PS2 mouse driver supplied with emWin. If you write your own driver, it is responsible for calling the generic routines.

The generic mouse routines will in turn call the corresponding PID routines.

Generic mouse API

The table below lists the generic mouse routines in alphabetical order. These functions may be used with any type of mouse driver. Detailed descriptions follow.

Routine Description
GUI_MOUSE_GetState() Return the current state of the mouse.
GUI_MOUSE_StoreState() Store the current state of the mouse.

PS2 mouse driver

The driver supports any type of PS2 mouse.

Using the PS2 mouse driver

The driver is very easy to use. In the startup code, the init function GUI_MOUSE_DRIVER_PS2_Init() should be called.

The application should somehow notice when a byte is received from the mouse. When this happens, the function GUI_MOUSE_DRIVER_PS2_OnRx() should be called and the byte received passed as parameter. The driver in turn then calls GUI_PID_StoreState as required.

The reception of the byte is typically handled in an interrupt service routine.

An example ISR could look as follows: (Note that this is of course different for different systems)

void interrupt OnRx(void) {
  char Data;

  Data = UART_REG;                   // Read data from the hardware
  GUI_MOUSE_DRIVER_PS2_OnRx(Data);   // Pass it on to the driver
}

PS2 mouse driver API

The table below lists the available mouse driver routines in alphabetical order.

Routine Description
GUI_MOUSE_DRIVER_PS2_Init() Initialize the mouse driver.
GUI_MOUSE_DRIVER_PS2_OnRx() Called form receive interrupt routines.

Touch screen driver

A touch screen driver will typically simply call GUI_PID_StoreState() as described earlier. Any type of touch screen can be supported this way. Your are responsible for writing the driver code (which is usually fairly simple).

The most common way of interfacing a touch screen is the 4-pin analog interface, for which a driver is supplied.

Generic touch screen API

The generic touch screen API is used with any type of driver (analog, digital, etc.). A driver calls the appropriate routines as necessary. If you write your own driver, it has to call the generic routines.

The table below lists the generic touch-screen routines in alphabetical order. These functions may be used with any type of touch-screen driver. Detailed descriptions follow.

Routine Description
GUI_TOUCH_GetState() Return the current state of the touch-screen.
GUI_TOUCH_StoreState() Store the current state of the touch-screen using X- and Y-coordinates.
GUI_TOUCH_StoreStateEx() Store the current state of the touch-screen.

The analog touch screen driver

The emWin touch-screen driver handles analog input (from an 8-bit or better A/D converter), debouncing and calibration of the touch-screen.

The touch-screen driver continuously monitors and updates the touch-panel through the use of the function GUI_TOUCH_Exec(), which calls the appropriate generic touch-screen API routines when it recognizes that an action has been performed or something has changed.

How an analog touch screen works

The touch panel consists of 2 thin conducting layers of glass, normally insulated from each other. If the user presses the touch panel, the two layers are connected at that point. If a voltage is applied to the Y-layer, when pressed, a voltage can be measured at the X+/X-terminals. This voltage depends on the touch position. The same thing holds true the other way round. If a voltage is applied to the X-layer, when pressed, a voltage can be measured at the Y+/Y-terminals.

Setting up the analog touch screen

Putting a touch panel into operation should be done in the following steps:

  • Implementing the hardware routines
  • Implementing regular calls to GUI_TOUCH_Exec()
  • Verifying proper operation with the oscilloscope
  • Using example to determine calibration values
  • Adding a call of GUI_TOUCH_Calibrate() to the initialization routine LCD_X_Config() using the determined values

The following shows a detailed description of each step.

Implementing the hardware routines

The first step of implementing a touch screen should be filling the hardware routines with code. These routines are:

GUI_TOUCH_X_ActivateX(), GUI_TOUCH_X_ActivateY()
GUI_TOUCH_X_MeasureX(),  GUI_TOUCH_X_MeasureY()

A module GUI_TOUCH_X.c containing the empty routines is located in the folder Sample\GUI_X. You can use this module as a starting point.

The activate routines should prepare the measurement by switching on the measurement voltage. GUI_TOUCH_X_ActivateX() for example should prepare the measurement in Y by switching on the measurement voltage in X. Further it should switch of the voltage in Y and disable the measurement in X.

The measurement routines should return the measurement result of a A/D converter. Later in this chapter you will find an example implementation of the hardware routines.

Implementing regular calls to GUI_TOUCH_Exec()

The second step of implementing a touch screen is to make sure, that the function GUI_TOUCH_Exec() will be called in regular intervals. Your application should call it about 100 times/second. If you are using a real-time operating system, the easiest way to make sure this function is called is to create a separate task. When not using a multitasking system, you can use an interrupt service routine to do the job.

Verifying proper operation with the oscilloscope

After implementing the call of GUI_TOUCH_Exec() make sure the hardware works. The easiest way to do this is to measure the supply and measurement voltages of the touch panel with a oscilloscope. The following table shows a typical result. The first column shows the supply voltage of an axis, the second column shows the result of measuring the measurement voltage when pressing in the middle of the touch panel.

Supply voltage    Measurement voltage

Use example to determine calibration values

The third step is to get the minimum and maximum values of the A/D converter. emWin needs this values to convert the measurement result to the touch position in pixels. These 4 values are:

Value How to get them
GUI_TOUCH_AD_TOP Press the touch at the top and write down the analog input value in Y.
GUI_TOUCH_AD_BOTTOM Press the touch at the bottom and write down the analog input value in Y.
GUI_TOUCH_AD_LEFT Press the touch at the left and write down the analog input value in X.
GUI_TOUCH_AD_RIGHT Press the touch at the right and write down the analog input value in X.

The example folder of emWin contains a small program which can be used to get these values from your touch panel. It is located in the folder Sample\Tutorial and its name is TOUCH_Sample.c. Run this example on your hardware. The output should be similar to the screenshot at the right side.

Use GUI_TOUCH_Calibrate() with the above values

The last step is adding a call to GUI_TOUCH_Calibrate() using the calibration values. The recommended location for calibrating the touch screen is the initialization routine LCD_X_Config() which is located in LCDConf.c. similar to following example:

#define GUI_TOUCH_AD_TOP     877
#define GUI_TOUCH_AD_BOTTOM  273
#define GUI_TOUCH_AD_LEFT    232
#define GUI_TOUCH_AD_RIGHT   918
.
.
.
void LCD_X_Config(void) {
  //
  // Initialize display driver
  //
  .
  .
  .
  //
  // Set orientation of touch screen (only required when using
  //
  TouchOrientation = (GUI_MIRROR_X * LCD_GetMirrorX()) |
                     (GUI_MIRROR_Y * LCD_GetMirrorY()) |
                     (GUI_SWAP_XY  * LCD_GetSwapXY()) ;
  GUI_TOUCH_SetOrientation(TouchOrientation);
  //
  // Calibrate touch screen
  //
  GUI_TOUCH_Calibrate(GUI_COORD_X, 0, 240, TOUCH_AD_TOP , TOUCH_AD_BOTTOM);
  GUI_TOUCH_Calibrate(GUI_COORD_Y, 0, 320, TOUCH_AD_LEFT, TOUCH_AD_RIGHT);
}

Runtime calibration

In practice the exact values for the configuration file can be determined only for one touch panel. Because there are small differences between the parts of a series it could be very needful to calibrate each device at run-time. This can be done by using the function GUI_TOUCH_Calibrate(). The Sample folder contains the example TOUCH_Calibrate.c which shows, how a touch screen can be calibrated at run time:

Hardware routines

The following four hardware-dependent functions need to be added to your project if you use the driver supplied with emWin, as they are called by GUI_TOUCH_Exec() when polling the touch-panel. A suggested place is in the file GUI_X.c. These functions are as follows:

Routine Description
GUI_TOUCH_X_ActivateX() Prepares measurement for Y-axis.
GUI_TOUCH_X_ActivateY() Prepares measurement for X-axis.
GUI_TOUCH_X_MeasureX() Returns the X-result of the A/D converter.
GUI_TOUCH_X_MeasureY() Returns the Y-result of the A/D converter.

GUI_TOUCH_X_ActivateX(), GUI_TOUCH_X_ActivateY()

Description

These routines are called from GUI_TOUCH_Exec() to activate the measurement of the X- and the Y-axes. GUI_TOUCH_X_ActivateX() switches on the measurement voltage to the X-axis; GUI_TOUCH_X_ActivateY() switches on the voltage to the Y-axis. Switching on the voltage in X means the value for the Y-axis can be measured and vice versa.

Prototypes

void GUI_TOUCH_X_ActivateX(void);
void GUI_TOUCH_X_ActivateY(void);

GUI_TOUCH_X_MeasureX(), GUI_TOUCH_X_MeasureY()

Description

These routines are called from GUI_TOUCH_Exec() to return the measurement values from the A/D converter for the X- and the Y-axes.

Prototypes

int GUI_TOUCH_X_MeasureX(void);
int GUI_TOUCH_X_MeasureY(void);

Example implementation

The following shows an example implementation of the touch hardware routines for a Mitsubishi M16C/80 controller:

void GUI_TOUCH_X_ActivateX(void) {
  U8 Data;
  asm("fclr i");                  /* Disable interrupts            */
  Data  = P10;                    /* Read port data                */
  Data |=   (1 << 2) | (1 << 3);  /* Switch on  power in X
                                     and  enable measurement in Y  */
  Data &= ~((1 << 4) | (1 << 5)); /* Switch off power in Y
                                     and disable measurement in X  */
  P10   = Data;                   /* Write port data               */
  asm("fset i");                  /* Enable interrupts             */
}

void GUI_TOUCH_X_ActivateY(void) {
  U8 Data;
  asm("fclr i");                  /* Disable interrupts            */
  Data  = P10;                    /* Read port data                */
  Data |=   (1 << 5) | (1 << 4);  /* Switch on power in Y
                                     and  enable measurement in X  */
  Data &= ~((1 << 3) | (1 << 2)); /* Switch off power in X
                                     and disable measurement in Y  */
  P10   = Data;                   /* Write port data               */
  asm("fset i");                  /* Enable interrupts             */
}

static void ReadADCx(int channel) {
  ADCON0  = channel               /* Select channel 0-7            */
           | (0 << 3)             /* One shot mode                 */
           | (0 << 6)                 /* A-D conversion start (0=stop) */
           | (0 << 7);            /* FAD/4 select                  */
  ADCON1  =  (0 << 0)             /* A-D sweep select (XX)         */
           | (0 << 2)             /* No sweep mode                 */
           | (0 << 3)             /* 8 bit mode                    */
           | (0 << 4)             /* FAD4 select                   */
           | (1 << 5)             /* VRef connected                */
           | (0 << 6);            /* Anex0/1 not used              */
  ADCON2  =  (1 << 0);            /* Use example and hold          */
  ADIC    = 0;                    /* Reset IR flag                 */
  ADCON0 |= (1 << 6);             /* Start conversion              */
  while ((ADIC & (1 << 3)) == 0); /* Wait for end of conversion    */
  ADCON0 &= ~(6 << 0);            /* Start conversion = 0          */
}

int GUI_TOUCH_X_MeasureX(void) {
  ReadADCx(0);
  return AD0;
}

int GUI_TOUCH_X_MeasureY(void) {
  ReadADCx(1);
  return AD1;
}

Driver API for analog touch screens

The table below lists the available analog touch screen driver routines in alphabetical order. These functions only apply if you are using the driver included with emWin.

Routine Description
GUI_TOUCH_Calibrate() Changes the calibration.
GUI_TOUCH_Exec() Activates the measurement of the X- and Y-axes; needs to be called about 100 times/second.
GUI_TOUCH_SetOrientation() Sets the logical display orientation.

Configuring the analog touch-screen driver

The touch screen driver is completely run-time configurable. GUI_TOUCH_Calibrate() should be used to specify the physical values returned by the A/D converter for 2 positions per axis. If the display needs to be turned or mirrored, GUI_TOUCH_SetOrientation() can be used to set a new orientation without changing anything at the hardware routines.

Configuring the touch screen should be done before emWin manages any touch input.

Example
#define TOUCH_AD_LEFT   0x3c0
#define TOUCH_AD_RIGHT  0x034
#define TOUCH_AD_TOP    0x3b0
#define TOUCH_AD_BOTTOM 0x034

Orientation = (GUI_MIRROR_X * LCD_GetMirrorXEx(0)) |
              (GUI_MIRROR_Y * LCD_GetMirrorYEx(0)) |
              (GUI_SWAP_XY  * LCD_GetSwapXYEx (0));
GUI_TOUCH_SetOrientation(Orientation);
GUI_TOUCH_Calibrate(GUI_COORD_X, 0, 239, TOUCH_AD_LEFT, TOUCH_AD_RIGHT);
GUI_TOUCH_Calibrate(GUI_COORD_Y, 0, 319, TOUCH_AD_TOP,  TOUCH_AD_BOTTOM);

Joystick input example

The following example shows how the pointer input device API can be used to process the input from a joystick:

/*********************************************************************
*
*         _JoystickTask
*
* Purpose:
*   Periodically read the Joystick and inform emWin using
*   GUI_PID_StoreState.
*   It supports dynamic acceleration of the pointer.
*   The Joystick is a simple, standard 5 switch (digital) type.
*
*/
static void _JoystickTask(void) {
  GUI_PID_STATE State;
  int Stat;
  int StatPrev = 0;
  int TimeAcc = 0;   // Dynamic acceleration value
  int xMax, yMax;
 
  xMax = LCD_GetXSize() - 1;
  yMax = LCD_GetYSize() - 1;
  while (1) {
    Stat = HW_ReadJoystick();
    //
    // Handle dynamic pointer acceleration
    //
    if (Stat == StatPrev) {
      if (TimeAcc < 10) {
        TimeAcc++;
      }
    } else {
      TimeAcc = 1;
    }
    if (Stat || (Stat != StatPrev)) {
      //
      // Compute the new coordinates
      //
      GUI_PID_GetState(&State);
      if (Stat & JOYSTICK_LEFT) {
        State.x -= TimeAcc;
      }
      if (Stat & JOYSTICK_RIGHT) {
        State.x += TimeAcc;
      }
      if (Stat & JOYSTICK_UP) {
        State.y -= TimeAcc;
      }
      if (Stat & JOYSTICK_DOWN) {
        State.y += TimeAcc;
      }
      //
      // Make sure coordinates are still in bounds
      //
      if (State.x < 0) {
        State.x = 0;
      }
      if (State.y < 0) {
        State.y = 0;
      }
      if (State.x >= xMax) {
        State.x = xMax;
      }
      if (State.y > yMax) {
        State.y = yMax;
      }
      //
      // Inform emWin
      //
      State.Pressed = (Stat & JOYSTICK_ENTER) ? 1: 0;
      GUI_PID_StoreState(&State);
      StatPrev = Stat;
    }
    OS_Delay(40);
  }
}