利用STM32ZET6 进行FFT采集频率

 

准备电赛的中,尝试了几种测量频率的方法,也参考了一些博主,没有一种可以测量范围很广的方法,而且大多数都要豆豆下载,本人属于看见要给钱就会直接撤退的,当然也不是批判官方,毕竟还是有许多好东西的。下面的代码都是我在准备比赛时写的,可以测量。如果大佬们有什么更好的方法可以指正一下。 (时隔第一次写这篇文章 说一下最终结果 2023年全国电子设计大赛 省一)在自己和队友摸索中得到这个成绩还是不错了。

平台:stm32F103

FFT(快速傅里叶变换)是一种高效的数字信号处理算法,用于将一个信号从时域(即时间域)转换为频域(即频率域),并提取信号中的频率信息。FFT算法是由美国数学家J.W. Cooley和J.W. Tukey于1965年首次提出的。

FFT算法可以大幅加速傅里叶变换的计算,将计算复杂度从O(n^2)降低到O(n log n)。这使得FFT成为数字信号处理、通信、图像处理等领域中广泛使用的算法之一。

在数字信号处理中,FFT算法通常用于谱分析、信号过滤、滤波器设计、图像处理等领域。例如,人们可以通过FFT算法将音频信号转换为频谱图,以便分析它的频率成分,或者通过FFT算法将图像转换为频域图像,并使用频域滤波器进行图像去噪或增强等处理操作。

学习一个新知识就像谈恋爱一样,慢慢了解才是最真诚的。

步入正题 我们先来一个最简单的一步一步深入了解,最终抱得美人归。

1.输入捕获模式

首先先引入一个简单的概念

bf7fff65383d432d992c270dc81ccf31.png本节使用的就是测周法  废话不多说直接上代码

#include "stm32f10x.h"                  // 头文件

void PWM_Init(void)
{
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);  // 使能TIM2外设时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);  // 使能GPIOA外设时钟

    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  // GPIO引脚配置为复用推挽输出模式
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;  // 配置GPIOA的引脚0(TIM2通道1)
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  // GPIO速度设置为50MHz
    GPIO_Init(GPIOA, &GPIO_InitStructure);  // 初始化GPIOA端口

    TIM_InternalClockConfig(TIM2);  // 使用内部时钟作为TIM2的时钟源

    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
    TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;  // 时钟分频设为不分频
    TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;  // 计数器向上计数模式
    TIM_TimeBaseInitStructure.TIM_Period = 100 - 1;  // 设定周期为100(ARR寄存器的值)
    TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1;  // 预分频器设为719(PSC寄存器的值)
    TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);  // 初始化TIM2的时间基准设置

    TIM_OCInitTypeDef TIM_OCInitStructure;
    TIM_OCStructInit(&TIM_OCInitStructure);
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;  // 输出比较模式设为PWM模式1
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;  // 输出极性设为高电平有效
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;  // 输出使能
    TIM_OCInitStructure.TIM_Pulse = 0;  // 初始占空比设为0(CCR寄存器的值)
    TIM_OC1Init(TIM2, &TIM_OCInitStructure);  // 初始化TIM2的通道1输出比较配置

    TIM_Cmd(TIM2, ENABLE);  // 使能TIM2计数器
}

void PWM_SetCompare1(uint16_t Compare)  //调节ccr的值控制占空比 = ccr/arr
{
    TIM_SetCompare1(TIM2, Compare);  // 设置TIM2通道1的比较值
}

void PWM_SetPrescaler(uint16_t Prescaler) //改变psc大小 决定pwm频率
{
    TIM_PrescalerConfig(TIM2, Prescaler, TIM_PSCReloadMode_Immediate);  // 设置TIM2的预分频值
}

配置PWM的频率和占空比,相信有一点32知识的友友都能看懂。我就不做过多的介绍。不懂的可以去学习一下。

输入捕获可以对输入信号的上升沿、下降沿、双边沿进行捕获,用于测量脉宽、频率、占空比当相应的 ICx 信号检测到跳变沿后,将使用捕获/比较寄存器(TIMx_CCRx)来锁存计数器的值。通过检测 TIMx_CHx 上的边沿信号,在边沿信号发生跳变(比如上升沿/下降沿)的时候,将当前定时器的值(TIMx_CNT)存放到对应的通道的捕获/比较寄存(TIMx_CCRx)里面,完成一次捕获。同时还可以配置捕获时是否触发中断/DMA 等。通过对上升沿下降沿捕获的值来计算需要的值
 

#include "stm32f10x.h"                  // 包含设备头文件

void IC_Init(void)
{
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);   // 使能TIM3定时器的时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);  // 使能GPIOA的时钟
    
    GPIO_InitTypeDef GPIO_InitStructure;  // 定义GPIO初始化结构体
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;  // GPIO模式为上拉输入
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;      // GPIO引脚为Pin_6
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  // GPIO速度为50MHz
    GPIO_Init(GPIOA, &GPIO_InitStructure);   // 初始化GPIOA端口
    
    TIM_InternalClockConfig(TIM3);  // 配置TIM3为内部时钟源
    
    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;  // 定义定时器基本参数结构体
    TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;  // 设置时钟分频系数
    TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;  // 设置计数模式为向上计数
    TIM_TimeBaseInitStructure.TIM_Period = 65536 - 1;  // 设置自动重载寄存器(ARR)的值,即计数周期-1
    TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1;  // 设置预分频器(PSC)的值
    TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;  // 设置重复计数器的值
    TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);  // 初始化TIM3
    
    TIM_ICInitTypeDef TIM_ICInitStructure;  // 定义输入捕获参数结构体
    TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;  // 设置输入捕获通道为通道1
    TIM_ICInitStructure.TIM_ICFilter = 0xF;  // 设置输入捕获滤波器的值
    TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;  // 设置输入信号的极性为上升沿触发
    TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;  // 设置输入捕获预分频系数
    TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;  // 设置输入捕获选择直接输入
    TIM_PWMIConfig(TIM3, &TIM_ICInitStructure);  // 配置TIM3为脉宽测量输入模式
    
    TIM_SelectInputTrigger(TIM3, TIM_TS_TI1FP1);  // 选择外部触发源为TI1FP1
    TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset);  // 配置TIM3为从模式并将其复位
    
    TIM_Cmd(TIM3, ENABLE);  // 使能TIM3定时器
}

uint32_t IC_GetFreq(void)
{
    return 1000000 / (TIM_GetCapture1(TIM3) + 1);  // 返回捕获频率:1000000除以(TIM3的捕获寄存器1的值+1)
}

uint32_t IC_GetDuty(void)
{
    return (TIM_GetCapture2(TIM3) + 1) * 100 / (TIM_GetCapture1(TIM3) + 1);  // 返回占空比:(TIM3的捕获寄存器2的值+1)乘以100除以(TIM3的捕获寄存器1的值+1)
}

上述代码最重要的一个步骤便是计算返回频率。

uint32_t IC_GetFreq(void)
{
    return 1000000 / (TIM_GetCapture1(TIM3) + 1);  // 返回捕获频率:1000000除以(TIM3的捕获寄存器1的值+1)
}

从此段代码我们可以看出返回值的大小为72M时钟/PSC(预分频)=1MHZ,在此基础上除以输入捕获的值其中 TIM_GetCapture1(TIM3) 是用于获取 TIM3 定时器捕获寄存器 1(Capture Register 1)的值。该寄存器记录了输入信号引发的定时器计数器的值。在这段代码中,+ 1 是为了避免除以零的情况发生。如果不加 + 1,当捕获寄存器的值为 0 时,计算结果将导致除以零错误。通过增加 1,即使捕获寄存器的值为 0,也能避免除以零的错误,并且不会改变实际计算的结果。因此,表达式 TIM_GetCapture1(TIM3) + 1 的目的是获取捕获寄存器 1 的值,并保证计算的安全性。

(由上面代码可以很清楚的发现我们这里设置的是上升沿捕获)当然如果你想要其他方式触发也是可以的,这里我们简单看一下,我们可以跳转到定义去查看

/** @defgroup TIM_Input_Capture_Polarity 
  * @{
  */

#define  TIM_ICPolarity_Rising             ((uint16_t)0x0000)
#define  TIM_ICPolarity_Falling            ((uint16_t)0x0002)
#define  TIM_ICPolarity_BothEdge           ((uint16_t)0x000A)
#define IS_TIM_IC_POLARITY(POLARITY) (((POLARITY) == TIM_ICPolarity_Rising) || \
                                      ((POLARITY) == TIM_ICPolarity_Falling))
#define IS_TIM_IC_POLARITY_LITE(POLARITY) (((POLARITY) == TIM_ICPolarity_Rising) || \
                                           ((POLARITY) == TIM_ICPolarity_Falling)|| \
                                           ((POLARITY) == TIM_ICPolarity_BothEdge))       

 这段代码定义了用于定时器输入捕获极性的宏和相应的判断宏。

  • TIM_ICPolarity_Rising:上升沿触发模式,表示输入信号在上升沿触发捕获。
  • TIM_ICPolarity_Falling:下降沿触发模式,表示输入信号在下降沿触发捕获。
  • TIM_ICPolarity_BothEdge:双边沿触发模式,表示输入信号在上升沿和下降沿都会触发

代码中 TIM_PWMIConfig(TIM3, &TIM_ICInitStructure);  // 配置TIM3为脉宽测量输入模式得到占空比的函数就不做过多解释,感兴趣的小伙伴可以去了解一下PWMI的内涵,也就是

247f17ac860440acbdb1db92224d5aa6.png如果在直连过程中配置一个通道那么另一个通道就会是其的相反捕获方式

   占空比

uint32_t IC_GetDuty(void)
{
    return (TIM_GetCapture2(TIM3) + 1) * 100 / (TIM_GetCapture1(TIM3) + 1);  // 返回占空比:(TIM3的捕获寄存器2的值+1)乘以100除以(TIM3的捕获寄存器1的值+1)
}

具体而言,CCR2(TIM_GetCapture2(TIM3) + 1)代表高电平信号的结束时间,CCR1(TIM_GetCapture1(TIM3) + 1)代表一个周期的时间。将结束时间加1是为了避免除零错误。然后,将结束时间除以周期时间,得到一个小数值表示占空比。乘以100后,将小数值转换为百分比形式,并将结果作为uint32_t类型返回。这种计算方法常用于测量脉冲信号的占空比,例如在电子设备中对PWM(脉宽调制)信号进行控制和监测等应用场景。

好了输入捕获就讲到这里,接下来就是本节的重头戏。

2.FFT测频率

我所利用的方法也是FFT+ADC+DMA进行测量 ,借鉴一些博主的所测资料。

优缺点

1、fft方式测频率,峰峰值可以最低为20mV,输入捕获方式,峰峰值需要达到七、八百mV以上,才可以精准测量;
2、程序的测量精度利用信号发生器进行了验证(下面表格的数据基本是在峰峰值20mV下测得,峰峰值更大些精度会更高些。

026b974825fc4e09b882e6be896abf1a.png

我们要明白用stm32f103自带的12位ADC进行数据采集,FFT之后如果要获取信号频率、幅度等信息还要知道采样频率Fs,因此一般都是用定时器触发ADC采集,再用DMA进行搬运,定时器时间可以自己设置,采样频率也就知道了。我使用定时器触发ADC采集,采样频率为被测频率的2倍。并且将值储存在一个1024大小的数组里。然后使用stm32官方自带的FFT库,调用一个函数,将1024个数据传进去,然后将出来的数据再进行一些简单的分析得到频率与幅值的信息。有关FFT的具体原理与数据意义这里不细讲。推荐CSDN的文章https://blog.csdn.net/zhaopeizhaopeipei/article/details/53908238
具体配置如下(只贴出关键部分):

定时器配置

/******************************************************************
函数名称:TIM2_PWM_Init(u16 arr,u16 psc)
函数功能:定时器2,PWM输出模式初始化函数
参数说明: arr:重装载值
		     psc:预分频值
备    注:通过TIM2-CH2的PWM输出触发ADC采样
*******************************************************************/ 	
void TIM2_PWM_Init(u16 arr,u16 psc)
{  
	GPIO_InitTypeDef GPIO_InitStructure;
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	TIM_OCInitTypeDef  TIM_OCInitStructure;//定时器比较通道配置
	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);	//使能定时器2时钟
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA  | RCC_APB2Periph_AFIO, ENABLE);  //使能GPIO外设和AFIO复用功能模块时钟
 
   //设置该引脚为复用输出功能,输出TIM2 CH2的PWM脉冲波形	GPIOA.1
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; //PA1,TIM_CH2
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIO
 
   //初始化TIM3
	TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
	TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值 
	TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
	
	//初始化TIM2 Channel 2 PWM模式	 
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式2
 	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能,可以选择是否启用比较输出
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //输出极性:TIM输出比较极性低
	TIM_OCInitStructure.TIM_Pulse=1000;//比较匹配值,用于设置比较通道的比较值 // CCR
	TIM_OC2Init(TIM2, &TIM_OCInitStructure);  //根据T指定的参数初始化外设TIM2

	TIM_CtrlPWMOutputs(TIM2, ENABLE);//使能PWM输出
	
	TIM_Cmd(TIM2, ENABLE);  //使能TIM2
}

将PSC和ARR配置为形参,便于修改采样频率。然后进行配置DMA缓存所采集到的ADC数据

#include "dma.h"
#include "usart.h"

/******************************************************************
函数名称:MYDMA1_Config()
函数功能:DMA1初始化配置
参数说明:DMA_CHx:DMA通道选择
		 cpar:DMA外设ADC基地址
		 cmar:DMA内存基地址
		 cndtrDMA通道的DMA缓存的大小
备    注:
*******************************************************************/
void MYDMA1_Config(DMA_Channel_TypeDef* DMA_CHx,u32 cpar,u32 cmar,u16 cndtr)
{
	DMA_InitTypeDef DMA_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	
 	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);	//使能DMA传输
	
  DMA_DeInit(DMA_CHx);   //将DMA的通道1寄存器重设为缺省值
	DMA_InitStructure.DMA_PeripheralBaseAddr = cpar;  //DMA外设ADC基地址
	DMA_InitStructure.DMA_MemoryBaseAddr = cmar;  //DMA内存基地址
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;  //数据传输方向,从外设读取发送到内存//
	DMA_InitStructure.DMA_BufferSize = cndtr;  //DMA通道的DMA缓存的大小
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;  //外设地址寄存器不变 始终转运同一个ADC地址
	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;  //工作在循环模式
	DMA_InitStructure.DMA_Priority = DMA_Priority_High; //DMA通道 x拥有高优先级 
	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;  //DMA通道x没有设置为内存到内存传输
	DMA_Init(DMA_CHx, &DMA_InitStructure);  //ADC1匹配DMA通道1
	
	DMA_ITConfig(DMA1_Channel1,DMA1_IT_TC1,ENABLE);	//使能DMA传输中断	
	
	//配置中断优先级
	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);//使能DMA通道
}
 

不难看出 cpar:DMA外设ADC基地址
               cmar:DMA内存基地址
               cndtrDMA通道的DMA缓存的大小

通过设置其地址便可以将ADC采集到的值转运到DMA中,随后在进行FFT变换。

主函数配置如下:

	MYDMA1_Config(DMA1_Channel1,(u32)&ADC1->DR,(u32)&currentadc,1);//将ADC采集的数据转运到DMA中

 配置DMA采样ADC值 将采集到的值通过STM32自带的DSP库进行FFT运算。

void DMA1_Channel1_IRQHandler(void)
{
	u16 i = 0;
	
  DMA_Cmd(DMA1_Channel1, DISABLE);
  adc_flag=1;
  DMA_ClearITPendingBit(DMA_IT_HT);
  DMA_ClearITPendingBit(DMA1_IT_TC1);
	for(i=0;i<NPT;i++)
		InBufArray[i] = ((signed short)(ADC_Value[i])) << 16;		
					 																												
	cr4_fft_1024_stm32(OutBufArray, InBufArray, NPT);
	//cr4_fft_256_stm32(OutBufArray, InBufArray, NPT);
	GetPowerMag();	
	DMA_Cmd(DMA1_Channel1, ENABLE);
}

