Пример #1
0
void UserTask() {
    DISPLAY lcd;
    int page=0;

    lcd.drawForm(-2);
    for (int i=0; i<30; i++) {
        TimerWait(100);
        if (EventCatch(JOY_MASK)) {
            EventRaise(EVT_EXIT);
            for (;;) NextTask();
        }
    }
    lcd.drawForm(page);
    for (;;) {
        switch (EventCatch(JOY_MASK)) {
            case JOY_UP:
                if (page>0) lcd.drawForm(--page);
                break;
            case JOY_DOWN:
                if (page<3) lcd.drawForm(++page);
                break;
            case JOY_PUSH:
                lcd.drawForm(-1);
                EventRaise(EVT_EXIT+EVT_SHUTDOWN);
                for (;;) NextTask();

        }
        lcd.drawData(page);
        NextTask();
    }
}
Пример #2
0
Файл: main.c Проект: sndae/b3r1
int main (void)
{
    Initialization();

    show_mhz();				// Display CPU speed
    TimerWait(1000);		// Wait 1 second

    while(1)
    {
        printLCD("Show ADC");			// Display the text in quotes on the LCD
        while (!KEY_VALID);				// Wait for joystick to be moved or pressed.
        if (getkey() == 1)				// If enter was pressed then do what is in the braces, just skip over it.
        {
            TimerWait(500);				// debounce joystick button
            showADC();
            printLCD("Back ADCs");
            TimerWait(2000);
        }

        printLCD("Balance");			// Display the text in quotes on the LCD
        while (!KEY_VALID);				// Wait for joystick to be moved or pressed.
        if (getkey() == 1)				// If enter was pressed then do what is in the braces, else just continue.
        {
            TimerWait(500);				// debounce joystick button
            balance();
            printLCD("Back Balance");
            TimerWait(2000);
        }

        printLCD("rprintf");			// Display the text in quotes on the LCD
        while (!KEY_VALID);				// Wait for joystick to be moved or pressed.
        if (getkey() == 1)				// If enter was pressed then do what is in the braces, else just continue.
        {
            TimerWait(500);				// debounce joystick button
            rprintf_test();
            printLCD("Back rprintf");
            TimerWait(2000);
        }

        printLCD("PWM Test");			// Display the text in quotes on the LCD
        while (!KEY_VALID);				// Wait for joystick to be moved or pressed.
        if (getkey() == 1)				// If enter was pressed then do what is in the braces, else just continue.
        {
            TimerWait(500);				// debounce joystick button
            PWM_Test();
            printLCD("Back PWM Test");
            TimerWait(3000);
        }
    }
}
Пример #3
0
/**
  Stalls the CPU for the number of microseconds specified by MicroSeconds.

  @param  MicroSeconds  The minimum number of microseconds to delay.

  @return The value of MicroSeconds inputted.

**/
UINTN
EFIAPI
MicroSecondDelay (
  IN      UINTN                     MicroSeconds
  )
{
  TimerWait(uSecsToTB(MicroSeconds));

  return MicroSeconds;
}
Пример #4
0
void test_calls_wptr_time (Workspace p)
{
    int timeo, to2;

    timeo = TimerRead (p);
    to2 = timeo + 42;
    TimerWait (p, to2);
    if (Time_AFTER (to2, timeo)) {
        /* boo */
        StopP (p);
    }
}
Пример #5
0
Файл: main.c Проект: sndae/b3r1
/*****************************************************************************
 *  showADC - Displays ADC reading on the LCD display.
 *****************************************************************************/
