subsys: add monochrome character framebuffer

Add monochrome character framebuffer for monochrome
graphic dot matrix displays and electrophoretic displays.

These displays are mostly monochrome and can only display
black and some other color, for example white. Typically,
a byte controls 8 pixels, arranged vertically or horizontally
depending on the controller or settings.
The API is not suitable to display graphics, the purpose is
to display text or symbols. It is possible to use several fonts.
A font can also consist of graphic symbols only and thus,
for example, enable the realization of a menu.

Signed-off-by: Johann Fischer <j.fischer@phytec.de>
This commit is contained in:
Johann Fischer 2018-07-19 17:43:39 +02:00 committed by Anas Nashif
commit f531e0d62e
8 changed files with 5691 additions and 0 deletions

162
include/display/cfb.h Normal file
View file

@ -0,0 +1,162 @@
/*
* Copyright (c) 2018 PHYTEC Messtechnik GmbH
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
* @brief Public Monochrome Character Framebuffer API
*/
#ifndef __CFB_H__
#define __CFB_H__
#include <device.h>
#include <display.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Display Drivers
* @defgroup display_interfaces Display Drivers
* @{
* @}
*/
/**
* @brief Public Monochrome Character Framebuffer API
* @defgroup monochrome_character_framebuffer Monochrome Character Framebuffer
* @ingroup display_interfaces
* @{
*/
enum cfb_display_param {
CFB_DISPLAY_HEIGH = 0,
CFB_DISPLAY_WIDTH,
CFB_DISPLAY_PPT,
CFB_DISPLAY_ROWS,
CFB_DISPLAY_COLS,
};
enum cfb_font_caps {
CFB_FONT_MONO_VPACKED = BIT(0),
CFB_FONT_MONO_HPACKED = BIT(1),
};
struct cfb_font {
const void *data;
u8_t width;
u8_t height;
enum cfb_font_caps caps;
u8_t first_char;
u8_t last_char;
};
/**
* @brief Macro for creating a font entry.
*
* @param _name Name of the font entry.
* @param _width Width of the font in pixels
* @param _height Heigth of the font in pixels.
* @param _caps Font capabilities.
* @param _data Raw data of the font.
* @param _fc Character mapped to first font element.
* @param _lc Character mapped to last font element.
*/
#define FONT_ENTRY_DEFINE(_name, _width, _height, _caps, _data, _fc, _lc) \
static const struct cfb_font _name \
__attribute__ ((section(".font_entry."))) __attribute__((used)) = \
{ \
.width = _width, \
.height = _height, \
.caps = _caps, \
.data = _data, \
.first_char = _fc, \
.last_char = _lc, \
}
/**
* @brief Print a string into the framebuffer.
*
* @param dev Pointer to device structure for driver instance
* @param str String to print
* @param x Position in X direction of the beginning of the string
* @param y Position in Y direction of the beginning of the string
*
* @return 0 on success, negative value otherwise
*/
int cfb_print(struct device *dev, char *str, u16_t x, u16_t y);
/**
* @brief Clear framebuffer.
*
* @param dev Pointer to device structure for driver instance
* @param clear_display Clear the display as well
*
* @return 0 on success, negative value otherwise
*/
int cfb_framebuffer_clear(struct device *dev, bool clear_display);
/**
* @brief Finalize framebuffer and write it to display RAM,
* invert or reorder pixels if necessary.
*
* @param dev Pointer to device structure for driver instance
*
* @return 0 on success, negative value otherwise
*/
int cfb_framebuffer_finalize(struct device *dev);
/**
* @brief Get display parameter.
*
* @param dev Pointer to device structure for driver instance
* @param cfb_display_param One of the display parameters
*
* @return Display parameter value
*/
int cfb_get_display_parameter(struct device *dev, enum cfb_display_param);
/**
* @brief Set font.
*
* @param dev Pointer to device structure for driver instance
* @param idx Font index
*
* @return 0 on success, negative value otherwise
*/
int cfb_framebuffer_set_font(struct device *dev, u8_t idx);
/**
* @brief Get font size.
*
* @param dev Pointer to device structure for driver instance
* @param idx Font index
* @param width Pointers to the variable where the font width will be stored.
* @param height Pointers to the variable where the font height will be stored.
*
* @return 0 on success, negative value otherwise
*/
int cfb_get_font_size(struct device *dev, u8_t idx, u8_t *width, u8_t *height);
/**
* @brief Initialize Character Framebuffer.
*
* @param dev Pointer to device structure for driver instance
*
* @return 0 on success, negative value otherwise
*/
int cfb_framebuffer_init(struct device *dev);
#ifdef __cplusplus
}
#endif
/**
* @}
*/
#endif /* __CFB_H__ */

View file

@ -80,3 +80,10 @@
KEEP(*(SORT(.shell_root_cmd_*)));
__shell_root_cmds_end = .;
} GROUP_LINK_IN(ROMABLE_REGION)
SECTION_DATA_PROLOGUE(font_entry_sections, (OPTIONAL),)
{
__font_entry_start = .;
KEEP(*(SORT_BY_NAME(".font_entry.*")))
__font_entry_end = .;
} GROUP_LINK_IN(ROMABLE_REGION)

