mirror of https://github.com/radex/radmatrix.git
Compare commits
6 Commits
634406bb2e
...
2cc5f3d6fc
Author | SHA1 | Date |
---|---|---|
radex | 2cc5f3d6fc | |
radex | 72045008f8 | |
radex | 20acc815de | |
radex | 6ba449a1a4 | |
radex | da893fd9de | |
radex | 62a69a4c6d |
|
@ -14,6 +14,5 @@ board = pico
|
||||||
framework = arduino
|
framework = arduino
|
||||||
board_build.core = earlephilhower
|
board_build.core = earlephilhower
|
||||||
board_build.filesystem_size = 0.5m
|
board_build.filesystem_size = 0.5m
|
||||||
lib_deps =
|
; lib_deps =
|
||||||
; khoih-prog/RP2040_SD@^1.0.1
|
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
#define AUDIO_PIN 2
|
#define AUDIO_PIN 2
|
||||||
|
|
||||||
#define AUDIO_RATE 22000.0f
|
#define AUDIO_RATE 22000.0f
|
||||||
#define BUFFER_LEN 512*2
|
#define BUFFER_LEN 512*4
|
||||||
#define BUFFER_LEN_MS (BUFFER_LEN / AUDIO_RATE) * 1000.0f
|
#define BUFFER_LEN_MS (BUFFER_LEN / AUDIO_RATE) * 1000.0f
|
||||||
|
|
||||||
extern uint8_t wav_buffer_0[BUFFER_LEN];
|
extern uint8_t wav_buffer_0[BUFFER_LEN];
|
||||||
|
|
|
@ -56,3 +56,52 @@ int32_t gfx_decoder_handleLoop() {
|
||||||
}
|
}
|
||||||
return 0;
|
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<<n color
|
||||||
|
for (int x = startX; x < startX + 10; x++) {
|
||||||
|
for (int y = startY + 5; y < startY + 10; y++) {
|
||||||
|
buffer[y * ROW_COUNT + x] = color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// box with 1<<n - 1 color for comparison
|
||||||
|
for (int x = startX; x < startX + 10; x++) {
|
||||||
|
for (int y = startY; y < startY + 5; y++) {
|
||||||
|
buffer[y * ROW_COUNT + x] = color - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// full color
|
||||||
|
for (int x = 30; x < ROW_COUNT; x++) {
|
||||||
|
for (int y = 20; y < 25; y++) {
|
||||||
|
buffer[y * ROW_COUNT + x] = 255;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// smooth gradient - lower range
|
||||||
|
for (int x = 0; x < ROW_COUNT; x++) {
|
||||||
|
for (int y = 30; y < 35; y++) {
|
||||||
|
buffer[y * ROW_COUNT + x] = x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// smooth gradient
|
||||||
|
float delta = 256 / (COL_COUNT);
|
||||||
|
for (int x = 0; x < ROW_COUNT; x++) {
|
||||||
|
for (int y = 35; y < 40; y++) {
|
||||||
|
buffer[y * ROW_COUNT + x] = (x + 1) * delta;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
leds_set_framebuffer(buffer);
|
||||||
|
}
|
||||||
|
|
|
@ -10,5 +10,6 @@ extern uint8_t gfxFrameBuffer[6400];
|
||||||
|
|
||||||
int32_t gfx_decoder_loadNextFrame();
|
int32_t gfx_decoder_loadNextFrame();
|
||||||
int32_t gfx_decoder_handleLoop();
|
int32_t gfx_decoder_handleLoop();
|
||||||
|
void gfx_decoder_setTestFrame();
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -11,6 +11,8 @@ uint pusher_sm = 255; // invalid
|
||||||
uint delay_sm = 255; // invalid
|
uint delay_sm = 255; // invalid
|
||||||
uint row_sm = 255; // invalid
|
uint row_sm = 255; // invalid
|
||||||
|
|
||||||
|
#define LEDS_PIO_CLKDIV 1
|
||||||
|
|
||||||
// NOTE: RCLK, SRCLK capture on *rising* edge
|
// NOTE: RCLK, SRCLK capture on *rising* edge
|
||||||
inline void pulsePin(uint8_t pin) {
|
inline void pulsePin(uint8_t pin) {
|
||||||
gpio_put(pin, HIGH);
|
gpio_put(pin, HIGH);
|
||||||
|
@ -34,16 +36,36 @@ inline void outputEnable(uint8_t pin, bool enable) {
|
||||||
uint8_t brightnessPhase = 0;
|
uint8_t brightnessPhase = 0;
|
||||||
|
|
||||||
// delays in nanoseconds
|
// 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] = {
|
uint32_t brightnessPhaseDelays[COLOR_BITS] = {
|
||||||
NS_TO_DELAY(50),
|
// NOTE: 100ns seems to be the minimum that's (barely) visible
|
||||||
NS_TO_DELAY(100),
|
/* 1 */ NS_TO_DELAY(170),
|
||||||
NS_TO_DELAY(200),
|
/* 2 */ NS_TO_DELAY(180),
|
||||||
NS_TO_DELAY(500),
|
/* 4 */ NS_TO_DELAY(210),
|
||||||
NS_TO_DELAY(1500),
|
/* 8 */ NS_TO_DELAY(540),
|
||||||
NS_TO_DELAY(6000),
|
/* 16 */ NS_TO_DELAY(2300), // x2
|
||||||
NS_TO_DELAY(20000),
|
/* 32 */ NS_TO_DELAY(3000), // x4
|
||||||
NS_TO_DELAY(60000),
|
/* 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
|
// NOTE: Alignment required to allow 4-byte reads
|
||||||
|
@ -102,18 +124,18 @@ void leds_set_framebuffer(uint8_t *buffer) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// set row shifting data
|
// set row shifting data
|
||||||
bool firstRow = y == (ROW_COUNT - 1);
|
bool firstRow = y == 0;
|
||||||
uint32_t rowPulses = 1;
|
uint32_t extraPulses = 0;
|
||||||
|
|
||||||
if (moduleY == 0) {
|
if (moduleY == 0) {
|
||||||
rowPulses++;
|
extraPulses++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (moduleY == 7 || moduleY == 14 || (moduleY == 0 && yModule != 0)) {
|
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;
|
ledBuffer[bi][outputYOffset + COL_MODULES] = rowData;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -183,11 +205,16 @@ void leds_initRenderer() {
|
||||||
multicore_launch_core1(main2);
|
multicore_launch_core1(main2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void leds_nextPhase();
|
||||||
|
|
||||||
void leds_render() {
|
void leds_render() {
|
||||||
if (!ledBufferReady) {
|
if (!ledBufferReady) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// next brightness phase
|
||||||
|
leds_nextPhase();
|
||||||
|
|
||||||
auto buffer = ledBuffer[brightnessPhase];
|
auto buffer = ledBuffer[brightnessPhase];
|
||||||
auto delayData = brightnessPhaseDelays[brightnessPhase];
|
auto delayData = brightnessPhaseDelays[brightnessPhase];
|
||||||
|
|
||||||
|
@ -209,9 +236,19 @@ void leds_render() {
|
||||||
// set delay data
|
// set delay data
|
||||||
pio_sm_put_blocking(leds_pio, delay_sm, delayData);
|
pio_sm_put_blocking(leds_pio, delay_sm, delayData);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// next brightness phase
|
void leds_nextPhase() {
|
||||||
brightnessPhase = (brightnessPhase + 1) % COLOR_BITS;
|
brightnessPhase++;
|
||||||
|
|
||||||
|
if (brightnessPhase == COLOR_BITS) {
|
||||||
|
brightnessPhase = 0;
|
||||||
|
ditheringPhase = (ditheringPhase + 1) % DITHERING_PHASES;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (ditheringPhase >= brightnessPhaseDithering[brightnessPhase]) {
|
||||||
|
brightnessPhase++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void leds_initPusher() {
|
void leds_initPusher() {
|
||||||
|
@ -222,7 +259,7 @@ void leds_initPusher() {
|
||||||
uint offset = pio_add_program(pio, &leds_px_pusher_program);
|
uint offset = pio_add_program(pio, &leds_px_pusher_program);
|
||||||
|
|
||||||
pio_sm_config config = leds_px_pusher_program_get_default_config(offset);
|
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
|
// Shift OSR to the right, autopull
|
||||||
sm_config_set_out_shift(&config, true, true, 32);
|
sm_config_set_out_shift(&config, true, true, 32);
|
||||||
|
@ -262,7 +299,7 @@ void leds_initRowSelector() {
|
||||||
uint offset = pio_add_program(pio, &leds_row_selector_program);
|
uint offset = pio_add_program(pio, &leds_row_selector_program);
|
||||||
|
|
||||||
pio_sm_config config = leds_row_selector_program_get_default_config(offset);
|
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
|
// Shift OSR to the right, autopull
|
||||||
sm_config_set_out_shift(&config, true, true, 32);
|
sm_config_set_out_shift(&config, true, true, 32);
|
||||||
|
@ -291,7 +328,7 @@ void leds_initDelay() {
|
||||||
uint offset = pio_add_program(pio, &leds_delay_program);
|
uint offset = pio_add_program(pio, &leds_delay_program);
|
||||||
|
|
||||||
pio_sm_config config = leds_delay_program_get_default_config(offset);
|
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
|
// Shift OSR to the right, autopull
|
||||||
sm_config_set_out_shift(&config, true, true, 32);
|
sm_config_set_out_shift(&config, true, true, 32);
|
||||||
|
|
|
@ -24,10 +24,10 @@
|
||||||
|
|
||||||
#define COLOR_BITS 8
|
#define COLOR_BITS 8
|
||||||
#define FPS 30
|
#define FPS 30
|
||||||
#define MS_PER_FRAME 1000 / FPS
|
#define MS_PER_FRAME (1000 / FPS)
|
||||||
|
|
||||||
#define CPU_MHZ 125
|
#define CPU_MHZ 125
|
||||||
#define NS_PER_CYCLE 1000 / CPU_MHZ
|
#define NS_PER_CYCLE (1000 / CPU_MHZ)
|
||||||
|
|
||||||
void leds_init();
|
void leds_init();
|
||||||
void leds_initRenderer();
|
void leds_initRenderer();
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
.define public irq_did_latch 0
|
.define public irq_did_latch 0
|
||||||
.define public irq_delaying 1
|
|
||||||
.define public irq_row_selected 1
|
.define public irq_row_selected 1
|
||||||
|
.define public irq_delaying 2
|
||||||
|
|
||||||
; TODO: check if delays can be lowered with a PCB
|
; TODO: check if delays can be lowered with a PCB
|
||||||
.define public srclk_0_delay 1
|
.define public srclk_0_delay 1
|
||||||
|
@ -48,7 +48,7 @@ entry_point:
|
||||||
.wrap_target
|
.wrap_target
|
||||||
; LSB=1 indicates first (bottom) row, ergo, high SER for the first pulse
|
; LSB=1 indicates first (bottom) row, ergo, high SER for the first pulse
|
||||||
out pins, 1 side 0 [srclk_0_delay]
|
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
|
out x, 31
|
||||||
loop:
|
loop:
|
||||||
; pulse SRCLK x times
|
; pulse SRCLK x times
|
||||||
|
|
|
@ -9,8 +9,8 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define irq_did_latch 0
|
#define irq_did_latch 0
|
||||||
#define irq_delaying 1
|
|
||||||
#define irq_row_selected 1
|
#define irq_row_selected 1
|
||||||
|
#define irq_delaying 2
|
||||||
#define srclk_0_delay 1
|
#define srclk_0_delay 1
|
||||||
#define srclk_1_delay 2
|
#define srclk_1_delay 2
|
||||||
#define rclk_1_delay 3
|
#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]
|
0x1a41, // 2: jmp x--, 1 side 1 [2]
|
||||||
0x7028, // 3: out x, 8 side 0
|
0x7028, // 3: out x, 8 side 0
|
||||||
0x0020, // 4: jmp !x, 0
|
0x0020, // 4: jmp !x, 0
|
||||||
0x2041, // 5: wait 0 irq, 1
|
0x2042, // 5: wait 0 irq, 2
|
||||||
0x20c1, // 6: wait 1 irq, 1
|
0x20c1, // 6: wait 1 irq, 1
|
||||||
0xc000, // 7: irq nowait 0
|
0xc000, // 7: irq nowait 0
|
||||||
0xe301, // 8: set pins, 1 [3]
|
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[] = {
|
static const uint16_t leds_delay_program_instructions[] = {
|
||||||
// .wrap_target
|
// .wrap_target
|
||||||
0x20c0, // 0: wait 1 irq, 0
|
0x20c0, // 0: wait 1 irq, 0
|
||||||
0xc001, // 1: irq nowait 1
|
0xc002, // 1: irq nowait 2
|
||||||
0x6020, // 2: out x, 32
|
0x6020, // 2: out x, 32
|
||||||
0x1043, // 3: jmp x--, 3 side 0
|
0x1043, // 3: jmp x--, 3 side 0
|
||||||
0xd841, // 4: irq clear 1 side 1
|
0xd842, // 4: irq clear 2 side 1
|
||||||
// .wrap
|
// .wrap
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,8 @@ void setup() {
|
||||||
sd_loadPlaylist();
|
sd_loadPlaylist();
|
||||||
|
|
||||||
loadVideo(0);
|
loadVideo(0);
|
||||||
|
|
||||||
|
// gfx_decoder_setTestFrame();
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t currentVideoIndex = 0;
|
size_t currentVideoIndex = 0;
|
||||||
|
@ -61,7 +63,9 @@ void nextSong() {
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
if (digitalRead(4) == LOW) {
|
if (digitalRead(4) == LOW) {
|
||||||
|
delay(100);
|
||||||
nextSong();
|
nextSong();
|
||||||
|
delay(50);
|
||||||
}
|
}
|
||||||
|
|
||||||
// if (Serial.available() > 0) {
|
// if (Serial.available() > 0) {
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <SPI.h>
|
#include <SPI.h>
|
||||||
// #include <RP2040_SD.h>
|
|
||||||
#include "sd.h"
|
#include "sd.h"
|
||||||
#include "audio.h"
|
#include "audio.h"
|
||||||
#include "gfx_decoder.h"
|
#include "gfx_decoder.h"
|
||||||
|
@ -11,17 +10,22 @@
|
||||||
|
|
||||||
#define SD_DET_PIN 28
|
#define SD_DET_PIN 28
|
||||||
|
|
||||||
|
#define SD_PIN_SS 17
|
||||||
|
#define SD_PIN_SCK 18
|
||||||
|
#define SD_PIN_MOSI 19
|
||||||
|
#define SD_PIN_MISO 16
|
||||||
|
|
||||||
static spi_t spi = {
|
static spi_t spi = {
|
||||||
.hw_inst = spi0,
|
.hw_inst = spi0,
|
||||||
.miso_gpio = 4,
|
.miso_gpio = SD_PIN_MISO,
|
||||||
.mosi_gpio = 3,
|
.mosi_gpio = SD_PIN_MOSI,
|
||||||
.sck_gpio = 2,
|
.sck_gpio = SD_PIN_SCK,
|
||||||
.baud_rate = 10 * 1000 * 1000,
|
.baud_rate = 10 * 1000 * 1000,
|
||||||
};
|
};
|
||||||
|
|
||||||
static sd_spi_if_t spi_if = {
|
static sd_spi_if_t spi_if = {
|
||||||
.spi = &spi,
|
.spi = &spi,
|
||||||
.ss_gpio = 7,
|
.ss_gpio = SD_PIN_SS,
|
||||||
};
|
};
|
||||||
|
|
||||||
static sd_card_t sd_card = {
|
static sd_card_t sd_card = {
|
||||||
|
@ -29,212 +33,85 @@ static sd_card_t sd_card = {
|
||||||
.spi_if_p = &spi_if,
|
.spi_if_p = &spi_if,
|
||||||
};
|
};
|
||||||
|
|
||||||
void sd_test() {
|
size_t sd_get_num() {
|
||||||
FATFS fs;
|
return 1;
|
||||||
FRESULT fr = f_mount(&fs, "", 1);
|
|
||||||
if (FR_OK != fr) panic("f_mount error: %s (%d)\n", FRESULT_str(fr), fr);
|
|
||||||
FIL fil;
|
|
||||||
const char* const filename = "filename.txt";
|
|
||||||
fr = f_open(&fil, filename, FA_OPEN_APPEND | FA_WRITE);
|
|
||||||
if (FR_OK != fr && FR_EXIST != fr)
|
|
||||||
panic("f_open(%s) error: %s (%d)\n", filename, FRESULT_str(fr), fr);
|
|
||||||
if (f_printf(&fil, "Hello, world!\n") < 0) {
|
|
||||||
printf("f_printf failed\n");
|
|
||||||
}
|
|
||||||
fr = f_close(&fil);
|
|
||||||
if (FR_OK != fr) {
|
|
||||||
printf("f_close error: %s (%d)\n", FRESULT_str(fr), fr);
|
|
||||||
}
|
|
||||||
f_unmount("");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
sd_card_t *sd_get_by_num(size_t num) {
|
||||||
#if PIN_SPI_SS != 17
|
if (num == 0) {
|
||||||
#error "PIN_SPI_SS must be 17"
|
return &sd_card;
|
||||||
#endif
|
}
|
||||||
#if PIN_SPI_SCK != 18
|
|
||||||
#error "PIN_SPI_SCK must be 18"
|
return nullptr;
|
||||||
#endif
|
}
|
||||||
#if PIN_SPI_MOSI != 19
|
|
||||||
#error "PIN_SPI_MOSI must be 19"
|
|
||||||
#endif
|
|
||||||
#if PIN_SPI_MISO != 16
|
|
||||||
#error "PIN_SPI_MISO must be 16"
|
|
||||||
#endif
|
|
||||||
*/
|
|
||||||
|
|
||||||
void setupSDPins() {
|
void setupSDPins() {
|
||||||
// TODO: Is that even needed if we use built-in SPI?
|
// TODO: Is that even needed if we use built-in SPI?
|
||||||
/*
|
|
||||||
pinMode(PIN_SPI_MISO, INPUT);
|
|
||||||
pinMode(PIN_SPI_SS, OUTPUT);
|
|
||||||
digitalWrite(PIN_SPI_SS, HIGH);
|
|
||||||
pinMode(PIN_SPI_SCK, OUTPUT);
|
|
||||||
pinMode(PIN_SPI_MOSI, OUTPUT);
|
|
||||||
pinMode(SD_DET_PIN, INPUT_PULLUP);
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isSDCardInserted() {
|
bool isSDCardInserted() {
|
||||||
return digitalRead(SD_DET_PIN) == LOW;
|
return digitalRead(SD_DET_PIN) == LOW;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
#define CHECK_RESULT(result, caller) \
|
||||||
void printSDConfig();
|
if (result != FR_OK) { \
|
||||||
void testSDCard();
|
Serial.print(caller); \
|
||||||
void printSDStats();
|
Serial.print(" error: "); \
|
||||||
void printDirectory(File dir, int numTabs);
|
Serial.print(FRESULT_str(result)); \
|
||||||
*/
|
Serial.print(" ("); \
|
||||||
|
Serial.print(result); \
|
||||||
|
Serial.println(")"); \
|
||||||
|
}
|
||||||
|
|
||||||
|
FATFS *fs;
|
||||||
|
|
||||||
void setupSD() {
|
void setupSD() {
|
||||||
/*
|
Serial.println("Initializing SD card...");
|
||||||
SPI.begin();
|
|
||||||
|
|
||||||
// printSDConfig();
|
sd_init_driver();
|
||||||
|
|
||||||
if (!SD.begin(20000000, PIN_SPI_SS)) {
|
fs = (FATFS*) malloc(sizeof(FATFS));
|
||||||
Serial.println("SD Initialization failed!");
|
FRESULT result = f_mount(fs, "", 1);
|
||||||
// Serial.print("Error code: ");
|
CHECK_RESULT(result, "f_mount");
|
||||||
// Serial.println(SD.card.errorCode(), HEX);
|
|
||||||
while (true) {}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Serial.println("SD Initialization done");
|
Serial.println("SD Initialization done");
|
||||||
|
|
||||||
// testSDCard();
|
|
||||||
// printSDStats(SD.volume);
|
|
||||||
|
|
||||||
// File root = SD.open("/");
|
|
||||||
// printDirectory(root, 0);
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
void printSDConfig() {
|
|
||||||
Serial.println(BOARD_NAME);
|
|
||||||
Serial.println(RP2040_SD_VERSION);
|
|
||||||
|
|
||||||
Serial.print("Initializing SD card with SS = ");
|
|
||||||
Serial.println(PIN_SPI_SS);
|
|
||||||
Serial.print("SCK = ");
|
|
||||||
Serial.println(PIN_SPI_SCK);
|
|
||||||
Serial.print("MOSI = ");
|
|
||||||
Serial.println(PIN_SPI_MOSI);
|
|
||||||
Serial.print("MISO = ");
|
|
||||||
Serial.println(PIN_SPI_MISO);
|
|
||||||
}
|
|
||||||
|
|
||||||
void testSDCard() {
|
|
||||||
File myFile = SD.open("test.txt", FILE_WRITE);
|
|
||||||
|
|
||||||
// if the file opened okay, write to it:
|
|
||||||
if (myFile) {
|
|
||||||
Serial.print("Writing to test.txt...");
|
|
||||||
myFile.println("testing 1, 2, 3.");
|
|
||||||
myFile.close();
|
|
||||||
Serial.println("done.");
|
|
||||||
} else {
|
|
||||||
Serial.println("error opening test.txt");
|
|
||||||
}
|
|
||||||
|
|
||||||
// re-open the file for reading:
|
|
||||||
myFile = SD.open("test.txt");
|
|
||||||
if (myFile) {
|
|
||||||
Serial.println("test.txt:");
|
|
||||||
|
|
||||||
while (myFile.available()) {
|
|
||||||
Serial.write(myFile.read());
|
|
||||||
}
|
|
||||||
myFile.close();
|
|
||||||
} else {
|
|
||||||
Serial.println("error opening test.txt");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void printDirectory(File dir, int numTabs) {
|
|
||||||
while (true) {
|
|
||||||
File entry = dir.openNextFile();
|
|
||||||
|
|
||||||
if (!entry) {
|
|
||||||
// no more files
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (uint8_t i = 0; i < numTabs; i++) {
|
|
||||||
Serial.print('\t');
|
|
||||||
}
|
|
||||||
|
|
||||||
Serial.print(entry.name());
|
|
||||||
|
|
||||||
if (entry.isDirectory()) {
|
|
||||||
Serial.println("/");
|
|
||||||
printDirectory(entry, numTabs + 1);
|
|
||||||
} else {
|
|
||||||
// files have sizes, directories do not
|
|
||||||
Serial.print("\t\t");
|
|
||||||
Serial.println(entry.size(), DEC);
|
|
||||||
}
|
|
||||||
entry.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void printSDStats(RP2040_SdVolume volume) {
|
|
||||||
Serial.print("Clusters: ");
|
|
||||||
Serial.println(volume.clusterCount());
|
|
||||||
Serial.print("Blocks x Cluster: ");
|
|
||||||
Serial.println(volume.blocksPerCluster());
|
|
||||||
|
|
||||||
Serial.print("Total Blocks: ");
|
|
||||||
Serial.println(volume.blocksPerCluster() * volume.clusterCount());
|
|
||||||
Serial.println();
|
|
||||||
|
|
||||||
// print the type and size of the first FAT-type volume
|
|
||||||
uint32_t volumesize;
|
|
||||||
Serial.print("Volume type is: FAT");
|
|
||||||
Serial.println(volume.fatType(), DEC);
|
|
||||||
|
|
||||||
volumesize = volume.blocksPerCluster(); // clusters are collections of blocks
|
|
||||||
volumesize *= volume.clusterCount(); // we'll have a lot of clusters
|
|
||||||
volumesize /= 2; // SD card blocks are always 512 bytes (2 blocks are 1 KB)
|
|
||||||
Serial.print("Volume size (KB): ");
|
|
||||||
Serial.println(volumesize);
|
|
||||||
Serial.print("Volume size (MB): ");
|
|
||||||
volumesize /= 1024;
|
|
||||||
Serial.println(volumesize);
|
|
||||||
Serial.print("Volume size (GB): ");
|
|
||||||
Serial.println((float)volumesize / 1024.0);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
String playlist[128] = {};
|
String playlist[128] = {};
|
||||||
size_t playlistSize = 0;
|
size_t playlistSize = 0;
|
||||||
|
|
||||||
void sd_loadPlaylist() {
|
void sd_loadPlaylist() {
|
||||||
/*
|
|
||||||
auto path = "video/playlist.txt";
|
auto path = "video/playlist.txt";
|
||||||
|
|
||||||
if (!SD.exists(path)) {
|
FIL playlistFile;
|
||||||
Serial.println("Could not find playlist for videos :(");
|
FRESULT result = f_open(&playlistFile, path, FA_READ);
|
||||||
return;
|
CHECK_RESULT(result, "playlist file open");
|
||||||
}
|
|
||||||
|
|
||||||
auto playlistFile = SD.open(path, FILE_READ);
|
|
||||||
Serial.println("Playlist file opened");
|
Serial.println("Playlist file opened");
|
||||||
|
|
||||||
char playlist_buffer[512];
|
char playlist_buffer[512];
|
||||||
auto fileSize = playlistFile.size();
|
auto fileSize = f_size(&playlistFile);
|
||||||
|
|
||||||
if (fileSize > sizeof(playlist_buffer)) {
|
if (fileSize > sizeof(playlist_buffer)) {
|
||||||
Serial.print("Playlist file too large, max: ");
|
Serial.print("Playlist file too large, max: ");
|
||||||
Serial.println(sizeof(playlist_buffer));
|
Serial.println(sizeof(playlist_buffer));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (playlistFile.read(&playlist_buffer, sizeof(playlist_buffer)) != fileSize) {
|
unsigned int bytesRead;
|
||||||
Serial.println("Could not read playlist file");
|
result = f_read(&playlistFile, &playlist_buffer, fileSize, &bytesRead);
|
||||||
|
CHECK_RESULT(result, "playlist file read");
|
||||||
|
|
||||||
|
if (bytesRead != fileSize) {
|
||||||
|
Serial.print("playlist file read error: read ");
|
||||||
|
Serial.print(bytesRead);
|
||||||
|
Serial.print(" bytes, expected ");
|
||||||
|
Serial.println(fileSize);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
playlistFile.close();
|
result = f_close(&playlistFile);
|
||||||
|
CHECK_RESULT(result, "playlist file close");
|
||||||
|
|
||||||
Serial.println("Parsing playlist...");
|
Serial.println("Parsing playlist...");
|
||||||
|
|
||||||
|
@ -271,13 +148,11 @@ void sd_loadPlaylist() {
|
||||||
Serial.print(": ");
|
Serial.print(": ");
|
||||||
Serial.println(playlist[i]);
|
Serial.println(playlist[i]);
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// File audioFile;
|
FIL *audioFile;
|
||||||
|
|
||||||
void sd_loadAudio(size_t index) {
|
void sd_loadAudio(size_t index) {
|
||||||
/*
|
|
||||||
if (index >= playlistSize) {
|
if (index >= playlistSize) {
|
||||||
Serial.println("Index out of range");
|
Serial.println("Index out of range");
|
||||||
return;
|
return;
|
||||||
|
@ -285,37 +160,42 @@ void sd_loadAudio(size_t index) {
|
||||||
|
|
||||||
auto path = "video/" + playlist[index] + "/audio.bin";
|
auto path = "video/" + playlist[index] + "/audio.bin";
|
||||||
|
|
||||||
if (!SD.exists(path)) {
|
FRESULT result;
|
||||||
Serial.println("Audio not found :(");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (audioFile) {
|
if (audioFile) {
|
||||||
audioFile.close();
|
result = f_close(audioFile);
|
||||||
|
CHECK_RESULT(result, "audio file close");
|
||||||
|
free(audioFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
audioFile = SD.open(path, FILE_READ);
|
audioFile = (FIL*) malloc(sizeof(FIL));
|
||||||
|
result = f_open(audioFile, path.c_str(), FA_READ);
|
||||||
|
CHECK_RESULT(result, "audio file open");
|
||||||
|
|
||||||
Serial.println("Audio file opened");
|
Serial.println("Audio file opened");
|
||||||
|
|
||||||
audio_stop();
|
audio_stop();
|
||||||
|
|
||||||
// load two buffers' worth of audio
|
// load two buffers' worth of audio
|
||||||
if (audioFile.read(&wav_buffer_0, BUFFER_LEN) < BUFFER_LEN) {
|
unsigned int bytesRead;
|
||||||
|
result = f_read(audioFile, &wav_buffer_0, BUFFER_LEN, &bytesRead);
|
||||||
|
CHECK_RESULT(result, "first audio sample");
|
||||||
|
if (bytesRead < BUFFER_LEN) {
|
||||||
Serial.println("Could not read first sample");
|
Serial.println("Could not read first sample");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (audioFile.read(&wav_buffer_1, BUFFER_LEN) < BUFFER_LEN) {
|
result = f_read(audioFile, &wav_buffer_1, BUFFER_LEN, &bytesRead);
|
||||||
|
CHECK_RESULT(result, "second audio sample");
|
||||||
|
if (bytesRead < BUFFER_LEN) {
|
||||||
Serial.println("Could not read second sample");
|
Serial.println("Could not read second sample");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
audio_start();
|
audio_start();
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void sd_loadNextAudio() {
|
void sd_loadNextAudio() {
|
||||||
/*
|
|
||||||
if (!next_buffer_requested) {
|
if (!next_buffer_requested) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -323,11 +203,14 @@ void sd_loadNextAudio() {
|
||||||
|
|
||||||
auto b4 = millis();
|
auto b4 = millis();
|
||||||
auto next_buffer = wav_buffer1_active ? &wav_buffer_0 : &wav_buffer_1;
|
auto next_buffer = wav_buffer1_active ? &wav_buffer_0 : &wav_buffer_1;
|
||||||
auto bytesRead = audioFile.read(next_buffer, BUFFER_LEN);
|
|
||||||
|
FRESULT result;
|
||||||
|
unsigned int bytesRead;
|
||||||
|
|
||||||
|
result = f_read(audioFile, next_buffer, BUFFER_LEN, &bytesRead);
|
||||||
|
CHECK_RESULT(result, "audio sample");
|
||||||
|
|
||||||
if (bytesRead < BUFFER_LEN) {
|
if (bytesRead < BUFFER_LEN) {
|
||||||
// Serial.println("End of audio file, rewinding...");
|
|
||||||
// audioFile.seek(0);
|
|
||||||
Serial.println("End of audio.");
|
Serial.println("End of audio.");
|
||||||
audio_stop();
|
audio_stop();
|
||||||
} else {
|
} else {
|
||||||
|
@ -337,14 +220,11 @@ void sd_loadNextAudio() {
|
||||||
Serial.print(" bytes from audio file in ");
|
Serial.print(" bytes from audio file in ");
|
||||||
Serial.print(millis() - b4);
|
Serial.print(millis() - b4);
|
||||||
Serial.println("ms");
|
Serial.println("ms");
|
||||||
* /
|
*/
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool sd_loadGfxFrameLengths(size_t index) {
|
bool sd_loadGfxFrameLengths(size_t index) {
|
||||||
return false;
|
|
||||||
/*
|
|
||||||
if (index >= playlistSize) {
|
if (index >= playlistSize) {
|
||||||
Serial.println("Index out of range");
|
Serial.println("Index out of range");
|
||||||
return false;
|
return false;
|
||||||
|
@ -352,39 +232,42 @@ bool sd_loadGfxFrameLengths(size_t index) {
|
||||||
|
|
||||||
auto path = "video/" + playlist[index] + "/gfx_len.bin";
|
auto path = "video/" + playlist[index] + "/gfx_len.bin";
|
||||||
|
|
||||||
if (!SD.exists(path)) {
|
FIL lengthsFile;
|
||||||
Serial.println("Frame lengths file not found :(");
|
FRESULT result = f_open(&lengthsFile, path.c_str(), FA_READ);
|
||||||
return false;
|
CHECK_RESULT(result, "frame lengths file open");
|
||||||
}
|
|
||||||
|
|
||||||
auto lengthsFile = SD.open(path, FILE_READ);
|
auto fileSize = f_size(&lengthsFile);
|
||||||
auto fileSize = lengthsFile.size();
|
|
||||||
|
|
||||||
if (fileSize > sizeof(gfxFrameLengthsBuffer)) {
|
if (fileSize > sizeof(gfxFrameLengthsBuffer)) {
|
||||||
Serial.println("Frame lengths file too large");
|
Serial.println("Frame lengths file too large");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
Serial.println(fileSize);
|
||||||
frameCount = fileSize / sizeof(uint16_t);
|
frameCount = fileSize / sizeof(uint16_t);
|
||||||
|
|
||||||
while (lengthsFile.available()) {
|
unsigned int bytesRead;
|
||||||
lengthsFile.read(&gfxFrameLengthsBuffer, sizeof(gfxFrameLengthsBuffer));
|
result = f_read(&lengthsFile, &gfxFrameLengthsBuffer, fileSize, &bytesRead);
|
||||||
|
CHECK_RESULT(result, "playlist file read");
|
||||||
|
|
||||||
|
if (bytesRead != fileSize) {
|
||||||
|
Serial.print("frame lengths file read error: read ");
|
||||||
|
Serial.print(bytesRead);
|
||||||
|
Serial.print(" bytes, expected ");
|
||||||
|
Serial.println(fileSize);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
lengthsFile.close();
|
result = f_close(&lengthsFile);
|
||||||
Serial.println("Done reading frame lengths");
|
CHECK_RESULT(result, "frame lengths file close");
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// File gfxFile;
|
FIL *gfxFile;
|
||||||
|
|
||||||
uint16_t frameIdx = 0;
|
uint16_t frameIdx = 0;
|
||||||
|
|
||||||
bool sd_loadGfxBlob(size_t index) {
|
bool sd_loadGfxBlob(size_t index) {
|
||||||
return false;
|
|
||||||
/*
|
|
||||||
if (index >= playlistSize) {
|
if (index >= playlistSize) {
|
||||||
Serial.println("Index out of range");
|
Serial.println("Index out of range");
|
||||||
return false;
|
return false;
|
||||||
|
@ -392,28 +275,32 @@ bool sd_loadGfxBlob(size_t index) {
|
||||||
|
|
||||||
auto path = "video/" + playlist[index] + "/gfx.bin";
|
auto path = "video/" + playlist[index] + "/gfx.bin";
|
||||||
|
|
||||||
if (!SD.exists(path)) {
|
FRESULT result;
|
||||||
Serial.println("Gfx blob file not found :(");
|
|
||||||
return false;
|
if (gfxFile) {
|
||||||
|
result = f_close(gfxFile);
|
||||||
|
CHECK_RESULT(result, "gfx file close");
|
||||||
|
free(gfxFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
gfxFile = SD.open(path, FILE_READ);
|
gfxFile = (FIL*) malloc(sizeof(FIL));
|
||||||
|
result = f_open(gfxFile, path.c_str(), FA_READ);
|
||||||
|
CHECK_RESULT(result, "gfx blob file open");
|
||||||
|
|
||||||
Serial.println("Opened video frames");
|
Serial.println("Opened video frames");
|
||||||
|
|
||||||
frameIdx = 0;
|
frameIdx = 0;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns size of frame read or -1 if error
|
// Returns size of frame read or -1 if error
|
||||||
int32_t sd_loadNextFrame() {
|
int32_t sd_loadNextFrame() {
|
||||||
return -1;
|
|
||||||
/*
|
|
||||||
if (frameIdx > 0) {
|
if (frameIdx > 0) {
|
||||||
// return -1;
|
// return -1;
|
||||||
}
|
}
|
||||||
if (!gfxFile || !gfxFile.available()) {
|
|
||||||
|
if (!gfxFile) {
|
||||||
Serial.println("Gfx file not available");
|
Serial.println("Gfx file not available");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -425,14 +312,19 @@ int32_t sd_loadNextFrame() {
|
||||||
|
|
||||||
// get size of frame png
|
// get size of frame png
|
||||||
auto frameSize = gfxFrameLengthsBuffer[frameIdx];
|
auto frameSize = gfxFrameLengthsBuffer[frameIdx];
|
||||||
|
|
||||||
if (frameSize > sizeof(gfxFrameBuffer)) {
|
if (frameSize > sizeof(gfxFrameBuffer)) {
|
||||||
Serial.print("Frame too large: ");
|
Serial.print("Frame too large: ");
|
||||||
Serial.println(frameSize);
|
Serial.println(frameSize);
|
||||||
|
while (true) {}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// read data
|
// read data
|
||||||
auto bytesRead = gfxFile.read(&gfxFrameBuffer, frameSize);
|
unsigned int bytesRead;
|
||||||
|
FRESULT result = f_read(gfxFile, &gfxFrameBuffer, frameSize, &bytesRead);
|
||||||
|
CHECK_RESULT(result, "playlist file read");
|
||||||
|
|
||||||
if (bytesRead < frameSize) {
|
if (bytesRead < frameSize) {
|
||||||
Serial.println("Could not read the entire frame");
|
Serial.println("Could not read the entire frame");
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -440,9 +332,6 @@ int32_t sd_loadNextFrame() {
|
||||||
|
|
||||||
// increment
|
// increment
|
||||||
if (frameIdx == frameCount - 1) {
|
if (frameIdx == frameCount - 1) {
|
||||||
// Serial.println("Last frame, rewinding...");
|
|
||||||
// gfxFile.seek(0);
|
|
||||||
// frameIdx = 0;
|
|
||||||
Serial.println("Last frame, next video!");
|
Serial.println("Last frame, next video!");
|
||||||
return -2;
|
return -2;
|
||||||
} else {
|
} else {
|
||||||
|
@ -450,5 +339,4 @@ int32_t sd_loadNextFrame() {
|
||||||
}
|
}
|
||||||
|
|
||||||
return frameSize;
|
return frameSize;
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue