RS485

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

Hi,

**** Device ***
Atmega 640
XTAL = 16MHZ
Compiler = ICCAVR 7.19
**** End *******

I am using a two wire 485 chip and it all works fine except that after when I get the Atmel to send a sting to the PC it misses the last letter.

E.g. send "Hello" receive "Hell" (Hmm need a better example :))

I know it is because I am looking at the transmit buffer to see when it is empty and then I set the 485 chip attached to the AVR back to RX mode from TX. Before all the data has been sent.

So is there a register I can look at that tells me when the all the data has gone including the Stop byte, or must I just add a delay to make sure it all gets sent?

The code is as follows: -

//-----------------------------------------------------------------------------
//
//	Function name :    RS485_Start_TX
//
//	Returns :          None
//
//	Parameters :       None
//
//	Purpose :          Enables the RS485 to TX
//
//-----------------------------------------------------------------------------
void RS485_Start_TX(void)
{
  RS485_RX_HIGH	   	 	   			   // Enable the RS485 Chip to recive	
  RS485_TX_HIGH						   // Enable the RS485 Chip to recive
}

//-----------------------------------------------------------------------------
//
//	Function name :    RS485_Start_RX
//
//	Returns :          None
//
//	Parameters :       None
//
//	Purpose :          Enables the RS485 to RX
//
//-----------------------------------------------------------------------------
void RS485_Start_RX(void)
{
  while (!(UCSR0A & (1 << TXC0)));	// Wait for the transmision to complete
  while (!(UCSR0A & (1 << UDRE0))); // Wait for empty transmit buffer
   
  RS485_TX_LOW					   // Enable the RS485 Chip to RX
  RS485_RX_LOW	   	 	   		   // Enable the RS485 Chip to RX	
}

//-----------------------------------------------------------------------------
//
//	Function name :    UART0_Char
//
//	Returns :          None
//
//	Parameters :       char data -> The Char to send
//
//	Purpose :          Sends a single character to UART0
//
//-----------------------------------------------------------------------------
void UART0_Char(char data)
{ 
  while (!(UCSR0A & (1 << UDRE0)));	   // Wait for empty transmit buffer
  UDR0 = data; 	  					   // Start transmition
}

//-----------------------------------------------------------------------------
//
//	Function name :    UART0_String
//
//	Returns :          None
//
//	Parameters :       Char* string -> Send a string
//
//	Purpose :          Sends a string to UART0
//
//-----------------------------------------------------------------------------
void UART0_String(char* string)
{
  RS485_Start_TX();
  
  while(*string)  		 			 // Send String 											
	{
    while(!(UCSR0A & (1 << UDRE0)));	 // Wait until Tx fifo empty, if necessary
    UDR0 = *string++;                    // Fill FIFO
	}
  RS485_Start_RX();   
}

Thanks,

Dora-9

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

Dont disable tx until last char is gone... check the TXC bit to tell when its ok

Imagecraft compiler user

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

Hi,

If you look in the code i have posted i am already doing this :-

void RS485_Start_RX(void)
{
  while (!(UCSR0A & (1 << TXC0)));   // Wait for the transmision to complete
  while (!(UCSR0A & (1 << UDRE0))); // Wait for empty transmit buffer
   
  RS485_TX_LOW                  // Enable the RS485 Chip to RX
  RS485_RX_LOW                         // Enable the RS485 Chip to RX   
} 

But i still need a delay in this function to make it work: -

void RS485_Start_RX(void)
{
  while (!(UCSR0A & (1 << TXC0)));   // Wait for the transmision to complete
  while (!(UCSR0A & (1 << UDRE0))); // Wait for empty transmit buffer
   
 Delay_usec(250);    // 250 uSecond Delay 

  RS485_TX_LOW                  // Enable the RS485 Chip to RX
  RS485_RX_LOW                         // Enable the RS485 Chip to RX   
} 

Thanks,

Andy

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

Hi,

If you look in the code i have posted i am already doing this :-

void RS485_Start_RX(void)
{
  while (!(UCSR0A & (1 << TXC0)));   // Wait for the transmision to complete
  while (!(UCSR0A & (1 << UDRE0))); // Wait for empty transmit buffer
   
  RS485_TX_LOW                  // Enable the RS485 Chip to RX
  RS485_RX_LOW                         // Enable the RS485 Chip to RX   
} 

But i still need a delay in this function to make it work: -

void RS485_Start_RX(void)
{
  while (!(UCSR0A & (1 << TXC0)));   // Wait for the transmision to complete
  while (!(UCSR0A & (1 << UDRE0))); // Wait for empty transmit buffer
   
 Delay_usec(250);    // 250 uSecond Delay 

  RS485_TX_LOW                  // Enable the RS485 Chip to RX
  RS485_RX_LOW                         // Enable the RS485 Chip to RX   
} 

Thanks,

Andy

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
 RS485_RX_HIGH                            // Enable the RS485 Chip to recive 

This DISABLES rx usually with the chips I have used.

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

I couldn't get

while (!(UCSR0A & (1 << TXC0)));   // Wait for the transmision to complete  

to work for me either, so added a delay, fixed in my case.

There must more to this issue!

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

Here's what I think may be happening, based on a quick read of the AVR UART doc. You have to reset TXC0 manually unless you have a TXCIE0 interrupt, which will reset TXC0 automatically. If you don't reset TXC0 at the end of your transmission (and I don't see your code doing that), on the next transmission it will still be set and your loop that tests it will immediately fall through, not waiting for the last character to be sent.

So probably your first string of the session goes out correctly, but every string after that fails until you reset the chip in some fashion.

To reset TXC0 manually you write a '1' to the bit.

BTW, according to the doc you don't need to also wait for UDRE0 to go high, since TXC0 will only go high when the TX buffer is empty _and_ the last bit is shifted out.

Mike

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

Quote:
I couldn't get

Code:
while (!(UCSR0A & (1 << TXC0))); // Wait for the transmision to complete

to work for me either, so added a delay, fixed in my case.

...welll davef..you may remember that from a thread of December last year I said:
Quote:
Quote:
while(!(UCSRA & (1<<TXC)))
wouldn't fix the problem,
But, unless the code is within an ISR where TXC is cleared automatically, you need to clear TXC as soon as you put data in the UDR or it will stay set so the test will fail.
which is what the datasheet says and what snarflemike is also saying.

I bet a copy of winAvr that this will fix the problem :lol:

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

There's probably a two-level tx-buffer in the usart.

You'll have to wait a little while to get the last char out before you toggle the RX Enable/TX disable lines.

Or clear the TXC and check for that after the last byte has set the UDRE bit.

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

John,

I tried to implement your suggestion (last Dec), but I must have left out the most important part!

I look forward to my next RS485 implementation as chances are better that it will work as it should. Thanks for clarifying things even further.

Cheers,
davef