新手入门
- 积分
- 3
- 金钱
- 3
- 注册时间
- 2021-2-27
- 在线时间
- 0 小时
|
发表于 2021-6-20 23:45:33
|
显示全部楼层
修正版:
#include "i2s.h"
#include "delay.h"
I2S_HandleTypeDef I2S_Handle;
DMA_HandleTypeDef I2S_TxDMA_Handle; //DMA发送句柄
DMA_HandleTypeDef I2S_RxDMA_Handler; //DMA接收句柄
//开启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_GPIOA_CLK_ENABLE(); //使能GPIOX时钟
__HAL_RCC_GPIOC_CLK_ENABLE(); //使能GPIOX时钟
GPIO_Initure.Mode = GPIO_MODE_AF_PP;
GPIO_Initure.Pull = GPIO_PULLUP;
GPIO_Initure.Speed = GPIO_SPEED_HIGH;
GPIO_Initure.Pin = GPIO_PIN_15;
GPIO_Initure.Alternate = GPIO_AF5_SPI2;
HAL_GPIO_Init(GPIOA, &GPIO_Initure);
GPIO_Initure.Pin = GPIO_PIN_7 | GPIO_PIN_10 | GPIO_PIN_12;
HAL_GPIO_Init(GPIOC, &GPIO_Initure);
GPIO_Initure.Pin = GPIO_PIN_11;
GPIO_Initure.Alternate = GPIO_AF6_I2S2ext;
HAL_GPIO_Init(GPIOC, &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) {
void I2S2_Init(u16 I2S_Standard, u16 I2S_Mode, u16 I2S_Clock_Polarity, u16 I2S_DataFormat) {
I2S_Handle.Instance = SPI2;
__HAL_I2S_DISABLE(&I2S_Handle);
I2S_Handle.Init.Mode = I2S_Mode;
I2S_Handle.Init.Standard = I2S_Standard;
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_Clock_Polarity;
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(); //DMA1时钟使能
__HAL_LINKDMA(&I2S_Handle, hdmatx, I2S_TxDMA_Handle);
I2S_TxDMA_Handle.Instance = DMA1_Stream4;
I2S_TxDMA_Handle.Init.Channel = DMA_CHANNEL_0; //通道0 SPI2_TX通道
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,结束播放
}
//I2S开始录音
void I2S_Rec_Start(void) {
__HAL_DMA_ENABLE(&I2S_RxDMA_Handler); //开启DMA RX传输
}
//关闭I2S录音
void I2S_Rec_Stop(void) {
__HAL_DMA_DISABLE(&I2S_RxDMA_Handler); //结束录音
}
|
|