• AVR Freaks

Helpful ReplyHot!PIC32MX460 Uart Tx Interrupt stops

Author
ryanafleming
New Member
  • Total Posts : 15
  • Reward points : 0
  • Joined: 2017/12/17 23:00:51
  • Location: 0
  • Status: offline
2019/12/05 23:33:12 (permalink)
0

PIC32MX460 Uart Tx Interrupt stops

I have ran into an issue with pic32 interrupt. I can either get it to work but constantly enter the interrupt routine despite no data being preset, or I can get the interrupt to stop altogether. the example below has disabled rx and error interrupts, so only the tx interrupt should trigger the ISR.
 
I have set up my interrupt with the following settings:
void Debug_Init(void) {
 //Uart1
 //U1Tx/RF8
 //U1Rx/RF2
 PLIB_USART_Disable(USART_ID_1);
 //baud = 115200
 //data bits = 8
 //parity = none
 //stop bits = 0
 //rts/dtr/cts don't bother using

 //set pin direction as either output or input
 TRISFSET = (1 << 2); //set rx for digital input
 TRISFCLR = (1 << 8); //clear tx for output

 ////////////////////////////////
 // Set up Registers
 //Clear all uart registers for safety reasons before beginning comms
 U1MODE = 0;
 U1STA = 0;
 U1BRG = 0;

 //set registers
 U1MODEbits.UEN = 0; //only rx and tx are controlled by hardware, rts/cts are done manually.
 U1MODEbits.PDSEL = 0; //8 bits no parity
 U1MODEbits.STSEL = 0; //0 stop bits
 U1MODEbits.BRGH = 0;

 //U1STAbits.UTXISEL = 0; //interrupt generated when a space is available in tx register buffer
 U1STAbits.UTXISEL = 0b10; //interrupt generated when a tx buffer is empty
 U1STAbits.URXEN = 1;
 U1STAbits.UTXEN = 1;
 U1STAbits.URXISEL = 0b00; //rx triggered when buffer has at least 1 character in it

 //something with with bplib baud set and enable!
 uint32_t clockSource = SYS_CLK_PeripheralFrequencyGet(CLK_BUS_PERIPHERAL_1);
 U1BRGSET = (clockSource / 16 / 115200) - 1;

 ///////////////////////////////////////////
 // Set up interrupts
 //Clear interrupt status flags initially in case any have already been set
 SYS_INT_SourceStatusClear(INT_SOURCE_USART_1_TRANSMIT);
 SYS_INT_SourceStatusClear(INT_SOURCE_USART_1_ERROR);
 SYS_INT_SourceStatusClear(INT_SOURCE_USART_1_RECEIVE);

 // Enable the interrupt source so that an interrupt will trigger for these events
// SYS_INT_SourceEnable(INT_SOURCE_USART_1_ERROR);
// SYS_INT_SourceEnable(INT_SOURCE_USART_1_RECEIVE);

 //set interrupt priority
 SYS_INT_VectorPrioritySet(INT_VECTOR_UART1, INT_PRIORITY_LEVEL1);
 SYS_INT_VectorSubprioritySet(INT_VECTOR_UART1, INT_SUBPRIORITY_LEVEL0);

 //////////////////////////////////////////////
 //do me last...actually enable module
 U1MODEbits.ON = 1;
}

 
As soon as U1MODEbits.ON = 1; gets set, the U1TxIF flag appears to be set. This doesn't matter immediately as I have my interrupt disabled.
 
I then start sending some data:

void Debug_SendByte(uint8_t b) {
 Debug.Tx.buf[Debug.Tx.head++] = b;

 if (Debug.Tx.head >= sizeof (Debug.Tx.buf)) {
  Debug.Tx.head = 0;
 }

 //set tx interrupt flag if it wasn't already enabled
 if (!SYS_INT_SourceIsEnabled(INT_SOURCE_USART_1_TRANSMIT)) {
  SYS_INT_SourceEnable(INT_SOURCE_USART_1_TRANSMIT);
 }
}

 
Now that I have enabled my interrupt and added some data to my ring buffer, the interrupt triggers.
 
