diff options
| author | Matt Johnston <[email protected]> | 2025-09-14 16:30:31 +0800 |
|---|---|---|
| committer | Matt Johnston <[email protected]> | 2025-09-14 16:54:14 +0800 |
| commit | 8f10e3638d77cadf058b9083de09fc7189048b0b (patch) | |
| tree | 804739dd75306154351460b0ee4dde51a3170caa /embassy-rp | |
| parent | be794533d3929e316c65b4296de47292ae0eae67 (diff) | |
rp/pio: Add onewire strong pullups, parasite power
DS18B20 sensors require a strong pullup to be applied for the duration
of the temperature conversion, within 10us of the command. The rp2xxx
pins have sufficient drive strength to use as the pullup (no external
mosfet needed).
Add a new write_bytes_pullup() that will apply the pullup after
bytes are written. Existing read_bytes()/write_bytes() has no change to
onewire timing.
A pio_onewire_parasite example reads multiple sensors individually,
applying the strong pullup.
Diffstat (limited to 'embassy-rp')
| -rw-r--r-- | embassy-rp/CHANGELOG.md | 1 | ||||
| -rw-r--r-- | embassy-rp/src/pio_programs/onewire.rs | 45 |
2 files changed, 43 insertions, 3 deletions
diff --git a/embassy-rp/CHANGELOG.md b/embassy-rp/CHANGELOG.md index b50d41dd1..841c9f068 100644 --- a/embassy-rp/CHANGELOG.md +++ b/embassy-rp/CHANGELOG.md | |||
| @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | |||
| 10 | 10 | ||
| 11 | - Add PIO SPI | 11 | - Add PIO SPI |
| 12 | - Add PIO I2S input | 12 | - Add PIO I2S input |
| 13 | - Add PIO onewire parasite power strong pullup | ||
| 13 | 14 | ||
| 14 | ## 0.8.0 - 2025-08-26 | 15 | ## 0.8.0 - 2025-08-26 |
| 15 | 16 | ||
diff --git a/embassy-rp/src/pio_programs/onewire.rs b/embassy-rp/src/pio_programs/onewire.rs index 287ddab41..980d0fe5f 100644 --- a/embassy-rp/src/pio_programs/onewire.rs +++ b/embassy-rp/src/pio_programs/onewire.rs | |||
| @@ -52,7 +52,8 @@ impl<'a, PIO: Instance> PioOneWireProgram<'a, PIO> { | |||
| 52 | 52 | ||
| 53 | ; The low pulse was already done, we only need to delay and poll the bit in case we are reading | 53 | ; The low pulse was already done, we only need to delay and poll the bit in case we are reading |
| 54 | write_1: | 54 | write_1: |
| 55 | nop side 0 [( 6 / CLK) - 1] ; Delay before sampling the input pin | 55 | jmp y--, continue_1 side 0 [( 6 / CLK) - 1] ; Delay before sampling input. Always decrement y |
| 56 | continue_1: | ||
| 56 | in pins, 1 side 0 [(48 / CLK) - 1] ; This writes the state of the pin into the ISR | 57 | in pins, 1 side 0 [(48 / CLK) - 1] ; This writes the state of the pin into the ISR |
| 57 | ; Fallthrough | 58 | ; Fallthrough |
| 58 | 59 | ||
| @@ -61,9 +62,24 @@ impl<'a, PIO: Instance> PioOneWireProgram<'a, PIO> { | |||
| 61 | .wrap_target | 62 | .wrap_target |
| 62 | out x, 1 side 0 [(12 / CLK) - 1] ; Stalls if no data available in TX FIFO and OSR | 63 | out x, 1 side 0 [(12 / CLK) - 1] ; Stalls if no data available in TX FIFO and OSR |
| 63 | jmp x--, write_1 side 1 [( 6 / CLK) - 1] ; Do the always low part of a bit, jump to write_1 if we want to write a 1 bit | 64 | jmp x--, write_1 side 1 [( 6 / CLK) - 1] ; Do the always low part of a bit, jump to write_1 if we want to write a 1 bit |
| 64 | in null, 1 side 1 [(54 / CLK) - 1] ; Do the remainder of the low part of a 0 bit | 65 | jmp y--, continue_0 side 1 [(48 / CLK) - 1] ; Do the remainder of the low part of a 0 bit |
| 65 | ; This writes 0 into the ISR so that the shift count stays in sync | 66 | jmp pullup side 1 [( 6 / CLK) - 1] ; Remain low while jumping |
| 67 | continue_0: | ||
| 68 | in null, 1 side 1 [( 6 / CLK) - 1] ; This writes 0 into the ISR so that the shift count stays in sync | ||
| 66 | .wrap | 69 | .wrap |
| 70 | |||
| 71 | ; Assume that strong pullup commands always have MSB (the last bit) = 0, | ||
| 72 | ; since the rising edge can be used to start the operation. | ||
| 73 | ; That's the case for DS18B20 (44h and 48h). | ||
| 74 | pullup: | ||
| 75 | set pins, 1 side 1[( 6 / CLK) - 1] ; Drive pin high output immediately. | ||
| 76 | ; Strong pullup must be within 10us of rise. | ||
| 77 | in null, 1 side 1[( 6 / CLK) - 1] ; Keep ISR in sync. Must occur after the y--. | ||
| 78 | out null, 8 side 1[( 6 / CLK) - 1] ; Wait for write_bytes_pullup() delay to complete. | ||
| 79 | ; The delay is hundreds of ms, so done externally. | ||
| 80 | set pins, 0 side 0[( 6 / CLK) - 1] ; Back to open drain, pin low when driven | ||
| 81 | in null, 8 side 1[( 6 / CLK) - 1] ; Inform write_bytes_pullup() it's ready | ||
| 82 | jmp next_bit side 0[( 6 / CLK) - 1] ; Continue | ||
| 67 | "# | 83 | "# |
| 68 | ); | 84 | ); |
| 69 | 85 | ||
| @@ -98,6 +114,7 @@ impl<'d, PIO: Instance, const SM: usize> PioOneWire<'d, PIO, SM> { | |||
| 98 | let mut cfg = Config::default(); | 114 | let mut cfg = Config::default(); |
| 99 | cfg.use_program(&program.prg, &[&pin]); | 115 | cfg.use_program(&program.prg, &[&pin]); |
| 100 | cfg.set_in_pins(&[&pin]); | 116 | cfg.set_in_pins(&[&pin]); |
| 117 | cfg.set_set_pins(&[&pin]); | ||
| 101 | 118 | ||
| 102 | let shift_cfg = ShiftConfig { | 119 | let shift_cfg = ShiftConfig { |
| 103 | auto_fill: true, | 120 | auto_fill: true, |
| @@ -146,6 +163,19 @@ impl<'d, PIO: Instance, const SM: usize> PioOneWire<'d, PIO, SM> { | |||
| 146 | 163 | ||
| 147 | /// Write bytes to the onewire bus | 164 | /// Write bytes to the onewire bus |
| 148 | pub async fn write_bytes(&mut self, data: &[u8]) { | 165 | pub async fn write_bytes(&mut self, data: &[u8]) { |
| 166 | unsafe { self.sm.set_y(u32::MAX as u32) }; | ||
| 167 | let (rx, tx) = self.sm.rx_tx(); | ||
| 168 | for b in data { | ||
| 169 | tx.wait_push(*b as u32).await; | ||
| 170 | |||
| 171 | // Empty the buffer that is being filled with every write | ||
| 172 | let _ = rx.wait_pull().await; | ||
| 173 | } | ||
| 174 | } | ||
| 175 | |||
| 176 | /// Write bytes to the onewire bus, then apply a strong pullup | ||
| 177 | pub async fn write_bytes_pullup(&mut self, data: &[u8], pullup_time: embassy_time::Duration) { | ||
| 178 | unsafe { self.sm.set_y(data.len() as u32 * 8 - 1) }; | ||
| 149 | let (rx, tx) = self.sm.rx_tx(); | 179 | let (rx, tx) = self.sm.rx_tx(); |
| 150 | for b in data { | 180 | for b in data { |
| 151 | tx.wait_push(*b as u32).await; | 181 | tx.wait_push(*b as u32).await; |
| @@ -153,10 +183,19 @@ impl<'d, PIO: Instance, const SM: usize> PioOneWire<'d, PIO, SM> { | |||
| 153 | // Empty the buffer that is being filled with every write | 183 | // Empty the buffer that is being filled with every write |
| 154 | let _ = rx.wait_pull().await; | 184 | let _ = rx.wait_pull().await; |
| 155 | } | 185 | } |
| 186 | |||
| 187 | // Perform the delay, usually hundreds of ms. | ||
| 188 | embassy_time::Timer::after(pullup_time).await; | ||
| 189 | |||
| 190 | // Signal that delay has completed | ||
| 191 | tx.wait_push(0 as u32).await; | ||
| 192 | // Wait until it's back at 0 low, open drain | ||
| 193 | let _ = rx.wait_pull().await; | ||
| 156 | } | 194 | } |
| 157 | 195 | ||
| 158 | /// Read bytes from the onewire bus | 196 | /// Read bytes from the onewire bus |
| 159 | pub async fn read_bytes(&mut self, data: &mut [u8]) { | 197 | pub async fn read_bytes(&mut self, data: &mut [u8]) { |
| 198 | unsafe { self.sm.set_y(u32::MAX as u32) }; | ||
| 160 | let (rx, tx) = self.sm.rx_tx(); | 199 | let (rx, tx) = self.sm.rx_tx(); |
| 161 | for b in data { | 200 | for b in data { |
| 162 | // Write all 1's so that we can read what the device responds | 201 | // Write all 1's so that we can read what the device responds |
