The STM32H7S78-DK development board is a super development board with unrivaled multimedia functions .
Why do I say that? It has digital microphone, I2S audio codec, SD card and Flash storage, 5-inch high-definition TFT LCD, and WIFI expansion card.
When these functions are combined together, you can unleash endless imagination. It can be used as a digital photo album, an Internet radio, or a smart speaker like Xiao Ai. In short, the gameplay is diverse and the potential is endless.
In this article, we will experience the most basic audio playback function, that is, playing pop music stored in Flash.
The general logic is this:
But the problem is that the whole process involves Flash reading, audio file parsing (Wav), I2S (DMA mode), WM8904 driver...
How can we master so many complex and profound contents in a short period of time to complete a simple task: playing pop music?
After a period of exploration, the solution came: using the official BSP (board support package)
There are all the functions we need here. Whatever function we want to achieve, we just need to set the parameters and call it. There is no need to fully understand the complicated process. Everything is done in target-driven mode.
Okay, without further ado, let’s start our pop music journey.
1. Import the official routine BSP in CubeIDE.
2. Read README.md carefully, which details what demo programs this BSP has and how to use it.
3. The most critical step: burning the music file (bin) into the Flash storage.
### <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)
Here he provides a piece of sample music audio_sample_tdm.bin (size 512KB), we will replace it with the pop music we want to play.
You must be wondering where to get audio in bin format.
First, download an mp3, and then go to an online mp3 to wav format website, of which there are many.
for example:
https://www.aconvert.com/cn/audio/mp3-to-wav/
Bit rate and sampling rate reference:
After getting the WAV format, directly change the suffix of .wav to .bin (simple and crude, in fact, wav is the most original i2s digital audio file)
WAV is a sound file format developed by Microsoft. It complies with the RIFF (Resource Interchange File Format) file specification and is used to save audio information resources on the Windows platform. It is widely supported by the Windows platform and its applications. The format also supports multiple compression algorithms such as MSADPCM and CCITT A LAW, and supports multiple audio numbers, sampling frequencies and channels. The standard formatted WAV file has a sampling frequency of 44100Hz and a sampling bit of 16bit. Therefore, the standard (here, standard is just a widely used waveform audio solution) WAV file is the same as the CD audio format, with a sampling frequency of 44.1KHz and a 16-bit quantization number. The sound file quality is almost the same as CD audio.
Burn our pop music miaozhen.bin (about 10.9MB) into the Flash of the development board through STM32CubeProgrammer.
The first step is to connect the development board and select the Flash driver: MX66UW1G45G_STM32H7S78-DK.
Step 2: Select our bin format audio, enter the starting address ( 0x700A0000 ), and start burning.
4. Make a major change to the official support package BSP to implement our function: play pop music
As you can see in main.c, BSP has a total of 6 functions, which are selected by user buttons. We will keep it simple and only need the audio play function.
So just block this program block and change it to: AudioPlay_demo();
Go to main.h and check the definition of the audio file:
/* 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 is easy to understand. It is the address we just wrote to Flash: 0x700A0000 .
AUDIO_FILE_SIZE is the file size of the audio segment:
Using the file manager, we can see that the bin file is 11,520,128 bits, which is converted to hexadecimal: 0xAFC880 , which is the AUDIO_FILE_SIZE above.
This must be aligned, otherwise there will be confusion during the loop playback.
Finally, we come to the core file of playing music, audio_play.c:
We can read the functions of each part carefully and discard the redundant ones.
In the core while loop, only
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);
This is when audio playback starts.
uint8_t AUDIO_Process(void) reads music files from Flash storage.
AUDIO_Process(); is the music loop that plays the music file repeatedly, and double-buffered DMA is used here.
/**
* @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;
}
Finally, we plug in the 3.5mm earphones and burn the program, then we can hear the popular music "Second Hand" and let's rock out together!
STM32播放流行音乐