drivers: mb_display: rework bbc:microbit display support
Rework bbc:microbit display support to use nRF LED matrix display controller driver and allow to use it with bbc:microbit v2 board. This patch turns the driver into a higher level driver using the display controller API. Code that directly accesses hardware (GPIO) is removed. This driver is reworked to be more generic. It still has a lot of potential for improvement, but it requires changes in all applications that use this tool. Signed-off-by: Johann Fischer <johann.fischer@nordicsemi.no>
This commit is contained in:
parent
faaa08b4c1
commit
44585b7fc5
2 changed files with 160 additions and 202 deletions
|
@ -5,9 +5,8 @@
|
||||||
|
|
||||||
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 || BOARD_BBC_MICROBIT_V2
|
||||||
depends on PRINTK
|
depends on PRINTK
|
||||||
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.
|
||||||
|
|
|
@ -1,106 +1,54 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2017 Intel Corporation
|
* Copyright (c) 2017 Intel Corporation
|
||||||
|
* Copyright (c) 2021, Nordic Semiconductor ASA
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* References:
|
* This tool uses display controller driver API and requires
|
||||||
*
|
* a suitable LED matrix controller driver.
|
||||||
* https://www.microbit.co.uk/device/screen
|
|
||||||
* https://lancaster-university.github.io/microbit-docs/ubit/display/
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <zephyr.h>
|
#include <zephyr.h>
|
||||||
#include <init.h>
|
#include <init.h>
|
||||||
#include <drivers/gpio.h>
|
|
||||||
#include <device.h>
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/printk.h>
|
#include <sys/printk.h>
|
||||||
|
|
||||||
#include <display/mb_display.h>
|
#include <display/mb_display.h>
|
||||||
|
#include <drivers/display.h>
|
||||||
|
|
||||||
#include "mb_font.h"
|
#include "mb_font.h"
|
||||||
|
|
||||||
|
#include <logging/log.h>
|
||||||
|
LOG_MODULE_REGISTER(mb_disp, CONFIG_DISPLAY_LOG_LEVEL);
|
||||||
|
|
||||||
#define MODE_MASK BIT_MASK(16)
|
#define MODE_MASK BIT_MASK(16)
|
||||||
|
|
||||||
/* Onboard LED Row 1 */
|
|
||||||
#define LED_ROW1_GPIO_PIN 13
|
|
||||||
#define LED_ROW1_GPIO_PORT DT_LABEL(DT_NODELABEL(gpio0))
|
|
||||||
|
|
||||||
/* Onboard LED Row 2 */
|
|
||||||
#define LED_ROW2_GPIO_PIN 14
|
|
||||||
#define LED_ROW2_GPIO_PORT DT_LABEL(DT_NODELABEL(gpio0))
|
|
||||||
|
|
||||||
/* Onboard LED Row 3 */
|
|
||||||
#define LED_ROW3_GPIO_PIN 15
|
|
||||||
#define LED_ROW3_GPIO_PORT DT_LABEL(DT_NODELABEL(gpio0))
|
|
||||||
|
|
||||||
/* Onboard LED Column 1 */
|
|
||||||
#define LED_COL1_GPIO_PIN 4
|
|
||||||
#define LED_COL1_GPIO_PORT DT_LABEL(DT_NODELABEL(gpio0))
|
|
||||||
|
|
||||||
/* Onboard LED Column 2 */
|
|
||||||
#define LED_COL2_GPIO_PIN 5
|
|
||||||
#define LED_COL2_GPIO_PORT DT_LABEL(DT_NODELABEL(gpio0))
|
|
||||||
|
|
||||||
/* Onboard LED Column 3 */
|
|
||||||
#define LED_COL3_GPIO_PIN 6
|
|
||||||
#define LED_COL3_GPIO_PORT DT_LABEL(DT_NODELABEL(gpio0))
|
|
||||||
|
|
||||||
/* Onboard LED Column 4 */
|
|
||||||
#define LED_COL4_GPIO_PIN 7
|
|
||||||
#define LED_COL4_GPIO_PORT DT_LABEL(DT_NODELABEL(gpio0))
|
|
||||||
|
|
||||||
/* Onboard LED Column 5 */
|
|
||||||
#define LED_COL5_GPIO_PIN 8
|
|
||||||
#define LED_COL5_GPIO_PORT DT_LABEL(DT_NODELABEL(gpio0))
|
|
||||||
|
|
||||||
/* Onboard LED Column 6 */
|
|
||||||
#define LED_COL6_GPIO_PIN 9
|
|
||||||
#define LED_COL6_GPIO_PORT DT_LABEL(DT_NODELABEL(gpio0))
|
|
||||||
|
|
||||||
/* Onboard LED Column 7 */
|
|
||||||
#define LED_COL7_GPIO_PIN 10
|
|
||||||
#define LED_COL7_GPIO_PORT DT_LABEL(DT_NODELABEL(gpio0))
|
|
||||||
|
|
||||||
/* Onboard LED Column 8 */
|
|
||||||
#define LED_COL8_GPIO_PIN 11
|
|
||||||
#define LED_COL8_GPIO_PORT DT_LABEL(DT_NODELABEL(gpio0))
|
|
||||||
|
|
||||||
/* Onboard LED Column 9 */
|
|
||||||
#define LED_COL9_GPIO_PIN 12
|
|
||||||
#define LED_COL9_GPIO_PORT DT_LABEL(DT_NODELABEL(gpio0))
|
|
||||||
|
|
||||||
|
|
||||||
#define DISPLAY_ROWS 3
|
|
||||||
#define DISPLAY_COLS 9
|
|
||||||
|
|
||||||
#define SCROLL_OFF 0
|
#define SCROLL_OFF 0
|
||||||
#define SCROLL_START 1
|
#define SCROLL_START 1
|
||||||
|
|
||||||
#define SCROLL_DEFAULT_DURATION_MS 80
|
#define SCROLL_DEFAULT_DURATION_MS 80
|
||||||
|
|
||||||
|
#define MB_DISP_XRES 5
|
||||||
|
#define MB_DISP_YRES 5
|
||||||
|
|
||||||
struct mb_display {
|
struct mb_display {
|
||||||
const struct device *dev; /* GPIO device */
|
const struct device *lm_dev; /* LED matrix display device */
|
||||||
|
|
||||||
struct k_timer timer; /* Rendering timer */
|
struct k_work_delayable dwork; /* Delayable work item */
|
||||||
|
|
||||||
uint8_t img_count; /* Image count */
|
uint8_t img_count; /* Image count */
|
||||||
|
|
||||||
uint8_t cur_img; /* Current image or character to show */
|
uint8_t cur_img; /* Current image or character to show */
|
||||||
|
|
||||||
uint8_t scroll:3, /* Scroll shift */
|
uint8_t scroll:3, /* Scroll shift */
|
||||||
first:1, /* First frame of a scroll sequence */
|
first:1, /* First frame of a scroll sequence */
|
||||||
loop:1, /* Loop to beginning */
|
loop:1, /* Loop to beginning */
|
||||||
text:1, /* We're showing a string (not image) */
|
text:1, /* We're showing a string (not image) */
|
||||||
img_sep:1; /* One column image separation */
|
img_sep:1, /* One column image separation */
|
||||||
|
msb:1; /* MSB represents the first pixel */
|
||||||
|
|
||||||
/* The following variables track the currently shown image */
|
int32_t duration; /* Duration for each shown image */
|
||||||
uint8_t cur; /* Currently rendered row */
|
|
||||||
uint32_t row[3]; /* Content (columns) for each row */
|
|
||||||
int64_t expiry; /* When to stop showing current image */
|
|
||||||
int32_t duration; /* Duration for each shown image */
|
|
||||||
|
|
||||||
union {
|
union {
|
||||||
const struct mb_image *img; /* Array of images to show */
|
const struct mb_image *img; /* Array of images to show */
|
||||||
|
@ -111,24 +59,6 @@ struct mb_display {
|
||||||
char str_buf[CONFIG_MICROBIT_DISPLAY_STR_MAX];
|
char str_buf[CONFIG_MICROBIT_DISPLAY_STR_MAX];
|
||||||
};
|
};
|
||||||
|
|
||||||
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)));
|
|
||||||
|
|
||||||
static inline const struct mb_image *get_font(char ch)
|
static inline const struct mb_image *get_font(char ch)
|
||||||
{
|
{
|
||||||
if (ch < MB_FONT_START || ch > MB_FONT_END) {
|
if (ch < MB_FONT_START || ch > MB_FONT_END) {
|
||||||
|
@ -138,64 +68,78 @@ static inline const struct mb_image *get_font(char ch)
|
||||||
return &mb_font[ch - MB_FONT_START];
|
return &mb_font[ch - MB_FONT_START];
|
||||||
}
|
}
|
||||||
|
|
||||||
#define GET_PIXEL(img, x, y) ((img)->row[y] & BIT(x))
|
static ALWAYS_INLINE uint8_t flip_pixels(uint8_t b)
|
||||||
|
|
||||||
/* Precalculate all three rows of an image and start the rendering. */
|
|
||||||
static void start_image(struct mb_display *disp, const struct mb_image *img)
|
|
||||||
{
|
{
|
||||||
int row, col;
|
b = (b & 0xf0) >> 4 | (b & 0x0f) << 4;
|
||||||
|
b = (b & 0xcc) >> 2 | (b & 0x33) << 2;
|
||||||
|
b = (b & 0xaa) >> 1 | (b & 0x55) << 1;
|
||||||
|
|
||||||
for (row = 0; row < DISPLAY_ROWS; row++) {
|
return b;
|
||||||
disp->row[row] = 0U;
|
}
|
||||||
|
|
||||||
for (col = 0; col < DISPLAY_COLS; col++) {
|
static int update_content(struct mb_display *disp, const struct mb_image *img)
|
||||||
if (GET_PIXEL(img, map[row][col].x, map[row][col].y)) {
|
{
|
||||||
disp->row[row] |= BIT(LED_COL1_GPIO_PIN + col);
|
const struct display_buffer_descriptor buf_desc = {
|
||||||
}
|
.buf_size = sizeof(struct mb_image),
|
||||||
|
.width = MB_DISP_XRES,
|
||||||
|
.height = MB_DISP_YRES,
|
||||||
|
.pitch = 8,
|
||||||
|
};
|
||||||
|
struct mb_image tmp_img;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (disp->msb) {
|
||||||
|
for (int i = 0; i < sizeof(struct mb_image); i++) {
|
||||||
|
tmp_img.row[i] = flip_pixels(img->row[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
disp->row[row] = ~disp->row[row] & col_mask;
|
ret = display_write(disp->lm_dev, 0, 0, &buf_desc, &tmp_img);
|
||||||
disp->row[row] |= BIT(LED_ROW1_GPIO_PIN + row);
|
|
||||||
}
|
|
||||||
|
|
||||||
disp->cur = 0U;
|
|
||||||
|
|
||||||
if (disp->duration == SYS_FOREVER_MS) {
|
|
||||||
disp->expiry = SYS_FOREVER_MS;
|
|
||||||
} else {
|
} else {
|
||||||
disp->expiry = k_uptime_get() + disp->duration;
|
ret = display_write(disp->lm_dev, 0, 0, &buf_desc, img);
|
||||||
}
|
}
|
||||||
|
|
||||||
k_timer_start(&disp->timer, K_NO_WAIT, K_MSEC(4));
|
if (ret < 0) {
|
||||||
}
|
LOG_ERR("Write to display controller failed");
|
||||||
|
return ret;
|
||||||
#define ROW_PIN(n) (LED_ROW1_GPIO_PIN + (n))
|
|
||||||
|
|
||||||
static inline void update_pins(struct mb_display *disp, uint32_t val)
|
|
||||||
{
|
|
||||||
uint32_t pin, prev = (disp->cur + 2) % 3;
|
|
||||||
|
|
||||||
/* Disable the previous row */
|
|
||||||
gpio_pin_set_raw(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_set_raw(disp->dev, pin, !!(val & BIT(pin)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Enable the new row */
|
LOG_DBG("Image duration %d", disp->duration);
|
||||||
gpio_pin_set_raw(disp->dev, ROW_PIN(disp->cur), 1);
|
if (disp->duration != SYS_FOREVER_MS) {
|
||||||
|
k_work_reschedule(&disp->dwork, K_MSEC(disp->duration));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void reset_display(struct mb_display *disp)
|
static int start_image(struct mb_display *disp, const struct mb_image *img)
|
||||||
{
|
{
|
||||||
k_timer_stop(&disp->timer);
|
int ret;
|
||||||
|
|
||||||
|
ret = display_blanking_off(disp->lm_dev);
|
||||||
|
if (ret < 0) {
|
||||||
|
LOG_ERR("Set blanking off failed");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return update_content(disp, img);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int reset_display(struct mb_display *disp)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
disp->str = NULL;
|
disp->str = NULL;
|
||||||
disp->cur_img = 0U;
|
disp->cur_img = 0U;
|
||||||
disp->img = NULL;
|
disp->img = NULL;
|
||||||
disp->img_count = 0U;
|
disp->img_count = 0U;
|
||||||
disp->scroll = SCROLL_OFF;
|
disp->scroll = SCROLL_OFF;
|
||||||
|
|
||||||
|
ret = display_blanking_on(disp->lm_dev);
|
||||||
|
if (ret < 0) {
|
||||||
|
LOG_ERR("Set blanking on failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct mb_image *current_img(struct mb_display *disp)
|
static const struct mb_image *current_img(struct mb_display *disp)
|
||||||
|
@ -243,16 +187,15 @@ static inline bool last_frame(struct mb_display *disp)
|
||||||
|
|
||||||
static inline uint8_t scroll_steps(struct mb_display *disp)
|
static inline uint8_t scroll_steps(struct mb_display *disp)
|
||||||
{
|
{
|
||||||
return 5 + disp->img_sep;
|
return MB_DISP_XRES + disp->img_sep;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void update_scroll(struct mb_display *disp)
|
static int update_scroll(struct mb_display *disp)
|
||||||
{
|
{
|
||||||
if (disp->scroll < scroll_steps(disp)) {
|
if (disp->scroll < scroll_steps(disp)) {
|
||||||
struct mb_image img;
|
struct mb_image img;
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < 5; i++) {
|
for (int i = 0; i < MB_DISP_XRES; i++) {
|
||||||
const struct mb_image *i1 = current_img(disp);
|
const struct mb_image *i1 = current_img(disp);
|
||||||
const struct mb_image *i2 = next_img(disp);
|
const struct mb_image *i2 = next_img(disp);
|
||||||
|
|
||||||
|
@ -262,7 +205,7 @@ static void update_scroll(struct mb_display *disp)
|
||||||
}
|
}
|
||||||
|
|
||||||
disp->scroll++;
|
disp->scroll++;
|
||||||
start_image(disp, &img);
|
return update_content(disp, &img);
|
||||||
} else {
|
} else {
|
||||||
if (disp->first) {
|
if (disp->first) {
|
||||||
disp->first = 0U;
|
disp->first = 0U;
|
||||||
|
@ -272,8 +215,7 @@ static void update_scroll(struct mb_display *disp)
|
||||||
|
|
||||||
if (last_frame(disp)) {
|
if (last_frame(disp)) {
|
||||||
if (!disp->loop) {
|
if (!disp->loop) {
|
||||||
reset_display(disp);
|
return reset_display(disp);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
disp->cur_img = 0U;
|
disp->cur_img = 0U;
|
||||||
|
@ -281,55 +223,41 @@ static void update_scroll(struct mb_display *disp)
|
||||||
}
|
}
|
||||||
|
|
||||||
disp->scroll = SCROLL_START;
|
disp->scroll = SCROLL_START;
|
||||||
start_image(disp, current_img(disp));
|
return update_content(disp, current_img(disp));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void update_image(struct mb_display *disp)
|
static int update_image(struct mb_display *disp)
|
||||||
{
|
{
|
||||||
disp->cur_img++;
|
disp->cur_img++;
|
||||||
|
|
||||||
if (last_frame(disp)) {
|
if (last_frame(disp)) {
|
||||||
if (!disp->loop) {
|
if (!disp->loop) {
|
||||||
reset_display(disp);
|
return reset_display(disp);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
disp->cur_img = 0U;
|
disp->cur_img = 0U;
|
||||||
}
|
}
|
||||||
|
|
||||||
start_image(disp, current_img(disp));
|
return update_content(disp, current_img(disp));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void show_row(struct k_timer *timer)
|
static void update_display_work(struct k_work *work)
|
||||||
{
|
{
|
||||||
struct mb_display *disp = CONTAINER_OF(timer, struct mb_display, timer);
|
struct k_work_delayable *dwork = k_work_delayable_from_work(work);
|
||||||
|
struct mb_display *disp = CONTAINER_OF(dwork, struct mb_display, dwork);
|
||||||
|
int ret;
|
||||||
|
|
||||||
update_pins(disp, disp->row[disp->cur]);
|
if (disp->scroll) {
|
||||||
disp->cur = (disp->cur + 1) % DISPLAY_ROWS;
|
ret = update_scroll(disp);
|
||||||
|
} else {
|
||||||
if (disp->cur == 0U && disp->expiry != SYS_FOREVER_MS &&
|
ret = update_image(disp);
|
||||||
k_uptime_get() > disp->expiry) {
|
|
||||||
if (disp->scroll) {
|
|
||||||
update_scroll(disp);
|
|
||||||
} else {
|
|
||||||
update_image(disp);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
__ASSERT(ret == 0, "Failed to update display");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void clear_display(struct k_timer *timer)
|
static int start_scroll(struct mb_display *disp, int32_t duration)
|
||||||
{
|
|
||||||
struct mb_display *disp = CONTAINER_OF(timer, struct mb_display, timer);
|
|
||||||
|
|
||||||
update_pins(disp, col_mask);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct mb_display display = {
|
|
||||||
.timer = Z_TIMER_INITIALIZER(display.timer, show_row, clear_display),
|
|
||||||
};
|
|
||||||
|
|
||||||
static void start_scroll(struct mb_display *disp, int32_t duration)
|
|
||||||
{
|
{
|
||||||
/* Divide total duration by number of scrolling steps */
|
/* Divide total duration by number of scrolling steps */
|
||||||
if (duration) {
|
if (duration) {
|
||||||
|
@ -341,24 +269,37 @@ static void start_scroll(struct mb_display *disp, int32_t duration)
|
||||||
disp->scroll = SCROLL_START;
|
disp->scroll = SCROLL_START;
|
||||||
disp->first = 1U;
|
disp->first = 1U;
|
||||||
disp->cur_img = 0U;
|
disp->cur_img = 0U;
|
||||||
start_image(disp, get_font(' '));
|
return start_image(disp, get_font(' '));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void start_single(struct mb_display *disp, int32_t duration)
|
static int start_single(struct mb_display *disp, int32_t duration)
|
||||||
{
|
{
|
||||||
disp->duration = duration;
|
disp->duration = duration;
|
||||||
|
|
||||||
if (disp->text) {
|
if (disp->text) {
|
||||||
start_image(disp, get_font(disp->str[0]));
|
return start_image(disp, get_font(disp->str[0]));
|
||||||
} else {
|
} else {
|
||||||
start_image(disp, disp->img);
|
return start_image(disp, disp->img);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void mb_display_stop(struct mb_display *disp)
|
||||||
|
{
|
||||||
|
struct k_work_sync sync;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
k_work_cancel_delayable_sync(&disp->dwork, &sync);
|
||||||
|
LOG_DBG("delayable work stopped %p", disp);
|
||||||
|
ret = reset_display(disp);
|
||||||
|
__ASSERT(ret == 0, "Failed to reset display");
|
||||||
|
}
|
||||||
|
|
||||||
void mb_display_image(struct mb_display *disp, uint32_t mode, int32_t duration,
|
void mb_display_image(struct mb_display *disp, uint32_t mode, int32_t duration,
|
||||||
const struct mb_image *img, uint8_t img_count)
|
const struct mb_image *img, uint8_t img_count)
|
||||||
{
|
{
|
||||||
reset_display(disp);
|
int ret;
|
||||||
|
|
||||||
|
mb_display_stop(disp);
|
||||||
|
|
||||||
__ASSERT(img && img_count > 0, "Invalid parameters");
|
__ASSERT(img && img_count > 0, "Invalid parameters");
|
||||||
|
|
||||||
|
@ -372,27 +313,25 @@ void mb_display_image(struct mb_display *disp, uint32_t mode, int32_t duration,
|
||||||
switch (mode & MODE_MASK) {
|
switch (mode & MODE_MASK) {
|
||||||
case MB_DISPLAY_MODE_DEFAULT:
|
case MB_DISPLAY_MODE_DEFAULT:
|
||||||
case MB_DISPLAY_MODE_SINGLE:
|
case MB_DISPLAY_MODE_SINGLE:
|
||||||
start_single(disp, duration);
|
ret = start_single(disp, duration);
|
||||||
|
__ASSERT(ret == 0, "Failed to start single mode");
|
||||||
break;
|
break;
|
||||||
case MB_DISPLAY_MODE_SCROLL:
|
case MB_DISPLAY_MODE_SCROLL:
|
||||||
start_scroll(disp, duration);
|
ret = start_scroll(disp, duration);
|
||||||
|
__ASSERT(ret == 0, "Failed to start scroll mode");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
__ASSERT(0, "Invalid display mode");
|
__ASSERT(0, "Invalid display mode");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void mb_display_stop(struct mb_display *disp)
|
|
||||||
{
|
|
||||||
reset_display(disp);
|
|
||||||
}
|
|
||||||
|
|
||||||
void mb_display_print(struct mb_display *disp, uint32_t mode,
|
void mb_display_print(struct mb_display *disp, uint32_t mode,
|
||||||
int32_t duration, const char *fmt, ...)
|
int32_t duration, const char *fmt, ...)
|
||||||
{
|
{
|
||||||
va_list ap;
|
va_list ap;
|
||||||
|
int ret;
|
||||||
|
|
||||||
reset_display(disp);
|
mb_display_stop(disp);
|
||||||
|
|
||||||
va_start(ap, fmt);
|
va_start(ap, fmt);
|
||||||
vsnprintk(disp->str_buf, sizeof(disp->str_buf), fmt, ap);
|
vsnprintk(disp->str_buf, sizeof(disp->str_buf), fmt, ap);
|
||||||
|
@ -411,43 +350,63 @@ void mb_display_print(struct mb_display *disp, uint32_t mode,
|
||||||
switch (mode & MODE_MASK) {
|
switch (mode & MODE_MASK) {
|
||||||
case MB_DISPLAY_MODE_DEFAULT:
|
case MB_DISPLAY_MODE_DEFAULT:
|
||||||
case MB_DISPLAY_MODE_SCROLL:
|
case MB_DISPLAY_MODE_SCROLL:
|
||||||
start_scroll(disp, duration);
|
ret = start_scroll(disp, duration);
|
||||||
|
__ASSERT(ret == 0, "Failed to start scroll mode");
|
||||||
break;
|
break;
|
||||||
case MB_DISPLAY_MODE_SINGLE:
|
case MB_DISPLAY_MODE_SINGLE:
|
||||||
start_single(disp, duration);
|
ret = start_single(disp, duration);
|
||||||
|
__ASSERT(ret == 0, "Failed to start single mode");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
__ASSERT(0, "Invalid display mode");
|
__ASSERT(0, "Invalid display mode");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int mb_display_init(struct mb_display *disp)
|
||||||
|
{
|
||||||
|
struct display_capabilities caps;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
display_get_capabilities(disp->lm_dev, &caps);
|
||||||
|
if (caps.x_resolution != MB_DISP_XRES ||
|
||||||
|
caps.y_resolution != MB_DISP_YRES) {
|
||||||
|
LOG_ERR("Not supported display resolution");
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (caps.screen_info & SCREEN_INFO_MONO_MSB_FIRST) {
|
||||||
|
disp->msb = 1U;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = display_set_brightness(disp->lm_dev, 0xFF);
|
||||||
|
if (ret < 0) {
|
||||||
|
LOG_ERR("Failed to set brightness");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
k_work_init_delayable(&disp->dwork, update_display_work);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct mb_display display;
|
||||||
|
|
||||||
struct mb_display *mb_display_get(void)
|
struct mb_display *mb_display_get(void)
|
||||||
{
|
{
|
||||||
return &display;
|
return &display;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mb_display_init(const struct device *dev)
|
static int mb_display_init_on_boot(const struct device *dev)
|
||||||
{
|
{
|
||||||
ARG_UNUSED(dev);
|
ARG_UNUSED(dev);
|
||||||
|
|
||||||
display.dev = device_get_binding(DT_LABEL(DT_NODELABEL(gpio0)));
|
display.lm_dev = DEVICE_DT_GET_ONE(nordic_nrf_led_matrix);
|
||||||
|
if (!device_is_ready(display.lm_dev)) {
|
||||||
|
LOG_ERR("Display controller device not ready");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
__ASSERT(dev, "No GPIO device found");
|
return mb_display_init(&display);
|
||||||
|
|
||||||
gpio_pin_configure(display.dev, LED_ROW1_GPIO_PIN, GPIO_OUTPUT);
|
|
||||||
gpio_pin_configure(display.dev, LED_ROW2_GPIO_PIN, GPIO_OUTPUT);
|
|
||||||
gpio_pin_configure(display.dev, LED_ROW3_GPIO_PIN, GPIO_OUTPUT);
|
|
||||||
gpio_pin_configure(display.dev, LED_COL1_GPIO_PIN, GPIO_OUTPUT);
|
|
||||||
gpio_pin_configure(display.dev, LED_COL2_GPIO_PIN, GPIO_OUTPUT);
|
|
||||||
gpio_pin_configure(display.dev, LED_COL3_GPIO_PIN, GPIO_OUTPUT);
|
|
||||||
gpio_pin_configure(display.dev, LED_COL4_GPIO_PIN, GPIO_OUTPUT);
|
|
||||||
gpio_pin_configure(display.dev, LED_COL5_GPIO_PIN, GPIO_OUTPUT);
|
|
||||||
gpio_pin_configure(display.dev, LED_COL6_GPIO_PIN, GPIO_OUTPUT);
|
|
||||||
gpio_pin_configure(display.dev, LED_COL7_GPIO_PIN, GPIO_OUTPUT);
|
|
||||||
gpio_pin_configure(display.dev, LED_COL8_GPIO_PIN, GPIO_OUTPUT);
|
|
||||||
gpio_pin_configure(display.dev, LED_COL9_GPIO_PIN, GPIO_OUTPUT);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SYS_INIT(mb_display_init, POST_KERNEL, CONFIG_DISPLAY_INIT_PRIORITY);
|
SYS_INIT(mb_display_init_on_boot, APPLICATION, CONFIG_DISPLAY_INIT_PRIORITY);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue