SiLabs C8051F120-TBデバッグボード上のDIYバランススタンド



回転するもののバランスを取ることを計画している場合は、ホイール、飛​​行機のプロペラ、空飛ぶ円盤のいずれかです。 それとも、プログラマーの仕事の様子の物語に興味がありますか? バランスのとれたスタンドの作成に関する魅力的なストーリー...





まえがき

スーパーバイザーのドミトリエフ・イヴァン・アレクゼーヴィッチ、エンジニア設計者のアンドレイ・アラポフ、電子技術者のアレクサンダー・トゥラエフ、ギダル・グリゴリエヴィッチに感謝します。 このブースは、緊密に結ばれたチームの成果です。



私は物語から始めます:私は組織のプログラマーとして働いています
それはまったく秘密ではありませんが、関連性はありません、私たちはUAVに従事していると言うだけです
定期的にさまざまな興味深いタスクがあり、航空機のプロペラの高精度のバランスを取ることが必要になりました。 そのようなバランスを取るための機器を購入できることが判明しましたが、費用がかかりますので、私たち自身でそれを行うことにしました。



なぜ必要なのかを少しお話しします。 このネジを取り付けた飛行機は、アイドル(800 rpm)でひどくソーセージでした。 これらは通常、静的および動的にバランスがとられています。 静的なバランスは、回転せずに回転の中心に対してバランスをとることで構成され、動的なバランスは回​​転中にバランスを取ります。



静的バランスに関しては、ここではすべてが明確であり、ねじは回転中心に対して単純にバランスが取られていますが、回転中にねじが振動を開始し始めたときに動的バランスをどうするかについて説明します。



そのようなタスクのために構築された
シンプルなデバイス
バネに取り付けられたフレームが大規模なベースで構成されています。

電気モーターが巨大なベースに取り付けられており、プーリーを介して平衡ネジが取り付けられている軸が回転します。 加速度計もフレームに取り付けられ、ホールセンサーはネジで軸に取り付けられています。 電気モーターは、回転数を制御するchastotnikに接続されています。

SiLabs C8051F120-TBデバッグボードのADCに接続されたアンプを介して、2軸の加速度計が偏差計として使用されました。 回転体が0度を通過する瞬間を捕捉するために、ホールセンサーが設置され、そこからの信号がデバッグボードのもう1つの脚に供給されました。



それで、シンプルなユニットを得ました。
回転体でフレームの加速度を測定し、平衡ねじを回転させるシャフトの0度の通過に関する信号を与えることができます。





/シンプルなデバイスの外観/



このデザインが与えられ、タスクは、 電気テーププラスチシンまたはアラカルの断片が非常に正確に計量された負荷を必要とし、バランスを取るためにプロペラブレードの端にそれを貼り付ける量をプログラムで見つけるように設定されました。 また、便利で直感的なインターフェースを備えたアプリケーションを作成して、5分でその使用方法を理解できるようにします。

そして、私は魅力的な仕事を始めました










最初は1日で管理できると思っていましたが、タスクは非常に簡単です。 しかし、オシロスコープで信号を取得すると、設備全体の振動、電源からの干渉、およびその他のノイズにより、ADCからキャプチャされた信号が均一で理解できないノイズに変わることがわかりました。 よく見ると、明らかな周期的な最大値と最小値を見ることができます。 ソフトウェアとハ​​ードウェアをデバッグするのに約1週間、またはそれ以上かかりましたが、デバイスの精度が目を楽しませ始めました。





/オシロスコープ測定値/



デバッグボードで、読み取り値を取得してCOMポートに送信するプログラムを作成しました。

定義する
コントローラーを構成し、メイン変数を定義し、配列と定数を選択します。 プログラミング用にデバッグボードを準備しています。



#include "c8051f120.h" #define SYSCLK 98000000 //     #define BAUDRATEU0 57600 //  Uart0    RS232  COM  #define SAMPLE_RATE 24500000 // Sample frequency in Hz #define INT_DEC 256 #define SAR_CLK 12250000 //   #define FREQT0 (748*2) //  0 #define BUFADCSIZE 512 //BUFADC sfr16 ADC0 = 0xbe; // ADC0 data sfr16 RCAP2 = 0xca; // Timer2 capture/reload sfr16 RCAP3 = 0xca; // Timer3 capture/reload sfr16 TMR2 = 0xcc; // Timer2 sfr16 TMR3 = 0xcc; // Timer3 bit ProcessFlag = 0, ADCFlag = 0, flFree = 1, flNewADC = 0; // xdata unsigned int BufADC[BUFADCSIZE], ADCcount = 0, RTC = 0, RTCP = 0, int_dec = INT_DEC, tmpA = 0, lastTmp = 0; xdata float Propeller = 0.0, tmp_float; xdata long accumulator = 0L; #define RESETTICK (1496) sbit LED = P1^6; //      sbit BUTTON = P3^7; // //UART0      #define NBFM 50 xdata unsigned char BuferFromModem [NBFM]; xdata unsigned char wBFM, rBFM, marBFM; #define SIZE_BUFFER0 50 xdata char BufferInModem[SIZE_BUFFER0]; xdata int r0, rk; bit flTransmiter; //-----         void OutModem1(unsigned char Data, char i) { BufferInModem[i] = Data | 0x80; } //------------------------------------------------------------------------------ void OutModem2(unsigned int Data, char i) { BufferInModem[i] = (Data & 0x007f)| 0x80; BufferInModem[i+1] = ((Data & 0x3f80) >> 7)| 0x80; } //------------------------------------------------------------------------------ void OutModem4(unsigned long int Data, char i) { BufferInModem[i] = (Data & 0x0000007f)| 0x80; BufferInModem[i+1] = ((Data & 0x3f80) >> 7) | 0x80; BufferInModem[i+2] = ((Data & 0x1fc000) >> 14) | 0x80; BufferInModem[i+3] = ((Data & 0xfe00000)>> 21) | 0x80; } //----       void OSCILLATOR_Init (void) { int loop; char SFRPAGE_SAVE = SFRPAGE; SFRPAGE = CONFIG_PAGE; OSCICN = 0x83; CLKSEL = 0x00; SFRPAGE = CONFIG_PAGE; PLL0CN = 0x00; SFRPAGE = LEGACY_PAGE; FLSCL = 0x10; SFRPAGE = CONFIG_PAGE; PLL0CN |= 0x01; PLL0DIV = 0x01; PLL0FLT = 0x01; PLL0MUL = 0x04; for (loop=0; loop<256; loop++); PLL0CN |= 0x02; while(!(PLL0CN & 0x10)); CLKSEL = 0x02; SFRPAGE = SFRPAGE_SAVE; } /*Init*/ void Init() { //  SFRPAGE = TIMER01_PAGE; TCON = 0x51; TMOD = 0x11; CKCON = 0x18; SFRPAGE = TMR3_PAGE; TMR3CN = 0x04; TMR3CF = 0x08; RCAP3 = -SYSCLK/SAMPLE_RATE; TMR3 = RCAP3; EIE2 &= ~0x01; TR3 = 1; //    Uart SFRPAGE = TMR2_PAGE; TMR2CF = 0x08; // Timer 2 Configuration RCAP2 = - ((long) SYSCLK/BAUDRATEU0/16); TMR2L = 0x00; // Timer 2 Low Byte TMR2H = 0x00; // Timer 2 High Byte TMR2CN = 0x04; // Timer 2 CONTROL TR2 = 1; SFRPAGE = UART0_PAGE; SCON0 = 0x50; SSTA0 = 0x05; ES0 = 1; // ADC() SFRPAGE = ADC0_PAGE; AMX0SL = 0x01; ADC0CN = 0x80; SFRPAGE = ADC0_PAGE; ADC0CN = 0x04; REF0CN = 0x07; AMX0CF = 0x00; AMX0SL = 0x01; ADC0CF = (SYSCLK/SAR_CLK) << 3; ADC0CF |= 0x00; //   PGA gain => 00 = 1 (default), 01 =2, 02 = 4, 03 = 8 EIE2 |= 0x02; // enable ADC interrupts SFRPAGE = ADC0_PAGE; ADC0CN = 0x84; //   SFRPAGE = CONFIG_PAGE; P0MDOUT = 0xFF; P1MDOUT = 0xFF; P2MDOUT = 0xFF; P3MDOUT = 0xFF;*/ XBR0 = 0x44; XBR1 = 0x04; XBR2 = 0x40; //   OSCILLATOR_Init(); //  IE = 0x9B; EIE2 |= 0x02; //  IP = 0x13; EIP2 = 0x02; // }
      
      









主な機能
ここでは、無限ループで絶えず回転し、受信したADC測定値を送信

 //------------------------------------------------------------------- void main(void) { xdata unsigned int i=0, tmpint; WDTCN = 0xde; //  ,     WDTCN = 0xad; //   ,          ,      EA=0; //     Init(); //  i = 0; while(i++ < BUFADCSIZE) { BufADC[i]=0; } EA=1; while(1) { if(RTC>(7*FREQT0)) { IE0=1; } if(ProcessFlag == 1) { ADCFlag = 0; flFree = 0; EIE2 &= ~0x02; //  //     tmpint = ADCcount; ADCcount = 1; while(ADCcount < tmpint) { //Write to UART0-------------------------------------- BufferInModem[0] = 40 | 0x40; BufferInModem[0] &= ~0x80; OutModem2((int)Propeller, 1); OutModem2((int)ADCcount, 3); OutModem2((int)BufADC[ADCcount++],5); OutModem2((int)tmpint, 7); r0 = 0; rk = 9; BufferInModem[rk] = 0; for (i = r0; i < rk; i++ ) BufferInModem[rk] = BufferInModem[rk] ^ BufferInModem[i]; BufferInModem[rk] = BufferInModem[rk] | 0x80; rk++; flTransmiter = 1; SFRPAGE = 0x00; TI0 = 1; RTC=0; while(flTransmiter) { if(RTC>(RESETTICK)) { RTC=0; break; } } RTC=0; LED=0; } i = 0; while(i++ < BUFADCSIZE) { BufADC[i]=0; } ADCcount = 0; ProcessFlag = 0; flFree = 1; } } }
      
      









ホールセンサーが接続されている脚からの割り込みイベントを作成します

中断
ここでは、ホールセンサーからの中断を監視します。

 void INT0 (void) interrupt 0 //         { if(LED!=1) { LED=1; //   } else if(RTC > RTCP) //     ( 0 - 60000 /) { Propeller = Propeller+((60.0*FREQT0/(RTC - RTCP))-Propeller)*1.0; RTCP = RTC; } RTCP=0; RTC=0; if((flFree == 1)&& (ADCFlag == 1)) { ProcessFlag = 1; } else if(!ProcessFlag) EIE2 |= 0x02; //  return; }
      
      









どれだけ時間が経過したかを正確に知るために、タイマーを開始し、その時間をカウント

タイマー
 void TIMER_ISR0 (void) interrupt 1 //     FREQT0 = SYSCLK/65535  { RTC++; return; }
      
      









別のイベントは、入力/出力ポートを使用した作業です。
 void UART0_isr(void) interrupt 4 //    ,        { xdata char SFRPAGE_SAVE = SFRPAGE; SFRPAGE = UART0_PAGE; if (RI0) { BuferFromModem [wBFM++] = SBUF0; //    if(wBFM >= NBFM) { wBFM = 0; marBFM = 1; } RI0 = 0; } if (TI0) { if(r0 < rk) { SBUF0 = BufferInModem[r0++]; //    } else { flTransmiter = 0; } TI0 = 0; } SFRPAGE = SFRPAGE_SAVE; return; }
      
      









ADCから取得したすべてのデータはバッファに書き込まれ、パケット全体を1ターンで転送します。 この方法により、情報の取得中に送信に時間を浪費することがなくなり、結果としてより高速に動作し、より多くのポイントが削除されます。



ADCの中断
ここで、 ADC測定バッファに書き込みます

 void ADC0_ISR (void) interrupt 15 //      { xdata char SFRPAGE_SAVE = SFRPAGE; SFRPAGE = ADC0_PAGE; //  ,   AD0INT = 0; accumulator += ADC0; int_dec--; if (int_dec == 0) { int_dec = INT_DEC; BufADC[ADCcount] = accumulator >> 8; AMX0SL = 0x00; ADCcount++; ADCFlag = 1; if(ADCcount>BUFADCSIZE) { ADCcount=BUFADCSIZE; ProcessFlag=1; EIE2 &= ~0x02; //  } accumulator = 0L; } SFRPAGE = SFRPAGE_SAVE; return; }
      
      









必要な偏差を何らかの方法で分離するために、デスクトップアプリケーションで、以前タンバリンを使用して少し画像を処理するために使用していたフーリエ変換を適用することにしました。

インターフェイスを開発するために、C ++ Builder 6.0を使用しました



ヘッダーのプラグ可能なライブラリ、変数、定数
 //--------------------------------------------------------------------------- #include <vcl.h> #pragma hdrstop #include "balansCom4.h" #include <windows.h> #include <vector.h> #include "fstream.h" #include "math.h" //--------------------------------------------------------------------------- #define assert(ignore)((void)0) #define BUFSIZE 4096 //--------------------------------------------------------------------------- #pragma package(smart_init) #pragma link "CPort" #pragma link "PERFGRAP" #pragma resource "*.dfm" TForm1 *Form1; int N=1024,k=10; ShortComplex arr[4096]; double Amp_F[4096]; double Phase_F[4096]; double Amp_max=0, Phase_max=0; float r=0,rmax=0, fi=0, xx=0, yy=0; float K_flt = 0.00005; float Krmax = 0.05; float kAmp = 0.1; float a=1, b=0; //--------------------------------------------------------------------------- bool perekl=false; struct hComException{}; String tmptxt; float RadMass[365], RadMassMax, j_max; int fiMax, WACHDOG; long data_i=0; long bad = 0; int V, Xlast, A, Alast, Vlast; #define COM "Com5" #define BodRate CBR_57600 #define TIMEOUT 3000 //---------------------------------------------------------------------------
      
      









受信信号から目的の周波数を抽出するには、直接フーリエ変換と逆フーリエ変換が非常に有用であることが証明されました。 データは連続ストリームに注がれ、処理する時間を確保するために、最適化されたバージョン、いわゆるFFTを適用しました。 これは万能薬ではありません。ビデオストリームを処理するには、GPUを並列化して使用することをお勧めしますが、このタスクには非常に適しています。



FFT関数
 static unsigned char reverse256[]= { 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0, 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC, 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6, 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1, 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9, 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3, 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF, }; static long double temp; inline void operator+=(ShortComplex &x, const Complex &y) { x.re += (double)y.re; x.im += (double)y.im; } inline void operator-=(ShortComplex &x, const Complex &y) { x.re -= (double)y.re; x.im -= (double)y.im; } inline void operator*=(Complex &x, const Complex &y) { temp = x.re; x.re = temp * y.re - x.im * y.im; x.im = temp * y.im + x.im * y.re; } inline void operator*=(Complex &x, const ShortComplex &y) { temp = x.re; x.re = temp * y.re - x.im * y.im; x.im = temp * y.im + x.im * y.re; } inline void operator/=(ShortComplex &x, double div) { x.re /= div; x.im /= div; } //array exp(-2*pi*j/2^n) for n= 1,...,32 //exp(-2*pi*j/2^n) = Complex( cos(2*pi/2^n), -sin(2*pi/2^n) ) static Complex W2n[32]={ {-1.00000000000000000000000000000000, 0.00000000000000000000000000000000}, // W2 calculator (copy/paste) : po, ps { 0.00000000000000000000000000000000, -1.00000000000000000000000000000000}, // W4: p/2=o, p/2=s { 0.70710678118654752440084436210485, -0.70710678118654752440084436210485}, // W8: p/4=o, p/4=s { 0.92387953251128675612818318939679, -0.38268343236508977172845998403040}, // p/8=o, p/8=s { 0.98078528040323044912618223613424, -0.19509032201612826784828486847702}, // p/16= { 0.99518472667219688624483695310948, -9.80171403295606019941955638886e-2}, // p/32= { 0.99879545620517239271477160475910, -4.90676743274180142549549769426e-2}, // p/64= { 0.99969881869620422011576564966617, -2.45412285229122880317345294592e-2}, // p/128= { 0.99992470183914454092164649119638, -1.22715382857199260794082619510e-2}, // p/256= { 0.99998117528260114265699043772857, -6.13588464915447535964023459037e-3}, // p/(2y9)= { 0.99999529380957617151158012570012, -3.06795676296597627014536549091e-3}, // p/(2y10)= { 0.99999882345170190992902571017153, -1.53398018628476561230369715026e-3}, // p/(2y11)= { 0.99999970586288221916022821773877, -7.66990318742704526938568357948e-4}, // p/(2y12)= { 0.99999992646571785114473148070739, -3.83495187571395589072461681181e-4}, // p/(2y13)= { 0.99999998161642929380834691540291, -1.91747597310703307439909561989e-4}, // p/(2y14)= { 0.99999999540410731289097193313961, -9.58737990959773458705172109764e-5}, // p/(2y15)= { 0.99999999885102682756267330779455, -4.79368996030668845490039904946e-5}, // p/(2y16)= { 0.99999999971275670684941397221864, -2.39684498084182187291865771650e-5}, // p/(2y17)= { 0.99999999992818917670977509588385, -1.19842249050697064215215615969e-5}, // p/(2y18)= { 0.99999999998204729417728262414778, -5.99211245264242784287971180889e-6}, // p/(2y19)= { 0.99999999999551182354431058417300, -2.99605622633466075045481280835e-6}, // p/(2y20)= { 0.99999999999887795588607701655175, -1.49802811316901122885427884615e-6}, // p/(2y21)= { 0.99999999999971948897151921479472, -7.49014056584715721130498566730e-7}, // p/(2y22)= { 0.99999999999992987224287980123973, -3.74507028292384123903169179084e-7}, // p/(2y23)= { 0.99999999999998246806071995015625, -1.87253514146195344868824576593e-7}, // p/(2y24)= { 0.99999999999999561701517998752946, -9.36267570730980827990672866808e-8}, // p/(2y25)= { 0.99999999999999890425379499688176, -4.68133785365490926951155181385e-8}, // p/(2y26)= { 0.99999999999999972606344874922040, -2.34066892682745527595054934190e-8}, // p/(2y27)= { 0.99999999999999993151586218730510, -1.17033446341372771812462135032e-8}, // p/(2y28)= { 0.99999999999999998287896554682627, -5.85167231706863869080979010083e-9}, // p/(2y29)= { 0.99999999999999999571974138670657, -2.92583615853431935792823046906e-9}, // p/(2y30)= { 0.99999999999999999892993534667664, -1.46291807926715968052953216186e-9}, // p/(2y31)= }; void fft(ShortComplex *x, int T, bool complement) { unsigned int I, J, Nmax, N, Nd2, k, m, mpNd2, Skew; unsigned char *Ic = (unsigned char*) &I; unsigned char *Jc = (unsigned char*) &J; ShortComplex S; ShortComplex *Wstore, *Warray; Complex WN, W, Temp, *pWN; Nmax = 1 << T; //first interchanging for(I = 1; I < Nmax - 1; I++) { Jc[0] = reverse256[Ic[3]]; Jc[1] = reverse256[Ic[2]]; Jc[2] = reverse256[Ic[1]]; Jc[3] = reverse256[Ic[0]]; J >>= (32 - T); if (I < J) { S = x[I]; x[I] = x[J]; x[J] = S; } } //rotation multiplier array allocation Wstore = new ShortComplex[Nmax / 2]; Wstore[0].re = 1.0; Wstore[0].im = 0.0; //main loop for(N = 2, Nd2 = 1, pWN = W2n, Skew = Nmax >> 1; N <= Nmax; Nd2 = N, N += N, pWN++, Skew >>= 1) { //WN = W(1, N) = exp(-2*pi*j/N) WN= *pWN; if (complement) WN.im = -WN.im; for(Warray = Wstore, k = 0; k < Nd2; k++, Warray += Skew) { if (k & 1) { W *= WN; *Warray = W; } else W = *Warray; for(m = k; m < Nmax; m += N) { mpNd2 = m + Nd2; Temp = W; Temp *= x[mpNd2]; x[mpNd2] = x[m]; x[mpNd2] -= Temp; x[m] += Temp; } } } delete [] Wstore; if (complement) { for( I = 0; I < Nmax; I++ ) x[I] /= Nmax; } }
      
      









GUIを準備し、受信用のCOMポートを構成します。

起動および終了関数
 __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { for(int i=0; i<362; i++) RadMass[i]=0; RadMassMax=0; Image1->Canvas->Rectangle(0,0,Image1->Width, Image1->Height); int i=0; float r, fi=0; float xx, yy; while(i<100) { Image1->Canvas->Pen->Color=clGreen; i=i+10; r=i; fi=0; while(fi<360) { fi=fi+1; xx = r*cos(fi) + Image1->Width/2; yy = r*sin(fi) + Image1->Height/2; Image1->Canvas->Ellipse((int)xx,(int)yy, (int)xx+2,(int)yy+2); } } Image1->Canvas->Pen->Color=clBlack; Button2Click(Owner); flEdit=0; hCom = CreateFile(COM,GENERIC_READ | GENERIC_WRITE,0,NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); if( hCom == INVALID_HANDLE_VALUE ) { ShowMessage("Com port error"); CloseHandle(hCom); Stat->SimpleText="Com port error"; } else { SetCommMask(hCom, EV_RXCHAR); SetupComm(hCom, 1500, 1500); CommTimeOuts.ReadIntervalTimeout = MAXDWORD; CommTimeOuts.ReadTotalTimeoutMultiplier = TIMEOUT; CommTimeOuts.ReadTotalTimeoutConstant = TIMEOUT; CommTimeOuts.WriteTotalTimeoutMultiplier = TIMEOUT; CommTimeOuts.WriteTotalTimeoutConstant = TIMEOUT; if(!SetCommTimeouts(hCom, &CommTimeOuts)) { hCom = 0; throw hComException(); } memset(&dcb, 0, sizeof(dcb)); dcb.DCBlength = sizeof(DCB); GetCommState(hCom, &dcb); dcb.BaudRate = BodRate; dcb.fParity = 0; dcb.ByteSize = 8; dcb.Parity = NOPARITY; dcb.StopBits = ONESTOPBIT; dcb.fAbortOnError = FALSE; dcb.fDtrControl = DTR_CONTROL_DISABLE; dcb.fRtsControl = RTS_CONTROL_DISABLE; dcb.fBinary = TRUE; dcb.fParity = FALSE; dcb.fInX = FALSE; dcb.fOutX = FALSE; dcb.XonChar = 0; dcb.XoffChar = (unsigned char)0xFF; dcb.fErrorChar = FALSE; dcb.fNull = FALSE; dcb.fOutxCtsFlow = FALSE; dcb.fOutxDsrFlow = FALSE; dcb.XonLim = 128; dcb.XoffLim = 128; SetCommState(hCom,&dcb); PurgeComm(hCom, PURGE_RXCLEAR); begin = GetTickCount(); tmptxt = COM; tmptxt += " br"; tmptxt += dcb.BaudRate; tmptxt += " p"; tmptxt += dcb.Parity; tmptxt += " By"; tmptxt += dcb.ByteSize; tmptxt += " sb"; tmptxt += dcb.StopBits; Stat->SimpleText= tmptxt; overlapped.hEvent = CreateEvent(NULL, true, true, NULL); } } void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action) { CloseHandle(hCom); }
      
      







スタンドでは完全にオプションですが、必要に応じて何らかの方法でデータを上げることができるように、保存機能とロード機能を追加することにしました。 データは保存されましたが、将来使用されることはなかったため、実行できません。

保存およびダウンロード機能
 void TForm1::SaveToFile(String FileName) { fstream file_; file_.open(FileName.c_str(), ios::out); if (!file_) { file_.close(); return; } int count_ = 0, tmp_count; tmp_count = Series1->XValues->MaxValue-1; if(tmp_count>(Series2->XValues->MaxValue-1)) tmp_count = Series2->XValues->MaxValue-1; if(tmp_count>(Series6->XValues->MaxValue-1)) tmp_count = Series6->XValues->MaxValue-1; while(count_++ < tmp_count) { int a1Propeller = Series1->YValue[count_]; int a2X1 = Series2->YValue[count_]; int a6Ugol = Series6->YValue[count_]; file_ << a1Propeller << " " << a2X1 << " " << a6Ugol << " " ; } file_.close(); } void TForm1::LoadFromFile(String FileName) { fstream file; file.open(FileName.c_str()); if (!file) { file.close(); return; } float a1Propeller = 0, a2X1 = 0, a6Ugol = 0; Series1->Clear(); Series2->Clear(); Series6->Clear(); long file_i=0; file_i=0; while(!file.eof()) { file >> a1Propeller >> a2X1 >> a6Ugol; Application->ProcessMessages(); Series1->Add(a1Propeller); Series2->Add(a2X1); Series6->Add(a6Ugol); Dannye->Cells[0][0]="Propeller"; Dannye->Cells[1][0]=a1Propeller; Dannye->Cells[0][1]="X1"; Dannye->Cells[1][1]=a2X1; float r, fi, xx, yy; r = (Image1->Height/2)*(a*a2X1-b)/4095; fi = a6Ugol; RadMass[(int)fi]=RadMass[(int)fi] + (r-RadMass[(int)fi])*Krmax; if(RadMass[(int)fi]>RadMassMax) { float lastRadMax; int lastfiMax; lastfiMax = fiMax; lastRadMax = RadMass[lastfiMax]; Image1->Canvas->Pen->Color=clWhite; Image1->Canvas->MoveTo(Image1->Width/2, Image1->Height/2); xx= lastRadMax*cos(lastfiMax) + Image1->Width/2; yy= lastRadMax*sin(lastfiMax) + Image1->Height/2; Image1->Canvas->LineTo(xx, yy); Image1->Canvas->MoveTo(xx, yy+1); Image1->Canvas->LineTo(Image1->Width/2, Image1->Height/2+1); Image1->Canvas->MoveTo(xx, yy-1); Image1->Canvas->LineTo(Image1->Width/2, Image1->Height/2-1); Image1->Canvas->MoveTo(xx+1, yy); Image1->Canvas->LineTo(Image1->Width/2+1, Image1->Height/2); Image1->Canvas->MoveTo(xx-1, yy); Image1->Canvas->LineTo(Image1->Width/2-1, Image1->Height/2); RadMassMax = RadMass[(int)fi]; fiMax = fi; lastRadMax = RadMassMax; lastfiMax = fiMax; Image1->Canvas->Pen->Color=clRed; Image1->Canvas->MoveTo(Image1->Width/2, Image1->Height/2); xx= RadMassMax*cos(2*M_PI*fiMax/360)+Image1->Width/2; yy= RadMassMax*sin(2*M_PI*fiMax/360) + Image1->Height/2; Image1->Canvas->LineTo(xx, yy); Image1->Canvas->MoveTo(xx, yy+1); Image1->Canvas->LineTo(Image1->Width/2, Image1->Height/2+1); Image1->Canvas->MoveTo(xx, yy-1); Image1->Canvas->LineTo(Image1->Width/2, Image1->Height/2-1); Image1->Canvas->MoveTo(xx+1, yy); Image1->Canvas->LineTo(Image1->Width/2+1, Image1->Height/2); Image1->Canvas->MoveTo(xx-1, yy); Image1->Canvas->LineTo(Image1->Width/2-1, Image1->Height/2); Image1->Canvas->Pen->Color=clBlack; Dannye->Cells[0][3]=""; Dannye->Cells[1][3]= (int)RadMassMax; } xx = r*cos(2*M_PI*fi/360) + Image1->Width/2; yy = r*sin(2*M_PI*fi/360) + Image1->Height/2; Image1->Canvas->Ellipse((int)xx,(int)yy, (int)xx+2,(int)yy+2); if (file_i > (N+1)) { int count_= 0; while(count_ < N) { arr[count_].re= Series2->YValue[count_+file_i-N]; arr[count_++].im= 0.0; } Series7->Clear(); Series8->Clear(); fft(arr, k, false); int i=0; double nSamplesPerSec; int Nmax= (N + 1) / 2; double *freq= new double[Nmax]; double *amp= new double[Nmax]; double *phase= new double[Nmax]; int j= 0; double limit= 0.001; double abs2min= limit * limit * N * N; double abs2max= 10E150; if (arr[i].re >= limit) { amp[j]= arr[i].re / N; freq[j]= 0.0; phase[j]= 0.0; ++j; } ++i; for(i= 1; i < Nmax; ++i) { double re= arr[i].re; double im= arr[i].im; long double abs2; abs2 = re * re + im * im; if (abs2 < abs2min) continue; if (abs2 > abs2max) abs2=abs2max; amp[j]= 2.0 * sqrt((double)abs2) / N; Amp_F[j] = Amp_F[j]+(amp[j]-Amp_F[j])*K_flt; Series7->Add(Amp_F[j]); phase[j]= atan2(im, re); phase[j]+= M_PI_2; if (phase[j] > M_PI) phase[j]-= 2*M_PI; phase[j]= phase[j] * M_PI / 180.0; Phase_F[j] = Phase_F[j] + (phase[j] - Phase_F[j])*K_flt; Series8->Add(Phase_F[j]); freq[j]= (nSamplesPerSec * i) / N; ++j; } delete[] amp; delete[] freq; delete[] phase; } file_i++; } file.close(); return; } //--------------------------------------------------------------------------- void __fastcall TForm1::Button1Click(TObject *Sender) { String FileName_; FileName_ = ExtractFilePath(Application->ExeName); FileName_ += "Data\\"; FileName_ += FormatDateTime("dd_mmm_yyy'-'hh_nn'",Now()); SaveDialog1->FileName = FileName_; if(SaveDialog1->Execute()) { SaveToFile(SaveDialog1->FileName); flEdit=0; } } //--------------------------------------------------------------------------- void __fastcall TForm1::Button3Click(TObject *Sender) { if(OpenDialog1->Execute()) { LoadFromFile(OpenDialog1->FileName); } }
      
      









