embassy-rs/embassy · error
Event is already in use
Error message
Event is already in use
What it means
new_many_to_many panics when an event's PUBLISH register already holds a non-zero value, meaning another PPI/DPPI channel already publishes that event. This prevents silently rewiring hardware event routing. The driver volatile-reads the PUBLISH register before writing the new channel configuration.
Solutions
- Drop or disable the existing PPI channel that publishes the same event before creating the new one.
- Check for duplicate events in the events array and across existing channel configurations.
- If one event must trigger multiple tasks, list multiple tasks on a single channel instead of creating parallel channels for the same event.
- After a soft reset, explicitly clear PUBLISH registers before reconfiguration.
Example fix
// before let c1 = ManyToMany::new_many_to_many(p.PPI_CH0, [event], [task_a]); let c2 = ManyToMany::new_many_to_many(p.PPI_CH1, [event], [task_b]); // panics // after let c1 = ManyToMany::new_many_to_many(p.PPI_CH0, [event], [task_a, task_b]);
Defensive patterns
Strategy: validation
Validate before calling
// Check the event's PUBLISH register before configuring a channel.
let pub_reg = event.publish_reg().read_volatile();
if pub_reg != 0 {
// event already published by another channel
} Prevention
- Design event routing so each event belongs to exactly one channel; fan out via multiple tasks on one channel.
- Centralize PPI setup in a single initialization function.
- Clear PUBLISH/SUBSCRIBE registers after soft resets before re-init.
When it happens
Trigger: Calling dppi::ManyToMany::new_many_to_many(ch, events, tasks) with an Event whose PUBLISH register is non-zero — e.g. the event was already published by a previously created channel (new_one_to_one/new_many_to_many) or written manually, and that channel has not been disabled or dropped.
Common situations: Two radio peripherals (e.g. TIMER compare and radio READY) feeding two different channels; routing the same GPIOTE event to multiple sink tasks through separate channels; re-running initialization code after a state reset without clearing PUBLISH registers.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Task is already in use
- Boot prepare error
- unwrap of ` ` failed
- unwrap of ` ` failed
- Bad 802.15.4 channel
AI-assisted analysis of embassy-rs/embassy@463a07b963 (2026-09-10).
Data as JSON: /api/errors/753efc255f9f90b6.
Report an issue: GitHub.
Appendix: source
Thrown at embassy-nrf/src/ppi/dppi.rs:40
Ppi::new_many_to_many(ch, [event], [task1, task2])
}
}
impl<'d, C: ConfigurableChannel, const EVENT_COUNT: usize, const TASK_COUNT: usize>
Ppi<'d, C, EVENT_COUNT, TASK_COUNT>
{
/// Configure a DPPI channel to trigger all `tasks` when any of the `events` fires.
pub fn new_many_to_many(ch: Peri<'d, C>, events: [Event<'d>; EVENT_COUNT], tasks: [Task<'d>; TASK_COUNT]) -> Self {
let val = DPPI_ENABLE_BIT | (ch.number() as u32 & DPPI_CHANNEL_MASK);
for task in tasks {
if unsafe { task.subscribe_reg().read_volatile() } != 0 {
panic!("Task is already in use");
}
unsafe { task.subscribe_reg().write_volatile(val) }
}
for event in events {
if unsafe { event.publish_reg().read_volatile() } != 0 {
panic!("Event is already in use");
}
unsafe { event.publish_reg().write_volatile(val) }
}
Self { ch, events, tasks }
}
}
impl<'d, C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize> Ppi<'d, C, EVENT_COUNT, TASK_COUNT> {
/// Enables the channel.
pub fn enable(&mut self) {
let n = self.ch.number();
self.ch.regs().chenset().write(|w| w.0 = 1 << n);
}
/// Disables the channel.
pub fn disable(&mut self) {
let n = self.ch.number();View on GitHub (pinned to 463a07b963)