WaitForStrTimeout in USART module

General discussion relating to the library modules supplied with the compiler

Moderators: David Barker, Jerry Messina

Post Reply
User avatar
RadioT
Registered User
Registered User
Posts: 157
Joined: Tue Nov 27, 2007 12:50 pm
Location: Winnipeg, Canada

WaitForStrTimeout in USART module

Post by RadioT » Wed Jan 30, 2008 10:30 am

Hello,

In using WaitForStrTimeout in the USART module, I put togther the following subroutine. The idea is that once a return response header from a modem is detected, I grab the rest of the string after this detected header and put it into a string array. Trouble is, sometimes WaitForStrTimeout is missing the beginning of the array, and I have to make the request again. Maybe I'm just using it wrong, but here is the essential code I pulled out of the main program:

Code: Select all

Device = 18F66J16
Clock = 32

#option USART_BRGH = true
//#option USART_BRG16 = true   //doesn't compile in this test for some reason, although it works in my main code-get a "constant expression violates subrange bounds" error on set baud below
#option RX_PRIORITY = IPLow 
Include "usart.bas"
Include "usart2.bas"
Include "convert.bas"
Include "string.bas"
 

Dim
Modem_response_code As String(5),
i As Byte,
temp As Word,
Modem_result As String(81),
RC2IP As IPR3.5,     // RC2IP, EUSART2 receive interrupt priority (0 = low)
BRGH16_1 As BAUDCON1.3  // set 16 bit baud register enable bit

Sub get_USART1(pStringIn As String, Timeout As Word)
   USART.WaitForStrTimeout(pStringIn,Timeout) 
   i = 0
   Repeat    
      Modem_result(i) = USART.ReadByte    
      temp = StrToDec(Modem_result(i)) 
      Inc(i)
      If i = 81 Then i = 0 EndIf //size of array is 80 bytes ("bound" didn't work here, maybe because it's a string array?)
      If temp = 0 Then Exit EndIf //exit if end of string encountered
      If temp < 20 Then Dec(i) EndIf //do not accept nonprinting characters
   Until Not USART.DataAvailableTimeout(10)      
End Sub 

//Set up serial ports   
// Set up EUSART2, for the GPS. The EUSART2 module settings don't seem to work in the module with the modified NMEA code, so they are set here.
  USART2.SetBaudrate(br9600)
  RCSTA2 = %10010000
  TXSTA2 = %00100100
  BAUDCON2.5=0   // RX data polarity set to active high = 0, non-inverted to the receiver, since going coming in directly at TTL level with no RS232 level shifter
  BAUDCON2.4=0   // TX clock idle state is high = 0 (since going to an external RS232 level shifter as debug output)
  RC2IP = 0       // set EUSART2 interrupt priority to low
  //set up EUSART1, for the modem, to 19200 bps
  RCSTA1 = %10010000
  TXSTA1 = %00100100
  BRGH16_1 = 1
  SPBRGH1=$01     '32 MHz  ((32,000,000/19200)/4) - 1 = 415 = $019F for 19200 bps
  SPBRG1=$9F      '32 MHz    9600 = $1A high, $09 low  

  //calling the subroutine:
get_USART1("+REQ:",10000)
Modem_response_code = Mid(Modem_result,0,1)
USART2.Write("Modem_response_code is: ",Modem_response_code)

get_USART1("+REQL:",10000)
Modem_response_code = Mid(Modem_result,1,1)
USART2.Write("Modem_response_code is: ",Modem_response_code)
"+REQ:" are the characters that would form the beginning of the first response string I am looking for. +REQ: is followed by a single digit number. This works about 4 out of 5 times; sometimes I get "R" back.

However, when looking for the contents of the string "+REQL:", it fails about 1 out of 3 tries. The "+REQL:" response is longer than +REQ; +REQL is followed by a space, then a digit, then a comma, then a space, number, comma, 3 more times. There is always a carriage return and line feed and "OK" after this string as well. For now, I just want to capture the first digit consistently. Then I'll move on to catching each of the others later in this response string.

I've been around and around on this, and this seemed like the best variation. This is from a 900 line program that calls about a dozen modules and I've been able to plough through all of it to now, it's just this silly problem that I hope looks obvious to someone. I already am using the low priority interrupt for serial in on USART2 from a GPS using a modified NMEA user module (USART2 output is used for debug). The high priority interrupt routine contains 3 different interrupt sources and a check for key presses. These, though, are disabled when I run this sub. Any suggestions?

-Tom

Doj
Posts: 362
Joined: Wed Apr 11, 2007 10:18 pm
Location: East Sussex

