FireBeetle 播放音频的更大存储空间

前面的文章介绍了 DAC 方式播放和用更高精度的 PWM 方式直接播放音频文件,但是很明显我们遇到了的2个问题:

  1. 存储空间有限,APP 中最大只能存放2.7MB的音频;
  2. 每次都需要手工将数据转化为 .h ,比较麻烦。

这次就介绍如何在 FireBeetle上使用更大的空间。从介绍中可以看到FireBeetle Flash 为16MB,但是 APP 最大只能用到3MB,余下的空间要么分配给 SPIFFS,要么分给 FATFS。

SPIFFS 和 FATFS 都是一种文件系统。其中SPIFFS 是一个用于 SPI NOR flash 设备的嵌入式文件系统,支持磨损均衡、文件系统一致性检查等功能【参考1】。同样的 FATFS也是一种文件系统【参考2】。很明显 ESP32 上我们可以使用更大的空间,因此这里尝试使用 FATSFS来存储音频文件。

对于 ESP32 来说,每次上传的 APP 和 FATFS 是分开的。比如,编译之后生成了一个 3MB 的APP, 那么上传时只会烧写更新 3MB APP那一段的SPI NOR中的内容,余下的部分不会改变(加上压缩以及快速的串口通讯,我们并不会感觉上传太慢)。因此,我们还需要一个额外的工具来完成上传 FATFS 的部分。这个工具就是arduino esp32fs 插件,这个插件能将文件传输到ESP32 的SPIFFS, LittleFS 或者FatFS分区上,项目地址如下:

https://github.com/lorol/arduino-esp32fs-plugin

下载到编译好的文件是一个 JAR文件。

安装方法是在你 Arduino.exe 的目录中,Tools 下创建 ESP32FS/Tool 目录,然后将上面这个文件放置进去:

重启 Arduino 之后在 Tools 菜单中会出现 “ESP32 Sketch Data Upload”的选项。

为了给 FATFS 分区上传,我们还需要2个额外的工具 mklittlefs.exe 和 mkfatfs.exe。这两个工具需要放在C:\Users\用户名\AppData\Local\Arduino15\packages\firebeetle32\hardware\esp32\0.1.1\tools 目录下。

上面准备妥当之后,先编写一个测试程序。这个程序会列出当前 FATFS 上面的文件名称。

#include "FS.h"
#include "FFat.h"

void listDir(fs::FS &fs, const char * dirname, uint8_t levels){
    Serial.printf("Listing directory: %s\r\n", dirname);

    File root = fs.open(dirname);
    if(!root){
        Serial.println("- failed to open directory");
        return;
    }
    if(!root.isDirectory()){
        Serial.println(" - not a directory");
        return;
    }

    File file = root.openNextFile();
    while(file){
        if(file.isDirectory()){
            Serial.print("  DIR : ");
            Serial.println(file.name());
            if(levels){
                listDir(fs, file.name(), levels -1);
            }
        } else {
            Serial.print("  FILE: ");
            Serial.print(file.name());
            Serial.print("\tSIZE: ");
            Serial.println(file.size());
        }
        file = root.openNextFile();
    }

    
}

void setup() {
  Serial.begin(115200);
  if(!FFat.begin()){
        Serial.println("FFat Mount Failed");
        return;
  }  
    Serial.printf("Total space: %10u\n", FFat.totalBytes());
    Serial.printf("Free space: %10u\n", FFat.freeBytes());  
    
}

void loop() {
    listDir(FFat, "/", 0);
    delay(5000);
}

此外,我们还要在程序目录下创建一个 data 目录,然后将3支歌曲的文件放在里面。

直接使用菜单上传FATFS分区。

选择 FATFS:

看到下面的字样就表示已经成功:

接下来,像普通 Arduino 代码一样上传我们的程序,打开串口就能看到结果:

有了上面的代码,我们可以很容易编写出来播放代码:

#include "FS.h"
#include "FFat.h"

#include "data\1990.h"
#include "soc/sens_reg.h"  // For dacWrite() patch, TEB Sep-16-2019

void listDir(fs::FS &fs, const char * dirname, uint8_t levels){
    Serial.printf("Listing directory: %s\r\n", dirname);

    File root = fs.open(dirname);
    if(!root){
        Serial.println("- failed to open directory");
        return;
    }
    if(!root.isDirectory()){
        Serial.println(" - not a directory");
        return;
    }

    File file = root.openNextFile();
    while(file){
        if(file.isDirectory()){
            Serial.print("  DIR : ");
            Serial.println(file.name());
            if(levels){
                listDir(fs, file.name(), levels -1);
            }
        } else {
            Serial.print("  FILE: ");
            Serial.print(file.name());
            Serial.print("\tSIZE: ");
            Serial.println(file.size());
        }
        file = root.openNextFile();
    }

    
}

void playFile(fs::FS &fs, const char * path){
    Serial.printf("Playing file: %s\r\n", path);

    File file = fs.open(path);
    if(!file){
        Serial.println("- failed to open file for reading");
        return;
    }

    uint8_t buffer[1024*4];
    size_t bytessend;
    while(file.available()){
        bytessend=file.read(buffer, 1024*4);
        for (int i=0;i<1024*4;i++) {
            CLEAR_PERI_REG_MASK(SENS_SAR_DAC_CTRL2_REG, SENS_DAC_CW_EN1_M);
            SET_PERI_REG_BITS(RTC_IO_PAD_DAC1_REG, RTC_IO_PDAC1_DAC, buffer[i], RTC_IO_PDAC1_DAC_S);
            SET_PERI_REG_MASK(RTC_IO_PAD_DAC1_REG, RTC_IO_PDAC1_XPD_DAC | RTC_IO_PDAC1_DAC_XPD_FORCE);
            ets_delay_us(125);
        }
    }
    file.close();
}
void setup() {
  Serial.begin(115200);
  if(!FFat.begin()){
        Serial.println("FFat Mount Failed");
        return;
  }  
    Serial.printf("Total space: %10u\n", FFat.totalBytes());
    Serial.printf("Free space: %10u\n", FFat.freeBytes());  
    
    listDir(FFat, "/", 0);
}

void loop() {
    playFile(FFat, "/1st.wav");
    playFile(FFat, "/10years.wav");
    playFile(FFat, "/1990.wav");
}

参考:

  1. https://docs.espressif.com/projects/esp-idf/zh_CN/release-v4.1/api-reference/storage/spiffs.html
  2. https://docs.espressif.com/projects/esp-idf/zh_CN/latest/esp32/api-reference/storage/fatfs.html

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注