cc1989summer 发表于 2024-10-28 23:58

【STM32H7S78-DK】⑩播放流行音乐,大家一起摇摆!

<div class='showpostmsg'><p><span style="font-size:18px;"><strong>STM32H7S78-DK 开发板是具备<span style="color:#f1c40f;">无敌多媒体功能</span>的超级开发板。</strong></span></p>

<p>&nbsp;</p>

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

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

<p>&nbsp;</p>

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

<p>&nbsp;</p>

<p>大致逻辑是这样的:</p>

<p>&nbsp;</p>

<p> &nbsp;</p>

<p>但问题是,整个流程涉及Flash读取、音频文件解析(Wav)、I2S(DMA方式)、WM8904驱动&hellip;&hellip;</p>

<p>短时间内如何掌握这么多复杂高深的内容,从而完成我们一个简单的任务:<span style="color:#e74c3c;"><span style="font-size:22px;"><strong>播放流行音乐</strong></span></span></p>

<p>&nbsp;</p>

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

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

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

<p>&nbsp;</p>

<p>1. 在CubeIDE中导入官方例程BSP。</p>

<p>&nbsp;</p>

<p> &nbsp;</p>

<p>&nbsp;</p>

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

<p>&nbsp;</p>

<p> &nbsp;</p>

<p>&nbsp;</p>

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

<p>&nbsp;</p>

<p>### &lt;b&gt;How to use it ?&lt;/b&gt;</p>

<p>&nbsp;</p>

<p>- Use STM32CubeProgrammer, available on <a href="http://www.st.com" target="_blank">www.st.com</a> or any other in system programming</p>

<p>tool to load &quot;BSP/Binary/<span style="color:#9b59b6;"><strong>audio_sample_tdm.bin</strong></span>&quot; file to the external QSPI flash</p>

<p>at the address 0x700A0000.</p>

<p>-<span style="color:#e74c3c;"><strong> Open the STM32CubeProgrammer tool</strong></span></p>

<p><span style="color:#e74c3c;"><strong>- Select the octoSPI external flash loader &quot;MX66UW1G45G_STM32H7S78-DK&quot;</strong></span></p>

<p><span style="color:#e74c3c;"><strong>- From Erasing &amp; Programming menu, browse and open the output binary file relative to this example</strong></span></p>

<p><span style="color:#e74c3c;"><strong>- Load the file into the external QSPI flash using &quot;Start Programming&quot; at the address APPLICATION_ADDRESS (0x700A0000)</strong></span></p>

<p>&nbsp;</p>

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

<p>&nbsp;</p>

<p> &nbsp;</p>

<p>&nbsp;</p>

<p>&nbsp;</p>

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

<p>&nbsp;</p>

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

<p>比如:</p>

<p><a href="https://www.aconvert.com/cn/audio/mp3-to-wav/" target="_blank">https://www.aconvert.com/cn/audio/mp3-to-wav/</a></p>

<p>&nbsp;</p>

<p>比特率和采样率参考:</p>

<p>&nbsp;</p>

<p> &nbsp;</p>

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

<p>&nbsp;</p>

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

<p>&nbsp;</p>

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

<p>&nbsp;</p>

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

<p>&nbsp;</p>

<p> &nbsp;</p>

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

<p>&nbsp;</p>

<p> &nbsp;</p>

<p>&nbsp;</p>

<p>4. 对官方支持包BSP大做手术,实现我们的功能:<span style="font-size:22px;"><span style="color:#e67e22;"><strong>播放流行音乐</strong></span></span></p>

<p>&nbsp;</p>

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

<p>&nbsp;</p>

<p>&nbsp;</p>

<p> &nbsp;</p>

<p>&nbsp;</p>

<p>&nbsp;</p>

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

<p>&nbsp;</p>

<p> &nbsp;</p>

<p>&nbsp;</p>

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

<p>&nbsp;</p>

<pre>
<code>/* 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</code></pre>

<p>AUDIO_SRC_FILE_ADDRESS好理解,就是刚才我们写入Flash的地址:<strong>0x700A0000</strong>。</p>

<p>AUDIO_FILE_SIZE就是该段音频的文件大小:</p>

<p>&nbsp;</p>

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

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

<p>&nbsp;</p>

<p>&nbsp;</p>

<p> &nbsp;</p>

<p>&nbsp;</p>

<p>&nbsp;</p>

<p>&nbsp;</p>

<p> &nbsp;</p>

<p>&nbsp;</p>

<p>&nbsp;</p>

<p>&nbsp;</p>

<p><strong>最后来到播放音乐的核心&nbsp;audio_play.c文件中:</strong></p>

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

<p>&nbsp;</p>

<p>最核心的while循环中,仅保留</p>

<p>AUDIO_Process();</p>

<pre>
<code>#include "main.h"
#include &lt;stdio.h&gt;

/** @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;
uint32_t fptr;
BUFFER_StateTypeDef state;
uint32_t AudioFileSize;
uint32_t *SrcAddress;
}AUDIO_BufferTypeDef;

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

uint32_t AudioFreq = {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 ---------------------------------------------------------*/

