I wrote this small program (with lots of inspiration from oscale's simpledcc and this forum) to see if the arduino was capable of controlling HO modelltrains through DCC. Only one small problem still exists: the locos won't move :P.
Well, that's not enirely true though, when put on the track it moves very slowly forward in small steps. Is there anyone else who have had this problem? Is there something wrong with my code? Or could it be due to lack of power?
#define DCC_PIN 13 //Arduino pin for DCC out
//definitions for state machine
#define SEPERATOR 1
#define DATABYTE 2
#define END_OF_DATA 3
#define MAXMSG 2 //Maximum number of messages
#define TIMER_SHORT 57 //58 usec pulse length
#define TIMER_LONG 115 //116 usec pulse length
boolean every_second_isr = false;
unsigned char timer = TIMER_LONG;
unsigned char state = DATABYTE;
unsigned char currentBit = 0x80;
unsigned char movementBase = 64;
unsigned char locoDirection = 0;
unsigned char locoSpeed = 2;
unsigned char addressBase = 0;
unsigned char locoAddress = 1;
int msgIndex = 0;
int byteIndex = 0;
struct Message{
unsigned char data[7];
unsigned char len;
};
struct Message msg[MAXMSG]={
{{0xFF,0xFF,0,0xFF,0,0,0},4}, //idle message
{{0xFF,0,0,0,0,0,0},4} //preamble 0 address 0 movement 0 errordetection 1
};
ISR(TIMER2_COMPA_vect) {
if(every_second_isr) {
digitalWrite(DCC_PIN,1);
every_second_isr = false;
} else {
digitalWrite(DCC_PIN,0);
every_second_isr = true;
switch(state) {
case DATABYTE:
if(msg[msgIndex].data[byteIndex] & currentBit){
timer = TIMER_SHORT;
} else {
timer = TIMER_LONG;
}
currentBit = currentBit >> 1;
if(currentBit==0) {
byteIndex++;
if(byteIndex >= msg[msgIndex].len) {
state = END_OF_DATA;
msgIndex++;
if(msgIndex >= MAXMSG) {
msgIndex = 0;
}
byteIndex = 0;
} else {
state = SEPERATOR;
}
}
break;
case SEPERATOR:
timer = TIMER_LONG;
state = DATABYTE;
currentBit = 0x80;
break;
case END_OF_DATA:
timer = TIMER_SHORT;
state = DATABYTE;
currentBit = 0x80;
break;
}
OCR2A = timer;
}
}
void setup(void){
pinMode(DCC_PIN,OUTPUT);//this is for the DCCSignal
assembleDCCMsg();
//Start the timer
setupTimer2();
}
void loop(void){
}
void setupTimer2(){
TCCR2B = 0; //Disable the timer
TCCR2A = _BV(WGM21); //Configure the wave form and actions
TIFR2 = 0; //Clear the interupt flag
TIMSK2 = _BV(OCIE2A); //Enable interupts
OCR2A = timer; //Define the value to compare against
TCNT2 = 0; //Reset the timer
TCCR2B = _BV(CS21); //Enable the timer
}
void assembleDCCMsg(){
byte addressByte = addressBase | locoAddress;
byte movementByte = movementBase | locoDirection | locoSpeed;
byte errorDetectionByte = addressByte ^ movementByte;
noInterrupts();
msg[1].data[1] = addressByte;
msg[1].data[2] = movementByte;
msg[1].data[3] = errorDetectionByte;
interrupts();
}
Edit: I should also mention that the loco runs fine with my bought reference system