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:
Johan Hedberg 2017-04-04 13:01:22 +03:00
commit 7e5df0cc02
5 changed files with 177 additions and 36 deletions

View file

@ -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

View file

@ -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)

View file

@ -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.

View file

@ -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.

View file

@ -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!");
} }