Skip to content
Open
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
2 changes: 1 addition & 1 deletion embassy-stm32/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
<!-- next-header -->
## Unreleased - ReleaseDate

- fix: Fixed STM32H5 builds requiring time feature
- feat: Add blocking read/write methods for STM32 CAN (bxCAN and FDCAN)

## 0.4.0 - 2025-08-26

Expand Down
96 changes: 96 additions & 0 deletions embassy-stm32/src/can/bxcan/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,17 @@ impl<'d> Can<'d> {
}
}

/// Blocking version of enable. Enables the peripheral and synchronizes with the bus.
///
/// This will wait for 11 consecutive recessive bits (bus idle state).
/// In loopback or internal modes, this typically completes very quickly.
pub fn blocking_enable(&mut self) {
while self.info.regs.enable_non_blocking().is_err() {
// CAN initialization is usually quick, especially in loopback mode
// Simple busy wait is acceptable for this one-time initialization
}
}

/// Enables or disables the peripheral from automatically wakeup when a SOF is detected on the bus
/// while the peripheral is in sleep mode
pub fn set_automatic_wakeup(&mut self, enabled: bool) {
Expand Down Expand Up @@ -343,6 +354,18 @@ impl<'d> Can<'d> {
self.split().0.write(frame).await
}

/// Blocking write frame.
///
/// If the TX queue is full, this will wait until there is space.
pub fn blocking_write(&mut self, frame: &Frame) -> TransmitStatus {
loop {
match self.try_write(frame) {
Ok(status) => return status,
Err(TryWriteError::Full) => continue,
}
}
}

/// Attempts to transmit a frame without blocking.
///
/// Returns [Err(TryWriteError::Full)] if the frame can not be queued for transmission now.
Expand Down Expand Up @@ -416,6 +439,19 @@ impl<'d> Can<'d> {
RxMode::read(&self.info).await
}

/// Blocking read frame.
///
/// If no CAN frame is in the RX buffer, this will wait until there is one.
pub fn blocking_read(&mut self) -> Result<Envelope, BusError> {
loop {
match self.try_read() {
Ok(envelope) => return Ok(envelope),
Err(TryReadError::Empty) => continue,
Err(TryReadError::BusError(e)) => return Err(e),
}
}
}

/// Attempts to read a CAN frame without blocking.
///
/// Returns [Err(TryReadError::Empty)] if there are no frames in the rx queue.
Expand Down Expand Up @@ -480,6 +516,11 @@ impl<'d, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCan<'d, TX_
self.tx.write(frame).await
}

/// Blocking write frame to TX buffer.
pub fn blocking_write(&mut self, frame: &Frame) {
self.tx.blocking_write(frame)
}

/// Returns a sender that can be used for sending CAN frames.
pub fn writer(&self) -> BufferedCanSender {
self.tx.writer()
Expand All @@ -490,6 +531,11 @@ impl<'d, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCan<'d, TX_
self.rx.read().await
}

/// Blocking read frame from RX buffer.
pub fn blocking_read(&mut self) -> Result<Envelope, BusError> {
self.rx.blocking_read()
}

/// Attempts to read a CAN frame without blocking.
///
/// Returns [Err(TryReadError::Empty)] if there are no frames in the rx queue.
Expand Down Expand Up @@ -540,6 +586,18 @@ impl<'d> CanTx<'d> {
.await
}

/// Blocking write frame.
///
/// If the TX queue is full, this will wait until there is space.
pub fn blocking_write(&mut self, frame: &Frame) -> TransmitStatus {
loop {
match self.try_write(frame) {
Ok(status) => return status,
Err(TryWriteError::Full) => continue,
}
}
}

/// Attempts to transmit a frame without blocking.
///
/// Returns [Err(TryWriteError::Full)] if the frame can not be queued for transmission now.
Expand Down Expand Up @@ -690,6 +748,20 @@ impl<'d, const TX_BUF_SIZE: usize> BufferedCanTx<'d, TX_BUF_SIZE> {
waker(); // Wake for Tx
}

/// Blocking write frame to TX buffer.
pub fn blocking_write(&mut self, frame: &Frame) {
loop {
match self.tx_buf.try_send(*frame) {
Ok(()) => {
let waker = self.info.tx_waker;
waker();
return;
}
Err(_) => continue, // Channel full, continue waiting
}
}
}

/// Returns a sender that can be used for sending CAN frames.
pub fn writer(&self) -> BufferedCanSender {
BufferedCanSender {
Expand All @@ -716,6 +788,19 @@ impl<'d> CanRx<'d> {
RxMode::read(&self.info).await
}

/// Blocking read frame.
///
/// If no CAN frame is in the RX buffer, this will wait until there is one.
pub fn blocking_read(&mut self) -> Result<Envelope, BusError> {
loop {
match self.try_read() {
Ok(envelope) => return Ok(envelope),
Err(TryReadError::Empty) => continue,
Err(TryReadError::BusError(e)) => return Err(e),
}
}
}

/// Attempts to read a CAN frame without blocking.
///
/// Returns [Err(TryReadError::Empty)] if there are no frames in the rx queue.
Expand Down Expand Up @@ -783,6 +868,17 @@ impl<'d, const RX_BUF_SIZE: usize> BufferedCanRx<'d, RX_BUF_SIZE> {
self.rx_buf.receive().await
}

/// Blocking read frame from RX buffer.
pub fn blocking_read(&mut self) -> Result<Envelope, BusError> {
loop {
match self.try_read() {
Ok(envelope) => return Ok(envelope),
Err(TryReadError::Empty) => continue,
Err(TryReadError::BusError(e)) => return Err(e),
}
}
}

/// Attempts to read a CAN frame without blocking.
///
/// Returns [Err(TryReadError::Empty)] if there are no frames in the rx queue.
Expand Down
Loading