mirror of
https://github.com/esp-rs/esp-hal.git
synced 2025-10-01 06:11:03 +00:00
Add self-testing mode for TWAI
peripheral. (#1929)
* Add self-testing mode for `TWAI` peripheral * changelog entry * fix docs build * fix async example * Restore example to original state fix comment * `NoAck` -> `SelfTest`
This commit is contained in:
parent
888e9425a4
commit
a3a3ef12e4
@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- Added new `Io::new_no_bind_interrupt` constructor (#1861)
|
- Added new `Io::new_no_bind_interrupt` constructor (#1861)
|
||||||
- Added touch pad support for esp32 (#1873)
|
- Added touch pad support for esp32 (#1873)
|
||||||
- Allow configuration of period updating method for MCPWM timers (#1898)
|
- Allow configuration of period updating method for MCPWM timers (#1898)
|
||||||
|
- Add self-testing mode for TWAI peripheral. (#1929)
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
//! # use esp_hal::twai::filter;
|
//! # use esp_hal::twai::filter;
|
||||||
//! # use esp_hal::twai::TwaiConfiguration;
|
//! # use esp_hal::twai::TwaiConfiguration;
|
||||||
//! # use esp_hal::twai::BaudRate;
|
//! # use esp_hal::twai::BaudRate;
|
||||||
|
//! # use esp_hal::twai::TwaiMode;
|
||||||
//! # use esp_hal::gpio::Io;
|
//! # use esp_hal::gpio::Io;
|
||||||
//! # use embedded_can::Frame;
|
//! # use embedded_can::Frame;
|
||||||
//! # use core::option::Option::None;
|
//! # use core::option::Option::None;
|
||||||
@ -49,6 +50,7 @@
|
|||||||
//! can_rx_pin,
|
//! can_rx_pin,
|
||||||
//! &clocks,
|
//! &clocks,
|
||||||
//! CAN_BAUDRATE,
|
//! CAN_BAUDRATE,
|
||||||
|
//! TwaiMode::Normal
|
||||||
//! );
|
//! );
|
||||||
//!
|
//!
|
||||||
//! // Partially filter the incoming messages to reduce overhead of receiving
|
//! // Partially filter the incoming messages to reduce overhead of receiving
|
||||||
@ -71,6 +73,61 @@
|
|||||||
//! }
|
//! }
|
||||||
//! # }
|
//! # }
|
||||||
//! ```
|
//! ```
|
||||||
|
//! ### Self-testing (self reception of transmitted messages)
|
||||||
|
//! ```rust, no_run
|
||||||
|
#![doc = crate::before_snippet!()]
|
||||||
|
//! # use esp_hal::twai;
|
||||||
|
//! # use embedded_can::Id;
|
||||||
|
//! # use esp_hal::twai::filter::SingleStandardFilter;
|
||||||
|
//! # use esp_hal::twai::filter;
|
||||||
|
//! # use esp_hal::twai::TwaiConfiguration;
|
||||||
|
//! # use esp_hal::twai::BaudRate;
|
||||||
|
//! # use esp_hal::twai::EspTwaiFrame;
|
||||||
|
//! # use esp_hal::twai::StandardId;
|
||||||
|
//! # use esp_hal::twai::TwaiMode;
|
||||||
|
//! # use esp_hal::gpio::Io;
|
||||||
|
//! # use embedded_can::Frame;
|
||||||
|
//! # use core::option::Option::None;
|
||||||
|
//! # use nb::block;
|
||||||
|
//! # let io = Io::new(peripherals.GPIO, peripherals.IO_MUX);
|
||||||
|
//! // Use GPIO pins 2 and 3 to connect to the respective pins on the CAN
|
||||||
|
//! // transceiver.
|
||||||
|
//! let can_tx_pin = io.pins.gpio2;
|
||||||
|
//! let can_rx_pin = io.pins.gpio3;
|
||||||
|
//!
|
||||||
|
//! // The speed of the CAN bus.
|
||||||
|
//! const CAN_BAUDRATE: twai::BaudRate = BaudRate::B1000K;
|
||||||
|
//!
|
||||||
|
//! // Begin configuring the TWAI peripheral.
|
||||||
|
//! let mut can_config = twai::TwaiConfiguration::new(
|
||||||
|
//! peripherals.TWAI0,
|
||||||
|
//! can_tx_pin,
|
||||||
|
//! can_rx_pin,
|
||||||
|
//! &clocks,
|
||||||
|
//! CAN_BAUDRATE,
|
||||||
|
//! TwaiMode::SelfTest
|
||||||
|
//! );
|
||||||
|
//!
|
||||||
|
//! // Partially filter the incoming messages to reduce overhead of receiving
|
||||||
|
//! // undesired messages
|
||||||
|
//! const FILTER: twai::filter::SingleStandardFilter =
|
||||||
|
//! SingleStandardFilter::new(b"xxxxxxxxxx0", b"x",
|
||||||
|
//! [b"xxxxxxxx", b"xxxxxxxx"]);
|
||||||
|
//! can_config.set_filter(FILTER);
|
||||||
|
//!
|
||||||
|
//! // 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 can = can_config.start();
|
||||||
|
//!
|
||||||
|
//! let frame = EspTwaiFrame::new_self_reception(StandardId::ZERO.into(),
|
||||||
|
//! &[1, 2, 3]).unwrap();
|
||||||
|
//! // Wait for a frame to be received.
|
||||||
|
//! let frame = block!(can.receive()).unwrap();
|
||||||
|
//!
|
||||||
|
//! loop {}
|
||||||
|
//! # }
|
||||||
|
//! ```
|
||||||
|
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
|
|
||||||
@ -186,6 +243,17 @@ impl embedded_can::Error for ErrorKind {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Specifies in which mode the TWAI controller will operate.
|
||||||
|
pub enum TwaiMode {
|
||||||
|
/// Normal operating mode
|
||||||
|
Normal,
|
||||||
|
/// Self-test mode (no acknowledgement required for a successful message
|
||||||
|
/// transmission)
|
||||||
|
SelfTest,
|
||||||
|
/// Listen only operating mode
|
||||||
|
ListenOnly,
|
||||||
|
}
|
||||||
|
|
||||||
/// Standard 11-bit CAN Identifier (`0..=0x7FF`).
|
/// Standard 11-bit CAN Identifier (`0..=0x7FF`).
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
pub struct StandardId(u16);
|
pub struct StandardId(u16);
|
||||||
@ -400,6 +468,7 @@ pub struct EspTwaiFrame {
|
|||||||
dlc: usize,
|
dlc: usize,
|
||||||
data: [u8; 8],
|
data: [u8; 8],
|
||||||
is_remote: bool,
|
is_remote: bool,
|
||||||
|
self_reception: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EspTwaiFrame {
|
impl EspTwaiFrame {
|
||||||
@ -418,6 +487,7 @@ impl EspTwaiFrame {
|
|||||||
data: d,
|
data: d,
|
||||||
dlc: data.len(),
|
dlc: data.len(),
|
||||||
is_remote: false,
|
is_remote: false,
|
||||||
|
self_reception: false,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -432,6 +502,25 @@ impl EspTwaiFrame {
|
|||||||
data: [0; 8],
|
data: [0; 8],
|
||||||
dlc,
|
dlc,
|
||||||
is_remote: true,
|
is_remote: true,
|
||||||
|
self_reception: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_self_reception(id: Id, data: &[u8]) -> Option<Self> {
|
||||||
|
if data.len() > 8 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut d: [u8; 8] = [0; 8];
|
||||||
|
let (left, _unused) = d.split_at_mut(data.len());
|
||||||
|
left.clone_from_slice(data);
|
||||||
|
|
||||||
|
Some(EspTwaiFrame {
|
||||||
|
id,
|
||||||
|
data: d,
|
||||||
|
dlc: data.len(),
|
||||||
|
is_remote: false,
|
||||||
|
self_reception: true,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -455,6 +544,7 @@ impl EspTwaiFrame {
|
|||||||
data,
|
data,
|
||||||
dlc,
|
dlc,
|
||||||
is_remote: false,
|
is_remote: false,
|
||||||
|
self_reception: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -643,6 +733,7 @@ where
|
|||||||
clocks: &Clocks<'d>,
|
clocks: &Clocks<'d>,
|
||||||
baud_rate: BaudRate,
|
baud_rate: BaudRate,
|
||||||
no_transceiver: bool,
|
no_transceiver: bool,
|
||||||
|
mode: TwaiMode,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
// Enable the peripheral clock for the TWAI peripheral.
|
// Enable the peripheral clock for the TWAI peripheral.
|
||||||
T::enable_peripheral();
|
T::enable_peripheral();
|
||||||
@ -662,10 +753,24 @@ where
|
|||||||
rx_pin.set_to_input(crate::private::Internal);
|
rx_pin.set_to_input(crate::private::Internal);
|
||||||
rx_pin.connect_input_to_peripheral(T::INPUT_SIGNAL, crate::private::Internal);
|
rx_pin.connect_input_to_peripheral(T::INPUT_SIGNAL, crate::private::Internal);
|
||||||
|
|
||||||
// Set mod to listen only first
|
// Set the operating mode based on provided option
|
||||||
T::register_block()
|
match mode {
|
||||||
.mode()
|
TwaiMode::Normal => {
|
||||||
.modify(|_, w| w.listen_only_mode().set_bit());
|
// Do nothing special, the default state is Normal mode.
|
||||||
|
}
|
||||||
|
TwaiMode::SelfTest => {
|
||||||
|
// Set the self-test mode (no acknowledgement required)
|
||||||
|
T::register_block()
|
||||||
|
.mode()
|
||||||
|
.modify(|_, w| w.self_test_mode().set_bit());
|
||||||
|
}
|
||||||
|
TwaiMode::ListenOnly => {
|
||||||
|
// Set listen-only mode
|
||||||
|
T::register_block()
|
||||||
|
.mode()
|
||||||
|
.modify(|_, w| w.listen_only_mode().set_bit());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Set TEC to 0
|
// Set TEC to 0
|
||||||
T::register_block()
|
T::register_block()
|
||||||
@ -810,8 +915,9 @@ where
|
|||||||
rx_pin: impl Peripheral<P = RX> + 'd,
|
rx_pin: impl Peripheral<P = RX> + 'd,
|
||||||
clocks: &Clocks<'d>,
|
clocks: &Clocks<'d>,
|
||||||
baud_rate: BaudRate,
|
baud_rate: BaudRate,
|
||||||
|
mode: TwaiMode,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self::new_internal(peripheral, tx_pin, rx_pin, clocks, baud_rate, false)
|
Self::new_internal(peripheral, tx_pin, rx_pin, clocks, baud_rate, false, mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new instance of [TwaiConfiguration] meant to connect two ESP32s
|
/// Create a new instance of [TwaiConfiguration] meant to connect two ESP32s
|
||||||
@ -825,8 +931,9 @@ where
|
|||||||
rx_pin: impl Peripheral<P = RX> + 'd,
|
rx_pin: impl Peripheral<P = RX> + 'd,
|
||||||
clocks: &Clocks<'d>,
|
clocks: &Clocks<'d>,
|
||||||
baud_rate: BaudRate,
|
baud_rate: BaudRate,
|
||||||
|
mode: TwaiMode,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self::new_internal(peripheral, tx_pin, rx_pin, clocks, baud_rate, true)
|
Self::new_internal(peripheral, tx_pin, rx_pin, clocks, baud_rate, true, mode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -855,8 +962,10 @@ where
|
|||||||
rx_pin: impl Peripheral<P = RX> + 'd,
|
rx_pin: impl Peripheral<P = RX> + 'd,
|
||||||
clocks: &Clocks<'d>,
|
clocks: &Clocks<'d>,
|
||||||
baud_rate: BaudRate,
|
baud_rate: BaudRate,
|
||||||
|
mode: TwaiMode,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut this = Self::new_internal(peripheral, tx_pin, rx_pin, clocks, baud_rate, false);
|
let mut this =
|
||||||
|
Self::new_internal(peripheral, tx_pin, rx_pin, clocks, baud_rate, false, mode);
|
||||||
this.internal_set_interrupt_handler(T::async_handler());
|
this.internal_set_interrupt_handler(T::async_handler());
|
||||||
this
|
this
|
||||||
}
|
}
|
||||||
@ -872,8 +981,10 @@ where
|
|||||||
rx_pin: impl Peripheral<P = RX> + 'd,
|
rx_pin: impl Peripheral<P = RX> + 'd,
|
||||||
clocks: &Clocks<'d>,
|
clocks: &Clocks<'d>,
|
||||||
baud_rate: BaudRate,
|
baud_rate: BaudRate,
|
||||||
|
mode: TwaiMode,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut this = Self::new_internal(peripheral, tx_pin, rx_pin, clocks, baud_rate, true);
|
let mut this =
|
||||||
|
Self::new_internal(peripheral, tx_pin, rx_pin, clocks, baud_rate, true, mode);
|
||||||
this.internal_set_interrupt_handler(T::async_handler());
|
this.internal_set_interrupt_handler(T::async_handler());
|
||||||
this
|
this
|
||||||
}
|
}
|
||||||
@ -1264,9 +1375,19 @@ pub trait OperationInstance: Instance {
|
|||||||
// Is RTR frame, so no data is included.
|
// Is RTR frame, so no data is included.
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the transmit request command, this will lock the transmit buffer until
|
// Trigger the appropriate transmission request based on self_reception flag
|
||||||
// the transmission is complete or aborted.
|
if frame.self_reception {
|
||||||
register_block.cmd().write(|w| w.tx_req().set_bit());
|
#[cfg(any(esp32, esp32c3, esp32s2, esp32s3))]
|
||||||
|
register_block.cmd().write(|w| w.self_rx_req().set_bit());
|
||||||
|
#[cfg(not(any(esp32, esp32c3, esp32s2, esp32s3)))]
|
||||||
|
register_block
|
||||||
|
.cmd()
|
||||||
|
.write(|w| w.self_rx_request().set_bit());
|
||||||
|
} else {
|
||||||
|
// Set the transmit request command, this will lock the transmit buffer until
|
||||||
|
// the transmission is complete or aborted.
|
||||||
|
register_block.cmd().write(|w| w.tx_req().set_bit());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read a frame from the peripheral.
|
/// Read a frame from the peripheral.
|
||||||
|
@ -31,7 +31,7 @@ use esp_hal::{
|
|||||||
peripherals::{self, Peripherals, TWAI0},
|
peripherals::{self, Peripherals, TWAI0},
|
||||||
system::SystemControl,
|
system::SystemControl,
|
||||||
timer::{timg::TimerGroup, ErasedTimer, OneShotTimer},
|
timer::{timg::TimerGroup, ErasedTimer, OneShotTimer},
|
||||||
twai::{self, EspTwaiFrame, TwaiRx, TwaiTx},
|
twai::{self, EspTwaiFrame, TwaiMode, TwaiRx, TwaiTx},
|
||||||
};
|
};
|
||||||
use esp_println::println;
|
use esp_println::println;
|
||||||
use static_cell::StaticCell;
|
use static_cell::StaticCell;
|
||||||
@ -122,6 +122,7 @@ async fn main(spawner: Spawner) {
|
|||||||
can_rx_pin,
|
can_rx_pin,
|
||||||
&clocks,
|
&clocks,
|
||||||
CAN_BAUDRATE,
|
CAN_BAUDRATE,
|
||||||
|
TwaiMode::Normal,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Partially filter the incoming messages to reduce overhead of receiving
|
// Partially filter the incoming messages to reduce overhead of receiving
|
||||||
|
@ -13,6 +13,9 @@
|
|||||||
//! ESP1/GPIO0 --- ESP1/GPIO2 --- ESP2/GPIO0 --- ESP2/GPIO2 --- 4.8kOhm --- ESP1/5V
|
//! ESP1/GPIO0 --- ESP1/GPIO2 --- ESP2/GPIO0 --- ESP2/GPIO2 --- 4.8kOhm --- ESP1/5V
|
||||||
//!
|
//!
|
||||||
//! `IS_FIRST_SENDER` below must be set to false on one of the ESP's
|
//! `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.
|
||||||
|
|
||||||
//% CHIPS: esp32c3 esp32c6 esp32s2 esp32s3
|
//% CHIPS: esp32c3 esp32c6 esp32s2 esp32s3
|
||||||
|
|
||||||
@ -28,7 +31,7 @@ use esp_hal::{
|
|||||||
peripherals::Peripherals,
|
peripherals::Peripherals,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
system::SystemControl,
|
system::SystemControl,
|
||||||
twai::{self, filter::SingleStandardFilter, EspTwaiFrame, StandardId},
|
twai::{self, filter::SingleStandardFilter, EspTwaiFrame, StandardId, TwaiMode},
|
||||||
};
|
};
|
||||||
use esp_println::println;
|
use esp_println::println;
|
||||||
use nb::block;
|
use nb::block;
|
||||||
@ -48,15 +51,18 @@ fn main() -> ! {
|
|||||||
const CAN_BAUDRATE: twai::BaudRate = twai::BaudRate::B1000K;
|
const CAN_BAUDRATE: twai::BaudRate = twai::BaudRate::B1000K;
|
||||||
|
|
||||||
// !!! Use `new` when using a transceiver. `new_no_transceiver` sets TX to open-drain
|
// !!! 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
|
// Begin configuring the TWAI peripheral. The peripheral is in a reset like
|
||||||
// state that prevents transmission but allows configuration.
|
// state that prevents transmission but allows configuration.
|
||||||
|
// For self-testing use `SelfTest` mode of the TWAI peripheral.
|
||||||
let mut can_config = twai::TwaiConfiguration::new_no_transceiver(
|
let mut can_config = twai::TwaiConfiguration::new_no_transceiver(
|
||||||
peripherals.TWAI0,
|
peripherals.TWAI0,
|
||||||
can_tx_pin,
|
can_tx_pin,
|
||||||
can_rx_pin,
|
can_rx_pin,
|
||||||
&clocks,
|
&clocks,
|
||||||
CAN_BAUDRATE,
|
CAN_BAUDRATE,
|
||||||
|
TwaiMode::Normal,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Partially filter the incoming messages to reduce overhead of receiving
|
// Partially filter the incoming messages to reduce overhead of receiving
|
||||||
@ -76,6 +82,7 @@ fn main() -> ! {
|
|||||||
|
|
||||||
if IS_FIRST_SENDER {
|
if IS_FIRST_SENDER {
|
||||||
// Send a frame to the other ESP
|
// Send a frame to the other ESP
|
||||||
|
// Use `new_self_reception` if you want to use self-testing.
|
||||||
let frame = EspTwaiFrame::new(StandardId::ZERO.into(), &[1, 2, 3]).unwrap();
|
let frame = EspTwaiFrame::new(StandardId::ZERO.into(), &[1, 2, 3]).unwrap();
|
||||||
block!(can.transmit(&frame)).unwrap();
|
block!(can.transmit(&frame)).unwrap();
|
||||||
println!("Sent a frame");
|
println!("Sent a frame");
|
||||||
|
Loading…
x
Reference in New Issue
Block a user