整个工程文件是在江科大的OLED显示屏OLED-V2.0版本IIC四针脚接口UTF-8的工程上编写的,在屏幕显示过程中,只用到了OLED显示屏的绘制直线和绘制像素点两个函数(注意,显示屏的绘制函数坐标可以任意指定,而不是按页写入。任一屏幕只要有上述两个函数均可使用。
工程接线:
STM32F103C8T6 ---------TypeC供电/(3.3V也可以但现象可能不理想)
OLED VCC-----------3.3V
GND---------GND
SCL----------PB8
SDA---------PB9
声音传感器 VCC---------3.3V
GND--------GND
AOUT-------PA0
DOUT-------悬空
一、制作过程
首先需要移植STM32官方的DSP库,在程序中,用定时器定时触发ADC采样,采集声音传感器得到音频信号,并用DMA进行数据转移。因为需要256点FFT变换,故每次触发ADC采样256个值。并将这些值预处理后做FFT变换并计算各次谐波的幅值。由FFT变换的性质可知,前128个幅值点与后128个幅值点对称,所以只需前128个数据(一般采样点的数量是屏幕像素点数的2倍,本款OLED为64*128像素,故推荐采用256点)。最后通过0.96寸OLED屏幕绘制频谱图。
二、需要使用到的模块
1.STM32F103C8T6
2.OLED显示屏(0.96寸四针脚IIC协议)
3.声音检测模块(LM386功放)
4.可调频率发生装置(可以手机或电脑模拟)
三、所用到的基本知识简介
1.声音传感器(LM386音频功放)简介
参数名称 | 参数 |
音频放大芯片 | LM386(放大200倍) |
工作电压 | 3.3V-5.5V |
LM386是一种音频集成功率放大器,具有自身功耗低、更新内链增益可调整、电源电压范围大、外接元件少和总谐波失真小等优点。主要应用于低电压消费类产品。为使外围元件最少,电压增益内置为20。在1脚和8脚之间增加一只外接电阻和电容,便可将电压增益调为200以内的任意值。
引脚表示 | 描述 |
DOUT | 数字量输出 |
AOUT | 模拟量输出 |
GND | 电源地 |
VCC | 电源正(3.3V-5V) |
声音传感器的默认采样频率为1KHz,有效采样频段为50~20KHz(注:无法覆盖整个人耳可听到的频段20~20KHz),故实验实际效果低频段不太准确。
问题:AOUT和DOUT引脚所输出的是什么?
AOUT输出的实际上输出的就是声音信号,接上喇叭可以听到声音。声音信号变化很快,如果要采集声音信号需要很高采样率。例如电话采样率为8KHZ。波形的振幅表示声音的强度大小,声音越大振幅越大。故通过单片机采集到的AD值不能表示声音的大小。此模块也不能测声音具体多少分贝。
DOUT输出的是高低电平,当声音超过设置的阈值,输出低电平。
问题:模块上A和D可调电阻的作用?
A是调节放大增量,D是设置阈值。Aout大过阈值,Dout输出低电平,反之输出高电平
附:声音传感器用户手册及模块原理图
Sound-Sensor-UserManual.pdfhttps://www.waveshare.net/w/upload/1/1e/Sound-Sensor-UserManual.pdfSound_Sensor_V2.pdfhttps://www.waveshare.net/w/upload/c/ce/Sound_Sensor_V2.pdf
2.STM32DSP库FFT(快速傅里叶变换)简介
DSP库下载移植方法
方法一:在线安装(此方法安装了所有的关于DSP库函数)
点击Pack Installer选项
等待加载完成,观察右半侧界面点击Generic->ARM::CMSIS-DSP选择任意版本点击Install即可(示例已安装过,所以为Remove)
等待安装完成。
方法二:下载安装包安装(此方法仅提供了256点,1024点FFT变换文件)
基于STM32的FFT音乐频谱.zip
链接: https://pan.baidu.com/s/1XT_iIoIhcJJymkA5urFMVA?pwd=lkh0 提取码: lkh0
点击此链接,下载文件名为DSP的文件
下载解压完成之后,找到文件中名为DSP的文件.复制粘贴到自己的工程中,路径任意指定(若存放在已有的文件夹下,则可跳过第一步和第二步),因为官方库函数,所以我在工程中单独创建一个新的文件夹存放这些函数.
完成之后进入工程,引入库函数,并添加文件路径
进入工程之后,第一步点击Options for Target...选项
第二步,在弹出的选项卡中,点击C/C++选项,找到Include Paths输入框,在输入框的最右端有三个点,点击该三个点选项,进入Folder Setup选项卡添加DSP库的路径即可.
完成路径配置之后点击OK,OK,退出此界面.
第三步,点击File Extensions,Books and Environment...选项.
第四步,在弹出的界面中,第二栏Groups添加新的分组名称为"DSP"或其它名称(若DSP文件存放在了以有的文件分组下,无需进行此次创建),创建完成后,点击新创建的分组选项(若DSP文件存放在了以有的文件分组下,则需点击父目录文件夹名称,开始配置第三栏Files,点击最下方Add Files...选项,在弹出的对话框中选中存放的路径文件夹,导入即可.(因为此次实验只进行256点FFT计算,所以我只添加了cr4_fft_256_stm32.s ,stm32_dsp.h和table_fft.h则必须添加.(需要1024点FFT计算,只需把cr4_fft_256_stm32.s换成cr4_fft_1024_stm32.s即可,或者同时将他们引入)
点击OK后,Project中弹出以下文件,完成配置.
附:大佬们的详细安装及测试教程
STM32 DSP库的使用方法_keil uvision5有dsp库吗-CSDN博客https://blog.csdn.net/u010058695/article/details/112665306
测试是否移植正确(该部分转载上述链接中部分内容):
首先在程序中用math库函数中的sin或cos函数模拟一段确知的连续信号
1 /******************************************************************
2 函数名称:InitBufInArray()
3 函数功能:模拟采样数据,采样数据中包含3种频率正弦波(350Hz,8400Hz,18725Hz)
4 参数说明:
5 备 注:在lBufInArray数组中,每个数据的高16位存储采样数据的实部,
6 低16位存储采样数据的虚部(总是为0)
7 作 者:博客园 依旧淡然(http://www.cnblogs.com/menlsh/)
8 *******************************************************************/
9 void InitBufInArray()
10 {
11 unsigned short i;
12 float fx;
13 for(i=0; i<NPT; i++)
14 {
15 fx = 1500 * sin(PI2 * i * 350.0 / Fs) +
16 2700 * sin(PI2 * i * 8400.0 / Fs) +
17 4000 * sin(PI2 * i * 18725.0 / Fs);
18 lBufInArray[i] = ((signed short)fx) << 16;
19 }
20 }
其中,NPT是采样点数256,PI2是2π(即6.28318530717959),Fs是采样频率44800。可以看到采样数据中包含了3种频率的正弦波,分别为350Hz,8400Hz和18725Hz。
从代码中不难看出计算一个FFT变换,需要将所有数据左移16位,在本实验中传感器所采集到的数据均左移16位即可。高16位存放实数部分,低16位存放虚数部分。
浅浅的提一下、在实际FFT变换中存在这样一种方法:通过一个点复数序列求出两个点实数序列的离散傅里叶变换,进一步提升快速傅里叶变换的效率。因此可以用128点傅里叶变换来巧妙的计算256点FFT傅里叶变换。但是,stm32提供的FFT函数库为基4的傅里叶变换(64点,256点,1024点)。
我们需要进行256点FFT变换,故调用此函数
void cr4_fft_256_stm32(void *pssOUT, void *pssIN, u16 Nbin);
参数1:*pssOUT一个用于存放结果的地址,通常为一个长度为Nbin/2的数组。
参数2:*pssIN一个用于存放待变换数据的地址,通常为一个长度为Nbin的数组。
参数3:指定待变换的点数。尽管参数在函数定义中被明确指定为 256 点,但这个参数可能用于函数的一般化设计,以便将来可以轻松地修改或扩展为其他点数的 FFT 变换。
调用该函数之后,在pssOUT数组中就存放了进行FFT运算之后的结果数据。该数组中每个元素的数据格式为:高16位存储虚部,低16位存储实部。
计算各次谐波的幅值
/******************************************************************
函数名称:GetPowerMag()
函数功能:计算各次谐波幅值
参数说明:
备 注:先将lBufOutArray分解成实部(X)和虚部(Y),然后计算幅值(sqrt(X*X+Y*Y)
作 者:博客园 依旧淡然(http://www.cnblogs.com/menlsh/)
*******************************************************************/
void GetPowerMag()
{
signed short lX,lY;
float X,Y,Mag;
unsigned short i;
for(i=0; i<NPT/2; i++)
{
lX = (lBufOutArray[i] << 16) >> 16;
lY = (lBufOutArray[i] >> 16);
X = NPT * ((float)lX) / 32768;
Y = NPT * ((float)lY) / 32768;
Mag = sqrt(X * X + Y * Y) / NPT;
if(i == 0)
lBufMagArray[i] = (unsigned long)(Mag * 32768);
else
lBufMagArray[i] = (unsigned long)(Mag * 65536);
}
}
调用该函数,可计算出各次谐波的幅值,并存储在 lBufMagArray数组中.最后,通过串口或者OLED打印出该数值中的内容.
添加DSP库后,完整的测试代码如下
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "stm32_dsp.h"
#include "Sound_Sensor.h"
#include "math.h"
/******************************************************************
函数名称:InitBufInArray()
函数功能:模拟采样数据,采样数据中包含3种频率正弦波(350Hz,8400Hz,18725Hz)
参数说明:
备 注:在lBufInArray数组中,每个数据的高16位存储采样数据的实部,
低16位存储采样数据的虚部(总是为0)
作 者:博客园 依旧淡然(http://www.cnblogs.com/menlsh/)
*******************************************************************/
uint32_t NPT = 256, Fs = 44800;//44800
float PI2 = 6.283185;
long lBufInArray[256] = {0}, lBufOutArray[128],lBufMagArray[128];
/******************************************************************
函数名称:GetPowerMag()
函数功能:计算各次谐波幅值
参数说明:
备 注:先将lBufOutArray分解成实部(X)和虚部(Y),然后计算幅值(sqrt(X*X+Y*Y)
作 者:博客园 依旧淡然(http://www.cnblogs.com/menlsh/)
*******************************************************************/
void GetPowerMag()
{
signed short lX,lY;
float X,Y,Mag;
unsigned short i;
for(i=0; i<NPT/2; i++)
{
lX = (lBufOutArray[i] << 16) >> 16;
lY = (lBufOutArray[i] >> 16);
X = NPT * ((float)lX) / 32768;
Y = NPT * ((float)lY) / 32768;
Mag = sqrt(X * X + Y * Y) / NPT;
if(i == 0)
lBufMagArray[i] = (unsigned long)(Mag * 32768);
else
lBufMagArray[i] = (unsigned long)(Mag * 65536);
}
}
void InitBufInArray()
{
unsigned short i;
float fx;
for(i=0; i<NPT; i++)
{
fx = 1500 * sin(PI2 * i * 350.0 / Fs) +
2700 * sin(PI2 * i * 8400.0 / Fs) +
4000 * sin(PI2 * i * 18725.0 / Fs);
lBufInArray[i] = ((signed short)fx) << 16;
}
}
int main(void)
{
OLED_Init(); //OLED屏幕初始化
uint16_t i = 0;
uint8_t x = 0;
int16_t y = 0;
InitBufIAnArray();
cr4_fft_256_stm32(lBufOutArray, lBufInArray, NPT);
GetPowerMag();
while (1)
{
for(i=1; i<128; i++)
{
x = i;
y = 63 - lBufMagArray[x] / 10;
if(y < 0 )
{
y = 0;
}
OLED_DrawLine(x,y,x,64);
}
/*******************显示*******************/
OLED_Update();
OLED_Clear();
}
}
移植成功实验现象:
在频率为350Hz,8400Hz和18725Hz时,幅值出现峰值,分别为1492、2696和3996(因像素限制最高只能为64,所以无法显示具体高度,峰值位置正确即可),对应的x值(OLED屏幕横坐标)为2,48,107.或者用串口打印出这些数据.若能观察到此现象或数据相符,则说明已经成功移植.
四、实验代码编写
本实验用到高级定时器1来触发ADC1的采样频率,确保采样间隔稳定.
ADC采样外部触发源
在该代码中,选择定时器1的通道2来输出PWM触发ADC1来定时采样
由奈奎斯特采样定律可知,采样频率需要大于两倍的信号频率,声音信号的频率范围为20~20kHz,若直接分辨整个人耳可听到的频率范围,至少需要40KHz的采样频率,但是,在屏幕上均匀量化显示,会发现频谱集中在OLED屏幕的前1/4区域,所以为了更好的显示效果,本实验采样频率为10KHz,所绘制的频谱图范围为0~5KHz.采样频率为10KHz,故把定时器的溢出时间(PWM的周期)设为100us.
TIM1.c
//TIM1配置,arr为重加载值,psc为预分频系数
void TIM1_Init(u16 arr,u16 psc)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE); //时钟使能
//定时器TIM2初始化
TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx的时间基数单位
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式1
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
TIM_OCInitStructure.TIM_Pulse = 50;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //输出极性:TIM输出比较极性低
TIM_OC2Init(TIM1, & TIM_OCInitStructure); //初始化外设TIM1_CH2
TIM_Cmd(TIM1, ENABLE); //使能TIMx
TIM_CtrlPWMOutputs(TIM1, ENABLE);
}
在传感器代码中首先开始总线时钟,始终初始化GPIO代码,该实验选择ADC1的通道1,即PA0引脚,配置为模拟输入模式,只需采集一个通道DMA数据,因此ADC关闭扫描.由定时器定时触发采样,关闭连续转换模式.初始化ADC并使能ADC到DMA.打开DMA转运完成中断,在中断中完成数据移位操作,变成FFT函数可直接使用的格式.
Sound_Sensor.c
uint16_t AD_Value[256];
void Sound_Sensor_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure; //GPIO配置结构体定义
ADC_InitTypeDef ADC_InitStructure; //ADC配置结构体定义
DMA_InitTypeDef DMA_InitStructure; //DMA配置结构体定义
NVIC_InitTypeDef NVIC_InitStructure; //NVIC配置结构体定义
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能GPIOA时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); //使能ADC1通道时钟
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE); //使能ADC1通道时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //ADC采样,故需要模拟输入
GPIO_Init(GPIOA, &GPIO_InitStructure);
//ADC1初始化
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //独立ADC模式
ADC_InitStructure.ADC_ScanConvMode = DISABLE; //关闭扫描方式
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //关闭连续转换模式
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC2;//触发源选择定时器1的通道二触发
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //采集数据右对齐
ADC_InitStructure.ADC_NbrOfChannel = 1; //要转换的通道数目
ADC_Init(ADC1, &ADC_InitStructure);
RCC_ADCCLKConfig(RCC_PCLK2_Div6); //配置ADC时钟,为PCLK2的6分频,72M/6=12M,最好不要超过14M
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_1Cycles5);//配置ADC1通道6为1.5个采样周期,数据较多,以最快速度采样
ADC_DMACmd(ADC1,ENABLE);//使能DMA转运ADC1的数据
ADC_Cmd(ADC1,ENABLE); //使能ADC1
ADC_ResetCalibration(ADC1); //复位校准寄存器
while(ADC_GetResetCalibrationStatus(ADC1)); //等待校准寄存器复位完成
ADC_StartCalibration(ADC1); //ADC校准
while(ADC_GetCalibrationStatus(ADC1)); //等待校准完成
ADC_ExternalTrigConvCmd(ADC1, ENABLE); //设置外部触发模式使能
//DMA1初始化
DMA_DeInit(DMA1_Channel1); //配置DMA前需要先关闭DMA,因为本实验只初始化一次,在本实验中实测删去影响不大
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR; //ADC1地址
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)AD_Value; //内存地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //方向(从外设到内存)
DMA_InitStructure.DMA_BufferSize = 256; //传输内容的大小
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址固定仅有一个传感器
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //内存地址自增,需要256个数据
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord ; //以半字节长度进行传输(16位)
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord ; //以半字节长度进行传输(16位)
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular ; //循环传输模式
DMA_InitStructure.DMA_Priority = DMA_Priority_High ; //优先级按需指定
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //失能内存到内存的传输
DMA_Init(DMA1_Channel1, &DMA_InitStructure);
DMA_ITConfig(DMA1_Channel1,DMA_IT_TC, ENABLE); //使能传输完成中断
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
DMA_Cmd(DMA1_Channel1,ENABLE);
TIM1_Init(99,71);//72000000/7200=10000Hz,每100us采集一次
}
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "stm32_dsp.h"
#include "Sound_Sensor.h"
#include "math.h"
/******************************************************************
函数名称:InitBufInArray()
函数功能:模拟采样数据,采样数据中包含3种频率正弦波(350Hz,8400Hz,18725Hz)
参数说明:
备 注:在lBufInArray数组中,每个数据的高16位存储采样数据的实部,
低16位存储采样数据的虚部(总是为0)
作 者:博客园 依旧淡然(http://www.cnblogs.com/menlsh/)
*******************************************************************/
uint32_t NPT = 256, Fs = 9984;//采样频率为方便计算,选用一个接近10000且,Fs/NTP为整数的值(39)
//OLED显示中,第i个像素点表示的为i*39Hz的频率
float PI2 = 6.283185;
long lBufInArray[256] = {0}, lBufOutArray[128],lBufMagArray[128];
/******************************************************************
函数名称:GetPowerMag()
函数功能:计算各次谐波幅值
参数说明:
备 注:先将lBufOutArray分解成实部(X)和虚部(Y),然后计算幅值(sqrt(X*X+Y*Y)
作 者:博客园 依旧淡然(http://www.cnblogs.com/menlsh/)
*******************************************************************/
void GetPowerMag()
{
signed short lX,lY;
float X,Y,Mag;
unsigned short i;
for(i=0; i<NPT/2; i++)
{
lX = (lBufOutArray[i] << 16) >> 16;
lY = (lBufOutArray[i] >> 16);
X = NPT * ((float)lX) / 32768;
Y = NPT * ((float)lY) / 32768;
Mag = sqrt(X * X + Y * Y) / NPT;
if(i == 0)
lBufMagArray[i] = (unsigned long)(Mag * 32768);
else
lBufMagArray[i] = (unsigned long)(Mag * 65536);
}
}
void InitBufInArray()
{
unsigned short i;
for(i=0; i<NPT; i++)
{
lBufInArray[i] = ((signed short)(AD_Value[i] - 2048)) << 16;
}
}
int main(void)
{
OLED_Init(); //OLED屏幕初始化
Sound_Sensor_Init(); //声音传感器模块初始化
uint8_t fall_pot[128];
for(int index = 0; index < 128; index++)//将下落的像素点初始化在最低端
{
fall_pot[index] = 63;
}
uint16_t i = 0;
uint8_t x = 0;
int16_t y = 0;
while (1)
{
for(i=1; i<128; i++) //绘制128个列像素的每一点幅值,从1开始是为了舍弃谐波中的第一个直流分量
{
x = i; //OLED显示屏列
y = 63 - lBufMagArray[x] / 10; //OLED显示屏左上角坐标为(0,0),右下角为(127, 63)、lBufMagArray[x]/10是为了量化显示结果
if(y < 0 ) //如果y超出了屏幕,则按最大值绘制
{
y = 0;
}
OLED_DrawLine(x,y,x,63);//开始画线,前两个参数为线的顶端,后两个为低端位置,低端y轴固定为63
/*为了更美观的显示,显示下落的点*/
OLED_DrawPoint(i, fall_pot[i]);//绘制一个像素点
if(fall_pot[i] >= y) //像素点不断刷新,并由频谱线举起,(行坐标越小,代表越高)
{
fall_pot[i] = y; //频谱线高于像素点时,更新像素点在频谱线上端
}
fall_pot[i]++; //使像素点没循环一次下降1,等效于 fall_pot[i]=fall_poll[i]+n;n=1;n越大,下降速度越快。
}
/*******************统一显示*******************/
OLED_Update();
OLED_Clear();
}
}
//中断处理函数
void DMA1_Channel1_IRQHandler(void)
{
if(DMA_GetITStatus(DMA1_IT_TC1)!=RESET)
{
//中断处理代码
InitBufInArray(); //将数据256个数据均左移16位
cr4_fft_256_stm32(lBufOutArray, lBufInArray, NPT);//进行FFT变换
GetPowerMag(); //计算各次谐波幅值
DMA_ClearITPendingBit(DMA1_IT_TC1); //清除完成中断标志位
}
}
经实测、在while循环中可适当加入一些延时函数,这样更容易观察声音的节奏。
五、最终程序实验现象
1.频谱分析测试
固定频率发生在线网址:在线音调发生器 - 播放任何音调或频率
<iframe allowfullscreen="true" data-mediaembed="csdn" frameborder="0" id="UFkT1R2K-1736752339285" src="https://live.csdn.net/v/embed/443110"></iframe>固定频率频谱
在视频中我们可以观察到,当音调发生频率在5000Hz左右时,峰值刚好在OLED屏幕最右侧,所以达到了实验的预期效果。当频率大于5000Hz时,会发生频谱混叠,现象视频中没有展示。
2.音乐频谱显示
<iframe allowfullscreen="true" data-mediaembed="csdn" frameborder="0" id="Boil0pWy-1736759439219" src="https://live.csdn.net/v/embed/443152"></iframe>音乐频谱
在视频中可观察到低频仍然存在较大的波动,经过测试,可能是供电电压不稳定所导致,因此可以选择使用Type-C接口供电,以上情况可能会有所改善。
另外指出,该频谱线宽仅为1px,可适当增加线宽提高观赏性。这里暂不提供代码,思路与上述过程一致,对128个数据等间隔采样即可。
本程序源码:https://pan.baidu.com/s/1XT_iIoIhcJJymkA5urFMVA?pwd=lkh0 提取码: lkh0
以上内容为个人制作过程所遇到的问题,因能力有限,文章中难免会出现错误,观点仅供参考!
标签:STM32F103,频谱,DMA,FFT,TIM,采样,InitStructure,ADC,256 From: https://blog.csdn.net/2302_80229212/article/details/145097224