Set SEGPs immediately.
[kokare.git] / kokare.c
1 #include <avr/io.h>
2 #include <avr/interrupt.h>
3 #include <inttypes.h>
4 #include <math.h>
5
6 #define SEGA 4
7 #define SEGB 2
8 #define SEGC 1
9 #define SEGD 32
10 #define SEGE 64
11 #define SEGF 16
12 #define SEGG 8
13 #define SEGP 128
14
15 uint8_t font[16] = {
16     SEGA | SEGB | SEGC | SEGD | SEGE | SEGF,
17     SEGB | SEGC,
18     SEGA | SEGB | SEGD | SEGE | SEGG,
19     SEGA | SEGB | SEGC | SEGD | SEGG,
20     SEGB | SEGC | SEGF | SEGG,
21     SEGA | SEGC | SEGD | SEGF | SEGG,
22     SEGA | SEGC | SEGD | SEGE | SEGF | SEGG,
23     SEGA | SEGB | SEGC,
24     SEGA | SEGB | SEGC | SEGD | SEGE | SEGF | SEGG,
25     SEGA | SEGB | SEGC | SEGD | SEGF | SEGG,
26     SEGA | SEGB | SEGC | SEGE | SEGF | SEGG,
27     SEGC | SEGD | SEGE | SEGF | SEGG,
28     SEGA | SEGD | SEGE | SEGF,
29     SEGB | SEGC | SEGD | SEGE | SEGG,
30     SEGA | SEGD | SEGE | SEGF | SEGG,
31     SEGA | SEGE | SEGF | SEGG,
32 };
33 /* LED */
34 uint8_t dsp[2] = {0, 0};
35 char leda = 0;
36 char ledc = 0;
37 /* Timer */
38 volatile int oticks = 0;
39 unsigned long mnow;
40 /* Pulse counter */
41 volatile char pstate = 0;
42 char pval = 0;
43 /* Switch */
44 volatile char sstate = 0;
45 int stime = 0;
46 /* Temp sensor */
47 volatile char tstate = 0;
48 volatile char tlock = 0;
49 unsigned long tstart;
50 unsigned long ttime;
51 unsigned long ttimea = 10000;
52 char tavgok = 0;
53 /* Conversion loop */
54 int tempk;
55 volatile ktok = 0;
56 /* Zero-cross detector*/
57 volatile char zok = 0;
58 volatile char ztime = 0;
59 /* Triac */
60 char trstate = 0;
61 char tron = 0;
62 volatile char trtime;
63 volatile char trdelay = 0;
64
65 void init(void)
66 {
67     /* Timer init
68      * Timer 0 cycles the Triac
69      * Timer 1 is used for global timing
70      * Timer 2 cycles the LED display
71      */
72     OCR0A = 100;
73     TCCR0A = 2;
74     TCCR0B = 1;
75     TIMSK0 = 2;
76     TCCR1A = 0;
77     TCCR1B = 1;
78     TIMSK1 = 1;
79     OCR2A = 16;
80     TCCR2A = 2;
81     TCCR2B = 4;
82     TIMSK2 = 2;
83     
84     /*
85      * B0..2 = Pulse sensor
86      * B3..5 = ISP
87      * B6..7 = CLK
88      */
89     DDRB = 0x38;
90     PORTB = 0x07;
91     PCMSK0 = 0x07;
92     PCICR = 0x01;
93     /*
94      * C0..5 = LEDA0..5
95      * C6 = /RESET
96      * C7 = NC
97      */
98     DDRC = 0x3f;
99     PORTC = 0x00;
100     /*
101      * D0 = Triac
102      * D1 = NTC FET
103      * D2 = ZCD (INT0)
104      * D3 = NTC Op-amp (INT1)
105      * D4..5 = LEDA6..7
106      * D6..7 = LEDC0..1
107      */
108     DDRD = 0xf3;
109     PORTD = 0x00;
110     EICRA = 0x0d;
111     EIMSK = 0x03;
112 }
113
114 unsigned char bindisp(unsigned char num)
115 {
116     unsigned char ret;
117     
118     ret = 0;
119     if(num & 1)
120         ret |= SEGA;
121     if(num & 2)
122         ret |= SEGB;
123     if(num & 4)
124         ret |= SEGC;
125     if(num & 8)
126         ret |= SEGD;
127     if(num & 16)
128         ret |= SEGE;
129     if(num & 32)
130         ret |= SEGF;
131     if(num & 64)
132         ret |= SEGG;
133     if(num & 128)
134         ret |= SEGP;
135     return(ret);
136 }
137
138 void display(char num, char d0, char d1)
139 {
140     dsp[0] = font[(num / 10) % 10] | (d0?SEGP:0);
141     dsp[1] = font[num % 10] | (d1?SEGP:0);
142 }
143
144 void disphex(unsigned char num)
145 {
146     dsp[0] = font[(num & 0xf0) >> 4];
147     dsp[1] = font[num & 0x0f];
148 }
149
150 unsigned long getticks(void)
151 {
152     uint16_t v;
153     unsigned long r;
154     
155     cli();
156     v = TCNT1;
157     r = v + (((unsigned long)oticks) << 16);
158     if((TIFR1 & 0x01) && !(v & 0x8000))
159         r += 0x10000;
160     sei();
161     return(r);
162 }
163
164 void ledcycle(void)
165 {
166     uint8_t c, d, v;
167     
168     if(++leda >= 8) {
169         leda = 0;
170         if(++ledc >= 2)
171             ledc = 0;
172     }
173     if(dsp[ledc] & (1 << leda)) {
174         if(leda < 6) {
175             c = 1 << leda;
176             d = 0;
177         } else {
178             c = 0;
179             d = 0x10 << (leda - 6);
180         }
181         d |= ledc?0x40:0x80;
182     } else {
183         c = d = 0;
184     }
185     PORTC = c;
186     PORTD = (PORTD & 0x0f) | d;
187 }
188
189 void tempcycle(void)
190 {
191     if(tstate == 0) {
192         if((PIND & 8) && (tlock == 0)) {
193             cli();
194             PORTD |= 2;
195             sei();
196             tstart = mnow;
197             tstate = 1;
198         }
199     } else if(tstate == 1) {
200         if(mnow - tstart > 1000) {
201             cli();
202             PORTD &= ~2;
203             sei();
204             tstate = 0;
205             tstart = mnow;
206         }
207     }
208 }
209
210 void calcavg(void)
211 {
212     if(tlock == 1) {
213         tlock = 2;
214         ttimea = ((ttimea * 15) + ttime) >> 4;
215         tlock = 0;
216         tavgok = 1;
217     }
218 }
219
220 void convcycle(void)
221 {
222     static char state = 0;
223     static unsigned long last = 0;
224     static float a, ra, l, t;
225     
226     /*
227      * Theoretically:
228      *  t = RC * ln(2) => R = t / (C * ln(2))
229      *  R = A * exp(B / T) => T = B / ln(R / A)
230      *  T = B / ln(R / (A * C * ln(2)))
231      * In the following: 
232      *  a = ttimea as float
233      *  C = 1e6 / (A * C * ln(2))
234      *  ra = a * C
235      *  l = ln(ra)
236      *  t = B / l
237      * Note, temperature is in Kelvin
238      */
239 #define C 9.792934
240 #define B 4020.0
241     if(state == 0) {
242         if((mnow - last > 200000) && tavgok) {
243             a = (float)ttimea;
244             state = 1;
245             tavgok = 0;
246             last = mnow;
247         }
248     } else if(state == 1) {
249         ra = a * C;
250         state = 2;
251     } else if(state == 2) {
252         l = log(ra);
253         state = 3;
254     } else if(state == 3) {
255         t = B / l;
256         state = 4;
257     } else if(state == 4) {
258         tempk = (int)t;
259         ktok = 1;
260         state = 0;
261     }
262 }
263
264 int main(void)
265 {
266     int state, cur, run, rstate, delta;
267     unsigned long utime;
268     
269     state = 0;
270     cur = 100;
271     run = 0;
272     rstate = 0;
273     init();
274     sei();
275     display(0, 0, 0);
276
277     while(1) {
278         mnow = getticks();
279         tempcycle();
280         calcavg();
281         convcycle();
282
283 #if 1
284         /*
285          * User interface
286          */
287         if(state == 0) {
288             /* Display temperature */
289             if(ktok) {
290                 ktok = 0;
291                 if((tempk >= 273) && (tempk <= 372)) {
292                     display(tempk - 273, 0, run);
293                 } else {
294                     dsp[0] = SEGG;
295                     dsp[1] = SEGG | (run?SEGP:0);
296                 }
297             }
298             if(pval != 0)
299                 state = 1;
300             if(sstate == 2) {
301                 sstate = 0;
302                 if(stime > 10)
303                     state = 2;
304                 else
305                     run = !run;
306             }
307         } else if(state == 1) {
308             /* Temp setting */
309             if(pval != 0) {
310                 cur += pval;
311                 pval = 0;
312                 if(cur < 0)
313                     cur = 0;
314                 if(cur > 100)
315                     cur = 100;
316                 if(cur < 100) {
317                     display(cur, 0, run);
318                 } else {
319                     dsp[0] = SEGG;
320                     dsp[1] = SEGG | (run?SEGP:0);
321                 }
322                 utime = mnow;
323             }
324             if(mnow - utime > 1000000)
325                 state = 0;
326             if(sstate == 2) {
327                 run = !run;
328                 sstate = 0;
329             }
330         } else if(state == 2) {
331             /* Display raw temp time reading */
332             if(ttimea < 20000) {
333                 display((ttimea / 100) % 100, 1, ttimea >= 10000);
334             } else {
335                 display(ttimea / 1000, 0, 0);
336             }
337             if(sstate == 2) {
338                 state = 0;
339                 sstate = 0;
340             }
341         }
342         /*
343          * Set Triac to match temperature
344          */
345         if(run) {
346             delta = cur - (tempk - 273);
347             if(rstate == 0) {
348                 if(delta > 0) {
349                     tron = 1;
350                     if(delta > 8) {
351                         /* For some reason, the Triac currently doesn't
352                          * trigger on one of the AC half-cycles below 0.7
353                          * ms. */
354                         trdelay = 7;
355                     } else {
356                         trdelay = 79 - (delta * 9);
357                     }
358                 } else {
359                     tron = 0;
360                     rstate = 1;
361                 }
362             } else if(rstate == 1) {
363                 tron = 0;
364                 if(delta >= 2)
365                     rstate = 0;
366             }
367         } else {
368             tron = 0;
369         }
370 #endif
371         /*
372           dsp[0] = bindisp((ttimea & 0xff00) >> 8);
373           dsp[1] = bindisp(ttimea & 0x00ff);
374         */
375         /*
376           disphex((ttimea & 0xff000) >> 12);
377         */
378 #if 0
379         /*
380           Temp display
381         */
382         if(ttimea < 20000) {
383             display((ttimea / 100) % 100);
384             dsp[0] |= SEGP;
385             if(ttimea >= 10000)
386                 dsp[1] |= SEGP;
387         } else {
388             display(ttimea / 1000);
389         }
390 #endif
391 #if 0
392         /*
393          * ZVD debug
394          */
395         if(zok) {
396             if(++cur > 99)
397                 cur = 0;
398             display(cur);
399             zok = 0;
400         }
401 #endif
402 #if 0
403         /*
404           Phony Triac control
405          */
406         if(pval != 0) {
407             cur += pval;
408             if(cur < 0)
409                 cur = 0;
410             if(cur > 99)
411                 cur = 99;
412             display(cur);
413             trdelay = 99 - cur;
414             pval = 0;
415         }
416         if(sstate == 2) {
417             tron = !tron;
418             sstate = 0;
419         }
420         if(tron)
421             dsp[1] |= SEGP;
422         else
423             dsp[1] &= ~SEGP;
424 #endif
425 #if 0
426         /*
427           Pulse counter display
428         */
429         cur += pval;
430         pval = 0;
431         if(sstate == 2) {
432             cur = stime;
433             sstate = 0;
434         }
435         if(cur > 99)
436             cur = 99;
437         if(cur < -99)
438             cur = -99;
439         if(cur < 0) {
440             display(-cur);
441             dsp[0] |= SEGP;
442         } else {
443             display(cur);
444         }
445         if(PINB & 4)
446             dsp[1] |= SEGP;
447 #endif
448     }
449 }
450
451 ISR(SIG_INTERRUPT0)
452 {
453     ztime = 0;
454     zok = 1;
455 }
456
457 ISR(SIG_INTERRUPT1)
458 {
459     unsigned long now;
460     
461     now = getticks();
462     if(tstate == 0) {
463         tstate = 1;
464         if(tlock != 2)
465             ttime = now - tstart;
466         tstart = now;
467         PORTD |= 2;
468         tlock = 1;
469     }
470 }
471
472 ISR(SIG_OUTPUT_COMPARE0A)
473 {
474     if(trstate == 0) {
475         ztime++;
476         if(tron && (ztime >= trdelay)) {
477             PORTD |= 1;
478             trstate = 1;
479             trtime = 0;
480         }
481     } else if(trstate == 1) {
482         trtime++;
483         if(trtime >= 5) {
484             PORTD &= ~1;
485             trstate = 0;
486         }
487     }
488 }
489
490 ISR(SIG_OUTPUT_COMPARE2A)
491 {
492     ledcycle();
493 }
494
495 ISR(SIG_OVERFLOW1)
496 {
497     oticks++;
498 }
499
500 ISR(SIG_PIN_CHANGE0)
501 {
502     if((sstate == 0) && !(PINB & 4)) {
503         stime = oticks;
504         sstate = 1;
505     }
506     if((sstate == 1) && (PINB & 4)) {
507         stime = oticks - stime;
508         sstate = 2;
509     }
510     if(pstate == 0) {
511         if((PINB & 2) == 0) {
512             pstate = 1;
513         } else if((PINB & 1) == 0) {
514             pstate = 2;
515         }
516     } else if(pstate == 1) {
517         if((PINB & 1) == 0) {
518             pval++;
519             pstate = 3;
520         } else {
521             pstate = 0;
522         }
523     } else if(pstate == 2) {
524         if((PINB & 2) == 0) {
525             pval--;
526             pstate = 3;
527         } else {
528             pstate = 0;
529         }
530     } else {
531         if((PINB & 2) && (PINB & 1))
532             pstate = 0;
533     }
534 }