leiradel
Posts: 32
Joined: Wed Feb 13, 2019 10:38 pm

Mini UART interrupt enable

Sat Mar 09, 2019 7:18 pm

Hey all,

I'm struggling to enable RX interrupts for the Mini UART.

What I'm doing is:
  • Enable IRQ with cpsie i
  • Enable RX interrupts by writing 1 to AUX_MU_IER_REG
  • Enable AUX interrupts by writing 1<<29 to 0xXX00b210
To test, my IRQ handler only writes stuff to my framebuffer, but I don't get any output. I'm 100% sure the framebuffer is ok because I write to it in another places and it works. The interrupt vectors are correctly set because the UND handler is correctly called when I add an undefined instruction to my code.

Maybe I'm missing a step?

Thanks in advance,

Andre

boochow
Posts: 9
Joined: Thu Feb 08, 2018 2:42 pm
Contact: Website Twitter

Re: Mini UART interrupt enable

Sun Mar 10, 2019 12:46 am

If you are referencing the peripheral manual, please note that there are lots of bugs.
Especially, in the description of mini UART IER register flags, RX flag and TX flag are accidentally swapped.

See errata and dwelch67's sample code.

https://elinux.org/BCM2835_datasheet_errata#p12

https://github.com/dwelch67/raspberrypi ... 4/uart04.c

LdB
Posts: 1209
Joined: Wed Dec 07, 2016 2:29 pm

Re: Mini UART interrupt enable

Sun Mar 10, 2019 2:03 am

Try that again with right uart

1.) Did you enable the UART (#define AUX_ENABLES (RPi_IO_Base_Addr+0x00215004) )
2.) Did you put the GPIO 14 and 15 to GPIO_ALTFUNC5

My init code if it helps

Code: Select all

#define AUX_ENABLES (volatile uint32_t*)(RPi_IO_Base_Addr+0x00215004)

bool miniuart_init (unsigned int baudrate)
{
    uint32_t Buffer[5] = { 0 };
    if (mailbox_tag_message(&Buffer[0], 5, 
        MAILBOX_TAG_GET_CLOCK_RATE,	8, 8, CLK_CORE_ID, 0))   // Get core clock frequency check for fail 
    {
        uint32_t Divisor = (Buffer[4] / (baudrate * 8)) - 1;      // Calculate divisor
        if (Divisor <= 0xFFFF) { 
            PUT32(AUX_ENABLES, 1);                                 // Enable miniuart

            MINIUART->CNTL.RXE = 0;                                // Disable receiver
            MINIUART->CNTL.TXE = 0;                                // Disable transmitter

            MINIUART->LCR.DATA_LENGTH = 1;               // Data length = 8 bits
            MINIUART->MCR.RTS = 0;                                 // Set RTS line high
            MINIUART->IIR.RXFIFO_CLEAR = 1;                 // Clear RX FIFO
            MINIUART->IIR.TXFIFO_CLEAR = 1;                // Clear TX FIFO

             MINIUART->BAUD.DIVISOR = Divisor;             // Set the divisor

             gpio_setup(14, GPIO_ALTFUNC5);                  // GPIO 14 to ALT FUNC5 mode
             gpio_setup(15, GPIO_ALTFUNC5);                  // GPIO 15 to ALT FUNC5 mode

             /* Enable interrupts */
             MINIUART->IER.RXEI = 1;                     // Interrupt enable on RX fifo
             MINIUART->IER.LSI = 1;                        // Interrupt enable on overrun error, parity error, framing error etc

              MINIUART->CNTL.RXE = 1;                             // Enable receiver
              MINIUART->CNTL.TXE = 1;                              // Enable transmitter
              return true;                                                        // Return success
        }
    }
     return false;                                               // Invalid baudrate or mailbox clock error
}

leiradel
Posts: 32
Joined: Wed Feb 13, 2019 10:38 pm

Re: Mini UART interrupt enable

Sun Mar 10, 2019 10:15 pm

Thanks everyone, I was using the revised document but I've changed some of my code based on your suggestions/snippets. The main issue though was that I wasn't adding the base peripheral IO address to the register offset whan enabling the AUX interrupts :oops:

For the record, this is the meat of my Mini UART setup and IRQ code, consider it public domain:

Code: Select all

static int uart_read_isr(const uint32_t value) {
  (void)value;
  mem_dmb();

  while (1) {
    const uint32_t iir = mem_read32(BASE_ADDR + AUX_MU_IIR_REG);

    if ((iir & 1) == 1) {
      // No Mini UART interrupt pending, return.
      break;
    }
    
    if ((iir & 6) == 4) {
      // Character available, remove it from the receive FIFO.
      const uint32_t k = mem_read32(BASE_ADDR + AUX_MU_IO_REG) & 0xff;
      // Send it to user code.
      s_callback(k);
    }
  }

  mem_dmb();
  return 0;
}

void uart_init(const unsigned baudrate, uart_newchar_t newchar) {
  // Get the clock used with the Mini UART.
  const uint32_t clock_rate = prop_getclockrate(PROP_CLOCK_CORE);

  // Set pins 14 and 15 to use with the Mini UART.
  gpio_select(14, GPIO_FUNCTION_5);
  gpio_select(15, GPIO_FUNCTION_5);

  // Turn pull up/down off for those pins.
  gpio_setpull(14, GPIO_PULL_OFF);
  gpio_setpull(15, GPIO_PULL_OFF);

  mem_dmb();

  // Enable only the Mini UART.
  mem_write32(BASE_ADDR + AUX_ENABLES, 1);
  // Disable receiving and transmitting while we configure the Mini UART.
  mem_write32(BASE_ADDR + AUX_MU_CNTL_REG, 0);
  // Turn off interrupts.
  mem_write32(BASE_ADDR + AUX_MU_IER_REG, 0);
  // Set data size to 8 bits.
  mem_write32(BASE_ADDR + AUX_MU_LCR_REG, 3);
  // Put RTS high.
  mem_write32(BASE_ADDR + AUX_MU_MCR_REG, 0);
  // Clear both receive and transmit FIFOs.
  mem_write32(BASE_ADDR + AUX_MU_IIR_REG, 0xc6);

  // Set the desired baudrate.
  const uint32_t divisor = clock_rate / (8 * baudrate) - 1;
  mem_write32(BASE_ADDR + AUX_MU_BAUD_REG, divisor);

  if (newchar != NULL) {
    // Install the IRQ handler and enable read interrupts.
    s_callback = newchar;
    isr_addhandler(ISR_IRQ, uart_read_isr);
    mem_write32(BASE_ADDR + AUX_MU_IER_REG, 5);
    isr_enablebasic(29);
  }

  // Enable receiving and transmitting.
  mem_write32(BASE_ADDR + AUX_MU_CNTL_REG, 3);
}
Thanks again,

Andre

Return to “Bare metal, Assembly language”