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 downes8388I2cWriteReg(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.36ses8388I2cWriteReg(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/DSPlen : 数据长度	*    @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.5dbtempreg |= mingain & 0x07;		 //12dbes8388I2cWriteReg(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.rhkb.cn/news/493514.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

OB删除1.5亿数据耗费2小时

目录 回顾&#xff1a;mysql是怎么删除数据的&#xff1f; 删除方案 代码实现 执行结果 结论 本篇是实际操作 批量处理数据以及线程池线程数设置 记录学习 背景&#xff1a;有一张用户标签表&#xff0c;存储数据量达4个亿&#xff0c;使用OceanBase存储&#xff0c;由于…

20241217使用M6000显卡在WIN10下跑whisper来识别中英文字幕

20241217使用M6000显卡在WIN10下跑whisper来识别中英文字幕 2024/12/17 17:21 缘起&#xff0c;最近需要识别法国电影《地下铁》的法语字幕&#xff0c;使用 字幕小工具V1.2【whisper套壳/GUI封装了】 无效。 那就是直接使用最原始的whisper来干了。 当你重装WIN10的时候&#…

linux普通用户使用sudo不需要输密码

1.root用户如果没有密码&#xff0c;先给root用户设置密码 sudo passwd root #设置密码 2.修改visudo配置 su #切换到root用户下 sudo visudo #修改visudo配置文件 用户名 ALL(ALL) NOPASSWD: ALL #下图所示处新增一行配置 用户名需要输入自己当前主机的用户名

【C++11】可变模板参数

目录 可变模板的定义方式 参数包的展开方式 递归的方式展开参数包 STL中的emplace相关接口函数 STL容器中emplace相关插入接口函数 ​编辑 模拟实现&#xff1a;emplace接口 C11的新特性可变参数模板能够让您创建可以接受可变参数的函数模板和类模板&#xff0c;相比 C9…

python 曲线拟合,曲线拟合交点

目录 效果图: 源代码: 效果图: 源代码: import json import os import shutilimport cv2 import numpy as npfrom numpy.polynomial.polynomial import Polynomialdef calculate_distance(x1, y1, x2, y2):return np.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2)def get_new_g…

Java模拟Mqtt客户端连接Mqtt Broker

Java模拟Mqtt客户端基本流程 引入Paho MQTT客户端库 <dependency><groupId>org.eclipse.paho</groupId><artifactId>org.eclipse.paho.mqttv5.client</artifactId><version>1.2.5</version> </dependency>设置mqtt配置数据 …

圣诞快乐(h5 css js(圣诞树))

一&#xff0c;整体设计思路 圣诞树h5&#xff08;简易&#xff09; 1.页面布局与样式&#xff1a; 页面使用了全屏的黑色背景&#xff0c;中央显示圣诞树&#xff0c;树形由三层绿色的三角形组成&#xff0c;每一层的大小逐渐变小。树干是一个棕色的矩形&#xff0c;位于三角…

多音轨视频使用FFmpeg删除不要音轨方法

近期给孩子找宫崎骏动画&#xff0c;但是有很多是多音轨视频但是默认的都是日语&#xff0c;电视上看没办法所以只能下载后删除音轨文件只保留中文。 方法分两步&#xff0c;先安装FFmpeg在转文件即可。 第一步FFmpeg安装 FFmpeg是一个开源项目&#xff0c;包含了处理视频的…

时空信息平台架构搭建:基于netty封装TCP通讯模块(IdleStateHandler网络连接监测,处理假死)

文章目录 引言I 异步TCP连接操作II 心跳机制:空闲检测(读空闲和写空闲)基于Netty的IdleStateHandler类实现心跳机制(网络连接监测)常规的处理假死健壮性的处理假死方案引言 基于netty实现TCP客户端:封装断线重连、连接保持 https://blog.csdn.net/z929118967/article/de…

中国新能源汽车公共充电桩数据合集(2002-2023年)

数据来源&#xff1a;全国各省市统计年鉴、统计公报、国家能源署、中国汽车行业协会&#xff0c;各类汽车统计年鉴、中国电动汽车充电基础设施促进联盟等 时间跨度&#xff1a;新能源汽车数据集&#xff1a;2002-2023年&#xff08;不同数据时间跨度有差异&#xff0c;详见数据…

设计模式12:状态模式

系列总链接&#xff1a;《大话设计模式》学习记录_net 大话设计-CSDN博客 参考&#xff1a;设计模式之状态模式 (C 实现)_设计模式的状态模式实现-CSDN博客 1.概述 状态模式允许一个对象在其内部状态改变时改变其行为。对象看起来像是改变了其类。使用状态模式可以将状态的相…

国内网络在Ubuntu 22.04中在线安装Ollama并配置Open-WebuiDify

配置docker科技网络 登录后复制 创建或编辑 Docker 配置文件 让docker使用代理&#xff1a; sudo mkdir /etc/systemd/system/docker.service.d -p sudo vim /etc/systemd/system/docker.service.d/http-proxy.conf 文件&#xff0c;并添加以下内容&#xff1a; [Service] En…

【线性代数】理解矩阵乘法的意义(点乘)

刚接触线性代数时&#xff0c;很不理解矩阵乘法的计算规则&#xff0c;为什么规则定义的看起来那么有规律却又莫名其妙&#xff0c;现在参考了一些资料&#xff0c;回过头重新总结下个人对矩阵乘法的理解&#xff08;严格来说是点乘&#xff09;。 理解矩阵和矩阵的乘法&#x…

国标GB28181协议平台Liveweb:搭建建筑工地无线视频联网监控系统方案

随着科技高速发展&#xff0c;视频信号经过数字压缩&#xff0c;通过互联网宽带或者移动4G网络传递&#xff0c;可实现远程视频监控功能。将这一功能运用于施工现场安全管理&#xff0c;势必会大大提高管理效率&#xff0c;提升监管层次。而这些&#xff0c;通过Liveweb监控系统…

SQL语句练习

阅读《SQL必知必会》&#xff08;第五版&#xff09;然后结合往常表做的练习记录 这里使用的数据库时sqlite3,使用的工具时navicat 表资源链接https://wenku.baidu.com/view/349fb3639b6648d7c1c74652.html 表录入后如上图所示。后面如果有多张表之间的操作&#xff0c;在引入…

SAP RESTful架构和OData协议

一、RESTful架构 RESTful 架构&#xff08;Representational State Transfer&#xff09;是一种软件架构风格&#xff0c;专门用于构建基于网络的分布式系统&#xff0c;尤其是在 Web 服务中。它通过利用 HTTP 协议和一组简单的操作&#xff08;如 GET、POST、PUT、DELETE&…

基于MATLAB的图像增强

&#x1f351;个人主页&#xff1a;Jupiter. &#x1f680; 所属专栏&#xff1a;传知代码 欢迎大家点赞收藏评论&#x1f60a; 目录 一、背景及意义介绍背景图像采集过程中的局限性 意义 二、概述三、代码结构及说明&#xff08;一&#xff09;整体结构&#xff08;二&#xf…

通过阿里云 Milvus 与 PAI 搭建高效的检索增强对话系统

背景介绍 阿里云向量检索服务Milvus版&#xff08;简称阿里云Milvus&#xff09;是一款云上全托管服务&#xff0c;确保了了与开源Milvus的100%兼容性&#xff0c;并支持无缝迁移。在开源版本的基础上增强了可扩展性&#xff0c;能提供大规模 AI 向量数据的相似性检索服务。相…

滚珠花键的保养与维护方法

滚珠花键作为关键的线性运动引导装置&#xff0c;以其高精度和高刚性在众多领域发挥着举足轻重的作用。然而&#xff0c;为了保持其卓越的性能&#xff0c;保养与维护措施不可或缺。 滚珠花键的保养与维护其实就是润滑与清洁&#xff0c;以下是一些具体的保养与维护方法&#x…

Layui table不使用url属性结合laypage组件实现动态分页

从后台一次性获取所有数据赋值给 Layui table 组件的 data 属性&#xff0c;若数据量大时&#xff0c;很可能会超出浏览器字符串最大长度&#xff0c;导致渲染数据失败。Layui table 结合 laypage 组件实现动态分页可解决此问题。 HTML增加分页组件标签 在table后增加一个用于…