/**
* @brief Audio Play demo
* @paramNone
* @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_tTS_State;
uint8_t VolStr = {0};
uint8_t FreqStr = {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, &amp;x_size);
BSP_LCD_GetYSize(0, &amp;y_size);

AudioFreq_ptr = &amp;AudioFreq; /*48K*/
UserButtonPressed = 0;

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

Audio_SetHint(0);
UTIL_LCD_SetFont(&amp;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, &amp;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, &amp;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, &amp;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 CODECFAIL ", 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();
}

}</code></pre>

<p>&nbsp;</p>

<p>AUDIO_Start((uint32_t *)AUDIO_SRC_FILE_ADDRESS + PLAY_HEADER, (uint32_t)AUDIO_FILE_SIZE);</p>

<p>&nbsp;</p>

<p>就是音频播放的开始。</p>

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

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

<p>&nbsp;</p>

<p>&nbsp;</p>

<pre>
<code>/**
* @briefGets Data from storage unit.
* @paramNone
* @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) &lt; buffer_ctl.AudioFileSize) &amp;&amp; (ReadDataNbr &lt; NbrOfData))
{
    pbuf= lptr ;
    ReadDataNbr++;
}

return ReadDataNbr;
}

/**
* @briefManages Audio process.
* @paramNone
* @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 &gt;= 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,
                        &amp;buffer_ctl.buff,
                        AUDIO_BUFFER_SIZE /2);

      if( bytesread &gt;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,
                        &amp;buffer_ctl.buff,
                        AUDIO_BUFFER_SIZE /2);
      if( bytesread &gt; 0)
      {
      buffer_ctl.state = BUFFER_OFFSET_NONE;
      buffer_ctl.fptr += bytesread;
      }
    }
    break;

default:
    error_state = AUDIO_ERROR_NOTREADY;
    break;
}
return (uint8_t) error_state;
}</code></pre>

<p>&nbsp;</p>

<p>&nbsp;</p>

<p>&nbsp;</p>

<p><span style="color:#f39c12;"><strong><span style="font-size:24px;">最后我们插上3.5mm耳机,烧录程序,就能听到流行音乐《秒针》响起,一起尽情摇摆吧!</span></strong></span></p>

<p>&nbsp;</p>

<p>&nbsp;</p>

<p><span style="color:#f39c12;"><strong><span style="font-size:24px;"><img height="50" src="https://bbs.eeworld.com.cn/static/editor/plugins/hkemoji/sticker/facebook/wanwan21.gif" width="63" /></span></strong></span></p>

<p>&nbsp;</p>

<p> &nbsp;</p>

<p>&nbsp;</p>

<p>&nbsp;</p>

<p>&nbsp;</p>

<p>f7e15894b95fcff90c7ed44dfeeb40f7<br />
&nbsp;</p>

<p>&nbsp;</p>

<p>&nbsp;</p>

<p>&nbsp;</p>

<p>&nbsp;</p>
</div><script>                                        var loginstr = '<div class="locked">查看本帖全部内容,请<a href="javascript:;"   style="color:#e60000" class="loginf">登录</a>或者<a href="https://bbs.eeworld.com.cn/member.php?mod=register_eeworld.php&action=wechat" style="color:#e60000" target="_blank">注册</a></div>';
                                       
                                        if(parseInt(discuz_uid)==0){
                                                                                                (function($){
                                                        var postHeight = getTextHeight(400);
                                                        $(".showpostmsg").html($(".showpostmsg").html());
                                                        $(".showpostmsg").after(loginstr);
                                                        $(".showpostmsg").css({height:postHeight,overflow:"hidden"});
                                                })(jQuery);
                                        }                </script><script type="text/javascript">(function(d,c){var a=d.createElement("script"),m=d.getElementsByTagName("script"),eewurl="//counter.eeworld.com.cn/pv/count/";a.src=eewurl+c;m.parentNode.insertBefore(a,m)})(document,523)</script>

秦天qintian0303 发表于 2024-10-29 09:09

<p>可以弄个打击乐的乐器&nbsp; &nbsp; &nbsp;</p>

xujian2000 发表于 2024-10-29 16:46

<p><img height="48" src="https://bbs.eeworld.com.cn/static/editor/plugins/hkemoji/sticker/facebook/wanwan79.gif" width="52" /><img height="48" src="https://bbs.eeworld.com.cn/static/editor/plugins/hkemoji/sticker/facebook/wanwan88.gif" width="59" /></p>

lugl4313820 发表于 2024-10-29 17:11

强呀,我一直找WM8904的例程,没搞定,这次找到了,感谢大神的分享!

cc1989summer 发表于 2024-10-29 18:06

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

<p>这个可以有:victory:</p>

cc1989summer 发表于 2024-10-29 18:07

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

<p>官方的BSP是要好好利用上,底层的东西就没必要重复造轮子了。{:1_138:}</p>

freebsder 发表于 2024-10-29 18:55

<p>做个网络的,在线播放<img height="48" src="https://bbs.eeworld.com.cn/static/editor/plugins/hkemoji/sticker/facebook/loveliness.gif" width="48" /></p>

cc1989summer 发表于 2024-10-29 22:35

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

<p>有这个想法呢,还得继续摸索,搞定wifi……:victory:</p>
页: [1]
查看完整版本: 【STM32H7S78-DK】⑩播放流行音乐,大家一起摇摆!