|
发表于 2024-12-24 07:27:40
|
显示全部楼层
应该也可以的,SAI配置过一个,供参考
[C] 纯文本查看 复制代码 /*
*********************************************************************************************************
* 函 数 名: SAI_Mode_Config
* 功能说明: 配置STM32的SAI音频接口的工作模式。 放音
* 形 参: _mode : 1表示录音, 2表示放音, 3表示录音放音同时(保留未实现)
* _usStandard : 接口标准 I2S_Standard_Phillips, I2S_Standard_MSB 或 I2S_Standard_LSB
* _uiWordLen : 样本字长,可选的参数: SAI_DataSize_8b,SAI_DataSize_10b
* SAI_DataSize_16b, SAI_DataSize_20b, SAI_DataSize_24b, SAI_DataSize_32b
* 目前仅调试支持16bit。
* WM8978支持 16、20、24、32bit;
* STM32F492支持 8、10、16、20、24、32bit
*
* _usAudioFreq : 采样频率,I2S_AudioFreq_8K、I2S_AudioFreq_16K、I2S_AudioFreq_22K、
* I2S_AudioFreq_44K、I2S_AudioFreq_48
* 返 回 值: 无
*********************************************************************************************************
*/
/*
*********************************************************************************************************
* 特别说明:
* SAI_CK_A和SAI_CK_B的时钟可以由PLLSAI或者PLLI2S提供,由于LCD的LTDC要使用PLLSAI,这里WM8978使用PLLI2S。
* --------------------------------------
* 本工程 HSE_VALUE = 8MHz,PLL_M = 8。
* 第1步: 先获取PLLI2SCLK
* PLLI2SCLK = (HSE_VALUE / PLL_M) * PLLI2S_N) / PLLI2S_Q
*
* 第2步: 经过PLLI2SDiv_Q分频获得SAI-A和SAI-B。
* SAI_CK_A和SAI_CK_B = PLLI2SCLK / PLLI2SDiv_Q
*
* 第3步:
* 如果 MCKDIV[3:0] 不等于 0000,则 MCLK_x = SAI_CK_x / (MCKDIV[3:0] * 2)
* 如果 MCKDIV[3:0] 等于 0000,则 MCLK_x = SAI_CK_x
* 此时MCLK_x的输出就是SAI接口的MCLK的输出频率,即主时钟频率。
* 另外采样率跟主时钟频率的关系是MCLK/FS = 256,采样率通过SAI接口的SCK引脚输出。
* --------------------------------------------------------------------------------
* Note:误差率 = abs(实际采样率-目标采样率)/目标采样率 * 100%
*
* 采用下面的参数配置采样率误差如下:
* 目标采样率(Hz) 实际采样率(Hz) 误差率
* 8000 8000 0.0000%
* 16000 16000.60096 0.0037%
* 32000 32001.20192 0.0037%
* 48000 47991.07142 0.0186%
* 96000 95982.14285 0.0186%
* 192000 191894.53125 0.0549%
*
* 11020 11024.8766 0.0011%
* 22050 22049.7532 0.0011%
* 44100 44099.5065 0.0011%
*
*********************************************************************************************************
*/
typedef struct
{
uint32_t Fs;
uint16_t PLLI2S_N; /* 范围可以取192--432 */
uint16_t PLLI2S_Q; /* 范围可以取2--15 */
uint8_t PLLI2S_DivQ; /* 范围可以取1--32 */
uint8_t MCKDIV; /* 范围可以取0--15 */
}PLLI2SPSC_T;
const PLLI2SPSC_T g_tPLLI2SPSC[]=
{
//参数范围192-432 2-15 1-32
/* Fs PLLI2S_N, PLLI2S_Q PLLI2S_DivQ MCKDIV */
{8000 , 256, 5, 25, 0}, /* MCLK = 2.048MHz, 实际采样率 = 8000KHz */
{16000, 213, 2, 26, 0}, /* MCLK = 4.096153MHz,实际采样率 = 16000.6KHz */
{32000, 213, 2, 13, 0}, /* MCLK = 8.192307MHz,实际采样率 = 32001KHz */
{48000, 258, 3, 7, 0}, /* MCLK = 12.28571MHz, 实际采样率 = 47991KHz */
{96000, 344, 2, 7, 0}, /* MCLK = 24.57142MHz, 实际采样率 = 95982KHz */
{192000, 393, 2, 4, 0}, /* MCLK = 49.125MHz, 实际采样率 = 191894KHz */
{11020, 429, 2, 19, 2}, /* MCLK = 2.822368MHz, 实际采样率 = 11.024KHz */
{22050, 429, 2, 19, 1}, /* MCLK = 5.644737MHz, 实际采样率 = 22.049KHz */
{44100, 429, 2, 19, 0}, /* MCLK = 11.28947MHz, 实际采样率 = 44.099KHz */
};
static void SAI_Mode_Config(uint8_t _mode, uint16_t _usStandard, uint32_t _uiWordLen, uint32_t _uiAudioFreq, uint8_t _ucChannelMode)
{
uint32_t n = 0;
SAI_InitTypeDef SAI_InitStructure;
SAI_FrameInitTypeDef SAI_FrameInitStructure;
SAI_SlotInitTypeDef SAI_SlotInitStructure;
/* 这里采用列表的形式配置PLLI2S的时钟供SAI时钟使用 */
for(n = 0; n < (sizeof(g_tPLLI2SPSC)/sizeof(g_tPLLI2SPSC[0])); n++)
{
if(_uiAudioFreq == g_tPLLI2SPSC[n].Fs)
{
break;
}
}
/* 配置PLLI2S时钟 */
RCC_PLLI2SCmd(DISABLE);
RCC_PLLI2SConfig(g_tPLLI2SPSC[n].PLLI2S_N, g_tPLLI2SPSC[n].PLLI2S_Q, 4);
RCC_SAIPLLI2SClkDivConfig(g_tPLLI2SPSC[n].PLLI2S_DivQ);
/* 使能PLLI2S时钟 */
RCC_PLLI2SCmd(ENABLE);
/* 等待就绪 */
while(RCC_GetFlagStatus(RCC_FLAG_PLLI2SRDY) == RESET) {}
/* 配置SAI_Block_A和 SAI_Block_B的时钟源 */
RCC_SAIBlockACLKConfig(RCC_SAIBCLKSource_PLLI2S);
RCC_SAIBlockBCLKConfig(RCC_SAIBCLKSource_PLLI2S);
SAI_InitStructure.SAI_NoDivider = SAI_MasterDivider_Enabled;
SAI_InitStructure.SAI_MasterDivider = g_tPLLI2SPSC[n].MCKDIV;
/*
[内部同步]
1. 音频模块可声明为与第二个音频模块同步。在这种情况下,将共用位时钟和帧同步信号,以
减少通信时占用外部引脚的数量。
2. 声明为与另一个模块同步的音频模块将释放其 SCK_x、FS_x 和 MCLK_x 引脚以用作 GPIO。
3. 声明为异步的模块将使用其 I/O 引脚 FS_x、 SCK_x 和 MCLK_x(如果该音频模块被视为主模块)。
通常,音频模块同步模式可用于在全双工模式下配置 SAI。两个音频模块的其中一个可配置为主模块,另一个为从模块;
也可将二个均配置为从模块;一个模块声明为异步 SAI_xCR1 中的相应位 SYNCEN[1:0] = 00),
另一个声明为同步( SAI_xCR1 中的相应位SYNCEN[1:0] = 01)。
注意: 由于存在内部重新同步阶段, APB 频率 PCLK 必须大于或等于比特率时钟频率的二倍。
必须在使能主模式前使能从模式。
*/
if (_mode == 1) /* 放音 */
{
/* 配置音频子模块1 --- 放音的 , 两个子模块同步模式工作 */
SAI_InitStructure.SAI_AudioMode = SAI_Mode_MasterTx; /* 配置为主模式发送 */
SAI_InitStructure.SAI_Protocol = SAI_Free_Protocol; /* 协议 */
SAI_InitStructure.SAI_DataSize = _uiWordLen; /* 样本字长 */
SAI_InitStructure.SAI_FirstBit = SAI_FirstBit_MSB; /* bit次序,高bit先传 */
SAI_InitStructure.SAI_ClockStrobing = SAI_ClockStrobing_RisingEdge;
SAI_InitStructure.SAI_Synchro = SAI_Asynchronous; /* 申明为异步,使用本模块的 FS, SCK,MCLK */
SAI_InitStructure.SAI_OUTDRIV = SAI_OutputDrive_Enabled;
SAI_InitStructure.SAI_FIFOThreshold = SAI_FIFOThreshold_1QuarterFull;
SAI_Init(SAI_BLOCK1, &SAI_InitStructure);
/* Configure SAI_Block_x Frame
Frame Length : 32
Frame active Length: 16
FS Definition : Start frame + Channel Side identification
FS Polarity: FS active Low
FS Offset: FS asserted one bit before the first bit of slot 0 */
/*
则帧长度应为 8 到 256 之间的一个等于 2
的 n 次幂的数。这是为了确保音频帧的每个位时钟包含整数个 MCLK 脉冲,这样可确保解码器内的外部 DAC/ADC 正确工作。
*/
SAI_FrameInitStructure.SAI_FrameLength = 32; /* ST板子是64, V6是32 */
SAI_FrameInitStructure.SAI_ActiveFrameLength = 16;
SAI_FrameInitStructure.SAI_FSDefinition = I2S_FS_ChannelIdentification; /* FS定义为左右声道 */
SAI_FrameInitStructure.SAI_FSPolarity = SAI_FS_ActiveLow;
SAI_FrameInitStructure.SAI_FSOffset = SAI_FS_BeforeFirstBit;
SAI_FrameInit(SAI_BLOCK1, &SAI_FrameInitStructure);
/* 配置 SAI Block_x Slot
Slot First Bit Offset : 0
Slot Size : 16
Slot Number : 2 <---- ST官方板子用的4个Slot,耳机和扬声器各2个左右声道。 安富莱V6板子WM8978就2个Slot表示左右声道。
Slot Active : All slot actives
*/
SAI_SlotInitStructure.SAI_FirstBitOffset = 0;
SAI_SlotInitStructure.SAI_SlotSize = SAI_SlotSize_16b;
// SAI_SlotInitStructure.SAI_SlotSize = SAI_SlotSize_DataSize; /* slot大小由SAI_ACR1 寄存器的 DS[3:0] 位中指定*/
if(_ucChannelMode == 2)
{
SAI_SlotInitStructure.SAI_SlotNumber = 2; /* 2个声道, 每个声道一个 Slot */
SAI_SlotInitStructure.SAI_SlotActive = SAI_SlotActive_0 |SAI_SlotActive_1;
}
else if(_ucChannelMode == 1)
{
SAI_SlotInitStructure.SAI_SlotNumber = 1; /* 1个声道 */
SAI_SlotInitStructure.SAI_SlotActive = SAI_SlotActive_0;
}
SAI_SlotInit(SAI_BLOCK1, &SAI_SlotInitStructure);
SAI_FlushFIFO(SAI_BLOCK1);
}
else if (_mode == 2) /* 录音 */
{
/* 配置音频子模块1 --- 放音的 , 两个子模块同步模式工作 */
SAI_InitStructure.SAI_AudioMode = SAI_Mode_MasterRx; /* 配置为主模式接收 */
SAI_InitStructure.SAI_Protocol = SAI_Free_Protocol; /* 协议 */
SAI_InitStructure.SAI_DataSize = _uiWordLen; /* 样本字长 */
SAI_InitStructure.SAI_FirstBit = SAI_FirstBit_MSB; /* bit次序,高bit先传 */
SAI_InitStructure.SAI_ClockStrobing = SAI_ClockStrobing_RisingEdge;
SAI_InitStructure.SAI_Synchro = SAI_Asynchronous; /* 申明为异步,使用本模块的 FS, SCK,MCLK */
SAI_InitStructure.SAI_OUTDRIV = SAI_OutputDrive_Enabled;
SAI_InitStructure.SAI_FIFOThreshold = SAI_FIFOThreshold_1QuarterFull;
SAI_Init(SAI_BLOCK2, &SAI_InitStructure);
/* Configure SAI_Block_x Frame
Frame Length : 32
Frame active Length: 16
FS Definition : Start frame + Channel Side identification
FS Polarity: FS active Low
FS Offset: FS asserted one bit before the first bit of slot 0 */
/*
则帧长度应为 8 到 256 之间的一个等于 2
的 n 次幂的数。这是为了确保音频帧的每个位时钟包含整数个 MCLK 脉冲,这样可确保解码器内的外部 DAC/ADC 正确工作。
*/
SAI_FrameInitStructure.SAI_FrameLength = 32; /* ST板子是64, V6是32 */
SAI_FrameInitStructure.SAI_ActiveFrameLength = 16;
SAI_FrameInitStructure.SAI_FSDefinition = I2S_FS_ChannelIdentification; /* FS定义为左右声道 */
SAI_FrameInitStructure.SAI_FSPolarity = SAI_FS_ActiveLow;
SAI_FrameInitStructure.SAI_FSOffset = SAI_FS_BeforeFirstBit;
SAI_FrameInit(SAI_BLOCK2, &SAI_FrameInitStructure);
/* 配置 SAI Block_x Slot
Slot First Bit Offset : 0
Slot Size : 16
Slot Number : 2 <---- ST官方板子用的4个Slot,耳机和扬声器各2个左右声道。 安富莱V6板子WM8978就2个Slot表示左右声道。
Slot Active : All slot actives
*/
SAI_SlotInitStructure.SAI_FirstBitOffset = 0;
SAI_SlotInitStructure.SAI_SlotSize = SAI_SlotSize_16b;
SAI_SlotInitStructure.SAI_SlotSize = SAI_SlotSize_DataSize;
SAI_SlotInitStructure.SAI_SlotNumber = 2; /* 2个声道, 每个声道一个 Slot */
SAI_SlotInitStructure.SAI_SlotActive = SAI_SlotActive_0;
SAI_SlotInit(SAI_BLOCK2, &SAI_SlotInitStructure);
SAI_FlushFIFO(SAI_BLOCK2);
}
else if (_mode == 3) /* 录音和放音同时 */
{
/* 配置音频子模块2 --- 录音的 */
SAI_InitStructure.SAI_AudioMode = SAI_Mode_SlaveRx; /* 配置为从模式接收 */
SAI_InitStructure.SAI_Protocol = SAI_Free_Protocol; /* 协议 */
SAI_InitStructure.SAI_DataSize = _uiWordLen; /* 字长参数,注意 SAI_DataSize_16b 不是 16 */
SAI_InitStructure.SAI_FirstBit = SAI_FirstBit_MSB; /* 标准I2S格式,都是高bit先传输 */
SAI_InitStructure.SAI_ClockStrobing = SAI_ClockStrobing_RisingEdge;
SAI_InitStructure.SAI_Synchro = SAI_Synchronous; /* 选同步模式, */
SAI_InitStructure.SAI_OUTDRIV = SAI_OutputDrive_Disabled;
SAI_InitStructure.SAI_FIFOThreshold = SAI_FIFOThreshold_1QuarterFull;
SAI_Init(SAI_BLOCK2, &SAI_InitStructure);
/* 配置音频子模块1 --- 放音的 , 两个子模块同步模式工作 */
SAI_InitStructure.SAI_AudioMode = SAI_Mode_MasterTx; /* 配置为主模式发送 */
SAI_InitStructure.SAI_Protocol = SAI_Free_Protocol; /* 协议 */
SAI_InitStructure.SAI_DataSize = _uiWordLen; /* 样本字长 */
SAI_InitStructure.SAI_FirstBit = SAI_FirstBit_MSB; /* bit次序,高bit先传 */
SAI_InitStructure.SAI_ClockStrobing = SAI_ClockStrobing_RisingEdge;
SAI_InitStructure.SAI_Synchro = SAI_Asynchronous; /* 申明为异步,使用本模块的 FS, SCK,MCLK */
SAI_InitStructure.SAI_OUTDRIV = SAI_OutputDrive_Enabled;
SAI_InitStructure.SAI_FIFOThreshold = SAI_FIFOThreshold_1QuarterFull;
SAI_Init(SAI_BLOCK1, &SAI_InitStructure);
/* Configure SAI_Block_x Frame
Frame Length : 32
Frame active Length: 16
FS Definition : Start frame + Channel Side identification
FS Polarity: FS active Low
FS Offset: FS asserted one bit before the first bit of slot 0 */
/*
则帧长度应为 8 到 256 之间的一个等于 2
的 n 次幂的数。这是为了确保音频帧的每个位时钟包含整数个 MCLK 脉冲,这样可确保解码器内的外部 DAC/ADC 正确工作。
*/
SAI_FrameInitStructure.SAI_FrameLength = 32; /* ST板子是64, V6是32 */
SAI_FrameInitStructure.SAI_ActiveFrameLength = 16;
SAI_FrameInitStructure.SAI_FSDefinition = I2S_FS_ChannelIdentification; /* FS定义为左右声道 */
SAI_FrameInitStructure.SAI_FSPolarity = SAI_FS_ActiveLow;
SAI_FrameInitStructure.SAI_FSOffset = SAI_FS_BeforeFirstBit;
SAI_FrameInit(SAI_BLOCK1, &SAI_FrameInitStructure);
SAI_FrameInit(SAI_BLOCK2, &SAI_FrameInitStructure);
/* 配置 SAI Block_x Slot
Slot First Bit Offset : 0
Slot Size : 16
Slot Number : 2 <---- ST官方板子用的4个Slot,耳机和扬声器各2个左右声道。 安富莱V6板子WM8978就2个Slot表示左右声道。
Slot Active : All slot actives
*/
SAI_SlotInitStructure.SAI_FirstBitOffset = 0;
SAI_SlotInitStructure.SAI_SlotSize = SAI_SlotSize_16b;
SAI_SlotInitStructure.SAI_SlotSize = SAI_SlotSize_DataSize;
SAI_SlotInitStructure.SAI_SlotNumber = 2; /* 2个声道, 每个声道一个 Slot */
SAI_SlotInitStructure.SAI_SlotActive = SAI_SlotActive_0;
SAI_SlotInit(SAI_BLOCK1, &SAI_SlotInitStructure);
SAI_SlotInit(SAI_BLOCK2, &SAI_SlotInitStructure);
SAI_FlushFIFO(SAI_BLOCK1);
SAI_FlushFIFO(SAI_BLOCK2);
}
}
[C] 纯文本查看 复制代码 /*
*********************************************************************************************************
* 函 数 名: Rec_DMA_Init
* 功能说明: 配置DMA
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
void Rec_DMA_Init(uint8_t *_pBufferSRC0, uint8_t *_pBufferSRC1, uint16_t _usSize)
{
NVIC_InitTypeDef NVIC_InitStructure;
/* 使能时钟 */
RCC_AHB1PeriphClockCmd(REC_DMA_CLOCK, ENABLE);
SAI_DMACmd(SAI_BLOCK2, DISABLE);
DMA_Cmd(REC_DMA_STREAM, DISABLE);
DMA_DeInit(REC_DMA_STREAM);
/* 配置DMA双缓冲 */
DMA_Init_Rec.DMA_Channel = REC_DMA_CHANNEL;
DMA_Init_Rec.DMA_PeripheralBaseAddr = SAI_BLOCK2_DR;
DMA_Init_Rec.DMA_Memory0BaseAddr = (uint32_t)_pBufferSRC0;
DMA_Init_Rec.DMA_DIR = DMA_DIR_PeripheralToMemory;
DMA_Init_Rec.DMA_BufferSize = _usSize;
DMA_Init_Rec.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_Init_Rec.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_Init_Rec.DMA_PeripheralDataSize = REC_DMA_PERIPH_DATA_SIZE;
DMA_Init_Rec.DMA_MemoryDataSize = REC_DMA_MEM_DATA_SIZE;
DMA_Init_Rec.DMA_Mode = DMA_Mode_Circular;
DMA_Init_Rec.DMA_Priority = DMA_Priority_High;
DMA_Init_Rec.DMA_FIFOMode = DMA_FIFOMode_Disable;
DMA_Init_Rec.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
DMA_Init_Rec.DMA_MemoryBurst = DMA_MemoryBurst_Single;
DMA_Init_Rec.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
DMA_DoubleBufferModeConfig(REC_DMA_STREAM, (uint32_t)_pBufferSRC1, DMA_Memory_0);
DMA_DoubleBufferModeCmd(REC_DMA_STREAM, ENABLE);
DMA_Init(REC_DMA_STREAM, &DMA_Init_Rec);
/* 使能传输完成中断 */
DMA_ITConfig(REC_DMA_STREAM, DMA_IT_TC, ENABLE);
/* 使能SAI DMA请求 */
SAI_DMACmd(SAI_BLOCK2, ENABLE);
/* 使能DMA中断 */
NVIC_InitStructure.NVIC_IRQChannel = REC_DMA_IRQ;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = EVAL_AUDIO_IRQ_PREPRIO;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = EVAL_AUDIO_IRQ_SUBRIO;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
[C] 纯文本查看 复制代码 /* 开始录音 */
case RecTaskAudioRecorde_5:
g_uiNum = 0;
wm8978_OutMute(1);
memset(pI2SBuffer0, 0, 2000);
memset(pI2SBuffer1, 0, 2000);
memset(pI2SBuffer2, 0, 2000);
memset(pI2SBuffer3, 0, 2000);
AUDIO_Init(3, I2S_Standard_Phillips, SAI_DataSize_16b, 32000, 1);
Play_DMA_Init(pI2SBuffer0, pI2SBuffer1, 1000);
AUDIO_Play();
Rec_DMA_Init(pI2SBuffer2, pI2SBuffer3, 1000);
REC_Play();
break; |
|