1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

ESP32+VS1003Bで作るmp3プレーヤー

Last updated at Posted at 2021-02-07

ESP32-WROOM-32D、VS1003B(MP3デコーダモジュール)、SDカードモジュールからなる簡単なmp3プレーヤーを制作しました。

【開発環境】
VisualStudioCode + PlatformIO

【結線】
connection.jpg

【ソースコード】
・プレーヤー部

player.cpp
#include <SPI.h>
#include <SD.h>
#include <Arduino.h>
#include <FreeRTOS.h>

#include "VS1003Bmp3.hpp"

// SD
#define SD_SCK  (18)
#define SD_MISO (19)
#define SD_MOSI (23)
#define SD_SS   ( 5)
SPIClass vSpi(VSPI);

volatile unsigned long cnt = 0;
#define SD_BUFF_SIZE   (32 * 64)
#define SD_BUFF_NUM (16) 
unsigned char sdBuff[SD_BUFF_NUM][SD_BUFF_SIZE];
unsigned char dummyBuff[8];
int readBuffNum = 0;
int writeBuffNum = 0;
typedef enum {
  BUFF_EMPTY,
  BUFF_READING,
  BUFF_WRITING,
  BUFF_FULL,
} buff_status_t;
int buffStatus[SD_BUFF_NUM] = {BUFF_EMPTY};

hw_timer_t *timer = NULL;   // 音声再生用
portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;

VS1003B::mp3 *m;

File gRoot;
int gFileCnt = 0;
#define PLAY_LIST_MAX (256)
char *playList[PLAY_LIST_MAX] = {0};

void IRAM_ATTR timerCallbackFunc() {
  portENTER_CRITICAL_ISR(&timerMux);
  size_t written = 32;
  size_t accumWritten = 0;

  if(buffStatus[readBuffNum] == BUFF_FULL && cnt == 0) {
    buffStatus[readBuffNum] = BUFF_READING;
  }
  if(buffStatus[readBuffNum] == BUFF_READING) {
    do {
      m->soundOut((const unsigned char *)&sdBuff[readBuffNum][cnt], written);

      cnt += written;
      accumWritten += written;
    } while(accumWritten == 32);

    if(cnt >= SD_BUFF_SIZE) {
      buffStatus[readBuffNum] = BUFF_EMPTY;
      int tmp_readBuffNum = readBuffNum;
      ++tmp_readBuffNum %= SD_BUFF_NUM;
      if(buffStatus[tmp_readBuffNum] == BUFF_FULL) {
        cnt = 0;
        ++readBuffNum %= SD_BUFF_NUM;
      }
    }
  }
  portEXIT_CRITICAL_ISR(&timerMux);
}

// プレイリストにファイルパスを格納
void getFileNames(char **playList, File dir) {
  for(;;) {
    File item = dir.openNextFile();
    if(!item || gFileCnt >= PLAY_LIST_MAX) {
      break;
    } 

    if(item.isDirectory()) {
      // 特定ディレクトリは中に入らないよう除外
      if(strcmp(item.name(), "/System Volume Information") != 0 &&
         strcmp(item.name(), "/hidden") != 0) {
        getFileNames(playList, item);
      }
    } else {
      playList[gFileCnt] = (char *)malloc(256);
      strcpy(playList[gFileCnt], item.name()); 
      gFileCnt++;
    }
  }
}

// プレイリスト作成
void makePlayList(char **playList) {
  File root = SD.open("/");
  getFileNames(playList, root);
  root.close();
}

void setup(void) {
  Serial.begin(115200);

  m = new VS1003B::mp3(HSPI, 14, 12, 13);

  vSpi.end();
  vSpi.begin(SD_SCK, SD_MISO, SD_MOSI, SD_SS);
  //vSpi.setFrequency(15000000);
  vSpi.setDataMode(SPI_MODE1);
  if(!SD.begin(SD_SS, vSpi, 15000000)) {
    Serial.println("SD card mount failed!!");
  }
  makePlayList(playList);

  for(int i = 0; i < 256; i++) {
    if(playList[i]) {
      Serial.printf("%s\r\n", playList[i]);
    }
  }

  // タイマー割り込み設定 
  timer = timerBegin(1, 4, true);
  timerAttachInterrupt(timer, timerCallbackFunc, true);
  timerAlarmWrite(timer, 558 << 5, true);
  timerAlarmDisable(timer);
}

