Shop OBEX P1 Docs P2 Docs Learn Events
Serial Communication Protocol — Parallax Forums

Serial Communication Protocol

idbruceidbruce Posts: 6,197
edited 2011-01-23 13:12 in Propeller 1
llllllllllllllllllll
«1

Comments

  • John AbshierJohn Abshier Posts: 1,116
    edited 2011-01-23 10:04
    The function will return the integer in a long which is 4 bytes. Do you mean the ASCII representation of the integer?

    John Abshier
  • idbruceidbruce Posts: 6,197
    edited 2011-01-23 10:08
    John

    Thanks for your response.

    Sorry, yes the ASCII representation of the integer.

    Bruce
  • Heater.Heater. Posts: 21,230
    edited 2011-01-23 10:10
    Bruce,


    If a function returns anything at all that will be a LONG integer.

    In which case it's size is 4 bytes and it's value is whatever it is. Yo can pass it as a parameter to another method directly.

    BUT

    Perhaps you mean that the returned integer value is actually a pointer, the address of, some string representation of some other integer.

    Which do you mean?
  • Heater.Heater. Posts: 21,230
    edited 2011-01-23 10:12
    OK,

    So now is that string representation, say "1234", terminated with a NULL byte (like in C) or does it have a byte at the beginning indicating the length (like in Pascal)?

    Either way the length is built into the string and you need only pass it's address to the next function. The next function may then need to deal with the length issue.
  • idbruceidbruce Posts: 6,197
    edited 2011-01-23 10:16
    Heater

    I want to pass some data to FullDuplexSerial as a decimal value, however I need to know the size of that value to know when to stop reading. For example, when I reach, a delimiter, the next value will be data size, which will indicate how many bytes to read to get the actual value I seek.

    <delimiter><data size><value I seek>

    Bruce
  • idbruceidbruce Posts: 6,197
    edited 2011-01-23 10:19
    Sorry Again

    Actually it would be <delimiter><data size><delimiter><value I seek>
  • idbruceidbruce Posts: 6,197
    edited 2011-01-23 10:23
    Here is a better example of what I am trying to accomplish. US is the delimiter.
    Sender.Tx(STX)
      { Format and send main data content between here and the next comment }
      Sender.Dec(DataSize)
      Sender.Tx(US)
      Sender.Str(DataString)
      Sender.Tx(US)
      Sender.Dec(CRCSize)
      Sender.Tx(US)
      Sender.Dec(CRC)
      { Main data content should have been formatted and sent by this point }
      'Send End Of Text message to the Receiver
      Sender.Tx(ETX)
    
    Bruce
  • Mike GreenMike Green Posts: 23,101
    edited 2011-01-23 10:26
    It's still not clear what you're asking. If you're returning the number as an ASCII string, the way to specify the length of the string is to use a zero byte at the end of the string value and to return the address of the start of the string. As mentioned, Pascal and some other languages store the length of the string in the first byte of the string and the actual string characters begin at the 2nd byte. The address of the length byte is what is returned. If you want to return two items from your function, one a small number (a length or size) and one an address (pointer), you can make use of the fact that, on the Propeller I, addresses are always 16 bits or less, so you can return the small number in the upper byte of the 32 bit return value and the address (pointer) in the lower 3 bytes. You'd use something like: "RETURN (smallNumber << 24) | (address & $FFFFFF)" in the function and you'd need to do something like this in the routine calling the function:

    pointer := functionCall( ... )
    small := temp >> 24
    pointer := pointer & $FFFFFF
  • Heater.Heater. Posts: 21,230
    edited 2011-01-23 10:28
    So you are saying an example string might look like:
    ;3;593;
    Where the delimiter is the semi-colon, the length is 3 and the data is 593?

    In which case if you have the address of all that as "strAddr" you can use BYTE[strAddr] which will get the first semi-colon. Then increment strAddr and use BYTE[...] again which will get you the length of 3. And so on.

    So just put that in a loop and check for the delimeters and values as you go around.

    When you get to the value part just pass it to FullDuplexSerial one character at a time as they are discovered. "ser.tx(someChar)"

    However, if you have all these nice delimeters in your strings why bother putting the length in there anyway?
  • Mike GreenMike Green Posts: 23,101
    edited 2011-01-23 10:30
    If your string is properly terminated by a zero byte, you can compute the string size (very quickly) by using STRSIZE:

    Sender.dec(STRSIZE(DataString))
    Sender.tx(US)
    Sender.str(DataString)

    You don't have to explicitly pass the length of the string this way.
  • idbruceidbruce Posts: 6,197
    edited 2011-01-23 10:32
    Because I am using a cyclic redundancy check to ascertain the validity of the data.
    CON
      _CLKMODE = XTAL1 + PLL16X
      _XINFREQ = 5_000_000  
      
      #1, HOME, GOTOXY, #8, BKSP, TAB, LF, CLREOL, CLRDN, CR
      #14, GOTOX, GOTOY, CLS
      BUFFER_SIZE = 80
      'Transmission and reception constants
      STX = 2 'Start Of Text
      ETX = 3 'End Of Text
      ENQ = 5 'Enquire the RESULT of the transmission
      ACK = 6 'Positive RESULT of a successful transmission
      NAK = 21 'Negative RESULT of an unsuccessful transmission
      US = 31 'Data block seperator which is used for parsing
      'Cyclic redundancy check constants
      CRC_HEAD = %1110_0000 '4 bit tag, 4 bit payload
      CRC_MASK4 = %0000_1111 'Payload mask
      CRC_TAIL = %10_000000 '2 bit tag, 6 bit payload
      CRC_MASK6 = %00_111111 'Payload mask   
      
    VAR
      LONG stack[32]
      BYTE Buffer[BUFFER_SIZE]
        
    OBJ
      Terminal : "FullDuplexSerial"
      Sender : "FullDuplexSerial"
      Receiver : "FullDuplexSerial"
    PUB Main
      Terminal.Start(31, 30, 0, 115_200)
      Receiver.Start(0, 1, 0, 9600)
      
      WAITCNT(CLKFREQ * 2 + CNT)
      Terminal.Tx(CLS)
      Terminal.Rxflush
      COGNEW(SendDataStringWithCRC2, @stack)
      MonitorForIncomingData  
    PUB MonitorForIncomingData | Index, Char
      'Monitor for Start Of Text (Incoming Data) 
      REPEAT UNTIL Receiver.Rx == STX
      'Start Of Text has been found
      'Start buffer filling process
      Terminal.Str(string("Buffer filling has started!", CR))
      'Clear the buffer and fill it with zeros 
      BYTEFILL(@Buffer, 0, BUFFER_SIZE)
      'Set buffer array index to zero  
      Index := 0
      'Fill the buffer with pertinent data until
      'End Of Text is found 
      REPEAT WHILE (Char := Receiver.Rx) <> ETX
        Buffer[Index] := Char
        Index++
      'Buffer filling complete
      Terminal.Str(string("Buffer filling has ended!", CR))
      
      'Display the data contained within the Buffer
      Terminal.Str(@Buffer)
      Terminal.Tx(13)
    PUB SendDataStringWithCRC1(DataString) | DataSize, CRC
      Sender.Start(1, 0, 0, 9600)
      WAITCNT(CLKFREQ * 4 + CNT)
      DataSize := STRSIZE(DataString)
      CRC := ComputeCRC(@DataString, DataSize)  
      'Send Start Of Text message to the Receiver
      Sender.Tx(STX)
      { Format and send main data content between here and the next comment }
      Sender.Dec(DataSize)
      Sender.Tx(US)
      Sender.Str(DataString)
      Sender.Tx(US)
      Sender.Dec(CRC)
      { Main data content should have been formatted and sent by this point }
      'Send End Of Text message to the Receiver
      Sender.Tx(ETX)
    PUB SendDataStringWithCRC2 | DataString, DataSize, CRC
      Sender.Start(1, 0, 0, 9600)
      WAITCNT(CLKFREQ * 4 + CNT)
      DataString := STRING("The big, bad, burly, brown bear, barely bit the beautifully bestowed bosoomly blonde.")
      DataSize := STRSIZE(DataString)
      CRC := ComputeCRC(@DataString, DataSize)  
      'Send Start Of Text message to the Receiver
      Sender.Tx(STX)
      { Format and send main data content between here and the next comment }
      Sender.Dec(DataSize)
      Sender.Tx(US)
      Sender.Str(DataString)
    '  Sender.Tx(US)
    '  Sender.Dec(CRCSize)
      Sender.Tx(US)
      Sender.Dec(CRC)
      { Main data content should have been formatted and sent by this point }
      'Send End Of Text message to the Receiver
      Sender.Tx(ETX)
    PUB ComputeCRC(DataPointer, SizeOfBytes)
      
      REPEAT SizeOfBytes
      
        RESULT ^= (BYTE[DataPointer++] << 8)
        
        REPEAT 8
        
          RESULT <<= 1
          
          IF RESULT & $10000
          
            RESULT ^= $1021
            
      RESULT &= $FFFF
    
  • idbruceidbruce Posts: 6,197
    edited 2011-01-23 10:39
    I already know the string size for DataString, it is the size of the return value of ComputeCRC that seek, because its size will not be a constant size
  • Heater.Heater. Posts: 21,230
    edited 2011-01-23 10:39
    Looks like you will have to loop around all the bytes in the string parsing out the delimeters, length, value. Maintain a pointer containing the address of the actual start of the value digits. Pass that and the length to the CRC function. I might be tempted to add a routine to FullDuplexSerial that takes a string of bytes and a lenth. like your CRC function..
  • Heater.Heater. Posts: 21,230
    edited 2011-01-23 10:42
    Bruce,
    it is the size of the return value of ComputeCRC that seek

    Make life easy, change ComputeCRC to return an actual integer CRC value not a string representation of it. Then just pass that to FullDuplexSerial as str.dec(CRC)
  • idbruceidbruce Posts: 6,197
    edited 2011-01-23 10:43
    Heater

    The DataString goes to the ComputeCRC befere it is sent to the Receiver(FDS).

    Bruce
  • idbruceidbruce Posts: 6,197
    edited 2011-01-23 10:45
    Heater

    If I am not mistaken, it does already return an integer instead of a string value.

    Bruce
  • idbruceidbruce Posts: 6,197
    edited 2011-01-23 10:46
    If it returned a string I would just use strsize
  • MagIO2MagIO2 Posts: 2,243
    edited 2011-01-23 10:48
    Hi Bruce,

    is this question related to the other serial thread? You want some propellers to communicate with each other, right? I don't understand why you want to convert integers to strings for transmitting, read it as string on receiver-side and then parse it again to have an integer. This means lots of extra work for the propeller and for you making the code work.
    I'd simply send "binary" data. It's easy to write some functions like txWord or txLong (depending on the range your integers will have) and the corresponding rx functions.
  • idbruceidbruce Posts: 6,197
    edited 2011-01-23 10:49
    MagIO

    The whole point of the CRC function is the numeric value. It tests the validity of the data.

    Bruce
  • idbruceidbruce Posts: 6,197
    edited 2011-01-23 10:52
    That is just it, I don't want to pass the the CRC value as a string. But I need to know when I have the crc value in a buffer and only the crc value in the buffer.
  • MagIO2MagIO2 Posts: 2,243
    edited 2011-01-23 10:57
    I don't talk about the CRC, I talk about converting everything to string or not and later on back to integers. A CRC does not care what kind of data is behind. So, you can have a CRC value for a string or for a long.
  • idbruceidbruce Posts: 6,197
    edited 2011-01-23 11:05
    MagIO

    Someone already provided a solution, but it involves conversion. If I know the size of the data during my reads, I really shouldn't have to do any conversion, just read x amount of bytes and that goes in one buffer, read another amount of bytes and that goes into another buffer. But I need to know the size of my reads for filling the buffers correctly.

    Bruce
  • idbruceidbruce Posts: 6,197
    edited 2011-01-23 11:10
    I am trying to change this:
    'Fill the buffer with pertinent data until
      'End Of Text is found 
      REPEAT WHILE (Char := Receiver.Rx) <> ETX
        Buffer[Index] := Char
        Index++
    

    To this:
    'Fill the buffer with pertinent data until
       
      REPEAT SizeOfData
        Buffer[Index] := Char
        Index++
      'End Of Text is found
      REPEAT WHILE (Char := Receiver.Rx) <> ETX
    
  • Tracy AllenTracy Allen Posts: 6,666
    edited 2011-01-23 11:14
    Bruce,
    So, you need to insert the length of the string at the beginning of the transmission, including the length of the CRC as ascii? The easiest way around that is to pad the transmission of the CRC with zeros. So as decimal, for example, 00767, always 5 digits to be added to the main data length.

    In your example there are three instances fullDuplexSerial. Nothing wrong with that, except that it starts three cogs. If that becomes limiting, look at pcFullDuplexSerial4FC, which runs 4 uarts out of one cog.
  • Heater.Heater. Posts: 21,230
    edited 2011-01-23 11:15
    This is all very confusing.

    If I wanted to transfer a bunch of integer values via FullDuplex Serial I might just send them as raw binary. Four BYTEs per LONG. I'd have a LONG or WORD at the beginning to indicate the length of my message in bytes. I'd have a LONG or WORD at the end to hold the CRC value. I'd have some known BYTE value like STX to indicate the start of a message and perhaps some other to indicate the end. I'd have some hand shaking going on between sender an receiver to be sure this all stays in sync.
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2011-01-23 11:19
    So send the data in hex -- fixed length -- and be done with it.

    -Phil
  • idbruceidbruce Posts: 6,197
    edited 2011-01-23 11:24
    Okay Heater

    1. read = I want to send a string = "Test" the data length is strsize(string("Test")) so for data length read Test into buffer skip over delimiters


    go to the next data length which indicates CRC size and then read for CRC size read into CRC buffer.
  • Heater.Heater. Posts: 21,230
    edited 2011-01-23 11:25
    Phil,

    Now that is an excellent idea.

    Because if you do so you can still use other non-hex characters as markers/delimiters for start of message, end of message etc.

    If you can tolerate halving the data transfer rate that is.
  • Heater.Heater. Posts: 21,230
    edited 2011-01-23 11:29
    Bruce,

    That's even more confusing:)

    Can you write for us an example of how a message will look on the line?
  • idbruceidbruce Posts: 6,197
    edited 2011-01-23 11:41
    Heater

    My messages are simple "20.2500"

    Of course that can be accomplished by Sender.Str(STRING("20.2500") but that does not include the indication of the start of a transmission, delimiters, error checking, additional parameters if any, etc.... or end of a transmission.

    My goal is to create an object that can pass numerous values, with each value having a error check all during a single valid transmission between STX and ETX.

    Bruce
Sign In or Register to comment.