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

Avoiding GOTO/LABEL in coding...
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
ELCouz



Joined: 18 Jul 2007
Posts: 427
Location: Montreal,Quebec

View user's profile Send private message

Avoiding GOTO/LABEL in coding...
PostPosted: Fri Jan 30, 2015 4:59 pm     Reply with quote

I need to read the following data from serial :

Code:


First Char  (Any  4 letters) | Second Char (Any  4 letters) | Value

R                            | A                            | 0000 to 4095 (in ASCII)
G                            | B                            | 0000 to 4095 (in ASCII)
B                            | C                            | 0000 to 4095 (in ASCII)
W                            | D                            | 0000 to 4095 (in ASCII)

Example data from serial (all in ASCII) --> RA2544GA0032BA0000WA4000





In state machine programming that code would looks like (ugly):

Code:


int8 packetcolor,packetstep,packetchannel,packetvalue;
int1 canread;

#INT_RDA
void serial_isr() {
int8 rxchar;

rxchar=getc();

start: //label for start of the sorting

if (packetstep == 0){ //first step read the first char (R/G/B or W)

   switch (rxchar){
      case 'R' :packetcolor=1;
      packetstep = 1;
      break;
      case 'G' :packetcolor=2;
      packetstep = 1;
      break;
      case 'B' :packetcolor=3;
      packetstep = 1;
      break;
      case 'W' :packetcolor=4;
      packetstep = 1;
      break;
      default:packetstep = 0; //stay at 0 if not RGBW (to restart trying capturing the first char)
      break;
   }
   
}

if (packetstep == 2){ //second step try to read the second char

   switch (rxchar){
      case 'A' :packetchannel=1;
      packetstep = 3;
      break;
      case 'B' :packetchannel=2;
      packetstep = 3;
      break;
      case 'C' :packetchannel=3;
      packetstep = 3;
      break;
      case 'D' :packetchannel=4;
      packetstep = 3;
      break;
      default:packetstep = 0;
      Goto start;//stay at 0 and restart the sorting (second chance catching the first char [RGBW] if not ABCD) by going at the beginning of the interrupt
      break;
   }
}

if (packetstep == 3){ //third step getting values

   switch (rxchar){
      case '0' : //store number
      packetstep = 4;
      break;
      case '1' ://store number
      packetstep = 4;
      break;
      case '2' ://store number
      packetstep = 4;
      break;
      case '3' ://store number
      packetstep = 4;
      break;
      default:packetstep = 0; //Not a valid number so restart
      Goto start;//stay at 0 and restart the sorting (second chance catching the first char [RGBW] if not a number) by going at the beginning of the interrupt
      break;
   }

}

[...]

Flag the canread to true when everything is finished capturing then read the vars!


     
}





