CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to support@ccsinfo.com

rs232 collision detection

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
Linuxbuilders



Joined: 20 Mar 2010
Posts: 193
Location: Auckland NZ

View user's profile Send private message

rs232 collision detection
PostPosted: Sun Apr 05, 2015 2:03 am     Reply with quote

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

View user's profile Send private message

PostPosted: Sun Apr 05, 2015 2:07 am     Reply with quote

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: 19451

View user's profile Send private message

PostPosted: Sun Apr 05, 2015 2:46 am     Reply with quote

Uuurgh.

A horrible example of how to not use the watchdog.....
Do some searching on this. Evil or Very Mad

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

View user's profile Send private message

PostPosted: Sun Apr 05, 2015 3:07 am     Reply with quote

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

View user's profile Send private message

PostPosted: Sun Apr 05, 2015 4:20 am     Reply with quote

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

View user's profile Send private message

PostPosted: Tue Apr 07, 2015 5:08 am     Reply with quote

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: 19451

View user's profile Send private message

PostPosted: Tue Apr 07, 2015 6:16 am     Reply with quote

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

View user's profile Send private message

PostPosted: Wed Apr 08, 2015 3:01 am     Reply with quote

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 Smile

Thnx for help.
_________________
Help "d" others and then you shell receive some help from "d" others.
RF_Developer



Joined: 07 Feb 2011
Posts: 839

View user's profile Send private message

PostPosted: Wed Apr 08, 2015 3:22 am     Reply with quote

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: 19451

View user's profile Send private message

PostPosted: Wed Apr 08, 2015 3:23 am     Reply with quote

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

View user's profile Send private message

PostPosted: Wed Apr 08, 2015 3:28 am     Reply with quote

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 Smile.

LCD screen displaying text, idiot easy. And to make it funny it turns over the hard one Smile. 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

View user's profile Send private message

PostPosted: Wed Apr 08, 2015 4:35 am     Reply with quote

I have worked it out but posting so others do not run in circles here Smile

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 Smile

like: START-20 chars-STOP

thnx for help.
_________________
Help "d" others and then you shell receive some help from "d" others.
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
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