911|7

169

帖子

1

TA的资源

纯净的硅(初级)

楼主
 

【STM32H7S78-DK】⑩播放流行音乐,大家一起摇摆! [复制链接]

STM32H7S78-DK 开发板是具备无敌多媒体功能的超级开发板。

 

为什么这么讲呢,他具备数字麦克风、I2S音频编解码、SD卡及Flash存储、5英寸高清晰TFT LCD、WIFI扩展卡。

这些功能集合到一块,可以发挥无穷想象,既可以做一个数码相册、也可以做一个网络收音机、或者是像小爱同学一样的智能音箱,总之玩法多样,潜力无穷。

 

本篇我们来体验最基本的音频播放功能,即播放存储在Flash上的流行音乐。

 

大致逻辑是这样的:

 

 

但问题是,整个流程涉及Flash读取、音频文件解析(Wav)、I2S(DMA方式)、WM8904驱动……

短时间内如何掌握这么多复杂高深的内容,从而完成我们一个简单的任务:播放流行音乐

 

经过一段时间的摸索,办法来了:利用官方的BSP(board support package,板级支持包)

这里面有我们需要的一切函数,我们想要实现什么功能,直接设置好参数调用就好了,无需彻底搞懂复杂的过程,一切以目标驱动模式完成。

好了,话不多说,开始我们的流行音乐之旅吧。

 

1. 在CubeIDE中导入官方例程BSP。

 

 

 

2.  熟读README.md,详细介绍了这个BSP都有啥演示程序,以及如何使用。

 

 

 

3. 最关键的环节:烧录音乐文件(bin)到Flash存储中。

 

### <b>How to use it ?</b>

 

- Use STM32CubeProgrammer, available on www.st.com or any other in system programming

tool to load "BSP/Binary/audio_sample_tdm.bin" file to the external QSPI flash

at the address 0x700A0000.

- Open the STM32CubeProgrammer tool

- Select the octoSPI external flash loader "MX66UW1G45G_STM32H7S78-DK"

- From Erasing & Programming menu, browse and open the output binary file relative to this example

- Load the file into the external QSPI flash using "Start Programming" at the address APPLICATION_ADDRESS (0x700A0000)

 

他这里提供了一段实例音乐audio_sample_tdm.bin(大小512KB),我们将他替换为我们想要播放的流行音乐。

 

 

 

 

你肯定纳闷,上哪去搞bin格式的音频。

 

首先下载一首mp3,其次去在线mp3转wav格式网站,这种很多。

比如:

https://www.aconvert.com/cn/audio/mp3-to-wav/

 

比特率和采样率参考:

 

 

得到WAV格式后,直接将.wav后缀改为.bin(简单粗暴有木有,其实wav就是最原始的i2s数字音频文件)

 

WAV为微软公司(Microsoft)开发的一种声音文件格式,它符合RIFF(Resource Interchange File Format)文件规范,用于保存Windows平台的音频信息资源,被Windows平台及其应用程序所广泛支持,该格式也支持MSADPCM,CCITT A LAW等多种压缩运算法,支持多种音频数字,取样频率和声道。标准格式化的WAV文件采样频率为44100Hz,采样比特为16bit,因此标准的(这里说标准,只是一种广泛采用的波形音频方案)WAV文件和CD音频格式一样,也是44.1KHz的取样频率,16位量化数字,在声音文件质量和CD音频相差无几。

 

将我们的流行音乐miaozhen.bin(大约10.9MB)通过STM32CubeProgrammer烧录到开发板的Flash中。

 

第一步,连接开发板,选择Flash驱动:MX66UW1G45G_STM32H7S78-DK。

 

 

第二部:选择我们的bin格式音频,输入起始地址(0x700A0000),开始烧录。

 

 

 

4. 对官方支持包BSP大做手术,实现我们的功能:播放流行音乐

 

main.c中可以看到,BSP总共6个功能,由用户按钮选择,我们简单干脆,只要audio play功能。

 

 

 

 

 

所以把该程序块直接屏蔽,仅改为:AudioPlay_demo();

 

 

 

来到main.h中,查看对音频文件的定义:

 

/* The Audio file is flashed with STM32CubeProgrammer @ flash address =  AUDIO_SRC_FILE_ADDRESS */
#define AUDIO_SRC_FILE_ADDRESS       0x700A0000   /* Audio file address in flash */
#define AUDIO_FILE_SIZE              0xafc880

AUDIO_SRC_FILE_ADDRESS好理解,就是刚才我们写入Flash的地址:0x700A0000

AUDIO_FILE_SIZE就是该段音频的文件大小:

 

用文件管理器可看到,该bin文件为11,520,128 bit,换算为16进制为:0xAFC880,也就是上面的AUDIO_FILE_SIZE

这里一定要对上,否则在循环播放的过程中会错乱。

 

 

 

 

 

 

 

 

 

 

最后来到播放音乐的核心 audio_play.c文件中:

我们可以仔细阅读各部分的功能,多余的也不要了。

 

最核心的while循环中,仅保留

AUDIO_Process();

#include "main.h"
#include <stdio.h>

/** @addtogroup STM32H7RSxx_HAL_Examples
  * @{
  */

/** @addtogroup BSP
  * @{
  */

/* Private typedef -----------------------------------------------------------*/

/* Private define ------------------------------------------------------------*/

/*Since SysTick is set to 1ms (unless to set it quicker) */
/* to run up to 48khz, a buffer around 1000 (or more) is requested*/
/* to run up to 96khz, a buffer around 2000 (or more) is requested*/
#define AUDIO_DEFAULT_VOLUME    90

/* Audio file size and start address are defined here since the audio file is
   stored in Flash memory as a constant table of 16-bit data */
#define AUDIO_START_OFFSET_ADDRESS    0            /* Offset relative to audio file header size */
#define AUDIO_BUFFER_SIZE             2048

/* Audio file size and start address are defined here since the audio file is
   stored in Flash memory as a constant table of 16-bit data */
#define AUDIO_START_OFFSET_ADDRESS    0            /* Offset relative to audio file header size */
/* Private typedef -----------------------------------------------------------*/

typedef enum {
  BUFFER_OFFSET_NONE = 0,
  BUFFER_OFFSET_HALF,
  BUFFER_OFFSET_FULL,
}BUFFER_StateTypeDef;

typedef struct {
  uint8_t buff[AUDIO_BUFFER_SIZE];
  uint32_t fptr;
  BUFFER_StateTypeDef state;
  uint32_t AudioFileSize;
  uint32_t *SrcAddress;
}AUDIO_BufferTypeDef;

/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
ALIGN_32BYTES (static AUDIO_BufferTypeDef  buffer_ctl);
__IO uint32_t audio_state;
__IO uint32_t uwVolume = 20;
__IO uint32_t uwPauseEnabledStatus = 0;

uint32_t AudioFreq[8] = {96000, 48000, 44100, 32000, 22050, 16000, 11025, 8000};

BSP_AUDIO_Init_t AudioPlayInit;

/* Private function prototypes -----------------------------------------------*/
static void Audio_SetHint(uint32_t Index);
static uint32_t GetData(void *pdata, uint32_t offset, uint8_t *pbuf, uint32_t NbrOfData);
AUDIO_ErrorTypeDef AUDIO_Start(uint32_t *psrc_address, uint32_t file_size);

extern TS_Init_t hTS;
/* Private functions ---------------------------------------------------------*/

/**
  * [url=home.php?mod=space&uid=159083]@brief[/url] Audio Play demo
  * @param  None
  * @retval None
  */