View file

@ -16,3 +16,4 @@ add_subdirectory(random)
add_subdirectory(storage)
add_subdirectory_ifdef(CONFIG_SETTINGS settings)
add_subdirectory_ifdef(CONFIG_PM_CONTROL_OS power)
add_subdirectory(fb)

View file

@ -36,3 +36,5 @@ source "subsys/settings/Kconfig"
source "subsys/app_memory/Kconfig"
source "subsys/power/Kconfig"
source "subsys/fb/Kconfig"

2
subsys/fb/CMakeLists.txt Normal file
View file

@ -0,0 +1,2 @@
zephyr_sources_ifdef(CONFIG_CHARACTER_FRAMEBUFFER cfb.c)
zephyr_sources_ifdef(CONFIG_CHARACTER_FRAMEBUFFER_USE_DEFAULT_FONTS cfb_fonts.c)

30
subsys/fb/Kconfig Normal file
View file

@ -0,0 +1,30 @@
# Kconfig - frame buffer configuration options
#
# Copyright (c) 2018 Phytec Messtechnik GmbH
#
# SPDX-License-Identifier: Apache-2.0
#
menu "Framebuffer"
config CHARACTER_FRAMEBUFFER
bool "Character framebuffer for dot matrix displays"
help
Character framebuffer for dot matrix displays.
if CHARACTER_FRAMEBUFFER
config CHARACTER_FRAMEBUFFER_USE_DEFAULT_FONTS
bool "Use default fonts"
default y
help
Use default fonts.
module = CFB
module-str = cfb
source "subsys/logging/Kconfig.template.log_config"
endif
endmenu

308
subsys/fb/cfb.c Normal file
View file

