-
Notifications
You must be signed in to change notification settings - Fork 253
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
ESP32: Async SPI Master freezes in write
#1728
Comments
What exactly sure what you are after but something like this works: #![no_std]
#![no_main]
use embassy_executor::Spawner;
use embassy_time::{Duration, Timer};
use esp_backtrace as _;
use esp_hal::{
clock::ClockControl,
dma::*,
dma_descriptors,
gpio::Io,
peripherals::Peripherals,
prelude::*,
spi::{
master::{prelude::*, Spi},
SpiMode,
},
system::SystemControl,
timer::timg::TimerGroup,
};
#[main]
async fn main(_spawner: Spawner) {
esp_println::println!("Init!");
let peripherals = Peripherals::take();
let system = SystemControl::new(peripherals.SYSTEM);
let clocks = ClockControl::boot_defaults(system.clock_control).freeze();
let timg0 = TimerGroup::new_async(peripherals.TIMG0, &clocks);
esp_hal_embassy::init(&clocks, timg0);
let io = Io::new(peripherals.GPIO, peripherals.IO_MUX);
let sclk = io.pins.gpio0;
let miso = io.pins.gpio2;
let mosi = io.pins.gpio4;
let cs = io.pins.gpio5;
let dma = Dma::new(peripherals.DMA);
#[cfg(any(feature = "esp32", feature = "esp32s2"))]
let dma_channel = dma.spi2channel;
#[cfg(not(any(feature = "esp32", feature = "esp32s2")))]
let dma_channel = dma.channel0;
let (descriptors, rx_descriptors) = dma_descriptors!(32000);
let spi = Spi::new(peripherals.SPI2, 100.kHz(), SpiMode::Mode0, &clocks)
.with_pins(Some(sclk), Some(mosi), Some(miso), Some(cs))
.with_dma(
dma_channel.configure_for_async(false, DmaPriority::Priority0),
descriptors,
rx_descriptors,
);
do_spi(spi).await;
}
async fn do_spi<'a>(
mut spi: esp_hal::spi::master::dma::SpiDma<
'a,
esp_hal::peripherals::SPI2,
Channel0,
esp_hal::spi::FullDuplexMode,
esp_hal::Async,
>,
) {
esp_println::println!("here");
let send_buffer = [0, 1, 2, 3, 4, 5, 6, 7];
loop {
let mut buffer = [0; 8];
esp_println::println!("Sending bytes");
embedded_hal_async::spi::SpiBus::transfer(&mut spi, &mut buffer, &send_buffer)
.await
.unwrap();
esp_println::println!("Bytes received: {:?}", buffer);
Timer::after(Duration::from_millis(5_000)).await;
}
} If you want to spawn an embassy task you would need to promote the lifetime to static (e.g. |
Thanks, I missed that. Is there some example using SpiDMA as an embedded_hal_async SpiDevice (using embassy_embedded_hal or embedded_hal_bus)? I am still trying to figure out why my code gets blocked at the flush at the end of a transaction (which seems to be waiting for some peripheral ref). |
We used to have those examples but decided to consolidate the mostly redundant examples. |
I will try to fix it and will create a repro example if I'm sure it is an issue with the crate (most likely, I'm just using it wrong). |
You can read about the details in the TRM - e.g. for ESP32-C6: https://www.espressif.com/sites/default/files/documentation/esp32-c6_technical_reference_manual_en.pdf#page=793 |
I further experimented with it, but the async spi stack in esp-hal (0.18) always gets stuck during a write. I have attached a stripped-down version of my code as well as the sync equivalent that works. Do you have any idea what might cause this? Broken Async (embedded_hal_bus also did not work): // SPDX-FileCopyrightText: © 2023 Technical University of Munich, Chair of Connected Mobility
// SPDX-License-Identifier: MIT
// Based on https://github.com/esp-rs/esp-hal/blob/main/esp32-hal/examples/embassy_hello_world.rs, https://github.com/esp-rs/esp-template/blob/main/src/main.rs & https://github.com/esp-rs/esp-wifi/blob/main/examples-esp32/examples/embassy_dhcp.rs
#![no_std]
#![no_main]
extern crate alloc;
pub mod wifi;
pub mod epaper_display_impl;
use embedded_hal_async::{delay::DelayNs, spi::SpiDevice};
use esp_backtrace as _;
use hal::prelude::*;
pub mod epaper_v2;
use hal::spi::master::prelude::*;
#[global_allocator]
static ALLOCATOR: esp_alloc::EspHeap = esp_alloc::EspHeap::empty();
static RNG: once_cell::sync::OnceCell<hal::rng::Rng> = once_cell::sync::OnceCell::new();
const ESP_GETRANDOM_ERROR: u32 = getrandom::Error::CUSTOM_START + 1;
const NODE_ID: uuid::Uuid = uuid::uuid!("0827240a-3050-4604-bf3e-564c41c77106");
fn init_heap() {
const HEAP_SIZE: usize = 32 * 1024;
static mut HEAP: core::mem::MaybeUninit<[u8; HEAP_SIZE]> = core::mem::MaybeUninit::uninit();
unsafe {
ALLOCATOR.init(HEAP.as_mut_ptr() as *mut u8, HEAP_SIZE);
}
}
#[entry]
fn main() -> ! {
esp_println::logger::init_logger(log::LevelFilter::Info);
esp_println::println!("Start Edgeless Embedded.");
// https://github.com/esp-rs/esp-template/blob/main/src/main.rs
init_heap();
let peripherals = hal::peripherals::Peripherals::take();
#[allow(unused_variables)]
let io = hal::gpio::Io::new(peripherals.GPIO, peripherals.IO_MUX);
let system = hal::system::SystemControl::new(peripherals.SYSTEM);
let clocks = hal::clock::ClockControl::max(system.clock_control).freeze();
let timer_group0 = hal::timer::timg::TimerGroup::new_async(peripherals.TIMG0, &clocks);
let timer_group1 = hal::timer::timg::TimerGroup::new(peripherals.TIMG1, &clocks, None);
esp_hal_embassy::init(&clocks, timer_group0);
let dma = hal::dma::Dma::new(peripherals.DMA);
let dma_channel = dma.spi2channel;
let (mut descriptors, mut rx_descriptors) = hal::dma_descriptors!(32000);
static DESCRIPTORS : static_cell::StaticCell<[hal::dma::DmaDescriptor; 8]> = static_cell::StaticCell::new();
let desc = DESCRIPTORS.init_with(|| descriptors);
static RX_DESCRIPTORS : static_cell::StaticCell<[hal::dma::DmaDescriptor; 8]> = static_cell::StaticCell::new();
let rx_desc = RX_DESCRIPTORS.init_with(|| rx_descriptors);
let spi = hal::spi::master::Spi::new(peripherals.SPI2, 100u32.kHz(), hal::spi::SpiMode::Mode0, &clocks)
.with_sck(io.pins.gpio18)
.with_mosi(io.pins.gpio23);
let spi_dma = spi.with_dma(
dma_channel.configure_for_async(false, desc, rx_desc, hal::dma::DmaPriority::Priority0)
);
let display_pin = hal::gpio::Output::new(io.pins.gpio5, hal::gpio::Level::Low);
let spi_mutex = embassy_sync::mutex::Mutex::new(spi_dma);
static SPI_MUTEX : static_cell::StaticCell<embassy_sync::mutex::Mutex<
embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex,
hal::spi::master::dma::SpiDma<
'static,
hal::peripherals::SPI2,
hal::dma::Spi2DmaChannel,
hal::spi::FullDuplexMode,
hal::Async
>,
>> = static_cell::StaticCell::new();
let spi_mutex = SPI_MUTEX.init_with(|| spi_mutex);
let mut spi_dev = embassy_embedded_hal::shared_bus::asynch::spi::SpiDevice::new(spi_mutex, display_pin);
let busy_pin = hal::gpio::Input::new(io.pins.gpio4, hal::gpio::Pull::None);
let dc_pin = hal::gpio::Output::new(io.pins.gpio17, hal::gpio::Level::High);
let rst_pin = hal::gpio::Output::new(io.pins.gpio16, hal::gpio::Level::High);
let mut epaper_delay = embassy_time::Delay{};
static SPI_DEV: static_cell::StaticCell<embassy_embedded_hal::shared_bus::asynch::spi::SpiDevice<
'static,
embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex,
hal::spi::master::dma::SpiDma<
'static,
hal::peripherals::SPI2,
hal::dma::Spi2DmaChannel,
hal::spi::FullDuplexMode,
hal::Async
>,
hal::gpio::Output<
'static,
hal::gpio::GpioPin<5>
>,
>> = static_cell::StaticCell::new();
let spi_dev = SPI_DEV.init_with(|| spi_dev);
static EXECUTOR_RAW: static_cell::StaticCell<esp_hal_embassy::Executor> = static_cell::StaticCell::new();
let executor = EXECUTOR_RAW.init_with(|| esp_hal_embassy::Executor::new());
executor.run(|spawner| {
spawner.spawn(async_main(
spi_dev,
busy_pin,
dc_pin,
rst_pin,
epaper_delay
));
});
#[allow(unreachable_code)]
loop {}
}
#[embassy_executor::task]
async fn async_main (
spi: &'static mut embassy_embedded_hal::shared_bus::asynch::spi::SpiDevice<
'static,
embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex,
hal::spi::master::dma::SpiDma<
'static,
hal::peripherals::SPI2,
hal::dma::Spi2DmaChannel,
hal::spi::FullDuplexMode,
hal::Async
>,
hal::gpio::Output<
'static,
hal::gpio::GpioPin<5>
>,
>,
mut busy: hal::gpio::Input<'static, hal::gpio::Gpio4>,
mut dc: hal::gpio::Output<'static, hal::gpio::Gpio17>,
mut rst: hal::gpio::Output<'static, hal::gpio::Gpio16>,
mut delay: embassy_time::Delay
) {
log::info!("Start");
log::info!("Reset");
rst.set_high();
delay.delay_ms(10).await;
rst.set_low();
delay.delay_ms(10).await;
rst.set_high();
delay.delay_ms(200).await;
log::info!("Wait Idle");
while busy.is_high() {
delay.delay_ms(10).await;
}
log::info!("SPI DC");
dc.set_low();
log::info!("SPI Write");
log::info!("{:?}", spi.write(&[18]).await);
log::info!("SPI_WRITE_DONE");
} Working Sync Version: // SPDX-FileCopyrightText: © 2023 Technical University of Munich, Chair of Connected Mobility
// SPDX-License-Identifier: MIT
// Based on https://github.com/esp-rs/esp-hal/blob/main/esp32-hal/examples/embassy_hello_world.rs, https://github.com/esp-rs/esp-template/blob/main/src/main.rs & https://github.com/esp-rs/esp-wifi/blob/main/examples-esp32/examples/embassy_dhcp.rs
#![no_std]
#![no_main]
extern crate alloc;
pub mod wifi;
pub mod epaper_display_impl;
use embedded_hal_async::delay::DelayNs;
use esp_backtrace as _;
use hal::prelude::*;
use embedded_hal::spi::SpiDevice;
pub mod epaper_v2;
// use hal::spi::master::prelude::*;
#[global_allocator]
static ALLOCATOR: esp_alloc::EspHeap = esp_alloc::EspHeap::empty();
static RNG: once_cell::sync::OnceCell<hal::rng::Rng> = once_cell::sync::OnceCell::new();
const ESP_GETRANDOM_ERROR: u32 = getrandom::Error::CUSTOM_START + 1;
const NODE_ID: uuid::Uuid = uuid::uuid!("0827240a-3050-4604-bf3e-564c41c77106");
fn init_heap() {
const HEAP_SIZE: usize = 32 * 1024;
static mut HEAP: core::mem::MaybeUninit<[u8; HEAP_SIZE]> = core::mem::MaybeUninit::uninit();
unsafe {
ALLOCATOR.init(HEAP.as_mut_ptr() as *mut u8, HEAP_SIZE);
}
}
#[entry]
fn main() -> ! {
esp_println::logger::init_logger(log::LevelFilter::Info);
esp_println::println!("Start Edgeless Embedded.");
// https://github.com/esp-rs/esp-template/blob/main/src/main.rs
init_heap();
let peripherals = hal::peripherals::Peripherals::take();
#[allow(unused_variables)]
let io = hal::gpio::Io::new(peripherals.GPIO, peripherals.IO_MUX);
let system = hal::system::SystemControl::new(peripherals.SYSTEM);
let clocks = hal::clock::ClockControl::max(system.clock_control).freeze();
let timer_group0 = hal::timer::timg::TimerGroup::new_async(peripherals.TIMG0, &clocks);
let timer_group1 = hal::timer::timg::TimerGroup::new(peripherals.TIMG1, &clocks, None);
esp_hal_embassy::init(&clocks, timer_group0);
let spi = hal::spi::master::Spi::new(peripherals.SPI2, 100u32.kHz(), hal::spi::SpiMode::Mode0, &clocks)
.with_sck(io.pins.gpio18)
.with_mosi(io.pins.gpio23);
let display_pin = hal::gpio::Output::new(io.pins.gpio5, hal::gpio::Level::Low);
let mut spi_dev = embedded_hal_bus::spi::ExclusiveDevice::new_no_delay(spi, display_pin).unwrap();
let busy_pin = hal::gpio::Input::new(io.pins.gpio4, hal::gpio::Pull::None);
let dc_pin = hal::gpio::Output::new(io.pins.gpio17, hal::gpio::Level::High);
let rst_pin = hal::gpio::Output::new(io.pins.gpio16, hal::gpio::Level::High);
let mut epaper_delay = embassy_time::Delay{};
static SPI_DEV: static_cell::StaticCell<embedded_hal_bus::spi::ExclusiveDevice<
hal::spi::master::Spi<
'static,
hal::peripherals::SPI2,
hal::spi::FullDuplexMode,
>,
hal::gpio::Output<
'static,
hal::gpio::GpioPin<5>
>,
embedded_hal_bus::spi::NoDelay,
>> = static_cell::StaticCell::new();
let spi_dev = SPI_DEV.init_with(|| spi_dev);
static EXECUTOR_RAW: static_cell::StaticCell<esp_hal_embassy::Executor> = static_cell::StaticCell::new();
let executor = EXECUTOR_RAW.init_with(|| esp_hal_embassy::Executor::new());
executor.run(|spawner| {
spawner.spawn(async_main(
spi_dev,
busy_pin,
dc_pin,
rst_pin,
epaper_delay
));
});
#[allow(unreachable_code)]
loop {}
}
#[embassy_executor::task]
async fn async_main (
spi: &'static mut embedded_hal_bus::spi::ExclusiveDevice<
hal::spi::master::Spi<
'static,
hal::peripherals::SPI2,
// hal::dma::Spi2DmaChannel,
hal::spi::FullDuplexMode,
// hal::Async
>,
hal::gpio::Output<
'static,
hal::gpio::GpioPin<5>
>,
embedded_hal_bus::spi::NoDelay,
>,
mut busy: hal::gpio::Input<'static, hal::gpio::Gpio4>,
mut dc: hal::gpio::Output<'static, hal::gpio::Gpio17>,
mut rst: hal::gpio::Output<'static, hal::gpio::Gpio16>,
mut delay: embassy_time::Delay
) {
log::info!("Start");
log::info!("Reset");
rst.set_high();
delay.delay_ms(10).await;
rst.set_low();
delay.delay_ms(10).await;
rst.set_high();
delay.delay_ms(200).await;
log::info!("Wait Idle");
while busy.is_high() {
delay.delay_ms(10).await;
}
log::info!("SPI DC");
dc.set_low();
log::info!("SPI Write");
log::info!("{:?}", spi.write(&[18]));
log::info!("SPI_WRITE_DONE");
} Deps:
|
Ok took a while to get your example to compile. It works for me on ESP32-C3 but I can confirm I see it waiting forever on ESP32 |
write
Sorry for that, is there anything I can do to simplify it for repro? |
No problem! Here is a minimal repro (just change the //! Embassy SPI
//!
//! Folowing pins are used:
//! SCLK GPIO0
//! MISO GPIO2
//! MOSI GPIO4
//! CS GPIO5
//!
//! Depending on your target and the board you are using you have to change the
//! pins.
//!
//! Connect MISO and MOSI pins to see the outgoing data is read as incoming
//! data.
//!
//! This is an example of running the embassy executor with SPI.
//% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3
//% FEATURES: async embassy embassy-time-timg0 embassy-generic-timers
#![no_std]
#![no_main]
use embassy_executor::Spawner;
use embassy_time::{Duration, Timer};
use esp_backtrace as _;
use esp_hal::{
clock::ClockControl,
dma::*,
dma_descriptors,
gpio::Io,
peripherals::Peripherals,
prelude::*,
spi::{
master::{prelude::*, Spi},
SpiMode,
},
system::SystemControl,
timer::timg::TimerGroup,
};
#[main]
async fn main(_spawner: Spawner) {
esp_println::println!("Init!");
let peripherals = Peripherals::take();
let system = SystemControl::new(peripherals.SYSTEM);
let clocks = ClockControl::boot_defaults(system.clock_control).freeze();
let timg0 = TimerGroup::new_async(peripherals.TIMG0, &clocks);
esp_hal_embassy::init(&clocks, timg0);
let io = Io::new(peripherals.GPIO, peripherals.IO_MUX);
let sclk = io.pins.gpio0;
let miso = io.pins.gpio2;
let mosi = io.pins.gpio4;
let cs = io.pins.gpio5;
let dma = Dma::new(peripherals.DMA);
#[cfg(any(feature = "esp32", feature = "esp32s2"))]
let dma_channel = dma.spi2channel;
#[cfg(not(any(feature = "esp32", feature = "esp32s2")))]
let dma_channel = dma.channel0;
let (descriptors, rx_descriptors) = dma_descriptors!(32000);
let mut spi = Spi::new(peripherals.SPI2, 100.kHz(), SpiMode::Mode0, &clocks)
.with_pins(Some(sclk), Some(mosi), Some(miso), Some(cs))
.with_dma(
dma_channel.configure_for_async(false, DmaPriority::Priority0),
descriptors,
rx_descriptors,
);
let send_buffer = [0; 5];
loop {
esp_println::println!("Sending bytes");
embedded_hal_async::spi::SpiBus::write(&mut spi, &send_buffer)
.await
.unwrap();
esp_println::println!("Done");
Timer::after(Duration::from_millis(1_000)).await;
}
} Very small writes (< 17 in my tests) will make ESP32 never finish awaiting the write. It works on e.g. ESP32-C3 |
Hi,
I wanted to use
hal::spi::master::Spi
as anembedded_hal_async::spi::SpiBus
.Given the
asynch
module is not public I don't see any way to do that.Is this hidden by accident, or am I missing something?
The text was updated successfully, but these errors were encountered: