embassy-rs / embassy Goto Github PK
View Code? Open in Web Editor NEWModern embedded framework, using Rust and async.
Home Page: https://embassy.dev
License: Apache License 2.0
Modern embedded framework, using Rust and async.
Home Page: https://embassy.dev
License: Apache License 2.0
It would be convenient if peripherals supported going into low power mode (disconnecting GPIO pins, disabling SPI, etc.). I'm not sure if this should live in embassy or in user code. It can be a bit fiddly to write because you need a way to represent the different states, which gets complicated with the typed state you get on Rust (a disconnected pin is not the same type as an input or output pin, even though in reality it is zero-sized and doesn't even exist in machine code).
I'm extensively using I2C in my project together with embassy
and it would be nice to have an I2C interface trait to implement for a particular chip (stm32wb55 in my case).
Affected examples:
gpiote_port
gpiote
qspi
uart
Compiling embassy-examples v0.1.0 (C:\Users\user\Desktop\code\rust\embassy\examples)
Finished dev [optimized + debuginfo] target(s) in 0.75s
Running `probe-run --chip nRF52840_xxAA --defmt C:\Users\user\Desktop\code\rust\embassy\target\thumbv7em-none-eabihf\debug\gpiote_port`
(HOST) INFO flashing program (18.48 KiB)
(HOST) INFO success!
────────────────────────────────────────────────────────────────────────────────
0.000000 INFO Hello World!
└─ gpiote_port::__cortex_m_rt_main @ src\bin\gpiote_port.rs:51
0.000001 ERROR panicked at 'No clock set'
└─ embassy::time::now @ C:\Users\user\Desktop\code\rust\embassy\embassy\src\time\mod.rs:22
I assume each embassy hal should implement every trait?
All the default functions in OwnedInterrupt (set_handler, remove_handler, enable, disable...) can currently be overridden by a OwnedInterrupt implementation, we don't want this.
static UART: Once<Mutex<Uarte<pac::UARTE0>>> = Once::new();
#[thread::task(pool_size = 20)]
async fn serial_send(buf: Vec<u8>) {
let uart = UART.get().unwrap();
let _err = uart.lock().send(&buf).await;
}
#[thread::task]
async fn serial_recv() {
let mut buf = [0; 1];
loop {
let uart = UART.get().unwrap();
let _err = uart.lock().receive(&mut buf).await;
}
}
fn send(buf: &[u8]) {
thread::spawn(serial_send(Vec::from(buf)));
}
pub fn start(uart: Uarte<pac::UARTE0>) {
let _err = UART.call_once(|| Mutex::new(uart));
send(&[1,2,3,4,5]);
thread::spawn(serial_recv());
}
Pattern: accepts an OwnedInterrupt and sets the handler to one of many static handlers defined in the signal module. Poll_wait stores waker.clone()
in a static array (using a malloc/free scheme to keep track of available handlers/wakers). Interrupt handler disables interrupt and wakes waker.
This scheme is resistant to memory moves, and solves headaches from dealing with interrupts.
In the following example "after write" is printed but "after drop" is never reached.
All the read methods also block forever but while that could be an issue on my end, blocking forever on drop seems very unexpected so I raised this issue.
#[embassy::main]
async fn main(_spawner: Spawner) {
let mut p = unsafe { Peripherals::steal() };
let mut irq = interrupt::take!(UARTE0_UART0);
loop {
let mut tx_buffer = [0u8; 4096];
let mut rx_buffer = [0u8; 4096];
{
let mut config = uarte::Config::default();
config.parity = uarte::Parity::EXCLUDED;
config.baudrate = uarte::Baudrate::BAUD115200;
let uart = unsafe {
BufferedUarte::new(
&mut p.UARTE0,
&mut p.TIMER0,
&mut p.PPI_CH0,
&mut p.PPI_CH1,
&mut irq,
&mut p.P1_05,
&mut p.P1_04,
&mut p.P1_07,
&mut p.P1_06,
config,
&mut rx_buffer,
&mut tx_buffer,
)
};
info!("uart initialized!");
pin_mut!(uart);
Timer::after(Duration::from_millis(1000)).await;
info!("before write");
uart.write_all(b"AT\r\n").await.unwrap();
info!("after write");
}
info!("after drop");
}
}
We want embassy drivers to be usable with both the async embassy-traits
and the blocking embedded_hal::blocking
traits.
Reasons:
Would it be acceptable if I created a new embassy-stm32f4xx folder and submitted a pull request?
Since introducing the ctx
pointer, the handler is now two words, so setting it can race with the interrupt firing! On race it's possible for the new handler to be called with the old ctx pointer or viceversa.
Since this is very low level, I'd rather have the user enable/disable the interrupt if this is a concern, instead of doing a critical section in set_handler/remove_handler.
Currently, interrupts are structured such that they are owned by one trait. However, it may be the case that interrupts are used by multiple traits independently, and the current project structure exposes interrupts to the user. I propose the following changes:
Thoughts?
Many drivers have new
be unsafe to have the user pledge that they won't leak any futures, so we can use DMA with borrowed slices which is much more ergonomic than embedded_dma
.
We should add an in-depth explanation on why that is and what are the implications somewhere (readme, main module) and link to it from all the affected new
s.
The code transmutes the EXTI
singleton struct out of nothing and passes it to code that expects an unique borrow, which might cause problems since the code does RMWs:
A no_std version of tokio mutex, async-std mutex. Similar to this mutex.
I had some trouble with an earlier version of embassy because I hit a data race in the way that the interrupts was set to trigger. That issues is fixed now, AFAICT when I look at the code, but I think someone should do a detailed audit of the logic at some point. It's the kind of thing where you can't just wait for bug reports, because if it does cause problems they will be very rare and difficult to reproduce, since they rely on timing.
This isn't a criticism of the code, which looks good, but just a note that an audit would be worthwhile sometime in the future when the codebase has stabilized a bit.
I'm working on an embedded project that has an stm32g0x1 microcontroller which needs to act as an i2c peripheral (aka slave) on one bus and an i2c controller (aka master) on another bus. It also needs to be a peripheral on a SPI bus.
Do you have any thoughts on how this could / should be implemented in Embassy? Assuming I end up using Embassy, I'm happy to do most of the implementation of this myself, but any guidance would be greatly appreciated.
It's using the UnsafeCell to get multiple &mut
s to the content simultaneously
Document the methods on AsyncBufReadExt
In particular I need a method that will fill the provided buffer with as much data is available but not wait for further data to arrive.
It's unsafe because the user must not drop the futures.
As the title says, when using embassy_nrf::Gpiote together with nrf-softdevice, the nrf-softdevice (S140 7.2.0)
an explicit panic occurs, in this case with
0.001253 ERROR panicked at 'fault_handler 4097 191576 0'
I still have to investigate this further (and throw together a minimal example), but I assume it has something to do with NVIC access or critical sections.
If someone could point out where to look further it would be much appreciated
Edit:
I created a button handler task which awaits a pin using GPIOTE port interrupt, then polls the desired pin every 5ms (timer provided by embassy::time) to debounce
Currently the trait is called 'read'; it should be more descriptive.
many OwnedInterrupt methods should have compiler fences, so for example enabling/disabling is guaranteed to be ordered vs other memory accesses.
every task would allocate a "waitqueue node" in their own future, you link them together as a linked list
with Pin you're guaranteed that these nodes don't move and that Drop is called, so it's sound
it's how futures-intrusive works internally
but it'd need lots and lots of unsafe
and care so codesize doesn't blow up :S
futures-intrusive is a bit heavy on code size, I think it can be improved somewhat
I originally thought that the qei trait would be useful, but I now think that polling with delays is a better approach. The main problem with the qei trait is that you can lose time (ticks) when using the interrupt approach. I will leave this open for a few days and remove it if no one objects.
Finished dev [optimized + debuginfo] target(s) in 1.27s
Running `probe-run --chip nRF52810_xxAA C:\Users\XXX\embassy\target\thumbv7em-none-eabi\debug\rtc_async`
(HOST) INFO flashing program (19.04 KiB)
(HOST) INFO success!
────────────────────────────────────────────────────────────────────────────────
0.000000 INFO Hello World!
└─ rtc_async::__cortex_m_rt_main @ src\bin\rtc_async.rs:41
0.000001 INFO tick
└─ rtc_async::run2::task::{{closure}} @ src\bin\rtc_async.rs:30
0.000002 INFO BIG INFREQUENT TICK
└─ rtc_async::run1::task::{{closure}} @ src\bin\rtc_async.rs:22
Stuck at this forever.
The async_raw
example runs as expected, so the bug might not actually be in the RTC code.
Similar to AnyPin from the nrf and rp HALs. You'd call .degrade() on a typed interrupt and get an AnyInterrupt.
Together with AnyPin and type-erased AnyUarte/AnySpim/etc peripherals you could reduce code duplication due to generics.
for example:
#[thread::task]
async fn run() {
info!("hello");
thread::spawn(run());
}
The current Rand trait is completely out of scope, it's not even async.
Maybe in the future we can have an async Rand trait, but for now the demand doesn't seem to be there.
Maybe rename OwnedInterrupt
-> Interrupt
and FOOInterrupt
-> FOO
.
This would make embassy's interrupt
mod incompatible with the PACs', but much more clearer. Right now all the "old-style" enum interrupt and the "embassy-style" owned interrupt stuff is mixed.
The current plan is this:
I would like to port my (ls7366)[https://github.com/theunkn0wn1/LS7366_rust/] driver to embassy, but SPI is not implemented for STM32.
Context: #91 (comment) (item 1)
MCU: stm32f446
on a nucleo board.
I attached a logic analyzer as well as a FTDI to the TX/RX of Uart4 to observe the results of the test.
the software being run is here
Logic analyzer / remote end receives bytes [0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0x1, 0xf0, ...]
In [19]: con.read_all()
Out[19]: b'\x00\xff\x00\xff\x00\x00\xff\x00\xff\x06\x00\x00\xff\x00\xff\x00\xff\x00\xff\x00\x00\xff\x00\xff\x00\xff\x00\xff\x00\x00\xff\x00\x00\x00\xff\x00\x00\xff\x00\xff\x00\xff\x00\xff\x00\xff\x00\xff\x00\xff\x00\xff\x00\xff'
I finally got around to testing on my device, but received a panic:
panic_halt::panic@0x0800b392 (~\.cargo\registry\src\github.com-1ecc6299db9ec823\panic-halt-0.2.0\src\lib.rs:33)
core::panicking::panic_fmt@0x0800a99e (Unknown Source:92)
embassy::time::now@0x0800a390 (~\.cargo\git\checkouts\embassy-52086c5c548bb7b2\c6cf9b8\embassy\src\time\mod.rs:22)
embassy::time::instant::Instant::now@0x08009ab6 (~\.cargo\git\checkouts\embassy-52086c5c548bb7b2\c6cf9b8\embassy\src\time\instant.rs:19)
embassy::executor::Executor::run@0x08009d66 (~\.cargo\git\checkouts\embassy-52086c5c548bb7b2\c6cf9b8\embassy\src\executor\mod.rs:217)
rust_grinder::__cortex_m_rt_main@0x08002292 (~\workspace\tproject\src\main.rs:111)
rust_grinder::__cortex_m_rt_main_trampoline@0x08002212 (~\workspace\tproject\src\main.rs:102)
Is it possible to use embassy without rtc? The Qspi example does not have it enabled. stm32 has it, but I haven't used it yet, so it would take me some time to enable.
I'm not 100% sure whether the executor is safe wrt Send/non-Send futures.
Ideally there should be two APIs, one to spawn non-Send futures, and another one to spawn Send futures. Only the second one should be usable cross-thread (where in embedded context "thread" means "interrupt priority level)
So I've just tried the new updates and found no way to link up timer compare events to PPI, as ppi::Event
is not implemented for the timer events and Event::from_reg
is private.
Will Event::from_reg
eventually become public or be implemented for the rest of the peripherals?
For now I will just make from_reg
public and use it like in BufferedUarte, but I might be missing something
Need an embedded verison: https://github.com/smol-rs/async-channel
To ease adoption of async and reduce boilerplate for simple projects, we could have a macro that allows you to simply have an async main. No need for creating executor, setting up the clock and alarm, the macro does it for you with some sensible, opinionated config. tokio and async-std have similar macros.
This'd be optional of course, you'd still be able to set up executor(s) manually.
The main issue is how to do clock setup in a way that works with all boards (especially on stm32s) or how to let the user specify it. It needs to be done before executor start.
#[embassy::main]
async fn main(spawner: Spawner) {
// here you can do async stuff directly.
// if you need more tasks you can spawn
spawner.spawn(foo()).unwrap()
}
Up to 15 pins can be waited on concurrently for stm32. The HAL should allow this capability.
Use of borrowed buffers is unsafe.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.