初始版本

This commit is contained in:
xiaozhengsheng
2025-08-19 09:49:41 +08:00
parent 10f1ddf1c1
commit 6df0f7d96e
2974 changed files with 1712873 additions and 54 deletions

View File

@@ -0,0 +1,159 @@
/**
* Copyright (c) 2016 - 2020, 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

View File

@@ -0,0 +1,289 @@
/**
* Copyright (c) 2015 - 2020, 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_ADC_H__
#define NRFX_ADC_H__
#include <nrfx.h>
#include <hal/nrf_adc.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @defgroup nrfx_adc ADC driver
* @{
* @ingroup nrf_adc
* @brief Analog-to-Digital Converter (ADC) peripheral driver.
*/
/** @brief Driver event types. */
typedef enum
{
NRFX_ADC_EVT_DONE, ///< Event generated when the buffer is filled with samples.
NRFX_ADC_EVT_SAMPLE, ///< Event generated when the requested channel is sampled.
} nrfx_adc_evt_type_t;
/** @brief ADC driver DONE event structure. */
typedef struct
{
nrf_adc_value_t * p_buffer; ///< Pointer to the buffer with converted samples.
uint16_t size; ///< Number of samples in the buffer.
} nrfx_adc_done_evt_t;
/** @brief SAMPLE event structure. */
typedef struct
{
nrf_adc_value_t sample; ///< Converted sample.
} nrfx_adc_sample_evt_t;
/** @brief ADC driver event. */
typedef struct
{
nrfx_adc_evt_type_t type; ///< Event type.
union
{
nrfx_adc_done_evt_t done; ///< Data for DONE event.
nrfx_adc_sample_evt_t sample; ///< Data for SAMPLE event.
} data; ///< Union to store event data.
} nrfx_adc_evt_t;
/** @brief Macro for initializing the ADC channel with the default configuration. */
#define NRFX_ADC_DEFAULT_CHANNEL(analog_input) \
{ \
NULL, \
{ \
.resolution = NRF_ADC_CONFIG_RES_10BIT, \
.scaling = NRF_ADC_CONFIG_SCALING_INPUT_FULL_SCALE, \
.reference = NRF_ADC_CONFIG_REF_VBG, \
.input = (analog_input), \
.extref = NRF_ADC_CONFIG_EXTREFSEL_NONE \
} \
}
/** @brief Forward declaration of the nrfx_adc_channel_t type. */
typedef struct nrfx_adc_channel_s nrfx_adc_channel_t;
/**
* @brief ADC channel.
*
* This structure is defined by the user and used by the driver. Therefore, it should
* not be defined on the stack as a local variable.
*/
struct nrfx_adc_channel_s
{
nrfx_adc_channel_t * p_next; ///< Pointer to the next enabled channel (for internal use).
nrf_adc_config_t config; ///< ADC configuration for the current channel.
};
/** @brief ADC configuration. */
typedef struct
{
uint8_t interrupt_priority; ///< Priority of ADC interrupt.
} nrfx_adc_config_t;
/** @brief ADC default configuration. */
#define NRFX_ADC_DEFAULT_CONFIG \
{ \
.interrupt_priority = NRFX_ADC_CONFIG_IRQ_PRIORITY \
}
/**
* @brief User event handler prototype.
*
* This function is called when the requested number of samples has been processed.
*
* @param p_event Event.
*/
typedef void (*nrfx_adc_event_handler_t)(nrfx_adc_evt_t const * p_event);
/**
* @brief Function for initializing the ADC.
*
* If a valid event handler is provided, the driver is initialized in non-blocking mode.
* If event_handler is NULL, the driver works in blocking mode.
*
* @param[in] p_config Pointer to the structure with the initial configuration.
* @param[in] event_handler Event handler provided by the user.
*
* @retval NRFX_SUCCESS Initialization was successful.
* @retval NRFX_ERROR_INVALID_STATE The driver is already initialized.
*/
nrfx_err_t nrfx_adc_init(nrfx_adc_config_t const * p_config,
nrfx_adc_event_handler_t event_handler);
/**
* @brief Function for uninitializing the ADC.
*
* This function stops all ongoing conversions and disables all channels.
*/
void nrfx_adc_uninit(void);
/**
* @brief Function for enabling an ADC channel.
*
* This function configures and enables the channel. When @ref nrfx_adc_buffer_convert is
* called, all channels that have been enabled with this function are sampled.
*
* This function can be called only when there is no conversion in progress
* (the ADC is not busy).
*
* @note The channel instance variable @p p_channel is used by the driver as an item
* in a list. Therefore, it cannot be an automatic variable that is located on the stack.
*
* @param[in] p_channel Pointer to the channel instance.
*/
void nrfx_adc_channel_enable(nrfx_adc_channel_t * const p_channel);
/**
* @brief Function for disabling an ADC channel.
*
* This function can be called only when there is no conversion in progress
* (the ADC is not busy).
*
* @param p_channel Pointer to the channel instance.
*/
void nrfx_adc_channel_disable(nrfx_adc_channel_t * const p_channel);
/**
* @brief Function for disabling all ADC channels.
*
* This function can be called only when there is no conversion in progress
* (the ADC is not busy).
*/
void nrfx_adc_all_channels_disable(void);
/**
* @brief Function for starting ADC sampling.
*
* This function triggers single ADC sampling. If more than one channel is enabled, the driver
* emulates scanning and all channels are sampled in the order they were enabled.
*/
void nrfx_adc_sample(void);
/**
* @brief Function for executing a single ADC conversion.
*
* This function selects the desired input and starts a single conversion. If a valid pointer
* is provided for the result, the function blocks until the conversion is completed. Otherwise, the
* function returns when the conversion is started, and the result is provided in an event (driver
* must be initialized in non-blocking mode, otherwise an assertion will fail). The function will
* fail if ADC is busy. The channel does not need to be enabled to perform a single conversion.
*
* @param[in] p_channel Channel.
* @param[out] p_value Pointer to the location where the result is to be placed. Unless NULL is
* provided, the function is blocking.
*
* @retval NRFX_SUCCESS Conversion was successful.
* @retval NRFX_ERROR_BUSY The ADC driver is busy.
*/
nrfx_err_t nrfx_adc_sample_convert(nrfx_adc_channel_t const * const p_channel,
nrf_adc_value_t * p_value);
/**
* @brief Function for converting data to the buffer.
*
* If the driver is initialized in non-blocking mode, this function returns when the first
* conversion is set up. When the buffer is filled, the application is notified by the event
* handler. If the driver is initialized in blocking mode, the function returns when the buffer is
* filled.
*
* Conversion is done on all enabled channels, but it is not triggered by this
* function. This function will prepare the ADC for sampling and then
* wait for the SAMPLE task. Sampling can be triggered manually by the @ref
* nrfx_adc_sample function or by PPI using the @ref NRF_ADC_TASK_START task.
*
* @note If more than one channel is enabled, the function emulates scanning, and
* a single START task will trigger conversion on all enabled channels. For example:
* If 3 channels are enabled and the user requests 6 samples, the completion event
* handler will be called after 2 START tasks.
*
* @note The application must adjust the sampling frequency. The maximum frequency
* depends on the sampling timer and the maximum latency of the ADC interrupt. If
* an interrupt is not handled before the next sampling is triggered, the sample
* will be lost.
*
* @param[in] buffer Result buffer.
* @param[in] size Buffer size in samples.
*
* @retval NRFX_SUCCESS Conversion was successful.
* @retval NRFX_ERROR_BUSY The driver is busy.
*/
nrfx_err_t nrfx_adc_buffer_convert(nrf_adc_value_t * buffer, uint16_t size);
/**
* @brief Function for retrieving the ADC state.
*
* @retval true The ADC is busy.
* @retval false The ADC is ready.
*/
bool nrfx_adc_is_busy(void);
/**
* @brief Function for getting the address of the ADC START task.
*
* This function is used to get the address of the START task, which can be used to trigger ADC
* conversion.
*
* @return Start task address.
*/
__STATIC_INLINE uint32_t nrfx_adc_start_task_get(void);
#ifndef SUPPRESS_INLINE_IMPLEMENTATION
__STATIC_INLINE uint32_t nrfx_adc_start_task_get(void)
{
return nrf_adc_task_address_get(NRF_ADC_TASK_START);
}
#endif
/** @} */
void nrfx_adc_irq_handler(void);
#ifdef __cplusplus
}
#endif
#endif // NRFX_ADC_H__

View File

@@ -0,0 +1,206 @@
/**
* Copyright (c) 2016 - 2020, 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__

View File

@@ -0,0 +1,248 @@
/**
* Copyright (c) 2015 - 2020, 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_COMP_H__
#define NRFX_COMP_H__
#include <nrfx.h>
#include <hal/nrf_comp.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @defgroup nrfx_comp COMP driver
* @{
* @ingroup nrf_comp
* @brief Comparator (COMP) peripheral driver.
*/
/**
* @brief Macro for converting the threshold voltage to an integer value
* (needed by the COMP_TH register).
*
* @param[in] vol Voltage to be changed to COMP_TH register value. This value
* must not be smaller than reference voltage divided by 64.
* @param[in] ref Reference voltage.
*/
#define NRFX_VOLTAGE_THRESHOLD_TO_INT(vol, ref) \
(uint8_t)(((vol) > ((ref) / 64)) ? (NRFX_ROUNDED_DIV((vol) * 64,(ref)) - 1) : 0)
/**
* @brief COMP event handler function type.
*
* @param[in] event COMP event.
*/
typedef void (* nrfx_comp_event_handler_t)(nrf_comp_event_t event);
/** @brief COMP shortcut masks. */
typedef enum
{
NRFX_COMP_SHORT_STOP_AFTER_CROSS_EVT = COMP_SHORTS_CROSS_STOP_Msk, /*!< Shortcut between the CROSS event and the STOP task. */
NRFX_COMP_SHORT_STOP_AFTER_UP_EVT = COMP_SHORTS_UP_STOP_Msk, /*!< Shortcut between the UP event and the STOP task. */
NRFX_COMP_SHORT_STOP_AFTER_DOWN_EVT = COMP_SHORTS_DOWN_STOP_Msk /*!< Shortcut between the DOWN event and the STOP task. */
} nrfx_comp_short_mask_t;
/** @brief COMP events masks. */
typedef enum
{
NRFX_COMP_EVT_EN_CROSS_MASK = COMP_INTENSET_CROSS_Msk, /*!< CROSS event (generated after VIN+ == VIN-). */
NRFX_COMP_EVT_EN_UP_MASK = COMP_INTENSET_UP_Msk, /*!< UP event (generated when VIN+ crosses VIN- while increasing). */
NRFX_COMP_EVT_EN_DOWN_MASK = COMP_INTENSET_DOWN_Msk, /*!< DOWN event (generated when VIN+ crosses VIN- while decreasing). */
NRFX_COMP_EVT_EN_READY_MASK = COMP_INTENSET_READY_Msk /*!< READY event (generated when the module is ready). */
} nrfx_comp_evt_en_mask_t;
/** @brief COMP configuration. */
typedef struct
{
nrf_comp_ref_t reference; /**< Reference selection. */
nrf_comp_ext_ref_t ext_ref; /**< External analog reference selection. */
nrf_comp_main_mode_t main_mode; /**< Main operation mode. */
nrf_comp_th_t threshold; /**< Structure holding THDOWN and THUP values needed by the COMP_TH register. */
nrf_comp_sp_mode_t speed_mode; /**< Speed and power mode. */
nrf_comp_hyst_t hyst; /**< Comparator hysteresis. */
#if defined (COMP_ISOURCE_ISOURCE_Msk) || defined (__NRFX_DOXYGEN__)
nrf_isource_t isource; /**< Current source selected on analog input. */
#endif
nrf_comp_input_t input; /**< Input to be monitored. */
uint8_t interrupt_priority; /**< Interrupt priority. */
} nrfx_comp_config_t;
/** @brief COMP threshold default configuration. */
#define NRFX_COMP_CONFIG_TH \
{ \
.th_down = NRFX_VOLTAGE_THRESHOLD_TO_INT(0.5, 1.8), \
.th_up = NRFX_VOLTAGE_THRESHOLD_TO_INT(1.5, 1.8) \
}
/** @brief COMP driver default configuration including the COMP HAL configuration. */
#if defined (COMP_ISOURCE_ISOURCE_Msk) || defined (__NRFX_DOXYGEN__)
#define NRFX_COMP_DEFAULT_CONFIG(_input) \
{ \
.reference = (nrf_comp_ref_t)NRFX_COMP_CONFIG_REF, \
.ext_ref = NRF_COMP_EXT_REF_0, \
.main_mode = (nrf_comp_main_mode_t)NRFX_COMP_CONFIG_MAIN_MODE, \
.threshold = NRFX_COMP_CONFIG_TH, \
.speed_mode = (nrf_comp_sp_mode_t)NRFX_COMP_CONFIG_SPEED_MODE, \
.hyst = (nrf_comp_hyst_t)NRFX_COMP_CONFIG_HYST, \
.isource = (nrf_isource_t)NRFX_COMP_CONFIG_ISOURCE, \
.input = (nrf_comp_input_t)_input, \
.interrupt_priority = NRFX_COMP_CONFIG_IRQ_PRIORITY \
}
#else
#define NRFX_COMP_DEFAULT_CONFIG(_input) \
{ \
.reference = (nrf_comp_ref_t)NRFX_COMP_CONFIG_REF, \
.ext_ref = NRF_COMP_EXT_REF_0, \
.main_mode = (nrf_comp_main_mode_t)NRFX_COMP_CONFIG_MAIN_MODE, \
.threshold = NRFX_COMP_CONFIG_TH, \
.speed_mode = (nrf_comp_sp_mode_t)NRFX_COMP_CONFIG_SPEED_MODE, \
.hyst = (nrf_comp_hyst_t)NRFX_COMP_CONFIG_HYST, \
.input = (nrf_comp_input_t)_input, \
.interrupt_priority = NRFX_COMP_CONFIG_IRQ_PRIORITY \
}
#endif
/**
* @brief Function for initializing the COMP driver.
*
* This function initializes the COMP driver, but does not enable the peripheral or any interrupts.
* To start the driver, call the function @ref nrfx_comp_start() after initialization.
*
* @param[in] p_config Pointer to the structure with the 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 has already been initialized.
* @retval NRFX_ERROR_BUSY The LPCOMP peripheral is already in use.
* This is possible only if @ref nrfx_prs module
* is enabled.
*/
nrfx_err_t nrfx_comp_init(nrfx_comp_config_t const * p_config,
nrfx_comp_event_handler_t event_handler);
/**
* @brief Function for uninitializing the COMP driver.
*
* This function uninitializes the COMP driver. The COMP peripheral and
* its interrupts are disabled, and local variables are cleaned. After this call, you must
* initialize the driver again by calling nrfx_comp_init() if you want to use it.
*
* @sa nrfx_comp_stop
*/
void nrfx_comp_uninit(void);
/**
* @brief Function for setting the analog input.
*
* @param[in] psel COMP analog pin selection.
*/
void nrfx_comp_pin_select(nrf_comp_input_t psel);
/**
* @brief Function for starting the COMP peripheral and interrupts.
*
* Before calling this function, the driver must be initialized. This function
* enables the COMP peripheral and its interrupts.
*
* @param[in] comp_evt_en_mask Mask of events to be enabled. This parameter is to be built as
* an OR of elements from @ref nrfx_comp_evt_en_mask_t.
* @param[in] comp_shorts_mask Mask of shortcuts to be enabled. This parameter is to be built as
* an OR of elements from @ref nrfx_comp_short_mask_t.
*
* @sa nrfx_comp_init
*/
void nrfx_comp_start(uint32_t comp_evt_en_mask, uint32_t comp_shorts_mask);
/**
* @brief Function for stopping the COMP peripheral.
*
* Before calling this function, the driver must be enabled. This function disables the COMP
* peripheral and its interrupts.
*
* @sa nrfx_comp_uninit
*/
void nrfx_comp_stop(void);
/**
* @brief Function for copying the current state of the comparator result to the RESULT register.
*
* @retval 0 The input voltage is below the threshold (VIN+ < VIN-).
* @retval 1 The input voltage is above the threshold (VIN+ > VIN-).
*/
uint32_t nrfx_comp_sample(void);
/**
* @brief Function for getting the address of a COMP task.
*
* @param[in] task COMP task.
*
* @return Address of the given COMP task.
*/
__STATIC_INLINE uint32_t nrfx_comp_task_address_get(nrf_comp_task_t task)
{
return (uint32_t)nrf_comp_task_address_get(task);
}
/**
* @brief Function for getting the address of a COMP event.
*
* @param[in] event COMP event.
*
* @return Address of the given COMP event.
*/
__STATIC_INLINE uint32_t nrfx_comp_event_address_get(nrf_comp_event_t event)
{
return (uint32_t)nrf_comp_event_address_get(event);
}
/** @} */
void nrfx_comp_irq_handler(void);
#ifdef __cplusplus
}
#endif
#endif // NRFX_COMP_H__

View File

@@ -0,0 +1,185 @@
/**
* Copyright (c) 2018 - 2020, 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_DPPI_H__
#define NRFX_DPPI_H__
#include <nrfx.h>
#include <hal/nrf_dppi.h>
/**
* @defgroup nrfx_dppi DPPI allocator
* @{
* @ingroup nrf_dppi
* @brief Distributed Programmable Peripheral Interconnect (DPPI) allocator.
*/
#ifdef __cplusplus
extern "C" {
#endif
/** @brief Function for freeing all allocated channels and groups. */
void nrfx_dppi_free(void);
/**
* @brief Function for allocating a DPPI channel.
* @details This function allocates the first unused DPPI channel.
*
* @param[out] p_channel Pointer to the DPPI channel number 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_dppi_channel_alloc(uint8_t * p_channel);
/**
* @brief Function for freeing a DPPI channel.
* @details This function also disables the chosen channel.
*
* @param[in] channel DPPI channel to be freed.
*
* @retval NRFX_SUCCESS The channel was successfully freed.
* @retval NRFX_ERROR_INVALID_PARAM The specified channel is not allocated.
*/
nrfx_err_t nrfx_dppi_channel_free(uint8_t channel);
/**
* @brief Function for enabling a DPPI channel.
*
* @param[in] channel DPPI channel to be enabled.
*
* @retval NRFX_SUCCESS The channel was successfully enabled.
* @retval NRFX_ERROR_INVALID_PARAM The specified channel is not allocated.
*/
nrfx_err_t nrfx_dppi_channel_enable(uint8_t channel);
/**
* @brief Function for disabling a DPPI channel.
*
* @param[in] channel DPPI channel to be disabled.
*
* @retval NRFX_SUCCESS The channel was successfully disabled.
* @retval NRFX_ERROR_INVALID_PARAM The specified channel is not allocated.
*/
nrfx_err_t nrfx_dppi_channel_disable(uint8_t channel);
/**
* @brief Function for allocating a DPPI channel group.
* @details This function allocates the first unused DPPI group.
*
* @param[out] p_group Pointer to the DPPI 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_dppi_group_alloc(nrf_dppi_channel_group_t * p_group);
/**
* @brief Function for freeing a DPPI channel group.
* @details This function also disables the chosen group.
*
* @param[in] group DPPI channel group to be freed.
*
* @retval NRFX_SUCCESS The channel group was successfully freed.
* @retval NRFX_ERROR_INVALID_PARAM The specified group is not allocated.
*/
nrfx_err_t nrfx_dppi_group_free(nrf_dppi_channel_group_t group);
/**
* @brief Function for including a DPPI channel in a channel group.
*
* @param[in] channel DPPI 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 The specified group or channel is not allocated.
*/
nrfx_err_t nrfx_dppi_channel_include_in_group(uint8_t channel,
nrf_dppi_channel_group_t group);
/**
* @brief Function for removing a DPPI channel from a channel group.
*
* @param[in] channel DPPI 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 The specified group or channel is not allocated.
*/
nrfx_err_t nrfx_dppi_channel_remove_from_group(uint8_t channel,
nrf_dppi_channel_group_t group);
/**
* @brief Function for clearing a DPPI channel group.
*
* @param[in] group Channel group to be cleared.
*
* @retval NRFX_SUCCESS The group was successfully cleared.
* @retval NRFX_ERROR_INVALID_PARAM The specified group is not allocated.
*/
nrfx_err_t nrfx_dppi_group_clear(nrf_dppi_channel_group_t group);
/**
* @brief Function for enabling a DPPI channel group.
*
* @param[in] group Channel group to be enabled.
*
* @retval NRFX_SUCCESS The group was successfully enabled.
* @retval NRFX_ERROR_INVALID_PARAM The specified group is not allocated.
*/
nrfx_err_t nrfx_dppi_group_enable(nrf_dppi_channel_group_t group);
/**
* @brief Function for disabling a DPPI channel group.
*
* @param[in] group Channel group to be disabled.
*
* @retval NRFX_SUCCESS The group was successfully disabled.
* @retval NRFX_ERROR_INVALID_PARAM The specified group is not allocated.
*/
nrfx_err_t nrfx_dppi_group_disable(nrf_dppi_channel_group_t group);
/** @} */
#ifdef __cplusplus
}
#endif
#endif // NRFX_DPPI_H__

View File

@@ -0,0 +1,494 @@
/**
* Copyright (c) 2015 - 2020, 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__

View File

@@ -0,0 +1,253 @@
/**
* Copyright (c) 2015 - 2020, 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_I2S_H__
#define NRFX_I2S_H__
#include <nrfx.h>
#include <hal/nrf_i2s.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @defgroup nrfx_i2s I2S driver
* @{
* @ingroup nrf_i2s
* @brief Inter-IC Sound (I2S) peripheral driver.
*/
/**
* @brief This value can be provided instead of a pin number for the signals
* SDOUT, SDIN, and MCK to specify that a given signal is not used
* and therefore does not need to be connected to a pin.
*/
#define NRFX_I2S_PIN_NOT_USED 0xFF
/** @brief I2S driver configuration structure. */
typedef struct
{
uint8_t sck_pin; ///< SCK pin number.
uint8_t lrck_pin; ///< LRCK pin number.
uint8_t mck_pin; ///< MCK pin number.
/**< Optional. Use @ref NRFX_I2S_PIN_NOT_USED
* if this signal is not needed. */
uint8_t sdout_pin; ///< SDOUT pin number.
/**< Optional. Use @ref NRFX_I2S_PIN_NOT_USED
* if this signal is not needed. */
uint8_t sdin_pin; ///< SDIN pin number.
/**< Optional. Use @ref NRFX_I2S_PIN_NOT_USED
* if this signal is not needed. */
uint8_t irq_priority; ///< Interrupt priority.
nrf_i2s_mode_t mode; ///< Mode of operation.
nrf_i2s_format_t format; ///< Frame format.
nrf_i2s_align_t alignment; ///< Alignment of sample within a frame.
nrf_i2s_swidth_t sample_width; ///< Sample width.
nrf_i2s_channels_t channels; ///< Enabled channels.
nrf_i2s_mck_t mck_setup; ///< Master clock setup.
nrf_i2s_ratio_t ratio; ///< MCK/LRCK ratio.
} nrfx_i2s_config_t;
/** @brief I2S driver buffers structure. */
typedef struct
{
uint32_t * p_rx_buffer; ///< Pointer to the buffer for received data.
uint32_t const * p_tx_buffer; ///< Pointer to the buffer with data to be sent.
} nrfx_i2s_buffers_t;
/** @brief I2S driver default configuration. */
#define NRFX_I2S_DEFAULT_CONFIG \
{ \
.sck_pin = NRFX_I2S_CONFIG_SCK_PIN, \
.lrck_pin = NRFX_I2S_CONFIG_LRCK_PIN, \
.mck_pin = NRFX_I2S_CONFIG_MCK_PIN, \
.sdout_pin = NRFX_I2S_CONFIG_SDOUT_PIN, \
.sdin_pin = NRFX_I2S_CONFIG_SDIN_PIN, \
.irq_priority = NRFX_I2S_CONFIG_IRQ_PRIORITY, \
.mode = (nrf_i2s_mode_t)NRFX_I2S_CONFIG_MASTER, \
.format = (nrf_i2s_format_t)NRFX_I2S_CONFIG_FORMAT, \
.alignment = (nrf_i2s_align_t)NRFX_I2S_CONFIG_ALIGN, \
.sample_width = (nrf_i2s_swidth_t)NRFX_I2S_CONFIG_SWIDTH, \
.channels = (nrf_i2s_channels_t)NRFX_I2S_CONFIG_CHANNELS, \
.mck_setup = (nrf_i2s_mck_t)NRFX_I2S_CONFIG_MCK_SETUP, \
.ratio = (nrf_i2s_ratio_t)NRFX_I2S_CONFIG_RATIO, \
}
#define NRFX_I2S_STATUS_NEXT_BUFFERS_NEEDED (1UL << 0)
/**< The application must provide buffers that are to be used in the next
* part of the transfer. A call to @ref nrfx_i2s_next_buffers_set must
* be done before the currently used buffers are completely processed
* (that is, the time remaining for supplying the next buffers depends on
* the used size of the buffers). */
/**
* @brief I2S driver data handler type.
*
* A data handling function of this type must be specified during the initialization
* of the driver. The driver will call this function when it finishes using
* buffers passed to it by the application, and when it needs to be provided
* with buffers for the next part of the transfer.
*
* @note The @c p_released pointer passed to this function is temporary and
* will be invalid after the function returns, hence it cannot be stored
* and used later. If needed, the pointed content (that is, buffers pointers)
* must be copied instead.
*
* @param[in] p_released Pointer to a structure with pointers to buffers
* passed previously to the driver that will no longer
* be accessed by it (they can be now safely released or
* used for another purpose, in particular for a next
* part of the transfer).
* This pointer will be NULL if the application did not
* supply the buffers for the next part of the transfer
* (via a call to @ref nrfx_i2s_next_buffers_set) since
* the previous time the data handler signaled such need.
* This means that data corruption occurred (the previous
* buffers are used for the second time) and no buffers
* can be released at the moment.
* Both pointers in this structure are NULL when the
* handler is called for the first time after a transfer
* is started, because no data has been transferred yet
* at this point. In all successive calls the pointers
* specify what has been sent (TX) and what has been
* received (RX) in the part of transfer that has just
* been completed (provided that a given direction is
* enabled, see @ref nrfx_i2s_start).
* @param[in] status Bit field describing the current status of the transfer.
* It can be 0 or a combination of the following flags:
* - @ref NRFX_I2S_STATUS_NEXT_BUFFERS_NEEDED
*/
typedef void (* nrfx_i2s_data_handler_t)(nrfx_i2s_buffers_t const * p_released,
uint32_t status);
/**
* @brief Function for initializing the I2S driver.
*
* @param[in] p_config Pointer to the structure with the initial configuration.
* @param[in] handler Data handler provided by the user. Must not be NULL.
*
* @retval NRFX_SUCCESS Initialization was successful.
* @retval NRFX_ERROR_INVALID_STATE The driver was already initialized.
* @retval NRFX_ERROR_INVALID_PARAM The requested combination of configuration
* options is not allowed by the I2S peripheral.
*/
nrfx_err_t nrfx_i2s_init(nrfx_i2s_config_t const * p_config,
nrfx_i2s_data_handler_t handler);
/** @brief Function for uninitializing the I2S driver. */
void nrfx_i2s_uninit(void);
/**
* @brief Function for starting the continuous I2S transfer.
*
* The I2S data transfer can be performed in one of three modes: RX (reception)
* only, TX (transmission) only, or in both directions simultaneously.
* The mode is selected by specifying a proper buffer for a given direction
* in the call to this function or by passing NULL instead if this direction
* is to be disabled.
*
* The length of the buffer (which is a common value for RX and TX if both
* directions are enabled) is specified in 32-bit words. One 32-bit memory
* word can either contain four 8-bit samples, two 16-bit samples, or one
* right-aligned 24-bit sample sign-extended to a 32-bit value.
* For a detailed memory mapping for different supported configurations,
* see the @linkProductSpecification52.
*
* @note Peripherals using EasyDMA (including I2S) 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_initial_buffers Pointer to a structure specifying the buffers
* to be used in the initial part of the transfer
* (buffers for all consecutive parts are provided
* through the data handler).
* @param[in] buffer_size Size of the buffers (in 32-bit words).
* Must not be 0.
* @param[in] flags Transfer options (0 for default settings).
* Currently, no additional flags are available.
*
* @retval NRFX_SUCCESS The operation was successful.
* @retval NRFX_ERROR_INVALID_STATE Transfer was already started or
* the driver has not been initialized.
* @retval NRFX_ERROR_INVALID_ADDR The provided buffers are not placed
* in the Data RAM region.
*/
nrfx_err_t nrfx_i2s_start(nrfx_i2s_buffers_t const * p_initial_buffers,
uint16_t buffer_size,
uint8_t flags);
/**
* @brief Function for supplying the buffers to be used in the next part of
* the transfer.
*
* The application must call this function when the data handler receives
* @ref NRFX_I2S_STATUS_NEXT_BUFFERS_NEEDED in the @c status parameter.
* The call can be done immediately from the data handler function or later,
* but it has to be done before the I2S peripheral finishes processing the
* buffers supplied previously. Otherwise, data corruption will occur.
*
* @sa nrfx_i2s_data_handler_t
*
* @retval NRFX_SUCCESS If the operation was successful.
* @retval NRFX_ERROR_INVALID_STATE If the buffers were already supplied or
* the peripheral is currently being stopped.
*/
nrfx_err_t nrfx_i2s_next_buffers_set(nrfx_i2s_buffers_t const * p_buffers);
/** @brief Function for stopping the I2S transfer. */
void nrfx_i2s_stop(void);
/** @} */
void nrfx_i2s_irq_handler(void);
#ifdef __cplusplus
}
#endif
#endif // NRFX_I2S_H__

View File

@@ -0,0 +1,153 @@
/**
* Copyright (c) 2014 - 2020, 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_LPCOMP_H__
#define NRFX_LPCOMP_H__
#include <nrfx.h>
#include <hal/nrf_lpcomp.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @defgroup nrfx_lpcomp LPCOMP driver
* @{
* @ingroup nrf_lpcomp
* @brief Low Power Comparator (LPCOMP) peripheral driver.
*/
/**
* @brief LPCOMP event handler function type.
* @param[in] event LPCOMP event.
*/
typedef void (* nrfx_lpcomp_event_handler_t)(nrf_lpcomp_event_t event);
/** @brief LPCOMP configuration. */
typedef struct
{
nrf_lpcomp_config_t hal; /**< LPCOMP HAL configuration. */
nrf_lpcomp_input_t input; /**< Input to be monitored. */
uint8_t interrupt_priority; /**< LPCOMP interrupt priority. */
} nrfx_lpcomp_config_t;
/** @brief LPCOMP driver default configuration, including the LPCOMP HAL configuration. */
#ifdef NRF52_SERIES
#define NRFX_LPCOMP_DEFAULT_CONFIG \
{ \
.hal = { (nrf_lpcomp_ref_t)NRFX_LPCOMP_CONFIG_REFERENCE , \
(nrf_lpcomp_detect_t)NRFX_LPCOMP_CONFIG_DETECTION, \
(nrf_lpcomp_hysteresis_t)NRFX_LPCOMP_CONFIG_HYST }, \
.input = (nrf_lpcomp_input_t)NRFX_LPCOMP_CONFIG_INPUT, \
.interrupt_priority = NRFX_LPCOMP_CONFIG_IRQ_PRIORITY \
}
#else
#define NRFX_LPCOMP_DEFAULT_CONFIG \
{ \
.hal = { (nrf_lpcomp_ref_t)NRFX_LPCOMP_CONFIG_REFERENCE , \
(nrf_lpcomp_detect_t)NRFX_LPCOMP_CONFIG_DETECTION }, \
.input = (nrf_lpcomp_input_t)NRFX_LPCOMP_CONFIG_INPUT, \
.interrupt_priority = NRFX_LPCOMP_CONFIG_IRQ_PRIORITY \
}
#endif
/**
* @brief Function for initializing the LPCOMP driver.
*
* This function initializes the LPCOMP driver, but does not enable the peripheral or any interrupts.
* To start the driver, call the function nrfx_lpcomp_enable() after initialization.
*
* @param[in] p_config Pointer to the structure with the 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 has already been initialized.
* @retval NRFX_ERROR_BUSY The COMP peripheral is already in use.
* This is possible only if @ref nrfx_prs module
* is enabled.
*/
nrfx_err_t nrfx_lpcomp_init(nrfx_lpcomp_config_t const * p_config,
nrfx_lpcomp_event_handler_t event_handler);
/**
* @brief Function for uninitializing the LCOMP driver.
*
* This function uninitializes the LPCOMP driver. The LPCOMP peripheral and
* its interrupts are disabled, and local variables are cleaned. After this call, you must
* initialize the driver again by calling nrfx_lpcomp_init() if you want to use it.
*
* @sa nrfx_lpcomp_disable
* @sa nrfx_lpcomp_init
*/
void nrfx_lpcomp_uninit(void);
/**
* @brief Function for enabling the LPCOMP peripheral and interrupts.
*
* Before calling this function, the driver must be initialized. This function
* enables the LPCOMP peripheral and its interrupts.
*
* @sa nrfx_lpcomp_disable
*/
void nrfx_lpcomp_enable(void);
/**
* @brief Function for disabling the LPCOMP peripheral.
*
* Before calling this function, the driver must be initialized. This function disables the LPCOMP
* peripheral and its interrupts.
*
* @sa nrfx_lpcomp_enable
*/
void nrfx_lpcomp_disable(void);
/** @} */
void nrfx_lpcomp_irq_handler(void);
#ifdef __cplusplus
}
#endif
#endif // NRFX_LPCOMP_H__

View File

