STM32CubeIDE Audio播放音频,DAC + TIM + DMA https://blog.csdn.net/sudaroot/article/details/106337717
STM32CubeIDE Audio播放音频,PWM + TIM https://blog.csdn.net/sudaroot/article/details/106373388
STM32CbueIDE Audio播放音频 WM8978 + I2S https://blog.csdn.net/sudaroot/article/details/106528371
STM32CubeIDE Audio播放音频 WM8978 + I2S + DMA双缓存模式 https://blog.csdn.net/sudaroot/article/details/106660351
STM32CubeIDE USB Audio声卡 WM8978 + I2S https://blog.csdn.net/sudaroot/article/details/106834893
仅想播放声音,一切从简。
步骤:了解wav音频格式,了解I2S协议,看手册配置WM8978,把数据通过I2S发给WM8978。
简化,暂不使用DMA发送数据,在网上都是双DMA缓存外部存储发送音频数据,容易绕晕。
不使用外部存储,使用芯片内部flash存储音频,音频数据加上程序不超过内部flash大小即可。
这既是我喜欢STM32CbueIDE这个软件的原因,能实时看到编译后的芯片资源剩余情况。
I2S 与 SPI 共用以下三个引脚:
● SD / DACDAT:串行数据(映射到 MOSI 引脚),用于发送或接收两个时分复用的数据通道上的数据(仅半双工模式)。
● WS / LRC:字选择(映射到 NSS 引脚),是主模式下的数据控制信号输出以及从模式下的数据控制信号输入。切换声道。
● CK / BCLK:串行时钟(映射到 SCK 引脚),是主模式下的串行时钟输出以及从模式下的串行时钟输入。
● I2S2ext_SD / I2S3ext_SD / ADCDAT:用于控制 I2S 全双工模式的附加引脚 (映射到 MISO 引脚)。
● MCK / MCLK:当 I2S 配置为主模式(并且 SPI_I2SPR 寄存器中的 MCKOE 位置 1)时,使用主时钟(单独映射)输出此附加时钟,该时钟输出频率 256 × FS,其中 FS 为音频信号采样频率。
注意STM32 对 24bit音频的相关操作。
WM8978手册:https://download.csdn.net/download/sudaroot/12497130
●I2S接口,支持最高192K,24bit音频播放
●DAC信噪比98dB;ADC信噪比90dB
●支持无电容耳机驱动(提供40mW@16Ω的输出能力)
●支持扬声器输出(提供0.9W@8Ω的驱动能力)
●支持立体声差分输入/麦克风输入
●支持左右声道音量独立调节
●支持3D效果,支持5路EQ调节
WM8978的控制通过I2S接口(即数字音频接口)同MCU进行音频数据传输(支持音频接收和发送)。
通过两线(MODE=0,即IIC接口)或三线(MODE=1)接口进行配置。
注意:
WM8978 的 寄存器只支持写操作不支持读操作。自己可以把寄存器值用数组存储下来。
WM8978 每个寄存器有0 ~ 8bit,共9bit控制。
看下图I2C的时序,除了设备地址后,在发送寄存器地址的时候把要发送给该寄存器的数据的最高位(即bit8)一起发送。
即发送(reg_addr << 1) | ((data >> 8) & 0x01);
由于WM8978只支持写操作,无读操作。故有唯一(写)地址 0x34 。
最简单的操作顺序:
1、芯片上电和关机操作顺序
SPEAKER OUTPUTS (LOUT2 AND ROUT2) 后面还有内容,看手册54页吧。
实际按照上电顺序操作,再R4选择I2S(FMT)和数据格式(WL),R6选择外部MCK时钟CLKSEL = 0,再调整一下左右声道音量,就能出声音了。
如果喜欢输出音质好一下R10寄存器设置一下 DACOSR128 = 1;
还有R43寄存器ROUT2信号路径还具有可选的反相器。用于该反相的放大器可用于以-15dB-> + 6dB的可调增益范围混入AUXR信号。这样就可以将“蜂鸣”信号仅应用于扬声器输出,而不会影响HP或线路输出。
硬件图:某原子STM32F407ZG + WM8978
配置I2C控制WM8978寄存器。
I2S协议。使用的音频是双声道8KHz 16bit音频。
生成代码。
WM8978控制代码,其中datas.h里面是音频数据。
#include
#include
#include
#include "datas.h"
#define WM8978_ADDRESS 0x1A
#define WM8978_WIRTE_ADDRESS (WM8978_ADDRESS << 1 | 0)
#define BUFFER_SIZE 2048
HAL_StatusTypeDef WM8978_Register_Wirter(uint8_t reg_addr, uint16_t data)
{
uint8_t pData[10] = {0};
pData[0] = (reg_addr << 1) | ((data >> 8) & 0x01);
pData[1] = data & 0xFF;
return HAL_I2C_Master_Transmit(&hi2c1, WM8978_WIRTE_ADDRESS, pData, 2, 1000);
}
void WM8978_Init(void)
{
WM8978_Register_Wirter(0, 0); // 软复位
WM8978_Register_Wirter(1, 0x0F); // 模拟放大器使能, 使能输出输入缓存区
WM8978_Register_Wirter(3, 0x7F); // 使能左右声道和L\ROUT2
WM8978_Register_Wirter(4, 0x10); // I2S 16bit
WM8978_Register_Wirter(6,0); // MCU提供时钟
WM8978_Register_Wirter(10, 0x08); // 输出音质最好
WM8978_Register_Wirter(43, 0x10); // ROUT2反相
WM8978_Register_Wirter(54,30); // 设置LOUT2左声道音量
WM8978_Register_Wirter(55,30|(1<<8)); // 设置ROUT2右声道音量, 更新左右声道音量
}
void WM8978_Palyer(void)
{
uint32_t DataLength = 0;
uint8_t* DataAddress = NULL;
uint16_t* TempAddress = NULL;
DataLength = sizeof(data) - 0x2c;
DataAddress = (unsigned char *)(data + 0x2c);
TempAddress = (uint16_t*)DataAddress;
while(1)
{
HAL_I2S_Transmit(&hi2s2, TempAddress, BUFFER_SIZE / 2, 1000);
DataLength -= BUFFER_SIZE;
TempAddress += (BUFFER_SIZE / 2);
if(DataLength < BUFFER_SIZE) break;
}
}
主函数:
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_CRC_Init();
MX_I2C1_Init();
MX_I2S2_Init();
MX_USART1_UART_Init();
printf("Sudaroot\r\n");
WM8978_Init();
WM8978_Palyer();
while (1)
{
}
}
源码:https://download.csdn.net/download/sudaroot/12497400
本人是一个嵌入式未入门小白,博客仅仅代表我个人主观见解方便记录成长笔记。 若有与大神大大见解有冲突,我坚信大神大大见解是对的,我的是错的。 感谢~!