esp-hal icon indicating copy to clipboard operation
esp-hal copied to clipboard

TWAI: support all devices and improve testing of the peripheral driver

Open jessebraham opened this issue 3 years ago • 13 comments

With #192 merged we now have a TWAI driver for the ESP32-C3. We should support all remaining devices as well before our next release is published. This can be done all in one swing or with a PR per device, doesn't matter. At the time of writing the ESP32-C2 is the only supported device which does not have TWAI.

  • [ ] ESP32
  • [x] ESP32-C3
  • [x] ESP32-C6
  • [ ] ESP32-H2
  • [x] ESP32-S2
  • [x] ESP32-S3

In addition to supporting all devices, it would be ideal to convert the example to a "self test", not requiring any external hardware. There is an example in ESP-IDF which does just this, and can be found here. We generally prefer to avoid requiring external hardware whenever possible (I2C being the lone exception) so ideally this peripheral should be no different. If this is not possible/feasible then two ESPs talking to each other would also be an acceptable solution.

  • [ ] Self-test example

jessebraham avatar Dec 22 '22 00:12 jessebraham

This is awesome !. I'm about to start a (Rust) project which requires TWAI (for connection to NEMA2000), on some existing ESP32 hardware. Is there an ETA for the next release ?

markfarnan avatar Dec 30 '22 23:12 markfarnan

There are at least a couple smaller tasks that will need to be done first, but I imagine we should be able to publish new releases at some point in January. I will be back at work next week so I will evaluate where we stand at that time.

jessebraham avatar Dec 31 '22 00:12 jessebraham

ESP32-S3 support was added in #325.

I will add support for the remaining two chips.

jessebraham avatar Jan 03 '23 18:01 jessebraham

The ESP32-C6 seems to fail in the same way that the ESP32 and ESP32-S2 do, so this will need to be investigated as well.

jessebraham avatar Feb 27 '23 17:02 jessebraham

I have an ESP32-C6 dev board and a motor controller with which i communicate via TWAI if you need to test anything

noonien avatar Mar 21 '23 14:03 noonien

@noonien I think all the necessary cfgs are in place for the ESP32-C6, I had the twai.rs example in my local branch and while it built and ran on the device, I was not able to send/receive any data. You should be able to copy the twai.rs example from another chip's HAL and make the necessary changes to get things building. If you were willing to look into that it would be appreciated, otherwise I'm sure somebody will get to it eventually 😁

jessebraham avatar Mar 22 '23 14:03 jessebraham

While I was on it, I looked into ESP32 without any luck. There are quite a few chapters in the ESP32 Errata document and also esp-idf seems to include some workarounds. I tried to replicate them but no luck.

H2 would hopefully mostly just involve renaming registers/fields to match C6 and some magic applied to the pre-scaler value

bjoernQ avatar Mar 21 '24 17:03 bjoernQ

I tried to send out frames periodically in the experiment code: https://github.com/zephyr-atomi/esp32-weather-kit/blob/main/examples/can_exp.rs, but don't get any signal out on GPIO 1 (TX) of my ESP32C3 board(made by WeAct). The baudrate is set as 10Kbps.

The CAN-bus runs correctly, I can see other CAN frames on it by other devices, and I assume my connection is correct. (I enabled RP2040 with CAN2040 and STM32F103 on the CANbus, both works correctly).

Please let me know anything wrong on my code. Thanks!

zephyr-atomi avatar Apr 07 '24 06:04 zephyr-atomi

I also experienced problems with GPIO 1 while others work fine: https://github.com/esp-rs/esp-hal/issues/1207#issuecomment-1971291234

bjoernQ avatar Apr 07 '24 08:04 bjoernQ

GPIO1 is default UART TX, meaning any logging done can corrupt the TWAI frames. I'm not sure what the best way of converying this information is, any logging via esp_println through the UART will go over any abstraction we have in the HAL.

MabezDev avatar Apr 07 '24 18:04 MabezDev

Ah it's C3 .... UART0 is GPIO20/21 then. I had problems with GPIO 1 and TWAI there

bjoernQ avatar Apr 07 '24 20:04 bjoernQ

I'm trying Macchina A0 (which I believe is esp32)

https://github.com/rnd-ash/Macchina-J2534/blob/main/firmware/custom_can.cpp#L57-L58

image

Unless I'm doing something horribly wrong, there's something wrong with the timing/registers/interrupt/something... I tell it "send 0x7E0 02 3E 00 55 55 55 55 55" and across the wire I see all sorts of different arbitration IDs come across "invalid" as if they got corrupted (0x1DA, 0x3DA, etc.)

#![no_std]
#![no_main]
#![feature(type_alias_impl_trait)]

use embassy_executor::Spawner;
use embassy_sync::{blocking_mutex::raw::NoopRawMutex, channel::Channel};
use embassy_time::{Duration, Timer};
use embedded_can::{Frame, Id, StandardId};
use esp_backtrace as _;
use esp_hal::{
    clock::ClockControl,
    embassy,
    gpio::IO,
    interrupt,
    peripherals::{self, Peripherals, TWAI0},
    prelude::*,
    timer::TimerGroup,
    twai::{self, filter::SingleStandardFilter, EspTwaiFrame, TwaiRx, TwaiTx},
};
use esp_println::{print, println};
use static_cell::make_static;

type TwaiOutbox = Channel<NoopRawMutex, EspTwaiFrame, 16>;

#[embassy_executor::task]
async fn run(channel: &'static TwaiOutbox) {
    loop {
        let frame = Frame::new(
            Id::Standard(StandardId::new(0x7e0).unwrap()),
            &[0x02, 0x3e, 0x00, 0x55, 0x55, 0x55, 0x55, 0x55],
        )
        .unwrap();
        channel.send(frame).await;
        Timer::after(Duration::from_millis(1_000)).await;
    }
}

