7 years, 11 months ago.

Interrupt handler locks up

I am using a STM32F747 Disco development board. I have conected a MIDI device to the USART6_RX pin (D0). Furthermore, I have connected a external device using SPI.

A software timer fires a software interrupt every 500 ms. This interrupt routine will send a command through SPI to the device. Furthermore, I am using a counter that increments an integer on the screen once a second.

After receiving one byte of serial data on USART6_RX, this interrupt handler seems to lock up. This results in the main loop locking up (so it stops running through the while loop. The interesting part is that the software interrupt (that sends a SPI command every 500 ms) keeps running.

What is going on here?

Main program code

#include "mbed.h"
#include "LCD_DISCO_F746NG.h"
#include "RF22.h"

LCD_DISCO_F746NG lcd;                                   //LCD Object
Buzzer mySpiDevice(D10,D11,D12,D13,D2);   //SPI object
Serial midi(D1, D0);                                    //Midi Object
Serial pc(USBTX, USBRX);                                //PC Object - For debugging purposes

Ticker commandTimer;                                     //Software Timer that creates software IRQ
Timer updateNumberTimer;                            //Software Timer to create non-blocking delays      

/*
 *  Commands the SPI device to send a message
 */
void sendCommand()
{
         mySpiDevice.sendMessage();
}

/*
 * Is called in the event of an Serial RX interrupt
 * Please note that this can be on ANY UART port (so also from the debugger)
 */
void Rx_interrupt()
{
      //Now it's empty
      return;
}

int main()
{
    midi.baud(31250);       //Initializes the midi to correct baudrate
    pc.baud(115200);        //Selected highest baudrate for USB to be sure that is is faster than midi
    midi.attach(&Rx_interrupt, Serial::RxIrq);  //If an serial RX interrupt occurs, the RX_interrupt function will be called
  
    commandTimer.attach(&sendCommand, .5);        //Initializes the software timer firing a soft IRQ every 500 ms
    
    pc.printf("Initialisation OK\r\n");
    
    lcd.Clear(LCD_COLOR_WHITE);
    lcd.SetTextColor(LCD_COLOR_BLACK);
    lcd.SetFont(&Font20);
    lcd.SetBackColor(LCD_COLOR_WHITE);
    
    int nCounter = 0;
    updateNumberTimer.start(); //Starts the counter
    char pNumberBuf[10];
    
    while (1)
    {
        if (updateNumberTimer.read_ms() > 1000)
        {
            pc.printf("Teller is: %d\r\n", nCounter);
            nCounter++; 
            sprintf(pNumberBuf, "Time: %04d\0", nCounter);
            lcd.DisplayStringAt(50, 50, (uint8_t *)pNumberBuf, LEFT_MODE);
            updateNumberTimer.reset();  //Reset counter to 0
        }
    }  
}

Buzzer via SPI sendMessage

void Buzzer::sendMessage()
{
    uint8_t data[3] = {"0x10, 0x11, 0x12};   //Blink command
    send(data, sizeof(data));   //Send byte
}

void Buzzer::send(const uint8_t* data, uint8_t len)
{
    __disable_irq();
         //memcpy the data to TX buffer before sending it to the buzzer
    __enable_irq();

     spiWrite(REG_PACKET_LENGTH, len); //Send first byte only (for now as test)
}

void spiWrite(uint8_t reg, uint8_t val)
{
    __disable_irq();    // Disable Interrupts
    _slaveSelectPin = 0;
    _spi.write(reg | SPI_WRITE_MASK); 
    _spi.write(val); 
    _slaveSelectPin = 1;
    __enable_irq();     // Enable Interrupts
}

So bottomline; Why - after receiving one byte of serial data - does the main thread lock up and does the software interrupt continue to send messages as it should?

2 Answers

7 years, 11 months ago.

I have no idea why your timer interrupt keeps functioning. Only reason that should happen is because it has higher priority set, but as far as I am aware every interrupt initializes with the maximum priority if you don't specifically change it.

To solve your real issue: Put in your RX interrupt a midi.getc(). Later you can actually do something with it, but the issue is if you don't clear the received byte from the buffer, it will keep firing that same interrupt since it considers it not yet handled, there is still something in the receive buffer.

Thanks Erik, I added the midi.getc(). This seems to work. But after stopping the Serial feed and restarting it, the midi.readable() keeps returning false. Do you know if the USART will lock up in the case of an buffer overflow?

posted by Alex van Rijs 01 Jun 2016

Nop, it will just throw first received one away and makes place for new one. Where is this readable however? Do you take into account if your interrupt handler is reading everything, there is never data for your main program if you have the readable there?

posted by Erik - 01 Jun 2016
7 years, 11 months ago.

In addition to Erik's answer you have an issue with

sprintf(pNumberBuf, "Time: %04d\0", nCounter);

This isn't going to cause the problem you have right now but it could come back to bite you in the future,

"Time: %04d\0" is 11 characters long, sprintf will then add a null on the end automatically meaning this function call will write 12 bytes to pNumberBuf. pNumberBuf only has 10 bytes allocated.

Remove the \0 at the end of the string, its redundant, sprintf adds it, and increase the size of pNumberBuf by a few bytes.

Ideally also use snprintf rather than sprintf in order to ensure there are no nasty gotchas that don't show up in testing but do when you try to use the code (e.g. after 2 3/4 hours nCounter will hit 5 digits and your string length will increase by 1).

Hi Andy, thanks for your message. It was kind of messy of me and corrected it directly. One weird problem left: Every now and then the USART seems to lock up and then midi.readable() keeps returning false...Seems that the interrupt flag in the USARt register isn't set anymore. Could this be because a buffer overflow?

posted by Alex van Rijs 01 Jun 2016

I'm not sure quite what could cause that but to play safe make it so that the interrupt reads all the waiting data and no more.

void Rx_interrupt()
{
      while(midi.readable())
        midi.getc();
}
posted by Andy A 01 Jun 2016