diff --git a/esp-hal-common/build.rs b/esp-hal-common/build.rs index 422abaaaadd..aa8b1395153 100644 --- a/esp-hal-common/build.rs +++ b/esp-hal-common/build.rs @@ -39,6 +39,7 @@ fn main() { // - 'systimer' // - 'timg0' // - 'timg1' + // - 'twai' // - 'uart2' // - 'usb_otg' // - 'usb_serial_jtag' @@ -59,6 +60,7 @@ fn main() { "spi3", "timg0", "timg1", + "twai", "uart2", ] } else if esp32c2 { @@ -82,6 +84,7 @@ fn main() { "systimer", "timg0", "timg1", + "twai", "usb_serial_jtag", ] } else if esp32s2 { @@ -98,6 +101,7 @@ fn main() { "systimer", "timg0", "timg1", + "twai", "usb_otg", ] } else if esp32s3 { @@ -114,6 +118,7 @@ fn main() { "systimer", "timg0", "timg1", + "twai", "uart2", "usb_otg", "usb_serial_jtag", diff --git a/esp-hal-common/src/gpio/esp32.rs b/esp-hal-common/src/gpio/esp32.rs index 0c7831c862f..61455529966 100644 --- a/esp-hal-common/src/gpio/esp32.rs +++ b/esp-hal-common/src/gpio/esp32.rs @@ -167,7 +167,7 @@ pub enum InputSignal { RMT_SIG_6 = 89, RMT_SIG_7 = 90, EXT_ADC_START = 93, - CAN_RX = 94, + TWAI_RX = 94, I2CEXT1_SCL = 95, I2CEXT1_SDA = 96, HOST_CARD_DETECT_N_1 = 97, @@ -198,7 +198,7 @@ pub enum InputSignal { PWM3_CAP1 = 122, PWM3_CAP2 = 123, PWM3_CAP3 = 124, - CAN_CLKOUT = 125, + TWAI_CLKOUT = 125, SPID4 = 128, SPID5 = 129, SPID6 = 130, @@ -422,8 +422,8 @@ pub enum OutputSignal { PWM2_3L = 119, PWM2_4H = 120, PWM2_4L = 121, - CAN_TX = 123, - CAN_BUS_OFF_ON = 124, + TWAI_TX = 123, + TWAI_BUS_OFF_ON = 124, SPID4 = 128, SPID5 = 129, SPID6 = 130, diff --git a/esp-hal-common/src/gpio/esp32s2.rs b/esp-hal-common/src/gpio/esp32s2.rs index f4ac7cb7636..28e632537fb 100644 --- a/esp-hal-common/src/gpio/esp32s2.rs +++ b/esp-hal-common/src/gpio/esp32s2.rs @@ -128,6 +128,7 @@ pub enum InputSignal { FSPIIO6 = 115, FSPIIO7 = 116, FSPICS0 = 117, + TWAI_RX = 123, SUBSPIQ = 127, SUBSPID = 128, SUBSPIHD = 129, @@ -215,6 +216,7 @@ pub enum OutputSignal { FSPICS3 = 120, FSPICS4 = 121, FSPICS5 = 122, + TWAI_TX = 123, SUBSPICLK = 126, SUBSPIQ = 127, SUBSPID = 128, diff --git a/esp-hal-common/src/lib.rs b/esp-hal-common/src/lib.rs index f5a2f812121..8da3438d0a5 100644 --- a/esp-hal-common/src/lib.rs +++ b/esp-hal-common/src/lib.rs @@ -73,7 +73,7 @@ pub mod system; #[cfg(systimer)] pub mod systimer; pub mod timer; -#[cfg(any(esp32s3, esp32c3))] +#[cfg(twai)] pub mod twai; pub mod uart; #[cfg(usb_serial_jtag)] diff --git a/esp-hal-common/src/system.rs b/esp-hal-common/src/system.rs index fb2f70e4b65..827ae49e048 100644 --- a/esp-hal-common/src/system.rs +++ b/esp-hal-common/src/system.rs @@ -42,7 +42,7 @@ pub enum Peripheral { I2s1, #[cfg(usb_otg)] Usb, - #[cfg(any(esp32s3, esp32c3))] + #[cfg(twai)] Twai, } @@ -151,7 +151,7 @@ impl PeripheralClockControl { perip_clk_en0.modify(|_, w| w.usb_clk_en().set_bit()); perip_rst_en0.modify(|_, w| w.usb_rst().clear_bit()); } - #[cfg(any(esp32s3, esp32c3))] + #[cfg(twai)] Peripheral::Twai => { perip_clk_en0.modify(|_, w| w.twai_clk_en().set_bit()); perip_rst_en0.modify(|_, w| w.twai_rst().clear_bit()); diff --git a/esp-hal-common/src/twai/mod.rs b/esp-hal-common/src/twai/mod.rs index 4d76006d7b2..209b6714842 100644 --- a/esp-hal-common/src/twai/mod.rs +++ b/esp-hal-common/src/twai/mod.rs @@ -705,7 +705,6 @@ pub trait Instance { fn register_block(&self) -> &RegisterBlock; } -#[cfg(any(esp32s3, esp32c3))] impl Instance for crate::peripherals::TWAI { #[inline(always)] fn register_block(&self) -> &RegisterBlock { diff --git a/esp32-hal/Cargo.toml b/esp32-hal/Cargo.toml index f6134673532..7acbc8e5dd5 100644 --- a/esp32-hal/Cargo.toml +++ b/esp32-hal/Cargo.toml @@ -26,6 +26,7 @@ categories = [ [dependencies] embassy-time = { version = "0.1.0", features = ["nightly"], optional = true } +embedded-can = { version = "0.4.1", optional = true } embedded-hal = { version = "0.2.7", features = ["unproven"] } embedded-hal-1 = { version = "=1.0.0-alpha.9", optional = true, package = "embedded-hal" } embedded-hal-async = { version = "0.1.0-alpha.3", optional = true } @@ -48,7 +49,7 @@ static_cell = "1.0.0" [features] default = ["rt", "vectored"] bluetooth = [] -eh1 = ["esp-hal-common/eh1", "dep:embedded-hal-1", "dep:embedded-hal-nb"] +eh1 = ["dep:embedded-can", "esp-hal-common/eh1", "dep:embedded-hal-1", "dep:embedded-hal-nb"] rt = ["xtensa-lx-rt/esp32"] smartled = ["esp-hal-common/smartled"] ufmt = ["esp-hal-common/ufmt"] diff --git a/esp32-hal/examples/twai.rs b/esp32-hal/examples/twai.rs new file mode 100644 index 00000000000..9ca8ab7e592 --- /dev/null +++ b/esp32-hal/examples/twai.rs @@ -0,0 +1,110 @@ +//! This example sends a CAN message to another ESP and receives it back. +//! +//! Wiring: +//! This example works without CAN Transceivers by: +//! * setting the tx pins to open drain +//! * connecting all rx and tx pins together +//! * adding a pull-up to the signal pins +//! +//! ESP1/GND --- ESP2/GND +//! ESP1/IO2 --- ESP1/IO4 --- ESP2/IO2 --- ESP2/IO4 --- 4.8kOhm --- ESP1/5V +//! +//! `IS_FIRST_SENDER` below must be set to false on one of the ESP's + +#![no_std] +#![no_main] + +const IS_FIRST_SENDER: bool = true; + +// Run this example with the eh1 feature enabled to use embedded-can instead of +// embedded-hal-0.2.7. embedded-can was split off from embedded-hal before it's +// upgrade to 1.0.0. cargo run --example twai --features eh1 --release +#[cfg(feature = "eh1")] +use embedded_can::{nb::Can, Frame, StandardId}; +// Run this example without the eh1 flag to use the embedded-hal 0.2.7 CAN traits. +// cargo run --example twai --release +#[cfg(not(feature = "eh1"))] +use embedded_hal::can::{Can, Frame, StandardId}; +use esp32_hal::{ + clock::ClockControl, + gpio::IO, + peripherals::Peripherals, + prelude::*, + timer::TimerGroup, + twai, + Rtc, +}; +use esp_backtrace as _; +use esp_println::println; +use nb::block; +use xtensa_lx_rt::entry; + +#[entry] +fn main() -> ! { + let peripherals = Peripherals::take(); + let mut system = peripherals.DPORT.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks); + let mut wdt = timer_group0.wdt; + let mut rtc = Rtc::new(peripherals.RTC_CNTL); + + // Disable MWDT and RWDT (Watchdog) flash boot protection + wdt.disable(); + rtc.rwdt.disable(); + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + + // Set the tx pin as open drain. Skip this if using transceivers. + let can_tx_pin = io.pins.gpio2.into_open_drain_output(); + let can_rx_pin = io.pins.gpio4; + + // The speed of the CAN bus. + const CAN_BAUDRATE: twai::BaudRate = twai::BaudRate::B1000K; + + // Begin configuring the TWAI peripheral. The peripheral is in a reset like + // state that prevents transmission but allows configuration. + let mut can_config = twai::TwaiConfiguration::new( + peripherals.TWAI, + can_tx_pin, + can_rx_pin, + &mut system.peripheral_clock_control, + &clocks, + CAN_BAUDRATE, + ); + + // 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. + const FILTER: twai::filter::SingleStandardFilter = + twai::filter::SingleStandardFilter::new(b"00000000000", 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(); + + if IS_FIRST_SENDER { + // Send a frame to the other ESP + let frame = Frame::new(StandardId::ZERO, &[1, 2, 3]).unwrap(); + block!(can.transmit(&frame)).unwrap(); + println!("Sent a frame"); + } + + // Wait for a frame to be received. + let frame = block!(can.receive()).unwrap(); + + println!("Received a frame: {frame:?}"); + + if !IS_FIRST_SENDER { + // Transmit the frame back to the other ESP + block!(can.transmit(&frame)).unwrap(); + println!("Sent a frame"); + } + + loop {} +} diff --git a/esp32-hal/src/lib.rs b/esp32-hal/src/lib.rs index a213b5a1813..a5c5ef1b9b8 100644 --- a/esp32-hal/src/lib.rs +++ b/esp32-hal/src/lib.rs @@ -26,6 +26,7 @@ pub use esp_hal_common::{ spi, system, timer, + twai, uart, utils, Cpu, diff --git a/esp32c3-hal/examples/twai.rs b/esp32c3-hal/examples/twai.rs index 67a9cd4e00f..de956cc1dda 100644 --- a/esp32c3-hal/examples/twai.rs +++ b/esp32c3-hal/examples/twai.rs @@ -1,15 +1,30 @@ +//! This example sends a CAN message to another ESP and receives it back. +//! +//! Wiring: +//! This example works without CAN Transceivers by: +//! * setting the tx pins to open drain +//! * connecting all rx and tx pins together +//! * adding a pull-up to the signal pins +//! +//! ESP1/GND --- ESP2/GND +//! ESP1/IO2 --- ESP1/IO3 --- ESP2/IO2 --- ESP2/IO3 --- 4.8kOhm --- ESP1/5V +//! +//! `IS_FIRST_SENDER` below must be set to false on one of the ESP's + #![no_std] #![no_main] +const IS_FIRST_SENDER: bool = true; + // Run this example with the eh1 feature enabled to use embedded-can instead of // embedded-hal-0.2.7. embedded-can was split off from embedded-hal before it's // upgrade to 1.0.0. cargo run --example twai --features eh1 --release #[cfg(feature = "eh1")] -use embedded_can::{nb::Can, Frame, Id}; +use embedded_can::{nb::Can, Frame, StandardId}; // Run this example without the eh1 flag to use the embedded-hal 0.2.7 CAN traits. // cargo run --example twai --release #[cfg(not(feature = "eh1"))] -use embedded_hal::can::{Can, Frame, Id}; +use embedded_hal::can::{Can, Frame, StandardId}; use esp32c3_hal::{ clock::ClockControl, gpio::IO, @@ -44,9 +59,8 @@ fn main() -> ! { 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; + // Set the tx pin as open drain. Skip this if using transceivers. + let can_tx_pin = io.pins.gpio2.into_open_drain_output(); let can_rx_pin = io.pins.gpio3; // The speed of the CAN bus. @@ -67,10 +81,10 @@ fn main() -> ! { // 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 - // standard ids of an even value. + // these partial acceptance filters to exactly match. + // A filter that matches StandardId::ZERO. const FILTER: twai::filter::SingleStandardFilter = - twai::filter::SingleStandardFilter::new(b"xxxxxxxxxx0", b"x", [b"xxxxxxxx", b"xxxxxxxx"]); + twai::filter::SingleStandardFilter::new(b"00000000000", b"x", [b"xxxxxxxx", b"xxxxxxxx"]); can_config.set_filter(FILTER); // Start the peripheral. This locks the configuration settings of the peripheral @@ -78,31 +92,23 @@ fn main() -> ! { // received. let mut can = can_config.start(); - loop { - // Wait for a frame to be received. - let frame = block!(can.receive()).unwrap(); - - println!("Received a frame:"); - - // Print different messages based on the frame id type. - match frame.id() { - Id::Standard(id) => { - println!("\tStandard Id: {:?}", id); - } - Id::Extended(id) => { - println!("\tExtended Id: {:?}", id); - } - } - - // Print out the frame data or the requested data length code for a remote - // transmission request frame. - if frame.is_data_frame() { - println!("\tData: {:?}", frame.data()); - } else { - println!("\tRemote Frame. Data Length Code: {}", frame.dlc()); - } - - // Transmit the frame back. - let _result = block!(can.transmit(&frame)).unwrap(); + if IS_FIRST_SENDER { + // Send a frame to the other ESP + let frame = Frame::new(StandardId::ZERO, &[1, 2, 3]).unwrap(); + block!(can.transmit(&frame)).unwrap(); + println!("Sent a frame"); } + + // Wait for a frame to be received. + let frame = block!(can.receive()).unwrap(); + + println!("Received a frame: {frame:?}"); + + if !IS_FIRST_SENDER { + // Transmit the frame back to the other ESP + block!(can.transmit(&frame)).unwrap(); + println!("Sent a frame"); + } + + loop {} } diff --git a/esp32s2-hal/Cargo.toml b/esp32s2-hal/Cargo.toml index bd59c30bbc3..ec97d27115d 100644 --- a/esp32s2-hal/Cargo.toml +++ b/esp32s2-hal/Cargo.toml @@ -26,6 +26,7 @@ categories = [ [dependencies] embassy-time = { version = "0.1.0", features = ["nightly"], optional = true } +embedded-can = { version = "0.4.1", optional = true } embedded-hal = { version = "0.2.7", features = ["unproven"] } embedded-hal-1 = { version = "=1.0.0-alpha.9", optional = true, package = "embedded-hal" } embedded-hal-async = { version = "0.1.0-alpha.3", optional = true } @@ -50,7 +51,7 @@ static_cell = "1.0.0" [features] default = ["rt", "vectored"] -eh1 = ["esp-hal-common/eh1", "dep:embedded-hal-1", "dep:embedded-hal-nb"] +eh1 = ["dep:embedded-can", "esp-hal-common/eh1", "dep:embedded-hal-1", "dep:embedded-hal-nb"] rt = ["xtensa-lx-rt/esp32s2"] smartled = ["esp-hal-common/smartled"] ufmt = ["esp-hal-common/ufmt"] diff --git a/esp32s2-hal/examples/twai.rs b/esp32s2-hal/examples/twai.rs new file mode 100644 index 00000000000..910c1dfe22b --- /dev/null +++ b/esp32s2-hal/examples/twai.rs @@ -0,0 +1,110 @@ +//! This example sends a CAN message to another ESP and receives it back. +//! +//! Wiring: +//! This example works without CAN Transceivers by: +//! * setting the tx pins to open drain +//! * connecting all rx and tx pins together +//! * adding a pull-up to the signal pins +//! +//! ESP1/GND --- ESP2/GND +//! ESP1/IO2 --- ESP1/IO3 --- ESP2/IO2 --- ESP2/IO3 --- 4.8kOhm --- ESP1/5V +//! +//! `IS_FIRST_SENDER` below must be set to false on one of the ESP's + +#![no_std] +#![no_main] + +const IS_FIRST_SENDER: bool = true; + +// Run this example with the eh1 feature enabled to use embedded-can instead of +// embedded-hal-0.2.7. embedded-can was split off from embedded-hal before it's +// upgrade to 1.0.0. cargo run --example twai --features eh1 --release +#[cfg(feature = "eh1")] +use embedded_can::{nb::Can, Frame, StandardId}; +// Run this example without the eh1 flag to use the embedded-hal 0.2.7 CAN traits. +// cargo run --example twai --release +#[cfg(not(feature = "eh1"))] +use embedded_hal::can::{Can, Frame, StandardId}; +use esp32s2_hal::{ + clock::ClockControl, + gpio::IO, + peripherals::Peripherals, + prelude::*, + timer::TimerGroup, + twai, + Rtc, +}; +use esp_backtrace as _; +use esp_println::println; +use nb::block; +use xtensa_lx_rt::entry; + +#[entry] +fn main() -> ! { + let peripherals = Peripherals::take(); + let mut system = peripherals.SYSTEM.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks); + let mut wdt = timer_group0.wdt; + let mut rtc = Rtc::new(peripherals.RTC_CNTL); + + // Disable watchdog timer + wdt.disable(); + rtc.rwdt.disable(); + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + + // Set the tx pin as open drain. Skip this if using transceivers. + let can_tx_pin = io.pins.gpio2.into_open_drain_output(); + let can_rx_pin = io.pins.gpio3; + + // The speed of the CAN bus. + const CAN_BAUDRATE: twai::BaudRate = twai::BaudRate::B1000K; + + // Begin configuring the TWAI peripheral. The peripheral is in a reset like + // state that prevents transmission but allows configuration. + let mut can_config = twai::TwaiConfiguration::new( + peripherals.TWAI, + can_tx_pin, + can_rx_pin, + &mut system.peripheral_clock_control, + &clocks, + CAN_BAUDRATE, + ); + + // 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. + const FILTER: twai::filter::SingleStandardFilter = + twai::filter::SingleStandardFilter::new(b"00000000000", 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(); + + if IS_FIRST_SENDER { + // Send a frame to the other ESP + let frame = Frame::new(StandardId::ZERO, &[1, 2, 3]).unwrap(); + block!(can.transmit(&frame)).unwrap(); + println!("Sent a frame"); + } + + // Wait for a frame to be received. + let frame = block!(can.receive()).unwrap(); + + println!("Received a frame: {frame:?}"); + + if !IS_FIRST_SENDER { + // Transmit the frame back to the other ESP + block!(can.transmit(&frame)).unwrap(); + println!("Sent a frame"); + } + + loop {} +} diff --git a/esp32s2-hal/src/lib.rs b/esp32s2-hal/src/lib.rs index 1c9f481fb53..94a08e8e7da 100644 --- a/esp32s2-hal/src/lib.rs +++ b/esp32s2-hal/src/lib.rs @@ -26,6 +26,7 @@ pub use esp_hal_common::{ system, systimer, timer, + twai, uart, utils, Cpu,