dbeaec4cdc
Migrate pages: * Delayed ACK and TCP Performance * TCP Network Performance * NxWM Threading * Framebuffer Character Driver From Confluence wiki to official wiki
383 lines
No EOL
14 KiB
ReStructuredText
383 lines
No EOL
14 KiB
ReStructuredText
============================
|
||
Framebuffer Character Driver
|
||
============================
|
||
|
||
.. warning::
|
||
Migrated from:
|
||
https://cwiki.apache.org/confluence/display/NUTTX/Framebuffer+Character+Driver
|
||
|
||
|
||
NX Graphics
|
||
===========
|
||
|
||
NuttX has supported higher level graphics for some time with
|
||
the OS's :doc:`/components/nxgraphics/index` and application
|
||
oriented :doc:`NxWidgets </applications/graphics/nxwidgets/index>` and the tiny
|
||
window manager :doc:`NxWM </applications/graphics/nxwm/index>`.
|
||
These are higher level in the sense that the primary
|
||
graphical function is to support windowing and control
|
||
of tools and toolbars within windows. These graphics
|
||
tools often do not meet the needs
|
||
of developers with very low end graphics and minimal display
|
||
requirements.
|
||
|
||
Figure 1
|
||
========
|
||
|
||
The framebuffer character driver, along with the option LCD
|
||
framebuffer interface, is an optional lighter-weight graphics interface.
|
||
|
||
.. image:: GraphicsInterfaces.png
|
||
|
||
Framebuffer Character Driver details
|
||
====================================
|
||
|
||
A `framebuffer character driver` has been recently been added
|
||
to bypass the complexity of `NX` and to provide a direct
|
||
application interface to the framebuffer graphic device.
|
||
The framebuffer buffer character devices, as with all
|
||
character devices, provides the interface to the graphics
|
||
device via stand POSIX VFS commands (``open()``, ``close()``,
|
||
``read()``, ``write()``, ``seek()``, ...), through IOCTL commands,
|
||
and for this driver via the ``mmap()`` function. These
|
||
interfaces are described below,
|
||
|
||
The framebuffer character driver is located in the NuttX
|
||
source tree at ``drivers/video/fb.c``. It is enabled in the
|
||
build with ``CONFIG_VIDEO_FB=y``. In order to register the
|
||
framebuffer driver, you will need to include logic in the
|
||
your board-specific start-up function that calls
|
||
``fb_register()`` That code sequence might look something
|
||
like:
|
||
|
||
.. code-block:: c
|
||
|
||
#include <nuttx/video/fb.h>
|
||
|
||
#ifdef CONFIG_VIDEO_FB
|
||
/* Initialize and register the simulated framebuffer driver */
|
||
|
||
ret = fb_register(0, 0);
|
||
if (ret < 0)
|
||
{
|
||
syslog(LOG_ERR, "ERROR: fb_register() failed: %d\n", ret);
|
||
}
|
||
#endif
|
||
|
||
The ``fb_register()`` function takes two parameters:
|
||
|
||
* `display`. The display number for the case of boards
|
||
supporting multiple displays or for hardware that supports
|
||
multiple layers (each layer is consider a display). Typically zero.
|
||
* `plane`. Identifies the color plane on hardware that supports
|
||
separate framebuffer "planes" for each color component.
|
||
Should be zero because no planar hardware is currently
|
||
supported by NuttX.
|
||
|
||
``fb_register()`` will register the framebuffer character device
|
||
at ``/dev/fb`` `N` where `N` is the display number if the devices
|
||
supports only a single plane. If the hardware supports
|
||
multiple color planes, then the device will be registered
|
||
at ``/dev/fb`` `N-M` where `N` is the again display number but `M`
|
||
is the display plane.
|
||
|
||
There is a simple example at ``apps/examples/fb`` that provides
|
||
an illustration of most of the following interfacing methods.
|
||
|
||
POSIX Interfaces
|
||
================
|
||
|
||
The interaction with the framebuffer character driver via POSIX
|
||
VFS interface calls is the same as for other character drivers.
|
||
The only aspect that might require some additional discussion
|
||
is the use of ``read()``, ``write()``, and ``seek()``.
|
||
|
||
* ``read()`` returns data from the framebuffer memory and
|
||
updates the file position based on the number of bytes read.
|
||
* ``write()`` puts data into the framebuffer memory and
|
||
also updates the file position.
|
||
|
||
That file position is initially set to the position
|
||
zero meaning the beginning of the framebuffer. It is
|
||
advanced each time you ``read()`` from or ``write()`` to the
|
||
framebuffer. Is also updated by ``seek()``:
|
||
|
||
* ``seek()`` sets the file position to any desired
|
||
location within the framebuffer.
|
||
|
||
The file position is in units of `bytes`. This can be
|
||
confusing because other positional data may be in units
|
||
`pixels`. Pixels have different `depth` in different displays,
|
||
that is, different graphic hardware may support pixels with
|
||
differing bits-per-pixel. The pixel depth can be obtained
|
||
using one of the IOCTL commands listed below. Since the file
|
||
position is in bytes, the bits-per-pixel must be taken account
|
||
when using ``read()``, ``write()``, and ``seek()``. The usual conversion
|
||
from pixels to bytes is:
|
||
|
||
.. code-block:: C
|
||
|
||
start_byte = (start_pixel * bits_per_pixel) >> 3;
|
||
end_byte = (end_pixel * bits_per_pixel + 7) >> 3;
|
||
|
||
While the framebuffer may be accessed with these POSIX interfaces,
|
||
a more typical way of interacting with the framebuffer from an
|
||
application would involve use of ``mmap()`` as described below.
|
||
|
||
IOCTL Commands
|
||
==============
|
||
|
||
* ``FBIOGET_VIDEOINFO``. Get color plane info. Its argument is
|
||
pointer a writable instance of ``struct fb_videoinfo_s``:
|
||
|
||
.. code-block:: c
|
||
|
||
struct fb_videoinfo_s
|
||
{
|
||
uint8_t fmt; /* see FB_FMT_* */
|
||
fb_coord_t xres; /* Horizontal resolution in pixel columns */
|
||
fb_coord_t yres; /* Vertical resolution in pixel rows */
|
||
uint8_t nplanes; /* Number of color planes supported */
|
||
};
|
||
|
||
* ``FBIOGET_PLANEINFO``. Get video plane info. It received
|
||
a pointer to a writable instance of ``struct fb_planeinfo_s`` as its argument:
|
||
|
||
.. code-block:: C
|
||
|
||
struct fb_planeinfo_s
|
||
{
|
||
FAR void *fbmem; /* Start of frame buffer memory */
|
||
uint32_t fblen; /* Length of frame buffer memory in bytes */
|
||
fb_coord_t stride; /* Length of a line in bytes */
|
||
uint8_t display; /* Display number */
|
||
uint8_t bpp; /* Bits per pixel */
|
||
};
|
||
|
||
* ``FBIOGET_CMAP`` and ``FBIOPUT_CMAP``. Get/Put RGB color mapping.
|
||
These commands are available only if the hardware and
|
||
framebuffer driver support color mapping (``CONFIG_FB_CMAP=y``).
|
||
They each take a pointer to an instance of ``struct fb_cmap_s``
|
||
as an argument (writeable for ``FBIOGET_CMAP`` and read-only
|
||
for ``FBIOPUT_CMAP``).
|
||
|
||
.. code-block:: c
|
||
|
||
#ifdef CONFIG_FB_CMAP
|
||
struct fb_cmap_s
|
||
{
|
||
uint16_t first; /* Offset offset first color entry in tables */
|
||
uint16_t len; /* Number of color entries in tables */
|
||
|
||
/* Tables of color component. Any may be NULL if not used */
|
||
|
||
uint8_t *red; /* Table of 8-bit red values */
|
||
uint8_t *green; /* Table of 8-bit green values */
|
||
uint8_t *blue; /* Table of 8-bit blue values */
|
||
#ifdef CONFIG_FB_TRANSPARENCY
|
||
uint8_t *transp; /* Table of 8-bit transparency */
|
||
#endif
|
||
};
|
||
#endif
|
||
|
||
* ``FBIOGET_CURSOR``. Get cursor attributes. This command is
|
||
available only if the hardware and framebuffer driver
|
||
support cursors (``CONFIG_FB_HWCURSOR=y``). It take a pointer
|
||
to a writable instance of ``struct fb_cursorattrib_s``:
|
||
|
||
.. code-block:: c
|
||
|
||
#ifdef CONFIG_FB_HWCURSOR
|
||
#ifdef CONFIG_FB_HWCURSORIMAGE
|
||
struct fb_cursorimage_s
|
||
{
|
||
fb_coord_t width; /* Width of the cursor image in pixels */
|
||
fb_coord_t height /* Height of the cursor image in pixels */
|
||
const uint8_t *image; /* Pointer to image data */
|
||
};
|
||
#endif
|
||
|
||
struct fb_cursorpos_s
|
||
{
|
||
fb_coord_t x; /* X position in pixels */
|
||
fb_coord_t y; /* Y position in rows */
|
||
};
|
||
|
||
#ifdef CONFIG_FB_HWCURSORSIZE
|
||
struct fb_cursorsize_s
|
||
{
|
||
fb_coord_t h; /* Height in rows */
|
||
fb_coord_t w; /* Width in pixels */
|
||
};
|
||
#endif
|
||
|
||
struct fb_cursorattrib_s
|
||
{
|
||
#ifdef CONFIG_FB_HWCURSORIMAGE
|
||
uint8_t fmt; /* Video format of cursor */
|
||
#endif
|
||
struct fb_cursorpos_s pos; /* Current cursor position */
|
||
#ifdef CONFIG_FB_HWCURSORSIZE
|
||
struct fb_cursorsize_s mxsize; /* Maximum cursor size */
|
||
struct fb_cursorsize_s size; /* Current size */
|
||
#endif
|
||
};
|
||
#endif
|
||
|
||
* ``FBIOPUT_CURSOR``. Set cursor attributes. This command is
|
||
available only if the hardware and framebuffer driver
|
||
support cursors (``CONFIG_FB_HWCURSOR=y``). It take a
|
||
pointer to a writable instance of ``struct fb_setcursor_s``:
|
||
|
||
.. code-block:: c
|
||
|
||
#ifdef CONFIG_FB_HWCURSOR
|
||
struct fb_setcursor_s
|
||
{
|
||
uint8_t flags; /* See FB_CUR_* definitions */
|
||
struct fb_cursorpos_s pos; /* Cursor position */
|
||
#ifdef CONFIG_FB_HWCURSORSIZE
|
||
struct fb_cursorsize_s size; /* Cursor size */
|
||
#endif
|
||
#ifdef CONFIG_FB_HWCURSORIMAGE
|
||
struct fb_cursorimage_s img; /* Cursor image */
|
||
#endif
|
||
};
|
||
#endif
|
||
|
||
* ``FBIO_UPDATE``. This IOCTL command updates a rectangular region
|
||
in the framebuffer. Some hardware requires that there be
|
||
such a notification when a change is made to the
|
||
framebuffer (see, for example, the discussion of LCD drivers
|
||
below). This IOTCL command is if ``CONFIG_NX_UPDATE=y`` is
|
||
defined. It takes a pointer to a read-only instance of
|
||
``struct nxgl_rect_s`` that describes the region to be updated:
|
||
|
||
.. code-block:: c
|
||
|
||
struct nxgl_rect_s
|
||
{
|
||
struct nxgl_point_s pt1; /* Upper, left-hand corner */
|
||
struct nxgl_point_s pt2; /* Lower, right-hand corner */
|
||
};
|
||
|
||
``mmap()``
|
||
==========
|
||
|
||
Above we talked about using ``read()``, ``write()``, and ``seek()`` to
|
||
access the framebuffer. The simplest way to access the
|
||
framebuffer, however, is by using the ``mmap()`` to map
|
||
the framebuffer memory into the application memory
|
||
space. The following ``mmap()`` command, for example, can
|
||
be used to obtain a pointer to a read-able, write-able
|
||
copy of the framebuffer:
|
||
|
||
.. code-block:: c
|
||
|
||
FAR void *fbmem;
|
||
|
||
fbmem = mmap(NULL, fblen, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_FILE, fd, 0);
|
||
if (state.fbmem == MAP_FAILED)
|
||
{
|
||
/* Handle failure */
|
||
...
|
||
}
|
||
|
||
printf("Mapped FB: %p\n", fbmem);
|
||
|
||
Where fd is the file descriptor of the opened framebuffer
|
||
character driver and ``fblen`` was obtained via an IOCTL
|
||
command as described above. NOTE that the framebuffer
|
||
buffer pointer is also available within the values
|
||
returned by the IOCTL commands. The address is a
|
||
kernel memory address and may not be valid in all
|
||
build configurations. Hence, ``mmap()`` is the preferred,
|
||
portable way to get the framebuffer address.
|
||
|
||
Framebuffer vs. LCD Graphics Drivers
|
||
====================================
|
||
|
||
Framebuffer graphics drivers are very common in high-end CPUs
|
||
but most low-end, embedded hardware will not support a
|
||
framebuffer.
|
||
|
||
A framebuffer graphics driver supports a region of memory
|
||
that is shared both by the software and by the graphics
|
||
hardware. Any modification to the framebuffer memory
|
||
results in a corresponding modification on the display
|
||
with no intervening software interaction. Some video
|
||
memory is dual ported to support concurrent video processor
|
||
and application processor accesses; or perhaps the LCD
|
||
peripheral just constantly DMAs the framebuffer memory
|
||
to the graphics hardware.
|
||
|
||
Most low-end embedded MCUs have a much simpler hardware
|
||
interface: The interface to the LCD may be through a simple
|
||
parallel interface or, more commonly, through a slower serial
|
||
interface such as SPI. In order to support such low-end
|
||
hardware with the framebuffer character driver, a special
|
||
software layer called the `Framebuffer LCD Front End` has
|
||
been developed. This is the topic of the next paragraph.
|
||
|
||
LCD Framebuffer Front-End
|
||
=========================
|
||
|
||
The `LCD Framebuffer Front-End` provides a standard NuttX
|
||
framebuffer interface, but works on top of a standard
|
||
parallel or serial LCD driver. It provides the framebuffer,
|
||
the framebuffer interface, and the hooks to adapt the LCD
|
||
driver. The LCD framebuffer front-end can be found in the
|
||
NuttX source tree at ``drivers/lcd/lcd_framebuffer.c``.
|
||
|
||
In order to provide updates to the LCD hardware after
|
||
updates to the framebuffer, the LCD framebuffer front-end
|
||
must be notified when significant changes to the framebuffer
|
||
have been made. This notification is supported when
|
||
``CONFIG_NX_UPDATE=y`` is defined in the configuration. In
|
||
this case, the LCD framebuffer front-end will support
|
||
the special. OS-internal interface function ``nx_notify_rectangle()``
|
||
which defines the rectangular region in the framebuffer that
|
||
has been changed. In response to a call to ``nx_notify_rectangle()``
|
||
will use the lower-level LCD interface to update only that
|
||
rectangular region on the display.
|
||
|
||
This kind of update for standard LCD drivers is very efficient:
|
||
It is usually more efficient to update a region on the
|
||
display than it is for form a complex image with text and
|
||
line drawing; the updated region seems to update very
|
||
quickly because of that. In fact, many of the low-end
|
||
LCD drivers already include an internal framebuffer to
|
||
support this style of LCD update.
|
||
|
||
When used with LCD character driver, the ``nx_notify_rectangle()``
|
||
function will be called by the character river in response
|
||
to the ``FBIO_UPDATE IOCTL`` command.
|
||
|
||
Another advantage of the framebuffer, both the LCD internal
|
||
framebuffer and the framebuffer character driver, is
|
||
that super-efficient reading of the LCD display memory:
|
||
The LCD display memory is not read at all! The read is
|
||
from the copy in the framebuffer.
|
||
|
||
Of course, using both an LCD internal framebuffer with the
|
||
framebuffer character drivers is wasteful; one framebuffer
|
||
is enough!
|
||
|
||
As a caution, it is important to remember that a framebuffer
|
||
can be quite large. For example, a 480x320 display with
|
||
16-bit RGB pixels would require an allocated framebuffer
|
||
of size 300 KiB. This is inappropriate with most small
|
||
MCUs (unless they support external memory). For tiny displays,
|
||
such as 128x64 1-bit monochromatic displays, the framebuffer
|
||
memory usage is not bad: 1 KiB in that example.
|
||
|
||
Framebuffer Graphics Library
|
||
============================
|
||
|
||
Now the missing part is some kind of application-space
|
||
framebuffer graphics library. The NuttX framebuffer
|
||
driver is superficially similar to the Linux framebuffer
|
||
driver so there is a lot of support for Linux
|
||
framebuffer graphics support that should be easily ported to
|
||
NuttX – Perhaps DirectFB would be an GPL option? SDL with its
|
||
MIT license might be a more compatible source for such a port. |