硬汉嵌入式论坛

 找回密码
 立即注册
查看: 999|回复: 2
收起左侧

I2S dma接受求助

[复制链接]

9

主题

25

回帖

52

积分

初级会员

积分
52
发表于 2024-12-23 17:45:54 | 显示全部楼层 |阅读模式
如题,请教个问题,在II2S中开启了DMA接受,因为是单通道的音频,能不能配置成只接受左声道或者右声道的数据
回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
117512
QQ
发表于 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;	
回复

使用道具 举报

9

主题

25

回帖

52

积分

初级会员

积分
52
 楼主| 发表于 2024-12-24 09:06:53 | 显示全部楼层
eric2013 发表于 2024-12-24 07:27
应该也可以的,SAI配置过一个,供参考

[mw_shl_code=c,true]/*

收到,多谢
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|Archiver|手机版|硬汉嵌入式论坛

GMT+8, 2025-8-12 00:49 , Processed in 0.048328 second(s), 25 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

快速回复 返回顶部 返回列表