async-stm32f1xx icon indicating copy to clipboard operation
async-stm32f1xx copied to clipboard

The DMA interrupts is not enough for handling serial tx/rx correctly.

Open katyo opened this issue 5 years ago • 3 comments

As I see you only use DMA interrupts to control serial transfer. This is not enough for correct operation.

In case of receiving we should at least handle an idle interrupt (IDLE). The idle event will triggers at the moment when no more transfer happens on rx line (IDLE frame detected by receiver). This is important especially in case of receiving packets of variable length or when length is not known. Currently we have potentially infinite await until rx DMA buffer is (half) fullfilled, which is wrong. Also it would be good catch an errors (parity errors, break frame, etc).

In case of transmitting we should await until transmission physically completed before returning Poll::Ready. Usually it happens after tx DMA buffer already is empty and when transfer complete (TC) event is triggered by transmitter, so this is last TC event for this transfer. This important particularly in case on simplex or half-duplex transfer when we need await until transmission actually complete to switch line to receive data.

katyo avatar Jul 21 '20 04:07 katyo

First off, thanks for your interest in this project and sorry for the late reply! (I am currently quite occupied with my thesis on this project)

In case of transmitting we should await until transmission physically completed before returning Poll::Ready.

So, you are saying that physical completion is different from Transfer::is_done? To me, this looks as it would be equivalent.

I agree with you in general: handling idle frames is important for some communication. Do you have any proposal on how to implement this? As far as I know, there is no such abstraction in stm32f1xx-hal yet, so we would need to use stm32f1 manually, if we wanted to support idle frames. async-stm32f1xx was created to thinly wrap and integrate the HAL peripherals into the futures ecosystem. As those peripherals don't support idle frames as far as I know this task does not seem straight forward.

mkroening avatar Jul 26 '20 18:07 mkroening

I mean that DMA transfer complete is not a same as USART transfer complete.

First TC appears when physical DMA controller sent last word of data to IO device (i.e. TX buffer already is empty). Second TC means whan IO device (USART transmitter in our case) actually sent last word of data and stops transfer. The delay between first and second events for serial transfer depends from transfer speed and usually can take significant time comparing to MCU core speed. As I say before this is quite important thing in case of half-duplex and simplex serial IO when you cannot start receiving until transmission is done.

Of course, I agree that stm32f1xx-hal is far from completion and some frequently used in practice features is not implemented for now.

katyo avatar Jul 27 '20 05:07 katyo

@katyo You brought up a good point that I hadn't thought of. For example, in my use case I have the following:

self.en.set_high();
self.serial.write(buf);
self.en.set_low();

The GPIO controls an rs485 transceiver; I suppose in this case that set_low() could cut off the transmission before I have finished. As for implementation, there is a DMA interrupt (DMAX_STREAMX) and a shared interrupt (USARTX) that can fire for Txe, Rxne, and Idle events. My idea of implementation is the following:

  • The DMA interrupt fires; the Txe interrupt is set.
  • The Txe interrupt is set. Poll::Ready() is returned

For receive, the idle interrupt could be used.

xoviat avatar Dec 31 '20 03:12 xoviat