diff --git a/firmware/spade/src/CMakeLists.txt b/firmware/spade/src/CMakeLists.txt index 07b307e59a..7f1e36efe9 100644 --- a/firmware/spade/src/CMakeLists.txt +++ b/firmware/spade/src/CMakeLists.txt @@ -13,9 +13,6 @@ target_link_libraries(spade include_directories(${SPADE_TARGET}/jerry/include) include_directories(./) -pico_enable_stdio_usb(${PROJECT_NAME} 1) -pico_enable_stdio_uart(${PROJECT_NAME} 1) - file(READ version.json VERSION_JSON) string(JSON VERSION GET ${VERSION_JSON} version) diff --git a/firmware/spade/src/pc/main.c b/firmware/spade/src/pc/main.c index d284c60756..99b78ee85b 100644 --- a/firmware/spade/src/pc/main.c +++ b/firmware/spade/src/pc/main.c @@ -145,14 +145,14 @@ static void keyboard(struct mfb_window *window, mfb_key key, mfb_key_mod mod, bo mfb_close(window); } - if (key == KB_KEY_W) spade_call_press( 5); // map_move(map_get_first('p'), 0, -1); - if (key == KB_KEY_S) spade_call_press( 7); // map_move(map_get_first('p'), 0, 1); - if (key == KB_KEY_A) spade_call_press( 6); // map_move(map_get_first('p'), 1, 0); - if (key == KB_KEY_D) spade_call_press( 8); // map_move(map_get_first('p'), -1, 0); - if (key == KB_KEY_I) spade_call_press(12); // map_move(map_get_first('p'), 0, -1); - if (key == KB_KEY_K) spade_call_press(14); // map_move(map_get_first('p'), 0, 1); - if (key == KB_KEY_J) spade_call_press(13); // map_move(map_get_first('p'), 1, 0); - if (key == KB_KEY_L) spade_call_press(15); // map_move(map_get_first('p'), -1, 0); + if (key == KB_KEY_W) spade_call_press(0); // map_move(map_get_first('p'), 0, -1); + if (key == KB_KEY_S) spade_call_press(1); // map_move(map_get_first('p'), 0, 1); + if (key == KB_KEY_A) spade_call_press(2); // map_move(map_get_first('p'), 1, 0); + if (key == KB_KEY_D) spade_call_press(3); // map_move(map_get_first('p'), -1, 0); + if (key == KB_KEY_I) spade_call_press(4); // map_move(map_get_first('p'), 0, -1); + if (key == KB_KEY_K) spade_call_press(5); // map_move(map_get_first('p'), 0, 1); + if (key == KB_KEY_J) spade_call_press(6); // map_move(map_get_first('p'), 1, 0); + if (key == KB_KEY_L) spade_call_press(7); // map_move(map_get_first('p'), -1, 0); } #endif diff --git a/firmware/spade/src/rpi/CMakeLists.txt b/firmware/spade/src/rpi/CMakeLists.txt index 8ab9ccce77..65496bfd5f 100644 --- a/firmware/spade/src/rpi/CMakeLists.txt +++ b/firmware/spade/src/rpi/CMakeLists.txt @@ -1,27 +1,35 @@ pico_sdk_init() -add_definitions(-DSPADE_EMBEDDED -DSPADE_AUDIO -DPICO_NO_BI_PROGRAM_BUILD_DATE) -set(DCMAKE_BUILD_TYPE Release) +add_definitions(-DSPADE_EMBEDDED -DSPADE_AUDIO) +set(DCMAKE_BUILD_TYPE Debug) target_compile_definitions(spade PRIVATE - # compile time configuration of I2S - PICO_AUDIO_I2S_MONO_INPUT=1 - USE_AUDIO_I2S=1 - PICO_AUDIO_I2S_DATA_PIN=9 - PICO_AUDIO_I2S_CLOCK_PIN_BASE=10 - # PICO_DEFAULT_UART=0 - # PICO_DEFAULT_UART_TX_PIN=28 - # PICO_DEFAULT_UART_RX_PIN=29 + # compile time configuration of I2S + PICO_AUDIO_I2S_MONO_INPUT=1 + USE_AUDIO_I2S=1 + PICO_AUDIO_I2S_DATA_PIN=9 + PICO_AUDIO_I2S_CLOCK_PIN_BASE=10 + # PICO_DEFAULT_UART=0 + # PICO_DEFAULT_UART_TX_PIN=28 + # PICO_DEFAULT_UART_RX_PIN=29 ) +pico_enable_stdio_usb(${PROJECT_NAME} 1) +pico_enable_stdio_uart(${PROJECT_NAME} 1) + +add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../../sprig_hal sprig_hal) + target_link_libraries(spade PRIVATE - pico_stdlib - pico_audio_i2s - pico_multicore - hardware_spi - hardware_timer - hardware_pwm - hardware_adc + pico_stdlib + pico_audio_i2s + pico_multicore + hardware_spi + hardware_timer + hardware_pwm + hardware_adc + sprig_hal ) +add_compile_definitions(PICO_HEAP_SIZE=16384) + pico_enable_stdio_usb(spade 1) pico_enable_stdio_uart(spade 0) diff --git a/firmware/spade/src/rpi/audio.c b/firmware/spade/src/rpi/audio.c deleted file mode 100644 index d57b8c37d0..0000000000 --- a/firmware/spade/src/rpi/audio.c +++ /dev/null @@ -1,65 +0,0 @@ -/** - * Driver for the Raspberry Pi audio system. All actual sound generation code is in shared/audio/piano.c. - */ - -#include "shared/audio/piano.c" - -#include "hardware/clocks.h" -#include "hardware/structs/clocks.h" -#include "pico/audio_i2s.h" -#include "pico/binary_info.h" -bi_decl(bi_3pins_with_names(PICO_AUDIO_I2S_DATA_PIN, "I2S DIN", PICO_AUDIO_I2S_CLOCK_PIN_BASE, "I2S BCK", PICO_AUDIO_I2S_CLOCK_PIN_BASE+1, "I2S LRCK")); - -static struct audio_buffer_pool *audio_buffer_pool_init() { - - static audio_format_t audio_format = { - .format = AUDIO_BUFFER_FORMAT_PCM_S16, - .sample_freq = SAMPLES_PER_SECOND, - .channel_count = 1, - }; - - static struct audio_buffer_format producer_format = { - .format = &audio_format, - .sample_stride = 2 - }; - - struct audio_buffer_pool *producer_pool = audio_new_producer_pool(&producer_format, 3, - SAMPLES_PER_BUFFER); // todo correct size - bool __unused ok; - const struct audio_format *output_format; - - struct audio_i2s_config config = { - .data_pin = PICO_AUDIO_I2S_DATA_PIN, - .clock_pin_base = PICO_AUDIO_I2S_CLOCK_PIN_BASE, - .dma_channel = 0, - .pio_sm = 0, - }; - - output_format = audio_i2s_setup(&audio_format, &config); - if (!output_format) { - panic("PicoAudio: Unable to open audio device.\n"); - } - - ok = audio_i2s_connect(producer_pool); - assert(ok); - audio_i2s_set_enabled(true); - - return producer_pool; -} - -struct audio_buffer_pool *audio_bufpool; - -void audio_init(void) { - audio_bufpool = audio_buffer_pool_init(); -} - -void audio_try_push_samples(void) { - struct audio_buffer *buffer = take_audio_buffer(audio_bufpool, false); - if (buffer == NULL) return; - - piano_fill_sample_buf((int16_t *)buffer->buffer->bytes, buffer->max_sample_count); - buffer->sample_count = buffer->max_sample_count; - - // send to PIO DMA - give_audio_buffer(audio_bufpool, buffer); -} diff --git a/firmware/spade/src/rpi/main.c b/firmware/spade/src/rpi/main.c index 105a0bb53c..7a236954bd 100644 --- a/firmware/spade/src/rpi/main.c +++ b/firmware/spade/src/rpi/main.c @@ -1,9 +1,6 @@ #include "pico/stdlib.h" -#include "hardware/pwm.h" -#include "hardware/spi.h" #include "hardware/timer.h" #include "hardware/watchdog.h" -#include "hardware/adc.h" #include "pico/util/queue.h" #include "pico/multicore.h" #include "jerryscript.h" @@ -26,12 +23,9 @@ // Debugging shortcut #define yell puts -#ifdef SPADE_AUDIO - #include "audio.c" -#endif +#include "HAL.h" // More firmware stuiff -#include "ST7735_TFT.h" #include "upload.h" // Other imports @@ -40,6 +34,8 @@ #include "shared/js_runtime/jerry_mem.h" #include "shared/js_runtime/jerryxx.c" #include "shared/js_runtime/js.h" +#include "shared/audio/piano.c" +#include "shared/version.h" // screen is 20 characters wide #define SCREEN_WIDTH_CHARS 20 @@ -54,9 +50,9 @@ static void fatal_error() { while (1) { text_clear(); render_errorbuf(); - st7735_fill_start(); - render(st7735_fill_send); - st7735_fill_finish(); + fill_start(); + render(write_pixel); + fill_end(); } } #include "shared/ui/errorbuf.h" @@ -76,21 +72,8 @@ typedef struct { uint8_t last_state; uint8_t ring_i; } ButtonState; -// W, S, A, D, I, K, J, L -uint button_pins[] = { 5, 7, 6, 8, 12, 14, 13, 15 }; -static ButtonState button_states[ARR_LEN(button_pins)] = {0}; -typedef enum { - Button_W, - Button_S, - Button_A, - Button_D, - Button_I, - Button_K, - Button_J, - Button_L, - Button_None -} Button; +static ButtonState button_states[8] = {0}; static bool button_history_read(ButtonState *bs, int i) { // We want to store bools compactly so we have to do some bit twiddling. @@ -104,25 +87,17 @@ static void button_history_write(ButtonState *bs, int i, bool value) { bs->history[i/8] &= ~(1 << (i % 8)); } -static void button_init(void) { - for (int i = 0; i < ARR_LEN(button_pins); i++) { - ButtonState *bs = button_states + i; - gpio_set_dir(button_pins[i], GPIO_IN); - gpio_pull_up(button_pins[i]); - } -} - /** * Poll the buttons and push any keypresses to the main core. * * (Should be run in a loop on a non-primary core.) */ static void button_poll(void) { - for (int i = 0; i < ARR_LEN(button_pins); i++) { + for (int i = 0; i < 8; i++) { ButtonState *bs = button_states + i; bs->ring_i = (bs->ring_i + 1) % HISTORY_LEN; // Incrememnt ringbuffer index - button_history_write(bs, bs->ring_i, gpio_get(button_pins[i])); + button_history_write(bs, bs->ring_i, get_button(i)); // up is true if more than 5/6 are true int up = 0; @@ -135,63 +110,19 @@ static void button_poll(void) { bs->last_state = up; if (!up) { // Send the keypress to the main core - multicore_fifo_push_blocking(button_pins[i]); + multicore_fifo_push_blocking(i); } } } } -// Turn on the power lights and dim them with PWM. -static void power_lights() { - // left white light - const int pin_num_0 = 28; - gpio_set_function(pin_num_0, GPIO_FUNC_PWM); - uint slice_num_0 = pwm_gpio_to_slice_num(pin_num_0); - pwm_set_enabled(slice_num_0, true); - pwm_set_gpio_level(pin_num_0, 65535/8); - - // right blue light - // const pin_num_1 = 4; - // gpio_set_function(pin_num_1, GPIO_FUNC_PWM); - // uint slice_num_1 = pwm_gpio_to_slice_num(pin_num_1); - // pwm_set_enabled(slice_num_1, true); - // pwm_set_gpio_level(pin_num_1, 65535/4); -} - // Entry point for the second core that polls the buttons. static void core1_entry(void) { - button_init(); - while (1) { button_poll(); } } -/** - * Seed the random number generator with entropy from - * random electricity as well as temperature readings. - */ -static void rng_init(void) { - adc_init(); - uint32_t seed = 0; - - // Read some random electricity - for (int i = 0; i < 4; i++) { - adc_select_input(4); - sleep_ms(1); - seed ^= adc_read(); - } - - // Read some temperature data - adc_set_temp_sensor_enabled(true); - adc_select_input(4); - sleep_ms(1); - seed ^= adc_read(); - adc_set_temp_sensor_enabled(false); - - srand(seed); -} - char serial_commands[][128] = { "UPLOAD", "VERSION", @@ -285,27 +216,9 @@ typedef enum { return 10; } - static Button get_button_press() { + static Sprig_Button get_button_press() { if (!multicore_fifo_rvalid()) return Button_None; - - switch (multicore_fifo_pop_blocking()) { - case 5: - return Button_W; - case 7: - return Button_S; - case 6: - return Button_A; - case 8: - return Button_D; - case 12: - return Button_I; - case 14: - return Button_K; - case 13: - return Button_J; - case 15: - return Button_L; - } + return (Sprig_Button) multicore_fifo_pop_blocking(); } typedef struct { @@ -429,7 +342,7 @@ void update_welcome_state(Welcome_State* welcome_state) { set_game(welcome_state->games[welcome_state->games_i]); } - Button button_pressed = get_button_press(); + Sprig_Button button_pressed = get_button_press(); if (welcome_state->screen == GAME_MENU) switch (button_pressed) { @@ -463,18 +376,27 @@ void update_welcome_state(Welcome_State* welcome_state) { } } +void audio_try_push_samples(void) { + struct audio_buffer *buffer = get_audio_buffer(false); + if (buffer == NULL) return; + + piano_fill_sample_buf((int16_t *)buffer->buffer->bytes, buffer->max_sample_count); + buffer->sample_count = buffer->max_sample_count; + + // send to PIO DMA + push_audio_buffer( buffer); +} + int main() { timer_hw->dbgpause = 0; // Overclock the RP2040! set_sys_clock_khz(270000, true); - errorbuf_color = color16(0, 255, 255); // cyan + hw_init(); // init HAL + stdio_init_all(); // Init serial port - power_lights(); // Turn on the power lights - stdio_init_all(); // Init serial port - st7735_init(); // Init display - rng_init(); // Init RNG + errorbuf_color = color16(0, 255, 255); // cyan // Init JerryScript jerry_init(JERRY_INIT_MEM_STATS); @@ -487,17 +409,6 @@ int main() { multicore_launch_core1(core1_entry); - /** - * We get a bunch of fake keypresses at startup, so we need to - * drain them from the FIFO queue. - * - * What really needs to be done here is to have button_init - * record when it starts so that we can ignore keypresses after - * that timestamp. - */ - sleep_ms(50); - while (multicore_fifo_rvalid()) multicore_fifo_pop_blocking(); - Welcome_State welcome_state = { .screen = NEW_SLOT, .games = malloc(METADATA_MAX_ENTRIES * sizeof(Game)), // leaks but it's fine since lifetime=program @@ -518,9 +429,9 @@ int main() { strcpy(errorbuf, delete_confirm_screen); render_errorbuf(); - st7735_fill_start(); - render(st7735_fill_send); - st7735_fill_finish(); + fill_start(); + render(write_pixel); + fill_end(); load_new_scripts(); } @@ -544,7 +455,6 @@ int main() { .song_free = piano_jerry_song_free, .song_chars = piano_jerry_song_chars, }); - audio_init(); #endif // Current time for timer handling (see frame_cb in shared/sprig_engine/engine.js) @@ -555,7 +465,7 @@ int main() { while (1) { // Handle any new button presses while (multicore_fifo_rvalid()) { - spade_call_press(multicore_fifo_pop_blocking()); + spade_call_press(get_button_press()); } // Run async code @@ -577,9 +487,9 @@ int main() { // Render render_errorbuf(); - st7735_fill_start(); - render(st7735_fill_send); - st7735_fill_finish(); + fill_start(); + render(write_pixel); + fill_end(); } /** @@ -607,9 +517,9 @@ int main() { " \n" " sprig.hackclub.com \n"); render_errorbuf(); - st7735_fill_start(); - render(st7735_fill_send); - st7735_fill_finish(); + fill_start(); + render(write_pixel); + fill_end(); /** * Watchdog is a mechanism designed to catch infinite loops. It will diff --git a/firmware/spade/src/shared/js_runtime/js.h b/firmware/spade/src/shared/js_runtime/js.h index 84e0bc7975..1dcfb371c5 100644 --- a/firmware/spade/src/shared/js_runtime/js.h +++ b/firmware/spade/src/shared/js_runtime/js.h @@ -118,11 +118,11 @@ static void js_promises(void) { } } -static void spade_call_press(int pin) { +static void spade_call_press(Sprig_Button button) { if (!spade_state.press_cb) return; jerry_value_t this_value = jerry_create_undefined(); - jerry_value_t args[] = { jerry_create_number(pin) }; + jerry_value_t args[] = { jerry_create_number(button) }; jerry_value_t res = jerry_call_function( spade_state.press_cb, diff --git a/firmware/spade/src/shared/sprig_engine/engine.js b/firmware/spade/src/shared/sprig_engine/engine.js index a11b56da84..64dc0fd248 100644 --- a/firmware/spade/src/shared/sprig_engine/engine.js +++ b/firmware/spade/src/shared/sprig_engine/engine.js @@ -97,26 +97,8 @@ exports.afterInput = fn => (console.log('engine.js:afterInputs'), afterInputs.pu // exports.afterInput = fn => afterInputs.push(fn); const button = { - pinToHandlers: { - "5": [], - "7": [], - "6": [], - "8": [], - "12": [], - "14": [], - "13": [], - "15": [], - }, - keyToPin: { - "w": "5", - "s": "7", - "a": "6", - "d": "8", - "i": "12", - "k": "14", - "j": "13", - "l": "15", - } + pinToHandlers: [...Array(8)].map(_ => []), + keys: ["w", "s", "a", "d", "i", "k", "j", "l"] }; native.press_cb(pin => { @@ -172,9 +154,9 @@ native.press_cb(pin => { } exports.onInput = (key, fn) => { - const pin = button.keyToPin[key]; + const pin = button.keys.indexOf(key); - if (pin === undefined) + if (pin === -1) throw new Error(`the sprig doesn't have a "${key}" button!`); button.pinToHandlers[pin].push(fn);