STM32F103 硬件SPI+WM8978单声道语音回环
STM32F3/F4系列芯片拥有全双工I2S接口可以与Codec通信,但是目前F1系列芯片或有些国产芯片没有全双工的I2S,要想使用Codec传输音频只能使用两个半双工合为一个全双工I2S,这不仅硬件上需要额外引脚,软件配置上也会少一个可使用的SPI接口。针对这一问题,我们可以使用SPI接口与Codec的“左对齐”接口模式进行通信(不是用SPI模拟I2S接口),单片机作为从器件,Codec作为主器件。接下来介绍使用DMA+乒乓buffer双缓存形式来进行语音回环处理。
/************WM8978配置**********/
uint8_t WM8978_Write_Reg(uint16_t nRegAddr,uint16_t nRegValue)
{IIC_Start();IIC_Send_Byte(0x34);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,0x0008); //左对齐模式WM8978_Write_Reg(6,0x01AD); //MCLK=12MHzWM8978_Write_Reg(7,0x000A); //8KWM8978_Write_Reg(10,0x0008); //ADC高通滤波器打开WM8978_Write_Reg(14,0x0108); //DAC高通滤波器打开WM8978_Write_Reg(36,0x0018);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);
}
SPI2_CS ---> WM8978_LRC
SPI2_SCK ---> WM8978_BCLK
SPI2_MISO ---> WM8978_ADC
SPI2_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); //使能定时器1时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能GPIOA外设时钟GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;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_timTIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);//初始化PWM模式TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式1TIM_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 SPI2_RxBuf[320],SPI2_TxBuf[320];
__IO uint32_t SPI2_Flag=0;void SPI2_Init(void)
{NVIC_InitTypeDef NVIC_InitStructure;DMA_InitTypeDef DMA_InitStructure;GPIO_InitTypeDef GPIO_InitStructure;SPI_InitTypeDef SPI_InitStructure;pADC=&SPI2_RxBuf[160];pDAC=&SPI2_TxBuf[160];NVIC_InitStructure.NVIC_IRQChannel=DMA1_Channel4_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2; NVIC_InitStructure.NVIC_IRQChannelSubPriority=2; NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;NVIC_Init(&NVIC_InitStructure);RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2,ENABLE);GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;GPIO_Init(GPIOB,&GPIO_InitStructure);GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;GPIO_InitStructure.GPIO_Pin=GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_15;GPIO_Init(GPIOB,&GPIO_InitStructure);DMA_InitStructure.DMA_PeripheralBaseAddr=(uint32_t)&SPI2->DR; //外设地址DMA_InitStructure.DMA_MemoryBaseAddr=(uint32_t)SPI2_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(DMA1_Channel4,&DMA_InitStructure);DMA_InitStructure.DMA_PeripheralBaseAddr=(uint32_t)&SPI2->DR; //外设地址DMA_InitStructure.DMA_MemoryBaseAddr=(uint32_t)SPI2_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(DMA1_Channel5,&DMA_InitStructure);DMA_ITConfig(DMA1_Channel4,DMA_IT_HT,ENABLE);DMA_ITConfig(DMA1_Channel4,DMA_IT_TC,ENABLE);DMA_Cmd(DMA1_Channel4,ENABLE);DMA_Cmd(DMA1_Channel5,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; //定义波特率预分频的值:波特率预分频值为16SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //数据传输从MSB位开始SPI_Init(SPI2, &SPI_InitStructure); //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器SPI_I2S_DMACmd(SPI2,SPI_I2S_DMAReq_Tx,ENABLE);SPI_I2S_DMACmd(SPI2,SPI_I2S_DMAReq_Rx,ENABLE);SPI_Cmd(SPI2,ENABLE);
}void DMA1_Channel4_IRQHandler(void)
{if(DMA_GetITStatus(DMA1_IT_HT4) == SET){DMA_ClearITPendingBit(DMA1_IT_HT4);pADC=&SPI2RxBuf[0];pDAC=&SPI2_TxBuf[0];SPI2_Flag=1;}if(DMA_GetITStatus(DMA1_IT_TC4) == SET){DMA_ClearITPendingBit(DMA1_IT_TC4);pADC=&SPI2_RxBuf[160];pDAC=&SPI2_TxBuf[160];SPI2_Flag=1;}
}
int main()
{NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);delay_init(SYSCLK_FREQ_72MHz);WM8978_Init();PWM_Init(3,2); //PA08 PWM产生12MHz频率给CodecSPI2_Init();Led_Init();while(1){if(SPI2_Flag == 1){SPI2_Flag=0;for(uint32_t i=0;i<160;i++) //声音回环{pDAC[i]=pADC[i];}GPIOC->ODR^=GPIO_Pin_13;}}
}
https://download.csdn.net/download/qq_44065318/86245927
STM32F103 硬件SPI+WM8978单声道语音回环相关推荐
- CC2652RB硬件SPI驱动W5100S网卡进行回环测试
1. W5100S 1.1 简介 W5100S 是一款多功能的单芯片网络接口芯片,内部集成全硬件的TCP/IP协议栈,以太网MAC和 10Base-T/100Base-TX以太网控制器.主要应用于高集 ...
- STM32F103 硬件SPI驱动外部FLASH W25X16
之前学习过使用,汇顶和盛源达的蓝牙芯片驱动外部FLASH,从外部FLASH中读出图片原始数据,并刷新到OLED上,最开始入门的芯片也是STM321但是在学校的时候基本没有使用STM32来驱动外部FLA ...
- ad5764的回读,硬件spi接口
之前写过了mcu通过硬件spi接口向dac芯片ad5764的数据寄存器写值输出电压,ad5764的offset与gain寄存器的值也是可以通过硬件spi读出来的. 第一步:将待读取的芯片设置为读模式 ...
- pcm 8k单声道和双声道采样_音频接口PCM
1 简介 PCM (Pulse Code Modulation) 是通过等时间隔(即采样率时钟周期)采样将模拟信号数字化的方法.图 1为 4 bit 采样深度的PCM 数据量化示意图.PCM 数字音频 ...
- 关于声音的前后左右上下的控制以及单声道立体声的区别
一. 声像 在立体声重放技术中,用摆声像的方法把各个乐器的位置还原出来,这就是声像.一个乐器就是一个声像点. 我们知道声音是一种波,点击下面链接以flash形式演示波的振动. http://huang ...
- STM32硬件SPI通过fm17550读取身份证UID,识别银行卡,识别TYPEA与TYPEB
上面是我的微信和QQ群,欢迎新朋友的加入. 目录 1.硬件平台 2.驱动实现 3.循环扫卡测试程序 4.TYPEB射频卡扫描 5.TYPEA射频卡扫描 6.身份证UID读取 1.硬件平台 1.STM3 ...
- STM32硬件SPI控制TM1638 按键数码管LED显示模块
STM32硬件SPI控制TM1638按键数码管LED显示模块 从淘宝买来的,TM1638专门是控制LED的,LED组合起来就可以变成数码管,还有按键,这个我就没管了,不想管了,发这个帖子只是为了记 ...
- STM32CubeMX | 基于STM32使用HAL库硬件SPI驱动WK2124一拖四SPI转四路串口芯片
STM32CubeMX | 基于STM32使用HAL库硬件SPI驱动WK2124一拖四SPI转四路串口芯片 STM32基础工程生成 首先使用STM32CUBEMX生成STM32的基础工程,配置时钟到7 ...
- STM32音频I2S单声道处理方法
STM32的M4系列芯片支持全双工的I2S,而一般做语音信号处理的场景一般都采用单声道的处理方法,因为通常情况下左右声道采集的语音都是一样的.然而DMA方式下,STM32的芯片不像DSP那样支持DMA ...
- 硬件SPI+LCD增强屏幕刷新率
1.前言 近日在研究如何提高LCD的刷新率,修改程序发现LCD屏幕用的是模拟通信,所以刷新特别慢,在设置硬件SPI,并使用通过HAL库HAL_SPI_Transmit()函数发送数据后刷新率并没有提升 ...
最新文章
- 你不知道的CSS单位
- git 连接到远程的其它分支问题
- C#路径/文件/目录/I/O常见操作汇总(一)
- 王健林:中国房地产不会崩盘 进入十年平稳发展期
- 3种关闭linux系统端口方法
- python将一行作为字段_关于python:Django admin在同一行显示多个字段
- [SpringSecurity]框架概述
- P3834 【模板】可持久化线段树 2(整体二分做法)
- gin 生成api文档_gin-swagger 生成RESTful风格OpenAPI文档
- lwip协议栈在linux运行,LwIP协议栈在uCOS II下的实现
- 文档安全管理系统服务器地址是什么,一种文档安全管理系统登录方法及装置
- Python GUI界面编程初步 04 - Tkinter设计框架和概念
- Android高级-Android操作SQL数据管理,增删改查
- NPOI导出 The maximum column width for an individual cell is 255 characters
- 一些Gym三星单刷的比赛总结
- nodejs下载集成到idea
- 第24期、宠物医院管理系统
- 解读WPF中的Binding
- 上号神器扫码教程,常见问题解答
- picturebox绑定datagridview
热门文章
- Centos 7环境MySql8.0.28源码安装
- mysql当前时间的本月_mysql 获得当前月1号的日期 和 0点日期方法
- 番外4:自动进行功放输出阻抗匹配设计(匹配至4次谐波)
- 人工智能 —— 归结演绎推理
- Microsoft Office LTSC ProPlus 2021 Volume(含:Project + Visio)离线命令方式安装说明(原创)
- ROS智能车定位导航仿真(原赛道自主导航,构建地图再导航)
- 解决在onedrive里无法打开onenote笔记本的问题
- M1芯片的Mac在开发iOS项目时遇到的问题汇总(模拟器无法运行,Cocoapods错误等)
- SVO 论文与代码分析总结
- pycharm双击无反应