日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

MiniSTM32F103实现家庭普通电路中的电流谐波检测

發布時間:2024/3/12 编程问答 46 豆豆
生活随笔 收集整理的這篇文章主要介紹了 MiniSTM32F103实现家庭普通电路中的电流谐波检测 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前言:本人大二小白一個,通過一些小項目來充實下自己,不斷學習,希望能與大家多多交流~

接下來主要分為代碼設計和實際采集操作過程兩部分來講講

111

  • 代碼設計
    • 程序流程圖
    • 配置TIM2、ADC和DMA
    • FFT算法
    • 處理FFT點
    • 尋找直流分量、基波和諧波
  • 采集過程
    • 采集方式
    • 碰到的問題

代碼設計

程序流程圖


配置MiniSTM32的TIM2、ADC和DMA,利用TIM2產生矩形波,在上升沿觸發ADC采集,可通過控制矩形波的周期來控制采樣頻率fs,然后由DMA把采集到的數據送入內存,共采集256個點。

配置TIM2、ADC和DMA

個人認為用庫函數配置板子上的外設比較簡單,把是什么、怎么用這兩方面搞清楚就行了,可參考正點原子的教學視頻。
代碼如下:

#include "adc.h"volatile uint16_t ADC_ConvertedValue[256]; //ADC采樣的數據 u16 DMA1_MEM_LEN; extern complex x[N]; #define ADC1_DR_Address ((u32)0x4001244C) //ADC1的地址//TIM2配置,arr為重加載值,psc為預分頻系數 void TIM2_Init(u16 arr,u16 psc) {TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;TIM_OCInitTypeDef TIM_OCInitStructure;RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2|RCC_APB2Periph_GPIOA, ENABLE); //時鐘使能//定時器TIM2初始化TIM_TimeBaseStructure.TIM_Period = arr; //設置在下一個更新事件裝入活動的自動重裝載寄存器周期的值TIM_TimeBaseStructure.TIM_Prescaler =psc; //設置用來作為TIMx時鐘頻率除數的預分頻值TIM_TimeBaseStructure.TIM_ClockDivision = 0; //設置時鐘分割:TDTS = Tck_timTIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上計數模式TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); //根據指定的參數初始化TIMx的時間基數單位TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //選擇定時器模式:TIM脈沖寬度調制模式1TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比較輸出使能TIM_OCInitStructure.TIM_Pulse = 10;TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //輸出極性:TIM輸出比較極性低TIM_OC2Init(TIM2, & TIM_OCInitStructure); //初始化外設TIM2_CH2TIM_Cmd(TIM2, ENABLE); //使能TIM2 }//DMA1配置 void DMA1_Config() {DMA_InitTypeDef DMA_InitStructure;RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE); //使能ADC1通道時鐘DMA1_MEM_LEN = 256;//DMA1初始化DMA_DeInit(DMA1_Channel1);DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address; //ADC1地址DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&ADC_ConvertedValue; //內存地址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; //內存地址固定DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord ; //外設數據單位DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord ; //內存數據單位DMA_InitStructure.DMA_Mode = DMA_Mode_Circular ; //DMA模式:循環傳輸DMA_InitStructure.DMA_Priority = DMA_Priority_High ; //優先級:高DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //禁止內存到內存的傳輸DMA_Init(DMA1_Channel1, &DMA_InitStructure); //配置DMA1 }void DMA1_Enable() {DMA_Cmd(DMA1_Channel1,DISABLE);DMA_SetCurrDataCounter(DMA1_Channel1,DMA1_MEM_LEN); DMA_Cmd(DMA1_Channel1,ENABLE); }void Adc_Init() {ADC_InitTypeDef ADC_InitStructure;GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_ADC1, ENABLE); //使能GPIOA時鐘//PA6 作為模擬通道輸入引腳GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;GPIO_Init(GPIOA, &GPIO_InitStructure);ADC_DeInit(ADC1); //復位ADC1,將外設 ADC1 的全部寄存器重設為缺省值//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_T2_CC2; //使用外部觸發模式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分頻,即12MHz,(239.5+12.5)/12Mhz=21usADC_Cmd(ADC1,ENABLE);ADC_ResetCalibration(ADC1); //復位校準寄存器while(ADC_GetResetCalibrationStatus(ADC1)); //等待校準寄存器復位完成ADC_StartCalibration(ADC1); //ADC校準while(ADC_GetCalibrationStatus(ADC1)); //等待校準完成ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_239Cycles5); //配置ADC1通道6為239.5個采樣周期 ,采樣周期為21usADC_ExternalTrigConvCmd(ADC1, ENABLE); //設置外部昂一觸發模式使能 }

FFT算法

FFT是最重要,也是最難懂的。由于論述篇幅太長,這里就不說了,請看另一篇文章:對于FFT和DFT的理解
這里給出實現FFT的代碼:

#include "sys.h" #include "fft.h"complex x[N],W[N];//分別為輸入序列,變換核void RaderReverse() {u16 i,j,k;//第一個和最后一個數位置不變,故不處理for(i=1,j=N/2; i<N-1; ++i) {//原始坐標小于變換坐標才交換,防止重復if(i<j){complex temp = x[j];x[j] = x[i];x[i] = temp;}k = N/2; // 用于比較最高位while(k <= j){ // 位判斷為1j = j-k;// 該位變為0k = k/2;// 用于比較下一高位}j = j+k;// 判斷為0的位變為1} } void fft() {int i=0,j=0,k=0,l=0;complex up,down,product;RaderReverse();//w是蝶形系數for(int i=0;i<N;++i) {W[i].real = cos((2*M_PI*i)/N);W[i].img = -sin((2*M_PI*i)/N); }for(i=0; i<log(N)/log(2); ++i) /*log(n)/log(2) 級蝶形運算 stage */ {l = 1<<i;for(j=0;j<N;j+= 2*l) /*一組蝶形運算 group,每組group的蝶形因子乘數不同*/ {for(k=0;k<l;++k) /*一個蝶形運算 每個group內的蝶形運算的蝶形因子乘數成規律變化*/ {//product = x[j+k+l]*W[n*k/2/l]; //一次乘法mul(x[j+k+l], W[N*k/2/l], &product); // up = x[j+k] + product; //一次加法,得蝶形變換上半部分輸出 // down = x[j+k] - product;//一次減法,得蝶形變換下半部分輸出add(x[j+k], product, &up);sub(x[j+k], product, &down);x[j+k] = up;//不占用新的空間,將結果輸出儲存在輸入的位置x[j+k+l] = down;}}} }//尋找基波、高次諧波對應的點 double Amplitude[6], Phase[6];//基波幅值, 相位 volatile u16 j, m, k;//已知基波頻率,采樣頻率,采樣總點數 double I2,I1; void Calculate_Wave() {volatile double Max_Amplitude = 0.0;volatile double t =0.0;// m = j*256/4339+1;//計算基波頻率對應的點 // m = 0;//計算直流分量Amplitude[0] = (sqrt(x[0].real*x[0].real+x[0].img*x[0].img))/256;//基波對應的點Phase[0] = 180*atan2(x[0].img, x[0].real)/M_PI;for(k=1; k<52; k++){t = sqrt(x[k].real*x[k].real +x[k].img*x[k].img)/128;if(t > Max_Amplitude){Max_Amplitude = t;j = k;}}if(j != 0){for(k=1; k<6; k++){m = k*j;//計算頻率j對應的點,第一個點為基波頻率Amplitude[k] = (sqrt(x[m].real*x[m].real+x[m].img*x[m].img))/128;//高次諧波對應的點Phase[k] = 180*atan2(x[m].img, x[m].real)/M_PI;}} I2 = (Amplitude[0] + Amplitude[1]*0.7071)/R;I1 = I2 * 100; }void add(complex a,complex b,complex *c) //復數加法計算 {c->real=a.real+b.real;c->img=a.img+b.img; }void mul(complex a,complex b,complex *c) //復數乘法計算 {c->real=a.real*b.real - a.img*b.img;c->img=a.real*b.img + a.img*b.real; }void sub(complex a,complex b,complex *c) //復數減法計算 {c->real=a.real-b.real;c->img=a.img-b.img; }

處理FFT點

把時域信號變換到頻域,最重要的三個量是頻率、幅值和相位,如何根據FFT的結果算得這三個量?

例如某點n所表示的頻率為:Fn=(n-1)*Fs/N,n是第n個點,Fs是采樣頻率,N是采集點數,Fs/N又稱為頻率分辨率,如果采樣頻率Fs為256Hz,采樣點數為256點,則可以分辨到1Hz。
FFT后某點n是復數(a+bi的形式)
幅值:每一個點對應一個頻率,幅值指的是在該頻率下信號分量的幅值。假設原始信號的峰值為A,那么FFT?結果的每個點(除了第一個點直流分量之外)的模值就是A的N/2倍。而第一個點就是直流分量,它的模值就是原直流分量幅值的N倍。
相位:Pn=atan2(b,a),也就是arctan,反正切求得相角。
這里需要說明的是: FFT后得到的相位實際上是cos()的相位,因為這個FFT過程,根據慣例設定cos(x)的初始相位為0,sin(x)的初始相位為-90°。

尋找直流分量、基波和諧波

直流分量頻率為0,FFT后對應的是第一個頻率點,直接計算即可。
如何尋找基波?結合頻譜圖特性來看,基波的幅值是最大的,所以在這里采用的方法是尋找幅值最大所對應的頻率點,該頻率點對應的頻率即是基波頻率,由于已知市電的頻率為50Hz,所以這里為了節省計算量只在前52個點里面找幅值最大的點。
已知基波,那么諧波頻率是基波頻率的整數倍,可直接算出相關的頻率點。

采集過程

采集方式

由于測試對象是家庭普通電路,需要用到開口式電流互感器,非接觸式,直接夾在單根火線或者零線上。

互感器二次側電阻越小越好,因為這里ADC+電阻的組合其實相當于一個電流表,互感器處于工作狀態時,二次側接近短路。
ADC所讀取的值就是采集點值,FFT后通過流壓關系(歐姆定律)和變比關系即可推出一次側原電路的電流值。

碰到的問題

1.這里需要注意,一般的家用電器(至少我家的是)火線零線都是合并在一條線里面的,用一根粗電線來包含單獨的火線零線,電流互感器直接測這跟粗電線是測不到數據的,因為互感器(變壓器)的原理是利用電流激發的磁場效應,如果互感器同時測火線零線,電流激發的磁場相互抵消,互感器不工作。
2.電流互感器的電流變比不要過大,比如我用的是1000/5A的,變比200,用來測只有最大只有幾十安的電路不合適,因為二次側的電流太小了,影響ADC采集。為了增大二次側電流,我采用的方法是增加一次側纏繞匝數,從原本的單根火線穿入改成兩根火線穿入(還是同一根火線),匝數比從原來的1:200變成了2:200,電流變比降低到了100,這樣二次側的電流就增大了。

總結

以上是生活随笔為你收集整理的MiniSTM32F103实现家庭普通电路中的电流谐波检测的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。