From eed79921274a8150ea7314c791671b1ccd5a2c1e Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Mon, 22 Nov 2021 16:34:56 +0000 Subject: [PATCH] HUB75 Fix intermittent display corruption issue The FM6126A register write was causing some weirdness on soft reset where the pin state wasn't predictable. Have twiddled some pins at startup to ensure everything works as expected. Add set_hsv and set_all_hsv (since setting individual pixels with hsv is S L O W) Made "flip()" blocking, it was easy to accidentally get a little tearing. --- micropython/modules/hub75/hub75.c | 4 ++ micropython/modules/hub75/hub75.cpp | 55 ++++++++++++++++ micropython/modules/hub75/hub75.h | 2 + micropython/modules/hub75/lib/hub75.cpp | 87 +++++++++++++++++++------ micropython/modules/hub75/lib/hub75.hpp | 8 +-- 5 files changed, 130 insertions(+), 26 deletions(-) diff --git a/micropython/modules/hub75/hub75.c b/micropython/modules/hub75/hub75.c index 38e1ea7e..36396827 100644 --- a/micropython/modules/hub75/hub75.c +++ b/micropython/modules/hub75/hub75.c @@ -4,6 +4,8 @@ /***** Methods *****/ MP_DEFINE_CONST_FUN_OBJ_1(Hub75___del___obj, Hub75___del__); 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_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); @@ -13,6 +15,8 @@ MP_DEFINE_CONST_FUN_OBJ_1(Hub75_flip_obj, Hub75_flip); 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_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_all_hsv), MP_ROM_PTR(&Hub75_set_all_hsv_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) }, diff --git a/micropython/modules/hub75/hub75.cpp b/micropython/modules/hub75/hub75.cpp index 04a73771..7ae918b6 100644 --- a/micropython/modules/hub75/hub75.cpp +++ b/micropython/modules/hub75/hub75.cpp @@ -162,4 +162,59 @@ mp_obj_t Hub75_set_rgb(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_arg 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_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; +} + } \ No newline at end of file diff --git a/micropython/modules/hub75/hub75.h b/micropython/modules/hub75/hub75.h index 8dc3cae5..7a72b804 100644 --- a/micropython/modules/hub75/hub75.h +++ b/micropython/modules/hub75/hub75.h @@ -11,5 +11,7 @@ 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_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_all_hsv(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); \ No newline at end of file diff --git a/micropython/modules/hub75/lib/hub75.cpp b/micropython/modules/hub75/lib/hub75.cpp index 1fbd32d2..bf1297fd 100644 --- a/micropython/modules/hub75/lib/hub75.cpp +++ b/micropython/modules/hub75/lib/hub75.cpp @@ -1,30 +1,63 @@ #include #include +#include #include "hub75.hpp" +// Basic function to convert Hue, Saturation and Value to an RGB colour +Pixel hsv_to_rgb(float h, float s, float v) { + if(h < 0.0f) { + h = 1.0f + fmod(h, 1.0f); + } + + int i = int(h * 6); + float f = h * 6 - i; + + v = v * 255.0f; + + float sv = s * v; + float fsv = f * sv; + + auto p = uint8_t(-sv + v); + auto q = uint8_t(-fsv + v); + auto t = uint8_t(fsv - sv + v); + + uint8_t bv = uint8_t(v); + + switch (i % 6) { + default: + case 0: return Pixel(bv, t, p); + case 1: return Pixel(q, bv, p); + case 2: return Pixel(p, bv, t); + case 3: return Pixel(p, q, bv); + case 4: return Pixel(t, p, bv); + case 5: return Pixel(bv, p, q); + } +} + + Hub75::Hub75(uint8_t width, uint8_t height, Pixel *buffer) : width(width), height(height), front_buffer(buffer), back_buffer(buffer + width * height) { // 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_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_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_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_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_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_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); + 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); } void Hub75::set_rgb(uint8_t x, uint8_t y, uint8_t r, uint8_t g, uint8_t b) { @@ -39,7 +72,22 @@ void Hub75::set_rgb(uint8_t x, uint8_t y, uint8_t r, uint8_t g, uint8_t b) { front_buffer[offset] = Pixel(r, g, b); } +void Hub75::set_hsv(uint8_t x, uint8_t y, float h, float s, float v) { + int offset = 0; + if(y >= 32) { + y -= 32; + offset = (y * width + x) * 2; + offset += 1; + } else { + offset = (y * width + x) * 2; + } + front_buffer[offset] = 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; @@ -86,8 +134,8 @@ void Hub75::start(irq_handler_t handler) { data_prog_offs = pio_add_program(pio, &hub75_data_rgb888_program); row_prog_offs = pio_add_program(pio, &hub75_row_program); - hub75_data_rgb888_program_init(pio, sm_data, data_prog_offs, DATA_BASE_PIN, CLK_PIN); - hub75_row_program_init(pio, sm_row, row_prog_offs, ROWSEL_BASE_PIN, ROWSEL_N_PINS, STROBE_PIN); + 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); if(dma_channel_is_claimed(dma_channel)) { irq_set_enabled(DMA_IRQ_0, false); @@ -145,8 +193,8 @@ void Hub75::stop(irq_handler_t handler) { if(pio_sm_is_claimed(pio, sm_data)) pio_sm_unclaim(pio, sm_row); pio_clear_instruction_memory(pio); - gpio_put(pin_oe, !OE_POLARITY); - //gpio_put(pin_stb, !STB_POLARITY); + gpio_put(pin_oe, !oe_polarity); + gpio_put(pin_stb, !stb_polarity); } Hub75::~Hub75() { @@ -162,6 +210,7 @@ void Hub75::clear() { void Hub75::flip() { do_flip = true; + while(do_flip) {}; } void Hub75::display_update() { @@ -215,7 +264,7 @@ void Hub75::dma_complete() { dma_channel_acknowledge_irq0(dma_channel); // SM is finished when it stalls on empty TX FIFO (or, y'know, DMA callback) - // hub75_wait_tx_stall(pio, sm_data); + 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); diff --git a/micropython/modules/hub75/lib/hub75.hpp b/micropython/modules/hub75/lib/hub75.hpp index 1411c557..a37c7b14 100644 --- a/micropython/modules/hub75/lib/hub75.hpp +++ b/micropython/modules/hub75/lib/hub75.hpp @@ -11,13 +11,6 @@ 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 CLK_PIN = 11; -const uint STROBE_PIN = 12; -const uint OEN_PIN = 13; - -const bool CLK_POLARITY = 1; -const bool STB_POLARITY = 1; -const bool OE_POLARITY = 0; // 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. @@ -113,6 +106,7 @@ class Hub75 { void FM6126A_write_register(uint16_t value, uint8_t position); void set_rgb(uint8_t x, uint8_t y, uint8_t r, uint8_t g, uint8_t b); + void set_hsv(uint8_t x, uint8_t y, float r, float g, float b); void display_update(); void clear(); void start(irq_handler_t handler);