From 104c819412d63c9ecfb4eeacd60d3f3e3cde4164 Mon Sep 17 00:00:00 2001 From: Gee Bartlett Date: Fri, 16 Dec 2022 20:53:16 +0000 Subject: [PATCH] Hub75: Update to use Picographics (#601) * have a basic pg working needs optimizing * working well ported some examples from GU * started on micropython module * Fixes to get new Hub75 compiling in MP * stripped down for faster compilling * Update hub75.cmake * added hub75 to galatic and eviro as it is needed for picographics * Update picographics.c * added hu75 update * added _ModPicoGraphics_obj_t * Update hub75.cpp * update bindings * some examples needs linting * added other panel sizes and linted * Update picographics.cpp * Update picographics.c * fixing gc memory allocation * Update hub75.cpp * Update interstate75_balls_demo.cpp * review * broke * working with built in panel defs * still borked * not much change needs review * Update hub75.cpp * reverted alot of things * adding i75 lib * lots of updates ready to test compile mp * Update picographics.h * little tweaks * an inability to count fixed! * fixed some readme's * lots of tiding * fixed linting and removed experimental code * Minor formatting * Minor formatting and cmake tidy * Removed unneeded parts of examples * Final tidy * tidy examples and adding more * updated to new library * documentation tweaks * fixed inclusion of interstate75 module * syncing some stuff * fixed linting Co-authored-by: ZodiusInfuser --- drivers/CMakeLists.txt | 1 + drivers/hub75/hub75.cmake | 5 +- drivers/hub75/hub75.cpp | 122 ++----- drivers/hub75/hub75.hpp | 17 +- drivers/hub75_legacy/CMakeLists.txt | 1 + drivers/hub75_legacy/hub75.cmake | 11 + drivers/hub75_legacy/hub75.cpp | 316 ++++++++++++++++++ drivers/hub75_legacy/hub75.hpp | 129 +++++++ drivers/hub75_legacy/hub75.pio | 158 +++++++++ examples/interstate75/CMakeLists.txt | 8 +- .../interstate75_balls_demo.cmake | 14 + .../interstate75/interstate75_balls_demo.cpp | 124 +++++++ .../interstate75/interstate75_example.cmake | 15 + .../interstate75/interstate75_example.cpp | 59 ++++ .../interstate75_fire_effect.cmake | 15 + .../interstate75/interstate75_fire_effect.cpp | 130 +++++++ .../interstate75/interstate75_rainbow.cmake | 16 + .../interstate75/interstate75_rainbow.cpp | 120 +++++++ .../interstate75_rainbow_text.cmake | 15 + .../interstate75_rainbow_text.cpp | 117 +++++++ examples/interstate75_legacy/CMakeLists.txt | 3 + .../font_outline_10x14.hpp | 0 .../interstate75_hello_world.cmake | 0 .../interstate75_hello_world.cpp | 0 .../interstate75_pio_dma.cmake | 0 .../interstate75_pio_dma.cpp | 0 .../interstate75_scrolling_text.cmake | 0 .../interstate75_scrolling_text.cpp | 0 libraries/CMakeLists.txt | 1 + libraries/interstate75/CMakeLists.txt | 1 + libraries/interstate75/README.md | 84 +++++ libraries/interstate75/interstate75.cmake | 7 + libraries/interstate75/interstate75.hpp | 16 + libraries/pico_graphics/README.md | 4 +- .../examples/interstate75/75W/cheerlights.py | 111 ++++++ .../examples/interstate75/75W/clock.py | 194 +++++++++++ .../examples/interstate75/balls_demo.py | 62 ++++ micropython/examples/interstate75/buttons.py | 46 +++ .../examples/interstate75/font_10x14.py | 100 ------ .../examples/interstate75/font_8x12.py | 100 ------ .../i75_128x64_scrolling_wavy_clock.py | 53 --- .../interstate75/i75_32x32_generic.py | 23 -- .../examples/interstate75/i75_64x32_clock.py | 148 -------- .../interstate75/i75_64x64_fm6126a.py | 23 -- .../interstate75/i75_64x64_generic.py | 23 -- .../interstate75/i75_64x64_scrolling_text.py | 50 --- micropython/examples/interstate75/rainbow.py | 68 ++++ .../examples/interstate75/raw_set_pixel.py | 43 +++ micropython/modules/hub75/README.md | 83 ++--- micropython/modules/hub75/hub75.c | 28 +- micropython/modules/hub75/hub75.cpp | 185 ++-------- micropython/modules/hub75/hub75.h | 12 +- micropython/modules/hub75/micropython.cmake | 20 +- micropython/modules/picographics/README.md | 16 + .../modules/picographics/picographics.c | 11 +- .../modules/picographics/picographics.cpp | 67 ++++ .../modules/picographics/picographics.h | 10 +- micropython/modules_py/interstate75.md | 89 +++++ micropython/modules_py/interstate75.py | 62 ++++ micropython/modules_py/modules_py.cmake | 1 + 60 files changed, 2249 insertions(+), 888 deletions(-) create mode 100644 drivers/hub75_legacy/CMakeLists.txt create mode 100644 drivers/hub75_legacy/hub75.cmake create mode 100644 drivers/hub75_legacy/hub75.cpp create mode 100644 drivers/hub75_legacy/hub75.hpp create mode 100644 drivers/hub75_legacy/hub75.pio create mode 100644 examples/interstate75/interstate75_balls_demo.cmake create mode 100644 examples/interstate75/interstate75_balls_demo.cpp create mode 100644 examples/interstate75/interstate75_example.cmake create mode 100644 examples/interstate75/interstate75_example.cpp create mode 100644 examples/interstate75/interstate75_fire_effect.cmake create mode 100644 examples/interstate75/interstate75_fire_effect.cpp create mode 100644 examples/interstate75/interstate75_rainbow.cmake create mode 100644 examples/interstate75/interstate75_rainbow.cpp create mode 100644 examples/interstate75/interstate75_rainbow_text.cmake create mode 100644 examples/interstate75/interstate75_rainbow_text.cpp create mode 100644 examples/interstate75_legacy/CMakeLists.txt rename examples/{interstate75 => interstate75_legacy}/font_outline_10x14.hpp (100%) rename examples/{interstate75 => interstate75_legacy}/interstate75_hello_world.cmake (100%) rename examples/{interstate75 => interstate75_legacy}/interstate75_hello_world.cpp (100%) rename examples/{interstate75 => interstate75_legacy}/interstate75_pio_dma.cmake (100%) rename examples/{interstate75 => interstate75_legacy}/interstate75_pio_dma.cpp (100%) rename examples/{interstate75 => interstate75_legacy}/interstate75_scrolling_text.cmake (100%) rename examples/{interstate75 => interstate75_legacy}/interstate75_scrolling_text.cpp (100%) create mode 100644 libraries/interstate75/CMakeLists.txt create mode 100644 libraries/interstate75/README.md create mode 100644 libraries/interstate75/interstate75.cmake create mode 100644 libraries/interstate75/interstate75.hpp create mode 100644 micropython/examples/interstate75/75W/cheerlights.py create mode 100644 micropython/examples/interstate75/75W/clock.py create mode 100644 micropython/examples/interstate75/balls_demo.py create mode 100644 micropython/examples/interstate75/buttons.py delete mode 100644 micropython/examples/interstate75/font_10x14.py delete mode 100644 micropython/examples/interstate75/font_8x12.py delete mode 100644 micropython/examples/interstate75/i75_128x64_scrolling_wavy_clock.py delete mode 100644 micropython/examples/interstate75/i75_32x32_generic.py delete mode 100644 micropython/examples/interstate75/i75_64x32_clock.py delete mode 100644 micropython/examples/interstate75/i75_64x64_fm6126a.py delete mode 100644 micropython/examples/interstate75/i75_64x64_generic.py delete mode 100644 micropython/examples/interstate75/i75_64x64_scrolling_text.py create mode 100644 micropython/examples/interstate75/rainbow.py create mode 100644 micropython/examples/interstate75/raw_set_pixel.py create mode 100644 micropython/modules_py/interstate75.md create mode 100644 micropython/modules_py/interstate75.py diff --git a/drivers/CMakeLists.txt b/drivers/CMakeLists.txt index 5c005228..d0d9e09c 100644 --- a/drivers/CMakeLists.txt +++ b/drivers/CMakeLists.txt @@ -27,6 +27,7 @@ add_subdirectory(rgbled) add_subdirectory(icp10125) add_subdirectory(scd4x) add_subdirectory(hub75) +add_subdirectory(hub75_legacy) add_subdirectory(uc8151) add_subdirectory(uc8159) add_subdirectory(uc8151_legacy) diff --git a/drivers/hub75/hub75.cmake b/drivers/hub75/hub75.cmake index e8a10567..04cf801a 100644 --- a/drivers/hub75/hub75.cmake +++ b/drivers/hub75/hub75.cmake @@ -3,9 +3,10 @@ add_library(hub75 INTERFACE) target_sources(hub75 INTERFACE ${CMAKE_CURRENT_LIST_DIR}/hub75.cpp) +pico_generate_pio_header(hub75 ${CMAKE_CURRENT_LIST_DIR}/hub75.pio) + target_include_directories(hub75 INTERFACE ${CMAKE_CURRENT_LIST_DIR}) # Pull in pico libraries that we need -target_link_libraries(hub75 INTERFACE pico_stdlib hardware_pio hardware_dma) +target_link_libraries(hub75 INTERFACE pico_stdlib hardware_pio hardware_dma pico_graphics) -pico_generate_pio_header(hub75 ${CMAKE_CURRENT_LIST_DIR}/hub75.pio) \ No newline at end of file diff --git a/drivers/hub75/hub75.cpp b/drivers/hub75/hub75.cpp index d6f0a439..3114210a 100644 --- a/drivers/hub75/hub75.cpp +++ b/drivers/hub75/hub75.cpp @@ -4,37 +4,9 @@ #include "hub75.hpp" +namespace pimoroni { -// Basic function to convert Hue, Saturation and Value to an RGB colour -Pixel hsv_to_rgb(float h, float s, float v) { - if(h < 0.0f) { - h = 1.0f + fmod(h, 1.0f); - } - int i = int(h * 6); - float f = h * 6 - i; - - v = v * 255.0f; - - float sv = s * v; - float fsv = f * sv; - - auto p = uint8_t(-sv + v); - auto q = uint8_t(-fsv + v); - auto t = uint8_t(fsv - sv + v); - - uint8_t bv = uint8_t(v); - - switch (i % 6) { - default: - case 0: return Pixel(bv, t, p); - case 1: return Pixel(q, bv, p); - case 2: return Pixel(p, bv, t); - case 3: return Pixel(p, q, bv); - case 4: return Pixel(t, p, bv); - case 5: return Pixel(bv, p, q); - } -} 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) @@ -59,12 +31,10 @@ Hub75::Hub75(uint width, uint height, Pixel *buffer, PanelType panel_type, bool gpio_init(pin_oe); gpio_set_function(pin_oe, GPIO_FUNC_SIO); gpio_set_dir(pin_oe, true); gpio_put(pin_clk, !oe_polarity); if (buffer == nullptr) { - front_buffer = new Pixel[width * height]; back_buffer = new Pixel[width * height]; managed_buffer = true; } else { - front_buffer = buffer; - back_buffer = buffer + width * height; + back_buffer = buffer; managed_buffer = false; } @@ -86,17 +56,13 @@ void Hub75::set_color(uint x, uint y, Pixel c) { } else { offset = (y * width + x) * 2; } - front_buffer[offset] = c; + back_buffer[offset] = c; } -void Hub75::set_rgb(uint x, uint y, uint8_t r, uint8_t g, uint8_t b) { +void Hub75::set_pixel(uint x, uint y, uint8_t r, uint8_t g, uint8_t b) { 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)); -} - void Hub75::FM6126A_write_register(uint16_t value, uint8_t position) { gpio_put(pin_clk, !clk_polarity); gpio_put(pin_stb, !stb_polarity); @@ -163,24 +129,16 @@ void Hub75::start(irq_handler_t handler) { channel_config_set_dreq(&config, pio_get_dreq(pio, sm_data, true)); dma_channel_configure(dma_channel, &config, &pio->txf[sm_data], NULL, 0, false); - dma_channel_claim(dma_flip_channel); - dma_channel_config flip_config = dma_channel_get_default_config(dma_flip_channel); - channel_config_set_transfer_data_size(&flip_config, DMA_SIZE_32); - channel_config_set_read_increment(&flip_config, true); - channel_config_set_write_increment(&flip_config, true); - 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); - dma_channel_set_irq1_enabled(dma_flip_channel, true); dma_channel_set_irq0_enabled(dma_channel, true); irq_set_enabled(pio_get_dreq(pio, sm_data, true), true); irq_set_enabled(DMA_IRQ_0, true); - irq_set_enabled(DMA_IRQ_1, true); + row = 0; bit = 0; @@ -192,7 +150,6 @@ void Hub75::start(irq_handler_t handler) { } void Hub75::stop(irq_handler_t handler) { - do_flip = false; irq_set_enabled(DMA_IRQ_0, false); irq_set_enabled(DMA_IRQ_1, false); @@ -207,15 +164,6 @@ void Hub75::stop(irq_handler_t handler) { dma_channel_unclaim(dma_channel); } - if(dma_channel_is_claimed(dma_flip_channel)){ - dma_channel_set_irq1_enabled(dma_flip_channel, false); - irq_remove_handler(DMA_IRQ_1, handler); - //dma_channel_wait_for_finish_blocking(dma_flip_channel); - dma_channel_abort(dma_flip_channel); - dma_channel_acknowledge_irq1(dma_flip_channel); - dma_channel_unclaim(dma_flip_channel); - } - if(pio_sm_is_claimed(pio, sm_data)) { pio_sm_set_enabled(pio, sm_data, false); pio_sm_drain_tx_fifo(pio, sm_data); @@ -240,7 +188,6 @@ void Hub75::stop(irq_handler_t handler) { Hub75::~Hub75() { if (managed_buffer) { - delete[] front_buffer; delete[] back_buffer; } } @@ -248,31 +195,13 @@ Hub75::~Hub75() { void Hub75::clear() { for(auto x = 0u; x < width; x++) { for(auto y = 0u; y < height; y++) { - set_rgb(x, y, 0, 0, 0); + set_pixel(x, y, 0, 0, 0); } } } -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) { - best_effort_wfe_or_timeout(make_timeout_time_us(10)); - }; -} void Hub75::dma_complete() { - if(dma_channel_get_irq1_status(dma_flip_channel)) { - dma_channel_acknowledge_irq1(dma_flip_channel); - do_flip = false; - } if(dma_channel_get_irq0_status(dma_channel)) { dma_channel_acknowledge_irq0(dma_channel); @@ -290,15 +219,6 @@ void Hub75::dma_complete() { // Latch row data, pulse output enable for new row. pio_sm_put_blocking(pio, sm_row, row | (brightness << 5 << bit)); - if (do_flip && bit == 0 && row == 0) { - // Literally flip the front and back buffers by swapping their addresses - Pixel *tmp = back_buffer; - back_buffer = front_buffer; - front_buffer = tmp; - // Then, read the contents of the back buffer into the front buffer - dma_channel_set_trans_count(dma_flip_channel, width * height, true); - } - row++; if(row == height / 2) { @@ -313,4 +233,34 @@ void Hub75::dma_complete() { dma_channel_set_trans_count(dma_channel, width * 2, false); dma_channel_set_read_addr(dma_channel, &back_buffer[row * width * 2], true); } +} + +void Hub75::update(PicoGraphics *graphics) { + if(graphics->pen_type == PicoGraphics::PEN_RGB888) { + uint32_t *p = (uint32_t *)graphics->frame_buffer; + for(uint y = 0; y < height; y++) { + for(uint x = 0; x < width; x++) { + uint32_t col = *p; + uint8_t r = (col & 0xff0000) >> 16; + uint8_t g = (col & 0x00ff00) >> 8; + uint8_t b = (col & 0x0000ff) >> 0; + set_pixel(x, y, r, g, b); + p++; + } + } + } + else if(graphics->pen_type == PicoGraphics::PEN_RGB565) { + uint16_t *p = (uint16_t *)graphics->frame_buffer; + for(uint y = 0; y < height; y++) { + for(uint x = 0; x < width; x++) { + uint16_t col = __builtin_bswap16(*p); + uint8_t r = (col & 0b1111100000000000) >> 8; + uint8_t g = (col & 0b0000011111100000) >> 3; + uint8_t b = (col & 0b0000000000011111) << 3; + set_pixel(x, y, r, g, b); + p++; + } + } + } +} } \ No newline at end of file diff --git a/drivers/hub75/hub75.hpp b/drivers/hub75/hub75.hpp index f469ee28..15832ec3 100644 --- a/drivers/hub75/hub75.hpp +++ b/drivers/hub75/hub75.hpp @@ -4,8 +4,10 @@ #include "hardware/pio.h" #include "hardware/dma.h" #include "hardware/irq.h" +#include "libraries/pico_graphics/pico_graphics.hpp" #include "hub75.pio.h" +namespace pimoroni { const uint DATA_BASE_PIN = 0; const uint DATA_N_PINS = 6; const uint ROWSEL_BASE_PIN = 6; @@ -52,7 +54,6 @@ class Hub75 { public: uint width; uint height; - Pixel *front_buffer; Pixel *back_buffer; bool managed_buffer = false; PanelType panel_type; @@ -61,8 +62,6 @@ class Hub75 { // DMA & PIO uint dma_channel = 0; - uint dma_flip_channel = 1; - volatile bool do_flip = false; uint bit = 0; uint row = 0; @@ -110,7 +109,8 @@ class Hub75 { unsigned int pin_led_g = 17; unsigned int pin_led_b = 18; - Hub75(uint width, uint height, Pixel *buffer) : Hub75(width, height, buffer, PANEL_GENERIC, false) {}; + Hub75(uint width, uint height) : Hub75(width, height, nullptr) {}; + Hub75(uint width, uint height, Pixel *buffer) : Hub75(width, height, buffer, PANEL_GENERIC) {}; Hub75(uint width, uint height, Pixel *buffer, PanelType panel_type) : Hub75(width, height, buffer, panel_type, false) {}; Hub75(uint width, uint height, Pixel *buffer, PanelType panel_type, bool inverted_stb); ~Hub75(); @@ -118,12 +118,13 @@ class Hub75 { void FM6126A_write_register(uint16_t value, uint8_t position); void FM6126A_setup(); 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 set_pixel(uint x, uint y, uint8_t r, uint8_t g, uint8_t b); void display_update(); void clear(); void start(irq_handler_t handler); void stop(irq_handler_t handler); - void flip(bool copybuffer=true); void dma_complete(); -}; \ No newline at end of file + void update(PicoGraphics *graphics); + }; +} \ No newline at end of file diff --git a/drivers/hub75_legacy/CMakeLists.txt b/drivers/hub75_legacy/CMakeLists.txt new file mode 100644 index 00000000..95895379 --- /dev/null +++ b/drivers/hub75_legacy/CMakeLists.txt @@ -0,0 +1 @@ +include(${CMAKE_CURRENT_LIST_DIR}/hub75.cmake) \ No newline at end of file diff --git a/drivers/hub75_legacy/hub75.cmake b/drivers/hub75_legacy/hub75.cmake new file mode 100644 index 00000000..19480ab7 --- /dev/null +++ b/drivers/hub75_legacy/hub75.cmake @@ -0,0 +1,11 @@ +add_library(hub75_legacy INTERFACE) + +target_sources(hub75_legacy INTERFACE + ${CMAKE_CURRENT_LIST_DIR}/hub75.cpp) + +target_include_directories(hub75_legacy INTERFACE ${CMAKE_CURRENT_LIST_DIR}) + +# Pull in pico libraries that we need +target_link_libraries(hub75_legacy INTERFACE pico_stdlib hardware_pio hardware_dma) + +pico_generate_pio_header(hub75_legacy ${CMAKE_CURRENT_LIST_DIR}/hub75.pio) \ No newline at end of file diff --git a/drivers/hub75_legacy/hub75.cpp b/drivers/hub75_legacy/hub75.cpp new file mode 100644 index 00000000..d6f0a439 --- /dev/null +++ b/drivers/hub75_legacy/hub75.cpp @@ -0,0 +1,316 @@ +#include +#include +#include + +#include "hub75.hpp" + + +// Basic function to convert Hue, Saturation and Value to an RGB colour +Pixel hsv_to_rgb(float h, float s, float v) { + if(h < 0.0f) { + h = 1.0f + fmod(h, 1.0f); + } + + int i = int(h * 6); + float f = h * 6 - i; + + v = v * 255.0f; + + float sv = s * v; + float fsv = f * sv; + + auto p = uint8_t(-sv + v); + auto q = uint8_t(-fsv + v); + auto t = uint8_t(fsv - sv + v); + + uint8_t bv = uint8_t(v); + + switch (i % 6) { + default: + case 0: return Pixel(bv, t, p); + case 1: return Pixel(q, bv, p); + case 2: return Pixel(p, bv, t); + case 3: return Pixel(p, q, bv); + case 4: return Pixel(t, p, bv); + case 5: return Pixel(bv, p, q); + } +} + +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) + { + // Set up allllll the GPIO + gpio_init(pin_r0); gpio_set_function(pin_r0, GPIO_FUNC_SIO); gpio_set_dir(pin_r0, true); gpio_put(pin_r0, 0); + gpio_init(pin_g0); gpio_set_function(pin_g0, GPIO_FUNC_SIO); gpio_set_dir(pin_g0, true); gpio_put(pin_g0, 0); + gpio_init(pin_b0); gpio_set_function(pin_b0, GPIO_FUNC_SIO); gpio_set_dir(pin_b0, true); gpio_put(pin_b0, 0); + + gpio_init(pin_r1); gpio_set_function(pin_r1, GPIO_FUNC_SIO); gpio_set_dir(pin_r1, true); gpio_put(pin_r1, 0); + gpio_init(pin_g1); gpio_set_function(pin_g1, GPIO_FUNC_SIO); gpio_set_dir(pin_g1, true); gpio_put(pin_g1, 0); + gpio_init(pin_b1); gpio_set_function(pin_b1, GPIO_FUNC_SIO); gpio_set_dir(pin_b1, true); gpio_put(pin_b1, 0); + + gpio_init(pin_row_a); gpio_set_function(pin_row_a, GPIO_FUNC_SIO); gpio_set_dir(pin_row_a, true); gpio_put(pin_row_a, 0); + gpio_init(pin_row_b); gpio_set_function(pin_row_b, GPIO_FUNC_SIO); gpio_set_dir(pin_row_b, true); gpio_put(pin_row_b, 0); + gpio_init(pin_row_c); gpio_set_function(pin_row_c, GPIO_FUNC_SIO); gpio_set_dir(pin_row_c, true); gpio_put(pin_row_c, 0); + gpio_init(pin_row_d); gpio_set_function(pin_row_d, GPIO_FUNC_SIO); gpio_set_dir(pin_row_d, true); gpio_put(pin_row_d, 0); + gpio_init(pin_row_e); gpio_set_function(pin_row_e, GPIO_FUNC_SIO); gpio_set_dir(pin_row_e, true); gpio_put(pin_row_e, 0); + + gpio_init(pin_clk); gpio_set_function(pin_clk, GPIO_FUNC_SIO); gpio_set_dir(pin_clk, true); gpio_put(pin_clk, !clk_polarity); + gpio_init(pin_stb); gpio_set_function(pin_stb, GPIO_FUNC_SIO); gpio_set_dir(pin_stb, true); gpio_put(pin_clk, !stb_polarity); + gpio_init(pin_oe); gpio_set_function(pin_oe, GPIO_FUNC_SIO); gpio_set_dir(pin_oe, true); gpio_put(pin_clk, !oe_polarity); + + if (buffer == nullptr) { + front_buffer = new Pixel[width * height]; + back_buffer = new Pixel[width * height]; + managed_buffer = true; + } else { + front_buffer = buffer; + back_buffer = buffer + width * height; + managed_buffer = false; + } + + if (brightness == 0) { + if (width >= 64) brightness = 6; + if (width >= 96) brightness = 3; + if (width >= 128) brightness = 2; + if (width >= 160) brightness = 1; + } +} + +void Hub75::set_color(uint x, uint y, Pixel c) { + int offset = 0; + if(x >= width || y >= height) return; + if(y >= height / 2) { + y -= height / 2; + offset = (y * width + x) * 2; + offset += 1; + } else { + offset = (y * width + x) * 2; + } + 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)); +} + +void Hub75::set_hsv(uint x, uint y, float h, float s, float v) { + set_color(x, y, hsv_to_rgb(h, s, v)); +} + +void Hub75::FM6126A_write_register(uint16_t value, uint8_t position) { + gpio_put(pin_clk, !clk_polarity); + gpio_put(pin_stb, !stb_polarity); + + uint8_t threshold = width - position; + for(auto i = 0u; i < width; i++) { + auto j = i % 16; + bool b = value & (1 << j); + + gpio_put(pin_r0, b); + gpio_put(pin_g0, b); + gpio_put(pin_b0, b); + gpio_put(pin_r1, b); + gpio_put(pin_g1, b); + gpio_put(pin_b1, b); + + // Assert strobe/latch if i > threshold + // This somehow indicates to the FM6126A which register we want to write :| + gpio_put(pin_stb, i > threshold); + gpio_put(pin_clk, clk_polarity); + sleep_us(10); + gpio_put(pin_clk, !clk_polarity); + } +} + +void Hub75::FM6126A_setup() { + // Ridiculous register write nonsense for the FM6126A-based 64x64 matrix + FM6126A_write_register(0b1111111111111110, 12); + FM6126A_write_register(0b0000001000000000, 13); +} + +void Hub75::start(irq_handler_t handler) { + if(handler) { + dma_channel = 0; + + // Try as I might, I can't seem to coax MicroPython into leaving PIO in a known state upon soft reset + // check for claimed PIO and prepare a clean slate. + stop(handler); + + if (panel_type == PANEL_FM6126A) { + FM6126A_setup(); + } + + // Claim the PIO so we can clean it upon soft restart + pio_sm_claim(pio, sm_data); + pio_sm_claim(pio, sm_row); + + data_prog_offs = pio_add_program(pio, &hub75_data_rgb888_program); + if (inverted_stb) { + row_prog_offs = pio_add_program(pio, &hub75_row_inverted_program); + } else { + row_prog_offs = pio_add_program(pio, &hub75_row_program); + } + hub75_data_rgb888_program_init(pio, sm_data, data_prog_offs, DATA_BASE_PIN, pin_clk); + hub75_row_program_init(pio, sm_row, row_prog_offs, ROWSEL_BASE_PIN, ROWSEL_N_PINS, pin_stb); + + // Prevent flicker in Python caused by the smaller dataset just blasting through the PIO too quickly + pio_sm_set_clkdiv(pio, sm_data, width <= 32 ? 2.0f : 1.0f); + + dma_channel_claim(dma_channel); + dma_channel_config config = dma_channel_get_default_config(dma_channel); + channel_config_set_transfer_data_size(&config, DMA_SIZE_32); + channel_config_set_bswap(&config, false); + channel_config_set_dreq(&config, pio_get_dreq(pio, sm_data, true)); + dma_channel_configure(dma_channel, &config, &pio->txf[sm_data], NULL, 0, false); + + dma_channel_claim(dma_flip_channel); + dma_channel_config flip_config = dma_channel_get_default_config(dma_flip_channel); + channel_config_set_transfer_data_size(&flip_config, DMA_SIZE_32); + channel_config_set_read_increment(&flip_config, true); + channel_config_set_write_increment(&flip_config, true); + 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); + + dma_channel_set_irq1_enabled(dma_flip_channel, true); + dma_channel_set_irq0_enabled(dma_channel, true); + + irq_set_enabled(pio_get_dreq(pio, sm_data, true), true); + irq_set_enabled(DMA_IRQ_0, true); + irq_set_enabled(DMA_IRQ_1, true); + + row = 0; + bit = 0; + + hub75_data_rgb888_set_shift(pio, sm_data, data_prog_offs, bit); + dma_channel_set_trans_count(dma_channel, width * 2, false); + dma_channel_set_read_addr(dma_channel, &back_buffer, true); + } +} + +void Hub75::stop(irq_handler_t handler) { + do_flip = false; + + irq_set_enabled(DMA_IRQ_0, false); + irq_set_enabled(DMA_IRQ_1, false); + irq_set_enabled(pio_get_dreq(pio, sm_data, true), false); + + if(dma_channel_is_claimed(dma_channel)) { + dma_channel_set_irq0_enabled(dma_channel, false); + irq_remove_handler(DMA_IRQ_0, handler); + //dma_channel_wait_for_finish_blocking(dma_channel); + dma_channel_abort(dma_channel); + dma_channel_acknowledge_irq0(dma_channel); + dma_channel_unclaim(dma_channel); + } + + if(dma_channel_is_claimed(dma_flip_channel)){ + dma_channel_set_irq1_enabled(dma_flip_channel, false); + irq_remove_handler(DMA_IRQ_1, handler); + //dma_channel_wait_for_finish_blocking(dma_flip_channel); + dma_channel_abort(dma_flip_channel); + dma_channel_acknowledge_irq1(dma_flip_channel); + dma_channel_unclaim(dma_flip_channel); + } + + if(pio_sm_is_claimed(pio, sm_data)) { + pio_sm_set_enabled(pio, sm_data, false); + pio_sm_drain_tx_fifo(pio, sm_data); + pio_sm_unclaim(pio, sm_data); + } + + if(pio_sm_is_claimed(pio, sm_row)) { + pio_sm_set_enabled(pio, sm_row, false); + pio_sm_drain_tx_fifo(pio, sm_row); + pio_sm_unclaim(pio, sm_row); + } + + pio_clear_instruction_memory(pio); + + // Make sure the GPIO is in a known good state + // since we don't know what the PIO might have done with it + gpio_put_masked(0b111111 << pin_r0, 0); + gpio_put_masked(0b11111 << pin_row_a, 0); + gpio_put(pin_clk, !clk_polarity); + gpio_put(pin_clk, !oe_polarity); +} + +Hub75::~Hub75() { + if (managed_buffer) { + delete[] front_buffer; + delete[] back_buffer; + } +} + +void Hub75::clear() { + for(auto x = 0u; x < width; x++) { + for(auto y = 0u; y < height; y++) { + set_rgb(x, y, 0, 0, 0); + } + } +} + +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) { + best_effort_wfe_or_timeout(make_timeout_time_us(10)); + }; +} + +void Hub75::dma_complete() { + if(dma_channel_get_irq1_status(dma_flip_channel)) { + dma_channel_acknowledge_irq1(dma_flip_channel); + do_flip = false; + } + + if(dma_channel_get_irq0_status(dma_channel)) { + dma_channel_acknowledge_irq0(dma_channel); + + // Push out a dummy pixel for each row + pio_sm_put_blocking(pio, sm_data, 0); + pio_sm_put_blocking(pio, sm_data, 0); + + // SM is finished when it stalls on empty TX FIFO + hub75_wait_tx_stall(pio, sm_data); + + // Check that previous OEn pulse is finished, else things WILL get out of sequence + hub75_wait_tx_stall(pio, sm_row); + + // Latch row data, pulse output enable for new row. + pio_sm_put_blocking(pio, sm_row, row | (brightness << 5 << bit)); + + if (do_flip && bit == 0 && row == 0) { + // Literally flip the front and back buffers by swapping their addresses + Pixel *tmp = back_buffer; + back_buffer = front_buffer; + front_buffer = tmp; + // Then, read the contents of the back buffer into the front buffer + dma_channel_set_trans_count(dma_flip_channel, width * height, true); + } + + row++; + + if(row == height / 2) { + row = 0; + bit++; + if (bit == BIT_DEPTH) { + bit = 0; + } + hub75_data_rgb888_set_shift(pio, sm_data, data_prog_offs, bit); + } + + dma_channel_set_trans_count(dma_channel, width * 2, false); + dma_channel_set_read_addr(dma_channel, &back_buffer[row * width * 2], true); + } +} \ No newline at end of file diff --git a/drivers/hub75_legacy/hub75.hpp b/drivers/hub75_legacy/hub75.hpp new file mode 100644 index 00000000..f469ee28 --- /dev/null +++ b/drivers/hub75_legacy/hub75.hpp @@ -0,0 +1,129 @@ +#include +#include "pico/stdlib.h" + +#include "hardware/pio.h" +#include "hardware/dma.h" +#include "hardware/irq.h" +#include "hub75.pio.h" + +const uint DATA_BASE_PIN = 0; +const uint DATA_N_PINS = 6; +const uint ROWSEL_BASE_PIN = 6; +const uint ROWSEL_N_PINS = 5; +const uint BIT_DEPTH = 10; + +// This gamma table is used to correct our 8-bit (0-255) colours up to 11-bit, +// allowing us to gamma correct without losing dynamic range. +constexpr uint16_t GAMMA_10BIT[256] = { + 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, + 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, + 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 24, 25, + 26, 27, 29, 30, 31, 33, 34, 35, 37, 38, 40, 41, 43, 44, 46, 47, + 49, 51, 53, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, + 80, 82, 85, 87, 89, 92, 94, 96, 99, 101, 104, 106, 109, 112, 114, 117, + 120, 122, 125, 128, 131, 134, 137, 140, 143, 146, 149, 152, 155, 158, 161, 164, + 168, 171, 174, 178, 181, 185, 188, 192, 195, 199, 202, 206, 210, 214, 217, 221, + 225, 229, 233, 237, 241, 245, 249, 253, 257, 261, 265, 270, 274, 278, 283, 287, + 291, 296, 300, 305, 309, 314, 319, 323, 328, 333, 338, 343, 347, 352, 357, 362, + 367, 372, 378, 383, 388, 393, 398, 404, 409, 414, 420, 425, 431, 436, 442, 447, + 453, 459, 464, 470, 476, 482, 488, 494, 499, 505, 511, 518, 524, 530, 536, 542, + 548, 555, 561, 568, 574, 580, 587, 593, 600, 607, 613, 620, 627, 633, 640, 647, + 654, 661, 668, 675, 682, 689, 696, 703, 711, 718, 725, 733, 740, 747, 755, 762, + 770, 777, 785, 793, 800, 808, 816, 824, 832, 839, 847, 855, 863, 872, 880, 888, + 896, 904, 912, 921, 929, 938, 946, 954, 963, 972, 980, 989, 997, 1006, 1015, 1023 +}; + + +struct Pixel { + uint32_t color; + constexpr Pixel() : color(0) {}; + constexpr Pixel(uint32_t color) : color(color) {}; + constexpr Pixel(uint8_t r, uint8_t g, uint8_t b) : color((GAMMA_10BIT[b] << 20) | (GAMMA_10BIT[g] << 10) | GAMMA_10BIT[r]) {}; +}; + +enum PanelType { + PANEL_GENERIC = 0, + PANEL_FM6126A, +}; + +Pixel hsv_to_rgb(float h, float s, float v); + +class Hub75 { + public: + uint width; + uint height; + Pixel *front_buffer; + Pixel *back_buffer; + bool managed_buffer = false; + PanelType panel_type; + bool inverted_stb = false; + Pixel background = 0; + + // DMA & PIO + uint dma_channel = 0; + uint dma_flip_channel = 1; + volatile bool do_flip = false; + uint bit = 0; + uint row = 0; + + PIO pio = pio0; + uint sm_data = 0; + uint sm_row = 1; + + uint data_prog_offs = 0; + uint row_prog_offs = 0; + + uint brightness = 6; + + + // Top half of display - 16 rows on a 32x32 panel + unsigned int pin_r0 = 0; + unsigned int pin_g0 = 1; + unsigned int pin_b0 = 2; + + // Bottom half of display - 16 rows on a 64x64 panel + unsigned int pin_r1 = 3; + unsigned int pin_g1 = 4; + unsigned int pin_b1 = 5; + + // Address pins, 5 lines = 2^5 = 32 values (max 64x64 display) + unsigned int pin_row_a = 6; + unsigned int pin_row_b = 7; + unsigned int pin_row_c = 8; + unsigned int pin_row_d = 9; + unsigned int pin_row_e = 10; + + // Sundry things + unsigned int pin_clk = 11; // Clock + unsigned int pin_stb = 12; // Strobe/Latch + unsigned int pin_oe = 13; // Output Enable + + const bool clk_polarity = 1; + const bool stb_polarity = 1; + const bool oe_polarity = 0; + + // User buttons and status LED + unsigned int pin_sw_a = 14; + unsigned int pin_sw_user = 23; + + unsigned int pin_led_r = 16; + unsigned int pin_led_g = 17; + unsigned int pin_led_b = 18; + + Hub75(uint width, uint height, Pixel *buffer) : Hub75(width, height, buffer, PANEL_GENERIC, false) {}; + Hub75(uint width, uint height, Pixel *buffer, PanelType panel_type) : Hub75(width, height, buffer, panel_type, false) {}; + Hub75(uint width, uint height, Pixel *buffer, PanelType panel_type, bool inverted_stb); + ~Hub75(); + + void FM6126A_write_register(uint16_t value, uint8_t position); + void FM6126A_setup(); + 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(bool copybuffer=true); + void dma_complete(); +}; \ No newline at end of file diff --git a/drivers/hub75_legacy/hub75.pio b/drivers/hub75_legacy/hub75.pio new file mode 100644 index 00000000..facfdafa --- /dev/null +++ b/drivers/hub75_legacy/hub75.pio @@ -0,0 +1,158 @@ +; +; Copyright (c) 2020 Raspberry Pi (Trading) Ltd. +; +; SPDX-License-Identifier: BSD-3-Clause +; + +.program hub75_row + +; side-set pin 0 is LATCH +; side-set pin 1 is OEn +; OUT pins are row select A-E +; +; Each FIFO record consists of: +; - 5-bit row select (LSBs) +; - Pulse width - 1 (27 MSBs) +; +; Repeatedly select a row, pulse LATCH, and generate a pulse of a certain +; width on OEn. + +.side_set 2 + +.wrap_target + out pins, 5 [1] side 0x2 ; Deassert OEn, output row select + out x, 27 [7] side 0x3 ; Pulse LATCH, get OEn pulse width +pulse_loop: + jmp x-- pulse_loop side 0x0 ; Assert OEn for x+1 cycles +.wrap + +.program hub75_row_inverted + +; side-set pin 0 is LATCH +; side-set pin 1 is OEn +; OUT pins are row select A-E +; +; Each FIFO record consists of: +; - 5-bit row select (LSBs) +; - Pulse width - 1 (27 MSBs) +; +; Repeatedly select a row, pulse LATCH, and generate a pulse of a certain +; width on OEn. + +.side_set 2 + +.wrap_target + out pins, 5 [1] side 0x3 ; Deassert OEn, output row select + out x, 27 [7] side 0x2 ; Pulse LATCH, get OEn pulse width +pulse_loop: + jmp x-- pulse_loop side 0x1 ; Assert OEn for x+1 cycles +.wrap + +% c-sdk { +static inline void hub75_row_program_init(PIO pio, uint sm, uint offset, uint row_base_pin, uint n_row_pins, uint latch_base_pin) { + pio_sm_set_consecutive_pindirs(pio, sm, row_base_pin, n_row_pins, true); + pio_sm_set_consecutive_pindirs(pio, sm, latch_base_pin, 2, true); + for (uint i = row_base_pin; i < row_base_pin + n_row_pins; ++i) + pio_gpio_init(pio, i); + pio_gpio_init(pio, latch_base_pin); + pio_gpio_init(pio, latch_base_pin + 1); + + pio_sm_config c = hub75_row_program_get_default_config(offset); + sm_config_set_out_pins(&c, row_base_pin, n_row_pins); + sm_config_set_sideset_pins(&c, latch_base_pin); + sm_config_set_out_shift(&c, true, true, 32); + pio_sm_init(pio, sm, offset, &c); + pio_sm_set_enabled(pio, sm, true); +} + +static inline void hub75_wait_tx_stall(PIO pio, uint sm) { + uint32_t txstall_mask = 1u << (PIO_FDEBUG_TXSTALL_LSB + sm); + pio->fdebug = txstall_mask; + while (!(pio->fdebug & txstall_mask)) + tight_loop_contents(); +} +%} + +.program hub75_data_rgb888 +.side_set 1 + +; Each FIFO record consists of a RGB888 pixel. (This is ok for e.g. an RGB565 +; source which has been gamma-corrected) +; +; Even pixels are sent on R0, G0, B0 and odd pixels on R1, G1, B1 (typically +; these are for different parts of the screen, NOT for adjacent pixels, so the +; frame buffer must be interleaved before passing to PIO.) +; +; Each pass through, we take bit n, n + 8 and n + 16 from each pixel, for n in +; {0...7}. Therefore the pixels need to be transmitted 8 times (ouch) to build +; up the full 8 bit value for each channel, and perform bit-planed PWM by +; varying pulse widths on the other state machine, in ascending powers of 2. +; This avoids a lot of bit shuffling on the processors, at the cost of DMA +; bandwidth (which we have loads of). + +; Might want to close your eyes before you read this +public entry_point: +.wrap_target + +public shift0: ; R0 G0 B0 (Top half of 64x64 displays) + pull side 0 ; gets patched to `out null, n` if n nonzero (otherwise the PULL is required for fencing) + in osr, 1 side 0 ; Red0 N + out null, 10 side 0 ; Red0 discard + + in osr, 1 side 0 ; Green0 N + out null, 10 side 0 ; Green0 discard + + in osr, 1 side 0 ; Blue0 N + out null, 32 side 0 ; Remainder discard + +public shift1: ; R1 G1 B1 (Bottom half of 64x64 displays) + pull side 0 ; gets patched to `out null, n` if n nonzero (otherwise the PULL is required for fencing) + in osr, 1 side 1 ; Red1 N + out null, 10 side 1 ; Red1 discard + + in osr, 1 side 1 ; Green1 N + out null, 10 side 1 ; Green1 discard + + in osr, 1 side 1 ; Blue1 N + out null, 32 side 1 ; Remainder discard + + in null, 26 side 1 ; Note we are just doing this little manoeuvre here to get GPIOs in the order + mov pins, ::isr side 1 ; R0, G0, B0, R1, G1, B1. Can go 1 cycle faster if reversed + +.wrap +; Note that because the clock edge for pixel n is in the middle of pixel n + +; 1, a dummy pixel at the end is required to clock the last piece of genuine +; data. (Also 1 pixel of garbage is clocked out at the start, but this is +; harmless) + +% c-sdk { +static inline void hub75_data_rgb888_program_init(PIO pio, uint sm, uint offset, uint rgb_base_pin, uint clock_pin) { + pio_sm_set_consecutive_pindirs(pio, sm, rgb_base_pin, 6, true); + pio_sm_set_consecutive_pindirs(pio, sm, clock_pin, 1, true); + for (uint i = rgb_base_pin; i < rgb_base_pin + 6; ++i) + pio_gpio_init(pio, i); + pio_gpio_init(pio, clock_pin); + + pio_sm_config c = hub75_data_rgb888_program_get_default_config(offset); + sm_config_set_out_pins(&c, rgb_base_pin, 6); + sm_config_set_sideset_pins(&c, clock_pin); + sm_config_set_out_shift(&c, true, true, 32); + // ISR shift to left. R0 ends up at bit 5. We push it up to MSB and then flip the register. + sm_config_set_in_shift(&c, false, false, 32); + sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX); + pio_sm_init(pio, sm, offset, &c); + pio_sm_exec(pio, sm, offset + hub75_data_rgb888_offset_entry_point); + pio_sm_set_enabled(pio, sm, true); +} + +// Patch a data program at `offset` to preshift pixels by `shamt` +static inline void hub75_data_rgb888_set_shift(PIO pio, uint sm, uint offset, uint shamt) { + uint16_t instr; + if (shamt == 0) + instr = pio_encode_pull(false, true); // blocking PULL + else + instr = pio_encode_out(pio_null, shamt); + pio->instr_mem[offset + hub75_data_rgb888_offset_shift0] = instr; + pio->instr_mem[offset + hub75_data_rgb888_offset_shift1] = instr; +} +%} diff --git a/examples/interstate75/CMakeLists.txt b/examples/interstate75/CMakeLists.txt index 7d8d4278..ef1e2f19 100644 --- a/examples/interstate75/CMakeLists.txt +++ b/examples/interstate75/CMakeLists.txt @@ -1,3 +1,5 @@ -include(interstate75_hello_world.cmake) -include(interstate75_pio_dma.cmake) -include(interstate75_scrolling_text.cmake) \ No newline at end of file +include(interstate75_rainbow.cmake) +include(interstate75_balls_demo.cmake) +include(interstate75_rainbow_text.cmake) +include(interstate75_fire_effect.cmake) +include(interstate75_example.cmake) \ No newline at end of file diff --git a/examples/interstate75/interstate75_balls_demo.cmake b/examples/interstate75/interstate75_balls_demo.cmake new file mode 100644 index 00000000..50faf2f5 --- /dev/null +++ b/examples/interstate75/interstate75_balls_demo.cmake @@ -0,0 +1,14 @@ +set(OUTPUT_NAME interstate75_balls_demo) +add_executable(${OUTPUT_NAME} interstate75_balls_demo.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_balls_demo.cpp b/examples/interstate75/interstate75_balls_demo.cpp new file mode 100644 index 00000000..4366539c --- /dev/null +++ b/examples/interstate75/interstate75_balls_demo.cpp @@ -0,0 +1,124 @@ +#include +#include "pico/stdlib.h" + +#include "libraries/pico_graphics/pico_graphics.hpp" +#include "drivers/hub75/hub75.hpp" + +using namespace pimoroni; + +const uint8_t QTY_BALLS = 10; + +//If the display looks streaky or corrupted then uncomment one of the other initalisers + +//Works with our 32x32 panels https://shop.pimoroni.com/products/rgb-led-matrix-panel?variant=35962488650 https://shop.pimoroni.com/products/rgb-led-matrix-panel?variant=19321740999 +//Hub75 hub75(32, 32, nullptr, PANEL_GENERIC, false); +//or using 2 of these panels +//Hub75 hub75(64, 32, nullptr, PANEL_GENERIC, false); + +//and 64x32 panel https://shop.pimoroni.com/products/rgb-led-matrix-panel?variant=42312764298 +//Hub75 hub75(64, 32, nullptr, PANEL_GENERIC, false); +//or using 2 of these panels +//Hub75 hub75(128, 32, nullptr, PANEL_GENERIC, false); + +//Works with our 64x64 panel https://shop.pimoroni.com/products/rgb-led-matrix-panel?variant=3029531983882 +Hub75 hub75(64, 64, nullptr, PANEL_FM6126A, false); +//or using 2 of these panels +//Hub75 hub75(128, 64, nullptr, PANEL_GENERIC, false); + + + +PicoGraphics_PenRGB888 graphics(hub75.width, hub75.height, nullptr); + +// Callback for the dma interrupt (required) +void __isr dma_complete() { + hub75.dma_complete(); +} + +// HSV Conversion expects float inputs in the range of 0.00-1.00 for each channel +// Outputs are rgb in the range 0-255 for each channel +void from_hsv(float h, float s, float v, uint8_t &r, uint8_t &g, uint8_t &b) { + float i = floor(h * 6.0f); + float f = h * 6.0f - i; + v *= 255.0f; + uint8_t p = v * (1.0f - s); + uint8_t q = v * (1.0f - f * s); + uint8_t t = v * (1.0f - (1.0f - f) * s); + + switch (int(i) % 6) { + case 0: r = v; g = t; b = p; break; + case 1: r = q; g = v; b = p; break; + case 2: r = p; g = v; b = t; break; + case 3: r = p; g = q; b = v; break; + case 4: r = t; g = p; b = v; break; + case 5: r = v; g = p; b = q; break; + } +} + +int main() { + hub75.start(dma_complete); + + struct pt { + float x; + float y; + uint8_t r; + float dx; + float dy; + Pen pen; + }; + + std::vector shapes; + for(uint8_t i = 0; i < QTY_BALLS; i++) { + pt shape; + shape.x = rand() % graphics.bounds.w; + shape.y = rand() % graphics.bounds.h; + shape.r = (rand() % 5) + 2; + shape.dx = float(rand() % 255) / 128.0f; + shape.dy = float(rand() % 255) / 128.0f; + shape.pen = graphics.create_pen(rand() % 255, rand() % 255, rand() % 255); + shapes.push_back(shape); + } + + Point text_location(0, 0); + + Pen BG = graphics.create_pen(0, 0, 0); + Pen WHITE = graphics.create_pen(200, 200, 200); + + while(true) { + graphics.set_pen(BG); + graphics.clear(); + + for(auto &shape : shapes) { + shape.x += shape.dx; + shape.y += shape.dy; + if((shape.x - shape.r) < 0) { + shape.dx *= -1; + shape.x = shape.r; + } + if((shape.x + shape.r) >= graphics.bounds.w) { + shape.dx *= -1; + shape.x = graphics.bounds.w - shape.r; + } + if((shape.y - shape.r) < 0) { + shape.dy *= -1; + shape.y = shape.r; + } + if((shape.y + shape.r) >= graphics.bounds.h) { + shape.dy *= -1; + shape.y = graphics.bounds.h - shape.r; + } + + graphics.set_pen(shape.pen); + graphics.circle(Point(shape.x, shape.y), shape.r); + + } + + graphics.set_pen(WHITE); + graphics.text("Hello World", text_location, false, 1.0f); + + // update screen + hub75.update(&graphics); + sleep_ms(1000 / 30); + } + + return 0; +} \ No newline at end of file diff --git a/examples/interstate75/interstate75_example.cmake b/examples/interstate75/interstate75_example.cmake new file mode 100644 index 00000000..216f62e0 --- /dev/null +++ b/examples/interstate75/interstate75_example.cmake @@ -0,0 +1,15 @@ +set(OUTPUT_NAME interstate75_example) +add_executable(${OUTPUT_NAME} interstate75_example.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 + interstate75 +) diff --git a/examples/interstate75/interstate75_example.cpp b/examples/interstate75/interstate75_example.cpp new file mode 100644 index 00000000..1ed37bd8 --- /dev/null +++ b/examples/interstate75/interstate75_example.cpp @@ -0,0 +1,59 @@ +#include "libraries/pico_graphics/pico_graphics.hpp" +#include "libraries/interstate75/interstate75.hpp" +#include "rgbled.hpp" +#include "button.hpp" + +using namespace pimoroni; + +// Display driver for a single 32x32 hub75 matrix +Hub75 hub75(32, 32, nullptr, PANEL_GENERIC, false); + +// Graphics library - in 24Bit mode with 16M colours +PicoGraphics_PenRGB888 graphics(hub75.width, hub75.height, nullptr); + +// And each button +Button button_a(Interstate75::A); +Button button_b(Interstate75::B); + +// RGB LED +RGBLED led(Interstate75::LED_R, Interstate75::LED_G, Interstate75::LED_B, ACTIVE_LOW); + +// Interrupt callback required function +void __isr dma_complete() { + hub75.dma_complete(); +} + +int main() { + hub75.start(dma_complete); + led.set_rgb(0,0,0); + while(true) { + // detect if the A button is pressed (could be A, B, C, D or E) + if(button_a.raw()) { + // make the led glow green + // parameters are red, green, blue all between 0 and 255 + // these are also gamma corrected + led.set_rgb(0, 255, 0); + } + + // set the colour of the pen + // parameters are red, green, blue all between 0 and 255 + graphics.set_pen(100, 40, 50); + + // fill the screen with the current pen colour + graphics.clear(); + + // draw a box to put some text in + graphics.set_pen(10, 20, 100); + Rect text_rect(1, 1, hub75.width-2, hub75.height-2); + graphics.rectangle(text_rect); + + // write some text inside the box with 10 pixels of margin + // automatically word wrapping + text_rect.deflate(1); + graphics.set_pen(110, 120, 130); + graphics.text("This is text", Point(text_rect.x, text_rect.y), text_rect.w, 1.0f); + + // now we've done our drawing let's update the screen + hub75.update(&graphics); + } +} \ No newline at end of file diff --git a/examples/interstate75/interstate75_fire_effect.cmake b/examples/interstate75/interstate75_fire_effect.cmake new file mode 100644 index 00000000..9047772e --- /dev/null +++ b/examples/interstate75/interstate75_fire_effect.cmake @@ -0,0 +1,15 @@ +set(OUTPUT_NAME interstate75_fire_effect) +add_executable(${OUTPUT_NAME} interstate75_fire_effect.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 + pico_graphics +) diff --git a/examples/interstate75/interstate75_fire_effect.cpp b/examples/interstate75/interstate75_fire_effect.cpp new file mode 100644 index 00000000..c721eff2 --- /dev/null +++ b/examples/interstate75/interstate75_fire_effect.cpp @@ -0,0 +1,130 @@ +#include "pico/stdlib.h" + +#include "libraries/pico_graphics/pico_graphics.hpp" +#include "drivers/hub75/hub75.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 32*2 pixels. + +//If the display looks streaky or corrupted then uncomment one of the other initalisers + +//Works with our 32x32 panels https://shop.pimoroni.com/products/rgb-led-matrix-panel?variant=35962488650 https://shop.pimoroni.com/products/rgb-led-matrix-panel?variant=19321740999 +Hub75 hub75(32, 32, nullptr, PANEL_GENERIC, false); +//or using 2 of these panels +//Hub75 hub75(64, 32, nullptr, PANEL_GENERIC, false); + +//and 64x32 panel https://shop.pimoroni.com/products/rgb-led-matrix-panel?variant=42312764298 +//Hub75 hub75(64, 32, nullptr, PANEL_GENERIC, false); +//or using 2 of these panels +//Hub75 hub75(128, 32, nullptr, PANEL_GENERIC, false); + +//Works with our 64x64 panel https://shop.pimoroni.com/products/rgb-led-matrix-panel?variant=3029531983882 +//Hub75 hub75(64, 64, nullptr, PANEL_GENERIC, false); +//or using 2 of these panels +//Hub75 hub75(128, 64, nullptr, PANEL_GENERIC, false); + + +PicoGraphics_PenRGB888 graphics(hub75.width, hub75.height, nullptr); + +// extra row of pixels for sourcing flames and averaging +int width = hub75.width; +int height = hub75.height + 2; + +// a buffer that's at least big enough to store 55 x 15 values (to allow for both orientations) +float heat[4500] = {0.0f}; + +void set(int x, int y, float v) { + heat[x + y * width] = v; +} + +float get(int x, int y) { + /*if(x < 0 || x >= width || y < 0 || y >= height) { + return 0.0f; + }*/ + x = x < 0 ? 0 : x; + x = x >= width ? width - 1 : x; + + return heat[x + y * width]; +} + +// Interrupt callback required function +void __isr dma_complete() { + hub75.dma_complete(); +} + + +int main() { + + //stdio_init_all(); + + //Start hub75 driver + hub75.start(dma_complete); + + bool landscape = true; + + while(true) { + + for(int y = 0; y < height; y++) { + for(int x = 0; x < width; x++) { + float value = get(x, y); + + graphics.set_pen(0, 0, 0); + if(value > 0.5f) { + graphics.set_pen(255, 255, 180); + } + else if(value > 0.4f) { + graphics.set_pen(220, 160, 0); + } + else if(value > 0.3f) { + graphics.set_pen(180, 30, 0); + } + else if(value > 0.22f) { + graphics.set_pen(20, 20, 20); + } + + if(landscape) { + graphics.pixel(Point(x, y)); + } + else{ + graphics.pixel(Point(y, x)); + } + + // update this pixel by averaging the below pixels + float average = (get(x, y) + get(x, y + 2) + get(x, y + 1) + get(x - 1, y + 1) + get(x + 1, y + 1)) / 5.0f; + + // damping factor to ensure flame tapers out towards the top of the displays + average *= landscape ? 0.99f : 0.99f; + + // update the heat map with our newly averaged value + set(x, y, average); + } + } + + hub75.update(&graphics); + + // clear the bottom row and then add a new fire seed to it + for(int x = 0; x < width; x++) { + set(x, height - 1, 0.0f); + } + + // add a new random heat source + int source_count = landscape ? 7 : 1; + for(int c = 0; c < source_count; c++) { + int px = (rand() % (width - 4)) + 2; + set(px , height - 2, 1.0f); + set(px + 1, height - 2, 1.0f); + set(px - 1, height - 2, 1.0f); + set(px , height - 1, 1.0f); + set(px + 1, height - 1, 1.0f); + set(px - 1, height - 1, 1.0f); + } + + sleep_ms(20); + } + + return 0; +} diff --git a/examples/interstate75/interstate75_rainbow.cmake b/examples/interstate75/interstate75_rainbow.cmake new file mode 100644 index 00000000..ce76c3b9 --- /dev/null +++ b/examples/interstate75/interstate75_rainbow.cmake @@ -0,0 +1,16 @@ +set(OUTPUT_NAME interstate75_rainbow) +add_executable(${OUTPUT_NAME} interstate75_rainbow.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 + hardware_pwm + hub75 + interstate75 +) diff --git a/examples/interstate75/interstate75_rainbow.cpp b/examples/interstate75/interstate75_rainbow.cpp new file mode 100644 index 00000000..49b60777 --- /dev/null +++ b/examples/interstate75/interstate75_rainbow.cpp @@ -0,0 +1,120 @@ +#include +#include "pico/stdlib.h" + +#include "libraries/pico_graphics/pico_graphics.hpp" +#include "libraries/interstate75/interstate75.hpp" + +using namespace pimoroni; + +RGBLED led_rgb(Interstate75::LED_R, Interstate75::LED_G, Interstate75::LED_B, Polarity::ACTIVE_LOW); +Button button_a(Interstate75::A); +Button button_b(Interstate75::B); + + + +//If the display looks streaky or corrupted then uncomment one of the other initalisers + +//Works with our 32x32 panels https://shop.pimoroni.com/products/rgb-led-matrix-panel?variant=35962488650 https://shop.pimoroni.com/products/rgb-led-matrix-panel?variant=19321740999 +Hub75 hub75(32, 32, nullptr, PANEL_GENERIC, false); +//or using 2 of these panels +//Hub75 hub75(64, 32, nullptr, PANEL_GENERIC, false); + +//and 64x32 panel https://shop.pimoroni.com/products/rgb-led-matrix-panel?variant=42312764298 +//Hub75 hub75(64, 32, nullptr, PANEL_GENERIC, false); +//or using 2 of these panels +//Hub75 hub75(128, 32, nullptr, PANEL_GENERIC, false); + +//Works with our 64x64 panel https://shop.pimoroni.com/products/rgb-led-matrix-panel?variant=3029531983882 +//Hub75 hub75(64, 64, nullptr, PANEL_GENERIC, false); +//or using 2 of these panels +//Hub75 hub75(128, 64, nullptr, PANEL_GENERIC, false); + +PicoGraphics_PenRGB888 graphics(hub75.width, hub75.height, nullptr); + +// HSV Conversion expects float inputs in the range of 0.00-1.00 for each channel +// Outputs are rgb in the range 0-255 for each channel +void from_hsv(float h, float s, float v, uint8_t &r, uint8_t &g, uint8_t &b) { + float i = floor(h * 6.0f); + float f = h * 6.0f - i; + v *= 255.0f; + uint8_t p = v * (1.0f - s); + uint8_t q = v * (1.0f - f * s); + uint8_t t = v * (1.0f - (1.0f - f) * s); + + switch (int(i) % 6) { + case 0: r = v; g = t; b = p; break; + case 1: r = q; g = v; b = p; break; + case 2: r = p; g = v; b = t; break; + case 3: r = p; g = q; b = v; break; + case 4: r = t; g = p; b = v; break; + case 5: r = v; g = p; b = q; break; + } +} + + +// Interrupt callback required function +void __isr dma_complete() { + hub75.dma_complete(); +} + +int main() { + + stdio_init_all(); + + uint8_t hue_map[hub75.width][3]; + for(uint i = 0; i < hub75.width; i++) { + from_hsv(i / (float) hub75.width, 1.0f, 1.0f, hue_map[i][0], hue_map[i][1], hue_map[i][2]); + } + + hub75.start(dma_complete); + graphics.set_font("bitmap8"); + + float i = 0; + bool animate = true; + float stripe_width = 3.0f; + float speed = 5.0f; + float curve = 0.0f; + float led_h = 0.0f; + + while(true) { + + if(animate) { + i += speed; + } + + if(button_a.raw()) { + speed += 0.05f; + speed = speed >= 10.0f ? 10.0f : speed; + animate = true; + } + if(button_b.raw()) { + speed -= 0.05f; + speed = speed <= 0.0f ? 0.0f : speed; + animate = true; + } + + for(uint x = 0; x < hub75.width; x++) { + for(uint y = 0; y < hub75.height; y++) { + int v = ((sin((x + y) / stripe_width + (sin((y * 3.1415927f * 2.0f) / (float)hub75.width) * curve) + i / 15.0f) + 1.5f) / 2.5f) * 255.0f; + + uint8_t r = (hue_map[x][0] * v) / 256; + uint8_t g = (hue_map[x][1] * v) / 256; + uint8_t b = (hue_map[x][2] * v) / 256; + + graphics.set_pen(r, g, b); + graphics.pixel(Point(x, y)); + } + } + hub75.update(&graphics); + + led_rgb.set_hsv(led_h, 1.0f, 1.0f); + led_h += 0.01; + + + sleep_ms(20); + } + + printf("done\n"); + + return 0; +} diff --git a/examples/interstate75/interstate75_rainbow_text.cmake b/examples/interstate75/interstate75_rainbow_text.cmake new file mode 100644 index 00000000..87963c78 --- /dev/null +++ b/examples/interstate75/interstate75_rainbow_text.cmake @@ -0,0 +1,15 @@ +set(OUTPUT_NAME interstate75_rainbow_text) +add_executable(${OUTPUT_NAME} interstate75_rainbow_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 + pico_graphics +) diff --git a/examples/interstate75/interstate75_rainbow_text.cpp b/examples/interstate75/interstate75_rainbow_text.cpp new file mode 100644 index 00000000..650625fe --- /dev/null +++ b/examples/interstate75/interstate75_rainbow_text.cpp @@ -0,0 +1,117 @@ +#include +#include "pico/stdlib.h" + +#include "libraries/pico_graphics/pico_graphics.hpp" +#include "drivers/hub75/hub75.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 32*2 pixels. +//If the display looks streaky or corrupted then uncomment one of the other initalisers + +//Works with our 32x32 panels https://shop.pimoroni.com/products/rgb-led-matrix-panel?variant=35962488650 https://shop.pimoroni.com/products/rgb-led-matrix-panel?variant=19321740999 +Hub75 hub75(32, 32, nullptr, PANEL_GENERIC, false); +//or using 2 of these panels +//Hub75 hub75(64, 32, nullptr, PANEL_GENERIC, false); + +//and 64x32 panel https://shop.pimoroni.com/products/rgb-led-matrix-panel?variant=42312764298 +//Hub75 hub75(64, 32, nullptr, PANEL_GENERIC, false); +//or using 2 of these panels +//Hub75 hub75(128, 32, nullptr, PANEL_GENERIC, false); + +//Works with our 64x64 panel https://shop.pimoroni.com/products/rgb-led-matrix-panel?variant=3029531983882 +//Hub75 hub75(64, 64, nullptr, PANEL_GENERIC, false); +//or using 2 of these panels +//Hub75 hub75(128, 64, nullptr, PANEL_GENERIC, false); + +PicoGraphics_PenRGB888 graphics(hub75.width, hub75.height, nullptr); + + +// HSV Conversion expects float inputs in the range of 0.00-1.00 for each channel +// Outputs are rgb in the range 0-255 for each channel +void from_hsv(float h, float s, float v, uint8_t &r, uint8_t &g, uint8_t &b) { + float i = floor(h * 6.0f); + float f = h * 6.0f - i; + v *= 255.0f; + uint8_t p = v * (1.0f - s); + uint8_t q = v * (1.0f - f * s); + uint8_t t = v * (1.0f - (1.0f - f) * s); + + switch (int(i) % 6) { + case 0: r = v; g = t; b = p; break; + case 1: r = q; g = v; b = p; break; + case 2: r = p; g = v; b = t; break; + case 3: r = p; g = q; b = v; break; + case 4: r = t; g = p; b = v; break; + case 5: r = v; g = p; b = q; break; + } +} + +void text(std::string t, Point p, float s = 1.0f, float a = 1.0f) { + int w = graphics.measure_text(t, s); + p.x += (hub75.width / 2) - (w / 2); + p.y += (hub75.height / 2); + graphics.text(t, Point(p.x, p.y), -1, s, a); + graphics.text(t, Point(p.x + 1, p.y), -1, s, a); + graphics.text(t, Point(p.x + 1, p.y + 1), -1, s, a); + graphics.text(t, Point(p.x, p.y + 1), -1, s, a); +} + +// Interrupt callback required function +void __isr dma_complete() { + hub75.dma_complete(); +} + +int main() { + //Start hub75 driver + hub75.start(dma_complete); + + uint8_t hue_map[hub75.width][3]; + for(int i = 0; i < (int)hub75.width; i++) { + from_hsv(i / (float)hub75.width, 1.0f, 1.0f, hue_map[i][0], hue_map[i][1], hue_map[i][2]); + } + + graphics.set_font("sans"); + uint i = 0; + + while(true) { + i++; + graphics.set_pen(0, 0, 0); + graphics.clear(); + + float s = 0.8f;//0.65f + (sin(i / 25.0f) * 0.15f); + float a = 2.0f;// (sin(i / 25.0f) * 100.0f); + + float x = (sin((i) / 50.0f) * 190.0f); + float y = (cos((i) / 30.0f) * 10.0f - 10.0f); + graphics.set_pen(255, 255, 255); + text("Interstate 75 HUB75 display driver", Point(x, y), s, a); + + uint8_t *p = (uint8_t *)graphics.frame_buffer; + for(size_t i = 0; i < hub75.width * hub75.height; i++) { + int x = i % hub75.width; + int y = i / hub75.width; + uint r = *p++; + uint g = *p++; + uint b = *p++; + p++; + + if(r > 0) { + r = hue_map[x][0]; + g = hue_map[x][1]; + b = hue_map[x][2]; + } + + graphics.set_pen(r, g, b); + graphics.pixel(Point(x, y)); + } + hub75.update(&graphics); + } + + printf("done\n"); + + return 0; +} diff --git a/examples/interstate75_legacy/CMakeLists.txt b/examples/interstate75_legacy/CMakeLists.txt new file mode 100644 index 00000000..7d8d4278 --- /dev/null +++ b/examples/interstate75_legacy/CMakeLists.txt @@ -0,0 +1,3 @@ +include(interstate75_hello_world.cmake) +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_legacy/font_outline_10x14.hpp similarity index 100% rename from examples/interstate75/font_outline_10x14.hpp rename to examples/interstate75_legacy/font_outline_10x14.hpp diff --git a/examples/interstate75/interstate75_hello_world.cmake b/examples/interstate75_legacy/interstate75_hello_world.cmake similarity index 100% rename from examples/interstate75/interstate75_hello_world.cmake rename to examples/interstate75_legacy/interstate75_hello_world.cmake diff --git a/examples/interstate75/interstate75_hello_world.cpp b/examples/interstate75_legacy/interstate75_hello_world.cpp similarity index 100% rename from examples/interstate75/interstate75_hello_world.cpp rename to examples/interstate75_legacy/interstate75_hello_world.cpp diff --git a/examples/interstate75/interstate75_pio_dma.cmake b/examples/interstate75_legacy/interstate75_pio_dma.cmake similarity index 100% rename from examples/interstate75/interstate75_pio_dma.cmake rename to examples/interstate75_legacy/interstate75_pio_dma.cmake diff --git a/examples/interstate75/interstate75_pio_dma.cpp b/examples/interstate75_legacy/interstate75_pio_dma.cpp similarity index 100% rename from examples/interstate75/interstate75_pio_dma.cpp rename to examples/interstate75_legacy/interstate75_pio_dma.cpp diff --git a/examples/interstate75/interstate75_scrolling_text.cmake b/examples/interstate75_legacy/interstate75_scrolling_text.cmake similarity index 100% rename from examples/interstate75/interstate75_scrolling_text.cmake rename to examples/interstate75_legacy/interstate75_scrolling_text.cmake diff --git a/examples/interstate75/interstate75_scrolling_text.cpp b/examples/interstate75_legacy/interstate75_scrolling_text.cpp similarity index 100% rename from examples/interstate75/interstate75_scrolling_text.cpp rename to examples/interstate75_legacy/interstate75_scrolling_text.cpp diff --git a/libraries/CMakeLists.txt b/libraries/CMakeLists.txt index 309f25f9..d83d9b99 100644 --- a/libraries/CMakeLists.txt +++ b/libraries/CMakeLists.txt @@ -38,3 +38,4 @@ add_subdirectory(jpegdec) add_subdirectory(inky_frame) add_subdirectory(galactic_unicorn) add_subdirectory(gfx_pack) +add_subdirectory(interstate75) diff --git a/libraries/interstate75/CMakeLists.txt b/libraries/interstate75/CMakeLists.txt new file mode 100644 index 00000000..91a0e9b6 --- /dev/null +++ b/libraries/interstate75/CMakeLists.txt @@ -0,0 +1 @@ +include(interstate75.cmake) \ No newline at end of file diff --git a/libraries/interstate75/README.md b/libraries/interstate75/README.md new file mode 100644 index 00000000..817fdf68 --- /dev/null +++ b/libraries/interstate75/README.md @@ -0,0 +1,84 @@ +# Interstate 75 (C++) + +This library offers convenient functions for interacting with [Interstate75](https://shop.pimoroni.com/products/interstate-75) and [Interstate75W](https://shop.pimoroni.com/products/interstate-75-w) - Interstate75 and Interstate75W offer an convenient way and 2 input buttons for all your display and control needs. + +- [Example Program](#example-program) +- [Function Reference](#function-reference) + - [PicoGraphics](#picographics) + - [HUB75](#hub75) + +## Example Program + +The following example sets up Pico Display, displays some basic demo text and graphics and will illuminate the LED green if the A button is pressed. + +```c++ +#include "libraries/pico_graphics/pico_graphics.hpp" +#include "libraries/interstate75/interstate75.hpp" +#include "rgbled.hpp" +#include "button.hpp" + +using namespace pimoroni; + +// Display driver for a single 32x32 hub75 matrix +Hub75 hub75(32, 32, nullptr, PANEL_GENERIC, false); + +// Graphics library - in 24Bit mode with 16M colours +PicoGraphics_PenRGB888 graphics(hub75.width, hub75.height, nullptr); + +// And each button +Button button_a(Interstate75::A); +Button button_b(Interstate75::B); + +// RGB LED +RGBLED led(Interstate75::LED_R, Interstate75::LED_G, Interstate75::LED_B); + +// Interrupt callback required function +void __isr dma_complete() { + hub75.dma_complete(); +} + +int main() { + hub75.start(dma_complete); + + while(true) { + // detect if the A button is pressed (could be A, B, C, D or E) + if(button_a.raw()) { + // make the led glow green + // parameters are red, green, blue all between 0 and 255 + // these are also gamma corrected + led.set_rgb(0, 255, 0); + } + + // set the colour of the pen + // parameters are red, green, blue all between 0 and 255 + graphics.set_pen(100, 40, 50); + + // fill the screen with the current pen colour + graphics.clear(); + + // draw a box to put some text in + graphics.set_pen(10, 20, 100); + Rect text_rect(1, 1, hub75.width-2, hub75.height-2); + graphics.rectangle(text_rect); + + // write some text inside the box with 10 pixels of margin + // automatically word wrapping + text_rect.deflate(1); + graphics.set_pen(110, 120, 130); + graphics.text("This is text", Point(text_rect.x, text_rect.y), text_rect.w, 1.0f); + + // now we've done our drawing let's update the screen + hub75.update(&graphics); + } +} +``` + +## Function Reference + +### PicoGraphics + +Pico GFX Pack uses our Pico Graphics library to draw graphics and text. For more information [read the Pico Graphics function reference.](../pico_graphics/README.md#function-reference). + +### HUB75 + +Pico Display uses the HUB75 display driver to handle the led matrix. For more information [read the HUB75 README.](../../drivers/hub75/README.md). \ No newline at end of file diff --git a/libraries/interstate75/interstate75.cmake b/libraries/interstate75/interstate75.cmake new file mode 100644 index 00000000..34ca6b2f --- /dev/null +++ b/libraries/interstate75/interstate75.cmake @@ -0,0 +1,7 @@ +set(LIB_NAME interstate75) +add_library(${LIB_NAME} INTERFACE) + +target_include_directories(${LIB_NAME} INTERFACE ${CMAKE_CURRENT_LIST_DIR}) + +# Pull in pico libraries that we need +target_link_libraries(${LIB_NAME} INTERFACE pico_stdlib hub75 button rgbled hardware_pwm) \ No newline at end of file diff --git a/libraries/interstate75/interstate75.hpp b/libraries/interstate75/interstate75.hpp new file mode 100644 index 00000000..28ac8838 --- /dev/null +++ b/libraries/interstate75/interstate75.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include "drivers/hub75/hub75.hpp" +#include "drivers/button/button.hpp" +#include "drivers/rgbled/rgbled.hpp" + +namespace pimoroni { + + namespace Interstate75{ + static const uint8_t A = 14; + static const uint8_t B = 15; + static const uint8_t LED_R = 16; + static const uint8_t LED_G = 17; + static const uint8_t LED_B = 18; + }; +} diff --git a/libraries/pico_graphics/README.md b/libraries/pico_graphics/README.md index 947e65bd..633ef8cd 100644 --- a/libraries/pico_graphics/README.md +++ b/libraries/pico_graphics/README.md @@ -49,7 +49,8 @@ A monochrome OLED display can currently only work with `1Bit` type buffers, and * `P4` - 4-bit packed, with an 8 colour palette. This is commonly used for 7/8-colour e-ink displays or driving large displays with few colours. * `P8` - 8-bit, with a 256 colour palette. Great balance of memory usage versus available colours. You can replace palette entries on the fly. * `RGB332` - 8-bit, with a fixed 256 colour RGB332 palette. Great for quickly porting an RGB565 app to use less RAM. Limits your colour choices, but is easier to grok. -* `RGB565` - 16-bit, 65K "True Colour." Great for rainbows, gradients and images but comes at the cost of RAM! +* `RGB565` - 16-bit, 65K "High Colour." Great for rainbows, gradients and images but comes at the cost of RAM! +* `RGB888` - 24-bit, 16M "True Colour." Lists every color possible to be seen by the human eye but comes at double the cost of RAM compared to `RGB565`. ### Creating A Pico Graphics Instance @@ -63,6 +64,7 @@ PicoGraphics_PenP4 graphics(WIDTH, HEIGHT, nullptr); // For colour LCDs such PicoGraphics_PenP8 graphics(WIDTH, HEIGHT, nullptr); // ditto- uses 2x the RAM of P4 PicoGraphics_PenRGB332 graphics(WIDTH, HEIGHT, nullptr); // ditto PicoGraphics_PenRGB565 graphics(WIDTH, HEIGHT, nullptr); // For 16-bit colour LCDs. Uses 2x the RAM of P8 or RGB332 but permits 65K colour. +PicoGraphics_PenRGB888 graphics(WIDTH, HEIGHT, nullptr); // For 32-bit colour devices. Uses 4x the RAM of P8 or RGB332 but permits 16M colour. ``` To draw something to a display you should create a display driver instance, eg: diff --git a/micropython/examples/interstate75/75W/cheerlights.py b/micropython/examples/interstate75/75W/cheerlights.py new file mode 100644 index 00000000..79d41231 --- /dev/null +++ b/micropython/examples/interstate75/75W/cheerlights.py @@ -0,0 +1,111 @@ +# The Interstate75W example updates a pixel every five(ish) minutes +# to display the most recent #cheerlights colour. Discover the most popular +# colours over time, or use it as an avant garde (but colourful) 53 hour clock! +# Find out more about the Cheerlights API at https://cheerlights.com/ +# +# To run this example you'll need WIFI_CONFIG.py and network_manager.py from +# the pimoroni-pico micropython/examples/common folder + +import WIFI_CONFIG +from network_manager import NetworkManager +import uasyncio +import urequests +import time +import interstate75 +from machine import Timer + + +URL = 'http://api.thingspeak.com/channels/1417/field/2/last.json' + +UPDATE_INTERVAL = 327 # refresh interval in secs. Be nice to free APIs! +# this esoteric number is used so that a column of LEDs equates (approximately) to an hour + + +def status_handler(mode, status, ip): + # reports wifi connection status + print(mode, status, ip) + print('Connecting to wifi...') + if status is not None: + if status: + print('Wifi connection successful!') + else: + print('Wifi connection failed!') + + +def hex_to_rgb(hex): + # converts a hex colour code into RGB + h = hex.lstrip('#') + r, g, b = (int(h[i:i + 2], 16) for i in (0, 2, 4)) + return r, g, b + + +def get_data(): + # open the json file + print(f'Requesting URL: {URL}') + r = urequests.get(URL) + # open the json data + j = r.json() + print('Data obtained!') + r.close() + + # extract hex colour from the json data + hex = j['field2'] + + # add the new hex colour to the end of the array + colour_array.append(hex) + print(f'Colour added to array: {hex}') + # remove the oldest colour in the array + colour_array.pop(0) + update_leds() + + +def update_leds(): + # light up the LEDs + # this step takes a second, it's doing a lot of hex_to_rgb calculations! + print("Updating LEDs...") + i = 0 + for x in range(width): + for y in range(height): + r = hex_to_rgb(colour_array[i])[0] + g = hex_to_rgb(colour_array[i])[1] + b = hex_to_rgb(colour_array[i])[2] + current_colour = graphics.create_pen(r, g, b) + graphics.set_pen(current_colour) + graphics.pixel(x, y) + i = i + 1 + i75.update(graphics) + print("LEDs updated!") + + +i75 = interstate75.Interstate75(display=interstate75.DISPLAY_INTERSTATE75_32X32) +graphics = i75.display + +width = i75.width +height = i75.height + + +current_colour = graphics.create_pen(0, 0, 0) + +# set up an list to store the colours +colour_array = ["#000000"] * height * width + +# set up wifi +try: + network_manager = NetworkManager(WIFI_CONFIG.COUNTRY, status_handler=status_handler) + uasyncio.get_event_loop().run_until_complete(network_manager.client(WIFI_CONFIG.SSID, WIFI_CONFIG.PSK)) +except Exception as e: + print(f'Wifi connection failed! {e}') + +# get the first lot of data +get_data() + +# start timer (the timer will call the function to update our data every UPDATE_INTERVAL) +timer = Timer(-1) +timer.init(period=UPDATE_INTERVAL * 1000, mode=Timer.PERIODIC, callback=lambda t: get_data()) + +while True: + + update_leds() + + # pause for a moment (important or the USB serial device will fail) + time.sleep(0.001) diff --git a/micropython/examples/interstate75/75W/clock.py b/micropython/examples/interstate75/75W/clock.py new file mode 100644 index 00000000..c95ca905 --- /dev/null +++ b/micropython/examples/interstate75/75W/clock.py @@ -0,0 +1,194 @@ +# Clock example with NTP synchronization +# +# Create a secrets.py with your Wifi details to be able to get the time +# when the Interstate75W isn't connected to Thonny. +# +# secrets.py should contain: +# WIFI_SSID = "Your WiFi SSID" +# WIFI_PASSWORD = "Your WiFi password" + +import time +import math +import machine +import network +import ntptime +from interstate75 import Interstate75, DISPLAY_INTERSTATE75_32X32 + +i75 = Interstate75(display=DISPLAY_INTERSTATE75_32X32) +graphics = i75.display + +width = i75.width +height = i75.height + +try: + from secrets import WIFI_SSID, WIFI_PASSWORD + wifi_available = True +except ImportError: + print("Create secrets.py with your WiFi credentials to get time from NTP") + wifi_available = False + + +# constants for controlling the background colour throughout the day +MIDDAY_HUE = 1.1 +MIDNIGHT_HUE = 0.8 +HUE_OFFSET = -0.1 + +MIDDAY_SATURATION = 1.0 +MIDNIGHT_SATURATION = 1.0 + +MIDDAY_VALUE = 0.8 +MIDNIGHT_VALUE = 0.3 + +# create the rtc object +rtc = machine.RTC() + + +# set up some pens to use later +WHITE = graphics.create_pen(255, 255, 255) +BLACK = graphics.create_pen(0, 0, 0) + + +@micropython.native # noqa: F821 +def from_hsv(h, s, v): + i = math.floor(h * 6.0) + f = h * 6.0 - i + v *= 255.0 + p = v * (1.0 - s) + q = v * (1.0 - f * s) + t = v * (1.0 - (1.0 - f) * s) + + i = int(i) % 6 + if i == 0: + return int(v), int(t), int(p) + if i == 1: + return int(q), int(v), int(p) + if i == 2: + return int(p), int(v), int(t) + if i == 3: + return int(p), int(q), int(v) + if i == 4: + return int(t), int(p), int(v) + if i == 5: + return int(v), int(p), int(q) + + +# function for drawing a gradient background +def gradient_background(start_hue, start_sat, start_val, end_hue, end_sat, end_val): + half_width = width // 2 + for x in range(0, half_width): + hue = ((end_hue - start_hue) * (x / half_width)) + start_hue + sat = ((end_sat - start_sat) * (x / half_width)) + start_sat + val = ((end_val - start_val) * (x / half_width)) + start_val + colour = from_hsv(hue, sat, val) + graphics.set_pen(graphics.create_pen(int(colour[0]), int(colour[1]), int(colour[2]))) + for y in range(0, height): + graphics.pixel(x, y) + graphics.pixel(width - x - 1, y) + + colour = from_hsv(end_hue, end_sat, end_val) + graphics.set_pen(graphics.create_pen(int(colour[0]), int(colour[1]), int(colour[2]))) + for y in range(0, height): + graphics.pixel(half_width, y) + + +# function for drawing outlined text +def outline_text(text, x, y): + graphics.set_pen(BLACK) + graphics.text(text, x - 1, y - 1, -1, 1) + graphics.text(text, x, y - 1, -1, 1) + graphics.text(text, x + 1, y - 1, -1, 1) + graphics.text(text, x - 1, y, -1, 1) + graphics.text(text, x + 1, y, -1, 1) + graphics.text(text, x - 1, y + 1, -1, 1) + graphics.text(text, x, y + 1, -1, 1) + graphics.text(text, x + 1, y + 1, -1, 1) + + graphics.set_pen(WHITE) + graphics.text(text, x, y, -1, 1) + + +# Connect to wifi and synchronize the RTC time from NTP +def sync_time(): + if not wifi_available: + return + + # Start connection + wlan = network.WLAN(network.STA_IF) + wlan.active(True) + wlan.connect(WIFI_SSID, WIFI_PASSWORD) + + # Wait for connect success or failure + max_wait = 100 + while max_wait > 0: + if wlan.status() < 0 or wlan.status() >= 3: + break + max_wait -= 1 + print('waiting for connection...') + time.sleep(0.2) + + redraw_display_if_reqd() + + if max_wait > 0: + print("Connected") + + try: + ntptime.settime() + print("Time set") + except OSError: + pass + + wlan.disconnect() + wlan.active(False) + + +# NTP synchronizes the time to UTC, this allows you to adjust the displayed time + +utc_offset = 0 + +year, month, day, wd, hour, minute, second, _ = rtc.datetime() + +last_second = second + + +# Check whether the RTC time has changed and if so redraw the display +def redraw_display_if_reqd(): + global year, month, day, wd, hour, minute, second, last_second + + year, month, day, wd, hour, minute, second, _ = rtc.datetime() + if second != last_second: + hour += utc_offset + time_through_day = (((hour * 60) + minute) * 60) + second + percent_through_day = time_through_day / 86400 + percent_to_midday = 1.0 - ((math.cos(percent_through_day * math.pi * 2) + 1) / 2) + print(percent_to_midday) + + hue = ((MIDDAY_HUE - MIDNIGHT_HUE) * percent_to_midday) + MIDNIGHT_HUE + sat = ((MIDDAY_SATURATION - MIDNIGHT_SATURATION) * percent_to_midday) + MIDNIGHT_SATURATION + val = ((MIDDAY_VALUE - MIDNIGHT_VALUE) * percent_to_midday) + MIDNIGHT_VALUE + + gradient_background(hue, sat, val, + hue + HUE_OFFSET, sat, val) + + clock = "{:02}:{:02}:{:02}".format(hour, minute, second) + + # set the font + graphics.set_font("bitmap8") + + # calculate text position so that it is centred + w = graphics.measure_text(clock, 1) + x = int(width / 2 - w / 2 + 1) + y = 11 + + outline_text(clock, x, y) + + last_second = second + + +sync_time() + +while True: + + redraw_display_if_reqd() + + # Update the display + i75.update() diff --git a/micropython/examples/interstate75/balls_demo.py b/micropython/examples/interstate75/balls_demo.py new file mode 100644 index 00000000..29921148 --- /dev/null +++ b/micropython/examples/interstate75/balls_demo.py @@ -0,0 +1,62 @@ +import time +import random +from interstate75 import Interstate75, DISPLAY_INTERSTATE75_32X32 + +i75 = Interstate75(display=DISPLAY_INTERSTATE75_32X32) +graphics = i75.display + +width = i75.width +height = i75.height + + +class Ball: + def __init__(self, x, y, r, dx, dy, pen): + self.x = x + self.y = y + self.r = r + self.dx = dx + self.dy = dy + self.pen = pen + + +# initialise shapes +balls = [] +for i in range(0, 10): + r = random.randint(0, 3) + 3 + balls.append( + Ball( + random.randint(r, r + (width - 2 * r)), + random.randint(r, r + (height - 2 * r)), + r, + (7 - r) / 4, + (7 - r) / 4, + graphics.create_pen(random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)), + ) + ) + +BG = graphics.create_pen(0, 0, 0) + +while True: + graphics.set_pen(BG) + graphics.clear() + + for ball in balls: + ball.x += ball.dx + ball.y += ball.dy + + xmax = width - ball.r + xmin = ball.r + ymax = height - ball.r + ymin = ball.r + + if ball.x < xmin or ball.x > xmax: + ball.dx *= -1 + + if ball.y < ymin or ball.y > ymax: + ball.dy *= -1 + + graphics.set_pen(ball.pen) + graphics.circle(int(ball.x), int(ball.y), int(ball.r)) + + i75.update() + time.sleep(0.025) diff --git a/micropython/examples/interstate75/buttons.py b/micropython/examples/interstate75/buttons.py new file mode 100644 index 00000000..b60a3be6 --- /dev/null +++ b/micropython/examples/interstate75/buttons.py @@ -0,0 +1,46 @@ +''' +buttons.py +Push either switch A or switch B to change the display +''' +import interstate75 + +i75 = interstate75.Interstate75(display=interstate75.DISPLAY_INTERSTATE75_32X32) +graphics = i75.display + +width = i75.width +height = i75.height + +A_COLOR = graphics.create_pen(0x31, 0x81, 0xCE) +A_TEXT = graphics.create_pen(0xCE, 0x7E, 0x31) + +B_COLOR = graphics.create_pen(0xC3, 0x3C, 0xBD) +B_TEXT = graphics.create_pen(0x3C, 0xC3, 0x42) + +BG = graphics.create_pen(0xC1, 0x99, 0x3E) + + +def display_a(): + graphics.set_pen(A_COLOR) + graphics.clear() + graphics.set_pen(A_TEXT) + graphics.text("A", 8, 6, False, 3) + i75.update() + + +def display_b(): + graphics.set_pen(B_COLOR) + graphics.clear() + graphics.set_pen(B_TEXT) + graphics.text("B", 8, 6, False, 3) + i75.update() + + +graphics.set_pen(BG) +graphics.clear() +i75.update() + +while 1: + if i75.switch_pressed(interstate75.SWITCH_A): + display_a() + if i75.switch_pressed(interstate75.SWITCH_B): + display_b() diff --git a/micropython/examples/interstate75/font_10x14.py b/micropython/examples/interstate75/font_10x14.py deleted file mode 100644 index 83e28f39..00000000 --- a/micropython/examples/interstate75/font_10x14.py +++ /dev/null @@ -1,100 +0,0 @@ -letter_width = 10 -letter_height = 14 -font = [ - [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], # "" -] diff --git a/micropython/examples/interstate75/font_8x12.py b/micropython/examples/interstate75/font_8x12.py deleted file mode 100644 index 37410aeb..00000000 --- a/micropython/examples/interstate75/font_8x12.py +++ /dev/null @@ -1,100 +0,0 @@ -letter_width = 8 -letter_height = 12 -font = [ - [0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000], # " " - [0x2fc, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000], # "!" - [0x01c, 0x000, 0x01c, 0x000, 0x000, 0x000, 0x000, 0x000], # """ - [0x090, 0x090, 0x3fc, 0x090, 0x090, 0x3fc, 0x090, 0x090], # "#" - [0x238, 0x244, 0x7fe, 0x244, 0x184, 0x000, 0x000, 0x000], # "$" - [0x008, 0x014, 0x308, 0x0c0, 0x030, 0x10c, 0x280, 0x100], # "%" - [0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000], # "&" - [0x01c, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000], # "'" - [0x0f0, 0x30c, 0x402, 0x000, 0x000, 0x000, 0x000, 0x000], # "(" - [0x402, 0x30c, 0x0f0, 0x000, 0x000, 0x000, 0x000, 0x000], # ")" - [0x024, 0x018, 0x018, 0x024, 0x000, 0x000, 0x000, 0x000], # "*" - [0x040, 0x040, 0x1f0, 0x040, 0x040, 0x000, 0x000, 0x000], # "+" - [0x400, 0x300, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000], # "," - [0x040, 0x040, 0x040, 0x040, 0x040, 0x000, 0x000, 0x000], # "-" - [0x200, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000], # "." - [0x600, 0x180, 0x060, 0x018, 0x006, 0x000, 0x000, 0x000], # "/" - [0x1f8, 0x204, 0x204, 0x204, 0x1f8, 0x000, 0x000, 0x000], # "0" - [0x210, 0x208, 0x3fc, 0x200, 0x200, 0x000, 0x000, 0x000], # "1" - [0x208, 0x304, 0x284, 0x244, 0x238, 0x000, 0x000, 0x000], # "2" - [0x108, 0x204, 0x204, 0x264, 0x198, 0x000, 0x000, 0x000], # "3" - [0x0c0, 0x0b0, 0x088, 0x3fc, 0x080, 0x000, 0x000, 0x000], # "4" - [0x23c, 0x224, 0x224, 0x224, 0x1c4, 0x000, 0x000, 0x000], # "5" - [0x1f8, 0x224, 0x224, 0x224, 0x1c4, 0x000, 0x000, 0x000], # "6" - [0x004, 0x004, 0x384, 0x064, 0x01c, 0x000, 0x000, 0x000], # "7" - [0x1d8, 0x224, 0x224, 0x224, 0x1d8, 0x000, 0x000, 0x000], # "8" - [0x238, 0x244, 0x244, 0x244, 0x1f8, 0x000, 0x000, 0x000], # "9" - [0x204, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000], # ":" - [0x400, 0x304, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000], # ";" - [0x080, 0x140, 0x220, 0x000, 0x000, 0x000, 0x000, 0x000], # "<" - [0x220, 0x220, 0x220, 0x220, 0x220, 0x220, 0x000, 0x000], # "=" - [0x220, 0x140, 0x080, 0x000, 0x000, 0x000, 0x000, 0x000], # ">" - [0x000, 0x004, 0x2c4, 0x024, 0x018, 0x000, 0x000, 0x000], # "?" - [0x3f0, 0x408, 0x9c4, 0xa24, 0xa24, 0x1f8, 0x000, 0x000], # "@" - [0x380, 0x070, 0x04c, 0x070, 0x380, 0x000, 0x000, 0x000], # "A" - [0x3fc, 0x224, 0x224, 0x1d8, 0x000, 0x000, 0x000, 0x000], # "B" - [0x1f8, 0x204, 0x204, 0x204, 0x000, 0x000, 0x000, 0x000], # "C" - [0x3fc, 0x204, 0x204, 0x1f8, 0x000, 0x000, 0x000, 0x000], # "D" - [0x3fc, 0x224, 0x224, 0x224, 0x000, 0x000, 0x000, 0x000], # "E" - [0x3fc, 0x024, 0x024, 0x004, 0x000, 0x000, 0x000, 0x000], # "F" - [0x1f8, 0x204, 0x204, 0x244, 0x1c0, 0x000, 0x000, 0x000], # "G" - [0x3fc, 0x020, 0x020, 0x020, 0x3fc, 0x000, 0x000, 0x000], # "H" - [0x204, 0x204, 0x3fc, 0x204, 0x204, 0x000, 0x000, 0x000], # "I" - [0x204, 0x204, 0x1fc, 0x004, 0x004, 0x000, 0x000, 0x000], # "J" - [0x3fc, 0x020, 0x0d0, 0x30c, 0x000, 0x000, 0x000, 0x000], # "K" - [0x3fc, 0x200, 0x200, 0x200, 0x000, 0x000, 0x000, 0x000], # "L" - [0x3fc, 0x008, 0x010, 0x020, 0x020, 0x010, 0x008, 0x3fc], # "M" - [0x3fc, 0x018, 0x060, 0x180, 0x3fc, 0x000, 0x000, 0x000], # "N" - [0x1f8, 0x204, 0x204, 0x204, 0x204, 0x1f8, 0x000, 0x000], # "O" - [0x3fc, 0x044, 0x044, 0x038, 0x000, 0x000, 0x000, 0x000], # "P" - [0x1f8, 0x204, 0x204, 0x204, 0x204, 0x5f8, 0x400, 0x000], # "Q" - [0x3fc, 0x044, 0x0c4, 0x338, 0x000, 0x000, 0x000, 0x000], # "R" - [0x238, 0x244, 0x244, 0x184, 0x000, 0x000, 0x000, 0x000], # "S" - [0x004, 0x004, 0x3fc, 0x004, 0x004, 0x000, 0x000, 0x000], # "T" - [0x1fc, 0x200, 0x200, 0x200, 0x1fc, 0x000, 0x000, 0x000], # "U" - [0x07c, 0x180, 0x200, 0x180, 0x07c, 0x000, 0x000, 0x000], # "V" - [0x07c, 0x180, 0x200, 0x1c0, 0x200, 0x180, 0x07c, 0x000], # "W" - [0x30c, 0x090, 0x060, 0x090, 0x30c, 0x000, 0x000, 0x000], # "X" - [0x00c, 0x030, 0x3c0, 0x030, 0x00c, 0x000, 0x000, 0x000], # "Y" - [0x304, 0x284, 0x264, 0x214, 0x20c, 0x000, 0x000, 0x000], # "Z" - [0x3fc, 0x204, 0x204, 0x000, 0x000, 0x000, 0x000, 0x000], # "[" - [0x006, 0x018, 0x060, 0x180, 0x600, 0x000, 0x000, 0x000], # "\" - [0x204, 0x204, 0x3fc, 0x000, 0x000, 0x000, 0x000, 0x000], # "]" - [0x010, 0x008, 0x004, 0x008, 0x010, 0x000, 0x000, 0x000], # "^" - [0x400, 0x400, 0x400, 0x400, 0x400, 0x400, 0x000, 0x000], # "_" - [0x002, 0x004, 0x008, 0x000, 0x000, 0x000, 0x000, 0x000], # "`" - [0x1c0, 0x220, 0x220, 0x220, 0x1c0, 0x200, 0x000, 0x000], # "a" - [0x3fc, 0x220, 0x220, 0x1c0, 0x000, 0x000, 0x000, 0x000], # "b" - [0x1c0, 0x220, 0x220, 0x000, 0x000, 0x000, 0x000, 0x000], # "c" - [0x1c0, 0x220, 0x220, 0x3fc, 0x000, 0x000, 0x000, 0x000], # "d" - [0x1c0, 0x2a0, 0x2a0, 0x240, 0x000, 0x000, 0x000, 0x000], # "e" - [0x3f8, 0x024, 0x004, 0x000, 0x000, 0x000, 0x000, 0x000], # "f" - [0x4c0, 0x920, 0x920, 0x920, 0x7c0, 0x000, 0x000, 0x000], # "g" - [0x3fc, 0x020, 0x020, 0x3c0, 0x000, 0x000, 0x000, 0x000], # "h" - [0x3e8, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000], # "i" - [0x400, 0x400, 0x3e8, 0x000, 0x000, 0x000, 0x000, 0x000], # "j" - [0x3fc, 0x080, 0x140, 0x220, 0x000, 0x000, 0x000, 0x000], # "k" - [0x1fc, 0x200, 0x200, 0x000, 0x000, 0x000, 0x000, 0x000], # "l" - [0x3c0, 0x020, 0x020, 0x1c0, 0x020, 0x020, 0x3c0, 0x000], # "m" - [0x3c0, 0x020, 0x020, 0x3c0, 0x000, 0x000, 0x000, 0x000], # "n" - [0x1c0, 0x220, 0x220, 0x220, 0x1c0, 0x000, 0x000, 0x000], # "o" - [0xfe0, 0x120, 0x120, 0x0c0, 0x000, 0x000, 0x000, 0x000], # "p" - [0x0c0, 0x120, 0x120, 0xfe0, 0x400, 0x000, 0x000, 0x000], # "q" - [0x3c0, 0x020, 0x020, 0x000, 0x000, 0x000, 0x000, 0x000], # "r" - [0x240, 0x2a0, 0x120, 0x000, 0x000, 0x000, 0x000, 0x000], # "s" - [0x040, 0x1f0, 0x240, 0x200, 0x000, 0x000, 0x000, 0x000], # "t" - [0x1e0, 0x200, 0x200, 0x1e0, 0x000, 0x000, 0x000, 0x000], # "u" - [0x060, 0x180, 0x200, 0x180, 0x060, 0x000, 0x000, 0x000], # "v" - [0x060, 0x180, 0x200, 0x180, 0x200, 0x180, 0x060, 0x000], # "w" - [0x220, 0x140, 0x080, 0x140, 0x220, 0x000, 0x000, 0x000], # "x" - [0x460, 0x280, 0x100, 0x080, 0x060, 0x000, 0x000, 0x000], # "y" - [0x220, 0x320, 0x2a0, 0x260, 0x220, 0x000, 0x000, 0x000], # "z" - [0x060, 0x39c, 0x402, 0x000, 0x000, 0x000, 0x000, 0x000], # "{" - [0x7fe, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000], # "|" - [0x402, 0x39c, 0x060, 0x000, 0x000, 0x000, 0x000, 0x000], # "}" - [0x080, 0x040, 0x040, 0x080, 0x080, 0x040, 0x000, 0x000], # "~" - [0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000], # "" -] diff --git a/micropython/examples/interstate75/i75_128x64_scrolling_wavy_clock.py b/micropython/examples/interstate75/i75_128x64_scrolling_wavy_clock.py deleted file mode 100644 index c6f30cd9..00000000 --- a/micropython/examples/interstate75/i75_128x64_scrolling_wavy_clock.py +++ /dev/null @@ -1,53 +0,0 @@ -from time import ticks_ms -import math - -from machine import RTC -import hub75 -# from font_8x12 import font, letter_width, letter_height -from font_10x14 import font, letter_width, letter_height - -WIDTH, HEIGHT = 128, 64 - -rtc = RTC() - -hub = hub75.Hub75(WIDTH, HEIGHT, stb_invert=True) -hub.start() -hub.clear() - -t_s = ticks_ms() -f_s = ticks_ms() / 1000.0 - -frames = 0 - - -def scroll_text(text, y, t): - text_length = len(text) - x = int(t) - letter = int((x / letter_width) % text_length) - pixel = x % letter_width - char = ord(text[letter]) - for s_x in range(WIDTH): - col = font[char - 32][pixel] - s_y = y + int(math.sin((t / 3.0) + s_x / 30.0) * 8) - hub.set_color_masked(s_x, s_y, col, hub75.color_hsv((t + s_x) / WIDTH, 1.0, 1.0)) - pixel += 1 - if pixel == letter_width: - pixel = 0 - letter += 1 - if letter == text_length: - letter = 0 - char = ord(text[letter]) - - -while True: - year, month, day, wd, hour, minute, second, _ = rtc.datetime() - text = " {:02}:{:02}:{:02} {:04}/{:02}/{:02}".format(hour, minute, second, year, month, day) - - hub.clear() - t = (ticks_ms() - t_s) / 50.0 - scroll_text(text, int(HEIGHT / 2) - int(letter_height / 2), t) - hub.flip() - frames += 1 - if frames % 60 == 0: - f_e = ticks_ms() / 1000.0 - print(frames / (f_e - f_s)) diff --git a/micropython/examples/interstate75/i75_32x32_generic.py b/micropython/examples/interstate75/i75_32x32_generic.py deleted file mode 100644 index 9020ef9b..00000000 --- a/micropython/examples/interstate75/i75_32x32_generic.py +++ /dev/null @@ -1,23 +0,0 @@ -import hub75 -import time - -WIDTH, HEIGHT = 32, 32 - -hub = hub75.Hub75(WIDTH, HEIGHT, panel_type=hub75.PANEL_GENERIC) - -hub.start() -hub.clear() -hub.flip() - -while True: - h = time.ticks_ms() / 5000.0 - hub.set_all_hsv(h, 1.0, 1.0) - for y in range(8): - for x in range(WIDTH): - c = int(x * 255 / WIDTH) - hub.set_rgb(x, y, c, c, c) - for x in range(WIDTH): - hub.set_rgb(x, x, 255, 0, 0) - hub.set_rgb(WIDTH - 1 - x, x, 255, 0, 0) - hub.flip() - time.sleep(1.0 / 60) diff --git a/micropython/examples/interstate75/i75_64x32_clock.py b/micropython/examples/interstate75/i75_64x32_clock.py deleted file mode 100644 index 9a843730..00000000 --- a/micropython/examples/interstate75/i75_64x32_clock.py +++ /dev/null @@ -1,148 +0,0 @@ -import hub75 -from time import ticks_ms -import math -from machine import RTC - -WIDTH, HEIGHT = 64, 32 -MAX_DIST = (WIDTH * WIDTH + HEIGHT * HEIGHT) * 0.5 -DIGITS = [0b1110111, 0b0100100, 0b1011101, 0b1101101, 0b0101110, - 0b1101011, 0b1111010, 0b0100101, 0b1111111, 0b0101111] - -ox = 32 -oy = 16 -hue = 0 - -rtc = RTC() - -hub = hub75.Hub75(WIDTH, HEIGHT, stb_invert=False) -hub.start() -hub.clear() - -set_hsv = hub.set_hsv -set_rgb = hub.set_rgb - - -def dot(x, y, h, s, v): - set_hsv(x, y, h, s, v) - set_hsv(x + 1, y, h, s, v) - set_hsv(x, y + 1, h, s, v) - set_hsv(x + 1, y + 1, h, s, v) - - -def shader_fg(x, y): - """Shades the lit pixels of a digit""" - h = ((x - ox) * (x - ox) + (y - oy) * (y - oy)) / MAX_DIST - set_hsv(x, y, h + hue, 1.0, 1.0) - - -def shader_bg(x, y): - """Shades the unlit pixels of a digit""" - set_rgb(x, y, 10, 10, 10) - - -def draw_number(x, y, number, fg=None, bg=None, digit_width=8, digit_height=15, digit_spacing=2): - """Draw a sequence of digits. - - Uses lines to draw digits like so: - _ _ _ _ _ _ _ - | | | _| _| |_| |_ |_ | |_| |_| - |_| | |_ _| | _| |_| | |_| | - - Digits are bit-packed into DIGITS, - each part corresponds to 1-bit: - - 0b0000001 = Top - 0b0000010 = Top Left - 0b0000100 = Top Right - 0b0001000 = Middle - 0b0010000 = Bottom Left - 0b0100000 = Bottom Right - 0b1000000 = Bottom - - """ - v_line = int((digit_height - 3) / 2) - h_line = digit_width - 2 - - if fg is None: - def fg(x, y): - set_rgb(x, y, 255, 255, 255) - - if bg is None: - def bg(x, y): - pass - - for digit in number: - if digit == " ": - x += digit_spacing - continue - - if digit == ".": - fg(x, y + v_line + v_line + 2) - x += digit_spacing - - try: - parts = DIGITS[ord(digit) - 48] - except IndexError: - x += digit_spacing - continue - - shader = fg if parts & 1 else bg # top - for px in range(h_line): - shader(x + px + 1, y) - - shader = fg if parts & 2 else bg # top left - for py in range(v_line): - shader(x, y + py + 1) - - shader = fg if parts & 4 else bg # top right - for py in range(v_line): - shader(x + h_line + 1, y + 1 + py) - - shader = fg if parts & 8 else bg # middle - for px in range(h_line): - shader(x + px + 1, y + v_line + 1) - - shader = fg if parts & 16 else bg # bottom left - for py in range(v_line): - shader(x, y + v_line + 2 + py) - - shader = fg if parts & 32 else bg # bottom right - for py in range(v_line): - shader(x + h_line + 1, y + v_line + 2 + py) - - shader = fg if parts & 64 else bg # bottom - for px in range(h_line): - shader(x + px + 1, y + v_line + v_line + 2) - - x += digit_width + digit_spacing - - return x, y - - -while True: - t = ticks_ms() - hue = t / 3000.0 - ox = int(32 * math.sin(t / 4000.0) + 32) - oy = int(16 * math.sin((t + 5000) / 3000.0) + 16) - hub.clear() - - year, month, day, wd, hour, minute, second, _ = rtc.datetime() - hms = "{:02} {:02} {:02}".format(hour, minute, second) - ymd = "{:04} {:02} {:02}".format(year, month, day) - - # Hour / Minute / Second - draw_number(1, 1, hms, fg=shader_fg, bg=shader_bg) - - # Year / Month / Day - draw_number(8, 20, ymd, fg=shader_fg, bg=shader_bg, digit_width=5, digit_height=7, digit_spacing=1) - - # Blinking dots - dot_v = (math.sin(t / 1000.0 * math.pi * 2) + 1.0) / 2.0 - dot(20, 5, hue, 0.5, dot_v) - dot(20, 10, hue, 0.5, dot_v) - dot(42, 5, hue, 0.5, dot_v) - dot(42, 10, hue, 0.5, dot_v) - - # hub.set_rgb(ox, oy, 255, 255, 255) - - hub.flip() diff --git a/micropython/examples/interstate75/i75_64x64_fm6126a.py b/micropython/examples/interstate75/i75_64x64_fm6126a.py deleted file mode 100644 index 3244f498..00000000 --- a/micropython/examples/interstate75/i75_64x64_fm6126a.py +++ /dev/null @@ -1,23 +0,0 @@ -import hub75 -import time - -WIDTH, HEIGHT = 64, 64 - -hub = hub75.Hub75(WIDTH, HEIGHT, panel_type=hub75.PANEL_FM6126A) - -hub.start() -hub.clear() -hub.flip() - -while True: - h = time.ticks_ms() / 5000.0 - hub.set_all_hsv(h, 1.0, 1.0) - for y in range(8): - for x in range(WIDTH): - c = int(x * 255 / WIDTH) - hub.set_rgb(x, y, c, c, c) - for x in range(WIDTH): - hub.set_rgb(x, x, 255, 0, 0) - hub.set_rgb(WIDTH - 1 - x, x, 255, 0, 0) - hub.flip() - time.sleep(1.0 / 60) diff --git a/micropython/examples/interstate75/i75_64x64_generic.py b/micropython/examples/interstate75/i75_64x64_generic.py deleted file mode 100644 index 586583f9..00000000 --- a/micropython/examples/interstate75/i75_64x64_generic.py +++ /dev/null @@ -1,23 +0,0 @@ -import hub75 -import time - -WIDTH, HEIGHT = 64, 64 - -hub = hub75.Hub75(WIDTH, HEIGHT, panel_type=hub75.PANEL_GENERIC) - -hub.start() -hub.clear() -hub.flip() - -while True: - h = time.ticks_ms() / 5000.0 - hub.set_all_hsv(h, 1.0, 1.0) - for y in range(8): - for x in range(WIDTH): - c = int(x * 255 / WIDTH) - hub.set_rgb(x, y, c, c, c) - for x in range(WIDTH): - hub.set_rgb(x, x, 255, 0, 0) - hub.set_rgb(WIDTH - 1 - x, x, 255, 0, 0) - hub.flip() - time.sleep(1.0 / 60) diff --git a/micropython/examples/interstate75/i75_64x64_scrolling_text.py b/micropython/examples/interstate75/i75_64x64_scrolling_text.py deleted file mode 100644 index 85480be3..00000000 --- a/micropython/examples/interstate75/i75_64x64_scrolling_text.py +++ /dev/null @@ -1,50 +0,0 @@ -from time import ticks_ms -import math - -import hub75 -# from font_8x12 import font, letter_width, letter_height -from font_10x14 import font, letter_width, letter_height - - -WIDTH, HEIGHT = 64, 64 - -hub = hub75.Hub75(WIDTH, HEIGHT, stb_invert=True) -hub.start() -hub.clear() - -text = " Hello World!" - -t_s = ticks_ms() -f_s = ticks_ms() / 1000.0 - -frames = 0 - - -def scroll_text(text, y, t): - text_length = len(text) - x = int(t) - letter = int((x / letter_width) % text_length) - pixel = x % letter_width - char = ord(text[letter]) - for s_x in range(WIDTH): - col = font[char - 32][pixel] - s_y = y + int(math.sin((t / 3.0) + s_x / 30.0) * 8) - hub.set_color_masked(s_x, s_y, col, hub75.color_hsv(s_x / WIDTH, 1.0, 1.0)) - pixel += 1 - if pixel == letter_width: - pixel = 0 - letter += 1 - if letter == text_length: - letter = 0 - char = ord(text[letter]) - - -while True: - hub.clear() - t = (ticks_ms() - t_s) / 50.0 - scroll_text(text, int(HEIGHT / 2) - int(letter_height / 2), t) - hub.flip() - frames += 1 - if frames % 60 == 0: - f_e = ticks_ms() / 1000.0 - print(frames / (f_e - f_s)) diff --git a/micropython/examples/interstate75/rainbow.py b/micropython/examples/interstate75/rainbow.py new file mode 100644 index 00000000..de812587 --- /dev/null +++ b/micropython/examples/interstate75/rainbow.py @@ -0,0 +1,68 @@ +import time +import math +import interstate75 + +i75 = interstate75.Interstate75(display=interstate75.DISPLAY_INTERSTATE75_32X32) +graphics = i75.display + +width = i75.width +height = i75.height + + +@micropython.native # noqa: F821 +def from_hsv(h, s, v): + i = math.floor(h * 6.0) + f = h * 6.0 - i + v *= 255.0 + p = v * (1.0 - s) + q = v * (1.0 - f * s) + t = v * (1.0 - (1.0 - f) * s) + + i = int(i) % 6 + if i == 0: + return int(v), int(t), int(p) + if i == 1: + return int(q), int(v), int(p) + if i == 2: + return int(p), int(v), int(t) + if i == 3: + return int(p), int(q), int(v) + if i == 4: + return int(t), int(p), int(v) + if i == 5: + return int(v), int(p), int(q) + + +@micropython.native # noqa: F821 +def draw(): + global hue_offset, phase + phase_percent = phase / 15 + for x in range(width): + colour = hue_map[int((x + (hue_offset * width)) % width)] + for y in range(height): + v = ((math.sin((x + y) / stripe_width + phase_percent) + 1.5) / 2.5) + + graphics.set_pen(graphics.create_pen(int(colour[0] * v), int(colour[1] * v), int(colour[2] * v))) + graphics.pixel(x, y) + + i75.update(graphics) + + +hue_map = [from_hsv(x / width, 1.0, 0.5) for x in range(width)] +hue_offset = 0.0 + +animate = True +stripe_width = 3.0 +speed = 5.0 + + +phase = 0 +while True: + + if animate: + phase += speed + + start = time.ticks_ms() + draw() + + print("total took: {} ms".format(time.ticks_ms() - start)) diff --git a/micropython/examples/interstate75/raw_set_pixel.py b/micropython/examples/interstate75/raw_set_pixel.py new file mode 100644 index 00000000..46f2a592 --- /dev/null +++ b/micropython/examples/interstate75/raw_set_pixel.py @@ -0,0 +1,43 @@ +''' +raw_set_pixel.py +This example shows how to set the pixels on the display individually without having to use pico graphics. +This method can be used to save on memory usage. +''' + +import hub75 +import random +import time + +HEIGHT = 32 +WIDTH = 32 +MAX_PIXELS = 64 + +h75 = hub75.Hub75(WIDTH, HEIGHT, stb_invert=False) + + +def rand_pixel(): + x = random.randint(0, WIDTH) + y = random.randint(0, HEIGHT) + return x, y + + +def rand_color(): + r = random.randint(0, 255) + g = random.randint(0, 255) + b = random.randint(0, 255) + return r, g, b + + +h75.start() +counter = 0 + +while 1: + x, y = rand_pixel() + r, g, b = rand_color() + print('Setting Pixel x: {0} y: {1}'.format(x, y)) + h75.set_pixel(x, y, r, g, b) + time.sleep(0.2) + counter += 1 + if counter > MAX_PIXELS: + counter = 0 + h75.clear() diff --git a/micropython/modules/hub75/README.md b/micropython/modules/hub75/README.md index 5ae6d2c6..27bd1a35 100644 --- a/micropython/modules/hub75/README.md +++ b/micropython/modules/hub75/README.md @@ -1,6 +1,10 @@ -# Interstate 75 +# HUB75 -The Interstate 75 library is intended for the Interstate 75 "HUB75" matrix panel driver board. +This library can be used with the Interstate 75 and 75W bypasses the use of picographics and can be used for situations where RAM is constrained or for custom display configurations like 2x2 HUB75 panels. + +For most cases we recommend using the picographics based module for Interstate 75 and 75W as it contains a lot of helper functions to draw text and shapes, further information on its usage can be found here: [Interstate75](../../modules_py/interstate75.md) + +The Interstate 75 library is intended for the Interstate 75 and Interstate 75 W "HUB75" matrix panel driver board. It can, in theory, be used with your own custom wiring, though custom pin assignments are not supported yet. @@ -9,20 +13,17 @@ It can, in theory, be used with your own custom wiring, though custom pin assign - [FM6216A Panels](#fm6216a-panels) - [Quick Reference](#quick-reference) - [Set A Pixel](#set-a-pixel) - - [Color](#color) - - [RGB](#rgb) - - [HSV](#hsv) - - [Update The Display](#update-the-display) + - [Clear The Display](#clear-the-display) ## 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. +The Hub 75 driver uses the PIO hardware on the RP2040. There are only two PIOs with four state machines each, and hub75 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. +Construct a new `Hub75` instance, specifying the width/height of the display and any additional options. ```python import hub75 @@ -35,7 +36,7 @@ matrix = hub75.Hub75(WIDTH, HEIGHT, stb_invert=True) Use `stb_invert` if you see a missing middle row corruption on the top row. -Start the matrix strip by calling `start`. This sets up DMA and PIO to drive your panel, pulling rows from the back buffer and refreshing as fast as it can. +Start the matrix strip by calling start. This sets up DMA and PIO to drive your panel, pulling rows from the back buffer and refreshing as fast as it can. ```python matrix.start() @@ -43,7 +44,7 @@ matrix.start() ### FM6216A Panels -Some panels - based on the FM6126A chips - require a couple of register settings in order for them to display anything at all. Interstate 75 will set these for you if you specify `panel_type=hub75.PANEL_FM6126A`. Eg: +Some panels - based on the FM6126A chips - require a couple of register settings for them to display anything at all. Interstate 75 will set these for you if you specify `panel_type=hub75.PANEL_FM6126A`. Eg: ```python import hub75 @@ -51,72 +52,26 @@ import hub75 WIDTH = 64 HEIGHT = 64 -matrix = hub75.Hub75(WIDTH, HEIGHT, panel_type=hub75.PANEL_FM6126A) +matrix = hub75.Hub75(WIDTH, HEIGHT,panel_type=hub75.PANEL_FM6126A) ``` + ## 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. - -#### 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 +You can set the colour of a pixel using RGB values. This will instantly update the pixel on the matrix display. Set the top left-most LED - `0, 0` - to Purple `255, 0, 255`: ```python -matrix.set_rgb(0, 0, 255, 0, 255) +matrix.set_pixel(0, 0, 255, 0, 255) ``` -#### HSV +### Clear the display -Set the top left-most LED - `0, o` - to Red `0.0`: +Calling `.clear()` will clear the whole contents of the display ```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`. +matrix.clear() +``` \ No newline at end of file diff --git a/micropython/modules/hub75/hub75.c b/micropython/modules/hub75/hub75.c index 39d49f80..bcb77cc4 100644 --- a/micropython/modules/hub75/hub75.c +++ b/micropython/modules/hub75/hub75.c @@ -3,35 +3,21 @@ /***** Methods *****/ MP_DEFINE_CONST_FUN_OBJ_1(Hub75___del___obj, Hub75___del__); -MP_DEFINE_CONST_FUN_OBJ_KW(Hub75_set_color_obj, 3, Hub75_set_color); -MP_DEFINE_CONST_FUN_OBJ_KW(Hub75_set_rgb_obj, 5, Hub75_set_rgb); -MP_DEFINE_CONST_FUN_OBJ_KW(Hub75_set_hsv_obj, 5, Hub75_set_hsv); -MP_DEFINE_CONST_FUN_OBJ_KW(Hub75_set_color_masked_obj, 5, Hub75_set_color_masked); -MP_DEFINE_CONST_FUN_OBJ_2(Hub75_set_all_color_obj, Hub75_set_all_color); -MP_DEFINE_CONST_FUN_OBJ_KW(Hub75_set_all_hsv_obj, 3, Hub75_set_all_hsv); +MP_DEFINE_CONST_FUN_OBJ_KW(Hub75_set_pixel_obj, 5, Hub75_set_pixel); 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_2(Hub75_update_obj, Hub75_update); -MP_DEFINE_CONST_FUN_OBJ_3(Hub75_color_obj, Hub75_color); -MP_DEFINE_CONST_FUN_OBJ_3(Hub75_color_hsv_obj, Hub75_color_hsv); /***** Binding of Methods *****/ STATIC const mp_rom_map_elem_t Hub75_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&Hub75___del___obj) }, - { MP_ROM_QSTR(MP_QSTR_set_color), MP_ROM_PTR(&Hub75_set_color_obj) }, - { MP_ROM_QSTR(MP_QSTR_set_rgb), MP_ROM_PTR(&Hub75_set_rgb_obj) }, - { MP_ROM_QSTR(MP_QSTR_set_hsv), MP_ROM_PTR(&Hub75_set_hsv_obj) }, - { MP_ROM_QSTR(MP_QSTR_set_color_masked), MP_ROM_PTR(&Hub75_set_color_masked_obj) }, - { MP_ROM_QSTR(MP_QSTR_set_all_hsv), MP_ROM_PTR(&Hub75_set_all_hsv_obj) }, - { MP_ROM_QSTR(MP_QSTR_set_all_color), MP_ROM_PTR(&Hub75_set_all_color_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_pixel), MP_ROM_PTR(&Hub75_set_pixel_obj) }, { MP_ROM_QSTR(MP_QSTR_clear), MP_ROM_PTR(&Hub75_clear_obj) }, { 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) }, + { MP_ROM_QSTR(MP_QSTR_update), MP_ROM_PTR(&Hub75_update_obj) }, }; STATIC MP_DEFINE_CONST_DICT(Hub75_locals_dict, Hub75_locals_dict_table); @@ -50,10 +36,8 @@ const mp_obj_type_t Hub75_type = { STATIC const mp_map_elem_t hub75_globals_table[] = { { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_hub75) }, { MP_OBJ_NEW_QSTR(MP_QSTR_Hub75), (mp_obj_t)&Hub75_type }, - { MP_OBJ_NEW_QSTR(MP_QSTR_PANEL_GENERIC), MP_ROM_INT(0) }, - { MP_OBJ_NEW_QSTR(MP_QSTR_PANEL_FM6126A), MP_ROM_INT(1) }, - { MP_ROM_QSTR(MP_QSTR_color), MP_ROM_PTR(&Hub75_color_obj) }, - { MP_ROM_QSTR(MP_QSTR_color_hsv), MP_ROM_PTR(&Hub75_color_hsv_obj) }, + { MP_ROM_QSTR(MP_QSTR_PANEL_GENERIC), MP_ROM_INT(0) }, + { MP_ROM_QSTR(MP_QSTR_PANEL_FM6126A), MP_ROM_INT(1) }, { MP_ROM_QSTR(MP_QSTR_BUTTON_A), MP_ROM_INT(14) }, { MP_ROM_QSTR(MP_QSTR_BUTTON_USER), MP_ROM_INT(23) }, { MP_ROM_QSTR(MP_QSTR_LED_R), MP_ROM_INT(16) }, diff --git a/micropython/modules/hub75/hub75.cpp b/micropython/modules/hub75/hub75.cpp index 13f7ce58..5172dc82 100644 --- a/micropython/modules/hub75/hub75.cpp +++ b/micropython/modules/hub75/hub75.cpp @@ -1,14 +1,28 @@ #include -#include "hub75.hpp" +#include "drivers/hub75/hub75.hpp" +#include "libraries/pico_graphics/pico_graphics.hpp" #include "pico/multicore.h" #include "micropython/modules/util.hpp" +using namespace pimoroni; + extern "C" { #include "hub75.h" #include "py/builtin.h" #include "py/mpthread.h" +#include "micropython/modules/pimoroni_i2c/pimoroni_i2c.h" + +typedef struct _ModPicoGraphics_obj_t { + mp_obj_base_t base; + PicoGraphics *graphics; + DisplayDriver *display; + void *spritedata; + void *buffer; + _PimoroniI2C_obj_t *i2c; + //mp_obj_t scanline_callback; // Not really feasible in MicroPython +} ModPicoGraphics_obj_t; typedef struct _mp_obj_float_t { mp_obj_base_t base; @@ -44,17 +58,12 @@ void Hub75_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind mp_print_str(print, " x "); mp_obj_print_helper(print, mp_obj_new_int(self->hub75->height), PRINT_REPR); - mp_print_str(print, ", addr = front: "); - mp_obj_print_helper(print, mp_obj_new_int((uint32_t)&self->hub75->front_buffer[0]), PRINT_REPR); - mp_print_str(print, " back: "); - mp_obj_print_helper(print, mp_obj_new_int((uint32_t)&self->hub75->back_buffer[0]), PRINT_REPR); - switch(self->hub75->panel_type) { case PANEL_GENERIC: mp_print_str(print, ", panel: generic "); break; case PANEL_FM6126A: - mp_print_str(print, ", panel: generic "); + mp_print_str(print, ", panel: fm6126a "); break; } @@ -101,11 +110,11 @@ mp_obj_t Hub75_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, c mp_buffer_info_t bufinfo; mp_get_buffer_raise(args[ARG_buffer].u_obj, &bufinfo, MP_BUFFER_RW); buffer = (Pixel *)bufinfo.buf; - if(bufinfo.len < (size_t)(width * height * 2 * sizeof(Pixel))) { + if(bufinfo.len < (size_t)(width * height * sizeof(Pixel))) { mp_raise_ValueError("Supplied buffer is too small!"); } } else { - buffer = m_new(Pixel, width * height * 2); + buffer = m_new(Pixel, width * height); } hub75_obj = m_new_obj_with_finaliser(_Hub75_obj_t); @@ -122,28 +131,15 @@ mp_obj_t Hub75_clear(mp_obj_t self_in) { return mp_const_none; } -mp_obj_t Hub75_flip(mp_obj_t self_in) { +mp_obj_t Hub75_update(mp_obj_t self_in, mp_obj_t graphics_in) { _Hub75_obj_t *self = MP_OBJ_TO_PTR2(self_in, _Hub75_obj_t); - self->hub75->flip(); + ModPicoGraphics_obj_t *picographics = MP_OBJ_TO_PTR2(graphics_in, ModPicoGraphics_obj_t); + + self->hub75->update(picographics->graphics); 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; -} - -void Hub75_display_update() { - if(hub75_obj) { - hub75_obj->hub75->start(nullptr); - } -} - mp_obj_t Hub75_start(mp_obj_t self_in) { _Hub75_obj_t *self = MP_OBJ_TO_PTR2(self_in, _Hub75_obj_t); self->hub75->start(dma_complete); @@ -156,70 +152,7 @@ mp_obj_t Hub75_stop(mp_obj_t self_in) { return mp_const_none; } -mp_obj_t Hub75_set_color_masked(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_self, ARG_x, ARG_y, ARG_mask, ARG_color }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_x, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_y, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_mask, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_color, MP_ARG_REQUIRED | MP_ARG_INT }, - }; - - // Parse args. - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - int x = args[ARG_x].u_int; - int y = args[ARG_y].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); - - for(auto py = 0; py < 32; py++) { - if(m & (1 << py)) { - self->hub75->set_color(x, py + y, c); - } - } - - return mp_const_none; -} - -mp_obj_t Hub75_color(mp_obj_t r, mp_obj_t g, mp_obj_t b) { - return mp_obj_new_int(Pixel(mp_obj_get_int(r), mp_obj_get_int(g), mp_obj_get_int(b)).color); -} - -mp_obj_t Hub75_color_hsv(mp_obj_t h, mp_obj_t s, mp_obj_t v) { - return mp_obj_new_int(hsv_to_rgb(mp_obj_get_float(h), mp_obj_get_float(s), mp_obj_get_float(v)).color); -} - -mp_obj_t Hub75_set_color(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_self, ARG_x, ARG_y, ARG_color }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_x, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_y, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_color, MP_ARG_REQUIRED | MP_ARG_INT }, - }; - - // Parse args. - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - int x = args[ARG_x].u_int; - int y = args[ARG_y].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); - - return mp_const_none; -} - -mp_obj_t Hub75_set_rgb(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { +mp_obj_t Hub75_set_pixel(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { enum { ARG_self, ARG_x, ARG_y, ARG_r, ARG_g, ARG_b }; static const mp_arg_t allowed_args[] = { { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, @@ -241,77 +174,7 @@ mp_obj_t Hub75_set_rgb(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_arg int b = args[ARG_b].u_int; _Hub75_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, _Hub75_obj_t); - self->hub75->set_rgb(x, y, r, g, b); - - return mp_const_none; -} - -mp_obj_t Hub75_set_hsv(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_self, ARG_x, ARG_y, ARG_h, ARG_s, ARG_v }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_x, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_y, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_h, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_s, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_v, MP_ARG_REQUIRED | MP_ARG_OBJ } - }; - - // Parse args. - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - int x = args[ARG_x].u_int; - int y = args[ARG_y].u_int; - float h = mp_obj_get_float(args[ARG_h].u_obj); - float s = mp_obj_get_float(args[ARG_s].u_obj); - float v = mp_obj_get_float(args[ARG_v].u_obj); - - _Hub75_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, _Hub75_obj_t); - self->hub75->set_hsv(x, y, h, s, v); - - return mp_const_none; -} - -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); - - 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++) { - self->hub75->set_color(x, y, c); - } - } - - return mp_const_none; -} - -mp_obj_t Hub75_set_all_hsv(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_self, ARG_h, ARG_s, ARG_v }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_h, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_s, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_v, MP_ARG_REQUIRED | MP_ARG_OBJ } - }; - - // Parse args. - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - float h = mp_obj_get_float(args[ARG_h].u_obj); - float s = mp_obj_get_float(args[ARG_s].u_obj); - float v = mp_obj_get_float(args[ARG_v].u_obj); - - _Hub75_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, _Hub75_obj_t); - - for (auto x = 0u; x < self->hub75->width; x++) { - for (auto y = 0u; y < self->hub75->height; y++) { - self->hub75->set_hsv(x, y, h, s, v); - } - } + self->hub75->set_pixel(x, y, r, g, b); return mp_const_none; } diff --git a/micropython/modules/hub75/hub75.h b/micropython/modules/hub75/hub75.h index 61637d6b..22666d74 100644 --- a/micropython/modules/hub75/hub75.h +++ b/micropython/modules/hub75/hub75.h @@ -11,14 +11,6 @@ extern mp_obj_t Hub75_make_new(const mp_obj_type_t *type, size_t n_args, size_t extern mp_obj_t Hub75___del__(mp_obj_t self_in); extern mp_obj_t Hub75_start(mp_obj_t self_in); extern mp_obj_t Hub75_stop(mp_obj_t self_in); -extern mp_obj_t Hub75_set_color(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); -extern mp_obj_t Hub75_set_rgb(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); -extern mp_obj_t Hub75_set_hsv(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); -extern mp_obj_t Hub75_set_color_masked(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); -extern mp_obj_t Hub75_color(mp_obj_t r, mp_obj_t g, mp_obj_t b); -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_set_pixel(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); extern mp_obj_t Hub75_clear(mp_obj_t self_in); -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 +extern mp_obj_t Hub75_update(mp_obj_t self_in, mp_obj_t graphics_in); \ No newline at end of file diff --git a/micropython/modules/hub75/micropython.cmake b/micropython/modules/hub75/micropython.cmake index d43a05f9..76544776 100644 --- a/micropython/modules/hub75/micropython.cmake +++ b/micropython/modules/hub75/micropython.cmake @@ -1,21 +1,23 @@ -add_library(usermod_hub75 INTERFACE) +set(MOD_NAME hub75) +string(TOUPPER ${MOD_NAME} MOD_NAME_UPPER) +add_library(usermod_${MOD_NAME} INTERFACE) -target_sources(usermod_hub75 INTERFACE - ${CMAKE_CURRENT_LIST_DIR}/hub75.c - ${CMAKE_CURRENT_LIST_DIR}/hub75.cpp +target_sources(usermod_${MOD_NAME} INTERFACE + ${CMAKE_CURRENT_LIST_DIR}/${MOD_NAME}.c + ${CMAKE_CURRENT_LIST_DIR}/${MOD_NAME}.cpp ${CMAKE_CURRENT_LIST_DIR}/../../../drivers/hub75/hub75.cpp ) -target_include_directories(usermod_hub75 INTERFACE +target_include_directories(usermod_${MOD_NAME} INTERFACE ${CMAKE_CURRENT_LIST_DIR} - ${CMAKE_CURRENT_LIST_DIR}/../../../drivers/hub75/ + ${CMAKE_CURRENT_LIST_DIR}/../../../libraries/pico_graphics/ ) -target_compile_definitions(usermod_hub75 INTERFACE +target_compile_definitions(usermod_${MOD_NAME} INTERFACE MODULE_HUB75_ENABLED=1 ) -target_link_libraries(usermod INTERFACE usermod_hub75) +target_link_libraries(usermod INTERFACE usermod_${MOD_NAME}) set_source_files_properties( ${CMAKE_CURRENT_LIST_DIR}/hub75.c @@ -23,4 +25,4 @@ set_source_files_properties( "-Wno-discarded-qualifiers -Wno-implicit-int" ) -pico_generate_pio_header(usermod_hub75 ${CMAKE_CURRENT_LIST_DIR}/../../../drivers/hub75/hub75.pio) \ No newline at end of file +pico_generate_pio_header(usermod_${MOD_NAME} ${CMAKE_CURRENT_LIST_DIR}/../../../drivers/hub75/hub75.pio) \ No newline at end of file diff --git a/micropython/modules/picographics/README.md b/micropython/modules/picographics/README.md index 72b699f6..b8cf50e1 100644 --- a/micropython/modules/picographics/README.md +++ b/micropython/modules/picographics/README.md @@ -65,6 +65,22 @@ Bear in mind that MicroPython has only 192K of RAM available- a 320x240 pixel di * Inky Frame - 600x447 7-colour e-ink - `DISPLAY_INKY_FRAME` * Pico GFX Pack - 128x64 mono LCD Matrix - `DISPLAY_GFX_PACK` * Galactic Unicorn - 53x11 LED Matrix - `DISPLAY_GALACTIC_UNICORN` +* Interstate75 and 75W - HUB75 Matrix driver - `DISPLAY_INTERSTATE75_SIZEOFMATRIX` please read below! + +#### Interstate75 and Interstate75W Display modes + +Both the Interstate75 and Interstate75W support lots of different sizes of HUB75 matrix displays. + +The available display ssettings are listed here: + +* 32 x 32 Matrix - `DISPLAY_INTERSTATE75_32X32` +* 64 x 32 Matrix - `DISPLAY_INTERSTATE75_64X32` +* 96 x 32 Matrix - `DISPLAY_INTERSTATE75_96X32` +* 128 x 32 Matrix - `DISPLAY_INTERSTATE75_128X32` +* 64 x 64 Matrix - `DISPLAY_INTERSTATE75_64X64` +* 128 x 64 Matrix - `DISPLAY_INTERSTATE75_128X64` +* 192 x 64 Matrix - `DISPLAY_INTERSTATE75_192X64` +* 256 x 64 Matrix - `DISPLAY_INTERSTATE75_256X64` ### Supported Graphics Modes (Pen Type) diff --git a/micropython/modules/picographics/picographics.c b/micropython/modules/picographics/picographics.c index 7341aff9..f1c0bc81 100644 --- a/micropython/modules/picographics/picographics.c +++ b/micropython/modules/picographics/picographics.c @@ -126,7 +126,16 @@ STATIC const mp_map_elem_t picographics_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_DISPLAY_INKY_FRAME), MP_ROM_INT(DISPLAY_INKY_FRAME) }, { MP_ROM_QSTR(MP_QSTR_DISPLAY_INKY_FRAME_4), MP_ROM_INT(DISPLAY_INKY_FRAME_4) }, { MP_ROM_QSTR(MP_QSTR_DISPLAY_GALACTIC_UNICORN), MP_ROM_INT(DISPLAY_GALACTIC_UNICORN) }, - { MP_ROM_QSTR(MP_QSTR_DISPLAY_GFX_PACK), MP_ROM_INT(DISPLAY_GFX_PACK) }, + { MP_ROM_QSTR(MP_QSTR_DISPLAY_GFX_PACK), MP_ROM_INT(DISPLAY_GFX_PACK) }, + { MP_ROM_QSTR(MP_QSTR_DISPLAY_INTERSTATE75_32X32), MP_ROM_INT(DISPLAY_INTERSTATE75_32X32) }, + { MP_ROM_QSTR(MP_QSTR_DISPLAY_INTERSTATE75_64X32), MP_ROM_INT(DISPLAY_INTERSTATE75_64X32) }, + { MP_ROM_QSTR(MP_QSTR_DISPLAY_INTERSTATE75_96X32), MP_ROM_INT(DISPLAY_INTERSTATE75_96X32) }, + { MP_ROM_QSTR(MP_QSTR_DISPLAY_INTERSTATE75_128X32), MP_ROM_INT(DISPLAY_INTERSTATE75_128X32) }, + { MP_ROM_QSTR(MP_QSTR_DISPLAY_INTERSTATE75_64X64), MP_ROM_INT(DISPLAY_INTERSTATE75_64X64) }, + { MP_ROM_QSTR(MP_QSTR_DISPLAY_INTERSTATE75_128X64), MP_ROM_INT(DISPLAY_INTERSTATE75_128X64) }, + { MP_ROM_QSTR(MP_QSTR_DISPLAY_INTERSTATE75_192X64), MP_ROM_INT(DISPLAY_INTERSTATE75_192X64) }, + { MP_ROM_QSTR(MP_QSTR_DISPLAY_INTERSTATE75_256X64), MP_ROM_INT(DISPLAY_INTERSTATE75_256X64) }, + { MP_ROM_QSTR(MP_QSTR_PEN_1BIT), MP_ROM_INT(PEN_1BIT) }, { MP_ROM_QSTR(MP_QSTR_PEN_P4), MP_ROM_INT(PEN_P4) }, diff --git a/micropython/modules/picographics/picographics.cpp b/micropython/modules/picographics/picographics.cpp index 9d3d6dc1..9095709d 100644 --- a/micropython/modules/picographics/picographics.cpp +++ b/micropython/modules/picographics/picographics.cpp @@ -128,6 +128,70 @@ bool get_display_settings(PicoGraphicsDisplay display, int &width, int &height, if(rotate == -1) rotate = (int)Rotation::ROTATE_0; if(pen_type == -1) pen_type = PEN_1BIT; break; + case DISPLAY_INTERSTATE75_32X32: + width = 32; + height = 32; + bus_type = BUS_PIO; + // Portrait to match labelling + if(rotate == -1) rotate = (int)Rotation::ROTATE_0; + if(pen_type == -1) pen_type = PEN_RGB888; + break; + case DISPLAY_INTERSTATE75_64X32: + width = 64; + height = 32; + bus_type = BUS_PIO; + // Portrait to match labelling + if(rotate == -1) rotate = (int)Rotation::ROTATE_0; + if(pen_type == -1) pen_type = PEN_RGB888; + break; + case DISPLAY_INTERSTATE75_96X32: + width = 96; + height = 32; + bus_type = BUS_PIO; + // Portrait to match labelling + if(rotate == -1) rotate = (int)Rotation::ROTATE_0; + if(pen_type == -1) pen_type = PEN_RGB888; + break; + case DISPLAY_INTERSTATE75_128X32: + width = 128; + height = 32; + bus_type = BUS_PIO; + // Portrait to match labelling + if(rotate == -1) rotate = (int)Rotation::ROTATE_0; + if(pen_type == -1) pen_type = PEN_RGB888; + break; + case DISPLAY_INTERSTATE75_64X64: + width = 64; + height = 64; + bus_type = BUS_PIO; + // Portrait to match labelling + if(rotate == -1) rotate = (int)Rotation::ROTATE_0; + if(pen_type == -1) pen_type = PEN_RGB888; + break; + case DISPLAY_INTERSTATE75_128X64: + width = 128; + height = 64; + bus_type = BUS_PIO; + // Portrait to match labelling + if(rotate == -1) rotate = (int)Rotation::ROTATE_0; + if(pen_type == -1) pen_type = PEN_RGB888; + break; + case DISPLAY_INTERSTATE75_192X64: + width = 192; + height = 64; + bus_type = BUS_PIO; + // Portrait to match labelling + if(rotate == -1) rotate = (int)Rotation::ROTATE_0; + if(pen_type == -1) pen_type = PEN_RGB888; + break; + case DISPLAY_INTERSTATE75_256X64: + width = 256; + height = 64; + bus_type = BUS_PIO; + // Portrait to match labelling + if(rotate == -1) rotate = (int)Rotation::ROTATE_0; + if(pen_type == -1) pen_type = PEN_RGB888; + break; default: return false; } @@ -249,6 +313,9 @@ mp_obj_t ModPicoGraphics_make_new(const mp_obj_type_t *type, size_t n_args, size } else if (display == DISPLAY_GFX_PACK) { self->display = m_new_class(ST7567, width, height, spi_bus); + } else if (display == DISPLAY_INTERSTATE75_32X32 || display == DISPLAY_INTERSTATE75_64X64 || display == DISPLAY_INTERSTATE75_64X32) { + self->display = m_new_class(DisplayDriver, width, height, (Rotation)rotate); + } else { self->display = m_new_class(ST7789, width, height, (Rotation)rotate, round, spi_bus); } diff --git a/micropython/modules/picographics/picographics.h b/micropython/modules/picographics/picographics.h index 4df440e2..9c1a9c15 100644 --- a/micropython/modules/picographics/picographics.h +++ b/micropython/modules/picographics/picographics.h @@ -15,7 +15,15 @@ enum PicoGraphicsDisplay { DISPLAY_INKY_FRAME, DISPLAY_INKY_FRAME_4, DISPLAY_GALACTIC_UNICORN, - DISPLAY_GFX_PACK + DISPLAY_GFX_PACK, + DISPLAY_INTERSTATE75_32X32, + DISPLAY_INTERSTATE75_64X32, + DISPLAY_INTERSTATE75_96X32, + DISPLAY_INTERSTATE75_128X32, + DISPLAY_INTERSTATE75_64X64, + DISPLAY_INTERSTATE75_128X64, + DISPLAY_INTERSTATE75_192X64, + DISPLAY_INTERSTATE75_256X64, }; enum PicoGraphicsPenType { diff --git a/micropython/modules_py/interstate75.md b/micropython/modules_py/interstate75.md new file mode 100644 index 00000000..68ffc2b5 --- /dev/null +++ b/micropython/modules_py/interstate75.md @@ -0,0 +1,89 @@ +# Interstate75 (MicroPython) + +This library offers convenient functions for interacting with [Interstate75](https://shop.pimoroni.com/products/interstate-75) and [Interstate75W](https://shop.pimoroni.com/products/interstate-75-w) - Interstate75 and Interstate75W offer a convenient way and 2 input buttons for all your display and control needs. + +## Table of Content +- [Table of Content](#table-of-content) +- [Interstate75 Module](#interstate75-class) + - [Switches](#switches) + - [RGB LED](#rgb-led) + + + +## Interstate75 Class + +The `Interstate75` class deals with RGB LED and buttons on the Interstate75 and 75W. To create one, import the `interstate75` module, then define a new `board` variable. + +This is where you define the HUB75 matrix display size that you wish to use by defining `display=` + +```python +DISPLAY_INTERSTATE75_32X32 +DISPLAY_INTERSTATE75_64X32 +DISPLAY_INTERSTATE75_96X32 +DISPLAY_INTERSTATE75_128X32 +DISPLAY_INTERSTATE75_64X64 +DISPLAY_INTERSTATE75_128X64 +DISPLAY_INTERSTATE75_192X64 +DISPLAY_INTERSTATE75_256X64 +``` + + +```python +import interstate75 + +display = interstate75.DISPLAY_INTERSTATE75_32X32 + +board = interstate75.Interstate75(display=display) +``` + +From here, all features can be accessed by calling functions on `board`. In addition, when using Qwiic / Stemma QT devices, the I2C channel to use can be accessed with `board.i2c`. + +### Switches + +Interstate75 and 75W have two switches in the front of the board. To read one of the switches, call `.switch_pressed(switch)`, where `switch` is a value from `0` to `.NUM_SWITCHES - 1`. This returns `True` when the specified switch is pressed, and `False` otherwise. + +To read a specific input, the `interstate75` module contains these handy constants: + +* `SWITCH_A` = `0` +* `SWITCH_B` = `1` + +```python +if board.switch_pressed(SWITCH_A): + # Do something interesting here! + +if board.switch_pressed(SWITCH_B): + # Do something else even more interesting here! +``` + + +### RGB LED + +The Interstate has an RGB LED. This is accessed via the following method. + + +`.set_led(r, g, b)` + +Where r, g, b are values between 0 and 255 + + +example: + +```python +board.set_led(255, 0, 0) # Makes the LED Red +board.set_led(0, 255, 0) # Makes the LED Blue +board.set_led(0, 0, 255) # Makes the LED Green +``` + +## Display + +The display is all handled by our custom picographics drivers they can be accessed via `.display`. + +example: + +```python +display = board.display +display.text("Hello World!", 0, 0) +display.line(0, 0, 128, 64) +board.update() # Update display with the above items +``` +All the picographics functions can be found [Here](../modules/picographics/README.md) \ No newline at end of file diff --git a/micropython/modules_py/interstate75.py b/micropython/modules_py/interstate75.py new file mode 100644 index 00000000..94d13a12 --- /dev/null +++ b/micropython/modules_py/interstate75.py @@ -0,0 +1,62 @@ +from pimoroni import RGBLED, Button +from picographics import PicoGraphics, DISPLAY_INTERSTATE75_32X32, DISPLAY_INTERSTATE75_64X32, DISPLAY_INTERSTATE75_96X32, DISPLAY_INTERSTATE75_128X32, DISPLAY_INTERSTATE75_64X64, DISPLAY_INTERSTATE75_128X64, DISPLAY_INTERSTATE75_192X64, DISPLAY_INTERSTATE75_256X64 +from pimoroni_i2c import PimoroniI2C +import hub75 + +# Index Constants +SWITCH_A = 0 +SWITCH_B = 1 + + +class Interstate75: + I2C_SDA_PIN = 20 + I2C_SCL_PIN = 21 + SWITCH_PINS = (14, 15) + LED_R_PIN = 16 + LED_G_PIN = 17 + LED_B_PIN = 18 + + # Display Types + DISPLAY_INTERSTATE75_32X32 = DISPLAY_INTERSTATE75_32X32 + DISPLAY_INTERSTATE75_64X32 = DISPLAY_INTERSTATE75_64X32 + DISPLAY_INTERSTATE75_96X32 = DISPLAY_INTERSTATE75_96X32 + DISPLAY_INTERSTATE75_128X32 = DISPLAY_INTERSTATE75_128X32 + DISPLAY_INTERSTATE75_64X64 = DISPLAY_INTERSTATE75_64X64 + DISPLAY_INTERSTATE75_128X64 = DISPLAY_INTERSTATE75_128X64 + DISPLAY_INTERSTATE75_192X64 = DISPLAY_INTERSTATE75_192X64 + DISPLAY_INTERSTATE75_256X64 = DISPLAY_INTERSTATE75_256X64 + + PANEL_GENERIC = hub75.PANEL_GENERIC + PANEL_FM6126A = hub75.PANEL_FM6126A + + # Count Constants + NUM_SWITCHES = 2 + + def __init__(self, display, panel_type=hub75.PANEL_GENERIC, stb_invert=False): + self.display = PicoGraphics(display=display) + self.width, self.height = self.display.get_bounds() + self.hub75 = hub75.Hub75(self.width, self.height, panel_type=panel_type, stb_invert=stb_invert) + self.hub75.start() + + # Set up the user switches + self.__switches = [] + for i in range(self.NUM_SWITCHES): + self.__switches.append(Button(self.SWITCH_PINS[i])) + + self.__rgb = RGBLED(Interstate75.LED_R_PIN, Interstate75.LED_G_PIN, Interstate75.LED_B_PIN, invert=True) + + # Set up the i2c for Qw/st and Breakout Garden + self.i2c = PimoroniI2C(self.I2C_SDA_PIN, self.I2C_SCL_PIN, 100000) + + def update(self, buffer=None): + if buffer is None: + buffer = self.display + self.hub75.update(buffer) + + def switch_pressed(self, switch): + if switch < 0 or switch >= self.NUM_SWITCHES: + raise ValueError("switch out of range. Expected SWITCH_A (0), SWITCH_B (1)") + return self.__switches[switch].is_pressed + + def set_led(self, r, g, b): + self.__rgb.set_rgb(r, g, b) diff --git a/micropython/modules_py/modules_py.cmake b/micropython/modules_py/modules_py.cmake index d7fd2165..11cf0d01 100644 --- a/micropython/modules_py/modules_py.cmake +++ b/micropython/modules_py/modules_py.cmake @@ -19,6 +19,7 @@ target_link_libraries(usermod INTERFACE usermod_modules_py) #copy_module(usermod_modules_py ${CMAKE_CURRENT_LIST_DIR}/picosystem.py picosystem) copy_module(usermod_modules_py ${CMAKE_CURRENT_LIST_DIR}/pimoroni.py pimoroni) copy_module(usermod_modules_py ${CMAKE_CURRENT_LIST_DIR}/gfx_pack.py gfx_pack) +copy_module(usermod_modules_py ${CMAKE_CURRENT_LIST_DIR}/interstate75.py interstate75) if(PICO_BOARD STREQUAL "pico_w") copy_module(usermod_modules_py ${CMAKE_CURRENT_LIST_DIR}/automation.py automation) copy_module(usermod_modules_py ${CMAKE_CURRENT_LIST_DIR}/inventor.py inventor)