aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDario Nieuwenhuis <[email protected]>2024-12-31 11:33:54 +0100
committerGitHub <[email protected]>2024-12-31 11:33:54 +0100
commit29dce03adc40099105f0c4b430be5c1f413b1a52 (patch)
tree9848ed7f979513c895a916572f6672bbd2f03264
parent18773c377a1eca726f928426249c899afddb1877 (diff)
parent7070f5364eb903c6c6b0b28348a582f5a9e1f89e (diff)
Merge pull request #3679 from trnila/1wire
stm32/usart: configurable readback for half-duplex to support 1-wire + ds18b20 example
-rw-r--r--embassy-stm32/src/usart/buffered.rs100
-rw-r--r--embassy-stm32/src/usart/mod.rs77
-rw-r--r--examples/stm32g0/src/bin/onewire_ds18b20.rs271
3 files changed, 424 insertions, 24 deletions
diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs
index 814be2858..7bbe01a00 100644
--- a/embassy-stm32/src/usart/buffered.rs
+++ b/embassy-stm32/src/usart/buffered.rs
@@ -12,8 +12,9 @@ use embassy_sync::waitqueue::AtomicWaker;
12#[cfg(not(any(usart_v1, usart_v2)))] 12#[cfg(not(any(usart_v1, usart_v2)))]
13use super::DePin; 13use super::DePin;
14use super::{ 14use super::{
15 clear_interrupt_flags, configure, rdr, reconfigure, send_break, set_baudrate, sr, tdr, Config, ConfigError, CtsPin, 15 clear_interrupt_flags, configure, half_duplex_set_rx_tx_before_write, rdr, reconfigure, send_break, set_baudrate,
16 Error, Info, Instance, Regs, RtsPin, RxPin, TxPin, 16 sr, tdr, Config, ConfigError, CtsPin, Duplex, Error, HalfDuplexConfig, HalfDuplexReadback, Info, Instance, Regs,
17 RtsPin, RxPin, TxPin,
17}; 18};
18use crate::gpio::{AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed}; 19use crate::gpio::{AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed};
19use crate::interrupt::{self, InterruptExt}; 20use crate::interrupt::{self, InterruptExt};
@@ -108,6 +109,8 @@ unsafe fn on_interrupt(r: Regs, state: &'static State) {
108 }); 109 });
109 } 110 }
110 111
112 half_duplex_set_rx_tx_before_write(&r, state.half_duplex_readback.load(Ordering::Relaxed));
113
111 tdr(r).write_volatile(buf[0].into()); 114 tdr(r).write_volatile(buf[0].into());
112 tx_reader.pop_done(1); 115 tx_reader.pop_done(1);
113 } else { 116 } else {
@@ -126,6 +129,7 @@ pub(super) struct State {
126 tx_buf: RingBuffer, 129 tx_buf: RingBuffer,
127 tx_done: AtomicBool, 130 tx_done: AtomicBool,
128 tx_rx_refcount: AtomicU8, 131 tx_rx_refcount: AtomicU8,
132 half_duplex_readback: AtomicBool,
129} 133}
130 134
131impl State { 135impl State {
@@ -137,6 +141,7 @@ impl State {
137 tx_waker: AtomicWaker::new(), 141 tx_waker: AtomicWaker::new(),
138 tx_done: AtomicBool::new(true), 142 tx_done: AtomicBool::new(true),
139 tx_rx_refcount: AtomicU8::new(0), 143 tx_rx_refcount: AtomicU8::new(0),
144 half_duplex_readback: AtomicBool::new(false),
140 } 145 }
141 } 146 }
142} 147}
@@ -321,6 +326,84 @@ impl<'d> BufferedUart<'d> {
321 ) 326 )
322 } 327 }
323 328
329 /// Create a single-wire half-duplex Uart transceiver on a single Tx pin.
330 ///
331 /// See [`new_half_duplex_on_rx`][`Self::new_half_duplex_on_rx`] if you would prefer to use an Rx pin
332 /// (when it is available for your chip). There is no functional difference between these methods, as both
333 /// allow bidirectional communication.
334 ///
335 /// The TX pin is always released when no data is transmitted. Thus, it acts as a standard
336 /// I/O in idle or in reception. It means that the I/O must be configured so that TX is
337 /// configured as alternate function open-drain with an external pull-up
338 /// Apart from this, the communication protocol is similar to normal USART mode. Any conflict
339 /// on the line must be managed by software (for instance by using a centralized arbiter).
340 #[doc(alias("HDSEL"))]
341 pub fn new_half_duplex<T: Instance>(
342 peri: impl Peripheral<P = T> + 'd,
343 tx: impl Peripheral<P = impl TxPin<T>> + 'd,
344 _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
345 tx_buffer: &'d mut [u8],
346 rx_buffer: &'d mut [u8],
347 mut config: Config,
348 readback: HalfDuplexReadback,
349 half_duplex: HalfDuplexConfig,
350 ) -> Result<Self, ConfigError> {
351 #[cfg(not(any(usart_v1, usart_v2)))]
352 {
353 config.swap_rx_tx = false;
354 }
355 config.duplex = Duplex::Half(readback);
356
357 Self::new_inner(
358 peri,
359 None,
360 new_pin!(tx, half_duplex.af_type()),
361 None,
362 None,
363 None,
364 tx_buffer,
365 rx_buffer,
366 config,
367 )
368 }
369
370 /// Create a single-wire half-duplex Uart transceiver on a single Rx pin.
371 ///
372 /// See [`new_half_duplex`][`Self::new_half_duplex`] if you would prefer to use an Tx pin.
373 /// There is no functional difference between these methods, as both allow bidirectional communication.
374 ///
375 /// The pin is always released when no data is transmitted. Thus, it acts as a standard
376 /// I/O in idle or in reception.
377 /// Apart from this, the communication protocol is similar to normal USART mode. Any conflict
378 /// on the line must be managed by software (for instance by using a centralized arbiter).
379 #[cfg(not(any(usart_v1, usart_v2)))]
380 #[doc(alias("HDSEL"))]
381 pub fn new_half_duplex_on_rx<T: Instance>(
382 peri: impl Peripheral<P = T> + 'd,
383 rx: impl Peripheral<P = impl RxPin<T>> + 'd,
384 _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
385 tx_buffer: &'d mut [u8],
386 rx_buffer: &'d mut [u8],
387 mut config: Config,
388 readback: HalfDuplexReadback,
389 half_duplex: HalfDuplexConfig,
390 ) -> Result<Self, ConfigError> {
391 config.swap_rx_tx = true;
392 config.duplex = Duplex::Half(readback);
393
394 Self::new_inner(
395 peri,
396 new_pin!(rx, half_duplex.af_type()),
397 None,
398 None,
399 None,
400 None,
401 tx_buffer,
402 rx_buffer,
403 config,
404 )
405 }
406
324 fn new_inner<T: Instance>( 407 fn new_inner<T: Instance>(
325 _peri: impl Peripheral<P = T> + 'd, 408 _peri: impl Peripheral<P = T> + 'd,
326 rx: Option<PeripheralRef<'d, AnyPin>>, 409 rx: Option<PeripheralRef<'d, AnyPin>>,
@@ -336,6 +419,11 @@ impl<'d> BufferedUart<'d> {
336 let state = T::buffered_state(); 419 let state = T::buffered_state();
337 let kernel_clock = T::frequency(); 420 let kernel_clock = T::frequency();
338 421
422 state.half_duplex_readback.store(
423 config.duplex == Duplex::Half(HalfDuplexReadback::Readback),
424 Ordering::Relaxed,
425 );
426
339 let mut this = Self { 427 let mut this = Self {
340 rx: BufferedUartRx { 428 rx: BufferedUartRx {
341 info, 429 info,
@@ -381,12 +469,20 @@ impl<'d> BufferedUart<'d> {
381 w.set_ctse(self.tx.cts.is_some()); 469 w.set_ctse(self.tx.cts.is_some());
382 #[cfg(not(any(usart_v1, usart_v2)))] 470 #[cfg(not(any(usart_v1, usart_v2)))]
383 w.set_dem(self.tx.de.is_some()); 471 w.set_dem(self.tx.de.is_some());
472 w.set_hdsel(config.duplex.is_half());
384 }); 473 });
385 configure(info, self.rx.kernel_clock, &config, true, true)?; 474 configure(info, self.rx.kernel_clock, &config, true, true)?;
386 475
387 info.regs.cr1().modify(|w| { 476 info.regs.cr1().modify(|w| {
388 w.set_rxneie(true); 477 w.set_rxneie(true);
389 w.set_idleie(true); 478 w.set_idleie(true);
479
480 if config.duplex.is_half() {
481 // The te and re bits will be set by write, read and flush methods.
482 // Receiver should be enabled by default for Half-Duplex.
483 w.set_te(false);
484 w.set_re(true);
485 }
390 }); 486 });
391 487
392 info.interrupt.unpend(); 488 info.interrupt.unpend();
diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs
index 2d801e6bf..48cc4f6d6 100644
--- a/embassy-stm32/src/usart/mod.rs
+++ b/embassy-stm32/src/usart/mod.rs
@@ -125,6 +125,33 @@ pub enum StopBits {
125 STOP1P5, 125 STOP1P5,
126} 126}
127 127
128#[derive(Clone, Copy, PartialEq, Eq, Debug)]
129#[cfg_attr(feature = "defmt", derive(defmt::Format))]
130/// Enables or disables receiver so written data are read back in half-duplex mode
131pub enum HalfDuplexReadback {
132 /// Disables receiver so written data are not read back
133 NoReadback,
134 /// Enables receiver so written data are read back
135 Readback,
136}
137
138#[derive(Clone, Copy, PartialEq, Eq, Debug)]
139#[cfg_attr(feature = "defmt", derive(defmt::Format))]
140/// Duplex mode
141pub enum Duplex {
142 /// Full duplex
143 Full,
144 /// Half duplex with possibility to read back written data
145 Half(HalfDuplexReadback),
146}
147
148impl Duplex {
149 /// Returns true if half-duplex
150 fn is_half(&self) -> bool {
151 matches!(self, Duplex::Half(_))
152 }
153}
154
128#[non_exhaustive] 155#[non_exhaustive]
129#[derive(Clone, Copy, PartialEq, Eq, Debug)] 156#[derive(Clone, Copy, PartialEq, Eq, Debug)]
130#[cfg_attr(feature = "defmt", derive(defmt::Format))] 157#[cfg_attr(feature = "defmt", derive(defmt::Format))]
@@ -181,7 +208,7 @@ pub struct Config {
181 pub rx_pull: Pull, 208 pub rx_pull: Pull,
182 209
183 // private: set by new_half_duplex, not by the user. 210 // private: set by new_half_duplex, not by the user.
184 half_duplex: bool, 211 duplex: Duplex,
185} 212}
186 213
187impl Config { 214impl Config {
@@ -220,7 +247,7 @@ impl Default for Config {
220 #[cfg(any(usart_v3, usart_v4))] 247 #[cfg(any(usart_v3, usart_v4))]
221 invert_rx: false, 248 invert_rx: false,
222 rx_pull: Pull::None, 249 rx_pull: Pull::None,
223 half_duplex: false, 250 duplex: Duplex::Full,
224 } 251 }
225 } 252 }
226} 253}
@@ -308,6 +335,7 @@ pub struct UartTx<'d, M: Mode> {
308 cts: Option<PeripheralRef<'d, AnyPin>>, 335 cts: Option<PeripheralRef<'d, AnyPin>>,
309 de: Option<PeripheralRef<'d, AnyPin>>, 336 de: Option<PeripheralRef<'d, AnyPin>>,
310 tx_dma: Option<ChannelAndRequest<'d>>, 337 tx_dma: Option<ChannelAndRequest<'d>>,
338 duplex: Duplex,
311 _phantom: PhantomData<M>, 339 _phantom: PhantomData<M>,
312} 340}
313 341
@@ -409,13 +437,7 @@ impl<'d> UartTx<'d, Async> {
409 pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> { 437 pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> {
410 let r = self.info.regs; 438 let r = self.info.regs;
411 439
412 // Enable Transmitter and disable Receiver for Half-Duplex mode 440 half_duplex_set_rx_tx_before_write(&r, self.duplex == Duplex::Half(HalfDuplexReadback::Readback));
413 let mut cr1 = r.cr1().read();
414 if r.cr3().read().hdsel() && !cr1.te() {
415 cr1.set_te(true);
416 cr1.set_re(false);
417 r.cr1().write_value(cr1);
418 }
419 441
420 let ch = self.tx_dma.as_mut().unwrap(); 442 let ch = self.tx_dma.as_mut().unwrap();
421 r.cr3().modify(|reg| { 443 r.cr3().modify(|reg| {
@@ -485,6 +507,7 @@ impl<'d, M: Mode> UartTx<'d, M> {
485 cts, 507 cts,
486 de: None, 508 de: None,
487 tx_dma, 509 tx_dma,
510 duplex: config.duplex,
488 _phantom: PhantomData, 511 _phantom: PhantomData,
489 }; 512 };
490 this.enable_and_configure(&config)?; 513 this.enable_and_configure(&config)?;
@@ -515,13 +538,7 @@ impl<'d, M: Mode> UartTx<'d, M> {
515 pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> { 538 pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> {
516 let r = self.info.regs; 539 let r = self.info.regs;
517 540
518 // Enable Transmitter and disable Receiver for Half-Duplex mode 541 half_duplex_set_rx_tx_before_write(&r, self.duplex == Duplex::Half(HalfDuplexReadback::Readback));
519 let mut cr1 = r.cr1().read();
520 if r.cr3().read().hdsel() && !cr1.te() {
521 cr1.set_te(true);
522 cr1.set_re(false);
523 r.cr1().write_value(cr1);
524 }
525 542
526 for &b in buffer { 543 for &b in buffer {
527 while !sr(r).read().txe() {} 544 while !sr(r).read().txe() {}
@@ -600,6 +617,17 @@ pub fn send_break(regs: &Regs) {
600 regs.rqr().write(|w| w.set_sbkrq(true)); 617 regs.rqr().write(|w| w.set_sbkrq(true));
601} 618}
602 619
620/// Enable Transmitter and disable Receiver for Half-Duplex mode
621/// In case of readback, keep Receiver enabled
622fn half_duplex_set_rx_tx_before_write(r: &Regs, enable_readback: bool) {
623 let mut cr1 = r.cr1().read();
624 if r.cr3().read().hdsel() && !cr1.te() {
625 cr1.set_te(true);
626 cr1.set_re(enable_readback);
627 r.cr1().write_value(cr1);
628 }
629}
630
603impl<'d> UartRx<'d, Async> { 631impl<'d> UartRx<'d, Async> {
604 /// Create a new rx-only UART with no hardware flow control. 632 /// Create a new rx-only UART with no hardware flow control.
605 /// 633 ///
@@ -1149,13 +1177,14 @@ impl<'d> Uart<'d, Async> {
1149 tx_dma: impl Peripheral<P = impl TxDma<T>> + 'd, 1177 tx_dma: impl Peripheral<P = impl TxDma<T>> + 'd,
1150 rx_dma: impl Peripheral<P = impl RxDma<T>> + 'd, 1178 rx_dma: impl Peripheral<P = impl RxDma<T>> + 'd,
1151 mut config: Config, 1179 mut config: Config,
1180 readback: HalfDuplexReadback,
1152 half_duplex: HalfDuplexConfig, 1181 half_duplex: HalfDuplexConfig,
1153 ) -> Result<Self, ConfigError> { 1182 ) -> Result<Self, ConfigError> {
1154 #[cfg(not(any(usart_v1, usart_v2)))] 1183 #[cfg(not(any(usart_v1, usart_v2)))]
1155 { 1184 {
1156 config.swap_rx_tx = false; 1185 config.swap_rx_tx = false;
1157 } 1186 }
1158 config.half_duplex = true; 1187 config.duplex = Duplex::Half(readback);
1159 1188
1160 Self::new_inner( 1189 Self::new_inner(
1161 peri, 1190 peri,
@@ -1188,10 +1217,11 @@ impl<'d> Uart<'d, Async> {
1188 tx_dma: impl Peripheral<P = impl TxDma<T>> + 'd, 1217 tx_dma: impl Peripheral<P = impl TxDma<T>> + 'd,
1189 rx_dma: impl Peripheral<P = impl RxDma<T>> + 'd, 1218 rx_dma: impl Peripheral<P = impl RxDma<T>> + 'd,
1190 mut config: Config, 1219 mut config: Config,
1220 readback: HalfDuplexReadback,
1191 half_duplex: HalfDuplexConfig, 1221 half_duplex: HalfDuplexConfig,
1192 ) -> Result<Self, ConfigError> { 1222 ) -> Result<Self, ConfigError> {
1193 config.swap_rx_tx = true; 1223 config.swap_rx_tx = true;
1194 config.half_duplex = true; 1224 config.duplex = Duplex::Half(readback);
1195 1225
1196 Self::new_inner( 1226 Self::new_inner(
1197 peri, 1227 peri,
@@ -1307,13 +1337,14 @@ impl<'d> Uart<'d, Blocking> {
1307 peri: impl Peripheral<P = T> + 'd, 1337 peri: impl Peripheral<P = T> + 'd,
1308 tx: impl Peripheral<P = impl TxPin<T>> + 'd, 1338 tx: impl Peripheral<P = impl TxPin<T>> + 'd,
1309 mut config: Config, 1339 mut config: Config,
1340 readback: HalfDuplexReadback,
1310 half_duplex: HalfDuplexConfig, 1341 half_duplex: HalfDuplexConfig,
1311 ) -> Result<Self, ConfigError> { 1342 ) -> Result<Self, ConfigError> {
1312 #[cfg(not(any(usart_v1, usart_v2)))] 1343 #[cfg(not(any(usart_v1, usart_v2)))]
1313 { 1344 {
1314 config.swap_rx_tx = false; 1345 config.swap_rx_tx = false;
1315 } 1346 }
1316 config.half_duplex = true; 1347 config.duplex = Duplex::Half(readback);
1317 1348
1318 Self::new_inner( 1349 Self::new_inner(
1319 peri, 1350 peri,
@@ -1343,10 +1374,11 @@ impl<'d> Uart<'d, Blocking> {
1343 peri: impl Peripheral<P = T> + 'd, 1374 peri: impl Peripheral<P = T> + 'd,
1344 rx: impl Peripheral<P = impl RxPin<T>> + 'd, 1375 rx: impl Peripheral<P = impl RxPin<T>> + 'd,
1345 mut config: Config, 1376 mut config: Config,
1377 readback: HalfDuplexReadback,
1346 half_duplex: HalfDuplexConfig, 1378 half_duplex: HalfDuplexConfig,
1347 ) -> Result<Self, ConfigError> { 1379 ) -> Result<Self, ConfigError> {
1348 config.swap_rx_tx = true; 1380 config.swap_rx_tx = true;
1349 config.half_duplex = true; 1381 config.duplex = Duplex::Half(readback);
1350 1382
1351 Self::new_inner( 1383 Self::new_inner(
1352 peri, 1384 peri,
@@ -1388,6 +1420,7 @@ impl<'d, M: Mode> Uart<'d, M> {
1388 cts, 1420 cts,
1389 de, 1421 de,
1390 tx_dma, 1422 tx_dma,
1423 duplex: config.duplex,
1391 }, 1424 },
1392 rx: UartRx { 1425 rx: UartRx {
1393 _phantom: PhantomData, 1426 _phantom: PhantomData,
@@ -1667,14 +1700,14 @@ fn configure(
1667 r.cr3().modify(|w| { 1700 r.cr3().modify(|w| {
1668 #[cfg(not(usart_v1))] 1701 #[cfg(not(usart_v1))]
1669 w.set_onebit(config.assume_noise_free); 1702 w.set_onebit(config.assume_noise_free);
1670 w.set_hdsel(config.half_duplex); 1703 w.set_hdsel(config.duplex.is_half());
1671 }); 1704 });
1672 1705
1673 r.cr1().write(|w| { 1706 r.cr1().write(|w| {
1674 // enable uart 1707 // enable uart
1675 w.set_ue(true); 1708 w.set_ue(true);
1676 1709
1677 if config.half_duplex { 1710 if config.duplex.is_half() {
1678 // The te and re bits will be set by write, read and flush methods. 1711 // The te and re bits will be set by write, read and flush methods.
1679 // Receiver should be enabled by default for Half-Duplex. 1712 // Receiver should be enabled by default for Half-Duplex.
1680 w.set_te(false); 1713 w.set_te(false);
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
6use cortex_m::singleton;
7use defmt::*;
8use embassy_executor::Spawner;
9use embassy_stm32::mode::Async;
10use embassy_stm32::usart::{
11 BufferedUartRx, BufferedUartTx, Config, ConfigError, HalfDuplexConfig, RingBufferedUartRx, UartTx,
12};
13use embassy_stm32::{bind_interrupts, peripherals, usart};
14use embassy_time::{Duration, Timer};
15use {defmt_rtt as _, panic_probe as _};
16
17/// Create onewire bus using DMA USART
18fn 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
46fn 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]
73async 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
92pub trait SetBaudrate {
93 fn set_baudrate(&mut self, baudrate: u32) -> Result<(), ConfigError>;
94}
95impl SetBaudrate for BufferedUartTx<'_> {
96 fn set_baudrate(&mut self, baudrate: u32) -> Result<(), ConfigError> {
97 BufferedUartTx::set_baudrate(self, baudrate)
98 }
99}
100impl SetBaudrate for BufferedUartRx<'_> {
101 fn set_baudrate(&mut self, baudrate: u32) -> Result<(), ConfigError> {
102 BufferedUartRx::set_baudrate(self, baudrate)
103 }
104}
105impl SetBaudrate for RingBufferedUartRx<'_> {
106 fn set_baudrate(&mut self, baudrate: u32) -> Result<(), ConfigError> {
107 RingBufferedUartRx::set_baudrate(self, baudrate)
108 }
109}
110impl 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
117pub struct OneWire<TX, RX>
118where
119 TX: embedded_io_async::Write + SetBaudrate,
120 RX: embedded_io_async::Read + SetBaudrate,
121{
122 tx: TX,
123 rx: RX,
124}
125
126impl<TX, RX> OneWire<TX, RX>
127where
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
207pub struct Ds18b20<TX, RX>
208where
209 TX: embedded_io_async::Write + SetBaudrate,
210 RX: embedded_io_async::Read + SetBaudrate,
211{
212 bus: OneWire<TX, RX>,
213}
214
215impl<TX, RX> Ds18b20<TX, RX>
216where
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}