STM32失真度仪及示波器
將失真度儀和示波器放到一塊是因為兩個工程需要做的事情非常的相似,只有小部分問題不同。所以將兩個放在一起說,兩個的不同點我會標注出來。
信號系統中的“失真度”定義為全部諧波能量與基波能量之比的平方根值。失真有多種:諧波失真、互調失真、相位失真等等。我們通常所說的失真度的技術術語為總諧波失真,簡稱THD。
失真度的測量方法
1.基波抑制法
2.諧波分析
3.模擬法
4.數字化方法
數字化方法是指先通過將信號數字化并送入計算機,在由計算機計算出失真度的測量方法。根據失真度的計算方法可分為FFT法和曲線擬合法。
利用stm32肯定是采用FFT(快速傅里葉變換)進行測量。
一、THD基本定義公式:
THD的基本定義是功率比例關系,各次諧波功率值和與基波功率之比的百分比表示
二、THD等效關系公式:
三、THD近似測量公式:
在數字信號處理中,學習了FFT是時域一頻域變換分析中最基本的方法之一,是非常重要的一個概念。這里對FFT就不再過多介紹。
失真度儀代碼整體思路
利用STM32將一個正弦波采集64或者256或者1024個點,一定是要保證最少采滿一個周期,之所以采64或者256或者1024點是因為,st官方提供了一個FFT變換的庫,鏈接:https://pan.baidu.com/s/1rVO6uYRk7eL2yID2U8IF9Q?提取碼:mhsr,現在官方已經下載不了了,我把這個庫的文件分享給大家。
采好點后利用DMA傳輸(減輕CPU負擔),做FFT變換,做完以后直接套用THD計算公式計算失真度。
示波器代碼整體思路
示波器工作和失真度儀非常雷同,采點,采好后利用DMA傳輸進行FFT,然后得到一些想要的值,比如最大值,最小值,信號的頻率等等。
需要注意的是,這里AD采集需要配置為定時器觸發(fā),這樣的話可以定時器定時時間就是 adc的采樣時間,采樣頻率>2*信號頻率。后續(xù)再通過按鍵調整采樣率,這樣2021年電賽A題不就出來了(電賽用的TI板子)。
閑話少說,我們上代碼
失真度儀:
adc配置
?#include "adc.h"
//初始化定時器?? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ? ??
void ?Adc_Init(void)
{ ?? ?
?? ?ADC_InitTypeDef ADC_InitStructure;?
?? ?GPIO_InitTypeDef GPIO_InitStructure;
?? ?RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_ADC1, ENABLE );?? ? ?//使能ADC1通道時鐘
?
?? ?RCC_ADCCLKConfig(RCC_PCLK2_Div6); ? //設置ADC分頻因子6 72M/6=12,ADC最大時間不能超過14M
?? ?//PA6 作為模擬通道輸入引腳 ? ? ? ? ? ? ? ? ? ? ? ??
?? ?GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
?? ?GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;?? ??? ?//模擬輸入
?? ?GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
?? ?GPIO_Init(GPIOA, &GPIO_InitStructure);?? ?
?? ?ADC_DeInit(ADC1); ?//復位ADC1,將外設 ADC1 的全部寄存器重設為缺省值
?? ?ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;?? ?//ADC工作模式:ADC1工作在獨立模式
?? ?ADC_InitStructure.ADC_ScanConvMode = DISABLE;?? ?//模數轉換工作在單通道模式
?? ?ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;?? ?//模數轉換工作在非連續(xù)轉換模式
?? ?ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_CC2;?? ?//轉換由定時器2的通道2觸發(fā)(只有在上升沿時可以觸發(fā))
?? ?ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;?? ?//ADC數據右對齊
?? ?ADC_InitStructure.ADC_NbrOfChannel = 1;?? ?//順序進行規(guī)則轉換的ADC通道的數目
?? ?ADC_Init(ADC1, &ADC_InitStructure);?? ?//根據ADC_InitStruct中指定的參數初始化外設ADCx的寄存器 ??
?? ?ADC_Cmd(ADC1, ENABLE);?? ?//使能指定的ADC1
?? ?
?? ?ADC_DMACmd(ADC1, ENABLE);?? ?//ADC的DMA功能使能
?? ?
?? ?ADC_ResetCalibration(ADC1);?? ?//使能復位校準 ?
?? ??
?? ?ADC_RegularChannelConfig(ADC1, ADC_Channel_6, 1, ADC_SampleTime_1Cycles5 );//ADC1通道6,采樣時間為239.5周期?? ??
?? ??
?? ?ADC_ResetCalibration(ADC1);//復位較準寄存器
?? ??
?? ?while(ADC_GetResetCalibrationStatus(ADC1));?? ?//等待復位校準結束
?? ?
?? ?ADC_StartCalibration(ADC1);?? ? //開啟AD校準
?
?? ?while(ADC_GetCalibrationStatus(ADC1));?? ? //等待校準結束
?
?? ?ADC_SoftwareStartConvCmd(ADC1, ENABLE);?? ??? ?//使能指定的ADC1的軟件轉換啟動功能
}?? ??? ??? ?
?ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_CC2;?? ?//轉換由定時器2的通道2觸發(fā)(只有在上升沿時可以觸發(fā))
一定要注意這句,配置為定時器觸發(fā)。
將采集好的值作為一個數組調用以上函數進行FFT變換,然后套用公式計算失真度
//計算總諧振失真函數
void GetTHD()
{
?? ?unsigned short i=20;
?? ?float ?Uo1,Uo2,Uo3,Uo4,Uo5;
?? ?double THD,thd_fz=0,thd_fm=0;
?? ?Uo1=Mag[100];
?? ?Uo2=Mag[200];
?? ?Uo3=Mag[300];
?? ?Uo4=Mag[400];
?? ?Uo5=Mag[500];
?? ?thd_fm=Uo1;
?? ?thd_fz=Uo2*Uo2 + Uo3*Uo3 + Uo4*Uo4 + Uo5*Uo5;
?? ?thd_fz=sqrt(thd_fz);
?? ?THD=thd_fz/thd_fm*100;
?? ?sprintf((unsigned char *)temp1,"THD:%5lf%%",THD);
}
接下來再說一下調整采樣率,將按鍵設置為中斷線,按下按鍵進入中斷,在中斷服務函數中,改變定時器定時時間就改變了采樣率,在標準庫里,有這樣一個函數:
void TIM_PrescalerConfig(TIM_TypeDef* TIMx, uint16_t Prescaler, uint16_t TIM_PSCReloadMode)
{
? /* Check the parameters */
? assert_param(IS_TIM_ALL_PERIPH(TIMx));
? assert_param(IS_TIM_PRESCALER_RELOAD(TIM_PSCReloadMode));
? /* Set the Prescaler value */
? TIMx->PSC = Prescaler;
? /* Set or reset the UG Bit */
? TIMx->EGR = TIM_PSCReloadMode;
}
很顯然,這個函數可以改變定時器的pre,即adc的采樣率。
總結一下,adc使用定時器觸發(fā)設置采樣率,DMA傳輸進行FFT變換,可是ST官方提供的FFT庫只有64點,256點和1024點。如果我想做32點的怎么辦,如果我想做512點的怎么辦,這時候官方的庫就不在適用了,只能自己寫一個FFT變換的函數,我寫了一個蝶形變換的函數,可以自己設置點數,只要是2^n次方就行。
void Mfft(double pr[], double pi[], int n, int k, double fr[], double fi[])
//pr[] ?輸入實部
// ?pi[] ?輸入虛部 ?(時域時未進行傅里葉變換,沒有虛部,此數組全部賦值為0)
// n 采樣點數
// ?k : ?2的k次方=n ? ? ?采樣點數可以自定義
//fr[] ?變換實部
// ?fi[] ?變換虛部
{
? ? int it,m,is,i,j,nv,l0;
? ? double p,q,s,vr,vi,poddr,poddi;
? ? for (it=0; it<=n-1; it++) ?//將pr[0]和pi[0]循環(huán)賦值給fr[]和fi[]
? ? {
? ? ? ? m=it;
? ? ? ? is=0;
? ? ? ? for(i=0; i<=k-1; i++)
? ? ? ? {
? ? ? ? ? ? j=m/2;
? ? ? ? ? ? is=2*is+(m-2*j);
? ? ? ? ? ? m=j;
? ? ? ? }
? ? ? ? fr[it]=pr[is];
? ? ? ? fi[it]=pi[is];
? ? ?}
? ? ?pr[0]=1.0;
? ? ?pi[0]=0.0;
? ? ?p=6.283185306/(1.0*n);
? ? ?pr[1]=cos(p); //w=e^-j2pi/n歐拉公式表
? ? ?pi[1]=-sin(p);
? ? ?for (i=2; i<=n-1; i++) ?//計算pr[]
? ? ?{
? ? ? ? ?p=pr[i-1]*pr[1];
? ? ? ? ?q=pi[i-1]*pi[1];
? ? ? ? ?s=(pr[i-1]+pi[i-1])*(pr[1]+pi[1]);
? ? ? ? ?pr[i]=p-q; pi[i]=s-p-q;
? ? ?}
? ? ?for (it=0; it<=n-2; it=it+2)
? ? ?{
? ? ? ? ? vr=fr[it];
? ? ? ? ? vi=fi[it];
? ? ? ? ? fr[it]=vr+fr[it+1];
? ? ? ? ? fi[it]=vi+fi[it+1];
? ? ? ? ? fr[it+1]=vr-fr[it+1];
? ? ? ? ? fi[it+1]=vi-fi[it+1];
? ? ?}
? ? ?m=n/2;
? ? ?nv=2;
? ? ?for (l0=k-2; l0>=0; l0--) //蝶形計算
? ? ?{
? ? ? ? ? m=m/2;
? ? ? ? ? nv=2*nv;
? ? ? ? ? for (it=0; it<=(m-1)*nv; it=it+nv)
? ? ? ? ? ? ? for (j=0; j<=(nv/2)-1; j++)
? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? p=pr[m*j]*fr[it+j+nv/2];
? ? ? ? ? ? ? ? ? ? q=pi[m*j]*fi[it+j+nv/2];
? ? ? ? ? ? ? ? ? ? s=pr[m*j]+pi[m*j];
? ? ? ? ? ? ? ? ? ? s=s*(fr[it+j+nv/2]+fi[it+j+nv/2]);
? ? ? ? ? ? ? ? ? ? poddr=p-q;
? ? ? ? ? ? ? ? ? ? poddi=s-p-q;
? ? ? ? ? ? ? ? ? ? fr[it+j+nv/2]=fr[it+j]-poddr;
? ? ? ? ? ? ? ? ? ? fi[it+j+nv/2]=fi[it+j]-poddi;
? ? ? ? ? ? ? ? ? ? fr[it+j]=fr[it+j]+poddr;
? ? ? ? ? ? ? ? ? ? fi[it+j]=fi[it+j]+poddi;
? ? ? ? ? ? ? ?}
? ? ? ?}
? ? ? ?for (i=0; i<=n-1; i++)
? ? ? ?{
? ? ? ? ? ?pr[i]=sqrt(fr[i]*fr[i]+fi[i]*fi[i]); ?//計算幅值
? ? ? ?}
? ? ? ?return;
}
之后對自寫的FFT變換做了改進優(yōu)化,鏈接https://pan.baidu.com/s/1V0vs6tk6bd9VdooRwo4iZw?
提取碼:pea8
總結
以上是生活随笔為你收集整理的STM32失真度仪及示波器的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 爱客云服务催生CRM变革,爱客 中小企业
- 下一篇: prtg linux 性能,PRTG 网