@@ -0,0 +1,356 @@
/**
* Copyright (c) 2018 - 2020, 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_NFCT_H__
#define NRFX_NFCT_H__
#include <nrfx.h>
#include <hal/nrf_nfct.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @defgroup nrfx_nfct NFCT driver
* @{
* @ingroup nrf_nfct
* @brief Near Field Communication Tag (NFCT) peripheral driver.
*/
#define NRFX_NFCT_NFCID1_SINGLE_SIZE 4u ///< Length of single-size NFCID1.
#define NRFX_NFCT_NFCID1_DOUBLE_SIZE 7u ///< Length of double-size NFCID1.
#define NRFX_NFCT_NFCID1_TRIPLE_SIZE 10u ///< Length of triple-size NFCID1.
#define NRFX_NFCT_NFCID1_DEFAULT_LEN NRFX_NFCT_NFCID1_DOUBLE_SIZE ///< Default length of NFC ID. */
/** @brief NFCT hardware states. */
typedef enum
{
NRFX_NFCT_STATE_DISABLED = NRF_NFCT_TASK_DISABLE, ///< NFC Tag is disabled (no sensing of an external NFC field).
NRFX_NFCT_STATE_SENSING = NRF_NFCT_TASK_SENSE, ///< NFC Tag is sensing whether there is an external NFC field.
NRFX_NFCT_STATE_ACTIVATED = NRF_NFCT_TASK_ACTIVATE, ///< NFC Tag is powered-up (see @ref nrfx_nfct_active_state_t for possible substates).
} nrfx_nfct_state_t;
/**
* @brief NFC tag states, when NFCT hardware is activated.
*
* @details These states are substates of the @ref NRFX_NFCT_STATE_ACTIVATED state.
*/
typedef enum
{
NRFX_NFCT_ACTIVE_STATE_IDLE = NRF_NFCT_TASK_GOIDLE, ///< NFC Tag is activated and idle (not selected by a reader).
NRFX_NFCT_ACTIVE_STATE_SLEEP = NRF_NFCT_TASK_GOSLEEP, ///< NFC Tag is sleeping.
NRFX_NFCT_ACTIVE_STATE_DEFAULT, ///< NFC Tag is either sleeping or idle, depending on the previous state before being selected by a poller.
} nrfx_nfct_active_state_t;
/**
* @brief NFCT driver event types, passed to the upper-layer callback function
* provided during the initialization.
*/
typedef enum
{
NRFX_NFCT_EVT_FIELD_DETECTED = NRF_NFCT_INT_FIELDDETECTED_MASK, ///< External NFC field is detected.
NRFX_NFCT_EVT_FIELD_LOST = NRF_NFCT_INT_FIELDLOST_MASK, ///< External NFC Field is lost.
NRFX_NFCT_EVT_SELECTED = NRF_NFCT_INT_SELECTED_MASK, ///< Tag was selected by the poller.
NRFX_NFCT_EVT_RX_FRAMESTART = NRF_NFCT_INT_RXFRAMESTART_MASK, ///< Data frame reception started.
NRFX_NFCT_EVT_RX_FRAMEEND = NRF_NFCT_INT_RXFRAMEEND_MASK, ///< Data frame is received.
NRFX_NFCT_EVT_TX_FRAMESTART = NRF_NFCT_INT_TXFRAMESTART_MASK, ///< Data frame transmission started.
NRFX_NFCT_EVT_TX_FRAMEEND = NRF_NFCT_INT_TXFRAMEEND_MASK, ///< Data frame is transmitted.
NRFX_NFCT_EVT_ERROR = NRF_NFCT_INT_ERROR_MASK, ///< Error occurred in an NFC communication.
} nrfx_nfct_evt_id_t;
/** @brief NFCT timing-related error types. */
typedef enum
{
NRFX_NFCT_ERROR_FRAMEDELAYTIMEOUT, ///< No response frame was transmitted to the poller in the transmit window.
NRFX_NFCT_ERROR_NUM, ///< Total number of possible errors.
} nrfx_nfct_error_t;
/** @brief NFCT driver parameter types. */
typedef enum
{
NRFX_NFCT_PARAM_ID_FDT, ///< NFC-A Frame Delay Time parameter.
NRFX_NFCT_PARAM_ID_SEL_RES, ///< Value of the 'Protocol' field in the NFC-A SEL_RES frame.
NRFX_NFCT_PARAM_ID_NFCID1, ///< NFC-A NFCID1 setting (NFC tag identifier).
} nrfx_nfct_param_id_t;
/** @brief NFCID1 descriptor. */
typedef struct
{
uint8_t const * p_id; ///< NFCID1 data.
uint8_t id_size; ///< NFCID1 size.
} nrfx_nfct_nfcid1_t;
/** @brief NFCT driver parameter descriptor. */
typedef struct
{
nrfx_nfct_param_id_t id; ///< Type of parameter.
union
{
uint32_t fdt; ///< NFC-A Frame Delay Time. Filled when nrfx_nfct_param_t::id is @ref NRFX_NFCT_PARAM_ID_FDT.
uint8_t sel_res_protocol; ///< NFC-A value of the 'Protocol' field in the SEL_RES frame. Filled when nrfx_nfct_param_t::id is @ref NRFX_NFCT_PARAM_ID_SEL_RES.
nrfx_nfct_nfcid1_t nfcid1; ///< NFC-A NFCID1 value (tag identifier). Filled when nrfx_nfct_param_t::id is @ref NRFX_NFCT_PARAM_ID_NFCID1.
} data; ///< Union to store parameter data.
} nrfx_nfct_param_t;
/** @brief NFCT driver RX/TX buffer descriptor. */
typedef struct
{
uint32_t data_size; ///< RX/TX buffer size.
uint8_t const * p_data; ///< RX/TX buffer.
} nrfx_nfct_data_desc_t;
/** @brief Structure used to describe the @ref NRFX_NFCT_EVT_RX_FRAMEEND event type. */
typedef struct
{
uint32_t rx_status; ///< RX error status.
nrfx_nfct_data_desc_t rx_data; ///< RX buffer.
} nrfx_nfct_evt_rx_frameend_t;
/** @brief Structure used to describe the @ref NRFX_NFCT_EVT_TX_FRAMESTART event type. */
typedef struct
{
nrfx_nfct_data_desc_t tx_data; ///< TX buffer.
} nrfx_nfct_evt_tx_framestart_t;
/** @brief Structure used to describe the @ref NRFX_NFCT_EVT_ERROR event type. */
typedef struct
{
nrfx_nfct_error_t reason; ///< Reason for error.
} nrfx_nfct_evt_error_t;
/** @brief NFCT driver event. */
typedef struct
{
nrfx_nfct_evt_id_t evt_id; ///< Type of event.
union
{
nrfx_nfct_evt_rx_frameend_t rx_frameend; ///< End of the RX frame data. Filled when nrfx_nfct_evt_t::evt_id is @ref NRFX_NFCT_EVT_RX_FRAMEEND.
nrfx_nfct_evt_tx_framestart_t tx_framestart; ///< Start of the TX frame data. Filled when nrfx_nfct_evt_t::evt_id is @ref NRFX_NFCT_EVT_TX_FRAMESTART.
nrfx_nfct_evt_error_t error; ///< Error data. Filled when nrfx_nfct_evt_t::evt_id is @ref NRFX_NFCT_EVT_ERROR.
} params; ///< Union to store event data.
} nrfx_nfct_evt_t;
/**
* @brief Callback descriptor to pass events from the NFCT driver to the upper layer.
*
* @param[in] p_event Pointer to the event descriptor.
*
* @note @ref NRFX_NFCT_EVT_FIELD_DETECTED and @ref NRFX_NFCT_EVT_FIELD_LOST are generated only on field state transitions,
* i.e. there will be no multiple events of the same type (out of the 2 mentioned) coming in a row.
*/
typedef void (*nrfx_nfct_handler_t)(nrfx_nfct_evt_t const * p_event);
/** @brief NFCT driver configuration structure. */
typedef struct
{
uint32_t rxtx_int_mask; ///< Mask for enabling RX/TX events. Indicate which events must be forwarded to the upper layer by using @ref nrfx_nfct_evt_id_t. By default, no events are enabled. */
nrfx_nfct_handler_t cb; ///< Callback.
} nrfx_nfct_config_t;
/**
* @brief Function for initializing the NFCT driver.
*
* @param[in] p_config Pointer to the NFCT driver configuration structure.
*
* @retval NRFX_SUCCESS The NFCT driver was initialized successfully.
* @retval NRFX_ERROR_INVALID_STATE The NFCT driver is already initialized.
*/
nrfx_err_t nrfx_nfct_init(nrfx_nfct_config_t const * p_config);
/**
* @brief Function for uninitializing the NFCT driver.
*
* After uninitialization, the instance is in disabled state.
*/
void nrfx_nfct_uninit(void);
/**
* @brief Function for starting the NFC subsystem.
*
* After this function completes, NFC readers are able to detect the tag.
*/
void nrfx_nfct_enable(void);
/**
* @brief Function for disabling the NFCT driver.
*
* After this function returns, NFC readers are no longer able to connect
* to the tag.
*/
void nrfx_nfct_disable(void);
/**
* @brief Function for checking whether the external NFC field is present in the range of the tag.
*
* @retval true The NFC field is present.
* @retval false No NFC field is present.
*/
bool nrfx_nfct_field_check(void);
/**
* @brief Function for preparing the NFCT driver for receiving an NFC frame.
*
* @param[in] p_rx_data Pointer to the RX buffer.
*/
void nrfx_nfct_rx(nrfx_nfct_data_desc_t const * p_rx_data);
/**
* @brief Function for transmitting an NFC frame.
*
* @param[in] p_tx_data Pointer to the TX buffer.
* @param[in] delay_mode Delay mode of the NFCT frame timer.
*
* @retval NRFX_SUCCESS The operation was successful.
* @retval NRFX_ERROR_INVALID_LENGTH The TX buffer size is invalid.
*/
nrfx_err_t nrfx_nfct_tx(nrfx_nfct_data_desc_t const * p_tx_data,
nrf_nfct_frame_delay_mode_t delay_mode);
/**
* @brief Function for moving the NFCT to a new state.
*
* @note The HFCLK must be running before activating the NFCT with
* @ref NRFX_NFCT_STATE_ACTIVATED.
*
* @param[in] state The required state.
*/
void nrfx_nfct_state_force(nrfx_nfct_state_t state);
/**
* @brief Function for moving the NFCT to a new initial substate within @ref NRFX_NFCT_STATE_ACTIVATED.
*
* @param[in] sub_state The required substate.
*/
void nrfx_nfct_init_substate_force(nrfx_nfct_active_state_t sub_state);
/**
* @brief Function for setting the NFC communication parameter.
*
* @note Parameter validation for length and acceptable values.
*
* @param[in] p_param Pointer to parameter descriptor.
*
* @retval NRFX_SUCCESS The operation was successful.
* @retval NRFX_ERROR_INVALID_PARAM The parameter data is invalid.
*/
nrfx_err_t nrfx_nfct_parameter_set(nrfx_nfct_param_t const * p_param);
/**
* @brief Function for getting default bytes for NFCID1.
*
* @param[in,out] p_nfcid1_buff In: empty buffer for data;
* Out: buffer with the NFCID1 default data. These values
* can be used to fill the Type 2 Tag Internal Bytes.
* @param[in] nfcid1_buff_len Length of the NFCID1 buffer.
*
* @retval NRFX_SUCCESS The operation was successful.
* @retval NRFX_ERROR_INVALID_LENGTH Length of the NFCID buffer is different than
* @ref NRFX_NFCT_NFCID1_SINGLE_SIZE,
* @ref NRFX_NFCT_NFCID1_DOUBLE_SIZE, or
* @ref NRFX_NFCT_NFCID1_TRIPLE_SIZE.
*/
nrfx_err_t nrfx_nfct_nfcid1_default_bytes_get(uint8_t * const p_nfcid1_buff,
uint32_t nfcid1_buff_len);
/**
* @brief Function for enabling the automatic collision resolution.
*
* @details As defined by the NFC Forum Digital Protocol Technical Specification (and ISO 14443-3),
* the automatic collision resolution is implemented in the NFCT hardware.
* This function allows enabling and disabling this feature.
*/
void nrfx_nfct_autocolres_enable(void);
/**
* @brief Function for disabling the automatic collision resolution.
*
* @details See also details in @ref nrfx_nfct_autocolres_enable.
*/
void nrfx_nfct_autocolres_disable(void);
/** @} */
void nrfx_nfct_irq_handler(void);
#ifdef __cplusplus
}
#endif
/**
* @defgroup nrfx_nfct_fixes NFCT driver fixes and workarounds
* @{
* @ingroup nrf_nfct
* @brief Fixes for hardware-related anomalies.
*
* If you are using the nRF52832 chip, the workarounds for the following anomalies are applied:
* - 79. NFCT: A false EVENTS_FIELDDETECTED event occurs after the field is lost.
* - 116. NFCT does not release HFCLK when switching from ACTIVATED to SENSE mode.
* To implement the first workaround, an instance of NRF_TIMER is used. After the NFC field is detected,
* the timing module periodically polls its state to determine when the field is turned off.
* To implement the second workaround, power reset is used to release the clock acquired by NFCT
* after the field is turned off. Note that the NFCT register configuration is restored to defaults.
*
* If you are using the nRF52840 chip, rev. Engineering A, the workarounds for the following anomalies
* are applied:
* - 98. NFCT: The NFCT is not able to communicate with the peer.
* - 116. NFCT does not release HFCLK when switching from ACTIVATED to SENSE mode.
* - 144. NFCT: Not optimal NFC performance
*
* If you are using the nRF52840 chip, rev. 1, or rev. Engineering B or C, the workarounds for the following
* anomalies are applied:
* - 190. NFCT: Event FIELDDETECTED can be generated too early.
* To implement this workaround, an instance of NRF_TIMER is used. After the NFC field is detected,
* the timing module measures the necessary waiting period after which NFCT can be activated.
* This debouncing technique is used to filter possible field instabilities.
*
* The application of the implemented workarounds for the nRF52840 chip is determined at runtime and depends
* on the chip variant.
*
* The current code contains a patch for the anomaly 25 (NFCT: Reset value of
* SENSRES register is incorrect), so that the module now works on Windows Phone.
* @}
*/
#endif // NRFX_NFCT_H__

View File

@@ -0,0 +1,295 @@
/**
* Copyright (c) 2019 - 2020, 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__

View File

@@ -0,0 +1,207 @@
/**
* Copyright (c) 2015 - 2020, 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_PDM_H__
#define NRFX_PDM_H__
#include <nrfx.h>
#include <hal/nrf_pdm.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @defgroup nrfx_pdm PDM driver
* @{
* @ingroup nrf_pdm
* @brief Pulse Density Modulation (PDM) peripheral driver.
*/
/** @brief Maximum supported PDM buffer size. */
#define NRFX_PDM_MAX_BUFFER_SIZE 32767
/** @brief PDM error type. */
typedef enum
{
NRFX_PDM_NO_ERROR = 0, ///< No error.
NRFX_PDM_ERROR_OVERFLOW = 1 ///< Overflow error.
} nrfx_pdm_error_t;
/** @brief PDM event structure. */
typedef struct
{
bool buffer_requested; ///< Buffer request flag.
int16_t * buffer_released; ///< Pointer to the released buffer. Can be NULL.
nrfx_pdm_error_t error; ///< Error type.
} nrfx_pdm_evt_t;
/** @brief PDM interface driver configuration structure. */
typedef struct
{
nrf_pdm_mode_t mode; ///< Interface operation mode.
nrf_pdm_edge_t edge; ///< Sampling mode.
uint8_t pin_clk; ///< CLK pin.
uint8_t pin_din; ///< DIN pin.
nrf_pdm_freq_t clock_freq; ///< Clock frequency.
nrf_pdm_gain_t gain_l; ///< Left channel gain.
nrf_pdm_gain_t gain_r; ///< Right channel gain.
uint8_t interrupt_priority; ///< Interrupt priority.
} nrfx_pdm_config_t;
/**
* @brief Macro for setting @ref nrfx_pdm_config_t to default settings
* in the single-ended mode.
*
* @param _pin_clk CLK output pin.
* @param _pin_din DIN input pin.
*/
#define NRFX_PDM_DEFAULT_CONFIG(_pin_clk, _pin_din) \
{ \
.mode = (nrf_pdm_mode_t)NRFX_PDM_CONFIG_MODE, \
.edge = (nrf_pdm_edge_t)NRFX_PDM_CONFIG_EDGE, \
.pin_clk = _pin_clk, \
.pin_din = _pin_din, \
.clock_freq = (nrf_pdm_freq_t)NRFX_PDM_CONFIG_CLOCK_FREQ, \
.gain_l = NRF_PDM_GAIN_DEFAULT, \
.gain_r = NRF_PDM_GAIN_DEFAULT, \
.interrupt_priority = NRFX_PDM_CONFIG_IRQ_PRIORITY \
}
/**
* @brief Handler for the PDM interface ready events.
*
* This event handler is called on a buffer request, an error or when a buffer
* is full and ready to be processed.
*
* @param[in] p_evt Pointer to the PDM event structure.
*/
typedef void (*nrfx_pdm_event_handler_t)(nrfx_pdm_evt_t const * const p_evt);
/**
* @brief Function for initializing the PDM interface.
*
* @param[in] p_config Pointer to the structure with the initial configuration.
* @param[in] event_handler Event handler provided by the user. Cannot be NULL.
*
* @retval NRFX_SUCCESS Initialization was successful.
* @retval NRFX_ERROR_INVALID_STATE The driver is already initialized.
* @retval NRFX_ERROR_INVALID_PARAM Invalid configuration was specified.
*/
nrfx_err_t nrfx_pdm_init(nrfx_pdm_config_t const * p_config,
nrfx_pdm_event_handler_t event_handler);
/**
* @brief Function for uninitializing the PDM interface.
*
* This function stops PDM sampling, if it is in progress.
*/
void nrfx_pdm_uninit(void);
/**
* @brief Function for getting the address of a PDM interface task.
*
* @param[in] task Task.
*
* @return Task address.
*/
__STATIC_INLINE uint32_t nrfx_pdm_task_address_get(nrf_pdm_task_t task)
{
return nrf_pdm_task_address_get(task);
}
/**
* @brief Function for getting the state of the PDM interface.
*
* @retval true The PDM interface is enabled.
* @retval false The PDM interface is disabled.
*/
__STATIC_INLINE bool nrfx_pdm_enable_check(void)
{
return nrf_pdm_enable_check();
}
/**
* @brief Function for starting the PDM sampling.
*
* @retval NRFX_SUCCESS Sampling was started successfully or was already in progress.
* @retval NRFX_ERROR_BUSY Previous start/stop operation is in progress.
*/
nrfx_err_t nrfx_pdm_start(void);
/**
* @brief Function for stopping the PDM sampling.
*
* When this function is called, the PDM interface is stopped after finishing
* the current frame.
* The event handler function might be called once more after calling this function.
*
* @retval NRFX_SUCCESS Sampling was stopped successfully or was already stopped before.
* @retval NRFX_ERROR_BUSY Previous start/stop operation is in progress.
*/
nrfx_err_t nrfx_pdm_stop(void);
/**
* @brief Function for supplying the sample buffer.
*
* Call this function after every buffer request event.
*
* @param[in] buffer Pointer to the receive buffer. Cannot be NULL.
* @param[in] buffer_length Length of the receive buffer in 16-bit words.
*
* @retval NRFX_SUCCESS The buffer was applied successfully.
* @retval NRFX_ERROR_BUSY The buffer was already supplied or the peripheral is currently being stopped.
* @retval NRFX_ERROR_INVALID_STATE The driver was not initialized.
* @retval NRFX_ERROR_INVALID_PARAM Invalid parameters were provided.
*/
nrfx_err_t nrfx_pdm_buffer_set(int16_t * buffer, uint16_t buffer_length);
/** @} */
void nrfx_pdm_irq_handler(void);
#ifdef __cplusplus
}
#endif
#endif // NRFX_PDM_H__

View File

@@ -0,0 +1,380 @@
/**
* Copyright (c) 2017 - 2020, 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__ */

View File

@@ -0,0 +1,92 @@
/**
* Copyright (c) 2015 - 2020, 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__

View File

@@ -0,0 +1,331 @@
/**
* Copyright (c) 2015 - 2020, 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__

View File

@@ -0,0 +1,476 @@
/**
* Copyright (c) 2015 - 2020, 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__

View File

@@ -0,0 +1,198 @@
/**
* Copyright (c) 2015 - 2020, 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_QDEC_H__
#define NRFX_QDEC_H__
#include <nrfx.h>
#include <hal/nrf_qdec.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @defgroup nrfx_qdec QDEC driver
* @{
* @ingroup nrf_qdec
* @brief Quadrature Decoder (QDEC) peripheral driver.
*/
/** @brief QDEC configuration structure. */
typedef struct
{
nrf_qdec_reportper_t reportper; /**< Report period in samples. */
nrf_qdec_sampleper_t sampleper; /**< Sampling period in microseconds. */
uint32_t psela; /**< Pin number for A input. */
uint32_t pselb; /**< Pin number for B input. */
uint32_t pselled; /**< Pin number for LED output. */
uint32_t ledpre; /**< Time (in microseconds) how long LED is switched on before sampling. */
nrf_qdec_ledpol_t ledpol; /**< Active LED polarity. */
bool dbfen; /**< State of debouncing filter. */
bool sample_inten; /**< Enabling sample ready interrupt. */
uint8_t interrupt_priority; /**< QDEC interrupt priority. */
} nrfx_qdec_config_t;
/**@brief QDEC default configuration. */
#define NRFX_QDEC_DEFAULT_CONFIG \
{ \
.reportper = (nrf_qdec_reportper_t)NRFX_QDEC_CONFIG_REPORTPER, \
.sampleper = (nrf_qdec_sampleper_t)NRFX_QDEC_CONFIG_SAMPLEPER, \
.psela = NRFX_QDEC_CONFIG_PIO_A, \
.pselb = NRFX_QDEC_CONFIG_PIO_B, \
.pselled = NRFX_QDEC_CONFIG_PIO_LED, \
.ledpre = NRFX_QDEC_CONFIG_LEDPRE, \
.ledpol = (nrf_qdec_ledpol_t)NRFX_QDEC_CONFIG_LEDPOL, \
.dbfen = NRFX_QDEC_CONFIG_DBFEN, \
.sample_inten = NRFX_QDEC_CONFIG_SAMPLE_INTEN, \
.interrupt_priority = NRFX_QDEC_CONFIG_IRQ_PRIORITY, \
}
/** @brief QDEC sample event data. */
typedef struct
{
int8_t value; /**< Sample value. */
} nrfx_qdec_sample_data_evt_t;
/** @brief QDEC report event data. */
typedef struct
{
int16_t acc; /**< Accumulated transitions. */
uint16_t accdbl; /**< Accumulated double transitions. */
} nrfx_qdec_report_data_evt_t;
/** @brief QDEC event handler structure. */
typedef struct
{
nrf_qdec_event_t type; /**< Event type. */
union
{
nrfx_qdec_sample_data_evt_t sample; /**< Sample event data. */
nrfx_qdec_report_data_evt_t report; /**< Report event data. */
} data; /**< Union to store event data. */
} nrfx_qdec_event_t;
/**
* @brief QDEC event handler.
*
* @param[in] event QDEC event structure.
*/
typedef void (*nrfx_qdec_event_handler_t)(nrfx_qdec_event_t event);
/**
* @brief Function for initializing QDEC.
*
* @param[in] p_config Pointer to the structure with the 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 QDEC was already initialized.
*/
nrfx_err_t nrfx_qdec_init(nrfx_qdec_config_t const * p_config,
nrfx_qdec_event_handler_t event_handler);
/**
* @brief Function for uninitializing QDEC.
*
* @note Function asserts if module is uninitialized.
*/
void nrfx_qdec_uninit(void);
/**
* @brief Function for enabling QDEC.
*
* @note Function asserts if module is uninitialized or enabled.
*/
void nrfx_qdec_enable(void);
/**
* @brief Function for disabling QDEC.
*
* @note Function asserts if module is uninitialized or disabled.
*/
void nrfx_qdec_disable(void);
/**
* @brief Function for reading accumulated transitions from the QDEC peripheral.
*
* @note Function asserts if module is not enabled.
* @note Accumulators are cleared after reading.
*
* @param[out] p_acc Pointer to store the accumulated transitions.
* @param[out] p_accdbl Pointer to store the accumulated double transitions.
*/
void nrfx_qdec_accumulators_read(int16_t * p_acc, int16_t * p_accdbl);
/**
* @brief Function for returning the address of the specified QDEC task.
*
* @param task QDEC task.
*
* @return Task address.
*/
__STATIC_INLINE uint32_t nrfx_qdec_task_address_get(nrf_qdec_task_t task)
{
return (uint32_t)nrf_qdec_task_address_get(task);
}
/**
* @brief Function for returning the address of the specified QDEC event.
*
* @param event QDEC event.
*
* @return Event address.
*/
__STATIC_INLINE uint32_t nrfx_qdec_event_address_get(nrf_qdec_event_t event)
{
return (uint32_t)nrf_qdec_event_address_get(event);
}
/** @} */
void nrfx_qdec_irq_handler(void);
#ifdef __cplusplus
}
#endif
#endif // NRFX_QDEC_H__

View File

@@ -0,0 +1,330 @@
/**
* Copyright (c) 2016 - 2020, 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_QSPI_H__
#define NRFX_QSPI_H__
#include <nrfx.h>
#include <hal/nrf_qspi.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @defgroup nrfx_qspi QSPI driver
* @{
* @ingroup nrf_qspi
* @brief Quad Serial Peripheral Interface (QSPI) peripheral driver.
*/
/** @brief QSPI driver instance configuration structure. */
typedef struct
{
uint32_t xip_offset; /**< Address offset into the external memory for Execute in Place operation. */
nrf_qspi_pins_t pins; /**< Pin configuration structure. */
nrf_qspi_prot_conf_t prot_if; /**< Protocol layer interface configuration structure. */
nrf_qspi_phy_conf_t phy_if; /**< Physical layer interface configuration structure. */
uint8_t irq_priority; /**< Interrupt priority. */
} nrfx_qspi_config_t;
/** @brief QSPI instance default configuration. */
#define NRFX_QSPI_DEFAULT_CONFIG \
{ \
.xip_offset = NRFX_QSPI_CONFIG_XIP_OFFSET, \
.pins = { \
.sck_pin = NRFX_QSPI_PIN_SCK, \
.csn_pin = NRFX_QSPI_PIN_CSN, \
.io0_pin = NRFX_QSPI_PIN_IO0, \
.io1_pin = NRFX_QSPI_PIN_IO1, \
.io2_pin = NRFX_QSPI_PIN_IO2, \
.io3_pin = NRFX_QSPI_PIN_IO3, \
}, \
.prot_if = { \
.readoc = (nrf_qspi_readoc_t)NRFX_QSPI_CONFIG_READOC, \
.writeoc = (nrf_qspi_writeoc_t)NRFX_QSPI_CONFIG_WRITEOC, \
.addrmode = (nrf_qspi_addrmode_t)NRFX_QSPI_CONFIG_ADDRMODE, \
.dpmconfig = false, \
}, \
.phy_if = { \
.sck_delay = (uint8_t)NRFX_QSPI_CONFIG_SCK_DELAY, \
.dpmen = false, \
.spi_mode = (nrf_qspi_spi_mode_t)NRFX_QSPI_CONFIG_MODE, \
.sck_freq = (nrf_qspi_frequency_t)NRFX_QSPI_CONFIG_FREQUENCY, \
}, \
.irq_priority = (uint8_t)NRFX_QSPI_CONFIG_IRQ_PRIORITY, \
}
/** @brief QSPI custom instruction helper with the default configuration. */
#define NRFX_QSPI_DEFAULT_CINSTR(opc, len) \
{ \
.opcode = (opc), \
.length = (len), \
.io2_level = false, \
.io3_level = false, \
.wipwait = false, \
.wren = false \
}
/**
* @brief QSPI master driver event types, passed to the handler routine provided
* during initialization.
*/
typedef enum
{
NRFX_QSPI_EVENT_DONE, /**< Transfer done. */
} nrfx_qspi_evt_t;
/** @brief QSPI driver event handler type. */
typedef void (*nrfx_qspi_handler_t)(nrfx_qspi_evt_t event, void * p_context);
/**
* @brief Function for initializing the QSPI driver instance.
*
* This function configures the peripheral and its interrupts, and activates it. During the
* activation process, the internal clocks are started and the QSPI peripheral tries to read
* the status byte to read the busy bit. Reading the status byte is done in a simple poll and wait
* mechanism.
* If the busy bit is 1, this indicates issues with the external memory device. As a result,
* @ref nrfx_qspi_init returns NRFX_ERROR_TIMEOUT.
*
* In case of issues:
* - Check the connection.
* - Make sure that the memory device does not perform other operations like erasing or writing.
* - Check if there is a short circuit.
*
* @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 Pointer to context. Use in the interrupt handler.
*
* @retval NRFX_SUCCESS Initialization was successful.
* @retval NRFX_ERROR_TIMEOUT The peripheral cannot connect with external memory.
* @retval NRFX_ERROR_INVALID_STATE The driver was already initialized.
* @retval NRFX_ERROR_INVALID_PARAM The pin configuration was incorrect.
*/
nrfx_err_t nrfx_qspi_init(nrfx_qspi_config_t const * p_config,
nrfx_qspi_handler_t handler,
void * p_context);
/** @brief Function for uninitializing the QSPI driver instance. */
void nrfx_qspi_uninit(void);
/**
* @brief Function for reading data from the QSPI memory.
*
* Write, read, and erase operations check memory device busy state before starting the operation.
* If the memory is busy, the resulting action depends on the mode in which the read operation is used:
* - blocking mode (without handler) - a delay occurs until the last operation runs and
* until the operation data is being read.
* - interrupt mode (with handler) - event emission occurs after the last operation
* and reading of data are finished.
*
* @param[out] p_rx_buffer Pointer to the receive buffer.
* @param[in] rx_buffer_length Size of the data to read.
* @param[in] src_address Address in memory to read from.
*
* @retval NRFX_SUCCESS The operation was successful (blocking mode) or operation
* was commissioned (handler mode).
* @retval NRFX_ERROR_BUSY The driver currently handles another operation.
* @retval NRFX_ERROR_INVALID_ADDR The provided buffer is not placed in the Data RAM region
* or its address is not aligned to a 32-bit word.
*/
nrfx_err_t nrfx_qspi_read(void * p_rx_buffer,
size_t rx_buffer_length,
uint32_t src_address);
/**
* @brief Function for writing data to QSPI memory.
*
* Write, read, and erase operations check memory device busy state before starting the operation.
* If the memory is busy, the resulting action depends on the mode in which the write operation is used:
* - blocking mode (without handler) - a delay occurs until the last operation runs or
* until the operation data is being sent.
* - interrupt mode (with handler) - event emission occurs after the last operation
* and sending of operation data are finished.
* To manually control operation execution in the memory device, use @ref nrfx_qspi_mem_busy_check
* after executing the write function.
* Remember that an incoming event signalizes only that data was sent to the memory device and the periheral
* before the write operation checked if memory was busy.
*
* @param[in] p_tx_buffer Pointer to the writing buffer.
* @param[in] tx_buffer_length Size of the data to write.
* @param[in] dst_address Address in memory to write to.
*
* @retval NRFX_SUCCESS The operation was successful (blocking mode) or operation
* was commissioned (handler mode).
* @retval NRFX_ERROR_BUSY The driver currently handles other operation.
* @retval NRFX_ERROR_INVALID_ADDR The provided buffer is not placed in the Data RAM region
* or its address is not aligned to a 32-bit word.
*/
nrfx_err_t nrfx_qspi_write(void const * p_tx_buffer,
size_t tx_buffer_length,
uint32_t dst_address);
/**
* @brief Function for starting erasing of one memory block - 4KB, 64KB, or the whole chip.
*
* Write, read, and erase operations check memory device busy state before starting the operation.
* If the memory is busy, the resulting action depends on the mode in which the erase operation is used:
* - blocking mode (without handler) - a delay occurs until the last operation runs or
* until the operation data is being sent.
* - interrupt mode (with handler) - event emission occurs after the last operation
* and sending of operation data are finished.
* To manually control operation execution in the memory device, use @ref nrfx_qspi_mem_busy_check
* after executing the erase function.
* Remember that an incoming event signalizes only that data was sent to the memory device and the periheral
* before the erase operation checked if memory was busy.
*
* @param[in] length Size of data to erase. See @ref nrf_qspi_erase_len_t.
* @param[in] start_address Memory address to start erasing. If chip erase is performed, address
* field is ommited.
*
* @retval NRFX_SUCCESS The operation was successful (blocking mode) or operation
* was commissioned (handler mode).
* @retval NRFX_ERROR_INVALID_ADDR The provided start address is not aligned to a 32-bit word.
* @retval NRFX_ERROR_BUSY The driver currently handles another operation.
*/
nrfx_err_t nrfx_qspi_erase(nrf_qspi_erase_len_t length,
uint32_t start_address);
/**
* @brief Function for starting an erase operation of the whole chip.
*
* @retval NRFX_SUCCESS The operation was successful (blocking mode) or operation
* was commissioned (handler mode).
* @retval NRFX_ERROR_BUSY The driver currently handles another operation.
*/
nrfx_err_t nrfx_qspi_chip_erase(void);
/**
* @brief Function for getting the current driver status and status byte of memory device with
* testing WIP (write in progress) bit.
*
* @retval NRFX_SUCCESS The driver and memory are ready to handle a new operation.
* @retval NRFX_ERROR_BUSY The driver or memory currently handle another operation.
*/
nrfx_err_t nrfx_qspi_mem_busy_check(void);
/**
* @brief Function for sending operation code, sending data, and receiving data from the memory device.
*
* Use this function to transfer configuration data to memory and to receive data from memory.
* Pointers can be addresses from flash memory.
* This function is a synchronous function and should be used only if necessary.
*
* @param[in] p_config Pointer to the structure with opcode and transfer configuration.
* @param[in] p_tx_buffer Pointer to the array with data to send. Can be NULL if only opcode is transmitted.
* @param[out] p_rx_buffer Pointer to the array for data to receive. Can be NULL if there is nothing to receive.
*
* @retval NRFX_SUCCESS The operation was successful.
* @retval NRFX_ERROR_TIMEOUT The external memory is busy or there are connection issues.
* @retval NRFX_ERROR_BUSY The driver currently handles other operation.
*/
nrfx_err_t nrfx_qspi_cinstr_xfer(nrf_qspi_cinstr_conf_t const * p_config,
void const * p_tx_buffer,
void * p_rx_buffer);
/**
* @brief Function for sending operation code and data to the memory device with simpler configuration.
*
* Use this function to transfer configuration data to memory and to receive data from memory.
* This function is a synchronous function and should be used only if necessary.
*
* @param[in] opcode Operation code. Sending first.
* @param[in] length Length of the data to send and opcode. See @ref nrf_qspi_cinstr_len_t.
* @param[in] p_tx_buffer Pointer to input data array.
*
* @retval NRFX_SUCCESS The operation was successful.
* @retval NRFX_ERROR_BUSY The driver currently handles another operation.
*/
nrfx_err_t nrfx_qspi_cinstr_quick_send(uint8_t opcode,
nrf_qspi_cinstr_len_t length,
void const * p_tx_buffer);
/**
* @brief Function for starting the custom instruction long frame mode.
*
* The long frame mode is a mechanism that allows for arbitrary byte length custom instructions.
* Use this function to initiate a custom transaction by sending custom instruction opcode.
* To send and receive data, use @ref nrfx_qspi_lfm_xfer.
*
* @param[in] p_config Pointer to the structure with custom instruction opcode and transfer
* configuration. Transfer length must be set to @ref NRF_QSPI_CINSTR_LEN_1B.
*
* @retval NRFX_SUCCESS Operation was successful.
* @retval NRFX_ERROR_BUSY Driver currently handles other operation.
* @retval NRFX_ERROR_TIMEOUT External memory is busy or there are connection issues.
*/
nrfx_err_t nrfx_qspi_lfm_start(nrf_qspi_cinstr_conf_t const * p_config);
/**
* @brief Function for sending and receiving data in the custom instruction long frame mode.
*
* Both specified buffers must be at least @p transfer_length bytes in size.
*
* @param[in] p_tx_buffer Pointer to the array with data to send.
* Can be NULL if there is nothing to send.
* @param[out] p_rx_buffer Pointer to the array for receiving data.
* Can be NULL if there is nothing to receive.
* @param[in] transfer_length Number of bytes to send and receive.
* @param[in] finalize True if custom instruction long frame mode is to be finalized
* after this transfer.
*
* @retval NRFX_SUCCESS Operation was successful.
* @retval NRFX_ERROR_TIMEOUT External memory is busy or there are connection issues.
* Long frame mode becomes deactivated.
*/
nrfx_err_t nrfx_qspi_lfm_xfer(void const * p_tx_buffer,
void * p_rx_buffer,
size_t transfer_length,
bool finalize);
/** @} */
void nrfx_qspi_irq_handler(void);
#ifdef __cplusplus
}
#endif
#endif // NRFX_QSPI_H__

View File

@@ -0,0 +1,120 @@
/**
* Copyright (c) 2016 - 2020, 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__

View File

@@ -0,0 +1,378 @@
/**
* Copyright (c) 2014 - 2020, 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__

View File

@@ -0,0 +1,322 @@
/**
* Copyright (c) 2015 - 2020, 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__

View File

@@ -0,0 +1,382 @@
/**
* Copyright (c) 2019 - 2020, 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__

View File

@@ -0,0 +1,255 @@
/**
* Copyright (c) 2015 - 2020, 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__

View File

@@ -0,0 +1,385 @@
/**
* Copyright (c) 2015 - 2020, 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__

View File

@@ -0,0 +1,256 @@
/**
* Copyright (c) 2015 - 2020, 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_SPIS_H__
#define NRFX_SPIS_H__
#include <nrfx.h>
#include <hal/nrf_spis.h>
#include <hal/nrf_gpio.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @defgroup nrfx_spis SPIS driver
* @{
* @ingroup nrf_spis
* @brief Serial Peripheral Interface Slave with EasyDMA (SPIS) driver.
*/
/** @brief Data structure for the Serial Peripheral Interface Slave with EasyDMA (SPIS) driver instance. */
typedef struct
{
NRF_SPIS_Type * p_reg; //!< Pointer to a structure with SPIS registers.
uint8_t drv_inst_idx; //!< Index of the driver instance. For internal use only.
} nrfx_spis_t;
#ifndef __NRFX_DOXYGEN__
enum {
#if NRFX_CHECK(NRFX_SPIS0_ENABLED)
NRFX_SPIS0_INST_IDX,
#endif
#if NRFX_CHECK(NRFX_SPIS1_ENABLED)
NRFX_SPIS1_INST_IDX,
#endif
#if NRFX_CHECK(NRFX_SPIS2_ENABLED)
NRFX_SPIS2_INST_IDX,
#endif
#if NRFX_CHECK(NRFX_SPIS3_ENABLED)
NRFX_SPIS3_INST_IDX,
#endif
NRFX_SPIS_ENABLED_COUNT
};
#endif
/** @brief Macro for creating an instance of the SPI slave driver. */
#define NRFX_SPIS_INSTANCE(id) \
{ \
.p_reg = NRFX_CONCAT_2(NRF_SPIS, id), \
.drv_inst_idx = NRFX_CONCAT_3(NRFX_SPIS, id, _INST_IDX), \
}
/**
* @brief This value can be provided instead of a pin number for the signals MOSI
* and MISO to specify that the given signal is not used and therefore
* does not need to be connected to a pin.
*/
#define NRFX_SPIS_PIN_NOT_USED 0xFF
/** @brief Default pull-up configuration of the SPI CS. */
#define NRFX_SPIS_DEFAULT_CSN_PULLUP NRF_GPIO_PIN_NOPULL
/** @brief Default drive configuration of the SPI MISO. */
#define NRFX_SPIS_DEFAULT_MISO_DRIVE NRF_GPIO_PIN_S0S1
/** @brief SPI slave driver event types. */
typedef enum
{
NRFX_SPIS_BUFFERS_SET_DONE, //!< Memory buffer set event. Memory buffers have been set successfully to the SPI slave device, and SPI transaction can be done.
NRFX_SPIS_XFER_DONE, //!< SPI transaction event. SPI transaction has been completed.
NRFX_SPIS_EVT_TYPE_MAX //!< Enumeration upper bound.
} nrfx_spis_evt_type_t;
/** @brief SPI slave driver event structure. */
typedef struct
{
nrfx_spis_evt_type_t evt_type; //!< Type of the event.
size_t rx_amount; //!< Number of bytes received in the last transaction. This parameter is only valid for @ref NRFX_SPIS_XFER_DONE events.
size_t tx_amount; //!< Number of bytes transmitted in the last transaction. This parameter is only valid for @ref NRFX_SPIS_XFER_DONE events.
} nrfx_spis_evt_t;
/** @brief The default configuration of the SPI slave instance. */
#define NRFX_SPIS_DEFAULT_CONFIG \
{ \
.miso_pin = NRFX_SPIS_PIN_NOT_USED, \
.mosi_pin = NRFX_SPIS_PIN_NOT_USED, \
.sck_pin = NRFX_SPIS_PIN_NOT_USED, \
.csn_pin = NRFX_SPIS_PIN_NOT_USED, \
.mode = NRF_SPIS_MODE_0, \
.bit_order = NRF_SPIS_BIT_ORDER_MSB_FIRST, \
.csn_pullup = NRFX_SPIS_DEFAULT_CSN_PULLUP, \
.miso_drive = NRFX_SPIS_DEFAULT_MISO_DRIVE, \
.def = NRFX_SPIS_DEFAULT_DEF, \
.orc = NRFX_SPIS_DEFAULT_ORC, \
.irq_priority = NRFX_SPIS_DEFAULT_CONFIG_IRQ_PRIORITY, \
}
/** @brief SPI peripheral device configuration data. */
typedef struct
{
uint32_t miso_pin; //!< SPI MISO pin (optional).
/**< Set @ref NRFX_SPIS_PIN_NOT_USED
* if this signal is not needed. */
uint32_t mosi_pin; //!< SPI MOSI pin (optional).
/**< Set @ref NRFX_SPIS_PIN_NOT_USED
* if this signal is not needed. */
uint32_t sck_pin; //!< SPI SCK pin.
uint32_t csn_pin; //!< SPI CSN pin.
nrf_spis_mode_t mode; //!< SPI mode.
nrf_spis_bit_order_t bit_order; //!< SPI transaction bit order.
nrf_gpio_pin_pull_t csn_pullup; //!< CSN pin pull-up configuration.
nrf_gpio_pin_drive_t miso_drive; //!< MISO pin drive configuration.
uint8_t def; //!< Character clocked out in case of an ignored transaction.
uint8_t orc; //!< Character clocked out after an over-read of the transmit buffer.
uint8_t irq_priority; //!< Interrupt priority.
} nrfx_spis_config_t;
/**
* @brief SPI slave driver event handler type.
*
* @param[in] p_event Pointer to the event structure. The structure is
* allocated on the stack so it is valid only until
* the event handler returns.
* @param[in] p_context Context set on initialization.
*/
typedef void (*nrfx_spis_event_handler_t)(nrfx_spis_evt_t const * p_event,
void * p_context);
/**
* @brief Function for initializing the SPI slave driver instance.
*
* @note When the nRF52 Anomaly 109 workaround for SPIS is enabled, this function
* initializes the GPIOTE driver as well, and uses one of GPIOTE channels
* to detect falling edges on CSN pin.
*
* @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 Function to be called by the SPI slave driver upon event.
* Must not be NULL.
* @param[in] p_context Context passed to the event handler.
*
* @retval NRFX_SUCCESS The initialization was successful.
* @retval NRFX_ERROR_INVALID_STATE The instance is already initialized.
* @retval NRFX_ERROR_INVALID_PARAM Invalid parameter is supplied.
* @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_INTERNAL GPIOTE channel for detecting falling edges
* on CSN pin cannot be initialized. Possible
* only when using nRF52 Anomaly 109 workaround.
*/
nrfx_err_t nrfx_spis_init(nrfx_spis_t const * const p_instance,
nrfx_spis_config_t const * p_config,
nrfx_spis_event_handler_t event_handler,
void * p_context);
/**
* @brief Function for uninitializing the SPI slave driver instance.
*
* @param[in] p_instance Pointer to the driver instance structure.
*/
void nrfx_spis_uninit(nrfx_spis_t const * const p_instance);
/**
* @brief Function for preparing the SPI slave instance for a single SPI transaction.
*
* This function prepares the SPI slave device to be ready for a single SPI transaction. It configures
* the SPI slave device to use the memory supplied with the function call in SPI transactions.
*
* When either the memory buffer configuration or the SPI transaction has been
* completed, the event callback function will be called with the appropriate event
* @ref nrfx_spis_evt_type_t. The callback function can be called before returning from
* this function, because it is called from the SPI slave interrupt context.
*
* @note This function can be called from the callback function context.
*
* @note Client applications must call this function after every @ref NRFX_SPIS_XFER_DONE event if
* the SPI slave driver must be prepared for a possible new SPI transaction.
*
* @note Peripherals using EasyDMA (including SPIS) 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_tx_buffer Pointer to the TX buffer. Can be NULL when the buffer length is zero.
* @param[in] p_rx_buffer Pointer to the RX buffer. Can be NULL when the buffer length is zero.
* @param[in] tx_buffer_length Length of the TX buffer in bytes.
* @param[in] rx_buffer_length Length of the RX buffer in bytes.
*
* @retval NRFX_SUCCESS The operation was successful.
* @retval NRFX_ERROR_INVALID_STATE The operation failed because the SPI slave device is in an incorrect state.
* @retval NRFX_ERROR_INVALID_ADDR The provided buffers are not placed in the Data
* RAM region.
* @retval NRFX_ERROR_INVALID_LENGTH Provided lengths exceed the EasyDMA limits for the peripheral.
* @retval NRFX_ERROR_INTERNAL The operation failed because of an internal error.
*/
nrfx_err_t nrfx_spis_buffers_set(nrfx_spis_t const * const p_instance,
uint8_t const * p_tx_buffer,
size_t tx_buffer_length,
uint8_t * p_rx_buffer,
size_t rx_buffer_length);
/** @} */
void nrfx_spis_0_irq_handler(void);
void nrfx_spis_1_irq_handler(void);
void nrfx_spis_2_irq_handler(void);
void nrfx_spis_3_irq_handler(void);
#ifdef __cplusplus
}
#endif
#endif // NRFX_SPIS_H__

