Not transmitting the null character during UART transmission

Started by shantanu@india, Feb 22, 2022, 02:32 PM

Previous topic - Next topic

shantanu@india

Hi,
I am sending a stream of 12 bytes through hardware UART(18F24K22) where the first 10 bytes contain information & the last two byes are the checksum of the first 10 bytes.
Problem is whenever the checksum is 512D(200H) , the following program snippet transmits 11 bytes instead of the required 12 bytes.

      StrN outbuffer1=Str$(Dec6 tx_data)
            StrN outbuffer2=Str$(Dec4 shift_count)
            For buff_ptr2=0 To 3
                outbuffer1[buff_ptr2+6]=outbuffer2[buff_ptr2]
            Next
            checksum=0
            For buff_ptr2=0 To 9
                checksum = checksum+outbuffer1[buff_ptr2]
            Next
            outbuffer1[10]=checksum.HighByte
            outbuffer1[11]=checksum.LowByte         
            HSerOut[Str outbuffer1\12]
As you can see I am telling the compiler to send out 12 bytes but it is terminating the HSerOut as soon as a null byte is encountered.
What can be a workaround?
Regards
Shantanu

trastikata

Quote from: shantanu@india on Feb 22, 2022, 02:32 PMWhat can be a workaround?

Hi shantanu,

Why are you using a null-terminated byte array? Try with an ordinary byte array instead.

If you need the null-terminated byte array, then write a small procedure replacing the HSerOut where you manually load all bytes from the array in the hardware register.

shantanu@india

I'm not using null terminated byte array, I'm just building an array where a zero byte is being accidentally created.
Anyways... The PIC is 500 miles from my home and i can do little about it now. I can only edit the PC software online and ignore the checksum bytes so that a bytecount error is not generated.
Didn't know that /12 command would be ignored and only 11 bytes would be transmitted if a null is encountered
Regards
Shantanu

charliecoutas

I think the problem is that the terminator for a string is a null byte. When the runtime code encounters a null byte, that means "end of string", regardless of anything else.

Charlie

trastikata

QuoteStrN outbuffer1=Str$(Dec6 tx_data)

This is a null-terminated byte array and I think in this case the compiler command HSerOut will ignore anything behind the first 0-byte.

If you create an ordinary byte array, I think it should work as required.

shantanu@india

Trastikata,
What is the difference between a null terminated & ordinary byte array? I am just loading the outbuffer with some bytes & sending them out sith the Hserout command. Why can't a null byte lie somewhere inside the byte array? Why should the Hserout stop when it encounters a null byte when it is being instructed to send out 12 bytes?
I am using the StrN to generate a null terminated byte array....no doubt about it. But while loading the 'outbuffer' I am not loading the null terminator into it purposefully,,,,it is getting generated inadvertently.
Ill check the compiled code tomorrow.
Thanks anyway...Ill be more careful in future
Regards
Shantanu

top204

Remember, both the Str and the StrN commands were created to imitate a String variable from a Byte Array before String variables were available in the compiler, so they will not pass a null value. i.e. 0, in some commands.

This is where procedures come into their own, and now make some of the compiler's original modifiers  redundant. The modifiers were added to give some similarity to the BASIC Stamp II modules that were popular back then. Very expensive and very slow, but popular. :-)

Below is a procedure and a small demo snippet that shows how to transmit X elements from a byte array, regardless of what values it is loaded with:

    Device = 18F25K20
    Declare Xtal = 16
'
' Setup USART1
'   
    Declare Hserial_Baud = 9600
    Declare HRSOut1_Pin = PORTC.6

    Dim MyByteArray[12] As Byte = "123456789012"     ' Create an array and load it with values that can be seen on a serial terminal
'--------------------------------------------------------------------
Main:
    OutPacket(MyByteArray, 12)        ' Call the procedure and transmit 12 bytes from the array "MyByteArray"

'--------------------------------------------------------------------
' Transmit X elements from a byte array
' Input     : pArrAddr holds the address of the array to transmit
'           : pSize holds the amount of bytes to transmit from the array
' Output    : None
' Notes     : None

Proc OutPacket(ByRef pArrAddr As Word, pSize As Byte)
    Dim bData As Byte
    Repeat                          ' Create a loop for the amount of elements to transmit
        bData = Ptr8(pArrAddr++)    ' Read from the array, with address increment
        HRSOut bData                ' Transmit it
        Dec pSize                   ' Decrement the pSize variable
    Until pSize = 0                 ' Repeat until all elements are transmitted
EndProc

top204

Or for a more efficient procedure on an 18F device:

    Device = 18F25K20
    Declare Xtal = 16
