diff options
| author | Dario Nieuwenhuis <[email protected]> | 2024-12-31 11:33:54 +0100 |
|---|---|---|
| committer | GitHub <[email protected]> | 2024-12-31 11:33:54 +0100 |
| commit | 29dce03adc40099105f0c4b430be5c1f413b1a52 (patch) | |
| tree | 9848ed7f979513c895a916572f6672bbd2f03264 | |
| parent | 18773c377a1eca726f928426249c899afddb1877 (diff) | |
| parent | 7070f5364eb903c6c6b0b28348a582f5a9e1f89e (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.rs | 100 | ||||
| -rw-r--r-- | embassy-stm32/src/usart/mod.rs | 77 | ||||
| -rw-r--r-- | examples/stm32g0/src/bin/onewire_ds18b20.rs | 271 |
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)))] |
| 13 | use super::DePin; | 13 | use super::DePin; |
| 14 | use super::{ | 14 | use 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 | }; |
| 18 | use crate::gpio::{AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed}; | 19 | use crate::gpio::{AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed}; |
| 19 | use crate::interrupt::{self, InterruptExt}; | 20 | use 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 | ||
| 131 | impl State { | 135 | impl 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 | ||
| 131 | pub 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 | ||
| 141 | pub enum Duplex { | ||
| 142 | /// Full duplex | ||
| 143 | Full, | ||
| 144 | /// Half duplex with possibility to read back written data | ||
| 145 | Half(HalfDuplexReadback), | ||
| 146 | } | ||
| 147 | |||
| 148 | impl 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 | ||
| 187 | impl Config { | 214 | impl 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 | ||
| 622 | fn 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 | |||
| 603 | impl<'d> UartRx<'d, Async> { | 631 | impl<'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 | |||
| 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 | } | ||