Post by Doj » Wed Jan 30, 2008 11:07 am

Hello Tom,
I was having this problem one time, though not using an interrupt system.

What happens if you reduce the text string to be searched to "REQ:" or even less?, you might see a point where the read data is always consistent(the initial characters will then act as a trigger to the rx routine to go and look at the incoming data).
What solved my problem was that the 2byte buffer was full and would not allow new data in, so what appeared to be missing data was due to the buffer getting characters when I was not expecting it.
I solved this by switching the UART module on and off immediately before going to the RX routine(just clearing RCSTA did not work)

User avatar
RadioT
Registered User
Registered User
Posts: 157
Joined: Tue Nov 27, 2007 12:50 pm
Location: Winnipeg, Canada

Post by RadioT » Wed Jan 30, 2008 11:26 pm

Hi Mark,
Thanks for the suggestions. I tried them but for whatever reason, it still would miss the odd reply, although it did reduce the episodes. It's required to hit 100% on identifying the key response character because it's one of those applications that can cost someone money if repeats are needed. Whenever a reply is missed, a pre-submitted message is resent over a commercial communication channel, thus adding to communication charges! So I need to be very sure it doesn't miss any intact reply strings that would tell the routine the message went successfully the first time.

What did finally work was to take the brute-force approach. I wait for any data to appear on the port, collect it all, then once back in the main routine test each byte for matches. I changed the subroutine header to:

Code: Select all

Sub get_USART1(Timeout As Word)
USART.DataAvailableTimeout(Timeout)
and changed the calling code and processing step to:

Code: Select all

get_USART1(10000)  
for i = 0 to 10
   if  Modem_result(i) = "Q" and Modem_result(i+1) = ":" then 
      Modem_response_code = Modem_result(i+2)
   endif
next  
to get the result character "Modem_response_code" I'm looking for. So far, this is working 100%. Unfortunately, this eats up extra memory and clock cycles, and I lose some flexibility by having to use less generic code, where each trigger character has to be defined in the "if" loop. But hey, it works.

73's,
de Tom

Doj
Posts: 362
Joined: Wed Apr 11, 2007 10:18 pm
Location: East Sussex

Post by Doj » Thu Jan 31, 2008 6:37 pm

Hey brute force never did anyone any harm!, glad its ok, sounds like the interrupt system is too complex to catch it?, as you say its ok now so who cares for the moment!

User avatar
RadioT
Registered User
Registered User
Posts: 157
Joined: Tue Nov 27, 2007 12:50 pm
Location: Winnipeg, Canada

Post by RadioT » Wed Feb 20, 2008 12:36 am

Go figure. The code has grown in size and now I needed to put the serial routine into a module so it could be called by code in different parts of the program (now 3000 lines, 65kB with 2.7kB of RAM). Now it's broken again.

I did find, however that if I move the declaration for the string into which EUSART data is to be placed, different calls work and other calls do not.
Any ideas out there??

Code: Select all

Sub get_USART1(Timeout As Word)
   Dim Position As Byte
   USART.DataAvailableTimeout(Timeout)
   Position = 0
   Repeat    
      String_result(Position) = USART.ReadByte    
      temp = StrToDec(String_result(Position)) 
      Inc(Position)
      If Position = 81 Then Position = 0 EndIf //size of String_result array is 81 bytes ("bound" didn't work here)
      If temp = 0 Then Exit EndIf
      //If temp = 14 Then Exit EndIf
      If temp < 20 Then Dec(Position) EndIf 
   Until Not USART.DataAvailableTimeout(10) 
End Sub   
Here's one of the calling functions:

Code: Select all



get_USART1(1000)  
   For i = 0 To 10 
      If  String_result(i) = "Q" And String_result(i+1) = ":" Then 
         String_signal = String_result(i+2) 
         //break 
       EndIf 
    Next  
One thing I do see when I diff the .asm files are big differences in memory paging parameters. All I would do is move an 81 byte string definition from the top of a parameter declaration list down 10 lines in the list.

User avatar
RadioT
Registered User
Registered User
Posts: 157
Joined: Tue Nov 27, 2007 12:50 pm
Location: Winnipeg, Canada

Post by RadioT » Wed Feb 20, 2008 5:30 am

Here I am answering my own question. I found that if I put the function back in the main code file and placed the calling functions near the EUSART code (within a few hundred lines) the function worked.

User avatar
David Barker
Swordfish Developer
Posts: 1214
Joined: Tue Oct 03, 2006 7:01 pm
Location: Saltburn by the Sea, UK
Contact:

