Display Driver Model

From uGFX Wiki
Jump to: navigation, search

A uGFX display driver can fall into one of three models. Unlike desktop graphics processors, embedded LCD's often have a very different access model that means that traditional graphics libraries do not support them efficiently, if at all.

Some graphics controllers may require different models in different situations. As an example, the Nokia 6610 GE8 controller supports the window model in normal orientation but due to bugs in the controller requires a point and block model in any other orientation.

Framebuffer Model

This is the model that most graphics libraries support and is most suitable for advanced graphics processors. It requires that the graphics hardware supplies a framebuffer which is a block of RAM that is pixel addressable as normal memory from the CPU side. The graphics hardware then updates the display in the background by seeing the changes that the CPU makes to the framebuffer. It is also the simplest type of new hardware to support in uGFX.

Many other graphics libraries try to support other types of hardware by allocating system RAM to a virtual framebuffer and then providing a sync call to flush the framebuffer to the real display. This has a number of issues...

  • It allocates large amounts of system RAM which is often a precious resource in an embedded environment, and
  • The sync call is usually very inefficient as either the entire display must be updated or a difference comparison must be made.

There may be other reasons that a sync is needed (eg to allow updating of the display only during vertical refresh) so uGFX still supports a sync call. It is strongly suggested however that unless your graphics hardware supports a native framebuffer that you do not use this model.

uGFX supports this model of driver through the GDISP framebuffer driver. Anyone interfacing to new hardware need only write a new board file. Copy /drivers/gdisp/framebuffer/board_framebuffer_template.h to board_framebuffer.h in your project directory and modify it to suit your hardware. Existing examples can be found in the predefined board area such as support for the Linux framebuffer in /boards/base/Linux-Framebuffer.

Required Routines:

  • board_init() - Initialise the framebuffer and return its address and the display properties

Optional Routines:

  • board_flush() - Flush (sync) the framebuffer to the display
  • board_backlight() - Adjust the display backlight
  • board_contrast() - Adjust the display contrast
  • board_power() - Enter/Leave sleep modes

Window Model

Most embedded LCD's use this model of controller. Unfortunately most graphics libraries do not support these controllers efficiently. One of the main reasons for writing uGFX was to support these types of controllers efficiently.

In this model the hardware provides a programmable window area. This window area is written to by sequentially sending pixels to the graphics controller. When the pixels reach the end of a line in the window the controller wraps to the start of the next line in the window. When it reaches the bottom of the window it may (or may not) wrap back to the beginning of the window.

Reading from the display is often not supported, and if it is it is by the same windowed method.

As the display surface is not RAM addressable and the physical connections are often via a slow bus (at least compared to RAM addressing) such as SPI, I2C or byte parallel, the reading and writing speed can be slow. This means that efficiency of the drawing operations is very important and completely different methods of drawing need to be used compared to a framebuffer. uGFX automatically handles all of these differences.

Software only screen rotation is not possible with these controllers (unlike a framebuffer). Some hardware support is required. There are two possible ways this can be done, by rotating the way the cursor is moved within the drawing window, or by rotating the display itself relative to the internal framebuffer. By policy we prefer that the first method is implemented if the controller supports both. This preference allows retention of existing display contents with the rotation only affecting new drawing operations (more flexible for the end-user application).

To write a driver of this type, copy an existing driver that is similar and modify it to suit. To allow portability to other boards that use the same controller but where the electrical interface might be different, the hardware interface logic should be separated from the controller logic into a separate board file. See the existing drivers for more details.

Required Routines:

  • gdisp_lld_init() - Initialise the controller and display
  • gdisp_lld_write_start() - Start a windowed write operation
  • gdisp_lld_write_color() - Send one pixel to the current window at the current position
  • gdisp_lld_write_stop() - Stop a windowed write operation

Optional Routines:

  • gdisp_lld_write_pos() - Set the current position within the write window (increases drawing efficiency)
  • gdisp_lld_read_start() - Start a windowed read operation
  • gdisp_lld_read_color() - Read one pixel from the current window at the current position
  • gdisp_lld_read_stop() - Stop a windowed read operation
  • gdisp_lld_set_clip() - Set a hardware clipping region. All writes are clipped to this area (regardless of the current window)
  • gdisp_lld_control() - Handle backlight, contrast, screen orientation and any driver specific control commands
  • gdisp_lld_query() - Query some driver specific value
  • Any Point and Block Model routines as described below.

Point and Block Model

In this model the controller provides basic drawing operations such as set-point, fill-block, fill-block-from-image. Many of the same considerations apply as for the window model above. Reading from the display is often not supported.

Drivers may mix routines from this model into the window model above. If a specific set-point, fill-block or fill-block-from-image routine is provided in a window model driver it will be used in preference to the general window model calls above. When calls are mixed like this the driver is still considered to be a window model driver. For example, a controller may have a more efficient set-point command that can be used in preference to a windowed single pixel write.

Required Routines:

  • gdisp_lld_init() - Initialise the controller and display
  • gdisp_lld_draw_pixel() - Set a single pixel

Optional Routines:

  • gdisp_lld_fill_area() - Fill a block with a color
  • gdisp_lld_blit_area() - Fill a block from an array of pixels
  • gdisp_lld_vertical_scroll() - Scroll up or down a windowed area of the display
  • gdisp_lld_get_pixel_color() - Get the color of a single pixel
  • gdisp_lld_set_clip() - Set a hardware clipping region. All writes are clipped to this area.
  • gdisp_lld_control() - Handle backlight, contrast, screen orientation and any driver specific control commands
  • gdisp_lld_query() - Query some driver specific value

Board Files

Board files are used as the interface between the display driver (software) and the actual display controller (hardware). The board file implements the functions that are used to generate the electrical/physical signals using the microcontroller's peripherals. Each driver provides its own board file template which can be found in the driver directory (/drivers/gdisp/*). The user of the driver has to copy the board file template from the driver directory to his project and implement the empty functions accordingly. The board file API for a Window Model or Point and Block Model GDISP driver looks like the following (although it may vary slightly from driver to driver):

static void     init_board(GDisplay *g);
static void     post_init_board(GDisplay *g);
static void     setpin_reset(GDisplay *g, bool_t state);
static void     set_backlight(GDisplay *g, uint8_t percent);
static void     acquire_bus(GDisplay *g);
static void     release_bus(GDisplay *g);
static void     write_index(GDisplay *g, uint16_t index);
static void     write_data(GDisplay *g, uint16_t data);
static void     setreadmode(GDisplay *g);
static void     setwritemode(GDisplay *g);
static uint16_t read_data(GDisplay *g);

The post_init_board() routine is sometimes used to increase the bus speed after successful initialization of the controller by the driver.