Changed the drivers HAL implementations to the ones using NRF5 SDK, moved nrf_drv_config.h to MCU specific targets, updated lists of device features, so non-compiling stuff is excluded from build.

pull/2234/head
Głąbek, Andrzej 2016-06-15 20:56:03 +01:00
parent 96a175973d
commit df6bd2f66c
63 changed files with 13281 additions and 4357 deletions

View File

@ -1388,7 +1388,7 @@
"supported_form_factors": ["ARDUINO"],
"inherits": ["MCU_NRF51_32K"],
"progen": {"target": "nrf51-dk"},
"device_has": ["ANALOGIN", "ERROR_PATTERN", "I2C", "INTERRUPTIN", "PORTIN", "PORTINOUT", "PORTOUT", "PWMOUT", "SERIAL", "SLEEP", "SPI", "SPISLAVE"]
"device_has": ["ERROR_PATTERN", "I2C", "INTERRUPTIN", "PORTIN", "PORTINOUT", "PORTOUT", "SERIAL", "RTC", "SERIAL_ASYNCH", "SLEEP"]
},
"NRF51_DK_BOOT": {
"supported_form_factors": ["ARDUINO"],
@ -1764,6 +1764,6 @@
"supported_form_factors": ["ARDUINO"],
"inherits": ["MCU_NRF52"],
"progen": {"target": "nrf52-dk"},
"device_has": ["ANALOGIN", "ERROR_PATTERN", "I2C", "INTERRUPTIN", "PORTIN", "PORTINOUT", "PORTOUT", "PWMOUT", "SERIAL", "SLEEP", "SPI", "SPISLAVE"]
"device_has": ["ERROR_PATTERN", "I2C", "INTERRUPTIN", "PORTIN", "PORTINOUT", "PORTOUT", "RTC", "SERIAL", "SERIAL_ASYNCH", "SLEEP"]
}
}

View File

@ -18,6 +18,8 @@
#include "cmsis.h"
#include "pinmap.h"
#ifdef DEVICE_ANALOGIN
#define ANALOGIN_MEDIAN_FILTER 1
#define ADC_10BIT_RANGE 0x3FF
#define ADC_RANGE ADC_10BIT_RANGE
@ -79,3 +81,5 @@ float analogin_read(analogin_t *obj)
uint16_t value = analogin_read_u16(obj);
return (float)value * (1.0f / (float)ADC_RANGE);
}
#endif // DEVICE_ANALOGIN

View File

@ -1,58 +0,0 @@
/* mbed Microcontroller Library
* Copyright (c) 2006-2013 ARM Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "mbed_assert.h"
#include "gpio_api.h"
#include "pinmap.h"
void gpio_init(gpio_t *obj, PinName pin)
{
obj->pin = pin;
if (pin == (PinName)NC) {
return;
}
obj->mask = (1ul << pin);
obj->reg_set = &NRF_GPIO->OUTSET;
obj->reg_clr = &NRF_GPIO->OUTCLR;
obj->reg_in = &NRF_GPIO->IN;
obj->reg_dir = &NRF_GPIO->DIR;
}
void gpio_mode(gpio_t *obj, PinMode mode)
{
pin_mode(obj->pin, mode);
}
void gpio_dir(gpio_t *obj, PinDirection direction)
{
MBED_ASSERT(obj->pin != (PinName)NC);
switch (direction) {
case PIN_INPUT:
NRF_GPIO->PIN_CNF[obj->pin] = (GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos)
| (GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos)
| (GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos)
| (GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos);
break;
case PIN_OUTPUT:
NRF_GPIO->PIN_CNF[obj->pin] = (GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos)
| (GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos)
| (GPIO_PIN_CNF_PULL_Disabled << GPIO_PIN_CNF_PULL_Pos)
| (GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos)
| (GPIO_PIN_CNF_DIR_Output << GPIO_PIN_CNF_DIR_Pos);
break;
}
}

View File

@ -1,127 +0,0 @@
/* mbed Microcontroller Library
* Copyright (c) 2013 Nordic Semiconductor
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stddef.h>
#include "cmsis.h"
#include "gpio_irq_api.h"
#include "mbed_error.h"
#define CHANNEL_NUM 31
static uint32_t channel_ids[CHANNEL_NUM] = {0}; //each pin will be given an id, if id is 0 the pin can be ignored.
static uint8_t channel_enabled[CHANNEL_NUM] = {0};
static uint32_t portRISE = 0;
static uint32_t portFALL = 0;
static gpio_irq_handler irq_handler;
#ifdef __cplusplus
extern "C" {
#endif
void GPIOTE_IRQHandler(void)
{
volatile uint32_t newVal = NRF_GPIO->IN;
if ((NRF_GPIOTE->EVENTS_PORT != 0) && ((NRF_GPIOTE->INTENSET & GPIOTE_INTENSET_PORT_Msk) != 0)) {
NRF_GPIOTE->EVENTS_PORT = 0;
for (uint8_t i = 0; i<31; i++) {
if (channel_ids[i]>0) {
if (channel_enabled[i]) {
if( ((newVal>>i)&1) && ( ( (NRF_GPIO->PIN_CNF[i] >>GPIO_PIN_CNF_SENSE_Pos) & GPIO_PIN_CNF_SENSE_Low) != GPIO_PIN_CNF_SENSE_Low) && ( (portRISE>>i)&1) ){
irq_handler(channel_ids[i], IRQ_RISE);
} else if ((((newVal >> i) & 1) == 0) &&
(((NRF_GPIO->PIN_CNF[i] >> GPIO_PIN_CNF_SENSE_Pos) & GPIO_PIN_CNF_SENSE_Low) == GPIO_PIN_CNF_SENSE_Low) &&
((portFALL >> i) & 1)) {
irq_handler(channel_ids[i], IRQ_FALL);
}
}
if (NRF_GPIO->PIN_CNF[i] & GPIO_PIN_CNF_SENSE_Msk) {
NRF_GPIO->PIN_CNF[i] &= ~(GPIO_PIN_CNF_SENSE_Msk);
if (newVal >> i & 1) {
NRF_GPIO->PIN_CNF[i] |= (GPIO_PIN_CNF_SENSE_Low << GPIO_PIN_CNF_SENSE_Pos);
} else {
NRF_GPIO->PIN_CNF[i] |= (GPIO_PIN_CNF_SENSE_High << GPIO_PIN_CNF_SENSE_Pos);
}
}
}
}
}
}
#ifdef __cplusplus
}
#endif
int gpio_irq_init(gpio_irq_t *obj, PinName pin, gpio_irq_handler handler, uint32_t id)
{
if (pin == NC) {
return -1;
}
irq_handler = handler;
obj->ch = pin;
NRF_GPIOTE->EVENTS_PORT = 0;
channel_ids[pin] = id;
channel_enabled[pin] = 1;
NRF_GPIOTE->INTENSET = GPIOTE_INTENSET_PORT_Set << GPIOTE_INTENSET_PORT_Pos;
NVIC_SetPriority(GPIOTE_IRQn, 3);
NVIC_EnableIRQ (GPIOTE_IRQn);
return 0;
}
void gpio_irq_free(gpio_irq_t *obj)
{
channel_ids[obj->ch] = 0;
}
void gpio_irq_set(gpio_irq_t *obj, gpio_irq_event event, uint32_t enable)
{
NRF_GPIO->PIN_CNF[obj->ch] &= ~(GPIO_PIN_CNF_SENSE_Msk);
if (enable) {
if (event == IRQ_RISE) {
portRISE |= (1 << obj->ch);
} else if (event == IRQ_FALL) {
portFALL |= (1 << obj->ch);
}
} else {
if (event == IRQ_RISE) {
portRISE &= ~(1 << obj->ch);
} else if (event == IRQ_FALL) {
portFALL &= ~(1 << obj->ch);
}
}
if (((portRISE >> obj->ch) & 1) || ((portFALL >> obj->ch) & 1)) {
if ((NRF_GPIO->IN >> obj->ch) & 1) {
NRF_GPIO->PIN_CNF[obj->ch] |= (GPIO_PIN_CNF_SENSE_Low << GPIO_PIN_CNF_SENSE_Pos); // | (GPIO_PIN_CNF_PULL_Disabled << GPIO_PIN_CNF_PULL_Pos);
} else {
NRF_GPIO->PIN_CNF[obj->ch] |= (GPIO_PIN_CNF_SENSE_High << GPIO_PIN_CNF_SENSE_Pos); //| (GPIO_PIN_CNF_PULL_Disabled << GPIO_PIN_CNF_PULL_Pos);
}
}
}
void gpio_irq_enable(gpio_irq_t *obj)
{
channel_enabled[obj->ch] = 1;
}
void gpio_irq_disable(gpio_irq_t *obj)
{
channel_enabled[obj->ch] = 0;
}

View File

@ -1,56 +0,0 @@
/* mbed Microcontroller Library
* Copyright (c) 2006-2013 ARM Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef MBED_GPIO_OBJECT_H
#define MBED_GPIO_OBJECT_H
#include "mbed_assert.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
PinName pin;
uint32_t mask;
__IO uint32_t *reg_dir;
__IO uint32_t *reg_set;
__IO uint32_t *reg_clr;
__I uint32_t *reg_in;
} gpio_t;
static inline void gpio_write(gpio_t *obj, int value) {
MBED_ASSERT(obj->pin != (PinName)NC);
if (value)
*obj->reg_set = obj->mask;
else
*obj->reg_clr = obj->mask;
}
static inline int gpio_read(gpio_t *obj) {
MBED_ASSERT(obj->pin != (PinName)NC);
return ((*obj->reg_in & obj->mask) ? 1 : 0);
}
static inline int gpio_is_connected(const gpio_t *obj) {
return obj->pin != (PinName)NC;
}
#ifdef __cplusplus
}
#endif
#endif

View File

@ -1,309 +0,0 @@
/* mbed Microcontroller Library
* Copyright (c) 2013 Nordic Semiconductor
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "mbed_assert.h"
#include "i2c_api.h"
#include "cmsis.h"
#include "pinmap.h"
#include "twi_master.h"
#include "mbed_error.h"
// nRF51822's I2C_0 and SPI_0 (I2C_1, SPI_1 and SPIS1) share the same address.
// They can't be used at the same time. So we use two global variable to track the usage.
// See nRF51822 address information at nRF51822_PS v2.0.pdf - Table 15 Peripheral instance reference
volatile i2c_spi_peripheral_t i2c0_spi0_peripheral = {0, 0, 0, 0};
volatile i2c_spi_peripheral_t i2c1_spi1_peripheral = {0, 0, 0, 0};
void i2c_interface_enable(i2c_t *obj)
{
obj->i2c->ENABLE = (TWI_ENABLE_ENABLE_Enabled << TWI_ENABLE_ENABLE_Pos);
}
void twi_master_init(i2c_t *obj, PinName sda, PinName scl, int frequency)
{
NRF_GPIO->PIN_CNF[scl] = ((GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos) |
(GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos) |
(GPIO_PIN_CNF_PULL_Disabled << GPIO_PIN_CNF_PULL_Pos) |
(GPIO_PIN_CNF_DRIVE_S0D1 << GPIO_PIN_CNF_DRIVE_Pos) |
(GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos));
NRF_GPIO->PIN_CNF[sda] = ((GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos) |
(GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos) |
(GPIO_PIN_CNF_PULL_Disabled << GPIO_PIN_CNF_PULL_Pos) |
(GPIO_PIN_CNF_DRIVE_S0D1 << GPIO_PIN_CNF_DRIVE_Pos) |
(GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos));
obj->i2c->PSELSCL = scl;
obj->i2c->PSELSDA = sda;
// set default frequency at 100k
i2c_frequency(obj, frequency);
i2c_interface_enable(obj);
}
void i2c_init(i2c_t *obj, PinName sda, PinName scl)
{
NRF_TWI_Type *i2c = NULL;
if (i2c0_spi0_peripheral.usage == I2C_SPI_PERIPHERAL_FOR_I2C &&
i2c0_spi0_peripheral.sda_mosi == (uint8_t)sda &&
i2c0_spi0_peripheral.scl_miso == (uint8_t)scl) {
// The I2C with the same pins is already initialized
i2c = (NRF_TWI_Type *)I2C_0;
obj->peripheral = 0x1;
} else if (i2c1_spi1_peripheral.usage == I2C_SPI_PERIPHERAL_FOR_I2C &&
i2c1_spi1_peripheral.sda_mosi == (uint8_t)sda &&
i2c1_spi1_peripheral.scl_miso == (uint8_t)scl) {
// The I2C with the same pins is already initialized
i2c = (NRF_TWI_Type *)I2C_1;
obj->peripheral = 0x2;
} else if (i2c0_spi0_peripheral.usage == 0) {
i2c0_spi0_peripheral.usage = I2C_SPI_PERIPHERAL_FOR_I2C;
i2c0_spi0_peripheral.sda_mosi = (uint8_t)sda;
i2c0_spi0_peripheral.scl_miso = (uint8_t)scl;
i2c = (NRF_TWI_Type *)I2C_0;
obj->peripheral = 0x1;
} else if (i2c1_spi1_peripheral.usage == 0) {
i2c1_spi1_peripheral.usage = I2C_SPI_PERIPHERAL_FOR_I2C;
i2c1_spi1_peripheral.sda_mosi = (uint8_t)sda;
i2c1_spi1_peripheral.scl_miso = (uint8_t)scl;
i2c = (NRF_TWI_Type *)I2C_1;
obj->peripheral = 0x2;
} else {
// No available peripheral
error("No available I2C");
}
twi_master_init_and_clear(i2c);
obj->i2c = i2c;
obj->scl = scl;
obj->sda = sda;
obj->i2c->EVENTS_ERROR = 0;
obj->i2c->ENABLE = TWI_ENABLE_ENABLE_Disabled << TWI_ENABLE_ENABLE_Pos;
obj->i2c->POWER = 0;
for (int i = 0; i<100; i++) {
}
obj->i2c->POWER = 1;
twi_master_init(obj, sda, scl, 100000);
}
void i2c_reset(i2c_t *obj)
{
obj->i2c->EVENTS_ERROR = 0;
obj->i2c->ENABLE = TWI_ENABLE_ENABLE_Disabled << TWI_ENABLE_ENABLE_Pos;
obj->i2c->POWER = 0;
for (int i = 0; i<100; i++) {
}
obj->i2c->POWER = 1;
twi_master_init(obj, obj->sda, obj->scl, obj->freq);
}
int i2c_start(i2c_t *obj)
{
int status = 0;
i2c_reset(obj);
obj->address_set = 0;
return status;
}
int i2c_stop(i2c_t *obj)
{
int timeOut = 100000;
obj->i2c->EVENTS_STOPPED = 0;
// write the stop bit
obj->i2c->TASKS_STOP = 1;
while (!obj->i2c->EVENTS_STOPPED) {
timeOut--;
if (timeOut<0) {
return 1;
}
}
obj->address_set = 0;
i2c_reset(obj);
return 0;
}
int i2c_do_write(i2c_t *obj, int value)
{
int timeOut = 100000;
obj->i2c->TXD = value;
while (!obj->i2c->EVENTS_TXDSENT) {
timeOut--;
if (timeOut<0) {
return 1;
}
}
obj->i2c->EVENTS_TXDSENT = 0;
return 0;
}
int i2c_do_read(i2c_t *obj, char *data, int last)
{
int timeOut = 100000;
if (last) {
// To trigger stop task when a byte is received,
// must be set before resume task.
obj->i2c->SHORTS = 2;
}
obj->i2c->TASKS_RESUME = 1;
while (!obj->i2c->EVENTS_RXDREADY) {
timeOut--;
if (timeOut<0) {
return 1;
}
}
obj->i2c->EVENTS_RXDREADY = 0;
*data = obj->i2c->RXD;
return 0;
}
void i2c_frequency(i2c_t *obj, int hz)
{
if (hz<250000) {
obj->freq = 100000;
obj->i2c->FREQUENCY = (TWI_FREQUENCY_FREQUENCY_K100 << TWI_FREQUENCY_FREQUENCY_Pos);
} else if (hz<400000) {
obj->freq = 250000;
obj->i2c->FREQUENCY = (TWI_FREQUENCY_FREQUENCY_K250 << TWI_FREQUENCY_FREQUENCY_Pos);
} else {
obj->freq = 400000;
obj->i2c->FREQUENCY = (TWI_FREQUENCY_FREQUENCY_K400 << TWI_FREQUENCY_FREQUENCY_Pos);
}
}
int checkError(i2c_t *obj)
{
if (obj->i2c->EVENTS_ERROR == 1) {
if (obj->i2c->ERRORSRC & TWI_ERRORSRC_ANACK_Msk) {
obj->i2c->EVENTS_ERROR = 0;
obj->i2c->TASKS_STOP = 1;
return I2C_ERROR_BUS_BUSY;
}
obj->i2c->EVENTS_ERROR = 0;
obj->i2c->TASKS_STOP = 1;
return I2C_ERROR_NO_SLAVE;
}
return 0;
}
int i2c_read(i2c_t *obj, int address, char *data, int length, int stop)
{
int status, count, errorResult;
obj->i2c->ADDRESS = (address >> 1);
obj->i2c->SHORTS = 1; // to trigger suspend task when a byte is received
obj->i2c->EVENTS_RXDREADY = 0;
obj->i2c->TASKS_STARTRX = 1;
// Read in all except last byte
for (count = 0; count < (length - 1); count++) {
status = i2c_do_read(obj, &data[count], 0);
if (status) {
errorResult = checkError(obj);
i2c_reset(obj);
if (errorResult<0) {
return errorResult;
}
return count;
}
}
// read in last byte
status = i2c_do_read(obj, &data[length - 1], 1);
if (status) {
i2c_reset(obj);
return length - 1;
}
// If not repeated start, send stop.
if (stop) {
while (!obj->i2c->EVENTS_STOPPED) {
}
obj->i2c->EVENTS_STOPPED = 0;
}
return length;
}
int i2c_write(i2c_t *obj, int address, const char *data, int length, int stop)
{
int status, errorResult;
obj->i2c->ADDRESS = (address >> 1);
obj->i2c->SHORTS = 0;
obj->i2c->TASKS_STARTTX = 1;
for (int i = 0; i<length; i++) {
status = i2c_do_write(obj, data[i]);
if (status) {
i2c_reset(obj);
errorResult = checkError(obj);
if (errorResult<0) {
return errorResult;
}
return i;
}
}
// If not repeated start, send stop.
if (stop) {
if (i2c_stop(obj)) {
return I2C_ERROR_NO_SLAVE;
}
}
return length;
}
int i2c_byte_read(i2c_t *obj, int last)
{
char data;
int status;
status = i2c_do_read(obj, &data, last);
if (status) {
i2c_reset(obj);
}
return data;
}
int i2c_byte_write(i2c_t *obj, int data)
{
int status = 0;
if (!obj->address_set) {
obj->address_set = 1;
obj->i2c->ADDRESS = (data >> 1);
if (data & 1) {
obj->i2c->EVENTS_RXDREADY = 0;
obj->i2c->SHORTS = 1;
obj->i2c->TASKS_STARTRX = 1;
} else {
obj->i2c->SHORTS = 0;
obj->i2c->TASKS_STARTTX = 1;
}
} else {
status = i2c_do_write(obj, data);
if (status) {
i2c_reset(obj);
}
}
return (1 - status);
}

View File

@ -1,86 +0,0 @@
/* mbed Microcontroller Library
* Copyright (c) 2013 Nordic Semiconductor
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef MBED_OBJECTS_H
#define MBED_OBJECTS_H
#include "cmsis.h"
#include "PortNames.h"
#include "PeripheralNames.h"
#include "PinNames.h"
#ifdef __cplusplus
extern "C" {
#endif
#define I2C_SPI_PERIPHERAL_FOR_I2C 1
#define I2C_SPI_PERIPHERAL_FOR_SPI 2
typedef struct {
uint8_t usage; // I2C: 1, SPI: 2
uint8_t sda_mosi;
uint8_t scl_miso;
uint8_t sclk;
} i2c_spi_peripheral_t;
struct serial_s {
NRF_UART_Type *uart;
int index;
};
struct spi_s {
NRF_SPI_Type *spi;
NRF_SPIS_Type *spis;
uint8_t peripheral;
};
struct port_s {
__IO uint32_t *reg_cnf;
__IO uint32_t *reg_out;
__I uint32_t *reg_in;
PortName port;
uint32_t mask;
};
struct pwmout_s {
PWMName pwm;
PinName pin;
};
struct i2c_s {
NRF_TWI_Type *i2c;
PinName sda;
PinName scl;
int freq;
uint8_t address_set;
uint8_t peripheral;
};
struct analogin_s {
ADCName adc;
uint8_t adc_pin;
};
struct gpio_irq_s {
uint32_t ch;
};
#include "gpio_object.h"
#ifdef __cplusplus
}
#endif
#endif

View File

@ -1,84 +0,0 @@
/* mbed Microcontroller Library
* Copyright (c) 2013 Nordic Semiconductor
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "port_api.h"
#include "pinmap.h"
#include "gpio_api.h"
PinName port_pin(PortName port, int pin_n)
{
return (PinName)(pin_n);
}
void port_init(port_t *obj, PortName port, int mask, PinDirection dir)
{
obj->port = port;
obj->mask = mask;
obj->reg_out = &NRF_GPIO->OUT;
obj->reg_in = &NRF_GPIO->IN;
obj->reg_cnf = NRF_GPIO->PIN_CNF;
port_dir(obj, dir);
}
void port_mode(port_t *obj, PinMode mode)
{
uint32_t i;
// The mode is set per pin: reuse pinmap logic
for (i = 0; i<31; i++) {
if (obj->mask & (1 << i)) {
pin_mode(port_pin(obj->port, i), mode);
}
}
}
void port_dir(port_t *obj, PinDirection dir)
{
int i;
switch (dir) {
case PIN_INPUT:
for (i = 0; i<31; i++) {
if (obj->mask & (1 << i)) {
obj->reg_cnf[i] = (GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos)
| (GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos)
| (GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos)
| (GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos);
}
}
break;
case PIN_OUTPUT:
for (i = 0; i<31; i++) {
if (obj->mask & (1 << i)) {
obj->reg_cnf[i] = (GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos)
| (GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos)
| (GPIO_PIN_CNF_PULL_Disabled << GPIO_PIN_CNF_PULL_Pos)
| (GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos)
| (GPIO_PIN_CNF_DIR_Output << GPIO_PIN_CNF_DIR_Pos);
}
}
break;
}
}
void port_write(port_t *obj, int value)
{
*obj->reg_out = value;
}
int port_read(port_t *obj)
{
return (*obj->reg_in);
}

View File

@ -14,305 +14,160 @@
* limitations under the License.
*/
#include "mbed_assert.h"
#include "mbed_error.h"
#include "pwmout_api.h"
#include "cmsis.h"
#include "pinmap.h"
#include "mbed_error.h"
#define NO_PWMS 3
#define TIMER_PRECISION 4 //4us ticks
#define TIMER_PRESCALER 6 //4us ticks = 16Mhz/(2**6)
static const PinMap PinMap_PWM[] = {
{p0, PWM_1, 1},
{p1, PWM_1, 1},
{p2, PWM_1, 1},
{p3, PWM_1, 1},
{p4, PWM_1, 1},
{p5, PWM_1, 1},
{p6, PWM_1, 1},
{p7, PWM_1, 1},
{p8, PWM_1, 1},
{p9, PWM_1, 1},
{p10, PWM_1, 1},
{p11, PWM_1, 1},
{p12, PWM_1, 1},
{p13, PWM_1, 1},
{p14, PWM_1, 1},
{p15, PWM_1, 1},
{p16, PWM_1, 1},
{p17, PWM_1, 1},
{p18, PWM_1, 1},
{p19, PWM_1, 1},
{p20, PWM_1, 1},
{p21, PWM_1, 1},
{p22, PWM_1, 1},
{p23, PWM_1, 1},
{p24, PWM_1, 1},
{p25, PWM_1, 1},
{p28, PWM_1, 1},
{p29, PWM_1, 1},
{p30, PWM_1, 1},
{NC, NC, 0}
};
#if DEVICE_PWMOUT
static NRF_TIMER_Type *Timers[1] = {
NRF_TIMER2
};
#include "nrf.h"
#include "nrf_drv_timer.h"
#include "app_pwm.h"
uint16_t PERIOD = 20000 / TIMER_PRECISION; //20ms
uint8_t PWM_taken[NO_PWMS] = {0, 0, 0};
uint16_t PULSE_WIDTH[NO_PWMS] = {1, 1, 1}; //set to 1 instead of 0
uint16_t ACTUAL_PULSE[NO_PWMS] = {0, 0, 0};
#define PWM_INSTANCE_COUNT 2
#define PWM_CHANNELS_PER_INSTANCE 2
#define PWM_DEFAULT_PERIOD_US 20000
#define PWM_PERIOD_MIN 3
/** @brief Function for handling timer 2 peripheral interrupts.
*/
#ifdef __cplusplus
extern "C" {
#endif
void TIMER2_IRQHandler(void)
typedef struct
{
NRF_TIMER2->EVENTS_COMPARE[3] = 0;
NRF_TIMER2->CC[3] = PERIOD;
const app_pwm_t * const instance;
NRF_TIMER_Type * const timer_reg;
uint8_t channels_allocated;
uint32_t pins[PWM_CHANNELS_PER_INSTANCE];
uint16_t period_us;
uint16_t duty_ticks[PWM_CHANNELS_PER_INSTANCE];
} pwm_t;
if (PWM_taken[0]) {
NRF_TIMER2->CC[0] = PULSE_WIDTH[0];
}
if (PWM_taken[1]) {
NRF_TIMER2->CC[1] = PULSE_WIDTH[1];
}
if (PWM_taken[2]) {
NRF_TIMER2->CC[2] = PULSE_WIDTH[2];
}
NRF_TIMER2->TASKS_START = 1;
APP_PWM_INSTANCE(m_pwm_instance_0, 1); //PWM0: Timer 1
APP_PWM_INSTANCE(m_pwm_instance_1, 2); //PWM1: Timer 2
static pwm_t m_pwm[] = {
{.instance = &m_pwm_instance_0, .timer_reg = NRF_TIMER1, .channels_allocated = 0},
{.instance = &m_pwm_instance_1, .timer_reg = NRF_TIMER2, .channels_allocated = 0}
};
static inline void pwm_ticks_set(pwm_t* pwm, uint8_t channel, uint16_t ticks)
{
pwm->duty_ticks[channel] = ticks;
while (app_pwm_channel_duty_ticks_set(pwm->instance, channel, ticks) != NRF_SUCCESS);
}
#ifdef __cplusplus
}
#endif
/** @brief Function for initializing the Timer peripherals.
*/
void timer_init(uint8_t pwmChoice)
static void pwm_reinit(pwm_t * pwm)
{
NRF_TIMER_Type *timer = Timers[0];
timer->TASKS_STOP = 0;
if (pwmChoice == 0) {
timer->POWER = 0;
timer->POWER = 1;
timer->MODE = TIMER_MODE_MODE_Timer;
timer->BITMODE = TIMER_BITMODE_BITMODE_16Bit << TIMER_BITMODE_BITMODE_Pos;
timer->PRESCALER = TIMER_PRESCALER;
timer->CC[3] = PERIOD;
}
timer->CC[pwmChoice] = PULSE_WIDTH[pwmChoice];
//high priority application interrupt
NVIC_SetPriority(TIMER2_IRQn, 1);
NVIC_EnableIRQ(TIMER2_IRQn);
timer->TASKS_START = 0x01;
}
static void timer_free()
{
NRF_TIMER_Type *timer = Timers[0];
for(uint8_t i = 1; i < NO_PWMS; i++){
if(PWM_taken[i]){
break;
}
if((i == NO_PWMS - 1) && (!PWM_taken[i]))
timer->TASKS_STOP = 0x01;
}
}
/** @brief Function for initializing the GPIO Tasks/Events peripheral.
*/
void gpiote_init(PinName pin, uint8_t channel_number)
{
// Connect GPIO input buffers and configure PWM_OUTPUT_PIN_NUMBER as an output.
NRF_GPIO->PIN_CNF[pin] = (GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos)
| (GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos)
| (GPIO_PIN_CNF_PULL_Disabled << GPIO_PIN_CNF_PULL_Pos)
| (GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos)
| (GPIO_PIN_CNF_DIR_Output << GPIO_PIN_CNF_DIR_Pos);
NRF_GPIO->OUTCLR = (1UL << pin);
// Configure GPIOTE channel 0 to toggle the PWM pin state
// @note Only one GPIOTE task can be connected to an output pin.
/* Configure channel to Pin31, not connected to the pin, and configure as a tasks that will set it to proper level */
NRF_GPIOTE->CONFIG[channel_number] = (GPIOTE_CONFIG_MODE_Task << GPIOTE_CONFIG_MODE_Pos) |
(31UL << GPIOTE_CONFIG_PSEL_Pos) |
(GPIOTE_CONFIG_POLARITY_HiToLo << GPIOTE_CONFIG_POLARITY_Pos);
/* Three NOPs are required to make sure configuration is written before setting tasks or getting events */
__NOP();
__NOP();
__NOP();
/* Launch the task to take the GPIOTE channel output to the desired level */
NRF_GPIOTE->TASKS_OUT[channel_number] = 1;
/* Finally configure the channel as the caller expects. If OUTINIT works, the channel is configured properly.
If it does not, the channel output inheritance sets the proper level. */
NRF_GPIOTE->CONFIG[channel_number] = (GPIOTE_CONFIG_MODE_Task << GPIOTE_CONFIG_MODE_Pos) |
((uint32_t)pin << GPIOTE_CONFIG_PSEL_Pos) |
((uint32_t)GPIOTE_CONFIG_POLARITY_Toggle << GPIOTE_CONFIG_POLARITY_Pos) |
((uint32_t)GPIOTE_CONFIG_OUTINIT_Low << GPIOTE_CONFIG_OUTINIT_Pos); // ((uint32_t)GPIOTE_CONFIG_OUTINIT_High <<
// GPIOTE_CONFIG_OUTINIT_Pos);//
/* Three NOPs are required to make sure configuration is written before setting tasks or getting events */
__NOP();
__NOP();
__NOP();
}
static void gpiote_free(PinName pin,uint8_t channel_number)
{
NRF_GPIOTE->TASKS_OUT[channel_number] = 0;
NRF_GPIOTE->CONFIG[channel_number] = 0;
NRF_GPIO->PIN_CNF[pin] = (GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos);
}
/** @brief Function for initializing the Programmable Peripheral Interconnect peripheral.
*/
static void ppi_init(uint8_t pwm)
{
//using ppi channels 0-7 (only 0-7 are available)
uint8_t channel_number = 2 * pwm;
NRF_TIMER_Type *timer = Timers[0];
// Configure PPI channel 0 to toggle ADVERTISING_LED_PIN_NO on every TIMER1 COMPARE[0] match
NRF_PPI->CH[channel_number].TEP = (uint32_t)&NRF_GPIOTE->TASKS_OUT[pwm];
NRF_PPI->CH[channel_number + 1].TEP = (uint32_t)&NRF_GPIOTE->TASKS_OUT[pwm];
NRF_PPI->CH[channel_number].EEP = (uint32_t)&timer->EVENTS_COMPARE[pwm];
NRF_PPI->CH[channel_number + 1].EEP = (uint32_t)&timer->EVENTS_COMPARE[3];
// Enable PPI channels.
NRF_PPI->CHEN |= (1 << channel_number) |
(1 << (channel_number + 1));
}
static void ppi_free(uint8_t pwm)
{
//using ppi channels 0-7 (only 0-7 are available)
uint8_t channel_number = 2*pwm;
// Disable PPI channels.
NRF_PPI->CHEN &= (~(1 << channel_number))
& (~(1 << (channel_number+1)));
}
void setModulation(pwmout_t *obj, uint8_t toggle, uint8_t high)
{
if (high) {
NRF_GPIOTE->CONFIG[obj->pwm] |= ((uint32_t)GPIOTE_CONFIG_OUTINIT_High << GPIOTE_CONFIG_OUTINIT_Pos);
if (toggle) {
NRF_GPIOTE->CONFIG[obj->pwm] |= (GPIOTE_CONFIG_MODE_Task << GPIOTE_CONFIG_MODE_Pos) |
((uint32_t)GPIOTE_CONFIG_POLARITY_Toggle << GPIOTE_CONFIG_POLARITY_Pos);
} else {
NRF_GPIOTE->CONFIG[obj->pwm] &= ~((uint32_t)GPIOTE_CONFIG_POLARITY_Toggle << GPIOTE_CONFIG_POLARITY_Pos);
NRF_GPIOTE->CONFIG[obj->pwm] |= ((uint32_t)GPIOTE_CONFIG_POLARITY_LoToHi << GPIOTE_CONFIG_POLARITY_Pos);
}
} else {
NRF_GPIOTE->CONFIG[obj->pwm] &= ~((uint32_t)GPIOTE_CONFIG_OUTINIT_High << GPIOTE_CONFIG_OUTINIT_Pos);
if (toggle) {
NRF_GPIOTE->CONFIG[obj->pwm] |= (GPIOTE_CONFIG_MODE_Task << GPIOTE_CONFIG_MODE_Pos) |
((uint32_t)GPIOTE_CONFIG_POLARITY_Toggle << GPIOTE_CONFIG_POLARITY_Pos);
} else {
NRF_GPIOTE->CONFIG[obj->pwm] &= ~((uint32_t)GPIOTE_CONFIG_POLARITY_Toggle << GPIOTE_CONFIG_POLARITY_Pos);
NRF_GPIOTE->CONFIG[obj->pwm] |= ((uint32_t)GPIOTE_CONFIG_POLARITY_HiToLo << GPIOTE_CONFIG_POLARITY_Pos);
app_pwm_uninit(pwm->instance);
app_pwm_config_t pwm_cfg = APP_PWM_DEFAULT_CONFIG_2CH(pwm->period_us,
pwm->pins[0],
pwm->pins[1]);
app_pwm_init(pwm->instance, &pwm_cfg, NULL);
app_pwm_enable(pwm->instance);
for (uint8_t channel = 0; channel < PWM_CHANNELS_PER_INSTANCE; ++channel) {
if ((pwm->channels_allocated & (1 << channel)) && (pwm->pins[channel] != APP_PWM_NOPIN)) {
app_pwm_channel_duty_ticks_set(pwm->instance, channel, pwm->duty_ticks[channel]);
}
}
}
void pwmout_init(pwmout_t *obj, PinName pin)
{
// determine the channel
uint8_t pwmOutSuccess = 0;
PWMName pwm = (PWMName)pinmap_peripheral(pin, PinMap_PWM);
if (pin == NC) {
error("PwmOut init failed. Invalid pin name.");
}
// Check if pin is already initialized and find the next free channel.
uint8_t free_instance = 0xFF;
uint8_t free_channel = 0xFF;
MBED_ASSERT(pwm != (PWMName)NC);
if (PWM_taken[(uint8_t)pwm]) {
for (uint8_t i = 1; !pwmOutSuccess && (i<NO_PWMS); i++) {
if (!PWM_taken[i]) {
pwm = (PWMName)i;
PWM_taken[i] = 1;
pwmOutSuccess = 1;
for (uint8_t inst = 0; inst < PWM_INSTANCE_COUNT; ++inst) {
if (m_pwm[inst].channels_allocated) {
for (uint8_t channel = 0; channel < PWM_CHANNELS_PER_INSTANCE; ++channel) {
if (m_pwm[inst].channels_allocated & (1 << channel)) {
if (m_pwm[inst].pins[channel] == (uint32_t)pin) {
error("PwmOut init failed. Pin is already in use.");
return;
}
}
else {
if (free_instance == 0xFF) {
free_instance = inst;
free_channel = channel;
}
}
}
}
else {
if (free_instance == 0xFF) {
free_instance = inst;
free_channel = 0;
}
}
} else {
pwmOutSuccess = 1;
PWM_taken[(uint8_t)pwm] = 1;
}
if (!pwmOutSuccess) {
error("PwmOut pin mapping failed. All available PWM channels are in use.");
if (free_instance == 0xFF)
{
error("PwmOut init failed. All available PWM channels are in use.");
return;
}
obj->pwm = pwm;
obj->pin = pin;
gpiote_init(pin, (uint8_t)pwm);
ppi_init((uint8_t)pwm);
if (pwm == 0) {
NRF_POWER->TASKS_CONSTLAT = 1;
// Init / reinit PWM instance.
m_pwm[free_instance].pins[free_channel] = (uint32_t) pin;
m_pwm[free_instance].duty_ticks[free_channel] = 0;
if (!m_pwm[free_instance].channels_allocated) {
m_pwm[free_instance].period_us = PWM_DEFAULT_PERIOD_US;
for (uint8_t channel = 1; channel < PWM_CHANNELS_PER_INSTANCE; ++channel) {
m_pwm[free_instance].pins[channel] = APP_PWM_NOPIN;
m_pwm[free_instance].duty_ticks[channel] = 0;
}
app_pwm_config_t pwm_cfg = APP_PWM_DEFAULT_CONFIG_1CH(PWM_DEFAULT_PERIOD_US, pin);
app_pwm_init(m_pwm[free_instance].instance, &pwm_cfg, NULL);
app_pwm_enable(m_pwm[free_instance].instance);
}
else {
pwm_reinit(&m_pwm[free_instance]);
}
m_pwm[free_instance].channels_allocated |= (1 << free_channel);
timer_init((uint8_t)pwm);
//default to 20ms: standard for servos, and fine for e.g. brightness control
pwmout_period_ms(obj, 20);
pwmout_write (obj, 0);
obj->pin = pin;
obj->pwm_struct = (void *) &m_pwm[free_instance];
obj->pwm_channel = free_channel;
}
void pwmout_free(pwmout_t* obj) {
void pwmout_free(pwmout_t *obj)
{
MBED_ASSERT(obj->pwm != (PWMName)NC);
pwmout_write(obj, 0);
PWM_taken[obj->pwm] = 0;
timer_free();
ppi_free(obj->pwm);
gpiote_free(obj->pin,obj->pwm);
MBED_ASSERT(obj->pwm_channel < PWM_CHANNELS_PER_INSTANCE);
pwm_t * pwm = (pwm_t *) obj->pwm_struct;
pwm->channels_allocated &= ~(1 << obj->pwm_channel);
pwm->pins[obj->pwm_channel] = APP_PWM_NOPIN;
pwm->duty_ticks[obj->pwm_channel] = 0;
app_pwm_uninit(pwm->instance);
if (pwm->channels_allocated) {
pwm_reinit(pwm);
}
obj->pwm_struct = NULL;
}
void pwmout_write(pwmout_t *obj, float value)
{
uint16_t oldPulseWidth;
NRF_TIMER2->EVENTS_COMPARE[3] = 0;
NRF_TIMER2->TASKS_STOP = 1;
if (value < 0.0f) {
value = 0.0;
} else if (value > 1.0f) {
value = 1.0;
pwm_t * pwm = (pwm_t *) obj->pwm_struct;
if (value > 1.0f) {
value = 1.0f;
}
oldPulseWidth = ACTUAL_PULSE[obj->pwm];
ACTUAL_PULSE[obj->pwm] = PULSE_WIDTH[obj->pwm] = value * PERIOD;
if (PULSE_WIDTH[obj->pwm] == 0) {
PULSE_WIDTH[obj->pwm] = 1;
setModulation(obj, 0, 0);
} else if (PULSE_WIDTH[obj->pwm] == PERIOD) {
PULSE_WIDTH[obj->pwm] = PERIOD - 1;
setModulation(obj, 0, 1);
} else if ((oldPulseWidth == 0) || (oldPulseWidth == PERIOD)) {
setModulation(obj, 1, oldPulseWidth == PERIOD);
}
NRF_TIMER2->INTENSET = TIMER_INTENSET_COMPARE3_Msk;
NRF_TIMER2->SHORTS = TIMER_SHORTS_COMPARE3_CLEAR_Msk | TIMER_SHORTS_COMPARE3_STOP_Msk;
NRF_TIMER2->TASKS_START = 1;
uint16_t ticks = (uint16_t)((float)app_pwm_cycle_ticks_get(pwm->instance) * value);
}
float pwmout_read(pwmout_t *obj)
{
return ((float)PULSE_WIDTH[obj->pwm] / (float)PERIOD);
pwm_t * pwm = (pwm_t *) obj->pwm_struct;
MBED_ASSERT(pwm != NULL);
return ((float)pwm->duty_ticks[obj->pwm_channel] / (float)app_pwm_cycle_ticks_get(pwm->instance));
}
void pwmout_period(pwmout_t *obj, float seconds)
@ -325,24 +180,15 @@ void pwmout_period_ms(pwmout_t *obj, int ms)
pwmout_period_us(obj, ms * 1000);
}
// Set the PWM period, keeping the duty cycle the same.
void pwmout_period_us(pwmout_t *obj, int us)
{
uint32_t periodInTicks = us / TIMER_PRECISION;
NRF_TIMER2->EVENTS_COMPARE[3] = 0;
NRF_TIMER2->TASKS_STOP = 1;
if (periodInTicks>((1 << 16) - 1)) {
PERIOD = (1 << 16) - 1; //131ms
} else if (periodInTicks<5) {
PERIOD = 5;
} else {
PERIOD = periodInTicks;
pwm_t * pwm = (pwm_t *) obj->pwm_struct;
MBED_ASSERT(pwm != NULL);
if (us < PWM_PERIOD_MIN) {
us = PWM_PERIOD_MIN;
}
NRF_TIMER2->INTENSET = TIMER_INTENSET_COMPARE3_Msk;
NRF_TIMER2->SHORTS = TIMER_SHORTS_COMPARE3_CLEAR_Msk | TIMER_SHORTS_COMPARE3_STOP_Msk;
NRF_TIMER2->TASKS_START = 1;
pwm->period_us = (uint32_t)us;
pwm_reinit(pwm);
}
void pwmout_pulsewidth(pwmout_t *obj, float seconds)
@ -357,24 +203,11 @@ void pwmout_pulsewidth_ms(pwmout_t *obj, int ms)
void pwmout_pulsewidth_us(pwmout_t *obj, int us)
{
uint32_t pulseInTicks = us / TIMER_PRECISION;
uint16_t oldPulseWidth = ACTUAL_PULSE[obj->pwm];
NRF_TIMER2->EVENTS_COMPARE[3] = 0;
NRF_TIMER2->TASKS_STOP = 1;
ACTUAL_PULSE[obj->pwm] = PULSE_WIDTH[obj->pwm] = pulseInTicks;
if (PULSE_WIDTH[obj->pwm] == 0) {
PULSE_WIDTH[obj->pwm] = 1;
setModulation(obj, 0, 0);
} else if (PULSE_WIDTH[obj->pwm] == PERIOD) {
PULSE_WIDTH[obj->pwm] = PERIOD - 1;
setModulation(obj, 0, 1);
} else if ((oldPulseWidth == 0) || (oldPulseWidth == PERIOD)) {
setModulation(obj, 1, oldPulseWidth == PERIOD);
}
NRF_TIMER2->INTENSET = TIMER_INTENSET_COMPARE3_Msk;
NRF_TIMER2->SHORTS = TIMER_SHORTS_COMPARE3_CLEAR_Msk | TIMER_SHORTS_COMPARE3_STOP_Msk;
NRF_TIMER2->TASKS_START = 1;
pwm_t * pwm = (pwm_t *) obj->pwm_struct;
MBED_ASSERT(pwm);
uint16_t ticks = nrf_timer_us_to_ticks((uint32_t)us, nrf_timer_frequency_get(pwm->timer_reg));
pwm_ticks_set(pwm, obj->pwm_channel, ticks);
}
#endif // DEVICE_PWMOUT

View File

@ -44,10 +44,10 @@
* This functionality requires a more complicated interrupt handling and driver
* initialization, hence it is not always desirable to use it.
*/
#define PERIPHERAL_RESOURCE_SHARING_ENABLED 0
#define PERIPHERAL_RESOURCE_SHARING_ENABLED 1
/* CLOCK */
#define CLOCK_ENABLED 0
#define CLOCK_ENABLED 1
#if (CLOCK_ENABLED == 1)
#define CLOCK_CONFIG_XTAL_FREQ NRF_CLOCK_XTALFREQ_Default
@ -56,16 +56,20 @@
#endif
/* GPIOTE */
#define GPIOTE_ENABLED 0
#define GPIOTE_ENABLED 1
#if (GPIOTE_ENABLED == 1)
#define GPIOTE_CONFIG_USE_SWI_EGU false
#define GPIOTE_CONFIG_IRQ_PRIORITY APP_IRQ_PRIORITY_LOW
#define GPIOTE_CONFIG_NUM_OF_LOW_POWER_EVENTS 1
#define GPIOTE_CONFIG_NUM_OF_LOW_POWER_EVENTS 8
#endif
/* TIMER */
#ifdef SOFTDEVICE_PRESENT
#define TIMER0_ENABLED 0
#else
#define TIMER0_ENABLED 1
#endif
#if (TIMER0_ENABLED == 1)
#define TIMER0_CONFIG_FREQUENCY NRF_TIMER_FREQ_16MHz
@ -76,7 +80,7 @@
#define TIMER0_INSTANCE_INDEX 0
#endif
#define TIMER1_ENABLED 0
#define TIMER1_ENABLED 1
#if (TIMER1_ENABLED == 1)
#define TIMER1_CONFIG_FREQUENCY NRF_TIMER_FREQ_16MHz
@ -87,7 +91,7 @@
#define TIMER1_INSTANCE_INDEX (TIMER0_ENABLED)
#endif
#define TIMER2_ENABLED 0
#define TIMER2_ENABLED 1
#if (TIMER2_ENABLED == 1)
#define TIMER2_CONFIG_FREQUENCY NRF_TIMER_FREQ_16MHz
@ -134,7 +138,7 @@
#define RTC0_INSTANCE_INDEX 0
#endif
#define RTC1_ENABLED 0
#define RTC1_ENABLED 1
#if (RTC1_ENABLED == 1)
#define RTC1_CONFIG_FREQUENCY 32768
@ -224,7 +228,7 @@
#define PWM_COUNT (PWM0_ENABLED + PWM1_ENABLED + PWM2_ENABLED)
/* SPI */
#define SPI0_ENABLED 0
#define SPI0_ENABLED 1
#if (SPI0_ENABLED == 1)
#define SPI0_USE_EASY_DMA 0
@ -237,7 +241,7 @@
#define SPI0_INSTANCE_INDEX 0
#endif
#define SPI1_ENABLED 0
#define SPI1_ENABLED 1
#if (SPI1_ENABLED == 1)
#define SPI1_USE_EASY_DMA 0
@ -302,17 +306,17 @@
#define SPIS_COUNT (SPIS0_ENABLED + SPIS1_ENABLED + SPIS2_ENABLED)
/* UART */
#define UART0_ENABLED 0
#define UART0_ENABLED 1
#if (UART0_ENABLED == 1)
#define UART0_CONFIG_HWFC NRF_UART_HWFC_DISABLED
#define UART0_CONFIG_HWFC NRF_UART_HWFC_ENABLED
#define UART0_CONFIG_PARITY NRF_UART_PARITY_EXCLUDED
#define UART0_CONFIG_BAUDRATE NRF_UART_BAUDRATE_115200
#define UART0_CONFIG_PSEL_TXD 0
#define UART0_CONFIG_PSEL_RXD 0
#define UART0_CONFIG_PSEL_CTS 0
#define UART0_CONFIG_PSEL_RTS 0
#define UART0_CONFIG_IRQ_PRIORITY APP_IRQ_PRIORITY_LOW
#define UART0_CONFIG_BAUDRATE NRF_UART_BAUDRATE_9600
#define UART0_CONFIG_PSEL_TXD 9
#define UART0_CONFIG_PSEL_RXD 11
#define UART0_CONFIG_PSEL_CTS 10
#define UART0_CONFIG_PSEL_RTS 8
#define UART0_CONFIG_IRQ_PRIORITY APP_IRQ_PRIORITY_HIGH
#ifdef NRF52
#define UART0_CONFIG_USE_EASY_DMA false
//Compile time flag
@ -321,7 +325,7 @@
#endif //NRF52
#endif
#define TWI0_ENABLED 0
#define TWI0_ENABLED 1
#if (TWI0_ENABLED == 1)
#define TWI0_USE_EASY_DMA 0
@ -334,7 +338,7 @@
#define TWI0_INSTANCE_INDEX 0
#endif
#define TWI1_ENABLED 0
#define TWI1_ENABLED 1
#if (TWI1_ENABLED == 1)
#define TWI1_USE_EASY_DMA 0
@ -397,7 +401,7 @@
#endif
/* ADC */
#define ADC_ENABLED 0
#define ADC_ENABLED 1
#if (ADC_ENABLED == 1)
#define ADC_CONFIG_IRQ_PRIORITY APP_IRQ_PRIORITY_LOW

View File

@ -1,306 +0,0 @@
/* mbed Microcontroller Library
* Copyright (c) 2013 Nordic Semiconductor
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// math.h required for floating point operations for baud rate calculation
//#include <math.h>
#include <string.h>
#include "mbed_assert.h"
#include "serial_api.h"
#include "cmsis.h"
#include "pinmap.h"
/******************************************************************************
* INITIALIZATION
******************************************************************************/
#define UART_NUM 1
static uint32_t serial_irq_ids[UART_NUM] = {0};
static uart_irq_handler irq_handler;
static const int acceptedSpeeds[18][2] = {
{1200, UART_BAUDRATE_BAUDRATE_Baud1200},
{2400, UART_BAUDRATE_BAUDRATE_Baud2400},
{4800, UART_BAUDRATE_BAUDRATE_Baud4800},
{9600, UART_BAUDRATE_BAUDRATE_Baud9600},
{14400, UART_BAUDRATE_BAUDRATE_Baud14400},
{19200, UART_BAUDRATE_BAUDRATE_Baud19200},
{28800, UART_BAUDRATE_BAUDRATE_Baud28800},
{31250, (0x00800000UL) /* 31250 baud */},
{38400, UART_BAUDRATE_BAUDRATE_Baud38400},
{56000, (0x00E51000UL) /* 56000 baud */},
{57600, UART_BAUDRATE_BAUDRATE_Baud57600},
{76800, UART_BAUDRATE_BAUDRATE_Baud76800},
{115200, UART_BAUDRATE_BAUDRATE_Baud115200},
{230400, UART_BAUDRATE_BAUDRATE_Baud230400},
{250000, UART_BAUDRATE_BAUDRATE_Baud250000},
{460800, UART_BAUDRATE_BAUDRATE_Baud460800},
{921600, UART_BAUDRATE_BAUDRATE_Baud921600},
{1000000, UART_BAUDRATE_BAUDRATE_Baud1M}
};
int stdio_uart_inited = 0;
serial_t stdio_uart;
void serial_init(serial_t *obj, PinName tx, PinName rx) {
UARTName uart = UART_0;
obj->uart = (NRF_UART_Type *)uart;
//pin configurations --
NRF_GPIO->OUT |= (1 << tx);
NRF_GPIO->OUT |= (1 << RTS_PIN_NUMBER);
NRF_GPIO->DIR |= (1 << tx); //TX_PIN_NUMBER);
NRF_GPIO->DIR |= (1 << RTS_PIN_NUMBER);
NRF_GPIO->DIR &= ~(1 << rx); //RX_PIN_NUMBER);
NRF_GPIO->DIR &= ~(1 << CTS_PIN_NUMBER);
// set default baud rate and format
serial_baud (obj, 9600);
serial_format(obj, 8, ParityNone, 1);
obj->uart->ENABLE = (UART_ENABLE_ENABLE_Enabled << UART_ENABLE_ENABLE_Pos);
obj->uart->TASKS_STARTTX = 1;
obj->uart->TASKS_STARTRX = 1;
obj->uart->EVENTS_RXDRDY = 0;
// dummy write needed or TXDRDY trails write rather than leads write.
// pins are disconnected so nothing is physically transmitted on the wire
obj->uart->PSELTXD = 0xFFFFFFFF;
obj->uart->EVENTS_TXDRDY = 0;
obj->uart->TXD = 0;
while (obj->uart->EVENTS_TXDRDY != 1);
obj->index = 0;
obj->uart->PSELRTS = RTS_PIN_NUMBER;
obj->uart->PSELTXD = tx; //TX_PIN_NUMBER;
obj->uart->PSELCTS = CTS_PIN_NUMBER;
obj->uart->PSELRXD = rx; //RX_PIN_NUMBER;
// set rx/tx pins in PullUp mode
if (tx != NC) {
pin_mode(tx, PullUp);
}
if (rx != NC) {
pin_mode(rx, PullUp);
}
if (uart == STDIO_UART) {
stdio_uart_inited = 1;
memcpy(&stdio_uart, obj, sizeof(serial_t));
}
}
void serial_free(serial_t *obj)
{
serial_irq_ids[obj->index] = 0;
}
// serial_baud
// set the baud rate, taking in to account the current SystemFrequency
void serial_baud(serial_t *obj, int baudrate)
{
if (baudrate<=1200) {
obj->uart->BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud1200;
return;
}
for (int i = 1; i<17; i++) {
if (baudrate<acceptedSpeeds[i][0]) {
obj->uart->BAUDRATE = acceptedSpeeds[i - 1][1];
return;
}
}
obj->uart->BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud1M;
}
void serial_format(serial_t *obj, int data_bits, SerialParity parity, int stop_bits)
{
// 0: 1 stop bits, 1: 2 stop bits
// int parity_enable, parity_select;
switch (parity) {
case ParityNone:
obj->uart->CONFIG = 0;
break;
default:
obj->uart->CONFIG = (UART_CONFIG_PARITY_Included << UART_CONFIG_PARITY_Pos);
return;
}
//no Flow Control
}
//******************************************************************************
// * INTERRUPT HANDLING
//******************************************************************************
static inline void uart_irq(uint32_t iir, uint32_t index)
{
SerialIrq irq_type;
switch (iir) {
case 1:
irq_type = TxIrq;
break;
case 2:
irq_type = RxIrq;
break;
default:
return;
}
if (serial_irq_ids[index] != 0) {
irq_handler(serial_irq_ids[index], irq_type);
}
}
#ifdef __cplusplus
extern "C" {
#endif
void UART0_IRQHandler()
{
uint32_t irtype = 0;
if((NRF_UART0->INTENSET & 0x80) && NRF_UART0->EVENTS_TXDRDY) {
irtype = 1;
} else if((NRF_UART0->INTENSET & 0x04) && NRF_UART0->EVENTS_RXDRDY) {
irtype = 2;
}
uart_irq(irtype, 0);
}
#ifdef __cplusplus
}
#endif
void serial_irq_handler(serial_t *obj, uart_irq_handler handler, uint32_t id)
{
irq_handler = handler;
serial_irq_ids[obj->index] = id;
}
void serial_irq_set(serial_t *obj, SerialIrq irq, uint32_t enable)
{
IRQn_Type irq_n = (IRQn_Type)0;
switch ((int)obj->uart) {
case UART_0:
irq_n = UART0_IRQn;
break;
}
if (enable) {
switch (irq) {
case RxIrq:
obj->uart->INTENSET = (UART_INTENSET_RXDRDY_Msk);
break;
case TxIrq:
obj->uart->INTENSET = (UART_INTENSET_TXDRDY_Msk);
break;
}
NVIC_SetPriority(irq_n, 3);
NVIC_EnableIRQ(irq_n);
} else { // disable
// maseked writes to INTENSET dont disable and masked writes to
// INTENCLR seemed to clear the entire register, not bits.
// Added INTEN to memory map and seems to allow set and clearing of specific bits as desired
int all_disabled = 0;
switch (irq) {
case RxIrq:
obj->uart->INTENCLR = (UART_INTENCLR_RXDRDY_Msk);
all_disabled = (obj->uart->INTENCLR & (UART_INTENCLR_TXDRDY_Msk)) == 0;
break;
case TxIrq:
obj->uart->INTENCLR = (UART_INTENCLR_TXDRDY_Msk);
all_disabled = (obj->uart->INTENCLR & (UART_INTENCLR_RXDRDY_Msk)) == 0;
break;
}
if (all_disabled) {
NVIC_DisableIRQ(irq_n);
}
}
}
//******************************************************************************
//* READ/WRITE
//******************************************************************************
int serial_getc(serial_t *obj)
{
while (!serial_readable(obj)) {
}
obj->uart->EVENTS_RXDRDY = 0;
return (uint8_t)obj->uart->RXD;
}
void serial_putc(serial_t *obj, int c)
{
while (!serial_writable(obj)) {
}
obj->uart->EVENTS_TXDRDY = 0;
obj->uart->TXD = (uint8_t)c;
}
int serial_readable(serial_t *obj)
{
return (obj->uart->EVENTS_RXDRDY == 1);
}
int serial_writable(serial_t *obj)
{
return (obj->uart->EVENTS_TXDRDY == 1);
}
void serial_break_set(serial_t *obj)
{
obj->uart->TASKS_SUSPEND = 1;
}
void serial_break_clear(serial_t *obj)
{
obj->uart->TASKS_STARTTX = 1;
obj->uart->TASKS_STARTRX = 1;
}
void serial_set_flow_control(serial_t *obj, FlowControl type, PinName rxflow, PinName txflow)
{
if (type == FlowControlRTSCTS || type == FlowControlRTS) {
NRF_GPIO->DIR |= (1<<rxflow);
pin_mode(rxflow, PullUp);
obj->uart->PSELRTS = rxflow;
obj->uart->CONFIG |= 0x01; // Enable HWFC
}
if (type == FlowControlRTSCTS || type == FlowControlCTS) {
NRF_GPIO->DIR &= ~(1<<txflow);
pin_mode(txflow, PullUp);
obj->uart->PSELCTS = txflow;
obj->uart->CONFIG |= 0x01; // Enable HWFC;
}
if (type == FlowControlNone) {
obj->uart->PSELRTS = 0xFFFFFFFF; // Disable RTS
obj->uart->PSELCTS = 0xFFFFFFFF; // Disable CTS
obj->uart->CONFIG &= ~0x01; // Enable HWFC;
}
}
void serial_clear(serial_t *obj) {
}

View File

@ -1,286 +0,0 @@
/* mbed Microcontroller Library
* Copyright (c) 2013 Nordic Semiconductor
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//#include <math.h>
#include "mbed_assert.h"
#include "spi_api.h"
#include "cmsis.h"
#include "pinmap.h"
#include "mbed_error.h"
#define SPIS_MESSAGE_SIZE 1
volatile uint8_t m_tx_buf[SPIS_MESSAGE_SIZE] = {0};
volatile uint8_t m_rx_buf[SPIS_MESSAGE_SIZE] = {0};
// nRF51822's I2C_0 and SPI_0 (I2C_1, SPI_1 and SPIS1) share the same address.
// They can't be used at the same time. So we use two global variable to track the usage.
// See nRF51822 address information at nRF51822_PS v2.0.pdf - Table 15 Peripheral instance reference
extern volatile i2c_spi_peripheral_t i2c0_spi0_peripheral; // from i2c_api.c
extern volatile i2c_spi_peripheral_t i2c1_spi1_peripheral;
void spi_init(spi_t *obj, PinName mosi, PinName miso, PinName sclk, PinName ssel)
{
SPIName spi;
if (ssel == NC && i2c0_spi0_peripheral.usage == I2C_SPI_PERIPHERAL_FOR_SPI &&
i2c0_spi0_peripheral.sda_mosi == (uint8_t)mosi &&
i2c0_spi0_peripheral.scl_miso == (uint8_t)miso &&
i2c0_spi0_peripheral.sclk == (uint8_t)sclk) {
// The SPI with the same pins is already initialized
spi = SPI_0;
obj->peripheral = 0x1;
} else if (ssel == NC && i2c1_spi1_peripheral.usage == I2C_SPI_PERIPHERAL_FOR_SPI &&
i2c1_spi1_peripheral.sda_mosi == (uint8_t)mosi &&
i2c1_spi1_peripheral.scl_miso == (uint8_t)miso &&
i2c1_spi1_peripheral.sclk == (uint8_t)sclk) {
// The SPI with the same pins is already initialized
spi = SPI_1;
obj->peripheral = 0x2;
} else if (i2c1_spi1_peripheral.usage == 0) {
i2c1_spi1_peripheral.usage = I2C_SPI_PERIPHERAL_FOR_SPI;
i2c1_spi1_peripheral.sda_mosi = (uint8_t)mosi;
i2c1_spi1_peripheral.scl_miso = (uint8_t)miso;
i2c1_spi1_peripheral.sclk = (uint8_t)sclk;
spi = SPI_1;
obj->peripheral = 0x2;
} else if (i2c0_spi0_peripheral.usage == 0) {
i2c0_spi0_peripheral.usage = I2C_SPI_PERIPHERAL_FOR_SPI;
i2c0_spi0_peripheral.sda_mosi = (uint8_t)mosi;
i2c0_spi0_peripheral.scl_miso = (uint8_t)miso;
i2c0_spi0_peripheral.sclk = (uint8_t)sclk;
spi = SPI_0;
obj->peripheral = 0x1;
} else {
// No available peripheral
error("No available SPI");
}
if (ssel==NC) {
obj->spi = (NRF_SPI_Type *)spi;
obj->spis = (NRF_SPIS_Type *)NC;
} else {
obj->spi = (NRF_SPI_Type *)NC;
obj->spis = (NRF_SPIS_Type *)spi;
}
// pin out the spi pins
if (ssel != NC) { //slave
obj->spis->POWER = 0;
obj->spis->POWER = 1;
NRF_GPIO->PIN_CNF[mosi] = (GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos)
| (GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos)
| (GPIO_PIN_CNF_PULL_Disabled << GPIO_PIN_CNF_PULL_Pos)
| (GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos)
| (GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos);
NRF_GPIO->PIN_CNF[miso] = (GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos)
| (GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos)
| (GPIO_PIN_CNF_PULL_Disabled << GPIO_PIN_CNF_PULL_Pos)
| (GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos)
| (GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos);
NRF_GPIO->PIN_CNF[sclk] = (GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos)
| (GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos)
| (GPIO_PIN_CNF_PULL_Disabled << GPIO_PIN_CNF_PULL_Pos)
| (GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos)
| (GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos);
NRF_GPIO->PIN_CNF[ssel] = (GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos)
| (GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos)
| (GPIO_PIN_CNF_PULL_Disabled << GPIO_PIN_CNF_PULL_Pos)
| (GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos)
| (GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos);
obj->spis->PSELMOSI = mosi;
obj->spis->PSELMISO = miso;
obj->spis->PSELSCK = sclk;
obj->spis->PSELCSN = ssel;
obj->spis->EVENTS_END = 0;
obj->spis->EVENTS_ACQUIRED = 0;
obj->spis->MAXRX = SPIS_MESSAGE_SIZE;
obj->spis->MAXTX = SPIS_MESSAGE_SIZE;
obj->spis->TXDPTR = (uint32_t)&m_tx_buf[0];
obj->spis->RXDPTR = (uint32_t)&m_rx_buf[0];
obj->spis->SHORTS = (SPIS_SHORTS_END_ACQUIRE_Enabled << SPIS_SHORTS_END_ACQUIRE_Pos);
spi_format(obj, 8, 0, 1); // 8 bits, mode 0, slave
} else { //master
obj->spi->POWER = 0;
obj->spi->POWER = 1;
//NRF_GPIO->DIR |= (1<<mosi);
NRF_GPIO->PIN_CNF[mosi] = (GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos)
| (GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos)
| (GPIO_PIN_CNF_PULL_Disabled << GPIO_PIN_CNF_PULL_Pos)
| (GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos)
| (GPIO_PIN_CNF_DIR_Output << GPIO_PIN_CNF_DIR_Pos);
obj->spi->PSELMOSI = mosi;
NRF_GPIO->PIN_CNF[sclk] = (GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos)
| (GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos)
| (GPIO_PIN_CNF_PULL_Disabled << GPIO_PIN_CNF_PULL_Pos)
| (GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos)
| (GPIO_PIN_CNF_DIR_Output << GPIO_PIN_CNF_DIR_Pos);
obj->spi->PSELSCK = sclk;
//NRF_GPIO->DIR &= ~(1<<miso);
NRF_GPIO->PIN_CNF[miso] = (GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos)
| (GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos)
| (GPIO_PIN_CNF_PULL_Disabled << GPIO_PIN_CNF_PULL_Pos)
| (GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos)
| (GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos);
obj->spi->PSELMISO = miso;
obj->spi->EVENTS_READY = 0U;
spi_format(obj, 8, 0, 0); // 8 bits, mode 0, master
spi_frequency(obj, 1000000);
}
}
void spi_free(spi_t *obj)
{
}
static inline void spi_disable(spi_t *obj, int slave)
{
if (slave) {
obj->spis->ENABLE = (SPIS_ENABLE_ENABLE_Disabled << SPIS_ENABLE_ENABLE_Pos);
} else {
obj->spi->ENABLE = (SPI_ENABLE_ENABLE_Disabled << SPI_ENABLE_ENABLE_Pos);
}
}
static inline void spi_enable(spi_t *obj, int slave)
{
if (slave) {
obj->spis->ENABLE = (SPIS_ENABLE_ENABLE_Enabled << SPIS_ENABLE_ENABLE_Pos);
} else {
obj->spi->ENABLE = (SPI_ENABLE_ENABLE_Enabled << SPI_ENABLE_ENABLE_Pos);
}
}
void spi_format(spi_t *obj, int bits, int mode, int slave)
{
uint32_t config_mode = 0;
spi_disable(obj, slave);
if (bits != 8) {
error("Only 8bits SPI supported");
}
switch (mode) {
case 0:
config_mode = (SPI_CONFIG_CPHA_Leading << SPI_CONFIG_CPHA_Pos) | (SPI_CONFIG_CPOL_ActiveHigh << SPI_CONFIG_CPOL_Pos);
break;
case 1:
config_mode = (SPI_CONFIG_CPHA_Trailing << SPI_CONFIG_CPHA_Pos) | (SPI_CONFIG_CPOL_ActiveHigh << SPI_CONFIG_CPOL_Pos);
break;
case 2:
config_mode = (SPI_CONFIG_CPHA_Leading << SPI_CONFIG_CPHA_Pos) | (SPI_CONFIG_CPOL_ActiveLow << SPI_CONFIG_CPOL_Pos);
break;
case 3:
config_mode = (SPI_CONFIG_CPHA_Trailing << SPI_CONFIG_CPHA_Pos) | (SPI_CONFIG_CPOL_ActiveLow << SPI_CONFIG_CPOL_Pos);
break;
default:
error("SPI format error");
break;
}
//default to msb first
if (slave) {
obj->spis->CONFIG = (config_mode | (SPI_CONFIG_ORDER_MsbFirst << SPI_CONFIG_ORDER_Pos));
} else {
obj->spi->CONFIG = (config_mode | (SPI_CONFIG_ORDER_MsbFirst << SPI_CONFIG_ORDER_Pos));
}
spi_enable(obj, slave);
}
void spi_frequency(spi_t *obj, int hz)
{
if ((int)obj->spi==NC) {
return;
}
spi_disable(obj, 0);
if (hz<250000) { //125Kbps
obj->spi->FREQUENCY = (uint32_t) SPI_FREQUENCY_FREQUENCY_K125;
} else if (hz<500000) { //250Kbps
obj->spi->FREQUENCY = (uint32_t) SPI_FREQUENCY_FREQUENCY_K250;
} else if (hz<1000000) { //500Kbps
obj->spi->FREQUENCY = (uint32_t) SPI_FREQUENCY_FREQUENCY_K500;
} else if (hz<2000000) { //1Mbps
obj->spi->FREQUENCY = (uint32_t) SPI_FREQUENCY_FREQUENCY_M1;
} else if (hz<4000000) { //2Mbps
obj->spi->FREQUENCY = (uint32_t) SPI_FREQUENCY_FREQUENCY_M2;
} else if (hz<8000000) { //4Mbps
obj->spi->FREQUENCY = (uint32_t) SPI_FREQUENCY_FREQUENCY_M4;
} else { //8Mbps
obj->spi->FREQUENCY = (uint32_t) SPI_FREQUENCY_FREQUENCY_M8;
}
spi_enable(obj, 0);
}
static inline int spi_readable(spi_t *obj)
{
return (obj->spi->EVENTS_READY == 1);
}
static inline int spi_writeable(spi_t *obj)
{
return (obj->spi->EVENTS_READY == 0);
}
static inline int spi_read(spi_t *obj)
{
while (!spi_readable(obj)) {
}
obj->spi->EVENTS_READY = 0;
return (int)obj->spi->RXD;
}
int spi_master_write(spi_t *obj, int value)
{
while (!spi_writeable(obj)) {
}
obj->spi->TXD = (uint32_t)value;
return spi_read(obj);
}
//static inline int spis_writeable(spi_t *obj) {
// return (obj->spis->EVENTS_ACQUIRED==1);
//}
int spi_slave_receive(spi_t *obj)
{
return obj->spis->EVENTS_END;
}
int spi_slave_read(spi_t *obj)
{
return m_rx_buf[0];
}
void spi_slave_write(spi_t *obj, int value)
{
m_tx_buf[0] = value & 0xFF;
obj->spis->TASKS_RELEASE = 1;
obj->spis->EVENTS_ACQUIRED = 0;
obj->spis->EVENTS_END = 0;
}

View File

@ -1,20 +0,0 @@
/* Copyright (c) 2012 Nordic Semiconductor. All Rights Reserved.
*
* The information contained herein is property of Nordic Semiconductor ASA.
* Terms and conditions of usage are described in detail in NORDIC
* SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT.
*
* Licensees are granted free, non-transferable use of the information. NO
* WARRANTY of ANY KIND is provided. This heading must NOT be removed from
* the file.
*
*/
#ifndef TWI_MASTER_CONFIG
#define TWI_MASTER_CONFIG
#include "PinNames.h"
#define TWI_MASTER_CONFIG_CLOCK_PIN_NUMBER (I2C_SCL0)
#define TWI_MASTER_CONFIG_DATA_PIN_NUMBER (I2C_SDA0)
#endif

View File

@ -1,304 +0,0 @@
/* Copyright (c) 2009 Nordic Semiconductor. All Rights Reserved.
*
* The information contained herein is property of Nordic Semiconductor ASA.
* Terms and conditions of usage are described in detail in NORDIC
* SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT.
*
* Licensees are granted free, non-transferable use of the information. NO
* WARRANTY of ANY KIND is provided. This heading must NOT be removed from
* the file.
*
*/
#include "twi_master.h"
#include "twi_config.h"
#include <stdbool.h>
#include <stdint.h>
#include "nrf.h"
#include "nrf_delay.h"
/* Max cycles approximately to wait on RXDREADY and TXDREADY event,
* This is optimized way instead of using timers, this is not power aware. */
#define MAX_TIMEOUT_LOOPS (20000UL) /**< MAX while loops to wait for RXD/TXD event */
static bool twi_master_write(uint8_t * data, uint8_t data_length, bool issue_stop_condition, NRF_TWI_Type* twi)
{
uint32_t timeout = MAX_TIMEOUT_LOOPS; /* max loops to wait for EVENTS_TXDSENT event*/
if (data_length == 0)
{
/* Return false for requesting data of size 0 */
return false;
}
twi->TXD = *data++;
twi->TASKS_STARTTX = 1;
/** @snippet [TWI HW master write] */
while (true)
{
while (twi->EVENTS_TXDSENT == 0 && twi->EVENTS_ERROR == 0 && (--timeout))
{
// Do nothing.
}
if (timeout == 0 || NRF_TWI1->EVENTS_ERROR != 0)
{
// Recover the peripheral as indicated by PAN 56: "TWI: TWI module lock-up." found at
// Product Anomaly Notification document found at
// https://www.nordicsemi.com/eng/Products/Bluetooth-R-low-energy/nRF51822/#Downloads
twi->EVENTS_ERROR = 0;
twi->ENABLE = TWI_ENABLE_ENABLE_Disabled << TWI_ENABLE_ENABLE_Pos;
twi->POWER = 0;
nrf_delay_us(5);
twi->POWER = 1;
twi->ENABLE = TWI_ENABLE_ENABLE_Enabled << TWI_ENABLE_ENABLE_Pos;
(void)twi_master_init_and_clear(twi);
return false;
}
twi->EVENTS_TXDSENT = 0;
if (--data_length == 0)
{
break;
}
twi->TXD = *data++;
}
/** @snippet [TWI HW master write] */
if (issue_stop_condition)
{
twi->EVENTS_STOPPED = 0;
twi->TASKS_STOP = 1;
/* Wait until stop sequence is sent */
while(twi->EVENTS_STOPPED == 0)
{
// Do nothing.
}
}
return true;
}
/** @brief Function for read by twi_master.
*/
static bool twi_master_read(uint8_t * data, uint8_t data_length, bool issue_stop_condition, NRF_TWI_Type* twi)
{
uint32_t timeout = MAX_TIMEOUT_LOOPS; /* max loops to wait for RXDREADY event*/
if (data_length == 0)
{
/* Return false for requesting data of size 0 */
return false;
}
else if (data_length == 1)
{
NRF_PPI->CH[0].TEP = (uint32_t)&twi->TASKS_STOP;
}
else
{
NRF_PPI->CH[0].TEP = (uint32_t)&twi->TASKS_SUSPEND;
}
NRF_PPI->CHENSET = PPI_CHENSET_CH0_Msk;
twi->EVENTS_RXDREADY = 0;
twi->TASKS_STARTRX = 1;
/** @snippet [TWI HW master read] */
while (true)
{
while (twi->EVENTS_RXDREADY == 0 && NRF_TWI1->EVENTS_ERROR == 0 && (--timeout))
{
// Do nothing.
}
twi->EVENTS_RXDREADY = 0;
if (timeout == 0 || twi->EVENTS_ERROR != 0)
{
// Recover the peripheral as indicated by PAN 56: "TWI: TWI module lock-up." found at
// Product Anomaly Notification document found at
// https://www.nordicsemi.com/eng/Products/Bluetooth-R-low-energy/nRF51822/#Downloads
twi->EVENTS_ERROR = 0;
twi->ENABLE = TWI_ENABLE_ENABLE_Disabled << TWI_ENABLE_ENABLE_Pos;
twi->POWER = 0;
nrf_delay_us(5);
twi->POWER = 1;
twi->ENABLE = TWI_ENABLE_ENABLE_Enabled << TWI_ENABLE_ENABLE_Pos;
(void)twi_master_init_and_clear(twi);
return false;
}
*data++ = NRF_TWI1->RXD;
/* Configure PPI to stop TWI master before we get last BB event */
if (--data_length == 1)
{
NRF_PPI->CH[0].TEP = (uint32_t)&NRF_TWI1->TASKS_STOP;
}
if (data_length == 0)
{
break;
}
// Recover the peripheral as indicated by PAN 56: "TWI: TWI module lock-up." found at
// Product Anomaly Notification document found at
// https://www.nordicsemi.com/eng/Products/Bluetooth-R-low-energy/nRF51822/#Downloads
nrf_delay_us(20);
twi->TASKS_RESUME = 1;
}
/** @snippet [TWI HW master read] */
/* Wait until stop sequence is sent */
while(twi->EVENTS_STOPPED == 0)
{
// Do nothing.
}
twi->EVENTS_STOPPED = 0;
NRF_PPI->CHENCLR = PPI_CHENCLR_CH0_Msk;
return true;
}
/**
* @brief Function for detecting stuck slaves (SDA = 0 and SCL = 1) and tries to clear the bus.
*
* @return
* @retval false Bus is stuck.
* @retval true Bus is clear.
*/
static bool twi_master_clear_bus(NRF_TWI_Type* twi)
{
uint32_t twi_state;
bool bus_clear;
uint32_t clk_pin_config;
uint32_t data_pin_config;
// Save and disable TWI hardware so software can take control over the pins.
twi_state = twi->ENABLE;
twi->ENABLE = TWI_ENABLE_ENABLE_Disabled << TWI_ENABLE_ENABLE_Pos;
clk_pin_config = \
NRF_GPIO->PIN_CNF[TWI_MASTER_CONFIG_CLOCK_PIN_NUMBER];
NRF_GPIO->PIN_CNF[TWI_MASTER_CONFIG_CLOCK_PIN_NUMBER] = \
(GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos) \
| (GPIO_PIN_CNF_DRIVE_S0D1 << GPIO_PIN_CNF_DRIVE_Pos) \
| (GPIO_PIN_CNF_PULL_Pullup << GPIO_PIN_CNF_PULL_Pos) \
| (GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos) \
| (GPIO_PIN_CNF_DIR_Output << GPIO_PIN_CNF_DIR_Pos);
data_pin_config = \
NRF_GPIO->PIN_CNF[TWI_MASTER_CONFIG_DATA_PIN_NUMBER];
NRF_GPIO->PIN_CNF[TWI_MASTER_CONFIG_DATA_PIN_NUMBER] = \
(GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos) \
| (GPIO_PIN_CNF_DRIVE_S0D1 << GPIO_PIN_CNF_DRIVE_Pos) \
| (GPIO_PIN_CNF_PULL_Pullup << GPIO_PIN_CNF_PULL_Pos) \
| (GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos) \
| (GPIO_PIN_CNF_DIR_Output << GPIO_PIN_CNF_DIR_Pos);
TWI_SDA_HIGH();
TWI_SCL_HIGH();
TWI_DELAY();
if ((TWI_SDA_READ() == 1) && (TWI_SCL_READ() == 1))
{
bus_clear = true;
}
else
{
uint_fast8_t i;
bus_clear = false;
// Clock max 18 pulses worst case scenario(9 for master to send the rest of command and 9
// for slave to respond) to SCL line and wait for SDA come high.
for (i=18; i--;)
{
TWI_SCL_LOW();
TWI_DELAY();
TWI_SCL_HIGH();
TWI_DELAY();
if (TWI_SDA_READ() == 1)
{
bus_clear = true;
break;
}
}
}
NRF_GPIO->PIN_CNF[TWI_MASTER_CONFIG_CLOCK_PIN_NUMBER] = clk_pin_config;
NRF_GPIO->PIN_CNF[TWI_MASTER_CONFIG_DATA_PIN_NUMBER] = data_pin_config;
twi->ENABLE = twi_state;
return bus_clear;
}
/** @brief Function for initializing the twi_master.
*/
bool twi_master_init_and_clear(NRF_TWI_Type* twi)
{
/* To secure correct signal levels on the pins used by the TWI
master when the system is in OFF mode, and when the TWI master is
disabled, these pins must be configured in the GPIO peripheral.
*/
NRF_GPIO->PIN_CNF[TWI_MASTER_CONFIG_CLOCK_PIN_NUMBER] = \
(GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos) \
| (GPIO_PIN_CNF_DRIVE_S0D1 << GPIO_PIN_CNF_DRIVE_Pos) \
| (GPIO_PIN_CNF_PULL_Pullup << GPIO_PIN_CNF_PULL_Pos) \
| (GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos) \
| (GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos);
NRF_GPIO->PIN_CNF[TWI_MASTER_CONFIG_DATA_PIN_NUMBER] = \
(GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos) \
| (GPIO_PIN_CNF_DRIVE_S0D1 << GPIO_PIN_CNF_DRIVE_Pos) \
| (GPIO_PIN_CNF_PULL_Pullup << GPIO_PIN_CNF_PULL_Pos) \
| (GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos) \
| (GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos);
twi->EVENTS_RXDREADY = 0;
twi->EVENTS_TXDSENT = 0;
twi->PSELSCL = TWI_MASTER_CONFIG_CLOCK_PIN_NUMBER;
twi->PSELSDA = TWI_MASTER_CONFIG_DATA_PIN_NUMBER;
twi->FREQUENCY = TWI_FREQUENCY_FREQUENCY_K100 << TWI_FREQUENCY_FREQUENCY_Pos;
NRF_PPI->CH[0].EEP = (uint32_t)&twi->EVENTS_BB;
NRF_PPI->CH[0].TEP = (uint32_t)&twi->TASKS_SUSPEND;
NRF_PPI->CHENCLR = PPI_CHENCLR_CH0_Msk;
twi->ENABLE = TWI_ENABLE_ENABLE_Enabled << TWI_ENABLE_ENABLE_Pos;
return twi_master_clear_bus(twi);
}
/** @brief Function for transfer by twi_master.
*/
bool twi_master_transfer(uint8_t address,
uint8_t * data,
uint8_t data_length,
bool issue_stop_condition,
NRF_TWI_Type* twi)
{
bool transfer_succeeded = false;
if (data_length > 0 && twi_master_clear_bus(twi))
{
twi->ADDRESS = (address >> 1);
if ((address & TWI_READ_BIT))
{
transfer_succeeded = twi_master_read(data, data_length, issue_stop_condition, twi);
}
else
{
transfer_succeeded = twi_master_write(data, data_length, issue_stop_condition, twi);
}
}
return transfer_succeeded;
}
/*lint --flb "Leave library region" */

View File

@ -1,112 +0,0 @@
/* Copyright (c) 2009 Nordic Semiconductor. All Rights Reserved.
*
* The information contained herein is property of Nordic Semiconductor ASA.
* Terms and conditions of usage are described in detail in NORDIC
* SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT.
*
* Licensees are granted free, non-transferable use of the information. NO
* WARRANTY of ANY KIND is provided. This heading must NOT be removed from
* the file.
*
*/
#ifndef TWI_MASTER_H
#define TWI_MASTER_H
#ifdef __cplusplus
extern "C" {
#endif
/*lint ++flb "Enter library region" */
#include <stdbool.h>
#include <stdint.h>
#include "nrf51.h"
/** @file
* @brief Software controlled TWI Master driver.
*
*
* @defgroup lib_driver_twi_master Software controlled TWI Master driver
* @{
* @ingroup nrf_drivers
* @brief Software controlled TWI Master driver.
*
* Supported features:
* - Repeated start
* - No multi-master
* - Only 7-bit addressing
* - Supports clock stretching (with optional SMBus style slave timeout)
* - Tries to handle slaves stuck in the middle of transfer
*/
#define TWI_READ_BIT (0x01) //!< If this bit is set in the address field, transfer direction is from slave to master.
#define TWI_ISSUE_STOP ((bool)true) //!< Parameter for @ref twi_master_transfer
#define TWI_DONT_ISSUE_STOP ((bool)false) //!< Parameter for @ref twi_master_transfer
/* These macros are needed to see if the slave is stuck and we as master send dummy clock cycles to end its wait */
/*lint -e717 -save "Suppress do {} while (0) for these macros" */
/*lint ++flb "Enter library region" */
#define TWI_SCL_HIGH() do { NRF_GPIO->OUTSET = (1UL << TWI_MASTER_CONFIG_CLOCK_PIN_NUMBER); } while(0) /*!< Pulls SCL line high */
#define TWI_SCL_LOW() do { NRF_GPIO->OUTCLR = (1UL << TWI_MASTER_CONFIG_CLOCK_PIN_NUMBER); } while(0) /*!< Pulls SCL line low */
#define TWI_SDA_HIGH() do { NRF_GPIO->OUTSET = (1UL << TWI_MASTER_CONFIG_DATA_PIN_NUMBER); } while(0) /*!< Pulls SDA line high */
#define TWI_SDA_LOW() do { NRF_GPIO->OUTCLR = (1UL << TWI_MASTER_CONFIG_DATA_PIN_NUMBER); } while(0) /*!< Pulls SDA line low */
#define TWI_SDA_INPUT() do { NRF_GPIO->DIRCLR = (1UL << TWI_MASTER_CONFIG_DATA_PIN_NUMBER); } while(0) /*!< Configures SDA pin as input */
#define TWI_SDA_OUTPUT() do { NRF_GPIO->DIRSET = (1UL << TWI_MASTER_CONFIG_DATA_PIN_NUMBER); } while(0) /*!< Configures SDA pin as output */
#define TWI_SCL_OUTPUT() do { NRF_GPIO->DIRSET = (1UL << TWI_MASTER_CONFIG_CLOCK_PIN_NUMBER); } while(0) /*!< Configures SCL pin as output */
/*lint -restore */
#define TWI_SDA_READ() ((NRF_GPIO->IN >> TWI_MASTER_CONFIG_DATA_PIN_NUMBER) & 0x1UL) /*!< Reads current state of SDA */
#define TWI_SCL_READ() ((NRF_GPIO->IN >> TWI_MASTER_CONFIG_CLOCK_PIN_NUMBER) & 0x1UL) /*!< Reads current state of SCL */
#define TWI_DELAY() nrf_delay_us(4) /*!< Time to wait when pin states are changed. For fast-mode the delay can be zero and for standard-mode 4 us delay is sufficient. */
/**
* @brief Function for initializing TWI bus IO pins and checks if the bus is operational.
*
* Both pins are configured as Standard-0, No-drive-1 (open drain).
*
* @param twi The TWI interface to use - either NRF_TWI0 or NRF_TWI1
* @return
* @retval true TWI bus is clear for transfers.
* @retval false TWI bus is stuck.
*/
bool twi_master_init_and_clear(NRF_TWI_Type* twi);
/**
* @brief Function for transferring data over TWI bus.
*
* If TWI master detects even one NACK from the slave or timeout occurs, STOP condition is issued
* and the function returns false.
* Bit 0 (@ref TWI_READ_BIT) in the address parameter controls transfer direction;
* - If 1, master reads data_length number of bytes from the slave
* - If 0, master writes data_length number of bytes to the slave.
*
* @note Make sure at least data_length number of bytes is allocated in data if TWI_READ_BIT is set.
* @note @ref TWI_ISSUE_STOP
*
* @param address Data transfer direction (LSB) / Slave address (7 MSBs).
* @param data Pointer to data.
* @param data_length Number of bytes to transfer.
* @param issue_stop_condition If @ref TWI_ISSUE_STOP, STOP condition is issued before exiting function. If @ref TWI_DONT_ISSUE_STOP, STOP condition is not issued before exiting function. If transfer failed for any reason, STOP condition will be issued in any case.
* @param twi The TWI interface to use - either NRF_TWI0 or NRF_TWI1
* @return
* @retval true Data transfer succeeded without errors.
* @retval false Data transfer failed.
*/
bool twi_master_transfer(uint8_t address, uint8_t *data, uint8_t data_length, bool issue_stop_condition, NRF_TWI_Type* twi);
/**
*@}
**/
#ifdef __cplusplus
}
#endif
/*lint --flb "Leave library region" */
#endif //TWI_MASTER_H

View File

@ -1,602 +0,0 @@
/* mbed Microcontroller Library
* Copyright (c) 2013 Nordic Semiconductor
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stddef.h>
#include <stdbool.h>
#include "us_ticker_api.h"
#include "cmsis.h"
#include "PeripheralNames.h"
#include "nrf_delay.h"
#include "toolchain.h"
/*
* Note: The micro-second timer API on the nRF51 platform is implemented using
* the RTC counter run at 32kHz (sourced from an external oscillator). This is
* a trade-off between precision and power. Running a normal 32-bit MCU counter
* at high frequency causes the average power consumption to rise to a few
* hundred micro-amps, which is prohibitive for typical low-power BLE
* applications.
* A 32kHz clock doesn't offer the precision needed for keeping u-second time,
* but we're assuming that this will not be a problem for the average user.
*/
#define MAX_RTC_COUNTER_VAL 0x00FFFFFF /**< Maximum value of the RTC counter. */
#define RTC_CLOCK_FREQ (uint32_t)(32768)
#define RTC1_IRQ_PRI 3 /**< Priority of the RTC1 interrupt (used
* for checking for timeouts and executing
* timeout handlers). This must be the same
* as APP_IRQ_PRIORITY_LOW; taken from the
* Nordic SDK. */
#define MAX_RTC_TASKS_DELAY 47 /**< Maximum delay until an RTC task is executed. */
#define FUZZY_RTC_TICKS 2 /* RTC COMPARE occurs when a CC register is N and the RTC
* COUNTER value transitions from N-1 to N. If we're trying to
* setup a callback for a time which will arrive very shortly,
* there are limits to how short the callback interval may be for us
* to rely upon the RTC Compare trigger. If the COUNTER is N,
* writing N+2 to a CC register is guaranteed to trigger a COMPARE
* event at N+2. */
#define RTC_UNITS_TO_MICROSECONDS(RTC_UNITS) (((RTC_UNITS) * (uint64_t)1000000) / RTC_CLOCK_FREQ)
#define MICROSECONDS_TO_RTC_UNITS(MICROS) ((((uint64_t)(MICROS) * RTC_CLOCK_FREQ) + 999999) / 1000000)
static bool us_ticker_inited = false;
static volatile uint32_t overflowCount; /**< The number of times the 24-bit RTC counter has overflowed. */
static volatile bool us_ticker_callbackPending = false;
static uint32_t us_ticker_callbackTimestamp;
static bool os_tick_started = false; /**< flag indicating if the os_tick has started */
/**
* The value previously set in the capture compare register of channel 1
*/
static uint32_t previous_tick_cc_value = 0;
/*
RTX provide the following definitions which are used by the tick code:
* os_trv: The number (minus 1) of clock cycle between two tick.
* os_clockrate: Time duration between two ticks (in us).
* OS_Tick_Handler: The function which handle a tick event.
This function is special because it never returns.
Those definitions are used by the code which handle the os tick.
To allow compilation of us_ticker programs without RTOS, those symbols are
exported from this module as weak ones.
*/
MBED_WEAK uint32_t const os_trv;
MBED_WEAK uint32_t const os_clockrate;
MBED_WEAK void OS_Tick_Handler() { }
static inline void rtc1_enableCompareInterrupt(void)
{
NRF_RTC1->EVTENCLR = RTC_EVTEN_COMPARE0_Msk;
NRF_RTC1->INTENSET = RTC_INTENSET_COMPARE0_Msk;
}
static inline void rtc1_disableCompareInterrupt(void)
{
NRF_RTC1->INTENCLR = RTC_INTENSET_COMPARE0_Msk;
NRF_RTC1->EVTENCLR = RTC_EVTEN_COMPARE0_Msk;
}
static inline void rtc1_enableOverflowInterrupt(void)
{
NRF_RTC1->EVTENCLR = RTC_EVTEN_OVRFLW_Msk;
NRF_RTC1->INTENSET = RTC_INTENSET_OVRFLW_Msk;
}
static inline void rtc1_disableOverflowInterrupt(void)
{
NRF_RTC1->INTENCLR = RTC_INTENSET_OVRFLW_Msk;
NRF_RTC1->EVTENCLR = RTC_EVTEN_OVRFLW_Msk;
}
static inline void invokeCallback(void)
{
us_ticker_callbackPending = false;
rtc1_disableCompareInterrupt();
us_ticker_irq_handler();
}
/**
* @brief Function for starting the RTC1 timer. The RTC timer is expected to
* keep running--some interrupts may be disabled temporarily.
*/
static void rtc1_start()
{
NRF_RTC1->PRESCALER = 0; /* for no pre-scaling. */
rtc1_enableOverflowInterrupt();
NVIC_SetPriority(RTC1_IRQn, RTC1_IRQ_PRI);
NVIC_ClearPendingIRQ(RTC1_IRQn);
NVIC_EnableIRQ(RTC1_IRQn);
NRF_RTC1->TASKS_START = 1;
nrf_delay_us(MAX_RTC_TASKS_DELAY);
}
/**
* @brief Function for stopping the RTC1 timer. We don't expect to call this.
*/
void rtc1_stop(void)
{
// If the os tick has been started, RTC1 shouldn't be stopped
// In that case, us ticker and overflow interrupt are disabled.
if (os_tick_started) {
rtc1_disableCompareInterrupt();
rtc1_disableOverflowInterrupt();
} else {
NVIC_DisableIRQ(RTC1_IRQn);
rtc1_disableCompareInterrupt();
rtc1_disableOverflowInterrupt();
NRF_RTC1->TASKS_STOP = 1;
nrf_delay_us(MAX_RTC_TASKS_DELAY);
NRF_RTC1->TASKS_CLEAR = 1;
nrf_delay_us(MAX_RTC_TASKS_DELAY);
}
}
/**
* @brief Function for returning the current value of the RTC1 counter.
*
* @return Current RTC1 counter as a 64-bit value with 56-bit precision (even
* though the underlying counter is 24-bit)
*/
static inline uint64_t rtc1_getCounter64(void)
{
if (NRF_RTC1->EVENTS_OVRFLW) {
overflowCount++;
NRF_RTC1->EVENTS_OVRFLW = 0;
NRF_RTC1->EVTENCLR = RTC_EVTEN_OVRFLW_Msk;
}
return ((uint64_t)overflowCount << 24) | NRF_RTC1->COUNTER;
}
/**
* @brief Function for returning the current value of the RTC1 counter.
*
* @return Current RTC1 counter as a 32-bit value (even though the underlying counter is 24-bit)
*/
static inline uint32_t rtc1_getCounter(void)
{
return rtc1_getCounter64();
}
/**
* @brief Function for handling the RTC1 interrupt for us ticker (capture compare channel 0 and overflow).
*
* @details Checks for timeouts, and executes timeout handlers for expired timers.
*/
void us_ticker_handler(void)
{
if (NRF_RTC1->EVENTS_OVRFLW) {
overflowCount++;
NRF_RTC1->EVENTS_OVRFLW = 0;
NRF_RTC1->EVTENCLR = RTC_EVTEN_OVRFLW_Msk;
}
if (NRF_RTC1->EVENTS_COMPARE[0]) {
NRF_RTC1->EVENTS_COMPARE[0] = 0;
NRF_RTC1->EVTENCLR = RTC_EVTEN_COMPARE0_Msk;
if (us_ticker_callbackPending && ((int)(us_ticker_callbackTimestamp - rtc1_getCounter()) <= 0))
invokeCallback();
}
}
void us_ticker_init(void)
{
if (us_ticker_inited) {
return;
}
rtc1_start();
us_ticker_inited = true;
}
uint32_t us_ticker_read()
{
if (!us_ticker_inited) {
us_ticker_init();
}
/* Return a pseudo microsecond counter value. This is only as precise as the
* 32khz low-freq clock source, but could be adequate.*/
return RTC_UNITS_TO_MICROSECONDS(rtc1_getCounter64());
}
/**
* Setup the us_ticker callback interrupt to go at the given timestamp.
*
* @Note: Only one callback is pending at any time.
*
* @Note: If a callback is pending, and this function is called again, the new
* callback-time overrides the existing callback setting. It is the caller's
* responsibility to ensure that this function is called to setup a callback for
* the earliest timeout.
*
* @Note: If this function is used to setup an interrupt which is immediately
* pending--such as for 'now' or a time in the past,--then the callback is
* invoked a few ticks later.
*/
void us_ticker_set_interrupt(timestamp_t timestamp)
{
if (!us_ticker_inited) {
us_ticker_init();
}
/*
* The argument to this function is a 32-bit microsecond timestamp for when
* a callback should be invoked. On the nRF51, we use an RTC timer running
* at 32kHz to implement a low-power us-ticker. This results in a problem
* based on the fact that 1000000 is not a multiple of 32768.
*
* Going from a micro-second based timestamp to a 32kHz based RTC-time is a
* linear mapping; but this mapping doesn't preserve wraparounds--i.e. when
* the 32-bit micro-second timestamp wraps around unfortunately the
* underlying RTC counter doesn't. The result is that timestamp expiry
* checks on micro-second timestamps don't yield the same result when
* applied on the corresponding RTC timestamp values.
*
* One solution is to translate the incoming 32-bit timestamp into a virtual
* 64-bit timestamp based on the knowledge of system-uptime, and then use
* this wraparound-free 64-bit value to do a linear mapping to RTC time.
* System uptime on an nRF is maintained using the 24-bit RTC counter. We
* track the overflow count to extend the 24-bit hardware counter by an
* additional 32 bits. RTC_UNITS_TO_MICROSECONDS() converts this into
* microsecond units (in 64-bits).
*/
const uint64_t currentTime64 = RTC_UNITS_TO_MICROSECONDS(rtc1_getCounter64());
uint64_t timestamp64 = (currentTime64 & ~(uint64_t)0xFFFFFFFFULL) + timestamp;
if (((uint32_t)currentTime64 > 0x80000000) && (timestamp < 0x80000000)) {
timestamp64 += (uint64_t)0x100000000ULL;
}
uint32_t newCallbackTime = MICROSECONDS_TO_RTC_UNITS(timestamp64);
/* Check for repeat setup of an existing callback. This is actually not
* important; the following code should work even without this check. */
if (us_ticker_callbackPending && (newCallbackTime == us_ticker_callbackTimestamp)) {
return;
}
/* Check for callbacks which are immediately (or will *very* shortly become) pending.
* Even if they are immediately pending, they are scheduled to trigger a few
* ticks later. This keeps things simple by invoking the callback from an
* independent interrupt context. */
if ((int)(newCallbackTime - rtc1_getCounter()) <= (int)FUZZY_RTC_TICKS) {
newCallbackTime = rtc1_getCounter() + FUZZY_RTC_TICKS;
}
NRF_RTC1->CC[0] = newCallbackTime & MAX_RTC_COUNTER_VAL;
us_ticker_callbackTimestamp = newCallbackTime;
if (!us_ticker_callbackPending) {
us_ticker_callbackPending = true;
rtc1_enableCompareInterrupt();
}
}
void us_ticker_disable_interrupt(void)
{
if (us_ticker_callbackPending) {
rtc1_disableCompareInterrupt();
us_ticker_callbackPending = false;
}
}
void us_ticker_clear_interrupt(void)
{
NRF_RTC1->EVENTS_OVRFLW = 0;
NRF_RTC1->EVENTS_COMPARE[0] = 0;
}
#if defined (__CC_ARM) /* ARMCC Compiler */
__asm void RTC1_IRQHandler(void)
{
IMPORT OS_Tick_Handler
IMPORT us_ticker_handler
/**
* Chanel 1 of RTC1 is used by RTX as a systick.
* If the compare event on channel 1 is set, then branch to OS_Tick_Handler.
* Otherwise, just execute us_ticker_handler.
* This function has to be written in assembly and tagged as naked because OS_Tick_Handler
* will never return.
* A c function would put lr on the stack before calling OS_Tick_Handler and this value
* would never been dequeued.
*
* \code
* void RTC1_IRQHandler(void) {
if(NRF_RTC1->EVENTS_COMPARE[1]) {
// never return...
OS_Tick_Handler();
} else {
us_ticker_handler();
}
}
* \endcode
*/
ldr r0,=0x40011144
ldr r1, [r0, #0]
cmp r1, #0
beq US_TICKER_HANDLER
bl OS_Tick_Handler
US_TICKER_HANDLER
push {r3, lr}
bl us_ticker_handler
pop {r3, pc}
nop /* padding */
}
#elif defined (__GNUC__) /* GNU Compiler */
__attribute__((naked)) void RTC1_IRQHandler(void)
{
/**
* Chanel 1 of RTC1 is used by RTX as a systick.
* If the compare event on channel 1 is set, then branch to OS_Tick_Handler.
* Otherwise, just execute us_ticker_handler.
* This function has to be written in assembly and tagged as naked because OS_Tick_Handler
* will never return.
* A c function would put lr on the stack before calling OS_Tick_Handler and this value
* would never been dequeued.
*
* \code
* void RTC1_IRQHandler(void) {
if(NRF_RTC1->EVENTS_COMPARE[1]) {
// never return...
OS_Tick_Handler();
} else {
us_ticker_handler();
}
}
* \endcode
*/
__asm__ (
"ldr r0,=0x40011144\n"
"ldr r1, [r0, #0]\n"
"cmp r1, #0\n"
"beq US_TICKER_HANDLER\n"
"bl OS_Tick_Handler\n"
"US_TICKER_HANDLER:\n"
"push {r3, lr}\n"
"bl us_ticker_handler\n"
"pop {r3, pc}\n"
"nop"
);
}
#else
#error Compiler not supported.
#error Provide a definition of RTC1_IRQHandler.
/*
* Chanel 1 of RTC1 is used by RTX as a systick.
* If the compare event on channel 1 is set, then branch to OS_Tick_Handler.
* Otherwise, just execute us_ticker_handler.
* This function has to be written in assembly and tagged as naked because OS_Tick_Handler
* will never return.
* A c function would put lr on the stack before calling OS_Tick_Handler and this value
* will never been dequeued. After a certain time a stack overflow will happen.
*
* \code
* void RTC1_IRQHandler(void) {
if(NRF_RTC1->EVENTS_COMPARE[1]) {
// never return...
OS_Tick_Handler();
} else {
us_ticker_handler();
}
}
* \endcode
*/
#endif
/**
* Return the next number of clock cycle needed for the next tick.
* @note This function has been carrefuly optimized for a systick occuring every 1000us.
*/
static uint32_t get_next_tick_cc_delta() {
uint32_t delta = 0;
if (os_clockrate != 1000) {
// In RTX, by default SYSTICK is is used.
// A tick event is generated every os_trv + 1 clock cycles of the system timer.
delta = os_trv + 1;
} else {
// If the clockrate is set to 1000us then 1000 tick should happen every second.
// Unfortunatelly, when clockrate is set to 1000, os_trv is equal to 31.
// If (os_trv + 1) is used as the delta value between two ticks, 1000 ticks will be
// generated in 32000 clock cycle instead of 32768 clock cycles.
// As a result, if a user schedule an OS timer to start in 100s, the timer will start
// instead after 97.656s
// The code below fix this issue, a clock rate of 1000s will generate 1000 ticks in 32768
// clock cycles.
// The strategy is simple, for 1000 ticks:
// * 768 ticks will occur 33 clock cycles after the previous tick
// * 232 ticks will occur 32 clock cycles after the previous tick
// By default every delta is equal to 33.
// Every five ticks (20%, 200 delta in one second), the delta is equal to 32
// The remaining (32) deltas equal to 32 are distributed using primes numbers.
static uint32_t counter = 0;
if ((counter % 5) == 0 || (counter % 31) == 0 || (counter % 139) == 0 || (counter == 503)) {
delta = 32;
} else {
delta = 33;
}
++counter;
if (counter == 1000) {
counter = 0;
}
}
return delta;
}
static inline void clear_tick_interrupt() {
NRF_RTC1->EVENTS_COMPARE[1] = 0;
NRF_RTC1->EVTENCLR = (1 << 17);
}
/**
* Indicate if a value is included in a range which can be wrapped.
* @param begin start of the range
* @param end end of the range
* @param val value to check
* @return true if the value is included in the range and false otherwise.
*/
static inline bool is_in_wrapped_range(uint32_t begin, uint32_t end, uint32_t val) {
// regular case, begin < end
// return true if begin <= val < end
if (begin < end) {
if (begin <= val && val < end) {
return true;
} else {
return false;
}
} else {
// In this case end < begin because it has wrap around the limits
// return false if end < val < begin
if (end < val && val < begin) {
return false;
} else {
return true;
}
}
}
/**
* Register the next tick.
*/
static void register_next_tick() {
previous_tick_cc_value = NRF_RTC1->CC[1];
uint32_t delta = get_next_tick_cc_delta();
uint32_t new_compare_value = (previous_tick_cc_value + delta) & MAX_RTC_COUNTER_VAL;
// Disable irq directly for few cycles,
// Validation of the new CC value against the COUNTER,
// Setting the new CC value and enabling CC IRQ should be an atomic operation
// Otherwise, there is a possibility to set an invalid CC value because
// the RTC1 keeps running.
// This code is very short 20-38 cycles in the worst case, it shouldn't
// disturb softdevice.
__disable_irq();
uint32_t current_counter = NRF_RTC1->COUNTER;
// If an overflow occur, set the next tick in COUNTER + delta clock cycles
if (is_in_wrapped_range(previous_tick_cc_value, new_compare_value, current_counter) == false) {
new_compare_value = current_counter + delta;
}
NRF_RTC1->CC[1] = new_compare_value;
// set the interrupt of CC channel 1 and reenable IRQs
NRF_RTC1->INTENSET = RTC_INTENSET_COMPARE1_Msk;
__enable_irq();
}
/**
* Initialize alternative hardware timer as RTX kernel timer
* This function is directly called by RTX.
* @note this function shouldn't be called directly.
* @return IRQ number of the alternative hardware timer
*/
int os_tick_init (void)
{
NRF_CLOCK->LFCLKSRC = (CLOCK_LFCLKSRC_SRC_Xtal << CLOCK_LFCLKSRC_SRC_Pos);
NRF_CLOCK->EVENTS_LFCLKSTARTED = 0;
NRF_CLOCK->TASKS_LFCLKSTART = 1;
while (NRF_CLOCK->EVENTS_LFCLKSTARTED == 0) {
// wait for the low frequency clock start
}
NRF_RTC1->PRESCALER = 0; /* for no pre-scaling. */
NVIC_SetPriority(RTC1_IRQn, RTC1_IRQ_PRI);
NVIC_ClearPendingIRQ(RTC1_IRQn);
NVIC_EnableIRQ(RTC1_IRQn);
NRF_RTC1->TASKS_START = 1;
nrf_delay_us(MAX_RTC_TASKS_DELAY);
NRF_RTC1->CC[1] = 0;
clear_tick_interrupt();
register_next_tick();
os_tick_started = true;
return RTC1_IRQn;
}
/**
* Acknowledge the tick interrupt.
* This function is called by the function OS_Tick_Handler of RTX.
* @note this function shouldn't be called directly.
*/
void os_tick_irqack(void)
{
clear_tick_interrupt();
register_next_tick();
}
/**
* Returns the overflow flag of the alternative hardware timer.
* @note This function is exposed by RTX kernel.
* @return 1 if the timer has overflowed and 0 otherwise.
*/
uint32_t os_tick_ovf(void) {
uint32_t current_counter = NRF_RTC1->COUNTER;
uint32_t next_tick_cc_value = NRF_RTC1->CC[1];
return is_in_wrapped_range(previous_tick_cc_value, next_tick_cc_value, current_counter) ? 0 : 1;
}
/**
* Return the value of the alternative hardware timer.
* @note The documentation is not very clear about what is expected as a result,
* is it an ascending counter, a descending one ?
* None of this is specified.
* The default systick is a descending counter and this function return values in
* descending order, even if the internal counter used is an ascending one.
* @return the value of the alternative hardware timer.
*/
uint32_t os_tick_val(void) {
uint32_t current_counter = NRF_RTC1->COUNTER;
uint32_t next_tick_cc_value = NRF_RTC1->CC[1];
// do not use os_tick_ovf because its counter value can be different
if(is_in_wrapped_range(previous_tick_cc_value, next_tick_cc_value, current_counter)) {
if (next_tick_cc_value > previous_tick_cc_value) {
return next_tick_cc_value - current_counter;
} else if(current_counter <= next_tick_cc_value) {
return next_tick_cc_value - current_counter;
} else {
return next_tick_cc_value + (MAX_RTC_COUNTER_VAL - current_counter);
}
} else {
// use (os_trv + 1) has the base step, can be totally inacurate ...
uint32_t clock_cycles_by_tick = os_trv + 1;
// if current counter has wrap arround, add the limit to it.
if (current_counter < next_tick_cc_value) {
current_counter = current_counter + MAX_RTC_COUNTER_VAL;
}
return clock_cycles_by_tick - ((current_counter - next_tick_cc_value) % clock_cycles_by_tick);
}
return 0;
}

View File

@ -18,6 +18,8 @@
#include "cmsis.h"
#include "pinmap.h"
#ifdef DEVICE_ANALOGIN
#define ANALOGIN_MEDIAN_FILTER 1
#define ADC_10BIT_RANGE 0x3FF
#define ADC_RANGE ADC_10BIT_RANGE
@ -80,3 +82,5 @@ float analogin_read(analogin_t *obj)
uint16_t value = analogin_read_u16(obj);
return (float)value * (1.0f / (float)ADC_RANGE);
}
#endif // DEVICE_ANALOGIN

View File

@ -1,58 +0,0 @@
/* mbed Microcontroller Library
* Copyright (c) 2006-2013 ARM Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "mbed_assert.h"
#include "gpio_api.h"
#include "pinmap.h"
void gpio_init(gpio_t *obj, PinName pin)
{
obj->pin = pin;
if (pin == (PinName)NC) {
return;
}
obj->mask = (1ul << pin);
obj->reg_set = &NRF_GPIO->OUTSET;
obj->reg_clr = &NRF_GPIO->OUTCLR;
obj->reg_in = &NRF_GPIO->IN;
obj->reg_dir = &NRF_GPIO->DIR;
}
void gpio_mode(gpio_t *obj, PinMode mode)
{
pin_mode(obj->pin, mode);
}
void gpio_dir(gpio_t *obj, PinDirection direction)
{
MBED_ASSERT(obj->pin != (PinName)NC);
switch (direction) {
case PIN_INPUT:
NRF_GPIO->PIN_CNF[obj->pin] = (GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos)
| (GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos)
| (GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos)
| (GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos);
break;
case PIN_OUTPUT:
NRF_GPIO->PIN_CNF[obj->pin] = (GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos)
| (GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos)
| (GPIO_PIN_CNF_PULL_Disabled << GPIO_PIN_CNF_PULL_Pos)
| (GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos)
| (GPIO_PIN_CNF_DIR_Output << GPIO_PIN_CNF_DIR_Pos);
break;
}
}

View File

@ -1,127 +0,0 @@
/* mbed Microcontroller Library
* Copyright (c) 2013 Nordic Semiconductor
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stddef.h>
#include "cmsis.h"
#include "gpio_irq_api.h"
#include "mbed_error.h"
#define CHANNEL_NUM 31
static uint32_t channel_ids[CHANNEL_NUM] = {0}; //each pin will be given an id, if id is 0 the pin can be ignored.
static uint8_t channel_enabled[CHANNEL_NUM] = {0};
static uint32_t portRISE = 0;
static uint32_t portFALL = 0;
static gpio_irq_handler irq_handler;
#ifdef __cplusplus
extern "C" {
#endif
void GPIOTE_IRQHandler(void)
{
volatile uint32_t newVal = NRF_GPIO->IN;
if ((NRF_GPIOTE->EVENTS_PORT != 0) && ((NRF_GPIOTE->INTENSET & GPIOTE_INTENSET_PORT_Msk) != 0)) {
NRF_GPIOTE->EVENTS_PORT = 0;
for (uint8_t i = 0; i<31; i++) {
if (channel_ids[i]>0) {
if (channel_enabled[i]) {
if( ((newVal>>i)&1) && ( ( (NRF_GPIO->PIN_CNF[i] >>GPIO_PIN_CNF_SENSE_Pos) & GPIO_PIN_CNF_SENSE_Low) != GPIO_PIN_CNF_SENSE_Low) && ( (portRISE>>i)&1) ){
irq_handler(channel_ids[i], IRQ_RISE);
} else if ((((newVal >> i) & 1) == 0) &&
(((NRF_GPIO->PIN_CNF[i] >> GPIO_PIN_CNF_SENSE_Pos) & GPIO_PIN_CNF_SENSE_Low) == GPIO_PIN_CNF_SENSE_Low) &&
((portFALL >> i) & 1)) {
irq_handler(channel_ids[i], IRQ_FALL);
}
}
if (NRF_GPIO->PIN_CNF[i] & GPIO_PIN_CNF_SENSE_Msk) {
NRF_GPIO->PIN_CNF[i] &= ~(GPIO_PIN_CNF_SENSE_Msk);
if (newVal >> i & 1) {
NRF_GPIO->PIN_CNF[i] |= (GPIO_PIN_CNF_SENSE_Low << GPIO_PIN_CNF_SENSE_Pos);
} else {
NRF_GPIO->PIN_CNF[i] |= (GPIO_PIN_CNF_SENSE_High << GPIO_PIN_CNF_SENSE_Pos);
}
}
}
}
}
}
#ifdef __cplusplus
}
#endif
int gpio_irq_init(gpio_irq_t *obj, PinName pin, gpio_irq_handler handler, uint32_t id)
{
if (pin == NC) {
return -1;
}
irq_handler = handler;
obj->ch = pin;
NRF_GPIOTE->EVENTS_PORT = 0;
channel_ids[pin] = id;
channel_enabled[pin] = 1;
NRF_GPIOTE->INTENSET = GPIOTE_INTENSET_PORT_Set << GPIOTE_INTENSET_PORT_Pos;
NVIC_SetPriority(GPIOTE_IRQn, 3);
NVIC_EnableIRQ (GPIOTE_IRQn);
return 0;
}
void gpio_irq_free(gpio_irq_t *obj)
{
channel_ids[obj->ch] = 0;
}
void gpio_irq_set(gpio_irq_t *obj, gpio_irq_event event, uint32_t enable)
{
NRF_GPIO->PIN_CNF[obj->ch] &= ~(GPIO_PIN_CNF_SENSE_Msk);
if (enable) {
if (event == IRQ_RISE) {
portRISE |= (1 << obj->ch);
} else if (event == IRQ_FALL) {
portFALL |= (1 << obj->ch);
}
} else {
if (event == IRQ_RISE) {
portRISE &= ~(1 << obj->ch);
} else if (event == IRQ_FALL) {
portFALL &= ~(1 << obj->ch);
}
}
if (((portRISE >> obj->ch) & 1) || ((portFALL >> obj->ch) & 1)) {
if ((NRF_GPIO->IN >> obj->ch) & 1) {
NRF_GPIO->PIN_CNF[obj->ch] |= (GPIO_PIN_CNF_SENSE_Low << GPIO_PIN_CNF_SENSE_Pos); // | (GPIO_PIN_CNF_PULL_Disabled << GPIO_PIN_CNF_PULL_Pos);
} else {
NRF_GPIO->PIN_CNF[obj->ch] |= (GPIO_PIN_CNF_SENSE_High << GPIO_PIN_CNF_SENSE_Pos); //| (GPIO_PIN_CNF_PULL_Disabled << GPIO_PIN_CNF_PULL_Pos);
}
}
}
void gpio_irq_enable(gpio_irq_t *obj)
{
channel_enabled[obj->ch] = 1;
}
void gpio_irq_disable(gpio_irq_t *obj)
{
channel_enabled[obj->ch] = 0;
}

View File

@ -1,311 +0,0 @@
/* mbed Microcontroller Library
* Copyright (c) 2013 Nordic Semiconductor
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "mbed_assert.h"
#include "mbed_error.h"
#include "i2c_api.h"
#include "cmsis.h"
#include "pinmap.h"
// nRF51822's I2C_0 and SPI_0 (I2C_1, SPI_1 and SPIS1) share the same address.
// They can't be used at the same time. So we use two global variable to track the usage.
// See nRF51822 address information at nRF51822_PS v2.0.pdf - Table 15 Peripheral instance reference
volatile i2c_spi_peripheral_t i2c0_spi0_peripheral = {0, 0, 0, 0};
volatile i2c_spi_peripheral_t i2c1_spi1_peripheral = {0, 0, 0, 0};
void i2c_interface_enable(i2c_t *obj)
{
obj->i2c->ENABLE = (TWI_ENABLE_ENABLE_Enabled << TWI_ENABLE_ENABLE_Pos);
}
void twi_master_init(i2c_t *obj, PinName sda, PinName scl, int frequency)
{
NRF_GPIO->PIN_CNF[scl] = ((GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos) |
(GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos) |
(GPIO_PIN_CNF_PULL_Disabled << GPIO_PIN_CNF_PULL_Pos) |
(GPIO_PIN_CNF_DRIVE_S0D1 << GPIO_PIN_CNF_DRIVE_Pos) |
(GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos));
NRF_GPIO->PIN_CNF[sda] = ((GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos) |
(GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos) |
(GPIO_PIN_CNF_PULL_Disabled << GPIO_PIN_CNF_PULL_Pos) |
(GPIO_PIN_CNF_DRIVE_S0D1 << GPIO_PIN_CNF_DRIVE_Pos) |
(GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos));
obj->i2c->PSELSCL = scl;
obj->i2c->PSELSDA = sda;
// set default frequency at 100k
i2c_frequency(obj, frequency);
i2c_interface_enable(obj);
}
void i2c_init(i2c_t *obj, PinName sda, PinName scl)
{
// Initialize variable to avoid compiler warnings
NRF_TWI_Type *i2c = (NRF_TWI_Type *)I2C_0;
if (i2c0_spi0_peripheral.usage == I2C_SPI_PERIPHERAL_FOR_I2C &&
i2c0_spi0_peripheral.sda_mosi == (uint8_t)sda &&
i2c0_spi0_peripheral.scl_miso == (uint8_t)scl) {
// The I2C with the same pins is already initialized
i2c = (NRF_TWI_Type *)I2C_0;
obj->peripheral = 0x1;
} else if (i2c1_spi1_peripheral.usage == I2C_SPI_PERIPHERAL_FOR_I2C &&
i2c1_spi1_peripheral.sda_mosi == (uint8_t)sda &&
i2c1_spi1_peripheral.scl_miso == (uint8_t)scl) {
// The I2C with the same pins is already initialized
i2c = (NRF_TWI_Type *)I2C_1;
obj->peripheral = 0x2;
} else if (i2c0_spi0_peripheral.usage == 0) {
i2c0_spi0_peripheral.usage = I2C_SPI_PERIPHERAL_FOR_I2C;
i2c0_spi0_peripheral.sda_mosi = (uint8_t)sda;
i2c0_spi0_peripheral.scl_miso = (uint8_t)scl;
i2c = (NRF_TWI_Type *)I2C_0;
obj->peripheral = 0x1;
} else if (i2c1_spi1_peripheral.usage == 0) {
i2c1_spi1_peripheral.usage = I2C_SPI_PERIPHERAL_FOR_I2C;
i2c1_spi1_peripheral.sda_mosi = (uint8_t)sda;
i2c1_spi1_peripheral.scl_miso = (uint8_t)scl;
i2c = (NRF_TWI_Type *)I2C_1;
obj->peripheral = 0x2;
} else {
// No available peripheral
error("No available I2C");
}
obj->i2c = i2c;
obj->scl = scl;
obj->sda = sda;
obj->i2c->EVENTS_ERROR = 0;
obj->i2c->ENABLE = TWI_ENABLE_ENABLE_Disabled << TWI_ENABLE_ENABLE_Pos;
// TODO: usage on nrf52 ?
// obj->i2c->POWER = 0;
for (int i = 0; i<100; i++) {
}
// TODO: usage on nrf52 ?
// obj->i2c->POWER = 1;
twi_master_init(obj, sda, scl, 100000);
}
void i2c_reset(i2c_t *obj)
{
obj->i2c->EVENTS_ERROR = 0;
obj->i2c->ENABLE = TWI_ENABLE_ENABLE_Disabled << TWI_ENABLE_ENABLE_Pos;
// TODO: usage on nrf52 ?
// obj->i2c->POWER = 0;
for (int i = 0; i<100; i++) {
}
// TODO: usage on nrf52 ?
// obj->i2c->POWER = 1;
twi_master_init(obj, obj->sda, obj->scl, obj->freq);
}
int i2c_start(i2c_t *obj)
{
int status = 0;
i2c_reset(obj);
obj->address_set = 0;
return status;
}
int i2c_stop(i2c_t *obj)
{
int timeOut = 100000;
obj->i2c->EVENTS_STOPPED = 0;
// write the stop bit
obj->i2c->TASKS_STOP = 1;
while (!obj->i2c->EVENTS_STOPPED) {
timeOut--;
if (timeOut<0) {
return 1;
}
}
obj->address_set = 0;
i2c_reset(obj);
return 0;
}
int i2c_do_write(i2c_t *obj, int value)
{
int timeOut = 100000;
obj->i2c->TXD = value;
while (!obj->i2c->EVENTS_TXDSENT) {
timeOut--;
if (timeOut<0) {
return 1;
}
}
obj->i2c->EVENTS_TXDSENT = 0;
return 0;
}
int i2c_do_read(i2c_t *obj, char *data, int last)
{
int timeOut = 100000;
if (last) {
// To trigger stop task when a byte is received,
// must be set before resume task.
obj->i2c->SHORTS = 2;
}
obj->i2c->TASKS_RESUME = 1;
while (!obj->i2c->EVENTS_RXDREADY) {
timeOut--;
if (timeOut<0) {
return 1;
}
}
obj->i2c->EVENTS_RXDREADY = 0;
*data = obj->i2c->RXD;
return 0;
}
void i2c_frequency(i2c_t *obj, int hz)
{
if (hz<250000) {
obj->freq = 100000;
obj->i2c->FREQUENCY = (TWI_FREQUENCY_FREQUENCY_K100 << TWI_FREQUENCY_FREQUENCY_Pos);
} else if (hz<400000) {
obj->freq = 250000;
obj->i2c->FREQUENCY = (TWI_FREQUENCY_FREQUENCY_K250 << TWI_FREQUENCY_FREQUENCY_Pos);
} else {
obj->freq = 400000;
obj->i2c->FREQUENCY = (TWI_FREQUENCY_FREQUENCY_K400 << TWI_FREQUENCY_FREQUENCY_Pos);
}
}
int checkError(i2c_t *obj)
{
if (obj->i2c->EVENTS_ERROR == 1) {
if (obj->i2c->ERRORSRC & TWI_ERRORSRC_ANACK_Msk) {
obj->i2c->EVENTS_ERROR = 0;
obj->i2c->TASKS_STOP = 1;
return I2C_ERROR_BUS_BUSY;
}
obj->i2c->EVENTS_ERROR = 0;
obj->i2c->TASKS_STOP = 1;
return I2C_ERROR_NO_SLAVE;
}
return 0;
}
int i2c_read(i2c_t *obj, int address, char *data, int length, int stop)
{
int status, count, errorResult;
obj->i2c->ADDRESS = (address >> 1);
obj->i2c->SHORTS = 1; // to trigger suspend task when a byte is received
obj->i2c->EVENTS_RXDREADY = 0;
obj->i2c->TASKS_STARTRX = 1;
// Read in all except last byte
for (count = 0; count < (length - 1); count++) {
status = i2c_do_read(obj, &data[count], 0);
if (status) {
errorResult = checkError(obj);
i2c_reset(obj);
if (errorResult<0) {
return errorResult;
}
return count;
}
}
// read in last byte
status = i2c_do_read(obj, &data[length - 1], 1);
if (status) {
i2c_reset(obj);
return length - 1;
}
// If not repeated start, send stop.
if (stop) {
while (!obj->i2c->EVENTS_STOPPED) {
}
obj->i2c->EVENTS_STOPPED = 0;
}
return length;
}
int i2c_write(i2c_t *obj, int address, const char *data, int length, int stop)
{
int status, errorResult;
obj->i2c->ADDRESS = (address >> 1);
obj->i2c->SHORTS = 0;
obj->i2c->TASKS_STARTTX = 1;
for (int i = 0; i<length; i++) {
status = i2c_do_write(obj, data[i]);
if (status) {
i2c_reset(obj);
errorResult = checkError(obj);
if (errorResult<0) {
return errorResult;
}
return i;
}
}
// If not repeated start, send stop.
if (stop) {
if (i2c_stop(obj)) {
return I2C_ERROR_NO_SLAVE;
}
}
return length;
}
int i2c_byte_read(i2c_t *obj, int last)
{
char data;
int status;
status = i2c_do_read(obj, &data, last);
if (status) {
i2c_reset(obj);
}
return data;
}
int i2c_byte_write(i2c_t *obj, int data)
{
int status = 0;
if (!obj->address_set) {
obj->address_set = 1;
obj->i2c->ADDRESS = (data >> 1);
if (data & 1) {
obj->i2c->EVENTS_RXDREADY = 0;
obj->i2c->SHORTS = 1;
obj->i2c->TASKS_STARTRX = 1;
} else {
obj->i2c->SHORTS = 0;
obj->i2c->TASKS_STARTTX = 1;
}
} else {
status = i2c_do_write(obj, data);
if (status) {
i2c_reset(obj);
}
}
return (1 - status);
}

View File

@ -19,331 +19,51 @@
#include "cmsis.h"
#include "pinmap.h"
#define NO_PWMS 3
#define TIMER_PRECISION 4 //4us ticks
#define TIMER_PRESCALER 6 //4us ticks = 16Mhz/(2**6)
static const PinMap PinMap_PWM[] = {
{p0, PWM_1, 1},
{p1, PWM_1, 1},
{p2, PWM_1, 1},
{p3, PWM_1, 1},
{p4, PWM_1, 1},
{p5, PWM_1, 1},
{p6, PWM_1, 1},
{p7, PWM_1, 1},
{p8, PWM_1, 1},
{p9, PWM_1, 1},
{p10, PWM_1, 1},
{p11, PWM_1, 1},
{p12, PWM_1, 1},
{p13, PWM_1, 1},
{p14, PWM_1, 1},
{p15, PWM_1, 1},
{p16, PWM_1, 1},
{p17, PWM_1, 1},
{p18, PWM_1, 1},
{p19, PWM_1, 1},
{p20, PWM_1, 1},
{p21, PWM_1, 1},
{p22, PWM_1, 1},
{p23, PWM_1, 1},
{p24, PWM_1, 1},
{p25, PWM_1, 1},
{p28, PWM_1, 1},
{p29, PWM_1, 1},
{p30, PWM_1, 1},
{NC, NC, 0}
};
#if DEVICE_PWMOUT
static NRF_TIMER_Type *Timers[1] = {
NRF_TIMER2
};
// TODO - provide an implementation.
uint16_t PERIOD = 20000 / TIMER_PRECISION; //20ms
uint8_t PWM_taken[NO_PWMS] = {0, 0, 0};
uint16_t PULSE_WIDTH[NO_PWMS] = {1, 1, 1}; //set to 1 instead of 0
uint16_t ACTUAL_PULSE[NO_PWMS] = {0, 0, 0};
/** @brief Function for handling timer 2 peripheral interrupts.
*/
#ifdef __cplusplus
extern "C" {
#endif
void TIMER2_IRQHandler(void)
{
NRF_TIMER2->EVENTS_COMPARE[3] = 0;
NRF_TIMER2->CC[3] = PERIOD;
if (PWM_taken[0]) {
NRF_TIMER2->CC[0] = PULSE_WIDTH[0];
}
if (PWM_taken[1]) {
NRF_TIMER2->CC[1] = PULSE_WIDTH[1];
}
if (PWM_taken[2]) {
NRF_TIMER2->CC[2] = PULSE_WIDTH[2];
}
NRF_TIMER2->TASKS_START = 1;
}
#ifdef __cplusplus
}
#endif
/** @brief Function for initializing the Timer peripherals.
*/
void timer_init(uint8_t pwmChoice)
{
NRF_TIMER_Type *timer = Timers[0];
timer->TASKS_STOP = 0;
if (pwmChoice == 0) {
// TODO: understand this for nrf52
// timer->POWER = 0;
// timer->POWER = 1;
timer->MODE = TIMER_MODE_MODE_Timer;
timer->BITMODE = TIMER_BITMODE_BITMODE_16Bit << TIMER_BITMODE_BITMODE_Pos;
timer->PRESCALER = TIMER_PRESCALER;
timer->CC[3] = PERIOD;
}
timer->CC[pwmChoice] = PULSE_WIDTH[pwmChoice];
//high priority application interrupt
NVIC_SetPriority(TIMER2_IRQn, 1);
NVIC_EnableIRQ(TIMER2_IRQn);
timer->TASKS_START = 0x01;
}
/** @brief Function for initializing the GPIO Tasks/Events peripheral.
*/
void gpiote_init(PinName pin, uint8_t channel_number)
{
// Connect GPIO input buffers and configure PWM_OUTPUT_PIN_NUMBER as an output.
NRF_GPIO->PIN_CNF[pin] = (GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos)
| (GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos)
| (GPIO_PIN_CNF_PULL_Disabled << GPIO_PIN_CNF_PULL_Pos)
| (GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos)
| (GPIO_PIN_CNF_DIR_Output << GPIO_PIN_CNF_DIR_Pos);
NRF_GPIO->OUTCLR = (1UL << pin);
// Configure GPIOTE channel 0 to toggle the PWM pin state
// @note Only one GPIOTE task can be connected to an output pin.
/* Configure channel to Pin31, not connected to the pin, and configure as a tasks that will set it to proper level */
NRF_GPIOTE->CONFIG[channel_number] = (GPIOTE_CONFIG_MODE_Task << GPIOTE_CONFIG_MODE_Pos) |
(31UL << GPIOTE_CONFIG_PSEL_Pos) |
(GPIOTE_CONFIG_POLARITY_HiToLo << GPIOTE_CONFIG_POLARITY_Pos);
/* Three NOPs are required to make sure configuration is written before setting tasks or getting events */
__NOP();
__NOP();
__NOP();
/* Launch the task to take the GPIOTE channel output to the desired level */
NRF_GPIOTE->TASKS_OUT[channel_number] = 1;
/* Finally configure the channel as the caller expects. If OUTINIT works, the channel is configured properly.
If it does not, the channel output inheritance sets the proper level. */
NRF_GPIOTE->CONFIG[channel_number] = (GPIOTE_CONFIG_MODE_Task << GPIOTE_CONFIG_MODE_Pos) |
((uint32_t)pin << GPIOTE_CONFIG_PSEL_Pos) |
((uint32_t)GPIOTE_CONFIG_POLARITY_Toggle << GPIOTE_CONFIG_POLARITY_Pos) |
((uint32_t)GPIOTE_CONFIG_OUTINIT_Low << GPIOTE_CONFIG_OUTINIT_Pos); // ((uint32_t)GPIOTE_CONFIG_OUTINIT_High <<
// GPIOTE_CONFIG_OUTINIT_Pos);//
/* Three NOPs are required to make sure configuration is written before setting tasks or getting events */
__NOP();
__NOP();
__NOP();
}
/** @brief Function for initializing the Programmable Peripheral Interconnect peripheral.
*/
static void ppi_init(uint8_t pwm)
{
//using ppi channels 0-7 (only 0-7 are available)
uint8_t channel_number = 2 * pwm;
NRF_TIMER_Type *timer = Timers[0];
// Configure PPI channel 0 to toggle ADVERTISING_LED_PIN_NO on every TIMER1 COMPARE[0] match
NRF_PPI->CH[channel_number].TEP = (uint32_t)&NRF_GPIOTE->TASKS_OUT[pwm];
NRF_PPI->CH[channel_number + 1].TEP = (uint32_t)&NRF_GPIOTE->TASKS_OUT[pwm];
NRF_PPI->CH[channel_number].EEP = (uint32_t)&timer->EVENTS_COMPARE[pwm];
NRF_PPI->CH[channel_number + 1].EEP = (uint32_t)&timer->EVENTS_COMPARE[3];
// Enable PPI channels.
NRF_PPI->CHEN |= (1 << channel_number) |
(1 << (channel_number + 1));
}
void setModulation(pwmout_t *obj, uint8_t toggle, uint8_t high)
{
if (high) {
NRF_GPIOTE->CONFIG[obj->pwm] |= ((uint32_t)GPIOTE_CONFIG_OUTINIT_High << GPIOTE_CONFIG_OUTINIT_Pos);
if (toggle) {
NRF_GPIOTE->CONFIG[obj->pwm] |= (GPIOTE_CONFIG_MODE_Task << GPIOTE_CONFIG_MODE_Pos) |
((uint32_t)GPIOTE_CONFIG_POLARITY_Toggle << GPIOTE_CONFIG_POLARITY_Pos);
} else {
NRF_GPIOTE->CONFIG[obj->pwm] &= ~((uint32_t)GPIOTE_CONFIG_POLARITY_Toggle << GPIOTE_CONFIG_POLARITY_Pos);
NRF_GPIOTE->CONFIG[obj->pwm] |= ((uint32_t)GPIOTE_CONFIG_POLARITY_LoToHi << GPIOTE_CONFIG_POLARITY_Pos);
}
} else {
NRF_GPIOTE->CONFIG[obj->pwm] &= ~((uint32_t)GPIOTE_CONFIG_OUTINIT_High << GPIOTE_CONFIG_OUTINIT_Pos);
if (toggle) {
NRF_GPIOTE->CONFIG[obj->pwm] |= (GPIOTE_CONFIG_MODE_Task << GPIOTE_CONFIG_MODE_Pos) |
((uint32_t)GPIOTE_CONFIG_POLARITY_Toggle << GPIOTE_CONFIG_POLARITY_Pos);
} else {
NRF_GPIOTE->CONFIG[obj->pwm] &= ~((uint32_t)GPIOTE_CONFIG_POLARITY_Toggle << GPIOTE_CONFIG_POLARITY_Pos);
NRF_GPIOTE->CONFIG[obj->pwm] |= ((uint32_t)GPIOTE_CONFIG_POLARITY_HiToLo << GPIOTE_CONFIG_POLARITY_Pos);
}
}
}
//#include "nrf_pwm.h"
void pwmout_init(pwmout_t *obj, PinName pin)
{
// determine the channel
uint8_t pwmOutSuccess = 0;
PWMName pwm = (PWMName)pinmap_peripheral(pin, PinMap_PWM);
MBED_ASSERT(pwm != (PWMName)NC);
if (PWM_taken[(uint8_t)pwm]) {
for (uint8_t i = 1; !pwmOutSuccess && (i<NO_PWMS); i++) {
if (!PWM_taken[i]) {
pwm = (PWMName)i;
PWM_taken[i] = 1;
pwmOutSuccess = 1;
}
}
} else {
pwmOutSuccess = 1;
PWM_taken[(uint8_t)pwm] = 1;
}
if (!pwmOutSuccess) {
error("PwmOut pin mapping failed. All available PWM channels are in use.");
}
obj->pwm = pwm;
obj->pin = pin;
gpiote_init(pin, (uint8_t)pwm);
ppi_init((uint8_t)pwm);
if (pwm == 0) {
NRF_POWER->TASKS_CONSTLAT = 1;
}
timer_init((uint8_t)pwm);
//default to 20ms: standard for servos, and fine for e.g. brightness control
pwmout_period_ms(obj, 20);
pwmout_write (obj, 0);
}
void pwmout_free(pwmout_t *obj)
{
MBED_ASSERT(obj->pwm != (PWMName)NC);
PWM_taken[obj->pwm] = 0;
pwmout_write(obj, 0);
}
void pwmout_write(pwmout_t *obj, float value)
void pwmout_write(pwmout_t *obj, float percent)
{
uint16_t oldPulseWidth;
NRF_TIMER2->EVENTS_COMPARE[3] = 0;
NRF_TIMER2->TASKS_STOP = 1;
if (value < 0.0f) {
value = 0.0;
} else if (value > 1.0f) {
value = 1.0;
}
oldPulseWidth = ACTUAL_PULSE[obj->pwm];
ACTUAL_PULSE[obj->pwm] = PULSE_WIDTH[obj->pwm] = value * PERIOD;
if (PULSE_WIDTH[obj->pwm] == 0) {
PULSE_WIDTH[obj->pwm] = 1;
setModulation(obj, 0, 0);
} else if (PULSE_WIDTH[obj->pwm] == PERIOD) {
PULSE_WIDTH[obj->pwm] = PERIOD - 1;
setModulation(obj, 0, 1);
} else if ((oldPulseWidth == 0) || (oldPulseWidth == PERIOD)) {
setModulation(obj, 1, oldPulseWidth == PERIOD);
}
NRF_TIMER2->INTENSET = TIMER_INTENSET_COMPARE3_Msk;
NRF_TIMER2->SHORTS = TIMER_SHORTS_COMPARE3_CLEAR_Msk | TIMER_SHORTS_COMPARE3_STOP_Msk;
NRF_TIMER2->TASKS_START = 1;
}
float pwmout_read(pwmout_t *obj)
{
return ((float)PULSE_WIDTH[obj->pwm] / (float)PERIOD);
return 0.0f;
}
void pwmout_period(pwmout_t *obj, float seconds)
{
pwmout_period_us(obj, seconds * 1000000.0f);
}
void pwmout_period_ms(pwmout_t *obj, int ms)
{
pwmout_period_us(obj, ms * 1000);
}
// Set the PWM period, keeping the duty cycle the same.
void pwmout_period_us(pwmout_t *obj, int us)
{
(void) obj; // Avoid compiler warnings
uint32_t periodInTicks = us / TIMER_PRECISION;
NRF_TIMER2->EVENTS_COMPARE[3] = 0;
NRF_TIMER2->TASKS_STOP = 1;
if (periodInTicks>((1 << 16) - 1)) {
PERIOD = (1 << 16) - 1; //131ms
} else if (periodInTicks<5) {
PERIOD = 5;
} else {
PERIOD = periodInTicks;
}
NRF_TIMER2->INTENSET = TIMER_INTENSET_COMPARE3_Msk;
NRF_TIMER2->SHORTS = TIMER_SHORTS_COMPARE3_CLEAR_Msk | TIMER_SHORTS_COMPARE3_STOP_Msk;
NRF_TIMER2->TASKS_START = 1;
}
void pwmout_pulsewidth(pwmout_t *obj, float seconds)
{
pwmout_pulsewidth_us(obj, seconds * 1000000.0f);
}
void pwmout_pulsewidth_ms(pwmout_t *obj, int ms)
{
pwmout_pulsewidth_us(obj, ms * 1000);
}
void pwmout_pulsewidth_us(pwmout_t *obj, int us)
{
uint32_t pulseInTicks = us / TIMER_PRECISION;
uint16_t oldPulseWidth = ACTUAL_PULSE[obj->pwm];
NRF_TIMER2->EVENTS_COMPARE[3] = 0;
NRF_TIMER2->TASKS_STOP = 1;
ACTUAL_PULSE[obj->pwm] = PULSE_WIDTH[obj->pwm] = pulseInTicks;
if (PULSE_WIDTH[obj->pwm] == 0) {
PULSE_WIDTH[obj->pwm] = 1;
setModulation(obj, 0, 0);
} else if (PULSE_WIDTH[obj->pwm] == PERIOD) {
PULSE_WIDTH[obj->pwm] = PERIOD - 1;
setModulation(obj, 0, 1);
} else if ((oldPulseWidth == 0) || (oldPulseWidth == PERIOD)) {
setModulation(obj, 1, oldPulseWidth == PERIOD);
}
NRF_TIMER2->INTENSET = TIMER_INTENSET_COMPARE3_Msk;
NRF_TIMER2->SHORTS = TIMER_SHORTS_COMPARE3_CLEAR_Msk | TIMER_SHORTS_COMPARE3_STOP_Msk;
NRF_TIMER2->TASKS_START = 1;
}
#endif // DEVICE_PWMOUT

View File

@ -0,0 +1,488 @@
/*
* Copyright (c) Nordic Semiconductor ASA
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this
* list of conditions and the following disclaimer in the documentation and/or
* other materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of other
* contributors to this software may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef NRF_DRV_CONFIG_H
#define NRF_DRV_CONFIG_H
/**
* Provide a non-zero value here in applications that need to use several
* peripherals with the same ID that are sharing certain resources
* (for example, SPI0 and TWI0). Obviously, such peripherals cannot be used
* simultaneously. Therefore, this definition allows to initialize the driver
* for another peripheral from a given group only after the previously used one
* is uninitialized. Normally, this is not possible, because interrupt handlers
* are implemented in individual drivers.
* This functionality requires a more complicated interrupt handling and driver
* initialization, hence it is not always desirable to use it.
*/
#define PERIPHERAL_RESOURCE_SHARING_ENABLED 1
/* CLOCK */
#define CLOCK_ENABLED 1
#if (CLOCK_ENABLED == 1)
#define CLOCK_CONFIG_XTAL_FREQ NRF_CLOCK_XTALFREQ_Default
#define CLOCK_CONFIG_LF_SRC NRF_CLOCK_LFCLK_Xtal
#define CLOCK_CONFIG_IRQ_PRIORITY APP_IRQ_PRIORITY_LOW
#endif
/* GPIOTE */
#define GPIOTE_ENABLED 1
#if (GPIOTE_ENABLED == 1)
#define GPIOTE_CONFIG_USE_SWI_EGU false
#define GPIOTE_CONFIG_IRQ_PRIORITY APP_IRQ_PRIORITY_LOW
#define GPIOTE_CONFIG_NUM_OF_LOW_POWER_EVENTS 8
#endif
/* TIMER */
#ifdef SOFTDEVICE_PRESENT
#define TIMER0_ENABLED 0
#else
#define TIMER0_ENABLED 1
#endif
#if (TIMER0_ENABLED == 1)
#define TIMER0_CONFIG_FREQUENCY NRF_TIMER_FREQ_16MHz
#define TIMER0_CONFIG_MODE TIMER_MODE_MODE_Timer
#define TIMER0_CONFIG_BIT_WIDTH TIMER_BITMODE_BITMODE_32Bit
#define TIMER0_CONFIG_IRQ_PRIORITY APP_IRQ_PRIORITY_LOW
#define TIMER0_INSTANCE_INDEX 0
#endif
#define TIMER1_ENABLED 1
#if (TIMER1_ENABLED == 1)
#define TIMER1_CONFIG_FREQUENCY NRF_TIMER_FREQ_16MHz
#define TIMER1_CONFIG_MODE TIMER_MODE_MODE_Timer
#define TIMER1_CONFIG_BIT_WIDTH TIMER_BITMODE_BITMODE_16Bit
#define TIMER1_CONFIG_IRQ_PRIORITY APP_IRQ_PRIORITY_LOW
#define TIMER1_INSTANCE_INDEX (TIMER0_ENABLED)
#endif
#define TIMER2_ENABLED 1
#if (TIMER2_ENABLED == 1)
#define TIMER2_CONFIG_FREQUENCY NRF_TIMER_FREQ_16MHz
#define TIMER2_CONFIG_MODE TIMER_MODE_MODE_Timer
#define TIMER2_CONFIG_BIT_WIDTH TIMER_BITMODE_BITMODE_16Bit
#define TIMER2_CONFIG_IRQ_PRIORITY APP_IRQ_PRIORITY_LOW
#define TIMER2_INSTANCE_INDEX (TIMER1_ENABLED+TIMER0_ENABLED)
#endif
#define TIMER3_ENABLED 0
#if (TIMER3_ENABLED == 1)
#define TIMER3_CONFIG_FREQUENCY NRF_TIMER_FREQ_16MHz
#define TIMER3_CONFIG_MODE TIMER_MODE_MODE_Timer
#define TIMER3_CONFIG_BIT_WIDTH TIMER_BITMODE_BITMODE_16Bit
#define TIMER3_CONFIG_IRQ_PRIORITY APP_IRQ_PRIORITY_LOW
#define TIMER3_INSTANCE_INDEX (TIMER2_ENABLED+TIMER1_ENABLED+TIMER0_ENABLED)
#endif
#define TIMER4_ENABLED 0
#if (TIMER4_ENABLED == 1)
#define TIMER4_CONFIG_FREQUENCY NRF_TIMER_FREQ_16MHz
#define TIMER4_CONFIG_MODE TIMER_MODE_MODE_Timer
#define TIMER4_CONFIG_BIT_WIDTH TIMER_BITMODE_BITMODE_16Bit
#define TIMER4_CONFIG_IRQ_PRIORITY APP_IRQ_PRIORITY_LOW
#define TIMER4_INSTANCE_INDEX (TIMER3_ENABLED+TIMER2_ENABLED+TIMER1_ENABLED+TIMER0_ENABLED)
#endif
#define TIMER_COUNT (TIMER0_ENABLED + TIMER1_ENABLED + TIMER2_ENABLED + TIMER3_ENABLED + TIMER4_ENABLED)
/* RTC */
#define RTC0_ENABLED 0
#if (RTC0_ENABLED == 1)
#define RTC0_CONFIG_FREQUENCY 32678
#define RTC0_CONFIG_IRQ_PRIORITY APP_IRQ_PRIORITY_LOW
#define RTC0_CONFIG_RELIABLE false
#define RTC0_INSTANCE_INDEX 0
#endif
#define RTC1_ENABLED 1
#if (RTC1_ENABLED == 1)
#define RTC1_CONFIG_FREQUENCY 32768
#define RTC1_CONFIG_IRQ_PRIORITY APP_IRQ_PRIORITY_LOW
#define RTC1_CONFIG_RELIABLE false
#define RTC1_INSTANCE_INDEX (RTC0_ENABLED)
#endif
#define RTC2_ENABLED 0
#if (RTC2_ENABLED == 1)
#define RTC2_CONFIG_FREQUENCY 32768
#define RTC2_CONFIG_IRQ_PRIORITY APP_IRQ_PRIORITY_LOW
#define RTC2_CONFIG_RELIABLE false
#define RTC2_INSTANCE_INDEX (RTC0_ENABLED+RTC1_ENABLED)
#endif
#define RTC_COUNT (RTC0_ENABLED+RTC1_ENABLED+RTC2_ENABLED)
#define NRF_MAXIMUM_LATENCY_US 2000
/* RNG */
#define RNG_ENABLED 0
#if (RNG_ENABLED == 1)
#define RNG_CONFIG_ERROR_CORRECTION true
#define RNG_CONFIG_POOL_SIZE 8
#define RNG_CONFIG_IRQ_PRIORITY APP_IRQ_PRIORITY_LOW
#endif
/* PWM */
#define PWM0_ENABLED 1
#if (PWM0_ENABLED == 1)
#define PWM0_CONFIG_OUT0_PIN 2
#define PWM0_CONFIG_OUT1_PIN 3
#define PWM0_CONFIG_OUT2_PIN 4
#define PWM0_CONFIG_OUT3_PIN 5
#define PWM0_CONFIG_IRQ_PRIORITY APP_IRQ_PRIORITY_LOW
#define PWM0_CONFIG_BASE_CLOCK NRF_PWM_CLK_1MHz
#define PWM0_CONFIG_COUNT_MODE NRF_PWM_MODE_UP
#define PWM0_CONFIG_TOP_VALUE 1000
#define PWM0_CONFIG_LOAD_MODE NRF_PWM_LOAD_COMMON
#define PWM0_CONFIG_STEP_MODE NRF_PWM_STEP_AUTO
#define PWM0_INSTANCE_INDEX 0
#endif
#define PWM1_ENABLED 0
#if (PWM1_ENABLED == 1)
#define PWM1_CONFIG_OUT0_PIN 2
#define PWM1_CONFIG_OUT1_PIN 3
#define PWM1_CONFIG_OUT2_PIN 4
#define PWM1_CONFIG_OUT3_PIN 5
#define PWM1_CONFIG_IRQ_PRIORITY APP_IRQ_PRIORITY_LOW
#define PWM1_CONFIG_BASE_CLOCK NRF_PWM_CLK_1MHz
#define PWM1_CONFIG_COUNT_MODE NRF_PWM_MODE_UP
#define PWM1_CONFIG_TOP_VALUE 1000
#define PWM1_CONFIG_LOAD_MODE NRF_PWM_LOAD_COMMON
#define PWM1_CONFIG_STEP_MODE NRF_PWM_STEP_AUTO
#define PWM1_INSTANCE_INDEX (PWM0_ENABLED)
#endif
#define PWM2_ENABLED 0
#if (PWM2_ENABLED == 1)
#define PWM2_CONFIG_OUT0_PIN 2
#define PWM2_CONFIG_OUT1_PIN 3
#define PWM2_CONFIG_OUT2_PIN 4
#define PWM2_CONFIG_OUT3_PIN 5
#define PWM2_CONFIG_IRQ_PRIORITY APP_IRQ_PRIORITY_LOW
#define PWM2_CONFIG_BASE_CLOCK NRF_PWM_CLK_1MHz
#define PWM2_CONFIG_COUNT_MODE NRF_PWM_MODE_UP
#define PWM2_CONFIG_TOP_VALUE 1000
#define PWM2_CONFIG_LOAD_MODE NRF_PWM_LOAD_COMMON
#define PWM2_CONFIG_STEP_MODE NRF_PWM_STEP_AUTO
#define PWM2_INSTANCE_INDEX (PWM0_ENABLED + PWM1_ENABLED)
#endif
#define PWM_COUNT (PWM0_ENABLED + PWM1_ENABLED + PWM2_ENABLED)
/* SPI */
#define SPI0_ENABLED 1
#if (SPI0_ENABLED == 1)
#define SPI0_USE_EASY_DMA 0
#define SPI0_CONFIG_SCK_PIN 2
#define SPI0_CONFIG_MOSI_PIN 3
#define SPI0_CONFIG_MISO_PIN 4
#define SPI0_CONFIG_IRQ_PRIORITY APP_IRQ_PRIORITY_LOW
#define SPI0_INSTANCE_INDEX 0
#endif
#define SPI1_ENABLED 1
#if (SPI1_ENABLED == 1)
#define SPI1_USE_EASY_DMA 0
#define SPI1_CONFIG_SCK_PIN 2
#define SPI1_CONFIG_MOSI_PIN 3
#define SPI1_CONFIG_MISO_PIN 4
#define SPI1_CONFIG_IRQ_PRIORITY APP_IRQ_PRIORITY_LOW
#define SPI1_INSTANCE_INDEX (SPI0_ENABLED)
#endif
#define SPI2_ENABLED 0
#if (SPI2_ENABLED == 1)
#define SPI2_USE_EASY_DMA 0
#define SPI2_CONFIG_SCK_PIN 2
#define SPI2_CONFIG_MOSI_PIN 3
#define SPI2_CONFIG_MISO_PIN 4
#define SPI2_CONFIG_IRQ_PRIORITY APP_IRQ_PRIORITY_LOW
#define SPI2_INSTANCE_INDEX (SPI0_ENABLED + SPI1_ENABLED)
#endif
#define SPI_COUNT (SPI0_ENABLED + SPI1_ENABLED + SPI2_ENABLED)
/* SPIS */
#define SPIS0_ENABLED 0
#if (SPIS0_ENABLED == 1)
#define SPIS0_CONFIG_SCK_PIN 2
#define SPIS0_CONFIG_MOSI_PIN 3
#define SPIS0_CONFIG_MISO_PIN 4
#define SPIS0_CONFIG_IRQ_PRIORITY APP_IRQ_PRIORITY_LOW
#define SPIS0_INSTANCE_INDEX 0
#endif
#define SPIS1_ENABLED 0
#if (SPIS1_ENABLED == 1)
#define SPIS1_CONFIG_SCK_PIN 2
#define SPIS1_CONFIG_MOSI_PIN 3
#define SPIS1_CONFIG_MISO_PIN 4
#define SPIS1_CONFIG_IRQ_PRIORITY APP_IRQ_PRIORITY_LOW
#define SPIS1_INSTANCE_INDEX SPIS0_ENABLED
#endif
#define SPIS2_ENABLED 0
#if (SPIS2_ENABLED == 1)
#define SPIS2_CONFIG_SCK_PIN 2
#define SPIS2_CONFIG_MOSI_PIN 3
#define SPIS2_CONFIG_MISO_PIN 4
#define SPIS2_CONFIG_IRQ_PRIORITY APP_IRQ_PRIORITY_LOW
#define SPIS2_INSTANCE_INDEX (SPIS0_ENABLED + SPIS1_ENABLED)
#endif
#define SPIS_COUNT (SPIS0_ENABLED + SPIS1_ENABLED + SPIS2_ENABLED)
/* UART */
#define UART0_ENABLED 1
#if (UART0_ENABLED == 1)
#define UART0_CONFIG_HWFC NRF_UART_HWFC_ENABLED
#define UART0_CONFIG_PARITY NRF_UART_PARITY_EXCLUDED
#define UART0_CONFIG_BAUDRATE NRF_UART_BAUDRATE_9600
#define UART0_CONFIG_PSEL_TXD 6
#define UART0_CONFIG_PSEL_RXD 8
#define UART0_CONFIG_PSEL_CTS 7
#define UART0_CONFIG_PSEL_RTS 5
#define UART0_CONFIG_IRQ_PRIORITY APP_IRQ_PRIORITY_HIGH
#ifdef NRF52
#define UART0_CONFIG_USE_EASY_DMA false
//Compile time flag
#define UART_EASY_DMA_SUPPORT 1
#define UART_LEGACY_SUPPORT 1
#endif //NRF52
#endif
#define TWI0_ENABLED 1
#if (TWI0_ENABLED == 1)
#define TWI0_USE_EASY_DMA 0
#define TWI0_CONFIG_FREQUENCY NRF_TWI_FREQ_100K
#define TWI0_CONFIG_SCL 0
#define TWI0_CONFIG_SDA 1
#define TWI0_CONFIG_IRQ_PRIORITY APP_IRQ_PRIORITY_LOW
#define TWI0_INSTANCE_INDEX 0
#endif
#define TWI1_ENABLED 1
#if (TWI1_ENABLED == 1)
#define TWI1_USE_EASY_DMA 0
#define TWI1_CONFIG_FREQUENCY NRF_TWI_FREQ_100K
#define TWI1_CONFIG_SCL 0
#define TWI1_CONFIG_SDA 1
#define TWI1_CONFIG_IRQ_PRIORITY APP_IRQ_PRIORITY_LOW
#define TWI1_INSTANCE_INDEX (TWI0_ENABLED)
#endif
#define TWI_COUNT (TWI0_ENABLED + TWI1_ENABLED)
/* TWIS */
#define TWIS0_ENABLED 0
#if (TWIS0_ENABLED == 1)
#define TWIS0_CONFIG_ADDR0 0
#define TWIS0_CONFIG_ADDR1 0 /* 0: Disabled */
#define TWIS0_CONFIG_SCL 0
#define TWIS0_CONFIG_SDA 1
#define TWIS0_CONFIG_IRQ_PRIORITY APP_IRQ_PRIORITY_LOW
#define TWIS0_INSTANCE_INDEX 0
#endif
#define TWIS1_ENABLED 0
#if (TWIS1_ENABLED == 1)
#define TWIS1_CONFIG_ADDR0 0
#define TWIS1_CONFIG_ADDR1 0 /* 0: Disabled */
#define TWIS1_CONFIG_SCL 0
#define TWIS1_CONFIG_SDA 1
#define TWIS1_CONFIG_IRQ_PRIORITY APP_IRQ_PRIORITY_LOW
#define TWIS1_INSTANCE_INDEX (TWIS0_ENABLED)
#endif
#define TWIS_COUNT (TWIS0_ENABLED + TWIS1_ENABLED)
/* For more documentation see nrf_drv_twis.h file */
#define TWIS_ASSUME_INIT_AFTER_RESET_ONLY 0
/* For more documentation see nrf_drv_twis.h file */
#define TWIS_NO_SYNC_MODE 0
/* QDEC */
#define QDEC_ENABLED 0
#if (QDEC_ENABLED == 1)
#define QDEC_CONFIG_REPORTPER NRF_QDEC_REPORTPER_10
#define QDEC_CONFIG_SAMPLEPER NRF_QDEC_SAMPLEPER_16384us
#define QDEC_CONFIG_PIO_A 1
#define QDEC_CONFIG_PIO_B 2
#define QDEC_CONFIG_PIO_LED 3
#define QDEC_CONFIG_LEDPRE 511
#define QDEC_CONFIG_LEDPOL NRF_QDEC_LEPOL_ACTIVE_HIGH
#define QDEC_CONFIG_IRQ_PRIORITY APP_IRQ_PRIORITY_LOW
#define QDEC_CONFIG_DBFEN false
#define QDEC_CONFIG_SAMPLE_INTEN false
#endif
/* ADC */
#define ADC_ENABLED 0
#if (ADC_ENABLED == 1)
#define ADC_CONFIG_IRQ_PRIORITY APP_IRQ_PRIORITY_LOW
#endif
/* SAADC */
#define SAADC_ENABLED 1
#if (SAADC_ENABLED == 1)
#define SAADC_CONFIG_RESOLUTION NRF_SAADC_RESOLUTION_10BIT
#define SAADC_CONFIG_OVERSAMPLE NRF_SAADC_OVERSAMPLE_DISABLED
#define SAADC_CONFIG_IRQ_PRIORITY APP_IRQ_PRIORITY_LOW
#endif
/* PDM */
#define PDM_ENABLED 0
#if (PDM_ENABLED == 1)
#define PDM_CONFIG_MODE NRF_PDM_MODE_MONO
#define PDM_CONFIG_EDGE NRF_PDM_EDGE_LEFTFALLING
#define PDM_CONFIG_CLOCK_FREQ NRF_PDM_FREQ_1032K
#define PDM_CONFIG_IRQ_PRIORITY APP_IRQ_PRIORITY_LOW
#endif
/* COMP */
#define COMP_ENABLED 0
#if (COMP_ENABLED == 1)
#define COMP_CONFIG_REF NRF_COMP_REF_Int1V8
#define COMP_CONFIG_MAIN_MODE NRF_COMP_MAIN_MODE_SE
#define COMP_CONFIG_SPEED_MODE NRF_COMP_SP_MODE_High
#define COMP_CONFIG_HYST NRF_COMP_HYST_NoHyst
#define COMP_CONFIG_ISOURCE NRF_COMP_ISOURCE_Off
#define COMP_CONFIG_IRQ_PRIORITY APP_IRQ_PRIORITY_LOW
#define COMP_CONFIG_INPUT NRF_COMP_INPUT_0
#endif
/* LPCOMP */
#define LPCOMP_ENABLED 0
#if (LPCOMP_ENABLED == 1)
#define LPCOMP_CONFIG_REFERENCE NRF_LPCOMP_REF_SUPPLY_4_8
#define LPCOMP_CONFIG_DETECTION NRF_LPCOMP_DETECT_DOWN
#define LPCOMP_CONFIG_IRQ_PRIORITY APP_IRQ_PRIORITY_LOW
#define LPCOMP_CONFIG_INPUT NRF_LPCOMP_INPUT_0
#endif
/* WDT */
#define WDT_ENABLED 0
#if (WDT_ENABLED == 1)
#define WDT_CONFIG_BEHAVIOUR NRF_WDT_BEHAVIOUR_RUN_SLEEP
#define WDT_CONFIG_RELOAD_VALUE 2000
#define WDT_CONFIG_IRQ_PRIORITY APP_IRQ_PRIORITY_HIGH
#endif
/* SWI EGU */
#ifdef NRF52
#define EGU_ENABLED 0
#endif
/* I2S */
#define I2S_ENABLED 0
#if (I2S_ENABLED == 1)
#define I2S_CONFIG_SCK_PIN 22
#define I2S_CONFIG_LRCK_PIN 23
#define I2S_CONFIG_MCK_PIN NRF_DRV_I2S_PIN_NOT_USED
#define I2S_CONFIG_SDOUT_PIN 24
#define I2S_CONFIG_SDIN_PIN 25
#define I2S_CONFIG_IRQ_PRIORITY APP_IRQ_PRIORITY_HIGH
#define I2S_CONFIG_MASTER NRF_I2S_MODE_MASTER
#define I2S_CONFIG_FORMAT NRF_I2S_FORMAT_I2S
#define I2S_CONFIG_ALIGN NRF_I2S_ALIGN_LEFT
#define I2S_CONFIG_SWIDTH NRF_I2S_SWIDTH_16BIT
#define I2S_CONFIG_CHANNELS NRF_I2S_CHANNELS_STEREO
#define I2S_CONFIG_MCK_SETUP NRF_I2S_MCK_32MDIV8
#define I2S_CONFIG_RATIO NRF_I2S_RATIO_256X
#endif
#include "nrf_drv_config_validation.h"
#endif // NRF_DRV_CONFIG_H

View File

@ -1,305 +0,0 @@
/* mbed Microcontroller Library
* Copyright (c) 2013 Nordic Semiconductor
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// math.h required for floating point operations for baud rate calculation
//#include <math.h>
#include <string.h>
#include "mbed_assert.h"
#include "serial_api.h"
#include "cmsis.h"
#include "pinmap.h"
/******************************************************************************
* INITIALIZATION
******************************************************************************/
#define UART_NUM 1
static uint32_t serial_irq_ids[UART_NUM] = {0};
static uart_irq_handler irq_handler;
static const uint32_t acceptedSpeeds[17][2] = {
{1200, UART_BAUDRATE_BAUDRATE_Baud1200},
{2400, UART_BAUDRATE_BAUDRATE_Baud2400},
{4800, UART_BAUDRATE_BAUDRATE_Baud4800},
{9600, UART_BAUDRATE_BAUDRATE_Baud9600},
{14400, UART_BAUDRATE_BAUDRATE_Baud14400},
{19200, UART_BAUDRATE_BAUDRATE_Baud19200},
{28800, UART_BAUDRATE_BAUDRATE_Baud28800},
{31250, (0x00800000UL) /* 31250 baud */},
{38400, UART_BAUDRATE_BAUDRATE_Baud38400},
{57600, UART_BAUDRATE_BAUDRATE_Baud57600},
{76800, UART_BAUDRATE_BAUDRATE_Baud76800},
{115200, UART_BAUDRATE_BAUDRATE_Baud115200},
{230400, UART_BAUDRATE_BAUDRATE_Baud230400},
{250000, UART_BAUDRATE_BAUDRATE_Baud250000},
{460800, UART_BAUDRATE_BAUDRATE_Baud460800},
{921600, UART_BAUDRATE_BAUDRATE_Baud921600},
{1000000, UART_BAUDRATE_BAUDRATE_Baud1M}
};
int stdio_uart_inited = 0;
serial_t stdio_uart;
void serial_init(serial_t *obj, PinName tx, PinName rx) {
UARTName uart = UART_0;
obj->uart = (NRF_UART_Type *)uart;
//pin configurations --
NRF_GPIO->DIR |= (1 << tx); //TX_PIN_NUMBER);
NRF_GPIO->DIR |= (1 << RTS_PIN_NUMBER);
NRF_GPIO->DIR &= ~(1 << rx); //RX_PIN_NUMBER);
NRF_GPIO->DIR &= ~(1 << CTS_PIN_NUMBER);
// set default baud rate and format
serial_baud (obj, 9600);
serial_format(obj, 8, ParityNone, 1);
obj->uart->ENABLE = (UART_ENABLE_ENABLE_Enabled << UART_ENABLE_ENABLE_Pos);
obj->uart->TASKS_STARTTX = 1;
obj->uart->TASKS_STARTRX = 1;
obj->uart->EVENTS_RXDRDY = 0;
// dummy write needed or TXDRDY trails write rather than leads write.
// pins are disconnected so nothing is physically transmitted on the wire
obj->uart->TXD = 0;
obj->index = 0;
obj->uart->PSELRTS = RTS_PIN_NUMBER;
obj->uart->PSELTXD = tx; //TX_PIN_NUMBER;
obj->uart->PSELCTS = CTS_PIN_NUMBER;
obj->uart->PSELRXD = rx; //RX_PIN_NUMBER;
// set rx/tx pins in PullUp mode
if (tx != NC) {
pin_mode(tx, PullUp);
}
if (rx != NC) {
pin_mode(rx, PullUp);
}
if (uart == STDIO_UART) {
stdio_uart_inited = 1;
memcpy(&stdio_uart, obj, sizeof(serial_t));
}
}
void serial_free(serial_t *obj)
{
serial_irq_ids[obj->index] = 0;
}
// serial_baud
// set the baud rate, taking in to account the current SystemFrequency
void serial_baud(serial_t *obj, int baudrate)
{
if (baudrate<=1200) {
obj->uart->BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud1200;
return;
}
for (int i = 1; i<17; i++) {
if (baudrate<acceptedSpeeds[i][0]) {
obj->uart->BAUDRATE = acceptedSpeeds[i - 1][1];
return;
}
}
obj->uart->BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud1M;
}
void serial_format(serial_t *obj, int data_bits, SerialParity parity, int stop_bits)
{
/* Avoid compiler warnings */
(void) data_bits;
(void) stop_bits;
// 0: 1 stop bits, 1: 2 stop bits
// int parity_enable, parity_select;
switch (parity) {
case ParityNone:
obj->uart->CONFIG = 0;
break;
default:
obj->uart->CONFIG = (UART_CONFIG_PARITY_Included << UART_CONFIG_PARITY_Pos);
return;
}
//no Flow Control
}
//******************************************************************************
// * INTERRUPT HANDLING
//******************************************************************************
static inline void uart_irq(uint32_t iir, uint32_t index)
{
SerialIrq irq_type;
switch (iir) {
case 1:
irq_type = TxIrq;
break;
case 2:
irq_type = RxIrq;
break;
default:
return;
}
if (serial_irq_ids[index] != 0) {
irq_handler(serial_irq_ids[index], irq_type);
}
}
#ifdef __cplusplus
extern "C" {
#endif
void UART0_IRQHandler()
{
uint32_t irtype = 0;
if((NRF_UART0->INTENSET & 0x80) && NRF_UART0->EVENTS_TXDRDY) {
irtype = 1;
} else if((NRF_UART0->INTENSET & 0x04) && NRF_UART0->EVENTS_RXDRDY) {
irtype = 2;
}
uart_irq(irtype, 0);
}
#ifdef __cplusplus
}
#endif
void serial_irq_handler(serial_t *obj, uart_irq_handler handler, uint32_t id)
{
irq_handler = handler;
serial_irq_ids[obj->index] = id;
}
void serial_irq_set(serial_t *obj, SerialIrq irq, uint32_t enable)
{
IRQn_Type irq_n = (IRQn_Type)0;
switch ((int)obj->uart) {
case UART_0:
irq_n = UART0_IRQn;
break;
}
if (enable) {
switch (irq) {
case RxIrq:
obj->uart->INTENSET = (UART_INTENSET_RXDRDY_Msk);
break;
case TxIrq:
obj->uart->INTENSET = (UART_INTENSET_TXDRDY_Msk);
break;
}
NVIC_SetPriority(irq_n, 3);
NVIC_EnableIRQ(irq_n);
} else { // disable
// maseked writes to INTENSET dont disable and masked writes to
// INTENCLR seemed to clear the entire register, not bits.
// Added INTEN to memory map and seems to allow set and clearing of specific bits as desired
int all_disabled = 0;
switch (irq) {
case RxIrq:
obj->uart->INTENCLR = (UART_INTENCLR_RXDRDY_Msk);
all_disabled = (obj->uart->INTENCLR & (UART_INTENCLR_TXDRDY_Msk)) == 0;
break;
case TxIrq:
obj->uart->INTENCLR = (UART_INTENCLR_TXDRDY_Msk);
all_disabled = (obj->uart->INTENCLR & (UART_INTENCLR_RXDRDY_Msk)) == 0;
break;
}
if (all_disabled) {
NVIC_DisableIRQ(irq_n);
}
}
}
//******************************************************************************
//* READ/WRITE
//******************************************************************************
int serial_getc(serial_t *obj)
{
while (!serial_readable(obj)) {
}
obj->uart->EVENTS_RXDRDY = 0;
return (uint8_t)obj->uart->RXD;
}
void serial_putc(serial_t *obj, int c)
{
while (!serial_writable(obj)) {
}
obj->uart->EVENTS_TXDRDY = 0;
obj->uart->TXD = (uint8_t)c;
}
int serial_readable(serial_t *obj)
{
return (obj->uart->EVENTS_RXDRDY == 1);
}
int serial_writable(serial_t *obj)
{
return (obj->uart->EVENTS_TXDRDY == 1);
}
void serial_break_set(serial_t *obj)
{
obj->uart->TASKS_SUSPEND = 1;
}
void serial_break_clear(serial_t *obj)
{
obj->uart->TASKS_STARTTX = 1;
obj->uart->TASKS_STARTRX = 1;
}
void serial_set_flow_control(serial_t *obj, FlowControl type, PinName rxflow, PinName txflow)
{
if (type == FlowControlRTSCTS || type == FlowControlRTS) {
NRF_GPIO->DIR |= (1<<rxflow);
pin_mode(rxflow, PullUp);
obj->uart->PSELRTS = rxflow;
obj->uart->CONFIG |= 0x01; // Enable HWFC
}
if (type == FlowControlRTSCTS || type == FlowControlCTS) {
NRF_GPIO->DIR &= ~(1<<txflow);
pin_mode(txflow, PullUp);
obj->uart->PSELCTS = txflow;
obj->uart->CONFIG |= 0x01; // Enable HWFC;
}
if (type == FlowControlNone) {
obj->uart->PSELRTS = 0xFFFFFFFF; // Disable RTS
obj->uart->PSELCTS = 0xFFFFFFFF; // Disable CTS
obj->uart->CONFIG &= ~0x01; // Enable HWFC;
}
}
void serial_clear(serial_t *obj) {
/* Avoid compiler warnings */
(void) obj;
}

View File

@ -1,32 +0,0 @@
/* mbed Microcontroller Library
* Copyright (c) 2006-2015 ARM Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "sleep_api.h"
#include "cmsis.h"
#include "mbed_interface.h"
void sleep(void)
{
// ensure debug is disconnected if semihost is enabled....
NRF_POWER->TASKS_LOWPWR = 1;
// wait for interrupt
__WFE();
}
void deepsleep(void)
{
sleep();
// NRF_POWER->SYSTEMOFF=1;
}

View File

@ -1,243 +0,0 @@
/* mbed Microcontroller Library
* Copyright (c) 2013 Nordic Semiconductor
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//#include <math.h>
#include "mbed_assert.h"
#include "mbed_error.h"
#include "spi_api.h"
#include "cmsis.h"
#include "pinmap.h"
#define SPIS_MESSAGE_SIZE 1
volatile uint8_t m_tx_buf[SPIS_MESSAGE_SIZE] = {0};
volatile uint8_t m_rx_buf[SPIS_MESSAGE_SIZE] = {0};
// nRF51822's I2C_0 and SPI_0 (I2C_1, SPI_1 and SPIS1) share the same address.
// They can't be used at the same time. So we use two global variable to track the usage.
// See nRF51822 address information at nRF51822_PS v2.0.pdf - Table 15 Peripheral instance reference
extern volatile i2c_spi_peripheral_t i2c0_spi0_peripheral; // from i2c_api.c
extern volatile i2c_spi_peripheral_t i2c1_spi1_peripheral;
void spi_init(spi_t *obj, PinName mosi, PinName miso, PinName sclk, PinName ssel)
{
SPIName spi = SPI_0; // Initialize to avoid compiler warnings
if (ssel == NC && i2c0_spi0_peripheral.usage == I2C_SPI_PERIPHERAL_FOR_SPI &&
i2c0_spi0_peripheral.sda_mosi == (uint8_t)mosi &&
i2c0_spi0_peripheral.scl_miso == (uint8_t)miso &&
i2c0_spi0_peripheral.sclk == (uint8_t)sclk) {
// The SPI with the same pins is already initialized
spi = SPI_0;
obj->peripheral = 0x1;
} else if (ssel == NC && i2c1_spi1_peripheral.usage == I2C_SPI_PERIPHERAL_FOR_SPI &&
i2c1_spi1_peripheral.sda_mosi == (uint8_t)mosi &&
i2c1_spi1_peripheral.scl_miso == (uint8_t)miso &&
i2c1_spi1_peripheral.sclk == (uint8_t)sclk) {
// The SPI with the same pins is already initialized
spi = SPI_1;
obj->peripheral = 0x2;
} else if (i2c1_spi1_peripheral.usage == 0) {
i2c1_spi1_peripheral.usage = I2C_SPI_PERIPHERAL_FOR_SPI;
i2c1_spi1_peripheral.sda_mosi = (uint8_t)mosi;
i2c1_spi1_peripheral.scl_miso = (uint8_t)miso;
i2c1_spi1_peripheral.sclk = (uint8_t)sclk;
spi = SPI_1;
obj->peripheral = 0x2;
} else if (i2c0_spi0_peripheral.usage == 0) {
i2c0_spi0_peripheral.usage = I2C_SPI_PERIPHERAL_FOR_SPI;
i2c0_spi0_peripheral.sda_mosi = (uint8_t)mosi;
i2c0_spi0_peripheral.scl_miso = (uint8_t)miso;
i2c0_spi0_peripheral.sclk = (uint8_t)sclk;
spi = SPI_0;
obj->peripheral = 0x1;
} else {
// No available peripheral
error("No available SPI");
}
if (ssel==NC) {
obj->spi = (NRF_SPI_Type *)spi;
obj->spis = (NRF_SPIS_Type *)NC;
} else {
obj->spi = (NRF_SPI_Type *)NC;
obj->spis = (NRF_SPIS_Type *)spi;
}
//master
// TODO: understand implication of this on nrf52
// obj->spi->POWER = 0;
// obj->spi->POWER = 1;
//NRF_GPIO->DIR |= (1<<mosi);
NRF_GPIO->PIN_CNF[mosi] = (GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos)
| (GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos)
| (GPIO_PIN_CNF_PULL_Disabled << GPIO_PIN_CNF_PULL_Pos)
| (GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos)
| (GPIO_PIN_CNF_DIR_Output << GPIO_PIN_CNF_DIR_Pos);
obj->spi->PSELMOSI = mosi;
NRF_GPIO->PIN_CNF[sclk] = (GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos)
| (GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos)
| (GPIO_PIN_CNF_PULL_Disabled << GPIO_PIN_CNF_PULL_Pos)
| (GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos)
| (GPIO_PIN_CNF_DIR_Output << GPIO_PIN_CNF_DIR_Pos);
obj->spi->PSELSCK = sclk;
//NRF_GPIO->DIR &= ~(1<<miso);
NRF_GPIO->PIN_CNF[miso] = (GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos)
| (GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos)
| (GPIO_PIN_CNF_PULL_Disabled << GPIO_PIN_CNF_PULL_Pos)
| (GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos)
| (GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos);
obj->spi->PSELMISO = miso;
obj->spi->EVENTS_READY = 0U;
// spi_format(obj, 8, 0, SPI_MSB); // 8 bits, mode 0, master
spi_frequency(obj, 1000000);
}
void spi_free(spi_t *obj)
{
(void) obj; // Avoid compiler warnings
}
static inline void spi_disable(spi_t *obj, int slave)
{
obj->spi->ENABLE = (SPI_ENABLE_ENABLE_Disabled << SPI_ENABLE_ENABLE_Pos);
}
static inline void spi_enable(spi_t *obj, int slave)
{
obj->spi->ENABLE = (SPI_ENABLE_ENABLE_Enabled << SPI_ENABLE_ENABLE_Pos);
}
void spi_format(spi_t *obj, int bits, int mode, int slave)
{
uint32_t config_mode = 0;
spi_disable(obj, slave);
if (bits != 8) {
error("Only 8bits SPI supported");
}
switch (mode) {
case 0:
config_mode = (SPI_CONFIG_CPHA_Leading << SPI_CONFIG_CPHA_Pos) | (SPI_CONFIG_CPOL_ActiveHigh << SPI_CONFIG_CPOL_Pos);
break;
case 1:
config_mode = (SPI_CONFIG_CPHA_Trailing << SPI_CONFIG_CPHA_Pos) | (SPI_CONFIG_CPOL_ActiveHigh << SPI_CONFIG_CPOL_Pos);
break;
case 2:
config_mode = (SPI_CONFIG_CPHA_Leading << SPI_CONFIG_CPHA_Pos) | (SPI_CONFIG_CPOL_ActiveLow << SPI_CONFIG_CPOL_Pos);
break;
case 3:
config_mode = (SPI_CONFIG_CPHA_Trailing << SPI_CONFIG_CPHA_Pos) | (SPI_CONFIG_CPOL_ActiveLow << SPI_CONFIG_CPOL_Pos);
break;
default:
error("SPI format error");
break;
}
//obj->spi->CONFIG = (config_mode | (((order == SPI_MSB) ? SPI_CONFIG_ORDER_MsbFirst : SPI_CONFIG_ORDER_LsbFirst) << SPI_CONFIG_ORDER_Pos));
//default to msb first
if (slave) {
obj->spis->CONFIG = (config_mode | (SPI_CONFIG_ORDER_MsbFirst << SPI_CONFIG_ORDER_Pos));
} else {
obj->spi->CONFIG = (config_mode | (SPI_CONFIG_ORDER_MsbFirst << SPI_CONFIG_ORDER_Pos));
}
spi_enable(obj, slave);
}
void spi_frequency(spi_t *obj, int hz)
{
if ((int)obj->spi==NC) {
return;
}
spi_disable(obj, 0);
if (hz<250000) { //125Kbps
obj->spi->FREQUENCY = (uint32_t) SPI_FREQUENCY_FREQUENCY_K125;
} else if (hz<500000) { //250Kbps
obj->spi->FREQUENCY = (uint32_t) SPI_FREQUENCY_FREQUENCY_K250;
} else if (hz<1000000) { //500Kbps
obj->spi->FREQUENCY = (uint32_t) SPI_FREQUENCY_FREQUENCY_K500;
} else if (hz<2000000) { //1Mbps
obj->spi->FREQUENCY = (uint32_t) SPI_FREQUENCY_FREQUENCY_M1;
} else if (hz<4000000) { //2Mbps
obj->spi->FREQUENCY = (uint32_t) SPI_FREQUENCY_FREQUENCY_M2;
} else if (hz<8000000) { //4Mbps
obj->spi->FREQUENCY = (uint32_t) SPI_FREQUENCY_FREQUENCY_M4;
} else { //8Mbps
obj->spi->FREQUENCY = (uint32_t) SPI_FREQUENCY_FREQUENCY_M8;
}
spi_enable(obj, 0);
}
static inline int spi_readable(spi_t *obj)
{
return (obj->spi->EVENTS_READY == 1);
}
static inline int spi_writeable(spi_t *obj)
{
return (obj->spi->EVENTS_READY == 0);
}
static inline int spi_read(spi_t *obj)
{
while (!spi_readable(obj)) {
}
obj->spi->EVENTS_READY = 0;
return (int)obj->spi->RXD;
}
int spi_master_write(spi_t *obj, int value)
{
while (!spi_writeable(obj)) {
}
obj->spi->TXD = (uint32_t)value;
return spi_read(obj);
}
//static inline int spis_writeable(spi_t *obj) {
// return (obj->spis->EVENTS_ACQUIRED==1);
//}
int spi_slave_receive(spi_t *obj)
{
return obj->spis->EVENTS_END;
}
int spi_slave_read(spi_t *obj)
{
(void) obj; // Avoid compiler warnings
return m_rx_buf[0];
}
void spi_slave_write(spi_t *obj, int value)
{
m_tx_buf[0] = value & 0xFF;
obj->spis->TASKS_RELEASE = 1;
obj->spis->EVENTS_ACQUIRED = 0;
obj->spis->EVENTS_END = 0;
}

View File

@ -1,277 +0,0 @@
/* mbed Microcontroller Library
* Copyright (c) 2013 Nordic Semiconductor
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stddef.h>
#include <stdbool.h>
#include "us_ticker_api.h"
#include "cmsis.h"
#include "PeripheralNames.h"
#include "nrf_delay.h"
/*
* Note: The micro-second timer API on the nRF51 platform is implemented using
* the RTC counter run at 32kHz (sourced from an external oscillator). This is
* a trade-off between precision and power. Running a normal 32-bit MCU counter
* at high frequency causes the average power consumption to rise to a few
* hundred micro-amps, which is prohibitive for typical low-power BLE
* applications.
* A 32kHz clock doesn't offer the precision needed for keeping u-second time,
* but we're assuming that this will not be a problem for the average user.
*/
#define MAX_RTC_COUNTER_VAL 0x00FFFFFF /**< Maximum value of the RTC counter. */
#define RTC_CLOCK_FREQ (uint32_t)(32768)
#define RTC1_IRQ_PRI 3 /**< Priority of the RTC1 interrupt (used
* for checking for timeouts and executing
* timeout handlers). This must be the same
* as APP_IRQ_PRIORITY_LOW; taken from the
* Nordic SDK. */
#define MAX_RTC_TASKS_DELAY 47 /**< Maximum delay until an RTC task is executed. */
#define FUZZY_RTC_TICKS 2 /* RTC COMPARE occurs when a CC register is N and the RTC
* COUNTER value transitions from N-1 to N. If we're trying to
* setup a callback for a time which will arrive very shortly,
* there are limits to how short the callback interval may be for us
* to rely upon the RTC Compare trigger. If the COUNTER is N,
* writing N+2 to a CC register is guaranteed to trigger a COMPARE
* event at N+2. */
#define RTC_UNITS_TO_MICROSECONDS(RTC_UNITS) (((RTC_UNITS) * (uint64_t)1000000) / RTC_CLOCK_FREQ)
#define MICROSECONDS_TO_RTC_UNITS(MICROS) ((((uint64_t)(MICROS) * RTC_CLOCK_FREQ) + 999999) / 1000000)
static bool us_ticker_inited = false;
static volatile bool us_ticker_callbackPending = false;
static uint32_t us_ticker_callbackTimestamp;
volatile uint32_t overflowCount; /**< The number of times the 24-bit RTC counter has overflowed. */
static inline void rtc1_enableCompareInterrupt(void)
{
NRF_RTC1->EVTENCLR = RTC_EVTEN_COMPARE0_Msk;
NRF_RTC1->INTENSET = RTC_INTENSET_COMPARE0_Msk;
}
static inline void rtc1_disableCompareInterrupt(void)
{
NRF_RTC1->INTENCLR = RTC_INTENSET_COMPARE0_Msk;
NRF_RTC1->EVTENCLR = RTC_EVTEN_COMPARE0_Msk;
}
static inline void rtc1_enableOverflowInterrupt(void)
{
NRF_RTC1->EVTENCLR = RTC_EVTEN_OVRFLW_Msk;
NRF_RTC1->INTENSET = RTC_INTENSET_OVRFLW_Msk;
}
static inline void rtc1_disableOverflowInterrupt(void)
{
NRF_RTC1->INTENCLR = RTC_INTENSET_OVRFLW_Msk;
NRF_RTC1->EVTENCLR = RTC_EVTEN_OVRFLW_Msk;
}
static inline void invokeCallback(void)
{
us_ticker_callbackPending = false;
rtc1_disableCompareInterrupt();
us_ticker_irq_handler();
}
/**
* @brief Function for starting the RTC1 timer. The RTC timer is expected to
* keep running--some interrupts may be disabled temporarily.
*/
static void rtc1_start()
{
NRF_RTC1->PRESCALER = 0; /* for no pre-scaling. */
rtc1_enableOverflowInterrupt();
NVIC_SetPriority(RTC1_IRQn, RTC1_IRQ_PRI);
NVIC_ClearPendingIRQ(RTC1_IRQn);
NVIC_EnableIRQ(RTC1_IRQn);
NRF_RTC1->TASKS_START = 1;
nrf_delay_us(MAX_RTC_TASKS_DELAY);
}
/**
* @brief Function for stopping the RTC1 timer. We don't expect to call this.
*/
void rtc1_stop(void)
{
NVIC_DisableIRQ(RTC1_IRQn);
rtc1_disableCompareInterrupt();
rtc1_disableOverflowInterrupt();
NRF_RTC1->TASKS_STOP = 1;
nrf_delay_us(MAX_RTC_TASKS_DELAY);
NRF_RTC1->TASKS_CLEAR = 1;
nrf_delay_us(MAX_RTC_TASKS_DELAY);
}
/**
* @brief Function for returning the current value of the RTC1 counter.
*
* @return Current RTC1 counter as a 64-bit value with 56-bit precision (even
* though the underlying counter is 24-bit)
*/
static inline uint64_t rtc1_getCounter64(void)
{
if (NRF_RTC1->EVENTS_OVRFLW) {
overflowCount++;
NRF_RTC1->EVENTS_OVRFLW = 0;
NRF_RTC1->EVTENCLR = RTC_EVTEN_OVRFLW_Msk;
}
return ((uint64_t)overflowCount << 24) | NRF_RTC1->COUNTER;
}
/**
* @brief Function for returning the current value of the RTC1 counter.
*
* @return Current RTC1 counter as a 32-bit value (even though the underlying counter is 24-bit)
*/
uint32_t rtc1_getCounter(void)
{
return rtc1_getCounter64();
}
/**
* @brief Function for handling the RTC1 interrupt.
*
* @details Checks for timeouts, and executes timeout handlers for expired timers.
*/
void RTC1_IRQHandler(void)
{
if (NRF_RTC1->EVENTS_OVRFLW) {
overflowCount++;
NRF_RTC1->EVENTS_OVRFLW = 0;
NRF_RTC1->EVTENCLR = RTC_EVTEN_OVRFLW_Msk;
}
if (NRF_RTC1->EVENTS_COMPARE[0] && us_ticker_callbackPending && ((int)(us_ticker_callbackTimestamp - rtc1_getCounter()) <= 0)) {
NRF_RTC1->EVENTS_COMPARE[0] = 0;
NRF_RTC1->EVTENCLR = RTC_EVTEN_COMPARE0_Msk;
invokeCallback();
}
if (NRF_RTC1->EVENTS_COMPARE[1]) {
// Compare[1] used by lp ticker
NRF_RTC1->EVENTS_COMPARE[1] = 0;
NRF_RTC1->EVTENCLR = RTC_EVTEN_COMPARE1_Msk;
}
}
void us_ticker_init(void)
{
if (us_ticker_inited) {
return;
}
rtc1_start();
us_ticker_inited = true;
}
uint32_t us_ticker_read()
{
if (!us_ticker_inited) {
us_ticker_init();
}
/* Return a pseudo microsecond counter value. This is only as precise as the
* 32khz low-freq clock source, but could be adequate.*/
return RTC_UNITS_TO_MICROSECONDS(rtc1_getCounter64());
}
/**
* Setup the us_ticker callback interrupt to go at the given timestamp.
*
* @Note: Only one callback is pending at any time.
*
* @Note: If a callback is pending, and this function is called again, the new
* callback-time overrides the existing callback setting. It is the caller's
* responsibility to ensure that this function is called to setup a callback for
* the earliest timeout.
*
* @Note: If this function is used to setup an interrupt which is immediately
* pending--such as for 'now' or a time in the past,--then the callback is
* invoked a few ticks later.
*/
void us_ticker_set_interrupt(timestamp_t timestamp)
{
if (!us_ticker_inited) {
us_ticker_init();
}
/*
* The argument to this function is a 32-bit microsecond timestamp for when
* a callback should be invoked. On the nRF51, we use an RTC timer running
* at 32kHz to implement a low-power us-ticker. This results in a problem
* based on the fact that 1000000 is not a multiple of 32768.
*
* Going from a micro-second based timestamp to a 32kHz based RTC-time is a
* linear mapping; but this mapping doesn't preserve wraparounds--i.e. when
* the 32-bit micro-second timestamp wraps around unfortunately the
* underlying RTC counter doesn't. The result is that timestamp expiry
* checks on micro-second timestamps don't yield the same result when
* applied on the corresponding RTC timestamp values.
*
* One solution is to translate the incoming 32-bit timestamp into a virtual
* 64-bit timestamp based on the knowledge of system-uptime, and then use
* this wraparound-free 64-bit value to do a linear mapping to RTC time.
* System uptime on an nRF is maintained using the 24-bit RTC counter. We
* track the overflow count to extend the 24-bit hardware counter by an
* additional 32 bits. RTC_UNITS_TO_MICROSECONDS() converts this into
* microsecond units (in 64-bits).
*/
const uint64_t currentTime64 = RTC_UNITS_TO_MICROSECONDS(rtc1_getCounter64());
uint64_t timestamp64 = (currentTime64 & ~(uint64_t)0xFFFFFFFFULL) + timestamp;
if (((uint32_t)currentTime64 > 0x80000000) && (timestamp < 0x80000000)) {
timestamp64 += (uint64_t)0x100000000ULL;
}
uint32_t newCallbackTime = MICROSECONDS_TO_RTC_UNITS(timestamp64);
/* Check for repeat setup of an existing callback. This is actually not
* important; the following code should work even without this check. */
if (us_ticker_callbackPending && (newCallbackTime == us_ticker_callbackTimestamp)) {
return;
}
/* Check for callbacks which are immediately (or will *very* shortly become) pending.
* Even if they are immediately pending, they are scheduled to trigger a few
* ticks later. This keeps things simple by invoking the callback from an
* independent interrupt context. */
if ((int)(newCallbackTime - rtc1_getCounter()) <= (int)FUZZY_RTC_TICKS) {
newCallbackTime = rtc1_getCounter() + FUZZY_RTC_TICKS;
}
NRF_RTC1->CC[0] = newCallbackTime & MAX_RTC_COUNTER_VAL;
us_ticker_callbackTimestamp = newCallbackTime;
if (!us_ticker_callbackPending) {
us_ticker_callbackPending = true;
rtc1_enableCompareInterrupt();
}
}
void us_ticker_disable_interrupt(void)
{
if (us_ticker_callbackPending) {
rtc1_disableCompareInterrupt();
us_ticker_callbackPending = false;
}
}
void us_ticker_clear_interrupt(void)
{
NRF_RTC1->EVENTS_OVRFLW = 0;
NRF_RTC1->EVENTS_COMPARE[0] = 0;
}

View File

@ -0,0 +1,245 @@
/* mbed Microcontroller Library
* Copyright (c) 2006-2013 ARM Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "mbed_assert.h"
#include "gpio_api.h"
#include "gpio_irq_api.h"
#include "pinmap.h"
#include "nrf_drv_gpiote.h"
#define GPIO_PIN_COUNT 31
typedef enum {
GPIO_NOT_USED = 0,
GPIO_USED = 1,
GPIO_USED_IRQ = 2,
GPIO_USED_IRQ_DISABLED = 3
} gpio_usage_t;
typedef struct {
bool used_as_gpio : 1;
PinDirection direction : 1;
bool init_high : 1;
PinMode pull : 2;
bool used_as_irq : 1;
bool irq_fall : 1;
bool irq_rise : 1;
} gpio_cfg_t;
uint32_t m_gpio_initialized;
gpio_cfg_t m_gpio_cfg[GPIO_PIN_COUNT];
/***********
GPIO IRQ
***********/
static gpio_irq_handler m_irq_handler;
static uint32_t m_channel_ids[GPIO_PIN_COUNT] = {0};
uint32_t m_gpio_irq_enabled;
static void gpiote_irq_handler(nrf_drv_gpiote_pin_t pin, nrf_gpiote_polarity_t action)
{
nrf_gpio_pin_sense_t sense = nrf_gpio_pin_sense_get(pin);
gpio_irq_event event = (sense == NRF_GPIO_PIN_SENSE_LOW) ? IRQ_RISE : IRQ_FALL;
if (m_gpio_irq_enabled & (1UL << pin)) {
if (((event == IRQ_RISE) && m_gpio_cfg[pin].irq_rise)
|| ((event == IRQ_FALL) && m_gpio_cfg[pin].irq_fall)) {
m_irq_handler(m_channel_ids[pin], event);
}
}
}
void gpio_init(gpio_t *obj, PinName pin)
{
obj->pin = pin;
if (pin == (PinName)NC) {
return;
}
MBED_ASSERT((uint32_t)pin < GPIO_PIN_COUNT);
(void) nrf_drv_gpiote_init();
m_gpio_cfg[obj->pin].used_as_gpio = true;
}
int gpio_read(gpio_t *obj)
{
MBED_ASSERT(obj->pin != (PinName)NC);
if (m_gpio_cfg[obj->pin].direction == PIN_OUTPUT) {
return ((NRF_GPIO->OUTSET & (1UL << obj->pin)) ? 1 : 0);
} else {
return nrf_gpio_pin_read(obj->pin);
}
}
static void gpio_apply_config(uint8_t pin)
{
if (m_gpio_initialized & (1UL << pin)) {
if ((m_gpio_cfg[pin].direction == PIN_OUTPUT) && (!m_gpio_cfg[pin].used_as_irq)) {
nrf_drv_gpiote_out_uninit(pin);
}
else {
nrf_drv_gpiote_in_uninit(pin);
}
}
if (m_gpio_cfg[pin].used_as_gpio || m_gpio_cfg[pin].used_as_irq) {
if ((m_gpio_cfg[pin].direction == PIN_INPUT)
|| (m_gpio_cfg[pin].used_as_irq)) {
//Configure as input.
nrf_drv_gpiote_in_config_t cfg;
cfg.hi_accuracy = false;
cfg.is_watcher = false;
cfg.sense = NRF_GPIOTE_POLARITY_TOGGLE;
if (m_gpio_cfg[pin].used_as_irq) {
cfg.pull = NRF_GPIO_PIN_PULLUP;
nrf_drv_gpiote_in_init(pin, &cfg, gpiote_irq_handler);
if ((m_gpio_irq_enabled & (1 << pin))
&& (m_gpio_cfg[pin].irq_rise || m_gpio_cfg[pin].irq_fall))
{
nrf_drv_gpiote_in_event_enable(pin, false);
}
}
else {
switch(m_gpio_cfg[pin].pull) {
case PullUp:
cfg.pull = NRF_GPIO_PIN_PULLUP;
break;
case PullDown:
cfg.pull = NRF_GPIO_PIN_PULLDOWN;
break;
default:
cfg.pull = NRF_GPIO_PIN_NOPULL;
break;
}
nrf_drv_gpiote_in_init(pin, &cfg, NULL);
}
}
else {
// Configure as output.
nrf_drv_gpiote_out_config_t cfg = GPIOTE_CONFIG_OUT_SIMPLE(m_gpio_cfg[pin].init_high);
nrf_drv_gpiote_out_init(pin, &cfg);
}
m_gpio_initialized |= (1UL << pin);
}
else {
m_gpio_initialized &= ~(1UL << pin);
}
}
void gpio_mode(gpio_t *obj, PinMode mode)
{
MBED_ASSERT(obj->pin <= GPIO_PIN_COUNT);
m_gpio_cfg[obj->pin].pull = mode;
gpio_apply_config(obj->pin);
}
void gpio_dir(gpio_t *obj, PinDirection direction)
{
MBED_ASSERT(obj->pin <= GPIO_PIN_COUNT);
m_gpio_cfg[obj->pin].direction = direction;
gpio_apply_config(obj->pin);
}
/***********
GPIO IRQ
***********/
int gpio_irq_init(gpio_irq_t *obj, PinName pin, gpio_irq_handler handler, uint32_t id)
{
if (pin == NC) {
return -1;
}
MBED_ASSERT((uint32_t)pin < GPIO_PIN_COUNT);
(void) nrf_drv_gpiote_init();
m_gpio_cfg[pin].used_as_irq = true;
m_channel_ids[pin] = id;
obj->ch = pin;
m_irq_handler = handler;
m_channel_ids[pin] = id;
gpio_apply_config(pin);
return 1;
}
void gpio_irq_free(gpio_irq_t *obj)
{
nrf_drv_gpiote_in_uninit(obj->ch);
m_gpio_cfg[obj->ch].used_as_irq = false;
m_channel_ids[obj->ch] = 0;
gpio_apply_config(obj->ch);
}
void gpio_irq_set(gpio_irq_t *obj, gpio_irq_event event, uint32_t enable)
{
bool event_enabled_before = false;
if ((m_gpio_irq_enabled & (1 << obj->ch))
&& (m_gpio_cfg[obj->ch].irq_rise || m_gpio_cfg[obj->ch].irq_fall)) {
event_enabled_before = true;
}
if (event == IRQ_RISE) {
m_gpio_cfg[obj->ch].irq_rise = enable ? true : false;
}
else if (event == IRQ_FALL) {
m_gpio_cfg[obj->ch].irq_fall = enable ? true : false;
}
bool event_enabled_after = false;
if ((m_gpio_irq_enabled & (1 << obj->ch))
&& (m_gpio_cfg[obj->ch].irq_rise || m_gpio_cfg[obj->ch].irq_fall)) {
event_enabled_after = true;
}
if (event_enabled_before != event_enabled_after) {
if (event_enabled_after) {
nrf_drv_gpiote_in_event_enable(obj->ch,false);
} else {
nrf_drv_gpiote_in_event_disable(obj->ch);
}
}
}
void gpio_irq_enable(gpio_irq_t *obj)
{
m_gpio_irq_enabled |= (1 << obj->ch);
if (m_gpio_cfg[obj->ch].irq_rise || m_gpio_cfg[obj->ch].irq_fall) {
nrf_drv_gpiote_in_event_enable(obj->ch, true);
}
}
void gpio_irq_disable(gpio_irq_t *obj)
{
m_gpio_irq_enabled &= ~(1 << obj->ch);
if (m_gpio_cfg[obj->ch].irq_rise || m_gpio_cfg[obj->ch].irq_fall) {
nrf_drv_gpiote_in_event_enable(obj->ch, false);
}
}

View File

@ -18,31 +18,23 @@
#include "mbed_assert.h"
#include "nrf_gpio.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
PinName pin;
uint32_t mask;
__IO uint32_t *reg_dir;
__IO uint32_t *reg_set;
__IO uint32_t *reg_clr;
__I uint32_t *reg_in;
} gpio_t;
static inline void gpio_write(gpio_t *obj, int value) {
MBED_ASSERT(obj->pin != (PinName)NC);
if (value)
*obj->reg_set = obj->mask;
else
*obj->reg_clr = obj->mask;
}
static inline int gpio_read(gpio_t *obj) {
MBED_ASSERT(obj->pin != (PinName)NC);
return ((*obj->reg_in & obj->mask) ? 1 : 0);
if (value) {
nrf_gpio_pin_set(obj->pin);
} else {
nrf_gpio_pin_clear(obj->pin);
}
}
static inline int gpio_is_connected(const gpio_t *obj) {

View File

@ -0,0 +1,277 @@
/* mbed Microcontroller Library
* Copyright (c) 2013 Nordic Semiconductor
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "mbed_assert.h"
#include "mbed_error.h"
#include "i2c_api.h"
#include "nrf_drv_twi.h"
#include "app_util_platform.h"
#if DEVICE_I2C
#if DEVICE_I2C_ASYNCH
#define TWI_IDX(obj) ((obj)->i2c.twi_idx)
#else
#define TWI_IDX(obj) ((obj)->twi_idx)
#endif
#define TWI_INFO(obj) (&m_twi_info[TWI_IDX(obj)])
typedef struct {
bool initialized;
nrf_drv_twi_config_t config;
volatile bool transfer_finished;
#if DEVICE_I2C_ASYNCH
volatile uint32_t events;
void (*handler)(void);
uint32_t event_mask;
#endif
} twi_info_t;
static twi_info_t m_twi_info[TWI_COUNT];
static nrf_drv_twi_t const m_twi_instances[TWI_COUNT] = {
#if TWI0_ENABLED
NRF_DRV_TWI_INSTANCE(0),
#endif
#if TWI1_ENABLED
NRF_DRV_TWI_INSTANCE(1),
#endif
};
static void twi_event_handler(nrf_drv_twi_evt_t const *event, void *context)
{
twi_info_t * twi_info = TWI_INFO((i2c_t *)context);
twi_info->transfer_finished = true;
#if DEVICE_I2C_ASYNCH
switch (event->type) {
case NRF_DRV_TWI_EVT_DONE:
twi_info->events |= I2C_EVENT_TRANSFER_COMPLETE;
break;
case NRF_DRV_TWI_EVT_ADDRESS_NACK:
twi_info->events |= I2C_EVENT_ERROR_NO_SLAVE;
break;
case NRF_DRV_TWI_EVT_DATA_NACK:
twi_info->events |= I2C_EVENT_ERROR;
break;
}
if (twi_info->handler) {
twi_info->handler();
}
#endif // DEVICE_I2C_ASYNCH
}
static uint8_t twi_address(int i2c_address)
{
// The TWI driver requires 7-bit slave address (without R/W bit).
return (i2c_address >> 1);
}
void i2c_init(i2c_t *obj, PinName sda, PinName scl)
{
int i;
for (i = 0; i < TWI_COUNT; ++i) {
if (m_twi_info[i].initialized &&
m_twi_info[i].config.sda == (uint32_t)sda &&
m_twi_info[i].config.scl == (uint32_t)scl) {
TWI_IDX(obj) = i;
TWI_INFO(obj)->config.frequency = NRF_TWI_FREQ_100K;
i2c_reset(obj);
return;
}
}
nrf_drv_twi_config_t const config = {
.scl = scl,
.sda = sda,
.frequency = NRF_TWI_FREQ_100K,
.interrupt_priority = APP_IRQ_PRIORITY_LOW,
};
for (i = 0; i < TWI_COUNT; ++i) {
if (!m_twi_info[i].initialized) {
nrf_drv_twi_t const *twi = &m_twi_instances[i];
ret_code_t ret_code =
nrf_drv_twi_init(twi, &config, twi_event_handler, obj);
if (ret_code == NRF_SUCCESS) {
TWI_IDX(obj) = i;
TWI_INFO(obj)->initialized = true;
TWI_INFO(obj)->config = config;
nrf_drv_twi_enable(twi);
return;
}
}
}
// No available peripheral
error("No available I2C");
}
void i2c_reset(i2c_t *obj)
{
twi_info_t *twi_info = TWI_INFO(obj);
nrf_drv_twi_t const *twi = &m_twi_instances[TWI_IDX(obj)];
nrf_drv_twi_uninit(twi);
nrf_drv_twi_init(twi, &twi_info->config, twi_event_handler, obj);
nrf_drv_twi_enable(twi);
}
int i2c_start(i2c_t *obj)
{
(void)obj;
return -1; // Not implemented.
}
int i2c_stop(i2c_t *obj)
{
(void)obj;
return -1; // Not implemented.
}
void i2c_frequency(i2c_t *obj, int hz)
{
twi_info_t *twi_info = TWI_INFO(obj);
nrf_drv_twi_t const *twi = &m_twi_instances[TWI_IDX(obj)];
if (hz < 250000) {
twi_info->config.frequency = NRF_TWI_FREQ_100K;
} else if (hz < 400000) {
twi_info->config.frequency = NRF_TWI_FREQ_250K;
} else {
twi_info->config.frequency = NRF_TWI_FREQ_400K;
}
nrf_twi_frequency_set(twi->reg.p_twi, twi_info->config.frequency);
}
int i2c_read(i2c_t *obj, int address, char *data, int length, int stop)
{
(void)stop;
twi_info_t *twi_info = TWI_INFO(obj);
nrf_drv_twi_t const *twi = &m_twi_instances[TWI_IDX(obj)];
twi_info->transfer_finished = false;
ret_code_t ret_code = nrf_drv_twi_rx(twi, twi_address(address),
(uint8_t *)data, length);
if (ret_code != NRF_SUCCESS) {
return 0;
}
while (!twi_info->transfer_finished) {}
return nrf_drv_twi_data_count_get(twi);
}
int i2c_write(i2c_t *obj, int address, const char *data, int length, int stop)
{
twi_info_t *twi_info = TWI_INFO(obj);
nrf_drv_twi_t const *twi = &m_twi_instances[TWI_IDX(obj)];
twi_info->transfer_finished = false;
ret_code_t ret_code = nrf_drv_twi_tx(twi, twi_address(address),
(uint8_t const *)data, length, (stop == 0));
if (ret_code != NRF_SUCCESS) {
return 0;
}
while (!twi_info->transfer_finished) {}
return nrf_drv_twi_data_count_get(twi);
}
int i2c_byte_read(i2c_t *obj, int last)
{
(void)obj;
(void)last;
return -1; // Not implemented.
}
int i2c_byte_write(i2c_t *obj, int data)
{
(void)obj;
(void)data;
return -1; // Not implemented.
}
#if DEVICE_I2C_ASYNCH
void i2c_transfer_asynch(i2c_t *obj, void *tx, size_t tx_length,
void *rx, size_t rx_length, uint32_t address,
uint32_t stop, uint32_t handler,
uint32_t event, DMAUsage hint)
{
(void)stop;
(void)hint;
if (i2c_active(obj)) {
return;
}
if ((tx_length == 0) && (rx_length == 0)) {
return;
}
twi_info_t *twi_info = TWI_INFO(obj);
twi_info->events = 0;
twi_info->handler = (void (*)(void))handler;
twi_info->event_mask = event;
uint8_t twi_addr = twi_address(address);
nrf_drv_twi_t const *twi = &m_twi_instances[TWI_IDX(obj)];
if ((tx_length > 0) && (rx_length == 0)) {
nrf_drv_twi_xfer_desc_t const xfer =
NRF_DRV_TWI_XFER_DESC_TX(twi_addr, (uint8_t *)tx, tx_length);
nrf_drv_twi_xfer(twi, &xfer,
stop ? 0 : NRF_DRV_TWI_FLAG_TX_NO_STOP);
}
else if ((tx_length == 0) && (rx_length > 0)) {
nrf_drv_twi_xfer_desc_t const xfer =
NRF_DRV_TWI_XFER_DESC_RX(twi_addr, rx, rx_length);
nrf_drv_twi_xfer(twi, &xfer, 0);
}
else if ((tx_length > 0) && (rx_length > 0)) {
nrf_drv_twi_xfer_desc_t const xfer =
NRF_DRV_TWI_XFER_DESC_TXRX(twi_addr,
(uint8_t *)tx, tx_length, rx, rx_length);
nrf_drv_twi_xfer(twi, &xfer, 0);
}
}
uint32_t i2c_irq_handler_asynch(i2c_t *obj)
{
twi_info_t *twi_info = TWI_INFO(obj);
return (twi_info->events & twi_info->event_mask);
}
uint8_t i2c_active(i2c_t *obj)
{
nrf_drv_twi_t const *twi = &m_twi_instances[TWI_IDX(obj)];
return nrf_drv_twi_is_busy(twi);
}
void i2c_abort_asynch(i2c_t *obj)
{
i2c_reset(obj);
}
#endif // DEVICE_I2C_ASYNCH
#endif // DEVICE_I2C

View File

@ -0,0 +1,66 @@
/* mbed Microcontroller Library
* Copyright (c) 2015 ARM Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "lp_ticker_api.h"
#if DEVICE_LOWPOWERTIMER
#include "rtc_common.h"
#include "sleep_api.h"
void lp_ticker_init(void)
{
rtc_common_init();
}
uint32_t lp_ticker_read(void)
{
return rtc_common_32bit_ticks_get();
}
void lp_ticker_set_interrupt(uint32_t now, uint32_t time)
{
(void)now;
// The passed 32-bit 'time' value is wrapped properly by the driver, so it
// is usable by the 24-bit counter.
ret_code_t result = nrf_drv_rtc_cc_set(&m_rtc_common, LP_TICKER_CC_CHANNEL,
time, true);
if (result != NRF_SUCCESS)
{
MBED_ASSERT(false);
}
}
uint32_t lp_ticker_get_overflows_counter(void)
{
// Cut out the part of 'm_rtc_common_overflows' used by
// 'rtc_common_32bit_ticks_get()'.
return (m_rtc_common_overflows >> (32 - RTC_COUNTER_BITS));
}
uint32_t lp_ticker_get_compare_match(void)
{
return nrf_rtc_cc_get(m_rtc_common.p_reg, LP_TICKER_CC_CHANNEL);
}
void lp_ticker_sleep_until(uint32_t now, uint32_t time)
{
lp_ticker_set_interrupt(now, time);
sleep_t sleep_obj;
mbed_enter_sleep(&sleep_obj);
mbed_exit_sleep(&sleep_obj);
}
#endif // DEVICE_LOWPOWERTIMER

View File

@ -21,29 +21,19 @@
#include "PeripheralNames.h"
#include "PinNames.h"
#include "nrf_drv_spi.h"
#ifdef __cplusplus
extern "C" {
#endif
#define I2C_SPI_PERIPHERAL_FOR_I2C 1
#define I2C_SPI_PERIPHERAL_FOR_SPI 2
typedef struct {
uint8_t usage; // I2C: 1, SPI: 2
uint8_t sda_mosi;
uint8_t scl_miso;
uint8_t sclk;
} i2c_spi_peripheral_t;
struct serial_s {
NRF_UART_Type *uart;
int index;
};
struct spi_s {
NRF_SPI_Type *spi;
NRF_SPIS_Type *spis;
uint8_t peripheral;
nrf_drv_spi_t *p_spi;
volatile bool busy;
bool async_mode;
};
struct port_s {
@ -55,17 +45,14 @@ struct port_s {
};
struct pwmout_s {
PWMName pwm;
PWMName pwm_name;
PinName pin;
uint8_t pwm_channel;
void * pwm_struct;
};
struct i2c_s {
NRF_TWI_Type *i2c;
PinName sda;
PinName scl;
int freq;
uint8_t address_set;
uint8_t peripheral;
uint8_t twi_idx;
};
struct analogin_s {

View File

@ -0,0 +1,69 @@
/* mbed Microcontroller Library
* Copyright (c) 2013 Nordic Semiconductor
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "rtc_api.h"
#if DEVICE_RTC
#include "rtc_common.h"
#include "nrf_drv_clock.h"
#include "app_util_platform.h"
static time_t m_time_base;
void rtc_init(void)
{
rtc_common_init();
}
void rtc_free(void)
{
// A common counter is used for RTC, lp_ticker and us_ticker, so it can't be
// disabled here, but this does not cause any extra cost. Besides, currently
// this function is not used by RTC API in mbed-drivers.
}
int rtc_isenabled(void)
{
return m_rtc_common_enabled;
}
static uint32_t rtc_seconds_get(void)
{
// Convert current counter value to seconds.
uint32_t seconds = nrf_drv_rtc_counter_get(&m_rtc_common) / RTC_INPUT_FREQ;
// Add proper amount of seconds for each registered overflow of the counter.
uint32_t seconds_per_overflow = (1 << RTC_COUNTER_BITS) / RTC_INPUT_FREQ;
return (seconds + (m_rtc_common_overflows * seconds_per_overflow));
}
time_t rtc_read(void)
{
return m_time_base + rtc_seconds_get();
}
void rtc_write(time_t t)
{
uint32_t seconds;
do {
seconds = rtc_seconds_get();
m_time_base = t - seconds;
// If the number of seconds indicated by the counter changed during the
// update of the time base, just repeat the update, now using the new
// number of seconds.
} while (seconds != rtc_seconds_get());
}
#endif // DEVICE_RTC

View File

@ -1,5 +1,5 @@
/* mbed Microcontroller Library
* Copyright (c) 2006-2013 ARM Limited
* Copyright (c) 2015 ARM Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -13,20 +13,22 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "mbed_assert.h"
#include "pinmap.h"
#include "mbed_error.h"
void pin_function(PinName pin, int function)
{
}
#ifndef RTC_COMMON_H
#define RTC_COMMON_H
void pin_mode(PinName pin, PinMode mode)
{
MBED_ASSERT(pin != (PinName)NC);
#include "nrf_drv_rtc.h"
uint32_t pin_number = (uint32_t)pin;
#define RTC_COUNTER_BITS 24
NRF_GPIO->PIN_CNF[pin_number] &= ~GPIO_PIN_CNF_PULL_Msk;
NRF_GPIO->PIN_CNF[pin_number] |= (mode << GPIO_PIN_CNF_PULL_Pos);
}
#define LP_TICKER_CC_CHANNEL 0
#define US_TICKER_CC_CHANNEL 1
extern nrf_drv_rtc_t const m_rtc_common;
extern bool m_rtc_common_enabled;
extern uint32_t volatile m_rtc_common_overflows;
void rtc_common_init(void);
uint32_t rtc_common_32bit_ticks_get(void);
#endif // RTC_COMMON_H

View File

@ -0,0 +1,580 @@
/* Copyright (c) 2015 Nordic Semiconductor. All Rights Reserved.
*
* The information contained herein is property of Nordic Semiconductor ASA.
* Terms and conditions of usage are described in detail in NORDIC
* SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT.
*
* Licensees are granted free, non-transferable use of the information. NO
* WARRANTY of ANY KIND is provided. This heading must NOT be removed from
* the file.
*
*/
#include "nrf_drv_gpiote.h"
#include "nrf_drv_common.h"
#include "nrf_drv_config.h"
#include "app_util_platform.h"
#include "nrf_assert.h"
#define FORBIDDEN_HANDLER_ADDRESS ((nrf_drv_gpiote_evt_handler_t)UINT32_MAX)
#define PIN_NOT_USED (-1)
#define PIN_USED (-2)
#define NO_CHANNELS (-1)
#define SENSE_FIELD_POS (6)
#define SENSE_FIELD_MASK (0xC0)
/**
* @brief Macro for conveting task-event index to an address of an event register.
*
* Macro utilizes the fact that registers are grouped together in ascending order.
*/
#define TE_IDX_TO_EVENT_ADDR(idx) (nrf_gpiote_events_t)((uint32_t)NRF_GPIOTE_EVENTS_IN_0+(sizeof(uint32_t)*(idx)))
/**
* @brief Macro for conveting task-event index to an address of a task register.
*
* Macro utilizes the fact that registers are grouped together in ascending order.
*/
#define TE_IDX_TO_TASK_ADDR(idx) (nrf_gpiote_tasks_t)((uint32_t)NRF_GPIOTE_TASKS_OUT_0+(sizeof(uint32_t)*(idx)))
//lint -save -e661
typedef struct
{
nrf_drv_gpiote_evt_handler_t handlers[NUMBER_OF_GPIO_TE+GPIOTE_CONFIG_NUM_OF_LOW_POWER_EVENTS];
int8_t pin_assignments[NUMBER_OF_PINS];
int8_t port_handlers_pins[GPIOTE_CONFIG_NUM_OF_LOW_POWER_EVENTS];
nrf_drv_state_t state;
} gpiote_control_block_t;
static gpiote_control_block_t m_cb;
__STATIC_INLINE bool pin_in_use(uint32_t pin)
{
return (m_cb.pin_assignments[pin] != PIN_NOT_USED);
}
__STATIC_INLINE bool pin_in_use_as_non_task_out(uint32_t pin)
{
return (m_cb.pin_assignments[pin] == PIN_USED);
}
__STATIC_INLINE bool pin_in_use_by_te(uint32_t pin)
{
return (m_cb.pin_assignments[pin] >= 0 && m_cb.pin_assignments[pin] < NUMBER_OF_GPIO_TE) ? true : false;
}
__STATIC_INLINE bool pin_in_use_by_port(uint32_t pin)
{
return (m_cb.pin_assignments[pin] >= NUMBER_OF_GPIO_TE);
}
__STATIC_INLINE bool pin_in_use_by_gpiote(uint32_t pin)
{
return (m_cb.pin_assignments[pin] >= 0);
}
__STATIC_INLINE void pin_in_use_by_te_set(uint32_t pin,
uint32_t channel_id,
nrf_drv_gpiote_evt_handler_t handler,
bool is_channel)
{
m_cb.pin_assignments[pin] = channel_id;
m_cb.handlers[channel_id] = handler;
if (!is_channel)
{
m_cb.port_handlers_pins[channel_id-NUMBER_OF_GPIO_TE] = (int8_t)pin;
}
}
__STATIC_INLINE void pin_in_use_set(uint32_t pin)
{
m_cb.pin_assignments[pin] = PIN_USED;
}
__STATIC_INLINE void pin_in_use_clear(uint32_t pin)
{
m_cb.pin_assignments[pin] = PIN_NOT_USED;
}
__STATIC_INLINE int8_t channel_port_get(uint32_t pin)
{
return m_cb.pin_assignments[pin];
}
__STATIC_INLINE nrf_drv_gpiote_evt_handler_t channel_handler_get(uint32_t channel)
{
return m_cb.handlers[channel];
}
static int8_t channel_port_alloc(uint32_t pin,nrf_drv_gpiote_evt_handler_t handler, bool channel)
{
int8_t channel_id = NO_CHANNELS;
uint32_t i;
uint32_t start_idx = channel ? 0 : NUMBER_OF_GPIO_TE;
uint32_t end_idx = channel ? NUMBER_OF_GPIO_TE : (NUMBER_OF_GPIO_TE+GPIOTE_CONFIG_NUM_OF_LOW_POWER_EVENTS);
//critical section
for (i = start_idx; i < end_idx; i++)
{
if (m_cb.handlers[i] == FORBIDDEN_HANDLER_ADDRESS)
{
pin_in_use_by_te_set(pin, i, handler, channel);
channel_id = i;
break;
}
}
//critical section
return channel_id;
}
static void channel_free(uint8_t channel_id)
{
m_cb.handlers[channel_id] = FORBIDDEN_HANDLER_ADDRESS;
if (channel_id >= NUMBER_OF_GPIO_TE)
{
m_cb.port_handlers_pins[channel_id-NUMBER_OF_GPIO_TE] = (int8_t)PIN_NOT_USED;
}
}
ret_code_t nrf_drv_gpiote_init(void)
{
if (m_cb.state != NRF_DRV_STATE_UNINITIALIZED)
{
return NRF_ERROR_INVALID_STATE;
}
uint8_t i;
for (i = 0; i < NUMBER_OF_PINS; i++)
{
pin_in_use_clear(i);
}
for (i = 0; i < (NUMBER_OF_GPIO_TE+GPIOTE_CONFIG_NUM_OF_LOW_POWER_EVENTS); i++)
{
channel_free(i);
}
nrf_drv_common_irq_enable(GPIOTE_IRQn, GPIOTE_CONFIG_IRQ_PRIORITY);
nrf_gpiote_int_enable(GPIOTE_INTENSET_PORT_Msk);
m_cb.state = NRF_DRV_STATE_INITIALIZED;
return NRF_SUCCESS;
}
bool nrf_drv_gpiote_is_init(void)
{
return (m_cb.state != NRF_DRV_STATE_UNINITIALIZED) ? true : false;
}
void nrf_drv_gpiote_uninit(void)
{
ASSERT(m_cb.state!=NRF_DRV_STATE_UNINITIALIZED);
uint32_t i;
for (i = 0; i < NUMBER_OF_PINS; i++)
{
if (pin_in_use_as_non_task_out(i))
{
nrf_drv_gpiote_out_uninit(i);
}
else if( pin_in_use_by_gpiote(i))
{
/* Disable gpiote_in is having the same effect on out pin as gpiote_out_uninit on
* so it can be called on all pins used by GPIOTE.
*/
nrf_drv_gpiote_in_uninit(i);
}
}
m_cb.state = NRF_DRV_STATE_UNINITIALIZED;
}
ret_code_t nrf_drv_gpiote_out_init(nrf_drv_gpiote_pin_t pin,
nrf_drv_gpiote_out_config_t const * p_config)
{
ASSERT(pin < NUMBER_OF_PINS);
ASSERT(m_cb.state == NRF_DRV_STATE_INITIALIZED);
ASSERT(p_config);
ret_code_t result = NRF_SUCCESS;
if (pin_in_use(pin))
{
result = NRF_ERROR_INVALID_STATE;
}
else
{
if (p_config->task_pin)
{
int8_t channel = channel_port_alloc(pin, NULL, true);
if (channel != NO_CHANNELS)
{
nrf_gpiote_task_configure(channel, pin, p_config->action, p_config->init_state);
}
else
{
result = NRF_ERROR_NO_MEM;
}
}
else
{
pin_in_use_set(pin);
}
if (result == NRF_SUCCESS)
{
if (p_config->init_state == NRF_GPIOTE_INITIAL_VALUE_HIGH)
{
nrf_gpio_pin_set(pin);
}
else
{
nrf_gpio_pin_clear(pin);
}
nrf_gpio_cfg_output(pin);
}
}
return result;
}
void nrf_drv_gpiote_out_uninit(nrf_drv_gpiote_pin_t pin)
{
ASSERT(pin < NUMBER_OF_PINS);
ASSERT(pin_in_use(pin));
if (pin_in_use_by_te(pin))
{
channel_free((uint8_t)channel_port_get(pin));
nrf_gpiote_te_default(channel_port_get(pin));
}
pin_in_use_clear(pin);
nrf_gpio_cfg_default(pin);
}
void nrf_drv_gpiote_out_set(nrf_drv_gpiote_pin_t pin)
{
ASSERT(pin < NUMBER_OF_PINS);
ASSERT(pin_in_use(pin));
ASSERT(!pin_in_use_by_te(pin))
nrf_gpio_pin_set(pin);
}
void nrf_drv_gpiote_out_clear(nrf_drv_gpiote_pin_t pin)
{
ASSERT(pin < NUMBER_OF_PINS);
ASSERT(pin_in_use(pin));
ASSERT(!pin_in_use_by_te(pin))
nrf_gpio_pin_clear(pin);
}
void nrf_drv_gpiote_out_toggle(nrf_drv_gpiote_pin_t pin)
{
ASSERT(pin < NUMBER_OF_PINS);
ASSERT(pin_in_use(pin));
ASSERT(!pin_in_use_by_te(pin))
nrf_gpio_pin_toggle(pin);
}
void nrf_drv_gpiote_out_task_enable(nrf_drv_gpiote_pin_t pin)
{
ASSERT(pin < NUMBER_OF_PINS);
ASSERT(pin_in_use(pin));
ASSERT(pin_in_use_by_te(pin))
nrf_gpiote_task_enable(m_cb.pin_assignments[pin]);
}
void nrf_drv_gpiote_out_task_disable(nrf_drv_gpiote_pin_t pin)
{
ASSERT(pin < NUMBER_OF_PINS);
ASSERT(pin_in_use(pin));
ASSERT(pin_in_use_by_te(pin))
nrf_gpiote_task_disable(m_cb.pin_assignments[pin]);
}
uint32_t nrf_drv_gpiote_out_task_addr_get(nrf_drv_gpiote_pin_t pin)
{
ASSERT(pin < NUMBER_OF_PINS);
ASSERT(pin_in_use_by_te(pin));
nrf_gpiote_tasks_t task = TE_IDX_TO_TASK_ADDR(channel_port_get(pin));
return nrf_gpiote_task_addr_get(task);
}
void nrf_drv_gpiote_out_task_force(nrf_drv_gpiote_pin_t pin, uint8_t state)
{
ASSERT(pin < NUMBER_OF_PINS);
ASSERT(pin_in_use(pin));
ASSERT(pin_in_use_by_te(pin));
nrf_gpiote_outinit_t init_val = state ? NRF_GPIOTE_INITIAL_VALUE_HIGH : NRF_GPIOTE_INITIAL_VALUE_LOW;
nrf_gpiote_task_force(m_cb.pin_assignments[pin], init_val);
}
void nrf_drv_gpiote_out_task_trigger(nrf_drv_gpiote_pin_t pin)
{
ASSERT(pin < NUMBER_OF_PINS);
ASSERT(pin_in_use(pin));
ASSERT(pin_in_use_by_te(pin));
nrf_gpiote_tasks_t task = TE_IDX_TO_TASK_ADDR(channel_port_get(pin));;
nrf_gpiote_task_set(task);
}
ret_code_t nrf_drv_gpiote_in_init(nrf_drv_gpiote_pin_t pin,
nrf_drv_gpiote_in_config_t const * p_config,
nrf_drv_gpiote_evt_handler_t evt_handler)
{
ASSERT(pin < NUMBER_OF_PINS);
ret_code_t result = NRF_SUCCESS;
/* Only one GPIOTE channel can be assigned to one physical pin. */
if (pin_in_use_by_gpiote(pin))
{
result = NRF_ERROR_INVALID_STATE;
}
else
{
int8_t channel = channel_port_alloc(pin, evt_handler, p_config->hi_accuracy);
if (channel != NO_CHANNELS)
{
if (p_config->is_watcher)
{
nrf_gpio_cfg_watcher(pin);
}
else
{
nrf_gpio_cfg_input(pin,p_config->pull);
}
if (p_config->hi_accuracy)
{
nrf_gpiote_event_configure(channel, pin,p_config->sense);
}
else
{
m_cb.port_handlers_pins[channel-NUMBER_OF_GPIO_TE] |= (p_config->sense)<< SENSE_FIELD_POS;
}
}
else
{
result = NRF_ERROR_NO_MEM;
}
}
return result;
}
void nrf_drv_gpiote_in_event_enable(nrf_drv_gpiote_pin_t pin, bool int_enable)
{
ASSERT(pin < NUMBER_OF_PINS);
ASSERT(pin_in_use_by_gpiote(pin));
if (pin_in_use_by_port(pin))
{
uint8_t pin_and_sense = m_cb.port_handlers_pins[channel_port_get(pin)-NUMBER_OF_GPIO_TE];
nrf_gpiote_polarity_t polarity = (nrf_gpiote_polarity_t)(pin_and_sense >> SENSE_FIELD_POS);
nrf_gpio_pin_sense_t sense;
if (polarity == NRF_GPIOTE_POLARITY_TOGGLE)
{
/* read current pin state and set for next sense to oposit */
sense = (nrf_gpio_pins_read() & (1 << pin)) ?
NRF_GPIO_PIN_SENSE_LOW : NRF_GPIO_PIN_SENSE_HIGH;
}
else
{
sense = (polarity == NRF_GPIOTE_POLARITY_LOTOHI) ?
NRF_GPIO_PIN_SENSE_HIGH : NRF_GPIO_PIN_SENSE_LOW;
}
nrf_gpio_cfg_sense_set(pin,sense);
}
else if(pin_in_use_by_te(pin))
{
int32_t channel = (int32_t)channel_port_get(pin);
nrf_gpiote_events_t event = TE_IDX_TO_EVENT_ADDR(channel);
nrf_gpiote_event_enable(channel);
nrf_gpiote_event_clear(event);
if (int_enable)
{
nrf_drv_gpiote_evt_handler_t handler = channel_handler_get(channel_port_get(pin));
// Enable the interrupt only if event handler was provided.
if (handler)
{
nrf_gpiote_int_enable(1 << channel);
}
}
}
}
void nrf_drv_gpiote_in_event_disable(nrf_drv_gpiote_pin_t pin)
{
ASSERT(pin < NUMBER_OF_PINS);
ASSERT(pin_in_use_by_gpiote(pin));
if (pin_in_use_by_port(pin))
{
nrf_gpio_cfg_sense_set(pin,NRF_GPIO_PIN_NOSENSE);
}
else if(pin_in_use_by_te(pin))
{
int32_t channel = (int32_t)channel_port_get(pin);
nrf_gpiote_event_disable(channel);
nrf_gpiote_int_disable(1 << channel);
}
}
void nrf_drv_gpiote_in_uninit(nrf_drv_gpiote_pin_t pin)
{
ASSERT(pin < NUMBER_OF_PINS);
ASSERT(pin_in_use_by_gpiote(pin));
nrf_drv_gpiote_in_event_disable(pin);
if(pin_in_use_by_te(pin))
{
nrf_gpiote_te_default(channel_port_get(pin));
}
nrf_gpio_cfg_default(pin);
channel_free((uint8_t)channel_port_get(pin));
pin_in_use_clear(pin);
}
bool nrf_drv_gpiote_in_is_set(nrf_drv_gpiote_pin_t pin)
{
ASSERT(pin < NUMBER_OF_PINS);
return nrf_gpio_pin_read(pin) ? true : false;
}
uint32_t nrf_drv_gpiote_in_event_addr_get(nrf_drv_gpiote_pin_t pin)
{
ASSERT(pin < NUMBER_OF_PINS);
ASSERT(pin_in_use_by_te(pin));
nrf_gpiote_events_t event = TE_IDX_TO_EVENT_ADDR(channel_port_get(pin));
return nrf_gpiote_event_addr_get(event);
}
void GPIOTE_IRQHandler(void)
{
uint32_t status = 0;
uint32_t input = 0;
/* collect status of all GPIOTE pin events. Processing is done once all are collected and cleared.*/
uint32_t i;
nrf_gpiote_events_t event = NRF_GPIOTE_EVENTS_IN_0;
uint32_t mask = (uint32_t)NRF_GPIOTE_INT_IN0_MASK;
for (i = 0; i < NUMBER_OF_GPIO_TE; i++)
{
if (nrf_gpiote_event_is_set(event) && nrf_gpiote_int_is_enabled(mask))
{
nrf_gpiote_event_clear(event);
status |= mask;
}
mask <<= 1;
/* Incrementing to next event, utilizing the fact that events are grouped together
* in ascending order. */
event = (nrf_gpiote_events_t)((uint32_t)event + sizeof(uint32_t));
}
/* collect PORT status event, if event is set read pins state. Processing is postponed to the
* end of interrupt. */
if (nrf_gpiote_event_is_set(NRF_GPIOTE_EVENTS_PORT))
{
nrf_gpiote_event_clear(NRF_GPIOTE_EVENTS_PORT);
status |= (uint32_t)NRF_GPIOTE_INT_PORT_MASK;
input = nrf_gpio_pins_read();
}
/* Process pin events. */
if (status & NRF_GPIOTE_INT_IN_MASK)
{
mask = (uint32_t)NRF_GPIOTE_INT_IN0_MASK;
for (i = 0; i < NUMBER_OF_GPIO_TE; i++)
{
if (mask & status)
{
nrf_drv_gpiote_pin_t pin = nrf_gpiote_event_pin_get(i);
nrf_gpiote_polarity_t polarity = nrf_gpiote_event_polarity_get(i);
nrf_drv_gpiote_evt_handler_t handler = channel_handler_get(i);
handler(pin,polarity);
}
mask <<= 1;
}
}
if (status & (uint32_t)NRF_GPIOTE_INT_PORT_MASK)
{
/* Process port event. */
uint8_t repeat = 0;
uint32_t toggle_mask = 0;
uint32_t pins_to_check = 0xFFFFFFFFuL;
do
{
repeat = 0;
for (i = 0; i < GPIOTE_CONFIG_NUM_OF_LOW_POWER_EVENTS; i++)
{
uint8_t pin_and_sense = m_cb.port_handlers_pins[i];
nrf_drv_gpiote_pin_t pin = (pin_and_sense & ~SENSE_FIELD_MASK);
if ((m_cb.port_handlers_pins[i] != PIN_NOT_USED)
&& ((1UL << pin) & pins_to_check))
{
nrf_gpiote_polarity_t polarity =
(nrf_gpiote_polarity_t)((pin_and_sense & SENSE_FIELD_MASK) >> SENSE_FIELD_POS);
nrf_drv_gpiote_evt_handler_t handler = channel_handler_get(channel_port_get(pin));
if (handler || polarity == NRF_GPIOTE_POLARITY_TOGGLE)
{
mask = 1 << pin;
if (polarity == NRF_GPIOTE_POLARITY_TOGGLE)
{
toggle_mask |= mask;
}
nrf_gpio_pin_sense_t sense = nrf_gpio_pin_sense_get(pin);
if (((mask & input) && (sense==NRF_GPIO_PIN_SENSE_HIGH)) ||
(!(mask & input) && (sense==NRF_GPIO_PIN_SENSE_LOW)) )
{
if (polarity == NRF_GPIOTE_POLARITY_TOGGLE)
{
nrf_gpio_pin_sense_t next_sense = (sense == NRF_GPIO_PIN_SENSE_HIGH) ?
NRF_GPIO_PIN_SENSE_LOW : NRF_GPIO_PIN_SENSE_HIGH;
nrf_gpio_cfg_sense_set(pin, next_sense);
++repeat;
}
if (handler)
{
handler(pin, polarity);
}
}
}
}
}
if (repeat)
{
// When one of the pins in low-accuracy and toggle mode becomes active,
// it's sense mode is inverted to clear the internal SENSE signal.
// State of any other enabled low-accuracy input in toggle mode must be checked
// explicitly, because it does not trigger the interrput when SENSE signal is active.
// For more information about SENSE functionality, refer to Product Specification.
uint32_t new_input = nrf_gpio_pins_read();
if (new_input == input)
{
//No change.
repeat = 0;
}
else
{
input = new_input;
pins_to_check = toggle_mask;
}
}
}
while (repeat);
}
}
//lint -restore

View File

@ -0,0 +1,309 @@
/* Copyright (c) 2015 Nordic Semiconductor. All Rights Reserved.
*
* The information contained herein is property of Nordic Semiconductor ASA.
* Terms and conditions of usage are described in detail in NORDIC
* SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT.
*
* Licensees are granted free, non-transferable use of the information. NO
* WARRANTY of ANY KIND is provided. This heading must NOT be removed from
* the file.
*
*/
#ifndef NRF_DRV_GPIOTE__
#define NRF_DRV_GPIOTE__
/**
* @addtogroup nrf_gpiote GPIOTE abstraction and driver
* @ingroup nrf_drivers
* @brief GPIOTE APIs.
* @defgroup nrf_drv_gpiote GPIOTE driver
* @{
* @ingroup nrf_gpiote
* @brief GPIOTE driver for managing input and output pins.
*/
#include "nrf_gpiote.h"
#include "nrf_gpio.h"
#include "nrf_drv_config.h"
#include "sdk_errors.h"
#include <stdint.h>
#include <stdbool.h>
/**@brief Input pin configuration. */
typedef struct
{
nrf_gpiote_polarity_t sense; /**< Transition that triggers interrupt. */
nrf_gpio_pin_pull_t pull; /**< Pulling mode. */
bool is_watcher; /**< True when the input pin is tracking an output pin. */
bool hi_accuracy;/**< True when high accuracy (IN_EVENT) is used. */
} nrf_drv_gpiote_in_config_t;
/**@brief Macro for configuring a pin to use a GPIO IN or PORT EVENT to detect low-to-high transition.
* @details Set hi_accu to true to use IN_EVENT. */
#define GPIOTE_CONFIG_IN_SENSE_LOTOHI(hi_accu) \
{ \
.is_watcher = false, \
.hi_accuracy = hi_accu, \
.pull = NRF_GPIO_PIN_NOPULL, \
.sense = NRF_GPIOTE_POLARITY_LOTOHI, \
}
/**@brief Macro for configuring a pin to use a GPIO IN or PORT EVENT to detect high-to-low transition.
* @details Set hi_accu to true to use IN_EVENT. */
#define GPIOTE_CONFIG_IN_SENSE_HITOLO(hi_accu) \
{ \
.is_watcher = false, \
.hi_accuracy = hi_accu, \
.pull = NRF_GPIO_PIN_NOPULL, \
.sense = NRF_GPIOTE_POLARITY_HITOLO, \
}
/**@brief Macro for configuring a pin to use a GPIO IN or PORT EVENT to detect any change on the pin.
* @details Set hi_accu to true to use IN_EVENT.*/
#define GPIOTE_CONFIG_IN_SENSE_TOGGLE(hi_accu) \
{ \
.is_watcher = false, \
.hi_accuracy = hi_accu, \
.pull = NRF_GPIO_PIN_NOPULL, \
.sense = NRF_GPIOTE_POLARITY_TOGGLE, \
}
/**@brief Output pin configuration. */
typedef struct
{
nrf_gpiote_polarity_t action; /**< Configuration of the pin task. */
nrf_gpiote_outinit_t init_state; /**< Initial state of the output pin. */
bool task_pin; /**< True if the pin is controlled by a GPIOTE task. */
} nrf_drv_gpiote_out_config_t;
/**@brief Macro for configuring a pin to use as output. GPIOTE is not used for the pin. */
#define GPIOTE_CONFIG_OUT_SIMPLE(init_high) \
{ \
.init_state = init_high ? NRF_GPIOTE_INITIAL_VALUE_HIGH : NRF_GPIOTE_INITIAL_VALUE_LOW, \
.task_pin = false, \
}
/**@brief Macro for configuring a pin to use the GPIO OUT TASK to change the state from high to low.
* @details The task will clear the pin. Therefore, the pin is set initially. */
#define GPIOTE_CONFIG_OUT_TASK_LOW \
{ \
.init_state = NRF_GPIOTE_INITIAL_VALUE_HIGH, \
.task_pin = true, \
.action = NRF_GPIOTE_POLARITY_HITOLO, \
}
/**@brief Macro for configuring a pin to use the GPIO OUT TASK to change the state from low to high.
* @details The task will set the pin. Therefore, the pin is cleared initially. */
#define GPIOTE_CONFIG_OUT_TASK_HIGH \
{ \
.init_state = NRF_GPIOTE_INITIAL_VALUE_LOW, \
.task_pin = true, \
.action = NRF_GPIOTE_POLARITY_LOTOHI, \
}
/**@brief Macro for configuring a pin to use the GPIO OUT TASK to toggle the pin state.
* @details The initial pin state must be provided. */
#define GPIOTE_CONFIG_OUT_TASK_TOGGLE(init_high) \
{ \
.init_state = init_high ? NRF_GPIOTE_INITIAL_VALUE_HIGH : NRF_GPIOTE_INITIAL_VALUE_LOW, \
.task_pin = true, \
.action = NRF_GPIOTE_POLARITY_TOGGLE, \
}
/** @brief Pin. */
typedef uint32_t nrf_drv_gpiote_pin_t;
/**
* @brief Pin event handler prototype.
* @param pin Pin that triggered this event.
* @param action Action that lead to triggering this event.
*/
typedef void (*nrf_drv_gpiote_evt_handler_t)(nrf_drv_gpiote_pin_t pin, nrf_gpiote_polarity_t action);
/**
* @brief Function for initializing the GPIOTE module.
*
* @details Only static configuration is supported to prevent the shared
* resource being customized by the initiator.
*
* @retval NRF_SUCCESS If initialization was successful.
* @retval NRF_ERROR_INVALID_STATE If the driver was already initialized.
*/
ret_code_t nrf_drv_gpiote_init(void);
/**
* @brief Function for checking if the GPIOTE module is initialized.
*
* @details The GPIOTE module is a shared module. Therefore, you should check if
* the module is already initialized and skip initialization if it is.
*
* @retval true If the module is already initialized.
* @retval false If the module is not initialized.
*/
bool nrf_drv_gpiote_is_init(void);
/**
* @brief Function for uninitializing the GPIOTE module.
*/
void nrf_drv_gpiote_uninit(void);
/**
* @brief Function for initializing a GPIOTE output pin.
* @details The output pin can be controlled by the CPU or by PPI. The initial
* configuration specifies which mode is used. If PPI mode is used, the driver
* attempts to allocate one of the available GPIOTE channels. If no channel is
* available, an error is returned.
*
* @param[in] pin Pin.
* @param[in] p_config Initial configuration.
*
* @retval NRF_SUCCESS If initialization was successful.
* @retval NRF_ERROR_INVALID_STATE If the driver is not initialized or the pin is already used.
* @retval NRF_ERROR_NO_MEM If no GPIOTE channel is available.
*/
ret_code_t nrf_drv_gpiote_out_init(nrf_drv_gpiote_pin_t pin,
nrf_drv_gpiote_out_config_t const * p_config);
/**
* @brief Function for uninitializing a GPIOTE output pin.
* @details The driver frees the GPIOTE channel if the output pin was using one.
*
* @param[in] pin Pin.
*/
void nrf_drv_gpiote_out_uninit(nrf_drv_gpiote_pin_t pin);
/**
* @brief Function for setting a GPIOTE output pin.
*
* @param[in] pin Pin.
*/
void nrf_drv_gpiote_out_set(nrf_drv_gpiote_pin_t pin);
/**
* @brief Function for clearing a GPIOTE output pin.
*
* @param[in] pin Pin.
*/
void nrf_drv_gpiote_out_clear(nrf_drv_gpiote_pin_t pin);
/**
* @brief Function for toggling a GPIOTE output pin.
*
* @param[in] pin Pin.
*/
void nrf_drv_gpiote_out_toggle(nrf_drv_gpiote_pin_t pin);
/**
* @brief Function for enabling a GPIOTE output pin task.
*
* @param[in] pin Pin.
*/
void nrf_drv_gpiote_out_task_enable(nrf_drv_gpiote_pin_t pin);
/**
* @brief Function for disabling a GPIOTE output pin task.
*
* @param[in] pin Pin.
*/
void nrf_drv_gpiote_out_task_disable(nrf_drv_gpiote_pin_t pin);
/**
* @brief Function for getting the address of a configurable GPIOTE task.
*
* @param[in] pin Pin.
*/
uint32_t nrf_drv_gpiote_out_task_addr_get(nrf_drv_gpiote_pin_t pin);
/**
* @brief Function for initializing a GPIOTE input pin.
* @details The input pin can act in two ways:
* - lower accuracy but low power (high frequency clock not needed)
* - higher accuracy (high frequency clock required)
*
* The initial configuration specifies which mode is used.
* If high-accuracy mode is used, the driver attempts to allocate one
* of the available GPIOTE channels. If no channel is
* available, an error is returned.
* In low accuracy mode SENSE feature is used. In this case only one active pin
* can be detected at a time. It can be worked around by setting all of the used
* low accuracy pins to toggle mode.
* For more information about SENSE functionality, refer to Product Specification.
*
* @param[in] pin Pin.
* @param[in] p_config Initial configuration.
* @param[in] evt_handler User function to be called when the configured transition occurs.
*
* @retval NRF_SUCCESS If initialization was successful.
* @retval NRF_ERROR_INVALID_STATE If the driver is not initialized or the pin is already used.
* @retval NRF_ERROR_NO_MEM If no GPIOTE channel is available.
*/
ret_code_t nrf_drv_gpiote_in_init(nrf_drv_gpiote_pin_t pin,
nrf_drv_gpiote_in_config_t const * p_config,
nrf_drv_gpiote_evt_handler_t evt_handler);
/**
* @brief Function for uninitializing a GPIOTE input pin.
* @details The driver frees the GPIOTE channel if the input pin was using one.
*
* @param[in] pin Pin.
*/
void nrf_drv_gpiote_in_uninit(nrf_drv_gpiote_pin_t pin);
/**
* @brief Function for enabling sensing of a GPIOTE input pin.
*
* @details If the input pin is configured as high-accuracy pin, the function
* enables an IN_EVENT. Otherwise, the function enables the GPIO sense mechanism.
* Note that a PORT event is shared between multiple pins, therefore the
* interrupt is always enabled.
*
* @param[in] pin Pin.
* @param[in] int_enable True to enable the interrupt. Always valid for a high-accuracy pin.
*/
void nrf_drv_gpiote_in_event_enable(nrf_drv_gpiote_pin_t pin, bool int_enable);
/**
* @brief Function for disabling a GPIOTE input pin.
*
* @param[in] pin Pin.
*/
void nrf_drv_gpiote_in_event_disable(nrf_drv_gpiote_pin_t pin);
/**
* @brief Function for checking if a GPIOTE input pin is set.
*
* @param[in] pin Pin.
* @retval true If the input pin is set.
* @retval false If the input pin is not set.
*/
bool nrf_drv_gpiote_in_is_set(nrf_drv_gpiote_pin_t pin);
/**
* @brief Function for getting the address of a GPIOTE input pin event.
* @details If the pin is configured to use low-accuracy mode, the address of the PORT event is returned.
*
* @param[in] pin Pin.
*/
uint32_t nrf_drv_gpiote_in_event_addr_get(nrf_drv_gpiote_pin_t pin);
/**
* @brief Function for forcing a specific state on the pin configured as task.
*
* @param[in] pin Pin.
* @param[in] state Pin state.
*/
void nrf_drv_gpiote_out_task_force(nrf_drv_gpiote_pin_t pin, uint8_t state);
/**
* @brief Function for triggering the task manually.
*
* @param[in] pin Pin.
*/
void nrf_drv_gpiote_out_task_trigger(nrf_drv_gpiote_pin_t pin);
/**
*@}
**/
#endif //NRF_DRV_GPIOTE__

View File

@ -0,0 +1,402 @@
/* Copyright (c) 2015 Nordic Semiconductor. All Rights Reserved.
*
* The information contained herein is property of Nordic Semiconductor ASA.
* Terms and conditions of usage are described in detail in NORDIC
* SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT.
*
* Licensees are granted free, non-transferable use of the information. NO
* WARRANTY of ANY KIND is provided. This heading must NOT be removed from
* the file.
*
*/
#ifndef NRF_PPI_H__
#define NRF_PPI_H__
#include <stddef.h>
#include "nrf.h"
/**
* @defgroup nrf_ppi_hal PPI HAL
* @{
* @ingroup nrf_ppi
* @brief Hardware access layer for setting up Programmable Peripheral Interconnect (PPI) channels.
*/
#define NRF_PPI_TASK_SET (1UL)
/**
* @enum nrf_ppi_channel_t
* @brief PPI channels.
*/
typedef enum
{
NRF_PPI_CHANNEL0 = PPI_CHEN_CH0_Pos, /**< Channel 0. */
NRF_PPI_CHANNEL1 = PPI_CHEN_CH1_Pos, /**< Channel 1. */
NRF_PPI_CHANNEL2 = PPI_CHEN_CH2_Pos, /**< Channel 2. */
NRF_PPI_CHANNEL3 = PPI_CHEN_CH3_Pos, /**< Channel 3. */
NRF_PPI_CHANNEL4 = PPI_CHEN_CH4_Pos, /**< Channel 4. */
NRF_PPI_CHANNEL5 = PPI_CHEN_CH5_Pos, /**< Channel 5. */
NRF_PPI_CHANNEL6 = PPI_CHEN_CH6_Pos, /**< Channel 6. */
NRF_PPI_CHANNEL7 = PPI_CHEN_CH7_Pos, /**< Channel 7. */
NRF_PPI_CHANNEL8 = PPI_CHEN_CH8_Pos, /**< Channel 8. */
NRF_PPI_CHANNEL9 = PPI_CHEN_CH9_Pos, /**< Channel 9. */
NRF_PPI_CHANNEL10 = PPI_CHEN_CH10_Pos, /**< Channel 10. */
NRF_PPI_CHANNEL11 = PPI_CHEN_CH11_Pos, /**< Channel 11. */
NRF_PPI_CHANNEL12 = PPI_CHEN_CH12_Pos, /**< Channel 12. */
NRF_PPI_CHANNEL13 = PPI_CHEN_CH13_Pos, /**< Channel 13. */
NRF_PPI_CHANNEL14 = PPI_CHEN_CH14_Pos, /**< Channel 14. */
NRF_PPI_CHANNEL15 = PPI_CHEN_CH15_Pos, /**< Channel 15. */
#ifdef NRF52
NRF_PPI_CHANNEL16 = PPI_CHEN_CH16_Pos, /**< Channel 16. */
NRF_PPI_CHANNEL17 = PPI_CHEN_CH17_Pos, /**< Channel 17. */
NRF_PPI_CHANNEL18 = PPI_CHEN_CH18_Pos, /**< Channel 18. */
NRF_PPI_CHANNEL19 = PPI_CHEN_CH19_Pos, /**< Channel 19. */
#endif
NRF_PPI_CHANNEL20 = PPI_CHEN_CH20_Pos, /**< Channel 20. */
NRF_PPI_CHANNEL21 = PPI_CHEN_CH21_Pos, /**< Channel 21. */
NRF_PPI_CHANNEL22 = PPI_CHEN_CH22_Pos, /**< Channel 22. */
NRF_PPI_CHANNEL23 = PPI_CHEN_CH23_Pos, /**< Channel 23. */
NRF_PPI_CHANNEL24 = PPI_CHEN_CH24_Pos, /**< Channel 24. */
NRF_PPI_CHANNEL25 = PPI_CHEN_CH25_Pos, /**< Channel 25. */
NRF_PPI_CHANNEL26 = PPI_CHEN_CH26_Pos, /**< Channel 26. */
NRF_PPI_CHANNEL27 = PPI_CHEN_CH27_Pos, /**< Channel 27. */
NRF_PPI_CHANNEL28 = PPI_CHEN_CH28_Pos, /**< Channel 28. */
NRF_PPI_CHANNEL29 = PPI_CHEN_CH29_Pos, /**< Channel 29. */
NRF_PPI_CHANNEL30 = PPI_CHEN_CH30_Pos, /**< Channel 30. */
NRF_PPI_CHANNEL31 = PPI_CHEN_CH31_Pos /**< Channel 31. */
} nrf_ppi_channel_t;
/**
* @enum nrf_ppi_channel_group_t
* @brief PPI channel groups.
*/
typedef enum
{
NRF_PPI_CHANNEL_GROUP0 = 0, /**< Channel group 0. */
NRF_PPI_CHANNEL_GROUP1 = 1, /**< Channel group 1. */
NRF_PPI_CHANNEL_GROUP2 = 2, /**< Channel group 2. */
NRF_PPI_CHANNEL_GROUP3 = 3, /**< Channel group 3. */
#ifdef NRF52
NRF_PPI_CHANNEL_GROUP4 = 4, /**< Channel group 4. */
NRF_PPI_CHANNEL_GROUP5 = 5 /**< Channel group 5. */
#endif
} nrf_ppi_channel_group_t;
/**
* @enum nrf_ppi_channel_include_t
* @brief Definition of which PPI channels belong to a group.
*/
typedef enum
{
NRF_PPI_CHANNEL_EXCLUDE = PPI_CHG_CH0_Excluded, /**< Channel excluded from a group. */
NRF_PPI_CHANNEL_INCLUDE = PPI_CHG_CH0_Included /**< Channel included in a group. */
} nrf_ppi_channel_include_t;
/**
* @enum nrf_ppi_channel_enable_t
* @brief Definition if a PPI channel is enabled.
*/
typedef enum
{
NRF_PPI_CHANNEL_DISABLED = PPI_CHEN_CH0_Disabled, /**< Channel disabled. */
NRF_PPI_CHANNEL_ENABLED = PPI_CHEN_CH0_Enabled /**< Channel enabled. */
} nrf_ppi_channel_enable_t;
/**
* @enum nrf_ppi_task_t
* @brief PPI tasks.
*/
typedef enum
{
/*lint -save -e30 -esym(628,__INTADDR__)*/
NRF_PPI_TASK_CHG0_EN = offsetof(NRF_PPI_Type, TASKS_CHG[0].EN), /**< Task for enabling channel group 0 */
NRF_PPI_TASK_CHG0_DIS = offsetof(NRF_PPI_Type, TASKS_CHG[0].DIS), /**< Task for disabling channel group 0 */
NRF_PPI_TASK_CHG1_EN = offsetof(NRF_PPI_Type, TASKS_CHG[1].EN), /**< Task for enabling channel group 1 */
NRF_PPI_TASK_CHG1_DIS = offsetof(NRF_PPI_Type, TASKS_CHG[1].DIS), /**< Task for disabling channel group 1 */
NRF_PPI_TASK_CHG2_EN = offsetof(NRF_PPI_Type, TASKS_CHG[2].EN), /**< Task for enabling channel group 2 */
NRF_PPI_TASK_CHG2_DIS = offsetof(NRF_PPI_Type, TASKS_CHG[2].DIS), /**< Task for disabling channel group 2 */
NRF_PPI_TASK_CHG3_EN = offsetof(NRF_PPI_Type, TASKS_CHG[3].EN), /**< Task for enabling channel group 3 */
NRF_PPI_TASK_CHG3_DIS = offsetof(NRF_PPI_Type, TASKS_CHG[3].DIS), /**< Task for disabling channel group 3 */
#ifdef NRF52
NRF_PPI_TASK_CHG4_EN = offsetof(NRF_PPI_Type, TASKS_CHG[4].EN), /**< Task for enabling channel group 4 */
NRF_PPI_TASK_CHG4_DIS = offsetof(NRF_PPI_Type, TASKS_CHG[4].DIS), /**< Task for disabling channel group 4 */
NRF_PPI_TASK_CHG5_EN = offsetof(NRF_PPI_Type, TASKS_CHG[5].EN), /**< Task for enabling channel group 5 */
NRF_PPI_TASK_CHG5_DIS = offsetof(NRF_PPI_Type, TASKS_CHG[5].DIS) /**< Task for disabling channel group 5 */
#endif
/*lint -restore*/
} nrf_ppi_task_t;
/**
* @brief Function for enabling a given PPI channel.
*
* @details This function enables only one channel.
*
* @param[in] channel Channel to enable.
*
* */
__STATIC_INLINE void nrf_ppi_channel_enable(nrf_ppi_channel_t channel)
{
NRF_PPI->CHENSET = PPI_CHENSET_CH0_Set << ((uint32_t) channel);
}
/**
* @brief Function for disabling a given PPI channel.
*
* @details This function disables only one channel.
*
* @param[in] channel Channel to disable.
*/
__STATIC_INLINE void nrf_ppi_channel_disable(nrf_ppi_channel_t channel)
{
NRF_PPI->CHENCLR = PPI_CHENCLR_CH0_Clear << ((uint32_t) channel);
}
/**
* @brief Function for checking if a given PPI channel is enabled.
*
* @details This function checks only one channel.
*
* @param[in] channel Channel to check.
*
* @retval NRF_PPI_CHANNEL_ENABLED If the channel is enabled.
* @retval NRF_PPI_CHANNEL_DISABLED If the channel is not enabled.
*
*/
__STATIC_INLINE nrf_ppi_channel_enable_t nrf_ppi_channel_enable_get(nrf_ppi_channel_t channel)
{
if (NRF_PPI->CHEN & (PPI_CHEN_CH0_Msk << ((uint32_t) channel)))
{
return NRF_PPI_CHANNEL_ENABLED;
}
else
{
return NRF_PPI_CHANNEL_DISABLED;
}
}
/**
* @brief Function for disabling all PPI channels.
*/
__STATIC_INLINE void nrf_ppi_channel_disable_all(void)
{
NRF_PPI->CHENCLR = ((uint32_t)0xFFFFFFFFuL);
}
/**
* @brief Function for disabling multiple PPI channels.
*
* @param[in] mask Channel mask.
*/
__STATIC_INLINE void nrf_ppi_channels_disable(uint32_t mask)
{
NRF_PPI->CHENCLR = mask;
}
/**
* @brief Function for setting up event and task endpoints for a given PPI channel.
*
* @param[in] eep Event register address.
*
* @param[in] tep Task register address.
*
* @param[in] channel Channel to which the given endpoints are assigned.
*/
__STATIC_INLINE void nrf_ppi_channel_endpoint_setup(nrf_ppi_channel_t channel,
uint32_t eep,
uint32_t tep)
{
NRF_PPI->CH[(uint32_t) channel].EEP = eep;
NRF_PPI->CH[(uint32_t) channel].TEP = tep;
}
#ifdef NRF52
/**
* @brief Function for setting up task endpoint for a given PPI fork.
*
* @param[in] fork_tep Task register address.
*
* @param[in] channel Channel to which the given fork endpoint is assigned.
*/
__STATIC_INLINE void nrf_ppi_fork_endpoint_setup(nrf_ppi_channel_t channel,
uint32_t fork_tep)
{
NRF_PPI->FORK[(uint32_t) channel].TEP = fork_tep;
}
/**
* @brief Function for setting up event and task endpoints for a given PPI channel and fork.
*
* @param[in] eep Event register address.
*
* @param[in] tep Task register address.
*
* @param[in] fork_tep Fork task register address (register value).
*
* @param[in] channel Channel to which the given endpoints are assigned.
*/
__STATIC_INLINE void nrf_ppi_channel_and_fork_endpoint_setup(nrf_ppi_channel_t channel,
uint32_t eep,
uint32_t tep,
uint32_t fork_tep)
{
nrf_ppi_channel_endpoint_setup(channel, eep, tep);
nrf_ppi_fork_endpoint_setup(channel, fork_tep);
}
#endif
/**
* @brief Function for including a PPI channel in a channel group.
*
* @details This function adds only one channel to the group.
*
* @param[in] channel Channel to be included in the group.
*
* @param[in] channel_group Channel group.
*
*/
__STATIC_INLINE void nrf_ppi_channel_include_in_group(nrf_ppi_channel_t channel,
nrf_ppi_channel_group_t channel_group)
{
NRF_PPI->CHG[(uint32_t) channel_group] =
NRF_PPI->CHG[(uint32_t) channel_group] | (PPI_CHG_CH0_Included << ((uint32_t) channel));
}
/**
* @brief Function for including multiple PPI channels in a channel group.
*
* @details This function adds all specified channels to the group.
*
* @param[in] channel_mask Channels to be included in the group.
*
* @param[in] channel_group Channel group.
*
*/
__STATIC_INLINE void nrf_ppi_channels_include_in_group(uint32_t channel_mask,
nrf_ppi_channel_group_t channel_group)
{
NRF_PPI->CHG[(uint32_t) channel_group] =
NRF_PPI->CHG[(uint32_t) channel_group] | (channel_mask);
}
/**
* @brief Function for removing a PPI channel from a channel group.
*
* @details This function removes only one channel from the group.
*
* @param[in] channel Channel to be removed from the group.
*
* @param[in] channel_group Channel group.
*/
__STATIC_INLINE void nrf_ppi_channel_remove_from_group(nrf_ppi_channel_t channel,
nrf_ppi_channel_group_t channel_group)
{
NRF_PPI->CHG[(uint32_t) channel_group] =
NRF_PPI->CHG[(uint32_t) channel_group] & ~(PPI_CHG_CH0_Included << ((uint32_t) channel));
}
/**
* @brief Function for removing multiple PPI channels from a channel group.
*
* @details This function removes all specified channels from the group.
*
* @param[in] channel_mask Channels to be removed from the group.
*
* @param[in] channel_group Channel group.
*/
__STATIC_INLINE void nrf_ppi_channels_remove_from_group(uint32_t channel_mask,
nrf_ppi_channel_group_t channel_group)
{
NRF_PPI->CHG[(uint32_t) channel_group] =
NRF_PPI->CHG[(uint32_t) channel_group] & ~(channel_mask);
}
/**
* @brief Function for removing all PPI channels from a channel group.
*
* @param[in] group Channel group.
*
*/
__STATIC_INLINE void nrf_ppi_channel_group_clear(nrf_ppi_channel_group_t group)
{
NRF_PPI->CHG[(uint32_t) group] = 0;
}
/**
* @brief Function for enabling a channel group.
*
* @param[in] group Channel group.
*
*/
__STATIC_INLINE void nrf_ppi_group_enable(nrf_ppi_channel_group_t group)
{
NRF_PPI->TASKS_CHG[(uint32_t) group].EN = NRF_PPI_TASK_SET;
}
/**
* @brief Function for disabling a channel group.
*
* @param[in] group Channel group.
*
*/
__STATIC_INLINE void nrf_ppi_group_disable(nrf_ppi_channel_group_t group)
{
NRF_PPI->TASKS_CHG[(uint32_t) group].DIS = NRF_PPI_TASK_SET;
}
/**
* @brief Function for setting a PPI task.
*
* @param[in] ppi_task PPI task to set.
*/
__STATIC_INLINE void nrf_ppi_task_trigger(nrf_ppi_task_t ppi_task)
{
*((volatile uint32_t *) ((uint8_t *) NRF_PPI_BASE + (uint32_t) ppi_task)) = NRF_PPI_TASK_SET;
}
/**
* @brief Function for returning the address of a specific PPI task register.
*
* @param[in] ppi_task PPI task.
*/
__STATIC_INLINE uint32_t * nrf_ppi_task_address_get(nrf_ppi_task_t ppi_task)
{
return (uint32_t *) ((uint8_t *) NRF_PPI_BASE + (uint32_t) ppi_task);
}
/**
* @brief Function for returning the PPI enable task address of a specific group.
*
* @param[in] group PPI group.
*/
__STATIC_INLINE uint32_t * nrf_ppi_task_group_enable_address_get(nrf_ppi_channel_group_t group)
{
return (uint32_t *) &NRF_PPI->TASKS_CHG[(uint32_t) group].EN;
}
/**
* @brief Function for returning the PPI disable task address of a specific group.
*
* @param[in] group PPI group.
*/
__STATIC_INLINE uint32_t * nrf_ppi_task_group_disable_address_get(nrf_ppi_channel_group_t group)
{
return (uint32_t *) &NRF_PPI->TASKS_CHG[(uint32_t) group].DIS;
}
/**
*@}
**/
/*lint --flb "Leave library region" */
#endif // NRF_PPI_H__

View File

@ -0,0 +1,304 @@
/* Copyright (c) 2014 Nordic Semiconductor. All Rights Reserved.
*
* The information contained herein is property of Nordic Semiconductor ASA.
* Terms and conditions of usage are described in detail in NORDIC
* SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT.
*
* Licensees are granted free, non-transferable use of the information. NO
* WARRANTY of ANY KIND is provided. This heading must NOT be removed from
* the file.
*
*/
/**
* @file
* @brief RTC HAL API.
*/
#ifndef NRF_RTC_H
#define NRF_RTC_H
/**
* @defgroup nrf_rtc_hal RTC HAL
* @{
* @ingroup nrf_rtc
* @brief Hardware access layer for managing the real time counter (RTC).
*/
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include "nrf.h"
#include "nrf_assert.h"
/**
* @brief Macro for getting the number of compare channels available
* in a given RTC instance.
*/
#ifdef NRF51
#define NRF_RTC_CC_CHANNEL_COUNT(id) 4
#else
#define NRF_RTC_CC_CHANNEL_COUNT(id) ((id) == 0 ? 3 : 4)
#endif
#define RTC_INPUT_FREQ 32768 /**< Input frequency of the RTC instance. */
/**< Macro for wrapping values to RTC capacity. */
#define RTC_WRAP(val) (val & RTC_COUNTER_COUNTER_Msk)
#define RTC_CHANNEL_INT_MASK(ch) ((uint32_t)NRF_RTC_INT_COMPARE0_MASK << ch)
#define RTC_CHANNEL_EVENT_ADDR(ch) (nrf_rtc_event_t)(NRF_RTC_EVENT_COMPARE_0 + ch*sizeof(uint32_t))
/**
* @enum nrf_rtc_task_t
* @brief RTC tasks.
*/
typedef enum
{
/*lint -save -e30*/
NRF_RTC_TASK_START = offsetof(NRF_RTC_Type,TASKS_START), /**< Start. */
NRF_RTC_TASK_STOP = offsetof(NRF_RTC_Type,TASKS_STOP), /**< Stop. */
NRF_RTC_TASK_CLEAR = offsetof(NRF_RTC_Type,TASKS_CLEAR), /**< Clear. */
NRF_RTC_TASK_TRIGGER_OVERFLOW = offsetof(NRF_RTC_Type,TASKS_TRIGOVRFLW),/**< Trigger overflow. */
/*lint -restore*/
} nrf_rtc_task_t;
/**
* @enum nrf_rtc_event_t
* @brief RTC events.
*/
typedef enum
{
/*lint -save -e30*/
NRF_RTC_EVENT_TICK = offsetof(NRF_RTC_Type,EVENTS_TICK), /**< Tick event. */
NRF_RTC_EVENT_OVERFLOW = offsetof(NRF_RTC_Type,EVENTS_OVRFLW), /**< Overflow event. */
NRF_RTC_EVENT_COMPARE_0 = offsetof(NRF_RTC_Type,EVENTS_COMPARE[0]), /**< Compare 0 event. */
NRF_RTC_EVENT_COMPARE_1 = offsetof(NRF_RTC_Type,EVENTS_COMPARE[1]), /**< Compare 1 event. */
NRF_RTC_EVENT_COMPARE_2 = offsetof(NRF_RTC_Type,EVENTS_COMPARE[2]), /**< Compare 2 event. */
NRF_RTC_EVENT_COMPARE_3 = offsetof(NRF_RTC_Type,EVENTS_COMPARE[3]) /**< Compare 3 event. */
/*lint -restore*/
} nrf_rtc_event_t;
/**
* @enum nrf_rtc_int_t
* @brief RTC interrupts.
*/
typedef enum
{
NRF_RTC_INT_TICK_MASK = RTC_INTENSET_TICK_Msk, /**< RTC interrupt from tick event. */
NRF_RTC_INT_OVERFLOW_MASK = RTC_INTENSET_OVRFLW_Msk, /**< RTC interrupt from overflow event. */
NRF_RTC_INT_COMPARE0_MASK = RTC_INTENSET_COMPARE0_Msk, /**< RTC interrupt from compare event on channel 0. */
NRF_RTC_INT_COMPARE1_MASK = RTC_INTENSET_COMPARE1_Msk, /**< RTC interrupt from compare event on channel 1. */
NRF_RTC_INT_COMPARE2_MASK = RTC_INTENSET_COMPARE2_Msk, /**< RTC interrupt from compare event on channel 2. */
NRF_RTC_INT_COMPARE3_MASK = RTC_INTENSET_COMPARE3_Msk /**< RTC interrupt from compare event on channel 3. */
} nrf_rtc_int_t;
/**@brief Function for setting a compare value for a channel.
*
* @param[in] p_rtc Pointer to the instance register structure.
* @param[in] ch Channel.
* @param[in] cc_val Compare value to set.
*/
__STATIC_INLINE void nrf_rtc_cc_set(NRF_RTC_Type * p_rtc, uint32_t ch, uint32_t cc_val);
/**@brief Function for returning the compare value for a channel.
*
* @param[in] p_rtc Pointer to the instance register structure.
* @param[in] ch Channel.
*
* @return COMPARE[ch] value.
*/
__STATIC_INLINE uint32_t nrf_rtc_cc_get(NRF_RTC_Type * p_rtc, uint32_t ch);
/**@brief Function for enabling interrupts.
*
* @param[in] p_rtc Pointer to the instance register structure.
* @param[in] mask Interrupt mask to be enabled.
*/
__STATIC_INLINE void nrf_rtc_int_enable(NRF_RTC_Type * p_rtc, uint32_t mask);
/**@brief Function for disabling interrupts.
*
* @param[in] p_rtc Pointer to the instance register structure.
* @param[in] mask Interrupt mask to be disabled.
*/
__STATIC_INLINE void nrf_rtc_int_disable(NRF_RTC_Type * p_rtc, uint32_t mask);
/**@brief Function for checking if interrupts are enabled.
*
* @param[in] p_rtc Pointer to the instance register structure.
* @param[in] mask Mask of interrupt flags to check.
*
* @return Mask with enabled interrupts.
*/
__STATIC_INLINE uint32_t nrf_rtc_int_is_enabled(NRF_RTC_Type * p_rtc, uint32_t mask);
/**@brief Function for returning the status of currently enabled interrupts.
*
* @param[in] p_rtc Pointer to the instance register structure.
*
* @return Value in INTEN register.
*/
__STATIC_INLINE uint32_t nrf_rtc_int_get(NRF_RTC_Type * p_rtc);
/**@brief Function for checking if an event is pending.
*
* @param[in] p_rtc Pointer to the instance register structure.
* @param[in] event Address of the event.
*
* @return Mask of pending events.
*/
__STATIC_INLINE uint32_t nrf_rtc_event_pending(NRF_RTC_Type * p_rtc, nrf_rtc_event_t event);
/**@brief Function for clearing an event.
*
* @param[in] p_rtc Pointer to the instance register structure.
* @param[in] event Event to clear.
*/
__STATIC_INLINE void nrf_rtc_event_clear(NRF_RTC_Type * p_rtc, nrf_rtc_event_t event);
/**@brief Function for returning a counter value.
*
* @param[in] p_rtc Pointer to the instance register structure.
*
* @return Counter value.
*/
__STATIC_INLINE uint32_t nrf_rtc_counter_get(NRF_RTC_Type * p_rtc);
/**@brief Function for setting a prescaler value.
*
* @param[in] p_rtc Pointer to the instance register structure.
* @param[in] val Value to set the prescaler to.
*/
__STATIC_INLINE void nrf_rtc_prescaler_set(NRF_RTC_Type * p_rtc, uint32_t val);
/**@brief Function for returning the address of an event.
*
* @param[in] p_rtc Pointer to the instance register structure.
* @param[in] event Requested event.
*
* @return Address of the requested event register.
*/
__STATIC_INLINE uint32_t nrf_rtc_event_address_get(NRF_RTC_Type * p_rtc, nrf_rtc_event_t event);
/**@brief Function for returning the address of a task.
*
* @param[in] p_rtc Pointer to the instance register structure.
* @param[in] task Requested task.
*
* @return Address of the requested task register.
*/
__STATIC_INLINE uint32_t nrf_rtc_task_address_get(NRF_RTC_Type * p_rtc, nrf_rtc_task_t task);
/**@brief Function for starting a task.
*
* @param[in] p_rtc Pointer to the instance register structure.
* @param[in] task Requested task.
*/
__STATIC_INLINE void nrf_rtc_task_trigger(NRF_RTC_Type * p_rtc, nrf_rtc_task_t task);
/**@brief Function for enabling events.
*
* @param[in] p_rtc Pointer to the instance register structure.
* @param[in] mask Mask of event flags to enable.
*/
__STATIC_INLINE void nrf_rtc_event_enable(NRF_RTC_Type * p_rtc, uint32_t mask);
/**@brief Function for disabling an event.
*
* @param[in] p_rtc Pointer to the instance register structure.
* @param[in] event Requested event.
*/
__STATIC_INLINE void nrf_rtc_event_disable(NRF_RTC_Type * p_rtc, uint32_t event);
/**
*@}
**/
#ifndef SUPPRESS_INLINE_IMPLEMENTATION
__STATIC_INLINE void nrf_rtc_cc_set(NRF_RTC_Type * p_rtc, uint32_t ch, uint32_t cc_val)
{
p_rtc->CC[ch] = cc_val;
}
__STATIC_INLINE uint32_t nrf_rtc_cc_get(NRF_RTC_Type * p_rtc, uint32_t ch)
{
return p_rtc->CC[ch];
}
__STATIC_INLINE void nrf_rtc_int_enable(NRF_RTC_Type * p_rtc, uint32_t mask)
{
p_rtc->INTENSET = mask;
}
__STATIC_INLINE void nrf_rtc_int_disable(NRF_RTC_Type * p_rtc, uint32_t mask)
{
p_rtc->INTENCLR = mask;
}
__STATIC_INLINE uint32_t nrf_rtc_int_is_enabled(NRF_RTC_Type * p_rtc, uint32_t mask)
{
return (p_rtc->INTENSET & mask);
}
__STATIC_INLINE uint32_t nrf_rtc_int_get(NRF_RTC_Type * p_rtc)
{
return p_rtc->INTENSET;
}
__STATIC_INLINE uint32_t nrf_rtc_event_pending(NRF_RTC_Type * p_rtc, nrf_rtc_event_t event)
{
return *(volatile uint32_t *)((uint8_t *)p_rtc + (uint32_t)event);
}
__STATIC_INLINE void nrf_rtc_event_clear(NRF_RTC_Type * p_rtc, nrf_rtc_event_t event)
{
*((volatile uint32_t *)((uint8_t *)p_rtc + (uint32_t)event)) = 0;
#if __CORTEX_M == 0x04
volatile uint32_t dummy = *((volatile uint32_t *)((uint8_t *)p_rtc + (uint32_t)event));
(void)dummy;
#endif
}
__STATIC_INLINE uint32_t nrf_rtc_counter_get(NRF_RTC_Type * p_rtc)
{
return p_rtc->COUNTER;
}
__STATIC_INLINE void nrf_rtc_prescaler_set(NRF_RTC_Type * p_rtc, uint32_t val)
{
ASSERT(val <= (RTC_PRESCALER_PRESCALER_Msk >> RTC_PRESCALER_PRESCALER_Pos));
p_rtc->PRESCALER = val;
}
__STATIC_INLINE uint32_t rtc_prescaler_get(NRF_RTC_Type * p_rtc)
{
return p_rtc->PRESCALER;
}
__STATIC_INLINE uint32_t nrf_rtc_event_address_get(NRF_RTC_Type * p_rtc, nrf_rtc_event_t event)
{
return (uint32_t)p_rtc + event;
}
__STATIC_INLINE uint32_t nrf_rtc_task_address_get(NRF_RTC_Type * p_rtc, nrf_rtc_task_t task)
{
return (uint32_t)p_rtc + task;
}
__STATIC_INLINE void nrf_rtc_task_trigger(NRF_RTC_Type * p_rtc, nrf_rtc_task_t task)
{
*(__IO uint32_t *)((uint32_t)p_rtc + task) = 1;
}
__STATIC_INLINE void nrf_rtc_event_enable(NRF_RTC_Type * p_rtc, uint32_t mask)
{
p_rtc->EVTENSET = mask;
}
__STATIC_INLINE void nrf_rtc_event_disable(NRF_RTC_Type * p_rtc, uint32_t mask)
{
p_rtc->EVTENCLR = mask;
}
#endif
#endif /* NRF_RTC_H */

View File

@ -0,0 +1,334 @@
/* Copyright (c) 2015 Nordic Semiconductor. All Rights Reserved.
*
* The information contained herein is property of Nordic Semiconductor ASA.
* Terms and conditions of usage are described in detail in NORDIC
* SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT.
*
* Licensees are granted free, non-transferable use of the information. NO
* WARRANTY of ANY KIND is provided. This heading must NOT be removed from
* the file.
*
*/
/**
* @defgroup nrf_spi_hal SPI HAL
* @{
* @ingroup nrf_spi_master
*
* @brief Hardware access layer for accessing the SPI peripheral.
*/
#ifndef NRF_SPI_H__
#define NRF_SPI_H__
#include <stddef.h>
#include <stdbool.h>
#include <stdint.h>
#include "nrf.h"
/**
* @brief This value can be used as a parameter for the @ref nrf_spi_pins_set
* function to specify that a given SPI signal (SCK, MOSI, or MISO)
* shall not be connected to a physical pin.
*/
#define NRF_SPI_PIN_NOT_CONNECTED 0xFFFFFFFF
/**
* @brief SPI events.
*/
typedef enum
{
/*lint -save -e30*/
NRF_SPI_EVENT_READY = offsetof(NRF_SPI_Type, EVENTS_READY) ///< TXD byte sent and RXD byte received.
/*lint -restore*/
} nrf_spi_event_t;
/**
* @brief SPI interrupts.
*/
typedef enum
{
NRF_SPI_INT_READY_MASK = SPI_INTENSET_READY_Msk ///< Interrupt on READY event.
} nrf_spi_int_mask_t;
/**
* @brief SPI data rates.
*/
typedef enum
{
NRF_SPI_FREQ_125K = SPI_FREQUENCY_FREQUENCY_K125, ///< 125 kbps.
NRF_SPI_FREQ_250K = SPI_FREQUENCY_FREQUENCY_K250, ///< 250 kbps.
NRF_SPI_FREQ_500K = SPI_FREQUENCY_FREQUENCY_K500, ///< 500 kbps.
NRF_SPI_FREQ_1M = SPI_FREQUENCY_FREQUENCY_M1, ///< 1 Mbps.
NRF_SPI_FREQ_2M = SPI_FREQUENCY_FREQUENCY_M2, ///< 2 Mbps.
NRF_SPI_FREQ_4M = SPI_FREQUENCY_FREQUENCY_M4, ///< 4 Mbps.
// [conversion to 'int' needed to prevent compilers from complaining
// that the provided value (0x80000000UL) is out of range of "int"]
NRF_SPI_FREQ_8M = (int)SPI_FREQUENCY_FREQUENCY_M8 ///< 8 Mbps.
} nrf_spi_frequency_t;
/**
* @brief SPI modes.
*/
typedef enum
{
NRF_SPI_MODE_0, ///< SCK active high, sample on leading edge of clock.
NRF_SPI_MODE_1, ///< SCK active high, sample on trailing edge of clock.
NRF_SPI_MODE_2, ///< SCK active low, sample on leading edge of clock.
NRF_SPI_MODE_3 ///< SCK active low, sample on trailing edge of clock.
} nrf_spi_mode_t;
/**
* @brief SPI bit orders.
*/
typedef enum
{
NRF_SPI_BIT_ORDER_MSB_FIRST = SPI_CONFIG_ORDER_MsbFirst, ///< Most significant bit shifted out first.
NRF_SPI_BIT_ORDER_LSB_FIRST = SPI_CONFIG_ORDER_LsbFirst ///< Least significant bit shifted out first.
} nrf_spi_bit_order_t;
/**
* @brief Function for clearing a specific SPI event.
*
* @param[in] p_spi SPI instance.
* @param[in] spi_event Event to clear.
*/
__STATIC_INLINE void nrf_spi_event_clear(NRF_SPI_Type * p_spi,
nrf_spi_event_t spi_event);
/**
* @brief Function for checking the state of a specific SPI event.
*
* @param[in] p_spi SPI instance.
* @param[in] spi_event Event to check.
*
* @retval true If the event is set.
* @retval false If the event is not set.
*/
__STATIC_INLINE bool nrf_spi_event_check(NRF_SPI_Type * p_spi,
nrf_spi_event_t spi_event);
/**
* @brief Function for getting the address of a specific SPI event register.
*
* @param[in] p_spi SPI instance.
* @param[in] spi_event Requested event.
*
* @return Address of the specified event register.
*/
__STATIC_INLINE uint32_t * nrf_spi_event_address_get(NRF_SPI_Type * p_spi,
nrf_spi_event_t spi_event);
/**
* @brief Function for enabling specified interrupts.
*
* @param[in] p_spi SPI instance.
* @param[in] spi_int_mask Interrupts to enable.
*/
__STATIC_INLINE void nrf_spi_int_enable(NRF_SPI_Type * p_spi,
uint32_t spi_int_mask);
/**
* @brief Function for disabling specified interrupts.
*
* @param[in] p_spi SPI instance.
* @param[in] spi_int_mask Interrupts to disable.
*/
__STATIC_INLINE void nrf_spi_int_disable(NRF_SPI_Type * p_spi,
uint32_t spi_int_mask);
/**
* @brief Function for retrieving the state of a given interrupt.
*
* @param[in] p_spi SPI instance.
* @param[in] spi_int Interrupt to check.
*
* @retval true If the interrupt is enabled.
* @retval false If the interrupt is not enabled.
*/
__STATIC_INLINE bool nrf_spi_int_enable_check(NRF_SPI_Type * p_spi,
nrf_spi_int_mask_t spi_int);
/**
* @brief Function for enabling the SPI peripheral.
*
* @param[in] p_spi SPI instance.
*/
__STATIC_INLINE void nrf_spi_enable(NRF_SPI_Type * p_spi);
/**
* @brief Function for disabling the SPI peripheral.
*
* @param[in] p_spi SPI instance.
*/
__STATIC_INLINE void nrf_spi_disable(NRF_SPI_Type * p_spi);
/**
* @brief Function for configuring SPI pins.
*
* If a given signal is not needed, pass the @ref NRF_SPI_PIN_NOT_CONNECTED
* value instead of its pin number.
*
* @param[in] p_spi SPI instance.
* @param[in] sck_pin SCK pin number.
* @param[in] mosi_pin MOSI pin number.
* @param[in] miso_pin MISO pin number.
*/
__STATIC_INLINE void nrf_spi_pins_set(NRF_SPI_Type * p_spi,
uint32_t sck_pin,
uint32_t mosi_pin,
uint32_t miso_pin);
/**
* @brief Function for writing data to the SPI transmitter register.
*
* @param[in] p_spi SPI instance.
* @param[in] data TX data to send.
*/
__STATIC_INLINE void nrf_spi_txd_set(NRF_SPI_Type * p_spi, uint8_t data);
/**
* @brief Function for reading data from the SPI receiver register.
*
* @param[in] p_spi SPI instance.
*
* @return RX data received.
*/
__STATIC_INLINE uint8_t nrf_spi_rxd_get(NRF_SPI_Type * p_spi);
/**
* @brief Function for setting the SPI master data rate.
*
* @param[in] p_spi SPI instance.
* @param[in] frequency SPI frequency.
*/
__STATIC_INLINE void nrf_spi_frequency_set(NRF_SPI_Type * p_spi,
nrf_spi_frequency_t frequency);
/**
* @brief Function for setting the SPI configuration.
*
* @param[in] p_spi SPI instance.
* @param[in] spi_mode SPI mode.
* @param[in] spi_bit_order SPI bit order.
*/
__STATIC_INLINE void nrf_spi_configure(NRF_SPI_Type * p_spi,
nrf_spi_mode_t spi_mode,
nrf_spi_bit_order_t spi_bit_order);
#ifndef SUPPRESS_INLINE_IMPLEMENTATION
__STATIC_INLINE void nrf_spi_event_clear(NRF_SPI_Type * p_spi,
nrf_spi_event_t spi_event)
{
*((volatile uint32_t *)((uint8_t *)p_spi + (uint32_t)spi_event)) = 0x0UL;
}
__STATIC_INLINE bool nrf_spi_event_check(NRF_SPI_Type * p_spi,
nrf_spi_event_t spi_event)
{
return (bool)*(volatile uint32_t *)((uint8_t *)p_spi + (uint32_t)spi_event);
}
__STATIC_INLINE uint32_t * nrf_spi_event_address_get(NRF_SPI_Type * p_spi,
nrf_spi_event_t spi_event)
{
return (uint32_t *)((uint8_t *)p_spi + (uint32_t)spi_event);
}
__STATIC_INLINE void nrf_spi_int_enable(NRF_SPI_Type * p_spi,
uint32_t spi_int_mask)
{
p_spi->INTENSET = spi_int_mask;
}
__STATIC_INLINE void nrf_spi_int_disable(NRF_SPI_Type * p_spi,
uint32_t spi_int_mask)
{
p_spi->INTENCLR = spi_int_mask;
}
__STATIC_INLINE bool nrf_spi_int_enable_check(NRF_SPI_Type * p_spi,
nrf_spi_int_mask_t spi_int)
{
return (bool)(p_spi->INTENSET & spi_int);
}
__STATIC_INLINE void nrf_spi_enable(NRF_SPI_Type * p_spi)
{
p_spi->ENABLE = (SPI_ENABLE_ENABLE_Enabled << SPI_ENABLE_ENABLE_Pos);
}
__STATIC_INLINE void nrf_spi_disable(NRF_SPI_Type * p_spi)
{
p_spi->ENABLE = (SPI_ENABLE_ENABLE_Disabled << SPI_ENABLE_ENABLE_Pos);
}
__STATIC_INLINE void nrf_spi_pins_set(NRF_SPI_Type * p_spi,
uint32_t sck_pin,
uint32_t mosi_pin,
uint32_t miso_pin)
{
p_spi->PSELSCK = sck_pin;
p_spi->PSELMOSI = mosi_pin;
p_spi->PSELMISO = miso_pin;
}
__STATIC_INLINE void nrf_spi_txd_set(NRF_SPI_Type * p_spi, uint8_t data)
{
p_spi->TXD = data;
}
__STATIC_INLINE uint8_t nrf_spi_rxd_get(NRF_SPI_Type * p_spi)
{
return p_spi->RXD;
}
__STATIC_INLINE void nrf_spi_frequency_set(NRF_SPI_Type * p_spi,
nrf_spi_frequency_t frequency)
{
p_spi->FREQUENCY = frequency;
}
__STATIC_INLINE void nrf_spi_configure(NRF_SPI_Type * p_spi,
nrf_spi_mode_t spi_mode,
nrf_spi_bit_order_t spi_bit_order)
{
uint32_t config = (spi_bit_order == NRF_SPI_BIT_ORDER_MSB_FIRST ?
SPI_CONFIG_ORDER_MsbFirst : SPI_CONFIG_ORDER_LsbFirst);
switch (spi_mode)
{
default:
case NRF_SPI_MODE_0:
config |= (SPI_CONFIG_CPOL_ActiveHigh << SPI_CONFIG_CPOL_Pos) |
(SPI_CONFIG_CPHA_Leading << SPI_CONFIG_CPHA_Pos);
break;
case NRF_SPI_MODE_1:
config |= (SPI_CONFIG_CPOL_ActiveHigh << SPI_CONFIG_CPOL_Pos) |
(SPI_CONFIG_CPHA_Trailing << SPI_CONFIG_CPHA_Pos);
break;
case NRF_SPI_MODE_2:
config |= (SPI_CONFIG_CPOL_ActiveLow << SPI_CONFIG_CPOL_Pos) |
(SPI_CONFIG_CPHA_Leading << SPI_CONFIG_CPHA_Pos);
break;
case NRF_SPI_MODE_3:
config |= (SPI_CONFIG_CPOL_ActiveLow << SPI_CONFIG_CPOL_Pos) |
(SPI_CONFIG_CPHA_Trailing << SPI_CONFIG_CPHA_Pos);
break;
}
p_spi->CONFIG = config;
}
#endif // SUPPRESS_INLINE_IMPLEMENTATION
#endif // NRF_SPI_H__
/** @} */

View File

@ -0,0 +1,520 @@
/* Copyright (c) 2015 Nordic Semiconductor. All Rights Reserved.
*
* The information contained herein is property of Nordic Semiconductor ASA.
* Terms and conditions of usage are described in detail in NORDIC
* SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT.
*
* Licensees are granted free, non-transferable use of the information. NO
* WARRANTY of ANY KIND is provided. This heading must NOT be removed from
* the file.
*
*/
/**
* @defgroup nrf_spim_hal SPIM HAL
* @{
* @ingroup nrf_spi_master
*
* @brief Hardware access layer for accessing the SPIM peripheral.
*/
#ifndef NRF_SPIM_H__
#define NRF_SPIM_H__
#include <stddef.h>
#include <stdbool.h>
#include <stdint.h>
#include "nrf.h"
/**
* @brief This value can be used as a parameter for the @ref nrf_spim_pins_set
* function to specify that a given SPI signal (SCK, MOSI, or MISO)
* shall not be connected to a physical pin.
*/
#define NRF_SPIM_PIN_NOT_CONNECTED 0xFFFFFFFF
/**
* @brief SPIM tasks.
*/
typedef enum
{
/*lint -save -e30*/
NRF_SPIM_TASK_START = offsetof(NRF_SPIM_Type, TASKS_START), ///< Start SPI transaction.
NRF_SPIM_TASK_STOP = offsetof(NRF_SPIM_Type, TASKS_STOP), ///< Stop SPI transaction.
NRF_SPIM_TASK_SUSPEND = offsetof(NRF_SPIM_Type, TASKS_SUSPEND), ///< Suspend SPI transaction.
NRF_SPIM_TASK_RESUME = offsetof(NRF_SPIM_Type, TASKS_RESUME) ///< Resume SPI transaction.
/*lint -restore*/
} nrf_spim_task_t;
/**
* @brief SPIM events.
*/
typedef enum
{
/*lint -save -e30*/
NRF_SPIM_EVENT_STOPPED = offsetof(NRF_SPIM_Type, EVENTS_STOPPED), ///< SPI transaction has stopped.
NRF_SPIM_EVENT_ENDRX = offsetof(NRF_SPIM_Type, EVENTS_ENDRX), ///< End of RXD buffer reached.
#ifdef NRF52
NRF_SPIM_EVENT_END = offsetof(NRF_SPIM_Type, EVENTS_END), ///< End of RXD buffer and TXD buffer reached.
#endif
NRF_SPIM_EVENT_ENDTX = offsetof(NRF_SPIM_Type, EVENTS_ENDTX), ///< End of TXD buffer reached.
NRF_SPIM_EVENT_STARTED = offsetof(NRF_SPIM_Type, EVENTS_STARTED) ///< Transaction started.
/*lint -restore*/
} nrf_spim_event_t;
#ifdef NRF52
/**
* @brief SPIM shortcuts.
*/
typedef enum
{
NRF_SPIM_SHORT_END_START_MASK = SPIM_SHORTS_END_START_Msk ///< Shortcut between END event and START task.
} nrf_spim_short_mask_t;
#endif
/**
* @brief SPIM interrupts.
*/
typedef enum
{
NRF_SPIM_INT_STOPPED_MASK = SPIM_INTENSET_STOPPED_Msk, ///< Interrupt on STOPPED event.
NRF_SPIM_INT_ENDRX_MASK = SPIM_INTENSET_ENDRX_Msk, ///< Interrupt on ENDRX event.
#ifdef NRF52
NRF_SPIM_INT_END_MASK = SPIM_INTENSET_END_Msk, ///< Interrupt on END event.
#endif
NRF_SPIM_INT_ENDTX_MASK = SPIM_INTENSET_ENDTX_Msk, ///< Interrupt on ENDTX event.
NRF_SPIM_INT_STARTED_MASK = SPIM_INTENSET_STARTED_Msk ///< Interrupt on STARTED event.
} nrf_spim_int_mask_t;
/**
* @brief SPI master data rates.
*/
typedef enum
{
NRF_SPIM_FREQ_125K = SPIM_FREQUENCY_FREQUENCY_K125, ///< 125 kbps.
NRF_SPIM_FREQ_250K = SPIM_FREQUENCY_FREQUENCY_K250, ///< 250 kbps.
NRF_SPIM_FREQ_500K = SPIM_FREQUENCY_FREQUENCY_K500, ///< 500 kbps.
NRF_SPIM_FREQ_1M = SPIM_FREQUENCY_FREQUENCY_M1, ///< 1 Mbps.
NRF_SPIM_FREQ_2M = SPIM_FREQUENCY_FREQUENCY_M2, ///< 2 Mbps.
NRF_SPIM_FREQ_4M = SPIM_FREQUENCY_FREQUENCY_M4, ///< 4 Mbps.
// [conversion to 'int' needed to prevent compilers from complaining
// that the provided value (0x80000000UL) is out of range of "int"]
NRF_SPIM_FREQ_8M = (int)SPIM_FREQUENCY_FREQUENCY_M8 ///< 8 Mbps.
} nrf_spim_frequency_t;
/**
* @brief SPI modes.
*/
typedef enum
{
NRF_SPIM_MODE_0, ///< SCK active high, sample on leading edge of clock.
NRF_SPIM_MODE_1, ///< SCK active high, sample on trailing edge of clock.
NRF_SPIM_MODE_2, ///< SCK active low, sample on leading edge of clock.
NRF_SPIM_MODE_3 ///< SCK active low, sample on trailing edge of clock.
} nrf_spim_mode_t;
/**
* @brief SPI bit orders.
*/
typedef enum
{
NRF_SPIM_BIT_ORDER_MSB_FIRST = SPIM_CONFIG_ORDER_MsbFirst, ///< Most significant bit shifted out first.
NRF_SPIM_BIT_ORDER_LSB_FIRST = SPIM_CONFIG_ORDER_LsbFirst ///< Least significant bit shifted out first.
} nrf_spim_bit_order_t;
/**
* @brief Function for activating a specific SPIM task.
*
* @param[in] p_spim SPIM instance.
* @param[in] spim_task Task to activate.
*/
__STATIC_INLINE void nrf_spim_task_trigger(NRF_SPIM_Type * p_spim,
nrf_spim_task_t spim_task);
/**
* @brief Function for getting the address of a specific SPIM task register.
*
* @param[in] p_spim SPIM instance.
* @param[in] spim_task Requested task.
*
* @return Address of the specified task register.
*/
__STATIC_INLINE uint32_t nrf_spim_task_address_get(NRF_SPIM_Type * p_spim,
nrf_spim_task_t spim_task);
/**
* @brief Function for clearing a specific SPIM event.
*
* @param[in] p_spim SPIM instance.
* @param[in] spim_event Event to clear.
*/
__STATIC_INLINE void nrf_spim_event_clear(NRF_SPIM_Type * p_spim,
nrf_spim_event_t spim_event);
/**
* @brief Function for checking the state of a specific SPIM event.
*
* @param[in] p_spim SPIM instance.
* @param[in] spim_event Event to check.
*
* @retval true If the event is set.
* @retval false If the event is not set.
*/
__STATIC_INLINE bool nrf_spim_event_check(NRF_SPIM_Type * p_spim,
nrf_spim_event_t spim_event);
/**
* @brief Function for getting the address of a specific SPIM event register.
*
* @param[in] p_spim SPIM instance.
* @param[in] spim_event Requested event.
*
* @return Address of the specified event register.
*/
__STATIC_INLINE uint32_t nrf_spim_event_address_get(NRF_SPIM_Type * p_spim,
nrf_spim_event_t spim_event);
#ifdef NRF52
/**
* @brief Function for enabling specified shortcuts.
*
* @param[in] p_spim SPIM instance.
* @param[in] spim_shorts_mask Shortcuts to enable.
*/
__STATIC_INLINE void nrf_spim_shorts_enable(NRF_SPIM_Type * p_spim,
uint32_t spim_shorts_mask);
/**
* @brief Function for disabling specified shortcuts.
*
* @param[in] p_spim SPIM instance.
* @param[in] spim_shorts_mask Shortcuts to disable.
*/
__STATIC_INLINE void nrf_spim_shorts_disable(NRF_SPIM_Type * p_spim,
uint32_t spim_shorts_mask);
/**
* @brief Function for getting shorts setting.
*
* @param[in] p_spim SPIM instance.
*/
__STATIC_INLINE uint32_t nrf_spim_shorts_get(NRF_SPIM_Type * p_spim);
#endif
/**
* @brief Function for enabling specified interrupts.
*
* @param[in] p_spim SPIM instance.
* @param[in] spim_int_mask Interrupts to enable.
*/
__STATIC_INLINE void nrf_spim_int_enable(NRF_SPIM_Type * p_spim,
uint32_t spim_int_mask);
/**
* @brief Function for disabling specified interrupts.
*
* @param[in] p_spim SPIM instance.
* @param[in] spim_int_mask Interrupts to disable.
*/
__STATIC_INLINE void nrf_spim_int_disable(NRF_SPIM_Type * p_spim,
uint32_t spim_int_mask);
/**
* @brief Function for retrieving the state of a given interrupt.
*
* @param[in] p_spim SPIM instance.
* @param[in] spim_int Interrupt to check.
*
* @retval true If the interrupt is enabled.
* @retval false If the interrupt is not enabled.
*/
__STATIC_INLINE bool nrf_spim_int_enable_check(NRF_SPIM_Type * p_spim,
nrf_spim_int_mask_t spim_int);
/**
* @brief Function for enabling the SPIM peripheral.
*
* @param[in] p_spim SPIM instance.
*/
__STATIC_INLINE void nrf_spim_enable(NRF_SPIM_Type * p_spim);
/**
* @brief Function for disabling the SPIM peripheral.
*
* @param[in] p_spim SPIM instance.
*/
__STATIC_INLINE void nrf_spim_disable(NRF_SPIM_Type * p_spim);
/**
* @brief Function for configuring SPIM pins.
*
* If a given signal is not needed, pass the @ref NRF_SPIM_PIN_NOT_CONNECTED
* value instead of its pin number.
*
* @param[in] p_spim SPIM instance.
* @param[in] sck_pin SCK pin number.
* @param[in] mosi_pin MOSI pin number.
* @param[in] miso_pin MISO pin number.
*/
__STATIC_INLINE void nrf_spim_pins_set(NRF_SPIM_Type * p_spim,
uint32_t sck_pin,
uint32_t mosi_pin,
uint32_t miso_pin);
/**
* @brief Function for setting the SPI master data rate.
*
* @param[in] p_spim SPIM instance.
* @param[in] frequency SPI frequency.
*/
__STATIC_INLINE void nrf_spim_frequency_set(NRF_SPIM_Type * p_spim,
nrf_spim_frequency_t frequency);
/**
* @brief Function for setting the transmit buffer.
*
* @param[in] p_spim SPIM instance.
* @param[in] p_buffer Pointer to the buffer with data to send.
* @param[in] length Maximum number of data bytes to transmit.
*/
__STATIC_INLINE void nrf_spim_tx_buffer_set(NRF_SPIM_Type * p_spim,
uint8_t const * p_buffer,
uint8_t length);
/**
* @brief Function for setting the receive buffer.
*
* @param[in] p_spim SPIM instance.
* @param[in] p_buffer Pointer to the buffer for received data.
* @param[in] length Maximum number of data bytes to receive.
*/
__STATIC_INLINE void nrf_spim_rx_buffer_set(NRF_SPIM_Type * p_spim,
uint8_t * p_buffer,
uint8_t length);
/**
* @brief Function for setting the SPI configuration.
*
* @param[in] p_spim SPIM instance.
* @param[in] spi_mode SPI mode.
* @param[in] spi_bit_order SPI bit order.
*/
__STATIC_INLINE void nrf_spim_configure(NRF_SPIM_Type * p_spim,
nrf_spim_mode_t spi_mode,
nrf_spim_bit_order_t spi_bit_order);
/**
* @brief Function for setting the over-read character.
*
* @param[in] p_spim SPIM instance.
* @param[in] orc Over-read character that is clocked out in case of
* an over-read of the TXD buffer.
*/
__STATIC_INLINE void nrf_spim_orc_set(NRF_SPIM_Type * p_spim,
uint8_t orc);
#ifdef NRF52
/**
* @brief Function for enabling the TX list feature.
*
* @param[in] p_spim SPIM instance.
*/
__STATIC_INLINE void nrf_spim_tx_list_enable(NRF_SPIM_Type * p_spim);
/**
* @brief Function for disabling the TX list feature.
*
* @param[in] p_spim SPIM instance.
*/
__STATIC_INLINE void nrf_spim_tx_list_disable(NRF_SPIM_Type * p_spim);
/**
* @brief Function for enabling the RX list feature.
*
* @param[in] p_spim SPIM instance.
*/
__STATIC_INLINE void nrf_spim_rx_list_enable(NRF_SPIM_Type * p_spim);
/**
* @brief Function for disabling the RX list feature.
*
* @param[in] p_spim SPIM instance.
*/
__STATIC_INLINE void nrf_spim_rx_list_disable(NRF_SPIM_Type * p_spim);
#endif
#ifndef SUPPRESS_INLINE_IMPLEMENTATION
__STATIC_INLINE void nrf_spim_task_trigger(NRF_SPIM_Type * p_spim,
nrf_spim_task_t spim_task)
{
*((volatile uint32_t *)((uint8_t *)p_spim + (uint32_t)spim_task)) = 0x1UL;
}
__STATIC_INLINE uint32_t nrf_spim_task_address_get(NRF_SPIM_Type * p_spim,
nrf_spim_task_t spim_task)
{
return (uint32_t)((uint8_t *)p_spim + (uint32_t)spim_task);
}
__STATIC_INLINE void nrf_spim_event_clear(NRF_SPIM_Type * p_spim,
nrf_spim_event_t spim_event)
{
*((volatile uint32_t *)((uint8_t *)p_spim + (uint32_t)spim_event)) = 0x0UL;
}
__STATIC_INLINE bool nrf_spim_event_check(NRF_SPIM_Type * p_spim,
nrf_spim_event_t spim_event)
{
return (bool)*(volatile uint32_t *)((uint8_t *)p_spim + (uint32_t)spim_event);
}
__STATIC_INLINE uint32_t nrf_spim_event_address_get(NRF_SPIM_Type * p_spim,
nrf_spim_event_t spim_event)
{
return (uint32_t)((uint8_t *)p_spim + (uint32_t)spim_event);
}
#ifdef NRF52
__STATIC_INLINE void nrf_spim_shorts_enable(NRF_SPIM_Type * p_spim,
uint32_t spim_shorts_mask)
{
p_spim->SHORTS |= spim_shorts_mask;
}
__STATIC_INLINE void nrf_spim_shorts_disable(NRF_SPIM_Type * p_spim,
uint32_t spim_shorts_mask)
{
p_spim->SHORTS &= ~(spim_shorts_mask);
}
__STATIC_INLINE uint32_t nrf_spim_shorts_get(NRF_SPIM_Type * p_spim)
{
return p_spim->SHORTS;
}
#endif
__STATIC_INLINE void nrf_spim_int_enable(NRF_SPIM_Type * p_spim,
uint32_t spim_int_mask)
{
p_spim->INTENSET = spim_int_mask;
}
__STATIC_INLINE void nrf_spim_int_disable(NRF_SPIM_Type * p_spim,
uint32_t spim_int_mask)
{
p_spim->INTENCLR = spim_int_mask;
}
__STATIC_INLINE bool nrf_spim_int_enable_check(NRF_SPIM_Type * p_spim,
nrf_spim_int_mask_t spim_int)
{
return (bool)(p_spim->INTENSET & spim_int);
}
__STATIC_INLINE void nrf_spim_enable(NRF_SPIM_Type * p_spim)
{
p_spim->ENABLE = (SPIM_ENABLE_ENABLE_Enabled << SPIM_ENABLE_ENABLE_Pos);
}
__STATIC_INLINE void nrf_spim_disable(NRF_SPIM_Type * p_spim)
{
p_spim->ENABLE = (SPIM_ENABLE_ENABLE_Disabled << SPIM_ENABLE_ENABLE_Pos);
}
__STATIC_INLINE void nrf_spim_pins_set(NRF_SPIM_Type * p_spim,
uint32_t sck_pin,
uint32_t mosi_pin,
uint32_t miso_pin)
{
p_spim->PSEL.SCK = sck_pin;
p_spim->PSEL.MOSI = mosi_pin;
p_spim->PSEL.MISO = miso_pin;
}
__STATIC_INLINE void nrf_spim_frequency_set(NRF_SPIM_Type * p_spim,
nrf_spim_frequency_t frequency)
{
p_spim->FREQUENCY = frequency;
}
__STATIC_INLINE void nrf_spim_tx_buffer_set(NRF_SPIM_Type * p_spim,
uint8_t const * p_buffer,
uint8_t length)
{
p_spim->TXD.PTR = (uint32_t)p_buffer;
p_spim->TXD.MAXCNT = length;
}
__STATIC_INLINE void nrf_spim_rx_buffer_set(NRF_SPIM_Type * p_spim,
uint8_t * p_buffer,
uint8_t length)
{
p_spim->RXD.PTR = (uint32_t)p_buffer;
p_spim->RXD.MAXCNT = length;
}
__STATIC_INLINE void nrf_spim_configure(NRF_SPIM_Type * p_spim,
nrf_spim_mode_t spi_mode,
nrf_spim_bit_order_t spi_bit_order)
{
uint32_t config = (spi_bit_order == NRF_SPIM_BIT_ORDER_MSB_FIRST ?
SPIM_CONFIG_ORDER_MsbFirst : SPIM_CONFIG_ORDER_LsbFirst);
switch (spi_mode)
{
default:
case NRF_SPIM_MODE_0:
config |= (SPIM_CONFIG_CPOL_ActiveHigh << SPIM_CONFIG_CPOL_Pos) |
(SPIM_CONFIG_CPHA_Leading << SPIM_CONFIG_CPHA_Pos);
break;
case NRF_SPIM_MODE_1:
config |= (SPIM_CONFIG_CPOL_ActiveHigh << SPIM_CONFIG_CPOL_Pos) |
(SPIM_CONFIG_CPHA_Trailing << SPIM_CONFIG_CPHA_Pos);
break;
case NRF_SPIM_MODE_2:
config |= (SPIM_CONFIG_CPOL_ActiveLow << SPIM_CONFIG_CPOL_Pos) |
(SPIM_CONFIG_CPHA_Leading << SPIM_CONFIG_CPHA_Pos);
break;
case NRF_SPIM_MODE_3:
config |= (SPIM_CONFIG_CPOL_ActiveLow << SPIM_CONFIG_CPOL_Pos) |
(SPIM_CONFIG_CPHA_Trailing << SPIM_CONFIG_CPHA_Pos);
break;
}
p_spim->CONFIG = config;
}
__STATIC_INLINE void nrf_spim_orc_set(NRF_SPIM_Type * p_spim,
uint8_t orc)
{
p_spim->ORC = orc;
}
#ifdef NRF52
__STATIC_INLINE void nrf_spim_tx_list_enable(NRF_SPIM_Type * p_spim)
{
p_spim->TXD.LIST = 1;
}
__STATIC_INLINE void nrf_spim_tx_list_disable(NRF_SPIM_Type * p_spim)
{
p_spim->TXD.LIST = 0;
}
__STATIC_INLINE void nrf_spim_rx_list_enable(NRF_SPIM_Type * p_spim)
{
p_spim->RXD.LIST = 1;
}
__STATIC_INLINE void nrf_spim_rx_list_disable(NRF_SPIM_Type * p_spim)
{
p_spim->RXD.LIST = 0;
}
#endif
#endif // SUPPRESS_INLINE_IMPLEMENTATION
#endif // NRF_SPIM_H__
/** @} */

View File

@ -0,0 +1,576 @@
/* Copyright (c) 2014 Nordic Semiconductor. All Rights Reserved.
*
* The information contained herein is property of Nordic Semiconductor ASA.
* Terms and conditions of usage are described in detail in NORDIC
* SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT.
*
* Licensees are granted free, non-transferable use of the information. NO
* WARRANTY of ANY KIND is provided. This heading must NOT be removed from
* the file.
*
*/
/**
* @defgroup nrf_timer_hal Timer HAL
* @{
* @ingroup nrf_timer
*
* @brief Hardware access layer for accessing the timer peripheral.
*/
#ifndef NRF_TIMER_H__
#define NRF_TIMER_H__
#include <stddef.h>
#include <stdbool.h>
#include <stdint.h>
#include "nrf.h"
#include "nrf_assert.h"
/**
* @brief Macro for validating the correctness of the BIT_WIDTH setting.
*/
#ifdef NRF51
/**
* In the nRF51 Series, timer instance 0 supports all available bit widths.
* The other two instances support only 8 and 16 bits.
*/
#define NRF_TIMER_IS_BIT_WIDTH_VALID(p_timer, bit_width) \
((p_timer == NRF_TIMER0) || (bit_width <= NRF_TIMER_BIT_WIDTH_16))
#else
/**
* In the nRF52 Series, all timer instances support all available bit widths.
*/
#define NRF_TIMER_IS_BIT_WIDTH_VALID(p_timer, bit_width) true
#endif
/**
* @brief Macro for getting the number of capture/compare channels available
* in a given timer instance.
*/
#ifdef NRF51
#define NRF_TIMER_CC_CHANNEL_COUNT(id) 4
#else
#define NRF_TIMER_CC_CHANNEL_COUNT(id) ((id) <= 2 ? 4 : 6)
#endif
/**
* @brief Timer tasks.
*/
typedef enum
{
/*lint -save -e30 -esym(628,__INTADDR__)*/
NRF_TIMER_TASK_START = offsetof(NRF_TIMER_Type, TASKS_START), ///< Task for starting the timer.
NRF_TIMER_TASK_STOP = offsetof(NRF_TIMER_Type, TASKS_STOP), ///< Task for stopping the timer.
NRF_TIMER_TASK_COUNT = offsetof(NRF_TIMER_Type, TASKS_COUNT), ///< Task for incrementing the timer (in counter mode).
NRF_TIMER_TASK_CLEAR = offsetof(NRF_TIMER_Type, TASKS_CLEAR), ///< Task for resetting the timer value.
NRF_TIMER_TASK_SHUTDOWN = offsetof(NRF_TIMER_Type, TASKS_SHUTDOWN), ///< Task for powering off the timer.
NRF_TIMER_TASK_CAPTURE0 = offsetof(NRF_TIMER_Type, TASKS_CAPTURE[0]), ///< Task for capturing the timer value on channel 0.
NRF_TIMER_TASK_CAPTURE1 = offsetof(NRF_TIMER_Type, TASKS_CAPTURE[1]), ///< Task for capturing the timer value on channel 1.
NRF_TIMER_TASK_CAPTURE2 = offsetof(NRF_TIMER_Type, TASKS_CAPTURE[2]), ///< Task for capturing the timer value on channel 2.
NRF_TIMER_TASK_CAPTURE3 = offsetof(NRF_TIMER_Type, TASKS_CAPTURE[3]), ///< Task for capturing the timer value on channel 3.
#ifdef NRF52
NRF_TIMER_TASK_CAPTURE4 = offsetof(NRF_TIMER_Type, TASKS_CAPTURE[4]), ///< Task for capturing the timer value on channel 4.
NRF_TIMER_TASK_CAPTURE5 = offsetof(NRF_TIMER_Type, TASKS_CAPTURE[5]), ///< Task for capturing the timer value on channel 5.
#endif
/*lint -restore*/
} nrf_timer_task_t;
/**
* @brief Timer events.
*/
typedef enum
{
/*lint -save -e30*/
NRF_TIMER_EVENT_COMPARE0 = offsetof(NRF_TIMER_Type, EVENTS_COMPARE[0]), ///< Event from compare channel 0.
NRF_TIMER_EVENT_COMPARE1 = offsetof(NRF_TIMER_Type, EVENTS_COMPARE[1]), ///< Event from compare channel 1.
NRF_TIMER_EVENT_COMPARE2 = offsetof(NRF_TIMER_Type, EVENTS_COMPARE[2]), ///< Event from compare channel 2.
NRF_TIMER_EVENT_COMPARE3 = offsetof(NRF_TIMER_Type, EVENTS_COMPARE[3]), ///< Event from compare channel 3.
#ifdef NRF52
NRF_TIMER_EVENT_COMPARE4 = offsetof(NRF_TIMER_Type, EVENTS_COMPARE[4]), ///< Event from compare channel 4.
NRF_TIMER_EVENT_COMPARE5 = offsetof(NRF_TIMER_Type, EVENTS_COMPARE[5]), ///< Event from compare channel 5.
#endif
/*lint -restore*/
} nrf_timer_event_t;
/**
* @brief Types of timer shortcuts.
*/
typedef enum
{
NRF_TIMER_SHORT_COMPARE0_STOP_MASK = TIMER_SHORTS_COMPARE0_STOP_Msk, ///< Shortcut for stopping the timer based on compare 0.
NRF_TIMER_SHORT_COMPARE1_STOP_MASK = TIMER_SHORTS_COMPARE1_STOP_Msk, ///< Shortcut for stopping the timer based on compare 1.
NRF_TIMER_SHORT_COMPARE2_STOP_MASK = TIMER_SHORTS_COMPARE2_STOP_Msk, ///< Shortcut for stopping the timer based on compare 2.
NRF_TIMER_SHORT_COMPARE3_STOP_MASK = TIMER_SHORTS_COMPARE3_STOP_Msk, ///< Shortcut for stopping the timer based on compare 3.
#ifdef NRF52
NRF_TIMER_SHORT_COMPARE4_STOP_MASK = TIMER_SHORTS_COMPARE4_STOP_Msk, ///< Shortcut for stopping the timer based on compare 4.
NRF_TIMER_SHORT_COMPARE5_STOP_MASK = TIMER_SHORTS_COMPARE5_STOP_Msk, ///< Shortcut for stopping the timer based on compare 5.
#endif
NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK = TIMER_SHORTS_COMPARE0_CLEAR_Msk, ///< Shortcut for clearing the timer based on compare 0.
NRF_TIMER_SHORT_COMPARE1_CLEAR_MASK = TIMER_SHORTS_COMPARE1_CLEAR_Msk, ///< Shortcut for clearing the timer based on compare 1.
NRF_TIMER_SHORT_COMPARE2_CLEAR_MASK = TIMER_SHORTS_COMPARE2_CLEAR_Msk, ///< Shortcut for clearing the timer based on compare 2.
NRF_TIMER_SHORT_COMPARE3_CLEAR_MASK = TIMER_SHORTS_COMPARE3_CLEAR_Msk, ///< Shortcut for clearing the timer based on compare 3.
#ifdef NRF52
NRF_TIMER_SHORT_COMPARE4_CLEAR_MASK = TIMER_SHORTS_COMPARE4_CLEAR_Msk, ///< Shortcut for clearing the timer based on compare 4.
NRF_TIMER_SHORT_COMPARE5_CLEAR_MASK = TIMER_SHORTS_COMPARE5_CLEAR_Msk, ///< Shortcut for clearing the timer based on compare 5.
#endif
} nrf_timer_short_mask_t;
/**
* @brief Timer modes.
*/
typedef enum
{
NRF_TIMER_MODE_TIMER = TIMER_MODE_MODE_Timer, ///< Timer mode: timer.
NRF_TIMER_MODE_COUNTER = TIMER_MODE_MODE_Counter, ///< Timer mode: counter.
#ifdef NRF52
NRF_TIMER_MODE_LOW_POWER_COUNTER = TIMER_MODE_MODE_LowPowerCounter, ///< Timer mode: low-power counter.
#endif
} nrf_timer_mode_t;
/**
* @brief Timer bit width.
*/
typedef enum
{
NRF_TIMER_BIT_WIDTH_8 = TIMER_BITMODE_BITMODE_08Bit, ///< Timer bit width 8 bit.
NRF_TIMER_BIT_WIDTH_16 = TIMER_BITMODE_BITMODE_16Bit, ///< Timer bit width 16 bit.
NRF_TIMER_BIT_WIDTH_24 = TIMER_BITMODE_BITMODE_24Bit, ///< Timer bit width 24 bit.
NRF_TIMER_BIT_WIDTH_32 = TIMER_BITMODE_BITMODE_32Bit ///< Timer bit width 32 bit.
} nrf_timer_bit_width_t;
/**
* @brief Timer prescalers.
*/
typedef enum
{
NRF_TIMER_FREQ_16MHz = 0, ///< Timer frequency 16 MHz.
NRF_TIMER_FREQ_8MHz, ///< Timer frequency 8 MHz.
NRF_TIMER_FREQ_4MHz, ///< Timer frequency 4 MHz.
NRF_TIMER_FREQ_2MHz, ///< Timer frequency 2 MHz.
NRF_TIMER_FREQ_1MHz, ///< Timer frequency 1 MHz.
NRF_TIMER_FREQ_500kHz, ///< Timer frequency 500 kHz.
NRF_TIMER_FREQ_250kHz, ///< Timer frequency 250 kHz.
NRF_TIMER_FREQ_125kHz, ///< Timer frequency 125 kHz.
NRF_TIMER_FREQ_62500Hz, ///< Timer frequency 62500 Hz.
NRF_TIMER_FREQ_31250Hz ///< Timer frequency 31250 Hz.
} nrf_timer_frequency_t;
/**
* @brief Timer capture/compare channels.
*/
typedef enum
{
NRF_TIMER_CC_CHANNEL0 = 0, ///< Timer capture/compare channel 0.
NRF_TIMER_CC_CHANNEL1, ///< Timer capture/compare channel 1.
NRF_TIMER_CC_CHANNEL2, ///< Timer capture/compare channel 2.
NRF_TIMER_CC_CHANNEL3, ///< Timer capture/compare channel 3.
#ifdef NRF52
NRF_TIMER_CC_CHANNEL4, ///< Timer capture/compare channel 4.
NRF_TIMER_CC_CHANNEL5, ///< Timer capture/compare channel 5.
#endif
} nrf_timer_cc_channel_t;
/**
* @brief Timer interrupts.
*/
typedef enum
{
NRF_TIMER_INT_COMPARE0_MASK = TIMER_INTENSET_COMPARE0_Msk, ///< Timer interrupt from compare event on channel 0.
NRF_TIMER_INT_COMPARE1_MASK = TIMER_INTENSET_COMPARE1_Msk, ///< Timer interrupt from compare event on channel 1.
NRF_TIMER_INT_COMPARE2_MASK = TIMER_INTENSET_COMPARE2_Msk, ///< Timer interrupt from compare event on channel 2.
NRF_TIMER_INT_COMPARE3_MASK = TIMER_INTENSET_COMPARE3_Msk, ///< Timer interrupt from compare event on channel 3.
#ifdef NRF52
NRF_TIMER_INT_COMPARE4_MASK = TIMER_INTENSET_COMPARE4_Msk, ///< Timer interrupt from compare event on channel 4.
NRF_TIMER_INT_COMPARE5_MASK = TIMER_INTENSET_COMPARE5_Msk, ///< Timer interrupt from compare event on channel 5.
#endif
} nrf_timer_int_mask_t;
/**
* @brief Function for activating a specific timer task.
*
* @param[in] p_timer Timer instance.
* @param[in] task Task to activate.
*/
__STATIC_INLINE void nrf_timer_task_trigger(NRF_TIMER_Type * p_timer,
nrf_timer_task_t task);
/**
* @brief Function for getting the address of a specific timer task register.
*
* @param[in] p_timer Timer instance.
* @param[in] task Requested task.
*
* @return Address of the specified task register.
*/
__STATIC_INLINE uint32_t * nrf_timer_task_address_get(NRF_TIMER_Type * p_timer,
nrf_timer_task_t task);
/**
* @brief Function for clearing a specific timer event.
*
* @param[in] p_timer Timer instance.
* @param[in] event Event to clear.
*/
__STATIC_INLINE void nrf_timer_event_clear(NRF_TIMER_Type * p_timer,
nrf_timer_event_t event);
/**
* @brief Function for checking the state of a specific timer event.
*
* @param[in] p_timer Timer instance.
* @param[in] event Event to check.
*
* @retval true If the event is set.
* @retval false If the event is not set.
*/
__STATIC_INLINE bool nrf_timer_event_check(NRF_TIMER_Type * p_timer,
nrf_timer_event_t event);
/**
* @brief Function for getting the address of a specific timer event register.
*
* @param[in] p_timer Timer instance.
* @param[in] event Requested event.
*
* @return Address of the specified event register.
*/
__STATIC_INLINE uint32_t * nrf_timer_event_address_get(NRF_TIMER_Type * p_timer,
nrf_timer_event_t event);
/**
* @brief Function for enabling specified shortcuts.
*
* @param[in] p_timer Timer instance.
* @param[in] timer_shorts_mask Shortcuts to enable.
*/
__STATIC_INLINE void nrf_timer_shorts_enable(NRF_TIMER_Type * p_timer,
uint32_t timer_shorts_mask);
/**
* @brief Function for disabling specified shortcuts.
*
* @param[in] p_timer Timer instance.
* @param[in] timer_shorts_mask Shortcuts to disable.
*/
__STATIC_INLINE void nrf_timer_shorts_disable(NRF_TIMER_Type * p_timer,
uint32_t timer_shorts_mask);
/**
* @brief Function for enabling specified interrupts.
*
* @param[in] p_timer Timer instance.
* @param[in] timer_int_mask Interrupts to enable.
*/
__STATIC_INLINE void nrf_timer_int_enable(NRF_TIMER_Type * p_timer,
uint32_t timer_int_mask);
/**
* @brief Function for disabling specified interrupts.
*
* @param[in] p_timer Timer instance.
* @param[in] timer_int_mask Interrupts to disable.
*/
__STATIC_INLINE void nrf_timer_int_disable(NRF_TIMER_Type * p_timer,
uint32_t timer_int_mask);
/**
* @brief Function for retrieving the state of a given interrupt.
*
* @param[in] p_timer Timer instance.
* @param[in] timer_int Interrupt to check.
*
* @retval true If the interrupt is enabled.
* @retval false If the interrupt is not enabled.
*/
__STATIC_INLINE bool nrf_timer_int_enable_check(NRF_TIMER_Type * p_timer,
uint32_t timer_int);
/**
* @brief Function for setting the timer mode.
*
* @param[in] p_timer Timer instance.
* @param[in] mode Timer mode.
*/
__STATIC_INLINE void nrf_timer_mode_set(NRF_TIMER_Type * p_timer,
nrf_timer_mode_t mode);
/**
* @brief Function for retrieving the timer mode.
*
* @param[in] p_timer Timer instance.
*
* @return Timer mode.
*/
__STATIC_INLINE nrf_timer_mode_t nrf_timer_mode_get(NRF_TIMER_Type * p_timer);
/**
* @brief Function for setting the timer bit width.
*
* @param[in] p_timer Timer instance.
* @param[in] bit_width Timer bit width.
*/
__STATIC_INLINE void nrf_timer_bit_width_set(NRF_TIMER_Type * p_timer,
nrf_timer_bit_width_t bit_width);
/**
* @brief Function for retrieving the timer bit width.
*
* @param[in] p_timer Timer instance.
*
* @return Timer bit width.
*/
__STATIC_INLINE nrf_timer_bit_width_t nrf_timer_bit_width_get(NRF_TIMER_Type * p_timer);
/**
* @brief Function for setting the timer frequency.
*
* @param[in] p_timer Timer instance.
* @param[in] frequency Timer frequency.
*/
__STATIC_INLINE void nrf_timer_frequency_set(NRF_TIMER_Type * p_timer,
nrf_timer_frequency_t frequency);
/**
* @brief Function for retrieving the timer frequency.
*
* @param[in] p_timer Timer instance.
*
* @return Timer frequency.
*/
__STATIC_INLINE nrf_timer_frequency_t nrf_timer_frequency_get(NRF_TIMER_Type * p_timer);
/**
* @brief Function for writing the capture/compare register for a specified channel.
*
* @param[in] p_timer Timer instance.
* @param[in] cc_channel Requested capture/compare channel.
* @param[in] cc_value Value to write to the capture/compare register.
*/
__STATIC_INLINE void nrf_timer_cc_write(NRF_TIMER_Type * p_timer,
nrf_timer_cc_channel_t cc_channel,
uint32_t cc_value);
/**
* @brief Function for retrieving the capture/compare value for a specified channel.
*
* @param[in] p_timer Timer instance.
* @param[in] cc_channel Requested capture/compare channel.
*
* @return Value from the requested capture/compare register.
*/
__STATIC_INLINE uint32_t nrf_timer_cc_read(NRF_TIMER_Type * p_timer,
nrf_timer_cc_channel_t cc_channel);
/**
* @brief Function for getting a specific timer capture task.
*
* @param[in] channel Capture channel.
*
* @return Capture task.
*/
__STATIC_INLINE nrf_timer_task_t nrf_timer_capture_task_get(uint32_t channel);
/**
* @brief Function for getting a specific timer compare event.
*
* @param[in] channel Compare channel.
*
* @return Compare event.
*/
__STATIC_INLINE nrf_timer_event_t nrf_timer_compare_event_get(uint32_t channel);
/**
* @brief Function for getting a specific timer compare interrupt.
*
* @param[in] channel Compare channel.
*
* @return Compare interrupt.
*/
__STATIC_INLINE nrf_timer_int_mask_t nrf_timer_compare_int_get(uint32_t channel);
/**
* @brief Function for calculating the number of timer ticks for a given time
* (in microseconds) and timer frequency.
*
* @param[in] time_us Time in microseconds.
* @param[in] frequency Timer frequency.
*
* @return Number of timer ticks.
*/
__STATIC_INLINE uint32_t nrf_timer_us_to_ticks(uint32_t time_us,
nrf_timer_frequency_t frequency);
/**
* @brief Function for calculating the number of timer ticks for a given time
* (in milliseconds) and timer frequency.
*
* @param[in] time_ms Time in milliseconds.
* @param[in] frequency Timer frequency.
*
* @return Number of timer ticks.
*/
__STATIC_INLINE uint32_t nrf_timer_ms_to_ticks(uint32_t time_ms,
nrf_timer_frequency_t frequency);
#ifndef SUPPRESS_INLINE_IMPLEMENTATION
__STATIC_INLINE void nrf_timer_task_trigger(NRF_TIMER_Type * p_timer,
nrf_timer_task_t task)
{
*((volatile uint32_t *)((uint8_t *)p_timer + (uint32_t)task)) = 0x1UL;
}
__STATIC_INLINE uint32_t * nrf_timer_task_address_get(NRF_TIMER_Type * p_timer,
nrf_timer_task_t task)
{
return (uint32_t *)((uint8_t *)p_timer + (uint32_t)task);
}
__STATIC_INLINE void nrf_timer_event_clear(NRF_TIMER_Type * p_timer,
nrf_timer_event_t event)
{
*((volatile uint32_t *)((uint8_t *)p_timer + (uint32_t)event)) = 0x0UL;
}
__STATIC_INLINE bool nrf_timer_event_check(NRF_TIMER_Type * p_timer,
nrf_timer_event_t event)
{
return (bool)*(volatile uint32_t *)((uint8_t *)p_timer + (uint32_t)event);
}
__STATIC_INLINE uint32_t * nrf_timer_event_address_get(NRF_TIMER_Type * p_timer,
nrf_timer_event_t event)
{
return (uint32_t *)((uint8_t *)p_timer + (uint32_t)event);
}
__STATIC_INLINE void nrf_timer_shorts_enable(NRF_TIMER_Type * p_timer,
uint32_t timer_shorts_mask)
{
p_timer->SHORTS |= timer_shorts_mask;
}
__STATIC_INLINE void nrf_timer_shorts_disable(NRF_TIMER_Type * p_timer,
uint32_t timer_shorts_mask)
{
p_timer->SHORTS &= ~(timer_shorts_mask);
}
__STATIC_INLINE void nrf_timer_int_enable(NRF_TIMER_Type * p_timer,
uint32_t timer_int_mask)
{
p_timer->INTENSET = timer_int_mask;
}
__STATIC_INLINE void nrf_timer_int_disable(NRF_TIMER_Type * p_timer,
uint32_t timer_int_mask)
{
p_timer->INTENCLR = timer_int_mask;
}
__STATIC_INLINE bool nrf_timer_int_enable_check(NRF_TIMER_Type * p_timer,
uint32_t timer_int)
{
return (bool)(p_timer->INTENSET & timer_int);
}
__STATIC_INLINE void nrf_timer_mode_set(NRF_TIMER_Type * p_timer,
nrf_timer_mode_t mode)
{
p_timer->MODE = (p_timer->MODE & ~TIMER_MODE_MODE_Msk) |
((mode << TIMER_MODE_MODE_Pos) & TIMER_MODE_MODE_Msk);
}
__STATIC_INLINE nrf_timer_mode_t nrf_timer_mode_get(NRF_TIMER_Type * p_timer)
{
return (nrf_timer_mode_t)(p_timer->MODE);
}
__STATIC_INLINE void nrf_timer_bit_width_set(NRF_TIMER_Type * p_timer,
nrf_timer_bit_width_t bit_width)
{
p_timer->BITMODE = (p_timer->BITMODE & ~TIMER_BITMODE_BITMODE_Msk) |
((bit_width << TIMER_BITMODE_BITMODE_Pos) &
TIMER_BITMODE_BITMODE_Msk);
}
__STATIC_INLINE nrf_timer_bit_width_t nrf_timer_bit_width_get(NRF_TIMER_Type * p_timer)
{
return (nrf_timer_bit_width_t)(p_timer->BITMODE);
}
__STATIC_INLINE void nrf_timer_frequency_set(NRF_TIMER_Type * p_timer,
nrf_timer_frequency_t frequency)
{
p_timer->PRESCALER = (p_timer->PRESCALER & ~TIMER_PRESCALER_PRESCALER_Msk) |
((frequency << TIMER_PRESCALER_PRESCALER_Pos) &
TIMER_PRESCALER_PRESCALER_Msk);
}
__STATIC_INLINE nrf_timer_frequency_t nrf_timer_frequency_get(NRF_TIMER_Type * p_timer)
{
return (nrf_timer_frequency_t)(p_timer->PRESCALER);
}
__STATIC_INLINE void nrf_timer_cc_write(NRF_TIMER_Type * p_timer,
nrf_timer_cc_channel_t cc_channel,
uint32_t cc_value)
{
p_timer->CC[cc_channel] = cc_value;
}
__STATIC_INLINE uint32_t nrf_timer_cc_read(NRF_TIMER_Type * p_timer,
nrf_timer_cc_channel_t cc_channel)
{
return (uint32_t)p_timer->CC[cc_channel];
}
__STATIC_INLINE nrf_timer_task_t nrf_timer_capture_task_get(uint32_t channel)
{
return (nrf_timer_task_t)
((uint32_t)NRF_TIMER_TASK_CAPTURE0 + (channel * sizeof(uint32_t)));
}
__STATIC_INLINE nrf_timer_event_t nrf_timer_compare_event_get(uint32_t channel)
{
return (nrf_timer_event_t)
((uint32_t)NRF_TIMER_EVENT_COMPARE0 + (channel * sizeof(uint32_t)));
}
__STATIC_INLINE nrf_timer_int_mask_t nrf_timer_compare_int_get(uint32_t channel)
{
return (nrf_timer_int_mask_t)
((uint32_t)NRF_TIMER_INT_COMPARE0_MASK << channel);
}
__STATIC_INLINE uint32_t nrf_timer_us_to_ticks(uint32_t time_us,
nrf_timer_frequency_t frequency)
{
// The "frequency" parameter here is actually the prescaler value, and the
// timer runs at the following frequency: f = 16 MHz / 2^prescaler.
uint32_t prescaler = (uint32_t)frequency;
ASSERT(time_us <= (UINT32_MAX / 16UL));
return ((time_us * 16UL) >> prescaler);
}
__STATIC_INLINE uint32_t nrf_timer_ms_to_ticks(uint32_t time_ms,
nrf_timer_frequency_t frequency)
{
// The "frequency" parameter here is actually the prescaler value, and the
// timer runs at the following frequency: f = 16000 kHz / 2^prescaler.
uint32_t prescaler = (uint32_t)frequency;
ASSERT(time_ms <= (UINT32_MAX / 16000UL));
return ((time_ms * 16000UL) >> prescaler);
}
#endif // SUPPRESS_INLINE_IMPLEMENTATION
#endif // NRF_TIMER_H__
/** @} */

View File

@ -0,0 +1,402 @@
/* Copyright (c) 2015 Nordic Semiconductor. All Rights Reserved.
*
* The information contained herein is property of Nordic Semiconductor ASA.
* Terms and conditions of usage are described in detail in NORDIC
* SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT.
*
* Licensees are granted free, non-transferable use of the information. NO
* WARRANTY of ANY KIND is provided. This heading must NOT be removed from
* the file.
*
*/
#ifndef NRF_TWI_H__
#define NRF_TWI_H__
/**
* @defgroup nrf_twi_hal TWI HAL
* @{
* @ingroup nrf_twi_master
*
* @brief Hardware access layer for managing the TWI peripheral.
*/
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include "nrf.h"
/**
* @brief TWI tasks.
*/
typedef enum
{
/*lint -save -e30*/
NRF_TWI_TASK_STARTRX = offsetof(NRF_TWI_Type, TASKS_STARTRX), ///< Start TWI receive sequence.
NRF_TWI_TASK_STARTTX = offsetof(NRF_TWI_Type, TASKS_STARTTX), ///< Start TWI transmit sequence.
NRF_TWI_TASK_STOP = offsetof(NRF_TWI_Type, TASKS_STOP), ///< Stop TWI transaction.
NRF_TWI_TASK_SUSPEND = offsetof(NRF_TWI_Type, TASKS_SUSPEND), ///< Suspend TWI transaction.
NRF_TWI_TASK_RESUME = offsetof(NRF_TWI_Type, TASKS_RESUME) ///< Resume TWI transaction.
/*lint -restore*/
} nrf_twi_task_t;
/**
* @brief TWI events.
*/
typedef enum
{
/*lint -save -e30*/
NRF_TWI_EVENT_STOPPED = offsetof(NRF_TWI_Type, EVENTS_STOPPED), ///< TWI stopped.
NRF_TWI_EVENT_RXDREADY = offsetof(NRF_TWI_Type, EVENTS_RXDREADY), ///< TWI RXD byte received.
NRF_TWI_EVENT_TXDSENT = offsetof(NRF_TWI_Type, EVENTS_TXDSENT), ///< TWI TXD byte sent.
NRF_TWI_EVENT_ERROR = offsetof(NRF_TWI_Type, EVENTS_ERROR), ///< TWI error.
NRF_TWI_EVENT_BB = offsetof(NRF_TWI_Type, EVENTS_BB), ///< TWI byte boundary, generated before each byte that is sent or received.
NRF_TWI_EVENT_SUSPENDED = offsetof(NRF_TWI_Type, EVENTS_SUSPENDED) ///< TWI entered the suspended state.
/*lint -restore*/
} nrf_twi_event_t;
/**
* @brief TWI shortcuts.
*/
typedef enum
{
NRF_TWI_SHORT_BB_SUSPEND_MASK = TWI_SHORTS_BB_SUSPEND_Msk, ///< Shortcut between BB event and SUSPEND task.
NRF_TWI_SHORT_BB_STOP_MASK = TWI_SHORTS_BB_STOP_Msk, ///< Shortcut between BB event and STOP task.
} nrf_twi_short_mask_t;
/**
* @brief TWI interrupts.
*/
typedef enum
{
NRF_TWI_INT_STOPPED_MASK = TWI_INTENSET_STOPPED_Msk, ///< Interrupt on STOPPED event.
NRF_TWI_INT_RXDREADY_MASK = TWI_INTENSET_RXDREADY_Msk, ///< Interrupt on RXDREADY event.
NRF_TWI_INT_TXDSENT_MASK = TWI_INTENSET_TXDSENT_Msk, ///< Interrupt on TXDSENT event.
NRF_TWI_INT_ERROR_MASK = TWI_INTENSET_ERROR_Msk, ///< Interrupt on ERROR event.
NRF_TWI_INT_BB_MASK = TWI_INTENSET_BB_Msk, ///< Interrupt on BB event.
NRF_TWI_INT_SUSPENDED_MASK = TWI_INTENSET_SUSPENDED_Msk ///< Interrupt on SUSPENDED event.
} nrf_twi_int_mask_t;
/**
* @brief TWI error source.
*/
typedef enum
{
NRF_TWI_ERROR_ADDRESS_NACK = TWI_ERRORSRC_ANACK_Msk, ///< NACK received after sending the address.
NRF_TWI_ERROR_DATA_NACK = TWI_ERRORSRC_DNACK_Msk, ///< NACK received after sending a data byte.
NRF_TWI_ERROR_OVERRUN = TWI_ERRORSRC_OVERRUN_Msk ///< Overrun error.
/**< A new byte was received before the previous byte was read
* from the RXD register (previous data is lost). */
} nrf_twi_error_t;
/**
* @brief TWI master clock frequency.
*/
typedef enum
{
NRF_TWI_FREQ_100K = TWI_FREQUENCY_FREQUENCY_K100, ///< 100 kbps.
NRF_TWI_FREQ_250K = TWI_FREQUENCY_FREQUENCY_K250, ///< 250 kbps.
NRF_TWI_FREQ_400K = TWI_FREQUENCY_FREQUENCY_K400 ///< 400 kbps.
} nrf_twi_frequency_t;
/**
* @brief Function for activating a specific TWI task.
*
* @param[in] p_twi TWI instance.
* @param[in] task Task to activate.
*/
__STATIC_INLINE void nrf_twi_task_trigger(NRF_TWI_Type * p_twi,
nrf_twi_task_t task);
/**
* @brief Function for getting the address of a specific TWI task register.
*
* @param[in] p_twi TWI instance.
* @param[in] task Requested task.
*
* @return Address of the specified task register.
*/
__STATIC_INLINE uint32_t * nrf_twi_task_address_get(NRF_TWI_Type * p_twi,
nrf_twi_task_t task);
/**
* @brief Function for clearing a specific TWI event.
*
* @param[in] p_twi TWI instance.
* @param[in] event Event to clear.
*/
__STATIC_INLINE void nrf_twi_event_clear(NRF_TWI_Type * p_twi,
nrf_twi_event_t event);
/**
* @brief Function for checking the state of a specific event.
*
* @param[in] p_twi TWI instance.
* @param[in] event Event to check.
*
* @retval true If the event is set.
* @retval false If the event is not set.
*/
__STATIC_INLINE bool nrf_twi_event_check(NRF_TWI_Type * p_twi,
nrf_twi_event_t event);
/**
* @brief Function for getting the address of a specific TWI event register.
*
* @param[in] p_twi TWI instance.
* @param[in] event Requested event.
*
* @return Address of the specified event register.
*/
__STATIC_INLINE uint32_t * nrf_twi_event_address_get(NRF_TWI_Type * p_twi,
nrf_twi_event_t event);
/**
* @brief Function for enabling specified shortcuts.
*
* @param[in] p_twi TWI instance.
* @param[in] shorts_mask Shortcuts to enable.
*/
__STATIC_INLINE void nrf_twi_shorts_enable(NRF_TWI_Type * p_twi,
uint32_t shorts_mask);
/**
* @brief Function for disabling specified shortcuts.
*
* @param[in] p_twi TWI instance.
* @param[in] shorts_mask Shortcuts to disable.
*/
__STATIC_INLINE void nrf_twi_shorts_disable(NRF_TWI_Type * p_twi,
uint32_t shorts_mask);
/**
* @brief Function for enabling specified interrupts.
*
* @param[in] p_twi TWI instance.
* @param[in] int_mask Interrupts to enable.
*/
__STATIC_INLINE void nrf_twi_int_enable(NRF_TWI_Type * p_twi,
uint32_t int_mask);
/**
* @brief Function for disabling specified interrupts.
*
* @param[in] p_twi TWI instance.
* @param[in] int_mask Interrupts to disable.
*/
__STATIC_INLINE void nrf_twi_int_disable(NRF_TWI_Type * p_twi,
uint32_t int_mask);
/**
* @brief Function for retrieving the state of a given interrupt.
*
* @param[in] p_twi TWI instance.
* @param[in] int_mask Interrupt to check.
*
* @retval true If the interrupt is enabled.
* @retval false If the interrupt is not enabled.
*/
__STATIC_INLINE bool nrf_twi_int_enable_check(NRF_TWI_Type * p_twi,
nrf_twi_int_mask_t int_mask);
/**
* @brief Function for enabling the TWI peripheral.
*
* @param[in] p_twi TWI instance.
*/
__STATIC_INLINE void nrf_twi_enable(NRF_TWI_Type * p_twi);
/**
* @brief Function for disabling the TWI peripheral.
*
* @param[in] p_twi TWI instance.
*/
__STATIC_INLINE void nrf_twi_disable(NRF_TWI_Type * p_twi);
/**
* @brief Function for configuring TWI pins.
*
*
* @param[in] p_twi TWI instance.
* @param[in] scl_pin SCL pin number.
* @param[in] sda_pin SDA pin number.
*/
__STATIC_INLINE void nrf_twi_pins_set(NRF_TWI_Type * p_twi,
uint32_t scl_pin,
uint32_t sda_pin);
/**
* @brief Function for setting the TWI master clock frequency.
*
* @param[in] p_twi TWI instance.
* @param[in] frequency TWI frequency.
*/
__STATIC_INLINE void nrf_twi_frequency_set(NRF_TWI_Type * p_twi,
nrf_twi_frequency_t frequency);
/**
* @brief Function for checking the TWI error source.
*
* The error flags are cleared after reading.
*
* @param[in] p_twi TWI instance.
*
* @return Mask with error source flags.
*/
__STATIC_INLINE uint32_t nrf_twi_errorsrc_get_and_clear(NRF_TWI_Type * p_twi);
/**
* @brief Function for setting the address to be used in TWI transfers.
*
* @param[in] p_twi TWI instance.
* @param[in] address Address to be used in transfers.
*/
__STATIC_INLINE void nrf_twi_address_set(NRF_TWI_Type * p_twi, uint8_t address);
/**
* @brief Function for reading data received by TWI.
*
* @param[in] p_twi TWI instance.
*
* @return Received data.
*/
__STATIC_INLINE uint8_t nrf_twi_rxd_get(NRF_TWI_Type * p_twi);
/**
* @brief Function for writing data to be transmitted by TWI.
*
* @param[in] p_twi TWI instance.
* @param[in] data Data to be transmitted.
*/
__STATIC_INLINE void nrf_twi_txd_set(NRF_TWI_Type * p_twi, uint8_t data);
__STATIC_INLINE void nrf_twi_shorts_set(NRF_TWI_Type * p_twi,
uint32_t shorts_mask);
/**
* @}
*/
#ifndef SUPPRESS_INLINE_IMPLEMENTATION
__STATIC_INLINE void nrf_twi_task_trigger(NRF_TWI_Type * p_twi,
nrf_twi_task_t task)
{
*((volatile uint32_t *)((uint8_t *)p_twi + (uint32_t)task)) = 0x1UL;
}
__STATIC_INLINE uint32_t * nrf_twi_task_address_get(NRF_TWI_Type * p_twi,
nrf_twi_task_t task)
{
return (uint32_t *)((uint8_t *)p_twi + (uint32_t)task);
}
__STATIC_INLINE void nrf_twi_event_clear(NRF_TWI_Type * p_twi,
nrf_twi_event_t event)
{
*((volatile uint32_t *)((uint8_t *)p_twi + (uint32_t)event)) = 0x0UL;
}
__STATIC_INLINE bool nrf_twi_event_check(NRF_TWI_Type * p_twi,
nrf_twi_event_t event)
{
return (bool)*(volatile uint32_t *)((uint8_t *)p_twi + (uint32_t)event);
}
__STATIC_INLINE uint32_t * nrf_twi_event_address_get(NRF_TWI_Type * p_twi,
nrf_twi_event_t event)
{
return (uint32_t *)((uint8_t *)p_twi + (uint32_t)event);
}
__STATIC_INLINE void nrf_twi_shorts_enable(NRF_TWI_Type * p_twi,
uint32_t shorts_mask)
{
p_twi->SHORTS |= shorts_mask;
}
__STATIC_INLINE void nrf_twi_shorts_disable(NRF_TWI_Type * p_twi,
uint32_t shorts_mask)
{
p_twi->SHORTS &= ~(shorts_mask);
}
__STATIC_INLINE void nrf_twi_int_enable(NRF_TWI_Type * p_twi,
uint32_t int_mask)
{
p_twi->INTENSET = int_mask;
}
__STATIC_INLINE void nrf_twi_int_disable(NRF_TWI_Type * p_twi,
uint32_t int_mask)
{
p_twi->INTENCLR = int_mask;
}
__STATIC_INLINE bool nrf_twi_int_enable_check(NRF_TWI_Type * p_twi,
nrf_twi_int_mask_t int_mask)
{
return (bool)(p_twi->INTENSET & int_mask);
}
__STATIC_INLINE void nrf_twi_enable(NRF_TWI_Type * p_twi)
{
p_twi->ENABLE = (TWI_ENABLE_ENABLE_Enabled << TWI_ENABLE_ENABLE_Pos);
}
__STATIC_INLINE void nrf_twi_disable(NRF_TWI_Type * p_twi)
{
p_twi->ENABLE = (TWI_ENABLE_ENABLE_Disabled << TWI_ENABLE_ENABLE_Pos);
}
__STATIC_INLINE void nrf_twi_pins_set(NRF_TWI_Type * p_twi,
uint32_t scl_pin,
uint32_t sda_pin)
{
p_twi->PSELSCL = scl_pin;
p_twi->PSELSDA = sda_pin;
}
__STATIC_INLINE void nrf_twi_frequency_set(NRF_TWI_Type * p_twi,
nrf_twi_frequency_t frequency)
{
p_twi->FREQUENCY = frequency;
}
__STATIC_INLINE uint32_t nrf_twi_errorsrc_get_and_clear(NRF_TWI_Type * p_twi)
{
uint32_t error_source = p_twi->ERRORSRC;
// [error flags are cleared by writing '1' on their position]
p_twi->ERRORSRC = error_source;
return error_source;
}
__STATIC_INLINE void nrf_twi_address_set(NRF_TWI_Type * p_twi, uint8_t address)
{
p_twi->ADDRESS = address;
}
__STATIC_INLINE uint8_t nrf_twi_rxd_get(NRF_TWI_Type * p_twi)
{
return (uint8_t)p_twi->RXD;
}
__STATIC_INLINE void nrf_twi_txd_set(NRF_TWI_Type * p_twi, uint8_t data)
{
p_twi->TXD = data;
}
__STATIC_INLINE void nrf_twi_shorts_set(NRF_TWI_Type * p_twi,
uint32_t shorts_mask)
{
p_twi->SHORTS = shorts_mask;
}
#endif // SUPPRESS_INLINE_IMPLEMENTATION
#endif // NRF_TWI_H__

View File

@ -0,0 +1,471 @@
/* Copyright (c) 2015 Nordic Semiconductor. All Rights Reserved.
*
* The information contained herein is property of Nordic Semiconductor ASA.
* Terms and conditions of usage are described in detail in NORDIC
* SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT.
*
* Licensees are granted free, non-transferable use of the information. NO
* WARRANTY of ANY KIND is provided. This heading must NOT be removed from
* the file.
*
*/
#ifndef NRF_UART_H__
#define NRF_UART_H__
#include "nrf.h"
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
/**
* @defgroup nrf_uart_hal UART HAL
* @{
* @ingroup nrf_uart
*
* @brief Hardware access layer for accessing the UART peripheral.
*/
#define NRF_UART_PSEL_DISCONNECTED 0xFFFFFFFF
/**
* @enum nrf_uart_task_t
* @brief UART tasks.
*/
typedef enum
{
/*lint -save -e30 -esym(628,__INTADDR__)*/
NRF_UART_TASK_STARTRX = offsetof(NRF_UART_Type, TASKS_STARTRX), /**< Task for starting reception. */
NRF_UART_TASK_STOPRX = offsetof(NRF_UART_Type, TASKS_STOPRX), /**< Task for stopping reception. */
NRF_UART_TASK_STARTTX = offsetof(NRF_UART_Type, TASKS_STARTTX), /**< Task for starting transmission. */
NRF_UART_TASK_STOPTX = offsetof(NRF_UART_Type, TASKS_STOPTX), /**< Task for stopping transmission. */
NRF_UART_TASK_SUSPEND = offsetof(NRF_UART_Type, TASKS_SUSPEND), /**< Task for suspending UART. */
/*lint -restore*/
} nrf_uart_task_t;
/**
* @enum nrf_uart_event_t
* @brief UART events.
*/
typedef enum
{
/*lint -save -e30*/
NRF_UART_EVENT_CTS = offsetof(NRF_UART_Type, EVENTS_CTS), /**< Event from CTS line activation. */
NRF_UART_EVENT_NCTS = offsetof(NRF_UART_Type, EVENTS_NCTS), /**< Event from CTS line deactivation. */
NRF_UART_EVENT_RXDRDY = offsetof(NRF_UART_Type, EVENTS_RXDRDY),/**< Event from data ready in RXD. */
NRF_UART_EVENT_TXDRDY = offsetof(NRF_UART_Type, EVENTS_TXDRDY),/**< Event from data sent from TXD. */
NRF_UART_EVENT_ERROR = offsetof(NRF_UART_Type, EVENTS_ERROR), /**< Event from error detection. */
NRF_UART_EVENT_RXTO = offsetof(NRF_UART_Type, EVENTS_RXTO) /**< Event from receiver timeout. */
/*lint -restore*/
} nrf_uart_event_t;
/**
* @enum nrf_uart_int_mask_t
* @brief UART interrupts.
*/
typedef enum
{
/*lint -save -e30*/
NRF_UART_INT_MASK_CTS = UART_INTENCLR_CTS_Msk, /**< CTS line activation interrupt. */
NRF_UART_INT_MASK_NCTS = UART_INTENCLR_NCTS_Msk, /**< CTS line deactivation interrupt. */
NRF_UART_INT_MASK_RXDRDY = UART_INTENCLR_RXDRDY_Msk, /**< Data ready in RXD interrupt. */
NRF_UART_INT_MASK_TXDRDY = UART_INTENCLR_TXDRDY_Msk, /**< Data sent from TXD interrupt. */
NRF_UART_INT_MASK_ERROR = UART_INTENCLR_ERROR_Msk, /**< Error detection interrupt. */
NRF_UART_INT_MASK_RXTO = UART_INTENCLR_RXTO_Msk /**< Receiver timeout interrupt. */
/*lint -restore*/
} nrf_uart_int_mask_t;
/**
* @enum nrf_uart_baudrate_t
* @brief Baudrates supported by UART.
*/
typedef enum
{
#ifdef NRF52
NRF_UART_BAUDRATE_1200 = UARTE_BAUDRATE_BAUDRATE_Baud1200, /**< 1200 baud. */
NRF_UART_BAUDRATE_2400 = UARTE_BAUDRATE_BAUDRATE_Baud2400, /**< 2400 baud. */
NRF_UART_BAUDRATE_4800 = UARTE_BAUDRATE_BAUDRATE_Baud4800, /**< 4800 baud. */
NRF_UART_BAUDRATE_9600 = UARTE_BAUDRATE_BAUDRATE_Baud9600, /**< 9600 baud. */
NRF_UART_BAUDRATE_14400 = UARTE_BAUDRATE_BAUDRATE_Baud14400, /**< 14400 baud. */
NRF_UART_BAUDRATE_19200 = UARTE_BAUDRATE_BAUDRATE_Baud19200, /**< 19200 baud. */
NRF_UART_BAUDRATE_28800 = UARTE_BAUDRATE_BAUDRATE_Baud28800, /**< 28800 baud. */
NRF_UART_BAUDRATE_38400 = UARTE_BAUDRATE_BAUDRATE_Baud38400, /**< 38400 baud. */
NRF_UART_BAUDRATE_57600 = UARTE_BAUDRATE_BAUDRATE_Baud57600, /**< 57600 baud. */
NRF_UART_BAUDRATE_76800 = UARTE_BAUDRATE_BAUDRATE_Baud76800, /**< 76800 baud. */
NRF_UART_BAUDRATE_115200 = UARTE_BAUDRATE_BAUDRATE_Baud115200, /**< 115200 baud. */
NRF_UART_BAUDRATE_230400 = UARTE_BAUDRATE_BAUDRATE_Baud230400, /**< 230400 baud. */
NRF_UART_BAUDRATE_250000 = UARTE_BAUDRATE_BAUDRATE_Baud250000, /**< 250000 baud. */
NRF_UART_BAUDRATE_460800 = UARTE_BAUDRATE_BAUDRATE_Baud460800, /**< 460800 baud. */
NRF_UART_BAUDRATE_921600 = UARTE_BAUDRATE_BAUDRATE_Baud921600, /**< 921600 baud. */
NRF_UART_BAUDRATE_1000000 = UARTE_BAUDRATE_BAUDRATE_Baud1M, /**< 1000000 baud. */
#else
NRF_UART_BAUDRATE_1200 = UART_BAUDRATE_BAUDRATE_Baud1200, /**< 1200 baud. */
NRF_UART_BAUDRATE_2400 = UART_BAUDRATE_BAUDRATE_Baud2400, /**< 2400 baud. */
NRF_UART_BAUDRATE_4800 = UART_BAUDRATE_BAUDRATE_Baud4800, /**< 4800 baud. */
NRF_UART_BAUDRATE_9600 = UART_BAUDRATE_BAUDRATE_Baud9600, /**< 9600 baud. */
NRF_UART_BAUDRATE_14400 = UART_BAUDRATE_BAUDRATE_Baud14400, /**< 14400 baud. */
NRF_UART_BAUDRATE_19200 = UART_BAUDRATE_BAUDRATE_Baud19200, /**< 19200 baud. */
NRF_UART_BAUDRATE_28800 = UART_BAUDRATE_BAUDRATE_Baud28800, /**< 28800 baud. */
NRF_UART_BAUDRATE_38400 = UART_BAUDRATE_BAUDRATE_Baud38400, /**< 38400 baud. */
NRF_UART_BAUDRATE_57600 = UART_BAUDRATE_BAUDRATE_Baud57600, /**< 57600 baud. */
NRF_UART_BAUDRATE_76800 = UART_BAUDRATE_BAUDRATE_Baud76800, /**< 76800 baud. */
NRF_UART_BAUDRATE_115200 = UART_BAUDRATE_BAUDRATE_Baud115200, /**< 115200 baud. */
NRF_UART_BAUDRATE_230400 = UART_BAUDRATE_BAUDRATE_Baud230400, /**< 230400 baud. */
NRF_UART_BAUDRATE_250000 = UART_BAUDRATE_BAUDRATE_Baud250000, /**< 250000 baud. */
NRF_UART_BAUDRATE_460800 = UART_BAUDRATE_BAUDRATE_Baud460800, /**< 460800 baud. */
NRF_UART_BAUDRATE_921600 = UART_BAUDRATE_BAUDRATE_Baud921600, /**< 921600 baud. */
NRF_UART_BAUDRATE_1000000 = UART_BAUDRATE_BAUDRATE_Baud1M, /**< 1000000 baud. */
#endif
} nrf_uart_baudrate_t;
/**
* @enum nrf_uart_error_mask_t
* @brief Types of UART error masks.
*/
typedef enum
{
NRF_UART_ERROR_OVERRUN_MASK = UART_ERRORSRC_OVERRUN_Msk, /**< Overrun error. */
NRF_UART_ERROR_PARITY_MASK = UART_ERRORSRC_PARITY_Msk, /**< Parity error. */
NRF_UART_ERROR_FRAMING_MASK = UART_ERRORSRC_FRAMING_Msk, /**< Framing error. */
NRF_UART_ERROR_BREAK_MASK = UART_ERRORSRC_BREAK_Msk, /**< Break error. */
} nrf_uart_error_mask_t;
/**
* @enum nrf_uart_parity_t
* @brief Types of UART parity modes.
*/
typedef enum
{
NRF_UART_PARITY_EXCLUDED = UART_CONFIG_PARITY_Excluded << UART_CONFIG_PARITY_Pos, /**< Parity excluded. */
NRF_UART_PARITY_INCLUDED = UART_CONFIG_PARITY_Included << UART_CONFIG_PARITY_Pos, /**< Parity included. */
} nrf_uart_parity_t;
/**
* @enum nrf_uart_hwfc_t
* @brief Types of UART flow control modes.
*/
typedef enum
{
NRF_UART_HWFC_DISABLED = UART_CONFIG_HWFC_Disabled, /**< HW flow control disabled. */
NRF_UART_HWFC_ENABLED = UART_CONFIG_HWFC_Enabled, /**< HW flow control enabled. */
} nrf_uart_hwfc_t;
/**
* @brief Function for clearing a specific UART event.
*
* @param[in] p_reg UART instance.
* @param[in] event Event to clear.
*/
__STATIC_INLINE void nrf_uart_event_clear(NRF_UART_Type * p_reg, nrf_uart_event_t event);
/**
* @brief Function for checking the state of a specific UART event.
*
* @param[in] p_reg UART instance.
* @param[in] event Event to check.
*
* @retval True if event is set, False otherwise.
*/
__STATIC_INLINE bool nrf_uart_event_check(NRF_UART_Type * p_reg, nrf_uart_event_t event);
/**
* @brief Function for returning the address of a specific UART event register.
*
* @param[in] p_reg UART instance.
* @param[in] event Desired event.
*
* @retval Address of specified event register.
*/
__STATIC_INLINE uint32_t nrf_uart_event_address_get(NRF_UART_Type * p_reg,
nrf_uart_event_t event);
/**
* @brief Function for enabling a specific interrupt.
*
* @param p_reg Instance.
* @param int_mask Interrupts to enable.
*/
__STATIC_INLINE void nrf_uart_int_enable(NRF_UART_Type * p_reg, uint32_t int_mask);
/**
* @brief Function for retrieving the state of a given interrupt.
*
* @param p_reg Instance.
* @param int_mask Mask of interrupt to check.
*
* @retval true If the interrupt is enabled.
* @retval false If the interrupt is not enabled.
*/
__STATIC_INLINE bool nrf_uart_int_enable_check(NRF_UART_Type * p_reg, uint32_t int_mask);
/**
* @brief Function for disabling specific interrupts.
*
* @param p_reg Instance.
* @param int_mask Interrupts to disable.
*/
__STATIC_INLINE void nrf_uart_int_disable(NRF_UART_Type * p_reg, uint32_t int_mask);
/**
* @brief Function for getting error source mask. Function is clearing error source flags after reading.
*
* @param p_reg Instance.
* @return Mask with error source flags.
*/
__STATIC_INLINE uint32_t nrf_uart_errorsrc_get_and_clear(NRF_UART_Type * p_reg);
/**
* @brief Function for enabling UART.
*
* @param p_reg Instance.
*/
__STATIC_INLINE void nrf_uart_enable(NRF_UART_Type * p_reg);
/**
* @brief Function for disabling UART.
*
* @param p_reg Instance.
*/
__STATIC_INLINE void nrf_uart_disable(NRF_UART_Type * p_reg);
/**
* @brief Function for configuring TX/RX pins.
*
* @param p_reg Instance.
* @param pseltxd TXD pin number.
* @param pselrxd RXD pin number.
*/
__STATIC_INLINE void nrf_uart_txrx_pins_set(NRF_UART_Type * p_reg, uint32_t pseltxd, uint32_t pselrxd);
/**
* @brief Function for disconnecting TX/RX pins.
*
* @param p_reg Instance.
*/
__STATIC_INLINE void nrf_uart_txrx_pins_disconnect(NRF_UART_Type * p_reg);
/**
* @brief Function for getting TX pin.
*
* @param p_reg Instance.
*/
__STATIC_INLINE uint32_t nrf_uart_tx_pin_get(NRF_UART_Type * p_reg);
/**
* @brief Function for getting RX pin.
*
* @param p_reg Instance.
*/
__STATIC_INLINE uint32_t nrf_uart_rx_pin_get(NRF_UART_Type * p_reg);
/**
* @brief Function for getting RTS pin.
*
* @param p_reg Instance.
*/
__STATIC_INLINE uint32_t nrf_uart_rts_pin_get(NRF_UART_Type * p_reg);
/**
* @brief Function for getting CTS pin.
*
* @param p_reg Instance.
*/
__STATIC_INLINE uint32_t nrf_uart_cts_pin_get(NRF_UART_Type * p_reg);
/**
* @brief Function for configuring flow control pins.
*
* @param p_reg Instance.
* @param pselrts RTS pin number.
* @param pselcts CTS pin number.
*/
__STATIC_INLINE void nrf_uart_hwfc_pins_set(NRF_UART_Type * p_reg,
uint32_t pselrts,
uint32_t pselcts);
/**
* @brief Function for disconnecting flow control pins.
*
* @param p_reg Instance.
*/
__STATIC_INLINE void nrf_uart_hwfc_pins_disconnect(NRF_UART_Type * p_reg);
/**
* @brief Function for reading RX data.
*
* @param p_reg Instance.
* @return Received byte.
*/
__STATIC_INLINE uint8_t nrf_uart_rxd_get(NRF_UART_Type * p_reg);
/**
* @brief Function for setting Tx data.
*
* @param p_reg Instance.
* @param txd Byte.
*/
__STATIC_INLINE void nrf_uart_txd_set(NRF_UART_Type * p_reg, uint8_t txd);
/**
* @brief Function for starting an UART task.
*
* @param p_reg Instance.
* @param task Task.
*/
__STATIC_INLINE void nrf_uart_task_trigger(NRF_UART_Type * p_reg, nrf_uart_task_t task);
/**
* @brief Function for returning the address of a specific task register.
*
* @param p_reg Instance.
* @param task Task.
*
* @return Task address.
*/
__STATIC_INLINE uint32_t nrf_uart_task_address_get(NRF_UART_Type * p_reg, nrf_uart_task_t task);
/**
* @brief Function for configuring UART.
*
* @param p_reg Instance.
* @param hwfc Hardware flow control. Enabled if true.
* @param parity Parity. Included if true.
*/
__STATIC_INLINE void nrf_uart_configure(NRF_UART_Type * p_reg,
nrf_uart_parity_t parity,
nrf_uart_hwfc_t hwfc);
/**
* @brief Function for setting UART baudrate.
*
* @param p_reg Instance.
* @param baudrate Baudrate.
*/
__STATIC_INLINE void nrf_uart_baudrate_set(NRF_UART_Type * p_reg, nrf_uart_baudrate_t baudrate);
#ifndef SUPPRESS_INLINE_IMPLEMENTATION
__STATIC_INLINE void nrf_uart_event_clear(NRF_UART_Type * p_reg, nrf_uart_event_t event)
{
*((volatile uint32_t *)((uint8_t *)p_reg + (uint32_t)event)) = 0x0UL;
}
__STATIC_INLINE bool nrf_uart_event_check(NRF_UART_Type * p_reg, nrf_uart_event_t event)
{
return (bool)*(volatile uint32_t *)((uint8_t *)p_reg + (uint32_t)event);
}
__STATIC_INLINE uint32_t nrf_uart_event_address_get(NRF_UART_Type * p_reg,
nrf_uart_event_t event)
{
return (uint32_t)((uint8_t *)p_reg + (uint32_t)event);
}
__STATIC_INLINE void nrf_uart_int_enable(NRF_UART_Type * p_reg, uint32_t int_mask)
{
p_reg->INTENSET = int_mask;
}
__STATIC_INLINE bool nrf_uart_int_enable_check(NRF_UART_Type * p_reg, uint32_t int_mask)
{
return (bool)(p_reg->INTENSET & int_mask);
}
__STATIC_INLINE void nrf_uart_int_disable(NRF_UART_Type * p_reg, uint32_t int_mask)
{
p_reg->INTENCLR = int_mask;
}
__STATIC_INLINE uint32_t nrf_uart_errorsrc_get_and_clear(NRF_UART_Type * p_reg)
{
uint32_t errsrc_mask = p_reg->ERRORSRC;
p_reg->ERRORSRC = errsrc_mask;
return errsrc_mask;
}
__STATIC_INLINE void nrf_uart_enable(NRF_UART_Type * p_reg)
{
p_reg->ENABLE = UART_ENABLE_ENABLE_Enabled;
}
__STATIC_INLINE void nrf_uart_disable(NRF_UART_Type * p_reg)
{
p_reg->ENABLE = UART_ENABLE_ENABLE_Disabled;
}
__STATIC_INLINE void nrf_uart_txrx_pins_set(NRF_UART_Type * p_reg, uint32_t pseltxd, uint32_t pselrxd)
{
p_reg->PSELTXD = pseltxd;
p_reg->PSELRXD = pselrxd;
}
__STATIC_INLINE void nrf_uart_txrx_pins_disconnect(NRF_UART_Type * p_reg)
{
nrf_uart_txrx_pins_set(p_reg, NRF_UART_PSEL_DISCONNECTED, NRF_UART_PSEL_DISCONNECTED);
}
__STATIC_INLINE uint32_t nrf_uart_tx_pin_get(NRF_UART_Type * p_reg)
{
return p_reg->PSELTXD;
}
__STATIC_INLINE uint32_t nrf_uart_rx_pin_get(NRF_UART_Type * p_reg)
{
return p_reg->PSELRXD;
}
__STATIC_INLINE uint32_t nrf_uart_rts_pin_get(NRF_UART_Type * p_reg)
{
return p_reg->PSELRTS;
}
__STATIC_INLINE uint32_t nrf_uart_cts_pin_get(NRF_UART_Type * p_reg)
{
return p_reg->PSELCTS;
}
__STATIC_INLINE void nrf_uart_hwfc_pins_set(NRF_UART_Type * p_reg, uint32_t pselrts, uint32_t pselcts)
{
p_reg->PSELRTS = pselrts;
p_reg->PSELCTS = pselcts;
}
__STATIC_INLINE void nrf_uart_hwfc_pins_disconnect(NRF_UART_Type * p_reg)
{
nrf_uart_hwfc_pins_set(p_reg, NRF_UART_PSEL_DISCONNECTED, NRF_UART_PSEL_DISCONNECTED);
}
__STATIC_INLINE uint8_t nrf_uart_rxd_get(NRF_UART_Type * p_reg)
{
return p_reg->RXD;
}
__STATIC_INLINE void nrf_uart_txd_set(NRF_UART_Type * p_reg, uint8_t txd)
{
p_reg->TXD = txd;
}
__STATIC_INLINE void nrf_uart_task_trigger(NRF_UART_Type * p_reg, nrf_uart_task_t task)
{
*((volatile uint32_t *)((uint8_t *)p_reg + (uint32_t)task)) = 0x1UL;
}
__STATIC_INLINE uint32_t nrf_uart_task_address_get(NRF_UART_Type * p_reg, nrf_uart_task_t task)
{
return (uint32_t)p_reg + (uint32_t)task;
}
__STATIC_INLINE void nrf_uart_configure(NRF_UART_Type * p_reg,
nrf_uart_parity_t parity,
nrf_uart_hwfc_t hwfc)
{
p_reg->CONFIG = (uint32_t)parity | (uint32_t)hwfc;
}
__STATIC_INLINE void nrf_uart_baudrate_set(NRF_UART_Type * p_reg, nrf_uart_baudrate_t baudrate)
{
p_reg->BAUDRATE = baudrate;
}
#endif //SUPPRESS_INLINE_IMPLEMENTATION
/** @} */
#endif //NRF_UART_H__

View File

@ -0,0 +1,534 @@
/* Copyright (c) 2015 Nordic Semiconductor. All Rights Reserved.
*
* The information contained herein is property of Nordic Semiconductor ASA.
* Terms and conditions of usage are described in detail in NORDIC
* SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT.
*
* Licensees are granted free, non-transferable use of the information. NO
* WARRANTY of ANY KIND is provided. This heading must NOT be removed from
* the file.
*
*/
#ifndef NRF_UARTE_H__
#define NRF_UARTE_H__
#include "nrf.h"
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#define NRF_UARTE_PSEL_DISCONNECTED 0xFFFFFFFF
/**
* @defgroup nrf_uarte_hal UARTE HAL
* @{
* @ingroup nrf_uart
*
* @brief Hardware access layer for accessing the UARTE peripheral.
*/
/**
* @enum nrf_uarte_task_t
* @brief UARTE tasks.
*/
typedef enum
{
/*lint -save -e30*/
NRF_UARTE_TASK_STARTRX = offsetof(NRF_UARTE_Type, TASKS_STARTRX),///< Start UART receiver.
NRF_UARTE_TASK_STOPRX = offsetof(NRF_UARTE_Type, TASKS_STOPRX), ///< Stop UART receiver.
NRF_UARTE_TASK_STARTTX = offsetof(NRF_UARTE_Type, TASKS_STARTTX),///< Start UART transmitter.
NRF_UARTE_TASK_STOPTX = offsetof(NRF_UARTE_Type, TASKS_STOPTX), ///< Stop UART transmitter.
NRF_UARTE_TASK_FLUSHRX = offsetof(NRF_UARTE_Type, TASKS_FLUSHRX) ///< Flush RX FIFO in RX buffer.
/*lint -restore*/
} nrf_uarte_task_t;
/**
* @enum nrf_uarte_event_t
* @brief UARTE events.
*/
typedef enum
{
/*lint -save -e30*/
NRF_UARTE_EVENT_CTS = offsetof(NRF_UARTE_Type, EVENTS_CTS), ///< CTS is activated.
NRF_UARTE_EVENT_NCTS = offsetof(NRF_UARTE_Type, EVENTS_NCTS), ///< CTS is deactivated.
NRF_UARTE_EVENT_ENDRX = offsetof(NRF_UARTE_Type, EVENTS_ENDRX), ///< Receive buffer is filled up.
NRF_UARTE_EVENT_ENDTX = offsetof(NRF_UARTE_Type, EVENTS_ENDTX), ///< Last TX byte transmitted.
NRF_UARTE_EVENT_ERROR = offsetof(NRF_UARTE_Type, EVENTS_ERROR), ///< Error detected.
NRF_UARTE_EVENT_RXTO = offsetof(NRF_UARTE_Type, EVENTS_RXTO), ///< Receiver timeout.
NRF_UARTE_EVENT_RXSTARTED = offsetof(NRF_UARTE_Type, EVENTS_RXSTARTED),///< Receiver has started.
NRF_UARTE_EVENT_TXSTARTED = offsetof(NRF_UARTE_Type, EVENTS_TXSTARTED),///< Transmitter has started.
NRF_UARTE_EVENT_TXSTOPPED = offsetof(NRF_UARTE_Type, EVENTS_TXSTOPPED) ///< Transmitted stopped.
/*lint -restore*/
} nrf_uarte_event_t;
/**
* @brief Types of UARTE shortcuts.
*/
typedef enum
{
NRF_UARTE_SHORT_ENDRX_STARTRX = UARTE_SHORTS_ENDRX_STARTRX_Msk,///< Shortcut between ENDRX event and STARTRX task.
NRF_UARTE_SHORT_ENDRX_STOPRX = UARTE_SHORTS_ENDRX_STOPRX_Msk, ///< Shortcut between ENDRX event and STOPRX task.
} nrf_uarte_short_t;
/**
* @enum nrf_uarte_int_mask_t
* @brief UARTE interrupts.
*/
typedef enum
{
NRF_UARTE_INT_CTS_MASK = UARTE_INTENSET_CTS_Msk, ///< Interrupt on CTS event.
NRF_UARTE_INT_NCTSRX_MASK = UARTE_INTENSET_NCTS_Msk, ///< Interrupt on NCTS event.
NRF_UARTE_INT_ENDRX_MASK = UARTE_INTENSET_ENDRX_Msk, ///< Interrupt on ENDRX event.
NRF_UARTE_INT_ENDTX_MASK = UARTE_INTENSET_ENDTX_Msk, ///< Interrupt on ENDTX event.
NRF_UARTE_INT_ERROR_MASK = UARTE_INTENSET_ERROR_Msk, ///< Interrupt on ERROR event.
NRF_UARTE_INT_RXTO_MASK = UARTE_INTENSET_RXTO_Msk, ///< Interrupt on RXTO event.
NRF_UARTE_INT_RXSTARTED_MASK = UARTE_INTENSET_RXSTARTED_Msk,///< Interrupt on RXSTARTED event.
NRF_UARTE_INT_TXSTARTED_MASK = UARTE_INTENSET_TXSTARTED_Msk,///< Interrupt on TXSTARTED event.
NRF_UARTE_INT_TXSTOPPED_MASK = UARTE_INTENSET_TXSTOPPED_Msk ///< Interrupt on TXSTOPPED event.
} nrf_uarte_int_mask_t;
/**
* @enum nrf_uarte_baudrate_t
* @brief Baudrates supported by UARTE.
*/
typedef enum
{
NRF_UARTE_BAUDRATE_1200 = UARTE_BAUDRATE_BAUDRATE_Baud1200, ///< 1200 baud.
NRF_UARTE_BAUDRATE_2400 = UARTE_BAUDRATE_BAUDRATE_Baud2400, ///< 2400 baud.
NRF_UARTE_BAUDRATE_4800 = UARTE_BAUDRATE_BAUDRATE_Baud4800, ///< 4800 baud.
NRF_UARTE_BAUDRATE_9600 = UARTE_BAUDRATE_BAUDRATE_Baud9600, ///< 9600 baud.
NRF_UARTE_BAUDRATE_14400 = UARTE_BAUDRATE_BAUDRATE_Baud14400, ///< 14400 baud.
NRF_UARTE_BAUDRATE_19200 = UARTE_BAUDRATE_BAUDRATE_Baud19200, ///< 19200 baud.
NRF_UARTE_BAUDRATE_28800 = UARTE_BAUDRATE_BAUDRATE_Baud28800, ///< 28800 baud.
NRF_UARTE_BAUDRATE_38400 = UARTE_BAUDRATE_BAUDRATE_Baud38400, ///< 38400 baud.
NRF_UARTE_BAUDRATE_57600 = UARTE_BAUDRATE_BAUDRATE_Baud57600, ///< 57600 baud.
NRF_UARTE_BAUDRATE_76800 = UARTE_BAUDRATE_BAUDRATE_Baud76800, ///< 76800 baud.
NRF_UARTE_BAUDRATE_115200 = UARTE_BAUDRATE_BAUDRATE_Baud115200, ///< 115200 baud.
NRF_UARTE_BAUDRATE_230400 = UARTE_BAUDRATE_BAUDRATE_Baud230400, ///< 230400 baud.
NRF_UARTE_BAUDRATE_250000 = UARTE_BAUDRATE_BAUDRATE_Baud250000, ///< 250000 baud.
NRF_UARTE_BAUDRATE_460800 = UARTE_BAUDRATE_BAUDRATE_Baud460800, ///< 460800 baud.
NRF_UARTE_BAUDRATE_921600 = UARTE_BAUDRATE_BAUDRATE_Baud921600, ///< 921600 baud.
NRF_UARTE_BAUDRATE_1000000 = UARTE_BAUDRATE_BAUDRATE_Baud1M, ///< 1000000 baud.
} nrf_uarte_baudrate_t;
/**
* @enum nrf_uarte_error_mask_t
* @brief Types of UARTE error masks.
*/
typedef enum
{
NRF_UARTE_ERROR_OVERRUN_MASK = UARTE_ERRORSRC_OVERRUN_Msk, ///< Overrun error.
NRF_UARTE_ERROR_PARITY_MASK = UARTE_ERRORSRC_PARITY_Msk, ///< Parity error.
NRF_UARTE_ERROR_FRAMING_MASK = UARTE_ERRORSRC_FRAMING_Msk, ///< Framing error.
NRF_UARTE_ERROR_BREAK_MASK = UARTE_ERRORSRC_BREAK_Msk, ///< Break error.
} nrf_uarte_error_mask_t;
/**
* @enum nrf_uarte_parity_t
* @brief Types of UARTE parity modes.
*/
typedef enum
{
NRF_UARTE_PARITY_EXCLUDED = UARTE_CONFIG_PARITY_Excluded << UARTE_CONFIG_PARITY_Pos, ///< Parity excluded.
NRF_UARTE_PARITY_INCLUDED = UARTE_CONFIG_PARITY_Included << UARTE_CONFIG_PARITY_Pos, ///< Parity included.
} nrf_uarte_parity_t;
/**
* @enum nrf_uarte_hwfc_t
* @brief Types of UARTE flow control modes.
*/
typedef enum
{
NRF_UARTE_HWFC_DISABLED = UARTE_CONFIG_HWFC_Disabled << UARTE_CONFIG_HWFC_Pos, ///< HW flow control disabled.
NRF_UARTE_HWFC_ENABLED = UARTE_CONFIG_HWFC_Enabled << UARTE_CONFIG_HWFC_Pos, ///< HW flow control enabled.
} nrf_uarte_hwfc_t;
/**
* @brief Function for clearing a specific UARTE event.
*
* @param[in] p_reg UARTE instance.
* @param[in] event Event to clear.
*/
__STATIC_INLINE void nrf_uarte_event_clear(NRF_UARTE_Type * p_reg, nrf_uarte_event_t event);
/**
* @brief Function for checking the state of a specific UARTE event.
*
* @param[in] p_reg UARTE instance.
* @param[in] event Event to check.
*
* @retval True if event is set, False otherwise.
*/
__STATIC_INLINE bool nrf_uarte_event_check(NRF_UARTE_Type * p_reg, nrf_uarte_event_t event);
/**
* @brief Function for returning the address of a specific UARTE event register.
*
* @param[in] p_reg UARTE instance.
* @param[in] event Desired event.
*
* @retval Address of specified event register.
*/
__STATIC_INLINE uint32_t nrf_uarte_event_address_get(NRF_UARTE_Type * p_reg,
nrf_uarte_event_t event);
/**
* @brief Function for enabling UARTE shortcuts.
*
* @param p_reg UARTE instance.
* @param shorts_mask Shortcuts to enable.
*/
__STATIC_INLINE void nrf_uarte_shorts_enable(NRF_UARTE_Type * p_reg, uint32_t shorts_mask);
/**
* @brief Function for disabling UARTE shortcuts.
*
* @param p_reg UARTE instance.
* @param shorts_mask Shortcuts to disable.
*/
__STATIC_INLINE void nrf_uarte_shorts_disable(NRF_UARTE_Type * p_reg, uint32_t shorts_mask);
/**
* @brief Function for enabling UARTE interrupts.
*
* @param p_reg Instance.
* @param int_mask Interrupts to enable.
*/
__STATIC_INLINE void nrf_uarte_int_enable(NRF_UARTE_Type * p_reg, uint32_t int_mask);
/**
* @brief Function for retrieving the state of a given interrupt.
*
* @param p_reg Instance.
* @param int_mask Mask of interrupt to check.
*
* @retval true If the interrupt is enabled.
* @retval false If the interrupt is not enabled.
*/
__STATIC_INLINE bool nrf_uarte_int_enable_check(NRF_UARTE_Type * p_reg, nrf_uarte_int_mask_t int_mask);
/**
* @brief Function for disabling specific interrupts.
*
* @param p_reg Instance.
* @param int_mask Interrupts to disable.
*/
__STATIC_INLINE void nrf_uarte_int_disable(NRF_UARTE_Type * p_reg, uint32_t int_mask);
/**
* @brief Function for getting error source mask. Function is clearing error source flags after reading.
*
* @param p_reg Instance.
* @return Mask with error source flags.
*/
__STATIC_INLINE uint32_t nrf_uarte_errorsrc_get_and_clear(NRF_UARTE_Type * p_reg);
/**
* @brief Function for enabling UARTE.
*
* @param p_reg Instance.
*/
__STATIC_INLINE void nrf_uarte_enable(NRF_UARTE_Type * p_reg);
/**
* @brief Function for disabling UARTE.
*
* @param p_reg Instance.
*/
__STATIC_INLINE void nrf_uarte_disable(NRF_UARTE_Type * p_reg);
/**
* @brief Function for configuring TX/RX pins.
*
* @param p_reg Instance.
* @param pseltxd TXD pin number.
* @param pselrxd RXD pin number.
*/
__STATIC_INLINE void nrf_uarte_txrx_pins_set(NRF_UARTE_Type * p_reg, uint32_t pseltxd, uint32_t pselrxd);
/**
* @brief Function for disconnecting TX/RX pins.
*
* @param p_reg Instance.
*/
__STATIC_INLINE void nrf_uarte_txrx_pins_disconnect(NRF_UARTE_Type * p_reg);
/**
* @brief Function for getting TX pin.
*
* @param p_reg Instance.
*/
__STATIC_INLINE uint32_t nrf_uarte_tx_pin_get(NRF_UARTE_Type * p_reg);
/**
* @brief Function for getting RX pin.
*
* @param p_reg Instance.
*/
__STATIC_INLINE uint32_t nrf_uarte_rx_pin_get(NRF_UARTE_Type * p_reg);
/**
* @brief Function for getting RTS pin.
*
* @param p_reg Instance.
*/
__STATIC_INLINE uint32_t nrf_uarte_rts_pin_get(NRF_UARTE_Type * p_reg);
/**
* @brief Function for getting CTS pin.
*
* @param p_reg Instance.
*/
__STATIC_INLINE uint32_t nrf_uarte_cts_pin_get(NRF_UARTE_Type * p_reg);
/**
* @brief Function for configuring flow control pins.
*
* @param p_reg Instance.
* @param pselrts RTS pin number.
* @param pselcts CTS pin number.
*/
__STATIC_INLINE void nrf_uarte_hwfc_pins_set(NRF_UARTE_Type * p_reg,
uint32_t pselrts,
uint32_t pselcts);
/**
* @brief Function for disconnecting flow control pins.
*
* @param p_reg Instance.
*/
__STATIC_INLINE void nrf_uarte_hwfc_pins_disconnect(NRF_UARTE_Type * p_reg);
/**
* @brief Function for starting an UARTE task.
*
* @param p_reg Instance.
* @param task Task.
*/
__STATIC_INLINE void nrf_uarte_task_trigger(NRF_UARTE_Type * p_reg, nrf_uarte_task_t task);
/**
* @brief Function for returning the address of a specific task register.
*
* @param p_reg Instance.
* @param task Task.
*
* @return Task address.
*/
__STATIC_INLINE uint32_t nrf_uarte_task_address_get(NRF_UARTE_Type * p_reg, nrf_uarte_task_t task);
/**
* @brief Function for configuring UARTE.
*
* @param p_reg Instance.
* @param hwfc Hardware flow control. Enabled if true.
* @param parity Parity. Included if true.
*/
__STATIC_INLINE void nrf_uarte_configure(NRF_UARTE_Type * p_reg,
nrf_uarte_parity_t parity,
nrf_uarte_hwfc_t hwfc);
/**
* @brief Function for setting UARTE baudrate.
*
* @param p_reg Instance.
* @param baudrate Baudrate.
*/
__STATIC_INLINE void nrf_uarte_baudrate_set(NRF_UARTE_Type * p_reg, nrf_uarte_baudrate_t baudrate);
/**
* @brief Function for setting the transmit buffer.
*
* @param[in] p_reg Instance.
* @param[in] p_buffer Pointer to the buffer with data to send.
* @param[in] length Maximum number of data bytes to transmit.
*/
__STATIC_INLINE void nrf_uarte_tx_buffer_set(NRF_UARTE_Type * p_reg,
uint8_t const * p_buffer,
uint8_t length);
/**
* @brief Function for getting number of bytes transmitted in the last transaction.
*
* @param[in] p_reg Instance.
*
* @retval Amount of bytes transmitted.
*/
__STATIC_INLINE uint32_t nrf_uarte_tx_amount_get(NRF_UARTE_Type * p_reg);
/**
* @brief Function for setting the receive buffer.
*
* @param[in] p_reg Instance.
* @param[in] p_buffer Pointer to the buffer for received data.
* @param[in] length Maximum number of data bytes to receive.
*/
__STATIC_INLINE void nrf_uarte_rx_buffer_set(NRF_UARTE_Type * p_reg,
uint8_t * p_buffer,
uint8_t length);
/**
* @brief Function for getting number of bytes received in the last transaction.
*
* @param[in] p_reg Instance.
*
* @retval Amount of bytes received.
*/
__STATIC_INLINE uint32_t nrf_uarte_rx_amount_get(NRF_UARTE_Type * p_reg);
#ifndef SUPPRESS_INLINE_IMPLEMENTATION
__STATIC_INLINE void nrf_uarte_event_clear(NRF_UARTE_Type * p_reg, nrf_uarte_event_t event)
{
*((volatile uint32_t *)((uint8_t *)p_reg + (uint32_t)event)) = 0x0UL;
}
__STATIC_INLINE bool nrf_uarte_event_check(NRF_UARTE_Type * p_reg, nrf_uarte_event_t event)
{
return (bool)*(volatile uint32_t *)((uint8_t *)p_reg + (uint32_t)event);
}
__STATIC_INLINE uint32_t nrf_uarte_event_address_get(NRF_UARTE_Type * p_reg,
nrf_uarte_event_t event)
{
return (uint32_t)((uint8_t *)p_reg + (uint32_t)event);
}
__STATIC_INLINE void nrf_uarte_shorts_enable(NRF_UARTE_Type * p_reg, uint32_t shorts_mask)
{
p_reg->SHORTS |= shorts_mask;
}
__STATIC_INLINE void nrf_uarte_shorts_disable(NRF_UARTE_Type * p_reg, uint32_t shorts_mask)
{
p_reg->SHORTS &= ~(shorts_mask);
}
__STATIC_INLINE void nrf_uarte_int_enable(NRF_UARTE_Type * p_reg, uint32_t int_mask)
{
p_reg->INTENSET = int_mask;
}
__STATIC_INLINE bool nrf_uarte_int_enable_check(NRF_UARTE_Type * p_reg, nrf_uarte_int_mask_t int_mask)
{
return (bool)(p_reg->INTENSET & int_mask);
}
__STATIC_INLINE void nrf_uarte_int_disable(NRF_UARTE_Type * p_reg, uint32_t int_mask)
{
p_reg->INTENCLR = int_mask;
}
__STATIC_INLINE uint32_t nrf_uarte_errorsrc_get_and_clear(NRF_UARTE_Type * p_reg)
{
uint32_t errsrc_mask = p_reg->ERRORSRC;
p_reg->ERRORSRC = errsrc_mask;
return errsrc_mask;
}
__STATIC_INLINE void nrf_uarte_enable(NRF_UARTE_Type * p_reg)
{
p_reg->ENABLE = UARTE_ENABLE_ENABLE_Enabled;
}
__STATIC_INLINE void nrf_uarte_disable(NRF_UARTE_Type * p_reg)
{
p_reg->ENABLE = UARTE_ENABLE_ENABLE_Disabled;
}
__STATIC_INLINE void nrf_uarte_txrx_pins_set(NRF_UARTE_Type * p_reg, uint32_t pseltxd, uint32_t pselrxd)
{
p_reg->PSEL.TXD = pseltxd;
p_reg->PSEL.RXD = pselrxd;
}
__STATIC_INLINE void nrf_uarte_txrx_pins_disconnect(NRF_UARTE_Type * p_reg)
{
nrf_uarte_txrx_pins_set(p_reg, NRF_UARTE_PSEL_DISCONNECTED, NRF_UARTE_PSEL_DISCONNECTED);
}
__STATIC_INLINE uint32_t nrf_uarte_tx_pin_get(NRF_UARTE_Type * p_reg)
{
return p_reg->PSEL.TXD;
}
__STATIC_INLINE uint32_t nrf_uarte_rx_pin_get(NRF_UARTE_Type * p_reg)
{
return p_reg->PSEL.RXD;
}
__STATIC_INLINE uint32_t nrf_uarte_rts_pin_get(NRF_UARTE_Type * p_reg)
{
return p_reg->PSEL.RTS;
}
__STATIC_INLINE uint32_t nrf_uarte_cts_pin_get(NRF_UARTE_Type * p_reg)
{
return p_reg->PSEL.CTS;
}
__STATIC_INLINE void nrf_uarte_hwfc_pins_set(NRF_UARTE_Type * p_reg, uint32_t pselrts, uint32_t pselcts)
{
p_reg->PSEL.RTS = pselrts;
p_reg->PSEL.CTS = pselcts;
}
__STATIC_INLINE void nrf_uarte_hwfc_pins_disconnect(NRF_UARTE_Type * p_reg)
{
nrf_uarte_hwfc_pins_set(p_reg, NRF_UARTE_PSEL_DISCONNECTED, NRF_UARTE_PSEL_DISCONNECTED);
}
__STATIC_INLINE void nrf_uarte_task_trigger(NRF_UARTE_Type * p_reg, nrf_uarte_task_t task)
{
*((volatile uint32_t *)((uint8_t *)p_reg + (uint32_t)task)) = 0x1UL;
}
__STATIC_INLINE uint32_t nrf_uarte_task_address_get(NRF_UARTE_Type * p_reg, nrf_uarte_task_t task)
{
return (uint32_t)p_reg + (uint32_t)task;
}
__STATIC_INLINE void nrf_uarte_configure(NRF_UARTE_Type * p_reg,
nrf_uarte_parity_t parity,
nrf_uarte_hwfc_t hwfc)
{
p_reg->CONFIG = (uint32_t)parity | (uint32_t)hwfc;
}
__STATIC_INLINE void nrf_uarte_baudrate_set(NRF_UARTE_Type * p_reg, nrf_uarte_baudrate_t baudrate)
{
p_reg->BAUDRATE = baudrate;
}
__STATIC_INLINE void nrf_uarte_tx_buffer_set(NRF_UARTE_Type * p_reg,
uint8_t const * p_buffer,
uint8_t length)
{
p_reg->TXD.PTR = (uint32_t)p_buffer;
p_reg->TXD.MAXCNT = length;
}
__STATIC_INLINE uint32_t nrf_uarte_tx_amount_get(NRF_UARTE_Type * p_reg)
{
return p_reg->TXD.AMOUNT;
}
__STATIC_INLINE void nrf_uarte_rx_buffer_set(NRF_UARTE_Type * p_reg,
uint8_t * p_buffer,
uint8_t length)
{
p_reg->RXD.PTR = (uint32_t)p_buffer;
p_reg->RXD.MAXCNT = length;
}
__STATIC_INLINE uint32_t nrf_uarte_rx_amount_get(NRF_UARTE_Type * p_reg)
{
return p_reg->RXD.AMOUNT;
}
#endif //SUPPRESS_INLINE_IMPLEMENTATION
/** @} */
#endif //NRF_UARTE_H__

View File

@ -0,0 +1,427 @@
/* Copyright (c) 2015 Nordic Semiconductor. All Rights Reserved.
*
* The information contained herein is property of Nordic Semiconductor ASA.
* Terms and conditions of usage are described in detail in NORDIC
* SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT.
*
* Licensees are granted free, non-transferable use of the information. NO
* WARRANTY of ANY KIND is provided. This heading must NOT be removed from
* the file.
*
*/
#include <stdlib.h>
#include "nrf.h"
#include "nrf_drv_ppi.h"
#include "nrf_drv_common.h"
#include "nrf_ppi.h"
#include "app_util_platform.h"
#include "sdk_common.h"
static nrf_drv_state_t m_drv_state; /**< Driver state */
static uint32_t m_channels_allocated; /**< Bitmap representing channels availability. 1 when a channel is allocated, 0 otherwise. */
static uint8_t m_groups_allocated; /**< Bitmap representing groups availability. 1 when a group is allocated, 0 otherwise.*/
/**@brief Compute a group mask (needed for driver internals, not used for NRF_PPI registers).
* @param[in] group Group number to transform to a mask.
* @retval Group mask.
*/
__STATIC_INLINE uint32_t group_to_mask(nrf_ppi_channel_group_t group)
{
return (1uL << (uint32_t) group);
}
/**@brief Check whether a channel is a programmable channel and can be used by an application.
* @param[in] channel Channel to check.
* @retval true The channel is a programmable application channel.
* false The channel is used by a SoftDevice or is preprogrammed.
*/
__STATIC_INLINE bool is_programmable_app_channel(nrf_ppi_channel_t channel)
{
return ((NRF_PPI_PROG_APP_CHANNELS_MASK & nrf_drv_ppi_channel_to_mask(channel)) != 0);
}
/**@brief Check whether a channels can be used by an application.
* @param[in] channel Channel mask to check.
* @retval true All specified channels can be used by an application.
* false At least one specified channel is used by a SoftDevice.
*/
__STATIC_INLINE bool are_app_channels(uint32_t channel_mask)
{
//lint -e(587)
return ((~(NRF_PPI_ALL_APP_CHANNELS_MASK) & channel_mask) == 0);
}
/**@brief Check whether a channel can be used by an application.
* @param[in] channel Channel to check.
* @retval true The channel can be used by an application.
* false The channel is used by a SoftDevice.
*/
__STATIC_INLINE bool is_app_channel(nrf_ppi_channel_t channel)
{
return are_app_channels(nrf_drv_ppi_channel_to_mask(channel));
}
/**@brief Check whether a channel group can be used by an application.
* @param[in] group Group to check.
* @retval true The group is an application group.
* false The group is not an application group (this group either does not exist or
* it is used by a SoftDevice).
*/
__STATIC_INLINE bool is_app_group(nrf_ppi_channel_group_t group)
{
return ((NRF_PPI_ALL_APP_GROUPS_MASK & group_to_mask(group)) != 0);
}
/**@brief Check whether a channel is allocated.
* @param[in] channel_num Channel number to check.
* @retval true The channel is allocated.
* false The channel is not allocated.
*/
__STATIC_INLINE bool is_allocated_channel(nrf_ppi_channel_t channel)
{
return ((m_channels_allocated & nrf_drv_ppi_channel_to_mask(channel)) != 0);
}
/**@brief Set channel allocated indication.
* @param[in] channel_num Specifies the channel to set the "allocated" indication.
*/
__STATIC_INLINE void channel_allocated_set(nrf_ppi_channel_t channel)
{
m_channels_allocated |= nrf_drv_ppi_channel_to_mask(channel);
}
/**@brief Clear channel allocated indication.
* @param[in] channel_num Specifies the channel to clear the "allocated" indication.
*/
__STATIC_INLINE void channel_allocated_clr(nrf_ppi_channel_t channel)
{
m_channels_allocated &= ~nrf_drv_ppi_channel_to_mask(channel);
}
/**@brief Clear all allocated channels.
*/
__STATIC_INLINE void channel_allocated_clr_all(void)
{
m_channels_allocated &= ~NRF_PPI_ALL_APP_CHANNELS_MASK;
}
/**@brief Check whether a group is allocated.
* @param[in] group_num Group number to check.
* @retval true The group is allocated.
* false The group is not allocated.
*/
__STATIC_INLINE bool is_allocated_group(nrf_ppi_channel_group_t group)
{
return ((m_groups_allocated & group_to_mask(group)) != 0);
}
/**@brief Set group allocated indication.
* @param[in] group_num Specifies the group to set the "allocated" indication.
*/
__STATIC_INLINE void group_allocated_set(nrf_ppi_channel_group_t group)
{
m_groups_allocated |= group_to_mask(group);
}
/**@brief Clear group allocated indication.
* @param[in] group_num Specifies the group to clear the "allocated" indication.
*/
__STATIC_INLINE void group_allocated_clr(nrf_ppi_channel_group_t group)
{
m_groups_allocated &= ~group_to_mask(group);
}
/**@brief Clear all allocated groups.
*/
__STATIC_INLINE void group_allocated_clr_all()
{
m_groups_allocated &= ~NRF_PPI_ALL_APP_GROUPS_MASK;
}
uint32_t nrf_drv_ppi_init(void)
{
uint32_t err_code;
if (m_drv_state == NRF_DRV_STATE_UNINITIALIZED)
{
m_drv_state = NRF_DRV_STATE_INITIALIZED;
err_code = NRF_SUCCESS;
}
else
{
err_code = MODULE_ALREADY_INITIALIZED;
}
return err_code;
}
uint32_t nrf_drv_ppi_uninit(void)
{
uint32_t mask = NRF_PPI_ALL_APP_GROUPS_MASK;
nrf_ppi_channel_group_t group;
if (m_drv_state == NRF_DRV_STATE_UNINITIALIZED)
{
return NRF_ERROR_INVALID_STATE;
}
m_drv_state = NRF_DRV_STATE_UNINITIALIZED;
// Disable all channels and groups
nrf_ppi_channels_disable(NRF_PPI_ALL_APP_CHANNELS_MASK);
for (group = NRF_PPI_CHANNEL_GROUP0; mask != 0; mask &= ~group_to_mask(group), group++)
{
if(mask & group_to_mask(group))
{
nrf_ppi_channel_group_clear(group);
}
}
channel_allocated_clr_all();
group_allocated_clr_all();
return NRF_SUCCESS;
}
uint32_t nrf_drv_ppi_channel_alloc(nrf_ppi_channel_t * p_channel)
{
uint32_t err_code;
nrf_ppi_channel_t channel;
uint32_t mask = 0;
err_code = NRF_ERROR_NO_MEM;
mask = NRF_PPI_PROG_APP_CHANNELS_MASK;
for (channel = NRF_PPI_CHANNEL0; mask != 0; mask &= ~nrf_drv_ppi_channel_to_mask(channel), channel++)
{
CRITICAL_REGION_ENTER();
if ((mask & nrf_drv_ppi_channel_to_mask(channel)) && (!is_allocated_channel(channel)))
{
channel_allocated_set(channel);
*p_channel = channel;
err_code = NRF_SUCCESS;
}
CRITICAL_REGION_EXIT();
if (err_code == NRF_SUCCESS)
{
break;
}
}
return err_code;
}
uint32_t nrf_drv_ppi_channel_free(nrf_ppi_channel_t channel)
{
if (!is_programmable_app_channel(channel))
{
return NRF_ERROR_INVALID_PARAM;
}
// First disable this channel
nrf_ppi_channel_disable(channel);
CRITICAL_REGION_ENTER();
channel_allocated_clr(channel);
CRITICAL_REGION_EXIT();
return NRF_SUCCESS;
}
uint32_t nrf_drv_ppi_channel_assign(nrf_ppi_channel_t channel, uint32_t eep, uint32_t tep)
{
VERIFY_PARAM_NOT_NULL((uint32_t *)eep);
VERIFY_PARAM_NOT_NULL((uint32_t *)tep);
if (!is_programmable_app_channel(channel))
{
return NRF_ERROR_INVALID_PARAM;
}
if (!is_allocated_channel(channel))
{
return NRF_ERROR_INVALID_STATE;
}
nrf_ppi_channel_endpoint_setup(channel, eep, tep);
return NRF_SUCCESS;
}
uint32_t nrf_drv_ppi_channel_fork_assign(nrf_ppi_channel_t channel, uint32_t fork_tep)
{
#ifdef NRF51
return NRF_ERROR_NOT_SUPPORTED;
#else
if (!is_programmable_app_channel(channel))
{
return NRF_ERROR_INVALID_PARAM;
}
if (!is_allocated_channel(channel))
{
return NRF_ERROR_INVALID_STATE;
}
nrf_ppi_fork_endpoint_setup(channel, fork_tep);
return NRF_SUCCESS;
#endif
}
uint32_t nrf_drv_ppi_channel_enable(nrf_ppi_channel_t channel)
{
if (!is_app_channel(channel))
{
return NRF_ERROR_INVALID_PARAM;
}
if (is_programmable_app_channel(channel) && !is_allocated_channel(channel))
{
return NRF_ERROR_INVALID_STATE;
}
nrf_ppi_channel_enable(channel);
return NRF_SUCCESS;
}
uint32_t nrf_drv_ppi_channel_disable(nrf_ppi_channel_t channel)
{
if (!is_app_channel(channel))
{
return NRF_ERROR_INVALID_PARAM;
}
if (is_programmable_app_channel(channel) && !is_allocated_channel(channel))
{
return NRF_ERROR_INVALID_STATE;
}
nrf_ppi_channel_disable(channel);
return NRF_SUCCESS;
}
uint32_t nrf_drv_ppi_group_alloc(nrf_ppi_channel_group_t * p_group)
{
uint32_t err_code;
uint32_t mask = 0;
nrf_ppi_channel_group_t group;
err_code = NRF_ERROR_NO_MEM;
mask = NRF_PPI_ALL_APP_GROUPS_MASK;
for (group = NRF_PPI_CHANNEL_GROUP0; mask != 0; mask &= ~group_to_mask(group), group++)
{
CRITICAL_REGION_ENTER();
if ((mask & group_to_mask(group)) && (!is_allocated_group(group)))
{
group_allocated_set(group);
*p_group = group;
err_code = NRF_SUCCESS;
}
CRITICAL_REGION_EXIT();
if (err_code == NRF_SUCCESS)
{
break;
}
}
return err_code;
}
uint32_t nrf_drv_ppi_group_free(nrf_ppi_channel_group_t group)
{
if (!is_app_group(group))
{
return NRF_ERROR_INVALID_PARAM;
}
if (!is_allocated_group(group))
{
return NRF_ERROR_INVALID_STATE;
}
else
nrf_ppi_group_disable(group);
CRITICAL_REGION_ENTER();
group_allocated_clr(group);
CRITICAL_REGION_EXIT();
return NRF_SUCCESS;
}
uint32_t nrf_drv_ppi_group_enable(nrf_ppi_channel_group_t group)
{
if (!is_app_group(group))
{
return NRF_ERROR_INVALID_PARAM;
}
if (!is_allocated_group(group))
{
return NRF_ERROR_INVALID_STATE;
}
nrf_ppi_group_enable(group);
return NRF_SUCCESS;
}
uint32_t nrf_drv_ppi_group_disable(nrf_ppi_channel_group_t group)
{
if (!is_app_group(group))
{
return NRF_ERROR_INVALID_PARAM;
}
nrf_ppi_group_disable(group);
return NRF_SUCCESS;
}
uint32_t nrf_drv_ppi_channels_remove_from_group(uint32_t channel_mask,
nrf_ppi_channel_group_t group)
{
if (!is_app_group(group))
{
return NRF_ERROR_INVALID_PARAM;
}
if (!is_allocated_group(group))
{
return NRF_ERROR_INVALID_STATE;
}
if (!are_app_channels(channel_mask))
{
return NRF_ERROR_INVALID_PARAM;
}
CRITICAL_REGION_ENTER();
nrf_ppi_channels_remove_from_group(channel_mask, group);
CRITICAL_REGION_EXIT();
return NRF_SUCCESS;
}
uint32_t nrf_drv_ppi_channels_include_in_group(uint32_t channel_mask,
nrf_ppi_channel_group_t group)
{
if (!is_app_group(group))
{
return NRF_ERROR_INVALID_PARAM;
}
if (!is_allocated_group(group))
{
return NRF_ERROR_INVALID_STATE;
}
if (!are_app_channels(channel_mask))
{
return NRF_ERROR_INVALID_PARAM;
}
CRITICAL_REGION_ENTER();
nrf_ppi_channels_include_in_group(channel_mask, group);
CRITICAL_REGION_EXIT();
return NRF_SUCCESS;
}

View File

@ -0,0 +1,284 @@
/* Copyright (c) 2015 Nordic Semiconductor. All Rights Reserved.
*
* The information contained herein is property of Nordic Semiconductor ASA.
* Terms and conditions of usage are described in detail in NORDIC
* SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT.
*
* Licensees are granted free, non-transferable use of the information. NO
* WARRANTY of ANY KIND is provided. This heading must NOT be removed from
* the file.
*
*/
#ifndef NRF_DRV_PPI_H
#define NRF_DRV_PPI_H
/*lint ++flb "Enter library region" */
#include "sdk_errors.h"
#include "nrf_ppi.h"
#include <stdbool.h>
#include <stdint.h>
/** @file
*
* @addtogroup nrf_ppi PPI HAL and driver
* @ingroup nrf_drivers
* @brief Programmable Peripheral Interconnect (PPI) APIs.
*
* @details The PPI HAL provides basic APIs for accessing the registers of the PPI.
* The PPI driver provides APIs on a higher level.
*
* @defgroup lib_driver_ppi PPI driver
* @{
* @ingroup nrf_ppi
*
* @brief Programmable Peripheral Interconnect (PPI) driver.
*/
#include "sdk_resources.h"
#ifdef NRF52
#define NRF_PPI_ALL_APP_CHANNELS_MASK ((uint32_t)0xFFFFFFFFuL & ~(NRF_PPI_CHANNELS_USED)) /**< All PPI channels available to the application. */
#define NRF_PPI_PROG_APP_CHANNELS_MASK ((uint32_t)0x000FFFFFuL & ~(NRF_PPI_CHANNELS_USED)) /**< Programmable PPI channels available to the application. */
#define NRF_PPI_ALL_APP_GROUPS_MASK ((uint32_t)0x0000003FuL & ~(NRF_PPI_GROUPS_USED)) /**< All PPI groups available to the application. */
#else
#define NRF_PPI_ALL_APP_CHANNELS_MASK ((uint32_t)0xFFF0FFFFuL & ~(NRF_PPI_CHANNELS_USED)) /**< All PPI channels available to the application. */
#define NRF_PPI_PROG_APP_CHANNELS_MASK ((uint32_t)0x0000FFFFuL & ~(NRF_PPI_CHANNELS_USED)) /**< Programmable PPI channels available to the application. */
#define NRF_PPI_ALL_APP_GROUPS_MASK ((uint32_t)0x0000000FuL & ~(NRF_PPI_GROUPS_USED)) /**< All PPI groups available to the application. */
#endif
/**@brief Function for initializing PPI module.
*
* @retval NRF_SUCCESS If the module was successfully initialized.
* @retval MODULE_ALREADY_INITIALIZED If the module has already been initialized.
*/
uint32_t nrf_drv_ppi_init(void);
/**@brief Function for uninitializing the PPI module.
*
* This function also disables all channels and clears the channel groups.
*
* @retval NRF_SUCCESS If the module was successfully uninitialized.
* @retval NRF_ERROR_INVALID_STATE If the module has not been initialized yet.
* @retval NRF_ERROR_INTERNAL If the channels or groups could not be disabled.
*/
uint32_t nrf_drv_ppi_uninit(void);
/**@brief Function for allocating a PPI channel.
* @details This function allocates the first unused PPI channel.
*
* @param[out] p_channel Pointer to the PPI channel that has been allocated.
*
* @retval NRF_SUCCESS If the channel was successfully allocated.
* @retval NRF_ERROR_NO_MEM If there is no available channel to be used.
*/
uint32_t nrf_drv_ppi_channel_alloc(nrf_ppi_channel_t * p_channel);
/**@brief Function for freeing a PPI channel.
* @details This function also disables the chosen channel.
*
* @param[in] channel PPI channel to be freed.
*
* @retval NRF_SUCCESS If the channel was successfully freed.
* @retval NRF_ERROR_INVALID_PARAM If the channel is not user-configurable.
*/
uint32_t nrf_drv_ppi_channel_free(nrf_ppi_channel_t channel);
/**@brief Function for assigning task and event endpoints to the PPI channel.
*
* @param[in] channel PPI channel to be assigned endpoints.
*
* @param[in] eep Event endpoint address.
*
* @param[in] tep Task endpoint address.
*
* @retval NRF_SUCCESS If the channel was successfully assigned.
* @retval NRF_ERROR_INVALID_STATE If the channel is not allocated for the user.
* @retval NRF_ERROR_INVALID_PARAM If the channel is not user-configurable.
*/
uint32_t nrf_drv_ppi_channel_assign(nrf_ppi_channel_t channel, uint32_t eep, uint32_t tep);
/**@brief Function for assigning or clearing fork endpoint to the PPI channel.
*
* @param[in] channel PPI channel to be assigned endpoints.
*
* @param[in] fork_tep Fork task endpoint address or 0 to clear.
*
* @retval NRF_SUCCESS If the channel was successfully assigned.
* @retval NRF_ERROR_INVALID_STATE If the channel is not allocated for the user.
* @retval NRF_ERROR_INVALID_PARAM If the channel is not user-configurable.
* @retval NRF_ERROR_NOT_SUPPORTED If function is not supported.
*/
uint32_t nrf_drv_ppi_channel_fork_assign(nrf_ppi_channel_t channel, uint32_t fork_tep);
/**@brief Function for enabling a PPI channel.
*
* @param[in] channel PPI channel to be enabled.
*
* @retval NRF_SUCCESS If the channel was successfully enabled.
* @retval NRF_ERROR_INVALID_STATE If the user-configurable channel is not allocated.
* @retval NRF_ERROR_INVALID_PARAM If the channel cannot be enabled by the user.
*/
uint32_t nrf_drv_ppi_channel_enable(nrf_ppi_channel_t channel);
/**@brief Function for disabling a PPI channel.
*
* @param[in] channel PPI channel to be disabled.
*
* @retval NRF_SUCCESS If the channel was successfully disabled.
* @retval NRF_ERROR_INVALID_STATE If the user-configurable channel is not allocated.
* @retval NRF_ERROR_INVALID_PARAM If the channel cannot be disabled by the user.
*/
uint32_t nrf_drv_ppi_channel_disable(nrf_ppi_channel_t channel);
/**@brief Function for allocating a PPI channel group.
* @details This function allocates the first unused PPI group.
*
* @param[out] p_group Pointer to the PPI channel group that has been allocated.
*
* @retval NRF_SUCCESS If the channel group was successfully allocated.
* @retval NRF_ERROR_NO_MEM If there is no available channel group to be used.
*/
uint32_t nrf_drv_ppi_group_alloc(nrf_ppi_channel_group_t * p_group);
/**@brief Function for freeing a PPI channel group.
* @details This function also disables the chosen group.
*
* @param[in] group PPI channel group to be freed.
*
* @retval NRF_SUCCESS If the channel group was successfully freed.
* @retval NRF_ERROR_INVALID_PARAM If the channel group is not user-configurable.
*/
uint32_t nrf_drv_ppi_group_free(nrf_ppi_channel_group_t group);
/**@brief Compute a channel mask for NRF_PPI registers.
*
* @param[in] channel Channel number to transform to a mask.
*
* @retval Channel mask.
*/
__STATIC_INLINE uint32_t nrf_drv_ppi_channel_to_mask(nrf_ppi_channel_t channel)
{
return (1uL << (uint32_t) channel);
}
/**@brief Function for including multiple PPI channels in a channel group.
*
* @param[in] channel_mask PPI channels to be added.
* @param[in] group Channel group in which to include the channels.
*
* @retval NRF_SUCCESS If the channels was successfully included.
*/
uint32_t nrf_drv_ppi_channels_include_in_group(uint32_t channel_mask,
nrf_ppi_channel_group_t group);
/**@brief Function for including a PPI channel in a channel group.
*
* @param[in] channel PPI channel to be added.
* @param[in] group Channel group in which to include the channel.
*
* @retval NRF_SUCCESS If the channel was successfully included.
*/
__STATIC_INLINE uint32_t nrf_drv_ppi_channel_include_in_group(nrf_ppi_channel_t channel,
nrf_ppi_channel_group_t group)
{
return nrf_drv_ppi_channels_include_in_group(nrf_drv_ppi_channel_to_mask(channel), group);
}
/**@brief Function for removing multiple PPI channels from a channel group.
*
* @param[in] channel_mask PPI channels to be removed.
* @param[in] group Channel group from which to remove the channels.
*
* @retval NRF_SUCCESS If the channel was successfully removed.
*/
uint32_t nrf_drv_ppi_channels_remove_from_group(uint32_t channel_mask,
nrf_ppi_channel_group_t group);
/**@brief Function for removing a PPI channel from a channel group.
*
* @param[in] channel PPI channel to be removed.
* @param[in] group Channel group from which to remove the channel.
*
* @retval NRF_SUCCESS If the channel was successfully removed.
*/
__STATIC_INLINE uint32_t nrf_drv_ppi_channel_remove_from_group(nrf_ppi_channel_t channel,
nrf_ppi_channel_group_t group)
{
return nrf_drv_ppi_channels_remove_from_group(nrf_drv_ppi_channel_to_mask(channel), group);
}
/**@brief Function for clearing a PPI channel group.
*
* @param[in] group Channel group to be cleared.
*
* @retval NRF_SUCCESS If the group was successfully cleared.
*/
__STATIC_INLINE uint32_t nrf_drv_ppi_group_clear(nrf_ppi_channel_group_t group)
{
return nrf_drv_ppi_channels_remove_from_group(NRF_PPI_ALL_APP_CHANNELS_MASK, group);
}
/**@brief Function for enabling a PPI channel group.
*
* @param[in] group Channel group to be enabled.
*
* @retval NRF_SUCCESS If the group was successfully enabled.
*/
uint32_t nrf_drv_ppi_group_enable(nrf_ppi_channel_group_t group);
/**@brief Function for disabling a PPI channel group.
*
* @param[in] group Channel group to be disabled.
*
* @retval NRF_SUCCESS If the group was successfully disabled.
*/
uint32_t nrf_drv_ppi_group_disable(nrf_ppi_channel_group_t group);
/**
* @brief Function for getting the address of a PPI task.
*
* @param[in] task Task.
*
* @retval Task address.
*/
__STATIC_INLINE uint32_t nrf_drv_ppi_task_addr_get(nrf_ppi_task_t task)
{
return (uint32_t) nrf_ppi_task_address_get(task);
}
/**
* @brief Function for getting the address of a PPI group enable task.
*
* @param[in] group PPI channel group
*
* @retval Task address.
*/
__STATIC_INLINE uint32_t nrf_drv_ppi_task_addr_group_enable_get(nrf_ppi_channel_group_t group)
{
return (uint32_t) nrf_ppi_task_group_enable_address_get(group);
}
/**
* @brief Function for getting the address of a PPI group enable task.
*
* @param[in] group PPI channel group
*
* @retval Task address.
*/
__STATIC_INLINE uint32_t nrf_drv_ppi_task_addr_group_disable_get(nrf_ppi_channel_group_t group)
{
return (uint32_t) nrf_ppi_task_group_disable_address_get(group);
}
/**
*@}
**/
/*lint --flb "Leave library region" */
#endif // NRF_DRV_PPI_H

View File

@ -0,0 +1,290 @@
/* Copyright (c) 2014 Nordic Semiconductor. All Rights Reserved.
*
* The information contained herein is property of Nordic Semiconductor ASA.
* Terms and conditions of usage are described in detail in NORDIC
* SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT.
*
* Licensees are granted free, non-transferable use of the information. NO
* WARRANTY of ANY KIND is provided. This heading must NOT be removed from
* the file.
*
*/
#include "nrf_drv_rtc.h"
#include "nrf_rtc.h"
#include "nrf_assert.h"
#include "app_util_platform.h"
/**@brief RTC driver instance control block structure. */
typedef struct
{
nrf_drv_state_t state; /**< Instance state. */
bool reliable; /**< Reliable mode flag. */
uint8_t tick_latency; /**< Maximum length of interrupt handler in ticks (max 7.7 ms). */
} nrf_drv_rtc_cb_t;
// User callbacks local storage.
static nrf_drv_rtc_handler_t m_handlers[RTC_COUNT];
static nrf_drv_rtc_cb_t m_cb[RTC_COUNT];
static const nrf_drv_rtc_config_t m_default_config[] = {
#if RTC0_ENABLED
NRF_DRV_RTC_DEFAULT_CONFIG(0),
#endif
#if RTC1_ENABLED
NRF_DRV_RTC_DEFAULT_CONFIG(1),
#endif
#if RTC2_ENABLED
NRF_DRV_RTC_DEFAULT_CONFIG(2)
#endif
};
ret_code_t nrf_drv_rtc_init(nrf_drv_rtc_t const * const p_instance,
nrf_drv_rtc_config_t const * p_config,
nrf_drv_rtc_handler_t handler)
{
if (handler)
{
m_handlers[p_instance->instance_id] = handler;
}
else
{
return NRF_ERROR_INVALID_PARAM;
}
if (p_config == NULL)
{
p_config = &m_default_config[p_instance->instance_id];
}
if (m_cb[p_instance->instance_id].state != NRF_DRV_STATE_UNINITIALIZED)
{
return NRF_ERROR_INVALID_STATE;
}
nrf_drv_common_irq_enable(p_instance->irq, p_config->interrupt_priority);
nrf_rtc_prescaler_set(p_instance->p_reg, p_config->prescaler);
m_cb[p_instance->instance_id].reliable = p_config->reliable;
m_cb[p_instance->instance_id].tick_latency = p_config->tick_latency;
m_cb[p_instance->instance_id].state = NRF_DRV_STATE_INITIALIZED;
return NRF_SUCCESS;
}
void nrf_drv_rtc_uninit(nrf_drv_rtc_t const * const p_instance)
{
uint32_t mask = NRF_RTC_INT_TICK_MASK |
NRF_RTC_INT_OVERFLOW_MASK |
NRF_RTC_INT_COMPARE0_MASK |
NRF_RTC_INT_COMPARE1_MASK |
NRF_RTC_INT_COMPARE2_MASK |
NRF_RTC_INT_COMPARE3_MASK;
ASSERT(m_cb[p_instance->instance_id].state != NRF_DRV_STATE_UNINITIALIZED);
nrf_drv_common_irq_disable(p_instance->irq);
nrf_rtc_task_trigger(p_instance->p_reg, NRF_RTC_TASK_STOP);
nrf_rtc_event_disable(p_instance->p_reg, mask);
nrf_rtc_int_disable(p_instance->p_reg, mask);
m_cb[p_instance->instance_id].state = NRF_DRV_STATE_UNINITIALIZED;
}
void nrf_drv_rtc_enable(nrf_drv_rtc_t const * const p_instance)
{
ASSERT(m_cb[p_instance->instance_id].state == NRF_DRV_STATE_INITIALIZED);
nrf_rtc_task_trigger(p_instance->p_reg, NRF_RTC_TASK_START);
m_cb[p_instance->instance_id].state = NRF_DRV_STATE_POWERED_ON;
}
void nrf_drv_rtc_disable(nrf_drv_rtc_t const * const p_instance)
{
ASSERT(m_cb[p_instance->instance_id].state == NRF_DRV_STATE_POWERED_ON);
nrf_rtc_task_trigger(p_instance->p_reg, NRF_RTC_TASK_STOP);
m_cb[p_instance->instance_id].state = NRF_DRV_STATE_INITIALIZED;
}
ret_code_t nrf_drv_rtc_cc_disable(nrf_drv_rtc_t const * const p_instance, uint32_t channel)
{
ASSERT(m_cb[p_instance->instance_id].state != NRF_DRV_STATE_UNINITIALIZED);
ASSERT(channel<p_instance->cc_channel_count);
uint32_t int_mask = RTC_CHANNEL_INT_MASK(channel);
nrf_rtc_event_t event = RTC_CHANNEL_EVENT_ADDR(channel);
nrf_rtc_event_disable(p_instance->p_reg,int_mask);
if (nrf_rtc_int_is_enabled(p_instance->p_reg,int_mask))
{
nrf_rtc_int_disable(p_instance->p_reg,int_mask);
if (nrf_rtc_event_pending(p_instance->p_reg,event))
{
nrf_rtc_event_clear(p_instance->p_reg,event);
return NRF_ERROR_TIMEOUT;
}
}
return NRF_SUCCESS;
}
ret_code_t nrf_drv_rtc_cc_set(nrf_drv_rtc_t const * const p_instance,
uint32_t channel,
uint32_t val,
bool enable_irq)
{
ASSERT(m_cb[p_instance->instance_id].state != NRF_DRV_STATE_UNINITIALIZED);
ASSERT(channel<p_instance->cc_channel_count);
uint32_t int_mask = RTC_CHANNEL_INT_MASK(channel);
nrf_rtc_event_t event = RTC_CHANNEL_EVENT_ADDR(channel);
nrf_rtc_event_disable(p_instance->p_reg, int_mask);
nrf_rtc_int_disable(p_instance->p_reg, int_mask);
val = RTC_WRAP(val);
if (m_cb[p_instance->instance_id].reliable)
{
nrf_rtc_cc_set(p_instance->p_reg,channel,val);
uint32_t cnt = nrf_rtc_counter_get(p_instance->p_reg);
int32_t diff = cnt - val;
if (cnt < val)
{
diff += RTC_COUNTER_COUNTER_Msk;
}
if (diff < m_cb[p_instance->instance_id].tick_latency)
{
return NRF_ERROR_TIMEOUT;
}
}
else
{
nrf_rtc_cc_set(p_instance->p_reg,channel,val);
}
if (enable_irq)
{
nrf_rtc_event_clear(p_instance->p_reg,event);
nrf_rtc_int_enable(p_instance->p_reg, int_mask);
}
nrf_rtc_event_enable(p_instance->p_reg,int_mask);
return NRF_SUCCESS;
}
void nrf_drv_rtc_tick_enable(nrf_drv_rtc_t const * const p_instance, bool enable_irq)
{
nrf_rtc_event_t event = NRF_RTC_EVENT_TICK;
uint32_t mask = NRF_RTC_INT_TICK_MASK;
nrf_rtc_event_clear(p_instance->p_reg, event);
nrf_rtc_event_enable(p_instance->p_reg, mask);
if (enable_irq)
{
nrf_rtc_int_enable(p_instance->p_reg, mask);
}
}
void nrf_drv_rtc_tick_disable(nrf_drv_rtc_t const * const p_instance)
{
uint32_t mask = NRF_RTC_INT_TICK_MASK;
nrf_rtc_event_disable(p_instance->p_reg, mask);
nrf_rtc_int_disable(p_instance->p_reg, mask);
}
void nrf_drv_rtc_overflow_enable(nrf_drv_rtc_t const * const p_instance, bool enable_irq)
{
nrf_rtc_event_t event = NRF_RTC_EVENT_OVERFLOW;
uint32_t mask = NRF_RTC_INT_OVERFLOW_MASK;
nrf_rtc_event_clear(p_instance->p_reg, event);
nrf_rtc_event_enable(p_instance->p_reg, mask);
if (enable_irq)
{
nrf_rtc_int_enable(p_instance->p_reg, mask);
}
}
void nrf_drv_rtc_overflow_disable(nrf_drv_rtc_t const * const p_instance)
{
uint32_t mask = NRF_RTC_INT_OVERFLOW_MASK;
nrf_rtc_event_disable(p_instance->p_reg, mask);
nrf_rtc_int_disable(p_instance->p_reg, mask);
}
uint32_t nrf_drv_rtc_max_ticks_get(nrf_drv_rtc_t const * const p_instance)
{
ASSERT(m_cb[p_instance->instance_id].reliable);
uint32_t ticks;
if (m_cb[p_instance->instance_id].reliable)
{
ticks = RTC_COUNTER_COUNTER_Msk - m_cb[p_instance->instance_id].tick_latency;
}
else
{
ticks = RTC_COUNTER_COUNTER_Msk;
}
return ticks;
}
/**@brief Generic function for handling RTC interrupt
*
* @param[in] p_reg Pointer to instance register structure.
* @param[in] instance_id Index of instance.
*/
__STATIC_INLINE void nrf_drv_rtc_int_handler(NRF_RTC_Type * p_reg,
uint32_t instance_id,
uint32_t channel_count)
{
uint32_t i;
uint32_t int_mask = (uint32_t)NRF_RTC_INT_COMPARE0_MASK;
nrf_rtc_event_t event = NRF_RTC_EVENT_COMPARE_0;
for (i = 0; i < channel_count; i++)
{
if (nrf_rtc_int_is_enabled(p_reg,int_mask) && nrf_rtc_event_pending(p_reg,event))
{
nrf_rtc_event_disable(p_reg,int_mask);
nrf_rtc_int_disable(p_reg,int_mask);
nrf_rtc_event_clear(p_reg,event);
m_handlers[instance_id]((nrf_drv_rtc_int_type_t)i);
}
int_mask <<= 1;
event = (nrf_rtc_event_t)((uint32_t)event + sizeof(uint32_t));
}
event = NRF_RTC_EVENT_TICK;
if (nrf_rtc_int_is_enabled(p_reg,NRF_RTC_INT_TICK_MASK) &&
nrf_rtc_event_pending(p_reg, event))
{
nrf_rtc_event_clear(p_reg, event);
m_handlers[instance_id](NRF_DRV_RTC_INT_TICK);
}
event = NRF_RTC_EVENT_OVERFLOW;
if (nrf_rtc_int_is_enabled(p_reg,NRF_RTC_INT_OVERFLOW_MASK) &&
nrf_rtc_event_pending(p_reg, event))
{
nrf_rtc_event_clear(p_reg,event);
m_handlers[instance_id](NRF_DRV_RTC_INT_OVERFLOW);
}
}
#if RTC0_ENABLED
void RTC0_IRQHandler(void)
{
nrf_drv_rtc_int_handler(NRF_RTC0,RTC0_INSTANCE_INDEX, NRF_RTC_CC_CHANNEL_COUNT(0));
}
#endif
#if RTC1_ENABLED
void RTC1_IRQHandler(void)
{
nrf_drv_rtc_int_handler(NRF_RTC1,RTC1_INSTANCE_INDEX, NRF_RTC_CC_CHANNEL_COUNT(1));
}
#endif
#if RTC2_ENABLED
void RTC2_IRQHandler(void)
{
nrf_drv_rtc_int_handler(NRF_RTC2,RTC2_INSTANCE_INDEX, NRF_RTC_CC_CHANNEL_COUNT(2));
}
#endif

View File

@ -0,0 +1,325 @@
/* Copyright (c) 2014 Nordic Semiconductor. All Rights Reserved.
*
* The information contained herein is property of Nordic Semiconductor ASA.
* Terms and conditions of usage are described in detail in NORDIC
* SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT.
*
* Licensees are granted free, non-transferable use of the information. NO
* WARRANTY of ANY KIND is provided. This heading must NOT be removed from
* the file.
*
*/
#ifndef NRF_DRV_RTC_H
#define NRF_DRV_RTC_H
#include "nordic_common.h"
#include "nrf_drv_config.h"
#include "nrf_drv_common.h"
#include "nrf_rtc.h"
#include "sdk_errors.h"
/**
* @addtogroup nrf_rtc RTC HAL and driver
* @ingroup nrf_drivers
* @brief Real timer counter (RTC) APIs.
* @details The RTC HAL provides basic APIs for accessing the registers of the real time counter (RTC).
* The RTC driver provides APIs on a higher level.
*
* @defgroup nrf_drv_rtc RTC driver
* @{
* @ingroup nrf_rtc
* @brief Real timer counter (RTC) driver.
*/
/**@brief Macro to convert microseconds into ticks. */
#define RTC_US_TO_TICKS(us,freq) ((us*freq)/1000000)
/**
* @enum nrf_drv_rtc_int_type_t
* @brief RTC driver interrupt types.
*/
typedef enum
{
NRF_DRV_RTC_INT_COMPARE0 = 0, /**< Interrupt from COMPARE0 event. */
NRF_DRV_RTC_INT_COMPARE1 = 1, /**< Interrupt from COMPARE1 event. */
NRF_DRV_RTC_INT_COMPARE2 = 2, /**< Interrupt from COMPARE2 event. */
NRF_DRV_RTC_INT_COMPARE3 = 3, /**< Interrupt from COMPARE3 event. */
NRF_DRV_RTC_INT_TICK = 4, /**< Interrupt from TICK event. */
NRF_DRV_RTC_INT_OVERFLOW = 5 /**< Interrupt from OVERFLOW event. */
} nrf_drv_rtc_int_type_t;
/**@brief RTC driver instance structure. */
typedef struct
{
NRF_RTC_Type * p_reg; /**< Pointer to instance register set. */
IRQn_Type irq; /**< Instance IRQ ID. */
uint8_t instance_id; /**< Instance index. */
uint8_t cc_channel_count; /**< Number of capture/compare channels. */
} nrf_drv_rtc_t;
/**@brief Macro for creating RTC driver instance.*/
#define NRF_DRV_RTC_INSTANCE(id) \
{ \
.p_reg = CONCAT_2(NRF_RTC, id), \
.irq = CONCAT_3(RTC, id, _IRQn), \
.instance_id = CONCAT_3(RTC, id, _INSTANCE_INDEX),\
.cc_channel_count = NRF_RTC_CC_CHANNEL_COUNT(id), \
}
/**@brief RTC driver instance configuration structure. */
typedef struct
{
uint16_t prescaler; /**< Prescaler. */
uint8_t interrupt_priority; /**< Interrupt priority. */
uint8_t tick_latency; /**< Maximum length of interrupt handler in ticks (max 7.7 ms). */
bool reliable; /**< Reliable mode flag. */
} nrf_drv_rtc_config_t;
/**@brief RTC instance default configuration. */
#define NRF_DRV_RTC_DEFAULT_CONFIG(id) \
{ \
.prescaler = (uint16_t)(RTC_INPUT_FREQ / CONCAT_3(RTC, id, _CONFIG_FREQUENCY))-1, \
.interrupt_priority = CONCAT_3(RTC, id, _CONFIG_IRQ_PRIORITY), \
.reliable = CONCAT_3(RTC, id, _CONFIG_RELIABLE), \
.tick_latency = RTC_US_TO_TICKS(NRF_MAXIMUM_LATENCY_US, CONCAT_3(RTC, id, _CONFIG_FREQUENCY)), \
}
/**@brief RTC driver instance handler type. */
typedef void (*nrf_drv_rtc_handler_t)(nrf_drv_rtc_int_type_t int_type);
/**@brief Function for initializing the RTC driver instance.
*
* After initialization, the instance is in power off state.
*
* @param[in] p_instance Pointer to the instance.
* @param[in] p_config Initial configuration. Default configuration used if NULL.
* @param[in] handler User's event handler.
*
* @retval NRF_SUCCESS If successfully initialized.
* @retval NRF_ERROR_INVALID_PARAM If no handler was provided.
* @retval NRF_ERROR_INVALID_STATE If the instance is already initialized.
*/
ret_code_t nrf_drv_rtc_init(nrf_drv_rtc_t const * const p_instance,
nrf_drv_rtc_config_t const * p_config,
nrf_drv_rtc_handler_t handler);
/**@brief Function for uninitializing the RTC driver instance.
*
* After uninitialization, the instance is in idle state. The hardware should return to the state
* before initialization. The function asserts if the instance is in idle state.
*
* @param[in] p_instance Pointer to the instance.
*/
void nrf_drv_rtc_uninit(nrf_drv_rtc_t const * const p_instance);
/**@brief Function for enabling the RTC driver instance.
*
* @note Function asserts if instance is enabled.
*
* @param[in] p_instance Pointer to the instance.
*/
void nrf_drv_rtc_enable(nrf_drv_rtc_t const * const p_instance);
/**@brief Function for disabling the RTC driver instance.
*
* @note Function asserts if instance is disabled.
*
* @param[in] p_instance Pointer to instance.
*/
void nrf_drv_rtc_disable(nrf_drv_rtc_t const * const p_instance);
/**@brief Function for setting a compare channel.
*
* The function asserts if the instance is not initialized or if the channel parameter is
* wrong. The function powers on the instance if the instance was in power off state.
*
* The driver is not entering a critical section when configuring RTC, which means that it can be
* preempted for a certain amount of time. When the driver was preempted and the value to be set
* is short in time, there is a risk that the driver sets a compare value that is
* behind. If RTCn_CONFIG_RELIABLE is 1 for the given instance, the Reliable mode handles that case.
* However, to detect if the requested value is behind, this mode makes the following assumptions:
* - The maximum preemption time in ticks (8-bit value) is known and is less than 7.7 ms
* (for prescaler = 0, RTC frequency 32 kHz).
* - The requested absolute compare value is not bigger than (0x00FFFFFF)-tick_latency. It is
* the user's responsibility to ensure that.
*
* @param[in] p_instance Pointer to the instance.
* @param[in] channel One of the instance's channels.
* @param[in] val Absolute value to be set in the compare register.
* @param[in] enable_irq True to enable the interrupt. False to disable the interrupt.
*
* @retval NRF_SUCCESS If the procedure was successful.
* @retval NRF_ERROR_TIMEOUT If the compare was not set because the request value is behind the current counter
* value. This error can only be reported if RTCn_CONFIG_RELIABLE = 1.
*/
ret_code_t nrf_drv_rtc_cc_set(nrf_drv_rtc_t const * const p_instance,
uint32_t channel,
uint32_t val,
bool enable_irq);
/**@brief Function for disabling a channel.
*
* This function disables channel events and channel interrupts. The function asserts if the instance is not
* initialized or if the channel parameter is wrong.
*
* @param[in] p_instance Pointer to the instance.
* @param[in] channel One of the instance's channels.
*
* @retval NRF_SUCCESS If the procedure was successful.
* @retval NRF_ERROR_TIMEOUT If an interrupt was pending on the requested channel.
*/
ret_code_t nrf_drv_rtc_cc_disable(nrf_drv_rtc_t const * const p_instance, uint32_t channel);
/**@brief Function for enabling tick.
*
* This function enables the tick event and optionally the interrupt. The function asserts if the instance is not
* powered on.
*
* @param[in] p_instance Pointer to the instance.
* @param[in] enable_irq True to enable the interrupt. False to disable the interrupt.
*/
void nrf_drv_rtc_tick_enable(nrf_drv_rtc_t const * const p_instance, bool enable_irq);
/**@brief Function for disabling tick.
*
* This function disables the tick event and interrupt.
*
* @param[in] p_instance Pointer to the instance.
*/
void nrf_drv_rtc_tick_disable(nrf_drv_rtc_t const * const p_instance);
/**@brief Function for enabling overflow.
*
* This function enables the overflow event and optionally the interrupt. The function asserts if the instance is
* not powered on.
*
* @param[in] p_instance Pointer to the instance.
* @param[in] enable_irq True to enable the interrupt. False to disable the interrupt.
*/
void nrf_drv_rtc_overflow_enable(nrf_drv_rtc_t const * const p_instance, bool enable_irq);
/**@brief Function for disabling overflow.
*
* This function disables the overflow event and interrupt.
*
* @param[in] p_instance Pointer to the instance.
*/
void nrf_drv_rtc_overflow_disable(nrf_drv_rtc_t const * const p_instance);
/**@brief Function for getting the maximum relative ticks value that can be set in the compare channel.
*
* When a SoftDevice is used, it occupies the highest level interrupt, so that the application code can be
* interrupted at any moment for a certain period of time. If Reliable mode is enabled, the provided
* maximum latency is taken into account and the return value is smaller than the RTC counter
* resolution. If Reliable mode is disabled, the return value equals the counter resolution.
*
* @param[in] p_instance Pointer to the instance.
*
* @retval ticks Maximum ticks value.
*/
uint32_t nrf_drv_rtc_max_ticks_get(nrf_drv_rtc_t const * const p_instance);
/**@brief Function for disabling all instance interrupts.
*
* @param[in] p_instance Pointer to the instance.
* @param[in] p_mask Pointer to the location where the mask is filled.
*/
__STATIC_INLINE void nrf_drv_rtc_int_disable(nrf_drv_rtc_t const * const p_instance,
uint32_t * p_mask);
/**@brief Function for enabling instance interrupts.
*
* @param[in] p_instance Pointer to the instance.
* @param[in] mask Mask of interrupts to enable.
*/
__STATIC_INLINE void nrf_drv_rtc_int_enable(nrf_drv_rtc_t const * const p_instance, uint32_t mask);
/**@brief Function for retrieving the current counter value.
*
* This function asserts if the instance is not powered on or if p_val is NULL.
*
* @param[in] p_instance Pointer to the instance.
*
* @retval value Counter value.
*/
__STATIC_INLINE uint32_t nrf_drv_rtc_counter_get(nrf_drv_rtc_t const * const p_instance);
/**@brief Function for clearing the counter value.
*
* This function asserts if the instance is not powered on.
*
* @param[in] p_instance Pointer to the instance.
*/
__STATIC_INLINE void nrf_drv_rtc_counter_clear(nrf_drv_rtc_t const * const p_instance);
/**@brief Function for returning a requested task address for the RTC driver instance.
*
* This function asserts if the output pointer is NULL. The task address can be used by the PPI module.
*
* @param[in] p_instance Pointer to the instance.
* @param[in] task One of the peripheral tasks.
*
* @retval Address of task register.
*/
__STATIC_INLINE uint32_t nrf_drv_rtc_task_address_get(nrf_drv_rtc_t const * const p_instance,
nrf_rtc_task_t task);
/**@brief Function for returning a requested event address for the RTC driver instance.
*
* This function asserts if the output pointer is NULL. The event address can be used by the PPI module.
*
* @param[in] p_instance Pointer to the instance.
* @param[in] event One of the peripheral events.
*
* @retval Address of event register.
*/
__STATIC_INLINE uint32_t nrf_drv_rtc_event_address_get(nrf_drv_rtc_t const * const p_instance,
nrf_rtc_event_t event);
#ifndef SUPPRESS_INLINE_IMPLEMENTATION
__STATIC_INLINE void nrf_drv_rtc_int_disable(nrf_drv_rtc_t const * const p_instance,
uint32_t * p_mask)
{
*p_mask = nrf_rtc_int_get(p_instance->p_reg);
nrf_rtc_int_disable(p_instance->p_reg, NRF_RTC_INT_TICK_MASK |
NRF_RTC_INT_OVERFLOW_MASK |
NRF_RTC_INT_COMPARE0_MASK |
NRF_RTC_INT_COMPARE1_MASK |
NRF_RTC_INT_COMPARE2_MASK |
NRF_RTC_INT_COMPARE3_MASK);
}
__STATIC_INLINE void nrf_drv_rtc_int_enable(nrf_drv_rtc_t const * const p_instance, uint32_t mask)
{
nrf_rtc_int_enable(p_instance->p_reg, mask);
}
__STATIC_INLINE uint32_t nrf_drv_rtc_counter_get(nrf_drv_rtc_t const * const p_instance)
{
return nrf_rtc_counter_get(p_instance->p_reg);
}
__STATIC_INLINE void nrf_drv_rtc_counter_clear(nrf_drv_rtc_t const * const p_instance)
{
nrf_rtc_task_trigger(p_instance->p_reg,NRF_RTC_TASK_CLEAR);
}
__STATIC_INLINE uint32_t nrf_drv_rtc_task_address_get(nrf_drv_rtc_t const * const p_instance,
nrf_rtc_task_t task)
{
return nrf_rtc_task_address_get(p_instance->p_reg, task);
}
__STATIC_INLINE uint32_t nrf_drv_rtc_event_address_get(nrf_drv_rtc_t const * const p_instance,
nrf_rtc_event_t event)
{
return nrf_rtc_event_address_get(p_instance->p_reg, event);
}
#endif /* SUPPRESS_INLINE_IMPLEMENTATION */
/**
*@}
**/
#endif /* NRF_DRV_RTC_H */

View File

@ -0,0 +1,655 @@
/* Copyright (c) 2015 Nordic Semiconductor. All Rights Reserved.
*
* The information contained herein is property of Nordic Semiconductor ASA.
* Terms and conditions of usage are described in detail in NORDIC
* SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT.
*
* Licensees are granted free, non-transferable use of the information. NO
* WARRANTY of ANY KIND is provided. This heading must NOT be removed from
* the file.
*
*/
#include "nrf_drv_spi.h"
#include "nrf_drv_common.h"
#include "nrf_gpio.h"
#include "nrf_assert.h"
#include "app_util_platform.h"
#ifndef NRF52
// Make sure SPIx_USE_EASY_DMA is 0 for nRF51 (if a common
// "nrf_drv_config.h" file is provided for nRF51 and nRF52).
#undef SPI0_USE_EASY_DMA
#define SPI0_USE_EASY_DMA 0
#undef SPI1_USE_EASY_DMA
#define SPI1_USE_EASY_DMA 0
#undef SPI2_USE_EASY_DMA
#define SPI2_USE_EASY_DMA 0
#endif
// This set of macros makes it possible to exclude parts of code when one type
// of supported peripherals is not used.
#if ((SPI0_ENABLED && SPI0_USE_EASY_DMA) || \
(SPI1_ENABLED && SPI1_USE_EASY_DMA) || \
(SPI2_ENABLED && SPI2_USE_EASY_DMA))
#define SPIM_IN_USE
#endif
#if ((SPI0_ENABLED && !SPI0_USE_EASY_DMA) || \
(SPI1_ENABLED && !SPI1_USE_EASY_DMA) || \
(SPI2_ENABLED && !SPI2_USE_EASY_DMA))
#define SPI_IN_USE
#endif
#if defined(SPIM_IN_USE) && defined(SPI_IN_USE)
// SPIM and SPI combined
#define CODE_FOR_SPIM(code) if (p_instance->use_easy_dma) { code }
#define CODE_FOR_SPI(code) else { code }
#elif defined(SPIM_IN_USE) && !defined(SPI_IN_USE)
// SPIM only
#define CODE_FOR_SPIM(code) { code }
#define CODE_FOR_SPI(code)
#elif !defined(SPIM_IN_USE) && defined(SPI_IN_USE)
// SPI only
#define CODE_FOR_SPIM(code)
#define CODE_FOR_SPI(code) { code }
#else
#error "Wrong configuration."
#endif
#ifdef SPIM_IN_USE
#ifdef NRF52_PAN_23
#define END_INT_MASK (NRF_SPIM_INT_ENDTX_MASK | NRF_SPIM_INT_ENDRX_MASK)
#else
#define END_INT_MASK NRF_SPIM_INT_END_MASK
#endif
#endif
// Control block - driver instance local data.
typedef struct
{
nrf_drv_spi_handler_t handler;
nrf_drv_spi_evt_t evt; // Keep the struct that is ready for event handler. Less memcpy.
nrf_drv_state_t state;
volatile bool transfer_in_progress;
// [no need for 'volatile' attribute for the following members, as they
// are not concurrently used in IRQ handlers and main line code]
uint8_t ss_pin;
uint8_t orc;
uint8_t bytes_transferred;
bool tx_done : 1;
bool rx_done : 1;
} spi_control_block_t;
static spi_control_block_t m_cb[SPI_COUNT];
static nrf_drv_spi_config_t const m_default_config[SPI_COUNT] = {
#if SPI0_ENABLED
NRF_DRV_SPI_DEFAULT_CONFIG(0),
#endif
#if SPI1_ENABLED
NRF_DRV_SPI_DEFAULT_CONFIG(1),
#endif
#if SPI2_ENABLED
NRF_DRV_SPI_DEFAULT_CONFIG(2),
#endif
};
#if PERIPHERAL_RESOURCE_SHARING_ENABLED
#define IRQ_HANDLER_NAME(n) irq_handler_for_instance_##n
#define IRQ_HANDLER(n) static void IRQ_HANDLER_NAME(n)(void)
#if SPI0_ENABLED
IRQ_HANDLER(0);
#endif
#if SPI1_ENABLED
IRQ_HANDLER(1);
#endif
#if SPI2_ENABLED
IRQ_HANDLER(2);
#endif
static nrf_drv_irq_handler_t const m_irq_handlers[SPI_COUNT] = {
#if SPI0_ENABLED
IRQ_HANDLER_NAME(0),
#endif
#if SPI1_ENABLED
IRQ_HANDLER_NAME(1),
#endif
#if SPI2_ENABLED
IRQ_HANDLER_NAME(2),
#endif
};
#else
#define IRQ_HANDLER(n) void SPI##n##_IRQ_HANDLER(void)
#endif // PERIPHERAL_RESOURCE_SHARING_ENABLED
ret_code_t nrf_drv_spi_init(nrf_drv_spi_t const * const p_instance,
nrf_drv_spi_config_t const * p_config,
nrf_drv_spi_handler_t handler)
{
spi_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
if (p_cb->state != NRF_DRV_STATE_UNINITIALIZED)
{
return NRF_ERROR_INVALID_STATE;
}
#if PERIPHERAL_RESOURCE_SHARING_ENABLED
if (nrf_drv_common_per_res_acquire(p_instance->p_registers,
m_irq_handlers[p_instance->drv_inst_idx]) != NRF_SUCCESS)
{
return NRF_ERROR_BUSY;
}
#endif
if (p_config == NULL)
{
p_config = &m_default_config[p_instance->drv_inst_idx];
}
p_cb->handler = handler;
uint32_t mosi_pin;
uint32_t miso_pin;
// Configure pins used by the peripheral:
// - SCK - output with initial value corresponding with the SPI mode used:
// 0 - for modes 0 and 1 (CPOL = 0), 1 - for modes 2 and 3 (CPOL = 1);
// according to the reference manual guidelines this pin and its input
// buffer must always be connected for the SPI to work.
if (p_config->mode <= NRF_DRV_SPI_MODE_1)
{
nrf_gpio_pin_clear(p_config->sck_pin);
}
else
{
nrf_gpio_pin_set(p_config->sck_pin);
}
NRF_GPIO->PIN_CNF[p_config->sck_pin] =
(GPIO_PIN_CNF_DIR_Output << GPIO_PIN_CNF_DIR_Pos)
| (GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos)
| (GPIO_PIN_CNF_PULL_Disabled << GPIO_PIN_CNF_PULL_Pos)
| (GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos)
| (GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos);
// - MOSI (optional) - output with initial value 0,
if (p_config->mosi_pin != NRF_DRV_SPI_PIN_NOT_USED)
{
mosi_pin = p_config->mosi_pin;
nrf_gpio_pin_clear(mosi_pin);
nrf_gpio_cfg_output(mosi_pin);
}
else
{
mosi_pin = NRF_SPI_PIN_NOT_CONNECTED;
}
// - MISO (optional) - input,
if (p_config->miso_pin != NRF_DRV_SPI_PIN_NOT_USED)
{
miso_pin = p_config->miso_pin;
nrf_gpio_cfg_input(miso_pin, NRF_GPIO_PIN_NOPULL);
}
else
{
miso_pin = NRF_SPI_PIN_NOT_CONNECTED;
}
// - Slave Select (optional) - output with initial value 1 (inactive).
if (p_config->ss_pin != NRF_DRV_SPI_PIN_NOT_USED)
{
nrf_gpio_pin_set(p_config->ss_pin);
nrf_gpio_cfg_output(p_config->ss_pin);
}
m_cb[p_instance->drv_inst_idx].ss_pin = p_config->ss_pin;
CODE_FOR_SPIM
(
NRF_SPIM_Type * p_spim = p_instance->p_registers;
nrf_spim_pins_set(p_spim, p_config->sck_pin, mosi_pin, miso_pin);
nrf_spim_frequency_set(p_spim,
(nrf_spim_frequency_t)p_config->frequency);
nrf_spim_configure(p_spim,
(nrf_spim_mode_t)p_config->mode,
(nrf_spim_bit_order_t)p_config->bit_order);
nrf_spim_orc_set(p_spim, p_config->orc);
if (p_cb->handler)
{
nrf_spim_int_enable(p_spim, END_INT_MASK | NRF_SPIM_INT_STOPPED_MASK);
}
nrf_spim_enable(p_spim);
)
CODE_FOR_SPI
(
NRF_SPI_Type * p_spi = p_instance->p_registers;
nrf_spi_pins_set(p_spi, p_config->sck_pin, mosi_pin, miso_pin);
nrf_spi_frequency_set(p_spi,
(nrf_spi_frequency_t)p_config->frequency);
nrf_spi_configure(p_spi,
(nrf_spi_mode_t)p_config->mode,
(nrf_spi_bit_order_t)p_config->bit_order);
m_cb[p_instance->drv_inst_idx].orc = p_config->orc;
if (p_cb->handler)
{
nrf_spi_int_enable(p_spi, NRF_SPI_INT_READY_MASK);
}
nrf_spi_enable(p_spi);
)
if (p_cb->handler)
{
nrf_drv_common_irq_enable(p_instance->irq, p_config->irq_priority);
}
p_cb->transfer_in_progress = false;
p_cb->state = NRF_DRV_STATE_INITIALIZED;
return NRF_SUCCESS;
}
void nrf_drv_spi_uninit(nrf_drv_spi_t const * const p_instance)
{
spi_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
ASSERT(p_cb->state != NRF_DRV_STATE_UNINITIALIZED);
if (p_cb->handler)
{
nrf_drv_common_irq_disable(p_instance->irq);
}
#define DISABLE_ALL 0xFFFFFFFF
CODE_FOR_SPIM
(
NRF_SPIM_Type * p_spim = p_instance->p_registers;
if (p_cb->handler)
{
nrf_spim_int_disable(p_spim, DISABLE_ALL);
if (p_cb->transfer_in_progress)
{
// Ensure that SPI is not performing any transfer.
nrf_spim_task_trigger(p_spim, NRF_SPIM_TASK_STOP);
while (!nrf_spim_event_check(p_spim, NRF_SPIM_EVENT_STOPPED)) {}
p_cb->transfer_in_progress = false;
}
}
nrf_spim_disable(p_spim);
)
CODE_FOR_SPI
(
NRF_SPI_Type * p_spi = p_instance->p_registers;
if (p_cb->handler)
{
nrf_spi_int_disable(p_spi, DISABLE_ALL);
}
nrf_spi_disable(p_spi);
)
#undef DISABLE_ALL
#if PERIPHERAL_RESOURCE_SHARING_ENABLED
nrf_drv_common_per_res_release(p_instance->p_registers);
#endif
p_cb->state = NRF_DRV_STATE_UNINITIALIZED;
}
ret_code_t nrf_drv_spi_transfer(nrf_drv_spi_t const * const p_instance,
uint8_t const * p_tx_buffer,
uint8_t tx_buffer_length,
uint8_t * p_rx_buffer,
uint8_t rx_buffer_length)
{
nrf_drv_spi_xfer_desc_t xfer_desc;
xfer_desc.p_tx_buffer = p_tx_buffer;
xfer_desc.p_rx_buffer = p_rx_buffer;
xfer_desc.tx_length = tx_buffer_length;
xfer_desc.rx_length = rx_buffer_length;
return nrf_drv_spi_xfer(p_instance, &xfer_desc, 0);
}
static void finish_transfer(spi_control_block_t * p_cb)
{
// If Slave Select signal is used, this is the time to deactivate it.
if (p_cb->ss_pin != NRF_DRV_SPI_PIN_NOT_USED)
{
nrf_gpio_pin_set(p_cb->ss_pin);
}
// By clearing this flag before calling the handler we allow subsequent
// transfers to be started directly from the handler function.
p_cb->transfer_in_progress = false;
p_cb->evt.type = NRF_DRV_SPI_EVENT_DONE;
p_cb->handler(&p_cb->evt);
}
#ifdef SPI_IN_USE
// This function is called from IRQ handler or, in blocking mode, directly
// from the 'nrf_drv_spi_transfer' function.
// It returns true as long as the transfer should be continued, otherwise (when
// there is nothing more to send/receive) it returns false.
static bool transfer_byte(NRF_SPI_Type * p_spi, spi_control_block_t * p_cb)
{
// Read the data byte received in this transfer and store it in RX buffer,
// if needed.
volatile uint8_t rx_data = nrf_spi_rxd_get(p_spi);
if (p_cb->bytes_transferred < p_cb->evt.data.done.rx_length)
{
p_cb->evt.data.done.p_rx_buffer[p_cb->bytes_transferred] = rx_data;
}
++p_cb->bytes_transferred;
// Check if there are more bytes to send or receive and write proper data
// byte (next one from TX buffer or over-run character) to the TXD register
// when needed.
// NOTE - we've already used 'p_cb->bytes_transferred + 1' bytes from our
// buffers, because we take advantage of double buffering of TXD
// register (so in effect one byte is still being transmitted now);
// see how the transfer is started in the 'nrf_drv_spi_transfer'
// function.
uint16_t bytes_used = p_cb->bytes_transferred + 1;
if (bytes_used < p_cb->evt.data.done.tx_length)
{
nrf_spi_txd_set(p_spi, p_cb->evt.data.done.p_tx_buffer[bytes_used]);
return true;
}
else if (bytes_used < p_cb->evt.data.done.rx_length)
{
nrf_spi_txd_set(p_spi, p_cb->orc);
return true;
}
return (p_cb->bytes_transferred < p_cb->evt.data.done.tx_length ||
p_cb->bytes_transferred < p_cb->evt.data.done.rx_length);
}
static void spi_xfer(NRF_SPI_Type * p_spi,
spi_control_block_t * p_cb,
nrf_drv_spi_xfer_desc_t const * p_xfer_desc)
{
p_cb->bytes_transferred = 0;
nrf_spi_int_disable(p_spi, NRF_SPI_INT_READY_MASK);
nrf_spi_event_clear(p_spi, NRF_SPI_EVENT_READY);
// Start the transfer by writing some byte to the TXD register;
// if TX buffer is not empty, take the first byte from this buffer,
// otherwise - use over-run character.
nrf_spi_txd_set(p_spi,
(p_xfer_desc->tx_length > 0 ? p_xfer_desc->p_tx_buffer[0] : p_cb->orc));
// TXD register is double buffered, so next byte to be transmitted can
// be written immediately, if needed, i.e. if TX or RX transfer is to
// be more that 1 byte long. Again - if there is something more in TX
// buffer send it, otherwise use over-run character.
if (p_xfer_desc->tx_length > 1)
{
nrf_spi_txd_set(p_spi, p_xfer_desc->p_tx_buffer[1]);
}
else if (p_xfer_desc->rx_length > 1)
{
nrf_spi_txd_set(p_spi, p_cb->orc);
}
// For blocking mode (user handler not provided) wait here for READY
// events (indicating that the byte from TXD register was transmitted
// and a new incoming byte was moved to the RXD register) and continue
// transaction until all requested bytes are transferred.
// In non-blocking mode - IRQ service routine will do this stuff.
if (p_cb->handler)
{
nrf_spi_int_enable(p_spi, NRF_SPI_INT_READY_MASK);
}
else
{
do {
while (!nrf_spi_event_check(p_spi, NRF_SPI_EVENT_READY)) {}
nrf_spi_event_clear(p_spi, NRF_SPI_EVENT_READY);
} while (transfer_byte(p_spi, p_cb));
if (p_cb->ss_pin != NRF_DRV_SPI_PIN_NOT_USED)
{
nrf_gpio_pin_set(p_cb->ss_pin);
}
}
}
#endif // SPI_IN_USE
#ifdef SPIM_IN_USE
__STATIC_INLINE void spim_int_enable(NRF_SPIM_Type * p_spim, bool enable)
{
if (!enable)
{
nrf_spim_int_disable(p_spim, END_INT_MASK | NRF_SPIM_INT_STOPPED_MASK);
}
else
{
nrf_spim_int_enable(p_spim, END_INT_MASK | NRF_SPIM_INT_STOPPED_MASK);
}
}
__STATIC_INLINE void spim_list_enable_handle(NRF_SPIM_Type * p_spim, uint32_t flags)
{
#ifndef NRF52_PAN_46
if (NRF_DRV_SPI_FLAG_TX_POSTINC & flags)
{
nrf_spim_tx_list_enable(p_spim);
}
else
{
nrf_spim_tx_list_disable(p_spim);
}
if (NRF_DRV_SPI_FLAG_RX_POSTINC & flags)
{
nrf_spim_rx_list_enable(p_spim);
}
else
{
nrf_spim_rx_list_disable(p_spim);
}
#endif
}
static ret_code_t spim_xfer(NRF_SPIM_Type * p_spim,
spi_control_block_t * p_cb,
nrf_drv_spi_xfer_desc_t const * p_xfer_desc,
uint32_t flags)
{
// EasyDMA requires that transfer buffers are placed in Data RAM region;
// signal error if they are not.
if ((p_xfer_desc->p_tx_buffer != NULL && !nrf_drv_is_in_RAM(p_xfer_desc->p_tx_buffer)) ||
(p_xfer_desc->p_rx_buffer != NULL && !nrf_drv_is_in_RAM(p_xfer_desc->p_rx_buffer)))
{
p_cb->transfer_in_progress = false;
return NRF_ERROR_INVALID_ADDR;
}
nrf_spim_tx_buffer_set(p_spim, p_xfer_desc->p_tx_buffer, p_xfer_desc->tx_length);
nrf_spim_rx_buffer_set(p_spim, p_xfer_desc->p_rx_buffer, p_xfer_desc->rx_length);
#ifdef NRF52_PAN_23
nrf_spim_event_clear(p_spim, NRF_SPIM_EVENT_ENDTX);
nrf_spim_event_clear(p_spim, NRF_SPIM_EVENT_ENDRX);
#else
nrf_spim_event_clear(p_spim, NRF_SPIM_EVENT_END);
#endif
nrf_spim_event_clear(p_spim, NRF_SPIM_EVENT_STOPPED);
spim_list_enable_handle(p_spim, flags);
if (!(flags & NRF_DRV_SPI_FLAG_HOLD_XFER))
{
nrf_spim_task_trigger(p_spim, NRF_SPIM_TASK_START);
}
if (!p_cb->handler)
{
#ifdef NRF52_PAN_23
while (!nrf_spim_event_check(p_spim, NRF_SPIM_EVENT_ENDTX) ||
!nrf_spim_event_check(p_spim, NRF_SPIM_EVENT_ENDRX)) {}
#else
while (!nrf_spim_event_check(p_spim, NRF_SPIM_EVENT_END)){}
#endif
// Stop the peripheral after transaction is finished.
nrf_spim_task_trigger(p_spim, NRF_SPIM_TASK_STOP);
while (!nrf_spim_event_check(p_spim, NRF_SPIM_EVENT_STOPPED)) {}
if (p_cb->ss_pin != NRF_DRV_SPI_PIN_NOT_USED)
{
nrf_gpio_pin_set(p_cb->ss_pin);
}
}
else
{
spim_int_enable(p_spim, !(flags & NRF_DRV_SPI_FLAG_NO_XFER_EVT_HANDLER));
}
return NRF_SUCCESS;
}
#endif
ret_code_t nrf_drv_spi_xfer(nrf_drv_spi_t const * const p_instance,
nrf_drv_spi_xfer_desc_t const * p_xfer_desc,
uint32_t flags)
{
spi_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
ASSERT(p_cb->state != NRF_DRV_STATE_UNINITIALIZED);
ASSERT(p_tx_buffer != NULL || tx_buffer_length == 0);
ASSERT(p_rx_buffer != NULL || rx_buffer_length == 0);
if (p_cb->transfer_in_progress)
{
return NRF_ERROR_BUSY;
}
else
{
if (p_cb->handler && !(flags & (NRF_DRV_SPI_FLAG_REPEATED_XFER | NRF_DRV_SPI_FLAG_NO_XFER_EVT_HANDLER)))
{
p_cb->transfer_in_progress = true;
}
}
p_cb->evt.data.done = *p_xfer_desc;
p_cb->tx_done = false;
p_cb->rx_done = false;
if (p_cb->ss_pin != NRF_DRV_SPI_PIN_NOT_USED)
{
nrf_gpio_pin_clear(p_cb->ss_pin);
}
CODE_FOR_SPIM
(
return spim_xfer(p_instance->p_registers, p_cb, p_xfer_desc, flags);
)
CODE_FOR_SPI
(
if (flags)
{
p_cb->transfer_in_progress = false;
return NRF_ERROR_NOT_SUPPORTED;
}
spi_xfer(p_instance->p_registers, p_cb, p_xfer_desc);
return NRF_SUCCESS;
)
}
#ifdef SPIM_IN_USE
static void irq_handler_spim(NRF_SPIM_Type * p_spim, spi_control_block_t * p_cb)
{
ASSERT(p_cb->handler);
#ifdef NRF52_PAN_23
if (nrf_spim_event_check(p_spim, NRF_SPIM_EVENT_STOPPED))
{
nrf_spim_event_clear(p_spim, NRF_SPIM_EVENT_STOPPED);
finish_transfer(p_cb);
}
else
{
if (nrf_spim_event_check(p_spim, NRF_SPIM_EVENT_ENDTX))
{
nrf_spim_event_clear(p_spim, NRF_SPIM_EVENT_ENDTX);
p_cb->tx_done = true;
}
if (nrf_spim_event_check(p_spim, NRF_SPIM_EVENT_ENDRX))
{
nrf_spim_event_clear(p_spim, NRF_SPIM_EVENT_ENDRX);
p_cb->rx_done = true;
}
if (p_cb->tx_done && p_cb->rx_done)
{
nrf_spim_task_trigger(p_spim, NRF_SPIM_TASK_STOP);
}
}
#else
if (nrf_spim_event_check(p_spim, NRF_SPIM_EVENT_END))
{
nrf_spim_event_clear(p_spim, NRF_SPIM_EVENT_END);
finish_transfer(p_cb);
}
#endif
}
uint32_t nrf_drv_spi_start_task_get(nrf_drv_spi_t const * p_instance)
{
NRF_SPIM_Type * p_spim = (NRF_SPIM_Type *)p_instance->p_registers;
return nrf_spim_task_address_get(p_spim, NRF_SPIM_TASK_START);
}
uint32_t nrf_drv_spi_end_event_get(nrf_drv_spi_t const * p_instance)
{
NRF_SPIM_Type * p_spim = (NRF_SPIM_Type *)p_instance->p_registers;
return nrf_spim_event_address_get(p_spim, NRF_SPIM_EVENT_END);
}
#endif // SPIM_IN_USE
#ifdef SPI_IN_USE
static void irq_handler_spi(NRF_SPI_Type * p_spi, spi_control_block_t * p_cb)
{
ASSERT(p_cb->handler);
nrf_spi_event_clear(p_spi, NRF_SPI_EVENT_READY);
if (!transfer_byte(p_spi, p_cb))
{
finish_transfer(p_cb);
}
}
#endif // SPI_IN_USE
#if SPI0_ENABLED
IRQ_HANDLER(0)
{
spi_control_block_t * p_cb = &m_cb[SPI0_INSTANCE_INDEX];
#if SPI0_USE_EASY_DMA
irq_handler_spim(NRF_SPIM0, p_cb);
#else
irq_handler_spi(NRF_SPI0, p_cb);
#endif
}
#endif // SPI0_ENABLED
#if SPI1_ENABLED
IRQ_HANDLER(1)
{
spi_control_block_t * p_cb = &m_cb[SPI1_INSTANCE_INDEX];
#if SPI1_USE_EASY_DMA
irq_handler_spim(NRF_SPIM1, p_cb);
#else
irq_handler_spi(NRF_SPI1, p_cb);
#endif
}
#endif // SPI1_ENABLED
#if SPI2_ENABLED
IRQ_HANDLER(2)
{
spi_control_block_t * p_cb = &m_cb[SPI2_INSTANCE_INDEX];
#if SPI2_USE_EASY_DMA
irq_handler_spim(NRF_SPIM2, p_cb);
#else
irq_handler_spi(NRF_SPI2, p_cb);
#endif
}
#endif // SPI2_ENABLED

View File

@ -0,0 +1,368 @@
/* Copyright (c) 2015 Nordic Semiconductor. All Rights Reserved.
*
* The information contained herein is property of Nordic Semiconductor ASA.
* Terms and conditions of usage are described in detail in NORDIC
* SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT.
*
* Licensees are granted free, non-transferable use of the information. NO
* WARRANTY of ANY KIND is provided. This heading must NOT be removed from
* the file.
*
*/
/**@file
* @addtogroup nrf_spi Serial peripheral interface (SPI)
* @ingroup nrf_drivers
* @brief Serial peripheral interface (SPI) APIs.
*
*
* @addtogroup nrf_spi_master SPI master HAL and driver
* @ingroup nrf_spi
* @brief SPI master APIs.
*/
#ifndef NRF_DRV_SPI_H__
#define NRF_DRV_SPI_H__
#include "nordic_common.h"
#include "nrf_drv_config.h"
#include "nrf_spi.h"
#include "nrf_spim.h"
#include "sdk_errors.h"
#if defined(NRF52)
#define NRF_DRV_SPI_PERIPHERAL(id) \
(CONCAT_3(SPI, id, _USE_EASY_DMA) == 1 ? \
(void *)CONCAT_2(NRF_SPIM, id) \
: (void *)CONCAT_2(NRF_SPI, id))
#define SPI2_IRQ SPIM2_SPIS2_SPI2_IRQn
#define SPI2_IRQ_HANDLER SPIM2_SPIS2_SPI2_IRQHandler
#else
#define NRF_DRV_SPI_PERIPHERAL(id) (void *)CONCAT_2(NRF_SPI, id)
#endif
#define SPI0_IRQ SPI0_TWI0_IRQn
#define SPI0_IRQ_HANDLER SPI0_TWI0_IRQHandler
#define SPI1_IRQ SPI1_TWI1_IRQn
#define SPI1_IRQ_HANDLER SPI1_TWI1_IRQHandler
/**
* @defgroup nrf_drv_spi_master SPI master driver
* @{
* @ingroup nrf_spi_master
*
* @brief Multi-instance SPI master driver.
*/
/**
* @brief SPI master driver instance data structure.
*/
typedef struct
{
void * p_registers; ///< Pointer to the structure with SPI/SPIM peripheral instance registers.
IRQn_Type irq; ///< SPI/SPIM peripheral instance IRQ number.
uint8_t drv_inst_idx; ///< Driver instance index.
bool use_easy_dma; ///< True if the peripheral with EasyDMA (SPIM) shall be used.
} nrf_drv_spi_t;
/**
* @brief Macro for creating an SPI master driver instance.
*/
#define NRF_DRV_SPI_INSTANCE(id) \
{ \
.p_registers = NRF_DRV_SPI_PERIPHERAL(id), \
.irq = CONCAT_3(SPI, id, _IRQ), \
.drv_inst_idx = CONCAT_3(SPI, id, _INSTANCE_INDEX), \
.use_easy_dma = CONCAT_3(SPI, id, _USE_EASY_DMA) \
}
/**
* @brief This value can be provided instead of a pin number for signals MOSI,
* MISO, and Slave Select to specify that the given signal is not used and
* therefore does not need to be connected to a pin.
*/
#define NRF_DRV_SPI_PIN_NOT_USED 0xFF
/**
* @brief SPI data rates.
*/
typedef enum
{
NRF_DRV_SPI_FREQ_125K = NRF_SPI_FREQ_125K, ///< 125 kbps.
NRF_DRV_SPI_FREQ_250K = NRF_SPI_FREQ_250K, ///< 250 kbps.
NRF_DRV_SPI_FREQ_500K = NRF_SPI_FREQ_500K, ///< 500 kbps.
NRF_DRV_SPI_FREQ_1M = NRF_SPI_FREQ_1M, ///< 1 Mbps.
NRF_DRV_SPI_FREQ_2M = NRF_SPI_FREQ_2M, ///< 2 Mbps.
NRF_DRV_SPI_FREQ_4M = NRF_SPI_FREQ_4M, ///< 4 Mbps.
NRF_DRV_SPI_FREQ_8M = NRF_SPI_FREQ_8M ///< 8 Mbps.
} nrf_drv_spi_frequency_t;
/**
* @brief SPI modes.
*/
typedef enum
{
NRF_DRV_SPI_MODE_0 = NRF_SPI_MODE_0, ///< SCK active high, sample on leading edge of clock.
NRF_DRV_SPI_MODE_1 = NRF_SPI_MODE_1, ///< SCK active high, sample on trailing edge of clock.
NRF_DRV_SPI_MODE_2 = NRF_SPI_MODE_2, ///< SCK active low, sample on leading edge of clock.
NRF_DRV_SPI_MODE_3 = NRF_SPI_MODE_3 ///< SCK active low, sample on trailing edge of clock.
} nrf_drv_spi_mode_t;
/**
* @brief SPI bit orders.
*/
typedef enum
{
NRF_DRV_SPI_BIT_ORDER_MSB_FIRST = NRF_SPI_BIT_ORDER_MSB_FIRST, ///< Most significant bit shifted out first.
NRF_DRV_SPI_BIT_ORDER_LSB_FIRST = NRF_SPI_BIT_ORDER_LSB_FIRST ///< Least significant bit shifted out first.
} nrf_drv_spi_bit_order_t;
/**
* @brief SPI master driver instance configuration structure.
*/
typedef struct
{
uint8_t sck_pin; ///< SCK pin number.
uint8_t mosi_pin; ///< MOSI pin number (optional).
/**< Set to @ref NRF_DRV_SPI_PIN_NOT_USED
* if this signal is not needed. */
uint8_t miso_pin; ///< MISO pin number (optional).
/**< Set to @ref NRF_DRV_SPI_PIN_NOT_USED
* if this signal is not needed. */
uint8_t ss_pin; ///< Slave Select pin number (optional).
/**< Set to @ref NRF_DRV_SPI_PIN_NOT_USED
* if this signal is not needed. The driver
* supports only active low for this signal.
* If the signal should be active high,
* it must be controlled externally. */
uint8_t irq_priority; ///< Interrupt priority.
uint8_t orc; ///< Over-run character.
/**< This character is used when all bytes from the TX buffer are sent,
but the transfer continues due to RX. */
nrf_drv_spi_frequency_t frequency; ///< SPI frequency.
nrf_drv_spi_mode_t mode; ///< SPI mode.
nrf_drv_spi_bit_order_t bit_order; ///< SPI bit order.
} nrf_drv_spi_config_t;
/**
* @brief SPI master instance default configuration.
*/
#define NRF_DRV_SPI_DEFAULT_CONFIG(id) \
{ \
.sck_pin = CONCAT_3(SPI, id, _CONFIG_SCK_PIN), \
.mosi_pin = CONCAT_3(SPI, id, _CONFIG_MOSI_PIN), \
.miso_pin = CONCAT_3(SPI, id, _CONFIG_MISO_PIN), \
.ss_pin = NRF_DRV_SPI_PIN_NOT_USED, \
.irq_priority = CONCAT_3(SPI, id, _CONFIG_IRQ_PRIORITY), \
.orc = 0xFF, \
.frequency = NRF_DRV_SPI_FREQ_4M, \
.mode = NRF_DRV_SPI_MODE_0, \
.bit_order = NRF_DRV_SPI_BIT_ORDER_MSB_FIRST, \
}
#define NRF_DRV_SPI_FLAG_TX_POSTINC (1UL << 0) /**< TX buffer address incremented after transfer. */
#define NRF_DRV_SPI_FLAG_RX_POSTINC (1UL << 1) /**< RX buffer address incremented after transfer. */
#define NRF_DRV_SPI_FLAG_NO_XFER_EVT_HANDLER (1UL << 2) /**< Interrupt after each transfer is suppressed, and the event handler is not called. */
#define NRF_DRV_SPI_FLAG_HOLD_XFER (1UL << 3) /**< Set up the transfer but do not start it. */
#define NRF_DRV_SPI_FLAG_REPEATED_XFER (1UL << 4) /**< Flag indicating that the transfer will be executed multiple times. */
/**
* @brief Single transfer descriptor structure.
*/
typedef struct
{
uint8_t const * p_tx_buffer; ///< Pointer to TX buffer.
uint8_t tx_length; ///< TX buffer length.
uint8_t * p_rx_buffer; ///< Pointer to RX buffer.
uint8_t rx_length; ///< RX buffer length.
}nrf_drv_spi_xfer_desc_t;
/**
* @brief Macro for setting up single transfer descriptor.
*
* This macro is for internal use only.
*/
#define NRF_DRV_SPI_SINGLE_XFER(p_tx, tx_len, p_rx, rx_len) \
{ \
.p_tx_buffer = (uint8_t const *)(p_tx), \
.tx_length = (tx_len), \
.p_rx_buffer = (p_rx), \
.rx_length = (rx_len), \
}
/**
* @brief Macro for setting duplex TX RX transfer.
*/
#define NRF_DRV_SPI_XFER_TRX(p_tx_buf, tx_length, p_rx_buf, rx_length) \
NRF_DRV_SPI_SINGLE_XFER(p_tx_buf, tx_length, p_rx_buf, rx_length)
/**
* @brief Macro for setting TX transfer.
*/
#define NRF_DRV_SPI_XFER_TX(p_buf, length) \
NRF_DRV_SPI_SINGLE_XFER(p_buf, length, NULL, 0)
/**
* @brief Macro for setting RX transfer.
*/
#define NRF_DRV_SPI_XFER_RX(p_buf, length) \
NRF_DRV_SPI_SINGLE_XFER(NULL, 0, p_buf, length)
/**
* @brief SPI master driver event types, passed to the handler routine provided
* during initialization.
*/
typedef enum
{
NRF_DRV_SPI_EVENT_DONE, ///< Transfer done.
} nrf_drv_spi_evt_type_t;
typedef struct
{
nrf_drv_spi_evt_type_t type; ///< Event type.
union
{
nrf_drv_spi_xfer_desc_t done; ///< Event data for DONE event.
} data;
} nrf_drv_spi_evt_t;
/**
* @brief SPI master driver event handler type.
*/
typedef void (*nrf_drv_spi_handler_t)(nrf_drv_spi_evt_t const * p_event);
/**
* @brief Function for initializing the SPI master driver instance.
*
* This function configures and enables the specified peripheral.
*
* @param[in] p_instance Pointer to the instance structure.
* @param[in] p_config Pointer to the structure with the initial configuration.
* If NULL, the default configuration is used.
* @param handler Event handler provided by the user. If NULL, transfers
* will be performed in blocking mode.
*
* @retval NRF_SUCCESS If initialization was successful.
* @retval NRF_ERROR_INVALID_STATE If the driver was already initialized.
* @retval NRF_ERROR_BUSY If some other peripheral with the same
* instance ID is already in use. This is
* possible only if PERIPHERAL_RESOURCE_SHARING_ENABLED
* is set to a value other than zero.
*/
ret_code_t nrf_drv_spi_init(nrf_drv_spi_t const * const p_instance,
nrf_drv_spi_config_t const * p_config,
nrf_drv_spi_handler_t handler);
/**
* @brief Function for uninitializing the SPI master driver instance.
*
* @param[in] p_instance Pointer to the instance structure.
*/
void nrf_drv_spi_uninit(nrf_drv_spi_t const * const p_instance);
/**
* @brief Function for starting the SPI data transfer.
*
* If an event handler was provided in the @ref nrf_drv_spi_init call, this function
* returns immediately and the handler is called when the transfer is done.
* Otherwise, the transfer is performed in blocking mode, which means that this function
* returns when the transfer is finished.
*
* @note Peripherals using EasyDMA (for example, SPIM) require the transfer buffers
* to be placed in the Data RAM region. If they are not and an SPIM instance is
* used, this function will fail with the error code NRF_ERROR_INVALID_ADDR.
*
* @param[in] p_instance Pointer to the instance structure.
* @param[in] p_tx_buffer Pointer to the transmit buffer. Can be NULL
* if there is nothing to send.
* @param tx_buffer_length Length of the transmit buffer.
* @param[in] p_rx_buffer Pointer to the receive buffer. Can be NULL
* if there is nothing to receive.
* @param rx_buffer_length Length of the receive buffer.
*
* @retval NRF_SUCCESS If the operation was successful.
* @retval NRF_ERROR_BUSY If a previously started transfer has not finished
* yet.
* @retval NRF_ERROR_INVALID_ADDR If the provided buffers are not placed in the Data
* RAM region.
*/
ret_code_t nrf_drv_spi_transfer(nrf_drv_spi_t const * const p_instance,
uint8_t const * p_tx_buffer,
uint8_t tx_buffer_length,
uint8_t * p_rx_buffer,
uint8_t rx_buffer_length);
/**
* @brief Function for starting the SPI data transfer with additional option flags.
*
* Function enables customizing the transfer by using option flags.
*
* Additional options are provided using the flags parameter:
*
* - @ref NRF_DRV_SPI_FLAG_TX_POSTINC and @ref NRF_DRV_SPI_FLAG_RX_POSTINC<span></span>:
* Post-incrementation of buffer addresses. Supported only by SPIM.
* - @ref NRF_DRV_SPI_FLAG_HOLD_XFER<span></span>: Driver is not starting the transfer. Use this
* flag if the transfer is triggered externally by PPI. Supported only by SPIM. Use
* @ref nrf_drv_twi_start_task_get to get the address of the start task.
* - @ref NRF_DRV_SPI_FLAG_NO_XFER_EVT_HANDLER<span></span>: No user event handler after transfer
* completion. This also means no interrupt at the end of the transfer. Supported only by SPIM.
* If @ref NRF_DRV_SPI_FLAG_NO_XFER_EVT_HANDLER is used, the driver does not set the instance into
* busy state, so you must ensure that the next transfers are set up when SPIM is not active.
* @ref nrf_drv_spi_end_event_get function can be used to detect end of transfer. Option can be used
* together with @ref NRF_DRV_SPI_FLAG_REPEATED_XFER to prepare a sequence of SPI transfers
* without interruptions.
* - @ref NRF_DRV_SPI_FLAG_REPEATED_XFER<span></span>: Prepare for repeated transfers. You can set
* up a number of transfers that will be triggered externally (for example by PPI). An example is
* a TXRX transfer with the options @ref NRF_DRV_SPI_FLAG_RX_POSTINC,
* @ref NRF_DRV_SPI_FLAG_NO_XFER_EVT_HANDLER, and @ref NRF_DRV_SPI_FLAG_REPEATED_XFER. After the
* transfer is set up, a set of transfers can be triggered by PPI that will read, for example,
* the same register of an external component and put it into a RAM buffer without any interrupts.
* @ref nrf_drv_spi_end_event_get can be used to get the address of the END event, which can be
* used to count the number of transfers. If @ref NRF_DRV_SPI_FLAG_REPEATED_XFER is used,
* the driver does not set the instance into busy state, so you must ensure that the next
* transfers are set up when SPIM is not active. Supported only by SPIM.
* @note Function is intended to be used only in non-blocking mode.
*
* @param p_instance SPI instance.
* @param p_xfer_desc Pointer to the transfer descriptor.
* @param flags Transfer options (0 for default settings).
*
* @retval NRF_SUCCESS If the procedure was successful.
* @retval NRF_ERROR_BUSY If the driver is not ready for a new transfer.
* @retval NRF_ERROR_NOT_SUPPORTED If the provided parameters are not supported.
* @retval NRF_ERROR_INVALID_ADDR If the provided buffers are not placed in the Data
* RAM region.
*/
ret_code_t nrf_drv_spi_xfer(nrf_drv_spi_t const * const p_instance,
nrf_drv_spi_xfer_desc_t const * p_xfer_desc,
uint32_t flags);
/**
* @brief Function for returning the address of a SPIM start task.
*
* This function should be used if @ref nrf_drv_spi_xfer was called with the flag @ref NRF_DRV_SPI_FLAG_HOLD_XFER.
* In that case, the transfer is not started by the driver, but it must be started externally by PPI.
*
* @param[in] p_instance SPI instance.
*
* @return Start task address.
*/
uint32_t nrf_drv_spi_start_task_get(nrf_drv_spi_t const * p_instance);
/**
* @brief Function for returning the address of a END SPIM event.
*
* A END event can be used to detect the end of a transfer if the @ref NRF_DRV_SPI_FLAG_NO_XFER_EVT_HANDLER
* option is used.
*
* @param[in] p_instance SPI instance.
*
* @return END event address.
*/
uint32_t nrf_drv_spi_end_event_get(nrf_drv_spi_t const * p_instance);
#endif // NRF_DRV_SPI_H__
/** @} */

View File

@ -0,0 +1,280 @@
/* Copyright (c) 2015 Nordic Semiconductor. All Rights Reserved.
*
* The information contained herein is property of Nordic Semiconductor ASA.
* Terms and conditions of usage are described in detail in NORDIC
* SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT.
*
* Licensees are granted free, non-transferable use of the information. NO
* WARRANTY of ANY KIND is provided. This heading must NOT be removed from
* the file.
*
*/
#include "nrf_drv_timer.h"
#include "nrf_drv_common.h"
#include "app_util_platform.h"
#if (TIMER_COUNT == 0)
#error "No TIMER instances enabled in the driver configuration file."
#endif
/**@brief Timer control block. */
typedef struct
{
nrf_timer_event_handler_t handler;
void * context;
nrf_drv_state_t state;
} timer_control_block_t;
static timer_control_block_t m_cb[TIMER_COUNT];
static const nrf_drv_timer_config_t m_default_config[TIMER_COUNT] = {
#if TIMER0_ENABLED
NRF_DRV_TIMER_DEFAULT_CONFIG(0),
#endif
#if TIMER1_ENABLED
NRF_DRV_TIMER_DEFAULT_CONFIG(1),
#endif
#if TIMER2_ENABLED
NRF_DRV_TIMER_DEFAULT_CONFIG(2),
#endif
#if TIMER3_ENABLED
NRF_DRV_TIMER_DEFAULT_CONFIG(3),
#endif
#if TIMER4_ENABLED
NRF_DRV_TIMER_DEFAULT_CONFIG(4),
#endif
};
ret_code_t nrf_drv_timer_init(nrf_drv_timer_t const * const p_instance,
nrf_drv_timer_config_t const * p_config,
nrf_timer_event_handler_t timer_event_handler)
{
timer_control_block_t * p_cb = &m_cb[p_instance->instance_id];
#ifdef SOFTDEVICE_PRESENT
ASSERT(p_instance->p_reg != NRF_TIMER0);
#endif
ASSERT(NRF_TIMER_IS_BIT_WIDTH_VALID(p_instance->p_reg, p_config->bit_width));
if (p_cb->state != NRF_DRV_STATE_UNINITIALIZED)
{
return NRF_ERROR_INVALID_STATE;
}
if (timer_event_handler == NULL)
{
return NRF_ERROR_INVALID_PARAM;
}
if (p_config == NULL)
{
p_config = &m_default_config[p_instance->instance_id];
}
p_cb->handler = timer_event_handler;
p_cb->context = p_config->p_context;
uint8_t i;
for (i = 0; i < p_instance->cc_channel_count; ++i)
{
nrf_timer_event_clear(p_instance->p_reg,
nrf_timer_compare_event_get(i));
}
nrf_drv_common_irq_enable(nrf_drv_get_IRQn(p_instance->p_reg),
p_config->interrupt_priority);
nrf_timer_mode_set(p_instance->p_reg, p_config->mode);
nrf_timer_bit_width_set(p_instance->p_reg, p_config->bit_width);
nrf_timer_frequency_set(p_instance->p_reg, p_config->frequency);
p_cb->state = NRF_DRV_STATE_INITIALIZED;
return NRF_SUCCESS;
}
void nrf_drv_timer_uninit(nrf_drv_timer_t const * const p_instance)
{
nrf_drv_common_irq_disable(nrf_drv_get_IRQn(p_instance->p_reg));
#define DISABLE_ALL UINT32_MAX
nrf_timer_shorts_disable(p_instance->p_reg, DISABLE_ALL);
nrf_timer_int_disable(p_instance->p_reg, DISABLE_ALL);
#undef DISABLE_ALL
nrf_drv_timer_disable(p_instance);
m_cb[p_instance->instance_id].state = NRF_DRV_STATE_UNINITIALIZED;
}
void nrf_drv_timer_enable(nrf_drv_timer_t const * const p_instance)
{
ASSERT(m_cb[p_instance->instance_id].state == NRF_DRV_STATE_INITIALIZED);
nrf_timer_task_trigger(p_instance->p_reg, NRF_TIMER_TASK_START);
m_cb[p_instance->instance_id].state = NRF_DRV_STATE_POWERED_ON;
}
void nrf_drv_timer_disable(nrf_drv_timer_t const * const p_instance)
{
ASSERT(m_cb[p_instance->instance_id].state == NRF_DRV_STATE_POWERED_ON);
nrf_timer_task_trigger(p_instance->p_reg, NRF_TIMER_TASK_SHUTDOWN);
m_cb[p_instance->instance_id].state = NRF_DRV_STATE_INITIALIZED;
}
void nrf_drv_timer_resume(nrf_drv_timer_t const * const p_instance)
{
ASSERT(m_cb[p_instance->instance_id].state == NRF_DRV_STATE_POWERED_ON);
nrf_timer_task_trigger(p_instance->p_reg, NRF_TIMER_TASK_START);
}
void nrf_drv_timer_pause(nrf_drv_timer_t const * const p_instance)
{
ASSERT(m_cb[p_instance->instance_id].state == NRF_DRV_STATE_POWERED_ON);
nrf_timer_task_trigger(p_instance->p_reg, NRF_TIMER_TASK_STOP);
}
void nrf_drv_timer_clear(nrf_drv_timer_t const * const p_instance)
{
ASSERT(m_cb[p_instance->instance_id].state != NRF_DRV_STATE_UNINITIALIZED);
nrf_timer_task_trigger(p_instance->p_reg, NRF_TIMER_TASK_CLEAR);
}
void nrf_drv_timer_increment(nrf_drv_timer_t const * const p_instance)
{
ASSERT(m_cb[p_instance->instance_id].state == NRF_DRV_STATE_POWERED_ON);
ASSERT(nrf_timer_mode_get(p_instance->p_reg) != NRF_TIMER_MODE_TIMER);
nrf_timer_task_trigger(p_instance->p_reg, NRF_TIMER_TASK_COUNT);
}
uint32_t nrf_drv_timer_capture(nrf_drv_timer_t const * const p_instance,
nrf_timer_cc_channel_t cc_channel)
{
ASSERT(m_cb[p_instance->instance_id].state == NRF_DRV_STATE_POWERED_ON);
ASSERT(cc_channel < p_instance->cc_channel_count);
nrf_timer_task_trigger(p_instance->p_reg,
nrf_timer_capture_task_get(cc_channel));
return nrf_timer_cc_read(p_instance->p_reg, cc_channel);
}
void nrf_drv_timer_compare(nrf_drv_timer_t const * const p_instance,
nrf_timer_cc_channel_t cc_channel,
uint32_t cc_value,
bool enable_int)
{
nrf_timer_int_mask_t timer_int = nrf_timer_compare_int_get(cc_channel);
if (enable_int)
{
nrf_timer_int_enable(p_instance->p_reg, timer_int);
}
else
{
nrf_timer_int_disable(p_instance->p_reg, timer_int);
}
nrf_timer_cc_write(p_instance->p_reg, cc_channel, cc_value);
}
void nrf_drv_timer_extended_compare(nrf_drv_timer_t const * const p_instance,
nrf_timer_cc_channel_t cc_channel,
uint32_t cc_value,
nrf_timer_short_mask_t timer_short_mask,
bool enable_int)
{
nrf_timer_shorts_disable(p_instance->p_reg,
(TIMER_SHORTS_COMPARE0_STOP_Msk << cc_channel) |
(TIMER_SHORTS_COMPARE0_CLEAR_Msk << cc_channel));
nrf_timer_shorts_enable(p_instance->p_reg, timer_short_mask);
(void)nrf_drv_timer_compare(p_instance,
cc_channel,
cc_value,
enable_int);
}
void nrf_drv_timer_compare_int_enable(nrf_drv_timer_t const * const p_instance,
uint32_t channel)
{
ASSERT(m_cb[p_instance->instance_id].state != NRF_DRV_STATE_UNINITIALIZED);
ASSERT(channel < p_instance->cc_channel_count);
nrf_timer_event_clear(p_instance->p_reg,
nrf_timer_compare_event_get(channel));
nrf_timer_int_enable(p_instance->p_reg,
nrf_timer_compare_int_get(channel));
}
void nrf_drv_timer_compare_int_disable(nrf_drv_timer_t const * const p_instance,
uint32_t channel)
{
ASSERT(m_cb[p_instance->instance_id].state != NRF_DRV_STATE_UNINITIALIZED);
ASSERT(channel < p_instance->cc_channel_count);
nrf_timer_int_disable(p_instance->p_reg,
nrf_timer_compare_int_get(channel));
}
static void irq_handler(NRF_TIMER_Type * p_reg,
timer_control_block_t * p_cb,
uint8_t channel_count)
{
uint8_t i;
for (i = 0; i < channel_count; ++i)
{
nrf_timer_event_t event = nrf_timer_compare_event_get(i);
nrf_timer_int_mask_t int_mask = nrf_timer_compare_int_get(i);
if (nrf_timer_event_check(p_reg, event) &&
nrf_timer_int_enable_check(p_reg, int_mask))
{
nrf_timer_event_clear(p_reg, event);
p_cb->handler(event, p_cb->context);
}
}
}
#if TIMER0_ENABLED
void TIMER0_IRQHandler(void)
{
irq_handler(NRF_TIMER0, &m_cb[TIMER0_INSTANCE_INDEX],
NRF_TIMER_CC_CHANNEL_COUNT(0));
}
#endif
#if TIMER1_ENABLED
void TIMER1_IRQHandler(void)
{
irq_handler(NRF_TIMER1, &m_cb[TIMER1_INSTANCE_INDEX],
NRF_TIMER_CC_CHANNEL_COUNT(1));
}
#endif
#if TIMER2_ENABLED
void TIMER2_IRQHandler(void)
{
irq_handler(NRF_TIMER2, &m_cb[TIMER2_INSTANCE_INDEX],
NRF_TIMER_CC_CHANNEL_COUNT(2));
}
#endif
#if TIMER3_ENABLED
void TIMER3_IRQHandler(void)
{
irq_handler(NRF_TIMER3, &m_cb[TIMER3_INSTANCE_INDEX],
NRF_TIMER_CC_CHANNEL_COUNT(3));
}
#endif
#if TIMER4_ENABLED
void TIMER4_IRQHandler(void)
{
irq_handler(NRF_TIMER4, &m_cb[TIMER4_INSTANCE_INDEX],
NRF_TIMER_CC_CHANNEL_COUNT(4));
}
#endif

View File

@ -0,0 +1,372 @@
/* Copyright (c) 2015 Nordic Semiconductor. All Rights Reserved.
*
* The information contained herein is property of Nordic Semiconductor ASA.
* Terms and conditions of usage are described in detail in NORDIC
* SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT.
*
* Licensees are granted free, non-transferable use of the information. NO
* WARRANTY of ANY KIND is provided. This heading must NOT be removed from
* the file.
*
*/
/**@file
* @addtogroup nrf_timer Timer HAL and driver
* @ingroup nrf_drivers
* @brief Timer APIs.
* @details The timer HAL provides basic APIs for accessing the registers
* of the timer. The timer driver provides APIs on a higher level.
*
* @defgroup lib_driver_timer Timer driver
* @{
* @ingroup nrf_timer
* @brief Multi-instance timer driver.
*/
#ifndef NRF_DRV_TIMER_H__
#define NRF_DRV_TIMER_H__
#include "nordic_common.h"
#include "nrf_drv_config.h"
#include "nrf_timer.h"
#include "sdk_errors.h"
#include "nrf_assert.h"
/**
* @brief Timer driver instance data structure.
*/
typedef struct
{
NRF_TIMER_Type * p_reg; ///< Pointer to the structure with TIMER peripheral instance registers.
uint8_t instance_id; ///< Driver instance index.
uint8_t cc_channel_count; ///< Number of capture/compare channels.
} nrf_drv_timer_t;
/**
* @brief Macro for creating a timer driver instance.
*/
#define NRF_DRV_TIMER_INSTANCE(id) \
{ \
.p_reg = CONCAT_2(NRF_TIMER, id), \
.instance_id = CONCAT_3(TIMER, id, _INSTANCE_INDEX), \
.cc_channel_count = NRF_TIMER_CC_CHANNEL_COUNT(id), \
}
/**
* @brief Timer driver instance configuration structure.
*/
typedef struct
{
nrf_timer_frequency_t frequency; ///< Frequency.
nrf_timer_mode_t mode; ///< Mode of operation.
nrf_timer_bit_width_t bit_width; ///< Bit width.
uint8_t interrupt_priority; ///< Interrupt priority.
void * p_context; ///< Context passed to interrupt handler.
} nrf_drv_timer_config_t;
#define TIMER_CONFIG_FREQUENCY(id) CONCAT_3(TIMER, id, _CONFIG_FREQUENCY)
#define TIMER_CONFIG_MODE(id) CONCAT_3(TIMER, id, _CONFIG_MODE)
#define TIMER_CONFIG_BIT_WIDTH(id) CONCAT_3(TIMER, id, _CONFIG_BIT_WIDTH)
#define TIMER_CONFIG_IRQ_PRIORITY(id) CONCAT_3(TIMER, id, _CONFIG_IRQ_PRIORITY)
/**
* @brief Timer driver instance default configuration.
*/
#define NRF_DRV_TIMER_DEFAULT_CONFIG(id) \
{ \
.frequency = TIMER_CONFIG_FREQUENCY(id), \
.mode = (nrf_timer_mode_t)TIMER_CONFIG_MODE(id), \
.bit_width = (nrf_timer_bit_width_t)TIMER_CONFIG_BIT_WIDTH(id), \
.interrupt_priority = TIMER_CONFIG_IRQ_PRIORITY(id), \
.p_context = NULL \
}
/**
* @brief Timer driver event handler type.
*
* @param[in] event_type Timer event.
* @param[in] p_context General purpose parameter set during initialization of
* the timer. This parameter can be used to pass
* additional information to the handler function, for
* example, the timer ID.
*/
typedef void (* nrf_timer_event_handler_t)(nrf_timer_event_t event_type,
void * p_context);
/**
* @brief Function for initializing the timer.
*
* @param[in] p_instance Timer instance.
* @param[in] p_config Initial configuration.
* If NULL, the default configuration is used.
* @param[in] timer_event_handler Event handler provided by the user.
* Must not be NULL.
*
* @retval NRF_SUCCESS If initialization was successful.
* @retval NRF_ERROR_INVALID_STATE If the instance is already initialized.
* @retval NRF_ERROR_INVALID_PARAM If no handler was provided.
*/
ret_code_t nrf_drv_timer_init(nrf_drv_timer_t const * const p_instance,
nrf_drv_timer_config_t const * p_config,
nrf_timer_event_handler_t timer_event_handler);
/**
* @brief Function for uninitializing the timer.
*
* @param[in] p_instance Timer instance.
*/
void nrf_drv_timer_uninit(nrf_drv_timer_t const * const p_instance);
/**
* @brief Function for turning on the timer.
*
* @param[in] p_instance Timer instance.
*/
void nrf_drv_timer_enable(nrf_drv_timer_t const * const p_instance);
/**
* @brief Function for turning off the timer.
*
* Note that the timer will allow to enter the lowest possible SYSTEM_ON state
* only after this function is called.
*
* @param[in] p_instance Timer instance.
*/
void nrf_drv_timer_disable(nrf_drv_timer_t const * const p_instance);
/**
* @brief Function for pausing the timer.
*
* @param[in] p_instance Timer instance.
*/
void nrf_drv_timer_pause(nrf_drv_timer_t const * const p_instance);
/**
* @brief Function for resuming the timer.
*
* @param[in] p_instance Timer instance.
*/
void nrf_drv_timer_resume(nrf_drv_timer_t const * const p_instance);
/**
* @brief Function for clearing the timer.
*
* @param[in] p_instance Timer instance.
*/
void nrf_drv_timer_clear(nrf_drv_timer_t const * const p_instance);
/**
* @brief Function for incrementing the timer.
*
* @param[in] p_instance Timer instance.
*/
void nrf_drv_timer_increment(nrf_drv_timer_t const * const p_instance);
/**
* @brief Function for returning the address of a specific timer task.
*
* @param[in] p_instance Timer instance.
* @param[in] timer_task Timer task.
*
* @return Task address.
*/
__STATIC_INLINE uint32_t nrf_drv_timer_task_address_get(
nrf_drv_timer_t const * const p_instance,
nrf_timer_task_t timer_task);
/**
* @brief Function for returning the address of a specific timer capture task.
*
* @param[in] p_instance Timer instance.
* @param[in] channel Capture channel number.
*
* @return Task address.
*/
__STATIC_INLINE uint32_t nrf_drv_timer_capture_task_address_get(
nrf_drv_timer_t const * const p_instance,
uint32_t channel);
/**
* @brief Function for returning the address of a specific timer event.
*
* @param[in] p_instance Timer instance.
* @param[in] timer_event Timer event.
*
* @return Event address.
*/
__STATIC_INLINE uint32_t nrf_drv_timer_event_address_get(
nrf_drv_timer_t const * const p_instance,
nrf_timer_event_t timer_event);
/**
* @brief Function for returning the address of a specific timer compare event.
*
* @param[in] p_instance Timer instance.
* @param[in] channel Compare channel number.
*
* @return Event address.
*/
__STATIC_INLINE uint32_t nrf_drv_timer_compare_event_address_get(
nrf_drv_timer_t const * const p_instance,
uint32_t channel);
/**
* @brief Function for capturing the timer value.
*
* @param[in] p_instance Timer instance.
* @param[in] cc_channel Capture channel number.
*
* @return Captured value.
*/
uint32_t nrf_drv_timer_capture(nrf_drv_timer_t const * const p_instance,
nrf_timer_cc_channel_t cc_channel);
/**
* @brief Function for returning the capture value from a specific channel.
*
* Use this function to read channel values when PPI is used for capturing.
*
* @param[in] p_instance Timer instance.
* @param[in] cc_channel Capture channel number.
*
* @return Captured value.
*/
__STATIC_INLINE uint32_t nrf_drv_timer_capture_get(
nrf_drv_timer_t const * const p_instance,
nrf_timer_cc_channel_t cc_channel);
/**
* @brief Function for setting the timer channel in compare mode.
*
* @param[in] p_instance Timer instance.
* @param[in] cc_channel Compare channel number.
* @param[in] cc_value Compare value.
* @param[in] enable_int Enable or disable the interrupt for the compare channel.
*/
void nrf_drv_timer_compare(nrf_drv_timer_t const * const p_instance,
nrf_timer_cc_channel_t cc_channel,
uint32_t cc_value,
bool enable_int);
/**
* @brief Function for setting the timer channel in extended compare mode.
*
* @param[in] p_instance Timer instance.
* @param[in] cc_channel Compare channel number.
* @param[in] cc_value Compare value.
* @param[in] timer_short_mask Shortcut between the compare event on the channel
* and the timer task (STOP or CLEAR).
* @param[in] enable_int Enable or disable the interrupt for the compare
* channel.
*/
void nrf_drv_timer_extended_compare(nrf_drv_timer_t const * const p_instance,
nrf_timer_cc_channel_t cc_channel,
uint32_t cc_value,
nrf_timer_short_mask_t timer_short_mask,
bool enable_int);
/**
* @brief Function for converting time in microseconds to timer ticks.
*
* @param[in] p_instance Timer instance.
* @param[in] time_us Time in microseconds.
*
* @return Number of ticks.
*/
__STATIC_INLINE uint32_t nrf_drv_timer_us_to_ticks(
nrf_drv_timer_t const * const p_instance,
uint32_t time_us);
/**
* @brief Function for converting time in milliseconds to timer ticks.
*
* @param[in] p_instance Timer instance.
* @param[in] time_ms Time in milliseconds.
*
* @return Number of ticks.
*/
__STATIC_INLINE uint32_t nrf_drv_timer_ms_to_ticks(
nrf_drv_timer_t const * const p_instance,
uint32_t time_ms);
/**
* @brief Function for enabling timer compare interrupt.
*
* @param[in] p_instance Timer instance.
* @param[in] channel Compare channel.
*/
void nrf_drv_timer_compare_int_enable(nrf_drv_timer_t const * const p_instance,
uint32_t channel);
/**
* @brief Function for disabling timer compare interrupt.
*
* @param[in] p_instance Timer instance.
* @param[in] channel Compare channel.
*/
void nrf_drv_timer_compare_int_disable(nrf_drv_timer_t const * const p_instance,
uint32_t channel);
#ifndef SUPPRESS_INLINE_IMPLEMENTATION
__STATIC_INLINE uint32_t nrf_drv_timer_task_address_get(
nrf_drv_timer_t const * const p_instance,
nrf_timer_task_t timer_task)
{
return (uint32_t)nrf_timer_task_address_get(p_instance->p_reg, timer_task);
}
__STATIC_INLINE uint32_t nrf_drv_timer_capture_task_address_get(
nrf_drv_timer_t const * const p_instance,
uint32_t channel)
{
ASSERT(channel < p_instance->cc_channel_count);
return (uint32_t)nrf_timer_task_address_get(p_instance->p_reg,
nrf_timer_capture_task_get(channel));
}
__STATIC_INLINE uint32_t nrf_drv_timer_event_address_get(
nrf_drv_timer_t const * const p_instance,
nrf_timer_event_t timer_event)
{
return (uint32_t)nrf_timer_event_address_get(p_instance->p_reg, timer_event);
}
__STATIC_INLINE uint32_t nrf_drv_timer_compare_event_address_get(
nrf_drv_timer_t const * const p_instance,
uint32_t channel)
{
ASSERT(channel < p_instance->cc_channel_count);
return (uint32_t)nrf_timer_event_address_get(p_instance->p_reg,
nrf_timer_compare_event_get(channel));
}
__STATIC_INLINE uint32_t nrf_drv_timer_capture_get(
nrf_drv_timer_t const * const p_instance,
nrf_timer_cc_channel_t cc_channel)
{
return nrf_timer_cc_read(p_instance->p_reg, cc_channel);
}
__STATIC_INLINE uint32_t nrf_drv_timer_us_to_ticks(
nrf_drv_timer_t const * const p_instance,
uint32_t timer_us)
{
return nrf_timer_us_to_ticks(timer_us,
nrf_timer_frequency_get(p_instance->p_reg));
}
__STATIC_INLINE uint32_t nrf_drv_timer_ms_to_ticks(
nrf_drv_timer_t const * const p_instance,
uint32_t timer_ms)
{
return nrf_timer_ms_to_ticks(timer_ms,
nrf_timer_frequency_get(p_instance->p_reg));
}
#endif // SUPPRESS_INLINE_IMPLEMENTATION
#endif // NRF_DRV_TIMER_H__
/** @} */

View File

@ -0,0 +1,972 @@
/* Copyright (c) 2015 Nordic Semiconductor. All Rights Reserved.
*
* The information contained herein is property of Nordic Semiconductor ASA.
* Terms and conditions of usage are described in detail in NORDIC
* SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT.
*
* Licensees are granted free, non-transferable use of the information. NO
* WARRANTY of ANY KIND is provided. This heading must NOT be removed from
* the file.
*
*/
#include "nrf_drv_twi.h"
#include "nrf_drv_common.h"
#include "nrf_gpio.h"
#include "nrf_assert.h"
#include "app_util_platform.h"
#include "nrf_delay.h"
#include <stdio.h>
#define TWI0_IRQ_HANDLER SPI0_TWI0_IRQHandler
#define TWI1_IRQ_HANDLER SPI1_TWI1_IRQHandler
#if (defined(TWIM_IN_USE) && defined(TWI_IN_USE))
// TWIM and TWI combined
#define CODE_FOR_TWIM(code) if (p_instance->use_easy_dma) { code }
#define CODE_FOR_TWI(code) else { code }
#elif (defined(TWIM_IN_USE) && !defined(TWI_IN_USE))
// TWIM only
#define CODE_FOR_TWIM(code) { code }
#define CODE_FOR_TWI(code)
#elif (!defined(TWIM_IN_USE) && defined(TWI_IN_USE))
// TWI only
#define CODE_FOR_TWIM(code)
#define CODE_FOR_TWI(code) { code }
#else
#error "Wrong configuration."
#endif
// All interrupt flags
#define DISABLE_ALL 0xFFFFFFFF
#define SCL_PIN_CONF ((GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos) \
| (GPIO_PIN_CNF_DRIVE_S0D1 << GPIO_PIN_CNF_DRIVE_Pos) \
| (GPIO_PIN_CNF_PULL_Pullup << GPIO_PIN_CNF_PULL_Pos) \
| (GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos) \
| (GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos))
#define SDA_PIN_CONF SCL_PIN_CONF
#define SCL_PIN_CONF_CLR ((GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos) \
| (GPIO_PIN_CNF_DRIVE_S0D1 << GPIO_PIN_CNF_DRIVE_Pos) \
| (GPIO_PIN_CNF_PULL_Pullup << GPIO_PIN_CNF_PULL_Pos) \
| (GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos) \
| (GPIO_PIN_CNF_DIR_Output << GPIO_PIN_CNF_DIR_Pos))
#define SDA_PIN_CONF_CLR SCL_PIN_CONF_CLR
// Control block - driver instance local data.
typedef struct
{
nrf_drv_twi_evt_handler_t handler;
void * p_context;
volatile uint32_t int_mask;
nrf_drv_twi_xfer_desc_t xfer_desc;
uint32_t flags;
uint8_t * p_curr_buf;
uint8_t curr_length;
bool curr_no_stop;
nrf_drv_state_t state;
bool error;
volatile bool busy;
bool repeated;
uint8_t bytes_transferred;
} twi_control_block_t;
static twi_control_block_t m_cb[TWI_COUNT];
static nrf_drv_twi_config_t const m_default_config[TWI_COUNT] = {
#if TWI0_ENABLED
NRF_DRV_TWI_DEFAULT_CONFIG(0),
#endif
#if TWI1_ENABLED
NRF_DRV_TWI_DEFAULT_CONFIG(1),
#endif
};
#if PERIPHERAL_RESOURCE_SHARING_ENABLED
#define IRQ_HANDLER_NAME(n) irq_handler_for_instance_##n
#define IRQ_HANDLER(n) static void IRQ_HANDLER_NAME(n)(void)
#if TWI0_ENABLED
IRQ_HANDLER(0);
#endif
#if TWI1_ENABLED
IRQ_HANDLER(1);
#endif
static nrf_drv_irq_handler_t const m_irq_handlers[TWI_COUNT] = {
#if TWI0_ENABLED
IRQ_HANDLER_NAME(0),
#endif
#if TWI1_ENABLED
IRQ_HANDLER_NAME(1),
#endif
};
#else
#define IRQ_HANDLER(n) void SPI##n##_TWI##n##_IRQHandler(void)
#endif // PERIPHERAL_RESOURCE_SHARING_ENABLED
static void twi_clear_bus(nrf_drv_twi_t const * const p_instance,
nrf_drv_twi_config_t const * p_config)
{
NRF_GPIO->PIN_CNF[p_config->scl] = SCL_PIN_CONF;
NRF_GPIO->PIN_CNF[p_config->sda] = SDA_PIN_CONF;
nrf_gpio_pin_set(p_config->scl);
nrf_gpio_pin_set(p_config->sda);
NRF_GPIO->PIN_CNF[p_config->scl] = SCL_PIN_CONF_CLR;
NRF_GPIO->PIN_CNF[p_config->sda] = SDA_PIN_CONF_CLR;
nrf_delay_us(4);
for(int i = 0; i < 9; i++)
{
if (nrf_gpio_pin_read(p_config->sda))
{
if(i == 0)
{
return;
}
else
{
break;
}
}
nrf_gpio_pin_clear(p_config->scl);
nrf_delay_us(4);
nrf_gpio_pin_set(p_config->scl);
nrf_delay_us(4);
}
nrf_gpio_pin_clear(p_config->sda);
nrf_delay_us(4);
nrf_gpio_pin_set(p_config->sda);
}
ret_code_t nrf_drv_twi_init(nrf_drv_twi_t const * p_instance,
nrf_drv_twi_config_t const * p_config,
nrf_drv_twi_evt_handler_t event_handler,
void * p_context)
{
twi_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
if (p_cb->state != NRF_DRV_STATE_UNINITIALIZED)
{
return NRF_ERROR_INVALID_STATE;
}
if (p_config == NULL)
{
p_config = &m_default_config[p_instance->drv_inst_idx];
}
#if PERIPHERAL_RESOURCE_SHARING_ENABLED
if (nrf_drv_common_per_res_acquire(p_instance->reg.p_twi,
m_irq_handlers[p_instance->drv_inst_idx]) != NRF_SUCCESS)
{
return NRF_ERROR_BUSY;
}
#endif // PERIPHERAL_RESOURCE_SHARING_ENABLED
p_cb->handler = event_handler;
p_cb->p_context = p_context;
p_cb->int_mask = 0;
p_cb->repeated = false;
p_cb->busy = false;
twi_clear_bus(p_instance, p_config);
/* To secure correct signal levels on the pins used by the TWI
master when the system is in OFF mode, and when the TWI master is
disabled, these pins must be configured in the GPIO peripheral.
*/
NRF_GPIO->PIN_CNF[p_config->scl] = SCL_PIN_CONF;
NRF_GPIO->PIN_CNF[p_config->sda] = SDA_PIN_CONF;
CODE_FOR_TWIM
(
NRF_TWIM_Type * p_twim = p_instance->reg.p_twim;
nrf_twim_pins_set(p_twim, p_config->scl, p_config->sda);
nrf_twim_frequency_set(p_twim,
(nrf_twim_frequency_t)p_config->frequency);
)
CODE_FOR_TWI
(
NRF_TWI_Type * p_twi = p_instance->reg.p_twi;
nrf_twi_pins_set(p_twi, p_config->scl, p_config->sda);
nrf_twi_frequency_set(p_twi,
(nrf_twi_frequency_t)p_config->frequency);
)
if (p_cb->handler)
{
CODE_FOR_TWIM
(
nrf_drv_common_irq_enable(nrf_drv_get_IRQn((void *)p_instance->reg.p_twim),
p_config->interrupt_priority);
)
CODE_FOR_TWI
(
nrf_drv_common_irq_enable(nrf_drv_get_IRQn((void *)p_instance->reg.p_twi),
p_config->interrupt_priority);
)
}
p_cb->state = NRF_DRV_STATE_INITIALIZED;
return NRF_SUCCESS;
}
void nrf_drv_twi_uninit(nrf_drv_twi_t const * p_instance)
{
twi_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
ASSERT(p_cb->state != NRF_DRV_STATE_UNINITIALIZED);
if (p_cb->handler)
{
CODE_FOR_TWIM
(
nrf_drv_common_irq_disable(nrf_drv_get_IRQn((void *)p_instance->reg.p_twim));
)
CODE_FOR_TWI
(
nrf_drv_common_irq_disable(nrf_drv_get_IRQn((void *)p_instance->reg.p_twi));
)
}
nrf_drv_twi_disable(p_instance);
#if PERIPHERAL_RESOURCE_SHARING_ENABLED
nrf_drv_common_per_res_release(p_instance->reg.p_twi);
#endif
p_cb->state = NRF_DRV_STATE_UNINITIALIZED;
}
void nrf_drv_twi_enable(nrf_drv_twi_t const * p_instance)
{
twi_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
ASSERT(p_cb->state == NRF_DRV_STATE_INITIALIZED);
CODE_FOR_TWIM
(
NRF_TWIM_Type * p_twim = p_instance->reg.p_twim;
nrf_twim_enable(p_twim);
)
CODE_FOR_TWI
(
NRF_TWI_Type * p_twi = p_instance->reg.p_twi;
nrf_twi_enable(p_twi);
)
p_cb->state = NRF_DRV_STATE_POWERED_ON;
}
void nrf_drv_twi_disable(nrf_drv_twi_t const * p_instance)
{
twi_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
ASSERT(p_cb->state != NRF_DRV_STATE_UNINITIALIZED);
CODE_FOR_TWIM
(
NRF_TWIM_Type * p_twim = p_instance->reg.p_twim;
p_cb->int_mask = 0;
nrf_twim_int_disable(p_twim, DISABLE_ALL);
nrf_twim_shorts_disable(p_twim, DISABLE_ALL);
nrf_twim_disable(p_twim);
)
CODE_FOR_TWI
(
NRF_TWI_Type * p_twi = p_instance->reg.p_twi;
nrf_twi_int_disable(p_twi, DISABLE_ALL);
nrf_twi_shorts_disable(p_twi, DISABLE_ALL);
nrf_twi_disable(p_twi);
)
p_cb->state = NRF_DRV_STATE_INITIALIZED;
}
#ifdef TWI_IN_USE
static bool twi_send_byte(NRF_TWI_Type * p_twi,
uint8_t const * p_data,
uint8_t length,
uint8_t * p_bytes_transferred,
bool no_stop)
{
if (*p_bytes_transferred < length)
{
nrf_twi_txd_set(p_twi, p_data[*p_bytes_transferred]);
++(*p_bytes_transferred);
}
else
{
if (no_stop)
{
nrf_twi_task_trigger(p_twi, NRF_TWI_TASK_SUSPEND);
return false;
}
else
{
nrf_twi_task_trigger(p_twi, NRF_TWI_TASK_STOP);
}
}
return true;
}
static void twi_receive_byte(NRF_TWI_Type * p_twi,
uint8_t * p_data,
uint8_t length,
uint8_t * p_bytes_transferred)
{
if (*p_bytes_transferred < length)
{
p_data[*p_bytes_transferred] = nrf_twi_rxd_get(p_twi);
++(*p_bytes_transferred);
if (*p_bytes_transferred == length-1)
{
nrf_twi_shorts_set(p_twi, NRF_TWI_SHORT_BB_STOP_MASK);
}
else if (*p_bytes_transferred == length)
{
return;
}
nrf_twi_task_trigger(p_twi, NRF_TWI_TASK_RESUME);
}
}
static bool twi_transfer(NRF_TWI_Type * p_twi,
bool * p_error,
uint8_t * p_bytes_transferred,
uint8_t * p_data,
uint8_t length,
bool no_stop)
{
bool do_stop_check;
if ((*p_error == true) || (*p_bytes_transferred == length))
{
do_stop_check = true;
}
else
{
do_stop_check = false;
}
if (*p_error)
{
nrf_twi_event_clear(p_twi, NRF_TWI_EVENT_ERROR);
nrf_twi_event_clear(p_twi, NRF_TWI_EVENT_TXDSENT);
nrf_twi_event_clear(p_twi, NRF_TWI_EVENT_RXDREADY);
}
else if (nrf_twi_event_check(p_twi, NRF_TWI_EVENT_ERROR))
{
nrf_twi_event_clear(p_twi, NRF_TWI_EVENT_ERROR);
nrf_twi_task_trigger(p_twi, NRF_TWI_TASK_STOP);
*p_error = true;
}
else
{
if (nrf_twi_event_check(p_twi, NRF_TWI_EVENT_TXDSENT))
{
nrf_twi_event_clear(p_twi, NRF_TWI_EVENT_TXDSENT);
if (nrf_twi_event_check(p_twi, NRF_TWI_EVENT_ERROR))
{
nrf_twi_event_clear(p_twi, NRF_TWI_EVENT_ERROR);
nrf_twi_task_trigger(p_twi, NRF_TWI_TASK_STOP);
*p_error = true;
}
else
{
if (!twi_send_byte(p_twi, p_data, length, p_bytes_transferred, no_stop))
{
return false;
}
}
}
else if (nrf_twi_event_check(p_twi, NRF_TWI_EVENT_RXDREADY))
{
nrf_twi_event_clear(p_twi, NRF_TWI_EVENT_RXDREADY);
if (nrf_twi_event_check(p_twi, NRF_TWI_EVENT_ERROR))
{
nrf_twi_event_clear(p_twi, NRF_TWI_EVENT_ERROR);
nrf_twi_task_trigger(p_twi, NRF_TWI_TASK_STOP);
*p_error = true;
}
else
{
twi_receive_byte(p_twi, p_data, length, p_bytes_transferred);
}
}
}
if (do_stop_check && nrf_twi_event_check(p_twi, NRF_TWI_EVENT_STOPPED))
{
nrf_twi_event_clear(p_twi, NRF_TWI_EVENT_STOPPED);
return false;
}
return true;
}
static ret_code_t twi_tx_start_transfer(twi_control_block_t * p_cb,
NRF_TWI_Type * p_twi,
uint8_t const * p_data,
uint8_t length,
bool no_stop)
{
ret_code_t ret_code = NRF_SUCCESS;
nrf_twi_event_clear(p_twi, NRF_TWI_EVENT_STOPPED);
nrf_twi_event_clear(p_twi, NRF_TWI_EVENT_ERROR);
nrf_twi_event_clear(p_twi, NRF_TWI_EVENT_TXDSENT);
nrf_twi_event_clear(p_twi, NRF_TWI_EVENT_RXDREADY);
nrf_twi_shorts_set(p_twi, 0);
p_cb->bytes_transferred = 0;
p_cb->error = false;
// In case TWI is suspended resume its operation.
nrf_twi_task_trigger(p_twi, NRF_TWI_TASK_RESUME);
nrf_twi_task_trigger(p_twi, NRF_TWI_TASK_STARTTX);
(void)twi_send_byte(p_twi, p_data, length, &p_cb->bytes_transferred, no_stop);
if (p_cb->handler)
{
p_cb->int_mask = NRF_TWI_INT_STOPPED_MASK |
NRF_TWI_INT_ERROR_MASK |
NRF_TWI_INT_TXDSENT_MASK |
NRF_TWI_INT_RXDREADY_MASK;
nrf_twi_int_enable(p_twi, p_cb->int_mask);
}
else
{
while (twi_transfer(p_twi, &p_cb->error, &p_cb->bytes_transferred, (uint8_t *)p_data, length, no_stop))
{}
if (p_cb->error)
{
ret_code = NRF_ERROR_INTERNAL;
}
}
return ret_code;
}
static ret_code_t twi_rx_start_transfer(twi_control_block_t * p_cb,
NRF_TWI_Type * p_twi,
uint8_t const * p_data,
uint8_t length)
{
ret_code_t ret_code = NRF_SUCCESS;
nrf_twi_event_clear(p_twi, NRF_TWI_EVENT_STOPPED);
nrf_twi_event_clear(p_twi, NRF_TWI_EVENT_ERROR);
nrf_twi_event_clear(p_twi, NRF_TWI_EVENT_TXDSENT);
nrf_twi_event_clear(p_twi, NRF_TWI_EVENT_RXDREADY);
p_cb->bytes_transferred = 0;
p_cb->error = false;
if (length == 1)
{
nrf_twi_shorts_set(p_twi, NRF_TWI_SHORT_BB_STOP_MASK);
}
else
{
nrf_twi_shorts_set(p_twi, NRF_TWI_SHORT_BB_SUSPEND_MASK);
}
// In case TWI is suspended resume its operation.
nrf_twi_task_trigger(p_twi, NRF_TWI_TASK_RESUME);
nrf_twi_task_trigger(p_twi, NRF_TWI_TASK_STARTRX);
if (p_cb->handler)
{
p_cb->int_mask = NRF_TWI_INT_STOPPED_MASK |
NRF_TWI_INT_ERROR_MASK |
NRF_TWI_INT_TXDSENT_MASK |
NRF_TWI_INT_RXDREADY_MASK;
nrf_twi_int_enable(p_twi, p_cb->int_mask);
}
else
{
while (twi_transfer(p_twi, &p_cb->error, &p_cb->bytes_transferred, (uint8_t*)p_data, length, false))
{}
if (p_cb->error)
{
ret_code = NRF_ERROR_INTERNAL;
}
}
return ret_code;
}
__STATIC_INLINE ret_code_t twi_xfer(twi_control_block_t * p_cb,
NRF_TWI_Type * p_twi,
nrf_drv_twi_xfer_desc_t const * p_xfer_desc,
uint32_t flags)
{
ret_code_t ret = NRF_SUCCESS;
/* Block TWI interrupts to ensure that function is not interrupted by TWI interrupt. */
nrf_twi_int_disable(p_twi, DISABLE_ALL);
if (p_cb->busy)
{
nrf_twi_int_enable(p_twi, p_cb->int_mask);
return NRF_ERROR_BUSY;
}
else
{
p_cb->busy = (NRF_DRV_TWI_FLAG_NO_XFER_EVT_HANDLER & flags) ? false : true;
}
if (flags & NRF_DRV_TWI_FLAG_HOLD_XFER)
{
return NRF_ERROR_NOT_SUPPORTED;
}
p_cb->flags = flags;
p_cb->xfer_desc = *p_xfer_desc;
p_cb->curr_length = p_xfer_desc->primary_length;
p_cb->p_curr_buf = p_xfer_desc->p_primary_buf;
nrf_twi_address_set(p_twi, p_xfer_desc->address);
if (p_xfer_desc->type != NRF_DRV_TWI_XFER_RX)
{
p_cb->curr_no_stop = ((p_xfer_desc->type == NRF_DRV_TWI_XFER_TX) &&
!(flags & NRF_DRV_TWI_FLAG_TX_NO_STOP)) ? false : true;
ret = twi_tx_start_transfer(p_cb, p_twi, p_xfer_desc->p_primary_buf, p_xfer_desc->primary_length, p_cb->curr_no_stop);
}
else
{
p_cb->curr_no_stop = false;
ret = twi_rx_start_transfer(p_cb, p_twi, p_xfer_desc->p_primary_buf, p_xfer_desc->primary_length);
}
if (p_cb->handler == NULL)
{
p_cb->busy = false;
}
return ret;
}
#endif
#ifdef TWIM_IN_USE
__STATIC_INLINE void twim_list_enable_handle(NRF_TWIM_Type * p_twim, uint32_t flags)
{
if (NRF_DRV_TWI_FLAG_TX_POSTINC & flags)
{
nrf_twim_tx_list_enable(p_twim);
}
else
{
nrf_twim_tx_list_disable(p_twim);
}
if (NRF_DRV_TWI_FLAG_RX_POSTINC & flags)
{
nrf_twim_rx_list_enable(p_twim);
}
else
{
nrf_twim_rx_list_disable(p_twim);
}
#ifndef NRF52_PAN_46
#endif
}
__STATIC_INLINE ret_code_t twim_xfer(twi_control_block_t * p_cb,
NRF_TWIM_Type * p_twim,
nrf_drv_twi_xfer_desc_t const * p_xfer_desc,
uint32_t flags)
{
ret_code_t ret = NRF_SUCCESS;
nrf_twim_task_t start_task = NRF_TWIM_TASK_STARTTX;
nrf_twim_event_t evt_to_wait = NRF_TWIM_EVENT_STOPPED;
if (!nrf_drv_is_in_RAM(p_xfer_desc->p_primary_buf))
{
return NRF_ERROR_INVALID_ADDR;
}
/* Block TWI interrupts to ensure that function is not interrupted by TWI interrupt. */
nrf_twim_int_disable(p_twim, DISABLE_ALL);
if (p_cb->busy)
{
nrf_twim_int_enable(p_twim, p_cb->int_mask);
return NRF_ERROR_BUSY;
}
else
{
p_cb->busy = ((NRF_DRV_TWI_FLAG_NO_XFER_EVT_HANDLER & flags) ||
(NRF_DRV_TWI_FLAG_REPEATED_XFER & flags)) ? false: true;
}
p_cb->xfer_desc = *p_xfer_desc;
p_cb->repeated = (flags & NRF_DRV_TWI_FLAG_REPEATED_XFER) ? true : false;
nrf_twim_address_set(p_twim, p_xfer_desc->address);
nrf_twim_event_clear(p_twim, NRF_TWIM_EVENT_STOPPED);
nrf_twim_event_clear(p_twim, NRF_TWIM_EVENT_ERROR);
twim_list_enable_handle(p_twim, flags);
switch (p_xfer_desc->type)
{
case NRF_DRV_TWI_XFER_TXTX:
ASSERT(!(flags & NRF_DRV_TWI_FLAG_REPEATED_XFER));
ASSERT(!(flags & NRF_DRV_TWI_FLAG_HOLD_XFER));
ASSERT(!(flags & NRF_DRV_TWI_FLAG_NO_XFER_EVT_HANDLER));
if (!nrf_drv_is_in_RAM(p_xfer_desc->p_secondary_buf))
{
return NRF_ERROR_INVALID_ADDR;
}
nrf_twim_shorts_set(p_twim, NRF_TWIM_SHORT_LASTTX_SUSPEND_MASK);
nrf_twim_tx_buffer_set(p_twim, p_xfer_desc->p_primary_buf, p_xfer_desc->primary_length);
nrf_twim_event_clear(p_twim, NRF_TWIM_EVENT_TXSTARTED);
nrf_twim_event_clear(p_twim, NRF_TWIM_EVENT_LASTTX);
nrf_twim_event_clear(p_twim, NRF_TWIM_EVENT_SUSPENDED);
nrf_twim_task_trigger(p_twim, NRF_TWIM_TASK_RESUME);
nrf_twim_task_trigger(p_twim, NRF_TWIM_TASK_STARTTX);
while(!nrf_twim_event_check(p_twim, NRF_TWIM_EVENT_TXSTARTED))
{}
nrf_twim_event_clear(p_twim, NRF_TWIM_EVENT_TXSTARTED);
nrf_twim_tx_buffer_set(p_twim, p_xfer_desc->p_secondary_buf, p_xfer_desc->secondary_length);
p_cb->int_mask = NRF_TWIM_INT_SUSPENDED_MASK | NRF_TWIM_INT_ERROR_MASK;
break;
case NRF_DRV_TWI_XFER_TXRX:
nrf_twim_tx_buffer_set(p_twim, p_xfer_desc->p_primary_buf, p_xfer_desc->primary_length);
nrf_twim_rx_buffer_set(p_twim, p_xfer_desc->p_secondary_buf, p_xfer_desc->secondary_length);
nrf_twim_shorts_set(p_twim, NRF_TWIM_SHORT_LASTTX_STARTRX_MASK |
NRF_TWIM_SHORT_LASTRX_STOP_MASK);
p_cb->int_mask = NRF_TWIM_INT_STOPPED_MASK | NRF_TWIM_INT_ERROR_MASK;
break;
case NRF_DRV_TWI_XFER_TX:
nrf_twim_tx_buffer_set(p_twim, p_xfer_desc->p_primary_buf, p_xfer_desc->primary_length);
if (NRF_DRV_TWI_FLAG_TX_NO_STOP & flags)
{
nrf_twim_shorts_set(p_twim, NRF_TWIM_SHORT_LASTTX_SUSPEND_MASK);
p_cb->int_mask = NRF_TWIM_INT_SUSPENDED_MASK | NRF_TWIM_INT_ERROR_MASK;
nrf_twim_event_clear(p_twim, NRF_TWIM_EVENT_SUSPENDED);
evt_to_wait = NRF_TWIM_EVENT_SUSPENDED;
}
else
{
nrf_twim_shorts_set(p_twim, NRF_TWIM_SHORT_LASTTX_STOP_MASK);
p_cb->int_mask = NRF_TWIM_INT_STOPPED_MASK | NRF_TWIM_INT_ERROR_MASK;
}
nrf_twim_task_trigger(p_twim, NRF_TWIM_TASK_RESUME);
break;
case NRF_DRV_TWI_XFER_RX:
nrf_twim_rx_buffer_set(p_twim, p_xfer_desc->p_primary_buf, p_xfer_desc->primary_length);
nrf_twim_shorts_set(p_twim, NRF_TWIM_SHORT_LASTRX_STOP_MASK);
p_cb->int_mask = NRF_TWIM_INT_STOPPED_MASK | NRF_TWIM_INT_ERROR_MASK;
start_task = NRF_TWIM_TASK_STARTRX;
nrf_twim_task_trigger(p_twim, NRF_TWIM_TASK_RESUME);
break;
default:
ret = NRF_ERROR_INVALID_PARAM;
break;
}
if (!(flags & NRF_DRV_TWI_FLAG_HOLD_XFER) && (p_xfer_desc->type != NRF_DRV_TWI_XFER_TXTX))
{
nrf_twim_task_trigger(p_twim, start_task);
}
if (p_cb->handler)
{
if (flags & NRF_DRV_TWI_FLAG_NO_XFER_EVT_HANDLER)
{
p_cb->int_mask = NRF_TWIM_INT_ERROR_MASK;
}
nrf_twim_int_enable(p_twim, p_cb->int_mask);
}
else
{
while (!nrf_twim_event_check(p_twim, evt_to_wait))
{
if (nrf_twim_event_check(p_twim, NRF_TWIM_EVENT_ERROR))
{
nrf_twim_event_clear(p_twim, NRF_TWIM_EVENT_ERROR);
nrf_twim_task_trigger(p_twim, NRF_TWIM_TASK_RESUME);
nrf_twim_task_trigger(p_twim, NRF_TWIM_TASK_STOP);
evt_to_wait = NRF_TWIM_EVENT_STOPPED;
}
}
uint32_t errorsrc = nrf_twim_errorsrc_get_and_clear(p_twim);
p_cb->busy = false;
if (errorsrc)
{
ret = NRF_ERROR_INTERNAL;
}
}
return ret;
}
#endif
ret_code_t nrf_drv_twi_xfer(nrf_drv_twi_t const * p_instance,
nrf_drv_twi_xfer_desc_t const * p_xfer_desc,
uint32_t flags)
{
ret_code_t ret = NRF_SUCCESS;
twi_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
// TXRX and TXTX transfers are support only in non-blocking mode.
ASSERT( !((p_cb->handler == NULL) && (p_xfer_desc->type == NRF_DRV_TWI_XFER_TXRX)));
ASSERT( !((p_cb->handler == NULL) && (p_xfer_desc->type == NRF_DRV_TWI_XFER_TXTX)));
CODE_FOR_TWIM
(
ret = twim_xfer(p_cb, (NRF_TWIM_Type *)p_instance->reg.p_twim, p_xfer_desc, flags);
)
CODE_FOR_TWI
(
if ( (NRF_DRV_TWI_FLAG_TX_POSTINC | NRF_DRV_TWI_FLAG_RX_POSTINC) & flags)
{
return NRF_ERROR_NOT_SUPPORTED;
}
ret = twi_xfer(p_cb, (NRF_TWI_Type *)p_instance->reg.p_twi, p_xfer_desc, flags);
)
return ret;
}
ret_code_t nrf_drv_twi_tx(nrf_drv_twi_t const * p_instance,
uint8_t address,
uint8_t const * p_data,
uint8_t length,
bool no_stop)
{
nrf_drv_twi_xfer_desc_t xfer = NRF_DRV_TWI_XFER_DESC_TX(address, (uint8_t*)p_data, length);
return nrf_drv_twi_xfer(p_instance, &xfer, no_stop ? NRF_DRV_TWI_FLAG_TX_NO_STOP : 0);
}
ret_code_t nrf_drv_twi_rx(nrf_drv_twi_t const * p_instance,
uint8_t address,
uint8_t * p_data,
uint8_t length)
{
nrf_drv_twi_xfer_desc_t xfer = NRF_DRV_TWI_XFER_DESC_RX(address, p_data, length);
return nrf_drv_twi_xfer(p_instance, &xfer, 0);
}
uint32_t nrf_drv_twi_data_count_get(nrf_drv_twi_t const * const p_instance)
{
CODE_FOR_TWIM
(
ASSERT(false);
return 0;
)
CODE_FOR_TWI
(
return m_cb[p_instance->drv_inst_idx].bytes_transferred;
)
}
uint32_t nrf_drv_twi_start_task_get(nrf_drv_twi_t const * p_instance, nrf_drv_twi_xfer_type_t xfer_type)
{
CODE_FOR_TWIM
(
return (uint32_t)nrf_twim_task_address_get(p_instance->reg.p_twim,
(xfer_type != NRF_DRV_TWI_XFER_RX) ? NRF_TWIM_TASK_STARTTX : NRF_TWIM_TASK_STARTRX);
)
CODE_FOR_TWI
(
return (uint32_t)nrf_twi_task_address_get(p_instance->reg.p_twi,
(xfer_type != NRF_DRV_TWI_XFER_RX) ? NRF_TWI_TASK_STARTTX : NRF_TWI_TASK_STARTRX);
)
}
uint32_t nrf_drv_twi_stopped_event_get(nrf_drv_twi_t const * p_instance)
{
CODE_FOR_TWIM
(
return (uint32_t)nrf_twim_event_address_get(p_instance->reg.p_twim, NRF_TWIM_EVENT_STOPPED);
)
CODE_FOR_TWI
(
return (uint32_t)nrf_twi_event_address_get(p_instance->reg.p_twi, NRF_TWI_EVENT_STOPPED);
)
}
#ifdef TWIM_IN_USE
static void irq_handler_twim(NRF_TWIM_Type * p_twim, twi_control_block_t * p_cb)
{
ASSERT(p_cb->handler);
if (nrf_twim_event_check(p_twim, NRF_TWIM_EVENT_ERROR))
{
nrf_twim_event_clear(p_twim, NRF_TWIM_EVENT_ERROR);
if (!nrf_twim_event_check(p_twim, NRF_TWIM_EVENT_STOPPED))
{
nrf_twim_int_disable(p_twim, p_cb->int_mask);
p_cb->int_mask = NRF_TWIM_INT_STOPPED_MASK;
nrf_twim_int_enable(p_twim, p_cb->int_mask);
nrf_twim_task_trigger(p_twim, NRF_TWIM_TASK_RESUME);
nrf_twim_task_trigger(p_twim, NRF_TWIM_TASK_STOP);
return;
}
}
nrf_drv_twi_evt_t event;
if (nrf_twim_event_check(p_twim, NRF_TWIM_EVENT_STOPPED))
{
nrf_twim_event_clear(p_twim, NRF_TWIM_EVENT_STOPPED);
event.xfer_desc = p_cb->xfer_desc;
if (p_cb->error)
{
event.xfer_desc.primary_length = (p_cb->xfer_desc.type == NRF_DRV_TWI_XFER_RX) ?
(uint8_t)nrf_twim_rxd_amount_get(p_twim) : (uint8_t)nrf_twim_txd_amount_get(p_twim);
event.xfer_desc.secondary_length = (p_cb->xfer_desc.type == NRF_DRV_TWI_XFER_TXRX) ?
(uint8_t)nrf_twim_rxd_amount_get(p_twim) : (uint8_t)nrf_twim_txd_amount_get(p_twim);
}
nrf_twim_event_clear(p_twim, NRF_TWIM_EVENT_LASTTX);
nrf_twim_event_clear(p_twim, NRF_TWIM_EVENT_LASTRX);
if (!p_cb->repeated || p_cb->error)
{
nrf_twim_shorts_set(p_twim, 0);
p_cb->int_mask = 0;
nrf_twim_int_disable(p_twim, DISABLE_ALL);
}
}
else
{
nrf_twim_event_clear(p_twim, NRF_TWIM_EVENT_SUSPENDED);
if (p_cb->xfer_desc.type == NRF_DRV_TWI_XFER_TX)
{
event.xfer_desc = p_cb->xfer_desc;
if (!p_cb->repeated)
{
nrf_twim_shorts_set(p_twim, 0);
p_cb->int_mask = 0;
nrf_twim_int_disable(p_twim, DISABLE_ALL);
}
}
else
{
nrf_twim_shorts_set(p_twim, NRF_TWIM_SHORT_LASTTX_STOP_MASK);
p_cb->int_mask = NRF_TWIM_INT_STOPPED_MASK | NRF_TWIM_INT_ERROR_MASK;
nrf_twim_int_disable(p_twim, DISABLE_ALL);
nrf_twim_int_enable(p_twim, p_cb->int_mask);
nrf_twim_task_trigger(p_twim, NRF_TWIM_TASK_STARTTX);
nrf_twim_task_trigger(p_twim, NRF_TWIM_TASK_RESUME);
return;
}
}
uint32_t errorsrc = nrf_twim_errorsrc_get_and_clear(p_twim);
if (errorsrc & NRF_TWIM_ERROR_ADDRESS_NACK)
{
event.type = NRF_DRV_TWI_EVT_ADDRESS_NACK;
}
else if (errorsrc & NRF_TWIM_ERROR_DATA_NACK)
{
event.type = NRF_DRV_TWI_EVT_DATA_NACK;
}
else
{
event.type = NRF_DRV_TWI_EVT_DONE;
}
if (!p_cb->repeated)
{
p_cb->busy = false;
}
p_cb->handler(&event, p_cb->p_context);
}
#endif // TWIM_IN_USE
#ifdef TWI_IN_USE
static void irq_handler_twi(NRF_TWI_Type * p_twi, twi_control_block_t * p_cb)
{
ASSERT(p_cb->handler);
if (twi_transfer(p_twi, &p_cb->error, &p_cb->bytes_transferred, p_cb->p_curr_buf, p_cb->curr_length, p_cb->curr_no_stop ))
{
return;
}
if (!p_cb->error &&
((p_cb->xfer_desc.type == NRF_DRV_TWI_XFER_TXRX) ||
(p_cb->xfer_desc.type == NRF_DRV_TWI_XFER_TXTX)) &&
p_cb->p_curr_buf == p_cb->xfer_desc.p_primary_buf)
{
p_cb->p_curr_buf = p_cb->xfer_desc.p_secondary_buf;
p_cb->curr_length = p_cb->xfer_desc.secondary_length;
p_cb->curr_no_stop = (p_cb->flags & NRF_DRV_TWI_FLAG_TX_NO_STOP);
if (p_cb->xfer_desc.type == NRF_DRV_TWI_XFER_TXTX)
{
(void)twi_tx_start_transfer(p_cb, p_twi, p_cb->p_curr_buf, p_cb->curr_length, p_cb->curr_no_stop);
}
else
{
(void)twi_rx_start_transfer(p_cb, p_twi, p_cb->p_curr_buf, p_cb->curr_length);
}
}
else
{
nrf_drv_twi_evt_t event;
event.xfer_desc = p_cb->xfer_desc;
if (p_cb->error)
{
uint32_t errorsrc = nrf_twi_errorsrc_get_and_clear(p_twi);
if (errorsrc & NRF_TWI_ERROR_ADDRESS_NACK)
{
event.type = NRF_DRV_TWI_EVT_ADDRESS_NACK;
}
else if (errorsrc & NRF_TWI_ERROR_DATA_NACK)
{
event.type = NRF_DRV_TWI_EVT_DATA_NACK;
}
}
else
{
event.type = NRF_DRV_TWI_EVT_DONE;
}
p_cb->busy = false;
if (!(NRF_DRV_TWI_FLAG_NO_XFER_EVT_HANDLER & p_cb->flags))
{
p_cb->handler(&event, p_cb->p_context);
}
}
}
#endif // TWI_IN_USE
#if TWI0_ENABLED
IRQ_HANDLER(0)
{
#if (TWI0_USE_EASY_DMA == 1) && defined(NRF52)
irq_handler_twim(NRF_TWIM0,
#else
irq_handler_twi(NRF_TWI0,
#endif
&m_cb[TWI0_INSTANCE_INDEX]);
}
#endif // TWI0_ENABLED
#if TWI1_ENABLED
IRQ_HANDLER(1)
{
#if (TWI1_USE_EASY_DMA == 1)
irq_handler_twim(NRF_TWIM1,
#else
irq_handler_twi(NRF_TWI1,
#endif
&m_cb[TWI1_INSTANCE_INDEX]);
}
#endif // TWI1_ENABLED

View File

@ -0,0 +1,384 @@
/* Copyright (c) 2015 Nordic Semiconductor. All Rights Reserved.
*
* The information contained herein is property of Nordic Semiconductor ASA.
* Terms and conditions of usage are described in detail in NORDIC
* SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT.
*
* Licensees are granted free, non-transferable use of the information. NO
* WARRANTY of ANY KIND is provided. This heading must NOT be removed from
* the file.
*
*/
/**
*
* @defgroup nrf_twi Two-wire interface (TWI)
* @ingroup nrf_drivers
* @brief Two-wire interface (TWI) APIs.
*
* @defgroup nrf_twi_master TWI master HAL and driver
* @ingroup nrf_twi
* @brief TWI master APIs.
* @details The TWI and TWIM HALs provide basic APIs for accessing the registers of the TWI and TWIM peripherals, respectively.
*
* The TWI master driver provides APIs on a higher level.
*
*/
#ifndef NRF_DRV_TWI_H__
#define NRF_DRV_TWI_H__
#include "nordic_common.h"
#include "nrf_drv_config.h"
// This set of macros makes it possible to exclude parts of code when one type
// of supported peripherals is not used.
#if ((TWI0_ENABLED == 1 && TWI0_USE_EASY_DMA == 1) || \
(TWI1_ENABLED == 1 && TWI1_USE_EASY_DMA == 1))
#define TWIM_IN_USE
#endif
#if ((TWI0_ENABLED == 1 && TWI0_USE_EASY_DMA != 1) || \
(TWI1_ENABLED == 1 && TWI1_USE_EASY_DMA != 1))
#define TWI_IN_USE
#endif
#include "nrf_twi.h"
#ifdef TWIM_IN_USE
#include "nrf_twim.h"
#endif
#include "sdk_errors.h"
#if defined(NRF52)
#define NRF_DRV_TWI_PERIPHERAL(id) \
(CONCAT_3(TWI, id, _USE_EASY_DMA) == 1 ? \
(void *)CONCAT_2(NRF_TWIM, id) \
: (void *)CONCAT_2(NRF_TWI, id))
#else
#define NRF_DRV_TWI_PERIPHERAL(id) (void *)CONCAT_2(NRF_TWI, id)
#endif
/**
* @defgroup nrf_drv_twi TWI master driver
* @{
* @ingroup nrf_twi_master
* @brief Multi-instance TWI master driver.
*/
/**
* @brief Structure for the TWI master driver instance.
*/
typedef struct
{
union
{
#ifdef TWIM_IN_USE
NRF_TWIM_Type * p_twim; ///< Pointer to a structure with TWIM registers.
#endif
NRF_TWI_Type * p_twi; ///< Pointer to a structure with TWI registers.
} reg;
uint8_t drv_inst_idx; ///< Driver instance index.
bool use_easy_dma; ///< True if the peripheral with EasyDMA (TWIM) shall be used.
} nrf_drv_twi_t;
/**
* @brief Macro for creating a TWI master driver instance.
*/
#define NRF_DRV_TWI_INSTANCE(id) \
{ \
.reg = {NRF_DRV_TWI_PERIPHERAL(id)}, \
.drv_inst_idx = CONCAT_3(TWI, id, _INSTANCE_INDEX), \
.use_easy_dma = CONCAT_3(TWI, id, _USE_EASY_DMA) \
}
/**
* @brief Structure for the TWI master driver instance configuration.
*/
typedef struct
{
uint32_t scl; ///< SCL pin number.
uint32_t sda; ///< SDA pin number.
nrf_twi_frequency_t frequency; ///< TWI frequency.
uint8_t interrupt_priority; ///< Interrupt priority.
} nrf_drv_twi_config_t;
/**
* @brief TWI master driver instance default configuration.
*/
#define NRF_DRV_TWI_DEFAULT_CONFIG(id) \
{ \
.frequency = CONCAT_3(TWI, id, _CONFIG_FREQUENCY), \
.scl = CONCAT_3(TWI, id, _CONFIG_SCL), \
.sda = CONCAT_3(TWI, id, _CONFIG_SDA), \
.interrupt_priority = CONCAT_3(TWI, id, _CONFIG_IRQ_PRIORITY) \
}
#define NRF_DRV_TWI_FLAG_TX_POSTINC (1UL << 0) /**< TX buffer address incremented after transfer. */
#define NRF_DRV_TWI_FLAG_RX_POSTINC (1UL << 1) /**< RX buffer address incremented after transfer. */
#define NRF_DRV_TWI_FLAG_NO_XFER_EVT_HANDLER (1UL << 2) /**< Interrupt after each transfer is suppressed, and the event handler is not called. */
#define NRF_DRV_TWI_FLAG_HOLD_XFER (1UL << 3) /**< Set up the transfer but do not start it. */
#define NRF_DRV_TWI_FLAG_REPEATED_XFER (1UL << 4) /**< Flag indicating that the transfer will be executed multiple times. */
#define NRF_DRV_TWI_FLAG_TX_NO_STOP (1UL << 5) /**< Flag indicating that the TX transfer will not end with a stop condition. */
/**
* @brief TWI master driver event types.
*/
typedef enum
{
NRF_DRV_TWI_EVT_DONE, ///< Transfer completed event.
NRF_DRV_TWI_EVT_ADDRESS_NACK, ///< Error event: NACK received after sending the address.
NRF_DRV_TWI_EVT_DATA_NACK ///< Error event: NACK received after sending a data byte.
} nrf_drv_twi_evt_type_t;
/**
* @brief TWI master driver transfer types.
*/
typedef enum
{
NRF_DRV_TWI_XFER_TX, ///< TX transfer.
NRF_DRV_TWI_XFER_RX, ///< RX transfer.
NRF_DRV_TWI_XFER_TXRX, ///< TX transfer followed by RX transfer with repeated start.
NRF_DRV_TWI_XFER_TXTX ///< TX transfer followed by TX transfer with repeated start.
} nrf_drv_twi_xfer_type_t;
/**
* @brief Structure for a TWI transfer descriptor.
*/
typedef struct
{
nrf_drv_twi_xfer_type_t type; ///< Type of transfer.
uint8_t address; ///< Slave address.
uint8_t primary_length; ///< Number of bytes transferred.
uint8_t secondary_length; ///< Number of bytes transferred.
uint8_t * p_primary_buf; ///< Pointer to transferred data.
uint8_t * p_secondary_buf; ///< Pointer to transferred data.
} nrf_drv_twi_xfer_desc_t;
/**@brief Macro for setting the TX transfer descriptor. */
#define NRF_DRV_TWI_XFER_DESC_TX(addr, p_data, length) \
{ \
.type = NRF_DRV_TWI_XFER_TX, \
.address = addr, \
.primary_length = length, \
.p_primary_buf = p_data, \
}
/**@brief Macro for setting the RX transfer descriptor. */
#define NRF_DRV_TWI_XFER_DESC_RX(addr, p_data, length) \
{ \
.type = NRF_DRV_TWI_XFER_RX, \
.address = addr, \
.primary_length = length, \
.p_primary_buf = p_data, \
}
/**@brief Macro for setting the TXRX transfer descriptor. */
#define NRF_DRV_TWI_XFER_DESC_TXRX(addr, p_tx, tx_len, p_rx, rx_len) \
{ \
.type = NRF_DRV_TWI_XFER_TXRX, \
.address = addr, \
.primary_length = tx_len, \
.secondary_length = rx_len, \
.p_primary_buf = p_tx, \
.p_secondary_buf = p_rx, \
}
/**@brief Macro for setting the TXTX transfer descriptor. */
#define NRF_DRV_TWI_XFER_DESC_TXTX(addr, p_tx, tx_len, p_tx2, tx_len2) \
{ \
.type = NRF_DRV_TWI_XFER_TXTX, \
.address = addr, \
.primary_length = tx_len, \
.secondary_length = tx_len2, \
.p_primary_buf = p_tx, \
.p_secondary_buf = p_tx2, \
}
/**
* @brief Structure for a TWI event.
*/
typedef struct
{
nrf_drv_twi_evt_type_t type; ///< Event type.
nrf_drv_twi_xfer_desc_t xfer_desc; ///< Transfer details.
} nrf_drv_twi_evt_t;
/**
* @brief TWI event handler prototype.
*/
typedef void (* nrf_drv_twi_evt_handler_t)(nrf_drv_twi_evt_t const * p_event,
void * p_context);
/**
* @brief Function for initializing the TWI instance.
*
* @param[in] p_instance TWI instance.
* @param[in] p_config Initial configuration. If NULL, the default configuration is used.
* @param[in] event_handler Event handler provided by the user. If NULL, blocking mode is enabled.
* @param[in] p_context Context passed to event handler.
*
* @retval NRF_SUCCESS If initialization was successful.
* @retval NRF_ERROR_INVALID_STATE If the driver is in invalid state.
* @retval NRF_ERROR_BUSY If some other peripheral with the same
* instance ID is already in use. This is
* possible only if PERIPHERAL_RESOURCE_SHARING_ENABLED
* is set to a value other than zero.
*/
ret_code_t nrf_drv_twi_init(nrf_drv_twi_t const * p_instance,
nrf_drv_twi_config_t const * p_config,
nrf_drv_twi_evt_handler_t event_handler,
void * p_context);
/**
* @brief Function for uninitializing the TWI instance.
*
* @param[in] p_instance TWI instance.
*/
void nrf_drv_twi_uninit(nrf_drv_twi_t const * p_instance);
/**
* @brief Function for enabling the TWI instance.
*
* @param[in] p_instance TWI instance.
*/
void nrf_drv_twi_enable(nrf_drv_twi_t const * p_instance);
/**
* @brief Function for disabling the TWI instance.
*
* @param[in] p_instance TWI instance.
*/
void nrf_drv_twi_disable(nrf_drv_twi_t const * p_instance);
/**
* @brief Function for sending data to a TWI slave.
*
* The transmission will be stopped when an error occurs. If a transfer is ongoing,
* the function returns the error code @ref NRF_ERROR_BUSY.
*
* @param[in] p_instance TWI instance.
* @param[in] address Address of a specific slave device (only 7 LSB).
* @param[in] p_data Pointer to a transmit buffer.
* @param[in] length Number of bytes to send.
* @param[in] no_stop If set, the stop condition is not generated on the bus
* after the transfer has completed successfully (allowing
* for a repeated start in the next transfer).
*
* @retval NRF_SUCCESS If the procedure was successful.
* @retval NRF_ERROR_BUSY If the driver is not ready for a new transfer.
* @retval NRF_ERROR_INTERNAL If an error was detected by hardware.
*/
ret_code_t nrf_drv_twi_tx(nrf_drv_twi_t const * p_instance,
uint8_t address,
uint8_t const * p_data,
uint8_t length,
bool no_stop);
/**
* @brief Function for reading data from a TWI slave.
*
* The transmission will be stopped when an error occurs. If a transfer is ongoing,
* the function returns the error code @ref NRF_ERROR_BUSY.
*
* @param[in] p_instance TWI instance.
* @param[in] address Address of a specific slave device (only 7 LSB).
* @param[in] p_data Pointer to a receive buffer.
* @param[in] length Number of bytes to be received.
*
* @retval NRF_SUCCESS If the procedure was successful.
* @retval NRF_ERROR_BUSY If the driver is not ready for a new transfer.
* @retval NRF_ERROR_INTERNAL If an error was detected by hardware.
*/
ret_code_t nrf_drv_twi_rx(nrf_drv_twi_t const * p_instance,
uint8_t address,
uint8_t * p_data,
uint8_t length);
/**
* @brief Function for preparing a TWI transfer.
*
* The following transfer types can be configured (@ref nrf_drv_twi_xfer_desc_t::type):
* - @ref NRF_DRV_TWI_XFER_TXRX<span></span>: Write operation followed by a read operation (without STOP condition in between).
* - @ref NRF_DRV_TWI_XFER_TXTX<span></span>: Write operation followed by a write operation (without STOP condition in between).
* - @ref NRF_DRV_TWI_XFER_TX<span></span>: Write operation (with or without STOP condition).
* - @ref NRF_DRV_TWI_XFER_RX<span></span>: Read operation (with STOP condition).
*
* Additional options are provided using the flags parameter:
* - @ref NRF_DRV_TWI_FLAG_TX_POSTINC and @ref NRF_DRV_TWI_FLAG_RX_POSTINC<span></span>: Post-incrementation of buffer addresses. Supported only by TWIM.
* - @ref NRF_DRV_TWI_FLAG_NO_XFER_EVT_HANDLER<span></span>: No user event handler after transfer completion. In most cases, this also means no interrupt at the end of the transfer.
* - @ref NRF_DRV_TWI_FLAG_HOLD_XFER<span></span>: Driver is not starting the transfer. Use this flag if the transfer is triggered externally by PPI. Supported only by TWIM.
* Use @ref nrf_drv_twi_start_task_get to get the address of the start task.
* - @ref NRF_DRV_TWI_FLAG_REPEATED_XFER<span></span>: Prepare for repeated transfers. You can set up a number of transfers that will be triggered externally (for example by PPI).
* An example is a TXRX transfer with the options @ref NRF_DRV_TWI_FLAG_RX_POSTINC, @ref NRF_DRV_TWI_FLAG_NO_XFER_EVT_HANDLER, and @ref NRF_DRV_TWI_FLAG_REPEATED_XFER.
* After the transfer is set up, a set of transfers can be triggered by PPI that will read, for example, the same register of an
* external component and put it into a RAM buffer without any interrupts. @ref nrf_drv_twi_stopped_event_get can be used to get the
* address of the STOPPED event, which can be used to count the number of transfers. If @ref NRF_DRV_TWI_FLAG_REPEATED_XFER is used,
* the driver does not set the instance into busy state, so you must ensure that the next transfers are set up
* when TWIM is not active. Supported only by TWIM.
* - @ref NRF_DRV_TWI_FLAG_TX_NO_STOP<span></span>: No stop condition after TX transfer.
*
* @note
* Some flag combinations are invalid:
* - @ref NRF_DRV_TWI_FLAG_TX_NO_STOP with @ref nrf_drv_twi_xfer_desc_t::type different than @ref NRF_DRV_TWI_XFER_TX
* - @ref NRF_DRV_TWI_FLAG_REPEATED_XFER with @ref nrf_drv_twi_xfer_desc_t::type set to @ref NRF_DRV_TWI_XFER_TXTX
*
* If @ref nrf_drv_twi_xfer_desc_t::type is set to @ref NRF_DRV_TWI_XFER_TX and the @ref NRF_DRV_TWI_FLAG_TX_NO_STOP and @ref NRF_DRV_TWI_FLAG_REPEATED_XFER
* flags are set, two tasks must be used to trigger a transfer: TASKS_RESUME followed by TASKS_STARTTX. If no stop condition is generated,
* TWIM is in SUSPENDED state. Therefore, it must be resumed before the transfer can be started.
*
* @note
* This function should be used only if the instance is configured to work in non-blocking mode. If the function is used in blocking mode, the driver asserts.
* @note If you are using this function with TWI, the only supported flag is @ref NRF_DRV_TWI_FLAG_TX_NO_STOP. All other flags require TWIM.
*
* @param[in] p_instance TWI instance.
* @param[in] p_xfer_desc Pointer to the transfer descriptor.
* @param[in] flags Transfer options (0 for default settings).
*
* @retval NRF_SUCCESS If the procedure was successful.
* @retval NRF_ERROR_BUSY If the driver is not ready for a new transfer.
* @retval NRF_ERROR_NOT_SUPPORTED If the provided parameters are not supported.
*/
ret_code_t nrf_drv_twi_xfer(nrf_drv_twi_t const * p_instance,
nrf_drv_twi_xfer_desc_t const * p_xfer_desc,
uint32_t flags);
/**
* @brief Function for getting the transferred data count.
*
* This function provides valid results only in legacy mode.
*
* @param[in] p_instance TWI instance.
*
* @return Data count.
*/
uint32_t nrf_drv_twi_data_count_get(nrf_drv_twi_t const * const p_instance);
/**
* @brief Function for returning the address of a TWI/TWIM start task.
*
* This function should be used if @ref nrf_drv_twi_xfer was called with the flag @ref NRF_DRV_TWI_FLAG_HOLD_XFER.
* In that case, the transfer is not started by the driver, but it must be started externally by PPI.
*
* @param[in] p_instance TWI instance.
* @param[in] xfer_type Transfer type used in the last call of the @ref nrf_drv_twi_xfer function.
*
* @return Start task address (TX or RX) depending on the value of xfer_type.
*/
uint32_t nrf_drv_twi_start_task_get(nrf_drv_twi_t const * p_instance, nrf_drv_twi_xfer_type_t xfer_type);
/**
* @brief Function for returning the address of a STOPPED TWI/TWIM event.
*
* A STOPPED event can be used to detect the end of a transfer if the @ref NRF_DRV_TWI_FLAG_NO_XFER_EVT_HANDLER
* option is used.
*
* @param[in] p_instance TWI instance.
*
* @return STOPPED event address.
*/
uint32_t nrf_drv_twi_stopped_event_get(nrf_drv_twi_t const * p_instance);
/**
*@}
**/
#endif // NRF_DRV_TWI_H__

View File

@ -0,0 +1,851 @@
/* Copyright (c) 2015 Nordic Semiconductor. All Rights Reserved.
*
* The information contained herein is property of Nordic Semiconductor ASA.
* Terms and conditions of usage are described in detail in NORDIC
* SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT.
*
* Licensees are granted free, non-transferable use of the information. NO
* WARRANTY of ANY KIND is provided. This heading must NOT be removed from
* the file.
*
*/
#include "nrf_drv_uart.h"
#include "nrf_assert.h"
#include "nordic_common.h"
#include "nrf_drv_common.h"
#include "nrf_gpio.h"
#include "app_util_platform.h"
// This set of macros makes it possible to exclude parts of code, when one type
// of supported peripherals is not used.
#ifdef NRF51
#define UART_IN_USE
#elif defined(NRF52)
#if (UART_EASY_DMA_SUPPORT == 1)
#define UARTE_IN_USE
#endif
#if (UART_LEGACY_SUPPORT == 1)
#define UART_IN_USE
#endif
#endif
#if (defined(UARTE_IN_USE) && defined(UART_IN_USE))
// UARTE and UART combined
#define CODE_FOR_UARTE(code) if (m_cb.use_easy_dma) { code }
#define CODE_FOR_UART(code) else { code }
#elif (defined(UARTE_IN_USE) && !defined(UART_IN_USE))
// UARTE only
#define CODE_FOR_UARTE(code) { code }
#define CODE_FOR_UART(code)
#elif (!defined(UARTE_IN_USE) && defined(UART_IN_USE))
// UART only
#define CODE_FOR_UARTE(code)
#define CODE_FOR_UART(code) { code }
#else
#error "Wrong configuration."
#endif
#ifndef IS_EASY_DMA_RAM_ADDRESS
#define IS_EASY_DMA_RAM_ADDRESS(addr) (((uint32_t)addr & 0xFFFF0000) == 0x20000000)
#endif
#define TX_COUNTER_ABORT_REQ_VALUE 256
typedef struct
{
void * p_context;
nrf_uart_event_handler_t handler;
uint8_t const * p_tx_buffer;
uint8_t * p_rx_buffer;
uint8_t * p_rx_secondary_buffer;
volatile uint16_t tx_counter;
uint8_t tx_buffer_length;
uint8_t rx_buffer_length;
uint8_t rx_secondary_buffer_length;
volatile uint8_t rx_counter;
bool rx_enabled;
nrf_drv_state_t state;
#if (defined(UARTE_IN_USE) && defined(UART_IN_USE))
bool use_easy_dma;
#endif
} uart_control_block_t;
static uart_control_block_t m_cb;
static const nrf_drv_uart_config_t m_default_config = NRF_DRV_UART_DEFAULT_CONFIG;
__STATIC_INLINE void apply_config(nrf_drv_uart_config_t const * p_config)
{
nrf_gpio_pin_set(p_config->pseltxd);
nrf_gpio_cfg_output(p_config->pseltxd);
nrf_gpio_cfg_input(p_config->pselrxd, NRF_GPIO_PIN_NOPULL);
CODE_FOR_UARTE
(
nrf_uarte_baudrate_set(NRF_UARTE0, (nrf_uarte_baudrate_t)p_config->baudrate);
nrf_uarte_configure(NRF_UARTE0, (nrf_uarte_parity_t)p_config->parity,
(nrf_uarte_hwfc_t)p_config->hwfc);
nrf_uarte_txrx_pins_set(NRF_UARTE0, p_config->pseltxd, p_config->pselrxd);
if (p_config->hwfc == NRF_UART_HWFC_ENABLED)
{
nrf_gpio_cfg_input(p_config->pselcts, NRF_GPIO_PIN_NOPULL);
nrf_gpio_pin_set(p_config->pselrts);
nrf_gpio_cfg_output(p_config->pselrts);
nrf_uarte_hwfc_pins_set(NRF_UARTE0, p_config->pselrts, p_config->pselcts);
}
)
CODE_FOR_UART
(
nrf_uart_baudrate_set(NRF_UART0, p_config->baudrate);
nrf_uart_configure(NRF_UART0, p_config->parity, p_config->hwfc);
nrf_uart_txrx_pins_set(NRF_UART0, p_config->pseltxd, p_config->pselrxd);
if (p_config->hwfc == NRF_UART_HWFC_ENABLED)
{
nrf_gpio_cfg_input(p_config->pselcts, NRF_GPIO_PIN_NOPULL);
nrf_gpio_pin_set(p_config->pselrts);
nrf_gpio_cfg_output(p_config->pselrts);
nrf_uart_hwfc_pins_set(NRF_UART0, p_config->pselrts, p_config->pselcts);
}
)
}
__STATIC_INLINE void interrupts_enable(uint8_t interrupt_priority)
{
CODE_FOR_UARTE
(
nrf_uarte_event_clear(NRF_UARTE0, NRF_UARTE_EVENT_ENDRX);
nrf_uarte_event_clear(NRF_UARTE0, NRF_UARTE_EVENT_ENDTX);
nrf_uarte_event_clear(NRF_UARTE0, NRF_UARTE_EVENT_ERROR);
nrf_uarte_event_clear(NRF_UARTE0, NRF_UARTE_EVENT_RXTO);
nrf_uarte_int_enable(NRF_UARTE0, NRF_UARTE_INT_ENDRX_MASK |
NRF_UARTE_INT_ENDTX_MASK |
NRF_UARTE_INT_ERROR_MASK |
NRF_UARTE_INT_RXTO_MASK);
)
CODE_FOR_UART
(
nrf_uart_event_clear(NRF_UART0, NRF_UART_EVENT_TXDRDY);
nrf_uart_event_clear(NRF_UART0, NRF_UART_EVENT_RXTO);
nrf_uart_int_enable(NRF_UART0, NRF_UART_INT_MASK_TXDRDY |
NRF_UART_INT_MASK_RXTO);
)
nrf_drv_common_irq_enable(UART0_IRQn, interrupt_priority);
}
__STATIC_INLINE void interrupts_disable(void)
{
CODE_FOR_UARTE
(
nrf_uarte_int_disable(NRF_UARTE0, NRF_UARTE_INT_ENDRX_MASK |
NRF_UARTE_INT_ENDTX_MASK |
NRF_UARTE_INT_ERROR_MASK |
NRF_UARTE_INT_RXTO_MASK);
)
CODE_FOR_UART
(
nrf_uart_int_disable(NRF_UART0, NRF_UART_INT_MASK_RXDRDY |
NRF_UART_INT_MASK_TXDRDY |
NRF_UART_INT_MASK_ERROR |
NRF_UART_INT_MASK_RXTO);
)
nrf_drv_common_irq_disable(UART0_IRQn);
}
__STATIC_INLINE void pins_to_default(void)
{
/* Reset pins to default states */
uint32_t txd;
uint32_t rxd;
uint32_t rts;
uint32_t cts;
CODE_FOR_UARTE
(
txd = nrf_uarte_tx_pin_get(NRF_UARTE0);
rxd = nrf_uarte_rx_pin_get(NRF_UARTE0);
rts = nrf_uarte_rts_pin_get(NRF_UARTE0);
cts = nrf_uarte_cts_pin_get(NRF_UARTE0);
nrf_uarte_txrx_pins_disconnect(NRF_UARTE0);
nrf_uarte_hwfc_pins_disconnect(NRF_UARTE0);
)
CODE_FOR_UART
(
txd = nrf_uart_tx_pin_get(NRF_UART0);
rxd = nrf_uart_rx_pin_get(NRF_UART0);
rts = nrf_uart_rts_pin_get(NRF_UART0);
cts = nrf_uart_cts_pin_get(NRF_UART0);
nrf_uart_txrx_pins_disconnect(NRF_UART0);
nrf_uart_hwfc_pins_disconnect(NRF_UART0);
)
nrf_gpio_cfg_default(txd);
nrf_gpio_cfg_default(rxd);
if (cts != NRF_UART_PSEL_DISCONNECTED)
{
nrf_gpio_cfg_default(cts);
}
if (rts != NRF_UART_PSEL_DISCONNECTED)
{
nrf_gpio_cfg_default(rts);
}
}
__STATIC_INLINE void uart_enable(void)
{
CODE_FOR_UARTE(nrf_uarte_enable(NRF_UARTE0);)
CODE_FOR_UART(nrf_uart_enable(NRF_UART0););
}
__STATIC_INLINE void uart_disable(void)
{
CODE_FOR_UARTE(nrf_uarte_disable(NRF_UARTE0);)
CODE_FOR_UART(nrf_uart_disable(NRF_UART0););
}
ret_code_t nrf_drv_uart_init(nrf_drv_uart_config_t const * p_config,
nrf_uart_event_handler_t event_handler)
{
if (m_cb.state != NRF_DRV_STATE_UNINITIALIZED)
{
return NRF_ERROR_INVALID_STATE;
}
if (p_config == NULL)
{
p_config = &m_default_config;
}
#if (defined(UARTE_IN_USE) && defined(UART_IN_USE))
m_cb.use_easy_dma = p_config->use_easy_dma;
#endif
apply_config(p_config);
m_cb.handler = event_handler;
m_cb.p_context = p_config->p_context;
if (m_cb.handler)
{
interrupts_enable(p_config->interrupt_priority);
}
uart_enable();
m_cb.rx_buffer_length = 0;
m_cb.rx_secondary_buffer_length = 0;
m_cb.tx_buffer_length = 0;
m_cb.state = NRF_DRV_STATE_INITIALIZED;
m_cb.rx_enabled = false;
return NRF_SUCCESS;
}
void nrf_drv_uart_uninit(void)
{
uart_disable();
if (m_cb.handler)
{
interrupts_disable();
}
pins_to_default();
m_cb.state = NRF_DRV_STATE_UNINITIALIZED;
m_cb.handler = NULL;
}
#if defined(UART_IN_USE)
__STATIC_INLINE void tx_byte(void)
{
nrf_uart_event_clear(NRF_UART0, NRF_UART_EVENT_TXDRDY);
uint8_t txd = m_cb.p_tx_buffer[m_cb.tx_counter];
m_cb.tx_counter++;
nrf_uart_txd_set(NRF_UART0, txd);
}
__STATIC_INLINE ret_code_t nrf_drv_uart_tx_for_uart()
{
ret_code_t err_code = NRF_SUCCESS;
nrf_uart_event_clear(NRF_UART0, NRF_UART_EVENT_TXDRDY);
nrf_uart_task_trigger(NRF_UART0, NRF_UART_TASK_STARTTX);
tx_byte();
if (m_cb.handler == NULL)
{
while (m_cb.tx_counter < (uint16_t) m_cb.tx_buffer_length)
{
while (!nrf_uart_event_check(NRF_UART0, NRF_UART_EVENT_TXDRDY) &&
m_cb.tx_counter != TX_COUNTER_ABORT_REQ_VALUE)
{
}
if (m_cb.tx_counter != TX_COUNTER_ABORT_REQ_VALUE)
{
tx_byte();
}
}
if (m_cb.tx_counter == TX_COUNTER_ABORT_REQ_VALUE)
{
err_code = NRF_ERROR_FORBIDDEN;
}
else
{
while (!nrf_uart_event_check(NRF_UART0, NRF_UART_EVENT_TXDRDY))
{
}
nrf_uart_task_trigger(NRF_UART0, NRF_UART_TASK_STOPTX);
}
m_cb.tx_buffer_length = 0;
}
return err_code;
}
#endif
#if defined(UARTE_IN_USE)
__STATIC_INLINE ret_code_t nrf_drv_uart_tx_for_uarte()
{
ret_code_t err_code = NRF_SUCCESS;
nrf_uarte_event_clear(NRF_UARTE0, NRF_UARTE_EVENT_ENDTX);
nrf_uarte_event_clear(NRF_UARTE0, NRF_UARTE_EVENT_TXSTOPPED);
nrf_uarte_tx_buffer_set(NRF_UARTE0, m_cb.p_tx_buffer, m_cb.tx_buffer_length);
nrf_uarte_task_trigger(NRF_UARTE0, NRF_UARTE_TASK_STARTTX);
if (m_cb.handler == NULL)
{
bool endtx;
bool txstopped;
do
{
endtx = nrf_uarte_event_check(NRF_UARTE0, NRF_UARTE_EVENT_ENDTX);
txstopped = nrf_uarte_event_check(NRF_UARTE0, NRF_UARTE_EVENT_TXSTOPPED);
}
while ((!endtx) && (!txstopped));
if (txstopped)
{
err_code = NRF_ERROR_FORBIDDEN;
}
m_cb.tx_buffer_length = 0;
}
return err_code;
}
#endif
ret_code_t nrf_drv_uart_tx(uint8_t const * const p_data, uint8_t length)
{
ASSERT(m_cb.state == NRF_DRV_STATE_INITIALIZED);
ASSERT(length>0);
ASSERT(p_data);
CODE_FOR_UARTE
(
// EasyDMA requires that transfer buffers are placed in DataRAM,
// signal error if the are not.
if (!IS_EASY_DMA_RAM_ADDRESS(p_data))
{
return NRF_ERROR_INVALID_ADDR;
}
)
if (nrf_drv_uart_tx_in_progress())
{
return NRF_ERROR_BUSY;
}
m_cb.tx_buffer_length = length;
m_cb.p_tx_buffer = p_data;
m_cb.tx_counter = 0;
CODE_FOR_UARTE
(
return nrf_drv_uart_tx_for_uarte();
)
CODE_FOR_UART
(
return nrf_drv_uart_tx_for_uart();
)
}
bool nrf_drv_uart_tx_in_progress(void)
{
return (m_cb.tx_buffer_length != 0);
}
#if defined(UART_IN_USE)
__STATIC_INLINE void rx_enable(void)
{
nrf_uart_event_clear(NRF_UART0, NRF_UART_EVENT_ERROR);
nrf_uart_event_clear(NRF_UART0, NRF_UART_EVENT_RXDRDY);
nrf_uart_task_trigger(NRF_UART0, NRF_UART_TASK_STARTRX);
}
__STATIC_INLINE void rx_byte(void)
{
if (!m_cb.rx_buffer_length)
{
nrf_uart_event_clear(NRF_UART0, NRF_UART_EVENT_RXDRDY);
// Byte received when buffer is not set - data lost.
(void) nrf_uart_rxd_get(NRF_UART0);
return;
}
nrf_uart_event_clear(NRF_UART0, NRF_UART_EVENT_RXDRDY);
m_cb.p_rx_buffer[m_cb.rx_counter] = nrf_uart_rxd_get(NRF_UART0);
m_cb.rx_counter++;
}
__STATIC_INLINE ret_code_t nrf_drv_uart_rx_for_uart(uint8_t * p_data, uint8_t length, bool second_buffer)
{
if ((!m_cb.rx_enabled) && (!second_buffer))
{
rx_enable();
}
if (m_cb.handler == NULL)
{
nrf_uart_event_clear(NRF_UART0, NRF_UART_EVENT_RXTO);
bool rxrdy;
bool rxto;
bool error;
do
{
do
{
error = nrf_uart_event_check(NRF_UART0, NRF_UART_EVENT_ERROR);
rxrdy = nrf_uart_event_check(NRF_UART0, NRF_UART_EVENT_RXDRDY);
rxto = nrf_uart_event_check(NRF_UART0, NRF_UART_EVENT_RXTO);
} while ((!rxrdy) && (!rxto) && (!error));
if (error || rxto)
{
break;
}
rx_byte();
} while (m_cb.rx_buffer_length > m_cb.rx_counter);
m_cb.rx_buffer_length = 0;
if (error)
{
return NRF_ERROR_INTERNAL;
}
if (rxto)
{
return NRF_ERROR_FORBIDDEN;
}
if (m_cb.rx_enabled)
{
nrf_uart_task_trigger(NRF_UART0, NRF_UART_TASK_STARTRX);
}
else
{
// Skip stopping RX if driver is forced to be enabled.
nrf_uart_task_trigger(NRF_UART0, NRF_UART_TASK_STOPRX);
}
}
else
{
nrf_uart_int_enable(NRF_UART0, NRF_UART_INT_MASK_RXDRDY | NRF_UART_INT_MASK_ERROR);
}
return NRF_SUCCESS;
}
#endif
#if defined(UARTE_IN_USE)
__STATIC_INLINE ret_code_t nrf_drv_uart_rx_for_uarte(uint8_t * p_data, uint8_t length, bool second_buffer)
{
nrf_uarte_event_clear(NRF_UARTE0, NRF_UARTE_EVENT_ENDRX);
nrf_uarte_event_clear(NRF_UARTE0, NRF_UARTE_EVENT_RXTO);
nrf_uarte_rx_buffer_set(NRF_UARTE0, p_data, length);
if (!second_buffer)
{
nrf_uarte_task_trigger(NRF_UARTE0, NRF_UARTE_TASK_STARTRX);
}
else
{
nrf_uarte_shorts_enable(NRF_UARTE0, NRF_UARTE_SHORT_ENDRX_STARTRX);
}
if (m_cb.handler == NULL)
{
bool endrx;
bool rxto;
bool error;
do {
endrx = nrf_uarte_event_check(NRF_UARTE0, NRF_UARTE_EVENT_ENDRX);
rxto = nrf_uarte_event_check(NRF_UARTE0, NRF_UARTE_EVENT_RXTO);
error = nrf_uarte_event_check(NRF_UARTE0, NRF_UARTE_EVENT_ERROR);
}while ((!endrx) && (!rxto) && (!error));
m_cb.rx_buffer_length = 0;
if (error)
{
return NRF_ERROR_INTERNAL;
}
if (rxto)
{
return NRF_ERROR_FORBIDDEN;
}
}
else
{
nrf_uarte_int_enable(NRF_UARTE0, NRF_UARTE_INT_ERROR_MASK | NRF_UARTE_INT_ENDRX_MASK);
}
return NRF_SUCCESS;
}
#endif
ret_code_t nrf_drv_uart_rx(uint8_t * p_data, uint8_t length)
{
ASSERT(m_cb.state == NRF_DRV_STATE_INITIALIZED);
ASSERT(length>0);
CODE_FOR_UARTE
(
// EasyDMA requires that transfer buffers are placed in DataRAM,
// signal error if the are not.
if (!IS_EASY_DMA_RAM_ADDRESS(p_data))
{
return NRF_ERROR_INVALID_ADDR;
}
)
bool second_buffer = false;
if (m_cb.handler)
{
CODE_FOR_UARTE
(
nrf_uarte_int_disable(NRF_UARTE0, NRF_UARTE_INT_ERROR_MASK | NRF_UARTE_INT_ENDRX_MASK);
)
CODE_FOR_UART
(
nrf_uart_int_disable(NRF_UART0, NRF_UART_INT_MASK_RXDRDY | NRF_UART_INT_MASK_ERROR);
)
}
if (m_cb.rx_buffer_length != 0)
{
if (m_cb.rx_secondary_buffer_length != 0)
{
if (m_cb.handler)
{
CODE_FOR_UARTE
(
nrf_uarte_int_enable(NRF_UARTE0, NRF_UARTE_INT_ERROR_MASK | NRF_UARTE_INT_ENDRX_MASK);
)
CODE_FOR_UART
(
nrf_uart_int_enable(NRF_UART0, NRF_UART_INT_MASK_RXDRDY | NRF_UART_INT_MASK_ERROR);
)
}
return NRF_ERROR_BUSY;
}
second_buffer = true;
}
if (!second_buffer)
{
m_cb.rx_buffer_length = length;
m_cb.p_rx_buffer = p_data;
m_cb.rx_counter = 0;
m_cb.rx_secondary_buffer_length = 0;
}
else
{
m_cb.p_rx_secondary_buffer = p_data;
m_cb.rx_secondary_buffer_length = length;
}
CODE_FOR_UARTE
(
return nrf_drv_uart_rx_for_uarte(p_data, length, second_buffer);
)
CODE_FOR_UART
(
return nrf_drv_uart_rx_for_uart(p_data, length, second_buffer);
)
}
void nrf_drv_uart_rx_enable(void)
{
//Easy dma mode does not support enabling receiver without setting up buffer.
CODE_FOR_UARTE
(
ASSERT(false);
)
CODE_FOR_UART
(
if (!m_cb.rx_enabled)
{
rx_enable();
m_cb.rx_enabled = true;
}
)
}
void nrf_drv_uart_rx_disable(void)
{
//Easy dma mode does not support enabling receiver without setting up buffer.
CODE_FOR_UARTE
(
ASSERT(false);
)
CODE_FOR_UART
(
nrf_uart_task_trigger(NRF_UART0, NRF_UART_TASK_STOPRX);
m_cb.rx_enabled = false;
)
}
uint32_t nrf_drv_uart_errorsrc_get(void)
{
uint32_t errsrc;
CODE_FOR_UARTE
(
nrf_uarte_event_clear(NRF_UARTE0, NRF_UARTE_EVENT_ERROR);
errsrc = nrf_uarte_errorsrc_get_and_clear(NRF_UARTE0);
)
CODE_FOR_UART
(
nrf_uart_event_clear(NRF_UART0, NRF_UART_EVENT_ERROR);
errsrc = nrf_uart_errorsrc_get_and_clear(NRF_UART0);
)
return errsrc;
}
__STATIC_INLINE void rx_done_event(uint8_t bytes, uint8_t * p_data)
{
nrf_drv_uart_event_t event;
event.type = NRF_DRV_UART_EVT_RX_DONE;
event.data.rxtx.bytes = bytes;
event.data.rxtx.p_data = p_data;
m_cb.handler(&event,m_cb.p_context);
}
__STATIC_INLINE void tx_done_event(uint8_t bytes)
{
nrf_drv_uart_event_t event;
event.type = NRF_DRV_UART_EVT_TX_DONE;
event.data.rxtx.bytes = bytes;
event.data.rxtx.p_data = (uint8_t *)m_cb.p_tx_buffer;
m_cb.tx_buffer_length = 0;
m_cb.handler(&event,m_cb.p_context);
}
void nrf_drv_uart_tx_abort(void)
{
CODE_FOR_UARTE
(
nrf_uarte_event_clear(NRF_UARTE0, NRF_UARTE_EVENT_TXSTOPPED);
nrf_uarte_task_trigger(NRF_UARTE0, NRF_UARTE_TASK_STOPTX);
if (m_cb.handler == NULL)
{
while(!nrf_uarte_event_check(NRF_UARTE0, NRF_UARTE_EVENT_TXSTOPPED));
}
)
CODE_FOR_UART
(
nrf_uart_task_trigger(NRF_UART0, NRF_UART_TASK_STOPTX);
if (m_cb.handler)
{
tx_done_event(m_cb.tx_counter);
}
else
{
m_cb.tx_counter = TX_COUNTER_ABORT_REQ_VALUE;
}
)
}
void nrf_drv_uart_rx_abort(void)
{
CODE_FOR_UARTE
(
nrf_uarte_task_trigger(NRF_UARTE0, NRF_UARTE_TASK_STOPRX);
)
CODE_FOR_UART
(
nrf_uart_int_disable(NRF_UART0, NRF_UART_INT_MASK_RXDRDY | NRF_UART_INT_MASK_ERROR);
nrf_uart_task_trigger(NRF_UART0, NRF_UART_TASK_STOPRX);
)
}
#if defined(UART_IN_USE)
__STATIC_INLINE void uart_irq_handler()
{
if (nrf_uart_int_enable_check(NRF_UART0, NRF_UART_INT_MASK_ERROR) &&
nrf_uart_event_check(NRF_UART0, NRF_UART_EVENT_ERROR))
{
nrf_drv_uart_event_t event;
nrf_uart_event_clear(NRF_UART0, NRF_UART_EVENT_ERROR);
nrf_uart_int_disable(NRF_UART0, NRF_UART_INT_MASK_RXDRDY | NRF_UART_INT_MASK_ERROR);
if (!m_cb.rx_enabled)
{
nrf_uart_task_trigger(NRF_UART0, NRF_UART_TASK_STOPRX);
}
event.type = NRF_DRV_UART_EVT_ERROR;
event.data.error.error_mask = nrf_uart_errorsrc_get_and_clear(NRF_UART0);
event.data.error.rxtx.bytes = m_cb.rx_buffer_length;
event.data.error.rxtx.p_data = m_cb.p_rx_buffer;
//abort transfer
m_cb.rx_buffer_length = 0;
m_cb.rx_secondary_buffer_length = 0;
m_cb.handler(&event,m_cb.p_context);
}
else if (nrf_uart_int_enable_check(NRF_UART0, NRF_UART_INT_MASK_RXDRDY) &&
nrf_uart_event_check(NRF_UART0, NRF_UART_EVENT_RXDRDY))
{
rx_byte();
if (m_cb.rx_buffer_length == m_cb.rx_counter)
{
if (m_cb.rx_secondary_buffer_length)
{
uint8_t * p_data = m_cb.p_rx_buffer;
uint8_t rx_counter = m_cb.rx_counter;
//Switch to secondary buffer.
m_cb.rx_buffer_length = m_cb.rx_secondary_buffer_length;
m_cb.p_rx_buffer = m_cb.p_rx_secondary_buffer;
m_cb.rx_secondary_buffer_length = 0;
m_cb.rx_counter = 0;
rx_done_event(rx_counter, p_data);
}
else
{
if (!m_cb.rx_enabled)
{
nrf_uart_task_trigger(NRF_UART0, NRF_UART_TASK_STOPRX);
}
nrf_uart_int_disable(NRF_UART0, NRF_UART_INT_MASK_RXDRDY | NRF_UART_INT_MASK_ERROR);
m_cb.rx_buffer_length = 0;
rx_done_event(m_cb.rx_counter, m_cb.p_rx_buffer);
}
}
}
if (nrf_uart_event_check(NRF_UART0, NRF_UART_EVENT_TXDRDY))
{
if (m_cb.tx_counter < (uint16_t) m_cb.tx_buffer_length)
{
tx_byte();
}
else
{
nrf_uart_event_clear(NRF_UART0, NRF_UART_EVENT_TXDRDY);
if (m_cb.tx_buffer_length)
{
tx_done_event(m_cb.tx_buffer_length);
}
}
}
if (nrf_uart_event_check(NRF_UART0, NRF_UART_EVENT_RXTO))
{
nrf_uart_event_clear(NRF_UART0, NRF_UART_EVENT_RXTO);
// RXTO event may be triggered as a result of abort call. In th
if (m_cb.rx_enabled)
{
nrf_uart_task_trigger(NRF_UART0, NRF_UART_TASK_STARTRX);
}
if (m_cb.rx_buffer_length)
{
m_cb.rx_buffer_length = 0;
rx_done_event(m_cb.rx_counter, m_cb.p_rx_buffer);
}
}
}
#endif
#if defined(UARTE_IN_USE)
__STATIC_INLINE void uarte_irq_handler()
{
if (nrf_uarte_event_check(NRF_UARTE0, NRF_UARTE_EVENT_ERROR))
{
nrf_drv_uart_event_t event;
nrf_uarte_event_clear(NRF_UARTE0, NRF_UARTE_EVENT_ERROR);
event.type = NRF_DRV_UART_EVT_ERROR;
event.data.error.error_mask = nrf_uarte_errorsrc_get_and_clear(NRF_UARTE0);
event.data.error.rxtx.bytes = nrf_uarte_rx_amount_get(NRF_UARTE0);
event.data.error.rxtx.p_data = m_cb.p_rx_buffer;
//abort transfer
m_cb.rx_buffer_length = 0;
m_cb.rx_secondary_buffer_length = 0;
m_cb.handler(&event,m_cb.p_context);
}
else if (nrf_uarte_event_check(NRF_UARTE0, NRF_UARTE_EVENT_ENDRX))
{
nrf_uarte_event_clear(NRF_UARTE0, NRF_UARTE_EVENT_ENDRX);
uint8_t amount = nrf_uarte_rx_amount_get(NRF_UARTE0);
// If the transfer was stopped before completion, amount of transfered bytes
// will not be equal to the buffer length. Interrupted trunsfer is ignored.
if (amount == m_cb.rx_buffer_length)
{
if (m_cb.rx_secondary_buffer_length)
{
uint8_t * p_data = m_cb.p_rx_buffer;
nrf_uarte_shorts_disable(NRF_UARTE0, NRF_UARTE_SHORT_ENDRX_STARTRX);
m_cb.rx_buffer_length = m_cb.rx_secondary_buffer_length;
m_cb.p_rx_buffer = m_cb.p_rx_secondary_buffer;
m_cb.rx_secondary_buffer_length = 0;
rx_done_event(amount, p_data);
}
else
{
m_cb.rx_buffer_length = 0;
rx_done_event(amount, m_cb.p_rx_buffer);
}
}
}
if (nrf_uarte_event_check(NRF_UARTE0, NRF_UARTE_EVENT_RXTO))
{
nrf_uarte_event_clear(NRF_UARTE0, NRF_UARTE_EVENT_RXTO);
if (m_cb.rx_buffer_length)
{
m_cb.rx_buffer_length = 0;
rx_done_event(nrf_uarte_rx_amount_get(NRF_UARTE0), m_cb.p_rx_buffer);
}
}
if (nrf_uarte_event_check(NRF_UARTE0, NRF_UARTE_EVENT_ENDTX))
{
nrf_uarte_event_clear(NRF_UARTE0, NRF_UARTE_EVENT_ENDTX);
if (m_cb.tx_buffer_length)
{
tx_done_event(nrf_uarte_tx_amount_get(NRF_UARTE0));
}
}
}
#endif
void UART0_IRQHandler(void)
{
CODE_FOR_UARTE
(
uarte_irq_handler();
)
CODE_FOR_UART
(
uart_irq_handler();
)
}

View File

@ -0,0 +1,293 @@
/* Copyright (c) 2015 Nordic Semiconductor. All Rights Reserved.
*
* The information contained herein is property of Nordic Semiconductor ASA.
* Terms and conditions of usage are described in detail in NORDIC
* SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT.
*
* Licensees are granted free, non-transferable use of the information. NO
* WARRANTY of ANY KIND is provided. This heading must NOT be removed from
* the file.
*
*/
/**@file
* @addtogroup nrf_uart UART driver and HAL
* @ingroup nrf_drivers
* @brief UART API.
* @details The UART driver provides APIs for utilizing the UART peripheral.
*
* @defgroup nrf_drv_uart UART driver
* @{
* @ingroup nrf_uart
*
* @brief UART driver.
*/
#ifndef NRF_DRV_UART_H
#define NRF_DRV_UART_H
#include "nrf_uart.h"
#ifdef NRF52
#include "nrf_uarte.h"
#endif
#include "sdk_errors.h"
#include "nrf_drv_config.h"
/**
* @brief Types of UART driver events.
*/
typedef enum
{
NRF_DRV_UART_EVT_TX_DONE, ///< Requested TX transfer completed.
NRF_DRV_UART_EVT_RX_DONE, ///< Requested RX transfer completed.
NRF_DRV_UART_EVT_ERROR, ///< Error reported by UART peripheral.
} nrf_drv_uart_evt_type_t;
/**@brief Structure for UART configuration. */
typedef struct
{
uint32_t pseltxd; ///< TXD pin number.
uint32_t pselrxd; ///< RXD pin number.
uint32_t pselcts; ///< CTS pin number.
uint32_t pselrts; ///< RTS pin number.
void * p_context; ///< Context passed to interrupt handler.
nrf_uart_hwfc_t hwfc; ///< Flow control configuration.
nrf_uart_parity_t parity; ///< Parity configuration.
nrf_uart_baudrate_t baudrate; ///< Baudrate.
uint8_t interrupt_priority; ///< Interrupt priority.
#ifdef NRF52
bool use_easy_dma;
#endif
} nrf_drv_uart_config_t;
/**@brief UART default configuration. */
#ifdef NRF52
#if !UART_LEGACY_SUPPORT
#define DEFAULT_CONFIG_USE_EASY_DMA true
#elif !UART_EASY_DMA_SUPPORT
#define DEFAULT_CONFIG_USE_EASY_DMA false
#else
#define DEFAULT_CONFIG_USE_EASY_DMA UART0_CONFIG_USE_EASY_DMA
#endif
#define NRF_DRV_UART_DEFAULT_CONFIG \
{ \
.pseltxd = UART0_CONFIG_PSEL_TXD, \
.pselrxd = UART0_CONFIG_PSEL_RXD, \
.pselcts = UART0_CONFIG_PSEL_CTS, \
.pselrts = UART0_CONFIG_PSEL_RTS, \
.p_context = NULL, \
.hwfc = UART0_CONFIG_HWFC, \
.parity = UART0_CONFIG_PARITY, \
.baudrate = UART0_CONFIG_BAUDRATE, \
.interrupt_priority = UART0_CONFIG_IRQ_PRIORITY, \
.use_easy_dma = DEFAULT_CONFIG_USE_EASY_DMA \
}
#else
#define NRF_DRV_UART_DEFAULT_CONFIG \
{ \
.pseltxd = UART0_CONFIG_PSEL_TXD, \
.pselrxd = UART0_CONFIG_PSEL_RXD, \
.pselcts = UART0_CONFIG_PSEL_CTS, \
.pselrts = UART0_CONFIG_PSEL_RTS, \
.p_context = NULL, \
.hwfc = UART0_CONFIG_HWFC, \
.parity = UART0_CONFIG_PARITY, \
.baudrate = UART0_CONFIG_BAUDRATE, \
.interrupt_priority = UART0_CONFIG_IRQ_PRIORITY \
}
#endif
/**@brief Structure for UART transfer completion event. */
typedef struct
{
uint8_t * p_data; ///< Pointer to memory used for transfer.
uint8_t bytes; ///< Number of bytes transfered.
} nrf_drv_uart_xfer_evt_t;
/**@brief Structure for UART error event. */
typedef struct
{
nrf_drv_uart_xfer_evt_t rxtx; ///< Transfer details includes number of bytes transfered.
uint32_t error_mask;///< Mask of error flags that generated the event.
} nrf_drv_uart_error_evt_t;
/**@brief Structure for UART event. */
typedef struct
{
nrf_drv_uart_evt_type_t type; ///< Event type.
union
{
nrf_drv_uart_xfer_evt_t rxtx; ///< Data provided for transfer completion events.
nrf_drv_uart_error_evt_t error;///< Data provided for error event.
} data;
} nrf_drv_uart_event_t;
/**
* @brief UART interrupt event handler.
*
* @param[in] p_event Pointer to event structure. Event is allocated on the stack so it is available
* only within the context of the event handler.
* @param[in] p_context Context passed to interrupt handler, set on initialization.
*/
typedef void (*nrf_uart_event_handler_t)(nrf_drv_uart_event_t * p_event, void * p_context);
/**
* @brief Function for initializing the UART driver.
*
* This function configures and enables UART. After this function GPIO pins are controlled by UART.
*
* @param[in] p_config Initial configuration. Default configuration used if NULL.
* @param[in] event_handler Event handler provided by the user. If not provided driver works in
* blocking mode.
*
* @retval NRF_SUCCESS If initialization was successful.
* @retval NRF_ERROR_INVALID_STATE If driver is already initialized.
*/
ret_code_t nrf_drv_uart_init(nrf_drv_uart_config_t const * p_config,
nrf_uart_event_handler_t event_handler);
/**
* @brief Function for uninitializing the UART driver.
*/
void nrf_drv_uart_uninit(void);
/**
* @brief Function for getting the address of a specific UART task.
*
* @param[in] task Task.
*
* @return Task address.
*/
__STATIC_INLINE uint32_t nrf_drv_uart_task_address_get(nrf_uart_task_t task);
/**
* @brief Function for getting the address of a specific UART event.
*
* @param[in] event Event.
*
* @return Event address.
*/
__STATIC_INLINE uint32_t nrf_drv_uart_event_address_get(nrf_uart_event_t event);
/**
* @brief Function for sending data over UART.
*
* If an event handler was provided in nrf_drv_uart_init() call, this function
* returns immediately and the handler is called when the transfer is done.
* Otherwise, the transfer is performed in blocking mode, i.e. this function
* returns when the transfer is finished. Blocking mode is not using interrupt so
* there is no context switching inside the function.
*
* @note Peripherals using EasyDMA (i.e. UARTE) require that the transfer buffers
* are placed in the Data RAM region. If they are not and UARTE instance is
* used, this function will fail with error code NRF_ERROR_INVALID_ADDR.
*
* @param[in] p_data Pointer to data.
* @param[in] length Number of bytes to send.
*
* @retval NRF_SUCCESS If initialization was successful.
* @retval NRF_ERROR_BUSY If driver is already transferring.
* @retval NRF_ERROR_FORBIDDEN If the transfer was aborted from a different context
* (blocking mode only, also see @ref nrf_drv_uart_rx_disable).
* @retval NRF_ERROR_INVALID_ADDR If p_data does not point to RAM buffer (UARTE only).
*/
ret_code_t nrf_drv_uart_tx(uint8_t const * const p_data, uint8_t length);
/**
* @brief Function for checking if UART is currently transmitting.
*
* @retval true If UART is transmitting.
* @retval false If UART is not transmitting.
*/
bool nrf_drv_uart_tx_in_progress(void);
/**
* @brief Function for aborting any ongoing transmission.
* @note @ref NRF_DRV_UART_EVT_TX_DONE event will be generated in non-blocking mode. Event will
* contain number of bytes sent until abort was called. If Easy DMA is not used event will be
* called from the function context. If Easy DMA is used it will be called from UART interrupt
* context.
*/
void nrf_drv_uart_tx_abort(void);
/**
* @brief Function for receiving data over UART.
*
* If an event handler was provided in the nrf_drv_uart_init() call, this function
* returns immediately and the handler is called when the transfer is done.
* Otherwise, the transfer is performed in blocking mode, i.e. this function
* returns when the transfer is finished. Blocking mode is not using interrupt so
* there is no context switching inside the function.
* The receive buffer pointer is double buffered in non-blocking mode. The secondary
* buffer can be set immediately after starting the transfer and will be filled
* when the primary buffer is full. The double buffering feature allows
* receiving data continuously.
*
* @note Peripherals using EasyDMA (i.e. UARTE) require that the transfer buffers
* are placed in the Data RAM region. If they are not and UARTE instance is
* used, this function will fail with error code NRF_ERROR_INVALID_ADDR.
* @param[in] p_data Pointer to data.
* @param[in] length Number of bytes to receive.
*
* @retval NRF_SUCCESS If initialization was successful.
* @retval NRF_ERROR_BUSY If the driver is already receiving
* (and the secondary buffer has already been set
* in non-blocking mode).
* @retval NRF_ERROR_FORBIDDEN If the transfer was aborted from a different context
* (blocking mode only, also see @ref nrf_drv_uart_rx_disable).
* @retval NRF_ERROR_INTERNAL If UART peripheral reported an error.
* @retval NRF_ERROR_INVALID_ADDR If p_data does not point to RAM buffer (UARTE only).
*/
ret_code_t nrf_drv_uart_rx(uint8_t * p_data, uint8_t length);
/**
* @brief Function for enabling receiver.
*
* UART has 6 byte long RX FIFO and it will be used to store incoming data. If user will not call
* UART receive function before FIFO is filled, overrun error will encounter. Enabling receiver
* without specifying RX buffer is supported only in UART mode (without Easy DMA). Receiver must be
* explicitly closed by the user @sa nrf_drv_uart_rx_disable. Function asserts if mode is wrong.
*/
void nrf_drv_uart_rx_enable(void);
/**
* @brief Function for disabling receiver.
*
* Function must be called to close the receiver after it has been explicitly enabled by
* @sa nrf_drv_uart_rx_enable. Feature is supported only in UART mode (without Easy DMA). Function
* asserts if mode is wrong.
*/
void nrf_drv_uart_rx_disable(void);
/**
* @brief Function for aborting any ongoing reception.
* @note @ref NRF_DRV_UART_EVT_RX_DONE event will be generated in non-blocking mode. Event will
* contain number of bytes received until abort was called. If Easy DMA is not used event will be
* called from the function context. If Easy DMA is used it will be called from UART interrupt
* context.
*/
void nrf_drv_uart_rx_abort(void);
/**
* @brief Function for reading error source mask. Mask contains values from @ref nrf_uart_error_mask_t.
* @note Function should be used in blocking mode only. In case of non-blocking mode error event is
* generated. Function clears error sources after reading.
*
* @retval Mask of reported errors.
*/
uint32_t nrf_drv_uart_errorsrc_get(void);
#ifndef SUPPRESS_INLINE_IMPLEMENTATION
__STATIC_INLINE uint32_t nrf_drv_uart_task_address_get(nrf_uart_task_t task)
{
return nrf_uart_task_address_get(NRF_UART0, task);
}
__STATIC_INLINE uint32_t nrf_drv_uart_event_address_get(nrf_uart_event_t event)
{
return nrf_uart_event_address_get(NRF_UART0, event);
}
#endif //SUPPRESS_INLINE_IMPLEMENTATION
#endif //NRF_DRV_UART_H
/** @} */

View File

@ -0,0 +1,876 @@
/* Copyright (c) 2015 Nordic Semiconductor. All Rights Reserved.
*
* The information contained herein is property of Nordic Semiconductor ASA.
* Terms and conditions of usage are described in detail in NORDIC
* SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT.
*
* Licensees are granted free, non-transferable use of the information. NO
* WARRANTY of ANY KIND is provided. This heading must NOT be removed from
* the file.
*
*/
#include "app_pwm.h"
#include "nrf_drv_timer.h"
#include "nrf_drv_ppi.h"
#include "nrf_drv_common.h"
#include "nrf_drv_gpiote.h"
#include "nrf_gpiote.h"
#include "nrf_gpio.h"
#include "app_util.h"
#include "app_util_platform.h"
#include "nrf_assert.h"
#define APP_PWM_CHANNEL_INITIALIZED 1
#define APP_PWM_CHANNEL_UNINITIALIZED 0
#define APP_PWM_CHANNEL_ENABLED 1
#define APP_PWM_CHANNEL_DISABLED 0
#define TIMER_PRESCALER_MAX 9
#define TIMER_MAX_PULSEWIDTH_US_ON_16M 4095
#define APP_PWM_REQUIRED_PPI_CHANNELS_PER_INSTANCE 2
#define APP_PWM_REQUIRED_PPI_CHANNELS_PER_CHANNEL 2
#define UNALLOCATED 0xFFFFFFFFUL
#define BUSY_STATE_CHANGING 0xFE
#define BUSY_STATE_IDLE 0xFF
#define PWM_MAIN_CC_CHANNEL 2
#define PWM_SECONDARY_CC_CHANNEL 3
#ifdef NRF52
static bool m_use_ppi_delay_workaround;
#endif
/**
* @brief PWM busy status
*
* Stores the number of a channel being currently updated.
*
*/
static volatile uint8_t m_pwm_busy[TIMER_COUNT];
/**
* @brief New duty cycle value
*
* When the channel duty cycle reaches this value, the update process is complete.
*/
static volatile uint32_t m_pwm_target_value[TIMER_COUNT];
/**
* @brief PWM ready counter
*
* The value in this counter is decremented in every PWM cycle after initiating the update.
* If an event handler function was specified by the user, it is being called
* after two cycle events (at least one full PWM cycle).
*/
volatile uint8_t m_pwm_ready_counter[TIMER_COUNT][APP_PWM_CHANNELS_PER_INSTANCE];
/**
* @brief Pointers to instances
*
* This array connects any active timer instance number with the pointer to the PWM instance.
* It is used by the interrupt runtime.
*/
static const app_pwm_t * m_instances[TIMER_COUNT];
// Macros for getting the polarity of given instance/channel.
#define POLARITY_ACTIVE(INST,CH) (( ((INST)->p_cb)->channels_cb[(CH)].polarity == \
APP_PWM_POLARITY_ACTIVE_LOW)?(0):(1))
#define POLARITY_INACTIVE(INST,CH) (( ((INST)->p_cb)->channels_cb[(CH)].polarity == \
APP_PWM_POLARITY_ACTIVE_LOW)?(1):(0))
//lint -save -e534
/**
* @brief Workaround for PAN-73.
*
* @param[in] timer Timer.
* @param[in] enable Enable or disable.
*/
static void pan73_workaround(NRF_TIMER_Type * p_timer, bool enable)
{
#ifdef NRF51
if (p_timer == NRF_TIMER0)
{
*(uint32_t *)0x40008C0C = (enable ? 1 : 0);
}
else if (p_timer == NRF_TIMER1)
{
*(uint32_t *)0x40009C0C = (enable ? 1 : 0);
}
else if (p_timer == NRF_TIMER2)
{
*(uint32_t *)0x4000AC0C = (enable ? 1 : 0);
}
#endif
return;
}
bool app_pwm_busy_check(app_pwm_t const * const p_instance)
{
uint8_t busy_state = (m_pwm_busy[p_instance->p_timer->instance_id]);
bool busy = true;
if (busy_state != BUSY_STATE_IDLE)
{
if (busy_state != BUSY_STATE_CHANGING)
{
if (nrf_drv_timer_capture_get(p_instance->p_timer, (nrf_timer_cc_channel_t) busy_state)
== m_pwm_target_value[p_instance->p_timer->instance_id])
{
m_pwm_busy[p_instance->p_timer->instance_id] = BUSY_STATE_IDLE;
busy = false;
}
}
}
else
{
busy = false;
}
return busy;
}
/**
* @brief Function for enabling the IRQ for a given PWM instance.
*
* @param[in] p_instance PWM instance.
*/
__STATIC_INLINE void pwm_irq_enable(app_pwm_t const * const p_instance)
{
nrf_drv_timer_compare_int_enable(p_instance->p_timer, PWM_MAIN_CC_CHANNEL);
}
/**
* @brief Function for disabling the IRQ for a given PWM instance.
*
* @param[in] p_instance PWM instance.
*/
__STATIC_INLINE void pwm_irq_disable(app_pwm_t const * const p_instance)
{
nrf_drv_timer_compare_int_disable(p_instance->p_timer, PWM_MAIN_CC_CHANNEL);
}
/**
* @brief Function for disabling PWM channel PPI.
*
* @param[in] p_instance PWM instance.
*/
__STATIC_INLINE void pwm_channel_ppi_disable(app_pwm_t const * const p_instance, uint8_t channel)
{
app_pwm_cb_t * p_cb = p_instance->p_cb;
nrf_drv_ppi_channel_disable(p_cb->channels_cb[channel].ppi_channels[0]);
nrf_drv_ppi_channel_disable(p_cb->channels_cb[channel].ppi_channels[1]);
}
/**
* @brief Function for disabling PWM PPI.
*
* @param[in] p_instance PWM instance.
*/
__STATIC_INLINE void pwm_ppi_disable(app_pwm_t const * const p_instance)
{
app_pwm_cb_t * p_cb = p_instance->p_cb;
nrf_drv_ppi_channel_disable(p_cb->ppi_channels[0]);
nrf_drv_ppi_channel_disable(p_cb->ppi_channels[1]);
}
/**
* @brief This function is called on interrupt after duty set.
*
* @param[in] timer Timer used by PWM.
* @param[in] timer_instance_id Timer index.
*/
void pwm_ready_tick(nrf_timer_event_t event_type, void * p_context)
{
uint32_t timer_instance_id = (uint32_t)p_context;
uint8_t disable = 1;
for (uint8_t channel = 0; channel < APP_PWM_CHANNELS_PER_INSTANCE; ++channel)
{
if (m_pwm_ready_counter[timer_instance_id][channel])
{
--m_pwm_ready_counter[timer_instance_id][channel];
if (!m_pwm_ready_counter[timer_instance_id][channel])
{
app_pwm_cb_t * p_cb = m_instances[timer_instance_id]->p_cb;
p_cb->p_ready_callback(timer_instance_id);
}
else
{
disable = 0;
}
}
}
if (disable)
{
pwm_irq_disable(m_instances[timer_instance_id]);
}
}
/**
* @brief Function for resource de-allocation.
*
* @param[in] p_instance PWM instance.
*/
//lint -e{650}
static void pwm_dealloc(app_pwm_t const * const p_instance)
{
app_pwm_cb_t * p_cb = p_instance->p_cb;
for (uint8_t i = 0; i < APP_PWM_REQUIRED_PPI_CHANNELS_PER_INSTANCE; ++i)
{
if (p_cb->ppi_channels[i] != (nrf_ppi_channel_t)(uint8_t)(UNALLOCATED))
{
nrf_drv_ppi_channel_free(p_cb->ppi_channels[i]);
}
}
if (p_cb->ppi_group != (nrf_ppi_channel_group_t)UNALLOCATED)
{
nrf_drv_ppi_group_free(p_cb->ppi_group);
}
for (uint8_t ch = 0; ch < APP_PWM_CHANNELS_PER_INSTANCE; ++ch)
{
for (uint8_t i = 0; i < APP_PWM_REQUIRED_PPI_CHANNELS_PER_CHANNEL; ++i)
{
if (p_cb->channels_cb[ch].ppi_channels[i] != (nrf_ppi_channel_t)UNALLOCATED)
{
nrf_drv_ppi_channel_free(p_cb->channels_cb[ch].ppi_channels[i]);
p_cb->channels_cb[ch].ppi_channels[i] = (nrf_ppi_channel_t)UNALLOCATED;
}
}
if (p_cb->channels_cb[ch].gpio_pin != UNALLOCATED)
{
nrf_drv_gpiote_out_uninit(p_cb->channels_cb[ch].gpio_pin);
p_cb->channels_cb[ch].gpio_pin = UNALLOCATED;
}
p_cb->channels_cb[ch].initialized = APP_PWM_CHANNEL_UNINITIALIZED;
}
nrf_drv_timer_uninit(p_instance->p_timer);
return;
}
/**
* @brief PWM state transition from (0%, 100%) to 0% or 100%.
*
* @param[in] p_instance PWM instance.
* @param[in] channel PWM channel number.
* @param[in] ticks Number of clock ticks.
*/
static void pwm_transition_n_to_0or100(app_pwm_t const * const p_instance,
uint8_t channel, uint16_t ticks)
{
app_pwm_cb_t * p_cb = p_instance->p_cb;
app_pwm_channel_cb_t * p_ch_cb = &p_cb->channels_cb[channel];
nrf_ppi_channel_group_t p_ppigrp = p_cb->ppi_group;
pwm_ppi_disable(p_instance);
nrf_drv_ppi_group_clear(p_ppigrp);
nrf_drv_ppi_channels_include_in_group(
nrf_drv_ppi_channel_to_mask(p_ch_cb->ppi_channels[0]) |
nrf_drv_ppi_channel_to_mask(p_ch_cb->ppi_channels[1]),
p_ppigrp);
if (!ticks)
{
nrf_drv_ppi_channel_assign(p_cb->ppi_channels[0],
nrf_drv_timer_compare_event_address_get(p_instance->p_timer, channel),
nrf_drv_ppi_task_addr_group_disable_get(p_ppigrp));
nrf_drv_timer_compare(p_instance->p_timer, (nrf_timer_cc_channel_t) PWM_SECONDARY_CC_CHANNEL, 0, false);
m_pwm_target_value[p_instance->p_timer->instance_id] =
nrf_drv_timer_capture_get(p_instance->p_timer, (nrf_timer_cc_channel_t) channel);
nrf_drv_ppi_channel_assign(p_cb->ppi_channels[1],
nrf_drv_timer_compare_event_address_get(p_instance->p_timer, channel),
nrf_drv_timer_capture_task_address_get(p_instance->p_timer, PWM_SECONDARY_CC_CHANNEL));
}
else
{
ticks = p_cb->period;
nrf_drv_ppi_channel_assign(p_cb->ppi_channels[0],
nrf_drv_timer_compare_event_address_get(p_instance->p_timer, PWM_MAIN_CC_CHANNEL),
nrf_drv_ppi_task_addr_group_disable_get(p_ppigrp));
// Set secondary CC channel to non-zero value:
nrf_drv_timer_compare(p_instance->p_timer, (nrf_timer_cc_channel_t) PWM_SECONDARY_CC_CHANNEL, 1, false);
m_pwm_target_value[p_instance->p_timer->instance_id] = 0;
// The captured value will be equal to 0, because timer clear on main PWM CC channel compare is enabled.
nrf_drv_ppi_channel_assign(p_cb->ppi_channels[1],
nrf_drv_timer_compare_event_address_get(p_instance->p_timer, PWM_MAIN_CC_CHANNEL),
nrf_drv_timer_capture_task_address_get(p_instance->p_timer, PWM_SECONDARY_CC_CHANNEL));
}
nrf_drv_ppi_channel_enable(p_cb->ppi_channels[0]);
nrf_drv_ppi_channel_enable(p_cb->ppi_channels[1]);
p_ch_cb->pulsewidth = ticks;
m_pwm_busy[p_instance->p_timer->instance_id] = PWM_SECONDARY_CC_CHANNEL;
}
/**
* @brief PWM state transition from (0%, 100%) to (0%, 100%).
*
* @param[in] p_instance PWM instance.
* @param[in] channel PWM channel number.
* @param[in] ticks Number of clock ticks.
*/
static void pwm_transition_n_to_m(app_pwm_t const * const p_instance,
uint8_t channel, uint16_t ticks)
{
app_pwm_cb_t * p_cb = p_instance->p_cb;
app_pwm_channel_cb_t * p_ch_cb = &p_cb->channels_cb[channel];
nrf_ppi_channel_group_t p_ppigrp = p_cb->ppi_group;
pwm_ppi_disable(p_instance);
nrf_drv_ppi_group_clear(p_ppigrp);
nrf_drv_ppi_channels_include_in_group(
nrf_drv_ppi_channel_to_mask(p_cb->ppi_channels[0]) |
nrf_drv_ppi_channel_to_mask(p_cb->ppi_channels[1]),
p_ppigrp);
nrf_drv_ppi_channel_assign(p_cb->ppi_channels[0],
nrf_drv_timer_compare_event_address_get(p_instance->p_timer, PWM_SECONDARY_CC_CHANNEL),
nrf_drv_timer_capture_task_address_get(p_instance->p_timer, channel));
#ifdef NRF52
if (ticks + ((nrf_timer_frequency_get(p_instance->p_timer->p_reg) ==
(m_use_ppi_delay_workaround ? NRF_TIMER_FREQ_8MHz : NRF_TIMER_FREQ_16MHz) ) ? 1 : 0)
< p_ch_cb->pulsewidth)
#else
if (ticks + ((nrf_timer_frequency_get(p_instance->p_timer->p_reg) == NRF_TIMER_FREQ_16MHz) ? 1 : 0)
< p_ch_cb->pulsewidth)
#endif
{
// For lower value, we need one more transition. Timer task delay is included.
// If prescaler is disabled, one tick must be added because of 1 PCLK16M clock cycle delay.
nrf_drv_ppi_channel_assign(p_cb->ppi_channels[1],
nrf_drv_timer_compare_event_address_get(p_instance->p_timer, PWM_SECONDARY_CC_CHANNEL),
nrf_drv_gpiote_out_task_addr_get(p_ch_cb->gpio_pin));
}
else
{
nrf_drv_ppi_channel_remove_from_group(p_cb->ppi_channels[1], p_ppigrp);
}
p_ch_cb->pulsewidth = ticks;
nrf_drv_timer_compare(p_instance->p_timer, (nrf_timer_cc_channel_t) PWM_SECONDARY_CC_CHANNEL, ticks, false);
nrf_drv_ppi_group_enable(p_ppigrp);
m_pwm_target_value[p_instance->p_timer->instance_id] = ticks;
m_pwm_busy[p_instance->p_timer->instance_id] = channel;
}
/**
* @brief PWM state transition from 0% or 100% to (0%, 100%).
*
* @param[in] p_instance PWM instance.
* @param[in] channel PWM channel number.
* @param[in] ticks Number of clock ticks.
*/
static void pwm_transition_0or100_to_n(app_pwm_t const * const p_instance,
uint8_t channel, uint16_t ticks)
{
app_pwm_cb_t * p_cb = p_instance->p_cb;
app_pwm_channel_cb_t * p_ch_cb = &p_cb->channels_cb[channel];
nrf_ppi_channel_group_t p_ppigrp = p_cb->ppi_group;
nrf_timer_cc_channel_t pwm_ch_cc = (nrf_timer_cc_channel_t)(channel);
pwm_ppi_disable(p_instance);
pwm_channel_ppi_disable(p_instance, channel);
nrf_drv_timer_compare(p_instance->p_timer, pwm_ch_cc, ticks, false);
nrf_drv_ppi_group_clear(p_ppigrp);
nrf_drv_ppi_channels_include_in_group(
nrf_drv_ppi_channel_to_mask(p_ch_cb->ppi_channels[0])|
nrf_drv_ppi_channel_to_mask(p_ch_cb->ppi_channels[1]),
p_ppigrp);
if (!p_ch_cb->pulsewidth)
{
// Channel is at 0%.
nrf_drv_ppi_channel_assign(p_cb->ppi_channels[0],
nrf_drv_timer_compare_event_address_get(p_instance->p_timer, channel),
nrf_drv_ppi_task_addr_group_enable_get(p_ppigrp));
nrf_drv_timer_compare(p_instance->p_timer, (nrf_timer_cc_channel_t) PWM_SECONDARY_CC_CHANNEL, 0, false);
m_pwm_target_value[p_instance->p_timer->instance_id] =
nrf_drv_timer_capture_get(p_instance->p_timer, (nrf_timer_cc_channel_t) channel);
nrf_drv_ppi_channel_assign(p_cb->ppi_channels[1],
nrf_drv_timer_compare_event_address_get(p_instance->p_timer, channel),
nrf_drv_timer_capture_task_address_get(p_instance->p_timer, PWM_SECONDARY_CC_CHANNEL));
}
else
{
// Channel is at 100%.
nrf_drv_ppi_channel_assign(p_cb->ppi_channels[0],
nrf_drv_timer_compare_event_address_get(p_instance->p_timer, PWM_MAIN_CC_CHANNEL),
nrf_drv_ppi_task_addr_group_enable_get(p_ppigrp));
// Set secondary CC channel to non-zero value:
nrf_drv_timer_compare(p_instance->p_timer, (nrf_timer_cc_channel_t) PWM_SECONDARY_CC_CHANNEL, 1, false);
m_pwm_target_value[p_instance->p_timer->instance_id] = 0;
// The captured value will be equal to 0, because timer clear on main PWM CC channel compare is enabled.
nrf_drv_ppi_channel_assign(p_cb->ppi_channels[1],
nrf_drv_timer_compare_event_address_get(p_instance->p_timer, PWM_MAIN_CC_CHANNEL),
nrf_drv_timer_capture_task_address_get(p_instance->p_timer, PWM_SECONDARY_CC_CHANNEL));
}
nrf_drv_ppi_channel_enable(p_cb->ppi_channels[0]);
nrf_drv_ppi_channel_enable(p_cb->ppi_channels[1]);
p_ch_cb->pulsewidth = ticks;
m_pwm_busy[p_instance->p_timer->instance_id] = PWM_SECONDARY_CC_CHANNEL;
}
/**
* @brief PWM state transition from 0% or 100% to 0% or 100%.
*
* @param[in] p_instance PWM instance.
* @param[in] channel PWM channel number.
* @param[in] ticks Number of clock ticks.
*/
static void pwm_transition_0or100_to_0or100(app_pwm_t const * const p_instance,
uint8_t channel, uint16_t ticks)
{
app_pwm_cb_t * p_cb = p_instance->p_cb;
app_pwm_channel_cb_t * p_ch_cb = &p_cb->channels_cb[channel];
nrf_timer_cc_channel_t pwm_ch_cc = (nrf_timer_cc_channel_t)(channel);
pwm_ppi_disable(p_instance);
pwm_channel_ppi_disable(p_instance, channel);
if (!ticks)
{
// Set to 0%.
nrf_drv_gpiote_out_task_force(p_ch_cb->gpio_pin, POLARITY_INACTIVE(p_instance, channel));
}
else if (ticks >= p_cb->period)
{
// Set to 100%.
ticks = p_cb->period;
nrf_drv_gpiote_out_task_force(p_ch_cb->gpio_pin, POLARITY_ACTIVE(p_instance, channel));
}
nrf_drv_timer_compare(p_instance->p_timer, pwm_ch_cc, ticks, false);
p_ch_cb->pulsewidth = ticks;
m_pwm_busy[p_instance->p_timer->instance_id] = BUSY_STATE_IDLE;
return;
}
ret_code_t app_pwm_channel_duty_ticks_set(app_pwm_t const * const p_instance,
uint8_t channel,
uint16_t ticks)
{
app_pwm_cb_t * p_cb = p_instance->p_cb;
app_pwm_channel_cb_t * p_ch_cb = &p_cb->channels_cb[channel];
ASSERT(channel < APP_PWM_CHANNELS_PER_INSTANCE);
ASSERT(p_ch_cb->initialized == APP_PWM_CHANNEL_INITIALIZED);
if (p_cb->state != NRF_DRV_STATE_POWERED_ON)
{
return NRF_ERROR_INVALID_STATE;
}
if (ticks == p_ch_cb->pulsewidth)
{
if (p_cb->p_ready_callback)
{
p_cb->p_ready_callback(p_instance->p_timer->instance_id);
}
return NRF_SUCCESS; // No action required.
}
if (app_pwm_busy_check(p_instance))
{
return NRF_ERROR_BUSY; // PPI channels for synchronization are still in use.
}
m_pwm_busy[p_instance->p_timer->instance_id] = BUSY_STATE_CHANGING;
// Pulse width change sequence:
if (!p_ch_cb->pulsewidth || p_ch_cb->pulsewidth >= p_cb->period)
{
// Channel is disabled (0%) or at 100%.
if (!ticks || ticks >= p_cb->period)
{
// Set to 0 or 100%.
pwm_transition_0or100_to_0or100(p_instance, channel, ticks);
}
else
{
// Other value.
pwm_transition_0or100_to_n(p_instance, channel, ticks);
}
}
else
{
// Channel is at other value.
if (!ticks || ticks >= p_cb->period)
{
// Disable channel (set to 0%) or set to 100%.
pwm_transition_n_to_0or100(p_instance, channel, ticks);
}
else
{
// Set to any other value.
pwm_transition_n_to_m(p_instance, channel, ticks);
}
}
if (p_instance->p_cb->p_ready_callback)
{
//PWM ready interrupt handler will be called after one full period.
m_pwm_ready_counter[p_instance->p_timer->instance_id][channel] = 2;
pwm_irq_enable(p_instance);
}
return NRF_SUCCESS;
}
uint16_t app_pwm_channel_duty_ticks_get(app_pwm_t const * const p_instance, uint8_t channel)
{
app_pwm_cb_t * p_cb = p_instance->p_cb;
app_pwm_channel_cb_t * p_ch_cb = &p_cb->channels_cb[channel];
return p_ch_cb->pulsewidth;
}
uint16_t app_pwm_cycle_ticks_get(app_pwm_t const * const p_instance)
{
app_pwm_cb_t * p_cb = p_instance->p_cb;
return (uint16_t)p_cb->period;
}
ret_code_t app_pwm_channel_duty_set(app_pwm_t const * const p_instance,
uint8_t channel, app_pwm_duty_t duty)
{
uint32_t ticks = ((uint32_t)app_pwm_cycle_ticks_get(p_instance) * (uint32_t)duty) / 100UL;
return app_pwm_channel_duty_ticks_set(p_instance, channel, ticks);
}
app_pwm_duty_t app_pwm_channel_duty_get(app_pwm_t const * const p_instance, uint8_t channel)
{
uint32_t value = ((uint32_t)app_pwm_channel_duty_ticks_get(p_instance, channel) * 100UL) \
/ (uint32_t)app_pwm_cycle_ticks_get(p_instance);
return (app_pwm_duty_t)value;
}
/**
* @brief Function for initializing the PWM channel.
*
* @param[in] p_instance PWM instance.
* @param[in] channel Channel number.
* @param[in] pin GPIO pin number.
*
* @retval NRF_SUCCESS If initialization was successful.
* @retval NRF_ERROR_NO_MEM If there were not enough free resources.
* @retval NRF_ERROR_INVALID_STATE If the timer is already in use or initialization failed.
*/
static ret_code_t app_pwm_channel_init(app_pwm_t const * const p_instance, uint8_t channel,
uint32_t pin, app_pwm_polarity_t polarity)
{
ASSERT(channel < APP_PWM_CHANNELS_PER_INSTANCE);
app_pwm_cb_t * p_cb = p_instance->p_cb;
app_pwm_channel_cb_t * p_channel_cb = &p_cb->channels_cb[channel];
if (p_cb->state != NRF_DRV_STATE_UNINITIALIZED)
{
return NRF_ERROR_INVALID_STATE;
}
p_channel_cb->pulsewidth = 0;
p_channel_cb->polarity = polarity;
ret_code_t err_code;
/* GPIOTE setup: */
nrf_drv_gpiote_out_config_t out_cfg = GPIOTE_CONFIG_OUT_TASK_TOGGLE( POLARITY_INACTIVE(p_instance, channel) );
err_code = nrf_drv_gpiote_out_init((nrf_drv_gpiote_pin_t)pin,&out_cfg);
if (err_code != NRF_SUCCESS)
{
return NRF_ERROR_NO_MEM;
}
p_cb->channels_cb[channel].gpio_pin = pin;
// Set output to inactive state.
if (polarity)
{
nrf_gpio_pin_clear(pin);
}
else
{
nrf_gpio_pin_set(pin);
}
/* PPI setup: */
for (uint8_t i = 0; i < APP_PWM_REQUIRED_PPI_CHANNELS_PER_CHANNEL; ++i)
{
if (nrf_drv_ppi_channel_alloc(&p_channel_cb->ppi_channels[i]) != NRF_SUCCESS)
{
return NRF_ERROR_NO_MEM; // Resource de-allocation is done by callee.
}
}
nrf_drv_ppi_channel_disable(p_channel_cb->ppi_channels[0]);
nrf_drv_ppi_channel_disable(p_channel_cb->ppi_channels[1]);
nrf_drv_ppi_channel_assign(p_channel_cb->ppi_channels[0],
nrf_drv_timer_compare_event_address_get(p_instance->p_timer, channel),
nrf_drv_gpiote_out_task_addr_get(p_channel_cb->gpio_pin));
nrf_drv_ppi_channel_assign(p_channel_cb->ppi_channels[1],
nrf_drv_timer_compare_event_address_get(p_instance->p_timer, PWM_MAIN_CC_CHANNEL),
nrf_drv_gpiote_out_task_addr_get(p_channel_cb->gpio_pin));
p_channel_cb->initialized = APP_PWM_CHANNEL_INITIALIZED;
m_pwm_ready_counter[p_instance->p_timer->instance_id][channel] = 0;
return NRF_SUCCESS;
}
/**
* @brief Function for calculating target timer frequency, which will allow to set given period length.
*
* @param[in] period_us Desired period in microseconds.
*
* @retval Timer frequency.
*/
__STATIC_INLINE nrf_timer_frequency_t pwm_calculate_timer_frequency(uint32_t period_us)
{
uint32_t f = (uint32_t) NRF_TIMER_FREQ_16MHz;
uint32_t min = (uint32_t) NRF_TIMER_FREQ_31250Hz;
while ((period_us > TIMER_MAX_PULSEWIDTH_US_ON_16M) && (f < min))
{
period_us >>= 1;
++f;
}
#ifdef NRF52
if ((m_use_ppi_delay_workaround) && (f == (uint32_t) NRF_TIMER_FREQ_16MHz))
{
f = (uint32_t) NRF_TIMER_FREQ_8MHz;
}
#endif
return (nrf_timer_frequency_t) f;
}
ret_code_t app_pwm_init(app_pwm_t const * const p_instance, app_pwm_config_t const * const p_config,
app_pwm_callback_t p_ready_callback)
{
ASSERT(p_instance);
if (!p_config)
{
return NRF_ERROR_INVALID_DATA;
}
app_pwm_cb_t * p_cb = p_instance->p_cb;
if (p_cb->state != NRF_DRV_STATE_UNINITIALIZED)
{
return NRF_ERROR_INVALID_STATE;
}
uint32_t err_code = nrf_drv_ppi_init();
if ((err_code != NRF_SUCCESS) && (err_code != MODULE_ALREADY_INITIALIZED))
{
return NRF_ERROR_NO_MEM;
}
if (!nrf_drv_gpiote_is_init())
{
err_code = nrf_drv_gpiote_init();
if (err_code != NRF_SUCCESS)
{
return NRF_ERROR_INTERNAL;
}
}
#ifdef NRF52
if (((*(uint32_t *)0xF0000FE8) & 0x000000F0) == 0x30)
{
m_use_ppi_delay_workaround = false;
}
else
{
m_use_ppi_delay_workaround = true;
}
#endif
// Innitialize resource status:
p_cb->ppi_channels[0] = (nrf_ppi_channel_t)UNALLOCATED;
p_cb->ppi_channels[1] = (nrf_ppi_channel_t)UNALLOCATED;
p_cb->ppi_group = (nrf_ppi_channel_group_t)UNALLOCATED;
for (uint8_t i = 0; i < APP_PWM_CHANNELS_PER_INSTANCE; ++i)
{
p_cb->channels_cb[i].initialized = APP_PWM_CHANNEL_UNINITIALIZED;
p_cb->channels_cb[i].ppi_channels[0] = (nrf_ppi_channel_t)UNALLOCATED;
p_cb->channels_cb[i].ppi_channels[1] = (nrf_ppi_channel_t)UNALLOCATED;
p_cb->channels_cb[i].gpio_pin = UNALLOCATED;
}
// Allocate PPI channels and groups:
for (uint8_t i = 0; i < APP_PWM_REQUIRED_PPI_CHANNELS_PER_INSTANCE; ++i)
{
if (nrf_drv_ppi_channel_alloc(&p_cb->ppi_channels[i]) != NRF_SUCCESS)
{
pwm_dealloc(p_instance);
return NRF_ERROR_NO_MEM;
}
}
if (nrf_drv_ppi_group_alloc(&p_cb->ppi_group) != NRF_SUCCESS)
{
pwm_dealloc(p_instance);
return NRF_ERROR_NO_MEM;
}
// Initialize channels:
for (uint8_t i = 0; i < APP_PWM_CHANNELS_PER_INSTANCE; ++i)
{
if (p_config->pins[i] != APP_PWM_NOPIN)
{
err_code = app_pwm_channel_init(p_instance, i, p_config->pins[i], p_config->pin_polarity[i]);
if (err_code != NRF_SUCCESS)
{
pwm_dealloc(p_instance);
return err_code;
}
app_pwm_channel_duty_ticks_set(p_instance, i, 0);
}
}
// Initialize timer:
nrf_timer_frequency_t timer_freq = pwm_calculate_timer_frequency(p_config->period_us);
nrf_drv_timer_config_t timer_cfg = {
.frequency = timer_freq,
.mode = NRF_TIMER_MODE_TIMER,
.bit_width = NRF_TIMER_BIT_WIDTH_16,
.interrupt_priority = APP_IRQ_PRIORITY_LOW,
.p_context = (void *) (uint32_t) p_instance->p_timer->instance_id
};
err_code = nrf_drv_timer_init(p_instance->p_timer, &timer_cfg,
pwm_ready_tick);
if (err_code != NRF_SUCCESS)
{
pwm_dealloc(p_instance);
return err_code;
}
uint32_t ticks = nrf_drv_timer_us_to_ticks(p_instance->p_timer, p_config->period_us);
p_cb->period = ticks;
nrf_drv_timer_clear(p_instance->p_timer);
nrf_drv_timer_extended_compare(p_instance->p_timer, (nrf_timer_cc_channel_t) PWM_MAIN_CC_CHANNEL,
ticks, NRF_TIMER_SHORT_COMPARE2_CLEAR_MASK, true);
nrf_drv_timer_compare_int_disable(p_instance->p_timer, PWM_MAIN_CC_CHANNEL);
p_cb->p_ready_callback = p_ready_callback;
m_instances[p_instance->p_timer->instance_id] = p_instance;
m_pwm_busy[p_instance->p_timer->instance_id] = BUSY_STATE_IDLE;
p_cb->state = NRF_DRV_STATE_INITIALIZED;
return NRF_SUCCESS;
}
void app_pwm_enable(app_pwm_t const * const p_instance)
{
app_pwm_cb_t * p_cb = p_instance->p_cb;
ASSERT(p_cb->state != NRF_DRV_STATE_UNINITIALIZED);
for (uint32_t channel = 0; channel < APP_PWM_CHANNELS_PER_INSTANCE; ++channel)
{
app_pwm_channel_cb_t * p_ch_cb = &p_cb->channels_cb[channel];
m_pwm_ready_counter[p_instance->p_timer->instance_id][channel] = 0;
if (p_ch_cb->initialized)
{
nrf_drv_gpiote_out_task_force(p_ch_cb->gpio_pin, POLARITY_INACTIVE(p_instance, channel));
nrf_drv_gpiote_out_task_enable(p_ch_cb->gpio_pin);
p_ch_cb->pulsewidth = 0;
}
}
m_pwm_busy[p_instance->p_timer->instance_id] = BUSY_STATE_IDLE;
pan73_workaround(p_instance->p_timer->p_reg, true);
nrf_drv_timer_clear(p_instance->p_timer);
nrf_drv_timer_enable(p_instance->p_timer);
p_cb->state = NRF_DRV_STATE_POWERED_ON;
return;
}
void app_pwm_disable(app_pwm_t const * const p_instance)
{
app_pwm_cb_t * p_cb = p_instance->p_cb;
ASSERT(p_cb->state != NRF_DRV_STATE_UNINITIALIZED);
nrf_drv_timer_disable(p_instance->p_timer);
pwm_irq_disable(p_instance);
for (uint8_t ppi_channel = 0; ppi_channel < APP_PWM_REQUIRED_PPI_CHANNELS_PER_INSTANCE; ++ppi_channel)
{
nrf_drv_ppi_channel_disable(p_cb->ppi_channels[ppi_channel]);
}
for (uint8_t channel = 0; channel < APP_PWM_CHANNELS_PER_INSTANCE; ++channel)
{
app_pwm_channel_cb_t * p_ch_cb = &p_cb->channels_cb[channel];
if (p_ch_cb->initialized)
{
uint8_t polarity = POLARITY_INACTIVE(p_instance, channel);
if (polarity)
{
nrf_gpio_pin_set(p_ch_cb->gpio_pin);
}
else
{
nrf_gpio_pin_clear(p_ch_cb->gpio_pin);
}
nrf_drv_gpiote_out_task_disable(p_ch_cb->gpio_pin);
nrf_drv_ppi_channel_disable(p_ch_cb->ppi_channels[0]);
nrf_drv_ppi_channel_disable(p_ch_cb->ppi_channels[1]);
}
}
pan73_workaround(p_instance->p_timer->p_reg, false);
p_cb->state = NRF_DRV_STATE_INITIALIZED;
return;
}
ret_code_t app_pwm_uninit(app_pwm_t const * const p_instance)
{
app_pwm_cb_t * p_cb = p_instance->p_cb;
if (p_cb->state == NRF_DRV_STATE_POWERED_ON)
{
app_pwm_disable(p_instance);
}
else if (p_cb->state == NRF_DRV_STATE_UNINITIALIZED)
{
return NRF_ERROR_INVALID_STATE;
}
pwm_dealloc(p_instance);
p_cb->state = NRF_DRV_STATE_UNINITIALIZED;
return NRF_SUCCESS;
}
//lint -restore

View File

@ -0,0 +1,295 @@
/* Copyright (c) 2015 Nordic Semiconductor. All Rights Reserved.
*
* The information contained herein is property of Nordic Semiconductor ASA.
* Terms and conditions of usage are described in detail in NORDIC
* SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT.
*
* Licensees are granted free, non-transferable use of the information. NO
* WARRANTY of ANY KIND is provided. This heading must NOT be removed from
* the file.
*
*/
/** @file
*
* @defgroup app_pwm Pulse-width modulation (PWM)
* @{
* @ingroup app_common
*
* @brief Module for generating a pulse-width modulated output signal.
*
* @details This module provides a PWM implementation using timers, GPIOTE, and PPI.
*
* Resource usage:
* - 2 PPI channels per instance + 2 PPI channels per PWM channel.
* - 1 PPI group per instance.
* - 1 GPIOTE channel per PWM channel.
*
* For example, a PWM instance with two channels will consume 2+4 PPI channels, 1 PPI group, and 2 GPIOTE channels.
*
* The maximum number of PWM channels per instance is 2.
*/
#ifndef APP_PWM_H__
#define APP_PWM_H__
#include <stdint.h>
#include "sdk_errors.h"
#include "nrf_drv_timer.h"
#include "nrf_drv_common.h"
#include "nrf_drv_ppi.h"
#define APP_PWM_NOPIN 0xFFFFFFFF
/** @brief Number of channels for one timer instance (fixed to 2 due to timer properties).*/
#define APP_PWM_CHANNELS_PER_INSTANCE 2
/**@brief Macro for creating a PWM instance. */
#define APP_PWM_INSTANCE(name, num) \
const nrf_drv_timer_t m_pwm_##name##_timer = NRF_DRV_TIMER_INSTANCE(num); \
app_pwm_cb_t m_pwm_##name##_cb; \
/*lint -e{545}*/ \
const app_pwm_t name = { \
.p_cb = &m_pwm_##name##_cb, \
.p_timer = &m_pwm_##name##_timer, \
}
/**@brief PWM instance default configuration (1 channel). */
#define APP_PWM_DEFAULT_CONFIG_1CH(period_in_us, pin) \
{ \
.pins = {pin, APP_PWM_NOPIN}, \
.pin_polarity = {APP_PWM_POLARITY_ACTIVE_LOW, APP_PWM_POLARITY_ACTIVE_LOW}, \
.num_of_channels = 1, \
.period_us = period_in_us \
}
/**@brief PWM instance default configuration (2 channels). */
#define APP_PWM_DEFAULT_CONFIG_2CH(period_in_us, pin0, pin1) \
{ \
.pins = {pin0, pin1}, \
.pin_polarity = {APP_PWM_POLARITY_ACTIVE_LOW, APP_PWM_POLARITY_ACTIVE_LOW}, \
.num_of_channels = 2, \
.period_us = period_in_us \
}
typedef uint16_t app_pwm_duty_t;
/**
* @brief PWM callback that is executed when a PWM duty change has been completed.
*
* @param[in] pwm_id PWM instance ID.
*/
typedef void (* app_pwm_callback_t)(uint32_t);
/**
* @brief Channel polarity.
*/
typedef enum
{
APP_PWM_POLARITY_ACTIVE_LOW = 0,
APP_PWM_POLARITY_ACTIVE_HIGH = 1
} app_pwm_polarity_t;
/**@brief PWM configuration structure used for initialization. */
typedef struct
{
uint32_t pins[APP_PWM_CHANNELS_PER_INSTANCE]; //!< Pins configured as PWM output.
app_pwm_polarity_t pin_polarity[APP_PWM_CHANNELS_PER_INSTANCE]; //!< Polarity of active state on pin.
uint32_t num_of_channels; //!< Number of channels that can be used.
uint32_t period_us; //!< PWM signal output period to configure (in microseconds).
} app_pwm_config_t;
/**
* @cond (NODOX)
* @defgroup app_pwm_internal Auxiliary internal types declarations
* @{
* @internal
*
* @brief Module for internal usage inside the library only
*
* There are some definitions that must be included in the header file because
* of the way the library is set up. In this way, the are accessible to the user.
* However, any functions and variables defined here may change at any time
* without a warning, so you should not access them directly.
*/
/**
* @brief PWM channel instance
*
* This structure holds all data needed by a single PWM channel.
*/
typedef struct
{
uint32_t gpio_pin; //!< Pin that is used by this PWM channel.
uint32_t pulsewidth; //!< The copy of duty currently set (in ticks).
nrf_ppi_channel_t ppi_channels[2]; //!< PPI channels used by the PWM channel to clear and set the output.
app_pwm_polarity_t polarity; //!< The active state of the pin.
uint8_t initialized; //!< The internal information if the selected channel was initialized.
} app_pwm_channel_cb_t;
/**
* @brief Variable part of PWM instance
*
* This structure holds instance data that may change.
*/
typedef struct
{
app_pwm_channel_cb_t channels_cb[APP_PWM_CHANNELS_PER_INSTANCE]; //!< Channels data
uint32_t period; //!< Selected period in ticks
app_pwm_callback_t p_ready_callback; //!< Callback function called on PWM readiness
nrf_ppi_channel_t ppi_channels[2]; //!< PPI channels used temporary while changing duty
nrf_ppi_channel_group_t ppi_group; //!< PPI group used to synchronize changes on channels
nrf_drv_state_t state; //!< Current driver status
} app_pwm_cb_t;
/** @}
* @endcond
*/
/**@brief PWM instance structure. */
typedef struct
{
app_pwm_cb_t *p_cb; //!< Pointer to control block internals.
nrf_drv_timer_t const * const p_timer; //!< Timer used by this PWM instance.
} app_pwm_t;
/**
* @brief Function for checking if the PWM instance is busy updating the duty cycle.
*
* @param[in] p_instance PWM instance.
*
* @retval True If the PWM instance is ready for duty cycle changes.
* @retval False If a change operation is in progress.
*/
bool app_pwm_busy_check(app_pwm_t const * const p_instance);
/**
* @brief Function for initializing a PWM instance.
*
* @param[in] p_instance PWM instance.
* @param[in] p_config Initial configuration.
* @param[in] p_ready_callback Pointer to ready callback function (or NULL to disable).
*
* @retval NRF_SUCCESS If initialization was successful.
* @retval NRF_ERROR_NO_MEM If there were not enough free resources.
* @retval NRF_ERROR_INVALID_PARAM If an invalid configuration structure was passed.
* @retval NRF_ERROR_INVALID_STATE If the timer/PWM is already in use or if initialization failed.
*/
ret_code_t app_pwm_init(app_pwm_t const * const p_instance, app_pwm_config_t const * const p_config,
app_pwm_callback_t p_ready_callback);
/**
* @brief Function for uninitializing a PWM instance and releasing the allocated resources.
*
* @param[in] p_instance PWM instance.
*
* @retval NRF_SUCCESS If uninitialization was successful.
* @retval NRF_ERROR_INVALID_STATE If the given instance was not initialized.
*/
ret_code_t app_pwm_uninit(app_pwm_t const * const p_instance);
/**
* @brief Function for enabling a PWM instance after initialization.
*
* @param[in] p_instance PWM instance.
*/
void app_pwm_enable(app_pwm_t const * const p_instance);
/**
* @brief Function for disabling a PWM instance after initialization.
*
* @param[in] p_instance PWM instance.
*/
void app_pwm_disable(app_pwm_t const * const p_instance);
/**
* @brief Function for setting the PWM channel duty cycle in percents.
*
* A duty cycle change requires one full PWM clock period to finish.
* If another change is attempted for any channel of given instance before
* the current change is complete, the new attempt will result in the error
* NRF_ERROR_BUSY.
*
* @param[in] p_instance PWM instance.
* @param[in] channel Channel number.
* @param[in] duty Duty cycle (0 - 100).
*
* @retval NRF_SUCCESS If the operation was successful.
* @retval NRF_ERROR_BUSY If the PWM is not ready yet.
* @retval NRF_ERROR_INVALID_STATE If the given instance was not initialized.
*
*/
ret_code_t app_pwm_channel_duty_set(app_pwm_t const * const p_instance,
uint8_t channel, app_pwm_duty_t duty);
/**
* @brief Function for retrieving the PWM channel duty cycle in percents.
*
* @param[in] p_instance PWM instance.
* @param[in] channel Channel number.
*
* @return Duty cycle value.
*/
app_pwm_duty_t app_pwm_channel_duty_get(app_pwm_t const * const p_instance, uint8_t channel);
/**
* @name Functions accessing values in ticks
*
* Auxiliary functions that allow to get values in actual timer ticks.
* @{
*/
/**
* @brief Function for setting PWM channel duty cycle in clock ticks.
*
* @note Duty cycle changes require one full PWM clock period to finish.
* Until that, the next change attempt (for any channel of given instance)
* will result in an NRF_ERROR_BUSY error.
*
* @param[in] p_instance PWM instance.
* @param[in] channel Channel number.
* @param[in] ticks Number of PWM clock ticks.
*
* @retval NRF_SUCCESS If the operation was successful.
* @retval NRF_ERROR_BUSY If PWM is not ready yet.
* @retval NRF_ERROR_INVALID_STATE If the given instance was not initialized.
*/
ret_code_t app_pwm_channel_duty_ticks_set(app_pwm_t const * const p_instance,
uint8_t channel,
uint16_t ticks);
/**
* @brief Function for retrieving the PWM channel duty cycle in ticks.
*
* This function retrieves the real, currently set duty cycle in ticks.
* For one full PWM cycle the value might be different than the value set by the last
* @ref app_pwm_channel_duty_ticks_set function call.
*
* @param[in] p_instance PWM instance.
* @param[in] channel Channel number.
*
* @return Number of ticks set for selected channel.
*
*/
uint16_t app_pwm_channel_duty_ticks_get(app_pwm_t const * const p_instance, uint8_t channel);
/**
* @brief Function for returning the number of ticks in a whole cycle.
*
* @param[in] p_instance PWM instance.
*
* @return Number of ticks that corresponds to 100% of the duty cycle.
*/
uint16_t app_pwm_cycle_ticks_get(app_pwm_t const * const p_instance);
/** @} */
#endif
/** @} */

View File

@ -0,0 +1,469 @@
/* mbed Microcontroller Library
* Copyright (c) 2013 Nordic Semiconductor
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <string.h>
#include "mbed_assert.h"
#include "mbed_error.h"
#include "serial_api.h"
#include "nrf_drv_uart.h"
#include "app_util_platform.h"
#include "nrf_gpio.h"
#if DEVICE_SERIAL_ASYNCH
#define SERIAL_S(obj) (&obj->serial)
#else
#define SERIAL_S(obj) (obj)
#endif
#define UART_INSTANCE_COUNT 1
#define UART_INSTANCE NRF_UART0
#define UART_INSTANCE_ID 0
#define UART_IRQn UART0_IRQn
#define UART_CB uart_cb[UART_INSTANCE_ID]
int stdio_uart_inited = 0;
serial_t stdio_uart;
static nrf_drv_uart_config_t uart_config = NRF_DRV_UART_DEFAULT_CONFIG;
typedef struct {
volatile bool tx_active;
volatile bool rx_active;
bool async_mode;
bool initialized;
uint8_t irqs_enabled;
uint32_t irq_context;
uart_irq_handler irq_handler;
uint32_t registered_events;
uint32_t event_flags;
void (*async_handler)();
uint8_t *rx_buffer;
uint8_t rx_pos;
uint8_t rx_length;
} uart_ctlblock_t;
static uart_ctlblock_t uart_cb[UART_INSTANCE_COUNT];
static nrf_uart_baudrate_t baud_translate(int rate)
{
nrf_uart_baudrate_t baud;
if (rate < 57600) {
if (rate < 14400) {
if (rate < 2400) {
baud = NRF_UART_BAUDRATE_1200;
} else if (rate < 4800) {
baud = NRF_UART_BAUDRATE_2400;
} else if (rate < 9600) {
baud = NRF_UART_BAUDRATE_4800;
} else {
baud = NRF_UART_BAUDRATE_9600;
}
} else {
if (rate < 19200) {
baud = NRF_UART_BAUDRATE_14400;
} else if (rate < 28800) {
baud = NRF_UART_BAUDRATE_19200;
} else if (rate < 38400) {
baud = NRF_UART_BAUDRATE_28800;
} else {
baud = NRF_UART_BAUDRATE_38400;
}
}
} else {
if (rate < 250000) {
if (rate < 76800) {
baud = NRF_UART_BAUDRATE_57600;
} else if (rate < 115200) {
baud = NRF_UART_BAUDRATE_76800;
} else if (rate < 230400) {
baud = NRF_UART_BAUDRATE_115200;
} else {
baud = NRF_UART_BAUDRATE_230400;
}
} else {
if (rate < 460800) {
baud = NRF_UART_BAUDRATE_250000;
} else if (rate < 921600) {
baud = NRF_UART_BAUDRATE_460800;
} else if (rate < 1000000) {
baud = NRF_UART_BAUDRATE_921600;
} else {
baud = NRF_UART_BAUDRATE_1000000;
}
}
}
return baud;
}
void serial_baud(serial_t *obj, int baudrate)
{
(void)obj;
uart_config.baudrate = baud_translate(baudrate);
// Reconfigure UART peripheral.
nrf_uart_baudrate_set(UART_INSTANCE, uart_config.baudrate);
}
void uart_event_handler(nrf_drv_uart_event_t *p_event, void *p_context)
{
(void)p_context;
if (p_event->type == NRF_DRV_UART_EVT_TX_DONE) {
UART_CB.tx_active = false;
if (UART_CB.async_mode) {
if (UART_CB.async_handler) {
UART_CB.event_flags |= SERIAL_EVENT_TX_COMPLETE;
UART_CB.async_handler();
}
}
else {
if (UART_CB.irqs_enabled & (1 << NRF_DRV_UART_EVT_TX_DONE)) {
if (UART_CB.irq_handler) {
UART_CB.irq_handler(UART_CB.irq_context, TxIrq);
}
}
}
}
if (p_event->type == NRF_DRV_UART_EVT_RX_DONE) {
if (UART_CB.async_mode) {
bool rx_end = true;
if (UART_CB.registered_events & SERIAL_EVENT_RX_CHARACTER_MATCH) {
serial_t *serial = (serial_t *)(uart_config.p_context);
uint8_t *rx_buffer = (uint8_t *)(serial->rx_buff.buffer);
uint8_t last_char = rx_buffer[serial->rx_buff.pos];
++serial->rx_buff.pos;
if (last_char == serial->char_match) {
UART_CB.event_flags |= SERIAL_EVENT_RX_CHARACTER_MATCH;
serial->char_found = 1;
}
else {
if (serial->rx_buff.pos < serial->rx_buff.length) {
rx_end = false;
nrf_drv_uart_rx(&rx_buffer[serial->rx_buff.pos], 1);
}
}
}
if (rx_end && UART_CB.async_handler) {
UART_CB.rx_active = false;
UART_CB.event_flags |= SERIAL_EVENT_RX_COMPLETE;
UART_CB.async_handler();
}
}
else {
UART_CB.rx_active = false;
if (UART_CB.irqs_enabled & (1 << NRF_DRV_UART_EVT_RX_DONE)) {
if (UART_CB.irq_handler) {
UART_CB.irq_handler(UART_CB.irq_context, RxIrq);
}
}
}
}
if (p_event->type == NRF_DRV_UART_EVT_ERROR) {
if (UART_CB.async_mode && p_event->data.error.error_mask) {
if (UART_CB.async_handler) {
UART_CB.event_flags |= SERIAL_EVENT_ERROR;
if (p_event->data.error.error_mask & NRF_UART_ERROR_PARITY_MASK) {
UART_CB.event_flags |= SERIAL_EVENT_RX_PARITY_ERROR;
}
if (p_event->data.error.error_mask & NRF_UART_ERROR_FRAMING_MASK) {
UART_CB.event_flags |= SERIAL_EVENT_RX_FRAMING_ERROR;
}
if (p_event->data.error.error_mask & NRF_UART_ERROR_OVERRUN_MASK) {
UART_CB.event_flags |= SERIAL_EVENT_RX_OVERRUN_ERROR;
}
UART_CB.async_handler();
}
}
}
}
void serial_init(serial_t *obj, PinName tx, PinName rx)
{
uart_config.pseltxd =
(tx == NC) ? NRF_UART_PSEL_DISCONNECTED : (uint32_t)tx;
uart_config.pselrxd =
(rx == NC) ? NRF_UART_PSEL_DISCONNECTED : (uint32_t)rx;
uart_config.p_context = (void *)obj;
if (UART_CB.initialized) {
// Reconfigure RX/TX pins only.
nrf_uart_txrx_pins_set(UART_INSTANCE,
uart_config.pseltxd, uart_config.pselrxd);
return;
}
memset(&UART_CB, 0, sizeof(uart_ctlblock_t));
ret_code_t err_code = nrf_drv_uart_init(&uart_config, uart_event_handler);
if (err_code == NRF_SUCCESS) {
UART_CB.initialized = true;
nrf_drv_uart_rx_enable();
stdio_uart_inited = 1;
memcpy(&stdio_uart, obj, sizeof(serial_t));
}
else {
error("UART init failure.");
}
}
void serial_free(serial_t *obj)
{
(void)obj;
if (UART_CB.initialized) {
nrf_drv_uart_uninit();
UART_CB.initialized = false;
}
}
int serial_writable(serial_t *obj)
{
(void)obj;
if (!UART_CB.async_mode) {
return true;
}
return !UART_CB.tx_active;
}
int serial_readable(serial_t *obj)
{
(void)obj;
return nrf_uart_event_check(UART_INSTANCE, NRF_UART_EVENT_RXDRDY);
}
void serial_putc(serial_t *obj, int c)
{
(void)obj;
UART_CB.async_mode = false;
UART_CB.tx_active = true;
uint8_t data = c;
nrf_drv_uart_tx(&data, 1);
while (UART_CB.tx_active) {
}
}
int serial_getc(serial_t *obj)
{
(void)obj;
UART_CB.async_mode = false;
UART_CB.rx_active = true;
uint8_t data;
nrf_drv_uart_rx(&data, 1);
while (UART_CB.rx_active) {
}
return (int)data;
}
void serial_irq_handler(serial_t *obj, uart_irq_handler handler, uint32_t id)
{
(void)obj;
UART_CB.irq_handler = handler;
UART_CB.irq_context = id;
}
void serial_irq_set(serial_t *obj, SerialIrq irq, uint32_t enable)
{
(void)obj;
if (enable) {
switch (irq) {
case RxIrq:
UART_CB.irqs_enabled |= (1 << NRF_DRV_UART_EVT_RX_DONE);
break;
case TxIrq:
UART_CB.irqs_enabled |= (1 << NRF_DRV_UART_EVT_TX_DONE);
break;
}
} else {
switch (irq) {
case RxIrq:
UART_CB.irqs_enabled &= ~(1 << NRF_DRV_UART_EVT_RX_DONE);
break;
case TxIrq:
UART_CB.irqs_enabled &= ~(1 << NRF_DRV_UART_EVT_TX_DONE);
break;
}
}
}
#if DEVICE_SERIAL_ASYNCH
int serial_tx_asynch(serial_t *obj, const void *tx, size_t tx_length,
uint8_t tx_width, uint32_t handler, uint32_t event,
DMAUsage hint)
{
(void)obj;
(void)tx_width;
(void)hint;
if (UART_CB.tx_active) {
return 0;
}
// TX length is limited to 255 in uart driver.
if (tx_length > 255) {
tx_length = 255;
}
UART_CB.async_mode = true;
UART_CB.tx_active = true;
UART_CB.registered_events &= ~SERIAL_EVENT_TX_ALL;
UART_CB.registered_events |= event;
UART_CB.async_handler = (void(*)())handler;
if (nrf_drv_uart_tx((uint8_t *)tx, (uint8_t)tx_length) == NRF_SUCCESS) {
return tx_length;
}
return 0;
}
void serial_rx_asynch(serial_t *obj, void *rx, size_t rx_length,
uint8_t rx_width, uint32_t handler, uint32_t event,
uint8_t char_match, DMAUsage hint)
{
(void)rx_width;
(void)hint;
if (UART_CB.rx_active || !rx_length) {
return;
}
// RX length is limited to 255 in uart driver.
if (rx_length > 255) {
rx_length = 255;
}
UART_CB.async_mode = true;
UART_CB.rx_active = true;
if (char_match == SERIAL_RESERVED_CHAR_MATCH) {
event &= ~SERIAL_EVENT_RX_CHARACTER_MATCH;
}
UART_CB.registered_events &= ~SERIAL_EVENT_RX_ALL;
UART_CB.registered_events |= event;
UART_CB.async_handler = (void(*)())handler;
if (event & SERIAL_EVENT_RX_CHARACTER_MATCH) {
obj->char_match = char_match;
obj->rx_buff.buffer = rx;
obj->rx_buff.length = rx_length;
obj->rx_buff.pos = 0;
obj->rx_buff.width = 8;
nrf_drv_uart_rx((uint8_t *)rx, 1);
} else {
nrf_drv_uart_rx((uint8_t *)rx, (uint8_t)rx_length);
}
}
uint8_t serial_tx_active(serial_t *obj)
{
(void)obj;
return UART_CB.tx_active;
}
uint8_t serial_rx_active(serial_t *obj)
{
(void)obj;
return UART_CB.rx_active;
}
int serial_irq_handler_asynch(serial_t *obj)
{
(void)obj;
uint32_t result = UART_CB.registered_events & UART_CB.event_flags;
UART_CB.event_flags &= (~result);
return result;
}
void serial_tx_abort_asynch(serial_t *obj)
{
(void)obj;
nrf_drv_uart_tx_abort();
}
void serial_rx_abort_asynch(serial_t *obj)
{
(void)obj;
nrf_drv_uart_rx_abort();
}
#endif
void serial_format(serial_t *obj,
int data_bits, SerialParity parity, int stop_bits)
{
(void)obj;
(void)data_bits;
(void)stop_bits;
if (parity == ParityNone) {
uart_config.parity = NRF_UART_PARITY_EXCLUDED;
} else {
uart_config.parity = NRF_UART_PARITY_INCLUDED;
}
// Reconfigure UART peripheral.
nrf_uart_configure(UART_INSTANCE, uart_config.parity, uart_config.hwfc);
}
void serial_break_set(serial_t *obj)
{
(void)obj;
nrf_uart_task_trigger(UART_INSTANCE, NRF_UART_TASK_SUSPEND);
nrf_uart_txrx_pins_disconnect(UART_INSTANCE);
nrf_gpio_pin_set(uart_config.pseltxd);
nrf_gpio_pin_clear(uart_config.pseltxd);
}
void serial_break_clear(serial_t *obj)
{
(void)obj;
nrf_gpio_pin_set(uart_config.pseltxd);
nrf_uart_txrx_pins_set(UART_INSTANCE,
uart_config.pseltxd, uart_config.pselrxd);
nrf_uart_task_trigger(UART_INSTANCE, NRF_UART_TASK_STARTTX);
nrf_uart_task_trigger(UART_INSTANCE, NRF_UART_TASK_STARTRX);
}
void serial_set_flow_control(serial_t *obj,
FlowControl type, PinName rxflow, PinName txflow)
{
(void)obj;
if (type == FlowControlNone) {
uart_config.hwfc = NRF_UART_HWFC_DISABLED;
rxflow = NC;
txflow = NC;
} else {
uart_config.hwfc = NRF_UART_HWFC_ENABLED;
if (type == FlowControlRTS) {
txflow = NC;
} else if (type == FlowControlCTS) {
rxflow = NC;
}
}
uart_config.pselrts =
(rxflow == NC) ? NRF_UART_PSEL_DISCONNECTED : (uint32_t)rxflow;
uart_config.pselcts =
(txflow == NC) ? NRF_UART_PSEL_DISCONNECTED : (uint32_t)txflow;
// Reconfigure UART peripheral.
if (uart_config.hwfc == NRF_UART_HWFC_ENABLED) {
nrf_gpio_cfg_input(uart_config.pselcts, NRF_GPIO_PIN_NOPULL);
nrf_gpio_pin_set(uart_config.pselrts);
nrf_gpio_cfg_output(uart_config.pselrts);
nrf_uart_hwfc_pins_set(UART_INSTANCE,
uart_config.pselrts, uart_config.pselcts);
}
nrf_uart_configure(UART_INSTANCE, uart_config.parity, uart_config.hwfc);
}
void serial_clear(serial_t *obj)
{
(void)obj;
}

View File

@ -0,0 +1,218 @@
/* mbed Microcontroller Library
* Copyright (c) 2013 Nordic Semiconductor
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//#include <math.h>
#include "spi_api.h"
#include "cmsis.h"
#include "pinmap.h"
#include "mbed_assert.h"
#include "mbed_error.h"
#include "nrf_drv_spi.h"
#include "app_util_platform.h"
#if DEVICE_SPI
#define SPI_MESSAGE_SIZE 1
volatile uint8_t m_tx_buf[SPI_MESSAGE_SIZE] = {0};
volatile uint8_t m_rx_buf[SPI_MESSAGE_SIZE] = {0};
static nrf_drv_spi_config_t m_config = NRF_DRV_SPI_DEFAULT_CONFIG(1);
static nrf_drv_spi_t spi1 = NRF_DRV_SPI_INSTANCE(1);
typedef void (*user_handler_t)(void);
static user_handler_t m_user_handler;
static uint32_t m_event;
static spi_t * m_spi_struct;
#if DEVICE_SPI_ASYNCH
#define SPI_S(obj) (&obj->spi)
#define SPI_DRV(obj) (obj->spi.p_spi)
#else
#define SPI_S(obj) (obj)
#define SPI_DRV(obj) (obj->p_spi)
#endif
static void master_event_handler(nrf_drv_spi_evt_t const * event)
{
if (event->type == NRF_DRV_SPI_EVENT_DONE) {
SPI_S(m_spi_struct)->busy = false;
if (SPI_S(m_spi_struct)->async_mode) {
m_user_handler();
}
}
}
void spi_init(spi_t *obj, PinName mosi, PinName miso, PinName sclk)
{
m_config.sck_pin = sclk;
m_config.mosi_pin = mosi;
m_config.miso_pin = miso;
SPI_S(obj)->busy = false;
m_spi_struct = obj;
SPI_DRV(obj) = &spi1;
(void)nrf_drv_spi_init(&spi1, &m_config, master_event_handler);
}
void spi_free(spi_t *obj)
{
nrf_drv_spi_uninit(&spi1);
}
int spi_busy(spi_t *obj)
{
return (int)(SPI_S(obj)->busy);
}
static nrf_drv_spi_mode_t mode_translate(int mode)
{
nrf_drv_spi_mode_t config_mode = NRF_DRV_SPI_MODE_0;
switch (mode) {
case 0:
config_mode = NRF_DRV_SPI_MODE_0;
break;
case 1:
config_mode = NRF_DRV_SPI_MODE_1;
break;
case 2:
config_mode = NRF_DRV_SPI_MODE_2;
break;
case 3:
config_mode = NRF_DRV_SPI_MODE_3;
break;
default:
error("SPI format error");
break;
}
return config_mode;
}
static nrf_drv_spi_frequency_t freq_translate(int hz)
{
nrf_drv_spi_frequency_t frequency;
if (hz<250000) { //125Kbps
frequency = NRF_DRV_SPI_FREQ_125K;
} else if (hz<500000) { //250Kbps
frequency = NRF_DRV_SPI_FREQ_250K;
} else if (hz<1000000) { //500Kbps
frequency = NRF_DRV_SPI_FREQ_500K;
} else if (hz<2000000) { //1Mbps
frequency = NRF_DRV_SPI_FREQ_1M;
} else if (hz<4000000) { //2Mbps
frequency = NRF_DRV_SPI_FREQ_2M;
} else if (hz<8000000) { //4Mbps
frequency = NRF_DRV_SPI_FREQ_4M;
} else { //8Mbps
frequency = NRF_DRV_SPI_FREQ_8M;
}
return frequency;
}
void spi_format(spi_t *obj, int bits, int mode, spi_bitorder_t order)
{
if (bits != 8) {
error("Only 8bits SPI supported");
}
m_config.bit_order = ((order == SPI_MSB) ? NRF_DRV_SPI_BIT_ORDER_MSB_FIRST : NRF_DRV_SPI_BIT_ORDER_LSB_FIRST);
nrf_drv_spi_mode_t config_mode = mode_translate(mode);
if (m_config.mode != config_mode) {
nrf_drv_spi_uninit(&spi1);
m_config.mode = config_mode;
(void)nrf_drv_spi_init(&spi1, &m_config, master_event_handler);
}
}
void spi_frequency(spi_t *obj, int hz)
{
nrf_drv_spi_frequency_t frequency = freq_translate(hz);
if (frequency != m_config.frequency) {
nrf_drv_spi_uninit(&spi1);
m_config.frequency = frequency;
(void)nrf_drv_spi_init(&spi1, &m_config, master_event_handler);
}
}
int spi_master_write(spi_t *obj, int value)
{
while (SPI_S(obj)->busy);
m_tx_buf[0] = value;
SPI_S(obj)->async_mode = false;
SPI_S(obj)->busy = true;
(void)nrf_drv_spi_transfer(SPI_DRV(obj), (uint8_t const *) m_tx_buf, 1,
(uint8_t *) m_rx_buf, 1);
while (SPI_S(obj)->busy);
return m_rx_buf[0];
}
int spi_slave_receive(spi_t *obj)
{
return 0;
}
int spi_slave_read(spi_t *obj)
{
return 0;
}
void spi_slave_write(spi_t *obj, int value)
{
(void) obj;
(void) value;
}
#if DEVICE_SPI_ASYNCH
void spi_master_transfer(spi_t *obj,
void *tx, size_t tx_length,
void *rx, size_t rx_length,
uint32_t handler,
uint32_t event,
DMAUsage hint)
{
m_user_handler = (user_handler_t)handler;
m_event = event;
SPI_S(obj)->async_mode = true;
SPI_S(obj)->busy = true;
(void)nrf_drv_spi_transfer(SPI_DRV(obj), (uint8_t const *) tx, tx_length,
(uint8_t *) rx, rx_length);
}
uint32_t spi_irq_handler_asynch(spi_t *obj)
{
return m_event & SPI_EVENT_COMPLETE;
}
uint8_t spi_active(spi_t *obj)
{
return SPI_S(obj)->busy;
}
void spi_abort_asynch(spi_t *obj)
{
nrf_drv_spi_abort(SPI_DRV(obj));
}
#endif
#endif // DEVICE_SPI

View File

@ -0,0 +1,145 @@
/* mbed Microcontroller Library
* Copyright (c) 2013 Nordic Semiconductor
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "us_ticker_api.h"
#include "rtc_common.h"
#include "app_util.h"
//------------------------------------------------------------------------------
// Common stuff used also by lp_ticker and rtc_api (see "rtc_common.h").
//
#include "nrf_drv_clock.h"
#include "app_util_platform.h"
nrf_drv_rtc_t const m_rtc_common = NRF_DRV_RTC_INSTANCE(1);
bool m_rtc_common_enabled = false;
uint32_t volatile m_rtc_common_overflows = 0;
static void irq_handler(nrf_drv_rtc_int_type_t int_type)
{
if (int_type == (NRF_DRV_RTC_INT_COMPARE0 + US_TICKER_CC_CHANNEL)) {
us_ticker_irq_handler();
}
else if (int_type == NRF_DRV_RTC_INT_OVERFLOW) {
++m_rtc_common_overflows;
}
}
void rtc_common_init(void)
{
if (m_rtc_common_enabled) {
return;
}
(void)nrf_drv_clock_init();
// RTC is driven by the low frequency (32.768 kHz) clock, a proper request
// must be made to have it running.
nrf_drv_clock_lfclk_request(NULL);
nrf_drv_rtc_config_t const config = {
.prescaler = 0, // no prescaling
.interrupt_priority = APP_IRQ_PRIORITY_LOW,
.reliable = false
};
if (nrf_drv_rtc_init(&m_rtc_common, &config, irq_handler) != NRF_SUCCESS) {
MBED_ASSERT(false); // initialization failed
return;
}
nrf_drv_rtc_overflow_enable(&m_rtc_common, true);
nrf_drv_rtc_enable(&m_rtc_common);
m_rtc_common_enabled = true;
}
uint32_t rtc_common_32bit_ticks_get(void)
{
uint32_t ticks = nrf_drv_rtc_counter_get(&m_rtc_common);
// The counter used for time measurements is less than 32 bit wide,
// so its value is complemented with the number of registered overflows
// of the counter.
ticks += (m_rtc_common_overflows << RTC_COUNTER_BITS);
return ticks;
}
//------------------------------------------------------------------------------
void us_ticker_init(void)
{
rtc_common_init();
}
static uint64_t us_ticker_64bit_get(void)
{
uint32_t ticks = rtc_common_32bit_ticks_get();
// [ticks -> microseconds]
return ROUNDED_DIV(((uint64_t)ticks) * 1000000, RTC_INPUT_FREQ);
}
uint32_t us_ticker_read()
{
return (uint32_t)us_ticker_64bit_get();
}
void us_ticker_set_interrupt(timestamp_t timestamp)
{
// The internal counter is clocked with a frequency that cannot be easily
// multiplied to 1 MHz, therefore besides the translation of values
// (microsecond <-> ticks) a special care of overflows handling must be
// taken. Here the 32-bit timestamp value is complemented with information
// about current the system up time of (ticks + number of overflows of tick
// counter on upper bits, converted to microseconds), and such 64-bit value
// is then translated to counter ticks. Finally, the lower 24 bits of thus
// calculated value is written to the counter compare register to prepare
// the interrupt generation.
uint64_t current_time64 = us_ticker_64bit_get();
// [add upper 32 bits from the current time to the timestamp value]
uint64_t timestamp64 = timestamp + (current_time64 & ~(uint64_t)0xFFFFFFFF);
// [if the original timestamp value happens to be after the 32 bit counter
// of microsends overflows, correct the upper 32 bits accordingly]
if (timestamp < (uint32_t)(current_time64 & 0xFFFFFFFF)) {
timestamp64 += ((uint64_t)1 << 32);
}
// [microseconds -> ticks, always round the result up to avoid too early
// interrupt generation]
uint32_t compare_value =
(uint32_t)CEIL_DIV((timestamp64) * RTC_INPUT_FREQ, 1000000);
// The COMPARE event occurs when the value in compare register is N and
// the counter value changes from N-1 to N. Therefore, the minimal safe
// difference between the compare value to be set and the current counter
// value is 2 ticks. This guarantees that the compare trigger is properly
// setup before the compare condition occurs.
uint32_t closest_safe_compare = rtc_common_32bit_ticks_get() + 2;
if ((int)(compare_value - closest_safe_compare) <= 0) {
compare_value = closest_safe_compare;
}
ret_code_t result = nrf_drv_rtc_cc_set(&m_rtc_common, US_TICKER_CC_CHANNEL,
compare_value, true);
if (result != NRF_SUCCESS) {
MBED_ASSERT(false);
}
}
void us_ticker_disable_interrupt(void)
{
nrf_drv_rtc_cc_disable(&m_rtc_common, US_TICKER_CC_CHANNEL);
}
void us_ticker_clear_interrupt(void)
{
// No implementation needed. Interrupt flags are cleared by IRQ handler
// in 'nrf_drv_rtc'.
}