OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 349|回复: 0

stm32h750i2s仿照f407一直不成功,希望高手指点指点

[复制链接]

3

主题

14

帖子

0

精华

初级会员

Rank: 2

积分
127
金钱
127
注册时间
2016-10-28
在线时间
33 小时
发表于 2024-8-9 22:54:51 | 显示全部楼层 |阅读模式
1金钱
想用stm32h750的i2s功能,按照stm32f407的例程修改,一直没成功,希望高手指点指点
//I2S2初始化
//I2S_Standard:I2S标准,可以设置:I2S_STANDARD_PHILIPS/I2S_STANDARD_MSB/
//                                       I2S_STANDARD_LSB/I2S_STANDARD_PCM_SHORT/I2S_STANDARD_PCM_LONG
//I2S_Mode:I2S工作模式,可以设置:I2S_MODE_SLAVE_TX/I2S_MODE_SLAVE_RX/I2S_MODE_MASTER_TX/I2S_MODE_MASTER_RX
//I2S_Clock_Polarity:时钟电平,可以设置为:I2S_CPOL_LOW/I2S_CPOL_HIGH
//I2S_DataFormat:数据长度,可以设置:I2S_DATAFORMAT_16B/I2S_DATAFORMAT_16B_EXTENDED/I2S_DATAFORMAT_24B/I2S_DATAFORMAT_32B
void I2S1_Init(u32 I2S_Standard,u32 I2S_Mode,u32 I2S_Clock_Polarity,u32 I2S_DataFormat)
{
        I2S1_Handler.Instance=SPI1;
        I2S1_Handler.Init.Mode=I2S_Mode;                                        //IIS模式
        I2S1_Handler.Init.Standard=I2S_Standard;                        //IIS标准
        I2S1_Handler.Init.DataFormat=I2S_DataFormat;                //IIS数据长度
        I2S1_Handler.Init.MCLKOutput=I2S_MCLKOUTPUT_ENABLE;        //主时钟输出使能
        I2S1_Handler.Init.AudioFreq=I2S_AUDIOFREQ_DEFAULT;        //IIS频率设置
        I2S1_Handler.Init.CPOL=I2S_Clock_Polarity;                        //空闲状态时钟电平
        I2S1_Handler.Init.FirstBit = I2S_FIRSTBIT_MSB;
  I2S1_Handler.Init.WSInversion = I2S_WS_INVERSION_DISABLE;
  I2S1_Handler.Init.Data24BitAlignment = I2S_DATA_24BIT_ALIGNMENT_RIGHT;
  I2S1_Handler.Init.MasterKeepIOState = I2S_MASTER_KEEP_IO_STATE_DISABLE;
       
        HAL_I2S_Init(&I2S1_Handler);
        if(HAL_I2S_Init(&I2S1_Handler) != HAL_OK)
  {
      printf("I2S初始化失败\r\n");
  }                                                       
        SPI1->CFG1|=1<<15;                                                                                                //SPI1 TX DMA请求使能.
        __HAL_I2S_ENABLE(&I2S1_Handler);                                        //使能I2S2       
}

//I2S底层驱动,时钟使能,引脚配置,DMA配置
//此函数会被HAL_I2S_Init()调用
//hi2s:I2S句柄
void HAL_I2S_MspInit(I2S_HandleTypeDef *hi2s)
{
        GPIO_InitTypeDef GPIO_Initure;
  RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};
               
        PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_SPI1;
        PeriphClkInitStruct.Spi123ClockSelection = RCC_SPI123CLKSOURCE_PLL;
        if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
        {
                printf("PeriphClkInitStruct失败\r\n");
        }
        __HAL_RCC_SPI1_CLK_ENABLE();                        //使能SPI2/I2S2时钟
        __HAL_RCC_GPIOA_CLK_ENABLE();               //使能GPIOB时钟
        __HAL_RCC_GPIOC_CLK_ENABLE();               //使能GPIOC时钟
       
        //初始化PB12/13
        GPIO_Initure.Pin=GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7;  
        GPIO_Initure.Mode=GPIO_MODE_AF_PP;          //推挽复用
        GPIO_Initure.Pull=GPIO_NOPULL;              //
        GPIO_Initure.Speed=GPIO_SPEED_HIGH;         //高速
        GPIO_Initure.Alternate=GPIO_AF5_SPI1;       //复用为SPI/I2S  
        HAL_GPIO_Init(GPIOA,&GPIO_Initure);         //初始化
       
        //初始化PC2/PC3/PC6
        GPIO_Initure.Pin=GPIO_PIN_4;
        HAL_GPIO_Init(GPIOC,&GPIO_Initure);         //初始化       
}

//采样率计算公式:Fs=I2SxCLK/[256*(2*I2SDIV+ODD)]
//I2SxCLK=(HSE/pllm)*PLLI2SN/PLLI2SR
//一般HSE=8Mhz
//pllm:在Sys_Clock_Set设置的时候确定,一般是8
//PLLI2SN:一般是192~432
//PLLI2SR:2~7
//I2SDIV:2~255
//ODD:0/1
//I2S分频系数表@pllm=8,HSE=8Mhz,即vco输入频率为1Mhz
//表格式:采样率/10,PLLI2SN,PLLI2SR,I2SDIV,ODD
const u16 I2S_PSC_TBL[][5]=
{
        {800 ,256,5,12,1},                //8Khz采样率
        {1102,429,4,19,0},                //11.025Khz采样率
        {1600,213,2,13,0},                //16Khz采样率
        {2205,429,4, 9,1},                //22.05Khz采样率
        {3200,213,2, 6,1},                //32Khz采样率
        {4410,271,2, 6,0},                //44.1Khz采样率
        {4800,258,3, 2,1},                //48Khz采样率
        {8820,316,2, 3,1},                //88.2Khz采样率
        {9600,344,2, 3,1},          //96Khz采样率
        {17640,361,2,2,0},          //176.4Khz采样率
        {19200,393,2,2,0},          //192Khz采样率
};

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

