STM32H743的SAI1 DMA接收异常问题

2019-07-20 06:14发布

大家帮我看一下,下面配置是否有问题,时钟都是对的,但是无法接收到音频数据,求解!

//V1.1 20160129
//新增SAIB_Init、SAIA_RX_DMA_Init、SAI_Rec_Start和SAI_Rec_Stop等函数                                                                  
//////////////////////////////////////////////////////////////////////////////////        
extern u8 sai1arecbuf1[SAI_RX_DMA_BUF_SIZE*2];         //DMA接收缓存1
extern u8 sai1arecbuf2[SAI_RX_DMA_BUF_SIZE*2];         //DMA接收缓存2
extern u8 sai1brecbuf1[SAI_RX_DMA_BUF_SIZE*2];         //DMA接收缓存1
extern u8 sai1brecbuf2[SAI_RX_DMA_BUF_SIZE*2];         //DMA接收缓存2


SAI_HandleTypeDef SAI1A_Handler;        //SAI1 Block A句柄
SAI_HandleTypeDef SAI1B_Handler;        //SAI1 Block B句柄
DMA_HandleTypeDef SAI1A_RXDMA_Handler;   //DMA接收句柄
DMA_HandleTypeDef SAI1B_RXDMA_Handler;   //DMA接收句柄
extern void rec_sai1a_dma_rx_callback(void);
extern void rec_sai1b_dma_rx_callback(void);


//SAI1初始化
void marster_sai1_init(void) //初始化sai1
{
  RCC_PeriphCLKInitTypeDef RCC_PeriphCLKInitStruct;
  /* Configure PLLSAI prescalers */
  /* PLL2SAI_VCO: VCO_429M
     SAI_CLK(first level) = PLLSAI_VCO/PLLSAIP = 344/7 = 11.289 Mhz */
  RCC_PeriphCLKInitStruct.PeriphClockSelection = RCC_PERIPHCLK_SAI1;
  RCC_PeriphCLKInitStruct.Sai1ClockSelection = RCC_SAI1CLKSOURCE_PLL2;
  RCC_PeriphCLKInitStruct.PLL2.PLL2P = 7;
  RCC_PeriphCLKInitStruct.PLL2.PLL2Q = 1;
  RCC_PeriphCLKInitStruct.PLL2.PLL2R = 1;
  RCC_PeriphCLKInitStruct.PLL2.PLL2N = 344;
  RCC_PeriphCLKInitStruct.PLL2.PLL2M = 25;
  HAL_RCCEx_PeriphCLKConfig(&RCC_PeriphCLKInitStruct);
        SAIA1_RX_DMA_Init(sai1arecbuf1,sai1arecbuf2,SAI_RX_DMA_BUF_SIZE,1);          //配置TX DMA,16位
        SAIB1_RX_DMA_Init(sai1brecbuf1,sai1brecbuf2,SAI_RX_DMA_BUF_SIZE,1);        //配置RX DMA               
        SAIB1_Init(3,1,4);                        //SAI1 Block B从模式接收,16位         
        SAIA1_Init(1,1,4);                        //SAI1 Block; A,主发送,16位数据
        sai1a_rx_callback = rec_sai1a_dma_rx_callback;                        //回调函数指wav_sai_dma_callback  
        sai1b_rx_callback = rec_sai1b_dma_rx_callback;                        //回调函数指wav_i2s_dma_callback       
}


