Replies: 1 comment
-
I tried to write a quick proof of the concept and it seems to work fine. Sorry for the horrible code but manually implementing futures is something I've never done before and I also have no idea how you would work with the references to registers/peripherals and their lifetimes. struct TxFuture<'d, 'f> {
regs: &'d RegisterBlock,
twai: &'d PeripheralRef<'d, AnyTwai>,
frame: &'f EspTwaiFrame,
wait_tx_done: bool,
}
impl core::future::Future for TxFuture<'_, '_> {
type Output = Result<(), EspTwaiError>;
fn poll(
mut self: core::pin::Pin<&mut Self>,
cx: &mut core::task::Context<'_>,
) -> Poll<Self::Output> {
self.twai.async_state().tx_waker.register(cx.waker());
let status = self.regs.status().read();
if status.bus_off_st().bit_is_set() {
return Poll::Ready(Err(EspTwaiError::BusOff));
}
// Check that the peripheral is not already transmitting a packet.
if !status.tx_buf_st().bit_is_set() {
return Poll::Pending;
}
// if we not have requested the transmission yet we do that now.
if !self.wait_tx_done {
write_frame(self.regs, self.frame);
self.wait_tx_done = true;
return Poll::Pending;
}
// tx buf is free again, so we are finished
return Poll::Ready(Ok(()));
}
}
impl<'d, 'f> Drop for TxFuture<'d, 'f> {
fn drop(&mut self) {
self.regs.cmd().write(|w| w.abort_tx().set_bit());
}
}
impl TwaiTx<'_, Async> {
/// Transmits an `EspTwaiFrame` asynchronously over the TWAI bus.
pub async fn transmit_async(&mut self, frame: &EspTwaiFrame) -> Result<(), EspTwaiError> {
self.twai.enable_interrupts();
TxFuture {
regs: self.regs(),
twai: &self.twai,
frame,
wait_tx_done: false,
}
.await
}
} |
Beta Was this translation helpful? Give feedback.
0 replies
# for free
to join this conversation on GitHub.
Already have an account?
# to comment
-
I'm currently working on implementing ISO-TP (ISO 15765-2, a transport protocol to transfer messages greater than a single CAN frame) for the ESP32 TWAI interfaces. The specification requires you to ensure timeouts that start with the successful transmission of a CAN frame. One example is the separation time (ST) between consecutive frames. You have to start the transmission of the next CAN frame with the given separation time after the previous frame has been transmitted.
The current asynchronous TWAI driver registers its waker for the TX interrupt. After that it checks, if the TX buffer of the TWAI peripheral is already filled from a previous call. If that's the case, the future is polled again as soon as the TX interrupt fired. We now can fill the TX buffer from this future and return Poll::Ready as the peripheral now takes care of the transmission (earlier or later depending on for example the bus load and lost arbitration).
This means we can measure the time starting with submitting the frame to the TWAI peripheral. We can not measure the time starting after the frame has been successfully sent. Maybe the picture down below helps understanding the problem.
That's why I have the idea to add/change the asynchronous TWAI driver with which we wait until the transmission was successful. In addition, if we stop polling the future because we were not able to send the frame in time, the transmission is automatically canceled on drop (writing the TWAI_ABORT_TX bit in the control register).
So the overall flow would be:
Does anyone else need this kind of fine control over the transmission of CAN frames, and if so, does this sound reasonable to you?
Beta Was this translation helpful? Give feedback.
All reactions