PIC32 UART buffer overrun in MIDI receiver application

Find out how to setup your programmer's software and how to solve many common problems.

Moderator: phalanx

Post Reply
wave.jaco
Posts: 28
Joined: Sat Aug 01, 2015 2:45 am
Location: South Africa

PIC32 UART buffer overrun in MIDI receiver application

Post by wave.jaco » Wed Oct 19, 2016 1:19 am

I have a system that consists of both a MIDI transmitter and MIDI receiver, that are separate units and connected between each other with cables. A PIC32MX6xx-series microcontroller forms the heart of each of these two units/sub-systems. The transmitter generates MIDI messages of notes played on an old mechanical musical keyboard that has been MIDI-fied. Each note-on/off event consists of 3 bytes of data, as per the official MIDI standard:

[*] MIDI channel
[*] Note number
[*] Velocity

This data is sent over the PIC32's UART at the standard MIDI baud rate of 31250 bps. Furthermore, the UART conifugration is the commonly used 8 data bits, no parity, 1 stop bit. Of course, the receiving PIC32's UART is also configured exactly the same. Upon receiving the MIDI messages, the receiver performs some operations based on the received MIDI message and some other user inputs/configurations. The whole process works mostly fine even with successive notes that are played very fast.

However, the problem comes in when a large enough chord is played and the notes from the keyboard are pressed down exactly at the same time, typically with a chord size of 5 notes and larger. What happens is that most of the MIDI messages received are processed correctly, but the UART peripheral freezes at what seems to be the last received (perhaps partial) MIDI message. Thereafter, no more MIDI messages are received. An important aspect to mention is that the transmitter still transmits all the MIDI messages as it should - no problem there at all.

Now, what I do know and found out after a lot of searching on this type of issue, is that a UART buffer overrun error occurs. I know this because the UxSTAbits.OERR bit is set when this freezing phenomenon occurs. When I clear this bit, the UART operation continues as per normal. My conclusion is thus that the MIDI messages are transmitted and received faster than they are actually read and processed by the receiver, causing a buffer overrun due to the data in the FIFO buffer not being read fast enough. Consequently, this causes a loss of data, since the UART peripheral freezes while there are still MIDI messages being sent by the transmitter.

What I would like to ask: How can bypass this issue? I know the UART receive buffer is 8 levels deep. From my initial understanding, this would mean one character received for each level. However, from the datasheet it seems that a total of 13 characters can be received before the OERR bit is asserted by the PIC. Therefore, my understanding of these levels are probably wrong (refer to attached image).

This would make some sense with the larger than 4-note chord issue, since 4 chords consist of 4x 3-byte messages = 12 bytes that are received (and processed fine), and a 5-note chord consist of 5x 3-byte messages = 15 bytes that are received, where the problem starts to occur.

This is how I handled the UART reception until now:

Code: Select all

void main(void)
    { 
    char dataRx[1] = {0x00};
    
    /* --------------------------------------- */
    /* Initialization code and other code here */
    /* --------------------------------------- */
    
    /* UART reception */
    U1STAbits.OERR = 0; /* This happens only once */
    while (1)
    {        
        /* UART receive and constructing event string */        
        while (U1STAbits.URXDA == 0);
        getsUART1(1, dataRx, 0);

        /* Process the received data */
        processIncomingByte(dataRx);
    } /* while */
    } /* main() */
The function processIncomingByte() determines which byte(s) of the complete MIDI message have been received thus far, and performs some operations after a complete MIDI message is received. It determines that one byte at a time, as the bytes are received over the UART.

One option that comes to mind is to somehow clear the FIFO buffer immediately after each MIDI message is received (and copied into a software-defined buffer for processing), so that a buffer overrun should (in theory) never occur. I have tried this, but it didn't solve the problem and the buffer still overruns as described. I have tried it as follows:

Code: Select all

void main(void)
{
int rxCount = 0;
char dataBuf[3] = {0, 0, 0};

/* --------------------------------------- */
/* Initialization code and other code here */
/* --------------------------------------- */

/* UART reception */
U1STAbits.OERR = 0; /* This happens only once */
while (1)
{   
    while (U1STAbits.URXDA == 0);
    rxCount++; /* Increment counter as soon as byte is received on UART */

    /* After 3 bytes are received, i.e. a complete MIDI message */
    if (rxCount == 3)
    {
        rxCount = 0; /* Reset counter */
        getsUART1(3, dataBuf, 0);
        U1STAbits.OERR = 0; /* clear the FIFO buffer */
        processIncomingMessage(dataBuf); /* Process the received data */
    } /* if */
} /* while */
} /* main() */
The function processIncomingMessage() in this case immediately performs the intended operations, since a complete MIDI message is passed to it in this case.

I suspect that the processing time for all the MIDI messages received are just too long to read new incoming MIDI messages when large chords are played (with notes depressed at exactly the same time), since everything works fine, even with rapid successive single notes. Therefore, another option that comes to mind is to simply increase the PIC's clock frequency. Currently it is set at 8 MHz. Could it possibly solve this issue if I just increase the PIC's clock frequency? Processing of the messages would happen much faster, while keeping the baud rate the same.

Although I am not bound by the standard MIDI baud rate of 31250 bps, I want to avoid changing the baud rate as far as possible. In any case, this will probably not solve the problem anyway. Increasing the time between transmitting MIDI messages might work, but that could introduce a tell-tale delay as more and more MIDI messages have to be transmitted in a specified time period.

Another possible solution would be to use a software-defined FIFO buffer as described in https://eewiki.net/display/microcontrol ... munication. However, I cannot figure out how to properly clear the hardware FIFO buffer after each byte is received on the UART in order to utilize this software-defined FIFO buffer. If I figured that out, I could just as well clear the hardware FIFO buffer after each MIDI message (3 bytes) is received, as described earlier in the second code snippet.

I know this is quite a mouthful, but I tried to describe my problem as clear and complete as I possibly can.

Any help in this regard would be greatly appreciated. This is a rather urgent situation.

Thanks in advance.
Attachments
UART reception.JPG
From the PIC32 datasheet

User avatar
phalanx
Non-SFE Guru
Posts: 1949
Joined: Sun Nov 30, 2003 8:57 am
Location: Candia, NH

Re: PIC32 UART buffer overrun in MIDI receiver application

Post by phalanx » Tue Oct 25, 2016 8:33 pm

If you are running your PIC at 8Mhz (4 MIPS), you can only execute 1100ish instructions between each received UART character at 31250bps. That isn't very much and depending on your other processing, you can easily cause delays which prevent you from pulling all the received characters out of the buffer before it overflows.

These PICs run up to 80 MIPS so you are leaving a lot of performance on the table by clocking it for 4 MIPS.

The proper fix is you should be using interrupts (or DMA if this PIC has it) to send and receive serial data. An interrupt service routing for serial Rx would immediately read a character out of the hardware buffer and put it into a software defined one. Your main program loop would then periodically check the software buffer and act on it when necessary. In this instance you will never have a UART buffer overflow since every received character is immediately removed as soon as it's received. DMA requires even less overhead but is more complicated to set up properly.

-Bill

wave.jaco
Posts: 28
Joined: Sat Aug 01, 2015 2:45 am
Location: South Africa

Re: PIC32 UART buffer overrun in MIDI receiver application

Post by wave.jaco » Wed Oct 26, 2016 1:50 am

Hi Bill, thanks for your post.

I am seriously considering to increase the system clock to a much higher rate, since there is indeed a lot of processing going on with the received data. In the meantime I have implemented a software FIFO buffer, but I am experiencing some new problems with the received data. After a little bit of playing some rather intensively busy music on the keyboard, it seems that the receiving system gets "locked-up" in an unexpected state in terms of processing the incoming data. It is as if data is lost/missed during all the processing that causes all the incoming data after that lost data to be interpreted completely incorrectly.

I am now using an interrupt for parsing the incoming data, instead of the method in my previous post. This is my ISR at the moment:

Code: Select all

void __ISR(0, ipl1) InterruptHandler(void)
{
    /* If software FIFO buffer is full */
    if (rxFifo.numBytes == FIFO_BUFFER_SIZE)
    {
        fifoFlagOverflow = TRUE;
    } 
    /* If the software FIFO buffer is not full */
    else if (rxFifo.numBytes < FIFO_BUFFER_SIZE)
    {
        #if 0 /* if necessary */
        if (U1STAbits.OERR = 1)
        {
            U1STAbits.OERR = 0;
        }
        #endif
        
        /* Read a byte from the UART receive (hardware) FIFO */
        rxFifo.dataBuffer[rxFifo.lastByteIndex] = U1RXREG;   
        if (rxFifo.lastByteIndex == FIFO_BUFFER_SIZE)
        {
            rxFifo.lastByteIndex = 0; /* Roll-over the index counter */       
        }            
        else            
        {
            rxFifo.lastByteIndex++;
        }            
        rxFifo.numBytes++;
    }
    
    /* If buffer has now been filled up after the byte read above */
    if (rxFifo.numBytes == FIFO_BUFFER_SIZE)
    {
        fifoFlagFull = TRUE;
        
        /* TODO: Still to decide what to do here... */
    }
    
    /* If the index has reached the end of the buffer */
    if (rxFifo.lastByteIndex == FIFO_BUFFER_SIZE)
    {
        rxFifo.lastByteIndex = 0; /* Roll-over the index counter */       
    }
    
    /* TODO: move elsewhere and handle properly later... */
    //fifoFlagBufEmpty = FALSE;
} /* ISR */
The rxFifo structure that is used in the ISR (and elsewhere, since it is a global variable) looks as follows (FIFO_BUFFER_SIZE is currently 100 bytes):

Code: Select all

typedef struct softFifo
{
    int dataBuffer[FIFO_BUFFER_SIZE];
    int firstByteIndex;
    int lastByteIndex;
    int numBytes;
} fifoBuf_t;
In the main routine, my code looks as follows (stripped to include relevant sections):

Code: Select all

void main(void)
{
   int dataRx = 0;

   initMain();

    while (1)
    {
        /* While there is data in the software FIFO buffer */
        while (fifoFlagBufEmpty == FALSE)
        {
            dataRx = getByte();
            processIncomingMessage((unsigned char*)&dataRx);
        }
        
        /* Check for software FIFO buffer overflow */
        /* TODO: Still to decide what to do here */
        if (fifoFlagOverflow == TRUE)
        {
            /* ...code handling of overflow here... */
            fifoFlagOverflow = FALSE;
        }
    }
    asm("nop"); /* for breakpoint */
}


int getByte(void)
{
    int byteValue = -1;
    
    /* If data exists in the buffer */
    if (rxFifo.numBytes > 0)
    {
        byteValue = rxFifo.dataBuffer[rxFifo.firstByteIndex];
        rxFifo.firstByteIndex++;
        rxFifo.numBytes--;
        //rxFifo.lastByteIndex--;
    }
    /* NOTE: this condition should actually never be reached */
    else /* If the software FIFO buffer is empty */
    {
        fifoFlagBufEmpty = TRUE; /* (Now it IS empty) */
        return byteValue; /* with value = -1, indicating no data */
    }
    
    /* If the software FIFO buffer is empty, after the above read */
    /* This is the same as (numBytes == 0) */
    if ((rxFifo.firstByteIndex + 1) == rxFifo.lastByteIndex)
    {
        fifoFlagBufEmpty = TRUE;
        /* Note that numBytes should now also be 0 */
    }
    
    /* If at the end of the buffer */
    if (rxFifo.firstByteIndex == FIFO_BUFFER_SIZE)
    {
        rxFifo.firstByteIndex = 0; /* Roll-over index counter */
    }
    
    return byteValue;
} /* getByte() */
The function processIncomingMessage() executes a particular process when a complete MIDI message is received. It does it by checking each incoming byte passed to it and determining first which type of message it is, and thereafter waiting for subsequent parts of the message until a complete message is received. Therefore, it doesn't do much until a complete message is received.

The transmitting end is running at 8 MHz and its UART at a baud rate of 31250 bps. That seems to be working nicely, regardless of how much I throw at it while playing on the keyboard (I have also tested this with a MIDI synth on my computer and all works perfect). So I do not see any real need to increase the performance on the transmitting side.

The receiving side can perhaps do with some increase in performance. Since I have the receiving side on 8 MHz / 31250 bps baud rate as well, I thought of just increasing the system clock frequency. Of course, this would influence the setup of UART peripheral. I am currently setting up the UART with the following code, contained in the initMain() function called in main().

Code: Select all

void UART_initUART(unsigned int channel, unsigned int baudRate, BOOL BRGH_bit, 
                   unsigned int clk)
{
    /* Enable UART, BRGH = 0, simplex, RX idle LOW, 1 stop, no parity */
    unsigned int uxMode = 0x8800; 
    /* Enable RX & TX */
    unsigned int uxSta = 0x1400;
    unsigned int brg;
    brg = UART_CalcBRG(baudRate, BRGH_bit, clk);
	 switch (channel)
    {
        case 1:
            U1BRG = brg;
            U1MODE = uxMode;
            U1STA = uxSta;
            break;
        case 2:
            U2BRG = brg;
            U2MODE = uxMode;
            U2STA = uxSta;
            break; 
    } /* switch */
} /* UART_initUART */


unsigned int UART_CalcBRG(unsigned int baudRate, BOOL BRGH_bit, unsigned int clk)
{
    unsigned int BRGval;    
    if (BRGH_bit == FALSE) /* low speed baud rate generator used */
    {
        BRGval = (clk / (16 * baudRate)) - 1;
    }
    else /* high speed baud rate generator used */
    {
        BRGval = (clk / (4 * baudRate)) - 1;
    }
    
    return BRGval;
} /* UART_CalcBRG */
My timing functions used for delays are also automatically adjusted according to the system clock frequency.

To see if I can marginally improve on the performance, I have increased the system clock to 10 MHz for a first try. But now it seems that the UART is not receiving the data properly anymore. It is actually receiving data, but it doesn't match the data that is being transmitted to it. First-off, this would sound like a baud rate mismatch between the two sides, but I cannot fathom why that would happen, since the baud rate is not supposed to be changed on the receiving side when the system clock is changed - only the BRG value will (in accordance with the system clock, as per the calculation formula in the PIC's datasheet).

The only thing that comes to mind regarding the UART might be to do with the BRGH bit (which is currently set to 0), but I can't foresee why the current setting would be causing the problem. The only other thing might be the software FIFO buffer size. However, I have even increased it to 5000 bytes and I still get the same "getting locked-up" problem, so I doubt that it has to do with the FIFO buffer size, and that it has more to do with the amount of processing performed on the incoming data, that might be too slow for a high rate of incoming data (due to the low system clock frequency).

I would like to increase the system clock all the way to 40 MHz and assess the performance again. But, I would first need to figure out what is causing this anomaly with this "slight" increase to 10 MHz first.

wave.jaco
Posts: 28
Joined: Sat Aug 01, 2015 2:45 am
Location: South Africa

Re: PIC32 UART buffer overrun in MIDI receiver application

Post by wave.jaco » Wed Oct 26, 2016 3:49 pm

Success!! I have found the error!

My first mistake was that I did not set the configuration bits up correctly so that the CPU and peripheral clocks are correctly calculated with the pre-/postscalers and PLLs. My configuration bits were set such that only the FRC was used, and not the FRC with PLL. Therefore, the system and peripherals were still running at 8 MHz while I set the baud rate generator up for another clock frequency. That explains the funny data received over the UART.

Furthermore, I had to set up the actual pre-/postscaler values to the correct ones to give me a 40 MHz system clock.

Now, no matter what I throw at the receiver thus far, it performs and processes all the floods of MIDI messages like a champ! Not a single "lock-up" due to missed data over the UART. And even with a software FIFO buffer size of only 100 bytes! Therefore, I am very certain I had a situation where the 8 MHz clock simply didn't offer enough performance. Increasing it to 40 MHz makes a huge difference!

As always, thanks for your most valuable assistance, Bill. Your recommendation of increasing the system clock is what solved my problem (after sorting out my silly mistakes with the pre-/postscalers). Your assistance is highly appreciated!

User avatar
phalanx
Non-SFE Guru
Posts: 1949
Joined: Sun Nov 30, 2003 8:57 am
Location: Candia, NH

Re: PIC32 UART buffer overrun in MIDI receiver application

Post by phalanx » Wed Nov 02, 2016 11:34 am

Sorry I've been slow responding. I've been on the road for work the past couple of weeks.

I'm glad the increase in the system clock got you squared away but I would still consider implementing interrupts as the proper way to solve your problem. In its current state, it's possible for you to run into the same problem again if in the future you increase the amount of processing that the rest of your program performs.

Just something to consider!

-Bill

wave.jaco
Posts: 28
Joined: Sat Aug 01, 2015 2:45 am
Location: South Africa

Re: PIC32 UART buffer overrun in MIDI receiver application

Post by wave.jaco » Wed Nov 02, 2016 2:21 pm

No problem, Bill! Thanks for your reply.

I am actually using interrupts now for the UART reception to fill the software FIFO buffer. It works like a charm. To be honest, I will probably never handle UART reception in any other way than using interrupts after experiencing the problem that I had. Those interrupts are truly very useful.

Once again, thanks for all your help, Bill!

Post Reply