0,0 → 1,610 |
// ------------------------------------------------------------------ |
// |
// 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; |
} |