diff options
Diffstat (limited to 'embassy-rp/src/pio_programs')
| -rw-r--r-- | embassy-rp/src/pio_programs/onewire.rs | 117 |
1 files changed, 56 insertions, 61 deletions
diff --git a/embassy-rp/src/pio_programs/onewire.rs b/embassy-rp/src/pio_programs/onewire.rs index 9f2ed5695..82fd98b96 100644 --- a/embassy-rp/src/pio_programs/onewire.rs +++ b/embassy-rp/src/pio_programs/onewire.rs | |||
| @@ -12,7 +12,6 @@ pub struct PioOneWireProgram<'a, PIO: Instance> { | |||
| 12 | prg: LoadedProgram<'a, PIO>, | 12 | prg: LoadedProgram<'a, PIO>, |
| 13 | reset_addr: u8, | 13 | reset_addr: u8, |
| 14 | next_bit_addr: u8, | 14 | next_bit_addr: u8, |
| 15 | search_addr: u8, | ||
| 16 | } | 15 | } |
| 17 | 16 | ||
| 18 | impl<'a, PIO: Instance> PioOneWireProgram<'a, PIO> { | 17 | impl<'a, PIO: Instance> PioOneWireProgram<'a, PIO> { |
| @@ -53,35 +52,17 @@ impl<'a, PIO: Instance> PioOneWireProgram<'a, PIO> { | |||
| 53 | ; The low pulse was already done, we only need to delay and poll the bit in case we are reading | 52 | ; The low pulse was already done, we only need to delay and poll the bit in case we are reading |
| 54 | write_1: | 53 | write_1: |
| 55 | nop side 0 [( 6 / CLK) - 1] ; Delay before sampling the input pin | 54 | nop side 0 [( 6 / CLK) - 1] ; Delay before sampling the input pin |
| 56 | in pins, 1 side 0 [(54 / CLK) - 1] ; This writes the state of the pin into the ISR | 55 | in pins, 1 side 0 [(48 / CLK) - 1] ; This writes the state of the pin into the ISR |
| 57 | ; Fallthrough | 56 | ; Fallthrough |
| 58 | 57 | ||
| 59 | ; This is the entry point when reading and writing data | 58 | ; This is the entry point when reading and writing data |
| 60 | public next_bit: | 59 | public next_bit: |
| 61 | .wrap_target | 60 | .wrap_target |
| 62 | out x, 1 side 0 [(18 / CLK) - 1] ; Stalls if no data available in TX FIFO and OSR | 61 | 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 [(12 / CLK) - 1] ; Do the always low part of a bit, jump to write_1 if we want to write a 1 bit | 62 | 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 [(60 / CLK) - 1] ; Do the remainder of the low part of a 0 bit | 63 | in null, 1 side 1 [(54 / 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 | 64 | ; This writes 0 into the ISR so that the shift count stays in sync |
| 66 | .wrap | 65 | .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 | "# | 66 | "# |
| 86 | ); | 67 | ); |
| 87 | 68 | ||
| @@ -89,7 +70,6 @@ impl<'a, PIO: Instance> PioOneWireProgram<'a, PIO> { | |||
| 89 | prg: common.load_program(&prg.program), | 70 | prg: common.load_program(&prg.program), |
| 90 | reset_addr: prg.public_defines.reset as u8, | 71 | reset_addr: prg.public_defines.reset as u8, |
| 91 | next_bit_addr: prg.public_defines.next_bit as u8, | 72 | next_bit_addr: prg.public_defines.next_bit as u8, |
| 92 | search_addr: prg.public_defines.search as u8, | ||
| 93 | } | 73 | } |
| 94 | } | 74 | } |
| 95 | } | 75 | } |
| @@ -98,7 +78,6 @@ pub struct PioOneWire<'d, PIO: Instance, const SM: usize> { | |||
| 98 | sm: StateMachine<'d, PIO, SM>, | 78 | sm: StateMachine<'d, PIO, SM>, |
| 99 | cfg: Config<'d, PIO>, | 79 | cfg: Config<'d, PIO>, |
| 100 | reset_addr: u8, | 80 | reset_addr: u8, |
| 101 | search_addr: u8, | ||
| 102 | next_bit_addr: u8, | 81 | next_bit_addr: u8, |
| 103 | } | 82 | } |
| 104 | 83 | ||
| @@ -119,13 +98,16 @@ impl<'d, PIO: Instance, const SM: usize> PioOneWire<'d, PIO, SM> { | |||
| 119 | cfg.use_program(&program.prg, &[&pin]); | 98 | cfg.use_program(&program.prg, &[&pin]); |
| 120 | cfg.set_in_pins(&[&pin]); | 99 | cfg.set_in_pins(&[&pin]); |
| 121 | 100 | ||
| 122 | let byte_shift = ShiftConfig { | 101 | cfg.shift_in = ShiftConfig { |
| 102 | auto_fill: true, | ||
| 103 | direction: ShiftDirection::Right, | ||
| 104 | threshold: 8, | ||
| 105 | }; | ||
| 106 | cfg.shift_out = ShiftConfig { | ||
| 123 | auto_fill: true, | 107 | auto_fill: true, |
| 124 | direction: ShiftDirection::Right, | 108 | direction: ShiftDirection::Right, |
| 125 | threshold: 8, | 109 | threshold: 8, |
| 126 | }; | 110 | }; |
| 127 | cfg.shift_in = byte_shift; | ||
| 128 | cfg.shift_out = byte_shift; | ||
| 129 | 111 | ||
| 130 | let divider = (clk_sys_freq() / 1000000) as u16 * 6; | 112 | let divider = (clk_sys_freq() / 1000000) as u16 * 6; |
| 131 | cfg.clock_divider = divider.into(); | 113 | cfg.clock_divider = divider.into(); |
| @@ -142,11 +124,11 @@ impl<'d, PIO: Instance, const SM: usize> PioOneWire<'d, PIO, SM> { | |||
| 142 | sm, | 124 | sm, |
| 143 | cfg, | 125 | cfg, |
| 144 | reset_addr: program.reset_addr, | 126 | reset_addr: program.reset_addr, |
| 145 | search_addr: program.search_addr, | ||
| 146 | next_bit_addr: program.next_bit_addr, | 127 | next_bit_addr: program.next_bit_addr, |
| 147 | } | 128 | } |
| 148 | } | 129 | } |
| 149 | 130 | ||
| 131 | /// Perform an initialization sequence, will return true if a presence pulse was detected from a device | ||
| 150 | pub async fn reset(&mut self) -> bool { | 132 | pub async fn reset(&mut self) -> bool { |
| 151 | // The state machine immediately starts running when jumping to this address | 133 | // The state machine immediately starts running when jumping to this address |
| 152 | unsafe { | 134 | unsafe { |
| @@ -164,18 +146,18 @@ impl<'d, PIO: Instance, const SM: usize> PioOneWire<'d, PIO, SM> { | |||
| 164 | found | 146 | found |
| 165 | } | 147 | } |
| 166 | 148 | ||
| 167 | /// Write bytes over the wire | 149 | /// Write bytes to the onewire bus |
| 168 | pub async fn write_bytes(&mut self, data: &[u8]) { | 150 | pub async fn write_bytes(&mut self, data: &[u8]) { |
| 169 | let (rx, tx) = self.sm.rx_tx(); | 151 | let (rx, tx) = self.sm.rx_tx(); |
| 170 | for b in data { | 152 | for b in data { |
| 171 | tx.wait_push(*b as u32).await; | 153 | tx.wait_push(*b as u32).await; |
| 172 | 154 | ||
| 173 | // Empty the buffer that is always filled | 155 | // Empty the buffer that is being filled with every write |
| 174 | let _ = rx.wait_pull().await; | 156 | let _ = rx.wait_pull().await; |
| 175 | } | 157 | } |
| 176 | } | 158 | } |
| 177 | 159 | ||
| 178 | /// Read bytes from the wire | 160 | /// Read bytes from the onewire bus |
| 179 | pub async fn read_bytes(&mut self, data: &mut [u8]) { | 161 | pub async fn read_bytes(&mut self, data: &mut [u8]) { |
| 180 | let (rx, tx) = self.sm.rx_tx(); | 162 | let (rx, tx) = self.sm.rx_tx(); |
| 181 | for b in data { | 163 | for b in data { |
| @@ -187,19 +169,29 @@ impl<'d, PIO: Instance, const SM: usize> PioOneWire<'d, PIO, SM> { | |||
| 187 | } | 169 | } |
| 188 | 170 | ||
| 189 | async fn search(&mut self, state: &mut PioOneWireSearch) -> Option<u64> { | 171 | async fn search(&mut self, state: &mut PioOneWireSearch) -> Option<u64> { |
| 190 | let _ = self.reset().await; | 172 | if !self.reset().await { |
| 191 | self.write_bytes(&[0xF0]).await; | 173 | // No device present, no use in searching |
| 174 | state.finished = true; | ||
| 175 | return None; | ||
| 176 | } | ||
| 177 | self.write_bytes(&[0xF0]).await; // 0xF0 is the search rom command | ||
| 192 | 178 | ||
| 193 | let shift_cfg = self.prepare_search(); | 179 | self.prepare_search(); |
| 194 | 180 | ||
| 195 | let (rx, tx) = self.sm.rx_tx(); | 181 | let (rx, tx) = self.sm.rx_tx(); |
| 196 | 182 | ||
| 197 | let mut value = 0u64; | 183 | let mut value = 0; |
| 198 | let mut last_zero = 0; | 184 | let mut last_zero = 0; |
| 199 | 185 | ||
| 200 | for bit in 0..64 { | 186 | for bit in 0..64 { |
| 201 | let push = match rx.wait_pull().await { | 187 | // Write 2 dummy bits to read a bit and its complement |
| 202 | 0b00 => { | 188 | tx.wait_push(0x1).await; |
| 189 | tx.wait_push(0x1).await; | ||
| 190 | let in1 = rx.wait_pull().await; | ||
| 191 | let in2 = rx.wait_pull().await; | ||
| 192 | let push = match (in1, in2) { | ||
| 193 | (0, 0) => { | ||
| 194 | // If both are 0, it means we have devices with 0 and 1 bits in this position | ||
| 203 | let write_value = if bit < state.last_discrepancy { | 195 | let write_value = if bit < state.last_discrepancy { |
| 204 | (state.last_rom & (1 << bit)) != 0 | 196 | (state.last_rom & (1 << bit)) != 0 |
| 205 | } else { | 197 | } else { |
| @@ -213,10 +205,11 @@ impl<'d, PIO: Instance, const SM: usize> PioOneWire<'d, PIO, SM> { | |||
| 213 | 0 | 205 | 0 |
| 214 | } | 206 | } |
| 215 | } | 207 | } |
| 216 | 0b01 => 0, | 208 | (0, 1) => 0, // Only devices with a 0 bit in this position |
| 217 | 0b10 => 1, | 209 | (1, 0) => 1, // Only devices with a 1 bit in this position |
| 218 | _ => { | 210 | _ => { |
| 219 | self.restore_after_search(&shift_cfg); | 211 | // If both are 1, it means there is no device active and there is no point in continuing |
| 212 | self.restore_after_search(); | ||
| 220 | state.finished = true; | 213 | state.finished = true; |
| 221 | return None; | 214 | return None; |
| 222 | } | 215 | } |
| @@ -226,9 +219,10 @@ impl<'d, PIO: Instance, const SM: usize> PioOneWire<'d, PIO, SM> { | |||
| 226 | value |= 1 << 63; | 219 | value |= 1 << 63; |
| 227 | } | 220 | } |
| 228 | tx.wait_push(push).await; | 221 | tx.wait_push(push).await; |
| 222 | let _ = rx.wait_pull().await; // Discard the result of the write action | ||
| 229 | } | 223 | } |
| 230 | 224 | ||
| 231 | self.restore_after_search(&shift_cfg); | 225 | self.restore_after_search(); |
| 232 | 226 | ||
| 233 | state.last_discrepancy = last_zero; | 227 | state.last_discrepancy = last_zero; |
| 234 | state.finished = last_zero == 0; | 228 | state.finished = last_zero == 0; |
| @@ -236,44 +230,42 @@ impl<'d, PIO: Instance, const SM: usize> PioOneWire<'d, PIO, SM> { | |||
| 236 | Some(value) | 230 | Some(value) |
| 237 | } | 231 | } |
| 238 | 232 | ||
| 239 | fn prepare_search(&mut self) -> ShiftConfig { | 233 | fn prepare_search(&mut self) { |
| 240 | let shift_cfg = self.cfg.shift_in; | 234 | self.cfg.shift_in.threshold = 1; |
| 241 | self.cfg.shift_in = ShiftConfig { | 235 | self.cfg.shift_in.direction = ShiftDirection::Left; |
| 242 | auto_fill: true, | 236 | self.cfg.shift_out.threshold = 1; |
| 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 | 237 | ||
| 252 | self.sm.set_enable(false); | 238 | self.sm.set_enable(false); |
| 253 | self.sm.set_config(&self.cfg); | 239 | self.sm.set_config(&self.cfg); |
| 254 | 240 | ||
| 241 | // set_config jumps to the wrong address so jump to the right one here | ||
| 255 | unsafe { | 242 | unsafe { |
| 256 | self.sm.exec_jmp(self.search_addr); | 243 | self.sm.exec_jmp(self.next_bit_addr); |
| 257 | } | 244 | } |
| 258 | self.sm.set_enable(true); | 245 | self.sm.set_enable(true); |
| 259 | shift_cfg | ||
| 260 | } | 246 | } |
| 261 | 247 | ||
| 262 | fn restore_after_search(&mut self, cfg: &ShiftConfig) { | 248 | fn restore_after_search(&mut self) { |
| 263 | self.cfg.shift_in = *cfg; | 249 | self.cfg.shift_in.threshold = 8; |
| 264 | self.cfg.shift_out = *cfg; | 250 | self.cfg.shift_in.direction = ShiftDirection::Right; |
| 251 | self.cfg.shift_out.threshold = 8; | ||
| 265 | 252 | ||
| 266 | self.sm.set_enable(false); | 253 | self.sm.set_enable(false); |
| 267 | self.sm.set_config(&self.cfg); | 254 | self.sm.set_config(&self.cfg); |
| 255 | |||
| 256 | // Clear the state in case we aborted prematurely with some bits still in the shift registers | ||
| 257 | self.sm.clear_fifos(); | ||
| 258 | self.sm.restart(); | ||
| 259 | |||
| 260 | // set_config jumps to the wrong address so jump to the right one here | ||
| 268 | unsafe { | 261 | unsafe { |
| 269 | self.sm.exec_jmp(self.next_bit_addr); | 262 | self.sm.exec_jmp(self.next_bit_addr); |
| 270 | } | 263 | } |
| 271 | self.sm.clear_fifos(); | ||
| 272 | self.sm.restart(); | ||
| 273 | self.sm.set_enable(true); | 264 | self.sm.set_enable(true); |
| 274 | } | 265 | } |
| 275 | } | 266 | } |
| 276 | 267 | ||
| 268 | /// Onewire search state | ||
| 277 | pub struct PioOneWireSearch { | 269 | pub struct PioOneWireSearch { |
| 278 | last_rom: u64, | 270 | last_rom: u64, |
| 279 | last_discrepancy: u8, | 271 | last_discrepancy: u8, |
| @@ -281,6 +273,7 @@ pub struct PioOneWireSearch { | |||
| 281 | } | 273 | } |
| 282 | 274 | ||
| 283 | impl PioOneWireSearch { | 275 | impl PioOneWireSearch { |
| 276 | /// Create a new Onewire search state | ||
| 284 | pub fn new() -> Self { | 277 | pub fn new() -> Self { |
| 285 | Self { | 278 | Self { |
| 286 | last_rom: 0, | 279 | last_rom: 0, |
| @@ -289,6 +282,7 @@ impl PioOneWireSearch { | |||
| 289 | } | 282 | } |
| 290 | } | 283 | } |
| 291 | 284 | ||
| 285 | /// Search for the next address on the bus | ||
| 292 | pub async fn next<PIO: Instance, const SM: usize>(&mut self, pio: &mut PioOneWire<'_, PIO, SM>) -> Option<u64> { | 286 | pub async fn next<PIO: Instance, const SM: usize>(&mut self, pio: &mut PioOneWire<'_, PIO, SM>) -> Option<u64> { |
| 293 | if self.finished { | 287 | if self.finished { |
| 294 | None | 288 | None |
| @@ -297,6 +291,7 @@ impl PioOneWireSearch { | |||
| 297 | } | 291 | } |
| 298 | } | 292 | } |
| 299 | 293 | ||
| 294 | /// Is finished when all devices have been found | ||
| 300 | pub fn is_finished(&self) -> bool { | 295 | pub fn is_finished(&self) -> bool { |
| 301 | self.finished | 296 | self.finished |
| 302 | } | 297 | } |