//SAI Block A初始化,I2S,飞利浦标准
//mode:工作模式,可以设置:SAI_MODEMASTER_TX/SAI_MODEMASTER_RX/SAI_MODESLAVE_TX/SAI_MODESLAVE_RX
//cpol:数据在时钟的上升/下降沿选通,可以设置:SAI_CLOCKSTROBING_FALLINGEDGE/SAI_CLOCKSTROBING_RISINGEDGE
//datalen:数据大小,可以设置:SAI_DATASIZE_8/10/16/20/24/32
void SAIA1_Init(u32 mode,u32 cpol,u32 datalen)
{
    HAL_SAI_DeInit(&SAI1A_Handler);                          //清除以前的配置
    SAI1A_Handler.Instance=SAI1_Block_A;                     //SAI1 Bock A
    SAI1A_Handler.Init.AudioMode=SAI_MODEMASTER_RX;          //设置SAI1工作模式
    SAI1A_Handler.Init.Synchro=SAI_ASYNCHRONOUS;             //音频模块异步
    SAI1A_Handler.Init.OutputDrive=SAI_OUTPUTDRIVE_ENABLE;   //立即驱动音频模块输出
    SAI1A_Handler.Init.NoDivider=SAI_MASTERDIVIDER_ENABLE;   //使能主时钟分频器(MCKDIV)
          SAI1A_Handler.Init.FIFOThreshold=SAI_FIFOTHRESHOLD_1QF;  //设置FIFO阈值,1/4 FIFO
    SAI1A_Handler.Init.AudioFrequency = SAI_AUDIO_FREQUENCY_16K;       
    SAI1A_Handler.Init.MonoStereoMode=SAI_STEREOMODE;        //立体声模式
    SAI1A_Handler.Init.Protocol=SAI_FREE_PROTOCOL;           //设置SAI1协议为:自由协议(支持I2S/LSB/MSB/TDM/PCM/DSP等协议)
    SAI1A_Handler.Init.DataSize=SAI_DATASIZE_16;                     //设置数据大小
    SAI1A_Handler.Init.FirstBit=SAI_FIRSTBIT_MSB;            //数据MSB位优先
    SAI1A_Handler.Init.ClockStrobing=SAI_CLOCKSTROBING_RISINGEDGE;                   //数据在时钟的上升/下降沿选通

    //帧设置
    SAI1A_Handler.FrameInit.FrameLength=32;                  //设置帧长度为32,左通道16个SCK,右通道16个SCK.
    SAI1A_Handler.FrameInit.ActiveFrameLength=16;            //设置帧同步有效电平长度,在I2S模式下=1/2帧长.
    SAI1A_Handler.FrameInit.FSDefinition=SAI_FS_CHANNEL_IDENTIFICATION;//FS信号为SOF信号+通道识别信号
    SAI1A_Handler.FrameInit.FSPolarity=SAI_FS_ACTIVE_LOW;    //FS低电平有效(下降沿)
    SAI1A_Handler.FrameInit.FSOffset=SAI_FS_BEFOREFIRSTBIT;  //在slot0的第一位的前一位使能FS,以匹配飞利浦标准       

    //SLOT设置
    SAI1A_Handler.SlotInit.FirstBitOffset=0;                 //slot偏移(FBOFF)为0
    SAI1A_Handler.SlotInit.SlotSize=SAI_SLOTSIZE_16B;        //slot大小为16位
    SAI1A_Handler.SlotInit.SlotNumber=2;                     //slot数为2个   
    SAI1A_Handler.SlotInit.SlotActive=SAI_SLOTACTIVE_0|SAI_SLOTACTIVE_1;//使能slot0和slot1

    HAL_SAI_Init(&SAI1A_Handler);                            //初始化SAI
    SAIA1_DMA_Enable();
                __HAL_SAI_ENABLE(&SAI1A_Handler);                        //使能SAI
}

//SAI Block B初始化,I2S,飞利浦标准
//mode:工作模式,可以设置:SAI_MODEMASTER_TX/SAI_MODEMASTER_RX/SAI_MODESLAVE_TX/SAI_MODESLAVE_RX
//cpol:数据在时钟的上升/下降沿选通,可以设置:SAI_CLOCKSTROBING_FALLINGEDGE/SAI_CLOCKSTROBING_RISINGEDGE
//datalen:数据大小,可以设置:SAI_DATASIZE_8/10/16/20/24/32
void SAIB1_Init(u32 mode,u32 cpol,u32 datalen)
{
    HAL_SAI_DeInit(&SAI1B_Handler);                         //清除以前的配置
    SAI1B_Handler.Instance=SAI1_Block_B;                    //SAI1 Bock B
    SAI1B_Handler.Init.AudioMode=SAI_MODESLAVE_RX;                      //设置SAI1工作模式
    SAI1B_Handler.Init.Synchro=SAI_SYNCHRONOUS;             //音频模块同步
    SAI1B_Handler.Init.OutputDrive=SAI_OUTPUTDRIVE_ENABLE;  //立即驱动音频模块输出
    SAI1B_Handler.Init.NoDivider=SAI_MASTERDIVIDER_ENABLE;  //使能主时钟分频器(MCKDIV)
    SAI1B_Handler.Init.FIFOThreshold=SAI_FIFOTHRESHOLD_1QF; //设置FIFO阈值,1/4 FIFO
    SAI1B_Handler.Init.AudioFrequency = SAI_AUDIO_FREQUENCY_16K;            
          SAI1B_Handler.Init.MonoStereoMode=SAI_STEREOMODE;       //立体声模式
    SAI1B_Handler.Init.Protocol=SAI_FREE_PROTOCOL;          //设置SAI1协议为:自由协议(支持I2S/LSB/MSB/TDM/PCM/DSP等协议)
    SAI1B_Handler.Init.DataSize=SAI_DATASIZE_16;            //设置数据大小
    SAI1B_Handler.Init.FirstBit=SAI_FIRSTBIT_MSB;           //数据MSB位优先
    SAI1B_Handler.Init.ClockStrobing=SAI_CLOCKSTROBING_RISINGEDGE;//数据在时钟的上升/下降沿选通

    //帧设置
    SAI1B_Handler.FrameInit.FrameLength=32;                 //设置帧长度为32,左通道16个SCK,右通道16个SCK.
    SAI1B_Handler.FrameInit.ActiveFrameLength=16;           //设置帧同步有效电平长度,在I2S模式下=1/2帧长.
    SAI1B_Handler.FrameInit.FSDefinition=SAI_FS_CHANNEL_IDENTIFICATION;//FS信号为SOF信号+通道识别信号
    SAI1B_Handler.FrameInit.FSPolarity=SAI_FS_ACTIVE_LOW;   //FS低电平有效(下降沿)
    SAI1B_Handler.FrameInit.FSOffset=SAI_FS_BEFOREFIRSTBIT; //在slot0的第一位的前一位使能FS,以匹配飞利浦标准       

    //SLOT设置
    SAI1B_Handler.SlotInit.FirstBitOffset=0;                //slot偏移(FBOFF)为0
    SAI1B_Handler.SlotInit.SlotSize=SAI_SLOTSIZE_16B;       //slot大小为16位
    SAI1B_Handler.SlotInit.SlotNumber=2;                    //slot数为2个   
    SAI1B_Handler.SlotInit.SlotActive=SAI_SLOTACTIVE_0|SAI_SLOTACTIVE_1;//使能slot0和slot1

    HAL_SAI_Init(&SAI1B_Handler);
    SAIB1_DMA_Enable();                                      //使能SAI的DMA功能
    __HAL_SAI_ENABLE(&SAI1B_Handler);                       //使能SAI
}

//SAI底层驱动,引脚配置,时钟使能
//此函数会被HAL_SAI_Init()调用
//hsdram:SAI句柄
void HAL_SAI_MspInit(SAI_HandleTypeDef *hsai)
{
        GPIO_InitTypeDef GPIO_Initure;
        __HAL_RCC_SAI1_CLK_ENABLE();                //使能SAI1时钟
        __HAL_RCC_GPIOE_CLK_ENABLE();               //使能GPIOE时钟
       
        //初始化PE2,3,4,5,6
        GPIO_Initure.Pin=GPIO_PIN_2|GPIO_PIN_3|GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6;  
        GPIO_Initure.Mode=GPIO_MODE_AF_PP;          //推挽复用
        GPIO_Initure.Pull=GPIO_NOPULL;              //上拉
        GPIO_Initure.Speed=GPIO_SPEED_FREQ_VERY_HIGH; //高速
        GPIO_Initure.Alternate=GPIO_AF6_SAI1;       //复用为SAI   
        HAL_GPIO_Init(GPIOE,&GPIO_Initure);         //初始化
}

