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

Timeout with TMR3 for INT_RDA

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



Joined: 30 Jan 2008
Posts: 197

View user's profile Send private message

Timeout with TMR3 for INT_RDA
PostPosted: Thu Jan 30, 2014 3:16 pm     Reply with quote

Hello,

I'm using CCS V4.079 to implement a timeout of 30 seconds when I receives data by the UART, but I can not leave the loop reception, someone can tell me where is my mistake!

Here is my code:

Code:
#include <18F4520.h>
#fuses HS,NOWDT,NOPROTECT,NOLVP,NOBROWNOUT
#use delay(clock=20000000)
#use rs232(baud=19200, xmit=PIN_C6, rcv=PIN_C7, timeout=5000)// RS232 Estándar

int8 next_in = 0;
int8 next_out = 0;
int8 theChar = 0;
int8 i;
int8 buffer[32];
char Signal_VAL[6];
int32 TimeOUT3_Clock;
int32 TimeOUT3;


#INT_RDA
void serial_isr() {                       

   int t;
   buffer[next_in]=getc();
   t=next_in;
   next_in=(next_in+1) % 32;
   if(next_in==next_out)
   next_in=t;                       
}

#define bkbhit (next_in!=next_out)

int8 bgetc() {
   BYTE c;
   WHILE(!bkbhit) ;
   c=buffer[next_out];
   next_out=(next_out+1) % 32;
   return(c);
}

void Clear_Buffer(){
   for (i=0; i<32; i++){
      buffer[i]= 0x00;
   }
   next_in = 0;
   next_out = 0;
}

#INT_TIMER3
void Set_TimeOUT3() {
   Set_Timer3(64911);
   
   TimeOUT3_Clock++;
   if (TimeOUT3_Clock >= 1000){
      TimeOUT3++;
      TimeOUT3_Clock = 0;
   }
}
 
void Read_ISIG(){

    Clear_Buffer();
    TimeOUT3 = 0;
    TimeOUT3_Clock = 0;
    enable_interrupts(INT_TIMER3);

    printf("AT_ISIG=1\r"); 
    Delay_ms(100);

    while ((theChar != '_') || (TimeOUT3 <= 30))theChar = bgetc();                             
         theChar = bgetc();
         if (theChar == 'I') {
            theChar = bgetc();
            if (theChar == 'S') {
               theChar = bgetc();
               if (theChar == 'I') {
                  theChar = bgetc();
                  if (theChar == 'G') {
                     theChar = bgetc();
                     if (theChar == ':') {
                        theChar = bgetc(); 

                  for (i=0; i<5; i++){
                     Signal_VAL[i] = bgetc();
                  }
               }
            }
         }
      }
   }

   disable_interrupts(INT_TIMER3);
}

void main() {

   enable_interrupts(global);
   enable_interrupts(int_rda);

   setup_timer_3(T3_INTERNAL | T3_DIV_BY_8);
   set_timer3(64911);
   disable_interrupts(INT_TIMER3);

   Read_ISIG();

while (TRUE){

  delay_ms(200);   
 }
}
asmboy



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

View user's profile Send private message AIM Address

PostPosted: Thu Jan 30, 2014 3:21 pm     Reply with quote

what do you think happens in bgetc() if you call it and
no character is waiting in the buffer??
pilar



Joined: 30 Jan 2008
Posts: 197

View user's profile Send private message

PostPosted: Thu Jan 30, 2014 3:29 pm     Reply with quote

Embarassed Precisely by this is TimeOUT3 produced by TMR3
Ttelmah



Joined: 11 Mar 2010
Posts: 19553

View user's profile Send private message

PostPosted: Thu Jan 30, 2014 3:50 pm     Reply with quote

The point is that instead of calling bgetc, when you want a character, you need to use bkbhit, to see if there _is_ a character.
So you use something like:
Code:

int8 wait_getc(void)
{
    while (TRUE)
    {
        if (bkbhit())
            return bgetc();
        if (TIMEOUT3>30)
            return 0;
    }
}

