Jump to content


Google Ads are only seen by non-members of RMweb - Create an RMweb account and you'll only receive modelling ads.

Photo
- - - - -

RailCom capable DIY DCC command station and booster





  • Please log in to reply
18 replies to this topic

#1 Dutch_Master

Dutch_Master

    Member


  • Members
  • PipPip
  • 5,249 posts

Posted 05 May 2017 - 15:39

This project was started as an idea to replace the innards of my ageing Uhlenbrock IntelliBox (early original version) as it stopped working properly a fair while ago, even though I've subsequently replaced it with a Roco Z21 unit. It was also an exercise for me in PCB design. Actually, it still is, as it's not really finished yet: having made some mistakes/omissions, revised versions have been created. But the basics can be seen below.

 

DCC_RailCom_command-station_booster.jpg

(double-click to enlarge)

 

The schematics (or diagram if you prefer) is made with Kicad, an Open Source and thus free PCB design suite. Sadly, it lacks the option of exporting the schematic as image, so I'd had to take a detour making one. This required transforming the original output several times, which caused the resulting image to be slightly blurred. To minimise the impact of that, I created a larger drawing, which may fill your screen completely if you click on the image above. Sorry! :sorry: :punish:

 

Before embarking on a tour of the schematics, first a look at the circumference. You'll notice it has letters and digits on the edge. This creates a convenient grid to refer to if you're searching components. I'll use it too.

 

We'll start with the power supply, in the top-right corner (A-5/6). A barrel jack allows the use of a box-standard laptop power brick, 15-18V DC will do. Diode D1 (Schottky type for its low forward voltage loss) protect the circuit against wrong polarity. U2, a 5V voltage regulator, provides the circuit with a stable power source. Capacitor C2 covers any current peak requirements for the driver chip while capacitors C3 and C4 provide some suppression of unwanted AC signals on the power lines. To the left (A-4) is the actual driver chip, the LMD18200T. The small capacitors are required to drive the output MOSFETs properly, so shouldn't be omitted!

 

Below that is the keypad (B/C-4/5). This is a 6x5 matrix, containing 6 rows and 5 columns. Most keys are designated, but I'd forgotten a few. As the construction of the matrix isn't clear, I've redone it in a subsequent revision. (taking a sneak peak, the current matrix for the keys is a 5x8, that's 40 push buttons) In the matrix, each row has a push button to connect it to one of the columns. To prevent short circuits by simultaneous key-presses, between each row and the button there's a diode to prevent this. Difficult to spot and (partially) addressed in the current revision of the diagram. You'll notice the rows and columns have letter/digit combinations assigned to them. These are called labels, and the program knows that equally named labels are to be connected on the resulting PCB. As such, it's just a convenient method to de-clutter the schematics for human eyes.

 

Below the keypad is an LCD (C-5). It says 16x2 but it should have been a 16x4 display. Sadly, I haven't found a symbol for that in the Kicad libraries, so 16x2 will have to do :( Either side, an array of resistors and capacitors, each with labels for the keypad columns. (C-4/6). In grid B-3 a pair of rotary encoders with switch. The latter is actually a small push-button under the shaft of the encoder. These are used to control the speed and direction of a loco. Having 2 means you can control 2 loco's simultaneously. Pushing the button when the loco has any speed-step >0 will result in an emergency stop for that loco, if speed is zero, then the loco changes direction. Right on the edge, B-6, a row of led's with their resistors. You'll notice these too carry labels. That's because they are also in a matrix, but separate to the keys, hence the different names. The careful observer would notice 2 combinations are missing, these are on the opposite side of the diagram (B-1). We'll get to those a little later.

 

Now, the "main event", U1, the microcontroller. This is the Amtel ATmega2560 chip. I used it as replacement for the Arduino Mega2560 board I originally envisaged using. Again, Kicad doesn't have a symbol for it yet, so instead I used the chip itself and subsequently had to build up the required paraphernalia for it to work, being the 5V power supply and the clock- and reset circuitry. Both can be found in the top left corner (A-1). You'll also find the 'missing' leds there (one green, one red), as well as another pair of push buttons. These are the Stop and Go buttons, which disable/enable track power respectively. Using the chip directly meant also implementing the ICSP header. ICSP means "in circuit serial programming" and is an industry standard for (re-)programming microcontrollers w/o having to remove them from the circuit they're used in. The header is bottom-right to the chip. As said, all of that is provided by the Arduino Mega2560 board, so in practicality you'd only need the driver chip, keypad- and led-matrix.

 

I've selected the various in/output pins for the various functions they'd perform on availability on the aforementioned Arduino board headers. This means you can simply plug them in w/o having to worry the code needing altering if you change between the board and using the chip directly. The pin-count is as follows: 30 digital I/O pins (excluding the ICSP header) and 6 analog pins. Theoretically you can use the more powerful Arduino Due board as it's pin-compatible, but this requires a redesign of the led matrix (the Due ports cannot supply as much current as the ATmega family can) and a considerable change in the code, basically a re-write!

 

This concludes the hardware tour of the diagram. In the next post I'll describe how the matrices work.



Google Ads are only seen by non-members of RMweb - Create an RMweb account and you'll only receive modelling ads.

#2 Dutch_Master

Dutch_Master

    Member


  • Members
  • PipPip
  • 5,249 posts

Posted 05 May 2017 - 17:48

The matrix. Not a film (well, it is actually, just not here :P ) but a clever way of maximising in/output options while minimising the required in/output pins on the microcontroller.

 

As said, a matrix consists of rows (the horizontal lines) and columns (the vertical lines). On the keypad, each key has a diode, as we've seen earlier, to prevent short circuits. How does it work?

 

The microcontroller puts a logic high on each of the rows sequentially. When you press a button, a current will flow through the diode and the push-button to  the corresponding column. This then is detected by the microcontroller that sequentially checks the input levels on the columns, known as sampling. However, in practice, push-buttons aren't perfect and their contact-surfaces bounce when operated. It's very fast, in the order of micro-seconds, but sufficient for the microcontroller to notice. Thus I added a small capacitor, to keep the charge while the contacts of the push-button are bouncing. The resistor pulls the column-line back down so it doesn't give a false positive for the next sampling. The keypad is located centrally, underneath the display. Not all keys though, 2 sets of 5 keys each are located either side of the display and control the functions F0-F4 of the loco under control.

 

Next, the led matrix. Again, rows and columns. If an led needs to light up, the microcontroller puts a logic high on the row, while pulling the corresponding column to logic low. This has the advantage that at any one time, only a single led is on, thus saving on overall current consumption. As a result, apart from the current needed for running your trains, the base consumption by the microcontroller and led's is about 20-30mA, which is negligent compared to the power needed for the trains themselves. The output pins of the ATmega2560 are capable of supplying 20mA each, so can be used directly to control the led's. The Arduino Due can't, its capabilities are limited to 6-15mA depending on the pin used, and that's pushing it in some cases. That's why the led matrix needs a redesign if you want to choose the Due over the Mega2560. In the led matrix are the red and green Stop and Go led's and 2 sets of 5 yellow led's each, displaying the status of the function outputs F0-F4. Each set of led's is positioned above the rotary encoder and either side of the LCD.

 

The LCD itself is fairly straight-forward: some control lines and an 8-bit data bus for sending over characters and commands. On the display, the loco number and speed step (either absolute or relative). The rotary encoders consist of a pair of switches that are out-of-sync slightly. This way, the microcontroller can determine which way the encoder is turned: clockwise or counter-clockwise. Each digital pin of the microcontroller has a switch-able internal pull-up resistor built in that the code can switch on (and off!) and in this case, they're switched on. That means as you rotate the shaft, each output is periodically switched to low, otherwise it'll have a logic high level. The rotary encoders are located either side of the keypad and have large knobs to ease turning them.



#3 Junctionmad

Junctionmad

    Member


  • Members
  • PipPip
  • 1,033 posts

Posted 28 May 2017 - 07:20

Nice design

Have something similar on the go but based around STM32 , personally having done a considerable amount of AVR development , I've switched to ARM CORE.

best of luck
  • Thanks x 1

#4 Dutch_Master

Dutch_Master

    Member


  • Members
  • PipPip
  • 5,249 posts

Posted 28 May 2017 - 12:56

