CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

#use RS232(TRANSMIT_BUFFER= Behavior

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
jonnewbill



Joined: 29 Jul 2015
Posts: 10

View user's profile Send private message

#use RS232(TRANSMIT_BUFFER= Behavior
PostPosted: Mon Sep 07, 2015 2:27 pm     Reply with quote

I'm trying to use the TRANSMIT_BUFFER feature in the #use RS232 command. Without TRANSMIT_BUFFER field my application transmits all printf strings correctly. When I use TRANSMIT_BUFFER=32 as shown here,

Code:

#use rs232(baud=38400, RECEIVE_BUFFER=16, TRANSMIT_BUFFER=32, TXISR, ERRORS, stream=SHOW_UART,  xmit=U1Tx_, rcv=U1Rx_)


It seems when I try to printf more than 32 characters then characters are lost. I would have expected that when the transmit buffer was full that the printf and putc routines would hang waiting for characters to be transmitted the way they do when no TRANSMIT_BUFFER is used. Is this expected behavior?
_________________
I remember the days before OPS (Other Peoples Software) when all the bytes in my computer were written by me.
Ttelmah



Joined: 11 Mar 2010
Posts: 19515

View user's profile Send private message

PostPosted: Mon Sep 07, 2015 2:37 pm     Reply with quote

Yes.

You have to check the space is in the buffer before sending. The buffered routines are designed to be 'non hanging' so if there is not space the characters are just lost.

tx_buffer_available(STREAM) returns how much space is currently available.

Personally I wish there was an option to select the behaviour. By default it throws the new characters, I'd prefer it to throw the old.

If you want to add checking and hold, then just encapsulate the putc:
Code:

void buffer_putc(int8 chr)
{
    while(tx_buffer_available(STREAM)<1)
        ;//wait for space
    fputc(chr,STREAM);
}

//then printf to this:

printf(buffer_putc,"What you want");

This will then wait when there is not space in the buffer.
jonnewbill



Joined: 29 Jul 2015
Posts: 10

View user's profile Send private message

PostPosted: Mon Sep 07, 2015 6:46 pm     Reply with quote

I was not aware of the non-standard
Code:
printf(putcFunction,format,vars);
function. It is not described in the documentation other than a line showing that format in the manual with no explanation that I could find. To create a FIFO buffered serial port that doesn't block on FIFO full is not very useful. If the FIFO port was a blocking call it would address the more common and useful case and if I want to avoid blocking on the call only then I could check FIFO space before making the call.

This compiler continues to surprise me but not in a good way. . .

Thanks for your help.
_________________
I remember the days before OPS (Other Peoples Software) when all the bytes in my computer were written by me.
Ttelmah



Joined: 11 Mar 2010
Posts: 19515

View user's profile Send private message

PostPosted: Mon Sep 07, 2015 11:00 pm     Reply with quote

Realistically for embedded applications, it is important to not pause the flow of code. Hence, non blocking is the way to go. As you see, if you need blocking, then it is easy to add,
jonnewbill



Joined: 29 Jul 2015
Posts: 10

View user's profile Send private message

PostPosted: Mon Sep 07, 2015 11:55 pm     Reply with quote

In my experience blocking is an important element of embedded processing and certainly blocking is much preferred over randomly losing transmission data without any error return to the caller. I have written many FIFO serial buffer implementations for embedded applications over the past 30+ years and they all provided for blocking on transmit buffer full. I can't imagine any application where this "randomly discard data" method would be acceptable.

I tried your code above. What I'm seeing is that tx_buffer_available() never returns a value less than 1. In the manual there's a cryptic reference in the description of tx_buffer_available()

"Transmit buffer is implemented has [SIC] a circular buffer, so be sure to check to make sure there is room for at least one more then[SIC] what is actually needed"

I think this is saying the implementation of their FIFO doesn't allow the last available character to be used. This often happens when FIFO's are implemented with head and tail pointers instead of head and length. With head and tail pointers it's not possible to distinguish between an empty or full FIFO.

The code that seems to work is
Code:

void buffer_putc(int8 chr)
{
    // Available must be 2 or more before another character can be added
    while(tx_buffer_available(STREAM) < 2)
        ;//wait for space
    fputc(chr,STREAM);
}

//then printf to this:

printf(buffer_putc,"What you want");


Thanks again for your help. Much appreciated.
_________________
I remember the days before OPS (Other Peoples Software) when all the bytes in my computer were written by me.
Ttelmah



Joined: 11 Mar 2010
Posts: 19515

View user's profile Send private message

PostPosted: Tue Sep 08, 2015 12:27 am     Reply with quote

If you need to ensure data is not lost, then you need to ensure you buffer is big enough.

Loss of a time 'slot' is more important than loss of data. The transmission protocol should be designed to handle the latter....

Yes, I had seen there comment about needing two characters. It is actually perfectly possible for a circular buffer to use all available characters, but CCS have obviously used the simpler coding that makes this difficult.

I must admit I always use my own buffering, but that's because I want to lose the oldest data rather than the new data, on an overflow.
RF_Developer



Joined: 07 Feb 2011
Posts: 839

View user's profile Send private message

PostPosted: Tue Sep 08, 2015 7:16 am     Reply with quote

jonnewbill wrote:
In my experience blocking is an important element of embedded processing and certainly blocking is much preferred over randomly losing transmission data without any error return to the caller. I have written many FIFO serial buffer implementations for embedded applications over the past 30+ years and they all provided for blocking on transmit buffer full. I can't imagine any application where this "randomly discard data" method would be acceptable.


