aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--embassy-rp/CHANGELOG.md1
-rw-r--r--embassy-rp/src/pio_programs/onewire.rs45
-rw-r--r--examples/rp/src/bin/pio_onewire.rs1
-rw-r--r--examples/rp/src/bin/pio_onewire_parasite.rs89
4 files changed, 133 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
diff --git a/examples/rp/src/bin/pio_onewire.rs b/examples/rp/src/bin/pio_onewire.rs
index 379e2b8f9..102f13c45 100644
--- a/examples/rp/src/bin/pio_onewire.rs
+++ b/examples/rp/src/bin/pio_onewire.rs
@@ -1,4 +1,5 @@
1//! This example shows how you can use PIO to read one or more `DS18B20` one-wire temperature sensors. 1//! This example shows how you can use PIO to read one or more `DS18B20` one-wire temperature sensors.
2//! This uses externally powered sensors. For parasite power, see the pio_onewire_parasite.rs example.
2 3
3#![no_std] 4#![no_std]
4#![no_main] 5#![no_main]
diff --git a/examples/rp/src/bin/pio_onewire_parasite.rs b/examples/rp/src/bin/pio_onewire_parasite.rs
new file mode 100644
index 000000000..fd076dee0
--- /dev/null
+++ b/examples/rp/src/bin/pio_onewire_parasite.rs
@@ -0,0 +1,89 @@
1//! This example shows how you can use PIO to read one or more `DS18B20`
2//! one-wire temperature sensors using parasite power.
3//! It applies a strong pullup during conversion, see "Powering the DS18B20" in the datasheet.
4//! For externally powered sensors, use the pio_onewire.rs example.
5
6#![no_std]
7#![no_main]
8use defmt::*;
9use embassy_executor::Spawner;
10use embassy_rp::bind_interrupts;
11use embassy_rp::peripherals::PIO0;
12use embassy_rp::pio::{InterruptHandler, Pio};
13use embassy_rp::pio_programs::onewire::{PioOneWire, PioOneWireProgram, PioOneWireSearch};
14use embassy_time::Duration;
15use heapless::Vec;
16use {defmt_rtt as _, panic_probe as _};
17
18bind_interrupts!(struct Irqs {
19 PIO0_IRQ_0 => InterruptHandler<PIO0>;
20});
21
22#[embassy_executor::main]
23async fn main(_spawner: Spawner) {
24 let p = embassy_rp::init(Default::default());
25 let mut pio = Pio::new(p.PIO0, Irqs);
26
27 let prg = PioOneWireProgram::new(&mut pio.common);
28 let mut onewire = PioOneWire::new(&mut pio.common, pio.sm0, p.PIN_2, &prg);
29
30 info!("Starting onewire search");
31
32 let mut devices = Vec::<u64, 10>::new();
33 let mut search = PioOneWireSearch::new();
34 for _ in 0..10 {
35 if !search.is_finished() {
36 if let Some(address) = search.next(&mut onewire).await {
37 if crc8(&address.to_le_bytes()) == 0 {
38 info!("Found address: {:x}", address);
39 let _ = devices.push(address);
40 } else {
41 warn!("Found invalid address: {:x}", address);
42 }
43 }
44 }
45 }
46
47 info!("Search done, found {} devices", devices.len());
48
49 loop {
50 // Read all devices one by one
51 for device in &devices {
52 onewire.reset().await;
53 onewire.write_bytes(&[0x55]).await; // Match rom
54 onewire.write_bytes(&device.to_le_bytes()).await;
55 // 750 ms delay required for default 12-bit resolution.
56 onewire.write_bytes_pullup(&[0x44], Duration::from_millis(750)).await;
57
58 onewire.reset().await;
59 onewire.write_bytes(&[0x55]).await; // Match rom
60 onewire.write_bytes(&device.to_le_bytes()).await;
61 onewire.write_bytes(&[0xBE]).await; // Read scratchpad
62
63 let mut data = [0; 9];
64 onewire.read_bytes(&mut data).await;
65 if crc8(&data) == 0 {
66 let temp = ((data[1] as u32) << 8 | data[0] as u32) as f32 / 16.;
67 info!("Read device {:x}: {} deg C", device, temp);
68 } else {
69 warn!("Reading device {:x} failed. {:02x}", device, data);
70 }
71 }
72 }
73}
74
75fn crc8(data: &[u8]) -> u8 {
76 let mut crc = 0;
77 for b in data {
78 let mut data_byte = *b;
79 for _ in 0..8 {
80 let temp = (crc ^ data_byte) & 0x01;
81 crc >>= 1;
82 if temp != 0 {
83 crc ^= 0x8C;
84 }
85 data_byte >>= 1;
86 }
87 }
88 crc
89}