Files
seedbox/firmware/bringup/main.c
Gee 2196633ed5
Some checks failed
check / check (push) Has been cancelled
Seedbox v0.1 + firmware, generic kit/synth, ROOT/SCALE pages
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>
2026-10-11 20:39:36 +03:00

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();
}
}
}