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 <christopher.parrott2@gmail.com>
This commit is contained in:
Gee Bartlett 2022-12-16 20:53:16 +00:00 committed by GitHub
parent a9d9c1089e
commit 104c819412
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
60 changed files with 2249 additions and 888 deletions

View File

@ -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)

View File

@ -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)

View File

@ -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) {
@ -314,3 +234,33 @@ void Hub75::dma_complete() {
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++;
}
}
}
}
}

View File

@ -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();
void update(PicoGraphics *graphics);
};
}

View File

@ -0,0 +1 @@
include(${CMAKE_CURRENT_LIST_DIR}/hub75.cmake)

View File

@ -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)

View File

@ -0,0 +1,316 @@
#include <cstring>
#include <algorithm>
#include <cmath>
#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);
}
}

View File

@ -0,0 +1,129 @@
#include <stdint.h>
#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();
};

View File

@ -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;
}
%}

View File

@ -1,3 +1,5 @@
include(interstate75_hello_world.cmake)
include(interstate75_pio_dma.cmake)
include(interstate75_scrolling_text.cmake)
include(interstate75_rainbow.cmake)
include(interstate75_balls_demo.cmake)
include(interstate75_rainbow_text.cmake)
include(interstate75_fire_effect.cmake)
include(interstate75_example.cmake)

View File

@ -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
)

View File

@ -0,0 +1,124 @@
#include <math.h>
#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<pt> 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;
}

View File

@ -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
)

View File

@ -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);
}
}

View File

@ -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
)

View File

@ -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;
}

View File

@ -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
)

View File

@ -0,0 +1,120 @@
#include <math.h>
#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;
}

View File

@ -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
)

View File

@ -0,0 +1,117 @@
#include <math.h>
#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;
}

View File

@ -0,0 +1,3 @@
include(interstate75_hello_world.cmake)
include(interstate75_pio_dma.cmake)
include(interstate75_scrolling_text.cmake)

View File

@ -38,3 +38,4 @@ add_subdirectory(jpegdec)
add_subdirectory(inky_frame)
add_subdirectory(galactic_unicorn)
add_subdirectory(gfx_pack)
add_subdirectory(interstate75)

View File

@ -0,0 +1 @@
include(interstate75.cmake)

View File

@ -0,0 +1,84 @@
# Interstate 75 (C++) <!-- omit in toc -->
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).

View File

@ -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)

View File

@ -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;
};
}

View File

@ -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:

View File

@ -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)

View File

@ -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()

View File

@ -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)

View File

@ -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()

View File

@ -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], # ""
]

View File

@ -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], # ""
]

View File

@ -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))

View File

@ -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)

View File

@ -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()

View File

@ -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)

View File

@ -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)

View File

@ -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))

View File

@ -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))

View File

@ -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()

View File

@ -1,6 +1,10 @@
# Interstate 75 <!-- omit in toc -->
# HUB75 <!-- omit in toc -->
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
@ -54,69 +55,23 @@ HEIGHT = 64
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)
matrix.clear()
```
### 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`.

View File

@ -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) },

View File

@ -1,14 +1,28 @@
#include <cstdio>
#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;
}

View File

@ -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);
extern mp_obj_t Hub75_update(mp_obj_t self_in, mp_obj_t graphics_in);

View File

@ -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)
pico_generate_pio_header(usermod_${MOD_NAME} ${CMAKE_CURRENT_LIST_DIR}/../../../drivers/hub75/hub75.pio)

View File

@ -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)

View File

@ -127,6 +127,15 @@ STATIC const mp_map_elem_t picographics_globals_table[] = {
{ 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_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) },

View File

@ -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);
}

View File

@ -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 {

View File

@ -0,0 +1,89 @@
# Interstate75 (MicroPython) <!-- omit in toc -->
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)

View File

@ -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)

View File

@ -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)