RT-Thread组件之Audio框架i2s驱动的编写

前言

  1. rt-thread本次使用的是标志版本为5.0。
  2. 本次实现的是音频播放部分的i2s驱动
  3. 使用的芯片为stm32f407zgt6
  4. 在网上搜了一下关于rt-thread 音频部分的内容,没有实质的提供i2s对接的硬件驱动。所以才有了此次的记录,做一下分享记录。
  5. 参考的git源码驱动,这个是用的sai接口,而f4并没有
  6. 本次注重实际代码部分,关于一些音频相关的理论知识自行查阅相关资料。
  7. 使用到的音频模块为wm8978,相关驱动参考之前的文章
  8. 音频文件保存到sd中,使用的是rt-thread 的虚拟文件系统组件

rt-thread的audio框架

在这里插入图片描述

配置阶段

在这里插入图片描述在这里插入图片描述

播放阶段

在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述
底层驱动需要做的是,通知框架发送下一帧数据 ,不然会阻塞
在这里插入图片描述

录音阶段(本文暂未涉及)

  1. 过程跟播放是类似的

I2S 音频驱动(暂时只做了播放)

头文件

#ifndef F4_OS_DR_I2S_H
#define F4_OS_DR_I2S_H

#include <rtthread.h>
#include <rthw.h>
#include <rtdevice.h>
#include <drv_common.h>
#include <drv_dma.h>
#include <drv_config.h>

#define I2S_TX_DMA_FIFO_SIZE (4096)
#define I2S_SOUND_NAME "sound0"
#define CODEC_I2C_NAME  ("i2c1")
struct stm32_i2s_cnf {
    rt_uint32_t standard;
    rt_uint32_t mode;
    rt_uint32_t data_format;
    rt_uint32_t clk_cpl;
};

struct stm32_audio { 
    I2S_HandleTypeDef i2s;
    DMA_HandleTypeDef dma_tx;
    struct rt_audio_device audio;
    struct rt_audio_configure replay_config;
    struct stm32_i2s_cnf *i2s_conf;
    rt_int32_t replay_volume;
    rt_uint8_t *tx_fifo;
    rt_bool_t startup;
};

int rt_hw_audio_init(void);
#endif //F4_OS_DR_I2S_H

原文件

#include "board.h"
#include "drv_audio.h"
#include "audio_wm8978.h"

#define DBG_TAG              "drv.sound"
#ifdef  DRV_DEBUG
#define DBG_LVL              DBG_LOG
#else
#define DBG_LVL              DBG_LOG
#endif /* DRV_DEBUG */

#include <rtdbg.h>


struct stm32_audio _stm32_audio_play = {
        .i2s.Instance=SPI2
};

#define _i2s _stm32_audio_play.i2s
#define _dma_tx _stm32_audio_play.dma_tx
//采样率计算公式:Fs=I2SxCLK/[256*(2*I2SDIV+ODD)]
//I2SxCLK=(HSE/pllm)*PLLI2SN/PLLI2SR
//一般HSE=8Mhz
//pllm:在Sys_Clock_Set设置的时候确定,一般是8
//PLLI2SN:一般是192~432
//PLLI2SR:2~7
//I2SDIV:2~255
//ODD:0/1
//I2S分频系数表@pllm=8,HSE=8Mhz,即vco输入频率为1Mhz
//表格式:采样率/10,PLLI2SN,PLLI2SR,I2SDIV,ODD
const rt_uint16_t i2s_psc_tbl[][5] =
        {
                {800,   256, 5, 12, 1},        //8Khz采样率
                {1102,  429, 4, 19, 0},        //11.025Khz采样率
                {1600,  213, 2, 13, 0},        //16Khz采样率
                {2205,  429, 4, 9,  1},        //22.05Khz采样率
                {3200,  213, 2, 6,  1},        //32Khz采样率
                {4410,  271, 2, 6,  0},        //44.1Khz采样率
                {4800,  258, 3, 3,  1},        //48Khz采样率
                {8820,  316, 2, 3,  1},        //88.2Khz采样率
                {9600,  344, 2, 3,  1},    //96Khz采样率
                {17640, 361, 2, 2,  0},    //176.4Khz采样率
                {19200, 393, 2, 2,  0},    //192Khz采样率
        };