void loop() {
  // プレイリストのインデックス
  static int fileIndex = 0;
  // 新しいファイルの始まり
  static bool first = true;
   bool flg_waitForBufferFilled = true;
  
  File file = SD.open(playList[fileIndex], FILE_READ);

  if(!file) {
    Serial.println("FILE_READ error...");
  } else {
    // 再生データ長を取得
    int rest = file.size();
    cnt = 0;
    readBuffNum = 0;
    writeBuffNum = 0;
    memset(buffStatus, BUFF_EMPTY, sizeof(buffStatus));

    while(rest > 0) {
      if(first) {
        first = false;
        // バッファ全面にデータを読み込んでおく
        do {
          file.read(sdBuff[writeBuffNum], rest < SD_BUFF_SIZE ? rest : SD_BUFF_SIZE);
          rest -= SD_BUFF_SIZE;
          ++writeBuffNum;
        } while(writeBuffNum < SD_BUFF_NUM);

        for(int i = 0; i < SD_BUFF_NUM; i++) {
          buffStatus[i] = BUFF_FULL;
        }
        readBuffNum = 0;
        writeBuffNum = SD_BUFF_NUM / 2;

        if(flg_waitForBufferFilled) {
          // 再生開始
          timerAlarmEnable(timer);
          flg_waitForBufferFilled = false;
        }
      }
      // 空のバッファにデータ読み込み
      if(buffStatus[writeBuffNum] == BUFF_EMPTY) {
        buffStatus[writeBuffNum] = BUFF_WRITING;
        while(!file.read(sdBuff[writeBuffNum], rest < SD_BUFF_SIZE ? rest : SD_BUFF_SIZE)) {
          //Serial.printf("sd read() error. rest:%d\r\n", rest);
        }
        rest -= SD_BUFF_SIZE;
        buffStatus[writeBuffNum] = BUFF_FULL;
        //Serial.printf("RB:%d, WB:%d\r\n", readBuffNum, writeBuffNum);
      } else {
        ++writeBuffNum %= SD_BUFF_NUM;
      }
    }
    ++fileIndex %= gFileCnt;
  }
  delay(1);
}

・DACモジュールドライバ部

VS1003Bmp3.hpp
#ifndef _H_VS1003B_MP3_H_
#define _H_VS1003B_MP3_H_

#include <SPI.h>
#include <Arduino.h>
#include <FreeRTOS.h>

namespace VS1003B {
  // direct gpio
  #define GPIO_0TO31SET_REG   *((volatile unsigned long *)GPIO_OUT_W1TS_REG)
  #define GPIO_0TO31CLR_REG   *((volatile unsigned long *)GPIO_OUT_W1TC_REG)
  #define GPIO_0TO32RD_REG    *((volatile unsigned long *)GPIO_IN_REG)

  // pin connections with VS1003B
  #define VS1003B_SCK   (14)
  #define VS1003B_MISO  (12)
  #define VS1003B_MOSI  (13)

  #define VS1003B_XCS   (22)
  #define VS1003B_XDCS  (27)
  #define VS1003B_XRST  (26)
  #define VS1003B_DREQ  (25)

  // SCI_MODE
  #define SM_DIFF       (0b000000000000001)
  #define SM_SETTOZERO  (0b000000000000010)
  #define SM_RESET      (0b000000000000100)
  #define SM_OUTOFWAV   (0b000000000001000)
  #define SM_PDOWN      (0b000000000010000)
  #define SM_TESTS      (0b000000000100000)
  #define SM_STREAM     (0b000000001000000)
  #define SM_SETTOZERO2 (0b000000010000000)
  #define SM_DACT       (0b000000100000000)
  #define SM_SDIORD     (0b000001000000000)
  #define SM_SDISHARE   (0b000010000000000)
  #define SM_SDINEW     (0b000100000000000)
  #define SM_ADPCM      (0b001000000000000)
  #define SM_ADPCM_HP   (0b010000000000000)
  #define SM_LINE_IN    (0b100000000000000)

