From 1bb4383074988717095b4ef109d9329f76447efd Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Thu, 2 Dec 2021 12:41:30 +0000 Subject: [PATCH] HUB75 Perf, C++ scrolling text, docs --- drivers/hub75/hub75.cpp | 21 ++-- drivers/hub75/hub75.hpp | 5 +- examples/interstate75/CMakeLists.txt | 3 +- examples/interstate75/font_outline_10x14.hpp | 100 ++++++++++++++++++ .../interstate75_scrolling_text.cmake | 14 +++ .../interstate75_scrolling_text.cpp | 79 ++++++++++++++ micropython/modules/hub75/README.md | 70 ++++++++++-- micropython/modules/hub75/hub75.c | 2 + micropython/modules/hub75/hub75.cpp | 24 +++-- micropython/modules/hub75/hub75.h | 3 +- 10 files changed, 292 insertions(+), 29 deletions(-) create mode 100644 examples/interstate75/font_outline_10x14.hpp create mode 100644 examples/interstate75/interstate75_scrolling_text.cmake create mode 100644 examples/interstate75/interstate75_scrolling_text.cpp diff --git a/drivers/hub75/hub75.cpp b/drivers/hub75/hub75.cpp index 13ace73e..d6f0a439 100644 --- a/drivers/hub75/hub75.cpp +++ b/drivers/hub75/hub75.cpp @@ -36,7 +36,6 @@ Pixel hsv_to_rgb(float h, float s, float v) { } } - Hub75::Hub75(uint width, uint height, Pixel *buffer, PanelType panel_type, bool inverted_stb) : width(width), height(height), panel_type(panel_type), inverted_stb(inverted_stb) { @@ -77,7 +76,7 @@ Hub75::Hub75(uint width, uint height, Pixel *buffer, PanelType panel_type, bool } } -void Hub75::set_color(uint x, uint y, uint32_t c) { +void Hub75::set_color(uint x, uint y, Pixel c) { int offset = 0; if(x >= width || y >= height) return; if(y >= height / 2) { @@ -87,15 +86,15 @@ void Hub75::set_color(uint x, uint y, uint32_t c) { } else { offset = (y * width + x) * 2; } - front_buffer[offset].color = c; + front_buffer[offset] = c; } void Hub75::set_rgb(uint x, uint y, uint8_t r, uint8_t g, uint8_t b) { - set_color(x, y, Pixel(r, g, b).color); + set_color(x, y, Pixel(r, g, b)); } void Hub75::set_hsv(uint x, uint y, float h, float s, float v) { - set_color(x, y, hsv_to_rgb(h, s, v).color); + set_color(x, y, hsv_to_rgb(h, s, v)); } void Hub75::FM6126A_write_register(uint16_t value, uint8_t position) { @@ -172,7 +171,6 @@ void Hub75::start(irq_handler_t handler) { channel_config_set_bswap(&flip_config, false); dma_channel_configure(dma_flip_channel, &flip_config, nullptr, nullptr, 0, false); - // Same handler for both DMA channels irq_set_exclusive_handler(DMA_IRQ_0, handler); irq_set_exclusive_handler(DMA_IRQ_1, handler); @@ -255,7 +253,14 @@ void Hub75::clear() { } } -void Hub75::flip() { +void Hub75::flip(bool copybuffer) { + dma_channel_config flip_config = dma_get_channel_config(dma_flip_channel); + channel_config_set_read_increment(&flip_config, copybuffer); + dma_channel_configure(dma_flip_channel, &flip_config, nullptr, nullptr, 0, false); + + dma_channel_set_read_addr(dma_flip_channel, copybuffer ? front_buffer : &background, false); + dma_channel_set_write_addr(dma_flip_channel, back_buffer, false); + // Flip and block until the front buffer has been prepared do_flip = true; while(do_flip) { @@ -291,8 +296,6 @@ void Hub75::dma_complete() { back_buffer = front_buffer; front_buffer = tmp; // Then, read the contents of the back buffer into the front buffer - dma_channel_set_read_addr(dma_flip_channel, back_buffer, false); - dma_channel_set_write_addr(dma_flip_channel, front_buffer, false); dma_channel_set_trans_count(dma_flip_channel, width * height, true); } diff --git a/drivers/hub75/hub75.hpp b/drivers/hub75/hub75.hpp index 30234cd4..f469ee28 100644 --- a/drivers/hub75/hub75.hpp +++ b/drivers/hub75/hub75.hpp @@ -57,6 +57,7 @@ class Hub75 { bool managed_buffer = false; PanelType panel_type; bool inverted_stb = false; + Pixel background = 0; // DMA & PIO uint dma_channel = 0; @@ -116,13 +117,13 @@ class Hub75 { void FM6126A_write_register(uint16_t value, uint8_t position); void FM6126A_setup(); - void set_color(uint x, uint y, uint32_t c); + void set_color(uint x, uint y, Pixel c); void set_rgb(uint x, uint y, uint8_t r, uint8_t g, uint8_t b); void set_hsv(uint x, uint y, float r, float g, float b); void display_update(); void clear(); void start(irq_handler_t handler); void stop(irq_handler_t handler); - void flip(); + void flip(bool copybuffer=true); void dma_complete(); }; \ No newline at end of file diff --git a/examples/interstate75/CMakeLists.txt b/examples/interstate75/CMakeLists.txt index a0c31fbc..7d8d4278 100644 --- a/examples/interstate75/CMakeLists.txt +++ b/examples/interstate75/CMakeLists.txt @@ -1,2 +1,3 @@ include(interstate75_hello_world.cmake) -include(interstate75_pio_dma.cmake) \ No newline at end of file +include(interstate75_pio_dma.cmake) +include(interstate75_scrolling_text.cmake) \ No newline at end of file diff --git a/examples/interstate75/font_outline_10x14.hpp b/examples/interstate75/font_outline_10x14.hpp new file mode 100644 index 00000000..2d32a570 --- /dev/null +++ b/examples/interstate75/font_outline_10x14.hpp @@ -0,0 +1,100 @@ +const uint8_t letter_width = 10; +const uint8_t letter_height = 14; +uint32_t font[][letter_width] = { + {0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, // " " + {0x0ffc, 0x0a04, 0x0ffc, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, // "!" + {0x007c, 0x0044, 0x007c, 0x0044, 0x007c, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, // """ + {0x03f0, 0x02d0, 0x0edc, 0x0804, 0x0edc, 0x0edc, 0x0804, 0x0edc, 0x02d0, 0x03f0}, // "//" + {0x0ef8, 0x0b8c, 0x1b76, 0x1002, 0x1b76, 0x0cd4, 0x079c, 0x0000, 0x0000, 0x0000}, // "$" + {0x0038, 0x006c, 0x0f54, 0x09ec, 0x0e78, 0x079c, 0x0de4, 0x0abc, 0x0d80, 0x0700}, // "%" + {0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, // "&" + {0x007c, 0x0044, 0x007c, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, // "'" + {0x03f0, 0x0e1c, 0x19e6, 0x173a, 0x1c0e, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, // "(" + {0x1c0e, 0x173a, 0x19e6, 0x0e1c, 0x03f0, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, // ")" + {0x00fc, 0x00b4, 0x00cc, 0x00cc, 0x00b4, 0x00fc, 0x0000, 0x0000, 0x0000, 0x0000}, // "*" + {0x01c0, 0x0140, 0x0770, 0x0410, 0x0770, 0x0140, 0x01c0, 0x0000, 0x0000, 0x0000}, // "+" + {0x1c00, 0x1700, 0x1900, 0x0f00, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, // "," + {0x01c0, 0x0140, 0x0140, 0x0140, 0x0140, 0x0140, 0x01c0, 0x0000, 0x0000, 0x0000}, // "-" + {0x0e00, 0x0a00, 0x0e00, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, // "." + {0x1e00, 0x1380, 0x1ce0, 0x0738, 0x01ce, 0x0072, 0x001e, 0x0000, 0x0000, 0x0000}, // "/" + {0x07f8, 0x0c0c, 0x0bf4, 0x0a14, 0x0bf4, 0x0c0c, 0x07f8, 0x0000, 0x0000, 0x0000}, // "0" + {0x0e70, 0x0a58, 0x0bec, 0x0804, 0x0bfc, 0x0a00, 0x0e00, 0x0000, 0x0000, 0x0000}, // "1" + {0x0e38, 0x0b2c, 0x09b4, 0x0ad4, 0x0b74, 0x0b8c, 0x0ef8, 0x0000, 0x0000, 0x0000}, // "2" + {0x0738, 0x0d2c, 0x0b34, 0x0bf4, 0x0b34, 0x0ccc, 0x07f8, 0x0000, 0x0000, 0x0000}, // "3" + {0x03c0, 0x0270, 0x0298, 0x0eec, 0x0804, 0x0efc, 0x0380, 0x0000, 0x0000, 0x0000}, // "4" + {0x0efc, 0x0a84, 0x0ab4, 0x0ab4, 0x0bb4, 0x0c74, 0x07dc, 0x0000, 0x0000, 0x0000}, // "5" + {0x07f8, 0x0c0c, 0x0bb4, 0x0ab4, 0x0bb4, 0x0c74, 0x07dc, 0x0000, 0x0000, 0x0000}, // "6" + {0x001c, 0x0014, 0x0f94, 0x08f4, 0x0f34, 0x01c4, 0x007c, 0x0000, 0x0000, 0x0000}, // "7" + {0x07f8, 0x0c4c, 0x0bb4, 0x0ab4, 0x0bb4, 0x0c4c, 0x07f8, 0x0000, 0x0000, 0x0000}, // "8" + {0x0ef8, 0x0b8c, 0x0b74, 0x0b54, 0x0b74, 0x0c0c, 0x07f8, 0x0000, 0x0000, 0x0000}, // "9" + {0x0e1c, 0x0a14, 0x0e1c, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, // ":" + {0x1c00, 0x171c, 0x1914, 0x0f1c, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, // ";" + {0x0380, 0x06c0, 0x0d60, 0x0ba0, 0x0ee0, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, // "<" + {0x0ee0, 0x0aa0, 0x0aa0, 0x0aa0, 0x0aa0, 0x0aa0, 0x0aa0, 0x0ee0, 0x0000, 0x0000}, // "=" + {0x0ee0, 0x0ba0, 0x0d60, 0x06c0, 0x0380, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, // ">" + {0x0000, 0x001c, 0x0fd4, 0x0a74, 0x0fb4, 0x00cc, 0x0078, 0x0000, 0x0000, 0x0000}, // "?" + {0x0ff0, 0x1818, 0x37ec, 0x2c74, 0x2bb4, 0x2bb4, 0x3c0c, 0x07f8, 0x0000, 0x0000}, // "@" + {0x0f80, 0x08f0, 0x0f1c, 0x0164, 0x0f1c, 0x08f0, 0x0f80, 0x0000, 0x0000, 0x0000}, // "A" + {0x0ffc, 0x0804, 0x0bb4, 0x0bb4, 0x0c4c, 0x07f8, 0x0000, 0x0000, 0x0000, 0x0000}, // "B" + {0x07f8, 0x0c0c, 0x0bf4, 0x0a14, 0x0a14, 0x0e1c, 0x0000, 0x0000, 0x0000, 0x0000}, // "C" + {0x0ffc, 0x0804, 0x0bf4, 0x0bf4, 0x0c0c, 0x07f8, 0x0000, 0x0000, 0x0000, 0x0000}, // "D" + {0x0ffc, 0x0804, 0x0bb4, 0x0ab4, 0x0ab4, 0x0efc, 0x0000, 0x0000, 0x0000, 0x0000}, // "E" + {0x0ffc, 0x0804, 0x0fb4, 0x00b4, 0x00f4, 0x001c, 0x0000, 0x0000, 0x0000, 0x0000}, // "F" + {0x07f8, 0x0c0c, 0x0bf4, 0x0bd4, 0x0b54, 0x0c5c, 0x07c0, 0x0000, 0x0000, 0x0000}, // "G" + {0x0ffc, 0x0804, 0x0fbc, 0x00a0, 0x0fbc, 0x0804, 0x0ffc, 0x0000, 0x0000, 0x0000}, // "H" + {0x0e1c, 0x0a14, 0x0bf4, 0x0804, 0x0bf4, 0x0a14, 0x0e1c, 0x0000, 0x0000, 0x0000}, // "I" + {0x0e1c, 0x0a14, 0x0bf4, 0x0c04, 0x07f4, 0x0014, 0x001c, 0x0000, 0x0000, 0x0000}, // "J" + {0x0ffc, 0x0804, 0x0fbc, 0x0e5c, 0x09e4, 0x0f3c, 0x0000, 0x0000, 0x0000, 0x0000}, // "K" + {0x0ffc, 0x0804, 0x0bfc, 0x0a00, 0x0a00, 0x0e00, 0x0000, 0x0000, 0x0000, 0x0000}, // "L" + {0x0ffc, 0x0804, 0x0fec, 0x00d8, 0x00b0, 0x00b0, 0x00d8, 0x0fec, 0x0804, 0x0ffc}, // "M" + {0x0ffc, 0x0804, 0x0fcc, 0x0738, 0x0cfc, 0x0804, 0x0ffc, 0x0000, 0x0000, 0x0000}, // "N" + {0x07f8, 0x0c0c, 0x0bf4, 0x0a14, 0x0a14, 0x0bf4, 0x0c0c, 0x07f8, 0x0000, 0x0000}, // "O" + {0x0ffc, 0x0804, 0x0f74, 0x0174, 0x018c, 0x00f8, 0x0000, 0x0000, 0x0000, 0x0000}, // "P" + {0x07f8, 0x0c0c, 0x0bf4, 0x0a14, 0x0a14, 0x1bf4, 0x140c, 0x17f8, 0x1c00, 0x0000}, // "Q" + {0x0ffc, 0x0804, 0x0f74, 0x0e74, 0x098c, 0x0ff8, 0x0000, 0x0000, 0x0000, 0x0000}, // "R" + {0x0ef8, 0x0b8c, 0x0b74, 0x0b54, 0x0cd4, 0x079c, 0x0000, 0x0000, 0x0000, 0x0000}, // "S" + {0x001c, 0x0014, 0x0ff4, 0x0804, 0x0ff4, 0x0014, 0x001c, 0x0000, 0x0000, 0x0000}, // "T" + {0x07fc, 0x0c04, 0x0bfc, 0x0a00, 0x0bfc, 0x0c04, 0x07fc, 0x0000, 0x0000, 0x0000}, // "U" + {0x01fc, 0x0704, 0x0cfc, 0x0b80, 0x0cfc, 0x0704, 0x01fc, 0x0000, 0x0000, 0x0000}, // "V" + {0x01fc, 0x0704, 0x0cfc, 0x0bc0, 0x0c40, 0x0bc0, 0x0cfc, 0x0704, 0x01fc, 0x0000}, // "W" + {0x0f3c, 0x09e4, 0x0edc, 0x0330, 0x0edc, 0x09e4, 0x0f3c, 0x0000, 0x0000, 0x0000}, // "X" + {0x003c, 0x00e4, 0x0f9c, 0x0870, 0x0f9c, 0x00e4, 0x003c, 0x0000, 0x0000, 0x0000}, // "Y" + {0x0f1c, 0x0994, 0x0af4, 0x0b34, 0x0bd4, 0x0a64, 0x0e3c, 0x0000, 0x0000, 0x0000}, // "Z" + {0x0ffc, 0x0804, 0x0bf4, 0x0a14, 0x0e1c, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, // "{" + {0x001e, 0x0072, 0x01ce, 0x0738, 0x1ce0, 0x1380, 0x1e00, 0x0000, 0x0000, 0x0000}, // "\" + {0x0e1c, 0x0a14, 0x0bf4, 0x0804, 0x0ffc, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, // "}" + {0x0070, 0x0058, 0x006c, 0x0034, 0x006c, 0x0058, 0x0070, 0x0000, 0x0000, 0x0000}, // "^" + {0x1c00, 0x1400, 0x1400, 0x1400, 0x1400, 0x1400, 0x1400, 0x1c00, 0x0000, 0x0000}, // "_" + {0x000e, 0x001a, 0x0036, 0x002c, 0x0038, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, // "`" + {0x07c0, 0x0c60, 0x0ba0, 0x0aa0, 0x0ba0, 0x0c60, 0x0bc0, 0x0e00, 0x0000, 0x0000}, // "a" + {0x0ffc, 0x0804, 0x0bbc, 0x0ba0, 0x0c60, 0x07c0, 0x0000, 0x0000, 0x0000, 0x0000}, // "b" + {0x07c0, 0x0c60, 0x0ba0, 0x0aa0, 0x0ee0, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, // "c" + {0x07c0, 0x0c60, 0x0ba0, 0x0bbc, 0x0804, 0x0ffc, 0x0000, 0x0000, 0x0000, 0x0000}, // "d" + {0x07c0, 0x0c60, 0x0aa0, 0x0aa0, 0x0b60, 0x0fc0, 0x0000, 0x0000, 0x0000, 0x0000}, // "e" + {0x0ff8, 0x080c, 0x0fb4, 0x00f4, 0x001c, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, // "f" + {0x1fc0, 0x3660, 0x2da0, 0x2da0, 0x2da0, 0x3060, 0x1fc0, 0x0000, 0x0000, 0x0000}, // "g" + {0x0ffc, 0x0804, 0x0fbc, 0x0fa0, 0x0860, 0x0fc0, 0x0000, 0x0000, 0x0000, 0x0000}, // "h" + {0x0ff8, 0x0828, 0x0ff8, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, // "i" + {0x1c00, 0x1400, 0x17f8, 0x1828, 0x0ff8, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, // "j" + {0x0ffc, 0x0804, 0x0efc, 0x0d60, 0x0ba0, 0x0ee0, 0x0000, 0x0000, 0x0000, 0x0000}, // "k" + {0x07fc, 0x0c04, 0x0bfc, 0x0a00, 0x0e00, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, // "l" + {0x0fc0, 0x0860, 0x0fa0, 0x07a0, 0x0460, 0x07a0, 0x0fa0, 0x0860, 0x0fc0, 0x0000}, // "m" + {0x0fc0, 0x0860, 0x0fa0, 0x0fa0, 0x0860, 0x0fc0, 0x0000, 0x0000, 0x0000, 0x0000}, // "n" + {0x07c0, 0x0c60, 0x0ba0, 0x0aa0, 0x0ba0, 0x0c60, 0x07c0, 0x0000, 0x0000, 0x0000}, // "o" + {0x3fe0, 0x2020, 0x3da0, 0x05a0, 0x0660, 0x03c0, 0x0000, 0x0000, 0x0000, 0x0000}, // "p" + {0x03c0, 0x0660, 0x05a0, 0x3da0, 0x2020, 0x37e0, 0x1c00, 0x0000, 0x0000, 0x0000}, // "q" + {0x0fc0, 0x0860, 0x0fa0, 0x00a0, 0x00e0, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, // "r" + {0x0fc0, 0x0b60, 0x0aa0, 0x0da0, 0x07e0, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, // "s" + {0x01c0, 0x0770, 0x0c10, 0x0b70, 0x0bc0, 0x0e00, 0x0000, 0x0000, 0x0000, 0x0000}, // "t" + {0x07e0, 0x0c20, 0x0be0, 0x0be0, 0x0c20, 0x07e0, 0x0000, 0x0000, 0x0000, 0x0000}, // "u" + {0x01e0, 0x0720, 0x0ce0, 0x0b80, 0x0ce0, 0x0720, 0x01e0, 0x0000, 0x0000, 0x0000}, // "v" + {0x01e0, 0x0720, 0x0ce0, 0x0b80, 0x0c80, 0x0b80, 0x0ce0, 0x0720, 0x01e0, 0x0000}, // "w" + {0x0ee0, 0x0ba0, 0x0d60, 0x06c0, 0x0d60, 0x0ba0, 0x0ee0, 0x0000, 0x0000, 0x0000}, // "x" + {0x1de0, 0x1720, 0x1ae0, 0x0d80, 0x06e0, 0x0320, 0x01e0, 0x0000, 0x0000, 0x0000}, // "y" + {0x0ee0, 0x0ba0, 0x09a0, 0x0aa0, 0x0b20, 0x0ba0, 0x0ee0, 0x0000, 0x0000, 0x0000}, // "z" + {0x01e0, 0x0f3c, 0x18c6, 0x17fa, 0x1c0e, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, // "{" + {0x1ffe, 0x1002, 0x1ffe, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, // "|" + {0x1c0e, 0x17fa, 0x18c6, 0x0f3c, 0x01e0, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, // "}" + {0x0380, 0x02c0, 0x0340, 0x0340, 0x02c0, 0x02c0, 0x0340, 0x01c0, 0x0000, 0x0000}, // "~" + {0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, // "" +}; \ No newline at end of file diff --git a/examples/interstate75/interstate75_scrolling_text.cmake b/examples/interstate75/interstate75_scrolling_text.cmake new file mode 100644 index 00000000..02fd7e55 --- /dev/null +++ b/examples/interstate75/interstate75_scrolling_text.cmake @@ -0,0 +1,14 @@ +set(OUTPUT_NAME interstate75_scrolling_text) +add_executable(${OUTPUT_NAME} interstate75_scrolling_text.cpp) + +# enable usb output +pico_enable_stdio_usb(${OUTPUT_NAME} 1) + +pico_add_extra_outputs(${OUTPUT_NAME}) + +target_link_libraries(${OUTPUT_NAME} + pico_stdlib + pico_multicore + hardware_vreg + hub75 +) diff --git a/examples/interstate75/interstate75_scrolling_text.cpp b/examples/interstate75/interstate75_scrolling_text.cpp new file mode 100644 index 00000000..6036603d --- /dev/null +++ b/examples/interstate75/interstate75_scrolling_text.cpp @@ -0,0 +1,79 @@ +#include +#include +#include +#include +#include + +#include "pico/stdlib.h" +#include "hardware/vreg.h" + +#include "common/pimoroni_common.hpp" + +#include "hub75.hpp" +#include "font_outline_10x14.hpp" + +using namespace pimoroni; + +// Display size in pixels +// Should be either 64x64 or 32x32 but perhaps 64x32 an other sizes will work. +// Note: this example uses only 5 address lines so it's limited to 64 pixel high displays (32*2). +const uint8_t WIDTH = 64; +const uint8_t HEIGHT = 64; + +Hub75 hub75(WIDTH, HEIGHT, nullptr, PANEL_GENERIC, true); + +void __isr dma_complete() { + hub75.dma_complete(); +} + +void scroll_text(std::string_view text, uint y, float t, Pixel color) { + uint text_length = text.length(); + uint x = uint(t); + uint letter = uint((x / letter_width) % text_length); + uint pixel = x % letter_width; + char c = text.at(letter); + for (auto s_x = 0u; s_x < WIDTH; s_x++) { + uint32_t col = font[c - 32][pixel]; + for (auto s_y = 0u; s_y < letter_height; s_y++) { + // Calculate a bit of wiggle! + uint o_y = int(sinf((t / 3.0f) + s_x / 30.0f) * 8); + // Step through the uint32 that represents a single column + // of the current character, and render pixels for set bits. + if(col & (1 << s_y)) { + hub75.set_color(s_x, s_y + y + o_y, color); + } + } + // Advance to the text pixel/character + pixel++; + if (pixel == letter_width) { + pixel = 0; + letter++; + if (letter == text_length) letter = 0; + c = text.at(letter); + } + } +} + +int main() { + vreg_set_voltage(VREG_VOLTAGE_1_20); + sleep_us(100); + set_sys_clock_khz(266000, true); + + hub75.start(dma_complete); + + std::string text = " Hello World! How are you today? "; + + // Basic loop to draw something to the screen. + // This gets the distance from the middle of the display and uses it to paint a circular colour cycle. + while (true) { + hub75.background = hsv_to_rgb(millis() / 10000.0f, 1.0f, 0.5f); + + // Shadow + scroll_text(text, HEIGHT / 2 - letter_height / 2 + 1, (millis() + 50) / 50.0f, Pixel(0, 0, 0)); + // Text + scroll_text(text, HEIGHT / 2 - letter_height / 2, millis() / 50.0f, Pixel(255, 255, 255)); + + hub75.flip(false); // Flip and clear to the background colour + sleep_ms(1000 / 60); + } +} diff --git a/micropython/modules/hub75/README.md b/micropython/modules/hub75/README.md index e3318a3e..5ae6d2c6 100644 --- a/micropython/modules/hub75/README.md +++ b/micropython/modules/hub75/README.md @@ -4,17 +4,22 @@ The Interstate 75 library is intended for the Interstate 75 "HUB75" matrix panel It can, in theory, be used with your own custom wiring, though custom pin assignments are not supported yet. -- [Notes On PIO Limitations](#notes-on-pio-limitations) +- [Notes On PIO & DMA Limitations](#notes-on-pio--dma-limitations) - [Getting Started](#getting-started) - [FM6216A Panels](#fm6216a-panels) -- [Set A Pixel](#set-a-pixel) - - [RGB](#rgb) - - [HSV](#hsv) +- [Quick Reference](#quick-reference) + - [Set A Pixel](#set-a-pixel) + - [Color](#color) + - [RGB](#rgb) + - [HSV](#hsv) + - [Update The Display](#update-the-display) -## Notes On PIO Limitations +## Notes On PIO & DMA Limitations The Interstate 75 driver uses the PIO hardware on the RP2040. There are only two PIOs with four state machines each, and i75 uses one of these (`PIO0`) and two state machines- one for clocking out pixels, and another for latching/lighting a row. +It also uses two DMA channels, one to copy pixel data from the back buffer back to the front buffer and one to supply the row driving PIO with row data. + ## Getting Started Contruct a new `Hub75` instance, specifying the width/height of the display and any additional options. @@ -49,22 +54,69 @@ HEIGHT = 64 matrix = hub75.Hub75(WIDTH, HEIGHT, panel_type=hub75.PANEL_FM6126A) ``` -## Set A Pixel +## Quick Reference + +### Set A Pixel You can set the colour of an pixel in either the RGB colourspace, or HSV (Hue, Saturation, Value). HSV is useful for creating rainbow patterns. -### RGB +#### Color + +Set the top left-most LED - `0, 0` - to a pre-calculated colour. + +```python +matrix.set_color(0, 0, color) +``` + +There are a couple of functions for generating colours, which take your red, green and blue values, gamma correct them and pack them into a single number. By converting a colour and saving this value you can pay the cost of conversion up-front and drawing pixels in that colour will be faster: + +```python +red = hub75.color(255, 0, 0) +red = hub75.color_hsv(1.0, 0, 0) +``` + +Eg: + +```python +red = hub75.color(255, 0, 0) + +for x in range(32): + matrix.set_color(0, 0, red) +``` + +#### RGB Set the top left-most LED - `0, 0` - to Purple `255, 0, 255`: ```python -matrixx.set_rgb(0, 0, 255, 0, 255) +matrix.set_rgb(0, 0, 255, 0, 255) ``` -### HSV +#### HSV Set the top left-most LED - `0, o` - to Red `0.0`: ```python matrix.set_hsv(0, 0, 0.0, 1.0, 1.0) ``` + +### Update The Display + +You can update the back buffer - the framebuffer used by the driver to drive the screen - by calling `flip`: + +```python +matrix.flip() +``` + +`flip` will swap the front buffer (the one you draw into) with the back buffer (the one the screen is refreshed from) so that the display can start drawing your changes immediately. + +Since the back buffer contains your *previous* frame it then blocks and copies across the contents of the buffer you've just flipped. + +If you want fresh, clear buffer to draw into at the start of your next frame you can use `flip_and_clear` instead: + +```python +background_color = hub75.color(0, 0, 0) +matrix.flip_and_clear(background_color) +``` + +This will fill your buffer with the background colour, so you don't need to call `clear`. diff --git a/micropython/modules/hub75/hub75.c b/micropython/modules/hub75/hub75.c index 92b936b3..0058a612 100644 --- a/micropython/modules/hub75/hub75.c +++ b/micropython/modules/hub75/hub75.c @@ -13,6 +13,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(Hub75_clear_obj, Hub75_clear); MP_DEFINE_CONST_FUN_OBJ_1(Hub75_start_obj, Hub75_start); MP_DEFINE_CONST_FUN_OBJ_1(Hub75_stop_obj, Hub75_stop); MP_DEFINE_CONST_FUN_OBJ_1(Hub75_flip_obj, Hub75_flip); +MP_DEFINE_CONST_FUN_OBJ_2(Hub75_flip_and_clear_obj, Hub75_flip_and_clear); MP_DEFINE_CONST_FUN_OBJ_3(Hub75_color_obj, Hub75_color); MP_DEFINE_CONST_FUN_OBJ_3(Hub75_color_hsv_obj, Hub75_color_hsv); @@ -30,6 +31,7 @@ STATIC const mp_rom_map_elem_t Hub75_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_start), MP_ROM_PTR(&Hub75_start_obj) }, { MP_ROM_QSTR(MP_QSTR_stop), MP_ROM_PTR(&Hub75_stop_obj) }, { MP_ROM_QSTR(MP_QSTR_flip), MP_ROM_PTR(&Hub75_flip_obj) }, + { MP_ROM_QSTR(MP_QSTR_flip_and_clear), MP_ROM_PTR(&Hub75_flip_and_clear_obj) }, }; STATIC MP_DEFINE_CONST_DICT(Hub75_locals_dict, Hub75_locals_dict_table); diff --git a/micropython/modules/hub75/hub75.cpp b/micropython/modules/hub75/hub75.cpp index 45be9bb3..423d3074 100644 --- a/micropython/modules/hub75/hub75.cpp +++ b/micropython/modules/hub75/hub75.cpp @@ -124,10 +124,17 @@ mp_obj_t Hub75_clear(mp_obj_t self_in) { mp_obj_t Hub75_flip(mp_obj_t self_in) { _Hub75_obj_t *self = MP_OBJ_TO_PTR2(self_in, _Hub75_obj_t); - self->hub75->do_flip = true; - while (self->hub75->do_flip) { - MICROPY_EVENT_POLL_HOOK - } + self->hub75->flip(); + + return mp_const_none; +} + +mp_obj_t Hub75_flip_and_clear(mp_obj_t self_in, mp_obj_t color) { + _Hub75_obj_t *self = MP_OBJ_TO_PTR2(self_in, _Hub75_obj_t); + + self->hub75->background.color = mp_obj_get_int(color); + self->hub75->flip(false); + return mp_const_none; } @@ -165,7 +172,8 @@ mp_obj_t Hub75_set_color_masked(size_t n_args, const mp_obj_t *pos_args, mp_map_ int x = args[ARG_x].u_int; int y = args[ARG_y].u_int; - int c = args[ARG_color].u_int; + Pixel c; + c.color = args[ARG_color].u_int; int m = args[ARG_mask].u_int; _Hub75_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, _Hub75_obj_t); @@ -202,7 +210,8 @@ mp_obj_t Hub75_set_color(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_a int x = args[ARG_x].u_int; int y = args[ARG_y].u_int; - int c = args[ARG_color].u_int; + Pixel c; + c.color = args[ARG_color].u_int; _Hub75_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, _Hub75_obj_t); self->hub75->set_color(x, y, c); @@ -267,7 +276,8 @@ mp_obj_t Hub75_set_hsv(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_arg mp_obj_t Hub75_set_all_color(mp_obj_t self_in, mp_obj_t color) { _Hub75_obj_t *self = MP_OBJ_TO_PTR2(self_in, _Hub75_obj_t); - int c = mp_obj_get_int(color); + Pixel c; + c.color = mp_obj_get_int(color); for (auto x = 0u; x < self->hub75->width; x++) { for (auto y = 0u; y < self->hub75->height; y++) { diff --git a/micropython/modules/hub75/hub75.h b/micropython/modules/hub75/hub75.h index 93582aa6..61637d6b 100644 --- a/micropython/modules/hub75/hub75.h +++ b/micropython/modules/hub75/hub75.h @@ -20,4 +20,5 @@ extern mp_obj_t Hub75_color_hsv(mp_obj_t h, mp_obj_t s, mp_obj_t v); extern mp_obj_t Hub75_set_all_hsv(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); extern mp_obj_t Hub75_set_all_color(mp_obj_t self_in, mp_obj_t color); extern mp_obj_t Hub75_clear(mp_obj_t self_in); -extern mp_obj_t Hub75_flip(mp_obj_t self_in); \ No newline at end of file +extern mp_obj_t Hub75_flip(mp_obj_t self_in); +extern mp_obj_t Hub75_flip_and_clear(mp_obj_t self_in, mp_obj_t color); \ No newline at end of file