fixed timer0 bug
[svn42.git] / firmware / tuer.pde
1 #include <avr/io.h>
2 #include <avr/interrupt.h>
3
4 //********************************************************************//
5
6 #define HEARTBEAT_PIN 15 // blinking led indicating that system is active
7 #define HEARTBEAT_DURATION 10 // *10 ms, duration of heartbeat pulse
8 #define HEARTBEAT_DELAY 200   // *10 ms, 1/heartbeat-frequency
9 int heartbeat_cnt = 0;                    
10         
11 #define LEDS_ON 0xFC
12 #define LEDS_OFF 0x00
13
14 #define LEDS_GREEN_COMMON_PIN 16
15 #define LEDS_RED_COMMON_PIN 17 
16 #define LED_DELAY 50 // *2 ms, between led shifts
17 int led_delay_cnt = 0;
18 byte next_led = 0;
19
20 #define LIMIT_OPENED_PIN 18 // A4: limit switch for open
21 #define LIMIT_CLOSED_PIN 19 // A5: limit switch for close
22
23 #define MANUAL_OPEN_PIN 12  // keys for manual open and close
24 #define MANUAL_CLOSE_PIN 13 // 
25 #define DEBOUNCE_DELAY 625  // * 16us = 10ms
26 #define DEBOUNCE_IDLE 0     // currently no debouncing
27 #define DEBOUNCE_OPEN 1     // debouncing open key
28 #define DEBOUNCE_CLOSE 2    // debouncing close key
29 #define DEBOUNCE_FINISHED 4 // debouncing finished
30 byte debounce_state;
31 int debounce_cnt = 0;
32
33 #define IDLE 0      // close and open may be called
34 #define OPENING 1   // opening, only 's' command is allowed
35 #define CLOSING 2   // closing, onyl 's' command is allowed
36 #define WAIT 3      // wait some time after open or close and hold last step
37 #define ERROR 4     // an error occured
38
39 #define CMD_OPEN 'o'
40 #define CMD_CLOSE 'c'
41 #define CMD_STATUS 's'
42 #define CMD_RESET 'r'
43
44 #define STEPPER_OFF 0x30
45 byte current_state = IDLE;  // current state of internal state machine
46 byte next_step = 0;         // step counter 0 .. 3
47 #define MOVING_TIMEOUT 1600 // *2 ms, in case limit switches don't work stop and report an error
48 int timeout_cnt = 0;        // counts up to MOVING_TIMEOUT
49
50 //********************************************************************//
51
52 void init_limits()
53 {
54   pinMode(LIMIT_OPENED_PIN, INPUT);      // set pin to input
55   digitalWrite(LIMIT_OPENED_PIN, HIGH);  // turn on pullup resistors  
56
57   pinMode(LIMIT_CLOSED_PIN, INPUT);      // set pin to input
58   digitalWrite(LIMIT_CLOSED_PIN, HIGH);  // turn on pullup resistors  
59 }
60
61 boolean is_opened()
62 {
63   if(digitalRead(LIMIT_OPENED_PIN))
64      return false;
65      
66   return true;
67 }
68
69 boolean is_closed()
70 {
71   if(digitalRead(LIMIT_CLOSED_PIN))
72      return false;
73      
74   return true;
75 }
76
77 //**********//
78
79 void init_manual()
80 {
81   pinMode(MANUAL_OPEN_PIN, INPUT);      // set pin to input
82   digitalWrite(MANUAL_OPEN_PIN, HIGH);  // turn on pullup resistors  
83
84   pinMode(MANUAL_CLOSE_PIN, INPUT);     // set pin to input
85   digitalWrite(MANUAL_CLOSE_PIN, HIGH); // turn on pullup resistors  
86
87   debounce_state = DEBOUNCE_IDLE;
88   debounce_cnt = DEBOUNCE_DELAY;
89 }
90
91 boolean manual_open_pressed()
92 {
93   if(digitalRead(MANUAL_OPEN_PIN))
94      return false;
95      
96   return true;
97 }
98
99 boolean manual_close_pressed()
100 {
101   if(digitalRead(MANUAL_CLOSE_PIN))
102      return false;
103      
104   return true;
105 }
106
107 void start_debounce_timer()  // this breaks millis() function, but who cares
108 {
109   debounce_cnt = DEBOUNCE_DELAY;
110
111   TCCR0A = 0;         // no prescaler, WGM = 0 (normal)
112   TCCR0B = 1<<CS00;   // 
113   OCR0A = 255;        // 1+255 = 256 -> 16us @ 16 MHz
114   TCNT0 = 0;          // reseting timer
115   TIMSK0 = 1<<OCF0A;  // enable Interrupt
116
117 }
118
119 void stop_debounce_timer()
120 {
121   // timer0
122   TCCR0B = 0; // no clock source
123   TIMSK0 = 0; // disable timer interrupt
124 }
125
126 ISR(TIMER0_COMPA_vect)
127 {
128   if((debounce_state & DEBOUNCE_OPEN && manual_open_pressed()) ||
129      (debounce_state & DEBOUNCE_CLOSE && manual_close_pressed())) {
130     if(debounce_cnt) {
131       debounce_cnt--;
132       return;
133     }
134     debounce_state |= DEBOUNCE_FINISHED;
135   }
136   debounce_cnt = DEBOUNCE_DELAY;
137 }
138
139 boolean manual_open()
140 {
141   if(manual_open_pressed()) {
142     if(debounce_state & DEBOUNCE_CLOSE) {
143       stop_debounce_timer();
144       debounce_state = DEBOUNCE_IDLE;
145       return false;
146     }
147
148     if(debounce_state == DEBOUNCE_IDLE) {
149       debounce_state = DEBOUNCE_OPEN;
150       start_debounce_timer();
151     }
152     else if(debounce_state & DEBOUNCE_FINISHED) {
153       stop_debounce_timer();
154       debounce_state = DEBOUNCE_IDLE;
155       return true;
156     }
157   }
158   else {
159     stop_debounce_timer();
160     debounce_state = DEBOUNCE_IDLE;
161   }
162
163   return false;
164 }
165
166 boolean manual_close()
167 {
168   if(manual_close_pressed()) {
169     if(debounce_state & DEBOUNCE_OPEN) {
170       stop_debounce_timer();
171       debounce_state = DEBOUNCE_IDLE;
172       return false;
173     }
174
175     if(debounce_state == DEBOUNCE_IDLE) {
176       debounce_state = DEBOUNCE_CLOSE;
177       start_debounce_timer();
178     }
179     else if(debounce_state & DEBOUNCE_FINISHED) {
180       stop_debounce_timer();
181       debounce_state = DEBOUNCE_IDLE;
182       return true;
183     }
184   }
185   else {
186     stop_debounce_timer();
187     debounce_state = DEBOUNCE_IDLE;
188   }
189
190   return false;
191 }
192
193 //********************************************************************//
194
195 void reset_stepper()
196 {
197   next_step = 0;
198   PORTB = STEPPER_OFF;
199   timeout_cnt = 0;
200 }
201
202 void init_stepper()
203 {
204   DDRB = 0x0F; // set PortB 3:0 as output
205   reset_stepper();
206 }
207
208 byte step_table(byte step)
209 {
210   switch(step) { // 0011 xxxx, manual keys pull-ups stay active
211   case 0: return 0x33;
212   case 1: return 0x36;
213   case 2: return 0x3C;
214   case 3: return 0x39;
215   }
216   return STEPPER_OFF;
217 }
218
219 //**********//
220
221 void reset_leds()
222 {
223   led_delay_cnt = 0;
224   next_led = 0;
225   PORTD = LEDS_OFF;
226   digitalWrite(LEDS_GREEN_COMMON_PIN, HIGH);
227   digitalWrite(LEDS_RED_COMMON_PIN, HIGH);
228 }
229
230 void init_leds()
231 {
232   DDRD = 0xFC;
233   pinMode(LEDS_GREEN_COMMON_PIN, OUTPUT);
234   pinMode(LEDS_RED_COMMON_PIN, OUTPUT);
235   reset_leds();
236 }
237
238 byte led_table(byte led)
239 {
240   switch(led) {  // xxxx xx00, leave RxD and TxD to 0
241   case 0: return 0x04;
242   case 1: return 0x08;
243   case 2: return 0x10;
244   case 3: return 0x20;
245   case 4: return 0x40;
246   case 5: return 0x80; 
247   }
248   return LEDS_OFF;
249 }
250
251 void leds_green()
252 {
253   digitalWrite(LEDS_GREEN_COMMON_PIN, LOW);
254   digitalWrite(LEDS_RED_COMMON_PIN, HIGH);
255 }
256
257 void leds_red()
258 {
259   digitalWrite(LEDS_GREEN_COMMON_PIN, HIGH);
260   digitalWrite(LEDS_RED_COMMON_PIN, LOW);
261 }
262
263 void leds_toggle()
264 {
265   if(digitalRead(LEDS_GREEN_COMMON_PIN) == HIGH) {
266     digitalWrite(LEDS_GREEN_COMMON_PIN, LOW);
267     digitalWrite(LEDS_RED_COMMON_PIN, HIGH);
268   }
269   else {
270     digitalWrite(LEDS_GREEN_COMMON_PIN, HIGH);
271     digitalWrite(LEDS_RED_COMMON_PIN, LOW);
272   }
273 }
274
275 //**********//
276
277 void start_step_timer()
278 {
279   // timer 1: 2 ms, between stepper output state changes
280   TCCR1A = 0;                    // prescaler 1:256, WGM = 4 (CTC)
281   TCCR1B = 1<<WGM12 | 1<<CS12;   // 
282   OCR1A = 124;        // (1+124)*256 = 32000 -> 2 ms @ 16 MHz
283   TCNT1 = 0;          // reseting timer
284   TIMSK1 = 1<<OCIE1A; // enable Interrupt
285 }
286
287 void start_wait_timer()
288 {
289   // timer1: 250 ms, minimal delay between subsequent open/close
290   TCCR1A = 0;         // prescaler 1:256, WGM = 0 (normal)
291   TCCR1B = 1<<CS12;   // 
292   OCR1A = 15624;      // (1+15624)*256 = 4000000 -> 250 ms @ 16 MHz
293   TCNT1 = 0;          // reseting timer
294   TIMSK1 = 1<<OCIE1A; // enable Interrupt
295 }
296
297 void start_error_timer()
298 {
299   // timer1: 500 ms, blinking leds with 1 Hz
300   TCCR1A = 0;                  // prescaler 1:256, WGM = 4 (CTC)
301   TCCR1B = 1<<WGM12 | 1<<CS12; // 
302   OCR1A = 31249;      // (1+31249)*256 = 8000000 -> 500 ms @ 16 MHz
303   TCNT1 = 0;          // reseting timer
304   TIMSK1 = 1<<OCIE1A; // enable Interrupt
305 }
306
307 void stop_timer() // stop the timer
308 {
309   // timer1
310   TCCR1B = 0; // no clock source
311   TIMSK1 = 0; // disable timer interrupt
312 }
313
314 ISR(TIMER1_COMPA_vect)
315 {
316       // check if limit switch is active
317   if((current_state == OPENING && is_opened()) ||
318      (current_state == CLOSING && is_closed())) 
319   {
320     stop_timer();
321     reset_leds();
322     if(current_state == OPENING)
323       leds_green();
324     else
325       leds_red();
326     PORTD = LEDS_ON;
327     current_state = WAIT;
328     start_wait_timer();
329     return;
330   }
331       
332   if(current_state == OPENING || current_state == CLOSING) {
333     timeout_cnt++;
334     if(timeout_cnt >= MOVING_TIMEOUT) {
335       reset_stepper();
336       stop_timer();
337       current_state = ERROR;
338       Serial.println("Error: open/close took too long!");
339       start_error_timer();
340       leds_green();
341       PORTD = LEDS_ON;
342     }
343   }
344
345   if(current_state == OPENING) { // next step (open)
346     PORTB = step_table(next_step);
347     next_step++;
348     if(next_step >= 4)
349       next_step = 0;
350   }
351   else if(current_state == CLOSING) { // next step (close)
352     PORTB = step_table(next_step);
353     if(next_step == 0)
354       next_step = 3;
355     else
356       next_step--;
357   }
358   else if(current_state == WAIT) { // wait after last open/close finished -> idle
359     stop_timer();
360     reset_stepper();
361     current_state = IDLE;
362     return;
363   }
364   else if(current_state == ERROR) {
365     leds_toggle();
366     return;
367   }
368   else { // timer is useless stop it
369     stop_timer();
370     return;
371   }
372
373   led_delay_cnt++;
374   if(led_delay_cnt >= LED_DELAY) {
375     led_delay_cnt = 0;    
376
377     PORTD = led_table(next_led);
378
379     if(current_state == OPENING) {
380       if(next_led == 0)
381         next_led = 5;
382       else
383         next_led--;
384     }
385     else if(current_state == CLOSING) {
386       next_led++;
387       if(next_led >= 6)
388         next_led = 0;
389     }
390   }
391 }
392
393 //********************************************************************//
394
395 void reset_heartbeat()
396 {
397   digitalWrite(HEARTBEAT_PIN, HIGH);
398   heartbeat_cnt = 0;
399 }
400
401 void heartbeat_on()
402 {
403   digitalWrite(HEARTBEAT_PIN, LOW);
404 }
405
406 void heartbeat_off()
407 {
408   digitalWrite(HEARTBEAT_PIN, HIGH);
409 }
410
411 void init_heartbeat()
412 {
413   pinMode(HEARTBEAT_PIN, OUTPUT);
414   reset_heartbeat();
415   // timer 2: ~10 ms, timebase for heartbeat signal
416   TCCR2A = 1<<WGM21;                    // prescaler 1:1024, WGM = 2 (CTC)
417   TCCR2B = 1<<CS22 | 1<<CS21 | 1<<CS20; // 
418   OCR2A = 155;        // (1+155)*1024 = 159744 -> ~10 ms @ 16 MHz
419   TCNT2 = 0;          // reseting timer
420   TIMSK2 = 1<<OCIE2A; // enable Interrupt
421   heartbeat_on();
422 }
423
424 // while running this gets called every ~10ms
425 ISR(TIMER2_COMPA_vect)
426 {
427   heartbeat_cnt++;
428   if(heartbeat_cnt == HEARTBEAT_DURATION)
429     heartbeat_off();
430   else if(heartbeat_cnt >= HEARTBEAT_DELAY) {
431     heartbeat_on();
432     heartbeat_cnt = 0;
433   }
434 }
435
436 //********************************************************************//
437
438 void reset_after_error()
439 {
440   stop_timer();
441   reset_leds();
442
443   leds_red();
444   if(is_closed()) {
445     current_state = IDLE;
446     PORTD = LEDS_ON;
447   }
448   else {
449     current_state = CLOSING;
450     start_step_timer();
451   }
452   Serial.println("Ok, closing now");
453 }
454
455 void start_open()
456 {
457   if(is_opened()) {
458     Serial.println("Already open");
459     return;
460   }
461
462   reset_stepper();
463   reset_leds();
464   leds_green();
465   current_state = OPENING;
466   start_step_timer();
467   Serial.println("Ok");
468 }
469
470 void start_close()
471 {
472   if(is_closed()) {
473     Serial.println("Already closed");
474     return;
475   }
476     
477   reset_stepper();
478   reset_leds();
479   leds_red();
480   current_state = CLOSING;
481   start_step_timer();
482   Serial.println("Ok");
483 }
484
485 void print_status()
486 {
487   Serial.print("Status: ");
488   if(is_opened())
489     Serial.print("opened");
490   else if(is_closed())
491     Serial.print("closed");
492   else
493     Serial.print("<->");
494
495   switch(current_state) {
496   case IDLE: Serial.println(", idle"); break;
497   case OPENING: Serial.println(", opening"); break;
498   case CLOSING: Serial.println(", closing"); break;
499   case WAIT: Serial.println(", waiting"); break;
500   default: Serial.println(", <undefined state>"); break;
501   }
502   
503 }
504
505 //**********//
506
507 void setup()
508 {
509   init_limits();
510   init_stepper();
511   init_leds();
512   init_heartbeat();
513
514   Serial.begin(9600);
515
516   current_state = IDLE;
517
518       // make sure door is locked after reset
519   leds_red();
520   if(is_closed())
521     PORTD = LEDS_ON;
522   else {
523     current_state = CLOSING;
524     start_step_timer();
525   }
526 }
527
528 void loop()
529 {
530   if(Serial.available()) {
531     char command = Serial.read();
532
533     if(current_state == ERROR && command != CMD_RESET) {
534       Serial.println("Error: last open/close operation took to long!");
535     }
536     else if (command == CMD_RESET) {
537       reset_after_error();
538     }
539     else if (command == CMD_OPEN) {
540       if(current_state == IDLE) 
541         start_open();
542       else
543         Serial.println("Error: Operation in progress");
544     }
545     else if (command == CMD_CLOSE) {
546       if(current_state == IDLE) 
547         start_close();
548       else
549         Serial.println("Error: Operation in progress");
550     }
551     else if (command == CMD_STATUS)
552       print_status();
553     else
554       Serial.println("Error: unknown command");
555   }
556   if(manual_open() && !is_opened() && current_state == IDLE) {
557     Serial.println("open forced manually");
558     start_open();
559   }
560   if(manual_close() && !is_closed() && current_state == IDLE) {
561     Serial.println("close forced manually");
562     start_close();
563   }
564   if (current_state == IDLE) {
565     if(is_opened()) {
566       leds_green();
567       PORTD = LEDS_ON;
568     }
569     if(is_closed()) {
570       leds_red();
571       PORTD = LEDS_ON;
572     }
573   }
574 }