View File

@@ -0,0 +1,245 @@
/**
* Copyright (c) 2015 - 2020, 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_SWI_H__
#define NRFX_SWI_H__
#include <nrfx.h>
#if NRFX_CHECK(NRFX_EGU_ENABLED)
#include <hal/nrf_egu.h>
#endif
#ifndef SWI_COUNT
#define SWI_COUNT EGU_COUNT
#endif
#ifdef __cplusplus
extern "C" {
#endif
/**
* @defgroup nrfx_swi SWI driver
* @{
* @ingroup nrf_swi_egu
*
* @brief Driver for managing software interrupts (SWI).
*/
/** @brief SWI instance. */
typedef uint8_t nrfx_swi_t;
/**
* @brief SWI user flags.
*
* User flags are set during the SWI trigger and passed to the callback function as an argument.
*/
typedef uint16_t nrfx_swi_flags_t;
/** @brief Unallocated instance value. */
#define NRFX_SWI_UNALLOCATED ((nrfx_swi_t)0xFFuL)
/** @brief Default SWI priority. */
#define NRFX_SWI_DEFAULT_PRIORITY APP_IRQ_PRIORITY_LOWEST
/**
* @brief SWI handler function.
*
* @param swi SWI instance.
* @param flags User flags.
*/
typedef void (*nrfx_swi_handler_t)(nrfx_swi_t swi, nrfx_swi_flags_t flags);
/**
* @brief Function for allocating the first unused SWI instance and setting a handler.
*
* If provided handler is not NULL, an allocated SWI has its interrupt enabled by default.
* The interrupt can be disabled by @ref nrfx_swi_int_disable.
*
* @param[out] p_swi Points to a place where the allocated SWI instance
* number is to be stored.
* @param[in] event_handler Event handler function.
* If NULL, no interrupt will be enabled.
* It can be NULL only if the EGU driver is enabled.
* For classic SWI, it must be a valid handler pointer.
* @param[in] irq_priority Interrupt priority.
*
* @retval NRFX_SUCCESS The SWI was successfully allocated.
* @retval NRFX_ERROR_NO_MEM There is no available SWI to be used.
*/
nrfx_err_t nrfx_swi_alloc(nrfx_swi_t * p_swi,
nrfx_swi_handler_t event_handler,
uint32_t irq_priority);
/**
* @brief Function for disabling an allocated SWI interrupt.
*
* Use @ref nrfx_swi_int_enable to re-enable the interrupt.
*
* @param[in] swi SWI instance.
*/
void nrfx_swi_int_disable(nrfx_swi_t swi);
/**
* @brief Function for enabling an allocated SWI interrupt.
*
* @param[in] swi SWI instance.
*/
void nrfx_swi_int_enable(nrfx_swi_t swi);
/**
* @brief Function for freeing a previously allocated SWI.
*
* @param[in,out] p_swi SWI instance to free. The value is changed to
* @ref NRFX_SWI_UNALLOCATED on success.
*/
void nrfx_swi_free(nrfx_swi_t * p_swi);
/** @brief Function for freeing all allocated SWIs. */
void nrfx_swi_all_free(void);
/**
* @brief Function for triggering the SWI.
*
* @param[in] swi SWI to trigger.
* @param[in] flag_number Number of user flag to trigger.
*/
void nrfx_swi_trigger(nrfx_swi_t swi,
uint8_t flag_number);
/**
* @brief Function for checking if the specified SWI is currently allocated.
*
* @param[in] swi SWI instance.
*
* @retval true The SWI instance is allocated.
* @retval false The SWI instance is not allocated.
*/
bool nrfx_swi_is_allocated(nrfx_swi_t swi);
#if NRFX_CHECK(NRFX_EGU_ENABLED) || defined(__NRFX_DOXYGEN__)
/**
* @brief Function for returning the base address of the EGU peripheral
* associated with the specified SWI instance.
*
* @param[in] swi SWI instance.
*
* @return EGU base address or NULL if the specified SWI instance number
* is too high.
*/
__STATIC_INLINE NRF_EGU_Type * nrfx_swi_egu_instance_get(nrfx_swi_t swi)
{
#if (EGU_COUNT < SWI_COUNT)
if (swi >= EGU_COUNT)
{
return NULL;
}
#endif
uint32_t offset = ((uint32_t)swi) * ((uint32_t)NRF_EGU1 - (uint32_t)NRF_EGU0);
return (NRF_EGU_Type *)((uint32_t)NRF_EGU0 + offset);
}
/**
* @brief Function for returning the EGU trigger task address.
*
* @param[in] swi SWI instance.
* @param[in] channel Number of the EGU channel.
*
* @return Address of the EGU trigger task.
*/
__STATIC_INLINE uint32_t nrfx_swi_task_trigger_address_get(nrfx_swi_t swi,
uint8_t channel)
{
NRFX_ASSERT(nrfx_swi_is_allocated(swi));
NRF_EGU_Type * p_egu = nrfx_swi_egu_instance_get(swi);
#if (EGU_COUNT < SWI_COUNT)
if (p_egu == NULL)
{
return 0;
}
#endif
return (uint32_t)nrf_egu_task_trigger_address_get(p_egu, channel);
}
/**
* @brief Function for returning the EGU-triggered event address.
*
* @param[in] swi SWI instance.
* @param[in] channel Number of the EGU channel.
*
* @return Address of the EGU-triggered event.
*/
__STATIC_INLINE uint32_t nrfx_swi_event_triggered_address_get(nrfx_swi_t swi,
uint8_t channel)
{
NRFX_ASSERT(nrfx_swi_is_allocated(swi));
NRF_EGU_Type * p_egu = nrfx_swi_egu_instance_get(swi);
#if (EGU_COUNT < SWI_COUNT)
if (p_egu == NULL)
{
return 0;
}
#endif
return (uint32_t)nrf_egu_event_triggered_address_get(p_egu, channel);
}
#endif // NRFX_CHECK(NRFX_EGU_ENABLED) || defined(__NRFX_DOXYGEN__)
/** @} */
void nrfx_swi_0_irq_handler(void);
void nrfx_swi_1_irq_handler(void);
void nrfx_swi_2_irq_handler(void);
void nrfx_swi_3_irq_handler(void);
void nrfx_swi_4_irq_handler(void);
void nrfx_swi_5_irq_handler(void);
#ifdef __cplusplus
}
#endif
#endif // NRFX_SWI_H__

View File

@@ -0,0 +1,135 @@
/**
* Copyright (c) 2016 - 2020, 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_SYSTICK_H__
#define NRFX_SYSTICK_H__
#include <nrfx.h>
#include <hal/nrf_systick.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @defgroup nrfx_systick ARM(R) SysTick driver
* @{
* @ingroup nrf_systick
*
* @brief ARM(R) SysTick driver.
*
* This driver configures ARM(R) SysTick as a free-running timer.
* This timer is used to generate delays and pool for timeouts.
* Only relatively short timeouts are supported.
* The SysTick works on 64MHz and is 24-bit wide.
* This means that it overflows around 4 times per second and
* around 250 microseconds will be the highest supported time in the library.
* As it is hard to detect if the overflow is generated without
* using interrupts, the maximum delay range is halved for safety reasons.
*/
/**
* @brief The value type that holds the SysTick state.
*
* This variable is used to count the requested timeout.
* @sa nrfx_systick_get
*/
typedef struct {
uint32_t time; //!< Registered time value.
} nrfx_systick_state_t;
/**
* @brief Function for configuring and starting the timer.
*
* Function configures SysTick as a free-running timer without interrupt.
*/
void nrfx_systick_init(void);
/**
* @brief Function for getting the current SysTick state.
*
* Function gets the current state of the SysTick timer.
* It can be used to check time-out by @ref nrfx_systick_test.
*
* @param[out] p_state The pointer to the state variable to be filled.
*/
void nrfx_systick_get(nrfx_systick_state_t * p_state);
/**
* @brief Function for testing if the current time is higher in relation to the remembered state.
*
* @param[in] p_state Remembered state set by @ref nrfx_systick_get
* @param[in] us Required time-out.
*
* @retval true The current time is higher than the specified state plus the given time-out.
* @retval false The current time is lower than the specified state plus the given time-out.
*/
bool nrfx_systick_test(nrfx_systick_state_t const * p_state, uint32_t us);
/**
* @brief Function for delaying the execution for the specified amount of CPU ticks.
*
* @param[in] ticks Number of CPU ticks when the execution is blocked.
*/
void nrfx_systick_delay_ticks(uint32_t ticks);
/**
* @brief Function for delaying the execution for the specified amount of microseconds.
*
* @param[in] us Number of microseconds when the execution is blocked.
*/
void nrfx_systick_delay_us(uint32_t us);
/**
* @brief Function for delaying the execution for the specified amount of milliseconds.
*
* This delay function removes the limits of the highest possible delay value.
*
* @param[in] ms Number of milliseconds when the execution is blocked.
*/
void nrfx_systick_delay_ms(uint32_t ms);
/** @} */
#ifdef __cplusplus
}
#endif
#endif /* NRFX_SYSTICK_H__ */

View File

@@ -0,0 +1,162 @@
/**
* Copyright (c) 2019 - 2020, 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_TEMP_H__
#define NRFX_TEMP_H__
#include <nrfx.h>
#include <hal/nrf_temp.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @defgroup nrfx_temp TEMP driver
* @{
* @ingroup nrf_temp
* @brief Temperature sensor (TEMP) driver.
*/
/** @brief Structure for TEMP configuration. */
typedef struct
{
uint8_t interrupt_priority; /**< Interrupt priority. */
} nrfx_temp_config_t;
/** @brief TEMP default configuration. */
#define NRFX_TEMP_DEFAULT_CONFIG \
{ \
.interrupt_priority = NRFX_TEMP_DEFAULT_CONFIG_IRQ_PRIORITY, \
}
/**
* @brief TEMP driver data ready handler type.
*
* @param temperature Raw temperature in a 2's complement signed value
* representation. This value can be converted to Celsius
* scale using the @ref nrfx_temp_calculate() function.
*/
typedef void (* nrfx_temp_data_handler_t)(int32_t raw_temperature);
/**
* @brief Function for initializing the TEMP driver.
*
* @param[in] p_config Pointer to the structure with initial configuration.
* @param[in] handler Data handler provided by the user. If not provided,
* the driver is initialized in blocking mode.
*
* @retval NRFX_SUCCESS Driver was successfully initialized.
* @retval NRFX_ERROR_ALREADY_INITIALIZED Driver was already initialized.
*/
nrfx_err_t nrfx_temp_init(nrfx_temp_config_t const * p_config, nrfx_temp_data_handler_t handler);
/** @brief Function for uninitializing the TEMP driver. */
void nrfx_temp_uninit(void);
/**
* @brief Function for getting the temperature measurement in a 2's complement
* signed value representation.
*
* This function returns the last value prepared by the TEMP peripheral.
* In blocking mode, it should be used after calling the @ref nrfx_temp_measure()
* function. In non-blocking mode, it is called internally by the driver,
* and the value it returns is passed to the data handler.
*
* @return Temperature measurement result in a 2's complement signed value
* representation.
*/
__STATIC_INLINE int32_t nrfx_temp_result_get(void);
/**
* @brief Function for calculating the temperature value in Celsius scale from raw data.
*
* The returned temperature value is in Celsius scale, multiplied by 100
* For example, the actual temperature of 25.75[C] will be returned as a 2575 signed integer.
* Measurement accuracy is 0.25[C].
*
* @param[in] raw_measurement Temperature value in a 2's complement signed
* value representation.
*
* @return Temperature measurement result.
*/
int32_t nrfx_temp_calculate(int32_t raw_measurement);
/**
* @brief Function for starting the temperature measurement.
*
* Non-blocking mode:
* This function returns immediately. After a measurement, the handler specified
* during initialization is called, with measurement result as the parameter.
*
* Blocking mode:
* This function waits until the measurement is finished. The value should be read
* using the @ref nrfx_temp_result_get() function.
*
* @retval NRFX_SUCCESS In non-blocking mode: Measurement was started.
* An interrupt will be generated soon. <br>
* In blocking mode:
* Measurement was started and finished. Data can
* be read using the @ref nrfx_temp_result_get() function.
* @retval NRFX_ERROR_INTERNAL In non-blocking mode:
* Not applicable. <br>
* In blocking mode:
* Measurement data ready event did not occur.
*/
nrfx_err_t nrfx_temp_measure(void);
#ifndef SUPPRESS_INLINE_IMPLEMENTATION
__STATIC_INLINE int32_t nrfx_temp_result_get(void)
{
return nrf_temp_result_get(NRF_TEMP);
}
#endif // SUPPRESS_INLINE_IMPLEMENTATION
/** @} */
void nrfx_temp_irq_handler(void);
#ifdef __cplusplus
}
#endif
#endif // NRFX_TEMP_H__

View File

@@ -0,0 +1,409 @@
/**
* Copyright (c) 2015 - 2020, 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__

View File

@@ -0,0 +1,407 @@
/**
* Copyright (c) 2015 - 2020, 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__

View File

@@ -0,0 +1,56 @@
/**
* Copyright (c) 2019 - 2020, 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

View File

@@ -0,0 +1,448 @@
/**
* Copyright (c) 2015 - 2020, 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__

View File

@@ -0,0 +1,403 @@
/**
* Copyright (c) 2015 - 2020, 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_TWIS_H__
#define NRFX_TWIS_H__
#include <nrfx.h>
#include <hal/nrf_twis.h>
#include <hal/nrf_gpio.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @defgroup nrfx_twis TWIS driver
* @{
* @ingroup nrf_twis
* @brief Two Wire Interface Slave with EasyDMA (TWIS) peripheral driver.
*/
/** @brief TWIS driver instance data structure. */
typedef struct
{
NRF_TWIS_Type * p_reg; ///< Pointer to a structure with TWIS registers.
uint8_t drv_inst_idx; ///< Index of the driver instance. For internal use only.
} nrfx_twis_t;
#ifndef __NRFX_DOXYGEN__
enum {
#if NRFX_CHECK(NRFX_TWIS0_ENABLED)
NRFX_TWIS0_INST_IDX,
#endif
#if NRFX_CHECK(NRFX_TWIS1_ENABLED)
NRFX_TWIS1_INST_IDX,
#endif
#if NRFX_CHECK(NRFX_TWIS2_ENABLED)
NRFX_TWIS2_INST_IDX,
#endif
#if NRFX_CHECK(NRFX_TWIS3_ENABLED)
NRFX_TWIS3_INST_IDX,
#endif
NRFX_TWIS_ENABLED_COUNT
};
#endif
/** @brief Macro for creating a TWIS driver instance. */
#define NRFX_TWIS_INSTANCE(id) \
{ \
.p_reg = NRFX_CONCAT_2(NRF_TWIS, id), \
.drv_inst_idx = NRFX_CONCAT_3(NRFX_TWIS, id, _INST_IDX), \
}
/** @brief Event callback function event definitions. */
typedef enum
{
NRFX_TWIS_EVT_READ_REQ, ///< Read request detected.
/**< If there is no buffer prepared, buf_req flag in the even will be set.
Call then @ref nrfx_twis_tx_prepare to give parameters for buffer.
*/
NRFX_TWIS_EVT_READ_DONE, ///< Read request finished - free any data.
NRFX_TWIS_EVT_READ_ERROR, ///< Read request finished with error.
NRFX_TWIS_EVT_WRITE_REQ, ///< Write request detected.
/**< If there is no buffer prepared, buf_req flag in the even will be set.
Call then @ref nrfx_twis_rx_prepare to give parameters for buffer.
*/
NRFX_TWIS_EVT_WRITE_DONE, ///< Write request finished - process data.
NRFX_TWIS_EVT_WRITE_ERROR, ///< Write request finished with error.
NRFX_TWIS_EVT_GENERAL_ERROR ///< Error that happens not inside WRITE or READ transaction.
} nrfx_twis_evt_type_t;
/**
* @brief Possible error sources.
*
* This is flag enum - values from this enum can be connected using logical or operator.
* @note
* You can use directly @ref nrf_twis_error_t. Error type enum is redefined here because
* of possible future extension (eg. supporting timeouts and synchronous mode).
*/
typedef enum
{
NRFX_TWIS_ERROR_OVERFLOW = NRF_TWIS_ERROR_OVERFLOW, /**< RX buffer overflow detected, and prevented. */
NRFX_TWIS_ERROR_DATA_NACK = NRF_TWIS_ERROR_DATA_NACK, /**< NACK sent after receiving a data byte. */
NRFX_TWIS_ERROR_OVERREAD = NRF_TWIS_ERROR_OVERREAD, /**< TX buffer over-read detected, and prevented. */
NRFX_TWIS_ERROR_UNEXPECTED_EVENT = 1 << 8 /**< Unexpected event detected by state machine. */
} nrfx_twis_error_t;
/** @brief TWIS driver event structure. */
typedef struct
{
nrfx_twis_evt_type_t type; ///< Event type.
union
{
bool buf_req; ///< Flag for @ref NRFX_TWIS_EVT_READ_REQ and @ref NRFX_TWIS_EVT_WRITE_REQ.
/**< Information if transmission buffer requires to be prepared. */
uint32_t tx_amount; ///< Data for @ref NRFX_TWIS_EVT_READ_DONE.
uint32_t rx_amount; ///< Data for @ref NRFX_TWIS_EVT_WRITE_DONE.
uint32_t error; ///< Data for @ref NRFX_TWIS_EVT_GENERAL_ERROR.
} data; ///< Union to store event data.
} nrfx_twis_evt_t;
/**
* @brief TWI slave event callback function type.
*
* @param[in] p_event Event information structure.
*/
typedef void (*nrfx_twis_event_handler_t)(nrfx_twis_evt_t const * p_event);
/** @brief Structure for TWIS configuration. */
typedef struct
{
uint32_t addr[2]; //!< Set addresses that this slave should respond. Set 0 to disable.
uint32_t scl; //!< SCL pin number.
uint32_t sda; //!< SDA pin number.
nrf_gpio_pin_pull_t scl_pull; //!< SCL pin pull.
nrf_gpio_pin_pull_t sda_pull; //!< SDA pin pull.
uint8_t interrupt_priority; //!< The priority of interrupt for the module to be set.
} nrfx_twis_config_t;
/** @brief Generate the default configuration for the TWIS driver instance. */
#define NRFX_TWIS_DEFAULT_CONFIG \
{ \
.addr = { NRFX_TWIS_DEFAULT_CONFIG_ADDR0, \
NRFX_TWIS_DEFAULT_CONFIG_ADDR1 }, \
.scl = 31, \
.sda = 31, \
.scl_pull = (nrf_gpio_pin_pull_t)NRFX_TWIS_DEFAULT_CONFIG_SCL_PULL, \
.sda_pull = (nrf_gpio_pin_pull_t)NRFX_TWIS_DEFAULT_CONFIG_SDA_PULL, \
.interrupt_priority = NRFX_TWIS_DEFAULT_CONFIG_IRQ_PRIORITY \
}
/**
* @brief Function for initializing the TWIS driver instance.
*
* Function initializes and enables the TWIS driver.
* @attention After driver initialization enable it with @ref nrfx_twis_enable.
*
* @param[in] p_instance Pointer to the driver instance structure.
* @attention @em p_instance has to be global object.
* It will be used by interrupts so make it sure that object
* is not destroyed when function is leaving.
* @param[in] p_config Pointer to the structure with the initial configuration.
* @param[in] event_handler Event handler provided by the user.
*
* @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 NRFX_PRS_ENABLED
* is set to a value other than zero.
*/
nrfx_err_t nrfx_twis_init(nrfx_twis_t const * p_instance,
nrfx_twis_config_t const * p_config,
nrfx_twis_event_handler_t event_handler);
/**
* @brief Function for uninitializing the TWIS driver instance.
*
* Function uninitializes the peripheral and resets all registers to default values.
*
* @note
* It is safe to call nrfx_twis_uninit even before initialization.
* Actually, @ref nrfx_twis_init function calls this function to
* make sure that TWIS state is known.
* @note
* If TWIS driver was in uninitialized state before calling this function,
* the selected pins would not be reset to default configuration.
*
* @param[in] p_instance Pointer to the driver instance structure.
*/
void nrfx_twis_uninit(nrfx_twis_t const * p_instance);
/**
* @brief Function for enabling the TWIS instance.
*
* This function enables the TWIS instance.
* Function defined if there is need for dynamically enabling and disabling the peripheral.
* Use @ref nrfx_twis_enable and @ref nrfx_twis_disable functions.
* They do not change any configuration registers.
*
* @param p_instance Pointer to the driver instance structure.
*/
void nrfx_twis_enable(nrfx_twis_t const * p_instance);
/**
* @brief Function for disabling the TWIS instance.
*
* This function disables the TWIS instance, which gives possibility to turn off the TWIS while
* holding configuration done by @ref nrfx_twis_init.
*
* @param p_instance Pointer to the driver instance structure.
*/
void nrfx_twis_disable(nrfx_twis_t const * p_instance);
/**
* @brief Function for getting and clearing the last error flags.
*
* This function gets the information about errors.
* This is also the only possibility to exit from the error substate of the internal state machine.
* @attention
* This function clears error state and flags.
*
* @param[in] p_instance Pointer to the driver instance structure.
*
* @return Error flags defined in @ref nrfx_twis_error_t.
*/
uint32_t nrfx_twis_error_get_and_clear(nrfx_twis_t const * p_instance);
/**
* @brief Function for preparing the data for sending.
*
* This function is to be used in response to the @ref NRFX_TWIS_EVT_READ_REQ event.
*
* @note Peripherals using EasyDMA (including TWIS) 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.
* @attention Transmission buffer must be placed in RAM.
*
* @param[in] p_instance Pointer to the driver instance structure.
* @param[in] p_buf Transmission buffer.
* @param[in] size Maximum number of bytes that master may read from buffer given.
*
* @retval NRFX_SUCCESS The preparation finished properly.
* @retval NRFX_ERROR_INVALID_ADDR The given @em p_buf is not placed inside the RAM.
* @retval NRFX_ERROR_INVALID_LENGTH There is a wrong value in the @em size parameter.
* @retval NRFX_ERROR_INVALID_STATE The module is not initialized or not enabled.
*/
nrfx_err_t nrfx_twis_tx_prepare(nrfx_twis_t const * p_instance,
void const * p_buf,
size_t size);
/**
* @brief Function for getting the number of transmitted bytes.
*
* This function returns the number of bytes sent.
* This function can be called after @ref NRFX_TWIS_EVT_READ_DONE or @ref NRFX_TWIS_EVT_READ_ERROR events.
*
* @param[in] p_instance Pointer to the driver instance structure.
*
* @return Number of bytes sent.
*/
__STATIC_INLINE size_t nrfx_twis_tx_amount(nrfx_twis_t const * p_instance);
/**
* @brief Function for preparing the data for receiving.
*
* This function must be used in response to the @ref NRFX_TWIS_EVT_WRITE_REQ event.
*
* @note Peripherals using EasyDMA (including TWIS) 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_buf Buffer that is to be filled with received data.
* @param[in] size Size of the buffer (maximum amount of data to receive).
*
* @retval NRFX_SUCCESS The preparation finished properly.
* @retval NRFX_ERROR_INVALID_ADDR The given @em p_buf is not placed inside the RAM.
* @retval NRFX_ERROR_INVALID_LENGTH There is a wrong value in the @em size parameter.
* @retval NRFX_ERROR_INVALID_STATE The module is not initialized or not enabled.
*/
nrfx_err_t nrfx_twis_rx_prepare(nrfx_twis_t const * p_instance,
void * p_buf,
size_t size);
/**
* @brief Function for getting the number of received bytes.
*
* This function returns number of bytes received.
* It can be called after @ref NRFX_TWIS_EVT_WRITE_DONE or @ref NRFX_TWIS_EVT_WRITE_ERROR events.
*
* @param[in] p_instance Pointer to the driver instance structure.
*
* @return Number of bytes received.
*/
__STATIC_INLINE size_t nrfx_twis_rx_amount(nrfx_twis_t const * p_instance);
/**
* @brief Function for checking if the driver is busy right now.
*
* This function tests the actual driver substate.
* If the driver is in any other state than IDLE or ERROR, this function returns true.
*
* @param[in] p_instance Pointer to the driver instance structure.
*
* @retval true The driver is in state other than ERROR or IDLE.
* @retval false There is no transmission pending.
*/
bool nrfx_twis_is_busy(nrfx_twis_t const * p_instance);
/**
* @brief Function for checking if the driver is waiting for a TX buffer.
*
* If this function returns true, the driver is stalled expecting
* of the @ref nrfx_twis_tx_prepare function call.
*
* @param[in] p_instance Pointer to the driver instance structure.
*
* @retval true The driver is waiting for @ref nrfx_twis_tx_prepare.
* @retval false The driver is not in the state where it is waiting for preparing a TX buffer.
*/
bool nrfx_twis_is_waiting_tx_buff(nrfx_twis_t const * p_instance);
/**
* @brief Function for checking if the driver is waiting for an RX buffer.
*
* If this function returns true, the driver is stalled expecting
* of the @ref nrfx_twis_rx_prepare function call.
*
* @param[in] p_instance Pointer to the driver instance structure.
*
* @retval true The driver is waiting for @ref nrfx_twis_rx_prepare.
* @retval false The driver is not in the state where it is waiting for preparing an RX buffer.
*/
bool nrfx_twis_is_waiting_rx_buff(nrfx_twis_t const * p_instance);
/**
* @brief Function for checking if the driver is sending data.
*
* If this function returns true, there is an ongoing output transmission.
*
* @param[in] p_instance Pointer to the driver instance structure.
*
* @retval true There is an ongoing output transmission.
* @retval false The driver is in other state.
*/
bool nrfx_twis_is_pending_tx(nrfx_twis_t const * p_instance);
/**
* @brief Function for checking if the driver is receiving data.
*
* If this function returns true, there is an ongoing input transmission.
*
* @param[in] p_instance Pointer to the driver instance structure.
*
* @retval true There is an ongoing input transmission.
* @retval false The driver is in other state.
*/
bool nrfx_twis_is_pending_rx(nrfx_twis_t const * p_instance);
#ifndef SUPPRESS_INLINE_IMPLEMENTATION
__STATIC_INLINE size_t nrfx_twis_tx_amount(nrfx_twis_t const * p_instance)
{
return nrf_twis_tx_amount_get(p_instance->p_reg);
}
__STATIC_INLINE size_t nrfx_twis_rx_amount(nrfx_twis_t const * p_instance)
{
return nrf_twis_rx_amount_get(p_instance->p_reg);
}
#endif // SUPPRESS_INLINE_IMPLEMENTATION
/** @} */
void nrfx_twis_0_irq_handler(void);
void nrfx_twis_1_irq_handler(void);
void nrfx_twis_2_irq_handler(void);
void nrfx_twis_3_irq_handler(void);
#ifdef __cplusplus
}
#endif
#endif // NRFX_TWIS_H__

View File

