// ------------------------------------------------------------------//// TRAIN TIMER// -----------//// Firmware for TRAIN02A board. It changes state of relay outputs// as defined in configuration. Default configuration is in .h file// and definitions may be changed online via USB port and if needed// stored into EEPROM.//// Uses ATmega8 at 8MHz (internal RC or external XTAL)//// (c) miho WWW.MLAB.CZ/PermaLink/TRAIN//// ------------------------------------------------------------------//// History//// 1.00 First version// Standard header files#include <avr/io.h> // Device Specific Defines#include <avr/interrupt.h> // For Timer and USART Interrupt#include <stdlib.h> // For rand()#include <avr/eeprom.h> // For EEPROM access (for configuration)#include <stdio.h> // For printf() etc#include <string.h> // For strcmp() etc#include <avr/pgmspace.h> // Data in Program Memory (strings)#include "TRAIN.h" // Hardware Definitions#include "TRAIN_TIMER.h" // Project Definitions - default configuration is here// Set Relay Outputsvoid SetOutput(const unsigned char Number, const unsigned char State){// Checkif(Number>7)return;// Set DDR (for output)(*RE_DDR_Table[Number]) |= RE_BIT_MASK[Number];// Set/Clear dataif(State)(*RE_PORT_Table[Number]) |= RE_BIT_MASK[Number];else(*RE_PORT_Table[Number]) &= ~RE_BIT_MASK[Number];}// Get Switch Input Stateunsigned char GetInput(const unsigned char Number){// Checkif(Number>7)return 0;// PullUp(*SW_PORT_Table[Number]) |= SW_BIT_MASK[Number];// Get Valuereturn ((*SW_PIN_Table[Number]) & SW_BIT_MASK[Number]) == 0 ? 1 : 0;}// EEPROM Dataunsigned int EEMEM EE_TimesOn[8] = DEFAULT_TIMES_ON ;unsigned int EEMEM EE_TimesOff[8] = DEFAULT_TIMES_OFF ;unsigned int EEMEM EE_TimesOnRnd[8] = DEFAULT_TIMES_ON_RND ;unsigned int EEMEM EE_TimesOffRnd[8] = DEFAULT_TIMES_OFF_RND ;// Runtime configurationstatic volatile unsigned int TimesOn[8];static volatile unsigned int TimesOff[8];static volatile unsigned int TimesOnRnd[8];static volatile unsigned int TimesOffRnd[8];// Load configuration from EEPROM to RAMvoid EE_Load(){// Copy data from EEPROM to RAMeeprom_read_block(&TimesOn, &EE_TimesOn, sizeof(TimesOn) );eeprom_read_block(&TimesOff, &EE_TimesOff, sizeof(TimesOff) );eeprom_read_block(&TimesOnRnd, &EE_TimesOnRnd, sizeof(TimesOnRnd) );eeprom_read_block(&TimesOffRnd, &EE_TimesOffRnd, sizeof(TimesOffRnd) );}// Store configuration to EEPROM from RAMvoid EE_Store(){// Copy data from RAM to EEPROMeeprom_write_block(&TimesOn, &EE_TimesOn, sizeof(TimesOn) );eeprom_write_block(&TimesOff, &EE_TimesOff, sizeof(TimesOff) );eeprom_write_block(&TimesOnRnd, &EE_TimesOnRnd, sizeof(TimesOnRnd) );eeprom_write_block(&TimesOffRnd, &EE_TimesOffRnd, sizeof(TimesOffRnd) );}// Init Timer 1void TimerInit(){TCCR1B |= (1<<WGM12); // Configure timer 1 for CTC modeTIMSK |= (1<<OCIE1A); // Enable CTC interrupt (Output Compare)OCR1A = F_CPU/F_TIME_GRAIN/8; // Peridic interruptTCCR1B |= (1 << CS11); // Start timer at Fcpu/8}// Global State of Realaysstatic volatile unsigned char RelayState[8];static volatile unsigned int Timers[8];// ISR - TIMER - 10msISR(TIMER1_COMPA_vect){// Time granularity to 1s counterstatic volatile unsigned int Timer;Timer++;if(Timer==F_TIME_GRAIN){// 1sfor(unsigned char i=0; i<8; i++){// Decrement all timersif(Timers[i]){Timers[i]--;}else{// Switch stateif(RelayState[i]){Timers[i] = TimesOff[i]-1 + (rand()%(TimesOffRnd[i]+1));RelayState[i] = 0;}else{Timers[i] = TimesOn[i]-1 + (rand()%(TimesOnRnd[i]+1));RelayState[i] = 1;}// Send new state to realy outputSetOutput(i, RelayState[i]);}}// Restart granularity counterTimer=0;}// Inputs statestatic volatile unsigned char ButtonState[8];// Read all inputsfor(unsigned char i=0; i<8; i++){// Read buttons to shift register (each button has its own)ButtonState[i] = (ButtonState[i] << 1) + GetInput(i);if(ButtonState[i]==1){// Just activated - direct action!/*RelayState[i] = !RelayState[i];SetOutput(i, RelayState[i]);*/// Just activated - change relay and exspire timerSetOutput(i, !RelayState[i]);Timers[i] = 0;}}}// USART TX bufferstatic volatile uint8_t UART_TxBuf[UART_TX0_BUFFER_SIZE];#if UART_TX0_BUFFER_SIZE > 256static volatile uint16_t UART_TxHead;static volatile uint16_t UART_TxTail;#elsestatic volatile uint8_t UART_TxHead;static volatile uint8_t UART_TxTail;#endif// USART RX bufferstatic volatile uint8_t UART_RxBuf[UART_RX0_BUFFER_SIZE];#if UART_RX0_BUFFER_SIZE > 256static volatile uint16_t UART_RxHead;static volatile uint16_t UART_RxTail;#elsestatic volatile uint8_t UART_RxHead;static volatile uint8_t UART_RxTail;#endif// USART Initvoid USART_Init(){// Init RX and TX FIFOUART_TxHead = 0;UART_TxTail = 0;UART_RxHead = 0;UART_RxTail = 0;// Set baud rate#define MYUBRR (F_CPU/16/BAUD-1)UBRRH = (unsigned char)(MYUBRR>>8);UBRRL = (unsigned char)MYUBRR;#undef MYUBRR// Enable RX, TX and Receive InterruptUCSRB = (1<<RXEN)|(1<<TXEN)|(1<<RXCIE);// Set frame format: 8data, 1stop bitUCSRC = (1<<URSEL)|(3<<UCSZ0);// Enable Interrupt// sei(); // not here (in main)}// ISR - USART - Receive InterruptISR(USART_RXC_vect){uint16_t tmphead;// Circular Buffer Indextmphead = ( UART_RxHead + 1) & UART_RX0_BUFFER_MASK;if ( tmphead != UART_RxTail ) {// Increment head pointerUART_RxHead = tmphead;// Store received dataUART_RxBuf[tmphead] = UDR;}}// USRAT get char (receive form FIFO)// If no char available waituint8_t USART_getc(void){uint16_t tmptail;// wait for datawhile(UART_RxHead==UART_RxTail);//wait/*if ( UART_RxHead==UART_RxTail ){return 0; // no data}*/// Increment Pointertmptail = (UART_RxTail + 1) & UART_RX0_BUFFER_MASK;UART_RxTail = tmptail;// Get data from bufferreturn UART_RxBuf[tmptail];}// ISR - UART - Transmit Interrupt (on empty)ISR(USART_UDRE_vect){uint16_t tmptail;if(UART_TxHead != UART_TxTail){// Increment Tail Pointertmptail = (UART_TxTail + 1) & UART_TX0_BUFFER_MASK;UART_TxTail = tmptail;// Send ByteUDR = UART_TxBuf[tmptail];} else{// TX FIFO empty, disable UDRE interruptUCSRB &= ~(1<<UDRIE);}}// USART put char (send to FIFO)// If no free room in buffer waitvoid USART_putc(char data){uint16_t tmphead;// Increment FIFO pointertmphead = (UART_TxHead + 1) & UART_TX0_BUFFER_MASK;while (tmphead == UART_TxTail){;// wait for free space in buffer}// Put data to FIFOUART_TxBuf[tmphead] = data;UART_TxHead = tmphead;// Enable UDRE interruptUCSRB |= (1<<UDRIE);}// Send string from Program Memoryvoid USART_TxString_P(const char * data){while(pgm_read_byte(data)!=0 )USART_putc(pgm_read_byte(data++));}// Print current config from RAMvoid PrintConfig(){USART_TxString_P(PSTR("COMMAND CHANNEL TIME_ON TIME_OFF RND_ON RND_OFF\n\r""-----------------------------------------------------------\n\r"));for(unsigned char i=0; i<8; i++){printf("CONFIG %u %5u %5u %5u %5u\n\r",i, TimesOn[i], TimesOff[i], TimesOnRnd[i], TimesOffRnd[i]);}}// Print Errorvoid PrintError(){printf("\n\r??");}// USART RX/TX streamstatic FILE uart_io = FDEV_SETUP_STREAM(USART_putc, USART_getc, _FDEV_SETUP_RW);// Program modes#define MODE_MANUAL 1#define MODE_COMMAND 0char Mode = MODE_COMMAND;// Main Programint main (){// Load Config from EEPROMEE_Load();// Timer InitTimerInit();// USART InitUSART_Init();stdout = &uart_io;stdin = &uart_io;// Enable Interrupt (timer and USART)sei();// Print InfoUSART_TxString_P(PSTR("\n\r\n\rTrain Timer\n\r----------------\n\r(c) miho "YEAR" WWW.MLAB.CZ\n\r"VERSION"\n\r\n\r"));PrintConfig();// Main Loop (deals with user via serial console)char ReceivedByte;for(;;){// MODE COMMAND (wait for line and proces it)// ----------------------------------------------------if(Mode==MODE_COMMAND){// Linechar Line[LINE_LENGTH];unsigned char LinePtr=0;// Print help for command modeUSART_TxString_P(PSTR("\n\rCommand Mode\n\r\n\r"" MANUAL -- Start Interactive Mode\n\r"" CONFIG Channel On Off Random_On Random_Off -- Set Time Config\n\r"" SAVE -- Save to EEPROM\n\r"" LIST -- Display Curent Settings\n\r"));// Get Linefor(;;){// Promptprintf("\n\r> ");LinePtr=0;for(;;){// Get CharReceivedByte = USART_getc();// CRif(ReceivedByte==CR){// EnterLine[LinePtr]=0;break;}// BS/DELif((ReceivedByte==BS) || (ReceivedByte==DEL)){// Backspace or deleteif(LinePtr>0){USART_putc(BS);USART_putc(' ');USART_putc(BS);LinePtr--;}continue;}// Not printable ASCIIif((ReceivedByte<' ') || (ReceivedByte>0x7E)){// Ignore control charscontinue;}// Space at the beginningif((ReceivedByte==' ') && (LinePtr==0)){// Ignore space at the beginningcontinue;}// Store Char to Line Bufferif(LinePtr<sizeof(Line)-2){// UpCaseif(ReceivedByte>='a' && ReceivedByte<='z')ReceivedByte+='A'-'a';// EchoUSART_putc(ReceivedByte);// Store to LineLine[LinePtr++] = ReceivedByte;}}// Process Line//printf(" [%s]\n\r", Line);char * LineP = Line;char s[sizeof(Line)];// Get Command (first word)if(sscanf(LineP, "%s", s)!=1)continue;// Empty line or commentif((s[0]=='#')||(s[0]==0)){continue;}// Command MANUALif(strcmp(s, "MANUAL")==0){printf("\n\r");Mode=MODE_MANUAL;break;}// Command LISTif(strcmp(s, "LIST")==0){printf("\n\r\n\r");PrintConfig();continue;}// Command CONFIGif(strcmp(s, "CONFIG")==0){char * LineP = Line+sizeof("CONFIG");unsigned char ch;// Read channel numberif(sscanf(LineP, "%hhu", &ch)!=1){PrintError();continue;}// Test max number of channelsif(ch>8){PrintError();continue;}// Get data from RAMunsigned int a = TimesOn[ch];unsigned int b = TimesOff[ch];unsigned int c = TimesOnRnd[ch];unsigned int d = TimesOffRnd[ch];// Process input dataif(sscanf(LineP, "%hhu%u%u%u%u", &ch, &a, &b, &c, &d)<2){PrintError();continue;}//printf("New Config %u %u %u %u %u", ch, a,b,c,d);// Store new data to ramTimesOn[ch] = a;TimesOff[ch] = b;TimesOnRnd[ch] = c;TimesOffRnd[ch] = d;// Clear TimerTimers[ch] = 0;continue;}// Command SAVEif(strcmp(s, "SAVE")==0){EE_Store();continue;}// Unknown CommandPrintError();}}// MODE MANUAL (Interactive Mode)// ----------------------------------------------------if(Mode==MODE_MANUAL){// Print help for command modeUSART_TxString_P(PSTR("\n\r""Interactive Mode\n\r\n\r""0..7 Reverse Channel 0..7 (and reset timer)\n\r""A..H Set Channel 0..7 on\n\r""a..h Set Channel 0..7 off\n\r""ESC Return from Interactive Mode\n\r\n\r> "));// Interractive loopfor(;;){// Get CHarReceivedByte = USART_getc();// Commands A-H - Switch the relay onif(ReceivedByte>='A' && ReceivedByte<='H'){RelayState[ReceivedByte-'A'] = 1;SetOutput(ReceivedByte-'A', 1);}// Commands a-h - Switch the relay offelse if(ReceivedByte>='a' && ReceivedByte<='h'){RelayState[ReceivedByte-'a'] = 0;SetOutput(ReceivedByte-'a', 0);}// Command 0-7 - Negate realayelse if(ReceivedByte>='0' && ReceivedByte<='7'){// Just activated - set relay and exspire timerSetOutput(ReceivedByte-'0', !RelayState[ReceivedByte-'0']);Timers[ReceivedByte-'0'] = 0;}// ESC - return from interractive modeelse if(ReceivedByte==ESC){printf("\n\r");Mode=MODE_COMMAND;break;}// Unknown Commandelse{USART_putc(BS);USART_putc('?');continue;}// Display CharUSART_putc(BS);USART_putc(ReceivedByte);}}}return 0;}