Thanks for the interest! As per my original message, I intended to use an Arduino board but didn't have the Kicad files for those. I now have, and am considering redoing the schematics. Given that the target audience is very small and perfectly capable of doing this themselves, I'm still in dubio whether I should spend the time. (and the current hot weather doesn't help either :( )

 

I've chosen the Arduino platform over others as there are cheap clones out that work just as well, using the same core chip as the original. This makes it easy for others to build their own copy.

 

In the mean time, using an Arduino Uno (the baseline model in the range) and code I found on the web I created a simple, very, very basic DCC controller. Sadly, the code changes for RailCom I inserted failed to work and a code review is required by some more experienced coder(s), I might be overlooking something. Once that's working, other code can be added to deal with the rest of the peripherals. It's not gonna be quick :no:



#5 keybuk

keybuk

    Registered Member


  • Members
  • Pip
  • 16 posts
  • LocationSan Francisco, CA

Posted 15 June 2017 - 07:24

In the mean time, using an Arduino Uno (the baseline model in the range) and code I found on the web I created a simple, very, very basic DCC controller. Sadly, the code changes for RailCom I inserted failed to work and a code review is required by some more experienced coder(s), I might be overlooking something. Once that's working, other code can be added to deal with the rest of the peripherals. It's not gonna be quick :no:

 

 

In general, for RailCom, you don't need to do much in the Booster - pulling the PWM pin of your LMD18200 low (or the Brake pin low, and mucking around with the Direction pin at the same time so you get the right short) is sufficient.

 

The main trick is generating the cutout at the right time.

 

If your Booster is generating the DCC signal itself (e.g. Arduino DCC) then it's a piece of cake, just use a timer.

 

Instead if you're taking in a logic signal from another Command Station, you'll have to parse the packet to detect the end bit, and then trigger the timer.

 

After that, timer for the initial cutout delay, then pull the pin down, and another timer to pull it back up again at the end of the cutout.


  • Agree x 1

#6 Dutch_Master

Dutch_Master

    Member


  • Members
  • PipPip
  • 5,249 posts

Posted 15 June 2017 - 18:49

Thanks for your interest! I'm aware of the required input levels of various pins to enable RailCom and made an attempt in a piece of code I found on the web. It needs a review, as it's not working as hoped.

 

Meanwhile, as alluded to before, the schematic needed a redesign to incorporate the actual Arduino shield. I've now done so and included some functionality missing from the original design. Plus I've split the diagrams into separate ones for the keypad and one for the rest of the hardware. I also found that exporting the schematics to PDF gives a better (read as: more readable) diagram, so I've attached both below:

 

the main diagram:

Attached File  dcc_rc_booster.pdf   84.16KB   19 downloads

the keypad:

Attached File  keypad-Sheet590C7E39.pdf   60.99KB   13 downloads

 

The top line on the keypad schematics looks different as I wanted to show how the actual matrix is constructed, electrically that is. The other rows are the same, but shown in a more compact way. It's not fully annotated, as the actual parts used depend on the space available on the PCB. Most likely it'll be SMD parts for the Schottky diodes, resistors and capacitors.

 

In the main schematic, I've added de-bounce capacitors to all inputs (switches/buttons) requiring such, this simplifies the code considerably as it no longer needs loops to implement an s/w de-bounce routine. I'm aware some developers want to do anything in S/W, but this kind of thing is easier done by hardware, and a capacitor is pittance cost-wise. (buy in bulk and they cost even less!). Another addition is the program track: a relay switches off the main track, while a resistor limits current to safe levels but still allow the acknowledge pulse when programming the decoder. The NMRA specifies the ACK pulse as 60mA minimum. (see NMRA spec below, page 2)

Attached File  s-9.2.3_2012_07.pdf   47.25KB   25 downloads

If not in use, the program track can be used normally, like for a spur or siding and thus be incorporated into the layout.

 

You will also notice several main parts have been connected to different ports as the layout of the shield is different from the plain microcontroller. I may relocate some parts again, to free communication lines for implementing comms for LocoNet, Xpressnet and perhaps even the ageing I2C ports the original IntelliBox has. Apropos microcontroller, I've now swapped the MEGA2560 board for an Arduino Due, the latter having a higher clock frequency and more program storage space and computing power. Downside is the 3V3 output levels, but the LMD18200 can handle input levels from 2V so that should suffice. The LCD probably requires the use of a level converter for the data bus and control lines as most (possibly all) displays use 5V levels on those. I'll see what I can come up with, if required it'll be in a next revision. :yes:


  • Thanks x 1

#7 keybuk

keybuk

    Registered Member


  • Members
  • Pip
  • 16 posts
  • LocationSan Francisco, CA

Posted 15 June 2017 - 23:04

A few notes on the schematic:

 

 

While the AVR is booting, or being programmed, the pins to the H-Bridge are going to be floating - so you could end up supplying power to the track in all sorts of ways unintentionally. A pull-down on PWM, and/or a pull-up on Brake would be worthwhile!

 

You probably don't need PWM and Brake together, if you're toggling the power via PWM, just stick Brake to ground rather than use a pin in the AVR. Likewise if you're toggling the power via Brake, just pull PWM to VSS.

 

AREF needs a cap to GND, either 10nF or 100nF - if you're using a board it probably has this, but it's not on your schematic.

 

 

You didn't say what's not working about the RailCom…

 

In general here's what you want to do:

  1. output the packet end bit
  2. timer for 26-32us
  3. pull PWM low (or Brake high)
  4. timer for 428us
  5. pull PWM high (or Brake low)
  6. resume the preamble

during 2-5, keep tabs of what the preambling should be, so you can resume at the right "point"



#8 Dutch_Master

Dutch_Master

    Member


  • Members
  • PipPip
  • 5,249 posts

Posted 15 June 2017 - 23:42

Are you familiar with the Arduino boards? If not, here's something about the Due I used for this design ;)

 

As for the code, I'd think it would be better for a separate thread and frankly, I'm not so sure RMWeb is the place to post it (it's really program code, no use to anyone). I did however do exactly what you described.

 

BTW, one does need all 3 input pins on the 18200 to achieve RailCom compatibility.

 

Oh whatever, I'll post it anyway:

// 23. November 2009
// works well with LMD18200 Booster !!!!!

/*Copyright (C) 2009 Michael Blank, RC addition for RailCom 2017 by Dutch_Master

This program is free software; you can redistribute it and/or modify it under the terms of the 
GNU General Public License as published by the Free Software Foundation; 
either version 3 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 
without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
See the GNU General Public License for more details.
*/


#define DCC_PIN    4  // Arduino pin for DCC out 
                      // this pin is connected to "DIRECTION" of LMD18200
#define DCC_PWM    5  // must be HIGH for signal out
                      // connected to "PWM in" of LMD18200
#define DCC_THERM  7  // thermal warning PIN
#define AN_SPEED   2  // analog reading for Speed Poti
#define AN_CURRENT 0  // analog input for current sense reading
#define DCC_BRAKE  6  // brake pin of driver, low for operation, high for railcom cutout


//Timer frequency is 2MHz for ( /8 prescale from 16MHz )
#define TIMER_SHORT 0x8D  // 58usec pulse length 
#define TIMER_LONG  0x1B  // 116usec pulse length 

unsigned char last_timer=TIMER_SHORT;  // store last timer value
   
unsigned char flag=0;  // used for short or long pulse
unsigned char every_second_isr = 0;  // pulse up or down

// definitions for state machine 
#define PREAMBLE 0    
#define SEPERATOR 1
#define SENDBYTE 2
#define RAILCOM 3

unsigned char state= PREAMBLE;
unsigned char preamble_count = 16;
unsigned char outbyte = 0;
unsigned char cbit = 0x80;

// variables for throttle
int locoSpeed=0;
int dir;
int last_locoSpeed=0;
int last_dir;
int dirPin = 12;
int FPin[] = { 8,9,10,11};
int maxF =3;
int locoAdr=40;   // this is the (fixed) address of the loco

// buffer for command
struct Message {
   unsigned char data[7];
   unsigned char len;
} ;

#define MAXMSG  2
// for the time being, use only two messages - the idle msg and the loco Speed msg

struct Message msg[MAXMSG] = { 
    { { 0xFF,     0, 0xFF, 0, 0, 0, 0}, 3},   // idle msg
    { { locoAdr, 0x3F,  0, 0, 0, 0, 0}, 4}    // locoMsg with 128 speed steps
  };               // loco msg must be filled later with speed and XOR data byte
                                
int msgIndex=0;  
int byteIndex=0;


//Setup Timer2.
//Configures the 8-Bit Timer2 to generate an interrupt at the specified frequency.
//Returns the time load value which must be loaded into TCNT2 inside your ISR routine.
void SetupTimer2(){
 
  //Timer2 Settings: Timer Prescaler /8, mode 0
  //Timmer clock = 16MHz/8 = 2MHz oder 0,5usec
  TCCR2A = 0;
  TCCR2B = 0<<CS22 | 1<<CS21 | 0<<CS20; 

  //Timer2 Overflow Interrupt Enable   
  TIMSK2 = 1<<TOIE2;

  //load the timer for its first cycle
  TCNT2=TIMER_SHORT; 

}

//Timer2 overflow interrupt vector handler
ISR(TIMER2_OVF_vect) {
  //Capture the current timer value TCTN2. This is how much error we have
  //due to interrupt latency and the work in this function
  //Reload the timer and correct for latency.  
  // for more info, see http://www.uchobby.com/index.php/2007/11/24/arduino-interrupts/
  unsigned char latency;
  
  // for every second interupt just toggle signal
  if (every_second_isr)  {
     digitalWrite(DCC_PIN,1);
     every_second_isr = 0;    
     
     // set timer to last value
     latency=TCNT2;
     TCNT2=latency+last_timer; 
     
  }  else  {  // != every second interrupt, advance bit or state
     digitalWrite(DCC_PIN,0);
     every_second_isr = 1; 
     
     switch(state)  {
       case PREAMBLE:
           flag=1; // short pulse
           preamble_count--;
           if (preamble_count == 0)  {  // advance to next state
              state = SEPERATOR;
              // get next message
              msgIndex++;
              if (msgIndex >= MAXMSG)  {  msgIndex = 0; }  
              byteIndex = 0; //start msg with byte 0
           }
           break;
        case SEPERATOR:
           flag=0; // long pulse
           // then advance to next state
           state = SENDBYTE;
           // goto next byte ...
           cbit = 0x80;  // send this bit next time first         
           outbyte = msg[msgIndex].data[byteIndex];
           break;
        case SENDBYTE:
           if (outbyte & cbit)  { 
              flag = 1;  // send short pulse
           }  else  {
              flag = 0;  // send long pulse
           }
           cbit = cbit >> 1;
           if (cbit == 0)  {  // last bit sent, is there a next byte?
              byteIndex++;
              if (byteIndex >= msg[msgIndex].len)  {
                 // this was already the XOR byte then advance to preamble
                 state = PREAMBLE;
                 preamble_count = 16;
              }  else  {
                 // send separtor and advance to next byte
                 state = SEPERATOR ;
              }
           }
           break;
         case RAILCOM:
           digitalWrite(DCC_PIN,1);
           delayMicroseconds(26);
           digitalWrite(DCC_PWM,0);
           digitalWrite(DCC_PIN,0);
           digitalWrite(DCC_BRAKE,1);
           delayMicroseconds(4);
           digitalWrite(DCC_PWM,1);
           delayMicroseconds(464);
           digitalWrite(DCC_PWM,0);
           delayMicroseconds(4);
           digitalWrite(DCC_BRAKE,0);
           delayMicroseconds(12);
           digitalWrite(DCC_PIN,1);
           break;
     }   
 
     if (flag)  {  // if data==1 then short pulse
        latency=TCNT2;
        TCNT2=latency+TIMER_SHORT;
        last_timer=TIMER_SHORT;
     }  else  {   // long pulse
        latency=TCNT2;
        TCNT2=latency+TIMER_LONG; 
        last_timer=TIMER_LONG;
     }  
  }

}

void setup(void) {
  
  //Set the pins for DCC to "output".
  pinMode(DCC_PIN,OUTPUT);   // this is for the DCC Signal
  
  pinMode(DCC_PWM,OUTPUT);   // will be kept high, PWM pin
  digitalWrite(DCC_PWM,1);
  
  pinMode(DCC_THERM, INPUT);
  digitalWrite(DCC_THERM,1); //enable pull up
  
  pinMode(dirPin, INPUT);
  digitalWrite(dirPin, 1);  //enable pull-up resistor !!
  
  for (int i=0 ; i<=maxF; i++){
     pinMode(FPin[i], INPUT);
     digitalWrite(FPin[i], 1);  //enable pull-up resistor
  }  
 
  read_locoSpeed_etc();
  assemble_dcc_msg();
  
  //Start the timer 
  SetupTimer2();   
  
}

void loop(void) {

  delay(200);
  
  if (read_locoSpeed_etc())  {
     // some reading changed
     // make new dcc message
     assemble_dcc_msg();
     ;
  }
 
}


boolean read_locoSpeed_etc()  {
   boolean changed = false;
   // read the analog input into a variable:
   
   // limit range to 0..127
   locoSpeed = (127L * analogRead(AN_SPEED))/1023;
   
   if (locoSpeed != last_locoSpeed) { 
      changed = true;  
      last_locoSpeed = locoSpeed;
   }

   dir = digitalRead(dirPin);
   
   if (dir != last_dir)  {  
      changed = true;  
      last_dir = dir;
   }

   return changed;
}

void assemble_dcc_msg() {
   int i;
   unsigned char data, xdata;
   
   if (locoSpeed == 1)  {  // this would result in emergency stop
      locoSpeed = 0;
   }
   
   // direction info first
   if (dir)  {  // forward
      data = 0x80;
   }  else  {
      data = 0;
   }
   
   data |=  locoSpeed;
     
   // add XOR byte 
   xdata = (msg[1].data[0] ^ msg[1].data[1]) ^ data;
   
   noInterrupts();  // make sure that only "matching" parts of the message are used in ISR
   msg[1].data[2] = data;
   msg[1].data[3] = xdata;
   interrupts();

}

The original code is here, for comparison:

http://www.oscale.net/?q=en/simpledcc

 

As you see, it uses a state machine. I've added a 4th state and the corresponding entry in the case sequence, but as far as I can tell, the code never reaches the RC state. The test rig was made up of an Arduino Uno and the LMD18200 chip, with associated parts, viewed with a Bitscope Micro on a laptop. I didn't take any screenshots as I couldn't get a stable image on screen (probably by my inexperience with the Bitscope, I only got it working the day before)

 

Note that this code WILL NOT WORK on the Due, as it doesn't have the various timers named in the code. So it needs to be ported, but only after RC is working on the Uno!


  • Thanks x 1

#9 keybuk

keybuk

    Registered Member


  • Members
  • Pip
  • 16 posts
  • LocationSan Francisco, CA

Posted 16 June 2017 - 06:09

Aha!

 

While you added a new RAILCOM state to your switch, and that looks okay (but more below), you haven't actually done anything to push the code into that state, or out again.

 

In the previous case, there's a change back to PREAMBLE:

state = PREAMBLE;

You'll want to change that to RAILCOM now...
 
You'll also need to send at least one more 1-bit before your cut out because otherwise the packet end bit is missing (looks like the code you're using just sweeps that all up inside the preamble)
 
Given your code, you could get away with doing that with some extra digitalWrite/delay
 
At the end of your RAILCOM state, you need to to switch to the PREAMBLE state.. also you probably don't want the latency code below to execute, so I'd change that last "break" to a return
state = PREAMBLE;
return;
 
So here's the giant BUT … you're doing all that delay code inside an ISR; the way the code you're modifying works is more of a tick/tock to generate the inverting DCC signal - and then for railcom, you're kinda just hanging a "Do not Disturb" on the door and getting busy
 
That's "okay for now", except you're not going to get maid service… by which I mean, no other interrupts will fire, and that probably means delayMicroseconds() won't work, since it's using an ISR for TIMER0
 
To fix that, re-enable interrupts at the start of your state:
 
case RAILCOM:
sei();
 
That should get you at least working!

  • Thanks x 1

#10 keybuk

keybuk

    Registered Member


  • Members
  • Pip
  • 16 posts
  • LocationSan Francisco, CA

Posted 16 June 2017 - 06:26

BTW, one does need all 3 input pins on the 18200 to achieve RailCom compatibility.

 
Answering separately:
 
A. It depends.
 
You need the DIRECTION pin obviously, toggling that back and forth is how we generate DCC signals using this particular H-Bridge.
 
 
But as to the other two… This is one of those moments where I have to use the words "read the data sheet for the LMD18200" but until you've read enough of these to understand them, that's pretty unhelpful, this is the important bit:

t6KJHrF.png

 

If you squint a bit, you can see that they actually all work together.

 

For RailCom, you don't just want the Bridge "off" you actually want the track wires shorted together into a closed loop - if the bridge leaves them floating, there's no circuit for the train's reply to come back on (unless the detector you're using puts one in).

 

