基于HAL库的I2S配置

2019-07-20 10:16发布

本帖最后由 襟铭心缘 于 2016-9-19 15:12 编辑

最近参考原子哥的SAI例子和标准库的例子成功用HAL库配置除了I2S。
下面是代码:
[mw_shl_code=c,true]#include "i2s.h"
#include "delay.h"
I2S_HandleTypeDef I2S_Handle;
DMA_HandleTypeDef I2S_TxDMA_Handle;

//开启I2S2的DMA功能,HAL库没有提供此函数
//因此我们需要自己操作寄存器编写一个
void I2S2_DMA_Enable(void)
{
  u32 tempreg=0;
  tempreg=SPI2->CR2;          //先读出以前的设置                        
        tempreg|=SPI_CR2_TXDMAEN;                                            //使能DMA
        SPI2->CR2=tempreg;                    //写入CR1寄存器中
}
void HAL_I2S_MspInit(I2S_HandleTypeDef *hi2s)
{
  GPIO_InitTypeDef GPIO_Initure;
        __HAL_RCC_SPI2_CLK_ENABLE();
        __HAL_RCC_GPIOB_CLK_ENABLE();
        __HAL_RCC_GPIOC_CLK_ENABLE();
        __HAL_RCC_GPIOD_CLK_ENABLE();
        __HAL_RCC_GPIOI_CLK_ENABLE();
        
        GPIO_Initure.Mode=GPIO_MODE_AF_PP;
        GPIO_Initure.Pull=GPIO_PULLUP;
        GPIO_Initure.Speed=GPIO_SPEED_HIGH;
        GPIO_Initure.Pin=GPIO_PIN_12;
        GPIO_Initure.Alternate=GPIO_AF5_SPI2;
        HAL_GPIO_Init(GPIOB,&GPIO_Initure);
        
        GPIO_Initure.Pin=GPIO_PIN_2;
        GPIO_Initure.Alternate=GPIO_AF6_I2S2ext;
        HAL_GPIO_Init(GPIOC,&GPIO_Initure);
        
        GPIO_Initure.Pin=GPIO_PIN_6;
        GPIO_Initure.Alternate=GPIO_AF5_SPI2;
        HAL_GPIO_Init(GPIOC,&GPIO_Initure);
        
        GPIO_Initure.Pin=GPIO_PIN_3;
        GPIO_Initure.Alternate=GPIO_AF5_SPI2;
        HAL_GPIO_Init(GPIOD,&GPIO_Initure);
        
        GPIO_Initure.Pin=GPIO_PIN_3;
        GPIO_Initure.Alternate=GPIO_AF5_SPI2;
        HAL_GPIO_Init(GPIOI,&GPIO_Initure);
        
        
}
//参数I2S_Mode:  @ref SPI_I2S_Mode  I2S_MODE_SLAVE_TX:从机发送;I2S_MODE_SLAVE_RX:从机接收;I2S_MODE_MASTER_TX:主机发送;I2S_MODE_MASTER_RX:主机接收;
//参数I2S_DataFormat: @ref SPI_I2S_Data_Format :数据长度,I2S_DATAFORMAT_16B,16位标准;I2S_DATAFORMAT_16B_EXTENDED,16位扩展(frame=32bit);I2S_DATAFORMAT_24B,24位;I2S_DATAFORMAT_32B,32位.

void I2S2_Init(u16 I2S_Mode,u16 I2S_DataFormat)
{
  
        I2S_Handle.Instance=SPI2;
        __HAL_I2S_DISABLE(&I2S_Handle);
        I2S_Handle.Init.Mode=I2S_Mode;
        I2S_Handle.Init.Standard=I2S_STANDARD_PHILIPS;
        I2S_Handle.Init.DataFormat=I2S_DataFormat;
        I2S_Handle.Init.MCLKOutput=I2S_MCLKOUTPUT_ENABLE;        
        I2S_Handle.Init.AudioFreq=I2S_AUDIOFREQ_DEFAULT;
        I2S_Handle.Init.CPOL=I2S_CPOL_LOW;
        I2S_Handle.Init.ClockSource=I2S_CLOCK_PLL;
        if(HAL_I2S_GetState(&I2S_Handle) == HAL_I2S_STATE_RESET)
  {
    HAL_I2S_MspInit(&I2S_Handle);               
  }
        HAL_I2S_Init(&I2S_Handle);
        __HAL_I2S_ENABLE(&I2S_Handle);
        I2S2_DMA_Enable();
        
}

//采样率计算公式:Fs=I2SxCLK/[256*(2*I2SDIV+ODD)]
//I2SxCLK=(HSE/pllm)*PLLI2SN/PLLI2SR
//一般HSE=25Mhz
//pllm:在Sys_Clock_Set设置的时候确定,一般是8
//PLLI2SN:一般是192~432
//PLLI2SR:2~7
//I2SDIV:2~255
//ODD:0/1
//I2S分频系数表@pllm=25,HSE=25Mhz,即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, 3,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采样率
};  

