Suggestions for decoding serial data that is encoded using pulse widths

Status
Not open for further replies.

kiwi64ajs

Member
Hi Guys,

I'm wanting add support for the Teensy 3.1 to an Arduino library I wrote based on AVRs that decodes the "Digital Command Control" (DCC) protocol used on Digital Model Railroads.

The library is here: https://github.com/mrrwa/NmraDcc

The AVR routine works by sensing the rising edge using the INTx input, which then grabs the current Timer0 Count, adds a count of 80us and sets the Timer0 CompareB value to that count and then uses the CompareB interrupt handler to sample the input to check if it is HIGH or LOW which corresponds to a bit value of 1 or 0.

For DCC a pulse width of 116us is a "1" and a pulse width of 56us is a "0", sampling the input 80us (or so) after the rising edge will let you sense the "1" or "0" values.

The ASCII Art diagram below should hopefully help describe the logic

Code:
//------------------------------------------------------------------------
// DCC Receive Routine
//
// Howto:    uses two interrupts: a rising edge in DCC polarity triggers INTx
//           in INTx handler, Timer0 CompareB with a delay of 80us is started.
//           On Timer0 CompareB Match the level of DCC is evaluated and
//           parsed.
//
//                           |<-----116us----->|
//
//           DCC 1: _________XXXXXXXXX_________XXXXXXXXX_________
//                           ^-INTx
//                           |----87us--->|
//                                        ^Timer-INT: reads zero
//
//           DCC 0: _________XXXXXXXXXXXXXXXXXX__________________
//                           ^-INTx
//                           |----------->|
//                                        ^Timer-INT: reads one
//           
//------------------------------------------------------------------------

I've used the AVRs for many years and am pretty familiar with their interrupts and timer peripherals, but I'm a total newbie to the Teensy 3.x chips, so I'm wondering how best to implement this type of logic using the ARM interrupts and timers?

With the reading I've done so far I wondered about using the attachInterrupt() to sense the rising edge and then attaching the IntervalTimer to delay the 80us before sampling the input to determine if I'm receiving a "1" or a "0".

However I'm wondering calling the IntervalTimer.begin() from the attachInterrupt() handler is going to be ok, and if I might run into issues with repeatedly doing the IntervalTimer begin/end for each rising edge.

I also see reference to a FlexTimer but not found any good descriptions for it yet so is there the equivalent of the AVRs ICP InputCapture on these Teensy 3.x devices that would capture the value of a Timer/Counter on a rising edge, fire an interrupt that lets me set a compare target value, then fire another interrupt on a compare match, so I can sample the input level?

Any suggestions or links for further reading would be greatly appreciated?

Regards

Alex Shepherd
 
Input capture would be the very best way to do this. Every timer channel supports 16 bit input capture. The timers can clock at 48 MHz, so you can get very precise measurement this way!

You might look at the FreqMeasure, FreqMeasureMulti and PulsePosition libraries for examples of using the input capture.
 
Input capture would be the very best way to do this. Every timer channel supports 16 bit input capture. The timers can clock at 48 MHz, so you can get very precise measurement this way!

You might look at the FreqMeasure, FreqMeasureMulti and PulsePosition libraries for examples of using the input capture.

Thanks for the quick response Paul.

I had a browse of those libraries and also tried to make sense of the datasheet for those processors but as you already know, the datasheet is rather large so it's going to take a while to digest that detail.

Actually, the more popular approach to decoding DCC packets in the Model Railroad world is to sample the DCC signal at 22us intervals as per the logic described here: http://siliconjunction.blogspot.co.nz/2013/07/an-interrupt-based-dcc-decoder.html When you get 2-3 successive HIGH samples before a LOW sample you have a "1" and where you get 4 or more successive HIGH samples you have a "0"

So after reading through the libraries you suggested, I now get a sense for how I can probably do it by sensing the rising edge and then sample after 80us using a timer to see if is a HIGH or LOW. However, given the 22us sampling method will probably be quite easy to implement with the IntervalTimer class, I think I might just try that method first.

While the 22us sampling uses more CPU all the time, the Teensy 3.x probably has more than enough CPU cycles to keep up with that and other things I'd like to also experiment with, like playing Audio using your Audio Adaptor Board (https://www.pjrc.com/store/teensy3_audio.html)

Regards

Alex Shepherd
 
Status
Not open for further replies.
Back
Top