8d151dfab08f9705bac3276a2500f83ff31a56e4
[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)
139 {
140     dsp[0] = font[(num / 10) % 10];
141     dsp[1] = font[num % 10];
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 /*
151  * This version is used outside interrupts.
152  * It spins until no overflow has happened.
153  */
154 unsigned long getticks(void)
155 {
156     uint16_t v;
157     unsigned long r;
158     
159     do {
160         v = oticks;
161         r = TCNT1 + (((unsigned long)oticks) << 16);
162     } while(v != oticks);
163     return(r);
164 }
165
166 /*
167  * This version is used in interrupts
168  */
169 unsigned long getticks2(void)
170 {
171     uint16_t v;
172     unsigned long r;
173     
174     v = TCNT1;
175     r = v + (((unsigned long)oticks) << 16);
176     if((TIFR1 & 0x01) && !(v & 0x8000))
177         return(r + 0x10000);
178     else
179         return(r);
180 }
181
182 void ledcycle(void)
183 {
184     uint8_t c, d, v;
185     
186     if(++leda >= 8) {
187         leda = 0;
188         if(++ledc >= 2)
189             ledc = 0;
190     }
191     if(dsp[ledc] & (1 << leda)) {
192         if(leda < 6) {
193             c = 1 << leda;
194             d = 0;
195         } else {
196             c = 0;
197             d = 0x10 << (leda - 6);
198         }
199         d |= ledc?0x40:0x80;
200     } else {
201         c = d = 0;
202     }
203     PORTC = c;
204     PORTD = (PORTD & 0x0f) | d;
205 }
206
207 void tempcycle(void)
208 {
209     if(tstate == 0) {
210         if((PIND & 8) && (tlock == 0)) {
211             cli();
212             PORTD |= 2;
213             sei();
214             tstart = mnow;
215             tstate = 1;
216         }
217     } else if(tstate == 1) {
218         if(mnow - tstart > 1000) {
219             cli();
220             PORTD &= ~2;
221             sei();
222             tstate = 0;
223             tstart = mnow;
224         }
225     }
226 }
227
228 void calcavg(void)
229 {
230     if(tlock == 1) {
231         tlock = 2;
232         ttimea = ((ttimea * 15) + ttime) >> 4;
233         tlock = 0;
234         tavgok = 1;
235     }
236 }
237
238 void convcycle(void)
239 {
240     static char state = 0;
241     static unsigned long last = 0;
242     static float a, ra, l, t;
243     
244     /*
245      * Theoretically:
246      *  t = RC * ln(2) => R = t / (C * ln(2))
247      *  R = A * exp(B / T) => T = B / ln(R / A)
248      *  T = B / ln(R / (A * C * ln(2)))
249      * In the following: 
250      *  a = ttimea as float
251      *  C = 1e6 / (A * C * ln(2))
252      *  ra = a * C
253      *  l = ln(ra)
254      *  t = B / l
255      * Note, temperature is in Kelvin
256      */
257 #define C 9.792934
258 #define B 4020.0
259     if(state == 0) {
260         if((mnow - last > 200000) && tavgok) {
261             a = (float)ttimea;
262             state = 1;
263             tavgok = 0;
264             last = mnow;
265         }
266     } else if(state == 1) {
267         ra = a * C;
268         state = 2;
269     } else if(state == 2) {
270         l = log(ra);
271         state = 3;
272     } else if(state == 3) {
273         t = B / l;
274         state = 4;
275     } else if(state == 4) {
276         tempk = (int)t;
277         ktok = 1;
278         state = 0;
279     }
280 }
281
282 int main(void)
283 {
284     int state, cur, run;
285     unsigned long utime;
286     
287     state = 0;
288     cur = 100;
289     run = 0;
290     init();
291     sei();
292     display(0);
293
294     while(1) {
295         mnow = getticks();
296         tempcycle();
297         calcavg();
298         convcycle();
299
300 #if 1
301         /*
302          * User interface
303          */
304         if(state == 0) {
305             /* Display temperature */
306             if(ktok) {
307                 ktok = 0;
308                 if((tempk >= 273) && (tempk <= 372)) {
309                     display(tempk - 273);
310                 } else {
311                     dsp[0] = dsp[1] = SEGG;
312                 }
313             }
314             if(pval != 0)
315                 state = 1;
316             if(sstate == 2) {
317                 sstate = 0;
318                 if(stime > 10)
319                     state = 2;
320                 else
321                     run = !run;
322             }
323             if(run)
324                 dsp[1] |= SEGP;
325             else
326                 dsp[1] &= ~SEGP;
327         } else if(state == 1) {
328             /* Temp setting */
329             if(pval != 0) {
330                 cur += pval;
331                 pval = 0;
332                 if(cur < 0)
333                     cur = 0;
334                 if(cur > 100)
335                     cur = 100;
336                 if(cur < 100)
337                     display(cur);
338                 else
339                     dsp[0] = dsp[1] = SEGG;
340                 utime = mnow;
341             }
342             if(mnow - utime > 1000000)
343                 state = 0;
344             if(sstate == 2) {
345                 run = !run;
346                 sstate = 0;
347             }
348         } else if(state == 2) {
349             /* Display raw temp time reading */
350             if(ttimea < 20000) {
351                 display((ttimea / 100) % 100);
352                 dsp[0] |= SEGP;
353                 if(ttimea >= 10000)
354                     dsp[1] |= SEGP;
355             } else {
356                 display(ttimea / 1000);
357             }
358             if(sstate == 2) {
359                 state = 0;
360                 sstate = 0;
361             }
362         }
363         /*
364          * Set Triac to match temperature
365          */
366         if(run) {
367             if(tempk - 273 < cur) {
368                 tron = 1;
369                 if(cur - (tempk - 273) > 5) {
370                     /* For some reason, the Triac currently doesn't
371                      * trigger on one of the AC half-cycles below 0.7
372                      * ms. */
373                     trdelay = 7;
374                 } else if(cur - (tempk - 273) >= 3) {
375                     trdelay = 20;
376                 } else if(cur - (tempk - 273) >= 2) {
377                     trdelay = 40;
378                 } else {
379                     trdelay = 70;
380                 }
381             } else {
382                 tron = 0;
383             }
384         } else {
385             tron = 0;
386         }
387 #endif
388         /*
389           dsp[0] = bindisp((ttimea & 0xff00) >> 8);
390           dsp[1] = bindisp(ttimea & 0x00ff);
391         */
392         /*
393           disphex((ttimea & 0xff000) >> 12);
394         */
395 #if 0
396         /*
397           Temp display
398         */
399         if(ttimea < 20000) {
400             display((ttimea / 100) % 100);
401             dsp[0] |= SEGP;
402             if(ttimea >= 10000)
403                 dsp[1] |= SEGP;
404         } else {
405             display(ttimea / 1000);
406         }
407 #endif
408 #if 0
409         /*
410          * ZVD debug
411          */
412         if(zok) {
413             if(++cur > 99)
414                 cur = 0;
415             display(cur);
416             zok = 0;
417         }
418 #endif
419 #if 0
420         /*
421           Phony Triac control
422          */
423         if(pval != 0) {
424             cur += pval;
425             if(cur < 0)
426                 cur = 0;
427             if(cur > 99)
428                 cur = 99;
429             display(cur);
430             trdelay = 99 - cur;
431             pval = 0;
432         }
433         if(sstate == 2) {
434             tron = !tron;
435             sstate = 0;
436         }
437         if(tron)
438             dsp[1] |= SEGP;
439         else
440             dsp[1] &= ~SEGP;
441 #endif
442 #if 0
443         /*
444           Pulse counter display
445         */
446         cur += pval;
447         pval = 0;
448         if(sstate == 2) {
449             cur = stime;
450             sstate = 0;
451         }
452         if(cur > 99)
453             cur = 99;
454         if(cur < -99)
455             cur = -99;
456         if(cur < 0) {
457             display(-cur);
458             dsp[0] |= SEGP;
459         } else {
460             display(cur);
461         }
462         if(PINB & 4)
463             dsp[1] |= SEGP;
464 #endif
465     }
466 }
467
468 ISR(SIG_INTERRUPT0)
469 {
470     ztime = 0;
471     zok = 1;
472 }
473
474 ISR(SIG_INTERRUPT1)
475 {
476     unsigned long now;
477     
478     now = getticks2();
479     if(tstate == 0) {
480         tstate = 1;
481         if(tlock != 2)
482             ttime = now - tstart;
483         tstart = now;
484         PORTD |= 2;
485         tlock = 1;
486     }
487 }
488
489 ISR(SIG_OUTPUT_COMPARE0A)
490 {
491     if(trstate == 0) {
492         ztime++;
493         if(tron && (ztime >= trdelay)) {
494             PORTD |= 1;
495             trstate = 1;
496             trtime = 0;
497         }
498     } else if(trstate == 1) {
499         trtime++;
500         if(trtime >= 5) {
501             PORTD &= ~1;
502             trstate = 0;
503         }
504     }
505 }
506
507 ISR(SIG_OUTPUT_COMPARE2A)
508 {
509     ledcycle();
510 }
511
512 ISR(SIG_OVERFLOW1)
513 {
514     oticks++;
515 }
516
517 ISR(SIG_PIN_CHANGE0)
518 {
519     if((sstate == 0) && !(PINB & 4)) {
520         stime = oticks;
521         sstate = 1;
522     }
523     if((sstate == 1) && (PINB & 4)) {
524         stime = oticks - stime;
525         sstate = 2;
526     }
527     if(pstate == 0) {
528         if((PINB & 2) == 0) {
529             pstate = 1;
530         } else if((PINB & 1) == 0) {
531             pstate = 2;
532         }
533     } else if(pstate == 1) {
534         if((PINB & 1) == 0) {
535             pval++;
536             pstate = 3;
537         } else {
538             pstate = 0;
539         }
540     } else if(pstate == 2) {
541         if((PINB & 2) == 0) {
542             pval--;
543             pstate = 3;
544         } else {
545             pstate = 0;
546         }
547     } else {
548         if((PINB & 2) && (PINB & 1))
549             pstate = 0;
550     }
551 }