前言:
从零开始学习I2s外设,配置Es8288寄存器实现录音播放。本文章使用主控芯片是APM32F411系类。音频相关的概念比较多,就不再次做过多的介绍,本文章只是简单实现边录边播功能。APM系类兼容st的芯片,所以用st的hal库来实现。本文的介绍顺序从es8388这款芯片开始,再到I2s协议,再到最后代码讲解。
一、Es8388芯片介绍
1、Es8388介绍
Es8388有 2 个 ADC 通道和 2 个 DAC 通道,麦克风放大器,耳机放大器,数字音效以及模拟混合和增益功能组成。
主要特点:
I2S 接口,支持最高 192K,24bit 音频播放。
DAC 信噪比 96dB;ADC 信噪比 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引脚,用于发送或接收两个时分复用的数据通道上的 数据。
主时钟的256=8*16*2
(4)I2S2ext_SD 和 I2S3ext_SD:用于控制 I2S 全双工模式的附加引脚(映射到 MISO 引脚)。
提示:大家可以将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);
}
总结:
总体上比较算是比较容易,就是看手册的时候得耐着性子看,还有比较多的概念啥的,慢慢理解吧,看的久了自然,不懂得慢慢百度,看的多了自然就知道了。