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