void AudioPlay_demo(void)
{
  uint32_t *AudioFreq_ptr;
  uint32_t y_size,x_size;
  uint32_t CurrentTickDetect =0;
  uint32_t LastTSTick = 0;
  uint32_t TS_Available = 1;
  uint16_t x1, y1;
  TS_State_t  TS_State;
  uint8_t VolStr[256] = {0};
  uint8_t FreqStr[256] = {0};
  int32_t ts_status = BSP_ERROR_NONE;
  int32_t nor_status = BSP_ERROR_NONE;
  Point Points2[] = {{226, 196}, {265, 223}, {226, 248}};
  BSP_XSPI_NOR_Init_t NorInit;

  BSP_LCD_GetXSize(0, &x_size);
  BSP_LCD_GetYSize(0, &y_size);

  AudioFreq_ptr = &AudioFreq[1]; /*48K*/
  UserButtonPressed = 0;

  uwPauseEnabledStatus = 1; /* 0 when audio is running, 1 when Pause is on */
  uwVolume = 90;

  Audio_SetHint(0);
  UTIL_LCD_SetFont(&Font20);

  UserButtonPressed = 0;

  /* External Nor Flash initialization to get audio file from */
  NorInit.InterfaceMode = MX66UW1G45G_OPI_MODE;
  NorInit.TransferRate = MX66UW1G45G_DTR_TRANSFER;

  if (BSP_XSPI_NOR_Init(0, &NorInit) != BSP_ERROR_NONE)
  {
    nor_status = BSP_ERROR_NO_INIT;
  }
  else if (BSP_XSPI_NOR_EnableMemoryMappedMode(0) != BSP_ERROR_NONE)
  {
    nor_status = BSP_ERROR_PERIPH_FAILURE;
  }

  if (nor_status != BSP_ERROR_NONE)
  {
    UTIL_LCD_SetBackColor(UTIL_LCD_COLOR_WHITE);
    UTIL_LCD_SetTextColor(UTIL_LCD_COLOR_RED);
    UTIL_LCD_DisplayStringAt(0, y_size - 95, (uint8_t *)"ERROR", CENTER_MODE);
    UTIL_LCD_DisplayStringAt(0, y_size - 80, (uint8_t *)"NOR flash XSPI cannot be initialized", CENTER_MODE);
  }

  hTS.Width = x_size;
  hTS.Height = y_size;
  hTS.Orientation = TS_SWAP_NONE;
  hTS.Accuracy = 10;

  /* Touchscreen initialization */
  ts_status = BSP_TS_Init(0, &hTS);

  if (ts_status != BSP_ERROR_NONE)
  {
    UTIL_LCD_SetBackColor(UTIL_LCD_COLOR_WHITE);
    UTIL_LCD_SetTextColor(UTIL_LCD_COLOR_RED);
    UTIL_LCD_DisplayStringAt(0, y_size - 95, (uint8_t *)"ERROR", CENTER_MODE);
    UTIL_LCD_DisplayStringAt(0, y_size - 80, (uint8_t *)"Touch Screen cannot be initialized", CENTER_MODE);
  }

  AudioPlayInit.Device = AUDIO_OUT_HEADPHONE;
  AudioPlayInit.ChannelsNbr = 2;
  AudioPlayInit.SampleRate = *AudioFreq_ptr;
  AudioPlayInit.BitsPerSample = AUDIO_RESOLUTION_16B;
  AudioPlayInit.Volume = uwVolume;

  if(BSP_AUDIO_OUT_Init(0, &AudioPlayInit) != 0)
  {
    UTIL_LCD_SetBackColor(UTIL_LCD_COLOR_WHITE);
    UTIL_LCD_SetTextColor(UTIL_LCD_COLOR_RED);
    UTIL_LCD_DisplayStringAt(0, y_size - 95, (uint8_t *)"  AUDIO CODEC  FAIL ", CENTER_MODE);
    UTIL_LCD_DisplayStringAt(0, y_size - 80, (uint8_t *)" Try to reset board ", CENTER_MODE);
  }

  /*
  Start playing the file from a circular buffer, once the DMA is enabled, it is
  always in running state. Application has to fill the buffer with the audio data
  using Transfer complete and/or half transfer complete interrupts callbacks
  (BSP_AUDIO_OUT_TransferComplete_CallBack() or BSP_AUDIO_OUT_HalfTransfer_CallBack()...
  */
  AUDIO_Start((uint32_t *)AUDIO_SRC_FILE_ADDRESS + PLAY_HEADER, (uint32_t)AUDIO_FILE_SIZE);


  while (1)
  {

    AUDIO_Process();
  }

}

 

AUDIO_Start((uint32_t *)AUDIO_SRC_FILE_ADDRESS + PLAY_HEADER, (uint32_t)AUDIO_FILE_SIZE);

 

就是音频播放的开始。

uint8_t AUDIO_Process(void)就是从Flash存储中读取音乐文件。

AUDIO_Process();就是音乐循环反复播放音乐文件,这里用到了双缓冲DMA。

 

 

/**
  * @brief  Gets Data from storage unit.
  * @param  None
  * @retval None
  */
static uint32_t GetData(void *pdata, uint32_t offset, uint8_t *pbuf, uint32_t NbrOfData)
{
  uint8_t *lptr = pdata;
  uint32_t ReadDataNbr;

  ReadDataNbr = 0;
  while(((offset + ReadDataNbr) < buffer_ctl.AudioFileSize) && (ReadDataNbr < NbrOfData))
  {
    pbuf[ReadDataNbr]= lptr [offset + ReadDataNbr];
    ReadDataNbr++;
  }

  return ReadDataNbr;
}