void showADC(void)
{
    InitADC();

    while (!(getkey() == 1))
    {
        gyroRaw = GetADC(gyro_sensor);
        accelRaw = GetADC(accel_sensor);
        TimerWait(100);
        show12bits(gyroRaw, accelRaw);
    }
    LCD_ShowColons(0);
}
void SampleLoopTask::Task(){
		
	int cnt = 0;
	int region;
	int attempt = 0;
	
	static int dout = 1;
	static double error = 0;
	
	int last_status = 0;
	double pos_prev = 0;
	double pCmd_prev = 0;
	double vCmd_prev = 0;
		
	int obj_ang_index = 0; // to keep continuous angle value when acrossing pi(or -pi).
	uint64_t cycle, cycle_prev;

	// for acceleration calculation
	double u = 0; 
	double alpha = 0;
	double DuDeta1Bar =0;
	double DuDeta2 =0; 
	double DalphaDeta1Bar =0;
	double DalphaDeta2 =0; 
	double eta1Bar = 0; 
	double z = 0;
	
	// to use eta2D in calculation of shD
	double eta2_prev = 0; 
	double eta2D = 0; 
	double eta2D_prev = 0; 
	
	double elapsedSec = 0;

	//test
	double feedforwardVal = 0;
	uint64_t cycle1, cycle2;

	while(!lost){
		TimerWait();
		
		ncycles = ClockCycles();
		sec=(double)(ncycles - ncycles_prev)/cps;
		ncycles_prev = ncycles;
		
		TimingProcess();
		
		// Read Inputs
		HW->ProcessInput(); // all digital & analog, encoders reading.
		
		// Get status of camera
		last_status = HW->ReadDigitalBit(IoHardware::FRAME_STATUS);
		
		// Send out pulse to trigger camera
		HW->WriteDigitalBit(IoHardware::CAMERA_TRIGGER, 1);
		HW->WriteDigitalBit(IoHardware::CAMERA_TRIGGER, 0);
		
		// test
		//cycle1 = ClockCycles();

		if(camera) {// && !lost){
			// Wait for camera to process data, with timeout counter
			while(HW->ReadDigitalBit(IoHardware::FRAME_STATUS) == last_status){
				if(++attempt == (int)(6.0e5/SAMPLE_RATE)) { // 5.0e5 must be found out by experiments to give the smallest time to determine an error status
					HW->SetAnalogOut(IoHardware::AMP_SIGNAL, 0.0); 
					HW->WriteDigitalBit(IoHardware::MOTOR_ENABLE, MOTOR_OFF);
					HW->motorStatus = MOTOR_OFF;
					lost = 1;
					FATAL_ERROR("Frame not received -");
					//strcpy(err_msg, "Frame not received -"); // // not work this way.
				}
			}
			attempt = 0;
			
			vnum[0] = -99;
			int n = VNET->Recv();

			region = (int)vnum[0];
			switch(region){
				case 0: //ROI_0:
					obj.x0 = vnum[1]; //mm
					obj.y0 = vnum[2]; //mm
					break;
				case 1: //ROI_1:
					obj.x1 = vnum[1]; //mm
					obj.y1 = vnum[2]; //mm
					break;
				default:
					HW->SetAnalogOut(IoHardware::AMP_SIGNAL, 0.0); 
					HW->WriteDigitalBit(IoHardware::MOTOR_ENABLE, MOTOR_OFF);
					HW->motorStatus = MOTOR_OFF;
					printf("roi-vnum[0]:%d\n", region);
					FATAL_ERROR("Object lost -");
					lost = 1;
			}
			
			obj.x = (obj.x0 + obj.x1)/2/1000; // convert to m
			obj.y = (obj.y0 + obj.y1)/2/1000;
			double obj_raw_angle = atan2(obj.y1-obj.y0, obj.x1-obj.x0); // within -pi to pi

			// to keep continuous angle value when acrossing pi(or -pi).
			obj.theta = obj_raw_angle + obj_ang_index*2*3.141592; // converted angle
			if ( fabs(obj.theta - obj.theta_prev) > 3.141592 ) {
				if (obj.theta_prev > obj.theta) // pi to -pi region change
					obj_ang_index++;
				else
					obj_ang_index--;
				obj.theta = obj_raw_angle + obj_ang_index*2*3.141592; // newly converted angle
			}
				
			// calculation of the object angular velocity
			obj.angVel = (obj.theta - obj.theta_prev) / sec; // the use of SAMPLE_RATE may not be a big difference.
			// low pass filter
			double alpha = 0.038; //0.02
			obj.angVel = alpha*obj.angVel + (1-alpha)*obj.angVel_prev;
			
			// calculation of the hand angular velocity
			handTheta = -(HW->GetEncoderCount(IoHardware::ENC_0)) * ENC_RAD_PER_CNT / GR / 4; // just access the data previously read by GetEnc...()
			handVel = (handTheta - handTheta_prev) /sec; //* SAMPLE_RATE;
			handVel = alpha*handVel + (1-alpha)*handVel_prev;
			
			// calculation of sh and \dot sh (=shD)
			sh = (obj.theta - handTheta) / K_R;
			eta2 = asin(-(obj.x-OBJ_X_OFFSET)/(RHO_H+RHO_O)); // alternative way to get eta2;

			/// test, eta2 compensation
			//eta2 = eta2 - 0.004*sin(obj.theta + 1.885); 
			
			// new method for shD, thus eta1 feedback
			eta2D = (eta2 - eta2_prev) / sec;
			eta2D = alpha*eta2D + (1-alpha)*eta2D_prev;
			shD = RHO_H*(eta2D - handVel);
			eta1 = M22*shD + M12*handVel;
			

			/*************************************************************/
			// calculation of the control input (acceleration command)
			eta1Bar = eta1 - COEFF_ETA1_STAR;
			z = handVel - TARGET_THETAHD;
			if (eta2 == 0) {
				u = -GAIN_K1*eta1Bar;
			}
			else {
				u = -GAIN_K1*eta1Bar*sin(eta2)/eta2 - GAIN_K2*eta2;
			}
			alpha = (u - COEFF_sig2*eta1Bar)/COEFF_sig3;
			if (eta2 == 0) {
				DuDeta1Bar = -GAIN_K1;
				DuDeta2 = -GAIN_K2;
			}
			else {
				DuDeta1Bar = -GAIN_K1*sin(eta2)/eta2;
				DuDeta2 = -GAIN_K1*eta1Bar*( (cos(eta2)*eta2 - sin(eta2))/(eta2*eta2) ) - GAIN_K2;
			}
			DalphaDeta1Bar = (DuDeta1Bar - COEFF_sig2)/COEFF_sig3;
			DalphaDeta2 = DuDeta2/COEFF_sig3;

			vInp = DalphaDeta1Bar*COEFF_sig1*sin(eta2) + DalphaDeta2*(COEFF_sig2*eta1Bar+COEFF_sig3*z) - eta2*COEFF_sig3 - GAIN_C*(z - alpha);
			/*************************************************************/
			aCmd = vInp; // calculated acceleration command
			
			obj.theta_prev = obj.theta;
			obj.angVel_prev = obj.angVel;
			handTheta_prev = handTheta;
			handVel_prev = handVel;
			eta2D_prev = eta2D;
			eta2_prev = eta2;
		}
		else { // Because the vision system keeps sending the data, QNX must read them, otherwise the vision system will get a send error.
			VNET->Process();
		}
		
		// test
		if (fabs(handVel) > 7.0 ) {//rad/sec
			HW->SetAnalogOut(IoHardware::AMP_SIGNAL, 0.0); 
			HW->WriteDigitalBit(IoHardware::MOTOR_ENABLE, MOTOR_OFF);
			HW->motorStatus = MOTOR_OFF;
			//VNET->Process();
			FATAL_ERROR("MOTOR RUNS TOO FAST");
			lost = 1;
		}
		
		//**************************************************************
		// reading & calculating output
		//
		// feedback
		cycle = ClockCycles();
		elapsedSec = (double)(cycle - cycle_prev)/cps;
		cycle_prev = cycle;
		
		//HW->ProcessInput(); // because of the encoder reading. Do not use this again. This takes a long time.
		enc = -(HW->ReadEncoder(IoHardware::ENC_0)); // direct read. Not working?
		pos = (enc * ENC_RAD_PER_CNT) / GR / 4; // convert to rad. GR:gear ratio (50). 4?
		vel = (pos - pos_prev) / elapsedSec; // to calculate more exact velocity.
				
		// This is necessary for not having motor run unexpectedly when swtiching from motor off to motor on.
		if(HW->motorStatus == MOTOR_ON) {
			cnt++;
	
			// acceleration inner loop
			// Notice that using no filtering velocity
			vCmd = vCmd_prev + aCmd / SAMPLE_RATE; 
			pCmd = pCmd_prev + vCmd_prev / SAMPLE_RATE + 0.5 * aCmd / (SAMPLE_RATE*SAMPLE_RATE);

			iCmd = calcI(vCmd, aCmd); // feedforward control. 
			feedforwardVal = iCmd;
			
			//iCmd += (150* (pCmd - pos) + 0.6 * (vCmd - vel) + Ki * error); // Ki 0
			//iCmd += (200* (pCmd - pos) + 0.6 * (vCmd - vel) + Ki * error); // Ki 0
			//iCmd += (350* (pCmd - pos) + 1.0 * (vCmd - vel) + Ki * error); // Ki 0
			iCmd += (150* (pCmd - pos) + 0.9 * (vCmd - vel) + Ki * error); // Ki 0
					
			pos_prev = pos;
			pCmd_prev = pCmd;
			vCmd_prev = vCmd;
		}
		else {
			iCmd = 0;
		}

		//**************************************************

		//**************************************************
		// control output
		// 
		//limit current based on motor specs
		if(iCmd > (MAX_CURRENT_MA) ){ // 1.6 mA
			HW->SetAnalogOut(IoHardware::AMP_SIGNAL, 0.0); 
			HW->WriteDigitalBit(IoHardware::MOTOR_ENABLE, MOTOR_OFF);
			HW->motorStatus = MOTOR_OFF;
			//VNET->Process();
			printf("iCmd:%f, pCmd:%f, pos:%f, vCmd:%f, vel:%f, aCmd:%f feedforwardVal:%f cnt:%d\n", iCmd, pCmd, pos, vCmd, vel, aCmd, feedforwardVal, cnt);
			FATAL_ERROR("MOTOR CURRENT TOO HIGH");
			lost = 1;
			iCmd = MAX_CURRENT_MA;
		}
		else if(iCmd < (-MAX_CURRENT_MA) ){
			HW->SetAnalogOut(IoHardware::AMP_SIGNAL, 0.0); 
			HW->WriteDigitalBit(IoHardware::MOTOR_ENABLE, MOTOR_OFF);
			HW->motorStatus = MOTOR_OFF;
			//VNET->Process();
			printf("iCmd:%f, pCmd:%f, pos:%f, vCmd:%f, vel:%f, aCmd:%f feedforwardVal:%f cnt:%d%f\n", iCmd, pCmd, pos, vCmd, vel, aCmd, feedforwardVal, cnt);
			FATAL_ERROR("MOTOR CURRENT TOO HIGH");
			lost = 1;
			iCmd = -MAX_CURRENT_MA;
		}
		
		ampVCmd =-iCmd * AMP_GAIN; // convert to analog signal. +-10 V.  -0.86 for 4.5 rad/s. for test use -0.8
		// sign change to agree with camera
		
		// output signal to amp
		if(!done){
			HW->WriteAnalogCh(IoHardware::AMP_SIGNAL, ampVCmd);
			//HW->WriteAnalogCh(IoHardware::AMP_SIGNAL, 0.0);
		}
		else{
			HW->WriteAnalogCh(IoHardware::AMP_SIGNAL, 0.0);
		}
		//*******************************************************

		//**************************************************
	
		HW->ProcessOutput(); // all digital & analog writing.

		// set outputs
		/*num[0] = obj.theta; 
		num[1] = obj.angVel;
		*/
		num[0] = vInp;//obj.theta; //vInp; //correctedAcc; //eta1; //handVel; //pos; //handTheta; //pos; // angle in rad
		num[1] = eta1;//obj.angVel; //eta1; //feedforwardVal; //pCmd; //eta2; //obj.theta; //vnum[0]; //obj.theta; //vel; //obj.theta;

		num[2] = pos; //feedbackVal; //vCmd; //obj.angVel; //obj.x; //vnum[1]; //obj.x1; //ampVCmd; //obj.x1; // position in m, *10 for cm
		num[3] = eta2; //handTheta; //iCmd; //sec; //elapsedSec; //sec; //vnum[2]; //obj.y1;
	
		MNET->Process();

	} // while()
	
	// for some reason, this does not work.
	HW->SetAnalogOut(IoHardware::AMP_SIGNAL, 0.0); 
	HW->WriteDigitalBit(IoHardware::MOTOR_ENABLE, MOTOR_OFF);
	HW->motorStatus = MOTOR_OFF;
	delay(10);
	FATAL_ERROR(err_msg);
	
}
void SampleLoopTask::Task() {

    int cnt = 0;
    int region;
    int attempt;

    static int dout = 1;
    static double error = 0;
    /*static int last_status = 0;
    static double pos_prev = 0;
    static double pCmd_prev = 0;
    static double vCmd_prev = 0;
    */
    int last_status = 0;
    double pos_prev = 0;
    double pCmd_prev = 0;
    double vCmd_prev = 0;

    int obj_ang_index = 0; // to keep continuous angle value when acrossing pi(or -pi).
    double obj_x_offset = 0;
    //double obj_y_offset = 0;
    uint64_t cycle, cycle_prev;

    // for acceleration calculation
    double z = 0; //!
    double h = 0; //! linearizing output
    double hD = 0; //!
    double hDD = 0; //!
    double hDDD = 0; //!
    double u =0; //

    double eta2_prev = 0; //!
    double eta2D = 0; //! to use eta2D in calculation of shD
    double eta2D_prev = 0; //! to use eta2D in calculation of shD

    double eta1_prev = 0; //!
    double eta1D = 0; //! to use eta1D instead of sigma1 * sin(eta2)
    double eta1D_prev = 0; //! to use eta1D instead of sigma1 * sin(eta2)

    //double eta_a = 0.015;
    //double eta_b = 0.02;
    //double ETA2 = 0;
    //double SIN_ETA2 = 0;
    //int eta1D_flag = 0;
    double err_sum = 0.0;

    // for SMC
    /*double s = 0;
    double lambda = 3;
    double eta = 0.1;
    double F = 0;
    double k = 0;
    double fHat = 0;
    double uHat = 0;
    */

    //test
    double feedforwardVal = 0;
    double feedbackVal = 0;
    //double test_v_prev = 0;
    //double vCmd_prev = 0;
    double elapsedSec = 0;
    uint64_t cycle1, cycle2;
    double elapsedSec1 = 0;
    double elapsedSec2 = 0;
    double obj_vel_uf = 0;
    double vInp_prev = 0;
    //double aCmd_sin = 0;
    //int update_done = 0; // for automatic x_offset update
    //double x_avg = 0;  // for automatic x_offset update
    //double x_max = 0;
    //double x_min = 0;

    while(!lost) {
        TimerWait();

        ncycles = ClockCycles();
        sec=(double)(ncycles - ncycles_prev)/cps;
        ncycles_prev = ncycles;

        TimingProcess();

        // Read Inputs
        HW->ProcessInput(); // all digital & analog, encoders reading.

        // Get status of camera
        last_status = HW->ReadDigitalBit(IoHardware::FRAME_STATUS);

        // Send out pulse to trigger camera
        HW->WriteDigitalBit(IoHardware::CAMERA_TRIGGER, 1);
        HW->WriteDigitalBit(IoHardware::CAMERA_TRIGGER, 0);

        // test
        //cycle1 = ClockCycles();

        if(camera) {// && !lost){
            // Wait for camera to process data, with timeout counter
            while(HW->ReadDigitalBit(IoHardware::FRAME_STATUS) == last_status) {
                if(++attempt == (int)(5.0e5/SAMPLE_RATE)) {
                    HW->SetAnalogOut(IoHardware::AMP_SIGNAL, 0.0);
                    HW->WriteDigitalBit(IoHardware::MOTOR_ENABLE, MOTOR_OFF);
                    HW->motorStatus = MOTOR_OFF;
                    delay(10);
                    FATAL_ERROR("Frame not received -");
                    //strcpy(err_msg, "Frame not received -");
                    lost = 1;
                }
            }
            attempt = 0;
            // test
            //cycle2 = ClockCycles();
            //elapsedSec1 = (double)(cycle2 - cycle1)/cps;


            vnum[0] = -99;
            int n = VNET->Recv();

            //test
            //cycle1 = ClockCycles();
            //elapsedSec2 = (double)(cycle1 - cycle2)/cps;

            region = (int)vnum[0];
            switch(region) {
            case 0: //ROI_0:
                obj.x0 = vnum[1]; //mm
                obj.y0 = vnum[2]; //mm
                break;
            case 1: //ROI_1:
                obj.x1 = vnum[1]; //mm
                obj.y1 = vnum[2]; //mm
                break;
            default:
                //HW->SetAnalogOut(IoHardware::AMP_SIGNAL, 0.0);
                //HW->WriteDigitalBit(IoHardware::MOTOR_ENABLE, MOTOR_OFF);
                printf("roi-vnum[0]:%d\n", region);
                FATAL_ERROR("Object lost -");
                //strcpy(err_msg, "Object lost -");
                lost = 1;
            }
            obj.x = (obj.x0 + obj.x1)/2/1000; // convert to m
            obj.y = (obj.y0 + obj.y1)/2/1000;
            double obj_raw_angle = atan2(obj.y1-obj.y0, obj.x1-obj.x0); // within -pi to pi
            // compensating angle offset
            // set only once at the beginning
            /*if (obj_x_offset == 0 && obj.x0 != 0 && obj.x1 != 0) {
            	obj_x_offset = obj.x;
            	obj_y_offset = obj.y;
            }*/
            // object center position compensation
            //obj.x = obj.x - 0.000250544*cos(obj_raw_angle + (-2.410447));

            //uint64_t cycle1 = ClockCycles();

            // to keep continuous angle value when acrossing pi(or -pi).
            obj.theta = obj_raw_angle + obj_ang_index*2*3.141592; // converted angle
            if ( fabs(obj.theta - obj.theta_prev) > 3.141592 ) {
                if (obj.theta_prev > obj.theta) // pi to -pi region change
                    obj_ang_index++;
                else
                    obj_ang_index--;
                obj.theta = obj_raw_angle + obj_ang_index*2*3.141592; // newly converted angle
            }

            // calculation of the object angular velocity
            //obj.angVel = (obj.theta - obj.theta_prev) * SAMPLE_RATE;
            obj.angVel = (obj.theta - obj.theta_prev) / sec;
            obj_vel_uf = obj.angVel;
            // low pass filter
            double alpha = 0.038; //0.02
            obj.angVel = alpha*obj.angVel + (1-alpha)*obj.angVel_prev;

            // calculation of the hand angular velocity
            handTheta = -(HW->GetEncoderCount(IoHardware::ENC_0)) * ENC_RAD_PER_CNT / GR / 4; // just access the data previously read by GetEnc...()
            handVel = (handTheta - handTheta_prev) /sec; //* SAMPLE_RATE;
            //alpha = 0.1;
            handVel = alpha*handVel + (1-alpha)*handVel_prev;

            // calculation of sh and \dot sh (=shD)
            sh = (obj.theta - handTheta) / K_R;
            //shD = (obj.angVel - handVel) / K_R;

            // coordinate transformation
            //eta1 = M22*shD + M12*handVel;
            // eta1D
            //eta1D = (eta1 - eta1_prev) / sec;
            //eta1D = alpha*eta1D + (1-alpha)*eta1D_prev;

            //eta2 = handTheta + sh/RHO_H;
            //obj_x_offset = OBJ_X_OFFSET + 0.0010775*sin(4*handTheta + 0.6422); //   0.0916609 or OBJ_X_OFFSET
            //eta2 = asin(-(obj.x-obj_x_offset)/(RHO_H+RHO_O)); // alternative way to get eta2;
            eta2 = asin(-(obj.x-OBJ_X_OFFSET)/(RHO_H+RHO_O)); // alternative way to get eta2;
            //eta2 = atan(-(obj.x-OBJ_X_OFFSET)/(obj.y-OBJ_Y_OFFSET+RHO_H+RHO_O)); // alternative way to get eta2;
            // xi = handVel;

            //eta2 compensation
            //eta2 = eta2 - 0.004*sin(obj.theta + 1.885);

            // new method for shD, thus eta1 feedback
            eta2D = (eta2 - eta2_prev) / sec;
            eta2D = alpha*eta2D + (1-alpha)*eta2D_prev;
            shD = RHO_H*(eta2D - handVel);
            eta1 = M22*shD + M12*handVel;

            // eta1D
            // don't use this. this creates motor current too high
            //eta1D = (eta1 - eta1_prev) / sec;
            //eta1D = 0.005*eta1D + (1-0.005)*eta1D_prev;

            //SIN_ETA2 = sin(eta2);
            /*ETA2 = eta2;
            if ( fabs(eta2) < eta_b ) {
            	if (eta2 > eta_a) {
            		//SIN_ETA2 = sin(2*eta2 - 0.02);
            		ETA2 = eta_b/(eta_b - eta_a)*(eta2 - eta_a);
            	}
            	else if (eta2 < -eta_a) {
            		ETA2 = eta_b/(eta_b - eta_a)*(eta2 + eta_a);
            	}
            	else {
            		ETA2 = 0;
            	}
            }*/


            // original
            /*************************************************************/
            // calculation of the control input (acceleration command)
            z = handTheta - TARGET_THETAH;
            h = eta2 - COEFF_sig3*z;
            hD = eta2D - COEFF_sig3*handVel;  // slightly better
            //hD = COEFF_sig2*eta1;
            hDD = COEFF_sig2*COEFF_sig1*sin(eta2);
            //hDD = COEFF_sig2*eta1D; // use directly calculated (filtered) value for eta1D
            /*if (eta1D_flag == 0) {
            	if ( fabs(eta1D - COEFF_sig2*sin(eta2)) < 0.02 ) {
            		eta1D_flag = 1;
            	}
            	else {
            		hDD = COEFF_sig2*COEFF_sig1*sin(eta2);
            	}
            }*/
            hDDD = COEFF_sig2*COEFF_sig1*cos(eta2)*eta2D; // slightly better
            //hDDD = COEFF_sig2*COEFF_sig1*cos(eta2)*(COEFF_sig2*eta1 + COEFF_sig3*handVel);

            /* For addition of integral control */
            if(HW->motorStatus == MOTOR_ON) {
                err_sum = err_sum + z*sec;
            }
            else {
                err_sum = 0;
            } // does not working successfully.
            /// test
            err_sum = 0;

            u = -GAIN_K1*hDDD - GAIN_K2*hDD - GAIN_K3*hD - GAIN_K4*h + 100*err_sum;

            vInp = ( u - COEFF_sig2*COEFF_sig1*sin(eta2)*(-eta2D + COEFF_sig1*COEFF_sig2*cos(eta2)) )/(COEFF_sig1*COEFF_sig2*COEFF_sig3*cos(eta2)); // slightly better
            //vInp = ( u - hDD*(-eta2D + COEFF_sig1*COEFF_sig2*cos(eta2)) )/(COEFF_sig1*COEFF_sig2*COEFF_sig3*cos(eta2));

            //vInp = ( u - COEFF_sig2*COEFF_sig1*sin(eta2)*(-COEFF_sig2*eta1-COEFF_sig3*handVel + COEFF_sig1*COEFF_sig2*cos(eta2)) )/(COEFF_sig1*COEFF_sig2*COEFF_sig3*cos(eta2));
            //vInp = ( u - hDD*(-COEFF_sig2*eta1-COEFF_sig3*handVel + COEFF_sig1*COEFF_sig2*cos(eta2)) )/(COEFF_sig1*COEFF_sig2*COEFF_sig3*cos(eta2));
            //vInp = ( u - COEFF_sig2*COEFF_sig1*sin(eta2)*(-COEFF_sig2*eta1-COEFF_sig3*handVel) - COEFF_sig1*COEFF_sig2*cos(eta2)*hDD)/(COEFF_sig1*COEFF_sig2*COEFF_sig3*cos(eta2));
            /*************************************************************/

            // Sliding Mode Control
            /*s = hDDD + 3*lambda*hDD + 3*lambda*lambda*hD + lambda*lambda*lambda*h;
            F = 0.02 * COEFF_sig1*COEFF_sig2*abs( -eta2D + COEFF_sig1*COEFF_sig2*cos(eta2) );
            k = F + eta;
            fHat = COEFF_sig1*COEFF_sig2*( -eta2D + COEFF_sig1*COEFF_sig2*cos(eta2) ) * eta2;
            uHat = -fHat - 3*lambda*hDDD - 3*lambda*lambda*hDD - lambda*lambda*lambda*hD;
            u = uHat - k*(s > 0 ? 1 : (s < 0 ? -1 : 0));
            vInp = u /(COEFF_sig1*COEFF_sig2*COEFF_sig3*cos(eta2));
            */

            //test input filter
            //vInp = 0.5*vInp + (1-0.5)*vInp_prev;

            aCmd = vInp; // calculated acceleration command


            obj.theta_prev = obj.theta;
            obj.angVel_prev = obj.angVel;
            handTheta_prev = handTheta;
            handVel_prev = handVel;
            eta2D_prev = eta2D;
            eta2_prev = eta2;

            eta1_prev = eta1;
            eta1D_prev = eta1D;

            vInp_prev = vInp;

            // For resetting of the upright eq. position
            /*if(HW->motorStatus == MOTOR_ON && update_done == 0) {
            	x_avg += obj.x;
            	if (obj.x > x_max) {
            		x_max = obj.x;
            	}
            	if (obj.x < x_min) {
            		x_min = obj.x;
            	}
            	if ( (cnt%int(SAMPLE_RATE)) == 1 ) {
            		x_avg = x_avg / SAMPLE_RATE;
            		if ( (x_max - x_avg) < 0.001 && (x_avg - x_min) < 0.001 ) {
            			obj_x_offset = x_avg;
            			//x_offset_test = x_avg;
            			update_done = 1;
            		}
            		else {
            			x_avg = 0;
            			x_max = obj.x;
            			x_min = obj.x;
            		}
            	}
            }*/

            //uint64_t cycle2 = ClockCycles();
            //elapsedSec=(double)(cycle2 - cycle1)/cps;
        }
        else { // Because the vision system keeps sending the data, QNX must read them, otherwise the vision system will get a send error.
            VNET->Process();
            //obj_ang_offset = 0.0; // reset the angle offset
            //aCmd = 0;
            //vCmd_prev = 0;
            //pCmd_prev = 0;
        }

        // test
        if (fabs(handVel) > 7.0 ) {//rad/sec
            HW->SetAnalogOut(IoHardware::AMP_SIGNAL, 0.0);
            HW->WriteDigitalBit(IoHardware::MOTOR_ENABLE, MOTOR_OFF);
            HW->motorStatus = MOTOR_OFF;
            delay(10);
            FATAL_ERROR("MOTOR RUNS TOO FAST");
            lost = 1;
        }

        //**************************************************************
        // reading & calculating output
        //
        // feedback
        cycle = ClockCycles();
        elapsedSec = (double)(cycle - cycle_prev)/cps;
        cycle_prev = cycle;


        //HW->ProcessInput(); // because of the encoder reading. Do not use this again. This takes a long time.
        //cycle2 = ClockCycles();
        enc = -(HW->ReadEncoder(IoHardware::ENC_0)); // direct read. Not working?
        //cycle1 = ClockCycles();
        //elapsedSec2 = (double)(cycle1 - cycle2)/cps;
        //enc = -(HW->GetEncoderCount(IoHardware::ENC_0)); // sign change to agree with camera
        pos = (enc * ENC_RAD_PER_CNT) / GR / 4; // convert to rad. GR:gear ratio (50). 4?
        //vel = (pos - pos_prev) * SAMPLE_RATE;
        vel = (pos - pos_prev) / elapsedSec; // to calculate more exact velocity.

        // This is necessary for not having motor run unexpectedly when swtiching from motor off to motor on.
        if(HW->motorStatus == MOTOR_ON) {
            cnt++;

            /*double T_period = 0.2; //2;
            // sinusoidal wave
            aCmd = -2*DEG2RAD*(2*3.141592/T_period)*(2*3.141592/T_period)*sin(2*3.141592*(cnt)/SAMPLE_RATE/T_period);
            //aCmd_sin = aCmd;
            if (cnt == 1) { // at the beginning
            	// because of the sinusoidal wave
            	vCmd_prev = 2*DEG2RAD*(2*3.141592/T_period);
            }
            */

            // acceleration inner loop
            // Notice that using no filtering velocity
            vCmd = vCmd_prev + aCmd / SAMPLE_RATE;
            pCmd = pCmd_prev + vCmd_prev / SAMPLE_RATE + 0.5 * aCmd / (SAMPLE_RATE*SAMPLE_RATE);
            //pCmd = pCmd_prev + vCmd / SAMPLE_RATE;
            //vCmd = vCmd_prev + aCmd * elapsedSec;
            //pCmd = pCmd_prev + vCmd * elapsedSec;

            iCmd = calcI(vCmd, aCmd); // feedforward control.
            feedforwardVal = iCmd;

            //iCmd += (150* (pCmd - pos) + 0.6 * (vCmd - vel) + Ki * error); // Ki 0
            //iCmd += (200* (pCmd - pos) + 0.6 * (vCmd - vel) + Ki * error); // Ki 0
            //iCmd += (350* (pCmd - pos) + 1.0 * (vCmd - vel) + Ki * error); // Ki 0
            iCmd += (150* (pCmd - pos) + 0.9 * (vCmd - vel) + Ki * error); // Ki 0

            pos_prev = pos;
            pCmd_prev = pCmd;
            vCmd_prev = vCmd;
        }
        else {
            iCmd = 0;
        }

        //**************************************************

        //**************************************************
        // control output
        //
        //limit current based on motor specs
        if(iCmd > (MAX_CURRENT_MA) ) { // 1.6 mA
            HW->SetAnalogOut(IoHardware::AMP_SIGNAL, 0.0);
            HW->WriteDigitalBit(IoHardware::MOTOR_ENABLE, MOTOR_OFF);
            HW->motorStatus = MOTOR_OFF;
            printf("iCmd:%f, pCmd:%f, pos:%f, vCmd:%f, vel:%f, aCmd:%f feedforwardVal:%f cnt:%d\n", iCmd, pCmd, pos, vCmd, vel, aCmd, feedforwardVal, cnt);
            delay(10);
            FATAL_ERROR("MOTOR CURRENT TOO HIGH");
            lost = 1;
            iCmd = MAX_CURRENT_MA;
        }
        else if(iCmd < (-MAX_CURRENT_MA) ) {
            HW->SetAnalogOut(IoHardware::AMP_SIGNAL, 0.0);
            HW->WriteDigitalBit(IoHardware::MOTOR_ENABLE, MOTOR_OFF);
            HW->motorStatus = MOTOR_OFF;
            printf("iCmd:%f, pCmd:%f, pos:%f, vCmd:%f, vel:%f, aCmd:%f feedforwardVal:%f cnt:%d%f\n", iCmd, pCmd, pos, vCmd, vel, aCmd, feedforwardVal, cnt);
            delay(10);
            FATAL_ERROR("MOTOR CURRENT TOO HIGH");
            lost = 1;
            iCmd = -MAX_CURRENT_MA;
        }

        ampVCmd =-iCmd * AMP_GAIN; // convert to analog signal. +-10 V.  -0.86 for 4.5 rad/s. for test use -0.8
        // sign change to agree with camera

        // output signal to amp
        if(!done) {
            HW->WriteAnalogCh(IoHardware::AMP_SIGNAL, ampVCmd);
            //HW->WriteAnalogCh(IoHardware::AMP_SIGNAL, 0.0);
        }
        else {
            HW->WriteAnalogCh(IoHardware::AMP_SIGNAL, 0.0);
        }
        //*******************************************************

        //**************************************************

        HW->ProcessOutput(); // all digital & analog writing.

        // keep the position angle within 2pi
        //if ( pos > (mu_k+1)*2*3.141592 )
        //	mu_k++;
        //pos = pos - mu_k*2*3.141592;

        /*num[0] = sec; //aCmd; //correctedAcc; //eta1; //handVel; //pos; //handTheta; //pos; // angle in rad
        num[1] = elapsedSec1; //test_v_prev; //feedforwardVal; //pCmd; //eta2; //obj.theta; //vnum[0]; //obj.theta; //vel; //obj.theta;
        num[2] = elapsedSec2; //feedbackVal; //vCmd; //obj.angVel; //obj.x; //vnum[1]; //obj.x1; //ampVCmd; //obj.x1; // position in m, *10 for cm
        num[3] = vCmd; //handTheta; //iCmd; //sec; //elapsedSec; //sec; //vnum[2]; //obj.y1;
        */

        // set outputs
        num[0] = vInp; //correctedAcc; //eta1; //handVel; //pos; //handTheta; //pos; // angle in rad
        num[1] = eta1; //feedforwardVal; //pCmd; //eta2; //obj.theta; //vnum[0]; //obj.theta; //vel; //obj.theta;
        num[2] = pos; //feedbackVal; //vCmd; //obj.angVel; //obj.x; //vnum[1]; //obj.x1; //ampVCmd; //obj.x1; // position in m, *10 for cm
        num[3] = eta2; //handTheta; //iCmd; //sec; //elapsedSec; //sec; //vnum[2]; //obj.y1;


        // acceleration innerloop
        /*num[0] = aCmd; //aCmd;
        num[1] = pCmd;
        num[2] = pos;
        //num[1] = feedforwardVal;
        //num[2] = feedbackVal;
        num[3] = vCmd;
        */

        // motor modeling
        /*num[0] = pos;
        num[1] = iCmd;
        num[2] = cnt;
        num[3] = 0;
        */
        MNET->Process();

    } // while()

    // for some reason, this does not work.
    HW->SetAnalogOut(IoHardware::AMP_SIGNAL, 0.0);
    HW->WriteDigitalBit(IoHardware::MOTOR_ENABLE, MOTOR_OFF);
    HW->motorStatus = MOTOR_OFF;
    delay(10);
    FATAL_ERROR(err_msg);

}
void SampleLoopTask::Task() {

    int cnt = 0;
    int region;
    int attempt;

    static int dout = 1;
    static int last_status = 0;
    static double pos_prev = 0;
    static double pCmd_prev = 0;
    static double vCmd_prev = 0;
    static double error = 0;

    int temp = 0;

    int mu_k = 0; // for test
    int obj_ang_index = 0; // to keep continuous angle value when acrossing pi(or -pi).
    //double obj_ang_offset = 0.0; // to subtract the object's angle at equilibrium position when inputting 'c' (camera)
    double obj_x_offset = 0;
    double obj_y_offset = 0;
    uint64_t cycle, cycle_prev;

    //test
    double eta1_prev = 0;
    double feedforwardVal = 0;
    double feedbackVal = 0;
    double test_v_prev = 0;
    //double vCmd_prev = 0;
    double elapsedSec = 0;
    uint64_t cycle1, cycle2;
    double elapsedSec1 = 0;
    double elapsedSec2 = 0;
    double aCmd_sin = 0;
    double obj_vel_uf = 0;
    double vInp_prev = 0;
    double x_avg = 0;  // for automatic x_offset update
    double x_max = 0;
    double x_min = 0;
    //int update_count = 0;  // for automatic x_offset update
    //int update_trigger = 0; // for automatic x_offset update
    int update_done = 0; // for automatic x_offset update
    double x_offset_test = 0;
    int switching_done = 0; // for controller swtiching

    while(!lost) {
        TimerWait();

        ncycles = ClockCycles();
        sec=(double)(ncycles - ncycles_prev)/cps;
        ncycles_prev = ncycles;

        TimingProcess();

        // Read Inputs
        HW->ProcessInput(); // all digital & analog, encoders reading.

        // Get status of camera
        last_status = HW->ReadDigitalBit(IoHardware::FRAME_STATUS);

        // Send out pulse to trigger camera
        HW->WriteDigitalBit(IoHardware::CAMERA_TRIGGER, 1);
        HW->WriteDigitalBit(IoHardware::CAMERA_TRIGGER, 0);

        // test
        //cycle1 = ClockCycles();

        if(camera) {// && !lost){
            // Wait for camera to process data, with timeout counter
            while(HW->ReadDigitalBit(IoHardware::FRAME_STATUS) == last_status) {
                if(++attempt == (int)(1e8/SAMPLE_RATE)) {
                    HW->SetAnalogOut(IoHardware::AMP_SIGNAL, 0.0);
                    HW->WriteDigitalBit(IoHardware::MOTOR_ENABLE, MOTOR_OFF);
                    FATAL_ERROR("Frame not received -");
                    //strcpy(err_msg, "Frame not received -");
                    lost = 1;
                }
            }
            attempt = 0;
            // test
            //cycle2 = ClockCycles();
            //elapsedSec1 = (double)(cycle2 - cycle1)/cps;


            vnum[0] = -99;
            int n = VNET->Recv();

            //test
            //cycle1 = ClockCycles();
            //elapsedSec2 = (double)(cycle1 - cycle2)/cps;

            region = (int)vnum[0];
            switch(region) {
            case 0: //ROI_0:
                obj.x0 = vnum[1]; //mm
                obj.y0 = vnum[2]; //mm
                break;
            case 1: //ROI_1:
                obj.x1 = vnum[1]; //mm
                obj.y1 = vnum[2]; //mm
                break;
            default:
                //HW->SetAnalogOut(IoHardware::AMP_SIGNAL, 0.0);
                //HW->WriteDigitalBit(IoHardware::MOTOR_ENABLE, MOTOR_OFF);
                printf("roi-vnum[0]:%d\n", region);
                FATAL_ERROR("Object lost -");
                //strcpy(err_msg, "Object lost -");
                lost = 1;
            }
            obj.x = (obj.x0 + obj.x1)/2/1000; // convert to m
            obj.y = (obj.y0 + obj.y1)/2/1000;
            double obj_raw_angle = atan2(obj.y1-obj.y0, obj.x1-obj.x0); // within -pi to pi
            // compensating angle offset
            // set only once at the beginning
            if (obj_x_offset == 0 && obj.x0 != 0 && obj.x1 != 0) {
                obj_x_offset = obj.x;
                obj_y_offset = obj.y;

                x_offset_test = obj.x; // test
            }

            //uint64_t cycle1 = ClockCycles();

            // to keep continuous angle value when acrossing pi(or -pi).
            obj.theta = obj_raw_angle + obj_ang_index*2*3.141592; // converted angle
            if ( fabs(obj.theta - obj.theta_prev) > 3.141592 ) {
                if (obj.theta_prev > obj.theta) // pi to -pi region change
                    obj_ang_index++;
                else
                    obj_ang_index--;
                obj.theta = obj_raw_angle + obj_ang_index*2*3.141592; // newly converted angle
            }

            // calculation of the object angular velocity
            //obj.angVel = (obj.theta - obj.theta_prev) * SAMPLE_RATE;
            obj.angVel = (obj.theta - obj.theta_prev) / sec;
            obj_vel_uf = obj.angVel;
            // low pass filter
            double alpha = 0.038; //0.06; //0.038; //0.02
            obj.angVel = alpha*obj.angVel + (1-alpha)*obj.angVel_prev;

            // calculation of the hand angular velocity
            handTheta = -(HW->GetEncoderCount(IoHardware::ENC_0)) * ENC_RAD_PER_CNT / GR / 4; // just access the data previously read by GetEnc...()
            handVel = (handTheta - handTheta_prev) / sec; //* SAMPLE_RATE;
            //alpha = 0.1;
            handVel = alpha*handVel + (1-alpha)*handVel_prev;

            // calculation of sh and \dot sh (=shD)
            sh = (obj.theta - handTheta) / K_R;
            shD = (obj.angVel - handVel) / K_R;

            // coordinate transformation
            eta1 = M22*shD + M12*handVel;
            //test filtering
            //eta1 = alpha*eta1 + (1-alpha)*eta1_prev;
            //eta1_prev = eta1;

            //eta2 = handTheta + sh/RHO_H;
            eta2 = asin(-(obj.x-obj_x_offset)/(RHO_H+RHO_O)); // alternative way to get eta2;

            // calculation of the control input (acceleration command)
            //xi := handVel;
            //eta1D := COEFF_D * sin(eta2); // COEFF_D
            //eta2D := COEFF_A * eta1 - xi/COEFF_B;
            //double GAIN_C2 = GAIN_C2_FAR;
            double GAIN_C2 = GAIN_C2_FAR;
            /*if ( abs(eta2) < 0.03 && abs(handVel) < 0.2) { //0.015 ) { // eta2=0.015(0.86 deg) : 3.45 mm in x
            //if ( abs(eta2) < 0.03 ) { //0.015 ) {
            	GAIN_C2 = GAIN_C2_NEAR;
            }*/
            double TANH = tanh(GAIN_C1*eta1 + GAIN_C2*eta2);
            alpha_eta = COEFF_B * (COEFF_A*eta1 + GAIN_C0*TANH + GAIN_C3*eta2);
            DalphaDeta1 = COEFF_B * ( COEFF_A + GAIN_C0*(1-TANH*TANH)*GAIN_C1 );
            DalphaDeta2 = COEFF_B * ( GAIN_C0*(1-TANH*TANH)*GAIN_C2 + GAIN_C3 );
            vInp = -GAIN_C * (handVel - alpha_eta) + DalphaDeta1*(COEFF_D * sin(eta2)) + DalphaDeta2*(COEFF_A * eta1 - handVel/COEFF_B);
            //vInp = 0.05*vInp + (1-0.05)*vInp_prev;
            aCmd = vInp; // calculated acceleration command

            obj.theta_prev = obj.theta;
            obj.angVel_prev = obj.angVel;
            handTheta_prev = handTheta;
            handVel_prev = handVel;
            vInp_prev = vInp;

            // automatic x offset update (old)
            /*if (eta2*eta2 + handVel*handVel/100 < 0.00325) {
            	update_trigger = 1;
            }
            if (update_trigger == 1 && update_done != 1) {
            	update_count += 1;
            	x_avg += obj.x;
            	if (update_count > SAMPLE_RATE) { // averaging for 1 sec.
            		//obj_x_offset = x_avg / update_count;
            		x_offset_test = x_avg / update_count;
            		update_trigger = 0;
            		update_count = 0;
            		update_done = 1;
            	}
            }*/

            // automatic x offset update
            /*if(HW->motorStatus == MOTOR_ON && update_done == 0) {
            	x_avg += obj.x;
            	if (obj.x > x_max) {
            		x_max = obj.x;
            	}
            	if (obj.x < x_min) {
            		x_min = obj.x;
            	}
            	if ( (cnt%int(SAMPLE_RATE)) == 1 ) {
            		x_avg = x_avg / SAMPLE_RATE;
            		if ( (x_max - x_avg) < 0.001 && (x_avg - x_min) < 0.001 ) {
            			//obj_x_offset = x_avg;
            			x_offset_test = x_avg;
            			update_done = 1;
            		}
            		else {
            			x_avg = 0;
            			x_max = obj.x;
            			x_min = obj.x;
            		}
            	}
            }
            */

            // Switching to a Linear Controller
            /*if (HW->motorStatus == MOTOR_ON) {
            	if (switching_done == 1) {
            		aCmd = -(2565.2*eta1 + 292.0*eta2 + 16.1*handVel); // pole placement:sigma = 2.3, zeta = 0.85, third pole at 5sigma
            	}
            	else if ( eta2*eta2 + 0.01*handVel*handVel < 0.003125 ) {
            		switching_done = 1;
            	}
            }*/


            //uint64_t cycle2 = ClockCycles();
            //elapsedSec=(double)(cycle2 - cycle1)/cps;
        }
        else { // Because the vision system keeps sending the data, QNX must read them, otherwise the vision system will get a send error.
            VNET->Process();
            //obj_ang_offset = 0.0; // reset the angle offset
            //aCmd = 0;
            //vCmd_prev = 0;
            //pCmd_prev = 0;
        }

        // test
        if (fabs(handVel) > 5.0 ) {//rad/sec
            HW->SetAnalogOut(IoHardware::AMP_SIGNAL, 0.0);
            HW->WriteDigitalBit(IoHardware::MOTOR_ENABLE, MOTOR_OFF);
            HW->motorStatus = MOTOR_OFF;
            FATAL_ERROR("MOTOR RUNS TOO FAST");
        }

        //**************************************************************
        // reading & calculating output
        //
        // feedback
        cycle = ClockCycles();
        elapsedSec = (double)(cycle - cycle_prev)/cps;
        cycle_prev = cycle;


        //HW->ProcessInput(); // because of the encoder reading. Do not use this again. This takes a long time.
        //cycle2 = ClockCycles();
        enc = -(HW->ReadEncoder(IoHardware::ENC_0)); // direct read. Not working?
        //cycle1 = ClockCycles();
        //elapsedSec2 = (double)(cycle1 - cycle2)/cps;
        //enc = -(HW->GetEncoderCount(IoHardware::ENC_0)); // sign change to agree with camera
        pos = (enc * ENC_RAD_PER_CNT) / GR / 4; // convert to rad. GR:gear ratio (50). 4?
        //vel = (pos - pos_prev) * SAMPLE_RATE;
        vel = (pos - pos_prev) / elapsedSec; // to calculate more exact velocity.

        // This is necessary for not having motor run unexpectedly when swtiching from motor off to motor on.
        if(HW->motorStatus == MOTOR_ON) {
            cnt++;

            /*double T_period = 0.2; //2;
            // sinusoidal wave
            aCmd = -2*DEG2RAD*(2*3.141592/T_period)*(2*3.141592/T_period)*sin(2*3.141592*(cnt)/SAMPLE_RATE/T_period);
            //aCmd_sin = aCmd;
            if (cnt == 1) { // at the beginning
            	// because of the sinusoidal wave
            	vCmd_prev = 2*DEG2RAD*(2*3.141592/T_period);
            }
            */

            // acceleration inner loop
            // Notice that using no filtering velocity
            vCmd = vCmd_prev + aCmd / SAMPLE_RATE;
            pCmd = pCmd_prev + vCmd_prev / SAMPLE_RATE + 0.5 * aCmd / (SAMPLE_RATE*SAMPLE_RATE);
            //pCmd = pCmd_prev + vCmd / SAMPLE_RATE;
            //vCmd = vCmd_prev + aCmd * elapsedSec;
            //pCmd = pCmd_prev + vCmd * elapsedSec;
            //test
            test_v_prev = vCmd_prev;

            iCmd = calcI(vCmd, aCmd); // feedforward control. Not being used currently.
            feedforwardVal = iCmd;

            //iCmd += (150* (pCmd - pos) + 0.6 * (vCmd - vel) + Ki * error); // Ki 0
            //iCmd += (200* (pCmd - pos) + 0.6 * (vCmd - vel) + Ki * error); // Ki 0
            //iCmd += (350* (pCmd - pos) + 1.0 * (vCmd - vel) + Ki * error); // Ki 0
            iCmd += (150* (pCmd - pos) + 0.9 * (vCmd - vel) + Ki * error); // Ki 0

            pos_prev = pos;
            pCmd_prev = pCmd;
            vCmd_prev = vCmd;
        }
        else {
            iCmd = 0;
        }

        //**************************************************


        //**************************************************
        // control output
        //
        //limit current based on motor specs
        if(iCmd > (MAX_CURRENT_MA) ) { // 1.6 mA
            HW->SetAnalogOut(IoHardware::AMP_SIGNAL, 0.0);
            HW->WriteDigitalBit(IoHardware::MOTOR_ENABLE, MOTOR_OFF);
            HW->motorStatus = MOTOR_OFF;
            printf("iCmd:%f, pCmd:%f, pos:%f, vCmd:%f, vel:%f, feedforwardVal:%f\n", iCmd, pCmd, pos, vCmd, vel, feedforwardVal);
            FATAL_ERROR("MOTOR CURRENT TOO HIGH");
            iCmd = MAX_CURRENT_MA;
        }
        else if(iCmd < (-MAX_CURRENT_MA) ) {
            HW->SetAnalogOut(IoHardware::AMP_SIGNAL, 0.0);
            HW->WriteDigitalBit(IoHardware::MOTOR_ENABLE, MOTOR_OFF);
            HW->motorStatus = MOTOR_OFF;
            printf("iCmd:%f, pCmd:%f, pos:%f, vCmd:%f, vel:%f, feedforwardVal:%f\n", iCmd, pCmd, pos, vCmd, vel, feedforwardVal);
            FATAL_ERROR("MOTOR CURRENT TOO HIGH");
            iCmd = -MAX_CURRENT_MA;
        }

        ampVCmd =-iCmd * AMP_GAIN; // convert to analog signal. +-10 V.  -0.86 for 4.5 rad/s. for test use -0.8
        // sign change to agree with camera

        // output signal to amp
        if(!done) {
            HW->WriteAnalogCh(IoHardware::AMP_SIGNAL, ampVCmd);
            //HW->WriteAnalogCh(IoHardware::AMP_SIGNAL, 0.0);
        }
        else {
            HW->WriteAnalogCh(IoHardware::AMP_SIGNAL, 0.0);
        }
        //*******************************************************


        //**************************************************

        HW->ProcessOutput(); // all digital & analog writing.

        // keep the position angle within 2pi
        //if ( pos > (mu_k+1)*2*3.141592 )
        //	mu_k++;
        //pos = pos - mu_k*2*3.141592;

        /*num[0] = sec; //aCmd; //correctedAcc; //eta1; //handVel; //pos; //handTheta; //pos; // angle in rad
        num[1] = elapsedSec1; //test_v_prev; //feedforwardVal; //pCmd; //eta2; //obj.theta; //vnum[0]; //obj.theta; //vel; //obj.theta;
        num[2] = elapsedSec2; //feedbackVal; //vCmd; //obj.angVel; //obj.x; //vnum[1]; //obj.x1; //ampVCmd; //obj.x1; // position in m, *10 for cm
        num[3] = vCmd; //handTheta; //iCmd; //sec; //elapsedSec; //sec; //vnum[2]; //obj.y1;
        */

        // set outputs
        /*num[0] = obj_vel_uf; //vInp; //correctedAcc; //eta1; //handVel; //pos; //handTheta; //pos; // angle in rad
        num[1] = vel; //eta1; //feedforwardVal; //pCmd; //eta2; //obj.theta; //vnum[0]; //obj.theta; //vel; //obj.theta;
        num[2] = obj.angVel; //feedbackVal; //vCmd; //obj.angVel; //obj.x; //vnum[1]; //obj.x1; //ampVCmd; //obj.x1; // position in m, *10 for cm
        num[3] = aCmd; //handTheta; //iCmd; //sec; //elapsedSec; //sec; //vnum[2]; //obj.y1;
        */

        num[0] = handVel;
        num[1] = aCmd; //x_offset_test;  // //obj_x_offset;
        num[2] = switching_done; //obj.x;
        num[3] = eta2;

        /*num[0] = handVel;
        num[1] = eta1;
        num[2] = vel;
        num[3] = eta2;
        */

        /*num[0] = vInp;
        num[1] = eta1;
        num[2] = pos;
        num[3] = eta2;
        */

        // acceleration innerloop
        /*num[0] = aCmd;
        num[1] = pCmd;
        num[2] = pos;
        //num[1] = feedforwardVal;
        //num[2] = feedbackVal;
        num[3] = vCmd;
        */

        // motor modeling
        /*num[0] = pos;
        num[1] = iCmd;
        num[2] = cnt;
        num[3] = 0;
        */
        MNET->Process();

    } // while()

    // for some reason, this does not work.
    HW->SetAnalogOut(IoHardware::AMP_SIGNAL, 0.0);
    HW->WriteDigitalBit(IoHardware::MOTOR_ENABLE, MOTOR_OFF);
    HW->motorStatus = MOTOR_OFF;
    delay(10);
    FATAL_ERROR(err_msg);

}
Пример #9
0
Файл: main.c Проект: sndae/b3r1
/*******************************************************
*	PWM test - check wiring and such
*******************************************************/
void PWM_Test(void)
{
    init_pwm();

    while (!(getkey() == 1))
    {

        for (int i = 0; i <= 255 - lmb_PARAM; i++) {
            show12bits(i, i);
            SetLeftMotorPWM(i);
            SetRightMotorPWM(i);
            TimerWait(500);
        }
        for (int i = 0; i <= 255 - lmb_PARAM; i++) {
            show12bits(i, i);
            SetLeftMotorPWM(-i);
            SetRightMotorPWM(-i);
            TimerWait(500);
        }

        /*
         	    printLCD("LFWD");
        	    SetLeftMotorPWM(255);
                SetRightMotorPWM(0);
                TimerWait(3000);

                printLCD("LREV");
        	    SetLeftMotorPWM(-255);
                SetRightMotorPWM(0);
                TimerWait(3000);

                printLCD("RFWD");
        	    SetLeftMotorPWM(0);
                SetRightMotorPWM(255);
                TimerWait(3000);

                printLCD("RREV");
        	    SetLeftMotorPWM(0);
                SetRightMotorPWM(-255);
                TimerWait(3000);

                printLCD("2 FWD low");
        		SetRightMotorPWM(128);
        	    SetLeftMotorPWM(128);
        	    TimerWait(3000);

                printLCD("2 REV med");
        	    SetRightMotorPWM(-200);
        	    SetLeftMotorPWM(-200);
        	    TimerWait(3000);

                printLCD("CCW");
        		SetRightMotorPWM(200);
        	    SetLeftMotorPWM(-200);
        	    TimerWait(3000);

                printLCD("CW");
        	    SetRightMotorPWM(-200);
        	    SetLeftMotorPWM(200);
        	    TimerWait(3000);
        */
    }
    // Done
    SetLeftMotorPWM(0);
    SetRightMotorPWM(0);
}
Пример #10
0
void GLWindow_Mainloop(void)
{
    USE_HIGH_PERFORMANCE_TIMER = EmulatorConfig.highperformancetimer;

    TimerInit();

    SCREEN_TEXTURE = calloc(256*224,4); //max possible size
    int scr_texture_loaded = 0;
    GLuint scr_texture;

    //do { GBA_RunFor(1); } while(GBA_MemoryReadFast16(CPU.R[R_PC]) != 0xDF05); //swi 0x05
    //CPU.R[R_PC] = 0x00000000;
    //do { GBA_RunFor(1); } while(CPU.R[R_PC] != 0x080002B0); GLWindow_GBACreateDissasembler();

    while(1)
    {
        if(GLWindow_HandleEvents()) break;

        if(GLWindow_Active && (PAUSED == 0))
        {
            if(RUNNING == RUN_GBA)
            {
                GLWindow_GBADisassemblerStartAddressSetDefault();

                GLWindow_GBAHandleInput();
                GBA_CheckKeypadInterrupt();

                GBA_RunFor(280896); //clocksperframe = 280896

                if(Keys_Down[VK_SPACE])
                {
                    GBA_SetFrameskip(10);
                    GBA_SoundResetBufferPointers();
                }
                else GBA_SetFrameskip(FRAMESKIP);

                if(GBA_HasToSkipFrame()==0)
                {
                    GBA_ConvertScreenBufferTo32RGB(SCREEN_TEXTURE);

                    glClear(GL_COLOR_BUFFER_BIT); //Clear screen

                    if(scr_texture_loaded) glDeleteTextures(1,&scr_texture);
                    scr_texture_loaded = 1;

                    glGenTextures(1,&scr_texture);
                    glBindTexture(GL_TEXTURE_2D,scr_texture);
                    if(EmulatorConfig.oglfilter)
                    {
                        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
                        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
                    }
                    else
                    {
                        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
                        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
                    }
                    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP);
                    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP);

                    glTexImage2D(GL_TEXTURE_2D,0,4,240,160, 0,GL_RGBA,GL_UNSIGNED_BYTE,SCREEN_TEXTURE);

                    glBindTexture(GL_TEXTURE_2D,scr_texture);
                    glColor3f(1.0,1.0,1.0);
                    glBegin( GL_QUADS );
                        glTexCoord2f(0,0); // Top-left vertex
                        glVertex3f(0,0,0);

                        glTexCoord2f(1,0); // Bottom-left vertex
                        glVertex3f(240,0,0);

                        glTexCoord2f(1,1); // Bottom-right vertex
                        glVertex3f(240,160,0);

                        glTexCoord2f(0,1); // Top-right vertex
                        glVertex3f(0,160,0);
                    glEnd();

                    GLWindow_SwapBuffers();
                }

                GBA_UpdateFrameskip();

                //GLWindow_MemViewerUpdate();
                //GLWindow_IOViewerUpdate();
                //GLWindow_DisassemblerUpdate();

                TimerWait(Keys_Down[VK_SPACE] == 0);
            }
            else
            {
                glClear(GL_COLOR_BUFFER_BIT); //Clear screen
                GLWindow_SwapBuffers();
                Sleep(100);
            }
        }
        else
        {
            Sleep(1); // Allow the CPU to rest a bit :P
        }
/*
if(RUNNING == RUN_GBA)
{
        if(Keys_Down['Q']) { int k = 10; while(k--) GLWindow_GBADisassemblerStep(); GLWindow_GBADisassemblerUpdate(); }
        if(Keys_Down['W']) { int k = 100; while(k--) GLWindow_GBADisassemblerStep(); GLWindow_GBADisassemblerUpdate(); }
        if(Keys_Down['R']) { int k = 1000; while(k--) GLWindow_GBADisassemblerStep(); GLWindow_GBADisassemblerUpdate(); }
}
*/
    }

    GLWindow_UnloadRom(1);

    if(scr_texture_loaded) glDeleteTextures(1,&scr_texture);

    TimerEnd();
}
Пример #11
0
static void *Spindown_Thread(void *arg)
{
    struct TimerInfo info;

    SpinDownState s_state = IDLE;
    SpinDownData  s_data[MAX_SPINDOWN_SAMPLES];
    uint8_t       s_index = 0;
    uint8_t       doSpinDown = 1;

    // set resistance to 0

    // Start the periodic timer @ 1s
    TimerStart (1000000, &info);

    // start the spindown process..
    while(doSpinDown && currentMode == MODE_CALIBRATE)
    {
        // Wait for periodic timer to expire
        TimerWait (&info);

        double kph = currentSpeed;

        switch (s_state)
        {
            case IDLE:
                //printf("%.2f : ", kph);
                //printf("Starting spindown calibration process...\n");
                snprintf(DisplayMessage, DISPLAY_MAX_MSG_SIZE, "Starting spindown calibration process...");

                // zero out the array of spindown entries & set index to start
                memset(s_data, 0, sizeof (s_data));
                s_index = 0;

                s_state = WAIT;
                break;

            case WAIT:
                if (kph > 50)
                {
                    s_state = READY;
                }
                else
                {
                    //printf("%.2f : ", kph);
                    //printf("Please increase speed to over 50 km/h...\n");
                    snprintf(DisplayMessage, DISPLAY_MAX_MSG_SIZE, "Please increase speed to over 50 km/h...");
                }
                break;

            case READY:
                if (kph > 50)
                {
                    //printf("%.2f : ", kph);
                    //printf("Please stop pedaling to initiate calibration...\n");
                    snprintf(DisplayMessage, DISPLAY_MAX_MSG_SIZE, "Please stop pedaling to initiate calibration...");
                }
                else
                {
                    s_state = RUNNING;
                }
                break;

            case RUNNING:
                //printf("%.2f : ", kph);
                //printf("Calibrating, please wait....\n");
                snprintf(DisplayMessage, DISPLAY_MAX_MSG_SIZE, "Calibrating, please wait....");

                // get current speed & add to array
                s_data[s_index].time = s_index+1;
                s_data[s_index].speed = kph;

                // check current speed is less than previous, else error
                if (s_index > 0)
                {
                    if (s_data[s_index].speed > s_data[s_index-1].speed)
                    {
                        s_state = ERROR;
                        break;
                    }
                }

                // have we reached enough samples, or low enough speed
                if ((s_data[s_index].speed < 10) || (s_index == MAX_SPINDOWN_SAMPLES))
                {
                    s_state = DONE;
                    break;
                }

                // ready for next sample
                s_index++;
                break;

            case ERROR:
                //printf("%.2f : ", kph);
                //printf("Error, did you start pedaling? ;)...\n\n");
                snprintf(DisplayMessage, DISPLAY_MAX_MSG_SIZE, "Error, did you start pedaling? ;)...");
                s_state = IDLE;
                break;

            case DONE:
                //printf("%.2f : ", kph);
                //printf("Spindown calibration process successful...\n\n");
                snprintf(DisplayMessage, DISPLAY_MAX_MSG_SIZE,
                        "Spindown successful: a = %lf, b = %lf, c = %lf, d = %lf",
                        power_curve_a, power_curve_b,
                        power_curve_c, power_curve_d);

                //int i;
                for (int i = 0; i < s_index; i++)
                {
                    //printf("%.0f, %.2f\n", s_data[i].time, s_data[i].speed);
                }
                //printf("\n");

                // ...and finish
                doSpinDown = 0;
                break;

            default:
                break;
        }

        if (s_state == DONE)
        {
            // Spindown completed, now fit data to curve
            FitCurve(s_data, s_index);
        }

        if (currentMode != MODE_CALIBRATE)
        {
            //printf("Spindown aborted..\n");
            snprintf(DisplayMessage, DISPLAY_MAX_MSG_SIZE, "Spindown aborted...");
        }
    }

    currentMode = lastMode;
    return NULL;
}
Пример #12
0
static void *ANT_Thread(void *arg)
{
    struct TimerInfo info;

    uint8_t   power_event_count = 0;
    uint16_t  power_accumulated = 0;
    uint16_t  power_instant = 0;

    double    previous_ke = 0;

    uint8_t   power_accel_index;
    double    power_accel_array[POWER_SAMPLE_DEPTH];
    double    power_accel_filtered;

    // zero out the array of power entries & set index to start
    memset(power_accel_array, 0, sizeof (power_accel_array));
    power_accel_index = 0;

    // USB setup
    if (libusb_init(NULL) != 0)
    {
        //printf("libusb: failed to initialise\n");
        snprintf(DisplayMessage, DISPLAY_MAX_MSG_SIZE, "libusb: failed to initialise...");
        return NULL;
    }

    devh = libusb_open_device_with_vid_pid(NULL, vendor_id, product_id);

    if (devh == NULL)
    {
        //printf("libusb: failed to open device 0x%04x:0x%04x\n", vendor_id, product_id);
        snprintf(DisplayMessage, DISPLAY_MAX_MSG_SIZE, "libusb: failed to open device 0x%04x:0x%04x\n", vendor_id, product_id);
    }
    else
    {
        // don't really care about the result..
        libusb_detach_kernel_driver(devh, 0);

        if (libusb_claim_interface(devh, 0) != 0)
        {
            //printf("libusb: failed to claim interface");
            snprintf(DisplayMessage, DISPLAY_MAX_MSG_SIZE, "libusb: failed to claim interface");

        }
        else
        {
            //printf("libusb: succesfully claimed interface\n");
            snprintf(DisplayMessage, DISPLAY_MAX_MSG_SIZE, "libusb: succesfully claimed interface");


            // ANT+ setup

            // reset
            ANTBuildMessage(1, ANT_SYSTEM_RESET, 0, 0, 0, 0, 0, 0, 0, 0, 0);
            ANTTransferMessage();

            // ANT docs say wait 500ms after reset before sending any more host commands
            milliSleep(500);

            // set network key
            ANTBuildMessage(9, ANT_SET_NETWORK, ANT_NETWORK_0, key[0], key[1], key[2], key[3], key[4], key[5], key[6], key[7]);
            ANTTransferMessage();

            // assign channel
            ANTBuildMessage(3, ANT_ASSIGN_CHANNEL, ANT_CHANNEL_1, ANT_CHANNEL_TYPE_TX, ANT_NETWORK_0, 0, 0, 0, 0, 0, 0);
            ANTTransferMessage();

            // set channel id
            uint16_t device_id = 0xBEEF;
            ANTBuildMessage(5, ANT_CHANNEL_ID, ANT_CHANNEL_1, device_id&0xff, (device_id>>8)&0xff, ANT_SPORT_POWER_TYPE, ANT_TRANSMISSION_TYPE, 0, 0, 0, 0);
            ANTTransferMessage();

            // set rf frequency
            ANTBuildMessage(2, ANT_CHANNEL_FREQUENCY, ANT_CHANNEL_1, ANT_SPORT_FREQUENCY, 0, 0, 0, 0, 0, 0, 0);
            ANTTransferMessage();

            // set channel period
            uint16_t period = ANT_SPORT_POWER_PERIOD;
            ANTBuildMessage(3, ANT_CHANNEL_PERIOD, ANT_CHANNEL_1, period&0xff, (period>>8)&0xff, 0, 0, 0, 0, 0, 0);
            ANTTransferMessage();

            // set tx power? (optional)

            // open channel
            ANTBuildMessage(1, ANT_OPEN_CHANNEL, ANT_CHANNEL_1, 0, 0, 0, 0, 0, 0, 0, 0);
            ANTTransferMessage();

            // Start the periodic timer @ 250ms
            // todo: use stricter ANT power message interval?
            TimerStart (250000, &info);

            //TESTING!!!
            // ONCE PER SECOND TO MATCH SPEED UPDATES
            //TimerStart (1000000, &info);

            // Run until terminated from main thread
            while (runAntThread)
            {
                // Wait for periodic timer to expire
                TimerWait (&info);

                // Broadcast data
                ////printf("ANT_Thread: timer expired...\n");

                // Data Page 0x10 message format
                // Byte 0 : Data Page Number - 0x10
                // Byte 1 : Update event count
                // Byte 2 : Pedal power - 0xFF
                // Byte 3 : Instantaneous cadence - 0xFF
                // Byte 4 : Accumulated power (LSB)
                // Byte 5 : Accumulated power (MSB)
                // Byte 6 : Instantaneous power (LSB)
                // Byte 7 : Instantaneous power (MSB)

                // #define POWER   150
                // power_instant = POWER;

                double speed = currentSpeed;

                // calculate the static power
                double power_static = (power_curve_a +
                                      (power_curve_b*speed) +
                                      (power_curve_c*speed*speed) +
                                      (power_curve_d*speed*speed*speed));

                // calculate the kinetic energy at this speed
                double speed_ms = speed / 3.6;
                double current_ke = GetKineticEnergy(speed_ms);

                // calculate the acceleration power. This calculation is dependent on the update
                // frequency, as we are looking for the change in stored kinetic energy per second
                //
                double power_accel = (current_ke - previous_ke) * PRU_UPDATE_FREQ;

                // todo: this may need some smoothing as the frequency increases?
                //       - start with a simple moving average of the acceleration power
                power_accel_array[power_accel_index++] = power_accel;
                if (power_accel_index >= POWER_SAMPLE_DEPTH)
                {
                    power_accel_index = 0;
                }

                power_accel_filtered = 0;
                for (int i = 0; i < POWER_SAMPLE_DEPTH; i++)
                {
                    power_accel_filtered += power_accel_array[i];
                }
                power_accel_filtered /= POWER_SAMPLE_DEPTH;

                //snprintf(DisplayMessage, DISPLAY_MAX_MSG_SIZE, "current_ke = %lf, previous_ke = %lf", total_kinetic_energy, previous_ke);
                //snprintf(DisplayMessage, DISPLAY_MAX_MSG_SIZE, "power_static = %.0lf, power_accel = %.0lf", power_static, power_accel);

                // Print out the value received from the PRU code
                //printf("%u events, %.0f RPM, %.2f km/h, %.2f watts\r\n", events, rpm, kph, power_static+power_accel);

                previous_ke = current_ke;

                power_event_count++;

                power_instant = power_static + power_accel_filtered;

                currentPower = power_instant;

                power_accumulated += power_instant;

                ANTBuildMessage(9, ANT_BROADCAST_DATA, ANT_CHANNEL_1, ANT_STANDARD_POWER, power_event_count, 0xFF, 0xFF,
                        power_accumulated&0xff, (power_accumulated>>8)&0xff, power_instant&0xff, (power_instant>>8)&0xff);

                ANTTransferMessage();
            }

            // ANT+ cleanup
            //printf("ANT_Thread: cleanup\n");

            // close channel
            ANTBuildMessage(1, ANT_CLOSE_CHANNEL, ANT_CHANNEL_1, 0, 0, 0, 0, 0, 0, 0, 0);
            ANTTransferMessage();

            // USB cleanup
            libusb_release_interface(devh, 0);
        }

        // USB cleanup
        libusb_close(devh);
    }
    libusb_exit(NULL);
    return NULL;
}
Пример #13
0
static void *Speed_Thread(void *arg)
{
    struct TimerInfo info;

    //return NULL;

    // Initialise the PRU
    //printf("Initialising PRU\n");
    if (prussdrv_init() != 0)
    {
        snprintf(DisplayMessage, DISPLAY_MAX_MSG_SIZE, "prussdrv_init failed");
        return NULL;
    }

    // Open an event
    if (prussdrv_open(PRU_EVTOUT_0))
    {
        //printf("prussdrv_open failed\n");
        snprintf(DisplayMessage, DISPLAY_MAX_MSG_SIZE, "prussdrv_open failed");
        return NULL;
    }

    // Get pointers to PRU0 local memory
    void *pruDataMem;
    prussdrv_map_prumem (PRUSS0_PRU0_DATARAM, &pruDataMem);
    unsigned int *pruData = (unsigned int *) pruDataMem;

    // Execute code on PRU
    //printf("Executing speed pru code\n");
    if (prussdrv_exec_program (0, "../pru/BeagleTrainer.bin") < 0)
    {
        //printf("prussdrv_exec_program failed\n");
        snprintf(DisplayMessage, DISPLAY_MAX_MSG_SIZE, "prussdrv_exec_program failed");
        return NULL;
    }

    // Start the periodic timer @ 125ms
    TimerStart (125000, &info);

    // Run until terminated from main thread
    while (runSpeedThread)
    {
        // Wait for periodic timer to expire
        TimerWait (&info);

        // Broadcast data
        ////printf("Speed Thread: timer expired...\n");
        uint32_t events = pruData[0];
        uint32_t frequency = events * PRU_UPDATE_FREQ;

        double rps = (double)frequency / PULSE_PER_REV;
        //double rpm = rps * 60.0;
        double mps = rps * 2.0 * M_PI * RADIUS / 1000.0;
        double kph = mps * 3.6;

        currentSpeed = kph;
        currentFrequency = events;

        // Print out the value received from the PRU code
        ////printf("%u events, %.0f RPM, %.2f km/h\r\n", events, rpm, kph);
        //snprintf(DisplayMessage, DISPLAY_MAX_MSG_SIZE, "%u events, %.0f RPM, %.2f km/h", events, rpm, kph);

}

    // cleanup
    //printf("Speed Thread: cleanup\n");
    prussdrv_pru_disable(0);
    prussdrv_exit();

    return NULL;
}