Skip to content

Conversation

@mikesmitty
Copy link

@mikesmitty mikesmitty commented Jul 9, 2025

Hey, I was looking at adding some Railcom-based block detection to my layout and I saw STM32 didn't have support for the cutout yet. I don't have other NUCLEO boards to test with, but here's the results from my NUCLEO-F446RE on my scope. Overall cutout window clocks in at 468us, right about in the middle of the spec (min/max: 454/488) and same for the initial delay, 30us (spec min/max: 26/32)

stm32-full-railcom-cutout
stm32-initial-railcom-cutout

@Asbelos
Copy link
Contributor

Asbelos commented Jul 9, 2025

The cutout on your scope pic appears to start at approximately the point where it should actually end. This may be simply a HiGH/LOW swap but I'm not a stm32 timer expert.

@mikesmitty
Copy link
Author

Oh, you're right. I think some bits of the XOR byte are getting cut off. I'll take a look at that

@mikesmitty
Copy link
Author

mikesmitty commented Jul 9, 2025

Ok, I fixed that timing issue and made the brake activation more sensible while I was at it. startRailcomTimer is called earlier than I realized (and previously with a hardcoded pin number). Now it's doing the full XOR byte, terminating bit, and the ~29us delay before the cutout. Some updated snapshots:
stm32-railcom-final-one-initial
stm32-final-one-full-window
And a pair with a zero final bit in the XOR byte:
stm32-railcom-final-zero-initial-delay
stm32-final-zero-full-window

@mikesmitty mikesmitty force-pushed the ms/stm32-railcom-cutout branch from 86d977f to bb1ce4a Compare July 9, 2025 23:59
Copy link
Contributor

@Asbelos Asbelos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no need to pass or handle isMain because Railcom does not apply to a prog track. So you can drop a lot of your duplicated prig track code but the track manager setTrackBrake you added needs to be a bit smarter at braking all MAIN and PROG-if-Joined tracks.

The AVR was hard coded as 9 because that's the only pin that could work.. so it could be left out of the call.
The lastBit shouldnt be an issue because we are always starting the timer in a known DCC signal state. (see waveform ... its part of the zero bit end-of-packet marker.

I suspect this is easier than you imagined.

@mikesmitty
Copy link
Author

mikesmitty commented Jul 13, 2025

Oh, right on. Yeah, I guess Railcom has no need for the concept of a programming track. I fixed the setMainBrake issue and removed the duplicated timer and got rid of lastBit as well.

The timing in DCCWaveform didn't match the comments exactly so I had to change the logic around a little bit. It was calling startRailcomTimer() at the beginning of the final XOR bit hence the need for lastBit depending on the value. This new logic triggers it consistently for cases where the final XOR bit is 0 or 1. If my understanding of the DCCWaveform flow is correct it should consistently call startRailcomTimer() 58us before the start of the packet terminator bit.

This is unfortunately virtually guaranteed to break it on AVR targets, but I don't have one to test with and fix it unfortunately. I believe it should just be getting called 58us and 174us later than before, for 1 and 0 bits respectively. I think that 193 token in DCCTimerAVR.cpp can just be reduced to 19 and that should resolve it however.

@mikesmitty
Copy link
Author

This is the before and after I added the logic to differentiate based on the final XOR bit for clarity's sake. With the shorter delay everything is correct if the XOR ends with a 1:
railcom-short-one

However if it ends with a zero the packet end bit gets cut off:
railcom-short-zero

Using the longer delay with an XOR ending in zero allows for the terminating bit:
railcom-long-zero

If it ends with a one bit though, an extra preamble bit gets inserted before the cut:
railcom-long-one

@Asbelos
Copy link
Contributor

Asbelos commented Jul 14, 2025

Please be aware that the timing of the cutout call is based on the AVR High Accuracy waveform where the first timer must take into account that the dcc pin change does not take place immediately. I don't undetstand why you are interested on the previous xor bit because that is always followed by a separate zero bit marking the end of the packet.

@mikesmitty mikesmitty force-pushed the ms/stm32-railcom-cutout branch from 23a4ac5 to bea58a7 Compare July 27, 2025 01:49
@mikesmitty mikesmitty changed the base branch from master to devel_railcom3 July 27, 2025 01:50
@mikesmitty
Copy link
Author

mikesmitty commented Jul 27, 2025

Ok, I think I've figured out where the misunderstanding is coming from. I've been talking about and working from the master branch which has a slightly broken implementation with timing that revolves around the beginning of the last XOR bit and I'm guessing you've been working from the context of the devel branch which is timed around the end of the packet end bit as one might expect.

Also, as it's probably not reasonable to add support for one platform and break it entirely for existing users I bought myself a Mega for testing to make sure it wasn't broken.

All that said, here's the updated version rebased against the devel branch. It works precisely as it should on both Mega and my STM32 here.

I also increased the preamble count because with the railcom cutout it was only generating 12 preamble bits instead of the spec minimum 14, but there is likely a more proper way to do that.

@mikesmitty
Copy link
Author

STM32 output:
Screenshot 2025-07-26 at 9 54 36 PM
Screenshot 2025-07-26 at 9 56 39 PM

and Mega output:
Screenshot 2025-07-26 at 10 00 16 PM
Screenshot 2025-07-26 at 10 01 56 PM

@mikesmitty mikesmitty force-pushed the ms/stm32-railcom-cutout branch from bea58a7 to 7b17e7f Compare July 27, 2025 02:13
@mikesmitty mikesmitty changed the base branch from devel_railcom3 to devel July 27, 2025 02:29
@mikesmitty mikesmitty force-pushed the ms/stm32-railcom-cutout branch from 7b17e7f to 1c01dae Compare July 27, 2025 02:35
@Asbelos
Copy link
Contributor

Asbelos commented Jul 27, 2025

I'm not sure why you needed to change the preamble bits to 18....

The 12 bit preamble is the minimum without railcom and 14 is to allow space for the long cutout.

@habazut
Copy link
Contributor

habazut commented Jul 27, 2025

Why oh why, the norms have been messed with, again.
RP9.2, 2004:

The preamble to a packet consists of a sequence of "1" bits. A digital decoder must not
accept as a valid, any preamble that has less then 10 complete one bits, or require for
proper reception of a packet with more than 12 complete one bits. A command station
must send a minimum of 14 full preamble bits.

(not really space for railcom here)

RCN-211-3, 2023:

Eine Zentrale muss mindestens 17 Synchronbits senden.
Ein Decoder muss ein Paket mit 12 oder mehr Synchronbits empfangen können.
Ein Decoder darf ein Paket mit weniger als 10 Synchronbits nicht als gültig
anerkennen.
(...)
Das Paket-Endebit ist ein Einsbit und markiert das Ende des Datenpaketes.
Folgt ohne Unterbrechung das nächste DCC-Paket, darf dieses Bit bei den
Synchronbits des folgenden Paketes mitgezählt werden

So you need to read the RCN-211 careful and if the CS produces 16+1 one bits between checksum and new packet start that seems to be enough. We have 16+1 as default in our code.

That it now says 17 (16+1) and not 16 is probably due to the circumstance of handling multiprotocol together with railcom.

Harald.

@mikesmitty
Copy link
Author

Fair enough. I figured having non-Railcom decoders receive a max of 12 with Railcom enabled, only just meeting the decoder upper limit in S-9.2 would potentially cause compatibility issues, but I'll change that back

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants