drivers: display: Enhance BBC micro:bit string output capabilities
Add a new mb_display_print() API which takes printf-style parameters and outputs scrolling text instead of one character at a time. The existing mb_display_str() API is renamed to mb_display_string() for consistency, and now also takes printf-style parameters. Change-Id: I59c42bcd74c62f05ecb6d097dc808b9e5c1984c5 Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
This commit is contained in:
parent
380d24de1d
commit
7e5df0cc02
5 changed files with 177 additions and 36 deletions
|
@ -9,14 +9,16 @@
|
||||||
config MICROBIT_DISPLAY
|
config MICROBIT_DISPLAY
|
||||||
bool "BBC micro:bit 5x5 LED Display support"
|
bool "BBC micro:bit 5x5 LED Display support"
|
||||||
depends on BOARD_BBC_MICROBIT
|
depends on BOARD_BBC_MICROBIT
|
||||||
|
depends on PRINTK
|
||||||
depends on GPIO
|
depends on GPIO
|
||||||
help
|
help
|
||||||
Enable this to be able to display images and text on the 5x5
|
Enable this to be able to display images and text on the 5x5
|
||||||
LED matrix display on the BBC micro:bit.
|
LED matrix display on the BBC micro:bit.
|
||||||
|
|
||||||
|
if MICROBIT_DISPLAY
|
||||||
|
|
||||||
config MICROBIT_DISPLAY_PIN_GRANULARITY
|
config MICROBIT_DISPLAY_PIN_GRANULARITY
|
||||||
bool "Access the GPIO on a per-pin instead of per-port basis"
|
bool "Access the GPIO on a per-pin instead of per-port basis"
|
||||||
depends on MICROBIT_DISPLAY
|
|
||||||
help
|
help
|
||||||
By default, the micro:bit display driver will update the GPIO
|
By default, the micro:bit display driver will update the GPIO
|
||||||
pins of the display (pins 4 through 15) by accessing the entire
|
pins of the display (pins 4 through 15) by accessing the entire
|
||||||
|
@ -24,3 +26,24 @@ config MICROBIT_DISPLAY_PIN_GRANULARITY
|
||||||
with other peripherals connected to the same GPIO port. Select
|
with other peripherals connected to the same GPIO port. Select
|
||||||
this option if other peripherals are connected to the same GPIO
|
this option if other peripherals are connected to the same GPIO
|
||||||
port.
|
port.
|
||||||
|
|
||||||
|
config MICROBIT_DISPLAY_STR_MAX
|
||||||
|
int "Maximum length of strings that can be shown on the display"
|
||||||
|
range 3 1024
|
||||||
|
default 40
|
||||||
|
help
|
||||||
|
This value specifies the maximum length of strings that can
|
||||||
|
be displayed using the mb_display_string() and mb_display_print()
|
||||||
|
APIs.
|
||||||
|
|
||||||
|
config MICROBIT_DISPLAY_SCROLL_STEP
|
||||||
|
int "Duration between two string scrolling steps (in milliseconds)"
|
||||||
|
range 20 2000
|
||||||
|
default 100
|
||||||
|
help
|
||||||
|
This value specifies the time between two scrolling steps of the
|
||||||
|
string scrolling functionality. Smaller values mean faster
|
||||||
|
scrolling whereas bigger values mean slower scrolling. It is
|
||||||
|
usually best to leave this at its default value (100ms).
|
||||||
|
|
||||||
|
endif # MICROBIT_DISPLAY
|
||||||
|
|
|
@ -16,6 +16,8 @@
|
||||||
#include <board.h>
|
#include <board.h>
|
||||||
#include <gpio.h>
|
#include <gpio.h>
|
||||||
#include <device.h>
|
#include <device.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <misc/printk.h>
|
||||||
|
|
||||||
#include <display/mb_display.h>
|
#include <display/mb_display.h>
|
||||||
|
|
||||||
|
@ -24,6 +26,12 @@
|
||||||
#define DISPLAY_ROWS 3
|
#define DISPLAY_ROWS 3
|
||||||
#define DISPLAY_COLS 9
|
#define DISPLAY_COLS 9
|
||||||
|
|
||||||
|
#define SCROLL_OFF 0
|
||||||
|
#define SCROLL_START 1
|
||||||
|
|
||||||
|
/* Time between scroll shifts */
|
||||||
|
#define SCROLL_DURATION K_MSEC(CONFIG_MICROBIT_DISPLAY_SCROLL_STEP)
|
||||||
|
|
||||||
struct mb_display {
|
struct mb_display {
|
||||||
struct device *dev; /* GPIO device */
|
struct device *dev; /* GPIO device */
|
||||||
|
|
||||||
|
@ -32,11 +40,16 @@ struct mb_display {
|
||||||
/* The following variables track the currently shown image */
|
/* The following variables track the currently shown image */
|
||||||
int cur; /* Currently rendered row */
|
int cur; /* Currently rendered row */
|
||||||
uint32_t row[3]; /* Content (columns) for each row */
|
uint32_t row[3]; /* Content (columns) for each row */
|
||||||
int64_t expiry; /* When to stop showing the current image */
|
int64_t expiry; /* When to stop showing current image */
|
||||||
|
|
||||||
int32_t duration; /* Duration for each shown image */
|
int32_t duration; /* Duration for each shown image */
|
||||||
|
|
||||||
|
struct mb_image img[2]; /* Current and next image */
|
||||||
|
uint16_t scroll; /* Scroll shift */
|
||||||
|
|
||||||
const char *str; /* String to be shown */
|
const char *str; /* String to be shown */
|
||||||
|
|
||||||
|
/* Buffer for printed strings */
|
||||||
|
char str_buf[CONFIG_MICROBIT_DISPLAY_STR_MAX];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct x_y {
|
struct x_y {
|
||||||
|
@ -57,10 +70,24 @@ static const struct x_y map[DISPLAY_ROWS][DISPLAY_COLS] = {
|
||||||
static const uint32_t col_mask = (((~0UL) << LED_COL1_GPIO_PIN) &
|
static const uint32_t col_mask = (((~0UL) << LED_COL1_GPIO_PIN) &
|
||||||
((~0UL) >> (31 - LED_COL9_GPIO_PIN)));
|
((~0UL) >> (31 - LED_COL9_GPIO_PIN)));
|
||||||
|
|
||||||
|
static inline void img_copy(struct mb_image *dst, const struct mb_image *src)
|
||||||
|
{
|
||||||
|
memcpy(dst, src, sizeof(*dst));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline const struct mb_image *get_font(char ch)
|
||||||
|
{
|
||||||
|
if (ch < MB_FONT_START || ch > MB_FONT_END) {
|
||||||
|
return &mb_font[' ' - MB_FONT_START];
|
||||||
|
}
|
||||||
|
|
||||||
|
return &mb_font[ch - MB_FONT_START];
|
||||||
|
}
|
||||||
|
|
||||||
#define GET_PIXEL(img, x, y) ((img)->row[y] & BIT(x))
|
#define GET_PIXEL(img, x, y) ((img)->row[y] & BIT(x))
|
||||||
|
|
||||||
/* Precalculate all three rows of an image */
|
/* Precalculate all three rows of an image and start the rendering. */
|
||||||
static void set_img(struct mb_display *disp, const struct mb_image *img)
|
static void start_image(struct mb_display *disp, const struct mb_image *img)
|
||||||
{
|
{
|
||||||
int row, col;
|
int row, col;
|
||||||
|
|
||||||
|
@ -78,6 +105,14 @@ static void set_img(struct mb_display *disp, const struct mb_image *img)
|
||||||
}
|
}
|
||||||
|
|
||||||
disp->cur = 0;
|
disp->cur = 0;
|
||||||
|
|
||||||
|
if (disp->duration == K_FOREVER) {
|
||||||
|
disp->expiry = K_FOREVER;
|
||||||
|
} else {
|
||||||
|
disp->expiry = k_uptime_get() + disp->duration;
|
||||||
|
}
|
||||||
|
|
||||||
|
k_timer_start(&disp->timer, K_NO_WAIT, K_MSEC(5));
|
||||||
}
|
}
|
||||||
|
|
||||||
#define ROW_PIN(n) (LED_ROW1_GPIO_PIN + (n))
|
#define ROW_PIN(n) (LED_ROW1_GPIO_PIN + (n))
|
||||||
|
@ -102,6 +137,40 @@ static inline void update_pins(struct mb_display *disp, uint32_t val)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void update_scroll(struct mb_display *disp)
|
||||||
|
{
|
||||||
|
if (disp->scroll < 5) {
|
||||||
|
struct mb_image img;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < 5; i++) {
|
||||||
|
img.row[i] = (disp->img[0].row[i] >> disp->scroll) |
|
||||||
|
(disp->img[1].row[i] << (5 - disp->scroll));
|
||||||
|
}
|
||||||
|
|
||||||
|
disp->scroll++;
|
||||||
|
start_image(disp, &img);
|
||||||
|
} else {
|
||||||
|
if (!disp->str) {
|
||||||
|
disp->scroll = SCROLL_OFF;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
img_copy(&disp->img[0], &disp->img[1]);
|
||||||
|
|
||||||
|
if (disp->str[0]) {
|
||||||
|
img_copy(&disp->img[1], get_font(disp->str[0]));
|
||||||
|
disp->str++;
|
||||||
|
} else {
|
||||||
|
img_copy(&disp->img[1], get_font(' '));
|
||||||
|
disp->str = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
disp->scroll = SCROLL_START;
|
||||||
|
start_image(disp, &disp->img[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void show_row(struct k_timer *timer)
|
static void show_row(struct k_timer *timer)
|
||||||
{
|
{
|
||||||
struct mb_display *disp = CONTAINER_OF(timer, struct mb_display, timer);
|
struct mb_display *disp = CONTAINER_OF(timer, struct mb_display, timer);
|
||||||
|
@ -111,11 +180,13 @@ static void show_row(struct k_timer *timer)
|
||||||
|
|
||||||
if (disp->cur == 0 && disp->expiry != K_FOREVER &&
|
if (disp->cur == 0 && disp->expiry != K_FOREVER &&
|
||||||
k_uptime_get() > disp->expiry) {
|
k_uptime_get() > disp->expiry) {
|
||||||
k_timer_stop(&disp->timer);
|
if (disp->scroll) {
|
||||||
|
update_scroll(disp);
|
||||||
if (disp->str && disp->str[0]) {
|
} else if (disp->str && disp->str[0]) {
|
||||||
mb_display_char(disp, disp->str[0], disp->duration);
|
start_image(disp, get_font(disp->str[0]));
|
||||||
disp->str++;
|
disp->str++;
|
||||||
|
} else {
|
||||||
|
k_timer_stop(&disp->timer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -134,44 +205,69 @@ static struct mb_display display = {
|
||||||
void mb_display_image(struct mb_display *disp, const struct mb_image *img,
|
void mb_display_image(struct mb_display *disp, const struct mb_image *img,
|
||||||
int32_t duration)
|
int32_t duration)
|
||||||
{
|
{
|
||||||
set_img(disp, img);
|
disp->str = NULL;
|
||||||
|
disp->scroll = SCROLL_OFF;
|
||||||
|
disp->duration = duration;
|
||||||
|
|
||||||
if (duration == K_FOREVER) {
|
start_image(disp, img);
|
||||||
disp->expiry = K_FOREVER;
|
|
||||||
} else {
|
|
||||||
disp->expiry = k_uptime_get() + duration;
|
|
||||||
}
|
|
||||||
|
|
||||||
k_timer_start(&disp->timer, K_NO_WAIT, K_MSEC(5));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void mb_display_stop(struct mb_display *disp)
|
void mb_display_stop(struct mb_display *disp)
|
||||||
{
|
{
|
||||||
k_timer_stop(&disp->timer);
|
k_timer_stop(&disp->timer);
|
||||||
disp->str = NULL;
|
disp->str = NULL;
|
||||||
|
disp->scroll = SCROLL_OFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
void mb_display_char(struct mb_display *disp, char chr, int32_t duration)
|
void mb_display_char(struct mb_display *disp, char chr, int32_t duration)
|
||||||
{
|
{
|
||||||
if (chr < MB_FONT_START || chr > MB_FONT_END) {
|
mb_display_image(disp, get_font(chr), duration);
|
||||||
chr = ' ';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mb_display_image(disp, &mb_font[chr - MB_FONT_START], duration);
|
void mb_display_string(struct mb_display *disp, int32_t duration,
|
||||||
}
|
const char *fmt, ...)
|
||||||
|
|
||||||
void mb_display_str(struct mb_display *disp, const char *str, int32_t duration)
|
|
||||||
{
|
{
|
||||||
__ASSERT(str, "NULL string");
|
va_list ap;
|
||||||
|
|
||||||
if (*str == '\0') {
|
va_start(ap, fmt);
|
||||||
|
vsnprintk(disp->str_buf, sizeof(disp->str_buf), fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
if (disp->str_buf[0] == '\0') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
disp->str = &str[1];
|
disp->str = &disp->str_buf[1];
|
||||||
disp->duration = duration;
|
disp->duration = duration;
|
||||||
|
disp->scroll = SCROLL_OFF;
|
||||||
|
|
||||||
mb_display_char(disp, *str, duration);
|
start_image(disp, get_font(disp->str_buf[0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
void mb_display_print(struct mb_display *disp, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
vsnprintk(disp->str_buf, sizeof(disp->str_buf), fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
if (disp->str_buf[0] == '\0') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (disp->str_buf[1] == '\0') {
|
||||||
|
disp->str = NULL;
|
||||||
|
} else {
|
||||||
|
disp->str = &disp->str_buf[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
img_copy(&disp->img[0], get_font(disp->str_buf[0]));
|
||||||
|
img_copy(&disp->img[1], get_font(disp->str_buf[1]));
|
||||||
|
disp->scroll = SCROLL_START;
|
||||||
|
disp->duration = SCROLL_DURATION;
|
||||||
|
|
||||||
|
start_image(disp, &disp->img[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct mb_display *mb_display_get(void)
|
struct mb_display *mb_display_get(void)
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
* @{
|
* @{
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <misc/util.h>
|
#include <misc/util.h>
|
||||||
|
|
||||||
|
@ -110,11 +111,32 @@ void mb_display_char(struct mb_display *disp, char chr, int32_t duration);
|
||||||
/**
|
/**
|
||||||
* @brief Display a string of characters on the BBC micro:bit LED display.
|
* @brief Display a string of characters on the BBC micro:bit LED display.
|
||||||
*
|
*
|
||||||
|
* This function takes a printf-style format string and outputs it one
|
||||||
|
* character at a time to the display. For scrolling-based string output
|
||||||
|
* see the mb_display_print() API.
|
||||||
|
*
|
||||||
* @param disp Display object.
|
* @param disp Display object.
|
||||||
* @param str String to display.
|
|
||||||
* @param duration Duration how long to show each character (in milliseconds).
|
* @param duration Duration how long to show each character (in milliseconds).
|
||||||
|
* @param fmt printf-style format string.
|
||||||
|
* @param ... Optional list of format arguments.
|
||||||
*/
|
*/
|
||||||
void mb_display_str(struct mb_display *disp, const char *str, int32_t duration);
|
__printf_like(3, 4) void mb_display_string(struct mb_display *disp,
|
||||||
|
int32_t duration,
|
||||||
|
const char *fmt, ...);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Print a string of characters on the BBC micro:bit LED display.
|
||||||
|
*
|
||||||
|
* This function takes a printf-style format string and outputs it in a
|
||||||
|
* scrolling fashion to the display. For character-by-character output
|
||||||
|
* instead of scrolling, see the mb_display_string() API.
|
||||||
|
*
|
||||||
|
* @param disp Display object.
|
||||||
|
* @param fmt printf-style format string
|
||||||
|
* @param ... Optional list of format arguments.
|
||||||
|
*/
|
||||||
|
__printf_like(2, 3) void mb_display_print(struct mb_display *disp,
|
||||||
|
const char *fmt, ...);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Stop the ongoing display of an image.
|
* @brief Stop the ongoing display of an image.
|
||||||
|
|
|
@ -24,4 +24,4 @@ Sample Output
|
||||||
|
|
||||||
The sample app displays a countdown of the characters 9-0, iterates
|
The sample app displays a countdown of the characters 9-0, iterates
|
||||||
through all pixels one-by-one, displays a smiley face, displays the text
|
through all pixels one-by-one, displays a smiley face, displays the text
|
||||||
"Zephyr!", and then stops.
|
"Hello Zephyr!" by scrolling, and then stops.
|
||||||
|
|
|
@ -24,7 +24,7 @@ void main(void)
|
||||||
int x, y;
|
int x, y;
|
||||||
|
|
||||||
/* Display countdown from '9' to '0' */
|
/* Display countdown from '9' to '0' */
|
||||||
mb_display_str(disp, "9876543210", K_SECONDS(1));
|
mb_display_string(disp, K_SECONDS(1), "9876543210");
|
||||||
|
|
||||||
k_sleep(K_SECONDS(11));
|
k_sleep(K_SECONDS(11));
|
||||||
|
|
||||||
|
@ -42,6 +42,6 @@ void main(void)
|
||||||
mb_display_image(disp, &smiley, K_SECONDS(2));
|
mb_display_image(disp, &smiley, K_SECONDS(2));
|
||||||
k_sleep(K_SECONDS(2));
|
k_sleep(K_SECONDS(2));
|
||||||
|
|
||||||
/* Show some text ("Zephyr!") */
|
/* Show some scrolling text ("Hello Zephyr!") */
|
||||||
mb_display_str(disp, "Zephyr!", K_SECONDS(1));
|
mb_display_print(disp, "Hello Zephyr!");
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue