sync
This commit is contained in:
@@ -7,6 +7,7 @@ target_sources(app PRIVATE
|
||||
src/main.c
|
||||
src/fs.c
|
||||
src/io.c
|
||||
src/audio.c
|
||||
src/usb.c
|
||||
src/protocol.c
|
||||
)
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
buzzer-button = &button0;
|
||||
audio-i2s = &i2s0;
|
||||
usb-uart = &cdc_acm_uart0;
|
||||
qspi-flash = &mx25r64;
|
||||
};
|
||||
|
||||
chosen {
|
||||
|
||||
@@ -2,19 +2,24 @@
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <zephyr/device.h>
|
||||
#include <zephyr/drivers/i2s.h>
|
||||
#include <zephyr/pm/device.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <audio.h>
|
||||
#include <fs.h>
|
||||
#include <io.h>
|
||||
|
||||
LOG_MODULE_REGISTER(audio, LOG_LEVEL_INF);
|
||||
LOG_MODULE_REGISTER(audio, LOG_LEVEL_DBG);
|
||||
|
||||
/* Dauer eines Blocks in ms (4096 Bytes / (16kHz * 2 Kanäle * 2 Bytes)) = 64 ms */
|
||||
#define BLOCK_DURATION_MS 64
|
||||
#define MAX_WAIT_TIME_MS (3 * BLOCK_DURATION_MS)
|
||||
|
||||
/* Slab für I2S. Keine weiteren Queues oder Threads nötig. */
|
||||
K_MEM_SLAB_DEFINE(audio_slab, AUDIO_BLOCK_SIZE, AUDIO_BLOCK_COUNT, 4);
|
||||
|
||||
/* Message Queue für Play-Kommandos (Pfade zu Dateien, max 64 Zeichen) */
|
||||
K_MSGQ_DEFINE(audio_play_msgq, 64, 4, 4);
|
||||
/* Message Queue für Play-Kommandos (auf 10 erhöht für Aneinanderreihung) */
|
||||
K_MSGQ_DEFINE(audio_play_msgq, 64, 10, 4);
|
||||
|
||||
/* Startup-Sicherung */
|
||||
K_SEM_DEFINE(audio_ready_sem, 0, 1);
|
||||
@@ -25,48 +30,84 @@ K_SEM_DEFINE(audio_ready_sem, 0, 1);
|
||||
#endif
|
||||
|
||||
static const struct device *const i2s_dev = DEVICE_DT_GET(I2S_NODE);
|
||||
static volatile bool abort_playback = false;
|
||||
static char next_random_filename[64] = {0};
|
||||
|
||||
int get_random_file(const char *path, char *out_filename, size_t max_len)
|
||||
static uint32_t audio_file_count = 0;
|
||||
static char cached_404_path[] = "/lfs/sys/404";
|
||||
|
||||
void audio_refresh_file_count(void)
|
||||
{
|
||||
struct fs_dir_t dirp;
|
||||
struct fs_dirent entry;
|
||||
int file_count = 0;
|
||||
int rc;
|
||||
uint32_t count = 0;
|
||||
|
||||
fs_dir_t_init(&dirp);
|
||||
rc = fs_opendir(&dirp, path);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
while (fs_readdir(&dirp, &entry) == 0 && entry.name[0] != '\0')
|
||||
if (fs_pm_opendir(&dirp, AUDIO_PATH) < 0)
|
||||
{
|
||||
if (entry.type == FS_DIR_ENTRY_FILE)
|
||||
file_count++;
|
||||
audio_file_count = 0;
|
||||
return;
|
||||
}
|
||||
fs_closedir(&dirp);
|
||||
|
||||
if (file_count == 0)
|
||||
return -ENOENT;
|
||||
|
||||
uint32_t random_index = k_cycle_get_32() % file_count;
|
||||
rc = fs_opendir(&dirp, path);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
int current_index = 0;
|
||||
while (fs_readdir(&dirp, &entry) == 0 && entry.name[0] != '\0')
|
||||
{
|
||||
if (entry.type == FS_DIR_ENTRY_FILE)
|
||||
{
|
||||
if (current_index == random_index)
|
||||
count++;
|
||||
}
|
||||
}
|
||||
fs_pm_closedir(&dirp);
|
||||
audio_file_count = count;
|
||||
LOG_INF("Audio cache refreshed: %u files found in %s", count, AUDIO_PATH);
|
||||
}
|
||||
|
||||
static void wait_for_i2s_drain(void)
|
||||
{
|
||||
/* Maximale Wartezeit berechnen (8 Blöcke * 64ms = 512ms + Toleranz) */
|
||||
int64_t deadline = k_uptime_get() + (AUDIO_BLOCK_COUNT * BLOCK_DURATION_MS) + 100;
|
||||
|
||||
while (k_mem_slab_num_free_get(&audio_slab) < AUDIO_BLOCK_COUNT)
|
||||
{
|
||||
if (k_uptime_get() >= deadline)
|
||||
{
|
||||
LOG_WRN("Timeout waiting for I2S drain");
|
||||
break;
|
||||
}
|
||||
k_sleep(K_MSEC(10));
|
||||
}
|
||||
}
|
||||
|
||||
int get_random_file(char *out_filename, size_t max_len)
|
||||
{
|
||||
if (audio_file_count == 0)
|
||||
{
|
||||
/* Fallback auf System-Sound, wenn Ordner leer */
|
||||
strncpy(out_filename, cached_404_path, max_len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct fs_dir_t dirp;
|
||||
struct fs_dirent entry;
|
||||
uint32_t target_index = k_cycle_get_32() % audio_file_count;
|
||||
uint32_t current_index = 0;
|
||||
|
||||
fs_dir_t_init(&dirp);
|
||||
if (fs_pm_opendir(&dirp, AUDIO_PATH) < 0)
|
||||
return -ENOENT;
|
||||
|
||||
while (fs_readdir(&dirp, &entry) == 0 && entry.name[0] != '\0')
|
||||
{
|
||||
if (entry.type == FS_DIR_ENTRY_FILE)
|
||||
{
|
||||
if (current_index == target_index)
|
||||
{
|
||||
snprintf(out_filename, max_len, "%s/%s", path, entry.name);
|
||||
snprintf(out_filename, max_len, "%s/%s", AUDIO_PATH, entry.name);
|
||||
break;
|
||||
}
|
||||
current_index++;
|
||||
}
|
||||
}
|
||||
fs_closedir(&dirp);
|
||||
fs_pm_closedir(&dirp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -75,115 +116,189 @@ void audio_system_ready(void)
|
||||
k_sem_give(&audio_ready_sem);
|
||||
}
|
||||
|
||||
void audio_stop(void)
|
||||
{
|
||||
LOG_DBG("Playback abort requested");
|
||||
abort_playback = true;
|
||||
k_msgq_purge(&audio_play_msgq);
|
||||
i2s_trigger(i2s_dev, I2S_DIR_TX, I2S_TRIGGER_DROP);
|
||||
}
|
||||
|
||||
void audio_play(const char *filename)
|
||||
{
|
||||
char buf[64] = {0};
|
||||
if (filename != NULL)
|
||||
{
|
||||
strncpy(buf, filename, sizeof(buf) - 1);
|
||||
}
|
||||
|
||||
if (k_msgq_put(&audio_play_msgq, &buf, K_NO_WAIT) < 0)
|
||||
{
|
||||
LOG_WRN("Audio queue full, dropping request");
|
||||
}
|
||||
}
|
||||
|
||||
void audio_thread(void *arg1, void *arg2, void *arg3)
|
||||
{
|
||||
LOG_DBG("Audio thread started");
|
||||
k_sem_take(&audio_ready_sem, K_FOREVER);
|
||||
|
||||
/* Ersten zufälligen Dateinamen beim Booten vorab cachen */
|
||||
get_random_file(next_random_filename, sizeof(next_random_filename));
|
||||
|
||||
char filename[64];
|
||||
|
||||
while (1)
|
||||
{
|
||||
/* 1. Auf Play-Kommando warten */
|
||||
k_msgq_get(&audio_play_msgq, &filename, K_FOREVER);
|
||||
|
||||
/* Sicherstellen, dass die I2S-Hardware nach einem vorherigen DRAIN
|
||||
oder bei extrem schnellem Neudrücken garantiert gestoppt und leer ist. */
|
||||
i2s_trigger(i2s_dev, I2S_DIR_TX, I2S_TRIGGER_DROP);
|
||||
|
||||
/* 2. Datei bestimmen */
|
||||
if (filename[0] == '\0')
|
||||
if (k_msgq_get(&audio_play_msgq, &filename, K_FOREVER) == 0)
|
||||
{
|
||||
if (get_random_file(AUDIO_PATH, filename, sizeof(filename)) < 0)
|
||||
fs_pm_flash_resume();
|
||||
|
||||
abort_playback = false;
|
||||
|
||||
/* 2. Datei bestimmen (aus Cache oder synchron als Fallback) */
|
||||
if (filename[0] == '\0')
|
||||
{
|
||||
LOG_ERR("No file found in %s", AUDIO_PATH);
|
||||
if (next_random_filename[0] != '\0')
|
||||
{
|
||||
/* Cache Hit: Sofort in den lokalen Puffer übernehmen */
|
||||
strncpy(filename, next_random_filename, sizeof(filename));
|
||||
next_random_filename[0] = '\0'; /* Cache als 'leer' markieren */
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Cache Miss (z.B. bei extrem schnellem Dauerfeuer): Synchron suchen */
|
||||
if (get_random_file(filename, sizeof(filename)) < 0)
|
||||
{
|
||||
LOG_ERR("No file found in %s", AUDIO_PATH);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct fs_file_t file;
|
||||
fs_file_t_init(&file);
|
||||
if (fs_open(&file, filename, FS_O_READ) < 0)
|
||||
{
|
||||
LOG_ERR("Failed to open %s", filename);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
struct fs_file_t file;
|
||||
fs_file_t_init(&file);
|
||||
if (fs_open(&file, filename, FS_O_READ) < 0)
|
||||
{
|
||||
LOG_ERR("Failed to open %s", filename);
|
||||
continue;
|
||||
}
|
||||
LOG_INF("Playing: %s", filename);
|
||||
io_status(true);
|
||||
|
||||
LOG_INF("Playing: %s", filename);
|
||||
io_status(true);
|
||||
bool i2s_started = false;
|
||||
bool aborted = false;
|
||||
bool trigger_started = false;
|
||||
int queued_blocks = 0;
|
||||
|
||||
/* 3. Synchrone Lese- und Abspiel-Schleife */
|
||||
while (1)
|
||||
{
|
||||
/* WICHTIG: Prüfen, ob während des Abspielens ein neues Kommando in die Queue gelegt wurde */
|
||||
if (k_msgq_num_used_get(&audio_play_msgq) > 0)
|
||||
while (!abort_playback)
|
||||
{
|
||||
LOG_DBG("New play request received, aborting current playback");
|
||||
aborted = true;
|
||||
break;
|
||||
void *block;
|
||||
/* Self-Healing Timeout bei I2S-Hängern */
|
||||
if (k_mem_slab_alloc(&audio_slab, &block, K_MSEC(MAX_WAIT_TIME_MS)) < 0)
|
||||
{
|
||||
LOG_ERR("I2S stall or slab timeout - resetting I2S");
|
||||
i2s_trigger(i2s_dev, I2S_DIR_TX, I2S_TRIGGER_DROP);
|
||||
audio_init(); // Setzt Hardware hart zurück
|
||||
break;
|
||||
}
|
||||
|
||||
if (abort_playback)
|
||||
{
|
||||
k_mem_slab_free(&audio_slab, &block);
|
||||
break;
|
||||
}
|
||||
|
||||
ssize_t bytes_read = fs_read(&file, block, AUDIO_BLOCK_SIZE / 2);
|
||||
|
||||
if (bytes_read <= 0)
|
||||
{
|
||||
k_mem_slab_free(&audio_slab, &block);
|
||||
break;
|
||||
}
|
||||
|
||||
/* In-Place Konvertierung Mono -> Stereo */
|
||||
int16_t *samples = (int16_t *)block;
|
||||
int samples_read = bytes_read / 2;
|
||||
|
||||
for (int i = samples_read - 1; i >= 0; i--)
|
||||
{
|
||||
int16_t sample = samples[i];
|
||||
samples[i * 2] = sample;
|
||||
samples[i * 2 + 1] = sample;
|
||||
}
|
||||
|
||||
/* Bei partiellem Block (Dateiende) den Rest mit Stille füllen */
|
||||
if (bytes_read < (AUDIO_BLOCK_SIZE / 2))
|
||||
{
|
||||
size_t valid_bytes = bytes_read * 2;
|
||||
memset((uint8_t *)block + valid_bytes, 0, AUDIO_BLOCK_SIZE - valid_bytes);
|
||||
}
|
||||
|
||||
/* Block in die DMA-Queue schieben */
|
||||
if (i2s_write(i2s_dev, block, AUDIO_BLOCK_SIZE) < 0)
|
||||
{
|
||||
k_mem_slab_free(&audio_slab, &block);
|
||||
break;
|
||||
}
|
||||
|
||||
/* HIER werden die Variablen verwendet: */
|
||||
queued_blocks++;
|
||||
|
||||
/* Regulärer Start: Erst wenn 2 Blöcke in der DMA-Queue liegen */
|
||||
if (!trigger_started && queued_blocks >= 2)
|
||||
{
|
||||
i2s_trigger(i2s_dev, I2S_DIR_TX, I2S_TRIGGER_START);
|
||||
trigger_started = true;
|
||||
LOG_DBG("I2S transmission started after queuing %d blocks", queued_blocks);
|
||||
}
|
||||
|
||||
/* Kurze Datei (kleiner als 1 Block): Sofort starten und Schleife verlassen */
|
||||
if (bytes_read < (AUDIO_BLOCK_SIZE / 2))
|
||||
{
|
||||
if (!trigger_started)
|
||||
{
|
||||
i2s_trigger(i2s_dev, I2S_DIR_TX, I2S_TRIGGER_START);
|
||||
trigger_started = true;
|
||||
LOG_DBG("I2S transmission started for short file (bytes read: %zd)", bytes_read);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void *block;
|
||||
if (k_mem_slab_alloc(&audio_slab, &block, K_FOREVER) != 0)
|
||||
if (abort_playback)
|
||||
{
|
||||
break;
|
||||
i2s_trigger(i2s_dev, I2S_DIR_TX, I2S_TRIGGER_DROP);
|
||||
LOG_DBG("Playback aborted via audio_stop()");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (k_msgq_num_used_get(&audio_play_msgq) > 0)
|
||||
{
|
||||
LOG_DBG("Play request pending, skipping DRAIN");
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_DBG("Sample finished, starting DRAIN");
|
||||
i2s_trigger(i2s_dev, I2S_DIR_TX, I2S_TRIGGER_DRAIN);
|
||||
wait_for_i2s_drain();
|
||||
}
|
||||
}
|
||||
|
||||
ssize_t bytes_read = fs_read(&file, block, AUDIO_BLOCK_SIZE / 2);
|
||||
|
||||
if (bytes_read <= 0)
|
||||
fs_close(&file);
|
||||
fs_pm_flash_suspend();
|
||||
if (k_msgq_num_used_get(&audio_play_msgq) == 0)
|
||||
{
|
||||
k_mem_slab_free(&audio_slab, &block);
|
||||
break; /* EOF oder Fehler */
|
||||
io_status(false);
|
||||
}
|
||||
|
||||
/* In-Place Konvertierung Mono -> Stereo */
|
||||
int16_t *samples = (int16_t *)block;
|
||||
int samples_read = bytes_read / 2;
|
||||
|
||||
for (int i = samples_read - 1; i >= 0; i--)
|
||||
if (k_msgq_num_used_get(&audio_play_msgq) == 0 && next_random_filename[0] == '\0')
|
||||
{
|
||||
int16_t sample = samples[i];
|
||||
samples[i * 2] = sample;
|
||||
samples[i * 2 + 1] = sample;
|
||||
}
|
||||
|
||||
if (bytes_read < (AUDIO_BLOCK_SIZE / 2))
|
||||
{
|
||||
size_t valid_bytes = bytes_read * 2;
|
||||
memset((uint8_t *)block + valid_bytes, 0, AUDIO_BLOCK_SIZE - valid_bytes);
|
||||
}
|
||||
|
||||
if (i2s_write(i2s_dev, block, AUDIO_BLOCK_SIZE) < 0)
|
||||
{
|
||||
k_mem_slab_free(&audio_slab, &block);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!i2s_started)
|
||||
{
|
||||
i2s_trigger(i2s_dev, I2S_DIR_TX, I2S_TRIGGER_START);
|
||||
i2s_started = true;
|
||||
if (get_random_file(next_random_filename, sizeof(next_random_filename)) == 0)
|
||||
{
|
||||
LOG_DBG("Pre-cached next random file: %s", next_random_filename);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 4. Aufräumen */
|
||||
if (aborted)
|
||||
{
|
||||
/* Hart abbrechen, Puffer sofort verwerfen */
|
||||
i2s_trigger(i2s_dev, I2S_DIR_TX, I2S_TRIGGER_DROP);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Sauber ausklingen lassen, bis der letzte I2S-Puffer leer ist */
|
||||
i2s_trigger(i2s_dev, I2S_DIR_TX, I2S_TRIGGER_DRAIN);
|
||||
}
|
||||
|
||||
fs_close(&file);
|
||||
io_status(false);
|
||||
LOG_DBG("Playback finished or aborted");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -210,16 +325,7 @@ int audio_init(void)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
audio_refresh_file_count();
|
||||
LOG_INF("Audio initialized: %u bits, %u.%03u kHz", config.word_size, config.frame_clk_freq / 1000, config.frame_clk_freq % 1000);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void audio_play(const char *filename)
|
||||
{
|
||||
char buf[64] = {0};
|
||||
if (filename != NULL)
|
||||
{
|
||||
strncpy(buf, filename, sizeof(buf) - 1);
|
||||
}
|
||||
k_msgq_put(&audio_play_msgq, &buf, K_NO_WAIT);
|
||||
}
|
||||
@@ -10,8 +10,8 @@
|
||||
#define AUDIO_THREAD_PRIORITY 5
|
||||
#define AUDIO_EVENTS_MASK (AUDIO_EVENT_PLAY | AUDIO_EVENT_STOP | AUDIO_EVENT_SYNC)
|
||||
|
||||
#define AUDIO_BLOCK_SIZE 1024
|
||||
#define AUDIO_BLOCK_COUNT 4
|
||||
#define AUDIO_BLOCK_SIZE 4096
|
||||
#define AUDIO_BLOCK_COUNT 8
|
||||
#define AUDIO_WORD_WIDTH 16
|
||||
#define AUDIO_SAMPLE_RATE 16000
|
||||
|
||||
@@ -27,7 +27,7 @@ int audio_init(void);
|
||||
*
|
||||
* @param filename The path to the audio file to play
|
||||
*/
|
||||
void audio_play(const char *filename)
|
||||
void audio_play(const char *filename);
|
||||
|
||||
/**
|
||||
* @brief Stops the currently playing audio
|
||||
@@ -39,4 +39,14 @@ void audio_stop(void);
|
||||
*/
|
||||
void audio_system_ready(void);
|
||||
|
||||
/**
|
||||
* @brief Refreshes the count of available audio files
|
||||
*/
|
||||
void audio_refresh_file_count(void);
|
||||
|
||||
/**
|
||||
* @brief Puts the QSPI flash into deep sleep mode to save power
|
||||
*/
|
||||
void flash_deep_sleep(void);
|
||||
|
||||
#endif // AUDIO_H
|
||||
@@ -1,10 +1,21 @@
|
||||
#include <zephyr/fs/littlefs.h>
|
||||
#include <zephyr/drivers/flash.h>
|
||||
#include <zephyr/pm/device.h>
|
||||
#include <fs.h>
|
||||
LOG_MODULE_REGISTER(buzz_fs, LOG_LEVEL_INF);
|
||||
LOG_MODULE_REGISTER(buzz_fs, LOG_LEVEL_DBG);
|
||||
|
||||
#define STORAGE_PARTITION_ID FIXED_PARTITION_ID(littlefs_storage)
|
||||
FS_LITTLEFS_DECLARE_DEFAULT_CONFIG(fs_storage_data);
|
||||
|
||||
#define QSPI_FLASH_NODE DT_ALIAS(qspi_flash)
|
||||
#if !DT_NODE_EXISTS(QSPI_FLASH_NODE)
|
||||
#error "QSPI Flash alias not defined in devicetree"
|
||||
#endif
|
||||
|
||||
static const struct device *flash_dev = DEVICE_DT_GET(QSPI_FLASH_NODE);
|
||||
static volatile uint32_t open_count = 0;
|
||||
static struct k_mutex flash_pm_lock;
|
||||
|
||||
static struct fs_mount_t fs_storage_mnt = {
|
||||
.type = FS_LITTLEFS,
|
||||
.fs_data = &fs_storage_data,
|
||||
@@ -18,6 +29,90 @@ int fs_init(void) {
|
||||
LOG_ERR("Error mounting filesystem: %d", rc);
|
||||
return rc;
|
||||
}
|
||||
k_mutex_init(&flash_pm_lock);
|
||||
LOG_DBG("Filesystem mounted successfully");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int fs_pm_flash_suspend(void)
|
||||
{
|
||||
if (!device_is_ready(flash_dev)) {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
k_mutex_lock(&flash_pm_lock, K_FOREVER);
|
||||
|
||||
if (open_count > 0) {
|
||||
open_count--;
|
||||
if (open_count == 0) {
|
||||
int rc = pm_device_action_run(flash_dev, PM_DEVICE_ACTION_SUSPEND);
|
||||
if (rc < 0) {
|
||||
LOG_WRN("Could not suspend flash: %d", rc);
|
||||
} else {
|
||||
LOG_DBG("Flash entered deep power-down");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
k_mutex_unlock(&flash_pm_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fs_pm_flash_resume(void)
|
||||
{
|
||||
if (!device_is_ready(flash_dev)) return -ENODEV;
|
||||
|
||||
k_mutex_lock(&flash_pm_lock, K_FOREVER);
|
||||
|
||||
if (open_count == 0) {
|
||||
int rc = pm_device_action_run(flash_dev, PM_DEVICE_ACTION_RESUME);
|
||||
if (rc == 0) {
|
||||
k_busy_wait(50); // t-exit-dpd
|
||||
LOG_DBG("Flash resumed");
|
||||
}
|
||||
}
|
||||
open_count++;
|
||||
|
||||
k_mutex_unlock(&flash_pm_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fs_pm_open(struct fs_file_t *file, const char *path, fs_mode_t mode)
|
||||
{
|
||||
int rc = fs_open(file, path, mode);
|
||||
if (rc == 0)
|
||||
{
|
||||
fs_pm_flash_resume();
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
int fs_pm_close(struct fs_file_t *file)
|
||||
{
|
||||
int rc = fs_close(file);
|
||||
if (rc == 0)
|
||||
{
|
||||
fs_pm_flash_suspend();
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
int fs_pm_opendir(struct fs_dir_t *dirp, const char *path)
|
||||
{
|
||||
int rc = fs_opendir(dirp, path);
|
||||
if (rc == 0)
|
||||
{
|
||||
fs_pm_flash_resume();
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
int fs_pm_closedir(struct fs_dir_t *dirp)
|
||||
{
|
||||
int rc = fs_closedir(dirp);
|
||||
if (rc == 0)
|
||||
{
|
||||
fs_pm_flash_suspend();
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
@@ -7,4 +7,20 @@
|
||||
* @brief Initializes the filesystem by mounting it
|
||||
*/
|
||||
int fs_init(void);
|
||||
|
||||
/**
|
||||
* @brief Puts the QSPI flash into deep sleep mode to save power
|
||||
*/
|
||||
int fs_pm_flash_suspend(void);
|
||||
|
||||
/**
|
||||
* @brief Resumes the QSPI flash from deep sleep mode
|
||||
*/
|
||||
int fs_pm_flash_resume(void);
|
||||
|
||||
|
||||
int fs_pm_open(struct fs_file_t *file, const char *path, fs_mode_t mode);
|
||||
int fs_pm_close(struct fs_file_t *file);
|
||||
int fs_pm_opendir(struct fs_dir_t *dirp, const char *path);
|
||||
int fs_pm_closedir(struct fs_dir_t *dirp);
|
||||
#endif // FS_H
|
||||
@@ -3,7 +3,7 @@
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
#include <audio.h>
|
||||
|
||||
LOG_MODULE_REGISTER(io, LOG_LEVEL_INF);
|
||||
LOG_MODULE_REGISTER(io, LOG_LEVEL_DBG);
|
||||
|
||||
#define STATUS_LED_NODE DT_ALIAS(status_led)
|
||||
#define USB_LED_NODE DT_ALIAS(usb_led)
|
||||
@@ -20,6 +20,7 @@ void button_isr(const struct device *dev, struct gpio_callback *cb, uint32_t pin
|
||||
gpio_pin_interrupt_configure_dt(&button_spec, GPIO_INT_DISABLE);
|
||||
|
||||
LOG_DBG("Button pressed, triggering audio play");
|
||||
audio_stop();
|
||||
audio_play(NULL);
|
||||
|
||||
k_work_reschedule(&debounce_work, K_MSEC(50));
|
||||
|
||||
@@ -75,8 +75,4 @@ int main(void)
|
||||
|
||||
LOG_INF("All subsystems initialized. Starting application threads.");
|
||||
audio_system_ready();
|
||||
audio_play("/lfs/sys/404");
|
||||
while (1) {
|
||||
k_sleep(K_FOREVER);
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include <usb.h>
|
||||
#include <protocol.h>
|
||||
#include <audio.h>
|
||||
|
||||
#define PROTOCOL_VERSION 1
|
||||
|
||||
@@ -44,7 +45,7 @@ int send_ls(const char *path)
|
||||
const char *ls_path = (path == NULL || path[0] == '\0') ? "/" : path;
|
||||
fs_dir_t_init(&dirp);
|
||||
|
||||
if (fs_opendir(&dirp, ls_path) < 0)
|
||||
if (fs_pm_opendir(&dirp, ls_path) < 0)
|
||||
{
|
||||
LOG_ERR("Failed to open directory '%s'", ls_path);
|
||||
return ENOENT;
|
||||
@@ -57,7 +58,7 @@ int send_ls(const char *path)
|
||||
usb_write_buffer((const uint8_t *)tx_buffer, strlen(tx_buffer));
|
||||
}
|
||||
|
||||
fs_closedir(&dirp);
|
||||
fs_pm_closedir(&dirp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -88,7 +89,7 @@ int put_binary_file(const char *filename, ssize_t filesize, uint32_t expected_cr
|
||||
fs_file_t_init(&file);
|
||||
fs_unlink(filename);
|
||||
LOG_DBG("Opening file '%s' for writing (expected size: %zd bytes, expected CRC32: 0x%08x)", filename, filesize, expected_crc32);
|
||||
rc = fs_open(&file, filename, FS_O_CREATE | FS_O_WRITE);
|
||||
rc = fs_pm_open(&file, filename, FS_O_CREATE | FS_O_WRITE);
|
||||
if (rc < 0)
|
||||
{
|
||||
LOG_ERR("Failed to open file '%s' for writing: %d", filename, rc);
|
||||
@@ -109,7 +110,7 @@ int put_binary_file(const char *filename, ssize_t filesize, uint32_t expected_cr
|
||||
if (read < 0)
|
||||
{
|
||||
LOG_ERR("Error reading from USB: %d", read);
|
||||
fs_close(&file);
|
||||
fs_pm_close(&file);
|
||||
return -read;
|
||||
}
|
||||
else if (read == 0)
|
||||
@@ -117,7 +118,7 @@ int put_binary_file(const char *filename, ssize_t filesize, uint32_t expected_cr
|
||||
if (retry_count >= 10)
|
||||
{
|
||||
LOG_ERR("No data received from USB after multiple attempts");
|
||||
fs_close(&file);
|
||||
fs_pm_close(&file);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
@@ -147,7 +148,7 @@ int put_binary_file(const char *filename, ssize_t filesize, uint32_t expected_cr
|
||||
if (written < 0)
|
||||
{
|
||||
LOG_ERR("Error writing to file '%s': %d", filename, (int)written);
|
||||
fs_close(&file);
|
||||
fs_pm_close(&file);
|
||||
return (int)written;
|
||||
}
|
||||
|
||||
@@ -163,7 +164,7 @@ int put_binary_file(const char *filename, ssize_t filesize, uint32_t expected_cr
|
||||
uint32_t duration = k_uptime_get_32() - start;
|
||||
uint32_t kb_per_s = (filesize * 1000) / (duration * 1024 + 1);
|
||||
LOG_DBG("Received file '%s' (%zd bytes) in %u ms (%u kb/s), CRC32: 0x%08x", filename, filesize, duration, kb_per_s, running_crc32);
|
||||
fs_close(&file);
|
||||
fs_pm_close(&file);
|
||||
LOG_DBG("Closed file '%s' after writing", filename);
|
||||
if (running_crc32 != expected_crc32)
|
||||
{
|
||||
@@ -224,6 +225,7 @@ void execute_current_command(void)
|
||||
if (rc == 0)
|
||||
{
|
||||
send_ok();
|
||||
audio_refresh_file_count(); // Nach erfolgreichem Upload die Anzahl der verfügbaren Audiodateien aktualisieren
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user