Skip to content

Atomic channel data updates#3363

Merged
pkendall64 merged 4 commits intoExpressLRS:masterfrom
pkendall64:next/atomic-channel-data-updates
Oct 27, 2025
Merged

Atomic channel data updates#3363
pkendall64 merged 4 commits intoExpressLRS:masterfrom
pkendall64:next/atomic-channel-data-updates

Conversation

@pkendall64
Copy link
Copy Markdown
Collaborator

Investigation

Investigating #3347, in Full resolution modes the channel data each channel is updated atomically as they are 32bit values aligned in an array. There is a multi-update problem with hybrid mode, which the PR addresses (see 1 below).

The code that takes the channels and converts them to SBUS output operates on each channel individually and each channel is read only one time in the code that builds the SBUS frame. So each channel is read atomically. The SBUS frame is only sent to the serial port once it is fully built.

Unless of course the compiler is generating multiple reads on the channelData array while forming the SBUS frame! Which is what it's doing.

Solution(s)

  1. Change the OTA channel generation so each channel is only changed once, i.e. not using the slot for intermediate data.
  2. In the serial output driver, we create a local copy of the global ChannelData and pass the pointer to the local copy down to the serial protocol drivers which may/do access each channel slot multiple times.
    Fixes SBUS Periodic glitch every15-20 seconds #3347

We do this because the generated code for serial output may access each
channel multiple times and the global buffer may be modified in-between accesses
@pkendall64 pkendall64 force-pushed the next/atomic-channel-data-updates branch from 547658e to 2d163ce Compare October 17, 2025 21:07
@CapnBry
Copy link
Copy Markdown
Member

CapnBry commented Oct 24, 2025

I've run into another problem with the direct ChannelData access used everywhere

When the data comes in and gets loaded into ChannelData TXModuleEndpoint::RcPacketToChannelsData(), there is a callback that then loads the headtracking data onto it. There's a brief bit of time between where one sets the channel values and the other overrides it. Sometimes the RC OTA packet is sent between these two, meaning that the channel values flicker back to the handset data for a packet, then back to the headtracker value for the next packet.

If I just hack into the source that the TXModuleEndpoint::RcPacketToChannelsData() makes a local copy, then calls the callback with that array, then copies the value into the channeldata array after overriding, I never see the flicker back, indicating that sometimes this concurrency issue is happening.

I'm testing with the headtrack data being fed in from the main tx_main loop to take the backpack out of the equation, putting the headtrack data in CH0 so it is sent every packet, and then just having the RX print a debug line if CH0 isn't the headtrack value. At 500Hz or 100Full I very rarely see one but at 50Hz I see one maybe every 10 seconds on average.

This needs to be addressed as well, but the maybe bigger issue is why the TOCK is going off right as the channel data is coming in from the handset. The sync is usually ~100us before transmit time but it seems there's enough jitter in either the handset or us that we're managing to transmit right as the data is arriving so our actual end-to-end latency may be fairly poor if our ETX sync is so bad.

@CapnBry
Copy link
Copy Markdown
Member

CapnBry commented Oct 27, 2025

I've added 29fe04f which has a new callback in Handset to allow overrides of the channel values before they are published. PPM and CRSF handsets are updated to operate on a local copy of the channels, which are then overridden, and then the RCDataReceived() method is called to "publish" them. Also note that PPM handset would update the arm flag before publishing and CRSF would update the arm flag after publishing, so I've made this consistent so they both update the arm flag then publish. I'm not sure which way is the most correct (perhaps the other way?) but it should be one or the other.

The changes to devBackpack.cpp are pretty rough, but this is rewritten in #3373 so I didn't want to spend much time on it only to cut it back out again after this is merged.

Let me know if you'd prefer I revert my changes and submit them another way. I didn't want to have to paste such a large diff here.

@pkendall64
Copy link
Copy Markdown
Collaborator Author

I've added 29fe04f which has a new callback in Handset to allow overrides of the channel values before they are published. PPM and CRSF handsets are updated to operate on a local copy of the channels, which are then overridden, and then the RCDataReceived() method is called to "publish" them. Also note that PPM handset would update the arm flag before publishing and CRSF would update the arm flag after publishing, so I've made this consistent so they both update the arm flag then publish. I'm not sure which way is the most correct (perhaps the other way?) but it should be one or the other.

The changes to devBackpack.cpp are pretty rough, but this is rewritten in #3373 so I didn't want to spend much time on it only to cut it back out again after this is merged.

Let me know if you'd prefer I revert my changes and submit them another way. I didn't want to have to paste such a large diff here.

All good. I'm ok with this since it's effectively temporary till #3373 is merged.

Copy link
Copy Markdown
Member

@CapnBry CapnBry left a comment

Choose a reason for hiding this comment

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

Code looks good and tested on Nomad TX / BAYCK Dual RX at 1000Hz (both dual core MCU). I don't have an SBUS tester I trust enough to see if one frame has a corrupted value, but for sure the old code had possible concurrency problems.

@pkendall64 pkendall64 merged commit fdda29d into ExpressLRS:master Oct 27, 2025
50 checks passed
@pkendall64 pkendall64 deleted the next/atomic-channel-data-updates branch October 27, 2025 21:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SBUS Periodic glitch every15-20 seconds

3 participants