void __ISR(_UART_1_VECTOR, ipl1AUTO) _IntHandlerDrvUsartInstance0(void) {
if (SYS_INT_SourceStatusGet(INT_SOURCE_USART_1_TRANSMIT)) {
SYS_INT_SourceStatusClear(INT_SOURCE_USART_1_TRANSMIT);
//_DRV_USART_BufferQueueTxTasks(0);
while ((Debug.Tx.head != Debug.Tx.tail) && !U1STAbits.UTXBF) {
//Uart.Tx.inOperation = true;
U1TXREG = Debug.Tx.buf[Debug.Tx.tail];
Debug.Tx.tail++;
if (Debug.Tx.tail >= ARRAY_SIZE(Debug.Tx.buf)) {
Debug.Tx.tail = 0;
}
}
if (Debug.Tx.head == Debug.Tx.tail) {
SYS_INT_SourceDisable(INT_SOURCE_USART_1_TRANSMIT);
}
}

// do rx and error checking stuff
...
}

 
In the ISR example, the status flag gets cleared as suggested. All the data gets sent if I place the status flag at the start of the interrupt, it won't clear if there is still data to send, but thats fine. Once all the data has been cleared, the interrupt is disabled and the status flag is cleared. And yet the interrupt still triggers, it just doesn't do anything... Attached is the debug of my status bits inside the ISR as soon as it enters it.
 
I really hope its something simple I have done...

Attached Image(s)

#1
sborden
Super Member
  • Total Posts : 1960
  • Reward points : 0
  • Joined: 2010/08/05 02:12:53
  • Location: 0
  • Status: offline
Re: PIC32MX460 Uart Tx Interrupt stops 2019/12/06 05:19:16 (permalink)
0
Disable the interrupt as soon as you enter. This prevents the flag from being set again because of transmitting while inside the ISR. When you are done transmitting (because TX is full), if you still have data in the ring buffer, then re-enable the IRQ. The LAST thing you do is clear the status flag.
 
Also, your TX code is not safe. You should be disabling interrupts while filling the buffer. Use of arrays instead of pointers adds extra places where your code could get interrupted and corrupt your indexes or data.
 
(Production working code for MX795, packet-based protocol for LARGE data packets.)

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

uint32_t TransmitGUIPacket() {
    if (SYS_INT_SourceIsEnabled(INT_SOURCE_USART_1_TRANSMIT)) {
       //
       // Resource busy, come back later.
        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);
    }
}

 
#2
Antipodean
Super Member
  • Total Posts : 1874
  • Reward points : 0
  • Joined: 2008/12/09 10:19:08
  • Location: Didcot, United Kingdom
  • Status: offline
Re: PIC32MX460 Uart Tx Interrupt stops 2019/12/06 06:18:23 (permalink)
0
sborden
Also, your TX code is not safe. You should be disabling interrupts while filling the buffer. Use of arrays instead of pointers adds extra places where your code could get interrupted and corrupt your indexes or data.
 

 
There is interrupt safe code on the web if you go looking. using it means you don't need to disable interruptsd while dealing with the buffer.
 

Do not use my alias in your message body when replying, your message will disappear ...

Alan
#3
sborden
Super Member
  • Total Posts : 1960
  • Reward points : 0
  • Joined: 2010/08/05 02:12:53
  • Location: 0
  • Status: offline
Re: PIC32MX460 Uart Tx Interrupt stops 2019/12/06 06:36:48 (permalink)
0
Well, let's face it: I just gave general advice. He should also be checking to make sure the circular buffer is not full before inserting data into it. That will definitely cause major problems. I so much prefer adding the "count" instead of relying on buffer pointer comparisons. That way you know how many characters you can send.
 
Come to think of it, that might be what is happening here, as well: Buffer overrun.
 
...And, IIRC in PIC32MXs the ISR for all UART events go to the same vector. That's why you need to check the source when you enter the ISR.
post edited by sborden - 2019/12/06 06:56:19
#4
ryanafleming
New Member
  • Total Posts : 15
  • Reward points : 0
  • Joined: 2017/12/17 23:00:51
  • Location: 0
  • Status: offline
Re: PIC32MX460 Uart Tx Interrupt stops 2019/12/06 06:59:13 (permalink)
0
Within my example, and the way I have written it, specifically the head integer is only written to outside of the interrupt, while the tail is only written to within the interrupt. A buffer overrun would only occur if i was to access anything outside of these variables ranges. That is:
 
typedef struct {
 struct {
  uint16_t tail;
  uint16_t head;
  uint8_t buffer[256];
 } Tx;
} Debug_t;

Debug_t Debug;

 
Knowing this and that at compile time the tail within the ISR will be compared against 256, it will always return back to 0. In this situation I have done several tests of the code in general on many other microcontrollers and I have not yet suffered from a buffer overflow.
 
I digress. The more interesting point I seem to be suffering from is why my interrupt is triggering when it is not enabled at all. Based on the interrupt bits set, I have set it up to only trigger when the 8 byte deep FIFO register is empty. As it stands everything in the code fundementally works. It can send, recieve do general processing etc. The interrupt just doesn't want to be cleared despite with the debugger showing that everything is actually disabled. I believe I must be missing a bit somewhere that is all :/ Maybe the global ISR controller has a status flag needs to be cleared also?
 
EDIT: reading back through I also agree that disabling the interrupt ASAP is the best course of action, but practically if I wanted to follow my coding style, the interrupts will either keep triggering or stop triggering altogether sadly.
post edited by ryanafleming - 2019/12/06 07:01:11
#5
sborden
Super Member
  • Total Posts : 1960
  • Reward points : 0
  • Joined: 2010/08/05 02:12:53
  • Location: 0
  • Status: offline
Re: PIC32MX460 Uart Tx Interrupt stops 2019/12/06 07:23:21 (permalink)
0
1. Remember the ISR will fire on ALL Uart events. They share the same vector (though, they seem disabled).
2. In your case, buffer overrun causes wrap-around/corruption/loss of data.
3. PIC32s have instruction pipelining. There is no real guarantee where/when the debugger will actually stop. Especially if you are using any optimization options. I have also seen it stop where there are no breakpoints. It would be great if you could toggle a LED and try out of debug mode (or, at least without breakpoints in the ISR).
#6
sborden
Super Member
  • Total Posts : 1960
  • Reward points : 0
  • Joined: 2010/08/05 02:12:53
  • Location: 0
  • Status: offline
Re: PIC32MX460 Uart Tx Interrupt stops 2019/12/06 07:41:12 (permalink) ☄ Helpfulby ryanafleming 2019/12/08 16:25:08
0
Your unsafe condition occurs:

Starting conditions:
1. Debug.Tx.head == 255.
2. Currently transmitting. Only 1 byte left to transmit! i.e., Debug.Tx.tail==254.

EVENT:

void Debug_SendByte(uint8_t b) {
 Debug.Tx.buf[Debug.Tx.head++] = b;

<<<<<<<<<<<<< INTERRUPT! >>>>>>>>>>>>
>>>>> Debug.Tx.head is now 256! Oh, no!

 if (Debug.Tx.head >= sizeof (Debug.Tx.buf)) {
  Debug.Tx.head = 0;
 }


... Meanwhile, back in ISR:

void __ISR(_UART_1_VECTOR, ipl1AUTO) _IntHandlerDrvUsartInstance0(void) {

 if (SYS_INT_SourceStatusGet(INT_SOURCE_USART_1_TRANSMIT)) {

     SYS_INT_SourceStatusClear(INT_SOURCE_USART_1_TRANSMIT);
     //_DRV_USART_BufferQueueTxTasks(0);

                      (256)
     while ((Debug.Tx.head != Debug.Tx.tail) && !U1STAbits.UTXBF) {
        //Uart.Tx.inOperation = true;
         U1TXREG = Debug.Tx.buf[Debug.Tx.tail];
         Debug.Tx.tail++;

         >>>>>>>>>>>>>>>> Debug.Tx.tail == 254->255

         if (Debug.Tx.tail >= ARRAY_SIZE(Debug.Tx.buf)) {
            Debug.Tx.tail = 0;
         }

         >>>>>>>>>>>>>>>> cool! Next char, since UTXBF is good to go.
         >>> Problem: Debug.Tx.head==256, still.

         >>> This time through, however, Debug.Tx.tail == 255->256, then IF statement makes it go 0!
         >>> TXBF is STILL good to go.
         >>> Debug.Tx.head (256) <> Debug.Tx.tail
         >>> Infinite loop ensues.
     }
     if (Debug.Tx.head == Debug.Tx.tail) {
        SYS_INT_SourceDisable(INT_SOURCE_USART_1_TRANSMIT);
     }
   }
 
 // do rx and error checking stuff
 ...
}

 
Well, not completely infinite. It will eventually settle out, but after a lot of grief.
post edited by sborden - 2019/12/06 07:46:40
#7
ryanafleming
New Member
  • Total Posts : 15
  • Reward points : 0
  • Joined: 2017/12/17 23:00:51
  • Location: 0
  • Status: offline
Re: PIC32MX460 Uart Tx Interrupt stops 2019/12/08 16:29:08 (permalink)
0
Great point sborden, something I hadn't considered in the past. I'll make some changes to ammend this.
 
EDIT: Found the issue...classic coding error that you would never had got without more information. My ISR is named _IntHandlerDrvUsartInstance0, this also the name of a UART2 driver that is in my code. While the other one is disabled, the function itself it not. I guess I am facinated as to why linker didn't pick up the error. Regardless, the other interrupt was triggering and jumping into this function. CHanging the name to _IntHandlerDrvUsartInstance1 instead of '0' solved the issue. my final issue is that the way this interrupt works was that as soon as you enable the Uart module, the interrupt status flag bit is set. If it were to be cleared at any point and the Tx interrupt disabled, it would never trigger again. As such I can only disable the interrupt OR clear the status flag.
I also tried to address the bug you raised by checking to ensure that the data is only written WHEN my head < the size of the buffer. This would at least skip one interrupt cycle and give the main code a chance to set the variable to its correct value. The alternative was to obviously set the datatype for my head and tail to a uint8 instead of uint16, though I wanted to write it a bit more dynamically.
Below is the code I now use in my interrupt for my Tx instead.
 
void __ISR(_UART_1_VECTOR, ipl1AUTO) _IntHandlerDrvUsartInstance1(void) {
 if (SYS_INT_SourceStatusGet(INT_SOURCE_USART_1_TRANSMIT)) {
  while ((Debug.Tx.head != Debug.Tx.tail) && (Debug.Tx.head < ARRAY_SIZE(Debug.Tx.buf)) && !U1STAbits.UTXBF) {
   U1TXREG = Debug.Tx.buf[Debug.Tx.tail];
   Debug.Tx.tail++;

   if (Debug.Tx.tail >= ARRAY_SIZE(Debug.Tx.buf)) {
    Debug.Tx.tail = 0;
   }
  }

  if (Debug.Tx.head == Debug.Tx.tail) {
   SYS_INT_SourceDisable(INT_SOURCE_USART_1_TRANSMIT);
  }
  else {
   SYS_INT_SourceStatusClear(INT_SOURCE_USART_1_TRANSMIT);
  }
 }
}

post edited by ryanafleming - 2019/12/08 17:06:16
#8
sborden
Super Member
  • Total Posts : 1960
  • Reward points : 0
  • Joined: 2010/08/05 02:12:53
  • Location: 0
  • Status: offline
Re: PIC32MX460 Uart Tx Interrupt stops 2019/12/09 05:04:45 (permalink)
0
🤦🏻‍♂️
Hate it when that happens...
#9
Jump to:
© 2020 APG vNext Commercial Version 4.5