//设置IIS的采样率(@MCKEN)
//samplerate:采样率,单位:Hz
//返回值:0,设置成功;1,无法设置.
u8 I2S2_SampleRate_Set(u32 samplerate)
{
        u8 i=0;
        u32 tempreg=0;  
        samplerate/=10;//缩小10倍   
        
        for(i=0;i<(sizeof(I2S_PSC_TBL)/10);i++)//看看改采样率是否可以支持
        {
                if(samplerate==I2S_PSC_TBL[0])break;
        }
        __HAL_RCC_PLLI2S_DISABLE();
  if(i==(sizeof(I2S_PSC_TBL)/10))return 1;//搜遍了也找不到
        //设置I2SxCLK的频率(x=2)  设置PLLI2SN PLLI2SR
  RCC->PLLI2SCFGR = ((u32)I2S_PSC_TBL[1] << 6)  | ((u32)I2S_PSC_TBL[2] << 28);
        RCC->CR|=1<<26;                                        //开启I2S时钟
        while((RCC->CR&1<<27)==0);                //等待I2S时钟开启成功.
        tempreg=I2S_PSC_TBL[3]<<0;        //设置I2SDIV
        tempreg|=I2S_PSC_TBL[4]<<8;        //设置ODD位
        tempreg|=1<<9;                                        //使能MCKOE位,输出MCK
        SPI2->I2SPR=tempreg;                        //设置I2SPR寄存器
        return 0;
}
//I2S2 TX DMA配置
//设置为双缓冲模式,并开启DMA传输完成中断
//buf0:M0AR地址.
//buf1:M1AR地址.
//num:每次传输数据量

void I2S2_TX_DMA_Init(u8* buf0,u8 *buf1,u16 num)
{
        __HAL_RCC_DMA1_CLK_ENABLE();
        __HAL_LINKDMA(&I2S_Handle,hdmatx,I2S_TxDMA_Handle);
        
        I2S_TxDMA_Handle.Instance=DMA1_Stream4;
        I2S_TxDMA_Handle.Init.Channel=DMA_CHANNEL_0;
        I2S_TxDMA_Handle.Init.Direction=DMA_MEMORY_TO_PERIPH;
        I2S_TxDMA_Handle.Init.FIFOMode=DMA_FIFOMODE_DISABLE;
        I2S_TxDMA_Handle.Init.FIFOThreshold=DMA_FIFO_THRESHOLD_1QUARTERFULL;
        I2S_TxDMA_Handle.Init.MemBurst=DMA_MBURST_SINGLE;
        I2S_TxDMA_Handle.Init.MemDataAlignment=DMA_MDATAALIGN_HALFWORD;
        I2S_TxDMA_Handle.Init.MemInc=DMA_MINC_ENABLE;
        I2S_TxDMA_Handle.Init.Mode=DMA_CIRCULAR;
        I2S_TxDMA_Handle.Init.PeriphBurst=DMA_PBURST_SINGLE;
        I2S_TxDMA_Handle.Init.PeriphDataAlignment=DMA_PDATAALIGN_HALFWORD;
        I2S_TxDMA_Handle.Init.PeriphInc=DMA_PINC_DISABLE;
        I2S_TxDMA_Handle.Init.Priority=DMA_PRIORITY_HIGH;
        HAL_DMA_DeInit(&I2S_TxDMA_Handle);                            //先清除以前的设置
  HAL_DMA_Init(&I2S_TxDMA_Handle);                                    //初始化DMA
        HAL_DMAEx_MultiBufferStart(&I2S_TxDMA_Handle,(u32)buf0,(u32)&SPI2->DR,(u32)buf1,num);//开启双缓冲
  __HAL_DMA_DISABLE(&I2S_TxDMA_Handle);                         //先关闭DMA
        
        __HAL_DMA_ENABLE_IT(&I2S_TxDMA_Handle,DMA_IT_TC);             //开启传输完成中断
  __HAL_DMA_CLEAR_FLAG(&I2S_TxDMA_Handle,DMA_FLAG_TCIF0_4);     //清除DMA传输完成中断标志位
  HAL_NVIC_SetPriority(DMA1_Stream4_IRQn,0,0);                    //DMA中断优先级
  HAL_NVIC_EnableIRQ(DMA1_Stream4_IRQn);
        
}
//I2S DMA回调函数指针
void (*i2s_tx_callback)(void);        //TX回调函数
//DMA1_Stream4中断服务函数
void DMA1_Stream4_IRQHandler(void)
{
/*        __HAL_DMA_GET_FLAG(xxx)!=RESET时可以正常工作
                __HAL_DMA_GET_FLAG(xxx)==SET时无法正常工作
                库文件stm32f4xx_hal_dma.h中说明了FLAG的状态是SET or RESET
                The state of FLAG (SET or RESET)
                很奇怪!!!        
*/
        if(__HAL_DMA_GET_FLAG(&I2S_TxDMA_Handle,DMA_FLAG_TCIF0_4)!=RESET)////DMA1_Stream4,传输完成标志
        {
                __HAL_DMA_CLEAR_FLAG(&I2S_TxDMA_Handle,DMA_FLAG_TCIF0_4);     //清除DMA传输完成中断标志位
    i2s_tx_callback();        //执行回调函数,读取数据等操作在这里面处理
        }                                                                                            
}  
//I2S开始播放
void I2S_Play_Start(void)
{            
        __HAL_DMA_ENABLE(&I2S_TxDMA_Handle);//开启DMA TX传输,开始播放                 
}
//关闭I2S播放
void I2S_Play_Stop(void)
{   
        __HAL_DMA_DISABLE(&I2S_TxDMA_Handle);//关闭DMA,结束播放                  
}        
        




[/mw_shl_code]

友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
该问题目前已经被作者或者管理员关闭, 无法添加新回复
4条回答
zhang00956
1楼-- · 2019-07-20 16:11
怎么没人顶啊,顶一下,用HAL库配置出I2S
lishi1013
2楼-- · 2019-07-20 17:01
你这个代码没问题吧,我用了声音不太对
czdspeed
3楼-- · 2019-07-20 20:57
 精彩回答 2  元偷偷看……
hpdell
4楼-- · 2019-07-21 00:34
czdspeed 发表于 2017-6-2 11:26
楼主我也用F429的板子按照你的板子进行了配置,但是没有时钟输出出来。按照我的理解,只要把GPIO和I2S配置 ...

你好,请问你的搞定了没有啊 ?

一周热门 更多>