Outputing a very specific squarewave for DCC

I want to make a simple dcc encoder (unlike the ledunino which does dcc decode), for a model railway/railroad dcc controller.

It needs to be able to send packets down a single wire where a binary 1 is a symetrical squarewave with a one wavelength period of 58 microseconds (f=17421Hz) and a binary 0 is twice as long at 116 microseconds (f=8620Hz). Both go between ±14v (that bit should be just a case of the correct driver circuit). Thing is , I don’t really want to expend to much arduino time generating these signals as the arduino needs to be able to respond to serial communication, do some calculation, create the data packets etc.

The packet itself contains 1111 1111 1111 110 <1Byte address> 0 <1 Byte Data> 0 <1 Byte Checksum> , obviously there can’t be any gaps in transmission between one bit and the next.

I need ideas as to what is the best way to generate the above signal, could some sort of audio chip do it? or would it be best to construct a pair of external oscilators to do the job with the arduino switching between them. Alternatively would it all be possible in software?

If anyone has any ideas/suggestions I’d be most greatful.

I would do it in software where a timer is used to generate in interrupt. The interrupt service routine (ISR) would toggle the output and set the time for the next period. So all in software with the minimum of overheads.

Agreeing with Mike - you may want to look at the SoftwareSerial libraries to see some examples of bit-banging serial comms . Add in a timer interrupt at three or four times the fastest bit-rate, and you should be in business.

Here's how I'd do it.

The foreground code takes the address, data, and checksum bytes, and stuffs them into a 6-byte array, already interleaved with the preamble and start bits. This simplifies the logic of the transmit code: it just sends whatever 41 bits you put in those 6 bytes.

The transmission is done by a state machine that transitions via a timer interrupt that occurs every 58 microseconds. It sets Current_state to the value in Next_state, then executes a switch() based on Current_state. The possible states and their actions are:


Set the output pin high. Set Next_state to ONE_LOW.


Set the output low. Check the next bit to be sent, and set Next_state accordingly. If all bits have been sent, set Next_state to GO_IDLE.


Set the output pin high. Set Next_state to ZERO_HIGH_B.


Set Next_state to ZERO_LOW_A.


Set the output low. Set Next_state to ZERO_LOW_B.


Check the next bit to be sent, and set Next_state accordingly. If all bits have been sent, set Next_state to GO_IDLE.


Set the output pin high. Reset the counter(s) used to step through the buffer. Set a flag for the foreground code to tell it transmission is complete. Set Next_state to IDLE_LOW.


Set the output pin high. Set Next_state to IDLE_LOW.


Set the output pin low. Set Next_state to IDLE_HIGH.

If you have lots of RAM to spare, you could save a few instructions in the interrupt routine by setting up an array of 41 bytes, and only storing 1 bit/byte. But even without doing that, the ISR code should be pretty small. Make sure that the code to change the output pin is the very first instruction in each block of code that changes it: that will minimize any jitter in the signal. You'll probably also want to access the output pin directly through the hardware register, to avoid the overhead associated with digitalWrite().


Oops - encoder only?
Much simpler - forget what I said about higher clock rates.

Cheers for your help guys, I am glad it will be a software problem rather than a hardware one. Yes it is an encode path only, 1 encoder to many decoders, which keeps things simple as the encoder can do all the timing. The timings period are +- 2us so that gives a bit of flexibility as well. I guess I'll knock up a basic ISR and send a known packet through it, and wire up the output to my computer soundcard (I have no 'scope unfortunately) to see roughly what comes out.

For DCC hardware there's no better place than :


There's loads of hardware circuits there.

-- Martyn