TXCIF for UART

Go To Last Post
12 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Bit 7 – RXCIF USART Receive Complete Interrupt Flag
This flag is set when there are unread data in the receive buffer and cleared when the receive buffer is empty.

OK.  I thought I understood RXC/RXCIF, but now I'm not sure.  I've reviewed old posts, and they're not really helping a lot.

 

I want, like many others, to implement rs485 (for Optiboot, as it happens), on mega0/xTiny.  That means waiting until for both the TX shift register and the TX data register to be empty before disabling the "transmiter" external circuit.  In my case, in a polled environment.  This is the realm of the TXC flags, which shows up as TXCIF in USART.STATUS on the xmega-like chips.  It's "well known" to be problematic, with inconvenient "automatic clearing" when the ISR is serviced, and inconvenient not-clearing unless you actually use the interrupts.

 

I THOUGHT that the main problem was the "not-clearing" part, and by clearing the flag manually (by writing a 1) you'd be able to tell when transmissions were complete.

But apparently, if you clear TXCIF while no output is in progress (both shift register and data register are empty), it will simply STAY cleared until you actually send at least one character.

 

I think this means that there is no way to check whether the transmitter is idle form an "ignorant" piece of code (that doesn't know whether there has been a recent transmit or not.)  Is that true?  (I want to turn off the transmitter in my "getch()" function, since stk500v1 is nicely lock-step, and won't be trying to receive commands unless it's done transmitting.  But it doesn't transmit anything at startup...)

 

