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

Problem using #INT_RDA and #INT_TBE for RS232 communication
Goto page 1, 2  Next
 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
ElectronPIC



Joined: 26 Dec 2016
Posts: 9

View user's profile Send private message

Problem using #INT_RDA and #INT_TBE for RS232 communication
PostPosted: Mon Dec 26, 2016 10:56 am     Reply with quote

Hello everybody!

I'm relatively new on C programming on PICs and I'm having problems in getting RS232 communication working right. So any help I can get would be appreciated.

What I want to do is to send a chain of commands to the PIC from a PC through RS232 and getting a reply on the PC.
I'm using the #INT_RDA interrupt to receive characters and save them in a buffer called BufferIn. I'm using the #INT_TBE interrupt to send the characters of the reply.

When the reception buffer (BufferIn) is full, I disable the #INT_RDA interrupt until all commands in the buffer are decoded. So I accept that some commands can be lost.

In principle, seems to work well. But the problem is that SOMETIMES, after that BufferIn gets full and all commands are decoded, when reception restarts there are some garbage characters that appears at the beginning of the buffer. I don't know why is this happening.

For the test, I'm using chains of "commands" wich are ended with a line feed character <LF>: [A<LF>BB<LF>CCC<LF>...GGGGGGG<LF>].
When I send four chains (from A<LF> to GGGGGGG<LF>), sometimes appears the garbage characters I'v mentioned.

The code I'm using is this:

Code:

#include <RS232-TxRx.h>                                                                                                           
#include <stdlib.h>                                                             
#include <stdlibm.h>                                                             


#define CommandLength 10                                                       
#define BufferInLength 20                                                       
#define BufferOutLength 500                                                           


char * BufferIn;
int iW_BI=0, iR_BI=0;
int CommandsToDecode=0;
char * BufferOut;                                                                                                                     
char * command;
char * reply;                                                                                                                                               
int iR_BO=0;                                                                 
int ExecuteSending=0;                                                             
char * message1;
char * message2;





#inline                                                                         
void ISRD_RDA_DecodeCommands(void)                                                   
{
   int i;
   
   i=iR_BI;   
   while (BufferIn[iR_BI]!=0x0A)
   {
      command[iR_BI-i]=BufferIn[iR_BI];
      iR_BI++;     
   }
   command[iR_BI-i]=0x00;
   sprintf(reply,"%s\r\n",command);
   if (reply[0]!=0x00)  {ExecuteSending=1;} 
   
   
   sprintf(message1,"[%s]\r\n",BufferIn); 
   if (message1[0]!=0x00)  {ExecuteSending=1;}   


   iR_BI++;                                                 
   CommandsToDecode--;
   
   if ((iW_BI==BufferInLength) && (!CommandsToDecode))                               
   {                                                                               
      if (message2[0]==0x00)   {sprintf(message2,"FULL\r\n");}
 
      iW_BI=0; iR_BI=0;                                     
      while (kbhit())   {BufferIn[0]=getc();}                             
      clear_interrupt(INT_RDA); enable_interrupts(INT_RDA);                   
   }                                                                             

   if (iW_BI<=iR_BI)    {iW_BI=0; iR_BI=0;}
}


#INT_RDA
void ISR_RDA_CharactersReception(void)
{       
   BufferIn[iW_BI]=getc();                                               
   iW_BI++;                                                                   
   
   if (BufferIn[iW_BI-1]==0x0A)  {CommandsToDecode++;}                                                                           

   if (iW_BI==BufferInLength)                                                   
   {                                                                                         
      if (CommandsToDecode)   {disable_interrupts(INT_RDA);}                         
      else                    {iW_BI=0; iR_BI=0; CommandsToDecode=0;}                   
   }                                                                           
}

#INT_TBE
void ISR_TBE_CharactersSending(void)
{                                                 
   putc(BufferOut[iR_BO]); 
   iR_BO++;                                                                     
   ExecuteSending=0;                                                             
                                                                                           
   if (BufferOut[iR_BO]==0x00)
   {
      disable_interrupts(INT_TBE);                                             
      ExecuteSending=1;                                                           
   }   
}

