UART Glitch On Startup Causes Hardfault in FreeRTOS

Jeff,

Thank you for writing! I’ll respond to the quick ones here and then report back as I dig through the more complex things to check.

v10 may be useful for debugging. The legacy code lives in v7.5.2 and will likely stay there barring major issue.

The baud rate is 2 MBaud (2,000,000).
The glitch occurs before the traffic. See below from 2.5s to 5s on the MAIN RX label for the glitch (0x00 FRAMING ERROR for the initialized UART):


The traffic is four 2-byte packets: two RX and two TX. Typically about 150 usec start to finish.

Your last option is what we see. Power on, init UART, glitch, proper UART comms, then a hardfault.

I can get you the UART ISR. We are using DMA for RX so there is not much to it. I also performed a test which moved the enabling of the NVIC for UART from before the glitch to after, which still hardfaulted: indicating the problem is not likely the ISR.

Worth noting that the US1_ErrorISR() does fire for the FRAME ERROR. It cannot be masked due to DMA being enabled. Sidebar: This appears to be a minor TRM inconsistency in the logic diagram for interrupt masks and the EIE behavior with DMA.

void USART1_IRQHandler(void)
/*
// Determines the cause of the USART1 interrupt request and calls the
// appropriate IRQ handler.
//
*/
{
uint32_t cr1reg;
uint32_t us1_status;

  us1_status = USART1->SR;    // read the usart`s status register
  cr1reg = USART1->CR1;       // read USART1`s control reg 1 for int enables
                              // the transmit interrupts are only enabled when
                              // data is placed in the tx buffer for transmission

  if(us1_status & 0x0f)       // if an error flag is set
    US1_ErrorISR(us1_status); // handle the error

  else
  {
    // transmit machine including the shift register is empty
    if(us1_status & BIT6 && (cr1reg & BIT6))
    {
      DISABLE_TCIE1;          // disable the IRQ pending bit in the usart
      Tx1_active = FALSE;     // set the transmission complete flag for the app
    }

    // transmit buffer register empty interrupt
    else if(us1_status & BIT7 && (cr1reg & BIT7))
      US1_TxISR();
  }
}

void US1_TxISR(void)
/*
// USART1 Transmitter interrupt service routine. Retreives a char from the
// transmitter ring buffer and writes it to the transmitter data register.
//
*/
{
  // if the Tx buffer is empty flag the transmitter as inactive
  // and return
  if(us1_txbuf_out == us1_txbuf_in)
  {
    us1_txbuf_count = 0;
    DISABLE_TXIE1;            // disable the tx interrupt until needed again
    return;                   // and exit
  }

  // Tx buffer not empty, transmit the next byte
  USART1->DR = us1_txbuf[us1_txbuf_out++];
  --us1_txbuf_count;          // decrement the tx buffer counter

  // check buffer array bounds and reset if necessary
  if(us1_txbuf_out > MAX_US1BUFS)
    us1_txbuf_out = 0;
}

void US1_ErrorISR(uint32_t status)
/*
// USART1 Error interrupt service routine. Ackowledge the error and read the
// receiver data register to reset the IRQ pending bit. The offending data
// is thrown away leaving the input parser to deal with the bad and incomplete
// data sequence. The if() statements are left over debugging artifacts.
//
*/
{
static uint8_t ecnt = 0;
uint8_t rch, i;

  i = 0;

  rch = USART1->DR;

  if(status & BIT0)       // if a parity error occurred
    i = 1;

  if(status & BIT1)       // if a framming error occurred
    i = 2;

  if(status & BIT2)       // if a noise error occurred
    i = 3;

  if(status & BIT3)       // if a overrun error occurred
    i = 4;

  dterr("rch %02x, ecnt %02x, status %02x <overrun, noise, frame, parity>", rch, ecnt, status);
  // all this code does is prevent a compile warning
  if(i > 0)
    --i;

  if(rch > 0)
    --rch;
}

Onward.

Cheers,
Joe