home *** CD-ROM | disk | FTP | other *** search
- /*****************************************************************
- Real time simulation of a thermal control system
-
- File: therm.c
-
- This program schedules a simulation of a thermal control system to
- run at highest priority, with its controller running at lower priority.
-
- The background uses the screen graphic display package to maintain a
- display of the system's state and to get operator input for changing setpoint
- or control mode.
-
-
- copyright (c) 1987, D.M. Auslander
-
- Created: 30-June-87
-
- Updates:
- 12-July-87, DMA, initial debugging
- 14-July-87, DMA, add scheduling
- 18-Aug-88, DMA, modified for Microsoft C and xignal
- */
-
- #include <stdio.h>
- #include "display.h"
- #include "dparam.h"
- #include "envir.h"
- #include "xignal.h"
-
- #define UP 0xc8 /* Key definitions */
- #define RIGHT 0xcd
- #define LEFT 0xcb
- #define DOWN 0xd0
- #define SH_UP 0x38 /* Note that these (shifted arrows) are not unique */
- #define SH_DOWN 0x32
-
- extern float get_th1(),get_ref1(),get_act(),get_th2(),get_ref2(),get_m1();
- extern float fv2b();
- extern int loop1(),loop2(),sim_h(); /* Functions that are scheduled */
- extern int chronos(); /* Scheduler */
-
- static float v2a,v2b; /* Dummy variables */
- static int rltm = 2; /* Flag to indicate real time operation
- 0 - Pure simulation
- 1 - Use scheduler, but no clock interrupt
- 2 - Full real time, with clock
- */
- static int cmode = 1; /* Control mode; 0 - manual, 1 - automatic */
- static float t; /* Running time */
- static float t1,t2; /* Computation of sampling interval for non-real-time */
- static float dv = 0.02,dvl = 0.1,dm = 0.02,dml = 0.1; /* Increment
- associated with arrow keys */
-
- /* Simulation system variables: */
-
- static float th1; /* object (pot) temperature */
- static float th2; /* heater temperature */
- static float th0; /* ambient temperature */
- static float k12; /* heat transfer coefficient, object-to-heater */
- static float k10; /* heat transfer coefficient, object-to-ambient */
- static float k20; /* heat transfer coefficient, heater-to-ambient */
- static float q12; /* heat transfer rate. object-to-heater */
- static float q10; /* heat transfer rate, object-to-ambient */
- static float q20; /* heat transfer rate, heater-to-ambient */
- static float qv; /* electrical energy input to heater */
- static float c1; /* heat capacity * mass, object */
- static float c2; /* heat capacity * mass, heater */
- static float rh; /* heater electrical resistance */
- static float vmax; /* maximum voltage from power amplifier */
- static float v; /* voltage input to power amplifier */
- static float vp; /* voltage output from power amplifier
- (unity voltage gain) */
- static float dt; /* simulation step size */
-
- /* Controller variables: */
-
- static float errt1; /* temperature error for object (pot) */
- static float errt2; /* temperature error for heater */
- static float kp1, kp2; /* controller proportional gains */
- static float ki1, ki2; /* controller integral gains */
- static float ref1; /* reference (desired) temperature
- of object */
- static float ref2; /* reference temperature for heater */
- static float m1min,m1max; /* output limits, outer control loop */
- static float m2min,m2max; /* output limits, inner control loop */
- static float i1min,i1max; /* integrator limits, outer loop */
- static float i2min,i2max; /* integrator limits, inner loop */
- static float tsmp1; /* sample time, outer loop */
- static float tsmp2; /* sample time, inner loop */
- static long mstick; /* millisec per clock tick */
- static float i1,i2; /* integrator values, outer, inner loop */
- static float m1,m2; /* controller outputs */
-
- static int tn0,tn1,tn2; /* Timer ID numbers for scheduler */
-
- main()
- {
- struct display d1,d2,d3;
- int i,dum;
- char cbuf[50];
- long ReadClock();
- float tm,tmold = -10.0;
-
- /* Initialize simulation and controller variables */
-
- th1 = 0.0; /* Temperatures */
- th2 = 0.0;
-
- i1 = 0.0; /* Controller integrators */
- i2 = 0.0;
-
- m1 = 0.0; /* Controller output */
- m2 = 0.0;
-
- /* Parameter values */
-
- c1 = 4.0; /* Heat capacities */
- c2 = 4.0;
-
- k12 = 1.0; /* Heat transfer coefficients */
- k10 = 0.3;
- k20 = 0.1;
-
- vmax = 4.0; /* Maximum voltage */
- rh = 1.0; /* Electrical resistance */
- th0 = 0.0; /* Ambient temperature */
-
- t = 0.0; /* Initialize running time */
- dt = 0.25; /* Simulation time step */
-
- tsmp1 = 2.0; /* Sample times for control */
- tsmp2 = 1.0;
- mstick = 50; /* Tick period in millisec */
-
- m1min = m2min = i1min = i2min = 0.0; /* Limits */
- m1max = 2.0;
- m2max = 1.0;
- i1max = 1.0;
- i2max = 2.0;
-
- ref1 = 0.2; /* Setpoint */
-
- kp1 = 0.7; /* Controller gains */
- kp2 = 1.7;
- ki1 = 0.4;
- ki2 = 0.4;
-
- /* Definition of dsp_set, for reference:
- dsp_set(pd,x,y,label,lngth,lcolor,vbot,vtop,fv1,fv2,fmt,dblnk) */
-
- /* First display, pot temperature */
- dsp_set(&d1,3.0,2.,"T_Pot",6.,1,0.0,1.0,get_th1,get_ref1,"%4.2f"," ");
-
- /* Second display, voltage to heater */
- dsp_set(&d2,10.0,2.,"V_Out",6.,2,0.0,2.0,get_act,fv2b,"%4.2f"," ");
- dsp_flag(&d2,1,1,0,0); /* No second variable */
-
- /* Third display, heater temperature */
- dsp_set(&d3,15.0,2.,"T_Heat",6.,3,0.0,1.0,get_th2,get_ref2,"%4.2f"," ");
-
-
- init_scr(); /* Initialize the screen */
- dsp_init(&d1); /* Display the screen static part */
- dsp_init(&d2);
- dsp_init(&d3);
-
- mv_cur(15.0,18.0); /* Print the label for time */
- printf("Time:");
-
- /* Schedule the control tasks
- Highest priority is for the simulation, then loop1, then loop2.
- Loop1 is given higher priority because, if they happen to request service
- simultaneously, loop1 produces data that loop2 uses. This is the reverse of
- the normal convention that would give higher priority to the faster loop.
- */
-
- if(rltm >= 1)
- {
- InitTime(mstick); /* Initialize chronos */
-
- if((tn0 = NewDTimer(sim_h,&dum,100)) < 0)
- {
- printf("Can't allocate timer\n");
- exit();
- }
- SetDTimer(tn0,(long)(1000.0 * dt)); /* Set sample interval */
-
- if((tn1 = NewDTimer(loop1,&dum,90)) < 0)
- {
- printf("Can't allocate timer\n");
- exit();
- }
- SetDTimer(tn1,(long)(1000.0 * tsmp1)); /* Set sample interval */
-
-
- if((tn2 = NewDTimer(loop2,&dum,80)) < 0)
- {
- printf("Can't allocate timer\n");
- exit();
- }
- SetDTimer(tn2,(long)(1000.0 * tsmp2)); /* Set sample interval */
-
- if(cmode == 0)
- {
- StopDTimer(tn1); /* Don't allow controllers to run if in
- manual mode */
- StopDTimer(tn2);
- }
- if(rltm == 2)
- {
- setalarm(mstick); /* set the system clock */
- xignal(XIGTMR,chronos); /* Turn on the interrupt */
- }
- }
- /* Set dummy variables */
- v2a = v2b = 0.0;
-
- t1 = tsmp1; /* Initialize sample interval so controller runs immediately,
- only used for pure simulation (rltm = 0) */
- t2 = tsmp2;
- mode_msg(); /* Put mode indicator on screen */
-
- for(i = 0; ;i++)
- {
- int chr;
-
- if(rltm == 0)
- { /* Use this to run a "pure" simulation --
- i.e., no real time at all */
-
- if(cmode) /* Check to see if mode is automatic */
- {
- if(t1 >= tsmp1)
- {
- loop1(); /* Control */
- t1 = 0.0;
- }
- if(t2 >= tsmp2)
- {
- loop2();
- t2 = 0.0;
- }
- t1 += dt;
- t2 += dt;
- }
- sim_h(); /* Run the simulation here if not real time */
- }
-
- if(rltm == 1)chronos(); /* Run the scheduler from the background */
- if(rltm >= 1)tm = ReadClock() / 1000.0;
- else tm = t;
-
- if((tm - tmold) >= 1.0)
- {
- mv_cur(16.0,18.0); /* Print the current time */
- printf("%5.0f",tm);
- tmold = tm;
- }
-
- dsp_data(&d1);
- dsp_data(&d2);
- dsp_data(&d3);
-
- chr = char_in(); /* Read the keyboard */
-
- if(chr) /* If character has been entered */
- {
- switch(chr)
- {
- case UP:
- if(cmode)set_ref1(get_ref1() + dv);
- else set_act(get_act() + dm);
- break;
-
- case DOWN:
- if(cmode)set_ref1(get_ref1() - dv);
- else set_act(get_act() - dm);
- break;
-
- case SH_UP:
- if(cmode)set_ref1(get_ref1() + dvl);
- else set_act(get_act() + dml);
- break;
-
- case SH_DOWN:
- if(cmode)set_ref1(get_ref1() - dvl);
- else set_act(get_act() - dml);
- break;
-
- case 'm':
- case 'M': /* Set to manual mode */
- if(cmode)
- {
- cmode = 0; /* Set mode */
- mode_msg(); /* Set mode indicator on screen */
-
- if(rltm >= 1)
- {
- StopDTimer(tn1); /* Turn off controllers */
- StopDTimer(tn2);
- }
- }
- break;
-
- case 'a':
- case 'A': /* Set to automatic mode */
- if(!cmode)
- {
- /* Set integrators for bumpless transfer;
- "atomic" change is not necessary because the control
- can't run until the mode is changed. */
-
- m1 = get_th2(); /* Set outer loop so inner loop is
- in balance */
-
- if(ki1 != 0.0) /* Don't change the integrator value if
- the integral gain is zero. */
- {
- i1 = (m1 - kp1 * (ref1 - get_th1()));
- }
- if(ki2 != 0.0)
- {
- i2 = v;
- }
- cmode = 1;
- if(rltm >= 1)
- {
- StartDTimer(tn1);
- StartDTimer(tn2); /* Turn Controllers on */
- }
- mode_msg(); /* Set mode indicator on screen */
- }
- break;
-
- }
-
- if(chr == '\r')break;
- }
- }
- if(rltm == 2)
- {
- setalarm(0L); /* Set timer back to DOS */
- xignal(XIGTMR,XIG_DFL); /* Turn off interrupt */
- }
- rst_scr(); /* Reset the screen */
- }
-
- /* Functions that return process variables -- these are all "atomic"
- processes and must disable interrupts while fetching data.
-
- When doing a non-real-time simulation, dummies must be supplied for
- enable() and disable().
- */
-
- float get_th1()
- {
- float vv;
-
- disable();
- vv = th1;
- enable();
- return(vv);
- }
-
- float get_th2()
- {
- float vv;
-
- disable();
- vv = th2;
- enable();
- return(vv);
- }
-
- float get_m1()
- {
- float vv;
-
- disable();
- vv = m1;
- enable();
- return(vv);
- }
-
- float get_act()
- {
- float vv;
-
- disable();
- vv = v;
- enable();
- return(vv);
- }
-
- float get_ref1()
- {
- float vv;
-
- disable();
- vv = ref1;
- enable();
- return(vv);
- }
-
- float get_ref2()
- {
- float vv;
-
- disable();
- vv = ref2;
- enable();
- return(vv);
- }
-
- /* Set process values -- also atomic */
-
- set_ref1(val)
- float val;
- {
- disable();
- ref1 = val;
- enable();
- }
-
- set_ref2(val)
- float val;
- {
- disable();
- ref2 = val;
- enable();
- }
-
- set_m1(val)
- float val;
- {
- disable();
- m1 = val;
- enable();
- }
-
- set_act(val)
- float val;
- {
- disable();
- v = val;
- enable();
- }
-
-
- float fv2b() /* Return a value for the process variable
- ------------ */
- {
- return(v2b);
- }
-
- /* Controller functions */
-
- pi_cntrl(erp,pt_intg,intmin,intmax,kp,ki,mmin,mmax,pt_m) /* Compute a
- -------------------------------------------------------- PI control */
- float erp,*pt_intg,intmin,intmax,kp,ki,mmin,mmax,*pt_m;
- {
- float flim();
-
- /* New integrator value */
- *pt_intg = flim(*pt_intg + ki * erp, intmin,intmax);
-
- /* Controller output */
- *pt_m = flim(kp *erp + *pt_intg, mmin, mmax); /* Controller output */
- }
-
- loop1(pdum) /* Outer loop
- ------- */
- int *pdum; /* Dummy argument from scheduler */
- {
- float mm;
-
- if(rltm >= 1)SetDTimer(tn1,(long)(1000.0 * tsmp1)); /* Set sample interval */
-
- errt1 = get_ref1() - get_th1();
- pi_cntrl(errt1,&i1,i1min,i1max,kp1,ki1,m1min,m1max,&mm);
-
- set_m1(mm);
- }
-
- loop2(pdum) /* Inner loop
- ------- */
- int *pdum; /* Dummy argument */
- {
- float mm;
-
- if(rltm >= 1)SetDTimer(tn2,(long)(1000.0 * tsmp2)); /* Set sample interval */
-
- set_ref2(get_m1()); /* Use output of outer loop for setpoint */
- errt2 = get_ref2() - get_th2();
- pi_cntrl(errt2,&i2,i2min,i2max,kp2,ki2,m2min,m2max,&mm);
- set_act(mm);
- }
-
- /* Simulation task -- This can be replaced by data in and out for
- a real control problem */
-
- sim_h(pdum) /* Simulation; use the Euler method to integrate one step
- ------- each time called. */
- int *pdum; /* Dummy argument from chronos */
- {
- float flim();
-
- if(rltm >= 1)SetDTimer(tn0,(long)(1000.0 * dt)); /* Set sample interval */
-
- q12 = k12 * (th1 - th2); /* Heat transfer rates */
- q10 = k10 * (th1 - th0);
- q20 = k20 * (th2 - th0);
-
- vp = flim(get_act(),0.0,vmax); /* Power amplifier output */
- qv = vp * vp / rh; /* Electric power into the heater */
-
- th1 -= dt * (q12 + q10)/c1; /* Pot temperature */
- th2 += dt * (q12 - q20 + qv)/c2; /* Heater temperature */
-
- t += dt; /* Increment time */
- }
-
- float flim(x,xmin,xmax)
- float x,xmin,xmax;
- {
- if(x < xmin)return(xmin);
- if(x > xmax)return(xmax);
- return(x);
- }
-
- mode_msg() /* Print a mode indicator on screen
- ---------- */
- {
- mv_cur(0.0,13.0);
-
- if(cmode)
- {
- printf("Mode: Automatic");
- }
- else
- {
- printf("Mode: Manual ");
- }
- }
-
- /* These dummies must be supplied if rltm != 2 -- they can be supplied
- here or in a separate file. */
-
- /*
- enable(){}
- disable(){}
- */
-
- /* They are commented out at this point since the default parameter setting
- is to run real time. */
-
- /* Panic routine shuts off interrupts and exits (called from chronos) */
-
- panic(msg) /* Shut off real time and exit
- ---------- */
- char *msg;
- {
- if(rltm == 2)xignal(XIGTMR,XIG_DFL);
- printf("<Panic> %s\n",msg);
- exit();
- }
-