NXP LPC54114双核MCU开发板——一个便携的MP3播放器
NXP LPC54114双核MCU开发板——一个便携的MP3播放器
标签
嵌入式系统
cjmf
更新2022-05-30
中国计量大学
1130

硬件介绍

恩智浦LPC Cortex-M4/M0系列开发板,采用LQFP64封装的LPC54110 MCU,Arduino板型架构,板载调试器和常用外设扩展,通常可直接利用LPC54110开发板对MCU性能进行评估和软件开发。

功能:

  • LPC54114J256BD64 Cortex-M4/M0+ 双核MCU
  • ISP多启动选项跳线,支持3.3V/1.8V工作电压选择,板载32.768KHz振荡器
  • 4个用户定义按键和一个复位按键
  • 9个发光二极管,其中4个低电平驱动(绿色),4个高电平驱动(红色),一个电源指示
  • 一个电位器模拟输入
  • 一个Micro USB接口连接到LPC54114的USB口
  • 一个Micro USB接口连接到板载调试器
  • 一个单线SPI方式TF卡接口和一个SPI  Flash存储器
  • 一个支持I2S音频输入/输出接口(WM8904)
  • 一个I2C 温度传感器(PCT2075DP)
  • 一个PDM数字麦克风(SPH0641LM4H)
  • 多个欧姆跳线电阻使能板载外部设备
  • 板载调试器及支持外部仿真器的10芯1.27mm SWD仿真插座
  • Arduino扩展接口
  • 支持Keil/EWARM集成开发环境

设计思路及细节 开机

默认情况下直接用 Keil 是读不到 DAP 烧写器的,而且也识别不到虚拟串口。这时候就要手动升级调试器固件。按住 RESET 上电,直至电脑出现 CRP DISABLED 盘符。删除所有文件,把附件中的文件拖进去。

TF卡读取遍历

char music_name[FILE_NAME_LEN];
f_mount(&fs, driverNumberBuffer, 1);
file_scan(path);

if (file_num == 0) {
  printf("no mp3 file!\r\n");
  return;
}

printf("Music Num= %d", file_num);
all_page = (file_num + 7) / 8;
current_page = 1;
printf("file_num = %d, all_page= %d\r\n", file_num, all_page);
memcpy(music_name, musicPathList, 30);
if (strstr(music_name, ".mp3") || strstr(music_name, ".MP3")) {
  printf("Playing: %s\r\n", music_name);
  mp3_player(music_name);
}

读取 TF 卡有如下几步:

  1. 挂载 TF 卡
  2. 遍历文件及子目录
  3. 如果是文件夹,则递归进入子目录
  4. 如果是文件,则判断文件后缀名是否为 .mp3,如果是则进行播放

MP3 解析

手动实现 MP3 文件的解析就比较复杂,这里直接使用写好的第三方 MP3 解析模块进行调用。

earlephilhower/ESP8266AudioMP3: Port of Helix MP3 code to ESP8266 (github.com)