@@ -0,0 +1,360 @@
/**
* Copyright (c) 2015 - 2020, 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__

View File

@@ -0,0 +1,359 @@
/**
* Copyright (c) 2015 - 2020, 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__

View File

@@ -0,0 +1,872 @@
/**
* Copyright (c) 2016 - 2020, 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_USBD_H__
#define NRFX_USBD_H__
#include <nrfx.h>
#include <hal/nrf_usbd.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @defgroup nrfx_usbd USBD driver
* @{
* @ingroup nrf_usbd
* @brief Universal Serial Bus Device (USBD) peripheral driver.
*/
/**
* @brief Number of bytes in the endpoint.
*/
#define NRFX_USBD_EPSIZE 64
/**
* @brief Number of bytes for isochronous endpoints.
*
* Number of bytes for isochronous endpoints in total.
* This number would be shared between IN and OUT endpoint.
* It may be also assigned totaly to one endpoint.
* @sa nrf_usbd_isosplit_set
* @sa nrf_usbd_isosplit_get
*/
#define NRFX_USBD_ISOSIZE 1024
/**
* @brief The size of internal feeder buffer.
*
* @sa nrfx_usbd_feeder_buffer_get
*/
#define NRFX_USBD_FEEDER_BUFFER_SIZE NRFX_USBD_EPSIZE
/**
* @name Macros for creating endpoint identifiers.
*
* Auxiliary macros for creating endpoint identifiers compatible with the USB specification.
* @{
* @brief Create identifier for IN endpoint.
*
* Simple macro to create IN endpoint identifier for given endpoint number.
*
* @param[in] n Endpoint number.
*
* @return Endpoint identifier that connects endpoint number and endpoint direction.
*/
#define NRFX_USBD_EPIN(n) ((nrfx_usbd_ep_t)NRF_USBD_EPIN(n))
/**
* @brief Create identifier for OUT endpoint.
*
* Simple macro to create OUT endpoint identifier for given endpoint number.
*
* @param[in] n Endpoint number.
*
* @return Endpoint identifier that connects endpoint number and endpoint direction.
*/
#define NRFX_USBD_EPOUT(n) ((nrfx_usbd_ep_t)NRF_USBD_EPOUT(n))
/** @} */
/**
* @brief Endpoint identifier.
*
* Endpoint identifier used in the driver.
* This endpoint number is consistent with USB 2.0 specification.
*/
typedef enum
{
NRFX_USBD_EPOUT0 = NRF_USBD_EPOUT(0), /**< Endpoint OUT 0 */
NRFX_USBD_EPOUT1 = NRF_USBD_EPOUT(1), /**< Endpoint OUT 1 */
NRFX_USBD_EPOUT2 = NRF_USBD_EPOUT(2), /**< Endpoint OUT 2 */
NRFX_USBD_EPOUT3 = NRF_USBD_EPOUT(3), /**< Endpoint OUT 3 */
NRFX_USBD_EPOUT4 = NRF_USBD_EPOUT(4), /**< Endpoint OUT 4 */
NRFX_USBD_EPOUT5 = NRF_USBD_EPOUT(5), /**< Endpoint OUT 5 */
NRFX_USBD_EPOUT6 = NRF_USBD_EPOUT(6), /**< Endpoint OUT 6 */
NRFX_USBD_EPOUT7 = NRF_USBD_EPOUT(7), /**< Endpoint OUT 7 */
NRFX_USBD_EPOUT8 = NRF_USBD_EPOUT(8), /**< Endpoint OUT 8 */
NRFX_USBD_EPIN0 = NRF_USBD_EPIN(0), /**< Endpoint IN 0 */
NRFX_USBD_EPIN1 = NRF_USBD_EPIN(1), /**< Endpoint IN 1 */
NRFX_USBD_EPIN2 = NRF_USBD_EPIN(2), /**< Endpoint IN 2 */
NRFX_USBD_EPIN3 = NRF_USBD_EPIN(3), /**< Endpoint IN 3 */
NRFX_USBD_EPIN4 = NRF_USBD_EPIN(4), /**< Endpoint IN 4 */
NRFX_USBD_EPIN5 = NRF_USBD_EPIN(5), /**< Endpoint IN 5 */
NRFX_USBD_EPIN6 = NRF_USBD_EPIN(6), /**< Endpoint IN 6 */
NRFX_USBD_EPIN7 = NRF_USBD_EPIN(7), /**< Endpoint IN 7 */
NRFX_USBD_EPIN8 = NRF_USBD_EPIN(8), /**< Endpoint IN 8 */
} nrfx_usbd_ep_t;
/**
* @brief Events generated by the driver.
*
* Enumeration of possible events that may be generated by the driver.
*/
typedef enum
{
NRFX_USBD_EVT_SOF, /**< Start Of Frame event on USB bus detected. */
NRFX_USBD_EVT_RESET, /**< Reset condition on USB bus detected. */
NRFX_USBD_EVT_SUSPEND, /**< This device should go to suspend mode now. */
NRFX_USBD_EVT_RESUME, /**< This device should resume from suspend now. */
NRFX_USBD_EVT_WUREQ, /**< Wakeup request - the USBD peripheral is ready to generate
WAKEUP signal after exiting low power mode. */
NRFX_USBD_EVT_SETUP, /**< Setup frame received and decoded. */
NRFX_USBD_EVT_EPTRANSFER, /**< For Rx (OUT: Host->Device):
* 1. The packet has been received but there is no buffer prepared for transfer already.
* 2. Whole transfer has been finished.
*
* For Tx (IN: Device->Host):
* The last packet from requested transfer has been transfered over USB bus and acknowledged.
*/
NRFX_USBD_EVT_CNT /**< Number of defined events. */
} nrfx_usbd_event_type_t;
/**
* @brief Endpoint status codes.
*
* Status codes that may be returned by @ref nrfx_usbd_ep_status_get or, except for
* @ref NRFX_USBD_EP_BUSY, reported together with @ref NRFX_USBD_EVT_EPTRANSFER.
*/
typedef enum
{
NRFX_USBD_EP_OK, /**< No error occured. */
NRFX_USBD_EP_WAITING, /**< Data received, no buffer prepared already - waiting for configured transfer. */
NRFX_USBD_EP_OVERLOAD, /**< Received number of bytes cannot fit given buffer.
* This error would also be returned when next_transfer function has been defined
* but currently received data cannot fit completely in current buffer.
* No data split from single endpoint transmission is supported.
*
* When this error is reported - data is left inside endpoint buffer.
* Clear endpoint or prepare new buffer and read it.
*/
NRFX_USBD_EP_ABORTED, /**< EP0 transfer can be aborted when new setup comes.
* Any other transfer can be aborted by USB reset or driver stopping.
*/
NRFX_USBD_EP_BUSY, /**< Transfer is in progress. */
} nrfx_usbd_ep_status_t;
/**
* @brief Event structure.
*
* Structure passed to event handler.
*/
typedef struct
{
nrfx_usbd_event_type_t type; /**< Event type. */
union
{
struct {
uint16_t framecnt; /**< Current value of frame counter. */
} sof; /**< Data available for @ref NRFX_USBD_EVT_SOF. */
struct {
nrfx_usbd_ep_t ep; /**< Endpoint number. */
} isocrc;
struct {
nrfx_usbd_ep_t ep; /**< Endpoint number. */
nrfx_usbd_ep_status_t status; /**< Status for the endpoint. */
} eptransfer;
} data; /**< Union to store event data. */
} nrfx_usbd_evt_t;
/**
* @brief USBD event callback function type.
*
* @param[in] p_event Event information structure.
*/
typedef void (*nrfx_usbd_event_handler_t)(nrfx_usbd_evt_t const * p_event);
/**
* @brief Universal data pointer.
*
* Universal data pointer that can be used for any type of transfer.
*/
typedef union
{
void const * tx; //!< Constant TX buffer pointer.
void * rx; //!< Writable RX buffer pointer.
uint32_t addr; //!< Numeric value used internally by the driver.
} nrfx_usbd_data_ptr_t;
/**
* @brief Structure to be filled with information about the next transfer.
*
* This is used mainly for transfer feeders and consumers.
* It describes a single endpoint transfer and therefore the size of the buffer
* can never be higher than the endpoint size.
*/
typedef struct
{
nrfx_usbd_data_ptr_t p_data; //!< Union with available data pointers used by the driver.
size_t size; //!< Size of the requested transfer.
} nrfx_usbd_ep_transfer_t;
/**
* @brief Flags for the current transfer.
*
* Flags configured for the transfer that can be merged using the bitwise 'or' operator (|).
*/
typedef enum
{
NRFX_USBD_TRANSFER_ZLP_FLAG = 1U << 0, //!< Add a zero-length packet.
} nrfx_usbd_transfer_flags_t;
/**
* @brief Total transfer configuration.
*
* This structure is used to configure total transfer information.
* It is used by internal built-in feeders and consumers.
*/
typedef struct
{
nrfx_usbd_data_ptr_t p_data; //!< Union with available data pointers used by the driver.
size_t size; //!< Total size of the requested transfer.
uint32_t flags; //!< Transfer flags.
/**< Use the @ref nrfx_usbd_transfer_flags_t values. */
} nrfx_usbd_transfer_t;
/**
* @brief Auxiliary macro for declaring IN transfer description with optional flags.
*
* The base macro for creating transfers with any configuration option.
*
* @param name Instance name.
* @param tx_buff Buffer to transfer.
* @param tx_size Transfer size.
* @param tx_flags Flags for the transfer (see @ref nrfx_usbd_transfer_flags_t).
*
* @return Configured variable with total transfer description.
*/
#define NRFX_USBD_TRANSFER_IN(name, tx_buff, tx_size, tx_flags) \
const nrfx_usbd_transfer_t name = { \
.p_data = { .tx = (tx_buff) }, \
.size = (tx_size), \
.flags = (tx_flags) \
}
/**
* @brief Helper macro for declaring OUT transfer item (@ref nrfx_usbd_transfer_t).
*
* @param name Instance name.
* @param rx_buff Buffer to transfer.
* @param rx_size Transfer size.
* */
#define NRFX_USBD_TRANSFER_OUT(name, rx_buff, rx_size) \
const nrfx_usbd_transfer_t name = { \
.p_data = { .rx = (rx_buff) }, \
.size = (rx_size), \
.flags = 0 \
}
/**
* @brief USBD transfer feeder.
*
* Pointer for a transfer feeder.
* Transfer feeder is a feedback function used to prepare a single
* TX (Device->Host) endpoint transfer.
*
* The transfers provided by the feeder must be simple:
* - The size of the transfer provided by this function is limited to a single endpoint buffer.
* Bigger transfers are not handled automatically in this case.
* - Flash transfers are not automatically supported- you must copy them to the RAM buffer before.
*
* @note
* This function may use @ref nrfx_usbd_feeder_buffer_get to gain a temporary buffer
* that can be used to prepare transfer.
*
* @param[out] p_next Structure with the data for the next transfer to be filled.
* Required only if the function returns true.
* @param[in,out] p_context Context variable configured with the transfer.
* @param[in] ep_size The endpoint size.
*
* @retval false The current transfer is the last one - you do not need to call
* the function again.
* @retval true There is more data to be prepared and when the current transfer
* finishes, the feeder function is expected to be called again.
*/
typedef bool (*nrfx_usbd_feeder_t)(nrfx_usbd_ep_transfer_t * p_next,
void * p_context,
size_t ep_size);
/**
* @brief USBD transfer consumer.
*
* Pointer for a transfer consumer.
* Transfer consumer is a feedback function used to prepare a single
* RX (Host->Device) endpoint transfer.
*
* The transfer must provide a buffer big enough to fit the whole data from the endpoint.
* Otherwise, the NRFX_USBD_EP_OVERLOAD event is generated.
*
* @param[out] p_next Structure with the data for the next transfer to be filled.
* Required only if the function returns true.
* @param[in,out] p_context Context variable configured with the transfer.
* @param[in] ep_size The endpoint size.
* @param[in] data_size Number of received bytes in the endpoint buffer.
*
* @retval false Current transfer is the last one - you do not need to call
* the function again.
* @retval true There is more data to be prepared and when current transfer
* finishes, the feeder function is expected to be called again.
*/
typedef bool (*nrfx_usbd_consumer_t)(nrfx_usbd_ep_transfer_t * p_next,
void * p_context,
size_t ep_size,
size_t data_size);
/**
* @brief Universal transfer handler.
*
* Union with feeder and consumer function pointer.
*/
typedef union
{
nrfx_usbd_feeder_t feeder; //!< Feeder function pointer.
nrfx_usbd_consumer_t consumer; //!< Consumer function pointer.
} nrfx_usbd_handler_t;
/**
* @brief USBD transfer descriptor.
*
* Universal structure that may hold the setup for callback configuration for
* IN or OUT type of the transfer.
*/
typedef struct
{
nrfx_usbd_handler_t handler; //!< Handler for the current transfer, function pointer.
void * p_context; //!< Context for the transfer handler.
} nrfx_usbd_handler_desc_t;
/**
* @brief Setup packet structure.
*
* Structure that contains interpreted SETUP packet as described in USB specification.
*/
typedef struct
{
uint8_t bmRequestType; //!< byte 0
uint8_t bRequest; //!< byte 1
uint16_t wValue; //!< byte 2, 3
uint16_t wIndex; //!< byte 4, 5
uint16_t wLength; //!< byte 6, 7
} nrfx_usbd_setup_t;
/**
* @brief Driver initialization.
*
* @param[in] event_handler Event handler provided by the user. Cannot be null.
*
* @retval NRFX_SUCCESS Initialization successful.
* @retval NRFX_ERROR_INVALID_STATE Driver was already initialized.
*/
nrfx_err_t nrfx_usbd_init(nrfx_usbd_event_handler_t event_handler);
/**
* @brief Driver deinitialization.
*/
void nrfx_usbd_uninit(void);
/**
* @brief Enable the USBD port.
*
* After calling this function USBD peripheral would be enabled.
* The USB LDO would be enabled.
* Enabled USBD peripheral would request HFCLK.
* This function does not enable external oscillator, so if it is not enabled by other part of the
* program after enabling USBD driver HFINT would be used for the USBD peripheral.
* It is perfectly fine until USBD is started. See @ref nrfx_usbd_start.
*
* In normal situation this function should be called in reaction to USBDETECTED
* event from POWER peripheral.
*
* Interrupts and USB pins pull-up would stay disabled until @ref nrfx_usbd_start
* function is called.
*/
void nrfx_usbd_enable(void);
/**
* @brief Disable the USBD port.
*
* After calling this function USBD peripheral would be disabled.
* No events would be detected or processed by the driver.
* Clock for the peripheral would be disconnected.
*/
void nrfx_usbd_disable(void);
/**
* @brief Start USB functionality.
*
* After calling this function USBD peripheral should be fully functional
* and all new incoming events / interrupts would be processed by the driver.
*
* Also only after calling this function host sees new connected device.
*
* Call this function when USBD power LDO regulator is ready - on USBPWRRDY event
* from POWER peripheral.
*
* Before USBD interrupts are enabled, external HFXO is requested.
*
* @param enable_sof The flag that is used to enable SOF processing.
* If it is false, SOF interrupt is left disabled and will not be generated.
* This improves power saving if SOF is not required.
*
* @note If the isochronous endpoints are going to be used,
* it is required to enable the SOF.
* In other case any isochronous endpoint would stay busy
* after first transmission.
*/
void nrfx_usbd_start(bool enable_sof);
/**
* @brief Stop USB functionality.
*
* This function disables USBD pull-up and interrupts.
*
* The HFXO request is released in this function.
*
* @note
* This function can also be used to logically disconnect USB from the HOST that
* would force it to enumerate device after calling @ref nrfx_usbd_start.
*/
void nrfx_usbd_stop(void);
/**
* @brief Check if driver is initialized.
*
* @retval false Driver is not initialized.
* @retval true Driver is initialized.
*/
bool nrfx_usbd_is_initialized(void);
/**
* @brief Check if driver is enabled.
*
* @retval false Driver is disabled.
* @retval true Driver is enabled.
*/
bool nrfx_usbd_is_enabled(void);
/**
* @brief Check if driver is started.
*
* @retval false Driver is not started.
* @retval true Driver is started (fully functional).
* @note The USBD peripheral interrupt state is checked.
*/
bool nrfx_usbd_is_started(void);
/**
* @brief Suspend USBD operation.
*
* The USBD peripheral is forced to go into the low power mode.
* The function has to be called in the reaction to @ref NRFX_USBD_EVT_SUSPEND event
* when the firmware is ready.
*
* After successful call of this function most of the USBD registers would be unavailable.
*
* @note Check returned value for the feedback if suspending was successful.
*
* @retval true USBD peripheral successfully suspended.
* @retval false USBD peripheral was not suspended due to resume detection.
*/
bool nrfx_usbd_suspend(void);
/**
* @brief Start wake up procedure.
*
* The USBD peripheral is forced to quit the low power mode.
* After calling this function all the USBD registers would be available.
*
* The hardware starts measuring time when wake up is possible.
* This may take 0-5&nbsp;ms depending on how long the SUSPEND state was kept on the USB line.
* When NRFX_USBD_EVT_WUREQ event is generated it means that Wake Up signaling has just been
* started on the USB lines.
*
* @note Do not expect only @ref NRFX_USBD_EVT_WUREQ event.
* There always may appear @ref NRFX_USBD_EVT_RESUME event.
* @note NRFX_USBD_EVT_WUREQ event means that Remote WakeUp signal
* has just begun to be generated.
* This may take up to 20&nbsp;ms for the bus to become active.
*
* @retval true WakeUp procedure started.
* @retval false No WakeUp procedure started - bus is already active.
*/
bool nrfx_usbd_wakeup_req(void);
/**
* @brief Check if USBD is in SUSPEND mode.
*
* @note This is the information about peripheral itself, not about the bus state.
*
* @retval true USBD peripheral is suspended.
* @retval false USBD peripheral is active.
*/
bool nrfx_usbd_suspend_check(void);
/**
* @brief Enable only interrupts that should be processed in SUSPEND mode.
*
* Auxiliary function to help with SUSPEND mode integration.
* It enables only the interrupts that can be properly processed without stable HFCLK.
*
* Normally all the interrupts are enabled.
* Use this function to suspend interrupt processing that may require stable HFCLK until the
* clock is enabled.
*
* @sa nrfx_usbd_active_irq_config
*/
void nrfx_usbd_suspend_irq_config(void);
/**
* @brief Default active interrupt configuration.
*
* Default interrupt configuration.
* Use in a pair with @ref nrfx_usbd_active_irq_config.
*
* @sa nrfx_usbd_suspend_irq_config
*/
void nrfx_usbd_active_irq_config(void);
/**
* @brief Check the bus state.
*
* This function checks if the bus state is suspended.
*
* @note The value returned by this function changes on SUSPEND and RESUME event processing.
*
* @retval true USBD bus is suspended.
* @retval false USBD bus is active.
*/
bool nrfx_usbd_bus_suspend_check(void);
/**
* @brief Force the bus state to active
*/
void nrfx_usbd_force_bus_wakeup(void);
/**
* @brief Configure packet size that should be supported by the endpoint.
*
* The real endpoint buffer size is always the same.
* This value sets max packet size that would be transmitted over the endpoint.
* This is required by the driver.
*
* @param[in] ep Endpoint number.
* @param[in] size Required maximum packet size.
*
* @note Endpoint size is always set to @ref NRFX_USBD_EPSIZE or @ref NRFX_USBD_ISOSIZE / 2
* when @ref nrfx_usbd_ep_enable function is called.
*/
void nrfx_usbd_ep_max_packet_size_set(nrfx_usbd_ep_t ep, uint16_t size);
/**
* @brief Get configured endpoint packet size.
*
* Function to get configured endpoint size on the buffer.
*
* @param[in] ep Endpoint number.
*
* @return Maximum pocket size configured on selected endpoint.
*/
uint16_t nrfx_usbd_ep_max_packet_size_get(nrfx_usbd_ep_t ep);
/**
* @brief Check if the selected endpoint is enabled.
*
* @param[in] ep Endpoint number to check.
*
* @retval true Endpoint is enabled.
* @retval false Endpoint is disabled.
*/
bool nrfx_usbd_ep_enable_check(nrfx_usbd_ep_t ep);
/**
* @brief Enable selected endpoint.
*
* This function enables endpoint itself and its interrupts.
*
* @param[in] ep Endpoint number to enable.
*
* @note
* Max packet size is set to endpoint default maximum value.
*
* @sa nrfx_usbd_ep_max_packet_size_set
*/
void nrfx_usbd_ep_enable(nrfx_usbd_ep_t ep);
/**
* @brief Disable selected endpoint.
*
* This function disables endpoint itself and its interrupts.
*
* @param[in] ep Endpoint number to disable.
*/
void nrfx_usbd_ep_disable(nrfx_usbd_ep_t ep);
/**
* @brief Disable all endpoints except for EP0.
*
* Disable all endpoints that can be disabled in USB device while it is still active.
*/
void nrfx_usbd_ep_default_config(void);
/**
* @brief Start sending data over endpoint.
*
* Function initializes endpoint transmission.
* This is asynchronous function - it finishes immediately after configuration
* for transmission is prepared.
*
* @note Data buffer pointed by p_data have to be kept active till
* @ref NRFX_USBD_EVT_EPTRANSFER event is generated.
*
* @param[in] ep Endpoint number.
* For IN endpoint sending would be initiated.
* For OUT endpoint receiving would be initiated.
* @param[in] p_transfer Transfer parameters.
*
* @retval NRFX_SUCCESS Transfer queued or started.
* @retval NRFX_ERROR_BUSY Selected endpoint is pending.
* @retval NRFX_ERROR_INVALID_ADDR Unexpected transfer on EPIN0 or EPOUT0.
*/
nrfx_err_t nrfx_usbd_ep_transfer(nrfx_usbd_ep_t ep,
nrfx_usbd_transfer_t const * p_transfer);
/**
* @brief Start sending data over the endpoint using the transfer handler function.
*
* This function initializes an endpoint transmission.
* Just before data is transmitted, the transfer handler
* is called and it prepares a data chunk.
*
* @param[in] ep Endpoint number.
* For an IN endpoint, sending is initiated.
* For an OUT endpoint, receiving is initiated.
* @param[in] p_handler Transfer handler - feeder for IN direction and consumer for
* OUT direction.
*
* @retval NRFX_SUCCESS Transfer queued or started.
* @retval NRFX_ERROR_BUSY Selected endpoint is pending.
* @retval NRFX_ERROR_INVALID_ADDR Unexpected transfer on EPIN0 or EPOUT0.
*/
nrfx_err_t nrfx_usbd_ep_handled_transfer(nrfx_usbd_ep_t ep,
nrfx_usbd_handler_desc_t const * p_handler);
/**
* @brief Get the temporary buffer to be used by the feeder.
*
* This buffer is used for TX transfers and it can be reused automatically
* when the transfer is finished.
* Use it for transfer preparation.
*
* May be used inside the feeder configured in @ref nrfx_usbd_ep_handled_transfer.
*
* @return Pointer to the buffer that can be used temporarily.
*
* @sa NRFX_USBD_FEEDER_BUFFER_SIZE
*/
void * nrfx_usbd_feeder_buffer_get(void);
/**
* @brief Get the information about last finished or current transfer.
*
* Function returns the status of the last buffer set for transfer on selected endpoint.
* The status considers last buffer set by @ref nrfx_usbd_ep_transfer function or
* by transfer callback function.
*
* @param[in] ep Endpoint number.
* @param[out] p_size Information about the current/last transfer size.
*
* @return Endpoint status.
*
* @sa nrfx_usbd_ep_status_t
*/
nrfx_usbd_ep_status_t nrfx_usbd_ep_status_get(nrfx_usbd_ep_t ep, size_t * p_size);
/**
* @brief Get number of received bytes.
*
* Get the number of received bytes.
* The function behavior is undefined when called on IN endpoint.
*
* @param[in] ep Endpoint number.
*
* @return Number of received bytes.
*/
size_t nrfx_usbd_epout_size_get(nrfx_usbd_ep_t ep);
/**
* @brief Check if endpoint buffer is ready or is under USB IP control.
*
* Function to test if endpoint is busy.
* Endpoint that is busy cannot be accessed by MCU.
* It means that:
* - OUT (TX) endpoint: Last uploaded data is still in endpoint and is waiting
* to be received by the host.
* - IN (RX) endpoint: Endpoint is ready to receive data from the host
* and the endpoint does not have any data.
* When endpoint is not busy:
* - OUT (TX) endpoint: New data can be uploaded.
* - IN (RX) endpoint: New data can be downloaded using @ref nrfx_usbd_ep_transfer
* function.
*
* @param[in] ep Endpoint number.
*
* @retval false Endpoint is not busy.
* @retval true Endpoint is busy.
*/
bool nrfx_usbd_ep_is_busy(nrfx_usbd_ep_t ep);
/**
* @brief Stall endpoint
*
* Stall endpoit to send error information during next transfer request from
* the host.
*
* @note To stall endpoint it is safer to use @ref nrfx_usbd_setup_stall
* @note Stalled endpoint would not be cleared when DMA transfer finishes.
*
* @param[in] ep Endpoint number to stall.
*/
void nrfx_usbd_ep_stall(nrfx_usbd_ep_t ep);
/**
* @brief Clear stall flag on endpoint.
*
* This function clears endpoint that is stalled.
* @note
* If it is OUT endpoint (receiving) it would be also prepared for reception.
* It means that busy flag would be set.
* @note
* In endpoint (transmitting) would not be cleared - it gives possibility to
* write new data before transmitting.
*
* @param[in] ep Endpoint number.
*/
void nrfx_usbd_ep_stall_clear(nrfx_usbd_ep_t ep);
/**
* @brief Check if endpoint is stalled.
*
* This function gets stall state of selected endpoint.
*
* @param[in] ep Endpoint number to check.
*
* @retval false Endpoint is not stalled.
* @retval true Endpoint is stalled.
*/
bool nrfx_usbd_ep_stall_check(nrfx_usbd_ep_t ep);
/**
* @brief Clear current endpoint data toggle.
*
* @param[in] ep Endpoint number to clear.
*/
void nrfx_usbd_ep_dtoggle_clear(nrfx_usbd_ep_t ep);
/**
* @brief Get parsed setup data.
*
* Function fills the parsed setup data structure.
*
* @param[out] p_setup Pointer to data structure that would be filled by
* parsed data.
*/
void nrfx_usbd_setup_get(nrfx_usbd_setup_t * p_setup);
/**
* @brief Clear the control endpoint for packet reception during DATA stage.
*
* This function may be called if any more data in control write transfer is expected.
* Clears only OUT endpoint to be able to take another OUT data token.
* It does not allow STATUS stage.
* @sa nrfx_usbd_setup_clear
*/
void nrfx_usbd_setup_data_clear(void);
/**
* @brief Clear setup endpoint.
*
* This function acknowledges setup when SETUP command was received and processed.
* It has to be called if no data respond for the SETUP command is sent.
*/
void nrfx_usbd_setup_clear(void);
/**
* @brief Stall setup endpoint.
*
* Mark an error on setup endpoint.
*/
void nrfx_usbd_setup_stall(void);
/**
* @brief Abort pending transfer on selected endpoint.
*
* @param[in] ep Endpoint number.
*/
void nrfx_usbd_ep_abort(nrfx_usbd_ep_t ep);
/**
* @brief Get the information about expected transfer SETUP data direction.
*
* Function returns the information about last expected transfer direction.
*
* @retval NRFX_USBD_EPOUT0 Expecting OUT (Host->Device) direction or no data.
* @retval NRFX_USBD_EPIN0 Expecting IN (Device->Host) direction.
*/
nrfx_usbd_ep_t nrfx_usbd_last_setup_dir_get(void);
/**
* @brief Drop transfer on OUT endpoint.
*
* @param[in] ep OUT endpoint ID.
*/
void nrfx_usbd_transfer_out_drop(nrfx_usbd_ep_t ep);
/** @} */
void nrfx_usbd_irq_handler(void);
#ifdef __cplusplus
}
#endif
#endif // NRFX_USBD_H__

View File

