added debug to timer0
[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     if(digitalRead(HEARTBEAT_PIN))
137       digitalWrite(HEARTBEAT_PIN, LOW);
138     else
139       digitalWrite(HEARTBEAT_PIN, HIGH);
140   }
141   debounce_cnt = DEBOUNCE_DELAY;
142 }
143
144 boolean manual_open()
145 {
146   if(manual_open_pressed()) {
147     if(debounce_state & DEBOUNCE_CLOSE) {
148       stop_debounce_timer();
149       debounce_state = DEBOUNCE_IDLE;
150       return false;
151     }
152
153     if(debounce_state == DEBOUNCE_IDLE) {
154       debounce_state = DEBOUNCE_OPEN;
155       start_debounce_timer();
156     }
157     else if(debounce_state & DEBOUNCE_FINISHED) {
158       stop_debounce_timer();
159       debounce_state = DEBOUNCE_IDLE;
160       return true;
161     }
162   }
163   else {
164     stop_debounce_timer();
165     debounce_state = DEBOUNCE_IDLE;
166   }
167
168   return false;
169 }
170
171 boolean manual_close()
172 {
173   if(manual_close_pressed()) {
174     if(debounce_state & DEBOUNCE_OPEN) {
175       stop_debounce_timer();
176       debounce_state = DEBOUNCE_IDLE;
177       return false;
178     }
179
180     if(debounce_state == DEBOUNCE_IDLE) {
181       debounce_state = DEBOUNCE_CLOSE;
182       start_debounce_timer();
183     }
184     else if(debounce_state & DEBOUNCE_FINISHED) {
185       stop_debounce_timer();
186       debounce_state = DEBOUNCE_IDLE;
187       return true;
188     }
189   }
190   else {
191     stop_debounce_timer();
192     debounce_state = DEBOUNCE_IDLE;
193   }
194
195   return false;
196 }
197
198 //********************************************************************//
199
200 void reset_stepper()
201 {
202   next_step = 0;
203   PORTB = STEPPER_OFF;
204   timeout_cnt = 0;
205 }
206
207 void init_stepper()
208 {
209   DDRB = 0x0F; // set PortB 3:0 as output
210   reset_stepper();
211 }
212
213 byte step_table(byte step)
214 {
215   switch(step) { // 0011 xxxx, manual keys pull-ups stay active
216   case 0: return 0x33;
217   case 1: return 0x36;
218   case 2: return 0x3C;
219   case 3: return 0x39;
220   }
221   return STEPPER_OFF;
222 }
223
224 //**********//
225
226 void reset_leds()
227 {
228   led_delay_cnt = 0;
229   next_led = 0;
230   PORTD = LEDS_OFF;
231   digitalWrite(LEDS_GREEN_COMMON_PIN, HIGH);
232   digitalWrite(LEDS_RED_COMMON_PIN, HIGH);
233 }
234
235 void init_leds()
236 {
237   DDRD = 0xFC;
238   pinMode(LEDS_GREEN_COMMON_PIN, OUTPUT);
239   pinMode(LEDS_RED_COMMON_PIN, OUTPUT);
240   reset_leds();
241 }
242
243 byte led_table(byte led)
244 {
245   switch(led) {  // xxxx xx00, leave RxD and TxD to 0
246   case 0: return 0x04;
247   case 1: return 0x08;
248   case 2: return 0x10;
249   case 3: return 0x20;
250   case 4: return 0x40;
251   case 5: return 0x80; 
252   }
253   return LEDS_OFF;
254 }
255
256 void leds_green()
257 {
258   digitalWrite(LEDS_GREEN_COMMON_PIN, LOW);
259   digitalWrite(LEDS_RED_COMMON_PIN, HIGH);
260 }
261
262 void leds_red()
263 {
264   digitalWrite(LEDS_GREEN_COMMON_PIN, HIGH);
265   digitalWrite(LEDS_RED_COMMON_PIN, LOW);
266 }
267
268 void leds_toggle()
269 {
270   if(digitalRead(LEDS_GREEN_COMMON_PIN) == HIGH) {
271     digitalWrite(LEDS_GREEN_COMMON_PIN, LOW);
272     digitalWrite(LEDS_RED_COMMON_PIN, HIGH);
273   }
274   else {
275     digitalWrite(LEDS_GREEN_COMMON_PIN, HIGH);
276     digitalWrite(LEDS_RED_COMMON_PIN, LOW);
277   }
278 }
279
280 //**********//
281
282 void start_step_timer()
283 {
284   // timer 1: 2 ms, between stepper output state changes
285   TCCR1A = 0;                    // prescaler 1:256, WGM = 4 (CTC)
286   TCCR1B = 1<<WGM12 | 1<<CS12;   // 
287   OCR1A = 124;        // (1+124)*256 = 32000 -> 2 ms @ 16 MHz
288   TCNT1 = 0;          // reseting timer
289   TIMSK1 = 1<<OCIE1A; // enable Interrupt
290 }
291
292 void start_wait_timer()
293 {
294   // timer1: 250 ms, minimal delay between subsequent open/close
295   TCCR1A = 0;         // prescaler 1:256, WGM = 0 (normal)
296   TCCR1B = 1<<CS12;   // 
297   OCR1A = 15624;      // (1+15624)*256 = 4000000 -> 250 ms @ 16 MHz
298   TCNT1 = 0;          // reseting timer
299   TIMSK1 = 1<<OCIE1A; // enable Interrupt
300 }
301
302 void start_error_timer()
303 {
304   // timer1: 500 ms, blinking leds with 1 Hz
305   TCCR1A = 0;                  // prescaler 1:256, WGM = 4 (CTC)
306   TCCR1B = 1<<WGM12 | 1<<CS12; // 
307   OCR1A = 31249;      // (1+31249)*256 = 8000000 -> 500 ms @ 16 MHz
308   TCNT1 = 0;          // reseting timer
309   TIMSK1 = 1<<OCIE1A; // enable Interrupt
310 }
311
312 void stop_timer() // stop the timer
313 {
314   // timer1
315   TCCR1B = 0; // no clock source
316   TIMSK1 = 0; // disable timer interrupt
317 }
318
319 ISR(TIMER1_COMPA_vect)
320 {
321       // check if limit switch is active
322   if((current_state == OPENING && is_opened()) ||
323      (current_state == CLOSING && is_closed())) 
324   {
325     stop_timer();
326     reset_leds();
327     if(current_state == OPENING)
328       leds_green();
329     else
330       leds_red();
331     PORTD = LEDS_ON;
332     current_state = WAIT;
333     start_wait_timer();
334     return;
335   }
336       
337   if(current_state == OPENING || current_state == CLOSING) {
338     timeout_cnt++;
339     if(timeout_cnt >= MOVING_TIMEOUT) {
340       reset_stepper();
341       stop_timer();
342       current_state = ERROR;
343       Serial.println("Error: open/close took too long!");
344       start_error_timer();
345       leds_green();
346       PORTD = LEDS_ON;
347     }
348   }
349
350   if(current_state == OPENING) { // next step (open)
351     PORTB = step_table(next_step);
352     next_step++;
353     if(next_step >= 4)
354       next_step = 0;
355   }
356   else if(current_state == CLOSING) { // next step (close)
357     PORTB = step_table(next_step);
358     if(next_step == 0)
359       next_step = 3;
360     else
361       next_step--;
362   }
363   else if(current_state == WAIT) { // wait after last open/close finished -> idle
364     stop_timer();
365     reset_stepper();
366     current_state = IDLE;
367     return;
368   }
369   else if(current_state == ERROR) {
370     leds_toggle();
371     return;
372   }
373   else { // timer is useless stop it
374     stop_timer();
375     return;
376   }
377
378   led_delay_cnt++;
379   if(led_delay_cnt >= LED_DELAY) {
380     led_delay_cnt = 0;    
381
382     PORTD = led_table(next_led);
383
384     if(current_state == OPENING) {
385       if(next_led == 0)
386         next_led = 5;
387       else
388         next_led--;
389     }
390     else if(current_state == CLOSING) {
391       next_led++;
392       if(next_led >= 6)
393         next_led = 0;
394     }
395   }
396 }
397
398 //********************************************************************//
399
400 void reset_heartbeat()
401 {
402   digitalWrite(HEARTBEAT_PIN, HIGH);
403   heartbeat_cnt = 0;
404 }
405
406 void heartbeat_on()
407 {
408   digitalWrite(HEARTBEAT_PIN, LOW);
409 }
410
411 void heartbeat_off()
412 {
413   digitalWrite(HEARTBEAT_PIN, HIGH);
414 }
415
416 void init_heartbeat()
417 {
418   pinMode(HEARTBEAT_PIN, OUTPUT);
419   reset_heartbeat();
420   // timer 2: ~10 ms, timebase for heartbeat signal
421 /*
422   TCCR2A = 1<<WGM21;                    // prescaler 1:1024, WGM = 2 (CTC)
423   TCCR2B = 1<<CS22 | 1<<CS21 | 1<<CS20; // 
424   OCR2A = 155;        // (1+155)*1024 = 159744 -> ~10 ms @ 16 MHz
425   TCNT2 = 0;          // reseting timer
426   TIMSK2 = 1<<OCIE2A; // enable Interrupt
427 */
428   heartbeat_on();
429 }
430
431 // while running this gets called every ~10ms
432 ISR(TIMER2_COMPA_vect)
433 {
434   heartbeat_cnt++;
435   if(heartbeat_cnt == HEARTBEAT_DURATION)
436     heartbeat_off();
437   else if(heartbeat_cnt >= HEARTBEAT_DELAY) {
438     heartbeat_on();
439     heartbeat_cnt = 0;
440   }
441 }
442
443 //********************************************************************//
444
445 void reset_after_error()
446 {
447   stop_timer();
448   reset_leds();
449
450   leds_red();
451   if(is_closed()) {
452     current_state = IDLE;
453     PORTD = LEDS_ON;
454   }
455   else {
456     current_state = CLOSING;
457     start_step_timer();
458   }
459   Serial.println("Ok, closing now");
460 }
461
462 void start_open()
463 {
464   if(is_opened()) {
465     Serial.println("Already open");
466     return;
467   }
468
469   reset_stepper();
470   reset_leds();
471   leds_green();
472   current_state = OPENING;
473   start_step_timer();
474   Serial.println("Ok");
475 }
476
477 void start_close()
478 {
479   if(is_closed()) {
480     Serial.println("Already closed");
481     return;
482   }
483     
484   reset_stepper();
485   reset_leds();
486   leds_red();
487   current_state = CLOSING;
488   start_step_timer();
489   Serial.println("Ok");
490 }
491
492 void print_status()
493 {
494   Serial.print("Status: ");
495   if(is_opened())
496     Serial.print("opened");
497   else if(is_closed())
498     Serial.print("closed");
499   else
500     Serial.print("<->");
501
502   switch(current_state) {
503   case IDLE: Serial.println(", idle"); break;
504   case OPENING: Serial.println(", opening"); break;
505   case CLOSING: Serial.println(", closing"); break;
506   case WAIT: Serial.println(", waiting"); break;
507   default: Serial.println(", <undefined state>"); break;
508   }
509   
510 }
511
512 //**********//
513
514 void setup()
515 {
516   init_limits();
517   init_stepper();
518   init_leds();
519   init_heartbeat();
520
521   Serial.begin(9600);
522
523   current_state = IDLE;
524
525       // make sure door is locked after reset
526   leds_red();
527   if(is_closed())
528     PORTD = LEDS_ON;
529   else {
530     current_state = CLOSING;
531     start_step_timer();
532   }
533 }
534
535 void loop()
536 {
537   if(Serial.available()) {
538     char command = Serial.read();
539
540     if(current_state == ERROR && command != CMD_RESET) {
541       Serial.println("Error: last open/close operation took to long!");
542     }
543     else if (command == CMD_RESET) {
544       reset_after_error();
545     }
546     else if (command == CMD_OPEN) {
547       if(current_state == IDLE) 
548         start_open();
549       else
550         Serial.println("Error: Operation in progress");
551     }
552     else if (command == CMD_CLOSE) {
553       if(current_state == IDLE) 
554         start_close();
555       else
556         Serial.println("Error: Operation in progress");
557     }
558     else if (command == CMD_STATUS)
559       print_status();
560     else
561       Serial.println("Error: unknown command");
562   }
563   if(manual_open() && !is_opened() && current_state == IDLE) {
564     Serial.println("open forced manually");
565     start_open();
566   }
567   if(manual_close() && !is_closed() && current_state == IDLE) {
568     Serial.println("close forced manually");
569     start_close();
570   }
571   if (current_state == IDLE) {
572     if(is_opened()) {
573       leds_green();
574       PORTD = LEDS_ON;
575     }
576     if(is_closed()) {
577       leds_red();
578       PORTD = LEDS_ON;
579     }
580   }
581 }