• AVR Freaks

Hot!PIC32MZ UART buffer issues w/ RS485

Page: 123 > Showing page 1 of 3
Author
thermant
Starting Member
  • Total Posts : 31
  • Reward points : 0
  • Joined: 2019/07/29 09:19:49
  • Location: 0
  • Status: offline
2019/11/20 18:57:56 (permalink)
0

PIC32MZ UART buffer issues w/ RS485

Hello everyone,
I made a post here a couple weeks ago where I was struggling to resolve an issue with a Modbus RS485 driver, and ultimately decided to just write my own after some feedback I had received.
I'm currently facing some bizarre to me issues that I can't quite seem to diagnose. I'm attempting to write an 11 byte message to "write multiple registers" at slave address 0x10 and the message is formatted as follows: [10 10 03 0C 00 01 02 00 54 54 33]. I haven't had any issues so far with formatting the modbus message and calculating the CRC, but the UART TX FIFO buffer is behaving sporadically and causing inconsistencies with interrupts.

        IEC5bits.U5TXIE = 0;  // Disable TX interrupt
        U5STAbits.UTXISEL = 0b00;  // Interrupt is generated and asserted while the transmit buffer contains at least one empty space
        UART5_RTS_Set(); UART5_CTS_Set();  // Setting CTS and RTS pins
        memcpy(writeBuffer, tempWrite, dataBytesToWrite); 
        writeBuffer[dataBytesToWrite] = crc_first;
        writeBuffer[dataBytesToWrite+1] = crc_second;
        U5TXREG = devAdd;
        U5TXREG = FUNCTIONCODE_WRITEMULTIPLE;
        U5TXREG = (uint8_t)((0xFF00 & stAdd) >> 8);
        U5TXREG = (uint8_t)(0x00FF & stAdd);
        U5TXREG = (uint8_t)((0xFF00 & numOfReg) >> 8);
        U5TXREG = (uint8_t)(0x00FF & numOfReg);
        U5TXREG = dataBytesToWrite;
        if(U5STAbits.UTXEN == 0)
            U5STAbits.UTXEN = 1;
        IEC5bits.U5TXIE = 1;

My thought process was to fill the FIFO buffer and then put the remaining bytes into a buffer, to then be put into the TX buffer when space is available. The TX interrupt is below

void __ISR(_UART5_TX_VECTOR) _UART5TXHandler(){
    IFS5bits.U5TXIF = 0; 
    if(bytesLeftToWrite == 0){
        while(U5STAbits.TRMT == 0); // transmit shift register is not empty, transmission is in progress, wait for it to end
        for(i = 0; i< 8; i++){
            throwawayByte[i] = U5RXREG;  // TX sometimes echoes back, just clear the RX buffer by reading it 8 times
        }
        UART5_RTS_Clear(); UART5_CTS_Clear();
        IEC5bits.U5RXIE = 1;
    }else{
        while(U5STAbits.UTXBF == 0 && bytesLeftToWrite != 0){
            // Not starting from 0,
            U5TXREG = writeBuffer[bytesToBeWritten-bytesLeftToWrite];
            bytesLeftToWrite--; 
        }
    }
    IFS5bits.U5TXIF = 0;
}

bytesLeftToWrite being my counter to see if the message has finished sending, in which case I clear RTS and enable the RX interrupt (its interrupt is 00, generates an interrupt when the buffer isnt empty). I've had some success with this, I've managed to both write a message in full and receive a message in full but that was with my read function which transmits exactly 8 bytes so I just fill the TX FIFO buffer and it does its thing, now im on the writing function which is at a minimum of 11 Bytes so the same tactic doesn't apply.
It has been very inconsistent, I've been snooping on the bus and seen that in some cases the entire message is written and everything is fine, in other cases I've seen that it writes 9 bytes out of 11 bytes and stops there. Those seem to be the source of the problem, I looked at the U5STA register and saw it report that the buffer wasn't full but wasn't triggering the interrupt so it would never enter the ISR to send the last two bytes. Occasionally I'll get framing errors as well, but also sometimes with the same exact code everything will be sent as its supposed to be.
 
So my question more so is that is using the FIFO buffer in the UART not advised? I'm considering writing a single byte at a time and setting the TX ISR to be asserted when the FIFO buffer is empty, placing one byte in and exiting, and repeating until the message has been sent in full. I was mostly concerned with framing issues or a potential pause between bytes being sent and the whole message being mishandled. Is there a suggested manner in which to send a message? UART should be rather simple and this thing has given me such a headache.
#1

42 Replies Related Threads

    ric
    Super Member
    • Total Posts : 24605
    • Reward points : 0
    • Joined: 2003/11/07 12:41:26
    • Location: Australia, Melbourne
    • Status: offline
    Re: PIC32MZ UART buffer issues w/ RS485 2019/11/20 19:09:48 (permalink)
    0
    What is the relationship between "dataBytesToWrite" and "bytesLeftToWrite"
    Blocking inside the TX interrupt for TRMT to set is a bit ugly.
    It is rarely a good idea to block inside an interrupt service, unless you are positive you don't need to be doing anything else in the meantime.
     
     
     

    I also post at: PicForum
    Links to useful PIC information: http://picforum.ric323.co...opic.php?f=59&t=15
    NEW USERS: Posting images, links and code - workaround for restrictions.
    To get a useful answer, always state which PIC you are using!
    #2
    thermant
    Starting Member
    • Total Posts : 31
    • Reward points : 0
    • Joined: 2019/07/29 09:19:49
    • Location: 0
    • Status: offline
    Re: PIC32MZ UART buffer issues w/ RS485 2019/11/21 12:39:30 (permalink)
    0
    ric
    What is the relationship between "dataBytesToWrite" and "bytesLeftToWrite"

    Good question, I was having a difficult time coming up with good variable names and that's what I came up with. 'dataBytesToWrite' is (2 x the number of registers to write to), in this case I'm writing to one register so it's 2 bytes. Since for that message the first 7B are always in the same order I just filled it in there, and then put the data and CRC into the write buffer to be sent off in the ISR.
    'bytesLeftToWrite' is my counter for how many remaining bytes are in my write buffer, 'bytesToBeWritten' is a fixed byte count for comparing the previous counter against so I can navigate through the buffer. I guess I alternatively could've just used a static counter inside the interrupt to make things a bit simpler there.

    Blocking inside the TX interrupt for TRMT to set is a bit ugly.
    It is rarely a good idea to block inside an interrupt service, unless you are positive you don't need to be doing anything else in the meantime.

    Yeah I hear you, I was mostly just trying to make sure the last byte had been sent out before I cleared RTS and enabled the RX interrupt, otherwise do you think it'd finish transmitting in that time?
     
     




    #3
    ric
    Super Member
    • Total Posts : 24605
    • Reward points : 0
    • Joined: 2003/11/07 12:41:26
    • Location: Australia, Melbourne
    • Status: offline
    Re: PIC32MZ UART buffer issues w/ RS485 2019/11/21 12:42:39 (permalink)
    0
    The point of my question is you did not show any code revealing how the dataBytesToWrite value gets into bytesLeftToWrite.
    A miscalculation there could be fatal.

    I also post at: PicForum
    Links to useful PIC information: http://picforum.ric323.co...opic.php?f=59&t=15
    NEW USERS: Posting images, links and code - workaround for restrictions.
    To get a useful answer, always state which PIC you are using!
    #4
    thermant
    Starting Member
    • Total Posts : 31
    • Reward points : 0
    • Joined: 2019/07/29 09:19:49
    • Location: 0
    • Status: offline
    Re: PIC32MZ UART buffer issues w/ RS485 2019/11/21 13:12:28 (permalink)
    0
    Also, i was previously trying to disable TX interrupts inside the TX ISR and it seemed to break the interrupts altogether and cause it to be stuck. Is there somewhere you should be disabling the interrupt, it's necessary to do so otherwise the interrupt will keep triggering
     
    #5
    thermant
    Starting Member
    • Total Posts : 31
    • Reward points : 0
    • Joined: 2019/07/29 09:19:49
    • Location: 0
    • Status: offline
    Re: PIC32MZ UART buffer issues w/ RS485 2019/11/21 13:21:47 (permalink)
    0
    ric
    The point of my question is you did not show any code revealing how the dataBytesToWrite value gets into bytesLeftToWrite.
    A miscalculation there could be fatal.



    I'll post it below, they don't directly interact with each other, and I haven't noticed an issue they could have caused.

    bytesLeftToWrite = 2 + 2*numOfReg;
    bytesToBeWritten = 2 + 2*numOfReg;
    dataBytesToWrite = 2 * numOfReg;

    numOfReg being passed in from the function call
    #6
    maxruben
    Super Member
    • Total Posts : 3371
    • Reward points : 0
    • Joined: 2011/02/22 03:35:11
    • Location: Sweden
    • Status: offline
    Re: PIC32MZ UART buffer issues w/ RS485 2019/11/22 09:18:19 (permalink)
    0
    Make sure to use the CLR, SET or INV offsets and a mask instead of regisgter.bit=x for registers with several shared bits that can be updated by the hardware, like the registers with interrupt flags.
     
    /Ruben
    #7
    thermant
    Starting Member
    • Total Posts : 31
    • Reward points : 0
    • Joined: 2019/07/29 09:19:49
    • Location: 0
    • Status: offline
    Re: PIC32MZ UART buffer issues w/ RS485 2019/11/22 10:36:12 (permalink)
    0
    Good to know. Is there a reason why that's the case?
    #8
    NKurzman
    A Guy on the Net
    • Total Posts : 18041
    • Reward points : 0
    • Joined: 2008/01/16 19:33:48
    • Location: 0
    • Status: online
    Re: PIC32MZ UART buffer issues w/ RS485 2019/11/22 11:11:15 (permalink)
    0
    thermant
    Good to know. Is there a reason why that's the case?


    Read
    Modify
    <Interrupt >
       Read
       Modify
       Write.
    <Interrupt >
    Write.
     
    if an Interrupt occurs during the Process it can be overwritten by the main loop code.
    CLR, SET or INV are Atomic Operations. They happen in a single ASM OP-Code so can not be interrupted.
     
    #9
    thermant
    Starting Member
    • Total Posts : 31
    • Reward points : 0
    • Joined: 2019/07/29 09:19:49
    • Location: 0
    • Status: offline
    Re: PIC32MZ UART buffer issues w/ RS485 2019/11/22 11:38:47 (permalink)
    0
    That's fair, I suppose I had assumed that the REGbits.x commands were also atomic. I'll roll in those changes, thanks for the advice. That could be the root cause of some of my issues to begin with
    #10
    NKurzman
    A Guy on the Net
    • Total Posts : 18041
    • Reward points : 0
    • Joined: 2008/01/16 19:33:48
    • Location: 0
    • Status: online
    Re: PIC32MZ UART buffer issues w/ RS485 2019/11/22 11:47:16 (permalink)
    0
    They could (and Should Be)  But the only way to be sure is to look at the ASM.
    Last I looked awhile back XC32 did not use CLR, SET or INV.  At least in free Mode anyway.
    #11
    sborden
    Super Member
    • Total Posts : 1953
    • Reward points : 0
    • Joined: 2010/08/05 02:12:53
    • Location: 0
    • Status: offline
    Re: PIC32MZ UART buffer issues w/ RS485 2019/11/22 11:48:00 (permalink)
    0
    Here is a complex protocol transmit routine I developed for GUI data to be transmitted to a remote machine. It is written using the PIC32 PLIBs and C32.
     

     
    //
    //******************************************************************************

    uint32_t TransmitGUIPacket() {
        if (SYS_INT_SourceIsEnabled(INT_SOURCE_USART_1_TRANSMIT)) {
            return 0;
        } else if (MyPacketSize) {
            //
            // Insert the status into every packet.
            *MyPacketHead++ = GUIErrorStatus.v[0];
            *MyPacketHead++ = GUIErrorStatus.v[1];
            *MyPacketHead++ = GUIErrorStatus.v[2];
            *MyPacketHead++ = GUIErrorStatus.v[3];
            MyPacketSize += 4;
            //
            // Prepare packet
            GUI_TXbuffer[5] = MyPacketSize & 0xFF;
            GUI_TXbuffer[6] = (MyPacketSize >> 8) & 0xFF;
            //
            // Insert Packet ID
            *MyPacketHead++ = GUIOutPackID.v[0];
            *MyPacketHead++ = GUIOutPackID.v[1];
            *MyPacketHead++ = GUIOutPackID.v[2];
            *MyPacketHead++ = GUIOutPackID.v[3];
            //
            // Insert End-of-Packet
            *MyPacketHead = EOT;
            //
            // Send to GUI
            TX_Count = MyPacketSize + 12;
            TX_Head = GUI_TXbuffer;
            SYS_INT_SourceEnable(INT_SOURCE_USART_1_TRANSMIT);
            MyPacketSize = 0;
            return 0;
        }
        return 1;
    }

    void __ISR(_UART1_TX_VECTOR, ipl7AUTO) _IntHandlerDrvUsartTransmitInstance0(void) {
        /* This is the USART Driver Transmit tasks routine.
           In this function, the driver checks if a transmit
           interrupt is active and performs respective action*/
        /* Reading the transmit interrupt flag */
        if (SYS_INT_SourceStatusGet(INT_SOURCE_USART_1_TRANSMIT)) {
            /* Disable the interrupt, to avoid calling ISR continuously*/
            SYS_INT_SourceDisable(INT_SOURCE_USART_1_TRANSMIT);
            if (TX_Count) {
                do {
                    if (!DRV_USART0_TransmitBufferIsFull()) {
                        DRV_USART0_WriteByte(*TX_Head);
                        ++TX_Head;
                        --TX_Count;
                    } else
                        break;
                } while (TX_Count);
                if (TX_Count)
                    SYS_INT_SourceEnable(INT_SOURCE_USART_1_TRANSMIT);
                else
                    //
                    // Release TX resource
                    GUI_Busy = false;
            }
            /* Clear up the interrupt flag */
            SYS_INT_SourceStatusClear(INT_SOURCE_USART_1_TRANSMIT);
        }
    }

     
    Gist: 0. Set up port.
            1. Prepare ALL your data into a buffer.
            2. Set a count for the number of chars in the buffer.
            3. Enable the TX interrupt to start transmission.
            4. TX interrupt disables itself when done. Checking to see if the interrupt is enabled tells you if the transmitter is busy.
            5. You can use a flag (i.e., GUI_Busy) to determine if the transmission is done, as well. The TX routine I included is the raw transmit routine. The routine that calls it prepares the data packet and sets the busy flag.
     
     
    post edited by sborden - 2019/11/22 11:51:23
    #12
    andersm
    Super Member
    • Total Posts : 2675
    • Reward points : 0
    • Joined: 2012/10/07 14:57:44
    • Location: 0
    • Status: offline
    Re: PIC32MZ UART buffer issues w/ RS485 2019/11/22 13:18:37 (permalink)
    0
    NKurzmanThey could (and Should Be)  But the only way to be sure is to look at the ASM.

    They can't be. You can't set a three-bit field to 0b101 atomically using the SET/CLR/INV registers. Using bitfields to alias hardware registers belongs squarely in the territory of "Don't Do That."
    #13
    NKurzman
    A Guy on the Net
    • Total Posts : 18041
    • Reward points : 0
    • Joined: 2008/01/16 19:33:48
    • Location: 0
    • Status: online
    Re: PIC32MZ UART buffer issues w/ RS485 2019/11/22 13:21:14 (permalink)
    0
    andersm
    NKurzmanThey could (and Should Be)  But the only way to be sure is to look at the ASM.

    They can't be. You can't set a three-bit field to 0b101 atomically using the SET/CLR/INV registers. Using bitfields to alias hardware registers belongs squarely in the territory of "Don't Do That."



    No, but you can set single bits in a bit-fields that way.  XC8 does.
    #14
    thermant
    Starting Member
    • Total Posts : 31
    • Reward points : 0
    • Joined: 2019/07/29 09:19:49
    • Location: 0
    • Status: offline
    Re: PIC32MZ UART buffer issues w/ RS485 2019/11/22 13:35:39 (permalink)
    0
    Thanks for the reference sborden. So you disable the TX interrupt upon entering the ISR, re-enable it later on if there's more to be sent, and then clear the interrupt flag before exiting. Am I following that correctly?
    So if I wanted to set the TX interrupt to 0b10, I'd have to clear bit 14 and set bit 15. Shame it doesn't just do that automatically/atomically, guess I could throw them into a macro to make it easier on the eyes
    #15
    andersm
    Super Member
    • Total Posts : 2675
    • Reward points : 0
    • Joined: 2012/10/07 14:57:44
    • Location: 0
    • Status: offline
    Re: PIC32MZ UART buffer issues w/ RS485 2019/11/22 14:32:49 (permalink)
    0
    NKurzman
    andersm
    NKurzmanThey could (and Should Be)  But the only way to be sure is to look at the ASM.

    They can't be. You can't set a three-bit field to 0b101 atomically using the SET/CLR/INV registers. Using bitfields to alias hardware registers belongs squarely in the territory of "Don't Do That."

    No, but you can set single bits in a bit-fields that way.  XC8 does.

    Having completely different semantics depending on the width of the field is the worst possible option. Also depending on whether the new value is a constant expression or not.
    #16
    NKurzman
    A Guy on the Net
    • Total Posts : 18041
    • Reward points : 0
    • Joined: 2008/01/16 19:33:48
    • Location: 0
    • Status: online
    Re: PIC32MZ UART buffer issues w/ RS485 2019/11/22 15:34:30 (permalink)
    0
    andersm  That is the Job of the Optimizer. To generate the Best and smallest code for a C Statement.
    Why would you want the Compiler to is 3 Opcode (or more) when one will achieve the result?  The compiler should make the best use on the opcodes and registers the CPU has.
    And the Compiler always treats constant expressions different then variables.  And may even convert a variable to a constant expression it is will be more optimal.
     
    My 2 cents
     
     
    #17
    maxruben
    Super Member
    • Total Posts : 3371
    • Reward points : 0
    • Joined: 2011/02/22 03:35:11
    • Location: Sweden
    • Status: offline
    Re: PIC32MZ UART buffer issues w/ RS485 2019/11/22 16:00:17 (permalink)
    0
    You don't need to disable TX interrupt inside the ISR, it won't interrupt itself. Interrupt priority will take care of that.
     
    If this is for 3 wire RS485 you also need to set the TX enable bit for the physical transmitter circuit and this can't be done using the TX interrupt alone since this ISR gets called when there is more room in the TX buffer, not necessarily when the last bit of the last byte has been sent out from the shift register. Some UARTs also can tell you when this is done and the line can be turned around for receiving.
     
    I have sometimes used a timer for that. Modbus also needs a short period of silence between messages.
     
    /Ruben
    #18
    NKurzman
    A Guy on the Net
    • Total Posts : 18041
    • Reward points : 0
    • Joined: 2008/01/16 19:33:48
    • Location: 0
    • Status: online
    Re: PIC32MZ UART buffer issues w/ RS485 2019/11/22 16:08:58 (permalink)
    0
    I have not Done the MZ but in the MX I changed the Transmitter Interrupt mode From FIFO to Transmitter empty.  This means you do not have to sit in the TX Interrupt until the last byte shifts out.  It could be checked in the main loop instead, assuming you can make the timing requirements.
    #19
    thermant
    Starting Member
    • Total Posts : 31
    • Reward points : 0
    • Joined: 2019/07/29 09:19:49
    • Location: 0
    • Status: offline
    Re: PIC32MZ UART buffer issues w/ RS485 2019/11/22 16:28:18 (permalink)
    0
    I will however need to disable the TX interrupt inside the ISR once the message has finished sending, otherwise once I've sent the last byte I'll just be spinning in the interrupt.

    If this is for 3 wire RS485 you also need to set the TX enable bit for the physical transmitter circuit and this can't be done using the TX interrupt alone since this ISR gets called when there is more room in the TX buffer, not necessarily when the last bit of the last byte has been sent out from the shift register. Some UARTs also can tell you when this is done and the line can be turned around for receiving.

    This is half-duplex, just a single line. I'd be having a much better time otherwise. That's why I was checking for the shift register to be finished, wanted to rule out the possibility of cutting the last byte off but also shouldn't be blocking in an interrupt.
    I'm on the master side as well, so its a bit comfier
    #20
    Page: 123 > Showing page 1 of 3
    Jump to:
    © 2019 APG vNext Commercial Version 4.5