View previous topic :: View next topic |
Author |
Message |
ElectronPIC
Joined: 26 Dec 2016 Posts: 9
|
Problem using #INT_RDA and #INT_TBE for RS232 communication |
Posted: Mon Dec 26, 2016 10:56 am |
|
|
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
|
|
|
ElectronPIC
Joined: 26 Dec 2016 Posts: 9
|
|
Posted: Mon Dec 26, 2016 11:13 am |
|
|
Thanks for your reply! I will watch that post. |
|
|
ElectronPIC
Joined: 26 Dec 2016 Posts: 9
|
|
Posted: Mon Dec 26, 2016 3:39 pm |
|
|
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
|
|
Posted: Mon Dec 26, 2016 4:02 pm |
|
|
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: 9246 Location: Greensville,Ontario
|
|
Posted: Mon Dec 26, 2016 5:47 pm |
|
|
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
|
|
Posted: Mon Dec 26, 2016 8:53 pm |
|
|
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: 19552
|
|
Posted: Tue Dec 27, 2016 12:29 am |
|
|
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: 6 Location: Ireland
|
re> Problem using #INT_RDA and #INT_TBE for RS232... |
Posted: Wed Dec 28, 2016 7:19 pm |
|
|
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 .
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: 19552
|
|
Posted: Thu Dec 29, 2016 2:32 am |
|
|
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: 1909
|
|
Posted: Thu Dec 29, 2016 7:11 am |
|
|
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: 6 Location: Ireland
|
|
Posted: Thu Dec 29, 2016 8:10 am |
|
|
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
|
|
Posted: Thu Dec 29, 2016 3:12 pm |
|
|
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: 9246 Location: Greensville,Ontario
|
|
Posted: Thu Dec 29, 2016 3:54 pm |
|
|
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: 6 Location: Ireland
|
|
Posted: Thu Dec 29, 2016 3:58 pm |
|
|
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... |
|
|
|