diff options
| author | Daniel Trnka <[email protected]> | 2024-12-22 17:37:20 +0100 |
|---|---|---|
| committer | Daniel Trnka <[email protected]> | 2024-12-22 19:55:21 +0100 |
| commit | 7070f5364eb903c6c6b0b28348a582f5a9e1f89e (patch) | |
| tree | 518fea56cfe7114db024bc7891ae8010dec2d358 /examples/stm32g0/src | |
| parent | a7983668da2947f7846f0abc4736708539aedc42 (diff) | |
examples/stm32g0: added ds18b20 temperature sensor on 1-wire bus
Diffstat (limited to 'examples/stm32g0/src')
| -rw-r--r-- | examples/stm32g0/src/bin/onewire_ds18b20.rs | 271 |
1 files changed, 271 insertions, 0 deletions
diff --git a/examples/stm32g0/src/bin/onewire_ds18b20.rs b/examples/stm32g0/src/bin/onewire_ds18b20.rs new file mode 100644 index 000000000..f85cc4ff8 --- /dev/null +++ b/examples/stm32g0/src/bin/onewire_ds18b20.rs | |||
| @@ -0,0 +1,271 @@ | |||
| 1 | //! This examples shows how you can use buffered or DMA UART to read a DS18B20 temperature sensor on 1-Wire bus. | ||
| 2 | //! Connect 5k pull-up resistor between PA9 and 3.3V. | ||
| 3 | #![no_std] | ||
| 4 | #![no_main] | ||
| 5 | |||
| 6 | use cortex_m::singleton; | ||
| 7 | use defmt::*; | ||
| 8 | use embassy_executor::Spawner; | ||
| 9 | use embassy_stm32::mode::Async; | ||
| 10 | use embassy_stm32::usart::{ | ||
| 11 | BufferedUartRx, BufferedUartTx, Config, ConfigError, HalfDuplexConfig, RingBufferedUartRx, UartTx, | ||
| 12 | }; | ||
| 13 | use embassy_stm32::{bind_interrupts, peripherals, usart}; | ||
| 14 | use embassy_time::{Duration, Timer}; | ||
| 15 | use {defmt_rtt as _, panic_probe as _}; | ||
| 16 | |||
| 17 | /// Create onewire bus using DMA USART | ||
| 18 | fn create_onewire(p: embassy_stm32::Peripherals) -> OneWire<UartTx<'static, Async>, RingBufferedUartRx<'static>> { | ||
| 19 | use embassy_stm32::usart::Uart; | ||
| 20 | bind_interrupts!(struct Irqs { | ||
| 21 | USART1 => usart::InterruptHandler<peripherals::USART1>; | ||
| 22 | }); | ||
| 23 | |||
| 24 | let usart = Uart::new_half_duplex( | ||
| 25 | p.USART1, | ||
| 26 | p.PA9, | ||
| 27 | Irqs, | ||
| 28 | p.DMA1_CH1, | ||
| 29 | p.DMA1_CH2, | ||
| 30 | Config::default(), | ||
| 31 | // Enable readback so we can read sensor pulling data low while transmission is in progress | ||
| 32 | usart::HalfDuplexReadback::Readback, | ||
| 33 | HalfDuplexConfig::OpenDrainExternal, | ||
| 34 | ) | ||
| 35 | .unwrap(); | ||
| 36 | |||
| 37 | const BUFFER_SIZE: usize = 16; | ||
| 38 | let (tx, rx) = usart.split(); | ||
| 39 | let rx_buf: &mut [u8; BUFFER_SIZE] = singleton!(TX_BUF: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE]).unwrap(); | ||
| 40 | let rx = rx.into_ring_buffered(rx_buf); | ||
| 41 | OneWire::new(tx, rx) | ||
| 42 | } | ||
| 43 | |||
| 44 | /* | ||
| 45 | /// Create onewire bus using buffered USART | ||
| 46 | fn create_onewire(p: embassy_stm32::Peripherals) -> OneWire<BufferedUartTx<'static>, BufferedUartRx<'static>> { | ||
| 47 | use embassy_stm32::usart::BufferedUart; | ||
| 48 | bind_interrupts!(struct Irqs { | ||
| 49 | USART1 => usart::BufferedInterruptHandler<peripherals::USART1>; | ||
| 50 | }); | ||
| 51 | |||
| 52 | const BUFFER_SIZE: usize = 16; | ||
| 53 | let tx_buf: &mut [u8; BUFFER_SIZE] = singleton!(TX_BUF: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE]).unwrap(); | ||
| 54 | let rx_buf: &mut [u8; BUFFER_SIZE] = singleton!(RX_BUF: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE]).unwrap(); | ||
| 55 | let usart = BufferedUart::new_half_duplex( | ||
| 56 | p.USART1, | ||
| 57 | p.PA9, | ||
| 58 | Irqs, | ||
| 59 | tx_buf, | ||
| 60 | rx_buf, | ||
| 61 | Config::default(), | ||
| 62 | // Enable readback so we can read sensor pulling data low while transmission is in progress | ||
| 63 | usart::HalfDuplexReadback::Readback, | ||
| 64 | HalfDuplexConfig::OpenDrainExternal, | ||
| 65 | ) | ||
| 66 | .unwrap(); | ||
| 67 | let (tx, rx) = usart.split(); | ||
| 68 | OneWire::new(tx, rx) | ||
| 69 | } | ||
| 70 | */ | ||
| 71 | |||
| 72 | #[embassy_executor::main] | ||
| 73 | async fn main(_spawner: Spawner) { | ||
| 74 | let p = embassy_stm32::init(Default::default()); | ||
| 75 | |||
| 76 | let onewire = create_onewire(p); | ||
| 77 | let mut sensor = Ds18b20::new(onewire); | ||
| 78 | |||
| 79 | loop { | ||
| 80 | // Start a new temperature measurement | ||
| 81 | sensor.start().await; | ||
| 82 | // Wait for the measurement to finish | ||
| 83 | Timer::after(Duration::from_secs(1)).await; | ||
| 84 | match sensor.temperature().await { | ||
| 85 | Ok(temp) => info!("temp = {:?} deg C", temp), | ||
| 86 | _ => error!("sensor error"), | ||
| 87 | } | ||
| 88 | Timer::after(Duration::from_secs(1)).await; | ||
| 89 | } | ||
| 90 | } | ||
| 91 | |||
| 92 | pub trait SetBaudrate { | ||
| 93 | fn set_baudrate(&mut self, baudrate: u32) -> Result<(), ConfigError>; | ||
| 94 | } | ||
| 95 | impl SetBaudrate for BufferedUartTx<'_> { | ||
| 96 | fn set_baudrate(&mut self, baudrate: u32) -> Result<(), ConfigError> { | ||
| 97 | BufferedUartTx::set_baudrate(self, baudrate) | ||
| 98 | } | ||
| 99 | } | ||
| 100 | impl SetBaudrate for BufferedUartRx<'_> { | ||
| 101 | fn set_baudrate(&mut self, baudrate: u32) -> Result<(), ConfigError> { | ||
| 102 | BufferedUartRx::set_baudrate(self, baudrate) | ||
| 103 | } | ||
| 104 | } | ||
| 105 | impl SetBaudrate for RingBufferedUartRx<'_> { | ||
| 106 | fn set_baudrate(&mut self, baudrate: u32) -> Result<(), ConfigError> { | ||
| 107 | RingBufferedUartRx::set_baudrate(self, baudrate) | ||
| 108 | } | ||
| 109 | } | ||
| 110 | impl SetBaudrate for UartTx<'_, Async> { | ||
| 111 | fn set_baudrate(&mut self, baudrate: u32) -> Result<(), ConfigError> { | ||
| 112 | UartTx::set_baudrate(self, baudrate) | ||
| 113 | } | ||
| 114 | } | ||
| 115 | |||
| 116 | /// Simplified OneWire bus driver | ||
| 117 | pub struct OneWire<TX, RX> | ||
| 118 | where | ||
| 119 | TX: embedded_io_async::Write + SetBaudrate, | ||
| 120 | RX: embedded_io_async::Read + SetBaudrate, | ||
| 121 | { | ||
| 122 | tx: TX, | ||
| 123 | rx: RX, | ||
| 124 | } | ||
| 125 | |||
| 126 | impl<TX, RX> OneWire<TX, RX> | ||
| 127 | where | ||
| 128 | TX: embedded_io_async::Write + SetBaudrate, | ||
| 129 | RX: embedded_io_async::Read + SetBaudrate, | ||
| 130 | { | ||
| 131 | // bitrate with one bit taking ~104 us | ||
| 132 | const RESET_BUADRATE: u32 = 9600; | ||
| 133 | // bitrate with one bit taking ~8.7 us | ||
| 134 | const BAUDRATE: u32 = 115200; | ||
| 135 | |||
| 136 | // startbit + 8 low bits = 9 * 1/115200 = 78 us low pulse | ||
| 137 | const LOGIC_1_CHAR: u8 = 0xFF; | ||
| 138 | // startbit only = 1/115200 = 8.7 us low pulse | ||
| 139 | const LOGIC_0_CHAR: u8 = 0x00; | ||
| 140 | |||
| 141 | // Address all devices on the bus | ||
| 142 | const COMMAND_SKIP_ROM: u8 = 0xCC; | ||
| 143 | |||
| 144 | pub fn new(tx: TX, rx: RX) -> Self { | ||
| 145 | Self { tx, rx } | ||
| 146 | } | ||
| 147 | |||
| 148 | fn set_baudrate(&mut self, baudrate: u32) -> Result<(), ConfigError> { | ||
| 149 | self.tx.set_baudrate(baudrate)?; | ||
| 150 | self.rx.set_baudrate(baudrate) | ||
| 151 | } | ||
| 152 | |||
| 153 | /// Reset the bus by at least 480 us low pulse. | ||
| 154 | pub async fn reset(&mut self) { | ||
| 155 | // Switch to 9600 baudrate, so one bit takes ~104 us | ||
| 156 | self.set_baudrate(Self::RESET_BUADRATE).expect("set_baudrate failed"); | ||
| 157 | // Low USART start bit + 4x low bits = 5 * 104 us = 520 us low pulse | ||
| 158 | self.tx.write(&[0xF0]).await.expect("write failed"); | ||
| 159 | |||
| 160 | // Read the value on the bus | ||
| 161 | let mut buffer = [0; 1]; | ||
| 162 | self.rx.read_exact(&mut buffer).await.expect("read failed"); | ||
| 163 | |||
| 164 | // Switch back to 115200 baudrate, so one bit takes ~8.7 us | ||
| 165 | self.set_baudrate(Self::BAUDRATE).expect("set_baudrate failed"); | ||
| 166 | |||
| 167 | // read and expect sensor pulled some high bits to low (device present) | ||
| 168 | if buffer[0] & 0xF != 0 || buffer[0] & 0xF0 == 0xF0 { | ||
| 169 | warn!("No device present"); | ||
| 170 | } | ||
| 171 | } | ||
| 172 | |||
| 173 | /// Send byte and read response on the bus. | ||
| 174 | pub async fn write_read_byte(&mut self, byte: u8) -> u8 { | ||
| 175 | // One byte is sent as 8 UART characters | ||
| 176 | let mut tx = [0; 8]; | ||
| 177 | for (pos, char) in tx.iter_mut().enumerate() { | ||
| 178 | *char = if (byte >> pos) & 0x1 == 0x1 { | ||
| 179 | Self::LOGIC_1_CHAR | ||
| 180 | } else { | ||
| 181 | Self::LOGIC_0_CHAR | ||
| 182 | }; | ||
| 183 | } | ||
| 184 | self.tx.write_all(&tx).await.expect("write failed"); | ||
| 185 | |||
| 186 | // Readback the value on the bus, sensors can pull logic 1 to 0 | ||
| 187 | let mut rx = [0; 8]; | ||
| 188 | self.rx.read_exact(&mut rx).await.expect("read failed"); | ||
| 189 | let mut bus_byte = 0; | ||
| 190 | for (pos, char) in rx.iter().enumerate() { | ||
| 191 | // if its 0xFF, sensor didnt pull the bus to low level | ||
| 192 | if *char == 0xFF { | ||
| 193 | bus_byte |= 1 << pos; | ||
| 194 | } | ||
| 195 | } | ||
| 196 | |||
| 197 | bus_byte | ||
| 198 | } | ||
| 199 | |||
| 200 | /// Read a byte from the bus. | ||
| 201 | pub async fn read_byte(&mut self) -> u8 { | ||
| 202 | self.write_read_byte(0xFF).await | ||
| 203 | } | ||
| 204 | } | ||
| 205 | |||
| 206 | /// DS18B20 temperature sensor driver | ||
| 207 | pub struct Ds18b20<TX, RX> | ||
| 208 | where | ||
| 209 | TX: embedded_io_async::Write + SetBaudrate, | ||
| 210 | RX: embedded_io_async::Read + SetBaudrate, | ||
| 211 | { | ||
| 212 | bus: OneWire<TX, RX>, | ||
| 213 | } | ||
| 214 | |||
| 215 | impl<TX, RX> Ds18b20<TX, RX> | ||
| 216 | where | ||
| 217 | TX: embedded_io_async::Write + SetBaudrate, | ||
| 218 | RX: embedded_io_async::Read + SetBaudrate, | ||
| 219 | { | ||
| 220 | /// Start a temperature conversion. | ||
| 221 | const FN_CONVERT_T: u8 = 0x44; | ||
| 222 | /// Read contents of the scratchpad containing the temperature. | ||
| 223 | const FN_READ_SCRATCHPAD: u8 = 0xBE; | ||
| 224 | |||
| 225 | pub fn new(bus: OneWire<TX, RX>) -> Self { | ||
| 226 | Self { bus } | ||
| 227 | } | ||
| 228 | |||
| 229 | /// Start a new measurement. Allow at least 1000ms before getting `temperature`. | ||
| 230 | pub async fn start(&mut self) { | ||
| 231 | self.bus.reset().await; | ||
| 232 | self.bus.write_read_byte(OneWire::<TX, RX>::COMMAND_SKIP_ROM).await; | ||
| 233 | self.bus.write_read_byte(Self::FN_CONVERT_T).await; | ||
| 234 | } | ||
| 235 | |||
| 236 | /// Calculate CRC8 of the data | ||
| 237 | fn crc8(data: &[u8]) -> u8 { | ||
| 238 | let mut temp; | ||
| 239 | let mut data_byte; | ||
| 240 | let mut crc = 0; | ||
| 241 | for b in data { | ||
| 242 | data_byte = *b; | ||
| 243 | for _ in 0..8 { | ||
| 244 | temp = (crc ^ data_byte) & 0x01; | ||
| 245 | crc >>= 1; | ||
| 246 | if temp != 0 { | ||
| 247 | crc ^= 0x8C; | ||
| 248 | } | ||
| 249 | data_byte >>= 1; | ||
| 250 | } | ||
| 251 | } | ||
| 252 | crc | ||
| 253 | } | ||
| 254 | |||
| 255 | /// Read the temperature. Ensure >1000ms has passed since `start` before calling this. | ||
| 256 | pub async fn temperature(&mut self) -> Result<f32, ()> { | ||
| 257 | self.bus.reset().await; | ||
| 258 | self.bus.write_read_byte(OneWire::<TX, RX>::COMMAND_SKIP_ROM).await; | ||
| 259 | self.bus.write_read_byte(Self::FN_READ_SCRATCHPAD).await; | ||
| 260 | |||
| 261 | let mut data = [0; 9]; | ||
| 262 | for byte in data.iter_mut() { | ||
| 263 | *byte = self.bus.read_byte().await; | ||
| 264 | } | ||
| 265 | |||
| 266 | match Self::crc8(&data) == 0 { | ||
| 267 | true => Ok(((data[1] as u16) << 8 | data[0] as u16) as f32 / 16.), | ||
| 268 | false => Err(()), | ||
| 269 | } | ||
| 270 | } | ||
| 271 | } | ||
