【STM32H7S78-DK】⑩播放流行音乐,大家一起摇摆!
<div class='showpostmsg'><p><span style="font-size:18px;"><strong>STM32H7S78-DK 开发板是具备<span style="color:#f1c40f;">无敌多媒体功能</span>的超级开发板。</strong></span></p><p> </p>
<p>为什么这么讲呢,他具备<strong>数字麦克风、I2S音频编解码、SD卡及Flash存储、5英寸高清晰TFT LCD、WIFI扩展卡。</strong></p>
<p><strong>这些功能集合到一块,可以发挥无穷想象,既可以做一个数码相册、也可以做一个网络收音机、或者是像小爱同学一样的智能音箱,总之玩法多样,潜力无穷。</strong></p>
<p> </p>
<p>本篇我们来体验最基本的音频播放功能,即播放存储在Flash上的流行音乐。</p>
<p> </p>
<p>大致逻辑是这样的:</p>
<p> </p>
<p> </p>
<p>但问题是,整个流程涉及Flash读取、音频文件解析(Wav)、I2S(DMA方式)、WM8904驱动……</p>
<p>短时间内如何掌握这么多复杂高深的内容,从而完成我们一个简单的任务:<span style="color:#e74c3c;"><span style="font-size:22px;"><strong>播放流行音乐</strong></span></span></p>
<p> </p>
<p>经过一段时间的摸索,办法来了:利用官方的<strong>BSP</strong>(board support package,板级支持包)</p>
<p>这里面有我们需要的一切函数,我们想要实现什么功能,直接设置好参数调用就好了,无需彻底搞懂复杂的过程,一切以目标驱动模式完成。</p>
<p>好了,话不多说,开始我们的流行音乐之旅吧。</p>
<p> </p>
<p>1. 在CubeIDE中导入官方例程BSP。</p>
<p> </p>
<p> </p>
<p> </p>
<p>2. 熟读README.md,详细介绍了这个BSP都有啥演示程序,以及如何使用。</p>
<p> </p>
<p> </p>
<p> </p>
<p>3. 最关键的环节:烧录音乐文件(bin)到Flash存储中。</p>
<p> </p>
<p>### <b>How to use it ?</b></p>
<p> </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 "BSP/Binary/<span style="color:#9b59b6;"><strong>audio_sample_tdm.bin</strong></span>" 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 "MX66UW1G45G_STM32H7S78-DK"</strong></span></p>
<p><span style="color:#e74c3c;"><strong>- From Erasing & 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 "Start Programming" at the address APPLICATION_ADDRESS (0x700A0000)</strong></span></p>
<p> </p>
<p>他这里提供了一段实例音乐<strong>audio_sample_tdm.bin(大小512KB),我们将他替换为我们想要播放的流行音乐。</strong></p>
<p> </p>
<p> </p>
<p> </p>
<p> </p>
<p>你肯定纳闷,上哪去搞bin格式的音频。</p>
<p> </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> </p>
<p>比特率和采样率参考:</p>
<p> </p>
<p> </p>
<p>得到WAV格式后,<strong>直接将.wav后缀改为.bin</strong>(简单粗暴有木有,其实wav就是最原始的i2s数字音频文件)</p>
<p> </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> </p>
<p><strong>将我们的流行音乐miaozhen.bin(大约10.9MB)通过STM32CubeProgrammer烧录到开发板的Flash中。</strong></p>
<p> </p>
<p>第一步,连接开发板,选择Flash驱动:MX66UW1G45G_STM32H7S78-DK。</p>
<p> </p>
<p> </p>
<p>第二部:选择我们的bin格式音频,输入起始地址(<strong>0x700A0000</strong>),开始烧录。</p>
<p> </p>
<p> </p>
<p> </p>
<p>4. 对官方支持包BSP大做手术,实现我们的功能:<span style="font-size:22px;"><span style="color:#e67e22;"><strong>播放流行音乐</strong></span></span></p>
<p> </p>
<p>main.c中可以看到,BSP总共6个功能,由用户按钮选择,我们简单干脆,<strong>只要audio play功能。</strong></p>
<p> </p>
<p> </p>
<p> </p>
<p> </p>
<p> </p>
<p>所以把该程序块直接屏蔽,仅改为:<strong>AudioPlay_demo();</strong></p>
<p> </p>
<p> </p>
<p> </p>
<p>来到main.h中,查看对音频文件的定义:</p>
<p> </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> </p>
<p>用文件管理器可看到,该bin文件为11,520,128 bit,换算为16进制为:<strong>0xAFC880</strong>,也就是上面的AUDIO_FILE_SIZE</p>
<p>这里一定要对上,否则在循环播放的过程中会错乱。</p>
<p> </p>
<p> </p>
<p> </p>
<p> </p>
<p> </p>
<p> </p>
<p> </p>
<p> </p>
<p> </p>
<p> </p>
<p><strong>最后来到播放音乐的核心 audio_play.c文件中:</strong></p>
<p>我们可以仔细阅读各部分的功能,多余的也不要了。</p>
<p> </p>
<p>最核心的while循环中,仅保留</p>
<p>AUDIO_Process();</p>
<pre>
<code>#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;
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, &x_size);
BSP_LCD_GetYSize(0, &y_size);
AudioFreq_ptr = &AudioFreq; /*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 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> </p>
<p>AUDIO_Start((uint32_t *)AUDIO_SRC_FILE_ADDRESS + PLAY_HEADER, (uint32_t)AUDIO_FILE_SIZE);</p>
<p> </p>
<p>就是音频播放的开始。</p>
<p>uint8_t AUDIO_Process(void)就是从Flash存储中读取音乐文件。</p>
<p>AUDIO_Process();就是音乐循环反复播放音乐文件,这里用到了双缓冲DMA。</p>
<p> </p>
<p> </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) < buffer_ctl.AudioFileSize) && (ReadDataNbr < 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 >= 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,
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);
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;
}</code></pre>
<p> </p>
<p> </p>
<p> </p>
<p><span style="color:#f39c12;"><strong><span style="font-size:24px;">最后我们插上3.5mm耳机,烧录程序,就能听到流行音乐《秒针》响起,一起尽情摇摆吧!</span></strong></span></p>
<p> </p>
<p> </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> </p>
<p> </p>
<p> </p>
<p> </p>
<p> </p>
<p>f7e15894b95fcff90c7ed44dfeeb40f7<br />
</p>
<p> </p>
<p> </p>
<p> </p>
<p> </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> <p>可以弄个打击乐的乐器 </p>
<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>
强呀,我一直找WM8904的例程,没搞定,这次找到了,感谢大神的分享! 秦天qintian0303 发表于 2024-10-29 09:09
可以弄个打击乐的乐器
<p>这个可以有:victory:</p>
lugl4313820 发表于 2024-10-29 17:11
强呀,我一直找WM8904的例程,没搞定,这次找到了,感谢大神的分享!
<p>官方的BSP是要好好利用上,底层的东西就没必要重复造轮子了。{:1_138:}</p>
<p>做个网络的,在线播放<img height="48" src="https://bbs.eeworld.com.cn/static/editor/plugins/hkemoji/sticker/facebook/loveliness.gif" width="48" /></p>
freebsder 发表于 2024-10-29 18:55
做个网络的,在线播放
<p>有这个想法呢,还得继续摸索,搞定wifi……:victory:</p>
页:
[1]