//设置SAIA的采样率(@MCKEN)
//samplerate:采样率,单位:Hz
//返回值:0,设置成功;1,无法设置.
rt_err_t I2S2_SampleRate_Set(rt_uint32_t samplerate) {

    rt_uint16_t i;
    volatile uint32_t tempreg = 0;
    RCC_PeriphCLKInitTypeDef RCCI2S2_ClkInitSture;
    //看看改采样率是否可以支持
    for (i = 0; i < (sizeof(i2s_psc_tbl) / 10); i++) {
        if ((samplerate / 10) == i2s_psc_tbl[i][0])break;
    }
    if (i == (sizeof(i2s_psc_tbl) / 10))return RT_ERROR;//搜遍了也找不到
    RCCI2S2_ClkInitSture.PeriphClockSelection = RCC_PERIPHCLK_I2S;            //外设时钟源选择
    RCCI2S2_ClkInitSture.PLLI2S.PLLI2SN = (uint32_t) i2s_psc_tbl[i][1];        //设置PLLI2SN
    RCCI2S2_ClkInitSture.PLLI2S.PLLI2SR = (uint32_t) i2s_psc_tbl[i][2];        //设置PLLI2SR
    HAL_RCCEx_PeriphCLKConfig(&RCCI2S2_ClkInitSture);                //设置时钟

    SET_BIT(RCC->CR, RCC_CR_PLLI2SON);       //开启I2S时钟
    while (READ_BIT(RCC->CR, RCC_CR_PLLI2SRDY) == 0);//等待I2S时钟开启成功.


    if (_i2s.Instance->I2SCFGR & SPI_I2SCFGR_I2SE) {
        __HAL_I2S_DISABLE(&_i2s); /*禁止i2s 配置以下寄存器 (手册上说明)*/
    }
    tempreg = i2s_psc_tbl[i][3] << 0;     //设置I2SDIV
    tempreg |= i2s_psc_tbl[i][4] << 8;    //设置ODD位
    tempreg |= 1 << 9;                    //使能MCKOE位,输出MCK
    WRITE_REG(_i2s.Instance->I2SPR, tempreg);//设置I2SPR寄存器
    if (!(_i2s.Instance->I2SCFGR & SPI_I2SCFGR_I2SE)) { /*如果i2s没有启动*/
        __HAL_I2S_ENABLE(&_i2s); /*启用i2s 配置以下寄存器 (手册上说明)*/
    }
    return RT_EOK;
}



//I2S2初始化
//I2S_Standard:I2S标准,可以设置:I2S_STANDARD_PHILIPS/I2S_STANDARD_MSB/
//				       I2S_STANDARD_LSB/I2S_STANDARD_PCM_SHORT/I2S_STANDARD_PCM_LONG
//I2S_Mode:I2S工作模式,可以设置:I2S_MODE_SLAVE_TX/I2S_MODE_SLAVE_RX/I2S_MODE_MASTER_TX/I2S_MODE_MASTER_RX
//I2S_Clock_Polarity:时钟电平,可以设置为:I2S_CPOL_LOW/I2S_CPOL_HIGH
//I2S_DataFormat:数据长度,可以设置:I2S_DATAFORMAT_16B/I2S_DATAFORMAT_16B_EXTENDED/I2S_DATAFORMAT_24B/I2S_DATAFORMAT_32B
static void
stm32_i2s_init(struct stm32_i2s_cnf *i2s_cnf) {


    _i2s.Init.Mode = i2s_cnf->mode;                    //IIS模式
    _i2s.Init.Standard = i2s_cnf->standard;            //IIS标准
    _i2s.Init.DataFormat = i2s_cnf->data_format;        //IIS数据长度
    _i2s.Init.MCLKOutput = I2S_MCLKOUTPUT_ENABLE;    //主时钟输出使能
    _i2s.Init.AudioFreq = I2S_AUDIOFREQ_DEFAULT;    //IIS频率设置
    _i2s.Init.CPOL = i2s_cnf->clk_cpl;          //空闲状态时钟电平
    _i2s.Init.ClockSource = I2S_CLOCK_PLL;        //IIS时钟源为PLL
    HAL_I2S_Init(&_i2s);
    SET_BIT(_i2s.Instance->CR2, SPI_CR2_TXDMAEN); //Tx Buffer DMA Enable
    __HAL_I2S_ENABLE(&_i2s);                    //使能I2S2
}

void audio_channel_set(rt_uint16_t channel) {
    // todo 不支持设置通道
    RT_UNUSED(channel);
}

void audio_samplerate_set(rt_uint32_t samplerate) {
    I2S2_SampleRate_Set(samplerate);
}

void audio_samplebits_set(rt_uint16_t samplebits) {
    if (_stm32_audio_play.i2s_conf) { /*如果用户自行配置的话就有用户的*/
        stm32_i2s_init(_stm32_audio_play.i2s_conf);
        return;
    }
    struct stm32_i2s_cnf i2s_cnf;
    switch (samplebits) {
        case 16:
            LOG_D("飞利浦标准,16位数据长度");
            wm8978_I2S_Cfg(2, 0);  // 飞利浦标准,主机发送,时钟低电平有效,16位帧长度
            i2s_cnf.mode = I2S_MODE_MASTER_TX;
            i2s_cnf.standard = I2S_STANDARD_PHILIPS;
            i2s_cnf.clk_cpl = I2S_CPOL_LOW;
            i2s_cnf.data_format = I2S_DATAFORMAT_16B_EXTENDED;
            break;
        case 24:
            LOG_D("飞利浦标准,24位数据长度");
            wm8978_I2S_Cfg(2, 2);     //飞利浦标准,主机发送,时钟低电平有效,24位扩展帧长度
            i2s_cnf.mode = I2S_MODE_MASTER_TX;
            i2s_cnf.standard = I2S_STANDARD_PHILIPS;
            i2s_cnf.clk_cpl = I2S_CPOL_LOW;
            i2s_cnf.data_format = I2S_DATAFORMAT_24B;
            break;
        case 32:
            wm8978_I2S_Cfg(2, 3);     //飞利浦标准,主机发送,时钟低电平有效,24位扩展帧长度
            i2s_cnf.mode = I2S_MODE_MASTER_TX;
            i2s_cnf.standard = I2S_STANDARD_PHILIPS;
            i2s_cnf.clk_cpl = I2S_CPOL_LOW;
            i2s_cnf.data_format = I2S_DATAFORMAT_32B;
            break;
        default:
            // todo
            return;
    }
    stm32_i2s_init(&i2s_cnf);
}


/* initial sai A */
rt_err_t audio_config_init() {
    rt_uint32_t temp;

    return RT_EOK;
}

static void i2s_transmit_complate(struct __DMA_HandleTypeDef *hdma);

