Merge branch 'master' of git.dolda2000.com:/srv/git/r/kokare
[kokare.git] / kokare.c
... / ...
CommitLineData
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
15uint8_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 */
34uint8_t dsp[2] = {0, 0};
35char leda = 0;
36char ledc = 0;
37/* Timer */
38volatile int oticks = 0;
39unsigned long mnow;
40/* Pulse counter */
41volatile char pstate = 0;
42char pval = 0;
43/* Switch */
44volatile char sstate = 0;
45int stime = 0;
46/* Temp sensor */
47volatile char tstate = 0;
48volatile char tlock = 0;
49unsigned long tstart;
50unsigned long ttime;
51unsigned long ttimea = 10000;
52char tavgok = 0;
53/* Conversion loop */
54int tempk;
55volatile ktok = 0;
56/* Zero-cross detector*/
57volatile char zok = 0;
58volatile char ztime = 0;
59/* Triac */
60char trstate = 0;
61char tron = 0;
62volatile char trtime;
63volatile char trdelay = 0;
64
65void 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
114unsigned 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
138void display(char num)
139{
140 dsp[0] = font[(num / 10) % 10];
141 dsp[1] = font[num % 10];
142}
143
144void disphex(unsigned char num)
145{
146 dsp[0] = font[(num & 0xf0) >> 4];
147 dsp[1] = font[num & 0x0f];
148}
149
150unsigned 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
164void 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
189void 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
210void 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
220void 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
264int main(void)
265{
266 int state, cur, run;
267 unsigned long utime;
268
269 state = 0;
270 cur = 100;
271 run = 0;
272 init();
273 sei();
274 display(0);
275
276 while(1) {
277 mnow = getticks();
278 tempcycle();
279 calcavg();
280 convcycle();
281
282#if 1
283 /*
284 * User interface
285 */
286 if(state == 0) {
287 /* Display temperature */
288 if(ktok) {
289 ktok = 0;
290 if((tempk >= 273) && (tempk <= 372)) {
291 display(tempk - 273);
292 } else {
293 dsp[0] = dsp[1] = SEGG;
294 }
295 }
296 if(pval != 0)
297 state = 1;
298 if(sstate == 2) {
299 sstate = 0;
300 if(stime > 10)
301 state = 2;
302 else
303 run = !run;
304 }
305 if(run)
306 dsp[1] |= SEGP;
307 else
308 dsp[1] &= ~SEGP;
309 } else if(state == 1) {
310 /* Temp setting */
311 if(pval != 0) {
312 cur += pval;
313 pval = 0;
314 if(cur < 0)
315 cur = 0;
316 if(cur > 100)
317 cur = 100;
318 if(cur < 100)
319 display(cur);
320 else
321 dsp[0] = dsp[1] = SEGG;
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);
334 dsp[0] |= SEGP;
335 if(ttimea >= 10000)
336 dsp[1] |= SEGP;
337 } else {
338 display(ttimea / 1000);
339 }
340 if(sstate == 2) {
341 state = 0;
342 sstate = 0;
343 }
344 }
345 /*
346 * Set Triac to match temperature
347 */
348 if(run) {
349 if(tempk - 273 < cur) {
350 tron = 1;
351 if(cur - (tempk - 273) > 5) {
352 /* For some reason, the Triac currently doesn't
353 * trigger on one of the AC half-cycles below 0.7
354 * ms. */
355 trdelay = 7;
356 } else if(cur - (tempk - 273) >= 3) {
357 trdelay = 20;
358 } else if(cur - (tempk - 273) >= 2) {
359 trdelay = 40;
360 } else {
361 trdelay = 70;
362 }
363 } else {
364 tron = 0;
365 }
366 } else {
367 tron = 0;
368 }
369#endif
370 /*
371 dsp[0] = bindisp((ttimea & 0xff00) >> 8);
372 dsp[1] = bindisp(ttimea & 0x00ff);
373 */
374 /*
375 disphex((ttimea & 0xff000) >> 12);
376 */
377#if 0
378 /*
379 Temp display
380 */
381 if(ttimea < 20000) {
382 display((ttimea / 100) % 100);
383 dsp[0] |= SEGP;
384 if(ttimea >= 10000)
385 dsp[1] |= SEGP;
386 } else {
387 display(ttimea / 1000);
388 }
389#endif
390#if 0
391 /*
392 * ZVD debug
393 */
394 if(zok) {
395 if(++cur > 99)
396 cur = 0;
397 display(cur);
398 zok = 0;
399 }
400#endif
401#if 0
402 /*
403 Phony Triac control
404 */
405 if(pval != 0) {
406 cur += pval;
407 if(cur < 0)
408 cur = 0;
409 if(cur > 99)
410 cur = 99;
411 display(cur);
412 trdelay = 99 - cur;
413 pval = 0;
414 }
415 if(sstate == 2) {
416 tron = !tron;
417 sstate = 0;
418 }
419 if(tron)
420 dsp[1] |= SEGP;
421 else
422 dsp[1] &= ~SEGP;
423#endif
424#if 0
425 /*
426 Pulse counter display
427 */
428 cur += pval;
429 pval = 0;
430 if(sstate == 2) {
431 cur = stime;
432 sstate = 0;
433 }
434 if(cur > 99)
435 cur = 99;
436 if(cur < -99)
437 cur = -99;
438 if(cur < 0) {
439 display(-cur);
440 dsp[0] |= SEGP;
441 } else {
442 display(cur);
443 }
444 if(PINB & 4)
445 dsp[1] |= SEGP;
446#endif
447 }
448}
449
450ISR(SIG_INTERRUPT0)
451{
452 ztime = 0;
453 zok = 1;
454}
455
456ISR(SIG_INTERRUPT1)
457{
458 unsigned long now;
459
460 now = getticks();
461 if(tstate == 0) {
462 tstate = 1;
463 if(tlock != 2)
464 ttime = now - tstart;
465 tstart = now;
466 PORTD |= 2;
467 tlock = 1;
468 }
469}
470
471ISR(SIG_OUTPUT_COMPARE0A)
472{
473 if(trstate == 0) {
474 ztime++;
475 if(tron && (ztime >= trdelay)) {
476 PORTD |= 1;
477 trstate = 1;
478 trtime = 0;
479 }
480 } else if(trstate == 1) {
481 trtime++;
482 if(trtime >= 5) {
483 PORTD &= ~1;
484 trstate = 0;
485 }
486 }
487}
488
489ISR(SIG_OUTPUT_COMPARE2A)
490{
491 ledcycle();
492}
493
494ISR(SIG_OVERFLOW1)
495{
496 oticks++;
497}
498
499ISR(SIG_PIN_CHANGE0)
500{
501 if((sstate == 0) && !(PINB & 4)) {
502 stime = oticks;
503 sstate = 1;
504 }
505 if((sstate == 1) && (PINB & 4)) {
506 stime = oticks - stime;
507 sstate = 2;
508 }
509 if(pstate == 0) {
510 if((PINB & 2) == 0) {
511 pstate = 1;
512 } else if((PINB & 1) == 0) {
513 pstate = 2;
514 }
515 } else if(pstate == 1) {
516 if((PINB & 1) == 0) {
517 pval++;
518 pstate = 3;
519 } else {
520 pstate = 0;
521 }
522 } else if(pstate == 2) {
523 if((PINB & 2) == 0) {
524 pval--;
525 pstate = 3;
526 } else {
527 pstate = 0;
528 }
529 } else {
530 if((PINB & 2) && (PINB & 1))
531 pstate = 0;
532 }
533}