Protocol timing diagram from the datasheet

I recently ran into a problem that might be interesting for others. I wanted to use multiple HX717 load cell ADCs in one of my projects. These chips use a very simple serial protocol: pulses on the clock line shift out the result of the previous conversion, and a new conversion is triggered immediately afterward. The number of additional pulses after the 24-bit result defines the gain and the channel for the next conversion.

Normal data transfer

This protocol makes it very easy to connect multiple ADCs to the same clock line while reading data from multiple sensors simultaneously. My approach was to generate the clock using a timer. A second channel of that timer triggers a DMA transfer. All data lines are connected to the same GPIO port, which makes it possible to use DMA to capture all inputs in parallel and store them in memory efficiently.

What I didn’t know is that there is an undocumented behavior, or perhaps a feature, built into the protocol. After triggering a conversion, the data line goes low when the result is ready. This allows you to trigger, for example, on a falling edge to start reading the data.

However, what is not documented is what happens if you don’t read the data. After approximately 3 ms, the device generates a pulse of about 160 µs, which repeats every 3 ms. You can interpret this as a kind of “Hey, I still have data you requested” signal. When using a single sensor, this can be helpful if you miss the initial falling edge for some reason. But when using multiple HX717s, this behavior becomes problematic.

Reminder pulses after result is ready

Another complication is that conversion time is not constant. I measured conversion times ranging from approximately 500 µs to 3500 µs. This introduces jitter in the first falling edge, meaning each HX717 becomes ready at a slightly different time. As a result, it’s not practical to trigger on the falling edge of a single device when multiple devices are used in parallel.

To address this, I initially tried using a fixed timing approach. The driver triggers conversions at a constant frequency to achieve a steady output rate. I used a trigger frequency of 290 Hz, which is below the specified maximum of 320 Hz. However, some conversions occasionally took longer than expected. In a setup with three HX717 devices, this meant that one device could still be busy while the others were already finished.

Occasional longer conversion time

To handle this, I added a check to ensure that all data lines had gone low (indicating “data ready”) before issuing the next clock sequence. This helped avoid reading incomplete data, but it also exposed the interaction with the periodic “reminder” pulses, which can interfere with edge-based detection if not handled carefully.

Besides occasionally skipping a conversion, this approach works in most cases, but there is an important edge case.

If a conversion is skipped because another sensor is not ready yet, the timing can align such that the clock pulses are sent exactly during one of those periodic “reminder” pulses on the data line. And that’s where things break down.

Edge case which leads to the problem

The device does not handle this situation well.

Initially, I assumed it would simply return the data as usual, since from a protocol perspective the sequence still appears valid. However, that’s not what happens. Instead, the device shifts out invalid data and then pulls the data line high for approximately 15 ms. This delays subsequent conversions and introduces corrupted samples, which must be detected and filtered out.

Unwanted behavior when triggered during reminder pulse

I considered several potential solutions, but ruled them out before implementation:

  • Extending the first high pulse to >160 µs
    • Not viable. The protocol only allows a maximum high time of 50 µs.
    • Pulses of around 80 µs already trigger a power-down condition.
  • Adding discrete logic to combine the data signals
    • Would require additional hardware components.
    • There are still timing scenarios where this approach could run into the same issue.
  • Polling the state of the data lines
    • Inefficent.
    • If the state is sampled just before the rising edge of the periodic pulse, it could lead to the same problem.

I believe there is no truly clean solution to this problem unless each HX717 runs on its own isolated interface. Since I wanted to operate up to six devices simultaneously in this project, that was not an option due to hardware limitations.

As I don’t have strict timing requirements for precise filtering and still had some processing resources available, I settled on the following approach:

I added a falling-edge interrupt for each data signal. The interrupts are disabled while the clock sequence is being sent, and all flags are cleared before re-enabling them. Inside the interrupt handler, I store a timestamp for each device and check whether the time difference between the current timestamp and the stored one is below a threshold of 2.8 ms.

This ensures two things:

  • a) that a falling edge has occurred after the clock sequence, and
  • b) that there is still some margin before the device triggers its periodic data-line pulse.

Based on this, the system only sends the next clock sequence when all devices are within a valid timing window.

Timing with 5 HX717 connected

In theory, this approach could lead to a situation where the system never finds a valid window to send a clock sequence. In practice, however, this is extremely unlikely due to independent clock drift and conversion-time jitter across devices.

In real-world operation, the results are solid. The driver achieves an average sample rate of around 316Hz, with the lowest observed rate dropping to 232 Hz over a five-minute measurement period.