Post by David Barker » Wed Feb 20, 2008 10:14 am

It's difficult to say for sure without knowing what you mean by works and what does not. However, it sounds like you may be accessing an array, string or other variable 'out of bounds' somewhere in your code. This can give the problem you describe. That is, when you move a data declaration the problem appears/ disappears because some other rogue variable is touching either RAM in the call frame or other module level declarations. Also check any ISRs with respect to correct context saving.

You may also want to consider using a 'state machine' approach for driving your main program, rather than using blocking or time blocking calls - however, it may be a little late in your development cycle to do this but if time permits, it may be worth investigating.

User avatar
RadioT
Registered User
Registered User
Posts: 157
Joined: Tue Nov 27, 2007 12:50 pm
Location: Winnipeg, Canada

Post by RadioT » Wed Feb 20, 2008 3:06 pm

Hello David,

Thanks for the quick reply. Well, I do have it going now, so I'm much happier.

Now, in order to prevent a repeat in the future, I thought I would ask what you meant by the statement:
...some other rogue variable is touching either RAM in the call frame or other module level declarations...
How could I check to make sure a variable is not touching the wrong RAM locations in the call? Is it a matter of ensuring writes don't go farther than the designated size of the variable (perhaps an adjacent string)?

As for using the state machine approach, I am using boolean flags in a main loop to determine when to enter a major branch, although once in the subs there are a few places where variable identities are checked. I use both case statements and simple comparison of variables to variables, where the branching can also occur. I am also disabling the ISRs (I have two) before calling the read-EUSART sub.

73's,
de Tom.

User avatar
David Barker
Swordfish Developer
Posts: 1214
Joined: Tue Oct 03, 2006 7:01 pm
Location: Saltburn by the Sea, UK
Contact:

Post by David Barker » Wed Feb 20, 2008 3:26 pm

> Now, in order to prevent a repeat in the future...How could I
> check to make sure a variable is not touching the wrong RAM
> locations in the call

You just have to be careful when coding - if you access a variable outside its allocated RAM (arrays and strings are normally the ones to watch) then you can have all sorts of erratic program behavior.

User avatar
RadioT
Registered User
Registered User
Posts: 157
Joined: Tue Nov 27, 2007 12:50 pm
Location: Winnipeg, Canada

Post by RadioT » Wed Feb 20, 2008 9:07 pm

I see what you mean. There must be something wonky going on with this code. If I do nothing but save the file under a different name and recompile, the code causes the PIC to do a reset when accessing a particular subroutine. Doing a diff on the assembler files, there are number of interesting differences:

Here is a an exerpt from the diff from the code that does work, with pointers to places in the code that are different from the code that does not work:

Code: Select all

?I001689_F000_001593_P000323 ; L#MK GLCD.SSD1339.LINE(118-(4-I),I+95,118+(4-I),I+95,$73,$8E)
        MOVLB 3
        MOVF F949_U32,0
        SUBLW 251            difference #1 - is SUBLW 4 in other code that does not work
        MOVLB 4
        MOVWF F1051_U32
        MOVLW 255            difference #2 - is MOVLW 0 in other code that does not work
        MOVLB 3
        SUBFWB F949_U32H,0
        MOVLB 4
        MOVWF F1051_U32H
        MOVLW 255             difference #3 - is MOVLW 0 in other code that does not work     
        MOVLB 3
        SUBFWB F949_U32HH,0
        MOVLB 4
        MOVWF F1051_U32HH
        MOVLW 255              difference #4 - is MOVLW 0 in other code that does not work           
Here is a an exerpt from the diff from the code that does not work, again with pointers to specific lines that are different from the code that does work:

Code: Select all

?I001964_F000_001924_P000331 ; L#MK IF  STRING_RESULT(I) = "I" AND STRING_RESULT(I+1) = ":" TH...
        LFSR 1,M138_U16
        MOVLB 3
        MOVF F852_U08,0
        ADDWF A1,1,0
        MOVLW 0
        ADDWFC A1H,1,0
        MOVFF INDF1,F1001_U08
        MOVLW 73
        MOVLB 4
        SUBWF F1001_U08,0
        MOVLB 0
        BNZ ENDIF_162
        LFSR 1,M138_U16
        MOVLB 3
        MOVF F852_U08,0
        ADDWF A1,1,0
        MOVLW 0
        ADDWFC A1H,1,0
        INFSNZ A1,1,0
        INCF A1H,1,0
        MOVFF INDF1,F1006_U08
        MOVLW 58                  /////This line is not in the code that works
        MOVLB 4                   /////This line is not in the code that works
        SUBWF F1006_U08,0         /////This line is not in the code that works
        MOVLB 0
        BNZ ENDIF_162
