Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add TWAI support for ESP32 and ESP32-S2 #341

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions esp-hal-common/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ fn main() {
// - 'systimer'
// - 'timg0'
// - 'timg1'
// - 'twai'
// - 'uart2'
// - 'usb_otg'
// - 'usb_serial_jtag'
Expand All @@ -59,6 +60,7 @@ fn main() {
"spi3",
"timg0",
"timg1",
"twai",
"uart2",
]
} else if esp32c2 {
Expand All @@ -82,6 +84,7 @@ fn main() {
"systimer",
"timg0",
"timg1",
"twai",
"usb_serial_jtag",
]
} else if esp32s2 {
Expand All @@ -98,6 +101,7 @@ fn main() {
"systimer",
"timg0",
"timg1",
"twai",
"usb_otg",
]
} else if esp32s3 {
Expand All @@ -114,6 +118,7 @@ fn main() {
"systimer",
"timg0",
"timg1",
"twai",
"uart2",
"usb_otg",
"usb_serial_jtag",
Expand Down
8 changes: 4 additions & 4 deletions esp-hal-common/src/gpio/esp32.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
2 changes: 2 additions & 0 deletions esp-hal-common/src/gpio/esp32s2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ pub enum InputSignal {
FSPIIO6 = 115,
FSPIIO7 = 116,
FSPICS0 = 117,
TWAI_RX = 123,
SUBSPIQ = 127,
SUBSPID = 128,
SUBSPIHD = 129,
Expand Down Expand Up @@ -215,6 +216,7 @@ pub enum OutputSignal {
FSPICS3 = 120,
FSPICS4 = 121,
FSPICS5 = 122,
TWAI_TX = 123,
SUBSPICLK = 126,
SUBSPIQ = 127,
SUBSPID = 128,
Expand Down
2 changes: 1 addition & 1 deletion esp-hal-common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down
4 changes: 2 additions & 2 deletions esp-hal-common/src/system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ pub enum Peripheral {
I2s1,
#[cfg(usb_otg)]
Usb,
#[cfg(any(esp32s3, esp32c3))]
#[cfg(twai)]
Twai,
}

Expand Down Expand Up @@ -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());
Expand Down
1 change: 0 additions & 1 deletion esp-hal-common/src/twai/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
3 changes: 2 additions & 1 deletion esp32-hal/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand All @@ -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"]
Expand Down
110 changes: 110 additions & 0 deletions esp32-hal/examples/twai.rs
Original file line number Diff line number Diff line change
@@ -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 {}
}
1 change: 1 addition & 0 deletions esp32-hal/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ pub use esp_hal_common::{
spi,
system,
timer,
twai,
uart,
utils,
Cpu,
Expand Down
74 changes: 40 additions & 34 deletions esp32c3-hal/examples/twai.rs
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -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.
Expand All @@ -67,42 +81,34 @@ 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
// and puts it into operation mode, allowing packets to be sent and
// 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 {}
}
3 changes: 2 additions & 1 deletion esp32s2-hal/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand All @@ -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"]
Expand Down
Loading