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