Frame Buffer Drivers

A framebuffer is a memory-mapped buffer that represents all the pixels necessary to drive a video display.

The Frame Buffer driver is intended to be used in the following scenarios:

  1. Whenever it is necessary to hold all the pixels that would be used to drive a video display. This includes:

    1. Graphics libraries that directly access the underlying framebuffer;

    2. Advanced UIs (e.g. alpha blending) that need to read back the image data;

  2. Applications that expect the framebuffer to exist;

Binding

LCD and frame buffer drivers usually are not directly accessed by user code, but are usually bound to another, higher-level device driver. In general, the binding sequence is:

  1. Get an instance of struct fb_vtable_s from the hardware-specific frame buffer device driver, and

  2. Provide that instance to the initialization method of the higher-level device driver.

Generic LCD Frame Buffer

This example will walk through the path from userspace to hardware-specific details on how an LCD screen is bound to a framebuffer.

  1. include/nuttx/video/fb.h provides all structures and APIs needed to work with frame buffer drivers:

    1. drivers/video/fb.c is the higher-level device driver. An instance of struct fb_vtable_s will be provided to it;

    2. fb_register registers the framebuffer character device at /dev/fbN where N is the display number;

    3. It also provides the prototype of up_fbinitialize, which may be defined by:

      1. An specific device into arch/<arch>/src/<chip> directory;

      2. By the LCD framebuffer adapter in drivers/lcd/lcd_framebuffer.c, which provides an intermediary interface between the Frame Buffer Driver and the LCD screen drivers;

  2. Let’s consider we are using the LCD framebuffer (CONFIG_LCD_FRAMEBUFFER = y):

    1. This interface implements the up_fbinitialize which:

      1. Provides the instance of struct fb_vtable_s (a member of struct lcdfb_dev_s);

      2. Calls board_lcd_initialize and board_lcd_getdev LCD-specific functions. These functions are defined in boards/<arch>/<chip>/<board>/src and prototyped in include/nuttx/board.h;

  3. Finally, the LCD screen drivers are usually available at drivers/lcd/ and implement the callbacks defined at include/nuttx/lcd/lcd.h:

    1. include/nuttx/lcd/lcd.h provides structures and APIs needed to work with LCD screens, whereas using the framebuffer adapter or the LCD Character Drivers;

Examples

Examples apply to specific cases of the Generic LCD Frame Buffer:

TTGO T-Display ESP32 board

This board contains an ST7789 TFT Display (135x240). By selecting the ttgo_t_display_esp32:lvgl_fb config, the lvgldemo example will be built with the framebuffer interface.

  • boards/xtensa/esp32/ttgo_t_display_esp32/src/esp32_bringup.c registers the framebuffer driver:

#ifdef CONFIG_VIDEO_FB
  ret = fb_register(0, 0);
  if (ret < 0)
    {
      syslog(LOG_ERR, "ERROR: Failed to initialize Frame Buffer Driver.\n");
    }
#endif
  • up_fbinitialize from the frame buffer adapter will then be called as CONFIG_LCD_FRAMEBUFFER = y:

    • board_lcd_initialize and board_lcd_getdev are defined at boards/xtensa/esp32/common/src/esp32_st7789.c:

      • board_lcd_initialize initializes the LCD hardware on the board by defining the SPI interface which is connected to the display controller;

      • board_lcd_getdev calls the st7789_lcdinitialize and returns a reference to the LCD object for the specified LCD;

      • st7789_lcdinitialize is part of the LCD screen driver at drivers/lcd/st7789.c;

  • The LVGL demo application (lvgldemo) makes use of the ioctl system call to trigger a FBIO_UPDATE request to the higher-level device driver to refresh the LCD screen with framebuffer data:

ioctl(state.fd, FBIO_UPDATE, (unsigned long)((uintptr_t)&fb_area));

NuttX Simulator

NuttX Simulator provides a X11-based framebuffer driver to simulate the framebuffer usage into a X11-compatible host.

By selecting the sim:lvgl_fb config, the lvgldemo example will be built with the framebuffer driver.

  • boards/sim/sim/sim/src/sim_bringup.c registers the framebuffer driver the same way TTGO T-Display ESP32 board;

  • arch/sim/src/sim/up_framebuffer.c and arch/sim/src/sim/up_x11framebuffer.c will be built as CONFIG_SIM_FRAMEBUFFER = y and CONFIG_SIM_X11FB = y are set, respectively;

    • up_framebuffer.c provides up_fbinitialize and,

    • calls up_x11initialize from up_x11framebuffer.c that initializes a X11-based window as a framebuffer. This is the underlying “driver”.

  • The LVGL demo application (lvgldemo) makes use of the ioctl system call to trigger a FBIO_UPDATE request to the higher-level device driver in order to refresh the LCD screen with framebuffer data as usual;

Warning

One must consider that framebuffer requires that the entire display’s pixels to be represented. Considering a 320x480 @RGB565 LCD screen, that would be 300KiB, which it’d be too much for a memory-constrained device.

However, when memory is not a constraint, framebuffer may offer applications a faster way to update display contents once writing to the RAM-mapped buffer is faster than doing multiple SPI transfers.

For memory-constrained devices, consider using LCD Character Drivers.