//SAI Block A采样率设置
//采样率计算公式:
//MCKDIV!=0: Fs=SAI_CK_x/[512*MCKDIV]
//MCKDIV==0: Fs=SAI_CK_x/256
//SAI_CK_x=(HSE/DIVM2)*PLLSAIN/PLLSAIQ/(PLLSAIDivQ+1)
//一般HSE=25Mhz
//pllm:在Stm32_Clock_Init设置的时候确定,一般是25
//PLLSAIN:一般是50~432
//PLLSAIQ:2~15
//PLLSAIDivQ:1~32
//MCKDIV:0~15
//SAI A分频系数表@pllm=25,HSE=25Mhz,即vco输入频率为1Mhz
const u16 SAI_PSC_TBL[][5]=
{
        {800 ,344,7,25,12},        //8Khz采样率
        {1102,429,38,25,2},        //11.025Khz采样率
        {1600,344,7,25,6},        //16Khz采样率
        {2205,429,38,25,1},        //22.05Khz采样率
        {3200,344,7,25,3},        //32Khz采样率
        {4410,429,38,25,0},        //44.1Khz采样率
        {4800,344,7,25,2},        //48Khz采样率
        {8820,271,38,25,1},        //88.2Khz采样率
        {9600,344,7,25,1},        //96Khz采样率
        {17640,271,38,25,0},        //176.4Khz采样率
        {19200,344,7,25,0},        //192Khz采样率
};

//开启SAI的DMA功能,HAL库没有提供此函数
//因此我们需要自己操作寄存器编写一个
void SAIA1_DMA_Enable(void)
{
        u32 tempreg=0;
        tempreg=SAI1_Block_A->CR1;          //先读出以前的设置                       
        tempreg|=1<<17;                                                    //使能DMA
        SAI1_Block_A->CR1=tempreg;                      //写入CR1寄存器中
}

//开启SAIB的DMA功能,HAL库没有提供此函数
//因此我们需要自己操作寄存器编写一个
void SAIB1_DMA_Enable(void)
{
        u32 tempreg=0;
        tempreg=SAI1_Block_B->CR1;          //先读出以前的设置                       
        tempreg|=1<<17;                                            //使能DMA
        SAI1_Block_B->CR1=tempreg;                    //写入CR1寄存器中
}

//SAIA TX DMA配置
//设置为双缓冲模式,并开启DMA传输完成中断
//buf0:M0AR地址.
//buf1:M1AR地址.
//num:每次传输数据量
//width:位宽(存储器和外设,同时设置),0,8位;1,16位;2,32位;
void SAIA1_RX_DMA_Init(u8* buf0,u8 *buf1,u16 num,u8 width)
{
    u32 memwidth=0,perwidth=0;      //外设和存储器位宽
    switch(width)
    {
        case 0:         //8位
            memwidth=DMA_MDATAALIGN_BYTE;
            perwidth=DMA_PDATAALIGN_BYTE;
            break;
        case 1:         //16位
            memwidth=DMA_MDATAALIGN_HALFWORD;
            perwidth=DMA_PDATAALIGN_HALFWORD;
            break;
        case 2:         //32位
            memwidth=DMA_MDATAALIGN_WORD;
            perwidth=DMA_PDATAALIGN_WORD;
            break;

    }
    __HAL_RCC_DMA2_CLK_ENABLE();                                    //使能DMA2时钟
                SAI1A_RXDMA_Handler.Init.Request= DMA_REQUEST_SAI1_A;   
                SAI1A_RXDMA_Handler.Instance=DMA2_Stream3;                       //DMA2数据流3               
    SAI1A_RXDMA_Handler.Init.Direction=DMA_PERIPH_TO_MEMORY;         //存储器到外设模式
    SAI1A_RXDMA_Handler.Init.PeriphInc=DMA_PINC_DISABLE;             //外设非增量模式
    SAI1A_RXDMA_Handler.Init.MemInc=DMA_MINC_ENABLE;                 //存储器增量模式
    SAI1A_RXDMA_Handler.Init.PeriphDataAlignment=DMA_MDATAALIGN_HALFWORD;//外设数据长度:16/32位
    SAI1A_RXDMA_Handler.Init.MemDataAlignment=DMA_PDATAALIGN_HALFWORD;//存储器数据长度:16/32位
    SAI1A_RXDMA_Handler.Init.Mode=DMA_CIRCULAR;                      //使用循环模式
                SAI1A_RXDMA_Handler.Init.Priority=DMA_PRIORITY_HIGH;             //高优先级
    SAI1A_RXDMA_Handler.Init.FIFOMode=DMA_FIFOMODE_DISABLE;          //不使用FIFO
    SAI1A_RXDMA_Handler.Init.FIFOThreshold= DMA_FIFO_THRESHOLD_FULL;   
                SAI1A_RXDMA_Handler.Init.MemBurst=DMA_MBURST_SINGLE;             //存储器单次突发传输
    SAI1A_RXDMA_Handler.Init.PeriphBurst=DMA_PBURST_SINGLE;          //外设突发单次传输
    __HAL_LINKDMA(&SAI1A_Handler,hdmarx,SAI1A_RXDMA_Handler);        //将DMA与SAI联系起来   
                HAL_DMA_DeInit(&SAI1A_RXDMA_Handler);                            //先清除以前的设置
    HAL_DMA_Init(&SAI1A_RXDMA_Handler);                                    //初始化DMA

    HAL_DMAEx_MultiBufferStart(&SAI1A_RXDMA_Handler,(u32)buf0,(u32)&SAI1_Block_A->DR,(u32)buf1,num);//开启双缓冲
    __HAL_DMA_DISABLE(&SAI1A_RXDMA_Handler);                         //先关闭DMA
    delay_us(10);                                                   //10us延时,防止-O2优化出问题        
    __HAL_DMA_ENABLE_IT(&SAI1A_RXDMA_Handler,DMA_IT_TC);             //开启传输完成中断
    HAL_NVIC_SetPriority(DMA2_Stream3_IRQn,1,0);                    //DMA中断优先级
    HAL_NVIC_EnableIRQ(DMA2_Stream3_IRQn);
}

//SAIA TX DMA配置
//设置为双缓冲模式,并开启DMA传输完成中断
//buf0:M0AR地址.
//buf1:M1AR地址.
//num:每次传输数据量
//width:位宽(存储器和外设,同时设置),0,8位;1,16位;2,32位;
void SAIB1_RX_DMA_Init(u8* buf0,u8 *buf1,u16 num,u8 width)
{
    u32 memwidth=0,perwidth=0;      //外设和存储器位宽
    switch(width)
    {
        case 0:         //8位
            memwidth=DMA_MDATAALIGN_BYTE;
            perwidth=DMA_PDATAALIGN_BYTE;
            break;
        case 1:         //16位
            memwidth=DMA_MDATAALIGN_HALFWORD;
            perwidth=DMA_PDATAALIGN_HALFWORD;
            break;
        case 2:         //32位
            memwidth=DMA_MDATAALIGN_WORD;
            perwidth=DMA_PDATAALIGN_WORD;
            break;

    }
    __HAL_RCC_DMA2_CLK_ENABLE();                                    //使能DMA2时钟
    __HAL_LINKDMA(&SAI1B_Handler,hdmarx,SAI1B_RXDMA_Handler);        //将DMA与SAI联系起来
    SAI1A_RXDMA_Handler.Init.Request= DMA_REQUEST_SAI1_B;  
                SAI1B_RXDMA_Handler.Instance=DMA2_Stream5;                       //DMA2数据流5                     
    SAI1B_RXDMA_Handler.Init.Direction=DMA_PERIPH_TO_MEMORY;         //外设到存储器模式
    SAI1B_RXDMA_Handler.Init.PeriphInc=DMA_PINC_DISABLE;             //外设非增量模式
    SAI1B_RXDMA_Handler.Init.MemInc=DMA_MINC_ENABLE;                 //存储器增量模式
    SAI1B_RXDMA_Handler.Init.PeriphDataAlignment=DMA_MDATAALIGN_HALFWORD;//外设数据长度:16/32位
    SAI1B_RXDMA_Handler.Init.MemDataAlignment=DMA_PDATAALIGN_HALFWORD; //存储器数据长度:16/32位
    SAI1B_RXDMA_Handler.Init.Mode=DMA_CIRCULAR;                      //使用循环模式
    SAI1B_RXDMA_Handler.Init.Priority=DMA_PRIORITY_MEDIUM;           //中等优先级
    SAI1B_RXDMA_Handler.Init.FIFOMode=DMA_FIFOMODE_DISABLE;          //不使用FIFO
    SAI1B_RXDMA_Handler.Init.MemBurst=DMA_MBURST_SINGLE;             //存储器单次突发传输
    SAI1B_RXDMA_Handler.Init.PeriphBurst=DMA_PBURST_SINGLE;          //外设突发单次传输
    HAL_DMA_DeInit(&SAI1B_RXDMA_Handler);                            //先清除以前的设置
    HAL_DMA_Init(&SAI1B_RXDMA_Handler);                                    //初始化DMA

    HAL_DMAEx_MultiBufferStart(&SAI1B_RXDMA_Handler,(u32)&SAI1_Block_B->DR,(u32)buf0,(u32)buf1,num);//开启双缓冲
    __HAL_DMA_DISABLE(&SAI1B_RXDMA_Handler);                         //先关闭接收DMA
    delay_us(10);                                                   //10us延时,防止-O2优化出问题
    __HAL_DMA_CLEAR_FLAG(&SAI1B_RXDMA_Handler,DMA_FLAG_TCIF1_5);     //清除DMA传输完成中断标志位
    __HAL_DMA_ENABLE_IT(&SAI1B_RXDMA_Handler,DMA_IT_TC);             //开启传输完成中断
    HAL_NVIC_SetPriority(DMA2_Stream5_IRQn,1,1);                    //DMA中断优先级
    HAL_NVIC_EnableIRQ(DMA2_Stream5_IRQn);
}


