diff --git a/esp-hal/Cargo.toml b/esp-hal/Cargo.toml index 1eed6879771..54399d3beaa 100644 --- a/esp-hal/Cargo.toml +++ b/esp-hal/Cargo.toml @@ -108,17 +108,17 @@ ufmt-write = { version = "0.1", optional = true } # IMPORTANT: # Each supported device MUST have its PAC included below along with a # corresponding feature. -esp32 = { version = "0.40", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "8d0a072" } -esp32c2 = { version = "0.29", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "8d0a072" } -esp32c3 = { version = "0.32", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "8d0a072" } -esp32c5 = { version = "0.2", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "8d0a072" } -esp32c6 = { version = "0.23", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "8d0a072" } -esp32c61 = { version = "0.3", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "8d0a072" } -esp32h2 = { version = "0.19", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "8d0a072" } -esp32s2 = { version = "0.31", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "8d0a072" } -esp32s3 = { version = "0.35", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "8d0a072" } -esp32s31 = { version = "0.1", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "8d0a072" } -esp32p4 = { version = "0.2", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "8d0a072" } +esp32 = { version = "0.40", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "aaa5c2ea8" } +esp32c2 = { version = "0.29", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "aaa5c2ea8" } +esp32c3 = { version = "0.32", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "aaa5c2ea8" } +esp32c5 = { version = "0.2", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "aaa5c2ea8" } +esp32c6 = { version = "0.23", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "aaa5c2ea8" } +esp32c61 = { version = "0.3", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "aaa5c2ea8" } +esp32h2 = { version = "0.19", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "aaa5c2ea8" } +esp32s2 = { version = "0.31", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "aaa5c2ea8" } +esp32s3 = { version = "0.35", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "aaa5c2ea8" } +esp32s31 = { version = "0.1", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "aaa5c2ea8" } +esp32p4 = { version = "0.2", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "aaa5c2ea8" } [target.'cfg(target_arch = "riscv32")'.dependencies] riscv = { version = "0.16.1" } diff --git a/esp-hal/src/twai/mod.rs b/esp-hal/src/twai/mod.rs index ab537e26dde..e875cc5763b 100644 --- a/esp-hal/src/twai/mod.rs +++ b/esp-hal/src/twai/mod.rs @@ -1219,6 +1219,24 @@ where self.regs().status().read().bus_off_st().bit_is_set() } + /// Initiates recovery from the bus-off state. + /// + /// The peripheral recovers once it has observed 128 occurrences of 11 + /// consecutive recessive (idle) bits on the bus. Use [`Self::is_bus_off`] + /// to poll whether recovery has completed. + /// + /// Does nothing if the peripheral is not in the bus-off state. + #[instability::unstable] + pub fn initiate_recovery(&mut self) { + if self.is_bus_off() { + // Entering the bus-off state parks the peripheral in reset mode; + // the 1-to-0 transition of the reset mode bit starts the recovery + // sequence. + self.regs().mode().modify(|_, w| w.reset_mode().set_bit()); + self.regs().mode().modify(|_, w| w.reset_mode().clear_bit()); + } + } + /// Get the number of messages that the peripheral has available in the /// receive FIFO. /// @@ -1289,6 +1307,14 @@ where /// NOTE: TODO: This may not work if using the self reception/self test /// functionality. See notes 1 and 2 in the "Frame Identifier" section /// of the reference manual. + /// + /// If a previous frame is still pending and the peripheral has entered the + /// error-passive state because of it (the bus cannot carry the frame, e.g. + /// no other node acknowledges it), the pending transmission is aborted and + /// [`EspTwaiError::TransmissionAborted`] is returned. An unacknowledged + /// transmitter's error counter does not increase past the error-passive + /// threshold, so without giving up the transmission would be retried + /// forever. pub fn transmit(&mut self, frame: &EspTwaiFrame) -> nb::Result<(), EspTwaiError> { let status = self.regs().status().read(); @@ -1298,6 +1324,12 @@ where } // Check that the peripheral is not already transmitting a packet. if status.tx_buf_st().bit_is_clear() { + if is_error_passive(self.regs()) { + // Give up on the pending frame: the bus is apparently unable to + // carry it. + self.regs().cmd().write(|w| w.abort_tx().set_bit()); + return nb::Result::Err(nb::Error::Other(EspTwaiError::TransmissionAborted)); + } return nb::Result::Err(nb::Error::WouldBlock); } @@ -1365,6 +1397,10 @@ pub enum TwaiInterrupt { ArbitrationLost, /// The controller has entered an error passive state. ErrorPassive, + /// The error or bus status has changed: an error counter crossed the + /// error warning limit in either direction, or the controller entered or + /// left the bus-off state. + ErrorWarning, } /// Represents errors that can occur in the TWAI driver. @@ -1375,6 +1411,10 @@ pub enum TwaiInterrupt { pub enum EspTwaiError { /// TWAI peripheral has entered a bus-off state. BusOff, + /// The transmission was aborted because the peripheral repeatedly failed + /// to transmit the frame and entered the error-passive state (e.g. no + /// other node on the bus acknowledged the frame). + TransmissionAborted, /// The received frame contains an invalid DLC. NonCompliantDlc(u8), /// Invalid data length. @@ -1483,6 +1523,7 @@ pub trait PrivateInstance: crate::private::Sealed { TwaiInterrupt::BusError => w.bus_err_int_ena().bit(enable), TwaiInterrupt::ArbitrationLost => w.arb_lost_int_ena().bit(enable), TwaiInterrupt::ErrorPassive => w.err_passive_int_ena().bit(enable), + TwaiInterrupt::ErrorWarning => w.err_warn_int_ena().bit(enable), }; } w @@ -1509,6 +1550,13 @@ fn release_receive_fifo(register_block: &RegisterBlock) { register_block.cmd().write(|w| w.release_buf().set_bit()); } +/// Check if the peripheral is in the error-passive state, i.e. one of the +/// error counters has reached 128. +fn is_error_passive(register_block: &RegisterBlock) -> bool { + register_block.tx_err_cnt().read().tx_err_cnt().bits() >= 128 + || register_block.rx_err_cnt().read().rx_err_cnt().bits() >= 128 +} + /// Write a frame to the peripheral. fn write_frame(register_block: &RegisterBlock, frame: &EspTwaiFrame) { // SAFETY: safe because there are 13 data registers and the slice is 13 bytes long max @@ -1685,6 +1733,12 @@ mod asynch { /// stops it, in case it is activly transmitting. Therefor it could be /// the case that even though the future is dropped, the frame was sent /// anyways. + /// + /// If the bus cannot carry the frame (e.g. no other node acknowledges + /// it), the transmission is aborted once the peripheral enters the + /// error-passive or bus-off state and the future resolves to + /// [`EspTwaiError::TransmissionAborted`] or [`EspTwaiError::BusOff`], + /// respectively. pub async fn transmit_async(&mut self, frame: &EspTwaiFrame) -> Result<(), EspTwaiError> { self.tx.transmit_async(frame).await } @@ -1732,7 +1786,21 @@ mod asynch { } // Check that the peripheral is not currently transmitting a packet. + // This must come before the error-passive check: a frame whose + // buffer has been released was transmitted successfully, no matter + // what state the error counters are in. if status.tx_buf_st().bit_is_clear() { + if is_error_passive(regs) { + // Give up on the pending frame: the bus is apparently + // unable to carry it (e.g. no other node acknowledges it), + // and an unacknowledged transmitter's error counter does + // not increase past the error-passive threshold, so the + // frame would otherwise be retried forever. The pending + // frame is not necessarily this future's own; a blocking + // `transmit` may have left it behind. + regs.cmd().write(|w| w.abort_tx().set_bit()); + return Poll::Ready(Err(EspTwaiError::TransmissionAborted)); + } return Poll::Pending; } @@ -1763,6 +1831,12 @@ mod asynch { /// stops it, in case it is actively transmitting. Therefor it could be /// the case that even though the future is dropped, the frame was sent /// anyways. + /// + /// If the bus cannot carry the frame (e.g. no other node acknowledges + /// it), the transmission is aborted once the peripheral enters the + /// error-passive or bus-off state and the future resolves to + /// [`EspTwaiError::TransmissionAborted`] or [`EspTwaiError::BusOff`], + /// respectively. pub async fn transmit_async(&mut self, frame: &EspTwaiFrame) -> Result<(), EspTwaiError> { TransmitFuture::new(self.twai.reborrow(), frame).await } @@ -1797,21 +1871,30 @@ mod asynch { let int_ena_reg = register_block.int_ena(); let int_ena = int_ena_reg.read(); + // The error warning interrupt fires on every change of the error or + // bus status. Entering bus-off sets both the bus-off and the error + // warning status; during bus-off recovery the transmit error counter + // counts down and the error warning status clears while the bus-off + // status is still set. Gating on both ensures the bus-off state is + // signalled only once per entry, instead of on every error warning + // interrupt while the state persists. + if int_raw.err_warn_int_st().bit_is_set() { + let status = register_block.status().read(); + + if status.bus_off_st().bit_is_set() && status.err_st().bit_is_set() { + // Any pending transmission is halted by entering the bus-off + // state; abort it to release the transmit buffer. + register_block.cmd().write(|w| w.abort_tx().set_bit()); + let _ = async_state.rx_queue.try_send(Err(EspTwaiError::BusOff)); + async_state.tx_waker.wake(); + async_state.err_waker.wake(); + } + } + if int_raw.rx_int_st().bit_is_set() { let status_reg = register_block.status(); - let status = status_reg.read(); - let rx_queue = &async_state.rx_queue; - if status.bus_off_st().bit_is_set() { - let _ = rx_queue.try_send(Err(EspTwaiError::BusOff)); - // Abort transmissions and wake senders if we are in bus-off state. - if status.tx_buf_st().bit_is_clear() { - register_block.cmd().write(|w| w.abort_tx().set_bit()); - async_state.tx_waker.wake(); - } - } - // Consumme all pending frames in the Rx FIFO while register_block .rx_message_cnt() @@ -1841,11 +1924,19 @@ mod asynch { async_state.tx_waker.wake(); } - if int_raw.bits() & 0b10110100 > 0 { + if int_raw.err_warn_int_st().bit_is_set() + || int_raw.err_passive_int_st().bit_is_set() + || int_raw.bus_err_int_st().bit_is_set() + { // We might want to use the error code to gather statistics in the // future. let _ = register_block.err_code_cap().read(); async_state.err_waker.wake(); + // A frame that cannot be transmitted never raises the transmit + // interrupt, so wake transmitters on errors to let them + // re-evaluate their pending frame and give up once the peripheral + // reaches the error-passive or bus-off state. + async_state.tx_waker.wake(); } // Clear interrupt request bits diff --git a/esp-phy/Cargo.toml b/esp-phy/Cargo.toml index 4b60c4354e2..adcbc672a7a 100644 --- a/esp-phy/Cargo.toml +++ b/esp-phy/Cargo.toml @@ -45,15 +45,15 @@ esp-wifi-sys-esp32h2 = { version = "0.2.0", optional = true, git = "https://gith esp-wifi-sys-esp32s2 = { version = "0.2.0", optional = true, git = "https://github.com/esp-rs/esp-wifi-sys.git", rev = "2ea8e3e" } esp-wifi-sys-esp32s3 = { version = "0.2.0", optional = true, git = "https://github.com/esp-rs/esp-wifi-sys.git", rev = "2ea8e3e" } -esp32 = { version = "0.40", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "8d0a072" } -esp32c2 = { version = "0.29", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "8d0a072" } -esp32c3 = { version = "0.32", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "8d0a072" } -esp32c5 = { version = "0.2", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "8d0a072" } -esp32c6 = { version = "0.23", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "8d0a072" } -esp32c61 = { version = "0.3", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "8d0a072" } -esp32h2 = { version = "0.19", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "8d0a072" } -esp32s2 = { version = "0.31", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "8d0a072" } -esp32s3 = { version = "0.35", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "8d0a072" } +esp32 = { version = "0.40", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "aaa5c2ea8" } +esp32c2 = { version = "0.29", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "aaa5c2ea8" } +esp32c3 = { version = "0.32", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "aaa5c2ea8" } +esp32c5 = { version = "0.2", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "aaa5c2ea8" } +esp32c6 = { version = "0.23", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "aaa5c2ea8" } +esp32c61 = { version = "0.3", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "aaa5c2ea8" } +esp32h2 = { version = "0.19", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "aaa5c2ea8" } +esp32s2 = { version = "0.31", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "aaa5c2ea8" } +esp32s3 = { version = "0.35", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "aaa5c2ea8" } embassy-sync = "0.8" diff --git a/esp-radio/Cargo.toml b/esp-radio/Cargo.toml index 455398bcf07..33ba374da17 100644 --- a/esp-radio/Cargo.toml +++ b/esp-radio/Cargo.toml @@ -90,15 +90,15 @@ esp-wifi-sys-esp32h2 = { version = "0.2.0", optional = true, git = "https://gith esp-wifi-sys-esp32s2 = { version = "0.2.0", optional = true, git = "https://github.com/esp-rs/esp-wifi-sys.git", rev = "2ea8e3e" } esp-wifi-sys-esp32s3 = { version = "0.2.0", optional = true, git = "https://github.com/esp-rs/esp-wifi-sys.git", rev = "2ea8e3e" } -esp32 = { version = "0.40", features = ["critical-section"], optional = true , git = "https://github.com/esp-rs/esp-pacs", rev = "8d0a072" } -esp32c2 = { version = "0.29", features = ["critical-section"], optional = true , git = "https://github.com/esp-rs/esp-pacs", rev = "8d0a072" } -esp32c3 = { version = "0.32", features = ["critical-section"], optional = true , git = "https://github.com/esp-rs/esp-pacs", rev = "8d0a072" } -esp32c5 = { version = "0.2", features = ["critical-section"], optional = true , git = "https://github.com/esp-rs/esp-pacs", rev = "8d0a072" } -esp32c6 = { version = "0.23", features = ["critical-section"], optional = true , git = "https://github.com/esp-rs/esp-pacs", rev = "8d0a072" } -esp32c61 = { version = "0.3", features = ["critical-section"], optional = true , git = "https://github.com/esp-rs/esp-pacs", rev = "8d0a072" } -esp32h2 = { version = "0.19", features = ["critical-section"], optional = true , git = "https://github.com/esp-rs/esp-pacs", rev = "8d0a072" } -esp32s2 = { version = "0.31", features = ["critical-section"], optional = true , git = "https://github.com/esp-rs/esp-pacs", rev = "8d0a072" } -esp32s3 = { version = "0.35", features = ["critical-section"], optional = true , git = "https://github.com/esp-rs/esp-pacs", rev = "8d0a072" } +esp32 = { version = "0.40", features = ["critical-section"], optional = true , git = "https://github.com/esp-rs/esp-pacs", rev = "aaa5c2ea8" } +esp32c2 = { version = "0.29", features = ["critical-section"], optional = true , git = "https://github.com/esp-rs/esp-pacs", rev = "aaa5c2ea8" } +esp32c3 = { version = "0.32", features = ["critical-section"], optional = true , git = "https://github.com/esp-rs/esp-pacs", rev = "aaa5c2ea8" } +esp32c5 = { version = "0.2", features = ["critical-section"], optional = true , git = "https://github.com/esp-rs/esp-pacs", rev = "aaa5c2ea8" } +esp32c6 = { version = "0.23", features = ["critical-section"], optional = true , git = "https://github.com/esp-rs/esp-pacs", rev = "aaa5c2ea8" } +esp32c61 = { version = "0.3", features = ["critical-section"], optional = true , git = "https://github.com/esp-rs/esp-pacs", rev = "aaa5c2ea8" } +esp32h2 = { version = "0.19", features = ["critical-section"], optional = true , git = "https://github.com/esp-rs/esp-pacs", rev = "aaa5c2ea8" } +esp32s2 = { version = "0.31", features = ["critical-section"], optional = true , git = "https://github.com/esp-rs/esp-pacs", rev = "aaa5c2ea8" } +esp32s3 = { version = "0.35", features = ["critical-section"], optional = true , git = "https://github.com/esp-rs/esp-pacs", rev = "aaa5c2ea8" } # Optional dependencies enabling ecosystem features embedded-io-06 = { package = "embedded-io", version = "0.6", default-features = false, optional = true } diff --git a/esp-rom-sys/Cargo.toml b/esp-rom-sys/Cargo.toml index 60517563812..4fae271caae 100644 --- a/esp-rom-sys/Cargo.toml +++ b/esp-rom-sys/Cargo.toml @@ -28,17 +28,17 @@ test = false [dependencies] document-features = "0.2" -esp32 = { version = "0.40", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "8d0a072" } -esp32c2 = { version = "0.29", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "8d0a072" } -esp32c3 = { version = "0.32", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "8d0a072" } -esp32c5 = { version = "0.2", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "8d0a072" } -esp32c6 = { version = "0.23", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "8d0a072" } -esp32c61 = { version = "0.3", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "8d0a072" } -esp32h2 = { version = "0.19", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "8d0a072" } -esp32s2 = { version = "0.31", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "8d0a072" } -esp32s3 = { version = "0.35", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "8d0a072" } -esp32s31 = { version = "0.1", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "8d0a072" } -esp32p4 = { version = "0.2", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "8d0a072" } +esp32 = { version = "0.40", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "aaa5c2ea8" } +esp32c2 = { version = "0.29", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "aaa5c2ea8" } +esp32c3 = { version = "0.32", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "aaa5c2ea8" } +esp32c5 = { version = "0.2", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "aaa5c2ea8" } +esp32c6 = { version = "0.23", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "aaa5c2ea8" } +esp32c61 = { version = "0.3", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "aaa5c2ea8" } +esp32h2 = { version = "0.19", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "aaa5c2ea8" } +esp32s2 = { version = "0.31", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "aaa5c2ea8" } +esp32s3 = { version = "0.35", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "aaa5c2ea8" } +esp32s31 = { version = "0.1", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "aaa5c2ea8" } +esp32p4 = { version = "0.2", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "aaa5c2ea8" } [build-dependencies] esp-metadata-generated = { version = "0.4.0", path = "../esp-metadata-generated", features = ["build-script"] } diff --git a/esp-storage/Cargo.toml b/esp-storage/Cargo.toml index 8add44a5a33..4f75667d0f0 100644 --- a/esp-storage/Cargo.toml +++ b/esp-storage/Cargo.toml @@ -39,17 +39,17 @@ esp-sync = { version = "0.2.1", path = "../esp-sync", optional = true } esp-rom-sys = { version = "~0.1", path = "../esp-rom-sys", optional = true } defmt = { version = "1.1", optional = true } -esp32 = { version = "0.40", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "8d0a072" } -esp32c2 = { version = "0.29", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "8d0a072" } -esp32c3 = { version = "0.32", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "8d0a072" } -esp32c5 = { version = "0.2", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "8d0a072" } -esp32c6 = { version = "0.23", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "8d0a072" } -esp32c61 = { version = "0.3", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "8d0a072" } -esp32h2 = { version = "0.19", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "8d0a072" } -esp32s2 = { version = "0.31", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "8d0a072" } -esp32s3 = { version = "0.35", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "8d0a072" } -esp32s31 = { version = "0.1", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "8d0a072" } -esp32p4 = { version = "0.2", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "8d0a072" } +esp32 = { version = "0.40", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "aaa5c2ea8" } +esp32c2 = { version = "0.29", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "aaa5c2ea8" } +esp32c3 = { version = "0.32", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "aaa5c2ea8" } +esp32c5 = { version = "0.2", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "aaa5c2ea8" } +esp32c6 = { version = "0.23", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "aaa5c2ea8" } +esp32c61 = { version = "0.3", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "aaa5c2ea8" } +esp32h2 = { version = "0.19", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "aaa5c2ea8" } +esp32s2 = { version = "0.31", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "aaa5c2ea8" } +esp32s3 = { version = "0.35", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "aaa5c2ea8" } +esp32s31 = { version = "0.1", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "aaa5c2ea8" } +esp32p4 = { version = "0.2", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "aaa5c2ea8" } # Unstable dependencies that are not (strictly) part of the public API document-features = "0.2" diff --git a/examples/async/embassy_twai/.cargo/config.toml b/examples/async/embassy_twai/.cargo/config.toml new file mode 100644 index 00000000000..44dacc4278b --- /dev/null +++ b/examples/async/embassy_twai/.cargo/config.toml @@ -0,0 +1,24 @@ +[target.'cfg(target_arch = "riscv32")'] +runner = "espflash flash --monitor" +rustflags = [ + "-C", "link-arg=-Tlinkall.x", + "-C", "force-frame-pointers", +] + +[target.'cfg(target_arch = "xtensa")'] +runner = "espflash flash --monitor" +rustflags = [ + # GNU LD + "-C", "link-arg=-Wl,-Tlinkall.x", + "-C", "link-arg=-nostartfiles", + + # LLD + # "-C", "link-arg=-Tlinkall.x", + # "-C", "linker=rust-lld", +] + +[env] +ESP_LOG = "info" + +[unstable] +build-std = ["core", "alloc"] diff --git a/examples/async/embassy_twai/Cargo.toml b/examples/async/embassy_twai/Cargo.toml new file mode 100644 index 00000000000..51ed6524526 --- /dev/null +++ b/examples/async/embassy_twai/Cargo.toml @@ -0,0 +1,61 @@ +[package] +name = "embassy-twai" +version = "0.0.0" +edition = "2024" +publish = false + +[dependencies] +embassy-executor = "0.10.0" +embassy-time = "0.5.0" +esp-backtrace = { path = "../../../esp-backtrace", features = [ + "panic-handler", + "println", +] } +esp-bootloader-esp-idf = { path = "../../../esp-bootloader-esp-idf" } +esp-hal = { path = "../../../esp-hal", features = ["log-04", "unstable"] } +esp-rtos = { path = "../../../esp-rtos", features = ["embassy", "log-04"] } +esp-println = { path = "../../../esp-println", features = ["log-04"] } + +[features] +esp32 = [ + "esp-backtrace/esp32", + "esp-bootloader-esp-idf/esp32", + "esp-rtos/esp32", + "esp-hal/esp32", +] +esp32c3 = [ + "esp-backtrace/esp32c3", + "esp-bootloader-esp-idf/esp32c3", + "esp-rtos/esp32c3", + "esp-hal/esp32c3", +] +esp32c6 = [ + "esp-backtrace/esp32c6", + "esp-bootloader-esp-idf/esp32c6", + "esp-rtos/esp32c6", + "esp-hal/esp32c6", +] +esp32h2 = [ + "esp-backtrace/esp32h2", + "esp-bootloader-esp-idf/esp32h2", + "esp-rtos/esp32h2", + "esp-hal/esp32h2", +] +esp32s2 = [ + "esp-backtrace/esp32s2", + "esp-bootloader-esp-idf/esp32s2", + "esp-rtos/esp32s2", + "esp-hal/esp32s2", +] +esp32s3 = [ + "esp-backtrace/esp32s3", + "esp-bootloader-esp-idf/esp32s3", + "esp-rtos/esp32s3", + "esp-hal/esp32s3", +] + +[profile.release] +debug = true +debug-assertions = true +lto = "fat" +codegen-units = 1 diff --git a/examples/async/embassy_twai/src/main.rs b/examples/async/embassy_twai/src/main.rs new file mode 100644 index 00000000000..3e3dda12f4e --- /dev/null +++ b/examples/async/embassy_twai/src/main.rs @@ -0,0 +1,127 @@ +//! embassy twai +//! +//! This is an example of running the embassy executor and asynchronously +//! sending and receiving TWAI messages between two ESPs. +//! +//! `IS_FIRST_SENDER` below must be set to false on one of the ESP's +//! +//! In case you want to use `self-testing`, get rid of everything related to the +//! aforementioned `IS_FIRST_SENDER` and follow the advice in the comments +//! related to this mode. +//! +//! The following wiring is assumed: +//! - TX/RX => GPIO2, connected internally and with internal pull-up resistor. +//! +//! ESP1/GND --- ESP2/GND +//! ESP1/GPIO2 --- ESP2/GPIO2 +//! +//! With no peer on the bus, transmissions fail: once the peripheral reaches +//! the error-passive state, `transmit_async` gives up on the frame and returns +//! `EspTwaiError::TransmissionAborted` instead of retrying forever. +//! +//! Notes for external transceiver use: +//! +//! The default setup assumes that two microcontrollers are connected directly +//! without an external transceiver. If you want to use an external transceiver, +//! you need to: +//! * uncomment the `rx_pin` line +//! * use `new()` function to create the TWAI configuration. +//! * change the `tx_pin` and `rx_pin` to the appropriate pins for your boards. + +//% CHIP_FILTER: twai_driver_supported + +#![no_std] +#![no_main] + +const IS_FIRST_SENDER: bool = true; + +use embassy_executor::Spawner; +use embassy_time::{Duration, Timer}; +use esp_backtrace as _; +use esp_hal::{ + interrupt::software::SoftwareInterruptControl, + timer::timg::TimerGroup, + twai::{self, EspTwaiFrame, StandardId, TwaiMode, filter::SingleStandardFilter}, +}; +use esp_println::println; + +esp_bootloader_esp_idf::esp_app_desc!(); + +#[esp_hal::main] +async fn main(_spawner: Spawner) { + esp_println::logger::init_logger_from_env(); + let peripherals = esp_hal::init(esp_hal::Config::default()); + + let sw_int = SoftwareInterruptControl::new(peripherals.SW_INTERRUPT); + let timg0 = TimerGroup::new(peripherals.TIMG0); + esp_rtos::start(timg0.timer0, sw_int.software_interrupt0); + + // Without an external transceiver, we only need a single line between the two + // MCUs. + let (rx_pin, tx_pin) = unsafe { peripherals.GPIO2.split() }; + // Use these if you want to use an external transceiver: + // let tx_pin = peripherals.GPIO2; + // let rx_pin = peripherals.GPIO0; + + // The speed of the bus. + const TWAI_BAUDRATE: twai::BaudRate = twai::BaudRate::B125K; + + // !!! Use `new` when using a transceiver. `new_no_transceiver` sets TX to + // open-drain Self-testing also works using the regular `new` function. + + // Begin configuring the TWAI peripheral. The peripheral is in a reset like + // state that prevents transmission but allows configuration. + // For self-testing use `SelfTest` mode of the TWAI peripheral. + let mut twai_config = twai::TwaiConfiguration::new_no_transceiver( + peripherals.TWAI0, + rx_pin, + tx_pin, + TWAI_BAUDRATE, + TwaiMode::Normal, + ); + + // Partially filter the incoming messages to reduce overhead of receiving + // undesired messages. Note that due to how the hardware filters messages, + // standard ids and extended ids may both match a filter. Frame ids should + // be explicitly checked in the application instead of fully relying on + // these partial acceptance filters to exactly match. + // A filter that matches StandardId::ZERO. + twai_config.set_filter( + const { SingleStandardFilter::new(b"xxxxxxxxxx1", b"x", [b"xxxxxxxx", b"xxxxxxxx"]) }, + ); + + // Start the peripheral. This locks the configuration settings of the peripheral + // and puts it into operation mode, allowing packets to be sent and + // received. + let mut twai = twai_config.into_async().start(); + + if IS_FIRST_SENDER { + // Send a frame to the other ESP + // Use `new_self_reception` if you want to use self-testing. + let frame = EspTwaiFrame::new(StandardId::ZERO, &[1, 2, 3]).unwrap(); + match twai.transmit_async(&frame).await { + Ok(()) => println!("Sent a frame"), + Err(e) => println!("Error sending a frame: {e:?}"), + } + } + + loop { + // Wait for a frame to be received. + match twai.receive_async().await { + Ok(frame) => println!("Received a frame: {frame:?}"), + Err(e) => { + println!("Error receiving a frame: {e:?}"); + continue; + } + } + + Timer::after(Duration::from_millis(250)).await; + + let frame = EspTwaiFrame::new(StandardId::ZERO, &[1, 2, 3]).unwrap(); + // Transmit a new frame back to the other ESP + match twai.transmit_async(&frame).await { + Ok(()) => println!("Sent a frame"), + Err(e) => println!("Error sending a frame: {e:?}"), + } + } +} diff --git a/hil-test/src/bin/misc_drivers.rs b/hil-test/src/bin/misc_drivers.rs index b61dff40b43..3d514f3177a 100644 --- a/hil-test/src/bin/misc_drivers.rs +++ b/hil-test/src/bin/misc_drivers.rs @@ -544,6 +544,8 @@ mod twai { #[embedded_test::tests(default_timeout = 3)] mod blocking_tests { + use esp_hal::gpio::{Level, NoPin}; + use super::*; #[init] @@ -582,11 +584,43 @@ mod twai { assert_eq!(frame.data(), &[1, 2, 3]) } + + fn no_init() {} + + #[test(init = no_init)] + // Make sure we don't hang when writing to a bus with no receivers + // also for non-async + fn test_write_into_the_void() { + let peripherals = esp_hal::init(esp_hal::Config::default()); + + // The bus is permanently recessive, so every transmission attempt + // fails immediately (a transmitted dominant bit reads back + // recessive) and drives the peripheral into the error-passive + // state, at which point the driver must give up on the pending + // frame. + let config = twai::TwaiConfiguration::new( + peripherals.TWAI0, + Level::High, + NoPin, + twai::BaudRate::B1000K, + TwaiMode::Normal, + ); + + let mut twai = config.start(); + + let frame = EspTwaiFrame::new(StandardId::new(5).unwrap(), b"12345678").unwrap(); + + block!(twai.transmit(&frame)).unwrap(); + assert_eq!( + block!(twai.transmit(&frame)), + Err(twai::EspTwaiError::TransmissionAborted) + ); + } } #[embedded_test::tests(default_timeout = 3, executor = hil_test::Executor::new())] mod async_tests { - use esp_hal::gpio::NoPin; + use esp_hal::gpio::{Level, NoPin}; use super::*; @@ -674,7 +708,6 @@ mod twai { fn no_init() {} #[test(init = no_init)] - #[ignore] // Regression test for https://github.com/esp-rs/esp-hal/issues/5307 async fn test_write_into_the_void() { let peripherals = esp_hal::init(esp_hal::Config::default()); @@ -683,9 +716,14 @@ mod twai { let timg0 = TimerGroup::new(peripherals.TIMG0); esp_rtos::start(timg0.timer0, sw_int.software_interrupt0); + // The bus is permanently recessive, so every transmission attempt + // fails immediately (a transmitted dominant bit reads back + // recessive) and drives the peripheral into the error-passive + // state, at which point the driver must give up on the pending + // frame. let config = twai::TwaiConfiguration::new( peripherals.TWAI0, - NoPin, + Level::High, NoPin, twai::BaudRate::B1000K, TwaiMode::Normal, @@ -695,7 +733,10 @@ mod twai { let frame = EspTwaiFrame::new(StandardId::new(5).unwrap(), b"12345678").unwrap(); - twai.transmit_async(&frame).await.unwrap(); + assert_eq!( + twai.transmit_async(&frame).await, + Err(twai::EspTwaiError::TransmissionAborted) + ); } } }