Example #1
0
void no_test_LCD()
{
    u32 btnsw;
    NX3_readBtnSw(&btnsw);
    // Set which control measurement we're using
    if (btnsw & msk_BTN_EAST || btnsw & msk_BTN_WEST)
    {
        // Write Proportional gain
        LCD_setcursor(1, 2);
        LCD_wrstring("   ");
        LCD_setcursor(1, 2);
        LCD_putnum(prop_gain, 10);

        // Write Integral gain
        LCD_setcursor(1, 7);
        LCD_wrstring("   ");
        LCD_setcursor(1, 7);
        LCD_putnum(integral_gain, 10);

        // Write Derivative gain
        LCD_setcursor(1, 12);
        LCD_wrstring("   ");
        LCD_setcursor(1, 12);
        LCD_putnum(deriv_gain, 10);

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

    }
}
Example #2
0
/*****
 * update_lcd() - update the LCD display with a new count and voltage
 *
 * writes the display with new information.  "vin_dccnt" is the  unsigned PWM duty
 * cycle and "frqcnt" is the signed frq_count.  The function assumes that the
 * static portion of the display has been written and that the dynamic portion of
 * the display is the same for all tests
 *****/
void update_lcd(int vin_dccnt, short frqcnt)
{
    Xfloat32	v;
    char		s[10];

    // update the PWM data
    v = vin_dccnt * .01 * PWM_VIN;
    voltstostrng(v, s);
    LCD_setcursor(1, 11);
    LCD_wrstring("      ");
    LCD_setcursor(1, 11);
    LCD_wrstring(s);


    v = (-3.3 / 4095.0) * (frqcnt) + 3.3;
    voltstostrng(v, s);
    LCD_setcursor(2, 3);
    LCD_wrstring("     ");
    LCD_setcursor(2, 3);
    LCD_wrstring(s);
    LCD_setcursor(2, 11);
    LCD_wrstring("     ");
    LCD_setcursor(2, 11);
    LCD_putnum(frqcnt, 10);
    return;
}
 /*****
  * update_lcd() - update the LCD display with a new count and voltage
  *
  * writes the display with new information.  "vin_dccnt" is the  unsigned PWM duty
  * cycle and "frqcnt" is the signed frq_count.  The function assumes that the
  * static portion of the display has been written and that the dynamic portion of
  * the display is the same for all tests
  *****/
 void update_lcd(int vin_dccnt, short frqcnt)
 {
 	Xfloat32	v;
 	char		s[10];

 	// update the PWM data
 	v = vin_dccnt * .01 * PWM_VIN;
 	voltstostrng(v, s);
 	LCD_setcursor(1, 11);
 	LCD_wrstring("      ");
 	LCD_setcursor(1, 11);
 	LCD_wrstring(s);

 	// update the data
    // ECE544 Students: Convert frequency count to 'volts'
     v = LIGHTSENSOR_Count2Volts((Xuint32)frqcnt);
 	voltstostrng(v, s);
 	LCD_setcursor(2, 3);
 	LCD_wrstring("     ");
 	LCD_setcursor(2, 3);
 	LCD_wrstring(s);
 	LCD_setcursor(2, 11);
 	LCD_wrstring("     ");
 	LCD_setcursor(2, 11);
 	LCD_putnum(frqcnt, 10);
 	return;
 }
Example #4
0
/*****
 * update_lcd() - update the frequency/duty cycle LCD display
 * 
 * writes the frequency and duty cycle to the specified line.  Assumes the
 * static portion of the display is already written and the format of each
 * line of the display is the same.
 *****/
void update_lcd(int freq, int dutycycle, u32 linenum)
{
	LCD_setcursor(linenum, 5);
	LCD_wrstring("   ");
	LCD_setcursor(linenum, 5);
	if (freq < 1000) // display Hz if frequency < 1Khz
	{
		LCD_putnum(freq, 10);
	}
	else  // display frequency in KHz
	{
		LCD_putnum((freq / 1000), 10);
		LCD_wrstring("K");
	}
	LCD_setcursor(linenum, 13);
	LCD_wrstring("  %");
	LCD_setcursor(linenum, 13);
	LCD_putnum(dutycycle, 10);
}
Example #5
0
int main()
{
	XStatus 	Status;
	u32			btnsw, old_btnsw = 0x000000FF;
	int			rotcnt, old_rotcnt = 0x1000;	
	bool		done = false;
	
	// 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 = INITIAL_FREQUENCY;
	pwm_duty = INITIAL_DUTY_CYCLE;
	clkfit = 0;
	new_perduty = false;
    
	// start the PWM timer and kick of the processing by enabling the Microblaze interrupt
	PWM_SetParams(&PWMTimerInst, pwm_freq, pwm_duty);	
	PWM_Start(&PWMTimerInst);
    microblaze_enable_interrupts();
    
	// display the greeting   
    LCD_setcursor(1,0);
    LCD_wrstring(" PWM LIB TEST R0");
	LCD_setcursor(2,0);
	LCD_wrstring(" by Roy Kravitz ");
	NX3_writeleds(0x000000FF);
	delay_msecs(2000);
	NX3_writeleds(0x00000000);
		
   // write the static text to the display
    LCD_clrd();
    LCD_setcursor(1,0);
    LCD_wrstring("G|FR:    DCY:  %");
    LCD_setcursor(2,0);
    LCD_wrstring("Vavg:           ");
      
    // main loop
	do
	{ 
		// check rotary encoder pushbutton to see if it's time to quit
		NX3_readBtnSw(&btnsw);
		if (btnsw & msk_BTN_ROT)
		{
			done = true;
		}
		else
		{
			new_perduty = false;				
			if (btnsw != old_btnsw)
			{	 
				switch (btnsw & PWM_FREQ_MSK)
				{
					case 0x00:	pwm_freq = PWM_FREQ_10HZ;	break;
					case 0x01:	pwm_freq = PWM_FREQ_100HZ;	break;
					case 0x02:	pwm_freq = PWM_FREQ_1KHZ;	break;
					case 0x03:	pwm_freq = PWM_FREQ_10KHZ;	break;
                                        case 0x04: 	pwm_freq = PWM_FREQ_50KHZ;	break;
                                        case 0x05: 	pwm_freq = PWM_FREQ_100KHZ;	break;
                                        case 0x06: 	pwm_freq = PWM_FREQ_150KHZ;	break;
                                        case 0x07: 	pwm_freq = PWM_FREQ_200KHZ;	break;
				}
				old_btnsw = btnsw;
				new_perduty = true;
			}
		
			// read rotary count and handle duty cycle changes
			// limit duty cycle to 0% to 99%
			ROT_readRotcnt(&rotcnt);
			if (rotcnt != old_rotcnt)
			{
				pwm_duty = MAX(0, MIN(rotcnt, 99));
				old_rotcnt = rotcnt;
				new_perduty = true;
			}

			// update generated frequency and duty cycle	
			if (new_perduty)
			{
				u32 freq, dutycycle;
				float vavg;
				char	s[10];
			
				// set the new PWM parameters - PWM_SetParams stops the timer
				Status = PWM_SetParams(&PWMTimerInst, pwm_freq, pwm_duty);
				if (Status == XST_SUCCESS)
				{
					PWM_GetParams(&PWMTimerInst, &freq, &dutycycle);
					update_lcd(freq, dutycycle, 1);
					vavg = dutycycle * .01 * 3.3;
					voltstostrng(vavg, s);
					LCD_setcursor(2,5);
					LCD_wrstring(s); 
										
					PWM_Start(&PWMTimerInst);
				}
			}
		}			
	} while (!done);
	
	// wait until rotary encoder button is released		
	do
	{
		NX3_readBtnSw(&btnsw);
		delay_msecs(10);
	} while ((btnsw & msk_BTN_ROT) == 0x80);

	// and say goodbye
	LCD_clrd();
	LCD_wrstring("That's all folks");
	delay_msecs(2000);
	LCD_clrd();
	exit(XST_SUCCESS);
 }
