TX complete flag not behaving as expected

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

The behavior of the TX complete flag is baffling me. I would really appreciate it if someone could shed some light on this behavior.

All the code does is set up the UART and then bounce back any data that is received. Initially the code didn't work until I added the 16 us delay. In order to try to figure out why, I toggled PD3 and then probed it with the oscilliscope.

#include 
#include 
#include 

#define F_CPU 12000000
#define TX0_EMPTY (UCSR0A & (1<<UDRE0))
#define RX0_COMPLETE (UCSR0A & (1<<RXC0))
#define TX0_COMPLETE (UCSR0A & (1<<TXC0))

int main(void)
{
	//UART0 SETUP
	#define baud 500000
	#define UBRR ((uint16_t)(F_CPU/(8.0*baud) - 1 + 0.5))
	UBRR0H = (uint8_t)(UBRR>>8);
    UBRR0L = (uint8_t)(UBRR);
	UCSR0A = (1<<U2X0); // DOUBLE SPEED MODE (BAUD = XTAL/(8*(UBRR+1)))
	UCSR0B = (1<<TXEN0)|(1<<RXEN0);  //ENABLE UART
	UCSR0C = (3<<UCSZ00);  //8-BIT RS232, 1 STOP BIT, NO PARITY
	DDRD |= (1<<PD6);	//PD6 controls the half-duplex direction on the RS-485 driver chip
	#define TX_MODE PORTD |= (1<<PD6);
	#define RX_MODE PORTD &= ~(1<<PD6);
	RX_MODE
	
	DDRD |= (1<<PD3);
	PORTD &= ~(1<<PD3);
	_delay_loop_2(64000);
	PORTD |= 1<<PD3;

	while(1)
	{
		if(RX0_COMPLETE)
		{
			_delay_loop_2(4);  //~1.2 us delay
			TX_MODE
			UDR0 = UDR0;
			PORTD ^= 1<<PD3;
			while(!TX0_COMPLETE)
				;
			PORTD ^= 1<<PD3;
			_delay_loop_2(50);  //16.2 us delay measured
			PORTD ^= 1<<PD3;
			RX_MODE
		}
		_delay_loop_2(64000);
		_delay_loop_2(64000);
		_delay_loop_2(64000);
	}

}

Below you can see the oscilliscope probes. I cant figure out why the while(!TX0_COMPLETE) last much shorter than the frame duration.

Any thoughts?

Attachment(s): 

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

You never clear the bit.

Iluvatar is the better part of Valar.

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

skeeve wrote:
You never clear the bit.
When I originally read the data sheet I read "The TXCn Flag bit is automatically cleared" and for some reason completely missed the rest of the sentence.

ATMEGA48p Datasheet wrote:
The TXCn Flag bit is automatically cleared when a transmit complete interrupt is executed, or it can be cleared by writing a one to its bit location.

Thanks!

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

dpaulsen wrote:
ATMEGA48p Datasheet wrote:
The TXCn Flag bit is automatically cleared when a transmit complete interrupt is executed, or it can be cleared by writing a one to its bit location.

But you have no interrupts enabled, no ISR for the transmit complete interrupt, so it never will be executed.
You have to:
- clear the flag by writting one to its bit location
or
- write (at least an empty) ISR for transmit-complete-interrupt and enable it

/Martin.

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

mtaschl wrote:
dpaulsen wrote:
ATMEGA48p Datasheet wrote:
The TXCn Flag bit is automatically cleared when a transmit complete interrupt is executed, or it can be cleared by writing a one to its bit location.

But you have no interrupts enabled, no ISR for the transmit complete interrupt, so it never will be executed.
You have to:
- clear the flag by writting one to its bit location
or
- write (at least an empty) ISR for transmit-complete-interrupt and enable it

Yes I did understand this, but thanks for clarifying for others who may benefit from this post.

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

Hello,

I have almost the same problem on my xmega128a3 with the USARTF0_DRE_vect, in my case the send/receive pin of the half duplex RS485 transceiver is switched to early to receive mode.

I've tried to insert a delay which help to send the fully data packet,but i run in trouble with the received data bits from the transceiver: my USARTF0_RXC_vect is never called.

Should i use the USARTF0_TXC_vect instead of USARTF0_DRE_vect? How can check when the last char is FYSICALLY transmitted over the line?

set transceiver in send mode

this routine is called by a forloop to send all c characters

  do{
        /* Wait until it is possible to put data into TX data register.
         * NOTE: If TXDataRegister never becomes empty this will be a DEADLOCK. */
        }while(!USART_IsTXDataRegisterEmpty(&USARTF0));
        USART_PutChar(&USARTC0, c);

set transceiver in receive mode /* this action happens to early , not all characters were transmitted */


from atmel uart library

#define USART_IsTXDataRegisterEmpty(_usart) (((_usart)->STATUS & USART_DREIF_bm) != 0)

  #define USART_PutChar(_usart, _data) ((_usart)->DATA = _data)

included in my cpp code 

ISR(USARTF0_DRE_vect)
{
USART_DataRegEmpty(&USART4_data);
}

atmel code

[code]
/*! \brief Data Register Empty Interrupt Service Routine.
*
* Data Register Empty Interrupt Service Routine.
* Transmits one byte from TX software buffer. Disables DRE interrupt if buffer
* is empty. Argument is pointer to USART (USART_data_t).
*
* \param usart_data The USART_data_t struct instance.
*/
void USART_DataRegEmpty(USART_data_t * usart_data)
{
USART_Buffer_t * bufPtr;
bufPtr = &usart_data->buffer;

/* Check if all data is transmitted. */
uint8_t tempTX_Tail = usart_data->buffer.TX_Tail;
if (bufPtr->TX_Head == tempTX_Tail){
/* Disable DRE interrupts. */
uint8_t tempCTRLA = usart_data->usart->CTRLA;
tempCTRLA = (tempCTRLA & ~USART_DREINTLVL_gm) | USART_DREINTLVL_OFF_gc;
usart_data->usart->CTRLA = tempCTRLA;

}else{
/* Start transmitting. */
uint8_t data = bufPtr->TX[usart_data->buffer.TX_Tail];
usart_data->usart->DATA = data;

/* Advance buffer tail. */
bufPtr->TX_Tail = (bufPtr->TX_Tail + 1) & USART_TX_BUFFER_MASK;
}
}

Any suggestion to solve my problem

Thanks

Karlo

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

If you use the "TXC" flag, then you can be assured that the last bit has physically left the USART before you switch the RS485 direction.

Because the USART is double buffered, the "DRE" flag will actually assert itself at the start of the transmission of the byte that had most recently been written into the transmit holding register, to give you a chance to queue up the next byte at the earliest opportunity, before the USART has a chance to go idle. This allows you to achieve sustained transmissions with the highest attainable throughput (ignoring the possibility to use DMA in Xmega AVRs).

Because the "DRE" flag goes true as soon as the holding register is available to hold the next byte, but before the current byte has finished physically clearing the USART, it can give the appearance, in situations like this, that the interrupt is firing too early.

It *is* possible to use a hybrid of both the "DRE" interrupt and the "TXC" interrupt, to sustain maximum throughput in the middle of each transmission, and still receive notification of the exact time at which the final bit of the final byte has physically left the USART.

But for many applications, maximizing throughput isn't all that necessary, so the "TXC" flag may be all you need.

Trying to fudge all this into Atmel's Xmega software framework may prove to be cumbersome. In my opinion, Atmel's framework may be a good starting point for getting to understand how the peripheral drivers are meant to work, but they are often not appropriate for direct use in every possible situation.

- Luke