News:

Let's find out together what makes a PIC Tick!

Main Menu

HRSIn and Wait.

Started by david, Aug 18, 2024, 11:45 PM

Previous topic - Next topic

david

Hello All,
I'm trying to send data between a main micro and a small OLED display micro and all was fine until feature creep happened.
I'm now trying to push data to the display and depending on the first character received I need to jump to two different labels.
What I need to achieve is something like this-
HRSIn wait ("X" or "Y")
Jump to label X/label Y

The serial data "X" (if sent) would always precede the serial data "Y" so it opens up the possibility of using the Timeout function where I check briefly for "X" before moving on and waiting for "Y".

Any thoughts would be welcome.

I'm starting to wonder if I'm reaching a coding cutoff age as this wouldn't be a problem in years past but then maybe coding is slowing down the aging process.....

Cheers,
David

SeanG_65

Why not just extract the first character then;

If char = "X" goto X
elseif char = "Y" go to Y
endif

Could also use procedures I guess

david

Thanks for the reply.
Yes, that part should work but my problem is I have to wait for one of two characters being sent and the wait command relies on a defined character or group of characters.
I think I have to read any incoming character then test if it's "X" or "Y" and jump accordingly as you have shown.

Cheers,
David

RGV250

Hi David,
QuoteI think I have to read any incoming character then test if it's "X" or "Y" and jump accordingly as you have shown.
I am not sure if this is the same as what you have in mind but I would receive the whole string and then use something like

TestChar = Left$(In_String, 1) 'Get the first character of the message.
If TestChar = X then

to test the first character and action from that.

Bob

david

Thanks Bob,
That looks like better approach than I was considering and is definitely worth a shot. 
I'm very aware of the limitations of a "push" only link between the main micro and the display micro.  The display doesn't know when something's coming and the main micro doesn't care.  Presently it has leading characters that the display waits for then collects the following data.
I'll do some playing.  At least it's not like model aeroplanes where I have to wait for the glue to dry before moving on....

Cheers,
David

joesaliba

Quote from: david on Aug 19, 2024, 08:35 AMAt least it's not like model aeroplanes where I have to wait for the glue to dry before moving on....

 ;D  ;D know the feeling!!

RGV250

