[En-Nut-Discussion] Questions regarding UART implementation

uprinz2 at netscape.net uprinz2 at netscape.net
Tue Sep 28 18:44:50 CEST 2010

Hi Thiago,

thanks for the infos. I thought that this would happen :) Lots of nice feature switches and none implemented. 
I agree to you, that sometimes it makes sense to determine the old lines and to abstract them. It's a good idea and I try to support it.

For the blockread / blockwrite functions I found a missleading description. So should there be some totally different things:
USART_BLOCKWRITE should control block transfer for writing.
USART_BLOCKREAD should control block transfer for reading.
USART_BLOCKING should control if the calling thread is blocked until transceiving is done.
I think there is an option ASYNC too which I would call the one that controls if a thread is blocked or not on calling a usart transfer (read or write).
But that is not working or not implemented.

For all those functions I miss something too: If you use transfers async, you will not get an reply on the read/write that is valid as the transfer is not finished at that time. So _ioctl needs another option too. Besides getting the information about the errors from the last transfer one needs to get the status of the current transfer, i.e. the number of bytes trasnferred and the status if the transfer is ongoing or whatever reason aborted.

So what we have is a usart that relies on ringbuffer even the ringbuffer struct supplies blockptr / blockcntr.
If you use packet oriented data, you cannot handle timeouts on packets cause the timeouts are based on the ringbuffer and, if the ringbuffer is to small two locks block your thread no, better, something blocks you that you cannot determine.

If you have a function that allocated memory to form a block you don't need to copy it to the ringbuffer for transfer but the actual implemenation does.

On smaller systems that need packet oriented communication ( block transfer) the ringbuffer memory could bee freed completely.

On bigger systems with DMA/PDC support, you save a lot of CPU time for all those TXE Interrupts that do not appear.

Unfortunately I cannot implement DMA in the actual structure as DMA should lock the calling thread until transfer is over or set a signal after finishing the transfer.
I tried to do that by using the normal StartTx(void) function that will rise an TXE Interrupt and this first TxReadyIrq( RINGBUF *rbf) will setup the DMA process.
Unfortunately this function is out of thread as it is an interrupt and therefore cannot set a NutEventWait that blocks the calling thread.

So here's what I am thinking about:
Assume that all functions for the USARTs get the USARTDCB as an argument:
- You can implement block-control with DMA in StartTx on CPUs that support that feature.
- You can implement block transfer by saving the ringbuffer (Data is taken from the calling functions buffer pointer)
- You can write totally different usart drivers for totally different architectures by keeping the Nut/OS usual function calls.

In my STM32 implementation I fear that if I can call one set of functions from all usart interrupts the code in flash will be much lower even I implement and enable all features.
All features mean: HW/SW-Handshake, DTR/DSR support, XON/XOFF, STX//ETX, Full-/Half-Duplex, IRDA, ...

The backdraw of this change would be that all architectures have to be modified to pass DEVUSART *dev or at least USARTDCB *dcb to all functions.
That would lead to one small problem, any function accessing the ringbuffer needs to derive it from the dcb.
For a 72MHz STM32 it's not a problem to do RINGBUF *rbf = dcb->dcb_rbf; at every function start. But how is that on an AVR?

Ah, by the way. I am thinking about making the things a bit comfortable. So one could set "Use Interrupts" and "Use DMA" independantly for every USART in a system. 
If it stays like it is, so usart1.c includes usart.c this saves some flash if the user unchecks the one or the other option.
If there is only one usart.c calld by the interrupts of usartx.c it could be an idea to include portions of the code only if at least one of the usarts has enabled that option.
So DMA handling in the general driver is only enabled and compiled if at least one usart has the option set in nutconf.

So now I have three options:
1 Modify usart.c / usart.h / uart.h to the new structure and hope that someone is helping me to bull AVR and ARM architecture to that level.
2 Just split usart.h / uart.h into stm32_usart.h and other_usart.h while usart.h includes the one or the other depending on the architecture selected.
3 Leave it as it is and forget about that all :)

By the way, Option 2 is what I did for TWI cause STM32 has two interfaces and 4 interrupt providers ( two per interface) that call the same code existing only once.
Old Tw*() functions are #defined to the stm32 specific functions. Works fine here :)

Best regards

-----Original Message-----
From: Thiago A. Corrêa <thiago.correa at gmail.com>
To: Ethernut User Chat (English) <en-nut-discussion at egnite.de>
Sent: Tue, Sep 28, 2010 4:57 pm
Subject: Re: [En-Nut-Discussion] Questions regarding UART implementation

Hi Ulrich,

    Sorry for taking so long to reply. In my application we use a
couple ioctl's for the UART, like GETSTATUS, SETSTATUS, SETSPEED,
etc... But from those listed, we only use this one:

> /*! \brief UART _ioctl() command code to set the flow control mode.
>  *
>  * The configuration parameter specifies the flow control mode.
>  */
> #define UART_SETFLOWCONTROL     0x0111
> This option definately needs some more explanation about the parameters,
> as flow control can be...
> Hardware: RTS/CTS and DSR/DTR and may be DCD
> Software: XON/XOFF and STX/ETX

We are using the atmega128 on this one, so there is only XON/XOFF and
RTS/CTS. We use as arguments the USART_MF_* macros like this:

  // Use No Flow Control
  _ioctl( _fileno( data->serialPort ), UART_GETFLOWCONTROL, &temp );
  temp &= ~USART_MF_XONXOFF;
  _ioctl( _fileno( data->serialPort ), UART_SETFLOWCONTROL, &temp );

The DTR can be controled by an ioctl command, but it toggles like a
GPIO, it's never used for actual flow control. The difference here is
that I can write a serial handling routine that can toggle the DTR
signal at will without it knowing to which serial port it's talking to
and how the board is wired. That info is in the .conf file

In my app, we use the DTR to let the serial device know that there is
a TCP connection for it to talk to, which is basically what the DTR
signal was for in the old days :)

I belive those macros were all from the BSD interfaces that Nut/OS was
originally based from.

Kind Regards,
    Thiago A. Correa


More information about the En-Nut-Discussion mailing list