This will exit with a '0' character, if one doesn't arrive, and timeout3 gets to 31

bgetc, sits _waiting_ for a character if one is not available, so TIMEOUT3 is never checked in your code....

Best Wishes
pilar



Joined: 30 Jan 2008
Posts: 197

View user's profile Send private message

PostPosted: Thu Jan 30, 2014 4:44 pm     Reply with quote

I made the changes that you suggest me but the problem persists or is there something I'm doing wrong?

Code:
#include <18F4520.h>
#fuses HS,NOWDT,NOPROTECT,NOLVP,NOBROWNOUT
#use delay(clock=20000000)
#use rs232(baud=19200, xmit=PIN_C6, rcv=PIN_C7, timeout=5000)// RS232 Estándar

int8 next_in = 0;
int8 next_out = 0;
int8 theChar = 0;
int8 i;
int8 buffer[32];
char Signal_VAL[6];
int32 TimeOUT3_Clock;
int32 TimeOUT3;

int8 wait_getc() ;

#INT_RDA
void serial_isr() {                       

   int t;
   buffer[next_in]=getc();
   t=next_in;
   next_in=(next_in+1) % 32;
   if(next_in==next_out)
   next_in=t;                       
}

#define bkbhit (next_in!=next_out)

int8 bgetc() {
   BYTE c;
   WHILE(!bkbhit) ;
   c=buffer[next_out];
   next_out=(next_out+1) % 32;
   return(c);
}


int8 wait_getc()
{
    while (TRUE)
    {
        if (bkbhit)
            return bgetc();
        if (TIMEOUT3>30)
            return 0;
    }
}
 

void Clear_Buffer(){
   for (i=0; i<32; i++){
      buffer[i]= 0x00;
   }
   next_in = 0;
   next_out = 0;
}

#INT_TIMER3
void Set_TimeOUT3() {
   Set_Timer3(64911);
   
   TimeOUT3_Clock++;
   if (TimeOUT3_Clock >= 1000){
      TimeOUT3++;
      TimeOUT3_Clock = 0;
   }
}
 
void Read_ISIG(){

    Clear_Buffer();
    TimeOUT3 = 0;
    TimeOUT3_Clock = 0;
    enable_interrupts(INT_TIMER3);

    printf("AT_ISIG=1\r"); 
    Delay_ms(100);

    while (theChar != '_') theChar = wait_getc();                             
         theChar = wait_getc();
         if (theChar == 'I') {
            theChar = wait_getc();
            if (theChar == 'S') {
               theChar = wait_getc();
               if (theChar == 'I') {
                  theChar = wait_getc();
                  if (theChar == 'G') {
                     theChar = wait_getc();
                     if (theChar == ':') {
                        theChar = wait_getc(); 

                  for (i=0; i<5; i++){
                     Signal_VAL[i] = wait_getc();
                  }
               }
            }
         }
      }
   }

   disable_interrupts(INT_TIMER3);
}

void main() {

   enable_interrupts(global);
   enable_interrupts(int_rda);

   setup_timer_3(T3_INTERNAL | T3_DIV_BY_8);
   set_timer3(64911);
   disable_interrupts(INT_TIMER3);

   Read_ISIG();

while (TRUE){

  delay_ms(200);   
 }
}
asmboy



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

View user's profile Send private message AIM Address

PostPosted: Thu Jan 30, 2014 6:57 pm     Reply with quote

ok next item:

the structure/logic of
read_isig()
is fatally flawed in that it seems TOTALLY error intolerant.

by this i mean that if the order of receiving of ISIG:
does not occur as you seem to expect- then
you are unable to recover and search for a correct preamble.

ALSO in the problematic read_isig() function ,
do you really intend for this to be executed before all the "if tests ??
Code:

theChar = wait_getc();
theChar = wait_getc();


are you expecting a junk character and wish to skip it ?
if so - that structure will not work as you intend either.
pilar



Joined: 30 Jan 2008
Posts: 197

View user's profile Send private message

PostPosted: Thu Jan 30, 2014 7:51 pm     Reply with quote

Hi asmboy,

The possible answer that I can be received from device are:

Code:
+CME ERROR: <320>



or

Code:

_ISIG: 45.7500000

_ISIG: 50.2000000

_ISIG: 52.6000000


......another possibility is that I did not receive any answer of device and the microcontroller remains in that condition in an infinite loop ...

I just want to capture only a (01) ISIG value with two decimal, then get out of the loop capture and also prevent the microcontroller hanging up in that infinite loop ....
Ttelmah



Joined: 11 Mar 2010
Posts: 19553

View user's profile Send private message

PostPosted: Fri Jan 31, 2014 4:07 am     Reply with quote

Change a lot.....

1) Get rid of the timeout in the #use rs232. This may well cause problems. You are using interrupt driven serial, which means the 'getc' routine, will _only_ be called when a character _is_ available. So no need for a timeout. Having this implies the compiler will add timing code to the getc routine, which you do not want in an ISR, and may interact with other things....

2) Fundamentally re-think your approach. Do a search here for 'state machine'. You want something like:
Code:

#include <18F4520.h>
#fuses HS,NOWDT,NOPROTECT,NOLVP,NOBROWNOUT
#use delay(clock=20000000)
#use rs232(baud=19200, UART1, ERRORS)

#include <ctype.h>

//states
enum (waiting,header,digit) read_state=waiting;
char header[]="ISIG: ";

//buffer definitions
int8 next_in = 0, next_out=0;
#define BUFFSIZ 32
int8 buffer[BUFFSIZ];
#define bkbhit (next_in!=next_out)

#define CLEAR_BUFF next_in=next_out
//you don't need to set the values to zero, just to each other.
//you also don't need to empty the buffer itself.

int8 bgetc(void)
{
   BYTE c;
   if(!bkbhit)
     return 0; //should never get here
   c=buffer[next_out];
   if (++next_out==BUFFSIZ)
      next_out=0;
   return(c);
}

#INT_RDA
void serial_isr(void)
{                       
   int t;
   buffer[next_in]=getc();
   t=next_in;
   if (++next_in==BUFFSIZ)
      next_in=0;
   if(next_in==next_out)
      next_in=t;                       
}

//clock definitions
int1 timeout=FALSE;
int8 waitfor=0;
#define PER_SEC 18 //19 ticks per second

#INT_TIMER3
void timer3_tick(void)
{
   static tick=PER_SEC;
   if (tick)
      --tick;
   else
   {
      if (waitfor)
         --waitfor;
      tick=PER_SEC;
   }
}

int32 Read_ISIG(void)
{
   int8 rxc;
   int8 charnum=0;
   int32 val=0;
   int8 ctr=8; //much larger than needed
   int1 rescan=FALSE;
   //sit waiting for max 30 seconds for ISIG message
   read_state=waiting;
   timeout=30;
   while (timeout)
   {
       if (bkbhit)
       {
           //now there is a character
           rxc=bgetc(); //read it
           do
           {
              if (rescan) //here a test has failed
              {
                 rescan=FALSE;
                 state=waiting;
                 charnum=0;
              }
              switch (read_state) {
              case waiting:
                 //here looking for the first character '_'
                 if (rxc=='_')
                    read_state++;
                 break;
              case header:
                 //now need to look for the ISIG header
                 if (rxc==header[charnum])
                 {
                    charnum++;
                    if (header[charnum]=='\0') //end of header
                       read_state++;
                 }
                 else
                 {
                    //problem
                    rescan=TRUE;
                    //re-test the current character
                 }   
                 break;
              case digit:
                 //Now should only be receiving digits or DP
                 if (rxc=='.')
                 {
                     ctr=2; //number of digits to read after the decimal
                 }
                 else
                 {
                     if (isdigit(rxc))
                     {
                         //here we have a digit
                         rxc-='0'; //convert to decimal
                         val*=10;
                         val+=rxc;
                         if (--ctr==0)
                         {
                            //have read two digits after the decimal
                            return val;
                         }
                     }
                     else
                     {
                         //Here we have hit a non digit. Something wrong
                         //rescan the character
                         rescan=TRUE;
                     }
                 }
                break;
             }
             if (read_state>digit)
                read_state=waiting; //trap impossible situation
          }
          while (rescan); //loop if the character needs to try again
       } //end of character tests
   }//only loop here while timeout > 0
   return 0;
}