@ -0,0 +1,308 @@
/*
* Copyright (c) 2018 PHYTEC Messtechnik GmbH
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr.h>
#include <string.h>
#include <display/cfb.h>
#define LOG_LEVEL CONFIG_CFB_LOG_LEVEL
#include <logging/log.h>
LOG_MODULE_REGISTER(cfb);
extern const struct cfb_font __font_entry_start[0];
extern const struct cfb_font __font_entry_end[0];
struct char_framebuffer {
/** Pointer to a buffer in RAM */
u8_t *buf;
/** Size of the framebuffer */
u32_t size;
/** Pointer to the font entry array */
const struct cfb_font *fonts;
/** Display pixel format */
enum display_pixel_format pixel_format;
/** Display screen info */
enum display_screen_info screen_info;
/** Resolution of a framebuffer in pixels in X direction */
u8_t x_res;
/** Resolution of a framebuffer in pixels in Y direction */
u8_t y_res;
/** Number of pixels per tile, typically 8 */
u8_t ppt;
/** Number of available fonts */
u8_t numof_fonts;
/** Current font index */
u8_t font_idx;
/** Font kerning */
s8_t kerning;
/** Invertedj*/
bool inverted;
};
static struct char_framebuffer char_fb;
static inline u8_t *get_glyph_ptr(const struct cfb_font *fptr, char c)
{
if (fptr->caps & CFB_FONT_MONO_VPACKED) {
return (u8_t *)fptr->data +
(c - fptr->first_char) *
(fptr->width * fptr->height / 8);
}
return NULL;
}
/*
* Draw the monochrome character in the monochrome tiled framebuffer,
* a byte is interpreted as 8 pixels ordered vertically among each other.
*/
static u8_t draw_char_vtmono(const struct char_framebuffer *fb,
char c, u16_t x, u16_t y)
{
const struct cfb_font *fptr = &(fb->fonts[fb->font_idx]);
u8_t *glyph_ptr;
if (c < fptr->first_char || c > fptr->last_char) {
c = ' ';
}
glyph_ptr = get_glyph_ptr(fptr, c);
if (!glyph_ptr) {
return 0;
}
for (size_t g_x = 0; g_x < fptr->width; g_x++) {
u32_t y_segment = y / 8;
for (size_t g_y = 0; g_y < fptr->height / 8; g_y++) {
u32_t fb_y = (y_segment + g_y) * fb->x_res;
if ((fb_y + x + g_x) >= fb->size) {
return 0;
}
fb->buf[fb_y + x + g_x] =
glyph_ptr[g_x * (fptr->height / 8) + g_y];
}
}
return fptr->width;
}
int cfb_print(struct device *dev, char *str, u16_t x, u16_t y)
{
const struct char_framebuffer *fb = &char_fb;
const struct cfb_font *fptr = &(fb->fonts[fb->font_idx]);
if (!fb->fonts || !fb->buf) {
return -1;
}
if (fptr->height % 8) {
LOG_ERR("Wrong font size");
return -1;
}
if ((fb->screen_info & SCREEN_INFO_MONO_VTILED) && !(y % 8)) {
for (size_t i = 0; i < strlen(str); i++) {
if (x + fptr->width > fb->x_res) {
x = 0;
y += fptr->height;
}
x += fb->kerning + draw_char_vtmono(fb, str[i], x, y);
}
return 0;
}
LOG_ERR("Unsupported framebuffer configuration");
return -1;
}
static int cfb_reverse_bytes(const struct char_framebuffer *fb)
{
if (!(fb->screen_info & SCREEN_INFO_MONO_VTILED)) {
LOG_ERR("Unsupported framebuffer configuration");
return -1;
}
for (size_t i = 0; i < fb->x_res * fb->y_res / 8; i++) {
fb->buf[i] = (fb->buf[i] & 0xf0) >> 4 |
(fb->buf[i] & 0x0f) << 4;
fb->buf[i] = (fb->buf[i] & 0xcc) >> 2 |
(fb->buf[i] & 0x33) << 2;
fb->buf[i] = (fb->buf[i] & 0xaa) >> 1 |
(fb->buf[i] & 0x55) << 1;
}
return 0;
}
static int cfb_invert(const struct char_framebuffer *fb)
{
for (size_t i = 0; i < fb->x_res * fb->y_res / 8; i++) {
fb->buf[i] = ~fb->buf[i];
}
return 0;
}
int cfb_framebuffer_clear(struct device *dev, bool clear_display)
{
const struct display_driver_api *api = dev->driver_api;
const struct char_framebuffer *fb = &char_fb;
struct display_buffer_descriptor desc;
if (!fb || !fb->buf) {
return -1;
}
desc.buf_size = fb->size;
desc.width = 0;
desc.height = 0;
desc.pitch = 0;
memset(fb->buf, 0, fb->size);
if (clear_display && (fb->screen_info & SCREEN_INFO_EPD)) {
api->set_contrast(dev, 1);
api->write(dev, 0, 0, &desc, fb->buf);
api->set_contrast(dev, 0);
}
return 0;
}
int cfb_framebuffer_finalize(struct device *dev)
{
const struct display_driver_api *api = dev->driver_api;
const struct char_framebuffer *fb = &char_fb;
struct display_buffer_descriptor desc;
if (!fb || !fb->buf) {
return -1;
}
desc.buf_size = fb->size;
desc.width = 0;
desc.height = 0;
desc.pitch = 0;
if (!(fb->pixel_format & PIXEL_FORMAT_MONO10) != !(fb->inverted)) {
cfb_invert(fb);
}
if (fb->screen_info & SCREEN_INFO_MONO_MSB_FIRST) {
cfb_reverse_bytes(fb);
}
return api->write(dev, 0, 0, &desc, fb->buf);
}
int cfb_get_display_parameter(struct device *dev,
enum cfb_display_param param)
{
const struct char_framebuffer *fb = &char_fb;
switch (param) {
case CFB_DISPLAY_HEIGH:
return fb->y_res;
case CFB_DISPLAY_WIDTH:
return fb->x_res;
case CFB_DISPLAY_PPT:
return fb->ppt;
case CFB_DISPLAY_ROWS:
if (fb->screen_info & SCREEN_INFO_MONO_VTILED) {
return fb->y_res / fb->ppt;
}
return fb->y_res;
case CFB_DISPLAY_COLS:
if (fb->screen_info & SCREEN_INFO_MONO_VTILED) {
return fb->x_res;
}
return fb->x_res / fb->ppt;
}
return 0;
}
int cfb_framebuffer_set_font(struct device *dev, u8_t idx)
{
struct char_framebuffer *fb = &char_fb;
if (idx >= fb->numof_fonts) {
return -1;
}
fb->font_idx = idx;
return 0;
}
int cfb_get_font_size(struct device *dev, u8_t idx, u8_t *width, u8_t *height)
{
const struct char_framebuffer *fb = &char_fb;
if (idx >= fb->numof_fonts) {
return -1;
}
if (width) {
*width = __font_entry_start[idx].width;
}
if (height) {
*height = __font_entry_start[idx].height;
}
return 0;
}
int cfb_framebuffer_init(struct device *dev)
{
const struct display_driver_api *api = dev->driver_api;
struct char_framebuffer *fb = &char_fb;
struct display_capabilities cfg;
api->get_capabilities(dev, &cfg);
fb->numof_fonts = __font_entry_end - __font_entry_start;
LOG_DBG("number of fonts %d", fb->numof_fonts);
if (!fb->numof_fonts) {
return -1;
}
fb->x_res = cfg.x_resolution;
fb->y_res = cfg.y_resolution;
fb->ppt = 8;
fb->pixel_format = cfg.current_pixel_format;
fb->screen_info = cfg.screen_info;
fb->buf = NULL;
fb->font_idx = 0;
fb->kerning = 0;
fb->inverted = false;
fb->fonts = __font_entry_start;
fb->font_idx = 0;
fb->size = fb->x_res * fb->y_res / fb->ppt;
fb->buf = k_malloc(fb->size);
if (!fb->buf) {
return -1;
}
memset(fb->buf, 0, fb->size);
return 0;
}

5179
subsys/fb/cfb_fonts.c Normal file

File diff suppressed because it is too large Load diff