Imported Upstream version 2.9.0
[debian/cc1111] / device / examples / an429.c
1 /*
2         this program the flow of air through a rotary flowmeter
3         and displays the calculated cfm. the output of the meter
4         is a small duty cycle pulse, the period of repatition of
5         which if proportional to the flow. the flow is compensated
6         for changes in pressure and temperature to maintain
7         calibration. if the flow exceeds an adjustable setpoint
8         it energizes a 2 form c relay for user application use.
9 */
10
11 #include <reg552.h>
12                              
13 #define ZERO_K          2730    /* 0 degress centigrade in kelvin       */
14 #define ONE_TENTH_CFM   4444444L /* 1/10 cfm in microseconds            */
15 #define STD_TEMP        2980    /* 25 degrees centigrade in kelvin      */
16 #define STD_ATM         147     /* one atmosphere in tenths psi         */
17 #define LOWEST_CFM      0x40    /* maximun period from meter 0x400000   */
18 #define START_ADC0      0x28    /* commands to start appropriate        */
19 #define START_ADC1      0x29    /* a/d conversion cycle                 */
20 #define START_ADC2      0x2a    /*                                      */
21 #define START_ADC3      0x2b    /*                                      */
22 #define START_ADC4      0x2c    /*                                      */
23 #define ADCI            0x10    /* a/d converter status flags           */
24 #define ADCS            0x08    /*                                      */
25 #define FREERUN_I       0x10    /*                                      */
26 #define SEG_A           0x01    /* P3 position for display segment 'a'  */
27 #define CFM             0x01    /* P3 position for 'cfm' led            */
28 #define SEG_B           0x02    /* P3 position for display segment 'b'  */
29 #define DEGREES         0x02    /* P3 position for 'degrees' led        */
30 #define SEG_C           0x04    /* P3 position for display segment 'c'  */
31 #define PSI             0x04    /* P3 position for 'psi' led            */
32 #define SEG_D           0x08    /* P3 position for display segment 'd'  */
33 #define SETPOINT        0x08    /* P3 position for 'setpoint' led       */
34 #define SEG_E           0x10    /* P3 position for display segment 'e'  */
35 #define SEG_F           0x20    /* P3 position for display segment 'f'  */
36 #define SEG_G           0x40    /* P3 position for display segment 'g'  */
37 #define SEG_DP          0x80    /* P3 position for display decimal pt.  */
38
39 typedef unsigned char byte;     /* type define objects with             */
40 typedef unsigned int word;      /* more classical microprocessor        */
41 typedef unsigned long l_word;   /* meaning                              */
42
43 #define TRUE 1                  /* define logical true / false          */
44 #define FALSE 0                 /* values for bit variables             */
45
46
47 /*
48         define look-up table of possible seven segment display
49         characters possible to display. table contents need to 
50         be inverted before use to be compatible with  U2 (udn2585a)
51 */
52
53 code byte segments[] =
54 {
55     SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F        ,      /* 0 */
56     SEG_B | SEG_C                                ,      /* 1 */
57     SEG_A | SEG_B |         SEG_D | SEG_E |         SEG_G,      /* 2 */
58     SEG_A | SEG_B | SEG_C | SEG_D |                 SEG_G,      /* 3 */
59     SEG_B | SEG_C |                 SEG_F | SEG_G,      /* 4 */
60     SEG_A |         SEG_C | SEG_D         | SEG_F | SEG_G,      /* 5 */
61     SEG_A |         SEG_C | SEG_D | SEG_E | SEG_F | SEG_G,      /* 6 */
62     SEG_A | SEG_B | SEG_C                                ,      /* 7 */
63     SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F | SEG_G,      /* 8 */
64     SEG_A | SEG_B | SEG_C | SEG_D |         SEG_F | SEG_G,      /* 9 */
65     SEG_A |                 SEG_D | SEG_E | SEG_F | SEG_G       /* error */
66 };
67
68 sbit    RELAY           = 0x96; /* active hi to turn on setpoint relay  */
69 sbit    STROBE_0        = 0x80; /* active hi to enable status led's     */
70 sbit    STROBE_1        = 0x81; /* active hi to enable display cr15     */
71 sbit    STROBE_2        = 0x82; /* active hi to enable display cr14     */
72 sbit    NO_FLOW         = 0x83; /* flag set when no flow detected       */
73 sbit    STROBE_3        = 0x84; /* active hi to enable display cr13     */
74 sbit    SEL_0           = 0x93; /* active low inputs used to select     */
75 sbit    SEL_1           = 0x94; /* mode being displayed                 */
76 sbit    INTR            = 0x95; /*                                      */
77 sbit    UPDATE          = 0x97; /* flag set when time to update display */
78 data    word    cfm;            /* gas flow in tenths of a cfm          */
79 data    word    setpoint;       /* relay setpoint in tenths of a cfm    */
80 data    word    degree_c;       /* temperature in tenths centagrade     */
81 data    l_word  corr;           /* intermediate calculation value       */
82 data    word    psi;            /* pressupe in tenths of a psi          */
83 data    byte    display0;       /* variables to hold values for the     */
84 data    byte    display1;       /* displays during refresh              */
85 data    byte    display2;       /*                                      */
86 data    byte    display3;       /*                                      */
87 data    byte    disp_pntr;      /* pointer to next display to enable    */
88 data    byte    refresh;        /* counter determines display updates   */
89 data    byte    high;           /* bits 16 - 23 of flow period          */
90 data    byte    middle;         /* bits 8 - 15 of flow period           */
91 data    byte    low;            /* bits 0 - 7  of flow period           */
92 data    byte    ticks;          /* incremented by timer overflow        */
93
94 /*
95         use the free-running I timer to multiplex the led displays
96         at approx. 1000 hz.
97 */
98
99 void multiplex() interrupt 3
100 {
101         switch(disp_pntr)
102         {
103         case 0x00:
104             STROBE_3 = FALSE;   /* turn off display cr13        */
105             P3 = 0xff;          /* turn off all segments        */
106             P3 = display0;      /* load segments for led's      */
107             STROBE_0 = TRUE;    /* turn on status led's         */
108             disp_pntr = 1;      /* increment pointer to dsiplay */
109             break;
110         case 0x01:
111             STROBE_0 = FALSE;   /* turn off status led's        */
112             P3 = 0xff;          /* turn off all segments        */
113             P3 = display1;      /* load segments for tenths     */
114             STROBE_1 = TRUE;    /* turn on display cr15         */
115             disp_pntr = 2;      /* increment pointer to dsiplay */
116             break;
117         case 0x02:
118             STROBE_1 = FALSE;   /* turn off display cr15        */
119             P3 = 0xff;          /* turn off all segments        */
120             P3 = display2;      /* load segments for units      */
121             STROBE_2 = TRUE;    /* turn on display cr14         */
122             disp_pntr = 3;      /* increment pointer to dsiplay */
123             break;
124         case 0x03:
125             STROBE_2 = FALSE;   /* turn off display cr14        */
126             P3 = 0xff;          /* turn off all segments        */
127             P3 = display3;      /* load segments for tens       */
128             STROBE_3 = TRUE;    /* turn on display cr13         */
129             disp_pntr = 0;      /* increment pointer to dsiplay */
130             break;
131         }
132 }
133
134 /*
135         use the free running pwm prescaler to generate
136         interrupts every 92 hz. every 32nd interrupt
137         set the UPDATE flag to enable the reading of
138         the command switches, and updating of the led
139         display contents.
140 */
141 void read_switch() interrupt 6
142 {
143         if(refresh++ == 32)
144         {       UPDATE = TRUE;
145                 refresh = 0;
146         }
147 }
148
149 /*
150         whenever the timer overflows from 0xffff to 0x0000
151         increment the variable 'ticks' which represent the
152         highest order (16 - 23) bits of the gas flow period
153         in microseconds. if the variable 'ticks' is greater
154         than the period representing a flow of < 0.1 cfm
155         then set the NO_FLOW flag to enable display of 00.0
156 */
157
158 void overflow() interrupt 1
159 {
160         if(++ticks > LOWEST_CFM)
161         {
162                 cfm = 0;
163                 ticks = 0;
164                 NO_FLOW = TRUE;
165         }
166 }
167
168 /*
169         an external interrupt generated by a tach pulse
170         from the flowmeter reads the current value of the
171         timer into variables 'low' and 'middle', and then
172         resets the timers. the 'ticks' variable described
173         above is also copied to variable 'high', and then
174         reset to zero. the NO_FLOW flag is cleared to 
175         enable display of the calculated cfm.
176 */
177
178 void calc_cfm() interrupt 0
179 {
180         low = TL0;
181         TL0 = 0;
182         middle = TH0;
183         TH0 = 0;
184         high = ticks;
185         ticks = 0;
186         NO_FLOW = FALSE; 
187 }
188
189 void main()
190 {
191         RELAY           = 0;    /* initialize output pins               */
192         INTR            = 1;
193         UPDATE          = 1;
194         STROBE_0        = 0;
195         STROBE_1        = 0;
196         STROBE_2        = 0;
197         STROBE_3        = 0;
198         NO_FLOW         = 0;
199         TL0     = 0;            /* timer 0 period 0x10000 u_seconds     */
200         TH0     = 0;
201         PWMP    = 255;          /* pwm timer interrupt at 92 hz         */
202         TR0     = 1;            /* enable timer 0                       */
203         IT0     = 1;            /* INT0 is edge active                  */
204         ticks   = 0;            /* initialize variables                 */
205         cfm     = 0;
206         low     = 0;
207         middle  = 0;
208         high    = 0;
209         degree_c = 250;
210         psi     = 147;
211         corr    = 0;
212         refresh = 0;
213         disp_pntr = 0;
214         IEN0    = 0xab;         /* enable intrrupts                     */
215 #ifdef MY
216 /*
217         main execution loop, executes forever.
218 */
219
220         while(1)
221         {
222
223 /*
224         calculate base cfm rate - first create long word representing
225         flow rate period in microseconds. then subtract out the time
226         overhead in servicing the routine 'calc_cfm'. then divide the
227         period into the period for 1/10 cfm, to get flow rate in 1/10
228         cfm resolution.
229 */ 
230
231                 corr = high * 0x10000L;
232                 corr += (middle * 0x100L);
233                 corr += low;
234                 corr = ONE_TENTH_CFM / corr;
235
236 /*
237         read temperature - measure output from the LM35 sensor,
238         scaled by the AMP-02. the scaling results in a range
239         of 0 to 51.0 degrees centigrade, in 0.2 degree steps.
240 */
241
242                 ADCON = START_ADC1;
243                 while(ADCON & ADCS) ;
244                 degree_c = ((word)ADDATH) << 8 | ADDATL;
245                 degree_c *= 2;
246
247 /*
248         compensate cfm rate for temperature - convert temperature
249         into degrees kelvin, then divide it into the measured flow
250         rate multiplied by the calibration temperature of the flow-
251         meter in degrees kelvin. (nominal 25 degrees centigrade)
252 */
253
254                 corr *= STD_TEMP;
255                 corr /= (ZERO_K + degree_c);   
256
257 /*
258         read pressure - measure output of the KP100A pressure trans-
259         ducer, scaled by the AMP_02. the scaling results in a range
260         of 0 to 25.5 psi, in 1/10 psi steps.
261 */
262
263                 ADCON = START_ADC0;
264                 while(ADCON & ADCS) ;
265                 psi = ((word) ADDATH << 8) | ADDATL;
266
267 /*
268         compensate cfm rate for pressure - multiply measured pres-
269         sure and the calculated flow rate, and then divide it by
270         the standard atmospheric pressure at sea-level. (nominal
271         14.7 psi)
272
273 */
274
275                 corr *= psi;
276                 corr /= STD_ATM;   
277                 cfm = corr;
278
279 /*
280         read setpoint pot to obtain setpoint in the range of
281         0 - 25.5 cfm in 1/10 cfm steps.
282 */
283
284                 ADCON = START_ADC2;
285                 while(ADCON & ADCS) ;
286                 setpoint = ADAT;
287
288 /*
289         test if cfm rate greater or equal to the
290         setpoint, and if so then energize relay
291 */
292
293                 if(setpoint > cfm)
294                         RELAY = 0;
295                 else
296                         RELAY = 1;
297
298 /*
299         test if update flag has been set, and if so reset flag.
300 */
301
302                 if(UPDATE)
303                 {
304                         UPDATE = 0;
305
306 /*
307         then test if the no flow flag has been set. if so then
308         display 00.0 cfm
309 */
310
311                         if(NO_FLOW)
312                         {
313                                 display0 = ~CFM;
314                                 display1 = ~segments[0];
315                                 display2 = ~(segments[0] | SEG_DP);
316                                 display3 = ~segments[0];
317                         }
318
319 /*
320         if the no flow flag was not set then read the display
321         select switches, and display the appropriate data.
322 */
323
324                         else if(SEL_0)
325                         {
326                                 if(SEL_1)
327                                 {
328
329 /*
330         if no swich is depressed then the default display is
331         the flow rate in cfm. test the flowrate is greater than
332         or equal to 30 cfm then display the overrange message
333         EEE else the flow in XX.X format.
334 */
335
336                                         if(cfm <= 300)
337                                         {
338                                                 display0 = ~CFM;
339                                                 display1 = ~segments[cfm % 10];
340                                                 cfm /= 10;
341                                                 display2 = ~(segments[cfm % 10]);
342                                                 cfm /= 10;
343                                                 display3 = ~segments[cfm % 10];
344                                         }
345                                         else                
346                                         {                   
347                                                 display0 = ~CFM;
348                                                 display1 = ~segments[10];
349                                                 display2 = ~segments[10];
350                                                 display3 = ~segments[10];
351                                         }
352                                 }
353
354 /*
355         if switch 1 is depressed then display temperature.
356 */
357
358                                 else
359                                 {       
360                                         display0 = ~DEGREES;
361                                         display1 = ~segments[degree_c % 10];
362                                         degree_c /= 10;
363                                         display2 = ~(segments[degree_c % 10] | SEG_DP);
364                                         degree_c /= 10;
365                                         display3 = ~segments[degree_c % 10];
366                                 }
367                         }
368                         else
369                         {
370
371 /*
372         if switch 2 depressed then display the pressure.
373 */
374
375                                 if(SEL_1)
376                                 {
377                                         display0 = ~PSI;
378                                         display1 = ~segments[psi % 10];
379                                         psi /= 10;
380                                         display2 = ~(segments[psi % 10] | SEG_DP);
381                                         psi /= 10;
382                                         display3 = ~segments[psi % 10];
383                                 }
384
385 /*
386         if switch 3 depressed then display the setpoint.
387 */
388
389                                 else
390                                 {
391                                         display0 = ~SETPOINT;
392                                         display1 = ~segments[setpoint % 10];
393                                         setpoint /= 10;
394                                         display2 = ~(segments[setpoint % 10] | SEG_DP);
395                                         setpoint /= 10;
396                                         display3 = ~segments[setpoint % 10];
397                                 }
398                         }
399                 }
400         }
401 #endif
402 }