#inline
void ISRD_TBE_CharactersSending(void)                                           
{                                                                                                                                                                                                                                                                                             
   if ((BufferOut[0]==0x00) || (BufferOut[iR_BO]==0x00))
   {           
      BufferOut[0]=0x00;
      iR_BO=0;
     
      if       (reply[0]!=0x00)     {strcat(BufferOut,reply); reply[0]=0x00;}
      if       (message1[0]!=0x00)  {strcat(BufferOut,message1); message1[0]=0x00;}
      if       (message2[0]!=0x00)  {strcat(BufferOut,message2); message2[0]=0x00;}
         
      if (BufferOut[0]!=0x00)    {enable_interrupts(INT_TBE);}
      else                       {ExecuteSending=0;}                                         
   }
}                                                                               


void main(void)                                                                 
{
   printf("*RESET*\r\n");   
   
   enable_interrupts(INT_RDA);                                                 
   enable_interrupts(GLOBAL);                                                   
   setup_wdt(WDT_ON);                                                                                                                                               

   BufferIn=malloc(BufferInLength+1);
   BufferIn[BufferInLength]=0x00;
   command=malloc(CommandLength);
   reply=malloc(BufferInLength);
   BufferOut=malloc(BufferOutLength);
   for (int16 i=0; i<BufferOutLength;i++)  {BufferOut[i]=0x00;}   
   

   message1=malloc(BufferInLength+5);
   message1[BufferInLength+5]=0x00;
   message2=malloc(7);
   message2[7]=0x00;
   
   reply[0]=0x00;
   BufferOut[0]=0x00;
   message1[0]=0x00;
   message2[0]=0x00;
   

   while (TRUE)                                                                     
   {                                                                             
      restart_wdt();                                                           

      if (ExecuteSending)                                                       
      {ISRD_TBE_CharactersSending();}                                           

      if (CommandsToDecode && (reply[0]==0x00))                           
      {ISRD_RDA_DecodeCommands();}                                         
   }                                                                             
}



The RS232 configuration is:
Code:

#use rs232(baud=115200,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8,stream=PORT1,ERRORS)


I'm using version 5.015 of CCS and I'm programming on a PIC18F2550.
Hope somebody can tell me what is happening or what am I doing wrong. Thanks in advance.
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Mon Dec 26, 2016 11:06 am     Reply with quote

The UART has a 2-deep receive fifo and the incoming shift-register.
It will still load up with characters, even if you shut off interrupts.
To clear the buffer, see the clear_uart() macro by Ttelmah in this post:
http://www.ccsinfo.com/forum/viewtopic.php?t=55147&start=7&highlight=clear_uart
ElectronPIC



Joined: 26 Dec 2016
Posts: 9

View user's profile Send private message

PostPosted: Mon Dec 26, 2016 11:13 am     Reply with quote

PCM programmer wrote:
The UART has a 2-deep receive fifo and the incoming shift-register.
It will still load up with characters, even if you shut off interrupts.
To clear the buffer, see the clear_uart() macro by Ttelmah in this post:
http://www.ccsinfo.com/forum/viewtopic.php?t=55147&start=7&highlight=clear_uart


Thanks for your reply! I will watch that post.
ElectronPIC



Joined: 26 Dec 2016
Posts: 9

View user's profile Send private message

PostPosted: Mon Dec 26, 2016 3:39 pm     Reply with quote

Well, I've looked at the post and I think I'm doing the same thing to clear the buffer before enabling it. The problem persists. When I send a long string of commands, when I start receiving characters again the garbage characters appear at the beginning of the buffer.
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Mon Dec 26, 2016 4:02 pm     Reply with quote

I didn't actually look at your program before. I just read your symptoms
and thought of the usual solution.

I didn't look closely at your code, but here are some suggestions.

1. Post your #fuses statement, and anything else near the top of your
program which is not shown.

2. Disable the Watchdog.

3. Get rid of malloc code as a test. Substitute global arrays. Declare
the size of the arrays. My point is, you've got a ton of malloc's.
Do we know that malloc works 100% in vs. 5.015 ? I don't know it.
temtronic



Joined: 01 Jul 2010
Posts: 9174
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Mon Dec 26, 2016 5:47 pm     Reply with quote

I'd like to see what happens when you use the CCS supplied examples of ISR for both xmt and rcv. I know ex_sisr.c works well....

Just a thought......easy enough to merge/compile/test....

I have to 'second' the 'get rid of the WDT' !

Jay
asmboy