'
' Setup USART1
'   
    Declare Hserial_Baud = 9600
    Declare HRSOut1_Pin = PORTC.6

    Dim MyByteArray[12] As Byte = "123456789012"    ' Create a Byte array and load it with values that can be seen on a serial terminal
   
    Dim wFSR0 As FSR0L.Word                         ' Combine FSR0L and FSR0H into a single 16-bit SFR
   
'--------------------------------------------------------------------
Main:
    OutPacket(MyByteArray, 12)      ' Transmit 12 elements from the array "MyByteArray"

'--------------------------------------------------------------------
' Transmit X elements from a byte array
' Input     : pArrAddr holds the address of the array to transmit
'           : pSize holds the amount of bytes to transmit from the array
' Output    : None
' Notes     : None

Proc OutPacket(ByRef pArrAddr As wFSR0, pSize As PRODL)
    Repeat                          ' Create a loop for the amount of elements to transmit
        HRSOut POSTINC0             ' Read from the array with auto address increment, and transmit it
        Dec pSize                   ' Decrement the pSize variable
    Until pSize = 0                 ' Repeat until all elements are transmitted
EndProc

top204

Or for a truly optimised procedure on an 18F device, there is the code below:

    Device = 18F25K20
    Declare Xtal = 16
'
' Setup USART1
'   
    Declare Hserial_Baud = 9600
    Declare HRSOut1_Pin = PORTC.6
   
    Dim MyByteArray[12] As Byte = "123456789012"     ' Create an array and load it with values that can be seen on a serial terminal
   
    Dim wFSR0 As FSR0L.Word         ' Combine FSR0L and FSR0H into a single 16-bit SFR
   
'--------------------------------------------------------------------
Main:
    OutPacket(MyByteArray, 10)      ' Transmit 10 elements from the array "MyByteArray"
    HRSOut 13
    OutPacket(MyByteArray, 11)      ' Transmit 11 elements from the array "MyByteArray"
    HRSOut 13
    OutPacket(MyByteArray, 12)      ' Transmit 12 elements from the array "MyByteArray"
    HRSOut 13
   
'--------------------------------------------------------------------
' Transmit X elements from a byte array
' Input     : pArrAddr holds the address of the array to transmit
'           : pSize holds the amount of bytes to transmit from the array
' Output    : None
' Notes     : None

Proc OutPacket(ByRef pArrAddr As wFSR0, pSize As PRODL)
Rept:
    HRSOut POSTINC0                 ' Read an element from the byte array with auto address increment, and transmit it
    Djnz pSize, Rept                ' Decrement pSize and repeat until all elements are transmitted
EndProc

It doesn't get any slimmer or faster. Look at the assembler code produced by the OutPacket procedure:

F1_000040 equ $ ; in [TEST_18F25K20.BAS] Proc OutPacket(ByRef pArrAddr As wFSR0, pSize As PRODL)
OutPacket
OutPacketRept
F1_000042 equ $ ; in [TEST_18F25K20.BAS] HRsout POSTINC0
    movf POSTINC0,W,0
    rcall __hrsout1__
F1_000043 equ $ ; in [TEST_18F25K20.BAS] Djnz pSize, Rept
    decfsz PRODL,F,0
    bra OutPacketRept
F1_000044 equ $ ; in [TEST_18F25K20.BAS] EndProc
    return 0 ; EndProc

Tight or what? The Djnz (Decrement and Jump if Not Zero) command is something I added many years ago, and mimics the mnemonics on the lovely old Z80 microprocessor. There are a few other loop mnemonic commands in the compiler that I wrote about on the old forum, but that is now long gone, and good riddens to it!!! I'll add them to the compiler's manual ASAP.

shantanu@india

I was expecting these detailed coding explanations from your side. Yes I understand that the iterative method is better for transmission of bytearrays. I'll remember.
Thanks Les.. as slways!!
Regards
Shantanu

shantanu@india

Found the issue in the assembler code for which the null byte is not transmitted. Les has already provided the requisite methodology of sending bytearrays.....so the following piece is just for academic interest.

__I2C_SingleBank_StrOut__
    movwf PP2
__I2C_SB_StrOut_loop__
    movf GEN4,W
    btfsc STATUS,2
    return
    movff PP2,FSR0L
    movff PP2H,FSR0H
    movf INDF0,W
    btfsc STATUS,2
    return
    rcall __byte_send__
    infsnz PP2,F
    incf PP2H,F
    decf GEN4,F
    bra __I2C_SB_StrOut_loop__

The btfsc STATUS,2 part causes the exit as soon as a null byte is encountered.
Regards
Shantanu

top204

Your thread has given me an idea for the next compiler update.

I will add a Declare to disable the null termination when sending an array using the Str modifier. The default will be to operate the same as it does now, but when the declare is issued in a program with a False, Off, or 0 prameter, it will send the whole array or the amount of elements given with the Str modifier, not look at the element's value at all. Something like:

Declare Str_NullTerminate = Off

shantanu@india

Regards
Shantanu