For loops, XORs, 2's complement in HX711 (24-bit ADC) library

The following code excerpt is from a HX711 (24-bit ADC) library that seems to be fairly popular (e.g., first hit from Google search on “HX711 Arduino library”).

It is supposed to read in 24 bits of data from the HX711. I don’t understand how it can work at all.

  1. The data array has three elements, but the for loop starts with index j = 3 (should start with 2). The XOR and “return” statements have it right (use data[2] for the eight most significant bits), so it seems that the eight most significant bits are lost (and written somewhere in memory where they shouldn’t be).

  2. The decrement code is in the for loop header “condition” position. Is that a valid use? How many times would for (byte j = 3; j–:wink: loop?

  3. The ^= (XOR) compound operator is not in the Arduino reference page. But I guess it works.

  4. The XOR compound operator strips the leading zero from the result. What’s the purpose of that? Doing that would change what should be a negative 8,388,608 (1 followed by 23 zeros) to zero, or would change a negative 1 (24 ones) to positive 8,388,607.

long HX711::read() {
	// wait for the chip to become ready
	while (!is_ready());

	byte data[3];

	// pulse the clock pin 24 times to read the data
	for (byte j = 3; j--;) {
		for (char i = 8; i--;) {
			digitalWrite(PD_SCK, HIGH);
			bitWrite(data[j], i, digitalRead(DOUT));
			digitalWrite(PD_SCK, LOW);
		}
	}

	// set the channel and the gain factor for the next reading using the clock pin
        // not pertinent to forum post, except that the for loop here looks correct  :) 
	for (int i = 0; i < GAIN; i++) {
		digitalWrite(PD_SCK, HIGH);
		digitalWrite(PD_SCK, LOW);
	}

	data[2] ^= 0x80;

	return ((uint32_t) data[2] << 16) | ((uint32_t) data[1] << 8) | (uint32_t) data[0];
}

but the for loop starts with index j = 3 (should start with 2).

Read the outermost for loop again.

If you doubt it, print out some intermediate values.
If you're still unsure, break the for loop down into the equivalent while loop.

Thank you. Hmm. I think I get it: j starts at 3, but when the so-called "condition" (j--) is first evaluated, it decrements j to 2. So we enter the first loop with j=2, as it should be.

But after the second loop finishes, with j=1, then the "condition" (j--) = 0, which is false, so the loop should not execute again.

That is, the loop should execute only 2 times, not three. So it still doesn't seem right.

(I'm at work :roll_eyes: and can't try printing intermediate results...)

Any thoughts on why the XOR?

Set j = 3
Evaluate j (it's three, so true). Now decrement j
Execute loop body with j=2
Evaluate j (It's two, so true). Now decrement j
Execute loop body with j=1
Evaluate j (It's one, so true). Now decrement j
Execute loop body with j=0
Evaluate j (It's zero, so false). Now decrement j
Don't execute loop body.

The XOR compound operator strips the leading zero from the result.

It does?
Hint:
0 ^ 0 = 0
0 ^ 1 = 1
1 ^ 0 = 1
1 ^ 1 = 0

Thanks again. I figured that the "condition" would be evaluated as a whole and not in the two steps you indicate.

Regarding the XOR question, whoops...I see it inverts the MSB bit. Ah ha!

Thanks much!

Regarding the XOR...

Since the output of this ADC is 2’s complement data coding with:
MIN 0x 800000
MAX 0x 7FFFFFF

After reading the ADC in an unsigned long variable, the code restores the sign by XOR'ing the MSB bit.

So, using an eight-bit word as an example,

01111111 = 127 becomes 11111111 = 255
00000000 = 0 becomes 01111111 = 128
10000000 = 128 becomes 00000000 = 0

Magic! :slight_smile:

Thanks for everyone’s help!