Code is not complete (in fact it's an aberration Twisted Evil )

It's just something that I've written on a piece of paper didn't even try to compile it yet!

I could skip the goto and declare invalid data but I would loose the entire packet until I catch the right first character.

Any solution to chicken or egg problem?

PS: I know it's bad to do this in the INT_RDA interrupt it's just I don't trust the built-in serial buffer from CCS (overwrite new data first) and KISS for testing.
_________________
Regards,
Laurent

-----------
Here's my first visual theme for the CCS C Compiler. Enjoy!
Mike Walne



Joined: 19 Feb 2004
Posts: 1785
Location: Boston Spa UK

View user's profile Send private message

Re: Avoiding GOTO/LABEL in coding...
PostPosted: Fri Jan 30, 2015 5:51 pm     Reply with quote

ELCouz wrote:

I could skip the goto and declare invalid data but I would loose the entire packet until I catch the right first character.
Can you explain more clearly what you want to happen on a restart.
Is there not a problem with second characters having possible hex values?

Mike

EDIT My first thought was to use a data array and only one loop, not 6 code versions for each character.
ELCouz



Joined: 18 Jul 2007
Posts: 427
Location: Montreal,Quebec

View user's profile Send private message

Re: Avoiding GOTO/LABEL in coding...
PostPosted: Fri Jan 30, 2015 5:54 pm     Reply with quote

Mike Walne wrote:
ELCouz wrote:

I could skip the goto and declare invalid data but I would loose the entire packet until I catch the right first character.
Can you explain more clearly what you want to happen on a restart.
Is there not a problem with second characters having possible hex values?

Mike


Sorry if it's not clear Smile


I want to retry capturing the first char hence the goto label (at top of the interrupt)
_________________
Regards,
Laurent

-----------
Here's my first visual theme for the CCS C Compiler. Enjoy!
Mike Walne



Joined: 19 Feb 2004
Posts: 1785
Location: Boston Spa UK

View user's profile Send private message

PostPosted: Fri Jan 30, 2015 5:57 pm     Reply with quote

You mean re-try with the character you've just tested?

Mike
ELCouz



Joined: 18 Jul 2007
Posts: 427
Location: Montreal,Quebec

View user's profile Send private message

PostPosted: Fri Jan 30, 2015 6:19 pm     Reply with quote

Mike Walne wrote:
You mean re-try with the character you've just tested?

Mike


Kind of ... since I've not tested it because it was at a different step...



_________________
Regards,
Laurent

-----------
Here's my first visual theme for the CCS C Compiler. Enjoy!
Ttelmah



Joined: 11 Mar 2010
Posts: 19378

View user's profile Send private message

PostPosted: Sat Jan 31, 2015 1:57 am     Reply with quote

It's classic (and easy) to do.

Given your values are just alpha, have an multi dimensional array with the characters in. "RGBW" for the first row.
Have the 'row number' as a _static_ variable. Set it to zero when declared.

Read the character.
Enclose code in a do loop. do{}while(TRUE);
If row==0.
Then just use a for loop through the column characters in the row. If the character matches 'packetcolor', is set to the column number+1, and the row is incremented. 'Break' out of the loop. If no match also break out.
If row==1.
Next time in since row is incremented, it now looks through the second row in the array. Again if there is a match, 'packetchannel' is set to column+1, and the row is incremented. Again break out of the loop. If however there is no match, simply set 'row' back to 0, and execute a 'continue', which means it'll execute the loop again, with row==0, and repeat the first comparison.
If row == 2, process the first digit of the value, and increment row. Break.
If row == 3, process the second digit of the value. etc...

When you get to the end of the value, set your flag, and remember to set row back to 0.

Just put a core together using this:
Code:

char first[]="RGBW";
char second[]="ABCD";
#inline
int8 find(char what, char * where)
{
   int8 seek; //routine to search for a single character in a four char array
   for (seek=0;seek<4;seek++)
   {
      if (where[seek]==what)
         return seek+1;
   }
   return 0;
}

#inline
int16 fast_ten(int16 x)
{
   int16 temp; //fastest way to multiply by 10 - also doesn't use mult
   temp=x*2;
   return (temp*4)+temp;
}

#INT_RDA
void rx_char(void)
{
   static int8 row=0;
   static int16 build_val;
   int8 temp;
   int8 rxchar;

   rxchar=getc();
   do
   {
      if (row==0)
         if ((temp=find(rxchar,first))==0) //test if zero, and save as well
            break;
         else
         {
            packetcolor=temp;
            row++; //next time check for channel
            break;
         }
      if (row==1)
         if ((temp=find(rxchar,second))==0)
         {
            row=0; //force the re-scan
            continue; //re-scan the character
         }
         else
         {
            packetchannel=temp;
            row++; //now looking for number 
            build_val=0;
            break; //exit
         }
      else
      {
         //Now all the numbers can be handled the same
         if (isdigit(rxchar))
         {
            build_val=fast_ten(build_val);
            build_val=build_val+(rxchar-'0');
            break; //exit
         }
         else
         {
            //here we have an non numeric, so value complete
            packet_value=build_val; //adjust variable names etc here to suit
            flag=TRUE;
            row=0;
            continue; //and scan the text character again
         }
      }
   } while(TRUE);
}


You'll get a warning about doing an assignment in a test, but this is just me avoiding having to transfer the value twice. I put the numeric result into 'packet_value' - don't know what your variable is called, and just set 'flag'.
ELCouz



Joined: 18 Jul 2007
Posts: 427
Location: Montreal,Quebec

View user's profile Send private message

PostPosted: Sat Jan 31, 2015 5:26 pm     Reply with quote

Ttelmah wrote:
It's classic (and easy) to do.

Given your values are just alpha, have an multi dimensional array with the characters in. "RGBW" for the first row.
Have the 'row number' as a _static_ variable. Set it to zero when declared.

Read the character.
Enclose code in a do loop. do{}while(TRUE);
If row==0.
Then just use a for loop through the column characters in the row. If the character matches 'packetcolor', is set to the column number+1, and the row is incremented. 'Break' out of the loop. If no match also break out.
If row==1.
Next time in since row is incremented, it now looks through the second row in the array. Again if there is a match, 'packetchannel' is set to column+1, and the row is incremented. Again break out of the loop. If however there is no match, simply set 'row' back to 0, and execute a 'continue', which means it'll execute the loop again, with row==0, and repeat the first comparison.
If row == 2, process the first digit of the value, and increment row. Break.
If row == 3, process the second digit of the value. etc...

When you get to the end of the value, set your flag, and remember to set row back to 0.

Just put a core together using this:
Code:

char first[]="RGBW";
char second[]="ABCD";
#inline
int8 find(char what, char * where)
{
   int8 seek; //routine to search for a single character in a four char array
   for (seek=0;seek<4;seek++)
   {
      if (where[seek]==what)
         return seek+1;
   }
   return 0;
}

#inline
int16 fast_ten(int16 x)
{
   int16 temp; //fastest way to multiply by 10 - also doesn't use mult
   temp=x*2;
   return (temp*4)+temp;
}

#INT_RDA
void rx_char(void)
{
   static int8 row=0;
   static int16 build_val;
   int8 temp;
   int8 rxchar;

   rxchar=getc();
   do
   {
      if (row==0)
         if ((temp=find(rxchar,first))==0) //test if zero, and save as well
            break;
         else
         {
            packetcolor=temp;
            row++; //next time check for channel
            break;
         }
      if (row==1)
         if ((temp=find(rxchar,second))==0)
         {
            row=0; //force the re-scan
            continue; //re-scan the character
         }
         else
         {
            packetchannel=temp;
            row++; //now looking for number 
            build_val=0;
            break; //exit
         }
      else
      {
         //Now all the numbers can be handled the same
         if (isdigit(rxchar))
         {
            build_val=fast_ten(build_val);
            build_val=build_val+(rxchar-'0');
            break; //exit
         }
         else
         {
            //here we have an non numeric, so value complete
            packet_value=build_val; //adjust variable names etc here to suit
            flag=TRUE;
            row=0;
            continue; //and scan the text character again
         }
      }
   } while(TRUE);
}


You'll get a warning about doing an assignment in a test, but this is just me avoiding having to transfer the value twice. I put the numeric result into 'packet_value' - don't know what your variable is called, and just set 'flag'.


Thank you very much for the help Ttelmah!

Very clean programming Smile

However... ain't the while(TRUE) gonna jam the whole RDA interrupt (less time you spent in interrupt the better it is normally)?
_________________
Regards,
Laurent

-----------
Here's my first visual theme for the CCS C Compiler. Enjoy!
ELCouz



Joined: 18 Jul 2007
Posts: 427
Location: Montreal,Quebec

View user's profile Send private message

PostPosted: Sat Jan 31, 2015 6:38 pm     Reply with quote

Code work but it won't capture the whole data Ttelmah...

Example data: RA3000GA2000BA1000WA0000

Example code (replicating the problem):

Code:


void main() {

enable_interrupts(INT_RDA);
enable_interrupts(GLOBAL);   
printf("PIC18F2550 Ready!\n\r");
   while(true) {
   
   if (canread){
   printf("Got a packet! :) ... Color: %u Channel: %u Value: %Lu \n\r",packetcolor,packetchannel,packetvalue);
   canread = false;
   }
   
   }
}



Will output this:
Code:
Got a packet! :) ... Color: 4 Channel: 1 Value: 0


