当前位置: 首页 > article >正文

APM32F411使用IIS外设驱动es8388实现自录自播

前言:

        从零开始学习I2s外设,配置Es8288寄存器实现录音播放。本文章使用主控芯片是APM32F411系类。音频相关的概念比较多,就不再次做过多的介绍,本文章只是简单实现边录边播功能。APM系类兼容st的芯片,所以用st的hal库来实现。本文的介绍顺序从es8388这款芯片开始,再到I2s协议,再到最后代码讲解。

一、Es8388芯片介绍

1、Es8388介绍

       Es8388有 2 ADC 通道和 2 DAC 通道,麦克风放大器,耳机放大器,数字音效以及模拟混合和增益功能组成。

        主要特点:

                I2S 接口,支持最高 192K,24bit 音频播放。

                DAC 信噪比 96dBADC 信噪比 95dB。

                支持主机和从机模式。

                支持立体声差分输入/麦克风输入。

                支持左右声道音量独立调节。

                支持 40mW 耳机输出,无爆音。

2、通路

设计通入从es8388的RIN1 进行数据采集编码,解码数据从ROUT1 发送至喇叭。主时钟又外部12.288M的晶振电路提供。

3、采样率相关寄存器

        本文设置的采样率是8K,作为正常通话,8K的采样已足够清晰。依据主时钟与采样的关系后续如做更改需要依照下表同步更新。

4、寄存器介绍

        寄存器介绍采用正点原子提供的手册做参考,并对其他可能影响的进行另外介绍。

        更新补充其他可能影响录音功能的寄存器:(以下寄存器只针对实现我上述所说的单右声道录音右声道播放)

4.1问题介绍

首先说一下我遇到的问题,我通过移植正点原子的历程实现边录边播功能,在探索者上完全没有问题,可以实现,但是在自己打的板子上却存在在录音上电时发出        “哒哒哒哒哒哒”        的声音,其中有一个难以理解的点时,因为寄存器高通滤波开启了导致的,依据笔者的理解,高通滤波时需要开的,但是他会带来“哒哒哒”的噪音不太理解,找了好久,一个一个尝试才解决的,但是至今不太理解,可能是硬件设计问题,硬件抄的成熟的方案。

默认开启,设置关闭就解决“哒哒哒”的声音

第二个问题就是在说话的时候也会伴随的杂音“哒哒哒”的杂音声音比较大后续通过一下寄存器减小了杂音,之前上电就出现,后者是持续存在,至今没完全找到原因,如果有知道的可以在评论区交流学习。

说一下以下寄存器:

左DAC清零静音,关闭无关寄存器,防止干扰,因为采用的是单声道,所以无关的寄存器需要全部关闭,禁止立体声也需要设置,这里不做赘述,相信各位英语应该都可以,直接拿出相关截图给各位。

麦克风相关寄存器,硬件原理图设置那边,其他的就需要断电,也符合他的特点,“低功耗”

总结:

        Es8388 中的ALC调节,MIC增益调节,喇叭声音等一些列寄存器设置都会影响到声音播放时的杂音和啸叫,提醒各位调试时可以先去掉喇叭,通过示波器区测试输入输出声波,保护耳朵要经。

二、I2S介绍

1、I2S简介

做一下另外补充:

(1)WS:字选择,映射到spi的nss引脚,及帧时钟用于切换左右声道数据,对应设置的采样率,可以通过示波器查看是否配置正确。

(2)SD:串行数据,映射到mosi引脚,用于发送或接收两个时分复用的数据通道上的 数据。

(3)CK :串行时钟(映射到 SCK 引脚),即位时钟,是主模式下的串行时钟输出以及从模 式下的串行时钟输入。CK 频率 =WS 频率( fs *2*16 16 位宽),如果是 32 位宽,则是: CK 频率=WS 频率( fs *2*32 32 位宽)

主时钟的256=8*16*2

(4)I2S2ext_SD 和 I2S3ext_SD:用于控制 I2S 全双工模式的附加引脚(映射到 MISO 引脚)。

(5)MCK :即主时钟输出,当 I2S 配置为主模式(并且 SPI_I2SPR 寄存器中的 MCKOE
1 )时,使用此时钟,该时钟输出频率 256 × fs fs 即音频信号采样频率( fs )。

提示:大家可以将MCK引脚接入MCU,通过MCU提供主时钟,可能会有一点点小误差,如果想要省下材料,可以直接查询手册

APMF411的I2S2_MCK引脚为PA6.大家可以作为参考,不过追求准确的话可以直接将MCK引脚连接至晶振电路,后续通过寄存器去合理设计。本文采用的是外部12.288M晶振解决的。

I2S的时钟发生器如下介绍,摘抄自正点原子手册,作为学习记录

2、I2s 相关寄存器介绍:
2.1 RCC_PLLI2SCFGR

2.2        SPI_I2SCFGR

2.3 SPI_I2SPR

三、Es8388配置,I2S代码讲解

1、Es8388初始化
/*****************************************************************************
 函 数 名  : Es8388Init
 功能描述  : Es8388初始化
 输入参数  : void
 返 回 值  : void
 作  者    : GCC
*****************************************************************************/
void Es8388Init(void)
{
	AudioI2cInit();					/* 初始化IIC接口 */	

	es8388I2cWriteReg(0, 0x80);     /* 软复位ES8388 */
	es8388I2cWriteReg(0, 0x00);
	DelayMs(100);                  /* 等待复位 */

    es8388I2cWriteReg(0x01, 0x58);	/*默认0x5c,设置0x58复位模拟软件部分*/
    es8388I2cWriteReg(0x01, 0x50);	/*PdnAna 为0,模拟部分可工作,*/
	
//	es8388I2cWriteReg(0x01, 0x53);//模拟部分不工作
	
    es8388I2cWriteReg(0x02, 0xF3);	/*断电ADC,DAC*/
    es8388I2cWriteReg(0x02, 0xF0);	/*0xf3 重新设置adc,dac,重新设置状态机复位,开启ADC DAC*/
	
//	es8388I2cWriteReg(0x03, 0x00);//ADCL power down,left analog input power down
    es8388I2cWriteReg(0x03, 0x09);   /* 麦克风偏置电源关闭 */
    es8388I2cWriteReg(0x00, 0x06);   /* 使能参考 500K驱动使能,默认500K */
    es8388I2cWriteReg(0x04, 0x00);   /* DAC电源管理,不打开任何通道 */
	
	es8388I2cWriteReg(0x05,0x88);	//LPDACL low powrer, LPLOUT2 low power
//	es8388I2cWriteReg(0x04, 0x90); 
//	es8388I2cWriteReg(0x0A,0xF0);
	
	es8388I2cWriteReg(0x08, 0x05);   /* MCLK 6分频产生2.048M时钟 */
    es8388I2cWriteReg(0x2B, 0x80);   /* DAC控制	DACLRC与ADCLRC相同 */

	 es8388I2cWriteReg(0x09, 0x00);   // 0x66 ADC L/R PGA增益配置为+24dB
    //es8388I2cWriteReg(0x09, 0x88);   /* ADC L/R PGA增益配置为+24dB */
//	es8388I2cWriteReg(0x0C, 0x8C); 	//都来源源于右通道
	es8388I2cWriteReg(0x0C, 0x0C);  //左右声道数据分开
	
	es8388I2cWriteReg(0x0D, 0x0A);   /* ADC配置 MCLK/采样率=1536,8K采样率 外部晶振12.288M*/
//    es8388I2cWriteReg(0x0D, 0x02);   /* ADC配置 MCLK/采样率=256 */
	es8388I2cWriteReg(0x10, 0x00);   /* ADC数字音量控制将信号衰减 L  设置为最小!!! */
    es8388I2cWriteReg(0x11, 0x00);   /* ADC数字音量控制将信号衰减 R  设置为最小!!! */
//	es8388I2cWriteReg(0x13,0xD5);	//lxlvl -1.5dB 1.36s
	
	es8388I2cWriteReg(0x16,0xF3);//Noise gate threshold -30dB, PGA gain held constant Noise gate function enable 

    es8388I2cWriteReg(0x17, 0x18);   /* DAC 音频数据为16bit */
    es8388I2cWriteReg(0x18, 0x0A);   /* DAC 配置 MCLK/采样率=256 */ 
    es8388I2cWriteReg(0x1A, 0x00);   /* DAC数字音量控制将信号衰减 L  设置为最小!!! */
    es8388I2cWriteReg(0x1B, 0x00);   /* DAC数字音量控制将信号衰减 R  设置为最小!!! */
//	es8388I2cWriteReg(0x1C,0xC0);//除重音模式
    es8388I2cWriteReg(0x27, 0xB8);   /* L混频器 */
	es8388I2cWriteReg(0x2A, 0xB8);   /* R混频器,-15 */

	es8388I2cWriteReg(0x0A,0);//选择输入通道

	es8388I2cWriteReg(0x0B,0x10);//
	
	es8388I2cWriteReg(0x0E,0x00);    /*关闭高通滤波防止哒哒哒声(如果存在可选择)*/
	es8388I2cWriteReg(0x03, 0xA9);   /* 麦克风偏置电源关闭 */
    es8388I2cWriteReg(0x06,0x02);
	es8388I2cWriteReg(0x04,0x90);
   DelayMs(100);
}
/*****************************************************************************
 函 数 名  : Es8388I2sCfg
 功能描述  : 设置ES8388工作模式
 输入参数  : fmt : 工作模式
				*    @arg      0, 飞利浦标准I2S;
				*    @arg      1, MSB(左对齐);
				*    @arg      2, LSB(右对齐);
				*    @arg      3, PCM/DSP
			 len : 数据长度	
				*    @arg      0, 24bit
				*    @arg      1, 20bit 
				*    @arg      2, 18bit 
				*    @arg      3, 16bit 
				*    @arg      4, 32bit 
 返 回 值  : void
 作  者    : GCC
*****************************************************************************/
void Es8388I2sCfg(uint8_t fmt, uint8_t len)
{
    fmt &= 0x03;
    len &= 0x07;    /* 限定范围 */
    es8388I2cWriteReg(23, (fmt << 1) | (len << 3));  /* R23,ES8388工作模式设置 */
}
/*****************************************************************************
 函 数 名  : Es8388Rout1Set
 功能描述  : 右声道声音输出大小设置
 输入参数  : voluem : 音量大小(0 ~ 33)
 返 回 值  : void
 作  者    : GCC
*****************************************************************************/
void Es8388Rout1Set(uint8_t volume)
{
    if (volume > 33)
    {
        volume = 33;
    }
    es8388I2cWriteReg(0x2F, volume);//ROUT1VOl右声道1
}
/*****************************************************************************
 函 数 名  : Es8388Rout2Set
 功能描述  : MIC_P 声音设置
 输入参数  : voluem : 音量大小(0 ~ 33)
 返 回 值  : void
 作  者    : GCC
*****************************************************************************/
void Es8388Rout2Set(uint8_t volume)
{
    if (volume > 33)
    {
        volume = 33;
    }
    es8388I2cWriteReg(0x31, volume);
}
/**
 * @brief       设置3D环绕声
 * @param       
				修改设置单声道
 * @retval      无
 */
/*****************************************************************************
 函 数 名  : Es8388Channel_3d_set
 功能描述  : 通道设置,3d立体音设置
 输入参数  : Channel 0 表示立体音 1表示单左声道,2表示设置右声道
			 depth : 0 ~ 7(3D强度,0关闭,7最强)
 返 回 值  : void
 作  者    : GCC
*****************************************************************************/
void Es8388Channel_3d_set(uint8_t depth,u8 Channel)
{ 
	switch(Channel)
	{
		case 0:
			depth &= 0x7;       /* 限定范围 */
			es8388I2cWriteReg(0x1D, depth << 2);    /* R7,3D环绕设置 */
		break;
		case 1:
			depth=(3<<5);
			es8388I2cWriteReg(0x1D,depth);
			break;
		case 2:
			depth=0xA0;//单声道关闭立体音
			es8388I2cWriteReg(0x1D,0xA0);
			break;
	}
}

/**
 * @brief       ES8388 DAC/ADC配置
 * @param       dacen : dac使能(1)/关闭(0)
 * @param       adcen : adc使能(1)/关闭(0)
 * @retval      无
 */
void Es8388AddaCfg(uint8_t dacen, uint8_t adcen)
{
    uint8_t tempreg = 0;
    
    tempreg |= ((!dacen) << 0);
    tempreg |= ((!adcen) << 1);
    tempreg |= ((!dacen) << 2);
    tempreg |= ((!adcen) << 3);
    es8388I2cWriteReg(0x02, tempreg);
}
/**
 * @brief       ES8388 DAC输出通道配置
 * @param       o1en : 通道1使能(1)/禁止(0)
 * @param       o2en : 通道2使能(1)/禁止(0)
 * @retval      无
 */
void Es8388OutputCfg(uint8_t o1en, uint8_t o2en)
{
    uint8_t tempreg = 0;
    tempreg |= o1en * (3 << 4);
    tempreg |= o2en * (3 << 2);
    es8388I2cWriteReg(0x04, tempreg);
}
/**
 * @brief       ES8388 MIC左右通道增益设置(MIC PGA增益)
 * @param       gain : 0~8, 对应0~24dB  3dB/Step
 * @retval      无
 */
void Es8388MicGain(uint8_t gain)
{
    gain &= 0x0F;	  //设置低4位
    gain |= gain << 4;//设置高位4位
    es8388I2cWriteReg(0x09, gain);       /* R9,左右通道PGA增益设置 */
}
/**
 * @brief       ES8388 ALC设置
 * @param       sel
 *   @arg       0,关闭ALC
 *   @arg       1,右通道ALC
 *   @arg       2,左通道ALC
 *   @arg       3,立体声ALC
 * @param       maxgain : 0~7,对应-6.5~+35.5dB
 * @param       mingain : 0~7,对应-12~+30dB 6dB/STEP
 * @retval      无
 */
void Es8388AlcCtrl(uint8_t sel, uint8_t maxgain, uint8_t mingain)
{
    uint8_t tempreg = 0;
    
    tempreg = sel << 6;
    tempreg |= (maxgain & 0x07) << 3;//set maximum gain of PGA	17.5db
    tempreg |= mingain & 0x07;		 //12db
    es8388I2cWriteReg(0x12, tempreg);     /* R18,ALC设置 */
}
/**
 * @brief       ES8388 ADC输出通道配置
 * @param       in : 输入通道
 *    @arg      0, 通道1输入
 *    @arg      1, 通道2输入
 * @retval      无
 */
void Es8388InputCfg(uint8_t in)
{
	es8388I2cWriteReg(0x0A, (5 * in) << 4);   /* ADC1 输入通道选择L/R  INPUT1 */
}
2、I2S 代码

/***************************************************************************************/
I2S_HandleTypeDef gAudioI2s;        /* I2S句柄 */
I2S_HandleTypeDef gAudioI2sSext;     /* I2S句柄 */
DMA_HandleTypeDef gI2sTxDma;  /* I2S发送DMA句柄 */
DMA_HandleTypeDef gI2sRxDma;  /* I2S接收DMA句柄 */
/************************************************************************************/
/**
 * @brief       I2S初始化
 * @param       i2s_standard        : I2S标准
 *   @note      可以设置            : I2S_STANDARD_PHILIPS/I2S_STANDARD_MSB/
 *                                    I2S_STANDARD_LSB/I2S_STANDARD_PCM_SHORT/I2S_STANDARD_PCM_LONG
 * @param       i2s_mode            : I2S工作模式
 *   @note      可以设置            : I2S_MODE_SLAVE_TX/I2S_MODE_SLAVE_RX/I2S_MODE_MASTER_TX/I2S_MODE_MASTER_RX
 * @param       i2s_clock_polarity  : 空闲状态时钟电平
 * @param       i2s_dataformat      : 数据长度
 *   @note      可以设置            : I2S_DATAFORMAT_16B/I2S_DATAFORMAT_16B_EXTENDED/I2S_DATAFORMAT_24B/I2S_DATAFORMAT_32B
 * @retval      无
 */
void AudioI2sInit(void)
{
    gAudioI2s.Instance = I2S_SPI;
    gAudioI2s.Init.Mode = I2S_MODE_MASTER_TX;           /* IIS模式 */
    gAudioI2s.Init.Standard = I2S_STANDARD_PHILIPS;     /* IIS标准 */
    gAudioI2s.Init.DataFormat = I2S_DATAFORMAT_16B;      /* IIS数据长度 */
    gAudioI2s.Init.MCLKOutput = I2S_MCLKOUTPUT_ENABLE;  /* 主时钟输出使能 */
    gAudioI2s.Init.AudioFreq = I2S_AUDIOFREQ_8K;   /* IIS频率设置 */
    gAudioI2s.Init.CPOL = I2S_CPOL_LOW;           /* 空闲状态时钟电平 */
    gAudioI2s.Init.ClockSource = I2S_CLOCK_PLL;         /* IIS时钟源为PLL */
    HAL_I2S_Init(&gAudioI2s);
    I2S_SPI->CR2 |= 1<<1;               /* SPI2/I2S2 TX DMA请求使能. */
    __HAL_I2S_ENABLE(&gAudioI2s);    /* 使能I2S2 */
}
/**
 * @brief       I2SEXT初始化
 * @param       i2s_standard        : I2S标准
 *   @note      可以设置            : I2S_STANDARD_PHILIPS/I2S_STANDARD_MSB/
 *                                    I2S_STANDARD_LSB/I2S_STANDARD_PCM_SHORT/I2S_STANDARD_PCM_LONG
 * @param       i2s_mode            : I2S工作模式
 *   @note      可以设置            : I2S_MODE_SLAVE_TX/I2S_MODE_SLAVE_RX/I2S_MODE_MASTER_TX/I2S_MODE_MASTER_RX
 * @param       i2s_clock_polarity  : 显示数字的位数
 * @param       i2s_dataformat      : 数据长度
 *   @note      可以设置            : I2S_DATAFORMAT_16B/I2S_DATAFORMAT_16B_EXTENDED/I2S_DATAFORMAT_24B/I2S_DATAFORMAT_32B
 * @retval      无
 */
void AudioI2sSextInit(void)
{
    gAudioI2sSext.Instance = I2SEXT_SPI;
    gAudioI2sSext.Init.Mode = I2S_MODE_SLAVE_RX;             /* IIS模式 */
    gAudioI2sSext.Init.Standard = I2S_STANDARD_PHILIPS;           /* IIS标准 */
    gAudioI2sSext.Init.DataFormat = I2S_DATAFORMAT_16B;       /* IIS数据长度 */
    gAudioI2sSext.Init.MCLKOutput = I2S_MCLKOUTPUT_DISABLE;   /* 主时钟输出使能 */
    gAudioI2sSext.Init.AudioFreq = I2S_AUDIOFREQ_8K;  		  /* IIS频率设置 */
    gAudioI2sSext.Init.CPOL = I2S_CPOL_LOW;         /* 空闲状态时钟电平 */
    gAudioI2sSext.Init.ClockSource = I2S_CLOCK_PLL;          /* IIS时钟源为PLL */
    HAL_I2S_Init(&gAudioI2sSext); 

    I2SEXT_SPI->CR2 |= 1<<0;                /* SPI2/I2S2 TX DMA请求使能. */
    __HAL_I2S_ENABLE(&gAudioI2sSext);     /* 使能I2S2 */
}

/**
 * @brief       I2S底层驱动,时钟使能,引脚配置,DMA配置
 * @note        此函数会被HAL_I2S_Init()调用
 * @param       hi2s:I2S句柄
 * @retval      无
 */
