Skip to content

[feature-nrf528xx] Serial re-implementation for the NRF52 series #6435

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 26, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions targets/TARGET_NORDIC/TARGET_NRF5x/PeripheralPinsDefault.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,26 @@
* Can be overwritten by user.
*/
MBED_WEAK const PinMapI2C PinMap_I2C[1] = {
{NC, NC, NC}
{ NC, NC, NC }
};

/* Default mapping between SPI pins and SPI instance.
* Can be overwritten by user.
*/
MBED_WEAK const PinMapSPI PinMap_SPI[1] = {
{NC, NC, NC, NC}
{ NC, NC, NC, NC }
};

/* Default mapping between PWM pins and PWM instance.
* Can be overwritten by user.
*/
MBED_WEAK const PinMapPWM PinMap_PWM[1] = {
{NC, NC}
{ NC, NC }
};

/* Default mapping between UART pins and UART instance.
* Can be overwritten by user.
*/
MBED_WEAK const PinMapUART PinMap_UART[1] = {
{ NC, NC, NC }
};
62 changes: 62 additions & 0 deletions targets/TARGET_NORDIC/TARGET_NRF5x/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,65 @@ The tables must be placed in a C compilation file.
1. When called from the same thread, it is safe to assign I2C and SPI objects to the same instance.
1. If an instance is being used exclusively for either I2C or SPI, the objects can safely be called from multiple threads.
1. If an instance is being used for both I2C and SPI, the user must provide thread safety between the objects.


### Serial

The serial implementation uses the UARTE module which works exclusively through EasyDMA and RAM buffers. For optimal performance, each configured instance (NRF52832 has 1, NRF52840 has 2) has three buffers statically assigned:

1. Rx DMA buffer, which EasyDMA is currently writing to.
1. Rx DMA buffer, pre-loaded in EasyDMA for automatic switchover.
1. Rx FIFO buffer, for serving data to the application.

When the first DMA buffer is full or flushed the interrupt handler will automatically copy the DMA buffer to the FIFO buffer. This happens in interrupt context to avoid data loss and with UARTE interrupts set at the highest priority. The FIFO buffer is backed by the Nordic atomic fifo, which can be read and written to safely without disabling interrupts.

#### Customization

All buffers can be resized to fit the application:

```
"name": "nordic",
"config": {
"uart-dma-size": {
"help": "UART DMA buffer. 2 buffers per instance. DMA buffer is filled by UARTE",
"value": 8
},
"uart-0-fifo-size": {
"help": "UART0 FIFO buffer. FIFO buffer is filled from DMA buffer.",
"value": 32
},
"uart-1-fifo-size": {
"help": "UART1 FIFO buffer. FIFO buffer is filled from DMA buffer.",
"value": 32
}
}
```

All DMA buffers are the same size and must be at least 5 bytes due to hardware restrictions. DMA buffers should be sized to handle the worst expected interrupt latency. FIFO buffers can be configured per instance and the size should reflect the largest expected burst data. For example, a serial debug port might receive a line of data at a time, so an 80 byte FIFO buffer would be adequate. A serial port connected to a wifi radio should have a FIFO buffer in the kilo byte range.

For the NRF52840, UARTE instances are assigned based on pins and calling order. Serial objects with the same pin configurations will go to the same instance. A custom configuration table can be provided by overriding the weakly defined default empty table. In the example below, serial objects using pins `p1` and `p2` for `Tx` and `Rx` will always be assigned to `Instance 1` and serial objects using pins `p3` and `p4` for `Tx` and `Rx` will be assigned to `Instance 0` regardless of calling order. The custom configuration table must always be terminated with a row of `NC`.

```
const PinMapI2C PinMap_UART[] = {
{p1, p2, 1},
{p3, p4, 0},
{NC, NC, NC}
};
```

The table must be placed in a C compilation file.


#### RTC2

Because each DMA buffer must be at least 5 bytes deep, each buffer is automatically flushed after a certain idle period to ensure low latency and correctness. This idle timeout is implemented using 2 of the 4 channels on RTC instance 2. This leaves RTC0 for the SoftDevice and RTC1 for Mbed tickers.

The RTC2 ISR is set at the lowest interrupt priority to ensure that UARTE interrupts take precedence. The last 2 of the 4 RTC channels are used for decoupling UARTE ISR context from Mbed IRQ events. This ensures that any user code will only delay other user callbacks and idle flushing and puts an upper bound on the interrupt handling time for the UARTE ISR.