When PWM is LOW, as you can see, DIRECTION no longer matters - but BRAKE still does, and it dictates whether the track is shorted at source, at sink, or left disconnected.

 

But if you just use BRAKE, and not PWM, then DIRECTION matters a bit - since it dictates whether the track is shorted at source or sink (basically, if you were to measure the track compared to your booster GND, does it look like +15V or GND)

 

SO...

 

If you know your RailCom Detector is going to provide a circuit for the train's current, you can just tie BRAKE to GND electrically, and set PWM LOW to generate the cut-out.

 

If you want to be a bit more correct, but don't mind that your outputs may be tied to Source, you can just tie PWM to VSS electrically, and set BRAKE HIGH to generate the cut-out.

 

If you actually care, and want the outputs set to Sink, you can still tie PWM to VSS, but you should set BRAKE HIGH and DIRECTION LOW for the cut-out.

 

 

Finally if you want to deal with current sensing, thermal overload, then you need the PWM back again as a pin, since you want to deal with those by PWM LOW, BRAKE HIGH, to kill all connection to the track.

 

 

Random aside - I just noticed in your code that you never turn PWM back on again, you need the power back on after the RailCom cutout.

 

Also I missed a line in my last block:

 

case RAILCOM:

state = NOTHING; // add this to make nothing happen until RAILCOM is done
sei();