//SAI DMA回调函数指针
void (*sai1a_rx_callback)(void);        //TX回调函数
void (*sai1b_rx_callback)(void);        //RX回调函数

//DMA2_Stream3中断服务函数
void DMA2_Stream3_IRQHandler(void)
{   
        if(__HAL_DMA_GET_FLAG(&SAI1A_RXDMA_Handler,DMA_FLAG_TCIF3_7)!=RESET) //DMA传输完成
        {
                        __HAL_DMA_CLEAR_FLAG(&SAI1A_RXDMA_Handler,DMA_FLAG_TCIF3_7);     //清除DMA传输完成中断标志位
                        if(sai1a_rx_callback!=NULL)sai1a_rx_callback();        //执行回调函数,读取数据等操作在这里面处理  
        }  
}

//DMA2_Stream5中断服务函数
void DMA2_Stream5_IRQHandler(void)
{  
        if(__HAL_DMA_GET_FLAG(&SAI1B_RXDMA_Handler,DMA_FLAG_TCIF1_5)!=RESET) //DMA传输完成
        {
                        __HAL_DMA_CLEAR_FLAG(&SAI1B_RXDMA_Handler,DMA_FLAG_TCIF1_5);     //清除DMA传输完成中断标志位
                        if(sai1b_rx_callback!=NULL)sai1b_rx_callback();        //执行回调函数,读取数据等操作在这里面处理  
        }        
}

//SAI开始播放
void SAIA1_Rec_Start(void)
{          
    __HAL_DMA_ENABLE(&SAI1A_RXDMA_Handler);//开启DMA TX传输                         
}
//关闭I2S播放
void SAIA1_Rec_Stop(void)
{   
    __HAL_DMA_DISABLE(&SAI1A_RXDMA_Handler);  //结束播放                    
}
//SAI开始录音
void SAIB1_Rec_Start(void)
{
    __HAL_DMA_ENABLE(&SAI1B_RXDMA_Handler);//开启DMA RX传输                     
}
//关闭SAI录音
void SAIB1_Rec_Stop(void)
{   
    __HAL_DMA_DISABLE(&SAI1B_RXDMA_Handler);//结束录音   
}


友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
该问题目前已经被作者或者管理员关闭, 无法添加新回复
4条回答
正点原子
1楼-- · 2019-07-20 08:03
 精彩回答 2  元偷偷看……
lzandyc
2楼-- · 2019-07-20 12:58
谢谢,已经搞定了
mhsdsa
3楼-- · 2019-07-20 17:08
感谢分享,谢谢大神
ya5201314lin
4楼-- · 2019-07-20 18:50
 精彩回答 2  元偷偷看……

一周热门 更多>