本次实现的功能为单片机DAC输出一个正弦波,然后ADC定时采样用DMA输出,最后对DAC输出的波形进行FFT。
单片机STM32F103ZET6
内部时钟
一、配置ADC
ADC端口为PA1,采用DMA输出,定时器3触发
![](https://img-blog.csdnimg.cn/img_convert/5c99ef052741b4ddf258614fe2353d0d.png)
![](https://img-blog.csdnimg.cn/img_convert/d232b36bb837f43ee94ed5c4e9006710.png)
![](https://img-blog.csdnimg.cn/img_convert/0b6fde1ff0d93376fc5a50be37f0279f.png)
![](https://img-blog.csdnimg.cn/img_convert/fe149871d9f202ade8a36925eb67868f.png)
定时器时钟64M,分频后为102.4KHz
ADC采样时间为102.4KHz/100=1.024KHz
二、配置DAC
DAC端口PA4
![](https://img-blog.csdnimg.cn/img_convert/628f49e5d6bf71aa075e9ab3afd79b40.png)
DMA传输
![](https://img-blog.csdnimg.cn/img_convert/5acab2877fd8d4726a94d7e019078bad.png)
定时器6
定时器时钟64M,分频后为1MHz
![](https://img-blog.csdnimg.cn/img_convert/c256fe6dc1b361a87ac5a640c4506040.png)
三、配置DSP
![](https://img-blog.csdnimg.cn/img_convert/b638e6ed20d1ed0f3227b6bf9faabbb1.png)
![](https://img-blog.csdnimg.cn/img_convert/f95be87f6896dadca3e7c9dcb2056633.png)
四、配置时钟
![](https://img-blog.csdnimg.cn/img_convert/074ab9cc0a89cf4dfd802bea4a4c66b5.png)
四、代码
注意生成的代码里初始化中DMA要在ADC之前
FFT需要#include "arm_math.h"头文件
需添加include
![](https://img-blog.csdnimg.cn/img_convert/58d62e1bde43986a33de05376cafcdaf.png)
![](https://img-blog.csdnimg.cn/img_convert/b0737772df5fb14e9236e79b86807a8d.png)
![](https://img-blog.csdnimg.cn/img_convert/5487dbea47de82d57174b8f3a7e8fde0.png)
在define后面补全USE_HAL_DRIVER,STM32F103xE,ARM_MATH_CM4,__CC_ARM,ARM_MATH_MATRIX_CHECK,ARM_MATH_ROUNDING,__FPU_PRESENT=1
![](https://img-blog.csdnimg.cn/img_convert/11daa4dcc8a818134c3d6af4885eb552.png)
main中加入ADC、DAC与FFT代码
uint16_t ad_value;
uint16_t adc_buffer[1] = {0};
HAL_TIM_Base_Start(&htim3);
HAL_ADC_Start_DMA(&hadc1,(uint32_t *)&adc_buffer,1);
HAL_TIM_Base_Start(&htim6);
SineWave_Data(n,DualSine12bit,1.6);
HAL_DAC_Start_DMA(&hdac,DAC_CHANNEL_1,(uint32_t *)DualSine12bit,n,DAC_ALIGN_12B_R);
arm_cfft_radix4_instance_f32 scfft; //FFT对应结构体变量
arm_cfft_radix4_init_f32(&scfft,fft_adc_n,0,1); //初始化scfft结构体,设置FFT相关参数
SineWave_Data是产生正弦波的点(子函数)
#include "math.h"
#define n 100
uint16_t DualSine12bit[n];
//num:要在一个正弦波中采集多少点
//*D:创建的一个数组用来存放正弦波各个点的数值的
//U:输出电压的峰值(0~1.5V)
//Pi:3.1415926 自己定义
void SineWave_Data( uint16_t num,uint16_t *D,float U)
{
uint16_t i;
for( i=0;i<num;i++)
{
D[i]=(uint16_t)((U*sin(( 1.0*i/(num-1))*2*3.14159265358979)+U)*4095/3.3);
}
}
FFT定义
#define fft_adc_n 1024 // 采1024个点
uint16_t i;
float adc_data[fft_adc_n*2]={0}; // 存ADC值
float fft_in_adc_data[fft_adc_n*2]={0}; // FFT输入,实部是ADC值,虚部补0
float fft_out_adc_data[fft_adc_n]={0}; // FFT输出
/*********************************************************************
Name : FFT_deal0(short int *data, float *fft_in,int data_length)
Funcation :对波形数据进行补零操作
Parameter : short int *data 波形信号
float *fft_in 输入信号(信号长度应是输出信号的2倍)
int data_length 输出信号长度(与波形信号长度保持一致)
Return 无
********************************************************************/
void FFT_deal0(float *data, float *fft_in,int data_length)
{
for(int i = 0;i < data_length;i++)
{
fft_in[2*i] = data[i];
fft_in[2*i+1]=0;
}
}
在ADC回调函数中加入标志位
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
ad_flag=1;
}
主函数
while(i<fft_adc_n) //等待1024个点
{
if(ad_flag==1)
{
i++;
ad_flag=0;
ad_value=HAL_ADC_GetValue(&hadc1);
adc_data[i]=ad_value;
}
}
for(i=0;i<fft_adc_n;i++) //打印采集到得ADC值
{
printf("%.2f\r\n",adc_data[i]);
}
//FFT
FFT_deal0(adc_data,fft_in_adc_data,fft_adc_n); //对采集后的数据进行补0,补足虚部
arm_cfft_radix4_f32(&scfft,fft_in_adc_data); //FFT计算(基4)
arm_cmplx_mag_f32(fft_in_adc_data,fft_out_adc_data,fft_adc_n); //把计算结果复数求模得幅值
printf("ffffffffffffffffffff\r\n");
//打印FFT输出
for(i=0;i<fft_adc_n;i++)
{
fft_out_adc_data[i]=fft_out_adc_data[i]*0.0008056640625;
printf("%.2f\r\n",fft_out_adc_data[i]);
}
while(1);
将采集到得ADC放入Excel打印出来
波形频率50Hz
1.024KHz采样频率采集1024个点,刚好50个波形
![](https://img-blog.csdnimg.cn/img_convert/7c44ca67b660e8522223b6fdd54c9c05.png)
FFT分析后得波形
![](https://img-blog.csdnimg.cn/img_convert/62a9815b534c86509ac3029657c19028.png)
![](https://img-blog.csdnimg.cn/img_convert/d09b30dda49838b5e09488a48d320c60.png)
可以看到频率为50左右,还是比较精准的