rt_err_t audio_tx_dma(void) {
    __HAL_RCC_DMA1_CLK_ENABLE();                                //使能DMA1时钟
    __HAL_LINKDMA(&_i2s, hdmatx, _dma_tx);                      //将DMA与I2S联系起来
    _dma_tx.Instance = DMA1_Stream4;                            //DMA1数据流4
    _dma_tx.Init.Channel = DMA_CHANNEL_0;                       //通道0
    _dma_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;              //存储器到外设模式
    _dma_tx.Init.PeriphInc = DMA_PINC_DISABLE;                  //外设非增量模式
    _dma_tx.Init.MemInc = DMA_MINC_ENABLE;                      //存储器增量模式
    /*todo 这里按照道理应该根据数据位宽来配置 ?*/
    _dma_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD; //外设数据长度:16位
    _dma_tx.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;    //存储器数据长度:16位
    _dma_tx.Init.Mode = DMA_CIRCULAR;                           //使用循环模式
    _dma_tx.Init.Priority = DMA_PRIORITY_VERY_HIGH;             //高优先级
    _dma_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;               //不使用FIFO
    _dma_tx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;       // 使用FIFO阈值完全配置
    _dma_tx.Init.MemBurst = DMA_MBURST_SINGLE;                  //存储器单次突发传输
    _dma_tx.Init.PeriphBurst = DMA_MBURST_SINGLE;               //外设突发单次传输

    HAL_DMA_DeInit(&_dma_tx);                              //先清除以前的设置
    HAL_DMA_Init(&_dma_tx);                                //初始化DMA
    __HAL_DMA_DISABLE(&_dma_tx);                                 //先关闭DMA
    /*开启dma双缓冲模式*/
    /* Enable the double buffer mode */
    _dma_tx.Instance->CR |= (uint32_t) DMA_SxCR_DBM;
    /* Configure DMA Stream destination address */
    _dma_tx.Instance->M1AR = (uint32_t) (_stm32_audio_play.tx_fifo + I2S_TX_DMA_FIFO_SIZE);
    /* Configure DMA Stream data length */
    _dma_tx.Instance->NDTR = I2S_TX_DMA_FIFO_SIZE / 2;
    /* Configure DMA Stream destination address */
    _dma_tx.Instance->PAR = (uint32_t) &(_i2s.Instance->DR);
    /* Configure DMA Stream source address */
    _dma_tx.Instance->M0AR = (uint32_t) _stm32_audio_play.tx_fifo;
    /* Clear all flags */
    __HAL_DMA_CLEAR_FLAG (&_dma_tx, __HAL_DMA_GET_TC_FLAG_INDEX(&_dma_tx));
    __HAL_DMA_CLEAR_FLAG (&_dma_tx, __HAL_DMA_GET_HT_FLAG_INDEX(&_dma_tx));
    __HAL_DMA_CLEAR_FLAG (&_dma_tx, __HAL_DMA_GET_TE_FLAG_INDEX(&_dma_tx));
    __HAL_DMA_CLEAR_FLAG (&_dma_tx, __HAL_DMA_GET_DME_FLAG_INDEX(&_dma_tx));
    __HAL_DMA_CLEAR_FLAG (&_dma_tx, __HAL_DMA_GET_FE_FLAG_INDEX(&_dma_tx));

    /* Enable Common interrupts*/
    /* todo 这里是否需要开启 错误中断使能 ?*/
    //DMA_IT_TE: 传输错误中断使能 (Transfer error interrupt enable)
    //DMA_IT_DME: 直接模式错误中断使能 (Direct mode error interrupt enable)

    rt_thread_delay(20);
    //DMA_IT_TC: 传输完成中断使能 (Transfer complete interrupt enable)
    __HAL_DMA_ENABLE_IT(&_dma_tx, DMA_IT_TC);                    //开启传输完成中断
    __HAL_DMA_CLEAR_FLAG(&_dma_tx, DMA_FLAG_TCIF0_4);            //清除DMA传输完成中断标志位
    HAL_NVIC_SetPriority(DMA1_Stream4_IRQn, 1, 0);    //DMA中断优先级
    HAL_NVIC_EnableIRQ(DMA1_Stream4_IRQn);

    __HAL_DMA_ENABLE(&_dma_tx);                                //打开dma
    return RT_EOK;
}


