diff options
| author | Dario Nieuwenhuis <[email protected]> | 2024-09-17 19:17:16 +0000 |
|---|---|---|
| committer | GitHub <[email protected]> | 2024-09-17 19:17:16 +0000 |
| commit | 303bcc4c16d656f1bd12586b0db5dd6f8ce14e75 (patch) | |
| tree | d5383ca86661121361b9bfa63854667534b5c537 /examples | |
| parent | 74ad31466b13c47734412ce830eab65d6de5d364 (diff) | |
| parent | 0bfc98a3e526075ad14517589e4879d14f50ad12 (diff) | |
Merge pull request #3347 from kalkyl/pio-onewire
rp: Add PIO example for one-wire temperature sensor
Diffstat (limited to 'examples')
| -rw-r--r-- | examples/rp/src/bin/pio_onewire.rs | 155 |
1 files changed, 155 insertions, 0 deletions
diff --git a/examples/rp/src/bin/pio_onewire.rs b/examples/rp/src/bin/pio_onewire.rs new file mode 100644 index 000000000..5076101ec --- /dev/null +++ b/examples/rp/src/bin/pio_onewire.rs | |||
| @@ -0,0 +1,155 @@ | |||
| 1 | //! This example shows how you can use PIO to read a `DS18B20` one-wire temperature sensor. | ||
| 2 | |||
| 3 | #![no_std] | ||
| 4 | #![no_main] | ||
| 5 | use defmt::*; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_rp::bind_interrupts; | ||
| 8 | use embassy_rp::peripherals::PIO0; | ||
| 9 | use embassy_rp::pio::{self, Common, Config, InterruptHandler, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine}; | ||
| 10 | use embassy_time::Timer; | ||
| 11 | use {defmt_rtt as _, panic_probe as _}; | ||
| 12 | |||
| 13 | bind_interrupts!(struct Irqs { | ||
| 14 | PIO0_IRQ_0 => InterruptHandler<PIO0>; | ||
| 15 | }); | ||
| 16 | |||
| 17 | #[embassy_executor::main] | ||
| 18 | async fn main(_spawner: Spawner) { | ||
| 19 | let p = embassy_rp::init(Default::default()); | ||
| 20 | let mut pio = Pio::new(p.PIO0, Irqs); | ||
| 21 | let mut sensor = Ds18b20::new(&mut pio.common, pio.sm0, p.PIN_2); | ||
| 22 | |||
| 23 | loop { | ||
| 24 | sensor.start().await; // Start a new measurement | ||
| 25 | Timer::after_secs(1).await; // Allow 1s for the measurement to finish | ||
| 26 | match sensor.temperature().await { | ||
| 27 | Ok(temp) => info!("temp = {:?} deg C", temp), | ||
| 28 | _ => error!("sensor error"), | ||
| 29 | } | ||
| 30 | Timer::after_secs(1).await; | ||
| 31 | } | ||
| 32 | } | ||
| 33 | |||
| 34 | /// DS18B20 temperature sensor driver | ||
| 35 | pub struct Ds18b20<'d, PIO: pio::Instance, const SM: usize> { | ||
| 36 | sm: StateMachine<'d, PIO, SM>, | ||
| 37 | } | ||
| 38 | |||
| 39 | impl<'d, PIO: pio::Instance, const SM: usize> Ds18b20<'d, PIO, SM> { | ||
| 40 | /// Create a new instance the driver | ||
| 41 | pub fn new(common: &mut Common<'d, PIO>, mut sm: StateMachine<'d, PIO, SM>, pin: impl PioPin) -> Self { | ||
| 42 | let prg = pio_proc::pio_asm!( | ||
| 43 | r#" | ||
| 44 | .wrap_target | ||
| 45 | again: | ||
| 46 | pull block | ||
| 47 | mov x, osr | ||
| 48 | jmp !x, read | ||
| 49 | write: | ||
| 50 | set pindirs, 1 | ||
| 51 | set pins, 0 | ||
| 52 | loop1: | ||
| 53 | jmp x--,loop1 | ||
| 54 | set pindirs, 0 [31] | ||
| 55 | wait 1 pin 0 [31] | ||
| 56 | pull block | ||
| 57 | mov x, osr | ||
| 58 | bytes1: | ||
| 59 | pull block | ||
| 60 | set y, 7 | ||
| 61 | set pindirs, 1 | ||
| 62 | bit1: | ||
| 63 | set pins, 0 [1] | ||
| 64 | out pins,1 [31] | ||
| 65 | set pins, 1 [20] | ||
| 66 | jmp y--,bit1 | ||
| 67 | jmp x--,bytes1 | ||
| 68 | set pindirs, 0 [31] | ||
| 69 | jmp again | ||
| 70 | read: | ||
| 71 | pull block | ||
| 72 | mov x, osr | ||
| 73 | bytes2: | ||
| 74 | set y, 7 | ||
| 75 | bit2: | ||
| 76 | set pindirs, 1 | ||
| 77 | set pins, 0 [1] | ||
| 78 | set pindirs, 0 [5] | ||
| 79 | in pins,1 [10] | ||
| 80 | jmp y--,bit2 | ||
| 81 | jmp x--,bytes2 | ||
| 82 | .wrap | ||
| 83 | "#, | ||
| 84 | ); | ||
| 85 | |||
| 86 | let pin = common.make_pio_pin(pin); | ||
| 87 | let mut cfg = Config::default(); | ||
| 88 | cfg.use_program(&common.load_program(&prg.program), &[]); | ||
| 89 | cfg.set_out_pins(&[&pin]); | ||
| 90 | cfg.set_in_pins(&[&pin]); | ||
| 91 | cfg.set_set_pins(&[&pin]); | ||
| 92 | cfg.shift_in = ShiftConfig { | ||
| 93 | auto_fill: true, | ||
| 94 | direction: ShiftDirection::Right, | ||
| 95 | threshold: 8, | ||
| 96 | }; | ||
| 97 | cfg.clock_divider = 255_u8.into(); | ||
| 98 | sm.set_config(&cfg); | ||
| 99 | sm.set_enable(true); | ||
| 100 | Self { sm } | ||
| 101 | } | ||
| 102 | |||
| 103 | /// Write bytes over the wire | ||
| 104 | async fn write_bytes(&mut self, bytes: &[u8]) { | ||
| 105 | self.sm.tx().wait_push(250).await; | ||
| 106 | self.sm.tx().wait_push(bytes.len() as u32 - 1).await; | ||
| 107 | for b in bytes { | ||
| 108 | self.sm.tx().wait_push(*b as u32).await; | ||
| 109 | } | ||
| 110 | } | ||
| 111 | |||
| 112 | /// Read bytes from the wire | ||
| 113 | async fn read_bytes(&mut self, bytes: &mut [u8]) { | ||
| 114 | self.sm.tx().wait_push(0).await; | ||
| 115 | self.sm.tx().wait_push(bytes.len() as u32 - 1).await; | ||
| 116 | for b in bytes.iter_mut() { | ||
| 117 | *b = (self.sm.rx().wait_pull().await >> 24) as u8; | ||
| 118 | } | ||
| 119 | } | ||
| 120 | |||
| 121 | /// Calculate CRC8 of the data | ||
| 122 | fn crc8(data: &[u8]) -> u8 { | ||
| 123 | let mut temp; | ||
| 124 | let mut data_byte; | ||
| 125 | let mut crc = 0; | ||
| 126 | for b in data { | ||
| 127 | data_byte = *b; | ||
| 128 | for _ in 0..8 { | ||
| 129 | temp = (crc ^ data_byte) & 0x01; | ||
| 130 | crc >>= 1; | ||
| 131 | if temp != 0 { | ||
| 132 | crc ^= 0x8C; | ||
| 133 | } | ||
| 134 | data_byte >>= 1; | ||
| 135 | } | ||
| 136 | } | ||
| 137 | crc | ||
| 138 | } | ||
| 139 | |||
| 140 | /// Start a new measurement. Allow at least 1000ms before getting `temperature`. | ||
| 141 | pub async fn start(&mut self) { | ||
| 142 | self.write_bytes(&[0xCC, 0x44]).await; | ||
| 143 | } | ||
| 144 | |||
| 145 | /// Read the temperature. Ensure >1000ms has passed since `start` before calling this. | ||
| 146 | pub async fn temperature(&mut self) -> Result<f32, ()> { | ||
| 147 | self.write_bytes(&[0xCC, 0xBE]).await; | ||
| 148 | let mut data = [0; 9]; | ||
| 149 | self.read_bytes(&mut data).await; | ||
| 150 | match Self::crc8(&data) == 0 { | ||
| 151 | true => Ok(((data[1] as u32) << 8 | data[0] as u32) as f32 / 16.), | ||
| 152 | false => Err(()), | ||
| 153 | } | ||
| 154 | } | ||
| 155 | } | ||