Joined: 20 Nov 2007
Posts: 2128
Location: albany ny

View user's profile Send private message AIM Address

PostPosted: Mon Dec 26, 2016 8:53 pm     Reply with quote

stop disabling the RDA and TBE interrupts.
then
Try to just do a clever job of parsing your RX buffer instead.
see if that helps. consider making bufferin 32
and do you really need such a big buffer out ??
based on when /what you transmit -why so big ??
look at the ccs examples ...
Ttelmah



Joined: 11 Mar 2010
Posts: 19368

View user's profile Send private message

PostPosted: Tue Dec 27, 2016 12:29 am     Reply with quote

RDA interrupt agree wholeheartedly.
However the TBE interrupt _has_ to be disabled when there is nothing to send.
Honestly leave INT_RDA enabled, set a flag when the command is seen, and do the parsing in the main code.
Consider 'swap buffers'. Have two RDA buffers. Have a flag to say which one is to be stored to. Then INT_RDA stores data into buffer[0]. When the command complete is seen, set the flag to say 'command seen', and start storing to the beginning of buffer[1]. In the main code parse the command from the buffer that is not being saved to.
kieranjefferies



Joined: 22 Dec 2016
Posts: 5
Location: Ireland

View user's profile Send private message

re> Problem using #INT_RDA and #INT_TBE for RS232...
PostPosted: Wed Dec 28, 2016 7:19 pm     Reply with quote

Hi, this is my first post here and hopefully I am replying to the topic...
Problem using #INT_RDA and #INT_TBE for RS232 communication.

I've been using CCS since way back when the compiler was distributed on floppy disk. I'm on 5.066 now Smile.

Before I ask my question, I'd like to say a big THANK YOU to PCM_programmer, Ttelmah, temtronic, asmboy and all the other super contributers to this forum for all your posts, insight and help down through the years...

My question is on the use of #INT TBE. I've never used it yet religiously use #INT RDA to construct incoming packets and I guess I've been relying on the compiler to handle the transmission of packets of data. My question is... when would you guys use the #INT TBE ?
Thanks
Kieran
Ttelmah



Joined: 11 Mar 2010
Posts: 19368

View user's profile Send private message

PostPosted: Thu Dec 29, 2016 2:32 am     Reply with quote

I use it all the time.

Have circular receive buffers on all incoming serials and similar buffers on all transmits.
Look at the example ex_stisr.c
Rewrite this not using '%' (a search here will find details on this), or always use 'binary' buffer sizes (4,8,16 etc.), and it is a good routine. It waits if the buffer goes 'full'.
newguy



Joined: 24 Jun 2004
Posts: 1903

View user's profile Send private message

PostPosted: Thu Dec 29, 2016 7:11 am     Reply with quote

I too use it all the time. That said, in my opinion it's the most "dangerous" interrupt because a mistake on your part will result in the PIC locking up.

As for why it's necessary? If you use a standard transmit routine (no TBE interrupt), you're effectively causing the PIC to completely stop what it's doing while it transmits. Even if you're using interrupts, the PIC can't attend to their "main loop" processing because it's effectively locked, focused on the transmit task.

...In my mind, just as big of a sin as using floats unnecessarily.
kieranjefferies



Joined: 22 Dec 2016
Posts: 5
Location: Ireland

View user's profile Send private message

PostPosted: Thu Dec 29, 2016 8:10 am     Reply with quote

Thanks Guys. Yes I can see in the assembly code of putc where the PIC is tied up waiting for TXIF to be set. My Tx and Rx routines have always been structured with start of transmission, command, n data bytes, checksum and end of transmission, all packets are validated on both ends of the link and yesterday when I saw the reference to #INT TBE, I was wondering how am I getting away without using it - and why would I use it... so both questions now answered.. putc looked after the timing and no TBE IRQ ties of the PIC during packet transmission,

Thanks again
Kieran
ElectronPIC



Joined: 26 Dec 2016
Posts: 9

View user's profile Send private message

PostPosted: Thu Dec 29, 2016 3:12 pm     Reply with quote

Thank you all for your answers!

PCM programmer wrote:
I didn't actually look at your program before. I just read your symptoms
and thought of the usual solution.

I didn't look closely at your code, but here are some suggestions.

1. Post your #fuses statement, and anything else near the top of your
program which is not shown.

2. Disable the Watchdog.

3. Get rid of malloc code as a test. Substitute global arrays. Declare
the size of the arrays. My point is, you've got a ton of malloc's.
Do we know that malloc works 100% in vs. 5.015 ? I don't know it.


I followed all your suggestions and I stopped having garbage characters. But then I modified the code a bit and the problem reappeared.

To summarize, after doing several tests I realized that my code had some errors.
On the other hand, I was having problems with the strcat () statements. I'm not sure, but I think it has to do with the issue of atomic operations and the #INT_TBE interrupt.

Anyway, your suggestions were useful to me to advance in the search of errors.
Regarding the deactivation of the watchdog that several recommend, is there any particular reason to do so? Usually bring problems?
Thanks for your help.


temtronic wrote:
I'd like to see what happens when you use the CCS supplied examples of ISR for both xmt and rcv. I know ex_sisr.c works well....

Just a thought......easy enough to merge/compile/test....

I have to 'second' the 'get rid of the WDT' !

Jay


I have looked at the example and in effect my intention is to implement a circular buffer as it is done there. But before I wanted to study the behavior of the code when the buffer is full (whether circular or not).
I ask you the same question about the watchdog, does it usually bring problems? Thanks!


asmboy wrote:
stop disabling the RDA and TBE interrupts.
then
Try to just do a clever job of parsing your RX buffer instead.
see if that helps. consider making bufferin 32
and do you really need such a big buffer out ??
based on when /what you transmit -why so big ??
look at the ccs examples ...


I have tried both ways: by disabling and re-enabling the RDA interrupt, and by overwriting the last character of the buffer. Currently (after correcting my mistakes) both work well.

But as you point out, in the examples it is done by overwriting the last character of the buffer. Is there a known reason to avoid disabling the RDA interrupt?

Regarding the output buffer, you're right. I do not really need such a large buffer, I just set it to that size for testing.
What I am looking for is to send a very large string of commands, causing the input buffer to fill several times during the process. With a large output buffer I can see what and how many commands are lost in the process, and the contents of the input buffer at any given time.
Thank you!


Ttelmah wrote:
RDA interrupt agree wholeheartedly.
However the TBE interrupt _has_ to be disabled when there is nothing to send.
Honestly leave INT_RDA enabled, set a flag when the command is seen, and do the parsing in the main code.
Consider 'swap buffers'. Have two RDA buffers. Have a flag to say which one is to be stored to. Then INT_RDA stores data into buffer[0]. When the command complete is seen, set the flag to say 'command seen', and start storing to the beginning of buffer[1]. In the main code parse the command from the buffer that is not being saved to.


Your idea of ​​"swap buffers" is very good. I'm going to try it, thank you!
About not disabling RDA interrupt, why is it so important?


Again, thank you all for your answers and comments!
temtronic



Joined: 01 Jul 2010
Posts: 9174
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Thu Dec 29, 2016 3:54 pm     Reply with quote

re: WDT

WDT should be disabled until you're 100% confident your code is correct and 'market ready' THEN enable the WDT and see what happens....
WDT testing could easily take another week or 2....

It needs to be disabled(off) during development as any 'quirk' in your code or even a compiler 'bug' _might_ go 'funny' causing the WDT to trigger when really it's YOUR code , not the WDT that reset the PIC.

Nothing can cause more grief than trying to figure out what's gone 'wrong' when something else leads you to the wrong path or conclusion...


Jay
kieranjefferies



Joined: 22 Dec 2016
Posts: 5
Location: Ireland

View user's profile Send private message

PostPosted: Thu Dec 29, 2016 3:58 pm     Reply with quote

Hi ElectronPIC, the reason you don't want to disable the RDA Interrupt is basically, you have no control over when bytes of data will arrive at the receiver input and if you don't read in and process / parse these byes before the arrival of subsequent bytes, they are over-written and lost. You could implement a solution without the RDA Interrupt if you had a very low baud rate, delays between subsequent bytes etc and dedicate most of your program resources to monitoring the serial port flags. But once you master the use of the RDA IRQ, you can create elegant and efficient routines which handle all communications reception with minimal impact on your main program.... the reception of data happens in the background. You can bring this to the ultimate level where the main program loop is notified of the reception of a complete & verified packet of data...
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Goto page 1, 2  Next
Page 1 of 2

 
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