static void i2s_dma_tx_set(void) {
    __HAL_RCC_DMA1_CLK_ENABLE(); // 使能DMA1时钟
    __HAL_LINKDMA(&_i2s, hdmatx, _dma_tx);//将DMA与I2S联系起来
    _dma_tx.Instance = DMA1_Stream4;                            //DMA1数据流4
    _dma_tx.Init.Channel = DMA_CHANNEL_0;                        //通道0
    _dma_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;                //存储器到外设模式
    _dma_tx.Init.PeriphInc = DMA_PINC_DISABLE;                    //外设非增量模式
    _dma_tx.Init.MemInc = DMA_MINC_ENABLE;                        //存储器增量模式
    _dma_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;    //外设数据长度:16位
    _dma_tx.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;        //存储器数据长度:16位
    _dma_tx.Init.Mode = DMA_CIRCULAR;                            //使用循环模式
    _dma_tx.Init.Priority = DMA_PRIORITY_VERY_HIGH;                    //高优先级
    _dma_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;                //不使用FIFO
    _dma_tx.Init.MemBurst = DMA_MBURST_SINGLE;                    //存储器单次突发传输
    _dma_tx.Init.PeriphBurst = DMA_PBURST_SINGLE;                //外设突发单次传输
    _dma_tx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
    HAL_DMA_DeInit(&_dma_tx);                                    //先清除以前的设置
    HAL_DMA_Init(&_dma_tx);                                        //初始化DMA

    HAL_DMAEx_MultiBufferStart(&_dma_tx,
                               (uint32_t) _stm32_audio_play.tx_fifo,
                               (uint32_t) &SPI2->DR,
                               (uint32_t) _stm32_audio_play.tx_fifo + I2S_TX_DMA_FIFO_SIZE,
                               I2S_TX_DMA_FIFO_SIZE / 2);///开启双缓冲
    rt_thread_delay(20);
    __HAL_DMA_ENABLE_IT(&_dma_tx, DMA_IT_TC);                                     //开启传输完成中断
    HAL_NVIC_SetPriority(DMA1_Stream4_IRQn, 1, 0);    //DMA中断优先级
    HAL_NVIC_EnableIRQ(DMA1_Stream4_IRQn);
}

void audio_config_set(struct rt_audio_configure config) {
    audio_channel_set(config.channels);
    audio_samplebits_set(config.samplebits);
    audio_samplerate_set(config.samplerate);

}

rt_err_t audio_init() {
    audio_tx_dma();
    audio_config_init();
    return RT_EOK;
}

static rt_err_t stm32_player_getcaps(struct rt_audio_device *audio, struct rt_audio_caps *caps) {
    rt_err_t result = RT_EOK;
    struct stm32_audio *st_audio = (struct stm32_audio *) audio->parent.user_data;

    LOG_D("%s:main_type: %d, sub_type: %d", __FUNCTION__, caps->main_type, caps->sub_type);
    switch (caps->main_type) {
        case AUDIO_TYPE_QUERY: /* query the types of hw_codec device */
        {
            switch (caps->sub_type) {
                case AUDIO_TYPE_QUERY:
                    caps->udata.mask = AUDIO_TYPE_OUTPUT | AUDIO_TYPE_MIXER;
                    break;

                default:
                    result = -RT_ERROR;
                    break;
            }

            break;
        }
        case AUDIO_TYPE_OUTPUT: /* Provide capabilities of OUTPUT unit */
        {
            switch (caps->sub_type) {
                case AUDIO_DSP_PARAM:
                    caps->udata.config.channels = st_audio->replay_config.channels;
                    caps->udata.config.samplebits = st_audio->replay_config.samplebits;
                    caps->udata.config.samplerate = st_audio->replay_config.samplerate;
                    break;

                case AUDIO_DSP_SAMPLERATE:
                    caps->udata.config.samplerate = st_audio->replay_config.samplerate;
                    break;

                case AUDIO_DSP_CHANNELS:
                    caps->udata.config.channels = st_audio->replay_config.channels;
                    break;

                case AUDIO_DSP_SAMPLEBITS:
                    caps->udata.config.samplebits = st_audio->replay_config.samplebits;
                    break;

                default:
                    result = -RT_ERROR;
                    break;
            }

            break;
        }

        case AUDIO_TYPE_MIXER: /* report the Mixer Units */
        {
            switch (caps->sub_type) {
                case AUDIO_MIXER_QUERY:
                    caps->udata.mask = AUDIO_MIXER_VOLUME | AUDIO_MIXER_LINE;
                    break;
                case AUDIO_MIXER_VOLUME:
                    caps->udata.value = st_audio->replay_volume;
                    break;
                case AUDIO_MIXER_LINE:
                    break;
                default:
                    result = -RT_ERROR;
                    break;
            }

            break;
        }

        default:
            result = -RT_ERROR;
            break;
    }

    return result;
}

