STM32失真度仪及示波器
將失真度儀和示波器放到一塊是因?yàn)閮蓚€(gè)工程需要做的事情非常的相似,只有小部分問題不同。所以將兩個(gè)放在一起說,兩個(gè)的不同點(diǎn)我會(huì)標(biāo)注出來。
信號(hào)系統(tǒng)中的“失真度”定義為全部諧波能量與基波能量之比的平方根值。失真有多種:諧波失真、互調(diào)失真、相位失真等等。我們通常所說的失真度的技術(shù)術(shù)語為總諧波失真,簡稱THD。
失真度的測(cè)量方法
1.基波抑制法
2.諧波分析
3.模擬法
4.數(shù)字化方法
數(shù)字化方法是指先通過將信號(hào)數(shù)字化并送入計(jì)算機(jī),在由計(jì)算機(jī)計(jì)算出失真度的測(cè)量方法。根據(jù)失真度的計(jì)算方法可分為FFT法和曲線擬合法。
利用stm32肯定是采用FFT(快速傅里葉變換)進(jìn)行測(cè)量。
一、THD基本定義公式:
THD的基本定義是功率比例關(guān)系,各次諧波功率值和與基波功率之比的百分比表示
二、THD等效關(guān)系公式:
三、THD近似測(cè)量公式:
在數(shù)字信號(hào)處理中,學(xué)習(xí)了FFT是時(shí)域一頻域變換分析中最基本的方法之一,是非常重要的一個(gè)概念。這里對(duì)FFT就不再過多介紹。
失真度儀代碼整體思路
利用STM32將一個(gè)正弦波采集64或者256或者1024個(gè)點(diǎn),一定是要保證最少采滿一個(gè)周期,之所以采64或者256或者1024點(diǎn)是因?yàn)?#xff0c;st官方提供了一個(gè)FFT變換的庫,鏈接:https://pan.baidu.com/s/1rVO6uYRk7eL2yID2U8IF9Q?提取碼:mhsr,現(xiàn)在官方已經(jīng)下載不了了,我把這個(gè)庫的文件分享給大家。
采好點(diǎn)后利用DMA傳輸(減輕CPU負(fù)擔(dān)),做FFT變換,做完以后直接套用THD計(jì)算公式計(jì)算失真度。
示波器代碼整體思路
示波器工作和失真度儀非常雷同,采點(diǎn),采好后利用DMA傳輸進(jìn)行FFT,然后得到一些想要的值,比如最大值,最小值,信號(hào)的頻率等等。
需要注意的是,這里AD采集需要配置為定時(shí)器觸發(fā),這樣的話可以定時(shí)器定時(shí)時(shí)間就是 adc的采樣時(shí)間,采樣頻率>2*信號(hào)頻率。后續(xù)再通過按鍵調(diào)整采樣率,這樣2021年電賽A題不就出來了(電賽用的TI板子)。
閑話少說,我們上代碼
失真度儀:
adc配置
?#include "adc.h"
//初始化定時(shí)器?? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ? ??
void ?Adc_Init(void)
{ ?? ?
?? ?ADC_InitTypeDef ADC_InitStructure;?
?? ?GPIO_InitTypeDef GPIO_InitStructure;
?? ?RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_ADC1, ENABLE );?? ? ?//使能ADC1通道時(shí)鐘
?
?? ?RCC_ADCCLKConfig(RCC_PCLK2_Div6); ? //設(shè)置ADC分頻因子6 72M/6=12,ADC最大時(shí)間不能超過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); ?//復(fù)位ADC1,將外設(shè) ADC1 的全部寄存器重設(shè)為缺省值
?? ?ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;?? ?//ADC工作模式:ADC1工作在獨(dú)立模式
?? ?ADC_InitStructure.ADC_ScanConvMode = DISABLE;?? ?//模數(shù)轉(zhuǎn)換工作在單通道模式
?? ?ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;?? ?//模數(shù)轉(zhuǎn)換工作在非連續(xù)轉(zhuǎn)換模式
?? ?ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_CC2;?? ?//轉(zhuǎn)換由定時(shí)器2的通道2觸發(fā)(只有在上升沿時(shí)可以觸發(fā))
?? ?ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;?? ?//ADC數(shù)據(jù)右對(duì)齊
?? ?ADC_InitStructure.ADC_NbrOfChannel = 1;?? ?//順序進(jìn)行規(guī)則轉(zhuǎn)換的ADC通道的數(shù)目
?? ?ADC_Init(ADC1, &ADC_InitStructure);?? ?//根據(jù)ADC_InitStruct中指定的參數(shù)初始化外設(shè)ADCx的寄存器 ??
?? ?ADC_Cmd(ADC1, ENABLE);?? ?//使能指定的ADC1
?? ?
?? ?ADC_DMACmd(ADC1, ENABLE);?? ?//ADC的DMA功能使能
?? ?
?? ?ADC_ResetCalibration(ADC1);?? ?//使能復(fù)位校準(zhǔn) ?
?? ??
?? ?ADC_RegularChannelConfig(ADC1, ADC_Channel_6, 1, ADC_SampleTime_1Cycles5 );//ADC1通道6,采樣時(shí)間為239.5周期?? ??
?? ??
?? ?ADC_ResetCalibration(ADC1);//復(fù)位較準(zhǔn)寄存器
?? ??
?? ?while(ADC_GetResetCalibrationStatus(ADC1));?? ?//等待復(fù)位校準(zhǔn)結(jié)束
?? ?
?? ?ADC_StartCalibration(ADC1);?? ? //開啟AD校準(zhǔn)
?
?? ?while(ADC_GetCalibrationStatus(ADC1));?? ? //等待校準(zhǔn)結(jié)束
?
?? ?ADC_SoftwareStartConvCmd(ADC1, ENABLE);?? ??? ?//使能指定的ADC1的軟件轉(zhuǎn)換啟動(dòng)功能
}?? ??? ??? ?
?ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_CC2;?? ?//轉(zhuǎn)換由定時(shí)器2的通道2觸發(fā)(只有在上升沿時(shí)可以觸發(fā))
一定要注意這句,配置為定時(shí)器觸發(fā)。
將采集好的值作為一個(gè)數(shù)組調(diào)用以上函數(shù)進(jìn)行FFT變換,然后套用公式計(jì)算失真度
//計(jì)算總諧振失真函數(shù)
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);
}
接下來再說一下調(diào)整采樣率,將按鍵設(shè)置為中斷線,按下按鍵進(jìn)入中斷,在中斷服務(wù)函數(shù)中,改變定時(shí)器定時(shí)時(shí)間就改變了采樣率,在標(biāo)準(zhǔn)庫里,有這樣一個(gè)函數(shù):
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;
}
很顯然,這個(gè)函數(shù)可以改變定時(shí)器的pre,即adc的采樣率。
總結(jié)一下,adc使用定時(shí)器觸發(fā)設(shè)置采樣率,DMA傳輸進(jìn)行FFT變換,可是ST官方提供的FFT庫只有64點(diǎn),256點(diǎn)和1024點(diǎn)。如果我想做32點(diǎn)的怎么辦,如果我想做512點(diǎn)的怎么辦,這時(shí)候官方的庫就不在適用了,只能自己寫一個(gè)FFT變換的函數(shù),我寫了一個(gè)蝶形變換的函數(shù),可以自己設(shè)置點(diǎn)數(shù),只要是2^n次方就行。
void Mfft(double pr[], double pi[], int n, int k, double fr[], double fi[])
//pr[] ?輸入實(shí)部
// ?pi[] ?輸入虛部 ?(時(shí)域時(shí)未進(jìn)行傅里葉變換,沒有虛部,此數(shù)組全部賦值為0)
// n 采樣點(diǎn)數(shù)
// ?k : ?2的k次方=n ? ? ?采樣點(diǎn)數(shù)可以自定義
//fr[] ?變換實(shí)部
// ?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++) ?//計(jì)算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--) //蝶形計(jì)算
? ? ?{
? ? ? ? ? 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]); ?//計(jì)算幅值
? ? ? ?}
? ? ? ?return;
}
之后對(duì)自寫的FFT變換做了改進(jìn)優(yōu)化,鏈接https://pan.baidu.com/s/1V0vs6tk6bd9VdooRwo4iZw?
提取碼:pea8
總結(jié)
以上是生活随笔為你收集整理的STM32失真度仪及示波器的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 爱客云服务催生CRM变革,爱客 中小企业
- 下一篇: 爱客ikcrm企业级应用的“免费”是鸡汤