  // SCI registers
  #define SR_MODE           (0x00)
  #define SR_STATUS         (0x01)
  #define SR_BASS           (0x02)
  #define SR_CLOCKF         (0x03)
  #define SR_DECODE_TIME    (0x04)
  #define SR_AUDATA         (0x05)
  #define SR_WRAM           (0x06)
  #define SR_WRAMADDR       (0x07)
  #define SR_HDAT0          (0x08)
  #define SR_HDAT1          (0x09)
  #define SR_AIADDR         (0x0a)
  #define SR_VOL            (0x0b)
  #define SR_AICTRL0        (0x0c)
  #define SR_AICTRL1        (0x0d)
  #define SR_AICTRL2        (0x0e)
  #define SR_AICTRL3        (0x0f)

  // task state
  #define VS1003B_STOPPED   (0)
  #define VS1003B_PLAYING   (1)
  volatile int vs1003bState = VS1003B_STOPPED;

  class mp3 {
      private:
      void sciWrite(unsigned char addr, unsigned short data) {
        GPIO_0TO31SET_REG = 1 << VS1003B_XDCS;
        GPIO_0TO31CLR_REG = 1 << VS1003B_XCS;
        
        vsSpi->write(0x02);   // write instruction
        vsSpi->write(addr);
        vsSpi->write16(data);
        while(!(GPIO_0TO32RD_REG & (1 << VS1003B_DREQ))) {
          ;
        }
        GPIO_0TO31SET_REG = 1 << VS1003B_XCS;
      }
      void sdiWriteBytes(const unsigned char *pdata, int size) {
        GPIO_0TO31CLR_REG = 1 << VS1003B_XDCS;
        vsSpi->writeBytes((unsigned char *)pdata, size);
        GPIO_0TO31SET_REG = 1 << VS1003B_XDCS;
      }
      public:
      SPIClass *vsSpi;
      mp3(int cSpi, int pinSck, int pinMiso, int pinMosi) {
        pinMode(VS1003B_XCS, OUTPUT);
        pinMode(VS1003B_XDCS, OUTPUT);
        pinMode(VS1003B_XRST, OUTPUT);
        pinMode(VS1003B_DREQ, INPUT);

        GPIO_0TO31SET_REG = (1 << VS1003B_XCS) | (1 << VS1003B_XDCS);
        vsSpi = new SPIClass(cSpi); // whether VSPI or HSPI.
        vsSpi->end();
        vsSpi->begin(pinSck, pinMiso, pinMosi, -1);

        while(!(GPIO_0TO32RD_REG & (1 << VS1003B_DREQ))) {
          delay(1);
        }

        sciWrite(SR_AUDATA, 44100 + 1); // set the sampling rate(44.1KHz) and stereo flag(1).
        sciWrite(SR_CLOCKF, 0x8000);    // set clock multiplier(x3.0), addition(0.0x) and frequency(12.288MHz).
        sciWrite(SR_VOL, 0x2727);
        sciWrite(SR_MODE, SM_RESET | SM_DIFF | SM_SDINEW);

        vsSpi->setFrequency(36864000 / 7);  // (XTAL * 3) / 7 : max       
      }
      void soundOut(const unsigned char *mp3buff, int size) {
        for(int i = 0; i < size; i += 32) {
    
          sdiWriteBytes(&mp3buff[0], 32);
          while(!(GPIO_0TO32RD_REG & (1 << VS1003B_DREQ))) {
            ;
          }
        }
      }
  };

} /* namespace VS1003B */
#endif /* _H_VS1003B_MP3_H_ */

platformio.ini
[env:esp32dev]
platform = espressif32
board = esp32dev
framework = arduino

lib_deps =
  # RECOMMENDED
  # Accept new functionality in a backwards compatible manner and patches


; enlarge flash capacity:
board_build.partitions = no_ota.csv
; set frequency to 240MHz
board_build.f_cpu = 240000000L
; for debugger
monitor_speed = 115200
upload_speed = 921600
debug_tool = minimodule

upload_port = COM5
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?