void HAL_I2S_MspInit(I2S_HandleTypeDef *hi2s)
{
	GPIO_InitTypeDef gpio_init_struct;

    I2S_SPI_CLK_ENABLE();           /* 使能SPI2/I2S2时钟 */
    I2S_WS_GPIO_CLK_ENABLE();       /* 使能I2S_WS时钟,左右声道 */
    I2S_SCLK_GPIO_CLK_ENABLE();     /* 使能I2S_SCLK时钟 */
    I2S_SDOUT_GPIO_CLK_ENABLE();    /* 使能I2S_SDOUT时钟 */
    I2S_SDIN_GPIO_CLK_ENABLE();     /* 使能I2S_SDIN时钟 */
//	I2S_MCLK_GPIO_CLK_ENABLE();     /* 使能I2S_MCLK时钟,当MCLK有内部提供 */
	
    gpio_init_struct.Pin = I2S_WS_GPIO_PIN;
    gpio_init_struct.Mode = GPIO_MODE_AF_PP;        /* 推挽复用 */
    gpio_init_struct.Pull = GPIO_PULLUP;            /* 上拉 */
    gpio_init_struct.Speed = GPIO_SPEED_HIGH;       /* 高速 */
    gpio_init_struct.Alternate = GPIO_AF_I2S_SPI;   /* 复用为SPI/I2S */
    
    HAL_GPIO_Init(I2S_WS_GPIO_PORT, &gpio_init_struct);   /* 初始化I2S_LRCK引脚 */

    gpio_init_struct.Pin = I2S_SCLK_GPIO_PIN;
    HAL_GPIO_Init(I2S_SCLK_GPIO_PORT, &gpio_init_struct);   /* 初始化I2S_SCLK引脚 */
   
//	gpio_init_struct.Pin = I2S_MCLK_GPIO_PIN;
//    HAL_GPIO_Init(I2S_MCLK_GPIO_PORT, &gpio_init_struct);   /* 初始化I2S_MCLK引脚 */
 
    gpio_init_struct.Pin = I2S_SDIN_GPIO_PIN;
    HAL_GPIO_Init(I2S_SDIN_GPIO_PORT, &gpio_init_struct);   /* 初始化I2S_SDIN引脚 */
    
    gpio_init_struct.Pin = I2S_SDOUT_GPIO_PIN;
    HAL_GPIO_Init(I2S_SDOUT_GPIO_PORT, &gpio_init_struct);  /* 初始化I2S_SDOUT引脚 */

    gpio_init_struct.Pin = I2S_SDOUT_GPIO_PIN;
    gpio_init_struct.Alternate = GPIO_AF_I2S_EXT_SPI;       /* 复用为I2Sext */
    HAL_GPIO_Init(I2S_SDOUT_GPIO_PORT, &gpio_init_struct);  /* 初始化I2S_SDOUT引脚 */

	  RCC->CR |= 1 << 26;                 /* 开启I2S时钟 */
    while((RCC->CR & 1 << 27) == 0);    /* 等待I2S时钟开启成功. */
}

/**
 * @brief       I2S TX DMA配置
 *  @note       设置为双缓冲模式,并开启DMA传输完成中断
 * @param       buf0 : M0AR地址.
 * @param       buf1 : M1AR地址.
 * @param       num  : 每次传输数据量
 * @retval      无
 */
void I2sTxDmaInit(uint8_t* buf0, uint16_t num)
{  
    I2S_TX_DMA_CLK_ENABLE();                                               /* 使能I2S TX DMA时钟 */
    __HAL_LINKDMA(&gAudioI2s, hdmatx, gI2sTxDma);              /* 将DMA与I2S联系起来 */

    gI2sTxDma.Instance = I2S_TX_DMASx;                            /* 设置I2S TX DMA数据流 */
    gI2sTxDma.Init.Channel = I2S_TX_DMASx_Channel;                /* 设置I2S TX DMA通道 */
    gI2sTxDma.Init.Direction = DMA_MEMORY_TO_PERIPH;              /* 存储器到外设模式 */
    gI2sTxDma.Init.PeriphInc = DMA_PINC_DISABLE;                  /* 外设非增量模式 */
    gI2sTxDma.Init.MemInc = DMA_MINC_ENABLE;                      /* 存储器增量模式 */
    gI2sTxDma.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD; /* 外设数据长度:16位 */
    gI2sTxDma.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;    /* 存储器数据长度:16位 */
	gI2sTxDma.Init.Mode = DMA_CIRCULAR;                           /* 使用循环模式 */
    gI2sTxDma.Init.Priority = DMA_PRIORITY_MEDIUM;                  /* 高优先级 */
    gI2sTxDma.Init.FIFOMode = DMA_FIFOMODE_DISABLE;               /* 不使用FIFO */
    gI2sTxDma.Init.MemBurst = DMA_MBURST_SINGLE;                  /* 存储器单次突发传输 */
    gI2sTxDma.Init.PeriphBurst = DMA_PBURST_SINGLE;               /* 外设突发单次传输 */
    HAL_DMA_DeInit(&gI2sTxDma);                                   /* 先清除以前的设置 */
    HAL_DMA_Init(&gI2sTxDma);                                     /* 初始化DMA */

	HAL_DMA_Start(&gI2sTxDma,(uint32_t)buf0,(uint32_t)&I2S_SPI->DR,num);//单缓存                                                /* 10us延时,防止-O2优化出问题 */
    DelayUs(10);   
	__HAL_DMA_ENABLE_IT(&gI2sTxDma, DMA_IT_TC);                   /* 开启传输完成中断 */
    __HAL_DMA_CLEAR_FLAG(&gI2sTxDma, I2S_TX_DMASx_FLAG);          /* 清除DMA传输完成中断标志位 */
	
	HAL_NVIC_SetPriority(I2S_TX_DMASx_IRQn,I2sTxPrePriority,I2sTxSubPriority);                /* DMA中断优先级 */
    HAL_NVIC_EnableIRQ(I2S_TX_DMASx_IRQn);
    __HAL_DMA_DISABLE(&gI2sTxDma);                                /* 先关闭DMA */
}
/**
 * @brief       I2Sext RX DMA配置
 *  @note       设置为双缓冲模式,并开启DMA传输完成中断
 * @param       buf0 : M0AR地址.
 * @param       buf1 : M1AR地址.
 * @param       num  : 每次传输数据量
 * @retval      无
 */