/**
  * @brief  Manages Audio process.
  * @param  None
  * @retval Audio error
  */
uint8_t AUDIO_Process(void)
{
  uint32_t bytesread;
  AUDIO_ErrorTypeDef error_state = AUDIO_ERROR_NONE;

  switch(audio_state)
  {
  case AUDIO_OUT_STATE_PLAYING:

    if(buffer_ctl.fptr >= buffer_ctl.AudioFileSize)
    {
      /* Play audio sample again ... */
      buffer_ctl.fptr = 0;
      error_state = AUDIO_ERROR_EOF;
    }

    /* 1st half buffer played; so fill it and continue playing from bottom*/
    if(buffer_ctl.state == BUFFER_OFFSET_HALF)
    {
      bytesread = GetData((void *)buffer_ctl.SrcAddress,
                          buffer_ctl.fptr,
                          &buffer_ctl.buff[0],
                          AUDIO_BUFFER_SIZE /2);

      if( bytesread >0)
      {
        buffer_ctl.state = BUFFER_OFFSET_NONE;
        buffer_ctl.fptr += bytesread;
      }
    }

    /* 2nd half buffer played; so fill it and continue playing from top */
    if(buffer_ctl.state == BUFFER_OFFSET_FULL)
    {
      bytesread = GetData((void *)buffer_ctl.SrcAddress,
                          buffer_ctl.fptr,
                          &buffer_ctl.buff[AUDIO_BUFFER_SIZE /2],
                          AUDIO_BUFFER_SIZE /2);
      if( bytesread > 0)
      {
        buffer_ctl.state = BUFFER_OFFSET_NONE;
        buffer_ctl.fptr += bytesread;
      }
    }
    break;

  default:
    error_state = AUDIO_ERROR_NOTREADY;
    break;
  }
  return (uint8_t) error_state;
}

 

 

 

最后我们插上3.5mm耳机,烧录程序,就能听到流行音乐《秒针》响起,一起尽情摇摆吧!

 

 

 

 

 

 

 

STM32播放流行音乐

 

 

 

 

 

此帖出自stm32/stm8论坛

最新回复

做个网络的,在线播放   详情 回复 发表于 2024-10-29 18:55
点赞 关注
 

回复
举报

6459

帖子

10

TA的资源

版主

沙发
 

可以弄个打击乐的乐器     

此帖出自stm32/stm8论坛

点评

这个可以有  详情 回复 发表于 2024-10-29 18:06
 
个人签名

在爱好的道路上不断前进,在生活的迷雾中播撒光引

 

回复

735

帖子

4

TA的资源

纯净的硅(初级)

板凳
 

此帖出自stm32/stm8论坛
 
个人签名徐建
20091127
 

回复

6968

帖子

11

TA的资源

版主

4
 
强呀,我一直找WM8904的例程,没搞定,这次找到了,感谢大神的分享!
此帖出自stm32/stm8论坛

点评

官方的BSP是要好好利用上,底层的东西就没必要重复造轮子了。  详情 回复 发表于 2024-10-29 18:07
 
 
 

回复

169

帖子

1

TA的资源

纯净的硅(初级)

5
 
秦天qintian0303 发表于 2024-10-29 09:09 可以弄个打击乐的乐器     

这个可以有

此帖出自stm32/stm8论坛
 
 
 

回复

169

帖子

1

TA的资源

纯净的硅(初级)

6
 
lugl4313820 发表于 2024-10-29 17:11 强呀,我一直找WM8904的例程,没搞定,这次找到了,感谢大神的分享!

官方的BSP是要好好利用上,底层的东西就没必要重复造轮子了。

此帖出自stm32/stm8论坛
 
 
 

回复

7608

帖子

2

TA的资源

五彩晶圆(高级)

7
 

做个网络的,在线播放

此帖出自stm32/stm8论坛

点评

有这个想法呢,还得继续摸索,搞定wifi……  详情 回复 发表于 2024-10-29 22:35
 
个人签名

默认摸鱼,再摸鱼。2022、9、28

 
 

回复

169

帖子

1

TA的资源

纯净的硅(初级)

8
 
freebsder 发表于 2024-10-29 18:55 做个网络的,在线播放

有这个想法呢,还得继续摸索,搞定wifi……

此帖出自stm32/stm8论坛
 
 
 

回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/8 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表