mirror of https://github.com/ARMmbed/mbed-os.git
Serial re-implementation for the NRF52 series
Serial implementation uses UARTE instead of UART peripheral: * EasyDMA is used for reading and writing data. * Triple buffering for receiving data. See README for full description.pull/6547/head
parent
9502011d96
commit
3663494bd9
|
@ -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 }
|
||||
};
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"name": "nordic",
|
||||
"config": {
|
||||
"uart-hwfc": {
|
||||
"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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#define NORDIC_TWI_COUNT TWI_COUNT
|
||||
#define NORDIC_SPI_COUNT SPI_COUNT
|
||||
#define NORDIC_PWM_COUNT PWM_COUNT
|
||||
#define NORDIC_UART_COUNT UARTE_COUNT
|
||||
|
||||
/* Define which instance to return when there are no free instances left.
|
||||
* The Mbed HAL API doesn't provide a way for signaling initialization errors
|
||||
|
@ -41,6 +42,7 @@
|
|||
#define DEFAULT_I2C_INSTANCE 0 // SPI counts down, choose instance furthest away
|
||||
#define DEFAULT_SPI_INSTANCE (NORDIC_SPI_COUNT - 1) // I2C counts up, choose instance furthers away
|
||||
#define DEFAULT_PWM_INSTANCE (NORDIC_PWM_COUNT - 1)
|
||||
#define DEFAULT_UART_INSTANCE (NORDIC_UART_COUNT - 1)
|
||||
|
||||
|
||||
/***
|
||||
|
@ -365,3 +367,115 @@ int pin_instance_pwm(PinName pwm)
|
|||
|
||||
return instance;
|
||||
}
|
||||
|
||||
|
||||
/***
|
||||
* _ _ _____ _______
|
||||
* | | | | /\ | __ \__ __|
|
||||
* | | | | / \ | |__) | | |
|
||||
* | | | |/ /\ \ | _ / | |
|
||||
* | |__| / ____ \| | \ \ | |
|
||||
* \____/_/ \_\_| \_\ |_|
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
/* Internal data structure to keep track of allocated instances.
|
||||
*/
|
||||
typedef struct {
|
||||
PinName tx;
|
||||
PinName rx;
|
||||
} uart_t;
|
||||
|
||||
static uart_t nordic_internal_uart[NORDIC_UART_COUNT] = {
|
||||
{ NC, NC },
|
||||
#if (NORDIC_UART_COUNT == 2)
|
||||
{ NC, NC }
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
* Brief Find hardware instance for the provided UART pin.
|
||||
*
|
||||
* The function will search the PeripheralPin map for a pre-allocated
|
||||
* assignment. If none is found the allocation map will be searched
|
||||
* to see if the same pins have been assigned an instance before.
|
||||
*
|
||||
* If no assignement is found and there is an empty slot left in the
|
||||
* map, the pins are stored in the map and the hardware instance is
|
||||
* returned.
|
||||
*
|
||||
* If no free instances are available, the default instance is returned.
|
||||
*
|
||||
* Parameter tx tx pin.
|
||||
* Parameter rx rx pin.
|
||||
*
|
||||
* Return Hardware instance associated with provided pins.
|
||||
*/
|
||||
int pin_instance_uart(PinName tx, PinName rx)
|
||||
{
|
||||
int instance = NC;
|
||||
|
||||
/* Search pin map for pre-allocated instance */
|
||||
for (size_t index = 0; ((PinMap_UART[index].tx != NC) && (PinMap_UART[index].rx != NC)); index++) {
|
||||
|
||||
/* Compare pins to entry. */
|
||||
if ((PinMap_UART[index].tx == tx) && (PinMap_UART[index].rx == rx)) {
|
||||
|
||||
DEBUG_PRINTF("found: %d %d %d\r\n", tx, rx, PinMap_UART[index].instance);
|
||||
|
||||
/* Instance found, save result. */
|
||||
instance = PinMap_UART[index].instance;
|
||||
|
||||
/* Lock out entry in map. */
|
||||
nordic_internal_uart[instance].tx = tx;
|
||||
nordic_internal_uart[instance].rx = rx;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* No instance was found in static map. */
|
||||
if (instance == NC) {
|
||||
|
||||
/* Search dynamic map for entry. */
|
||||
for (size_t index = 0; index < NORDIC_UART_COUNT; index++) {
|
||||
|
||||
/* Pins match previous dynamic allocation, return instance. */
|
||||
if ((nordic_internal_uart[index].tx == tx) && (nordic_internal_uart[index].rx == rx)) {
|
||||
|
||||
instance = index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* No instance was found in dynamic map. */
|
||||
if (instance == NC) {
|
||||
|
||||
/* Search dynamic map for empty slot. */
|
||||
for (size_t index = 0; index < NORDIC_UART_COUNT; index++) {
|
||||
|
||||
/* Empty slot found, reserve slot by storing pins. */
|
||||
if ((nordic_internal_uart[index].tx == NC) && (nordic_internal_uart[index].rx == NC)) {
|
||||
|
||||
nordic_internal_uart[index].tx = tx;
|
||||
nordic_internal_uart[index].rx = rx;
|
||||
|
||||
instance = index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(DEFAULT_UART_INSTANCE)
|
||||
/* Exhausted all options. Return default value. */
|
||||
if (instance == NC) {
|
||||
instance = DEFAULT_UART_INSTANCE;
|
||||
}
|
||||
#endif
|
||||
|
||||
DEBUG_PRINTF("UART: %d %d %d\r\n", tx, rx, instance);
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
|
|
|
@ -50,6 +50,15 @@ typedef struct {
|
|||
|
||||
extern const PinMapPWM PinMap_PWM[];
|
||||
|
||||
/* Data structure for pre-allocated UART instances. */
|
||||
typedef struct {
|
||||
PinName tx;
|
||||
PinName rx;
|
||||
int instance;
|
||||
} PinMapUART;
|
||||
|
||||
extern const PinMapUART PinMap_UART[];
|
||||
|
||||
/**
|
||||
* @brief Find hardware instance for the provided I2C pins.
|
||||
*
|
||||
|
@ -110,6 +119,26 @@ int pin_instance_spi(PinName mosi, PinName miso, PinName clk);
|
|||
*/
|
||||
int pin_instance_pwm(PinName pwm);
|
||||
|
||||
/**
|
||||
* @brief Find hardware instance for the provided UART pins.
|
||||
*
|
||||
* The function will search the PeripheralPin map for a pre-allocated
|
||||
* assignment. If none is found the allocation map will be searched
|
||||
* to see if the same pins have been assigned an instance before.
|
||||
*
|
||||
* If no assignement is found and there is an empty slot left in the
|
||||
* map, the pins are stored in the map and the hardware instance is
|
||||
* returned.
|
||||
*
|
||||
* If no free instances are available, the default instance is returned.
|
||||
*
|
||||
* @param[in] tx tx pin.
|
||||
* @param[in] rx rx pin.
|
||||
*
|
||||
* @return Hardware instance associated with provided pins.
|
||||
*/
|
||||
int pin_instance_uart(PinName tx, PinName rx);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue