Converting HSERIN Data to Number


Closed Thread
Results 1 to 9 of 9
  1. #1

    Default Converting HSERIN Data to Number

    I'm using USART to pass info from one PIC to another. On the slave PIC, I want to read an incoming data stream which will tell me what PWM duty cycle to set on the slave's CCP module. The format of the data sent is this:

    S0D3255~

    The first character "S" is the 'sync' character (or start of new transmission); the 2nd is the Chip ID (since there will be a few slaves connected to one Master); third character is what option on the slave is going to change; the 4th character is the number of digits in the adjustment value ("255") and the last character is the termination.

    I have an ISR attached to the Rc pin of the USART module on my PIC, so it fires overtime a character comes into the buffer. Here's the ISR:

    Code:
    ' ***************************************************************
    ' [RX_INT - interrupt handler]
    ' ***************************************************************
    GetBytes: 
    ' TODO: Update code to handle AdjVal > 1 character (an array?)
    
        HSERIN [Serialdata]                    ' Get the serial data (guaranteed here to have at
    
        IF Serialdata = SyncByte THEN          
            IF SyncRcvd THEN                   ' if it's a Sync byte,
                state = 0                      '   last command was corrupted; reset 'state'
                ERROR = 1                      '   and indicate error
            ENDIF
            SyncRcvd = 1                       '   indicate Sync was rcvd
        ELSE
            IF SyncRcvd THEN
                IF !ForMe THEN                 ' if we haven't rcvd the ID
                    IF Serialdata = ThisChipID THEN 'and this byte matches the ID
                        ForMe = 1              '     indicate ID rcvd
                    ELSE
                        state = 0               '    Not meant for this chip, reset and wait for next msg
                    ENDIF                      
                ELSEIF !CmdRcvd THEN
                    CmdVal = SerialData
                    CmdRcvd = 1
                ELSEIF !LenAdjRcvd THEN
                    LenAdjVal = SerialData                
                    LenAdjRcvd = 1
                    i = 0
                ELSEIF !AdjRcvd THEN
                    AdjVal = SerialData
                    myArray[i] = AdjVal
                    i = i + 1
                    If i = ArrayLength THEN
                        AdjRcvd = 1
                    ENDIF
                ELSEIF !MsgRcvd THEN
                    If SerialData = TxEndChar THEN
                        MsgRcvd = 1
                    ENDIF
                ENDIF
            ENDIF
        ENDIF
    
    @ INT_RETURN
    In the Main loop:

    Code:
    Main:
        IF MsgRcvd THEN GOSUB ProcessCmdAdj    ' Process incomming data
    .
    .
    .
    GOT Main
    And here's where I output the result (using MCSP's Serial Communicator):
    Code:
    '*********** Process received command **************************************
    ProcessCmdAdj:
    '    varResult = myArray[0]*100 + myArray[1]*10 + myArray[2]
    
        HSEROUT ["Cmd: ", CmdVal, _  ' send Command, Length of Adjustment & Adjustment value and CR/LF
            ", LenAdj: ", LenAdjVal, " [", DEC LenAdjVal, "]", _
               ", Adj: ", myArray[0], myArray[1], myArray[2], 13, 10]
            
            
            '   ", Adj: ", AdjVal, " [", DEC AdjVal,"]", 13, 10] 
    
        state      = 0               ' indicate CMD & ADJ has been processed
    
        cmdVal     = 0
        LenAdjVal  = 0
        AdjVal     = 0
    
        For i = 0 to (ArrayLength - 1) ' clear out array
            myArray[i] = 0
        NEXT i
    
    RETURN
    I can't figure out how to take the 3 characters stored in myArray and convert it to a numerical 255 value that I can then use to set PWM duty cycle. As you can see from the commented-out line above, I tried multiplying the first 2 characters by 100 & 10, but that just gives garbled results. I know the ISR is building the array properly because the above HSEROUT shows me the correct characters in each item of myArray[].

    I've never had to do this kind of math/byte manipulation before, so I'd appreciate help here. The full code for the project is here:

    Code:
    ' :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
    ' Based on Darrel Taylor's example
    ' http://www.picbasic.co.uk/forum/showthread.php?t=9932&p=65066#post65066
    ' :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
    
    '  MPASM, LabX-1 , PBP3, Using PIC 16F877A @ 4mHz, 9600 Baud,
    '  USART and MAX232 to MCS Serial Communicator on PC
    
    ' ***************************************************************
    ' >>>>>>>>>>>>>>>>>    Slave IC ID: 0    <<<<<<<<<<<<<<<<<<<<<<<
    ' ***************************************************************
    
    ThisChipID CON "0"
    
    DEFINE OSC 4
    
    DEFINE HSER_RCSTA 90h ' enable serial port, 
    DEFINE HSER_TXSTA 24h ' enable transmit, 
    DEFINE HSER_BAUD 9600 ' set baudrate to 9600                   
    DEFINE HSER_CLOERR  1 ' automatic clear overrun error  
    
    TRISD  = %00000000    ' D to outputs for the LEDs
    TRISC  = %10000000    ' PORTC.7 is the RX input, PORTC.6 is the TX output
    ADCON1 = %00001111    ' Set up ADCON1 register no matter what yr doing!!!                      
            
    ':::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
    INCLUDE "DT_INTS-14.bas"     ' Base Interrupt System
    INCLUDE "ReEnterPBP.bas"     ' Include if using PBP interrupts
    
    ;-- Place a copy of these variables in your Main program -------------------
    ;--   The compiler will tell you which lines to un-comment                --
    ;--   Do Not un-comment these lines                                       --
    ;---------------------------------------------------------------------------
    ;wsave   VAR BYTE    $20     SYSTEM      ' location for W if in bank0
    wsave   VAR BYTE    $70     SYSTEM      ' alternate save location for W 
                                             ' if using $70, comment wsave1-3
    
    ' --- IF any of these three lines cause an error ?? ------------------------
    '       Comment them out to fix the problem ----
    ' -- Which variables are needed, depends on the Chip you are using -- 
    wsave1  VAR BYTE    $A0     SYSTEM      ' location for W if in bank1
    wsave2  VAR BYTE    $120    SYSTEM      ' location for W if in bank2
    wsave3  VAR BYTE    $1A0    SYSTEM      ' location for W if in bank3
    ' --------------------------------------------------------------------------
    
    ' ***************************************************************
    ' ASM Interrupt Definitions
    ' ***************************************************************
    
    ASM
    INT_LIST  macro    ; IntSource,           Label,  Type, ResetFlag?
            INT_Handler   TMR1_INT,      ReloadTMR1,   ASM,  no       ; MUST be first
            INT_Handler   TMR1_INT,      _T1handler,   PBP,  yes
            INT_Handler     RX_INT,        _GetBytes,  PBP,  yes
        endm
        INT_CREATE     ; Creates the interrupt processor
    ENDASM
    
    ':::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
    '   Variable definition
    ':::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
    
    ERROR_LED  VAR PORTD.1            ' rename the LED's
    LATE_LED   VAR PORTD.2
    HEART_LED  VAR PORTD.0
    
    PORTD = 0
    
    LEDonDELAY CON 4
    
    TxEndChar   CON "~"
    SyncByte    CON "S"
    
    i           VAR BYTE
    
    SerialData  VAR BYTE
    CmdVal      VAR BYTE
    LenAdjVal   VAR BYTE
    AdjVal      VAR BYTE
    ERRORtime   VAR BYTE
    LATEtime    VAR BYTE
    HEARTtime   VAR BYTE
    
    state         VAR BYTE         
      SyncRcvd    VAR state.0      ' sync byte received
      ForMe       VAR state.1      ' packet is for this device
      CmdRcvd     VAR state.2      ' command has been received
      LenAdjRcvd  VAR state.3      ' decimal length of adjustment value to follow
      AdjRcvd     VAR state.4      ' adjustment factor has been received
      MsgRcvd     VAR state.5      ' compelte message received
      ERROR       VAR state.6      ' sync received out of order
      LATE        VAR state.7      ' command received before last one was processed
    
    state      = 0                 ' Initialize default values
    
    i          = 0
    
    cmdVal     = 0
    LenAdjVal  = 0
    AdjVal     = 0
    serialdata = 0                 
    
    ArrayLength CON 3
    myArray VAR BYTE[ArrayLength]
    varResult VAR BYTE
                    
    ' Example of received command from master chip:
    ' ============================================
    '    SyncByte, Chip Id, Cmd Id, Length of modifier, <modifier>, TxEndChar
    'HSEROUT ["S",     "0",    "A",                "3",      "155",    "~"] 
    
    
    ;--- Change these to match the desired interrupt frequency -------------------
    ;--- See http://DarrelTaylor.com/DT_INTS-14/TimerTemplate.html for more Info.
    @Freq       = 10                  ; Frequency of Interrupts in Hz
    @Prescaler  = 2                   ; Timers Prescaler setting
    T1CON = $10                       ; $30 = Prescaler 1:8, TMR1 OFF
    ; $00=1:1, $10=1:2, $20=1:4, $30=1:8 --  Must match @Prescaler value
    
    ' Enable interrupts
    @ INT_ENABLE  RX_INT        ; enable USART RX_INT interrupts
    @ INT_ENABLE  TMR1_INT      ; enable Timer 1 interrupts
    GOSUB StartTimer            ; Start Timer 1
    
    Main:
        IF MsgRcvd THEN GOSUB ProcessCmdAdj    ' Process incomming data
    
        IF ERROR THEN                          ' if there's been an Error
            HIGH ERROR_LED                     '   Turn ON ERROR LED
            ERROR = 0                          '   reset the error flag
            ERRORtime = LEDonDELAY             '   start countdown till LED-OFF
            HSEROUT ["Error",13,10]
    ;        HSEROUT ["SyncRcvd: ", DEC SyncRcvd,", SyncIndx: ",DEC SyncIndx, _
    ;                ", ForMe: ", DEC ForMe,", CmdRcvd: ",DEC CmdRcvd, _
    ;                ", AdjRcvd: ",DEC AdjRcvd,", Cmd: ",CmdVal, ", Adj: ",DEC AdjVal,13,10] 
        ENDIF
        IF LATE THEN                           ' if command took too long
            HIGH LATE_LED                      '   Turn ON LATE LED
            LATE = 0                           '   reset the LATE flag
            LATEtime = LEDonDELAY              '   start countdown till LED-OFF
            HSEROUT ["Late",13,10]
        ENDIF
    GOTO Main
    
    '*********** Process received command **************************************
    ProcessCmdAdj:
    '    varResult = myArray[0]*100 + myArray[1]*10 + myArray[2]
    
        HSEROUT ["Cmd: ", CmdVal, _  ' send Command, Length of Adjustment & Adjustment value and CR/LF
            ", LenAdj: ", LenAdjVal, " [", DEC LenAdjVal, "]", _
               ", Adj: ", myArray[0], myArray[1], myArray[2], 13, 10]
            
            
            '   ", Adj: ", AdjVal, " [", DEC AdjVal,"]", 13, 10] 
    
        state      = 0               ' indicate CMD & ADJ has been processed
    
        cmdVal     = 0
        LenAdjVal  = 0
        AdjVal     = 0
    
        For i = 0 to (ArrayLength - 1) ' clear out array
            myArray[i] = 0
        NEXT i
    
    RETURN
    
    ' ***************************************************************
    ' [TMR1_INT - interrupt handler]
    ' ***************************************************************
    T1handler:
        IF ERRORtime > 0 THEN                  ' if the Error LED is ON
            ERRORtime = ERRORtime - 1          '   decrement the count
            IF ERRORtime = 0 THEN              '   when it reaches 0
                LOW ERROR_LED                  '   turn the Error LED OFF
            ENDIF
        ENDIF        
        IF LATEtime > 0 THEN                   ' if the LATE LED is ON
            LATEtime = LATEtime - 1            '   decrement the count
            IF LATEtime = 0 THEN               '   when it reaches 0
                LOW LATE_LED                   '   turn the LATE LED OFF
            ENDIF
        ENDIF
        HEARTtime = HEARTtime + 1              ' Toggle heartbeat ~.5sec
        IF HEARTtime = 7 THEN
            HEARTtime = 0
            TOGGLE HEART_LED
        ENDIF       
    @ INT_RETURN
    
    ' ***************************************************************
    ' [RX_INT - interrupt handler]
    ' ***************************************************************
    GetBytes: 
    ' TODO: Update code to handle AdjVal > 1 character (an array?)
    
        HSERIN [Serialdata]                    ' Get the serial data (guaranteed here to have at
    
        IF Serialdata = SyncByte THEN          
            IF SyncRcvd THEN                   ' if it's a Sync byte,
                state = 0                      '   last command was corrupted; reset 'state'
                ERROR = 1                      '   and indicate error
            ENDIF
            SyncRcvd = 1                       '   indicate Sync was rcvd
        ELSE
            IF SyncRcvd THEN
                IF !ForMe THEN                 ' if we haven't rcvd the ID
                    IF Serialdata = ThisChipID THEN 'and this byte matches the ID
                        ForMe = 1              '     indicate ID rcvd
                    ELSE
                        state = 0               '    Not meant for this chip, reset and wait for next msg
                    ENDIF                      
                ELSEIF !CmdRcvd THEN
                    CmdVal = SerialData
                    CmdRcvd = 1
                ELSEIF !LenAdjRcvd THEN
                    LenAdjVal = SerialData                
                    LenAdjRcvd = 1
                    i = 0
                ELSEIF !AdjRcvd THEN
                    AdjVal = SerialData
                    myArray[i] = AdjVal
                    i = i + 1
                    If i = ArrayLength THEN
                        AdjRcvd = 1
                    ENDIF
                ELSEIF !MsgRcvd THEN
                    If SerialData = TxEndChar THEN
                        MsgRcvd = 1
                    ENDIF
                ENDIF
            ENDIF
        ENDIF
    
    @ INT_RETURN
    
    ;---[TMR1 reload - interrupt handler]-----------------------------------------
    ASM                               ; Calculate Timer Reload Constant
    ReloadInst  = 8                   ; # of Intructions used to reload timer
      if ((Prescaler == 1)||(Prescaler == 2)||(Prescaler == 4)||(Prescaler == 8))
    MaxCount    = 65536 + (ReloadInst / Prescaler)
    TimerReload = MaxCount - (OSC*1000000/4/Prescaler/Freq)
        if ((TimerReload < 0) || (TimerReload > (65535-ReloadInst)))
            error Invalid Timer Values - check "OSC", "Freq" and "Prescaler"
        endif
      else
          error Invalid Prescaler
      endif
    ENDASM
    
    @Timer1 = TMR1L                   ; map timer registers to a word variable
    Timer1       VAR WORD EXT
    TimerReload  CON EXT              ; Get the External Constant
    TMR1ON       VAR T1CON.0          ; Alias the Timers ON/OFF bit
    
    ;---Reload Timer1------
    ASM
    ReloadTMR1
        MOVE?CT  0, T1CON, TMR1ON     ;  1     stop timer
        MOVLW    LOW(TimerReload)     ;  1     Add TimerReload to the 
        ADDWF    TMR1L,F              ;  1     value in Timer1
        BTFSC    STATUS,C             ;  1/2
        INCF     TMR1H,F              ;  1
        MOVLW    HIGH(TimerReload)    ;  1
        ADDWF    TMR1H,F              ;  1
        MOVE?CT  1, T1CON, TMR1ON     ;  1     start timer
      INT_RETURN
    ENDASM
    
    ;---Start/Stop controls -----
    StartTimer:
        Timer1  = TimerReload         ; Load Timer
        TMR1ON = 1                    ; start timer
    RETURN
    
    StopTimer:
        TMR1ON = 0                    ; stop timer
    RETURN

  2. #2
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,604


    Did you find this post helpful? Yes | No

    Default Re: Converting HSERIN Data to Number

    Hi,

    I think it's "the usual" case of not treating the data the same way in both ends.

    It looks to me like you are sending ASCII but you're treating the received data as binary/raw numbers.
    If you send the decimal digit '1', store that in myArray[0] and later multiply that by 100 what you'll get is 4900 because 49 is the ASCII code for '1'.
    To do what you want you can use ARRAYREAD with the DEC modifier or simply use the aproach you already have but "offset" the values to account for the ASCII encoding.
    Code:
    varResult = ((myArray[0]-48)*100) + ((myArray[1]-48)*10) + (myArray[2]-48)
    Or you could subtract 48 when receiving the digits.

    /Henrik.

  3. #3


    Did you find this post helpful? Yes | No

    Default Re: Converting HSERIN Data to Number

    Quote Originally Posted by HenrikOlsson View Post
    Hi,

    I think it's "the usual" case of not treating the data the same way in both ends.

    It looks to me like you are sending ASCII but you're treating the received data as binary/raw numbers.
    If you send the decimal digit '1', store that in myArray[0] and later multiply that by 100 what you'll get is 4900 because 49 is the ASCII code for '1'.
    To do what you want you can use ARRAYREAD with the DEC modifier or simply use the aproach you already have but "offset" the values to account for the ASCII encoding.
    Code:
    varResult = ((myArray[0]-48)*100) + ((myArray[1]-48)*10) + (myArray[2]-48)
    Or you could subtract 48 when receiving the digits.

    /Henrik.
    Thanks Henrik! I'll give it a go tonight after work.

    Preferably, I'd just send the output of the 8-bit ADC module on the Master chip directly to the Slave (so, 0-255) rather than converting it to ASCII in a 3-digit format (e.g. "027"), but then I can't figure out how to process that in the ISR if I'm only getting one character at a time.

  4. #4
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,604


    Did you find this post helpful? Yes | No

    Default Re: Converting HSERIN Data to Number

    So why don't you? ;-)

    Master:
    Code:
    ADResult VAR BYTE
    ....
    ....
    HSEROUT [ADResult]   '1 byte
    Slave:
    Code:
    Duty VAR WORD
    HSERIN [Duty]   '1 byte
    But now, if you're trying to debug this by sending te value of Duty to the PC terminal you need to use the DEC modifier so it converts the value to ASCII for display to us humans, HSEROUT [DEC Duty]

    /Henrik.

  5. #5


    Did you find this post helpful? Yes | No

    Default Re: Converting HSERIN Data to Number

    Quote Originally Posted by HenrikOlsson View Post
    So why don't you? ;-)

    Master:
    Code:
    ADResult VAR BYTE
    ....
    ....
    HSEROUT [ADResult]   '1 byte
    Slave:
    Code:
    Duty VAR WORD
    HSERIN [Duty]   '1 byte
    But now, if you're trying to debug this by sending te value of Duty to the PC terminal you need to use the DEC modifier so it converts the value to ASCII for display to us humans, HSEROUT [DEC Duty]

    /Henrik.
    Thanks again, Henrik. Can I still incorporate this into my ISR? For that to work, the interrupt on Rc would need to fire when 1 byte gets into the buffet, right? So, instead of an array for 3 'characters' (which on their own are 1 byte, right?) I can just grab the next byte after receiving the AdjCmdVal.

  6. #6
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,604


    Did you find this post helpful? Yes | No

    Default Re: Converting HSERIN Data to Number

    Hi,
    The UART RX interrupt always* fires for each byte that comes into the RX buffer, there's no way to set a "threshold" on when the interrupt fires. After all, the buffer is only two bytes deep. But yes, you could set your code up so that after proper sync, adress, command etc you grab one byte and that's it.

    * Well not always but that's details details details.....

    /Henrik.

  7. #7


    Did you find this post helpful? Yes | No

    Default Re: Converting HSERIN Data to Number

    Quote Originally Posted by HenrikOlsson View Post
    Hi,
    The UART RX interrupt always* fires for each byte that comes into the RX buffer, there's no way to set a "threshold" on when the interrupt fires. After all, the buffer is only two bytes deep. But yes, you could set your code up so that after proper sync, adress, command etc you grab one byte and that's it.

    * Well not always but that's details details details.....

    /Henrik.
    Thanks again, Henrik. Much appreciated. I'm going to try all this out tonight.

  8. #8
    Join Date
    Aug 2003
    Posts
    985


    Did you find this post helpful? Yes | No

    Default Re: Converting HSERIN Data to Number

    Code:
    serialbuffer byte [4]
    serialbyte var byte
    index var byte
    
    …
    
    ‘ ooh looky here… a serial byte arrived
    for index = 1 to 3
    serialbuffer[index] = serialbuffer[index-1]
    next index
    serialbuffer[0] = serialbyte
    Now instead of checking for the first byte in the serial string,
    check for what you call the termination byte.
    I don’t know what that is, so now it’s $FE.

    Code:
    if serialbuffer[0] = $FE then
    ‘ we know the bytes serialbuffer [0,1,2,3] are the desired 4 byte string in reverse
    ‘ unless only a partial command was received
    if serialbuffer[3] = “S” then
    ‘ we know now that the entire four byte command was received if there was no serial error
    endif
    endif

  9. #9
    Join Date
    Aug 2003
    Posts
    985


    Did you find this post helpful? Yes | No

    Default Re: Converting HSERIN Data to Number

    Now if you only wanted to grab say byte 2 (beginning counting from zero).

    Code:
    counter var byte
    counter = 0
    
    'oh looky here.. a serial byte arrived
    
    if serialbyte = “S” then
    counter = 0
    else
    counter = counter + 1
    endif
    
    if counter = 2 then
    byteyoucareabout = serialbyte
    endif
    A lot of this will fall apart if another byte of the command can be the same value as the “S” or the termination character,
    which is why it’s better to go something close to the first way, and check both start and end chars if they are always the same.
    Last edited by Art; - 18th November 2015 at 05:29.

Similar Threads

  1. Replies: 3
    Last Post: - 22nd June 2015, 16:15
  2. read bytes number in Hserin
    By Rony in forum Serial
    Replies: 13
    Last Post: - 13th August 2010, 10:32
  3. Unable to use the number 8 in DATA statement
    By dbachman in forum mel PIC BASIC Pro
    Replies: 6
    Last Post: - 7th December 2008, 06:34
  4. Problem converting data to what I need
    By oldcarguy85 in forum mel PIC BASIC Pro
    Replies: 1
    Last Post: - 23rd November 2007, 22:43
  5. Need help converting NMEA data
    By tcbcats in forum mel PIC BASIC Pro
    Replies: 0
    Last Post: - 21st August 2004, 08:28

Members who have read this thread : 1

You do not have permission to view the list of names.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts