你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【STM32F769I-DISC1】实现音乐播放

[复制链接]
lugl 发布时间:2025-3-17 19:44

【前言】

前面我分享了SD卡以及LVGL的相关的文章:【STM32F769】读取音乐列表 - ST中文论坛活动 ST意法半导体中文论坛

在他的基础之上,我偿试读取.wav文件,并实现音乐的播放。

【移植】

在vscode中,我复制了管方的SAI驱动,以及wm8994的驱动到工程中。

image.png

2、编写播的代码adudio_play.c

void AudioPlay_demo(void)
{
    uint32_t AudioFreq = AF_48K;
    uint32_t *AudioFreq_ptr = &AudioFreq;

    /* Initialize FatFs */
    FRESULT fres;
    FATFS fs;
    fres = f_mount(&fs, "", 1);
    if (fres != FR_OK)
    {
        printf("无法挂载文件系统\n");
        return;
    }

    /* Initialize Audio Codec */
    if (BSP_AUDIO_OUT_Init(OUTPUT_DEVICE_BOTH, uwVolume, *AudioFreq_ptr) == 0)
    {
        printf("  AUDIO CODEC   OK  \n");
    }
    else
    {
        printf("  AUDIO CODEC  FAIL \n");
        printf(" Try to reset board \n");
    }

    /* Set Audio Frame Slot */
    BSP_AUDIO_OUT_SetAudioFrameSlot(CODEC_AUDIOFRAME_SLOT_02);

    /* Start Audio Playback */
    AUDIO_Start((uint32_t *)"Love.wav", 0); // 传递文件路径

    /* Infinite loop */
    while (1)
    {
        AUDIO_Process();
        osDelay(2);
    }
}

【注】其余代码见1楼。

然后插上SD卡,运行程序就可以听到声音了。

收藏 评论1 发布时间:2025-3-17 19:44

举报

1个回答
lugl 回答时间:2025-3-17 19:45:09
#include "audio_play.h"
#include "main.h"
#include <stdio.h>
#include "wm8994.h"
#include "stm32f769i_discovery_audio.h"
#include "ff.h"
#define AUDIO_DEFAULT_VOLUME 70

#define AUDIO_START_OFFSET_ADDRESS 0 /* Offset relative to audio file header size */
#define AUDIO_BUFFER_SIZE 8096
#define AF_48K 48000

/* Private typedef -----------------------------------------------------------*/
typedef enum
{
    AUDIO_STATE_IDLE = 0,
    AUDIO_STATE_INIT,
    AUDIO_STATE_PLAYING,
} AUDIO_PLAYBACK_StateTypeDef;

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;
    FIL file;                 // 添加文件句柄
    uint32_t AudioFileSize;   // 添加 AudioFileSize 成员
    uint32_t AudioSampleRate; // 添加 AudioSampleRate 成员
} AUDIO_BufferTypeDef;

typedef enum
{
    TS_ACT_NONE = 0,
    TS_ACT_VOLUME_DOWN,
    TS_ACT_VOLUME_UP,
    TS_ACT_PAUSE = 0xFE
} TS_ActionTypeDef;

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

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);
uint8_t AUDIO_Process(void);
/* Private functions ---------------------------------------------------------*/

/**
 * @brief  Audio Play demo
 * @param  None
 * @retval None
 */
void AudioPlay_demo(void)
{
    uint32_t AudioFreq = AF_48K;
    uint32_t *AudioFreq_ptr = &AudioFreq;

    /* Initialize FatFs */
    FRESULT fres;
    FATFS fs;
    fres = f_mount(&fs, "", 1);
    if (fres != FR_OK)
    {
        printf("无法挂载文件系统\n");
        return;
    }

    /* Initialize Audio Codec */
    if (BSP_AUDIO_OUT_Init(OUTPUT_DEVICE_BOTH, uwVolume, *AudioFreq_ptr) == 0)
    {
        printf("  AUDIO CODEC   OK  \n");
    }
    else
    {
        printf("  AUDIO CODEC  FAIL \n");
        printf(" Try to reset board \n");
    }

    /* Set Audio Frame Slot */
    BSP_AUDIO_OUT_SetAudioFrameSlot(CODEC_AUDIOFRAME_SLOT_02);

    /* Start Audio Playback */
    AUDIO_Start((uint32_t *)"Love.wav", 0); // 传递文件路径

    /* Infinite loop */
    while (1)
    {
        AUDIO_Process();
        osDelay(2);
    }
}

/**
 * @brief  Starts Audio streaming.
 * @param  None
 * @retval Audio error
 */
/**
 * @brief  Starts Audio streaming.
 * @param  None
 * @retval Audio error
 */
AUDIO_ErrorTypeDef AUDIO_Start(uint32_t *psrc_address, uint32_t file_size)
{
    FRESULT fres;
    fres = f_open(&buffer_ctl.file, (const char *)psrc_address, FA_READ);
    if (fres != FR_OK)
    {
        printf("无法打开文件\n");
        return AUDIO_ERROR_IO;
    }

    // 读取 WAV 文件头部信息
    uint8_t wav_header[44];
    fres = f_read(&buffer_ctl.file, wav_header, sizeof(wav_header), NULL);
    if (fres != FR_OK)
    {
        printf("无法读取 WAV 文件头部信息\n");
        f_close(&buffer_ctl.file);
        return AUDIO_ERROR_IO;
    }

    // 检查 WAV 文件头部信息
    if (memcmp(wav_header, "RIFF", 4) != 0 || memcmp(wav_header + 8, "WAVE", 4) != 0)
    {
        printf("不是有效的 WAV 文件\n");
        f_close(&buffer_ctl.file);
        return AUDIO_ERROR_IO;
    }

    // 获取采样率
    buffer_ctl.AudioSampleRate = (wav_header[24] << 0) |
                                 (wav_header[25] << 8) |
                                 (wav_header[26] << 16) |
                                 (wav_header[27] << 24);

    // 初始化音频编解码器,使用 WAV 文件的采样率
    if (BSP_AUDIO_OUT_Init(OUTPUT_DEVICE_BOTH, uwVolume, buffer_ctl.AudioSampleRate) != 0)
    {
        printf("  AUDIO CODEC  FAIL \n");
        printf(" Try to reset board \n");
        f_close(&buffer_ctl.file);
        return AUDIO_ERROR_IO;
    }

    // 获取文件大小
    buffer_ctl.AudioFileSize = f_size(&buffer_ctl.file);
    buffer_ctl.state = BUFFER_OFFSET_NONE;
    buffer_ctl.fptr = 0;

    // 重新设置文件指针到数据块开始位置
    uint32_t data_chunk_offset = (wav_header[36] << 0) |
                                 (wav_header[37] << 8) |
                                 (wav_header[38] << 16) |
                                 (wav_header[39] << 24);
    fres = f_lseek(&buffer_ctl.file, data_chunk_offset);
    if (fres != FR_OK)
    {
        printf("无法设置文件指针到数据块开始位置\n");
        f_close(&buffer_ctl.file);
        return AUDIO_ERROR_IO;
    }

    uint32_t bytesread = GetData(NULL, 0, &buffer_ctl.buff[0], AUDIO_BUFFER_SIZE);
    if (bytesread > 0)
    {
        BSP_AUDIO_OUT_Play((uint16_t *)&buffer_ctl.buff[0], AUDIO_BUFFER_SIZE);
        audio_state = AUDIO_STATE_PLAYING;
        buffer_ctl.fptr = bytesread;
        return AUDIO_ERROR_NONE;
    }
    return AUDIO_ERROR_IO;
}

/**
 * @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_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(NULL, 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(NULL, 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;
}

/**
 * @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)
{
    FRESULT fres;
    UINT bytesRead;

    fres = f_lseek(&buffer_ctl.file, offset);
    if (fres != FR_OK)
    {
        printf("无法设置文件指针\n");
        return 0;
    }

    fres = f_read(&buffer_ctl.file, pbuf, NbrOfData, &bytesRead);
    if (fres != FR_OK)
    {
        printf("无法读取文件\n");
        return 0;
    }

    return bytesRead;
}

/*------------------------------------------------------------------------------
       Callbacks implementation:
           the callbacks API are defined __weak in the stm32769i_discovery_audio.c file
           and their implementation should be done the user code if they are needed.
           Below some examples of callback implementations.
  ----------------------------------------------------------------------------*/
/**
 * @brief  Manages the full Transfer complete event.
 * @param  None
 * @retval None
 */
void BSP_AUDIO_OUT_TransferComplete_CallBack(void)
{
    if (audio_state == AUDIO_STATE_PLAYING)
    {
        /* allows AUDIO_Process() to refill 2nd part of the buffer  */
        buffer_ctl.state = BUFFER_OFFSET_FULL;
    }
}

/**
 * @brief  Manages the DMA Half Transfer complete event.
 * @param  None
 * @retval None
 */
void BSP_AUDIO_OUT_HalfTransfer_CallBack(void)
{
    if (audio_state == AUDIO_STATE_PLAYING)
    {
        /* allows AUDIO_Process() to refill 1st part of the buffer  */
        buffer_ctl.state = BUFFER_OFFSET_HALF;
    }
}

/**
 * @brief  Manages the DMA FIFO error event.
 * @param  None
 * @retval None
 */
void BSP_AUDIO_OUT_Error_CallBack(void)
{
    printf("       DMA  ERROR     \n");
}
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版