Initial commit

This commit is contained in:
2026-04-24 10:52:47 +09:00
commit 1707dc6349
1423 changed files with 1161776 additions and 0 deletions
+159
View File
@@ -0,0 +1,159 @@
/**
* Copyright (c) 2016 - 2021, 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, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, 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 its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA 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_BITMASK_H
#define NRF_BITMASK_H
#include <nrfx.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @defgroup nrf_bitmask Bitmask module
* @{
* @ingroup nrfx
* @brief Bitmask managing module.
*/
/** @brief Macro for getting index of byte in byte stream where @c abs_bit is put. */
#define BITMASK_BYTE_GET(abs_bit) ((abs_bit)/8)
/** @brief Macro for getting relative index of bit in byte. */
#define BITMASK_RELBIT_GET(abs_bit) ((abs_bit) & 0x00000007)
/**
* @brief Function for checking if bit in the multi-byte bit mask is set.
*
* @param[in] bit Bit index.
* @param[in] p_mask Pointer to mask with bit fields.
*
* @return 0 if bit is not set, positive value otherwise.
*/
__STATIC_INLINE uint32_t nrf_bitmask_bit_is_set(uint32_t bit, void const * p_mask)
{
uint8_t const * p_mask8 = (uint8_t const *)p_mask;
uint32_t byte_idx = BITMASK_BYTE_GET(bit);
bit = BITMASK_RELBIT_GET(bit);
return (1 << bit) & p_mask8[byte_idx];
}
/**
* @brief Function for setting a bit in the multi-byte bit mask.
*
* @param[in] bit Bit index.
* @param[in] p_mask Pointer to mask with bit fields.
*/
__STATIC_INLINE void nrf_bitmask_bit_set(uint32_t bit, void * p_mask)
{
uint8_t * p_mask8 = (uint8_t *)p_mask;
uint32_t byte_idx = BITMASK_BYTE_GET(bit);
bit = BITMASK_RELBIT_GET(bit);
p_mask8[byte_idx] |= (1 << bit);
}
/**
* @brief Function for clearing a bit in the multi-byte bit mask.
*
* @param[in] bit Bit index.
* @param[in] p_mask Pointer to mask with bit fields.
*/
__STATIC_INLINE void nrf_bitmask_bit_clear(uint32_t bit, void * p_mask)
{
uint8_t * p_mask8 = (uint8_t *)p_mask;
uint32_t byte_idx = BITMASK_BYTE_GET(bit);
bit = BITMASK_RELBIT_GET(bit);
p_mask8[byte_idx] &= ~(1 << bit);
}
/**
* @brief Function for performing bitwise OR operation on two multi-byte bit masks.
*
* @param[in] p_mask1 Pointer to the first bit mask.
* @param[in] p_mask2 Pointer to the second bit mask.
* @param[in] p_out_mask Pointer to the output bit mask.
* @param[in] length Length of output mask in bytes.
*/
__STATIC_INLINE void nrf_bitmask_masks_or(void const * p_mask1,
void const * p_mask2,
void * p_out_mask,
uint32_t length)
{
uint8_t const * p_mask8_1 = (uint8_t const *)p_mask1;
uint8_t const * p_mask8_2 = (uint8_t const *)p_mask2;
uint8_t * p_mask8_out = (uint8_t *)p_out_mask;
uint32_t i;
for (i = 0; i < length; i++)
{
p_mask8_out[i] = p_mask8_1[i] | p_mask8_2[i];
}
}
/**
* @brief Function for performing bitwise AND operation on two multi-byte bit masks.
*
* @param[in] p_mask1 Pointer to the first bit mask.
* @param[in] p_mask2 Pointer to the second bit mask.
* @param[in] p_out_mask Pointer to the output bit mask.
* @param[in] length Length of output mask in bytes.
*/
__STATIC_INLINE void nrf_bitmask_masks_and(void const * p_mask1,
void const * p_mask2,
void * p_out_mask,
uint32_t length)
{
uint8_t const * p_mask8_1 = (uint8_t const *)p_mask1;
uint8_t const * p_mask8_2 = (uint8_t const *)p_mask2;
uint8_t * p_mask8_out = (uint8_t *)p_out_mask;
uint32_t i;
for (i = 0; i < length; i++)
{
p_mask8_out[i] = p_mask8_1[i] & p_mask8_2[i];
}
}
/** @} */
#ifdef __cplusplus
}
#endif
#endif // NRF_BITMASK_H
+206
View File
@@ -0,0 +1,206 @@
/**
* Copyright (c) 2016 - 2021, 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, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, 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 its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA 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 NRFX_CLOCK_H__
#define NRFX_CLOCK_H__
#include <nrfx.h>
#include <hal/nrf_clock.h>
#include <nrfx_power_clock.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @defgroup nrfx_clock CLOCK driver
* @{
* @ingroup nrf_clock
* @brief CLOCK peripheral driver.
*/
/** @brief Clock events. */
typedef enum
{
NRFX_CLOCK_EVT_HFCLK_STARTED, ///< HFCLK has been started.
NRFX_CLOCK_EVT_LFCLK_STARTED, ///< LFCLK has been started.
NRFX_CLOCK_EVT_CTTO, ///< Calibration timeout.
NRFX_CLOCK_EVT_CAL_DONE ///< Calibration has been done.
} nrfx_clock_evt_type_t;
/**
* @brief Clock event handler.
*
* @param[in] event Event.
*/
typedef void (*nrfx_clock_event_handler_t)(nrfx_clock_evt_type_t event);
/**
* @brief Function for initializing internal structures in the nrfx_clock module.
*
* After initialization, the module is in power off state (clocks are not started).
*
* @param[in] event_handler Event handler provided by the user.
* Must not be NULL.
*
* @retval NRFX_SUCCESS The procedure is successful.
* @retval NRFX_ERROR_ALREADY_INITIALIZED The driver is already initialized.
*/
nrfx_err_t nrfx_clock_init(nrfx_clock_event_handler_t event_handler);
/** @brief Function for enabling interrupts in the clock module. */
void nrfx_clock_enable(void);
/** @brief Function for disabling interrupts in the clock module. */
void nrfx_clock_disable(void);
/** @brief Function for uninitializing the clock module. */
void nrfx_clock_uninit(void);
/** @brief Function for starting the LFCLK. */
void nrfx_clock_lfclk_start(void);
/** @brief Function for stopping the LFCLK. */
void nrfx_clock_lfclk_stop(void);
/**
* @brief Function for checking the LFCLK state.
*
* @retval true The LFCLK is running.
* @retval false The LFCLK is not running.
*/
__STATIC_INLINE bool nrfx_clock_lfclk_is_running(void);
/** @brief Function for starting the high-accuracy source HFCLK. */
void nrfx_clock_hfclk_start(void);
/** @brief Function for stopping the external high-accuracy source HFCLK. */
void nrfx_clock_hfclk_stop(void);
/**
* @brief Function for checking the HFCLK state.
*
* @retval true The HFCLK is running (XTAL source).
* @retval false The HFCLK is not running.
*/
__STATIC_INLINE bool nrfx_clock_hfclk_is_running(void);
/**
* @brief Function for starting the calibration of internal LFCLK.
*
* This function starts the calibration process. The process cannot be aborted. LFCLK and HFCLK
* must be running before this function is called.
*
* @retval NRFX_SUCCESS The procedure is successful.
* @retval NRFX_ERROR_INVALID_STATE The low-frequency of high-frequency clock is off.
* @retval NRFX_ERROR_BUSY Clock is in the calibration phase.
*/
nrfx_err_t nrfx_clock_calibration_start(void);
/**
* @brief Function for checking if calibration is in progress.
*
* This function indicates that the system is in calibration phase.
*
* @retval NRFX_SUCCESS The procedure is successful.
* @retval NRFX_ERROR_BUSY Clock is in the calibration phase.
*/
nrfx_err_t nrfx_clock_is_calibrating(void);
/**
* @brief Function for starting calibration timer.
*
* @param[in] interval Time after which the CTTO event and interrupt will be generated (in 0.25 s units).
*/
void nrfx_clock_calibration_timer_start(uint8_t interval);
/** @brief Function for stopping the calibration timer. */
void nrfx_clock_calibration_timer_stop(void);
/**@brief Function for returning a requested task address for the clock driver module.
*
* @param[in] task One of the peripheral tasks.
*
* @return Task address.
*/
__STATIC_INLINE uint32_t nrfx_clock_ppi_task_addr(nrf_clock_task_t task);
/**@brief Function for returning a requested event address for the clock driver module.
*
* @param[in] event One of the peripheral events.
*
* @return Event address.
*/
__STATIC_INLINE uint32_t nrfx_clock_ppi_event_addr(nrf_clock_event_t event);
#ifndef SUPPRESS_INLINE_IMPLEMENTATION
__STATIC_INLINE uint32_t nrfx_clock_ppi_task_addr(nrf_clock_task_t task)
{
return nrf_clock_task_address_get(task);
}
__STATIC_INLINE uint32_t nrfx_clock_ppi_event_addr(nrf_clock_event_t event)
{
return nrf_clock_event_address_get(event);
}
__STATIC_INLINE bool nrfx_clock_hfclk_is_running(void)
{
return nrf_clock_hf_is_running(NRF_CLOCK_HFCLK_HIGH_ACCURACY);
}
__STATIC_INLINE bool nrfx_clock_lfclk_is_running(void)
{
return nrf_clock_lf_is_running();
}
#endif //SUPPRESS_INLINE_IMPLEMENTATION
/** @} */
void nrfx_clock_irq_handler(void);
#ifdef __cplusplus
}
#endif
#endif // NRFX_CLOCK_H__
+494
View File
@@ -0,0 +1,494 @@
/**
* Copyright (c) 2015 - 2021, 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, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, 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 its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA 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 NRFX_GPIOTE_H__
#define NRFX_GPIOTE_H__
#include <nrfx.h>
#include <hal/nrf_gpiote.h>
#include <hal/nrf_gpio.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @defgroup nrfx_gpiote GPIOTE driver
* @{
* @ingroup nrf_gpiote
* @brief GPIO Task Event (GPIOTE) peripheral driver.
*/
/** @brief Input pin configuration. */
typedef struct
{
nrf_gpiote_polarity_t sense; /**< Transition that triggers the interrupt. */
nrf_gpio_pin_pull_t pull; /**< Pulling mode. */
bool is_watcher : 1; /**< True when the input pin is tracking an output pin. */
bool hi_accuracy : 1; /**< True when high accuracy (IN_EVENT) is used. */
bool skip_gpio_setup : 1; /**< Do not change GPIO configuration */
} nrfx_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 NRFX_GPIOTE_CONFIG_IN_SENSE_LOTOHI(hi_accu) \
{ \
.sense = NRF_GPIOTE_POLARITY_LOTOHI, \
.pull = NRF_GPIO_PIN_NOPULL, \
.is_watcher = false, \
.hi_accuracy = hi_accu, \
.skip_gpio_setup = false, \
}
/**
* @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 NRFX_GPIOTE_CONFIG_IN_SENSE_HITOLO(hi_accu) \
{ \
.sense = NRF_GPIOTE_POLARITY_HITOLO, \
.pull = NRF_GPIO_PIN_NOPULL, \
.is_watcher = false, \
.hi_accuracy = hi_accu, \
.skip_gpio_setup = false, \
}
/**
* @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 NRFX_GPIOTE_CONFIG_IN_SENSE_TOGGLE(hi_accu) \
{ \
.sense = NRF_GPIOTE_POLARITY_TOGGLE, \
.pull = NRF_GPIO_PIN_NOPULL, \
.is_watcher = false, \
.hi_accuracy = hi_accu, \
.skip_gpio_setup = false, \
}
/**
* @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.
* @note This macro prepares configuration that skips the GPIO setup.
*/
#define NRFX_GPIOTE_RAW_CONFIG_IN_SENSE_LOTOHI(hi_accu) \
{ \
.sense = NRF_GPIOTE_POLARITY_LOTOHI, \
.pull = NRF_GPIO_PIN_NOPULL, \
.is_watcher = false, \
.hi_accuracy = hi_accu, \
.skip_gpio_setup = true, \
}
/**
* @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.
* @note This macro prepares configuration that skips the GPIO setup.
*/
#define NRFX_GPIOTE_RAW_CONFIG_IN_SENSE_HITOLO(hi_accu) \
{ \
.sense = NRF_GPIOTE_POLARITY_HITOLO, \
.pull = NRF_GPIO_PIN_NOPULL, \
.is_watcher = false, \
.hi_accuracy = hi_accu, \
.skip_gpio_setup = true, \
}
/**
* @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.
* @note This macro prepares configuration that skips the GPIO setup.
*/
#define NRFX_GPIOTE_RAW_CONFIG_IN_SENSE_TOGGLE(hi_accu) \
{ \
.sense = NRF_GPIOTE_POLARITY_TOGGLE, \
.pull = NRF_GPIO_PIN_NOPULL, \
.is_watcher = false, \
.hi_accuracy = hi_accu, \
.skip_gpio_setup = true, \
}
/** @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. */
} nrfx_gpiote_out_config_t;
/** @brief Macro for configuring a pin to use as output. GPIOTE is not used for the pin. */
#define NRFX_GPIOTE_CONFIG_OUT_SIMPLE(init_high) \
{ \
.action = NRF_GPIOTE_POLARITY_LOTOHI, \
.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 NRFX_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 NRFX_GPIOTE_CONFIG_OUT_TASK_HIGH \
{ \
.action = NRF_GPIOTE_POLARITY_LOTOHI, \
.init_state = NRF_GPIOTE_INITIAL_VALUE_LOW, \
.task_pin = true, \
}
/**
* @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 NRFX_GPIOTE_CONFIG_OUT_TASK_TOGGLE(init_high) \
{ \
.action = NRF_GPIOTE_POLARITY_TOGGLE, \
.init_state = init_high ? NRF_GPIOTE_INITIAL_VALUE_HIGH : NRF_GPIOTE_INITIAL_VALUE_LOW, \
.task_pin = true, \
}
/** @brief Pin. */
typedef uint32_t nrfx_gpiote_pin_t;
/**
* @brief Pin event handler prototype.
*
* @param[in] pin Pin that triggered this event.
* @param[in] action Action that led to triggering this event.
*/
typedef void (*nrfx_gpiote_evt_handler_t)(nrfx_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 NRFX_SUCCESS Initialization was successful.
* @retval NRFX_ERROR_INVALID_STATE The driver was already initialized.
*/
nrfx_err_t nrfx_gpiote_init(void);
/**
* @brief Function for checking if the GPIOTE module is initialized.
*
* @details The GPIOTE module is a shared module. Therefore, check if
* the module is already initialized and skip initialization if it is.
*
* @retval true The module is already initialized.
* @retval false The module is not initialized.
*/
bool nrfx_gpiote_is_init(void);
/** @brief Function for uninitializing the GPIOTE module. */
void nrfx_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 NRFX_SUCCESS Initialization was successful.
* @retval NRFX_ERROR_INVALID_STATE The driver is not initialized or the pin is already used.
* @retval NRFX_ERROR_NO_MEM No GPIOTE channel is available.
*/
nrfx_err_t nrfx_gpiote_out_init(nrfx_gpiote_pin_t pin,
nrfx_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 nrfx_gpiote_out_uninit(nrfx_gpiote_pin_t pin);
/**
* @brief Function for setting a GPIOTE output pin.
*
* @param[in] pin Pin.
*/
void nrfx_gpiote_out_set(nrfx_gpiote_pin_t pin);
/**
* @brief Function for clearing a GPIOTE output pin.
*
* @param[in] pin Pin.
*/
void nrfx_gpiote_out_clear(nrfx_gpiote_pin_t pin);
/**
* @brief Function for toggling a GPIOTE output pin.
*
* @param[in] pin Pin.
*/
void nrfx_gpiote_out_toggle(nrfx_gpiote_pin_t pin);
/**
* @brief Function for enabling a GPIOTE output pin task.
*
* @param[in] pin Pin.
*/
void nrfx_gpiote_out_task_enable(nrfx_gpiote_pin_t pin);
/**
* @brief Function for disabling a GPIOTE output pin task.
*
* @param[in] pin Pin.
*/
void nrfx_gpiote_out_task_disable(nrfx_gpiote_pin_t pin);
/**
* @brief Function for getting the OUT task for the specified output pin.
*
* @details The returned task identifier can be used within @ref nrf_gpiote_hal,
* for example, to configure a DPPI channel.
*
* @param[in] pin Pin.
*
* @return OUT task associated with the specified output pin.
*/
nrf_gpiote_tasks_t nrfx_gpiote_out_task_get(nrfx_gpiote_pin_t pin);
/**
* @brief Function for getting the address of the OUT task for the specified output pin.
*
* @param[in] pin Pin.
*
* @return Address of OUT task.
*/
uint32_t nrfx_gpiote_out_task_addr_get(nrfx_gpiote_pin_t pin);
#if defined(GPIOTE_FEATURE_SET_PRESENT) || defined(__NRFX_DOXYGEN__)
/**
* @brief Function for getting the SET task for the specified output pin.
*
* @details The returned task identifier can be used within @ref nrf_gpiote_hal,
* for example, to configure a DPPI channel.
*
* @param[in] pin Pin.
*
* @return SET task associated with the specified output pin.
*/
nrf_gpiote_tasks_t nrfx_gpiote_set_task_get(nrfx_gpiote_pin_t pin);
/**
* @brief Function for getting the address of the SET task for the specified output pin.
*
* @param[in] pin Pin.
*
* @return Address of SET task.
*/
uint32_t nrfx_gpiote_set_task_addr_get(nrfx_gpiote_pin_t pin);
#endif // defined(GPIOTE_FEATURE_SET_PRESENT) || defined(__NRFX_DOXYGEN__)
#if defined(GPIOTE_FEATURE_CLR_PRESENT) || defined(__NRFX_DOXYGEN__)
/**
* @brief Function for getting the CLR task for the specified output pin.
*
* @details The returned task identifier can be used within @ref nrf_gpiote_hal,
* for example, to configure a DPPI channel.
*
* @param[in] pin Pin.
*
* @return CLR task associated with the specified output pin.
*/
nrf_gpiote_tasks_t nrfx_gpiote_clr_task_get(nrfx_gpiote_pin_t pin);
/**
* @brief Function for getting the address of the SET task for the specified output pin.
*
* @param[in] pin Pin.
*
* @return Address of CLR task.
*/
uint32_t nrfx_gpiote_clr_task_addr_get(nrfx_gpiote_pin_t pin);
#endif // defined(GPIOTE_FEATURE_CLR_PRESENT) || defined(__NRFX_DOXYGEN__)
/**
* @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 NRFX_SUCCESS Initialization was successful.
* @retval NRFX_ERROR_INVALID_STATE The driver is not initialized or the pin is already used.
* @retval NRFX_ERROR_NO_MEM No GPIOTE channel is available.
*/
nrfx_err_t nrfx_gpiote_in_init(nrfx_gpiote_pin_t pin,
nrfx_gpiote_in_config_t const * p_config,
nrfx_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 nrfx_gpiote_in_uninit(nrfx_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.
* The 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 nrfx_gpiote_in_event_enable(nrfx_gpiote_pin_t pin, bool int_enable);
/**
* @brief Function for disabling a GPIOTE input pin.
*
* @param[in] pin Pin.
*/
void nrfx_gpiote_in_event_disable(nrfx_gpiote_pin_t pin);
/**
* @brief Function for checking if a GPIOTE input pin is set.
*
* @param[in] pin Pin.
*
* @retval true The input pin is set.
* @retval false The input pin is not set.
*/
bool nrfx_gpiote_in_is_set(nrfx_gpiote_pin_t pin);
/**
* @brief Function for getting the GPIOTE event for the specified input pin.
*
* @details The returned event identifier can be used within @ref nrf_gpiote_hal,
* for example, to configure a DPPI channel.
* If the pin is configured to use low-accuracy mode, the PORT event
* is returned.
*
* @param[in] pin Pin.
*
* @return Event associated with the specified input pin.
*/
nrf_gpiote_events_t nrfx_gpiote_in_event_get(nrfx_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.
*
* @return Address of the specified input pin event.
*/
uint32_t nrfx_gpiote_in_event_addr_get(nrfx_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 nrfx_gpiote_out_task_force(nrfx_gpiote_pin_t pin, uint8_t state);
/**
* @brief Function for triggering the task OUT manually.
*
* @param[in] pin Pin.
*/
void nrfx_gpiote_out_task_trigger(nrfx_gpiote_pin_t pin);
#if defined(GPIOTE_FEATURE_SET_PRESENT) || defined(__NRFX_DOXYGEN__)
/**
* @brief Function for triggering the task SET manually.
*
* @param[in] pin Pin.
*/
void nrfx_gpiote_set_task_trigger(nrfx_gpiote_pin_t pin);
#endif // defined(GPIOTE_FEATURE_SET_PRESENT) || defined(__NRFX_DOXYGEN__)
#if defined(GPIOTE_FEATURE_CLR_PRESENT) || defined(__NRFX_DOXYGEN__)
/**
* @brief Function for triggering the task CLR manually.
*
* @param[in] pin Pin.
*/
void nrfx_gpiote_clr_task_trigger(nrfx_gpiote_pin_t pin);
#endif // defined(GPIOTE_FEATURE_CLR_PRESENT) || defined(__NRFX_DOXYGEN__)
/** @} */
void nrfx_gpiote_irq_handler(void);
#ifdef __cplusplus
}
#endif
#endif // NRFX_GPIOTE_H__
+295
View File
@@ -0,0 +1,295 @@
/**
* Copyright (c) 2019 - 2021, 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, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, 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 its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA 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 NRFX_NVMC_H__
#define NRFX_NVMC_H__
#include <nrfx.h>
#include <hal/nrf_nvmc.h>
#include <hal/nrf_ficr.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @defgroup nrfx_nvmc NVMC driver
* @{
* @ingroup nrf_nvmc
* @brief Non-Volatile Memory Controller (NVMC) peripheral driver.
*/
/**
* @brief Function for erasing a page in flash.
*
* This function blocks until the erase operation finishes.
*
* @note Depending on the source of the code being executed,
* the CPU may be halted during the operation.
* Refer to the Product Specification for more information.
*
* @param address Address of the first word in the page to erase.
*
* @retval NRFX_SUCCESS Page erase complete.
* @retval NRFX_ERROR_INVALID_ADDR Address is not aligned to the size of the page.
*/
nrfx_err_t nrfx_nvmc_page_erase(uint32_t address);
/**
* @brief Function for erasing the user information configuration register (UICR).
*
* @note Depending on the source of the code being executed,
* the CPU may be halted during the operation.
* Refer to the Product Specification for more information.
*
* @retval NRFX_SUCCESS UICR has been successfully erased.
* @retval NRFX_ERROR_NOT_SUPPORTED UICR erase is not supported.
*/
nrfx_err_t nrfx_nvmc_uicr_erase(void);
/**
* @brief Function for erasing the whole flash memory.
*
* @note All user code and UICR will be erased.
*/
void nrfx_nvmc_all_erase(void);
#if defined(NRF_NVMC_PARTIAL_ERASE_PRESENT)
/**
* @brief Function for initiating a complete page erase split into parts (also known as partial erase).
*
* This function initiates a partial erase with the specified duration.
* To execute each part of the partial erase, use @ref nrfx_nvmc_page_partial_erase_continue.
*
* @param address Address of the first word in the page to erase.
* @param duration_ms Time in milliseconds that each partial erase will take.
*
* @retval NRFX_SUCCESS Partial erase started.
* @retval NRFX_ERROR_INVALID_ADDR Address is not aligned to the size of the page.
*
* @sa nrfx_nvmc_page_partial_erase_continue()
*/
nrfx_err_t nrfx_nvmc_page_partial_erase_init(uint32_t address, uint32_t duration_ms);
/**
* @brief Function for performing a part of the complete page erase (also known as partial erase).
*
* This function must be called several times to erase the whole page, once for each erase part.
*
* @note The actual time needed to perform each part of the page erase is longer than the partial
* erase duration specified in the call to @ref nrfx_nvmc_page_partial_erase_init,
* since the NVMC peripheral needs certain additional amount of time to handle the process.
* For details regarding this additional time, see the "Electrical specification" section
* for the NVMC peripheral in the Product Specification.
*
* @note Using a page that was not completely erased leads to undefined behavior.
* Depending on the source of the code being executed,
* the CPU may be halted during the operation.
* Refer to the Product Specification for more information.
*
* @retval true Partial erase finished.
* @retval false Partial erase not finished.
* Call the function again to process the next part.
*/
bool nrfx_nvmc_page_partial_erase_continue(void);
#endif // defined(NRF_NVMC_PARTIAL_ERASE_PRESENT)
/**
* @brief Function for checking whether a byte is writable at the specified address.
*
* The NVMC is only able to write '0' to bits in the flash that are erased (set to '1').
* It cannot rewrite a bit back to '1'. This function checks if the value currently
* residing at the specified address can be transformed to the desired value
* without any '0' to '1' transitions.
*
* @param address Address to be checked.
* @param value Value to be checked.
*
* @retval true Byte can be written at the specified address.
* @retval false Byte cannot be written at the specified address.
* Erase the page or change the address.
*/
bool nrfx_nvmc_byte_writable_check(uint32_t address, uint8_t value);
/**
* @brief Function for writing a single byte to flash.
*
* To determine if the flash write has been completed, use @ref nrfx_nvmc_write_done_check().
*
* @note Depending on the source of the code being executed,
* the CPU may be halted during the operation.
* Refer to the Product Specification for more information.
*
* @param address Address to write to.
* @param value Value to write.
*/
void nrfx_nvmc_byte_write(uint32_t address, uint8_t value);
/**
* @brief Function for checking whether a word is writable at the specified address.
*
* The NVMC is only able to write '0' to bits in the Flash that are erased (set to '1').
* It cannot rewrite a bit back to '1'. This function checks if the value currently
* residing at the specified address can be transformed to the desired value
* without any '0' to '1' transitions.
*
* @param address Address to be checked. Must be word-aligned.
* @param value Value to be checked.
*
* @retval true Word can be written at the specified address.
* @retval false Word cannot be written at the specified address.
* Erase page or change address.
*/
bool nrfx_nvmc_word_writable_check(uint32_t address, uint32_t value);
/**
* @brief Function for writing a 32-bit word to flash.
*
* To determine if the flash write has been completed, use @ref nrfx_nvmc_write_done_check().
*
* @note Depending on the source of the code being executed,
* the CPU may be halted during the operation.
* Refer to the Product Specification for more information.
*
* @param address Address to write to. Must be word-aligned.
* @param value Value to write.
*/
void nrfx_nvmc_word_write(uint32_t address, uint32_t value);
/**
* @brief Function for writing consecutive bytes to flash.
*
* To determine if the last flash write has been completed, use @ref nrfx_nvmc_write_done_check().
*
* @note Depending on the source of the code being executed,
* the CPU may be halted during the operation.
* Refer to the Product Specification for more information.
*
* @param address Address to write to.
* @param src Pointer to the data to copy from.
* @param num_bytes Number of bytes to write.
*/
void nrfx_nvmc_bytes_write(uint32_t address, void const * src, uint32_t num_bytes);
/**
* @brief Function for writing consecutive words to flash.
*
* To determine if the last flash write has been completed, use @ref nrfx_nvmc_write_done_check().
*
* @note Depending on the source of the code being executed,
* the CPU may be halted during the operation.
* Refer to the Product Specification for more information.
*
* @param address Address to write to. Must be word-aligned.
* @param src Pointer to data to copy from. Must be word-aligned.
* @param num_words Number of words to write.
*/
void nrfx_nvmc_words_write(uint32_t address, void const * src, uint32_t num_words);
/**
* @brief Function for getting the total flash size in bytes.
*
* @return Flash total size in bytes.
*/
uint32_t nrfx_nvmc_flash_size_get(void);
/**
* @brief Function for getting the flash page size in bytes.
*
* @return Flash page size in bytes.
*/
uint32_t nrfx_nvmc_flash_page_size_get(void);
/**
* @brief Function for getting the flash page count.
*
* @return Flash page count.
*/
uint32_t nrfx_nvmc_flash_page_count_get(void);
/**
* @brief Function for checking if the last flash write has been completed.
*
* @retval true Last write completed successfully.
* @retval false Last write is still in progress.
*/
__STATIC_INLINE bool nrfx_nvmc_write_done_check(void);
#if defined(NRF_NVMC_ICACHE_PRESENT)
/**
* @brief Function for enabling the Instruction Cache (ICache).
*
* Enabling ICache reduces the amount of accesses to flash memory,
* which can boost performance and lower power consumption.
*/
__STATIC_INLINE void nrfx_nvmc_icache_enable(void);
/** @brief Function for disabling ICache. */
__STATIC_INLINE void nrfx_nvmc_icache_disable(void);
#endif // defined(NRF_NVMC_ICACHE_PRESENT)
#ifndef SUPPRESS_INLINE_IMPLEMENTATION
__STATIC_INLINE bool nrfx_nvmc_write_done_check(void)
{
return nrf_nvmc_ready_check(NRF_NVMC);
}
#if defined(NRF_NVMC_ICACHE_PRESENT)
__STATIC_INLINE void nrfx_nvmc_icache_enable(void)
{
nrf_nvmc_icache_config_set(NRF_NVMC, NRF_NVMC_ICACHE_ENABLE_WITH_PROFILING);
}
__STATIC_INLINE void nrfx_nvmc_icache_disable(void)
{
nrf_nvmc_icache_config_set(NRF_NVMC, NRF_NVMC_ICACHE_DISABLE);
}
#endif // defined(NRF_NVMC_ICACHE_PRESENT)
#endif // SUPPRESS_INLINE_IMPLEMENTATION
/** @} */
#ifdef __cplusplus
}
#endif
#endif // NRF_NVMC_H__
+380
View File
@@ -0,0 +1,380 @@
/**
* Copyright (c) 2017 - 2021, 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, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, 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 its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA 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 NRFX_POWER_H__
#define NRFX_POWER_H__
#include <nrfx.h>
#include <hal/nrf_power.h>
#include <nrfx_power_clock.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @defgroup nrfx_power POWER driver
* @{
* @ingroup nrf_power
* @brief POWER peripheral driver.
*/
/**
* @brief Power mode possible configurations
*/
typedef enum
{
NRFX_POWER_MODE_CONSTLAT, /**< Constant latency mode */
NRFX_POWER_MODE_LOWPWR /**< Low power mode */
}nrfx_power_mode_t;
#if NRF_POWER_HAS_SLEEPEVT || defined(__NRFX_DOXYGEN__)
/**
* @brief Events from power system
*/
typedef enum
{
NRFX_POWER_SLEEP_EVT_ENTER, /**< CPU entered WFI/WFE sleep
*
* Keep in mind that if this interrupt is enabled,
* it means that CPU was waken up just after WFI by this interrupt.
*/
NRFX_POWER_SLEEP_EVT_EXIT /**< CPU exited WFI/WFE sleep */
}nrfx_power_sleep_evt_t;
#endif /* NRF_POWER_HAS_SLEEPEVT */
#if NRF_POWER_HAS_USBREG || defined(__NRFX_DOXYGEN__)
/**
* @brief Events from USB power system
*/
typedef enum
{
NRFX_POWER_USB_EVT_DETECTED, /**< USB power detected on the connector (plugged in). */
NRFX_POWER_USB_EVT_REMOVED, /**< USB power removed from the connector. */
NRFX_POWER_USB_EVT_READY /**< USB power regulator ready. */
}nrfx_power_usb_evt_t;
/**
* @brief USB power state
*
* The single enumerator that holds all data about current state of USB
* related POWER.
*
* Organized this way that higher power state has higher numeric value
*/
typedef enum
{
NRFX_POWER_USB_STATE_DISCONNECTED, /**< No power on USB lines detected. */
NRFX_POWER_USB_STATE_CONNECTED, /**< The USB power is detected, but USB power regulator is not ready. */
NRFX_POWER_USB_STATE_READY /**< From the power viewpoint, USB is ready for working. */
}nrfx_power_usb_state_t;
#endif /* NRF_POWER_HAS_USBREG */
/**
* @name Callback types
*
* Defined types of callback functions.
* @{
*/
/**
* @brief Event handler for power failure warning.
*/
typedef void (*nrfx_power_pofwarn_event_handler_t)(void);
#if NRF_POWER_HAS_SLEEPEVT || defined(__NRFX_DOXYGEN__)
/**
* @brief Event handler for the sleep events.
*
* @param event Event type
*/
typedef void (*nrfx_power_sleep_event_handler_t)(nrfx_power_sleep_evt_t event);
#endif
#if NRF_POWER_HAS_USBREG || defined(__NRFX_DOXYGEN__)
/**
* @brief Event handler for the USB-related power events.
*
* @param event Event type
*/
typedef void (*nrfx_power_usb_event_handler_t)(nrfx_power_usb_evt_t event);
#endif
/** @} */
/**
* @brief General power configuration
*
* Parameters required to initialize power driver.
*/
typedef struct
{
/**
* @brief Enable main DCDC regulator.
*
* This bit only informs the driver that elements for DCDC regulator
* are installed and the regulator can be used.
* The regulator will be enabled or disabled automatically
* by the hardware, basing on current power requirement.
*/
bool dcdcen:1;
#if NRF_POWER_HAS_VDDH || defined(__NRFX_DOXYGEN__)
/**
* @brief Enable HV DCDC regulator.
*
* This bit only informs the driver that elements for DCDC regulator
* are installed and the regulator can be used.
* The regulator will be enabled or disabled automatically
* by the hardware, basing on current power requirement.
*/
bool dcdcenhv: 1;
#endif
}nrfx_power_config_t;
/**
* @brief The configuration for power failure comparator.
*
* Configuration used to enable and configure the power failure comparator.
*/
typedef struct
{
nrfx_power_pofwarn_event_handler_t handler; //!< Event handler.
#if NRF_POWER_HAS_POFCON || defined(__NRFX_DOXYGEN__)
nrf_power_pof_thr_t thr; //!< Threshold for power failure detection
#endif
#if NRF_POWER_HAS_VDDH || defined(__NRFX_DOXYGEN__)
nrf_power_pof_thrvddh_t thrvddh; //!< Threshold for power failure detection on the VDDH pin.
#endif
}nrfx_power_pofwarn_config_t;
#if NRF_POWER_HAS_SLEEPEVT || defined(__NRFX_DOXYGEN__)
/**
* @brief The configuration of sleep event processing.
*
* Configuration used to enable and configure sleep event handling.
*/
typedef struct
{
nrfx_power_sleep_event_handler_t handler; //!< Event handler.
bool en_enter:1; //!< Enable event on sleep entering.
bool en_exit :1; //!< Enable event on sleep exiting.
}nrfx_power_sleepevt_config_t;
#endif
#if NRF_POWER_HAS_USBREG || defined(__NRFX_DOXYGEN__)
/**
* @brief The configuration of the USB-related power events.
*
* Configuration used to enable and configure USB power event handling.
*/
typedef struct
{
nrfx_power_usb_event_handler_t handler; //!< Event processing.
}nrfx_power_usbevt_config_t;
#endif /* NRF_POWER_HAS_USBREG */
/**
* @brief Function for getting the handler of the power failure comparator.
* @return Handler of the power failure comparator.
*/
nrfx_power_pofwarn_event_handler_t nrfx_power_pof_handler_get(void);
#if NRF_POWER_HAS_USBREG || defined(__NRFX_DOXYGEN__)
/**
* @brief Function for getting the handler of the USB power.
* @return Handler of the USB power.
*/
nrfx_power_usb_event_handler_t nrfx_power_usb_handler_get(void);
#endif
/**
* @brief Function for initializing the power module driver.
*
* Enabled power module driver processes all the interrupts from the power system.
*
* @param[in] p_config Pointer to the structure with the initial configuration.
*
* @retval NRFX_SUCCESS Successfully initialized.
* @retval NRFX_ERROR_ALREADY_INITIALIZED Module was already initialized.
*/
nrfx_err_t nrfx_power_init(nrfx_power_config_t const * p_config);
/**
* @brief Function for unintializing the power module driver.
*
* Disables all the interrupt handling in the module.
*
* @sa nrfx_power_init
*/
void nrfx_power_uninit(void);
#if NRF_POWER_HAS_POFCON || defined(__NRFX_DOXYGEN__)
/**
* @brief Function for initializing the power failure comparator.
*
* Configures the power failure comparator. This function does not set it up and enable it.
* These steps can be done with functions @ref nrfx_power_pof_enable and @ref nrfx_power_pof_disable
* or with the SoftDevice API (when in use).
*
* @param[in] p_config Configuration with values and event handler.
* If event handler is set to NULL, the interrupt will be disabled.
*/
void nrfx_power_pof_init(nrfx_power_pofwarn_config_t const * p_config);
/**
* @brief Function for enabling the power failure comparator.
* Sets and enables the interrupt of the power failure comparator. This function cannot be in use
* when SoftDevice is enabled. If the event handler set in the init function is set to NULL, the interrupt
* will be disabled.
*
* @param[in] p_config Configuration with values and event handler.
*/
void nrfx_power_pof_enable(nrfx_power_pofwarn_config_t const * p_config);
/**
* @brief Function for disabling the power failure comparator.
*
* Disables the power failure comparator interrupt.
*/
void nrfx_power_pof_disable(void);
/**
* @brief Function for clearing the power failure comparator settings.
*
* Clears the settings of the power failure comparator.
*/
void nrfx_power_pof_uninit(void);
#endif // NRF_POWER_HAS_POFCON || defined(__NRFX_DOXYGEN__)
#if NRF_POWER_HAS_SLEEPEVT || defined(__NRFX_DOXYGEN__)
/**
* @brief Function for initializing the processing of the sleep events.
*
* Configures and sets up the sleep event processing.
*
* @param[in] p_config Configuration with values and event handler.
*
* @sa nrfx_power_sleepevt_uninit
*
*/
void nrfx_power_sleepevt_init(nrfx_power_sleepevt_config_t const * p_config);
/**
* @brief Function for enabling the processing of the sleep events.
*
* @param[in] p_config Configuration with values and event handler.
*/
void nrfx_power_sleepevt_enable(nrfx_power_sleepevt_config_t const * p_config);
/** @brief Function for disabling the processing of the sleep events. */
void nrfx_power_sleepevt_disable(void);
/**
* @brief Function for uninitializing the processing of the sleep events.
*
* @sa nrfx_power_sleepevt_init
*/
void nrfx_power_sleepevt_uninit(void);
#endif /* NRF_POWER_HAS_SLEEPEVT */
#if NRF_POWER_HAS_USBREG || defined(__NRFX_DOXYGEN__)
/**
* @brief Function for initializing the processing of USB power event.
*
* Configures and sets up the USB power event processing.
*
* @param[in] p_config Configuration with values and event handler.
*
* @sa nrfx_power_usbevt_uninit
*/
void nrfx_power_usbevt_init(nrfx_power_usbevt_config_t const * p_config);
/** @brief Function for enabling the processing of USB power event. */
void nrfx_power_usbevt_enable(void);
/** @brief Function for disabling the processing of USB power event. */
void nrfx_power_usbevt_disable(void);
/**
* @brief Function for uninitalizing the processing of USB power event.
*
* @sa nrfx_power_usbevt_init
*/
void nrfx_power_usbevt_uninit(void);
/**
* @brief Function for getting the status of USB power.
*
* @return Current USB power status.
*/
__STATIC_INLINE nrfx_power_usb_state_t nrfx_power_usbstatus_get(void);
#endif /* NRF_POWER_HAS_USBREG */
#ifndef SUPPRESS_INLINE_IMPLEMENTATION
#if NRF_POWER_HAS_USBREG
__STATIC_INLINE nrfx_power_usb_state_t nrfx_power_usbstatus_get(void)
{
uint32_t status = nrf_power_usbregstatus_get();
if(0 == (status & NRF_POWER_USBREGSTATUS_VBUSDETECT_MASK))
{
return NRFX_POWER_USB_STATE_DISCONNECTED;
}
if(0 == (status & NRF_POWER_USBREGSTATUS_OUTPUTRDY_MASK))
{
return NRFX_POWER_USB_STATE_CONNECTED;
}
return NRFX_POWER_USB_STATE_READY;
}
#endif /* NRF_POWER_HAS_USBREG */
#endif /* SUPPRESS_INLINE_IMPLEMENTATION */
/** @} */
void nrfx_power_irq_handler(void);
#ifdef __cplusplus
}
#endif
#endif /* NRFX_POWER_H__ */
@@ -0,0 +1,92 @@
/**
* Copyright (c) 2015 - 2021, 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, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, 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 its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA 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 NRFX_POWER_CLOCK_H__
#define NRFX_POWER_CLOCK_H__
#include <nrfx.h>
#ifdef __cplusplus
extern "C" {
#endif
__STATIC_INLINE void nrfx_power_clock_irq_init(void);
#ifndef SUPPRESS_INLINE_IMPLEMENTATION
__STATIC_INLINE void nrfx_power_clock_irq_init(void)
{
uint8_t priority;
#if NRFX_CHECK(NRFX_POWER_ENABLED) && NRFX_CHECK(NRFX_CLOCK_ENABLED)
#if NRFX_POWER_CONFIG_IRQ_PRIORITY != NRFX_CLOCK_CONFIG_IRQ_PRIORITY
#error "IRQ priorities for POWER and CLOCK must be the same. Check <nrfx_config.h>."
#endif
priority = NRFX_POWER_CONFIG_IRQ_PRIORITY;
#elif NRFX_CHECK(NRFX_POWER_ENABLED)
priority = NRFX_POWER_CONFIG_IRQ_PRIORITY;
#elif NRFX_CHECK(NRFX_CLOCK_ENABLED)
priority = NRFX_CLOCK_CONFIG_IRQ_PRIORITY;
#else
#error "This code is not supposed to be compiled when neither POWER nor CLOCK is enabled."
#endif
if (!NRFX_IRQ_IS_ENABLED(nrfx_get_irq_number(NRF_CLOCK)))
{
NRFX_IRQ_PRIORITY_SET(nrfx_get_irq_number(NRF_CLOCK), priority);
NRFX_IRQ_ENABLE(nrfx_get_irq_number(NRF_CLOCK));
}
}
#endif // SUPPRESS_INLINE_IMPLEMENTATION
#if NRFX_CHECK(NRFX_POWER_ENABLED) && NRFX_CHECK(NRFX_CLOCK_ENABLED)
void nrfx_power_clock_irq_handler(void);
#elif NRFX_CHECK(NRFX_POWER_ENABLED)
#define nrfx_power_irq_handler nrfx_power_clock_irq_handler
#elif NRFX_CHECK(NRFX_CLOCK_ENABLED)
#define nrfx_clock_irq_handler nrfx_power_clock_irq_handler
#endif
#ifdef __cplusplus
}
#endif
#endif // NRFX_POWER_CLOCK_H__
+331
View File
@@ -0,0 +1,331 @@
/**
* Copyright (c) 2015 - 2021, 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, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, 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 its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA 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 NRFX_PPI_H__
#define NRFX_PPI_H__
#include <nrfx.h>
#include <hal/nrf_ppi.h>
/**
* @defgroup nrfx_ppi PPI allocator
* @{
* @ingroup nrf_ppi
* @brief Programmable Peripheral Interconnect (PPI) allocator.
*/
#ifdef __cplusplus
extern "C" {
#endif
#if !defined (NRFX_PPI_CHANNELS_USED) || defined(__NRFX_DOXYGEN__)
/** @brief Bitfield representing PPI channels used by external modules. */
#define NRFX_PPI_CHANNELS_USED 0
#endif
#if !defined(NRFX_PPI_GROUPS_USED) || defined(__NRFX_DOXYGEN__)
/** @brief Bitfield representing PPI groups used by external modules. */
#define NRFX_PPI_GROUPS_USED 0
#endif
#if (PPI_CH_NUM > 16) || defined(__NRFX_DOXYGEN__)
/** @brief Bitfield representing all PPI channels available to the application. */
#define NRFX_PPI_ALL_APP_CHANNELS_MASK ((uint32_t)0xFFFFFFFFuL & ~(NRFX_PPI_CHANNELS_USED))
/** @brief Bitfield representing programmable PPI channels available to the application. */
#define NRFX_PPI_PROG_APP_CHANNELS_MASK ((uint32_t)0x000FFFFFuL & ~(NRFX_PPI_CHANNELS_USED))
#else
#define NRFX_PPI_ALL_APP_CHANNELS_MASK ((uint32_t)0xFFF0FFFFuL & ~(NRFX_PPI_CHANNELS_USED))
#define NRFX_PPI_PROG_APP_CHANNELS_MASK ((uint32_t)0x0000FFFFuL & ~(NRFX_PPI_CHANNELS_USED))
#endif
/** @brief Bitfield representing all PPI groups available to the application. */
#define NRFX_PPI_ALL_APP_GROUPS_MASK (((1uL << PPI_GROUP_NUM) - 1) & ~(NRFX_PPI_GROUPS_USED))
/**
* @brief Function for uninitializing the PPI module.
*
* This function disables all channels and clears the channel groups.
*/
void nrfx_ppi_free_all(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 NRFX_SUCCESS The channel was successfully allocated.
* @retval NRFX_ERROR_NO_MEM There is no available channel to be used.
*/
nrfx_err_t nrfx_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 NRFX_SUCCESS The channel was successfully freed.
* @retval NRFX_ERROR_INVALID_PARAM The channel is not user-configurable.
*/
nrfx_err_t nrfx_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 NRFX_SUCCESS The channel was successfully assigned.
* @retval NRFX_ERROR_INVALID_STATE The channel is not allocated for the user.
* @retval NRFX_ERROR_INVALID_PARAM The channel is not user-configurable.
*/
nrfx_err_t nrfx_ppi_channel_assign(nrf_ppi_channel_t channel, uint32_t eep, uint32_t tep);
/**
* @brief Function for assigning fork endpoint to the PPI channel or clearing it.
*
* @param[in] channel PPI channel to be assigned endpoints.
* @param[in] fork_tep Fork task endpoint address or 0 to clear.
*
* @retval NRFX_SUCCESS The channel was successfully assigned.
* @retval NRFX_ERROR_INVALID_STATE The channel is not allocated for the user.
* @retval NRFX_ERROR_NOT_SUPPORTED Function is not supported.
*/
nrfx_err_t nrfx_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 NRFX_SUCCESS The channel was successfully enabled.
* @retval NRFX_ERROR_INVALID_STATE The user-configurable channel is not allocated.
* @retval NRFX_ERROR_INVALID_PARAM The channel cannot be enabled by the user.
*/
nrfx_err_t nrfx_ppi_channel_enable(nrf_ppi_channel_t channel);
/**
* @brief Function for disabling a PPI channel.
*
* @param[in] channel PPI channel to be disabled.
*
* @retval NRFX_SUCCESS The channel was successfully disabled.
* @retval NRFX_ERROR_INVALID_STATE The user-configurable channel is not allocated.
* @retval NRFX_ERROR_INVALID_PARAM The channel cannot be disabled by the user.
*/
nrfx_err_t nrfx_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 NRFX_SUCCESS The channel group was successfully allocated.
* @retval NRFX_ERROR_NO_MEM There is no available channel group to be used.
*/
nrfx_err_t nrfx_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 NRFX_SUCCESS The channel group was successfully freed.
* @retval NRFX_ERROR_INVALID_PARAM The channel group is not user-configurable.
*/
nrfx_err_t nrfx_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.
*
* @return Channel mask.
*/
__STATIC_INLINE uint32_t nrfx_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 NRFX_SUCCESS The channels was successfully included.
* @retval NRFX_ERROR_INVALID_PARAM Group is not an application group or channels are not an
* application channels.
* @retval NRFX_ERROR_INVALID_STATE Group is not an allocated group.
*/
nrfx_err_t nrfx_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 NRFX_SUCCESS The channel was successfully included.
* @retval NRFX_ERROR_INVALID_PARAM Group is not an application group or channel is not an
* application channel.
* @retval NRFX_ERROR_INVALID_STATE Group is not an allocated group.
*/
__STATIC_INLINE nrfx_err_t nrfx_ppi_channel_include_in_group(nrf_ppi_channel_t channel,
nrf_ppi_channel_group_t group)
{
return nrfx_ppi_channels_include_in_group(nrfx_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 NRFX_SUCCESS The channel was successfully removed.
* @retval NRFX_ERROR_INVALID_PARAM Group is not an application group or channels are not an
* application channels.
* @retval NRFX_ERROR_INVALID_STATE Group is not an allocated group.
*/
nrfx_err_t nrfx_ppi_channels_remove_from_group(uint32_t channel_mask,
nrf_ppi_channel_group_t group);
/**
* @brief Function for removing a single 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 NRFX_SUCCESS The channel was successfully removed.
* @retval NRFX_ERROR_INVALID_PARAM Group is not an application group or channel is not an
* application channel.
* @retval NRFX_ERROR_INVALID_STATE Group is not an allocated group.
*/
__STATIC_INLINE nrfx_err_t nrfx_ppi_channel_remove_from_group(nrf_ppi_channel_t channel,
nrf_ppi_channel_group_t group)
{
return nrfx_ppi_channels_remove_from_group(nrfx_ppi_channel_to_mask(channel), group);
}
/**
* @brief Function for clearing a PPI channel group.
*
* @param[in] group Channel group to be cleared.
*
* @retval NRFX_SUCCESS The group was successfully cleared.
* @retval NRFX_ERROR_INVALID_PARAM Group is not an application group.
* @retval NRFX_ERROR_INVALID_STATE Group is not an allocated group.
*/
__STATIC_INLINE nrfx_err_t nrfx_ppi_group_clear(nrf_ppi_channel_group_t group)
{
return nrfx_ppi_channels_remove_from_group(NRFX_PPI_ALL_APP_CHANNELS_MASK, group);
}
/**
* @brief Function for enabling a PPI channel group.
*
* @param[in] group Channel group to be enabled.
*
* @retval NRFX_SUCCESS The group was successfully enabled.
* @retval NRFX_ERROR_INVALID_PARAM Group is not an application group.
* @retval NRFX_ERROR_INVALID_STATE Group is not an allocated group.
*/
nrfx_err_t nrfx_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 NRFX_SUCCESS The group was successfully disabled.
* @retval NRFX_ERROR_INVALID_PARAM Group is not an application group.
* @retval NRFX_ERROR_INVALID_STATE Group is not an allocated group.
*/
nrfx_err_t nrfx_ppi_group_disable(nrf_ppi_channel_group_t group);
/**
* @brief Function for getting the address of a PPI task.
*
* @param[in] task Task.
*
* @return Task address.
*/
__STATIC_INLINE uint32_t nrfx_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 the enable task of a PPI group.
*
* @param[in] group PPI channel group
*
* @return Task address.
*/
__STATIC_INLINE uint32_t nrfx_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 the enable task of a PPI group.
*
* @param[in] group PPI channel group
*
* @return Task address.
*/
__STATIC_INLINE uint32_t nrfx_ppi_task_addr_group_disable_get(nrf_ppi_channel_group_t group)
{
return (uint32_t) nrf_ppi_task_group_disable_address_get(group);
}
/** @} */
#ifdef __cplusplus
}
#endif
#endif // NRFX_PPI_H__
+476
View File
@@ -0,0 +1,476 @@
/**
* Copyright (c) 2015 - 2021, 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, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, 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 its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA 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 NRFX_PWM_H__
#define NRFX_PWM_H__
#include <nrfx.h>
#include <hal/nrf_pwm.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @defgroup nrfx_pwm PWM driver
* @{
* @ingroup nrf_pwm
* @brief Pulse Width Modulation (PWM) peripheral driver.
*/
/** @brief PWM driver instance data structure. */
typedef struct
{
NRF_PWM_Type * p_registers; ///< Pointer to the structure with PWM peripheral instance registers.
uint8_t drv_inst_idx; ///< Index of the driver instance. For internal use only.
} nrfx_pwm_t;
/** @brief Macro for creating a PWM driver instance. */
#define NRFX_PWM_INSTANCE(id) \
{ \
.p_registers = NRFX_CONCAT_2(NRF_PWM, id), \
.drv_inst_idx = NRFX_CONCAT_3(NRFX_PWM, id, _INST_IDX), \
}
#ifndef __NRFX_DOXYGEN__
enum {
#if NRFX_CHECK(NRFX_PWM0_ENABLED)
NRFX_PWM0_INST_IDX,
#endif
#if NRFX_CHECK(NRFX_PWM1_ENABLED)
NRFX_PWM1_INST_IDX,
#endif
#if NRFX_CHECK(NRFX_PWM2_ENABLED)
NRFX_PWM2_INST_IDX,
#endif
#if NRFX_CHECK(NRFX_PWM3_ENABLED)
NRFX_PWM3_INST_IDX,
#endif
NRFX_PWM_ENABLED_COUNT
};
#endif
/**
* @brief This value can be provided instead of a pin number for any channel
* to specify that its output is not used and therefore does not need
* to be connected to a pin.
*/
#define NRFX_PWM_PIN_NOT_USED 0xFF
/** @brief This value can be added to a pin number to invert its polarity (set idle state = 1). */
#define NRFX_PWM_PIN_INVERTED 0x80
/** @brief PWM driver configuration structure. */
typedef struct
{
uint8_t output_pins[NRF_PWM_CHANNEL_COUNT]; ///< Pin numbers for individual output channels (optional).
/**< Use @ref NRFX_PWM_PIN_NOT_USED
* if a given output channel is not needed. */
uint8_t irq_priority; ///< Interrupt priority.
nrf_pwm_clk_t base_clock; ///< Base clock frequency.
nrf_pwm_mode_t count_mode; ///< Operating mode of the pulse generator counter.
uint16_t top_value; ///< Value up to which the pulse generator counter counts.
nrf_pwm_dec_load_t load_mode; ///< Mode of loading sequence data from RAM.
nrf_pwm_dec_step_t step_mode; ///< Mode of advancing the active sequence.
} nrfx_pwm_config_t;
/** @brief PWM driver default configuration. */
#define NRFX_PWM_DEFAULT_CONFIG \
{ \
.output_pins = { NRFX_PWM_DEFAULT_CONFIG_OUT0_PIN, \
NRFX_PWM_DEFAULT_CONFIG_OUT1_PIN, \
NRFX_PWM_DEFAULT_CONFIG_OUT2_PIN, \
NRFX_PWM_DEFAULT_CONFIG_OUT3_PIN }, \
.irq_priority = NRFX_PWM_DEFAULT_CONFIG_IRQ_PRIORITY, \
.base_clock = (nrf_pwm_clk_t)NRFX_PWM_DEFAULT_CONFIG_BASE_CLOCK, \
.count_mode = (nrf_pwm_mode_t)NRFX_PWM_DEFAULT_CONFIG_COUNT_MODE, \
.top_value = NRFX_PWM_DEFAULT_CONFIG_TOP_VALUE, \
.load_mode = (nrf_pwm_dec_load_t)NRFX_PWM_DEFAULT_CONFIG_LOAD_MODE, \
.step_mode = (nrf_pwm_dec_step_t)NRFX_PWM_DEFAULT_CONFIG_STEP_MODE, \
}
/** @brief PWM flags that provide additional playback options. */
typedef enum
{
NRFX_PWM_FLAG_STOP = 0x01, /**< When the requested playback is finished,
the peripheral will be stopped.
@note The STOP task is triggered when
the last value of the final sequence is
loaded from RAM, and the peripheral stops
at the end of the current PWM period.
For sequences with configured repeating
of duty cycle values, this might result in
less than the requested number of repeats
of the last value. */
NRFX_PWM_FLAG_LOOP = 0x02, /**< When the requested playback is finished,
it will be started from the beginning.
This flag is ignored if used together
with @ref NRFX_PWM_FLAG_STOP.
@note The playback restart is done via a
shortcut configured in the PWM peripheral.
This shortcut triggers the proper starting
task when the final value of previous
playback is read from RAM and applied to
the pulse generator counter.
When this mechanism is used together with
the @ref NRF_PWM_STEP_TRIGGERED mode,
the playback restart will occur right
after switching to the final value (this
final value will be played only once). */
NRFX_PWM_FLAG_SIGNAL_END_SEQ0 = 0x04, /**< The event handler is to be
called when the last value
from sequence 0 is loaded. */
NRFX_PWM_FLAG_SIGNAL_END_SEQ1 = 0x08, /**< The event handler is to be
called when the last value
from sequence 1 is loaded. */
NRFX_PWM_FLAG_NO_EVT_FINISHED = 0x10, /**< The playback finished event
(enabled by default) is to be
suppressed. */
NRFX_PWM_FLAG_START_VIA_TASK = 0x80, /**< The playback must not be
started directly by the called
function. Instead, the function
must only prepare it and
return the address of the task
to be triggered to start the
playback. */
} nrfx_pwm_flag_t;
/** @brief PWM driver event type. */
typedef enum
{
NRFX_PWM_EVT_FINISHED, ///< Sequence playback finished.
NRFX_PWM_EVT_END_SEQ0, /**< End of sequence 0 reached. Its data can be
safely modified now. */
NRFX_PWM_EVT_END_SEQ1, /**< End of sequence 1 reached. Its data can be
safely modified now. */
NRFX_PWM_EVT_STOPPED, ///< The PWM peripheral has been stopped.
} nrfx_pwm_evt_type_t;
/** @brief PWM driver event handler type. */
typedef void (* nrfx_pwm_handler_t)(nrfx_pwm_evt_type_t event_type);
/**
* @brief Function for initializing the PWM driver.
*
* @param[in] p_instance Pointer to the driver instance structure.
* @param[in] p_config Pointer to the structure with the initial configuration.
* @param[in] handler Event handler provided by the user. If NULL is passed
* instead, event notifications are not done and PWM
* interrupts are disabled.
*
* @retval NRFX_SUCCESS Initialization was successful.
* @retval NRFX_ERROR_INVALID_STATE The driver was already initialized.
*/
nrfx_err_t nrfx_pwm_init(nrfx_pwm_t const * const p_instance,
nrfx_pwm_config_t const * p_config,
nrfx_pwm_handler_t handler);
/**
* @brief Function for uninitializing the PWM driver.
*
* If any sequence playback is in progress, it is stopped immediately.
*
* @param[in] p_instance Pointer to the driver instance structure.
*/
void nrfx_pwm_uninit(nrfx_pwm_t const * const p_instance);
/**
* @brief Function for starting a single sequence playback.
*
* To take advantage of the looping mechanism in the PWM peripheral, both
* sequences must be used (single sequence can be played back only once by
* the peripheral). Therefore, the provided sequence is internally set and
* played back as both sequence 0 and sequence 1. Consequently, if the end of
* sequence notifications are required, events for both sequences must be
* used (that is, both the @ref NRFX_PWM_FLAG_SIGNAL_END_SEQ0 flag
* and the @ref NRFX_PWM_FLAG_SIGNAL_END_SEQ1 flag must be specified, and
* the @ref NRFX_PWM_EVT_END_SEQ0 event and the @ref NRFX_PWM_EVT_END_SEQ1
* event must be handled in the same way).
*
* Use the @ref NRFX_PWM_FLAG_START_VIA_TASK flag if you want the playback
* to be only prepared by this function, and you want to start it later by
* triggering a task (for example, by using PPI). The function will then return
* the address of the task to be triggered.
*
* @note The array containing the duty cycle values for the specified sequence
* must be in RAM and cannot be allocated on the stack.
* For detailed information, see @ref nrf_pwm_sequence_t.
*
* @param[in] p_instance Pointer to the driver instance structure.
* @param[in] p_sequence Sequence to be played back.
* @param[in] playback_count Number of playbacks to be performed (must not be 0).
* @param[in] flags Additional options. Pass any combination of
* @ref nrfx_pwm_flag_t "playback flags", or 0
* for default settings.
*
* @return Address of the task to be triggered to start the playback if the @ref
* NRFX_PWM_FLAG_START_VIA_TASK flag was used, 0 otherwise.
*/
uint32_t nrfx_pwm_simple_playback(nrfx_pwm_t const * const p_instance,
nrf_pwm_sequence_t const * p_sequence,
uint16_t playback_count,
uint32_t flags);
/**
* @brief Function for starting a two-sequence playback.
*
* Use the @ref NRFX_PWM_FLAG_START_VIA_TASK flag if you want the playback
* to be only prepared by this function, and you want to start it later by
* triggering a task (using PPI for instance). The function will then return
* the address of the task to be triggered.
*
* @note The array containing the duty cycle values for the specified sequence
* must be in RAM and cannot be allocated on the stack.
* For detailed information, see @ref nrf_pwm_sequence_t.
*
* @param[in] p_instance Pointer to the driver instance structure.
* @param[in] p_sequence_0 First sequence to be played back.
* @param[in] p_sequence_1 Second sequence to be played back.
* @param[in] playback_count Number of playbacks to be performed (must not be 0).
* @param[in] flags Additional options. Pass any combination of
* @ref nrfx_pwm_flag_t "playback flags", or 0
* for default settings.
*
* @return Address of the task to be triggered to start the playback if the @ref
* NRFX_PWM_FLAG_START_VIA_TASK flag was used, 0 otherwise.
*/
uint32_t nrfx_pwm_complex_playback(nrfx_pwm_t const * const p_instance,
nrf_pwm_sequence_t const * p_sequence_0,
nrf_pwm_sequence_t const * p_sequence_1,
uint16_t playback_count,
uint32_t flags);
/**
* @brief Function for advancing the active sequence.
*
* This function only applies to @ref NRF_PWM_STEP_TRIGGERED mode.
*
* @param[in] p_instance Pointer to the driver instance structure.
*/
__STATIC_INLINE void nrfx_pwm_step(nrfx_pwm_t const * const p_instance);
/**
* @brief Function for stopping the sequence playback.
*
* The playback is stopped at the end of the current PWM period.
* This means that if the active sequence is configured to repeat each duty
* cycle value for a certain number of PWM periods, the last played value
* might appear on the output less times than requested.
*
* @note This function can be instructed to wait until the playback is stopped
* (by setting @p wait_until_stopped to true). Depending on
* the length of the PMW period, this might take a significant amount of
* time. Alternatively, the @ref nrfx_pwm_is_stopped function can be
* used to poll the status, or the @ref NRFX_PWM_EVT_STOPPED event can
* be used to get the notification when the playback is stopped, provided
* the event handler is defined.
*
* @param[in] p_instance Pointer to the driver instance structure.
* @param[in] wait_until_stopped If true, the function will not return until
* the playback is stopped.
*
* @retval true The PWM peripheral is stopped.
* @retval false The PWM peripheral is not stopped.
*/
bool nrfx_pwm_stop(nrfx_pwm_t const * const p_instance, bool wait_until_stopped);
/**
* @brief Function for checking the status of the PWM peripheral.
*
* @param[in] p_instance Pointer to the driver instance structure.
*
* @retval true The PWM peripheral is stopped.
* @retval false The PWM peripheral is not stopped.
*/
bool nrfx_pwm_is_stopped(nrfx_pwm_t const * const p_instance);
/**
* @brief Function for updating the sequence data during playback.
*
* @param[in] p_instance Pointer to the driver instance structure.
* @param[in] seq_id Identifier of the sequence (0 or 1).
* @param[in] p_sequence Pointer to the new sequence definition.
*/
__STATIC_INLINE void nrfx_pwm_sequence_update(nrfx_pwm_t const * const p_instance,
uint8_t seq_id,
nrf_pwm_sequence_t const * p_sequence);
/**
* @brief Function for updating the pointer to the duty cycle values
* in the specified sequence during playback.
*
* @param[in] p_instance Pointer to the driver instance structure.
* @param[in] seq_id Identifier of the sequence (0 or 1).
* @param[in] values New pointer to the duty cycle values.
*/
__STATIC_INLINE void nrfx_pwm_sequence_values_update(nrfx_pwm_t const * const p_instance,
uint8_t seq_id,
nrf_pwm_values_t values);
/**
* @brief Function for updating the number of duty cycle values
* in the specified sequence during playback.
*
* @param[in] p_instance Pointer to the driver instance structure.
* @param[in] seq_id Identifier of the sequence (0 or 1).
* @param[in] length New number of the duty cycle values.
*/
__STATIC_INLINE void nrfx_pwm_sequence_length_update(nrfx_pwm_t const * const p_instance,
uint8_t seq_id,
uint16_t length);
/**
* @brief Function for updating the number of repeats for duty cycle values
* in the specified sequence during playback.
*
* @param[in] p_instance Pointer to the driver instance structure.
* @param[in] seq_id Identifier of the sequence (0 or 1).
* @param[in] repeats New number of repeats.
*/
__STATIC_INLINE void nrfx_pwm_sequence_repeats_update(nrfx_pwm_t const * const p_instance,
uint8_t seq_id,
uint32_t repeats);
/**
* @brief Function for updating the additional delay after the specified
* sequence during playback.
*
* @param[in] p_instance Pointer to the driver instance structure.
* @param[in] seq_id Identifier of the sequence (0 or 1).
* @param[in] end_delay New end delay value (in PWM periods).
*/
__STATIC_INLINE void nrfx_pwm_sequence_end_delay_update(nrfx_pwm_t const * const p_instance,
uint8_t seq_id,
uint32_t end_delay);
/**
* @brief Function for returning the address of a specified PWM task that can
* be used in PPI module.
*
* @param[in] p_instance Pointer to the driver instance structure.
* @param[in] task Requested task.
*
* @return Task address.
*/
__STATIC_INLINE uint32_t nrfx_pwm_task_address_get(nrfx_pwm_t const * const p_instance,
nrf_pwm_task_t task);
/**
* @brief Function for returning the address of a specified PWM event that can
* be used in PPI module.
*
* @param[in] p_instance Pointer to the driver instance structure.
* @param[in] event Requested event.
*
* @return Event address.
*/
__STATIC_INLINE uint32_t nrfx_pwm_event_address_get(nrfx_pwm_t const * const p_instance,
nrf_pwm_event_t event);
#ifndef SUPPRESS_INLINE_IMPLEMENTATION
__STATIC_INLINE void nrfx_pwm_step(nrfx_pwm_t const * const p_instance)
{
nrf_pwm_task_trigger(p_instance->p_registers, NRF_PWM_TASK_NEXTSTEP);
}
__STATIC_INLINE void nrfx_pwm_sequence_update(nrfx_pwm_t const * const p_instance,
uint8_t seq_id,
nrf_pwm_sequence_t const * p_sequence)
{
nrf_pwm_sequence_set(p_instance->p_registers, seq_id, p_sequence);
}
__STATIC_INLINE void nrfx_pwm_sequence_values_update(nrfx_pwm_t const * const p_instance,
uint8_t seq_id,
nrf_pwm_values_t values)
{
nrf_pwm_seq_ptr_set(p_instance->p_registers, seq_id, values.p_raw);
}
__STATIC_INLINE void nrfx_pwm_sequence_length_update(nrfx_pwm_t const * const p_instance,
uint8_t seq_id,
uint16_t length)
{
nrf_pwm_seq_cnt_set(p_instance->p_registers, seq_id, length);
}
__STATIC_INLINE void nrfx_pwm_sequence_repeats_update(nrfx_pwm_t const * const p_instance,
uint8_t seq_id,
uint32_t repeats)
{
nrf_pwm_seq_refresh_set(p_instance->p_registers, seq_id, repeats);
}
__STATIC_INLINE void nrfx_pwm_sequence_end_delay_update(nrfx_pwm_t const * const p_instance,
uint8_t seq_id,
uint32_t end_delay)
{
nrf_pwm_seq_end_delay_set(p_instance->p_registers, seq_id, end_delay);
}
__STATIC_INLINE uint32_t nrfx_pwm_task_address_get(nrfx_pwm_t const * const p_instance,
nrf_pwm_task_t task)
{
return nrf_pwm_task_address_get(p_instance->p_registers, task);
}
__STATIC_INLINE uint32_t nrfx_pwm_event_address_get(nrfx_pwm_t const * const p_instance,
nrf_pwm_event_t event)
{
return nrf_pwm_event_address_get(p_instance->p_registers, event);
}
#endif // SUPPRESS_INLINE_IMPLEMENTATION
/** @} */
void nrfx_pwm_0_irq_handler(void);
void nrfx_pwm_1_irq_handler(void);
void nrfx_pwm_2_irq_handler(void);
void nrfx_pwm_3_irq_handler(void);
#ifdef __cplusplus
}
#endif
#endif // NRFX_PWM_H__
+120
View File
@@ -0,0 +1,120 @@
/**
* Copyright (c) 2016 - 2021, 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, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, 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 its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA 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 NRFX_RNG_H__
#define NRFX_RNG_H__
#include <nrfx.h>
#include <hal/nrf_rng.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @defgroup nrfx_rng RNG driver
* @{
* @ingroup nrf_rng
* @brief Random Number Generator (RNG) peripheral driver.
*/
/** @brief Struct for RNG configuration. */
typedef struct
{
bool error_correction : 1; /**< Error correction flag. */
uint8_t interrupt_priority; /**< Interrupt priority. */
} nrfx_rng_config_t;
/**
* @brief RNG default configuration.
* Basic usage:
* @code
* nrfx_rng_config_t config = NRFX_RNG_DEFAULT_CONFIG;
* if (nrfx_rng_init(&config, handler)
* { ...
* @endcode
*/
#define NRFX_RNG_DEFAULT_CONFIG \
{ \
.error_correction = NRFX_RNG_CONFIG_ERROR_CORRECTION, \
.interrupt_priority = NRFX_RNG_CONFIG_IRQ_PRIORITY, \
}
/** @brief RNG driver event handler type. */
typedef void (* nrfx_rng_evt_handler_t)(uint8_t rng_data);
/**
* @brief Function for initializing the nrfx_rng module.
*
* @param[in] p_config Pointer to the structure with the initial configuration.
* @param[in] handler Event handler provided by the user. Must not be NULL.
*
* @retval NRFX_SUCCESS Driver was successfully initialized.
* @retval NRFX_ERROR_ALREADY_INITIALIZED Driver was already initialized.
*/
nrfx_err_t nrfx_rng_init(nrfx_rng_config_t const * p_config, nrfx_rng_evt_handler_t handler);
/**
* @brief Function for starting the generation of random values.
*
* New data should be handled by handler passed to the @ref nrfx_rng_init() function.
*/
void nrfx_rng_start(void);
/**
* @brief Function for stopping the generation of random values.
*
* Function disables interrupts in peripheral and stops the generation of new random values.
*/
void nrfx_rng_stop(void);
/** @brief Function for uninitializing the nrfx_rng module. */
void nrfx_rng_uninit(void);
/** @} */
void nrfx_rng_irq_handler(void);
#ifdef __cplusplus
}
#endif
#endif // NRFX_RNG_H__
+378
View File
@@ -0,0 +1,378 @@
/**
* Copyright (c) 2014 - 2021, 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, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, 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 its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA 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 NRFX_RTC_H__
#define NRFX_RTC_H__
#include <nrfx.h>
#include <hal/nrf_rtc.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @defgroup nrfx_rtc RTC driver
* @{
* @ingroup nrf_rtc
* @brief Real Timer Counter (RTC) peripheral driver.
*/
/** @brief Macro for converting microseconds into ticks. */
#define NRFX_RTC_US_TO_TICKS(us,freq) (((us) * (freq)) / 1000000U)
/** @brief RTC driver interrupt types. */
typedef enum
{
NRFX_RTC_INT_COMPARE0 = 0, /**< Interrupt from COMPARE0 event. */
NRFX_RTC_INT_COMPARE1 = 1, /**< Interrupt from COMPARE1 event. */
NRFX_RTC_INT_COMPARE2 = 2, /**< Interrupt from COMPARE2 event. */
NRFX_RTC_INT_COMPARE3 = 3, /**< Interrupt from COMPARE3 event. */
NRFX_RTC_INT_TICK = 4, /**< Interrupt from TICK event. */
NRFX_RTC_INT_OVERFLOW = 5 /**< Interrupt from OVERFLOW event. */
} nrfx_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; /**< Index of the driver instance. For internal use only. */
uint8_t cc_channel_count; /**< Number of capture/compare channels. */
} nrfx_rtc_t;
/** @brief Macro for creating an RTC driver instance. */
#define NRFX_RTC_INSTANCE(id) \
{ \
.p_reg = NRFX_CONCAT_2(NRF_RTC, id), \
.irq = NRFX_CONCAT_3(RTC, id, _IRQn), \
.instance_id = NRFX_CONCAT_3(NRFX_RTC, id, _INST_IDX), \
.cc_channel_count = NRF_RTC_CC_CHANNEL_COUNT(id), \
}
#ifndef __NRFX_DOXYGEN__
enum {
#if NRFX_CHECK(NRFX_RTC0_ENABLED)
NRFX_RTC0_INST_IDX,
#endif
#if NRFX_CHECK(NRFX_RTC1_ENABLED)
NRFX_RTC1_INST_IDX,
#endif
#if NRFX_CHECK(NRFX_RTC2_ENABLED)
NRFX_RTC2_INST_IDX,
#endif
NRFX_RTC_ENABLED_COUNT
};
#endif
/** @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 the interrupt handler in ticks (maximum 7.7 ms). */
bool reliable; /**< Reliable mode flag. */
} nrfx_rtc_config_t;
/** @brief RTC instance default configuration. */
#define NRFX_RTC_DEFAULT_CONFIG \
{ \
.prescaler = RTC_FREQ_TO_PRESCALER(NRFX_RTC_DEFAULT_CONFIG_FREQUENCY), \
.interrupt_priority = NRFX_RTC_DEFAULT_CONFIG_IRQ_PRIORITY, \
.tick_latency = NRFX_RTC_US_TO_TICKS(NRFX_RTC_MAXIMUM_LATENCY_US, \
NRFX_RTC_DEFAULT_CONFIG_FREQUENCY), \
.reliable = NRFX_RTC_DEFAULT_CONFIG_RELIABLE, \
}
/** @brief RTC driver instance handler type. */
typedef void (*nrfx_rtc_handler_t)(nrfx_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 driver instance structure.
* @param[in] p_config Pointer to the structure with the initial configuration.
* @param[in] handler Event handler provided by the user.
* Must not be NULL.
*
* @retval NRFX_SUCCESS Successfully initialized.
* @retval NRFX_ERROR_INVALID_STATE The instance is already initialized.
*/
nrfx_err_t nrfx_rtc_init(nrfx_rtc_t const * const p_instance,
nrfx_rtc_config_t const * p_config,
nrfx_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.
*
* @param[in] p_instance Pointer to the driver instance structure.
*/
void nrfx_rtc_uninit(nrfx_rtc_t const * const p_instance);
/**
* @brief Function for enabling the RTC driver instance.
*
* @param[in] p_instance Pointer to the driver instance structure.
*/
void nrfx_rtc_enable(nrfx_rtc_t const * const p_instance);
/**
* @brief Function for disabling the RTC driver instance.
*
* @param[in] p_instance Pointer to the driver instance structure.
*/
void nrfx_rtc_disable(nrfx_rtc_t const * const p_instance);
/**
* @brief Function for setting a compare channel.
*
* 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. In this case, if the reliable mode is enabled for the specified instance,
* the risk is handled.
* 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 this.
*
* @param[in] p_instance Pointer to the driver instance structure.
* @param[in] channel One of the channels of the instance.
* @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 NRFX_SUCCESS The procedure is successful.
* @retval NRFX_ERROR_TIMEOUT The compare is not set because the request value is behind the
* current counter value. This error can only be reported
* if the reliable mode is enabled.
*/
nrfx_err_t nrfx_rtc_cc_set(nrfx_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.
*
* @param[in] p_instance Pointer to the driver instance structure.
* @param[in] channel One of the channels of the instance.
*
* @retval NRFX_SUCCESS The procedure is successful.
* @retval NRFX_ERROR_TIMEOUT Interrupt is pending on the requested channel.
*/
nrfx_err_t nrfx_rtc_cc_disable(nrfx_rtc_t const * const p_instance, uint32_t channel);
/**
* @brief Function for enabling the TICK event.
*
* This function enables the tick event and optionally the interrupt.
*
* @param[in] p_instance Pointer to the driver instance structure.
* @param[in] enable_irq True to enable the interrupt. False to disable the interrupt.
*/
void nrfx_rtc_tick_enable(nrfx_rtc_t const * const p_instance, bool enable_irq);
/**
* @brief Function for disabling the TICK event.
*
* This function disables the TICK event and interrupt.
*
* @param[in] p_instance Pointer to the driver instance structure.
*/
void nrfx_rtc_tick_disable(nrfx_rtc_t const * const p_instance);
/**
* @brief Function for enabling overflow.
*
* This function enables the overflow event and optionally the interrupt.
*
* @param[in] p_instance Pointer to the driver instance structure.
* @param[in] enable_irq True to enable the interrupt. False to disable the interrupt.
*/
void nrfx_rtc_overflow_enable(nrfx_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 driver instance structure.
*/
void nrfx_rtc_overflow_disable(nrfx_rtc_t const * const p_instance);
/**
* @brief Function for getting the maximum relative tick value that can be set in the compare channel.
*
* When a stack (for example SoftDevice) is used and it occupies high priority interrupts,
* the application code can be interrupted at any moment for a certain period of time.
* If the reliable mode is enabled, the provided maximum latency is taken into account
* and the return value is smaller than the RTC counter resolution.
* If the reliable mode is disabled, the return value equals the counter resolution.
*
* @param[in] p_instance Pointer to the driver instance structure.
*
* @return Maximum ticks value.
*/
uint32_t nrfx_rtc_max_ticks_get(nrfx_rtc_t const * const p_instance);
/**
* @brief Function for disabling all instance interrupts.
*
* @param[in] p_instance Pointer to the driver instance structure.
* @param[in] p_mask Pointer to the location where the mask is filled.
*/
__STATIC_INLINE void nrfx_rtc_int_disable(nrfx_rtc_t const * const p_instance,
uint32_t * p_mask);
/**
* @brief Function for enabling instance interrupts.
*
* @param[in] p_instance Pointer to the driver instance structure.
* @param[in] mask Mask of interrupts to enable.
*/
__STATIC_INLINE void nrfx_rtc_int_enable(nrfx_rtc_t const * const p_instance, uint32_t mask);
/**
* @brief Function for retrieving the current counter value.
*
* @param[in] p_instance Pointer to the driver instance structure.
*
* @return Counter value.
*/
__STATIC_INLINE uint32_t nrfx_rtc_counter_get(nrfx_rtc_t const * const p_instance);
/**
* @brief Function for clearing the counter value.
*
* @param[in] p_instance Pointer to the driver instance structure.
*/
__STATIC_INLINE void nrfx_rtc_counter_clear(nrfx_rtc_t const * const p_instance);
/**
* @brief Function for returning a requested task address for the RTC driver instance.
*
* 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.
*
* @return Address of task register.
*/
__STATIC_INLINE uint32_t nrfx_rtc_task_address_get(nrfx_rtc_t const * const p_instance,
nrf_rtc_task_t task);
/**
* @brief Function for returning a requested event address for the RTC driver instance.
*
* The event address can be used by the PPI module.
*
* @param[in] p_instance Pointer to the driver instance structure.
* @param[in] event One of the peripheral events.
*
* @return Address of event register.
*/
__STATIC_INLINE uint32_t nrfx_rtc_event_address_get(nrfx_rtc_t const * const p_instance,
nrf_rtc_event_t event);
#ifndef SUPPRESS_INLINE_IMPLEMENTATION
__STATIC_INLINE void nrfx_rtc_int_disable(nrfx_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 nrfx_rtc_int_enable(nrfx_rtc_t const * const p_instance, uint32_t mask)
{
nrf_rtc_int_enable(p_instance->p_reg, mask);
}
__STATIC_INLINE uint32_t nrfx_rtc_counter_get(nrfx_rtc_t const * const p_instance)
{
return nrf_rtc_counter_get(p_instance->p_reg);
}
__STATIC_INLINE void nrfx_rtc_counter_clear(nrfx_rtc_t const * const p_instance)
{
nrf_rtc_task_trigger(p_instance->p_reg, NRF_RTC_TASK_CLEAR);
}
__STATIC_INLINE uint32_t nrfx_rtc_task_address_get(nrfx_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 nrfx_rtc_event_address_get(nrfx_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
/** @} */
void nrfx_rtc_0_irq_handler(void);
void nrfx_rtc_1_irq_handler(void);
void nrfx_rtc_2_irq_handler(void);
#ifdef __cplusplus
}
#endif
#endif // NRFX_RTC_H__
+322
View File
@@ -0,0 +1,322 @@
/**
* Copyright (c) 2015 - 2021, 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, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, 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 its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA 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 NRFX_SAADC_H__
#define NRFX_SAADC_H__
#include <nrfx.h>
#include <hal/nrf_saadc.h>
#ifdef __cplusplus
extern "C" {
#endif
#if defined(NRFX_SAADC_API_V2)
#include "nrfx_saadc_v2.h"
#else
/**
* @defgroup nrfx_saadc SAADC legacy driver
* @{
* @ingroup nrf_saadc
* @brief Successive Approximation Analog-to-Digital Converter (SAADC) peripheral legacy driver.
*/
/** @brief Value to be set as high limit to disable limit detection. */
#define NRFX_SAADC_LIMITH_DISABLED (2047)
/** @brief Value to be set as low limit to disable limit detection. */
#define NRFX_SAADC_LIMITL_DISABLED (-2048)
/** @brief Macro for setting @ref nrfx_saadc_config_t to default settings. */
#define NRFX_SAADC_DEFAULT_CONFIG \
{ \
.resolution = (nrf_saadc_resolution_t)NRFX_SAADC_CONFIG_RESOLUTION, \
.oversample = (nrf_saadc_oversample_t)NRFX_SAADC_CONFIG_OVERSAMPLE, \
.interrupt_priority = NRFX_SAADC_CONFIG_IRQ_PRIORITY, \
.low_power_mode = NRFX_SAADC_CONFIG_LP_MODE \
}
/**
* @brief Macro for setting @ref nrf_saadc_channel_config_t to default settings
* in single-ended mode.
*
* @param PIN_P Analog input.
*/
#define NRFX_SAADC_DEFAULT_CHANNEL_CONFIG_SE(PIN_P) \
{ \
.resistor_p = NRF_SAADC_RESISTOR_DISABLED, \
.resistor_n = NRF_SAADC_RESISTOR_DISABLED, \
.gain = NRF_SAADC_GAIN1_6, \
.reference = NRF_SAADC_REFERENCE_INTERNAL, \
.acq_time = NRF_SAADC_ACQTIME_10US, \
.mode = NRF_SAADC_MODE_SINGLE_ENDED, \
.burst = NRF_SAADC_BURST_DISABLED, \
.pin_p = (nrf_saadc_input_t)(PIN_P), \
.pin_n = NRF_SAADC_INPUT_DISABLED \
}
/**
* @brief Macro for setting @ref nrf_saadc_channel_config_t to default settings
* in differential mode.
*
* @param PIN_P Positive analog input.
* @param PIN_N Negative analog input.
*/
#define NRFX_SAADC_DEFAULT_CHANNEL_CONFIG_DIFFERENTIAL(PIN_P, PIN_N) \
{ \
.resistor_p = NRF_SAADC_RESISTOR_DISABLED, \
.resistor_n = NRF_SAADC_RESISTOR_DISABLED, \
.gain = NRF_SAADC_GAIN1_6, \
.reference = NRF_SAADC_REFERENCE_INTERNAL, \
.acq_time = NRF_SAADC_ACQTIME_10US, \
.mode = NRF_SAADC_MODE_DIFFERENTIAL, \
.burst = NRF_SAADC_BURST_DISABLED, \
.pin_p = (nrf_saadc_input_t)(PIN_P), \
.pin_n = (nrf_saadc_input_t)(PIN_N) \
}
/** @brief SAADC driver configuration structure. */
typedef struct
{
nrf_saadc_resolution_t resolution; ///< Resolution configuration.
nrf_saadc_oversample_t oversample; ///< Oversampling configuration.
uint8_t interrupt_priority; ///< Interrupt priority.
bool low_power_mode; ///< Indicates if low power mode is active.
} nrfx_saadc_config_t;
/** @brief SAADC driver event types. */
typedef enum
{
NRFX_SAADC_EVT_DONE, ///< Event generated when the buffer is filled with samples.
NRFX_SAADC_EVT_LIMIT, ///< Event generated after one of the limits is reached.
NRFX_SAADC_EVT_CALIBRATEDONE ///< Event generated when the calibration is complete.
} nrfx_saadc_evt_type_t;
/** @brief SAADC driver done event data. */
typedef struct
{
nrf_saadc_value_t * p_buffer; ///< Pointer to buffer with converted samples.
uint16_t size; ///< Number of samples in the buffer.
} nrfx_saadc_done_evt_t;
/** @brief SAADC driver limit event data. */
typedef struct
{
uint8_t channel; ///< Channel on which the limit was detected.
nrf_saadc_limit_t limit_type; ///< Type of limit detected.
} nrfx_saadc_limit_evt_t;
/** @brief SAADC driver event structure. */
typedef struct
{
nrfx_saadc_evt_type_t type; ///< Event type.
union
{
nrfx_saadc_done_evt_t done; ///< Data for @ref NRFX_SAADC_EVT_DONE event.
nrfx_saadc_limit_evt_t limit; ///< Data for @ref NRFX_SAADC_EVT_LIMIT event.
} data; ///< Union to store event data.
} nrfx_saadc_evt_t;
/**
* @brief SAADC driver event handler.
*
* @param[in] p_event Pointer to an SAADC driver event. The event structure is allocated on
* the stack, so it is valid only within the context of the event handler.
*/
typedef void (* nrfx_saadc_event_handler_t)(nrfx_saadc_evt_t const * p_event);
/**
* @brief Function for initializing the SAADC.
*
* @param[in] p_config Pointer to the structure with initial configuration.
* @param[in] event_handler Event handler provided by the user.
* Must not be NULL.
*
* @retval NRFX_SUCCESS Initialization was successful.
* @retval NRFX_ERROR_INVALID_STATE The driver is already initialized.
*/
nrfx_err_t nrfx_saadc_init(nrfx_saadc_config_t const * p_config,
nrfx_saadc_event_handler_t event_handler);
/**
* @brief Function for uninitializing the SAADC.
*
* This function stops all ongoing conversions and disables all channels.
*/
void nrfx_saadc_uninit(void);
/**
* @brief Function for getting the address of a SAMPLE SAADC task.
*
* @return Task address.
*/
uint32_t nrfx_saadc_sample_task_get(void);
/**
* @brief Function for initializing an SAADC channel.
*
* This function configures and enables the channel.
*
* @param[in] channel Channel index.
* @param[in] p_config Pointer to the structure with the initial configuration.
*
* @retval NRFX_SUCCESS Initialization was successful.
* @retval NRFX_ERROR_INVALID_STATE The SAADC was not initialized.
* @retval NRFX_ERROR_NO_MEM The specified channel was already allocated.
*/
nrfx_err_t nrfx_saadc_channel_init(uint8_t channel,
nrf_saadc_channel_config_t const * const p_config);
/**
* @brief Function for uninitializing an SAADC channel.
*
* @param[in] channel Channel index.
*
* @retval NRFX_SUCCESS Uninitialization was successful.
* @retval NRFX_ERROR_BUSY The SAADC is busy.
*/
nrfx_err_t nrfx_saadc_channel_uninit(uint8_t channel);
/**
* @brief Function for starting the SAADC sampling.
*
* @retval NRFX_SUCCESS The SAADC sampling was triggered.
* @retval NRFX_ERROR_INVALID_STATE The SAADC is in idle state.
*/
nrfx_err_t nrfx_saadc_sample(void);
/**
* @brief Blocking function for executing a single SAADC conversion.
*
* This function selects the desired input, starts a single conversion,
* waits for it to finish, and returns the result.
*
* The function fails if the SAADC is busy.
*
* @param[in] channel Channel.
* @param[out] p_value Pointer to the location where the result is to be placed.
*
* @retval NRFX_SUCCESS The conversion was successful.
* @retval NRFX_ERROR_BUSY The SAADC driver is busy.
*/
nrfx_err_t nrfx_saadc_sample_convert(uint8_t channel, nrf_saadc_value_t * p_value);
/**
* @brief Function for issuing conversion of data to the buffer.
*
* This function is non-blocking. The application is notified about filling the buffer by the event
* handler. Conversion will be done on all enabled channels. If the SAADC is in idle state, the
* function will set up EasyDMA for the conversion. The SAADC will be ready for sampling and wait
* for the SAMPLE task. It can be triggered manually by the @ref nrfx_saadc_sample function
* or by PPI using the @ref NRF_SAADC_TASK_SAMPLE task. If one buffer is already set and the
* conversion is ongoing, calling this function will result in queuing the given buffer.
* The driver will start filling the issued buffer when the first one is completed.
* If the function is called again before the first buffer is filled or calibration
* is in progress, it will return with error.
*
* @param[in] buffer Result buffer.
* @param[in] size Buffer size in words.
*
* @retval NRFX_SUCCESS The conversion was successful.
* @retval NRFX_ERROR_BUSY The driver already has two buffers set or the calibration is in progress.
*/
nrfx_err_t nrfx_saadc_buffer_convert(nrf_saadc_value_t * buffer, uint16_t size);
/**
* @brief Function for triggering the SAADC offset calibration.
*
* This function is non-blocking. The application is notified about completion by the event handler.
* Calibration will also trigger DONE and RESULTDONE events.
*
* The function will fail if the SAADC is busy or calibration is already in progress.
*
* @retval NRFX_SUCCESS The calibration was started successfully.
* @retval NRFX_ERROR_BUSY The SAADC driver is busy.
*/
nrfx_err_t nrfx_saadc_calibrate_offset(void);
/**
* @brief Function for retrieving the SAADC state.
*
* @retval true The SAADC is busy.
* @retval false The SAADC is ready.
*/
bool nrfx_saadc_is_busy(void);
/**
* @brief Function for aborting the ongoing and buffered conversions.
*
* @note @ref NRFX_SAADC_EVT_DONE event will be generated if there is a conversion in progress.
* Event will contain number of words in the sample buffer.
*
* @warning This function must not be called from the context of event handler of the SAADC driver
* or from the context of interrupt with priority equal to or higher than priority
* of the SAADC interrupt.
*/
void nrfx_saadc_abort(void);
/**
* @brief Function for setting the SAADC channel limits.
* When limits are enabled and the result exceeds the defined bounds, the limit handler
* function is called.
*
* @param[in] channel SAADC channel number.
* @param[in] limit_low Lower limit (valid values from @ref NRFX_SAADC_LIMITL_DISABLED to
* @ref NRFX_SAADC_LIMITH_DISABLED). Conversion results below this value will
* trigger the handler function. Set to @ref NRFX_SAADC_LIMITL_DISABLED
* to disable this limit.
* @param[in] limit_high Upper limit (valid values from @ref NRFX_SAADC_LIMITL_DISABLED to
* @ref NRFX_SAADC_LIMITH_DISABLED). Conversion results above this value will
* trigger the handler function. Set to @ref NRFX_SAADC_LIMITH_DISABLED
* to disable this limit.
*/
void nrfx_saadc_limits_set(uint8_t channel, int16_t limit_low, int16_t limit_high);
/** @} */
#endif // defined(NRFX_SAADC_API_V2)
void nrfx_saadc_irq_handler(void);
#ifdef __cplusplus
}
#endif
#endif // NRFX_SAADC_H__
@@ -0,0 +1,382 @@
/**
* Copyright (c) 2019 - 2021, 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, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, 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 its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA 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 NRFX_SAADC_V2_H__
#define NRFX_SAADC_V2_H__
#ifndef NRFX_SAADC_H__
#error "This file should not be included directly. Include nrfx_saadc.h instead."
#endif
/**
* @defgroup nrfx_saadc_v2 SAADC v2 driver
* @{
* @ingroup nrf_saadc
* @brief Successive Approximation Analog-to-Digital Converter (SAADC) peripheral v2 driver.
* @details API description can be found <a href="../../drivers/include/nrfx_saadc_v2.h">here</a>.
* @}
*/
/**
* @brief SAADC channel default configuration for the single-ended mode.
*
* This configuration sets up single-ended SAADC channel with the following options:
* - resistor ladder disabled
* - gain: 1/6
* - reference voltage: internal 0.6 V
* - sample acquisition time: 10 us
* - burst disabled
*
* @param[in] _pin_p Positive input analog pin.
* @param[in] _index Channel index.
*
* @sa nrfx_saadc_channel_t
*/
#define NRFX_SAADC_DEFAULT_CHANNEL_SE(_pin_p, _index) \
{ \
.channel_config = \
{ \
.resistor_p = NRF_SAADC_RESISTOR_DISABLED, \
.resistor_n = NRF_SAADC_RESISTOR_DISABLED, \
.gain = NRF_SAADC_GAIN1_6, \
.reference = NRF_SAADC_REFERENCE_INTERNAL, \
.acq_time = NRF_SAADC_ACQTIME_10US, \
.mode = NRF_SAADC_MODE_SINGLE_ENDED, \
.burst = NRF_SAADC_BURST_DISABLED, \
}, \
.pin_p = (nrf_saadc_input_t)_pin_p, \
.pin_n = NRF_SAADC_INPUT_DISABLED, \
.channel_index = _index, \
}
/**
* @brief SAADC channel default configuration for the differential mode.
*
* This configuration sets up differential SAADC channel with the following options:
* - resistor ladder disabled
* - gain: 1/6
* - reference voltage: internal 0.6 V
* - sample acquisition time: 10 us
* - burst disabled
*
* @param[in] _pin_p Positive input analog pin.
* @param[in] _pin_n Negative input analog pin.
* @param[in] _index Channel index.
*
* @sa nrfx_saadc_channel_t
*/
#define NRFX_SAADC_DEFAULT_CHANNEL_DIFFERENTIAL(_pin_p, _pin_n, _index) \
{ \
.channel_config = \
{ \
.resistor_p = NRF_SAADC_RESISTOR_DISABLED, \
.resistor_n = NRF_SAADC_RESISTOR_DISABLED, \
.gain = NRF_SAADC_GAIN1_6, \
.reference = NRF_SAADC_REFERENCE_INTERNAL, \
.acq_time = NRF_SAADC_ACQTIME_10US, \
.mode = NRF_SAADC_MODE_DIFFERENTIAL, \
.burst = NRF_SAADC_BURST_DISABLED, \
}, \
.pin_p = (nrf_saadc_input_t)_pin_p, \
.pin_n = (nrf_saadc_input_t)_pin_n, \
.channel_index = _index, \
}
/**
* @brief SAADC driver advanced mode default configuration.
*
* This configuration sets up advanced mode of the SAADC driver with the following options:
* - oversampling disabled
* - burst disabled
* - internal sampling timer disabled
* - triggering of the START task on the END event disabled
*
* @param[in] _pin_p Positive input analog pin.
* @param[in] _pin_n Negative input analog pin.
* @param[in] _index Channel index.
*
* @sa nrfx_saadc_adv_config_t
*/
#define NRFX_SAADC_DEFAULT_ADV_CONFIG \
{ \
.oversampling = NRF_SAADC_OVERSAMPLE_DISABLED, \
.burst = NRF_SAADC_BURST_DISABLED, \
.internal_timer_cc = 0, \
.start_on_end = false, \
}
/** @brief SAADC channel configuration structure. */
typedef struct
{
nrf_saadc_channel_config_t channel_config; ///< Channel hardware configuration.
nrf_saadc_input_t pin_p; ///< Input positive pin selection.
nrf_saadc_input_t pin_n; ///< Input negative pin selection.
uint8_t channel_index; ///< Channel index.
} nrfx_saadc_channel_t;
/** @brief SAADC driver advanced mode configuration structure. */
typedef struct
{
nrf_saadc_oversample_t oversampling; ///< Oversampling configuration.
nrf_saadc_burst_t burst; ///< Burst configuration.
uint16_t internal_timer_cc; ///< Internal timer capture and compare value.
bool start_on_end; ///< Flag indicating if the START task is to be triggered on the END event.
} nrfx_saadc_adv_config_t;
/** @brief SAADC driver event types. */
typedef enum
{
NRFX_SAADC_EVT_DONE, ///< Event generated when the buffer is filled with samples.
NRFX_SAADC_EVT_LIMIT, ///< Event generated when one of the limits is reached.
NRFX_SAADC_EVT_CALIBRATEDONE, ///< Event generated when the calibration is complete.
NRFX_SAADC_EVT_BUF_REQ, ///< Event generated when the next buffer for continuous conversion is requested.
NRFX_SAADC_EVT_READY, ///< Event generated when the first buffer is acquired by the peripheral and sampling can be started.
NRFX_SAADC_EVT_FINISHED, ///< Event generated when all supplied buffers are filled with results.
} nrfx_saadc_evt_type_t;
/** @brief SAADC driver done event data. */
typedef struct
{
nrf_saadc_value_t * p_buffer; ///< Pointer to the buffer with converted samples.
uint16_t size; ///< Number of samples in the buffer.
} nrfx_saadc_done_evt_t;
/** @brief SAADC driver limit event data. */
typedef struct
{
uint8_t channel; ///< Channel on which the limit was detected.
nrf_saadc_limit_t limit_type; ///< Type of limit detected.
} nrfx_saadc_limit_evt_t;
/** @brief SAADC driver event structure. */
typedef struct
{
nrfx_saadc_evt_type_t type; ///< Event type.
union
{
nrfx_saadc_done_evt_t done; ///< Data for @ref NRFX_SAADC_EVT_DONE event.
nrfx_saadc_limit_evt_t limit; ///< Data for @ref NRFX_SAADC_EVT_LIMIT event.
} data; ///< Union to store event data.
} nrfx_saadc_evt_t;
/**
* @brief SAADC driver event handler.
*
* When operating in the advanced mode:
* - when the sampling is performed by the external timer, the external timer can be safely started
* on @ref NRFX_SAADC_EVT_READY and stopped on @ref NRFX_SAADC_EVT_FINISHED.
* - call the @ref nrfx_saadc_buffer_set() on @ref NRFX_SAADC_EVT_BUF_REQ to achieve the continuous conversion.
*
* @param[in] p_event Pointer to an SAADC driver event. The event structure is allocated on
* the stack, so it is valid only within the context of the event handler.
*/
typedef void (* nrfx_saadc_event_handler_t)(nrfx_saadc_evt_t const * p_event);
/**
* @brief Function for initializing the SAADC driver.
*
* @param[in] interrupt_priority Interrupt priority.
*
* @retval NRFX_SUCCESS Initialization was successful.
* @retval NRFX_ERROR_INVALID_STATE The driver is already initialized.
*/
nrfx_err_t nrfx_saadc_init(uint8_t interrupt_priority);
/**
* @brief Function for uninitializing the SAADC driver.
*
* This function stops all ongoing conversions and disables all channels.
*/
void nrfx_saadc_uninit(void);
/**
* @brief Function for configuring the SAADC channels.
*
* @note The values of the @ref nrf_saadc_channel_config_t.burst fields in channel configurations
* are ignored. They will be overridden with the value suitable for the selected driver
* operation mode.
* @note The desired mode (simple or advanced) must be set after the channels are configured.
*
* @param[in] p_channels Pointer to the array of channel configuration structures.
* @param[in] channel_count Number of channels to be configured.
*
* @retval NRFX_SUCCESS Configuration was successful.
* @retval NRFX_ERROR_BUSY There is a conversion or calibration ongoing.
* @retval NRFX_ERROR_INVALID_PARAM Attempt to configure the same channel more than once.
*/
nrfx_err_t nrfx_saadc_channels_config(nrfx_saadc_channel_t const * p_channels,
uint32_t channel_count);
/**
* @brief Function for setting the SAADC driver in the simple mode.
*
* The simple mode allows obtaining a single sample from each requested channel.
* The conversion can be done in a blocking or non-blocking manner.
* Sampling is initiated by calling @ref nrfx_saadc_mode_trigger() once.
*
* @param[in] channel_mask Bitmask of channels to be used in the simple mode.
* @param[in] resolution Resolution configuration.
* @param[in] oversampling Oversampling configuration.
* @param[in] event_handler Event handler provided by the user. In case of providing NULL,
* the conversion will be performed in the blocking manner.
*
* @retval NRFX_SUCCESS Initialization was successful.
* @retval NRFX_ERROR_BUSY There is a conversion or calibration ongoing.
* @retval NRFX_ERROR_INVALID_PARAM Attempt to activate channel that is not configured.
*/
nrfx_err_t nrfx_saadc_simple_mode_set(uint32_t channel_mask,
nrf_saadc_resolution_t resolution,
nrf_saadc_oversample_t oversampling,
nrfx_saadc_event_handler_t event_handler);
/**
* @brief Function for setting the SAADC driver in the advanced mode.
*
* The advanced mode allows performing double-buffered conversions of arbitrary length.
* The conversions can be done in a blocking or non-blocking manner. When performing conversions
* in the non-blocking manner and @ref nrfx_saadc_adv_config_t.internal_timer_cc is set to 0,
* sampling needs to be done by triggering @ref NRF_SAADC_TASK_SAMPLE externally
* (for example by using the TIMER and/or the PPI/DPPI).
* When performing conversions in the non-blocking manner and @ref nrfx_saadc_adv_config_t.start_on_end
* is false, the @ref NRF_SAADC_TASK_START needs to be triggered on @ref NRF_SAADC_EVENT_END
* externally (for example by using the PPI/DPPI).
* Sampling is initiated by calling @ref nrfx_saadc_mode_trigger(). In case of performing
* conversions in the blocking manner, @ref nrfx_saadc_mode_trigger() may need to be called several
* times as each call sample each requested channel once.
*
* @note The internal timer can only be used when a single input channel is enabled.
* @note The internal timer can only be used in the non-blocking mode.
*
* @param[in] channel_mask Bitmask of channels to be used in the advanced mode.
* @param[in] resolution Resolution configuration.
* @param[in] p_config Pointer to the structure with the advanced mode configuration.
* @param[in] event_handler Event handler provided by the user. In case of providing NULL,
* the conversion will be performed in the blocking manner.
*
* @retval NRFX_SUCCESS Initialization was successful.
* @retval NRFX_ERROR_BUSY There is a conversion or calibration ongoing.
* @retval NRFX_ERROR_INVALID_PARAM Attempt to activate channel that is not configured.
* @retval NRFX_ERROR_NOT_SUPPORTED Attempt to activate internal timer or oversampling without burst
* with multiple channels enabled.
*/
nrfx_err_t nrfx_saadc_advanced_mode_set(uint32_t channel_mask,
nrf_saadc_resolution_t resolution,
nrfx_saadc_adv_config_t const * p_config,
nrfx_saadc_event_handler_t event_handler);
/**
* @brief Function for supplying the buffer to be used in the next part of
* the conversion.
*
* @param[in] p_buffer Pointer to the buffer to be filled with conversion results.
* @param[in] size Number of @ref nrf_saadc_value_t samples in buffer.
*
* @retval NRFX_SUCCESS Buffer was supplied successfully.
* @retval NRFX_ERROR_INVALID_ADDR The provided buffer is not in the Data RAM region.
* @retval NRFX_ERROR_INVALID_LENGTH The provided buffer is not aligned to the number of activated channels
* or is too long for the EasyDMA to handle.
* @retval NRFX_ERROR_INVALID_STATE The driver is in the idle mode.
* @retval NRFX_ERROR_ALREADY_INITIALIZED Both buffers for double-buffered conversions are already set.
*/
nrfx_err_t nrfx_saadc_buffer_set(nrf_saadc_value_t * p_buffer, uint16_t size);
/**
* @brief Function for triggering the conversion in the configured mode.
*
* @retval NRFX_SUCCESS Operation finished successfully in the blocking manner or started
* successfully in the non-blocking manner.
* @retval NRFX_ERROR_BUSY The driver is performing the conversion in the advanced blocking mode.
* Call the function again to continue the conversion.
* @retval NRFX_ERROR_NO_MEM There is no buffer provided.
* Supply the buffer using @ref nrfx_saadc_buffer_set() and try again.
* @retval NRFX_ERROR_INVALID_STATE There is an ongoing conversion being performed in the non-blocking manner
* or the driver is in the idle mode.
*/
nrfx_err_t nrfx_saadc_mode_trigger(void);
/**
* @brief Function for aborting the ongoing and buffered conversions.
*
* @note @ref NRFX_SAADC_EVT_DONE event will be generated if there is a conversion in progress.
* Event will contain number of words in the sample buffer.
*/
void nrfx_saadc_abort(void);
/**
* @brief Function for setting the SAADC channel limits.
*
* When limits are enabled and the conversion result exceeds the defined bounds,
* the handler function is called with the corresponding event as parameter.
*
* @note Before the limits are set, the driver operation mode (simple or advanced) has
* to be configured. Only non-blocking conversions can be monitored.
*
* @note Changing of the driver operation mode disables all configured limits.
*
* @param[in] channel Channel index.
* @param[in] limit_low Limit low value to generate interrupt. Use @c INT16_MIN
* to disable interrupt generation.
* @param[in] limit_high Limit high value to generate interrupt. Use @c INT16_MAX
* to disable interrupt generation.
*
* @retval NRFX_SUCCESS Requested channel limits were set.
* @retval NRFX_ERROR_INVALID_PARAM Attempt to activate the limits on disabled channel.
* @retval NRFX_ERROR_FORBIDDEN Attempt to activate the limits for blocking conversions.
* @retval NRFX_ERROR_INVALID_STATE Attempt to activate the limits without configured mode.
*/
nrfx_err_t nrfx_saadc_limits_set(uint8_t channel, int16_t limit_low, int16_t limit_high);
/**
* @brief Function for starting the SAADC offset calibration.
*
* @note This function cancels the currently selected driver operation mode, if any.
* The desired mode (simple or advanced) must be set after the calibration process completes.
*
* @param[in] event_handler Event handler provided by the user. In case of providing NULL,
* the calibration will be performed in the blocking manner.
*
* @retval NRFX_SUCCESS Calibration finished successfully in the blocking manner
* or started successfully in the non-blocking manner.
* @retval NRFX_ERROR_BUSY There is a conversion or calibration ongoing.
*/
nrfx_err_t nrfx_saadc_offset_calibrate(nrfx_saadc_event_handler_t event_handler);
#endif // NRFX_SAADC_V2_H__
+255
View File
@@ -0,0 +1,255 @@
/**
* Copyright (c) 2015 - 2021, 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, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, 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 its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA 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 NRFX_SPI_H__
#define NRFX_SPI_H__
#include <nrfx.h>
#include <hal/nrf_spi.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @defgroup nrfx_spi SPI driver
* @{
* @ingroup nrf_spi
* @brief Serial Peripheral Interface master (SPI) driver.
*/
/** @brief Data structure of the Serial Peripheral Interface master (SPI) driver instance. */
typedef struct
{
NRF_SPI_Type * p_reg; ///< Pointer to a structure with SPI registers.
uint8_t drv_inst_idx; ///< Index of the driver instance. For internal use only.
} nrfx_spi_t;
enum {
#if NRFX_CHECK(NRFX_SPI0_ENABLED)
NRFX_SPI0_INST_IDX,
#endif
#if NRFX_CHECK(NRFX_SPI1_ENABLED)
NRFX_SPI1_INST_IDX,
#endif
#if NRFX_CHECK(NRFX_SPI2_ENABLED)
NRFX_SPI2_INST_IDX,
#endif
NRFX_SPI_ENABLED_COUNT
};
/** @brief Macro for creating an instance of the SPI master driver. */
#define NRFX_SPI_INSTANCE(id) \
{ \
.p_reg = NRFX_CONCAT_2(NRF_SPI, id), \
.drv_inst_idx = NRFX_CONCAT_3(NRFX_SPI, id, _INST_IDX), \
}
/**
* @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 NRFX_SPI_PIN_NOT_USED 0xFF
/** @brief Configuration structure of the SPI master driver instance. */
typedef struct
{
uint8_t sck_pin; ///< SCK pin number.
uint8_t mosi_pin; ///< MOSI pin number (optional).
/**< Set to @ref NRFX_SPI_PIN_NOT_USED
* if this signal is not needed. */
uint8_t miso_pin; ///< MISO pin number (optional).
/**< Set to @ref NRFX_SPI_PIN_NOT_USED
* if this signal is not needed. */
uint8_t ss_pin; ///< Slave Select pin number (optional).
/**< Set to @ref NRFX_SPI_PIN_NOT_USED
* if this signal is not needed. The driver
* supports only active low for this signal.
* If the signal must be active high,
* it must be controlled externally. */
uint8_t irq_priority; ///< Interrupt priority.
uint8_t orc; ///< Overrun character.
/**< This character is used when all bytes from the TX buffer are sent,
but the transfer continues due to RX. */
nrf_spi_frequency_t frequency; ///< SPI frequency.
nrf_spi_mode_t mode; ///< SPI mode.
nrf_spi_bit_order_t bit_order; ///< SPI bit order.
} nrfx_spi_config_t;
/** @brief SPI master instance default configuration. */
#define NRFX_SPI_DEFAULT_CONFIG \
{ \
.sck_pin = NRFX_SPI_PIN_NOT_USED, \
.mosi_pin = NRFX_SPI_PIN_NOT_USED, \
.miso_pin = NRFX_SPI_PIN_NOT_USED, \
.ss_pin = NRFX_SPI_PIN_NOT_USED, \
.irq_priority = NRFX_SPI_DEFAULT_CONFIG_IRQ_PRIORITY, \
.orc = 0xFF, \
.frequency = NRF_SPI_FREQ_4M, \
.mode = NRF_SPI_MODE_0, \
.bit_order = NRF_SPI_BIT_ORDER_MSB_FIRST, \
}
/** @brief Single transfer descriptor structure. */
typedef struct
{
uint8_t const * p_tx_buffer; ///< Pointer to TX buffer.
size_t tx_length; ///< TX buffer length.
uint8_t * p_rx_buffer; ///< Pointer to RX buffer.
size_t rx_length; ///< RX buffer length.
}nrfx_spi_xfer_desc_t;
/**
* @brief Macro for setting up single transfer descriptor.
*
* This macro is for internal use only.
*/
#define NRFX_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 the duplex TX RX transfer. */
#define NRFX_SPI_XFER_TRX(p_tx_buf, tx_length, p_rx_buf, rx_length) \
NRFX_SPI_SINGLE_XFER(p_tx_buf, tx_length, p_rx_buf, rx_length)
/** @brief Macro for setting the TX transfer. */
#define NRFX_SPI_XFER_TX(p_buf, length) \
NRFX_SPI_SINGLE_XFER(p_buf, length, NULL, 0)
/** @brief Macro for setting the RX transfer. */
#define NRFX_SPI_XFER_RX(p_buf, length) \
NRFX_SPI_SINGLE_XFER(NULL, 0, p_buf, length)
/**
* @brief SPI master driver event types, passed to the handler routine provided
* during initialization.
*/
typedef enum
{
NRFX_SPI_EVENT_DONE, ///< Transfer done.
} nrfx_spi_evt_type_t;
/** @brief SPI master event description with transmission details. */
typedef struct
{
nrfx_spi_evt_type_t type; ///< Event type.
nrfx_spi_xfer_desc_t xfer_desc; ///< Transfer details.
} nrfx_spi_evt_t;
/** @brief SPI master driver event handler type. */
typedef void (* nrfx_spi_evt_handler_t)(nrfx_spi_evt_t const * p_event,
void * p_context);
/**
* @brief Function for initializing the SPI master driver instance.
*
* This function configures and enables the specified peripheral.
*
* @param[in] p_instance Pointer to the driver instance structure.
* @param[in] p_config Pointer to the structure with the initial configuration.
* @param[in] handler Event handler provided by the user. If NULL, transfers
* will be performed in blocking mode.
* @param[in] p_context Context passed to the event handler.
*
* @retval NRFX_SUCCESS Initialization was successful.
* @retval NRFX_ERROR_INVALID_STATE The driver was already initialized.
* @retval NRFX_ERROR_BUSY Some other peripheral with the same
* instance ID is already in use. This is
* possible only if @ref nrfx_prs module
* is enabled.
*/
nrfx_err_t nrfx_spi_init(nrfx_spi_t const * const p_instance,
nrfx_spi_config_t const * p_config,
nrfx_spi_evt_handler_t handler,
void * p_context);
/**
* @brief Function for uninitializing the SPI master driver instance.
*
* @param[in] p_instance Pointer to the driver instance structure.
*/
void nrfx_spi_uninit(nrfx_spi_t const * const p_instance);
/**
* @brief Function for starting the SPI data transfer.
*
* If an event handler was provided in the @ref nrfx_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.
*
* @param p_instance Pointer to the driver instance structure.
* @param p_xfer_desc Pointer to the transfer descriptor.
* @param flags Transfer options (0 for default settings).
* Currently, no additional flags are available.
*
* @retval NRFX_SUCCESS The procedure is successful.
* @retval NRFX_ERROR_BUSY The driver is not ready for a new transfer.
* @retval NRFX_ERROR_NOT_SUPPORTED The provided parameters are not supported.
*/
nrfx_err_t nrfx_spi_xfer(nrfx_spi_t const * const p_instance,
nrfx_spi_xfer_desc_t const * p_xfer_desc,
uint32_t flags);
/**
* @brief Function for aborting the ongoing transfer.
*
* @param[in] p_instance Pointer to the driver instance structure.
*/
void nrfx_spi_abort(nrfx_spi_t const * p_instance);
/** @} */
void nrfx_spi_0_irq_handler(void);
void nrfx_spi_1_irq_handler(void);
void nrfx_spi_2_irq_handler(void);
#ifdef __cplusplus
}
#endif
#endif // NRFX_SPI_H__
+385
View File
@@ -0,0 +1,385 @@
/**
* Copyright (c) 2015 - 2021, 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, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, 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 its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA 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 NRFX_SPIM_H__
#define NRFX_SPIM_H__
#include <nrfx.h>
#include <hal/nrf_spim.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @defgroup nrfx_spim SPIM driver
* @{
* @ingroup nrf_spim
* @brief Serial Peripheral Interface Master with EasyDMA (SPIM) driver.
*/
/** @brief Data structure of the Serial Peripheral Interface Master with EasyDMA (SPIM) driver instance. */
typedef struct
{
NRF_SPIM_Type * p_reg; ///< Pointer to a structure with SPIM registers.
uint8_t drv_inst_idx; ///< Index of the driver instance. For internal use only.
} nrfx_spim_t;
#ifndef __NRFX_DOXYGEN__
enum {
#if NRFX_CHECK(NRFX_SPIM0_ENABLED)
NRFX_SPIM0_INST_IDX,
#endif
#if NRFX_CHECK(NRFX_SPIM1_ENABLED)
NRFX_SPIM1_INST_IDX,
#endif
#if NRFX_CHECK(NRFX_SPIM2_ENABLED)
NRFX_SPIM2_INST_IDX,
#endif
#if NRFX_CHECK(NRFX_SPIM3_ENABLED)
NRFX_SPIM3_INST_IDX,
#endif
NRFX_SPIM_ENABLED_COUNT
};
#endif
/** @brief Macro for creating an instance of the SPIM driver. */
#define NRFX_SPIM_INSTANCE(id) \
{ \
.p_reg = NRFX_CONCAT_2(NRF_SPIM, id), \
.drv_inst_idx = NRFX_CONCAT_3(NRFX_SPIM, id, _INST_IDX), \
}
/**
* @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 NRFX_SPIM_PIN_NOT_USED 0xFF
/** @brief Configuration structure of the SPIM driver instance. */
typedef struct
{
uint8_t sck_pin; ///< SCK pin number.
uint8_t mosi_pin; ///< MOSI pin number (optional).
/**< Set to @ref NRFX_SPIM_PIN_NOT_USED
* if this signal is not needed. */
uint8_t miso_pin; ///< MISO pin number (optional).
/**< Set to @ref NRFX_SPIM_PIN_NOT_USED
* if this signal is not needed. */
uint8_t ss_pin; ///< Slave Select pin number (optional).
/**< Set to @ref NRFX_SPIM_PIN_NOT_USED
* if this signal is not needed. */
bool ss_active_high; ///< Polarity of the Slave Select pin during transmission.
uint8_t irq_priority; ///< Interrupt priority.
uint8_t orc; ///< Overrun character.
/**< This character is used when all bytes from the TX buffer are sent,
but the transfer continues due to RX. */
nrf_spim_frequency_t frequency; ///< SPIM frequency.
nrf_spim_mode_t mode; ///< SPIM mode.
nrf_spim_bit_order_t bit_order; ///< SPIM bit order.
#if NRFX_CHECK(NRFX_SPIM_EXTENDED_ENABLED) || defined(__NRFX_DOXYGEN__)
uint8_t dcx_pin; ///< D/CX pin number (optional).
uint8_t rx_delay; ///< Sample delay for input serial data on MISO.
/**< The value specifies the delay, in number of 64 MHz clock cycles
* (15.625 ns), from the the sampling edge of SCK (leading edge for
* CONFIG.CPHA = 0, trailing edge for CONFIG.CPHA = 1) until
* the input serial data is sampled. */
bool use_hw_ss; ///< Indication to use software or hardware controlled Slave Select pin.
uint8_t ss_duration; ///< Slave Select duration before and after transmission.
/**< Minimum duration between the edge of CSN and the edge of SCK and minimum
* duration of CSN must stay inactive between transactions.
* The value is specified in number of 64 MHz clock cycles (15.625 ns).
* Supported only for hardware-controlled Slave Select. */
#endif
} nrfx_spim_config_t;
#if NRFX_CHECK(NRFX_SPIM_EXTENDED_ENABLED) || defined(__NRFX_DOXYGEN__)
/**
* @brief Extended default configuration of the SPIM instance.
*/
#define NRFX_SPIM_DEFAULT_EXTENDED_CONFIG \
.dcx_pin = NRFX_SPIM_PIN_NOT_USED, \
.rx_delay = 0x02, \
.use_hw_ss = false, \
.ss_duration = 0x02,
#else
#define NRFX_SPIM_DEFAULT_EXTENDED_CONFIG
#endif
/** @brief The default configuration of the SPIM master instance. */
#define NRFX_SPIM_DEFAULT_CONFIG \
{ \
.sck_pin = NRFX_SPIM_PIN_NOT_USED, \
.mosi_pin = NRFX_SPIM_PIN_NOT_USED, \
.miso_pin = NRFX_SPIM_PIN_NOT_USED, \
.ss_pin = NRFX_SPIM_PIN_NOT_USED, \
.ss_active_high = false, \
.irq_priority = NRFX_SPIM_DEFAULT_CONFIG_IRQ_PRIORITY, \
.orc = 0xFF, \
.frequency = NRF_SPIM_FREQ_4M, \
.mode = NRF_SPIM_MODE_0, \
.bit_order = NRF_SPIM_BIT_ORDER_MSB_FIRST, \
NRFX_SPIM_DEFAULT_EXTENDED_CONFIG \
}
/** @brief Flag indicating that TX buffer address will be incremented after transfer. */
#define NRFX_SPIM_FLAG_TX_POSTINC (1UL << 0)
/** @brief Flag indicating that RX buffer address will be incremented after transfer. */
#define NRFX_SPIM_FLAG_RX_POSTINC (1UL << 1)
/** @brief Flag indicating that the interrupt after each transfer will be suppressed, and the event handler will not be called. */
#define NRFX_SPIM_FLAG_NO_XFER_EVT_HANDLER (1UL << 2)
/** @brief Flag indicating that the transfer will be set up, but not started. */
#define NRFX_SPIM_FLAG_HOLD_XFER (1UL << 3)
/** @brief Flag indicating that the transfer will be executed multiple times. */
#define NRFX_SPIM_FLAG_REPEATED_XFER (1UL << 4)
/** @brief Single transfer descriptor structure. */
typedef struct
{
uint8_t const * p_tx_buffer; ///< Pointer to TX buffer.
size_t tx_length; ///< TX buffer length.
uint8_t * p_rx_buffer; ///< Pointer to RX buffer.
size_t rx_length; ///< RX buffer length.
} nrfx_spim_xfer_desc_t;
/**
* @brief Macro for setting up single transfer descriptor.
*
* This macro is for internal use only.
*/
#define NRFX_SPIM_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 the duplex TX RX transfer. */
#define NRFX_SPIM_XFER_TRX(p_tx_buf, tx_length, p_rx_buf, rx_length) \
NRFX_SPIM_SINGLE_XFER(p_tx_buf, tx_length, p_rx_buf, rx_length)
/** @brief Macro for setting the TX transfer. */
#define NRFX_SPIM_XFER_TX(p_buf, length) \
NRFX_SPIM_SINGLE_XFER(p_buf, length, NULL, 0)
/** @brief Macro for setting the RX transfer. */
#define NRFX_SPIM_XFER_RX(p_buf, length) \
NRFX_SPIM_SINGLE_XFER(NULL, 0, p_buf, length)
/**
* @brief SPIM master driver event types, passed to the handler routine provided
* during initialization.
*/
typedef enum
{
NRFX_SPIM_EVENT_DONE, ///< Transfer done.
} nrfx_spim_evt_type_t;
/** @brief SPIM event description with transmission details. */
typedef struct
{
nrfx_spim_evt_type_t type; ///< Event type.
nrfx_spim_xfer_desc_t xfer_desc; ///< Transfer details.
} nrfx_spim_evt_t;
/** @brief SPIM driver event handler type. */
typedef void (* nrfx_spim_evt_handler_t)(nrfx_spim_evt_t const * p_event,
void * p_context);
/**
* @brief Function for initializing the SPIM driver instance.
*
* This function configures and enables the specified peripheral.
*
* @param[in] p_instance Pointer to the driver instance structure.
* @param[in] p_config Pointer to the structure with the initial configuration.
* @param[in] handler Event handler provided by the user. If NULL, transfers
* will be performed in blocking mode.
* @param[in] p_context Context passed to event handler.
*
* @retval NRFX_SUCCESS Initialization was successful.
* @retval NRFX_ERROR_INVALID_STATE The driver was already initialized.
* @retval NRFX_ERROR_BUSY Some other peripheral with the same
* instance ID is already in use. This is
* possible only if @ref nrfx_prs module
* is enabled.
* @retval NRFX_ERROR_NOT_SUPPORTED Requested configuration is not supported
* by the SPIM instance.
*/
nrfx_err_t nrfx_spim_init(nrfx_spim_t const * const p_instance,
nrfx_spim_config_t const * p_config,
nrfx_spim_evt_handler_t handler,
void * p_context);
/**
* @brief Function for uninitializing the SPIM driver instance.
*
* @param[in] p_instance Pointer to the driver instance structure.
*/
void nrfx_spim_uninit(nrfx_spim_t const * const p_instance);
/**
* @brief Function for starting the SPIM data transfer.
*
* Additional options are provided using the @c flags parameter:
*
* - @ref NRFX_SPIM_FLAG_TX_POSTINC and @ref NRFX_SPIM_FLAG_RX_POSTINC -
* Post-incrementation of buffer addresses.
* - @ref NRFX_SPIM_FLAG_HOLD_XFER - Driver is not starting the transfer. Use this
* flag if the transfer is triggered externally by PPI. Use
* @ref nrfx_spim_start_task_get to get the address of the start task.
* - @ref NRFX_SPIM_FLAG_NO_XFER_EVT_HANDLER - No user event handler after transfer
* completion. This also means no interrupt at the end of the transfer.
* If @ref NRFX_SPIM_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 nrfx_spim_end_event_get function can be used to detect end of transfer. Option can be used
* together with @ref NRFX_SPIM_FLAG_REPEATED_XFER to prepare a sequence of SPI transfers
* without interruptions.
* - @ref NRFX_SPIM_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 NRFX_SPIM_FLAG_RX_POSTINC,
* @ref NRFX_SPIM_FLAG_NO_XFER_EVT_HANDLER, and @ref NRFX_SPIM_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 nrfx_spim_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 NRFX_SPIM_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.
*
* @note Peripherals using EasyDMA (including SPIM) require the transfer buffers
* to be placed in the Data RAM region. If this condition is not met,
* this function will fail with the error code NRFX_ERROR_INVALID_ADDR.
*
* @param p_instance Pointer to the driver instance structure.
* @param p_xfer_desc Pointer to the transfer descriptor.
* @param flags Transfer options (0 for default settings).
*
* @retval NRFX_SUCCESS The procedure is successful.
* @retval NRFX_ERROR_BUSY The driver is not ready for a new transfer.
* @retval NRFX_ERROR_NOT_SUPPORTED The provided parameters are not supported.
* @retval NRFX_ERROR_INVALID_ADDR The provided buffers are not placed in the Data
* RAM region.
*/
nrfx_err_t nrfx_spim_xfer(nrfx_spim_t const * const p_instance,
nrfx_spim_xfer_desc_t const * p_xfer_desc,
uint32_t flags);
#if NRFX_CHECK(NRFX_SPIM_EXTENDED_ENABLED) || defined(__NRFX_DOXYGEN__)
/**
* @brief Function for starting the SPIM data transfer with DCX control.
*
* See @ref nrfx_spim_xfer for description of additional options of transfer
* provided by the @c flags parameter.
*
* @note Peripherals that use EasyDMA (including SPIM) require the transfer buffers
* to be placed in the Data RAM region. If this condition is not met,
* this function will fail with the error code NRFX_ERROR_INVALID_ADDR.
*
* @param p_instance Pointer to the driver instance structure.
* @param p_xfer_desc Pointer to the transfer descriptor.
* @param flags Transfer options (0 for default settings).
* @param cmd_length Length of the command bytes preceding the data
* bytes. The DCX line will be low during transmission
* of command bytes and high during transmission of data bytes.
* Maximum value available for dividing the transmitted bytes
* into command bytes and data bytes is @ref NRF_SPIM_DCX_CNT_ALL_CMD - 1.
* The @ref NRF_SPIM_DCX_CNT_ALL_CMD value passed as the
* @c cmd_length parameter causes all transmitted bytes
* to be marked as command bytes.
*
* @retval NRFX_SUCCESS The procedure is successful.
* @retval NRFX_ERROR_BUSY The driver is not ready for a new transfer.
* @retval NRFX_ERROR_NOT_SUPPORTED The provided parameters are not supported.
* @retval NRFX_ERROR_INVALID_ADDR The provided buffers are not placed in the Data
* RAM region.
*/
nrfx_err_t nrfx_spim_xfer_dcx(nrfx_spim_t const * const p_instance,
nrfx_spim_xfer_desc_t const * p_xfer_desc,
uint32_t flags,
uint8_t cmd_length);
#endif
/**
* @brief Function for returning the address of a SPIM start task.
*
* This function is to be used if @ref nrfx_spim_xfer was called with the flag @ref NRFX_SPIM_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 Pointer to the driver instance structure.
*
* @return Start task address.
*/
uint32_t nrfx_spim_start_task_get(nrfx_spim_t const * p_instance);
/**
* @brief Function for returning the address of a END SPIM event.
*
* The END event can be used to detect the end of a transfer
* if the @ref NRFX_SPIM_FLAG_NO_XFER_EVT_HANDLER option is used.
*
* @param[in] p_instance Pointer to the driver instance structure.
*
* @return END event address.
*/
uint32_t nrfx_spim_end_event_get(nrfx_spim_t const * p_instance);
/**
* @brief Function for aborting ongoing transfer.
*
* @param[in] p_instance Pointer to the driver instance structure.
*/
void nrfx_spim_abort(nrfx_spim_t const * p_instance);
/** @} */
void nrfx_spim_0_irq_handler(void);
void nrfx_spim_1_irq_handler(void);
void nrfx_spim_2_irq_handler(void);
void nrfx_spim_3_irq_handler(void);
#ifdef __cplusplus
}
#endif
#endif // NRFX_SPIM_H__
+409
View File
@@ -0,0 +1,409 @@
/**
* Copyright (c) 2015 - 2021, 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, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, 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 its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA 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 NRFX_TIMER_H__
#define NRFX_TIMER_H__
#include <nrfx.h>
#include <hal/nrf_timer.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @defgroup nrfx_timer Timer driver
* @{
* @ingroup nrf_timer
* @brief TIMER peripheral driver.
*/
/**
* @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; ///< Index of the driver instance. For internal use only.
uint8_t cc_channel_count; ///< Number of capture/compare channels.
} nrfx_timer_t;
/** @brief Macro for creating a timer driver instance. */
#define NRFX_TIMER_INSTANCE(id) \
{ \
.p_reg = NRFX_CONCAT_2(NRF_TIMER, id), \
.instance_id = NRFX_CONCAT_3(NRFX_TIMER, id, _INST_IDX), \
.cc_channel_count = NRF_TIMER_CC_CHANNEL_COUNT(id), \
}
#ifndef __NRFX_DOXYGEN__
enum {
#if NRFX_CHECK(NRFX_TIMER0_ENABLED)
NRFX_TIMER0_INST_IDX,
#endif
#if NRFX_CHECK(NRFX_TIMER1_ENABLED)
NRFX_TIMER1_INST_IDX,
#endif
#if NRFX_CHECK(NRFX_TIMER2_ENABLED)
NRFX_TIMER2_INST_IDX,
#endif
#if NRFX_CHECK(NRFX_TIMER3_ENABLED)
NRFX_TIMER3_INST_IDX,
#endif
#if NRFX_CHECK(NRFX_TIMER4_ENABLED)
NRFX_TIMER4_INST_IDX,
#endif
NRFX_TIMER_ENABLED_COUNT
};
#endif
/** @brief The configuration structure of the timer driver instance. */
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.
} nrfx_timer_config_t;
/** @brief Timer driver instance default configuration. */
#define NRFX_TIMER_DEFAULT_CONFIG \
{ \
.frequency = (nrf_timer_frequency_t)NRFX_TIMER_DEFAULT_CONFIG_FREQUENCY,\
.mode = (nrf_timer_mode_t)NRFX_TIMER_DEFAULT_CONFIG_MODE, \
.bit_width = (nrf_timer_bit_width_t)NRFX_TIMER_DEFAULT_CONFIG_BIT_WIDTH,\
.interrupt_priority = NRFX_TIMER_DEFAULT_CONFIG_IRQ_PRIORITY, \
.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 (* nrfx_timer_event_handler_t)(nrf_timer_event_t event_type,
void * p_context);
/**
* @brief Function for initializing the timer.
*
* @param[in] p_instance Pointer to the driver instance structure.
* @param[in] p_config Pointer to the structure with the initial configuration.
* @param[in] timer_event_handler Event handler provided by the user.
* Must not be NULL.
*
* @retval NRFX_SUCCESS Initialization was successful.
* @retval NRFX_ERROR_INVALID_STATE The instance is already initialized.
*/
nrfx_err_t nrfx_timer_init(nrfx_timer_t const * const p_instance,
nrfx_timer_config_t const * p_config,
nrfx_timer_event_handler_t timer_event_handler);
/**
* @brief Function for uninitializing the timer.
*
* @param[in] p_instance Pointer to the driver instance structure.
*/
void nrfx_timer_uninit(nrfx_timer_t const * const p_instance);
/**
* @brief Function for turning on the timer.
*
* @param[in] p_instance Pointer to the driver instance structure.
*/
void nrfx_timer_enable(nrfx_timer_t const * const p_instance);
/**
* @brief Function for turning off the timer.
*
* The timer will allow to enter the lowest possible SYSTEM_ON state
* only after this function is called.
*
* @param[in] p_instance Pointer to the driver instance structure.
*/
void nrfx_timer_disable(nrfx_timer_t const * const p_instance);
/**
* @brief Function for checking the timer state.
*
* @param[in] p_instance Pointer to the driver instance structure.
*
* @retval true Timer is enabled.
* @retval false Timer is not enabled.
*/
bool nrfx_timer_is_enabled(nrfx_timer_t const * const p_instance);
/**
* @brief Function for pausing the timer.
*
* @param[in] p_instance Pointer to the driver instance structure.
*/
void nrfx_timer_pause(nrfx_timer_t const * const p_instance);
/**
* @brief Function for resuming the timer.
*
* @param[in] p_instance Pointer to the driver instance structure.
*/
void nrfx_timer_resume(nrfx_timer_t const * const p_instance);
/**
* @brief Function for clearing the timer.
*
* @param[in] p_instance Pointer to the driver instance structure.
*/
void nrfx_timer_clear(nrfx_timer_t const * const p_instance);
/**
* @brief Function for incrementing the timer.
*
* @param[in] p_instance Pointer to the driver instance structure.
*/
void nrfx_timer_increment(nrfx_timer_t const * const p_instance);
/**
* @brief Function for returning the address of the specified timer task.
*
* @param[in] p_instance Pointer to the driver instance structure.
* @param[in] timer_task Timer task.
*
* @return Task address.
*/
__STATIC_INLINE uint32_t nrfx_timer_task_address_get(nrfx_timer_t const * const p_instance,
nrf_timer_task_t timer_task);
/**
* @brief Function for returning the address of the specified timer capture task.
*
* @param[in] p_instance Pointer to the driver instance structure.
* @param[in] channel Capture channel number.
*
* @return Task address.
*/
__STATIC_INLINE uint32_t nrfx_timer_capture_task_address_get(nrfx_timer_t const * const p_instance,
uint32_t channel);
/**
* @brief Function for returning the address of the specified timer event.
*
* @param[in] p_instance Pointer to the driver instance structure.
* @param[in] timer_event Timer event.
*
* @return Event address.
*/
__STATIC_INLINE uint32_t nrfx_timer_event_address_get(nrfx_timer_t const * const p_instance,
nrf_timer_event_t timer_event);
/**
* @brief Function for returning the address of the specified timer compare event.
*
* @param[in] p_instance Pointer to the driver instance structure.
* @param[in] channel Compare channel number.
*
* @return Event address.
*/
__STATIC_INLINE uint32_t nrfx_timer_compare_event_address_get(nrfx_timer_t const * const p_instance,
uint32_t channel);
/**
* @brief Function for capturing the timer value.
*
* @param[in] p_instance Pointer to the driver instance structure.
* @param[in] cc_channel Capture channel number.
*
* @return Captured value.
*/
uint32_t nrfx_timer_capture(nrfx_timer_t const * const p_instance,
nrf_timer_cc_channel_t cc_channel);
/**
* @brief Function for returning the capture value from the specified channel.
*
* Use this function to read channel values when PPI is used for capturing.
*
* @param[in] p_instance Pointer to the driver instance structure.
* @param[in] cc_channel Capture channel number.
*
* @return Captured value.
*/
__STATIC_INLINE uint32_t nrfx_timer_capture_get(nrfx_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 Pointer to the driver instance structure.
* @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 nrfx_timer_compare(nrfx_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 the extended compare mode.
*
* @param[in] p_instance Pointer to the driver instance structure.
* @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 nrfx_timer_extended_compare(nrfx_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 Pointer to the driver instance structure.
* @param[in] time_us Time in microseconds.
*
* @return Number of ticks.
*/
__STATIC_INLINE uint32_t nrfx_timer_us_to_ticks(nrfx_timer_t const * const p_instance,
uint32_t time_us);
/**
* @brief Function for converting time in milliseconds to timer ticks.
*
* @param[in] p_instance Pointer to the driver instance structure.
* @param[in] time_ms Time in milliseconds.
*
* @return Number of ticks.
*/
__STATIC_INLINE uint32_t nrfx_timer_ms_to_ticks(nrfx_timer_t const * const p_instance,
uint32_t time_ms);
/**
* @brief Function for enabling timer compare interrupt.
*
* @param[in] p_instance Pointer to the driver instance structure.
* @param[in] channel Compare channel.
*/
void nrfx_timer_compare_int_enable(nrfx_timer_t const * const p_instance,
uint32_t channel);
/**
* @brief Function for disabling timer compare interrupt.
*
* @param[in] p_instance Pointer to the driver instance structure.
* @param[in] channel Compare channel.
*/
void nrfx_timer_compare_int_disable(nrfx_timer_t const * const p_instance,
uint32_t channel);
#ifndef SUPPRESS_INLINE_IMPLEMENTATION
__STATIC_INLINE uint32_t nrfx_timer_task_address_get(nrfx_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 nrfx_timer_capture_task_address_get(nrfx_timer_t const * const p_instance,
uint32_t channel)
{
NRFX_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 nrfx_timer_event_address_get(nrfx_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 nrfx_timer_compare_event_address_get(nrfx_timer_t const * const p_instance,
uint32_t channel)
{
NRFX_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 nrfx_timer_capture_get(nrfx_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 nrfx_timer_us_to_ticks(nrfx_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 nrfx_timer_ms_to_ticks(nrfx_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
/** @} */
void nrfx_timer_0_irq_handler(void);
void nrfx_timer_1_irq_handler(void);
void nrfx_timer_2_irq_handler(void);
void nrfx_timer_3_irq_handler(void);
void nrfx_timer_4_irq_handler(void);
#ifdef __cplusplus
}
#endif
#endif // NRFX_TIMER_H__
+407
View File
@@ -0,0 +1,407 @@
/**
* Copyright (c) 2015 - 2021, 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, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, 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 its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA 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 NRFX_TWI_H__
#define NRFX_TWI_H__
#include <nrfx.h>
#include <nrfx_twi_twim.h>
#include <hal/nrf_twi.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @defgroup nrfx_twi TWI driver
* @{
* @ingroup nrf_twi
* @brief Two Wire Interface master (TWI) peripheral driver.
*/
/**
* @brief Structure for the TWI master driver instance.
*/
typedef struct
{
NRF_TWI_Type * p_twi; ///< Pointer to a structure with TWI registers.
uint8_t drv_inst_idx; ///< Index of the driver instance. For internal use only.
} nrfx_twi_t;
/** @brief Macro for creating a TWI master driver instance. */
#define NRFX_TWI_INSTANCE(id) \
{ \
.p_twi = NRFX_CONCAT_2(NRF_TWI, id), \
.drv_inst_idx = NRFX_CONCAT_3(NRFX_TWI, id, _INST_IDX), \
}
#ifndef __NRFX_DOXYGEN__
enum {
#if NRFX_CHECK(NRFX_TWI0_ENABLED)
NRFX_TWI0_INST_IDX,
#endif
#if NRFX_CHECK(NRFX_TWI1_ENABLED)
NRFX_TWI1_INST_IDX,
#endif
NRFX_TWI_ENABLED_COUNT
};
#endif
/** @brief Structure for the configuration of the TWI master driver instance. */
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.
bool hold_bus_uninit; ///< Hold pull up state on GPIO pins after uninit.
} nrfx_twi_config_t;
/** @brief The default configuration of the TWI master driver instance. */
#define NRFX_TWI_DEFAULT_CONFIG \
{ \
.frequency = (nrf_twi_frequency_t)NRFX_TWI_DEFAULT_CONFIG_FREQUENCY, \
.scl = 31, \
.sda = 31, \
.interrupt_priority = NRFX_TWI_DEFAULT_CONFIG_IRQ_PRIORITY, \
.hold_bus_uninit = NRFX_TWI_DEFAULT_CONFIG_HOLD_BUS_UNINIT, \
}
/** @brief Flag indicating that the interrupt after each transfer will be suppressed, and the event handler will not be called. */
#define NRFX_TWI_FLAG_NO_XFER_EVT_HANDLER (1UL << 2)
/** @brief Flag indicating that the TX transfer will not end with a stop condition. */
#define NRFX_TWI_FLAG_TX_NO_STOP (1UL << 5)
/** @brief Flag indicating that the transfer will be suspended. */
#define NRFX_TWI_FLAG_SUSPEND (1UL << 6)
/** @brief TWI master driver event types. */
typedef enum
{
NRFX_TWI_EVT_DONE, ///< Transfer completed event.
NRFX_TWI_EVT_ADDRESS_NACK, ///< Error event: NACK received after sending the address.
NRFX_TWI_EVT_DATA_NACK, ///< Error event: NACK received after sending a data byte.
NRFX_TWI_EVT_OVERRUN, ///< Error event: The unread data is replaced by new data.
NRFX_TWI_EVT_BUS_ERROR ///< Error event: An unexpected transition occurred on the bus.
} nrfx_twi_evt_type_t;
/** @brief TWI master driver transfer types. */
typedef enum
{
NRFX_TWI_XFER_TX, ///< TX transfer.
NRFX_TWI_XFER_RX, ///< RX transfer.
NRFX_TWI_XFER_TXRX, ///< TX transfer followed by RX transfer with repeated start.
NRFX_TWI_XFER_TXTX ///< TX transfer followed by TX transfer with repeated start.
} nrfx_twi_xfer_type_t;
/** @brief Structure for a TWI transfer descriptor. */
typedef struct
{
nrfx_twi_xfer_type_t type; ///< Type of transfer.
uint8_t address; ///< Slave address.
size_t primary_length; ///< Number of bytes transferred.
size_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.
} nrfx_twi_xfer_desc_t;
/** @brief Macro for setting the TX transfer descriptor. */
#define NRFX_TWI_XFER_DESC_TX(addr, p_data, length) \
{ \
.type = NRFX_TWI_XFER_TX, \
.address = (addr), \
.primary_length = (length), \
.secondary_length = 0, \
.p_primary_buf = (p_data), \
.p_secondary_buf = NULL, \
}
/** @brief Macro for setting the RX transfer descriptor. */
#define NRFX_TWI_XFER_DESC_RX(addr, p_data, length) \
{ \
.type = NRFX_TWI_XFER_RX, \
.address = (addr), \
.primary_length = (length), \
.secondary_length = 0, \
.p_primary_buf = (p_data), \
.p_secondary_buf = NULL, \
}
/** @brief Macro for setting the TX-RX transfer descriptor. */
#define NRFX_TWI_XFER_DESC_TXRX(addr, p_tx, tx_len, p_rx, rx_len) \
{ \
.type = NRFX_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 TX-TX transfer descriptor. */
#define NRFX_TWI_XFER_DESC_TXTX(addr, p_tx, tx_len, p_tx2, tx_len2) \
{ \
.type = NRFX_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
{
nrfx_twi_evt_type_t type; ///< Event type.
nrfx_twi_xfer_desc_t xfer_desc; ///< Transfer details.
} nrfx_twi_evt_t;
/** @brief TWI event handler prototype. */
typedef void (* nrfx_twi_evt_handler_t)(nrfx_twi_evt_t const * p_event,
void * p_context);
/**
* @brief Function for initializing the TWI driver instance.
*
* @param[in] p_instance Pointer to the driver instance structure.
* @param[in] p_config Pointer to the structure with the initial configuration.
* @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 NRFX_SUCCESS Initialization is successful.
* @retval NRFX_ERROR_INVALID_STATE The driver is in invalid state.
* @retval NRFX_ERROR_BUSY Some other peripheral with the same
* instance ID is already in use. This is
* possible only if @ref nrfx_prs module
* is enabled.
*/
nrfx_err_t nrfx_twi_init(nrfx_twi_t const * p_instance,
nrfx_twi_config_t const * p_config,
nrfx_twi_evt_handler_t event_handler,
void * p_context);
/**
* @brief Function for uninitializing the TWI instance.
*
* @param[in] p_instance Pointer to the driver instance structure.
*/
void nrfx_twi_uninit(nrfx_twi_t const * p_instance);
/**
* @brief Function for enabling the TWI instance.
*
* @param[in] p_instance Pointer to the driver instance structure.
*/
void nrfx_twi_enable(nrfx_twi_t const * p_instance);
/**
* @brief Function for disabling the TWI instance.
*
* @param[in] p_instance Pointer to the driver instance structure.
*/
void nrfx_twi_disable(nrfx_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 NRFX_ERROR_BUSY.
*
* @note This function is deprecated. Use @ref nrfx_twi_xfer instead.
*
* @param[in] p_instance Pointer to the driver instance structure.
* @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 NRFX_SUCCESS The procedure is successful.
* @retval NRFX_ERROR_BUSY The driver is not ready for a new transfer.
* @retval NRFX_ERROR_INTERNAL An error is detected by hardware.
* @retval NRFX_ERROR_INVALID_STATE RX transaction is suspended on bus.
* @retval NRFX_ERROR_DRV_TWI_ERR_ANACK Negative acknowledgement (NACK) is received after sending
* the address in polling mode.
* @retval NRFX_ERROR_DRV_TWI_ERR_DNACK Negative acknowledgement (NACK) is received after sending
* a data byte in polling mode.
*/
nrfx_err_t nrfx_twi_tx(nrfx_twi_t const * p_instance,
uint8_t address,
uint8_t const * p_data,
size_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 NRFX_ERROR_BUSY.
*
* @note This function is deprecated. Use @ref nrfx_twi_xfer instead.
*
* @param[in] p_instance Pointer to the driver instance structure.
* @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 NRFX_SUCCESS The procedure is successful.
* @retval NRFX_ERROR_BUSY The driver is not ready for a new transfer.
* @retval NRFX_ERROR_INTERNAL An error is detected by hardware.
* @retval NRFX_ERROR_INVALID_STATE TX transaction is suspended on bus.
* @retval NRFX_ERROR_DRV_TWI_ERR_OVERRUN The unread data is replaced by new data.
* @retval NRFX_ERROR_DRV_TWI_ERR_ANACK Negative acknowledgement (NACK) is received after sending
* the address in polling mode.
* @retval NRFX_ERROR_DRV_TWI_ERR_DNACK Negative acknowledgement (NACK) is received after sending
* a data byte in polling mode.
*/
nrfx_err_t nrfx_twi_rx(nrfx_twi_t const * p_instance,
uint8_t address,
uint8_t * p_data,
size_t length);
/**
* @brief Function for performing a TWI transfer.
*
* The following transfer types can be configured (@ref nrfx_twi_xfer_desc_t::type):
* - @ref NRFX_TWI_XFER_TXRX - Write operation followed by a read operation (without STOP condition in between).
* - @ref NRFX_TWI_XFER_TXTX - Write operation followed by a write operation (without STOP condition in between).
* - @ref NRFX_TWI_XFER_TX - Write operation (with or without STOP condition).
* - @ref NRFX_TWI_XFER_RX - Read operation (with STOP condition).
*
* @note TX-RX and TX-TX transfers are supported only in non-blocking mode.
*
* Additional options are provided using the flags parameter:
* - @ref NRFX_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 NRFX_TWI_FLAG_TX_NO_STOP - No stop condition after TX transfer.
* - @ref NRFX_TWI_FLAG_SUSPEND - Transfer will be suspended. This allows for combining multiple transfers into one transaction.
* Only transactions with the same direction can be combined. To finish the transaction, call the function without this flag.
*
* @note
* Some flag combinations are invalid:
* - @ref NRFX_TWI_FLAG_TX_NO_STOP with @ref nrfx_twi_xfer_desc_t::type different than @ref NRFX_TWI_XFER_TX
*
* @param[in] p_instance Pointer to the driver instance structure.
* @param[in] p_xfer_desc Pointer to the transfer descriptor.
* @param[in] flags Transfer options (0 for default settings).
*
* @retval NRFX_SUCCESS The procedure is successful.
* @retval NRFX_ERROR_BUSY The driver is not ready for a new transfer.
* @retval NRFX_ERROR_NOT_SUPPORTED The provided parameters are not supported.
* @retval NRFX_ERROR_INTERNAL An unexpected transition occurred on the bus.
* @retval NRFX_ERROR_INVALID_STATE Other direction of transaction is suspended on the bus.
* @retval NRFX_ERROR_DRV_TWI_ERR_OVERRUN The unread data is replaced by new data (TXRX and RX)
* @retval NRFX_ERROR_DRV_TWI_ERR_ANACK Negative acknowledgement (NACK) is received after sending
* the address in polling mode.
* @retval NRFX_ERROR_DRV_TWI_ERR_DNACK Negative acknowledgement (NACK) is received after sending
* a data byte in polling mode.
*/
nrfx_err_t nrfx_twi_xfer(nrfx_twi_t const * p_instance,
nrfx_twi_xfer_desc_t const * p_xfer_desc,
uint32_t flags);
/**
* @brief Function for checking the TWI driver state.
*
* @param[in] p_instance TWI instance.
*
* @retval true The TWI driver is currently busy performing a transfer.
* @retval false The TWI driver is ready for a new transfer.
*/
bool nrfx_twi_is_busy(nrfx_twi_t const * p_instance);
/**
* @brief Function for getting the transferred data count.
*
* @param[in] p_instance Pointer to the driver instance structure.
*
* @return Data count.
*/
size_t nrfx_twi_data_count_get(nrfx_twi_t const * const p_instance);
/**
* @brief Function for returning the address of a STOPPED TWI event.
*
* A STOPPED event can be used to detect the end of a transfer if the @ref NRFX_TWI_FLAG_NO_XFER_EVT_HANDLER
* option is used.
*
* @param[in] p_instance Pointer to the driver instance structure.
*
* @return STOPPED event address.
*/
uint32_t nrfx_twi_stopped_event_get(nrfx_twi_t const * p_instance);
/**
* @brief Function for recovering the bus.
*
* This function checks if the bus is not stuck because of a slave holding the SDA line in the low state,
* and if needed it performs required number of pulses on the SCL line to make the slave release the SDA line.
* Finally, the function generates a STOP condition on the bus to put it into a known state.
*
* @note This function can be used only if the TWI driver is uninitialized.
*
* @param[in] scl_pin SCL pin number.
* @param[in] sda_pin SDA pin number.
*
* @retval NRFX_SUCCESS Bus recovery was successful.
* @retval NRFX_ERROR_INTERNAL Bus recovery failed.
*/
__STATIC_INLINE nrfx_err_t nrfx_twi_bus_recover(uint32_t scl_pin, uint32_t sda_pin);
#ifndef SUPPRESS_INLINE_IMPLEMENTATION
__STATIC_INLINE nrfx_err_t nrfx_twi_bus_recover(uint32_t scl_pin, uint32_t sda_pin)
{
return nrfx_twi_twim_bus_recover(scl_pin, sda_pin);
}
#endif
/** @} */
void nrfx_twi_0_irq_handler(void);
void nrfx_twi_1_irq_handler(void);
#ifdef __cplusplus
}
#endif
#endif // NRFX_TWI_H__
@@ -0,0 +1,56 @@
/**
* Copyright (c) 2019 - 2021, 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, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, 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 its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA 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 NRFX_TWI_TWIM_H
#define NRFX_TWI_TWIM_H
#include <nrfx.h>
#ifdef __cplusplus
extern "C" {
#endif
nrfx_err_t nrfx_twi_twim_bus_recover(uint32_t scl_pin, uint32_t sda_pin);
#ifdef __cplusplus
}
#endif
#endif // NRFX_TWI_TWIM_H
+448
View File
@@ -0,0 +1,448 @@
/**
* Copyright (c) 2015 - 2021, 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, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, 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 its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA 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 NRFX_TWIM_H__
#define NRFX_TWIM_H__
#include <nrfx.h>
#include <nrfx_twi_twim.h>
#include <hal/nrf_twim.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @defgroup nrfx_twim TWIM driver
* @{
* @ingroup nrf_twim
* @brief Two Wire Interface Master with EasyDMA (TWIM) peripheral driver.
*/
/** @brief Structure for the TWI master driver instance. */
typedef struct
{
NRF_TWIM_Type * p_twim; ///< Pointer to a structure with TWIM registers.
uint8_t drv_inst_idx; ///< Index of the driver instance. For internal use only.
} nrfx_twim_t;
/** @brief Macro for creating a TWI master driver instance. */
#define NRFX_TWIM_INSTANCE(id) \
{ \
.p_twim = NRFX_CONCAT_2(NRF_TWIM, id), \
.drv_inst_idx = NRFX_CONCAT_3(NRFX_TWIM, id, _INST_IDX), \
}
#ifndef __NRFX_DOXYGEN__
enum {
#if NRFX_CHECK(NRFX_TWIM0_ENABLED)
NRFX_TWIM0_INST_IDX,
#endif
#if NRFX_CHECK(NRFX_TWIM1_ENABLED)
NRFX_TWIM1_INST_IDX,
#endif
#if NRFX_CHECK(NRFX_TWIM2_ENABLED)
NRFX_TWIM2_INST_IDX,
#endif
#if NRFX_CHECK(NRFX_TWIM3_ENABLED)
NRFX_TWIM3_INST_IDX,
#endif
NRFX_TWIM_ENABLED_COUNT
};
#endif
/** @brief Structure for the TWI master driver instance configuration. */
typedef struct
{
uint32_t scl; ///< SCL pin number.
uint32_t sda; ///< SDA pin number.
nrf_twim_frequency_t frequency; ///< TWIM frequency.
uint8_t interrupt_priority; ///< Interrupt priority.
bool hold_bus_uninit; ///< Hold pull up state on GPIO pins after uninit.
} nrfx_twim_config_t;
/** @brief TWI master driver instance default configuration. */
#define NRFX_TWIM_DEFAULT_CONFIG \
{ \
.scl = 31, \
.sda = 31, \
.frequency = (nrf_twim_frequency_t)NRFX_TWIM_DEFAULT_CONFIG_FREQUENCY, \
.interrupt_priority = NRFX_TWIM_DEFAULT_CONFIG_IRQ_PRIORITY, \
.hold_bus_uninit = NRFX_TWIM_DEFAULT_CONFIG_HOLD_BUS_UNINIT, \
}
/** @brief Flag indicating that TX buffer address will be incremented after the transfer. */
#define NRFX_TWIM_FLAG_TX_POSTINC (1UL << 0)
/** @brief Flag indicating that RX buffer address will be incremented after the transfer. */
#define NRFX_TWIM_FLAG_RX_POSTINC (1UL << 1)
/** @brief Flag indicating that the interrupt after each transfer will be suppressed, and the event handler will not be called. */
#define NRFX_TWIM_FLAG_NO_XFER_EVT_HANDLER (1UL << 2)
/** @brief Flag indicating that the transfer will be set up, but not started. */
#define NRFX_TWIM_FLAG_HOLD_XFER (1UL << 3)
/** @brief Flag indicating that the transfer will be executed multiple times. */
#define NRFX_TWIM_FLAG_REPEATED_XFER (1UL << 4)
/** @brief Flag indicating that the TX transfer will not end with a stop condition. */
#define NRFX_TWIM_FLAG_TX_NO_STOP (1UL << 5)
/** @brief Flag indicating that checks for spurious STOP condition will not be performed. */
#define NRFX_TWIM_FLAG_NO_SPURIOUS_STOP_CHECK (1UL << 6)
/** @brief TWI master driver event types. */
typedef enum
{
NRFX_TWIM_EVT_DONE, ///< Transfer completed event.
NRFX_TWIM_EVT_ADDRESS_NACK, ///< Error event: NACK received after sending the address.
NRFX_TWIM_EVT_DATA_NACK, ///< Error event: NACK received after sending a data byte.
NRFX_TWIM_EVT_OVERRUN, ///< Error event: The unread data is replaced by new data.
NRFX_TWIM_EVT_BUS_ERROR ///< Error event: An unexpected transition occurred on the bus.
} nrfx_twim_evt_type_t;
/** @brief TWI master driver transfer types. */
typedef enum
{
NRFX_TWIM_XFER_TX, ///< TX transfer.
NRFX_TWIM_XFER_RX, ///< RX transfer.
NRFX_TWIM_XFER_TXRX, ///< TX transfer followed by RX transfer with repeated start.
NRFX_TWIM_XFER_TXTX ///< TX transfer followed by TX transfer with repeated start.
} nrfx_twim_xfer_type_t;
/** @brief Structure for a TWI transfer descriptor. */
typedef struct
{
nrfx_twim_xfer_type_t type; ///< Type of transfer.
uint8_t address; ///< Slave address.
size_t primary_length; ///< Number of bytes transferred.
size_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.
} nrfx_twim_xfer_desc_t;
/** @brief Macro for setting the TX transfer descriptor. */
#define NRFX_TWIM_XFER_DESC_TX(addr, p_data, length) \
{ \
.type = NRFX_TWIM_XFER_TX, \
.address = (addr), \
.primary_length = (length), \
.secondary_length = 0, \
.p_primary_buf = (p_data), \
.p_secondary_buf = NULL, \
}
/** @brief Macro for setting the RX transfer descriptor. */
#define NRFX_TWIM_XFER_DESC_RX(addr, p_data, length) \
{ \
.type = NRFX_TWIM_XFER_RX, \
.address = (addr), \
.primary_length = (length), \
.secondary_length = 0, \
.p_primary_buf = (p_data), \
.p_secondary_buf = NULL, \
}
/** @brief Macro for setting the TX-RX transfer descriptor. */
#define NRFX_TWIM_XFER_DESC_TXRX(addr, p_tx, tx_len, p_rx, rx_len) \
{ \
.type = NRFX_TWIM_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 TX-TX transfer descriptor. */
#define NRFX_TWIM_XFER_DESC_TXTX(addr, p_tx, tx_len, p_tx2, tx_len2) \
{ \
.type = NRFX_TWIM_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
{
nrfx_twim_evt_type_t type; ///< Event type.
nrfx_twim_xfer_desc_t xfer_desc; ///< Transfer details.
} nrfx_twim_evt_t;
/** @brief TWI event handler prototype. */
typedef void (* nrfx_twim_evt_handler_t)(nrfx_twim_evt_t const * p_event,
void * p_context);
/**
* @brief Function for initializing the TWI driver instance.
*
* @param[in] p_instance Pointer to the driver instance structure.
* @param[in] p_config Pointer to the structure with the initial configuration.
* @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 NRFX_SUCCESS Initialization was successful.
* @retval NRFX_ERROR_INVALID_STATE The driver is in invalid state.
* @retval NRFX_ERROR_BUSY Some other peripheral with the same
* instance ID is already in use. This is
* possible only if @ref nrfx_prs module
* is enabled.
*/
nrfx_err_t nrfx_twim_init(nrfx_twim_t const * p_instance,
nrfx_twim_config_t const * p_config,
nrfx_twim_evt_handler_t event_handler,
void * p_context);
/**
* @brief Function for uninitializing the TWI instance.
*
* @param[in] p_instance Pointer to the driver instance structure.
*/
void nrfx_twim_uninit(nrfx_twim_t const * p_instance);
/**
* @brief Function for enabling the TWI instance.
*
* @param[in] p_instance Pointer to the driver instance structure.
*/
void nrfx_twim_enable(nrfx_twim_t const * p_instance);
/**
* @brief Function for disabling the TWI instance.
*
* @param[in] p_instance Pointer to the driver instance structure.
*/
void nrfx_twim_disable(nrfx_twim_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 NRFX_ERROR_BUSY.
*
* @note This function is deprecated. Use @ref nrfx_twim_xfer instead.
*
* @note Peripherals using EasyDMA (including TWIM) require the transfer buffers
* to be placed in the Data RAM region. If this condition is not met,
* this function fails with the error code NRFX_ERROR_INVALID_ADDR.
*
* @param[in] p_instance Pointer to the driver instance structure.
* @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. Maximum possible length is
* dependent on the used SoC (see the MAXCNT register
* description in the Product Specification). The driver
* checks it with assertion.
* @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 NRFX_SUCCESS The procedure is successful.
* @retval NRFX_ERROR_BUSY The driver is not ready for a new transfer.
* @retval NRFX_ERROR_INTERNAL An unexpected transition occurred on the bus.
* @retval NRFX_ERROR_INVALID_ADDR The provided buffer is not placed in the Data RAM region.
* @retval NRFX_ERROR_DRV_TWI_ERR_ANACK NACK is received after sending the address in polling mode.
* @retval NRFX_ERROR_DRV_TWI_ERR_DNACK NACK is received after sending a data byte in polling mode.
*/
nrfx_err_t nrfx_twim_tx(nrfx_twim_t const * p_instance,
uint8_t address,
uint8_t const * p_data,
size_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 NRFX_ERROR_BUSY.
*
* @note This function is deprecated. Use @ref nrfx_twim_xfer instead.
*
* @param[in] p_instance Pointer to the driver instance structure.
* @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. Maximum possible length
* is dependent on the used SoC (see the MAXCNT register
* description in the Product Specification). The driver
* checks it with assertion.
*
* @retval NRFX_SUCCESS The procedure is successful.
* @retval NRFX_ERROR_BUSY The driver is not ready for a new transfer.
* @retval NRFX_ERROR_INTERNAL An unexpected transition occurred on the bus.
* @retval NRFX_ERROR_DRV_TWI_ERR_OVERRUN The unread data is replaced by new data.
* @retval NRFX_ERROR_DRV_TWI_ERR_ANACK NACK is received after sending the address in polling mode.
* @retval NRFX_ERROR_DRV_TWI_ERR_DNACK NACK is received after sending a data byte in polling mode.
*/
nrfx_err_t nrfx_twim_rx(nrfx_twim_t const * p_instance,
uint8_t address,
uint8_t * p_data,
size_t length);
/**
* @brief Function for performing a TWI transfer.
*
* The following transfer types can be configured (@ref nrfx_twim_xfer_desc_t::type):
* - @ref NRFX_TWIM_XFER_TXRX - Write operation followed by a read operation (without STOP condition in between).
* - @ref NRFX_TWIM_XFER_TXTX - Write operation followed by a write operation (without STOP condition in between).
* - @ref NRFX_TWIM_XFER_TX - Write operation (with or without STOP condition).
* - @ref NRFX_TWIM_XFER_RX - Read operation (with STOP condition).
*
* @note TX-RX and TX-TX transfers are supported only in non-blocking mode.
*
* Additional options are provided using the flags parameter:
* - @ref NRFX_TWIM_FLAG_TX_POSTINC and @ref NRFX_TWIM_FLAG_RX_POSTINC - Post-incrementation of buffer addresses.
* - @ref NRFX_TWIM_FLAG_NO_XFER_EVT_HANDLER - No user event handler after the transfer completion. In most cases, this also means no interrupt at the end of the transfer.
* - @ref NRFX_TWIM_FLAG_HOLD_XFER - Driver is not starting the transfer. Use this flag if the transfer is triggered externally by PPI.
* Use @ref nrfx_twim_start_task_get to get the address of the start task.
* - @ref NRFX_TWIM_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 NRFX_TWIM_FLAG_RX_POSTINC, @ref NRFX_TWIM_FLAG_NO_XFER_EVT_HANDLER, and @ref NRFX_TWIM_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 nrfx_twim_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 NRFX_TWIM_FLAG_REPEATED_XFER is used,
* the driver does not set the driver instance into busy state, so you must ensure that the next transfers are set up
* when TWIM is not active.
* - @ref NRFX_TWIM_FLAG_TX_NO_STOP - No stop condition after the TX transfer.
* - @ref NRFX_TWIM_FLAG_NO_SPURIOUS_STOP_CHECK - Checks for spurious STOP conditions are disabled.
* Used together with @ref NRFX_TWIM_FLAG_NO_XFER_EVT_HANDLER can result in lower power consumption
* when transfers are triggered externally and CPU is sleeping.
* Use only with I2C standard-compliant slave devices.
*
* @note
* Some flag combinations are invalid:
* - @ref NRFX_TWIM_FLAG_TX_NO_STOP with @ref nrfx_twim_xfer_desc_t::type different than @ref NRFX_TWIM_XFER_TX
* - @ref NRFX_TWIM_FLAG_REPEATED_XFER with @ref nrfx_twim_xfer_desc_t::type set to @ref NRFX_TWIM_XFER_TXTX
*
* If @ref nrfx_twim_xfer_desc_t::type is set to @ref NRFX_TWIM_XFER_TX and the @ref NRFX_TWIM_FLAG_TX_NO_STOP and @ref NRFX_TWIM_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 Peripherals using EasyDMA (including TWIM) require the transfer buffers
* to be placed in the Data RAM region. If this condition is not met,
* this function will fail with the error code NRFX_ERROR_INVALID_ADDR.
*
* @param[in] p_instance Pointer to the driver instance structure.
* @param[in] p_xfer_desc Pointer to the transfer descriptor.
* @param[in] flags Transfer options (0 for default settings).
*
* @retval NRFX_SUCCESS The procedure is successful.
* @retval NRFX_ERROR_BUSY The driver is not ready for a new transfer.
* @retval NRFX_ERROR_NOT_SUPPORTED The provided parameters are not supported.
* @retval NRFX_ERROR_INTERNAL An unexpected transition occurred on the bus.
* @retval NRFX_ERROR_INVALID_ADDR The provided buffers are not placed in the Data RAM region.
* @retval NRFX_ERROR_DRV_TWI_ERR_OVERRUN The unread data is replaced by new data.
* @retval NRFX_ERROR_DRV_TWI_ERR_ANACK NACK is received after sending the address.
* @retval NRFX_ERROR_DRV_TWI_ERR_DNACK NACK is received after sending a data byte.
*/
nrfx_err_t nrfx_twim_xfer(nrfx_twim_t const * p_instance,
nrfx_twim_xfer_desc_t const * p_xfer_desc,
uint32_t flags);
/**
* @brief Function for checking the TWI driver state.
*
* @param[in] p_instance TWI instance.
*
* @retval true The TWI driver is currently busy performing a transfer.
* @retval false The TWI driver is ready for a new transfer.
*/
bool nrfx_twim_is_busy(nrfx_twim_t const * p_instance);
/**
* @brief Function for returning the address of a TWIM start task.
*
* This function is to be used if @ref nrfx_twim_xfer was called with the flag @ref NRFX_TWIM_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 Pointer to the driver instance structure.
* @param[in] xfer_type Transfer type used in the last call of the @ref nrfx_twim_xfer function.
*
* @return Start task address (TX or RX) depending on the value of xfer_type.
*/
uint32_t nrfx_twim_start_task_get(nrfx_twim_t const * p_instance, nrfx_twim_xfer_type_t xfer_type);
/**
* @brief Function for returning the address of a STOPPED TWIM event.
*
* A STOPPED event can be used to detect the end of a transfer if the @ref NRFX_TWIM_FLAG_NO_XFER_EVT_HANDLER
* option is used.
*
* @param[in] p_instance Pointer to the driver instance structure.
*
* @return STOPPED event address.
*/
uint32_t nrfx_twim_stopped_event_get(nrfx_twim_t const * p_instance);
/**
* @brief Function for recovering the bus.
*
* This function checks if the bus is not stuck because of a slave holding the SDA line in the low state,
* and if needed it performs required number of pulses on the SCL line to make the slave release the SDA line.
* Finally, the function generates a STOP condition on the bus to put it into a known state.
*
* @note This function can be used only if the TWIM driver is uninitialized.
*
* @param[in] scl_pin SCL pin number.
* @param[in] sda_pin SDA pin number.
*
* @retval NRFX_SUCCESS Bus recovery was successful.
* @retval NRFX_ERROR_INTERNAL Bus recovery failed.
*/
__STATIC_INLINE nrfx_err_t nrfx_twim_bus_recover(uint32_t scl_pin, uint32_t sda_pin);
#ifndef SUPPRESS_INLINE_IMPLEMENTATION
__STATIC_INLINE nrfx_err_t nrfx_twim_bus_recover(uint32_t scl_pin, uint32_t sda_pin)
{
return nrfx_twi_twim_bus_recover(scl_pin, sda_pin);
}
#endif
/** @} */
void nrfx_twim_0_irq_handler(void);
void nrfx_twim_1_irq_handler(void);
void nrfx_twim_2_irq_handler(void);
void nrfx_twim_3_irq_handler(void);
#ifdef __cplusplus
}
#endif
#endif // NRFX_TWIM_H__
+360
View File
@@ -0,0 +1,360 @@
/**
* Copyright (c) 2015 - 2021, 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, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, 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 its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA 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 NRFX_UART_H__
#define NRFX_UART_H__
#include <nrfx.h>
#include <hal/nrf_uart.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @defgroup nrfx_uart UART driver
* @{
* @ingroup nrf_uart
* @brief UART peripheral driver.
*/
/** @brief Data structure of the UART driver instance. */
typedef struct
{
NRF_UART_Type * p_reg; ///< Pointer to a structure with UART registers.
uint8_t drv_inst_idx; ///< Index of the driver instance. For internal use only.
} nrfx_uart_t;
#ifndef __NRFX_DOXYGEN__
enum {
#if NRFX_CHECK(NRFX_UART0_ENABLED)
NRFX_UART0_INST_IDX,
#endif
NRFX_UART_ENABLED_COUNT
};
#endif
/** @brief Macro for creating a UART driver instance. */
#define NRFX_UART_INSTANCE(id) \
{ \
.p_reg = NRFX_CONCAT_2(NRF_UART, id), \
.drv_inst_idx = NRFX_CONCAT_3(NRFX_UART, id, _INST_IDX), \
}
/** @brief Types of UART driver events. */
typedef enum
{
NRFX_UART_EVT_TX_DONE, ///< Requested TX transfer completed.
NRFX_UART_EVT_RX_DONE, ///< Requested RX transfer completed.
NRFX_UART_EVT_ERROR, ///< Error reported by UART peripheral.
} nrfx_uart_evt_type_t;
/** @brief Structure for the 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; ///< Baud rate.
uint8_t interrupt_priority; ///< Interrupt priority.
} nrfx_uart_config_t;
/** @brief UART default configuration. */
#define NRFX_UART_DEFAULT_CONFIG \
{ \
.pseltxd = NRF_UART_PSEL_DISCONNECTED, \
.pselrxd = NRF_UART_PSEL_DISCONNECTED, \
.pselcts = NRF_UART_PSEL_DISCONNECTED, \
.pselrts = NRF_UART_PSEL_DISCONNECTED, \
.p_context = NULL, \
.hwfc = (nrf_uart_hwfc_t)NRFX_UART_DEFAULT_CONFIG_HWFC, \
.parity = (nrf_uart_parity_t)NRFX_UART_DEFAULT_CONFIG_PARITY, \
.baudrate = (nrf_uart_baudrate_t)NRFX_UART_DEFAULT_CONFIG_BAUDRATE, \
.interrupt_priority = NRFX_UART_DEFAULT_CONFIG_IRQ_PRIORITY, \
}
/** @brief Structure for the UART transfer completion event. */
typedef struct
{
uint8_t * p_data; ///< Pointer to memory used for transfer.
uint32_t bytes; ///< Number of bytes transfered.
} nrfx_uart_xfer_evt_t;
/** @brief Structure for the UART error event. */
typedef struct
{
nrfx_uart_xfer_evt_t rxtx; ///< Transfer details, including number of bytes transferred.
uint32_t error_mask; ///< Mask of error flags that generated the event.
} nrfx_uart_error_evt_t;
/** @brief Structure for the UART event. */
typedef struct
{
nrfx_uart_evt_type_t type; ///< Event type.
union
{
nrfx_uart_xfer_evt_t rxtx; ///< Data provided for transfer completion events.
nrfx_uart_error_evt_t error; ///< Data provided for error event.
} data; ///< Union to store event data.
} nrfx_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 the interrupt handler, set on initialization.
*/
typedef void (*nrfx_uart_event_handler_t)(nrfx_uart_event_t const * 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_instance Pointer to the driver instance structure.
* @param[in] p_config Pointer to the structure with the initial configuration.
* @param[in] event_handler Event handler provided by the user. If not provided, the driver works in
* blocking mode.
*
* @retval NRFX_SUCCESS Initialization is successful.
* @retval NRFX_ERROR_INVALID_STATE The driver is already initialized.
* @retval NRFX_ERROR_BUSY Some other peripheral with the same
* instance ID is already in use. This is
* possible only if @ref nrfx_prs module
* is enabled.
*/
nrfx_err_t nrfx_uart_init(nrfx_uart_t const * p_instance,
nrfx_uart_config_t const * p_config,
nrfx_uart_event_handler_t event_handler);
/**
* @brief Function for uninitializing the UART driver.
*
* @param[in] p_instance Pointer to the driver instance structure.
*/
void nrfx_uart_uninit(nrfx_uart_t const * p_instance);
/**
* @brief Function for getting the address of the specified UART task.
*
* @param[in] p_instance Pointer to the driver instance structure.
* @param[in] task Task.
*
* @return Task address.
*/
__STATIC_INLINE uint32_t nrfx_uart_task_address_get(nrfx_uart_t const * p_instance,
nrf_uart_task_t task);
/**
* @brief Function for getting the address of the specified UART event.
*
* @param[in] p_instance Pointer to the driver instance structure.
* @param[in] event Event.
*
* @return Event address.
*/
__STATIC_INLINE uint32_t nrfx_uart_event_address_get(nrfx_uart_t const * p_instance,
nrf_uart_event_t event);
/**
* @brief Function for sending data over UART.
*
* If an event handler was provided in nrfx_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, that is this function
* returns when the transfer is finished. Blocking mode is not using interrupt
* so there is no context switching inside the function.
*
* @param[in] p_instance Pointer to the driver instance structure.
* @param[in] p_data Pointer to data.
* @param[in] length Number of bytes to send.
*
* @retval NRFX_SUCCESS Initialization was successful.
* @retval NRFX_ERROR_BUSY Driver is already transferring.
* @retval NRFX_ERROR_FORBIDDEN The transfer was aborted from a different context
* (blocking mode only).
*/
nrfx_err_t nrfx_uart_tx(nrfx_uart_t const * p_instance,
uint8_t const * p_data,
size_t length);
/**
* @brief Function for checking if UART is currently transmitting.
*
* @param[in] p_instance Pointer to the driver instance structure.
*
* @retval true The UART is transmitting.
* @retval false The UART is not transmitting.
*/
bool nrfx_uart_tx_in_progress(nrfx_uart_t const * p_instance);
/**
* @brief Function for aborting any ongoing transmission.
* @note @ref NRFX_UART_EVT_TX_DONE event will be generated in non-blocking mode.
* It will contain number of bytes sent until the abort was called. The event
* handler will be called from the function context.
*
* @param[in] p_instance Pointer to the driver instance structure.
*/
void nrfx_uart_tx_abort(nrfx_uart_t const * p_instance);
/**
* @brief Function for receiving data over UART.
*
* If an event handler is provided in the nrfx_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, that is 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.
*
* If this function is used without a previous call to @ref nrfx_uart_rx_enable, the reception
* will be stopped on error or when the supplied buffer fills up. In both cases,
* RX FIFO gets disabled. This means that, in case of error, the bytes that follow are lost.
* If this nrfx_uart_rx() function is used with the previous call to @ref nrfx_uart_rx_enable,
* the reception is stopped in case of error, but FIFO is still ongoing. The receiver is still
* working, so after handling the error, an immediate repeated call to this nrfx_uart_rx()
* function with fresh data buffer will re-establish reception. To disable the receiver,
* you must call @ref nrfx_uart_rx_disable explicitly.
*
* @param[in] p_instance Pointer to the driver instance structure.
* @param[in] p_data Pointer to data.
* @param[in] length Number of bytes to receive.
*
* @retval NRFX_SUCCESS Reception is complete (in case of blocking mode) or it is
* successfully started (in case of non-blocking mode).
* @retval NRFX_ERROR_BUSY The driver is already receiving
* (and the secondary buffer has already been set
* in non-blocking mode).
* @retval NRFX_ERROR_FORBIDDEN The transfer was aborted from a different context
* (blocking mode only, also see @ref nrfx_uart_rx_disable).
* @retval NRFX_ERROR_INTERNAL The UART peripheral reported an error.
*/
nrfx_err_t nrfx_uart_rx(nrfx_uart_t const * p_instance,
uint8_t * p_data,
size_t length);
/**
* @brief Function for testing the receiver state in blocking mode.
*
* @param[in] p_instance Pointer to the driver instance structure.
*
* @retval true The receiver has at least one byte of data to get.
* @retval false The receiver is empty.
*/
bool nrfx_uart_rx_ready(nrfx_uart_t const * p_instance);
/**
* @brief Function for enabling the receiver.
*
* UART has a 6-byte-long RX FIFO and it is used to store incoming data. If a user does not call the
* UART receive function before the FIFO is filled, an overrun error will appear. The receiver must be
* explicitly closed by the user @sa nrfx_uart_rx_disable.
*
* @param[in] p_instance Pointer to the driver instance structure.
*/
void nrfx_uart_rx_enable(nrfx_uart_t const * p_instance);
/**
* @brief Function for disabling the receiver.
*
* This function must be called to close the receiver after it has been explicitly enabled by
* @sa nrfx_uart_rx_enable.
*
* @param[in] p_instance Pointer to the driver instance structure.
*/
void nrfx_uart_rx_disable(nrfx_uart_t const * p_instance);
/**
* @brief Function for aborting any ongoing reception.
* @note @ref NRFX_UART_EVT_TX_DONE event will be generated in non-blocking mode.
* It will contain number of bytes received until the abort was called. The event
* handler will be called from the UART interrupt context.
*
* @param[in] p_instance Pointer to the driver instance structure.
*/
void nrfx_uart_rx_abort(nrfx_uart_t const * p_instance);
/**
* @brief Function for reading error source mask. Mask contains values from @ref nrf_uart_error_mask_t.
* @note Function must be used in blocking mode only. In case of non-blocking mode, an error event is
* generated. Function clears error sources after reading.
*
* @param[in] p_instance Pointer to the driver instance structure.
*
* @return Mask of reported errors.
*/
uint32_t nrfx_uart_errorsrc_get(nrfx_uart_t const * p_instance);
#ifndef SUPPRESS_INLINE_IMPLEMENTATION
__STATIC_INLINE uint32_t nrfx_uart_task_address_get(nrfx_uart_t const * p_instance,
nrf_uart_task_t task)
{
return nrf_uart_task_address_get(p_instance->p_reg, task);
}
__STATIC_INLINE uint32_t nrfx_uart_event_address_get(nrfx_uart_t const * p_instance,
nrf_uart_event_t event)
{
return nrf_uart_event_address_get(p_instance->p_reg, event);
}
#endif // SUPPRESS_INLINE_IMPLEMENTATION
/** @} */
void nrfx_uart_0_irq_handler(void);
#ifdef __cplusplus
}
#endif
#endif // NRFX_UART_H__
+359
View File
@@ -0,0 +1,359 @@
/**
* Copyright (c) 2015 - 2021, 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, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, 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 its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA 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 NRFX_UARTE_H__
#define NRFX_UARTE_H__
#include <nrfx.h>
#include <hal/nrf_uarte.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @defgroup nrfx_uarte UARTE driver
* @{
* @ingroup nrf_uarte
* @brief UARTE peripheral driver.
*/
/** @brief Structure for the UARTE driver instance. */
typedef struct
{
NRF_UARTE_Type * p_reg; ///< Pointer to a structure with UARTE registers.
uint8_t drv_inst_idx; ///< Index of the driver instance. For internal use only.
} nrfx_uarte_t;
#ifndef __NRFX_DOXYGEN__
enum {
#if NRFX_CHECK(NRFX_UARTE0_ENABLED)
NRFX_UARTE0_INST_IDX,
#endif
#if NRFX_CHECK(NRFX_UARTE1_ENABLED)
NRFX_UARTE1_INST_IDX,
#endif
#if NRFX_CHECK(NRFX_UARTE2_ENABLED)
NRFX_UARTE2_INST_IDX,
#endif
#if NRFX_CHECK(NRFX_UARTE3_ENABLED)
NRFX_UARTE3_INST_IDX,
#endif
NRFX_UARTE_ENABLED_COUNT
};
#endif
/** @brief Macro for creating a UARTE driver instance. */
#define NRFX_UARTE_INSTANCE(id) \
{ \
.p_reg = NRFX_CONCAT_2(NRF_UARTE, id), \
.drv_inst_idx = NRFX_CONCAT_3(NRFX_UARTE, id, _INST_IDX), \
}
/** @brief Types of UARTE driver events. */
typedef enum
{
NRFX_UARTE_EVT_TX_DONE, ///< Requested TX transfer completed.
NRFX_UARTE_EVT_RX_DONE, ///< Requested RX transfer completed.
NRFX_UARTE_EVT_ERROR, ///< Error reported by UART peripheral.
} nrfx_uarte_evt_type_t;
/** @brief Structure for the UARTE 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_uarte_hwfc_t hwfc; ///< Flow control configuration.
nrf_uarte_parity_t parity; ///< Parity configuration.
nrf_uarte_baudrate_t baudrate; ///< Baud rate.
uint8_t interrupt_priority; ///< Interrupt priority.
} nrfx_uarte_config_t;
/** @brief UARTE default configuration. */
#define NRFX_UARTE_DEFAULT_CONFIG \
{ \
.pseltxd = NRF_UARTE_PSEL_DISCONNECTED, \
.pselrxd = NRF_UARTE_PSEL_DISCONNECTED, \
.pselcts = NRF_UARTE_PSEL_DISCONNECTED, \
.pselrts = NRF_UARTE_PSEL_DISCONNECTED, \
.p_context = NULL, \
.hwfc = (nrf_uarte_hwfc_t)NRFX_UARTE_DEFAULT_CONFIG_HWFC, \
.parity = (nrf_uarte_parity_t)NRFX_UARTE_DEFAULT_CONFIG_PARITY, \
.baudrate = (nrf_uarte_baudrate_t)NRFX_UARTE_DEFAULT_CONFIG_BAUDRATE, \
.interrupt_priority = NRFX_UARTE_DEFAULT_CONFIG_IRQ_PRIORITY, \
}
/** @brief Structure for the UARTE transfer completion event. */
typedef struct
{
uint8_t * p_data; ///< Pointer to memory used for transfer.
size_t bytes; ///< Number of bytes transfered.
} nrfx_uarte_xfer_evt_t;
/** @brief Structure for UARTE error event. */
typedef struct
{
nrfx_uarte_xfer_evt_t rxtx; ///< Transfer details, including number of bytes transferred.
uint32_t error_mask; ///< Mask of error flags that generated the event.
} nrfx_uarte_error_evt_t;
/** @brief Structure for UARTE event. */
typedef struct
{
nrfx_uarte_evt_type_t type; ///< Event type.
union
{
nrfx_uarte_xfer_evt_t rxtx; ///< Data provided for transfer completion events.
nrfx_uarte_error_evt_t error; ///< Data provided for error event.
} data; ///< Union to store event data.
} nrfx_uarte_event_t;
/**
* @brief UARTE 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 the interrupt handler, set on initialization.
*/
typedef void (*nrfx_uarte_event_handler_t)(nrfx_uarte_event_t const * p_event,
void * p_context);
/**
* @brief Function for initializing the UARTE driver.
*
* This function configures and enables UARTE. After this function GPIO pins are controlled by UARTE.
*
* @param[in] p_instance Pointer to the driver instance structure.
* @param[in] p_config Pointer to the structure with the initial configuration.
* @param[in] event_handler Event handler provided by the user. If not provided driver works in
* blocking mode.
*
* @retval NRFX_SUCCESS Initialization was successful.
* @retval NRFX_ERROR_INVALID_STATE Driver is already initialized.
* @retval NRFX_ERROR_BUSY Some other peripheral with the same
* instance ID is already in use. This is
* possible only if @ref nrfx_prs module
* is enabled.
*/
nrfx_err_t nrfx_uarte_init(nrfx_uarte_t const * p_instance,
nrfx_uarte_config_t const * p_config,
nrfx_uarte_event_handler_t event_handler);
/**
* @brief Function for uninitializing the UARTE driver.
*
* @param[in] p_instance Pointer to the driver instance structure.
*/
void nrfx_uarte_uninit(nrfx_uarte_t const * p_instance);
/**
* @brief Function for getting the address of the specified UARTE task.
*
* @param[in] p_instance Pointer to the driver instance structure.
* @param[in] task Task.
*
* @return Task address.
*/
__STATIC_INLINE uint32_t nrfx_uarte_task_address_get(nrfx_uarte_t const * p_instance,
nrf_uarte_task_t task);
/**
* @brief Function for getting the address of the specified UARTE event.
*
* @param[in] p_instance Pointer to the driver instance structure.
* @param[in] event Event.
*
* @return Event address.
*/
__STATIC_INLINE uint32_t nrfx_uarte_event_address_get(nrfx_uarte_t const * p_instance,
nrf_uarte_event_t event);
/**
* @brief Function for sending data over UARTE.
*
* If an event handler is provided in nrfx_uarte_init() call, this function
* returns immediately and the handler is called when the transfer is done.
* Otherwise, the transfer is performed in blocking mode, that is 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 (including UARTE) require the transfer buffers
* to be placed in the Data RAM region. If this condition is not met,
* this function will fail with the error code NRFX_ERROR_INVALID_ADDR.
*
* @param[in] p_instance Pointer to the driver instance structure.
* @param[in] p_data Pointer to data.
* @param[in] length Number of bytes to send. Maximum possible length is
* dependent on the used SoC (see the MAXCNT register
* description in the Product Specification). The driver
* checks it with assertion.
*
* @retval NRFX_SUCCESS Initialization was successful.
* @retval NRFX_ERROR_BUSY Driver is already transferring.
* @retval NRFX_ERROR_FORBIDDEN The transfer was aborted from a different context
* (blocking mode only).
* @retval NRFX_ERROR_INVALID_ADDR p_data does not point to RAM buffer.
*/
nrfx_err_t nrfx_uarte_tx(nrfx_uarte_t const * p_instance,
uint8_t const * p_data,
size_t length);
/**
* @brief Function for checking if UARTE is currently transmitting.
*
* @param[in] p_instance Pointer to the driver instance structure.
*
* @retval true The UARTE is transmitting.
* @retval false The UARTE is not transmitting.
*/
bool nrfx_uarte_tx_in_progress(nrfx_uarte_t const * p_instance);
/**
* @brief Function for aborting any ongoing transmission.
* @note @ref NRFX_UARTE_EVT_TX_DONE event will be generated in non-blocking mode.
* It will contain number of bytes sent until the abort was called. The event
* handler will be called from the UARTE interrupt context.
*
* @param[in] p_instance Pointer to the driver instance structure.
*/
void nrfx_uarte_tx_abort(nrfx_uarte_t const * p_instance);
/**
* @brief Function for receiving data over UARTE.
*
* If an event handler is provided in the nrfx_uarte_init() call, this function
* returns immediately and the handler is called when the transfer is done.
* Otherwise, the transfer is performed in blocking mode, that is 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 (including UARTE) require the transfer buffers
* to be placed in the Data RAM region. If this condition is not met,
* this function fails with the error code NRFX_ERROR_INVALID_ADDR.
*
* @param[in] p_instance Pointer to the driver instance structure.
* @param[in] p_data Pointer to data.
* @param[in] length Number of bytes to receive. Maximum possible length is
* dependent on the used SoC (see the MAXCNT register
* description in the Product Specification). The driver
* checks it with assertion.
*
* @retval NRFX_SUCCESS Initialization is successful.
* @retval NRFX_ERROR_BUSY The driver is already receiving
* (and the secondary buffer has already been set
* in non-blocking mode).
* @retval NRFX_ERROR_FORBIDDEN The transfer is aborted from a different context
* (blocking mode only).
* @retval NRFX_ERROR_INTERNAL The UARTE peripheral reports an error.
* @retval NRFX_ERROR_INVALID_ADDR p_data does not point to RAM buffer.
*/
nrfx_err_t nrfx_uarte_rx(nrfx_uarte_t const * p_instance,
uint8_t * p_data,
size_t length);
/**
* @brief Function for testing the receiver state in blocking mode.
*
* @param[in] p_instance Pointer to the driver instance structure.
*
* @retval true The receiver has at least one byte of data to get.
* @retval false The receiver is empty.
*/
bool nrfx_uarte_rx_ready(nrfx_uarte_t const * p_instance);
/**
* @brief Function for aborting any ongoing reception.
* @note @ref NRFX_UARTE_EVT_RX_DONE event will be generated in non-blocking mode.
* It will contain number of bytes received until the abort was called. The event
* handler will be called from the UARTE interrupt context.
*
* @param[in] p_instance Pointer to the driver instance structure.
*/
void nrfx_uarte_rx_abort(nrfx_uarte_t const * p_instance);
/**
* @brief Function for reading error source mask. Mask contains values from @ref nrf_uarte_error_mask_t.
* @note Function must be used in the blocking mode only. In case of non-blocking mode, an error event is
* generated. Function clears error sources after reading.
*
* @param[in] p_instance Pointer to the driver instance structure.
*
* @return Mask of reported errors.
*/
uint32_t nrfx_uarte_errorsrc_get(nrfx_uarte_t const * p_instance);
#ifndef SUPPRESS_INLINE_IMPLEMENTATION
__STATIC_INLINE uint32_t nrfx_uarte_task_address_get(nrfx_uarte_t const * p_instance,
nrf_uarte_task_t task)
{
return nrf_uarte_task_address_get(p_instance->p_reg, task);
}
__STATIC_INLINE uint32_t nrfx_uarte_event_address_get(nrfx_uarte_t const * p_instance,
nrf_uarte_event_t event)
{
return nrf_uarte_event_address_get(p_instance->p_reg, event);
}
#endif // SUPPRESS_INLINE_IMPLEMENTATION
/** @} */
void nrfx_uarte_0_irq_handler(void);
void nrfx_uarte_1_irq_handler(void);
void nrfx_uarte_2_irq_handler(void);
void nrfx_uarte_3_irq_handler(void);
#ifdef __cplusplus
}
#endif
#endif // NRFX_UARTE_H__
+168
View File
@@ -0,0 +1,168 @@
/**
* Copyright (c) 2014 - 2021, 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, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, 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 its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA 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 NRFX_WDT_H__
#define NRFX_WDT_H__
#include <nrfx.h>
#include <hal/nrf_wdt.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @defgroup nrfx_wdt WDT driver
* @{
* @ingroup nrf_wdt
* @brief Watchdog Timer (WDT) peripheral driver.
*/
#if !NRFX_CHECK(NRFX_WDT_CONFIG_NO_IRQ) || defined(__NRFX_DOXYGEN__)
/** @brief WDT instance interrupt priority configuration. */
#define NRFX_WDT_IRQ_CONFIG .interrupt_priority = NRFX_WDT_CONFIG_IRQ_PRIORITY
#else
#define NRFX_WDT_IRQ_CONFIG
#endif
/**@brief Struct for WDT initialization. */
typedef struct
{
nrf_wdt_behaviour_t behaviour; /**< WDT behaviour when CPU in sleep/halt mode. */
uint32_t reload_value; /**< WDT reload value in ms. */
#if !NRFX_CHECK(NRFX_WDT_CONFIG_NO_IRQ) || defined(__NRFX_DOXYGEN__)
uint8_t interrupt_priority; /**< WDT interrupt priority */
#endif
} nrfx_wdt_config_t;
/** @brief WDT event handler function type. */
typedef void (*nrfx_wdt_event_handler_t)(void);
/** @brief WDT channel ID type. */
typedef nrf_wdt_rr_register_t nrfx_wdt_channel_id;
/** @brief WDT driver default configuration. */
#define NRFX_WDT_DEAFULT_CONFIG \
{ \
.behaviour = (nrf_wdt_behaviour_t)NRFX_WDT_CONFIG_BEHAVIOUR, \
.reload_value = NRFX_WDT_CONFIG_RELOAD_VALUE, \
NRFX_WDT_IRQ_CONFIG \
}
/**
* @brief This function initializes the watchdog.
*
* @param[in] p_config Pointer to the structure with the initial configuration.
* @param[in] wdt_event_handler Event handler provided by the user. Ignored when
* @ref NRFX_WDT_CONFIG_NO_IRQ option is enabled.
*
* @return NRFX_SUCCESS on success, otherwise an error code.
*/
nrfx_err_t nrfx_wdt_init(nrfx_wdt_config_t const * p_config,
nrfx_wdt_event_handler_t wdt_event_handler);
/**
* @brief Function for allocating a watchdog channel.
*
* @note This function can not be called after nrfx_wdt_start(void).
*
* @param[out] p_channel_id ID of granted channel.
*
* @return NRFX_SUCCESS on success, otherwise an error code.
*/
nrfx_err_t nrfx_wdt_channel_alloc(nrfx_wdt_channel_id * p_channel_id);
/**
* @brief Function for starting the watchdog.
*
* @note After calling this function the watchdog is started, so the user needs to feed all allocated
* watchdog channels to avoid reset. At least one watchdog channel must be allocated.
*/
void nrfx_wdt_enable(void);
/**
* @brief Function for feeding the watchdog.
*
* @details Function feeds all allocated watchdog channels.
*/
void nrfx_wdt_feed(void);
/**
* @brief Function for feeding an invidual watchdog channel.
*
* @param[in] channel_id ID of watchdog channel.
*/
void nrfx_wdt_channel_feed(nrfx_wdt_channel_id channel_id);
/**
* @brief Function for returning a requested task address for the WDT driver module.
*
* @param[in] task One of the peripheral tasks.
*
* @return Task address.
*/
__STATIC_INLINE uint32_t nrfx_wdt_ppi_task_addr(nrf_wdt_task_t task)
{
return nrf_wdt_task_address_get(task);
}
/**
* @brief Function for returning a requested event address for the wdt driver module.
*
* @param[in] event One of the peripheral events.
*
* @return Event address.
*/
__STATIC_INLINE uint32_t nrfx_wdt_ppi_event_addr(nrf_wdt_event_t event)
{
return nrf_wdt_event_address_get(event);
}
/** @} */
void nrfx_wdt_irq_handler(void);
#ifdef __cplusplus
}
#endif
#endif
+342
View File
@@ -0,0 +1,342 @@
/**
* Copyright (c) 2017 - 2021, 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, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, 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 its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA 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 NRFX_COMMON_H__
#define NRFX_COMMON_H__
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <nrf.h>
#include <nrf_peripherals.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @defgroup nrfx_common Common module
* @{
* @ingroup nrfx
* @brief Common module.
*/
/**
* @brief Macro for checking if the specified identifier is defined and it has
* a non-zero value.
*
* Normally, preprocessors treat all undefined identifiers as having the value
* zero. However, some tools, like static code analyzers, can issue a warning
* when such identifier is evaluated. This macro gives the possibility to suppress
* such warnings only in places where this macro is used for evaluation, not in
* the whole analyzed code.
*/
#define NRFX_CHECK(module_enabled) (module_enabled)
/**
* @brief Macro for concatenating two tokens in macro expansion.
*
* @note This macro is expanded in two steps so that tokens given as macros
* themselves are fully expanded before they are merged.
*
* @param[in] p1 First token.
* @param[in] p2 Second token.
*
* @return The two tokens merged into one, unless they cannot together form
* a valid token (in such case, the preprocessor issues a warning and
* does not perform the concatenation).
*
* @sa NRFX_CONCAT_3
*/
#define NRFX_CONCAT_2(p1, p2) NRFX_CONCAT_2_(p1, p2)
/** @brief Internal macro used by @ref NRFX_CONCAT_2 to perform the expansion in two steps. */
#define NRFX_CONCAT_2_(p1, p2) p1 ## p2
/**
* @brief Macro for concatenating three tokens in macro expansion.
*
* @note This macro is expanded in two steps so that tokens given as macros
* themselves are fully expanded before they are merged.
*
* @param[in] p1 First token.
* @param[in] p2 Second token.
* @param[in] p3 Third token.
*
* @return The three tokens merged into one, unless they cannot together form
* a valid token (in such case, the preprocessor issues a warning and
* does not perform the concatenation).
*
* @sa NRFX_CONCAT_2
*/
#define NRFX_CONCAT_3(p1, p2, p3) NRFX_CONCAT_3_(p1, p2, p3)
/** @brief Internal macro used by @ref NRFX_CONCAT_3 to perform the expansion in two steps. */
#define NRFX_CONCAT_3_(p1, p2, p3) p1 ## p2 ## p3
/**
* @brief Macro for performing rounded integer division (as opposed to
* truncating the result).
*
* @param[in] a Numerator.
* @param[in] b Denominator.
*
* @return Rounded (integer) result of dividing @c a by @c b.
*/
#define NRFX_ROUNDED_DIV(a, b) (((a) + ((b) / 2)) / (b))
/**
* @brief Macro for performing integer division, making sure the result is rounded up.
*
* @details A typical use case for this macro is to compute the number of objects
* with size @c b required to hold @c a number of bytes.
*
* @param[in] a Numerator.
* @param[in] b Denominator.
*
* @return Integer result of dividing @c a by @c b, rounded up.
*/
#define NRFX_CEIL_DIV(a, b) ((((a) - 1) / (b)) + 1)
/**
* @brief Macro for getting the number of elements in an array.
*
* @param[in] array Name of the array.
*
* @return Array element count.
*/
#define NRFX_ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0]))
/**
* @brief Macro for getting the offset (in bytes) from the beginning of a structure
* of the specified type to its specified member.
*
* @param[in] type Structure type.
* @param[in] member Structure member whose offset is searched for.
*
* @return Member offset in bytes.
*/
#define NRFX_OFFSETOF(type, member) ((size_t)&(((type *)0)->member))
/**@brief Macro for checking if given lengths of EasyDMA transfers do not exceed
* the limit of the specified peripheral.
*
* @param[in] peripheral Peripheral to check the lengths against.
* @param[in] length1 First length to be checked.
* @param[in] length2 Second length to be checked (pass 0 if not needed).
*
* @retval true The length of buffers does not exceed the limit of the specified peripheral.
* @retval false The length of buffers exceeds the limit of the specified peripheral.
*/
#define NRFX_EASYDMA_LENGTH_VALIDATE(peripheral, length1, length2) \
(((length1) < (1U << NRFX_CONCAT_2(peripheral, _EASYDMA_MAXCNT_SIZE))) && \
((length2) < (1U << NRFX_CONCAT_2(peripheral, _EASYDMA_MAXCNT_SIZE))))
/**
* @brief Macro for waiting until condition is met.
*
* @param[in] condition Condition to meet.
* @param[in] attempts Maximum number of condition checks. Must not be 0.
* @param[in] delay_us Delay between consecutive checks, in microseconds.
* @param[out] result Boolean variable to store the result of the wait process.
* Set to true if the condition is met or false otherwise.
*/
#define NRFX_WAIT_FOR(condition, attempts, delay_us, result) \
do { \
result = false; \
uint32_t remaining_attempts = (attempts); \
do { \
if (condition) \
{ \
result = true; \
break; \
} \
NRFX_DELAY_US(delay_us); \
} while (--remaining_attempts); \
} while(0)
/**
* @brief Macro for getting the ID number of the specified peripheral.
*
* For peripherals in Nordic SoCs, there is a direct relationship between their
* ID numbers and their base addresses. See the chapter "Peripheral interface"
* (section "Peripheral ID") in the Product Specification.
*
* @param[in] base_addr Peripheral base address or pointer.
*
* @return ID number associated with the specified peripheral.
*/
#define NRFX_PERIPHERAL_ID_GET(base_addr) (uint8_t)((uint32_t)(base_addr) >> 12)
/**
* @brief Macro for getting the interrupt number assigned to a specific
* peripheral.
*
* For peripherals in Nordic SoCs, the IRQ number assigned to a peripheral is
* equal to its ID number. See the chapter "Peripheral interface" (sections
* "Peripheral ID" and "Interrupts") in the Product Specification.
*
* @param[in] base_addr Peripheral base address or pointer.
*
* @return Interrupt number associated with the specified peripheral.
*/
#define NRFX_IRQ_NUMBER_GET(base_addr) NRFX_PERIPHERAL_ID_GET(base_addr)
/** @brief IRQ handler type. */
typedef void (* nrfx_irq_handler_t)(void);
/** @brief Driver state. */
typedef enum
{
NRFX_DRV_STATE_UNINITIALIZED, ///< Uninitialized.
NRFX_DRV_STATE_INITIALIZED, ///< Initialized but powered off.
NRFX_DRV_STATE_POWERED_ON, ///< Initialized and powered on.
} nrfx_drv_state_t;
/**
* @brief Function for checking if an object is placed in the Data RAM region.
*
* Several peripherals (the ones using EasyDMA) require the transfer buffers
* to be placed in the Data RAM region. This function can be used to check if
* this condition is met.
*
* @param[in] p_object Pointer to an object whose location is to be checked.
*
* @retval true The pointed object is located in the Data RAM region.
* @retval false The pointed object is not located in the Data RAM region.
*/
__STATIC_INLINE bool nrfx_is_in_ram(void const * p_object);
/**
* @brief Function for checking if an object is aligned to a 32-bit word
*
* Several peripherals (the ones using EasyDMA) require the transfer buffers
* to be aligned to a 32-bit word. This function can be used to check if
* this condition is met.
*
* @param[in] p_object Pointer to an object whose location is to be checked.
*
* @retval true The pointed object is aligned to a 32-bit word.
* @retval false The pointed object is not aligned to a 32-bit word.
*/
__STATIC_INLINE bool nrfx_is_word_aligned(void const * p_object);
/**
* @brief Function for getting the interrupt number for the specified peripheral.
*
* @param[in] p_reg Peripheral base pointer.
*
* @return Interrupt number associated with the pointed peripheral.
*/
__STATIC_INLINE IRQn_Type nrfx_get_irq_number(void const * p_reg);
/**
* @brief Function for converting an INTEN register bit position to the
* corresponding event identifier.
*
* The event identifier is the offset between the event register address and
* the peripheral base address, and is equal (thus, can be directly cast) to
* the corresponding value of the enumerated type from HAL (nrf_*_event_t).
*
* @param[in] bit INTEN register bit position.
*
* @return Event identifier.
*
* @sa nrfx_event_to_bitpos
*/
__STATIC_INLINE uint32_t nrfx_bitpos_to_event(uint32_t bit);
/**
* @brief Function for converting an event identifier to the corresponding
* INTEN register bit position.
*
* The event identifier is the offset between the event register address and
* the peripheral base address, and is equal (thus, can be directly cast) to
* the corresponding value of the enumerated type from HAL (nrf_*_event_t).
*
* @param[in] event Event identifier.
*
* @return INTEN register bit position.
*
* @sa nrfx_bitpos_to_event
*/
__STATIC_INLINE uint32_t nrfx_event_to_bitpos(uint32_t event);
#ifndef SUPPRESS_INLINE_IMPLEMENTATION
__STATIC_INLINE bool nrfx_is_in_ram(void const * p_object)
{
return ((((uint32_t)p_object) & 0xE0000000u) == 0x20000000u);
}
__STATIC_INLINE bool nrfx_is_word_aligned(void const * p_object)
{
return ((((uint32_t)p_object) & 0x3u) == 0u);
}
__STATIC_INLINE IRQn_Type nrfx_get_irq_number(void const * p_reg)
{
return (IRQn_Type)NRFX_IRQ_NUMBER_GET(p_reg);
}
__STATIC_INLINE uint32_t nrfx_bitpos_to_event(uint32_t bit)
{
static const uint32_t event_reg_offset = 0x100u;
return event_reg_offset + (bit * sizeof(uint32_t));
}
__STATIC_INLINE uint32_t nrfx_event_to_bitpos(uint32_t event)
{
static const uint32_t event_reg_offset = 0x100u;
return (event - event_reg_offset) / sizeof(uint32_t);
}
#endif
/** @} */
#ifdef __cplusplus
}
#endif
#endif // NRFX_COMMON_H__
+85
View File
@@ -0,0 +1,85 @@
/**
* Copyright (c) 2017 - 2021, 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, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, 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 its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA 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 NRFX_ERRORS_H__
#define NRFX_ERRORS_H__
#if !NRFX_CHECK(NRFX_CUSTOM_ERROR_CODES)
/**
* @defgroup nrfx_error_codes Global Error Codes
* @{
* @ingroup nrfx
*
* @brief Global error code definitions.
*/
/** @brief Base number of error codes. */
#define NRFX_ERROR_BASE_NUM 0x0BAD0000
/** @brief Base number of driver error codes. */
#define NRFX_ERROR_DRIVERS_BASE_NUM (NRFX_ERROR_BASE_NUM + 0x10000)
/** @brief Enumerated type for error codes. */
typedef enum {
NRFX_SUCCESS = (NRFX_ERROR_BASE_NUM + 0), ///< Operation performed successfully.
NRFX_ERROR_INTERNAL = (NRFX_ERROR_BASE_NUM + 1), ///< Internal error.
NRFX_ERROR_NO_MEM = (NRFX_ERROR_BASE_NUM + 2), ///< No memory for operation.
NRFX_ERROR_NOT_SUPPORTED = (NRFX_ERROR_BASE_NUM + 3), ///< Not supported.
NRFX_ERROR_INVALID_PARAM = (NRFX_ERROR_BASE_NUM + 4), ///< Invalid parameter.
NRFX_ERROR_INVALID_STATE = (NRFX_ERROR_BASE_NUM + 5), ///< Invalid state, operation disallowed in this state.
NRFX_ERROR_INVALID_LENGTH = (NRFX_ERROR_BASE_NUM + 6), ///< Invalid length.
NRFX_ERROR_TIMEOUT = (NRFX_ERROR_BASE_NUM + 7), ///< Operation timed out.
NRFX_ERROR_FORBIDDEN = (NRFX_ERROR_BASE_NUM + 8), ///< Operation is forbidden.
NRFX_ERROR_NULL = (NRFX_ERROR_BASE_NUM + 9), ///< Null pointer.
NRFX_ERROR_INVALID_ADDR = (NRFX_ERROR_BASE_NUM + 10), ///< Bad memory address.
NRFX_ERROR_BUSY = (NRFX_ERROR_BASE_NUM + 11), ///< Busy.
NRFX_ERROR_ALREADY_INITIALIZED = (NRFX_ERROR_BASE_NUM + 12), ///< Module already initialized.
NRFX_ERROR_DRV_TWI_ERR_OVERRUN = (NRFX_ERROR_DRIVERS_BASE_NUM + 0), ///< TWI error: Overrun.
NRFX_ERROR_DRV_TWI_ERR_ANACK = (NRFX_ERROR_DRIVERS_BASE_NUM + 1), ///< TWI error: Address not acknowledged.
NRFX_ERROR_DRV_TWI_ERR_DNACK = (NRFX_ERROR_DRIVERS_BASE_NUM + 2) ///< TWI error: Data not acknowledged.
} nrfx_err_t;
/** @} */
#endif // !NRFX_CHECK(NRFX_CUSTOM_ERROR_CODES)
#endif // NRFX_ERRORS_H__
+395
View File
@@ -0,0 +1,395 @@
/**
* Copyright (c) 2016 - 2021, 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, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, 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 its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA 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.
*
*/
#include <nrfx.h>
#if NRFX_CHECK(NRFX_CLOCK_ENABLED)
#include <nrfx_clock.h>
#define NRFX_LOG_MODULE CLOCK
#include <nrfx_log.h>
#if NRFX_CHECK(NRFX_POWER_ENABLED)
extern bool nrfx_power_irq_enabled;
#endif
#define EVT_TO_STR(event) \
(event == NRF_CLOCK_EVENT_HFCLKSTARTED ? "NRF_CLOCK_EVENT_HFCLKSTARTED" : \
(event == NRF_CLOCK_EVENT_LFCLKSTARTED ? "NRF_CLOCK_EVENT_LFCLKSTARTED" : \
(event == NRF_CLOCK_EVENT_DONE ? "NRF_CLOCK_EVENT_DONE" : \
(event == NRF_CLOCK_EVENT_CTTO ? "NRF_CLOCK_EVENT_CTTO" : \
"UNKNOWN EVENT"))))
#if NRFX_CHECK(NRFX_CLOCK_CONFIG_LF_CAL_ENABLED)
#if (NRF_CLOCK_HAS_CALIBRATION == 0)
#error "Calibration is not available in the SoC that is used."
#endif
#if (NRFX_CLOCK_CONFIG_LF_SRC != CLOCK_LFCLKSRC_SRC_RC)
#error "Calibration can be performed only for the RC Oscillator."
#endif
#endif
#if !defined(USE_WORKAROUND_FOR_ANOMALY_132) && \
(defined(NRF52832_XXAA) || defined(NRF52832_XXAB))
// ANOMALY 132 - LFCLK needs to avoid frame from 66us to 138us after LFCLK stop. This solution
// applies delay of 138us before starting LFCLK.
#define USE_WORKAROUND_FOR_ANOMALY_132 1
// Convert time to cycles (nRF52832 is clocked with 64 MHz, use delay of 138 us).
#define ANOMALY_132_DELAY_CYCLES (64UL * 138)
#endif
#if !defined(USE_WORKAROUND_FOR_ANOMALY_192) && \
(defined(NRF52810_XXAA) || \
defined(NRF52832_XXAA) || defined(NRF52832_XXAB) || \
defined(NRF52840_XXAA))
// Enable workaround for nRF52 anomaly 192 (LFRC oscillator frequency is wrong
// after calibration, exceeding 500 ppm).
#define USE_WORKAROUND_FOR_ANOMALY_192 1
#endif
#if !defined(USE_WORKAROUND_FOR_ANOMALY_201) && \
(defined(NRF52810_XXAA) || \
defined(NRF52832_XXAA) || defined(NRF52832_XXAB) || \
defined(NRF52840_XXAA))
// Enable workaround for nRF52 anomaly 201 (EVENTS_HFCLKSTARTED might be generated twice).
#define USE_WORKAROUND_FOR_ANOMALY_201 1
#endif
#if NRFX_CHECK(NRFX_CLOCK_CONFIG_LF_CAL_ENABLED)
typedef enum
{
CAL_STATE_IDLE,
CAL_STATE_CAL
} nrfx_clock_cal_state_t;
#endif
/**@brief CLOCK control block. */
typedef struct
{
nrfx_clock_event_handler_t event_handler;
bool module_initialized; /*< Indicate the state of module */
#if NRFX_CHECK(USE_WORKAROUND_FOR_ANOMALY_201)
bool hfclk_started; /*< Anomaly 201 workaround. */
#endif
#if NRFX_CHECK(NRFX_CLOCK_CONFIG_LF_CAL_ENABLED)
volatile nrfx_clock_cal_state_t cal_state;
#endif
} nrfx_clock_cb_t;
static nrfx_clock_cb_t m_clock_cb;
/**
* This variable is used to check whether common POWER_CLOCK common interrupt
* should be disabled or not if @ref nrfx_power tries to disable the interrupt.
*/
#if NRFX_CHECK(NRFX_POWER_ENABLED)
bool nrfx_clock_irq_enabled;
#endif
#if NRFX_CHECK(USE_WORKAROUND_FOR_ANOMALY_132)
/**
* @brief Function for applying delay of 138us before starting LFCLK.
*/
static void nrfx_clock_anomaly_132(void)
{
uint32_t cyccnt_inital;
uint32_t core_debug;
uint32_t dwt_ctrl;
// Preserve DEMCR register to do not influence into its configuration. Enable the trace and
// debug blocks. It is required to read and write data to DWT block.
core_debug = CoreDebug->DEMCR;
CoreDebug->DEMCR = core_debug | CoreDebug_DEMCR_TRCENA_Msk;
// Preserve CTRL register in DWT block to do not influence into its configuration. Make sure
// that cycle counter is enabled.
dwt_ctrl = DWT->CTRL;
DWT->CTRL = dwt_ctrl | DWT_CTRL_CYCCNTENA_Msk;
// Store start value of cycle counter.
cyccnt_inital = DWT->CYCCNT;
// Delay required time.
while ((DWT->CYCCNT - cyccnt_inital) < ANOMALY_132_DELAY_CYCLES)
{}
// Restore preserved registers.
DWT->CTRL = dwt_ctrl;
CoreDebug->DEMCR = core_debug;
}
#endif // NRFX_CHECK(USE_WORKAROUND_FOR_ANOMALY_132)
nrfx_err_t nrfx_clock_init(nrfx_clock_event_handler_t event_handler)
{
NRFX_ASSERT(event_handler);
nrfx_err_t err_code = NRFX_SUCCESS;
if (m_clock_cb.module_initialized)
{
err_code = NRFX_ERROR_ALREADY_INITIALIZED;
}
else
{
#if NRFX_CHECK(NRFX_CLOCK_CONFIG_LF_CAL_ENABLED)
m_clock_cb.cal_state = CAL_STATE_IDLE;
#endif
m_clock_cb.event_handler = event_handler;
m_clock_cb.module_initialized = true;
#if NRFX_CHECK(USE_WORKAROUND_FOR_ANOMALY_201)
m_clock_cb.hfclk_started = false;
#endif
}
NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
void nrfx_clock_enable(void)
{
NRFX_ASSERT(m_clock_cb.module_initialized);
nrfx_power_clock_irq_init();
nrf_clock_lf_src_set((nrf_clock_lfclk_t)NRFX_CLOCK_CONFIG_LF_SRC);
#if NRFX_CHECK(NRFX_POWER_ENABLED)
nrfx_clock_irq_enabled = true;
#endif
NRFX_LOG_INFO("Module enabled.");
}
void nrfx_clock_disable(void)
{
NRFX_ASSERT(m_clock_cb.module_initialized);
#if NRFX_CHECK(NRFX_POWER_ENABLED)
NRFX_ASSERT(nrfx_clock_irq_enabled);
if (!nrfx_power_irq_enabled)
#endif
{
NRFX_IRQ_DISABLE(nrfx_get_irq_number(NRF_CLOCK));
}
nrf_clock_int_disable(CLOCK_INTENSET_HFCLKSTARTED_Msk |
CLOCK_INTENSET_LFCLKSTARTED_Msk |
#if NRFX_CHECK(NRFX_CLOCK_CONFIG_LF_CAL_ENABLED)
CLOCK_INTENSET_DONE_Msk |
CLOCK_INTENSET_CTTO_Msk |
#endif
0);
#if NRFX_CHECK(NRFX_POWER_ENABLED)
nrfx_clock_irq_enabled = false;
#endif
NRFX_LOG_INFO("Module disabled.");
}
void nrfx_clock_uninit(void)
{
NRFX_ASSERT(m_clock_cb.module_initialized);
nrfx_clock_lfclk_stop();
nrfx_clock_hfclk_stop();
m_clock_cb.module_initialized = false;
NRFX_LOG_INFO("Uninitialized.");
}
void nrfx_clock_lfclk_start(void)
{
NRFX_ASSERT(m_clock_cb.module_initialized);
nrf_clock_event_clear(NRF_CLOCK_EVENT_LFCLKSTARTED);
nrf_clock_int_enable(NRF_CLOCK_INT_LF_STARTED_MASK);
#if NRFX_CHECK(USE_WORKAROUND_FOR_ANOMALY_132)
nrfx_clock_anomaly_132();
#endif
nrf_clock_task_trigger(NRF_CLOCK_TASK_LFCLKSTART);
}
void nrfx_clock_lfclk_stop(void)
{
NRFX_ASSERT(m_clock_cb.module_initialized);
nrf_clock_int_disable(NRF_CLOCK_INT_LF_STARTED_MASK);
nrf_clock_event_clear(NRF_CLOCK_EVENT_LFCLKSTARTED);
nrf_clock_task_trigger(NRF_CLOCK_TASK_LFCLKSTOP);
while (nrf_clock_lf_is_running())
{}
}
void nrfx_clock_hfclk_start(void)
{
NRFX_ASSERT(m_clock_cb.module_initialized);
nrf_clock_event_clear(NRF_CLOCK_EVENT_HFCLKSTARTED);
nrf_clock_int_enable(NRF_CLOCK_INT_HF_STARTED_MASK);
nrf_clock_task_trigger(NRF_CLOCK_TASK_HFCLKSTART);
}
void nrfx_clock_hfclk_stop(void)
{
NRFX_ASSERT(m_clock_cb.module_initialized);
nrf_clock_int_disable(NRF_CLOCK_INT_HF_STARTED_MASK);
nrf_clock_event_clear(NRF_CLOCK_EVENT_HFCLKSTARTED);
nrf_clock_task_trigger(NRF_CLOCK_TASK_HFCLKSTOP);
while (nrf_clock_hf_is_running(NRF_CLOCK_HFCLK_HIGH_ACCURACY))
{}
#if NRFX_CHECK(USE_WORKAROUND_FOR_ANOMALY_201)
m_clock_cb.hfclk_started = false;
#endif
}
nrfx_err_t nrfx_clock_calibration_start(void)
{
nrfx_err_t err_code = NRFX_SUCCESS;
#if NRFX_CHECK(NRFX_CLOCK_CONFIG_LF_CAL_ENABLED)
if (nrfx_clock_hfclk_is_running() == false)
{
return NRFX_ERROR_INVALID_STATE;
}
if (nrfx_clock_lfclk_is_running() == false)
{
return NRFX_ERROR_INVALID_STATE;
}
if (m_clock_cb.cal_state == CAL_STATE_IDLE)
{
nrf_clock_event_clear(NRF_CLOCK_EVENT_DONE);
nrf_clock_int_enable(NRF_CLOCK_INT_DONE_MASK);
m_clock_cb.cal_state = CAL_STATE_CAL;
#if NRFX_CHECK(USE_WORKAROUND_FOR_ANOMALY_192)
*(volatile uint32_t *)0x40000C34 = 0x00000002;
#endif
nrf_clock_task_trigger(NRF_CLOCK_TASK_CAL);
}
else
{
err_code = NRFX_ERROR_BUSY;
}
#endif // NRFX_CHECK(NRFX_CLOCK_CONFIG_LF_CAL_ENABLED)
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
nrfx_err_t nrfx_clock_is_calibrating(void)
{
#if NRFX_CHECK(NRFX_CLOCK_CONFIG_LF_CAL_ENABLED)
if (m_clock_cb.cal_state == CAL_STATE_CAL)
{
return NRFX_ERROR_BUSY;
}
#endif
return NRFX_SUCCESS;
}
void nrfx_clock_calibration_timer_start(uint8_t interval)
{
#if NRFX_CHECK(NRFX_CLOCK_CONFIG_LF_CAL_ENABLED)
nrf_clock_cal_timer_timeout_set(interval);
nrf_clock_event_clear(NRF_CLOCK_EVENT_CTTO);
nrf_clock_int_enable(NRF_CLOCK_INT_CTTO_MASK);
nrf_clock_task_trigger(NRF_CLOCK_TASK_CTSTART);
#endif
}
void nrfx_clock_calibration_timer_stop(void)
{
#if NRFX_CHECK(NRFX_CLOCK_CONFIG_LF_CAL_ENABLED)
nrf_clock_int_disable(NRF_CLOCK_INT_CTTO_MASK);
nrf_clock_task_trigger(NRF_CLOCK_TASK_CTSTOP);
#endif
}
void nrfx_clock_irq_handler(void)
{
if (nrf_clock_event_check(NRF_CLOCK_EVENT_HFCLKSTARTED))
{
nrf_clock_event_clear(NRF_CLOCK_EVENT_HFCLKSTARTED);
NRFX_LOG_DEBUG("Event: %s.", EVT_TO_STR(NRF_CLOCK_EVENT_HFCLKSTARTED));
nrf_clock_int_disable(NRF_CLOCK_INT_HF_STARTED_MASK);
#if NRFX_CHECK(USE_WORKAROUND_FOR_ANOMALY_201)
if (!m_clock_cb.hfclk_started)
{
m_clock_cb.hfclk_started = true;
m_clock_cb.event_handler(NRFX_CLOCK_EVT_HFCLK_STARTED);
}
#else
m_clock_cb.event_handler(NRFX_CLOCK_EVT_HFCLK_STARTED);
#endif
}
if (nrf_clock_event_check(NRF_CLOCK_EVENT_LFCLKSTARTED))
{
nrf_clock_event_clear(NRF_CLOCK_EVENT_LFCLKSTARTED);
NRFX_LOG_DEBUG("Event: %s.", EVT_TO_STR(NRF_CLOCK_EVENT_LFCLKSTARTED));
nrf_clock_int_disable(NRF_CLOCK_INT_LF_STARTED_MASK);
m_clock_cb.event_handler(NRFX_CLOCK_EVT_LFCLK_STARTED);
}
#if NRFX_CHECK(NRFX_CLOCK_CONFIG_LF_CAL_ENABLED)
if (nrf_clock_event_check(NRF_CLOCK_EVENT_CTTO))
{
nrf_clock_event_clear(NRF_CLOCK_EVENT_CTTO);
NRFX_LOG_DEBUG("Event: %s.", EVT_TO_STR(NRF_CLOCK_EVENT_CTTO));
nrf_clock_int_disable(NRF_CLOCK_INT_CTTO_MASK);
m_clock_cb.event_handler(NRFX_CLOCK_EVT_CTTO);
}
if (nrf_clock_event_check(NRF_CLOCK_EVENT_DONE))
{
#if NRFX_CHECK(USE_WORKAROUND_FOR_ANOMALY_192)
*(volatile uint32_t *)0x40000C34 = 0x00000000;
#endif
nrf_clock_event_clear(NRF_CLOCK_EVENT_DONE);
NRFX_LOG_DEBUG("Event: %s.", EVT_TO_STR(NRF_CLOCK_EVENT_DONE));
nrf_clock_int_disable(NRF_CLOCK_INT_DONE_MASK);
m_clock_cb.cal_state = CAL_STATE_IDLE;
m_clock_cb.event_handler(NRFX_CLOCK_EVT_CAL_DONE);
}
#endif // NRFX_CHECK(NRFX_CLOCK_CONFIG_LF_CAL_ENABLED)
}
#endif // NRFX_CHECK(NRFX_CLOCK_ENABLED)
+937
View File
@@ -0,0 +1,937 @@
/**
* Copyright (c) 2015 - 2021, 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, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, 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 its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA 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.
*
*/
#include <nrfx.h>
#if NRFX_CHECK(NRFX_GPIOTE_ENABLED)
#include <nrfx_gpiote.h>
#include "nrf_bitmask.h"
#include <string.h>
#define NRFX_LOG_MODULE GPIOTE
#include <nrfx_log.h>
#if (GPIO_COUNT == 1)
#define MAX_PIN_NUMBER 32
#elif (GPIO_COUNT == 2)
#define MAX_PIN_NUMBER (32 + P1_PIN_NUM)
#else
#error "Not supported."
#endif
#define FORBIDDEN_HANDLER_ADDRESS ((nrfx_gpiote_evt_handler_t)UINT32_MAX)
#define PIN_NOT_USED (-1)
#define PIN_USED (-2)
#define NO_CHANNELS (-1)
#define POLARITY_FIELD_POS (6)
#define POLARITY_FIELD_MASK (0xC0)
/* Check if every pin can be encoded on provided number of bits. */
NRFX_STATIC_ASSERT(MAX_PIN_NUMBER <= (1 << POLARITY_FIELD_POS));
/**
* @brief Macro for converting 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 converting task-event index of OUT task to an address of a task register.
*
* Macro utilizes the fact that registers are grouped together in ascending order.
*/
#define TE_OUT_IDX_TO_TASK_ADDR(idx) (nrf_gpiote_tasks_t)((uint32_t)NRF_GPIOTE_TASKS_OUT_0 + \
(sizeof(uint32_t) * (idx)))
#if defined(GPIOTE_FEATURE_SET_PRESENT) || defined(__NRFX_DOXYGEN__)
/**
* @brief Macro for converting task-event index of SET task to an address of a task register.
*
* Macro utilizes the fact that registers are grouped together in ascending order.
*/
#define TE_SET_IDX_TO_TASK_ADDR(idx) (nrf_gpiote_tasks_t)((uint32_t)NRF_GPIOTE_TASKS_SET_0 + \
(sizeof(uint32_t) * (idx)))
#endif // defined(GPIOTE_FEATURE_SET_PRESENT) || defined(__NRFX_DOXYGEN__)
#if defined(GPIOTE_FEATURE_CLR_PRESENT) || defined(__NRFX_DOXYGEN__)
/**
* @brief Macro for converting task-event index of CLR task to an address of a task register.
*
* Macro utilizes the fact that registers are grouped together in ascending order.
*/
#define TE_CLR_IDX_TO_TASK_ADDR(idx) (nrf_gpiote_tasks_t)((uint32_t)NRF_GPIOTE_TASKS_CLR_0 + \
(sizeof(uint32_t) * (idx)))
#endif // defined(GPIOTE_FEATURE_CLR_PRESENT) || defined(__NRFX_DOXYGEN__)
/*lint -save -e571*/ /* Suppress "Warning 571: Suspicious cast" */
typedef struct
{
nrfx_gpiote_evt_handler_t handlers[GPIOTE_CH_NUM + NRFX_GPIOTE_CONFIG_NUM_OF_LOW_POWER_EVENTS];
int8_t pin_assignments[MAX_PIN_NUMBER];
int8_t port_handlers_pins[NRFX_GPIOTE_CONFIG_NUM_OF_LOW_POWER_EVENTS];
uint8_t configured_pins[((MAX_PIN_NUMBER)+7) / 8];
nrfx_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] < GPIOTE_CH_NUM) ?
true : false;
}
__STATIC_INLINE bool pin_in_use_by_port(uint32_t pin)
{
return (m_cb.pin_assignments[pin] >= GPIOTE_CH_NUM);
}
__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,
nrfx_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 - GPIOTE_CH_NUM] = (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 void pin_configured_set(uint32_t pin)
{
nrf_bitmask_bit_set(pin, m_cb.configured_pins);
}
__STATIC_INLINE void pin_configured_clear(uint32_t pin)
{
nrf_bitmask_bit_clear(pin, m_cb.configured_pins);
}
__STATIC_INLINE bool pin_configured_check(uint32_t pin)
{
return 0 != nrf_bitmask_bit_is_set(pin, m_cb.configured_pins);
}
__STATIC_INLINE int8_t channel_port_get(uint32_t pin)
{
return m_cb.pin_assignments[pin];
}
__STATIC_INLINE nrfx_gpiote_evt_handler_t channel_handler_get(uint32_t channel)
{
return m_cb.handlers[channel];
}
static nrfx_gpiote_pin_t port_handler_pin_get(uint32_t handler_idx)
{
uint8_t pin_and_polarity = (uint8_t)m_cb.port_handlers_pins[handler_idx];
return (nrfx_gpiote_pin_t)(pin_and_polarity & ~POLARITY_FIELD_MASK);
}
static nrf_gpiote_polarity_t port_handler_polarity_get(uint32_t handler_idx)
{
uint8_t pin_and_polarity = (uint8_t)m_cb.port_handlers_pins[handler_idx];
return (nrf_gpiote_polarity_t)((pin_and_polarity & POLARITY_FIELD_MASK) >> POLARITY_FIELD_POS);
}
static int8_t channel_port_alloc(uint32_t pin, nrfx_gpiote_evt_handler_t handler, bool channel)
{
int8_t channel_id = NO_CHANNELS;
uint32_t i;
uint32_t start_idx = channel ? 0 : GPIOTE_CH_NUM;
uint32_t end_idx =
channel ? GPIOTE_CH_NUM : (GPIOTE_CH_NUM + NRFX_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 >= GPIOTE_CH_NUM)
{
m_cb.port_handlers_pins[channel_id - GPIOTE_CH_NUM] = (int8_t)PIN_NOT_USED;
}
}
nrfx_err_t nrfx_gpiote_init(void)
{
nrfx_err_t err_code;
if (m_cb.state != NRFX_DRV_STATE_UNINITIALIZED)
{
err_code = NRFX_ERROR_INVALID_STATE;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
uint8_t i;
for (i = 0; i < MAX_PIN_NUMBER; i++)
{
if (nrf_gpio_pin_present_check(i))
{
pin_in_use_clear(i);
}
}
for (i = 0; i < (GPIOTE_CH_NUM + NRFX_GPIOTE_CONFIG_NUM_OF_LOW_POWER_EVENTS); i++)
{
channel_free(i);
}
memset(m_cb.configured_pins, 0, sizeof(m_cb.configured_pins));
NRFX_IRQ_PRIORITY_SET(nrfx_get_irq_number(NRF_GPIOTE), NRFX_GPIOTE_CONFIG_IRQ_PRIORITY);
NRFX_IRQ_ENABLE(nrfx_get_irq_number(NRF_GPIOTE));
nrf_gpiote_event_clear(NRF_GPIOTE_EVENTS_PORT);
nrf_gpiote_int_enable(GPIOTE_INTENSET_PORT_Msk);
m_cb.state = NRFX_DRV_STATE_INITIALIZED;
err_code = NRFX_SUCCESS;
NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
bool nrfx_gpiote_is_init(void)
{
return (m_cb.state != NRFX_DRV_STATE_UNINITIALIZED) ? true : false;
}
void nrfx_gpiote_uninit(void)
{
NRFX_ASSERT(m_cb.state != NRFX_DRV_STATE_UNINITIALIZED);
uint32_t i;
for (i = 0; i < MAX_PIN_NUMBER; i++)
{
if (nrf_gpio_pin_present_check(i))
{
if (pin_in_use_as_non_task_out(i))
{
nrfx_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.
*/
nrfx_gpiote_in_uninit(i);
}
}
}
m_cb.state = NRFX_DRV_STATE_UNINITIALIZED;
NRFX_LOG_INFO("Uninitialized.");
}
nrfx_err_t nrfx_gpiote_out_init(nrfx_gpiote_pin_t pin,
nrfx_gpiote_out_config_t const * p_config)
{
NRFX_ASSERT(nrf_gpio_pin_present_check(pin));
NRFX_ASSERT(m_cb.state == NRFX_DRV_STATE_INITIALIZED);
NRFX_ASSERT(p_config);
nrfx_err_t err_code = NRFX_SUCCESS;
if (pin_in_use(pin))
{
err_code = NRFX_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((uint32_t)channel,
pin,
p_config->action,
p_config->init_state);
}
else
{
err_code = NRFX_ERROR_NO_MEM;
}
}
else
{
pin_in_use_set(pin);
}
if (err_code == NRFX_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);
pin_configured_set(pin);
}
}
NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
void nrfx_gpiote_out_uninit(nrfx_gpiote_pin_t pin)
{
NRFX_ASSERT(nrf_gpio_pin_present_check(pin));
NRFX_ASSERT(pin_in_use(pin));
if (pin_in_use_by_te(pin))
{
channel_free((uint8_t)channel_port_get(pin));
nrf_gpiote_te_default((uint32_t)channel_port_get(pin));
}
pin_in_use_clear(pin);
if (pin_configured_check(pin))
{
nrf_gpio_cfg_default(pin);
pin_configured_clear(pin);
}
}
void nrfx_gpiote_out_set(nrfx_gpiote_pin_t pin)
{
NRFX_ASSERT(nrf_gpio_pin_present_check(pin));
NRFX_ASSERT(pin_in_use(pin));
NRFX_ASSERT(!pin_in_use_by_te(pin));
nrf_gpio_pin_set(pin);
}
void nrfx_gpiote_out_clear(nrfx_gpiote_pin_t pin)
{
NRFX_ASSERT(nrf_gpio_pin_present_check(pin));
NRFX_ASSERT(pin_in_use(pin));
NRFX_ASSERT(!pin_in_use_by_te(pin));
nrf_gpio_pin_clear(pin);
}
void nrfx_gpiote_out_toggle(nrfx_gpiote_pin_t pin)
{
NRFX_ASSERT(nrf_gpio_pin_present_check(pin));
NRFX_ASSERT(pin_in_use(pin));
NRFX_ASSERT(!pin_in_use_by_te(pin));
nrf_gpio_pin_toggle(pin);
}
void nrfx_gpiote_out_task_enable(nrfx_gpiote_pin_t pin)
{
NRFX_ASSERT(nrf_gpio_pin_present_check(pin));
NRFX_ASSERT(pin_in_use(pin));
NRFX_ASSERT(pin_in_use_by_te(pin));
nrf_gpiote_task_enable((uint32_t)m_cb.pin_assignments[pin]);
}
void nrfx_gpiote_out_task_disable(nrfx_gpiote_pin_t pin)
{
NRFX_ASSERT(nrf_gpio_pin_present_check(pin));
NRFX_ASSERT(pin_in_use(pin));
NRFX_ASSERT(pin_in_use_by_te(pin));
nrf_gpiote_task_disable((uint32_t)m_cb.pin_assignments[pin]);
}
nrf_gpiote_tasks_t nrfx_gpiote_out_task_get(nrfx_gpiote_pin_t pin)
{
NRFX_ASSERT(nrf_gpio_pin_present_check(pin));
NRFX_ASSERT(pin_in_use_by_te(pin));
return TE_OUT_IDX_TO_TASK_ADDR((uint32_t)channel_port_get(pin));
}
uint32_t nrfx_gpiote_out_task_addr_get(nrfx_gpiote_pin_t pin)
{
nrf_gpiote_tasks_t task = nrfx_gpiote_out_task_get(pin);
return nrf_gpiote_task_addr_get(task);
}
#if defined(GPIOTE_FEATURE_SET_PRESENT)
nrf_gpiote_tasks_t nrfx_gpiote_set_task_get(nrfx_gpiote_pin_t pin)
{
NRFX_ASSERT(nrf_gpio_pin_present_check(pin));
NRFX_ASSERT(pin_in_use_by_te(pin));
return TE_SET_IDX_TO_TASK_ADDR((uint32_t)channel_port_get(pin));
}
uint32_t nrfx_gpiote_set_task_addr_get(nrfx_gpiote_pin_t pin)
{
nrf_gpiote_tasks_t task = nrfx_gpiote_set_task_get(pin);
return nrf_gpiote_task_addr_get(task);
}
#endif // defined(GPIOTE_FEATURE_SET_PRESENT)
#if defined(GPIOTE_FEATURE_CLR_PRESENT)
nrf_gpiote_tasks_t nrfx_gpiote_clr_task_get(nrfx_gpiote_pin_t pin)
{
NRFX_ASSERT(nrf_gpio_pin_present_check(pin));
NRFX_ASSERT(pin_in_use_by_te(pin));
return TE_CLR_IDX_TO_TASK_ADDR((uint32_t)channel_port_get(pin));
}
uint32_t nrfx_gpiote_clr_task_addr_get(nrfx_gpiote_pin_t pin)
{
nrf_gpiote_tasks_t task = nrfx_gpiote_clr_task_get(pin);
return nrf_gpiote_task_addr_get(task);
}
#endif // defined(GPIOTE_FEATURE_CLR_PRESENT)
void nrfx_gpiote_out_task_force(nrfx_gpiote_pin_t pin, uint8_t state)
{
NRFX_ASSERT(nrf_gpio_pin_present_check(pin));
NRFX_ASSERT(pin_in_use(pin));
NRFX_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((uint32_t)m_cb.pin_assignments[pin], init_val);
}
void nrfx_gpiote_out_task_trigger(nrfx_gpiote_pin_t pin)
{
NRFX_ASSERT(nrf_gpio_pin_present_check(pin));
NRFX_ASSERT(pin_in_use(pin));
NRFX_ASSERT(pin_in_use_by_te(pin));
nrf_gpiote_tasks_t task = TE_OUT_IDX_TO_TASK_ADDR((uint32_t)channel_port_get(pin));
nrf_gpiote_task_set(task);
}
#if defined(GPIOTE_FEATURE_SET_PRESENT)
void nrfx_gpiote_set_task_trigger(nrfx_gpiote_pin_t pin)
{
NRFX_ASSERT(nrf_gpio_pin_present_check(pin));
NRFX_ASSERT(pin_in_use(pin));
NRFX_ASSERT(pin_in_use_by_te(pin));
nrf_gpiote_tasks_t task = TE_SET_IDX_TO_TASK_ADDR((uint32_t)channel_port_get(pin));
nrf_gpiote_task_set(task);
}
#endif // defined(GPIOTE_FEATURE_SET_PRESENT)
#if defined(GPIOTE_FEATURE_CLR_PRESENT)
void nrfx_gpiote_clr_task_trigger(nrfx_gpiote_pin_t pin)
{
NRFX_ASSERT(nrf_gpio_pin_present_check(pin));
NRFX_ASSERT(pin_in_use(pin));
NRFX_ASSERT(pin_in_use_by_te(pin));
nrf_gpiote_tasks_t task = TE_CLR_IDX_TO_TASK_ADDR((uint32_t)channel_port_get(pin));
nrf_gpiote_task_set(task);
}
#endif // defined(GPIOTE_FEATURE_CLR_PRESENT)
nrfx_err_t nrfx_gpiote_in_init(nrfx_gpiote_pin_t pin,
nrfx_gpiote_in_config_t const * p_config,
nrfx_gpiote_evt_handler_t evt_handler)
{
NRFX_ASSERT(nrf_gpio_pin_present_check(pin));
nrfx_err_t err_code = NRFX_SUCCESS;
/* Only one GPIOTE channel can be assigned to one physical pin. */
if (pin_in_use_by_gpiote(pin))
{
err_code = NRFX_ERROR_INVALID_STATE;
}
else
{
int8_t channel = channel_port_alloc(pin, evt_handler, p_config->hi_accuracy);
if (channel != NO_CHANNELS)
{
if (!p_config->skip_gpio_setup)
{
if (p_config->is_watcher)
{
nrf_gpio_cfg_watcher(pin);
}
else
{
nrf_gpio_cfg_input(pin, p_config->pull);
}
pin_configured_set(pin);
}
if (p_config->hi_accuracy)
{
nrf_gpiote_event_configure((uint32_t)channel, pin, p_config->sense);
}
else
{
m_cb.port_handlers_pins[channel - GPIOTE_CH_NUM] |= (p_config->sense) <<
POLARITY_FIELD_POS;
}
}
else
{
err_code = NRFX_ERROR_NO_MEM;
}
}
NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
void nrfx_gpiote_in_event_enable(nrfx_gpiote_pin_t pin, bool int_enable)
{
NRFX_ASSERT(nrf_gpio_pin_present_check(pin));
NRFX_ASSERT(pin_in_use_by_gpiote(pin));
if (pin_in_use_by_port(pin))
{
nrf_gpiote_polarity_t polarity =
port_handler_polarity_get(channel_port_get(pin) - GPIOTE_CH_NUM);
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_pin_read(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((uint32_t)channel);
nrf_gpiote_event_enable((uint32_t)channel);
nrf_gpiote_event_clear(event);
if (int_enable)
{
nrfx_gpiote_evt_handler_t handler = channel_handler_get((uint32_t)channel_port_get(pin));
// Enable the interrupt only if event handler was provided.
if (handler)
{
nrf_gpiote_int_enable(1 << channel);
}
}
}
}
void nrfx_gpiote_in_event_disable(nrfx_gpiote_pin_t pin)
{
NRFX_ASSERT(nrf_gpio_pin_present_check(pin));
NRFX_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((uint32_t)channel);
nrf_gpiote_int_disable(1 << channel);
}
}
void nrfx_gpiote_in_uninit(nrfx_gpiote_pin_t pin)
{
NRFX_ASSERT(nrf_gpio_pin_present_check(pin));
NRFX_ASSERT(pin_in_use_by_gpiote(pin));
nrfx_gpiote_in_event_disable(pin);
if (pin_in_use_by_te(pin))
{
nrf_gpiote_te_default((uint32_t)channel_port_get(pin));
}
if (pin_configured_check(pin))
{
nrf_gpio_cfg_default(pin);
pin_configured_clear(pin);
}
if (pin_in_use_by_gpiote(pin))
{
channel_free((uint8_t)channel_port_get(pin));
}
pin_in_use_clear(pin);
}
bool nrfx_gpiote_in_is_set(nrfx_gpiote_pin_t pin)
{
NRFX_ASSERT(nrf_gpio_pin_present_check(pin));
return nrf_gpio_pin_read(pin) ? true : false;
}
nrf_gpiote_events_t nrfx_gpiote_in_event_get(nrfx_gpiote_pin_t pin)
{
NRFX_ASSERT(nrf_gpio_pin_present_check(pin));
NRFX_ASSERT(pin_in_use_by_port(pin) || pin_in_use_by_te(pin));
if (pin_in_use_by_te(pin))
{
return TE_IDX_TO_EVENT_ADDR((uint32_t)channel_port_get(pin));
}
return NRF_GPIOTE_EVENTS_PORT;
}
uint32_t nrfx_gpiote_in_event_addr_get(nrfx_gpiote_pin_t pin)
{
nrf_gpiote_events_t event = nrfx_gpiote_in_event_get(pin);
return nrf_gpiote_event_addr_get(event);
}
#if defined(NRF_GPIO_LATCH_PRESENT)
static bool latch_pending_read_and_check(uint32_t * latch)
{
nrf_gpio_latches_read_and_clear(0, GPIO_COUNT, latch);
for (uint32_t port_idx = 0; port_idx < GPIO_COUNT; port_idx++)
{
if (latch[port_idx])
{
/* If any of the latch bits is still set, it means another edge has been captured
* before or during the interrupt processing. Therefore event-processing loop
* should be executed again. */
return true;
}
}
return false;
}
static void port_event_handle(uint32_t * latch)
{
do {
for (uint32_t i = 0; i < NRFX_GPIOTE_CONFIG_NUM_OF_LOW_POWER_EVENTS; i++)
{
if (m_cb.port_handlers_pins[i] == PIN_NOT_USED)
{
continue;
}
/* Process pin further only if LATCH bit associated with this pin was set. */
nrfx_gpiote_pin_t pin = port_handler_pin_get(i);
if (nrf_bitmask_bit_is_set(pin, latch))
{
nrf_gpiote_polarity_t polarity = port_handler_polarity_get(i);
nrf_gpio_pin_sense_t sense = nrf_gpio_pin_sense_get(pin);
NRFX_LOG_DEBUG("PORT event for pin: %d, polarity: %d.", pin, polarity);
/* Reconfigure sense to the opposite level, so the internal PINx.DETECT signal
* can be deasserted. Therefore PORT event generated again,
* unless some other PINx.DETECT signal is still active. */
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);
/* Try to clear LATCH bit corresponding to currently processed pin.
* This may not succeed if the pin's state changed during the interrupt processing
* and now it matches the new sense configuration. In such case,
* the pin will be processed again in another iteration of the outer loop. */
nrf_gpio_pin_latch_clear(pin);
/* Invoke user handler only if the sensed pin level
* matches its polarity configuration. */
nrfx_gpiote_evt_handler_t handler =
channel_handler_get((uint32_t)channel_port_get(pin));
if (handler &&
((polarity == NRF_GPIOTE_POLARITY_TOGGLE) ||
(sense == NRF_GPIO_PIN_SENSE_HIGH && polarity == NRF_GPIOTE_POLARITY_LOTOHI) ||
(sense == NRF_GPIO_PIN_SENSE_LOW && polarity == NRF_GPIOTE_POLARITY_HITOLO)))
{
handler(pin, polarity);
}
}
}
} while (latch_pending_read_and_check(latch));
}
#else
static bool input_read_and_check(uint32_t * input, uint32_t * pins_to_check)
{
bool process_inputs_again;
uint32_t new_input[GPIO_COUNT];
nrf_gpio_ports_read(0, GPIO_COUNT, new_input);
process_inputs_again = false;
for (uint32_t port_idx = 0; port_idx < GPIO_COUNT; port_idx++)
{
/* Execute XOR to find out which inputs have changed. */
uint32_t input_diff = input[port_idx] ^ new_input[port_idx];
input[port_idx] = new_input[port_idx];
if (input_diff)
{
/* If any differences among inputs were found, mark those pins
* to be processed again. */
pins_to_check[port_idx] = input_diff;
process_inputs_again = true;
}
else
{
pins_to_check[port_idx] = 0;
}
}
return process_inputs_again;
}
static void port_event_handle(uint32_t * input)
{
uint32_t pins_to_check[GPIO_COUNT];
for (uint32_t port_idx = 0; port_idx < GPIO_COUNT; port_idx++)
{
pins_to_check[port_idx] = 0xFFFFFFFF;
}
do {
for (uint32_t i = 0; i < NRFX_GPIOTE_CONFIG_NUM_OF_LOW_POWER_EVENTS; i++)
{
if (m_cb.port_handlers_pins[i] == PIN_NOT_USED)
{
continue;
}
nrfx_gpiote_pin_t pin = port_handler_pin_get(i);
if (nrf_bitmask_bit_is_set(pin, pins_to_check))
{
nrf_gpiote_polarity_t polarity = port_handler_polarity_get(i);
nrf_gpio_pin_sense_t sense = nrf_gpio_pin_sense_get(pin);
bool pin_state = nrf_bitmask_bit_is_set(pin, input);
/* Process pin further only if its state matches its sense level. */
if ((pin_state && (sense == NRF_GPIO_PIN_SENSE_HIGH)) ||
(!pin_state && (sense == NRF_GPIO_PIN_SENSE_LOW)) )
{
/* Reconfigure sense to the opposite level, so the internal PINx.DETECT signal
* can be deasserted. Therefore PORT event can be generated again,
* unless some other PINx.DETECT signal is still active. */
NRFX_LOG_DEBUG("PORT event for pin: %d, polarity: %d.", pin, polarity);
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);
/* Invoke user handler only if the sensed pin level
* matches its polarity configuration. */
nrfx_gpiote_evt_handler_t handler =
channel_handler_get((uint32_t)channel_port_get(pin));
if (handler &&
((polarity == NRF_GPIOTE_POLARITY_TOGGLE) ||
(sense == NRF_GPIO_PIN_SENSE_HIGH &&
polarity == NRF_GPIOTE_POLARITY_LOTOHI) ||
(sense == NRF_GPIO_PIN_SENSE_LOW &&
polarity == NRF_GPIOTE_POLARITY_HITOLO)))
{
handler(pin, polarity);
}
}
}
}
} while (input_read_and_check(input, pins_to_check));
}
#endif // defined(NRF_GPIO_LATCH_PRESENT)
void nrfx_gpiote_irq_handler(void)
{
uint32_t status = 0;
uint32_t input[GPIO_COUNT] = {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 < GPIOTE_CH_NUM; 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;
#if defined(NRF_GPIO_LATCH_PRESENT)
nrf_gpio_latches_read_and_clear(0, GPIO_COUNT, input);
#else
nrf_gpio_ports_read(0, GPIO_COUNT, input);
#endif
}
/* Process pin events. */
if (status & NRF_GPIOTE_INT_IN_MASK)
{
mask = (uint32_t)NRF_GPIOTE_INT_IN0_MASK;
for (i = 0; i < GPIOTE_CH_NUM; i++)
{
if (mask & status)
{
nrfx_gpiote_pin_t pin = nrf_gpiote_event_pin_get(i);
NRFX_LOG_DEBUG("Event in number: %d.", i);
nrf_gpiote_polarity_t polarity = nrf_gpiote_event_polarity_get(i);
nrfx_gpiote_evt_handler_t handler = channel_handler_get(i);
NRFX_LOG_DEBUG("Pin: %d, polarity: %d.", pin, polarity);
if (handler)
{
handler(pin, polarity);
}
}
mask <<= 1;
}
}
/* Process PORT event. */
if (status & (uint32_t)NRF_GPIOTE_INT_PORT_MASK)
{
port_event_handle(input);
}
}
/*lint -restore*/
#endif // NRFX_CHECK(NRFX_GPIOTE_ENABLED)
+435
View File
@@ -0,0 +1,435 @@
/**
* Copyright (c) 2019 - 2021, 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, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, 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 its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA 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.
*
*/
#include <nrfx.h>
#if NRFX_CHECK(NRFX_NVMC_ENABLED)
#include <nrfx_nvmc.h>
/**
* Value representing the number of bytes in a word.
*
* It is used in loops iterating over bytes contained in a word
* or in word-alignment checks.
*/
#define NVMC_BYTES_IN_WORD 4
/**
* Value representing non-volatile memory (NVM) page count.
*
* This symbol is needed to determine NVM page count for chips that cannot
* always access FICR for this information.
*/
#if defined(NRF9160_XXAA)
#define NVMC_FLASH_PAGE_COUNT 256
#endif
/**
* Value representing non-volatile memory (NVM) page size in bytes.
*
* This symbol is needed to determine NVM page size for chips that cannot
* always access FICR for this information.
*/
#if defined(NRF9160_XXAA)
#define NVMC_FLASH_PAGE_SIZE 0x1000 ///< 4 kB
#endif
#if defined(NRF_NVMC_PARTIAL_ERASE_PRESENT)
/**
* Value representing the page erase time.
*
* This value is used to determine whether the partial erase is still in progress.
*/
#if defined(NRF52810_XXAA) || defined(NRF52811_XXAA) || defined(NRF52840_XXAA)
#define NVMC_PAGE_ERASE_DURATION_MS 85
#elif defined(NRF52820_XXAA) || defined(NRF52833_XXAA) || defined(NRF9160_XXAA)
#define NVMC_PAGE_ERASE_DURATION_MS 87
#else
#error "Page partial erase present but could not determine its total duration for given SoC"
#endif
/**
* Value representing the invalid page partial erase address.
*
* This value is used for representing a NULL pointer for
* partial erase, as that address 0 can be a valid
* memory address in flash.
*/
#define NVMC_PARTIAL_ERASE_INVALID_ADDR 0xFFFFFFFF
/** Internal counter for page partial erase. */
static uint32_t m_partial_erase_time_elapsed;
/** Partial erase page address. */
static uint32_t m_partial_erase_page_addr = NVMC_PARTIAL_ERASE_INVALID_ADDR;
#endif // defined(NRF_NVMC_PARTIAL_ERASE_PRESENT)
static uint32_t flash_page_size_get(void)
{
uint32_t flash_page_size = 0;
#if defined(NRF51) || defined(NRF52_SERIES)
flash_page_size = nrf_ficr_codepagesize_get(NRF_FICR);
#elif defined(NVMC_FLASH_PAGE_SIZE)
flash_page_size = NVMC_FLASH_PAGE_SIZE;
#else
#error "Cannot determine flash page size for a given SoC."
#endif
return flash_page_size;
}
static uint32_t flash_page_count_get(void)
{
uint32_t page_count = 0;
#if defined(NRF51) || defined(NRF52_SERIES)
page_count = nrf_ficr_codesize_get(NRF_FICR);
#elif defined(NVMC_FLASH_PAGE_COUNT)
page_count = NVMC_FLASH_PAGE_COUNT;
#else
#error "Cannot determine flash page count for a given SoC."
#endif
return page_count;
}
static uint32_t flash_total_size_get(void)
{
return flash_page_size_get() * flash_page_count_get();
}
static bool is_page_aligned_check(uint32_t addr)
{
/* If the modulo operation returns '0', then the address is aligned. */
return !(addr % flash_page_size_get());
}
static uint32_t partial_word_create(uint32_t addr, uint8_t const * bytes, uint32_t bytes_count)
{
uint32_t value32;
uint32_t byte_shift;
byte_shift = addr % NVMC_BYTES_IN_WORD;
NRFX_ASSERT(bytes_count <= (NVMC_BYTES_IN_WORD - byte_shift));
value32 = 0xFFFFFFFF;
for (uint32_t i = 0; i < bytes_count; i++)
{
((uint8_t *)&value32)[byte_shift] = bytes[i];
byte_shift++;
}
return value32;
}
static void nvmc_readonly_mode_set(void)
{
#if defined(NRF_TRUSTZONE_NONSECURE)
nrf_nvmc_nonsecure_mode_set(NRF_NVMC, NRF_NVMC_NS_MODE_READONLY);
#else
nrf_nvmc_mode_set(NRF_NVMC, NRF_NVMC_MODE_READONLY);
#endif
}
static void nvmc_write_mode_set(void)
{
#if defined(NRF_TRUSTZONE_NONSECURE)
nrf_nvmc_nonsecure_mode_set(NRF_NVMC, NRF_NVMC_NS_MODE_WRITE);
#else
nrf_nvmc_mode_set(NRF_NVMC, NRF_NVMC_MODE_WRITE);
#endif
}
static void nvmc_erase_mode_set(void)
{
#if defined(NRF_TRUSTZONE_NONSECURE)
nrf_nvmc_nonsecure_mode_set(NRF_NVMC, NRF_NVMC_NS_MODE_ERASE);
#else
nrf_nvmc_mode_set(NRF_NVMC, NRF_NVMC_MODE_ERASE);
#endif
}
static void nvmc_word_write(uint32_t addr, uint32_t value)
{
#if defined(NRF9160_XXAA)
while (!nrf_nvmc_write_ready_check(NRF_NVMC))
{}
#else
while (!nrf_nvmc_ready_check(NRF_NVMC))
{}
#endif
*(volatile uint32_t *)addr = value;
__DMB();
}
static void nvmc_words_write(uint32_t addr, void const * src, uint32_t num_words)
{
for (uint32_t i = 0; i < num_words; i++)
{
nvmc_word_write(addr + (NVMC_BYTES_IN_WORD * i), ((uint32_t const *)src)[i]);
}
}
nrfx_err_t nrfx_nvmc_page_erase(uint32_t addr)
{
NRFX_ASSERT(addr < flash_total_size_get());
if (!is_page_aligned_check(addr))
{
return NRFX_ERROR_INVALID_ADDR;
}
nvmc_erase_mode_set();
nrf_nvmc_page_erase_start(NRF_NVMC, addr);
while (!nrf_nvmc_ready_check(NRF_NVMC))
{}
nvmc_readonly_mode_set();
return NRFX_SUCCESS;
}
nrfx_err_t nrfx_nvmc_uicr_erase(void)
{
#if defined(NVMC_ERASEUICR_ERASEUICR_Msk)
nvmc_erase_mode_set();
nrf_nvmc_uicr_erase_start(NRF_NVMC);
while (!nrf_nvmc_ready_check(NRF_NVMC))
{}
nvmc_readonly_mode_set();
return NRFX_SUCCESS;
#else
return NRFX_ERROR_NOT_SUPPORTED;
#endif
}
void nrfx_nvmc_all_erase(void)
{
nvmc_erase_mode_set();
nrf_nvmc_erase_all_start(NRF_NVMC);
while (!nrf_nvmc_ready_check(NRF_NVMC))
{}
nvmc_readonly_mode_set();
}
#if defined(NRF_NVMC_PARTIAL_ERASE_PRESENT)
nrfx_err_t nrfx_nvmc_page_partial_erase_init(uint32_t addr, uint32_t duration_ms)
{
NRFX_ASSERT(addr < flash_total_size_get());
if (!is_page_aligned_check(addr))
{
return NRFX_ERROR_INVALID_ADDR;
}
m_partial_erase_time_elapsed = 0;
m_partial_erase_page_addr = addr;
nrf_nvmc_partial_erase_duration_set(NRF_NVMC, duration_ms);
return NRFX_SUCCESS;
}
bool nrfx_nvmc_page_partial_erase_continue(void)
{
NRFX_ASSERT(m_partial_erase_page_addr != NVMC_PARTIAL_ERASE_INVALID_ADDR);
uint32_t duration_ms = nrf_nvmc_partial_erase_duration_get(NRF_NVMC);
#if defined(NVMC_CONFIG_WEN_PEen)
nrf_nvmc_mode_set(NRF_NVMC, NRF_NVMC_MODE_PARTIAL_ERASE);
#else
nrf_nvmc_mode_set(NRF_NVMC, NRF_NVMC_MODE_ERASE);
#endif
nrf_nvmc_page_partial_erase_start(NRF_NVMC, m_partial_erase_page_addr);
while (!nrf_nvmc_ready_check(NRF_NVMC))
{}
nvmc_readonly_mode_set();
m_partial_erase_time_elapsed += duration_ms;
if (m_partial_erase_time_elapsed < NVMC_PAGE_ERASE_DURATION_MS)
{
return false;
}
else
{
m_partial_erase_page_addr = NVMC_PARTIAL_ERASE_INVALID_ADDR;
return true;
}
}
#endif // defined(NRF_NVMC_PARTIAL_ERASE_PRESENT)
bool nrfx_nvmc_byte_writable_check(uint32_t addr, uint8_t val_to_check)
{
NRFX_ASSERT((addr < flash_total_size_get()) ||
((addr - (uint32_t)NRF_UICR) < sizeof(NRF_UICR_Type)));
uint8_t val_on_addr = *(uint8_t const *)addr;
return (val_to_check & val_on_addr) == val_to_check;
}
bool nrfx_nvmc_word_writable_check(uint32_t addr, uint32_t val_to_check)
{
NRFX_ASSERT((addr < flash_total_size_get()) ||
((addr - (uint32_t)NRF_UICR) < sizeof(NRF_UICR_Type)));
NRFX_ASSERT(nrfx_is_word_aligned((void const *)addr));
uint32_t val_on_addr = *(uint32_t const *)addr;
return (val_to_check & val_on_addr) == val_to_check;
}
void nrfx_nvmc_byte_write(uint32_t addr, uint8_t value)
{
uint32_t aligned_addr = addr & ~(0x03UL);
nrfx_nvmc_word_write(aligned_addr, partial_word_create(addr, &value, 1));
}
void nrfx_nvmc_word_write(uint32_t addr, uint32_t value)
{
NRFX_ASSERT((addr < flash_total_size_get()) ||
((addr - (uint32_t)NRF_UICR) < sizeof(NRF_UICR_Type)));
NRFX_ASSERT(nrfx_is_word_aligned((void const *)addr));
nvmc_write_mode_set();
nvmc_word_write(addr, value);
nvmc_readonly_mode_set();
}
void nrfx_nvmc_bytes_write(uint32_t addr, void const * src, uint32_t num_bytes)
{
NRFX_ASSERT((addr < flash_total_size_get()) ||
((addr - (uint32_t)NRF_UICR) < sizeof(NRF_UICR_Type)));
nvmc_write_mode_set();
uint8_t const * bytes_src = (uint8_t const *)src;
uint32_t unaligned_bytes = addr % NVMC_BYTES_IN_WORD;
if (unaligned_bytes != 0)
{
uint32_t leading_bytes = NVMC_BYTES_IN_WORD - unaligned_bytes;
if (leading_bytes > num_bytes)
{
leading_bytes = num_bytes;
}
nvmc_word_write(addr - unaligned_bytes,
partial_word_create(addr, bytes_src, leading_bytes));
num_bytes -= leading_bytes;
addr += leading_bytes;
bytes_src += leading_bytes;
}
#if defined(__CORTEX_M) && (__CORTEX_M == 0U)
if (!nrfx_is_word_aligned((void const *)bytes_src))
{
/* Cortex-M0 allows only word-aligned RAM access.
If source address is not word-aligned, bytes are combined
into words explicitly. */
for (uint32_t i = 0; i < num_bytes / NVMC_BYTES_IN_WORD; i++)
{
uint32_t word = (uint32_t)bytes_src[0]
| ((uint32_t)bytes_src[1]) << 8
| ((uint32_t)bytes_src[2]) << 16
| ((uint32_t)bytes_src[3]) << 24;
nvmc_word_write(addr, word);
bytes_src += NVMC_BYTES_IN_WORD;
addr += NVMC_BYTES_IN_WORD;
}
}
else
#endif
{
uint32_t word_count = num_bytes / NVMC_BYTES_IN_WORD;
nvmc_words_write(addr, (uint32_t const *)bytes_src, word_count);
addr += word_count * NVMC_BYTES_IN_WORD;
bytes_src += word_count * NVMC_BYTES_IN_WORD;
}
uint32_t trailing_bytes = num_bytes % NVMC_BYTES_IN_WORD;
if (trailing_bytes != 0)
{
nvmc_word_write(addr, partial_word_create(addr, bytes_src, trailing_bytes));
}
nvmc_readonly_mode_set();
}
void nrfx_nvmc_words_write(uint32_t addr, void const * src, uint32_t num_words)
{
NRFX_ASSERT((addr < flash_total_size_get()) ||
((addr - (uint32_t)NRF_UICR) < sizeof(NRF_UICR_Type)));
NRFX_ASSERT(nrfx_is_word_aligned((void const *)addr));
NRFX_ASSERT(nrfx_is_word_aligned(src));
nvmc_write_mode_set();
nvmc_words_write(addr, src, num_words);
nvmc_readonly_mode_set();
}
uint32_t nrfx_nvmc_flash_size_get(void)
{
return flash_total_size_get();
}
uint32_t nrfx_nvmc_flash_page_size_get(void)
{
return flash_page_size_get();
}
uint32_t nrfx_nvmc_flash_page_count_get(void)
{
return flash_page_count_get();
}
#endif // NRFX_CHECK(NRFX_NVMC_ENABLED)
+341
View File
@@ -0,0 +1,341 @@
/**
* Copyright (c) 2017 - 2021, 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, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, 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 its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA 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.
*
*/
#include <nrfx.h>
#if NRFX_CHECK(NRFX_POWER_ENABLED)
#include <nrfx_power.h>
#if defined(REGULATORS_PRESENT)
#include <hal/nrf_regulators.h>
#endif
#if NRFX_CHECK(NRFX_CLOCK_ENABLED)
extern bool nrfx_clock_irq_enabled;
extern void nrfx_clock_irq_handler(void);
#endif
/**
* @internal
* @defgroup nrfx_power_internals POWER driver internals
* @ingroup nrfx_power
*
* Internal variables, auxiliary macros and functions of POWER driver.
* @{
*/
/**
* This variable is used to check whether common POWER_CLOCK common interrupt
* should be disabled or not if @ref nrfx_clock tries to disable the interrupt.
*/
bool nrfx_power_irq_enabled;
/**
* @brief The initialization flag
*/
#define m_initialized nrfx_power_irq_enabled
/**
* @brief The handler of power fail comparator warning event
*/
static nrfx_power_pofwarn_event_handler_t m_pofwarn_handler;
#if NRF_POWER_HAS_SLEEPEVT
/**
* @brief The handler of sleep event handler
*/
static nrfx_power_sleep_event_handler_t m_sleepevt_handler;
#endif
#if NRF_POWER_HAS_USBREG
/**
* @brief The handler of USB power events
*/
static nrfx_power_usb_event_handler_t m_usbevt_handler;
#endif
/** @} */
nrfx_power_pofwarn_event_handler_t nrfx_power_pof_handler_get(void)
{
return m_pofwarn_handler;
}
#if NRF_POWER_HAS_USBREG
nrfx_power_usb_event_handler_t nrfx_power_usb_handler_get(void)
{
return m_usbevt_handler;
}
#endif
nrfx_err_t nrfx_power_init(nrfx_power_config_t const * p_config)
{
NRFX_ASSERT(p_config);
if (m_initialized)
{
return NRFX_ERROR_ALREADY_INITIALIZED;
}
#if NRF_POWER_HAS_DCDCEN_VDDH
nrf_power_dcdcen_vddh_set(p_config->dcdcenhv);
#endif
#if NRF_POWER_HAS_DCDCEN
nrf_power_dcdcen_set(p_config->dcdcen);
#else
nrf_regulators_dcdcen_set(NRF_REGULATORS, p_config->dcdcen);
#endif
nrfx_power_clock_irq_init();
m_initialized = true;
return NRFX_SUCCESS;
}
void nrfx_power_uninit(void)
{
NRFX_ASSERT(m_initialized);
#if NRFX_CHECK(NRFX_CLOCK_ENABLED)
if (!nrfx_clock_irq_enabled)
#endif
{
NRFX_IRQ_DISABLE(nrfx_get_irq_number(NRF_POWER));
}
#if NRF_POWER_HAS_POFCON
nrfx_power_pof_uninit();
#endif
#if NRF_POWER_HAS_SLEEPEVT
nrfx_power_sleepevt_uninit();
#endif
#if NRF_POWER_HAS_USBREG
nrfx_power_usbevt_uninit();
#endif
m_initialized = false;
}
#if NRF_POWER_HAS_POFCON
void nrfx_power_pof_init(nrfx_power_pofwarn_config_t const * p_config)
{
NRFX_ASSERT(p_config != NULL);
nrfx_power_pof_uninit();
if (p_config->handler != NULL)
{
m_pofwarn_handler = p_config->handler;
}
}
void nrfx_power_pof_enable(nrfx_power_pofwarn_config_t const * p_config)
{
nrf_power_pofcon_set(true, p_config->thr);
#if NRF_POWER_HAS_VDDH
nrf_power_pofcon_vddh_set(p_config->thrvddh);
#endif
if (m_pofwarn_handler != NULL)
{
nrf_power_int_enable(NRF_POWER_INT_POFWARN_MASK);
}
}
void nrfx_power_pof_disable(void)
{
nrf_power_pofcon_set(false, NRF_POWER_POFTHR_V27);
nrf_power_int_disable(NRF_POWER_INT_POFWARN_MASK);
}
void nrfx_power_pof_uninit(void)
{
m_pofwarn_handler = NULL;
}
#endif // NRF_POWER_HAS_POFCON
#if NRF_POWER_HAS_SLEEPEVT
void nrfx_power_sleepevt_init(nrfx_power_sleepevt_config_t const * p_config)
{
NRFX_ASSERT(p_config != NULL);
nrfx_power_sleepevt_uninit();
if (p_config->handler != NULL)
{
m_sleepevt_handler = p_config->handler;
}
}
void nrfx_power_sleepevt_enable(nrfx_power_sleepevt_config_t const * p_config)
{
uint32_t enmask = 0;
if (p_config->en_enter)
{
enmask |= NRF_POWER_INT_SLEEPENTER_MASK;
nrf_power_event_clear(NRF_POWER_EVENT_SLEEPENTER);
}
if (p_config->en_exit)
{
enmask |= NRF_POWER_INT_SLEEPEXIT_MASK;
nrf_power_event_clear(NRF_POWER_EVENT_SLEEPEXIT);
}
nrf_power_int_enable(enmask);
}
void nrfx_power_sleepevt_disable(void)
{
nrf_power_int_disable(
NRF_POWER_INT_SLEEPENTER_MASK |
NRF_POWER_INT_SLEEPEXIT_MASK);
}
void nrfx_power_sleepevt_uninit(void)
{
m_sleepevt_handler = NULL;
}
#endif /* NRF_POWER_HAS_SLEEPEVT */
#if NRF_POWER_HAS_USBREG
void nrfx_power_usbevt_init(nrfx_power_usbevt_config_t const * p_config)
{
NRFX_ASSERT(p_config != NULL);
nrfx_power_usbevt_uninit();
if (p_config->handler != NULL)
{
m_usbevt_handler = p_config->handler;
}
}
void nrfx_power_usbevt_enable(void)
{
nrf_power_int_enable(
NRF_POWER_INT_USBDETECTED_MASK |
NRF_POWER_INT_USBREMOVED_MASK |
NRF_POWER_INT_USBPWRRDY_MASK);
}
void nrfx_power_usbevt_disable(void)
{
nrf_power_int_disable(
NRF_POWER_INT_USBDETECTED_MASK |
NRF_POWER_INT_USBREMOVED_MASK |
NRF_POWER_INT_USBPWRRDY_MASK);
}
void nrfx_power_usbevt_uninit(void)
{
m_usbevt_handler = NULL;
}
#endif /* NRF_POWER_HAS_USBREG */
void nrfx_power_irq_handler(void)
{
uint32_t enabled = nrf_power_int_enable_get();
#if NRF_POWER_HAS_POFCON
if ((0 != (enabled & NRF_POWER_INT_POFWARN_MASK)) &&
nrf_power_event_get_and_clear(NRF_POWER_EVENT_POFWARN))
{
/* Cannot be null if event is enabled */
NRFX_ASSERT(m_pofwarn_handler != NULL);
m_pofwarn_handler();
}
#endif
#if NRF_POWER_HAS_SLEEPEVT
if ((0 != (enabled & NRF_POWER_INT_SLEEPENTER_MASK)) &&
nrf_power_event_get_and_clear(NRF_POWER_EVENT_SLEEPENTER))
{
/* Cannot be null if event is enabled */
NRFX_ASSERT(m_sleepevt_handler != NULL);
m_sleepevt_handler(NRFX_POWER_SLEEP_EVT_ENTER);
}
if ((0 != (enabled & NRF_POWER_INT_SLEEPEXIT_MASK)) &&
nrf_power_event_get_and_clear(NRF_POWER_EVENT_SLEEPEXIT))
{
/* Cannot be null if event is enabled */
NRFX_ASSERT(m_sleepevt_handler != NULL);
m_sleepevt_handler(NRFX_POWER_SLEEP_EVT_EXIT);
}
#endif
#if NRF_POWER_HAS_USBREG
if ((0 != (enabled & NRF_POWER_INT_USBDETECTED_MASK)) &&
nrf_power_event_get_and_clear(NRF_POWER_EVENT_USBDETECTED))
{
/* Cannot be null if event is enabled */
NRFX_ASSERT(m_usbevt_handler != NULL);
m_usbevt_handler(NRFX_POWER_USB_EVT_DETECTED);
}
if ((0 != (enabled & NRF_POWER_INT_USBREMOVED_MASK)) &&
nrf_power_event_get_and_clear(NRF_POWER_EVENT_USBREMOVED))
{
/* Cannot be null if event is enabled */
NRFX_ASSERT(m_usbevt_handler != NULL);
m_usbevt_handler(NRFX_POWER_USB_EVT_REMOVED);
}
if ((0 != (enabled & NRF_POWER_INT_USBPWRRDY_MASK)) &&
nrf_power_event_get_and_clear(NRF_POWER_EVENT_USBPWRRDY))
{
/* Cannot be null if event is enabled */
NRFX_ASSERT(m_usbevt_handler != NULL);
m_usbevt_handler(NRFX_POWER_USB_EVT_READY);
}
#endif
}
#if NRFX_CHECK(NRFX_CLOCK_ENABLED)
/*
* If both POWER and CLOCK drivers are used, a common IRQ handler function must
* be used that calls the handlers in these two drivers. This is because these
* two peripherals share one interrupt.
* This function is located here, not in a separate nrfx_power_clock.c file,
* so that it does not end up as the only symbol in a separate object when
* a library with nrfx is created. In such case, forcing a linker to use this
* function instead of another one defined as weak will require additional
* actions, and might be even impossible.
*/
void nrfx_power_clock_irq_handler(void)
{
nrfx_power_irq_handler();
nrfx_clock_irq_handler();
}
#endif
#endif // NRFX_CHECK(NRFX_POWER_ENABLED)
+530
View File
@@ -0,0 +1,530 @@
/**
* Copyright (c) 2015 - 2021, 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, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, 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 its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA 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.
*
*/
#include <nrfx.h>
#if NRFX_CHECK(NRFX_PPI_ENABLED)
#include <nrfx_ppi.h>
#define NRFX_LOG_MODULE PPI
#include <nrfx_log.h>
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.
* @retval false The channel is used by a stack (for example SoftDevice) or is preprogrammed.
*/
__STATIC_INLINE bool is_programmable_app_channel(nrf_ppi_channel_t channel)
{
return ((NRFX_PPI_PROG_APP_CHANNELS_MASK & nrfx_ppi_channel_to_mask(channel)) != 0);
}
/**
* @brief Check whether channels can be used by an application.
*
* @param[in] channel_mask Channel mask to check.
*
* @retval true All specified channels can be used by an application.
* @retval false At least one specified channel is used by a stack (for example SoftDevice).
*/
__STATIC_INLINE bool are_app_channels(uint32_t channel_mask)
{
//lint -e(587)
return ((~(NRFX_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.
* @retval false The channel is used by a stack (for example SoftDevice).
*/
__STATIC_INLINE bool is_app_channel(nrf_ppi_channel_t channel)
{
return are_app_channels(nrfx_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.
* @retval false The group is not an application group (this group either does not exist or
* it is used by a stack (for example SoftDevice)).
*/
__STATIC_INLINE bool is_app_group(nrf_ppi_channel_group_t group)
{
return ((NRFX_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.
* @retval false The channel is not allocated.
*/
__STATIC_INLINE bool is_allocated_channel(nrf_ppi_channel_t channel)
{
return ((m_channels_allocated & nrfx_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 |= nrfx_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 &= ~nrfx_ppi_channel_to_mask(channel);
}
/**
* @brief Clear all allocated channels.
*/
__STATIC_INLINE void channel_allocated_clr_all(void)
{
m_channels_allocated &= ~NRFX_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 &= ~NRFX_PPI_ALL_APP_GROUPS_MASK;
}
void nrfx_ppi_free_all(void)
{
uint32_t mask = NRFX_PPI_ALL_APP_GROUPS_MASK;
nrf_ppi_channel_group_t group;
// Disable all channels and groups
nrf_ppi_channels_disable(NRFX_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();
}
nrfx_err_t nrfx_ppi_channel_alloc(nrf_ppi_channel_t * p_channel)
{
nrfx_err_t err_code = NRFX_SUCCESS;
nrf_ppi_channel_t channel;
uint32_t mask = 0;
err_code = NRFX_ERROR_NO_MEM;
mask = NRFX_PPI_PROG_APP_CHANNELS_MASK;
for (channel = NRF_PPI_CHANNEL0;
mask != 0;
mask &= ~nrfx_ppi_channel_to_mask(channel), channel++)
{
NRFX_CRITICAL_SECTION_ENTER();
if ((mask & nrfx_ppi_channel_to_mask(channel)) && (!is_allocated_channel(channel)))
{
channel_allocated_set(channel);
*p_channel = channel;
err_code = NRFX_SUCCESS;
}
NRFX_CRITICAL_SECTION_EXIT();
if (err_code == NRFX_SUCCESS)
{
NRFX_LOG_INFO("Allocated channel: %d.", channel);
break;
}
}
NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
nrfx_err_t nrfx_ppi_channel_free(nrf_ppi_channel_t channel)
{
nrfx_err_t err_code = NRFX_SUCCESS;
if (!is_programmable_app_channel(channel))
{
err_code = NRFX_ERROR_INVALID_PARAM;
}
else
{
// First disable this channel
nrf_ppi_channel_disable(channel);
NRFX_CRITICAL_SECTION_ENTER();
channel_allocated_clr(channel);
NRFX_CRITICAL_SECTION_EXIT();
}
NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
nrfx_err_t nrfx_ppi_channel_assign(nrf_ppi_channel_t channel, uint32_t eep, uint32_t tep)
{
if ((uint32_t *)eep == NULL || (uint32_t *)tep == NULL)
{
return NRFX_ERROR_NULL;
}
nrfx_err_t err_code = NRFX_SUCCESS;
if (!is_programmable_app_channel(channel))
{
err_code = NRFX_ERROR_INVALID_PARAM;
}
else if (!is_allocated_channel(channel))
{
err_code = NRFX_ERROR_INVALID_STATE;
}
else
{
nrf_ppi_channel_endpoint_setup(channel, eep, tep);
NRFX_LOG_INFO("Assigned channel: %d, event end point: %x, task end point: %x.",
channel,
eep,
tep);
}
NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
nrfx_err_t nrfx_ppi_channel_fork_assign(nrf_ppi_channel_t channel, uint32_t fork_tep)
{
nrfx_err_t err_code = NRFX_SUCCESS;
#ifdef PPI_FEATURE_FORKS_PRESENT
if (!is_allocated_channel(channel))
{
err_code = NRFX_ERROR_INVALID_STATE;
}
else
{
nrf_ppi_fork_endpoint_setup(channel, fork_tep);
NRFX_LOG_INFO("Fork assigned channel: %d, task end point: %d.", channel, fork_tep);
}
NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
#else
err_code = NRFX_ERROR_NOT_SUPPORTED;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
#endif
}
nrfx_err_t nrfx_ppi_channel_enable(nrf_ppi_channel_t channel)
{
nrfx_err_t err_code = NRFX_SUCCESS;
if (!is_app_channel(channel))
{
err_code = NRFX_ERROR_INVALID_PARAM;
}
else if (is_programmable_app_channel(channel) && !is_allocated_channel(channel))
{
err_code = NRFX_ERROR_INVALID_STATE;
}
else
{
nrf_ppi_channel_enable(channel);
}
NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
nrfx_err_t nrfx_ppi_channel_disable(nrf_ppi_channel_t channel)
{
nrfx_err_t err_code = NRFX_SUCCESS;
if (!is_app_channel(channel))
{
err_code = NRFX_ERROR_INVALID_PARAM;
}
else if (is_programmable_app_channel(channel) && !is_allocated_channel(channel))
{
err_code = NRFX_ERROR_INVALID_STATE;
}
else
{
nrf_ppi_channel_disable(channel);
err_code = NRFX_SUCCESS;
}
NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
nrfx_err_t nrfx_ppi_group_alloc(nrf_ppi_channel_group_t * p_group)
{
nrfx_err_t err_code;
uint32_t mask = 0;
nrf_ppi_channel_group_t group;
err_code = NRFX_ERROR_NO_MEM;
mask = NRFX_PPI_ALL_APP_GROUPS_MASK;
for (group = NRF_PPI_CHANNEL_GROUP0; mask != 0; mask &= ~group_to_mask(group), group++)
{
NRFX_CRITICAL_SECTION_ENTER();
if ((mask & group_to_mask(group)) && (!is_allocated_group(group)))
{
group_allocated_set(group);
*p_group = group;
err_code = NRFX_SUCCESS;
}
NRFX_CRITICAL_SECTION_EXIT();
if (err_code == NRFX_SUCCESS)
{
NRFX_LOG_INFO("Allocated group: %d.", group);
break;
}
}
NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
nrfx_err_t nrfx_ppi_group_free(nrf_ppi_channel_group_t group)
{
nrfx_err_t err_code = NRFX_SUCCESS;
if (!is_app_group(group))
{
err_code = NRFX_ERROR_INVALID_PARAM;
}
if (!is_allocated_group(group))
{
err_code = NRFX_ERROR_INVALID_STATE;
}
else
{
nrf_ppi_group_disable(group);
NRFX_CRITICAL_SECTION_ENTER();
group_allocated_clr(group);
NRFX_CRITICAL_SECTION_EXIT();
}
NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
nrfx_err_t nrfx_ppi_group_enable(nrf_ppi_channel_group_t group)
{
nrfx_err_t err_code = NRFX_SUCCESS;
if (!is_app_group(group))
{
err_code = NRFX_ERROR_INVALID_PARAM;
}
else if (!is_allocated_group(group))
{
err_code = NRFX_ERROR_INVALID_STATE;
}
else
{
nrf_ppi_group_enable(group);
}
NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
nrfx_err_t nrfx_ppi_group_disable(nrf_ppi_channel_group_t group)
{
nrfx_err_t err_code = NRFX_SUCCESS;
if (!is_app_group(group))
{
err_code = NRFX_ERROR_INVALID_PARAM;
}
else
{
nrf_ppi_group_disable(group);
}
NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
nrfx_err_t nrfx_ppi_channels_remove_from_group(uint32_t channel_mask,
nrf_ppi_channel_group_t group)
{
nrfx_err_t err_code = NRFX_SUCCESS;
if (!is_app_group(group))
{
err_code = NRFX_ERROR_INVALID_PARAM;
}
else if (!is_allocated_group(group))
{
err_code = NRFX_ERROR_INVALID_STATE;
}
else if (!are_app_channels(channel_mask))
{
err_code = NRFX_ERROR_INVALID_PARAM;
}
else
{
NRFX_CRITICAL_SECTION_ENTER();
nrf_ppi_channels_remove_from_group(channel_mask, group);
NRFX_CRITICAL_SECTION_EXIT();
}
NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
nrfx_err_t nrfx_ppi_channels_include_in_group(uint32_t channel_mask,
nrf_ppi_channel_group_t group)
{
nrfx_err_t err_code = NRFX_SUCCESS;
if (!is_app_group(group))
{
err_code = NRFX_ERROR_INVALID_PARAM;
}
else if (!is_allocated_group(group))
{
err_code = NRFX_ERROR_INVALID_STATE;
}
else if (!are_app_channels(channel_mask))
{
err_code = NRFX_ERROR_INVALID_PARAM;
}
else
{
NRFX_CRITICAL_SECTION_ENTER();
nrf_ppi_channels_include_in_group(channel_mask, group);
NRFX_CRITICAL_SECTION_EXIT();
}
NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
#endif // NRFX_CHECK(NRFX_PPI_ENABLED)
+523
View File
@@ -0,0 +1,523 @@
/**
* Copyright (c) 2015 - 2021, 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, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, 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 its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA 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.
*
*/
#include <nrfx.h>
#if NRFX_CHECK(NRFX_PWM_ENABLED)
#if !(NRFX_CHECK(NRFX_PWM0_ENABLED) || NRFX_CHECK(NRFX_PWM1_ENABLED) || \
NRFX_CHECK(NRFX_PWM2_ENABLED) || NRFX_CHECK(NRFX_PWM3_ENABLED))
#error "No enabled PWM instances. Check <nrfx_config.h>."
#endif
#include <nrfx_pwm.h>
#include <hal/nrf_gpio.h>
#define NRFX_LOG_MODULE PWM
#include <nrfx_log.h>
#if NRFX_CHECK(NRFX_PWM_NRF52_ANOMALY_109_WORKAROUND_ENABLED)
// The workaround uses interrupts to wake up the CPU and ensure it is active
// when PWM is about to start a DMA transfer. For initial transfer, done when
// a playback is started via PPI, a specific EGU instance is used to generate
// an interrupt. During the playback, the PWM interrupt triggered on SEQEND
// event of a preceding sequence is used to protect the transfer done for
// the next sequence to be played.
#include <hal/nrf_egu.h>
#define USE_DMA_ISSUE_WORKAROUND
#endif
#if defined(USE_DMA_ISSUE_WORKAROUND)
#define EGU_IRQn(i) EGU_IRQn_(i)
#define EGU_IRQn_(i) SWI##i##_EGU##i##_IRQn
#define EGU_IRQHandler(i) EGU_IRQHandler_(i)
#define EGU_IRQHandler_(i) nrfx_swi_##i##_irq_handler
#define DMA_ISSUE_EGU_IDX NRFX_PWM_NRF52_ANOMALY_109_EGU_INSTANCE
#define DMA_ISSUE_EGU NRFX_CONCAT_2(NRF_EGU, DMA_ISSUE_EGU_IDX)
#define DMA_ISSUE_EGU_IRQn EGU_IRQn(DMA_ISSUE_EGU_IDX)
#define DMA_ISSUE_EGU_IRQHandler EGU_IRQHandler(DMA_ISSUE_EGU_IDX)
#endif
// Control block - driver instance local data.
typedef struct
{
#if defined(USE_DMA_ISSUE_WORKAROUND)
uint32_t starting_task_address;
#endif
nrfx_pwm_handler_t handler;
nrfx_drv_state_t volatile state;
uint8_t flags;
} pwm_control_block_t;
static pwm_control_block_t m_cb[NRFX_PWM_ENABLED_COUNT];
static void configure_pins(nrfx_pwm_t const * const p_instance,
nrfx_pwm_config_t const * p_config)
{
uint32_t out_pins[NRF_PWM_CHANNEL_COUNT];
uint8_t i;
for (i = 0; i < NRF_PWM_CHANNEL_COUNT; ++i)
{
uint8_t output_pin = p_config->output_pins[i];
if (output_pin != NRFX_PWM_PIN_NOT_USED)
{
bool inverted = output_pin & NRFX_PWM_PIN_INVERTED;
out_pins[i] = output_pin & ~NRFX_PWM_PIN_INVERTED;
if (inverted)
{
nrf_gpio_pin_set(out_pins[i]);
}
else
{
nrf_gpio_pin_clear(out_pins[i]);
}
nrf_gpio_cfg_output(out_pins[i]);
}
else
{
out_pins[i] = NRF_PWM_PIN_NOT_CONNECTED;
}
}
nrf_pwm_pins_set(p_instance->p_registers, out_pins);
}
nrfx_err_t nrfx_pwm_init(nrfx_pwm_t const * const p_instance,
nrfx_pwm_config_t const * p_config,
nrfx_pwm_handler_t handler)
{
NRFX_ASSERT(p_config);
nrfx_err_t err_code;
pwm_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
if (p_cb->state != NRFX_DRV_STATE_UNINITIALIZED)
{
err_code = NRFX_ERROR_INVALID_STATE;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
p_cb->handler = handler;
configure_pins(p_instance, p_config);
nrf_pwm_enable(p_instance->p_registers);
nrf_pwm_configure(p_instance->p_registers,
p_config->base_clock, p_config->count_mode, p_config->top_value);
nrf_pwm_decoder_set(p_instance->p_registers,
p_config->load_mode, p_config->step_mode);
nrf_pwm_shorts_set(p_instance->p_registers, 0);
nrf_pwm_int_set(p_instance->p_registers, 0);
nrf_pwm_event_clear(p_instance->p_registers, NRF_PWM_EVENT_LOOPSDONE);
nrf_pwm_event_clear(p_instance->p_registers, NRF_PWM_EVENT_SEQEND0);
nrf_pwm_event_clear(p_instance->p_registers, NRF_PWM_EVENT_SEQEND1);
nrf_pwm_event_clear(p_instance->p_registers, NRF_PWM_EVENT_STOPPED);
// The workaround for nRF52 Anomaly 109 "protects" DMA transfers by handling
// interrupts generated on SEQEND0 and SEQEND1 events (this ensures that
// the 64 MHz clock is ready when data for the next sequence to be played
// is read). Therefore, the PWM interrupt must be enabled even if the event
// handler is not used.
#if defined(USE_DMA_ISSUE_WORKAROUND)
NRFX_IRQ_PRIORITY_SET(DMA_ISSUE_EGU_IRQn, p_config->irq_priority);
NRFX_IRQ_ENABLE(DMA_ISSUE_EGU_IRQn);
#else
if (p_cb->handler)
#endif
{
NRFX_IRQ_PRIORITY_SET(nrfx_get_irq_number(p_instance->p_registers),
p_config->irq_priority);
NRFX_IRQ_ENABLE(nrfx_get_irq_number(p_instance->p_registers));
}
p_cb->state = NRFX_DRV_STATE_INITIALIZED;
err_code = NRFX_SUCCESS;
NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
void nrfx_pwm_uninit(nrfx_pwm_t const * const p_instance)
{
pwm_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
NRFX_ASSERT(p_cb->state != NRFX_DRV_STATE_UNINITIALIZED);
NRFX_IRQ_DISABLE(nrfx_get_irq_number(p_instance->p_registers));
#if defined(USE_DMA_ISSUE_WORKAROUND)
NRFX_IRQ_DISABLE(DMA_ISSUE_EGU_IRQn);
#endif
nrf_pwm_disable(p_instance->p_registers);
p_cb->state = NRFX_DRV_STATE_UNINITIALIZED;
}
static uint32_t start_playback(nrfx_pwm_t const * const p_instance,
pwm_control_block_t * p_cb,
uint8_t flags,
nrf_pwm_task_t starting_task)
{
p_cb->state = NRFX_DRV_STATE_POWERED_ON;
p_cb->flags = flags;
if (p_cb->handler)
{
// The notification about finished playback is by default enabled,
// but this can be suppressed.
// The notification that the peripheral has stopped is always enabled.
uint32_t int_mask = NRF_PWM_INT_LOOPSDONE_MASK |
NRF_PWM_INT_STOPPED_MASK;
// The workaround for nRF52 Anomaly 109 "protects" DMA transfers by
// handling interrupts generated on SEQEND0 and SEQEND1 events (see
// 'nrfx_pwm_init'), hence these events must be always enabled
// to generate interrupts.
// However, the user handler is called for them only when requested
// (see 'irq_handler').
#if defined(USE_DMA_ISSUE_WORKAROUND)
int_mask |= NRF_PWM_INT_SEQEND0_MASK | NRF_PWM_INT_SEQEND1_MASK;
#else
if (flags & NRFX_PWM_FLAG_SIGNAL_END_SEQ0)
{
int_mask |= NRF_PWM_INT_SEQEND0_MASK;
}
if (flags & NRFX_PWM_FLAG_SIGNAL_END_SEQ1)
{
int_mask |= NRF_PWM_INT_SEQEND1_MASK;
}
#endif
if (flags & NRFX_PWM_FLAG_NO_EVT_FINISHED)
{
int_mask &= ~NRF_PWM_INT_LOOPSDONE_MASK;
}
nrf_pwm_int_set(p_instance->p_registers, int_mask);
}
#if defined(USE_DMA_ISSUE_WORKAROUND)
else
{
nrf_pwm_int_set(p_instance->p_registers,
NRF_PWM_INT_SEQEND0_MASK | NRF_PWM_INT_SEQEND1_MASK);
}
#endif
nrf_pwm_event_clear(p_instance->p_registers, NRF_PWM_EVENT_STOPPED);
if (flags & NRFX_PWM_FLAG_START_VIA_TASK)
{
uint32_t starting_task_address =
nrf_pwm_task_address_get(p_instance->p_registers, starting_task);
#if defined(USE_DMA_ISSUE_WORKAROUND)
// To "protect" the initial DMA transfer it is required to start
// the PWM by triggering the proper task from EGU interrupt handler,
// it is not safe to do it directly via PPI.
p_cb->starting_task_address = starting_task_address;
nrf_egu_int_enable(DMA_ISSUE_EGU,
nrf_egu_int_get(DMA_ISSUE_EGU, p_instance->drv_inst_idx));
return (uint32_t)nrf_egu_task_trigger_address_get(DMA_ISSUE_EGU,
p_instance->drv_inst_idx);
#else
return starting_task_address;
#endif
}
nrf_pwm_task_trigger(p_instance->p_registers, starting_task);
return 0;
}
uint32_t nrfx_pwm_simple_playback(nrfx_pwm_t const * const p_instance,
nrf_pwm_sequence_t const * p_sequence,
uint16_t playback_count,
uint32_t flags)
{
pwm_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
NRFX_ASSERT(p_cb->state != NRFX_DRV_STATE_UNINITIALIZED);
NRFX_ASSERT(playback_count > 0);
NRFX_ASSERT(nrfx_is_in_ram(p_sequence->values.p_raw));
// To take advantage of the looping mechanism, we need to use both sequences
// (single sequence can be played back only once).
nrf_pwm_sequence_set(p_instance->p_registers, 0, p_sequence);
nrf_pwm_sequence_set(p_instance->p_registers, 1, p_sequence);
bool odd = (playback_count & 1);
nrf_pwm_loop_set(p_instance->p_registers,
(playback_count / 2) + (odd ? 1 : 0));
uint32_t shorts_mask;
if (flags & NRFX_PWM_FLAG_STOP)
{
shorts_mask = NRF_PWM_SHORT_LOOPSDONE_STOP_MASK;
}
else if (flags & NRFX_PWM_FLAG_LOOP)
{
shorts_mask = odd ? NRF_PWM_SHORT_LOOPSDONE_SEQSTART1_MASK
: NRF_PWM_SHORT_LOOPSDONE_SEQSTART0_MASK;
}
else
{
shorts_mask = 0;
}
nrf_pwm_shorts_set(p_instance->p_registers, shorts_mask);
NRFX_LOG_INFO("Function: %s, sequence length: %d.",
__func__,
p_sequence->length);
NRFX_LOG_DEBUG("Sequence data:");
NRFX_LOG_HEXDUMP_DEBUG((uint8_t *)p_sequence->values.p_raw,
p_sequence->length * sizeof(uint16_t));
return start_playback(p_instance, p_cb, flags,
odd ? NRF_PWM_TASK_SEQSTART1 : NRF_PWM_TASK_SEQSTART0);
}
uint32_t nrfx_pwm_complex_playback(nrfx_pwm_t const * const p_instance,
nrf_pwm_sequence_t const * p_sequence_0,
nrf_pwm_sequence_t const * p_sequence_1,
uint16_t playback_count,
uint32_t flags)
{
pwm_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
NRFX_ASSERT(p_cb->state != NRFX_DRV_STATE_UNINITIALIZED);
NRFX_ASSERT(playback_count > 0);
NRFX_ASSERT(nrfx_is_in_ram(p_sequence_0->values.p_raw));
NRFX_ASSERT(nrfx_is_in_ram(p_sequence_1->values.p_raw));
nrf_pwm_sequence_set(p_instance->p_registers, 0, p_sequence_0);
nrf_pwm_sequence_set(p_instance->p_registers, 1, p_sequence_1);
nrf_pwm_loop_set(p_instance->p_registers, playback_count);
uint32_t shorts_mask;
if (flags & NRFX_PWM_FLAG_STOP)
{
shorts_mask = NRF_PWM_SHORT_LOOPSDONE_STOP_MASK;
}
else if (flags & NRFX_PWM_FLAG_LOOP)
{
shorts_mask = NRF_PWM_SHORT_LOOPSDONE_SEQSTART0_MASK;
}
else
{
shorts_mask = 0;
}
nrf_pwm_shorts_set(p_instance->p_registers, shorts_mask);
NRFX_LOG_INFO("Function: %s, sequence 0 length: %d.",
__func__,
p_sequence_0->length);
NRFX_LOG_INFO("Function: %s, sequence 1 length: %d.",
__func__,
p_sequence_1->length);
NRFX_LOG_DEBUG("Sequence 0 data:");
NRFX_LOG_HEXDUMP_DEBUG(p_sequence_0->values.p_raw,
p_sequence_0->length * sizeof(uint16_t));
NRFX_LOG_DEBUG("Sequence 1 data:");
NRFX_LOG_HEXDUMP_DEBUG(p_sequence_1->values.p_raw,
p_sequence_1->length * sizeof(uint16_t));
return start_playback(p_instance, p_cb, flags, NRF_PWM_TASK_SEQSTART0);
}
bool nrfx_pwm_stop(nrfx_pwm_t const * const p_instance,
bool wait_until_stopped)
{
NRFX_ASSERT(m_cb[p_instance->drv_inst_idx].state != NRFX_DRV_STATE_UNINITIALIZED);
bool ret_val = false;
// Deactivate shortcuts before triggering the STOP task, otherwise the PWM
// could be immediately started again if the LOOPSDONE event occurred in
// the same peripheral clock cycle as the STOP task was triggered.
nrf_pwm_shorts_set(p_instance->p_registers, 0);
// Trigger the STOP task even if the PWM appears to be already stopped.
// It won't harm, but it could help if for some strange reason the stopped
// status was not reported correctly.
nrf_pwm_task_trigger(p_instance->p_registers, NRF_PWM_TASK_STOP);
if (nrfx_pwm_is_stopped(p_instance))
{
ret_val = true;
}
else
{
do {
if (nrfx_pwm_is_stopped(p_instance))
{
ret_val = true;
break;
}
} while (wait_until_stopped);
}
NRFX_LOG_INFO("%s returned %d.", __func__, ret_val);
return ret_val;
}
bool nrfx_pwm_is_stopped(nrfx_pwm_t const * const p_instance)
{
pwm_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
NRFX_ASSERT(p_cb->state != NRFX_DRV_STATE_UNINITIALIZED);
bool ret_val = false;
// If the event handler is used (interrupts are enabled), the state will
// be changed in interrupt handler when the STOPPED event occurs.
if (p_cb->state != NRFX_DRV_STATE_POWERED_ON)
{
ret_val = true;
}
// If interrupts are disabled, we must check the STOPPED event here.
if (nrf_pwm_event_check(p_instance->p_registers, NRF_PWM_EVENT_STOPPED))
{
p_cb->state = NRFX_DRV_STATE_INITIALIZED;
NRFX_LOG_INFO("Disabled.");
ret_val = true;
}
NRFX_LOG_INFO("%s returned %d.", __func__, ret_val);
return ret_val;
}
static void irq_handler(NRF_PWM_Type * p_pwm, pwm_control_block_t * p_cb)
{
// The user handler is called for SEQEND0 and SEQEND1 events only when the
// user asks for it (by setting proper flags when starting the playback).
if (nrf_pwm_event_check(p_pwm, NRF_PWM_EVENT_SEQEND0))
{
nrf_pwm_event_clear(p_pwm, NRF_PWM_EVENT_SEQEND0);
if ((p_cb->flags & NRFX_PWM_FLAG_SIGNAL_END_SEQ0) && p_cb->handler)
{
p_cb->handler(NRFX_PWM_EVT_END_SEQ0);
}
}
if (nrf_pwm_event_check(p_pwm, NRF_PWM_EVENT_SEQEND1))
{
nrf_pwm_event_clear(p_pwm, NRF_PWM_EVENT_SEQEND1);
if ((p_cb->flags & NRFX_PWM_FLAG_SIGNAL_END_SEQ1) && p_cb->handler)
{
p_cb->handler(NRFX_PWM_EVT_END_SEQ1);
}
}
// For LOOPSDONE the handler is called by default, but the user can disable
// this (via flags).
if (nrf_pwm_event_check(p_pwm, NRF_PWM_EVENT_LOOPSDONE))
{
nrf_pwm_event_clear(p_pwm, NRF_PWM_EVENT_LOOPSDONE);
if (!(p_cb->flags & NRFX_PWM_FLAG_NO_EVT_FINISHED) && p_cb->handler)
{
p_cb->handler(NRFX_PWM_EVT_FINISHED);
}
}
// The STOPPED event is always propagated to the user handler.
if (nrf_pwm_event_check(p_pwm, NRF_PWM_EVENT_STOPPED))
{
nrf_pwm_event_clear(p_pwm, NRF_PWM_EVENT_STOPPED);
p_cb->state = NRFX_DRV_STATE_INITIALIZED;
if (p_cb->handler)
{
p_cb->handler(NRFX_PWM_EVT_STOPPED);
}
}
}
#if defined(USE_DMA_ISSUE_WORKAROUND)
// See 'start_playback' why this is needed.
void DMA_ISSUE_EGU_IRQHandler(void)
{
int i;
for (i = 0; i < NRFX_PWM_ENABLED_COUNT; ++i)
{
volatile uint32_t * p_event_reg =
nrf_egu_event_triggered_address_get(DMA_ISSUE_EGU, i);
if (*p_event_reg)
{
*p_event_reg = 0;
*(volatile uint32_t *)(m_cb[i].starting_task_address) = 1;
}
}
}
#endif
#if NRFX_CHECK(NRFX_PWM0_ENABLED)
void nrfx_pwm_0_irq_handler(void)
{
irq_handler(NRF_PWM0, &m_cb[NRFX_PWM0_INST_IDX]);
}
#endif
#if NRFX_CHECK(NRFX_PWM1_ENABLED)
void nrfx_pwm_1_irq_handler(void)
{
irq_handler(NRF_PWM1, &m_cb[NRFX_PWM1_INST_IDX]);
}
#endif
#if NRFX_CHECK(NRFX_PWM2_ENABLED)
void nrfx_pwm_2_irq_handler(void)
{
irq_handler(NRF_PWM2, &m_cb[NRFX_PWM2_INST_IDX]);
}
#endif
#if NRFX_CHECK(NRFX_PWM3_ENABLED)
void nrfx_pwm_3_irq_handler(void)
{
irq_handler(NRF_PWM3, &m_cb[NRFX_PWM3_INST_IDX]);
}
#endif
#endif // NRFX_CHECK(NRFX_PWM_ENABLED)
+122
View File
@@ -0,0 +1,122 @@
/**
* Copyright (c) 2016 - 2021, 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, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, 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 its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA 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.
*
*/
#include <nrfx.h>
#if NRFX_CHECK(NRFX_RNG_ENABLED)
#include <nrfx_rng.h>
#define NRFX_LOG_MODULE RNG
#include <nrfx_log.h>
/**
* @brief Internal state of RNG driver.
*/
static nrfx_drv_state_t m_rng_state;
/**
* @brief Pointer to handler calling from interrupt routine.
*/
static nrfx_rng_evt_handler_t m_rng_hndl;
nrfx_err_t nrfx_rng_init(nrfx_rng_config_t const * p_config, nrfx_rng_evt_handler_t handler)
{
NRFX_ASSERT(p_config);
NRFX_ASSERT(handler);
if (m_rng_state != NRFX_DRV_STATE_UNINITIALIZED)
{
return NRFX_ERROR_ALREADY_INITIALIZED;
}
m_rng_hndl = handler;
if (p_config->error_correction)
{
nrf_rng_error_correction_enable();
}
nrf_rng_shorts_disable(NRF_RNG_SHORT_VALRDY_STOP_MASK);
NRFX_IRQ_PRIORITY_SET(RNG_IRQn, p_config->interrupt_priority);
NRFX_IRQ_ENABLE(RNG_IRQn);
m_rng_state = NRFX_DRV_STATE_INITIALIZED;
return NRFX_SUCCESS;
}
void nrfx_rng_start(void)
{
NRFX_ASSERT(m_rng_state == NRFX_DRV_STATE_INITIALIZED);
nrf_rng_event_clear(NRF_RNG_EVENT_VALRDY);
nrf_rng_int_enable(NRF_RNG_INT_VALRDY_MASK);
nrf_rng_task_trigger(NRF_RNG_TASK_START);
}
void nrfx_rng_stop(void)
{
NRFX_ASSERT(m_rng_state == NRFX_DRV_STATE_INITIALIZED);
nrf_rng_int_disable(NRF_RNG_INT_VALRDY_MASK);
nrf_rng_task_trigger(NRF_RNG_TASK_STOP);
}
void nrfx_rng_uninit(void)
{
NRFX_ASSERT(m_rng_state == NRFX_DRV_STATE_INITIALIZED);
nrf_rng_int_disable(NRF_RNG_INT_VALRDY_MASK);
nrf_rng_task_trigger(NRF_RNG_TASK_STOP);
NRFX_IRQ_DISABLE(RNG_IRQn);
m_rng_state = NRFX_DRV_STATE_UNINITIALIZED;
NRFX_LOG_INFO("Uninitialized.");
}
void nrfx_rng_irq_handler(void)
{
nrf_rng_event_clear(NRF_RNG_EVENT_VALRDY);
uint8_t rng_value = nrf_rng_random_value_get();
m_rng_hndl(rng_value);
NRFX_LOG_DEBUG("Event: NRF_RNG_EVENT_VALRDY.");
}
#endif // NRFX_CHECK(NRFX_RNG_ENABLED)
+348
View File
@@ -0,0 +1,348 @@
/**
* Copyright (c) 2014 - 2021, 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, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, 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 its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA 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.
*
*/
#include <nrfx.h>
#if NRFX_CHECK(NRFX_RTC_ENABLED)
#if !(NRFX_CHECK(NRFX_RTC0_ENABLED) || NRFX_CHECK(NRFX_RTC1_ENABLED) || \
NRFX_CHECK(NRFX_RTC2_ENABLED))
#error "No enabled RTC instances. Check <nrfx_config.h>."
#endif
#include <nrfx_rtc.h>
#define NRFX_LOG_MODULE RTC
#include <nrfx_log.h>
#define EVT_TO_STR(event) \
(event == NRF_RTC_EVENT_TICK ? "NRF_RTC_EVENT_TICK" : \
(event == NRF_RTC_EVENT_OVERFLOW ? "NRF_RTC_EVENT_OVERFLOW" : \
(event == NRF_RTC_EVENT_COMPARE_0 ? "NRF_RTC_EVENT_COMPARE_0" : \
(event == NRF_RTC_EVENT_COMPARE_1 ? "NRF_RTC_EVENT_COMPARE_1" : \
(event == NRF_RTC_EVENT_COMPARE_2 ? "NRF_RTC_EVENT_COMPARE_2" : \
(event == NRF_RTC_EVENT_COMPARE_3 ? "NRF_RTC_EVENT_COMPARE_3" : \
"UNKNOWN EVENT"))))))
/**@brief RTC driver instance control block structure. */
typedef struct
{
nrfx_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). */
} nrfx_rtc_cb_t;
// User callbacks local storage.
static nrfx_rtc_handler_t m_handlers[NRFX_RTC_ENABLED_COUNT];
static nrfx_rtc_cb_t m_cb[NRFX_RTC_ENABLED_COUNT];
nrfx_err_t nrfx_rtc_init(nrfx_rtc_t const * const p_instance,
nrfx_rtc_config_t const * p_config,
nrfx_rtc_handler_t handler)
{
NRFX_ASSERT(p_config);
NRFX_ASSERT(handler);
nrfx_err_t err_code;
m_handlers[p_instance->instance_id] = handler;
if (m_cb[p_instance->instance_id].state != NRFX_DRV_STATE_UNINITIALIZED)
{
err_code = NRFX_ERROR_INVALID_STATE;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
NRFX_IRQ_PRIORITY_SET(p_instance->irq, p_config->interrupt_priority);
NRFX_IRQ_ENABLE(p_instance->irq);
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 = NRFX_DRV_STATE_INITIALIZED;
err_code = NRFX_SUCCESS;
NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
void nrfx_rtc_uninit(nrfx_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;
NRFX_ASSERT(m_cb[p_instance->instance_id].state != NRFX_DRV_STATE_UNINITIALIZED);
NRFX_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 = NRFX_DRV_STATE_UNINITIALIZED;
NRFX_LOG_INFO("Uninitialized.");
}
void nrfx_rtc_enable(nrfx_rtc_t const * const p_instance)
{
NRFX_ASSERT(m_cb[p_instance->instance_id].state == NRFX_DRV_STATE_INITIALIZED);
nrf_rtc_task_trigger(p_instance->p_reg, NRF_RTC_TASK_START);
m_cb[p_instance->instance_id].state = NRFX_DRV_STATE_POWERED_ON;
NRFX_LOG_INFO("Enabled.");
}
void nrfx_rtc_disable(nrfx_rtc_t const * const p_instance)
{
NRFX_ASSERT(m_cb[p_instance->instance_id].state != NRFX_DRV_STATE_UNINITIALIZED);
nrf_rtc_task_trigger(p_instance->p_reg, NRF_RTC_TASK_STOP);
m_cb[p_instance->instance_id].state = NRFX_DRV_STATE_INITIALIZED;
NRFX_LOG_INFO("Disabled.");
}
nrfx_err_t nrfx_rtc_cc_disable(nrfx_rtc_t const * const p_instance, uint32_t channel)
{
NRFX_ASSERT(m_cb[p_instance->instance_id].state != NRFX_DRV_STATE_UNINITIALIZED);
NRFX_ASSERT(channel<p_instance->cc_channel_count);
nrfx_err_t err_code;
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);
err_code = NRFX_ERROR_TIMEOUT;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
}
NRFX_LOG_INFO("RTC id: %d, channel disabled: %lu.", p_instance->instance_id, channel);
err_code = NRFX_SUCCESS;
NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
nrfx_err_t nrfx_rtc_cc_set(nrfx_rtc_t const * const p_instance,
uint32_t channel,
uint32_t val,
bool enable_irq)
{
NRFX_ASSERT(m_cb[p_instance->instance_id].state != NRFX_DRV_STATE_UNINITIALIZED);
NRFX_ASSERT(channel<p_instance->cc_channel_count);
nrfx_err_t err_code;
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)
{
err_code = NRFX_ERROR_TIMEOUT;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
}
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);
NRFX_LOG_INFO("RTC id: %d, channel enabled: %lu, compare value: %lu.",
p_instance->instance_id,
channel,
val);
err_code = NRFX_SUCCESS;
NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
void nrfx_rtc_tick_enable(nrfx_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);
}
NRFX_LOG_INFO("Tick events enabled.");
}
void nrfx_rtc_tick_disable(nrfx_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);
NRFX_LOG_INFO("Tick events disabled.");
}
void nrfx_rtc_overflow_enable(nrfx_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 nrfx_rtc_overflow_disable(nrfx_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 nrfx_rtc_max_ticks_get(nrfx_rtc_t const * const p_instance)
{
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;
}
static void irq_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);
NRFX_LOG_DEBUG("Event: %s, instance id: %lu.", EVT_TO_STR(event), instance_id);
m_handlers[instance_id]((nrfx_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);
NRFX_LOG_DEBUG("Event: %s, instance id: %lu.", EVT_TO_STR(event), instance_id);
m_handlers[instance_id](NRFX_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);
NRFX_LOG_DEBUG("Event: %s, instance id: %lu.", EVT_TO_STR(event), instance_id);
m_handlers[instance_id](NRFX_RTC_INT_OVERFLOW);
}
}
#if NRFX_CHECK(NRFX_RTC0_ENABLED)
void nrfx_rtc_0_irq_handler(void)
{
irq_handler(NRF_RTC0, NRFX_RTC0_INST_IDX, NRF_RTC_CC_CHANNEL_COUNT(0));
}
#endif
#if NRFX_CHECK(NRFX_RTC1_ENABLED)
void nrfx_rtc_1_irq_handler(void)
{
irq_handler(NRF_RTC1, NRFX_RTC1_INST_IDX, NRF_RTC_CC_CHANNEL_COUNT(1));
}
#endif
#if NRFX_CHECK(NRFX_RTC2_ENABLED)
void nrfx_rtc_2_irq_handler(void)
{
irq_handler(NRF_RTC2, NRFX_RTC2_INST_IDX, NRF_RTC_CC_CHANNEL_COUNT(2));
}
#endif
#endif // NRFX_CHECK(NRFX_RTC_ENABLED)
File diff suppressed because it is too large Load Diff
+436
View File
@@ -0,0 +1,436 @@
/**
* Copyright (c) 2015 - 2021, 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, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, 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 its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA 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.
*
*/
#include <nrfx.h>
#if NRFX_CHECK(NRFX_SPI_ENABLED)
#if !(NRFX_CHECK(NRFX_SPI0_ENABLED) || NRFX_CHECK(NRFX_SPI1_ENABLED) || \
NRFX_CHECK(NRFX_SPI2_ENABLED))
#error "No enabled SPI instances. Check <nrfx_config.h>."
#endif
#include <nrfx_spi.h>
#include "prs/nrfx_prs.h"
#include <hal/nrf_gpio.h>
#define NRFX_LOG_MODULE SPI
#include <nrfx_log.h>
// Control block - driver instance local data.
typedef struct
{
nrfx_spi_evt_handler_t handler;
void * p_context;
nrfx_spi_evt_t evt; // Keep the struct that is ready for event handler. Less memcpy.
nrfx_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 miso_pin;
uint8_t orc;
size_t bytes_transferred;
bool abort;
} spi_control_block_t;
static spi_control_block_t m_cb[NRFX_SPI_ENABLED_COUNT];
nrfx_err_t nrfx_spi_init(nrfx_spi_t const * const p_instance,
nrfx_spi_config_t const * p_config,
nrfx_spi_evt_handler_t handler,
void * p_context)
{
NRFX_ASSERT(p_config);
spi_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
nrfx_err_t err_code;
if (p_cb->state != NRFX_DRV_STATE_UNINITIALIZED)
{
err_code = NRFX_ERROR_INVALID_STATE;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
#if NRFX_CHECK(NRFX_PRS_ENABLED)
static nrfx_irq_handler_t const irq_handlers[NRFX_SPI_ENABLED_COUNT] = {
#if NRFX_CHECK(NRFX_SPI0_ENABLED)
nrfx_spi_0_irq_handler,
#endif
#if NRFX_CHECK(NRFX_SPI1_ENABLED)
nrfx_spi_1_irq_handler,
#endif
#if NRFX_CHECK(NRFX_SPI2_ENABLED)
nrfx_spi_2_irq_handler,
#endif
};
if (nrfx_prs_acquire(p_instance->p_reg,
irq_handlers[p_instance->drv_inst_idx]) != NRFX_SUCCESS)
{
err_code = NRFX_ERROR_BUSY;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
#endif // NRFX_CHECK(NRFX_PRS_ENABLED)
p_cb->handler = handler;
p_cb->p_context = p_context;
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_SPI_MODE_1)
{
nrf_gpio_pin_clear(p_config->sck_pin);
}
else
{
nrf_gpio_pin_set(p_config->sck_pin);
}
nrf_gpio_cfg(p_config->sck_pin,
NRF_GPIO_PIN_DIR_OUTPUT,
NRF_GPIO_PIN_INPUT_CONNECT,
NRF_GPIO_PIN_NOPULL,
NRF_GPIO_PIN_S0S1,
NRF_GPIO_PIN_NOSENSE);
// - MOSI (optional) - output with initial value 0,
if (p_config->mosi_pin != NRFX_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 != NRFX_SPI_PIN_NOT_USED)
{
miso_pin = p_config->miso_pin;
nrf_gpio_cfg_input(miso_pin, (nrf_gpio_pin_pull_t)NRFX_SPI_MISO_PULL_CFG);
}
else
{
miso_pin = NRF_SPI_PIN_NOT_CONNECTED;
}
m_cb[p_instance->drv_inst_idx].miso_pin = p_config->miso_pin;
// - Slave Select (optional) - output with initial value 1 (inactive).
if (p_config->ss_pin != NRFX_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;
NRF_SPI_Type * p_spi = p_instance->p_reg;
nrf_spi_pins_set(p_spi, p_config->sck_pin, mosi_pin, miso_pin);
nrf_spi_frequency_set(p_spi, p_config->frequency);
nrf_spi_configure(p_spi, p_config->mode, p_config->bit_order);
m_cb[p_instance->drv_inst_idx].orc = p_config->orc;
nrf_spi_enable(p_spi);
if (p_cb->handler)
{
NRFX_IRQ_PRIORITY_SET(nrfx_get_irq_number(p_instance->p_reg),
p_config->irq_priority);
NRFX_IRQ_ENABLE(nrfx_get_irq_number(p_instance->p_reg));
}
p_cb->transfer_in_progress = false;
p_cb->state = NRFX_DRV_STATE_INITIALIZED;
err_code = NRFX_SUCCESS;
NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
void nrfx_spi_uninit(nrfx_spi_t const * const p_instance)
{
spi_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
NRFX_ASSERT(p_cb->state != NRFX_DRV_STATE_UNINITIALIZED);
if (p_cb->handler)
{
NRFX_IRQ_DISABLE(nrfx_get_irq_number(p_instance->p_reg));
}
NRF_SPI_Type * p_spi = p_instance->p_reg;
if (p_cb->handler)
{
nrf_spi_int_disable(p_spi, NRF_SPI_ALL_INTS_MASK);
}
if (p_cb->miso_pin != NRFX_SPI_PIN_NOT_USED)
{
nrf_gpio_cfg_default(p_cb->miso_pin);
}
nrf_spi_disable(p_spi);
#if NRFX_CHECK(NRFX_PRS_ENABLED)
nrfx_prs_release(p_instance->p_reg);
#endif
p_cb->state = NRFX_DRV_STATE_UNINITIALIZED;
}
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 != NRFX_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 = NRFX_SPI_EVENT_DONE;
p_cb->handler(&p_cb->evt, p_cb->p_context);
}
// This function is called from the IRQ handler or, in blocking mode, directly
// from the 'spi_xfer' 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 (always, because no further
// READY event can be generated until the current byte is read out from the
// RXD register), and store it in the RX buffer (only when needed).
volatile uint8_t rx_data = nrf_spi_rxd_get(p_spi);
if (p_cb->bytes_transferred < p_cb->evt.xfer_desc.rx_length)
{
p_cb->evt.xfer_desc.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 'spi_xfer' function.
size_t bytes_used = p_cb->bytes_transferred + 1;
if (p_cb->abort)
{
if (bytes_used < p_cb->evt.xfer_desc.tx_length)
{
p_cb->evt.xfer_desc.tx_length = bytes_used;
}
if (bytes_used < p_cb->evt.xfer_desc.rx_length)
{
p_cb->evt.xfer_desc.rx_length = bytes_used;
}
}
if (bytes_used < p_cb->evt.xfer_desc.tx_length)
{
nrf_spi_txd_set(p_spi, p_cb->evt.xfer_desc.p_tx_buffer[bytes_used]);
return true;
}
else if (bytes_used < p_cb->evt.xfer_desc.rx_length)
{
nrf_spi_txd_set(p_spi, p_cb->orc);
return true;
}
return (p_cb->bytes_transferred < p_cb->evt.xfer_desc.tx_length ||
p_cb->bytes_transferred < p_cb->evt.xfer_desc.rx_length);
}
static void spi_xfer(NRF_SPI_Type * p_spi,
spi_control_block_t * p_cb,
nrfx_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);
NRFX_LOG_DEBUG("SPI: Event: NRF_SPI_EVENT_READY.");
} while (transfer_byte(p_spi, p_cb));
if (p_cb->ss_pin != NRFX_SPI_PIN_NOT_USED)
{
nrf_gpio_pin_set(p_cb->ss_pin);
}
}
}
nrfx_err_t nrfx_spi_xfer(nrfx_spi_t const * const p_instance,
nrfx_spi_xfer_desc_t const * p_xfer_desc,
uint32_t flags)
{
spi_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
NRFX_ASSERT(p_cb->state != NRFX_DRV_STATE_UNINITIALIZED);
NRFX_ASSERT(p_xfer_desc->p_tx_buffer != NULL || p_xfer_desc->tx_length == 0);
NRFX_ASSERT(p_xfer_desc->p_rx_buffer != NULL || p_xfer_desc->rx_length == 0);
nrfx_err_t err_code = NRFX_SUCCESS;
if (p_cb->transfer_in_progress)
{
err_code = NRFX_ERROR_BUSY;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
else
{
if (p_cb->handler)
{
p_cb->transfer_in_progress = true;
}
}
p_cb->evt.xfer_desc = *p_xfer_desc;
p_cb->abort = false;
if (p_cb->ss_pin != NRFX_SPI_PIN_NOT_USED)
{
nrf_gpio_pin_clear(p_cb->ss_pin);
}
if (flags)
{
p_cb->transfer_in_progress = false;
err_code = NRFX_ERROR_NOT_SUPPORTED;
}
else
{
spi_xfer(p_instance->p_reg, p_cb, p_xfer_desc);
}
NRFX_LOG_INFO("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
void nrfx_spi_abort(nrfx_spi_t const * p_instance)
{
spi_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
NRFX_ASSERT(p_cb->state != NRFX_DRV_STATE_UNINITIALIZED);
p_cb->abort = true;
}
static void irq_handler(NRF_SPI_Type * p_spi, spi_control_block_t * p_cb)
{
NRFX_ASSERT(p_cb->handler);
nrf_spi_event_clear(p_spi, NRF_SPI_EVENT_READY);
NRFX_LOG_DEBUG("Event: NRF_SPI_EVENT_READY.");
if (!transfer_byte(p_spi, p_cb))
{
finish_transfer(p_cb);
}
}
#if NRFX_CHECK(NRFX_SPI0_ENABLED)
void nrfx_spi_0_irq_handler(void)
{
irq_handler(NRF_SPI0, &m_cb[NRFX_SPI0_INST_IDX]);
}
#endif
#if NRFX_CHECK(NRFX_SPI1_ENABLED)
void nrfx_spi_1_irq_handler(void)
{
irq_handler(NRF_SPI1, &m_cb[NRFX_SPI1_INST_IDX]);
}
#endif
#if NRFX_CHECK(NRFX_SPI2_ENABLED)
void nrfx_spi_2_irq_handler(void)
{
irq_handler(NRF_SPI2, &m_cb[NRFX_SPI2_INST_IDX]);
}
#endif
#endif // NRFX_CHECK(NRFX_SPI_ENABLED)
+697
View File
@@ -0,0 +1,697 @@
/**
* Copyright (c) 2015 - 2021, 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, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, 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 its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA 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.
*
*/
#include <nrfx.h>
#if NRFX_CHECK(NRFX_SPIM_ENABLED)
#if !(NRFX_CHECK(NRFX_SPIM0_ENABLED) || NRFX_CHECK(NRFX_SPIM1_ENABLED) || \
NRFX_CHECK(NRFX_SPIM2_ENABLED) || NRFX_CHECK(NRFX_SPIM3_ENABLED))
#error "No enabled SPIM instances. Check <nrfx_config.h>."
#endif
#if NRFX_CHECK(NRFX_SPIM_EXTENDED_ENABLED) && !NRFX_CHECK(NRFX_SPIM3_ENABLED)
#error "Extended options are available only in SPIM3 on the nRF52840 SoC."
#endif
#include <nrfx_spim.h>
#include "prs/nrfx_prs.h"
#include <hal/nrf_gpio.h>
#define NRFX_LOG_MODULE SPIM
#include <nrfx_log.h>
#define SPIMX_LENGTH_VALIDATE(peripheral, drv_inst_idx, rx_len, tx_len) \
(((drv_inst_idx) == NRFX_CONCAT_3(NRFX_, peripheral, _INST_IDX)) && \
NRFX_EASYDMA_LENGTH_VALIDATE(peripheral, rx_len, tx_len))
#if NRFX_CHECK(NRFX_SPIM0_ENABLED)
#define SPIM0_LENGTH_VALIDATE(...) SPIMX_LENGTH_VALIDATE(SPIM0, __VA_ARGS__)
#else
#define SPIM0_LENGTH_VALIDATE(...) 0
#endif
#if NRFX_CHECK(NRFX_SPIM1_ENABLED)
#define SPIM1_LENGTH_VALIDATE(...) SPIMX_LENGTH_VALIDATE(SPIM1, __VA_ARGS__)
#else
#define SPIM1_LENGTH_VALIDATE(...) 0
#endif
#if NRFX_CHECK(NRFX_SPIM2_ENABLED)
#define SPIM2_LENGTH_VALIDATE(...) SPIMX_LENGTH_VALIDATE(SPIM2, __VA_ARGS__)
#else
#define SPIM2_LENGTH_VALIDATE(...) 0
#endif
#if NRFX_CHECK(NRFX_SPIM3_ENABLED)
#define SPIM3_LENGTH_VALIDATE(...) SPIMX_LENGTH_VALIDATE(SPIM3, __VA_ARGS__)
#else
#define SPIM3_LENGTH_VALIDATE(...) 0
#endif
#define SPIM_LENGTH_VALIDATE(drv_inst_idx, rx_len, tx_len) \
(SPIM0_LENGTH_VALIDATE(drv_inst_idx, rx_len, tx_len) || \
SPIM1_LENGTH_VALIDATE(drv_inst_idx, rx_len, tx_len) || \
SPIM2_LENGTH_VALIDATE(drv_inst_idx, rx_len, tx_len) || \
SPIM3_LENGTH_VALIDATE(drv_inst_idx, rx_len, tx_len))
#if defined(NRF52840_XXAA) && (NRFX_CHECK(NRFX_SPIM3_ENABLED))
// Enable workaround for nRF52840 anomaly 195 (SPIM3 continues to draw current after disable).
#define USE_WORKAROUND_FOR_ANOMALY_195
#endif
// Control block - driver instance local data.
typedef struct
{
nrfx_spim_evt_handler_t handler;
void * p_context;
nrfx_spim_evt_t evt; // Keep the struct that is ready for event handler. Less memcpy.
nrfx_drv_state_t state;
volatile bool transfer_in_progress;
#if NRFX_CHECK(NRFX_SPIM_EXTENDED_ENABLED)
bool use_hw_ss;
#endif
// [no need for 'volatile' attribute for the following members, as they
// are not concurrently used in IRQ handlers and main line code]
bool ss_active_high;
uint8_t ss_pin;
uint8_t miso_pin;
uint8_t orc;
#if NRFX_CHECK(NRFX_SPIM_NRF52_ANOMALY_109_WORKAROUND_ENABLED)
size_t tx_length;
size_t rx_length;
#endif
} spim_control_block_t;
static spim_control_block_t m_cb[NRFX_SPIM_ENABLED_COUNT];
#if NRFX_CHECK(NRFX_SPIM3_NRF52840_ANOMALY_198_WORKAROUND_ENABLED)
// Workaround for nRF52840 anomaly 198: SPIM3 transmit data might be corrupted.
static uint32_t m_anomaly_198_preserved_value;
static void anomaly_198_enable(uint8_t const * p_buffer, size_t buf_len)
{
m_anomaly_198_preserved_value = *((volatile uint32_t *)0x40000E00);
if (buf_len == 0)
{
return;
}
uint32_t buffer_end_addr = ((uint32_t)p_buffer) + buf_len;
uint32_t block_addr = ((uint32_t)p_buffer) & ~0x1FFF;
uint32_t block_flag = (1UL << ((block_addr >> 13) & 0xFFFF));
uint32_t occupied_blocks = 0;
if (block_addr >= 0x20010000)
{
occupied_blocks = (1UL << 8);
}
else
{
do {
occupied_blocks |= block_flag;
block_flag <<= 1;
block_addr += 0x2000;
} while ((block_addr < buffer_end_addr) && (block_addr < 0x20012000));
}
*((volatile uint32_t *)0x40000E00) = occupied_blocks;
}
static void anomaly_198_disable(void)
{
*((volatile uint32_t *)0x40000E00) = m_anomaly_198_preserved_value;
}
#endif // NRFX_CHECK(NRFX_SPIM3_NRF52840_ANOMALY_198_WORKAROUND_ENABLED)
nrfx_err_t nrfx_spim_init(nrfx_spim_t const * const p_instance,
nrfx_spim_config_t const * p_config,
nrfx_spim_evt_handler_t handler,
void * p_context)
{
NRFX_ASSERT(p_config);
spim_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
nrfx_err_t err_code;
if (p_cb->state != NRFX_DRV_STATE_UNINITIALIZED)
{
err_code = NRFX_ERROR_INVALID_STATE;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
#if NRFX_CHECK(NRFX_SPIM_EXTENDED_ENABLED)
// Currently, only SPIM3 in nRF52840 supports the extended features.
// Other instances must be checked.
if ((p_instance->drv_inst_idx != NRFX_SPIM3_INST_IDX) &&
((p_config->dcx_pin != NRFX_SPIM_PIN_NOT_USED) ||
(p_config->frequency == NRF_SPIM_FREQ_16M) ||
(p_config->frequency == NRF_SPIM_FREQ_32M) ||
(p_config->use_hw_ss)))
{
err_code = NRFX_ERROR_NOT_SUPPORTED;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
#endif
NRF_SPIM_Type * p_spim = (NRF_SPIM_Type *)p_instance->p_reg;
#if NRFX_CHECK(NRFX_PRS_ENABLED)
static nrfx_irq_handler_t const irq_handlers[NRFX_SPIM_ENABLED_COUNT] = {
#if NRFX_CHECK(NRFX_SPIM0_ENABLED)
nrfx_spim_0_irq_handler,
#endif
#if NRFX_CHECK(NRFX_SPIM1_ENABLED)
nrfx_spim_1_irq_handler,
#endif
#if NRFX_CHECK(NRFX_SPIM2_ENABLED)
nrfx_spim_2_irq_handler,
#endif
#if NRFX_CHECK(NRFX_SPIM3_ENABLED)
nrfx_spim_3_irq_handler,
#endif
};
if (nrfx_prs_acquire(p_instance->p_reg,
irq_handlers[p_instance->drv_inst_idx]) != NRFX_SUCCESS)
{
err_code = NRFX_ERROR_BUSY;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
#endif // NRFX_CHECK(NRFX_PRS_ENABLED)
p_cb->handler = handler;
p_cb->p_context = p_context;
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_SPIM_MODE_1)
{
nrf_gpio_pin_clear(p_config->sck_pin);
}
else
{
nrf_gpio_pin_set(p_config->sck_pin);
}
nrf_gpio_cfg(p_config->sck_pin,
NRF_GPIO_PIN_DIR_OUTPUT,
NRF_GPIO_PIN_INPUT_CONNECT,
NRF_GPIO_PIN_NOPULL,
NRF_GPIO_PIN_S0S1,
NRF_GPIO_PIN_NOSENSE);
// - MOSI (optional) - output with initial value 0,
if (p_config->mosi_pin != NRFX_SPIM_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_SPIM_PIN_NOT_CONNECTED;
}
// - MISO (optional) - input,
if (p_config->miso_pin != NRFX_SPIM_PIN_NOT_USED)
{
miso_pin = p_config->miso_pin;
nrf_gpio_cfg_input(miso_pin, (nrf_gpio_pin_pull_t)NRFX_SPIM_MISO_PULL_CFG);
}
else
{
miso_pin = NRF_SPIM_PIN_NOT_CONNECTED;
}
p_cb->miso_pin = p_config->miso_pin;
// - Slave Select (optional) - output with initial value 1 (inactive).
// 'p_cb->ss_pin' variable is used during transfers to check if SS pin should be toggled,
// so this field needs to be initialized even if the pin is not used.
p_cb->ss_pin = p_config->ss_pin;
if (p_config->ss_pin != NRFX_SPIM_PIN_NOT_USED)
{
if (p_config->ss_active_high)
{
nrf_gpio_pin_clear(p_config->ss_pin);
}
else
{
nrf_gpio_pin_set(p_config->ss_pin);
}
nrf_gpio_cfg_output(p_config->ss_pin);
#if NRFX_CHECK(NRFX_SPIM_EXTENDED_ENABLED)
if (p_config->use_hw_ss)
{
p_cb->use_hw_ss = p_config->use_hw_ss;
nrf_spim_csn_configure(p_spim,
p_config->ss_pin,
(p_config->ss_active_high == true ?
NRF_SPIM_CSN_POL_HIGH : NRF_SPIM_CSN_POL_LOW),
p_config->ss_duration);
}
#endif
p_cb->ss_active_high = p_config->ss_active_high;
}
#if NRFX_CHECK(NRFX_SPIM_EXTENDED_ENABLED)
// - DCX (optional) - output.
if (p_config->dcx_pin != NRFX_SPIM_PIN_NOT_USED)
{
nrf_gpio_pin_set(p_config->dcx_pin);
nrf_gpio_cfg_output(p_config->dcx_pin);
nrf_spim_dcx_pin_set(p_spim, p_config->dcx_pin);
}
// Change rx delay
nrf_spim_iftiming_set(p_spim, p_config->rx_delay);
#endif
nrf_spim_pins_set(p_spim, p_config->sck_pin, mosi_pin, miso_pin);
nrf_spim_frequency_set(p_spim, p_config->frequency);
nrf_spim_configure(p_spim, p_config->mode, p_config->bit_order);
nrf_spim_orc_set(p_spim, p_config->orc);
nrf_spim_enable(p_spim);
if (p_cb->handler)
{
NRFX_IRQ_PRIORITY_SET(nrfx_get_irq_number(p_instance->p_reg),
p_config->irq_priority);
NRFX_IRQ_ENABLE(nrfx_get_irq_number(p_instance->p_reg));
}
p_cb->transfer_in_progress = false;
p_cb->state = NRFX_DRV_STATE_INITIALIZED;
err_code = NRFX_SUCCESS;
NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
void nrfx_spim_uninit(nrfx_spim_t const * const p_instance)
{
spim_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
NRFX_ASSERT(p_cb->state != NRFX_DRV_STATE_UNINITIALIZED);
if (p_cb->handler)
{
NRFX_IRQ_DISABLE(nrfx_get_irq_number(p_instance->p_reg));
}
NRF_SPIM_Type * p_spim = (NRF_SPIM_Type *)p_instance->p_reg;
if (p_cb->handler)
{
nrf_spim_int_disable(p_spim, NRF_SPIM_ALL_INTS_MASK);
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;
}
}
if (p_cb->miso_pin != NRFX_SPIM_PIN_NOT_USED)
{
nrf_gpio_cfg_default(p_cb->miso_pin);
}
nrf_spim_disable(p_spim);
#ifdef USE_WORKAROUND_FOR_ANOMALY_195
if (p_spim == NRF_SPIM3)
{
*(volatile uint32_t *)0x4002F004 = 1;
}
#endif
#if NRFX_CHECK(NRFX_PRS_ENABLED)
nrfx_prs_release(p_instance->p_reg);
#endif
p_cb->state = NRFX_DRV_STATE_UNINITIALIZED;
}
#if NRFX_CHECK(NRFX_SPIM_EXTENDED_ENABLED)
nrfx_err_t nrfx_spim_xfer_dcx(nrfx_spim_t const * const p_instance,
nrfx_spim_xfer_desc_t const * p_xfer_desc,
uint32_t flags,
uint8_t cmd_length)
{
NRFX_ASSERT(cmd_length <= NRF_SPIM_DCX_CNT_ALL_CMD);
nrf_spim_dcx_cnt_set((NRF_SPIM_Type *)p_instance->p_reg, cmd_length);
return nrfx_spim_xfer(p_instance, p_xfer_desc, 0);
}
#endif
static void finish_transfer(spim_control_block_t * p_cb)
{
// If Slave Select signal is used, this is the time to deactivate it.
if (p_cb->ss_pin != NRFX_SPIM_PIN_NOT_USED)
{
#if NRFX_CHECK(NRFX_SPIM_EXTENDED_ENABLED)
if (!p_cb->use_hw_ss)
#endif
{
if (p_cb->ss_active_high)
{
nrf_gpio_pin_clear(p_cb->ss_pin);
}
else
{
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 = NRFX_SPIM_EVENT_DONE;
p_cb->handler(&p_cb->evt, p_cb->p_context);
}
__STATIC_INLINE void spim_int_enable(NRF_SPIM_Type * p_spim, bool enable)
{
if (!enable)
{
nrf_spim_int_disable(p_spim, NRF_SPIM_INT_END_MASK);
}
else
{
nrf_spim_int_enable(p_spim, NRF_SPIM_INT_END_MASK);
}
}
__STATIC_INLINE void spim_list_enable_handle(NRF_SPIM_Type * p_spim, uint32_t flags)
{
if (NRFX_SPIM_FLAG_TX_POSTINC & flags)
{
nrf_spim_tx_list_enable(p_spim);
}
else
{
nrf_spim_tx_list_disable(p_spim);
}
if (NRFX_SPIM_FLAG_RX_POSTINC & flags)
{
nrf_spim_rx_list_enable(p_spim);
}
else
{
nrf_spim_rx_list_disable(p_spim);
}
}
static nrfx_err_t spim_xfer(NRF_SPIM_Type * p_spim,
spim_control_block_t * p_cb,
nrfx_spim_xfer_desc_t const * p_xfer_desc,
uint32_t flags)
{
nrfx_err_t err_code;
// 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 && !nrfx_is_in_ram(p_xfer_desc->p_tx_buffer)) ||
(p_xfer_desc->p_rx_buffer != NULL && !nrfx_is_in_ram(p_xfer_desc->p_rx_buffer)))
{
p_cb->transfer_in_progress = false;
err_code = NRFX_ERROR_INVALID_ADDR;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
#if NRFX_CHECK(NRFX_SPIM_NRF52_ANOMALY_109_WORKAROUND_ENABLED)
p_cb->tx_length = 0;
p_cb->rx_length = 0;
#endif
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);
#if NRFX_CHECK(NRFX_SPIM3_NRF52840_ANOMALY_198_WORKAROUND_ENABLED)
if (p_spim == NRF_SPIM3)
{
anomaly_198_enable(p_xfer_desc->p_tx_buffer, p_xfer_desc->tx_length);
}
#endif
nrf_spim_event_clear(p_spim, NRF_SPIM_EVENT_END);
spim_list_enable_handle(p_spim, flags);
if (!(flags & NRFX_SPIM_FLAG_HOLD_XFER))
{
nrf_spim_task_trigger(p_spim, NRF_SPIM_TASK_START);
}
#if NRFX_CHECK(NRFX_SPIM_NRF52_ANOMALY_109_WORKAROUND_ENABLED)
if (flags & NRFX_SPIM_FLAG_HOLD_XFER)
{
nrf_spim_event_clear(p_spim, NRF_SPIM_EVENT_STARTED);
p_cb->tx_length = p_xfer_desc->tx_length;
p_cb->rx_length = p_xfer_desc->rx_length;
nrf_spim_tx_buffer_set(p_spim, p_xfer_desc->p_tx_buffer, 0);
nrf_spim_rx_buffer_set(p_spim, p_xfer_desc->p_rx_buffer, 0);
nrf_spim_int_enable(p_spim, NRF_SPIM_INT_STARTED_MASK);
}
#endif
if (!p_cb->handler)
{
while (!nrf_spim_event_check(p_spim, NRF_SPIM_EVENT_END)){}
#if NRFX_CHECK(NRFX_SPIM3_NRF52840_ANOMALY_198_WORKAROUND_ENABLED)
if (p_spim == NRF_SPIM3)
{
anomaly_198_disable();
}
#endif
if (p_cb->ss_pin != NRFX_SPIM_PIN_NOT_USED)
{
#if NRFX_CHECK(NRFX_SPIM_EXTENDED_ENABLED)
if (!p_cb->use_hw_ss)
#endif
{
if (p_cb->ss_active_high)
{
nrf_gpio_pin_clear(p_cb->ss_pin);
}
else
{
nrf_gpio_pin_set(p_cb->ss_pin);
}
}
}
}
else
{
spim_int_enable(p_spim, !(flags & NRFX_SPIM_FLAG_NO_XFER_EVT_HANDLER));
}
err_code = NRFX_SUCCESS;
NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
nrfx_err_t nrfx_spim_xfer(nrfx_spim_t const * const p_instance,
nrfx_spim_xfer_desc_t const * p_xfer_desc,
uint32_t flags)
{
spim_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
NRFX_ASSERT(p_cb->state != NRFX_DRV_STATE_UNINITIALIZED);
NRFX_ASSERT(p_xfer_desc->p_tx_buffer != NULL || p_xfer_desc->tx_length == 0);
NRFX_ASSERT(p_xfer_desc->p_rx_buffer != NULL || p_xfer_desc->rx_length == 0);
NRFX_ASSERT(SPIM_LENGTH_VALIDATE(p_instance->drv_inst_idx,
p_xfer_desc->rx_length,
p_xfer_desc->tx_length));
nrfx_err_t err_code = NRFX_SUCCESS;
if (p_cb->transfer_in_progress)
{
err_code = NRFX_ERROR_BUSY;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
else
{
if (p_cb->handler && !(flags & (NRFX_SPIM_FLAG_REPEATED_XFER |
NRFX_SPIM_FLAG_NO_XFER_EVT_HANDLER)))
{
p_cb->transfer_in_progress = true;
}
}
p_cb->evt.xfer_desc = *p_xfer_desc;
if (p_cb->ss_pin != NRFX_SPIM_PIN_NOT_USED)
{
#if NRFX_CHECK(NRFX_SPIM_EXTENDED_ENABLED)
if (!p_cb->use_hw_ss)
#endif
{
if (p_cb->ss_active_high)
{
nrf_gpio_pin_set(p_cb->ss_pin);
}
else
{
nrf_gpio_pin_clear(p_cb->ss_pin);
}
}
}
return spim_xfer(p_instance->p_reg, p_cb, p_xfer_desc, flags);
}
void nrfx_spim_abort(nrfx_spim_t const * p_instance)
{
spim_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
NRFX_ASSERT(p_cb->state != NRFX_DRV_STATE_UNINITIALIZED);
nrf_spim_task_trigger(p_instance->p_reg, NRF_SPIM_TASK_STOP);
while (!nrf_spim_event_check(p_instance->p_reg, NRF_SPIM_EVENT_STOPPED))
{}
p_cb->transfer_in_progress = false;
}
uint32_t nrfx_spim_start_task_get(nrfx_spim_t const * p_instance)
{
NRF_SPIM_Type * p_spim = (NRF_SPIM_Type *)p_instance->p_reg;
return nrf_spim_task_address_get(p_spim, NRF_SPIM_TASK_START);
}
uint32_t nrfx_spim_end_event_get(nrfx_spim_t const * p_instance)
{
NRF_SPIM_Type * p_spim = (NRF_SPIM_Type *)p_instance->p_reg;
return nrf_spim_event_address_get(p_spim, NRF_SPIM_EVENT_END);
}
static void irq_handler(NRF_SPIM_Type * p_spim, spim_control_block_t * p_cb)
{
#if NRFX_CHECK(NRFX_SPIM_NRF52_ANOMALY_109_WORKAROUND_ENABLED)
if ((nrf_spim_int_enable_check(p_spim, NRF_SPIM_INT_STARTED_MASK)) &&
(nrf_spim_event_check(p_spim, NRF_SPIM_EVENT_STARTED)) )
{
/* Handle first, zero-length, auxiliary transmission. */
nrf_spim_event_clear(p_spim, NRF_SPIM_EVENT_STARTED);
nrf_spim_event_clear(p_spim, NRF_SPIM_EVENT_END);
NRFX_ASSERT(p_spim->TXD.MAXCNT == 0);
p_spim->TXD.MAXCNT = p_cb->tx_length;
NRFX_ASSERT(p_spim->RXD.MAXCNT == 0);
p_spim->RXD.MAXCNT = p_cb->rx_length;
/* Disable STARTED interrupt, used only in auxiliary transmission. */
nrf_spim_int_disable(p_spim, NRF_SPIM_INT_STARTED_MASK);
/* Start the actual, glitch-free transmission. */
nrf_spim_task_trigger(p_spim, NRF_SPIM_TASK_START);
return;
}
#endif
if (nrf_spim_event_check(p_spim, NRF_SPIM_EVENT_END))
{
#if NRFX_CHECK(NRFX_SPIM3_NRF52840_ANOMALY_198_WORKAROUND_ENABLED)
if (p_spim == NRF_SPIM3)
{
anomaly_198_disable();
}
#endif
nrf_spim_event_clear(p_spim, NRF_SPIM_EVENT_END);
NRFX_ASSERT(p_cb->handler);
NRFX_LOG_DEBUG("Event: NRF_SPIM_EVENT_END.");
finish_transfer(p_cb);
}
}
#if NRFX_CHECK(NRFX_SPIM0_ENABLED)
void nrfx_spim_0_irq_handler(void)
{
irq_handler(NRF_SPIM0, &m_cb[NRFX_SPIM0_INST_IDX]);
}
#endif
#if NRFX_CHECK(NRFX_SPIM1_ENABLED)
void nrfx_spim_1_irq_handler(void)
{
irq_handler(NRF_SPIM1, &m_cb[NRFX_SPIM1_INST_IDX]);
}
#endif
#if NRFX_CHECK(NRFX_SPIM2_ENABLED)
void nrfx_spim_2_irq_handler(void)
{
irq_handler(NRF_SPIM2, &m_cb[NRFX_SPIM2_INST_IDX]);
}
#endif
#if NRFX_CHECK(NRFX_SPIM3_ENABLED)
void nrfx_spim_3_irq_handler(void)
{
irq_handler(NRF_SPIM3, &m_cb[NRFX_SPIM3_INST_IDX]);
}
#endif
#endif // NRFX_CHECK(NRFX_SPIM_ENABLED)
+330
View File
@@ -0,0 +1,330 @@
/**
* Copyright (c) 2015 - 2021, 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, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, 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 its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA 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.
*
*/
#include <nrfx.h>
#if NRFX_CHECK(NRFX_TIMER_ENABLED)
#if !(NRFX_CHECK(NRFX_TIMER0_ENABLED) || NRFX_CHECK(NRFX_TIMER1_ENABLED) || \
NRFX_CHECK(NRFX_TIMER2_ENABLED) || NRFX_CHECK(NRFX_TIMER3_ENABLED) || \
NRFX_CHECK(NRFX_TIMER4_ENABLED))
#error "No enabled TIMER instances. Check <nrfx_config.h>."
#endif
#include <nrfx_timer.h>
#define NRFX_LOG_MODULE TIMER
#include <nrfx_log.h>
/**@brief Timer control block. */
typedef struct
{
nrfx_timer_event_handler_t handler;
void * context;
nrfx_drv_state_t state;
} timer_control_block_t;
static timer_control_block_t m_cb[NRFX_TIMER_ENABLED_COUNT];
nrfx_err_t nrfx_timer_init(nrfx_timer_t const * const p_instance,
nrfx_timer_config_t const * p_config,
nrfx_timer_event_handler_t timer_event_handler)
{
timer_control_block_t * p_cb = &m_cb[p_instance->instance_id];
#ifdef SOFTDEVICE_PRESENT
NRFX_ASSERT(p_instance->p_reg != NRF_TIMER0);
#endif
NRFX_ASSERT(p_config);
NRFX_ASSERT(timer_event_handler);
nrfx_err_t err_code;
if (p_cb->state != NRFX_DRV_STATE_UNINITIALIZED)
{
err_code = NRFX_ERROR_INVALID_STATE;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
/* Warning 685: Relational operator '<=' always evaluates to 'true'"
* Warning in NRF_TIMER_IS_BIT_WIDTH_VALID macro. Macro validate timers resolution.
* Not necessary in nRF52 based systems. Obligatory in nRF51 based systems.
*/
/*lint -save -e685 */
NRFX_ASSERT(NRF_TIMER_IS_BIT_WIDTH_VALID(p_instance->p_reg, p_config->bit_width));
//lint -restore
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));
}
NRFX_IRQ_PRIORITY_SET(nrfx_get_irq_number(p_instance->p_reg),
p_config->interrupt_priority);
NRFX_IRQ_ENABLE(nrfx_get_irq_number(p_instance->p_reg));
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 = NRFX_DRV_STATE_INITIALIZED;
err_code = NRFX_SUCCESS;
NRFX_LOG_INFO("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
void nrfx_timer_uninit(nrfx_timer_t const * const p_instance)
{
NRFX_IRQ_DISABLE(nrfx_get_irq_number(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
nrfx_timer_disable(p_instance);
m_cb[p_instance->instance_id].state = NRFX_DRV_STATE_UNINITIALIZED;
NRFX_LOG_INFO("Uninitialized instance: %d.", p_instance->instance_id);
}
void nrfx_timer_enable(nrfx_timer_t const * const p_instance)
{
NRFX_ASSERT(m_cb[p_instance->instance_id].state == NRFX_DRV_STATE_INITIALIZED);
nrf_timer_task_trigger(p_instance->p_reg, NRF_TIMER_TASK_START);
m_cb[p_instance->instance_id].state = NRFX_DRV_STATE_POWERED_ON;
NRFX_LOG_INFO("Enabled instance: %d.", p_instance->instance_id);
}
void nrfx_timer_disable(nrfx_timer_t const * const p_instance)
{
NRFX_ASSERT(m_cb[p_instance->instance_id].state != NRFX_DRV_STATE_UNINITIALIZED);
nrf_timer_task_trigger(p_instance->p_reg, NRF_TIMER_TASK_SHUTDOWN);
m_cb[p_instance->instance_id].state = NRFX_DRV_STATE_INITIALIZED;
NRFX_LOG_INFO("Disabled instance: %d.", p_instance->instance_id);
}
bool nrfx_timer_is_enabled(nrfx_timer_t const * const p_instance)
{
NRFX_ASSERT(m_cb[p_instance->instance_id].state != NRFX_DRV_STATE_UNINITIALIZED);
return (m_cb[p_instance->instance_id].state == NRFX_DRV_STATE_POWERED_ON);
}
void nrfx_timer_resume(nrfx_timer_t const * const p_instance)
{
NRFX_ASSERT(m_cb[p_instance->instance_id].state != NRFX_DRV_STATE_UNINITIALIZED);
nrf_timer_task_trigger(p_instance->p_reg, NRF_TIMER_TASK_START);
NRFX_LOG_INFO("Resumed instance: %d.", p_instance->instance_id);
}
void nrfx_timer_pause(nrfx_timer_t const * const p_instance)
{
NRFX_ASSERT(m_cb[p_instance->instance_id].state != NRFX_DRV_STATE_UNINITIALIZED);
nrf_timer_task_trigger(p_instance->p_reg, NRF_TIMER_TASK_STOP);
NRFX_LOG_INFO("Paused instance: %d.", p_instance->instance_id);
}
void nrfx_timer_clear(nrfx_timer_t const * const p_instance)
{
NRFX_ASSERT(m_cb[p_instance->instance_id].state != NRFX_DRV_STATE_UNINITIALIZED);
nrf_timer_task_trigger(p_instance->p_reg, NRF_TIMER_TASK_CLEAR);
}
void nrfx_timer_increment(nrfx_timer_t const * const p_instance)
{
NRFX_ASSERT(m_cb[p_instance->instance_id].state != NRFX_DRV_STATE_UNINITIALIZED);
NRFX_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 nrfx_timer_capture(nrfx_timer_t const * const p_instance,
nrf_timer_cc_channel_t cc_channel)
{
NRFX_ASSERT(m_cb[p_instance->instance_id].state != NRFX_DRV_STATE_UNINITIALIZED);
NRFX_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 nrfx_timer_compare(nrfx_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_event_clear(p_instance->p_reg, nrf_timer_compare_event_get(cc_channel));
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);
NRFX_LOG_INFO("Timer id: %d, capture value set: %lu, channel: %d.",
p_instance->instance_id,
cc_value,
cc_channel);
}
void nrfx_timer_extended_compare(nrfx_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);
nrfx_timer_compare(p_instance,
cc_channel,
cc_value,
enable_int);
NRFX_LOG_INFO("Timer id: %d, capture value set: %lu, channel: %d.",
p_instance->instance_id,
cc_value,
cc_channel);
}
void nrfx_timer_compare_int_enable(nrfx_timer_t const * const p_instance,
uint32_t channel)
{
NRFX_ASSERT(m_cb[p_instance->instance_id].state != NRFX_DRV_STATE_UNINITIALIZED);
NRFX_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 nrfx_timer_compare_int_disable(nrfx_timer_t const * const p_instance,
uint32_t channel)
{
NRFX_ASSERT(m_cb[p_instance->instance_id].state != NRFX_DRV_STATE_UNINITIALIZED);
NRFX_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);
NRFX_LOG_DEBUG("Compare event, channel: %d.", i);
p_cb->handler(event, p_cb->context);
}
}
}
#if NRFX_CHECK(NRFX_TIMER0_ENABLED)
void nrfx_timer_0_irq_handler(void)
{
irq_handler(NRF_TIMER0, &m_cb[NRFX_TIMER0_INST_IDX],
NRF_TIMER_CC_CHANNEL_COUNT(0));
}
#endif
#if NRFX_CHECK(NRFX_TIMER1_ENABLED)
void nrfx_timer_1_irq_handler(void)
{
irq_handler(NRF_TIMER1, &m_cb[NRFX_TIMER1_INST_IDX],
NRF_TIMER_CC_CHANNEL_COUNT(1));
}
#endif
#if NRFX_CHECK(NRFX_TIMER2_ENABLED)
void nrfx_timer_2_irq_handler(void)
{
irq_handler(NRF_TIMER2, &m_cb[NRFX_TIMER2_INST_IDX],
NRF_TIMER_CC_CHANNEL_COUNT(2));
}
#endif
#if NRFX_CHECK(NRFX_TIMER3_ENABLED)
void nrfx_timer_3_irq_handler(void)
{
irq_handler(NRF_TIMER3, &m_cb[NRFX_TIMER3_INST_IDX],
NRF_TIMER_CC_CHANNEL_COUNT(3));
}
#endif
#if NRFX_CHECK(NRFX_TIMER4_ENABLED)
void nrfx_timer_4_irq_handler(void)
{
irq_handler(NRF_TIMER4, &m_cb[NRFX_TIMER4_INST_IDX],
NRF_TIMER_CC_CHANNEL_COUNT(4));
}
#endif
#endif // NRFX_CHECK(NRFX_TIMER_ENABLED)
+783
View File
@@ -0,0 +1,783 @@
/**
* Copyright (c) 2015 - 2021, 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, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, 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 its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA 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.
*
*/
#include <nrfx.h>
#if NRFX_CHECK(NRFX_TWI_ENABLED)
#if !(NRFX_CHECK(NRFX_TWI0_ENABLED) || NRFX_CHECK(NRFX_TWI1_ENABLED))
#error "No enabled TWI instances. Check <nrfx_config.h>."
#endif
#include <nrfx_twi.h>
#include <hal/nrf_gpio.h>
#include "prs/nrfx_prs.h"
#define NRFX_LOG_MODULE TWI
#include <nrfx_log.h>
#define EVT_TO_STR(event) \
(event == NRFX_TWI_EVT_DONE ? "EVT_DONE" : \
(event == NRFX_TWI_EVT_ADDRESS_NACK ? "EVT_ADDRESS_NACK" : \
(event == NRFX_TWI_EVT_DATA_NACK ? "EVT_DATA_NACK" : \
(event == NRFX_TWI_EVT_OVERRUN ? "EVT_OVERRUN" : \
(event == NRFX_TWI_EVT_BUS_ERROR ? "EVT_BUS_ERROR" : \
"UNKNOWN ERROR")))))
#define EVT_TO_STR_TWI(event) \
(event == NRF_TWI_EVENT_STOPPED ? "NRF_TWI_EVENT_STOPPED" : \
(event == NRF_TWI_EVENT_RXDREADY ? "NRF_TWI_EVENT_RXDREADY" : \
(event == NRF_TWI_EVENT_TXDSENT ? "NRF_TWI_EVENT_TXDSENT" : \
(event == NRF_TWI_EVENT_ERROR ? "NRF_TWI_EVENT_ERROR" : \
(event == NRF_TWI_EVENT_BB ? "NRF_TWI_EVENT_BB" : \
(event == NRF_TWI_EVENT_SUSPENDED ? "NRF_TWI_EVENT_SUSPENDED" : \
"UNKNOWN ERROR"))))))
#define TRANSFER_TO_STR(type) \
(type == NRFX_TWI_XFER_TX ? "XFER_TX" : \
(type == NRFX_TWI_XFER_RX ? "XFER_RX" : \
(type == NRFX_TWI_XFER_TXRX ? "XFER_TXRX" : \
(type == NRFX_TWI_XFER_TXTX ? "XFER_TXTX" : \
"UNKNOWN TRANSFER TYPE"))))
#define TWI_PIN_INIT(_pin) nrf_gpio_cfg((_pin), \
NRF_GPIO_PIN_DIR_INPUT, \
NRF_GPIO_PIN_INPUT_CONNECT, \
NRF_GPIO_PIN_PULLUP, \
NRF_GPIO_PIN_S0D1, \
NRF_GPIO_PIN_NOSENSE)
#define TWI_FLAG_NO_STOP(flags) (flags & NRFX_TWI_FLAG_TX_NO_STOP)
#define TWI_FLAG_SUSPEND(flags) (flags & NRFX_TWI_FLAG_SUSPEND)
#define TWI_FLAG_NO_HANDLER_IN_USE(flags) (flags & NRFX_TWI_FLAG_NO_XFER_EVT_HANDLER)
#define HW_TIMEOUT 100000
/* TWI master driver suspend types. */
typedef enum
{
TWI_NO_SUSPEND, //< Last transfer was not suspended.
TWI_SUSPEND_TX, //< Last transfer was TX and was suspended.
TWI_SUSPEND_RX //< Last transfer was RX and was suspended.
} twi_suspend_t;
// Control block - driver instance local data.
typedef struct
{
nrfx_twi_evt_handler_t handler;
void * p_context;
volatile uint32_t int_mask;
nrfx_twi_xfer_desc_t xfer_desc;
uint32_t flags;
uint8_t * p_curr_buf;
size_t curr_length;
bool curr_tx_no_stop;
twi_suspend_t prev_suspend;
nrfx_drv_state_t state;
bool error;
volatile bool busy;
bool repeated;
size_t bytes_transferred;
bool hold_bus_uninit;
} twi_control_block_t;
static twi_control_block_t m_cb[NRFX_TWI_ENABLED_COUNT];
static nrfx_err_t twi_process_error(uint32_t errorsrc)
{
nrfx_err_t ret = NRFX_ERROR_INTERNAL;
if (errorsrc & NRF_TWI_ERROR_OVERRUN)
{
ret = NRFX_ERROR_DRV_TWI_ERR_OVERRUN;
}
if (errorsrc & NRF_TWI_ERROR_ADDRESS_NACK)
{
ret = NRFX_ERROR_DRV_TWI_ERR_ANACK;
}
if (errorsrc & NRF_TWI_ERROR_DATA_NACK)
{
ret = NRFX_ERROR_DRV_TWI_ERR_DNACK;
}
return ret;
}
static bool xfer_completeness_check(NRF_TWI_Type * p_twi, twi_control_block_t const * p_cb)
{
// If the actual number of transferred bytes is not equal to what was requested,
// but there was no error signaled by the peripheral, this means that something
// unexpected, like a premature STOP condition, was received on the bus.
// In such case the peripheral has to be disabled and re-enabled, so that its
// internal state machine is reinitialized.
if (p_cb->bytes_transferred != p_cb->curr_length)
{
nrf_twi_disable(p_twi);
nrf_twi_enable(p_twi);
return false;
}
else
{
return true;
}
}
nrfx_err_t nrfx_twi_init(nrfx_twi_t const * p_instance,
nrfx_twi_config_t const * p_config,
nrfx_twi_evt_handler_t event_handler,
void * p_context)
{
NRFX_ASSERT(p_config);
NRFX_ASSERT(p_config->scl != p_config->sda);
twi_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
nrfx_err_t err_code;
if (p_cb->state != NRFX_DRV_STATE_UNINITIALIZED)
{
err_code = NRFX_ERROR_INVALID_STATE;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
#if NRFX_CHECK(NRFX_PRS_ENABLED)
static nrfx_irq_handler_t const irq_handlers[NRFX_TWI_ENABLED_COUNT] = {
#if NRFX_CHECK(NRFX_TWI0_ENABLED)
nrfx_twi_0_irq_handler,
#endif
#if NRFX_CHECK(NRFX_TWI1_ENABLED)
nrfx_twi_1_irq_handler,
#endif
};
if (nrfx_prs_acquire(p_instance->p_twi,
irq_handlers[p_instance->drv_inst_idx]) != NRFX_SUCCESS)
{
err_code = NRFX_ERROR_BUSY;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
#endif // NRFX_CHECK(NRFX_PRS_ENABLED)
p_cb->handler = event_handler;
p_cb->p_context = p_context;
p_cb->int_mask = 0;
p_cb->prev_suspend = TWI_NO_SUSPEND;
p_cb->repeated = false;
p_cb->busy = false;
p_cb->hold_bus_uninit = p_config->hold_bus_uninit;
/* 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.
*/
TWI_PIN_INIT(p_config->scl);
TWI_PIN_INIT(p_config->sda);
NRF_TWI_Type * p_twi = p_instance->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)
{
NRFX_IRQ_PRIORITY_SET(nrfx_get_irq_number(p_instance->p_twi),
p_config->interrupt_priority);
NRFX_IRQ_ENABLE(nrfx_get_irq_number(p_instance->p_twi));
}
p_cb->state = NRFX_DRV_STATE_INITIALIZED;
err_code = NRFX_SUCCESS;
NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
void nrfx_twi_uninit(nrfx_twi_t const * p_instance)
{
twi_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
NRFX_ASSERT(p_cb->state != NRFX_DRV_STATE_UNINITIALIZED);
if (p_cb->handler)
{
NRFX_IRQ_DISABLE(nrfx_get_irq_number(p_instance->p_twi));
}
nrfx_twi_disable(p_instance);
#if NRFX_CHECK(NRFX_PRS_ENABLED)
nrfx_prs_release(p_instance->p_twi);
#endif
if (!p_cb->hold_bus_uninit)
{
nrf_gpio_cfg_default(nrf_twi_scl_pin_get(p_instance->p_twi));
nrf_gpio_cfg_default(nrf_twi_sda_pin_get(p_instance->p_twi));
}
p_cb->state = NRFX_DRV_STATE_UNINITIALIZED;
NRFX_LOG_INFO("Instance uninitialized: %d.", p_instance->drv_inst_idx);
}
void nrfx_twi_enable(nrfx_twi_t const * p_instance)
{
twi_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
NRFX_ASSERT(p_cb->state == NRFX_DRV_STATE_INITIALIZED);
NRF_TWI_Type * p_twi = p_instance->p_twi;
nrf_twi_enable(p_twi);
p_cb->state = NRFX_DRV_STATE_POWERED_ON;
NRFX_LOG_INFO("Instance enabled: %d.", p_instance->drv_inst_idx);
}
void nrfx_twi_disable(nrfx_twi_t const * p_instance)
{
twi_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
NRFX_ASSERT(p_cb->state != NRFX_DRV_STATE_UNINITIALIZED);
NRF_TWI_Type * p_twi = p_instance->p_twi;
nrf_twi_int_disable(p_twi, NRF_TWI_ALL_INTS_MASK);
nrf_twi_shorts_disable(p_twi, NRF_TWI_ALL_SHORTS_MASK);
nrf_twi_disable(p_twi);
p_cb->state = NRFX_DRV_STATE_INITIALIZED;
NRFX_LOG_INFO("Instance disabled: %d.", p_instance->drv_inst_idx);
}
static bool twi_send_byte(NRF_TWI_Type * p_twi,
twi_control_block_t * p_cb)
{
if (p_cb->bytes_transferred < p_cb->curr_length)
{
nrf_twi_txd_set(p_twi, p_cb->p_curr_buf[p_cb->bytes_transferred]);
}
else
{
if (p_cb->curr_tx_no_stop)
{
nrf_twi_task_trigger(p_twi, NRF_TWI_TASK_SUSPEND);
return false;
}
else if(TWI_FLAG_SUSPEND(p_cb->flags))
{
nrf_twi_task_trigger(p_twi, NRF_TWI_TASK_SUSPEND);
p_cb->prev_suspend = TWI_SUSPEND_TX;
return false;
}
else
{
nrf_twi_task_trigger(p_twi, NRF_TWI_TASK_STOP);
}
}
return true;
}
static bool twi_receive_byte(NRF_TWI_Type * p_twi,
twi_control_block_t * p_cb)
{
if (p_cb->bytes_transferred < p_cb->curr_length)
{
p_cb->p_curr_buf[p_cb->bytes_transferred] = nrf_twi_rxd_get(p_twi);
++(p_cb->bytes_transferred);
if ((p_cb->bytes_transferred == p_cb->curr_length - 1) && (!TWI_FLAG_SUSPEND(p_cb->flags)))
{
nrf_twi_shorts_set(p_twi, NRF_TWI_SHORT_BB_STOP_MASK);
}
else if (p_cb->bytes_transferred == p_cb->curr_length && (!TWI_FLAG_SUSPEND(p_cb->flags)))
{
return true;
}
else if (p_cb->bytes_transferred == p_cb->curr_length && TWI_FLAG_SUSPEND(p_cb->flags))
{
p_cb->prev_suspend = TWI_SUSPEND_RX;
return false;
}
nrf_twi_task_trigger(p_twi, NRF_TWI_TASK_RESUME);
}
return true;
}
static bool twi_transfer(NRF_TWI_Type * p_twi,
twi_control_block_t * p_cb)
{
bool stopped = false;
if (nrf_twi_event_check(p_twi, NRF_TWI_EVENT_STOPPED))
{
nrf_twi_event_clear(p_twi, NRF_TWI_EVENT_STOPPED);
NRFX_LOG_DEBUG("TWI: Event: %s.", EVT_TO_STR_TWI(NRF_TWI_EVENT_STOPPED));
// Delay handling of STOPPED event till the end of events processing procedure.
// If penultimate byte is received and function gets interrupted for long enough
// after enabling BB_STOP shortcut, RXDREADY for last byte as well as STOPPED
// may be active at the same time. Therefore RXREADY has to be processed before STOPPED to
// acquire last byte before finishing transmission by returning 'false'.
stopped = true;
}
if (p_cb->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);
NRFX_LOG_DEBUG("TWI: Event: %s.", EVT_TO_STR_TWI(NRF_TWI_EVENT_ERROR));
nrf_twi_task_trigger(p_twi, NRF_TWI_TASK_STOP);
p_cb->error = true;
}
else
{
if (nrf_twi_event_check(p_twi, NRF_TWI_EVENT_TXDSENT))
{
nrf_twi_event_clear(p_twi, NRF_TWI_EVENT_TXDSENT);
++(p_cb->bytes_transferred);
NRFX_LOG_DEBUG("TWI: Event: %s.", EVT_TO_STR_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);
NRFX_LOG_DEBUG("TWI: Event: %s.", EVT_TO_STR_TWI(NRF_TWI_EVENT_ERROR));
nrf_twi_task_trigger(p_twi, NRF_TWI_TASK_STOP);
p_cb->error = true;
}
else
{
if (!twi_send_byte(p_twi, p_cb))
{
return false;
}
}
}
else if (nrf_twi_event_check(p_twi, NRF_TWI_EVENT_RXDREADY))
{
nrf_twi_event_clear(p_twi, NRF_TWI_EVENT_RXDREADY);
NRFX_LOG_DEBUG("TWI: Event: %s.", EVT_TO_STR_TWI(NRF_TWI_EVENT_RXDREADY));
if (nrf_twi_event_check(p_twi, NRF_TWI_EVENT_ERROR))
{
NRFX_LOG_DEBUG("TWI: Event: %s.", EVT_TO_STR_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_cb->error = true;
}
else
{
if (!twi_receive_byte(p_twi, p_cb))
{
return false;
}
}
}
}
if (stopped)
{
p_cb->prev_suspend = TWI_NO_SUSPEND;
if (!p_cb->error)
{
p_cb->error = !xfer_completeness_check(p_twi, p_cb);
}
return false;
}
return true;
}
static nrfx_err_t twi_tx_start_transfer(NRF_TWI_Type * p_twi,
twi_control_block_t * p_cb)
{
nrfx_err_t ret_code = NRFX_SUCCESS;
volatile int32_t hw_timeout;
hw_timeout = HW_TIMEOUT;
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);
if (p_cb->prev_suspend != TWI_SUSPEND_TX)
{
nrf_twi_task_trigger(p_twi, NRF_TWI_TASK_STARTTX);
}
(void)twi_send_byte(p_twi, p_cb);
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 ((hw_timeout > 0) &&
twi_transfer(p_twi, p_cb))
{
hw_timeout--;
}
if (p_cb->error)
{
uint32_t errorsrc = nrf_twi_errorsrc_get_and_clear(p_twi);
if (errorsrc)
{
ret_code = twi_process_error(errorsrc);
}
else
{
ret_code = NRFX_ERROR_INTERNAL;
}
}
if (hw_timeout <= 0)
{
nrf_twi_disable(p_twi);
nrf_twi_enable(p_twi);
ret_code = NRFX_ERROR_INTERNAL;
}
}
return ret_code;
}
static nrfx_err_t twi_rx_start_transfer(NRF_TWI_Type * p_twi,
twi_control_block_t * p_cb)
{
nrfx_err_t ret_code = NRFX_SUCCESS;
volatile int32_t hw_timeout;
hw_timeout = HW_TIMEOUT;
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 ((p_cb->curr_length == 1) && (!TWI_FLAG_SUSPEND(p_cb->flags)))
{
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);
if (p_cb->prev_suspend != TWI_SUSPEND_RX)
{
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 ((hw_timeout > 0) &&
twi_transfer(p_twi, p_cb))
{
hw_timeout--;
}
if (p_cb->error)
{
uint32_t errorsrc = nrf_twi_errorsrc_get_and_clear(p_twi);
if (errorsrc)
{
ret_code = twi_process_error(errorsrc);
}
else
{
ret_code = NRFX_ERROR_INTERNAL;
}
}
if (hw_timeout <= 0)
{
nrf_twi_disable(p_twi);
nrf_twi_enable(p_twi);
ret_code = NRFX_ERROR_INTERNAL;
}
}
return ret_code;
}
__STATIC_INLINE nrfx_err_t twi_xfer(NRF_TWI_Type * p_twi,
twi_control_block_t * p_cb,
nrfx_twi_xfer_desc_t const * p_xfer_desc,
uint32_t flags)
{
nrfx_err_t err_code = NRFX_SUCCESS;
if ((p_cb->prev_suspend == TWI_SUSPEND_TX) && (p_xfer_desc->type == NRFX_TWI_XFER_RX))
{
/* RX is invalid after TX suspend */
return NRFX_ERROR_INVALID_STATE;
}
else if ((p_cb->prev_suspend == TWI_SUSPEND_RX) && (p_xfer_desc->type != NRFX_TWI_XFER_RX))
{
/* TX, TXRX and TXTX are invalid after RX suspend */
return NRFX_ERROR_INVALID_STATE;
}
/* Block TWI interrupts to ensure that function is not interrupted by TWI interrupt. */
nrf_twi_int_disable(p_twi, NRF_TWI_ALL_INTS_MASK);
if (p_cb->busy)
{
nrf_twi_int_enable(p_twi, p_cb->int_mask);
err_code = NRFX_ERROR_BUSY;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
else
{
p_cb->busy = (TWI_FLAG_NO_HANDLER_IN_USE(flags)) ? false : true;
}
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 != NRFX_TWI_XFER_RX)
{
p_cb->curr_tx_no_stop = ((p_xfer_desc->type == NRFX_TWI_XFER_TX) &&
!(flags & NRFX_TWI_FLAG_TX_NO_STOP)) ? false : true;
err_code = twi_tx_start_transfer(p_twi, p_cb);
}
else
{
err_code = twi_rx_start_transfer(p_twi, p_cb);
}
if (p_cb->handler == NULL)
{
p_cb->busy = false;
}
return err_code;
}
bool nrfx_twi_is_busy(nrfx_twi_t const * p_instance)
{
twi_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
return p_cb->busy;
}
nrfx_err_t nrfx_twi_xfer(nrfx_twi_t const * p_instance,
nrfx_twi_xfer_desc_t const * p_xfer_desc,
uint32_t flags)
{
nrfx_err_t err_code = NRFX_SUCCESS;
twi_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
// TXRX and TXTX transfers are supported only in non-blocking mode.
NRFX_ASSERT( !((p_cb->handler == NULL) && (p_xfer_desc->type == NRFX_TWI_XFER_TXRX)));
NRFX_ASSERT( !((p_cb->handler == NULL) && (p_xfer_desc->type == NRFX_TWI_XFER_TXTX)));
NRFX_LOG_INFO("Transfer type: %s.", TRANSFER_TO_STR(p_xfer_desc->type));
NRFX_LOG_INFO("Transfer buffers length: primary: %d, secondary: %d.",
p_xfer_desc->primary_length,
p_xfer_desc->secondary_length);
NRFX_LOG_DEBUG("Primary buffer data:");
NRFX_LOG_HEXDUMP_DEBUG(p_xfer_desc->p_primary_buf,
p_xfer_desc->primary_length * sizeof(p_xfer_desc->p_primary_buf[0]));
NRFX_LOG_DEBUG("Secondary buffer data:");
NRFX_LOG_HEXDUMP_DEBUG(p_xfer_desc->p_secondary_buf,
p_xfer_desc->secondary_length * sizeof(p_xfer_desc->p_secondary_buf[0]));
err_code = twi_xfer((NRF_TWI_Type *)p_instance->p_twi, p_cb, p_xfer_desc, flags);
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
nrfx_err_t nrfx_twi_tx(nrfx_twi_t const * p_instance,
uint8_t address,
uint8_t const * p_data,
size_t length,
bool no_stop)
{
nrfx_twi_xfer_desc_t xfer = NRFX_TWI_XFER_DESC_TX(address, (uint8_t*)p_data, length);
return nrfx_twi_xfer(p_instance, &xfer, no_stop ? NRFX_TWI_FLAG_TX_NO_STOP : 0);
}
nrfx_err_t nrfx_twi_rx(nrfx_twi_t const * p_instance,
uint8_t address,
uint8_t * p_data,
size_t length)
{
nrfx_twi_xfer_desc_t xfer = NRFX_TWI_XFER_DESC_RX(address, p_data, length);
return nrfx_twi_xfer(p_instance, &xfer, 0);
}
size_t nrfx_twi_data_count_get(nrfx_twi_t const * const p_instance)
{
return m_cb[p_instance->drv_inst_idx].bytes_transferred;
}
uint32_t nrfx_twi_stopped_event_get(nrfx_twi_t const * p_instance)
{
return (uint32_t)nrf_twi_event_address_get(p_instance->p_twi, NRF_TWI_EVENT_STOPPED);
}
static void twi_irq_handler(NRF_TWI_Type * p_twi, twi_control_block_t * p_cb)
{
NRFX_ASSERT(p_cb->handler);
if (twi_transfer(p_twi, p_cb))
{
return;
}
if (!p_cb->error &&
((p_cb->xfer_desc.type == NRFX_TWI_XFER_TXRX) ||
(p_cb->xfer_desc.type == NRFX_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_tx_no_stop = (p_cb->flags & NRFX_TWI_FLAG_TX_NO_STOP);
p_cb->prev_suspend = TWI_NO_SUSPEND;
if (p_cb->xfer_desc.type == NRFX_TWI_XFER_TXTX)
{
(void)twi_tx_start_transfer(p_twi, p_cb);
}
else
{
(void)twi_rx_start_transfer(p_twi, p_cb);
}
}
else
{
nrfx_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 = NRFX_TWI_EVT_ADDRESS_NACK;
NRFX_LOG_DEBUG("Event: %s.", EVT_TO_STR(NRFX_TWI_EVT_ADDRESS_NACK));
}
else if (errorsrc & NRF_TWI_ERROR_DATA_NACK)
{
event.type = NRFX_TWI_EVT_DATA_NACK;
NRFX_LOG_DEBUG("Event: %s.", EVT_TO_STR(NRFX_TWI_EVT_DATA_NACK));
}
else if (errorsrc & NRF_TWI_ERROR_OVERRUN)
{
event.type = NRFX_TWI_EVT_OVERRUN;
NRFX_LOG_DEBUG("Event: %s.", EVT_TO_STR(NRFX_TWI_EVT_OVERRUN));
}
else
{
event.type = NRFX_TWI_EVT_BUS_ERROR;
NRFX_LOG_DEBUG("Event: %s.", EVT_TO_STR(NRFX_TWI_EVT_BUS_ERROR));
}
}
else
{
event.type = NRFX_TWI_EVT_DONE;
NRFX_LOG_DEBUG("Event: %s.", EVT_TO_STR(NRFX_TWI_EVT_DONE));
}
p_cb->busy = false;
if (!(TWI_FLAG_NO_HANDLER_IN_USE(p_cb->flags)) || p_cb->error)
{
p_cb->handler(&event, p_cb->p_context);
}
}
}
#if NRFX_CHECK(NRFX_TWI0_ENABLED)
void nrfx_twi_0_irq_handler(void)
{
twi_irq_handler(NRF_TWI0, &m_cb[NRFX_TWI0_INST_IDX]);
}
#endif
#if NRFX_CHECK(NRFX_TWI1_ENABLED)
void nrfx_twi_1_irq_handler(void)
{
twi_irq_handler(NRF_TWI1, &m_cb[NRFX_TWI1_INST_IDX]);
}
#endif
#endif // NRFX_CHECK(NRFX_TWI_ENABLED)
+90
View File
@@ -0,0 +1,90 @@
/**
* Copyright (c) 2019 - 2021, 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, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, 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 its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA 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.
*
*/
#include <nrfx.h>
#include <nrf_gpio.h>
#define TWI_TWIM_PIN_CONFIGURE(_pin) nrf_gpio_cfg((_pin), \
NRF_GPIO_PIN_DIR_OUTPUT, \
NRF_GPIO_PIN_INPUT_CONNECT, \
NRF_GPIO_PIN_PULLUP, \
NRF_GPIO_PIN_S0D1, \
NRF_GPIO_PIN_NOSENSE)
nrfx_err_t nrfx_twi_twim_bus_recover(uint32_t scl_pin, uint32_t sda_pin)
{
nrf_gpio_pin_set(scl_pin);
nrf_gpio_pin_set(sda_pin);
TWI_TWIM_PIN_CONFIGURE(scl_pin);
TWI_TWIM_PIN_CONFIGURE(sda_pin);
NRFX_DELAY_US(4);
for (uint8_t i = 0; i < 9; i++)
{
if (nrf_gpio_pin_read(sda_pin))
{
break;
}
else
{
// Pulse CLOCK signal
nrf_gpio_pin_clear(scl_pin);
NRFX_DELAY_US(4);
nrf_gpio_pin_set(scl_pin);
NRFX_DELAY_US(4);
}
}
// Generate a STOP condition on the bus
nrf_gpio_pin_clear(sda_pin);
NRFX_DELAY_US(4);
nrf_gpio_pin_set(sda_pin);
NRFX_DELAY_US(4);
if (nrf_gpio_pin_read(sda_pin))
{
return NRFX_SUCCESS;
}
else
{
return NRFX_ERROR_INTERNAL;
}
}
+872
View File
@@ -0,0 +1,872 @@
/**
* Copyright (c) 2015 - 2021, 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, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, 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 its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA 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.
*
*/
#include <nrfx.h>
#if NRFX_CHECK(NRFX_TWIM_ENABLED)
#if !(NRFX_CHECK(NRFX_TWIM0_ENABLED) || \
NRFX_CHECK(NRFX_TWIM1_ENABLED) || \
NRFX_CHECK(NRFX_TWIM2_ENABLED) || \
NRFX_CHECK(NRFX_TWIM3_ENABLED))
#error "No enabled TWIM instances. Check <nrfx_config.h>."
#endif
#include <nrfx_twim.h>
#include <hal/nrf_gpio.h>
#include "prs/nrfx_prs.h"
#define NRFX_LOG_MODULE TWIM
#include <nrfx_log.h>
#define EVT_TO_STR(event) \
(event == NRFX_TWIM_EVT_DONE ? "EVT_DONE" : \
(event == NRFX_TWIM_EVT_ADDRESS_NACK ? "EVT_ADDRESS_NACK" : \
(event == NRFX_TWIM_EVT_DATA_NACK ? "EVT_DATA_NACK" : \
(event == NRFX_TWIM_EVT_OVERRUN ? "EVT_OVERRUN" : \
(event == NRFX_TWIM_EVT_BUS_ERROR ? "EVT_BUS_ERROR" : \
"UNKNOWN ERROR")))))
#define EVT_TO_STR_TWIM(event) \
(event == NRF_TWIM_EVENT_STOPPED ? "NRF_TWIM_EVENT_STOPPED" : \
(event == NRF_TWIM_EVENT_ERROR ? "NRF_TWIM_EVENT_ERROR" : \
(event == NRF_TWIM_EVENT_SUSPENDED ? "NRF_TWIM_EVENT_SUSPENDED" : \
(event == NRF_TWIM_EVENT_RXSTARTED ? "NRF_TWIM_EVENT_RXSTARTED" : \
(event == NRF_TWIM_EVENT_TXSTARTED ? "NRF_TWIM_EVENT_TXSTARTED" : \
(event == NRF_TWIM_EVENT_LASTRX ? "NRF_TWIM_EVENT_LASTRX" : \
(event == NRF_TWIM_EVENT_LASTTX ? "NRF_TWIM_EVENT_LASTTX" : \
"UNKNOWN ERROR")))))))
#define TRANSFER_TO_STR(type) \
(type == NRFX_TWIM_XFER_TX ? "XFER_TX" : \
(type == NRFX_TWIM_XFER_RX ? "XFER_RX" : \
(type == NRFX_TWIM_XFER_TXRX ? "XFER_TXRX" : \
(type == NRFX_TWIM_XFER_TXTX ? "XFER_TXTX" : \
"UNKNOWN TRANSFER TYPE"))))
#define TWIM_PIN_INIT(_pin) nrf_gpio_cfg((_pin), \
NRF_GPIO_PIN_DIR_INPUT, \
NRF_GPIO_PIN_INPUT_CONNECT, \
NRF_GPIO_PIN_PULLUP, \
NRF_GPIO_PIN_S0D1, \
NRF_GPIO_PIN_NOSENSE)
#define TWIMX_LENGTH_VALIDATE(peripheral, drv_inst_idx, len1, len2) \
(((drv_inst_idx) == NRFX_CONCAT_3(NRFX_, peripheral, _INST_IDX)) && \
NRFX_EASYDMA_LENGTH_VALIDATE(peripheral, len1, len2))
#if NRFX_CHECK(NRFX_TWIM0_ENABLED)
#define TWIM0_LENGTH_VALIDATE(...) TWIMX_LENGTH_VALIDATE(TWIM0, __VA_ARGS__)
#else
#define TWIM0_LENGTH_VALIDATE(...) 0
#endif
#if NRFX_CHECK(NRFX_TWIM1_ENABLED)
#define TWIM1_LENGTH_VALIDATE(...) TWIMX_LENGTH_VALIDATE(TWIM1, __VA_ARGS__)
#else
#define TWIM1_LENGTH_VALIDATE(...) 0
#endif
#if NRFX_CHECK(NRFX_TWIM2_ENABLED)
#define TWIM2_LENGTH_VALIDATE(...) TWIMX_LENGTH_VALIDATE(TWIM2, __VA_ARGS__)
#else
#define TWIM2_LENGTH_VALIDATE(...) 0
#endif
#if NRFX_CHECK(NRFX_TWIM3_ENABLED)
#define TWIM3_LENGTH_VALIDATE(...) TWIMX_LENGTH_VALIDATE(TWIM3, __VA_ARGS__)
#else
#define TWIM3_LENGTH_VALIDATE(...) 0
#endif
#define TWIM_LENGTH_VALIDATE(drv_inst_idx, len1, len2) \
(TWIM0_LENGTH_VALIDATE(drv_inst_idx, len1, len2) || \
TWIM1_LENGTH_VALIDATE(drv_inst_idx, len1, len2) || \
TWIM2_LENGTH_VALIDATE(drv_inst_idx, len1, len2) || \
TWIM3_LENGTH_VALIDATE(drv_inst_idx, len1, len2))
// Control block - driver instance local data.
typedef struct
{
nrfx_twim_evt_handler_t handler;
void * p_context;
volatile uint32_t int_mask;
nrfx_twim_xfer_desc_t xfer_desc;
uint32_t flags;
uint8_t * p_curr_buf;
size_t curr_length;
bool curr_no_stop;
nrfx_drv_state_t state;
bool error;
volatile bool busy;
bool repeated;
uint8_t bytes_transferred;
bool hold_bus_uninit;
#if NRFX_CHECK(NRFX_TWIM_NRF52_ANOMALY_109_WORKAROUND_ENABLED)
nrf_twim_frequency_t bus_frequency;
#endif
} twim_control_block_t;
static twim_control_block_t m_cb[NRFX_TWIM_ENABLED_COUNT];
static nrfx_err_t twi_process_error(uint32_t errorsrc)
{
nrfx_err_t ret = NRFX_ERROR_INTERNAL;
if (errorsrc & NRF_TWIM_ERROR_OVERRUN)
{
ret = NRFX_ERROR_DRV_TWI_ERR_OVERRUN;
}
if (errorsrc & NRF_TWIM_ERROR_ADDRESS_NACK)
{
ret = NRFX_ERROR_DRV_TWI_ERR_ANACK;
}
if (errorsrc & NRF_TWIM_ERROR_DATA_NACK)
{
ret = NRFX_ERROR_DRV_TWI_ERR_DNACK;
}
return ret;
}
static bool xfer_completeness_check(NRF_TWIM_Type * p_twim, twim_control_block_t const * p_cb)
{
// If the actual number of transferred bytes is not equal to what was requested,
// but there was no error signaled by the peripheral, this means that something
// unexpected, like a premature STOP condition, was received on the bus.
// In such case the peripheral has to be disabled and re-enabled, so that its
// internal state machine is reinitialized.
bool transfer_complete = true;
switch (p_cb->xfer_desc.type)
{
case NRFX_TWIM_XFER_TXTX:
// int_mask variable is used to determine which length should be checked
// against number of bytes latched in EasyDMA.
// NRF_TWIM_INT_SUSPENDED_MASK is configured only in first TX of TXTX transfer.
if (((p_cb->int_mask & NRF_TWIM_INT_SUSPENDED_MASK) &&
(nrf_twim_txd_amount_get(p_twim) != p_cb->xfer_desc.primary_length)) ||
(!(p_cb->int_mask & NRF_TWIM_INT_SUSPENDED_MASK) &&
(nrf_twim_txd_amount_get(p_twim) != p_cb->xfer_desc.secondary_length)))
{
transfer_complete = false;
}
break;
case NRFX_TWIM_XFER_TXRX:
if ((nrf_twim_txd_amount_get(p_twim) != p_cb->xfer_desc.primary_length) ||
(nrf_twim_rxd_amount_get(p_twim) != p_cb->xfer_desc.secondary_length))
{
transfer_complete = false;
}
break;
case NRFX_TWIM_XFER_TX:
if (nrf_twim_txd_amount_get(p_twim) != p_cb->xfer_desc.primary_length)
{
transfer_complete = false;
}
break;
case NRFX_TWIM_XFER_RX:
if (nrf_twim_rxd_amount_get(p_twim) != p_cb->xfer_desc.primary_length)
{
transfer_complete = false;
}
break;
default:
break;
}
if (!transfer_complete)
{
nrf_twim_disable(p_twim);
nrf_twim_enable(p_twim);
}
return transfer_complete;
}
nrfx_err_t nrfx_twim_init(nrfx_twim_t const * p_instance,
nrfx_twim_config_t const * p_config,
nrfx_twim_evt_handler_t event_handler,
void * p_context)
{
NRFX_ASSERT(p_config);
NRFX_ASSERT(p_config->scl != p_config->sda);
twim_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
nrfx_err_t err_code;
if (p_cb->state != NRFX_DRV_STATE_UNINITIALIZED)
{
err_code = NRFX_ERROR_INVALID_STATE;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
#if NRFX_CHECK(NRFX_PRS_ENABLED)
static nrfx_irq_handler_t const irq_handlers[NRFX_TWIM_ENABLED_COUNT] = {
#if NRFX_CHECK(NRFX_TWIM0_ENABLED)
nrfx_twim_0_irq_handler,
#endif
#if NRFX_CHECK(NRFX_TWIM1_ENABLED)
nrfx_twim_1_irq_handler,
#endif
#if NRFX_CHECK(NRFX_TWIM2_ENABLED)
nrfx_twim_2_irq_handler,
#endif
#if NRFX_CHECK(NRFX_TWIM3_ENABLED)
nrfx_twim_3_irq_handler,
#endif
};
if (nrfx_prs_acquire(p_instance->p_twim,
irq_handlers[p_instance->drv_inst_idx]) != NRFX_SUCCESS)
{
err_code = NRFX_ERROR_BUSY;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
#endif // NRFX_CHECK(NRFX_PRS_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;
p_cb->hold_bus_uninit = p_config->hold_bus_uninit;
#if NRFX_CHECK(NRFX_TWIM_NRF52_ANOMALY_109_WORKAROUND_ENABLED)
p_cb->bus_frequency = (nrf_twim_frequency_t)p_config->frequency;
#endif
/* 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.
*/
TWIM_PIN_INIT(p_config->scl);
TWIM_PIN_INIT(p_config->sda);
NRF_TWIM_Type * p_twim = p_instance->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);
if (p_cb->handler)
{
NRFX_IRQ_PRIORITY_SET(nrfx_get_irq_number(p_instance->p_twim),
p_config->interrupt_priority);
NRFX_IRQ_ENABLE(nrfx_get_irq_number(p_instance->p_twim));
}
p_cb->state = NRFX_DRV_STATE_INITIALIZED;
err_code = NRFX_SUCCESS;
NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
void nrfx_twim_uninit(nrfx_twim_t const * p_instance)
{
twim_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
NRFX_ASSERT(p_cb->state != NRFX_DRV_STATE_UNINITIALIZED);
if (p_cb->handler)
{
NRFX_IRQ_DISABLE(nrfx_get_irq_number(p_instance->p_twim));
}
nrfx_twim_disable(p_instance);
#if NRFX_CHECK(NRFX_PRS_ENABLED)
nrfx_prs_release(p_instance->p_twim);
#endif
if (!p_cb->hold_bus_uninit)
{
nrf_gpio_cfg_default(p_instance->p_twim->PSEL.SCL);
nrf_gpio_cfg_default(p_instance->p_twim->PSEL.SDA);
}
p_cb->state = NRFX_DRV_STATE_UNINITIALIZED;
NRFX_LOG_INFO("Instance uninitialized: %d.", p_instance->drv_inst_idx);
}
void nrfx_twim_enable(nrfx_twim_t const * p_instance)
{
twim_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
NRFX_ASSERT(p_cb->state == NRFX_DRV_STATE_INITIALIZED);
nrf_twim_enable(p_instance->p_twim);
p_cb->state = NRFX_DRV_STATE_POWERED_ON;
NRFX_LOG_INFO("Instance enabled: %d.", p_instance->drv_inst_idx);
}
void nrfx_twim_disable(nrfx_twim_t const * p_instance)
{
twim_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
NRFX_ASSERT(p_cb->state != NRFX_DRV_STATE_UNINITIALIZED);
NRF_TWIM_Type * p_twim = p_instance->p_twim;
p_cb->int_mask = 0;
nrf_twim_int_disable(p_twim, NRF_TWIM_ALL_INTS_MASK);
nrf_twim_shorts_disable(p_twim, NRF_TWIM_ALL_SHORTS_MASK);
nrf_twim_disable(p_twim);
p_cb->state = NRFX_DRV_STATE_INITIALIZED;
NRFX_LOG_INFO("Instance disabled: %d.", p_instance->drv_inst_idx);
}
bool nrfx_twim_is_busy(nrfx_twim_t const * p_instance)
{
twim_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
return p_cb->busy;
}
__STATIC_INLINE void twim_list_enable_handle(NRF_TWIM_Type * p_twim, uint32_t flags)
{
if (NRFX_TWIM_FLAG_TX_POSTINC & flags)
{
nrf_twim_tx_list_enable(p_twim);
}
else
{
nrf_twim_tx_list_disable(p_twim);
}
if (NRFX_TWIM_FLAG_RX_POSTINC & flags)
{
nrf_twim_rx_list_enable(p_twim);
}
else
{
nrf_twim_rx_list_disable(p_twim);
}
}
__STATIC_INLINE nrfx_err_t twim_xfer(twim_control_block_t * p_cb,
NRF_TWIM_Type * p_twim,
nrfx_twim_xfer_desc_t const * p_xfer_desc,
uint32_t flags)
{
nrfx_err_t err_code = NRFX_SUCCESS;
nrf_twim_task_t start_task = NRF_TWIM_TASK_STARTTX;
p_cb->error = false;
if (!nrfx_is_in_ram(p_xfer_desc->p_primary_buf))
{
err_code = NRFX_ERROR_INVALID_ADDR;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
/* Block TWI interrupts to ensure that function is not interrupted by TWI interrupt. */
nrf_twim_int_disable(p_twim, NRF_TWIM_ALL_INTS_MASK);
if (p_cb->busy)
{
nrf_twim_int_enable(p_twim, p_cb->int_mask);
err_code = NRFX_ERROR_BUSY;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
else
{
p_cb->busy = ((NRFX_TWIM_FLAG_NO_XFER_EVT_HANDLER & flags) ||
(NRFX_TWIM_FLAG_REPEATED_XFER & flags)) ? false: true;
}
p_cb->xfer_desc = *p_xfer_desc;
p_cb->repeated = (flags & NRFX_TWIM_FLAG_REPEATED_XFER) ? true : false;
p_cb->flags = flags;
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);
nrf_twim_event_clear(p_twim, NRF_TWIM_EVENT_LASTTX);
nrf_twim_event_clear(p_twim, NRF_TWIM_EVENT_SUSPENDED);
twim_list_enable_handle(p_twim, flags);
switch (p_xfer_desc->type)
{
case NRFX_TWIM_XFER_TXTX:
NRFX_ASSERT(!(flags & NRFX_TWIM_FLAG_REPEATED_XFER));
NRFX_ASSERT(!(flags & NRFX_TWIM_FLAG_HOLD_XFER));
NRFX_ASSERT(!(flags & NRFX_TWIM_FLAG_NO_XFER_EVT_HANDLER));
if (!nrfx_is_in_ram(p_xfer_desc->p_secondary_buf))
{
err_code = NRFX_ERROR_INVALID_ADDR;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
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_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))
{}
NRFX_LOG_DEBUG("TWIM: Event: %s.", EVT_TO_STR_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;
break;
case NRFX_TWIM_XFER_TXRX:
nrf_twim_tx_buffer_set(p_twim, p_xfer_desc->p_primary_buf, p_xfer_desc->primary_length);
if (!nrfx_is_in_ram(p_xfer_desc->p_secondary_buf))
{
err_code = NRFX_ERROR_INVALID_ADDR;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
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_task_trigger(p_twim, NRF_TWIM_TASK_RESUME);
break;
case NRFX_TWIM_XFER_TX:
nrf_twim_tx_buffer_set(p_twim, p_xfer_desc->p_primary_buf, p_xfer_desc->primary_length);
if (NRFX_TWIM_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;
}
else
{
nrf_twim_shorts_set(p_twim, NRF_TWIM_SHORT_LASTTX_STOP_MASK);
p_cb->int_mask = NRF_TWIM_INT_STOPPED_MASK;
}
nrf_twim_task_trigger(p_twim, NRF_TWIM_TASK_RESUME);
break;
case NRFX_TWIM_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;
start_task = NRF_TWIM_TASK_STARTRX;
nrf_twim_task_trigger(p_twim, NRF_TWIM_TASK_RESUME);
break;
default:
err_code = NRFX_ERROR_INVALID_PARAM;
break;
}
if (!(flags & NRFX_TWIM_FLAG_HOLD_XFER) && (p_xfer_desc->type != NRFX_TWIM_XFER_TXTX))
{
nrf_twim_task_trigger(p_twim, start_task);
}
if (p_cb->handler)
{
if (flags & NRFX_TWIM_FLAG_NO_XFER_EVT_HANDLER)
{
p_cb->int_mask = 0;
}
if (!(flags & NRFX_TWIM_FLAG_NO_SPURIOUS_STOP_CHECK))
{
p_cb->int_mask |= NRF_TWIM_INT_STOPPED_MASK;
}
// Interrupts for ERROR are implicitly enabled, regardless of driver configuration.
p_cb->int_mask |= NRF_TWIM_INT_ERROR_MASK;
nrf_twim_int_enable(p_twim, p_cb->int_mask);
#if NRFX_CHECK(NRFX_TWIM_NRF52_ANOMALY_109_WORKAROUND_ENABLED)
if ((flags & NRFX_TWIM_FLAG_HOLD_XFER) && (p_xfer_desc->type != NRFX_TWIM_XFER_RX))
{
twim_list_enable_handle(p_twim, 0);
p_twim->FREQUENCY = 0;
nrf_twim_event_clear(p_twim, NRF_TWIM_EVENT_TXSTARTED);
nrf_twim_int_enable(p_twim, NRF_TWIM_INT_TXSTARTED_MASK);
}
else
{
nrf_twim_frequency_set(p_twim, p_cb->bus_frequency);
}
#endif
}
else
{
bool transmission_finished = false;
do {
if (nrf_twim_event_check(p_twim, NRF_TWIM_EVENT_SUSPENDED))
{
NRFX_LOG_DEBUG("TWIM: Event: %s.", EVT_TO_STR_TWIM(NRF_TWIM_EVENT_SUSPENDED));
transmission_finished = true;
}
if (nrf_twim_event_check(p_twim, NRF_TWIM_EVENT_STOPPED))
{
nrf_twim_event_clear(p_twim, NRF_TWIM_EVENT_STOPPED);
NRFX_LOG_DEBUG("TWIM: Event: %s.", EVT_TO_STR_TWIM(NRF_TWIM_EVENT_STOPPED));
transmission_finished = true;
}
if (nrf_twim_event_check(p_twim, NRF_TWIM_EVENT_ERROR))
{
nrf_twim_event_clear(p_twim, NRF_TWIM_EVENT_ERROR);
NRFX_LOG_DEBUG("TWIM: Event: %s.", EVT_TO_STR_TWIM(NRF_TWIM_EVENT_ERROR));
bool lasttx_triggered = nrf_twim_event_check(p_twim, NRF_TWIM_EVENT_LASTTX);
uint32_t shorts_mask = nrf_twim_shorts_get(p_twim);
if (!(lasttx_triggered && (shorts_mask & NRF_TWIM_SHORT_LASTTX_STOP_MASK)))
{
// Unless LASTTX event arrived and LASTTX_STOP shortcut is active,
// triggering of STOP task in case of error has to be done manually.
nrf_twim_task_trigger(p_twim, NRF_TWIM_TASK_RESUME);
nrf_twim_task_trigger(p_twim, NRF_TWIM_TASK_STOP);
// Mark transmission as not finished yet,
// as STOPPED event is expected to arrive.
// If LASTTX_SUSPENDED shortcut is active,
// NACK has been received on last byte sent
// and SUSPENDED event happened to be checked before ERROR,
// transmission will be marked as finished.
// In such case this flag has to be overwritten.
transmission_finished = false;
}
if (lasttx_triggered && (shorts_mask & NRF_TWIM_SHORT_LASTTX_SUSPEND_MASK))
{
// When STOP task was triggered just before SUSPEND task has taken effect,
// SUSPENDED event may not arrive.
// However if SUSPENDED arrives it always arrives after ERROR.
// Therefore SUSPENDED has to be cleared
// so it does not cause premature termination of busy loop
// waiting for STOPPED event to arrive.
nrf_twim_event_clear(p_twim, NRF_TWIM_EVENT_SUSPENDED);
// Mark transmission as not finished yet,
// for same reasons as above.
transmission_finished = false;
}
}
} while (!transmission_finished);
uint32_t errorsrc = nrf_twim_errorsrc_get_and_clear(p_twim);
p_cb->busy = false;
if (errorsrc)
{
err_code = twi_process_error(errorsrc);
}
else
{
if (!(flags & NRFX_TWIM_FLAG_NO_SPURIOUS_STOP_CHECK) &&
!xfer_completeness_check(p_twim, p_cb))
{
err_code = NRFX_ERROR_INTERNAL;
}
}
}
return err_code;
}
nrfx_err_t nrfx_twim_xfer(nrfx_twim_t const * p_instance,
nrfx_twim_xfer_desc_t const * p_xfer_desc,
uint32_t flags)
{
NRFX_ASSERT(TWIM_LENGTH_VALIDATE(p_instance->drv_inst_idx,
p_xfer_desc->primary_length,
p_xfer_desc->secondary_length));
nrfx_err_t err_code = NRFX_SUCCESS;
twim_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
// TXRX and TXTX transfers are supported only in non-blocking mode.
NRFX_ASSERT( !((p_cb->handler == NULL) && (p_xfer_desc->type == NRFX_TWIM_XFER_TXRX)));
NRFX_ASSERT( !((p_cb->handler == NULL) && (p_xfer_desc->type == NRFX_TWIM_XFER_TXTX)));
NRFX_LOG_INFO("Transfer type: %s.", TRANSFER_TO_STR(p_xfer_desc->type));
NRFX_LOG_INFO("Transfer buffers length: primary: %d, secondary: %d.",
p_xfer_desc->primary_length,
p_xfer_desc->secondary_length);
NRFX_LOG_DEBUG("Primary buffer data:");
NRFX_LOG_HEXDUMP_DEBUG(p_xfer_desc->p_primary_buf,
p_xfer_desc->primary_length * sizeof(p_xfer_desc->p_primary_buf[0]));
NRFX_LOG_DEBUG("Secondary buffer data:");
NRFX_LOG_HEXDUMP_DEBUG(p_xfer_desc->p_secondary_buf,
p_xfer_desc->secondary_length * sizeof(p_xfer_desc->p_secondary_buf[0]));
err_code = twim_xfer(p_cb, (NRF_TWIM_Type *)p_instance->p_twim, p_xfer_desc, flags);
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
nrfx_err_t nrfx_twim_tx(nrfx_twim_t const * p_instance,
uint8_t address,
uint8_t const * p_data,
size_t length,
bool no_stop)
{
nrfx_twim_xfer_desc_t xfer = NRFX_TWIM_XFER_DESC_TX(address, (uint8_t*)p_data, length);
return nrfx_twim_xfer(p_instance, &xfer, no_stop ? NRFX_TWIM_FLAG_TX_NO_STOP : 0);
}
nrfx_err_t nrfx_twim_rx(nrfx_twim_t const * p_instance,
uint8_t address,
uint8_t * p_data,
size_t length)
{
nrfx_twim_xfer_desc_t xfer = NRFX_TWIM_XFER_DESC_RX(address, p_data, length);
return nrfx_twim_xfer(p_instance, &xfer, 0);
}
uint32_t nrfx_twim_start_task_get(nrfx_twim_t const * p_instance,
nrfx_twim_xfer_type_t xfer_type)
{
return (uint32_t)nrf_twim_task_address_get(p_instance->p_twim,
(xfer_type != NRFX_TWIM_XFER_RX) ? NRF_TWIM_TASK_STARTTX : NRF_TWIM_TASK_STARTRX);
}
uint32_t nrfx_twim_stopped_event_get(nrfx_twim_t const * p_instance)
{
return (uint32_t)nrf_twim_event_address_get(p_instance->p_twim, NRF_TWIM_EVENT_STOPPED);
}
static void twim_irq_handler(NRF_TWIM_Type * p_twim, twim_control_block_t * p_cb)
{
#if NRFX_CHECK(NRFX_TWIM_NRF52_ANOMALY_109_WORKAROUND_ENABLED)
/* Handle only workaround case. Can be used without TWIM handler in IRQs. */
if (nrf_twim_event_check(p_twim, NRF_TWIM_EVENT_TXSTARTED))
{
nrf_twim_event_clear(p_twim, NRF_TWIM_EVENT_TXSTARTED);
nrf_twim_int_disable(p_twim, NRF_TWIM_INT_TXSTARTED_MASK);
if (p_twim->FREQUENCY == 0)
{
// Set enable to zero to reset TWIM internal state.
nrf_twim_disable(p_twim);
nrf_twim_enable(p_twim);
// Set proper frequency.
nrf_twim_frequency_set(p_twim, p_cb->bus_frequency);
twim_list_enable_handle(p_twim, p_cb->flags);
// Start proper transmission.
nrf_twim_task_trigger(p_twim, NRF_TWIM_TASK_STARTTX);
return;
}
}
#endif
NRFX_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);
NRFX_LOG_DEBUG("TWIM: Event: %s.", EVT_TO_STR_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);
if (!(nrf_twim_event_check(p_twim, NRF_TWIM_EVENT_LASTTX) &&
(nrf_twim_shorts_get(p_twim) & NRF_TWIM_SHORT_LASTTX_STOP_MASK)))
{
nrf_twim_task_trigger(p_twim, NRF_TWIM_TASK_RESUME);
nrf_twim_task_trigger(p_twim, NRF_TWIM_TASK_STOP);
}
p_cb->error = true;
return;
}
}
nrfx_twim_evt_t event;
if (nrf_twim_event_check(p_twim, NRF_TWIM_EVENT_STOPPED))
{
NRFX_LOG_DEBUG("TWIM: Event: %s.", EVT_TO_STR_TWIM(NRF_TWIM_EVENT_STOPPED));
nrf_twim_event_clear(p_twim, NRF_TWIM_EVENT_STOPPED);
if (!(p_cb->flags & NRFX_TWIM_FLAG_NO_SPURIOUS_STOP_CHECK) && !p_cb->error)
{
p_cb->error = !xfer_completeness_check(p_twim, p_cb);
}
// Further processing of STOPPED event is valid only if NO_XFER_EVT_HANDLER
// setting is not used.
if (!(p_cb->flags & NRFX_TWIM_FLAG_NO_XFER_EVT_HANDLER))
{
event.xfer_desc = p_cb->xfer_desc;
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, NRF_TWIM_ALL_INTS_MASK);
// At this point interrupt handler should not be invoked again for current transfer.
// If STOPPED arrived during ERROR processing,
// its pending interrupt should be ignored.
// Otherwise spurious NRFX_TWIM_EVT_DONE or NRFX_TWIM_EVT_BUS_ERROR
// would be passed to user's handler.
NRFX_IRQ_PENDING_CLEAR(nrfx_get_irq_number(p_twim));
}
}
#if NRFX_CHECK(NRFX_TWIM_NRF52_ANOMALY_109_WORKAROUND_ENABLED)
else if (p_cb->xfer_desc.type != NRFX_TWIM_XFER_RX)
{
/* Add Anomaly 109 workaround for each potential repeated transfer starting from TX. */
twim_list_enable_handle(p_twim, 0);
p_twim->FREQUENCY = 0;
nrf_twim_int_enable(p_twim, NRF_TWIM_INT_TXSTARTED_MASK);
}
#endif
}
else
{
nrf_twim_event_clear(p_twim, NRF_TWIM_EVENT_SUSPENDED);
NRFX_LOG_DEBUG("TWIM: Event: %s.", EVT_TO_STR_TWIM(NRF_TWIM_EVENT_SUSPENDED));
if (p_cb->xfer_desc.type == NRFX_TWIM_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, NRF_TWIM_ALL_INTS_MASK);
// At this point interrupt handler should not be invoked again for current transfer.
// If STOPPED arrived during SUSPENDED processing,
// its pending interrupt should be ignored.
// Otherwise spurious NRFX_TWIM_EVT_DONE or NRFX_TWIM_EVT_BUS_ERROR
// would be passed to user's handler.
NRFX_IRQ_PENDING_CLEAR(nrfx_get_irq_number(p_twim));
}
}
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, NRF_TWIM_ALL_INTS_MASK);
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 = NRFX_TWIM_EVT_ADDRESS_NACK;
NRFX_LOG_DEBUG("Event: %s.", EVT_TO_STR(NRFX_TWIM_EVT_ADDRESS_NACK));
}
else if (errorsrc & NRF_TWIM_ERROR_DATA_NACK)
{
event.type = NRFX_TWIM_EVT_DATA_NACK;
NRFX_LOG_DEBUG("Event: %s.", EVT_TO_STR(NRFX_TWIM_EVT_DATA_NACK));
}
else if (errorsrc & NRF_TWIM_ERROR_OVERRUN)
{
event.type = NRFX_TWIM_EVT_OVERRUN;
NRFX_LOG_DEBUG("Event: %s.", EVT_TO_STR(NRFX_TWIM_EVT_OVERRUN));
}
else if (p_cb->error)
{
event.type = NRFX_TWIM_EVT_BUS_ERROR;
NRFX_LOG_DEBUG("Event: %s.", EVT_TO_STR(NRFX_TWIM_EVT_BUS_ERROR));
}
else
{
event.type = NRFX_TWIM_EVT_DONE;
NRFX_LOG_DEBUG("Event: %s.", EVT_TO_STR(NRFX_TWIM_EVT_DONE));
}
if (!p_cb->repeated)
{
p_cb->busy = false;
}
if (!(p_cb->flags & NRFX_TWIM_FLAG_NO_XFER_EVT_HANDLER) || p_cb->error)
{
p_cb->handler(&event, p_cb->p_context);
}
}
#if NRFX_CHECK(NRFX_TWIM0_ENABLED)
void nrfx_twim_0_irq_handler(void)
{
twim_irq_handler(NRF_TWIM0, &m_cb[NRFX_TWIM0_INST_IDX]);
}
#endif
#if NRFX_CHECK(NRFX_TWIM1_ENABLED)
void nrfx_twim_1_irq_handler(void)
{
twim_irq_handler(NRF_TWIM1, &m_cb[NRFX_TWIM1_INST_IDX]);
}
#endif
#if NRFX_CHECK(NRFX_TWIM2_ENABLED)
void nrfx_twim_2_irq_handler(void)
{
twim_irq_handler(NRF_TWIM2, &m_cb[NRFX_TWIM2_INST_IDX]);
}
#endif
#if NRFX_CHECK(NRFX_TWIM3_ENABLED)
void nrfx_twim_3_irq_handler(void)
{
twim_irq_handler(NRF_TWIM3, &m_cb[NRFX_TWIM3_INST_IDX]);
}
#endif
#endif // NRFX_CHECK(NRFX_TWIM_ENABLED)
+654
View File
@@ -0,0 +1,654 @@
/**
* Copyright (c) 2015 - 2021, 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, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, 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 its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA 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.
*
*/
#include <nrfx.h>
#if NRFX_CHECK(NRFX_UART_ENABLED)
#if !NRFX_CHECK(NRFX_UART0_ENABLED)
#error "No enabled UART instances. Check <nrfx_config.h>."
#endif
#include <nrfx_uart.h>
#include "prs/nrfx_prs.h"
#include <hal/nrf_gpio.h>
#define NRFX_LOG_MODULE UART
#include <nrfx_log.h>
#define EVT_TO_STR(event) \
(event == NRF_UART_EVENT_ERROR ? "NRF_UART_EVENT_ERROR" : \
"UNKNOWN EVENT")
#define TX_COUNTER_ABORT_REQ_VALUE UINT32_MAX
typedef struct
{
void * p_context;
nrfx_uart_event_handler_t handler;
uint8_t const * p_tx_buffer;
uint8_t * p_rx_buffer;
uint8_t * p_rx_secondary_buffer;
volatile size_t tx_buffer_length;
size_t rx_buffer_length;
size_t rx_secondary_buffer_length;
volatile size_t tx_counter;
volatile size_t rx_counter;
volatile bool tx_abort;
bool rx_enabled;
nrfx_drv_state_t state;
} uart_control_block_t;
static uart_control_block_t m_cb[NRFX_UART_ENABLED_COUNT];
static void apply_config(nrfx_uart_t const * p_instance,
nrfx_uart_config_t const * p_config)
{
if (p_config->pseltxd != NRF_UART_PSEL_DISCONNECTED)
{
nrf_gpio_pin_set(p_config->pseltxd);
nrf_gpio_cfg_output(p_config->pseltxd);
}
if (p_config->pselrxd != NRF_UART_PSEL_DISCONNECTED)
{
nrf_gpio_cfg_input(p_config->pselrxd, NRF_GPIO_PIN_NOPULL);
}
nrf_uart_baudrate_set(p_instance->p_reg, p_config->baudrate);
nrf_uart_configure(p_instance->p_reg, p_config->parity, p_config->hwfc);
nrf_uart_txrx_pins_set(p_instance->p_reg, p_config->pseltxd, p_config->pselrxd);
if (p_config->hwfc == NRF_UART_HWFC_ENABLED)
{
if (p_config->pselcts != NRF_UART_PSEL_DISCONNECTED)
{
nrf_gpio_cfg_input(p_config->pselcts, NRF_GPIO_PIN_NOPULL);
}
if (p_config->pselrts != NRF_UART_PSEL_DISCONNECTED)
{
nrf_gpio_pin_set(p_config->pselrts);
nrf_gpio_cfg_output(p_config->pselrts);
}
nrf_uart_hwfc_pins_set(p_instance->p_reg, p_config->pselrts, p_config->pselcts);
}
}
static void interrupts_enable(nrfx_uart_t const * p_instance,
uint8_t interrupt_priority)
{
nrf_uart_event_clear(p_instance->p_reg, NRF_UART_EVENT_TXDRDY);
nrf_uart_event_clear(p_instance->p_reg, NRF_UART_EVENT_RXTO);
nrf_uart_int_enable(p_instance->p_reg, NRF_UART_INT_MASK_TXDRDY |
NRF_UART_INT_MASK_RXTO);
NRFX_IRQ_PRIORITY_SET(nrfx_get_irq_number((void *)p_instance->p_reg),
interrupt_priority);
NRFX_IRQ_ENABLE(nrfx_get_irq_number((void *)p_instance->p_reg));
}
static void interrupts_disable(nrfx_uart_t const * p_instance)
{
nrf_uart_int_disable(p_instance->p_reg, NRF_UART_INT_MASK_RXDRDY |
NRF_UART_INT_MASK_TXDRDY |
NRF_UART_INT_MASK_ERROR |
NRF_UART_INT_MASK_RXTO);
NRFX_IRQ_DISABLE(nrfx_get_irq_number((void *)p_instance->p_reg));
}
static void pins_to_default(nrfx_uart_t const * p_instance)
{
/* Reset pins to default states */
uint32_t txd;
uint32_t rxd;
uint32_t rts;
uint32_t cts;
txd = nrf_uart_tx_pin_get(p_instance->p_reg);
rxd = nrf_uart_rx_pin_get(p_instance->p_reg);
rts = nrf_uart_rts_pin_get(p_instance->p_reg);
cts = nrf_uart_cts_pin_get(p_instance->p_reg);
nrf_uart_txrx_pins_disconnect(p_instance->p_reg);
nrf_uart_hwfc_pins_disconnect(p_instance->p_reg);
if (txd != NRF_UART_PSEL_DISCONNECTED)
{
nrf_gpio_cfg_default(txd);
}
if (rxd != NRF_UART_PSEL_DISCONNECTED)
{
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);
}
}
nrfx_err_t nrfx_uart_init(nrfx_uart_t const * p_instance,
nrfx_uart_config_t const * p_config,
nrfx_uart_event_handler_t event_handler)
{
NRFX_ASSERT(p_config);
uart_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
nrfx_err_t err_code = NRFX_SUCCESS;
if (p_cb->state != NRFX_DRV_STATE_UNINITIALIZED)
{
err_code = NRFX_ERROR_INVALID_STATE;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
#if NRFX_CHECK(NRFX_PRS_ENABLED)
static nrfx_irq_handler_t const irq_handlers[NRFX_UART_ENABLED_COUNT] = {
#if NRFX_CHECK(NRFX_UART0_ENABLED)
nrfx_uart_0_irq_handler,
#endif
};
if (nrfx_prs_acquire(p_instance->p_reg,
irq_handlers[p_instance->drv_inst_idx]) != NRFX_SUCCESS)
{
err_code = NRFX_ERROR_BUSY;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
#endif // NRFX_CHECK(NRFX_PRS_ENABLED)
apply_config(p_instance, p_config);
p_cb->handler = event_handler;
p_cb->p_context = p_config->p_context;
if (p_cb->handler)
{
interrupts_enable(p_instance, p_config->interrupt_priority);
}
nrf_uart_enable(p_instance->p_reg);
p_cb->rx_buffer_length = 0;
p_cb->rx_secondary_buffer_length = 0;
p_cb->rx_enabled = false;
p_cb->tx_buffer_length = 0;
p_cb->state = NRFX_DRV_STATE_INITIALIZED;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
void nrfx_uart_uninit(nrfx_uart_t const * p_instance)
{
uart_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
nrf_uart_disable(p_instance->p_reg);
if (p_cb->handler)
{
interrupts_disable(p_instance);
}
pins_to_default(p_instance);
#if NRFX_CHECK(NRFX_PRS_ENABLED)
nrfx_prs_release(p_instance->p_reg);
#endif
p_cb->state = NRFX_DRV_STATE_UNINITIALIZED;
p_cb->handler = NULL;
NRFX_LOG_INFO("Instance uninitialized: %d.", p_instance->drv_inst_idx);
}
static void tx_byte(NRF_UART_Type * p_uart, uart_control_block_t * p_cb)
{
nrf_uart_event_clear(p_uart, NRF_UART_EVENT_TXDRDY);
uint8_t txd = p_cb->p_tx_buffer[p_cb->tx_counter];
p_cb->tx_counter++;
nrf_uart_txd_set(p_uart, txd);
}
static bool tx_blocking(NRF_UART_Type * p_uart, uart_control_block_t * p_cb)
{
// Use a local variable to avoid undefined order of accessing two volatile variables
// in one statement.
size_t const tx_buffer_length = p_cb->tx_buffer_length;
while (p_cb->tx_counter < tx_buffer_length)
{
// Wait until the transmitter is ready to accept a new byte.
// Exit immediately if the transfer has been aborted.
while (!nrf_uart_event_check(p_uart, NRF_UART_EVENT_TXDRDY))
{
if (p_cb->tx_abort)
{
return false;
}
}
tx_byte(p_uart, p_cb);
}
return true;
}
nrfx_err_t nrfx_uart_tx(nrfx_uart_t const * p_instance,
uint8_t const * p_data,
size_t length)
{
uart_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
NRFX_ASSERT(p_cb->state == NRFX_DRV_STATE_INITIALIZED);
NRFX_ASSERT(p_data);
NRFX_ASSERT(length > 0);
nrfx_err_t err_code;
if (nrfx_uart_tx_in_progress(p_instance))
{
err_code = NRFX_ERROR_BUSY;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
p_cb->tx_buffer_length = length;
p_cb->p_tx_buffer = p_data;
p_cb->tx_counter = 0;
p_cb->tx_abort = false;
NRFX_LOG_INFO("Transfer tx_len: %d.", p_cb->tx_buffer_length);
NRFX_LOG_DEBUG("Tx data:");
NRFX_LOG_HEXDUMP_DEBUG(p_cb->p_tx_buffer,
p_cb->tx_buffer_length * sizeof(p_cb->p_tx_buffer[0]));
err_code = NRFX_SUCCESS;
nrf_uart_event_clear(p_instance->p_reg, NRF_UART_EVENT_TXDRDY);
nrf_uart_task_trigger(p_instance->p_reg, NRF_UART_TASK_STARTTX);
tx_byte(p_instance->p_reg, p_cb);
if (p_cb->handler == NULL)
{
if (!tx_blocking(p_instance->p_reg, p_cb))
{
// The transfer has been aborted.
err_code = NRFX_ERROR_FORBIDDEN;
}
else
{
// Wait until the last byte is completely transmitted.
while (!nrf_uart_event_check(p_instance->p_reg, NRF_UART_EVENT_TXDRDY))
{}
nrf_uart_task_trigger(p_instance->p_reg, NRF_UART_TASK_STOPTX);
}
p_cb->tx_buffer_length = 0;
}
NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
bool nrfx_uart_tx_in_progress(nrfx_uart_t const * p_instance)
{
return (m_cb[p_instance->drv_inst_idx].tx_buffer_length != 0);
}
static void rx_enable(nrfx_uart_t const * p_instance)
{
nrf_uart_event_clear(p_instance->p_reg, NRF_UART_EVENT_ERROR);
nrf_uart_event_clear(p_instance->p_reg, NRF_UART_EVENT_RXDRDY);
nrf_uart_task_trigger(p_instance->p_reg, NRF_UART_TASK_STARTRX);
}
static void rx_byte(NRF_UART_Type * p_uart, uart_control_block_t * p_cb)
{
if (!p_cb->rx_buffer_length)
{
nrf_uart_event_clear(p_uart, NRF_UART_EVENT_RXDRDY);
// Byte received when buffer is not set - data lost.
(void) nrf_uart_rxd_get(p_uart);
return;
}
nrf_uart_event_clear(p_uart, NRF_UART_EVENT_RXDRDY);
p_cb->p_rx_buffer[p_cb->rx_counter] = nrf_uart_rxd_get(p_uart);
p_cb->rx_counter++;
}
nrfx_err_t nrfx_uart_rx(nrfx_uart_t const * p_instance,
uint8_t * p_data,
size_t length)
{
uart_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
NRFX_ASSERT(m_cb[p_instance->drv_inst_idx].state == NRFX_DRV_STATE_INITIALIZED);
NRFX_ASSERT(p_data);
NRFX_ASSERT(length > 0);
nrfx_err_t err_code;
bool second_buffer = false;
if (p_cb->handler)
{
nrf_uart_int_disable(p_instance->p_reg, NRF_UART_INT_MASK_RXDRDY |
NRF_UART_INT_MASK_ERROR);
}
if (p_cb->rx_buffer_length != 0)
{
if (p_cb->rx_secondary_buffer_length != 0)
{
if (p_cb->handler)
{
nrf_uart_int_enable(p_instance->p_reg, NRF_UART_INT_MASK_RXDRDY |
NRF_UART_INT_MASK_ERROR);
}
err_code = NRFX_ERROR_BUSY;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
second_buffer = true;
}
if (!second_buffer)
{
p_cb->rx_buffer_length = length;
p_cb->p_rx_buffer = p_data;
p_cb->rx_counter = 0;
p_cb->rx_secondary_buffer_length = 0;
}
else
{
p_cb->p_rx_secondary_buffer = p_data;
p_cb->rx_secondary_buffer_length = length;
}
NRFX_LOG_INFO("Transfer rx_len: %d.", length);
if ((!p_cb->rx_enabled) && (!second_buffer))
{
rx_enable(p_instance);
}
if (p_cb->handler == NULL)
{
nrf_uart_event_clear(p_instance->p_reg, NRF_UART_EVENT_RXTO);
bool rxrdy;
bool rxto;
bool error;
do
{
do
{
error = nrf_uart_event_check(p_instance->p_reg, NRF_UART_EVENT_ERROR);
rxrdy = nrf_uart_event_check(p_instance->p_reg, NRF_UART_EVENT_RXDRDY);
rxto = nrf_uart_event_check(p_instance->p_reg, NRF_UART_EVENT_RXTO);
} while ((!rxrdy) && (!rxto) && (!error));
if (error || rxto)
{
break;
}
rx_byte(p_instance->p_reg, p_cb);
} while (p_cb->rx_buffer_length > p_cb->rx_counter);
p_cb->rx_buffer_length = 0;
if (error)
{
err_code = NRFX_ERROR_INTERNAL;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
if (rxto)
{
err_code = NRFX_ERROR_FORBIDDEN;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
if (p_cb->rx_enabled)
{
nrf_uart_task_trigger(p_instance->p_reg, NRF_UART_TASK_STARTRX);
}
else
{
// Skip stopping RX if driver is forced to be enabled.
nrf_uart_task_trigger(p_instance->p_reg, NRF_UART_TASK_STOPRX);
}
}
else
{
nrf_uart_int_enable(p_instance->p_reg, NRF_UART_INT_MASK_RXDRDY |
NRF_UART_INT_MASK_ERROR);
}
err_code = NRFX_SUCCESS;
NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
bool nrfx_uart_rx_ready(nrfx_uart_t const * p_instance)
{
return nrf_uart_event_check(p_instance->p_reg, NRF_UART_EVENT_RXDRDY);
}
void nrfx_uart_rx_enable(nrfx_uart_t const * p_instance)
{
if (!m_cb[p_instance->drv_inst_idx].rx_enabled)
{
rx_enable(p_instance);
m_cb[p_instance->drv_inst_idx].rx_enabled = true;
}
}
void nrfx_uart_rx_disable(nrfx_uart_t const * p_instance)
{
nrf_uart_task_trigger(p_instance->p_reg, NRF_UART_TASK_STOPRX);
m_cb[p_instance->drv_inst_idx].rx_enabled = false;
}
uint32_t nrfx_uart_errorsrc_get(nrfx_uart_t const * p_instance)
{
nrf_uart_event_clear(p_instance->p_reg, NRF_UART_EVENT_ERROR);
return nrf_uart_errorsrc_get_and_clear(p_instance->p_reg);
}
static void rx_done_event(uart_control_block_t * p_cb,
size_t bytes,
uint8_t * p_data)
{
nrfx_uart_event_t event;
event.type = NRFX_UART_EVT_RX_DONE;
event.data.rxtx.bytes = bytes;
event.data.rxtx.p_data = p_data;
p_cb->handler(&event, p_cb->p_context);
}
static void tx_done_event(uart_control_block_t * p_cb,
size_t bytes)
{
nrfx_uart_event_t event;
event.type = NRFX_UART_EVT_TX_DONE;
event.data.rxtx.bytes = bytes;
event.data.rxtx.p_data = (uint8_t *)p_cb->p_tx_buffer;
p_cb->tx_buffer_length = 0;
p_cb->handler(&event, p_cb->p_context);
}
void nrfx_uart_tx_abort(nrfx_uart_t const * p_instance)
{
uart_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
p_cb->tx_abort = true;
nrf_uart_task_trigger(p_instance->p_reg, NRF_UART_TASK_STOPTX);
if (p_cb->handler)
{
tx_done_event(p_cb, p_cb->tx_counter);
}
NRFX_LOG_INFO("TX transaction aborted.");
}
void nrfx_uart_rx_abort(nrfx_uart_t const * p_instance)
{
nrf_uart_int_disable(p_instance->p_reg, NRF_UART_INT_MASK_RXDRDY |
NRF_UART_INT_MASK_ERROR);
nrf_uart_task_trigger(p_instance->p_reg, NRF_UART_TASK_STOPRX);
NRFX_LOG_INFO("RX transaction aborted.");
}
static void uart_irq_handler(NRF_UART_Type * p_uart,
uart_control_block_t * p_cb)
{
if (nrf_uart_int_enable_check(p_uart, NRF_UART_INT_MASK_ERROR) &&
nrf_uart_event_check(p_uart, NRF_UART_EVENT_ERROR))
{
nrfx_uart_event_t event;
nrf_uart_event_clear(p_uart, NRF_UART_EVENT_ERROR);
NRFX_LOG_DEBUG("Event: %s.", EVT_TO_STR(NRF_UART_EVENT_ERROR));
nrf_uart_int_disable(p_uart, NRF_UART_INT_MASK_RXDRDY |
NRF_UART_INT_MASK_ERROR);
if (!p_cb->rx_enabled)
{
nrf_uart_task_trigger(p_uart, NRF_UART_TASK_STOPRX);
}
event.type = NRFX_UART_EVT_ERROR;
event.data.error.error_mask = nrf_uart_errorsrc_get_and_clear(p_uart);
event.data.error.rxtx.bytes = p_cb->rx_buffer_length;
event.data.error.rxtx.p_data = p_cb->p_rx_buffer;
// Abort transfer.
p_cb->rx_buffer_length = 0;
p_cb->rx_secondary_buffer_length = 0;
p_cb->handler(&event,p_cb->p_context);
}
else if (nrf_uart_int_enable_check(p_uart, NRF_UART_INT_MASK_RXDRDY) &&
nrf_uart_event_check(p_uart, NRF_UART_EVENT_RXDRDY))
{
rx_byte(p_uart, p_cb);
if (p_cb->rx_buffer_length == p_cb->rx_counter)
{
if (p_cb->rx_secondary_buffer_length)
{
uint8_t * p_data = p_cb->p_rx_buffer;
size_t rx_counter = p_cb->rx_counter;
// Switch to secondary buffer.
p_cb->rx_buffer_length = p_cb->rx_secondary_buffer_length;
p_cb->p_rx_buffer = p_cb->p_rx_secondary_buffer;
p_cb->rx_secondary_buffer_length = 0;
p_cb->rx_counter = 0;
rx_done_event(p_cb, rx_counter, p_data);
}
else
{
if (!p_cb->rx_enabled)
{
nrf_uart_task_trigger(p_uart, NRF_UART_TASK_STOPRX);
}
nrf_uart_int_disable(p_uart, NRF_UART_INT_MASK_RXDRDY |
NRF_UART_INT_MASK_ERROR);
p_cb->rx_buffer_length = 0;
rx_done_event(p_cb, p_cb->rx_counter, p_cb->p_rx_buffer);
}
}
}
if (nrf_uart_event_check(p_uart, NRF_UART_EVENT_TXDRDY))
{
// Use a local variable to avoid undefined order of accessing two volatile variables
// in one statement.
size_t const tx_buffer_length = p_cb->tx_buffer_length;
if (p_cb->tx_counter < tx_buffer_length && !p_cb->tx_abort)
{
tx_byte(p_uart, p_cb);
}
else
{
nrf_uart_event_clear(p_uart, NRF_UART_EVENT_TXDRDY);
if (p_cb->tx_buffer_length)
{
tx_done_event(p_cb, p_cb->tx_buffer_length);
}
}
}
if (nrf_uart_event_check(p_uart, NRF_UART_EVENT_RXTO))
{
nrf_uart_event_clear(p_uart, NRF_UART_EVENT_RXTO);
// RXTO event may be triggered as a result of abort call. In th
if (p_cb->rx_enabled)
{
nrf_uart_task_trigger(p_uart, NRF_UART_TASK_STARTRX);
}
if (p_cb->rx_buffer_length)
{
p_cb->rx_buffer_length = 0;
rx_done_event(p_cb, p_cb->rx_counter, p_cb->p_rx_buffer);
}
}
}
#if NRFX_CHECK(NRFX_UART0_ENABLED)
void nrfx_uart_0_irq_handler(void)
{
uart_irq_handler(NRF_UART0, &m_cb[NRFX_UART0_INST_IDX]);
}
#endif
#endif // NRFX_CHECK(NRFX_UART_ENABLED)
+702
View File
@@ -0,0 +1,702 @@
/**
* Copyright (c) 2015 - 2021, 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, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, 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 its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA 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.
*
*/
#include <nrfx.h>
#if NRFX_CHECK(NRFX_UARTE_ENABLED)
#if !(NRFX_CHECK(NRFX_UARTE0_ENABLED) || \
NRFX_CHECK(NRFX_UARTE1_ENABLED) || \
NRFX_CHECK(NRFX_UARTE2_ENABLED) || \
NRFX_CHECK(NRFX_UARTE3_ENABLED))
#error "No enabled UARTE instances. Check <nrfx_config.h>."
#endif
#include <nrfx_uarte.h>
#include "prs/nrfx_prs.h"
#include <hal/nrf_gpio.h>
#define NRFX_LOG_MODULE UARTE
#include <nrfx_log.h>
#define EVT_TO_STR(event) \
(event == NRF_UARTE_EVENT_ERROR ? "NRF_UARTE_EVENT_ERROR" : \
"UNKNOWN EVENT")
#define UARTEX_LENGTH_VALIDATE(peripheral, drv_inst_idx, len1, len2) \
(((drv_inst_idx) == NRFX_CONCAT_3(NRFX_, peripheral, _INST_IDX)) && \
NRFX_EASYDMA_LENGTH_VALIDATE(peripheral, len1, len2))
#if NRFX_CHECK(NRFX_UARTE0_ENABLED)
#define UARTE0_LENGTH_VALIDATE(...) UARTEX_LENGTH_VALIDATE(UARTE0, __VA_ARGS__)
#else
#define UARTE0_LENGTH_VALIDATE(...) 0
#endif
#if NRFX_CHECK(NRFX_UARTE1_ENABLED)
#define UARTE1_LENGTH_VALIDATE(...) UARTEX_LENGTH_VALIDATE(UARTE1, __VA_ARGS__)
#else
#define UARTE1_LENGTH_VALIDATE(...) 0
#endif
#if NRFX_CHECK(NRFX_UARTE2_ENABLED)
#define UARTE2_LENGTH_VALIDATE(...) UARTEX_LENGTH_VALIDATE(UARTE2, __VA_ARGS__)
#else
#define UARTE2_LENGTH_VALIDATE(...) 0
#endif
#if NRFX_CHECK(NRFX_UARTE3_ENABLED)
#define UARTE3_LENGTH_VALIDATE(...) UARTEX_LENGTH_VALIDATE(UARTE3, __VA_ARGS__)
#else
#define UARTE3_LENGTH_VALIDATE(...) 0
#endif
#define UARTE_LENGTH_VALIDATE(drv_inst_idx, length) \
(UARTE0_LENGTH_VALIDATE(drv_inst_idx, length, 0) || \
UARTE1_LENGTH_VALIDATE(drv_inst_idx, length, 0) || \
UARTE2_LENGTH_VALIDATE(drv_inst_idx, length, 0) || \
UARTE3_LENGTH_VALIDATE(drv_inst_idx, length, 0))
typedef struct
{
void * p_context;
nrfx_uarte_event_handler_t handler;
uint8_t const * p_tx_buffer;
uint8_t * p_rx_buffer;
uint8_t * p_rx_secondary_buffer;
volatile size_t tx_buffer_length;
size_t rx_buffer_length;
size_t rx_secondary_buffer_length;
nrfx_drv_state_t state;
} uarte_control_block_t;
static uarte_control_block_t m_cb[NRFX_UARTE_ENABLED_COUNT];
static void apply_config(nrfx_uarte_t const * p_instance,
nrfx_uarte_config_t const * p_config)
{
if (p_config->pseltxd != NRF_UARTE_PSEL_DISCONNECTED)
{
nrf_gpio_pin_set(p_config->pseltxd);
nrf_gpio_cfg_output(p_config->pseltxd);
}
if (p_config->pselrxd != NRF_UARTE_PSEL_DISCONNECTED)
{
nrf_gpio_cfg_input(p_config->pselrxd, NRF_GPIO_PIN_NOPULL);
}
nrf_uarte_baudrate_set(p_instance->p_reg, p_config->baudrate);
nrf_uarte_configure(p_instance->p_reg, p_config->parity, p_config->hwfc);
nrf_uarte_txrx_pins_set(p_instance->p_reg, p_config->pseltxd, p_config->pselrxd);
if (p_config->hwfc == NRF_UARTE_HWFC_ENABLED)
{
if (p_config->pselcts != NRF_UARTE_PSEL_DISCONNECTED)
{
nrf_gpio_cfg_input(p_config->pselcts, NRF_GPIO_PIN_NOPULL);
}
if (p_config->pselrts != NRF_UARTE_PSEL_DISCONNECTED)
{
nrf_gpio_pin_set(p_config->pselrts);
nrf_gpio_cfg_output(p_config->pselrts);
}
nrf_uarte_hwfc_pins_set(p_instance->p_reg, p_config->pselrts, p_config->pselcts);
}
}
static void interrupts_enable(nrfx_uarte_t const * p_instance,
uint8_t interrupt_priority)
{
nrf_uarte_event_clear(p_instance->p_reg, NRF_UARTE_EVENT_ENDRX);
nrf_uarte_event_clear(p_instance->p_reg, NRF_UARTE_EVENT_ENDTX);
nrf_uarte_event_clear(p_instance->p_reg, NRF_UARTE_EVENT_ERROR);
nrf_uarte_event_clear(p_instance->p_reg, NRF_UARTE_EVENT_RXTO);
nrf_uarte_event_clear(p_instance->p_reg, NRF_UARTE_EVENT_TXSTOPPED);
nrf_uarte_int_enable(p_instance->p_reg, NRF_UARTE_INT_ENDRX_MASK |
NRF_UARTE_INT_ENDTX_MASK |
NRF_UARTE_INT_ERROR_MASK |
NRF_UARTE_INT_RXTO_MASK |
NRF_UARTE_INT_TXSTOPPED_MASK);
NRFX_IRQ_PRIORITY_SET(nrfx_get_irq_number((void *)p_instance->p_reg),
interrupt_priority);
NRFX_IRQ_ENABLE(nrfx_get_irq_number((void *)p_instance->p_reg));
}
static void interrupts_disable(nrfx_uarte_t const * p_instance)
{
nrf_uarte_int_disable(p_instance->p_reg, NRF_UARTE_INT_ENDRX_MASK |
NRF_UARTE_INT_ENDTX_MASK |
NRF_UARTE_INT_ERROR_MASK |
NRF_UARTE_INT_RXTO_MASK |
NRF_UARTE_INT_TXSTOPPED_MASK);
NRFX_IRQ_DISABLE(nrfx_get_irq_number((void *)p_instance->p_reg));
}
static void pins_to_default(nrfx_uarte_t const * p_instance)
{
/* Reset pins to default states */
uint32_t txd;
uint32_t rxd;
uint32_t rts;
uint32_t cts;
txd = nrf_uarte_tx_pin_get(p_instance->p_reg);
rxd = nrf_uarte_rx_pin_get(p_instance->p_reg);
rts = nrf_uarte_rts_pin_get(p_instance->p_reg);
cts = nrf_uarte_cts_pin_get(p_instance->p_reg);
nrf_uarte_txrx_pins_disconnect(p_instance->p_reg);
nrf_uarte_hwfc_pins_disconnect(p_instance->p_reg);
if (txd != NRF_UARTE_PSEL_DISCONNECTED)
{
nrf_gpio_cfg_default(txd);
}
if (rxd != NRF_UARTE_PSEL_DISCONNECTED)
{
nrf_gpio_cfg_default(rxd);
}
if (cts != NRF_UARTE_PSEL_DISCONNECTED)
{
nrf_gpio_cfg_default(cts);
}
if (rts != NRF_UARTE_PSEL_DISCONNECTED)
{
nrf_gpio_cfg_default(rts);
}
}
nrfx_err_t nrfx_uarte_init(nrfx_uarte_t const * p_instance,
nrfx_uarte_config_t const * p_config,
nrfx_uarte_event_handler_t event_handler)
{
NRFX_ASSERT(p_config);
uarte_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
nrfx_err_t err_code = NRFX_SUCCESS;
if (p_cb->state != NRFX_DRV_STATE_UNINITIALIZED)
{
err_code = NRFX_ERROR_INVALID_STATE;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
#if NRFX_CHECK(NRFX_PRS_ENABLED)
static nrfx_irq_handler_t const irq_handlers[NRFX_UARTE_ENABLED_COUNT] = {
#if NRFX_CHECK(NRFX_UARTE0_ENABLED)
nrfx_uarte_0_irq_handler,
#endif
#if NRFX_CHECK(NRFX_UARTE1_ENABLED)
nrfx_uarte_1_irq_handler,
#endif
#if NRFX_CHECK(NRFX_UARTE2_ENABLED)
nrfx_uarte_2_irq_handler,
#endif
#if NRFX_CHECK(NRFX_UARTE3_ENABLED)
nrfx_uarte_3_irq_handler,
#endif
};
if (nrfx_prs_acquire(p_instance->p_reg,
irq_handlers[p_instance->drv_inst_idx]) != NRFX_SUCCESS)
{
err_code = NRFX_ERROR_BUSY;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
#endif // NRFX_CHECK(NRFX_PRS_ENABLED)
apply_config(p_instance, p_config);
#if defined(NRF5340_XXAA_APPLICATION) || defined(NRF5340_XXAA_NETWORK) || defined(NRF9160_XXAA)
// Apply workaround for anomalies:
// - nRF9160 - anomaly 23
// - nRF5340 - anomaly 44
volatile uint32_t const * rxenable_reg =
(volatile uint32_t *)(((uint32_t)p_instance->p_reg) + 0x564);
volatile uint32_t const * txenable_reg =
(volatile uint32_t *)(((uint32_t)p_instance->p_reg) + 0x568);
if (*txenable_reg == 1)
{
nrf_uarte_task_trigger(p_instance->p_reg, NRF_UARTE_TASK_STOPTX);
}
if (*rxenable_reg == 1)
{
nrf_uarte_enable(p_instance->p_reg);
nrf_uarte_task_trigger(p_instance->p_reg, NRF_UARTE_TASK_STOPRX);
while (*rxenable_reg)
{}
(void)nrf_uarte_errorsrc_get_and_clear(p_instance->p_reg);
nrf_uarte_disable(p_instance->p_reg);
}
#endif // defined(NRF5340_XXAA_APPLICATION) || defined(NRF5340_XXAA_NETWORK) || defined(NRF9160_XXAA)
p_cb->handler = event_handler;
p_cb->p_context = p_config->p_context;
if (p_cb->handler)
{
interrupts_enable(p_instance, p_config->interrupt_priority);
}
nrf_uarte_enable(p_instance->p_reg);
p_cb->rx_buffer_length = 0;
p_cb->rx_secondary_buffer_length = 0;
p_cb->tx_buffer_length = 0;
p_cb->state = NRFX_DRV_STATE_INITIALIZED;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
void nrfx_uarte_uninit(nrfx_uarte_t const * p_instance)
{
uarte_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
NRF_UARTE_Type * p_reg = p_instance->p_reg;
if (p_cb->handler)
{
interrupts_disable(p_instance);
}
// Make sure all transfers are finished before UARTE is disabled
// to achieve the lowest power consumption.
nrf_uarte_shorts_disable(p_reg, NRF_UARTE_SHORT_ENDRX_STARTRX);
// Check if there is any ongoing reception.
if (p_cb->rx_buffer_length)
{
nrf_uarte_event_clear(p_reg, NRF_UARTE_EVENT_RXTO);
nrf_uarte_task_trigger(p_reg, NRF_UARTE_TASK_STOPRX);
}
nrf_uarte_event_clear(p_reg, NRF_UARTE_EVENT_TXSTOPPED);
nrf_uarte_task_trigger(p_reg, NRF_UARTE_TASK_STOPTX);
// Wait for TXSTOPPED event and for RXTO event, provided that there was ongoing reception.
while (!nrf_uarte_event_check(p_reg, NRF_UARTE_EVENT_TXSTOPPED) ||
(p_cb->rx_buffer_length && !nrf_uarte_event_check(p_reg, NRF_UARTE_EVENT_RXTO)))
{}
nrf_uarte_disable(p_reg);
pins_to_default(p_instance);
#if NRFX_CHECK(NRFX_PRS_ENABLED)
nrfx_prs_release(p_reg);
#endif
p_cb->state = NRFX_DRV_STATE_UNINITIALIZED;
p_cb->handler = NULL;
NRFX_LOG_INFO("Instance uninitialized: %d.", p_instance->drv_inst_idx);
}
nrfx_err_t nrfx_uarte_tx(nrfx_uarte_t const * p_instance,
uint8_t const * p_data,
size_t length)
{
uarte_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
NRFX_ASSERT(p_cb->state == NRFX_DRV_STATE_INITIALIZED);
NRFX_ASSERT(p_data);
NRFX_ASSERT(length > 0);
NRFX_ASSERT(UARTE_LENGTH_VALIDATE(p_instance->drv_inst_idx, length));
nrfx_err_t err_code;
// EasyDMA requires that transfer buffers are placed in DataRAM,
// signal error if the are not.
if (!nrfx_is_in_ram(p_data))
{
err_code = NRFX_ERROR_INVALID_ADDR;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
if (nrfx_uarte_tx_in_progress(p_instance))
{
err_code = NRFX_ERROR_BUSY;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
p_cb->tx_buffer_length = length;
p_cb->p_tx_buffer = p_data;
NRFX_LOG_INFO("Transfer tx_len: %d.", p_cb->tx_buffer_length);
NRFX_LOG_DEBUG("Tx data:");
NRFX_LOG_HEXDUMP_DEBUG(p_cb->p_tx_buffer,
p_cb->tx_buffer_length * sizeof(p_cb->p_tx_buffer[0]));
err_code = NRFX_SUCCESS;
nrf_uarte_event_clear(p_instance->p_reg, NRF_UARTE_EVENT_ENDTX);
nrf_uarte_event_clear(p_instance->p_reg, NRF_UARTE_EVENT_TXSTOPPED);
nrf_uarte_tx_buffer_set(p_instance->p_reg, p_cb->p_tx_buffer, p_cb->tx_buffer_length);
nrf_uarte_task_trigger(p_instance->p_reg, NRF_UARTE_TASK_STARTTX);
if (p_cb->handler == NULL)
{
bool endtx;
bool txstopped;
do
{
endtx = nrf_uarte_event_check(p_instance->p_reg, NRF_UARTE_EVENT_ENDTX);
txstopped = nrf_uarte_event_check(p_instance->p_reg, NRF_UARTE_EVENT_TXSTOPPED);
}
while ((!endtx) && (!txstopped));
if (txstopped)
{
err_code = NRFX_ERROR_FORBIDDEN;
}
else
{
// Transmitter has to be stopped by triggering the STOPTX task to achieve
// the lowest possible level of the UARTE power consumption.
nrf_uarte_task_trigger(p_instance->p_reg, NRF_UARTE_TASK_STOPTX);
while (!nrf_uarte_event_check(p_instance->p_reg, NRF_UARTE_EVENT_TXSTOPPED))
{}
}
p_cb->tx_buffer_length = 0;
}
NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
bool nrfx_uarte_tx_in_progress(nrfx_uarte_t const * p_instance)
{
return (m_cb[p_instance->drv_inst_idx].tx_buffer_length != 0);
}
nrfx_err_t nrfx_uarte_rx(nrfx_uarte_t const * p_instance,
uint8_t * p_data,
size_t length)
{
uarte_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
NRFX_ASSERT(m_cb[p_instance->drv_inst_idx].state == NRFX_DRV_STATE_INITIALIZED);
NRFX_ASSERT(p_data);
NRFX_ASSERT(length > 0);
NRFX_ASSERT(UARTE_LENGTH_VALIDATE(p_instance->drv_inst_idx, length));
nrfx_err_t err_code;
// EasyDMA requires that transfer buffers are placed in DataRAM,
// signal error if the are not.
if (!nrfx_is_in_ram(p_data))
{
err_code = NRFX_ERROR_INVALID_ADDR;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
bool second_buffer = false;
if (p_cb->handler)
{
nrf_uarte_int_disable(p_instance->p_reg, NRF_UARTE_INT_ERROR_MASK |
NRF_UARTE_INT_ENDRX_MASK);
}
if (p_cb->rx_buffer_length != 0)
{
if (p_cb->rx_secondary_buffer_length != 0)
{
if (p_cb->handler)
{
nrf_uarte_int_enable(p_instance->p_reg, NRF_UARTE_INT_ERROR_MASK |
NRF_UARTE_INT_ENDRX_MASK);
}
err_code = NRFX_ERROR_BUSY;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
second_buffer = true;
}
if (!second_buffer)
{
p_cb->rx_buffer_length = length;
p_cb->p_rx_buffer = p_data;
p_cb->rx_secondary_buffer_length = 0;
}
else
{
p_cb->p_rx_secondary_buffer = p_data;
p_cb->rx_secondary_buffer_length = length;
}
NRFX_LOG_INFO("Transfer rx_len: %d.", length);
err_code = NRFX_SUCCESS;
nrf_uarte_event_clear(p_instance->p_reg, NRF_UARTE_EVENT_ENDRX);
nrf_uarte_event_clear(p_instance->p_reg, NRF_UARTE_EVENT_RXTO);
nrf_uarte_rx_buffer_set(p_instance->p_reg, p_data, length);
if (!second_buffer)
{
nrf_uarte_task_trigger(p_instance->p_reg, NRF_UARTE_TASK_STARTRX);
}
else
{
nrf_uarte_shorts_enable(p_instance->p_reg, NRF_UARTE_SHORT_ENDRX_STARTRX);
}
if (m_cb[p_instance->drv_inst_idx].handler == NULL)
{
bool endrx;
bool rxto;
bool error;
do {
endrx = nrf_uarte_event_check(p_instance->p_reg, NRF_UARTE_EVENT_ENDRX);
rxto = nrf_uarte_event_check(p_instance->p_reg, NRF_UARTE_EVENT_RXTO);
error = nrf_uarte_event_check(p_instance->p_reg, NRF_UARTE_EVENT_ERROR);
} while ((!endrx) && (!rxto) && (!error));
m_cb[p_instance->drv_inst_idx].rx_buffer_length = 0;
if (error)
{
err_code = NRFX_ERROR_INTERNAL;
}
if (rxto)
{
err_code = NRFX_ERROR_FORBIDDEN;
}
}
else
{
nrf_uarte_int_enable(p_instance->p_reg, NRF_UARTE_INT_ERROR_MASK |
NRF_UARTE_INT_ENDRX_MASK);
}
NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
bool nrfx_uarte_rx_ready(nrfx_uarte_t const * p_instance)
{
return nrf_uarte_event_check(p_instance->p_reg, NRF_UARTE_EVENT_ENDRX);
}
uint32_t nrfx_uarte_errorsrc_get(nrfx_uarte_t const * p_instance)
{
nrf_uarte_event_clear(p_instance->p_reg, NRF_UARTE_EVENT_ERROR);
return nrf_uarte_errorsrc_get_and_clear(p_instance->p_reg);
}
static void rx_done_event(uarte_control_block_t * p_cb,
size_t bytes,
uint8_t * p_data)
{
nrfx_uarte_event_t event;
event.type = NRFX_UARTE_EVT_RX_DONE;
event.data.rxtx.bytes = bytes;
event.data.rxtx.p_data = p_data;
p_cb->handler(&event, p_cb->p_context);
}
static void tx_done_event(uarte_control_block_t * p_cb,
size_t bytes)
{
nrfx_uarte_event_t event;
event.type = NRFX_UARTE_EVT_TX_DONE;
event.data.rxtx.bytes = bytes;
event.data.rxtx.p_data = (uint8_t *)p_cb->p_tx_buffer;
p_cb->tx_buffer_length = 0;
p_cb->handler(&event, p_cb->p_context);
}
void nrfx_uarte_tx_abort(nrfx_uarte_t const * p_instance)
{
uarte_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
nrf_uarte_event_clear(p_instance->p_reg, NRF_UARTE_EVENT_TXSTOPPED);
nrf_uarte_task_trigger(p_instance->p_reg, NRF_UARTE_TASK_STOPTX);
if (p_cb->handler == NULL)
{
while (!nrf_uarte_event_check(p_instance->p_reg, NRF_UARTE_EVENT_TXSTOPPED))
{}
}
NRFX_LOG_INFO("TX transaction aborted.");
}
void nrfx_uarte_rx_abort(nrfx_uarte_t const * p_instance)
{
uarte_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
// Short between ENDRX event and STARTRX task must be disabled before
// aborting transmission.
if (p_cb->rx_secondary_buffer_length != 0)
{
nrf_uarte_shorts_disable(p_instance->p_reg, NRF_UARTE_SHORT_ENDRX_STARTRX);
}
nrf_uarte_task_trigger(p_instance->p_reg, NRF_UARTE_TASK_STOPRX);
NRFX_LOG_INFO("RX transaction aborted.");
}
static void uarte_irq_handler(NRF_UARTE_Type * p_uarte,
uarte_control_block_t * p_cb)
{
if (nrf_uarte_event_check(p_uarte, NRF_UARTE_EVENT_ERROR))
{
nrfx_uarte_event_t event;
nrf_uarte_event_clear(p_uarte, NRF_UARTE_EVENT_ERROR);
event.type = NRFX_UARTE_EVT_ERROR;
event.data.error.error_mask = nrf_uarte_errorsrc_get_and_clear(p_uarte);
event.data.error.rxtx.bytes = nrf_uarte_rx_amount_get(p_uarte);
event.data.error.rxtx.p_data = p_cb->p_rx_buffer;
// Abort transfer.
p_cb->rx_buffer_length = 0;
p_cb->rx_secondary_buffer_length = 0;
p_cb->handler(&event, p_cb->p_context);
}
else if (nrf_uarte_event_check(p_uarte, NRF_UARTE_EVENT_ENDRX))
{
nrf_uarte_event_clear(p_uarte, NRF_UARTE_EVENT_ENDRX);
size_t amount = nrf_uarte_rx_amount_get(p_uarte);
// If the transfer was stopped before completion, amount of transfered bytes
// will not be equal to the buffer length. Interrupted transfer is ignored.
if (amount == p_cb->rx_buffer_length)
{
if (p_cb->rx_secondary_buffer_length != 0)
{
uint8_t * p_data = p_cb->p_rx_buffer;
nrf_uarte_shorts_disable(p_uarte, NRF_UARTE_SHORT_ENDRX_STARTRX);
p_cb->rx_buffer_length = p_cb->rx_secondary_buffer_length;
p_cb->p_rx_buffer = p_cb->p_rx_secondary_buffer;
p_cb->rx_secondary_buffer_length = 0;
rx_done_event(p_cb, amount, p_data);
}
else
{
p_cb->rx_buffer_length = 0;
rx_done_event(p_cb, amount, p_cb->p_rx_buffer);
}
}
}
if (nrf_uarte_event_check(p_uarte, NRF_UARTE_EVENT_RXTO))
{
nrf_uarte_event_clear(p_uarte, NRF_UARTE_EVENT_RXTO);
if (p_cb->rx_buffer_length != 0)
{
p_cb->rx_buffer_length = 0;
// In case of using double-buffered reception both variables storing buffer length
// have to be cleared to prevent incorrect behaviour of the driver.
p_cb->rx_secondary_buffer_length = 0;
rx_done_event(p_cb, nrf_uarte_rx_amount_get(p_uarte), p_cb->p_rx_buffer);
}
}
if (nrf_uarte_event_check(p_uarte, NRF_UARTE_EVENT_ENDTX))
{
nrf_uarte_event_clear(p_uarte, NRF_UARTE_EVENT_ENDTX);
// Transmitter has to be stopped by triggering STOPTX task to achieve
// the lowest possible level of the UARTE power consumption.
nrf_uarte_task_trigger(p_uarte, NRF_UARTE_TASK_STOPTX);
if (p_cb->tx_buffer_length != 0)
{
tx_done_event(p_cb, nrf_uarte_tx_amount_get(p_uarte));
}
}
if (nrf_uarte_event_check(p_uarte, NRF_UARTE_EVENT_TXSTOPPED))
{
nrf_uarte_event_clear(p_uarte, NRF_UARTE_EVENT_TXSTOPPED);
if (p_cb->tx_buffer_length != 0)
{
tx_done_event(p_cb, nrf_uarte_tx_amount_get(p_uarte));
}
}
}
#if NRFX_CHECK(NRFX_UARTE0_ENABLED)
void nrfx_uarte_0_irq_handler(void)
{
uarte_irq_handler(NRF_UARTE0, &m_cb[NRFX_UARTE0_INST_IDX]);
}
#endif
#if NRFX_CHECK(NRFX_UARTE1_ENABLED)
void nrfx_uarte_1_irq_handler(void)
{
uarte_irq_handler(NRF_UARTE1, &m_cb[NRFX_UARTE1_INST_IDX]);
}
#endif
#if NRFX_CHECK(NRFX_UARTE2_ENABLED)
void nrfx_uarte_2_irq_handler(void)
{
uarte_irq_handler(NRF_UARTE2, &m_cb[NRFX_UARTE2_INST_IDX]);
}
#endif
#if NRFX_CHECK(NRFX_UARTE3_ENABLED)
void nrfx_uarte_3_irq_handler(void)
{
uarte_irq_handler(NRF_UARTE3, &m_cb[NRFX_UARTE3_INST_IDX]);
}
#endif
#endif // NRFX_CHECK(NRFX_UARTE_ENABLED)
+166
View File
@@ -0,0 +1,166 @@
/**
* Copyright (c) 2015 - 2021, 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, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, 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 its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA 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.
*
*/
#include <nrfx.h>
#if NRFX_CHECK(NRFX_WDT_ENABLED)
#include <nrfx_wdt.h>
#define NRFX_LOG_MODULE WDT
#include <nrfx_log.h>
/**@brief WDT state. */
static nrfx_drv_state_t m_state;
/**@brief WDT alloc table. */
static uint8_t m_alloc_index;
#if !NRFX_CHECK(NRFX_WDT_CONFIG_NO_IRQ)
/**@brief WDT event handler. */
static nrfx_wdt_event_handler_t m_wdt_event_handler;
/**@brief WDT interrupt handler. */
void nrfx_wdt_irq_handler(void)
{
if (nrf_wdt_event_check(NRF_WDT_EVENT_TIMEOUT))
{
m_wdt_event_handler();
nrf_wdt_event_clear(NRF_WDT_EVENT_TIMEOUT);
}
}
#endif
nrfx_err_t nrfx_wdt_init(nrfx_wdt_config_t const * p_config,
nrfx_wdt_event_handler_t wdt_event_handler)
{
NRFX_ASSERT(p_config);
nrfx_err_t err_code;
#if !NRFX_CHECK(NRFX_WDT_CONFIG_NO_IRQ)
NRFX_ASSERT(wdt_event_handler != NULL);
m_wdt_event_handler = wdt_event_handler;
#else
NRFX_ASSERT(wdt_event_handler == NULL);
(void)wdt_event_handler;
#endif
if (m_state == NRFX_DRV_STATE_UNINITIALIZED)
{
m_state = NRFX_DRV_STATE_INITIALIZED;
}
else
{
err_code = NRFX_ERROR_INVALID_STATE;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
nrf_wdt_behaviour_set(p_config->behaviour);
uint64_t ticks = (p_config->reload_value * 32768ULL) / 1000;
NRFX_ASSERT(ticks <= UINT32_MAX);
nrf_wdt_reload_value_set((uint32_t) ticks);
#if !NRFX_CHECK(NRFX_WDT_CONFIG_NO_IRQ)
NRFX_IRQ_PRIORITY_SET(WDT_IRQn, p_config->interrupt_priority);
NRFX_IRQ_ENABLE(WDT_IRQn);
#endif
err_code = NRFX_SUCCESS;
NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
void nrfx_wdt_enable(void)
{
NRFX_ASSERT(m_alloc_index != 0);
NRFX_ASSERT(m_state == NRFX_DRV_STATE_INITIALIZED);
#if !NRFX_CHECK(NRFX_WDT_CONFIG_NO_IRQ)
nrf_wdt_int_enable(NRF_WDT_INT_TIMEOUT_MASK);
#endif
nrf_wdt_task_trigger(NRF_WDT_TASK_START);
m_state = NRFX_DRV_STATE_POWERED_ON;
NRFX_LOG_INFO("Enabled.");
}
void nrfx_wdt_feed(void)
{
NRFX_ASSERT(m_state == NRFX_DRV_STATE_POWERED_ON);
for (uint8_t i = 0; i < m_alloc_index; i++)
{
nrf_wdt_reload_request_set((nrf_wdt_rr_register_t)(NRF_WDT_RR0 + i));
}
}
nrfx_err_t nrfx_wdt_channel_alloc(nrfx_wdt_channel_id * p_channel_id)
{
nrfx_err_t result;
NRFX_ASSERT(p_channel_id);
NRFX_ASSERT(m_state == NRFX_DRV_STATE_INITIALIZED);
NRFX_CRITICAL_SECTION_ENTER();
if (m_alloc_index < NRF_WDT_CHANNEL_NUMBER)
{
*p_channel_id = (nrfx_wdt_channel_id)(NRF_WDT_RR0 + m_alloc_index);
m_alloc_index++;
nrf_wdt_reload_request_enable(*p_channel_id);
result = NRFX_SUCCESS;
}
else
{
result = NRFX_ERROR_NO_MEM;
}
NRFX_CRITICAL_SECTION_EXIT();
NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(result));
return result;
}
void nrfx_wdt_channel_feed(nrfx_wdt_channel_id channel_id)
{
NRFX_ASSERT(m_state == NRFX_DRV_STATE_POWERED_ON);
nrf_wdt_reload_request_set(channel_id);
}
#endif // NRFX_CHECK(NRFX_WDT_ENABLED)
+166
View File
@@ -0,0 +1,166 @@
/**
* Copyright (c) 2017 - 2021, 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, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, 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 its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA 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.
*
*/
#include <nrfx.h>
#if NRFX_CHECK(NRFX_PRS_ENABLED)
#include "nrfx_prs.h"
#define NRFX_LOG_MODULE PRS
#include <nrfx_log.h>
#define LOG_FUNCTION_EXIT(level, ret_code) \
NRFX_LOG_##level("Function: %s, error code: %s.", \
__func__, \
NRFX_LOG_ERROR_STRING_GET(ret_code))
typedef struct {
nrfx_irq_handler_t handler;
bool acquired;
} prs_box_t;
#define PRS_BOX_DEFINE(n) \
static prs_box_t m_prs_box_##n = { .handler = NULL, .acquired = false }; \
void nrfx_prs_box_##n##_irq_handler(void) \
{ \
NRFX_ASSERT(m_prs_box_##n.handler); \
m_prs_box_##n.handler(); \
}
#if defined(NRFX_PRS_BOX_0_ADDR) && NRFX_CHECK(NRFX_PRS_BOX_0_ENABLED)
PRS_BOX_DEFINE(0)
#endif
#if defined(NRFX_PRS_BOX_1_ADDR) && NRFX_CHECK(NRFX_PRS_BOX_1_ENABLED)
PRS_BOX_DEFINE(1)
#endif
#if defined(NRFX_PRS_BOX_2_ADDR) && NRFX_CHECK(NRFX_PRS_BOX_2_ENABLED)
PRS_BOX_DEFINE(2)
#endif
#if defined(NRFX_PRS_BOX_3_ADDR) && NRFX_CHECK(NRFX_PRS_BOX_3_ENABLED)
PRS_BOX_DEFINE(3)
#endif
#if defined(NRFX_PRS_BOX_4_ADDR) && NRFX_CHECK(NRFX_PRS_BOX_4_ENABLED)
PRS_BOX_DEFINE(4)
#endif
static prs_box_t * prs_box_get(void const * p_base_addr)
{
#if !defined(IS_PRS_BOX)
#define IS_PRS_BOX(n, p_base_addr) ((p_base_addr) == NRFX_PRS_BOX_##n##_ADDR)
#endif
#if defined(NRFX_PRS_BOX_0_ADDR) && NRFX_CHECK(NRFX_PRS_BOX_0_ENABLED)
if (IS_PRS_BOX(0, p_base_addr)) { return &m_prs_box_0; }
else
#endif
#if defined(NRFX_PRS_BOX_1_ADDR) && NRFX_CHECK(NRFX_PRS_BOX_1_ENABLED)
if (IS_PRS_BOX(1, p_base_addr)) { return &m_prs_box_1; }
else
#endif
#if defined(NRFX_PRS_BOX_2_ADDR) && NRFX_CHECK(NRFX_PRS_BOX_2_ENABLED)
if (IS_PRS_BOX(2, p_base_addr)) { return &m_prs_box_2; }
else
#endif
#if defined(NRFX_PRS_BOX_3_ADDR) && NRFX_CHECK(NRFX_PRS_BOX_3_ENABLED)
if (IS_PRS_BOX(3, p_base_addr)) { return &m_prs_box_3; }
else
#endif
#if defined(NRFX_PRS_BOX_4_ADDR) && NRFX_CHECK(NRFX_PRS_BOX_4_ENABLED)
if (IS_PRS_BOX(4, p_base_addr)) { return &m_prs_box_4; }
else
#endif
{
return NULL;
}
}
nrfx_err_t nrfx_prs_acquire(void const * p_base_addr,
nrfx_irq_handler_t irq_handler)
{
NRFX_ASSERT(p_base_addr);
nrfx_err_t ret_code;
prs_box_t * p_box = prs_box_get(p_base_addr);
if (p_box != NULL)
{
bool busy = false;
NRFX_CRITICAL_SECTION_ENTER();
if (p_box->acquired)
{
busy = true;
}
else
{
p_box->handler = irq_handler;
p_box->acquired = true;
}
NRFX_CRITICAL_SECTION_EXIT();
if (busy)
{
ret_code = NRFX_ERROR_BUSY;
LOG_FUNCTION_EXIT(WARNING, ret_code);
return ret_code;
}
}
ret_code = NRFX_SUCCESS;
LOG_FUNCTION_EXIT(INFO, ret_code);
return ret_code;
}
void nrfx_prs_release(void const * p_base_addr)
{
NRFX_ASSERT(p_base_addr);
prs_box_t * p_box = prs_box_get(p_base_addr);
if (p_box != NULL)
{
p_box->handler = NULL;
p_box->acquired = false;
}
}
#endif // NRFX_CHECK(NRFX_PRS_ENABLED)
+155
View File
@@ -0,0 +1,155 @@
/**
* Copyright (c) 2017 - 2021, 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, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, 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 its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA 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 NRFX_PRS_H__
#define NRFX_PRS_H__
#include <nrfx.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @defgroup nrfx_prs Peripheral Resource Sharing (PRS)
* @{
* @ingroup nrfx
*
* @brief Peripheral Resource Sharing interface (PRS).
*/
#if defined(NRF51)
// SPI0, TWI0
#define NRFX_PRS_BOX_0_ADDR NRF_SPI0
// SPI1, SPIS1, TWI1
#define NRFX_PRS_BOX_1_ADDR NRF_SPI1
#elif defined(NRF52810_XXAA)
// TWIM0, TWIS0, TWI0
#define NRFX_PRS_BOX_0_ADDR NRF_TWIM0
// SPIM0, SPIS0, SPI0
#define NRFX_PRS_BOX_1_ADDR NRF_SPIM0
// UARTE0, UART0
#define NRFX_PRS_BOX_2_ADDR NRF_UARTE0
#elif defined(NRF52811_XXAA)
// TWIM0, TWIS0, TWI0, SPIM1, SPIS1, SPI1
#define NRFX_PRS_BOX_0_ADDR NRF_TWIM0
// SPIM0, SPIS0, SPI0
#define NRFX_PRS_BOX_1_ADDR NRF_SPIM0
// UART0, UARTE0
#define NRFX_PRS_BOX_2_ADDR NRF_UART0
#elif defined(NRF52820_XXAA)
// SPIM0, SPIS0, TWIM0, TWIS0, SPI0, TWI0
#define NRFX_PRS_BOX_0_ADDR NRF_SPIM0
// SPIM1, SPIS1, TWIM1, TWIS1, SPI1, TWI1
#define NRFX_PRS_BOX_1_ADDR NRF_SPIM1
// UARTE0, UART0
#define NRFX_PRS_BOX_2_ADDR NRF_UARTE0
#elif defined(NRF52832_XXAA) || defined(NRF52832_XXAB) || \
defined(NRF52833_XXAA) || defined(NRF52840_XXAA)
// SPIM0, SPIS0, TWIM0, TWIS0, SPI0, TWI0
#define NRFX_PRS_BOX_0_ADDR NRF_SPIM0
// SPIM1, SPIS1, TWIM1, TWIS1, SPI1, TWI1
#define NRFX_PRS_BOX_1_ADDR NRF_SPIM1
// SPIM2, SPIS2, SPI2
#define NRFX_PRS_BOX_2_ADDR NRF_SPIM2
// COMP, LPCOMP
#define NRFX_PRS_BOX_3_ADDR NRF_COMP
// UARTE0, UART0
#define NRFX_PRS_BOX_4_ADDR NRF_UARTE0
#elif defined(NRF9160_XXAA)
// UARTE0, SPIM0, SPIS0, TWIM0, TWIS0
#define NRFX_PRS_BOX_0_ADDR NRF_UARTE0
// UARTE1, SPIM1, SPIS1, TWIM1, TWIS1
#define NRFX_PRS_BOX_1_ADDR NRF_UARTE1
// UARTE2, SPIM2, SPIS2, TWIM2, TWIS2
#define NRFX_PRS_BOX_2_ADDR NRF_UARTE2
// UARTE3, SPIM3, SPIS3, TWIM3, TWIS3
#define NRFX_PRS_BOX_3_ADDR NRF_UARTE3
#else
#error "Unknown device."
#endif
/**
* @brief Function for acquiring shared peripheral resources associated with
* the specified peripheral.
*
* Certain resources and registers are shared among peripherals that have
* the same ID (for example: SPI0, SPIM0, SPIS0, TWI0, TWIM0, and TWIS0 in
* nRF52832). Only one of them can be utilized at a given time. This function
* reserves proper resources to be used by the specified peripheral.
* If NRFX_PRS_ENABLED is set to a non-zero value, IRQ handlers for peripherals
* that are sharing resources with others are implemented by the @ref nrfx_prs
* module instead of individual drivers. The drivers must then specify their
* interrupt handling routines and register them by using this function.
*
* @param[in] p_base_addr Requested peripheral base pointer.
* @param[in] irq_handler Interrupt handler to register.
*
* @retval NRFX_SUCCESS If resources were acquired successfully or the
* specified peripheral is not handled by the PRS
* subsystem and there is no need to acquire resources
* for it.
* @retval NRFX_ERROR_BUSY If resources were already acquired.
*/
nrfx_err_t nrfx_prs_acquire(void const * p_base_addr,
nrfx_irq_handler_t irq_handler);
/**
* @brief Function for releasing shared resources reserved previously by
* @ref nrfx_prs_acquire() for the specified peripheral.
*
* @param[in] p_base_addr Released peripheral base pointer.
*/
void nrfx_prs_release(void const * p_base_addr);
/** @} */
void nrfx_prs_box_0_irq_handler(void);
void nrfx_prs_box_1_irq_handler(void);
void nrfx_prs_box_2_irq_handler(void);
void nrfx_prs_box_3_irq_handler(void);
void nrfx_prs_box_4_irq_handler(void);
#ifdef __cplusplus
}
#endif
#endif // NRFX_PRS_H__