Esempio n. 1
0
void set_PID_vals(row, col)
{

    u32 btnsw;

    NX3_readBtnSw(&btnsw);
    // Set which control measurement we're using
    if (btnsw & msk_BTN_NORTH)
    {
        // increment to the next selection.  If we're at the last enum, set it to the 1st (proportional)
        if (PID_current_sel == OFFSET) PID_current_sel = PROPORTIONAL;
        else PID_current_sel = (Control_t)((int)(PID_current_sel+1));  //casting to allow increment

        // cursor control logic
        if (PID_current_sel == PROPORTIONAL)
        {
            row = 1;
            col = 2;
        }
        else if (PID_current_sel == INTEGRAL)
        {
            row = 1;
            col = 7;
        }
        else if (PID_current_sel == DERIVATIVE)
        {
            row = 1;
            col = 12;
        }
        else if (PID_current_sel == OFFSET)
        {
            row  = 2;
            col  = 13;
        }
    }

    delay_msecs(20);
    //set cursor location and turn on cursor
    LCD_setcursor(row,col);
    LCD_docmd(LCD_DISPLAYONOFF, LCD_CURSOR_ON);


    if (btnsw & msk_BTN_EAST)
    {
        if (PID_current_sel == PROPORTIONAL)     prop_gain     += GAIN_INCREMENT;
        else if (PID_current_sel == INTEGRAL)    integral_gain += GAIN_INCREMENT;
        else if (PID_current_sel == DERIVATIVE)  deriv_gain    += GAIN_INCREMENT;
        else                                     offset        += GAIN_INCREMENT;
    }
    if (btnsw & msk_BTN_WEST)
    {
        if (PID_current_sel == PROPORTIONAL)     prop_gain     -= GAIN_INCREMENT;
        else if (PID_current_sel == INTEGRAL)    integral_gain -= GAIN_INCREMENT;
        else if (PID_current_sel == DERIVATIVE)  deriv_gain    -= GAIN_INCREMENT;
        else                                     offset        -= GAIN_INCREMENT;
    }
}
int main() {
	XStatus Status;
	u32 btnsw = 0x00000000, old_btnsw = 0x000000FF, diff_btnsw;
	u32 leds;
	int rotcnt, old_rotcnt;
	Test_t test;
	ParamSel_t param_select;

	bool wrlcd_dynamic;
	bool wrlcd_static;

	// initialize devices and set up interrupts, etc.
	Status = do_init();
	if (Status != XST_SUCCESS) {
		LCD_setcursor(1, 0);
		LCD_wrstring("****** ERROR *******");
		LCD_setcursor(2, 0);
		LCD_wrstring("INIT FAILED- EXITING");
		exit(XST_FAILURE);
	}

	// initialize the global variables
	timestamp = 0;
	pwm_freq = PWM_FREQUENCY_HZ;
	pwm_duty = PWM_STEPDC_MIN;

	// initialize the local variables
	btnsw = 0x00;
	old_btnsw = 0x00;
	rotcnt = 0;
	old_rotcnt = 0;
	param_select = SLCT_GP;
	wrlcd_dynamic = true;
	wrlcd_static = true;
	leds = LEDS_ALLOFF;
	_prev_button_down_count = 0;
	// Enable the Microblaze caches and
	// kick off the processing by enabling the Microblaze interrupt
	// this starts the FIT timer which updates the timestamp.
	if (USE_ICACHE == 1) {
		microblaze_invalidate_icache();
		microblaze_enable_icache();
	}
	if (USE_DCACHE == 1) {
		microblaze_invalidate_dcache();
		microblaze_enable_dcache();
	}
	microblaze_enable_interrupts();

	// display the greeting
	LCD_setcursor(1, 0);
	LCD_wrstring("ECE544 Project 2");
	LCD_setcursor(2, 0);
	LCD_wrstring("Starter App-R5.0");
	NX3_writeleds(LEDS_ALLON);
	delay_msecs(2000);
	NX3_writeleds(LEDS_ALLOFF);

	// Set the PWM and ADC min and max counts by forcing a characterization
	LCD_clrd();
	LCD_setcursor(1, 0);
	LCD_wrstring("Characterizing..");
	DoTest_Characterize();
	LCD_setcursor(2, 0);
	LCD_wrstring("...Done         ");

	//*****GAIN AND OFFSET ARE HARDWIRED IN THE STARTER APP *****
	// initialize the control parameters
	// start the set point in the middle of the range	
	setpoint = (ADC_max_cnt - ADC_min_cnt) / 2;
	offset = 100;
	GP = 10;
	GD = 5;
	GI = 5;
	init_high = false;
	//*****GAIN AND OFFSET ARE HARDWIRED IN THE STARTER APP *****


	// main loop - there is no exit except by hardware reset
	while (1) {
		// write static portion of output to display
		if (wrlcd_static) {
			LCD_clrd();
			LCD_setcursor(1, 0);
			LCD_wrstring("G|Pxxx Ixxx Dxxx");
			LCD_setcursor(2, 0);
			LCD_wrstring("SP:+x.xx OFF:xxx");
			wrlcd_static = false;
		}

		// write the dynamic portion of output to display
		if (wrlcd_dynamic) {
			Xfloat32 v;
			u16 count;
			char s[20];
			u32 row, col;

			// display GP, GI, and GD
			LCD_setcursor(1, 3);
			LCD_wrstring("   ");
			LCD_setcursor(1, 3);
			LCD_putnum(GP, 10);

			LCD_setcursor(1, 8);
			LCD_wrstring("   ");
			LCD_setcursor(1, 8);
			LCD_putnum(GI, 10);

			LCD_setcursor(1, 13);
			LCD_wrstring("   ");
			LCD_setcursor(1, 13);
			LCD_putnum(GD, 10);

			LCD_setcursor(2, 13);
			LCD_wrstring("   ");
			LCD_setcursor(2, 13);
			LCD_putnum(offset, 10);

			// display the setpoint in volts
			count = setpoint;
			v = PmodCtlSys_ADCVolts(count);
			voltstostrng(v, s);
			LCD_setcursor(2, 3);
			LCD_wrstring("     ");
			LCD_setcursor(2, 3);
			LCD_wrstring(s);

			// place the cursor under the currently selected parameter
			LCD_docmd(LCD_DISPLAYONOFF, LCD_CURSOR_OFF);
			switch (param_select) {
				case SLCT_OFFSET:
					row = 2;
					col = 13;
					break;
				case SLCT_GP:
					row = 1;
					col = 3;
					break;
				case SLCT_GD:
					row = 1;
					col = 13;
					break;
				case SLCT_GI:
					row = 1;
					col = 8;
					break;
				case SLCT_INVALID:
					break;
			}
			if (param_select != SLCT_INVALID) {
				LCD_setcursor(row, col);
				LCD_docmd(LCD_DISPLAYONOFF, LCD_CURSOR_ON);
			}
			wrlcd_dynamic = false;
		}

		// read switches and buttons to get the test to perform and its initial value
		// display the selected test on the LEDs   
		NX3_readBtnSw(&btnsw);
		init_high = (btnsw & SW_INIT_HILO) ? true : false;
		test = btnsw & SW_TEST_SELECT;

		//update the HI/LO and TEST LEDs
		leds &= ~(LEDS_HILO | LEDS_TEST);
		leds |= (init_high == true) ? LEDS_HILO : 0;
		leds |= test << 3;
		NX3_writeleds(leds);

		// read rotary count and handle setpoint changes
		// accelerate setpoint if speedup threshold has been exceeded
		// Use MIN and MAX to keep scaled ADC count within range
		ROT_readRotcnt(&rotcnt);
		if (rotcnt != old_rotcnt) {
			if (abs(rotcnt - old_rotcnt) > ROTCNT_SPEEDUP_GATE) {
				setpoint += (rotcnt - old_rotcnt) * ROTCNT_DELTA
						* ROTCNT_SPEEDUP_FACTOR;
			} else {
				setpoint += (rotcnt - old_rotcnt) * ROTCNT_DELTA;
			}
			setpoint = MAX(ADC_min_cnt, MIN(setpoint, ADC_max_cnt));
			old_rotcnt = rotcnt;
			wrlcd_dynamic = true;
		} // rotary count changed

		//***** NOTE:  User interface handling should go in this section.  *****
		//***** The starter app just lets the user select the setpoint and *****
		//***** run the test selected by the switches                      *****

		// look at the buttons and take action on the rising edge of a button press
		bool north_button = btnsw & msk_BTN_NORTH;
		bool east_button = btnsw & msk_BTN_EAST;
		bool west_button = btnsw & msk_BTN_WEST;
		bool button_down = north_button || east_button || west_button;

		if (button_down)
		{
			_prev_button_down_count++;
			if (_prev_button_down_count > 5)
				_prev_button_down_count = 5;

			if (north_button) {
				switch (param_select) {
					case SLCT_GP:
						param_select = SLCT_GI;
						break;
					case SLCT_GI:
						param_select = SLCT_GD;
						break;
					case SLCT_GD:
						param_select = SLCT_OFFSET;
						break;
					case SLCT_OFFSET:
						param_select = SLCT_GP;
						break;					
				}
				wrlcd_dynamic = true;
			}

			if (east_button) {
				switch (param_select) {
					case SLCT_GP:
						GP+=_prev_button_down_count;
						break;
					case SLCT_GI:
						GI+=_prev_button_down_count;
						break;
					case SLCT_GD:
						GD+=_prev_button_down_count;
						break;
					case SLCT_OFFSET:
						offset+=_prev_button_down_count;
						break;					
				}
				wrlcd_dynamic = true;
			}

			if (west_button) {
				switch (param_select) {
					case SLCT_GP:
						GP-=_prev_button_down_count;
						break;
					case SLCT_GI:
						GI-=_prev_button_down_count;
						break;			
					case SLCT_GD:
						GD-=_prev_button_down_count;
						break;
					case SLCT_OFFSET:
						offset-=_prev_button_down_count;
						break;					
				}
				wrlcd_dynamic = true;
			}
		} else {
			_prev_button_down_count = 0;
		}

		btnsw &= BTN_MSK_ALLBUTTONS;
		diff_btnsw = (btnsw ^ old_btnsw) & btnsw;
		old_btnsw = btnsw;

		if (diff_btnsw & BTN_RUN_XFER) // run a test
		{
			Xfloat32 vADC, vSP; // VADC from circuit and Vsetpoint
			char s1[20], s2[30]; // display and print strings
			int (*funcPtr)(void); // pointer to test function
			int n; // number of samples to transfer (returned by test functions)

			switch (test) // set up for the selected test
			{
				case TEST_BB: // Bit Bang control
					strcpy(s1, "|BB  |Press RBtn");
					strcpy(s2, "Bit Bang Control Test Data");
					funcPtr = DoTest_BB;
					break;
				case TEST_PID: // PID control
					strcpy(s1, "|PID |Press RBtn");
					strcpy(s2, "PID Control Test Data");
					funcPtr = DoTest_PID;
					break;
				case TEST_CHARACTERIZE: // characterize control system simulator
					strcpy(s1, "|CHAR|Press RBtn");
					strcpy(s2, "Characterization Test Data");
					funcPtr = DoTest_Characterize;
					break;
				default: // no test to run
					strcpy(s1, "");
					strcpy(s2, "");
					funcPtr = NULL;
					break;
			}

			// Change the display to indicate that a test will be run			
			LCD_clrd();
			LCD_setcursor(1, 0);
			LCD_wrstring(s1);
			LCD_setcursor(2, 0);
			LCD_wrstring("LED OFF-Release ");

			// turn on Run LED to show the test has begun
			// and do the test.  The test will return when the ADC samples array
			// has been filled.  Turn off the rightmost LED after the data has been 
			// captured to let the user know he/she can release the button
			if (funcPtr != NULL) {
				leds |= LEDS_RUN;
				NX3_writeleds(leds);
				n = funcPtr();
				leds &= ~LEDS_RUN;
				NX3_writeleds(leds);
				// wait for the Rotary Encoder button to be released
				// and then send the sample data to stdout
				do {
					NX3_readBtnSw(&btnsw);
					delay_msecs(10);
				} while (btnsw & BTN_RUN_XFER);

				// turn on Transfer LED to indicate that data is being transmitted
				// Show the transfer  traffic on the LCD
				leds |= LEDS_XFER;
				NX3_writeleds(leds);
				LCD_clrd();
				LCD_setcursor(1, 0);
				LCD_wrstring("Sending Data....");
				LCD_setcursor(2, 0);
				LCD_wrstring("S:    DATA:     ");

				// transfer the descriptive heading followed by the data	
				xil_printf("\n\r%s\tAppx. Sample Interval: %d msec\n\r", s2,
						adc_smple_interval);
				xil_printf("sample\tADCCount\tVout\tsetpoint\tPWMCount\n\r");

				// tell SerialCharter to start gathering data)
				xil_printf("===STARTPLOT===\n");

				for (smpl_idx = 2; smpl_idx < n; smpl_idx++) {
					u16 count;

					count = sample[smpl_idx];
					vADC = PmodCtlSys_ADCVolts(count);
					vSP = PmodCtlSys_ADCVolts(setpoint);
					voltstostrng(vADC, s1);
					voltstostrng(vSP, s2);
					xil_printf("%d\t%d\t%s\t%s\t%d\n\r", smpl_idx, count, s1,
							s2, PIDdebug[smpl_idx].pwm_count);
					LCD_setcursor(2, 2);
					LCD_wrstring("   ");
					LCD_setcursor(2, 2);
					LCD_putnum(smpl_idx, 10);
					LCD_setcursor(2, 11);
					LCD_wrstring("     ");
					LCD_setcursor(2, 11);
					LCD_putnum(count, 10);
				}

				// tell SeriaCharter to stop gathering data and display the graph			
				xil_printf("===ENDPLOT===\n");

				// transfer complete - turn Transfer LED off and wrap up command processing
				leds &= ~LEDS_XFER;
				NX3_writeleds(leds);
				wrlcd_static = true;
				wrlcd_dynamic = true;
			}
		} // run a test

		// wait a bit and start again
		delay_msecs(100);
	} // while(1) loop
} // end main()
Esempio n. 3
0
int main()
{
    XStatus 	Status;
    u32			btnsw = 0x00000000, old_btnsw = 0x000000FF;
    int			rotcnt, old_rotcnt = 0x1000;
    Test_t		test, next_test;
    is_scaled = false;
    bool lcd_initial = true;
    char 		sp[20];
    u16 row = 1;
    u16 col = 2;
    bool calc_done = false;
    freq_min_cnt = 3000;


    // initialize devices and set up interrupts, etc.
    Status = do_init();
    if (Status != XST_SUCCESS)
    {
        LCD_setcursor(1,0);
        LCD_wrstring("****** ERROR *******");
        LCD_setcursor(2,0);
        LCD_wrstring("INIT FAILED- EXITING");
        exit(XST_FAILURE);
    }

    // initialize the variables
    timestamp = 0;
    pwm_freq = PWM_FREQUENCY;
    next_test = TEST_INVALID;

    prop_gain     = PROP_INIT_GAIN;
    integral_gain = INT_INIT_GAIN;
    deriv_gain    = DERIV_INIT_GAIN;


    // Enable the Microblaze caches and kick off the processing by enabling the Microblaze
    // interrupt.  This starts the FIT timer which updates the timestamp.
    if (USE_ICACHE == 1)
    {
        microblaze_invalidate_icache();
        microblaze_enable_icache();
    }
    if (USE_DCACHE == 1)
    {
        microblaze_invalidate_dcache();
        microblaze_enable_dcache();
    }
    microblaze_enable_interrupts();

    // display the greeting
    LCD_setcursor(1,0);
    LCD_wrstring("PWM Ctrl sys");
    LCD_setcursor(2,0);
    LCD_wrstring("Erik R, Caren Z");
    NX3_writeleds(0xFF);
    //delay_msecs(2500);

    LCD_clrd();
    LCD_setcursor(1,0);
    LCD_wrstring("Characterizing..");
    //Run the LED characterization routine to establish sensor min's and max's
    DoTest_Characterize();
    LCD_setcursor(2,0);
    LCD_wrstring("Done.");



    NX3_writeleds(0x00);

    delay_msecs(500);
    //set initial screen
    // main loop - there is no exit except by hardware reset
    while (1)
    {
        //set Vin to min or max depending on switch 3 value
        if (btnsw & msk_SWITCH3)
        {
            pwm_duty = MAX_DUTY;
            dc_start = MAX_DUTY;
        }
        else
        {
            pwm_duty = MIN_DUTY;
            dc_start = MIN_DUTY;
        }

        // Write values to display when in "idle" state
        if (lcd_initial)
        {
            LCD_clrd();
            LCD_setcursor(1,0);
            LCD_wrstring("P:   I:   D:   ");
            LCD_setcursor(1,2);
            LCD_setcursor(2,0);
            LCD_wrstring("SP:+     OFF:   ");
            lcd_initial = false;
            LCD_setcursor(1,2);
        }
        no_test_LCD();
        NX3_readBtnSw(&btnsw);
        // Set which control measurement we're using
        if (btnsw & msk_BTN_NORTH)
        {
            // increment to the next selection.  If we're at the last enum, set it to the 1st (proportional)
            if (PID_current_sel == OFFSET) PID_current_sel = PROPORTIONAL;
            else PID_current_sel = (Control_t)((int)(PID_current_sel+1));  //casting to allow increment

            // cursor control logic
            if (PID_current_sel == PROPORTIONAL)
            {
                row = 1;
                col = 2;
            }
            else if (PID_current_sel == INTEGRAL)
            {
                row = 1;
                col = 7;
            }
            else if (PID_current_sel == DERIVATIVE)
            {
                row = 1;
                col = 12;
            }
            else if (PID_current_sel == OFFSET)
            {
                row  = 2;
                col  = 13;
            }
        }

        delay_msecs(20);
        //set cursor location and turn on cursor
        LCD_setcursor(row,col);
        LCD_docmd(LCD_DISPLAYONOFF, LCD_CURSOR_ON);

        if (btnsw & msk_BTN_EAST)
        {
            if (PID_current_sel == PROPORTIONAL)     prop_gain     += GAIN_INCREMENT;
            else if (PID_current_sel == INTEGRAL)    integral_gain += GAIN_INCREMENT;
            else if (PID_current_sel == DERIVATIVE)  deriv_gain    += GAIN_INCREMENT;
            else                                     offset        += GAIN_INCREMENT;
        }
        if (btnsw & msk_BTN_WEST)
        {
            if (PID_current_sel == PROPORTIONAL)     prop_gain     -= GAIN_INCREMENT;
            else if (PID_current_sel == INTEGRAL)    integral_gain -= GAIN_INCREMENT;
            else if (PID_current_sel == DERIVATIVE)  deriv_gain    -= GAIN_INCREMENT;
            else                                     offset        -= GAIN_INCREMENT;
        }


        // read sw[1:0] to get the test to perform.
        NX3_readBtnSw(&btnsw);
        test = btnsw & (msk_SWITCH1 | msk_SWITCH0);

        ROT_readRotcnt(&rotcnt);
        if (rotcnt != old_rotcnt)
        {
            //scale rotary count to setpoint values
            LCD_docmd(LCD_DISPLAYONOFF, LCD_CURSOR_OFF);
            setpoint = MAX(VOLT_MIN, MIN((rotcnt/SETPOINT_SCALE), VOLT_MAX));
            voltstostrng(setpoint, sp);
            old_rotcnt = rotcnt;
            LCD_setcursor(2, 3);
            LCD_wrstring(sp);
            LCD_setcursor(2, 3);
            LCD_wrstring('+');
        }

        if (test == TEST_T_CALLS) //unused
        {
            next_test = TEST_INVALID;
            lcd_initial = true;
            delay_msecs(3000);

        }


        else if ((test == TEST_BANG ) || (test == TEST_PID))  // Test 1 & 2 - control methods Bang Bang and PID
        {
            Xfloat32	v;
            char		s[20];

            // start the test on the rising edge of the Rotary Encoder button press
            // the test will write the light detector samples into the global "sample[]"
            // the samples will be sent to stdout when the Rotary Encoder button
            // is released
            //
            // NOTE ON DETECTING BUTTON CHANGES:
            // btnsw ^ old_btnsw will set the bits for all of the buttons and switches that
            // have changed state since the last time the buttons and switches were read
            // msk_BTN_ROT & btnsw will test whether the rotary encoder was one of the
            // buttons that changed from 0 -> 1

            if ((btnsw ^ old_btnsw) && (msk_BTN_ROT & btnsw))  // do the step test and dump data
            {

                /*Running Test (rotary pushbutton pushed): Show which control algorithm is running and
                  display instructions for running test and uploading data.*/

                if (test != next_test)
                {
                    if (test == TEST_BANG)
                    {
                        strcpy(s, "|BANG|Long Press");
                    }
                    else
                    {
                        strcpy(s, "|PID|Long Press");
                    }
                    delay_msecs(100);
                    LCD_clrd();
                    LCD_setcursor(1,0);
                    LCD_wrstring(s);
                    LCD_setcursor(2,0);
                    LCD_wrstring("RotBtn to start");
                }

                NX3_writeleds(0x01);
                if (test == TEST_BANG)  // perform bang bang calculations
                {
                    calc_bang();
                }
                else  // perform PID tests
                {
                    calc_PID();
                }
                NX3_writeleds(0x00);

                //FEATURE: wait for user input to send data over
                NX3_readBtnSw(&btnsw);
                if ((btnsw ^ old_btnsw) && (msk_BTN_ROT & btnsw))
                {
                    // light "Transfer" LED to indicate that data is being transmitted
                    // Show the traffic on the LCD
                    NX3_writeleds(0x02);
                    LCD_clrd();
                    LCD_setcursor(1, 0);
                    LCD_wrstring("Sending Data....");
                    LCD_setcursor(2, 0);
                    LCD_wrstring("S:    DATA:     ");

                    // print the descriptive heading followed by the data
                    if (test == TEST_BANG)
                    {
                        xil_printf("\n\rBang Bang! Test Data\t\tAppx. Sample Interval: %d msec\n\r", frq_smple_interval);
                    }
                    else
                    {
                        xil_printf("\n\rPID Test Data\t\tAppx. Sample Interval: %d msec\n\r", frq_smple_interval);
                    }

                    // trigger the serial charter program)
                    xil_printf("===STARTPLOT===\n");

                    // start with the second sample.  The first sample is not representative of
                    // the data.  This will pretty-up the graph a bit
                    for (smpl_idx = 1; smpl_idx < NUM_FRQ_SAMPLES; smpl_idx++)
                    {
                        u16 count;

                        count = sample[smpl_idx];
                        if (count > 4096)
                            count = 4095;

                        v = (-3.3 / 4095.0) * (count) + 3.3;

                        voltstostrng(v, s);
                        xil_printf("%d\t%d\t%s\n\r", smpl_idx, count, s);

                        LCD_setcursor(2, 2);
                        LCD_wrstring("   ");
                        LCD_setcursor(2, 2);
                        LCD_putnum(smpl_idx, 10);
                        LCD_setcursor(2, 11);
                        LCD_wrstring("     ");
                        LCD_setcursor(2, 11);
                        LCD_putnum(count, 10);
                    }

                    // stop the serial charter program
                    xil_printf("===ENDPLOT===\n");

                    NX3_writeleds(0x00);
                    old_btnsw = btnsw;
                    next_test = TEST_INVALID;
                    lcd_initial = true;
                    delay_msecs(3000);
                }
            }  // do the step test and dump data

        }
        else if (test == TEST_CHARACTERIZE)  // Test 3 - Characterize Response
        {

            // start the test on the rising edge of the Rotary Encoder button press
            // the test will write the samples into the global "sample[]"
            // the samples will be sent to stdout when the Rotary Encoder button
            // is released
            NX3_readBtnSw(&btnsw);
            if ((btnsw ^ old_btnsw) && (msk_BTN_ROT & btnsw))
            {
                LCD_docmd(LCD_DISPLAYONOFF, LCD_CURSOR_OFF);
                // light "Run" (rightmost) LED to show the test has begun
                // and do the test.  The test will return when the measured samples array
                // has been filled.  Turn off the rightmost LED after the data has been
                if (test != next_test)
                {
                    LCD_clrd();
                    LCD_setcursor(1,0);
                    LCD_wrstring("|CHAR|Long Press");
                    LCD_setcursor(2,0);
                    LCD_wrstring("RotBtn to start");
                }
                // captured to let the user know he/she can release the button
                NX3_readBtnSw(&btnsw);
                if ((msk_BTN_ROT & btnsw) && (!calc_done))
                {
                    NX3_writeleds(0x01);
                    LCD_clrd();
                    LCD_setcursor(1,0);
                    LCD_wrstring("Characterizing..");
                    DoTest_Characterize();
                    LCD_setcursor(2,0);
                    LCD_wrstring("Done.");
                    NX3_writeleds(0x00);
                    delay_msecs(750);
                    LCD_setcursor(2,8);
                    LCD_wrstring("<OK>");
                    calc_done = true;
                    next_test = test;
                }

                NX3_readBtnSw(&btnsw);
                if (msk_BTN_ROT & btnsw)
                {
                    // light "Transfer" LED to indicate that data is being transmitted
                    // Show the traffic on the LCD
                    NX3_writeleds(0x02);
                    LCD_clrd();
                    LCD_setcursor(1, 0);
                    LCD_wrstring("Sending Data....");
                    LCD_setcursor(2, 0);
                    LCD_wrstring("S:    DATA:     ");

                    xil_printf("\n\rCharacterization Test Data\t\tAppx. Sample Interval: %d msec\n\r", frq_smple_interval);

                    // trigger the serial charter program)
                    xil_printf("===STARTPLOT===\n\r");

                    for (smpl_idx = STEPDC_MIN; smpl_idx <= STEPDC_MAX; smpl_idx++)
                    {
                        u16 		count;
                        Xfloat32	v;
                        char		s[10];

                        count = sample[smpl_idx];
                        if (count > 4096)
                            count = 4095;

                        v = (-3.3 / 4095.0) * (count) + 3.3;

                        voltstostrng(v, s);
                        xil_printf("%d\t%d\t%s\n\r", smpl_idx, count, s);

                        LCD_setcursor(2, 2);
                        LCD_wrstring("   ");
                        LCD_setcursor(2, 2);
                        LCD_putnum(smpl_idx, 10);
                        LCD_setcursor(2, 11);
                        LCD_wrstring("     ");
                        LCD_setcursor(2, 11);
                        LCD_putnum(count, 10);
                    }

                    // stop the serial charter program
                    xil_printf("===ENDPLOT===\n\r");

                    NX3_writeleds(0x00);
                    old_btnsw = btnsw;
                    next_test = TEST_INVALID;
                    delay_msecs(1000);
                    lcd_initial = true;
                    delay_msecs(3000);
                }
            }  // do the step test and dump data

        } // Test 3 - Characterize Response
        else  // outside the current test range - blink LED's and hang
        {
            // should never get here but just in case
            NX3_writeleds(0xFF);
            delay_msecs(2000);
            NX3_writeleds(0x00);
        }
        // wait a bit and start again

        delay_msecs(100);
    }  // while(1) loop
}  // end main()