Musicbox зависает или больше не может найти песни (FeatherRP2040, VS1053, Neokey1x4 x 2, RotaryEncoder)
У меня возникла проблема с кодом музыкальной шкатулки, которая имеет следующие функции. Используя два neokey1x4, я переключаюсь между папками на SD-карте и нажимаю кнопки воспроизведения/паузы, остановки, перехода к следующей песне, перехода к предыдущей. Затем нужно выбрать и воспроизвести .mp3-файл на SD-карте. В каждой папке (01, 02, 03 и т.д.) находятся .mp3-файлы по следующей схеме: 001.mp3, 002.mp3 и т.д. Я использую функцию подсчёта песен в папке. Два ядра микроконтроллера используются для разделения кнопок и воспроизведения музыки. Либо воспроизводится только первая песня, либо другие песни не обнаруживаются, либо микроконтроллер зависает. За это время я перепробовал множество серийных отпечатков, чтобы найти причину проблемы. Я также проверил разные SD-карты (FAT32), заменил микроконтроллер или vs1053. В урезанном проекте всё работает нормально. Может быть, у кого-то из вас есть идеи? Большое спасибо!
#include <Wire.h>
#include <SPI.h>
#include <Adafruit_VS1053.h>
#include <SD.h>
#include <Adafruit_seesaw.h>
#include <Adafruit_NeoKey_1x4.h>
// Variables
volatile int setvolume = 20;
int lastsetvolume = 0;
volatile int currentFolder = 0;
volatile int currentSong = 0;
volatile bool isPlaying = false;
volatile bool folderChanged = false;
volatile bool songChanged = false;
int cachedLastSongIndex[16] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1};
// VS1053
#define VS1053_RESET -1
#define VS1053_CS 8
#define VS1053_DCS 10
#define CARDCS 7
#define VS1053_DREQ 9
Adafruit_VS1053_FilePlayer musicPlayer = Adafruit_VS1053_FilePlayer(VS1053_RESET, VS1053_CS, VS1053_DCS, VS1053_DREQ, CARDCS);
// Rotary Encoder
Adafruit_seesaw ss;
#define SEESAW_ADDR 0x36
#define SS_SWITCH 24
#define ENCODER_RESOLUTION 4
int32_t lastEncoderPosition = 0;
bool lastEncoderButtonState = HIGH;
// Neokeys
Adafruit_NeoKey_1x4 controlNeokey;
Adafruit_NeoKey_1x4 folderNeokey;
#define CONTROL_NEOKEY_ADDR 0x30
#define FOLDER_NEOKEY_ADDR 0x31
const uint32_t FOLDER_COLORS[] = {0x0000FF, 0x00FF00, 0xFF0000, 0xFFFF00};
uint8_t lastControlButtons = 0;
uint8_t lastFolderButtons = 0;
void setup() {
Serial.begin(115200);
while(!Serial);
if (!musicPlayer.begin()) {
Serial.println(F("VS1053 not found"));
while (1);
}
musicPlayer.setVolume(setvolume, setvolume);
musicPlayer.useInterrupt(VS1053_FILEPLAYER_PIN_INT);
if (!SD.begin(CARDCS)) {
Serial.println(F("SD-Card not found"));
while (1);
}
delay(1000); // time for init sd-card
}
void setup1() {
if (!ss.begin(SEESAW_ADDR)) {
Serial.println(F("seesaw not found"));
while(1);
}
ss.pinMode(SS_SWITCH, INPUT_PULLUP);
ss.setEncoderPosition(setvolume);
if (!controlNeokey.begin(CONTROL_NEOKEY_ADDR)) {
Serial.println("Control NeoKey not found");
while(1);
}
if (!folderNeokey.begin(FOLDER_NEOKEY_ADDR)) {
Serial.println("Folder NeoKey not found");
while(1);
}
updateFolderLEDs();
}
void loop() {
updateVolume();
if (folderChanged) {
stopPlayback();
currentSong = 0;
folderChanged = false;
}
if (songChanged) {
stopPlayback();
if (playSong(currentSong)) {
Serial.print(F("Changed to song: "));
Serial.println(currentSong + 1); // +1 for readable numbering
isPlaying = true;
} else {
Serial.println(F("Failed to change song"));
isPlaying = false;
}
songChanged = false;
}
if (isPlaying && musicPlayer.stopped()) {
playNextSong();
}
if (isPlaying) {
// Feed the buffer multiple times
for (int i = 0; i < 8; i++) {
if (musicPlayer.readyForData()) {
musicPlayer.feedBuffer();
} else {
break;
}
}
}
}
void loop1() {
handleRotaryEncoder();
handleControlButtons();
handleFolderButtons();
}
void updateVolume() {
if (setvolume != lastsetvolume) {
musicPlayer.setVolume(setvolume, setvolume);
lastsetvolume = setvolume;
Serial.println(setvolume, DEC);
}
}
void handleRotaryEncoder() {
int32_t encoderPosition = ss.getEncoderPosition();
if (encoderPosition != lastEncoderPosition) {
if (encoderPosition > lastEncoderPosition) {
setvolume = min(50, setvolume + 1);
Serial.println(F("leiser"));
} else {
setvolume = max(0, setvolume - 1);
Serial.println(F("lauter"));
}
lastEncoderPosition = encoderPosition;
}
bool currentEncoderButtonState = ss.digitalRead(SS_SWITCH);
if (currentEncoderButtonState != lastEncoderButtonState) {
if (currentEncoderButtonState == LOW) {
int newFolderGroup = ((currentFolder / 4) + 1) % 4;
currentFolder = newFolderGroup * 4;
updateFolderLEDs();
folderChanged = true;
Serial.print(F("Switched to folder group: "));
Serial.println(newFolderGroup + 1);
}
lastEncoderButtonState = currentEncoderButtonState;
}
}
void handleControlButtons() {
uint8_t buttons = controlNeokey.read();
uint8_t changed = buttons ^ lastControlButtons;
for (uint8_t i = 0; i < 4; i++) {
if (changed & (1 << i)) {
if (buttons & (1 << i)) {
switch (i) {
case 0:
togglePlayPause();
controlNeokey.pixels.setPixelColor(i, isPlaying ? 0xFF0000 : 0);
break;
case 1:
stopPlayback();
controlNeokey.pixels.setPixelColor(i, musicPlayer.stopped() ? 0xFF0000 : 0);
break;
case 2:
playNextSong();
controlNeokey.pixels.setPixelColor(i, 0xFF0000);
break;
case 3:
playPreviousSong();
controlNeokey.pixels.setPixelColor(i, 0xFF0000);
break;
}
controlNeokey.pixels.show();
// debouncing
delay(50);
} else {
controlNeokey.pixels.setPixelColor(i, 0);
controlNeokey.pixels.show();
}
}
}
lastControlButtons = buttons;
}
void handleFolderButtons() {
uint8_t buttons = folderNeokey.read();
uint8_t changed = buttons ^ lastFolderButtons;
for (uint8_t i = 0; i < 4; i++) {
if (changed & (1 << i)) {
if (buttons & (1 << i)) {
int selectedFolder = (currentFolder / 4) * 4 + i;
selectFolder(selectedFolder);
updateFolderLEDs();
folderChanged = true;
}
}
}
lastFolderButtons = buttons;
}
void togglePlayPause() {
if (isPlaying) {
musicPlayer.pausePlaying(true);
Serial.println(F("Paused"));
} else {
if (musicPlayer.stopped()) {
if (playSong(currentSong)) {
Serial.println(F("Started playing"));
} else {
Serial.println(F("Failed to start playing"));
isPlaying = false;
return;
}
} else {
musicPlayer.pausePlaying(false);
Serial.println(F("Resumed"));
}
}
isPlaying = !isPlaying;
}
void stopPlayback() {
musicPlayer.stopPlaying();
isPlaying = false;
currentSong = 0;
Serial.print(F("stop playing"));
}
void playNextSong() {
checkSDCard();
int lastSongIndex = getLastSongIndex();
int attempts = 0;
bool success = false;
while (attempts <= lastSongIndex && !success) {
currentSong++;
if (currentSong > lastSongIndex) {
currentSong = 0;
}
if (playSong(currentSong)) {
success = true;
Serial.print(F("Playing next song: "));
Serial.print(currentSong + 1); // +1 for readable numbering
Serial.print(F(" of "));
Serial.println(lastSongIndex + 1);
} else {
Serial.print(F("Failed to play song "));
Serial.println(currentSong + 1);
}
attempts++;
}
if (!success) {
Serial.println(F("Failed to play any song in the folder"));
isPlaying = false;
}
}
void playPreviousSong() {
int lastSongIndex = getLastSongIndex();
int attempts = 0;
bool success = false;
while (attempts <= lastSongIndex && !success) {
if (currentSong > 0) {
currentSong--;
} else {
currentSong = lastSongIndex;
}
if (playSong(currentSong)) {
success = true;
Serial.print(F("Playing previous song: "));
Serial.print(currentSong + 1); // +1 for readable numbering
Serial.print(F(" of "));
Serial.println(lastSongIndex + 1);
} else {
Serial.print(F("Failed to play song "));
Serial.println(currentSong + 1);
}
attempts++;
}
if (!success) {
Serial.println(F("Failed to play any song in the folder"));
isPlaying = false;
}
}
int getLastSongIndex() {
int lastIndex = 0;
char filename[20];
Serial.println(F("Entering getLastSongIndex()"));
Serial.print(F("Current folder: "));
Serial.println(currentFolder + 1);
while (true) {
snprintf(filename, sizeof(filename), "/%02d/%03d.mp3", currentFolder + 1, lastIndex + 1);
Serial.print(F("Checking file: "));
Serial.println(filename);
if (!SD.exists(filename)) {
Serial.println(F("File not found, breaking loop"));
break;
}
Serial.println(F("File found, incrementing lastIndex"));
lastIndex++;
if (lastIndex >= 999) {
Serial.println(F("Warning: More than 999 songs in folder, stopping search"));
break;
}
}
if (lastIndex == 0) {
Serial.print(F("No songs found in folder "));
Serial.println(currentFolder + 1);
} else {
Serial.print(F("Last song index in folder "));
Serial.print(currentFolder + 1);
Serial.print(F(": "));
Serial.println(lastIndex);
}
Serial.print(F("Returning lastIndex: "));
Serial.println(lastIndex > 0 ? lastIndex - 1 : 0);
return lastIndex > 0 ? lastIndex - 1 : 0;
}
bool playSong(int songIndex) {
char filename[20];
snprintf(filename, sizeof(filename), "/%02d/%03d.mp3", currentFolder + 1, songIndex + 1);
Serial.println(F("Entering playSong()"));
Serial.print(F("Attempting to play file: "));
Serial.println(filename);
if (SD.exists(filename)) {
Serial.println(F("File exists, attempting to start playback"));
if (musicPlayer.startPlayingFile(filename)) {
Serial.print(F("Now playing: "));
Serial.println(filename);
isPlaying = true;
return true;
} else {
Serial.print(F("Failed to start playing: "));
Serial.println(filename);
Serial.println(F("VS1053 may be having issues or file may be corrupted"));
return false;
}
} else {
Serial.print(F("File not found: "));
Serial.println(filename);
Serial.println(F("Check SD card contents and file naming"));
return false;
}
}
void selectFolder(int folder) {
if (currentFolder != folder) {
currentFolder = folder;
cachedLastSongIndex[currentFolder] = -1; // Reset cache for new folder
Serial.print(F("Current Folder: "));
Serial.println(currentFolder + 1);
folderChanged = true;
}
}
void updateFolderLEDs() {
uint32_t color = FOLDER_COLORS[currentFolder / 4];
for (uint8_t i = 0; i < 4; i++) {
if (i == currentFolder % 4) {
folderNeokey.pixels.setPixelColor(i, 0xFFFFFF); // White for selected folder
} else {
folderNeokey.pixels.setPixelColor(i, color);
}
}
folderNeokey.pixels.show();
}
void checkSDCard() {
if (!SD.begin(CARDCS)) {
Serial.println(F("SD-Card not found, new init"));
delay(100);
if (!SD.begin(CARDCS)) {
Serial.println(F("SD-Card-Init fails"));
}
}
}
Мой последовательный монитор:
- 19:41:21.474 -> Entering playSong()
- 19:41:21.474 -> Attempting to play file: /01/001.mp3
- 19:41:21.474 -> File exists, attempting to start playback
- 19:41:21.528 -> Now playing: /01/001.mp3
- 19:41:21.528 -> Started playing
- 19:41:28.365 -> Entering getLastSongIndex()
- 19:41:28.365 -> Current folder: 1
- 19:41:28.365 -> Checking file: /01/001.mp3
- 19:41:28.365 -> File not found, breaking loop
- 19:41:28.365 -> No songs found in folder 1
- 19:41:28.365 -> Returning lastIndex: 0
- 19:41:28.365 -> Entering playSong()
- 19:41:28.365 -> Attempting to play file: /01/001.mp3
- 19:41:28.662 -> File not found: /01/001.mp3
- 19:41:28.662 -> Check SD card contents and file naming
- 19:41:28.662 -> Failed to play song 1
- 19:41:28.662 -> Failed to play any song in the folder
Первую песню можно воспроизвести, а иногда и вторую, но тогда он больше не может найти ни одной песни.
Я переписал весь скетч на одноядерный и протестировал его с Arduino Zero в режиме отладки. В принципе, всё работает, но, похоже, есть проблема с доступом к SD-карте при воспроизведении .mp3. По сути, песни в папке не подсчитываются во время воспроизведения .mp3. В то же время я попробовал ещё раз в двухъядерном режиме, и там возникла та же проблема. Так что пока я предполагаю, что это не связано с программированием двух ядер. Я думаю о том, чтобы передать на аутсорсинг добавление песен в настройку. Как только у меня появится решение (как для одного ядра, так и для двух ядер), я выложу его здесь.
@mweisbr, 👍-1
Обсуждение1 ответ
В многоядерном режиме RP2040 каждое ядро использует свой стек. Вы, наверное, думали, что оба ядра используют один и тот же стек. Когда Pico работает в одноядерном режиме, ядру 0 доступны все 8 КБ стекового пространства. При использовании многоядерных setup1/loop1 эти 8 КБ разделяются на два стека по 4 КБ, по одному на ядро.
Для Pico при работе в многоядерном режиме это больше похоже на работу FreeRTOS, каждое ядро должно сигнализировать другому ядру о состоянии общего ресурса через механизм FIFO, см. документ о взаимодействии между ядрами.
По умолчанию второе ядро ничего не делает. Вот пример запуска второго ядра.
В этом Pico-MultiCore.ino показано, как временно останавливать и возобновлять работу другого ядра, а также как взаимодействовать друг с другом с помощью API, описанных по ссылке «Взаимодействие между ядрами» выше.
- Нужна помощь с матрицей 2X3 для шрифта Брайля
- Попытка сохранить файлы .wav с новым именем в каждом цикле.
- Чтение значений из текстового файла на SD-карте
- Можно ли использовать GSM и Data Logger Shield одновременно на одном Arduino?
- Как объявить массив переменного размера (глобально)
- Программирование Arduino с использованием Python, а не C/C ++
- Загрузка Arduino Nano дает ошибку: avrdude: stk500_recv(): programmer is not responding
- Как справиться с rollover millis()?
не могу найти ни одной песни в папке, иногда находит песню 002.mp3, но после этого не может найти ни одной песни, @mweisbr
Привет, Хён, у rp2040 два ядра. Оба могут использоваться настройками и настройками1, а также циклом и циклом1., @mweisbr
я добавил свой последовательный монитор, @mweisbr