diff --git a/hal/targets.json b/hal/targets.json index 477f0bc083..670543d345 100644 --- a/hal/targets.json +++ b/hal/targets.json @@ -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"] } } diff --git a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF51822/analogin_api.c b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF51822/analogin_api.c index 77b1ddcd38..44d5342831 100644 --- a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF51822/analogin_api.c +++ b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF51822/analogin_api.c @@ -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 diff --git a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF51822/gpio_api.c b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF51822/gpio_api.c deleted file mode 100644 index eb077c9f10..0000000000 --- a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF51822/gpio_api.c +++ /dev/null @@ -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; - } -} diff --git a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF51822/gpio_irq_api.c b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF51822/gpio_irq_api.c deleted file mode 100644 index 9d2fcad2c4..0000000000 --- a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF51822/gpio_irq_api.c +++ /dev/null @@ -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 -#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; -} diff --git a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF51822/gpio_object.h b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF51822/gpio_object.h deleted file mode 100644 index fe6d6c1e05..0000000000 --- a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF51822/gpio_object.h +++ /dev/null @@ -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 diff --git a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF51822/i2c_api.c b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF51822/i2c_api.c deleted file mode 100644 index 06c4b8b457..0000000000 --- a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF51822/i2c_api.c +++ /dev/null @@ -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; iaddress_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); -} diff --git a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF51822/objects.h b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF51822/objects.h deleted file mode 100644 index a87e1d7687..0000000000 --- a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF51822/objects.h +++ /dev/null @@ -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 diff --git a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF51822/port_api.c b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF51822/port_api.c deleted file mode 100644 index 4168a7845c..0000000000 --- a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF51822/port_api.c +++ /dev/null @@ -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); -} diff --git a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF51822/pwmout_api.c b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF51822/pwmout_api.c index 1ebe182dd5..063b94d37b 100644 --- a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF51822/pwmout_api.c +++ b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF51822/pwmout_api.c @@ -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 && (ipwm = 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 diff --git a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sdk/drivers_nrf/config/nrf_drv_config.h b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF51822/sdk/nrf_drv_config.h similarity index 94% rename from hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sdk/drivers_nrf/config/nrf_drv_config.h rename to hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF51822/sdk/nrf_drv_config.h index 6fcba3826e..31df5d63a5 100644 --- a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sdk/drivers_nrf/config/nrf_drv_config.h +++ b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF51822/sdk/nrf_drv_config.h @@ -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 diff --git a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF51822/serial_api.c b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF51822/serial_api.c deleted file mode 100644 index e48c929ded..0000000000 --- a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF51822/serial_api.c +++ /dev/null @@ -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 -#include -#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 (baudrateuart->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<uart->PSELRTS = rxflow; - - obj->uart->CONFIG |= 0x01; // Enable HWFC - } - - if (type == FlowControlRTSCTS || type == FlowControlCTS) { - NRF_GPIO->DIR &= ~(1<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) { -} diff --git a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF51822/spi_api.c b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF51822/spi_api.c deleted file mode 100644 index 35ad304b85..0000000000 --- a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF51822/spi_api.c +++ /dev/null @@ -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 -#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<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<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; -} diff --git a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF51822/twi_config.h b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF51822/twi_config.h deleted file mode 100644 index a5ad887d14..0000000000 --- a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF51822/twi_config.h +++ /dev/null @@ -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 diff --git a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF51822/twi_master.c b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF51822/twi_master.c deleted file mode 100644 index d7a02f0541..0000000000 --- a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF51822/twi_master.c +++ /dev/null @@ -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 -#include -#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" */ diff --git a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF51822/twi_master.h b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF51822/twi_master.h deleted file mode 100644 index 6a6593f87b..0000000000 --- a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF51822/twi_master.h +++ /dev/null @@ -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 -#include - -#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 diff --git a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF51822/us_ticker.c b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF51822/us_ticker.c deleted file mode 100644 index abbe793a52..0000000000 --- a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF51822/us_ticker.c +++ /dev/null @@ -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 -#include -#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; -} diff --git a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF52832/analogin_api.c b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF52832/analogin_api.c index c97ed15857..af4929d8ed 100644 --- a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF52832/analogin_api.c +++ b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF52832/analogin_api.c @@ -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 diff --git a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF52832/gpio_api.c b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF52832/gpio_api.c deleted file mode 100644 index eb077c9f10..0000000000 --- a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF52832/gpio_api.c +++ /dev/null @@ -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; - } -} diff --git a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF52832/gpio_irq_api.c b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF52832/gpio_irq_api.c deleted file mode 100644 index 9d2fcad2c4..0000000000 --- a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF52832/gpio_irq_api.c +++ /dev/null @@ -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 -#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; -} diff --git a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF52832/i2c_api.c b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF52832/i2c_api.c deleted file mode 100644 index 17989f3d0a..0000000000 --- a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF52832/i2c_api.c +++ /dev/null @@ -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; iaddress_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); -} diff --git a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF52832/pwmout_api.c b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF52832/pwmout_api.c index 6d5883f141..cf28491102 100644 --- a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF52832/pwmout_api.c +++ b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF52832/pwmout_api.c @@ -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 && (ipwm = 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 diff --git a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF52832/sdk/nrf_drv_config.h b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF52832/sdk/nrf_drv_config.h new file mode 100644 index 0000000000..bdf509e0a5 --- /dev/null +++ b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF52832/sdk/nrf_drv_config.h @@ -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 diff --git a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF52832/serial_api.c b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF52832/serial_api.c deleted file mode 100644 index 7abccc2e45..0000000000 --- a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF52832/serial_api.c +++ /dev/null @@ -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 -#include - -#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 (baudrateuart->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<uart->PSELRTS = rxflow; - - obj->uart->CONFIG |= 0x01; // Enable HWFC - } - - if (type == FlowControlRTSCTS || type == FlowControlCTS) { - NRF_GPIO->DIR &= ~(1<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; -} diff --git a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF52832/sleep.c b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF52832/sleep.c deleted file mode 100644 index 44aa8ae59c..0000000000 --- a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF52832/sleep.c +++ /dev/null @@ -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; -} diff --git a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF52832/spi_api.c b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF52832/spi_api.c deleted file mode 100644 index c26590587d..0000000000 --- a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF52832/spi_api.c +++ /dev/null @@ -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 -#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<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<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; -} diff --git a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF52832/us_ticker.c b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF52832/us_ticker.c deleted file mode 100644 index db558a7883..0000000000 --- a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF52832/us_ticker.c +++ /dev/null @@ -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 -#include -#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; -} diff --git a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/gpio_api.c b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/gpio_api.c new file mode 100644 index 0000000000..f9d281ecaf --- /dev/null +++ b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/gpio_api.c @@ -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); + } +} diff --git a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF52832/gpio_object.h b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/gpio_object.h similarity index 74% rename from hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF52832/gpio_object.h rename to hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/gpio_object.h index fe6d6c1e05..f7b621a9ed 100644 --- a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF52832/gpio_object.h +++ b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/gpio_object.h @@ -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) { diff --git a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/i2c_api.c b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/i2c_api.c new file mode 100644 index 0000000000..5fc4720c5e --- /dev/null +++ b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/i2c_api.c @@ -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 diff --git a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/lp_ticker.c b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/lp_ticker.c new file mode 100644 index 0000000000..ca292114ea --- /dev/null +++ b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/lp_ticker.c @@ -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 diff --git a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF52832/objects.h b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/objects.h similarity index 72% rename from hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF52832/objects.h rename to hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/objects.h index 3c2a093dd4..88090c3cca 100644 --- a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF52832/objects.h +++ b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/objects.h @@ -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 { diff --git a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF52832/pinmap.c b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/pinmap.c similarity index 100% rename from hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF52832/pinmap.c rename to hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/pinmap.c diff --git a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF52832/port_api.c b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/port_api.c similarity index 100% rename from hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF52832/port_api.c rename to hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/port_api.c diff --git a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/rtc_api.c b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/rtc_api.c new file mode 100644 index 0000000000..4792335375 --- /dev/null +++ b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/rtc_api.c @@ -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 diff --git a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF51822/pinmap.c b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/rtc_common.h similarity index 57% rename from hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF51822/pinmap.c rename to hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/rtc_common.h index 44ca32c039..43ec5ff512 100644 --- a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF51822/pinmap.c +++ b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/rtc_common.h @@ -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 diff --git a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sdk/drivers_nrf/gpiote/nrf_drv_gpiote.c b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sdk/drivers_nrf/gpiote/nrf_drv_gpiote.c new file mode 100644 index 0000000000..6dfe91a204 --- /dev/null +++ b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sdk/drivers_nrf/gpiote/nrf_drv_gpiote.c @@ -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 diff --git a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sdk/drivers_nrf/gpiote/nrf_drv_gpiote.h b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sdk/drivers_nrf/gpiote/nrf_drv_gpiote.h new file mode 100644 index 0000000000..9a7fc341b1 --- /dev/null +++ b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sdk/drivers_nrf/gpiote/nrf_drv_gpiote.h @@ -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 +#include + +/**@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__ diff --git a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sdk/drivers_nrf/hal/nrf_ppi.h b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sdk/drivers_nrf/hal/nrf_ppi.h new file mode 100644 index 0000000000..f7fcf47fde --- /dev/null +++ b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sdk/drivers_nrf/hal/nrf_ppi.h @@ -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 +#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__ diff --git a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sdk/drivers_nrf/hal/nrf_rtc.h b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sdk/drivers_nrf/hal/nrf_rtc.h new file mode 100644 index 0000000000..32e4d2f178 --- /dev/null +++ b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sdk/drivers_nrf/hal/nrf_rtc.h @@ -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 +#include +#include +#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 */ diff --git a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sdk/drivers_nrf/hal/nrf_spi.h b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sdk/drivers_nrf/hal/nrf_spi.h new file mode 100644 index 0000000000..2d3897d77f --- /dev/null +++ b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sdk/drivers_nrf/hal/nrf_spi.h @@ -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 +#include +#include + +#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__ + +/** @} */ diff --git a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sdk/drivers_nrf/hal/nrf_spim.h b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sdk/drivers_nrf/hal/nrf_spim.h new file mode 100644 index 0000000000..279f483df6 --- /dev/null +++ b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sdk/drivers_nrf/hal/nrf_spim.h @@ -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 +#include +#include + +#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__ + +/** @} */ diff --git a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sdk/drivers_nrf/hal/nrf_timer.h b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sdk/drivers_nrf/hal/nrf_timer.h new file mode 100644 index 0000000000..3da5160e78 --- /dev/null +++ b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sdk/drivers_nrf/hal/nrf_timer.h @@ -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 +#include +#include + +#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__ + +/** @} */ diff --git a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sdk/drivers_nrf/hal/nrf_twi.h b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sdk/drivers_nrf/hal/nrf_twi.h new file mode 100644 index 0000000000..8cf448ba9a --- /dev/null +++ b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sdk/drivers_nrf/hal/nrf_twi.h @@ -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 +#include +#include + +#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__ diff --git a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sdk/drivers_nrf/hal/nrf_uart.h b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sdk/drivers_nrf/hal/nrf_uart.h new file mode 100644 index 0000000000..4cac9402b7 --- /dev/null +++ b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sdk/drivers_nrf/hal/nrf_uart.h @@ -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 +#include +#include + +/** + * @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__ diff --git a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sdk/drivers_nrf/hal/nrf_uarte.h b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sdk/drivers_nrf/hal/nrf_uarte.h new file mode 100644 index 0000000000..8a4d42e7db --- /dev/null +++ b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sdk/drivers_nrf/hal/nrf_uarte.h @@ -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 +#include +#include + +#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__ diff --git a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sdk/drivers_nrf/ppi/nrf_drv_ppi.c b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sdk/drivers_nrf/ppi/nrf_drv_ppi.c new file mode 100644 index 0000000000..e487279de8 --- /dev/null +++ b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sdk/drivers_nrf/ppi/nrf_drv_ppi.c @@ -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 + +#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; +} diff --git a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sdk/drivers_nrf/ppi/nrf_drv_ppi.h b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sdk/drivers_nrf/ppi/nrf_drv_ppi.h new file mode 100644 index 0000000000..bd1c743879 --- /dev/null +++ b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sdk/drivers_nrf/ppi/nrf_drv_ppi.h @@ -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 +#include + +/** @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 diff --git a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sdk/drivers_nrf/rtc/nrf_drv_rtc.c b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sdk/drivers_nrf/rtc/nrf_drv_rtc.c new file mode 100644 index 0000000000..0af7ad3204 --- /dev/null +++ b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sdk/drivers_nrf/rtc/nrf_drv_rtc.c @@ -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(channelcc_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(channelcc_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 diff --git a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sdk/drivers_nrf/rtc/nrf_drv_rtc.h b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sdk/drivers_nrf/rtc/nrf_drv_rtc.h new file mode 100644 index 0000000000..c7148c89c6 --- /dev/null +++ b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sdk/drivers_nrf/rtc/nrf_drv_rtc.h @@ -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 */ diff --git a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sdk/drivers_nrf/spi_master/nrf_drv_spi.c b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sdk/drivers_nrf/spi_master/nrf_drv_spi.c new file mode 100644 index 0000000000..61f8527616 --- /dev/null +++ b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sdk/drivers_nrf/spi_master/nrf_drv_spi.c @@ -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 diff --git a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sdk/drivers_nrf/spi_master/nrf_drv_spi.h b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sdk/drivers_nrf/spi_master/nrf_drv_spi.h new file mode 100644 index 0000000000..aa63a3a728 --- /dev/null +++ b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sdk/drivers_nrf/spi_master/nrf_drv_spi.h @@ -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: + * Post-incrementation of buffer addresses. Supported only by SPIM. + * - @ref NRF_DRV_SPI_FLAG_HOLD_XFER: 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: 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: 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__ + +/** @} */ diff --git a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sdk/drivers_nrf/timer/nrf_drv_timer.c b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sdk/drivers_nrf/timer/nrf_drv_timer.c new file mode 100644 index 0000000000..41985d2fef --- /dev/null +++ b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sdk/drivers_nrf/timer/nrf_drv_timer.c @@ -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 diff --git a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sdk/drivers_nrf/timer/nrf_drv_timer.h b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sdk/drivers_nrf/timer/nrf_drv_timer.h new file mode 100644 index 0000000000..25d3bf84ba --- /dev/null +++ b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sdk/drivers_nrf/timer/nrf_drv_timer.h @@ -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__ + +/** @} */ diff --git a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sdk/drivers_nrf/twi_master/nrf_drv_twi.c b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sdk/drivers_nrf/twi_master/nrf_drv_twi.c new file mode 100644 index 0000000000..709fac5fb7 --- /dev/null +++ b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sdk/drivers_nrf/twi_master/nrf_drv_twi.c @@ -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 + +#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 diff --git a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sdk/drivers_nrf/twi_master/nrf_drv_twi.h b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sdk/drivers_nrf/twi_master/nrf_drv_twi.h new file mode 100644 index 0000000000..ee3b70c188 --- /dev/null +++ b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sdk/drivers_nrf/twi_master/nrf_drv_twi.h @@ -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: Write operation followed by a read operation (without STOP condition in between). + * - @ref NRF_DRV_TWI_XFER_TXTX: Write operation followed by a write operation (without STOP condition in between). + * - @ref NRF_DRV_TWI_XFER_TX: Write operation (with or without STOP condition). + * - @ref NRF_DRV_TWI_XFER_RX: 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: Post-incrementation of buffer addresses. Supported only by TWIM. + * - @ref NRF_DRV_TWI_FLAG_NO_XFER_EVT_HANDLER: 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: 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: 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: 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__ diff --git a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sdk/drivers_nrf/uart/nrf_drv_uart.c b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sdk/drivers_nrf/uart/nrf_drv_uart.c new file mode 100644 index 0000000000..a62cff1dd6 --- /dev/null +++ b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sdk/drivers_nrf/uart/nrf_drv_uart.c @@ -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(); + ) +} diff --git a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sdk/drivers_nrf/uart/nrf_drv_uart.h b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sdk/drivers_nrf/uart/nrf_drv_uart.h new file mode 100644 index 0000000000..53c9498259 --- /dev/null +++ b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sdk/drivers_nrf/uart/nrf_drv_uart.h @@ -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 +/** @} */ diff --git a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sdk/libraries/pwm/app_pwm.c b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sdk/libraries/pwm/app_pwm.c new file mode 100644 index 0000000000..db5c810419 --- /dev/null +++ b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sdk/libraries/pwm/app_pwm.c @@ -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 diff --git a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sdk/libraries/pwm/app_pwm.h b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sdk/libraries/pwm/app_pwm.h new file mode 100644 index 0000000000..7c619f1951 --- /dev/null +++ b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sdk/libraries/pwm/app_pwm.h @@ -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 +#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 + +/** @} */ diff --git a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/serial_api.c b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/serial_api.c new file mode 100644 index 0000000000..311a46f1b3 --- /dev/null +++ b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/serial_api.c @@ -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 +#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; +} diff --git a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF51822/sleep.c b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sleep.c similarity index 100% rename from hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF51822/sleep.c rename to hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/sleep.c diff --git a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/spi_api.c b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/spi_api.c new file mode 100644 index 0000000000..affd19f4e8 --- /dev/null +++ b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/spi_api.c @@ -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 + +#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 diff --git a/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/us_ticker.c b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/us_ticker.c new file mode 100644 index 0000000000..b35670ceae --- /dev/null +++ b/hal/targets/hal/TARGET_NORDIC/TARGET_NRF5/us_ticker.c @@ -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'. +}