From babe2f828ea78b7073791172d439fb137c27c48f Mon Sep 17 00:00:00 2001 From: Mitchell Marino Date: Tue, 25 Mar 2025 23:49:46 -0500 Subject: [PATCH] Preliminary speaker driver changes --- README.md | 54 +++------------------------------ main/drivers/all.cpp | 4 ++- main/drivers/all.h | 6 ++-- main/drivers/bottom_half.cpp | 1 + main/drivers/bottom_half.h | 1 - main/drivers/game_timer.cpp | 29 ++++++++++-------- main/drivers/game_timer.h | 10 +++--- main/drivers/sd.h | 4 ++- main/drivers/speaker.cpp | 59 ++++++++++++++++++++++++++++++------ main/drivers/speaker.h | 3 +- main/drivers/sseg.cpp | 11 +++++-- main/drivers/sseg.h | 4 +-- main/main.cpp | 2 -- 13 files changed, 99 insertions(+), 89 deletions(-) diff --git a/README.md b/README.md index 29171c3..d5e49a0 100755 --- a/README.md +++ b/README.md @@ -1,53 +1,9 @@ -| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-P4 | ESP32-S2 | ESP32-S3 | Linux | -| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | ----- | +# BLK_BOX_TC -# Hello World Example +ESP-IDF version `v5.3.2` -Starts a FreeRTOS task to print "Hello World". +Firmware for the BLK_BOX Top control board for the puzzle `PDE001`. -(See the README.md file in the upper level 'examples' directory for more information about examples.) +Convert audio files to 44100Hz mono signed 16 uncompressed .wav files with the following command: +```ffmpeg -i input.mp3 -ac 1 -ar 44100 -sample_fmt s16 output.wav``` -## How to use example - -Follow detailed instructions provided specifically for this example. - -Select the instructions depending on Espressif chip installed on your development board: - -- [ESP32 Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/stable/get-started/index.html) -- [ESP32-S2 Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/get-started/index.html) - - -## Example folder contents - -The project **hello_world** contains one source file in C language [hello_world_main.c](main/hello_world_main.c). The file is located in folder [main](main). - -ESP-IDF projects are built using CMake. The project build configuration is contained in `CMakeLists.txt` files that provide set of directives and instructions describing the project's source files and targets (executable, library, or both). - -Below is short explanation of remaining files in the project folder. - -``` -├── CMakeLists.txt -├── pytest_hello_world.py Python script used for automated testing -├── main -│ ├── CMakeLists.txt -│ └── hello_world_main.c -└── README.md This is the file you are currently reading -``` - -For more information on structure and contents of ESP-IDF projects, please refer to Section [Build System](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/build-system.html) of the ESP-IDF Programming Guide. - -## Troubleshooting - -* Program upload failure - - * Hardware connection is not correct: run `idf.py -p PORT monitor`, and reboot your board to see if there are any output logs. - * The baud rate for downloading is too high: lower your baud rate in the `menuconfig` menu, and try again. - -## Technical support and feedback - -Please use the following feedback channels: - -* For technical queries, go to the [esp32.com](https://esp32.com/) forum -* For a feature request or bug report, create a [GitHub issue](https://github.com/espressif/esp-idf/issues) - -We will get back to you as soon as possible. diff --git a/main/drivers/all.cpp b/main/drivers/all.cpp index 8f8593c..1298839 100644 --- a/main/drivers/all.cpp +++ b/main/drivers/all.cpp @@ -11,7 +11,9 @@ void init_drivers() { init_bottom_half(); init_sd(); init_speaker(); - + init_sseg(); + init_game_timers(); + } /// @brief Initializes I2C_NUM_0. diff --git a/main/drivers/all.h b/main/drivers/all.h index 7ed3382..1ad20bf 100644 --- a/main/drivers/all.h +++ b/main/drivers/all.h @@ -6,16 +6,16 @@ // #include "drivers/tft.h" // #include "drivers/wires.h" // #include "drivers/sd.h" -// #include "drivers/game_timer.h" -// #include "drivers/speaker.h" // #include "drivers/char_lcd.h" // #include "drivers/leds.h" // #include "drivers/power.h" #include "char_lcd.h" -#include "drivers/bottom_half.h" +#include "bottom_half.h" #include "sd.h" +#include "speaker.h" +#include "game_timer.h" void init_drivers(); diff --git a/main/drivers/bottom_half.cpp b/main/drivers/bottom_half.cpp index 97b7819..841fdf3 100644 --- a/main/drivers/bottom_half.cpp +++ b/main/drivers/bottom_half.cpp @@ -1,4 +1,5 @@ #include "bottom_half.h" +#include static const char *TAG = "bottom_half"; diff --git a/main/drivers/bottom_half.h b/main/drivers/bottom_half.h index 57c4772..86f8f36 100644 --- a/main/drivers/bottom_half.h +++ b/main/drivers/bottom_half.h @@ -3,7 +3,6 @@ #include "driver/i2c.h" #include "driver/gpio.h" -#include "esp_log.h" #define BOTTOM_I2C_NUM I2C_NUM_0 #define BOTTOM_I2C_ADDR 126 diff --git a/main/drivers/game_timer.cpp b/main/drivers/game_timer.cpp index f80b5f0..7c95f67 100644 --- a/main/drivers/game_timer.cpp +++ b/main/drivers/game_timer.cpp @@ -1,4 +1,7 @@ #include "game_timer.h" +#include + +static const char* TAG = "game_timer"; static bool is_module_playing; static bool is_game_playing; @@ -12,6 +15,14 @@ static uint32_t module_time_left; static void game_timer_task(void *arg); static uint32_t sat_sub(uint32_t x, uint32_t y); +void init_game_timers() { + ESP_LOGI(TAG, "Initializing game timers..."); + + xTaskCreate(game_timer_task, "game_timers", 4096, NULL, 10, NULL); + + ESP_LOGI(TAG, "Game timers initialized!"); +} + static void write_game_time(uint32_t millis) { if (millis > 60'000) { int disp_time = (millis / 60'000) * 100; @@ -31,23 +42,19 @@ static void write_module_time(uint32_t millis) { } } -void init_game_module_timer(void) { - xTaskCreate(game_timer_task, "game_module_timer", 4096, NULL, 10, NULL); -} - -void start_game_timer(void) { +void start_game_timer() { is_game_playing = true; } -void stop_game_timer(void) { +void stop_game_timer() { is_game_playing = false; } -void start_module_timer(void) { +void start_module_timer() { is_module_playing = true; } -void stop_module_timer(void) { +void stop_module_timer() { is_module_playing = false; } @@ -91,8 +98,7 @@ void time_penalty(uint32_t penalty) { } } -static void game_timer_task(void *arg) -{ +static void game_timer_task(void *arg) { TickType_t lastWakeTime = xTaskGetTickCount(); const uint32_t frequency = 100; @@ -118,8 +124,7 @@ static void game_timer_task(void *arg) vTaskDelete(NULL); } -static uint32_t sat_sub(uint32_t x, uint32_t y) -{ +static uint32_t sat_sub(uint32_t x, uint32_t y) { uint32_t res = x - y; res &= -(res <= x); return res; diff --git a/main/drivers/game_timer.h b/main/drivers/game_timer.h index 86a7b2b..f48c7e7 100644 --- a/main/drivers/game_timer.h +++ b/main/drivers/game_timer.h @@ -5,17 +5,17 @@ #include "sseg.h" /// Initializes the game and module timers. -void init_game_module_timer(void); +void init_game_timers(); /// Starts the game timer -void start_game_timer(void); +void start_game_timer(); /// Stops the game timer -void stop_game_timer(void); +void stop_game_timer(); /// Starts the module timer -void start_module_timer(void); +void start_module_timer(); /// Stops the module timer -void stop_module_timer(void); +void stop_module_timer(); /// Sets the game time in ms void set_game_time(int32_t new_time); diff --git a/main/drivers/sd.h b/main/drivers/sd.h index 24ceb3d..2ff79a6 100644 --- a/main/drivers/sd.h +++ b/main/drivers/sd.h @@ -22,8 +22,10 @@ extern sdmmc_card_t *card; /// @brief Initializes the SD card /// /// This requires the char_lcd to have been initialized. -/// @return +/// @return true if the sd card was successfully initialized bool init_sd(); + +/// @brief Unmounts the sd card void deinit_sd(); #endif /* SD_H */ \ No newline at end of file diff --git a/main/drivers/speaker.cpp b/main/drivers/speaker.cpp index 30589a8..312394a 100644 --- a/main/drivers/speaker.cpp +++ b/main/drivers/speaker.cpp @@ -57,9 +57,48 @@ esp_err_t play_raw_stop_queue(const char *fp, QueueHandle_t stop_handle) { return ESP_OK; } +esp_err_t play_wav(const char *fp) { + FILE *fh = fopen(fp, "rb"); + if (fh == NULL) { + ESP_LOGE(TAG, "Failed to open file"); + return ESP_ERR_INVALID_ARG; + } + + // skip the header... + fseek(fh, 44, SEEK_SET); + + int16_t *buf = (int16_t*) calloc(AUDIO_BUFFER, sizeof(int16_t)); + size_t bytes_read = 0; + size_t bytes_written = 0; + + bytes_read = fread(buf, sizeof(int16_t), AUDIO_BUFFER, fh); + for (int i = 0; i < bytes_read; i++) { + buf[i] = buf[i] >> 2; + } + + i2s_channel_preload_data(tx_chan, buf, bytes_read * sizeof(int16_t), &bytes_written); + i2s_channel_enable(tx_chan); + + while (bytes_read > 0) + { + bytes_read = fread(buf, sizeof(int16_t), AUDIO_BUFFER, fh); + for (int i = 0; i < bytes_read; i++) { + buf[i] = buf[i] >> 2; + } + // write the buffer to the i2s + i2s_channel_write(tx_chan, buf, bytes_read * sizeof(int16_t), &bytes_written, portMAX_DELAY); + // ESP_LOGI(TAG, "Bytes read: %d", bytes_read); + } + + i2s_channel_disable(tx_chan); + free(buf); + + return ESP_OK; +} + esp_err_t play_raw(const char *fp) { FILE *fh = fopen(fp, "rb"); - if (fh == NULL) + if (fh == NULL) { ESP_LOGE(TAG, "Failed to open file"); return ESP_ERR_INVALID_ARG; } @@ -70,18 +109,17 @@ esp_err_t play_raw(const char *fp) { size_t bytes_read = 0; size_t bytes_written = 0; - esp_err_t channel_enable_result = i2s_channel_enable(tx_chan); - ESP_ERROR_CHECK_WITHOUT_ABORT(channel_enable_result); - if (channel_enable_result != ESP_OK) { - return channel_enable_result; - } - bytes_read = fread(read_buf, sizeof(uint8_t), AUDIO_BUFFER, fh); for (int i = 0; i < bytes_read; i++) { write_buf[i] = read_buf[i] << 4; } - // i2s_channel_enable(tx_handle); + i2s_channel_preload_data(tx_chan, write_buf, bytes_read * sizeof(int16_t), &bytes_written); + esp_err_t channel_enable_result = i2s_channel_enable(tx_chan); + ESP_ERROR_CHECK_WITHOUT_ABORT(channel_enable_result); + if (channel_enable_result != ESP_OK) { + return channel_enable_result; + } while (bytes_read > 0) { // write the buffer to the i2s @@ -90,12 +128,12 @@ esp_err_t play_raw(const char *fp) { // int16_t val = buf[i]; // printf("%s0x%04X ", (val < 0 ? "-" : "+"), (val < 0 ? -val : val)); // }> - i2s_channel_write(tx_chan, write_buf, bytes_read * sizeof(int16_t), &bytes_written, portMAX_DELAY); bytes_read = fread(read_buf, sizeof(uint8_t), AUDIO_BUFFER, fh); for (int i = 0; i < bytes_read; i++) { write_buf[i] = read_buf[i] << 4; } vTaskDelay(pdMS_TO_TICKS(10)); + i2s_channel_write(tx_chan, write_buf, bytes_read * sizeof(int16_t), &bytes_written, portMAX_DELAY); } i2s_channel_disable(tx_chan); @@ -130,6 +168,9 @@ void init_speaker(void) { }; ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx_chan, &tx_std_cfg)); + ESP_ERROR_CHECK_WITHOUT_ABORT(play_wav("/sdcard/startup.wav")); + ESP_ERROR_CHECK_WITHOUT_ABORT(play_raw("/sdcard/boot.pcm")); + ESP_LOGI(TAG, "Speaker initialized!"); } diff --git a/main/drivers/speaker.h b/main/drivers/speaker.h index 795bc41..f020675 100644 --- a/main/drivers/speaker.h +++ b/main/drivers/speaker.h @@ -26,7 +26,8 @@ extern i2s_chan_handle_t tx_chan; esp_err_t play_raw_stop_queue(const char *fp, QueueHandle_t stop_handle); esp_err_t play_raw(const char *fp); -void init_speaker(void); +/// @brief Initalizes the speaker +void init_speaker(); void play_example(); #endif /* SPEAKER_H */ \ No newline at end of file diff --git a/main/drivers/sseg.cpp b/main/drivers/sseg.cpp index 6ccfded..5d2f890 100644 --- a/main/drivers/sseg.cpp +++ b/main/drivers/sseg.cpp @@ -1,11 +1,16 @@ #include "sseg.h" +#include "esp_log.h" TM1640* sseg = nullptr; -static const char *TAG = "sseg_driver"; +static const char *TAG = "sseg"; void init_sseg() { + ESP_LOGI(TAG, "Initializing sseg..."); + sseg = new TM1640(SSEG_PIN_DATA, SSEG_PIN_CLK, 8); + + ESP_LOGI(TAG, "Sseg initialized!"); } void set_game_sseg_raw(const uint8_t* digits) { @@ -22,7 +27,7 @@ void set_module_sseg_raw(const uint8_t* digits) { sseg->setSegments(digits[3], 7); } -void set_game_sseg_num(unsigned int value, int dot_pos) { +void set_game_sseg_num(uint32_t value, uint8_t dot_pos) { for (int i = 0; i < 4; i++) { auto idx = value % 10; sseg->sendChar(3-i, TM16XX_NUMBER_FONT[idx], i == dot_pos); @@ -30,7 +35,7 @@ void set_game_sseg_num(unsigned int value, int dot_pos) { } } -void set_module_sseg_num(unsigned int value, int dot_pos) { +void set_module_sseg_num(uint32_t value, uint8_t dot_pos) { for (int i = 0; i < 4; i++) { auto idx = value % 10; sseg->sendChar(7-i, TM16XX_NUMBER_FONT[idx], i == dot_pos); diff --git a/main/drivers/sseg.h b/main/drivers/sseg.h index ffc84e9..5e6a76e 100644 --- a/main/drivers/sseg.h +++ b/main/drivers/sseg.h @@ -27,9 +27,9 @@ void set_game_sseg_raw(const uint8_t* digits); void set_module_sseg_raw(const uint8_t* digits); /// sets the game timer to the given number, with the dot at position `dot_pos` from the right (0 => right-most digit). -void set_game_sseg_num(unsigned int value, int dot_pos); +void set_game_sseg_num(uint32_t value, uint8_t dot_pos); /// sets the module timer to the given number, with the dot at position `dot_pos` from the right (0 => right-most digit). -void set_module_sseg_num(unsigned int value, int dot_pos); +void set_module_sseg_num(uint32_t value, uint8_t dot_pos); #endif /* SSEG_H */ \ No newline at end of file diff --git a/main/main.cpp b/main/main.cpp index d394945..450c6da 100755 --- a/main/main.cpp +++ b/main/main.cpp @@ -25,8 +25,6 @@ extern "C" void app_main(void) { init_drivers(); init_tft(); - init_sseg(); - init_game_module_timer(); init_leds(); init_wires(); init_power_board();