View previous topic :: View next topic |
Author |
Message |
notbad
Joined: 10 Jan 2013 Posts: 68
|
Problem in receiving a string through UART/RS-485 |
Posted: Tue Sep 24, 2013 7:19 am |
|
|
Hi
I’m trying to read some registers from a modbus slave and store it in a string.(Communication Format is: ASCII 7,N,2 9600)
The normal query looks like this:
Code: |
TX=":010321000006D5\r\n"
RX=":01030C0000052A0CC200000000172AB2\r\n"
|
But I receive two extra null chars at the beginning of the response. (I check using a USB-485 converter on my PC.)
Here's the hex representation of message bytes:
Code: |
00003A30313033304330303030303532413043433230303030303030303137324142320D0A
|
The function get_string is a modified version of the one from the file input.c.
What do you guys think is the problem?
Code: |
#include <16F873A.h> //CCS v4.130
#device adc=10
#FUSES NOWDT, HS, PUT, NOBROWNOUT, NOLVP
#use delay(clock=16000000)
#use rs232(BAUD=9600,UART1,ERRORS)
#bit TRMT = getenv("BIT:TRMT")
#define RE_DE PIN_C5
int8 cmd[40];
int8 i;
int1 timeout_error=FALSE;
void putc7n2(int8 chr);
int8 getc7n2();
void get_string(char* s, int8 max);
//*********************************************************
void main()
{
setup_adc_ports(NO_ANALOGS);
while(1)
{
delay_ms(500);
strcpy(cmd,"dummy fill.");
output_high(RE_DE);
printf(putc7n2,":010321000006D5\r\n"); //read registers 2100-2105 (6)
while(TRMT==0); //wait untill sending is done
output_low(RE_DE);
get_string(cmd, 40); //store the response in cmd
output_high(RE_DE);
if (timeout_error)
printf(putc7n2, "Timeout\r\n");
else
{
for (i=0; i<40; ++i)
{
printf(putc7n2, "%X", cmd[i]);
}
printf(putc7n2, "\r\n");
}
while(TRMT==0); //wait untill sending is done
output_low(RE_DE);
}
}
//*********************************************************
void get_string(char* s, int8 max)
{
int16 timeout=10000; // 10000 * 10us = 100ms. The response has to be 'finished' in 100ms
int8 len=0;
char c;
timeout_error=FALSE;
--max;
do
{
while(!kbhit()&&(--timeout)) //wait for new charachter and decrease timeout.
delay_us(10);
if(kbhit()) // if it's kbhit, getc. if not, then timeout has reached.
c=getc7n2();
else
{
timeout_error=TRUE;
break;
}
if(len<=max)
s[len++]=c;
else
break;
} while(c!=0x0A);
s[len]=0;
}
//*********************************************************
void putc7n2(int8 chr)
{
bit_set(chr,7); // Simulate an extra stop bit by setting the MSB
putc(chr);
}
//*********************************************************
int8 getc7n2()
{
int8 chr;
chr=getc();
bit_clear(chr,7); // clear extra stop bit
return(chr);
} |
|
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19513
|
|
Posted: Tue Sep 24, 2013 8:41 am |
|
|
Possibly termination.
When the bus turns round, for a little while the 485, is undriven, while the other end decides it is going to reply. If the bus is not physically biased to the 'off' state, when undriven, and you are not using 485 chips that guarantee to see an undriven bus as 'idle', then you will see extra characters like this.
Choices to avoid this, are to either design a little bus biasing into the termination, or chose RS485 drivers than see an undriven bus as idle.
Other thing, do you have a pull up on the RX line of the PIC?.
When the receiver is disabled, when you are sending, this line needs to be pulled high, otherwise extra characters can be seen.
Best Wishes |
|
|
notbad
Joined: 10 Jan 2013 Posts: 68
|
|
Posted: Tue Sep 24, 2013 1:57 pm |
|
|
Thank you very much Ttelmah!
I already had 620 ohm idle biasing resistors, but the pull-up on RX solved the problem!
What exactly causes this? The crosstalk from TX Line?
It's weird because there were always exactly two nulls and nothing else!
As this issue is not discussed in the datasheet, how should I have known this? |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19513
|
|
Posted: Tue Sep 24, 2013 2:55 pm |
|
|
If you think about it the PIC pin is an input. When you turn off the transceiver, nothing is driving it. What is going to keep it high?.
Since you are not using interrupt driven RX, there are just two characters of buffering in the hardware. These are received when you once again start listening. A line going low, will give a zero character.
Best Wishes |
|
|
notbad
Joined: 10 Jan 2013 Posts: 68
|
|
Posted: Wed Sep 25, 2013 2:08 pm |
|
|
Thank you Ttelmah!
I added a button and a piece of code at the end of the while(1) to write a register in the slave.
The slave responds to command but I don’t need this response. This overruns the hardware buffer.
How can I safely clear the buffer and overrun errors?
I used two getc()s but the pic gets stuck if there is occasionally no response.
Another question:
Is checking TRMT bit enough to make sure a printf is 'done'?
Doesn't TRMT go high for a moment after the second last byte is sent and the last byte is being moved to the TSR? |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19513
|
|
Posted: Thu Sep 26, 2013 1:38 am |
|
|
The 'ERRORS' in the #use RS232 adds code to clear the overrun error, or framing error when a character is read.
However you can (of course) only read the character, when there is one to read.
So:
Code: |
if (kbhit())
getc();
if (kbhit())
getc();
|
Makes calling the getc's become dependant on there being a character there.
Or:
Code: |
while(kbhit())
getc();
|
Will just read anything that is waiting, then immediately return.
Honestly though I have to say consider interrupt driven receive.
Best Wishes |
|
|
notbad
Joined: 10 Jan 2013 Posts: 68
|
|
Posted: Thu Sep 26, 2013 3:13 am |
|
|
Code: |
while(kbhit())
getc();
|
This works. Thanks.
Can you please answer my other question. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19513
|
|
Posted: Thu Sep 26, 2013 3:37 am |
|
|
No. This is down to the phase in the instruction cycle, when things happen. The PIC does the transfer to the output register from the holding register _before_ status bits are tested in the instruction cycle.
Hence you can just test TRMT.
Best Wishes |
|
|
RF_Developer
Joined: 07 Feb 2011 Posts: 839
|
|
Posted: Thu Sep 26, 2013 4:10 am |
|
|
Ttelmah wrote: | A line going low, will give a zero character.
|
Hmm, a line going low should give a framing error as there's no stop bit. That suggests that some necessary error handling is not going on, or is giving unexpected or un-thought-about results. I've not looked into precisely what does happen with various error conditions, but it's plausible that when using "errors" in the #use rs232 as the default error handling will do something simple like resetting the UART after the error but may then pass a character, a null seems the most likely default, to the user's code. I'd expect only one however as there wouldn't be a start bit to start reception of any more (spurious) characters. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19513
|
|
Posted: Thu Sep 26, 2013 7:37 am |
|
|
It'll give a zero, and the framing error bit set in RS232_ERRORS.
He is not testing this bit, so as far as his code is concerned, it'll just give zero.
Best Wishes |
|
|
|