#### Limitations

* The UARTE hardware only supports 8-bit, None/Even parity, and 1 stop bit.
* The asynchronous read and write implementation currently only support 255 byte transfers.
* The EasyDMA hardware can only read from RAM, which means all Tx buffers must reside in RAM. If a Tx buffer residing in flash is passed to the asynchronous write function, the function will try to copy the Tx buffer to a temporary internal buffer and transmit the data from there.
* It is not possible to do an asynchronous write from flash and receive non-asynchronously at the same time since the non-asynchronous receive buffer is being used as the temporary transmission buffer.

Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,6 @@
extern "C" {
#endif

#define STDIO_UART_TX TX_PIN_NUMBER
#define STDIO_UART_RX RX_PIN_NUMBER
#define STDIO_UART UART_0

typedef enum {
UART_0 = (int)NRF_UART0_BASE
} UARTName;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,8 @@ typedef enum {
// mBed interface Pins
USBTX = TX_PIN_NUMBER,
USBRX = RX_PIN_NUMBER,
STDIO_UART_TX = TX_PIN_NUMBER,
STDIO_UART_RX = RX_PIN_NUMBER,

SPI_PSELMOSI0 = p23,
SPI_PSELMISO0 = p24,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@ typedef enum {
TX_PIN_NUMBER = p6,
CTS_PIN_NUMBER = p7,
RTS_PIN_NUMBER = p31,
STDIO_UART_TX = TX_PIN_NUMBER,
STDIO_UART_RX = RX_PIN_NUMBER,

I2C_SDA = p2,
I2C_SCL = p3,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,8 @@ typedef enum {
// mBed interface Pins
USBTX = TX_PIN_NUMBER,
USBRX = RX_PIN_NUMBER,
STDIO_UART_TX = TX_PIN_NUMBER,
STDIO_UART_RX = RX_PIN_NUMBER,

SPI_PSELMOSI0 = p23,
SPI_PSELMISO0 = p24,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,8 @@ typedef enum {
TX_PIN_NUMBER = p29,
CTS_PIN_NUMBER = p28,
RTS_PIN_NUMBER = p2,
STDIO_UART_TX = TX_PIN_NUMBER,
STDIO_UART_RX = RX_PIN_NUMBER,

// mBed interface Pins
USBTX = TX_PIN_NUMBER,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,9 @@ typedef enum {
TX_PIN_NUMBER = p6,
CTS_PIN_NUMBER = p7,
RTS_PIN_NUMBER = p31,
STDIO_UART_TX = TX_PIN_NUMBER,
STDIO_UART_RX = RX_PIN_NUMBER,

I2C_SDA0 = p2,
I2C_SCL0 = p3,

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ typedef enum {
TX_PIN_NUMBER = p6,
CTS_PIN_NUMBER = p7,
RTS_PIN_NUMBER = p31,
STDIO_UART_TX = TX_PIN_NUMBER,
STDIO_UART_RX = RX_PIN_NUMBER,
I2C_SDA0 = p2,
I2C_SCL0 = p3,
// mBed interface pins
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,8 @@ typedef enum {
TX_PIN_NUMBER = p6,
CTS_PIN_NUMBER = p7, //not on Header
RTS_PIN_NUMBER = p5, //not on Header
STDIO_UART_TX = TX_PIN_NUMBER,
STDIO_UART_RX = RX_PIN_NUMBER,

// mBed interface Pins
USBTX = TX_PIN_NUMBER,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3004,7 +3004,7 @@
// <e> UART_ENABLED - nrf_drv_uart - UART/UARTE peripheral driver
//==========================================================
#ifndef UART_ENABLED
#define UART_ENABLED 0
#define UART_ENABLED 1
#endif
// <o> UART_DEFAULT_CONFIG_HWFC - Hardware Flow Control

Expand Down Expand Up @@ -3044,7 +3044,7 @@
// <268435456=> 1000000 baud

#ifndef UART_DEFAULT_CONFIG_BAUDRATE
#define UART_DEFAULT_CONFIG_BAUDRATE 30801920
#define UART_DEFAULT_CONFIG_BAUDRATE 2576384
#endif

// <o> UART_DEFAULT_CONFIG_IRQ_PRIORITY - Interrupt priority
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,6 @@
extern "C" {
#endif

#define STDIO_UART_TX TX_PIN_NUMBER
#define STDIO_UART_RX RX_PIN_NUMBER
#define STDIO_UART UART_0

typedef enum
{
UART_0 = (int)NRF_UART0_BASE
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,8 @@ typedef enum {
// mBed interface Pins
USBTX = TX_PIN_NUMBER,
USBRX = RX_PIN_NUMBER,
STDIO_UART_TX = TX_PIN_NUMBER,
STDIO_UART_RX = RX_PIN_NUMBER,

SPI_PSELMOSI0 = P1_13,
SPI_PSELMISO0 = P1_14,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3004,7 +3004,7 @@
// <e> UART_ENABLED - nrf_drv_uart - UART/UARTE peripheral driver
//==========================================================
#ifndef UART_ENABLED
#define UART_ENABLED 0
#define UART_ENABLED 1
#endif
// <o> UART_DEFAULT_CONFIG_HWFC - Hardware Flow Control

Expand Down Expand Up @@ -3044,7 +3044,7 @@
// <268435456=> 1000000 baud

#ifndef UART_DEFAULT_CONFIG_BAUDRATE
#define UART_DEFAULT_CONFIG_BAUDRATE 30801920
#define UART_DEFAULT_CONFIG_BAUDRATE 2576384
#endif

// <o> UART_DEFAULT_CONFIG_IRQ_PRIORITY - Interrupt priority
Expand Down Expand Up @@ -3095,7 +3095,7 @@
// <e> UART1_ENABLED - Enable UART1 instance
//==========================================================
#ifndef UART1_ENABLED
#define UART1_ENABLED 0
#define UART1_ENABLED 1
#endif
// <q> UART1_CONFIG_USE_EASY_DMA - Default setting for using EasyDMA

Expand Down
29 changes: 29 additions & 0 deletions targets/TARGET_NORDIC/TARGET_NRF5x/mbed_lib.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"name": "nordic",
"config": {
"uart-hwfc": {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@marcuschangarm @0xc0170 Would having uart declared in both mbed_lib.json and targets.json cause any issues when overriding it in custom_targets.json?

I'm using a custom_targets.json which is intended to disable flow control, but the mbed_config.h always ends up containing the following:

#define MBED_CONF_NORDIC_UART_HWFC                        1    // set by target:MCU_NRF52840
#define MBED_CONF_NORDIC_UART_HWFC                        1    // set by library:nordic

Since I'm not using hardware flow control this results in my printf's hanging.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you might have missed my response here: https://os.mbed.com/forum/upcoming-features/topic/29477/?page=1#comment-56395

In any case, targets.json has been cleaned up and a new PR is in the pipeline: #6547

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@marcuschangarm
I believe the recommendation did not work due to @vergil-SI overriding uart-hwfc and uart_hwfc in custom_targets.json.

#6547 does seems to address this issue by doing target specific target_overrides in mbed_lib.json.

"help": "Enable hardware flow control for STDIO.",
"value": 1
},
"lf-clock-src": {
"help": "Select Low Frequency clock source.",
"value": "NRF_LF_SRC_XTAL"
},
"uart-timeout-us": {
"help": "Idle time in micro seconds between characters before buffer is flushed.",
"value": 2000
},
"uart-0-fifo-size": {
"help": "UART0 FIFO buffer. FIFO buffer is filled from DMA buffer.",
"value": 32
},
"uart-1-fifo-size": {
"help": "UART1 FIFO buffer. FIFO buffer is filled from DMA buffer.",
"value": 32
},
"uart-dma-size": {
"help": "UART DMA buffer. 2 buffers per instance. DMA buffer is filled by UARTE",
"value": 8
}
}
}
27 changes: 25 additions & 2 deletions targets/TARGET_NORDIC/TARGET_NRF5x/objects.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,32 @@
extern "C" {
#endif

#include "nrf_uart.h"

struct serial_s {
uint32_t placeholder; // struct is unused by nRF5x API implementation
}; // but it must be not empty (required by strict compiler - IAR)
int instance;
uint32_t tx;
uint32_t rx;
uint32_t cts;
uint32_t rts;
nrf_uart_hwfc_t hwfc;
nrf_uart_parity_t parity;
nrf_uart_baudrate_t baudrate;
uint32_t context;
uint32_t handler;
uint32_t mask;
uint32_t event;
bool update;
#if DEVICE_SERIAL_ASYNCH
bool rx_asynch;
uint32_t tx_handler;
uint32_t tx_mask;
uint32_t tx_event;
uint32_t rx_handler;
uint32_t rx_mask;
uint32_t rx_event;
#endif
};

struct spi_s {
int instance;
Expand Down
Loading