Merge pull request #193 from pimoroni/pure-cpp-i75

Pure C++ HUB75 example
pull/231/head
Philip Howard 2021-12-08 15:25:53 +00:00 zatwierdzone przez GitHub
commit c322717eb2
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
29 zmienionych plików z 2297 dodań i 0 usunięć

Wyświetl plik

@ -23,3 +23,4 @@ add_subdirectory(button)
add_subdirectory(plasma)
add_subdirectory(rgbled)
add_subdirectory(icp10125)
add_subdirectory(hub75)

Wyświetl plik

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

Wyświetl plik

@ -0,0 +1,11 @@
add_library(hub75 INTERFACE)
target_sources(hub75 INTERFACE
${CMAKE_CURRENT_LIST_DIR}/hub75.cpp)
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)
pico_generate_pio_header(hub75 ${CMAKE_CURRENT_LIST_DIR}/hub75.pio)

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

@ -37,3 +37,4 @@ add_subdirectory(pico_audio)
add_subdirectory(pico_wireless)
add_subdirectory(plasma2040)
add_subdirectory(interstate75)

Wyświetl plik

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

Wyświetl plik

@ -0,0 +1,100 @@
const uint8_t letter_width = 10;
const uint8_t letter_height = 14;
uint32_t font[][letter_width] = {
{0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, // " "
{0x0ffc, 0x0a04, 0x0ffc, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, // "!"
{0x007c, 0x0044, 0x007c, 0x0044, 0x007c, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, // """
{0x03f0, 0x02d0, 0x0edc, 0x0804, 0x0edc, 0x0edc, 0x0804, 0x0edc, 0x02d0, 0x03f0}, // "//"
{0x0ef8, 0x0b8c, 0x1b76, 0x1002, 0x1b76, 0x0cd4, 0x079c, 0x0000, 0x0000, 0x0000}, // "$"
{0x0038, 0x006c, 0x0f54, 0x09ec, 0x0e78, 0x079c, 0x0de4, 0x0abc, 0x0d80, 0x0700}, // "%"
{0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, // "&"
{0x007c, 0x0044, 0x007c, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, // "'"
{0x03f0, 0x0e1c, 0x19e6, 0x173a, 0x1c0e, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, // "("
{0x1c0e, 0x173a, 0x19e6, 0x0e1c, 0x03f0, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, // ")"
{0x00fc, 0x00b4, 0x00cc, 0x00cc, 0x00b4, 0x00fc, 0x0000, 0x0000, 0x0000, 0x0000}, // "*"
{0x01c0, 0x0140, 0x0770, 0x0410, 0x0770, 0x0140, 0x01c0, 0x0000, 0x0000, 0x0000}, // "+"
{0x1c00, 0x1700, 0x1900, 0x0f00, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, // ","
{0x01c0, 0x0140, 0x0140, 0x0140, 0x0140, 0x0140, 0x01c0, 0x0000, 0x0000, 0x0000}, // "-"
{0x0e00, 0x0a00, 0x0e00, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, // "."
{0x1e00, 0x1380, 0x1ce0, 0x0738, 0x01ce, 0x0072, 0x001e, 0x0000, 0x0000, 0x0000}, // "/"
{0x07f8, 0x0c0c, 0x0bf4, 0x0a14, 0x0bf4, 0x0c0c, 0x07f8, 0x0000, 0x0000, 0x0000}, // "0"
{0x0e70, 0x0a58, 0x0bec, 0x0804, 0x0bfc, 0x0a00, 0x0e00, 0x0000, 0x0000, 0x0000}, // "1"
{0x0e38, 0x0b2c, 0x09b4, 0x0ad4, 0x0b74, 0x0b8c, 0x0ef8, 0x0000, 0x0000, 0x0000}, // "2"
{0x0738, 0x0d2c, 0x0b34, 0x0bf4, 0x0b34, 0x0ccc, 0x07f8, 0x0000, 0x0000, 0x0000}, // "3"
{0x03c0, 0x0270, 0x0298, 0x0eec, 0x0804, 0x0efc, 0x0380, 0x0000, 0x0000, 0x0000}, // "4"
{0x0efc, 0x0a84, 0x0ab4, 0x0ab4, 0x0bb4, 0x0c74, 0x07dc, 0x0000, 0x0000, 0x0000}, // "5"
{0x07f8, 0x0c0c, 0x0bb4, 0x0ab4, 0x0bb4, 0x0c74, 0x07dc, 0x0000, 0x0000, 0x0000}, // "6"
{0x001c, 0x0014, 0x0f94, 0x08f4, 0x0f34, 0x01c4, 0x007c, 0x0000, 0x0000, 0x0000}, // "7"
{0x07f8, 0x0c4c, 0x0bb4, 0x0ab4, 0x0bb4, 0x0c4c, 0x07f8, 0x0000, 0x0000, 0x0000}, // "8"
{0x0ef8, 0x0b8c, 0x0b74, 0x0b54, 0x0b74, 0x0c0c, 0x07f8, 0x0000, 0x0000, 0x0000}, // "9"
{0x0e1c, 0x0a14, 0x0e1c, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, // ":"
{0x1c00, 0x171c, 0x1914, 0x0f1c, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, // ";"
{0x0380, 0x06c0, 0x0d60, 0x0ba0, 0x0ee0, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, // "<"
{0x0ee0, 0x0aa0, 0x0aa0, 0x0aa0, 0x0aa0, 0x0aa0, 0x0aa0, 0x0ee0, 0x0000, 0x0000}, // "="
{0x0ee0, 0x0ba0, 0x0d60, 0x06c0, 0x0380, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, // ">"
{0x0000, 0x001c, 0x0fd4, 0x0a74, 0x0fb4, 0x00cc, 0x0078, 0x0000, 0x0000, 0x0000}, // "?"
{0x0ff0, 0x1818, 0x37ec, 0x2c74, 0x2bb4, 0x2bb4, 0x3c0c, 0x07f8, 0x0000, 0x0000}, // "@"
{0x0f80, 0x08f0, 0x0f1c, 0x0164, 0x0f1c, 0x08f0, 0x0f80, 0x0000, 0x0000, 0x0000}, // "A"
{0x0ffc, 0x0804, 0x0bb4, 0x0bb4, 0x0c4c, 0x07f8, 0x0000, 0x0000, 0x0000, 0x0000}, // "B"
{0x07f8, 0x0c0c, 0x0bf4, 0x0a14, 0x0a14, 0x0e1c, 0x0000, 0x0000, 0x0000, 0x0000}, // "C"
{0x0ffc, 0x0804, 0x0bf4, 0x0bf4, 0x0c0c, 0x07f8, 0x0000, 0x0000, 0x0000, 0x0000}, // "D"
{0x0ffc, 0x0804, 0x0bb4, 0x0ab4, 0x0ab4, 0x0efc, 0x0000, 0x0000, 0x0000, 0x0000}, // "E"
{0x0ffc, 0x0804, 0x0fb4, 0x00b4, 0x00f4, 0x001c, 0x0000, 0x0000, 0x0000, 0x0000}, // "F"
{0x07f8, 0x0c0c, 0x0bf4, 0x0bd4, 0x0b54, 0x0c5c, 0x07c0, 0x0000, 0x0000, 0x0000}, // "G"
{0x0ffc, 0x0804, 0x0fbc, 0x00a0, 0x0fbc, 0x0804, 0x0ffc, 0x0000, 0x0000, 0x0000}, // "H"
{0x0e1c, 0x0a14, 0x0bf4, 0x0804, 0x0bf4, 0x0a14, 0x0e1c, 0x0000, 0x0000, 0x0000}, // "I"
{0x0e1c, 0x0a14, 0x0bf4, 0x0c04, 0x07f4, 0x0014, 0x001c, 0x0000, 0x0000, 0x0000}, // "J"
{0x0ffc, 0x0804, 0x0fbc, 0x0e5c, 0x09e4, 0x0f3c, 0x0000, 0x0000, 0x0000, 0x0000}, // "K"
{0x0ffc, 0x0804, 0x0bfc, 0x0a00, 0x0a00, 0x0e00, 0x0000, 0x0000, 0x0000, 0x0000}, // "L"
{0x0ffc, 0x0804, 0x0fec, 0x00d8, 0x00b0, 0x00b0, 0x00d8, 0x0fec, 0x0804, 0x0ffc}, // "M"
{0x0ffc, 0x0804, 0x0fcc, 0x0738, 0x0cfc, 0x0804, 0x0ffc, 0x0000, 0x0000, 0x0000}, // "N"
{0x07f8, 0x0c0c, 0x0bf4, 0x0a14, 0x0a14, 0x0bf4, 0x0c0c, 0x07f8, 0x0000, 0x0000}, // "O"
{0x0ffc, 0x0804, 0x0f74, 0x0174, 0x018c, 0x00f8, 0x0000, 0x0000, 0x0000, 0x0000}, // "P"
{0x07f8, 0x0c0c, 0x0bf4, 0x0a14, 0x0a14, 0x1bf4, 0x140c, 0x17f8, 0x1c00, 0x0000}, // "Q"
{0x0ffc, 0x0804, 0x0f74, 0x0e74, 0x098c, 0x0ff8, 0x0000, 0x0000, 0x0000, 0x0000}, // "R"
{0x0ef8, 0x0b8c, 0x0b74, 0x0b54, 0x0cd4, 0x079c, 0x0000, 0x0000, 0x0000, 0x0000}, // "S"
{0x001c, 0x0014, 0x0ff4, 0x0804, 0x0ff4, 0x0014, 0x001c, 0x0000, 0x0000, 0x0000}, // "T"
{0x07fc, 0x0c04, 0x0bfc, 0x0a00, 0x0bfc, 0x0c04, 0x07fc, 0x0000, 0x0000, 0x0000}, // "U"
{0x01fc, 0x0704, 0x0cfc, 0x0b80, 0x0cfc, 0x0704, 0x01fc, 0x0000, 0x0000, 0x0000}, // "V"
{0x01fc, 0x0704, 0x0cfc, 0x0bc0, 0x0c40, 0x0bc0, 0x0cfc, 0x0704, 0x01fc, 0x0000}, // "W"
{0x0f3c, 0x09e4, 0x0edc, 0x0330, 0x0edc, 0x09e4, 0x0f3c, 0x0000, 0x0000, 0x0000}, // "X"
{0x003c, 0x00e4, 0x0f9c, 0x0870, 0x0f9c, 0x00e4, 0x003c, 0x0000, 0x0000, 0x0000}, // "Y"
{0x0f1c, 0x0994, 0x0af4, 0x0b34, 0x0bd4, 0x0a64, 0x0e3c, 0x0000, 0x0000, 0x0000}, // "Z"
{0x0ffc, 0x0804, 0x0bf4, 0x0a14, 0x0e1c, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, // "{"
{0x001e, 0x0072, 0x01ce, 0x0738, 0x1ce0, 0x1380, 0x1e00, 0x0000, 0x0000, 0x0000}, // "\"
{0x0e1c, 0x0a14, 0x0bf4, 0x0804, 0x0ffc, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, // "}"
{0x0070, 0x0058, 0x006c, 0x0034, 0x006c, 0x0058, 0x0070, 0x0000, 0x0000, 0x0000}, // "^"
{0x1c00, 0x1400, 0x1400, 0x1400, 0x1400, 0x1400, 0x1400, 0x1c00, 0x0000, 0x0000}, // "_"
{0x000e, 0x001a, 0x0036, 0x002c, 0x0038, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, // "`"
{0x07c0, 0x0c60, 0x0ba0, 0x0aa0, 0x0ba0, 0x0c60, 0x0bc0, 0x0e00, 0x0000, 0x0000}, // "a"
{0x0ffc, 0x0804, 0x0bbc, 0x0ba0, 0x0c60, 0x07c0, 0x0000, 0x0000, 0x0000, 0x0000}, // "b"
{0x07c0, 0x0c60, 0x0ba0, 0x0aa0, 0x0ee0, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, // "c"
{0x07c0, 0x0c60, 0x0ba0, 0x0bbc, 0x0804, 0x0ffc, 0x0000, 0x0000, 0x0000, 0x0000}, // "d"
{0x07c0, 0x0c60, 0x0aa0, 0x0aa0, 0x0b60, 0x0fc0, 0x0000, 0x0000, 0x0000, 0x0000}, // "e"
{0x0ff8, 0x080c, 0x0fb4, 0x00f4, 0x001c, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, // "f"
{0x1fc0, 0x3660, 0x2da0, 0x2da0, 0x2da0, 0x3060, 0x1fc0, 0x0000, 0x0000, 0x0000}, // "g"
{0x0ffc, 0x0804, 0x0fbc, 0x0fa0, 0x0860, 0x0fc0, 0x0000, 0x0000, 0x0000, 0x0000}, // "h"
{0x0ff8, 0x0828, 0x0ff8, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, // "i"
{0x1c00, 0x1400, 0x17f8, 0x1828, 0x0ff8, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, // "j"
{0x0ffc, 0x0804, 0x0efc, 0x0d60, 0x0ba0, 0x0ee0, 0x0000, 0x0000, 0x0000, 0x0000}, // "k"
{0x07fc, 0x0c04, 0x0bfc, 0x0a00, 0x0e00, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, // "l"
{0x0fc0, 0x0860, 0x0fa0, 0x07a0, 0x0460, 0x07a0, 0x0fa0, 0x0860, 0x0fc0, 0x0000}, // "m"
{0x0fc0, 0x0860, 0x0fa0, 0x0fa0, 0x0860, 0x0fc0, 0x0000, 0x0000, 0x0000, 0x0000}, // "n"
{0x07c0, 0x0c60, 0x0ba0, 0x0aa0, 0x0ba0, 0x0c60, 0x07c0, 0x0000, 0x0000, 0x0000}, // "o"
{0x3fe0, 0x2020, 0x3da0, 0x05a0, 0x0660, 0x03c0, 0x0000, 0x0000, 0x0000, 0x0000}, // "p"
{0x03c0, 0x0660, 0x05a0, 0x3da0, 0x2020, 0x37e0, 0x1c00, 0x0000, 0x0000, 0x0000}, // "q"
{0x0fc0, 0x0860, 0x0fa0, 0x00a0, 0x00e0, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, // "r"
{0x0fc0, 0x0b60, 0x0aa0, 0x0da0, 0x07e0, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, // "s"
{0x01c0, 0x0770, 0x0c10, 0x0b70, 0x0bc0, 0x0e00, 0x0000, 0x0000, 0x0000, 0x0000}, // "t"
{0x07e0, 0x0c20, 0x0be0, 0x0be0, 0x0c20, 0x07e0, 0x0000, 0x0000, 0x0000, 0x0000}, // "u"
{0x01e0, 0x0720, 0x0ce0, 0x0b80, 0x0ce0, 0x0720, 0x01e0, 0x0000, 0x0000, 0x0000}, // "v"
{0x01e0, 0x0720, 0x0ce0, 0x0b80, 0x0c80, 0x0b80, 0x0ce0, 0x0720, 0x01e0, 0x0000}, // "w"
{0x0ee0, 0x0ba0, 0x0d60, 0x06c0, 0x0d60, 0x0ba0, 0x0ee0, 0x0000, 0x0000, 0x0000}, // "x"
{0x1de0, 0x1720, 0x1ae0, 0x0d80, 0x06e0, 0x0320, 0x01e0, 0x0000, 0x0000, 0x0000}, // "y"
{0x0ee0, 0x0ba0, 0x09a0, 0x0aa0, 0x0b20, 0x0ba0, 0x0ee0, 0x0000, 0x0000, 0x0000}, // "z"
{0x01e0, 0x0f3c, 0x18c6, 0x17fa, 0x1c0e, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, // "{"
{0x1ffe, 0x1002, 0x1ffe, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, // "|"
{0x1c0e, 0x17fa, 0x18c6, 0x0f3c, 0x01e0, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, // "}"
{0x0380, 0x02c0, 0x0340, 0x0340, 0x02c0, 0x02c0, 0x0340, 0x01c0, 0x0000, 0x0000}, // "~"
{0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, // ""
};

Wyświetl plik

@ -0,0 +1,12 @@
set(OUTPUT_NAME interstate75_hello_world)
add_executable(${OUTPUT_NAME} interstate75_hello_world.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
)

Wyświetl plik

@ -0,0 +1,320 @@
#include <stdio.h>
#include <math.h>
#include <cstdint>
#include <cstring>
#include "pico/stdlib.h"
#include "pico/multicore.h"
#include "hardware/vreg.h"
#include "common/pimoroni_common.hpp"
using namespace pimoroni;
/* Interstate 75 - HUB75 from basic principles.
While trying to get our I75 running I soon realised that I couldn't find any documentation or basics on the HUB75 protocol.
Yes there were libraries and tidbits that were seemingly loosely related, but nothing really laying out clear steps to update the 32x32 display on my desk.
So I wrote this.
This code may not be technically correct, but the results are visually appealing and fast enough.
More importantly, the code is legible and doesn't hide any implementation details beneath the inscrutable veneer of PIO.
No interpolators, no DMA, no PIO here- if you want a beautifully optimised library for HUB75 output then this... isn't it.
Just pure C. Pure CPU. Running on RP2040s Core1.
*/
// 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.
const uint8_t WIDTH = 64;
const uint8_t HEIGHT = 64;
// Settings below are correct for I76, change them to suit your setup:
// Top half of display - 16 rows on a 32x32 panel
const uint PIN_R0 = 0;
const uint PIN_G0 = 1;
const uint PIN_B0 = 2;
// Bottom half of display - 16 rows on a 64x64 panel
const uint PIN_R1 = 3;
const uint PIN_G1 = 4;
const uint PIN_B1 = 5;
// Address pins, 5 lines = 2^5 = 32 values (max 64x64 display)
const uint PIN_ROW_A = 6;
const uint PIN_ROW_B = 7;
const uint PIN_ROW_C = 8;
const uint PIN_ROW_D = 9;
const uint PIN_ROW_E = 10;
// Sundry things
const uint PIN_CLK = 11; // Clock
const uint PIN_STB = 12; // Strobe/Latch
const uint 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
const uint PIN_SW_A = 14;
const uint PIN_SW_USER = 23;
const uint PIN_LED_R = 16;
const uint PIN_LED_G = 17;
const uint PIN_LED_B = 18;
volatile bool flip = false;
// 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.
const uint16_t GAMMA_12BIT[256] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 47, 50,
52, 54, 57, 59, 62, 65, 67, 70, 73, 76, 79, 82, 85, 88, 91, 94,
98, 101, 105, 108, 112, 115, 119, 123, 127, 131, 135, 139, 143, 147, 151, 155,
160, 164, 169, 173, 178, 183, 187, 192, 197, 202, 207, 212, 217, 223, 228, 233,
239, 244, 250, 255, 261, 267, 273, 279, 285, 291, 297, 303, 309, 316, 322, 328,
335, 342, 348, 355, 362, 369, 376, 383, 390, 397, 404, 412, 419, 427, 434, 442,
449, 457, 465, 473, 481, 489, 497, 505, 513, 522, 530, 539, 547, 556, 565, 573,
582, 591, 600, 609, 618, 628, 637, 646, 656, 665, 675, 685, 694, 704, 714, 724,
734, 744, 755, 765, 775, 786, 796, 807, 817, 828, 839, 850, 861, 872, 883, 894,
905, 917, 928, 940, 951, 963, 975, 987, 998, 1010, 1022, 1035, 1047, 1059, 1071, 1084,
1096, 1109, 1122, 1135, 1147, 1160, 1173, 1186, 1199, 1213, 1226, 1239, 1253, 1266, 1280, 1294,
1308, 1321, 1335, 1349, 1364, 1378, 1392, 1406, 1421, 1435, 1450, 1465, 1479, 1494, 1509, 1524,
1539, 1554, 1570, 1585, 1600, 1616, 1631, 1647, 1663, 1678, 1694, 1710, 1726, 1743, 1759, 1775,
1791, 1808, 1824, 1841, 1858, 1875, 1891, 1908, 1925, 1943, 1960, 1977, 1994, 2012, 2029, 2047};
// We don't *need* to make Pixel a fancy struct with RGB values, but it helps.
#pragma pack(push, 1)
struct alignas(4) Pixel {
uint8_t r;
uint8_t g;
uint8_t b;
uint8_t _;
constexpr Pixel() : r(0), g(0), b(0), _(0) {};
constexpr Pixel(uint8_t r, uint8_t g, uint8_t b) : r(r), g(g), b(b), _(0) {};
constexpr Pixel(float r, float g, float b) : r((uint8_t)(r * 255.0f)), g((uint8_t)(g * 255.0f)), b((uint8_t)(b * 255.0f)), _(0) {};
};
#pragma pack(pop)
// Create our front and back buffers.
// We'll draw into the frontbuffer and then copy everything into the backbuffer which will be used to refresh the screen.
// Double-buffering the display avoids screen tearing with fast animations or slow update rates.
Pixel backbuffer[WIDTH][HEIGHT];
Pixel frontbuffer[WIDTH][HEIGHT];
// 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);
}
}
// Required for FM6126A-based displays which need some register config/init to work properly
void FM6126A_write_register(uint16_t value, uint8_t position) {
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_flip () {
flip = true; // TODO: rewrite to semaphore
}
void hub75_display_update() {
// Ridiculous register write nonsense for the FM6126A-based 64x64 matrix
FM6126A_write_register(0b1111111111111110, 12);
FM6126A_write_register(0b0000001000000000, 13);
while (true) {
// 0. Copy the contents of the front buffer into our backbuffer for output to the display.
// This uses another whole backbuffer worth of memory, but prevents visual tearing at low frequencies.
if (flip) {
memcpy((uint8_t *)backbuffer, (uint8_t *)frontbuffer, WIDTH * HEIGHT * sizeof(Pixel));
flip = false;
}
// Step through 0b00000001, 0b00000010, 0b00000100 etc
for(auto bit = 1u; bit < 1 << 11; bit <<= 1) {
// Since the display is in split into two equal halves, we step through y from 0 to HEIGHT / 2
for(auto y = 0u; y < HEIGHT / 2; y++) {
// 1. Shift out pixel data
// Shift out WIDTH pixels to the top and bottom half of the display
for(auto x = 0u; x < WIDTH; x++) {
// Get the current pixel for top/bottom half
// This is easy since we just need the pixels at X/Y and X/Y+HEIGHT/2
Pixel pixel_top = backbuffer[x][y];
Pixel pixel_bottom = backbuffer[x][y + HEIGHT / 2];
// Gamma correct the colour values from 8-bit to 11-bit
uint16_t pixel_top_b = GAMMA_12BIT[pixel_top.b];
uint16_t pixel_top_g = GAMMA_12BIT[pixel_top.g];
uint16_t pixel_top_r = GAMMA_12BIT[pixel_top.r];
uint16_t pixel_bottom_b = GAMMA_12BIT[pixel_bottom.b];
uint16_t pixel_bottom_g = GAMMA_12BIT[pixel_bottom.g];
uint16_t pixel_bottom_r = GAMMA_12BIT[pixel_bottom.r];
// Set the clock low while we set up the data pins
gpio_put(PIN_CLK, !CLK_POLARITY);
// Top half
gpio_put(PIN_R0, (bool)(pixel_top_r & bit));
gpio_put(PIN_G0, (bool)(pixel_top_g & bit));
gpio_put(PIN_B0, (bool)(pixel_top_b & bit));
// Bottom half
gpio_put(PIN_R1, (bool)(pixel_bottom_r & bit));
gpio_put(PIN_G1, (bool)(pixel_bottom_g & bit));
gpio_put(PIN_B1, (bool)(pixel_bottom_b & bit));
// Wiggle the clock
// The gamma correction above will ensure our clock stays asserted
// for some small amount of time, avoiding the need for an explicit delay.
gpio_put(PIN_CLK, CLK_POLARITY);
}
// 2. Set address pins
// Set the address pins to reflect the row to light up: 0 through 15 for 32x32 pixel panels
// We decode our 5-bit row address out onto the 5 GPIO pins by masking each bit in turn.
gpio_put_masked(0b11111 << PIN_ROW_A, y << PIN_ROW_A);
// 3. Assert latch/strobe signal (STB)
// This latches all the values we've just clocked into the column shift registers.
// The values will appear on the output pins, ready for the display to be driven.
gpio_put(PIN_STB, STB_POLARITY);
asm volatile("nop \nnop"); // Batman!
gpio_put(PIN_STB, !STB_POLARITY);
// 4. Asset the output-enable signal (OE)
// This turns on the display for a brief period to light the selected rows/columns.
gpio_put(PIN_OE, OE_POLARITY);
// 5. Delay
// Delay for a period of time coressponding to "bit"'s significance
for(auto s = 0u; s < bit; ++s) {
// The basic premise here is that "bit" will step through the values:
// 1, 2, 4, 8, 16, 32, 64, etc in sequence.
// If we plug this number into a delay loop, we'll get different magnitudes
// of delay which correspond exactly to the significance of each bit.
// The longer we delay here, the slower the overall panel refresh rate will be.
// But we need to delay *just enough* that we're not under-driving the panel and
// losing out on brightness.
asm volatile("nop \nnop"); // Batman!
}
// 6. De-assert output-enable signal (OE)
// Ready to go again!
gpio_put(PIN_OE, !OE_POLARITY);
// 7. GOTO 1.
}
sleep_us(1);
}
}
}
int main() {
// 1.3v allows overclock to ~280000-300000 but YMMV. Faster clock = faster screen update rate!
// vreg_set_voltage(VREG_VOLTAGE_1_30);
// sleep_ms(100);
// 200MHz is roughly about the lower limit for driving a 64x64 display smoothly.
// Just don't look at it out of the corner of your eye.
set_sys_clock_khz(200000, false);
// Set up allllll the GPIO
gpio_init(PIN_R0); gpio_set_function(PIN_R0, GPIO_FUNC_SIO); gpio_set_dir(PIN_R0, true);
gpio_init(PIN_G0); gpio_set_function(PIN_G0, GPIO_FUNC_SIO); gpio_set_dir(PIN_G0, true);
gpio_init(PIN_B0); gpio_set_function(PIN_B0, GPIO_FUNC_SIO); gpio_set_dir(PIN_B0, true);
gpio_init(PIN_R1); gpio_set_function(PIN_R1, GPIO_FUNC_SIO); gpio_set_dir(PIN_R1, true);
gpio_init(PIN_G1); gpio_set_function(PIN_G1, GPIO_FUNC_SIO); gpio_set_dir(PIN_G1, true);
gpio_init(PIN_B1); gpio_set_function(PIN_B1, GPIO_FUNC_SIO); gpio_set_dir(PIN_B1, true);
gpio_init(PIN_ROW_A); gpio_set_function(PIN_ROW_A, GPIO_FUNC_SIO); gpio_set_dir(PIN_ROW_A, true);
gpio_init(PIN_ROW_B); gpio_set_function(PIN_ROW_B, GPIO_FUNC_SIO); gpio_set_dir(PIN_ROW_B, true);
gpio_init(PIN_ROW_C); gpio_set_function(PIN_ROW_C, GPIO_FUNC_SIO); gpio_set_dir(PIN_ROW_C, true);
gpio_init(PIN_ROW_D); gpio_set_function(PIN_ROW_D, GPIO_FUNC_SIO); gpio_set_dir(PIN_ROW_D, true);
gpio_init(PIN_ROW_E); gpio_set_function(PIN_ROW_E, GPIO_FUNC_SIO); gpio_set_dir(PIN_ROW_E, true);
gpio_init(PIN_CLK); gpio_set_function(PIN_CLK, GPIO_FUNC_SIO); gpio_set_dir(PIN_CLK, true);
gpio_init(PIN_STB); gpio_set_function(PIN_STB, GPIO_FUNC_SIO); gpio_set_dir(PIN_STB, true);
gpio_init(PIN_OE); gpio_set_function(PIN_OE, GPIO_FUNC_SIO); gpio_set_dir(PIN_OE, true);
// Launch the display update routine on Core 1, it's hungry for cycles!
multicore_launch_core1(hub75_display_update);
// Basic loop to draw something to the screen.
// This gets the distance from the middle of the display and uses it to paint a circular colour cycle.
while (true) {
float offset = millis() / 10000.0f;
for(auto x = 0u; x < WIDTH; x++) {
for(auto y = 0u; y < HEIGHT; y++) {
// Center our rainbow circles
float x1 = ((int)x - WIDTH / 2);
float y1 = ((int)y - HEIGHT / 2);
// Get hue as the distance from the display center as float from 0.0 to 1.0f.
float h = float(x1*x1 + y1*y1) / float(WIDTH*WIDTH + HEIGHT*HEIGHT);
// Offset our hue to animate the effect
h -= offset;
frontbuffer[x][y] = hsv_to_rgb(h, 1.0f, 1.0f);
}
}
hub75_flip();
sleep_ms(1000 / 60);
}
}

Wyświetl plik

@ -0,0 +1,13 @@
set(OUTPUT_NAME interstate75_pio_dma)
add_executable(${OUTPUT_NAME} interstate75_pio_dma.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
hub75
)

Wyświetl plik

@ -0,0 +1,50 @@
#include <stdio.h>
#include <math.h>
#include <cstdint>
#include <cstring>
#include "pico/stdlib.h"
#include "common/pimoroni_common.hpp"
#include "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.
const uint8_t WIDTH = 32;
const uint8_t HEIGHT = 32;
Hub75 hub75(WIDTH, HEIGHT, nullptr);
void __isr dma_complete() {
hub75.dma_complete();
}
int main() {
hub75.start(dma_complete);
// Basic loop to draw something to the screen.
// This gets the distance from the middle of the display and uses it to paint a circular colour cycle.
while (true) {
float offset = millis() / 10000.0f;
for(auto x = 0u; x < WIDTH; x++) {
for(auto y = 0u; y < HEIGHT; y++) {
// Center our rainbow circles
float x1 = ((int)x - WIDTH / 2);
float y1 = ((int)y - HEIGHT / 2);
// Get hue as the distance from the display center as float from 0.0 to 1.0f.
float h = float(x1*x1 + y1*y1) / float(WIDTH*WIDTH + HEIGHT*HEIGHT);
// Offset our hue to animate the effect
h -= offset;
hub75.set_hsv(x, y, h, 1.0f, 1.0f);
}
}
hub75.flip();
sleep_ms(1000 / 60);
}
}

Wyświetl plik

@ -0,0 +1,14 @@
set(OUTPUT_NAME interstate75_scrolling_text)
add_executable(${OUTPUT_NAME} interstate75_scrolling_text.cpp)
# enable usb output
pico_enable_stdio_usb(${OUTPUT_NAME} 1)
pico_add_extra_outputs(${OUTPUT_NAME})
target_link_libraries(${OUTPUT_NAME}
pico_stdlib
pico_multicore
hardware_vreg
hub75
)

Wyświetl plik

@ -0,0 +1,79 @@
#include <stdio.h>
#include <cmath>
#include <cstdint>
#include <string>
#include <string_view>
#include "pico/stdlib.h"
#include "hardware/vreg.h"
#include "common/pimoroni_common.hpp"
#include "hub75.hpp"
#include "font_outline_10x14.hpp"
using namespace pimoroni;
// Display size in pixels
// Should be either 64x64 or 32x32 but perhaps 64x32 an other sizes will work.
// Note: this example uses only 5 address lines so it's limited to 64 pixel high displays (32*2).
const uint8_t WIDTH = 64;
const uint8_t HEIGHT = 64;
Hub75 hub75(WIDTH, HEIGHT, nullptr, PANEL_GENERIC, true);
void __isr dma_complete() {
hub75.dma_complete();
}
void scroll_text(std::string_view text, uint y, float t, Pixel color) {
uint text_length = text.length();
uint x = uint(t);
uint letter = uint((x / letter_width) % text_length);
uint pixel = x % letter_width;
char c = text.at(letter);
for (auto s_x = 0u; s_x < WIDTH; s_x++) {
uint32_t col = font[c - 32][pixel];
for (auto s_y = 0u; s_y < letter_height; s_y++) {
// Calculate a bit of wiggle!
uint o_y = int(sinf((t / 3.0f) + s_x / 30.0f) * 8);
// Step through the uint32 that represents a single column
// of the current character, and render pixels for set bits.
if(col & (1 << s_y)) {
hub75.set_color(s_x, s_y + y + o_y, color);
}
}
// Advance to the text pixel/character
pixel++;
if (pixel == letter_width) {
pixel = 0;
letter++;
if (letter == text_length) letter = 0;
c = text.at(letter);
}
}
}
int main() {
vreg_set_voltage(VREG_VOLTAGE_1_20);
sleep_us(100);
set_sys_clock_khz(266000, true);
hub75.start(dma_complete);
std::string text = " Hello World! How are you today? ";
// Basic loop to draw something to the screen.
// This gets the distance from the middle of the display and uses it to paint a circular colour cycle.
while (true) {
hub75.background = hsv_to_rgb(millis() / 10000.0f, 1.0f, 0.5f);
// Shadow
scroll_text(text, HEIGHT / 2 - letter_height / 2 + 1, (millis() + 50) / 50.0f, Pixel(0, 0, 0));
// Text
scroll_text(text, HEIGHT / 2 - letter_height / 2, millis() / 50.0f, Pixel(255, 255, 255));
hub75.flip(false); // Flip and clear to the background colour
sleep_ms(1000 / 60);
}
}

Wyświetl plik

@ -0,0 +1,100 @@
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], # ""
]

Wyświetl plik

@ -0,0 +1,100 @@
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], # ""
]

Wyświetl plik

@ -0,0 +1,53 @@
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))

Wyświetl plik

@ -0,0 +1,23 @@
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)

Wyświetl plik

@ -0,0 +1,148 @@
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()

Wyświetl plik

@ -0,0 +1,23 @@
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)

Wyświetl plik

@ -0,0 +1,23 @@
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)

Wyświetl plik

@ -0,0 +1,50 @@
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))

Wyświetl plik

@ -0,0 +1,122 @@
# Interstate 75 <!-- omit in toc -->
The Interstate 75 library is intended for the Interstate 75 "HUB75" matrix panel driver board.
It can, in theory, be used with your own custom wiring, though custom pin assignments are not supported yet.
- [Notes On PIO & DMA Limitations](#notes-on-pio--dma-limitations)
- [Getting Started](#getting-started)
- [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)
## Notes On PIO & DMA Limitations
The Interstate 75 driver uses the PIO hardware on the RP2040. There are only two PIOs with four state machines each, and i75 uses one of these (`PIO0`) and two state machines- one for clocking out pixels, and another for latching/lighting a row.
It also uses two DMA channels, one to copy pixel data from the back buffer back to the front buffer and one to supply the row driving PIO with row data.
## Getting Started
Contruct a new `Hub75` instance, specifying the width/height of the display and any additional options.
```python
import hub75
WIDTH = 64
HEIGHT = 64
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.
```python
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:
```python
import hub75
WIDTH = 64
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
Set the top left-most LED - `0, 0` - to Purple `255, 0, 255`:
```python
matrix.set_rgb(0, 0, 255, 0, 255)
```
#### HSV
Set the top left-most LED - `0, o` - to Red `0.0`:
```python
matrix.set_hsv(0, 0, 0.0, 1.0, 1.0)
```
### Update The Display
You can update the back buffer - the framebuffer used by the driver to drive the screen - by calling `flip`:
```python
matrix.flip()
```
`flip` will swap the front buffer (the one you draw into) with the back buffer (the one the screen is refreshed from) so that the display can start drawing your changes immediately.
Since the back buffer contains your *previous* frame it then blocks and copies across the contents of the buffer you've just flipped.
If you want fresh, clear buffer to draw into at the start of your next frame you can use `flip_and_clear` instead:
```python
background_color = hub75.color(0, 0, 0)
matrix.flip_and_clear(background_color)
```
This will fill your buffer with the background colour, so you don't need to call `clear`.

Wyświetl plik

@ -0,0 +1,77 @@
#include "hub75.h"
/***** 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_1(Hub75_clear_obj, Hub75_clear);
MP_DEFINE_CONST_FUN_OBJ_1(Hub75_start_obj, Hub75_start);
MP_DEFINE_CONST_FUN_OBJ_1(Hub75_stop_obj, Hub75_stop);
MP_DEFINE_CONST_FUN_OBJ_1(Hub75_flip_obj, Hub75_flip);
MP_DEFINE_CONST_FUN_OBJ_2(Hub75_flip_and_clear_obj, Hub75_flip_and_clear);
MP_DEFINE_CONST_FUN_OBJ_3(Hub75_color_obj, Hub75_color);
MP_DEFINE_CONST_FUN_OBJ_3(Hub75_color_hsv_obj, Hub75_color_hsv);
/***** 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_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) },
};
STATIC MP_DEFINE_CONST_DICT(Hub75_locals_dict, Hub75_locals_dict_table);
/***** Class Definition *****/
const mp_obj_type_t Hub75_type = {
{ &mp_type_type },
.name = MP_QSTR_hub75,
.print = Hub75_print,
.make_new = Hub75_make_new,
.locals_dict = (mp_obj_dict_t*)&Hub75_locals_dict,
};
/***** Globals Table *****/
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_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) },
{ MP_ROM_QSTR(MP_QSTR_LED_G), MP_ROM_INT(17) },
{ MP_ROM_QSTR(MP_QSTR_LED_B), MP_ROM_INT(18) },
{ MP_ROM_QSTR(MP_QSTR_PIN_A0), MP_ROM_INT(26) },
{ MP_ROM_QSTR(MP_QSTR_PIN_A1), MP_ROM_INT(27) },
{ MP_ROM_QSTR(MP_QSTR_PIN_A2), MP_ROM_INT(28) },
{ MP_ROM_QSTR(MP_QSTR_PIN_INT), MP_ROM_INT(19) },
{ MP_ROM_QSTR(MP_QSTR_PIN_SDA), MP_ROM_INT(20) },
{ MP_ROM_QSTR(MP_QSTR_PIN_SCL), MP_ROM_INT(21) },
{ MP_ROM_QSTR(MP_QSTR_CURRENT_SENSE), MP_ROM_INT(29) },
};
STATIC MP_DEFINE_CONST_DICT(mp_module_hub75_globals, hub75_globals_table);
/***** Module Definition *****/
const mp_obj_module_t hub75_user_cmodule = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t*)&mp_module_hub75_globals,
};
MP_REGISTER_MODULE(MP_QSTR_hub75, hub75_user_cmodule, MODULE_HUB75_ENABLED);

Wyświetl plik

@ -0,0 +1,319 @@
#include <cstdio>
#include "hub75.hpp"
#include "pico/multicore.h"
#define MP_OBJ_TO_PTR2(o, t) ((t *)(uintptr_t)(o))
extern "C" {
#include "hub75.h"
#include "py/builtin.h"
#include "py/mpthread.h"
typedef struct _mp_obj_float_t {
mp_obj_base_t base;
mp_float_t value;
} mp_obj_float_t;
const mp_obj_float_t const_float_1 = {{&mp_type_float}, 1.0f};
/********** WS2812 **********/
/***** Variables Struct *****/
typedef struct _Hub75_obj_t {
mp_obj_base_t base;
Hub75* hub75;
void *buf;
} _Hub75_obj_t;
_Hub75_obj_t *hub75_obj;
void __isr dma_complete() {
if(hub75_obj) hub75_obj->hub75->dma_complete();
}
/***** Print *****/
void Hub75_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
(void)kind; // Unused input parameter
_Hub75_obj_t *self = MP_OBJ_TO_PTR2(self_in, _Hub75_obj_t);
mp_print_str(print, "Hub75( ");
mp_print_str(print, "dimensions = ");
mp_obj_print_helper(print, mp_obj_new_int(self->hub75->width), PRINT_REPR);
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 ");
break;
}
mp_print_str(print, " )");
}
/***** Destructor ******/
mp_obj_t Hub75___del__(mp_obj_t self_in) {
_Hub75_obj_t *self = MP_OBJ_TO_PTR2(self_in, _Hub75_obj_t);
self->hub75->stop(dma_complete);
delete self->hub75;
return mp_const_none;
}
/***** Constructor *****/
mp_obj_t Hub75_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
enum {
ARG_width,
ARG_height,
ARG_buffer,
ARG_panel_type,
ARG_stb_invert
};
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_width, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_height, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_buffer, MP_ARG_OBJ, {.u_obj = nullptr} },
{ MP_QSTR_panel_type, MP_ARG_INT, {.u_int = 0} },
{ MP_QSTR_stb_invert, MP_ARG_INT, {.u_int = 0} },
};
// Parse args.
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
int width = args[ARG_width].u_int;
int height = args[ARG_height].u_int;
PanelType paneltype = (PanelType)args[ARG_panel_type].u_int;
bool stb_invert = args[ARG_stb_invert].u_int;
Pixel *buffer = nullptr;
if (args[ARG_buffer].u_obj) {
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))) {
mp_raise_ValueError("Supplied buffer is too small!");
}
} else {
buffer = m_new(Pixel, width * height * 2);
}
hub75_obj = m_new_obj_with_finaliser(_Hub75_obj_t);
hub75_obj->base.type = &Hub75_type;
hub75_obj->buf = buffer;
hub75_obj->hub75 = new Hub75(width, height, buffer, paneltype, stb_invert);
return MP_OBJ_FROM_PTR(hub75_obj);
}
mp_obj_t Hub75_clear(mp_obj_t self_in) {
_Hub75_obj_t *self = MP_OBJ_TO_PTR2(self_in, _Hub75_obj_t);
self->hub75->clear();
return mp_const_none;
}
mp_obj_t Hub75_flip(mp_obj_t self_in) {
_Hub75_obj_t *self = MP_OBJ_TO_PTR2(self_in, _Hub75_obj_t);
self->hub75->flip();
return mp_const_none;
}
mp_obj_t Hub75_flip_and_clear(mp_obj_t self_in, mp_obj_t color) {
_Hub75_obj_t *self = MP_OBJ_TO_PTR2(self_in, _Hub75_obj_t);
self->hub75->background.color = mp_obj_get_int(color);
self->hub75->flip(false);
return mp_const_none;
}
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);
return mp_const_none;
}
mp_obj_t Hub75_stop(mp_obj_t self_in) {
_Hub75_obj_t *self = MP_OBJ_TO_PTR2(self_in, _Hub75_obj_t);
self->hub75->stop(dma_complete);
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) {
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 },
{ MP_QSTR_x, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_y, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_r, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_g, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_b, 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;
int r = args[ARG_r].u_int;
int g = args[ARG_g].u_int;
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);
}
}
return mp_const_none;
}
}

Wyświetl plik

@ -0,0 +1,24 @@
// Include MicroPython API.
#include "py/runtime.h"
#include "py/objstr.h"
/***** Extern of Class Definition *****/
extern const mp_obj_type_t Hub75_type;
/***** Extern of Class Methods *****/
extern void Hub75_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind);
extern mp_obj_t Hub75_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args);
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_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);

Wyświetl plik

@ -0,0 +1,26 @@
add_library(usermod_hub75 INTERFACE)
target_sources(usermod_pico_display INTERFACE
${CMAKE_CURRENT_LIST_DIR}/hub75.c
${CMAKE_CURRENT_LIST_DIR}/hub75.cpp
${CMAKE_CURRENT_LIST_DIR}/../../../drivers/hub75/hub75.cpp
)
target_include_directories(usermod_hub75 INTERFACE
${CMAKE_CURRENT_LIST_DIR}
${CMAKE_CURRENT_LIST_DIR}/../../../drivers/hub75/
)
target_compile_definitions(usermod_hub75 INTERFACE
MODULE_HUB75_ENABLED=1
)
target_link_libraries(usermod INTERFACE usermod_hub75)
set_source_files_properties(
${CMAKE_CURRENT_LIST_DIR}/hub75.c
PROPERTIES COMPILE_FLAGS
"-Wno-discarded-qualifiers -Wno-implicit-int"
)
pico_generate_pio_header(usermod_hub75 ${CMAKE_CURRENT_LIST_DIR}/../../../drivers/hub75/hub75.pio)

Wyświetl plik

@ -37,4 +37,5 @@ include(pico_display_2/micropython)
include(pico_explorer/micropython)
include(pico_wireless/micropython)
include(plasma/micropython)
include(hub75/micropython)
include(ulab/code/micropython)