void I2sSextRxDmaInit(uint8_t* buf0, uint32_t num)	
{  
    I2SEXT_RX_DMA_CLK_ENABLE();                                            /* 使能I2S RX DMA时钟 */
    __HAL_LINKDMA(&gAudioI2sSext, hdmarx, gI2sRxDma);           /* 将DMA与I2S联系起来 */
	
    gI2sRxDma.Instance = I2SEXT_RX_DMASx;                         /* 设置I2S RX DMA数据流 */
    gI2sRxDma.Init.Channel = I2SEXT_RX_DMASx_Channel;             /* 设置I2S RX DMA通道 */
    gI2sRxDma.Init.Direction = DMA_PERIPH_TO_MEMORY;              /* 外设到存储器模式 */
    gI2sRxDma.Init.PeriphInc = DMA_PINC_DISABLE;                  /* 外设非增量模式 */
    gI2sRxDma.Init.MemInc = DMA_MINC_ENABLE;                      /* 存储器增量模式 */
    gI2sRxDma.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD; /* 外设数据长度:16位 */
    gI2sRxDma.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;    /* 存储器数据长度:16位 */
    gI2sRxDma.Init.Mode = DMA_CIRCULAR;                           /* 使用循环模式 */
    gI2sRxDma.Init.Priority = DMA_PRIORITY_HIGH;     	           /* 中等优先级 */
    gI2sRxDma.Init.FIFOMode = DMA_FIFOMODE_DISABLE;               /* 不使用FIFO */
    gI2sRxDma.Init.MemBurst = DMA_MBURST_SINGLE;                  /* 存储器单次突发传输 */
    gI2sRxDma.Init.PeriphBurst = DMA_PBURST_SINGLE;               /* 外设突发单次传输 */
	HAL_DMA_DeInit(&gI2sRxDma);                                   /* 先清除以前的设置 */
    HAL_DMA_Init(&gI2sRxDma);                                     /* 初始化DMA */
	HAL_DMA_Start(&gI2sRxDma,(uint32_t)&I2S3ext->DR,(uint32_t)buf0,num);
	/* 10us延时,防止-O2优化出问题 */
	DelayUs(10); 
	__HAL_DMA_ENABLE_IT(&gI2sRxDma, DMA_IT_TC);                   /* 开启传输完成中断 */
    __HAL_DMA_CLEAR_FLAG(&gI2sRxDma, I2SEXT_RX_DMASx_FLAG);       /* 清除DMA传输完成中断标志位 */
    
    HAL_NVIC_SetPriority(I2SEXT_RX_DMASx_IRQn,I2sRxPrePriority,I2sRxSubPriority);             /* DMA中断优先级 */
    HAL_NVIC_EnableIRQ(I2SEXT_RX_DMASx_IRQn);
	__HAL_DMA_DISABLE(&gI2sRxDma);                                /* 先关闭DMA */
}

void (*i2s_tx_callback)(void);  /* I2S DMA TX 回调函数指针 */
void (*i2s_rx_callback)(void);  /* I2S DMA RX 回调函数指针 */
/**
 * @brief       I2S TX DMA 中断服务函数
 * @param       无
 * @retval      无
 */
void I2S_TX_DMASx_Handle(void)
{  
    if (__HAL_DMA_GET_FLAG(&gI2sTxDma, I2S_TX_DMASx_FLAG) != RESET)   /* DMA传输完成 */
    {
        __HAL_DMA_CLEAR_FLAG(&gI2sTxDma, I2S_TX_DMASx_FLAG);          /* 清除DMA传输完成中断标志位 */
        if (i2s_tx_callback != NULL)
        {
            i2s_tx_callback();  /* 执行回调函数,读取数据等操作在这里面处理 */
        }
    }
}

