// ------------------------------------------------------------------
//
// 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 Outputs
void SetOutput(const unsigned char Number, const unsigned char State)
{
// Check
if(Number>7)
return;
// Set DDR (for output)
(*RE_DDR_Table[Number]) |= RE_BIT_MASK[Number];
// Set/Clear data
if(State)
(*RE_PORT_Table[Number]) |= RE_BIT_MASK[Number];
else
(*RE_PORT_Table[Number]) &= ~RE_BIT_MASK[Number];
}
// Get Switch Input State
unsigned char GetInput(const unsigned char Number)
{
// Check
if(Number>7)
return 0;
// PullUp
(*SW_PORT_Table[Number]) |= SW_BIT_MASK[Number];
// Get Value
return ((*SW_PIN_Table[Number]) & SW_BIT_MASK[Number]) == 0 ? 1 : 0;
}
// EEPROM Data
unsigned 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 configuration
static 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 RAM
void EE_Load()
{
// Copy data from EEPROM to RAM
eeprom_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 RAM
void EE_Store()
{
// Copy data from RAM to EEPROM
eeprom_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 1
void TimerInit()
{
TCCR1B |= (1<<WGM12); // Configure timer 1 for CTC mode
TIMSK |= (1<<OCIE1A); // Enable CTC interrupt (Output Compare)
OCR1A = F_CPU/F_TIME_GRAIN/8; // Peridic interrupt
TCCR1B |= (1 << CS11); // Start timer at Fcpu/8
}
// Global State of Realays
static volatile unsigned char RelayState[8];
static volatile unsigned int Timers[8];
// ISR - TIMER - 10ms
ISR(TIMER1_COMPA_vect)
{
// Time granularity to 1s counter
static volatile unsigned int Timer;
Timer++;
if(Timer==F_TIME_GRAIN)
{
// 1s
for(unsigned char i=0; i<8; i++)
{
// Decrement all timers
if(Timers[i])
{
Timers[i]--;
}
else
{
// Switch state
if(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 output
SetOutput(i, RelayState[i]);
}
}
// Restart granularity counter
Timer=0;
}
// Inputs state
static volatile unsigned char ButtonState[8];
// Read all inputs
for(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 timer
SetOutput(i, !RelayState[i]);
Timers[i] = 0;
}
}
}
// USART TX buffer
static volatile uint8_t UART_TxBuf[UART_TX0_BUFFER_SIZE];
#if UART_TX0_BUFFER_SIZE > 256
static volatile uint16_t UART_TxHead;
static volatile uint16_t UART_TxTail;
#else
static volatile uint8_t UART_TxHead;
static volatile uint8_t UART_TxTail;
#endif
// USART RX buffer
static volatile uint8_t UART_RxBuf[UART_RX0_BUFFER_SIZE];
#if UART_RX0_BUFFER_SIZE > 256
static volatile uint16_t UART_RxHead;
static volatile uint16_t UART_RxTail;
#else
static volatile uint8_t UART_RxHead;
static volatile uint8_t UART_RxTail;
#endif
// USART Init
void USART_Init()
{
// Init RX and TX FIFO
UART_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 Interrupt
UCSRB = (1<<RXEN)|(1<<TXEN)|(1<<RXCIE);
// Set frame format: 8data, 1stop bit
UCSRC = (1<<URSEL)|(3<<UCSZ0);
// Enable Interrupt
// sei(); // not here (in main)
}
// ISR - USART - Receive Interrupt
ISR(USART_RXC_vect)
{
uint16_t tmphead;
// Circular Buffer Index
tmphead = ( UART_RxHead + 1) & UART_RX0_BUFFER_MASK;
if ( tmphead != UART_RxTail ) {
// Increment head pointer
UART_RxHead = tmphead;
// Store received data
UART_RxBuf[tmphead] = UDR;
}
}
// USRAT get char (receive form FIFO)
// If no char available wait
uint8_t USART_getc(void)
{
uint16_t tmptail;
// wait for data
while(UART_RxHead==UART_RxTail)
;//wait
/*
if ( UART_RxHead==UART_RxTail )
{
return 0; // no data
}
*/
// Increment Pointer
tmptail = (UART_RxTail + 1) & UART_RX0_BUFFER_MASK;
UART_RxTail = tmptail;
// Get data from buffer
return UART_RxBuf[tmptail];
}
// ISR - UART - Transmit Interrupt (on empty)
ISR(USART_UDRE_vect)
{
uint16_t tmptail;
if(UART_TxHead != UART_TxTail)
{
// Increment Tail Pointer
tmptail = (UART_TxTail + 1) & UART_TX0_BUFFER_MASK;
UART_TxTail = tmptail;
// Send Byte
UDR = UART_TxBuf[tmptail];
} else
{
// TX FIFO empty, disable UDRE interrupt
UCSRB &= ~(1<<UDRIE);
}
}
// USART put char (send to FIFO)
// If no free room in buffer wait
void USART_putc(char data)
{
uint16_t tmphead;
// Increment FIFO pointer
tmphead = (UART_TxHead + 1) & UART_TX0_BUFFER_MASK;
while (tmphead == UART_TxTail)
{
;// wait for free space in buffer
}
// Put data to FIFO
UART_TxBuf[tmphead] = data;
UART_TxHead = tmphead;
// Enable UDRE interrupt
UCSRB |= (1<<UDRIE);
}
// Send string from Program Memory
void USART_TxString_P(const char * data)
{
while(pgm_read_byte(data)!=0 )
USART_putc(pgm_read_byte(data++));
}
// Print current config from RAM
void 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 Error
void PrintError()
{
printf("\n\r??");
}
// USART RX/TX stream
static FILE uart_io = FDEV_SETUP_STREAM(USART_putc, USART_getc, _FDEV_SETUP_RW);
// Program modes
#define MODE_MANUAL 1
#define MODE_COMMAND 0
char Mode = MODE_COMMAND;
// Main Program
int main ()
{
// Load Config from EEPROM
EE_Load();
// Timer Init
TimerInit();
// USART Init
USART_Init();
stdout = &uart_io;
stdin = &uart_io;
// Enable Interrupt (timer and USART)
sei();
// Print Info
USART_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)
{
// Line
char Line[LINE_LENGTH];
unsigned char LinePtr=0;
// Print help for command mode
USART_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 Line
for(;;)
{
// Prompt
printf("\n\r> ");
LinePtr=0;
for(;;)
{
// Get Char
ReceivedByte = USART_getc();
// CR
if(ReceivedByte==CR)
{
// Enter
Line[LinePtr]=0;
break;
}
// BS/DEL
if((ReceivedByte==BS) || (ReceivedByte==DEL))
{
// Backspace or delete
if(LinePtr>0)
{
USART_putc(BS);
USART_putc(' ');
USART_putc(BS);
LinePtr--;
}
continue;
}
// Not printable ASCII
if((ReceivedByte<' ') || (ReceivedByte>0x7E))
{
// Ignore control chars
continue;
}
// Space at the beginning
if((ReceivedByte==' ') && (LinePtr==0))
{
// Ignore space at the beginning
continue;
}
// Store Char to Line Buffer
if(LinePtr<sizeof(Line)-2)
{
// UpCase
if(ReceivedByte>='a' && ReceivedByte<='z')
ReceivedByte+='A'-'a';
// Echo
USART_putc(ReceivedByte);
// Store to Line
Line[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 comment
if((s[0]=='#')||(s[0]==0))
{
continue;
}
// Command MANUAL
if(strcmp(s, "MANUAL")==0)
{
printf("\n\r");
Mode=MODE_MANUAL;
break;
}
// Command LIST
if(strcmp(s, "LIST")==0)
{
printf("\n\r\n\r");
PrintConfig();
continue;
}
// Command CONFIG
if(strcmp(s, "CONFIG")==0)
{
char * LineP = Line+sizeof("CONFIG");
unsigned char ch;
// Read channel number
if(sscanf(LineP, "%hhu", &ch)!=1)
{
PrintError();
continue;
}
// Test max number of channels
if(ch>8)
{
PrintError();
continue;
}
// Get data from RAM
unsigned int a = TimesOn[ch];
unsigned int b = TimesOff[ch];
unsigned int c = TimesOnRnd[ch];
unsigned int d = TimesOffRnd[ch];
// Process input data
if(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 ram
TimesOn[ch] = a;
TimesOff[ch] = b;
TimesOnRnd[ch] = c;
TimesOffRnd[ch] = d;
// Clear Timer
Timers[ch] = 0;
continue;
}
// Command SAVE
if(strcmp(s, "SAVE")==0)
{
EE_Store();
continue;
}
// Unknown Command
PrintError();
}
}
// MODE MANUAL (Interactive Mode)
// ----------------------------------------------------
if(Mode==MODE_MANUAL)
{
// Print help for command mode
USART_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 loop
for(;;)
{
// Get CHar
ReceivedByte = USART_getc();
// Commands A-H - Switch the relay on
if(ReceivedByte>='A' && ReceivedByte<='H')
{
RelayState[ReceivedByte-'A'] = 1;
SetOutput(ReceivedByte-'A', 1);
}
// Commands a-h - Switch the relay off
else if(ReceivedByte>='a' && ReceivedByte<='h')
{
RelayState[ReceivedByte-'a'] = 0;
SetOutput(ReceivedByte-'a', 0);
}
// Command 0-7 - Negate realay
else if(ReceivedByte>='0' && ReceivedByte<='7')
{
// Just activated - set relay and exspire timer
SetOutput(ReceivedByte-'0', !RelayState[ReceivedByte-'0']);
Timers[ReceivedByte-'0'] = 0;
}
// ESC - return from interractive mode
else if(ReceivedByte==ESC)
{
printf("\n\r");
Mode=MODE_COMMAND;
break;
}
// Unknown Command
else
{
USART_putc(BS);
USART_putc('?');
continue;
}
// Display Char
USART_putc(BS);
USART_putc(ReceivedByte);
}
}
}
return 0;
}