/*****
 * DoTest_BB() - Perform Bang-Bang control
 * 
 * This function adjusts the global "pwm_duty" value to try to bring the
 * control system simulator closer to the ADC setpoint.  The initial state of the PWM
 * is derived from the global "init_high" flag. NUM_ADC_SAMPLES are collected
 * into the global array sample[] and the count returned.  The function toggles the TEST_RUNNING
 * signal high for the duration of the test as a debug aid.  An approximate
 * ADC sample interval is written to the global variable "adc_smpl_interval"
 *
 * Bang-bang control is the simplest form of closed loop control.  The PWM is turned either full-on
 * of full-off depending on whether the output is below or above the setpoint. 
 *
 * NOTE:  THIS ALGORIHM IS LEFT AS AN EXERCISE FOR THE PROGRAMMER
 *****/
int DoTest_BB(void)
{

	XStatus Status;         // Xilinx return status
	unsigned tss;           // starting timestamp
	u16 adc_1, adc_2; 		// holds the ADC count - sampled twice and averaged
	u16 adc_cnt; 			// ADC counts to display
	int n; 					// number of samples
    Xfloat32 setpoint_volts, adc_reading_volts;

	//The initial state of the PWM is derived from the global "init_high" flag.
	// true if inital state of test should be full-on, false otherwise
	if (init_high == true)
		pwm_duty = PWM_STEPDC_MAX;
	else
		pwm_duty = PWM_STEPDC_MIN;

	//Checks Status
	Status = PWM_SetParams(&PWMTimerInst, pwm_freq, pwm_duty);
	if (Status == XST_SUCCESS) {
		PWM_Start(&PWMTimerInst);
	} else {
		return -1;
	}
	delay_msecs(100);

	smpl_idx = 0;
	n = 0;
	tss = timestamp;

	//Calculate in volts our setpoint
	setpoint_volts = PmodCtlSys_ADCVolts(setpoint);
	////-------------------------------
	LCD_clrd();
	LCD_setcursor(1, 0);
	LCD_wrstring("BANG BANG");

	while (smpl_idx <= NUM_ADC_SAMPLES)
	{
		Status = PWM_SetParams(&PWMTimerInst, pwm_freq, pwm_duty);
		if (Status == XST_SUCCESS)
		{
			PWM_Start(&PWMTimerInst);
		}
		else {
			return -1;
		}

		//Sample the ADC and average the readings
		adc_1 = PmodCtlSys_readADC(&SPIInst);
		delay_msecs(1);
		adc_2 = PmodCtlSys_readADC(&SPIInst);
		adc_cnt = (adc_1 + adc_2) / 2;

		//Converting the ADC average into volts
		adc_reading_volts = PmodCtlSys_ADCVolts(adc_cnt);

		if (adc_reading_volts < setpoint_volts)
			pwm_duty = PWM_STEPDC_MIN; //input to 100% duty cycle
		else
			pwm_duty = PWM_STEPDC_MAX;

		//CHECK STATUS
		Status = PWM_SetParams(&PWMTimerInst, pwm_freq, pwm_duty);
		if (Status == XST_SUCCESS) {
			PWM_Start(&PWMTimerInst);
		} else {
			return -1;
		}

		//store in array
		sample[smpl_idx++] = adc_cnt;
		n++;
	}
	adc_smple_interval = (timestamp - tss) / smpl_idx;
	return n;

}
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()
Example #8
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()
int main()
{
	XStatus 	Status;
	u32			btnsw = 0x00000000, old_btnsw = 0x000000FF;
	int			rotcnt, old_rotcnt = 0x1000;
	Test_t		test, next_test;
	is_scaled = false;
	
	// 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;
	pwm_duty = STEPDC_MIN;
	next_test = TEST_INVALID;

	// 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("PmodCtlSys Test ");
	LCD_setcursor(2,0);
	LCD_wrstring("R3.0 by Robin M.");
	NX3_writeleds(0xFF);
	//Run the LED characterization routine to establish sensor min's and max's
    DoTest_Characterize();
	NX3_writeleds(0x00);
       
    // main loop - there is no exit except by hardware reset
	while (1)
	{ 
		// read sw[1:0] to get the test to perform.
		NX3_readBtnSw(&btnsw);
		test = btnsw & (msk_SWITCH1 | msk_SWITCH0);
			
		if (test == TEST_TRACKING)  // Test 0 = Track PWM voltage
		{
			// write the static info to display if necessary
			if (test != next_test)
			{
				LCD_clrd();
				LCD_setcursor(1,0);
				LCD_wrstring("| TRK| Vi: sx.xx");
				LCD_setcursor(2,0);
				LCD_wrstring("Vo:sx.xx C:sxxxx");
			}
				 						
			// read rotary count and handle duty cycle changes
			// limit duty cycle to between STEPDC_MIN and STEPDC_MAX
			// PWM frequency does not change in this test
			ROT_readRotcnt(&rotcnt);
			if (rotcnt != old_rotcnt)
			{
				pwm_duty = MAX(STEPDC_MIN, MIN(rotcnt, STEPDC_MAX));
				old_rotcnt = rotcnt;
			}
			DoTest_Track();
			next_test = TEST_TRACKING;
		}   // Test 0 = Track PWM voltage
		else if ((test == TEST_STEPHILO) || (test == TEST_STEPLOHI))  // Test 1 & 2 - Step response 
		{
			Xfloat32	v;
			char		s[20];	
			
			// write the static info to the display if necessary
			if (test != next_test)
			{
				if (test == TEST_STEPHILO)
				{
					strcpy(s, "|HILO|Press RBtn");
				}
				else
				{
					strcpy(s, "|LOHI|Press RBtn");
				}
				
				LCD_clrd();
				LCD_setcursor(1,0);
				LCD_wrstring(s);
				LCD_setcursor(2,0);
				LCD_wrstring("LED OFF-Release ");
			}
			
			// 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  
			{										
				// 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 
				// captured to let the user know he/she can release the button
				NX3_writeleds(0x01);
				if (test == TEST_STEPHILO)  // perform High->Low step test
				{				
					DoTest_Step(STEPDC_MAX);
				}
				else  // perform Low->High step
				{
					DoTest_Step(STEPDC_MIN);
				}
				NX3_writeleds(0x00);
				
				// 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 & msk_BTN_ROT) == 0x80);
				
				// 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_STEPHILO)
				{
					xil_printf("\n\rHigh->Low Test Data\t\tAppx. Sample Interval: %d msec\n\r", frq_smple_interval);
				}
				else
				{
					xil_printf("\n\rLow->High 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];
					
					//ECE544 Students:
                    //Convert from count to 'volts'
					v = LIGHTSENSOR_Count2Volts(count);
					
					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;			
			}  // do the step test and dump data
			else
			{
				next_test = test;
			}
		} // Test 1 & 2 - Step response
		else if (test == TEST_CHARACTERIZE)  // Test 3 - Characterize Response
		{
			if (test != next_test)
			{				
				LCD_clrd();
				LCD_setcursor(1,0);
				LCD_wrstring("|CHAR|Press RBtn");
				LCD_setcursor(2,0);
				LCD_wrstring("LED OFF-Release ");
			}
			// 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
			if ((btnsw ^ old_btnsw) && (msk_BTN_ROT & btnsw))  // do the step test and dump data  
			{
				// 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
				// captured to let the user know he/she can release the button
				NX3_writeleds(0x01);			
				DoTest_Characterize();
				NX3_writeleds(0x00);
				
				// 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 & msk_BTN_ROT) == 0x80);
				
				// 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];
					
					//ECE544 Students:
                    //Convert from count to 'volts'
					v = LIGHTSENSOR_Count2Volts((Xuint32)count);
					
					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;
			}  // do the step test and dump data
			else
			{
				next_test = test;
			}
		} // 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()