static void mp3_player(const char *filename) {
  int err, i, outputSamps, current_sample_rate = 0;

  int read_offset = 0;
  int bytes_left = 0;
  unsigned long Frames = 0;
  unsigned char *read_ptr = buffer;
  HMP3Decoder Mp3Decoder;

  unsigned char ReadFlag = 0;

  fres = f_open(&file, filename, FA_READ);

  if (fres != FR_OK) {
    printf("read file %s error  ! open another file\r\n", filename);
    fres = f_close(&file);

    if (++play_index >= file_num) {
      play_index = 0;
    }
    return;
  }

  Mp3Decoder = MP3InitDecoder();

  while (player_state != S_SWITCH) {

    fres = f_read(&file, buffer, sizeof(buffer), &rw_num);

    if (fres != FR_OK) {
      printf("Error Reading %s (%d)\r\n", filename, fres);
      break;
    }
    read_ptr = buffer;
    bytes_left = rw_num;

    while (player_state != S_SWITCH) {
      if (player_state == S_STOP) {
        continue;
      }

      player_state = S_PLAY;

      if (!ReadFlag) {
        read_offset = MP3FindSyncWord(read_ptr, bytes_left);
        if (read_offset < 0) {
          break;
        }

        read_ptr += read_offset;
        bytes_left -= read_offset;
        if (bytes_left < 1024) {

          i = (uint32_t)(bytes_left)&3;
          if (i)
            i = 4 - i;
          memcpy(buffer + i, read_ptr, bytes_left);
          read_ptr = buffer + i;
          fres = f_read(&file, buffer + bytes_left + i,
                        sizeof(buffer) - bytes_left - i, &rw_num);
          bytes_left += rw_num;
        }
        err = MP3Decode(Mp3Decoder, &read_ptr, &bytes_left, outBuf[bufflag], 0);

        Frames++;
      } else {
        while (!DMA_Flag) {
          if (1 == ReadFlag) {
            read_offset = MP3FindSyncWord(read_ptr, bytes_left);
            if (read_offset < 0) {
              break;
            }
            read_ptr += read_offset;
            bytes_left -= read_offset;
            if (bytes_left < 1024) {

              i = (uint32_t)(bytes_left)&3;
              if (i)
                i = 4 - i;
              memcpy(buffer + i, read_ptr, bytes_left);
              read_ptr = buffer + i;
              fres = f_read(&file, buffer + bytes_left + i,
                            sizeof(buffer) - bytes_left - i, &rw_num);
              bytes_left += rw_num;
            }
            err = MP3Decode(Mp3Decoder, &read_ptr, &bytes_left, outBuf[bufflag],
                            0);

            Frames++;
            ReadFlag = 2;
          }
        }
        DMA_Flag = 0;
        ReadFlag = 1;
      }

      if (err != ERR_MP3_NONE) {
        switch (err) {
        case ERR_MP3_INDATA_UNDERFLOW:
          printf("ERR_MP3_INDATA_UNDERFLOW\r\n");
          read_ptr = buffer;
          fres = f_read(&file, read_ptr, sizeof(buffer), &rw_num);
          bytes_left = rw_num;
          break;

        case ERR_MP3_MAINDATA_UNDERFLOW:

          printf("ERR_MP3_MAINDATA_UNDERFLOW\r\n");
          break;

        default:
          printf("UNKNOWN ERROR:%d\r\n", err);

          if (bytes_left > 0) {
            bytes_left--;
            read_ptr++;
          }
          break;
        }
      } else {
        MP3GetLastFrameInfo(Mp3Decoder, &Mp3FrameInfo);

        if (Mp3FrameInfo.samprate != current_sample_rate) {
          current_sample_rate = Mp3FrameInfo.samprate;

          printf(" \r\n Bitrate       %dKbps", Mp3FrameInfo.bitrate / 1000);
          printf(" \r\n Samprate      %dHz", current_sample_rate);
          printf(" \r\n BitsPerSample %db", Mp3FrameInfo.bitsPerSample);
          printf(" \r\n nChans        %d", Mp3FrameInfo.nChans);
          printf(" \r\n Layer         %d", Mp3FrameInfo.layer);
          printf(" \r\n Version       %d", Mp3FrameInfo.version);
          printf(" \r\n OutputSamps   %d", Mp3FrameInfo.outputSamps);
        }

        outputSamps = Mp3FrameInfo.outputSamps;

        if (outputSamps > 0) {
          if (Mp3FrameInfo.nChans == 1) {

            for (i = outputSamps - 1; i >= 0; i--) {
              outBuf[bufflag][i * 2] = outBuf[bufflag][i];
              outBuf[bufflag][i * 2 + 1] = outBuf[bufflag][i];
            }
            outputSamps *= 2;
          }

          for (copyCount = 0; copyCount < 2500; copyCount++) {
            outBufDouble[copyCount] = outBuf[0][copyCount];
            outBufDouble[copyCount + outputSamps] = outBuf[1][copyCount];
          }

          DMA_Init(DMA0);

          DMA_EnableChannel(DMA0, 15);
          DMA_SetChannelPriority(DMA0, 15, kDMA_ChannelPriority3);
          DMA_CreateHandle(&s_DmaTxHandle, DMA0, 15);

          s_TxTransfer.data = (uint8_t *)outBufDouble;
          s_TxTransfer.dataSize = outputSamps * 4;

          I2S_TxTransferCreateHandleDMA(I2S1, &s_TxHandle, &s_DmaTxHandle,
                                        MyTxCallback, (void *)&s_TxTransfer);
          I2S_TxTransferSendDMA(I2S1, &s_TxHandle, s_TxTransfer);
          bufflag = 1 - bufflag;
        }
      }

      if (file.fptr == file.fsize) {
        printf("END\r\n");
        if (play_index < file_num - 1) {
          play_index++;
          player_state = S_SWITCH;
        } else {
          play_index = 0;
          player_state = S_SWITCH;
        }

        break;
      }
    }
  }

  f_close(&file);
  MP3FreeDecoder(Mp3Decoder);
}

MP3 输出

这里需要提前调节 `app_wm8904.c` 文件中的 db 值,以免听力受损!!!

/* Adjust it to your needs, 0x0006 for -51 dB, 0x0039 for 0 dB etc. */
WM8904_SetVolume(&codecHandle, 0x0025, 0x0025);

输出使用 DMA 技术,解码后直接搬迁至音频输出 IC。

实现功能

  1. 自动读取 TF 卡中的 MP3 文件并播放

FqZ-YCXH7roaYajno990uLp0DKMX

  1. 自动输出 MP3 文件的格式参数

FqxcWUdmFMvn00-8YwjKiSi5BqZi

结尾

WM8904 低频信号输出比较差,提前将 MP3 做一个高通,滤去 1K 以下信号分量可以很大程度上减少噪声。

LPC54114 扩展性非常强大,可以实现多样的功能,在此感谢硬核团队的技术支持。

附件下载
LPC54114固件.zip
lpc54110_example.zip
团队介绍
中国计量大学
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2023 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号