/**
 * @brief       I2S RX DMA 中断服务函数
 * @param       无
 * @retval      无
 */
void I2SEXT_RX_DMASx_Handle(void)
{ 
    if(__HAL_DMA_GET_FLAG(&gI2sRxDma,I2SEXT_RX_DMASx_FLAG) != RESET) /* DMA传输完成 */
    {
        __HAL_DMA_CLEAR_FLAG(&gI2sRxDma, I2SEXT_RX_DMASx_FLAG);       /* 清除DMA传输完成中断标志位 */
        if (i2s_rx_callback != NULL)
        {
            i2s_rx_callback();  /* 执行回调函数,读取数据等操作在这里面处理 */
        }
    }
}
/**
 * @brief       I2S开始播放
 * @param       无
 * @retval      无
 */
void AudioI2sStart(void)
{
	__HAL_DMA_ENABLE(&gI2sTxDma);     /* 开启DMA TX传输 */
}
/**
 * @brief       I2S停止播放
 * @param       无
 * @retval      无
 */
void AudioI2sStop(void)
{
    __HAL_DMA_DISABLE(&gI2sTxDma);    /* 关闭DMA TX传输 */
}
/**
 * @brief       I2S开始录音
 * @param       无
 * @retval      无
 */
void AudioI2sSextStart(void)
{
    __HAL_DMA_ENABLE(&gI2sRxDma);     /* 开启DMA RX传输 */
}
/**
 * @brief       I2S停止录音
 * @param       无
 * @retval      无
 */
void AudioI2sSextStop(void)
{
    __HAL_DMA_DISABLE(&gI2sRxDma);   /* 关闭DMA RX传输 */
}
3、I2S 头文件
#ifndef __AUDIOIIS__H
#define __AUDIOIIS__H
#include "sys/sys.h"
/**********************************宏定义**********************************************/
#define GPIO_AF_I2S_SPI                 GPIO_AF6_SPI3
#define GPIO_AF_I2S_EXT_SPI             GPIO_AF7_I2S3ext

#define I2S_WS_GPIO_PORT              GPIOA
#define I2S_WS_GPIO_PIN               GPIO_PIN_15
#define I2S_WS_GPIO_CLK_ENABLE()      do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0)     /* PA口时钟使能 */

#define I2S_SCLK_GPIO_PORT              GPIOB
#define I2S_SCLK_GPIO_PIN               GPIO_PIN_3
#define I2S_SCLK_GPIO_CLK_ENABLE()      do{ __HAL_RCC_GPIOB_CLK_ENABLE(); }while(0)     /* PB口时钟使能 */

#define I2S_SDOUT_GPIO_PORT             GPIOB
#define I2S_SDOUT_GPIO_PIN              GPIO_PIN_4
#define I2S_SDOUT_GPIO_CLK_ENABLE()     do{ __HAL_RCC_GPIOB_CLK_ENABLE(); }while(0)     /* PB口时钟使能 */

#define I2S_SDIN_GPIO_PORT              GPIOB
#define I2S_SDIN_GPIO_PIN               GPIO_PIN_5
#define I2S_SDIN_GPIO_CLK_ENABLE()      do{ __HAL_RCC_GPIOB_CLK_ENABLE(); }while(0)     /* PB口时钟使能 */

#define I2S_SPI                         SPI3
#define I2SEXT_SPI                      I2S3ext
#define I2S_SPI_CLK_ENABLE()            do{ __HAL_RCC_SPI3_CLK_ENABLE(); }while(0)      /* I2S3时钟使能*/

#define I2S_TX_DMASx                    DMA1_Stream5
#define I2S_TX_DMASx_Channel            DMA_CHANNEL_0
#define I2S_TX_DMASx_Handle             DMA1_Stream5_IRQHandler
#define I2S_TX_DMASx_IRQn               DMA1_Stream5_IRQn
#define I2S_TX_DMASx_FLAG               DMA_FLAG_TCIF1_5
#define I2S_TX_DMA_CLK_ENABLE()         do{ __HAL_RCC_DMA1_CLK_ENABLE(); }while(0)   /* I2S3 TX DMA时钟使能 */

#define I2SEXT_RX_DMASx                    DMA1_Stream0
#define I2SEXT_RX_DMASx_Channel            DMA_CHANNEL_3
#define I2SEXT_RX_DMASx_Handle             DMA1_Stream0_IRQHandler
#define I2SEXT_RX_DMASx_IRQn               DMA1_Stream0_IRQn
#define I2SEXT_RX_DMASx_FLAG               DMA_FLAG_TCIF0_4
#define I2SEXT_RX_DMA_CLK_ENABLE()         do{ __HAL_RCC_DMA1_CLK_ENABLE(); }while(0)   /* I2S3 RX DMA时钟使能 */

#define REC_SAMPLERATE              8000       /* 采样率,8Khz */

/*****************************************全局函数***********************************/

