From 62a69a4c6dcd41234ee784d047cbc598dc2233f1 Mon Sep 17 00:00:00 2001 From: radex Date: Sun, 2 Jun 2024 11:02:47 +0200 Subject: [PATCH 1/3] pio-based everything 8-bit color work! --- firmware/src/leds.cpp | 20 +++++++++++--------- firmware/src/leds.h | 4 ++-- firmware/src/leds.pio | 4 ++-- firmware/src/leds.pio.h | 8 ++++---- 4 files changed, 19 insertions(+), 17 deletions(-) diff --git a/firmware/src/leds.cpp b/firmware/src/leds.cpp index 18f8ba1..563f466 100644 --- a/firmware/src/leds.cpp +++ b/firmware/src/leds.cpp @@ -12,6 +12,8 @@ uint pusher_sm = 255; // invalid uint delay_sm = 255; // invalid uint row_sm = 255; // invalid +#define LEDS_PIO_CLKDIV 1 + // NOTE: RCLK, SRCLK capture on *rising* edge inline void pulsePin(uint8_t pin) { gpio_put(pin, HIGH); @@ -36,7 +38,7 @@ inline void outputEnable(uint8_t pin, bool enable) { uint8_t brightnessPhase = 0; // delays in nanoseconds -#define NS_TO_DELAY(ns) ns / NS_PER_CYCLE +#define NS_TO_DELAY(ns) (ns / NS_PER_CYCLE / LEDS_PIO_CLKDIV) uint32_t brightnessPhaseDelays[COLOR_BITS] = { NS_TO_DELAY(50), NS_TO_DELAY(100), @@ -104,18 +106,18 @@ void leds_set_framebuffer(uint8_t *buffer) { } // set row shifting data - bool firstRow = y == (ROW_COUNT - 1); - uint32_t rowPulses = 1; + bool firstRow = y == 0; + uint32_t extraPulses = 0; if (moduleY == 0) { - rowPulses++; + extraPulses++; } if (moduleY == 7 || moduleY == 14 || (moduleY == 0 && yModule != 0)) { - rowPulses++; + extraPulses++; } - uint32_t rowData = firstRow | (rowPulses << 1); + uint32_t rowData = firstRow | (extraPulses << 1); ledBuffer[bi][outputYOffset + COL_MODULES] = rowData; } } @@ -224,7 +226,7 @@ void leds_initPusher() { uint offset = pio_add_program(pio, &leds_px_pusher_program); pio_sm_config config = leds_px_pusher_program_get_default_config(offset); - sm_config_set_clkdiv_int_frac(&config, 1, 0); + sm_config_set_clkdiv_int_frac(&config, LEDS_PIO_CLKDIV, 0); // Shift OSR to the right, autopull sm_config_set_out_shift(&config, true, true, 32); @@ -264,7 +266,7 @@ void leds_initRowSelector() { uint offset = pio_add_program(pio, &leds_row_selector_program); pio_sm_config config = leds_row_selector_program_get_default_config(offset); - sm_config_set_clkdiv_int_frac(&config, 1, 0); + sm_config_set_clkdiv_int_frac(&config, LEDS_PIO_CLKDIV, 0); // Shift OSR to the right, autopull sm_config_set_out_shift(&config, true, true, 32); @@ -293,7 +295,7 @@ void leds_initDelay() { uint offset = pio_add_program(pio, &leds_delay_program); pio_sm_config config = leds_delay_program_get_default_config(offset); - sm_config_set_clkdiv_int_frac(&config, 1, 0); + sm_config_set_clkdiv_int_frac(&config, LEDS_PIO_CLKDIV, 0); // Shift OSR to the right, autopull sm_config_set_out_shift(&config, true, true, 32); diff --git a/firmware/src/leds.h b/firmware/src/leds.h index c0d8510..80231f6 100644 --- a/firmware/src/leds.h +++ b/firmware/src/leds.h @@ -24,10 +24,10 @@ #define COLOR_BITS 8 #define FPS 30 -#define MS_PER_FRAME 1000 / FPS +#define MS_PER_FRAME (1000 / FPS) #define CPU_MHZ 125 -#define NS_PER_CYCLE 1000 / CPU_MHZ +#define NS_PER_CYCLE (1000 / CPU_MHZ) void leds_init(); void leds_initRenderer(); diff --git a/firmware/src/leds.pio b/firmware/src/leds.pio index 84dd463..cf559e7 100644 --- a/firmware/src/leds.pio +++ b/firmware/src/leds.pio @@ -1,6 +1,6 @@ .define public irq_did_latch 0 -.define public irq_delaying 1 .define public irq_row_selected 1 +.define public irq_delaying 2 ; TODO: check if delays can be lowered with a PCB .define public srclk_0_delay 1 @@ -48,7 +48,7 @@ entry_point: .wrap_target ; LSB=1 indicates first (bottom) row, ergo, high SER for the first pulse out pins, 1 side 0 [srclk_0_delay] - ; The rest of the word indicates number of SRCLK pulses + ; The rest of the word indicates number of extra SRCLK pulses out x, 31 loop: ; pulse SRCLK x times diff --git a/firmware/src/leds.pio.h b/firmware/src/leds.pio.h index 70af97f..43cde1e 100644 --- a/firmware/src/leds.pio.h +++ b/firmware/src/leds.pio.h @@ -9,8 +9,8 @@ #endif #define irq_did_latch 0 -#define irq_delaying 1 #define irq_row_selected 1 +#define irq_delaying 2 #define srclk_0_delay 1 #define srclk_1_delay 2 #define rclk_1_delay 3 @@ -29,7 +29,7 @@ static const uint16_t leds_px_pusher_program_instructions[] = { 0x1a41, // 2: jmp x--, 1 side 1 [2] 0x7028, // 3: out x, 8 side 0 0x0020, // 4: jmp !x, 0 - 0x2041, // 5: wait 0 irq, 1 + 0x2042, // 5: wait 0 irq, 2 0x20c1, // 6: wait 1 irq, 1 0xc000, // 7: irq nowait 0 0xe301, // 8: set pins, 1 [3] @@ -98,10 +98,10 @@ static inline pio_sm_config leds_row_selector_program_get_default_config(uint of static const uint16_t leds_delay_program_instructions[] = { // .wrap_target 0x20c0, // 0: wait 1 irq, 0 - 0xc001, // 1: irq nowait 1 + 0xc002, // 1: irq nowait 2 0x6020, // 2: out x, 32 0x1043, // 3: jmp x--, 3 side 0 - 0xd841, // 4: irq clear 1 side 1 + 0xd842, // 4: irq clear 2 side 1 // .wrap }; From da893fd9de66ea30703a9288db9dc172ecc89581 Mon Sep 17 00:00:00 2001 From: radex Date: Sun, 2 Jun 2024 12:05:42 +0200 Subject: [PATCH 2/3] wip gamma correction --- firmware/src/gfx_decoder.cpp | 49 ++++++++++++++++++++++++++++++++++++ firmware/src/gfx_decoder.h | 1 + firmware/src/leds.cpp | 17 +++++++------ 3 files changed, 59 insertions(+), 8 deletions(-) diff --git a/firmware/src/gfx_decoder.cpp b/firmware/src/gfx_decoder.cpp index 3be7839..1e59352 100644 --- a/firmware/src/gfx_decoder.cpp +++ b/firmware/src/gfx_decoder.cpp @@ -56,3 +56,52 @@ int32_t gfx_decoder_handleLoop() { } return 0; } + +void gfx_decoder_setTestFrame() { + uint8_t buffer[ROW_COUNT * COL_COUNT] = {0}; + + // le boxes + for (int i = 0; i < 8; i++) { + uint8_t color = 1 << i; + int startX = (i % 4) * 10; + int startY = (i / 4) * 10; + + // box with only 1< Date: Sun, 2 Jun 2024 13:15:49 +0200 Subject: [PATCH 3/3] gamma --- firmware/src/leds.cpp | 54 +++++++++++++++++++++++++++++++++++-------- firmware/src/main.cpp | 2 ++ 2 files changed, 46 insertions(+), 10 deletions(-) diff --git a/firmware/src/leds.cpp b/firmware/src/leds.cpp index 540894c..799dfa2 100644 --- a/firmware/src/leds.cpp +++ b/firmware/src/leds.cpp @@ -41,14 +41,33 @@ uint8_t brightnessPhase = 0; #define NS_TO_DELAY(ns) (ns / NS_PER_CYCLE / LEDS_PIO_CLKDIV) uint32_t brightnessPhaseDelays[COLOR_BITS] = { // NOTE: 100ns seems to be the minimum that's (barely) visible - /* 1 */ NS_TO_DELAY(115), - /* 2 */ NS_TO_DELAY(130), - /* 4 */ NS_TO_DELAY(140), - /* 8 */ NS_TO_DELAY(160), - /* 16 */ NS_TO_DELAY(190), - /* 32 */ NS_TO_DELAY(500), - /* 64 */ NS_TO_DELAY(3200), - /* 128 */ NS_TO_DELAY(14000), + /* 1 */ NS_TO_DELAY(170), + /* 2 */ NS_TO_DELAY(180), + /* 4 */ NS_TO_DELAY(210), + /* 8 */ NS_TO_DELAY(540), + /* 16 */ NS_TO_DELAY(2300), // x2 + /* 32 */ NS_TO_DELAY(3000), // x4 + /* 64 */ NS_TO_DELAY(2500), // x10 + /* 128 */ NS_TO_DELAY(3300), // x20 +}; + +#define DITHERING_PHASES 20; +uint8_t ditheringPhase = 0; +uint8_t brightnessPhaseDithering[COLOR_BITS] = { + // Out of DITHERING_PHASES, how many of these should a given + // brightness phase be displayed? + // NOTE: This is done brecause for small delays, pixel pushing dominates the time, making + // the display's duty cycle (and hence brightness) low. But since these less significant bits + // contribute little to the overall brightness, and overall displaying time is short (a fraction of + // a framerate), we can skip displaying these small brightness levels most of the time. + /* 1 */ 1, + /* 2 */ 1, + /* 4 */ 1, + /* 8 */ 1, + /* 16 */ 2, + /* 32 */ 4, + /* 64 */ 10, + /* 128 */ 20, }; // NOTE: Alignment required to allow 4-byte reads @@ -188,11 +207,16 @@ void leds_initRenderer() { multicore_launch_core1(main2); } +void leds_nextPhase(); + void leds_render() { if (!ledBufferReady) { return; } + // next brightness phase + leds_nextPhase(); + auto buffer = ledBuffer[brightnessPhase]; auto delayData = brightnessPhaseDelays[brightnessPhase]; @@ -214,9 +238,19 @@ void leds_render() { // set delay data pio_sm_put_blocking(leds_pio, delay_sm, delayData); } +} - // next brightness phase - brightnessPhase = (brightnessPhase + 1) % COLOR_BITS; +void leds_nextPhase() { + brightnessPhase++; + + if (brightnessPhase == COLOR_BITS) { + brightnessPhase = 0; + ditheringPhase = (ditheringPhase + 1) % DITHERING_PHASES; + } + + while (ditheringPhase >= brightnessPhaseDithering[brightnessPhase]) { + brightnessPhase++; + } } void leds_initPusher() { diff --git a/firmware/src/main.cpp b/firmware/src/main.cpp index 4aeaa64..7afa4ae 100644 --- a/firmware/src/main.cpp +++ b/firmware/src/main.cpp @@ -30,6 +30,8 @@ void setup() { sd_loadPlaylist(); loadVideo(0); + + // gfx_decoder_setTestFrame(); } size_t currentVideoIndex = 0;