#11 Dutch_Master

Dutch_Master

    Member


  • Members
  • PipPip
  • 5,249 posts

Posted 16 June 2017 - 13:28

Many thanks again for your input. I had a suspicion the RailCom case wasn't jumped to when it should, but couldn't see what went wrong as I'm not a coder. I'll try to implement your suggestions over the weekend, provided it's not too hot :heat: (mini-heatwave predicted, I hate warm weather :mad: )

 

I'm afraid I still disagree with your position on enabling RailCom with the LMD18200 chip*. Below is the data sheet for it, and as I've studied electronics some 30-odd years ago I'd like to think I can read and interpret their data correctly ;)  (Oi! No boasting D_M! :punish: )

Attached File  lmd18200_mosfet_H_bridge.pdf   686.38KB   6 downloads

 

If you scroll down to page 8 you'll find the truth-table on the inputs and what state the outputs will be at any combination. You are correct that for RC to work, the chip needs to 'short circuit' the outputs. Now, this can be done either on the positive or the GND rail, according to the aforementioned table. I choose the GND option. For this, Brake and PWM need to be logic 1 (high), while DIR is logic 0 (low). As for the timings, I've used this chap's work (German only, sorry):

https://www.opendcc....om/railcom.html

 

 I quite like his idea of a short 'neutral' period, to prevent shorting out districts when trains traverse the isolations during a cutout period.

 