#[embassy_executor::task]
async fn receiver(
    mut rx: TwaiRx<'static, TWAI0, esp_hal::Async>,
) -> ! {
    loop {
        let frame = rx.receive_async().await;
        match frame {
            Ok(frame) => {
                println!("Received a frame:");
            }
            Err(e) => {
                println!("Receive error: {:?}", e);
            }
        }
        Timer::after(Duration::from_millis(1_000)).await;
    }
}

#[embassy_executor::task]
async fn transmitter(
    mut tx: TwaiTx<'static, TWAI0, esp_hal::Async>,
    channel: &'static TwaiOutbox,
) -> ! {
    loop {
        let frame = channel.receive().await;
        let result = tx.transmit_async(&frame).await;

        match result {
            Ok(()) => {
                print!("Transmitted a frame: ");
                print!("frame.id = {:?} ", frame.id());
                print!("frame.dlc = {:?} ", frame.dlc());
                print!("frame.data = {:02x?}", frame.data());
                println!("");
            }
            Err(e) => {
                println!("Transmit error: {:?}", e);
            }
        }
    }
}

#[main]
async fn main(spawner: Spawner) {
    esp_println::println!("Init!");
    let peripherals = Peripherals::take();
    let system = peripherals.SYSTEM.split();
    let clocks = ClockControl::boot_defaults(system.clock_control).freeze();

    let timg0 = TimerGroup::new_async(peripherals.TIMG0, &clocks);
    embassy::init(&clocks, timg0);

    let io = IO::new(peripherals.GPIO, peripherals.IO_MUX);

    // Need to pull down GPIO 21 to unset the "S" (Silent Mode) pin on CAN Xceiver.
    let mut gpio21 = io.pins.gpio21.into_push_pull_output();
    gpio21.set_low();

    let can_rx_pin = io.pins.gpio4;
    let can_tx_pin = io.pins.gpio5;
    let can_baudrate = twai::BaudRate::B500K;
    let can_filter = SingleStandardFilter::new(b"xxxxxxxxxxx", b"x", [b"xxxxxxxx", b"xxxxxxxx"]);
    let mut can_config = twai::TwaiConfiguration::new_async(
        peripherals.TWAI0,
        can_tx_pin,
        can_rx_pin,
        &clocks,
        can_baudrate,
    );
    can_config.set_filter(can_filter);
    let can_driver = can_config.start();
    let (can_tx, can_rx) = can_driver.split();

    interrupt::enable(
        peripherals::Interrupt::TWAI0,
        interrupt::Priority::Priority1,
    )
    .unwrap();

    let channel = &*make_static!(Channel::new());

    spawner.spawn(receiver(can_rx)).ok();
    spawner.spawn(transmitter(can_tx, channel)).ok();
    spawner.spawn(run(channel)).ok();

    loop {
        Timer::after(Duration::from_millis(1_000)).await;
    }
}
[package]
name = "esp32_can_dongle"
version = "0.1.0"
edition = "2021"

[features]
esp32 = ["esp-hal/esp32", "esp-backtrace/esp32", "esp-println/esp32", "esp-hal-smartled/esp32"]
embassy = ["esp-hal/embassy"]
embassy-time-timg0 = ["esp-hal/embassy-time-timg0"]
embassy-executor-thread = ["esp-hal/embassy-executor-thread"]
embassy-generic-timers = ["embassy-time/generic-queue-8"]
async = ["esp-hal/async"]
default = ["esp32", "embassy", "embassy-time-timg0", "embassy-executor-thread", "embassy-generic-timers", "async"]

[dependencies]
esp-hal             = { version = "0.17.0", features = ["log"] }
esp-hal-smartled    = { version = "0.10.0" }
esp-backtrace       = { version = "0.11.1", features = ["exception-handler", "panic-handler", "println"] }
esp-println         = { version = "0.9.1", features = ["log"] }
embassy-executor    = { version = "0.5.0", features = ["task-arena-size-40960"] }
embassy-sync        = "0.5.0"
embassy-time        = "0.3.0"
embedded-can        = "0.4.1"
static_cell         = { version = "2.0.0", features = ["nightly"] }

[profile.release]
codegen-units    = 1
debug            = 2
debug-assertions = false
incremental      = false
opt-level        = 3
lto = 'fat'
overflow-checks  = false

Is it something I'm doing, is my device going bad/dying, or are the libraries just not ready for usage yet for this device?

brandonros avatar Apr 28 '24 17:04 brandonros

Ah it's C3 .... UART0 is GPIO20/21 then. I had problems with GPIO 1 and TWAI there

Also discovered that this is the case for the C6 too. Pin 0 and 1 can be TWAI Tx but neither can be TWAI Rx.

Works fine on esp-idf-hal.

rand12345 avatar Jun 21 '24 20:06 rand12345

Tested today on the S3, it does not work

ProfFan avatar Aug 01 '24 03:08 ProfFan

Tested today on the S3, it does not work

Did you test with transceivers or without? Last time I tested it, I used transceivers but long time since I last tried it

bjoernQ avatar Aug 01 '24 06:08 bjoernQ

Tested today on the S3, it does not work

Did you test with transceivers or without? Last time I tested it, I used transceivers but long time since I last tried it

I used transceivers. Tested on a live CAN bus with traffic, with esp-hal 0.18.0, also tested with a C/C++ based (ESP-IDF) CAN example. The IDF one works. I can test again tomorrow with main.

ProfFan avatar Aug 01 '24 23:08 ProfFan

I believe this issue encountered by @rand12345 and @brandonros is also fixed by #1906, TWAI works by accident for some pins because these pins are previously configured as input/output 🫠

ProfFan avatar Aug 06 '24 19:08 ProfFan