QuoteAt least it's not like model aeroplanes where I have to wait for the glue to dry before moving on....
I gave up with those years ago, I spent more time repairing them after crashing than flying them :(

trastikata

#7
Hello David,

Your situation is quite common with UART comms, especially with GPS modules where they output the NMEA messages and you have to look for the leading character and start collecting the entire message. Instead of HRSIn set the device in firmware using a Hardware interrupt and deal with strings and characters there, example:


Device = 18F25J50
Declare Xtal = 4

Symbol RC2IF = PIR3.5
Symbol OERR = RCSTA2.1
Symbol CREN = RCSTA2.4
Symbol PEIE = INTCON.6

...

On_Hardware_Interrupt GoTo ISR_HANDLER
GoTo Main

ISR_HANDLER:
    Context Save
        'Clear Overrun Error bit
        If OERR = 1 Then
            CREN = 0
            CREN = 1
        EndIf

        If RC2IF = 1 Then
            temp_byte1 = RCREG2

            If temp_byte1 = $A Or k > 84 Then
                If usart_rx = 1 Then
                    usart_rx = 0
                    usart_rx_ready = 1
                EndIf
            EndIf

            If usart_rx = 1 Then
                GPS_Buffer[k] = temp_byte1
                Inc k
            EndIf

            If temp_byte1 = 36 And usart_rx_ready = 0 Then
                Clear GPS_Buffer : k = 0
                GPS_Buffer[k] = temp_byte1
                usart_rx = 1
                Inc k
            EndIf

            RC2IF = 0
        EndIf
    Context Restore

Main:
    SetUart()
  
End


Proc SetUart()
    RCSTA2.7 = 1    'SPEN: Serial port Enable bit
    RCSTA2.2 = 0    'FERR: Framing Error bit clear
    RCSTA2.1 = 0    'OERR: Overrun Error bit clear
    BAUDCON2.1 = 1  'WUE: Wake-up Enable bit
    BAUDCON2.3 = 1  '16-Bit Baud Rate Register Enable bit
    TXSTA2.2 = 0    'BRGH: High Baud Rate Select bit
    SPBRGH2 = 0     'EUSARTx Baud Rate Generator Register High Byte
    SPBRG2 = 77     'EUSARTx Baud Rate Generator Register Low Byte
    INTCON.6 = 1    'Peripheral/Low-Priority Interrupt Enable bit
    RCON.7 = 0      'IPEN: Interrupt Priority Enable bit
    PIE3.5 = 1      'RC2IE: EUSART1 Receive Interrupt Enable bit
    PIR3.5 = 0      'RC2IF
    RCSTA2.4 = 1    'CREN
EndProc

david

Thanks for your input trastikata - I'll ponder that at a more respectable time of day.

Regarding GPS - I've done a lot of work in GPS but never collected a full sentence because there's a lot of redundant data.
I used comma counting-

HRSIn Wait ("VTG,")       'Wait for VTG sentence
For comma=1 To 6          'Step out 6 fields
While HRSIn<>",": Wend
Next comma
HRSIn Dec kph             'take next data field as kph

To comply with NMEA standard all GPS systems have the same number of fields per sentence but the size of the fields can vary.  This is typically time, Lat, Lon, heading and speed resolutions.  You certainly can't count on a fixed string size but the fields will always be in the same place.

Sadly my requirement is not nearly as straight forward but I'm probably not describing it properly.

Cheers,
David



Wimax

The solutions also depend on the requirements of your program, I think that the interrupt, as proposed by Trastikata, is the best solution if you cannot stop the program flow polling the device or the MCU isn't fast enough to do other things in between. 

top204

#10
When receiving into an array or a String, I always prefer to receive each byte seperately, even if the data is received via a serial buffer interrupt. This means that each byte received can be examined if required, and an end can be signalled if a particular byte is received, or sequence of bytes received.

Below is a very over-complicated method of receiving a byte array and examining the first two characters and performing a task based upon them. It is untested, but looks functional (over complex for the task, but functional in its mechanism):

'
'   /\\\\\\\\\
'  /\\\///////\\\
'  \/\\\     \/\\\                                                 /\\\          /\\\
'   \/\\\\\\\\\\\/        /\\\\\     /\\\\\\\\\\     /\\\\\\\\   /\\\\\\\\\\\  /\\\\\\\\\\\  /\\\\\\\\\
'    \/\\\//////\\\      /\\\///\\\  \/\\\//////    /\\\/////\\\ \////\\\////  \////\\\////  \////////\\\
'     \/\\\    \//\\\    /\\\  \//\\\ \/\\\\\\\\\\  /\\\\\\\\\\\     \/\\\         \/\\\        /\\\\\\\\\\
'      \/\\\     \//\\\  \//\\\  /\\\  \////////\\\ \//\\///////      \/\\\ /\\     \/\\\ /\\   /\\\/////\\\
'       \/\\\      \//\\\  \///\\\\\/    /\\\\\\\\\\  \//\\\\\\\\\\    \//\\\\\      \//\\\\\   \//\\\\\\\\/\\
'        \///        \///     \/////     \//////////    \//////////      \/////        \/////     \////////\//
'                                  Let's find out together what makes a PIC Tick!
'
' Receive data into an array and extract the first and second characters for examination
' Written for the Positron8 compiler by Les Johnson.
'
    Device = 18F25K20                           ' Tell the compiler what device to compile for
    Declare Xtal = 16                           ' Tell the compiler what frequency the device will be operating at (in MHz)
'
' Setup USART1
'
    Declare Hserial_Baud = 9600                 ' Set the Baud rate to 9600
    Declare HRSOut_Pin  = PORTC.6               ' Set the TX pin
'
' Create global variables here
'
    Dim wChars       As Word                    ' Holds the first and second character of the array received
    Dim bRecData[64] As Byte Heap               ' Holds the data received
   
'----------------------------------------------------------------------------
' The main program starts here
'
Main:
    Do                                          ' Create a loop
        Clear bRecData                          ' Clear the array before receiving into it
        wChars = DataReceive(bRecData, 10)      ' Receive data into the array: bRecData
        If wChars.Byte0 = "X" Then              ' Is the first character "X"?
            If wChars.Byte1 = "Y" Then          ' Yes. So is the second character "Y"?
                XandY()                         ' Yes. So call the X and Y procedure
            Else                                ' Otherwise...
                X_Only()                        ' Call the X with no Y procedure
            EndIf
          
        ElseIf wChars.Byte0 = "Y" Then          ' Is the first character "Y"?
            If wChars.Byte1 = "X" Then          ' Yes. So is the second character "X"?
                YandX()                         ' Yes. So call the Y and X procedure
            Else                                ' Otherwise...
                Y_Only()                        ' Call the Y with no X procedure
            EndIf
      
        ElseIf wChars = $FFFF Then              ' Was the return result $FFFF?
            RX_TimedOut()                       ' Yes. So the receive timed out
       
        Else                                    ' Otherwise...
            ' Use the data received without an "X" or "Y"
        EndIf
    Loop                                        ' Do it forever
 
'----------------------------------------------------------------------------  
' Do something here if the first character was "X" without "Y" following it
'
Proc X_Only()
   
EndProc

'----------------------------------------------------------------------------
' Do something here if the first character was "X" and the second character was "Y"
'
Proc XandY()
   
EndProc
 
'----------------------------------------------------------------------------
' Do something here if the first character was "Y" without "X" following it
'
Proc Y_Only()
   
EndProc

'----------------------------------------------------------------------------
' Do something here if the first character was "Y" and the second character was "X"
'
Proc YandX()
   
EndProc

'----------------------------------------------------------------------------  
' Do something here if the receive timed out
'
Proc RX_TimedOut()
   
EndProc
   
'----------------------------------------------------------------------------
' Receive serial data into an array when one of two characters are detected and return the first two characters of it
' Input     : pArrAddr holds the address of the array to fill with the data received
'           : pSize holds how many elements of the array to load into (1 to 255)
' Output    : Returns the first and second characters received in the low and high byte,
'             unless a timeout happens and it will return the value $FFFF
' Notes     : For an 18F device because it uses the POSTINC0 SFR
'
Proc DataReceive(ByRef pArrAddr As Word, pSize As Byte), Word  
    Dim bRecByte As Byte                        ' Holds the byte received
    Dim bCnt     As Byte                        ' Counts the bytes received
    Dim wFSR0_SFR As FSR0L.Word                 ' Combine SFRs FSR0L\H into a 16-bit SFR
   
    wFSR0_SFR = pArrAddr                        ' Load the address of the array into FSR0L\H
'
' Perform a similar method to Wait for "X" or "Y" characters
'
    Do                                          ' Create a loop
        HSerIn 2000, TimedOut, [bRecByte]       ' Receive a data byte into bRecByte with a timeout        
        Select bRecByte                         ' \
            Case "X", "Y"                       ' / Is the byte received "X" or "Y"?
                Result.Byte0 = bRecByte         ' Yes. So load the low byte of the Result with it
                POSTINC0 = bRecByte             ' Load the array with the byte received
                Break                           ' Exit the loop
        EndSelect
    Loop                                        ' Loop until the characters are received or a timeout occurs
    pSize = pSize - 2                           ' Make the value in pSize correct for the loop
    For bCnt = 0 To pSize                       ' Create a loop for the amount of array elements to receive
        HSerIn 2000, TimedOut, [bRecByte]       ' Receive a data byte into bRecByte with a timeout 
        POSTINC0 = bRecByte                     ' Load the array with the byte received
        If bCnt = 0 Then                        ' Is it the second element received? (the first element was received before the loop)
            Result.Byte1 = bRecByte             ' Yes. So return the second character received in the high byte of Result
        EndIf
    Next                                        ' Close the loop
    ExitProc                                    ' Exit the procedure
'
' Jump here if a timeout occurs, and return $FFFF
'   
TimedOut:
    Result = $FFFF                              ' Return $FFFF for a timeout
EndProc

But the very same could be performed by receiving the data into an array, then when completed, look at the array's first and second elements, with something like:

If MyArray#0 = "X" Then
    If MyArray#1 = "Y" Then
        XandY()
    Else
        X_Only()
    Endif
ElseIf MyArray#0 = "Y" Then
    If MyArray#1 = "X" Then
        YandX()
    Else
        Y_Only()
    Endif
EndIf

The data following "X" or "Y" or "XY" or "YX" probably needs to be used anyway, so the jump location can remove the first, second, or both elements from the array and parse the rest of the data.

david

Dear Les,
Many thanks for your time on those examples.  I think that's the logic I was looking for.
In the cold light of day I have decided to list the salient moments of both the main and display software.  I'm beginning to suspect that I have the display waiting for input after the main micro has already sent it.  I have a simple start screen with both main and display revision numbers shown and this hangs there for 2500mS, also some beeps that are just simple delays but which add to the total time.
I'm hopeful that after rearranging the HRSOut on the main and HRSIn Wait on the display, it should make more sense but will still likely need your logic example.
Thanks again,
David

david

Can someone please confirm my syntax and understanding of the following-

HRSIn {300,start},Wait("P"),Dec stopval
If stopval>19 Then GoTo autoset

If I send something like P50 the program detects the "P" and jumps to autoset.
I thought by having the {300,start} that it would wait for 300mS to detect a "P" and if not detected it would jump to start but I don't seem to exit that line of code.  I added a flag on a spare pin but it never goes high.
What have I missed?

Cheers,
David

top204

#13
When using modifiers, it is more clearer to use the HSerin commands. I created the HRsin commands for simple receives with no modifiers, but I had to add some capabilities later, and it actually makes things less readable.

The HRSin and HSerin commands both operate exactly the same, and both generate the same assembler code.

So for those modifiers, or multiple receives, it would clearer to use:

HSerin 300, start, [Wait("P"), Dec stopval]

If HRsin is used, it will be:

HRSIn {300, start}, Wait("P"), Dec stopval


david

Thank you for the explanation.  I may go back and change from HRSIn to HRSerin anyway.
I could not get the expression to time out and jump to start.  In desperation I tried dropping the Wait ("P") and found that this worked as hoped-

HRSIn {300,start} Dec stopval
If stopval>19 then Goto autoset

So if I sent P50 it would jump to autoset and if P50 was not sent it jumped to start.
Perhaps not as safe as it's not detecting the "P" but it was working nicely - until I tried to "improve" things a little.  After 20 years you would think I would learn to save working code before enhancing it....

Cheers,
David

top204

The timeout will still occur while waiting for a particular byte, so maybe your timeout is operating too quickly. Also make sure you have a high value pullup resistor on the RX line, so it is not floating. I always use a 150K or 180K resistor as the RX pullup, so it does not drag on the line, but keeps the pin from floating, which will cause havok with a timeout because the pin will continuously move high or low and trigger it.

You can see by the assembler code that the timeout is still operating when waiting:

F1_000085 equ $ ; in [TEST_18F25K20_RECEIVEDATA.BAS] HSerin 300, start,[WAIT("P"), Dec stopval]
    movlw 1
    movwf GENH,0
    movlw 44
    movwf GEN,0
_pblb__17
    rcall __hrsin1_with_timeout__
    bnc start
    sublw 80
    bnz _pblb__17
    clrf RPFH,0
    clrf GEN4H,0
    rcall __Dec_StreamIn_
    bnc start
    movwf stopval,0

See that it calls the "__hrsin1_with_timeout__" library routine while waiting for the value of 80, which is the ASCII "P" value.

Frizie

David, the soldering oven program contains an interrupt-based example.
When data comes in from the Nextion HMI in UART1, an interrupt follows.
By looking at what data (or character) is in RCREG, I can read in the rest of the data using HSERIN.

At the soldering oven, I only have to "wait" until one of the following 2 characters is received: 0x65 or 0x66.
If the first character received is 0x65, the rest contains data about HMI_Page, HMI_CompID and HMI_Event.
If the first character received is 0x66, the data that follows contains HMI_ActivePage.

See here, at the bottom of the PIC Basic program from it: Listing soldering oven
Ohm sweet Ohm | www.picbasic.nl

david

Hello Les,
I did try various time out values but nothing worked until I removed the Wait,"P" and just detected the following decimal value.  I will revisit this.
Rx in is connected directly to Tx out on the other micro. I have seen floating Rx inputs cause grief.  I'm aware that using one micro with on-board clock to talk to another with on-board clock could result in baudrate issues but none of the other data is showing clock rate problems and both micros are using their internal calibrated OSCON values.
Thank you for the explanations.  Tomorrow is another day....

Cheers,
David

david

Frizie - many thanks for linking to your example.  That's very similar to what I was wanting to achieve.  My biggest problem is having 2 micros with most of the work done by one but the display board also does things autonomously and having serial inputs wait while the other device sends is sometimes proving difficult to synch with this latest feature upgrade. My abundance of free time doesn't quite make up for lack of skills.

Cheers,
David

david

Well this is getting both embarrassing and frustrating.  I followed up on Les's advice and changed to using HSerIn but I still can't get any sense from the following-

HSerIn 300,start,[Wait ("P"), Dec stopval]
If stopval>9 then Goto autoset

I know the serial port is waiting and it's continuously sent one of two streams of data.
The first is "Pnn" where nn is a two digit number.   (sent every 200mS)
The second is "Rnn Mn Sn"   Three fields preceded by characters. (sent every 14mS to 700uS)

If I change code to force it to go to either the start label or the autoset label then the following code behaves as expected.
Frizie's example uses the Time out function but I haven't come across an example using both Time out and Wait. 
I also have to ask if there is a baud rate limit on these functions?   I'm using 115,200 baud.

Next step might be to lash up a small, minimal code test platform with a couple of LEDs and send serial data from the PC.
Any other thoughts on why this 2 line piece of code could be stalling me?  Is it time to switch to lawn bowls?

Cheers,
David