Here are two pieces of code from the same area of the diff, but where the last few lines are completely different:

Snippet from the code that does work:

Code: Select all

?I003790_F000_002054_I000332 ; L#MK IF  ((CURRMENU = SEND_ALRT_MSG_SCREEN) OR (CURRMENU = SEND_T...
        MOVLW 2
        MOVLB 10
        SUBWF M1425_U08,0
        MOVLB 0
        BTFSS STATUS,2,0
        BRA ENDIF_257
TRUE_569
Snippet, covering the same function call, from the code that works:

Code: Select all

?I003790_F000_002054_I000332 ; L#MK IF  ((CURRMENU = SEND_ALRT_MSG_SCREEN) OR (CURRMENU = SEND_T...
        MOVLW 2
        MOVLB 10
        SUBWF M1425_U08,0
        MOVLB 0
        BZ TRUE_569
        MOVLW 3
        MOVLB 10
        SUBWF M1425_U08,0
        MOVLB 0
        BNZ ENDIF_257
TRUE_569

User avatar
David Barker
Swordfish Developer
Posts: 1214
Joined: Tue Oct 03, 2006 7:01 pm
Location: Saltburn by the Sea, UK
Contact:

Post by David Barker » Wed Feb 20, 2008 9:23 pm

You would really need to email the complete program, with an example of working / not working so that I can view everything in context.

Could you also give a detailed explanation of "If I do nothing but save the file under a different name and recompile, the code causes the PIC to do a reset"

User avatar
RadioT
Registered User
Registered User
Posts: 157
Joined: Tue Nov 27, 2007 12:50 pm
Location: Winnipeg, Canada

Post by RadioT » Wed Feb 20, 2008 10:00 pm


Could you also give a detailed explanation of "If I do nothing but save the file under a different name and recompile, the code causes the PIC to do a reset"
OK, no problem.
1. I open the program that functions as expected in Swordfish. SF is updated to the most recent version.

2. I click on "compile" icon to compile the program.

3. I rename the program to another .bas name and save it to the same directory as the original file name.

4. I click on the "compile" icon to compile the program.

5. I open the Perforce diff tool and load each .asm file and compare the files. The result is what I posted earlier.

In the meantime, the other engineer is going through the code and re-writing the sections that gave a positive diff in the code to see if we can have different program names compile in an identical fashion. I don't want to use the files to build further on until we work around this little hiccup. I suspect I did something naughty in the graphic library I built on the first diff I posted, but anyway if you have a look maybe you'll see something obvious.

I also do see where reading from a string and printing to a serial debug output, the output is twice as long as the defined string! Reading couldn't cause any problems, but I certainly hope I am not fouling up RAM with a renegade write in a similar way.

Thanks so much for your help,

-Tom

User avatar
David Barker
Swordfish Developer
Posts: 1214
Joined: Tue Oct 03, 2006 7:01 pm
Location: Saltburn by the Sea, UK
Contact:

Post by David Barker » Wed Feb 20, 2008 10:08 pm

> I rename the program to another .bas name...click on the
> "compile" icon to compile the program...I open the Perforce
> diff tool and load

Again, without full source it is difficult to say but it may be that when you rename the file, the compiler is picking up a file with the same name from somewhere else. Check your paths for 'same name' files. The compiler searches

(a) Source folder, then...
(b) User Library (and all subfolders!!!!), then...
(c) System Library (and all subfolders!!!!)

If this isn't the problem then it does sound weird - I will need to see all your files to reproduce the error here in order to move forward with this.

User avatar
RadioT
Registered User
Registered User
Posts: 157
Joined: Tue Nov 27, 2007 12:50 pm
Location: Winnipeg, Canada

Post by RadioT » Wed Feb 20, 2008 10:28 pm

We actually did find two GLCD.bas files earlier but renamed the one we don't use.
-Tom

User avatar
RadioT
Registered User
Registered User
Posts: 157
Joined: Tue Nov 27, 2007 12:50 pm
Location: Winnipeg, Canada

Post by RadioT » Thu Feb 21, 2008 4:59 pm

Hi David,

I did a clean re-install and only moved in the user libraries needed to make the compiles work. Now I do not get the differences in the .asm files.

Therefore, some rogue file was being read. I had various development files in different directories of the Swordfish dir, there had to be something in there that shouldn't have been. The clean re-install did the trick.

I also placed a null ($00) at the end of the string after the EUSART times out. Now the string functions behave MUCH better!

Thanks again,

-Tom

Post Reply