HX711.cpp 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. /**
  2. *
  3. * HX711 library for Arduino
  4. * https://github.com/bogde/HX711
  5. *
  6. * MIT License
  7. * (c) 2018 Bogdan Necula
  8. *
  9. **/
  10. #include <Arduino.h>
  11. #include "HX711.h"
  12. // TEENSYDUINO has a port of Dean Camera's ATOMIC_BLOCK macros for AVR to ARM Cortex M3.
  13. #define HAS_ATOMIC_BLOCK (defined(ARDUINO_ARCH_AVR) || defined(TEENSYDUINO))
  14. // Whether we are running on either the ESP8266 or the ESP32.
  15. #define ARCH_ESPRESSIF (defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32))
  16. // Whether we are actually running on FreeRTOS.
  17. #define IS_FREE_RTOS defined(ARDUINO_ARCH_ESP32)
  18. // Define macro designating whether we're running on a reasonable
  19. // fast CPU and so should slow down sampling from GPIO.
  20. #define FAST_CPU \
  21. ( \
  22. ARCH_ESPRESSIF || \
  23. defined(ARDUINO_ARCH_SAM) || defined(ARDUINO_ARCH_SAMD) || \
  24. defined(ARDUINO_ARCH_STM32) || defined(TEENSYDUINO) \
  25. )
  26. #if HAS_ATOMIC_BLOCK
  27. // Acquire AVR-specific ATOMIC_BLOCK(ATOMIC_RESTORESTATE) macro.
  28. #include <util/atomic.h>
  29. #endif
  30. #if FAST_CPU
  31. // Make shiftIn() be aware of clockspeed for
  32. // faster CPUs like ESP32, Teensy 3.x and friends.
  33. // See also:
  34. // - https://github.com/bogde/HX711/issues/75
  35. // - https://github.com/arduino/Arduino/issues/6561
  36. // - https://community.hiveeyes.org/t/using-bogdans-canonical-hx711-library-on-the-esp32/539
  37. /*
  38. uint8_t shiftInSlow(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder) {
  39. uint8_t value = 0;
  40. uint8_t i;
  41. for(i = 0; i < 8; ++i) {
  42. digitalWrite(clockPin, HIGH);
  43. delayMicroseconds(1);
  44. digitalWrite(clockPin, LOW);
  45. delayMicroseconds(1);
  46. if(bitOrder == LSBFIRST)
  47. value |= digitalRead(dataPin) << i;
  48. else
  49. value |= digitalRead(dataPin) << (7 - i);
  50. }
  51. return value;
  52. }
  53. */
  54. uint8_t shiftInSlow(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder) {
  55. uint8_t value = 0;
  56. uint8_t i;
  57. for(i = 0; i < 8; ++i) {
  58. digitalWrite(clockPin, HIGH);
  59. delayMicroseconds(1);
  60. if(bitOrder == LSBFIRST)
  61. value |= digitalRead(dataPin) << i;
  62. else
  63. value |= digitalRead(dataPin) << (7 - i);
  64. digitalWrite(clockPin, LOW);
  65. delayMicroseconds(1);
  66. }
  67. return value;
  68. }
  69. #define SHIFTIN_WITH_SPEED_SUPPORT(data,clock,order) shiftInSlow(data,clock,order)
  70. #else
  71. #define SHIFTIN_WITH_SPEED_SUPPORT(data,clock,order) shiftIn(data,clock,order)
  72. #endif
  73. HX711::HX711() {
  74. }
  75. HX711::~HX711() {
  76. }
  77. void HX711::begin(byte dout, byte pd_sck, byte gain) {
  78. PD_SCK = pd_sck;
  79. DOUT = dout;
  80. pinMode(PD_SCK, OUTPUT);
  81. pinMode(DOUT, INPUT_PULLUP);
  82. set_gain(gain);
  83. }
  84. bool HX711::is_ready() {
  85. return digitalRead(DOUT) == LOW;
  86. }
  87. void HX711::set_gain(byte gain) {
  88. switch (gain) {
  89. case 128: // channel A, gain factor 128
  90. GAIN = 1;
  91. break;
  92. case 64: // channel A, gain factor 64
  93. GAIN = 3;
  94. break;
  95. case 32: // channel B, gain factor 32
  96. GAIN = 2;
  97. break;
  98. }
  99. //digitalWrite(PD_SCK, LOW);
  100. if(is_ready())
  101. read();
  102. }
  103. long HX711::read() {
  104. // Wait for the chip to become ready.
  105. wait_ready();
  106. // Define structures for reading data into.
  107. unsigned long value = 0;
  108. uint8_t data[3] = { 0 };
  109. uint8_t filler = 0x00;
  110. // Protect the read sequence from system interrupts. If an interrupt occurs during
  111. // the time the PD_SCK signal is high it will stretch the length of the clock pulse.
  112. // If the total pulse time exceeds 60 uSec this will cause the HX711 to enter
  113. // power down mode during the middle of the read sequence. While the device will
  114. // wake up when PD_SCK goes low again, the reset starts a new conversion cycle which
  115. // forces DOUT high until that cycle is completed.
  116. //
  117. // The result is that all subsequent bits read by shiftIn() will read back as 1,
  118. // corrupting the value returned by read(). The ATOMIC_BLOCK macro disables
  119. // interrupts during the sequence and then restores the interrupt mask to its previous
  120. // state after the sequence completes, insuring that the entire read-and-gain-set
  121. // sequence is not interrupted. The macro has a few minor advantages over bracketing
  122. // the sequence between `noInterrupts()` and `interrupts()` calls.
  123. #if HAS_ATOMIC_BLOCK
  124. ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
  125. #elif IS_FREE_RTOS
  126. // Begin of critical section.
  127. // Critical sections are used as a valid protection method
  128. // against simultaneous access in vanilla FreeRTOS.
  129. // Disable the scheduler and call portDISABLE_INTERRUPTS. This prevents
  130. // context switches and servicing of ISRs during a critical section.
  131. portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED;
  132. portENTER_CRITICAL(&mux);
  133. #else
  134. // Disable interrupts.
  135. noInterrupts();
  136. #endif
  137. // Pulse the clock pin 24 times to read the data.
  138. data[2] = shiftIn(DOUT, PD_SCK, MSBFIRST);
  139. data[1] = shiftIn(DOUT, PD_SCK, MSBFIRST);
  140. data[0] = shiftIn(DOUT, PD_SCK, MSBFIRST);
  141. /*
  142. data[2] = SHIFTIN_WITH_SPEED_SUPPORT(DOUT, PD_SCK, MSBFIRST);
  143. data[1] = SHIFTIN_WITH_SPEED_SUPPORT(DOUT, PD_SCK, MSBFIRST);
  144. data[0] = SHIFTIN_WITH_SPEED_SUPPORT(DOUT, PD_SCK, MSBFIRST);
  145. */
  146. // Set the channel and the gain factor for the next reading using the clock pin.
  147. for (unsigned int i = 0; i < GAIN; i++) {
  148. digitalWrite(PD_SCK, HIGH);
  149. #if ARCH_ESPRESSIF
  150. delayMicroseconds(1);
  151. #endif
  152. digitalWrite(PD_SCK, LOW);
  153. #if ARCH_ESPRESSIF
  154. delayMicroseconds(1);
  155. #endif
  156. }
  157. #if IS_FREE_RTOS
  158. // End of critical section.
  159. portEXIT_CRITICAL(&mux);
  160. #elif HAS_ATOMIC_BLOCK
  161. }
  162. #else
  163. // Enable interrupts again.
  164. interrupts();
  165. #endif
  166. // Replicate the most significant bit to pad out a 32-bit signed integer
  167. if (data[2] & 0x80) {
  168. filler = 0xFF;
  169. } else {
  170. filler = 0x00;
  171. }
  172. // Construct a 32-bit signed integer
  173. value = ( static_cast<unsigned long>(filler) << 24
  174. | static_cast<unsigned long>(data[2]) << 16
  175. | static_cast<unsigned long>(data[1]) << 8
  176. | static_cast<unsigned long>(data[0]) );
  177. return static_cast<long>(value);
  178. }
  179. void HX711::wait_ready(unsigned long delay_ms) {
  180. // Wait for the chip to become ready.
  181. // This is a blocking implementation and will
  182. // halt the sketch until a load cell is connected.
  183. while (!is_ready()) {
  184. // Probably will do no harm on AVR but will feed the Watchdog Timer (WDT) on ESP.
  185. // https://github.com/bogde/HX711/issues/73
  186. delay(delay_ms);
  187. }
  188. }
  189. bool HX711::wait_ready_retry(int retries, unsigned long delay_ms) {
  190. // Wait for the chip to become ready by
  191. // retrying for a specified amount of attempts.
  192. // https://github.com/bogde/HX711/issues/76
  193. int count = 0;
  194. while (count < retries) {
  195. if (is_ready()) {
  196. return true;
  197. }
  198. delay(delay_ms);
  199. count++;
  200. }
  201. return false;
  202. }
  203. bool HX711::wait_ready_timeout(unsigned long timeout, unsigned long delay_ms) {
  204. // Wait for the chip to become ready until timeout.
  205. // https://github.com/bogde/HX711/pull/96
  206. unsigned long millisStarted = millis();
  207. while (millis() - millisStarted < timeout) {
  208. if (is_ready()) {
  209. return true;
  210. }
  211. delay(delay_ms);
  212. }
  213. return false;
  214. }
  215. long HX711::read_average(byte times) {
  216. long sum = 0;
  217. for (byte i = 0; i < times; i++) {
  218. sum += read();
  219. // Probably will do no harm on AVR but will feed the Watchdog Timer (WDT) on ESP.
  220. // https://github.com/bogde/HX711/issues/73
  221. delay(0);
  222. }
  223. return sum / times;
  224. }
  225. double HX711::get_value(byte times) {
  226. return read_average(times) - OFFSET;
  227. }
  228. float HX711::get_units(byte times) {
  229. return get_value(times) / SCALE;
  230. }
  231. void HX711::tare(byte times) {
  232. double sum = read_average(times);
  233. set_offset(sum);
  234. }
  235. void HX711::set_scale(float scale) {
  236. SCALE = scale;
  237. }
  238. float HX711::get_scale() {
  239. return SCALE;
  240. }
  241. void HX711::set_offset(long offset) {
  242. OFFSET = offset;
  243. }
  244. long HX711::get_offset() {
  245. return OFFSET;
  246. }
  247. void HX711::power_down() {
  248. digitalWrite(PD_SCK, LOW);
  249. digitalWrite(PD_SCK, HIGH);
  250. }
  251. void HX711::power_up() {
  252. digitalWrite(PD_SCK, LOW);
  253. }