Not the four packet!

Will only display the last "packet" !


EDIT: the only change I did in your code is replacing FLAG by canread int1 var!

EDIT2: maybe I need a buffer because RDA is too fast for printf!
_________________
Regards,
Laurent

-----------
Here's my first visual theme for the CCS C Compiler. Enjoy!
Ttelmah



Joined: 11 Mar 2010
Posts: 19378

View user's profile Send private message

PostPosted: Sun Feb 01, 2015 1:56 am     Reply with quote

No more so than the goto.

Follow the paths. If it finds the character it _breaks_ (so comes straight out of the loop). Only if it doesn't find the character on the second route, or it hits an alpha character when waiting for the number, does it execute a 'continue' (so loops back and tries the character again).

OK. See you have tried it.

I expected what you are now seeing, but it was not clear from your description how the numbers are sent. Are they _always_ four digits?.
As written, it won't complete the number till it sees a _non numeric_ character.

So as soon as another packet follows it'll complete. If you are confident that there are always four digits, simply count instead. So:
Code:

#INT_RDA
void rx_char(void)
{
   static int8 row=0;
   static int16 build_val;
   static int8 digits;
   int8 temp;
   int8 rxchar;

   rxchar=getc();
   do
   {
      if (row==0)
         if ((temp=find(rxchar,first))==0) //test if zero, and save as well
            break;
         else
         {
            packetcolor=temp;
            row++; //next time check for channel
            break;
         }
      if (row==1)
         if ((temp=find(rxchar,second))==0)
         {
            row=0; //force the re-scan
            continue; //re-scan the character
         }
         else
         {
            packetchannel=temp;
            row++; //now looking for number
            build_val=0;
            digits=4; //four digits to receive
            break; //exit
         }
      else
      {
         //Now all the numbers can be handled the same
         build_val=fast_ten(build_val);
         build_val=build_val+(rxchar-'0');
         if (--digits)
            break; //exit if not zero
         else
         {
            //here we have received four digits
            packet_value=build_val; //adjust variable names etc here to suit
            flag=TRUE;
            row=0;
            break; //and exit
         }
         break;
      }
   } while(TRUE);
}

This will exit after four digits, but will _not_ test that the values are numeric.
ELCouz



Joined: 18 Jul 2007
Posts: 427
Location: Montreal,Quebec

View user's profile Send private message

PostPosted: Sun Feb 01, 2015 7:32 am     Reply with quote

Ttelmah wrote:
No more so than the goto.

Follow the paths. If it finds the character it _breaks_ (so comes straight out of the loop). Only if it doesn't find the character on the second route, or it hits an alpha character when waiting for the number, does it execute a 'continue' (so loops back and tries the character again).

OK. See you have tried it.

I expected what you are now seeing, but it was not clear from your description how the numbers are sent. Are they _always_ four digits?.
As written, it won't complete the number till it sees a _non numeric_ character.

So as soon as another packet follows it'll complete. If you are confident that there are always four digits, simply count instead. So:

{code_removed_to_shorten_the_quote}

This will exit after four digits, but will _not_ test that the values are numeric.


Yes exactly! Exactly 4 digits all the time!

You code works nicely however I still can't catch all the "packets" !

Your new code based on counting exactly detect and store the packet when it reach the last digit... but the output still is read once!


Same serial example: RA3000GA2000BA1000WA0000

Still output:


Code:

Color: 2 Channel: 1 Value: 0


Normally I should get this output:

