diff --git a/examples/pico_scroll/CMakeLists.txt b/examples/pico_scroll/CMakeLists.txt index f77148c5..30c1f752 100644 --- a/examples/pico_scroll/CMakeLists.txt +++ b/examples/pico_scroll/CMakeLists.txt @@ -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) \ No newline at end of file diff --git a/examples/pico_scroll/demo.cmake b/examples/pico_scroll/demo.cmake new file mode 100644 index 00000000..f77148c5 --- /dev/null +++ b/examples/pico_scroll/demo.cmake @@ -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) diff --git a/examples/pico_scroll/demo.cpp b/examples/pico_scroll/demo.cpp index 12dacbe7..79122978 100644 --- a/examples/pico_scroll/demo.cpp +++ b/examples/pico_scroll/demo.cpp @@ -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; diff --git a/examples/pico_scroll/scroll-text.cmake b/examples/pico_scroll/scroll-text.cmake new file mode 100644 index 00000000..38081ed9 --- /dev/null +++ b/examples/pico_scroll/scroll-text.cmake @@ -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) diff --git a/examples/pico_scroll/scroll-text.cpp b/examples/pico_scroll/scroll-text.cpp new file mode 100644 index 00000000..6ab9b8d5 --- /dev/null +++ b/examples/pico_scroll/scroll-text.cpp @@ -0,0 +1,41 @@ +#include +#include +#include +#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; +} diff --git a/libraries/pico_scroll/CMakeLists.txt b/libraries/pico_scroll/CMakeLists.txt index 33c3cafa..2473e623 100644 --- a/libraries/pico_scroll/CMakeLists.txt +++ b/libraries/pico_scroll/CMakeLists.txt @@ -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) \ No newline at end of file +include(pico_scroll.cmake) \ No newline at end of file diff --git a/libraries/pico_scroll/pico_scroll.cmake b/libraries/pico_scroll/pico_scroll.cmake index 33c3cafa..3b42f97d 100644 --- a/libraries/pico_scroll/pico_scroll.cmake +++ b/libraries/pico_scroll/pico_scroll.cmake @@ -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}) diff --git a/libraries/pico_scroll/pico_scroll.cpp b/libraries/pico_scroll/pico_scroll.cpp index 4b36c47d..06477639 100644 --- a/libraries/pico_scroll/pico_scroll.cpp +++ b/libraries/pico_scroll/pico_scroll.cpp @@ -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); } diff --git a/libraries/pico_scroll/pico_scroll.hpp b/libraries/pico_scroll/pico_scroll.hpp index d2981273..4cef0e3d 100644 --- a/libraries/pico_scroll/pico_scroll.hpp +++ b/libraries/pico_scroll/pico_scroll.hpp @@ -1,3 +1,4 @@ +#include #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); diff --git a/micropython/modules/pico_scroll/pico_scroll_font.c b/libraries/pico_scroll/pico_scroll_font.cpp similarity index 96% rename from micropython/modules/pico_scroll/pico_scroll_font.c rename to libraries/pico_scroll/pico_scroll_font.cpp index e1941474..4b92b2fb 100644 --- a/micropython/modules/pico_scroll/pico_scroll_font.c +++ b/libraries/pico_scroll/pico_scroll_font.cpp @@ -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; diff --git a/libraries/pico_scroll/pico_scroll_font.hpp b/libraries/pico_scroll/pico_scroll_font.hpp new file mode 100644 index 00000000..6c4fc0b5 --- /dev/null +++ b/libraries/pico_scroll/pico_scroll_font.hpp @@ -0,0 +1,4 @@ +#pragma once + +// external font API +int render_text(const char *text, unsigned int nchr, unsigned char *buffer, unsigned int nbfr); diff --git a/micropython/modules/pico_scroll/micropython.cmake b/micropython/modules/pico_scroll/micropython.cmake index d08dc4cb..9f3f9b53 100644 --- a/micropython/modules/pico_scroll/micropython.cmake +++ b/micropython/modules/pico_scroll/micropython.cmake @@ -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 diff --git a/micropython/modules/pico_scroll/pico_scroll.cpp b/micropython/modules/pico_scroll/pico_scroll.cpp index fa9f935d..2715e45c 100644 --- a/micropython/modules/pico_scroll/pico_scroll.cpp +++ b/micropython/modules/pico_scroll/pico_scroll.cpp @@ -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); } diff --git a/micropython/modules/pico_scroll/pico_scroll_font.h b/micropython/modules/pico_scroll/pico_scroll_font.h deleted file mode 100644 index 0490dc68..00000000 --- a/micropython/modules/pico_scroll/pico_scroll_font.h +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once - -// external font API -int render(unsigned char *text, int nchr, unsigned char *buffer, int nbfr);