|
5 | 5 | use bitmatch::bitmatch; |
6 | 6 | use core::unimplemented; |
7 | 7 | use embedded_hal as hal; |
| 8 | +use embedded_hal_async::spi::SpiBus as AsyncSpiBus; |
8 | 9 | use hal::spi::SpiBus; |
9 | 10 | // |
10 | 11 | // saturation |
@@ -93,6 +94,37 @@ impl<SPIERROR> From<SPIERROR> for Hx711Error<SPIERROR> { |
93 | 94 | } |
94 | 95 | } |
95 | 96 |
|
| 97 | +impl<SPI> Hx711<SPI> |
| 98 | +{ |
| 99 | + #[inline] |
| 100 | + /// Get the current mode. |
| 101 | + pub fn mode(&mut self) -> Mode { |
| 102 | + self.mode |
| 103 | + } |
| 104 | + |
| 105 | + /// To power down the chip the PD_SCK line has to be held in a 'high' state. To do this we |
| 106 | + /// would need to write a constant stream of binary '1' to the `SPI` bus which would totally defy |
| 107 | + /// the purpose. Therefore it's not implemented. |
| 108 | + // If the SDO pin would be idle high (and at least some MCU's seem to do that in mode 1) then the chip would automatically |
| 109 | + // power down if not used. Cool! |
| 110 | + pub fn disable(&mut self) -> ! { |
| 111 | + // when PD_SCK pin changes from low to high and stays at high for longer than 60µs, HX711 enters power down mode |
| 112 | + // When PD_SCK returns to low, chip will reset and enter normal operation mode. |
| 113 | + // this can't be implemented with SPI because we would have to write a constant stream |
| 114 | + // of binary '1' which would block the process |
| 115 | + unimplemented!("power_down is not possible with this driver implementation"); |
| 116 | + } |
| 117 | + |
| 118 | + /// Power up / down is not implemented (see disable) |
| 119 | + pub fn enable(&mut self) -> ! { |
| 120 | + // when PD_SCK pin changes from low to high and stays at high for longer than 60µs, HX711 enters power down mode |
| 121 | + // When PD_SCK returns to low, chip will reset and enter normal operation mode. |
| 122 | + // this can't be implemented with SPI because we would have to write a constant stream |
| 123 | + // of binary '1' which would block the process |
| 124 | + unimplemented!("power_down is not possible with this driver implementation"); |
| 125 | + } |
| 126 | +} |
| 127 | + |
96 | 128 | impl<SPI> Hx711<SPI> |
97 | 129 | where |
98 | 130 | SPI: SpiBus, |
@@ -171,33 +203,83 @@ where |
171 | 203 | self.read()?; // read writes Mode for the next read() |
172 | 204 | Ok(m) |
173 | 205 | } |
| 206 | +} |
174 | 207 |
|
175 | | - #[inline] |
176 | | - /// Get the current mode. |
177 | | - pub fn mode(&mut self) -> Mode { |
178 | | - self.mode |
| 208 | +impl<SPI> Hx711<SPI> |
| 209 | +where |
| 210 | + SPI: AsyncSpiBus, |
| 211 | +{ |
| 212 | + /// opens a connection to a HX711 on a specified `SPI`. |
| 213 | + /// |
| 214 | + /// The data sheet specifies PD_SCK high time and PD_SCK low time to be in the 0.2 to 50 us range, |
| 215 | + /// therefore bus speed has to be between 5 MHz and 20 kHz. |
| 216 | + pub fn new_async(spi: SPI) -> Self { |
| 217 | + Hx711 { |
| 218 | + spi, |
| 219 | + mode: Mode::ChAGain128, |
| 220 | + } |
179 | 221 | } |
180 | 222 |
|
181 | | - /// To power down the chip the PD_SCK line has to be held in a 'high' state. To do this we |
182 | | - /// would need to write a constant stream of binary '1' to the `SPI` bus which would totally defy |
183 | | - /// the purpose. Therefore it's not implemented. |
184 | | - // If the SDO pin would be idle high (and at least some MCU's seem to do that in mode 1) then the chip would automatically |
185 | | - // power down if not used. Cool! |
186 | | - pub fn disable(&mut self) -> Result<(), Hx711Error<SPI::Error>> { |
187 | | - // when PD_SCK pin changes from low to high and stays at high for longer than 60µs, HX711 enters power down mode |
188 | | - // When PD_SCK returns to low, chip will reset and enter normal operation mode. |
189 | | - // this can't be implemented with SPI because we would have to write a constant stream |
190 | | - // of binary '1' which would block the process |
191 | | - unimplemented!("power_down is not possible with this driver implementation"); |
| 223 | + /// reads a value from the HX711 and returns it |
| 224 | + /// # Errors |
| 225 | + /// Returns `SPI` errors |
| 226 | + pub async fn read_async(&mut self) -> Result<i32, SPI::Error> { |
| 227 | + // check if data is ready |
| 228 | + // When output data is not ready for retrieval, digital output pin DOUT is high. |
| 229 | + // Serial clock input PD_SCK should be low. When DOUT goes |
| 230 | + // to low, it indicates data is ready for retrieval. |
| 231 | + let mut txrx: [u8; 1] = [SIGNAL_LOW]; |
| 232 | + |
| 233 | + while txrx[0] != 0x00 { |
| 234 | + self.spi.transfer_in_place(&mut txrx).await?; |
| 235 | + } |
| 236 | + |
| 237 | + let mut buffer: [u8; 7] = [CLOCK, CLOCK, CLOCK, CLOCK, CLOCK, CLOCK, self.mode as u8]; |
| 238 | + |
| 239 | + self.spi.transfer_in_place(&mut buffer).await?; |
| 240 | + |
| 241 | + Ok(decode_output(&buffer)) // value should be in range 0x800000 - 0x7fffff according to datasheet |
192 | 242 | } |
193 | 243 |
|
194 | | - /// Power up / down is not implemented (see disable) |
195 | | - pub fn enable(&mut self) -> Result<(), Hx711Error<SPI::Error>> { |
196 | | - // when PD_SCK pin changes from low to high and stays at high for longer than 60µs, HX711 enters power down mode |
| 244 | + /// Reset the chip to it's default state. Mode is set to convert channel A with a gain factor of 128. |
| 245 | + /// # Errors |
| 246 | + /// Returns `SPI` errors |
| 247 | + #[inline] |
| 248 | + pub async fn reset_async(&mut self) -> Result<(), SPI::Error> { |
| 249 | + // when PD_SCK pin changes from low to high and stays at high for longer than 60µs, |
| 250 | + // HX711 enters power down mode. |
197 | 251 | // When PD_SCK returns to low, chip will reset and enter normal operation mode. |
198 | | - // this can't be implemented with SPI because we would have to write a constant stream |
199 | | - // of binary '1' which would block the process |
200 | | - unimplemented!("power_down is not possible with this driver implementation"); |
| 252 | + // speed is the raw SPI speed -> half bits per second. |
| 253 | + |
| 254 | + // max SPI clock frequency should be 5 MHz to satisfy the 0.2 us limit for the pulse length |
| 255 | + // we have to output more than 300 bytes to keep the line for at least 60 us high. |
| 256 | + |
| 257 | + let mut buffer: [u8; 301] = RESET_SIGNAL; |
| 258 | + |
| 259 | + self.spi.transfer_in_place(&mut buffer).await?; |
| 260 | + self.mode = Mode::ChAGain128; // this is the default mode after reset |
| 261 | + |
| 262 | + Ok(()) |
| 263 | + } |
| 264 | + |
| 265 | + /// Set the mode to the value specified. |
| 266 | + /// see the Mode struct for possible values |
| 267 | + /// # Usage |
| 268 | + /// |
| 269 | + /// ```rust |
| 270 | + /// my_hx711.set_mode_async(Mode::ChAGain128).await?; |
| 271 | + /// value1_chanel_a = my_hx711.read_async().await? |
| 272 | + /// value2_chanel_a = my_hx711.read_async().await? |
| 273 | + /// my_hx711.set_mode_async(Mode::ChBGain32).await?; |
| 274 | + /// value_chanel_b = my_hx711.read_async().await? |
| 275 | + ///``` |
| 276 | + /// # Errors |
| 277 | + /// Returns `SPI` errors |
| 278 | + #[inline] |
| 279 | + pub async fn set_mode_async(&mut self, m: Mode) -> Result<Mode, SPI::Error> { |
| 280 | + self.mode = m; |
| 281 | + self.read_async().await?; // read writes Mode for the next read() |
| 282 | + Ok(m) |
201 | 283 | } |
202 | 284 | } |
203 | 285 |
|
|
0 commit comments