/*************************************************************************/ /* */ /* HYBRID CONTROL V2.0 */ /* */ /* Efstratios D. Varkaris */ /* */ /* May 1990 */ /* */ /*************************************************************************/ /* INCLUDE THE HEADER FILES TO BE USED DURING COMPILATION */ #include "cwindows.h" /* contains declarations of the windows functions */ #include #include #include #include #include #include #include #include #include #include "rti815.h" /* header containing the RTI815 board definitions */ #include "winplus.h" /* header containing the new windows definitions */ #include "hybrid.h" /* header containing the hybrid control definitions */ #define SPACE ' ' /* THE FOLLOWING ARE GLOBAL VARIABLES USED IN THE WINDOWS INTERFACES */ FORMAT form1 = { FALSE, /* raw mode? */ 4, /* tab size */ LF2CRLF, /* end-of-line-formatting */ DELETE, /* backspace */ LOWER, /* case use */ {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1} }; /* RECORD CONTAINING THE DEFAULT VALUES FOR THE MENU FIELD ATTRIBUTES */ MENU_KEYS k1 = { 0, /* key that moves highlight to top */ 0, /* key that moves highlight to bottom */ UP_ARROW, /* key that moves highlight up one item */ DOWN_ARROW, /* key that moves highlight down */ 0, /* key that selects the highlighted item */ ESCAPE, /* key that makes menu disappear */ 0, /* key to page up */ 0, /* key to page down */ 0, /* user defined routine u_popup1(WINDOW *w, int keyinfo); */ 0, /* user defined routine u_popup2(WINDOW *w, int keyinfo); */ LEFT_ARROW, /* key that moves cursor left */ RIGHT_ARROW, /* key that moves cursor right */ HOME, /* key that moves cursor to beginning of line */ END, /* key that moves cursor to end of line */ CTRL_RIGHT_ARROW, /* key that moves cursor to next word */ CTRL_LEFT_ARROW, /* key that moves cursor to previous word */ DELETE }; /* KEYSTROKES NOT TO BE PROCESSED BUT NOT GIVING ERROR EITHER */ int neutrals[] = {CTRLC_KEY, INS, '\0'}; /* THE INPUT FIELD SPECS: FLOAT WITH 12 CHARS AND UP TO 7 DECIMALS */ char fspecs[] = "%12.7f"; /* no check is done for number of decimals now */ /* THE GLOBAL VARIABLE THAT CONTROLS THE run_control LOOP TERMINATION */ char stopped = FALSE; /* THE NEW INTERRUPT HANDLER FOR THE CONTROL-BREAK KEY */ void interrupt far break_handler() { stopped = TRUE; } /* UNUSED USER-ERROR FUNCTION NECESSARY TO DEFINE FOR THE WINDOWS TOOLKIT */ int user_err(FIELD *f, int scan, int key) { return(FALSE); } /**************************************************************************/ /* */ /* FUNCTION: display_help */ /* */ /* DESCRIPTION: The function opens a window on the screen and displays */ /* a brief summary of the editing keys. Pressing any key */ /* closes the help window returning to the previous screen. */ /* */ /* ARGUMENTS: ------- */ /* */ /* COMMENTS:þ Anything can be put here. It's better though if it is kept */ /* as generic as possible. */ /* þ The original implementation created the help window once, */ /* when first called, and then hidde/displayed it whenever was */ /* selected. It never worked OK. The memory allocation sceme in */ /* the CWTM51L.LIB is most likely the problem. Now it allocates */ /* and frees the memory every time is invoked. It works fine, */ /* yet a bit slower. */ /* */ /* */ /* CREATION: ver. 2.0 Efstratios D. Varkaris 03/25/90 */ /* */ /* REVISION: */ /* */ /**************************************************************************/ void display_help(void) { /* DEFINE THE WINDOW ATTRIBUTES AND THEIR DEFAULT VALUES */ WINDOW *helpwin; COLORS hcols; BORDER hbord; SHADOW hshad; int hwidth, hheight, scan; int dot = 254; hwidth = 61; hheight = 18; /* TURN CURSOR_OFF, CREATE THE WINDOW STRUCTURE AND DISPLAY IT ON SCREEN */ cursor_off(); create_window (&helpwin, hwidth, hheight); border_window(helpwin, &hbord, SINGLE, SINGLE, brown, white); clear_window(helpwin, &hcols, black, white, SPACE); display_window(helpwin, 3, 7, 1); /* WRITE THE HELP TEXT IN THE WINDOW BUFFER */ w_prints(helpwin, CENTER, 1, magenta, white, "Brief Help"); w_prints(helpwin, CENTER, 2, magenta, white, "ÄÄÄÄÄÄÄÄÄÄ"); w_printc(helpwin, 2, 3, magenta, white, 254); w_prints(helpwin, 5, 3, hcols.fc, hcols.bc, "To compute the values for the controller parameters"); w_prints(helpwin, 5, 4, hcols.fc, hcols.bc, "use the equations described in the Lab notes."); w_printc(helpwin, 2, 6, magenta, white, 254); w_prints(helpwin, 5, 6, hcols.fc, hcols.bc, "To move among the fields use or arrows."); w_prints(helpwin, 34, 6, lwhite, hcols.bc,"ENTER"); w_prints(helpwin, 43, 6, lwhite, hcols.bc,"UP/DOWN"); w_printc(helpwin, 2, 8, magenta, white, 254); w_prints(helpwin, 5, 8, hcols.fc, hcols.bc, "To edit a field use & arrows."); w_prints(helpwin, 25, 8, lwhite, hcols.bc, "BACKSPACE/DEL"); w_prints(helpwin, 41, 8, lwhite, hcols.bc, "LEFT/RIGHT"); w_printc(helpwin, 2, 10, magenta, white, 254); w_prints(helpwin, 5, 10, hcols.fc, hcols.bc, "To start the Aquisition/Control part press ."); w_prints(helpwin, 48, 10, lwhite, hcols.bc, "END"); w_printc(helpwin, 2, 12, magenta, white, 254); w_prints(helpwin, 5, 12, hcols.fc, hcols.bc, "To end the Control part press ."); w_prints(helpwin, 35, 12, lwhite, hcols.bc, "CTRL-BREAK"); w_printc(helpwin, 2, 14, magenta, white, 254); w_prints(helpwin, 5, 14, hcols.fc, hcols.bc, "To QUIT the program press ."); w_prints(helpwin, 31, 14, lwhite, hcols.bc, "ESC"); w_prints(helpwin, 35, 16, hcols.fc, hcols.bc, "Press any key to return."); /* READ THE KEY PRESSED, CLEAR THE KEYBOARD, HIDE THE WINDOW AND FREE THE MEMORY */ scan = _bios_keybrd(_KEYBRD_READ); clear_kbd(); hide_window(helpwin); free_window_memory(helpwin); cursor_on(); } /**************************************************************************/ /* */ /* FUNCTION: setup_timer */ /* */ /* DESCRIPTION: The function checks for the presence of the RTI815 A/D */ /* board and the AM9513 timer chip, by writing a 0xFF to the */ /* TIME_CMD_R location and trying to read back the default */ /* value 0x0B00 from the Counter Mode Register. If read OK, */ /* it programs the timer according to the sampling requested.*/ /* */ /* */ /* ARGUMENTS: sample: the sampling period in msec. */ /* */ /* */ /* COMMENTS:þ This is the most crucial routine as it sets the sampling */ /* period. It is hardware (board & chip) dependent and must be */ /* rewritten any time the A/D board is changed. */ /* þ It is general enough to handle any sampling requested from */ /* the theoretical upper limit (1æs/sample) to 10 min/sample. */ /* Yet the A/D board limitations and the bus bandwidth restrict */ /* the usable upper limit to less than 12KHz. */ /* þ Note that the AM9513 chip is a very powerfull timer and a */ /* small fraction of its capabilities are exploited here. The */ /* programmer must read the AM9513 SYSTEM TIMIMG CONTROLLER */ /* handbook (1983 AMD, Inc) before attempting to modify any of */ /* the code statements. */ /* */ /* CREATION: ver. 2.0 Efstratios D. Varkaris 03/25/90 */ /* */ /* REVISION: */ /* */ /**************************************************************************/ int setup_timer(double sample) { unsigned long sampling; /* the terminal value for the counter */ BYTE fout_source; /* the FOUT source of the MMR at fig 1-13 */ BYTE count_source; /* the Count source of the CMR at fig 1-18 */ BYTE higbyte, lowbyte; /* the 16bit counter byters */ BYTE MMR_lbyte, CMR_hbyte; /* frequency control bytes for the timer */ BYTE exist[2]; /* 2byte array to hold the bytes read from the CMR */ int *existptr; /* pointer to correlate the 2byte array with an int */ /* THE SAMPLE ASSUMED IN ms (DOUBLE) IS CONVERTERD INTO æs (LONG). Remember that theoretically the smallest sampling period the chip handles is 1 æsec (1MHz), yet the A/D board has a limit of 33 æsec (30KHz). Also with the current programming conventions the largest sampling period will be a little more that 10 min. (655350 msec) */ sampling = (long) (1000 * sample); existptr = (int *)exist; /* associate the 2byte array with an int */ /* INITIALIZE THE OSCILATOR SOURCES IN THE TWO MODE REGISTERS */ fout_source = 0xB0; /* choose F1 as FOUT source in fig 1-13 */ count_source = 0x0B; /* choose F1 as Count Source in fig 1-18 */ /* PROGRESIVELY CHANGE THE SOURCES AND DECREASE THE sampling VARIABLE */ while (sampling > 65535) { sampling /= 10; fout_source += 0x10; count_source++; } /* ATTACH THE DEFAULT BITS TO COMPLETE THE TWO MODE REGISTERS */ MMR_lbyte = fout_source & ~0x0F; /* TOD off, comprtrs off, fig 1-13 */ CMR_hbyte = count_source & 0x0F; /* No gating 000, Rising edge, fig 1-18 */ /* DERIVE THE HIGH AND LOW BYTES OF THE COUNTER IN HEX FORMAT */ higbyte = sampling / 256; lowbyte = sampling % 256; /* INITIALIZE THE AM9513 TIMER CIRCUITRY ACCORDING TO THE SAMPLING GIVEN. For details on programming the Timer Chip, see the AM9513 Timer Cotroller manual pages 1-1 to 1-26. In general you output the register Address first on the TIME_CMD_R and then R/W the data using the TIME_DAT_R. The register addresses are on fig 1-10. Use also pg 1-5 and fig 1-8. */ outp(TIME_CMD_R, 0xFF); /* reset the AM9513 counter chip */ outp(TIME_CMD_R, 0x17); /* select the Master Mode Register, fig 1-13 */ outp(TIME_DAT_R, MMR_lbyte); /* write the low byte to the MMR, fig 1-13 */ outp(TIME_DAT_R, 0xC1); /* FOUT by 1, FOUT ON, 8bt bus, BCD count: 11000001 */ outp(TIME_CMD_R, 0x01); /* select the Counter Mode Register, fig 1-18 */ /* CHECK IF THE COUNTER MODE REGISTER (CMD) HAS THE DEFAULT (RESET) VALUE */ exist[low] = inp(TIME_DAT_R); exist[hig] = inp(TIME_DAT_R); if (*existptr != 0x0B00) /* this is the CMR reset value as in pg1-22 */ return(-1); /* no A/D board exists, or the Timer is not responding */ /* PROGRAM THE COUNTER 1 REGISTER TO COUNT DOWN FROM sampling TO 0 AND STOP */ outp(TIME_DAT_R, 0x02); /* OUT high TC toggle, count mode A: 00000001 */ outp(TIME_DAT_R, CMR_hbyte); /* write the high byte to the CMR, fig 1-18 */ outp(TIME_CMD_R, 0x09); /* select the Load Register of Counter 1 */ outp(TIME_DAT_R, lowbyte); /* write the low byte to the Load Register */ outp(TIME_DAT_R, higbyte); /* write the high byte to the Load Register */ outp(TIME_CMD_R, 0x41); /* load the Load Register into the Counter 1 */ return(0); } /**************************************************************************/ /* */ /* FUNCTION: run_step */ /* */ /* DESCRIPTION: The function sends a step output of given magnitude to */ /* the D/A channel 0. */ /* */ /* ARGUMENTS: sample: The sampling period in msec. (placeholder for the */ /* future). */ /* step: The magnitude of the step output in volts. Must be in*/ /* the range of ñ10 volts. */ /* */ /* COMMENTS:þ Very strainghtforward implementation. The pieces of code */ /* that are commented out, are placeholders for future updates. */ /* þ Note that step values out of the range are automatically */ /* trancuated by using the closest avalaible values. */ /* þ Since the sampling is not currently used, a default value */ /* of 10 msec is used, so that the same setup_timer routine is */ /* used to check for the board existence. */ /* */ /* */ /* CREATION: ver. 2.0 Efstratios D. Varkaris 03/25/90 */ /* */ /* REVISION: */ /* */ /**************************************************************************/ int run_step(double sample, double step) { int *ctrl_ptr; /* used to associate the 2byte array "control" with int */ int dummy; /* just a dummy variable to clean the keyboard */ BYTE control[2]; /* 2byte array holding the step value to be output to the D/A channel 0 */ /* DEFINE THE WINDOW VARIABLES */ WINDOW *errwin; SHADOW s1; BORDER b1; COLORS errcols; int errwth, errhght, errxpos, errypos; sample = 1.; /* IF THE setup_timer RETURNED A HARDWARE ERROR, INFORM THE USER AND ABORT */ if (setup_timer(sample) != 0) { errwth = 30; errhght = 6; errxpos = 50; errypos = 18; create_window(&errwin, errwth, errhght); shadow_window(errwin, &s1, BOTTOM_RIGHT, black, SPACE); border_window(errwin, &b1, DOUBLE, SINGLE, white, brown); clear_window(errwin, &errcols, white, brown, SPACE); w_prints(errwin, CENTER, 1, lwhite + blink, brown, "HARDWARE ERROR!"); w_prints(errwin, CENTER, 2, lwhite, brown, "A/D Board not responding."); w_prints(errwin, CENTER, 3, lwhite, brown, "Press any key to abort."); display_window(errwin, errxpos, errypos, 1); _bios_keybrd(_KEYBRD_READ); hide_window(errwin); free_window_memory(errwin); return(-1); } ctrl_ptr = (int *)control; /* manipulate the control as an integer */ /* INITIALIZE THE A/D CHANNELS OF THE RTI-815 BOARD */ outp(CLEAR_R, 0x00); /* clear the status register of the board */ outp(MULTPLX_R, 0x82); /* scan disable, chan 0 selected => 00000000 */ outp(STRT_AD_R, 0x00); /* start the very first conversion of chan 0 */ /* CALCULATE THE STEP INPUT, AND TRUNCATE IT IF NECESSARY */ *ctrl_ptr = (int) (204.8 * step); /* convert to int in -2048 to +2047 mv */ if (*ctrl_ptr > maxsample) *ctrl_ptr = maxsample; if (*ctrl_ptr < minsample) *ctrl_ptr = minsample; /* WRITE THE STEP VALUE TO THE D/A CHANNEL 0 */ outp(DA0_LOW_R, control[low]); outp(DA0_HIG_R, control[hig]); /* WAIT FOR SOMEBODY TO HIT CTRL-BREAK */ stopped = FALSE; while (!stopped) {;} outp(DA0_LOW_R, 0x00); /* reset the D/A channel value */ outp(DA0_HIG_R, 0x00); clear_kbd(); /* clear the keyboard buffer (for decent BIOS) */ /* SOME BIOS ARE NOT AS COMPATIBLE, SO TRY SOMETHING MORE DRASTIC */ while (_bios_keybrd(_KEYBRD_READY)) dummy = _bios_keybrd(_KEYBRD_READ); return(0); } /**************************************************************************/ /* */ /* FUNCTION: run_pid */ /* */ /* DESCRIPTION: The routine implements the digital Proportional Integral*/ /* Derivative (PID) controller. It reads the process output */ /* from the A/D channel 0, calculates the PID control signal,*/ /* and outputs the result to the D/A channel 0. */ /* */ /* ARGUMENTS: sample: The sampling period in msec. */ /* refer: The reference signal in volts (range ñ10 v). */ /* progain: The proportional gain coefficient. */ /* intgain: The integral gain coefficient. */ /* dergain: The derivative gain coefficient. */ /* */ /* COMMENTS:þ For details about the algorithm, see the relevant handout */ /* and the references, given there. */ /* þ The software clipper prohibits calculated control values */ /* to exceed the ñ10 v. region. */ /* */ /* CREATION: ver. 2.0 Efstratios D. Varkaris 03/25/90 */ /* */ /* REVISION: */ /* */ /**************************************************************************/ int run_pid (double sample, double refer, double progain, double intgain, double dergain) { BYTE control[2], input[2]; /* 2byte arrays holding the input and control values of the A/D & and D/A channels 0 */ /* The variables below are those described in equation (3), page 4 of the experiment handout */ int reference; /* the reference signal r(k) */ int oldpfactor; /* the old proportional factor e1(k-1) */ int newpfactor; /* the new proportional factor e1(k) */ double oldifactor; /* the old integral factor e22(k-1) */ double newifactor; /* the new integral factor e22(k) */ double newdfactor; /* the new derivative factor e23(k) */ double timediffer; /* the samping period T in seconds */ int dummy; /* needed to read/clean the keyboard */ int *ctrl_ptr, *inpt_ptr; /* used to associate 2byte arrays with int */ /* The following 2 bytes hold the old and current values of the status register of counter 0. When a 0 count is reached a bit is flipped. By comparing the two continuously, we detect the end of the sampling period. */ register char new_status, old_status; /* DEFINE THE ERROR WINDOW VARIABLES */ WINDOW *errwin; SHADOW s1; BORDER b1; COLORS errcols; int errwth, errhght, errxpos, errypos; /* REPORT ERROR IF sample IS LOWER OR EQUAL TO ZERO AND ABORT EXECUTION */ if (sample <= 0.) { errwth = 30; errhght = 6; errxpos = 50; errypos = 18; create_window(&errwin, errwth, errhght); shadow_window(errwin, &s1, BOTTOM_RIGHT, black, SPACE); border_window(errwin, &b1, DOUBLE, SINGLE, white, brown); clear_window(errwin, &errcols, white, brown, SPACE); w_prints(errwin, CENTER, 1, lwhite + blink, brown, "INPUT ERROR!"); w_prints(errwin, CENTER, 2, lwhite, brown, "Sampling should be > 0."); w_prints(errwin, CENTER, 3, lwhite, brown, "Press any key to abort."); display_window(errwin, errxpos, errypos, 1); _bios_keybrd(_KEYBRD_READ); hide_window(errwin); free_window_memory(errwin); return(-1); } /* IF THE setup_timer RETURNED A HARDWARE ERROR, INFORM THE USER AND ABORT */ if (setup_timer(sample) != 0) { errwth = 30; errhght = 6; errxpos = 50; errypos = 18; create_window(&errwin, errwth, errhght); shadow_window(errwin, &s1, BOTTOM_RIGHT, black, SPACE); border_window(errwin, &b1, DOUBLE, SINGLE, white, brown); clear_window(errwin, &errcols, white, brown, SPACE); w_prints(errwin, CENTER, 1, lwhite + blink, brown, "HARDWARE ERROR!"); w_prints(errwin, CENTER, 2, lwhite, brown, "A/D Board not responding."); w_prints(errwin, CENTER, 3, lwhite, brown, "Press any key to abort."); display_window(errwin, errxpos, errypos, 1); _bios_keybrd(_KEYBRD_READ); hide_window(errwin); free_window_memory(errwin); return(-1); } /* INITIALIZE THE A/D CHANNELS OF THE RTI-815 BOARD */ outp(CLEAR_R, 0x00); /* clear the status register of the board */ outp(MULTPLX_R, 0x00); /* scan disable, chan 0 selected => 00000000 */ /* INITIALIZE THE A/D AND D/A VARIABLES AND CALCULATE THE CONSTANTS */ ctrl_ptr = (int *)control; inpt_ptr = (int *)input; reference = (int) (204.8 * refer); /* convert to int in -2048 to +2048 mv */ timediffer = sample / 1000.; /* convert sampling in seconds */ stopped = FALSE; /* CALCULATE THE FIRST CONTROL SIGNAL (CAUSAL SYSTEMS =>NEITHER INTEGRAL NOR DERIVATIVE TERMS PRESENT AT FIRST SIGNAL) */ newpfactor = 0; newifactor = 0; /* FOR THE INITIAL CONTROL SIGNAL THE PROGAIN IS ASSUMED ONE */ *ctrl_ptr = reference; /* SEND THE FIRST CONTROL SIGNAL */ outp(DA0_LOW_R, control[low]); outp(DA0_HIG_R, control[hig]); /* STRART THE FIRST A/D CONVERSION */ outp(STRT_AD_R, 0x00); /* start conversion of the channel 0 */ /* RUN THE CONTROL LAW UNTIL SOMEONE HITS CTRL-BREAK */ while (!stopped) { outp(TIME_CMD_R, 0x21); /* start the sampling count-down */ /* UPDATE THE CONTROLLER COEFFICIENTS AND GET A NEW INPUT */ oldpfactor = newpfactor; oldifactor = newifactor; input[low] = inp(AD_LOW_R); input[hig] = inp(AD_HIG_R); /* CALCULATE THE CONTROLLER OUTPUT */ newpfactor = reference - *inpt_ptr; newifactor = oldifactor + timediffer * newpfactor; newdfactor = (newpfactor - oldpfactor) / timediffer; *ctrl_ptr = (int) (progain * newpfactor + intgain * newifactor + dergain * newdfactor); /* USE A CLIPPER TO CUT DOWN VALUES BEYOND THE ñ10 Volt LIMIT OF THE D/A */ if (*ctrl_ptr > maxsample) *ctrl_ptr = maxsample; if (*ctrl_ptr < minsample) *ctrl_ptr = minsample; /* WRITE THE NEW CONTROLLER VALUE TO THE D/A CONVERTER */ outp(DA0_LOW_R, control[low]); outp(DA0_HIG_R, control[hig]); /* WAIT UNTIL THE SAMPLING PERIOD HAS ELAPSED */ old_status = inp(TIME_CMD_R); do new_status = inp(TIME_CMD_R); while (((new_status ^ old_status) & TIMEOUT) != TIMEOUT); outp(STRT_AD_R, 0x00); /* start conversion of the channel 0 */ } /* SOMEONE INTERRUPTED THE RUN: RESET THE D/A, AND FLUSH THE KEYBOARD */ outp(DA0_LOW_R, 0x00); outp(DA0_HIG_R, 0x00); clear_kbd(); /* clear the keyboard buffer (for decent BIOS) */ /* SOME BIOS ARE NOT AS COMPATIBLE, SO TRY SOMETHING MORE DRASTIC */ while (_bios_keybrd(_KEYBRD_READY)) dummy = _bios_keybrd(_KEYBRD_READ); return(0); } /**************************************************************************/ /* */ /* FUNCTION: run_fst */ /* */ /* DESCRIPTION: The routine implements the discrete Finite Settling Time*/ /* (FST) controller. It reads the process output from the */ /* A/D channel 0, calculates the FST control signal, and */ /* outputs the result to the D/A channel 0. An optional delay*/ /* parameter allows the control signal to be delayed several */ /* sampling periods. */ /* */ /* ARGUMENTS: sample: The sampling period in msec. */ /* refer: The reference signal in volts (range ñ10 v). */ /* delay: The number of sampling periods (ó255) to be delayed*/ /* ergain0: The error signal gain of the kth instant. */ /* ergain1: The error signal gain of the (k-1)th instant. */ /* ergain2: The error signal gain of the (k-2)th instant. */ /* ctgain1: The control signal gain of the (k-1-m)th instant.*/ /* ctgain2: The control signal gain of the (k-2-m)th instant.*/ /* */ /* COMMENTS:þ For details about the algorithm, see the relevant handout */ /* and the references, given there. */ /* þ The software clipper prohibits calculated control values */ /* to exceed the ñ10 v. region. */ /* þ The old error and control signals needed to calculate the */ /* current control ouput, are stored in circular arrays of int */ /* double types respectively. The manipulation of the various */ /* values takes place through pointers that span the circular */ /* arrays in ascending fushion. */ /* */ /* CREATION: ver. 2.0 Efstratios D. Varkaris 03/25/90 */ /* */ /* REVISION: */ /* */ /**************************************************************************/ int run_fst (double sample, double refer, int delay, double ergain0, double ergain1, double ergain2, double ctgain1, double ctgain2) { BYTE control[2], input[2]; /* 2byte arrays holding the input and control values of the A/D & and D/A channels 0 */ /* The following 2 bytes hold the old and current values of the status register of counter 0. When a 0 count is reached a bit is flipped. By comparing the two continuously, we detect the end of the sampling period. */ register char new_status, old_status; /* The variables below are those described in equation (25) page 15 of the experiment handout */ int *inpt_ptr; /* pointer to the input signal y(k) (as integer) */ int *ctrl_ptr; /* pointer the output signal u(k) (as integer) */ int reference; /* the reference signal r(k) */ int *inputptr; /* another pointer to the input signal y(k) */ int dummy; /* used for cleaning the keyboard */ int *inp1ptr; /* pointer to the input signal y(k-1) */ int *inp2ptr; /* pointer to the input signal y(k-2) */ int *inp_startbuf; /* pointer to the start of the input circular array */ int *inp_stopbuf; /* pointer to the end of the input circular array */ double *controlptr; /* pointer to the control signal u(k) (double) */ double *cont1ptr; /* pointer to the control signal u(k-1-M) */ double *cont2ptr; /* pointer to the control signal u(k-2-M) */ double constant; /* stores the precalculated constant values */ double *ctr_startbuf; /* pointer to start of the control circular array */ double *ctr_stopbuf; /* pointer to end of the control circular array */ /* DEFINE THE ERROR WINDOW VARIABLES */ WINDOW *errwin; SHADOW s1; BORDER b1; COLORS errcols; int errwth, errhght, errxpos, errypos; /* REPORT ERROR IF sample IS LOWER OR EQUAL TO ZERO AND ABORT EXECUTION */ if (sample <= 0.) { errwth = 30; errhght = 6; errxpos = 50; errypos = 18; create_window(&errwin, errwth, errhght); shadow_window(errwin, &s1, BOTTOM_RIGHT, black, SPACE); border_window(errwin, &b1, DOUBLE, SINGLE, white, brown); clear_window(errwin, &errcols, white, brown, SPACE); w_prints(errwin, CENTER, 1, lwhite + blink, brown, "INPUT ERROR!"); w_prints(errwin, CENTER, 2, lwhite, brown, "Sampling should be > 0."); w_prints(errwin, CENTER, 3, lwhite, brown, "Press any key to abort."); display_window(errwin, errxpos, errypos, 1); _bios_keybrd(_KEYBRD_READ); hide_window(errwin); free_window_memory(errwin); return(-1); } /* IF THE setup_timer RETURNED A HARDWARE ERROR, INFORM THE USER AND ABORT */ if (setup_timer(sample) != 0) { errwth = 30; errhght = 6; errxpos = 50; errypos = 18; create_window(&errwin, errwth, errhght); shadow_window(errwin, &s1, BOTTOM_RIGHT, black, SPACE); border_window(errwin, &b1, DOUBLE, SINGLE, white, brown); clear_window(errwin, &errcols, white, brown, SPACE); w_prints(errwin, CENTER, 1, lwhite + blink, brown, "HARDWARE ERROR!"); w_prints(errwin, CENTER, 2, lwhite, brown, "A/D Board not responding."); w_prints(errwin, CENTER, 3, lwhite, brown, "Press any key to abort."); display_window(errwin, errxpos, errypos, 1); _bios_keybrd(_KEYBRD_READ); hide_window(errwin); free_window_memory(errwin); return(-1); } /* INITIALIZE THE A/D AND D/A VARIABLES AND CALCULATE THE CONSTANTS */ ctrl_ptr = (int *)control; inpt_ptr = (int *)input; reference = (int) (204.8 * refer); /* convert to int mvolts -2048 2048 */ constant = reference * (ergain0 + ergain1 + ergain2); stopped = FALSE; /* GRAB A BUFFER OF SIZE "DELAY+3" FOR USE AS STORAGE FOR THE CONTROL VALUES */ ctr_startbuf = (double *) calloc (delay + 3, sizeof (double)); ctr_stopbuf = ctr_startbuf + delay + 2; /* last useful control buffer */ controlptr = ctr_stopbuf; cont1ptr = ctr_startbuf + 1; cont2ptr = ctr_startbuf; /* GRAB A BUFFER OF SIZE "3" FOR USE AS STORAGE FOR THE INPUT VALUES */ inp_startbuf = (int *) calloc (3, sizeof (int)); inp_stopbuf = inp_startbuf + 2; /* last useful input buffer */ inputptr = inp_stopbuf; inp1ptr = inp_startbuf + 1; inp2ptr = inp_startbuf; /* INITIALIZE THE A/D CHANNELS OF THE RTI-815 BOARD */ outp(CLEAR_R, 0x00); /* clear the status register of the board */ outp(MULTPLX_R, 0x00); /* scan disable, chan 0 selected => 00000000 */ /* OUTPUT THE FIRST CONTROL SIGNAL */ *ctrl_ptr = reference; outp(DA0_LOW_R, control[low]); outp(DA0_HIG_R, control[hig]); outp(STRT_AD_R, 0x00); /* start the very first conversion of chan 0 */ /* RUN THE CONTROL LAW UNTIL SOMEONE HITS CTRL-BREAK */ while (!stopped) { outp(TIME_CMD_R, 0x21); /* start the count-down */ /* UPDATE THE CONTROL AND INPUT POINTERS AND GET A NEW INPUT SAMPLE */ /* The notion behind the code below is that the values for the input and the control signals are saved in 2 circular arrays of sizes 3 and delay + 2 respectively. Using the pointers we navigate trough the circular arrays and pick/update the proper variables each time. */ controlptr = (controlptr == ctr_stopbuf) ? ctr_startbuf : controlptr+1; cont1ptr = (cont1ptr == ctr_stopbuf) ? ctr_startbuf : cont1ptr+1; cont2ptr = (cont2ptr == ctr_stopbuf) ? ctr_startbuf : cont2ptr+1; inputptr = (inputptr == inp_stopbuf) ? inp_startbuf : inputptr+1; inp1ptr = (inp1ptr == inp_stopbuf) ? inp_startbuf : inp1ptr+1; inp2ptr = (inp2ptr == inp_stopbuf) ? inp_startbuf : inp2ptr+1; input[low] = inp(AD_LOW_R); input[hig] = inp(AD_HIG_R); *inputptr = *inpt_ptr; /* WRITE THE DELAYED CONTROLLER VALUE TO THE D/A CONVERTER */ *ctrl_ptr = (int) *controlptr; outp(DA0_LOW_R, control[low]); outp(DA0_HIG_R, control[hig]); /* CALCULATE THE NEW CONTROLLER OUTPUT USING THE PREVIOUSLY STORED VALUES */ /* the formula below represents exactly the equation (21) of page 14 */ *controlptr = constant - ergain0 * (*inputptr) - ergain1 * (*inp1ptr) - ergain2 * (*inp2ptr) + ctgain1 * (*cont1ptr) + ctgain2 * (*cont2ptr); /* USE A CLIPPER TO CUT DOWN VALUES BEYOND THE +-10 Volt LIMIT OF THE D/A */ if (*controlptr > maxsample) *controlptr = maxsample; if (*controlptr < minsample) *controlptr = minsample; /* WAIT UNTIL THE SAMPLING PERIOD HAS ELAPSED */ old_status = inp(TIME_CMD_R); do new_status = inp(TIME_CMD_R); while (((new_status ^ old_status) & TIMEOUT) != TIMEOUT); outp(STRT_AD_R, 0x00); /* start conversion of the channel 0 */ } /* SOMEONE INTERRUPTED THE RUN : RESET THE D/A, AND FREE THE BUFFERS */ outp(DA0_LOW_R, 0x00); outp(DA0_HIG_R, 0x00); free (ctr_startbuf); free (inp_startbuf); clear_kbd(); /* clear the keyboard buffer (for decent BIOS) */ /* SOME BIOS ARE NOT AS COMPATIBLE, SO TRY SOMETHING MORE DRASTIC */ while (_bios_keybrd(_KEYBRD_READY)) dummy = _bios_keybrd(_KEYBRD_READ); return(0); } /**************************************************************************/ /* */ /* FUNCTION: run_state */ /* */ /* DESCRIPTION: The routine implements the State Feedback controller. It*/ /* reads the two process states using the A/D channels 0 & 1,*/ /* computes the control signal, and writes the result to the */ /* D/A channel 0. */ /* */ /* ARGUMENTS: sample: The sampling period in msec. */ /* gain[2]: The precomputed feedback gain vector. */ /* refer: The reference signal in volts (range ñ 10v) */ /* forward: The feedforward gain, necessary for zero steady */ /* state output to a step input. */ /* */ /* COMMENTS:þ For details about the algorithm, see the relevant handout */ /* and the references, given there. */ /* þ The software clipper prohibits calculated control values */ /* to exceed the ñ10 v. region. */ /* þ The actual algorithm is very similar to the Optimal Control*/ /* experiment, as expected from the formulation. */ /* */ /* CREATION: ver. 2.0 Efstratios D. Varkaris 03/25/90 */ /* */ /* REVISION: */ /* */ /**************************************************************************/ int run_state(double sample, double gain[], double refer, double forward) { /* The following 2 bytes hold the old and current values of the status register of counter 0. When a 0 count is reached a bit is flipped. By comparing the two continuously, we detect the end of the sampling period. */ register char new_status, old_status; BYTE state1[2], state2[2], control[2]; /* arrays of bytes used for storage of the states read from the A/D channels and the control to be written to the D/A channel */ int *st1_ptr, *st2_ptr, *ctrl_ptr; /* pointers to the states and the control byte arrays above */ int dummy; /* used to clean the keyboard */ double base; /* precalculate the constant related to the reference */ /* DEFINE THE ERROR WINDOW VARIABLES */ WINDOW *errwin; SHADOW s1; BORDER b1; COLORS errcols; int errwth, errhght, errxpos, errypos; /* REPORT ERROR IF sample IS LOWER OR EQUAL TO ZERO AND ABORT EXECUTION */ if (sample <= 0.) { errwth = 30; errhght = 6; errxpos = 50; errypos = 18; create_window(&errwin, errwth, errhght); shadow_window(errwin, &s1, BOTTOM_RIGHT, black, SPACE); border_window(errwin, &b1, DOUBLE, SINGLE, white, brown); clear_window(errwin, &errcols, white, brown, SPACE); w_prints(errwin, CENTER, 1, lwhite + blink, brown, "INPUT ERROR!"); w_prints(errwin, CENTER, 2, lwhite, brown, "Sampling should be > 0."); w_prints(errwin, CENTER, 3, lwhite, brown, "Press any key to abort."); display_window(errwin, errxpos, errypos, 1); _bios_keybrd(_KEYBRD_READ); hide_window(errwin); free_window_memory(errwin); return(-1); } /* IF THE setup_timer RETURNED A HARDWARE ERROR, INFORM THE USER AND ABORT */ if (setup_timer(sample) != 0) { errwth = 30; errhght = 6; errxpos = 50; errypos = 18; create_window(&errwin, errwth, errhght); shadow_window(errwin, &s1, BOTTOM_RIGHT, black, SPACE); border_window(errwin, &b1, DOUBLE, SINGLE, white, brown); clear_window(errwin, &errcols, white, brown, SPACE); w_prints(errwin, CENTER, 1, lwhite + blink, brown, "HARDWARE ERROR!"); w_prints(errwin, CENTER, 2, lwhite, brown, "A/D Board not responding."); w_prints(errwin, CENTER, 3, lwhite, brown, "Press any key to abort."); display_window(errwin, errxpos, errypos, 1); _bios_keybrd(_KEYBRD_READ); hide_window(errwin); free_window_memory(errwin); return(-1); } base = 204.8 * refer * forward; /* convert to int in -2048 to +2048 mv */ /* INITIALIZE THE REGISTERS THAT WILL HOLD THE A/D AND D/A VALUES */ st1_ptr = (int *)state1; st2_ptr = (int *)state2; ctrl_ptr = (int *)control; stopped = FALSE; *st1_ptr = 0; *st2_ptr = 0; /* strart the very first A/D conversion */ outp(MULTPLX_R, 0x00); /* scan disable, chan 0 selected => 00000000 */ outp(STRT_AD_R, 0x00); /* start conversion of the channel 0 */ /* RUN THE CONTROL LAW UNTIL SOMEONE HITS CTRL-BREAK */ while (!stopped) { outp(TIME_CMD_R, 0x21); /* start the count-down */ /* the formula below implements the equation (31) in page 18 of the experiment handout */ *ctrl_ptr = (int) (base - gain[0] * (*st1_ptr) - gain[1] * (*st2_ptr)); /* READ THE A/D CHANNEL 0 AND SELECT THE A/D CHANNEL 1 */ state1[low] = inp(AD_LOW_R); state1[hig] = inp(AD_HIG_R); outp(MULTPLX_R, 0x01); /* scan disable, chan 1 selected => 00000000 */ outp(STRT_AD_R, 0x00); /* start conversion of the channel 1 */ /* USE A CLIPPER TO PREVENT THE SIGNAL FROM OVERFLOWING */ if (*ctrl_ptr > maxsample) *ctrl_ptr = maxsample; if (*ctrl_ptr < minsample) *ctrl_ptr = minsample; /* OUTPUT THE CONTROL SIGNAL AND READ THE A/D CHANNEL 1 */ outp(DA0_LOW_R, control[low]); outp(DA0_HIG_R, control[hig]); state2[low] = inp(AD_LOW_R); state2[hig] = inp(AD_HIG_R); outp(MULTPLX_R, 0x00); /* scan disable, chan 0 selected => 00000000 */ /* WAIT UNTIL THE SAMPLING PERIOD HAS ELAPSED */ old_status = inp(TIME_CMD_R); do new_status = inp(TIME_CMD_R); while (((new_status ^ old_status) & TIMEOUT) != TIMEOUT); outp(STRT_AD_R, 0x00); /* start conversion of the channel 0 */ } /* SOMEONE INTERRUPTED RUN : RESET D/A, AND FLUSH THE KEYBOARD */ outp(DA0_LOW_R, 0x00); outp(DA0_HIG_R, 0x00); clear_kbd(); /* clear the keyboard buffer (for decent BIOS) */ /* SOME BIOS ARE NOT AS COMPATIBLE, SO TRY SOMETHING MORE DRASTIC */ while (_bios_keybrd(_KEYBRD_READY)) dummy = _bios_keybrd(_KEYBRD_READ); return(0); } /**************************************************************************/ /* */ /* FUNCTION: step_ctrl */ /* */ /* DESCRIPTION: The routine displays the Step Input menu, with a list */ /* of the variables and their assosiated fields. Full screen */ /* editing of the fields is allowed, and when finished it */ /* calls the "run_step" routine to execute the algorithm. So */ /* long as the algorithm runs, the control window flushes, */ /* prompting the student to press CTRL-BREAL to abort. */ /* */ /* ARGUMENTS: ------- */ /* */ /* COMMENTS: ------- */ /* */ /* CREATION: ver. 2.0 Efstratios D. Varkaris 03/25/90 */ /* */ /* REVISION: */ /* */ /**************************************************************************/ void step_ctrl(void) { /* DEFINE THE WINDOWS/GRAPHICS VARIABLES */ VSCREEN *vs1; FIELD f1, f2; WINDOW *inpwin, *ctrlwin; COLORS wc1, wc2, vsc1, vsc2, ctrlcols; SHADOW s1; BORDER b1; FIELD_ATTRIB *fattrib[2]; int index, action; unsigned fcolumn = 45; int ctrlwth = 21, ctrlhght = 6, ctrlxpos = 58, ctrlypos = 18; /* CREATE THE EXECUTION CONTROL WINDOW */ create_window (&ctrlwin, ctrlwth, ctrlhght); shadow_window(ctrlwin, &s1, BOTTOM_RIGHT, black, SPACE); border_window(ctrlwin, &b1, DOUBLE, SINGLE, white, brown); clear_window(ctrlwin, &ctrlcols, white, brown, SPACE); w_prints(ctrlwin, CENTER, 1, lwhite, brown, "Control Running"); w_prints(ctrlwin, CENTER, 2, lwhite, brown, "To stop press"); w_prints(ctrlwin, CENTER, 3, lwhite + blink, brown, "CTRL-BREAK"); /* CREATE THE DATA INPUT WINDOW */ create_window(&inpwin, 70, 20); shadow_window(inpwin, &s1, BOTTOM_RIGHT, black, 0xDF); border_window(inpwin, &b1, DOUBLE, DOUBLE, black, blue); clear_window(inpwin, &wc2, yellow, blue, SPACE); display_window(inpwin, 5, 2, 1); inpwin->p_format = &form1; /* CREATE THE VIRTUAL SCREEN TO HOLD THE DATA INPUT WINDOW */ creat_virtual_screen(&vs1, 70, 25); vs1->p_format = &form1; clear_virtual_screen(vs1, &vsc1, wc2.fc, wc2.bc, SPACE); w_load_vs(inpwin, vs1, 0, 0); /* WRITE THE LABEL, AND THE VARIABLE NAMES ONTO THE SCREEN BUFFER */ vs_prints(vs1, 18, 1, wc2.fc, wc2.bc, "S t e p C o n t r o l M o d e."); vs_prints(vs1, 27, 2, wc2.fc, wc2.bc, "Data Input Menu"); vsc2.fc = lwhite; vsc2.bc = red; vs_prints(vs1, 3, 5, wc2.fc, wc2.bc, "Sampling Period T (.3 ó T ms ó 100):"); vs_prints(vs1, 3, 7, wc2.fc, wc2.bc, "Reference Input R (-10V ó R ó +10V):"); /* CREATE THE FIELDS FOR THE VARIABLES */ creat_field(&f1, vs1, &vsc2, user_err, neutrals, 1, fcolumn, 5, fspecs); creat_field(&f2, vs1, &vsc2, user_err, neutrals, 2, fcolumn, 7, fspecs); /* ASSIGN VALID KEYS TO THE FIELDS AND STORE THE READ FORMAT IN AN ARRAY */ f1.p_keys = f2.p_keys = &k1; fattrib[0] = store_attr(&f1, vsc2.fc, vsc2.bc, fspecs); fattrib[1] = store_attr(&f2, vsc2.fc, vsc2.bc, fspecs); /* INITIALIZE THE SCREEN AND KEYBOARD AND READ THE FIRST FIELD */ index = 0; clear_kbd(); cursor_on(); action = read_field(&f1, vsc2.fc, vsc2.bc, fspecs); /* CONTINUE READING FIELDS UNTIL "ESC" IS PRESSED */ do { switch (action) { case ESCAPE: /* quit the Data Input Menu */ break; case END: /* start the Control Algorithm execution */ cursor_off(); display_window(ctrlwin, ctrlxpos, ctrlypos, 1); run_step(atof(f1.text), atof(f2.text)); hide_window(ctrlwin); clear_kbd(); cursor_on(); index = 0; action = read_field(&f1, vsc2.fc, vsc2.bc, fspecs); break; case ENTER: /* move to the next field */ case DOWN_ARROW: if (index == 1) { index = 0; } else { index++; } action = read_field(fattrib[index]->fieldptr, fattrib[index]->forcol,fattrib[index]->backcol, fattrib[index]->template); break; case UP_ARROW: /* move to the previous field */ if (index == 0) { index = 1; } else { index--; } action = read_field(fattrib[index]->fieldptr, fattrib[index]->forcol,fattrib[index]->backcol, fattrib[index]->template); break; default: /* stay on the same field and continue editing */ clear_kbd(); cursor_on(); action = read_field(fattrib[index]->fieldptr, fattrib[index]->forcol,fattrib[index]->backcol, fattrib[index]->template); break; } } while(action != ESCAPE); /* FREE ALL ALOCATED MEMORY AND EXIT THE STEP CONTROL MODE */ hide_window(inpwin); free_window_memory(ctrlwin); fre_virtual_screen_memory(vs1); free_window_memory(inpwin); } /**************************************************************************/ /* */ /* FUNCTION: pid_ctrl */ /* */ /* DESCRIPTION: The routine displays the PID Input menu, with a list */ /* of the variables and their assosiated fields. Full screen */ /* editing of the fields is allowed, and when finished it */ /* calls the "run_step" routine to execute the algorithm. So */ /* long as the algorithm runs, the control window flushes, */ /* prompting the student to press CTRL-BREAL to abort. */ /* */ /* ARGUMENTS: ------- */ /* */ /* COMMENTS: ------- */ /* */ /* CREATION: ver. 2.0 Efstratios D. Varkaris 03/25/90 */ /* */ /* REVISION: */ /* */ /**************************************************************************/ void pid_ctrl(void) { /* WINDOWS/GRAPHICS VARIABLES */ VSCREEN *vs1; FIELD f1, f2, f3, f4, f5, f6; WINDOW *inpwin, *ctrlwin; COLORS wc1, wc2, vsc1, vsc2, ctrlcols; SHADOW s1; BORDER b1; FIELD_ATTRIB *fattrib[6]; int index, action; unsigned fcolumn = 45; int ctrlwth = 21, ctrlhght = 6, ctrlxpos = 58, ctrlypos = 18; /* CREATE THE EXECUTION CONTROL WINDOW */ create_window (&ctrlwin, ctrlwth, ctrlhght); shadow_window(ctrlwin, &s1, BOTTOM_RIGHT, black, SPACE); border_window(ctrlwin, &b1, DOUBLE, SINGLE, white, brown); clear_window(ctrlwin, &ctrlcols, white, brown, SPACE); w_prints(ctrlwin, CENTER, 1, lwhite, brown, "Control Running"); w_prints(ctrlwin, CENTER, 2, lwhite, brown, "To stop press"); w_prints(ctrlwin, CENTER, 3, lwhite + blink, brown, "CTRL-BREAK"); /* CREATE THE WINDOW FOR THE FIELD */ create_window(&inpwin, 70, 20); shadow_window(inpwin, &s1, BOTTOM_RIGHT, black, 0xDF); border_window(inpwin, &b1, DOUBLE, DOUBLE, black, blue); clear_window(inpwin, &wc2, yellow, blue, SPACE); display_window(inpwin, 5, 2, 1); inpwin->p_format = &form1; /* CREATE THE VIRTUAL SCREEN TO HOLD THE DATA INPUT WINDOW */ creat_virtual_screen(&vs1, 70, 25); vs1->p_format = &form1; clear_virtual_screen(vs1, &vsc1, wc2.fc, wc2.bc, SPACE); w_load_vs(inpwin, vs1, 0, 0); /* WRITE THE LABEL, AND THE VARIABLE NAMES ONTO THE SCREEN BUFFER */ vs_prints(vs1, 16, 1, wc2.fc, wc2.bc, "P. I. D. C o n t r o l M o d e."); vs_prints(vs1, 27, 2, wc2.fc, wc2.bc, "Data Input Menu"); vsc2.fc = lwhite; vsc2.bc = red; vs_prints(vs1, 3, 5, wc2.fc, wc2.bc, "Sampling Period T (5 ó T ms ó 1000):"); vs_prints(vs1, 3, 7, wc2.fc, wc2.bc, "Reference Input R (-10V ó R ó +10V):"); vs_prints(vs1, 3, 9, wc2.fc, wc2.bc, "Proportional Gain Kp:"); vs_prints(vs1, 3, 11, wc2.fc, wc2.bc, "Integral Gain Ki:"); vs_prints(vs1, 3, 13, wc2.fc, wc2.bc, "Derivate Gain Kd:"); vs_prints(vs1, 3, 15, wc2.fc, wc2.bc, "Scaling Factor W (usually 1.0):"); /* CREATE THE FIELDS FOR THE VARIABLES */ creat_field(&f1, vs1, &vsc2, user_err, neutrals, 1, fcolumn, 5, fspecs); creat_field(&f2, vs1, &vsc2, user_err, neutrals, 2, fcolumn, 7, fspecs); creat_field(&f3, vs1, &vsc2, user_err, neutrals, 3, fcolumn, 9, fspecs); creat_field(&f4, vs1, &vsc2, user_err, neutrals, 4, fcolumn, 11, fspecs); creat_field(&f5, vs1, &vsc2, user_err, neutrals, 5, fcolumn, 13, fspecs); creat_field(&f6, vs1, &vsc2, user_err, neutrals, 6, fcolumn, 15, fspecs); /* ASSIGN VALID KEYS TO THE FIELDS AND STORE THE READ FORMAT IN AN ARRAY */ f1.p_keys = f2.p_keys = f3.p_keys = f4.p_keys =f5.p_keys = f6.p_keys = &k1; fattrib[0] = store_attr(&f1, vsc2.fc, vsc2.bc, fspecs); fattrib[1] = store_attr(&f2, vsc2.fc, vsc2.bc, fspecs); fattrib[2] = store_attr(&f3, vsc2.fc, vsc2.bc, fspecs); fattrib[3] = store_attr(&f4, vsc2.fc, vsc2.bc, fspecs); fattrib[4] = store_attr(&f5, vsc2.fc, vsc2.bc, fspecs); fattrib[5] = store_attr(&f6, vsc2.fc, vsc2.bc, fspecs); index = 0; clear_kbd(); cursor_on(); action = read_field(&f1, vsc2.fc, vsc2.bc, fspecs); do { switch (action) { case ESCAPE: /* quit the Data Input Menu */ break; case END: /* start the Control Algorithm execution */ cursor_off(); display_window(ctrlwin, ctrlxpos, ctrlypos, 1); run_pid(atof(f1.text), atof(f2.text), atof(f3.text) * atof(f6.text), atof(f4.text) * atof(f6.text), atof(f5.text) * atof(f6.text)); hide_window(ctrlwin); clear_kbd(); cursor_on(); index = 0; action = read_field(&f1, vsc2.fc, vsc2.bc, fspecs); break; case ENTER: /* move to the next field */ case DOWN_ARROW: if (index == 5) { index = 0; } else { index++; } action = read_field(fattrib[index]->fieldptr, fattrib[index]->forcol,fattrib[index]->backcol, fattrib[index]->template); break; case UP_ARROW: /* move to the previous field */ if (index == 0) { index = 5; } else { index--; } action = read_field(fattrib[index]->fieldptr, fattrib[index]->forcol,fattrib[index]->backcol, fattrib[index]->template); break; default: /* stay on the same field and continue editing */ clear_kbd(); cursor_on(); action = read_field(fattrib[index]->fieldptr, fattrib[index]->forcol,fattrib[index]->backcol, fattrib[index]->template); break; } } while(action != ESCAPE); /* FREE ALL ALOCATED MEMORY AND EXIT THE STEP CONTROL MODE */ hide_window(inpwin); free_window_memory(ctrlwin); fre_virtual_screen_memory(vs1); free_window_memory(inpwin); } /**************************************************************************/ /* */ /* FUNCTION: fst_ctrl */ /* */ /* DESCRIPTION: The routine displays the FST Input menu, with a list */ /* of the variables and their assosiated fields. Full screen */ /* editing of the fields is allowed, and when finished it */ /* calls the "run_step" routine to execute the algorithm. So */ /* long as the algorithm runs, the control window flushes, */ /* prompting the student to press CTRL-BREAL to abort. */ /* */ /* ARGUMENTS: ------- */ /* */ /* COMMENTS: ------- */ /* */ /* CREATION: ver. 2.0 Efstratios D. Varkaris 03/25/90 */ /* */ /* REVISION: */ /* */ /**************************************************************************/ void fst_ctrl(void) { /* DEFINE THE WINDOWS/GRAPHICS VARIABLES */ VSCREEN *vs1; FIELD f1, f2, f3, f4, f5, f6, f7, f8, f9; WINDOW *inpwin, *ctrlwin; COLORS wc1, wc2, vsc1, vsc2, ctrlcols; SHADOW s1; BORDER b1; FIELD_ATTRIB *fattrib[9]; int index, action; unsigned fcolumn = 45; int ctrlwth = 21, ctrlhght = 6, ctrlxpos = 58, ctrlypos = 18; /* CREATE THE EXECUTION CONTROL WINDOW */ create_window (&ctrlwin, ctrlwth, ctrlhght); shadow_window(ctrlwin, &s1, BOTTOM_RIGHT, black, SPACE); border_window(ctrlwin, &b1, DOUBLE, SINGLE, white, brown); clear_window(ctrlwin, &ctrlcols, white, brown, SPACE); w_prints(ctrlwin, CENTER, 1, lwhite, brown, "Control Running"); w_prints(ctrlwin, CENTER, 2, lwhite, brown, "To stop press"); w_prints(ctrlwin, CENTER, 3, lwhite + blink, brown, "CTRL-BREAK"); /* CREATE THE DATA INPUT WINDOW */ create_window(&inpwin, 70, 22); shadow_window(inpwin, &s1, BOTTOM_RIGHT, black, 0xDF); border_window(inpwin, &b1, DOUBLE, DOUBLE, black, blue); clear_window(inpwin, &wc2, yellow, blue, SPACE); display_window(inpwin, 5, 1, 1); inpwin->p_format = &form1; /* CREATE THE VIRTUAL SCREEN TO HOLD THE DATA INPUT WINDOW */ creat_virtual_screen(&vs1, 70, 25); vs1->p_format = &form1; clear_virtual_screen(vs1, &vsc1, wc2.fc, wc2.bc, SPACE); w_load_vs(inpwin, vs1, 0, 0); /* WRITE THE LABEL, AND THE VARIABLE NAMES ONTO THE SCREEN BUFFER */ vs_prints(vs1, 17, 1, wc2.fc, wc2.bc, "F. S. T. C o n t r o l M o d e."); vs_prints(vs1, 27, 2, wc2.fc, wc2.bc, "Data Input Menu"); vsc2.fc = lwhite; vsc2.bc = red; vs_prints(vs1, 3, 4, wc2.fc, wc2.bc, "Sampling Period T (1 ó T ms ó 10000):"); vs_prints(vs1, 3, 6, wc2.fc, wc2.bc, "Reference Input R (-10V ó R ó +10V):"); vs_prints(vs1, 3, 8, wc2.fc, wc2.bc, "Response Delay M (0 ó M ó 255):"); vs_prints(vs1, 3, 10, wc2.fc, wc2.bc, "0th Instant Error Gain Ke0:"); vs_prints(vs1, 3, 11, wc2.fc, wc2.bc, "1st Instant Error Gain Ke1:"); vs_prints(vs1, 3, 12, wc2.fc, wc2.bc, "2st Instant Error Gain Ke2:"); vs_prints(vs1, 3, 14, wc2.fc, wc2.bc, "1st Instant Control Gain Ku1:"); vs_prints(vs1, 3, 15, wc2.fc, wc2.bc, "2st Instant Control Gain Ku2:"); vs_prints(vs1, 3, 17, wc2.fc, wc2.bc, "Scaling Factor W (usually 1.0):"); /* CREATE THE FIELDS FOR THE VARIABLES */ creat_field(&f1, vs1, &vsc2, user_err, neutrals, 1, fcolumn, 4, fspecs); creat_field(&f2, vs1, &vsc2, user_err, neutrals, 2, fcolumn, 6, fspecs); creat_field(&f3, vs1, &vsc2, user_err, neutrals, 3, fcolumn, 8, fspecs); creat_field(&f4, vs1, &vsc2, user_err, neutrals, 4, fcolumn, 10, fspecs); creat_field(&f5, vs1, &vsc2, user_err, neutrals, 5, fcolumn, 11, fspecs); creat_field(&f6, vs1, &vsc2, user_err, neutrals, 6, fcolumn, 12, fspecs); creat_field(&f7, vs1, &vsc2, user_err, neutrals, 7, fcolumn, 14, fspecs); creat_field(&f8, vs1, &vsc2, user_err, neutrals, 8, fcolumn, 15, fspecs); creat_field(&f9, vs1, &vsc2, user_err, neutrals, 9, fcolumn, 17, fspecs); /* ASSIGN VALID KEYS TO THE FIELDS AND STORE THE READ FORMAT IN AN ARRAY */ f1.p_keys = f2.p_keys = f3.p_keys = f4.p_keys = &k1; f5.p_keys = f6.p_keys = f7.p_keys = f8.p_keys =f9.p_keys = &k1; fattrib[0] = store_attr(&f1, vsc2.fc, vsc2.bc, fspecs); fattrib[1] = store_attr(&f2, vsc2.fc, vsc2.bc, fspecs); fattrib[2] = store_attr(&f3, vsc2.fc, vsc2.bc, fspecs); fattrib[3] = store_attr(&f4, vsc2.fc, vsc2.bc, fspecs); fattrib[4] = store_attr(&f5, vsc2.fc, vsc2.bc, fspecs); fattrib[5] = store_attr(&f6, vsc2.fc, vsc2.bc, fspecs); fattrib[6] = store_attr(&f7, vsc2.fc, vsc2.bc, fspecs); fattrib[7] = store_attr(&f8, vsc2.fc, vsc2.bc, fspecs); fattrib[8] = store_attr(&f9, vsc2.fc, vsc2.bc, fspecs); /* INITIALIZE THE SCREEN AND KEYBOARD AND READ THE FIRST FIELD */ index = 0; clear_kbd(); cursor_on(); action = read_field(&f1, vsc2.fc, vsc2.bc, fspecs); /* CONTINUE READING FIELDS UNTIL "ESC" IS PRESSED */ do { switch (action) { case ESCAPE: /* quit the Data Input Menu */ break; case END: /* start the Control Algorithm execution */ cursor_off(); display_window(ctrlwin, ctrlxpos, ctrlypos, 1); run_fst(atof(f1.text), atof(f2.text), atoi(f3.text), atof(f4.text) * atof(f9.text), atof(f5.text) * atof(f9.text), atof(f6.text) * atof(f9.text), atof(f7.text) * atof(f9.text), atof(f8.text) * atof(f9.text)); hide_window(ctrlwin); clear_kbd(); cursor_on(); index = 0; action = read_field(&f1, vsc2.fc, vsc2.bc, fspecs); break; case ENTER: /* move to the next field */ case DOWN_ARROW: if (index == 8) { index = 0; } else { index++; } action = read_field(fattrib[index]->fieldptr, fattrib[index]->forcol,fattrib[index]->backcol, fattrib[index]->template); break; case UP_ARROW: /* move to the previous field */ if (index == 0) { index = 8; } else { index--; } action = read_field(fattrib[index]->fieldptr, fattrib[index]->forcol,fattrib[index]->backcol, fattrib[index]->template); break; default: /* stay on the same field and continue editing */ clear_kbd(); cursor_on(); action = read_field(fattrib[index]->fieldptr, fattrib[index]->forcol,fattrib[index]->backcol, fattrib[index]->template); break; } } while(action != ESCAPE); /* FREE ALL ALOCATED MEMORY AND EXIT THE STEP CONTROL MODE */ hide_window(inpwin); free_window_memory(ctrlwin); fre_virtual_screen_memory(vs1); free_window_memory(inpwin); } /**************************************************************************/ /* */ /* FUNCTION: state_ctrl */ /* */ /* DESCRIPTION: The routine displays the State Input menu, with a list */ /* of the variables and their assosiated fields. Full screen */ /* editing of the fields is allowed, and when finished it */ /* calls the "run_step" routine to execute the algorithm. So */ /* long as the algorithm runs, the control window flushes, */ /* prompting the student to press CTRL-BREAL to abort. */ /* */ /* ARGUMENTS: ------- */ /* */ /* COMMENTS: ------- */ /* */ /* CREATION: ver. 2.0 Efstratios D. Varkaris 03/25/90 */ /* */ /* REVISION: */ /* */ /**************************************************************************/ void state_ctrl(void) { /* DEFINE THE WINDOWS/GRAPHICS VARIABLES */ VSCREEN *vs1; FIELD f1, f2, f3, f4, f5, f6; WINDOW *inpwin, *ctrlwin; COLORS wc1, wc2, vsc1, vsc2, ctrlcols; SHADOW s1; BORDER b1; FIELD_ATTRIB *fattrib[6]; int index, action; unsigned fcolumn = 45; int ctrlwth = 21, ctrlhght = 6, ctrlxpos = 58, ctrlypos = 18; double gain[2]; /* CREATE THE EXECUTION CONTROL WINDOW */ create_window (&ctrlwin, ctrlwth, ctrlhght); shadow_window(ctrlwin, &s1, BOTTOM_RIGHT, black, SPACE); border_window(ctrlwin, &b1, DOUBLE, SINGLE, white, brown); clear_window(ctrlwin, &ctrlcols, white, brown, SPACE); w_prints(ctrlwin, CENTER, 1, lwhite, brown, "Control Running"); w_prints(ctrlwin, CENTER, 2, lwhite, brown, "To stop press"); w_prints(ctrlwin, CENTER, 3, lwhite + blink, brown, "CTRL-BREAK"); /* CREATE THE DATA INPUT MENU */ create_window(&inpwin, 70, 20); shadow_window(inpwin, &s1, BOTTOM_RIGHT, black, 0xDF); border_window(inpwin, &b1, DOUBLE, DOUBLE, black, blue); clear_window(inpwin, &wc2, yellow, blue, SPACE); display_window(inpwin, 5, 2, 1); inpwin->p_format = &form1; /* CREATE THE VIRTUAL SCREEN TO HOLD THE DATA INPUT WINDOW */ creat_virtual_screen(&vs1, 70, 25); vs1->p_format = &form1; clear_virtual_screen(vs1, &vsc1, wc2.fc, wc2.bc, SPACE); w_load_vs(inpwin, vs1, 0, 0); /* WRITE THE LABEL, AND THE VARIABLE NAMES ONTO THE SCREEN BUFFER */ vs_prints(vs1, 14, 1, wc2.fc, wc2.bc, "P o l e P l a c e m e n t M e t h o d"); vs_prints(vs1, 27, 2, wc2.fc, wc2.bc, "Data Input Menu"); vsc2.fc = lwhite; vsc2.bc = red; vs_prints(vs1, 3, 5, wc2.fc, wc2.bc, "Sampling Period T (5 ó T ms ó 1000):"); vs_prints(vs1, 3, 7, wc2.fc, wc2.bc, "Reference Input R (-10V ó R ó +10V):"); vs_prints(vs1, 3, 9, wc2.fc, wc2.bc, "Feedforward Gain G:"); vs_prints(vs1, 3, 11, wc2.fc, wc2.bc, "State Feedback gain K1:"); vs_prints(vs1, 3, 13, wc2.fc, wc2.bc, "State Feedback gain K2:"); vs_prints(vs1, 3, 15, wc2.fc, wc2.bc, "Scaling Factor W (usually 1.0):"); /* CREATE THE FIELDS FOR THE VARIABLES */ creat_field(&f1, vs1, &vsc2, user_err, neutrals, 1, fcolumn, 5, fspecs); creat_field(&f2, vs1, &vsc2, user_err, neutrals, 2, fcolumn, 7, fspecs); creat_field(&f3, vs1, &vsc2, user_err, neutrals, 3, fcolumn, 9, fspecs); creat_field(&f4, vs1, &vsc2, user_err, neutrals, 4, fcolumn, 11, fspecs); creat_field(&f5, vs1, &vsc2, user_err, neutrals, 5, fcolumn, 13, fspecs); creat_field(&f6, vs1, &vsc2, user_err, neutrals, 6, fcolumn, 15, fspecs); /* ASSIGN VALID KEYS TO THE FIELDS AND STORE THE READ FORMAT IN AN ARRAY */ f1.p_keys = f2.p_keys = f3.p_keys = f4.p_keys =f5.p_keys = f6.p_keys = &k1; fattrib[0] = store_attr(&f1, vsc2.fc, vsc2.bc, fspecs); fattrib[1] = store_attr(&f2, vsc2.fc, vsc2.bc, fspecs); fattrib[2] = store_attr(&f3, vsc2.fc, vsc2.bc, fspecs); fattrib[3] = store_attr(&f4, vsc2.fc, vsc2.bc, fspecs); fattrib[4] = store_attr(&f5, vsc2.fc, vsc2.bc, fspecs); fattrib[5] = store_attr(&f6, vsc2.fc, vsc2.bc, fspecs); /* INITIALIZE THE SCREEN AND KEYBOARD AND READ THE FIRST FIELD */ index = 0; clear_kbd(); cursor_on(); action = read_field(&f1, vsc2.fc, vsc2.bc, fspecs); /* CONTINUE READING FIELDS UNTIL "ESC" OR "END" IS PRESSED */ do { switch (action) { case ESCAPE: /* quit the Data Input Menu */ break; case END: /* start the Control Algorithm execution */ cursor_off(); display_window(ctrlwin, ctrlxpos, ctrlypos, 1); gain[0] = atof(f4.text) * atof(f6.text); gain[1] = atof(f5.text) * atof(f6.text); run_state(atof(f1.text), gain, atof(f2.text) * atof(f6.text), atof(f3.text) * atof(f6.text)); hide_window(ctrlwin); clear_kbd(); cursor_on(); index = 0; action = read_field(&f1, vsc2.fc, vsc2.bc, fspecs); break; case ENTER: /* move to the next field */ case DOWN_ARROW: if (index == 5) { index = 0; } else { index++; } action = read_field(fattrib[index]->fieldptr, fattrib[index]->forcol,fattrib[index]->backcol, fattrib[index]->template); break; case UP_ARROW: /* move to the previous field */ if (index == 0) { index = 5; } else { index--; } action = read_field(fattrib[index]->fieldptr, fattrib[index]->forcol,fattrib[index]->backcol, fattrib[index]->template); break; default: /* stay on the same field and continue editing */ clear_kbd(); cursor_on(); action = read_field(fattrib[index]->fieldptr, fattrib[index]->forcol,fattrib[index]->backcol, fattrib[index]->template); break; } } while(action != ESCAPE); /* FREE ALL ALOCATED MEMORY AND EXIT THE STEP CONTROL MODE */ hide_window(inpwin); free_window_memory(ctrlwin); fre_virtual_screen_memory(vs1); free_window_memory(inpwin); } /**************************************************************************/ /**************************************************************************/ /* */ /* FUNCTION: main */ /* */ /* DESCRIPTION: This is the main program routine. After an introductory */ /* screen a pop-up menu with the available control algorithms*/ /* is displayed on screen. The student selects the algorithm */ /* to be executed with the cursors or the highlighted keys. */ /* */ /* ARGUMENTS: ------- */ /* */ /* COMMENTS:þ The CWINDOWS toolkit used here for the screen design acts */ /* strangely with some low cost EGAs, hiding the cursor when in */ /* the input menus. The easy way around is to press the INSERT */ /* key when in any of the input menus. */ /* */ /* CREATION: ver. 2.0 Efstratios D. Varkaris 03/25/90 */ /* */ /* REVISION: */ /* */ /**************************************************************************/ COLORS pc1 = { LIGHTCYAN, /* foreground_color */ BLUE, /* background_color */ WHITE, /* highlight character color */ BLUE, /* menu bar foreground color */ LIGHTGREY, /* menu bar background color */ YELLOW, /* invalid choice_foreground_color */ LIGHTGREY /* invalid_choice_background_color */ }; BORDER pb1 = { YELLOW, /* border foreground color */ BLUE, /* border background color */ SINGLE, /* border horizontal character */ DOUBLE /* border vertical character */ }; SHADOW ps1 = { BOTTOM_RIGHT, /* if a shadow, values 1-4 tell type. */ BLACK, /* shadow color */ 'Û' /* menu character */ }; MENU_KEYS pmk = { HOME, /* key that moves highlight to top */ END, /* key that moves highlight to bottom */ UP_ARROW, /* key that moves highlight up one item */ DOWN_ARROW, /* key that moves highlight down */ ENTER, /* key that selects the highlighted item*/ ESCAPE, /* key that makes menu disappear */ PAGE_UP, /* key to page up */ PAGE_DOWN /* key to page down */ }; MENU_TEXT pop1 = {" Apply a Step Input ", " Run the PID controller ", " Run the FST controller ", " Run the Pole Placement ", ""}; /* HIGHLIGHTED CHARACTERS */ int hchars[] = {10, 11, 10, 10, EMPTY}; POPUP popstruct1 = { NULL, /* colors */ NULL, /* border */ NULL, /* shadow */ NULL, /* menu keys */ /* TITLE FEATURES */ YELLOW, /* title foreground color */ LIGHTGREY, /* title background color */ CENTER, /* title column position on first line */ " Main Menu ", /* ptr to the title string */ /* GENERAL FEATURES */ BLANK, /* background character */ NULL, /* pointer to the menu text */ RUN_TIME, /* # of maximum item (from 0) */ RUN_TIME, /* # of items displayed */ NULL /* ptr. to array of menu highlight #s */ }; /******************* MAIN PROGRAM LOOP **********************/ void main(void) { /* DEFINE THE WINDOWS/GRAPHICS VARIABLES */ WINDOW *titlwin, *menwin; COLORS wc1, wc2; BORDER b1; int option; /* ASSIGN THE ABOVE CONSTANTS TO THE POP-UP MENU FIELDS */ popstruct1.p_colors = &pc1; popstruct1.p_border = &pb1; popstruct1.p_shadow = NULL; popstruct1.p_keys = &pmk; *(popstruct1.text) = pop1; *(popstruct1.hchar) = hchars; /* INITIALIZE THE VIDEO ATTRIBUTES AND THE PAUSE FUNCTION */ pause = bios_delay; init_video(); /* initialize video */ cursor_off(); /* make the cursor disappear */ /* SUBSTITUTE THE CTRL-BREAK HANDLER WITH MY OWN ONE */ orig_break = _dos_getvect(CTRL_BREAK_INT); _dos_setvect(CTRL_BREAK_INT, break_handler); /* CLEAR THE SCREEN AND PRESERVE THE CURRENT MODE */ cls(white, black); clear_box(0, 0, 79, 24, cyan, blue, 0xB2); preserve_screen(screen.cols, screen.rows); /* CREATE THE INTRODUCTORY SCREEN WINDOW */ create_window(&titlwin, 64, 20); shadow_window(titlwin, &ps1, BOTTOM_RIGHT, BLACK, SPACE); display_window(titlwin, 8, 3, 1); clear_window(titlwin, &wc1, lwhite, blue, SPACE); /* WRITE THE WELCOME MESSAGE TO THE WINDOW BUFFER */ w_prints(titlwin, CENTER, 1, wc1.fc, wc1.bc, "COMPUTER APPLICATIONS LAB."); w_prints(titlwin, CENTER, 2, wc1.fc, wc1.bc, "ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ"); w_prints(titlwin, CENTER, 3, wc1.fc, wc1.bc, "Hybrid Control Experiment"); w_prints(titlwin, CENTER, 4, wc1.fc, wc1.bc, "Version 2.0"); w_prints(titlwin, CENTER, 6, wc1.fc, wc1.bc, "In this experiment a 2nd order hybrid plant with possible"); w_prints(titlwin, CENTER, 7, wc1.fc, wc1.bc, "time delay is controlled using the following controllers:"); w_prints(titlwin, 3, 10, wc1.fc, wc1.bc, "ÄÄÄÄÄÄÄÄÄÄÄÄÄÄ"); w_prints(titlwin, 3, 9, wc1.fc, wc1.bc, "PID Controller: Gc(s)= Kp + Ki*1/s + Kd*s"); w_prints(titlwin, 3, 12, wc1.fc, wc1.bc, "ÄÄÄÄÄÄÄÄÄÄÄÄÄÄ"); w_prints(titlwin, 3, 11, wc1.fc, wc1.bc, "FST Controller: U(k) = Ke0*E(k) + Ke1*E(k-1) + Ke2*E(k-2)"); w_prints(titlwin, 24, 12, wc1.fc, wc1.bc, "+ Ku1*U(k-m-1) + Ku2*U(k-m-2)"); w_prints(titlwin, 3, 15, wc1.fc, wc1.bc, "ÄÄÄÄÄÄÄÄÄÄÄÄÄÄ"); w_prints(titlwin, 3, 14, wc1.fc, wc1.bc, "Pole Placement: U(t) = G*R - (K1*X1 + K2*X2)"); w_prints(titlwin, CENTER, 16, wc1.fc, wc1.bc, "Different design techniques as well as effects of sampling"); w_prints(titlwin, CENTER, 17, wc1.fc, wc1.bc, "and time delay are investigated, and the results compared."); /* WAIT 4 sec AND THEN HIDE AND FREE THE INTRODUCTORY WINDOW */ (*pause)(4); clear_kbd(); hide_window(titlwin); free_window_memory(titlwin); /* CREATE THE POPUP-MENU */ create_popup_menu(&menwin, &popstruct1, NULL, 0, 0); /* READ THE USER-SELECTION AND CALL THE APPROPRIATE ALGORITHM */ do { cursor_off(); clear_box(0, 24, 79, 24, black, white, SPACE); prints(2, 24, black, white, "Use the or the highlighted keys to select press to exit"); prints(10, 24, lwhite, white, "UP/DOWN/ENTER"); prints(68, 24, lwhite, white, "ESC"); preserve_screen(screen.cols, screen.rows); display_window(menwin, 25, 8, 1); option = read_selection(menwin); switch (option) { case 0: /* Step Control is selected */ clear_box(0, 24, 79, 24, black, white, SPACE); prints(2, 24, black, white, "Press for help to start execution to quit"); prints(8, 24, lwhite, white,"F1"); prints(30,24, lwhite, white,"END"); prints(65, 24, lwhite, white,"ESC"); preserve_screen(screen.cols, screen.rows); hide_window(menwin); step_ctrl(); break; case 1: /* PID Control is selected */ clear_box(0, 24, 79, 24, black, white, SPACE); prints(2, 24, black, white, "Press for help to start execution to quit"); prints(8, 24, lwhite, white,"F1"); prints(30,24, lwhite, white,"END"); prints(65, 24, lwhite, white,"ESC"); preserve_screen(screen.cols, screen.rows); hide_window(menwin); pid_ctrl(); break; case 2: /* FST Control is selected */ clear_box(0, 24, 79, 24, black, white, SPACE); prints(2, 24, black, white, "Press for help to start execution to quit"); prints(8, 24, lwhite, white,"F1"); prints(30,24, lwhite, white,"END"); prints(65, 24, lwhite, white,"ESC"); preserve_screen(screen.cols, screen.rows); hide_window(menwin); fst_ctrl(); break; case 3: /* State Feedback Control is selected */ clear_box(0, 24, 79, 24, black, white, SPACE); prints(2, 24, black, white, "Press for help to start execution to quit"); prints(8, 24, lwhite, white,"F1"); prints(30,24, lwhite, white,"END"); prints(65, 24, lwhite, white,"ESC"); preserve_screen(screen.cols, screen.rows); hide_window(menwin); state_ctrl(); break; default: /* read input again */ break; } } while (option != -1); /* HIDE THE WINDOW, RELEASE THE MEMORY AND EXIT THE PROGRAM */ hide_window(menwin); free_window_memory(menwin); restore_video(); _dos_setvect(CTRL_BREAK_INT, orig_break); exit (0); }