This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

nRF51 UART Confusion

I am completely confused by the nRF51 UART. I'm trying to port code originally written for an STM32 (another cortex chip). There, I define an interrupt, enable the RXRDY or TXEMPTY interrupts, and if a character comes in, I get an interrupt, or if I send a character, I get an interrupt as soon as it leaves the TX register and enters the shift register. Simple, and it just works.

I'm trying to do the same here, but can't get the IRQ routine to fire at all. I'm using the PCA10028 dev kit, pins 9 and 11 as RX and TX, don't need flow control, and soft device 110 and SDK 7.

I assume my problem has something to do with the PPI interface, but can't wrap my head around things like STARTTX, STARTRX or events. Can you use the UART without these, and just use INTENSET and INTENCLR? Or are these required?

I looked at the uart examples, but none of these looked, to me, suited for the kind of serial I/O I need to perform. The uart example doesn't use IRQ, so it doesn't look suitable as a starting point. Neither does the experimental_ble_app_uart example, as it too uses simple, non interrupt uart code.

Plus, despite having "uart" in their project names, almost none of the examples actually send or receive anything over the UART, unless it is debug data via printf, which is certainly not what I'm looking to do.

Any hints on how to take control of the UART?

Thanks, Jeff.

Parents
  • Hi Jeff

    I agree with you that the uart example does not show how to use those events that you mention (data_ready and data_sent), but what SDK version are you using? I suspect that you are using older than SDK 8 at least since you mention experimental_ble_app_uart which is no longer experimental in SDK 8.

    I refer to SDK 8.1.0. The example \examples\peripheral\uart uses the app_uart library which uses two FIFO buffers in the background to queue data.

    • When data arrives on the UART it is put into the RX FIFO buffer and then the registered app_uart handler is called (the uart_error_handle in the example) with event APP_UART_DATA_READY. In the app_uart handler you could call app_uart_get to extract data from the RX FIFO buffer instead of polling it as done in the uart example.

    • When you want to send data to the UART, you call app_uart_put in order to put data into the TX FIFO, which will be sent over the UART if the UART is not currently busy. If the UART TX is currently busy sending data, an additional byte will be sent from the TX FIFO when the UART TX becomes available (when TXDRDY event is received, handled internally in app_uart library) or when CTS line goes low (indicating that the peer device is ready to receive data). When the TX FIFO is empty, i.e. all data has been sent, the app_uart event handler is called with the event type APP_UART_TX_EMPTY.

    So, in the \examples\peripheral\uart example, capture the APP_UART_TX_EMPTY and APP_UART_DATA_READY events (in uart_error_handle) instead of polling in the main loop. When you capture APP_UART_DATA_READY, call app_uart_get to extract data from the RX FIFO.

  • I believe I've found the problem.

    In the code I am porting, to start transmitting, I would simply enable the TXEMPTY interrupt. Since there was nothing in the TX register at the time, the interrupt would immediately fire, and the handler would pull a character from my buffer and send it. When that character was sent, the interrupt would fire again, and the next character would be pulled, and so on until the last character was sent, at which time the handler would disable the TXEMPTY interrupt.

    I assumed that this UART would behave similarly, and that to trigger sending data all I would need to do would be to enable the equivalent of TXEMPTY, which looked to be TXRDY. So my code to enable the transmitter was simply this:

    void SendTxBuff(void)
    {
      // Start the UART.
      NRF_UART0->INTENSET = (UART_INTENSET_TXDRDY_Set << UART_INTENSET_TXDRDY_Pos);
    }
    

    However, this does not cause the TXRDY interrupt to fire.

    I found that in order to kick start the process, I had to actually send the first character. Once I did, then the handler was called, and the rest of the characters would be sent. Like this:

    void SendTxBuff(void)
    {
      // Start the UART.
      NRF_UART0->INTENSET = (UART_INTENSET_TXDRDY_Set << UART_INTENSET_TXDRDY_Pos);
      // Send the first byte to get things started
      NRF_UART0->TXD = bufGetByte(&TxBuff);
    }
    

    Is this correct? Must I send the first byte to kick start the process, or is there something I missed about being able to enable the TXRDY interrupt "dry"?

    Thanks, Jeff.

Reply
  • I believe I've found the problem.

    In the code I am porting, to start transmitting, I would simply enable the TXEMPTY interrupt. Since there was nothing in the TX register at the time, the interrupt would immediately fire, and the handler would pull a character from my buffer and send it. When that character was sent, the interrupt would fire again, and the next character would be pulled, and so on until the last character was sent, at which time the handler would disable the TXEMPTY interrupt.

    I assumed that this UART would behave similarly, and that to trigger sending data all I would need to do would be to enable the equivalent of TXEMPTY, which looked to be TXRDY. So my code to enable the transmitter was simply this:

    void SendTxBuff(void)
    {
      // Start the UART.
      NRF_UART0->INTENSET = (UART_INTENSET_TXDRDY_Set << UART_INTENSET_TXDRDY_Pos);
    }
    

    However, this does not cause the TXRDY interrupt to fire.

    I found that in order to kick start the process, I had to actually send the first character. Once I did, then the handler was called, and the rest of the characters would be sent. Like this:

    void SendTxBuff(void)
    {
      // Start the UART.
      NRF_UART0->INTENSET = (UART_INTENSET_TXDRDY_Set << UART_INTENSET_TXDRDY_Pos);
      // Send the first byte to get things started
      NRF_UART0->TXD = bufGetByte(&TxBuff);
    }
    

    Is this correct? Must I send the first byte to kick start the process, or is there something I missed about being able to enable the TXRDY interrupt "dry"?

    Thanks, Jeff.

Children
No Data
Related