Rev Author Line No. Line
3047 miho 1 // ------------------------------------------------------------------
2 //
3 // TRAIN TIMER
4 // -----------
5 //
6 // Firmware for TRAIN02A board. It changes state of relay outputs
7 // as defined in configuration. Default configuration is in .h file
8 // and definitions may be changed online via USB port and if needed
9 // stored into EEPROM.
10 //
11 // Uses ATmega8 at 8MHz (internal RC or external XTAL)
12 //
13 // (c) miho WWW.MLAB.CZ/PermaLink/TRAIN
14 //
15 // ------------------------------------------------------------------
16 //
17 // History
18 //
19 // 1.00 First version
20  
21  
22 // Standard header files
23 #include <avr/io.h> // Device Specific Defines
24 #include <avr/interrupt.h> // For Timer and USART Interrupt
25 #include <stdlib.h> // For rand()
26 #include <avr/eeprom.h> // For EEPROM access (for configuration)
27 #include <stdio.h> // For printf() etc
28 #include <string.h> // For strcmp() etc
29 #include <avr/pgmspace.h> // Data in Program Memory (strings)
30  
31 #include "TRAIN.h" // Hardware Definitions
32 #include "TRAIN_TIMER.h" // Project Definitions - default configuration is here
33  
34  
35 // Set Relay Outputs
36 void SetOutput(const unsigned char Number, const unsigned char State)
37 {
38 // Check
39 if(Number>7)
40 return;
41  
42 // Set DDR (for output)
43 (*RE_DDR_Table[Number]) |= RE_BIT_MASK[Number];
44  
45 // Set/Clear data
46 if(State)
47 (*RE_PORT_Table[Number]) |= RE_BIT_MASK[Number];
48 else
49 (*RE_PORT_Table[Number]) &= ~RE_BIT_MASK[Number];
50 }
51  
52  
53 // Get Switch Input State
54 unsigned char GetInput(const unsigned char Number)
55 {
56 // Check
57 if(Number>7)
58 return 0;
59 // PullUp
60 (*SW_PORT_Table[Number]) |= SW_BIT_MASK[Number];
61 // Get Value
62 return ((*SW_PIN_Table[Number]) & SW_BIT_MASK[Number]) == 0 ? 1 : 0;
63 }
64  
65  
66 // EEPROM Data
67 unsigned int EEMEM EE_TimesOn[8] = DEFAULT_TIMES_ON ;
68 unsigned int EEMEM EE_TimesOff[8] = DEFAULT_TIMES_OFF ;
69 unsigned int EEMEM EE_TimesOnRnd[8] = DEFAULT_TIMES_ON_RND ;
70 unsigned int EEMEM EE_TimesOffRnd[8] = DEFAULT_TIMES_OFF_RND ;
71  
72  
73 // Runtime configuration
74 static volatile unsigned int TimesOn[8];
75 static volatile unsigned int TimesOff[8];
76 static volatile unsigned int TimesOnRnd[8];
77 static volatile unsigned int TimesOffRnd[8];
78  
79  
80 // Load configuration from EEPROM to RAM
81 void EE_Load()
82 {
83 // Copy data from EEPROM to RAM
84 eeprom_read_block(&TimesOn, &EE_TimesOn, sizeof(TimesOn) );
85 eeprom_read_block(&TimesOff, &EE_TimesOff, sizeof(TimesOff) );
86 eeprom_read_block(&TimesOnRnd, &EE_TimesOnRnd, sizeof(TimesOnRnd) );
87 eeprom_read_block(&TimesOffRnd, &EE_TimesOffRnd, sizeof(TimesOffRnd) );
88 }
89  
90  
91 // Store configuration to EEPROM from RAM
92 void EE_Store()
93 {
94 // Copy data from RAM to EEPROM
95 eeprom_write_block(&TimesOn, &EE_TimesOn, sizeof(TimesOn) );
96 eeprom_write_block(&TimesOff, &EE_TimesOff, sizeof(TimesOff) );
97 eeprom_write_block(&TimesOnRnd, &EE_TimesOnRnd, sizeof(TimesOnRnd) );
98 eeprom_write_block(&TimesOffRnd, &EE_TimesOffRnd, sizeof(TimesOffRnd) );
99 }
100  
101  
102 // Init Timer 1
103 void TimerInit()
104 {
105 TCCR1B |= (1<<WGM12); // Configure timer 1 for CTC mode
106 TIMSK |= (1<<OCIE1A); // Enable CTC interrupt (Output Compare)
107 OCR1A = F_CPU/F_TIME_GRAIN/8; // Peridic interrupt
108 TCCR1B |= (1 << CS11); // Start timer at Fcpu/8
109 }
110  
111  
112 // Global State of Realays
113 static volatile unsigned char RelayState[8];
114 static volatile unsigned int Timers[8];
115  
116  
117 // ISR - TIMER - 10ms
118 ISR(TIMER1_COMPA_vect)
119 {
120 // Time granularity to 1s counter
121 static volatile unsigned int Timer;
122  
123 Timer++;
124 if(Timer==F_TIME_GRAIN)
125 {
126 // 1s
127 for(unsigned char i=0; i<8; i++)
128 {
129 // Decrement all timers
130 if(Timers[i])
131 {
132 Timers[i]--;
133 }
134 else
135 {
136 // Switch state
137 if(RelayState[i])
138 {
139 Timers[i] = TimesOff[i]-1 + (rand()%(TimesOffRnd[i]+1));
140 RelayState[i] = 0;
141 }
142 else
143 {
144 Timers[i] = TimesOn[i]-1 + (rand()%(TimesOnRnd[i]+1));
145 RelayState[i] = 1;
146 }
147 // Send new state to realy output
148 SetOutput(i, RelayState[i]);
149 }
150 }
151 // Restart granularity counter
152 Timer=0;
153 }
154  
155 // Inputs state
156 static volatile unsigned char ButtonState[8];
157  
158 // Read all inputs
159 for(unsigned char i=0; i<8; i++)
160 {
161 // Read buttons to shift register (each button has its own)
162 ButtonState[i] = (ButtonState[i] << 1) + GetInput(i);
163 if(ButtonState[i]==1)
164 {
165 // Just activated - direct action!
166 /*
167 RelayState[i] = !RelayState[i];
168 SetOutput(i, RelayState[i]);
169 */
170 // Just activated - change relay and exspire timer
171 SetOutput(i, !RelayState[i]);
172 Timers[i] = 0;
173 }
174 }
175 }
176  
177  
178 // USART TX buffer
179 static volatile uint8_t UART_TxBuf[UART_TX0_BUFFER_SIZE];
180  
181 #if UART_TX0_BUFFER_SIZE > 256
182 static volatile uint16_t UART_TxHead;
183 static volatile uint16_t UART_TxTail;
184 #else
185 static volatile uint8_t UART_TxHead;
186 static volatile uint8_t UART_TxTail;
187 #endif
188  
189 // USART RX buffer
190 static volatile uint8_t UART_RxBuf[UART_RX0_BUFFER_SIZE];
191  
192 #if UART_RX0_BUFFER_SIZE > 256
193 static volatile uint16_t UART_RxHead;
194 static volatile uint16_t UART_RxTail;
195 #else
196 static volatile uint8_t UART_RxHead;
197 static volatile uint8_t UART_RxTail;
198 #endif
199  
200  
201 // USART Init
202 void USART_Init()
203 {
204 // Init RX and TX FIFO
205 UART_TxHead = 0;
206 UART_TxTail = 0;
207 UART_RxHead = 0;
208 UART_RxTail = 0;
209  
210 // Set baud rate
211 #define MYUBRR (F_CPU/16/BAUD-1)
212 UBRRH = (unsigned char)(MYUBRR>>8);
213 UBRRL = (unsigned char)MYUBRR;
214 #undef MYUBRR
215 // Enable RX, TX and Receive Interrupt
216 UCSRB = (1<<RXEN)|(1<<TXEN)|(1<<RXCIE);
217 // Set frame format: 8data, 1stop bit
218 UCSRC = (1<<URSEL)|(3<<UCSZ0);
219 // Enable Interrupt
220 // sei(); // not here (in main)
221 }
222  
223  
224 // ISR - USART - Receive Interrupt
225 ISR(USART_RXC_vect)
226 {
227 uint16_t tmphead;
228  
229 // Circular Buffer Index
230 tmphead = ( UART_RxHead + 1) & UART_RX0_BUFFER_MASK;
231  
232 if ( tmphead != UART_RxTail ) {
233 // Increment head pointer
234 UART_RxHead = tmphead;
235 // Store received data
236 UART_RxBuf[tmphead] = UDR;
237 }
238 }
239  
240  
241 // USRAT get char (receive form FIFO)
242 // If no char available wait
243 uint8_t USART_getc(void)
244 {
245 uint16_t tmptail;
246  
247 // wait for data
248 while(UART_RxHead==UART_RxTail)
249 ;//wait
250 /*
251 if ( UART_RxHead==UART_RxTail )
252 {
253 return 0; // no data
254 }
255 */
256  
257 // Increment Pointer
258 tmptail = (UART_RxTail + 1) & UART_RX0_BUFFER_MASK;
259 UART_RxTail = tmptail;
260  
261 // Get data from buffer
262 return UART_RxBuf[tmptail];
263 }
264  
265  
266 // ISR - UART - Transmit Interrupt (on empty)
267 ISR(USART_UDRE_vect)
268 {
269 uint16_t tmptail;
270  
271 if(UART_TxHead != UART_TxTail)
272 {
273 // Increment Tail Pointer
274 tmptail = (UART_TxTail + 1) & UART_TX0_BUFFER_MASK;
275 UART_TxTail = tmptail;
276 // Send Byte
277 UDR = UART_TxBuf[tmptail];
278 } else
279 {
280 // TX FIFO empty, disable UDRE interrupt
281 UCSRB &= ~(1<<UDRIE);
282 }
283 }
284  
285  
286 // USART put char (send to FIFO)
287 // If no free room in buffer wait
288 void USART_putc(char data)
289 {
290 uint16_t tmphead;
291  
292 // Increment FIFO pointer
293 tmphead = (UART_TxHead + 1) & UART_TX0_BUFFER_MASK;
294  
295 while (tmphead == UART_TxTail)
296 {
297 ;// wait for free space in buffer
298 }
299  
300 // Put data to FIFO
301 UART_TxBuf[tmphead] = data;
302 UART_TxHead = tmphead;
303  
304 // Enable UDRE interrupt
305 UCSRB |= (1<<UDRIE);
306 }
307  
308  
309 // Send string from Program Memory
310 void USART_TxString_P(const char * data)
311 {
312 while(pgm_read_byte(data)!=0 )
313 USART_putc(pgm_read_byte(data++));
314 }
315  
316  
317 // Print current config from RAM
318 void PrintConfig()
319 {
320 USART_TxString_P(PSTR(
321 "COMMAND CHANNEL TIME_ON TIME_OFF RND_ON RND_OFF\n\r"
322 "-----------------------------------------------------------\n\r"
323 ));
324 for(unsigned char i=0; i<8; i++)
325 {
326 printf("CONFIG %u %5u %5u %5u %5u\n\r",
327 i, TimesOn[i], TimesOff[i], TimesOnRnd[i], TimesOffRnd[i]);
328 }
329 }
330  
331  
332 // Print Error
333 void PrintError()
334 {
335 printf("\n\r??");
336 }
337  
338  
339 // USART RX/TX stream
340 static FILE uart_io = FDEV_SETUP_STREAM(USART_putc, USART_getc, _FDEV_SETUP_RW);
341  
342  
343 // Program modes
344 #define MODE_MANUAL 1
345 #define MODE_COMMAND 0
346  
347 char Mode = MODE_COMMAND;
348  
349  
350 // Main Program
351 int main ()
352 {
353 // Load Config from EEPROM
354 EE_Load();
355  
356 // Timer Init
357 TimerInit();
358  
359 // USART Init
360 USART_Init();
361 stdout = &uart_io;
362 stdin = &uart_io;
363  
364 // Enable Interrupt (timer and USART)
365 sei();
366  
367 // Print Info
368 USART_TxString_P(PSTR(
369 "\n\r\n\rTrain Timer\n\r----------------\n\r(c) miho "YEAR" WWW.MLAB.CZ\n\r"VERSION"\n\r\n\r"
370 ));
371 PrintConfig();
372  
373 // Main Loop (deals with user via serial console)
374 char ReceivedByte;
375 for(;;)
376 {
377 // MODE COMMAND (wait for line and proces it)
378 // ----------------------------------------------------
379 if(Mode==MODE_COMMAND)
380 {
381 // Line
382 char Line[LINE_LENGTH];
383 unsigned char LinePtr=0;
384  
385 // Print help for command mode
386 USART_TxString_P(PSTR(
387 "\n\rCommand Mode\n\r\n\r"
388 " MANUAL -- Start Interactive Mode\n\r"
389 " CONFIG Channel On Off Random_On Random_Off -- Set Time Config\n\r"
390 " SAVE -- Save to EEPROM\n\r"
391 " LIST -- Display Curent Settings\n\r"
392 ));
393  
394 // Get Line
395 for(;;)
396 {
397 // Prompt
398 printf("\n\r> ");
399 LinePtr=0;
400 for(;;)
401 {
402 // Get Char
403 ReceivedByte = USART_getc();
404  
405 // CR
406 if(ReceivedByte==CR)
407 {
408 // Enter
409 Line[LinePtr]=0;
410 break;
411 }
412  
413 // BS/DEL
414 if((ReceivedByte==BS) || (ReceivedByte==DEL))
415 {
416 // Backspace or delete
417 if(LinePtr>0)
418 {
419 USART_putc(BS);
420 USART_putc(' ');
421 USART_putc(BS);
422 LinePtr--;
423 }
424 continue;
425 }
426  
427 // Not printable ASCII
428 if((ReceivedByte<' ') || (ReceivedByte>0x7E))
429 {
430 // Ignore control chars
431 continue;
432 }
433  
434 // Space at the beginning
435 if((ReceivedByte==' ') && (LinePtr==0))
436 {
437 // Ignore space at the beginning
438 continue;
439 }
440  
441 // Store Char to Line Buffer
442 if(LinePtr<sizeof(Line)-2)
443 {
444 // UpCase
445 if(ReceivedByte>='a' && ReceivedByte<='z')
446 ReceivedByte+='A'-'a';
447 // Echo
448 USART_putc(ReceivedByte);
449 // Store to Line
450 Line[LinePtr++] = ReceivedByte;
451 }
452  
453 }
454  
455 // Process Line
456 //printf(" [%s]\n\r", Line);
457  
458 char * LineP = Line;
459 char s[sizeof(Line)];
460  
461 // Get Command (first word)
462 if(sscanf(LineP, "%s", s)!=1)
463 continue;
464  
465 // Empty line or comment
466 if((s[0]=='#')||(s[0]==0))
467 {
468 continue;
469 }
470  
471 // Command MANUAL
472 if(strcmp(s, "MANUAL")==0)
473 {
474 printf("\n\r");
475 Mode=MODE_MANUAL;
476 break;
477 }
478  
479 // Command LIST
480 if(strcmp(s, "LIST")==0)
481 {
482 printf("\n\r\n\r");
483 PrintConfig();
484 continue;
485 }
486  
487 // Command CONFIG
488 if(strcmp(s, "CONFIG")==0)
489 {
490 char * LineP = Line+sizeof("CONFIG");
491 unsigned char ch;
492  
493 // Read channel number
494 if(sscanf(LineP, "%hhu", &ch)!=1)
495 {
496 PrintError();
497 continue;
498 }
499  
500 // Test max number of channels
501 if(ch>8)
502 {
503 PrintError();
504 continue;
505 }
506  
507 // Get data from RAM
508 unsigned int a = TimesOn[ch];
509 unsigned int b = TimesOff[ch];
510 unsigned int c = TimesOnRnd[ch];
511 unsigned int d = TimesOffRnd[ch];
512  
513 // Process input data
514 if(sscanf(LineP, "%hhu%u%u%u%u", &ch, &a, &b, &c, &d)<2)
515 {
516 PrintError();
517 continue;
518 }
519  
520 //printf("New Config %u %u %u %u %u", ch, a,b,c,d);
521 // Store new data to ram
522 TimesOn[ch] = a;
523 TimesOff[ch] = b;
524 TimesOnRnd[ch] = c;
525 TimesOffRnd[ch] = d;
526  
527 // Clear Timer
528 Timers[ch] = 0;
529 continue;
530 }
531  
532 // Command SAVE
533 if(strcmp(s, "SAVE")==0)
534 {
535 EE_Store();
536 continue;
537 }
538  
539 // Unknown Command
540 PrintError();
541 }
542 }
543  
544 // MODE MANUAL (Interactive Mode)
545 // ----------------------------------------------------
546 if(Mode==MODE_MANUAL)
547 {
548 // Print help for command mode
549 USART_TxString_P(PSTR(
550 "\n\r"
551 "Interactive Mode\n\r\n\r"
552 "0..7 Reverse Channel 0..7 (and reset timer)\n\r"
553 "A..H Set Channel 0..7 on\n\r"
554 "a..h Set Channel 0..7 off\n\r"
555 "ESC Return from Interactive Mode\n\r\n\r> "
556 ));
557  
558 // Interractive loop
559 for(;;)
560 {
561 // Get CHar
562 ReceivedByte = USART_getc();
563  
564 // Commands A-H - Switch the relay on
565 if(ReceivedByte>='A' && ReceivedByte<='H')
566 {
567 RelayState[ReceivedByte-'A'] = 1;
568 SetOutput(ReceivedByte-'A', 1);
569 }
570  
571 // Commands a-h - Switch the relay off
572 else if(ReceivedByte>='a' && ReceivedByte<='h')
573 {
574 RelayState[ReceivedByte-'a'] = 0;
575 SetOutput(ReceivedByte-'a', 0);
576 }
577  
578 // Command 0-7 - Negate realay
579 else if(ReceivedByte>='0' && ReceivedByte<='7')
580 {
581 // Just activated - set relay and exspire timer
582 SetOutput(ReceivedByte-'0', !RelayState[ReceivedByte-'0']);
583 Timers[ReceivedByte-'0'] = 0;
584 }
585  
586 // ESC - return from interractive mode
587 else if(ReceivedByte==ESC)
588 {
589 printf("\n\r");
590 Mode=MODE_COMMAND;
591 break;
592 }
593  
594 // Unknown Command
595 else
596 {
597 USART_putc(BS);
598 USART_putc('?');
599 continue;
600 }
601  
602 // Display Char
603 USART_putc(BS);
604 USART_putc(ReceivedByte);
605 }
606 }
607 }
608  
609 return 0;
610 }