Code:

Color: 1 Channel: 1 Value: 3000
Color: 2 Channel: 1 Value: 2000
Color: 3 Channel: 1 Value: 1000
Color: 4 Channel: 1 Value: 0


I've tried with the built-in CCS serial buffer --> #use rs232(baud=115200, UART1,errors,RECEIVE_BUFFER = 64)

and throwing your whole RDA code into the kbhit statement in the main loop instead.

I get nothing anymore.

I think the problem is the RDA loop set the flag canread too quickly for the main loop to catch it!

Please correct me if i'm wrong!
_________________
Regards,
Laurent

-----------
Here's my first visual theme for the CCS C Compiler. Enjoy!
temtronic



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

View user's profile Send private message

PostPosted: Sun Feb 01, 2015 7:42 am     Reply with quote

hmm...
Wondering have you tried the CCS ex_sisr.c example program to use a buffer for the data, then parse the buffer?

Also, there might be a 'glitch' in the 'buffer' option in the use rs232(...) depending on the compiler version.

just ideas...

Jay
ELCouz



Joined: 18 Jul 2007
Posts: 427
Location: Montreal,Quebec

View user's profile Send private message

PostPosted: Sun Feb 01, 2015 7:45 am     Reply with quote

temtronic wrote:


Also, there might be a 'glitch' in the 'buffer' option in the use rs232(...) depending on the compiler version.

just ideas...

Jay


Right now I'm on V5.035, I plan to upgrade later when it will stabilize the last 2 version were bad!

Quote:
Wondering have you tried the CCS ex_sisr.c example program to use a buffer for the data, then parse the buffer?


I thought it was the same as the built-in buffer!

Of course, I will try it thanks! Very Happy
_________________
Regards,
Laurent

-----------
Here's my first visual theme for the CCS C Compiler. Enjoy!
ELCouz



Joined: 18 Jul 2007
Posts: 427
Location: Montreal,Quebec

View user's profile Send private message

PostPosted: Sun Feb 01, 2015 7:55 am     Reply with quote

Thanks it work!!


Now the output is:

Code:

Color: 1 Channel: 1 Value: 3000
Color: 2 Channel: 1 Value: 2000
Color: 3 Channel: 1 Value: 1000
Color: 4 Channel: 1 Value: 0


Everything work normally Smile


Seems built-in buffer was buggy... in fact it was driving me nuts!
_________________
Regards,
Laurent

-----------
Here's my first visual theme for the CCS C Compiler. Enjoy!
Ttelmah



Joined: 11 Mar 2010
Posts: 19378

View user's profile Send private message

PostPosted: Sun Feb 01, 2015 8:53 am     Reply with quote

The built in buffer will throw away the _latest_ character if it overflows. Ex_sisr throws the _oldest_. It's a very important difference, and has been commented on here.
You can't use the built in buffer with you using a serial interrupt.
ELCouz



Joined: 18 Jul 2007
Posts: 427
Location: Montreal,Quebec

View user's profile Send private message

PostPosted: Sun Feb 01, 2015 8:57 am     Reply with quote

Ttelmah wrote:
The built in buffer will throw away the _latest_ character if it overflows. Ex_sisr throws the _oldest_. It's a very important difference, and has been commented on here.
You can't use the built in buffer with you using a serial interrupt.


I did remove the RDA interrupt (and enables) since the built-in manage it itself!

I've seen that you noticed long time ago CCS for this buffer silly problem (way it treats the old vs new data)

However I wonder why it didn't changed as today?

What are the advantage of this, why they still want to leave it that way?

Thank you both Ttelmah and temtronic for your great support! Very Happy
_________________
Regards,
Laurent

-----------
Here's my first visual theme for the CCS C Compiler. Enjoy!
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