extern void AudioI2sInit(void);
extern void AudioI2sSextInit(void);
extern void I2sTxDmaInit(uint8_t* buf0, uint16_t num);
// void I2sSextRxDmaInit(uint8_t* buf0, uint8_t *buf1, uint16_t num);
extern void (*i2s_tx_callback)(void);
extern void (*i2s_rx_callback)(void);
extern void AudioI2sStart(void);
extern void AudioI2sStop(void);
extern void AudioI2sSextStart(void);
extern void AudioI2sSextStop(void);
extern void I2sSextRxDmaInit(uint8_t* buf0, uint32_t num);
//extern void I2sTxDmaInit(uint8_t* buf0,uint8_t *buf1, uint16_t num);
//extern void I2sSextRxDmaInit(uint8_t* buf0, uint8_t *buf1, uint16_t num);
extern void i2s_init(uint32_t i2s_standard, uint32_t i2s_mode, uint32_t i2s_clock_polarity, uint32_t i2s_dataformat);
extern void i2sext_init(uint32_t i2sext_standard, uint32_t i2sext_mode, uint32_t i2sext_clock_polarity, uint32_t i2sext_dataformat);
#endif

4、main函数
#include "config.h"
/****************************全局变量*************************************************/
uint8_t DmrUartTxbufData[REC_I2S_RX_DMA_BUF_SIZE];  /*DMR发送到MCU的PCM数据发送至I2S发声*/	
uint8_t DmrUartRxbufData[REC_I2S_RX_DMA_BUF_SIZE];
/*****************************************************************************
 函 数 名  : 
 功能描述  : 录音功能初始化
 输入参数  : void
 注	   意  :设置发发送数据初始化
 返 回 值  : void
 作  者    : GCC
*****************************************************************************/
void I2sTxCallback(void)
{
//	printf("I2sTxCallback\r\n");
}
void rec_i2s_dma_rx_callback(void)
{
	
}
void recoder_enter_rec_mode(void)
{
	Es8388AddaCfg(1, 1);          /* 开启ADC,DAC */
	Es8388InputCfg(0);            /* 开启输入通道(通道1,MIC所在通道) */
    Es8388MicGain(4);             /* MIC增益设置为最大 */
    Es8388AlcCtrl(1, 5,6);        /* 开启立体声ALC控制,以提高录音音量 5:23.5dB最大,最小+24dB*/
//    Es8388OutputCfg(1,0);        /* 关闭通道1和2的输出 */
	Es8388Rout1Set(15);           /* 关闭喇叭. */
    Es8388I2sCfg(0, 3);           /* 飞利浦标准,16位数据长度 */
	Es8388Channel_3d_set(0,2);		//输出右声道
	DelayMs(100);
 	//主传发送
	AudioI2sInit();
	AudioI2sSextInit();
	I2sSextRxDmaInit(DmrUartRxbufData, REC_I2S_RX_DMA_BUF_SIZE);
	i2s_rx_callback = rec_i2s_dma_rx_callback;
	
	I2sTxDmaInit(DmrUartRxbufData,REC_I2S_RX_DMA_BUF_SIZE);//
	i2s_tx_callback = I2sTxCallback;
	AudioI2sStart();   /* 开始I2S数据发送(主机) */
    AudioI2sSextStart();    /* 开始I2S数据接收(从机) */

}
void RecorderStart(void)
{
	recoder_enter_rec_mode();   /*进入录音模式 */
    while(1);
}
总结:

        总体上比较算是比较容易,就是看手册的时候得耐着性子看,还有比较多的概念啥的,慢慢理解吧,看的久了自然,不懂得慢慢百度,看的多了自然就知道了。


http://www.kler.cn/a/447510.html

相关文章:

  • 2024年港澳台华侨生联考师范类院校录取情况来
  • 嵌入式单片机的运行方式详解
  • CUDA编程 | 6.2 并发内核执行
  • PCL点云库入门——PCL库中点云数据拓扑关系之K-D树(KDtree)
  • VS Code Copilot 与 Cursor 对比
  • 苹果手机怎么清理空间:拯救你的拥挤手机
  • Mono里运行C#脚本2—参数配置
  • 如何在Java中使用封装好的API接口?
  • 【Leetcode 热题 100】124. 二叉树中的最大路径和
  • 混合开发环境---使用编程AI辅助开发Qt
  • NOI与USACO的关系
  • 博客系统(Java 实现详解)
  • 【最大似然估计】之Python实现
  • 图像生成工具WebUI
  • MySQL知识汇总(二):select
  • ARM原理
  • 文件包含tomato靶机通关
  • 39.在 Vue3 中使用 OpenLayers 导出 GeoJSON 文件及详解 GEOJSON 格式
  • LLMs之rStar:《Mutual Reasoning Makes Smaller LLMs Stronger Problem-Solvers》翻译与解读
  • 前端知识补充—HTML
  • Java每日一题(2)
  • 260-高速AD/DA开发板 大容量FPGA编程 USRP K7-SDR Kintex-7 XC7K325T
  • 基于NodeMCU的物联网空调控制系统设计
  • zookepper安装部署
  • Vue.js 核心概念:模板、指令、数据绑定
  • centos7安装python3(保留python2.7)