Move scroll_text, show_text, bitmap_1d and set_pixels into C++ library

Move scroll_text into the C++ library and make it support std::string.

Move show_bitmap_1d to set_bitmap_1d in the C++ library. Use it as the basis for show_text and scroll_text.

Change show_text to set_text since it does not implicitly show the result.

Add a new pico-scroll demo to show off the scrolling text functionality.
This commit is contained in:
Phil Howard 2021-04-21 14:35:26 +01:00 committed by Graeme Winter
parent 54e4c1c177
commit 8a15da1f66
14 changed files with 170 additions and 141 deletions

View File

@ -1,10 +1,2 @@
add_executable(
scroll
demo.cpp
)
# Pull in pico libraries that we need
target_link_libraries(scroll pico_stdlib pico_scroll)
# create map/bin/hex file etc.
pico_add_extra_outputs(scroll)
include(demo.cmake)
include(scroll-text.cmake)

View File

@ -0,0 +1,10 @@
add_executable(
scroll
demo.cpp
)
# Pull in pico libraries that we need
target_link_libraries(scroll pico_stdlib pico_scroll)
# create map/bin/hex file etc.
pico_add_extra_outputs(scroll)

View File

@ -12,6 +12,7 @@ PicoScroll pico_scroll;
int main() {
pico_scroll.init();
pico_scroll.scroll_text("Hello World, how are you today?", 255, 100);
bool a_pressed = false;

View File

@ -0,0 +1,10 @@
add_executable(
scroll-text
scroll-text.cpp
)
# Pull in pico libraries that we need
target_link_libraries(scroll-text pico_stdlib pico_scroll)
# create map/bin/hex file etc.
pico_add_extra_outputs(scroll-text)

View File

@ -0,0 +1,41 @@
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "pico/stdlib.h"
#include "pico_scroll.hpp"
using namespace pimoroni;
PicoScroll pico_scroll;
int main() {
stdio_init_all();
pico_scroll.init();
while(true) {
pico_scroll.scroll_text("Hello World, how are you today?", 64, 100);
sleep_ms(500);
pico_scroll.set_text("Test", 64);
pico_scroll.update();
sleep_ms(1000);
// Set pixels to a chessboard pattern
char pixels[PicoScroll::WIDTH * PicoScroll::HEIGHT] = {};
pico_scroll.set_pixels(pixels);
for (int y = 0; y < PicoScroll::HEIGHT; y++) {
for (int x = 0; x < PicoScroll::WIDTH; x++) {
pixels[y * PicoScroll::WIDTH + x] = ((x + y) & 0b1) * 64;
}
}
pico_scroll.set_pixels(pixels);
pico_scroll.update();
sleep_ms(1000);
}
return 0;
}

View File

@ -1,10 +1 @@
add_library(pico_scroll INTERFACE)
target_sources(pico_scroll INTERFACE
${CMAKE_CURRENT_LIST_DIR}/pico_scroll.cpp
)
target_include_directories(pico_scroll INTERFACE ${CMAKE_CURRENT_LIST_DIR})
# Pull in pico libraries that we need
target_link_libraries(pico_scroll INTERFACE pico_stdlib hardware_i2c)
include(pico_scroll.cmake)

View File

@ -1,7 +1,11 @@
add_library(pico_scroll INTERFACE)
target_sources(pico_scroll INTERFACE
set(PICO_SCROLL_SOURCES
${CMAKE_CURRENT_LIST_DIR}/pico_scroll.cpp
${CMAKE_CURRENT_LIST_DIR}/pico_scroll_font.cpp)
target_sources(pico_scroll INTERFACE
${PICO_SCROLL_SOURCES}
)
target_include_directories(pico_scroll INTERFACE ${CMAKE_CURRENT_LIST_DIR})

View File

@ -5,6 +5,7 @@
#include "hardware/i2c.h"
#include "pico_scroll.hpp"
#include "pico_scroll_font.hpp"
enum pin {
SDA = 4,
@ -84,6 +85,77 @@ namespace pimoroni {
memset(__fb, 0, BUFFER_SIZE);
}
void PicoScroll::set_pixels(const char *pixels) {
for (int y = 0; y < HEIGHT; y++) {
for (int x = 0; x < WIDTH; x++) {
set_pixel(x, y, pixels[y * WIDTH + x]);
}
}
}
void PicoScroll::set_bitmap_1d(const char *bitmap, size_t bitmap_len, int brightness, int offset) {
for (int x = 0; x < WIDTH; x++) {
int k = offset + x;
if ((k >= 0) && (k < (int)bitmap_len)) {
unsigned char col = bitmap[k];
for (int y = 0; y < HEIGHT; y++) {
int val = brightness * ((col >> y) & 1);
set_pixel(x, y, val);
}
}
}
}
void PicoScroll::set_text(const char *text, size_t text_len, int brightness, int offset) {
int draw_buffer_len = PicoScroll::WIDTH + 7;
unsigned char draw_buffer[draw_buffer_len];
// clear the scroll, so only need to write visible bytes
clear();
if ((offset < -WIDTH) || (offset > (int)(6 * text_len))) {
return;
}
// compute what can actually be seen, render only that...
// modify offset and bfr_len accordingly
if (offset < 0) {
int space = 1 + (WIDTH + offset) / 6;
if (space < (int)text_len) {
text_len = space;
}
} else {
int start = offset / 6;
offset -= start * 6;
text_len = text_len - start;
if (text_len > 4) {
text_len = 4;
}
}
if (draw_buffer_len > (int)(6 * text_len)) {
draw_buffer_len = 6 * text_len;
}
render_text(text, text_len, draw_buffer, draw_buffer_len);
set_bitmap_1d((const char *)draw_buffer, draw_buffer_len, brightness, offset);
}
void PicoScroll::scroll_text(const char *text, size_t text_len, int brightness, int delay_ms) {
int draw_buffer_len = 6 * text_len;
unsigned char *draw_buffer = (unsigned char *)malloc(sizeof(unsigned char) * draw_buffer_len);
render_text(text, text_len, draw_buffer, draw_buffer_len);
for (int offset = -WIDTH; offset < draw_buffer_len; offset++) {
clear();
set_bitmap_1d((const char *)draw_buffer, draw_buffer_len, brightness, offset);
update();
sleep_ms(delay_ms);
}
free(draw_buffer);
}
void PicoScroll::update() {
i2c_write(COLOR_OFFSET, (const char *)__fb, BUFFER_SIZE);
}

View File

@ -1,3 +1,4 @@
#include <string>
#pragma once
namespace pimoroni {
@ -20,6 +21,16 @@ namespace pimoroni {
public:
void init();
void update();
void set_pixels(const char *pixels);
void set_bitmap_1d(const char *bitmap, size_t bitmap_len, int brightness, int offset);
void scroll_text(const char *text, size_t text_len, int brightness, int delay_ms);
void scroll_text(std::string text, int brightness, int delay_ms=100) {
scroll_text(text.c_str(), text.length(), brightness, delay_ms);
};
void set_text(const char *text, size_t text_len, int brightness, int offset);
void set_text(std::string text, int brightness, int offset=0) {
set_text(text.c_str(), text.length(), brightness, offset);
}
void set_pixel(uint8_t x, uint8_t y, uint8_t v);
void clear();
bool is_pressed(uint8_t button);

View File

@ -1,4 +1,4 @@
#include "pico_scroll_font.h"
#include "pico_scroll_font.hpp"
/* static font data */
static unsigned char __bitmap[256][5] = {
@ -133,12 +133,12 @@ static unsigned char __bitmap[256][5] = {
};
/* render a text string to a pre-allocated buffer - strlen(text) * 6 bytes */
int render(unsigned char *text, int nchr, unsigned char *buffer, int nbfr) {
int render_text(const char *text, unsigned int nchr, unsigned char *buffer, unsigned int nbfr) {
// TODO check nbfr >= 6 * nchr
for (int i = 0; i < nchr; i++) {
unsigned char *symbol = __bitmap[text[i]];
for (int j = 0; j < 5; j++) {
for (unsigned int i = 0; i < nchr; i++) {
unsigned char *symbol = __bitmap[(unsigned int)text[i]];
for (unsigned int j = 0; j < 5; j++) {
buffer[i * 6 + j] = symbol[j];
}
buffer[i * 6 + 5] = 0x0;

View File

@ -0,0 +1,4 @@
#pragma once
// external font API
int render_text(const char *text, unsigned int nchr, unsigned char *buffer, unsigned int nbfr);

View File

@ -1,10 +1,11 @@
include(${CMAKE_CURRENT_LIST_DIR}/../../../libraries/pico_scroll/pico_scroll.cmake)
add_library(usermod_pico_scroll INTERFACE)
target_sources(usermod_pico_scroll INTERFACE
${CMAKE_CURRENT_LIST_DIR}/pico_scroll.c
${CMAKE_CURRENT_LIST_DIR}/pico_scroll_font.c
${CMAKE_CURRENT_LIST_DIR}/pico_scroll.cpp
${CMAKE_CURRENT_LIST_DIR}/../../../libraries/pico_scroll/pico_scroll.cpp
${PICO_SCROLL_SOURCES}
)
target_include_directories(usermod_pico_scroll INTERFACE

View File

@ -12,7 +12,6 @@ PicoScroll *scroll = nullptr;
extern "C" {
#include "pico_scroll.h"
#include "pico_scroll_font.h"
#define NOT_INITIALISED_MSG "Cannot call this function, as picoscroll is not initialised. Call picoscroll.init() first."
#define BUFFER_TOO_SMALL_MSG "bytearray too small: len(image) < width * height."
@ -65,40 +64,12 @@ mp_obj_t picoscroll_scroll_text(mp_obj_t text_obj, mp_obj_t brightness_obj,
mp_obj_t delay_ms_obj) {
if (scroll != nullptr) {
mp_buffer_info_t bufinfo;
unsigned char *buffer;
int text_len, bfr_len;
mp_get_buffer_raise(text_obj, &bufinfo, MP_BUFFER_READ);
unsigned char *values = (unsigned char *)bufinfo.buf;
int brightness = mp_obj_get_int(brightness_obj);
int delay_ms = mp_obj_get_int(delay_ms_obj);
text_len = bufinfo.len;
bfr_len = 6 * text_len;
scroll->scroll_text((const char *)bufinfo.buf, bufinfo.len, brightness, delay_ms);
int width = PicoScroll::WIDTH;
int height = PicoScroll::HEIGHT;
buffer = (unsigned char *)m_malloc(sizeof(unsigned char) * bfr_len);
render(values, text_len, buffer, bfr_len);
for (int offset = -width; offset < bfr_len; offset++) {
scroll->clear();
for (int x = 0; x < width; x++) {
int k = offset + x;
if ((k >= 0) && (k < bfr_len)) {
unsigned char col = buffer[k];
for (int y = 0; y < height; y++) {
int val = brightness * ((col >> y) & 1);
scroll->set_pixel(x, y, val);
}
}
}
scroll->update();
sleep_ms(delay_ms);
}
m_free(buffer);
} else {
mp_raise_msg(&mp_type_RuntimeError, NOT_INITIALISED_MSG);
}
@ -110,60 +81,11 @@ mp_obj_t picoscroll_show_text(mp_obj_t text_obj, mp_obj_t brightness_obj,
mp_obj_t offset_obj) {
if (scroll != nullptr) {
mp_buffer_info_t bufinfo;
unsigned char buffer[PicoScroll::WIDTH + 7];
int text_len, bfr_len;
mp_get_buffer_raise(text_obj, &bufinfo, MP_BUFFER_READ);
unsigned char *values = (unsigned char *)bufinfo.buf;
int brightness = mp_obj_get_int(brightness_obj);
int offset = mp_obj_get_int(offset_obj);
text_len = bufinfo.len;
bfr_len = PicoScroll::WIDTH + 7;
int width = PicoScroll::WIDTH;
int height = PicoScroll::HEIGHT;
// clear the scroll, so only need to write visible bytes
scroll->clear();
if ((offset < -width) || (offset > 6 * text_len)) {
return mp_const_none;
}
// compute what can actually be seen, render only that...
// modify offset and bfr_len accordingly
if (offset < 0) {
int space = 1 + (width + offset) / 6;
if (space < text_len) {
text_len = space;
}
} else {
int start = offset / 6;
offset -= start * 6;
text_len = text_len - start;
if (text_len > 4) {
text_len = 4;
}
}
if (bfr_len > 6 * text_len) {
bfr_len = 6 * text_len;
}
render(values, text_len, buffer, bfr_len);
for (int x = 0; x < width; x++) {
int k = offset + x;
if ((k >= 0) && (k < bfr_len)) {
unsigned char col = buffer[k];
for (int y = 0; y < height; y++) {
int val = brightness * ((col >> y) & 1);
scroll->set_pixel(x, y, val);
}
}
}
scroll->set_text((const char *)bufinfo.buf, bufinfo.len, brightness, offset);
} else {
mp_raise_msg(&mp_type_RuntimeError, NOT_INITIALISED_MSG);
}
@ -180,14 +102,7 @@ mp_obj_t picoscroll_set_pixels(mp_obj_t image_obj) {
mp_raise_msg(&mp_type_IndexError, BUFFER_TOO_SMALL_MSG);
}
unsigned char *values = (unsigned char *)bufinfo.buf;
for (int y = 0; y < PicoScroll::HEIGHT; y++) {
for (int x = 0; x < PicoScroll::WIDTH; x++) {
int val = values[y * PicoScroll::WIDTH + x];
scroll->set_pixel(x, y, val);
}
}
scroll->set_pixels((const char*)bufinfo.buf);
} else
mp_raise_msg(&mp_type_RuntimeError, NOT_INITIALISED_MSG);
@ -202,34 +117,15 @@ mp_obj_t picoscroll_show_bitmap_1d(mp_obj_t bitmap_obj, mp_obj_t brightness_obj,
int offset = mp_obj_get_int(offset_obj);
int brightness = mp_obj_get_int(brightness_obj);
int length = bufinfo.len;
int width = PicoScroll::WIDTH;
int height = PicoScroll::HEIGHT;
// this obviously shouldn't happen as the scroll is 17x7 pixels
// would fall off end of byte if this the case
if (height > 8) {
mp_raise_msg(&mp_type_RuntimeError, INCORRECT_SIZE_MSG);
}
unsigned char *values = (unsigned char *)bufinfo.buf;
// clear the scroll, so only need to write visible bytes
scroll->clear();
if ((offset < -width) || (offset > length)) {
if ((offset < -PicoScroll::WIDTH) || (offset > length)) {
return mp_const_none;
}
for (int x = 0; x < width; x++) {
int k = offset + x;
if ((k >= 0) && (k < length)) {
unsigned char col = values[k];
for (int y = 0; y < height; y++) {
int val = brightness * ((col >> y) & 1);
scroll->set_pixel(x, y, val);
}
}
}
scroll->set_bitmap_1d((const char *)bufinfo.buf, bufinfo.len, brightness, offset);
} else {
mp_raise_msg(&mp_type_RuntimeError, NOT_INITIALISED_MSG);
}

View File

@ -1,4 +0,0 @@
#pragma once
// external font API
int render(unsigned char *text, int nchr, unsigned char *buffer, int nbfr);