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

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);
}
总结:

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

相关文章:

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

前言&#xff1a; 从零开始学习I2s外设&#xff0c;配置Es8288寄存器实现录音播放。本文章使用主控芯片是APM32F411系类。音频相关的概念比较多&#xff0c;就不再次做过多的介绍&#xff0c;本文章只是简单实现边录边播功能。APM系类兼容st的芯片&#xff0c;所以用st的hal库来…...

RabbitMQ消息队列的笔记

Rabbit与Java相结合 引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-amqp</artifactId> </dependency> 在配置文件中编写关于rabbitmq的配置 rabbitmq:host: 192.168.190.132 /…...

JAVA没有搞头了吗?

前言 今年的Java程序员群体似乎承受着前所未有的焦虑。投递简历无人问津&#xff0c;难得的面试机会也难以把握&#xff0c;即便成功入职&#xff0c;也往往难以长久。于是&#xff0c;不少程序员感叹&#xff1a;互联网的寒冬似乎又一次卷土重来&#xff0c;环境如此恶劣&…...

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

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

游戏开发技能系统常用概念

一个角色同一时间可能存在多个Skill&#xff0c;一个当前播放的主动技能&#xff0c;还有好几个不在播放中&#xff0c;但是也没有结束的。 技能事件&#xff1a; 实现具体的技能功能&#xff0c;技能动作的执行都是通过触发事件来触发的&#xff0c;比如&#xff08;时间帧&am…...

【案例80】麒麟操作系统无法使用Uclient访问NC65

问题现象 麒麟操作系统&#xff0c;安装Uclient&#xff0c;添加应用后无法看到登录界面&#xff0c;一直在转圈。 问题分析 进入到Uclient的工作目录 发现在工作目录下&#xff0c;无相关app.log生成。 查看Uclient的main.log发现&#xff0c;有大量的报错与Uclient下的sha…...

【AIGC-ChatGPT进阶副业提示词】育儿锦囊:化解日常育儿难题的实用指南

引言 在育儿的道路上&#xff0c;每位父母都会遇到各种各样的挑战和困惑。从孩子的饮食习惯到沟通交流&#xff0c;从作息规律到行为管理&#xff0c;这些看似平常的问题往往会让父母感到焦虑和无助。本文将通过实际案例分析和解决方案&#xff0c;为父母们提供一个实用的育儿…...

使用Docker部署一个Node.js项目

本文档将介绍如何使用Docker来部署一个Node.js项目。Docker是一种开源平台&#xff0c;可以自动化应用程序的部署、扩展和管理。通过Docker&#xff0c;我们可以将应用程序及其依赖项打包到一个容器中&#xff0c;从而确保在不同环境中运行的一致性。接下来&#xff0c;我们将逐…...

数据科学与SQL:如何利用本福特法则识别财务数据造假?

目录 0 本福特法则介绍 1 数据准备 2 问题分析 步骤1:提取首位数: 步骤2:计算首位数字的实际频率分布 <...

文心一言对接FreeSWITCH实现大模型呼叫中心

文心一言对接FreeSWITCH实现大模型呼叫中心 作者&#xff1a;开源大模型智能呼叫中心FreeIPCC&#xff0c;Github&#xff1a;https://github.com/lihaiya/freeipcc 随着人工智能技术的快速发展&#xff0c;特别是大规模语言模型&#xff08;LLM&#xff09;的应用&#xff0…...

LSTM实现天气模型训练与预测

要实现一个天气预测的模型&#xff0c;并确保该模型可以反复进行训练和更新&#xff0c;先设计&#xff1a; 设计方案 数据获取&#xff1a; 使用公开的天气数据API&#xff08;例如OpenWeather API或其他类似的API&#xff09;获取天气数据。确保数据以合适的格式&#xff08…...

TCL发布万象分区,再造Mini LED技术天花板

作者 |辰纹 来源 | 洞见新研社 现实世界中&#xff0c;光通过悬浮在大气中的冰晶折射&#xff0c;呈现出环形、弧形、柱形或亮点的扩散&#xff0c;从而产生光晕&#xff0c;雨后的彩虹是我们经常能看到的光晕现象。 然而&#xff0c;当光晕出现在电视中&#xff0c;那就不是…...

2024广东省职业技能大赛云计算——私有云(OpenStack)平台搭建

OpenStack搭建 前言 搭建采用双节点安装&#xff0c;即controller控制节点和compute计算节点。 CentOS7 系统选择 2009 版本&#xff1a;CentOS-7-x86_64-DVD-2009.iso 可从阿里镜像站下载&#xff1a;https://mirrors.aliyun.com/centos/7/isos/x86_64/ OpenStack使用竞赛培…...

简单了解图注意力机制

简单了解图注意力机制 如果对传统的图匹配的聚合方式进行创新的话&#xff0c;也就是对h这一个节点的聚合方式进行创新。 h i ( l 1 ) Norm ⁡ ( σ ( h i ( l ) α ∥ h i ( l ) ∥ m i ( l ) ∥ m i ( l ) ∥ ) ) , \mathbf{h}_{i}^{(l1)}\operatorname{Norm}\left(\sigm…...

UI Automator Viewer操作

版本&#xff1a;24.4.1 使用UI Automator Viewer报错如下&#xff1a; Error obtaining Ul hierarchy Reason: Error while obtaining Ul hierarchy XML file: com.android.ddmlib.SyncException: Remote object doesnt exist!可以使用指令&#xff1a; 保存uix文件 adb sh…...

SpringBoot的创建方式