static rt_err_t stm32_player_configure(struct rt_audio_device *audio, struct rt_audio_caps *caps) {
    rt_err_t result = RT_EOK;
    struct stm32_audio *st_audio = (struct stm32_audio *) audio->parent.user_data;
    LOG_D("%s:main_type: %d, sub_type: %d", __FUNCTION__, caps->main_type, caps->sub_type);
    switch (caps->main_type) {
        case AUDIO_TYPE_MIXER: {
            switch (caps->sub_type) {
                case AUDIO_MIXER_MUTE: {
                    break;
                }
                case AUDIO_MIXER_VOLUME: {
                    int volume = caps->udata.value;
                    st_audio->replay_volume = volume;
                    /* set mixer volume */
                    wm8978_HPvol_Set(volume, volume);
                    break;
                }
                default:
                    result = -RT_ERROR;
                    break;
            }

            break;
        }
        case AUDIO_TYPE_OUTPUT: {
            switch (caps->sub_type) {
                case AUDIO_DSP_PARAM: {
                    struct rt_audio_configure config = caps->udata.config;
                    st_audio->replay_config.samplerate = config.samplerate;
                    st_audio->replay_config.samplebits = config.samplebits;
                    st_audio->replay_config.channels = config.channels;
                    audio_config_set(config);
                    break;
                }

                case AUDIO_DSP_SAMPLERATE: {
                    st_audio->replay_config.samplerate = caps->udata.config.samplerate; /*设置采样率*/
                    audio_samplerate_set(caps->udata.config.samplerate);
                    break;
                }

                case AUDIO_DSP_CHANNELS: {
                    st_audio->replay_config.channels = caps->udata.config.channels;
                    audio_channel_set(caps->udata.config.channels);
                    break;
                }

                case AUDIO_DSP_SAMPLEBITS: {
                    st_audio->replay_config.samplebits = caps->udata.config.samplebits;
                    audio_samplebits_set(caps->udata.config.samplebits);
                    break;
                }

                default:
                    result = -RT_ERROR;
                    break;
            }
            break;
        }
        default:
            break;
    }

    return result;
}

static rt_err_t stm32_player_init(struct rt_audio_device *audio) {
    /* initialize wm8988 */
    audio_init();
    wm8978_init(CODEC_I2C_NAME, WM8978_ADDR);
    wm8978_cnf_init();
    wm8978_HPvol_Set(30, 30);              //耳机音量设置
    wm8978_SPKvol_Set(50);                     //喇叭音量设置
    wm8978_ADDA_Cfg(1, 0);             //开启DAC
    wm8978_Input_Cfg(0, 0, 0);//关闭输入通道
    wm8978_Output_Cfg(1, 0);         //开启DAC输出
    return RT_EOK;
}

static rt_err_t stm32_player_start(struct rt_audio_device *audio, int stream) {
    if (stream == AUDIO_STREAM_REPLAY) {
        __HAL_DMA_ENABLE(&_dma_tx);
    }
    return RT_EOK;
}

static rt_err_t stm32_player_stop(struct rt_audio_device *audio, int stream) {
    if (stream == AUDIO_STREAM_REPLAY) {
        __HAL_DMA_DISABLE(&_dma_tx);
        /* Clear all flags */
        __HAL_DMA_CLEAR_FLAG (&_dma_tx, __HAL_DMA_GET_TC_FLAG_INDEX(&_dma_tx));
        __HAL_DMA_CLEAR_FLAG (&_dma_tx, __HAL_DMA_GET_HT_FLAG_INDEX(&_dma_tx));
        __HAL_DMA_CLEAR_FLAG (&_dma_tx, __HAL_DMA_GET_TE_FLAG_INDEX(&_dma_tx));
        __HAL_DMA_CLEAR_FLAG (&_dma_tx, __HAL_DMA_GET_DME_FLAG_INDEX(&_dma_tx));
        __HAL_DMA_CLEAR_FLAG (&_dma_tx, __HAL_DMA_GET_FE_FLAG_INDEX(&_dma_tx));
    }
    return RT_EOK;
}

rt_ssize_t stm32_player_transmit(struct rt_audio_device *audio, const void *writeBuf, void *readBuf, rt_size_t size) {

    return (rt_ssize_t) size;
}