@@ -0,0 +1,168 @@
/**
* Copyright (c) 2014 - 2020, 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

View File

@@ -0,0 +1,342 @@
/**
* Copyright (c) 2017 - 2020, 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__

View File

@@ -0,0 +1,85 @@
/**
* Copyright (c) 2017 - 2020, 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__

View File

@@ -0,0 +1,340 @@
/**
* Copyright (c) 2015 - 2020, 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_ADC_ENABLED)
#include <nrfx_adc.h>
#define NRFX_LOG_MODULE ADC
#include <nrfx_log.h>
#define EVT_TO_STR(event) (event == NRF_ADC_EVENT_END ? "NRF_ADC_EVENT_END" : "UNKNOWN EVENT")
typedef struct
{
nrfx_adc_event_handler_t event_handler;
nrfx_adc_channel_t * p_head;
nrfx_adc_channel_t * p_current_conv;
nrf_adc_value_t * p_buffer;
uint16_t size;
uint16_t idx;
nrfx_drv_state_t state;
} adc_cb_t;
static adc_cb_t m_cb;
nrfx_err_t nrfx_adc_init(nrfx_adc_config_t const * p_config,
nrfx_adc_event_handler_t event_handler)
{
NRFX_ASSERT(p_config);
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;
}
nrf_adc_event_clear(NRF_ADC_EVENT_END);
if (event_handler)
{
NRFX_IRQ_PRIORITY_SET(ADC_IRQn, p_config->interrupt_priority);
NRFX_IRQ_ENABLE(ADC_IRQn);
}
m_cb.event_handler = event_handler;
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;
}
void nrfx_adc_uninit(void)
{
NRFX_IRQ_DISABLE(ADC_IRQn);
nrf_adc_int_disable(NRF_ADC_INT_END_MASK);
nrf_adc_task_trigger(NRF_ADC_TASK_STOP);
// Disable all channels. This must be done after the interrupt is disabled
// because adc_sample_process() dereferences this pointer when it needs to
// switch back to the first channel in the list (when the number of samples
// to read is bigger than the number of enabled channels).
m_cb.p_head = NULL;
m_cb.state = NRFX_DRV_STATE_UNINITIALIZED;
}
void nrfx_adc_channel_enable(nrfx_adc_channel_t * const p_channel)
{
NRFX_ASSERT(!nrfx_adc_is_busy());
p_channel->p_next = NULL;
if (m_cb.p_head == NULL)
{
m_cb.p_head = p_channel;
}
else
{
nrfx_adc_channel_t * p_curr_channel = m_cb.p_head;
while (p_curr_channel->p_next != NULL)
{
NRFX_ASSERT(p_channel != p_curr_channel);
p_curr_channel = p_curr_channel->p_next;
}
p_curr_channel->p_next = p_channel;
}
NRFX_LOG_INFO("Enabled.");
}
void nrfx_adc_channel_disable(nrfx_adc_channel_t * const p_channel)
{
NRFX_ASSERT(m_cb.p_head);
NRFX_ASSERT(!nrfx_adc_is_busy());
nrfx_adc_channel_t * p_curr_channel = m_cb.p_head;
nrfx_adc_channel_t * p_prev_channel = NULL;
while (p_curr_channel != p_channel)
{
p_prev_channel = p_curr_channel;
p_curr_channel = p_curr_channel->p_next;
NRFX_ASSERT(p_curr_channel != NULL);
}
if (p_prev_channel)
{
p_prev_channel->p_next = p_curr_channel->p_next;
}
else
{
m_cb.p_head = p_curr_channel->p_next;
}
NRFX_LOG_INFO("Disabled.");
}
void nrfx_adc_all_channels_disable(void)
{
NRFX_ASSERT(!nrfx_adc_is_busy());
m_cb.p_head = NULL;
}
void nrfx_adc_sample(void)
{
NRFX_ASSERT(m_cb.state != NRFX_DRV_STATE_UNINITIALIZED);
NRFX_ASSERT(!nrf_adc_busy_check());
nrf_adc_task_trigger(NRF_ADC_TASK_START);
}
nrfx_err_t nrfx_adc_sample_convert(nrfx_adc_channel_t const * const p_channel,
nrf_adc_value_t * p_value)
{
nrfx_err_t err_code;
NRFX_ASSERT(m_cb.state != NRFX_DRV_STATE_UNINITIALIZED);
if (m_cb.state == NRFX_DRV_STATE_POWERED_ON)
{
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
{
m_cb.state = NRFX_DRV_STATE_POWERED_ON;
nrf_adc_init(&p_channel->config);
nrf_adc_enable();
nrf_adc_int_disable(NRF_ADC_INT_END_MASK);
nrf_adc_task_trigger(NRF_ADC_TASK_START);
if (p_value)
{
while (!nrf_adc_event_check(NRF_ADC_EVENT_END)) {}
nrf_adc_event_clear(NRF_ADC_EVENT_END);
*p_value = (nrf_adc_value_t)nrf_adc_result_get();
nrf_adc_disable();
m_cb.state = NRFX_DRV_STATE_INITIALIZED;
}
else
{
NRFX_ASSERT(m_cb.event_handler);
m_cb.p_buffer = NULL;
nrf_adc_int_enable(NRF_ADC_INT_END_MASK);
}
err_code = NRFX_SUCCESS;
NRFX_LOG_INFO("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
}
static bool adc_sample_process()
{
nrf_adc_event_clear(NRF_ADC_EVENT_END);
nrf_adc_disable();
m_cb.p_buffer[m_cb.idx] = (nrf_adc_value_t)nrf_adc_result_get();
m_cb.idx++;
if (m_cb.idx < m_cb.size)
{
bool task_trigger = false;
if (m_cb.p_current_conv->p_next == NULL)
{
// Make sure the list of channels has not been somehow removed
// (it is when all channels are disabled).
NRFX_ASSERT(m_cb.p_head);
m_cb.p_current_conv = m_cb.p_head;
}
else
{
m_cb.p_current_conv = m_cb.p_current_conv->p_next;
task_trigger = true;
}
nrf_adc_init(&m_cb.p_current_conv->config);
nrf_adc_enable();
if (task_trigger)
{
nrf_adc_task_trigger(NRF_ADC_TASK_START);
}
return false;
}
else
{
return true;
}
}
nrfx_err_t nrfx_adc_buffer_convert(nrf_adc_value_t * buffer, uint16_t size)
{
NRFX_ASSERT(m_cb.state != NRFX_DRV_STATE_UNINITIALIZED);
nrfx_err_t err_code;
NRFX_LOG_INFO("Number of samples requested to convert: %d.", size);
if (m_cb.state == NRFX_DRV_STATE_POWERED_ON)
{
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
{
m_cb.state = NRFX_DRV_STATE_POWERED_ON;
m_cb.p_current_conv = m_cb.p_head;
m_cb.size = size;
m_cb.idx = 0;
m_cb.p_buffer = buffer;
nrf_adc_init(&m_cb.p_current_conv->config);
nrf_adc_event_clear(NRF_ADC_EVENT_END);
nrf_adc_enable();
if (m_cb.event_handler)
{
nrf_adc_int_enable(NRF_ADC_INT_END_MASK);
}
else
{
while (1)
{
while (!nrf_adc_event_check(NRF_ADC_EVENT_END)){}
if (adc_sample_process())
{
m_cb.state = NRFX_DRV_STATE_INITIALIZED;
break;
}
}
}
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_adc_is_busy(void)
{
NRFX_ASSERT(m_cb.state != NRFX_DRV_STATE_UNINITIALIZED);
return (m_cb.state == NRFX_DRV_STATE_POWERED_ON) ? true : false;
}
void nrfx_adc_irq_handler(void)
{
if (m_cb.p_buffer == NULL)
{
nrf_adc_event_clear(NRF_ADC_EVENT_END);
NRFX_LOG_DEBUG("Event: %s.",NRFX_LOG_ERROR_STRING_GET(NRF_ADC_EVENT_END));
nrf_adc_int_disable(NRF_ADC_INT_END_MASK);
nrf_adc_disable();
nrfx_adc_evt_t evt;
evt.type = NRFX_ADC_EVT_SAMPLE;
evt.data.sample.sample = (nrf_adc_value_t)nrf_adc_result_get();
NRFX_LOG_DEBUG("ADC data:");
NRFX_LOG_HEXDUMP_DEBUG((uint8_t *)(&evt.data.sample.sample), sizeof(nrf_adc_value_t));
m_cb.state = NRFX_DRV_STATE_INITIALIZED;
m_cb.event_handler(&evt);
}
else if (adc_sample_process())
{
NRFX_LOG_DEBUG("Event: %s.", NRFX_LOG_ERROR_STRING_GET(NRF_ADC_EVENT_END));
nrf_adc_int_disable(NRF_ADC_INT_END_MASK);
nrfx_adc_evt_t evt;
evt.type = NRFX_ADC_EVT_DONE;
evt.data.done.p_buffer = m_cb.p_buffer;
evt.data.done.size = m_cb.size;
m_cb.state = NRFX_DRV_STATE_INITIALIZED;
NRFX_LOG_DEBUG("ADC data:");
NRFX_LOG_HEXDUMP_DEBUG((uint8_t *)m_cb.p_buffer, m_cb.size * sizeof(nrf_adc_value_t));
m_cb.event_handler(&evt);
}
}
#endif // NRFX_CHECK(NRFX_ADC_ENABLED)

View File

@@ -0,0 +1,395 @@
/**
* Copyright (c) 2016 - 2020, 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)

View File

@@ -0,0 +1,211 @@
/**
* Copyright (c) 2015 - 2020, 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_COMP_ENABLED)
#include <nrfx_comp.h>
#include "prs/nrfx_prs.h"
#define NRFX_LOG_MODULE COMP
#include <nrfx_log.h>
#define EVT_TO_STR(event) \
(event == NRF_COMP_EVENT_READY ? "NRF_COMP_EVENT_READY" : \
(event == NRF_COMP_EVENT_DOWN ? "NRF_COMP_EVENT_DOWN" : \
(event == NRF_COMP_EVENT_UP ? "NRF_COMP_EVENT_UP" : \
(event == NRF_COMP_EVENT_CROSS ? "NRF_COMP_EVENT_CROSS" : \
"UNKNOWN ERROR"))))
static nrfx_comp_event_handler_t m_comp_event_handler = NULL;
static nrfx_drv_state_t m_state = NRFX_DRV_STATE_UNINITIALIZED;
static void comp_execute_handler(nrf_comp_event_t event, uint32_t event_mask)
{
if (nrf_comp_event_check(event) && nrf_comp_int_enable_check(event_mask))
{
nrf_comp_event_clear(event);
NRFX_LOG_DEBUG("Event: %s.", EVT_TO_STR(event));
m_comp_event_handler(event);
}
}
void nrfx_comp_irq_handler(void)
{
comp_execute_handler(NRF_COMP_EVENT_READY, COMP_INTENSET_READY_Msk);
comp_execute_handler(NRF_COMP_EVENT_DOWN, COMP_INTENSET_DOWN_Msk);
comp_execute_handler(NRF_COMP_EVENT_UP, COMP_INTENSET_UP_Msk);
comp_execute_handler(NRF_COMP_EVENT_CROSS, COMP_INTENSET_CROSS_Msk);
}
nrfx_err_t nrfx_comp_init(nrfx_comp_config_t const * p_config,
nrfx_comp_event_handler_t event_handler)
{
NRFX_ASSERT(p_config);
NRFX_ASSERT(event_handler);
nrfx_err_t err_code;
if (m_state != NRFX_DRV_STATE_UNINITIALIZED)
{ // COMP driver is already initialized
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;
}
m_comp_event_handler = event_handler;
#if NRFX_CHECK(NRFX_PRS_ENABLED)
if (nrfx_prs_acquire(NRF_COMP, nrfx_comp_irq_handler) != 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
nrf_comp_task_trigger(NRF_COMP_TASK_STOP);
nrf_comp_enable();
// Clear events to be sure there are no leftovers.
nrf_comp_event_clear(NRF_COMP_EVENT_READY);
nrf_comp_event_clear(NRF_COMP_EVENT_DOWN);
nrf_comp_event_clear(NRF_COMP_EVENT_UP);
nrf_comp_event_clear(NRF_COMP_EVENT_CROSS);
nrf_comp_ref_set(p_config->reference);
//If external source is chosen, write to appropriate register.
if (p_config->reference == COMP_REFSEL_REFSEL_ARef)
{
nrf_comp_ext_ref_set(p_config->ext_ref);
}
nrf_comp_th_set(p_config->threshold);
nrf_comp_main_mode_set(p_config->main_mode);
nrf_comp_speed_mode_set(p_config->speed_mode);
nrf_comp_hysteresis_set(p_config->hyst);
#if defined (COMP_ISOURCE_ISOURCE_Msk)
nrf_comp_isource_set(p_config->isource);
#endif
nrf_comp_shorts_disable(NRFX_COMP_SHORT_STOP_AFTER_CROSS_EVT |
NRFX_COMP_SHORT_STOP_AFTER_UP_EVT |
NRFX_COMP_SHORT_STOP_AFTER_DOWN_EVT);
nrf_comp_int_disable(COMP_INTENCLR_CROSS_Msk |
COMP_INTENCLR_UP_Msk |
COMP_INTENCLR_DOWN_Msk |
COMP_INTENCLR_READY_Msk);
nrf_comp_input_select(p_config->input);
NRFX_IRQ_PRIORITY_SET(nrfx_get_irq_number(NRF_COMP), p_config->interrupt_priority);
NRFX_IRQ_ENABLE(nrfx_get_irq_number(NRF_COMP));
m_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_comp_uninit(void)
{
NRFX_ASSERT(m_state != NRFX_DRV_STATE_UNINITIALIZED);
NRFX_IRQ_DISABLE(nrfx_get_irq_number(NRF_COMP));
nrf_comp_disable();
#if NRFX_CHECK(NRFX_PRS_ENABLED)
nrfx_prs_release(NRF_COMP);
#endif
m_state = NRFX_DRV_STATE_UNINITIALIZED;
m_comp_event_handler = NULL;
NRFX_LOG_INFO("Uninitialized.");
}
void nrfx_comp_pin_select(nrf_comp_input_t psel)
{
bool comp_enable_state = nrf_comp_enable_check();
nrf_comp_task_trigger(NRF_COMP_TASK_STOP);
if (m_state == NRFX_DRV_STATE_POWERED_ON)
{
m_state = NRFX_DRV_STATE_INITIALIZED;
}
nrf_comp_disable();
nrf_comp_input_select(psel);
if (comp_enable_state == true)
{
nrf_comp_enable();
}
}
void nrfx_comp_start(uint32_t comp_int_mask, uint32_t comp_shorts_mask)
{
NRFX_ASSERT(m_state == NRFX_DRV_STATE_INITIALIZED);
nrf_comp_int_enable(comp_int_mask);
nrf_comp_shorts_enable(comp_shorts_mask);
nrf_comp_task_trigger(NRF_COMP_TASK_START);
m_state = NRFX_DRV_STATE_POWERED_ON;
NRFX_LOG_INFO("Enabled.");
}
void nrfx_comp_stop(void)
{
NRFX_ASSERT(m_state == NRFX_DRV_STATE_POWERED_ON);
nrf_comp_shorts_disable(UINT32_MAX);
nrf_comp_int_disable(UINT32_MAX);
nrf_comp_task_trigger(NRF_COMP_TASK_STOP);
m_state = NRFX_DRV_STATE_INITIALIZED;
NRFX_LOG_INFO("Disabled.");
}
uint32_t nrfx_comp_sample()
{
NRFX_ASSERT(m_state == NRFX_DRV_STATE_POWERED_ON);
nrf_comp_task_trigger(NRF_COMP_TASK_SAMPLE);
return nrf_comp_result_get();
}
#endif // NRFX_CHECK(NRFX_COMP_ENABLED)

View File

@@ -0,0 +1,319 @@
/**
* Copyright (c) 2018 - 2020, 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_DPPI_ENABLED)
#include <nrfx_dppi.h>
#define NRFX_LOG_MODULE DPPI
#include <nrfx_log.h>
#if !defined(NRFX_DPPI_CHANNELS_USED)
// Default mask of DPPI channels reserved for other modules.
#define NRFX_DPPI_CHANNELS_USED 0x00000000uL
#endif
#if !defined(NRFX_DPPI_GROUPS_USED)
// Default mask of DPPI groups reserved for other modules.
#define NRFX_DPPI_GROUPS_USED 0x00000000uL
#endif
#define DPPI_AVAILABLE_CHANNELS_MASK \
(((1UL << DPPI_CH_NUM) - 1) & (~NRFX_DPPI_CHANNELS_USED))
#define DPPI_AVAILABLE_GROUPS_MASK \
(((1UL << DPPI_GROUP_NUM) - 1) & (~NRFX_DPPI_GROUPS_USED))
/** @brief Set bit at given position. */
#define DPPI_BIT_SET(pos) (1uL << (pos))
static uint32_t m_allocated_channels;
static uint8_t m_allocated_groups;
__STATIC_INLINE bool channel_is_allocated(uint8_t channel)
{
return ((m_allocated_channels & DPPI_BIT_SET(channel)) != 0);
}
__STATIC_INLINE bool group_is_allocated(nrf_dppi_channel_group_t group)
{
return ((m_allocated_groups & DPPI_BIT_SET(group)) != 0);
}
void nrfx_dppi_free(void)
{
uint32_t mask = m_allocated_groups;
nrf_dppi_channel_group_t group = NRF_DPPI_CHANNEL_GROUP0;
// Disable all channels
nrf_dppi_channels_disable(NRF_DPPIC, m_allocated_channels);
// Clear all groups configurations
while (mask)
{
if (mask & DPPI_BIT_SET(group))
{
nrf_dppi_group_clear(NRF_DPPIC, group);
mask &= ~DPPI_BIT_SET(group);
}
group++;
}
// Clear all allocated channels.
m_allocated_channels = 0;
// Clear all allocated groups.
m_allocated_groups = 0;
}
nrfx_err_t nrfx_dppi_channel_alloc(uint8_t * p_channel)
{
nrfx_err_t err_code;
// Get mask of available DPPI channels
uint32_t remaining_channels = DPPI_AVAILABLE_CHANNELS_MASK & ~(m_allocated_channels);
uint8_t channel = 0;
if (!remaining_channels)
{
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;
}
// Find first free channel
while (!(remaining_channels & DPPI_BIT_SET(channel)))
{
channel++;
}
m_allocated_channels |= DPPI_BIT_SET(channel);
*p_channel = channel;
err_code = NRFX_SUCCESS;
NRFX_LOG_INFO("Allocated channel: %d.", channel);
return err_code;
}
nrfx_err_t nrfx_dppi_channel_free(uint8_t channel)
{
nrfx_err_t err_code = NRFX_SUCCESS;
if (!channel_is_allocated(channel))
{
err_code = NRFX_ERROR_INVALID_PARAM;
}
else
{
// First disable this channel
nrf_dppi_channels_disable(NRF_DPPIC, DPPI_BIT_SET(channel));
// Clear channel allocated indication.
m_allocated_channels &= ~DPPI_BIT_SET(channel);
}
NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
nrfx_err_t nrfx_dppi_channel_enable(uint8_t channel)
{
nrfx_err_t err_code = NRFX_SUCCESS;
if (!channel_is_allocated(channel))
{
err_code = NRFX_ERROR_INVALID_PARAM;
}
else
{
nrf_dppi_channels_enable(NRF_DPPIC, DPPI_BIT_SET(channel));
}
NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
nrfx_err_t nrfx_dppi_channel_disable(uint8_t channel)
{
nrfx_err_t err_code = NRFX_SUCCESS;
if (!channel_is_allocated(channel))
{
err_code = NRFX_ERROR_INVALID_PARAM;
}
else
{
nrf_dppi_channels_disable(NRF_DPPIC, DPPI_BIT_SET(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_dppi_group_alloc(nrf_dppi_channel_group_t * p_group)
{
nrfx_err_t err_code;
// Get mask of available DPPI groups
uint32_t remaining_groups = DPPI_AVAILABLE_GROUPS_MASK & ~(m_allocated_groups);
nrf_dppi_channel_group_t group = NRF_DPPI_CHANNEL_GROUP0;
if (!remaining_groups)
{
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;
}
// Find first free group
while (!(remaining_groups & DPPI_BIT_SET(group)))
{
group++;
}
m_allocated_groups |= DPPI_BIT_SET(group);
*p_group = group;
err_code = NRFX_SUCCESS;
NRFX_LOG_INFO("Allocated channel: %d.", group);
return err_code;
}
nrfx_err_t nrfx_dppi_group_free(nrf_dppi_channel_group_t group)
{
nrfx_err_t err_code = NRFX_SUCCESS;
if (!group_is_allocated(group))
{
err_code = NRFX_ERROR_INVALID_PARAM;
}
else
{
nrf_dppi_group_disable(NRF_DPPIC, group);
// Set bit value to zero at position corresponding to the group number.
m_allocated_groups &= ~DPPI_BIT_SET(group);
}
NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
nrfx_err_t nrfx_dppi_channel_include_in_group(uint8_t channel,
nrf_dppi_channel_group_t group)
{
nrfx_err_t err_code = NRFX_SUCCESS;
if (!group_is_allocated(group) || !channel_is_allocated(channel))
{
err_code = NRFX_ERROR_INVALID_PARAM;
}
else
{
nrf_dppi_channels_include_in_group(NRF_DPPIC, DPPI_BIT_SET(channel), group);
}
NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
nrfx_err_t nrfx_dppi_channel_remove_from_group(uint8_t channel,
nrf_dppi_channel_group_t group)
{
nrfx_err_t err_code = NRFX_SUCCESS;
if (!group_is_allocated(group) || !channel_is_allocated(channel))
{
err_code = NRFX_ERROR_INVALID_PARAM;
}
else
{
nrf_dppi_channels_remove_from_group(NRF_DPPIC, DPPI_BIT_SET(channel), group);
}
NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
nrfx_err_t nrfx_dppi_group_clear(nrf_dppi_channel_group_t group)
{
nrfx_err_t err_code = NRFX_SUCCESS;
if (!group_is_allocated(group))
{
err_code = NRFX_ERROR_INVALID_PARAM;
}
else
{
nrf_dppi_channels_remove_from_group(NRF_DPPIC, DPPI_AVAILABLE_CHANNELS_MASK, group);
}
NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
nrfx_err_t nrfx_dppi_group_enable(nrf_dppi_channel_group_t group)
{
nrfx_err_t err_code = NRFX_SUCCESS;
if (!group_is_allocated(group))
{
err_code = NRFX_ERROR_INVALID_PARAM;
}
else
{
nrf_dppi_group_enable(NRF_DPPIC, group);
}
NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
nrfx_err_t nrfx_dppi_group_disable(nrf_dppi_channel_group_t group)
{
nrfx_err_t err_code = NRFX_SUCCESS;
if (!group_is_allocated(group))
{
err_code = NRFX_ERROR_INVALID_PARAM;
}
else
{
nrf_dppi_group_disable(NRF_DPPIC, group);
}
NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
#endif // NRFX_CHECK(NRFX_DPPI_ENABLED)

View File

@@ -0,0 +1,934 @@
/**
* Copyright (c) 2015 - 2020, 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);
}
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)

View File

@@ -0,0 +1,445 @@
/**
* Copyright (c) 2015 - 2020, 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_I2S_ENABLED)
#include <nrfx_i2s.h>
#include <hal/nrf_gpio.h>
#define NRFX_LOG_MODULE I2S
#include <nrfx_log.h>
#define EVT_TO_STR(event) \
(event == NRF_I2S_EVENT_RXPTRUPD ? "NRF_I2S_EVENT_RXPTRUPD" : \
(event == NRF_I2S_EVENT_TXPTRUPD ? "NRF_I2S_EVENT_TXPTRUPD" : \
(event == NRF_I2S_EVENT_STOPPED ? "NRF_I2S_EVENT_STOPPED" : \
"UNKNOWN EVENT")))
#if !defined(USE_WORKAROUND_FOR_I2S_STOP_ANOMALY) && \
(defined(NRF52832_XXAA) || defined(NRF52832_XXAB) || defined(NRF52840_XXAA) || \
defined(NRF9160_XXAA))
// Enable workaround for nRF52832 and nRF52840 anomaly 194 / nrf9160 anomaly 1
// (STOP task does not switch off all resources).
#define USE_WORKAROUND_FOR_I2S_STOP_ANOMALY 1
#endif
// Control block - driver instance local data.
typedef struct
{
nrfx_i2s_data_handler_t handler;
nrfx_drv_state_t state;
bool use_rx : 1;
bool use_tx : 1;
bool rx_ready : 1;
bool tx_ready : 1;
bool buffers_needed : 1;
bool buffers_reused : 1;
uint16_t buffer_size;
nrfx_i2s_buffers_t next_buffers;
nrfx_i2s_buffers_t current_buffers;
} i2s_control_block_t;
static i2s_control_block_t m_cb;
static void configure_pins(nrfx_i2s_config_t const * p_config)
{
uint32_t mck_pin, sdout_pin, sdin_pin;
// Configure pins used by the peripheral:
// - SCK and LRCK (required) - depending on the mode of operation these
// pins are configured as outputs (in Master mode) or inputs (in Slave
// mode).
if (p_config->mode == NRF_I2S_MODE_MASTER)
{
nrf_gpio_cfg_output(p_config->sck_pin);
nrf_gpio_cfg_output(p_config->lrck_pin);
}
else
{
nrf_gpio_cfg_input(p_config->sck_pin, NRF_GPIO_PIN_NOPULL);
nrf_gpio_cfg_input(p_config->lrck_pin, NRF_GPIO_PIN_NOPULL);
}
// - MCK (optional) - always output,
if (p_config->mck_pin != NRFX_I2S_PIN_NOT_USED)
{
mck_pin = p_config->mck_pin;
nrf_gpio_cfg_output(mck_pin);
}
else
{
mck_pin = NRF_I2S_PIN_NOT_CONNECTED;
}
// - SDOUT (optional) - always output,
if (p_config->sdout_pin != NRFX_I2S_PIN_NOT_USED)
{
sdout_pin = p_config->sdout_pin;
nrf_gpio_cfg_output(sdout_pin);
}
else
{
sdout_pin = NRF_I2S_PIN_NOT_CONNECTED;
}
// - SDIN (optional) - always input.
if (p_config->sdin_pin != NRFX_I2S_PIN_NOT_USED)
{
sdin_pin = p_config->sdin_pin;
nrf_gpio_cfg_input(sdin_pin, NRF_GPIO_PIN_NOPULL);
}
else
{
sdin_pin = NRF_I2S_PIN_NOT_CONNECTED;
}
nrf_i2s_pins_set(NRF_I2S,
p_config->sck_pin,
p_config->lrck_pin,
mck_pin,
sdout_pin,
sdin_pin);
}
nrfx_err_t nrfx_i2s_init(nrfx_i2s_config_t const * p_config,
nrfx_i2s_data_handler_t handler)
{
NRFX_ASSERT(p_config);
NRFX_ASSERT(handler);
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;
}
if (!nrf_i2s_configure(NRF_I2S,
p_config->mode,
p_config->format,
p_config->alignment,
p_config->sample_width,
p_config->channels,
p_config->mck_setup,
p_config->ratio))
{
err_code = NRFX_ERROR_INVALID_PARAM;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
configure_pins(p_config);
m_cb.handler = handler;
NRFX_IRQ_PRIORITY_SET(I2S_IRQn, p_config->irq_priority);
NRFX_IRQ_ENABLE(I2S_IRQn);
m_cb.state = NRFX_DRV_STATE_INITIALIZED;
NRFX_LOG_INFO("Initialized.");
return NRFX_SUCCESS;
}
void nrfx_i2s_uninit(void)
{
NRFX_ASSERT(m_cb.state != NRFX_DRV_STATE_UNINITIALIZED);
nrfx_i2s_stop();
NRFX_IRQ_DISABLE(I2S_IRQn);
nrf_i2s_pins_set(NRF_I2S,
NRF_I2S_PIN_NOT_CONNECTED,
NRF_I2S_PIN_NOT_CONNECTED,
NRF_I2S_PIN_NOT_CONNECTED,
NRF_I2S_PIN_NOT_CONNECTED,
NRF_I2S_PIN_NOT_CONNECTED);
m_cb.state = NRFX_DRV_STATE_UNINITIALIZED;
NRFX_LOG_INFO("Uninitialized.");
}
nrfx_err_t nrfx_i2s_start(nrfx_i2s_buffers_t const * p_initial_buffers,
uint16_t buffer_size,
uint8_t flags)
{
NRFX_ASSERT(p_initial_buffers != NULL);
NRFX_ASSERT(p_initial_buffers->p_rx_buffer != NULL ||
p_initial_buffers->p_tx_buffer != NULL);
NRFX_ASSERT((p_initial_buffers->p_rx_buffer == NULL) ||
(nrfx_is_in_ram(p_initial_buffers->p_rx_buffer) &&
nrfx_is_word_aligned(p_initial_buffers->p_rx_buffer)));
NRFX_ASSERT((p_initial_buffers->p_tx_buffer == NULL) ||
(nrfx_is_in_ram(p_initial_buffers->p_tx_buffer) &&
nrfx_is_word_aligned(p_initial_buffers->p_tx_buffer)));
NRFX_ASSERT(buffer_size != 0);
(void)(flags);
nrfx_err_t err_code;
if (m_cb.state != NRFX_DRV_STATE_INITIALIZED)
{
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 (((p_initial_buffers->p_rx_buffer != NULL)
&& !nrfx_is_in_ram(p_initial_buffers->p_rx_buffer))
||
((p_initial_buffers->p_tx_buffer != NULL)
&& !nrfx_is_in_ram(p_initial_buffers->p_tx_buffer)))
{
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;
}
m_cb.use_rx = (p_initial_buffers->p_rx_buffer != NULL);
m_cb.use_tx = (p_initial_buffers->p_tx_buffer != NULL);
m_cb.rx_ready = false;
m_cb.tx_ready = false;
m_cb.buffers_needed = false;
m_cb.buffer_size = buffer_size;
// Set the provided initial buffers as next, they will become the current
// ones after the IRQ handler is called for the first time, what will occur
// right after the START task is triggered.
m_cb.next_buffers = *p_initial_buffers;
m_cb.current_buffers.p_rx_buffer = NULL;
m_cb.current_buffers.p_tx_buffer = NULL;
nrf_i2s_transfer_set(NRF_I2S,
m_cb.buffer_size,
m_cb.next_buffers.p_rx_buffer,
m_cb.next_buffers.p_tx_buffer);
nrf_i2s_enable(NRF_I2S);
m_cb.state = NRFX_DRV_STATE_POWERED_ON;
nrf_i2s_event_clear(NRF_I2S, NRF_I2S_EVENT_RXPTRUPD);
nrf_i2s_event_clear(NRF_I2S, NRF_I2S_EVENT_TXPTRUPD);
nrf_i2s_event_clear(NRF_I2S, NRF_I2S_EVENT_STOPPED);
nrf_i2s_int_enable(NRF_I2S, (m_cb.use_rx ? NRF_I2S_INT_RXPTRUPD_MASK : 0) |
(m_cb.use_tx ? NRF_I2S_INT_TXPTRUPD_MASK : 0) |
NRF_I2S_INT_STOPPED_MASK);
nrf_i2s_task_trigger(NRF_I2S, NRF_I2S_TASK_START);
NRFX_LOG_INFO("Started.");
return NRFX_SUCCESS;
}
nrfx_err_t nrfx_i2s_next_buffers_set(nrfx_i2s_buffers_t const * p_buffers)
{
NRFX_ASSERT(m_cb.state == NRFX_DRV_STATE_POWERED_ON);
NRFX_ASSERT(p_buffers);
NRFX_ASSERT((p_buffers->p_rx_buffer == NULL) ||
(nrfx_is_in_ram(p_buffers->p_rx_buffer) &&
nrfx_is_word_aligned(p_buffers->p_rx_buffer)));
NRFX_ASSERT((p_buffers->p_tx_buffer == NULL) ||
(nrfx_is_in_ram(p_buffers->p_tx_buffer) &&
nrfx_is_word_aligned(p_buffers->p_tx_buffer)));
nrfx_err_t err_code;
if (!m_cb.buffers_needed)
{
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 (((p_buffers->p_rx_buffer != NULL)
&& !nrfx_is_in_ram(p_buffers->p_rx_buffer))
||
((p_buffers->p_tx_buffer != NULL)
&& !nrfx_is_in_ram(p_buffers->p_tx_buffer)))
{
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 (m_cb.use_tx)
{
NRFX_ASSERT(p_buffers->p_tx_buffer != NULL);
nrf_i2s_tx_buffer_set(NRF_I2S, p_buffers->p_tx_buffer);
}
if (m_cb.use_rx)
{
NRFX_ASSERT(p_buffers->p_rx_buffer != NULL);
nrf_i2s_rx_buffer_set(NRF_I2S, p_buffers->p_rx_buffer);
}
m_cb.next_buffers = *p_buffers;
m_cb.buffers_needed = false;
return NRFX_SUCCESS;
}
void nrfx_i2s_stop(void)
{
NRFX_ASSERT(m_cb.state != NRFX_DRV_STATE_UNINITIALIZED);
m_cb.buffers_needed = false;
// First disable interrupts, then trigger the STOP task, so no spurious
// RXPTRUPD and TXPTRUPD events (see nRF52 anomaly 55) are processed.
nrf_i2s_int_disable(NRF_I2S, NRF_I2S_INT_RXPTRUPD_MASK |
NRF_I2S_INT_TXPTRUPD_MASK);
nrf_i2s_task_trigger(NRF_I2S, NRF_I2S_TASK_STOP);
#if NRFX_CHECK(USE_WORKAROUND_FOR_I2S_STOP_ANOMALY)
*((volatile uint32_t *)(((uint32_t)NRF_I2S) + 0x38)) = 1;
*((volatile uint32_t *)(((uint32_t)NRF_I2S) + 0x3C)) = 1;
#endif
}
void nrfx_i2s_irq_handler(void)
{
if (nrf_i2s_event_check(NRF_I2S, NRF_I2S_EVENT_TXPTRUPD))
{
nrf_i2s_event_clear(NRF_I2S, NRF_I2S_EVENT_TXPTRUPD);
m_cb.tx_ready = true;
if (m_cb.use_tx && m_cb.buffers_needed)
{
m_cb.buffers_reused = true;
}
}
if (nrf_i2s_event_check(NRF_I2S, NRF_I2S_EVENT_RXPTRUPD))
{
nrf_i2s_event_clear(NRF_I2S, NRF_I2S_EVENT_RXPTRUPD);
m_cb.rx_ready = true;
if (m_cb.use_rx && m_cb.buffers_needed)
{
m_cb.buffers_reused = true;
}
}
if (nrf_i2s_event_check(NRF_I2S, NRF_I2S_EVENT_STOPPED))
{
nrf_i2s_event_clear(NRF_I2S, NRF_I2S_EVENT_STOPPED);
nrf_i2s_int_disable(NRF_I2S, NRF_I2S_INT_STOPPED_MASK);
nrf_i2s_disable(NRF_I2S);
// When stopped, release all buffers, including these scheduled for
// the next transfer.
m_cb.handler(&m_cb.current_buffers, 0);
m_cb.handler(&m_cb.next_buffers, 0);
m_cb.state = NRFX_DRV_STATE_INITIALIZED;
NRFX_LOG_INFO("Stopped.");
}
else
{
// Check if the requested transfer has been completed:
// - full-duplex mode
if ((m_cb.use_tx && m_cb.use_rx && m_cb.tx_ready && m_cb.rx_ready) ||
// - TX only mode
(!m_cb.use_rx && m_cb.tx_ready) ||
// - RX only mode
(!m_cb.use_tx && m_cb.rx_ready))
{
m_cb.tx_ready = false;
m_cb.rx_ready = false;
// If the application did not supply the buffers for the next
// part of the transfer until this moment, the current buffers
// cannot be released, since the I2S peripheral already started
// using them. Signal this situation to the application by
// passing NULL instead of the structure with released buffers.
if (m_cb.buffers_reused)
{
m_cb.buffers_reused = false;
// This will most likely be set at this point. However, there is
// a small time window between TXPTRUPD and RXPTRUPD events,
// and it is theoretically possible that next buffers will be
// set in this window, so to be sure this flag is set to true,
// set it explicitly.
m_cb.buffers_needed = true;
m_cb.handler(NULL,
NRFX_I2S_STATUS_NEXT_BUFFERS_NEEDED);
}
else
{
// Buffers that have been used by the I2S peripheral (current)
// are now released and will be returned to the application,
// and the ones scheduled to be used as next become the current
// ones.
nrfx_i2s_buffers_t released_buffers = m_cb.current_buffers;
m_cb.current_buffers = m_cb.next_buffers;
m_cb.next_buffers.p_rx_buffer = NULL;
m_cb.next_buffers.p_tx_buffer = NULL;
m_cb.buffers_needed = true;
m_cb.handler(&released_buffers,
NRFX_I2S_STATUS_NEXT_BUFFERS_NEEDED);
}
}
}
}
#endif // NRFX_CHECK(NRFX_I2S_ENABLED)

View File

@@ -0,0 +1,174 @@
/**
* Copyright (c) 2015 - 2020, 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_LPCOMP_ENABLED)
#include <nrfx_lpcomp.h>
#include "prs/nrfx_prs.h"
#define NRFX_LOG_MODULE LPCOMP
#include <nrfx_log.h>
#define EVT_TO_STR(event) \
(event == NRF_LPCOMP_EVENT_READY ? "NRF_LPCOMP_EVENT_READY" : \
(event == NRF_LPCOMP_EVENT_DOWN ? "NRF_LPCOMP_EVENT_DOWN" : \
(event == NRF_LPCOMP_EVENT_UP ? "NRF_LPCOMP_EVENT_UP" : \
(event == NRF_LPCOMP_EVENT_CROSS ? "NRF_LPCOMP_EVENT_CROSS" : \
"UNKNOWN EVENT"))))
static nrfx_lpcomp_event_handler_t m_lpcomp_event_handler = NULL;
static nrfx_drv_state_t m_state = NRFX_DRV_STATE_UNINITIALIZED;
static void lpcomp_execute_handler(nrf_lpcomp_event_t event, uint32_t event_mask)
{
if (nrf_lpcomp_event_check(event) && nrf_lpcomp_int_enable_check(event_mask))
{
nrf_lpcomp_event_clear(event);
NRFX_LOG_DEBUG("Event: %s.", EVT_TO_STR(event));
m_lpcomp_event_handler(event);
}
}
void nrfx_lpcomp_irq_handler(void)
{
lpcomp_execute_handler(NRF_LPCOMP_EVENT_READY, LPCOMP_INTENSET_READY_Msk);
lpcomp_execute_handler(NRF_LPCOMP_EVENT_DOWN, LPCOMP_INTENSET_DOWN_Msk);
lpcomp_execute_handler(NRF_LPCOMP_EVENT_UP, LPCOMP_INTENSET_UP_Msk);
lpcomp_execute_handler(NRF_LPCOMP_EVENT_CROSS, LPCOMP_INTENSET_CROSS_Msk);
}
nrfx_err_t nrfx_lpcomp_init(nrfx_lpcomp_config_t const * p_config,
nrfx_lpcomp_event_handler_t event_handler)
{
NRFX_ASSERT(p_config);
NRFX_ASSERT(event_handler);
nrfx_err_t err_code;
if (m_state != NRFX_DRV_STATE_UNINITIALIZED)
{ // LPCOMP driver is already initialized
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;
}
m_lpcomp_event_handler = event_handler;
#if NRFX_CHECK(NRFX_PRS_ENABLED)
if (nrfx_prs_acquire(NRF_LPCOMP, nrfx_lpcomp_irq_handler) != 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
nrf_lpcomp_configure(&(p_config->hal));
nrf_lpcomp_input_select(p_config->input);
switch (p_config->hal.detection)
{
case NRF_LPCOMP_DETECT_UP:
nrf_lpcomp_int_enable(LPCOMP_INTENSET_UP_Msk);
break;
case NRF_LPCOMP_DETECT_DOWN:
nrf_lpcomp_int_enable(LPCOMP_INTENSET_DOWN_Msk);
break;
case NRF_LPCOMP_DETECT_CROSS:
nrf_lpcomp_int_enable(LPCOMP_INTENSET_CROSS_Msk);
break;
default:
break;
}
nrf_lpcomp_shorts_enable(NRF_LPCOMP_SHORT_READY_SAMPLE_MASK);
NRFX_IRQ_PRIORITY_SET(LPCOMP_IRQn, p_config->interrupt_priority);
NRFX_IRQ_ENABLE(LPCOMP_IRQn);
m_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_lpcomp_uninit(void)
{
NRFX_ASSERT(m_state != NRFX_DRV_STATE_UNINITIALIZED);
NRFX_IRQ_DISABLE(LPCOMP_IRQn);
nrfx_lpcomp_disable();
#if NRFX_CHECK(NRFX_PRS_ENABLED)
nrfx_prs_release(NRF_LPCOMP);
#endif
m_state = NRFX_DRV_STATE_UNINITIALIZED;
m_lpcomp_event_handler = NULL;
NRFX_LOG_INFO("Uninitialized.");
}
void nrfx_lpcomp_enable(void)
{
NRFX_ASSERT(m_state == NRFX_DRV_STATE_INITIALIZED);
nrf_lpcomp_enable();
nrf_lpcomp_task_trigger(NRF_LPCOMP_TASK_START);
m_state = NRFX_DRV_STATE_POWERED_ON;
NRFX_LOG_INFO("Enabled.");
}
void nrfx_lpcomp_disable(void)
{
NRFX_ASSERT(m_state == NRFX_DRV_STATE_POWERED_ON);
nrf_lpcomp_disable();
nrf_lpcomp_task_trigger(NRF_LPCOMP_TASK_STOP);
m_state = NRFX_DRV_STATE_INITIALIZED;
NRFX_LOG_INFO("Disabled.");
}
#endif // NRFX_CHECK(NRFX_LPCOMP_ENABLED)

View File

@@ -0,0 +1,848 @@
/**
* Copyright (c) 2018 - 2020, 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_NFCT_ENABLED)
#include <nrfx_nfct.h>
#define NRFX_LOG_MODULE NFCT
#include <nrfx_log.h>
#if defined(NRF52832_XXAA) || defined(NRF52832_XXAB) || \
defined(NRF52833_XXAA) || defined(NRF52840_XXAA)
#define USE_TIMER_WORKAROUND
#endif
#if defined(USE_TIMER_WORKAROUND)
#include <nrfx_timer.h>
typedef struct
{
const nrfx_timer_t timer; /**< Timer instance that supports the correct NFC field detection. */
#if defined(NRF52833_XXAA) || defined(NRF52840_XXAA)
bool fieldevents_filter_active; /**< Flag that indicates that the field events are ignored. */
bool is_hfclk_on; /**< HFCLK has started - one of the NFC activation conditions. */
bool is_delayed; /**< Required time delay has passed - one of the NFC activation conditions. */
#else
uint32_t field_state_cnt; /**< Counter of the FIELDLOST events. */
#endif // defined(NRF52833_XXAA) || defined(NRF52840_XXAA)
} nrfx_nfct_timer_workaround_t;
#if defined(NRF52833_XXAA) || defined(NRF52840_XXAA)
#define NRFX_NFCT_ACTIVATE_DELAY 1000 /**< Minimal delay in us between NFC field detection and activation of NFCT. */
#define NRFX_NFCT_TIMER_PERIOD NRFX_NFCT_ACTIVATE_DELAY
#else
#define NRFX_NFCT_FIELDLOST_THR 7
#define NRFX_NFCT_FIELD_TIMER_PERIOD 100 /**< Field polling period in us. */
#define NRFX_NFCT_TIMER_PERIOD NRFX_NFCT_FIELD_TIMER_PERIOD
#endif // defined(NRF52833_XXAA) || defined(NRF52840_XXAA)
#define NRFX_NFCT_TIMER_INSTANCE 4 /**< Timer instance used for various workarounds for the NFCT HW issues.*/
static nrfx_nfct_timer_workaround_t m_timer_workaround =
{
.timer = NRFX_TIMER_INSTANCE(NRFX_NFCT_TIMER_INSTANCE),
};
#endif // defined(USE_TIMER_WORKAROUND)
#define NRFX_NFCT_FWT_MAX_DIFF 1u /**< The maximal difference between the requested FWT and HW-limited FWT settings.*/
#define NFCT_FRAMEDELAYMAX_DEFAULT (0x00001000UL) /**< Default value of the FRAMEDELAYMAX. */
/* Mask of all possible interrupts that are relevant for data reception. */
#define NRFX_NFCT_RX_INT_MASK (NRF_NFCT_INT_RXFRAMESTART_MASK | \
NRF_NFCT_INT_RXFRAMEEND_MASK | \
NRF_NFCT_INT_RXERROR_MASK)
/* Mask of all possible interrupts that are relevant for data transmission. */
#define NRFX_NFCT_TX_INT_MASK (NRF_NFCT_INT_TXFRAMESTART_MASK | \
NRF_NFCT_INT_TXFRAMEEND_MASK)
/* Mask of all possible errors from the @ref NRF_NFCT_EVENT_RXERROR event. */
#define NRFX_NFCT_FRAME_STATUS_RX_ALL_MASK (NRF_NFCT_RX_FRAME_STATUS_CRC_MASK | \
NRF_NFCT_RX_FRAME_STATUS_PARITY_MASK | \
NRF_NFCT_RX_FRAME_STATUS_OVERRUN_MASK)
/* Mask of all possible errors from the @ref NRF_NFCT_EVENT_ERROR event. */
#if defined (NRF52832_XXAA) || defined(NRF52832_XXAB)
#define NRFX_NFCT_ERROR_STATUS_ALL_MASK (NRF_NFCT_ERROR_FRAMEDELAYTIMEOUT_MASK | \
NRF_NFCT_ERROR_NFCFIELDTOOSTRONG_MASK | \
NRF_NFCT_ERROR_NFCFIELDTOOWEAK_MASK)
#else
#define NRFX_NFCT_ERROR_STATUS_ALL_MASK (NRF_NFCT_ERROR_FRAMEDELAYTIMEOUT_MASK)
#endif
/* Macros for conversion of bits to bytes. */
#define NRFX_NFCT_BYTES_TO_BITS(_bytes) ((_bytes) << 3)
#define NRFX_NFCT_BITS_TO_BYTES(_bits) ((_bits) >> 3)
/* Macro for checking whether the NFCT interrupt is active. */
#define NRFX_NFCT_EVT_ACTIVE(_name) (nrf_nfct_event_check(NRFX_CONCAT_2(NRF_NFCT_EVENT_, _name)) && \
nrf_nfct_int_enable_check(NRFX_CONCAT_3(NRF_NFCT_INT_, _name, _MASK)))
/* Macro for callback execution. */
#define NRFX_NFCT_CB_HANDLE(_cb, _evt) \
if (_cb != NULL) \
{ \
_cb(&_evt); \
}
typedef enum
{
NRFX_NFC_FIELD_STATE_NONE, /**< Initial value that indicates no NFCT field events. */
NRFX_NFC_FIELD_STATE_OFF, /**< The NFCT FIELDLOST event has been set. */
NRFX_NFC_FIELD_STATE_ON, /**< The NFCT FIELDDETECTED event has been set. */
NRFX_NFC_FIELD_STATE_UNKNOWN /**< Both NFCT field events have been set - ambiguous state. */
} nrfx_nfct_field_state_t;
/**@brief NFCT control block. */
typedef struct
{
nrfx_nfct_config_t config;
nrfx_drv_state_t state;
volatile bool field_on;
uint32_t frame_delay_max;
} nrfx_nfct_control_block_t;
static nrfx_nfct_control_block_t m_nfct_cb;
/**
* @brief Common part of the setup used for the NFCT initialization and reinitialization.
*/
static void nrfx_nfct_hw_init_setup(void)
{
// Use Window Grid frame delay mode.
nrf_nfct_frame_delay_mode_set(NRF_NFCT_FRAME_DELAY_MODE_WINDOWGRID);
/* Begin: Workaround for anomaly 25 */
/* Workaround for wrong SENSRES values require using SDD00001, but here SDD00100 is used
because it is required to operate with Windows Phone */
nrf_nfct_sensres_bit_frame_sdd_set(NRF_NFCT_SENSRES_BIT_FRAME_SDD_00100);
/* End: Workaround for anomaly 25 */
}
static void nrfx_nfct_frame_delay_max_set(bool default_delay)
{
if (default_delay)
{
nrf_nfct_frame_delay_max_set(NFCT_FRAMEDELAYMAX_DEFAULT);
}
else
{
nrf_nfct_frame_delay_max_set(m_nfct_cb.frame_delay_max);
}
}
/**@brief Function for evaluating and handling the NFC field events.
*
* @param[in] field_state Current field state.
*/
static void nrfx_nfct_field_event_handler(volatile nrfx_nfct_field_state_t field_state)
{
nrfx_nfct_evt_t nfct_evt;
#if defined(NRF52833_XXAA) || defined(NRF52840_XXAA)
if(m_timer_workaround.fieldevents_filter_active)
{
return;
}
#endif // defined(NRF52833_XXAA) || defined(NRF52840_XXAA)
if (field_state == NRFX_NFC_FIELD_STATE_UNKNOWN)
{
/* Probe NFC field */
field_state = (nrfx_nfct_field_check()) ? NRFX_NFC_FIELD_STATE_ON : NRFX_NFC_FIELD_STATE_OFF;
}
/* Field event service. Only take action on field transition -
* based on the value of m_nfct_cb.field_on
*/
switch (field_state)
{
case NRFX_NFC_FIELD_STATE_ON:
if (!m_nfct_cb.field_on)
{
#if defined(NRF52833_XXAA) || defined(NRF52840_XXAA)
/* Begin: Workaround for anomaly 190 */
m_timer_workaround.is_hfclk_on = false;
m_timer_workaround.is_delayed = false;
m_timer_workaround.fieldevents_filter_active = true;
nrfx_timer_clear(&m_timer_workaround.timer);
nrfx_timer_enable(&m_timer_workaround.timer);
/* End: Workaround for anomaly 190 */
#elif defined(NRF52832_XXAA) || defined(NRF52832_XXAB)
nrfx_timer_clear(&m_timer_workaround.timer);
nrfx_timer_enable(&m_timer_workaround.timer);
m_timer_workaround.field_state_cnt = 0;
#endif // defined(NRF52833_XXAA) || defined(NRF52840_XXAA)
m_nfct_cb.field_on = true;
nfct_evt.evt_id = NRFX_NFCT_EVT_FIELD_DETECTED;
NRFX_NFCT_CB_HANDLE(m_nfct_cb.config.cb, nfct_evt);
}
break;
case NRFX_NFC_FIELD_STATE_OFF:
if (m_nfct_cb.field_on)
{
nrf_nfct_task_trigger(NRF_NFCT_TASK_SENSE);
nrf_nfct_int_disable(NRFX_NFCT_RX_INT_MASK | NRFX_NFCT_TX_INT_MASK);
m_nfct_cb.field_on = false;
nfct_evt.evt_id = NRFX_NFCT_EVT_FIELD_LOST;
/* Begin: Workaround for anomaly 218 */
nrfx_nfct_frame_delay_max_set(true);
/* End: Workaround for anomaly 218 */
NRFX_NFCT_CB_HANDLE(m_nfct_cb.config.cb, nfct_evt);
}
break;
default:
/* No implementation required */
break;
}
}
#if defined(USE_TIMER_WORKAROUND)
#if defined(NRF52833_XXAA) || defined(NRF52840_XXAA)
static void nrfx_nfct_activate_check(void)
{
static bool is_field_validation_pending = false;
if (is_field_validation_pending)
{
is_field_validation_pending = false;
m_timer_workaround.fieldevents_filter_active = false;
// Check the field status and take action if field is lost.
nrfx_nfct_field_event_handler(NRFX_NFC_FIELD_STATE_UNKNOWN);
return;
}
if ((m_timer_workaround.is_hfclk_on) && (m_timer_workaround.is_delayed))
{
nrf_nfct_task_trigger(NRF_NFCT_TASK_ACTIVATE);
is_field_validation_pending = true;
// Start the timer second time to validate whether the tag has locked to the field.
nrfx_timer_clear(&m_timer_workaround.timer);
nrfx_timer_enable(&m_timer_workaround.timer);
}
}
#endif // defined(NRF52833_XXAA) || defined(NRF52840_XXAA)
#if defined(NRF52832_XXAA) || defined(NRF52832_XXAB)
/* Begin: Workaround for anomaly 116 */
static inline void nrfx_nfct_reset(void)
{
uint32_t fdm;
uint32_t int_enabled;
uint8_t nfcid1[NRF_NFCT_SENSRES_NFCID1_SIZE_TRIPLE];
nrf_nfct_sensres_nfcid1_size_t nfcid1_size;
nrf_nfct_selres_protocol_t protocol;
// Save parameter settings before the reset of the NFCT peripheral.
fdm = nrf_nfct_frame_delay_max_get();
nfcid1_size = nrf_nfct_nfcid1_get(nfcid1);
protocol = nrf_nfct_selsres_protocol_get();
int_enabled = nrf_nfct_int_enable_get();
// Reset the NFCT peripheral.
*(volatile uint32_t *)0x40005FFC = 0;
*(volatile uint32_t *)0x40005FFC;
*(volatile uint32_t *)0x40005FFC = 1;
// Restore parameter settings after the reset of the NFCT peripheral.
nrf_nfct_frame_delay_max_set(fdm);
nrf_nfct_nfcid1_set(nfcid1, nfcid1_size);
nrf_nfct_selres_protocol_set(protocol);
// Restore general HW configuration.
nrfx_nfct_hw_init_setup();
// Restore interrupts.
nrf_nfct_int_enable(int_enabled);
// Disable interrupts associated with data exchange.
nrf_nfct_int_disable(NRFX_NFCT_RX_INT_MASK | NRFX_NFCT_TX_INT_MASK);
NRFX_LOG_INFO("Reinitialize");
}
/* End: Workaround for anomaly 116 */
static void nrfx_nfct_field_poll(void)
{
if (!nrfx_nfct_field_check())
{
if (++m_timer_workaround.field_state_cnt > NRFX_NFCT_FIELDLOST_THR)
{
nrfx_nfct_evt_t nfct_evt =
{
.evt_id = NRFX_NFCT_EVT_FIELD_LOST,
};
nrfx_timer_disable(&m_timer_workaround.timer);
m_nfct_cb.field_on = false;
/* Begin: Workaround for anomaly 218 */
nrfx_nfct_frame_delay_max_set(true);
/* End: Workaround for anomaly 218 */
/* Begin: Workaround for anomaly 116 */
/* resume the NFCT to initialized state */
nrfx_nfct_reset();
/* End: Workaround for anomaly 116 */
NRFX_NFCT_CB_HANDLE(m_nfct_cb.config.cb, nfct_evt);
}
return;
}
m_timer_workaround.field_state_cnt = 0;
}
#endif // defined(NRF52832_XXAA) || defined(NRF52832_XXAB)
static void nrfx_nfct_field_timer_handler(nrf_timer_event_t event_type, void * p_context)
{
(void)p_context;
if (event_type != NRF_TIMER_EVENT_COMPARE0)
{
return;
}
#if defined(NRF52833_XXAA) || defined(NRF52840_XXAA)
m_timer_workaround.is_delayed = true;
nrfx_timer_disable(&m_timer_workaround.timer);
nrfx_nfct_activate_check();
#else
nrfx_nfct_field_poll();
#endif // defined(NRF52833_XXAA) || defined(NRF52840_XXAA)
}
static inline nrfx_err_t nrfx_nfct_field_timer_config(void)
{
nrfx_err_t err_code;
nrfx_timer_config_t timer_cfg =
{
.frequency = NRF_TIMER_FREQ_1MHz,
.mode = NRF_TIMER_MODE_TIMER,
.bit_width = NRF_TIMER_BIT_WIDTH_16,
.interrupt_priority = NRFX_NFCT_CONFIG_IRQ_PRIORITY
};
err_code = nrfx_timer_init(&m_timer_workaround.timer, &timer_cfg, nrfx_nfct_field_timer_handler);
if (err_code != NRFX_SUCCESS)
{
return err_code;
}
nrfx_timer_extended_compare(&m_timer_workaround.timer,
NRF_TIMER_CC_CHANNEL0,
nrfx_timer_us_to_ticks(&m_timer_workaround.timer, NRFX_NFCT_TIMER_PERIOD),
NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK,
true);
return err_code;
}
#endif // defined(USE_TIMER_WORKAROUND)
static inline nrf_nfct_sensres_nfcid1_size_t nrf_nfct_nfcid1_size_to_sensres_size(uint8_t nfcid1_size)
{
switch (nfcid1_size)
{
case NRFX_NFCT_NFCID1_SINGLE_SIZE:
return NRF_NFCT_SENSRES_NFCID1_SIZE_SINGLE;
case NRFX_NFCT_NFCID1_DOUBLE_SIZE:
return NRF_NFCT_SENSRES_NFCID1_SIZE_DOUBLE;
case NRFX_NFCT_NFCID1_TRIPLE_SIZE:
return NRF_NFCT_SENSRES_NFCID1_SIZE_TRIPLE;
default:
return NRF_NFCT_SENSRES_NFCID1_SIZE_DOUBLE;
}
}
static inline void nrfx_nfct_rxtx_int_enable(uint32_t rxtx_int_mask)
{
nrf_nfct_int_enable(rxtx_int_mask & m_nfct_cb.config.rxtx_int_mask);
}
nrfx_err_t nrfx_nfct_init(nrfx_nfct_config_t const * p_config)
{
NRFX_ASSERT(p_config);
nrfx_err_t err_code = NRFX_SUCCESS;
if (m_nfct_cb.state != NRFX_DRV_STATE_UNINITIALIZED)
{
return NRFX_ERROR_INVALID_STATE;
}
m_nfct_cb.config = *p_config;
nrfx_nfct_hw_init_setup();
NRFX_IRQ_PENDING_CLEAR(NFCT_IRQn);
NRFX_IRQ_PRIORITY_SET(NFCT_IRQn, NRFX_NFCT_CONFIG_IRQ_PRIORITY);
NRFX_IRQ_ENABLE(NFCT_IRQn);
#if defined(USE_TIMER_WORKAROUND)
/* Initialize Timer module as the workaround for NFCT HW issues. */
err_code = nrfx_nfct_field_timer_config();
#endif // defined(USE_TIMER_WORKAROUND)
if (err_code == NRFX_SUCCESS)
{
uint8_t default_nfcid1[NRFX_NFCT_NFCID1_DEFAULT_LEN];
err_code = nrfx_nfct_nfcid1_default_bytes_get(default_nfcid1, sizeof(default_nfcid1));
NRFX_ASSERT(err_code == NRFX_SUCCESS);
nrf_nfct_nfcid1_set(default_nfcid1, NRF_NFCT_SENSRES_NFCID1_SIZE_DEFAULT);
}
else
{
return err_code;
}
m_nfct_cb.state = NRFX_DRV_STATE_INITIALIZED;
m_nfct_cb.frame_delay_max = NFCT_FRAMEDELAYMAX_DEFAULT;
NRFX_LOG_INFO("Initialized");
return err_code;
}
void nrfx_nfct_uninit(void)
{
nrfx_nfct_disable();
NRFX_IRQ_DISABLE(NFCT_IRQn);
NRFX_IRQ_PENDING_CLEAR(NFCT_IRQn);
#if defined(USE_TIMER_WORKAROUND)
/* De-initialize Timer module as the workaround for NFCT HW issues. */
nrfx_timer_uninit(&m_timer_workaround.timer);
#endif // defined(USE_TIMER_WORKAROUND)
m_nfct_cb.state = NRFX_DRV_STATE_UNINITIALIZED;
}
void nrfx_nfct_enable(void)
{
nrf_nfct_error_status_clear(NRFX_NFCT_ERROR_STATUS_ALL_MASK);
nrf_nfct_task_trigger(NRF_NFCT_TASK_SENSE);
nrf_nfct_int_enable(NRF_NFCT_INT_FIELDDETECTED_MASK | NRF_NFCT_INT_ERROR_MASK |
NRF_NFCT_INT_SELECTED_MASK);
#if !defined(NRF52832_XXAA) && !defined(NRF52832_XXAB)
nrf_nfct_int_enable(NRF_NFCT_INT_FIELDLOST_MASK);
#endif // !defined(NRF52832_XXAA) && !defined(NRF52832_XXAB)
NRFX_LOG_INFO("Start");
}
void nrfx_nfct_disable(void)
{
nrf_nfct_int_disable(NRF_NFCT_DISABLE_ALL_INT);
nrf_nfct_task_trigger(NRF_NFCT_TASK_DISABLE);
NRFX_LOG_INFO("Stop");
}
bool nrfx_nfct_field_check(void)
{
uint32_t const field_state = nrf_nfct_field_status_get();
if (((field_state & NRF_NFCT_FIELD_STATE_PRESENT_MASK) == 0) &&
((field_state & NRF_NFCT_FIELD_STATE_LOCK_MASK) == 0))
{
/* Field is not active */
return false;
}
return true;
}
void nrfx_nfct_rx(nrfx_nfct_data_desc_t const * p_tx_data)
{
NRFX_ASSERT(p_tx_data);
nrf_nfct_rxtx_buffer_set((uint8_t *) p_tx_data->p_data, p_tx_data->data_size);
nrfx_nfct_rxtx_int_enable(NRFX_NFCT_RX_INT_MASK);
nrf_nfct_task_trigger(NRF_NFCT_TASK_ENABLERXDATA);
}
nrfx_err_t nrfx_nfct_tx(nrfx_nfct_data_desc_t const * p_tx_data,
nrf_nfct_frame_delay_mode_t delay_mode)
{
NRFX_ASSERT(p_tx_data);
NRFX_ASSERT(p_tx_data->p_data);
if (p_tx_data->data_size == 0)
{
return NRFX_ERROR_INVALID_LENGTH;
}
nrf_nfct_rxtx_buffer_set((uint8_t *) p_tx_data->p_data, p_tx_data->data_size);
nrf_nfct_tx_bits_set(NRFX_NFCT_BYTES_TO_BITS(p_tx_data->data_size));
nrf_nfct_frame_delay_mode_set((nrf_nfct_frame_delay_mode_t) delay_mode);
nrfx_nfct_rxtx_int_enable(NRFX_NFCT_TX_INT_MASK);
nrf_nfct_task_trigger(NRF_NFCT_TASK_STARTTX);
NRFX_LOG_INFO("Tx start");
return NRFX_SUCCESS;
}
void nrfx_nfct_state_force(nrfx_nfct_state_t state)
{
#if defined(NRF52833_XXAA) || defined(NRF52840_XXAA)
if (state == NRFX_NFCT_STATE_ACTIVATED)
{
m_timer_workaround.is_hfclk_on = true;
/* NFCT will be activated based on additional conditions */
nrfx_nfct_activate_check();
return;
}
#endif // defined(NRF52833_XXAA) || defined(NRF52840_XXAA)
nrf_nfct_task_trigger((nrf_nfct_task_t) state);
}
void nrfx_nfct_init_substate_force(nrfx_nfct_active_state_t sub_state)
{
if (sub_state == NRFX_NFCT_ACTIVE_STATE_DEFAULT)
{
#if defined(NRF52832_XXAA) || defined(NRF52832_XXAB)
if (((*(uint32_t volatile *)(0x40005420)) & 0x1UL) == (1UL))
#else
if (nrf_nfct_sleep_state_get() == NRF_NFCT_SLEEP_STATE_SLEEP_A)
#endif //defined(NRF52832_XXAA) || defined(NRF52832_XXAB)
{
// Default state is SLEEP_A
nrf_nfct_task_trigger(NRF_NFCT_TASK_GOSLEEP);
}
else
{
// Default state is IDLE
nrf_nfct_task_trigger(NRF_NFCT_TASK_GOIDLE);
}
}
else
{
nrf_nfct_task_trigger((nrf_nfct_task_t) sub_state);
}
/* Begin: Workaround for anomaly 218 */
nrfx_nfct_frame_delay_max_set(true);
/* End: Workaround for anomaly 218 */
/* Disable TX/RX here (will be enabled at SELECTED) */
nrf_nfct_int_disable(NRFX_NFCT_RX_INT_MASK | NRFX_NFCT_TX_INT_MASK);
}
nrfx_err_t nrfx_nfct_parameter_set(nrfx_nfct_param_t const * p_param)
{
NRFX_ASSERT(p_param);
switch (p_param->id)
{
case NRFX_NFCT_PARAM_ID_FDT:
{
uint32_t delay = p_param->data.fdt;
uint32_t delay_thr = NFCT_FRAMEDELAYMAX_FRAMEDELAYMAX_Msk;
// Delay validation.
if (delay > (delay_thr + NRFX_NFCT_FWT_MAX_DIFF))
{
return NRFX_ERROR_INVALID_PARAM;
}
delay = (delay > delay_thr) ? delay_thr : delay;
m_nfct_cb.frame_delay_max = delay;
break;
}
case NRFX_NFCT_PARAM_ID_SEL_RES:
if (p_param->data.sel_res_protocol > NRF_NFCT_SELRES_PROTOCOL_NFCDEP_T4AT)
{
return NRFX_ERROR_INVALID_PARAM;
}
nrf_nfct_selres_protocol_set((nrf_nfct_selres_protocol_t) p_param->data.sel_res_protocol);
break;
case NRFX_NFCT_PARAM_ID_NFCID1:
{
nrf_nfct_sensres_nfcid1_size_t id_size_mask;
id_size_mask = nrf_nfct_nfcid1_size_to_sensres_size(p_param->data.nfcid1.id_size);
nrf_nfct_nfcid1_set(p_param->data.nfcid1.p_id, id_size_mask);
break;
}
default:
break;
}
return NRFX_SUCCESS;
}
nrfx_err_t nrfx_nfct_nfcid1_default_bytes_get(uint8_t * const p_nfcid1_buff,
uint32_t nfcid1_buff_len)
{
if ((nfcid1_buff_len != NRFX_NFCT_NFCID1_SINGLE_SIZE) &&
(nfcid1_buff_len != NRFX_NFCT_NFCID1_DOUBLE_SIZE) &&
(nfcid1_buff_len != NRFX_NFCT_NFCID1_TRIPLE_SIZE))
{
return NRFX_ERROR_INVALID_LENGTH;
}
uint32_t nfc_tag_header0 = NRF_FICR->NFC.TAGHEADER0;
uint32_t nfc_tag_header1 = NRF_FICR->NFC.TAGHEADER1;
uint32_t nfc_tag_header2 = NRF_FICR->NFC.TAGHEADER2;
p_nfcid1_buff[0] = (uint8_t) (nfc_tag_header0 >> 0);
p_nfcid1_buff[1] = (uint8_t) (nfc_tag_header0 >> 8);
p_nfcid1_buff[2] = (uint8_t) (nfc_tag_header0 >> 16);
p_nfcid1_buff[3] = (uint8_t) (nfc_tag_header1 >> 0);
if (nfcid1_buff_len != NRFX_NFCT_NFCID1_SINGLE_SIZE)
{
p_nfcid1_buff[4] = (uint8_t) (nfc_tag_header1 >> 8);
p_nfcid1_buff[5] = (uint8_t) (nfc_tag_header1 >> 16);
p_nfcid1_buff[6] = (uint8_t) (nfc_tag_header1 >> 24);
if (nfcid1_buff_len == NRFX_NFCT_NFCID1_TRIPLE_SIZE)
{
p_nfcid1_buff[7] = (uint8_t) (nfc_tag_header2 >> 0);
p_nfcid1_buff[8] = (uint8_t) (nfc_tag_header2 >> 8);
p_nfcid1_buff[9] = (uint8_t) (nfc_tag_header2 >> 16);
}
/* Begin: Workaround for anomaly 181. */
/* Workaround for wrong value in NFCID1. Value 0x88 cannot be used as byte 3
of a double-size NFCID1, according to the NFC Forum Digital Protocol specification. */
else if (p_nfcid1_buff[3] == 0x88)
{
p_nfcid1_buff[3] |= 0x11;
}
/* End: Workaround for anomaly 181 */
}
return NRFX_SUCCESS;
}
void nrfx_nfct_autocolres_enable(void)
{
#if defined(NRF52832_XXAA) || defined(NRF52832_XXAB)
(*(uint32_t *)(0x4000559C)) &= (~(0x1UL));
#else
nrf_nfct_autocolres_enable();
#endif //defined(NRF52832_XXAA) || defined(NRF52832_XXAB)
}
void nrfx_nfct_autocolres_disable(void)
{
#if defined(NRF52832_XXAA) || defined(NRF52832_XXAB)
(*(uint32_t *)(0x4000559C)) |= (0x1UL);
#else
nrf_nfct_autocolres_disable();
#endif //defined(NRF52832_XXAA) || defined(NRF52832_XXAB)
}
void nrfx_nfct_irq_handler(void)
{
nrfx_nfct_field_state_t current_field = NRFX_NFC_FIELD_STATE_NONE;
if (NRFX_NFCT_EVT_ACTIVE(FIELDDETECTED))
{
nrf_nfct_event_clear(NRF_NFCT_EVENT_FIELDDETECTED);
current_field = NRFX_NFC_FIELD_STATE_ON;
NRFX_LOG_DEBUG("Field detected");
}
#if !defined(NRF52832_XXAA) && !defined(NRF52832_XXAB)
if (NRFX_NFCT_EVT_ACTIVE(FIELDLOST))
{
nrf_nfct_event_clear(NRF_NFCT_EVENT_FIELDLOST);
current_field = (current_field == NRFX_NFC_FIELD_STATE_NONE) ?
NRFX_NFC_FIELD_STATE_OFF : NRFX_NFC_FIELD_STATE_UNKNOWN;
NRFX_LOG_DEBUG("Field lost");
}
#endif //!defined(NRF52832_XXAA) && !defined(NRF52832_XXAB)
/* Perform actions if any FIELD event is active */
if (current_field != NRFX_NFC_FIELD_STATE_NONE)
{
nrfx_nfct_field_event_handler(current_field);
}
if (NRFX_NFCT_EVT_ACTIVE(RXFRAMEEND))
{
nrf_nfct_event_clear(NRF_NFCT_EVENT_RXFRAMEEND);
nrfx_nfct_evt_t nfct_evt =
{
.evt_id = NRFX_NFCT_EVT_RX_FRAMEEND
};
/* Take into account only the number of whole bytes. */
nfct_evt.params.rx_frameend.rx_status = 0;
nfct_evt.params.rx_frameend.rx_data.p_data = nrf_nfct_rxtx_buffer_get();
nfct_evt.params.rx_frameend.rx_data.data_size = NRFX_NFCT_BITS_TO_BYTES(nrf_nfct_rx_bits_get(true));
if (NRFX_NFCT_EVT_ACTIVE(RXERROR))
{
nfct_evt.params.rx_frameend.rx_status =
(nrf_nfct_rx_frame_status_get() & NRFX_NFCT_FRAME_STATUS_RX_ALL_MASK);
nrf_nfct_event_clear(NRF_NFCT_EVENT_RXERROR);
NRFX_LOG_DEBUG("Rx error (0x%x)", (unsigned int) nfct_evt.params.rx_frameend.rx_status);
/* Clear rx frame status */
nrf_nfct_rx_frame_status_clear(NRFX_NFCT_FRAME_STATUS_RX_ALL_MASK);
}
NRFX_NFCT_CB_HANDLE(m_nfct_cb.config.cb, nfct_evt);
/* Clear TXFRAMESTART EVENT so it can be checked in hal_nfc_send */
nrf_nfct_event_clear(NRF_NFCT_EVENT_TXFRAMESTART);
NRFX_LOG_DEBUG("Rx fend");
}
if (NRFX_NFCT_EVT_ACTIVE(TXFRAMEEND))
{
nrf_nfct_event_clear(NRF_NFCT_EVENT_TXFRAMEEND);
nrfx_nfct_evt_t nfct_evt =
{
.evt_id = NRFX_NFCT_EVT_TX_FRAMEEND
};
/* Disable TX END event to ignore frame transmission other than READ response */
nrf_nfct_int_disable(NRFX_NFCT_TX_INT_MASK);
NRFX_NFCT_CB_HANDLE(m_nfct_cb.config.cb, nfct_evt);
NRFX_LOG_DEBUG("Tx fend");
}
if (NRFX_NFCT_EVT_ACTIVE(SELECTED))
{
nrf_nfct_event_clear(NRF_NFCT_EVENT_SELECTED);
/* Clear also RX END and RXERROR events because SW does not take care of
commands that were received before selecting the tag. */
nrf_nfct_event_clear(NRF_NFCT_EVENT_RXFRAMEEND);
nrf_nfct_event_clear(NRF_NFCT_EVENT_RXERROR);
nrf_nfct_event_clear(NRF_NFCT_EVENT_TXFRAMESTART);
nrf_nfct_event_clear(NRF_NFCT_EVENT_TXFRAMEEND);
/* Begin: Workaround for anomaly 218 */
nrfx_nfct_frame_delay_max_set(false);
/* End: Workaround for anomaly 218 */
/* At this point any previous error status can be ignored. */
nrf_nfct_rx_frame_status_clear(NRFX_NFCT_FRAME_STATUS_RX_ALL_MASK);
nrf_nfct_error_status_clear(NRFX_NFCT_ERROR_STATUS_ALL_MASK);
nrfx_nfct_evt_t nfct_evt =
{
.evt_id = NRFX_NFCT_EVT_SELECTED
};
NRFX_NFCT_CB_HANDLE(m_nfct_cb.config.cb, nfct_evt);
NRFX_LOG_DEBUG("Selected");
}
if (NRFX_NFCT_EVT_ACTIVE(ERROR))
{
uint32_t err_status = nrf_nfct_error_status_get();
nrf_nfct_event_clear(NRF_NFCT_EVENT_ERROR);
nrfx_nfct_evt_t nfct_evt =
{
.evt_id = NRFX_NFCT_EVT_ERROR
};
/* Clear FRAMEDELAYTIMEOUT error (expected HW behaviour) when SLP_REQ command was received. */
if (err_status & NRF_NFCT_ERROR_FRAMEDELAYTIMEOUT_MASK)
{
nrf_nfct_error_status_clear(NRF_NFCT_ERROR_FRAMEDELAYTIMEOUT_MASK);
nfct_evt.params.error.reason = NRFX_NFCT_ERROR_FRAMEDELAYTIMEOUT;
NRFX_NFCT_CB_HANDLE(m_nfct_cb.config.cb, nfct_evt);
}
/* Report any other error. */
err_status &= ~NRF_NFCT_ERROR_FRAMEDELAYTIMEOUT_MASK;
if (err_status)
{
NRFX_LOG_DEBUG("Error (0x%x)", (unsigned int) err_status);
}
/* Clear error status. */
nrf_nfct_error_status_clear(NRFX_NFCT_ERROR_STATUS_ALL_MASK);
}
if (NRFX_NFCT_EVT_ACTIVE(TXFRAMESTART))
{
nrf_nfct_event_clear(NRF_NFCT_EVENT_TXFRAMESTART);
if (m_nfct_cb.config.cb != NULL)
{
nrfx_nfct_evt_t nfct_evt;
nfct_evt.evt_id = NRFX_NFCT_EVT_TX_FRAMESTART;
nfct_evt.params.tx_framestart.tx_data.p_data = nrf_nfct_rxtx_buffer_get();
nfct_evt.params.tx_framestart.tx_data.data_size = NRFX_NFCT_BITS_TO_BYTES(nrf_nfct_tx_bits_get());
m_nfct_cb.config.cb(&nfct_evt);
}
}
}
#endif // NRFX_CHECK(NRFX_NFCT_ENABLED)

View File

@@ -0,0 +1,435 @@
/**
* Copyright (c) 2019 - 2020, 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)

View File

@@ -0,0 +1,370 @@
/**
* Copyright (c) 2015 - 2020, 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_PDM_ENABLED)
#include <nrfx_pdm.h>
#include <hal/nrf_gpio.h>
#define NRFX_LOG_MODULE PDM
#include <nrfx_log.h>
#define EVT_TO_STR(event) \
(event == NRF_PDM_EVENT_STARTED ? "NRF_PDM_EVENT_STARTED" : \
(event == NRF_PDM_EVENT_STOPPED ? "NRF_PDM_EVENT_STOPPED" : \
(event == NRF_PDM_EVENT_END ? "NRF_PDM_EVENT_END" : \
"UNKNOWN EVENT")))
/** @brief PDM interface status. */
typedef enum
{
NRFX_PDM_STATE_IDLE,
NRFX_PDM_STATE_RUNNING,
NRFX_PDM_STATE_STARTING,
NRFX_PDM_STATE_STOPPING
} nrfx_pdm_state_t;
/** @brief PDM interface control block.*/
typedef struct
{
nrfx_pdm_event_handler_t event_handler; ///< Event handler function pointer.
int16_t * buff_address[2]; ///< Sample buffers.
uint16_t buff_length[2]; ///< Length of the sample buffers.
nrfx_drv_state_t drv_state; ///< Driver state.
volatile nrfx_pdm_state_t op_state; ///< PDM peripheral operation state.
uint8_t active_buffer; ///< Number of currently active buffer.
uint8_t error; ///< Driver error flag.
volatile uint8_t irq_buff_request; ///< Request the next buffer in the ISR.
} nrfx_pdm_cb_t;
static nrfx_pdm_cb_t m_cb;
void nrfx_pdm_irq_handler(void)
{
if (nrf_pdm_event_check(NRF_PDM_EVENT_STARTED))
{
nrf_pdm_event_clear(NRF_PDM_EVENT_STARTED);
NRFX_LOG_DEBUG("Event: %s.", EVT_TO_STR(NRF_PDM_EVENT_STARTED));
uint8_t finished_buffer = m_cb.active_buffer;
// Check if the next buffer was set before.
uint8_t next_buffer = (~m_cb.active_buffer) & 0x01;
if (m_cb.buff_address[next_buffer] ||
m_cb.op_state == NRFX_PDM_STATE_STARTING)
{
nrfx_pdm_evt_t evt;
evt.error = NRFX_PDM_NO_ERROR;
m_cb.error = 0;
// Release the full buffer if ready and request the next one.
if (m_cb.op_state == NRFX_PDM_STATE_STARTING)
{
evt.buffer_released = 0;
m_cb.op_state = NRFX_PDM_STATE_RUNNING;
}
else
{
evt.buffer_released = m_cb.buff_address[finished_buffer];
m_cb.buff_address[finished_buffer] = 0;
m_cb.active_buffer = next_buffer;
}
evt.buffer_requested = true;
m_cb.event_handler(&evt);
}
else
{
// No next buffer available. Report an error.
// Do not request the new buffer as it was already done.
if (m_cb.error == 0)
{
nrfx_pdm_evt_t const evt = {
.buffer_requested = false,
.buffer_released = NULL,
.error = NRFX_PDM_ERROR_OVERFLOW
};
m_cb.error = 1;
m_cb.event_handler(&evt);
}
}
if (m_cb.op_state == NRFX_PDM_STATE_STARTING)
{
m_cb.op_state = NRFX_PDM_STATE_RUNNING;
}
}
else if (nrf_pdm_event_check(NRF_PDM_EVENT_STOPPED))
{
nrf_pdm_event_clear(NRF_PDM_EVENT_STOPPED);
NRFX_LOG_DEBUG("Event: %s.", EVT_TO_STR(NRF_PDM_EVENT_STOPPED));
nrf_pdm_disable();
m_cb.op_state = NRFX_PDM_STATE_IDLE;
// Release the buffers.
nrfx_pdm_evt_t evt;
evt.error = NRFX_PDM_NO_ERROR;
evt.buffer_requested = false;
if (m_cb.buff_address[m_cb.active_buffer])
{
evt.buffer_released = m_cb.buff_address[m_cb.active_buffer];
m_cb.buff_address[m_cb.active_buffer] = 0;
m_cb.event_handler(&evt);
}
uint8_t second_buffer = (~m_cb.active_buffer) & 0x01;
if (m_cb.buff_address[second_buffer])
{
evt.buffer_released = m_cb.buff_address[second_buffer];
m_cb.buff_address[second_buffer] = 0;
m_cb.event_handler(&evt);
}
m_cb.active_buffer = 0;
}
if (m_cb.irq_buff_request)
{
nrfx_pdm_evt_t const evt =
{
.buffer_requested = true,
.buffer_released = NULL,
.error = NRFX_PDM_NO_ERROR,
};
m_cb.irq_buff_request = 0;
m_cb.event_handler(&evt);
}
}
nrfx_err_t nrfx_pdm_init(nrfx_pdm_config_t const * p_config,
nrfx_pdm_event_handler_t event_handler)
{
NRFX_ASSERT(p_config);
NRFX_ASSERT(event_handler);
nrfx_err_t err_code;
if (m_cb.drv_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 (p_config->gain_l > NRF_PDM_GAIN_MAXIMUM ||
p_config->gain_r > NRF_PDM_GAIN_MAXIMUM)
{
err_code = NRFX_ERROR_INVALID_PARAM;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
m_cb.buff_address[0] = 0;
m_cb.buff_address[1] = 0;
m_cb.active_buffer = 0;
m_cb.error = 0;
m_cb.event_handler = event_handler;
m_cb.op_state = NRFX_PDM_STATE_IDLE;
nrf_pdm_clock_set(p_config->clock_freq);
nrf_pdm_mode_set(p_config->mode, p_config->edge);
nrf_pdm_gain_set(p_config->gain_l, p_config->gain_r);
nrf_gpio_cfg_output(p_config->pin_clk);
nrf_gpio_pin_clear(p_config->pin_clk);
nrf_gpio_cfg_input(p_config->pin_din, NRF_GPIO_PIN_NOPULL);
nrf_pdm_psel_connect(p_config->pin_clk, p_config->pin_din);
nrf_pdm_event_clear(NRF_PDM_EVENT_STARTED);
nrf_pdm_event_clear(NRF_PDM_EVENT_END);
nrf_pdm_event_clear(NRF_PDM_EVENT_STOPPED);
nrf_pdm_int_enable(NRF_PDM_INT_STARTED | NRF_PDM_INT_STOPPED);
NRFX_IRQ_PRIORITY_SET(PDM_IRQn, p_config->interrupt_priority);
NRFX_IRQ_ENABLE(PDM_IRQn);
m_cb.drv_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_pdm_uninit(void)
{
nrf_pdm_disable();
nrf_pdm_psel_disconnect();
m_cb.drv_state = NRFX_DRV_STATE_UNINITIALIZED;
NRFX_LOG_INFO("Uninitialized.");
}
static void pdm_start()
{
m_cb.drv_state = NRFX_DRV_STATE_POWERED_ON;
nrf_pdm_enable();
nrf_pdm_event_clear(NRF_PDM_EVENT_STARTED);
nrf_pdm_task_trigger(NRF_PDM_TASK_START);
}
static void pdm_buf_request()
{
m_cb.irq_buff_request = 1;
NRFX_IRQ_PENDING_SET(PDM_IRQn);
}
nrfx_err_t nrfx_pdm_start(void)
{
NRFX_ASSERT(m_cb.drv_state != NRFX_DRV_STATE_UNINITIALIZED);
nrfx_err_t err_code;
if (m_cb.op_state != NRFX_PDM_STATE_IDLE)
{
if (m_cb.op_state == NRFX_PDM_STATE_RUNNING)
{
err_code = NRFX_SUCCESS;
NRFX_LOG_INFO("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
err_code = NRFX_ERROR_BUSY;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
m_cb.op_state = NRFX_PDM_STATE_STARTING;
pdm_buf_request();
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_pdm_buffer_set(int16_t * buffer, uint16_t buffer_length)
{
if (m_cb.drv_state == NRFX_DRV_STATE_UNINITIALIZED)
{
return NRFX_ERROR_INVALID_STATE;
}
if (m_cb.op_state == NRFX_PDM_STATE_STOPPING)
{
return NRFX_ERROR_BUSY;
}
if ((buffer == NULL) || (buffer_length > NRFX_PDM_MAX_BUFFER_SIZE))
{
return NRFX_ERROR_INVALID_PARAM;
}
nrfx_err_t err_code = NRFX_SUCCESS;
// Enter the PDM critical section.
NRFX_IRQ_DISABLE(PDM_IRQn);
uint8_t next_buffer = (~m_cb.active_buffer) & 0x01;
if (m_cb.op_state == NRFX_PDM_STATE_STARTING)
{
next_buffer = 0;
}
if (m_cb.buff_address[next_buffer])
{
// Buffer already set.
err_code = NRFX_ERROR_BUSY;
}
else
{
m_cb.buff_address[next_buffer] = buffer;
m_cb.buff_length[next_buffer] = buffer_length;
nrf_pdm_buffer_set((uint32_t *)buffer, buffer_length);
if (m_cb.drv_state != NRFX_DRV_STATE_POWERED_ON)
{
pdm_start();
}
}
NRFX_IRQ_ENABLE(PDM_IRQn);
return err_code;
}
nrfx_err_t nrfx_pdm_stop(void)
{
NRFX_ASSERT(m_cb.drv_state != NRFX_DRV_STATE_UNINITIALIZED);
nrfx_err_t err_code;
if (m_cb.op_state != NRFX_PDM_STATE_RUNNING)
{
if (m_cb.op_state == NRFX_PDM_STATE_IDLE ||
m_cb.op_state == NRFX_PDM_STATE_STARTING)
{
nrf_pdm_disable();
m_cb.op_state = NRFX_PDM_STATE_IDLE;
err_code = NRFX_SUCCESS;
NRFX_LOG_INFO("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
err_code = NRFX_ERROR_BUSY;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
m_cb.drv_state = NRFX_DRV_STATE_INITIALIZED;
m_cb.op_state = NRFX_PDM_STATE_STOPPING;
nrf_pdm_task_trigger(NRF_PDM_TASK_STOP);
err_code = NRFX_SUCCESS;
NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
#endif // NRFX_CHECK(NRFX_PDM_ENABLED)

View File

@@ -0,0 +1,341 @@
/**
* Copyright (c) 2017 - 2020, 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)

View File

@@ -0,0 +1,530 @@
/**
* Copyright (c) 2015 - 2020, 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)

View File

@@ -0,0 +1,523 @@
/**
* Copyright (c) 2015 - 2020, 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)

View File

@@ -0,0 +1,204 @@
/**
* Copyright (c) 2015 - 2020, 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_QDEC_ENABLED)
#include <nrfx_qdec.h>
#include <hal/nrf_gpio.h>
#define NRFX_LOG_MODULE QDEC
#include <nrfx_log.h>
#define EVT_TO_STR(event) \
(event == NRF_QDEC_EVENT_SAMPLERDY ? "NRF_QDEC_EVENT_SAMPLERDY" : \
(event == NRF_QDEC_EVENT_REPORTRDY ? "NRF_QDEC_EVENT_REPORTRDY" : \
(event == NRF_QDEC_EVENT_ACCOF ? "NRF_QDEC_EVENT_ACCOF" : \
"UNKNOWN EVENT")))
static nrfx_qdec_event_handler_t m_qdec_event_handler = NULL;
static nrfx_drv_state_t m_state = NRFX_DRV_STATE_UNINITIALIZED;
void nrfx_qdec_irq_handler(void)
{
nrfx_qdec_event_t event;
if ( nrf_qdec_event_check(NRF_QDEC_EVENT_SAMPLERDY) &&
nrf_qdec_int_enable_check(NRF_QDEC_INT_SAMPLERDY_MASK) )
{
nrf_qdec_event_clear(NRF_QDEC_EVENT_SAMPLERDY);
NRFX_LOG_DEBUG("Event: %s.", EVT_TO_STR(NRF_QDEC_EVENT_SAMPLERDY));
event.type = NRF_QDEC_EVENT_SAMPLERDY;
event.data.sample.value = (int8_t)nrf_qdec_sample_get();
m_qdec_event_handler(event);
}
if ( nrf_qdec_event_check(NRF_QDEC_EVENT_REPORTRDY) &&
nrf_qdec_int_enable_check(NRF_QDEC_INT_REPORTRDY_MASK) )
{
nrf_qdec_event_clear(NRF_QDEC_EVENT_REPORTRDY);
NRFX_LOG_DEBUG("Event: %s.", EVT_TO_STR(NRF_QDEC_EVENT_REPORTRDY));
event.type = NRF_QDEC_EVENT_REPORTRDY;
event.data.report.acc = (int16_t)nrf_qdec_accread_get();
event.data.report.accdbl = (uint16_t)nrf_qdec_accdblread_get();
m_qdec_event_handler(event);
}
if ( nrf_qdec_event_check(NRF_QDEC_EVENT_ACCOF) &&
nrf_qdec_int_enable_check(NRF_QDEC_INT_ACCOF_MASK) )
{
nrf_qdec_event_clear(NRF_QDEC_EVENT_ACCOF);
NRFX_LOG_DEBUG("Event: %s.", EVT_TO_STR(NRF_QDEC_EVENT_ACCOF));
event.type = NRF_QDEC_EVENT_ACCOF;
m_qdec_event_handler(event);
}
}
nrfx_err_t nrfx_qdec_init(nrfx_qdec_config_t const * p_config,
nrfx_qdec_event_handler_t event_handler)
{
NRFX_ASSERT(p_config);
NRFX_ASSERT(event_handler);
nrfx_err_t err_code;
if (m_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;
}
m_qdec_event_handler = event_handler;
nrf_qdec_sampleper_set(p_config->sampleper);
nrf_gpio_cfg_input(p_config->psela, NRF_GPIO_PIN_NOPULL);
nrf_gpio_cfg_input(p_config->pselb, NRF_GPIO_PIN_NOPULL);
if (p_config->pselled != NRF_QDEC_LED_NOT_CONNECTED)
{
nrf_gpio_cfg_input(p_config->pselled, NRF_GPIO_PIN_NOPULL);
nrf_qdec_ledpre_set(p_config->ledpre);
nrf_qdec_ledpol_set(p_config->ledpol);
}
nrf_qdec_pio_assign(p_config->psela, p_config->pselb, p_config->pselled);
nrf_qdec_shorts_enable(NRF_QDEC_SHORT_REPORTRDY_READCLRACC_MASK);
if (p_config->dbfen)
{
nrf_qdec_dbfen_enable();
}
else
{
nrf_qdec_dbfen_disable();
}
uint32_t int_mask = NRF_QDEC_INT_ACCOF_MASK;
if (p_config->reportper != NRF_QDEC_REPORTPER_DISABLED)
{
nrf_qdec_reportper_set(p_config->reportper);
int_mask |= NRF_QDEC_INT_REPORTRDY_MASK;
}
if (p_config->sample_inten)
{
int_mask |= NRF_QDEC_INT_SAMPLERDY_MASK;
}
nrf_qdec_int_enable(int_mask);
NRFX_IRQ_PRIORITY_SET(QDEC_IRQn, p_config->interrupt_priority);
NRFX_IRQ_ENABLE(QDEC_IRQn);
m_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_qdec_uninit(void)
{
NRFX_ASSERT(m_state != NRFX_DRV_STATE_UNINITIALIZED);
nrfx_qdec_disable();
NRFX_IRQ_DISABLE(QDEC_IRQn);
m_state = NRFX_DRV_STATE_UNINITIALIZED;
NRFX_LOG_INFO("Uninitialized.");
}
void nrfx_qdec_enable(void)
{
NRFX_ASSERT(m_state == NRFX_DRV_STATE_INITIALIZED);
nrf_qdec_enable();
nrf_qdec_task_trigger(NRF_QDEC_TASK_START);
m_state = NRFX_DRV_STATE_POWERED_ON;
NRFX_LOG_INFO("Enabled.");
}
void nrfx_qdec_disable(void)
{
NRFX_ASSERT(m_state == NRFX_DRV_STATE_POWERED_ON);
nrf_qdec_task_trigger(NRF_QDEC_TASK_STOP);
nrf_qdec_disable();
m_state = NRFX_DRV_STATE_INITIALIZED;
NRFX_LOG_INFO("Disabled.");
}
void nrfx_qdec_accumulators_read(int16_t * p_acc, int16_t * p_accdbl)
{
NRFX_ASSERT(m_state == NRFX_DRV_STATE_POWERED_ON);
nrf_qdec_task_trigger(NRF_QDEC_TASK_READCLRACC);
*p_acc = (int16_t)nrf_qdec_accread_get();
*p_accdbl = (int16_t)nrf_qdec_accdblread_get();
NRFX_LOG_DEBUG("Accumulators data, ACC register:");
NRFX_LOG_HEXDUMP_DEBUG((uint8_t *)p_acc, sizeof(p_acc[0]));
NRFX_LOG_DEBUG("Accumulators data, ACCDBL register:");
NRFX_LOG_HEXDUMP_DEBUG((uint8_t *)p_accdbl, sizeof(p_accdbl[0]));
}
#endif // NRFX_CHECK(NRFX_QDEC_ENABLED)

View File

@@ -0,0 +1,417 @@
/**
* Copyright (c) 2016 - 2020, 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_QSPI_ENABLED)
#include <nrfx_qspi.h>
/** @brief Command byte used to read status register. */
#define QSPI_STD_CMD_RDSR 0x05
/** @brief Byte used to mask status register and retrieve the write-in-progess bit. */
#define QSPI_MEM_STATUSREG_WIP_Pos 0x01
/** @brief Default time used in timeout function. */
#define QSPI_DEF_WAIT_TIME_US 10
/** @brief Default number of tries in timeout function. */
#define QSPI_DEF_WAIT_ATTEMPTS 100
/** @brief Control block - driver instance local data. */
typedef struct
{
nrfx_qspi_handler_t handler; /**< Handler. */
nrfx_drv_state_t state; /**< Driver state. */
volatile bool is_busy; /**< Flag indicating that an operation is currently being performed. */
void * p_context; /**< Driver context used in interrupt. */
} qspi_control_block_t;
static qspi_control_block_t m_cb;
static nrfx_err_t qspi_task_perform(nrf_qspi_task_t task)
{
// Wait for peripheral
if (m_cb.is_busy)
{
return NRFX_ERROR_BUSY;
}
nrf_qspi_event_clear(NRF_QSPI, NRF_QSPI_EVENT_READY);
if (m_cb.handler)
{
m_cb.is_busy = true;
nrf_qspi_int_enable(NRF_QSPI, NRF_QSPI_INT_READY_MASK);
}
nrf_qspi_task_trigger(NRF_QSPI, task);
if (m_cb.handler == NULL)
{
while (!nrf_qspi_event_check(NRF_QSPI, NRF_QSPI_EVENT_READY))
{};
}
return NRFX_SUCCESS;
}
static bool qspi_pins_configure(nrf_qspi_pins_t const * p_config)
{
// Check if the user set meaningful values to struct fields. If not, return false.
if ((p_config->sck_pin == NRF_QSPI_PIN_NOT_CONNECTED) ||
(p_config->csn_pin == NRF_QSPI_PIN_NOT_CONNECTED) ||
(p_config->io0_pin == NRF_QSPI_PIN_NOT_CONNECTED) ||
(p_config->io1_pin == NRF_QSPI_PIN_NOT_CONNECTED))
{
return false;
}
nrf_qspi_pins_set(NRF_QSPI, p_config);
return true;
}
static nrfx_err_t qspi_ready_wait(void)
{
bool result;
NRFX_WAIT_FOR(nrf_qspi_event_check(NRF_QSPI, NRF_QSPI_EVENT_READY),
QSPI_DEF_WAIT_ATTEMPTS,
QSPI_DEF_WAIT_TIME_US,
result);
if (!result)
{
return NRFX_ERROR_TIMEOUT;
}
return NRFX_SUCCESS;
}
nrfx_err_t nrfx_qspi_init(nrfx_qspi_config_t const * p_config,
nrfx_qspi_handler_t handler,
void * p_context)
{
NRFX_ASSERT(p_config);
if (m_cb.state != NRFX_DRV_STATE_UNINITIALIZED)
{
return NRFX_ERROR_INVALID_STATE;
}
if (!qspi_pins_configure(&p_config->pins))
{
return NRFX_ERROR_INVALID_PARAM;
}
nrf_qspi_xip_offset_set(NRF_QSPI, p_config->xip_offset);
nrf_qspi_ifconfig0_set(NRF_QSPI, &p_config->prot_if);
nrf_qspi_ifconfig1_set(NRF_QSPI, &p_config->phy_if);
m_cb.is_busy = false;
m_cb.handler = handler;
m_cb.p_context = p_context;
/* QSPI interrupt is disabled because the device should be enabled in polling mode (wait for activate
task event ready)*/
nrf_qspi_int_disable(NRF_QSPI, NRF_QSPI_INT_READY_MASK);
if (handler)
{
NRFX_IRQ_PRIORITY_SET(QSPI_IRQn, p_config->irq_priority);
NRFX_IRQ_ENABLE(QSPI_IRQn);
}
m_cb.state = NRFX_DRV_STATE_INITIALIZED;
nrf_qspi_enable(NRF_QSPI);
nrf_qspi_event_clear(NRF_QSPI, NRF_QSPI_EVENT_READY);
nrf_qspi_task_trigger(NRF_QSPI, NRF_QSPI_TASK_ACTIVATE);
// Waiting for the peripheral to activate
return qspi_ready_wait();
}
nrfx_err_t nrfx_qspi_cinstr_xfer(nrf_qspi_cinstr_conf_t const * p_config,
void const * p_tx_buffer,
void * p_rx_buffer)
{
NRFX_ASSERT(m_cb.state != NRFX_DRV_STATE_UNINITIALIZED);
if (m_cb.is_busy)
{
return NRFX_ERROR_BUSY;
}
nrf_qspi_event_clear(NRF_QSPI, NRF_QSPI_EVENT_READY);
/* In some cases, only opcode should be sent. To prevent execution, set function code is
* surrounded by an if.
*/
if (p_tx_buffer)
{
nrf_qspi_cinstrdata_set(NRF_QSPI, p_config->length, p_tx_buffer);
}
nrf_qspi_int_disable(NRF_QSPI, NRF_QSPI_INT_READY_MASK);
nrf_qspi_cinstr_transfer_start(NRF_QSPI, p_config);
if (qspi_ready_wait() == NRFX_ERROR_TIMEOUT)
{
// This timeout should never occur when WIPWAIT is not active, since in this
// case the QSPI peripheral should send the command immediately, without any
// waiting for previous write to complete.
NRFX_ASSERT(p_config->wipwait);
return NRFX_ERROR_TIMEOUT;
}
nrf_qspi_event_clear(NRF_QSPI, NRF_QSPI_EVENT_READY);
if (p_rx_buffer)
{
nrf_qspi_cinstrdata_get(NRF_QSPI, p_config->length, p_rx_buffer);
}
return NRFX_SUCCESS;
}
nrfx_err_t nrfx_qspi_cinstr_quick_send(uint8_t opcode,
nrf_qspi_cinstr_len_t length,
void const * p_tx_buffer)
{
nrf_qspi_cinstr_conf_t config = NRFX_QSPI_DEFAULT_CINSTR(opcode, length);
return nrfx_qspi_cinstr_xfer(&config, p_tx_buffer, NULL);
}
nrfx_err_t nrfx_qspi_lfm_start(nrf_qspi_cinstr_conf_t const * p_config)
{
NRFX_ASSERT(m_cb.state != NRFX_DRV_STATE_UNINITIALIZED);
NRFX_ASSERT(!(nrf_qspi_cinstr_long_transfer_is_ongoing(NRF_QSPI)));
NRFX_ASSERT(p_config->length == NRF_QSPI_CINSTR_LEN_1B);
if (m_cb.is_busy)
{
return NRFX_ERROR_BUSY;
}
nrf_qspi_cinstr_long_transfer_start(NRF_QSPI, p_config);
if (qspi_ready_wait() == NRFX_ERROR_TIMEOUT)
{
/* In case of error, abort long frame mode */
nrf_qspi_cinstr_long_transfer_continue(NRF_QSPI, NRF_QSPI_CINSTR_LEN_1B, true);
return NRFX_ERROR_TIMEOUT;
}
m_cb.is_busy = true;
return NRFX_SUCCESS;
}
nrfx_err_t nrfx_qspi_lfm_xfer(void const * p_tx_buffer,
void * p_rx_buffer,
size_t transfer_length,
bool finalize)
{
NRFX_ASSERT(m_cb.state != NRFX_DRV_STATE_UNINITIALIZED);
NRFX_ASSERT(nrf_qspi_cinstr_long_transfer_is_ongoing(NRF_QSPI));
nrfx_err_t status = NRFX_SUCCESS;
/* Perform transfers in packets of 8 bytes. Last transfer may be shorter. */
nrf_qspi_cinstr_len_t length = NRF_QSPI_CINSTR_LEN_9B;
for (uint32_t curr_byte = 0; curr_byte < transfer_length; curr_byte += 8)
{
uint32_t remaining_bytes = transfer_length - curr_byte;
if (remaining_bytes < 8)
{
length = (nrf_qspi_cinstr_len_t)(remaining_bytes + 1);
}
if (p_tx_buffer)
{
nrf_qspi_cinstrdata_set(NRF_QSPI,
length,
&((uint8_t const *)p_tx_buffer)[curr_byte]);
}
nrf_qspi_event_clear(NRF_QSPI, NRF_QSPI_EVENT_READY);
if (remaining_bytes <= 8)
{
nrf_qspi_cinstr_long_transfer_continue(NRF_QSPI, length, finalize);
}
else
{
nrf_qspi_cinstr_long_transfer_continue(NRF_QSPI, length, false);
}
if (qspi_ready_wait() == NRFX_ERROR_TIMEOUT)
{
/* In case of error, abort long frame mode */
nrf_qspi_cinstr_long_transfer_continue(NRF_QSPI, NRF_QSPI_CINSTR_LEN_1B, true);
status = NRFX_ERROR_TIMEOUT;
break;
}
if (p_rx_buffer)
{
nrf_qspi_cinstrdata_get(NRF_QSPI,
length,
&((uint8_t *)p_rx_buffer)[curr_byte]);
}
}
nrf_qspi_event_clear(NRF_QSPI, NRF_QSPI_EVENT_READY);
if ((finalize) || (status == NRFX_ERROR_TIMEOUT))
{
m_cb.is_busy = false;
}
return status;
}
nrfx_err_t nrfx_qspi_mem_busy_check(void)
{
nrfx_err_t ret_code;
uint8_t status_value = 0;
nrf_qspi_cinstr_conf_t const config =
NRFX_QSPI_DEFAULT_CINSTR(QSPI_STD_CMD_RDSR,
NRF_QSPI_CINSTR_LEN_2B);
ret_code = nrfx_qspi_cinstr_xfer(&config, &status_value, &status_value);
if (ret_code != NRFX_SUCCESS)
{
return ret_code;
}
if ((status_value & QSPI_MEM_STATUSREG_WIP_Pos) != 0x00)
{
return NRFX_ERROR_BUSY;
}
return NRFX_SUCCESS;
}
void nrfx_qspi_uninit(void)
{
NRFX_ASSERT(m_cb.state != NRFX_DRV_STATE_UNINITIALIZED);
if (nrf_qspi_cinstr_long_transfer_is_ongoing(NRF_QSPI))
{
nrf_qspi_cinstr_long_transfer_continue(NRF_QSPI, NRF_QSPI_CINSTR_LEN_1B, true);
}
nrf_qspi_int_disable(NRF_QSPI, NRF_QSPI_INT_READY_MASK);
nrf_qspi_task_trigger(NRF_QSPI, NRF_QSPI_TASK_DEACTIVATE);
nrf_qspi_disable(NRF_QSPI);
NRFX_IRQ_DISABLE(QSPI_IRQn);
nrf_qspi_event_clear(NRF_QSPI, NRF_QSPI_EVENT_READY);
m_cb.state = NRFX_DRV_STATE_UNINITIALIZED;
}
nrfx_err_t nrfx_qspi_write(void const * p_tx_buffer,
size_t tx_buffer_length,
uint32_t dst_address)
{
NRFX_ASSERT(m_cb.state != NRFX_DRV_STATE_UNINITIALIZED);
NRFX_ASSERT(p_tx_buffer != NULL);
if (!nrfx_is_in_ram(p_tx_buffer) || !nrfx_is_word_aligned(p_tx_buffer))
{
return NRFX_ERROR_INVALID_ADDR;
}
nrf_qspi_write_buffer_set(NRF_QSPI, p_tx_buffer, tx_buffer_length, dst_address);
return qspi_task_perform(NRF_QSPI_TASK_WRITESTART);
}
nrfx_err_t nrfx_qspi_read(void * p_rx_buffer,
size_t rx_buffer_length,
uint32_t src_address)
{
NRFX_ASSERT(m_cb.state != NRFX_DRV_STATE_UNINITIALIZED);
NRFX_ASSERT(p_rx_buffer != NULL);
if (!nrfx_is_in_ram(p_rx_buffer) || !nrfx_is_word_aligned(p_rx_buffer))
{
return NRFX_ERROR_INVALID_ADDR;
}
nrf_qspi_read_buffer_set(NRF_QSPI, p_rx_buffer, rx_buffer_length, src_address);
return qspi_task_perform(NRF_QSPI_TASK_READSTART);
}
nrfx_err_t nrfx_qspi_erase(nrf_qspi_erase_len_t length,
uint32_t start_address)
{
NRFX_ASSERT(m_cb.state != NRFX_DRV_STATE_UNINITIALIZED);
if (!nrfx_is_word_aligned((void const *)start_address))
{
return NRFX_ERROR_INVALID_ADDR;
}
nrf_qspi_erase_ptr_set(NRF_QSPI, start_address, length);
return qspi_task_perform(NRF_QSPI_TASK_ERASESTART);
}
nrfx_err_t nrfx_qspi_chip_erase(void)
{
return nrfx_qspi_erase(NRF_QSPI_ERASE_LEN_ALL, 0);
}
void nrfx_qspi_irq_handler(void)
{
// Catch Event ready interrupts
if (nrf_qspi_event_check(NRF_QSPI, NRF_QSPI_EVENT_READY))
{
m_cb.is_busy = false;
nrf_qspi_event_clear(NRF_QSPI, NRF_QSPI_EVENT_READY);
m_cb.handler(NRFX_QSPI_EVENT_DONE, m_cb.p_context);
}
}
#endif // NRFX_CHECK(NRFX_QSPI_ENABLED)

View File

@@ -0,0 +1,122 @@
/**
* Copyright (c) 2016 - 2020, 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)

View File

@@ -0,0 +1,348 @@
/**
* Copyright (c) 2014 - 2020, 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

View File

@@ -0,0 +1,436 @@
/**
* Copyright (c) 2015 - 2020, 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)

View File

@@ -0,0 +1,697 @@
/**
* Copyright (c) 2015 - 2020, 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)

View File

@@ -0,0 +1,513 @@
/**
* Copyright (c) 2013 - 2020, 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_SPIS_ENABLED)
#if !(NRFX_CHECK(NRFX_SPIS0_ENABLED) || \
NRFX_CHECK(NRFX_SPIS1_ENABLED) || \
NRFX_CHECK(NRFX_SPIS2_ENABLED) || \
NRFX_CHECK(NRFX_SPIS3_ENABLED))
#error "No enabled SPIS instances. Check <nrfx_config.h>."
#endif
#include <nrfx_spis.h>
#include "prs/nrfx_prs.h"
#define NRFX_LOG_MODULE SPIS
#include <nrfx_log.h>
#define EVT_TO_STR(event) \
(event == NRF_SPIS_EVENT_ACQUIRED ? "NRF_SPIS_EVENT_ACQUIRED" : \
(event == NRF_SPIS_EVENT_END ? "NRF_SPIS_EVENT_END" : \
"UNKNOWN ERROR"))
#define SPISX_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_SPIS0_ENABLED)
#define SPIS0_LENGTH_VALIDATE(...) SPISX_LENGTH_VALIDATE(SPIS0, __VA_ARGS__)
#else
#define SPIS0_LENGTH_VALIDATE(...) 0
#endif
#if NRFX_CHECK(NRFX_SPIS1_ENABLED)
#define SPIS1_LENGTH_VALIDATE(...) SPISX_LENGTH_VALIDATE(SPIS1, __VA_ARGS__)
#else
#define SPIS1_LENGTH_VALIDATE(...) 0
#endif
#if NRFX_CHECK(NRFX_SPIS2_ENABLED)
#define SPIS2_LENGTH_VALIDATE(...) SPISX_LENGTH_VALIDATE(SPIS2, __VA_ARGS__)
#else
#define SPIS2_LENGTH_VALIDATE(...) 0
#endif
#if NRFX_CHECK(NRFX_SPIS3_ENABLED)
#define SPIS3_LENGTH_VALIDATE(...) SPISX_LENGTH_VALIDATE(SPIS3, __VA_ARGS__)
#else
#define SPIS3_LENGTH_VALIDATE(...) 0
#endif
#define SPIS_LENGTH_VALIDATE(drv_inst_idx, rx_len, tx_len) \
(SPIS0_LENGTH_VALIDATE(drv_inst_idx, rx_len, tx_len) || \
SPIS1_LENGTH_VALIDATE(drv_inst_idx, rx_len, tx_len) || \
SPIS2_LENGTH_VALIDATE(drv_inst_idx, rx_len, tx_len) || \
SPIS3_LENGTH_VALIDATE(drv_inst_idx, rx_len, tx_len))
#if NRFX_CHECK(NRFX_SPIS_NRF52_ANOMALY_109_WORKAROUND_ENABLED)
#include <nrfx_gpiote.h>
#define USE_DMA_ISSUE_WORKAROUND
// This handler is called by the GPIOTE driver when a falling edge is detected
// on the CSN line. There is no need to do anything here. The handling of the
// interrupt itself provides a protection for DMA transfers.
static void csn_event_handler(nrfx_gpiote_pin_t pin,
nrf_gpiote_polarity_t action)
{
}
#endif
/**@brief States of the SPI transaction state machine. */
typedef enum
{
SPIS_STATE_INIT, /**< Initialization state. In this state the module waits for a call to @ref spi_slave_buffers_set. */
SPIS_BUFFER_RESOURCE_REQUESTED, /**< State where the configuration of the memory buffers, which are to be used in SPI transaction, has started. */
SPIS_BUFFER_RESOURCE_CONFIGURED, /**< State where the configuration of the memory buffers, which are to be used in SPI transaction, has completed. */
SPIS_XFER_COMPLETED /**< State where SPI transaction has been completed. */
} nrfx_spis_state_t;
/**@brief SPIS control block - driver instance local data. */
typedef struct
{
volatile uint32_t tx_buffer_size; //!< SPI slave TX buffer size in bytes.
volatile uint32_t rx_buffer_size; //!< SPI slave RX buffer size in bytes.
nrfx_spis_event_handler_t handler; //!< SPI event handler.
volatile const uint8_t * tx_buffer; //!< SPI slave TX buffer.
volatile uint8_t * rx_buffer; //!< SPI slave RX buffer.
nrfx_drv_state_t state; //!< driver initialization state.
volatile nrfx_spis_state_t spi_state; //!< SPI slave state.
void * p_context; //!< Context set on initialization.
} spis_cb_t;
static spis_cb_t m_cb[NRFX_SPIS_ENABLED_COUNT];
nrfx_err_t nrfx_spis_init(nrfx_spis_t const * const p_instance,
nrfx_spis_config_t const * p_config,
nrfx_spis_event_handler_t event_handler,
void * p_context)
{
NRFX_ASSERT(p_config);
NRFX_ASSERT(event_handler);
spis_cb_t * p_cb = &m_cb[p_instance->drv_inst_idx];
nrfx_err_t err_code;
NRF_SPIS_Type * p_spis = p_instance->p_reg;
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 ((uint32_t)p_config->mode > (uint32_t)NRF_SPIS_MODE_3)
{
err_code = NRFX_ERROR_INVALID_PARAM;
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_SPIS_ENABLED_COUNT] = {
#if NRFX_CHECK(NRFX_SPIS0_ENABLED)
nrfx_spis_0_irq_handler,
#endif
#if NRFX_CHECK(NRFX_SPIS1_ENABLED)
nrfx_spis_1_irq_handler,
#endif
#if NRFX_CHECK(NRFX_SPIS2_ENABLED)
nrfx_spis_2_irq_handler,
#endif
#if NRFX_CHECK(NRFX_SPIS3_ENABLED)
nrfx_spis_3_irq_handler,
#endif
};
if (nrfx_prs_acquire(p_spis,
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)
// Configure the SPI pins for input.
uint32_t mosi_pin;
uint32_t miso_pin;
if (p_config->miso_pin != NRFX_SPIS_PIN_NOT_USED)
{
nrf_gpio_cfg(p_config->miso_pin,
NRF_GPIO_PIN_DIR_INPUT,
NRF_GPIO_PIN_INPUT_CONNECT,
NRF_GPIO_PIN_NOPULL,
p_config->miso_drive,
NRF_GPIO_PIN_NOSENSE);
miso_pin = p_config->miso_pin;
}
else
{
miso_pin = NRF_SPIS_PIN_NOT_CONNECTED;
}
if (p_config->mosi_pin != NRFX_SPIS_PIN_NOT_USED)
{
nrf_gpio_cfg(p_config->mosi_pin,
NRF_GPIO_PIN_DIR_INPUT,
NRF_GPIO_PIN_INPUT_CONNECT,
NRF_GPIO_PIN_NOPULL,
NRF_GPIO_PIN_S0S1,
NRF_GPIO_PIN_NOSENSE);
mosi_pin = p_config->mosi_pin;
}
else
{
mosi_pin = NRF_SPIS_PIN_NOT_CONNECTED;
}
nrf_gpio_cfg(p_config->csn_pin,
NRF_GPIO_PIN_DIR_INPUT,
NRF_GPIO_PIN_INPUT_CONNECT,
p_config->csn_pullup,
NRF_GPIO_PIN_S0S1,
NRF_GPIO_PIN_NOSENSE);
nrf_gpio_cfg(p_config->sck_pin,
NRF_GPIO_PIN_DIR_INPUT,
NRF_GPIO_PIN_INPUT_CONNECT,
NRF_GPIO_PIN_NOPULL,
NRF_GPIO_PIN_S0S1,
NRF_GPIO_PIN_NOSENSE);
nrf_spis_pins_set(p_spis, p_config->sck_pin, mosi_pin, miso_pin, p_config->csn_pin);
nrf_spis_rx_buffer_set(p_spis, NULL, 0);
nrf_spis_tx_buffer_set(p_spis, NULL, 0);
// Configure SPI mode.
nrf_spis_configure(p_spis, p_config->mode, p_config->bit_order);
// Configure DEF and ORC characters.
nrf_spis_def_set(p_spis, p_config->def);
nrf_spis_orc_set(p_spis, p_config->orc);
// Clear possible pending events.
nrf_spis_event_clear(p_spis, NRF_SPIS_EVENT_END);
nrf_spis_event_clear(p_spis, NRF_SPIS_EVENT_ACQUIRED);
// Enable END_ACQUIRE shortcut.
nrf_spis_shorts_enable(p_spis, NRF_SPIS_SHORT_END_ACQUIRE);
p_cb->spi_state = SPIS_STATE_INIT;
p_cb->handler = event_handler;
p_cb->p_context = p_context;
#if defined(USE_DMA_ISSUE_WORKAROUND)
// Configure a GPIOTE channel to generate interrupts on each falling edge
// on the CSN line. Handling of these interrupts will make the CPU active,
// and thus will protect the DMA transfers started by SPIS right after it
// is selected for communication.
// [the GPIOTE driver may be already initialized at this point (by this
// driver when another SPIS instance is used, or by an application code),
// so just ignore the returned value]
(void)nrfx_gpiote_init();
static nrfx_gpiote_in_config_t const csn_gpiote_config =
NRFX_GPIOTE_CONFIG_IN_SENSE_HITOLO(true);
nrfx_err_t gpiote_err_code = nrfx_gpiote_in_init(p_config->csn_pin,
&csn_gpiote_config, csn_event_handler);
if (gpiote_err_code != NRFX_SUCCESS)
{
err_code = NRFX_ERROR_INTERNAL;
NRFX_LOG_INFO("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
nrfx_gpiote_in_event_enable(p_config->csn_pin, true);
#endif
// Enable IRQ.
nrf_spis_int_enable(p_spis, NRF_SPIS_INT_ACQUIRED_MASK |
NRF_SPIS_INT_END_MASK);
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->state = NRFX_DRV_STATE_INITIALIZED;
// Enable SPI slave device.
nrf_spis_enable(p_spis);
NRFX_LOG_INFO("Initialized.");
return NRFX_SUCCESS;
}
void nrfx_spis_uninit(nrfx_spis_t const * const p_instance)
{
spis_cb_t * p_cb = &m_cb[p_instance->drv_inst_idx];
NRFX_ASSERT(p_cb->state != NRFX_DRV_STATE_UNINITIALIZED);
NRF_SPIS_Type * p_spis = p_instance->p_reg;
#define DISABLE_ALL 0xFFFFFFFF
nrf_spis_disable(p_spis);
NRFX_IRQ_DISABLE(nrfx_get_irq_number(p_instance->p_reg));
nrf_spis_int_disable(p_spis, DISABLE_ALL);
#undef DISABLE_ALL
#if NRFX_CHECK(NRFX_PRS_ENABLED)
nrfx_prs_release(p_spis);
#endif
p_cb->state = NRFX_DRV_STATE_UNINITIALIZED;
NRFX_LOG_INFO("Uninitialized.");
}
/**@brief Function for executing the state entry action. */
static void spis_state_entry_action_execute(NRF_SPIS_Type * p_spis,
spis_cb_t * p_cb)
{
nrfx_spis_evt_t event;
switch (p_cb->spi_state)
{
case SPIS_BUFFER_RESOURCE_REQUESTED:
nrf_spis_task_trigger(p_spis, NRF_SPIS_TASK_ACQUIRE);
break;
case SPIS_BUFFER_RESOURCE_CONFIGURED:
event.evt_type = NRFX_SPIS_BUFFERS_SET_DONE;
event.rx_amount = 0;
event.tx_amount = 0;
NRFX_ASSERT(p_cb->handler != NULL);
p_cb->handler(&event, p_cb->p_context);
break;
case SPIS_XFER_COMPLETED:
event.evt_type = NRFX_SPIS_XFER_DONE;
event.rx_amount = nrf_spis_rx_amount_get(p_spis);
event.tx_amount = nrf_spis_tx_amount_get(p_spis);
NRFX_LOG_INFO("Transfer rx_len:%d.", event.rx_amount);
NRFX_LOG_DEBUG("Rx data:");
NRFX_LOG_HEXDUMP_DEBUG((uint8_t const *)p_cb->rx_buffer,
event.rx_amount * sizeof(p_cb->rx_buffer[0]));
NRFX_ASSERT(p_cb->handler != NULL);
p_cb->handler(&event, p_cb->p_context);
break;
default:
// No implementation required.
break;
}
}
/**@brief Function for changing the state of the SPI state machine.
*
* @param[in] p_spis SPIS instance register.
* @param[in] p_cb SPIS instance control block.
* @param[in] new_state State where the state machine transits to.
*/
static void spis_state_change(NRF_SPIS_Type * p_spis,
spis_cb_t * p_cb,
nrfx_spis_state_t new_state)
{
p_cb->spi_state = new_state;
spis_state_entry_action_execute(p_spis, p_cb);
}
nrfx_err_t nrfx_spis_buffers_set(nrfx_spis_t const * const p_instance,
uint8_t const * p_tx_buffer,
size_t tx_buffer_length,
uint8_t * p_rx_buffer,
size_t rx_buffer_length)
{
NRFX_ASSERT(p_tx_buffer != NULL || tx_buffer_length == 0);
NRFX_ASSERT(p_rx_buffer != NULL || rx_buffer_length == 0);
spis_cb_t * p_cb = &m_cb[p_instance->drv_inst_idx];
nrfx_err_t err_code;
if (!SPIS_LENGTH_VALIDATE(p_instance->drv_inst_idx,
rx_buffer_length,
tx_buffer_length))
{
return NRFX_ERROR_INVALID_LENGTH;
}
// EasyDMA requires that transfer buffers are placed in Data RAM region;
// signal error if they are not.
if ((p_tx_buffer != NULL && !nrfx_is_in_ram(p_tx_buffer)) ||
(p_rx_buffer != NULL && !nrfx_is_in_ram(p_rx_buffer)))
{
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;
}
switch (p_cb->spi_state)
{
case SPIS_STATE_INIT:
case SPIS_XFER_COMPLETED:
case SPIS_BUFFER_RESOURCE_CONFIGURED:
p_cb->tx_buffer = p_tx_buffer;
p_cb->rx_buffer = p_rx_buffer;
p_cb->tx_buffer_size = tx_buffer_length;
p_cb->rx_buffer_size = rx_buffer_length;
err_code = NRFX_SUCCESS;
spis_state_change(p_instance->p_reg, p_cb, SPIS_BUFFER_RESOURCE_REQUESTED);
break;
case SPIS_BUFFER_RESOURCE_REQUESTED:
err_code = NRFX_ERROR_INVALID_STATE;
break;
default:
// @note: execution of this code path would imply internal error in the design.
err_code = NRFX_ERROR_INTERNAL;
break;
}
NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
static void spis_irq_handler(NRF_SPIS_Type * p_spis, spis_cb_t * p_cb)
{
// @note: as multiple events can be pending for processing, the correct event processing order
// is as follows:
// - SPI semaphore acquired event.
// - SPI transaction complete event.
// Check for SPI semaphore acquired event.
if (nrf_spis_event_check(p_spis, NRF_SPIS_EVENT_ACQUIRED))
{
nrf_spis_event_clear(p_spis, NRF_SPIS_EVENT_ACQUIRED);
NRFX_LOG_DEBUG("SPIS: Event: %s.", EVT_TO_STR(NRF_SPIS_EVENT_ACQUIRED));
switch (p_cb->spi_state)
{
case SPIS_BUFFER_RESOURCE_REQUESTED:
nrf_spis_tx_buffer_set(p_spis, (uint8_t *)p_cb->tx_buffer, p_cb->tx_buffer_size);
nrf_spis_rx_buffer_set(p_spis, (uint8_t *)p_cb->rx_buffer, p_cb->rx_buffer_size);
nrf_spis_task_trigger(p_spis, NRF_SPIS_TASK_RELEASE);
spis_state_change(p_spis, p_cb, SPIS_BUFFER_RESOURCE_CONFIGURED);
break;
default:
// No implementation required.
break;
}
}
// Check for SPI transaction complete event.
if (nrf_spis_event_check(p_spis, NRF_SPIS_EVENT_END))
{
nrf_spis_event_clear(p_spis, NRF_SPIS_EVENT_END);
NRFX_LOG_DEBUG("SPIS: Event: %s.", EVT_TO_STR(NRF_SPIS_EVENT_END));
switch (p_cb->spi_state)
{
case SPIS_BUFFER_RESOURCE_CONFIGURED:
spis_state_change(p_spis, p_cb, SPIS_XFER_COMPLETED);
break;
default:
// No implementation required.
break;
}
}
}
#if NRFX_CHECK(NRFX_SPIS0_ENABLED)
void nrfx_spis_0_irq_handler(void)
{
spis_irq_handler(NRF_SPIS0, &m_cb[NRFX_SPIS0_INST_IDX]);
}
#endif
#if NRFX_CHECK(NRFX_SPIS1_ENABLED)
void nrfx_spis_1_irq_handler(void)
{
spis_irq_handler(NRF_SPIS1, &m_cb[NRFX_SPIS1_INST_IDX]);
}
#endif
#if NRFX_CHECK(NRFX_SPIS2_ENABLED)
void nrfx_spis_2_irq_handler(void)
{
spis_irq_handler(NRF_SPIS2, &m_cb[NRFX_SPIS2_INST_IDX]);
}
#endif
#if NRFX_CHECK(NRFX_SPIS3_ENABLED)
void nrfx_spis_3_irq_handler(void)
{
spis_irq_handler(NRF_SPIS3, &m_cb[NRFX_SPIS3_INST_IDX]);
}
#endif
#endif // NRFX_CHECK(NRFX_SPIS_ENABLED)

View File

@@ -0,0 +1,439 @@
/**
* Copyright (c) 2015 - 2020, 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_SWI_ENABLED)
#include <nrfx_swi.h>
#define NRFX_LOG_MODULE SWI
#include <nrfx_log.h>
// NRFX_SWI_RESERVED_MASK - SWIs reserved for use by external modules.
#if NRFX_CHECK(NRFX_PWM_NRF52_ANOMALY_109_WORKAROUND_ENABLED)
#define NRFX_SWI_RESERVED_MASK ((NRFX_SWI_USED) | \
(1u << NRFX_PWM_NRF52_ANOMALY_109_EGU_INSTANCE))
#else
#define NRFX_SWI_RESERVED_MASK (NRFX_SWI_USED)
#endif
// NRFX_SWI_DISABLED_MASK - SWIs excluded from use in <nrfx_config.h>.
#if NRFX_CHECK(NRFX_SWI0_DISABLED)
#define NRFX_SWI0_DISABLED_MASK (1u << 0)
#else
#define NRFX_SWI0_DISABLED_MASK 0u
#endif
#if NRFX_CHECK(NRFX_SWI1_DISABLED)
#define NRFX_SWI1_DISABLED_MASK (1u << 1)
#else
#define NRFX_SWI1_DISABLED_MASK 0u
#endif
#if NRFX_CHECK(NRFX_SWI2_DISABLED)
#define NRFX_SWI2_DISABLED_MASK (1u << 2)
#else
#define NRFX_SWI2_DISABLED_MASK 0u
#endif
#if NRFX_CHECK(NRFX_SWI3_DISABLED)
#define NRFX_SWI3_DISABLED_MASK (1u << 3)
#else
#define NRFX_SWI3_DISABLED_MASK 0u
#endif
#if NRFX_CHECK(NRFX_SWI4_DISABLED)
#define NRFX_SWI4_DISABLED_MASK (1u << 4)
#else
#define NRFX_SWI4_DISABLED_MASK 0u
#endif
#if NRFX_CHECK(NRFX_SWI5_DISABLED)
#define NRFX_SWI5_DISABLED_MASK (1u << 5)
#else
#define NRFX_SWI5_DISABLED_MASK 0u
#endif
#define NRFX_SWI_DISABLED_MASK (NRFX_SWI0_DISABLED_MASK | \
NRFX_SWI1_DISABLED_MASK | \
NRFX_SWI2_DISABLED_MASK | \
NRFX_SWI3_DISABLED_MASK | \
NRFX_SWI4_DISABLED_MASK | \
NRFX_SWI5_DISABLED_MASK)
#if (NRFX_SWI_RESERVED_MASK & NRFX_SWI_DISABLED_MASK)
#error "A reserved SWI configured to be disabled. Check <nrfx_config.h> and NRFX_SWI_USED."
#endif
// NRFX_SWI_AVAILABLE_MASK - SWIs available for this module, i.e. present
// in the hardware and neither reserved by external modules nor disabled
// in <nrfx_config.h>.
#define NRFX_SWI_PRESENT_MASK ((1u << (SWI_COUNT)) - 1u)
#define NRFX_SWI_AVAILABLE_MASK (NRFX_SWI_PRESENT_MASK & \
~(NRFX_SWI_RESERVED_MASK | \
NRFX_SWI_DISABLED_MASK))
#if (NRFX_SWI_AVAILABLE_MASK == 0)
#error "No available SWI instances. Check <nrfx_config.h> and NRFX_SWI_USED."
#endif
#define NRFX_SWI_IS_AVAILABLE(idx) ((NRFX_SWI_AVAILABLE_MASK >> (idx)) & 1u)
#define NRFX_SWI_FIRST (NRFX_SWI_IS_AVAILABLE(0) ? 0u : \
(NRFX_SWI_IS_AVAILABLE(1) ? 1u : \
(NRFX_SWI_IS_AVAILABLE(2) ? 2u : \
(NRFX_SWI_IS_AVAILABLE(3) ? 3u : \
(NRFX_SWI_IS_AVAILABLE(4) ? 4u : \
5u)))))
#define NRFX_SWI_LAST (NRFX_SWI_IS_AVAILABLE(5) ? 5u : \
(NRFX_SWI_IS_AVAILABLE(4) ? 4u : \
(NRFX_SWI_IS_AVAILABLE(3) ? 3u : \
(NRFX_SWI_IS_AVAILABLE(2) ? 2u : \
(NRFX_SWI_IS_AVAILABLE(1) ? 1u : \
0u)))))
// NRFX_SWI_EGU_COUNT - number of EGU instances to be used by this module
// (note - if EGU is not present, EGU_COUNT is not defined).
#if NRFX_CHECK(NRFX_EGU_ENABLED)
#define NRFX_SWI_EGU_COUNT EGU_COUNT
#else
#define NRFX_SWI_EGU_COUNT 0
#endif
// These flags are needed only for SWIs that have no corresponding EGU unit
// (in EGU such flags are available in hardware).
#if (NRFX_SWI_EGU_COUNT < SWI_COUNT)
static nrfx_swi_flags_t m_swi_flags[SWI_COUNT - NRFX_SWI_EGU_COUNT];
#endif
static nrfx_swi_handler_t m_swi_handlers[SWI_COUNT];
static uint8_t m_swi_allocated_mask;
static void swi_mark_allocated(nrfx_swi_t swi)
{
m_swi_allocated_mask |= (1u << swi);
}
static void swi_mark_unallocated(nrfx_swi_t swi)
{
m_swi_allocated_mask &= ~(1u << swi);
}
static bool swi_is_allocated(nrfx_swi_t swi)
{
return (m_swi_allocated_mask & (1u << swi));
}
static bool swi_is_available(nrfx_swi_t swi)
{
return NRFX_SWI_IS_AVAILABLE(swi);
}
static IRQn_Type swi_irq_number_get(nrfx_swi_t swi)
{
#if defined(NRF_SWI)
return (IRQn_Type)(nrfx_get_irq_number(NRF_SWI) + swi);
#elif defined(NRF_SWI0)
return (IRQn_Type)(nrfx_get_irq_number(NRF_SWI0) + swi);
#else
return (IRQn_Type)(nrfx_get_irq_number(NRF_EGU0) + swi);
#endif
}
static void swi_int_enable(nrfx_swi_t swi)
{
#if NRFX_SWI_EGU_COUNT
if (swi < NRFX_SWI_EGU_COUNT)
{
NRF_EGU_Type * p_egu = nrfx_swi_egu_instance_get(swi);
NRFX_ASSERT(p_egu != NULL);
nrf_egu_int_enable(p_egu, NRF_EGU_INT_ALL);
if (m_swi_handlers[swi] == NULL)
{
return;
}
}
#endif
NRFX_IRQ_ENABLE(swi_irq_number_get(swi));
}
static void swi_int_disable(nrfx_swi_t swi)
{
NRFX_IRQ_DISABLE(swi_irq_number_get(swi));
#if NRFX_SWI_EGU_COUNT
if (swi < NRFX_SWI_EGU_COUNT)
{
nrf_egu_int_disable(nrfx_swi_egu_instance_get(swi), NRF_EGU_INT_ALL);
}
#endif
}
static void swi_handler_setup(nrfx_swi_t swi,
nrfx_swi_handler_t event_handler,
uint32_t irq_priority)
{
m_swi_handlers[swi] = event_handler;
NRFX_IRQ_PRIORITY_SET(swi_irq_number_get(swi), irq_priority);
swi_int_enable(swi);
}
static void swi_deallocate(nrfx_swi_t swi)
{
swi_int_disable(swi);
m_swi_handlers[swi] = NULL;
swi_mark_unallocated(swi);
}
nrfx_err_t nrfx_swi_alloc(nrfx_swi_t * p_swi,
nrfx_swi_handler_t event_handler,
uint32_t irq_priority)
{
NRFX_ASSERT(p_swi != NULL);
nrfx_err_t err_code;
for (nrfx_swi_t swi = NRFX_SWI_FIRST; swi <= NRFX_SWI_LAST; ++swi)
{
if (swi_is_available(swi))
{
bool allocated = false;
NRFX_CRITICAL_SECTION_ENTER();
if (!swi_is_allocated(swi))
{
swi_mark_allocated(swi);
allocated = true;
}
NRFX_CRITICAL_SECTION_EXIT();
if (allocated)
{
swi_handler_setup(swi, event_handler, irq_priority);
*p_swi = swi;
NRFX_LOG_INFO("SWI channel allocated: %d.", (*p_swi));
return NRFX_SUCCESS;
}
}
}
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;
}
bool nrfx_swi_is_allocated(nrfx_swi_t swi)
{
return swi_is_allocated(swi);
}
void nrfx_swi_int_disable(nrfx_swi_t swi)
{
NRFX_ASSERT(swi_is_allocated(swi));
swi_int_disable(swi);
}
void nrfx_swi_int_enable(nrfx_swi_t swi)
{
NRFX_ASSERT(swi_is_allocated(swi));
swi_int_enable(swi);
}
void nrfx_swi_free(nrfx_swi_t * p_swi)
{
NRFX_ASSERT(p_swi != NULL);
nrfx_swi_t swi = *p_swi;
NRFX_ASSERT(swi_is_allocated(swi));
swi_deallocate(swi);
*p_swi = NRFX_SWI_UNALLOCATED;
}
void nrfx_swi_all_free(void)
{
for (nrfx_swi_t swi = NRFX_SWI_FIRST; swi <= NRFX_SWI_LAST; ++swi)
{
if (swi_is_allocated(swi))
{
swi_deallocate(swi);
}
}
}
void nrfx_swi_trigger(nrfx_swi_t swi, uint8_t flag_number)
{
NRFX_ASSERT(swi_is_allocated(swi));
#if NRFX_SWI_EGU_COUNT
NRF_EGU_Type * p_egu = nrfx_swi_egu_instance_get(swi);
#if (NRFX_SWI_EGU_COUNT < SWI_COUNT)
if (p_egu == NULL)
{
m_swi_flags[swi - NRFX_SWI_EGU_COUNT] |= (1 << flag_number);
NRFX_IRQ_PENDING_SET(swi_irq_number_get(swi));
}
else
#endif // (NRFX_SWI_EGU_COUNT < SWI_COUNT)
{
nrf_egu_task_trigger(p_egu,
nrf_egu_task_trigger_get(p_egu, flag_number));
}
#else // -> #if !NRFX_SWI_EGU_COUNT
m_swi_flags[swi - NRFX_SWI_EGU_COUNT] |= (1 << flag_number);
NRFX_IRQ_PENDING_SET(swi_irq_number_get(swi));
#endif
}
#if NRFX_SWI_EGU_COUNT
static void egu_irq_handler(nrfx_swi_t swi, uint8_t egu_channel_count)
{
#if (NRFX_SWI_FIRST > 0)
NRFX_ASSERT(swi >= NRFX_SWI_FIRST);
#endif
NRFX_ASSERT(swi <= NRFX_SWI_LAST);
nrfx_swi_handler_t handler = m_swi_handlers[swi];
NRFX_ASSERT(handler != NULL);
NRF_EGU_Type * p_egu = nrfx_swi_egu_instance_get(swi);
NRFX_ASSERT(p_egu != NULL);
nrfx_swi_flags_t flags = 0;
for (uint8_t i = 0; i < egu_channel_count; ++i)
{
nrf_egu_event_t egu_event = nrf_egu_event_triggered_get(p_egu, i);
if (nrf_egu_event_check(p_egu, egu_event))
{
flags |= (1u << i);
nrf_egu_event_clear(p_egu, egu_event);
}
}
handler(swi, flags);
}
#endif // NRFX_SWI_EGU_COUNT
#if (NRFX_SWI_EGU_COUNT < SWI_COUNT)
static void swi_irq_handler(nrfx_swi_t swi)
{
#if (NRFX_SWI_FIRST > 0)
NRFX_ASSERT(swi >= NRFX_SWI_FIRST);
#endif
NRFX_ASSERT(swi <= NRFX_SWI_LAST);
nrfx_swi_handler_t handler = m_swi_handlers[swi];
NRFX_ASSERT(handler != NULL);
nrfx_swi_flags_t flags = m_swi_flags[swi - NRFX_SWI_EGU_COUNT];
m_swi_flags[swi - NRFX_SWI_EGU_COUNT] &= ~flags;
handler(swi, flags);
}
#endif // (NRFX_SWI_EGU_COUNT < SWI_COUNT)
#if NRFX_SWI_IS_AVAILABLE(0)
void nrfx_swi_0_irq_handler(void)
{
#if (NRFX_SWI_EGU_COUNT > 0)
egu_irq_handler(0, EGU0_CH_NUM);
#else
swi_irq_handler(0);
#endif
}
#endif // NRFX_SWI_IS_AVAILABLE(0)
#if NRFX_SWI_IS_AVAILABLE(1)
void nrfx_swi_1_irq_handler(void)
{
#if (NRFX_SWI_EGU_COUNT > 1)
egu_irq_handler(1, EGU1_CH_NUM);
#else
swi_irq_handler(1);
#endif
}
#endif // NRFX_SWI_IS_AVAILABLE(1)
#if NRFX_SWI_IS_AVAILABLE(2)
void nrfx_swi_2_irq_handler(void)
{
#if (NRFX_SWI_EGU_COUNT > 2)
egu_irq_handler(2, EGU2_CH_NUM);
#else
swi_irq_handler(2);
#endif
}
#endif // NRFX_SWI_IS_AVAILABLE(2)
#if NRFX_SWI_IS_AVAILABLE(3)
void nrfx_swi_3_irq_handler(void)
{
#if (NRFX_SWI_EGU_COUNT > 3)
egu_irq_handler(3, EGU3_CH_NUM);
#else
swi_irq_handler(3);
#endif
}
#endif // NRFX_SWI_IS_AVAILABLE(3)
#if NRFX_SWI_IS_AVAILABLE(4)
void nrfx_swi_4_irq_handler(void)
{
#if (NRFX_SWI_EGU_COUNT > 4)
egu_irq_handler(4, EGU4_CH_NUM);
#else
swi_irq_handler(4);
#endif
}
#endif // NRFX_SWI_IS_AVAILABLE(4)
#if NRFX_SWI_IS_AVAILABLE(5)
void nrfx_swi_5_irq_handler(void)
{
#if (NRFX_SWI_EGU_COUNT > 5)
egu_irq_handler(5, EGU5_CH_NUM);
#else
swi_irq_handler(5);
#endif
}
#endif // NRFX_SWI_IS_AVAILABLE(5)
#endif // NRFX_CHECK(NRFX_SWI_ENABLED)

View File

@@ -0,0 +1,170 @@
/**
* Copyright (c) 2016 - 2020, 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_SYSTICK_ENABLED)
#include <nrfx_systick.h>
/**
* @brief Maximum number of ticks to delay
*
* The maximum number of ticks should be much lower than
* Physical maximum count of the SysTick timer.
* It is dictated by the fact that it would be impossible to detect delay
* properly when the timer value warps around the starting point.
*/
#define NRFX_SYSTICK_TICKS_MAX (NRF_SYSTICK_VAL_MASK / 2UL)
/**
* @brief Number of milliseconds in a second
*/
#define NRFX_SYSTICK_MS (1000UL)
/**
* @brief Number of microseconds in a second
*/
#define NRFX_SYSTICK_US (1000UL * NRFX_SYSTICK_MS)
/**
* @brief Number of milliseconds to wait in single loop
*
* Constant used by @ref nrd_drv_systick_delay_ms function
* to split waiting into loops and rest.
*
* It describes the number of milliseconds to wait in single loop.
*
* See @ref nrfx_systick_delay_ms source code for details.
*/
#define NRFX_SYSTICK_MS_STEP (64U)
/**
* @brief Checks if the given time is in correct range
*
* Function tests given time is not to big for this library.
* Assertion is used for testing.
*
* @param us Time in microseconds to check
*/
#define NRFX_SYSTICK_ASSERT_TIMEOUT(us) \
NRFX_ASSERT(us <= (NRFX_SYSTICK_TICKS_MAX / ((SystemCoreClock) / NRFX_SYSTICK_US)));
/**
* @brief Function that converts microseconds to ticks
*
* Function converts from microseconds to CPU ticks.
*
* @param us Number of microseconds
*
* @return Number of ticks
*
* @sa nrfx_systick_ms_tick
*/
static inline uint32_t nrfx_systick_us_tick(uint32_t us)
{
return us * ((SystemCoreClock) / NRFX_SYSTICK_US);
}
/**
* @brief Function that converts milliseconds to ticks
*
* Function converts from milliseconds to CPU ticks.
*
* @param us Number of milliseconds
*
* @return Number of ticks
*
* @sa nrfx_systick_us_tick
*/
static inline uint32_t nrfx_systick_ms_tick(uint32_t ms)
{
return ms * ((SystemCoreClock) / NRFX_SYSTICK_MS);
}
void nrfx_systick_init(void)
{
nrf_systick_load_set(NRF_SYSTICK_VAL_MASK);
nrf_systick_csr_set(
NRF_SYSTICK_CSR_CLKSOURCE_CPU |
NRF_SYSTICK_CSR_TICKINT_DISABLE |
NRF_SYSTICK_CSR_ENABLE);
}
void nrfx_systick_get(nrfx_systick_state_t * p_state)
{
p_state->time = nrf_systick_val_get();
}
bool nrfx_systick_test(nrfx_systick_state_t const * p_state, uint32_t us)
{
NRFX_SYSTICK_ASSERT_TIMEOUT(us);
const uint32_t diff = NRF_SYSTICK_VAL_MASK & ((p_state->time) - nrf_systick_val_get());
return (diff >= nrfx_systick_us_tick(us));
}
void nrfx_systick_delay_ticks(uint32_t ticks)
{
NRFX_ASSERT(ticks <= NRFX_SYSTICK_TICKS_MAX);
const uint32_t start = nrf_systick_val_get();
while ((NRF_SYSTICK_VAL_MASK & (start - nrf_systick_val_get())) < ticks)
{
/* Nothing to do */
}
}
void nrfx_systick_delay_us(uint32_t us)
{
NRFX_SYSTICK_ASSERT_TIMEOUT(us);
nrfx_systick_delay_ticks(nrfx_systick_us_tick(us));
}
void nrfx_systick_delay_ms(uint32_t ms)
{
uint32_t n = ms / NRFX_SYSTICK_MS_STEP;
uint32_t r = ms % NRFX_SYSTICK_MS_STEP;
while (0 != (n--))
{
nrfx_systick_delay_ticks(nrfx_systick_ms_tick(NRFX_SYSTICK_MS_STEP));
}
nrfx_systick_delay_ticks(nrfx_systick_ms_tick(r));
}
#endif // NRFX_CHECK(NRFX_SYSTICK_ENABLED)

View File

@@ -0,0 +1,155 @@
/**
* Copyright (c) 2019 - 2020, 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_TEMP_ENABLED)
#include <nrfx_temp.h>
#if !defined(USE_WORKAROUND_FOR_TEMP_OFFSET_ANOMALY) && defined(NRF51)
// Enable workaround for nRF51 series anomaly 28
// (TEMP: Temperature offset value has to be manually loaded to the TEMP module).
#define USE_WORKAROUND_FOR_TEMP_OFFSET_ANOMALY 1
#endif
/** @brief Time of one check attempt.*/
#define NRFX_TEMP_TIME_US 4
/** @brief Maximum attempts to check whether conversion passed.*/
#define NRFX_TEMP_ATTEMPTS 10
/** @brief Internal state of TEMP driver. */
static nrfx_drv_state_t m_temp_state;
/** @brief Pointer to handler to be called from interrupt routine. */
static nrfx_temp_data_handler_t m_data_handler;
nrfx_err_t nrfx_temp_init(nrfx_temp_config_t const * p_config, nrfx_temp_data_handler_t handler)
{
NRFX_ASSERT(p_config);
if (m_temp_state != NRFX_DRV_STATE_UNINITIALIZED)
{
return NRFX_ERROR_ALREADY_INITIALIZED;
}
#if NRFX_CHECK(USE_WORKAROUND_FOR_TEMP_OFFSET_ANOMALY)
*(uint32_t volatile *)0x4000C504 = 0;
#endif
m_data_handler = handler;
if (m_data_handler)
{
nrf_temp_int_enable(NRF_TEMP, NRF_TEMP_INT_DATARDY_MASK);
NRFX_IRQ_PRIORITY_SET(TEMP_IRQn, p_config->interrupt_priority);
NRFX_IRQ_ENABLE(TEMP_IRQn);
}
m_temp_state = NRFX_DRV_STATE_INITIALIZED;
return NRFX_SUCCESS;
}
void nrfx_temp_uninit(void)
{
NRFX_ASSERT(m_temp_state == NRFX_DRV_STATE_INITIALIZED);
nrf_temp_task_trigger(NRF_TEMP, NRF_TEMP_TASK_STOP);
if (m_data_handler)
{
nrf_temp_int_disable(NRF_TEMP, NRF_TEMP_INT_DATARDY_MASK);
NRFX_IRQ_DISABLE(TEMP_IRQn);
}
m_temp_state = NRFX_DRV_STATE_UNINITIALIZED;
}
int32_t nrfx_temp_calculate(int32_t raw_measurement)
{
/* Raw temperature is a 2's complement signed value. Moreover, it is represented
* by 0.25[C] intervals, so division by 4 is needed. To preserve
* fractional part, raw value is multiplied by 100 before division.*/
return (raw_measurement * 100) / 4;
}
nrfx_err_t nrfx_temp_measure(void)
{
NRFX_ASSERT(m_temp_state == NRFX_DRV_STATE_INITIALIZED);
nrfx_err_t result = NRFX_SUCCESS;
nrf_temp_event_clear(NRF_TEMP, NRF_TEMP_EVENT_DATARDY);
nrf_temp_task_trigger(NRF_TEMP, NRF_TEMP_TASK_START);
if (!m_data_handler)
{
bool ev_result;
NRFX_WAIT_FOR(nrf_temp_event_check(NRF_TEMP, NRF_TEMP_EVENT_DATARDY),
NRFX_TEMP_ATTEMPTS,
NRFX_TEMP_TIME_US,
ev_result);
if (!ev_result)
{
result = NRFX_ERROR_INTERNAL;
}
else
{
nrf_temp_event_clear(NRF_TEMP, NRF_TEMP_EVENT_DATARDY);
}
nrf_temp_task_trigger(NRF_TEMP, NRF_TEMP_TASK_STOP);
}
return result;
}
void nrfx_temp_irq_handler(void)
{
NRFX_ASSERT(m_data_handler);
nrf_temp_task_trigger(NRF_TEMP, NRF_TEMP_TASK_STOP);
nrf_temp_event_clear(NRF_TEMP, NRF_TEMP_EVENT_DATARDY);
uint32_t raw_temp = nrfx_temp_result_get();
m_data_handler(raw_temp);
}
#endif // NRFX_CHECK(NRFX_TEMP_ENABLED)

View File

@@ -0,0 +1,330 @@
/**
* Copyright (c) 2015 - 2020, 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)

View File

@@ -0,0 +1,783 @@
/**
* Copyright (c) 2015 - 2020, 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)

View File

@@ -0,0 +1,90 @@
/**
* Copyright (c) 2019 - 2020, 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;
}
}

View File

@@ -0,0 +1,872 @@
/**
* Copyright (c) 2015 - 2020, 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)

View File

@@ -0,0 +1,857 @@
/**
* Copyright (c) 2015 - 2020, 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_TWIS_ENABLED)
#if !(NRFX_CHECK(NRFX_TWIS0_ENABLED) || \
NRFX_CHECK(NRFX_TWIS1_ENABLED) || \
NRFX_CHECK(NRFX_TWIS2_ENABLED) || \
NRFX_CHECK(NRFX_TWIS3_ENABLED))
#error "No enabled TWIS instances. Check <nrfx_config.h>."
#endif
#include <nrfx_twis.h>
#include "prs/nrfx_prs.h"
#define NRFX_LOG_MODULE TWIS
#include <nrfx_log.h>
#define EVT_TO_STR(event) \
(event == NRF_TWIS_EVENT_STOPPED ? "NRF_TWIS_EVENT_STOPPED" : \
(event == NRF_TWIS_EVENT_ERROR ? "NRF_TWIS_EVENT_ERROR" : \
(event == NRF_TWIS_EVENT_RXSTARTED ? "NRF_TWIS_EVENT_RXSTARTED" : \
(event == NRF_TWIS_EVENT_TXSTARTED ? "NRF_TWIS_EVENT_TXSTARTED" : \
(event == NRF_TWIS_EVENT_WRITE ? "NRF_TWIS_EVENT_WRITE" : \
(event == NRF_TWIS_EVENT_READ ? "NRF_TWIS_EVENT_READ" : \
"UNKNOWN EVENT"))))))
/**
* @brief Actual state of internal state machine
*
* Current substate of powered on state.
*/
typedef enum
{
NRFX_TWIS_SUBSTATE_IDLE, ///< No ongoing transmission
NRFX_TWIS_SUBSTATE_READ_WAITING, ///< Read request received, waiting for data
NRFX_TWIS_SUBSTATE_READ_PENDING, ///< Reading is actually pending (data sending)
NRFX_TWIS_SUBSTATE_WRITE_WAITING, ///< Write request received, waiting for data buffer
NRFX_TWIS_SUBSTATE_WRITE_PENDING, ///< Writing is actually pending (data receiving)
} nrfx_twis_substate_t;
// Control block - driver instance local data.
typedef struct
{
nrfx_twis_event_handler_t ev_handler;
// Internal copy of hardware errors flags merged with specific internal
// driver errors flags.
// This value can be changed in the interrupt and cleared in the main program.
// Always use Atomic load-store when updating this value in main loop.
volatile uint32_t error;
nrfx_drv_state_t state;
volatile nrfx_twis_substate_t substate;
volatile bool semaphore;
} twis_control_block_t;
static twis_control_block_t m_cb[NRFX_TWIS_ENABLED_COUNT];
/**
* @brief Used interrupts mask
*
* Mask for all interrupts used by this library
*/
static const uint32_t m_used_ints_mask = NRF_TWIS_INT_STOPPED_MASK |
NRF_TWIS_INT_ERROR_MASK |
NRF_TWIS_INT_RXSTARTED_MASK |
NRF_TWIS_INT_TXSTARTED_MASK |
NRF_TWIS_INT_WRITE_MASK |
NRF_TWIS_INT_READ_MASK;
/**
* @brief Clear all events
*
* Function clears all actually pending events
*/
static void nrfx_twis_clear_all_events(NRF_TWIS_Type * const p_reg)
{
/* Clear all events */
nrf_twis_event_clear(p_reg, NRF_TWIS_EVENT_STOPPED);
nrf_twis_event_clear(p_reg, NRF_TWIS_EVENT_ERROR);
nrf_twis_event_clear(p_reg, NRF_TWIS_EVENT_RXSTARTED);
nrf_twis_event_clear(p_reg, NRF_TWIS_EVENT_TXSTARTED);
nrf_twis_event_clear(p_reg, NRF_TWIS_EVENT_WRITE);
nrf_twis_event_clear(p_reg, NRF_TWIS_EVENT_READ);
}
/**
* @brief Reset all the registers to known state
*
* This function clears all registers that requires it to known state.
* TWIS is left disabled after this function.
* All events are cleared.
* @param[out] p_reg TWIS to reset register address
*/
static inline void nrfx_twis_swreset(NRF_TWIS_Type * p_reg)
{
/* Disable TWIS */
nrf_twis_disable(p_reg);
/* Disconnect pins */
nrf_twis_pins_set(p_reg, ~0U, ~0U);
/* Disable interrupt global for the instance */
NRFX_IRQ_DISABLE(nrfx_get_irq_number(p_reg));
/* Disable interrupts */
nrf_twis_int_disable(p_reg, ~0U);
}
/**
* @brief Configure pin
*
* Function configures selected for work as SDA or SCL.
* @param pin Pin number to configure
*/
static inline void nrfx_twis_config_pin(uint32_t pin, nrf_gpio_pin_pull_t pull)
{
nrf_gpio_cfg(pin,
NRF_GPIO_PIN_DIR_INPUT,
NRF_GPIO_PIN_INPUT_DISCONNECT,
pull,
NRF_GPIO_PIN_S0D1,
NRF_GPIO_PIN_NOSENSE);
}
/**
* @brief Auxiliary function for getting event state on right bit possition
*
* This function calls @ref nrf_twis_event_get function but the the result
* is shifted to match INTEN register scheme.
*
* @param[in,out] p_reg TWIS to read event from
* @param ev Event code
*
* @return Selected event state shifted by @ref nrfx_event_to_bitpos
*
* @sa nrf_twis_event_get
* @sa nrfx_event_to_bitpos
*/
static inline uint32_t nrfx_twis_event_bit_get(NRF_TWIS_Type * p_reg,
nrf_twis_event_t ev)
{
return (uint32_t)nrf_twis_event_get_and_clear(p_reg, ev) << nrfx_event_to_bitpos(ev);
}
/**
* @brief Auxiliary function for checking event bit inside given flags value
*
* Function used here to check presence of the event inside given flags value.
* It transforms given event to bit possition and then checks if in given variable it is cleared.
*
* @param flags Flags to test
* @param ev Event code
*
* @retval true Flag for selected event is set
* @retval false Flag for selected event is cleared
*/
static inline bool nrfx_twis_check_bit(uint32_t flags,
nrf_twis_event_t ev)
{
return 0 != (flags & (1U << nrfx_event_to_bitpos(ev)));
}
/**
* @brief Auxiliary function for clearing event bit in given flags value
*
* Function used to clear selected event bit.
*
* @param flags Flags to process
* @param ev Event code to clear
*
* @return Value @em flags with cleared event bit that matches given @em ev
*/
static inline uint32_t nrfx_twis_clear_bit(uint32_t flags,
nrf_twis_event_t ev)
{
return flags & ~(1U << nrfx_event_to_bitpos(ev));
}
static void call_event_handler(twis_control_block_t const * p_cb,
nrfx_twis_evt_t const * p_evt)
{
nrfx_twis_event_handler_t handler = p_cb->ev_handler;
if (handler != NULL)
{
handler(p_evt);
}
}
/**
* @brief Auxiliary function for error processing
*
* Function called when in current substate the event apears and it cannot be processed.
* It should be called also on ERROR event.
* If given @em error parameter has zero value the @ref NRFX_TWIS_ERROR_UNEXPECTED_EVENT
* would be set.
*
* @param p_cb Pointer to the driver instance control block.
* @param evt What error event raport to event handler
* @param error Error flags
*/
static inline void nrfx_twis_process_error(twis_control_block_t * p_cb,
nrfx_twis_evt_type_t evt,
uint32_t error)
{
if (0 == error)
{
error = NRFX_TWIS_ERROR_UNEXPECTED_EVENT;
}
nrfx_twis_evt_t evdata;
evdata.type = evt;
evdata.data.error = error;
p_cb->error |= error;
call_event_handler(p_cb, &evdata);
}
static void nrfx_twis_state_machine(NRF_TWIS_Type * p_reg,
twis_control_block_t * p_cb)
{
if (!NRFX_TWIS_NO_SYNC_MODE)
{
/* Exclude parallel processing of this function */
if (p_cb->semaphore)
{
return;
}
p_cb->semaphore = 1;
}
/* Event data structure to be passed into event handler */
nrfx_twis_evt_t evdata;
/* Current substate copy */
nrfx_twis_substate_t substate = p_cb->substate;
/* Event flags */
uint32_t ev = 0;
/* Get all events */
ev |= nrfx_twis_event_bit_get(p_reg, NRF_TWIS_EVENT_STOPPED);
ev |= nrfx_twis_event_bit_get(p_reg, NRF_TWIS_EVENT_ERROR);
ev |= nrfx_twis_event_bit_get(p_reg, NRF_TWIS_EVENT_RXSTARTED);
ev |= nrfx_twis_event_bit_get(p_reg, NRF_TWIS_EVENT_TXSTARTED);
ev |= nrfx_twis_event_bit_get(p_reg, NRF_TWIS_EVENT_WRITE);
ev |= nrfx_twis_event_bit_get(p_reg, NRF_TWIS_EVENT_READ);
/* State machine */
while (0 != ev)
{
switch (substate)
{
case NRFX_TWIS_SUBSTATE_IDLE:
if (nrfx_twis_check_bit(ev, NRF_TWIS_EVENT_STOPPED))
{
/* Stopped event is always allowed in IDLE state - just ignore */
ev = nrfx_twis_clear_bit(ev, NRF_TWIS_EVENT_STOPPED);
}
else if (nrfx_twis_check_bit(ev, NRF_TWIS_EVENT_READ))
{
evdata.type = NRFX_TWIS_EVT_READ_REQ;
if (nrfx_twis_check_bit(ev, NRF_TWIS_EVENT_TXSTARTED))
{
substate = NRFX_TWIS_SUBSTATE_READ_PENDING;
evdata.data.buf_req = false;
}
else
{
substate = NRFX_TWIS_SUBSTATE_READ_WAITING;
evdata.data.buf_req = true;
}
call_event_handler(p_cb, &evdata);
ev = nrfx_twis_clear_bit(ev, NRF_TWIS_EVENT_READ);
ev = nrfx_twis_clear_bit(ev, NRF_TWIS_EVENT_TXSTARTED);
ev = nrfx_twis_clear_bit(ev, NRF_TWIS_EVENT_WRITE);
ev = nrfx_twis_clear_bit(ev, NRF_TWIS_EVENT_RXSTARTED);
}
else if (nrfx_twis_check_bit(ev, NRF_TWIS_EVENT_WRITE))
{
evdata.type = NRFX_TWIS_EVT_WRITE_REQ;
if (nrfx_twis_check_bit(ev, NRF_TWIS_EVENT_RXSTARTED))
{
substate = NRFX_TWIS_SUBSTATE_WRITE_PENDING;
evdata.data.buf_req = false;
}
else
{
substate = NRFX_TWIS_SUBSTATE_WRITE_WAITING;
evdata.data.buf_req = true;
}
call_event_handler(p_cb, &evdata);
ev = nrfx_twis_clear_bit(ev, NRF_TWIS_EVENT_READ);
ev = nrfx_twis_clear_bit(ev, NRF_TWIS_EVENT_TXSTARTED);
ev = nrfx_twis_clear_bit(ev, NRF_TWIS_EVENT_WRITE);
ev = nrfx_twis_clear_bit(ev, NRF_TWIS_EVENT_RXSTARTED);
}
else
{
nrfx_twis_process_error(p_cb,
NRFX_TWIS_EVT_GENERAL_ERROR,
nrf_twis_error_source_get_and_clear(p_reg));
ev = 0;
}
break;
case NRFX_TWIS_SUBSTATE_READ_WAITING:
if (nrfx_twis_check_bit(ev, NRF_TWIS_EVENT_TXSTARTED) ||
nrfx_twis_check_bit(ev, NRF_TWIS_EVENT_WRITE) ||
nrfx_twis_check_bit(ev, NRF_TWIS_EVENT_READ) ||
nrfx_twis_check_bit(ev, NRF_TWIS_EVENT_STOPPED))
{
substate = NRFX_TWIS_SUBSTATE_READ_PENDING;
/* Any other bits requires further processing in PENDING substate */
ev = nrfx_twis_clear_bit(ev, NRF_TWIS_EVENT_TXSTARTED);
}
else
{
nrfx_twis_process_error(p_cb,
NRFX_TWIS_EVT_READ_ERROR,
nrf_twis_error_source_get_and_clear(p_reg));
substate = NRFX_TWIS_SUBSTATE_IDLE;
ev = 0;
}
break;
case NRFX_TWIS_SUBSTATE_READ_PENDING:
if (nrfx_twis_check_bit(ev, NRF_TWIS_EVENT_WRITE) ||
nrfx_twis_check_bit(ev, NRF_TWIS_EVENT_READ) ||
nrfx_twis_check_bit(ev, NRF_TWIS_EVENT_STOPPED))
{
evdata.type = NRFX_TWIS_EVT_READ_DONE;
evdata.data.tx_amount = nrf_twis_tx_amount_get(p_reg);
NRFX_LOG_INFO("Transfer tx_len:%d", evdata.data.tx_amount);
NRFX_LOG_DEBUG("Tx data:");
NRFX_LOG_HEXDUMP_DEBUG((uint8_t const *)p_reg->TXD.PTR,
evdata.data.tx_amount * sizeof(uint8_t));
call_event_handler(p_cb, &evdata);
/* Go to idle and repeat the state machine if READ or WRITE events detected.
* This time READ or WRITE would be started */
substate = NRFX_TWIS_SUBSTATE_IDLE;
ev = nrfx_twis_clear_bit(ev, NRF_TWIS_EVENT_STOPPED);
}
else
{
nrfx_twis_process_error(p_cb,
NRFX_TWIS_EVT_READ_ERROR,
nrf_twis_error_source_get_and_clear(p_reg));
substate = NRFX_TWIS_SUBSTATE_IDLE;
ev = 0;
}
break;
case NRFX_TWIS_SUBSTATE_WRITE_WAITING:
if (nrfx_twis_check_bit(ev, NRF_TWIS_EVENT_RXSTARTED) ||
nrfx_twis_check_bit(ev, NRF_TWIS_EVENT_WRITE) ||
nrfx_twis_check_bit(ev, NRF_TWIS_EVENT_READ) ||
nrfx_twis_check_bit(ev, NRF_TWIS_EVENT_STOPPED))
{
substate = NRFX_TWIS_SUBSTATE_WRITE_PENDING;
/* Any other bits requires further processing in PENDING substate */
ev = nrfx_twis_clear_bit(ev, NRF_TWIS_EVENT_RXSTARTED);
}
else
{
nrfx_twis_process_error(p_cb,
NRFX_TWIS_EVT_WRITE_ERROR,
nrf_twis_error_source_get_and_clear(p_reg));
substate = NRFX_TWIS_SUBSTATE_IDLE;
ev = 0;
}
break;
case NRFX_TWIS_SUBSTATE_WRITE_PENDING:
if (nrfx_twis_check_bit(ev, NRF_TWIS_EVENT_WRITE) ||
nrfx_twis_check_bit(ev, NRF_TWIS_EVENT_READ) ||
nrfx_twis_check_bit(ev, NRF_TWIS_EVENT_STOPPED))
{
evdata.type = NRFX_TWIS_EVT_WRITE_DONE;
evdata.data.rx_amount = nrf_twis_rx_amount_get(p_reg);
call_event_handler(p_cb, &evdata);
/* Go to idle and repeat the state machine if READ or WRITE events detected.
* This time READ or WRITE would be started */
substate = NRFX_TWIS_SUBSTATE_IDLE;
ev = nrfx_twis_clear_bit(ev, NRF_TWIS_EVENT_STOPPED);
}
else
{
nrfx_twis_process_error(p_cb,
NRFX_TWIS_EVT_WRITE_ERROR,
nrf_twis_error_source_get_and_clear(p_reg));
substate = NRFX_TWIS_SUBSTATE_IDLE;
ev = 0;
}
break;
default:
substate = NRFX_TWIS_SUBSTATE_IDLE;
/* Do not clear any events and repeat the machine */
break;
}
}
p_cb->substate = substate;
if (!NRFX_TWIS_NO_SYNC_MODE)
{
p_cb->semaphore = 0;
}
}
static inline void nrfx_twis_preprocess_status(nrfx_twis_t const * p_instance)
{
if (!NRFX_TWIS_NO_SYNC_MODE)
{
NRF_TWIS_Type * p_reg = p_instance->p_reg;
twis_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
if (NULL == p_cb->ev_handler)
{
nrfx_twis_state_machine(p_reg, p_cb);
}
}
}
/* -------------------------------------------------------------------------
* Implementation of interface functions
*
*/
nrfx_err_t nrfx_twis_init(nrfx_twis_t const * p_instance,
nrfx_twis_config_t const * p_config,
nrfx_twis_event_handler_t event_handler)
{
NRFX_ASSERT(p_config);
NRFX_ASSERT(p_config->scl != p_config->sda);
nrfx_err_t err_code;
NRF_TWIS_Type * p_reg = p_instance->p_reg;
twis_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;
}
#if NRFX_CHECK(NRFX_PRS_ENABLED)
static nrfx_irq_handler_t const irq_handlers[NRFX_TWIS_ENABLED_COUNT] = {
#if NRFX_CHECK(NRFX_TWIS0_ENABLED)
nrfx_twis_0_irq_handler,
#endif
#if NRFX_CHECK(NRFX_TWIS1_ENABLED)
nrfx_twis_1_irq_handler,
#endif
#if NRFX_CHECK(NRFX_TWIS2_ENABLED)
nrfx_twis_2_irq_handler,
#endif
#if NRFX_CHECK(NRFX_TWIS3_ENABLED)
nrfx_twis_3_irq_handler,
#endif
};
if (nrfx_prs_acquire(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)
if (!NRFX_TWIS_ASSUME_INIT_AFTER_RESET_ONLY)
{
nrfx_twis_swreset(p_reg);
}
nrfx_twis_config_pin(p_config->scl, p_config->scl_pull);
nrfx_twis_config_pin(p_config->sda, p_config->sda_pull);
nrf_twis_config_addr_mask_t addr_mask = (nrf_twis_config_addr_mask_t)0;
if (0 == (p_config->addr[0] | p_config->addr[1]))
{
addr_mask = NRF_TWIS_CONFIG_ADDRESS0_MASK;
}
else
{
if (0 != p_config->addr[0])
{
addr_mask |= NRF_TWIS_CONFIG_ADDRESS0_MASK;
}
if (0 != p_config->addr[1])
{
addr_mask |= NRF_TWIS_CONFIG_ADDRESS1_MASK;
}
}
/* Peripheral interrupt configure
* (note - interrupts still needs to be configured in INTEN register.
* This is done in enable function) */
NRFX_IRQ_PRIORITY_SET(nrfx_get_irq_number(p_reg),
p_config->interrupt_priority);
NRFX_IRQ_ENABLE(nrfx_get_irq_number(p_reg));
/* Configure */
nrf_twis_pins_set (p_reg, p_config->scl, p_config->sda);
nrf_twis_address_set (p_reg, 0, p_config->addr[0]);
nrf_twis_address_set (p_reg, 1, p_config->addr[1]);
nrf_twis_config_address_set(p_reg, addr_mask);
/* Clear semaphore */
if (!NRFX_TWIS_NO_SYNC_MODE)
{
p_cb->semaphore = 0;
}
/* Set internal instance variables */
p_cb->substate = NRFX_TWIS_SUBSTATE_IDLE;
p_cb->ev_handler = event_handler;
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_twis_uninit(nrfx_twis_t const * p_instance)
{
NRF_TWIS_Type * p_reg = p_instance->p_reg;
twis_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
NRFX_ASSERT(p_cb->state != NRFX_DRV_STATE_UNINITIALIZED);
TWIS_PSEL_Type psel = p_reg->PSEL;
nrfx_twis_swreset(p_reg);
/* Clear pins state if */
if (!(TWIS_PSEL_SCL_CONNECT_Msk & psel.SCL))
{
nrf_gpio_cfg_default(psel.SCL);
}
if (!(TWIS_PSEL_SDA_CONNECT_Msk & psel.SDA))
{
nrf_gpio_cfg_default(psel.SDA);
}
#if NRFX_CHECK(NRFX_PRS_ENABLED)
nrfx_prs_release(p_reg);
#endif
/* Clear variables */
p_cb->ev_handler = NULL;
p_cb->state = NRFX_DRV_STATE_UNINITIALIZED;
}
void nrfx_twis_enable(nrfx_twis_t const * p_instance)
{
NRF_TWIS_Type * p_reg = p_instance->p_reg;
twis_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
NRFX_ASSERT(p_cb->state == NRFX_DRV_STATE_INITIALIZED);
nrfx_twis_clear_all_events(p_reg);
/* Enable interrupts */
if (NULL != p_cb->ev_handler)
{
nrf_twis_int_enable(p_reg, m_used_ints_mask);
}
nrf_twis_enable(p_reg);
p_cb->error = 0;
p_cb->state = NRFX_DRV_STATE_POWERED_ON;
p_cb->substate = NRFX_TWIS_SUBSTATE_IDLE;
}
void nrfx_twis_disable(nrfx_twis_t const * p_instance)
{
NRF_TWIS_Type * p_reg = p_instance->p_reg;
twis_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
NRFX_ASSERT(p_cb->state != NRFX_DRV_STATE_UNINITIALIZED);
nrf_twis_int_disable(p_reg, m_used_ints_mask);
nrf_twis_disable(p_reg);
p_cb->state = NRFX_DRV_STATE_INITIALIZED;
}
/* ARM recommends not using the LDREX and STREX instructions in C code.
* This is because the compiler might generate loads and stores between
* LDREX and STREX, potentially clearing the exclusive monitor set by LDREX.
* This recommendation also applies to the byte, halfword, and doubleword
* variants LDREXB, STREXB, LDREXH, STREXH, LDREXD, and STREXD.
*
* This is the reason for the function below to be implemented in assembly.
*/
//lint -save -e578
#if defined (__CC_ARM )
static __ASM uint32_t nrfx_twis_error_get_and_clear_internal(uint32_t volatile * perror)
{
mov r3, r0
mov r1, #0
nrfx_twis_error_get_and_clear_internal_try
ldrex r0, [r3]
strex r2, r1, [r3]
cmp r2, r1 /* did this succeed? */
bne nrfx_twis_error_get_and_clear_internal_try /* no - try again */
bx lr
}
#elif defined ( __GNUC__ )
static uint32_t nrfx_twis_error_get_and_clear_internal(uint32_t volatile * perror)
{
uint32_t ret;
uint32_t temp;
__ASM volatile(
" .syntax unified \n"
"nrfx_twis_error_get_and_clear_internal_try: \n"
" ldrex %[ret], [%[perror]] \n"
" strex %[temp], %[zero], [%[perror]] \n"
" cmp %[temp], %[zero] \n"
" bne nrfx_twis_error_get_and_clear_internal_try \n"
: /* Output */
[ret]"=&l"(ret),
[temp]"=&l"(temp)
: /* Input */
[zero]"l"(0),
[perror]"l"(perror)
);
(void)temp;
return ret;
}
#elif defined ( __ICCARM__ )
static uint32_t nrfx_twis_error_get_and_clear_internal(uint32_t volatile * perror)
{
uint32_t ret;
uint32_t temp;
__ASM volatile(
"1: \n"
" ldrex %[ret], [%[perror]] \n"
" strex %[temp], %[zero], [%[perror]] \n"
" cmp %[temp], %[zero] \n"
" bne.n 1b \n"
: /* Output */
[ret]"=&l"(ret),
[temp]"=&l"(temp)
: /* Input */
[zero]"l"(0),
[perror]"l"(perror)
);
(void)temp;
return ret;
}
#else
#error Unknown compiler
#endif
//lint -restore
uint32_t nrfx_twis_error_get_and_clear(nrfx_twis_t const * p_instance)
{
nrfx_twis_preprocess_status(p_instance);
/* Make sure that access to error member is atomic
* so there is no bit that is cleared if it is not copied to local variable already. */
twis_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
return nrfx_twis_error_get_and_clear_internal(&p_cb->error);
}
nrfx_err_t nrfx_twis_tx_prepare(nrfx_twis_t const * p_instance,
void const * p_buf,
size_t size)
{
nrfx_err_t err_code;
twis_control_block_t const * p_cb = &m_cb[p_instance->drv_inst_idx];
/* Check power state*/
if (p_cb->state != NRFX_DRV_STATE_POWERED_ON)
{
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;
}
/* Check data address */
if (!nrfx_is_in_ram(p_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;
}
/* Check data size */
if ((size & TWIS_TXD_MAXCNT_MAXCNT_Msk) != size)
{
err_code = NRFX_ERROR_INVALID_LENGTH;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
nrf_twis_tx_prepare(p_instance->p_reg,
(uint8_t const *)p_buf,
size);
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_twis_rx_prepare(nrfx_twis_t const * p_instance,
void * p_buf,
size_t size)
{
nrfx_err_t err_code;
twis_control_block_t const * p_cb = &m_cb[p_instance->drv_inst_idx];
/* Check power state*/
if (p_cb->state != NRFX_DRV_STATE_POWERED_ON)
{
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;
}
/* Check data address */
if (!nrfx_is_in_ram(p_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;
}
/* Check data size */
if ((size & TWIS_RXD_MAXCNT_MAXCNT_Msk) != size)
{
err_code = NRFX_ERROR_INVALID_LENGTH;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
nrf_twis_rx_prepare(p_instance->p_reg,
(uint8_t *)p_buf,
size);
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_twis_is_busy(nrfx_twis_t const * p_instance)
{
nrfx_twis_preprocess_status(p_instance);
twis_control_block_t const * p_cb = &m_cb[p_instance->drv_inst_idx];
return NRFX_TWIS_SUBSTATE_IDLE != p_cb->substate;
}
bool nrfx_twis_is_waiting_tx_buff(nrfx_twis_t const * p_instance)
{
nrfx_twis_preprocess_status(p_instance);
twis_control_block_t const * p_cb = &m_cb[p_instance->drv_inst_idx];
return NRFX_TWIS_SUBSTATE_READ_WAITING == p_cb->substate;
}
bool nrfx_twis_is_waiting_rx_buff(nrfx_twis_t const * p_instance)
{
nrfx_twis_preprocess_status(p_instance);
twis_control_block_t const * p_cb = &m_cb[p_instance->drv_inst_idx];
return NRFX_TWIS_SUBSTATE_WRITE_WAITING == p_cb->substate;
}
bool nrfx_twis_is_pending_tx(nrfx_twis_t const * p_instance)
{
nrfx_twis_preprocess_status(p_instance);
twis_control_block_t const * p_cb = &m_cb[p_instance->drv_inst_idx];
return NRFX_TWIS_SUBSTATE_READ_PENDING == p_cb->substate;
}
bool nrfx_twis_is_pending_rx(nrfx_twis_t const * p_instance)
{
nrfx_twis_preprocess_status(p_instance);
twis_control_block_t const * p_cb = &m_cb[p_instance->drv_inst_idx];
return NRFX_TWIS_SUBSTATE_WRITE_PENDING == p_cb->substate;
}
#if NRFX_CHECK(NRFX_TWIS0_ENABLED)
void nrfx_twis_0_irq_handler(void)
{
nrfx_twis_state_machine(NRF_TWIS0, &m_cb[NRFX_TWIS0_INST_IDX]);
}
#endif
#if NRFX_CHECK(NRFX_TWIS1_ENABLED)
void nrfx_twis_1_irq_handler(void)
{
nrfx_twis_state_machine(NRF_TWIS1, &m_cb[NRFX_TWIS1_INST_IDX]);
}
#endif
#if NRFX_CHECK(NRFX_TWIS2_ENABLED)
void nrfx_twis_2_irq_handler(void)
{
nrfx_twis_state_machine(NRF_TWIS2, &m_cb[NRFX_TWIS2_INST_IDX]);
}
#endif
#if NRFX_CHECK(NRFX_TWIS3_ENABLED)
void nrfx_twis_3_irq_handler(void)
{
nrfx_twis_state_machine(NRF_TWIS3, &m_cb[NRFX_TWIS3_INST_IDX]);
}
#endif
#endif // NRFX_CHECK(NRFX_TWIS_ENABLED)

View File

@@ -0,0 +1,654 @@
/**
* Copyright (c) 2015 - 2020, 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)

View File

@@ -0,0 +1,702 @@
/**
* Copyright (c) 2015 - 2020, 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)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,93 @@
/**
* Copyright (c) 2016 - 2020, 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_USBD_ERRATA_H__
#define NRFX_USBD_ERRATA_H__
#include <nrfx.h>
#include <nrf_erratas.h>
#ifndef NRFX_USBD_ERRATA_ENABLE
/**
* @brief The constant that informs if errata should be enabled at all.
*
* If this constant is set to 0, all the Errata bug fixes will be automatically disabled.
*/
#define NRFX_USBD_ERRATA_ENABLE 1
#endif
/* Errata: ISO double buffering not functional. */
static inline bool nrfx_usbd_errata_166(void)
{
return NRFX_USBD_ERRATA_ENABLE && nrf52_errata_166();
}
/* Errata: USBD might not reach its active state. */
static inline bool nrfx_usbd_errata_171(void)
{
return NRFX_USBD_ERRATA_ENABLE && nrf52_errata_171();
}
/* Errata: USB cannot be enabled. */
static inline bool nrfx_usbd_errata_187(void)
{
return NRFX_USBD_ERRATA_ENABLE && nrf52_errata_187();
}
/* Errata: USBD cannot receive tasks during DMA. */
static inline bool nrfx_usbd_errata_199(void)
{
return NRFX_USBD_ERRATA_ENABLE && nrf52_errata_199();
}
/* Errata: Device remains in SUSPEND too long. */
static inline bool nrfx_usbd_errata_211(void)
{
return NRFX_USBD_ERRATA_ENABLE && nrf52_errata_211();
}
/* Errata: Unexpected behavior after reset. **/
static inline bool nrfx_usbd_errata_223(void)
{
return NRFX_USBD_ERRATA_ENABLE && nrf52_errata_223();
}
#endif // NRFX_USBD_ERRATA_H__

View File

@@ -0,0 +1,166 @@
/**
* Copyright (c) 2015 - 2020, 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)

View File

@@ -0,0 +1,166 @@
/**
* Copyright (c) 2017 - 2020, 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)

View File

@@ -0,0 +1,155 @@
/**
* Copyright (c) 2017 - 2020, 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__