这里贴出运算幅值代码(官方给出)

double sqr(double x)
{
  return x * x;
}

void GetPowerMag()
{
    signed short lX,lY;
    float X,Y,Mag;//实部,虚部,各频率幅值,最大幅值
    unsigned short i;
    for(i=0; i<NPT/2; i++)
    {
        lX  = (OutBufArray[i] << 16) >> 16;
        lY  = (OutBufArray[i] >> 16);
        
        //除以32768再乘65536是为了符合浮点数计算规律
        X = NPT * ((float)lX) / 32768;
        Y = NPT * ((float)lY) / 32768;
        Mag = sqrt(X * X + Y * Y) / NPT;
        if(i == 0)
            MagBufArray[i] = (unsigned long)(Mag * 32768);
        else
            MagBufArray[i] = (unsigned long)(Mag * 65536);
   }
}

此时FFT运算采集基础已经完成,接下来就可以进行运算了,通过幅值最大的点得到对应的频率

这里我就不做过多介绍了 由于作此篇文章是放假归来 发现没有保存和发布导致我在以前基础上进行了修改但是大致原理如此,我就把我们队的运算代码给出来吧(识别三角波和正弦波)

void select_max(void)   //发挥部分三角波与正弦波识别
{
	int i,j;
	float k1,m,m2;
  u16 temp2[5];    //缓存次大值
  u16 temp1[5];    //缓存最大值

 //LCD_ShowFloat(1,1,12,FreB,1,2);//浮点显示

	float aMax =0.0,aSecondMax = 0.0,aThirdMax = 0.0,afourdMax=0.0;
	float fMax =0.0,fThirdMax = 0.0,ffourdMaxx = 0.0;
	int nMax=0,nSecondMax=8,nThirdMax=0,nfourdMax;
	u8 firstf,secondf;
	aMax=MagBufArray[1];
//for ( i = 1; i < 100; i++)
	
	
	for ( i = 1; i < NPT/2; i++)//i必须是1,是0的话,会把直流分量加进去!!!!         //求最大值
	{
		
			if (MagBufArray[i]>=aMax)
			{
					aMax = MagBufArray[i]; 
					nMax=i;
					
			}
			
	}
				fMax=(72000000)/((NPT*(ARR+1)*(PSC+1))/nMax);
				FreA=(72000000)/((NPT*(ARR+1)*(PSC+1))/nMax);   // 小频率
				FreA+=2000;
				FreA=(FreA/5000)*5000;


	for ( i = 1; i<NPT/2; i++)                                                       //求次大值
	{
		
			if (i==nMax-4)i=nMax+4;
			if (MagBufArray[i]>aSecondMax)
			{
					aSecondMax = MagBufArray[i]; 
					nSecondMax=i;
			}
	}

      FreB=(72000000)/((NPT*(ARR+1)*(PSC+1))/nSecondMax);//大频率
	FreB+=2000;
	FreB=(FreB/5000)*5000;

//	FreB*=5;
			// LCD_ShowxNum(165,230,k,6,12,0);//A波频率
	
//******************************************频率 幅值 排序******************************************//	 

	
	
nMax<nSecondMax?(firstf=nMax,secondf=nSecondMax,aSecondMax = MagBufArray[nSecondMax],aMax = MagBufArray[nMax]):(firstf=nSecondMax,secondf=nMax,aSecondMax = MagBufArray[nMax],aMax = MagBufArray[nSecondMax]);
	//	nMax<nSecondMax?(firstf=nMax,secondf=nSecondMax,aSecondMax = MagBufArray[nSecondMax]):(firstf=nSecondMax,secondf=nMax,aSecondMax = MagBufArray[nMax]);
				   



for ( i=3*firstf-4; i<3*firstf+4; i++)     //temp值确定A波3次谐波采样范围            //求  
	{
			if (MagBufArray[i]>afourdMax)
			{
					afourdMax = MagBufArray[i]; 
					nfourdMax=i;
			}
	}
//**************************************************************************************************//	 
// 正弦  三角   EER  PIAO           OK 
//  20    25 	            20  40       
for ( i=3*secondf-30; i<3*secondf+30; i++)     //temp值确定A波3次谐波采样范围            //求  
	{
		

			if (MagBufArray[i]>aThirdMax)
			{
					aThirdMax = MagBufArray[i]; 
					nThirdMax=i;
			}
	}
					
	 fThirdMax=(72000000)/((NPT*(ARR+1)*(PSC+1))/nThirdMax); //3次谐波频率

//	//波形判断
	k1=fabs(3*fMax/fThirdMax);        // 基波/3次谐波

//  LCD_ShowxNum(15,170,nMax,4,12,0);//二大值点
//	LCD_ShowxNum(15,190, aMax,4,12,0);//最大值电压
//	
//  LCD_ShowxNum(62,170,nfourdMax,6,12,0);//A波3次谐波点
//	LCD_ShowxNum(65,190,afourdMax,4,12,0);//A波3次谐波点电压
//			
//	LCD_ShowxNum(115,170,nSecondMax,4,12,0);//大值点
//	LCD_ShowxNum(115,190,aSecondMax,4,12,0);//二大值电压
//	
//	LCD_ShowxNum(160,170,nThirdMax,6,12,0);//3次谐波点
//	LCD_ShowxNum(165,190,aThirdMax,4,12,0);//B波3次谐波点电压
//	
//  LCD_ShowChar(215,190,'V',12,0);   //最大值电压

	m=fabs((float)(aSecondMax/aThirdMax));  //幅度比值 判断波形
	
	m2=fabs((float)(aMax/afourdMax));  //幅度比值 判断波形
	
	LCD_ShowString(1,100,40,20,24,"B:");
	
	
//输入波形B的判断	
	if(m<12)  //阈值判断
	{ 
		LCD_ShowString(50,100,80,20,24,"SanJiao");
		flag = 1;  
		
	}
 else
 { 
	 LCD_ShowString(50,100,80,20,24,"    Sin"); 
     flag = 2;
 }
		LCD_ShowString(1,130,40,20,24,"A:");
 
//输入波形A的判断	
	if(m2<16)  //阈值判断
	{ 
		LCD_ShowString(50,130,80,20,24,"SanJiao");
		flag1 = 1;
	}
 else
 { 
	 LCD_ShowString(50,130,80,20,24,"    Sin"); 
   flag1 = 2;
 }
boxing[bo]=flag1;
boxing[bo+2]=flag;
bo++;
 
FREQ1 = FreB<FreA?FreB:FreA;
FREQ =  FreB>FreA?FreB:FreA;
 
if(bo==2){
	
	if(boxing[0]==boxing[1]&&boxing[1]==1){AD9833_SetFrequency(AD9833_REG_FREQ0, AD9833_OUT_TRIANGLE,AD9833_FREQ(FREQ1));}	
    if(boxing[0]==boxing[1]&&boxing[1]==2){AD9833_SetFrequency(AD9833_REG_FREQ0, AD9833_OUT_SINUS, AD9833_FREQ(FREQ1));}

	if(boxing[2]==boxing[3]&&boxing[3]==1){AD98331_SetFrequency(AD9833_REG_FREQ0, AD9833_OUT_TRIANGLE,AD98331_FREQ(FREQ));}
	if(boxing[2]==boxing[3]&&boxing[3]==2){AD98331_SetFrequency(AD9833_REG_FREQ0, AD9833_OUT_SINUS, AD98331_FREQ(FREQ));}
}
 

bo%=2; 



}                                 

这个代码呢只能帮我们达到省一的水平(太可恶了由于笔者想提前准备考研了很多东西没有精讲,需要代码的小伙伴可以联系我哦) 就是下面这个东西(十分简陋)由于没有指导老师全程由我和队友一步一步琢磨真的痛苦。

5896003b95544ee78f7465d1b0199003.jpeg

笔者能力有限 望指正

 

  • 8
    点赞
  • 57
    收藏
    觉得还不错? 一键收藏
  • 26
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 26
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值