aboutsummaryrefslogtreecommitdiff
path: root/embassy-rp/src/pio_programs
diff options
context:
space:
mode:
Diffstat (limited to 'embassy-rp/src/pio_programs')
-rw-r--r--embassy-rp/src/pio_programs/onewire.rs315
1 files changed, 254 insertions, 61 deletions
diff --git a/embassy-rp/src/pio_programs/onewire.rs b/embassy-rp/src/pio_programs/onewire.rs
index 00783aab0..9f2ed5695 100644
--- a/embassy-rp/src/pio_programs/onewire.rs
+++ b/embassy-rp/src/pio_programs/onewire.rs
@@ -1,11 +1,18 @@
1//! OneWire pio driver 1//! OneWire pio driver
2 2
3use crate::pio::{Common, Config, Instance, LoadedProgram, PioPin, ShiftConfig, ShiftDirection, StateMachine}; 3use crate::clocks::clk_sys_freq;
4use crate::gpio::Level;
5use crate::pio::{
6 Common, Config, Direction, Instance, LoadedProgram, PioPin, ShiftConfig, ShiftDirection, StateMachine,
7};
4use crate::Peri; 8use crate::Peri;
5 9
6/// This struct represents an onewire driver program 10/// This struct represents a onewire driver program
7pub struct PioOneWireProgram<'a, PIO: Instance> { 11pub struct PioOneWireProgram<'a, PIO: Instance> {
8 prg: LoadedProgram<'a, PIO>, 12 prg: LoadedProgram<'a, PIO>,
13 reset_addr: u8,
14 next_bit_addr: u8,
15 search_addr: u8,
9} 16}
10 17
11impl<'a, PIO: Instance> PioOneWireProgram<'a, PIO> { 18impl<'a, PIO: Instance> PioOneWireProgram<'a, PIO> {
@@ -13,56 +20,86 @@ impl<'a, PIO: Instance> PioOneWireProgram<'a, PIO> {
13 pub fn new(common: &mut Common<'a, PIO>) -> Self { 20 pub fn new(common: &mut Common<'a, PIO>) -> Self {
14 let prg = pio::pio_asm!( 21 let prg = pio::pio_asm!(
15 r#" 22 r#"
16 .wrap_target 23 ; We need to use the pins direction to simulate open drain output
17 again: 24 ; This results in all the side-set values being swapped from the actual pin value
18 pull block 25 .side_set 1 pindirs
19 mov x, osr 26
20 jmp !x, read 27 ; Set the origin to 0 so we can correctly use jmp instructions externally
21 write: 28 .origin 0
22 set pindirs, 1 29
23 set pins, 0 30 ; Tick rate is 1 tick per 6us, so all delays should be calculated back to that
24 loop1: 31 ; All the instructions have a calculated delay in [], -1 for the instruction
25 jmp x--,loop1 32 ; The delay also be 0 which will take 6us for the instruction itself
26 set pindirs, 0 [31] 33 .define CLK 6
27 wait 1 pin 0 [31] 34
28 pull block 35 ; Write the reset block after trigger
29 mov x, osr 36 public reset:
30 bytes1: 37 set x, 4 side 0 [(60 / CLK) - 1] ; idle before reset
31 pull block 38 reset_inner: ; Repeat the following 5 times, so 5*96us = 480us in total
32 set y, 7 39 nop side 1 [(90 / CLK) - 1]
33 set pindirs, 1 40 jmp x--, reset_inner side 1 [( 6 / CLK) - 1]
34 bit1: 41 ; Fallthrough
35 set pins, 0 [1] 42
36 out pins,1 [31] 43 ; Check for presence of one or more devices.
37 set pins, 1 [20] 44 ; This samples 32 times with an interval of 12us after a 18us delay.
38 jmp y--,bit1 45 ; If any bit is zero in the end value, there is a detection
39 jmp x--,bytes1 46 ; This whole function takes 480us
40 set pindirs, 0 [31] 47 set x, 31 side 0 [(24 / CLK) - 1] ; Loop 32 times -> 32*12us = 384us
41 jmp again 48 presence_check:
42 read: 49 in pins, 1 side 0 [( 6 / CLK) - 1] ; poll pin and push to isr
43 pull block 50 jmp x--, presence_check side 0 [( 6 / CLK) - 1]
44 mov x, osr 51 jmp next_bit side 0 [(72 / CLK) - 1]
45 bytes2: 52
46 set y, 7 53 ; The low pulse was already done, we only need to delay and poll the bit in case we are reading
47 bit2: 54 write_1:
48 set pindirs, 1 55 nop side 0 [( 6 / CLK) - 1] ; Delay before sampling the input pin
49 set pins, 0 [1] 56 in pins, 1 side 0 [(54 / CLK) - 1] ; This writes the state of the pin into the ISR
50 set pindirs, 0 [5] 57 ; Fallthrough
51 in pins,1 [10] 58
52 jmp y--,bit2 59 ; This is the entry point when reading and writing data
53 jmp x--,bytes2 60 public next_bit:
54 .wrap 61 .wrap_target
55 "#, 62 out x, 1 side 0 [(18 / CLK) - 1] ; Stalls if no data available in TX FIFO and OSR
63 jmp x--, write_1 side 1 [(12 / 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 [(60 / 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 .wrap
67
68 public search:
69 set x, 1 side 0 [(78 / CLK) - 1] ; Set x to 1 for the inner loop
70 search_inner:
71 ; Read 2 bits
72 nop side 1 [(12 / CLK) - 1] ; Do the always low part of a bit
73 nop side 0 [( 6 / CLK) - 1] ; Delay before sampling the input pin
74 in pins, 1 side 0 [(54 / CLK) - 1] ; This writes the state of the pin into the ISR
75 jmp x--, search_inner side 0 [(18 / CLK) - 1]
76 ; Fallthrough
77
78 ; Write output
79 out x, 1 side 0 [( 6 / CLK) - 1] ; Stalls if no data available in TX FIFO and OSR
80 jmp x--, search side 1 [(12 / CLK) - 1] ; Do the always low part of a bit, jump to search to write a 1 bit
81 ; Fallthrough
82
83 set x, 1 side 1 [(60 / CLK) - 1] ; Set x to 1 for the inner loop, write the remainder of the low part of a 0 bit
84 jmp search_inner side 0 [(18 / CLK) - 1]
85 "#
56 ); 86 );
57 let prg = common.load_program(&prg.program);
58 87
59 Self { prg } 88 Self {
89 prg: common.load_program(&prg.program),
90 reset_addr: prg.public_defines.reset as u8,
91 next_bit_addr: prg.public_defines.next_bit as u8,
92 search_addr: prg.public_defines.search as u8,
93 }
60 } 94 }
61} 95}
62
63/// Pio backed OneWire driver 96/// Pio backed OneWire driver
64pub struct PioOneWire<'d, PIO: Instance, const SM: usize> { 97pub struct PioOneWire<'d, PIO: Instance, const SM: usize> {
65 sm: StateMachine<'d, PIO, SM>, 98 sm: StateMachine<'d, PIO, SM>,
99 cfg: Config<'d, PIO>,
100 reset_addr: u8,
101 search_addr: u8,
102 next_bit_addr: u8,
66} 103}
67 104
68impl<'d, PIO: Instance, const SM: usize> PioOneWire<'d, PIO, SM> { 105impl<'d, PIO: Instance, const SM: usize> PioOneWire<'d, PIO, SM> {
@@ -74,37 +111,193 @@ impl<'d, PIO: Instance, const SM: usize> PioOneWire<'d, PIO, SM> {
74 program: &PioOneWireProgram<'d, PIO>, 111 program: &PioOneWireProgram<'d, PIO>,
75 ) -> Self { 112 ) -> Self {
76 let pin = common.make_pio_pin(pin); 113 let pin = common.make_pio_pin(pin);
114
115 sm.set_pin_dirs(Direction::In, &[&pin]);
116 sm.set_pins(Level::Low, &[&pin]);
117
77 let mut cfg = Config::default(); 118 let mut cfg = Config::default();
78 cfg.use_program(&program.prg, &[]); 119 cfg.use_program(&program.prg, &[&pin]);
79 cfg.set_out_pins(&[&pin]);
80 cfg.set_in_pins(&[&pin]); 120 cfg.set_in_pins(&[&pin]);
81 cfg.set_set_pins(&[&pin]); 121
82 cfg.shift_in = ShiftConfig { 122 let byte_shift = ShiftConfig {
83 auto_fill: true, 123 auto_fill: true,
84 direction: ShiftDirection::Right, 124 direction: ShiftDirection::Right,
85 threshold: 8, 125 threshold: 8,
86 }; 126 };
87 cfg.clock_divider = 255_u8.into(); 127 cfg.shift_in = byte_shift;
128 cfg.shift_out = byte_shift;
129
130 let divider = (clk_sys_freq() / 1000000) as u16 * 6;
131 cfg.clock_divider = divider.into();
132
88 sm.set_config(&cfg); 133 sm.set_config(&cfg);
134 sm.clear_fifos();
135 sm.restart();
136 unsafe {
137 sm.exec_jmp(program.next_bit_addr);
138 }
89 sm.set_enable(true); 139 sm.set_enable(true);
90 Self { sm } 140
141 Self {
142 sm,
143 cfg,
144 reset_addr: program.reset_addr,
145 search_addr: program.search_addr,
146 next_bit_addr: program.next_bit_addr,
147 }
148 }
149
150 pub async fn reset(&mut self) -> bool {
151 // The state machine immediately starts running when jumping to this address
152 unsafe {
153 self.sm.exec_jmp(self.reset_addr);
154 }
155
156 let rx = self.sm.rx();
157 let mut found = false;
158 for _ in 0..4 {
159 if rx.wait_pull().await != 0 {
160 found = true;
161 }
162 }
163
164 found
91 } 165 }
92 166
93 /// Write bytes over the wire 167 /// Write bytes over the wire
94 pub async fn write_bytes(&mut self, bytes: &[u8]) { 168 pub async fn write_bytes(&mut self, data: &[u8]) {
95 self.sm.tx().wait_push(250).await; 169 let (rx, tx) = self.sm.rx_tx();
96 self.sm.tx().wait_push(bytes.len() as u32 - 1).await; 170 for b in data {
97 for b in bytes { 171 tx.wait_push(*b as u32).await;
98 self.sm.tx().wait_push(*b as u32).await; 172
173 // Empty the buffer that is always filled
174 let _ = rx.wait_pull().await;
99 } 175 }
100 } 176 }
101 177
102 /// Read bytes from the wire 178 /// Read bytes from the wire
103 pub async fn read_bytes(&mut self, bytes: &mut [u8]) { 179 pub async fn read_bytes(&mut self, data: &mut [u8]) {
104 self.sm.tx().wait_push(0).await; 180 let (rx, tx) = self.sm.rx_tx();
105 self.sm.tx().wait_push(bytes.len() as u32 - 1).await; 181 for b in data {
106 for b in bytes.iter_mut() { 182 // Write all 1's so that we can read what the device responds
107 *b = (self.sm.rx().wait_pull().await >> 24) as u8; 183 tx.wait_push(0xff).await;
184
185 *b = (rx.wait_pull().await >> 24) as u8;
186 }
187 }
188
189 async fn search(&mut self, state: &mut PioOneWireSearch) -> Option<u64> {
190 let _ = self.reset().await;
191 self.write_bytes(&[0xF0]).await;
192
193 let shift_cfg = self.prepare_search();
194
195 let (rx, tx) = self.sm.rx_tx();
196
197 let mut value = 0u64;
198 let mut last_zero = 0;
199
200 for bit in 0..64 {
201 let push = match rx.wait_pull().await {
202 0b00 => {
203 let write_value = if bit < state.last_discrepancy {
204 (state.last_rom & (1 << bit)) != 0
205 } else {
206 bit == state.last_discrepancy
207 };
208
209 if write_value {
210 1
211 } else {
212 last_zero = bit;
213 0
214 }
215 }
216 0b01 => 0,
217 0b10 => 1,
218 _ => {
219 self.restore_after_search(&shift_cfg);
220 state.finished = true;
221 return None;
222 }
223 };
224 value >>= 1;
225 if push == 1 {
226 value |= 1 << 63;
227 }
228 tx.wait_push(push).await;
229 }
230
231 self.restore_after_search(&shift_cfg);
232
233 state.last_discrepancy = last_zero;
234 state.finished = last_zero == 0;
235 state.last_rom = value;
236 Some(value)
237 }
238
239 fn prepare_search(&mut self) -> ShiftConfig {
240 let shift_cfg = self.cfg.shift_in;
241 self.cfg.shift_in = ShiftConfig {
242 auto_fill: true,
243 direction: ShiftDirection::Left,
244 threshold: 2,
245 };
246 self.cfg.shift_out = ShiftConfig {
247 auto_fill: true,
248 direction: ShiftDirection::Right,
249 threshold: 1,
250 };
251
252 self.sm.set_enable(false);
253 self.sm.set_config(&self.cfg);
254
255 unsafe {
256 self.sm.exec_jmp(self.search_addr);
108 } 257 }
258 self.sm.set_enable(true);
259 shift_cfg
260 }
261
262 fn restore_after_search(&mut self, cfg: &ShiftConfig) {
263 self.cfg.shift_in = *cfg;
264 self.cfg.shift_out = *cfg;
265
266 self.sm.set_enable(false);
267 self.sm.set_config(&self.cfg);
268 unsafe {
269 self.sm.exec_jmp(self.next_bit_addr);
270 }
271 self.sm.clear_fifos();
272 self.sm.restart();
273 self.sm.set_enable(true);
274 }
275}
276
277pub struct PioOneWireSearch {
278 last_rom: u64,
279 last_discrepancy: u8,
280 finished: bool,
281}
282
283impl PioOneWireSearch {
284 pub fn new() -> Self {
285 Self {
286 last_rom: 0,
287 last_discrepancy: 0,
288 finished: false,
289 }
290 }
291
292 pub async fn next<PIO: Instance, const SM: usize>(&mut self, pio: &mut PioOneWire<'_, PIO, SM>) -> Option<u64> {
293 if self.finished {
294 None
295 } else {
296 pio.search(self).await
297 }
298 }
299
300 pub fn is_finished(&self) -> bool {
301 self.finished
109 } 302 }
110} 303}