hx711-pico-c
|
This is my implementation of reading from a HX711 via a Raspberry Pi Pico. It uses the RP2040's PIO feature to be as efficient as possible. It has two major functions: reading from a single HX711 and reading from multiple HX711s.
A MicroPython port is available here.
NOTE: if you are looking for a method to weigh objects (ie. by using the HX711 as a scale), see pico-scale.
The .gif above illustrates obtaining data from a single HX711 operating at 80 samples per second.
Run cmake
to build the example program. The .uf2
file you upload to your Pico will be found under build/tests/
.
Alternatively, include it as a submodule in your project and then #include "extern/hx711-pico-c/include/common.h"
.
https://endail.github.io/hx711-pico-c
See here for a pinout to choose two GPIO pins on the Pico (RP2040). One GPIO pin to connect to the HX711's clock pin and a second GPIO pin to connect to the HX711's data pin. You can choose any two pins as the clock and data pins, as long as they are capable of digital output and input respectively.
See here for a pinout to choose at least two separate GPIO pins on the Pico (RP2040).
For example, if you wanted to connect four HX711 chips, you could:
See the code example below for how you would set this up. You can choose any pins as the clock and data pins, as long as they are capable of digital output and input respectively.
You can connect up to 32 HX711s, although the Pico (RP2040) will limit you to the available pins.
Note: each chip should use the same sample rate. Using chips with different sample rates will lead to unpredictible results.
Channel A is selectable by setting the gain to 128 or 64. Channel B is selectable by setting the gain to 32.
The HX711 has no option for Channel A at a gain of 32, nor is there an option for Channel B at a gain of 128 or 64. Similarly, the HX711 is not capable of reading from Channel A and Channel B simultaneously. The gain must first be changed.
After powering up, the HX711 requires a small "settling time" before it can produce "valid stable output data" (see: HX711 datasheet pg. 3). By calling hx711_wait_settle()
and passing in the correct data rate, you can ensure your program is paused for the correct settling time. Alternatively, you can call hx711_get_settling_time()
and pass in a hx711_rate_t
which will return the number of milliseconds of settling time for the given data rate.
The HX711 requires the clock pin to be held high for at least 60us (60 microseconds) before it powers down. By calling hx711_wait_power_down()
after hx711_power_down()
you can ensure the chip is properly powered-down.
By setting the HX711 gain with hx711_set_gain
and then powering down, the chip saves the gain for when it is powered back up. This is a feature built-in to the HX711.
When calling hx711_power_up()
or hx711_multi_power_up()
it is assumed that the gain value passed to these functions indicates the previously saved gain value in the chip. If the previously saved gain is unknown, you can either:
hx711_get_value()
, hx711_multi_get_values()
, etc...), and the subsequent reads will have the correct gain; orhx711_set_gain()
or hx711_multi_set_gain()
with the gain you want.In the example code above, the final statement closes communication with the HX711. This leaves the HX711 in a powered-up state. hx711_close
and hx711_multi_close
stops the internal state machines from reading data from the HX711. Whereas hx711_power_down
and hx711_multi_power_down
also begins the power down process on a HX711 chip by setting the clock pin high.
When using multiple HX711 chips, it is possible they may be desynchronised if not powered up simultaneously. You can use hx711_multi_sync()
which will power down and then power up all chips together.
When using hx711_multi_t
, two interrupts are claimed: one for a PIO interrupt and one for a DMA interrupt. By default, PIO[N]_IRQ_0
and DMA_IRQ_0
are used, where [N]
is the PIO index being used (ie. configuring hx711_multi_t
with pio0
means the resulting interrupt is PIO0_IRQ_0
and pio1
results in PIO1_IRQ_0
). If you need to change the IRQ index for either PIO or DMA, you can do this when configuring.
Mutex functionality is included and enabled by default to protect the HX711 conversion process. If you are sure you do not need it, define the preprocessor flag HX711_NO_MUTEX
then recompile.
#include include/common.h
includes the PIO programs I have created for both hx711_t
and hx711_multi_t
. Calling hx711_get_default_config()
and hx711_multi_get_default_config()
will include those PIO programs in the configurations. If you want to change or use your own PIO programs, set the relevant hx711_*_config_t
defaults, and do the following:
hxcfg.pio_init
and hxcfg.pio_prog_init
take a pointer to the hx711_t
as the only parameter.
hxmcfg.pio_init
, hxmcfg.awaiter_prog_init
, and hxmcfg.reader_prog_init
take a pointer to the hx711_multi_t
as the only parameter.
The single chip hx711_t
functions with a single RP2040 State Machine (SM) in one PIO. This includes setting and changing the HX711's gain. The SM is configured to be free-running which constantly obtains values from the HX711. Values are buffered in the SM's RX FIFO which enables application code to retrieve the most up-to-date value possible. Reading from the RX FIFO simultaneously clears it, so applications are simply able to busy-wait on the RX_FIFO being filled for the next value.
The multi chip hx711_multi_t
functions with two RP2040 State Machines (SM) in one PIO. This includes setting and changing all HX711s gains. The first SM is the "awaiter". It is free-running. It constantly reads the pin state of each RP2040 GPIO pin configured as a data input pin. If and when every pin is low - indicating that every chip has data ready - a PIO interrupt is set.
The second SM is the "reader". It is also free-running. The reader waits for the data ready PIO interrupt from the awaiter and then begins the HX711 conversion period of reading in bits. The conversion period is synchronised with application code by setting and clearing an interrupt to denote when a conversion period is in progress.
The reader clocks in the state of each data input pin as a bitmask and then pushes it back out of the SM into the RX FIFO. There are 24 pushes; one for each HX711 bit. Due to the size of the RX FIFO only being 32 bits, a SM is not capable of buffering all HX711 input bits when there are multiple chips. Hence why there is a push
for each HX711 bit.
On the receiving end of the SM is a DMA channel which automatically reads in each bitmask of HX711 bits into an array. These bitmasks are then transformed into HX711 values for each chip and returned to application code.
channel_config_set_ring
in conjunction with a static array buffer to constantly read in values from the SM lead to misaligned write addresses. As the HX711 uses 3 bytes to represent a value and the ring buffer requires a "naturally aligned buffer", it would take another byte to "reset" the ring back to the initial address. An application could not simply read the buffer and obtain valid value.