diff --git a/drivers/Kconfig b/drivers/Kconfig index ae168d37b01..ece137d45ba 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -71,4 +71,7 @@ source "drivers/dma/Kconfig" source "drivers/usb/Kconfig" source "drivers/crypto/Kconfig" + +source "drivers/display/Kconfig" + endmenu diff --git a/drivers/Makefile b/drivers/Makefile index c0a93846f6d..4bf578b86c4 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -3,6 +3,7 @@ subdir-ccflags-y += -I$(srctree)/include/drivers obj-y = console/ obj-y += random/ obj-y += serial/ +obj-y += display/ obj-$(CONFIG_SYS_CLOCK_EXISTS) += timer/ obj-y += interrupt_controller/ obj-$(CONFIG_GROVE) += grove/ diff --git a/drivers/display/Kconfig b/drivers/display/Kconfig new file mode 100644 index 00000000000..58ba915ea42 --- /dev/null +++ b/drivers/display/Kconfig @@ -0,0 +1,26 @@ +# Kconfig - Display drivers + +# +# Copyright (c) 2017 Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 +# + +config MICROBIT_DISPLAY + bool "BBC micro:bit 5x5 LED Display support" + depends on BOARD_BBC_MICROBIT + depends on GPIO + help + Enable this to be able to display images and text on the 5x5 + LED matrix display on the BBC micro:bit. + +config MICROBIT_DISPLAY_PIN_GRANULARITY + bool "Access the GPIO on a per-pin instead of per-port basis" + depends on MICROBIT_DISPLAY + help + By default, the micro:bit display driver will update the GPIO + pins of the display (pins 4 through 15) by accessing the entire + GPIO port. This is done for efficiency, however it may interfere + with other peripherals connected to the same GPIO port. Select + this option if other peripherals are connected to the same GPIO + port. diff --git a/drivers/display/Makefile b/drivers/display/Makefile new file mode 100644 index 00000000000..551cca3f705 --- /dev/null +++ b/drivers/display/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_MICROBIT_DISPLAY) += mb_display.o mb_font.o diff --git a/drivers/display/mb_display.c b/drivers/display/mb_display.c new file mode 100644 index 00000000000..47d2afc64fe --- /dev/null +++ b/drivers/display/mb_display.c @@ -0,0 +1,206 @@ +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* + * References: + * + * https://www.microbit.co.uk/device/screen + * https://lancaster-university.github.io/microbit-docs/ubit/display/ + */ + +#include +#include +#include +#include +#include + +#include + +#include "mb_font.h" + +#define DISPLAY_ROWS 3 +#define DISPLAY_COLS 9 + +struct mb_display { + struct device *dev; /* GPIO device */ + + struct k_timer timer; /* Rendering timer */ + + /* The following variables track the currently shown image */ + int cur; /* Currently rendered row */ + uint32_t row[3]; /* Content (columns) for each row */ + int64_t expiry; /* When to stop showing the current image */ + + int32_t duration; /* Duration for each shown image */ + + const char *str; /* String to be shown */ +}; + +struct x_y { + uint8_t x:4, + y:4; +}; + +/* Where the X,Y coordinates of each row/col are found. + * The top left corner has the coordinates 0,0. + */ +static const struct x_y map[DISPLAY_ROWS][DISPLAY_COLS] = { + {{0, 0}, {2, 0}, {4, 0}, {4, 3}, {3, 3}, {2, 3}, {1, 3}, {0, 3}, {1, 2} }, + {{4, 2}, {0, 2}, {2, 2}, {1, 0}, {3, 0}, {3, 4}, {1, 4}, {0, 0}, {0, 0} }, + {{2, 4}, {4, 4}, {0, 4}, {0, 1}, {1, 1}, {2, 1}, {3, 1}, {4, 1}, {3, 2} }, +}; + +/* Mask of all the column bits */ +static const uint32_t col_mask = (((~0UL) << LED_COL1_GPIO_PIN) & + ((~0UL) >> (31 - LED_COL9_GPIO_PIN))); + +#define GET_PIXEL(img, x, y) ((img)->row[y] & BIT(x)) + +/* Precalculate all three rows of an image */ +static void set_img(struct mb_display *disp, const struct mb_image *img) +{ + int row, col; + + for (row = 0; row < DISPLAY_ROWS; row++) { + disp->row[row] = 0; + + for (col = 0; col < DISPLAY_COLS; col++) { + if (GET_PIXEL(img, map[row][col].x, map[row][col].y)) { + disp->row[row] |= BIT(LED_COL1_GPIO_PIN + col); + } + } + + disp->row[row] = ~disp->row[row] & col_mask; + disp->row[row] |= BIT(LED_ROW1_GPIO_PIN + row); + } + + disp->cur = 0; +} + +#define ROW_PIN(n) (LED_ROW1_GPIO_PIN + (n)) + +static inline void update_pins(struct mb_display *disp, uint32_t val) +{ + if (IS_ENABLED(CONFIG_MICROBIT_DISPLAY_PIN_GRANULARITY)) { + uint32_t pin, prev = (disp->cur + 2) % 3; + + /* Disable the previous row */ + gpio_pin_write(disp->dev, ROW_PIN(prev), 0); + + /* Set the column pins to their correct values */ + for (pin = LED_COL1_GPIO_PIN; pin <= LED_COL9_GPIO_PIN; pin++) { + gpio_pin_write(disp->dev, pin, !!(val & BIT(pin))); + } + + /* Enable the new row */ + gpio_pin_write(disp->dev, ROW_PIN(disp->cur), 1); + } else { + gpio_port_write(disp->dev, val); + } +} + +static void show_row(struct k_timer *timer) +{ + struct mb_display *disp = CONTAINER_OF(timer, struct mb_display, timer); + + update_pins(disp, disp->row[disp->cur]); + disp->cur = (disp->cur + 1) % DISPLAY_ROWS; + + if (disp->cur == 0 && disp->expiry != K_FOREVER && + k_uptime_get() > disp->expiry) { + k_timer_stop(&disp->timer); + + if (disp->str && disp->str[0]) { + mb_display_char(disp, disp->str[0], disp->duration); + disp->str++; + } + } +} + +static void clear_display(struct k_timer *timer) +{ + struct mb_display *disp = CONTAINER_OF(timer, struct mb_display, timer); + + update_pins(disp, col_mask); +} + +static struct mb_display display = { + .timer = K_TIMER_INITIALIZER(display.timer, show_row, clear_display), +}; + +void mb_display_image(struct mb_display *disp, const struct mb_image *img, + int32_t duration) +{ + set_img(disp, img); + + if (duration == K_FOREVER) { + 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) +{ + k_timer_stop(&disp->timer); + disp->str = NULL; +} + +void mb_display_char(struct mb_display *disp, char chr, int32_t duration) +{ + if (chr < MB_FONT_START || chr > MB_FONT_END) { + chr = ' '; + } + + mb_display_image(disp, &mb_font[chr - MB_FONT_START], duration); +} + +void mb_display_str(struct mb_display *disp, const char *str, int32_t duration) +{ + __ASSERT(str, "NULL string"); + + if (*str == '\0') { + return; + } + + disp->str = &str[1]; + disp->duration = duration; + + mb_display_char(disp, *str, duration); +} + +struct mb_display *mb_display_get(void) +{ + return &display; +} + +static int mb_display_init(struct device *dev) +{ + ARG_UNUSED(dev); + + display.dev = device_get_binding(CONFIG_GPIO_NRF5_P0_DEV_NAME); + + __ASSERT(dev, "No GPIO device found"); + + gpio_pin_configure(display.dev, LED_ROW1_GPIO_PIN, GPIO_DIR_OUT); + gpio_pin_configure(display.dev, LED_ROW2_GPIO_PIN, GPIO_DIR_OUT); + gpio_pin_configure(display.dev, LED_ROW3_GPIO_PIN, GPIO_DIR_OUT); + gpio_pin_configure(display.dev, LED_COL1_GPIO_PIN, GPIO_DIR_OUT); + gpio_pin_configure(display.dev, LED_COL2_GPIO_PIN, GPIO_DIR_OUT); + gpio_pin_configure(display.dev, LED_COL3_GPIO_PIN, GPIO_DIR_OUT); + gpio_pin_configure(display.dev, LED_COL4_GPIO_PIN, GPIO_DIR_OUT); + gpio_pin_configure(display.dev, LED_COL5_GPIO_PIN, GPIO_DIR_OUT); + gpio_pin_configure(display.dev, LED_COL6_GPIO_PIN, GPIO_DIR_OUT); + gpio_pin_configure(display.dev, LED_COL7_GPIO_PIN, GPIO_DIR_OUT); + gpio_pin_configure(display.dev, LED_COL8_GPIO_PIN, GPIO_DIR_OUT); + gpio_pin_configure(display.dev, LED_COL9_GPIO_PIN, GPIO_DIR_OUT); + + return 0; +} + +SYS_INIT(mb_display_init, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE); diff --git a/drivers/display/mb_font.c b/drivers/display/mb_font.c new file mode 100644 index 00000000000..f4344102eac --- /dev/null +++ b/drivers/display/mb_font.c @@ -0,0 +1,582 @@ +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include "mb_font.h" + +const struct mb_image mb_font[MB_FONT_COUNT] = { + /* ' ' */ + MB_IMAGE({ 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0 }), + /* '!' */ + MB_IMAGE({ 0, 1, 0, 0, 0 }, + { 0, 1, 0, 0, 0 }, + { 0, 1, 0, 0, 0 }, + { 0, 0, 0, 0, 0 }, + { 0, 1, 0, 0, 0 }), + /* '"' */ + MB_IMAGE({ 0, 1, 0, 1, 0 }, + { 0, 1, 0, 1, 0 }, + { 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0 }), + /* '#' */ + MB_IMAGE({ 0, 1, 0, 1, 0 }, + { 1, 1, 1, 1, 1 }, + { 0, 1, 0, 1, 0 }, + { 1, 1, 1, 1, 1 }, + { 0, 1, 0, 1, 0 }), + /* '$' */ + MB_IMAGE({ 0, 1, 1, 1, 0 }, + { 1, 1, 0, 0, 1 }, + { 0, 1, 1, 1, 0 }, + { 1, 0, 0, 1, 1 }, + { 0, 1, 1, 1, 0 }), + /* '%' */ + MB_IMAGE({ 1, 1, 0, 0, 1 }, + { 1, 0, 0, 1, 0 }, + { 0, 0, 1, 0, 0 }, + { 0, 1, 0, 0, 1 }, + { 1, 0, 0, 1, 1 }), + /* '&' */ + MB_IMAGE({ 0, 1, 1, 0, 0 }, + { 1, 0, 0, 1, 0 }, + { 0, 1, 1, 0, 0 }, + { 1, 0, 0, 1, 0 }, + { 0, 1, 1, 0, 1 }), + /* ''' */ + MB_IMAGE({ 0, 1, 0, 0, 0 }, + { 0, 1, 0, 0, 0 }, + { 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0 }), + /* '(' */ + MB_IMAGE({ 0, 0, 1, 0, 0 }, + { 0, 1, 0, 0, 0 }, + { 0, 1, 0, 0, 0 }, + { 0, 1, 0, 0, 0 }, + { 0, 0, 1, 0, 0 }), + /* ')' */ + MB_IMAGE({ 0, 1, 0, 0, 0 }, + { 0, 0, 1, 0, 0 }, + { 0, 0, 1, 0, 0 }, + { 0, 0, 1, 0, 0 }, + { 0, 1, 0, 0, 0 }), + /* '*' */ + MB_IMAGE({ 0, 0, 0, 0, 0 }, + { 0, 1, 0, 1, 0 }, + { 0, 0, 1, 0, 0 }, + { 0, 1, 0, 1, 0 }, + { 0, 0, 0, 0, 0 }), + /* '+' */ + MB_IMAGE({ 0, 0, 0, 0, 0 }, + { 0, 0, 1, 0, 0 }, + { 0, 1, 1, 1, 0 }, + { 0, 0, 1, 0, 0 }, + { 0, 0, 0, 0, 0 }), + /* ',' */ + MB_IMAGE({ 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0 }, + { 0, 0, 1, 0, 0 }, + { 0, 1, 0, 0, 0 }), + /* '-' */ + MB_IMAGE({ 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0 }, + { 0, 1, 1, 1, 0 }, + { 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0 }), + /* '.' */ + MB_IMAGE({ 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0 }, + { 0, 1, 0, 0, 0 }, + { 0, 0, 0, 0, 0 }), + /* '/' */ + MB_IMAGE({ 0, 0, 0, 0, 1 }, + { 0, 0, 0, 1, 0 }, + { 0, 0, 1, 0, 0 }, + { 0, 1, 0, 0, 0 }, + { 1, 0, 0, 0, 0 }), + /* '0' */ + MB_IMAGE({ 0, 1, 1, 0, 0 }, + { 1, 0, 0, 1, 0 }, + { 1, 0, 0, 1, 0 }, + { 1, 0, 0, 1, 0 }, + { 0, 1, 1, 0, 0 }), + /* '1' */ + MB_IMAGE({ 0, 0, 1, 0, 0 }, + { 0, 1, 1, 0, 0 }, + { 0, 0, 1, 0, 0 }, + { 0, 0, 1, 0, 0 }, + { 0, 1, 1, 1, 0 }), + /* '2' */ + MB_IMAGE({ 1, 1, 1, 0, 0 }, + { 0, 0, 0, 1, 0 }, + { 0, 1, 1, 0, 0 }, + { 1, 0, 0, 0, 0 }, + { 1, 1, 1, 1, 0 }), + /* '3' */ + MB_IMAGE({ 1, 1, 1, 1, 0 }, + { 0, 0, 0, 1, 0 }, + { 0, 0, 1, 0, 0 }, + { 1, 0, 0, 1, 0 }, + { 0, 1, 1, 0, 0 }), + /* '4' */ + MB_IMAGE({ 0, 0, 1, 1, 0 }, + { 0, 1, 0, 1, 0 }, + { 1, 0, 0, 1, 0 }, + { 1, 1, 1, 1, 1 }, + { 0, 0, 0, 1, 0 }), + /* '5' */ + MB_IMAGE({ 1, 1, 1, 1, 1 }, + { 1, 0, 0, 0, 0 }, + { 1, 1, 1, 1, 0 }, + { 0, 0, 0, 0, 1 }, + { 1, 1, 1, 1, 0 }), + /* '6' */ + MB_IMAGE({ 0, 0, 0, 1, 0 }, + { 0, 0, 1, 0, 0 }, + { 0, 1, 1, 1, 0 }, + { 1, 0, 0, 0, 1 }, + { 0, 1, 1, 1, 0 }), + /* '7' */ + MB_IMAGE({ 1, 1, 1, 1, 1 }, + { 0, 0, 0, 1, 0 }, + { 0, 0, 1, 0, 0 }, + { 0, 1, 0, 0, 0 }, + { 1, 0, 0, 0, 0 }), + /* '8' */ + MB_IMAGE({ 0, 1, 1, 1, 0 }, + { 1, 0, 0, 0, 1 }, + { 0, 1, 1, 1, 0 }, + { 1, 0, 0, 0, 1 }, + { 0, 1, 1, 1, 0 }), + /* '9' */ + MB_IMAGE({ 0, 1, 1, 1, 0 }, + { 1, 0, 0, 0, 1 }, + { 0, 1, 1, 1, 0 }, + { 0, 0, 1, 0, 0 }, + { 0, 1, 0, 0, 0 }), + /* ':' */ + MB_IMAGE({ 0, 0, 0, 0, 0 }, + { 0, 1, 0, 0, 0 }, + { 0, 0, 0, 0, 0 }, + { 0, 1, 0, 0, 0 }, + { 0, 0, 0, 0, 0 }), + /* ';' */ + MB_IMAGE({ 0, 0, 0, 0, 0 }, + { 0, 0, 1, 0, 0 }, + { 0, 0, 0, 0, 0 }, + { 0, 0, 1, 0, 0 }, + { 0, 1, 0, 0, 0 }), + /* '<' */ + MB_IMAGE({ 0, 0, 0, 1, 0 }, + { 0, 0, 1, 0, 0 }, + { 0, 1, 0, 0, 0 }, + { 0, 0, 1, 0, 0 }, + { 0, 0, 0, 1, 0 }), + /* '=' */ + MB_IMAGE({ 0, 0, 0, 0, 0 }, + { 0, 1, 1, 1, 0 }, + { 0, 0, 0, 0, 0 }, + { 0, 1, 1, 1, 0 }, + { 0, 0, 0, 0, 0 }), + /* '>' */ + MB_IMAGE({ 0, 1, 0, 0, 0 }, + { 0, 0, 1, 0, 0 }, + { 0, 0, 0, 1, 0 }, + { 0, 0, 1, 0, 0 }, + { 0, 1, 0, 0, 0 }), + /* '?' */ + MB_IMAGE({ 0, 1, 1, 1, 0 }, + { 1, 0, 0, 0, 1 }, + { 0, 0, 1, 1, 0 }, + { 0, 0, 0, 0, 0 }, + { 0, 0, 1, 0, 0 }), + /* '@' */ + MB_IMAGE({ 0, 1, 1, 1, 0 }, + { 1, 0, 0, 0, 1 }, + { 1, 0, 1, 0, 1 }, + { 1, 0, 0, 1, 1 }, + { 0, 1, 1, 0, 0 }), + /* 'A' */ + MB_IMAGE({ 0, 1, 1, 0, 0 }, + { 1, 0, 0, 1, 0 }, + { 1, 1, 1, 1, 0 }, + { 1, 0, 0, 1, 0 }, + { 1, 0, 0, 1, 0 }), + /* 'B' */ + MB_IMAGE({ 1, 1, 1, 0, 0 }, + { 1, 0, 0, 1, 0 }, + { 1, 1, 1, 0, 0 }, + { 1, 0, 0, 1, 0 }, + { 1, 1, 1, 0, 0 }), + /* 'C' */ + MB_IMAGE({ 0, 1, 1, 1, 0 }, + { 1, 0, 0, 0, 0 }, + { 1, 0, 0, 0, 0 }, + { 1, 0, 0, 0, 0 }, + { 0, 1, 1, 1, 0 }), + /* 'D' */ + MB_IMAGE({ 1, 1, 1, 0, 0 }, + { 1, 0, 0, 1, 0 }, + { 1, 0, 0, 1, 0 }, + { 1, 0, 0, 1, 0 }, + { 1, 1, 1, 0, 0 }), + /* 'E' */ + MB_IMAGE({ 1, 1, 1, 1, 0 }, + { 1, 0, 0, 0, 0 }, + { 1, 1, 1, 0, 0 }, + { 1, 0, 0, 0, 0 }, + { 1, 1, 1, 1, 0 }), + /* 'F' */ + MB_IMAGE({ 1, 1, 1, 1, 0 }, + { 1, 0, 0, 0, 0 }, + { 1, 1, 1, 0, 0 }, + { 1, 0, 0, 0, 0 }, + { 1, 0, 0, 0, 0 }), + /* 'G' */ + MB_IMAGE({ 0, 1, 1, 1, 0 }, + { 1, 0, 0, 0, 0 }, + { 1, 0, 0, 1, 1 }, + { 1, 0, 0, 0, 1 }, + { 0, 1, 1, 1, 0 }), + /* 'H' */ + MB_IMAGE({ 1, 0, 0, 1, 0 }, + { 1, 0, 0, 1, 0 }, + { 1, 1, 1, 1, 0 }, + { 1, 0, 0, 1, 0 }, + { 1, 0, 0, 1, 0 }), + /* 'I' */ + MB_IMAGE({ 1, 1, 1, 0, 0 }, + { 0, 1, 0, 0, 0 }, + { 0, 1, 0, 0, 0 }, + { 0, 1, 0, 0, 0 }, + { 1, 1, 1, 0, 0 }), + /* 'J' */ + MB_IMAGE({ 1, 1, 1, 1, 1 }, + { 0, 0, 0, 1, 0 }, + { 0, 0, 0, 1, 0 }, + { 1, 0, 0, 1, 0 }, + { 0, 1, 1, 0, 0 }), + /* 'K' */ + MB_IMAGE({ 1, 0, 0, 1, 0 }, + { 1, 0, 1, 0, 0 }, + { 1, 1, 0, 0, 0 }, + { 1, 0, 1, 0, 0 }, + { 1, 0, 0, 1, 0 }), + /* 'L' */ + MB_IMAGE({ 1, 0, 0, 0, 0 }, + { 1, 0, 0, 0, 0 }, + { 1, 0, 0, 0, 0 }, + { 1, 0, 0, 0, 0 }, + { 1, 1, 1, 1, 0 }), + /* 'M' */ + MB_IMAGE({ 1, 0, 0, 0, 1 }, + { 1, 1, 0, 1, 1 }, + { 1, 0, 1, 0, 1 }, + { 1, 0, 0, 0, 1 }, + { 1, 0, 0, 0, 1 }), + /* 'N' */ + MB_IMAGE({ 1, 0, 0, 0, 1 }, + { 1, 1, 0, 0, 1 }, + { 1, 0, 1, 0, 1 }, + { 1, 0, 0, 1, 1 }, + { 1, 0, 0, 0, 1 }), + /* 'O' */ + MB_IMAGE({ 0, 1, 1, 0, 0 }, + { 1, 0, 0, 1, 0 }, + { 1, 0, 0, 1, 0 }, + { 1, 0, 0, 1, 0 }, + { 0, 1, 1, 0, 0 }), + /* 'P' */ + MB_IMAGE({ 1, 1, 1, 0, 0 }, + { 1, 0, 0, 1, 0 }, + { 1, 1, 1, 0, 0 }, + { 1, 0, 0, 0, 0 }, + { 1, 0, 0, 0, 0 }), + /* 'Q' */ + MB_IMAGE({ 0, 1, 1, 0, 0 }, + { 1, 0, 0, 1, 0 }, + { 1, 0, 0, 1, 0 }, + { 0, 1, 1, 0, 0 }, + { 0, 0, 1, 1, 0 }), + /* 'R' */ + MB_IMAGE({ 1, 1, 1, 0, 0 }, + { 1, 0, 0, 1, 0 }, + { 1, 1, 1, 0, 0 }, + { 1, 0, 0, 1, 0 }, + { 1, 0, 0, 0, 1 }), + /* 'S' */ + MB_IMAGE({ 0, 1, 1, 1, 0 }, + { 1, 0, 0, 0, 0 }, + { 0, 1, 1, 0, 0 }, + { 0, 0, 0, 1, 0 }, + { 1, 1, 1, 0, 0 }), + /* 'T' */ + MB_IMAGE({ 1, 1, 1, 1, 1 }, + { 0, 0, 1, 0, 0 }, + { 0, 0, 1, 0, 0 }, + { 0, 0, 1, 0, 0 }, + { 0, 0, 1, 0, 0 }), + /* 'U' */ + MB_IMAGE({ 1, 0, 0, 1, 0 }, + { 1, 0, 0, 1, 0 }, + { 1, 0, 0, 1, 0 }, + { 1, 0, 0, 1, 0 }, + { 0, 1, 1, 0, 0 }), + /* 'V' */ + MB_IMAGE({ 1, 0, 0, 0, 1 }, + { 1, 0, 0, 0, 1 }, + { 1, 0, 0, 0, 1 }, + { 0, 1, 0, 1, 0 }, + { 0, 0, 1, 0, 0 }), + /* 'W' */ + MB_IMAGE({ 1, 0, 0, 0, 1 }, + { 1, 0, 0, 0, 1 }, + { 1, 0, 1, 0, 1 }, + { 1, 1, 0, 1, 1 }, + { 1, 0, 0, 0, 1 }), + /* 'X' */ + MB_IMAGE({ 1, 0, 0, 1, 0 }, + { 1, 0, 0, 1, 0 }, + { 0, 1, 1, 0, 0 }, + { 1, 0, 0, 1, 0 }, + { 1, 0, 0, 1, 0 }), + /* 'Y' */ + MB_IMAGE({ 1, 0, 0, 0, 1 }, + { 0, 1, 0, 1, 0 }, + { 0, 0, 1, 0, 0 }, + { 0, 0, 1, 0, 0 }, + { 0, 0, 1, 0, 0 }), + /* 'Z' */ + MB_IMAGE({ 1, 1, 1, 1, 0 }, + { 0, 0, 1, 0, 0 }, + { 0, 1, 0, 0, 0 }, + { 1, 0, 0, 0, 0 }, + { 1, 1, 1, 1, 0 }), + /* '[' */ + MB_IMAGE({ 0, 1, 1, 1, 0 }, + { 0, 1, 0, 0, 0 }, + { 0, 1, 0, 0, 0 }, + { 0, 1, 0, 0, 0 }, + { 0, 1, 1, 1, 0 }), + /* '\' */ + MB_IMAGE({ 1, 0, 0, 0, 0 }, + { 0, 1, 0, 0, 0 }, + { 0, 0, 1, 0, 0 }, + { 0, 0, 0, 1, 0 }, + { 0, 0, 0, 0, 1 }), + /* ']' */ + MB_IMAGE({ 0, 1, 1, 1, 0 }, + { 0, 0, 0, 1, 0 }, + { 0, 0, 0, 1, 0 }, + { 0, 0, 0, 1, 0 }, + { 0, 1, 1, 1, 0 }), + /* '^' */ + MB_IMAGE({ 0, 0, 1, 0, 0 }, + { 0, 1, 0, 1, 0 }, + { 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0 }), + /* '_' */ + MB_IMAGE({ 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0 }, + { 1, 1, 1, 1, 1 }), + /* '`' */ + MB_IMAGE({ 0, 1, 0, 0, 0 }, + { 0, 0, 1, 0, 0 }, + { 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0 }), + /* 'a' */ + MB_IMAGE({ 0, 0, 0, 0, 0 }, + { 0, 1, 1, 1, 0 }, + { 1, 0, 0, 1, 0 }, + { 1, 0, 0, 1, 0 }, + { 0, 1, 1, 1, 1 }), + /* 'b' */ + MB_IMAGE({ 1, 0, 0, 0, 0 }, + { 1, 0, 0, 0, 0 }, + { 1, 1, 1, 0, 0 }, + { 1, 0, 0, 1, 0 }, + { 1, 1, 1, 0, 0 }), + /* 'c' */ + MB_IMAGE({ 0, 0, 0, 0, 0 }, + { 0, 1, 1, 1, 0 }, + { 1, 0, 0, 0, 0 }, + { 1, 0, 0, 0, 0 }, + { 0, 1, 1, 1, 0 }), + /* 'd' */ + MB_IMAGE({ 0, 0, 0, 1, 0 }, + { 0, 0, 0, 1, 0 }, + { 0, 1, 1, 1, 0 }, + { 1, 0, 0, 1, 0 }, + { 0, 1, 1, 1, 0 }), + /* 'e' */ + MB_IMAGE({ 0, 1, 1, 0, 0 }, + { 1, 0, 0, 1, 0 }, + { 1, 1, 1, 0, 0 }, + { 1, 0, 0, 0, 0 }, + { 0, 1, 1, 1, 0 }), + /* 'f' */ + MB_IMAGE({ 0, 0, 1, 1, 0 }, + { 0, 1, 0, 0, 0 }, + { 1, 1, 1, 0, 0 }, + { 0, 1, 0, 0, 0 }, + { 0, 1, 0, 0, 0 }), + /* 'g' */ + MB_IMAGE({ 0, 1, 1, 1, 0 }, + { 1, 0, 0, 1, 0 }, + { 0, 1, 1, 1, 0 }, + { 0, 0, 0, 1, 0 }, + { 0, 1, 1, 0, 0 }), + /* 'h' */ + MB_IMAGE({ 1, 0, 0, 0, 0 }, + { 1, 0, 0, 0, 0 }, + { 1, 1, 1, 0, 0 }, + { 1, 0, 0, 1, 0 }, + { 1, 0, 0, 1, 0 }), + /* 'i' */ + MB_IMAGE({ 0, 1, 0, 0, 0 }, + { 0, 0, 0, 0, 0 }, + { 0, 1, 0, 0, 0 }, + { 0, 1, 0, 0, 0 }, + { 0, 1, 0, 0, 0 }), + /* 'j' */ + MB_IMAGE({ 0, 0, 0, 1, 0 }, + { 0, 0, 0, 0, 0 }, + { 0, 0, 0, 1, 0 }, + { 0, 0, 0, 1, 0 }, + { 0, 1, 1, 0, 0 }), + /* 'k' */ + MB_IMAGE({ 1, 0, 0, 0, 0 }, + { 1, 0, 1, 0, 0 }, + { 1, 1, 0, 0, 0 }, + { 1, 0, 1, 0, 0 }, + { 1, 0, 0, 1, 0 }), + /* 'l' */ + MB_IMAGE({ 0, 1, 0, 0, 0 }, + { 0, 1, 0, 0, 0 }, + { 0, 1, 0, 0, 0 }, + { 0, 1, 0, 0, 0 }, + { 0, 0, 1, 1, 0 }), + /* 'm' */ + MB_IMAGE({ 0, 0, 0, 0, 0 }, + { 1, 1, 0, 1, 1 }, + { 1, 0, 1, 0, 1 }, + { 1, 0, 0, 0, 1 }, + { 1, 0, 0, 0, 1 }), + /* 'n' */ + MB_IMAGE({ 0, 0, 0, 0, 0 }, + { 1, 1, 1, 0, 0 }, + { 1, 0, 0, 1, 0 }, + { 1, 0, 0, 1, 0 }, + { 1, 0, 0, 1, 0 }), + /* 'o' */ + MB_IMAGE({ 0, 0, 0, 0, 0 }, + { 0, 1, 1, 0, 0 }, + { 1, 0, 0, 1, 0 }, + { 1, 0, 0, 1, 0 }, + { 0, 1, 1, 0, 0 }), + /* 'p' */ + MB_IMAGE({ 0, 0, 0, 0, 0 }, + { 1, 1, 1, 0, 0 }, + { 1, 0, 0, 1, 0 }, + { 1, 1, 1, 0, 0 }, + { 1, 0, 0, 0, 0 }), + /* 'q' */ + MB_IMAGE({ 0, 0, 0, 0, 0 }, + { 0, 1, 1, 1, 0 }, + { 1, 0, 0, 1, 0 }, + { 0, 1, 1, 1, 0 }, + { 0, 0, 0, 1, 0 }), + /* 'r' */ + MB_IMAGE({ 0, 0, 0, 0, 0 }, + { 0, 1, 1, 1, 0 }, + { 1, 0, 0, 0, 0 }, + { 1, 0, 0, 0, 0 }, + { 1, 0, 0, 0, 0 }), + /* 's' */ + MB_IMAGE({ 0, 0, 0, 0, 0 }, + { 0, 0, 1, 1, 0 }, + { 0, 1, 0, 0, 0 }, + { 0, 0, 1, 0, 0 }, + { 1, 1, 0, 0, 0 }), + /* 't' */ + MB_IMAGE({ 0, 1, 0, 0, 0 }, + { 0, 1, 0, 0, 0 }, + { 0, 1, 1, 1, 0 }, + { 0, 1, 0, 0, 0 }, + { 0, 0, 1, 1, 1 }), + /* 'u' */ + MB_IMAGE({ 0, 0, 0, 0, 0 }, + { 1, 0, 0, 1, 0 }, + { 1, 0, 0, 1, 0 }, + { 1, 0, 0, 1, 0 }, + { 0, 1, 1, 1, 1 }), + /* 'v' */ + MB_IMAGE({ 0, 0, 0, 0, 0 }, + { 1, 0, 0, 0, 1 }, + { 1, 0, 0, 0, 1 }, + { 0, 1, 0, 1, 0 }, + { 0, 0, 1, 0, 0 }), + /* 'w' */ + MB_IMAGE({ 0, 0, 0, 0, 0 }, + { 1, 0, 0, 0, 1 }, + { 1, 0, 0, 0, 1 }, + { 1, 0, 1, 0, 1 }, + { 1, 1, 0, 1, 1 }), + /* 'x' */ + MB_IMAGE({ 0, 0, 0, 0, 0 }, + { 1, 0, 0, 1, 0 }, + { 0, 1, 1, 0, 0 }, + { 0, 1, 1, 0, 0 }, + { 1, 0, 0, 1, 0 }), + /* 'y' */ + MB_IMAGE({ 0, 0, 0, 0, 0 }, + { 1, 0, 0, 0, 1 }, + { 0, 1, 0, 1, 0 }, + { 0, 0, 1, 0, 0 }, + { 1, 1, 0, 0, 0 }), + /* 'z' */ + MB_IMAGE({ 0, 0, 0, 0, 0 }, + { 1, 1, 1, 1, 0 }, + { 0, 0, 1, 0, 0 }, + { 0, 1, 0, 0, 0 }, + { 1, 1, 1, 1, 0 }), + /* '{' */ + MB_IMAGE({ 0, 0, 1, 1, 0 }, + { 0, 0, 1, 0, 0 }, + { 0, 1, 1, 0, 0 }, + { 0, 0, 1, 0, 0 }, + { 0, 0, 1, 1, 0 }), + /* '|' */ + MB_IMAGE({ 0, 1, 0, 0, 0 }, + { 0, 1, 0, 0, 0 }, + { 0, 1, 0, 0, 0 }, + { 0, 1, 0, 0, 0 }, + { 0, 1, 0, 0, 0 }), + /* '}' */ + MB_IMAGE({ 1, 1, 0, 0, 0 }, + { 0, 1, 0, 0, 0 }, + { 0, 1, 1, 0, 0 }, + { 0, 1, 0, 0, 0 }, + { 1, 1, 0, 0, 0 }), + /* '~' */ + MB_IMAGE({ 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0 }, + { 0, 1, 1, 0, 0 }, + { 0, 0, 0, 1, 1 }, + { 0, 0, 0, 0, 0 }), +}; diff --git a/drivers/display/mb_font.h b/drivers/display/mb_font.h new file mode 100644 index 00000000000..8e03c540139 --- /dev/null +++ b/drivers/display/mb_font.h @@ -0,0 +1,11 @@ +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define MB_FONT_COUNT 95 +#define MB_FONT_START ' ' +#define MB_FONT_END '~' + +extern const struct mb_image mb_font[MB_FONT_COUNT]; diff --git a/include/display/mb_display.h b/include/display/mb_display.h new file mode 100644 index 00000000000..77df7d4b522 --- /dev/null +++ b/include/display/mb_display.h @@ -0,0 +1,134 @@ +/** @file + * @brief BBC micro:bit display APIs. + */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __MB_DISPLAY_H +#define __MB_DISPLAY_H + +/** + * @brief BBC micro:bit display APIs + * @defgroup mb_display BBC micro:bit display APIs + * @{ + */ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Representation of a BBC micro:bit display image. + * + * This struct should normally not be used directly, rather created + * using the MB_IMAGE() macro. + */ +struct mb_image { + union { + struct { + uint8_t c1:1, + c2:1, + c3:1, + c4:1, + c5:1; + } r[5]; + uint8_t row[5]; + }; +}; + +/** + * @def MB_IMAGE + * @brief Generate an image object from a given array rows/columns. + * + * This helper takes an array of 5 rows, each consisting of 5 0/1 values which + * correspond to the columns of that row. The value 0 means the pixel is + * disabled whereas a 1 means the pixel is enabled. + * + * The pixels go from left to right and top to bottom, i.e. top-left corner + * is the first row's first value, top-right is the first rows last value, + * and bottom-right corner is the last value of the last (5th) row. As an + * example, the following would create a smiley face image: + * + *
+ * static const struct mb_image smiley = MB_IMAGE({ 0, 1, 0, 1, 0 },
+ *						  { 0, 1, 0, 1, 0 },
+ *						  { 0, 0, 0, 0, 0 },
+ *						  { 1, 0, 0, 0, 1 },
+ *						  { 0, 1, 1, 1, 0 });
+ * 
+ * + * @param _rows Each of the 5 rows represented as a 5-value column array. + * + * @return Image bitmap that can be passed e.g. to mb_display_image(). + */ +#define MB_IMAGE(_rows...) { .r = { _rows } } + +/** + * @brief Opaque struct representing the BBC micro:bit display. + * + * For more information see the following links: + * + * https://www.microbit.co.uk/device/screen + * + * https://lancaster-university.github.io/microbit-docs/ubit/display/ + */ +struct mb_display; + +/** + * @brief Get a pointer to the BBC micro:bit display object. + * + * @return Pointer to display object. + */ +struct mb_display *mb_display_get(void); + +/** + * @brief Display an image on the BBC micro:bit LED display. + * + * @param disp Display object. + * @param img Bitmap of pixels. + * @param duration Duration how long to show the image (in milliseconds). + */ +void mb_display_image(struct mb_display *disp, const struct mb_image *img, + int32_t duration); + +/** + * @brief Display a character on the BBC micro:bit LED display. + * + * @param disp Display object. + * @param chr Character to display. + * @param duration Duration how long to show the character (in milliseconds). + */ +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. + * + * @param disp Display object. + * @param str String to display. + * @param duration Duration how long to show each character (in milliseconds). + */ +void mb_display_str(struct mb_display *disp, const char *str, int32_t duration); + +/** + * @brief Stop the ongoing display of an image. + * + * @param disp Display object. + */ +void mb_display_stop(struct mb_display *disp); + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif /* __MB_DISPLAY_H */ diff --git a/samples/boards/boards.rst b/samples/boards/boards.rst new file mode 100644 index 00000000000..f53410732bf --- /dev/null +++ b/samples/boards/boards.rst @@ -0,0 +1,8 @@ +Board-specific samples +###################### + +.. toctree:: + :maxdepth: 1 + :glob: + + */* diff --git a/samples/boards/microbit/display/Makefile b/samples/boards/microbit/display/Makefile new file mode 100644 index 00000000000..20ad981a51f --- /dev/null +++ b/samples/boards/microbit/display/Makefile @@ -0,0 +1,4 @@ +BOARD ?= bbc_microbit +CONF_FILE = prj.conf + +include ${ZEPHYR_BASE}/Makefile.test diff --git a/samples/boards/microbit/display/README.rst b/samples/boards/microbit/display/README.rst new file mode 100644 index 00000000000..a3a137da211 --- /dev/null +++ b/samples/boards/microbit/display/README.rst @@ -0,0 +1,27 @@ +.. _microbit_display: + +BBC micro:bit display +##################### + +Overview +******** +A simple example that demonstrates how to use the 5x5 LED matrix display +on the BBC micro:bit board. + +Building +******** + +This project outputs various things on the BBC micro:bit display. It can +be built as follows: + +.. code-block:: console + + $ cd samples/boards/microbit/display + $ make + +Sample Output +============= + +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 +"Zephyr!", and then stops. diff --git a/samples/boards/microbit/display/prj.conf b/samples/boards/microbit/display/prj.conf new file mode 100644 index 00000000000..9cbdd6e773e --- /dev/null +++ b/samples/boards/microbit/display/prj.conf @@ -0,0 +1,4 @@ +# nothing here +CONFIG_GPIO=y +CONFIG_MICROBIT_DISPLAY=y +#CONFIG_MICROBIT_DISPLAY_PIN_GRANULARITY=y diff --git a/samples/boards/microbit/display/src/Makefile b/samples/boards/microbit/display/src/Makefile new file mode 100644 index 00000000000..00066e15678 --- /dev/null +++ b/samples/boards/microbit/display/src/Makefile @@ -0,0 +1 @@ +obj-y = main.o diff --git a/samples/boards/microbit/display/src/main.c b/samples/boards/microbit/display/src/main.c new file mode 100644 index 00000000000..395377f2e8f --- /dev/null +++ b/samples/boards/microbit/display/src/main.c @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include + +#include + +static struct mb_image smiley = MB_IMAGE({ 0, 1, 0, 1, 0 }, + { 0, 1, 0, 1, 0 }, + { 0, 0, 0, 0, 0 }, + { 1, 0, 0, 0, 1 }, + { 0, 1, 1, 1, 0 }); + +void main(void) +{ + struct mb_display *disp = mb_display_get(); + int x, y; + + /* Display countdown from '9' to '0' */ + mb_display_str(disp, "9876543210", K_SECONDS(1)); + + k_sleep(K_SECONDS(11)); + + /* Iterate through all pixels one-by-one */ + for (y = 0; y < 5; y++) { + for (x = 0; x < 5; x++) { + struct mb_image pixel = {}; + pixel.row[y] = BIT(x); + mb_display_image(disp, &pixel, K_MSEC(250)); + k_sleep(K_MSEC(300)); + } + } + + /* Show a smiley-face */ + mb_display_image(disp, &smiley, K_SECONDS(2)); + k_sleep(K_SECONDS(2)); + + /* Show some text ("Zephyr!") */ + mb_display_str(disp, "Zephyr!", K_SECONDS(1)); +} diff --git a/samples/boards/microbit/display/testcase.ini b/samples/boards/microbit/display/testcase.ini new file mode 100644 index 00000000000..0763266506b --- /dev/null +++ b/samples/boards/microbit/display/testcase.ini @@ -0,0 +1,4 @@ +[test] +build_only = true +tags = samples +platform_whitelist = bbc_microbit diff --git a/samples/boards/microbit/microbit.rst b/samples/boards/microbit/microbit.rst new file mode 100644 index 00000000000..94a01846ac3 --- /dev/null +++ b/samples/boards/microbit/microbit.rst @@ -0,0 +1,8 @@ +BBC micro:bit Samples +##################### + +.. toctree:: + :maxdepth: 1 + :glob: + + **/* diff --git a/samples/samples.rst b/samples/samples.rst index 09fa57c0c2a..b21d6421217 100644 --- a/samples/samples.rst +++ b/samples/samples.rst @@ -15,6 +15,7 @@ Samples and Demos grove/* advanced power/power.rst + boards/*