//设置SAIA的采样率(@MCKEN)
//samplerate:采样率,单位:Hz
//返回值:0,设置成功;1,无法设置.
u8 I2S1_SampleRate_Set(u32 samplerate)
{   
        u8 i=0;
        u32 tempreg=0;

        RCC_PeriphCLKInitTypeDef RCCI2S1_ClkInitSture;  
        for(i=0;i<(sizeof(I2S_PSC_TBL)/10);i++)//看看改采样率是否可以支持
        {
                if((samplerate/10)==I2S_PSC_TBL[i][0])break;
        }
        if(i==(sizeof(I2S_PSC_TBL)/10))return 1;//搜遍了也找不到
//        RCCI2S1_ClkInitSture.PeriphClockSelection=RCC_PERIPHCLK_SPI1;        //外设时钟源选择
//        RCCI2S1_ClkInitSture.PLL2.PLL2N=(u32)I2S_PSC_TBL[i][1];            //设置PLLI2SN
//        RCCI2S1_ClkInitSture.PLL2.PLL2R=(u32)I2S_PSC_TBL[i][2];            //设置PLLI2SR
//        HAL_RCCEx_PeriphCLKConfig(&RCCI2S1_ClkInitSture);                     //设置时钟
//        RCC->CR|=1<<26;                                        //开启I2S时钟
//        while((RCC->CR&1<<27)==0);                //等待I2S时钟开启成功.
//        tempreg=I2S_PSC_TBL[i][3]<<16;        //设置I2SDIV
//        tempreg|=I2S_PSC_TBL[i][4]<<24;        //设置ODD位
//        tempreg|=1<<25;                                        //使能MCKOE位,输出MCK
//        //SPI1->I2SPR=tempreg;                        //设置I2SPR寄存器
//        SPI1->I2SCFGR=tempreg;
//        return 0;
        if((samplerate == I2S_AUDIOFREQ_11K) || (samplerate == I2S_AUDIOFREQ_22K) || (samplerate == I2S_AUDIOFREQ_44K))
  {
                RCCI2S1_ClkInitSture.PeriphClockSelection = RCC_PERIPHCLK_SPI1;
                RCCI2S1_ClkInitSture.Spi123ClockSelection = RCC_SPI123CLKSOURCE_PLL2;
                RCCI2S1_ClkInitSture.PLL2.PLL2M = 25;
                RCCI2S1_ClkInitSture.PLL2.PLL2N = 400;
                RCCI2S1_ClkInitSture.PLL2.PLL2P = 10;
                RCCI2S1_ClkInitSture.PLL2.PLL2R = 2;
                RCCI2S1_ClkInitSture.PLL2.PLL2Q = 2;
                RCCI2S1_ClkInitSture.PLL2.PLL2RGE = RCC_PLL2VCIRANGE_0;
                RCCI2S1_ClkInitSture.PLL2.PLL2VCOSEL = RCC_PLL2VCOWIDE;
                RCCI2S1_ClkInitSture.PLL2.PLL2FRACN = 0;
                if (HAL_RCCEx_PeriphCLKConfig(&RCCI2S1_ClkInitSture) != HAL_OK)
                {
                        while(1);
                }
        }
//        RCC->CR|=1<<26;                                        //开启I2S时钟
//        while((RCC->CR&1<<27)==0);                //等待I2S时钟开启成功.
        tempreg=I2S_PSC_TBL[i][3]<<16;        //设置I2SDIV
        tempreg|=I2S_PSC_TBL[i][4]<<24;        //设置ODD位
        tempreg|=1<<25;                                        //使能MCKOE位,输出MCK       
        SPI1->I2SCFGR=tempreg;//设置I2SCFGR寄存器
        //----------------------------------------------------------------
        __HAL_I2S_DISABLE(&I2S1_Handler);                          //关闭SAI
        I2S1_Handler.Init.AudioFreq=samplerate;               //设置播放频率  
        HAL_I2S_Init(&I2S1_Handler);                               //初始化SAI
        I2S_DMA_Enable();                                          //开启SAI的DMA功能
        __HAL_I2S_ENABLE(&I2S1_Handler);                           //开启SAI
        return 0;
}
//I2S2 TX DMA配置
//设置为双缓冲模式,并开启DMA传输完成中断
//buf0:M0AR地址.
//buf1:M1AR地址.
//num:每次传输数据量
void I2S1_TX_DMA_Init(u8* buf0,u8 *buf1,u16 num)
{  
    __HAL_RCC_DMA1_CLK_ENABLE();                                                    //使能DMA1时钟
    __HAL_LINKDMA(&I2S1_Handler,hdmatx,I2S1_TXDMA_Handler);                         //将DMA与I2S联系起来
       
    I2S1_TXDMA_Handler.Instance=DMA1_Stream5;                                       //DMA1数据流4                     
    I2S1_TXDMA_Handler.Init.Request=DMA_REQUEST_SPI1_TX;                                 
    I2S1_TXDMA_Handler.Init.Direction=DMA_MEMORY_TO_PERIPH;                         //存储器到外设模式
    I2S1_TXDMA_Handler.Init.PeriphInc=DMA_PINC_DISABLE;                             //外设非增量模式
    I2S1_TXDMA_Handler.Init.MemInc=DMA_MINC_ENABLE;                                 //存储器增量模式
    I2S1_TXDMA_Handler.Init.PeriphDataAlignment=DMA_PDATAALIGN_HALFWORD;           //外设数据长度:16位
    I2S1_TXDMA_Handler.Init.MemDataAlignment=DMA_MDATAALIGN_HALFWORD;            //存储器数据长度:16位
    I2S1_TXDMA_Handler.Init.Mode=DMA_CIRCULAR;                                      //使用循环模式
    I2S1_TXDMA_Handler.Init.Priority=DMA_PRIORITY_HIGH;                             //高优先级
    I2S1_TXDMA_Handler.Init.FIFOMode=DMA_FIFOMODE_DISABLE;                          //不使用FIFO
    I2S1_TXDMA_Handler.Init.MemBurst=DMA_MBURST_SINGLE;                             //存储器单次突发传输
    I2S1_TXDMA_Handler.Init.PeriphBurst=DMA_PBURST_SINGLE;                          //外设突发单次传输
    HAL_DMA_DeInit(&I2S1_TXDMA_Handler);                                            //先清除以前的设置
    HAL_DMA_Init(&I2S1_TXDMA_Handler);                                                    //初始化DMA
               
    HAL_DMAEx_MultiBufferStart(&I2S1_TXDMA_Handler,(u32)buf0,(u32)&SPI1->TXDR,(u32)buf1,num);//开启双缓冲
    __HAL_DMA_DISABLE(&I2S1_TXDMA_Handler);                                         //先关闭DMA
    delay_us(10);                                                                   //10us延时,防止-O2优化出问题        
    __HAL_DMA_ENABLE_IT(&I2S1_TXDMA_Handler,DMA_IT_TC);                             //开启传输完成中断
    __HAL_DMA_CLEAR_FLAG(&I2S1_TXDMA_Handler,DMA_FLAG_TCIF1_5);                     //清除DMA传输完成中断标志位

                HAL_NVIC_SetPriority(DMA1_Stream5_IRQn,10,0);                                    //DMA中断优先级
    HAL_NVIC_EnableIRQ(DMA1_Stream5_IRQn);
               
}
//I2S DMA回调函数指针
void (*i2s_tx_callback)(void);        //TX回调函数

void DMA1_Stream5_IRQHandler(void)
{   
    if(__HAL_DMA_GET_FLAG(&I2S1_TXDMA_Handler,DMA_FLAG_TCIF1_5)!=RESET) //DMA传输完成
    {
      __HAL_DMA_CLEAR_FLAG(&I2S1_TXDMA_Handler,DMA_FLAG_TCIF1_5);     //清除DMA传输完成中断标志位
        if(i2s_tx_callback!=NULL)i2s_tx_callback();        //执行回调函数,读取数据等操作在这里面处理  
    }                
}  
//I2S开始播放
void I2S_Play_Start(void)
{          
        __HAL_DMA_ENABLE(&I2S1_TXDMA_Handler);//开启DMA TX
}
//关闭I2S播放
void I2S_Play_Stop(void)
{
        __HAL_DMA_DISABLE(&I2S1_TXDMA_Handler);//结束播放;                 
}

正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

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

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2024-11-22 11:14

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

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