(Yes, I realize the the xmega USART has an rs485 mode that will set a particular pin appropriately.  I was hoping for more freedom of choice in pin selection.  If I had "internal" access to the state of that XDIR bit, that would solve all my problems.  But that doesn't seem possible, either.)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Would like to know the results, also.

 

Jim

 

Until Black Lives Matter, we do not have "All Lives Matter"!

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

In my case, in a polled environment.

If not using the interrupt, just clear the flag anytime you write to TXDATAL (is the only time/place you clear the flag), so anytime the flag is set there is no data going out, and will always be true. There is the initial state of the flag, which I seem to recall is clear, but if you somehow end up needing to check it before any data goes out then you can set baud to 0, enable tx, and put something in TXDATAL, which basically will get the flag set.

 

There is a proper way to clear the flag if interrupts are on (interrupt protect the tx write and the txc clear, so no chance of some long isr messing that up where your flag could be invalid), but if no interrupts it doesn't matter.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The behavior of the TXC flag hasn't changed from very old devices (eg mega8 and tiny2313).
Why is it a problem now?
I think the bad thing is the "ignorant" code.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I think the bad thing is the "ignorant" code.

Perhaps.  If you have a "message oriented" comm protocol it'd be easier to have less ignorant code, but I don't see how it's possible for generic getch()/putch() APIs; getch() has no idea whether a putch() has been done recently, and putch() has no way of knowing whether it is first, last, in-between, or only, in a potential sequence of multiple putch()s.

That's pretty much the rule for what X.28 would call "start-stop mode character transmission"; you fundamentally don't know where the message boundaries are.  (Maybe you shouldn't be trying to run RS485 over such ill-defined connections.  But... people do.)

 

 

Why is it a problem now?

I'm pretty sure it's always been a problem.  AFAIK, it usually gets fixed with horrible hacks like timeouts, or by using the interrupts (which I think ends up working slightly better), and remembering whether you've ever transmitted. :-(   Optiboot doesn't have room for that.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I think that the operation of TXC is not unique to AVR and is equivalent to general uc.
And shouldn't you complain to the person who created it if optiboot isn't suitable for it?
"Optiboot is ignorant code."

 

Who is the culprit who cleared TXCIF with your code?
Why didn't you give him a way to make it clear?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

kabasan wrote:
to the person who created it if optiboot isn't suitable for it?
Bill (westfw) is the current maintainer of Optiboot! wink

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Found code, may not be what you are using, don't know. When using putch you clear TXCIF so it is always clear whenever a byte is put in hardware, and is the only place the flag is cleared. When you eventually reach getch, you wait until the txcif flag is set. You cannot get to putch in any state that would be a problem if following the protocol, so nothing to worry about with putch.

 

Since you probably use getch before putch, as I said before you can get the txcif flag initially set by putting a byte in txdataL, and a baud of 0 works so basically no transmit. There are other ways, like using normal baud but no tx output enabled, and I guess you pick whatever is easiest.

 

void putch (char ch) {
  rs485_txon();  // turn on and leave on till we're done xmitting.
  while (0 == (MYUART.STATUS & USART_DREIF_bm));
  MYUART.TXDATAL = ch;

  MYUART.STATUS = USART_TXCIF_bm; //clear TXCIF
}

uint8_t getch (void) {

  while( (MYUART.STATUS & USART_TXCIF_bm) == 0 ){} //wait for TXCIF

this next function needs to change as we are doing the txcif waiting here
  rs485_txoff();    // To receive, turn off transmitter

  uint8_t ch, flags;
  while (!(MYUART.STATUS & USART_RXCIF_bm));
  flags = MYUART.RXDATAH;
  ch = MYUART.RXDATAL;
  if ((flags & USART_FERR_bm) == 0)
    watchdogReset();
#ifdef LED_DATA_FLASH
  LED_PORT.IN |= LED;
#endif

  return ch;
}

 

 

edit- this is what I have in my bootloader when I init the uart (txcif is set)-

 

            SI void
uartOn      ()
            {
            USART.CTRLB = (USART_RXEN_bm|USART_TXEN_bm);
            USART.TXDATAL = 0; //set TXCIF (w/baud val 0, no bytes go out)
            }

 

and the flag gets clear on any transmit-

 

            // blocking, tx 1 byte
            SI void
putByte     (u8 c)
            {
            while( ! isTxEmpty() );
            USART.TXDATAL = c;
            clearTxDone();
            }

 

and the autobaud waits until all tx is done before it starts as it messes with baud (autobaud is done for every 'packet')-

 

            SI void
autobaud    ()
            {
            while( ! isTxDone() );      // wait until tx done

            ...

 

Last Edited: Fri. Jul 9, 2021 - 04:20 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I don't know optiboot, but it may not be considered for use in half duplex.
Anyway, it would be a mistake to direct your anger at the device.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Assuming your talking about half duplex 485 here, rather then mess with the baud rate, and assuming you manually control the dir pin, during usart init, you set dir to receive mode, and send a character so no actual transmission on the line happens, but the flag would be set to the initial state needed. 

Hope that helps.

Jim

 

 

Keys to wealth:

Invest for cash flow, not capital gains!

Wealth is attracted, not chased! 

Income is proportional to how many you serve!

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Here is what appears to be the code-

https://github.com/Optiboot/opti...

 

It seems the current method simply blocks on each byte (putch) for txcif (in rs485_txoff()), and the code I posted just adds the ability to use the uart hardware buffer instead of blocking on each byte (and I now noted in the previous post that rs485_txoff() would also need changing to remove its txcif code, which I did not notice it was doing).

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I think that the operation of TXC is not unique to AVR and is equivalent to general uc.

Meh.  If TXC worked like DREIF, (was true whenever the tx shift register was idle, and had it's interruping-ness disabled by ... disabling that interrupt) (or equivalently, meant exactly "TX SR empty"), that would be fine.

The 16C550-style UARTs seem to do it right, and "compatible" IP seems to be used in many 32bit CPUs (NXP, for instance.  TEMT flag.)  PIC18 USARTs have TXMTIF, which looks right (TX Shift register empty)
(although, a casual glance at an AVR datasheet might also make one think that TXC is useful...)

 

Some chips don't seem to have that output SR status available at all (rp2040?) :-(

 

The frustrating thing about TXC is that it seems REALLY CLOSE to working in a useful way.  But not quite.  It looks fixable (as per curtvm comments) without only a dozen instructions or so.  But I've only got 255 instructions to use, so...  frustrating.

 

(The existing AVR (non-xMega) rs485 code comes from someone else and disables the transmitter after every byte.  It seems to work for some people.  I was hoping to do better.  I figure any use of STK500v1 over rs485 needs to be "lucky.")  (interestingly, I think STK500v2, with it's more formal message structure, would do a lot better.)