Closed hyx0329 closed 1 year ago
I am not an electronic expert. Could the SPI mode (clock polarity and clock phase) play a role here? Additionally the CS line is not used, even not defined in my Raspberry PI example. This could be a problem, too. I'll have a look.
I haven't found any documentation yet, but it seems that the default state of the SPI lines is influenced by the mode. Do you mind to try your code with SPI mode 1 instead?
Idle high for the SCLK pin would be desireable because it would auto power-down the hx711 (see datasheet) By looking at your first graph, I recognized something else: The SDI signal is high for the first 9 (fake) clock cycles which indicates that ther is no data waiting to be retrived. In this case the 'read' funktion should not emit any clock signal but instead keep the SDO line low for 8 cycles and then return with a 'WouldBlock'. When the function is called again (though the block! macro) it will see the SDI line go low and then start to write the clock cycle to SDO. Unfortunately I don't own a logic analyzer wich makes debugging a bit hard for me.
Could the SPI mode (clock polarity and clock phase) play a role here?
Nope, I've tried all 4 modes and they only affect the CLK's polarity and not the SDO's idle state.
Additionally the CS line is not used, even not defined in my Raspberry PI example.
The additional CS is used to control an external mosfet so I can pull the SDO low(in the first attempt). It's not used when I just invert the polarity.
I recognized something else: The SDI signal is high for the first 9 (fake) clock cycles which indicates that ther is no data waiting to be retrived. In this case the 'read' funktion should not emit any clock signal but instead keep the SDO line low for 8 cycles and then return with a 'WouldBlock'.
When I wasn't using the mosfet, the code did block, infinitely, because DOUT(DT) is always HIGH and there's nothing to be read. Bitbanging doesn't have this issue. The SDI signal for the first 9 clock cycles might be caused by the first pulse I pointed out(I mean the chip started to output a value).
In the meantime, I think I recognize a new bug. The transaction should be started only when the lowest bit of the value read is zero. So the code below should be change a bit.
maybe to something like this
if txrx[0] & 0b01 == 0b01
// as soon as the lowest bit is low data is ready
{
// sleep for 1 millisecond which is 1/100 of the conversion period to grab the data while it's hot
self.delay.delay_ms(1); // not sure if that's ok with nb
return Err(nb::Error::WouldBlock);
}
I'm uploading the data I captured for anyone interested. Should work with DSView.
Edit: for pulseview users, try srzips in the following zip
Idle high for the SCLK pin would be desireable because it would auto power-down the hx711 (see datasheet)
In this case, the fake clock should be sent immediately after the initial probing. That means everything should be done in one atomic SPI transaction, and the value is conditionally decoded using the last bytes. Otherwise after the probing, the chip is quickly powered off(because of idle HIGH), and it may not ready for a read at the time fake clock generated.
Thank you for helping me to debug this!
Could the SPI mode (clock polarity and clock phase) play a role here?
Nope, I've tried all 4 modes and they only affect the CLK's polarity and not the SDO's idle state.
That's what I would expect but it was the case for one STM32 user.
When I wasn't using the mosfet, the code did block, infinitely, because DOUT(DT) is always HIGH and there's nothing to be read. Bitbanging doesn't have this issue. The SDI signal for the first 9 clock cycles might be caused by the first pulse I pointed out(I mean the chip started to output a value).
The hx711 chip shuld ignore the SCK signal when it pulls the DOUT high but who knows.
if txrx[0] & 0b01 == 0b00
// as soon as the lowest bit is low data is ready
{
return Err(nb::Error::WouldBlock);
}
You are right. This should detect the falling edge in the DOUT much more reliable!
Note: it should be == 0b010
and I would delete the unnecessary delay which I think may cause problems on platforms other then Raspebrry PI. Unfortunately this change will break the API (not requiring the delay parameter anymore)
Note: it should be == 0b0~1~0
well, SPI bus seems to be active LOW so I think it should be 1
PS. occasionally, I'll get the result in the following picture, I know it's busy because the scale has no load on it.
My bad. You are right! It should be
if txrx[0] & 0b01 == 0b01 {
// as long as the lowest bit is high there is no data waiting
return Err(nb::Error::WouldBlock);
}
Concerning your PR: maybe I'd have to invert the bit pattern for the clock. I am looking into this, too.
I was testing on my Raspberry Pi with rppal as hal implementation and I get an extra signal on the hx711 clk line every second even without my code running. This blocks the chip for ~9 ms. But that's not the same what you experienced, right? Edit to add: On a freshly booted machine I don't see those extra spikes.
I get an extra signal on the hx711 clk line every second even without my code running
That's not the same what I experienced. But I think the spikes play a very similar role in disturbing the communication, like those in my setup.
That's a typical sequence using Raspberry PI and rppal: You can see the probes to see if hx711 is ready ant then it starts the read out. Looks good to me.
That's a typical sequence using Raspberry PI and rppal:
That looks good!
After doing a loop-back test using spidev-test I am confident that the spikes are caused by the cheap hx711 board. They seem to originate in the SCK line and due to crosstalk there are shorter spikes in the DOUT. During a data transfer they are not causing any problems (in my case) and when the hx711 is idle it will be busy for ~90 us, which is no big issue.
Maybe the spike you are seeing is caused by the hardware? It looks like it happens shortly after SPI clock is turned off. That would explain why this problem doesn't exsits with bit-banging.
I have several setups:
Even if the spikes(when mosfet is used) are caused by hardware, my CS's response on ESP32-C3 is still slow, thus I cannot use CS to keep SCK(SDO) idle low, which will likely to cause problems. (PS. SPI peripherals read value on clock transitions so my spikes(when mosfet is used) will not affect standard SPI communication.)
Key points:
The spikes are not that relevant to my issue.
I think I have to apologize for not making everything clear enough. (´;ω ;`)
You explaind very well, and I understand what you want. I just needed to understand what my code is doing and why it worked for me. This crate is my attemt to learn two things at once: Rust and embedded / bare metal programming. I just kept the Raspi as dev platform because I did not want to throw a third unknown into the mix.
Hey!
I've imlemented the feature invert-sdo
in the 0.5.0 branch. Could you have a look and maybe give it a try, please?
Thanks!
Two small bug:
the second one should be GAIN64
https://github.com/crjeder/hx711_spi/blob/ab323db5d6db2a70006326de3614b0485855db81/src/lib.rs#L25-L28
1
missed here, should be 0b01010101
https://github.com/crjeder/hx711_spi/blob/ab323db5d6db2a70006326de3614b0485855db81/src/lib.rs#L34-L35
The rest look good. Good job!
Done!
I was looking for a reason of the behavior and found a clue: the "normal" SPI is a Motorola developed protocol but there is a TI variant wich sends an extra "high" signal between frames. Together with a frame length of 16 bit this would explain the extra signal and the value being of by a factor of 2 (skipping the 17th bit). The best reference I found: ARM
there is a TI variant wich sends an extra "high" signal between frames
And that can explain why CS is not deserted after all data sent. In this case, some hosts may need the extra "high" signal to work with some devices implementing TI's variant of SPI.
I grabbed one random datasheet from the internet, found the example below.
Is your feature request related to a problem? Please describe.
I was trying to utilize the crate on ESP32-C3. The
block!(hx711.read())
will always block. After digging around, I found the SDO of ESP32-C3 is always idle at HIGH and I haven't find a way to change the behavior. So I decided to use a MOSFET to hold the SDO at LOW when no data is transfered. However, the CS on ESP32-C3 is disabled after SDO pulled itself HIGH, the value I got is not correct.Later I tried to invert the polarity of SDO with the MOSFET and change the code, it looks better now.
Describe the solution you'd like
Introduce a new feature
invert-sdo
, and enable it will invert the signal on SDO, allowing external inverter. For exampleDescribe alternatives you've considered
Maybe there's a way to fix it on ESP32-C3's side.
Additional context
MOSFET model 1N60C, an additional 1kΩ resistor is used when inverting the SDO. SPI speed is 1MHz.
Edit: wrong resistor value