1
\$\begingroup\$

I am working on a project that involves two ATMEGA328p MCUs communicating over long distance (between two floors of a building). One is master and the other one is slave. The master always initiates the communication by sending custom made AT commands which have a Carriage return '\r' and line feed '\n' as terminators at the end.
I am using UART (9600 bps with even parity). Here is what I noticed: When direct UART is used, everything works as expected. But when I replace the communication path with a very long wire (approx 5-10m) the UART started causing issues.
So I had to use RS-485 modules for obvious reasons.

The problem I am seeing is that the whole AT command is transmitted over the RS-485 path but the two ASCII codes of '\r' and '\n' are not received at all. I have checked using an FTDI clone that input to the RS-485 transmitter-side module is having CR and LF as expected but at the receiving end (after RS485 to UART conversion), whole command is received except CR and LF.

Please suggest me what to do as my whole software code depends on detection of CR and LF as a protocol. I have gone through many blogs/QAs but couldn't get my issue resolved. It seems like RS-485 module is not sending CR and LF.

These are the two UART functions that work at the lowest level. I have checked that my code always passes 1 as the trm argument

void UART_TxSTRING (char string[], int trm)     //This string function does not sends the NULL character '\0'
{   
    int i = 0;
    while (string[i] != '\0')       //Detect end of string
    {
        UART_TxByte(string[i]);
        i++;
    }
    if (trm == 1)                   //if trm=1, terminators will be sent with string
    {
        UART_TxByte('\r');          //Terminator
        UART_TxByte('\n');          //Terminator
    }
}

void UART_TxByte(char tx_data)
{   
    UCSR0B  &= ~RXIE;               //Disable Rx interrupts while sending data
    while (!(UCSR0A & TXC_FLAG));   //Check flag before transmitting
    UDR0 = (tx_data & 0xFF);        //Send the ASCII code
    UCSR0B  |= RXIE;                //Enable Rx interrupts again after transmission
}

Update: I modified the code to replace the CR with @ and replaced LF with $ Still only the termination characters are lost, rest of the command (long or short) is transmitted normally. I am feeling like there is some issue related to timing as the if(trm == 1) statement is causing the RS-485 IC to drop subsequent characters.

\$\endgroup\$
15
  • \$\begingroup\$ Look at the RS485 lines with a scope - do you see the CR/LF there? What exactly are these modules - do they have any sort of 'intelligence' in them or are they simple line drivers? \$\endgroup\$
    – brhans
    Commented Apr 2, 2021 at 18:04
  • \$\begingroup\$ Does sending a CR or LF with UART_TxByte() work? \$\endgroup\$
    – Rodo
    Commented Apr 2, 2021 at 18:05
  • \$\begingroup\$ @brhans I am using these modules images-na.ssl-images-amazon.com/images/I/… \$\endgroup\$ Commented Apr 2, 2021 at 18:16
  • \$\begingroup\$ @Rodo I transmitted this sequence repeatedly UART_TxByte('S'); UART_TxByte('\r'); UART_TxByte('M'); UART_TxByte('\n'); delayms(100); And observed on the serial terminal that only S and CR are received, M and LF are not received. \$\endgroup\$ Commented Apr 2, 2021 at 18:20
  • \$\begingroup\$ I can only use USB-TTL converter to trace packets since I do not have access to any kind of scope. There is something with this module that I cant figure out. \$\endgroup\$ Commented Apr 2, 2021 at 18:24

1 Answer 1

1
\$\begingroup\$

Not 100% sure this will resolve your issue, but something to check:

per Atmel datasheet, immediately prior to transmitting bytes you should be waiting for the UDREn bit, meaning the UDRn register is empty, and can accept a new byte.

UDREn is different than the TXCn bit, which you seem to be using (but it's not clear how you defined that mask).

Anyway, TXCn is for after, not before. Other differences: UDREn happens before the byte has been fully transmitted, and TXCn may not get automatically cleared the same way as UDREn.

Use UDREn when feeding a bunch of data to be transmitted in a block, use TXCn when waiting for the final byte to finish, in case that matters.

\$\endgroup\$
3
  • 1
    \$\begingroup\$ I noticed that there is nothing wrong with the code. The RS-485 module is omitting the last two ascii codes of any string I send. So I modified the code to send two extra spaces after CR and LF. This worked. \$\endgroup\$ Commented Apr 3, 2021 at 5:59
  • 1
    \$\begingroup\$ Take special care about te direction line. If it gets changed while transmitting, the transmitter/receiver will "omit" the bytes \$\endgroup\$
    – fifi_22
    Commented Apr 7, 2021 at 12:52
  • 1
    \$\begingroup\$ This must be the correct answer. The handling of RS-485 Transmit Enable is not shown, but it's the only thing which eplains why two last characters seem to be "eaten" by the module. Since TXC is examined before writing a byte to transmit, all bytes are written, but the CR is just being transmitted in the transmit shift register, and LF has just been sent to transmit data register, and likely the transmit enable signal is turned off right after calling the function. \$\endgroup\$
    – Justme
    Commented Mar 5, 2023 at 17:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.