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 CCS Technical Support

Setting Delimiters
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
Mark Weir



Joined: 11 Sep 2003
Posts: 51
Location: New Zealand

View user's profile Send private message

Setting Delimiters
PostPosted: Thu Jul 26, 2007 5:04 pm     Reply with quote

Hi all,

I am using a serial based routine based on the ex_sisr code.

Could anyone tell me how to filter the characters coming in to detect 2 delimiters?

I am trying to use * then # as delimiters. I would like to return from the RDA ISR if the first 2 characters are not correct.

Any suggestions wouls be welcome.

Cheers

Mark
_________________
Life is too short to avoid asking questions
treitmey



Joined: 23 Jan 2004
Posts: 1094
Location: Appleton,WI USA

View user's profile Send private message Visit poster's website

PostPosted: Fri Jul 27, 2007 7:48 am     Reply with quote

You would be better off using the ISR to just buffer the data.
Check the data somewhere else and set a flag.
see this post for an example
http://www.ccsinfo.com/forum/viewtopic.php?t=29092&postdays=0&postorder=asc&start=15


it really just comes down to a statement like
if ((RX_BUF[0]=='*')&& (RX_BUF[1]=='#'))
Mark Weir



Joined: 11 Sep 2003
Posts: 51
Location: New Zealand

View user's profile Send private message

Calculating the time
PostPosted: Fri Jul 27, 2007 2:41 pm     Reply with quote

Hi There,

I already take in the string to a buffer then check the buffer but it is too slow responding.
How can I calculate what the time taken to process the RDA ISR should be?
I have no delays anywhere yet if I send strings every 20mS the comms just gives up and jams the programme.

Cheers

Mark
_________________
Life is too short to avoid asking questions
Ttelmah
Guest







PostPosted: Sat Jul 28, 2007 3:00 am     Reply with quote

You need probably to rethink your whole 'approach', if there is a speed problem working outside the ISR.
I'd suggest an approach something like this:
Code:

//Use EX_SISR. Note _vital_ that your buffer size is a binary size:
//4,8,16,32 etc.. If not, the '%' operation in the interrupt handler, will
//switch to having to use a division, instead of a bitwise &, and will
//take hundreds of times longer.....
#define SEARCHING (0)
#define DELIM1 (1)
#define HDR (2)
#define INMSG (3)

void main() {
   int8 search_state=0;
   int8 temp;
   enable_interrupts(global);
   enable_interrupts(int_rda);

   do {
      if(bkbhit) {
        temp=bgetc();
        switch (search_state) {
        case SEARCHING:
           //Here just test for the first delimiter
           if (temp=='*') search_state=DELIM1;
           break;
        case DELIM1:
           //Now test for the first character of the message
           if (temp=='A') search_state=HDR; //put your character here.
           else search_state=SEARCHING; //start again if not
           break;
        case HDR:
           //Now just look for the second character in teh message
           if (temp=='B') search_state=INMSG; //put second char here
           else search_state=SEARCHING;
           break;
        case INMSG:
           //Now do what you want with the message
           if (temp!='#') {
               //process sequential message bytes here
           }
           else {
                  //possibly deal with complete message here
                  search_state=SEARCHING;
           }
           break;
        }
   } while (TRUE);
}

This is a classic example, of a 'state machine'. Each time round the loop, the code has a 'state' (search_state), and knows what it has to test for in this state. It checks just this, and nothing else. So, when sittng 'idle', it just looks for '*'. If it finds an *, it then starts looking for a 'A', and then moves to looking for a 'B'. Once it has found all of these in sequence, it arrives at the 'INMSG handler, and processes the message, till it sees '#'.
The point is that the work in each state is tiny. Though the code itself becomes long, and can be complex (if for example, there are two different possible 'headers', the state may advance to two different places according to which is seen), the time per loop is short. Combined with a buffer, this can handle very high rates if properly written. I have a similar 'parser', that searches through data arriving at 56000bps, wth some dozens of different messages (the 'INMSG handler itself switches to a second state machine that walks through a tree of possible message), which has been running for over ten years, in trading floors across the world, without missing a beat.

Best Wishes
inservi



Joined: 13 May 2007
Posts: 128

View user's profile Send private message

PostPosted: Sat Jul 28, 2007 3:52 am     Reply with quote

Hello Mark,

Ttelmah give you a very good solution.

I can propose a few enhancement for win some time more.

First in the 'serial_isr()' procedure, the check for Buffer full is not necessary for two reason. First, if the buffer is full, there is no warning and no special work can be do for recover the data loosing! second as some data is loosed anyway, why testing it !
With no flow control, the pic MUST be sure to get all characters sended !

I suggest :
Code:
void serial_isr() {
   buffer[next_in]=getc();
   next_in=(next_in+1) % BUFFER_SIZE;
}
I think its enough !

Second, In the 'bgetc()' function, i do not like the 'while(!bkbhit) ;' because the main program call bgetc() after testing bkbhit. I propose to delete this line.

Thirth enhancement,
I suppose that the two delimiters are sended at the start of message. If not then the 'state machine' proposed by Ttelmah is better.
If the order of the delimiters is not important, you can simply count the number of delimiters and reset if there is no delimiters as :

Code:

...
 int delimCount = 2;
...
  if(bkbhit) {
    temp=bgetc();
    if (temp=='*' || temp=='#') --delimCount ;
      else delimCount = 2 ;

    if ( delimCount == 0 ) {
      delimCount = 2 ;
      while ( delimCount == 2 ) {
        if(bkbhit) {
          temp=bgetc();

          if (temp=='*' || temp=='#') --delimCount ;
          ...
        // your code to do when the delimiters are
received
          ...
        }
      }
    }

...
  }
...



Best regards,
dro
_________________
in médio virtus
Mark Weir



Joined: 11 Sep 2003
Posts: 51
Location: New Zealand

View user's profile Send private message

PostPosted: Sun Jul 29, 2007 7:01 pm     Reply with quote

Thanks you both for your suggestions.

I will test them both in my application and come back to you with some feedback.

Cheers

Mark
_________________
Life is too short to avoid asking questions
Mark Weir



Joined: 11 Sep 2003
Posts: 51
Location: New Zealand

View user's profile Send private message

More Questions
PostPosted: Tue Jul 31, 2007 3:31 pm     Reply with quote

Some feed back for Ttelmah and inservi:

Guys, I have had an attempt at combining your sugesstions. All looked well at first however when I received a complete string into the buffer then tried another with an incorrect delimiter it was still received.
I have simplified things so that I just look for a * then take in the string until I see a <cr>.
Here is my attempt:



Code:
#include <18F4620.h>
#fuses HS,NOWDT,NOPROTECT,NOLVP
#use delay(clock=20000000)
#use rs232(baud=19200, xmit=PIN_C6, rcv=PIN_C7)


int1 valid_flag=0,string_flag = 0,eom_flag = 0;

int i = 1 ;
int search_state=0;
int temp;
long loop=0;

#define valid PIN_B3

#define SEARCHING (0)
#define MESSIN (1)

#define BUFFER_SIZE 32
BYTE buffer[BUFFER_SIZE];
BYTE buffer1[BUFFER_SIZE];
BYTE next_in = 0;
BYTE next_out = 0;


#int_rda
char serial_isr() {
   
   int t;
         
   buffer[next_in]=getc();
   t=next_in;
   next_in=(next_in+1) % BUFFER_SIZE;
   
}

#define bkbhit (next_in!=next_out)

BYTE bgetc() {
   BYTE c;
   c=buffer[next_out];
   next_out=(next_out+1) % BUFFER_SIZE;
   return(c);
}



void main() {

   
   enable_interrupts(global);
   enable_interrupts(int_rda);

     
   do {
      
      ++loop;
      
         if(bkbhit)
       {
         
           temp=bgetc();
          
                 switch (search_state)
                {
                  
                    case SEARCHING: if (temp=='*') buffer1[0] = temp; search_state=MESSIN; break;
                     // Test each character for a * until we find one
                                                                 
                    case MESSIN:
                    // Start taking in the string until we find a CR
                          
                          if(temp != '9')
                          {   
                             buffer1[i] = temp;// Buffer what comes in
                             ++ i;
                           }
                          
                          
                           if(temp =='9')
                           {
                              for(i=0;i<10;++ i)
                             
                              putc(buffer1[i]);// Print it back for testing
                             
                            }
                           
                          search_state=SEARCHING;// We have a string so reset the search state
                          valid_flag = 1;
                                                  
                          break;
                         
                      }
          
              }
          
                                                           
                             
                     if(valid_flag ==1) // Turn on the valid LED
                  {
                     valid_flag = 0;
                     output_high(valid);
                     loop = 0;
                  }
       
                   if(loop >= 40000) // Wait for a number of loops and turn the valid LED off
                   {
                  output_low(valid);
                   }
      
         } while (1);
   
}

Can either of you see where I am going wrong?

Cheers

Mark

_________________
Life is too short to avoid asking questions
Mark Weir



Joined: 11 Sep 2003
Posts: 51
Location: New Zealand

View user's profile Send private message

Please ignore the last post!!
PostPosted: Tue Jul 31, 2007 9:12 pm     Reply with quote

Hi All,

I have found a couple of my errors from the last post.
This one actually runs and filters the * character But.....
the buffer gets very muddled so i cant pass this to the rest of my loop yet.

Any thoughts?

Code:
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////                        Interrupt Comms8.C                             
////                                                               
////  This program  implements an interrupt service routine to buffer up incoming serial data.
////    The data string must start with a *   and will terminate with a <CR>         
////                                                               
//// 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


#include <18F4620.h>
#fuses HS,NOWDT,NOPROTECT,NOLVP
#use delay(clock=20000000)
#use rs232(baud=19200, xmit=PIN_C6, rcv=PIN_C7)


int1 valid_flag=0,string_flag = 0,eom_flag = 0;

int i,j  ;
int search_state=0;
int temp;
long loop=0;

#define valid PIN_B3
#define flush PIN_B2

#define on output_high
#define off output_low
#define tog output_toggle

#define SEARCHING (0)
#define MESSIN (1)

#define BUFFER_SIZE 32
BYTE buffer[BUFFER_SIZE];
BYTE buffer1[BUFFER_SIZE];
BYTE next_in = 0;
BYTE next_out = 0;

void CommRxFlush(void);

#int_rda
char serial_isr() {
   
   int t;
         
   buffer[next_in]=getc();
   t=next_in;
   next_in=(next_in+1) % BUFFER_SIZE;
   
}

#define bkbhit (next_in!=next_out)

BYTE bgetc() {
   BYTE c;
   c=buffer[next_out];
   next_out=(next_out+1) % BUFFER_SIZE;
   return(c);
}



void main() {

   
   enable_interrupts(global);
   enable_interrupts(int_rda);

     
   do {
      
      ++loop;
      
         if(bkbhit)
       {
         
              temp=bgetc();
          
           switch (search_state)
                {
                  
                    case SEARCHING:
                     // Test each character for a * until we find one
                    
                      if (temp=='*')
                     
                      {
                       search_state=MESSIN;
                      }
                     
                      else
                     
                         search_state=SEARCHING;
                       break;
                                                                 
                    case MESSIN:
                    // Start taking in the string until we find a CR
                          
                           for(j=0;buffer[j]!='\r';++j)
                     {
                        fputc(buffer[j]);
                     }
                                            
                          search_state=SEARCHING;// We have a string so reset the search state
                          valid_flag = 1;
                          
                          on(flush);
                          
                           CommRxFlush();
                                                  
                          break;
                         
                          }
                        }       
                             
                     if(valid_flag ==1) // Turn on the valid LED
                  {
                  valid_flag = 0;
                     on(valid);
                     loop = 0;
                  }
       
                   if(loop >= 60000) // Wait for a number of loops and turn the valid LED off
                   {
                  off(valid);
                  off(flush);
                   loop = 0;
                   }
      
         } while (1);
   
}


void CommRxFlush(void)

{
   disable_interrupts(int_rda);   //disable USART Rx interrupt
                          //reset all Rx indexes and counters
   next_in = 0;
   next_out = 0;
   search_state=SEARCHING;
   eom_flag = 0;
   
   enable_interrupts(int_rda);
}



Cheers

Mark
_________________
Life is too short to avoid asking questions
inservi



Joined: 13 May 2007
Posts: 128

View user's profile Send private message

PostPosted: Wed Aug 01, 2007 1:38 am     Reply with quote

Hello,

Here are tree idee:

Code:
             if ( temp == ' * ' ) { // you test temp with 3 chars ' * '. try to compare with '*' not ' * '



Code:
#int_rda
char serial_isr()
{
   
   //int t; // loosing time and space for nothing
   
   buffer[next_in] = getc ( );
   //t = next_in; // loosing time and space for nothing
   next_in = ( next_in + 1 ) % BUFFER_SIZE;
}


Code:
           case MESSIN:
             // Start taking in the string until we find a CR
           
             // --This loop is not right for two reasons --------------
             // --1) use bgetc ( ) for read the buffer !!! ------------
             // --2) use the principal loop for process the buffer because the '\r' is probably not yet in the buffer after the  ! ----
             //  for ( j = 0; buffer[j] != '\r'; ++j )
             //  {
             //     fputc ( buffer[j] );
             //  }
             // -------------------------------------------------------
             // in the 'case', just treating one char at a time

             if ( temp != '\r' )
             {
                fputc ( temp );
             }
             else
             {
                 search_state = SEARCHING; // We have a string so reset the search state
             
                 valid_flag = 1;
           
                 on ( flush );
             // it is normaly not necessary to flushing the buffer
                 CommRxFlush ( );
             }
           
             break;


if you absolutely need a loop in the 'case', try something like that
Code:
           case MESSIN:
              while ( temp != '\r' )
              {
                fputc ( temp );
                while (  !bkbhit ) ;
                temp = bgetc ( );
              }
               search_state = SEARCHING; // We have a string so reset the search state
             
               valid_flag = 1;
           
               on ( flush );
             // it is normaly not necessary to flushing the buffer
               CommRxFlush ( );
           
              break;

_________________
in médio virtus
ckielstra



Joined: 18 Mar 2004
Posts: 3680
Location: The Netherlands

View user's profile Send private message

PostPosted: Wed Aug 01, 2007 1:56 am     Reply with quote

Code:
#int_rda
char serial_isr() {
   
   int t;
         
   buffer[next_in]=getc();
   t=next_in;
   next_in=(next_in+1) % BUFFER_SIZE;
   
}


In the original example code there are two more lines at the end of this function:
Code:
   if(next_in==next_out)
     next_in=t;           // Buffer full !!
These lines are protecting your buffer from overwriting data that has not been read yet. Why did you remove these lines?
inservi



Joined: 13 May 2007
Posts: 128

View user's profile Send private message

PostPosted: Wed Aug 01, 2007 2:38 am     Reply with quote

Hello Ckielstra,

I proposed to remove this test of buffer overflow because it is not usefull. If the PIC cannot read rather quickly, some characteres will be lost any way. If characteres are lost, the main loop does not know it and the loss is not treated.
The result is in any way a loss of characteres.

I think that to remove this tests decreases the waste of time and thus the risk of loss of characteres.

Either the PIC is fast enough to receive all or not!
If the PIC is not fast enough, the only good way of dealing with this problem, consists in implementing a flow control with hand check.

What do you think about ?


Best regards,
dro.
_________________
in médio virtus


Last edited by inservi on Thu Aug 02, 2007 7:09 am; edited 1 time in total
treitmey



Joined: 23 Jan 2004
Posts: 1094
Location: Appleton,WI USA

View user's profile Send private message Visit poster's website

PostPosted: Wed Aug 01, 2007 2:24 pm     Reply with quote

Code:
#define VER_MINOR 05
#define VER_MAJOR 0
#include <18F4525.h>
#use delay(clock=20000000)
#fuses HS,NOWDT,NOLVP,PROTECT,PUT,BROWNOUT
//#use rs232(baud=19200,xmit=PIN_B3,INVERT,stream=DEBUG,DISABLE_INTS)
#use rs232(baud=19200,xmit=PIN_B3,INVERT,stream=DEBUG)
#use rs232(baud=9600,errors,xmit=PIN_C6,rcv=PIN_C7,stream=PC)
#case
#zero_ram
#define INTS_PER_SECOND_TMR2 77
#define BUFF_SZ 32
int8 rx_buf[BUFF_SZ];
int8 rx_i = 0;
int8 rx_o = 0;
int8 int_count_tmr2;
int8 timer[8];//set a side 8 different 1sec timers
#define tmr_led timer[0]
//=== prototypes====
init();
int8 bgetc();
//======================= Main ==============================
void main() {
  enum states {Looking,Found_star,Found_both};
  states rx_state=Looking;
  int8 rxdata=0;
  init();
  while(1)
  {
    while(rx_i!=rx_o)//do this loop when data is recieved
    {
      if(rx_state==Looking && bgetc() =='*') rx_state=Found_star;
      if(rx_state==Found_star) //only if "Found_star" do we read another char
      {
        if(bgetc() =='#') rx_state=Found_both;//check if next char is # else reset state
        else rx_state=Looking;
      }
      if(rx_state==Found_both)//if I found both, read 1 char at a time
      {
        fprintf(DEBUG,"0x%2X ",bgetc());
      }
    }
  }
}
init() {
  setup_adc_ports(NO_ANALOGS);
  fprintf(DEBUG,"Starting\n\r");
  fprintf(DEBUG,"test V%u.%02u\n\r",VER_MAJOR,VER_MINOR);
  enable_interrupts(GLOBAL);
  enable_interrupts(INT_RDA);
  enable_interrupts(INT_TIMER2);
  setup_timer_2(T2_DIV_BY_16,255,16);
  set_timer2(250);
  int_count_tmr2 = INTS_PER_SECOND_TMR2;
}
//=======================bgetc============================//
int8 bgetc() {
  int8 c;
  c=rx_buf[rx_o];
  rx_o=(rx_o+1) % BUFF_SZ;
  return(c);
}
//=======================int_rda============================//
#int_rda
char serial_isr() {
  int t;
  rx_buf[rx_i]=fgetc(PC);
  rx_i=(rx_i+1) % BUFF_SZ;
}

//=======================timer 2 ISR============================//
#INT_TIMER2                   // This function is called every time
void timer2_isr() {           // timer 2 overflows (250->0), which is
  int8 x;
  if(--int_count_tmr2==0){  // approximately 15 times per second for this program.
    //fprintf(DEBUG,".");//1 sec heartbeat
    for (x=0;x<sizeof(timer);x++){
      restart_wdt();
      if (timer[x]>0) timer[x]--;//reduce individual timer counts until all==0
    }
    int_count_tmr2 = INTS_PER_SECOND_TMR2;
  }
}
ckielstra



Joined: 18 Mar 2004
Posts: 3680
Location: The Netherlands

View user's profile Send private message

PostPosted: Wed Aug 01, 2007 3:14 pm     Reply with quote

inservi wrote:
I proposed to remove this test of buffer overflow because it is not usefull. If the PIC cannot read rather quickly, some characteres will be lost any way. If characteres are lost, the main loop does not know it and the loss is not treated.
The result is in any way a loss of characteres.

I think that to remove this tests decreases the waste of time and thus the risk of loss of characteres.

Either the PEAK is fast enough to receive all or not!
If the PIC is not fast enough, the only good way of dealing with this problem, consists in implementing a flow control with hand check.

What do you think about ?
I guess it is matter of taste. As you correctly said data will be lost anyway, so it is the higher software level that will have to detect and correct for this.

Hmmm, this makes me think. In this particular project we are only testing for start and end markers but to make it really complete there should also be a mechanism added for proofing the data is correct. In the code library examples can be found for several CRC calculation routines.
Mark Weir



Joined: 11 Sep 2003
Posts: 51
Location: New Zealand

View user's profile Send private message

Packet Setup
PostPosted: Wed Aug 01, 2007 3:25 pm     Reply with quote

Hi Ckielstra,

I am just working through the response from Treitmey. Just to clarify the packet we use is constructed thus:

* # 111122223333................ cksum1 chsum2 cksum3 <cr>

The start is *# the 1111 is a type code any number between 0 and 9999, the 2222 is an address any number between 0 and 9999, the 3333 is a command any number between 0 and 9999.
Then we send some data bytes ........, this can be any number from 1 to 100 bytes, then we have a checksum stored in three bytes. The checksum comes from adding the total of each preceeding byte but as a rolling byte so that the answer is between 0 and 255, the answer digits are sent in three bytes then finally we send a <cr> to end the string.

data heading back from these modules is identical in form it just uses ## at the start of the string.

I am very greatful for all the help I am getting here, I have struggled with this for a while now.

I will say though that i am learning a great deal in the process.

Cheers

Mark
_________________
Life is too short to avoid asking questions
Ttelmah
Guest







PostPosted: Thu Aug 02, 2007 2:10 am     Reply with quote

I hope your fields, actually vary from '0000'to '9999', rather then '0' to '9999' as you say!. In the latter case, parsing would basically be impossible...
What marks the end of the 'up to 100 bytes'?. Is it a space as you show?.

Best Wishes
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