==================== 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: #. Whenever it is necessary to hold all the pixels that would be used to drive a video display. This includes: #. Graphics libraries that directly access the underlying framebuffer; #. Advanced UIs (e.g. alpha blending) that need to read back the image data; #. 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: #. Get an instance of ``struct fb_vtable_s`` from the hardware-specific frame buffer device driver, and #. Provide that instance to the initialization method of the higher-level device driver. .. _genericlcdfb: 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. #. ``include/nuttx/video/fb.h`` provides all structures and APIs needed to work with frame buffer drivers: #. ``drivers/video/fb.c`` is the higher-level device driver. An instance of ``struct fb_vtable_s`` will be provided to it; #. ``fb_register`` registers the framebuffer character device at ``/dev/fbN`` where N is the display number; #. It also provides the prototype of ``up_fbinitialize``, which may be defined by: #. An specific device into ``arch//src/`` directory; #. 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; #. Let's consider we are using the LCD framebuffer (``CONFIG_LCD_FRAMEBUFFER = y``): #. This interface implements the ``up_fbinitialize`` which: #. Provides the instance of ``struct fb_vtable_s`` (a member of ``struct lcdfb_dev_s``); #. Calls ``board_lcd_initialize`` and ``board_lcd_getdev`` LCD-specific functions. These functions are defined in ``boards////src`` and prototyped in ``include/nuttx/board.h``; #. Finally, the LCD screen drivers are usually available at ``drivers/lcd/`` and implement the callbacks defined at ``include/nuttx/lcd/lcd.h``: #. ``include/nuttx/lcd/lcd.h`` provides structures and APIs needed to work with LCD screens, whereas using the framebuffer adapter or the :doc:`lcd`; VSYNC ====== Vertical synchronization (VSync) synchronizes the frame rate of an application's graphics with the refresh rate of the display, helping to establish stability. If not synchronized, it may cause screen tearing, which is the effect of the image appearing to have horizontal jagged edges or ghosting across the entire screen. VSYNC Offset ------------ During the VSYNC event, the screen starts displaying frame N while the renderer begins compositing window for frame N+1. The application processes waiting input and generates frame N+2. When the renderer has a short rendering time, it can cause a delay of almost two frames from the end of rendering to the completion of screen display. To solve this problem, ``FBIOSET_VSYNCOFFSET`` can be used to set the VSYNC offset time (in microseconds) and reduce the delay from input device to screen using the VSYNC offset. Examples ======== Examples apply to specific cases of the :ref:`genericlcdfb`: .. _ttgotdisplayesp32_fb: 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: .. code-block:: c #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: .. code-block:: c ioctl(state.fd, FBIO_UPDATE, (unsigned long)((uintptr_t)&fb_area)); NuttX Simulator ---------------- :doc:`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 :ref:`ttgotdisplayesp32_fb`; * ``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 :doc:`lcd`.