TX_BUFFER_EMPTY Timing

A catchall for PSoC Mixed-Signal Array (microcontroller) discussions not captured by the other forums.

Moderator: ericb

TX_BUFFER_EMPTY Timing

Postby jiml on Tue Feb 28, 2006 7:55 am

For a 1/2 duplex RS485 application I have the following code after sending a message....

RS485Tx(ON);
RS485_PutChar(0x02);
RS485_PutChar(sId);
RS485_PutChar(tId);

//
// Send body of response message ...here
//

//
// Send Terminal character
//
RS485_PutChar(0x04);

//
// Disable RS485 transmitter after a last char is done xmit...
//
while ((RS485_bReadTxStatus() &
(RS485_TX_COMPLETE | RS485_TX_BUFFER_EMPTY)) !=
(RS485_TX_COMPLETE | RS485_TX_BUFFER_EMPTY)
);

//
// Waste some time..should not need to do this!!!!
//
for (index=0; index< 100; index++);

RS485Tx(OFF);


From reading the UART datasheet I expected that the while() would idle until the last character was completely shifted out (incl. stop bit) however I have observed that the last character of the message is dropped without the for(...) statement. Why is the additional delay before disabling the RS485 tranmitter requried?

Thanks
jiml
jiml
The Big Cheese
The Big Cheese
 
Posts: 440
Joined: Tue Sep 27, 2005 2:35 pm

Postby graaja on Tue Feb 28, 2006 8:53 am

Jim,

The status you are checking will become true when the TX_COMPLETE flag is set for the byte that was sent before the last byte. At the same time TX_BUFFER_EMPTY flag also will be set as the last byte gets loaded to the shift register. So, when the while loop exits, you still will have the last byte in the shift register. Instead of the present test you should do

Code: Select all
// Wait for the last byte to be loaded to the Shift register
while(!(RS485_bReadTxStatus() & RS485_TX_BUFFER_EMPTY));
// Wait for the last byte to finish transmitting
while(!(RS485_bReadTxStatus() & RS485_TX_COMPLETE));
User avatar
graaja
PSoC Master
PSoC Master
 
Posts: 3084
Joined: Thu Dec 18, 2003 4:35 pm
Location: India

Postby jiml on Tue Feb 28, 2006 9:14 am

Thats the ticket...thanks! Somehow I was expecting that both flags would not be set until both the transmit holding register and the transmit shift reg. were empty... You two step check works fine.

Jim
jiml
The Big Cheese
The Big Cheese
 
Posts: 440
Joined: Tue Sep 27, 2005 2:35 pm

On second thought.....

Postby jiml on Tue Feb 28, 2006 2:21 pm

Doing some longer term testing on the solution that Graaja suggests turns up a problem at high(er) baud rates. I notice that after a few minutes the pSoc stops responding to messages and when I tell IceCube to stop running the CPU is sitting at the source line ....

while (!(RS485_bReadTxStatus() & RS485_TX_COMPLETE));

ie the second line of graaja's solution. The UART datasheet says that the TX_COMPLETE bit is cleared on reads of the status register so I am suspecting that sometimes both bits are read in the first while loop and then the second one blocks forever.

jiml
jiml
The Big Cheese
The Big Cheese
 
Posts: 440
Joined: Tue Sep 27, 2005 2:35 pm

Postby graaja on Tue Feb 28, 2006 6:26 pm

Jim,

You are correct. If there is some delay (because of some ISR) in executing the test for TX_BUFFER_EMPTY condition, and if all data gets transmitted during this time, the TX_COMPLETE flag for the last byte will be reset by the read that checks for TX_BUFFER_EMPTY condition for the last but first byte.

Under this condition, the only guaranteed solution to work will be to introduce a delay slightly greater than the duration to transmit a byte after the first check for TX_BUFFER_EMPTY.
User avatar
graaja
PSoC Master
PSoC Master
 
Posts: 3084
Joined: Thu Dec 18, 2003 4:35 pm
Location: India

Postby jiml on Wed Mar 01, 2006 1:36 am

Hmmm... this seems like a bug in the UART UM to me! There is no 100% reliable way to tell when all the queued tranmit acitivity is done.

I wonder if there is some way to modify the UM logic to only set the TX empty bit only if both the transmit shift register and the buffer register are empty.

jiml
jiml
The Big Cheese
The Big Cheese
 
Posts: 440
Joined: Tue Sep 27, 2005 2:35 pm

Postby werner on Wed Mar 01, 2006 2:08 am

jiml wrote:Hmmm... this seems like a bug in the UART UM to me! There is no 100% reliable way to tell when all the queued tranmit acitivity is done.


Wouldn't something like this do the trick ? (Pseudo-code, not tested.)

Code: Select all
uint8_t foo;

do foo = read_status();
while (!(foo & RS485_TX_BUFFER_EMPTY));
while (!(foo & RS485_TX_COMPLETE))
    foo = read_status();


- Werner
werner
The Big Cheese
The Big Cheese
 
Posts: 246
Joined: Fri Jan 20, 2006 10:20 am
Location: Buenos Aires, Argentina

Postby graaja on Wed Mar 01, 2006 3:57 am

Jim,

I agree with you. If there were a bit to indicate a transmit in progress also, then we can properly detect the last byte transmission complete. Without this, we cannot decide if the TX_COMPLETE flag is for the last byte transmitted.

Werner,

The code suggested by you is similar to the one suggested by me. The problem is this. Let us assume, immediately after the last PutChar call, there is an interrupt and the program branches to the interrupt. And when the program comes out of the interrupt, both the bytes in the Shift register as well as the Tx buffer have completed transmitting. In this case, when the Tx status register is read, both the TX buffer empty and Tx complete flags will be set. The first condition will exit because of the Tx buffer empty condition. But now, the next condition will wait for the Tx complete flag which will never get set again because there is no byte in transmission.

One workaround is to introduce a timeout during the second Tx complete flag test. This will come out of the loop everytime the last byte is transmitted and in case some ISR latency, still will exit because of the timeout.

Code: Select all
// Wait for the last byte to be loaded to the Shift register
while(!(RS485_bReadTxStatus() & RS485_TX_BUFFER_EMPTY));
Timeout = 1000;  // Initialize the Timeout value
// Wait for the last byte to finish transmitting or timeout to occur
while((!(RS485_bReadTxStatus() & RS485_TX_COMPLETE)) && (!Timeout))
{
   Timeout--;
}
User avatar
graaja
PSoC Master
PSoC Master
 
Posts: 3084
Joined: Thu Dec 18, 2003 4:35 pm
Location: India

Postby jiml on Wed Mar 01, 2006 5:56 am

Should the while loop be checking for (Timeout) rather than (!Timeout)?
jiml
jiml
The Big Cheese
The Big Cheese
 
Posts: 440
Joined: Tue Sep 27, 2005 2:35 pm

Postby graaja on Wed Mar 01, 2006 5:59 am

Oops. That was a mistake :oops:

The while loop should be checking for (Timeout).
User avatar
graaja
PSoC Master
PSoC Master
 
Posts: 3084
Joined: Thu Dec 18, 2003 4:35 pm
Location: India

Postby jiml on Wed Mar 01, 2006 6:09 am

Thanks Ganesh,
Your solution w/ the wait loop if required seems to be a good compromise although maybe someone can figure out how to actually fix the UM going fwd. UM writing is still a bit beyond me but I do have a growing list of things I wish the UART module could do so may tackle it at some point.

Thanks for your suggestions...
jiml
jiml
The Big Cheese
The Big Cheese
 
Posts: 440
Joined: Tue Sep 27, 2005 2:35 pm

Postby graaja on Wed Mar 01, 2006 6:20 am

Hi Jim,

I would like to point out that the problem is not with the UM, but with the actual hardware. UM is nothing but a collection of firmware code that utilizes the hardware. Corrections if any should be done on the silicon regarding this issue.

However, it's quite possible that this problem could be solved in firmware itself with some other approach and maybe somebody else with such an experience may update this thread.
User avatar
graaja
PSoC Master
PSoC Master
 
Posts: 3084
Joined: Thu Dec 18, 2003 4:35 pm
Location: India

Postby werner on Wed Mar 01, 2006 6:43 am

graaja wrote:The first condition will exit because of the Tx buffer empty condition. But now, the next condition will wait for the Tx complete flag which will never get set again because there is no byte in transmission.


Yes, but won't "empty" and "complete" both be set in this case ? Note that
my loop then only ones one (atomic) read of CR0, not two. The result of
this single read is kept in the variable "foo" (which would typically be just the
A register) in order to perform both bit tests on it.

- Werner
werner
The Big Cheese
The Big Cheese
 
Posts: 246
Joined: Fri Jan 20, 2006 10:20 am
Location: Buenos Aires, Argentina

Postby jiml on Wed Mar 01, 2006 6:44 am

Yes...thanks. I understand. I guess my hope was that by delving sufficiently deeply into the underlying hardware I might be able to come up with some configuration register changes that would change the behavior to be more like what I want .... and then back that modification into a UART+ sort of UM!

jiml
jiml
The Big Cheese
The Big Cheese
 
Posts: 440
Joined: Tue Sep 27, 2005 2:35 pm

Postby graaja on Wed Mar 01, 2006 7:07 am

Hello Werner,

I tested your code. This has the same effect as Jim's original code where the UART is stopped before the last character is sent.

I am attaching a project that I used to check all the methods discussed in this topic. The project can be tested by connecting to PC and running hyperterminal at 19200 baud.
Attachments
testuart.zip
(51.67 KiB) Downloaded 225 times
User avatar
graaja
PSoC Master
PSoC Master
 
Posts: 3084
Joined: Thu Dec 18, 2003 4:35 pm
Location: India

Postby werner on Wed Mar 01, 2006 7:32 am

graaja wrote:I tested your code. This has the same effect as Jim's original code where the UART is stopped before the last character is sent.


Your test looks good, assuming that UART_bReadTxStatus does
nothing but one read of CR0. Also the assembler code is correct.

However, you may still have an interaction with whatever UART_PutCRLF
and friends do. They also have to check that there is room, and may
therefore accidently clear CR0.

Maybe something like this would work better (again, untested, etc.):

Code: Select all
#include <stdint.h>

#define DCBxCR0 /* whatever you're using */
#define DCBxDR1 /* whatever you're using */
#define EMPTY 0x10
#define COMPLETE 0x20

static uint8_t status = EMPTY | COMPLETE;

static void update_status(void)
{
    status = reg[DCBxCR0];
}

static void send_byte(uint8_t byte)
{
    while (!(status & EMPTY))
        update_status();
    DCBxDR1 = byte;   
}

static void await_completion(void)
{
    while (!(status & EMPTY))
        update_status();
    while (!(status & COMPLETE))
        update_status();
}

void main(void)
{
    const char *p;

    for (p = "hello, cruel world\r\n"; *p; *p++)
        send_byte(*p);
    await_completion();
    /* shut down the hardware now */
}


This also avoids calls to library routines that may do unexpected things
behind our backs. In case something sets up some buffered and
interrupt-driven serial port handler, which may read CR0, you'd also
have to turn off that (e.g., by just turning off interrupts).

- Werner
werner
The Big Cheese
The Big Cheese
 
Posts: 246
Joined: Fri Jan 20, 2006 10:20 am
Location: Buenos Aires, Argentina

Postby graaja on Wed Mar 01, 2006 7:36 am

Werner,

The function PutChar does exactly the same as the SendByte function proposed by you. It waits for the TxBuffer to be empty before writing the data to the TxBuffer.
User avatar
graaja
PSoC Master
PSoC Master
 
Posts: 3084
Joined: Thu Dec 18, 2003 4:35 pm
Location: India

Postby werner on Wed Mar 01, 2006 7:46 am

graaja wrote:The function PutChar does exactly the same as the SendByte function proposed by you. It waits for the TxBuffer to be empty before writing the data to the TxBuffer.


Okay, but then you have to look at the status buffered by it, or you'd still get
a second read of CR0 every once in a while. (That still doesn't quite explain
the symptoms, though. Hmm, I guess I'll have to try this myself :-)

- Werne
werner
The Big Cheese
The Big Cheese
 
Posts: 246
Joined: Fri Jan 20, 2006 10:20 am
Location: Buenos Aires, Argentina

Postby werner on Wed Sep 13, 2006 8:06 am

werner wrote:Maybe something like this would work better (again, untested, etc.):


I've now done a simulation, and, provided that the hardware sets
TX_COMPLETE only if the buffer is empty at the time the byte is sent,
the following change may make this work:

Code: Select all
static void send_byte(uint8_t byte)
{
    while (!(status & EMPTY))
        update_status();
    CPU_F &= ~CPU_F_GIE;  // new
    DCBxDR1 = byte;
    update_status();  // new
    CPU_F |= CPU_F_GIE;  // new
}


A validation model in Promela is here:
http://www.almesberger.net/misc/uart.p
For more details on Promela and the SPIN model checker, look here
http://spinroot.com/

- Werner
werner
The Big Cheese
The Big Cheese
 
Posts: 246
Joined: Fri Jan 20, 2006 10:20 am
Location: Buenos Aires, Argentina


Return to PSoC1 General

Who is online

Users browsing this forum: Bing [Bot], Yahoo [Bot] and 1 guest

cron