static void stm32_player_buffer_info(struct rt_audio_device *audio, struct rt_audio_buf_info *info) {
    /**
     *               TX_FIFO
     * +----------------+----------------+
     * |     block1     |     block2     |
     * +----------------+----------------+
     *  \  block_size  / \  block_size  /
     */
    info->buffer = _stm32_audio_play.tx_fifo;
    info->total_size = I2S_TX_DMA_FIFO_SIZE * 2;
    info->block_size = I2S_TX_DMA_FIFO_SIZE;
    info->block_count = 2;
}

static struct rt_audio_ops _p_audio_ops =
        {
                .getcaps     = stm32_player_getcaps,
                .configure   = stm32_player_configure,
                .init        = stm32_player_init,
                .start       = stm32_player_start,
                .stop        = stm32_player_stop,
                .transmit    = stm32_player_transmit,
                .buffer_info = stm32_player_buffer_info,
        };

int rt_hw_audio_init(void) {
    rt_uint8_t *tx_fifo;
    tx_fifo = rt_malloc(I2S_TX_DMA_FIFO_SIZE * 2);
    if (tx_fifo == RT_NULL) {
        return -RT_ENOMEM;
    }
    rt_memset(tx_fifo, 0, I2S_TX_DMA_FIFO_SIZE);
    _stm32_audio_play.tx_fifo = tx_fifo;
    _stm32_audio_play.audio.ops = &_p_audio_ops;
    rt_audio_register(&_stm32_audio_play.audio, I2S_SOUND_NAME, RT_DEVICE_FLAG_WRONLY, &_stm32_audio_play);
    return RT_EOK;
}

INIT_DEVICE_EXPORT(rt_hw_audio_init);
void DMA1_Stream4_IRQHandler(void) {
    rt_interrupt_enter();
    if (__HAL_DMA_GET_FLAG(&_dma_tx, DMA_FLAG_TCIF0_4) != RESET) //DMA传输完成
    {
        __HAL_DMA_CLEAR_FLAG(&_dma_tx, DMA_FLAG_TCIF0_4); //清除DMA传输完成中断标志位
        rt_audio_tx_complete(&_stm32_audio_play.audio); /*通知发送下一帧数据*/
    }
    rt_interrupt_leave();
}


IO口初始化

//I2S底层驱动,时钟使能,引脚配置
//此函数会被HAL_I2S_Init()调用
//hi2s:I2S句柄
void HAL_I2S_MspInit(I2S_HandleTypeDef *hi2s) {
    if (hi2s->Instance == SPI2) {
        GPIO_InitTypeDef GPIO_InitStruct;
        /* Peripheral clock enable */
        __HAL_RCC_SPI2_CLK_ENABLE();
        __HAL_RCC_GPIOC_CLK_ENABLE();
        __HAL_RCC_GPIOB_CLK_ENABLE();
        /**I2S2 GPIO Configuration
        PC2     ------> I2S2_ext_SD
        PC3     ------> I2S2_SD
        PB12     ------> I2S2_WS
        PB13     ------> I2S2_CK
        PC6     ------> I2S2_MCK
        */
        GPIO_InitStruct.Pin = GPIO_PIN_2;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
        GPIO_InitStruct.Alternate = GPIO_AF6_I2S2ext;
        HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
        GPIO_InitStruct.Pin = GPIO_PIN_3|GPIO_PIN_6;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
        GPIO_InitStruct.Alternate = GPIO_AF5_SPI2;
        HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
        GPIO_InitStruct.Pin = GPIO_PIN_12|GPIO_PIN_13;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
        GPIO_InitStruct.Alternate = GPIO_AF5_SPI2;
        HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
    }
}

I2S测试

测试文件(参考官方)

#include <rtthread.h>
#include <rtdevice.h>
#include <dfs_posix.h>

#define BUFSZ   1024
#define SOUND_DEVICE_NAME    "sound0"    /* Audio 设备名称 */
static rt_device_t snd_dev;              /* Audio 设备句柄 */

struct RIFF_HEADER_DEF
{
    char riff_id[4];     // 'R','I','F','F'
    uint32_t riff_size;
    char riff_format[4]; // 'W','A','V','E'
};