To be fair, there's a number of different interpretations of "blocking" being referred to in this thread, not to mention conflation of the issues of transmit buffering with receive buffering.

Buffering is all about connecting processes, hardware of software, running at different speeds. When transmitting, the problem is that the peripheral is slow relative to the processor and so in many applications the processor would waste valuable time if it had to wait for each and every character, message or whatever. Transmit buffering allows the processor to store up outgoing characters etc., for them to be transmitted at some time later.

Generally, the term blocking in the transmit context is not about behaviour when the buffer is full (in which case the buffer is effectively not present); that's a special case. Instead its about the behaviour most of the time when the buffer is anything from empty to nearly full. Avoiding the buffer full case is generally simply a matter of providing a big enough buffer. Buffer full generally means "someone didn't design this right". In transmit, there's normally no reason to loose any characters, except in a full reset type situation. All the transmitting process has to do is wait until there is some space, but that's at a price of responsiveness to other tasks.

In receive the issues are somewhat different. When hardware gets a character it has to do something with it. Generally that's store it in the receive buffer, but what to do if that buffer is full? It can't force the code to do something to make space, and its time critical: another character may well be along in a little while. So, something has to give, a character has to be lost, but which one? No, its not a randomly chosen character, its either the one just received, or the one received longest ago, making space for the new character. The point being in the transmit situation the software can wait, but at a price of system performance, but in receive, the hardware, generally, can't wait. So the behaviour has to be different.

With many PIC embedded applications, transmit buffering is often a luxury (RAM is quite limited...), while receive buffering is critical. In either case, its up to the programmer to provide sufficient buffering space to avoid data loss or excessive blocking.

Personally I've never used the buffering provided by the CCS compiler. It has only been available relatively recently (not all version 5s have it), and many of my apps predate it, so I've done and probably will continue to do my own.
newguy



Joined: 24 Jun 2004
Posts: 1908

View user's profile Send private message

PostPosted: Tue Sep 08, 2015 7:55 am     Reply with quote

RF_Developer wrote:
Personally I've never used the buffering provided by the CCS compiler. It has only been available relatively recently (not all version 5s have it), and many of my apps predate it, so I've done and probably will continue to do my own.


Amen. The routines I currently use to buffer serial RX/TX were initially (many years ago) based off of the CCS example but what they've become is based on many years of field observations/performance and also the needs of the system I'm supporting.

It's nice that CCS offer support for buffering, but always bear in mind that you get what you pay for, that there is no such thing as a free ride, and my favourite maxim: never trust anything you didn't write yourself.
jonnewbill



Joined: 29 Jul 2015
Posts: 10

View user's profile Send private message

PostPosted: Sat Sep 19, 2015 1:29 pm     Reply with quote

The problem is that selecting TRANSMIT_BUFFER= in the #use RS232 directive changes the behavior of putc and printf routines. It is very non-intuitive that adding a transmit buffer causes the loss of data. Without it these routines wait for the transmitter to be ready and there is no loss of data and the calling routine is automatically made to wait for character transmission to complete before writing the next character but with TRANSMIT_BUFFER= these same routines simply discard data with no error return which is unexpected behavior. This behavior is compounded by there being no explanation of this in the documentation for TRANSMIT-BUFFER=n. A simple line like

"WARNING when TRANSMIT_BUFFER is used the caller is responsible for insuring there is space in the buffer by calling tx_buffer_available() or data is lost."

This would have saved several hours of investigation and frustration.
Here was my response to support@ccsinfo.com

And users on your forum have confirmed that when transmit buffering is selected there is no handling of overflow in your FIFO routines. Characters are simply discarded. This seems like a very poor design decision as I can see no application where this "randomly discard data" method would be useful. Also it means that by adding TRANSMIT_BUFFER= the behavior of printf and putc changes from waiting on transmitter available to "do not wait and discard data". Given that this is the rather unexpected behavior of the FIFO routines it would be most useful to include a description of it in the TRANSMIT_BUFFER= section of #use RS232 in the manual.

Also I have discovered that tx_buffer_available() always returns 1 and never 0. Such that if a putc() is done when tx_buffer_available() is 1 the character is lost. Meaning that one must test for tx_buffer_available() >= 2 before sending a character. So tx_buffer_available() doesn't actually return the number of buffer characters available since when it returns a value of 1 sending one more character causes it to be discarded due to overflow and the tx_buffer_available() function continues to return a value of 1
_________________
I remember the days before OPS (Other Peoples Software) when all the bytes in my computer were written by me.
Ttelmah



Joined: 11 Mar 2010
Posts: 19515

View user's profile Send private message

PostPosted: Sat Sep 19, 2015 11:22 pm     Reply with quote

The manual says almost exactly this under the tx_buffer_available entry.

Several comments.

First, I agree that there should be an option to allow the behaviour to be changed. An option in #use rs232, something like 'HOLD_ON_BUFFERFULL', to allow the behaviour to be changed from losing data to holding when the buffer is full, would be great. So _ask CCS_. Put in a request for this.
Then there has been a new instruction added to the latest compiler releases. They now have a function 'tx_buffer_full' that returns 'true' when there is no space in the buffer. This makes the overload:
Code:

void buffer_putc(int8 chr)
{
    while(tx_buffer_full(STREAM))
        ;//wait for space
    fputc(chr,STREAM);
}

Much nicer.
This suggests that CCS may well be working on adding something exactly like we are talking about.
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group