Some checks failed
check / check (push) Has been cancelled
First commit of the repo. Contents beyond the v0.1 simulator and layout: - Full Pico 2 W firmware (firmware/app, drivers, engine port, host test). - BANK page and 404 mode A/B removed; learned pads play exactly what was learned. - 404/MiniFreak renamed to KIT/SYNTH throughout (jacks, code, sim, docs, layout labels; wiring rebuilt). - ROLL returns to page select after a re-roll; a freshly learned track turns on. - LCD contrast 66. - ROOT and SCALE pages: 21 scales, AUTO = the seed picks; global and sticky. Intended music change: the scale table grew from 8 to 21 and AUTO picks from all of them, so every seed's scale changed. Vectors regenerated with npm run vectors, plus new key-override cases. Flash record is now version 2. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
161 lines
6.3 KiB
C
161 lines
6.3 KiB
C
/* Seedbox bring-up: exercises every part of the board and reports over USB serial (tio /dev/ttyACM0).
|
|
- LED blinks once a second (on the Pico 2 W the LED hangs off the wireless chip, hence cyw43_arch).
|
|
- Test notes, alternating every half second: OUT KIT ch 1 note 36, OUT SYNTH ch 1 note 60.
|
|
- MIDI IN bytes are printed in hex.
|
|
- Every button, the encoder, its push, SEED, RUN and the pots are printed when they change.
|
|
- The LCD shows live values at the bench contrast (LCD_VOP_DEFAULT).
|
|
- At boot the engine renders three golden cases and checks their checksums on the Pico. */
|
|
#include <stdio.h>
|
|
#include "pico/stdlib.h"
|
|
#include "pico/cyw43_arch.h"
|
|
#include "engine.h"
|
|
#include "midi_uart.h"
|
|
#include "pcd8544.h"
|
|
#include "inputs.h"
|
|
#include "encoder.h"
|
|
#include "pots.h"
|
|
|
|
static const char *const KEY_NAME[12] = { "KICK", "KICK 2", "SNARE", "(1,4)", "HAT", "HAT 16", "PERC", "GHOST", "BASS", "MEL A", "MEL B", "PAD" };
|
|
static const char *const SW_NAME[3] = { "SEED", "RUN", "PUSH" };
|
|
|
|
/* engine self-test: golden cases from test/vectors (8 bars each) */
|
|
typedef struct { uint32_t seed; int gen, energy, drift; uint32_t checksum; } golden_t;
|
|
static const golden_t GOLDEN[3] = {
|
|
{ 0x5EEDB0C5, 0, 64, 32, 0x4F5DE957 }, /* 5EEDB0C5_s0-0_g0_e64_d32 */
|
|
{ 0x5EEDB0C5, 3, 127, 0, 0x29C313A5 }, /* 5EEDB0C5_s0-0_g3_e127_d0, dense */
|
|
{ 0x5EEDB0C5, 4, 64, 32, 0x8C01C1F9 }, /* 5EEDB0C5_s0-0_g4_e64_d32, AMENIZER */
|
|
};
|
|
static int engine_ok;
|
|
static uint32_t engine_worst_us;
|
|
static uint32_t engine_got[3];
|
|
|
|
static void engine_selftest(void){
|
|
static sb_bar bar; /* static: too big for the stack */
|
|
int c, b, i;
|
|
engine_ok = 1; engine_worst_us = 0;
|
|
for(c = 0; c < 3; c++){
|
|
sb_state st = { GOLDEN[c].seed, {0, 0} };
|
|
sb_params p = { GOLDEN[c].gen, GOLDEN[c].energy, GOLDEN[c].drift, 0, -1, -1 };
|
|
uint32_t h = SB_FNV_INIT;
|
|
for(b = 0; b < 8; b++){
|
|
uint64_t t0 = time_us_64();
|
|
uint32_t dt;
|
|
sb_render_bar(&st, &p, b, &bar);
|
|
dt = (uint32_t)(time_us_64() - t0);
|
|
if(dt > engine_worst_us) engine_worst_us = dt;
|
|
for(i = 0; i < bar.n; i++) h = sb_checksum_event(h, &bar.ev[i]);
|
|
}
|
|
engine_got[c] = h;
|
|
if(h != GOLDEN[c].checksum) engine_ok = 0;
|
|
}
|
|
}
|
|
|
|
static void print_banner(void){
|
|
int c;
|
|
printf("\n== Seedbox bring-up ==\n");
|
|
for(c = 0; c < 3; c++)
|
|
printf("engine gen %d e%d d%d: checksum 0x%08lX, want 0x%08lX %s\n", GOLDEN[c].gen, GOLDEN[c].energy, GOLDEN[c].drift,
|
|
(unsigned long)engine_got[c], (unsigned long)GOLDEN[c].checksum, engine_got[c] == GOLDEN[c].checksum ? "OK" : "FAIL");
|
|
printf("engine worst bar render: %lu us\n", (unsigned long)engine_worst_us);
|
|
printf("test notes: OUT KIT ch1 note 36, OUT SYNTH ch1 note 60, alternating every 500 ms\n");
|
|
printf("LCD contrast %d; encoder AB now %d (3 = at rest), + = clockwise\n\n", LCD_VOP_DEFAULT, encoder_ab());
|
|
}
|
|
|
|
int main(void){
|
|
char line[24];
|
|
uint32_t prev_in = 0, now_in;
|
|
int32_t prev_enc = 0, enc;
|
|
int prev_pot[2] = {-1, -1}, led = 0, was_connected = 0, i;
|
|
int last_key = -1, last_in[3] = {-1, -1, -1};
|
|
int note_kit_off = -1, note_synth_off = -1; /* ms counters until note-off, -1 = none */
|
|
uint32_t ms = 0;
|
|
absolute_time_t next;
|
|
int have_led;
|
|
|
|
stdio_init_all();
|
|
midi_init();
|
|
inputs_init();
|
|
encoder_init();
|
|
pots_init();
|
|
lcd_init(LCD_VOP_DEFAULT);
|
|
lcd_text(0, 0, "SEEDBOX TEST");
|
|
lcd_text(0, 8, "ENGINE...");
|
|
lcd_flush();
|
|
have_led = cyw43_arch_init() == 0;
|
|
engine_selftest();
|
|
|
|
next = get_absolute_time();
|
|
for(;;){
|
|
next = delayed_by_us(next, 1000);
|
|
sleep_until(next);
|
|
ms++;
|
|
|
|
if(stdio_usb_connected() && !was_connected) print_banner();
|
|
was_connected = stdio_usb_connected();
|
|
|
|
/* buttons and switches */
|
|
now_in = inputs_scan();
|
|
if(now_in != prev_in){
|
|
for(i = 0; i < IN_COUNT; i++){
|
|
uint32_t bit = 1u << i;
|
|
if((now_in ^ prev_in) & bit){
|
|
int down = (now_in & bit) != 0;
|
|
if(i < IN_MATRIX){ printf("key %-6s %s\n", KEY_NAME[i], down ? "down" : "up"); if(down) last_key = i; }
|
|
else if(i == IN_RUN) printf("RUN %s\n", down ? "on" : "off");
|
|
else printf("%s %s\n", SW_NAME[i - IN_SEED], down ? "down" : "up");
|
|
}
|
|
}
|
|
prev_in = now_in;
|
|
}
|
|
|
|
/* encoder */
|
|
enc = encoder_count();
|
|
if(enc != prev_enc){
|
|
printf("encoder click %ld (%+ld), raw edges %ld\n", (long)enc, (long)(enc - prev_enc), (long)encoder_steps());
|
|
prev_enc = enc;
|
|
}
|
|
|
|
/* MIDI IN */
|
|
for(;;){
|
|
int b = midi_in_read();
|
|
if(b < 0) break;
|
|
printf("midi in %02X\n", b);
|
|
last_in[0] = last_in[1]; last_in[1] = last_in[2]; last_in[2] = b;
|
|
}
|
|
|
|
/* pots, every 10 ms */
|
|
if(ms % 10 == 0){
|
|
pots_update();
|
|
for(i = 0; i < 2; i++) if(pot_value(i) != prev_pot[i]){
|
|
prev_pot[i] = pot_value(i);
|
|
printf("%s %d (raw %d)\n", i == POT_ENERGY ? "ENERGY" : "DRIFT", pot_value(i), pot_raw(i));
|
|
}
|
|
}
|
|
|
|
/* test notes and LED */
|
|
if(note_kit_off > 0 && --note_kit_off == 0){ midi_note_off(MIDI_KIT, 0, 36); note_kit_off = -1; }
|
|
if(note_synth_off > 0 && --note_synth_off == 0){ midi_note_off(MIDI_SYNTH, 0, 60); note_synth_off = -1; }
|
|
if(ms % 1000 == 0){ midi_note_on(MIDI_KIT, 0, 36, 100); note_kit_off = 150; }
|
|
if(ms % 1000 == 500){ midi_note_on(MIDI_SYNTH, 0, 60, 100); note_synth_off = 150; }
|
|
if(ms % 500 == 0 && have_led){ led = !led; cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, led); }
|
|
|
|
/* screen, every 100 ms */
|
|
if(ms % 100 == 0){
|
|
lcd_clear();
|
|
lcd_text(0, 0, "SEEDBOX TEST");
|
|
snprintf(line, sizeof line, engine_ok ? "ENG OK %luUS" : "ENG FAIL", (unsigned long)engine_worst_us);
|
|
lcd_text(0, 8, line);
|
|
snprintf(line, sizeof line, "E%3d D%3d", pot_value(POT_ENERGY), pot_value(POT_DRIFT));
|
|
lcd_text(0, 16, line);
|
|
snprintf(line, sizeof line, "ENC%4ld V%3d", (long)enc, LCD_VOP_DEFAULT);
|
|
lcd_text(0, 24, line);
|
|
snprintf(line, sizeof line, "%s%s%s %s", (now_in >> IN_SEED) & 1 ? "S" : "-", (now_in >> IN_RUN) & 1 ? "R" : "-",
|
|
(now_in >> IN_PUSH) & 1 ? "P" : "-", last_key >= 0 ? KEY_NAME[last_key] : "");
|
|
lcd_text(0, 32, line);
|
|
if(last_in[2] >= 0){ snprintf(line, sizeof line, "IN %02X %02X %02X", last_in[0] & 0xFF, last_in[1] & 0xFF, last_in[2] & 0xFF); lcd_text(0, 40, line); }
|
|
else lcd_text(0, 40, "IN -");
|
|
lcd_flush();
|
|
}
|
|
}
|
|
}
|