Do-it-yourself balancing stand on the SiLabs C8051F120-TB debug board


If you are planning to balance something rotating, be it a wheel, an airplane propeller or a flying saucer. Or are you interested in a story of how a programmer’s workdays go? A fascinating story about creating a balancing stand ...


Foreword.
I express gratitude to my supervisor, Dmitriev Ivan Alekseevich, engineer-designer Andrei Arapov, electronics engineers Alexander Turaev and Gidal Grigoryevich. This booth is the result of a close-knit team.

I'll start with the story: I work as a programmer in an organization
It’s not a secret at all, but it’s not relevant, I’ll just say that we are engaged in UAVs
, where periodically there are many different interesting tasks, and it became necessary for us to balance the high accuracy of the aircraft propeller. As it turned out, equipment for such balancing can be bought, but it will cost a lot, we decided to do it ourselves.

I’ll tell you a little why it was needed. Our plane, with this screw, was terribly sausage at idle (800 rpm). These things are usually balanced, statically and dynamically. Static balancing consists in balancing with respect to the center of rotation, without rotation, and dynamic balancing is balancing during rotation.

As for static balancing, everything is clear here, the screw is simply balanced against the center of rotation, but what to do with dynamic balancing, when the screw starts to create vibration during rotation.

For such a task was built
simple device
, consisting of a frame attached to springs to a massive base.
An electric motor is installed on the massive base, and through the pulley it rotates the axis on which the balanced screw is mounted. Accelerometers are also installed on the frame, and a hall sensor is installed on the axis with a screw. The electric motor is connected to a chastotnik, which controls the frequency of its rotation.
An accelerometer on two axes was used as a deviation meter, through an amplifier connected to the ADC of the SiLabs C8051F120-TB debug board. In order to catch the moment of passage of the rotating body through 0 degrees, a hall sensor was installed, the signal from which was supplied to one more leg of the debug board.

So we got a simple unit,
which can measure the acceleration of the frame with the body of rotation, and give a signal about the passage through 0 degrees of the shaft rotating the balanced screw.


/ appearance of a simple device /

I was given this design, and they set the task to programmatically find out how much insulating tape , pieces of plasticine or Arakal need very precisely weighed loads, stick it to the edges of the screw blades so that it becomes balanced. And make an application with a convenient and intuitive interface so that in 5 minutes you can figure out how to use it.
And I started a fascinating work





At first I thought that I could manage in one day, and the task is very simple. But when taking the signal with an oscilloscope, it was found that the vibration of the entire installation, interference from the mains, and other noise, turn the captured signal from the ADC into a uniform incomprehensible noise. Although if you look closely, you can see an obvious periodic maximum and minimum. It took about a week, or even a little more, to debug the software and hardware, but then the accuracy of the device began to please the eye.


/ Oscilloscope readings /

I wrote a program on the debug board that takes readings and sends them to the COM port.
defines
We configure the controller, define the main variables, select arrays and constants. We are preparing the debug board for programming.

#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; //АЦП
}



Main function
Here we spin constantly in an infinite loop, and send the received ADC measurements
//-------------------------------------------------------------------
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;
		}
	}
}



We create an event for interruption from the leg to which the hall sensor is connected
Interruptions
Here we monitor interruptions from the Hall sensor.
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;
}



To know exactly how much time has passed, we start the timer, and count the time in it
Timer
void TIMER_ISR0 (void) interrupt 1  // считаем время с частотой FREQT0 = SYSCLK/65535 Гц
{
	RTC++;
	return;
}



A separate event is the work with the input / output port.
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;
}



All data taken from the ADC is written to the buffer, then to transfer the entire packet in one revolution. This method allowed us not to waste time transmitting while taking information, as a result it works faster and removes more points.

ADC Interruptions
Here we write to the ADC measurement buffer
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;
}



In order to somehow separate the necessary deviations, on the desktop application, I decided to apply the Fourier transform, which I had previously used to process pictures, a little conjuring with a tambourine, it turned out to select the necessary frequencies.
To develop the interface, I used C ++ Builder 6.0

Header pluggable libraries, variables, constants
//---------------------------------------------------------------------------
#include 
#pragma hdrstop
#include "balansCom4.h"
#include 
#include 
#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
//---------------------------------------------------------------------------



To extract the desired frequency from the received signal, the direct and inverse Fourier transforms turned out to be very useful. The data is poured in a continuous stream, and in order to have time to process it, I applied an optimized version, the so-called FFT . this is not a panacea, and for processing a video stream it is better to parallelize and use the GPU, but for this task, it is quite applicable.

FFT function
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;
    }
}



We prepare the GUI , and configure the COM port for reception.
Startup and Close Functions
__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);
}


I decided to attach the save and load functions so that I could somehow raise the data if necessary, although it is absolutely optional for the stand. Although the data was saved, it was never used in the future, so you can not do it.
Save and Download Functions
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);
   }
}



In order for the buffer to be received and decrypted automatically, I made it possible to do this on a timer, not a good idea, now I would do it differently, I would collect data on arrival in a separate stream, and send it to the output so as not to interfere with the input interface and others applications. However, this option turned out to be viable, and coped with its task quite successfully.
Processing Timer
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 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(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;
      }
}


So that you can clear everything and start over, I made a clear button and a function that cleans and redraws the field with the accumulated data.
Cleaning function
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(rCanvas->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;
}


To be able to gradually increase the sensitivity on the graphic field of the balance deviation, added a sensitivity switch.
Scale switches
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;
}



The result was a pretty convenient program that shows which way the imbalance exists, and having adjusted it by gluing 0.15 g of Arakal pieces, it was possible to balance the screw quite accurately.


/ The program itself is in operation /

If you look at the peaks in frequency, you can see that two amplitudes are pronounced, as it turned out one is responsible for the vibration of the screw, and the second is created by an electric motor, since it is connected via a belt and spin faster. Thus balancing the screw, we minimize the first peak by attaching a weight proportional to the deviation of the circle on the opposite side.

Also popular now: