本帖最后由 襟铭心缘 于 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]
你好,请问你的搞定了没有啊 ?
一周热门 更多>