*but I get the feeling we could be singing the same song from a different score. Hmmmm.... :this:

 

Anyway, I'll see if I can get the proposed changes in the code implemented soon, and see what it does on an actual RC enabled decoder (that's known to work, mind :P )



#12 keybuk

keybuk

    Registered Member


  • Members
  • Pip
  • 16 posts
  • LocationSan Francisco, CA

Posted 16 June 2017 - 21:55

I'm afraid I still disagree with your position on enabling RailCom with the LMD18200 chip*. Below is the data sheet for it, and as I've studied electronics some 30-odd years ago I'd like to think I can read and interpret their data correctly ;)  (Oi! No boasting D_M! :punish: )

attachicon.giflmd18200_mosfet_H_bridge.pdf

 

If you scroll down to page 8 you'll find the truth-table on the inputs and what state the outputs will be at any combination. You are correct that for RC to work, the chip needs to 'short circuit' the outputs. Now, this can be done either on the positive or the GND rail, according to the aforementioned table. I choose the GND option. For this, Brake and PWM need to be logic 1 (high), while DIR is logic 0 (low). As for the timings, I've used this chap's work (German only, sorry):

https://www.opendcc....om/railcom.html

 

Yeah, we're agreeing on the inputs for correct behavior RailCom needs Brake+Direction. Proper Off needs Brake+PWM.

 

But since most detectors out there are a shunt resistor across DCC-2, and taking power from DCC-1, they're actually making a circuit all of their own so it doesn't matter sooooo much in practice - which could lead to an apparently working (but incorrect) booster if you just used Brake+PWM.

 

It all falls down when someone makes a hall-effect RailCom detector (on the principle of Show Me Where It Says I Can't Do that).



#13 Dutch_Master

Dutch_Master

    Member


  • Members
  • PipPip
  • 5,249 posts

Posted 16 June 2017 - 22:08

I've changed the code, so before uploading, is this what you had in mind? Partial snippet, no need to copy over everything again :P

 case SENDBYTE:
           if (outbyte & cbit)  { 
              flag = 1;  // send short pulse
           }  else  {
              flag = 0;  // send long pulse
           }
           cbit = cbit >> 1;
           if (cbit == 0)  {  // last bit sent, is there a next byte?
              byteIndex++;
              if (byteIndex >= msg[msgIndex].len)  {
                 // this was already the XOR byte then advance to preamble
                 state = PREAMBLE;
                 preamble_count = 16;
              }  else  {
                 // send separator and advance to next byte
                 state = RAILCOM ;
              }
           }
           break;
         case RAILCOM:
         state = NOTHING; // make nothing happen until RAILCOM is done
          sei();
           digitalWrite(DCC_PIN,1);
           delayMicroseconds(26);
           digitalWrite(DCC_PWM,0);
           digitalWrite(DCC_PIN,0);
           digitalWrite(DCC_BRAKE,1);
           delayMicroseconds(4);
           digitalWrite(DCC_PWM,1);
           delayMicroseconds(464);
           digitalWrite(DCC_PWM,0);
           delayMicroseconds(4);
           digitalWrite(DCC_BRAKE,0);
           delayMicroseconds(12);
           digitalWrite(DCC_PIN,1);
           state = PREAMBLE;
        return;
     }   


#14 keybuk

keybuk

    Registered Member


  • Members
  • Pip
  • 16 posts
  • LocationSan Francisco, CA

Posted 16 June 2017 - 23:05

No not quite, 

 

the *OTHER* state change in SENDBYTE:

 

if (byteIndex >= msg[msgIndex].len) {

// this was already the XOR byte then advance to preamble
state = RAILCOM;
preamble_count = 16;
} else {
// send separator and advance to next byte
state = SEPARATOR ;
}


  • Thanks x 1

#15 Dutch_Master

Dutch_Master

    Member


  • Members
  • PipPip
  • 5,249 posts

Posted 16 June 2017 - 23:16

Ah, thx. As said, I'm not a coder, so learning lots here :yes:

 

It now looks like this, correct?

case SENDBYTE:
           if (outbyte & cbit)  { 
              flag = 1;  // send short pulse
           }  else  {
              flag = 0;  // send long pulse
           }
           cbit = cbit >> 1;
           if (cbit == 0)  {  // last bit sent, is there a next byte?
              byteIndex++;
              if (byteIndex >= msg[msgIndex].len) {
                // this was already the XOR byte then advance to railcom cutout
                state = RAILCOM;
                preamble_count = 16;
              } else {
                // send separator and advance to next byte
              state = SEPARATOR ;
               }
           }
           break;
         case RAILCOM:

Edited by Dutch_Master, 16 June 2017 - 23:17 .


#16 keybuk

keybuk

    Registered Member


  • Members
  • Pip
  • 16 posts
  • LocationSan Francisco, CA

Posted 18 June 2017 - 01:27

Yeah that should work


  • Thanks x 1

#17 Dutch_Master

Dutch_Master

    Member


  • Members
  • PipPip
  • 5,249 posts