バッファを自動的に受信して復号化するために、タイマーでこれを行うことを可能にしましたが、これは別の方法で行い、到着時にデータを別のストリームに収集し、入力インターフェイスなどに干渉しないように出力に送信しますアプリケーション。 しかし、このオプションは実行可能であることが判明し、そのタスクに非常にうまく対処しました。

処理タイマー
 void __fastcall TForm1::Timer1Timer(TObject *Sender) { if(CheckBox1->Checked) { flEdit=1; int Propeller, X1, ADCcount, ADCcountMax, Amax; unsigned char tmp40; int attempts = 3, nLetter40 = 11; int rBUF=0, wBUF=0, marBUF=0; feedback = 0; BYTE data1[BUFSIZE]; vector<unsigned char> data(data1, data1+BUFSIZE); unsigned char* buf = &data[0]; ADCcountMax=0; WaitCommEvent(hCom, &mask, &overlapped); signal = WaitForSingleObject(overlapped.hEvent, 7000); if(signal == WAIT_OBJECT_0) { if(GetOverlappedResult(hCom, &overlapped, &feedback, true)) if((mask & EV_RXCHAR)!=0) { ClearCommError(hCom, &feedback, &comstat); btr = comstat.cbInQue; if(btr) { ReadFile(hCom, buf, btr, &feedback, &overlapped); wBUF+=btr;//(DWORD)data.size(); if(wBUF >= BUFSIZE) { wBUF = 0; marBUF = 1; } } while(rBUF < (wBUF + (marBUF*BUFSIZE))) { unsigned char tmpBuf = data[rBUF]; tmpBuf = tmpBuf&~0x80; if(tmpBuf==(0x40 | 40)) { nLetter40=0; tmp40 = 0; tmp40 = tmp40 ^ data[rBUF]; } else if(nLetter40==0 && (data[rBUF]>>7)==1) { nLetter40++; Propeller = data[rBUF]&~0x80; tmp40 = tmp40 ^ data[rBUF]; } else if(nLetter40==1 && (data[rBUF]>>7)==1) { nLetter40++; Propeller |= ((int)(data[rBUF]&~0x80)<<7); tmp40 = tmp40 ^ data[rBUF]; } else if(nLetter40==2 && (data[rBUF]>>7)==1) { nLetter40++; ADCcount = data[rBUF]&~0x80; tmp40 = tmp40 ^ data[rBUF]; } else if(nLetter40==3 && (data[rBUF]>>7)==1) { nLetter40++; ADCcount |= ((int)(data[rBUF]&~0x80)<<7); tmp40 = tmp40 ^ data[rBUF]; } else if(nLetter40==4 && (data[rBUF]>>7)==1) { nLetter40++; X1 = data[rBUF]&~0x80; tmp40 = tmp40 ^ data[rBUF]; } else if(nLetter40==5 && (data[rBUF]>>7)==1) { nLetter40++; X1 |= ((int)(data[rBUF]&~0x80)<<7); tmp40 = tmp40 ^ data[rBUF]; } else if(nLetter40==6 && (data[rBUF]>>7)==1) { nLetter40++; Amax = data[rBUF]&~0x80; tmp40 = tmp40 ^ data[rBUF]; } else if(nLetter40==7 && (data[rBUF]>>7)==1) { nLetter40++; Amax |= ((int)(data[rBUF]&~0x80)<<7); tmp40 = tmp40 ^ data[rBUF++]; tmp40 = tmp40 | 0x80; float Angle=400; if(Amax>0) Angle = 360*ADCcount/Amax; if(tmp40 != 0x80 && tmp40 == data[rBUF] && ADCcount>0 && X1<4096 && X1>0 && Amax>0 && Propeller<1000 && Propeller>100 && Angle>=0 && Angle<=360) { //Chart1->BottomAxis->Scroll(1, false); Dannye->Cells[0][0]=""; Dannye->Cells[1][0]=Propeller; Series1->Add(Propeller); Dannye->Cells[0][1]="X1"; Dannye->Cells[1][1]=X1; V=Xlast-X1; A=Vlast-V; Vlast=V; Alast=A; Series2->Add(X1); Series6->Add(360*ADCcount/Amax); if (data_i > (N+1)) { int count_= 0; while(count_ < N) { arr[count_].re= Series2->YValue[count_+data_i-N]; arr[count_++].im= 0.0; } Series7->Clear(); Series8->Clear(); fft(arr, k, false); int i=0; double nSamplesPerSec; int Nmax= (N + 1) / 2; double *freq= new double[Nmax]; double *amp= new double[Nmax]; double *phase= new double[Nmax]; int j= 0; double limit= 0.001; double abs2min= limit * limit * N * N; double abs2max= 10E150; if (arr[i].re >= limit) { amp[j]= arr[i].re / N; freq[j]= 0.0; phase[j]= 0.0; ++j; } ++i; for(i= 1; i < Nmax; ++i) { double re= arr[i].re; double im= arr[i].im; long double abs2; abs2 = re * re + im * im; if (abs2 < abs2min) continue; if (abs2 > abs2max) abs2=abs2max; amp[j]= 2.0 * sqrt((double)abs2) / N; //Series7->Add(amp[j]); Amp_F[j] = Amp_F[j]+(amp[j]-Amp_F[j])*K_flt; Series7->Add(Amp_F[j]); phase[j]= atan2(im, re); phase[j]+= M_PI_2; if (phase[j] > M_PI) phase[j]-= 2*M_PI; phase[j]= phase[j] * M_PI / 180.0; //Series8->Add(phase[j]); Phase_F[j] = Phase_F[j] + (phase[j] - Phase_F[j])*K_flt; Series8->Add(Phase_F[j]); freq[j]= (nSamplesPerSec * i) / N; ++j; } delete[] amp; delete[] freq; delete[] phase; for(i= 0; i<Nmax; i++) { if(i>(j_max+1)) arr[i].re = 0; arr[i].im = 0; } fft(arr, k, true); /* count_= 0; while(count_ < N) { Series4->Add(arr[count_++].re); } */ Series4->Add(arr[(N-1)].re); float r, fi, xx, yy; r = (Image1->Height/2)*(a*arr[(N-1)].re-b)/4095; //X1=arr[1023].re; ///!!!!!!!!!! fi = 360*(ADCcount)/Amax; xx= r*cos(2*M_PI*fi/360)+Image1->Width/2; yy= r*sin(2*M_PI*fi/360) + Image1->Height/2; Image1->Canvas->Pen->Color = clYellow; Image1->Canvas->Ellipse((int)xx,(int)yy, (int)xx+2,(int)yy+2); } else { Series4->Add(X1); } Image1->Canvas->Pen->Color = clBlack; data_i++; Dannye->Cells[0][2]="  "; Dannye->Cells[1][2]=Amax; float r, fi, xx, yy; r = (Image1->Height/2)*(a*X1-b)/4095; fi = 360*(ADCcount)/Amax; if(X1>0 && X1<4095 && fi<361 && fi >0) { //  xx = r*cos(2*M_PI*fi/360) + Image1->Width/2; yy = r*sin(2*M_PI*fi/360) + Image1->Height/2; Image1->Canvas->Ellipse((int)xx,(int)yy, (int)xx+2,(int)yy+2); X1=arr[(N-1)].re; ///!!!!!!!!!! r = (Image1->Height/2)*(a*X1-b)/4095; RadMass[(int)fi]=RadMass[(int)fi] + (r-RadMass[(int)fi])*Krmax; //  xx= RadMass[(int)fi]*cos(2*M_PI*fi/360)+Image1->Width/2; yy= RadMass[(int)fi]*sin(2*M_PI*fi/360) + Image1->Height/2; Image1->Canvas->Pen->Color = clYellow; Image1->Canvas->Ellipse((int)xx,(int)yy, (int)xx+2,(int)yy+2); Image1->Canvas->Pen->Color = clBlack; if(RadMass[(int)fi]>RadMassMax) { Image1->Canvas->Pen->Color=clWhite; Image1->Canvas->MoveTo(Image1->Width/2, Image1->Height/2); xx= RadMassMax*cos(2*M_PI*fiMax/360)+Image1->Width/2; yy= RadMassMax*sin(2*M_PI*fiMax/360) + Image1->Height/2; Image1->Canvas->LineTo(xx, yy); Image1->Canvas->MoveTo(xx, yy+1); Image1->Canvas->LineTo(Image1->Width/2, Image1->Height/2+1); Image1->Canvas->MoveTo(xx, yy-1); Image1->Canvas->LineTo(Image1->Width/2, Image1->Height/2-1); Image1->Canvas->MoveTo(xx+1, yy); Image1->Canvas->LineTo(Image1->Width/2+1, Image1->Height/2); Image1->Canvas->MoveTo(xx-1, yy); Image1->Canvas->LineTo(Image1->Width/2-1, Image1->Height/2); RadMassMax = RadMass[(int)fi]; fiMax = (int)fi; Image1->Canvas->Pen->Color=clRed; Image1->Canvas->MoveTo(Image1->Width/2, Image1->Height/2); xx= RadMassMax*cos(2*M_PI*fiMax/360)+Image1->Width/2; yy= RadMassMax*sin(2*M_PI*fiMax/360) + Image1->Height/2; Image1->Canvas->LineTo(xx, yy); Image1->Canvas->MoveTo(xx, yy+1); Image1->Canvas->LineTo(Image1->Width/2, Image1->Height/2+1); Image1->Canvas->MoveTo(xx, yy-1); Image1->Canvas->LineTo(Image1->Width/2, Image1->Height/2-1); Image1->Canvas->MoveTo(xx+1, yy); Image1->Canvas->LineTo(Image1->Width/2+1, Image1->Height/2); Image1->Canvas->MoveTo(xx-1, yy); Image1->Canvas->LineTo(Image1->Width/2-1, Image1->Height/2); Image1->Canvas->Pen->Color=clBlack; Dannye->Cells[0][6]=""; Dannye->Cells[1][6]= (int)RadMassMax; Dannye->Cells[0][7] =""; Dannye->Cells[1][7] =(int)fiMax; } RadMassMax = RadMass[(int)fiMax]; } } else { bad++; } String tmp_txt; if(Amax>0) j_max = (int)((float)N/(float)Amax); Amp_max = Amp_max + ((Amp_F[(int)j_max]+Amp_F[(int)j_max-1]+Amp_F[(int)j_max+1])/3. - Amp_max)*kAmp; Phase_max = Phase_F[(int)j_max]; Dannye->Cells[0][3]=""; Dannye->Cells[1][3]=(int)Amp_max; Dannye->Cells[0][4]="  "; tmp_txt = j_max; tmp_txt += " |"; tmp_txt +=Phase_max; Dannye->Cells[1][4]=tmp_txt; Dannye->Cells[0][5]=" "; if(data_i>0) { tmp_txt = (int)(100*bad/(data_i+bad)); tmp_txt +=" %"; } else { tmp_txt = "100%"; } Dannye->Cells[1][5]=tmp_txt.c_str(); } if(rBUF++ >= BUFSIZE) { rBUF = 0; marBUF = 0; } } memset(buf, 0, BUFSIZE); } } else { CheckBox1->Checked = false; } delete[] buf; } }
      
      







すべてをクリアして最初からやり直せるように、クリアボタンと、蓄積されたデータでフィールドをクリーンアップして再描画する機能を作成しました。

クリーニング機能
 void __fastcall TForm1::Button2Click(TObject *Sender) { Series1->Clear(); Series2->Clear(); Series3->Clear(); Series4->Clear(); Series5->Clear(); Series6->Clear(); Series7->Clear(); Series8->Clear(); r=0; rmax=0; fi=0; xx=0; yy=0; data_i = 0; bad=0; Image1->Canvas->Rectangle(0,0,Image1->Width, Image1->Height); rmax = 0.3*sqrt(Image1->Width*Image1->Width+Image1->Height*Image1->Height); Image1->Canvas->Pen->Color=clBlue; r = rmax; while(fi<360) { if(fi>1) { Image1->Canvas->Pen->Color=clGreen; } xx = r*cos(2*M_PI*fi/360) + Image1->Width/2; yy = r*sin(2*M_PI*fi/360) + Image1->Height/2; Image1->Canvas->MoveTo(Image1->Width/2, Image1->Height/2); Image1->Canvas->LineTo(xx, yy); if(fi<=1){ Image1->Canvas->MoveTo(xx, yy+1); Image1->Canvas->LineTo(Image1->Width/2, Image1->Height/2+1); Image1->Canvas->MoveTo(xx, yy-1); Image1->Canvas->LineTo(Image1->Width/2, Image1->Height/2-1); Image1->Canvas->MoveTo(xx+1, yy); Image1->Canvas->LineTo(Image1->Width/2+1, Image1->Height/2); Image1->Canvas->MoveTo(xx-1, yy); Image1->Canvas->LineTo(Image1->Width/2-1, Image1->Height/2); } fi=fi+30; } r=0; do { r=r+15; fi=0; while(fi<360) { fi=fi+1; xx = r*cos(2*M_PI*fi/360) + Image1->Width/2; yy = r*sin(2*M_PI*fi/360) + Image1->Height/2; Image1->Canvas->Ellipse((int)xx,(int)yy, (int)xx+2,(int)yy+2); } }while(r<rmax); Image1->Canvas->Pen->Color=clRed; fi=0; while(fi<360) { fi=fi+1; r=RadMass[(int)fi]; xx = r*cos(2*M_PI*fi/360) + Image1->Width/2; yy = r*sin(2*M_PI*fi/360) + Image1->Height/2; //Image1->Canvas->LineTo(xx, yy); Image1->Canvas->Ellipse((int)xx,(int)yy, (int)xx+2,(int)yy+2); } Image1->Canvas->Pen->Color=clRed; for(int i=0; i<4; i++) { Image1->Canvas->MoveTo(Image1->Width/2+i, Image1->Height/2+i); Image1->Canvas->LineTo(RadMassMax*cos(2*M_PI*fiMax/360)+Image1->Width/2+i, RadMassMax*sin(2*M_PI*fiMax/360) + Image1->Height/2+i); } for(int i=0; i<362; i++) RadMass[i]=0; RadMassMax=0; for(int i=0; i<4095; i++) { Amp_F[i]=0; Phase_F[i]=0; arr[i].re=0; arr[i].im=0; } Image1->Canvas->Pen->Color=clBlack; }
      
      







バランス偏差のグラフィックフィールドで感度を徐々に上げることができるように、感度スイッチを追加しました。

スケールスイッチ
 void __fastcall TForm1::RadioButton1Click(TObject *Sender) { a=1; b=0; } //--------------------------------------------------------------------------- void __fastcall TForm1::RadioButton2Click(TObject *Sender) { a=2; b=2550; } //--------------------------------------------------------------------------- void __fastcall TForm1::RadioButton3Click(TObject *Sender) { a=3; b=5500; } //--------------------------------------------------------------------------- void __fastcall TForm1::RadioButton4Click(TObject *Sender) { a=4; b=8000; }
      
      









結果は、アンバランスがどのように存在するかを示すかなり便利なプログラムであり、0.15 gのArakalピースを接着することでアンバランスを調整したため、ネジを非常に正確にバランスさせることができました。





/プログラム自体が動作している/



周波数のピークを見ると、2つの振幅がはっきりしていることがわかります。1つはねじの振動の原因であり、もう1つは電気モーターによって作成されています。したがって、ネジのバランスを取り、反対側の円の偏差に比例する重量を取り付けることにより、最初のピークを最小化します。



All Articles