SpringBoot创建的五种方式 1.通过Springboot官网链接下载 注意SpringBoot项目的封装方式默认为Jar 需要查看一下&#xff0c;自己的Maven版本是否正确 创建成功 2.通过 aliyun官网链接下载 修改服务路径为阿里云链接 创建成功 3.通过Springboot官网下载 点击&#xff0c;拉到最…...

Vue3之性能优化

Vue3作为Vue框架的最新版本&#xff0c;在性能上进行了大量的优化&#xff0c;使得其在处理大型应用和复杂界面时表现更加出色。本文将详细介绍Vue3的性能提升、优化策略以及性能提升的实例&#xff0c;并结合具体代码和性能测试数据&#xff0c;展示Vue3在实际应用中的性能优势…...

RFdiffusion Sampler类 sample_step 方法解读

Sampler类的sample_step 方法的主要目的是根据扩散模型的预测生成在时间步 t-1 上的下一个三维结构、序列和其他相关特征。这是扩散采样过程的核心步骤之一。 源代码: def sample_step(self, *, t, x_t, seq_init, final_step):Generate the next pose that the model should…...

Flutter组件————FloatingActionButton

FloatingActionButton 是Flutter中的一个组件&#xff0c;通常用于显示一个圆形的按钮&#xff0c;它悬浮在内容之上&#xff0c;旨在吸引用户的注意力&#xff0c;并代表屏幕上的主要动作。这种按钮是Material Design的一部分&#xff0c;通常放置在页面的右下角&#xff0c;但…...

算法学习(十六)—— 综合练习

目录 1863. 找出所有子集的异或总和再求和 47. 全排列 Ⅱ 17. 电话号码的字母组合 22. 括号生成 77. 组合 494. 目标和 39. 组合总和 784. 字母大小写全排列 526. 优美的排列 51. N皇后 36. 有效的数独 37. 解数独 79. 单词搜索 1219. 黄金矿工 980. 不同路径 Ⅲ…...

kratos源码分析:熔断器

文章目录 为什么需要熔断Google sre弹性熔断算法kratos Breaker源码分析公共接口sre实现上报请求结果判定是否熔断 为什么需要熔断 一般来说&#xff0c;当服务器过载&#xff08;overload&#xff09;时&#xff0c;需要给client返回服务过载的报错 但是拒接请求也有成本&…...

CTF_1

CTF_Show 萌新赛 1.签到题 <?php if(isset($_GET[url])){system("curl https://".$_GET[url].".ctf.show"); }else{show_source(__FILE__); }?> 和 AI 一起分析 1.if(isset($_GET[url]))检查GET请求中是否存在名为url的参数。 curl 2.curl…...

【系统】Mac crontab 无法退出编辑模式问题

【系统】Mac crontab 无法退出编辑模式问题 背景一、问题回答1.定位原因&#xff1a;2.确认编辑器类型3.确保编辑器进入正确3.1 确认是否有crontab调度任务3.2 进入编辑器并确保编辑器正常3.3 保存操作 4.确认crontab任务存在5.确保脚本的可执行性和正确性 二、后续 背景 之前…...

K8s中 statefulset 和deployment的区别

在 Kubernetes 中&#xff0c;StatefulSet 和 Deployment 是两种管理 Pod 的控制器&#xff0c;它们的主要区别在于 状态管理 和 Pod 的标识。以下是详细对比&#xff1a; 1. 功能定位 Deployment 用途&#xff1a;用于 无状态应用 的部署&#xff0c;例如 Web 服务、API 服务…...

springboot中的AOP以及面向切面编程思想

快速入门体验AOP aop中相关概念 实现导入依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> 新建aop文件夹,里面声明XXXAspect类 @Aspect // 声…...

降低Mobx技术债问题-React前端数据流方案调研整理

我们现在主要是使用Mobx&#xff0c;但是Mobx的易于上手和灵活度也带来了很多预期以外的问题&#xff0c;随着项目的增长我们的代码技术债变得愈加沉重&#xff0c;不同的模块杂糅一起、单一store无限膨胀。 为此我们的调研是希望能找到一个更好的state配置、数据流的约定方案。…...

RabbitMQ消息可靠性保证机制7--可靠性分析-rabbitmq_tracing插件

rabbitmq_tracing插件 rabbitmq_tracing插件相当于Firehose的GUI版本&#xff0c;它同样能跟踪RabbitMQ中消息的注入流出情况。rabbitmq_tracing插件同样会对流入流出的消息进行封装&#xff0c;然后将封装后的消息日志存入相应的trace文件中。 # 开启插件 rabbitmq-plugins …...

SQL进阶技巧:如何求解直接线上最多的点数?

目录 0 问题描述 1 数据准备 2 问题分析 3 求解优化 步骤一:构建 “斜率键” 并统计点的数量(核心步骤) 步骤二:找出最多的点数(最终结果) 0 问题描述 “平面上最多的点数” 问题通常是指在一个二维平面中给定了若干个点的坐标(例如以 (x,y) 的形式表示),要求找…...

【老白学 Java】泛型应用 - 卡拉 OK(四)

泛型应用 - 卡拉 OK&#xff08;四&#xff09; 文章来源&#xff1a;《Head First Java》修炼感悟。 上文说到&#xff0c;解决了按歌名排序的问题后&#xff0c;老白立刻想到了按歌手名字排序的问题。 老白决定趁热打铁&#xff0c;尝试着实现自定义排序方式。 Collections…...

android studio更改应用图片,和应用名字。

更改应用图标&#xff0c;和名字 先打开AndroidManifest.xml文件。 更改图片文件名字&#xff08; 右键-->构建-->重命名&#xff08;R&#xff09;&#xff09;...