|
|
View previous topic :: View next topic |
Author |
Message |
Linuxbuilders
Joined: 20 Mar 2010 Posts: 193 Location: Auckland NZ
|
rs232 collision detection |
Posted: Sun Apr 05, 2015 2:03 am |
|
|
Hey,
I am using UART for RS485, I am using custom written protocol.
I have a buffer of 200 spaces, I load it with each CHAR as it comes from the bus (later on I will cut it into 10 of 20 CHAR chunks).
Here is the question, I want to make the protocol quite robust, so lets say if I have a corruption on the line from foreign sources which do clash with flowing data I need to reset the UART. Currently if I inject data over flowing data I crash the bus and cause the device to hang.
Any suggestions how to do what I need?
Code: |
definitions
...
#use rs232(baud=busspeed,xmit=TX,rcv=RX,parity=n,bits=8,stop=1,enable=TX_ON,RESTART_WDT,ERRORS,TIMEOUT=15,DISABLE_INTS)
...
uart
...
#INT_RDA HIGH
void comms_rda1(VOID)
{
BYTE c = 0;
restart_wdt(); // reset watchdog
rda1_RX_busy = 1;
c =getc();
rda1capture[rda1_counter] = c; // load CHAR into temporary buffer
rda1_counter++; // advance the buffer
rda1_RX_busy = 0; // RX free
} //VOID
...
in the main loop
...
IF (rda1_counter >= 20)
{
if ((rda1capture[0] == ADDH) && (rda1capture[1] == ADDL))
{
memcpy (message, rda1capture, 20);
MSG_READY = 1;
}
rda1_counter = 0;
}
...
Thank u 4 help. |
_________________ Help "d" others and then you shell receive some help from "d" others. |
|
|
Linuxbuilders
Joined: 20 Mar 2010 Posts: 193 Location: Auckland NZ
|
|
Posted: Sun Apr 05, 2015 2:07 am |
|
|
PS. By the way I do not crash the device in strict word meaning, it works, just does not process the data correctly anymore.
thnx _________________ Help "d" others and then you shell receive some help from "d" others. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19492
|
|
Posted: Sun Apr 05, 2015 2:46 am |
|
|
Uuurgh.
A horrible example of how to not use the watchdog.....
Do some searching on this.
Also get rid of DISABLE_INTS in your RS232 declaration. This is used, when you are handling a _software_ UART, and the compiler must not interrupt to do other things during a character. With the hardware UART, it is not wanted, and is potentially dangerous.
Then also get rid of TIMEOUT. This won't actually do anything in your code. TIMEOUT sets the code up, so if you go and sit in getc, and a character does not arrive, the compiler will timeout and let the code continue (with a flag to say this has happened). When using INT_RDA, this is not wanted, since the interrupt only occurs, _when a character is already waiting_.....
You only use this when you wait in a getc. And then you should test if it has happened. (returned character ==0, and RS232_errors ==0).
I'd suspect your problem may be nothing to do with the UART, but your code.
If (for instance) an incorrect packet arrives, what is to prevent rda1_counter from incrementing past the end of the buffer?. This would then cause chaos in the RAM, and code failure....
Look at how ex_sisr.c handles it's buffer. A _circular_ buffer that can never dangerously overflow.
At the minimum, add a test in the handler, like:
Code: |
if (++rda1_counter==200)
rda_counter=199; //lock at the maximum
|
The compiler _will_ automatically reset the UART if an error, is seen.
However if you want to know this has happened, after the getc, read the variable 'rs232_errors'. Bit 1 of this reflects an overrun error, and bit 2, a framing error after the character has been read. The compiler _will_ have already reset the UART from these errors, but you shouldn't receive the bytes as legitimate values. |
|
|
Linuxbuilders
Joined: 20 Mar 2010 Posts: 193 Location: Auckland NZ
|
|
Posted: Sun Apr 05, 2015 3:07 am |
|
|
Thnx, I will go for it. _________________ Help "d" others and then you shell receive some help from "d" others. |
|
|
Linuxbuilders
Joined: 20 Mar 2010 Posts: 193 Location: Auckland NZ
|
|
Posted: Sun Apr 05, 2015 4:20 am |
|
|
This works,
Code: |
#use rs232(baud=busspeed,xmit=TX,rcv=RX,parity=n,bits=8,stop=1,enable=TX_ON,RESTART_WDT,ERRORS)
#INT_RDA HIGH // first UART
void comms_rda1(VOID)
{
BYTE c = 0;
rda1_RX_busy = 1;
c = getc();
rda1capture[rda1_counter] = c; // load CHAR into temporary buffer
rda1_counter++; // advance the buffer
if (rda1_counter == 201)
{
rda1_counter = 200;
buffer_full = 1;
}
rda1_RX_busy = 0; // RX free
} //VOID
if (buffer_full != 0)
{
rda1_counter = 0;
MSG_READY = 0;
clrsrcn();
lcd_putc ("\fBUFFER FULL");
lcd_putc ("\nRESET to ZERO");
delayms(1000);
}
IF (rda1_counter >= 20)
{
memcpy (message, rda1capture, 200);
if ((message[0] == ADDH) && (message[1] == ADDL))
{
MSG_READY = 1;
}
printf ("%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c",message[0],message[1],message[2],message[3],message[4],message[5],message[6],message[7],message[8],message[9],message[10]
,message[11],message[12],message[13],message[14],message[15]);
printf ("\r\n");
printf ("%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c",message[16],message[17],message[18],message[19],message[20],message[21],message[22],message[23],message[24],message[25],message[26]
,message[27],message[28],message[29],message[30],message[31]);
printf ("\r\n");
rda1_counter = 0;
}
|
thnx _________________ Help "d" others and then you shell receive some help from "d" others. |
|
|
Linuxbuilders
Joined: 20 Mar 2010 Posts: 193 Location: Auckland NZ
|
|
Posted: Tue Apr 07, 2015 5:08 am |
|
|
Warning:
The PIC UART will shut down on overflow (3 characters received by the hardware with a GETC() call). The
"ERRORS" option prevents the shutdown by detecting the condition and resetting the UART.
That is my problem, when I manually crash the bus I get UART shutting down, it then is stuck on last received CHAR. INT kicks on but no data is read from the port.
How do I read the errors from RS232_ERRORS and how do I reset the UART then?
Thnx 4 help _________________ Help "d" others and then you shell receive some help from "d" others. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19492
|
|
Posted: Tue Apr 07, 2015 6:16 am |
|
|
RS232_ERRORS is just a variable. You can read it like any other variable.
When you call getc, the compiler will automatically clear the error (provided you have ERRORS in the #USE RS232 as you show).
have you tried without the spurious entries in your #USE RS232?. As I said, the disable_ints should not be there for an interrupt driven handler, and the timeout option also should not be there.
Are you sure this is the problem (I doubt it)?. What is the compiler version?.
There was a problem in the late V4, and early V5 compilers, where the global interrupt could become disabled after using a printf. This would hang the RS232 reception. Solution, just add an enable_interrupts(GLOBAL) after any call to printf. Have you tried without using high priority on the interrupt?. If you are using INT_EXT, this won't function as you may expect, and could cause problems. |
|
|
Linuxbuilders
Joined: 20 Mar 2010 Posts: 193 Location: Auckland NZ
|
|
Posted: Wed Apr 08, 2015 3:01 am |
|
|
Hey,
I have just bough latest compiler 5.044.
You right, I think I was wrong yesterday.
What I do is:
1. I have machine sending me regular 20 char messages, then on the line I have PC and I jump in the middle of the tx from the machine, it is slow 4800 speed so I can disturb it.
2. This way I create rubbish in the middle of TX.
3. Then my RX on the other machine goes spam up. My counter goes high why for some reason and the whole string is shifting randomly up to whatever it feels like.
4. Then the whole ram gets corrupted and everything goes crazy.
I need to work out my code as it is rubbish.
If you do not mind to let me know how the errors work out and I can understand it then I can see if this will be any use to me.
CCS datasheet is not very generous on this topic
Thnx for help. _________________ Help "d" others and then you shell receive some help from "d" others. |
|
|
RF_Developer
Joined: 07 Feb 2011 Posts: 839
|
|
Posted: Wed Apr 08, 2015 3:22 am |
|
|
Quote: | I want to make the protocol quite robust |
From what little I can see, you haven't really got a "protocol", let alone it being robust.
With RS485 being half-duplex, true clash detection is very difficult. Clash detection requires transmitters to listen while they transmit and detect errors as they happen. Instead, you're going to have to do receive error detection.
You need to deal with errors detected by the hardware. That won't do very much - you need to do far more to be able to consider your comms "robust". To be truly robust, you're going to have to "frame" your messages into structured packets that include some error detection mechanism. You are going to have to work out some error recovery mechanism, such as some form of ARQ.
All that can be done, its straightforward data comms coding. I've done it often, and use a simple framing and error detection scheme on a lot of my processor-processor comms.
Often, however, its easier and produces a much better (i.e. faster, more robust) result to use an already existing protocol. When I last used RS485 (between PCs and modules) I went for the old, but still functional MODBUS, as it has the protocol, error detection and recovery stuff sorted, so I don't have to re-invent it... badly. Other protocols are available, of course :-) |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19492
|
|
Posted: Wed Apr 08, 2015 3:23 am |
|
|
The UART has a number of error bits available (number depends on the PIC). Typically, overrun error, framing error, parity error (software only on the smaller PICs).
When you can 'getc', if ERRORS is set, the error bits in the receive status register are first copied to the RS232_ERRORS variable. Then the character is read. If you have parity enabled, on the standard PIC's the compiler then calculates, and checks this, and adds this bit to the errors variable.
When it reads RCREG, it checks the OERR bit. This is the only one that can hang the UART. If three characters arrive, and the buffer is not read, then the UART receiver will switch itself off, and set this bit. The receive interrupt remains set. It should basically be impossible to trigger this error if using interrupt driven RX (unless you sit for a while with the interrupt disabled). However as soon as the interrupt is re-enabled and the handler is called, and calls getc, this bit will be seen as set. The compiler when it sees it, turns off CREN (continuous receive enable), which _disables_ the receive part of the UART only, reads the character, clears OERR, and re-enables CREN. |
|
|
Linuxbuilders
Joined: 20 Mar 2010 Posts: 193 Location: Auckland NZ
|
|
Posted: Wed Apr 08, 2015 3:28 am |
|
|
True,
I have written something like that and it was working quite well. structures, encrypted with parity, etc...
However I have never tried to crash it, it was fairly good on its own as it was dropping all unknown messages.
What I do not is stupid simple where most of people fails .
LCD screen displaying text, idiot easy. And to make it funny it turns over the hard one . So far I could isolate the problem to my buffer shifting. I guess I need to work it out. Frame error detection, etc. could help in preliminary checks.
thnx _________________ Help "d" others and then you shell receive some help from "d" others. |
|
|
Linuxbuilders
Joined: 20 Mar 2010 Posts: 193 Location: Auckland NZ
|
|
Posted: Wed Apr 08, 2015 4:35 am |
|
|
I have worked it out but posting so others do not run in circles here
PIC18F4620
Code: |
int message[20];
int rda1_counter = 0;
short MSG_READY = 0;
#INT_RDA // first UART
void comms_rda1(VOID)
{
BYTE c = 0;
c = getc();
message[rda1_counter] = c;
IF (rda1_counter == 19)
{
MSG_READY = 1;
}
rda1_counter++; // advance the buffer
IF (rda1_counter > 19)
{
rda1_counter = 0;
}
} //VOID |
So I send 20 chars in one go - it works well, no problem, keeps counter on 0 each time as we shoot 20 chars and nicely reset over 20 back to 0.
- Now I send one char, counter is advancing to 1.
- Then I send 20 chars and my buffer is loading with the string but not moving the counter (it actually does but because I send 20+ loaded 1 then I reset it to 0 and add 1 so it stays on 1). If I now send 19 instead of 20 then I get it back to 0. So the counter and the buffer circles from zero to 20 and depending on the length of received packet goes around as many times as the chars are in the packet. That is why when I manually break the circle and inject foreign chars into the TX then my buffer is shifting, losing its proper count and my message is shifted from left to right
Code: | IF (rda1_counter > 19)
{
rda1_counter = 0;
} |
This will reset the counter over 20 chars. So the counter should be always on 0 regardless of how long is the incoming TX. It will just reload everything over 19 back on from 0 in a loop and stop on the starting point, so if it was 5 it will be back on 5.
So what I need here is a clock in the protocol so each char is marked with start and stop point which I did have in my old protocol but stopped using it now... I guess I get back here
like: START-20 chars-STOP
thnx for help. _________________ Help "d" others and then you shell receive some help from "d" others. |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|