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

【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中文论坛活动

即日起开启活动话题入口,之后的活动统一都放在此处,欢迎大家的加入!


最新内容

相似分享

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