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