当前位置:网站首页>STM32 SPI+WM8978语音回环
STM32 SPI+WM8978语音回环
2022-07-30 03:37:00 【Younique741】
STM32F3/F4系列芯片拥有全双工I2S接口可以与Codec通信,但是目前F1系列芯片或有些国产芯片没有全双工的I2S,要想使用Codec传输音频只能使用两个半双工合为一个全双工I2S,这不仅硬件上需要额外引脚,软件配置上也会少一个可使用的SPI接口。针对这一问题,我们可以使用SPI接口与Codec的“左对齐”接口模式进行通信,单片机作为从器件,Codec作为主器件。
/************WM8978配置**********/
uint8_t WM8978_Write_Reg(uint16_t nRegAddr,uint16_t nRegValue)
{
IIC_Start();
IIC_Send_Byte(WM8978_ADDR|0);
if(IIC_Wait_Ack())
return 1;
IIC_Send_Byte((nRegAddr<<1)|((nRegValue>>8)&0x01));
if(IIC_Wait_Ack())
return 2;
IIC_Send_Byte(nRegValue&0xFF);
if(IIC_Wait_Ack())
return 3;
IIC_Stop();
return 0;
}
void WM8978_Init(void)
{
IIC_Init();
WM8978_Write_Reg(0,0x0000);
WM8978_Write_Reg(1,0x013F);
WM8978_Write_Reg(2,0x01BF);
WM8978_Write_Reg(3,0x006F);
WM8978_Write_Reg(4,0x0010); //左对齐模式
WM8978_Write_Reg(6,0x01AD); //MCLK=12MHz
WM8978_Write_Reg(7,0x000A); //8K
WM8978_Write_Reg(10,0x0008); //ADC高通滤波器打开
WM8978_Write_Reg(14,0x0108); //DAC高通滤波器打开
WM8978_Write_Reg(15,0x01ED);
WM8978_Write_Reg(36,0x0018);
WM8978_Write_Reg(16,0x01ED);
WM8978_Write_Reg(43,0x0010);
WM8978_Write_Reg(44,0x0177);
WM8978_Write_Reg(45,0x0139);
WM8978_Write_Reg(46,0x0139);
WM8978_Write_Reg(47,0x0050);
WM8978_Write_Reg(48,0x0050);
WM8978_Write_Reg(49,0x0002);
WM8978_Write_Reg(52,0x012F);
WM8978_Write_Reg(53,0x012F);
WM8978_Write_Reg(54,0x0129);
WM8978_Write_Reg(55,0x0129);
}
SPI3_CS ---> WM8978_LRC
SPI3_SCK ---> WM8978_BCLK
SPI3_MISO ---> WM8978_ADC
SPI3_MOSI ---> WM8978_DAC
PA8--->WM8978_MCLK
void PWM_Init(uint16_t psc,uint16_t arr)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE); //使能定时器2时钟
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE); //使能GPIO外设时钟
GPIO_PinAFConfig(GPIOA,GPIO_PinSource8,GPIO_AF_6);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType=GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd=GPIO_PuPd_DOWN;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
TIM_TimeBaseStructure.TIM_Period = arr-1; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
TIM_TimeBaseStructure.TIM_Prescaler =psc-1; //设置用来作为TIMx时钟频率除数的预分频值
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
//初始化PWM模式
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式1
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Reset;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高
TIM_OCInitStructure.TIM_Pulse=arr>>1;
TIM_OC1Init(TIM1, &TIM_OCInitStructure);
TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);
TIM_ARRPreloadConfig(TIM1,ENABLE);
TIM_CtrlPWMOutputs(TIM1, ENABLE);
TIM_Cmd(TIM1, ENABLE);
}
int16_t *pADC,*pDAC;
int16_t SPI3_RxBuf[320],SPI3_TxBuf[320];
__IO uint32_t SPI3_Flag=0;
void SPI3_Init(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
DMA_InitTypeDef DMA_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
SPI_InitTypeDef SPI_InitStructure;
pADC=&SPI3_RxBuf[160];
pDAC=&SPI3_TxBuf[160];
NVIC_InitStructure.NVIC_IRQChannel=DMA2_Channel1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=2;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_Init(&NVIC_InitStructure);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA2,ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA,ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB,ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI3,ENABLE);
GPIO_PinAFConfig(GPIOA,GPIO_PinSource15,GPIO_AF_6);
GPIO_PinAFConfig(GPIOB,GPIO_PinSource3,GPIO_AF_6);
GPIO_PinAFConfig(GPIOB,GPIO_PinSource4,GPIO_AF_6);
GPIO_PinAFConfig(GPIOB,GPIO_PinSource5,GPIO_AF_6);
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_15;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType=GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd=GPIO_PuPd_UP;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5;
GPIO_Init(GPIOB,&GPIO_InitStructure);
DMA_InitStructure.DMA_PeripheralBaseAddr=(uint32_t)&SPI3->DR; //外设地址
DMA_InitStructure.DMA_MemoryBaseAddr=(uint32_t)SPI3_RxBuf; //DMA1内存地址
DMA_InitStructure.DMA_DIR=DMA_DIR_PeripheralSRC; //数据传输方向:外设到内存
DMA_InitStructure.DMA_BufferSize=320; //DMA1缓存大小
DMA_InitStructure.DMA_PeripheralInc=DMA_PeripheralInc_Disable; //外设地址不变
DMA_InitStructure.DMA_MemoryInc=DMA_MemoryInc_Enable; //内存地址递增
DMA_InitStructure.DMA_PeripheralDataSize=DMA_PeripheralDataSize_HalfWord; //数据宽度16位
DMA_InitStructure.DMA_MemoryDataSize=DMA_MemoryDataSize_HalfWord; //数据宽度16位
DMA_InitStructure.DMA_Mode=DMA_Mode_Circular; //DMA1工作循环模式
DMA_InitStructure.DMA_Priority=DMA_Priority_Low; //DMA1通道4中优先级低
DMA_InitStructure.DMA_M2M=DMA_M2M_Disable; //DMA1非内存到内存传输
DMA_Init(DMA2_Channel1,&DMA_InitStructure);
DMA_InitStructure.DMA_PeripheralBaseAddr=(uint32_t)&SPI3->DR; //外设地址
DMA_InitStructure.DMA_MemoryBaseAddr=(uint32_t)SPI3_TxBuf; //DMA1内存地址
DMA_InitStructure.DMA_DIR=DMA_DIR_PeripheralDST; //数据传输方向:内存到外设
DMA_InitStructure.DMA_BufferSize=320; //DMA1缓存大小
DMA_InitStructure.DMA_PeripheralInc=DMA_PeripheralInc_Disable; //外设地址不变
DMA_InitStructure.DMA_MemoryInc=DMA_MemoryInc_Enable; //内存地址递增
DMA_InitStructure.DMA_PeripheralDataSize=DMA_PeripheralDataSize_HalfWord; //数据宽度16位
DMA_InitStructure.DMA_MemoryDataSize=DMA_MemoryDataSize_HalfWord; //数据宽度16位
DMA_InitStructure.DMA_Mode=DMA_Mode_Circular; //DMA1工作循环模式
DMA_InitStructure.DMA_Priority=DMA_Priority_Low; //DMA1通道5中优先级低
DMA_InitStructure.DMA_M2M=DMA_M2M_Disable; //DMA1非内存到内存传输
DMA_Init(DMA2_Channel2,&DMA_InitStructure);
DMA_ITConfig(DMA2_Channel1,DMA2_IT_HT1,ENABLE);
DMA_ITConfig(DMA2_Channel1,DMA2_IT_TC1,ENABLE);
DMA_Cmd(DMA2_Channel1,ENABLE);
DMA_Cmd(DMA2_Channel2,ENABLE);
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //SPI设置为双线双向全双工
SPI_InitStructure.SPI_Mode = SPI_Mode_Slave; //SPI从机
SPI_InitStructure.SPI_DataSize = SPI_DataSize_16b; //发送接收16位帧结构
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; //时钟悬空低
SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; //数据捕获于第1个时钟沿
SPI_InitStructure.SPI_NSS = SPI_NSS_Hard; //CS引脚硬件配置
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16; //定义波特率预分频的值:波特率预分频值为32
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //数据传输从MSB位开始
SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC值计算的多项式
SPI_Init(SPI3, &SPI_InitStructure); //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器
SPI_RxFIFOThresholdConfig(SPI3, SPI_RxFIFOThreshold_HF);
SPI_I2S_DMACmd(SPI3,SPI_I2S_DMAReq_Tx,ENABLE);
SPI_I2S_DMACmd(SPI3,SPI_I2S_DMAReq_Rx,ENABLE);
SPI_Cmd(SPI3,ENABLE);
}
void DMA2_Channel1_IRQHandler(void)
{
if(DMA_GetITStatus(DMA2_IT_HT1) == SET)
{
DMA_ClearITPendingBit(DMA2_IT_HT1);
pADC=&SPI3_RxBuf[0];
pDAC=&SPI3_TxBuf[0];
SPI3_Flag=1;
}
if(DMA_GetITStatus(DMA2_IT_TC1) == SET)
{
DMA_ClearITPendingBit(DMA2_IT_TC1);
pADC=&SPI3_RxBuf[160];
pDAC=&SPI3_TxBuf[160];
SPI3_Flag=1;
}
}
int main()
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
delay_init(SYSCLK_FREQ_72MHz);
WM8978_Init();
PWM_Init(3,2); //PA08 PWM产生12MHz频率给Codec
SPI3_Init();
Led_Init();
while(1)
{
if(SPI3_Flag == 1) //声音回环
{
SPI3_Flag=0;
for(uint32_t i=0;i<160;i++)
{
pDAC[i]=pADC[i];
}
GPIO_ToggleBits(GPIOC,GPIO_Pin_13);
}
}
}
边栏推荐
- Nacos service registration and discovery
- Awesome, Tencent technical experts handed Redis technical notes, and the download volume has exceeded 30W
- Transformation of traditional projects
- Nacos服务注册与发现
- 传统项目转型
- Is the snowflake the same question?
- JIT vs AOT
- Introduction to management for technical people 1: What is management
- NLP自然语言处理(二)
- MySQL之数据查询(分类汇总与排序)
猜你喜欢
【C补充】整数与字符串的转换
护网行动基本介绍
开放地址法哈希实现——线性探测法
Nacos命名空间
HCIP实验(05)OSPF综合实验
Starlight does not ask passers-by!The young lady on the Wuhan campus successfully switched to software testing in three months and received a salary of 9k+13!
雪花是否一样问题
leetcode 5 questions a day-Day01
Three years of experience will only be a little bit (functional testing), and you may not even be able to find a job after resigning.
WPF 学习笔记《WPF布局基础》
随机推荐
(六)《数电》——二极管与CMOS门电路(入门)
Nacos实现高可用
OPENSQL
OpenFeign realize load balance
写给技术人的管理入门知识1:什么是管理
HCIP实验(05)OSPF综合实验
JUC (four): five/six states of shorthand thread
TCP拥塞控制技术 与BBR的加速原理
Public chains challenging the "Impossible Triangle"
curl命令获取外网ip
QT based on the third day (3) widget, dialog and mainwindow
SQL Server中如何在date类型中提取年、月、日数据
The most important transport layer
2022-07-29 第四小组 修身课 学习笔记(every day)
【Use of scientific research tools】A
The relationship between the number of Oracle processes and the number of sessions
Answer these 3 interview questions correctly, and the salary will go up by 20K
JIT VS AOT
LeetCode 114. 二叉树展开为链表(一题三吃)
WPF 学习笔记《WPF布局基础》