aboutsummaryrefslogtreecommitdiff
path: root/examples/stm32g0/src/bin/onewire_ds18b20.rs
blob: 43ecca8c40c8bbe47d063cb326e6745098b4e53c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
//! This examples shows how you can use buffered or DMA UART to read a DS18B20 temperature sensor on 1-Wire bus.
//! Connect 5k pull-up resistor between PA9 and 3.3V.
#![no_std]
#![no_main]

use cortex_m::singleton;
use defmt::*;
use embassy_executor::Spawner;
use embassy_stm32::mode::Async;
use embassy_stm32::usart::{
    BufferedUartRx, BufferedUartTx, Config, ConfigError, OutputConfig, RingBufferedUartRx, UartTx,
};
use embassy_stm32::{bind_interrupts, peripherals, usart};
use embassy_time::{Duration, Timer};
use {defmt_rtt as _, panic_probe as _};

/// Create onewire bus using DMA USART
fn create_onewire(p: embassy_stm32::Peripherals) -> OneWire<UartTx<'static, Async>, RingBufferedUartRx<'static>> {
    use embassy_stm32::usart::Uart;
    bind_interrupts!(struct Irqs {
        USART1 => usart::InterruptHandler<peripherals::USART1>;
    });

    let mut config = Config::default();
    config.tx_config = OutputConfig::OpenDrain;

    let usart = Uart::new_half_duplex(
        p.USART1,
        p.PA9,
        Irqs,
        p.DMA1_CH1,
        p.DMA1_CH2,
        config,
        // Enable readback so we can read sensor pulling data low while transmission is in progress
        usart::HalfDuplexReadback::Readback,
    )
    .unwrap();

    const BUFFER_SIZE: usize = 16;
    let (tx, rx) = usart.split();
    let rx_buf: &mut [u8; BUFFER_SIZE] = singleton!(TX_BUF: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE]).unwrap();
    let rx = rx.into_ring_buffered(rx_buf);
    OneWire::new(tx, rx)
}

/*
/// Create onewire bus using buffered USART
fn create_onewire(p: embassy_stm32::Peripherals) -> OneWire<BufferedUartTx<'static>, BufferedUartRx<'static>> {
    use embassy_stm32::usart::BufferedUart;
    bind_interrupts!(struct Irqs {
        USART1 => usart::BufferedInterruptHandler<peripherals::USART1>;
    });

    const BUFFER_SIZE: usize = 16;
    let mut config = Confi::default();
    config.tx_config = OutputConfig::OpenDrain;
    let tx_buf: &mut [u8; BUFFER_SIZE] = singleton!(TX_BUF: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE]).unwrap();
    let rx_buf: &mut [u8; BUFFER_SIZE] = singleton!(RX_BUF: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE]).unwrap();
    let usart = BufferedUart::new_half_duplex(
        p.USART1,
        p.PA9,
        Irqs,
        tx_buf,
        rx_buf,
        config,
        // Enable readback so we can read sensor pulling data low while transmission is in progress
        usart::HalfDuplexReadback::Readback,
    )
    .unwrap();
    let (tx, rx) = usart.split();
    OneWire::new(tx, rx)
}
*/

#[embassy_executor::main]
async fn main(_spawner: Spawner) {
    let p = embassy_stm32::init(Default::default());

    let onewire = create_onewire(p);
    let mut sensor = Ds18b20::new(onewire);

    loop {
        // Start a new temperature measurement
        sensor.start().await;
        // Wait for the measurement to finish
        Timer::after(Duration::from_secs(1)).await;
        match sensor.temperature().await {
            Ok(temp) => info!("temp = {:?} deg C", temp),
            _ => error!("sensor error"),
        }
        Timer::after(Duration::from_secs(1)).await;
    }
}

pub trait SetBaudrate {
    fn set_baudrate(&mut self, baudrate: u32) -> Result<(), ConfigError>;
}
impl SetBaudrate for BufferedUartTx<'_> {
    fn set_baudrate(&mut self, baudrate: u32) -> Result<(), ConfigError> {
        BufferedUartTx::set_baudrate(self, baudrate)
    }
}
impl SetBaudrate for BufferedUartRx<'_> {
    fn set_baudrate(&mut self, baudrate: u32) -> Result<(), ConfigError> {
        BufferedUartRx::set_baudrate(self, baudrate)
    }
}
impl SetBaudrate for RingBufferedUartRx<'_> {
    fn set_baudrate(&mut self, baudrate: u32) -> Result<(), ConfigError> {
        RingBufferedUartRx::set_baudrate(self, baudrate)
    }
}
impl SetBaudrate for UartTx<'_, Async> {
    fn set_baudrate(&mut self, baudrate: u32) -> Result<(), ConfigError> {
        UartTx::set_baudrate(self, baudrate)
    }
}

/// Simplified OneWire bus driver
pub struct OneWire<TX, RX>
where
    TX: embedded_io_async::Write + SetBaudrate,
    RX: embedded_io_async::Read + SetBaudrate,
{
    tx: TX,
    rx: RX,
}

impl<TX, RX> OneWire<TX, RX>
where
    TX: embedded_io_async::Write + SetBaudrate,
    RX: embedded_io_async::Read + SetBaudrate,
{
    // bitrate with one bit taking ~104 us
    const RESET_BUADRATE: u32 = 9600;
    // bitrate with one bit taking ~8.7 us
    const BAUDRATE: u32 = 115200;

    // startbit + 8 low bits = 9 * 1/115200 = 78 us low pulse
    const LOGIC_1_CHAR: u8 = 0xFF;
    // startbit only = 1/115200 = 8.7 us low pulse
    const LOGIC_0_CHAR: u8 = 0x00;

    // Address all devices on the bus
    const COMMAND_SKIP_ROM: u8 = 0xCC;

    pub fn new(tx: TX, rx: RX) -> Self {
        Self { tx, rx }
    }

    fn set_baudrate(&mut self, baudrate: u32) -> Result<(), ConfigError> {
        self.tx.set_baudrate(baudrate)?;
        self.rx.set_baudrate(baudrate)
    }

    /// Reset the bus by at least 480 us low pulse.
    pub async fn reset(&mut self) {
        // Switch to 9600 baudrate, so one bit takes ~104 us
        self.set_baudrate(Self::RESET_BUADRATE).expect("set_baudrate failed");
        // Low USART start bit + 4x low bits = 5 * 104 us = 520 us low pulse
        self.tx.write(&[0xF0]).await.expect("write failed");

        // Read the value on the bus
        let mut buffer = [0; 1];
        self.rx.read_exact(&mut buffer).await.expect("read failed");

        // Switch back to 115200 baudrate, so one bit takes ~8.7 us
        self.set_baudrate(Self::BAUDRATE).expect("set_baudrate failed");

        // read and expect sensor pulled some high bits to low (device present)
        if buffer[0] & 0xF != 0 || buffer[0] & 0xF0 == 0xF0 {
            warn!("No device present");
        }
    }

    /// Send byte and read response on the bus.
    pub async fn write_read_byte(&mut self, byte: u8) -> u8 {
        // One byte is sent as 8 UART characters
        let mut tx = [0; 8];
        for (pos, char) in tx.iter_mut().enumerate() {
            *char = if (byte >> pos) & 0x1 == 0x1 {
                Self::LOGIC_1_CHAR
            } else {
                Self::LOGIC_0_CHAR
            };
        }
        self.tx.write_all(&tx).await.expect("write failed");

        // Readback the value on the bus, sensors can pull logic 1 to 0
        let mut rx = [0; 8];
        self.rx.read_exact(&mut rx).await.expect("read failed");
        let mut bus_byte = 0;
        for (pos, char) in rx.iter().enumerate() {
            // if its 0xFF, sensor didnt pull the bus to low level
            if *char == 0xFF {
                bus_byte |= 1 << pos;
            }
        }

        bus_byte
    }

    /// Read a byte from the bus.
    pub async fn read_byte(&mut self) -> u8 {
        self.write_read_byte(0xFF).await
    }
}

/// DS18B20 temperature sensor driver
pub struct Ds18b20<TX, RX>
where
    TX: embedded_io_async::Write + SetBaudrate,
    RX: embedded_io_async::Read + SetBaudrate,
{
    bus: OneWire<TX, RX>,
}

impl<TX, RX> Ds18b20<TX, RX>
where
    TX: embedded_io_async::Write + SetBaudrate,
    RX: embedded_io_async::Read + SetBaudrate,
{
    /// Start a temperature conversion.
    const FN_CONVERT_T: u8 = 0x44;
    /// Read contents of the scratchpad containing the temperature.
    const FN_READ_SCRATCHPAD: u8 = 0xBE;

    pub fn new(bus: OneWire<TX, RX>) -> Self {
        Self { bus }
    }

    /// Start a new measurement. Allow at least 1000ms before getting `temperature`.
    pub async fn start(&mut self) {
        self.bus.reset().await;
        self.bus.write_read_byte(OneWire::<TX, RX>::COMMAND_SKIP_ROM).await;
        self.bus.write_read_byte(Self::FN_CONVERT_T).await;
    }

    /// Calculate CRC8 of the data
    fn crc8(data: &[u8]) -> u8 {
        let mut temp;
        let mut data_byte;
        let mut crc = 0;
        for b in data {
            data_byte = *b;
            for _ in 0..8 {
                temp = (crc ^ data_byte) & 0x01;
                crc >>= 1;
                if temp != 0 {
                    crc ^= 0x8C;
                }
                data_byte >>= 1;
            }
        }
        crc
    }

    /// Read the temperature. Ensure >1000ms has passed since `start` before calling this.
    pub async fn temperature(&mut self) -> Result<f32, ()> {
        self.bus.reset().await;
        self.bus.write_read_byte(OneWire::<TX, RX>::COMMAND_SKIP_ROM).await;
        self.bus.write_read_byte(Self::FN_READ_SCRATCHPAD).await;

        let mut data = [0; 9];
        for byte in data.iter_mut() {
            *byte = self.bus.read_byte().await;
        }

        match Self::crc8(&data) == 0 {
            true => Ok(((data[1] as i16) << 8 | data[0] as i16) as f32 / 16.),
            false => Err(()),
        }
    }
}