void main() {
   int32 isigval;
   enable_interrupts(global);
   enable_interrupts(int_rda);

   setup_timer_3(T3_INTERNAL | T3_DIV_BY_4);
   enable_interrupts(INT_TIMER3);
   //timer 3 now ticks at 19/sec 2000000/(4*4*65536)

   while (TRUE)
   {
      isigval=Read_ISIG()
      //will return with an ISIG value if available, or '0' if timeout. 
      delay_ms(200);
   }
}   


Now, 'no warranties'. Typed this untested, to show the approach.
This uses a timer at 19Hz (keep things slow, unless they need to be fast), and will wait for 30 seconds to find an 'ISIG' message.
It decodes the message to an int32 value (*100). So your
"_ISIG: 52.6000000" message should return 5260.

There are certain other changes, the test for 'BUFFSIZ', rather than '%', which is safe for non binary buffer sizes (CCS should have a warning about this in the example, but don't...).

If a character fails to match, it jumps back and tests it against the '_' again, so should recover when unexpected data arrives.

Very different.....

Just realised I had omitted the ':' from the header. Have added it.

Best Wishes


Last edited by Ttelmah on Sun Feb 02, 2014 1:18 am; edited 1 time in total
Mike Walne



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

View user's profile Send private message

PostPosted: Fri Jan 31, 2014 5:32 am     Reply with quote

I suggest you change the structure of your code. Work on the KISS principle.
Your very deep nesting is asking for trouble.

You also need to be clear on what you want to happen when:-

1) No data is received.
2) Wrong/garbled data is received during preamble. Continue waiting or re-start?
3) Wrong type of data received when expecting numerical digits.
4) Total time or time between characters is too long?

There are ways to go without requiring deep nesting, such as:-

a) Treat the preamble as a string and simply index through looking for the next character.
b) Create a state machine which looks for each preamble character in turn.
c) ...................

Then deal with the data.

Mike
pilar



Joined: 30 Jan 2008
Posts: 197

View user's profile Send private message

PostPosted: Fri Jan 31, 2014 8:11 am     Reply with quote

Ttelmah and Mike, thanks for your feedback, I will try to explain what I intend to do ..

I have a device which I need to link to a microcontroller, this device only answer to queries made by the microcontroller using AT commands, if the micro no makes a query the device does not answer...

The problem is that the ranswer time to a command varies greatly depending on which is the query, there are answer that occur after 10 ms or 100ms as there are also answers that take up to 20 0 30 seconds ..

The number of query commands that need to send the micro is the amount, if trying to use a state machine for these characters, I would have to implement a state machine for each answer ...

What I need is:

1 - If no data is received, after 30 seconds out of the loop and continue with another query
2 - if wrong/garbled data is received, must make another query again.
3 - All possible ANSWERS are encodigos ascii..
Mike Walne



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

View user's profile Send private message

PostPosted: Fri Jan 31, 2014 8:48 am     Reply with quote

Assemble the reply as a string, then compare to either the expected or all the reply string(s).
Could be part of a state machine solution.
Depends on which you're most comfortable with.

Mike

If you know what the expected reponse time is, you don't have to always wait for the full 30s.
asmboy



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

View user's profile Send private message AIM Address

PostPosted: Fri Jan 31, 2014 1:29 pm     Reply with quote

pilar:

Mr. Ttelmah has done you a great service here,
both in terms of logical, error resistant,
self-recovering code, but also in illustrating program flow ,
that ought to guide you through projects well after this one has been
handed in.
pilar



Joined: 30 Jan 2008
Posts: 197

View user's profile Send private message

PostPosted: Fri Jan 31, 2014 2:46 pm     Reply with quote

Thank you all..
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