From a2f703c2e13c59fbab433530499f5601110ca370 Mon Sep 17 00:00:00 2001 From: radex Date: Tue, 4 Jun 2024 18:31:09 +0200 Subject: [PATCH] extract config.h --- firmware/src/audio.cpp | 5 +- firmware/src/audio.h | 6 +-- firmware/src/config.cpp | 36 ++++++++++++++ firmware/src/config.h | 95 ++++++++++++++++++++++++++++++++++++ firmware/src/gfx_decoder.cpp | 4 +- firmware/src/gfx_decoder.h | 8 ++- firmware/src/leds.cpp | 64 +++++++----------------- firmware/src/leds.h | 28 +---------- firmware/src/life.cpp | 5 +- firmware/src/main.cpp | 28 +++++++---- firmware/src/sd.cpp | 22 ++++----- firmware/src/sd.h | 1 - 12 files changed, 191 insertions(+), 111 deletions(-) create mode 100644 firmware/src/config.cpp create mode 100644 firmware/src/config.h diff --git a/firmware/src/audio.cpp b/firmware/src/audio.cpp index ed66e7e..1b6efa1 100644 --- a/firmware/src/audio.cpp +++ b/firmware/src/audio.cpp @@ -5,8 +5,6 @@ #include "hardware/gpio.h" #include "audio.h" -// #include "audio_sample.h" - // Adapted from https://github.com/rgrosset/pico-pwm-audio #define MAX_PWM_POS (BUFFER_LEN << 3) @@ -42,9 +40,8 @@ void pwm_interrupt_handler() { } // 11 KHz is fine for speech. Phone lines generally sample at 8 KHz -#define SYS_CLOCK 125000000.0f #define AUDIO_WRAP 256.0f -#define AUDIO_CLK_DIV (SYS_CLOCK / AUDIO_WRAP / 8.0f / AUDIO_RATE) +#define AUDIO_CLK_DIV (CPU_CLOCK_HZ / AUDIO_WRAP / 8.0f / AUDIO_RATE) void init_audio() { gpio_set_function(AUDIO_PIN, GPIO_FUNC_PWM); diff --git a/firmware/src/audio.h b/firmware/src/audio.h index 208cb38..7bb7ea4 100644 --- a/firmware/src/audio.h +++ b/firmware/src/audio.h @@ -3,11 +3,7 @@ #ifndef _audio_h #define _audio_h -#define AUDIO_PIN 8 - -#define AUDIO_RATE 44000.0f -#define BUFFER_LEN 512*16 -#define BUFFER_LEN_MS (BUFFER_LEN / AUDIO_RATE) * 1000.0f +#include "config.h" extern uint8_t wav_buffer_0[BUFFER_LEN]; extern uint8_t wav_buffer_1[BUFFER_LEN]; diff --git a/firmware/src/config.cpp b/firmware/src/config.cpp new file mode 100644 index 0000000..38d350a --- /dev/null +++ b/firmware/src/config.cpp @@ -0,0 +1,36 @@ +#include +#include "config.h" + +#define CPU_MHZ ((uint32_t) CPU_CLOCK_HZ / 1000000) +#define NS_PER_CYCLE (1000 / CPU_MHZ) +#define NS_TO_DELAY(ns) (ns / NS_PER_CYCLE / LEDS_PIO_CLKDIV) + +// delays in nanoseconds per brightness phase (i.e. depth bit index) +uint32_t brightnessPhaseDelays[COLOR_BITS] = { + // NOTE: 100ns seems to be the minimum that's (barely) visible on current hardware + /* 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 +}; + +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, +}; diff --git a/firmware/src/config.h b/firmware/src/config.h new file mode 100644 index 0000000..98bd20e --- /dev/null +++ b/firmware/src/config.h @@ -0,0 +1,95 @@ +#pragma once + +#ifndef _config_h +#define _config_h + +// --- pinout --- + +#define AUDIO_PIN 8 + +#define SD_PIN_SS 1 +#define SD_PIN_SCK 2 +#define SD_PIN_MOSI 3 +#define SD_PIN_MISO 0 +#define SD_HAS_DETECTION true +#define SD_DET_PIN 4 + +#define CAN_ENABLED false +#define CAN_PIN_RX 5 +#define CAN_PIN_TX 7 +#define CAN_PIN_SILENT 6 + +#define UART_TX_PIN 16 +#define UART_RX_PIN 17 + +// NOTE: with current layout, 9-15 are free GPIOs (14, 15 only when using shared SER, SRCLR) +#define NEXT_PIN 9 + +#define CC1_PIN 26 +#define CC2_PIN 27 +#define V_SENSE_PIN 28 +#define I_SENSE_PIN 29 + +// --- pinout - screen --- + +#define COMMON_SER 21 +// #define COMMON_RCLK 23 +#define COMMON_SRCLR 25 + +// #define COL_SER 21 +#define COL_OE 22 +#define COL_RCLK 23 +#define COL_SRCLK 24 +// #define COL_SRCLR 25 + +// #define ROW_SER 14 +#define ROW_OE 13 +#define ROW_RCLK 20 +#define ROW_SRCLK 18 +// #define ROW_SRCLR 15 + +// --- screen settings --- + +#define ROW_MODULES 2 +#define COL_MODULES 2 + +#define COLOR_BITS 8 + +#define MAX_FPS 30 +#define MAX_LENGTH_MIN 10 + +#define ROW_COUNT ROW_MODULES * 20 +#define COL_COUNT COL_MODULES * 20 + +// --- audio settings --- + +#define AUDIO_RATE 44000.0f +#define BUFFER_LEN 512*16 + +// --- other settings --- + +#define CPU_CLOCK_HZ 125000000.0f +#define SD_CARD_BAUD_RATE 10 * 1000 * 1000 +#define REFERENCE_VOLTAGE 3.3f // for ADC + +// --- debug settings --- + +#define DEBUG_TEST_FRAME false +#define DEBUG_FIRST_FRAME false +#define DEBUG_FRAMEBUFFER false + +// --- screen - performance --- +// NOTE: In case of screen glitching, these may need to be tweaked to decrease +// data rate to the display and stay within the limits of the hardware/electrical connection +#define LEDS_PIO_CLKDIV 1 +// Also see leds.pio where delays may be adjusted (need manual compilation) + +// --- screen - color correction --- +// I do not understand color correction, gamma, it's all black magic to me +// This was manually tuned using DEBUG_TEST_FRAME=true test pattern +// See config.cpp +extern uint32_t brightnessPhaseDelays[COLOR_BITS]; +extern uint8_t brightnessPhaseDithering[COLOR_BITS]; +#define DITHERING_PHASES 20; + +#endif diff --git a/firmware/src/gfx_decoder.cpp b/firmware/src/gfx_decoder.cpp index 1e59352..4301d8b 100644 --- a/firmware/src/gfx_decoder.cpp +++ b/firmware/src/gfx_decoder.cpp @@ -3,10 +3,10 @@ #include "lodepng.h" #include "leds.h" -uint16_t gfxFrameLengthsBuffer[24000] = {0}; +uint16_t gfxFrameLengthsBuffer[GFX_DECODER_LENGTH_BUFFER_SIZE] = {0}; uint16_t frameCount = 0; -uint8_t gfxFrameBuffer[6400] = {0}; +uint8_t gfxFrameBuffer[GFX_DECODER_FRAME_BUFFER_SIZE] = {0}; int32_t gfx_decoder_loadNextFrame() { // load frame from SD card diff --git a/firmware/src/gfx_decoder.h b/firmware/src/gfx_decoder.h index aac5d8f..2a6c1f2 100644 --- a/firmware/src/gfx_decoder.h +++ b/firmware/src/gfx_decoder.h @@ -3,10 +3,14 @@ #define GFX_DECODER_H #include +#include "config.h" -extern uint16_t gfxFrameLengthsBuffer[24000]; +#define GFX_DECODER_LENGTH_BUFFER_SIZE MAX_LENGTH_MIN * 60 * MAX_FPS +#define GFX_DECODER_FRAME_BUFFER_SIZE ROW_COUNT * COL_COUNT * 4 + +extern uint16_t gfxFrameLengthsBuffer[GFX_DECODER_LENGTH_BUFFER_SIZE]; extern uint16_t frameCount; -extern uint8_t gfxFrameBuffer[6400]; +extern uint8_t gfxFrameBuffer[GFX_DECODER_FRAME_BUFFER_SIZE]; int32_t gfx_decoder_loadNextFrame(); int32_t gfx_decoder_handleLoop(); diff --git a/firmware/src/leds.cpp b/firmware/src/leds.cpp index 15112cd..e99c966 100644 --- a/firmware/src/leds.cpp +++ b/firmware/src/leds.cpp @@ -11,12 +11,9 @@ 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); - // there are glitches without this (maybe just due to breadboard...) asm volatile("nop \n nop \n nop"); gpio_put(pin, LOW); } @@ -34,42 +31,11 @@ inline void outputEnable(uint8_t pin, bool enable) { // we have COLOR_BITS-bit color depth, so 2^COLOR_BITS levels of brightness // we go from phase 0 to phase (COLOR_BITS-1) uint8_t brightnessPhase = 0; - -// delays in nanoseconds -#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(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 -uint8_t framebuffer[ROW_COUNT * COL_COUNT] __attribute__((aligned(32))) = {0}; +#if DEBUG_FRAMEBUFFER +uint8_t framebuffer[ROW_COUNT * COL_COUNT] = {0}; +#endif // Framebuffer encoded for fast PIO pixel pushing // There's one buffer for each of pixel's bit indices (aka brightness phases), @@ -81,13 +47,14 @@ uint8_t framebuffer[ROW_COUNT * COL_COUNT] __attribute__((aligned(32))) = {0}; // 1 bit (LSB) to indicate start of frame (1) or not (0) // remaining bits to indicate a number of shift register pulses // (again, 24 shift register stages per 20 rows, so there are placeholders) -uint32_t ledBuffer[8][ROW_COUNT * (COL_MODULES + 1)] = {0}; +uint32_t ledBuffer[COLOR_BITS][ROW_COUNT * (COL_MODULES + 1)] = {0}; bool ledBufferReady = false; void leds_set_framebuffer(uint8_t *buffer) { // TODO: Use a separate buffer, then copy to ledsBuffer to avoid tearing - for (int bi = 0; bi < 8; bi++) { - uint8_t bitPosition = 1 << bi; + #define UNUSED_BITS 8 - COLOR_BITS + for (int bi = 0; bi < COLOR_BITS; bi++) { + uint8_t bitPosition = 1 << (bi + UNUSED_BITS); for (int yModule = 0; yModule < ROW_MODULES; yModule++) { for (int moduleY = 0; moduleY < 20; moduleY++) { @@ -142,13 +109,17 @@ void leds_set_framebuffer(uint8_t *buffer) { } ledBufferReady = true; + #if DEBUG_FRAMEBUFFER // copy to framebuffer // TODO: mutex? double buffer? or something... memcpy(framebuffer, buffer, ROW_COUNT * COL_COUNT); + #endif } void leds_init() { + #if DEBUG_FRAMEBUFFER memset(framebuffer, 0, sizeof(framebuffer)); + #endif // disable output outputEnable(COL_OE, false); @@ -192,14 +163,9 @@ void main2() { } } -void leds_initPusher(); -void leds_initRowSelector(); -void leds_initDelay(); +void leds_initPIO(); void leds_initRenderer() { - leds_initPusher(); - leds_initRowSelector(); - leds_initDelay(); multicore_reset_core1(); multicore_launch_core1(main2); } @@ -341,3 +307,9 @@ void leds_initDelay() { pio_sm_init(pio, sm, offset, &config); pio_sm_set_enabled(pio, sm, true); } + +void leds_initPIO() { + leds_initPusher(); + leds_initRowSelector(); + leds_initDelay(); +} diff --git a/firmware/src/leds.h b/firmware/src/leds.h index bea1f32..2aa55fb 100644 --- a/firmware/src/leds.h +++ b/firmware/src/leds.h @@ -1,24 +1,8 @@ #pragma once #ifndef LEDS_H #define LEDS_H - #include - -#define COMMON_SER 21 -// #define COMMON_RCLK 23 -#define COMMON_SRCLR 25 - -// #define COL_SER 21 -#define COL_OE 22 -#define COL_RCLK 23 -#define COL_SRCLK 24 -// #define COL_SRCLR 25 - -// #define ROW_SER 14 -#define ROW_OE 13 -#define ROW_RCLK 20 -#define ROW_SRCLK 18 -// #define ROW_SRCLR 15 +#include "config.h" #if defined(COMMON_SER) && (defined(ROW_SER) || defined(COL_SER)) #error "COMMON_SER and ROW_SER/COL_SER cannot be defined at the same time" @@ -47,17 +31,9 @@ #define ROW_SRCLR COMMON_SRCLR #endif -#define ROW_MODULES 2 -#define ROW_COUNT ROW_MODULES * 20 -#define COL_MODULES 2 -#define COL_COUNT COL_MODULES * 20 - -#define COLOR_BITS 8 -#define FPS 30 +#define FPS MAX_FPS #define MS_PER_FRAME (1000 / FPS) -#define CPU_MHZ 125 -#define NS_PER_CYCLE (1000 / CPU_MHZ) void leds_init(); void leds_initRenderer(); diff --git a/firmware/src/life.cpp b/firmware/src/life.cpp index 2687f22..b5275f2 100644 --- a/firmware/src/life.cpp +++ b/firmware/src/life.cpp @@ -1,8 +1,9 @@ #include #include +#include "config.h" -#define CELLS_X 40 -#define CELLS_Y 40 +#define CELLS_X COL_MODULES * 20 +#define CELLS_Y ROW_MODULES * 20 #define CELL_COUNT (CELLS_X * CELLS_Y) bool cells[CELL_COUNT] = {0}; diff --git a/firmware/src/main.cpp b/firmware/src/main.cpp index f4dc8ae..04cd399 100644 --- a/firmware/src/main.cpp +++ b/firmware/src/main.cpp @@ -5,13 +5,13 @@ #include "leds.h" #include "gfx_decoder.h" #include "can2040.h" +#include "config.h" void loadVideo(size_t index); void setup() { leds_init(); - setupSDPins(); - pinMode(9, INPUT_PULLUP); + pinMode(NEXT_PIN, INPUT_PULLUP); delay(2000); Serial.begin(115200); @@ -20,24 +20,32 @@ void setup() { init_audio(); leds_initRenderer(); - // while (!isSDCardInserted()) { - // Serial.println("SD card not connected, waiting..."); - // delay(1000); - // } - // delay(100); + #if SD_HAS_DETECTION + while (!isSDCardInserted()) { + Serial.println("SD card not connected, waiting..."); + delay(1000); + } + delay(100); + #endif setupSD(); sd_loadPlaylist(); + #if DEBUG_TEST_FRAME + gfx_decoder_setTestFrame(); + #else loadVideo(0); - - // gfx_decoder_setTestFrame(); + #endif } size_t currentVideoIndex = 0; bool isLoaded = false; void loadVideo(size_t index) { + #if DEBUG_TEST_FRAME + return; + #endif + audio_stop(); sd_loadAudio(index); @@ -62,7 +70,7 @@ void nextSong() { } void loop() { - if (digitalRead(9) == LOW) { + if (digitalRead(NEXT_PIN) == LOW) { delay(100); nextSong(); delay(50); diff --git a/firmware/src/sd.cpp b/firmware/src/sd.cpp index 9ab3136..2c2e86d 100644 --- a/firmware/src/sd.cpp +++ b/firmware/src/sd.cpp @@ -3,24 +3,18 @@ #include "sd.h" #include "audio.h" #include "gfx_decoder.h" +#include "config.h" #include "hw_config.h" #include "ff.h" #include "f_util.h" -#define SD_DET_PIN 4 - -#define SD_PIN_SS 1 -#define SD_PIN_SCK 2 -#define SD_PIN_MOSI 3 -#define SD_PIN_MISO 0 - static spi_t spi = { .hw_inst = spi0, .miso_gpio = SD_PIN_MISO, .mosi_gpio = SD_PIN_MOSI, .sck_gpio = SD_PIN_SCK, - .baud_rate = 10 * 1000 * 1000, + .baud_rate = SD_CARD_BAUD_RATE, }; static sd_spi_if_t spi_if = { @@ -31,6 +25,10 @@ static sd_spi_if_t spi_if = { static sd_card_t sd_card = { .type = SD_IF_SPI, .spi_if_p = &spi_if, + #if SD_HAS_DETECTION + .use_card_detect = true, + .card_detect_gpio = SD_DET_PIN, + #endif }; size_t sd_get_num() { @@ -45,10 +43,6 @@ sd_card_t *sd_get_by_num(size_t num) { return nullptr; } -void setupSDPins() { - // TODO: Is that even needed if we use built-in SPI? -} - bool isSDCardInserted() { return digitalRead(SD_DET_PIN) == LOW; } @@ -296,9 +290,11 @@ bool sd_loadGfxBlob(size_t index) { // Returns size of frame read or -1 if error int32_t sd_loadNextFrame() { + #if DEBUG_FIRST_FRAME if (frameIdx > 0) { - // return -1; + return -1; } + #endif if (!gfxFile) { Serial.println("Gfx file not available"); diff --git a/firmware/src/sd.h b/firmware/src/sd.h index c243909..803b312 100644 --- a/firmware/src/sd.h +++ b/firmware/src/sd.h @@ -1,6 +1,5 @@ #pragma once -void setupSDPins(); void setupSD(); bool isSDCardInserted();