struct WAVE_FORMAT_DEF
{
    uint16_t FormatTag;
    uint16_t Channels;
    uint32_t SamplesPerSec;
    uint32_t AvgBytesPerSec;
    uint16_t BlockAlign;
    uint16_t BitsPerSample;
};

struct FMT_BLOCK_DEF
{
    char fmt_id[4];    // 'f','m','t',' '
    uint32_t fmt_size;
    struct WAVE_FORMAT_DEF wav_format;
};

struct DATA_BLOCK_DEF
{
    char data_id[4];     // 'R','I','F','F'
    uint32_t data_size;
};

struct wav_info
{
    struct RIFF_HEADER_DEF header;
    struct FMT_BLOCK_DEF   fmt_block;
    struct DATA_BLOCK_DEF  data_block;
};

int wavplay_sample(int argc, char **argv)
{
    int fd = -1;
    uint8_t *buffer = NULL;
    struct wav_info *info = NULL;
    struct rt_audio_caps caps = {0};
    if (argc != 2)
    {
        rt_kprintf("Usage:\n");
        rt_kprintf("wavplay_sample song.wav\n");
        return 0;
    }
    fd = open(argv[1], O_RDONLY);
    if (fd < 0)
    {
        rt_kprintf("open file failed!\n");
        goto __exit;
    }
    buffer = rt_malloc(BUFSZ);
    if (buffer == RT_NULL)
        goto __exit;
    info = (struct wav_info *) rt_malloc(sizeof * info);
    if (info == RT_NULL)
        goto __exit;
    if (read(fd, &(info->header), sizeof(struct RIFF_HEADER_DEF)) <= 0)
        goto __exit;
    if (read(fd, &(info->fmt_block),  sizeof(struct FMT_BLOCK_DEF)) <= 0)
        goto __exit;
    if (read(fd, &(info->data_block), sizeof(struct DATA_BLOCK_DEF)) <= 0)
        goto __exit;
    rt_kprintf("wav information:\n");
    rt_kprintf("samplerate %d\n", info->fmt_block.wav_format.SamplesPerSec);
    rt_kprintf("channel %d\n", info->fmt_block.wav_format.Channels);
    /* 根据设备名称查找 Audio 设备,获取设备句柄 */
    snd_dev = rt_device_find(SOUND_DEVICE_NAME);
    /* 以只写方式打开 Audio 播放设备 */
    rt_device_open(snd_dev, RT_DEVICE_OFLAG_WRONLY);
    /* 设置采样率、通道、采样位数等音频参数信息 */
    caps.main_type               = AUDIO_TYPE_OUTPUT;                           /* 输出类型(播放设备 )*/
    caps.sub_type                = AUDIO_DSP_PARAM;                             /* 设置所有音频参数信息 */
    caps.udata.config.samplerate = info->fmt_block.wav_format.SamplesPerSec;    /* 采样率 */
    caps.udata.config.channels   = info->fmt_block.wav_format.Channels;         /* 采样通道 */
    caps.udata.config.samplebits = 16;                                          /* 采样位数 */
    rt_device_control(snd_dev, AUDIO_CTL_CONFIGURE, &caps);

    while (1)
    {
        int length;

        /* 从文件系统读取 wav 文件的音频数据 */
        length = read(fd, buffer, BUFSZ);

        if (length <= 0)
            break;

        /* 向 Audio 设备写入音频数据 */
        rt_device_write(snd_dev, 0, buffer, length);
    }

    /* 关闭 Audio 设备 */
    rt_device_close(snd_dev);

    __exit:

    if (fd >= 0)
        close(fd);

    if (buffer)
        rt_free(buffer);

    if (info)
        rt_free(info);

    return 0;
}

//MSH_CMD_EXPORT(wavplay_sample,  play wav file);

int main() {
    rt_thread_mdelay(1000); // 防止sd卡还未自动挂载
    char *par[2] = {
            "wavplay_sample",
            "/music/whans-maidt.wav"
    };
    wavplay_sample(2, par);
    while (1) {

        rt_thread_delay(1000);
    }
}

结尾

  1. 在stm32f407zgt6板子上测试没有问题
  • 37
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

詩不诉卿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值