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

does putc block?

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







does putc block?
PostPosted: Thu Sep 22, 2005 9:11 am     Reply with quote

Hi,

I am wondering if the putc function blocks until the byte has been sent or if it writes to a buffer and returns?

If I write:

putc('A');
putc('A');
putc('A');

Is this relatively slow?

Cheers,
Simon
Ttelmah
Guest







PostPosted: Thu Sep 22, 2005 9:39 am     Reply with quote

With a software UART, it waits for each character. With a hardware UART, it transfers the character to the internal hardware buffer if possible, or waits for this to become empty if not.
For the three characters shown, assuming the UART buffers are empty, the first two characters will transfer immediately, and then the third character will block, and wait till the first character is sent.
If you want a fully 'non blocking' version, then use the TBE interrupt, and a software buffer.

Best Wishes
Simon
Guest







PostPosted: Thu Sep 22, 2005 11:05 am     Reply with quote

Yeah, that explains a few things - sort of.

I have noticed that the code execution slows down dramatically by moving from a two byte response to a three byte response, which you have explained.

What concerns me is that it appears that my interrupt routines are not being serviced until after the third byte is sent. I will try implementing it in software as you suggest - it is my preferred approach, but if putc is not blocking my interrupts then I am still unsure what is going on...

Thanks,
Simon
Ttelmah
Guest







PostPosted: Thu Sep 22, 2005 2:39 pm     Reply with quote

This is a buffered transmit routine, that I use without problems:
Code:


//Buffer tests and handling
#define isempty(buff,free,in,out,size) (free>=size)
#define isfull(buff,free,in,out,size) (free<2)
#define tobuff(buff,free,in,out,size,chr) {buff[in++]=chr;\
 --free;\
 in=(in & (size-1));}

unsigned int RSTfrom()
{
 /* Get character from the RS232 TX buffer */
 static unsigned int temp;
 temp=RSTout;
 RSTout=(++RSTout) & (STBUFF-1);
 temp=(RSTbuff[temp]);
 RSTcount++;
 return(temp);
}

/* Sizes and numbers for the buffers */
/* Each buffer uses a character array. Size is in the 'S' defines, and
   the init_buff routine will have to be amended to add extra buffers if required */
#define STBUFF (32)
int RSTcount,RSTin,RSTout;
unsigned int RSTbuff[STBUFF+1];
#byte TXREG=0xFAD
#bit TXIF=0xF9E.4
//Note change these for a 16 chip

#int_TBE
void TBE_isr(void) {
 /* If characters are waiting in the buffer, the next one is
 transferred to the UART
 otherwise the interrupt is disabled, waiting for the next
 transmission. */
 if (!isempty(RSTbuff,RSTcount,RSTin,RSTout,STBUFF)) {
    TXREG=RSTfrom();
 }
 else
    DISABLE_INTERRUPTS(INT_TBE);   
/* RS232 TX */
}

void tchar(unsigned int chr) {
 /* routine to send one character on the RS232.
 This puts the specified character into the software transmit buffer (if
 data is allready
 being transmitted), or else sends the single character to the RS232
 UART. */
 /* First check if the interrupt is enabled, and if not, write directly */
 if (TXIF==0) && (isempty (RSTbuff,RSTcount,RSTin,RSTout,STBUFF))) {
   /* Wait if the TX buffer has not cleared */
   while (TXIF==0) ;
     /* send character */
     TXREG=chr;
   }
   else {
     /* Hold transmission if the buffer is full */
     while (isfull(RSTbuff,RSTcount,RSTin,RSTout,STBUFF)) {
       if (TXIF) {
         /* Here the transmit hardware buffer is empty */
        TXREG=RSTfrom();
      }
   }
   /* put character into the output buffer */
   tobuff(RSTbuff,RSTcount,RSTin,RSTout,STBUFF,chr);
 }
 /* Enable interrupts */
 enable_interrupts(INT_TBE);
}


Simply use 'tchar(chr)', or 'printf(tchar,.....);
The routines handle the interrupts for themselves (disabling the interrupt when the buffer empties, and re-enabling it when data wants to be sent), so just enable the global interrupts and leave this to the routines.

Best Wishes
future



Joined: 14 May 2004
Posts: 330

View user's profile Send private message

PostPosted: Tue May 22, 2007 4:29 am     Reply with quote

So there is no way around blocking if I want to send a full 80x25 screen?

I am experimenting a tx thread that accepts pointer and length of data. It fills the buffer when possible and decrements the length or returns if buffer is full.
Ttelmah
Guest







PostPosted: Tue May 22, 2007 5:00 am     Reply with quote

Depends on the size of your PIC.
The posted buffered transmit code, cn be modified quite easily (int16), to handle buffers of a couple of thousand characters, if your PIC has this much RAM.

Best Wishes
future



Joined: 14 May 2004
Posts: 330

View user's profile Send private message

PostPosted: Tue May 22, 2007 9:05 am     Reply with quote

Yes I understand that the buffer must be able to hold the full string. I was just wondering how computer OS's does with its buffers...
Ttelmah
Guest







PostPosted: Tue May 22, 2007 10:37 am     Reply with quote

If you look at the standard 'PC', the UART itself, on the oldest units, has typically 16 characters of buffering, and on some of the new high speed versions, have up to 1KB (many of the USB 'UARTs' have this as well). Then the software in the PC, typically has in excess of 1KB of it's own buffer. Yet even then, the PC will often 'block' if asked to send large amounts of serial data. Remember that the PC is running a 'timeslicing' OS (the nature of the switching depends on the OS), so when a particular program 'blocks', provided it is reasonably written, the other programs remain running. So jobs carry on being 'done', despite the system waiting for the transmission. The same can be done on the PIC, with careful programming.

Best Wishes
Zer0flag
Guest







Very nice code but I have some questions
PostPosted: Mon Mar 31, 2008 4:47 am     Reply with quote

Hi all!

First I tried the buffered putc routine from the book "Embedded C Programming and the Microchip PIC" written by Barnett, Cox and O'Cull. As far as I can see their routine is also used in the code library in this forum under topic "Communcation between PIC's using wireless RS232". Unfortunately I had some problems with that routine. When I output a lot of chars (more than the buffer can hold) this routine makes my program freeze for some reason.

So I tried the nice buffered putc routine posted in this thread by Ttelmah and it works great. Thank you once again Ttelmah! BTW I had to add a missing bracket in the following code line to make the code compile:

Ttelmah wrote:
This is a buffered transmit routine, that I use without problems:
Code:

 if (TXIF==0) && (isempty (RSTbuff,RSTcount,RSTin,RSTout,STBUFF))) {



I had to change this to:

Code:

 if ((TXIF==0) && (isempty (RSTbuff,RSTcount,RSTin,RSTout,STBUFF))) {


Now I have another question. If I am not wrong the buffer size is defined in Ttelmah's code like this:
Code:

#define STBUFF (32)


But this seems to work only with buffer sizes that are powers of 2. E.g. if I enter a size like 31 or 250 the routine will print garbage. Why is that?

Thank you in advance!
Zer0flag
Ttelmah
Guest







PostPosted: Mon Mar 31, 2008 7:10 am     Reply with quote

There are two basic ways of performing single operation buffer size arithmetic.
1) Use the modulus operator (%).
2) Use the binary '&' operator.
My code uses the latter (and if it is the full version, should comment that it does).
The former will allow non binary multiple buffer sizes.
The difference, is code size, and speed. The compiler in latter versions, is smart enough, that if you use the modulus operator, with a fixed binary buffer size, it'll switch internally to using the '&' form. The difference is massive. For example, using a binary buffer size, the '&' form, takes typically just one instruction (plus about four instructions to access the value). The modulus version, takes about 120 instructions in total.
So, for simplicity, I use binary buffer sizes, to give the best performance.

The best way of handling non binary buffer sizes, is to instead use the increment and test approach. So (for the 'out' counter):
Code:

if ((++out)>=size)out=0;

This is not quite as fast as the binary '&' approach, taking about 8 or 9 instructions in total, but is nearly as good, and works for any buffer size.

Best Wishes
Zer0flag
Guest







PostPosted: Tue Apr 01, 2008 2:13 am     Reply with quote

Thank you for the good explanation Ttelmah!

Best regards,
Zer0flag
Zer0flag
Guest







PostPosted: Tue Apr 08, 2008 5:41 am     Reply with quote

I have some more comments and questions regarding Ttelmah's buffered putc routine:

The problem with the routine is, that it outputs 0x00 for the number of bytes in the buffer when it starts. I think this is because the variable RSTcount needs to be initialized to STBUFF (i.e. the size of the buffer) first instead of leaving it unitialized. This is because RSTcount represents the number of free bytes in the buffer and it is not 0 at start up but should be the size of the buffer:

Code:

int RSTcount=STBUFF;


After analyzing the code I do not really understand the purpose of some parts:

Code:

void tchar(unsigned int chr) {
 /* routine to send one character on the RS232.
 This puts the specified character into the software transmit buffer (if
 data is allready
 being transmitted), or else sends the single character to the RS232
 UART. */
 /* First check if the interrupt is enabled, and if not, write directly */
 if (TXIF==0) && (isempty (RSTbuff,RSTcount,RSTin,RSTout,STBUFF))) {
   /* Wait if the TX buffer has not cleared */
   while (TXIF==0) ;
     /* send character */
     TXREG=chr;
   }


As far as I understand this special case (see above) is executed when the HW transmit buffer is still transmitting data and at the same time our SW transmit buffer is empty. Then the program waits for the current transmission to finish (i.e. TXIF to become high again) and then sends the character directly, without using the SW buffer or interrupt driven transfer. Am I right? What is the purpose of this part and why doesn't this part also send the char directly when TXIF is high, i.e. when we don't have an ongoing transmission and the HW transmit buffer is empty?

Code:

   else {
     /* Hold transmission if the buffer is full */
     while (isfull(RSTbuff,RSTcount,RSTin,RSTout,STBUFF)) {
       if (TXIF) {
         /* Here the transmit hardware buffer is empty */
        TXREG=RSTfrom();
      }
   }
   /* put character into the output buffer */
   tobuff(RSTbuff,RSTcount,RSTin,RSTout,STBUFF,chr);
 }
 /* Enable interrupts */
 enable_interrupts(INT_TBE);
}


The "else" part of tchar is executed for all other cases. Here if the SW buffer is full and there is no ongoing transmission (TXIF high) we send a char from the SW buffer directly without waiting for the transmit interrupt to occur, right? Doesn't this interfere with the INT_TBE interrupt which can also occur and tries to send a char from the SW buffer?

Best regards,
Zer0flag
Ttelmah
Guest







PostPosted: Tue Apr 08, 2008 10:31 am     Reply with quote

The code has been mistyped. The original version, tests _TXIE_, not _TXIF_. TXIE, says that the interrupts are disabled, which only occurs if the chip has finished sending all data, and the interrupt routine has turned the interrupts off. In this state, all that is needed is to transfer the byte directly to the hardware.
The original code was posted in the thread 'Multi-Interrupt' (ID 22953), and includes initialisation, and doesn't have these errors. There is a slightly different version in the thread 'RS232 losing first character transmitted', which is slightly less efficient (it calls the interrupt even for the first character), but is slightly smaller.
I don't know where the mistyped version came from, I just cut and pasted from a search in the forum, and didn't check that it _was_ my original code....

Best Wishes
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