Posted 18 June 2017 - 02:37

OK, thx! If my brain doesn't melt in the mini-heatwave we have now, I'll give it a go shortly :yes:



#18 Dutch_Master

Dutch_Master

    Member


  • Members
  • PipPip
  • 5,249 posts

Posted 19 June 2017 - 01:02

Regret to report it's not working. I had to comment out the "state = nothing" line, otherwise it wouldn't compile. Put power on it, with aforementioned loco, but no success. Given the time of day and heat still lingering in the house ( :heat: ) I didn't check what exactly went wrong, that'll have to wait until I can set up the test environment again (this was a quick test, w/o my BitScope tool) but for that the house needs to cool down quite a bit. Meaning I'll get back to it in time :yes:



#19 Dutch_Master

Dutch_Master

    Member


  • Members
  • PipPip
  • 5,249 posts

Posted 14 September 2017 - 00:14

Time to dust off this thread. I've tweaked the design a little quite a bit since the last update:

 

Main PCB

Attached File  dcc_rc_booster.pdf   77.35KB   0 downloads

Keypad

Attached File  keypad.pdf   74.84KB   1 downloads

 

Main reason for doing so is that the Due uses 3V3 logic, while the LCD device is 5V. At the same time I transferred the leds, LCD and rotary encoders to the keypad page as well, using a connector. This helps with the overview of the design :)  For connectivity a 3rd sheet was added, containing a LocoNet bus and some X-Bus connectors. I now realise I've made a mistake on the LN design, I should rectify that later. Actually, I already have, in a new design. (yes, I haven't been completely idle this heatwave summer! ;) See below)

 

Thinking about the design, I thought I may as well bring the whole lot into C21, using an 7" LCD, with touch screen.

 

You'll find similarities between these designs:

 

main circuit

Attached File  DCC_lcd_booster.pdf   101.31KB   1 downloads

LCD and rotary encoder

Attached File  lcd_encoder.pdf   31.33KB   2 downloads

 

First thing you'll notice is that it has a lot more connectors! A pair for LocoNet, no less then 3 RJ12 buses for XpressNet, the Lenz CDE connector to connect their boosters to, as well as the 4 pin terminal for the X-Bus. Also included is the Lenz RS feedback bus. Both the RS bus and XpressNet are technically RS485 connections, so they're courtesy of the SP3485 chip. I (ab)used half of another chip to get a suitable idle state for LocoNet. Another "exotic" chip is the Si9986 driver. Normally you'd find it in decoders, but here I use it to drive the RailSync pins for both LocoNet and X-Bus. The 2x 47 Ohm resistors prevent a catastrophic short killing off the chip. I'd considered making a full-blown short circuit protection for it, but resistors are cheaper to replace ;) Logic gates U7 are EXNOR gates and used to provide a complementary pair of signals to drive the Si9986 w/o differences in timing (propagation delay). One port is an inverter, the other isn't. Exercise for the reader: finding out which one does what :P  Transistor pairs Q1 and Q2 are current sources (limited to ca 15mA) as specified in the LocoNet design document from Digitrax. MOSFET Q3 (SOT-32 package) switches the relay for the programming track, while Q4 controls the LCD back-light on the other PCB.

 

The LCD is a 7" model from the TFT01 series. I got mine from a Chinese company Elecfreaks some years ago, but apart from experimenting (which failed) not much was done with them. They include a touch screen chip, SD card slot and place for a Flash chip (user supplied, and soldered, so not really an option for Joe Average modeller). For obvious reasons I didn't connect the Flash pins, but the rest is available to the Aduino Due. I foresee the SD-card slot to have a role in transferring data between this device and a PC, like decoder definition lists, switch lists, images, etc. The Due has a fairly powerful core at breakneck speeds (well, for SBC's that is ;) ) so I suspect it has enough room for additional functionality.

 

I've also created full-size PCB's for these projects, but due to the limitations on RMweb attachments I can't post these here. If there is interest, I can put up all the project files on my Gdrive space, which includes the PCB files.

 

As I've alluded to before in this thread, I ain't no coder! So, any code that makes all this work is most likely not coming from me :no:  But feel free to cobble this together and give it a whirl. :yes:

 

PS: other projects commenced this summer include a RailCom-compatible current detector for LocoNet and versatile DCC reverser (for loop and wye), using current detection and/or external signals (switches, etc). Both fit on a standard size Eurocard PCB (100x160mm) and minimise the use of SMD parts so modellers have a chance of building one as a kit. The current detector needs code (Arduino Pro Mini) but as expected, not by me :no:

 

No progress has been made with the code discussed above, inclination just wasn't there. That may or may not return at any given time. We'll see.









Google Ads are only seen by non-members of RMweb - Create an RMweb account and you'll only receive modelling ads.