Initial commit: MT firmware project

- BLE peripheral applications
- dr_piezo and bladder_patch projects

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Charles Kwon
2026-01-25 17:26:39 +09:00
commit 72f5eb3cd9
2559 changed files with 1594625 additions and 0 deletions

View File

@@ -0,0 +1,340 @@
/**
* Copyright (c) 2015 - 2021, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <nrfx.h>
#if NRFX_CHECK(NRFX_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 - 2021, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <nrfx.h>
#if NRFX_CHECK(NRFX_CLOCK_ENABLED)
#include <nrfx_clock.h>
#define NRFX_LOG_MODULE CLOCK
#include <nrfx_log.h>
#if NRFX_CHECK(NRFX_POWER_ENABLED)
extern bool nrfx_power_irq_enabled;
#endif
#define EVT_TO_STR(event) \
(event == NRF_CLOCK_EVENT_HFCLKSTARTED ? "NRF_CLOCK_EVENT_HFCLKSTARTED" : \
(event == NRF_CLOCK_EVENT_LFCLKSTARTED ? "NRF_CLOCK_EVENT_LFCLKSTARTED" : \
(event == NRF_CLOCK_EVENT_DONE ? "NRF_CLOCK_EVENT_DONE" : \
(event == NRF_CLOCK_EVENT_CTTO ? "NRF_CLOCK_EVENT_CTTO" : \
"UNKNOWN EVENT"))))
#if NRFX_CHECK(NRFX_CLOCK_CONFIG_LF_CAL_ENABLED)
#if (NRF_CLOCK_HAS_CALIBRATION == 0)
#error "Calibration is not available in the SoC that is used."
#endif
#if (NRFX_CLOCK_CONFIG_LF_SRC != CLOCK_LFCLKSRC_SRC_RC)
#error "Calibration can be performed only for the RC Oscillator."
#endif
#endif
#if !defined(USE_WORKAROUND_FOR_ANOMALY_132) && \
(defined(NRF52832_XXAA) || defined(NRF52832_XXAB))
// ANOMALY 132 - LFCLK needs to avoid frame from 66us to 138us after LFCLK stop. This solution
// applies delay of 138us before starting LFCLK.
#define USE_WORKAROUND_FOR_ANOMALY_132 1
// Convert time to cycles (nRF52832 is clocked with 64 MHz, use delay of 138 us).
#define ANOMALY_132_DELAY_CYCLES (64UL * 138)
#endif
#if !defined(USE_WORKAROUND_FOR_ANOMALY_192) && \
(defined(NRF52810_XXAA) || \
defined(NRF52832_XXAA) || defined(NRF52832_XXAB) || \
defined(NRF52840_XXAA))
// Enable workaround for nRF52 anomaly 192 (LFRC oscillator frequency is wrong
// after calibration, exceeding 500 ppm).
#define USE_WORKAROUND_FOR_ANOMALY_192 1
#endif
#if !defined(USE_WORKAROUND_FOR_ANOMALY_201) && \
(defined(NRF52810_XXAA) || \
defined(NRF52832_XXAA) || defined(NRF52832_XXAB) || \
defined(NRF52840_XXAA))
// Enable workaround for nRF52 anomaly 201 (EVENTS_HFCLKSTARTED might be generated twice).
#define USE_WORKAROUND_FOR_ANOMALY_201 1
#endif
#if NRFX_CHECK(NRFX_CLOCK_CONFIG_LF_CAL_ENABLED)
typedef enum
{
CAL_STATE_IDLE,
CAL_STATE_CAL
} nrfx_clock_cal_state_t;
#endif
/**@brief CLOCK control block. */
typedef struct
{
nrfx_clock_event_handler_t event_handler;
bool module_initialized; /*< Indicate the state of module */
#if NRFX_CHECK(USE_WORKAROUND_FOR_ANOMALY_201)
bool hfclk_started; /*< Anomaly 201 workaround. */
#endif
#if NRFX_CHECK(NRFX_CLOCK_CONFIG_LF_CAL_ENABLED)
volatile nrfx_clock_cal_state_t cal_state;
#endif
} nrfx_clock_cb_t;
static nrfx_clock_cb_t m_clock_cb;
/**
* This variable is used to check whether common POWER_CLOCK common interrupt
* should be disabled or not if @ref nrfx_power tries to disable the interrupt.
*/
#if NRFX_CHECK(NRFX_POWER_ENABLED)
bool nrfx_clock_irq_enabled;
#endif
#if NRFX_CHECK(USE_WORKAROUND_FOR_ANOMALY_132)
/**
* @brief Function for applying delay of 138us before starting LFCLK.
*/
static void nrfx_clock_anomaly_132(void)
{
uint32_t cyccnt_inital;
uint32_t core_debug;
uint32_t dwt_ctrl;
// Preserve DEMCR register to do not influence into its configuration. Enable the trace and
// debug blocks. It is required to read and write data to DWT block.
core_debug = CoreDebug->DEMCR;
CoreDebug->DEMCR = core_debug | CoreDebug_DEMCR_TRCENA_Msk;
// Preserve CTRL register in DWT block to do not influence into its configuration. Make sure
// that cycle counter is enabled.
dwt_ctrl = DWT->CTRL;
DWT->CTRL = dwt_ctrl | DWT_CTRL_CYCCNTENA_Msk;
// Store start value of cycle counter.
cyccnt_inital = DWT->CYCCNT;
// Delay required time.
while ((DWT->CYCCNT - cyccnt_inital) < ANOMALY_132_DELAY_CYCLES)
{}
// Restore preserved registers.
DWT->CTRL = dwt_ctrl;
CoreDebug->DEMCR = core_debug;
}
#endif // NRFX_CHECK(USE_WORKAROUND_FOR_ANOMALY_132)
nrfx_err_t nrfx_clock_init(nrfx_clock_event_handler_t event_handler)
{
NRFX_ASSERT(event_handler);
nrfx_err_t err_code = NRFX_SUCCESS;
if (m_clock_cb.module_initialized)
{
err_code = NRFX_ERROR_ALREADY_INITIALIZED;
}
else
{
#if NRFX_CHECK(NRFX_CLOCK_CONFIG_LF_CAL_ENABLED)
m_clock_cb.cal_state = CAL_STATE_IDLE;
#endif
m_clock_cb.event_handler = event_handler;
m_clock_cb.module_initialized = true;
#if NRFX_CHECK(USE_WORKAROUND_FOR_ANOMALY_201)
m_clock_cb.hfclk_started = false;
#endif
}
NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
void nrfx_clock_enable(void)
{
NRFX_ASSERT(m_clock_cb.module_initialized);
nrfx_power_clock_irq_init();
nrf_clock_lf_src_set((nrf_clock_lfclk_t)NRFX_CLOCK_CONFIG_LF_SRC);
#if NRFX_CHECK(NRFX_POWER_ENABLED)
nrfx_clock_irq_enabled = true;
#endif
NRFX_LOG_INFO("Module enabled.");
}
void nrfx_clock_disable(void)
{
NRFX_ASSERT(m_clock_cb.module_initialized);
#if NRFX_CHECK(NRFX_POWER_ENABLED)
NRFX_ASSERT(nrfx_clock_irq_enabled);
if (!nrfx_power_irq_enabled)
#endif
{
NRFX_IRQ_DISABLE(nrfx_get_irq_number(NRF_CLOCK));
}
nrf_clock_int_disable(CLOCK_INTENSET_HFCLKSTARTED_Msk |
CLOCK_INTENSET_LFCLKSTARTED_Msk |
#if NRFX_CHECK(NRFX_CLOCK_CONFIG_LF_CAL_ENABLED)
CLOCK_INTENSET_DONE_Msk |
CLOCK_INTENSET_CTTO_Msk |
#endif
0);
#if NRFX_CHECK(NRFX_POWER_ENABLED)
nrfx_clock_irq_enabled = false;
#endif
NRFX_LOG_INFO("Module disabled.");
}
void nrfx_clock_uninit(void)
{
NRFX_ASSERT(m_clock_cb.module_initialized);
nrfx_clock_lfclk_stop();
nrfx_clock_hfclk_stop();
m_clock_cb.module_initialized = false;
NRFX_LOG_INFO("Uninitialized.");
}
void nrfx_clock_lfclk_start(void)
{
NRFX_ASSERT(m_clock_cb.module_initialized);
nrf_clock_event_clear(NRF_CLOCK_EVENT_LFCLKSTARTED);
nrf_clock_int_enable(NRF_CLOCK_INT_LF_STARTED_MASK);
#if NRFX_CHECK(USE_WORKAROUND_FOR_ANOMALY_132)
nrfx_clock_anomaly_132();
#endif
nrf_clock_task_trigger(NRF_CLOCK_TASK_LFCLKSTART);
}
void nrfx_clock_lfclk_stop(void)
{
NRFX_ASSERT(m_clock_cb.module_initialized);
nrf_clock_int_disable(NRF_CLOCK_INT_LF_STARTED_MASK);
nrf_clock_event_clear(NRF_CLOCK_EVENT_LFCLKSTARTED);
nrf_clock_task_trigger(NRF_CLOCK_TASK_LFCLKSTOP);
while (nrf_clock_lf_is_running())
{}
}
void nrfx_clock_hfclk_start(void)
{
NRFX_ASSERT(m_clock_cb.module_initialized);
nrf_clock_event_clear(NRF_CLOCK_EVENT_HFCLKSTARTED);
nrf_clock_int_enable(NRF_CLOCK_INT_HF_STARTED_MASK);
nrf_clock_task_trigger(NRF_CLOCK_TASK_HFCLKSTART);
}
void nrfx_clock_hfclk_stop(void)
{
NRFX_ASSERT(m_clock_cb.module_initialized);
nrf_clock_int_disable(NRF_CLOCK_INT_HF_STARTED_MASK);
nrf_clock_event_clear(NRF_CLOCK_EVENT_HFCLKSTARTED);
nrf_clock_task_trigger(NRF_CLOCK_TASK_HFCLKSTOP);
while (nrf_clock_hf_is_running(NRF_CLOCK_HFCLK_HIGH_ACCURACY))
{}
#if NRFX_CHECK(USE_WORKAROUND_FOR_ANOMALY_201)
m_clock_cb.hfclk_started = false;
#endif
}
nrfx_err_t nrfx_clock_calibration_start(void)
{
nrfx_err_t err_code = NRFX_SUCCESS;
#if NRFX_CHECK(NRFX_CLOCK_CONFIG_LF_CAL_ENABLED)
if (nrfx_clock_hfclk_is_running() == false)
{
return NRFX_ERROR_INVALID_STATE;
}
if (nrfx_clock_lfclk_is_running() == false)
{
return NRFX_ERROR_INVALID_STATE;
}
if (m_clock_cb.cal_state == CAL_STATE_IDLE)
{
nrf_clock_event_clear(NRF_CLOCK_EVENT_DONE);
nrf_clock_int_enable(NRF_CLOCK_INT_DONE_MASK);
m_clock_cb.cal_state = CAL_STATE_CAL;
#if NRFX_CHECK(USE_WORKAROUND_FOR_ANOMALY_192)
*(volatile uint32_t *)0x40000C34 = 0x00000002;
#endif
nrf_clock_task_trigger(NRF_CLOCK_TASK_CAL);
}
else
{
err_code = NRFX_ERROR_BUSY;
}
#endif // NRFX_CHECK(NRFX_CLOCK_CONFIG_LF_CAL_ENABLED)
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
nrfx_err_t nrfx_clock_is_calibrating(void)
{
#if NRFX_CHECK(NRFX_CLOCK_CONFIG_LF_CAL_ENABLED)
if (m_clock_cb.cal_state == CAL_STATE_CAL)
{
return NRFX_ERROR_BUSY;
}
#endif
return NRFX_SUCCESS;
}
void nrfx_clock_calibration_timer_start(uint8_t interval)
{
#if NRFX_CHECK(NRFX_CLOCK_CONFIG_LF_CAL_ENABLED)
nrf_clock_cal_timer_timeout_set(interval);
nrf_clock_event_clear(NRF_CLOCK_EVENT_CTTO);
nrf_clock_int_enable(NRF_CLOCK_INT_CTTO_MASK);
nrf_clock_task_trigger(NRF_CLOCK_TASK_CTSTART);
#endif
}
void nrfx_clock_calibration_timer_stop(void)
{
#if NRFX_CHECK(NRFX_CLOCK_CONFIG_LF_CAL_ENABLED)
nrf_clock_int_disable(NRF_CLOCK_INT_CTTO_MASK);
nrf_clock_task_trigger(NRF_CLOCK_TASK_CTSTOP);
#endif
}
void nrfx_clock_irq_handler(void)
{
if (nrf_clock_event_check(NRF_CLOCK_EVENT_HFCLKSTARTED))
{
nrf_clock_event_clear(NRF_CLOCK_EVENT_HFCLKSTARTED);
NRFX_LOG_DEBUG("Event: %s.", EVT_TO_STR(NRF_CLOCK_EVENT_HFCLKSTARTED));
nrf_clock_int_disable(NRF_CLOCK_INT_HF_STARTED_MASK);
#if NRFX_CHECK(USE_WORKAROUND_FOR_ANOMALY_201)
if (!m_clock_cb.hfclk_started)
{
m_clock_cb.hfclk_started = true;
m_clock_cb.event_handler(NRFX_CLOCK_EVT_HFCLK_STARTED);
}
#else
m_clock_cb.event_handler(NRFX_CLOCK_EVT_HFCLK_STARTED);
#endif
}
if (nrf_clock_event_check(NRF_CLOCK_EVENT_LFCLKSTARTED))
{
nrf_clock_event_clear(NRF_CLOCK_EVENT_LFCLKSTARTED);
NRFX_LOG_DEBUG("Event: %s.", EVT_TO_STR(NRF_CLOCK_EVENT_LFCLKSTARTED));
nrf_clock_int_disable(NRF_CLOCK_INT_LF_STARTED_MASK);
m_clock_cb.event_handler(NRFX_CLOCK_EVT_LFCLK_STARTED);
}
#if NRFX_CHECK(NRFX_CLOCK_CONFIG_LF_CAL_ENABLED)
if (nrf_clock_event_check(NRF_CLOCK_EVENT_CTTO))
{
nrf_clock_event_clear(NRF_CLOCK_EVENT_CTTO);
NRFX_LOG_DEBUG("Event: %s.", EVT_TO_STR(NRF_CLOCK_EVENT_CTTO));
nrf_clock_int_disable(NRF_CLOCK_INT_CTTO_MASK);
m_clock_cb.event_handler(NRFX_CLOCK_EVT_CTTO);
}
if (nrf_clock_event_check(NRF_CLOCK_EVENT_DONE))
{
#if NRFX_CHECK(USE_WORKAROUND_FOR_ANOMALY_192)
*(volatile uint32_t *)0x40000C34 = 0x00000000;
#endif
nrf_clock_event_clear(NRF_CLOCK_EVENT_DONE);
NRFX_LOG_DEBUG("Event: %s.", EVT_TO_STR(NRF_CLOCK_EVENT_DONE));
nrf_clock_int_disable(NRF_CLOCK_INT_DONE_MASK);
m_clock_cb.cal_state = CAL_STATE_IDLE;
m_clock_cb.event_handler(NRFX_CLOCK_EVT_CAL_DONE);
}
#endif // NRFX_CHECK(NRFX_CLOCK_CONFIG_LF_CAL_ENABLED)
}
#endif // NRFX_CHECK(NRFX_CLOCK_ENABLED)

View File

@@ -0,0 +1,212 @@
/**
* Copyright (c) 2015 - 2021, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <nrfx.h>
#if NRFX_CHECK(NRFX_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_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);
nrf_comp_enable();
nrf_comp_task_trigger(NRF_COMP_TASK_STOP);
// 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);
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 - 2021, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <nrfx.h>
#if NRFX_CHECK(NRFX_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,937 @@
/**
* Copyright (c) 2015 - 2021, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <nrfx.h>
#if NRFX_CHECK(NRFX_GPIOTE_ENABLED)
#include <nrfx_gpiote.h>
#include "nrf_bitmask.h"
#include <string.h>
#define NRFX_LOG_MODULE GPIOTE
#include <nrfx_log.h>
#if (GPIO_COUNT == 1)
#define MAX_PIN_NUMBER 32
#elif (GPIO_COUNT == 2)
#define MAX_PIN_NUMBER (32 + P1_PIN_NUM)
#else
#error "Not supported."
#endif
#define FORBIDDEN_HANDLER_ADDRESS ((nrfx_gpiote_evt_handler_t)UINT32_MAX)
#define PIN_NOT_USED (-1)
#define PIN_USED (-2)
#define NO_CHANNELS (-1)
#define POLARITY_FIELD_POS (6)
#define POLARITY_FIELD_MASK (0xC0)
/* Check if every pin can be encoded on provided number of bits. */
NRFX_STATIC_ASSERT(MAX_PIN_NUMBER <= (1 << POLARITY_FIELD_POS));
/**
* @brief Macro for converting task-event index to an address of an event register.
*
* Macro utilizes the fact that registers are grouped together in ascending order.
*/
#define TE_IDX_TO_EVENT_ADDR(idx) (nrf_gpiote_events_t)((uint32_t)NRF_GPIOTE_EVENTS_IN_0 + \
(sizeof(uint32_t) * (idx)))
/**
* @brief Macro for converting task-event index of OUT task to an address of a task register.
*
* Macro utilizes the fact that registers are grouped together in ascending order.
*/
#define TE_OUT_IDX_TO_TASK_ADDR(idx) (nrf_gpiote_tasks_t)((uint32_t)NRF_GPIOTE_TASKS_OUT_0 + \
(sizeof(uint32_t) * (idx)))
#if defined(GPIOTE_FEATURE_SET_PRESENT) || defined(__NRFX_DOXYGEN__)
/**
* @brief Macro for converting task-event index of SET task to an address of a task register.
*
* Macro utilizes the fact that registers are grouped together in ascending order.
*/
#define TE_SET_IDX_TO_TASK_ADDR(idx) (nrf_gpiote_tasks_t)((uint32_t)NRF_GPIOTE_TASKS_SET_0 + \
(sizeof(uint32_t) * (idx)))
#endif // defined(GPIOTE_FEATURE_SET_PRESENT) || defined(__NRFX_DOXYGEN__)
#if defined(GPIOTE_FEATURE_CLR_PRESENT) || defined(__NRFX_DOXYGEN__)
/**
* @brief Macro for converting task-event index of CLR task to an address of a task register.
*
* Macro utilizes the fact that registers are grouped together in ascending order.
*/
#define TE_CLR_IDX_TO_TASK_ADDR(idx) (nrf_gpiote_tasks_t)((uint32_t)NRF_GPIOTE_TASKS_CLR_0 + \
(sizeof(uint32_t) * (idx)))
#endif // defined(GPIOTE_FEATURE_CLR_PRESENT) || defined(__NRFX_DOXYGEN__)
/*lint -save -e571*/ /* Suppress "Warning 571: Suspicious cast" */
typedef struct
{
nrfx_gpiote_evt_handler_t handlers[GPIOTE_CH_NUM + NRFX_GPIOTE_CONFIG_NUM_OF_LOW_POWER_EVENTS];
int8_t pin_assignments[MAX_PIN_NUMBER];
int8_t port_handlers_pins[NRFX_GPIOTE_CONFIG_NUM_OF_LOW_POWER_EVENTS];
uint8_t configured_pins[((MAX_PIN_NUMBER)+7) / 8];
nrfx_drv_state_t state;
} gpiote_control_block_t;
static gpiote_control_block_t m_cb;
__STATIC_INLINE bool pin_in_use(uint32_t pin)
{
return (m_cb.pin_assignments[pin] != PIN_NOT_USED);
}
__STATIC_INLINE bool pin_in_use_as_non_task_out(uint32_t pin)
{
return (m_cb.pin_assignments[pin] == PIN_USED);
}
__STATIC_INLINE bool pin_in_use_by_te(uint32_t pin)
{
return (m_cb.pin_assignments[pin] >= 0 && m_cb.pin_assignments[pin] < GPIOTE_CH_NUM) ?
true : false;
}
__STATIC_INLINE bool pin_in_use_by_port(uint32_t pin)
{
return (m_cb.pin_assignments[pin] >= GPIOTE_CH_NUM);
}
__STATIC_INLINE bool pin_in_use_by_gpiote(uint32_t pin)
{
return (m_cb.pin_assignments[pin] >= 0);
}
__STATIC_INLINE void pin_in_use_by_te_set(uint32_t pin,
uint32_t channel_id,
nrfx_gpiote_evt_handler_t handler,
bool is_channel)
{
m_cb.pin_assignments[pin] = channel_id;
m_cb.handlers[channel_id] = handler;
if (!is_channel)
{
m_cb.port_handlers_pins[channel_id - GPIOTE_CH_NUM] = (int8_t)pin;
}
}
__STATIC_INLINE void pin_in_use_set(uint32_t pin)
{
m_cb.pin_assignments[pin] = PIN_USED;
}
__STATIC_INLINE void pin_in_use_clear(uint32_t pin)
{
m_cb.pin_assignments[pin] = PIN_NOT_USED;
}
__STATIC_INLINE void pin_configured_set(uint32_t pin)
{
nrf_bitmask_bit_set(pin, m_cb.configured_pins);
}
__STATIC_INLINE void pin_configured_clear(uint32_t pin)
{
nrf_bitmask_bit_clear(pin, m_cb.configured_pins);
}
__STATIC_INLINE bool pin_configured_check(uint32_t pin)
{
return 0 != nrf_bitmask_bit_is_set(pin, m_cb.configured_pins);
}
__STATIC_INLINE int8_t channel_port_get(uint32_t pin)
{
return m_cb.pin_assignments[pin];
}
__STATIC_INLINE nrfx_gpiote_evt_handler_t channel_handler_get(uint32_t channel)
{
return m_cb.handlers[channel];
}
static nrfx_gpiote_pin_t port_handler_pin_get(uint32_t handler_idx)
{
uint8_t pin_and_polarity = (uint8_t)m_cb.port_handlers_pins[handler_idx];
return (nrfx_gpiote_pin_t)(pin_and_polarity & ~POLARITY_FIELD_MASK);
}
static nrf_gpiote_polarity_t port_handler_polarity_get(uint32_t handler_idx)
{
uint8_t pin_and_polarity = (uint8_t)m_cb.port_handlers_pins[handler_idx];
return (nrf_gpiote_polarity_t)((pin_and_polarity & POLARITY_FIELD_MASK) >> POLARITY_FIELD_POS);
}
static int8_t channel_port_alloc(uint32_t pin, nrfx_gpiote_evt_handler_t handler, bool channel)
{
int8_t channel_id = NO_CHANNELS;
uint32_t i;
uint32_t start_idx = channel ? 0 : GPIOTE_CH_NUM;
uint32_t end_idx =
channel ? GPIOTE_CH_NUM : (GPIOTE_CH_NUM + NRFX_GPIOTE_CONFIG_NUM_OF_LOW_POWER_EVENTS);
// critical section
for (i = start_idx; i < end_idx; i++)
{
if (m_cb.handlers[i] == FORBIDDEN_HANDLER_ADDRESS)
{
pin_in_use_by_te_set(pin, i, handler, channel);
channel_id = i;
break;
}
}
// critical section
return channel_id;
}
static void channel_free(uint8_t channel_id)
{
m_cb.handlers[channel_id] = FORBIDDEN_HANDLER_ADDRESS;
if (channel_id >= GPIOTE_CH_NUM)
{
m_cb.port_handlers_pins[channel_id - GPIOTE_CH_NUM] = (int8_t)PIN_NOT_USED;
}
}
nrfx_err_t nrfx_gpiote_init(void)
{
nrfx_err_t err_code;
if (m_cb.state != NRFX_DRV_STATE_UNINITIALIZED)
{
err_code = NRFX_ERROR_INVALID_STATE;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
uint8_t i;
for (i = 0; i < MAX_PIN_NUMBER; i++)
{
if (nrf_gpio_pin_present_check(i))
{
pin_in_use_clear(i);
}
}
for (i = 0; i < (GPIOTE_CH_NUM + NRFX_GPIOTE_CONFIG_NUM_OF_LOW_POWER_EVENTS); i++)
{
channel_free(i);
}
memset(m_cb.configured_pins, 0, sizeof(m_cb.configured_pins));
NRFX_IRQ_PRIORITY_SET(nrfx_get_irq_number(NRF_GPIOTE), NRFX_GPIOTE_CONFIG_IRQ_PRIORITY);
NRFX_IRQ_ENABLE(nrfx_get_irq_number(NRF_GPIOTE));
nrf_gpiote_event_clear(NRF_GPIOTE_EVENTS_PORT);
nrf_gpiote_int_enable(GPIOTE_INTENSET_PORT_Msk);
m_cb.state = NRFX_DRV_STATE_INITIALIZED;
err_code = NRFX_SUCCESS;
NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
bool nrfx_gpiote_is_init(void)
{
return (m_cb.state != NRFX_DRV_STATE_UNINITIALIZED) ? true : false;
}
void nrfx_gpiote_uninit(void)
{
NRFX_ASSERT(m_cb.state != NRFX_DRV_STATE_UNINITIALIZED);
uint32_t i;
for (i = 0; i < MAX_PIN_NUMBER; i++)
{
if (nrf_gpio_pin_present_check(i))
{
if (pin_in_use_as_non_task_out(i))
{
nrfx_gpiote_out_uninit(i);
}
else if (pin_in_use_by_gpiote(i))
{
/* Disable gpiote_in is having the same effect on out pin as gpiote_out_uninit on
* so it can be called on all pins used by GPIOTE.
*/
nrfx_gpiote_in_uninit(i);
}
}
}
m_cb.state = NRFX_DRV_STATE_UNINITIALIZED;
NRFX_LOG_INFO("Uninitialized.");
}
nrfx_err_t nrfx_gpiote_out_init(nrfx_gpiote_pin_t pin,
nrfx_gpiote_out_config_t const * p_config)
{
NRFX_ASSERT(nrf_gpio_pin_present_check(pin));
NRFX_ASSERT(m_cb.state == NRFX_DRV_STATE_INITIALIZED);
NRFX_ASSERT(p_config);
nrfx_err_t err_code = NRFX_SUCCESS;
if (pin_in_use(pin))
{
err_code = NRFX_ERROR_INVALID_STATE;
}
else
{
if (p_config->task_pin)
{
int8_t channel = channel_port_alloc(pin, NULL, true);
if (channel != NO_CHANNELS)
{
nrf_gpiote_task_configure((uint32_t)channel,
pin,
p_config->action,
p_config->init_state);
}
else
{
err_code = NRFX_ERROR_NO_MEM;
}
}
else
{
pin_in_use_set(pin);
}
if (err_code == NRFX_SUCCESS)
{
if (p_config->init_state == NRF_GPIOTE_INITIAL_VALUE_HIGH)
{
nrf_gpio_pin_set(pin);
}
else
{
nrf_gpio_pin_clear(pin);
}
nrf_gpio_cfg_output(pin);
pin_configured_set(pin);
}
}
NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
void nrfx_gpiote_out_uninit(nrfx_gpiote_pin_t pin)
{
NRFX_ASSERT(nrf_gpio_pin_present_check(pin));
NRFX_ASSERT(pin_in_use(pin));
if (pin_in_use_by_te(pin))
{
channel_free((uint8_t)channel_port_get(pin));
nrf_gpiote_te_default((uint32_t)channel_port_get(pin));
}
pin_in_use_clear(pin);
if (pin_configured_check(pin))
{
nrf_gpio_cfg_default(pin);
pin_configured_clear(pin);
}
}
void nrfx_gpiote_out_set(nrfx_gpiote_pin_t pin)
{
NRFX_ASSERT(nrf_gpio_pin_present_check(pin));
NRFX_ASSERT(pin_in_use(pin));
NRFX_ASSERT(!pin_in_use_by_te(pin));
nrf_gpio_pin_set(pin);
}
void nrfx_gpiote_out_clear(nrfx_gpiote_pin_t pin)
{
NRFX_ASSERT(nrf_gpio_pin_present_check(pin));
NRFX_ASSERT(pin_in_use(pin));
NRFX_ASSERT(!pin_in_use_by_te(pin));
nrf_gpio_pin_clear(pin);
}
void nrfx_gpiote_out_toggle(nrfx_gpiote_pin_t pin)
{
NRFX_ASSERT(nrf_gpio_pin_present_check(pin));
NRFX_ASSERT(pin_in_use(pin));
NRFX_ASSERT(!pin_in_use_by_te(pin));
nrf_gpio_pin_toggle(pin);
}
void nrfx_gpiote_out_task_enable(nrfx_gpiote_pin_t pin)
{
NRFX_ASSERT(nrf_gpio_pin_present_check(pin));
NRFX_ASSERT(pin_in_use(pin));
NRFX_ASSERT(pin_in_use_by_te(pin));
nrf_gpiote_task_enable((uint32_t)m_cb.pin_assignments[pin]);
}
void nrfx_gpiote_out_task_disable(nrfx_gpiote_pin_t pin)
{
NRFX_ASSERT(nrf_gpio_pin_present_check(pin));
NRFX_ASSERT(pin_in_use(pin));
NRFX_ASSERT(pin_in_use_by_te(pin));
nrf_gpiote_task_disable((uint32_t)m_cb.pin_assignments[pin]);
}
nrf_gpiote_tasks_t nrfx_gpiote_out_task_get(nrfx_gpiote_pin_t pin)
{
NRFX_ASSERT(nrf_gpio_pin_present_check(pin));
NRFX_ASSERT(pin_in_use_by_te(pin));
return TE_OUT_IDX_TO_TASK_ADDR((uint32_t)channel_port_get(pin));
}
uint32_t nrfx_gpiote_out_task_addr_get(nrfx_gpiote_pin_t pin)
{
nrf_gpiote_tasks_t task = nrfx_gpiote_out_task_get(pin);
return nrf_gpiote_task_addr_get(task);
}
#if defined(GPIOTE_FEATURE_SET_PRESENT)
nrf_gpiote_tasks_t nrfx_gpiote_set_task_get(nrfx_gpiote_pin_t pin)
{
NRFX_ASSERT(nrf_gpio_pin_present_check(pin));
NRFX_ASSERT(pin_in_use_by_te(pin));
return TE_SET_IDX_TO_TASK_ADDR((uint32_t)channel_port_get(pin));
}
uint32_t nrfx_gpiote_set_task_addr_get(nrfx_gpiote_pin_t pin)
{
nrf_gpiote_tasks_t task = nrfx_gpiote_set_task_get(pin);
return nrf_gpiote_task_addr_get(task);
}
#endif // defined(GPIOTE_FEATURE_SET_PRESENT)
#if defined(GPIOTE_FEATURE_CLR_PRESENT)
nrf_gpiote_tasks_t nrfx_gpiote_clr_task_get(nrfx_gpiote_pin_t pin)
{
NRFX_ASSERT(nrf_gpio_pin_present_check(pin));
NRFX_ASSERT(pin_in_use_by_te(pin));
return TE_CLR_IDX_TO_TASK_ADDR((uint32_t)channel_port_get(pin));
}
uint32_t nrfx_gpiote_clr_task_addr_get(nrfx_gpiote_pin_t pin)
{
nrf_gpiote_tasks_t task = nrfx_gpiote_clr_task_get(pin);
return nrf_gpiote_task_addr_get(task);
}
#endif // defined(GPIOTE_FEATURE_CLR_PRESENT)
void nrfx_gpiote_out_task_force(nrfx_gpiote_pin_t pin, uint8_t state)
{
NRFX_ASSERT(nrf_gpio_pin_present_check(pin));
NRFX_ASSERT(pin_in_use(pin));
NRFX_ASSERT(pin_in_use_by_te(pin));
nrf_gpiote_outinit_t init_val =
state ? NRF_GPIOTE_INITIAL_VALUE_HIGH : NRF_GPIOTE_INITIAL_VALUE_LOW;
nrf_gpiote_task_force((uint32_t)m_cb.pin_assignments[pin], init_val);
}
void nrfx_gpiote_out_task_trigger(nrfx_gpiote_pin_t pin)
{
NRFX_ASSERT(nrf_gpio_pin_present_check(pin));
NRFX_ASSERT(pin_in_use(pin));
NRFX_ASSERT(pin_in_use_by_te(pin));
nrf_gpiote_tasks_t task = TE_OUT_IDX_TO_TASK_ADDR((uint32_t)channel_port_get(pin));
nrf_gpiote_task_set(task);
}
#if defined(GPIOTE_FEATURE_SET_PRESENT)
void nrfx_gpiote_set_task_trigger(nrfx_gpiote_pin_t pin)
{
NRFX_ASSERT(nrf_gpio_pin_present_check(pin));
NRFX_ASSERT(pin_in_use(pin));
NRFX_ASSERT(pin_in_use_by_te(pin));
nrf_gpiote_tasks_t task = TE_SET_IDX_TO_TASK_ADDR((uint32_t)channel_port_get(pin));
nrf_gpiote_task_set(task);
}
#endif // defined(GPIOTE_FEATURE_SET_PRESENT)
#if defined(GPIOTE_FEATURE_CLR_PRESENT)
void nrfx_gpiote_clr_task_trigger(nrfx_gpiote_pin_t pin)
{
NRFX_ASSERT(nrf_gpio_pin_present_check(pin));
NRFX_ASSERT(pin_in_use(pin));
NRFX_ASSERT(pin_in_use_by_te(pin));
nrf_gpiote_tasks_t task = TE_CLR_IDX_TO_TASK_ADDR((uint32_t)channel_port_get(pin));
nrf_gpiote_task_set(task);
}
#endif // defined(GPIOTE_FEATURE_CLR_PRESENT)
nrfx_err_t nrfx_gpiote_in_init(nrfx_gpiote_pin_t pin,
nrfx_gpiote_in_config_t const * p_config,
nrfx_gpiote_evt_handler_t evt_handler)
{
NRFX_ASSERT(nrf_gpio_pin_present_check(pin));
nrfx_err_t err_code = NRFX_SUCCESS;
/* Only one GPIOTE channel can be assigned to one physical pin. */
if (pin_in_use_by_gpiote(pin))
{
err_code = NRFX_ERROR_INVALID_STATE;
}
else
{
int8_t channel = channel_port_alloc(pin, evt_handler, p_config->hi_accuracy);
if (channel != NO_CHANNELS)
{
if (!p_config->skip_gpio_setup)
{
if (p_config->is_watcher)
{
nrf_gpio_cfg_watcher(pin);
}
else
{
nrf_gpio_cfg_input(pin, p_config->pull);
}
pin_configured_set(pin);
}
if (p_config->hi_accuracy)
{
nrf_gpiote_event_configure((uint32_t)channel, pin, p_config->sense);
}
else
{
m_cb.port_handlers_pins[channel - GPIOTE_CH_NUM] |= (p_config->sense) <<
POLARITY_FIELD_POS;
}
}
else
{
err_code = NRFX_ERROR_NO_MEM;
}
}
NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
void nrfx_gpiote_in_event_enable(nrfx_gpiote_pin_t pin, bool int_enable)
{
NRFX_ASSERT(nrf_gpio_pin_present_check(pin));
NRFX_ASSERT(pin_in_use_by_gpiote(pin));
if (pin_in_use_by_port(pin))
{
nrf_gpiote_polarity_t polarity =
port_handler_polarity_get(channel_port_get(pin) - GPIOTE_CH_NUM);
nrf_gpio_pin_sense_t sense;
if (polarity == NRF_GPIOTE_POLARITY_TOGGLE)
{
/* read current pin state and set for next sense to oposit */
sense = (nrf_gpio_pin_read(pin)) ?
NRF_GPIO_PIN_SENSE_LOW : NRF_GPIO_PIN_SENSE_HIGH;
}
else
{
sense = (polarity == NRF_GPIOTE_POLARITY_LOTOHI) ?
NRF_GPIO_PIN_SENSE_HIGH : NRF_GPIO_PIN_SENSE_LOW;
}
nrf_gpio_cfg_sense_set(pin, sense);
}
else if (pin_in_use_by_te(pin))
{
int32_t channel = (int32_t)channel_port_get(pin);
nrf_gpiote_events_t event = TE_IDX_TO_EVENT_ADDR((uint32_t)channel);
nrf_gpiote_event_enable((uint32_t)channel);
nrf_gpiote_event_clear(event);
if (int_enable)
{
nrfx_gpiote_evt_handler_t handler = channel_handler_get((uint32_t)channel_port_get(pin));
// Enable the interrupt only if event handler was provided.
if (handler)
{
nrf_gpiote_int_enable(1 << channel);
}
}
}
}
void nrfx_gpiote_in_event_disable(nrfx_gpiote_pin_t pin)
{
NRFX_ASSERT(nrf_gpio_pin_present_check(pin));
NRFX_ASSERT(pin_in_use_by_gpiote(pin));
if (pin_in_use_by_port(pin))
{
nrf_gpio_cfg_sense_set(pin, NRF_GPIO_PIN_NOSENSE);
}
else if (pin_in_use_by_te(pin))
{
int32_t channel = (int32_t)channel_port_get(pin);
nrf_gpiote_event_disable((uint32_t)channel);
nrf_gpiote_int_disable(1 << channel);
}
}
void nrfx_gpiote_in_uninit(nrfx_gpiote_pin_t pin)
{
NRFX_ASSERT(nrf_gpio_pin_present_check(pin));
NRFX_ASSERT(pin_in_use_by_gpiote(pin));
nrfx_gpiote_in_event_disable(pin);
if (pin_in_use_by_te(pin))
{
nrf_gpiote_te_default((uint32_t)channel_port_get(pin));
}
if (pin_configured_check(pin))
{
nrf_gpio_cfg_default(pin);
pin_configured_clear(pin);
}
if (pin_in_use_by_gpiote(pin))
{
channel_free((uint8_t)channel_port_get(pin));
}
pin_in_use_clear(pin);
}
bool nrfx_gpiote_in_is_set(nrfx_gpiote_pin_t pin)
{
NRFX_ASSERT(nrf_gpio_pin_present_check(pin));
return nrf_gpio_pin_read(pin) ? true : false;
}
nrf_gpiote_events_t nrfx_gpiote_in_event_get(nrfx_gpiote_pin_t pin)
{
NRFX_ASSERT(nrf_gpio_pin_present_check(pin));
NRFX_ASSERT(pin_in_use_by_port(pin) || pin_in_use_by_te(pin));
if (pin_in_use_by_te(pin))
{
return TE_IDX_TO_EVENT_ADDR((uint32_t)channel_port_get(pin));
}
return NRF_GPIOTE_EVENTS_PORT;
}
uint32_t nrfx_gpiote_in_event_addr_get(nrfx_gpiote_pin_t pin)
{
nrf_gpiote_events_t event = nrfx_gpiote_in_event_get(pin);
return nrf_gpiote_event_addr_get(event);
}
#if defined(NRF_GPIO_LATCH_PRESENT)
static bool latch_pending_read_and_check(uint32_t * latch)
{
nrf_gpio_latches_read_and_clear(0, GPIO_COUNT, latch);
for (uint32_t port_idx = 0; port_idx < GPIO_COUNT; port_idx++)
{
if (latch[port_idx])
{
/* If any of the latch bits is still set, it means another edge has been captured
* before or during the interrupt processing. Therefore event-processing loop
* should be executed again. */
return true;
}
}
return false;
}
static void port_event_handle(uint32_t * latch)
{
do {
for (uint32_t i = 0; i < NRFX_GPIOTE_CONFIG_NUM_OF_LOW_POWER_EVENTS; i++)
{
if (m_cb.port_handlers_pins[i] == PIN_NOT_USED)
{
continue;
}
/* Process pin further only if LATCH bit associated with this pin was set. */
nrfx_gpiote_pin_t pin = port_handler_pin_get(i);
if (nrf_bitmask_bit_is_set(pin, latch))
{
nrf_gpiote_polarity_t polarity = port_handler_polarity_get(i);
nrf_gpio_pin_sense_t sense = nrf_gpio_pin_sense_get(pin);
NRFX_LOG_DEBUG("PORT event for pin: %d, polarity: %d.", pin, polarity);
/* Reconfigure sense to the opposite level, so the internal PINx.DETECT signal
* can be deasserted. Therefore PORT event generated again,
* unless some other PINx.DETECT signal is still active. */
nrf_gpio_pin_sense_t next_sense =
(sense == NRF_GPIO_PIN_SENSE_HIGH) ? NRF_GPIO_PIN_SENSE_LOW :
NRF_GPIO_PIN_SENSE_HIGH;
nrf_gpio_cfg_sense_set(pin, next_sense);
/* Try to clear LATCH bit corresponding to currently processed pin.
* This may not succeed if the pin's state changed during the interrupt processing
* and now it matches the new sense configuration. In such case,
* the pin will be processed again in another iteration of the outer loop. */
nrf_gpio_pin_latch_clear(pin);
/* Invoke user handler only if the sensed pin level
* matches its polarity configuration. */
nrfx_gpiote_evt_handler_t handler =
channel_handler_get((uint32_t)channel_port_get(pin));
if (handler &&
((polarity == NRF_GPIOTE_POLARITY_TOGGLE) ||
(sense == NRF_GPIO_PIN_SENSE_HIGH && polarity == NRF_GPIOTE_POLARITY_LOTOHI) ||
(sense == NRF_GPIO_PIN_SENSE_LOW && polarity == NRF_GPIOTE_POLARITY_HITOLO)))
{
handler(pin, polarity);
}
}
}
} while (latch_pending_read_and_check(latch));
}
#else
static bool input_read_and_check(uint32_t * input, uint32_t * pins_to_check)
{
bool process_inputs_again;
uint32_t new_input[GPIO_COUNT];
nrf_gpio_ports_read(0, GPIO_COUNT, new_input);
process_inputs_again = false;
for (uint32_t port_idx = 0; port_idx < GPIO_COUNT; port_idx++)
{
/* Execute XOR to find out which inputs have changed. */
uint32_t input_diff = input[port_idx] ^ new_input[port_idx];
input[port_idx] = new_input[port_idx];
if (input_diff)
{
/* If any differences among inputs were found, mark those pins
* to be processed again. */
pins_to_check[port_idx] = input_diff;
process_inputs_again = true;
}
else
{
pins_to_check[port_idx] = 0;
}
}
return process_inputs_again;
}
static void port_event_handle(uint32_t * input)
{
uint32_t pins_to_check[GPIO_COUNT];
for (uint32_t port_idx = 0; port_idx < GPIO_COUNT; port_idx++)
{
pins_to_check[port_idx] = 0xFFFFFFFF;
}
do {
for (uint32_t i = 0; i < NRFX_GPIOTE_CONFIG_NUM_OF_LOW_POWER_EVENTS; i++)
{
if (m_cb.port_handlers_pins[i] == PIN_NOT_USED)
{
continue;
}
nrfx_gpiote_pin_t pin = port_handler_pin_get(i);
if (nrf_bitmask_bit_is_set(pin, pins_to_check))
{
nrf_gpiote_polarity_t polarity = port_handler_polarity_get(i);
nrf_gpio_pin_sense_t sense = nrf_gpio_pin_sense_get(pin);
bool pin_state = nrf_bitmask_bit_is_set(pin, input);
/* Process pin further only if its state matches its sense level. */
if ((pin_state && (sense == NRF_GPIO_PIN_SENSE_HIGH)) ||
(!pin_state && (sense == NRF_GPIO_PIN_SENSE_LOW)) )
{
/* Reconfigure sense to the opposite level, so the internal PINx.DETECT signal
* can be deasserted. Therefore PORT event can be generated again,
* unless some other PINx.DETECT signal is still active. */
NRFX_LOG_DEBUG("PORT event for pin: %d, polarity: %d.", pin, polarity);
nrf_gpio_pin_sense_t next_sense =
(sense == NRF_GPIO_PIN_SENSE_HIGH) ? NRF_GPIO_PIN_SENSE_LOW :
NRF_GPIO_PIN_SENSE_HIGH;
nrf_gpio_cfg_sense_set(pin, next_sense);
/* Invoke user handler only if the sensed pin level
* matches its polarity configuration. */
nrfx_gpiote_evt_handler_t handler =
channel_handler_get((uint32_t)channel_port_get(pin));
if (handler &&
((polarity == NRF_GPIOTE_POLARITY_TOGGLE) ||
(sense == NRF_GPIO_PIN_SENSE_HIGH &&
polarity == NRF_GPIOTE_POLARITY_LOTOHI) ||
(sense == NRF_GPIO_PIN_SENSE_LOW &&
polarity == NRF_GPIOTE_POLARITY_HITOLO)))
{
handler(pin, polarity);
}
}
}
}
} while (input_read_and_check(input, pins_to_check));
}
#endif // defined(NRF_GPIO_LATCH_PRESENT)
void nrfx_gpiote_irq_handler(void)
{
uint32_t status = 0;
uint32_t input[GPIO_COUNT] = {0};
/* collect status of all GPIOTE pin events. Processing is done once all are collected and cleared.*/
uint32_t i;
nrf_gpiote_events_t event = NRF_GPIOTE_EVENTS_IN_0;
uint32_t mask = (uint32_t)NRF_GPIOTE_INT_IN0_MASK;
for (i = 0; i < GPIOTE_CH_NUM; i++)
{
if (nrf_gpiote_event_is_set(event) && nrf_gpiote_int_is_enabled(mask))
{
nrf_gpiote_event_clear(event);
status |= mask;
}
mask <<= 1;
/* Incrementing to next event, utilizing the fact that events are grouped together
* in ascending order. */
event = (nrf_gpiote_events_t)((uint32_t)event + sizeof(uint32_t));
}
/* collect PORT status event, if event is set read pins state. Processing is postponed to the
* end of interrupt. */
if (nrf_gpiote_event_is_set(NRF_GPIOTE_EVENTS_PORT))
{
nrf_gpiote_event_clear(NRF_GPIOTE_EVENTS_PORT);
status |= (uint32_t)NRF_GPIOTE_INT_PORT_MASK;
#if defined(NRF_GPIO_LATCH_PRESENT)
nrf_gpio_latches_read_and_clear(0, GPIO_COUNT, input);
#else
nrf_gpio_ports_read(0, GPIO_COUNT, input);
#endif
}
/* Process pin events. */
if (status & NRF_GPIOTE_INT_IN_MASK)
{
mask = (uint32_t)NRF_GPIOTE_INT_IN0_MASK;
for (i = 0; i < GPIOTE_CH_NUM; i++)
{
if (mask & status)
{
nrfx_gpiote_pin_t pin = nrf_gpiote_event_pin_get(i);
NRFX_LOG_DEBUG("Event in number: %d.", i);
nrf_gpiote_polarity_t polarity = nrf_gpiote_event_polarity_get(i);
nrfx_gpiote_evt_handler_t handler = channel_handler_get(i);
NRFX_LOG_DEBUG("Pin: %d, polarity: %d.", pin, polarity);
if (handler)
{
handler(pin, polarity);
}
}
mask <<= 1;
}
}
/* Process PORT event. */
if (status & (uint32_t)NRF_GPIOTE_INT_PORT_MASK)
{
port_event_handle(input);
}
}
/*lint -restore*/
#endif // NRFX_CHECK(NRFX_GPIOTE_ENABLED)

View File

@@ -0,0 +1,445 @@
/**
* Copyright (c) 2015 - 2021, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <nrfx.h>
#if NRFX_CHECK(NRFX_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 - 2021, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <nrfx.h>
#if NRFX_CHECK(NRFX_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,867 @@
/**
* Copyright (c) 2018 - 2021, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <nrfx.h>
#if NRFX_CHECK(NRFX_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);
nrfx_err_t err = NRFX_SUCCESS;
if (p_tx_data->data_size == 0)
{
return NRFX_ERROR_INVALID_LENGTH;
}
NRFX_CRITICAL_SECTION_ENTER();
/* In case when NFC frame transmission has already started, it returns an error. */
if (NRFX_NFCT_EVT_ACTIVE(TXFRAMESTART))
{
err = NRFX_ERROR_BUSY;
}
else
{
/* In case when Tx operation was scheduled with delay, stop scheduled Tx operation. */
*(volatile uint32_t *)0x40005010 = 0x01;
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_frame_delay_max_set(false);
nrfx_nfct_rxtx_int_enable(NRFX_NFCT_TX_INT_MASK);
nrf_nfct_task_trigger(NRF_NFCT_TASK_STARTTX);
NRFX_LOG_INFO("Tx start");
}
NRFX_CRITICAL_SECTION_EXIT();
return err;
}
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 - 2021, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <nrfx.h>
#if NRFX_CHECK(NRFX_NVMC_ENABLED)
#include <nrfx_nvmc.h>
/**
* Value representing the number of bytes in a word.
*
* It is used in loops iterating over bytes contained in a word
* or in word-alignment checks.
*/
#define NVMC_BYTES_IN_WORD 4
/**
* Value representing non-volatile memory (NVM) page count.
*
* This symbol is needed to determine NVM page count for chips that cannot
* always access FICR for this information.
*/
#if defined(NRF9160_XXAA)
#define NVMC_FLASH_PAGE_COUNT 256
#endif
/**
* Value representing non-volatile memory (NVM) page size in bytes.
*
* This symbol is needed to determine NVM page size for chips that cannot
* always access FICR for this information.
*/
#if defined(NRF9160_XXAA)
#define NVMC_FLASH_PAGE_SIZE 0x1000 ///< 4 kB
#endif
#if defined(NRF_NVMC_PARTIAL_ERASE_PRESENT)
/**
* Value representing the page erase time.
*
* This value is used to determine whether the partial erase is still in progress.
*/
#if defined(NRF52810_XXAA) || defined(NRF52811_XXAA) || defined(NRF52840_XXAA)
#define NVMC_PAGE_ERASE_DURATION_MS 85
#elif defined(NRF52820_XXAA) || defined(NRF52833_XXAA) || defined(NRF9160_XXAA)
#define NVMC_PAGE_ERASE_DURATION_MS 87
#else
#error "Page partial erase present but could not determine its total duration for given SoC"
#endif
/**
* Value representing the invalid page partial erase address.
*
* This value is used for representing a NULL pointer for
* partial erase, as that address 0 can be a valid
* memory address in flash.
*/
#define NVMC_PARTIAL_ERASE_INVALID_ADDR 0xFFFFFFFF
/** Internal counter for page partial erase. */
static uint32_t m_partial_erase_time_elapsed;
/** Partial erase page address. */
static uint32_t m_partial_erase_page_addr = NVMC_PARTIAL_ERASE_INVALID_ADDR;
#endif // defined(NRF_NVMC_PARTIAL_ERASE_PRESENT)
static uint32_t flash_page_size_get(void)
{
uint32_t flash_page_size = 0;
#if defined(NRF51) || defined(NRF52_SERIES)
flash_page_size = nrf_ficr_codepagesize_get(NRF_FICR);
#elif defined(NVMC_FLASH_PAGE_SIZE)
flash_page_size = NVMC_FLASH_PAGE_SIZE;
#else
#error "Cannot determine flash page size for a given SoC."
#endif
return flash_page_size;
}
static uint32_t flash_page_count_get(void)
{
uint32_t page_count = 0;
#if defined(NRF51) || defined(NRF52_SERIES)
page_count = nrf_ficr_codesize_get(NRF_FICR);
#elif defined(NVMC_FLASH_PAGE_COUNT)
page_count = NVMC_FLASH_PAGE_COUNT;
#else
#error "Cannot determine flash page count for a given SoC."
#endif
return page_count;
}
static uint32_t flash_total_size_get(void)
{
return flash_page_size_get() * flash_page_count_get();
}
static bool is_page_aligned_check(uint32_t addr)
{
/* If the modulo operation returns '0', then the address is aligned. */
return !(addr % flash_page_size_get());
}
static uint32_t partial_word_create(uint32_t addr, uint8_t const * bytes, uint32_t bytes_count)
{
uint32_t value32;
uint32_t byte_shift;
byte_shift = addr % NVMC_BYTES_IN_WORD;
NRFX_ASSERT(bytes_count <= (NVMC_BYTES_IN_WORD - byte_shift));
value32 = 0xFFFFFFFF;
for (uint32_t i = 0; i < bytes_count; i++)
{
((uint8_t *)&value32)[byte_shift] = bytes[i];
byte_shift++;
}
return value32;
}
static void nvmc_readonly_mode_set(void)
{
#if defined(NRF_TRUSTZONE_NONSECURE)
nrf_nvmc_nonsecure_mode_set(NRF_NVMC, NRF_NVMC_NS_MODE_READONLY);
#else
nrf_nvmc_mode_set(NRF_NVMC, NRF_NVMC_MODE_READONLY);
#endif
}
static void nvmc_write_mode_set(void)
{
#if defined(NRF_TRUSTZONE_NONSECURE)
nrf_nvmc_nonsecure_mode_set(NRF_NVMC, NRF_NVMC_NS_MODE_WRITE);
#else
nrf_nvmc_mode_set(NRF_NVMC, NRF_NVMC_MODE_WRITE);
#endif
}
static void nvmc_erase_mode_set(void)
{
#if defined(NRF_TRUSTZONE_NONSECURE)
nrf_nvmc_nonsecure_mode_set(NRF_NVMC, NRF_NVMC_NS_MODE_ERASE);
#else
nrf_nvmc_mode_set(NRF_NVMC, NRF_NVMC_MODE_ERASE);
#endif
}
static void nvmc_word_write(uint32_t addr, uint32_t value)
{
#if defined(NRF9160_XXAA)
while (!nrf_nvmc_write_ready_check(NRF_NVMC))
{}
#else
while (!nrf_nvmc_ready_check(NRF_NVMC))
{}
#endif
*(volatile uint32_t *)addr = value;
__DMB();
}
static void nvmc_words_write(uint32_t addr, void const * src, uint32_t num_words)
{
for (uint32_t i = 0; i < num_words; i++)
{
nvmc_word_write(addr + (NVMC_BYTES_IN_WORD * i), ((uint32_t const *)src)[i]);
}
}
nrfx_err_t nrfx_nvmc_page_erase(uint32_t addr)
{
NRFX_ASSERT(addr < flash_total_size_get());
if (!is_page_aligned_check(addr))
{
return NRFX_ERROR_INVALID_ADDR;
}
nvmc_erase_mode_set();
nrf_nvmc_page_erase_start(NRF_NVMC, addr);
while (!nrf_nvmc_ready_check(NRF_NVMC))
{}
nvmc_readonly_mode_set();
return NRFX_SUCCESS;
}
nrfx_err_t nrfx_nvmc_uicr_erase(void)
{
#if defined(NVMC_ERASEUICR_ERASEUICR_Msk)
nvmc_erase_mode_set();
nrf_nvmc_uicr_erase_start(NRF_NVMC);
while (!nrf_nvmc_ready_check(NRF_NVMC))
{}
nvmc_readonly_mode_set();
return NRFX_SUCCESS;
#else
return NRFX_ERROR_NOT_SUPPORTED;
#endif
}
void nrfx_nvmc_all_erase(void)
{
nvmc_erase_mode_set();
nrf_nvmc_erase_all_start(NRF_NVMC);
while (!nrf_nvmc_ready_check(NRF_NVMC))
{}
nvmc_readonly_mode_set();
}
#if defined(NRF_NVMC_PARTIAL_ERASE_PRESENT)
nrfx_err_t nrfx_nvmc_page_partial_erase_init(uint32_t addr, uint32_t duration_ms)
{
NRFX_ASSERT(addr < flash_total_size_get());
if (!is_page_aligned_check(addr))
{
return NRFX_ERROR_INVALID_ADDR;
}
m_partial_erase_time_elapsed = 0;
m_partial_erase_page_addr = addr;
nrf_nvmc_partial_erase_duration_set(NRF_NVMC, duration_ms);
return NRFX_SUCCESS;
}
bool nrfx_nvmc_page_partial_erase_continue(void)
{
NRFX_ASSERT(m_partial_erase_page_addr != NVMC_PARTIAL_ERASE_INVALID_ADDR);
uint32_t duration_ms = nrf_nvmc_partial_erase_duration_get(NRF_NVMC);
#if defined(NVMC_CONFIG_WEN_PEen)
nrf_nvmc_mode_set(NRF_NVMC, NRF_NVMC_MODE_PARTIAL_ERASE);
#else
nrf_nvmc_mode_set(NRF_NVMC, NRF_NVMC_MODE_ERASE);
#endif
nrf_nvmc_page_partial_erase_start(NRF_NVMC, m_partial_erase_page_addr);
while (!nrf_nvmc_ready_check(NRF_NVMC))
{}
nvmc_readonly_mode_set();
m_partial_erase_time_elapsed += duration_ms;
if (m_partial_erase_time_elapsed < NVMC_PAGE_ERASE_DURATION_MS)
{
return false;
}
else
{
m_partial_erase_page_addr = NVMC_PARTIAL_ERASE_INVALID_ADDR;
return true;
}
}
#endif // defined(NRF_NVMC_PARTIAL_ERASE_PRESENT)
bool nrfx_nvmc_byte_writable_check(uint32_t addr, uint8_t val_to_check)
{
NRFX_ASSERT((addr < flash_total_size_get()) ||
((addr - (uint32_t)NRF_UICR) < sizeof(NRF_UICR_Type)));
uint8_t val_on_addr = *(uint8_t const *)addr;
return (val_to_check & val_on_addr) == val_to_check;
}
bool nrfx_nvmc_word_writable_check(uint32_t addr, uint32_t val_to_check)
{
NRFX_ASSERT((addr < flash_total_size_get()) ||
((addr - (uint32_t)NRF_UICR) < sizeof(NRF_UICR_Type)));
NRFX_ASSERT(nrfx_is_word_aligned((void const *)addr));
uint32_t val_on_addr = *(uint32_t const *)addr;
return (val_to_check & val_on_addr) == val_to_check;
}
void nrfx_nvmc_byte_write(uint32_t addr, uint8_t value)
{
uint32_t aligned_addr = addr & ~(0x03UL);
nrfx_nvmc_word_write(aligned_addr, partial_word_create(addr, &value, 1));
}
void nrfx_nvmc_word_write(uint32_t addr, uint32_t value)
{
NRFX_ASSERT((addr < flash_total_size_get()) ||
((addr - (uint32_t)NRF_UICR) < sizeof(NRF_UICR_Type)));
NRFX_ASSERT(nrfx_is_word_aligned((void const *)addr));
nvmc_write_mode_set();
nvmc_word_write(addr, value);
nvmc_readonly_mode_set();
}
void nrfx_nvmc_bytes_write(uint32_t addr, void const * src, uint32_t num_bytes)
{
NRFX_ASSERT((addr < flash_total_size_get()) ||
((addr - (uint32_t)NRF_UICR) < sizeof(NRF_UICR_Type)));
nvmc_write_mode_set();
uint8_t const * bytes_src = (uint8_t const *)src;
uint32_t unaligned_bytes = addr % NVMC_BYTES_IN_WORD;
if (unaligned_bytes != 0)
{
uint32_t leading_bytes = NVMC_BYTES_IN_WORD - unaligned_bytes;
if (leading_bytes > num_bytes)
{
leading_bytes = num_bytes;
}
nvmc_word_write(addr - unaligned_bytes,
partial_word_create(addr, bytes_src, leading_bytes));
num_bytes -= leading_bytes;
addr += leading_bytes;
bytes_src += leading_bytes;
}
#if defined(__CORTEX_M) && (__CORTEX_M == 0U)
if (!nrfx_is_word_aligned((void const *)bytes_src))
{
/* Cortex-M0 allows only word-aligned RAM access.
If source address is not word-aligned, bytes are combined
into words explicitly. */
for (uint32_t i = 0; i < num_bytes / NVMC_BYTES_IN_WORD; i++)
{
uint32_t word = (uint32_t)bytes_src[0]
| ((uint32_t)bytes_src[1]) << 8
| ((uint32_t)bytes_src[2]) << 16
| ((uint32_t)bytes_src[3]) << 24;
nvmc_word_write(addr, word);
bytes_src += NVMC_BYTES_IN_WORD;
addr += NVMC_BYTES_IN_WORD;
}
}
else
#endif
{
uint32_t word_count = num_bytes / NVMC_BYTES_IN_WORD;
nvmc_words_write(addr, (uint32_t const *)bytes_src, word_count);
addr += word_count * NVMC_BYTES_IN_WORD;
bytes_src += word_count * NVMC_BYTES_IN_WORD;
}
uint32_t trailing_bytes = num_bytes % NVMC_BYTES_IN_WORD;
if (trailing_bytes != 0)
{
nvmc_word_write(addr, partial_word_create(addr, bytes_src, trailing_bytes));
}
nvmc_readonly_mode_set();
}
void nrfx_nvmc_words_write(uint32_t addr, void const * src, uint32_t num_words)
{
NRFX_ASSERT((addr < flash_total_size_get()) ||
((addr - (uint32_t)NRF_UICR) < sizeof(NRF_UICR_Type)));
NRFX_ASSERT(nrfx_is_word_aligned((void const *)addr));
NRFX_ASSERT(nrfx_is_word_aligned(src));
nvmc_write_mode_set();
nvmc_words_write(addr, src, num_words);
nvmc_readonly_mode_set();
}
uint32_t nrfx_nvmc_flash_size_get(void)
{
return flash_total_size_get();
}
uint32_t nrfx_nvmc_flash_page_size_get(void)
{
return flash_page_size_get();
}
uint32_t nrfx_nvmc_flash_page_count_get(void)
{
return flash_page_count_get();
}
#endif // NRFX_CHECK(NRFX_NVMC_ENABLED)

View File

@@ -0,0 +1,370 @@
/**
* Copyright (c) 2015 - 2021, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <nrfx.h>
#if NRFX_CHECK(NRFX_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 - 2021, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <nrfx.h>
#if NRFX_CHECK(NRFX_POWER_ENABLED)
#include <nrfx_power.h>
#if defined(REGULATORS_PRESENT)
#include <hal/nrf_regulators.h>
#endif
#if NRFX_CHECK(NRFX_CLOCK_ENABLED)
extern bool nrfx_clock_irq_enabled;
extern void nrfx_clock_irq_handler(void);
#endif
/**
* @internal
* @defgroup nrfx_power_internals POWER driver internals
* @ingroup nrfx_power
*
* Internal variables, auxiliary macros and functions of POWER driver.
* @{
*/
/**
* This variable is used to check whether common POWER_CLOCK common interrupt
* should be disabled or not if @ref nrfx_clock tries to disable the interrupt.
*/
bool nrfx_power_irq_enabled;
/**
* @brief The initialization flag
*/
#define m_initialized nrfx_power_irq_enabled
/**
* @brief The handler of power fail comparator warning event
*/
static nrfx_power_pofwarn_event_handler_t m_pofwarn_handler;
#if NRF_POWER_HAS_SLEEPEVT
/**
* @brief The handler of sleep event handler
*/
static nrfx_power_sleep_event_handler_t m_sleepevt_handler;
#endif
#if NRF_POWER_HAS_USBREG
/**
* @brief The handler of USB power events
*/
static nrfx_power_usb_event_handler_t m_usbevt_handler;
#endif
/** @} */
nrfx_power_pofwarn_event_handler_t nrfx_power_pof_handler_get(void)
{
return m_pofwarn_handler;
}
#if NRF_POWER_HAS_USBREG
nrfx_power_usb_event_handler_t nrfx_power_usb_handler_get(void)
{
return m_usbevt_handler;
}
#endif
nrfx_err_t nrfx_power_init(nrfx_power_config_t const * p_config)
{
NRFX_ASSERT(p_config);
if (m_initialized)
{
return NRFX_ERROR_ALREADY_INITIALIZED;
}
#if NRF_POWER_HAS_DCDCEN_VDDH
nrf_power_dcdcen_vddh_set(p_config->dcdcenhv);
#endif
#if NRF_POWER_HAS_DCDCEN
nrf_power_dcdcen_set(p_config->dcdcen);
#else
nrf_regulators_dcdcen_set(NRF_REGULATORS, p_config->dcdcen);
#endif
nrfx_power_clock_irq_init();
m_initialized = true;
return NRFX_SUCCESS;
}
void nrfx_power_uninit(void)
{
NRFX_ASSERT(m_initialized);
#if NRFX_CHECK(NRFX_CLOCK_ENABLED)
if (!nrfx_clock_irq_enabled)
#endif
{
NRFX_IRQ_DISABLE(nrfx_get_irq_number(NRF_POWER));
}
#if NRF_POWER_HAS_POFCON
nrfx_power_pof_uninit();
#endif
#if NRF_POWER_HAS_SLEEPEVT
nrfx_power_sleepevt_uninit();
#endif
#if NRF_POWER_HAS_USBREG
nrfx_power_usbevt_uninit();
#endif
m_initialized = false;
}
#if NRF_POWER_HAS_POFCON
void nrfx_power_pof_init(nrfx_power_pofwarn_config_t const * p_config)
{
NRFX_ASSERT(p_config != NULL);
nrfx_power_pof_uninit();
if (p_config->handler != NULL)
{
m_pofwarn_handler = p_config->handler;
}
}
void nrfx_power_pof_enable(nrfx_power_pofwarn_config_t const * p_config)
{
nrf_power_pofcon_set(true, p_config->thr);
#if NRF_POWER_HAS_VDDH
nrf_power_pofcon_vddh_set(p_config->thrvddh);
#endif
if (m_pofwarn_handler != NULL)
{
nrf_power_int_enable(NRF_POWER_INT_POFWARN_MASK);
}
}
void nrfx_power_pof_disable(void)
{
nrf_power_pofcon_set(false, NRF_POWER_POFTHR_V27);
nrf_power_int_disable(NRF_POWER_INT_POFWARN_MASK);
}
void nrfx_power_pof_uninit(void)
{
m_pofwarn_handler = NULL;
}
#endif // NRF_POWER_HAS_POFCON
#if NRF_POWER_HAS_SLEEPEVT
void nrfx_power_sleepevt_init(nrfx_power_sleepevt_config_t const * p_config)
{
NRFX_ASSERT(p_config != NULL);
nrfx_power_sleepevt_uninit();
if (p_config->handler != NULL)
{
m_sleepevt_handler = p_config->handler;
}
}
void nrfx_power_sleepevt_enable(nrfx_power_sleepevt_config_t const * p_config)
{
uint32_t enmask = 0;
if (p_config->en_enter)
{
enmask |= NRF_POWER_INT_SLEEPENTER_MASK;
nrf_power_event_clear(NRF_POWER_EVENT_SLEEPENTER);
}
if (p_config->en_exit)
{
enmask |= NRF_POWER_INT_SLEEPEXIT_MASK;
nrf_power_event_clear(NRF_POWER_EVENT_SLEEPEXIT);
}
nrf_power_int_enable(enmask);
}
void nrfx_power_sleepevt_disable(void)
{
nrf_power_int_disable(
NRF_POWER_INT_SLEEPENTER_MASK |
NRF_POWER_INT_SLEEPEXIT_MASK);
}
void nrfx_power_sleepevt_uninit(void)
{
m_sleepevt_handler = NULL;
}
#endif /* NRF_POWER_HAS_SLEEPEVT */
#if NRF_POWER_HAS_USBREG
void nrfx_power_usbevt_init(nrfx_power_usbevt_config_t const * p_config)
{
NRFX_ASSERT(p_config != NULL);
nrfx_power_usbevt_uninit();
if (p_config->handler != NULL)
{
m_usbevt_handler = p_config->handler;
}
}
void nrfx_power_usbevt_enable(void)
{
nrf_power_int_enable(
NRF_POWER_INT_USBDETECTED_MASK |
NRF_POWER_INT_USBREMOVED_MASK |
NRF_POWER_INT_USBPWRRDY_MASK);
}
void nrfx_power_usbevt_disable(void)
{
nrf_power_int_disable(
NRF_POWER_INT_USBDETECTED_MASK |
NRF_POWER_INT_USBREMOVED_MASK |
NRF_POWER_INT_USBPWRRDY_MASK);
}
void nrfx_power_usbevt_uninit(void)
{
m_usbevt_handler = NULL;
}
#endif /* NRF_POWER_HAS_USBREG */
void nrfx_power_irq_handler(void)
{
uint32_t enabled = nrf_power_int_enable_get();
#if NRF_POWER_HAS_POFCON
if ((0 != (enabled & NRF_POWER_INT_POFWARN_MASK)) &&
nrf_power_event_get_and_clear(NRF_POWER_EVENT_POFWARN))
{
/* Cannot be null if event is enabled */
NRFX_ASSERT(m_pofwarn_handler != NULL);
m_pofwarn_handler();
}
#endif
#if NRF_POWER_HAS_SLEEPEVT
if ((0 != (enabled & NRF_POWER_INT_SLEEPENTER_MASK)) &&
nrf_power_event_get_and_clear(NRF_POWER_EVENT_SLEEPENTER))
{
/* Cannot be null if event is enabled */
NRFX_ASSERT(m_sleepevt_handler != NULL);
m_sleepevt_handler(NRFX_POWER_SLEEP_EVT_ENTER);
}
if ((0 != (enabled & NRF_POWER_INT_SLEEPEXIT_MASK)) &&
nrf_power_event_get_and_clear(NRF_POWER_EVENT_SLEEPEXIT))
{
/* Cannot be null if event is enabled */
NRFX_ASSERT(m_sleepevt_handler != NULL);
m_sleepevt_handler(NRFX_POWER_SLEEP_EVT_EXIT);
}
#endif
#if NRF_POWER_HAS_USBREG
if ((0 != (enabled & NRF_POWER_INT_USBDETECTED_MASK)) &&
nrf_power_event_get_and_clear(NRF_POWER_EVENT_USBDETECTED))
{
/* Cannot be null if event is enabled */
NRFX_ASSERT(m_usbevt_handler != NULL);
m_usbevt_handler(NRFX_POWER_USB_EVT_DETECTED);
}
if ((0 != (enabled & NRF_POWER_INT_USBREMOVED_MASK)) &&
nrf_power_event_get_and_clear(NRF_POWER_EVENT_USBREMOVED))
{
/* Cannot be null if event is enabled */
NRFX_ASSERT(m_usbevt_handler != NULL);
m_usbevt_handler(NRFX_POWER_USB_EVT_REMOVED);
}
if ((0 != (enabled & NRF_POWER_INT_USBPWRRDY_MASK)) &&
nrf_power_event_get_and_clear(NRF_POWER_EVENT_USBPWRRDY))
{
/* Cannot be null if event is enabled */
NRFX_ASSERT(m_usbevt_handler != NULL);
m_usbevt_handler(NRFX_POWER_USB_EVT_READY);
}
#endif
}
#if NRFX_CHECK(NRFX_CLOCK_ENABLED)
/*
* If both POWER and CLOCK drivers are used, a common IRQ handler function must
* be used that calls the handlers in these two drivers. This is because these
* two peripherals share one interrupt.
* This function is located here, not in a separate nrfx_power_clock.c file,
* so that it does not end up as the only symbol in a separate object when
* a library with nrfx is created. In such case, forcing a linker to use this
* function instead of another one defined as weak will require additional
* actions, and might be even impossible.
*/
void nrfx_power_clock_irq_handler(void)
{
nrfx_power_irq_handler();
nrfx_clock_irq_handler();
}
#endif
#endif // NRFX_CHECK(NRFX_POWER_ENABLED)

View File

@@ -0,0 +1,530 @@
/**
* Copyright (c) 2015 - 2021, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <nrfx.h>
#if NRFX_CHECK(NRFX_PPI_ENABLED)
#include <nrfx_ppi.h>
#define NRFX_LOG_MODULE PPI
#include <nrfx_log.h>
static uint32_t m_channels_allocated; /**< Bitmap representing channels availability. 1 when a channel is allocated, 0 otherwise. */
static uint8_t m_groups_allocated; /**< Bitmap representing groups availability. 1 when a group is allocated, 0 otherwise.*/
/**
* @brief Compute a group mask (needed for driver internals, not used for NRF_PPI registers).
*
* @param[in] group Group number to transform to a mask.
*
* @retval Group mask.
*/
__STATIC_INLINE uint32_t group_to_mask(nrf_ppi_channel_group_t group)
{
return (1uL << (uint32_t) group);
}
/**
* @brief Check whether a channel is a programmable channel and can be used by an application.
*
* @param[in] channel Channel to check.
*
* @retval true The channel is a programmable application channel.
* @retval false The channel is used by a stack (for example SoftDevice) or is preprogrammed.
*/
__STATIC_INLINE bool is_programmable_app_channel(nrf_ppi_channel_t channel)
{
return ((NRFX_PPI_PROG_APP_CHANNELS_MASK & nrfx_ppi_channel_to_mask(channel)) != 0);
}
/**
* @brief Check whether channels can be used by an application.
*
* @param[in] channel_mask Channel mask to check.
*
* @retval true All specified channels can be used by an application.
* @retval false At least one specified channel is used by a stack (for example SoftDevice).
*/
__STATIC_INLINE bool are_app_channels(uint32_t channel_mask)
{
//lint -e(587)
return ((~(NRFX_PPI_ALL_APP_CHANNELS_MASK) & channel_mask) == 0);
}
/**
* @brief Check whether a channel can be used by an application.
*
* @param[in] channel Channel to check.
*
* @retval true The channel can be used by an application.
* @retval false The channel is used by a stack (for example SoftDevice).
*/
__STATIC_INLINE bool is_app_channel(nrf_ppi_channel_t channel)
{
return are_app_channels(nrfx_ppi_channel_to_mask(channel));
}
/**
* @brief Check whether a channel group can be used by an application.
*
* @param[in] group Group to check.
*
* @retval true The group is an application group.
* @retval false The group is not an application group (this group either does not exist or
* it is used by a stack (for example SoftDevice)).
*/
__STATIC_INLINE bool is_app_group(nrf_ppi_channel_group_t group)
{
return ((NRFX_PPI_ALL_APP_GROUPS_MASK & group_to_mask(group)) != 0);
}
/**
* @brief Check whether a channel is allocated.
*
* @param[in] channel_num Channel number to check.
*
* @retval true The channel is allocated.
* @retval false The channel is not allocated.
*/
__STATIC_INLINE bool is_allocated_channel(nrf_ppi_channel_t channel)
{
return ((m_channels_allocated & nrfx_ppi_channel_to_mask(channel)) != 0);
}
/**
* @brief Set channel allocated indication.
*
* @param[in] channel_num Specifies the channel to set the "allocated" indication.
*/
__STATIC_INLINE void channel_allocated_set(nrf_ppi_channel_t channel)
{
m_channels_allocated |= nrfx_ppi_channel_to_mask(channel);
}
/**
* @brief Clear channel allocated indication.
*
* @param[in] channel_num Specifies the channel to clear the "allocated" indication.
*/
__STATIC_INLINE void channel_allocated_clr(nrf_ppi_channel_t channel)
{
m_channels_allocated &= ~nrfx_ppi_channel_to_mask(channel);
}
/**
* @brief Clear all allocated channels.
*/
__STATIC_INLINE void channel_allocated_clr_all(void)
{
m_channels_allocated &= ~NRFX_PPI_ALL_APP_CHANNELS_MASK;
}
/**
* @brief Check whether a group is allocated.
*
* @param[in] group_num Group number to check.
*
* @retval true The group is allocated.
* false The group is not allocated.
*/
__STATIC_INLINE bool is_allocated_group(nrf_ppi_channel_group_t group)
{
return ((m_groups_allocated & group_to_mask(group)) != 0);
}
/**
* @brief Set group allocated indication.
*
* @param[in] group_num Specifies the group to set the "allocated" indication.
*/
__STATIC_INLINE void group_allocated_set(nrf_ppi_channel_group_t group)
{
m_groups_allocated |= group_to_mask(group);
}
/**
* @brief Clear group allocated indication.
*
* @param[in] group_num Specifies the group to clear the "allocated" indication.
*/
__STATIC_INLINE void group_allocated_clr(nrf_ppi_channel_group_t group)
{
m_groups_allocated &= ~group_to_mask(group);
}
/**
* @brief Clear all allocated groups.
*/
__STATIC_INLINE void group_allocated_clr_all()
{
m_groups_allocated &= ~NRFX_PPI_ALL_APP_GROUPS_MASK;
}
void nrfx_ppi_free_all(void)
{
uint32_t mask = NRFX_PPI_ALL_APP_GROUPS_MASK;
nrf_ppi_channel_group_t group;
// Disable all channels and groups
nrf_ppi_channels_disable(NRFX_PPI_ALL_APP_CHANNELS_MASK);
for (group = NRF_PPI_CHANNEL_GROUP0; mask != 0; mask &= ~group_to_mask(group), group++)
{
if (mask & group_to_mask(group))
{
nrf_ppi_channel_group_clear(group);
}
}
channel_allocated_clr_all();
group_allocated_clr_all();
}
nrfx_err_t nrfx_ppi_channel_alloc(nrf_ppi_channel_t * p_channel)
{
nrfx_err_t err_code = NRFX_SUCCESS;
nrf_ppi_channel_t channel;
uint32_t mask = 0;
err_code = NRFX_ERROR_NO_MEM;
mask = NRFX_PPI_PROG_APP_CHANNELS_MASK;
for (channel = NRF_PPI_CHANNEL0;
mask != 0;
mask &= ~nrfx_ppi_channel_to_mask(channel), channel++)
{
NRFX_CRITICAL_SECTION_ENTER();
if ((mask & nrfx_ppi_channel_to_mask(channel)) && (!is_allocated_channel(channel)))
{
channel_allocated_set(channel);
*p_channel = channel;
err_code = NRFX_SUCCESS;
}
NRFX_CRITICAL_SECTION_EXIT();
if (err_code == NRFX_SUCCESS)
{
NRFX_LOG_INFO("Allocated channel: %d.", channel);
break;
}
}
NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
nrfx_err_t nrfx_ppi_channel_free(nrf_ppi_channel_t channel)
{
nrfx_err_t err_code = NRFX_SUCCESS;
if (!is_programmable_app_channel(channel))
{
err_code = NRFX_ERROR_INVALID_PARAM;
}
else
{
// First disable this channel
nrf_ppi_channel_disable(channel);
NRFX_CRITICAL_SECTION_ENTER();
channel_allocated_clr(channel);
NRFX_CRITICAL_SECTION_EXIT();
}
NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
nrfx_err_t nrfx_ppi_channel_assign(nrf_ppi_channel_t channel, uint32_t eep, uint32_t tep)
{
if ((uint32_t *)eep == NULL || (uint32_t *)tep == NULL)
{
return NRFX_ERROR_NULL;
}
nrfx_err_t err_code = NRFX_SUCCESS;
if (!is_programmable_app_channel(channel))
{
err_code = NRFX_ERROR_INVALID_PARAM;
}
else if (!is_allocated_channel(channel))
{
err_code = NRFX_ERROR_INVALID_STATE;
}
else
{
nrf_ppi_channel_endpoint_setup(channel, eep, tep);
NRFX_LOG_INFO("Assigned channel: %d, event end point: %x, task end point: %x.",
channel,
eep,
tep);
}
NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
nrfx_err_t nrfx_ppi_channel_fork_assign(nrf_ppi_channel_t channel, uint32_t fork_tep)
{
nrfx_err_t err_code = NRFX_SUCCESS;
#ifdef PPI_FEATURE_FORKS_PRESENT
if (!is_allocated_channel(channel))
{
err_code = NRFX_ERROR_INVALID_STATE;
}
else
{
nrf_ppi_fork_endpoint_setup(channel, fork_tep);
NRFX_LOG_INFO("Fork assigned channel: %d, task end point: %d.", channel, fork_tep);
}
NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
#else
err_code = NRFX_ERROR_NOT_SUPPORTED;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
#endif
}
nrfx_err_t nrfx_ppi_channel_enable(nrf_ppi_channel_t channel)
{
nrfx_err_t err_code = NRFX_SUCCESS;
if (!is_app_channel(channel))
{
err_code = NRFX_ERROR_INVALID_PARAM;
}
else if (is_programmable_app_channel(channel) && !is_allocated_channel(channel))
{
err_code = NRFX_ERROR_INVALID_STATE;
}
else
{
nrf_ppi_channel_enable(channel);
}
NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
nrfx_err_t nrfx_ppi_channel_disable(nrf_ppi_channel_t channel)
{
nrfx_err_t err_code = NRFX_SUCCESS;
if (!is_app_channel(channel))
{
err_code = NRFX_ERROR_INVALID_PARAM;
}
else if (is_programmable_app_channel(channel) && !is_allocated_channel(channel))
{
err_code = NRFX_ERROR_INVALID_STATE;
}
else
{
nrf_ppi_channel_disable(channel);
err_code = NRFX_SUCCESS;
}
NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
nrfx_err_t nrfx_ppi_group_alloc(nrf_ppi_channel_group_t * p_group)
{
nrfx_err_t err_code;
uint32_t mask = 0;
nrf_ppi_channel_group_t group;
err_code = NRFX_ERROR_NO_MEM;
mask = NRFX_PPI_ALL_APP_GROUPS_MASK;
for (group = NRF_PPI_CHANNEL_GROUP0; mask != 0; mask &= ~group_to_mask(group), group++)
{
NRFX_CRITICAL_SECTION_ENTER();
if ((mask & group_to_mask(group)) && (!is_allocated_group(group)))
{
group_allocated_set(group);
*p_group = group;
err_code = NRFX_SUCCESS;
}
NRFX_CRITICAL_SECTION_EXIT();
if (err_code == NRFX_SUCCESS)
{
NRFX_LOG_INFO("Allocated group: %d.", group);
break;
}
}
NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
nrfx_err_t nrfx_ppi_group_free(nrf_ppi_channel_group_t group)
{
nrfx_err_t err_code = NRFX_SUCCESS;
if (!is_app_group(group))
{
err_code = NRFX_ERROR_INVALID_PARAM;
}
if (!is_allocated_group(group))
{
err_code = NRFX_ERROR_INVALID_STATE;
}
else
{
nrf_ppi_group_disable(group);
NRFX_CRITICAL_SECTION_ENTER();
group_allocated_clr(group);
NRFX_CRITICAL_SECTION_EXIT();
}
NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
nrfx_err_t nrfx_ppi_group_enable(nrf_ppi_channel_group_t group)
{
nrfx_err_t err_code = NRFX_SUCCESS;
if (!is_app_group(group))
{
err_code = NRFX_ERROR_INVALID_PARAM;
}
else if (!is_allocated_group(group))
{
err_code = NRFX_ERROR_INVALID_STATE;
}
else
{
nrf_ppi_group_enable(group);
}
NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
nrfx_err_t nrfx_ppi_group_disable(nrf_ppi_channel_group_t group)
{
nrfx_err_t err_code = NRFX_SUCCESS;
if (!is_app_group(group))
{
err_code = NRFX_ERROR_INVALID_PARAM;
}
else
{
nrf_ppi_group_disable(group);
}
NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
nrfx_err_t nrfx_ppi_channels_remove_from_group(uint32_t channel_mask,
nrf_ppi_channel_group_t group)
{
nrfx_err_t err_code = NRFX_SUCCESS;
if (!is_app_group(group))
{
err_code = NRFX_ERROR_INVALID_PARAM;
}
else if (!is_allocated_group(group))
{
err_code = NRFX_ERROR_INVALID_STATE;
}
else if (!are_app_channels(channel_mask))
{
err_code = NRFX_ERROR_INVALID_PARAM;
}
else
{
NRFX_CRITICAL_SECTION_ENTER();
nrf_ppi_channels_remove_from_group(channel_mask, group);
NRFX_CRITICAL_SECTION_EXIT();
}
NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
nrfx_err_t nrfx_ppi_channels_include_in_group(uint32_t channel_mask,
nrf_ppi_channel_group_t group)
{
nrfx_err_t err_code = NRFX_SUCCESS;
if (!is_app_group(group))
{
err_code = NRFX_ERROR_INVALID_PARAM;
}
else if (!is_allocated_group(group))
{
err_code = NRFX_ERROR_INVALID_STATE;
}
else if (!are_app_channels(channel_mask))
{
err_code = NRFX_ERROR_INVALID_PARAM;
}
else
{
NRFX_CRITICAL_SECTION_ENTER();
nrf_ppi_channels_include_in_group(channel_mask, group);
NRFX_CRITICAL_SECTION_EXIT();
}
NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
#endif // NRFX_CHECK(NRFX_PPI_ENABLED)

View File

@@ -0,0 +1,523 @@
/**
* Copyright (c) 2015 - 2021, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <nrfx.h>
#if NRFX_CHECK(NRFX_PWM_ENABLED)
#if !(NRFX_CHECK(NRFX_PWM0_ENABLED) || NRFX_CHECK(NRFX_PWM1_ENABLED) || \
NRFX_CHECK(NRFX_PWM2_ENABLED) || NRFX_CHECK(NRFX_PWM3_ENABLED))
#error "No enabled PWM instances. Check <nrfx_config.h>."
#endif
#include <nrfx_pwm.h>
#include <hal/nrf_gpio.h>
#define NRFX_LOG_MODULE PWM
#include <nrfx_log.h>
#if NRFX_CHECK(NRFX_PWM_NRF52_ANOMALY_109_WORKAROUND_ENABLED)
// The workaround uses interrupts to wake up the CPU and ensure it is active
// when PWM is about to start a DMA transfer. For initial transfer, done when
// a playback is started via PPI, a specific EGU instance is used to generate
// an interrupt. During the playback, the PWM interrupt triggered on SEQEND
// event of a preceding sequence is used to protect the transfer done for
// the next sequence to be played.
#include <hal/nrf_egu.h>
#define USE_DMA_ISSUE_WORKAROUND
#endif
#if defined(USE_DMA_ISSUE_WORKAROUND)
#define EGU_IRQn(i) EGU_IRQn_(i)
#define EGU_IRQn_(i) SWI##i##_EGU##i##_IRQn
#define EGU_IRQHandler(i) EGU_IRQHandler_(i)
#define EGU_IRQHandler_(i) nrfx_swi_##i##_irq_handler
#define DMA_ISSUE_EGU_IDX NRFX_PWM_NRF52_ANOMALY_109_EGU_INSTANCE
#define DMA_ISSUE_EGU NRFX_CONCAT_2(NRF_EGU, DMA_ISSUE_EGU_IDX)
#define DMA_ISSUE_EGU_IRQn EGU_IRQn(DMA_ISSUE_EGU_IDX)
#define DMA_ISSUE_EGU_IRQHandler EGU_IRQHandler(DMA_ISSUE_EGU_IDX)
#endif
// Control block - driver instance local data.
typedef struct
{
#if defined(USE_DMA_ISSUE_WORKAROUND)
uint32_t starting_task_address;
#endif
nrfx_pwm_handler_t handler;
nrfx_drv_state_t volatile state;
uint8_t flags;
} pwm_control_block_t;
static pwm_control_block_t m_cb[NRFX_PWM_ENABLED_COUNT];
static void configure_pins(nrfx_pwm_t const * const p_instance,
nrfx_pwm_config_t const * p_config)
{
uint32_t out_pins[NRF_PWM_CHANNEL_COUNT];
uint8_t i;
for (i = 0; i < NRF_PWM_CHANNEL_COUNT; ++i)
{
uint8_t output_pin = p_config->output_pins[i];
if (output_pin != NRFX_PWM_PIN_NOT_USED)
{
bool inverted = output_pin & NRFX_PWM_PIN_INVERTED;
out_pins[i] = output_pin & ~NRFX_PWM_PIN_INVERTED;
if (inverted)
{
nrf_gpio_pin_set(out_pins[i]);
}
else
{
nrf_gpio_pin_clear(out_pins[i]);
}
nrf_gpio_cfg_output(out_pins[i]);
}
else
{
out_pins[i] = NRF_PWM_PIN_NOT_CONNECTED;
}
}
nrf_pwm_pins_set(p_instance->p_registers, out_pins);
}
nrfx_err_t nrfx_pwm_init(nrfx_pwm_t const * const p_instance,
nrfx_pwm_config_t const * p_config,
nrfx_pwm_handler_t handler)
{
NRFX_ASSERT(p_config);
nrfx_err_t err_code;
pwm_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
if (p_cb->state != NRFX_DRV_STATE_UNINITIALIZED)
{
err_code = NRFX_ERROR_INVALID_STATE;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
p_cb->handler = handler;
configure_pins(p_instance, p_config);
nrf_pwm_enable(p_instance->p_registers);
nrf_pwm_configure(p_instance->p_registers,
p_config->base_clock, p_config->count_mode, p_config->top_value);
nrf_pwm_decoder_set(p_instance->p_registers,
p_config->load_mode, p_config->step_mode);
nrf_pwm_shorts_set(p_instance->p_registers, 0);
nrf_pwm_int_set(p_instance->p_registers, 0);
nrf_pwm_event_clear(p_instance->p_registers, NRF_PWM_EVENT_LOOPSDONE);
nrf_pwm_event_clear(p_instance->p_registers, NRF_PWM_EVENT_SEQEND0);
nrf_pwm_event_clear(p_instance->p_registers, NRF_PWM_EVENT_SEQEND1);
nrf_pwm_event_clear(p_instance->p_registers, NRF_PWM_EVENT_STOPPED);
// The workaround for nRF52 Anomaly 109 "protects" DMA transfers by handling
// interrupts generated on SEQEND0 and SEQEND1 events (this ensures that
// the 64 MHz clock is ready when data for the next sequence to be played
// is read). Therefore, the PWM interrupt must be enabled even if the event
// handler is not used.
#if defined(USE_DMA_ISSUE_WORKAROUND)
NRFX_IRQ_PRIORITY_SET(DMA_ISSUE_EGU_IRQn, p_config->irq_priority);
NRFX_IRQ_ENABLE(DMA_ISSUE_EGU_IRQn);
#else
if (p_cb->handler)
#endif
{
NRFX_IRQ_PRIORITY_SET(nrfx_get_irq_number(p_instance->p_registers),
p_config->irq_priority);
NRFX_IRQ_ENABLE(nrfx_get_irq_number(p_instance->p_registers));
}
p_cb->state = NRFX_DRV_STATE_INITIALIZED;
err_code = NRFX_SUCCESS;
NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
void nrfx_pwm_uninit(nrfx_pwm_t const * const p_instance)
{
pwm_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
NRFX_ASSERT(p_cb->state != NRFX_DRV_STATE_UNINITIALIZED);
NRFX_IRQ_DISABLE(nrfx_get_irq_number(p_instance->p_registers));
#if defined(USE_DMA_ISSUE_WORKAROUND)
NRFX_IRQ_DISABLE(DMA_ISSUE_EGU_IRQn);
#endif
nrf_pwm_disable(p_instance->p_registers);
p_cb->state = NRFX_DRV_STATE_UNINITIALIZED;
}
static uint32_t start_playback(nrfx_pwm_t const * const p_instance,
pwm_control_block_t * p_cb,
uint8_t flags,
nrf_pwm_task_t starting_task)
{
p_cb->state = NRFX_DRV_STATE_POWERED_ON;
p_cb->flags = flags;
if (p_cb->handler)
{
// The notification about finished playback is by default enabled,
// but this can be suppressed.
// The notification that the peripheral has stopped is always enabled.
uint32_t int_mask = NRF_PWM_INT_LOOPSDONE_MASK |
NRF_PWM_INT_STOPPED_MASK;
// The workaround for nRF52 Anomaly 109 "protects" DMA transfers by
// handling interrupts generated on SEQEND0 and SEQEND1 events (see
// 'nrfx_pwm_init'), hence these events must be always enabled
// to generate interrupts.
// However, the user handler is called for them only when requested
// (see 'irq_handler').
#if defined(USE_DMA_ISSUE_WORKAROUND)
int_mask |= NRF_PWM_INT_SEQEND0_MASK | NRF_PWM_INT_SEQEND1_MASK;
#else
if (flags & NRFX_PWM_FLAG_SIGNAL_END_SEQ0)
{
int_mask |= NRF_PWM_INT_SEQEND0_MASK;
}
if (flags & NRFX_PWM_FLAG_SIGNAL_END_SEQ1)
{
int_mask |= NRF_PWM_INT_SEQEND1_MASK;
}
#endif
if (flags & NRFX_PWM_FLAG_NO_EVT_FINISHED)
{
int_mask &= ~NRF_PWM_INT_LOOPSDONE_MASK;
}
nrf_pwm_int_set(p_instance->p_registers, int_mask);
}
#if defined(USE_DMA_ISSUE_WORKAROUND)
else
{
nrf_pwm_int_set(p_instance->p_registers,
NRF_PWM_INT_SEQEND0_MASK | NRF_PWM_INT_SEQEND1_MASK);
}
#endif
nrf_pwm_event_clear(p_instance->p_registers, NRF_PWM_EVENT_STOPPED);
if (flags & NRFX_PWM_FLAG_START_VIA_TASK)
{
uint32_t starting_task_address =
nrf_pwm_task_address_get(p_instance->p_registers, starting_task);
#if defined(USE_DMA_ISSUE_WORKAROUND)
// To "protect" the initial DMA transfer it is required to start
// the PWM by triggering the proper task from EGU interrupt handler,
// it is not safe to do it directly via PPI.
p_cb->starting_task_address = starting_task_address;
nrf_egu_int_enable(DMA_ISSUE_EGU,
nrf_egu_int_get(DMA_ISSUE_EGU, p_instance->drv_inst_idx));
return (uint32_t)nrf_egu_task_trigger_address_get(DMA_ISSUE_EGU,
p_instance->drv_inst_idx);
#else
return starting_task_address;
#endif
}
nrf_pwm_task_trigger(p_instance->p_registers, starting_task);
return 0;
}
uint32_t nrfx_pwm_simple_playback(nrfx_pwm_t const * const p_instance,
nrf_pwm_sequence_t const * p_sequence,
uint16_t playback_count,
uint32_t flags)
{
pwm_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
NRFX_ASSERT(p_cb->state != NRFX_DRV_STATE_UNINITIALIZED);
NRFX_ASSERT(playback_count > 0);
NRFX_ASSERT(nrfx_is_in_ram(p_sequence->values.p_raw));
// To take advantage of the looping mechanism, we need to use both sequences
// (single sequence can be played back only once).
nrf_pwm_sequence_set(p_instance->p_registers, 0, p_sequence);
nrf_pwm_sequence_set(p_instance->p_registers, 1, p_sequence);
bool odd = (playback_count & 1);
nrf_pwm_loop_set(p_instance->p_registers,
(playback_count / 2) + (odd ? 1 : 0));
uint32_t shorts_mask;
if (flags & NRFX_PWM_FLAG_STOP)
{
shorts_mask = NRF_PWM_SHORT_LOOPSDONE_STOP_MASK;
}
else if (flags & NRFX_PWM_FLAG_LOOP)
{
shorts_mask = odd ? NRF_PWM_SHORT_LOOPSDONE_SEQSTART1_MASK
: NRF_PWM_SHORT_LOOPSDONE_SEQSTART0_MASK;
}
else
{
shorts_mask = 0;
}
nrf_pwm_shorts_set(p_instance->p_registers, shorts_mask);
NRFX_LOG_INFO("Function: %s, sequence length: %d.",
__func__,
p_sequence->length);
NRFX_LOG_DEBUG("Sequence data:");
NRFX_LOG_HEXDUMP_DEBUG((uint8_t *)p_sequence->values.p_raw,
p_sequence->length * sizeof(uint16_t));
return start_playback(p_instance, p_cb, flags,
odd ? NRF_PWM_TASK_SEQSTART1 : NRF_PWM_TASK_SEQSTART0);
}
uint32_t nrfx_pwm_complex_playback(nrfx_pwm_t const * const p_instance,
nrf_pwm_sequence_t const * p_sequence_0,
nrf_pwm_sequence_t const * p_sequence_1,
uint16_t playback_count,
uint32_t flags)
{
pwm_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
NRFX_ASSERT(p_cb->state != NRFX_DRV_STATE_UNINITIALIZED);
NRFX_ASSERT(playback_count > 0);
NRFX_ASSERT(nrfx_is_in_ram(p_sequence_0->values.p_raw));
NRFX_ASSERT(nrfx_is_in_ram(p_sequence_1->values.p_raw));
nrf_pwm_sequence_set(p_instance->p_registers, 0, p_sequence_0);
nrf_pwm_sequence_set(p_instance->p_registers, 1, p_sequence_1);
nrf_pwm_loop_set(p_instance->p_registers, playback_count);
uint32_t shorts_mask;
if (flags & NRFX_PWM_FLAG_STOP)
{
shorts_mask = NRF_PWM_SHORT_LOOPSDONE_STOP_MASK;
}
else if (flags & NRFX_PWM_FLAG_LOOP)
{
shorts_mask = NRF_PWM_SHORT_LOOPSDONE_SEQSTART0_MASK;
}
else
{
shorts_mask = 0;
}
nrf_pwm_shorts_set(p_instance->p_registers, shorts_mask);
NRFX_LOG_INFO("Function: %s, sequence 0 length: %d.",
__func__,
p_sequence_0->length);
NRFX_LOG_INFO("Function: %s, sequence 1 length: %d.",
__func__,
p_sequence_1->length);
NRFX_LOG_DEBUG("Sequence 0 data:");
NRFX_LOG_HEXDUMP_DEBUG(p_sequence_0->values.p_raw,
p_sequence_0->length * sizeof(uint16_t));
NRFX_LOG_DEBUG("Sequence 1 data:");
NRFX_LOG_HEXDUMP_DEBUG(p_sequence_1->values.p_raw,
p_sequence_1->length * sizeof(uint16_t));
return start_playback(p_instance, p_cb, flags, NRF_PWM_TASK_SEQSTART0);
}
bool nrfx_pwm_stop(nrfx_pwm_t const * const p_instance,
bool wait_until_stopped)
{
NRFX_ASSERT(m_cb[p_instance->drv_inst_idx].state != NRFX_DRV_STATE_UNINITIALIZED);
bool ret_val = false;
// Deactivate shortcuts before triggering the STOP task, otherwise the PWM
// could be immediately started again if the LOOPSDONE event occurred in
// the same peripheral clock cycle as the STOP task was triggered.
nrf_pwm_shorts_set(p_instance->p_registers, 0);
// Trigger the STOP task even if the PWM appears to be already stopped.
// It won't harm, but it could help if for some strange reason the stopped
// status was not reported correctly.
nrf_pwm_task_trigger(p_instance->p_registers, NRF_PWM_TASK_STOP);
if (nrfx_pwm_is_stopped(p_instance))
{
ret_val = true;
}
else
{
do {
if (nrfx_pwm_is_stopped(p_instance))
{
ret_val = true;
break;
}
} while (wait_until_stopped);
}
NRFX_LOG_INFO("%s returned %d.", __func__, ret_val);
return ret_val;
}
bool nrfx_pwm_is_stopped(nrfx_pwm_t const * const p_instance)
{
pwm_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
NRFX_ASSERT(p_cb->state != NRFX_DRV_STATE_UNINITIALIZED);
bool ret_val = false;
// If the event handler is used (interrupts are enabled), the state will
// be changed in interrupt handler when the STOPPED event occurs.
if (p_cb->state != NRFX_DRV_STATE_POWERED_ON)
{
ret_val = true;
}
// If interrupts are disabled, we must check the STOPPED event here.
if (nrf_pwm_event_check(p_instance->p_registers, NRF_PWM_EVENT_STOPPED))
{
p_cb->state = NRFX_DRV_STATE_INITIALIZED;
NRFX_LOG_INFO("Disabled.");
ret_val = true;
}
NRFX_LOG_INFO("%s returned %d.", __func__, ret_val);
return ret_val;
}
static void irq_handler(NRF_PWM_Type * p_pwm, pwm_control_block_t * p_cb)
{
// The user handler is called for SEQEND0 and SEQEND1 events only when the
// user asks for it (by setting proper flags when starting the playback).
if (nrf_pwm_event_check(p_pwm, NRF_PWM_EVENT_SEQEND0))
{
nrf_pwm_event_clear(p_pwm, NRF_PWM_EVENT_SEQEND0);
if ((p_cb->flags & NRFX_PWM_FLAG_SIGNAL_END_SEQ0) && p_cb->handler)
{
p_cb->handler(NRFX_PWM_EVT_END_SEQ0);
}
}
if (nrf_pwm_event_check(p_pwm, NRF_PWM_EVENT_SEQEND1))
{
nrf_pwm_event_clear(p_pwm, NRF_PWM_EVENT_SEQEND1);
if ((p_cb->flags & NRFX_PWM_FLAG_SIGNAL_END_SEQ1) && p_cb->handler)
{
p_cb->handler(NRFX_PWM_EVT_END_SEQ1);
}
}
// For LOOPSDONE the handler is called by default, but the user can disable
// this (via flags).
if (nrf_pwm_event_check(p_pwm, NRF_PWM_EVENT_LOOPSDONE))
{
nrf_pwm_event_clear(p_pwm, NRF_PWM_EVENT_LOOPSDONE);
if (!(p_cb->flags & NRFX_PWM_FLAG_NO_EVT_FINISHED) && p_cb->handler)
{
p_cb->handler(NRFX_PWM_EVT_FINISHED);
}
}
// The STOPPED event is always propagated to the user handler.
if (nrf_pwm_event_check(p_pwm, NRF_PWM_EVENT_STOPPED))
{
nrf_pwm_event_clear(p_pwm, NRF_PWM_EVENT_STOPPED);
p_cb->state = NRFX_DRV_STATE_INITIALIZED;
if (p_cb->handler)
{
p_cb->handler(NRFX_PWM_EVT_STOPPED);
}
}
}
#if defined(USE_DMA_ISSUE_WORKAROUND)
// See 'start_playback' why this is needed.
void DMA_ISSUE_EGU_IRQHandler(void)
{
int i;
for (i = 0; i < NRFX_PWM_ENABLED_COUNT; ++i)
{
volatile uint32_t * p_event_reg =
nrf_egu_event_triggered_address_get(DMA_ISSUE_EGU, i);
if (*p_event_reg)
{
*p_event_reg = 0;
*(volatile uint32_t *)(m_cb[i].starting_task_address) = 1;
}
}
}
#endif
#if NRFX_CHECK(NRFX_PWM0_ENABLED)
void nrfx_pwm_0_irq_handler(void)
{
irq_handler(NRF_PWM0, &m_cb[NRFX_PWM0_INST_IDX]);
}
#endif
#if NRFX_CHECK(NRFX_PWM1_ENABLED)
void nrfx_pwm_1_irq_handler(void)
{
irq_handler(NRF_PWM1, &m_cb[NRFX_PWM1_INST_IDX]);
}
#endif
#if NRFX_CHECK(NRFX_PWM2_ENABLED)
void nrfx_pwm_2_irq_handler(void)
{
irq_handler(NRF_PWM2, &m_cb[NRFX_PWM2_INST_IDX]);
}
#endif
#if NRFX_CHECK(NRFX_PWM3_ENABLED)
void nrfx_pwm_3_irq_handler(void)
{
irq_handler(NRF_PWM3, &m_cb[NRFX_PWM3_INST_IDX]);
}
#endif
#endif // NRFX_CHECK(NRFX_PWM_ENABLED)

View File

@@ -0,0 +1,204 @@
/**
* Copyright (c) 2015 - 2021, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <nrfx.h>
#if NRFX_CHECK(NRFX_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,464 @@
/**
* Copyright (c) 2016 - 2021, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <nrfx.h>
#if NRFX_CHECK(NRFX_QSPI_ENABLED)
#include <nrfx_qspi.h>
#include <hal/nrf_gpio.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 Macro for initializing a QSPI pin.
*
* QSPI peripheral expects high drive pin strength.
*/
#define QSPI_PIN_INIT(_pin) nrf_gpio_cfg((_pin), \
NRF_GPIO_PIN_DIR_INPUT, \
NRF_GPIO_PIN_INPUT_DISCONNECT, \
NRF_GPIO_PIN_NOPULL, \
NRF_GPIO_PIN_H0H1, \
NRF_GPIO_PIN_NOSENSE)
/** @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;
}
QSPI_PIN_INIT(p_config->sck_pin);
QSPI_PIN_INIT(p_config->csn_pin);
QSPI_PIN_INIT(p_config->io0_pin);
QSPI_PIN_INIT(p_config->io1_pin);
if (p_config->io2_pin != NRF_QSPI_PIN_NOT_CONNECTED)
{
QSPI_PIN_INIT(p_config->io2_pin);
}
if (p_config->io3_pin != NRF_QSPI_PIN_NOT_CONNECTED)
{
QSPI_PIN_INIT(p_config->io3_pin);
}
nrf_qspi_pins_set(NRF_QSPI, p_config);
return true;
}
static void qspi_pins_deconfigure(void)
{
nrf_qspi_pins_t pins;
nrf_qspi_pins_get(NRF_QSPI, &pins);
nrf_gpio_cfg_default(pins.sck_pin);
nrf_gpio_cfg_default(pins.csn_pin);
nrf_gpio_cfg_default(pins.io0_pin);
nrf_gpio_cfg_default(pins.io1_pin);
if (pins.io2_pin != NRF_QSPI_PIN_NOT_CONNECTED)
{
nrf_gpio_cfg_default(pins.io2_pin);
}
if (pins.io3_pin != NRF_QSPI_PIN_NOT_CONNECTED)
{
nrf_gpio_cfg_default(pins.io3_pin);
}
}
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);
}
NRFX_IRQ_DISABLE(QSPI_IRQn);
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);
nrf_qspi_event_clear(NRF_QSPI, NRF_QSPI_EVENT_READY);
qspi_pins_deconfigure();
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 - 2021, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <nrfx.h>
#if NRFX_CHECK(NRFX_RNG_ENABLED)
#include <nrfx_rng.h>
#define NRFX_LOG_MODULE RNG
#include <nrfx_log.h>
/**
* @brief Internal state of RNG driver.
*/
static nrfx_drv_state_t m_rng_state;
/**
* @brief Pointer to handler calling from interrupt routine.
*/
static nrfx_rng_evt_handler_t m_rng_hndl;
nrfx_err_t nrfx_rng_init(nrfx_rng_config_t const * p_config, nrfx_rng_evt_handler_t handler)
{
NRFX_ASSERT(p_config);
NRFX_ASSERT(handler);
if (m_rng_state != NRFX_DRV_STATE_UNINITIALIZED)
{
return NRFX_ERROR_ALREADY_INITIALIZED;
}
m_rng_hndl = handler;
if (p_config->error_correction)
{
nrf_rng_error_correction_enable();
}
nrf_rng_shorts_disable(NRF_RNG_SHORT_VALRDY_STOP_MASK);
NRFX_IRQ_PRIORITY_SET(RNG_IRQn, p_config->interrupt_priority);
NRFX_IRQ_ENABLE(RNG_IRQn);
m_rng_state = NRFX_DRV_STATE_INITIALIZED;
return NRFX_SUCCESS;
}
void nrfx_rng_start(void)
{
NRFX_ASSERT(m_rng_state == NRFX_DRV_STATE_INITIALIZED);
nrf_rng_event_clear(NRF_RNG_EVENT_VALRDY);
nrf_rng_int_enable(NRF_RNG_INT_VALRDY_MASK);
nrf_rng_task_trigger(NRF_RNG_TASK_START);
}
void nrfx_rng_stop(void)
{
NRFX_ASSERT(m_rng_state == NRFX_DRV_STATE_INITIALIZED);
nrf_rng_int_disable(NRF_RNG_INT_VALRDY_MASK);
nrf_rng_task_trigger(NRF_RNG_TASK_STOP);
}
void nrfx_rng_uninit(void)
{
NRFX_ASSERT(m_rng_state == NRFX_DRV_STATE_INITIALIZED);
nrf_rng_int_disable(NRF_RNG_INT_VALRDY_MASK);
nrf_rng_task_trigger(NRF_RNG_TASK_STOP);
NRFX_IRQ_DISABLE(RNG_IRQn);
m_rng_state = NRFX_DRV_STATE_UNINITIALIZED;
NRFX_LOG_INFO("Uninitialized.");
}
void nrfx_rng_irq_handler(void)
{
nrf_rng_event_clear(NRF_RNG_EVENT_VALRDY);
uint8_t rng_value = nrf_rng_random_value_get();
m_rng_hndl(rng_value);
NRFX_LOG_DEBUG("Event: NRF_RNG_EVENT_VALRDY.");
}
#endif // NRFX_CHECK(NRFX_RNG_ENABLED)

View File

@@ -0,0 +1,348 @@
/**
* Copyright (c) 2014 - 2021, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <nrfx.h>
#if NRFX_CHECK(NRFX_RTC_ENABLED)
#if !(NRFX_CHECK(NRFX_RTC0_ENABLED) || NRFX_CHECK(NRFX_RTC1_ENABLED) || \
NRFX_CHECK(NRFX_RTC2_ENABLED))
#error "No enabled RTC instances. Check <nrfx_config.h>."
#endif
#include <nrfx_rtc.h>
#define NRFX_LOG_MODULE RTC
#include <nrfx_log.h>
#define EVT_TO_STR(event) \
(event == NRF_RTC_EVENT_TICK ? "NRF_RTC_EVENT_TICK" : \
(event == NRF_RTC_EVENT_OVERFLOW ? "NRF_RTC_EVENT_OVERFLOW" : \
(event == NRF_RTC_EVENT_COMPARE_0 ? "NRF_RTC_EVENT_COMPARE_0" : \
(event == NRF_RTC_EVENT_COMPARE_1 ? "NRF_RTC_EVENT_COMPARE_1" : \
(event == NRF_RTC_EVENT_COMPARE_2 ? "NRF_RTC_EVENT_COMPARE_2" : \
(event == NRF_RTC_EVENT_COMPARE_3 ? "NRF_RTC_EVENT_COMPARE_3" : \
"UNKNOWN EVENT"))))))
/**@brief RTC driver instance control block structure. */
typedef struct
{
nrfx_drv_state_t state; /**< Instance state. */
bool reliable; /**< Reliable mode flag. */
uint8_t tick_latency; /**< Maximum length of interrupt handler in ticks (max 7.7 ms). */
} nrfx_rtc_cb_t;
// User callbacks local storage.
static nrfx_rtc_handler_t m_handlers[NRFX_RTC_ENABLED_COUNT];
static nrfx_rtc_cb_t m_cb[NRFX_RTC_ENABLED_COUNT];
nrfx_err_t nrfx_rtc_init(nrfx_rtc_t const * const p_instance,
nrfx_rtc_config_t const * p_config,
nrfx_rtc_handler_t handler)
{
NRFX_ASSERT(p_config);
NRFX_ASSERT(handler);
nrfx_err_t err_code;
m_handlers[p_instance->instance_id] = handler;
if (m_cb[p_instance->instance_id].state != NRFX_DRV_STATE_UNINITIALIZED)
{
err_code = NRFX_ERROR_INVALID_STATE;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
NRFX_IRQ_PRIORITY_SET(p_instance->irq, p_config->interrupt_priority);
NRFX_IRQ_ENABLE(p_instance->irq);
nrf_rtc_prescaler_set(p_instance->p_reg, p_config->prescaler);
m_cb[p_instance->instance_id].reliable = p_config->reliable;
m_cb[p_instance->instance_id].tick_latency = p_config->tick_latency;
m_cb[p_instance->instance_id].state = NRFX_DRV_STATE_INITIALIZED;
err_code = NRFX_SUCCESS;
NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
void nrfx_rtc_uninit(nrfx_rtc_t const * const p_instance)
{
uint32_t mask = NRF_RTC_INT_TICK_MASK |
NRF_RTC_INT_OVERFLOW_MASK |
NRF_RTC_INT_COMPARE0_MASK |
NRF_RTC_INT_COMPARE1_MASK |
NRF_RTC_INT_COMPARE2_MASK |
NRF_RTC_INT_COMPARE3_MASK;
NRFX_ASSERT(m_cb[p_instance->instance_id].state != NRFX_DRV_STATE_UNINITIALIZED);
NRFX_IRQ_DISABLE(p_instance->irq);
nrf_rtc_task_trigger(p_instance->p_reg, NRF_RTC_TASK_STOP);
nrf_rtc_event_disable(p_instance->p_reg, mask);
nrf_rtc_int_disable(p_instance->p_reg, mask);
m_cb[p_instance->instance_id].state = NRFX_DRV_STATE_UNINITIALIZED;
NRFX_LOG_INFO("Uninitialized.");
}
void nrfx_rtc_enable(nrfx_rtc_t const * const p_instance)
{
NRFX_ASSERT(m_cb[p_instance->instance_id].state == NRFX_DRV_STATE_INITIALIZED);
nrf_rtc_task_trigger(p_instance->p_reg, NRF_RTC_TASK_START);
m_cb[p_instance->instance_id].state = NRFX_DRV_STATE_POWERED_ON;
NRFX_LOG_INFO("Enabled.");
}
void nrfx_rtc_disable(nrfx_rtc_t const * const p_instance)
{
NRFX_ASSERT(m_cb[p_instance->instance_id].state != NRFX_DRV_STATE_UNINITIALIZED);
nrf_rtc_task_trigger(p_instance->p_reg, NRF_RTC_TASK_STOP);
m_cb[p_instance->instance_id].state = NRFX_DRV_STATE_INITIALIZED;
NRFX_LOG_INFO("Disabled.");
}
nrfx_err_t nrfx_rtc_cc_disable(nrfx_rtc_t const * const p_instance, uint32_t channel)
{
NRFX_ASSERT(m_cb[p_instance->instance_id].state != NRFX_DRV_STATE_UNINITIALIZED);
NRFX_ASSERT(channel<p_instance->cc_channel_count);
nrfx_err_t err_code;
uint32_t int_mask = RTC_CHANNEL_INT_MASK(channel);
nrf_rtc_event_t event = RTC_CHANNEL_EVENT_ADDR(channel);
nrf_rtc_event_disable(p_instance->p_reg,int_mask);
if (nrf_rtc_int_is_enabled(p_instance->p_reg,int_mask))
{
nrf_rtc_int_disable(p_instance->p_reg,int_mask);
if (nrf_rtc_event_pending(p_instance->p_reg,event))
{
nrf_rtc_event_clear(p_instance->p_reg,event);
err_code = NRFX_ERROR_TIMEOUT;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
}
NRFX_LOG_INFO("RTC id: %d, channel disabled: %lu.", p_instance->instance_id, channel);
err_code = NRFX_SUCCESS;
NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
nrfx_err_t nrfx_rtc_cc_set(nrfx_rtc_t const * const p_instance,
uint32_t channel,
uint32_t val,
bool enable_irq)
{
NRFX_ASSERT(m_cb[p_instance->instance_id].state != NRFX_DRV_STATE_UNINITIALIZED);
NRFX_ASSERT(channel<p_instance->cc_channel_count);
nrfx_err_t err_code;
uint32_t int_mask = RTC_CHANNEL_INT_MASK(channel);
nrf_rtc_event_t event = RTC_CHANNEL_EVENT_ADDR(channel);
nrf_rtc_event_disable(p_instance->p_reg, int_mask);
nrf_rtc_int_disable(p_instance->p_reg, int_mask);
val = RTC_WRAP(val);
if (m_cb[p_instance->instance_id].reliable)
{
nrf_rtc_cc_set(p_instance->p_reg,channel,val);
uint32_t cnt = nrf_rtc_counter_get(p_instance->p_reg);
int32_t diff = cnt - val;
if (cnt < val)
{
diff += RTC_COUNTER_COUNTER_Msk;
}
if (diff < m_cb[p_instance->instance_id].tick_latency)
{
err_code = NRFX_ERROR_TIMEOUT;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
}
else
{
nrf_rtc_cc_set(p_instance->p_reg,channel,val);
}
if (enable_irq)
{
nrf_rtc_event_clear(p_instance->p_reg,event);
nrf_rtc_int_enable(p_instance->p_reg, int_mask);
}
nrf_rtc_event_enable(p_instance->p_reg,int_mask);
NRFX_LOG_INFO("RTC id: %d, channel enabled: %lu, compare value: %lu.",
p_instance->instance_id,
channel,
val);
err_code = NRFX_SUCCESS;
NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
void nrfx_rtc_tick_enable(nrfx_rtc_t const * const p_instance, bool enable_irq)
{
nrf_rtc_event_t event = NRF_RTC_EVENT_TICK;
uint32_t mask = NRF_RTC_INT_TICK_MASK;
nrf_rtc_event_clear(p_instance->p_reg, event);
nrf_rtc_event_enable(p_instance->p_reg, mask);
if (enable_irq)
{
nrf_rtc_int_enable(p_instance->p_reg, mask);
}
NRFX_LOG_INFO("Tick events enabled.");
}
void nrfx_rtc_tick_disable(nrfx_rtc_t const * const p_instance)
{
uint32_t mask = NRF_RTC_INT_TICK_MASK;
nrf_rtc_event_disable(p_instance->p_reg, mask);
nrf_rtc_int_disable(p_instance->p_reg, mask);
NRFX_LOG_INFO("Tick events disabled.");
}
void nrfx_rtc_overflow_enable(nrfx_rtc_t const * const p_instance, bool enable_irq)
{
nrf_rtc_event_t event = NRF_RTC_EVENT_OVERFLOW;
uint32_t mask = NRF_RTC_INT_OVERFLOW_MASK;
nrf_rtc_event_clear(p_instance->p_reg, event);
nrf_rtc_event_enable(p_instance->p_reg, mask);
if (enable_irq)
{
nrf_rtc_int_enable(p_instance->p_reg, mask);
}
}
void nrfx_rtc_overflow_disable(nrfx_rtc_t const * const p_instance)
{
uint32_t mask = NRF_RTC_INT_OVERFLOW_MASK;
nrf_rtc_event_disable(p_instance->p_reg, mask);
nrf_rtc_int_disable(p_instance->p_reg, mask);
}
uint32_t nrfx_rtc_max_ticks_get(nrfx_rtc_t const * const p_instance)
{
uint32_t ticks;
if (m_cb[p_instance->instance_id].reliable)
{
ticks = RTC_COUNTER_COUNTER_Msk - m_cb[p_instance->instance_id].tick_latency;
}
else
{
ticks = RTC_COUNTER_COUNTER_Msk;
}
return ticks;
}
static void irq_handler(NRF_RTC_Type * p_reg,
uint32_t instance_id,
uint32_t channel_count)
{
uint32_t i;
uint32_t int_mask = (uint32_t)NRF_RTC_INT_COMPARE0_MASK;
nrf_rtc_event_t event = NRF_RTC_EVENT_COMPARE_0;
for (i = 0; i < channel_count; i++)
{
if (nrf_rtc_int_is_enabled(p_reg,int_mask) && nrf_rtc_event_pending(p_reg,event))
{
nrf_rtc_event_disable(p_reg,int_mask);
nrf_rtc_int_disable(p_reg,int_mask);
nrf_rtc_event_clear(p_reg,event);
NRFX_LOG_DEBUG("Event: %s, instance id: %lu.", EVT_TO_STR(event), instance_id);
m_handlers[instance_id]((nrfx_rtc_int_type_t)i);
}
int_mask <<= 1;
event = (nrf_rtc_event_t)((uint32_t)event + sizeof(uint32_t));
}
event = NRF_RTC_EVENT_TICK;
if (nrf_rtc_int_is_enabled(p_reg,NRF_RTC_INT_TICK_MASK) &&
nrf_rtc_event_pending(p_reg, event))
{
nrf_rtc_event_clear(p_reg, event);
NRFX_LOG_DEBUG("Event: %s, instance id: %lu.", EVT_TO_STR(event), instance_id);
m_handlers[instance_id](NRFX_RTC_INT_TICK);
}
event = NRF_RTC_EVENT_OVERFLOW;
if (nrf_rtc_int_is_enabled(p_reg,NRF_RTC_INT_OVERFLOW_MASK) &&
nrf_rtc_event_pending(p_reg, event))
{
nrf_rtc_event_clear(p_reg,event);
NRFX_LOG_DEBUG("Event: %s, instance id: %lu.", EVT_TO_STR(event), instance_id);
m_handlers[instance_id](NRFX_RTC_INT_OVERFLOW);
}
}
#if NRFX_CHECK(NRFX_RTC0_ENABLED)
void nrfx_rtc_0_irq_handler(void)
{
irq_handler(NRF_RTC0, NRFX_RTC0_INST_IDX, NRF_RTC_CC_CHANNEL_COUNT(0));
}
#endif
#if NRFX_CHECK(NRFX_RTC1_ENABLED)
void nrfx_rtc_1_irq_handler(void)
{
irq_handler(NRF_RTC1, NRFX_RTC1_INST_IDX, NRF_RTC_CC_CHANNEL_COUNT(1));
}
#endif
#if NRFX_CHECK(NRFX_RTC2_ENABLED)
void nrfx_rtc_2_irq_handler(void)
{
irq_handler(NRF_RTC2, NRFX_RTC2_INST_IDX, NRF_RTC_CC_CHANNEL_COUNT(2));
}
#endif
#endif // NRFX_CHECK(NRFX_RTC_ENABLED)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,436 @@
/**
* Copyright (c) 2015 - 2021, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <nrfx.h>
#if NRFX_CHECK(NRFX_SPI_ENABLED)
#if !(NRFX_CHECK(NRFX_SPI0_ENABLED) || NRFX_CHECK(NRFX_SPI1_ENABLED) || \
NRFX_CHECK(NRFX_SPI2_ENABLED))
#error "No enabled SPI instances. Check <nrfx_config.h>."
#endif
#include <nrfx_spi.h>
#include "prs/nrfx_prs.h"
#include <hal/nrf_gpio.h>
#define NRFX_LOG_MODULE SPI
#include <nrfx_log.h>
// Control block - driver instance local data.
typedef struct
{
nrfx_spi_evt_handler_t handler;
void * p_context;
nrfx_spi_evt_t evt; // Keep the struct that is ready for event handler. Less memcpy.
nrfx_drv_state_t state;
volatile bool transfer_in_progress;
// [no need for 'volatile' attribute for the following members, as they
// are not concurrently used in IRQ handlers and main line code]
uint8_t ss_pin;
uint8_t miso_pin;
uint8_t orc;
size_t bytes_transferred;
bool abort;
} spi_control_block_t;
static spi_control_block_t m_cb[NRFX_SPI_ENABLED_COUNT];
nrfx_err_t nrfx_spi_init(nrfx_spi_t const * const p_instance,
nrfx_spi_config_t const * p_config,
nrfx_spi_evt_handler_t handler,
void * p_context)
{
NRFX_ASSERT(p_config);
spi_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
nrfx_err_t err_code;
if (p_cb->state != NRFX_DRV_STATE_UNINITIALIZED)
{
err_code = NRFX_ERROR_INVALID_STATE;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
#if NRFX_CHECK(NRFX_PRS_ENABLED)
static nrfx_irq_handler_t const irq_handlers[NRFX_SPI_ENABLED_COUNT] = {
#if NRFX_CHECK(NRFX_SPI0_ENABLED)
nrfx_spi_0_irq_handler,
#endif
#if NRFX_CHECK(NRFX_SPI1_ENABLED)
nrfx_spi_1_irq_handler,
#endif
#if NRFX_CHECK(NRFX_SPI2_ENABLED)
nrfx_spi_2_irq_handler,
#endif
};
if (nrfx_prs_acquire(p_instance->p_reg,
irq_handlers[p_instance->drv_inst_idx]) != NRFX_SUCCESS)
{
err_code = NRFX_ERROR_BUSY;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
#endif // NRFX_CHECK(NRFX_PRS_ENABLED)
p_cb->handler = handler;
p_cb->p_context = p_context;
uint32_t mosi_pin;
uint32_t miso_pin;
// Configure pins used by the peripheral:
// - SCK - output with initial value corresponding with the SPI mode used:
// 0 - for modes 0 and 1 (CPOL = 0), 1 - for modes 2 and 3 (CPOL = 1);
// according to the reference manual guidelines this pin and its input
// buffer must always be connected for the SPI to work.
if (p_config->mode <= NRF_SPI_MODE_1)
{
nrf_gpio_pin_clear(p_config->sck_pin);
}
else
{
nrf_gpio_pin_set(p_config->sck_pin);
}
nrf_gpio_cfg(p_config->sck_pin,
NRF_GPIO_PIN_DIR_OUTPUT,
NRF_GPIO_PIN_INPUT_CONNECT,
NRF_GPIO_PIN_NOPULL,
NRF_GPIO_PIN_S0S1,
NRF_GPIO_PIN_NOSENSE);
// - MOSI (optional) - output with initial value 0,
if (p_config->mosi_pin != NRFX_SPI_PIN_NOT_USED)
{
mosi_pin = p_config->mosi_pin;
nrf_gpio_pin_clear(mosi_pin);
nrf_gpio_cfg_output(mosi_pin);
}
else
{
mosi_pin = NRF_SPI_PIN_NOT_CONNECTED;
}
// - MISO (optional) - input,
if (p_config->miso_pin != NRFX_SPI_PIN_NOT_USED)
{
miso_pin = p_config->miso_pin;
nrf_gpio_cfg_input(miso_pin, (nrf_gpio_pin_pull_t)NRFX_SPI_MISO_PULL_CFG);
}
else
{
miso_pin = NRF_SPI_PIN_NOT_CONNECTED;
}
m_cb[p_instance->drv_inst_idx].miso_pin = p_config->miso_pin;
// - Slave Select (optional) - output with initial value 1 (inactive).
if (p_config->ss_pin != NRFX_SPI_PIN_NOT_USED)
{
nrf_gpio_pin_set(p_config->ss_pin);
nrf_gpio_cfg_output(p_config->ss_pin);
}
m_cb[p_instance->drv_inst_idx].ss_pin = p_config->ss_pin;
NRF_SPI_Type * p_spi = p_instance->p_reg;
nrf_spi_pins_set(p_spi, p_config->sck_pin, mosi_pin, miso_pin);
nrf_spi_frequency_set(p_spi, p_config->frequency);
nrf_spi_configure(p_spi, p_config->mode, p_config->bit_order);
m_cb[p_instance->drv_inst_idx].orc = p_config->orc;
nrf_spi_enable(p_spi);
if (p_cb->handler)
{
NRFX_IRQ_PRIORITY_SET(nrfx_get_irq_number(p_instance->p_reg),
p_config->irq_priority);
NRFX_IRQ_ENABLE(nrfx_get_irq_number(p_instance->p_reg));
}
p_cb->transfer_in_progress = false;
p_cb->state = NRFX_DRV_STATE_INITIALIZED;
err_code = NRFX_SUCCESS;
NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
void nrfx_spi_uninit(nrfx_spi_t const * const p_instance)
{
spi_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
NRFX_ASSERT(p_cb->state != NRFX_DRV_STATE_UNINITIALIZED);
if (p_cb->handler)
{
NRFX_IRQ_DISABLE(nrfx_get_irq_number(p_instance->p_reg));
}
NRF_SPI_Type * p_spi = p_instance->p_reg;
if (p_cb->handler)
{
nrf_spi_int_disable(p_spi, NRF_SPI_ALL_INTS_MASK);
}
if (p_cb->miso_pin != NRFX_SPI_PIN_NOT_USED)
{
nrf_gpio_cfg_default(p_cb->miso_pin);
}
nrf_spi_disable(p_spi);
#if NRFX_CHECK(NRFX_PRS_ENABLED)
nrfx_prs_release(p_instance->p_reg);
#endif
p_cb->state = NRFX_DRV_STATE_UNINITIALIZED;
}
static void finish_transfer(spi_control_block_t * p_cb)
{
// If Slave Select signal is used, this is the time to deactivate it.
if (p_cb->ss_pin != NRFX_SPI_PIN_NOT_USED)
{
nrf_gpio_pin_set(p_cb->ss_pin);
}
// By clearing this flag before calling the handler we allow subsequent
// transfers to be started directly from the handler function.
p_cb->transfer_in_progress = false;
p_cb->evt.type = NRFX_SPI_EVENT_DONE;
p_cb->handler(&p_cb->evt, p_cb->p_context);
}
// This function is called from the IRQ handler or, in blocking mode, directly
// from the 'spi_xfer' function.
// It returns true as long as the transfer should be continued, otherwise (when
// there is nothing more to send/receive) it returns false.
static bool transfer_byte(NRF_SPI_Type * p_spi, spi_control_block_t * p_cb)
{
// Read the data byte received in this transfer (always, because no further
// READY event can be generated until the current byte is read out from the
// RXD register), and store it in the RX buffer (only when needed).
volatile uint8_t rx_data = nrf_spi_rxd_get(p_spi);
if (p_cb->bytes_transferred < p_cb->evt.xfer_desc.rx_length)
{
p_cb->evt.xfer_desc.p_rx_buffer[p_cb->bytes_transferred] = rx_data;
}
++p_cb->bytes_transferred;
// Check if there are more bytes to send or receive and write proper data
// byte (next one from TX buffer or over-run character) to the TXD register
// when needed.
// NOTE - we've already used 'p_cb->bytes_transferred + 1' bytes from our
// buffers, because we take advantage of double buffering of TXD
// register (so in effect one byte is still being transmitted now);
// see how the transfer is started in the 'spi_xfer' function.
size_t bytes_used = p_cb->bytes_transferred + 1;
if (p_cb->abort)
{
if (bytes_used < p_cb->evt.xfer_desc.tx_length)
{
p_cb->evt.xfer_desc.tx_length = bytes_used;
}
if (bytes_used < p_cb->evt.xfer_desc.rx_length)
{
p_cb->evt.xfer_desc.rx_length = bytes_used;
}
}
if (bytes_used < p_cb->evt.xfer_desc.tx_length)
{
nrf_spi_txd_set(p_spi, p_cb->evt.xfer_desc.p_tx_buffer[bytes_used]);
return true;
}
else if (bytes_used < p_cb->evt.xfer_desc.rx_length)
{
nrf_spi_txd_set(p_spi, p_cb->orc);
return true;
}
return (p_cb->bytes_transferred < p_cb->evt.xfer_desc.tx_length ||
p_cb->bytes_transferred < p_cb->evt.xfer_desc.rx_length);
}
static void spi_xfer(NRF_SPI_Type * p_spi,
spi_control_block_t * p_cb,
nrfx_spi_xfer_desc_t const * p_xfer_desc)
{
p_cb->bytes_transferred = 0;
nrf_spi_int_disable(p_spi, NRF_SPI_INT_READY_MASK);
nrf_spi_event_clear(p_spi, NRF_SPI_EVENT_READY);
// Start the transfer by writing some byte to the TXD register;
// if TX buffer is not empty, take the first byte from this buffer,
// otherwise - use over-run character.
nrf_spi_txd_set(p_spi,
(p_xfer_desc->tx_length > 0 ? p_xfer_desc->p_tx_buffer[0] : p_cb->orc));
// TXD register is double buffered, so next byte to be transmitted can
// be written immediately, if needed, i.e. if TX or RX transfer is to
// be more that 1 byte long. Again - if there is something more in TX
// buffer send it, otherwise use over-run character.
if (p_xfer_desc->tx_length > 1)
{
nrf_spi_txd_set(p_spi, p_xfer_desc->p_tx_buffer[1]);
}
else if (p_xfer_desc->rx_length > 1)
{
nrf_spi_txd_set(p_spi, p_cb->orc);
}
// For blocking mode (user handler not provided) wait here for READY
// events (indicating that the byte from TXD register was transmitted
// and a new incoming byte was moved to the RXD register) and continue
// transaction until all requested bytes are transferred.
// In non-blocking mode - IRQ service routine will do this stuff.
if (p_cb->handler)
{
nrf_spi_int_enable(p_spi, NRF_SPI_INT_READY_MASK);
}
else
{
do {
while (!nrf_spi_event_check(p_spi, NRF_SPI_EVENT_READY)) {}
nrf_spi_event_clear(p_spi, NRF_SPI_EVENT_READY);
NRFX_LOG_DEBUG("SPI: Event: NRF_SPI_EVENT_READY.");
} while (transfer_byte(p_spi, p_cb));
if (p_cb->ss_pin != NRFX_SPI_PIN_NOT_USED)
{
nrf_gpio_pin_set(p_cb->ss_pin);
}
}
}
nrfx_err_t nrfx_spi_xfer(nrfx_spi_t const * const p_instance,
nrfx_spi_xfer_desc_t const * p_xfer_desc,
uint32_t flags)
{
spi_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
NRFX_ASSERT(p_cb->state != NRFX_DRV_STATE_UNINITIALIZED);
NRFX_ASSERT(p_xfer_desc->p_tx_buffer != NULL || p_xfer_desc->tx_length == 0);
NRFX_ASSERT(p_xfer_desc->p_rx_buffer != NULL || p_xfer_desc->rx_length == 0);
nrfx_err_t err_code = NRFX_SUCCESS;
if (p_cb->transfer_in_progress)
{
err_code = NRFX_ERROR_BUSY;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
else
{
if (p_cb->handler)
{
p_cb->transfer_in_progress = true;
}
}
p_cb->evt.xfer_desc = *p_xfer_desc;
p_cb->abort = false;
if (p_cb->ss_pin != NRFX_SPI_PIN_NOT_USED)
{
nrf_gpio_pin_clear(p_cb->ss_pin);
}
if (flags)
{
p_cb->transfer_in_progress = false;
err_code = NRFX_ERROR_NOT_SUPPORTED;
}
else
{
spi_xfer(p_instance->p_reg, p_cb, p_xfer_desc);
}
NRFX_LOG_INFO("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
void nrfx_spi_abort(nrfx_spi_t const * p_instance)
{
spi_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
NRFX_ASSERT(p_cb->state != NRFX_DRV_STATE_UNINITIALIZED);
p_cb->abort = true;
}
static void irq_handler(NRF_SPI_Type * p_spi, spi_control_block_t * p_cb)
{
NRFX_ASSERT(p_cb->handler);
nrf_spi_event_clear(p_spi, NRF_SPI_EVENT_READY);
NRFX_LOG_DEBUG("Event: NRF_SPI_EVENT_READY.");
if (!transfer_byte(p_spi, p_cb))
{
finish_transfer(p_cb);
}
}
#if NRFX_CHECK(NRFX_SPI0_ENABLED)
void nrfx_spi_0_irq_handler(void)
{
irq_handler(NRF_SPI0, &m_cb[NRFX_SPI0_INST_IDX]);
}
#endif
#if NRFX_CHECK(NRFX_SPI1_ENABLED)
void nrfx_spi_1_irq_handler(void)
{
irq_handler(NRF_SPI1, &m_cb[NRFX_SPI1_INST_IDX]);
}
#endif
#if NRFX_CHECK(NRFX_SPI2_ENABLED)
void nrfx_spi_2_irq_handler(void)
{
irq_handler(NRF_SPI2, &m_cb[NRFX_SPI2_INST_IDX]);
}
#endif
#endif // NRFX_CHECK(NRFX_SPI_ENABLED)

View File

@@ -0,0 +1,697 @@
/**
* Copyright (c) 2015 - 2021, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <nrfx.h>
#if NRFX_CHECK(NRFX_SPIM_ENABLED)
#if !(NRFX_CHECK(NRFX_SPIM0_ENABLED) || NRFX_CHECK(NRFX_SPIM1_ENABLED) || \
NRFX_CHECK(NRFX_SPIM2_ENABLED) || NRFX_CHECK(NRFX_SPIM3_ENABLED))
#error "No enabled SPIM instances. Check <nrfx_config.h>."
#endif
#if NRFX_CHECK(NRFX_SPIM_EXTENDED_ENABLED) && !NRFX_CHECK(NRFX_SPIM3_ENABLED)
#error "Extended options are available only in SPIM3 on the nRF52840 SoC."
#endif
#include <nrfx_spim.h>
#include "prs/nrfx_prs.h"
#include <hal/nrf_gpio.h>
#define NRFX_LOG_MODULE SPIM
#include <nrfx_log.h>
#define SPIMX_LENGTH_VALIDATE(peripheral, drv_inst_idx, rx_len, tx_len) \
(((drv_inst_idx) == NRFX_CONCAT_3(NRFX_, peripheral, _INST_IDX)) && \
NRFX_EASYDMA_LENGTH_VALIDATE(peripheral, rx_len, tx_len))
#if NRFX_CHECK(NRFX_SPIM0_ENABLED)
#define SPIM0_LENGTH_VALIDATE(...) SPIMX_LENGTH_VALIDATE(SPIM0, __VA_ARGS__)
#else
#define SPIM0_LENGTH_VALIDATE(...) 0
#endif
#if NRFX_CHECK(NRFX_SPIM1_ENABLED)
#define SPIM1_LENGTH_VALIDATE(...) SPIMX_LENGTH_VALIDATE(SPIM1, __VA_ARGS__)
#else
#define SPIM1_LENGTH_VALIDATE(...) 0
#endif
#if NRFX_CHECK(NRFX_SPIM2_ENABLED)
#define SPIM2_LENGTH_VALIDATE(...) SPIMX_LENGTH_VALIDATE(SPIM2, __VA_ARGS__)
#else
#define SPIM2_LENGTH_VALIDATE(...) 0
#endif
#if NRFX_CHECK(NRFX_SPIM3_ENABLED)
#define SPIM3_LENGTH_VALIDATE(...) SPIMX_LENGTH_VALIDATE(SPIM3, __VA_ARGS__)
#else
#define SPIM3_LENGTH_VALIDATE(...) 0
#endif
#define SPIM_LENGTH_VALIDATE(drv_inst_idx, rx_len, tx_len) \
(SPIM0_LENGTH_VALIDATE(drv_inst_idx, rx_len, tx_len) || \
SPIM1_LENGTH_VALIDATE(drv_inst_idx, rx_len, tx_len) || \
SPIM2_LENGTH_VALIDATE(drv_inst_idx, rx_len, tx_len) || \
SPIM3_LENGTH_VALIDATE(drv_inst_idx, rx_len, tx_len))
#if defined(NRF52840_XXAA) && (NRFX_CHECK(NRFX_SPIM3_ENABLED))
// Enable workaround for nRF52840 anomaly 195 (SPIM3 continues to draw current after disable).
#define USE_WORKAROUND_FOR_ANOMALY_195
#endif
// Control block - driver instance local data.
typedef struct
{
nrfx_spim_evt_handler_t handler;
void * p_context;
nrfx_spim_evt_t evt; // Keep the struct that is ready for event handler. Less memcpy.
nrfx_drv_state_t state;
volatile bool transfer_in_progress;
#if NRFX_CHECK(NRFX_SPIM_EXTENDED_ENABLED)
bool use_hw_ss;
#endif
// [no need for 'volatile' attribute for the following members, as they
// are not concurrently used in IRQ handlers and main line code]
bool ss_active_high;
uint8_t ss_pin;
uint8_t miso_pin;
uint8_t orc;
#if NRFX_CHECK(NRFX_SPIM_NRF52_ANOMALY_109_WORKAROUND_ENABLED)
size_t tx_length;
size_t rx_length;
#endif
} spim_control_block_t;
static spim_control_block_t m_cb[NRFX_SPIM_ENABLED_COUNT];
#if NRFX_CHECK(NRFX_SPIM3_NRF52840_ANOMALY_198_WORKAROUND_ENABLED)
// Workaround for nRF52840 anomaly 198: SPIM3 transmit data might be corrupted.
static uint32_t m_anomaly_198_preserved_value;
static void anomaly_198_enable(uint8_t const * p_buffer, size_t buf_len)
{
m_anomaly_198_preserved_value = *((volatile uint32_t *)0x40000E00);
if (buf_len == 0)
{
return;
}
uint32_t buffer_end_addr = ((uint32_t)p_buffer) + buf_len;
uint32_t block_addr = ((uint32_t)p_buffer) & ~0x1FFF;
uint32_t block_flag = (1UL << ((block_addr >> 13) & 0xFFFF));
uint32_t occupied_blocks = 0;
if (block_addr >= 0x20010000)
{
occupied_blocks = (1UL << 8);
}
else
{
do {
occupied_blocks |= block_flag;
block_flag <<= 1;
block_addr += 0x2000;
} while ((block_addr < buffer_end_addr) && (block_addr < 0x20012000));
}
*((volatile uint32_t *)0x40000E00) = occupied_blocks;
}
static void anomaly_198_disable(void)
{
*((volatile uint32_t *)0x40000E00) = m_anomaly_198_preserved_value;
}
#endif // NRFX_CHECK(NRFX_SPIM3_NRF52840_ANOMALY_198_WORKAROUND_ENABLED)
nrfx_err_t nrfx_spim_init(nrfx_spim_t const * const p_instance,
nrfx_spim_config_t const * p_config,
nrfx_spim_evt_handler_t handler,
void * p_context)
{
NRFX_ASSERT(p_config);
spim_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
nrfx_err_t err_code;
if (p_cb->state != NRFX_DRV_STATE_UNINITIALIZED)
{
err_code = NRFX_ERROR_INVALID_STATE;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
#if NRFX_CHECK(NRFX_SPIM_EXTENDED_ENABLED)
// Currently, only SPIM3 in nRF52840 supports the extended features.
// Other instances must be checked.
if ((p_instance->drv_inst_idx != NRFX_SPIM3_INST_IDX) &&
((p_config->dcx_pin != NRFX_SPIM_PIN_NOT_USED) ||
(p_config->frequency == NRF_SPIM_FREQ_16M) ||
(p_config->frequency == NRF_SPIM_FREQ_32M) ||
(p_config->use_hw_ss)))
{
err_code = NRFX_ERROR_NOT_SUPPORTED;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
#endif
NRF_SPIM_Type * p_spim = (NRF_SPIM_Type *)p_instance->p_reg;
#if NRFX_CHECK(NRFX_PRS_ENABLED)
static nrfx_irq_handler_t const irq_handlers[NRFX_SPIM_ENABLED_COUNT] = {
#if NRFX_CHECK(NRFX_SPIM0_ENABLED)
nrfx_spim_0_irq_handler,
#endif
#if NRFX_CHECK(NRFX_SPIM1_ENABLED)
nrfx_spim_1_irq_handler,
#endif
#if NRFX_CHECK(NRFX_SPIM2_ENABLED)
nrfx_spim_2_irq_handler,
#endif
#if NRFX_CHECK(NRFX_SPIM3_ENABLED)
nrfx_spim_3_irq_handler,
#endif
};
if (nrfx_prs_acquire(p_instance->p_reg,
irq_handlers[p_instance->drv_inst_idx]) != NRFX_SUCCESS)
{
err_code = NRFX_ERROR_BUSY;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
#endif // NRFX_CHECK(NRFX_PRS_ENABLED)
p_cb->handler = handler;
p_cb->p_context = p_context;
uint32_t mosi_pin;
uint32_t miso_pin;
// Configure pins used by the peripheral:
// - SCK - output with initial value corresponding with the SPI mode used:
// 0 - for modes 0 and 1 (CPOL = 0), 1 - for modes 2 and 3 (CPOL = 1);
// according to the reference manual guidelines this pin and its input
// buffer must always be connected for the SPI to work.
if (p_config->mode <= NRF_SPIM_MODE_1)
{
nrf_gpio_pin_clear(p_config->sck_pin);
}
else
{
nrf_gpio_pin_set(p_config->sck_pin);
}
nrf_gpio_cfg(p_config->sck_pin,
NRF_GPIO_PIN_DIR_OUTPUT,
NRF_GPIO_PIN_INPUT_CONNECT,
NRF_GPIO_PIN_NOPULL,
NRF_GPIO_PIN_S0S1,
NRF_GPIO_PIN_NOSENSE);
// - MOSI (optional) - output with initial value 0,
if (p_config->mosi_pin != NRFX_SPIM_PIN_NOT_USED)
{
mosi_pin = p_config->mosi_pin;
nrf_gpio_pin_clear(mosi_pin);
nrf_gpio_cfg_output(mosi_pin);
}
else
{
mosi_pin = NRF_SPIM_PIN_NOT_CONNECTED;
}
// - MISO (optional) - input,
if (p_config->miso_pin != NRFX_SPIM_PIN_NOT_USED)
{
miso_pin = p_config->miso_pin;
nrf_gpio_cfg_input(miso_pin, (nrf_gpio_pin_pull_t)NRFX_SPIM_MISO_PULL_CFG);
}
else
{
miso_pin = NRF_SPIM_PIN_NOT_CONNECTED;
}
p_cb->miso_pin = p_config->miso_pin;
// - Slave Select (optional) - output with initial value 1 (inactive).
// 'p_cb->ss_pin' variable is used during transfers to check if SS pin should be toggled,
// so this field needs to be initialized even if the pin is not used.
p_cb->ss_pin = p_config->ss_pin;
if (p_config->ss_pin != NRFX_SPIM_PIN_NOT_USED)
{
if (p_config->ss_active_high)
{
nrf_gpio_pin_clear(p_config->ss_pin);
}
else
{
nrf_gpio_pin_set(p_config->ss_pin);
}
nrf_gpio_cfg_output(p_config->ss_pin);
#if NRFX_CHECK(NRFX_SPIM_EXTENDED_ENABLED)
if (p_config->use_hw_ss)
{
p_cb->use_hw_ss = p_config->use_hw_ss;
nrf_spim_csn_configure(p_spim,
p_config->ss_pin,
(p_config->ss_active_high == true ?
NRF_SPIM_CSN_POL_HIGH : NRF_SPIM_CSN_POL_LOW),
p_config->ss_duration);
}
#endif
p_cb->ss_active_high = p_config->ss_active_high;
}
#if NRFX_CHECK(NRFX_SPIM_EXTENDED_ENABLED)
// - DCX (optional) - output.
if (p_config->dcx_pin != NRFX_SPIM_PIN_NOT_USED)
{
nrf_gpio_pin_set(p_config->dcx_pin);
nrf_gpio_cfg_output(p_config->dcx_pin);
nrf_spim_dcx_pin_set(p_spim, p_config->dcx_pin);
}
// Change rx delay
nrf_spim_iftiming_set(p_spim, p_config->rx_delay);
#endif
nrf_spim_pins_set(p_spim, p_config->sck_pin, mosi_pin, miso_pin);
nrf_spim_frequency_set(p_spim, p_config->frequency);
nrf_spim_configure(p_spim, p_config->mode, p_config->bit_order);
nrf_spim_orc_set(p_spim, p_config->orc);
nrf_spim_enable(p_spim);
if (p_cb->handler)
{
NRFX_IRQ_PRIORITY_SET(nrfx_get_irq_number(p_instance->p_reg),
p_config->irq_priority);
NRFX_IRQ_ENABLE(nrfx_get_irq_number(p_instance->p_reg));
}
p_cb->transfer_in_progress = false;
p_cb->state = NRFX_DRV_STATE_INITIALIZED;
err_code = NRFX_SUCCESS;
NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
void nrfx_spim_uninit(nrfx_spim_t const * const p_instance)
{
spim_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
NRFX_ASSERT(p_cb->state != NRFX_DRV_STATE_UNINITIALIZED);
if (p_cb->handler)
{
NRFX_IRQ_DISABLE(nrfx_get_irq_number(p_instance->p_reg));
}
NRF_SPIM_Type * p_spim = (NRF_SPIM_Type *)p_instance->p_reg;
if (p_cb->handler)
{
nrf_spim_int_disable(p_spim, NRF_SPIM_ALL_INTS_MASK);
if (p_cb->transfer_in_progress)
{
// Ensure that SPI is not performing any transfer.
nrf_spim_task_trigger(p_spim, NRF_SPIM_TASK_STOP);
while (!nrf_spim_event_check(p_spim, NRF_SPIM_EVENT_STOPPED))
{}
p_cb->transfer_in_progress = false;
}
}
if (p_cb->miso_pin != NRFX_SPIM_PIN_NOT_USED)
{
nrf_gpio_cfg_default(p_cb->miso_pin);
}
nrf_spim_disable(p_spim);
#ifdef USE_WORKAROUND_FOR_ANOMALY_195
if (p_spim == NRF_SPIM3)
{
*(volatile uint32_t *)0x4002F004 = 1;
}
#endif
#if NRFX_CHECK(NRFX_PRS_ENABLED)
nrfx_prs_release(p_instance->p_reg);
#endif
p_cb->state = NRFX_DRV_STATE_UNINITIALIZED;
}
#if NRFX_CHECK(NRFX_SPIM_EXTENDED_ENABLED)
nrfx_err_t nrfx_spim_xfer_dcx(nrfx_spim_t const * const p_instance,
nrfx_spim_xfer_desc_t const * p_xfer_desc,
uint32_t flags,
uint8_t cmd_length)
{
NRFX_ASSERT(cmd_length <= NRF_SPIM_DCX_CNT_ALL_CMD);
nrf_spim_dcx_cnt_set((NRF_SPIM_Type *)p_instance->p_reg, cmd_length);
return nrfx_spim_xfer(p_instance, p_xfer_desc, 0);
}
#endif
static void finish_transfer(spim_control_block_t * p_cb)
{
// If Slave Select signal is used, this is the time to deactivate it.
if (p_cb->ss_pin != NRFX_SPIM_PIN_NOT_USED)
{
#if NRFX_CHECK(NRFX_SPIM_EXTENDED_ENABLED)
if (!p_cb->use_hw_ss)
#endif
{
if (p_cb->ss_active_high)
{
nrf_gpio_pin_clear(p_cb->ss_pin);
}
else
{
nrf_gpio_pin_set(p_cb->ss_pin);
}
}
}
// By clearing this flag before calling the handler we allow subsequent
// transfers to be started directly from the handler function.
p_cb->transfer_in_progress = false;
p_cb->evt.type = NRFX_SPIM_EVENT_DONE;
p_cb->handler(&p_cb->evt, p_cb->p_context);
}
__STATIC_INLINE void spim_int_enable(NRF_SPIM_Type * p_spim, bool enable)
{
if (!enable)
{
nrf_spim_int_disable(p_spim, NRF_SPIM_INT_END_MASK);
}
else
{
nrf_spim_int_enable(p_spim, NRF_SPIM_INT_END_MASK);
}
}
__STATIC_INLINE void spim_list_enable_handle(NRF_SPIM_Type * p_spim, uint32_t flags)
{
if (NRFX_SPIM_FLAG_TX_POSTINC & flags)
{
nrf_spim_tx_list_enable(p_spim);
}
else
{
nrf_spim_tx_list_disable(p_spim);
}
if (NRFX_SPIM_FLAG_RX_POSTINC & flags)
{
nrf_spim_rx_list_enable(p_spim);
}
else
{
nrf_spim_rx_list_disable(p_spim);
}
}
static nrfx_err_t spim_xfer(NRF_SPIM_Type * p_spim,
spim_control_block_t * p_cb,
nrfx_spim_xfer_desc_t const * p_xfer_desc,
uint32_t flags)
{
nrfx_err_t err_code;
// EasyDMA requires that transfer buffers are placed in Data RAM region;
// signal error if they are not.
if ((p_xfer_desc->p_tx_buffer != NULL && !nrfx_is_in_ram(p_xfer_desc->p_tx_buffer)) ||
(p_xfer_desc->p_rx_buffer != NULL && !nrfx_is_in_ram(p_xfer_desc->p_rx_buffer)))
{
p_cb->transfer_in_progress = false;
err_code = NRFX_ERROR_INVALID_ADDR;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
#if NRFX_CHECK(NRFX_SPIM_NRF52_ANOMALY_109_WORKAROUND_ENABLED)
p_cb->tx_length = 0;
p_cb->rx_length = 0;
#endif
nrf_spim_tx_buffer_set(p_spim, p_xfer_desc->p_tx_buffer, p_xfer_desc->tx_length);
nrf_spim_rx_buffer_set(p_spim, p_xfer_desc->p_rx_buffer, p_xfer_desc->rx_length);
#if NRFX_CHECK(NRFX_SPIM3_NRF52840_ANOMALY_198_WORKAROUND_ENABLED)
if (p_spim == NRF_SPIM3)
{
anomaly_198_enable(p_xfer_desc->p_tx_buffer, p_xfer_desc->tx_length);
}
#endif
nrf_spim_event_clear(p_spim, NRF_SPIM_EVENT_END);
spim_list_enable_handle(p_spim, flags);
if (!(flags & NRFX_SPIM_FLAG_HOLD_XFER))
{
nrf_spim_task_trigger(p_spim, NRF_SPIM_TASK_START);
}
#if NRFX_CHECK(NRFX_SPIM_NRF52_ANOMALY_109_WORKAROUND_ENABLED)
if (flags & NRFX_SPIM_FLAG_HOLD_XFER)
{
nrf_spim_event_clear(p_spim, NRF_SPIM_EVENT_STARTED);
p_cb->tx_length = p_xfer_desc->tx_length;
p_cb->rx_length = p_xfer_desc->rx_length;
nrf_spim_tx_buffer_set(p_spim, p_xfer_desc->p_tx_buffer, 0);
nrf_spim_rx_buffer_set(p_spim, p_xfer_desc->p_rx_buffer, 0);
nrf_spim_int_enable(p_spim, NRF_SPIM_INT_STARTED_MASK);
}
#endif
if (!p_cb->handler)
{
while (!nrf_spim_event_check(p_spim, NRF_SPIM_EVENT_END)){}
#if NRFX_CHECK(NRFX_SPIM3_NRF52840_ANOMALY_198_WORKAROUND_ENABLED)
if (p_spim == NRF_SPIM3)
{
anomaly_198_disable();
}
#endif
if (p_cb->ss_pin != NRFX_SPIM_PIN_NOT_USED)
{
#if NRFX_CHECK(NRFX_SPIM_EXTENDED_ENABLED)
if (!p_cb->use_hw_ss)
#endif
{
if (p_cb->ss_active_high)
{
nrf_gpio_pin_clear(p_cb->ss_pin);
}
else
{
nrf_gpio_pin_set(p_cb->ss_pin);
}
}
}
}
else
{
spim_int_enable(p_spim, !(flags & NRFX_SPIM_FLAG_NO_XFER_EVT_HANDLER));
}
err_code = NRFX_SUCCESS;
NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
nrfx_err_t nrfx_spim_xfer(nrfx_spim_t const * const p_instance,
nrfx_spim_xfer_desc_t const * p_xfer_desc,
uint32_t flags)
{
spim_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
NRFX_ASSERT(p_cb->state != NRFX_DRV_STATE_UNINITIALIZED);
NRFX_ASSERT(p_xfer_desc->p_tx_buffer != NULL || p_xfer_desc->tx_length == 0);
NRFX_ASSERT(p_xfer_desc->p_rx_buffer != NULL || p_xfer_desc->rx_length == 0);
NRFX_ASSERT(SPIM_LENGTH_VALIDATE(p_instance->drv_inst_idx,
p_xfer_desc->rx_length,
p_xfer_desc->tx_length));
nrfx_err_t err_code = NRFX_SUCCESS;
if (p_cb->transfer_in_progress)
{
err_code = NRFX_ERROR_BUSY;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
else
{
if (p_cb->handler && !(flags & (NRFX_SPIM_FLAG_REPEATED_XFER |
NRFX_SPIM_FLAG_NO_XFER_EVT_HANDLER)))
{
p_cb->transfer_in_progress = true;
}
}
p_cb->evt.xfer_desc = *p_xfer_desc;
if (p_cb->ss_pin != NRFX_SPIM_PIN_NOT_USED)
{
#if NRFX_CHECK(NRFX_SPIM_EXTENDED_ENABLED)
if (!p_cb->use_hw_ss)
#endif
{
if (p_cb->ss_active_high)
{
nrf_gpio_pin_set(p_cb->ss_pin);
}
else
{
nrf_gpio_pin_clear(p_cb->ss_pin);
}
}
}
return spim_xfer(p_instance->p_reg, p_cb, p_xfer_desc, flags);
}
void nrfx_spim_abort(nrfx_spim_t const * p_instance)
{
spim_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
NRFX_ASSERT(p_cb->state != NRFX_DRV_STATE_UNINITIALIZED);
nrf_spim_task_trigger(p_instance->p_reg, NRF_SPIM_TASK_STOP);
while (!nrf_spim_event_check(p_instance->p_reg, NRF_SPIM_EVENT_STOPPED))
{}
p_cb->transfer_in_progress = false;
}
uint32_t nrfx_spim_start_task_get(nrfx_spim_t const * p_instance)
{
NRF_SPIM_Type * p_spim = (NRF_SPIM_Type *)p_instance->p_reg;
return nrf_spim_task_address_get(p_spim, NRF_SPIM_TASK_START);
}
uint32_t nrfx_spim_end_event_get(nrfx_spim_t const * p_instance)
{
NRF_SPIM_Type * p_spim = (NRF_SPIM_Type *)p_instance->p_reg;
return nrf_spim_event_address_get(p_spim, NRF_SPIM_EVENT_END);
}
static void irq_handler(NRF_SPIM_Type * p_spim, spim_control_block_t * p_cb)
{
#if NRFX_CHECK(NRFX_SPIM_NRF52_ANOMALY_109_WORKAROUND_ENABLED)
if ((nrf_spim_int_enable_check(p_spim, NRF_SPIM_INT_STARTED_MASK)) &&
(nrf_spim_event_check(p_spim, NRF_SPIM_EVENT_STARTED)) )
{
/* Handle first, zero-length, auxiliary transmission. */
nrf_spim_event_clear(p_spim, NRF_SPIM_EVENT_STARTED);
nrf_spim_event_clear(p_spim, NRF_SPIM_EVENT_END);
NRFX_ASSERT(p_spim->TXD.MAXCNT == 0);
p_spim->TXD.MAXCNT = p_cb->tx_length;
NRFX_ASSERT(p_spim->RXD.MAXCNT == 0);
p_spim->RXD.MAXCNT = p_cb->rx_length;
/* Disable STARTED interrupt, used only in auxiliary transmission. */
nrf_spim_int_disable(p_spim, NRF_SPIM_INT_STARTED_MASK);
/* Start the actual, glitch-free transmission. */
nrf_spim_task_trigger(p_spim, NRF_SPIM_TASK_START);
return;
}
#endif
if (nrf_spim_event_check(p_spim, NRF_SPIM_EVENT_END))
{
#if NRFX_CHECK(NRFX_SPIM3_NRF52840_ANOMALY_198_WORKAROUND_ENABLED)
if (p_spim == NRF_SPIM3)
{
anomaly_198_disable();
}
#endif
nrf_spim_event_clear(p_spim, NRF_SPIM_EVENT_END);
NRFX_ASSERT(p_cb->handler);
NRFX_LOG_DEBUG("Event: NRF_SPIM_EVENT_END.");
finish_transfer(p_cb);
}
}
#if NRFX_CHECK(NRFX_SPIM0_ENABLED)
void nrfx_spim_0_irq_handler(void)
{
irq_handler(NRF_SPIM0, &m_cb[NRFX_SPIM0_INST_IDX]);
}
#endif
#if NRFX_CHECK(NRFX_SPIM1_ENABLED)
void nrfx_spim_1_irq_handler(void)
{
irq_handler(NRF_SPIM1, &m_cb[NRFX_SPIM1_INST_IDX]);
}
#endif
#if NRFX_CHECK(NRFX_SPIM2_ENABLED)
void nrfx_spim_2_irq_handler(void)
{
irq_handler(NRF_SPIM2, &m_cb[NRFX_SPIM2_INST_IDX]);
}
#endif
#if NRFX_CHECK(NRFX_SPIM3_ENABLED)
void nrfx_spim_3_irq_handler(void)
{
irq_handler(NRF_SPIM3, &m_cb[NRFX_SPIM3_INST_IDX]);
}
#endif
#endif // NRFX_CHECK(NRFX_SPIM_ENABLED)

View File

@@ -0,0 +1,513 @@
/**
* Copyright (c) 2013 - 2021, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <nrfx.h>
#if NRFX_CHECK(NRFX_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 - 2021, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <nrfx.h>
#if NRFX_CHECK(NRFX_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 - 2021, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <nrfx.h>
#if NRFX_CHECK(NRFX_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 - 2021, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <nrfx.h>
#if NRFX_CHECK(NRFX_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 - 2021, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <nrfx.h>
#if NRFX_CHECK(NRFX_TIMER_ENABLED)
#if !(NRFX_CHECK(NRFX_TIMER0_ENABLED) || NRFX_CHECK(NRFX_TIMER1_ENABLED) || \
NRFX_CHECK(NRFX_TIMER2_ENABLED) || NRFX_CHECK(NRFX_TIMER3_ENABLED) || \
NRFX_CHECK(NRFX_TIMER4_ENABLED))
#error "No enabled TIMER instances. Check <nrfx_config.h>."
#endif
#include <nrfx_timer.h>
#define NRFX_LOG_MODULE TIMER
#include <nrfx_log.h>
/**@brief Timer control block. */
typedef struct
{
nrfx_timer_event_handler_t handler;
void * context;
nrfx_drv_state_t state;
} timer_control_block_t;
static timer_control_block_t m_cb[NRFX_TIMER_ENABLED_COUNT];
nrfx_err_t nrfx_timer_init(nrfx_timer_t const * const p_instance,
nrfx_timer_config_t const * p_config,
nrfx_timer_event_handler_t timer_event_handler)
{
timer_control_block_t * p_cb = &m_cb[p_instance->instance_id];
#ifdef SOFTDEVICE_PRESENT
NRFX_ASSERT(p_instance->p_reg != NRF_TIMER0);
#endif
NRFX_ASSERT(p_config);
NRFX_ASSERT(timer_event_handler);
nrfx_err_t err_code;
if (p_cb->state != NRFX_DRV_STATE_UNINITIALIZED)
{
err_code = NRFX_ERROR_INVALID_STATE;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
/* Warning 685: Relational operator '<=' always evaluates to 'true'"
* Warning in NRF_TIMER_IS_BIT_WIDTH_VALID macro. Macro validate timers resolution.
* Not necessary in nRF52 based systems. Obligatory in nRF51 based systems.
*/
/*lint -save -e685 */
NRFX_ASSERT(NRF_TIMER_IS_BIT_WIDTH_VALID(p_instance->p_reg, p_config->bit_width));
//lint -restore
p_cb->handler = timer_event_handler;
p_cb->context = p_config->p_context;
uint8_t i;
for (i = 0; i < p_instance->cc_channel_count; ++i)
{
nrf_timer_event_clear(p_instance->p_reg,
nrf_timer_compare_event_get(i));
}
NRFX_IRQ_PRIORITY_SET(nrfx_get_irq_number(p_instance->p_reg),
p_config->interrupt_priority);
NRFX_IRQ_ENABLE(nrfx_get_irq_number(p_instance->p_reg));
nrf_timer_mode_set(p_instance->p_reg, p_config->mode);
nrf_timer_bit_width_set(p_instance->p_reg, p_config->bit_width);
nrf_timer_frequency_set(p_instance->p_reg, p_config->frequency);
p_cb->state = NRFX_DRV_STATE_INITIALIZED;
err_code = NRFX_SUCCESS;
NRFX_LOG_INFO("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
void nrfx_timer_uninit(nrfx_timer_t const * const p_instance)
{
NRFX_IRQ_DISABLE(nrfx_get_irq_number(p_instance->p_reg));
#define DISABLE_ALL UINT32_MAX
nrf_timer_shorts_disable(p_instance->p_reg, DISABLE_ALL);
nrf_timer_int_disable(p_instance->p_reg, DISABLE_ALL);
#undef DISABLE_ALL
nrfx_timer_disable(p_instance);
m_cb[p_instance->instance_id].state = NRFX_DRV_STATE_UNINITIALIZED;
NRFX_LOG_INFO("Uninitialized instance: %d.", p_instance->instance_id);
}
void nrfx_timer_enable(nrfx_timer_t const * const p_instance)
{
NRFX_ASSERT(m_cb[p_instance->instance_id].state == NRFX_DRV_STATE_INITIALIZED);
nrf_timer_task_trigger(p_instance->p_reg, NRF_TIMER_TASK_START);
m_cb[p_instance->instance_id].state = NRFX_DRV_STATE_POWERED_ON;
NRFX_LOG_INFO("Enabled instance: %d.", p_instance->instance_id);
}
void nrfx_timer_disable(nrfx_timer_t const * const p_instance)
{
NRFX_ASSERT(m_cb[p_instance->instance_id].state != NRFX_DRV_STATE_UNINITIALIZED);
nrf_timer_task_trigger(p_instance->p_reg, NRF_TIMER_TASK_SHUTDOWN);
m_cb[p_instance->instance_id].state = NRFX_DRV_STATE_INITIALIZED;
NRFX_LOG_INFO("Disabled instance: %d.", p_instance->instance_id);
}
bool nrfx_timer_is_enabled(nrfx_timer_t const * const p_instance)
{
NRFX_ASSERT(m_cb[p_instance->instance_id].state != NRFX_DRV_STATE_UNINITIALIZED);
return (m_cb[p_instance->instance_id].state == NRFX_DRV_STATE_POWERED_ON);
}
void nrfx_timer_resume(nrfx_timer_t const * const p_instance)
{
NRFX_ASSERT(m_cb[p_instance->instance_id].state != NRFX_DRV_STATE_UNINITIALIZED);
nrf_timer_task_trigger(p_instance->p_reg, NRF_TIMER_TASK_START);
NRFX_LOG_INFO("Resumed instance: %d.", p_instance->instance_id);
}
void nrfx_timer_pause(nrfx_timer_t const * const p_instance)
{
NRFX_ASSERT(m_cb[p_instance->instance_id].state != NRFX_DRV_STATE_UNINITIALIZED);
nrf_timer_task_trigger(p_instance->p_reg, NRF_TIMER_TASK_STOP);
NRFX_LOG_INFO("Paused instance: %d.", p_instance->instance_id);
}
void nrfx_timer_clear(nrfx_timer_t const * const p_instance)
{
NRFX_ASSERT(m_cb[p_instance->instance_id].state != NRFX_DRV_STATE_UNINITIALIZED);
nrf_timer_task_trigger(p_instance->p_reg, NRF_TIMER_TASK_CLEAR);
}
void nrfx_timer_increment(nrfx_timer_t const * const p_instance)
{
NRFX_ASSERT(m_cb[p_instance->instance_id].state != NRFX_DRV_STATE_UNINITIALIZED);
NRFX_ASSERT(nrf_timer_mode_get(p_instance->p_reg) != NRF_TIMER_MODE_TIMER);
nrf_timer_task_trigger(p_instance->p_reg, NRF_TIMER_TASK_COUNT);
}
uint32_t nrfx_timer_capture(nrfx_timer_t const * const p_instance,
nrf_timer_cc_channel_t cc_channel)
{
NRFX_ASSERT(m_cb[p_instance->instance_id].state != NRFX_DRV_STATE_UNINITIALIZED);
NRFX_ASSERT(cc_channel < p_instance->cc_channel_count);
nrf_timer_task_trigger(p_instance->p_reg,
nrf_timer_capture_task_get(cc_channel));
return nrf_timer_cc_read(p_instance->p_reg, cc_channel);
}
void nrfx_timer_compare(nrfx_timer_t const * const p_instance,
nrf_timer_cc_channel_t cc_channel,
uint32_t cc_value,
bool enable_int)
{
nrf_timer_int_mask_t timer_int = nrf_timer_compare_int_get(cc_channel);
if (enable_int)
{
nrf_timer_event_clear(p_instance->p_reg, nrf_timer_compare_event_get(cc_channel));
nrf_timer_int_enable(p_instance->p_reg, timer_int);
}
else
{
nrf_timer_int_disable(p_instance->p_reg, timer_int);
}
nrf_timer_cc_write(p_instance->p_reg, cc_channel, cc_value);
NRFX_LOG_INFO("Timer id: %d, capture value set: %lu, channel: %d.",
p_instance->instance_id,
cc_value,
cc_channel);
}
void nrfx_timer_extended_compare(nrfx_timer_t const * const p_instance,
nrf_timer_cc_channel_t cc_channel,
uint32_t cc_value,
nrf_timer_short_mask_t timer_short_mask,
bool enable_int)
{
nrf_timer_shorts_disable(p_instance->p_reg,
(TIMER_SHORTS_COMPARE0_STOP_Msk << cc_channel) |
(TIMER_SHORTS_COMPARE0_CLEAR_Msk << cc_channel));
nrf_timer_shorts_enable(p_instance->p_reg, timer_short_mask);
nrfx_timer_compare(p_instance,
cc_channel,
cc_value,
enable_int);
NRFX_LOG_INFO("Timer id: %d, capture value set: %lu, channel: %d.",
p_instance->instance_id,
cc_value,
cc_channel);
}
void nrfx_timer_compare_int_enable(nrfx_timer_t const * const p_instance,
uint32_t channel)
{
NRFX_ASSERT(m_cb[p_instance->instance_id].state != NRFX_DRV_STATE_UNINITIALIZED);
NRFX_ASSERT(channel < p_instance->cc_channel_count);
nrf_timer_event_clear(p_instance->p_reg,
nrf_timer_compare_event_get(channel));
nrf_timer_int_enable(p_instance->p_reg,
nrf_timer_compare_int_get(channel));
}
void nrfx_timer_compare_int_disable(nrfx_timer_t const * const p_instance,
uint32_t channel)
{
NRFX_ASSERT(m_cb[p_instance->instance_id].state != NRFX_DRV_STATE_UNINITIALIZED);
NRFX_ASSERT(channel < p_instance->cc_channel_count);
nrf_timer_int_disable(p_instance->p_reg,
nrf_timer_compare_int_get(channel));
}
static void irq_handler(NRF_TIMER_Type * p_reg,
timer_control_block_t * p_cb,
uint8_t channel_count)
{
uint8_t i;
for (i = 0; i < channel_count; ++i)
{
nrf_timer_event_t event = nrf_timer_compare_event_get(i);
nrf_timer_int_mask_t int_mask = nrf_timer_compare_int_get(i);
if (nrf_timer_event_check(p_reg, event) &&
nrf_timer_int_enable_check(p_reg, int_mask))
{
nrf_timer_event_clear(p_reg, event);
NRFX_LOG_DEBUG("Compare event, channel: %d.", i);
p_cb->handler(event, p_cb->context);
}
}
}
#if NRFX_CHECK(NRFX_TIMER0_ENABLED)
void nrfx_timer_0_irq_handler(void)
{
irq_handler(NRF_TIMER0, &m_cb[NRFX_TIMER0_INST_IDX],
NRF_TIMER_CC_CHANNEL_COUNT(0));
}
#endif
#if NRFX_CHECK(NRFX_TIMER1_ENABLED)
void nrfx_timer_1_irq_handler(void)
{
irq_handler(NRF_TIMER1, &m_cb[NRFX_TIMER1_INST_IDX],
NRF_TIMER_CC_CHANNEL_COUNT(1));
}
#endif
#if NRFX_CHECK(NRFX_TIMER2_ENABLED)
void nrfx_timer_2_irq_handler(void)
{
irq_handler(NRF_TIMER2, &m_cb[NRFX_TIMER2_INST_IDX],
NRF_TIMER_CC_CHANNEL_COUNT(2));
}
#endif
#if NRFX_CHECK(NRFX_TIMER3_ENABLED)
void nrfx_timer_3_irq_handler(void)
{
irq_handler(NRF_TIMER3, &m_cb[NRFX_TIMER3_INST_IDX],
NRF_TIMER_CC_CHANNEL_COUNT(3));
}
#endif
#if NRFX_CHECK(NRFX_TIMER4_ENABLED)
void nrfx_timer_4_irq_handler(void)
{
irq_handler(NRF_TIMER4, &m_cb[NRFX_TIMER4_INST_IDX],
NRF_TIMER_CC_CHANNEL_COUNT(4));
}
#endif
#endif // NRFX_CHECK(NRFX_TIMER_ENABLED)

View File

@@ -0,0 +1,783 @@
/**
* Copyright (c) 2015 - 2021, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <nrfx.h>
#if NRFX_CHECK(NRFX_TWI_ENABLED)
#if !(NRFX_CHECK(NRFX_TWI0_ENABLED) || NRFX_CHECK(NRFX_TWI1_ENABLED))
#error "No enabled TWI instances. Check <nrfx_config.h>."
#endif
#include <nrfx_twi.h>
#include <hal/nrf_gpio.h>
#include "prs/nrfx_prs.h"
#define NRFX_LOG_MODULE TWI
#include <nrfx_log.h>
#define EVT_TO_STR(event) \
(event == NRFX_TWI_EVT_DONE ? "EVT_DONE" : \
(event == NRFX_TWI_EVT_ADDRESS_NACK ? "EVT_ADDRESS_NACK" : \
(event == NRFX_TWI_EVT_DATA_NACK ? "EVT_DATA_NACK" : \
(event == NRFX_TWI_EVT_OVERRUN ? "EVT_OVERRUN" : \
(event == NRFX_TWI_EVT_BUS_ERROR ? "EVT_BUS_ERROR" : \
"UNKNOWN ERROR")))))
#define EVT_TO_STR_TWI(event) \
(event == NRF_TWI_EVENT_STOPPED ? "NRF_TWI_EVENT_STOPPED" : \
(event == NRF_TWI_EVENT_RXDREADY ? "NRF_TWI_EVENT_RXDREADY" : \
(event == NRF_TWI_EVENT_TXDSENT ? "NRF_TWI_EVENT_TXDSENT" : \
(event == NRF_TWI_EVENT_ERROR ? "NRF_TWI_EVENT_ERROR" : \
(event == NRF_TWI_EVENT_BB ? "NRF_TWI_EVENT_BB" : \
(event == NRF_TWI_EVENT_SUSPENDED ? "NRF_TWI_EVENT_SUSPENDED" : \
"UNKNOWN ERROR"))))))
#define TRANSFER_TO_STR(type) \
(type == NRFX_TWI_XFER_TX ? "XFER_TX" : \
(type == NRFX_TWI_XFER_RX ? "XFER_RX" : \
(type == NRFX_TWI_XFER_TXRX ? "XFER_TXRX" : \
(type == NRFX_TWI_XFER_TXTX ? "XFER_TXTX" : \
"UNKNOWN TRANSFER TYPE"))))
#define TWI_PIN_INIT(_pin) nrf_gpio_cfg((_pin), \
NRF_GPIO_PIN_DIR_INPUT, \
NRF_GPIO_PIN_INPUT_CONNECT, \
NRF_GPIO_PIN_PULLUP, \
NRF_GPIO_PIN_S0D1, \
NRF_GPIO_PIN_NOSENSE)
#define TWI_FLAG_NO_STOP(flags) (flags & NRFX_TWI_FLAG_TX_NO_STOP)
#define TWI_FLAG_SUSPEND(flags) (flags & NRFX_TWI_FLAG_SUSPEND)
#define TWI_FLAG_NO_HANDLER_IN_USE(flags) (flags & NRFX_TWI_FLAG_NO_XFER_EVT_HANDLER)
#define HW_TIMEOUT 100000
/* TWI master driver suspend types. */
typedef enum
{
TWI_NO_SUSPEND, //< Last transfer was not suspended.
TWI_SUSPEND_TX, //< Last transfer was TX and was suspended.
TWI_SUSPEND_RX //< Last transfer was RX and was suspended.
} twi_suspend_t;
// Control block - driver instance local data.
typedef struct
{
nrfx_twi_evt_handler_t handler;
void * p_context;
volatile uint32_t int_mask;
nrfx_twi_xfer_desc_t xfer_desc;
uint32_t flags;
uint8_t * p_curr_buf;
size_t curr_length;
bool curr_tx_no_stop;
twi_suspend_t prev_suspend;
nrfx_drv_state_t state;
bool error;
volatile bool busy;
bool repeated;
size_t bytes_transferred;
bool hold_bus_uninit;
} twi_control_block_t;
static twi_control_block_t m_cb[NRFX_TWI_ENABLED_COUNT];
static nrfx_err_t twi_process_error(uint32_t errorsrc)
{
nrfx_err_t ret = NRFX_ERROR_INTERNAL;
if (errorsrc & NRF_TWI_ERROR_OVERRUN)
{
ret = NRFX_ERROR_DRV_TWI_ERR_OVERRUN;
}
if (errorsrc & NRF_TWI_ERROR_ADDRESS_NACK)
{
ret = NRFX_ERROR_DRV_TWI_ERR_ANACK;
}
if (errorsrc & NRF_TWI_ERROR_DATA_NACK)
{
ret = NRFX_ERROR_DRV_TWI_ERR_DNACK;
}
return ret;
}
static bool xfer_completeness_check(NRF_TWI_Type * p_twi, twi_control_block_t const * p_cb)
{
// If the actual number of transferred bytes is not equal to what was requested,
// but there was no error signaled by the peripheral, this means that something
// unexpected, like a premature STOP condition, was received on the bus.
// In such case the peripheral has to be disabled and re-enabled, so that its
// internal state machine is reinitialized.
if (p_cb->bytes_transferred != p_cb->curr_length)
{
nrf_twi_disable(p_twi);
nrf_twi_enable(p_twi);
return false;
}
else
{
return true;
}
}
nrfx_err_t nrfx_twi_init(nrfx_twi_t const * p_instance,
nrfx_twi_config_t const * p_config,
nrfx_twi_evt_handler_t event_handler,
void * p_context)
{
NRFX_ASSERT(p_config);
NRFX_ASSERT(p_config->scl != p_config->sda);
twi_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
nrfx_err_t err_code;
if (p_cb->state != NRFX_DRV_STATE_UNINITIALIZED)
{
err_code = NRFX_ERROR_INVALID_STATE;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
#if NRFX_CHECK(NRFX_PRS_ENABLED)
static nrfx_irq_handler_t const irq_handlers[NRFX_TWI_ENABLED_COUNT] = {
#if NRFX_CHECK(NRFX_TWI0_ENABLED)
nrfx_twi_0_irq_handler,
#endif
#if NRFX_CHECK(NRFX_TWI1_ENABLED)
nrfx_twi_1_irq_handler,
#endif
};
if (nrfx_prs_acquire(p_instance->p_twi,
irq_handlers[p_instance->drv_inst_idx]) != NRFX_SUCCESS)
{
err_code = NRFX_ERROR_BUSY;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
#endif // NRFX_CHECK(NRFX_PRS_ENABLED)
p_cb->handler = event_handler;
p_cb->p_context = p_context;
p_cb->int_mask = 0;
p_cb->prev_suspend = TWI_NO_SUSPEND;
p_cb->repeated = false;
p_cb->busy = false;
p_cb->hold_bus_uninit = p_config->hold_bus_uninit;
/* To secure correct signal levels on the pins used by the TWI
master when the system is in OFF mode, and when the TWI master is
disabled, these pins must be configured in the GPIO peripheral.
*/
TWI_PIN_INIT(p_config->scl);
TWI_PIN_INIT(p_config->sda);
NRF_TWI_Type * p_twi = p_instance->p_twi;
nrf_twi_pins_set(p_twi, p_config->scl, p_config->sda);
nrf_twi_frequency_set(p_twi,
(nrf_twi_frequency_t)p_config->frequency);
if (p_cb->handler)
{
NRFX_IRQ_PRIORITY_SET(nrfx_get_irq_number(p_instance->p_twi),
p_config->interrupt_priority);
NRFX_IRQ_ENABLE(nrfx_get_irq_number(p_instance->p_twi));
}
p_cb->state = NRFX_DRV_STATE_INITIALIZED;
err_code = NRFX_SUCCESS;
NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
void nrfx_twi_uninit(nrfx_twi_t const * p_instance)
{
twi_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
NRFX_ASSERT(p_cb->state != NRFX_DRV_STATE_UNINITIALIZED);
if (p_cb->handler)
{
NRFX_IRQ_DISABLE(nrfx_get_irq_number(p_instance->p_twi));
}
nrfx_twi_disable(p_instance);
#if NRFX_CHECK(NRFX_PRS_ENABLED)
nrfx_prs_release(p_instance->p_twi);
#endif
if (!p_cb->hold_bus_uninit)
{
nrf_gpio_cfg_default(nrf_twi_scl_pin_get(p_instance->p_twi));
nrf_gpio_cfg_default(nrf_twi_sda_pin_get(p_instance->p_twi));
}
p_cb->state = NRFX_DRV_STATE_UNINITIALIZED;
NRFX_LOG_INFO("Instance uninitialized: %d.", p_instance->drv_inst_idx);
}
void nrfx_twi_enable(nrfx_twi_t const * p_instance)
{
twi_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
NRFX_ASSERT(p_cb->state == NRFX_DRV_STATE_INITIALIZED);
NRF_TWI_Type * p_twi = p_instance->p_twi;
nrf_twi_enable(p_twi);
p_cb->state = NRFX_DRV_STATE_POWERED_ON;
NRFX_LOG_INFO("Instance enabled: %d.", p_instance->drv_inst_idx);
}
void nrfx_twi_disable(nrfx_twi_t const * p_instance)
{
twi_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
NRFX_ASSERT(p_cb->state != NRFX_DRV_STATE_UNINITIALIZED);
NRF_TWI_Type * p_twi = p_instance->p_twi;
nrf_twi_int_disable(p_twi, NRF_TWI_ALL_INTS_MASK);
nrf_twi_shorts_disable(p_twi, NRF_TWI_ALL_SHORTS_MASK);
nrf_twi_disable(p_twi);
p_cb->state = NRFX_DRV_STATE_INITIALIZED;
NRFX_LOG_INFO("Instance disabled: %d.", p_instance->drv_inst_idx);
}
static bool twi_send_byte(NRF_TWI_Type * p_twi,
twi_control_block_t * p_cb)
{
if (p_cb->bytes_transferred < p_cb->curr_length)
{
nrf_twi_txd_set(p_twi, p_cb->p_curr_buf[p_cb->bytes_transferred]);
}
else
{
if (p_cb->curr_tx_no_stop)
{
nrf_twi_task_trigger(p_twi, NRF_TWI_TASK_SUSPEND);
return false;
}
else if(TWI_FLAG_SUSPEND(p_cb->flags))
{
nrf_twi_task_trigger(p_twi, NRF_TWI_TASK_SUSPEND);
p_cb->prev_suspend = TWI_SUSPEND_TX;
return false;
}
else
{
nrf_twi_task_trigger(p_twi, NRF_TWI_TASK_STOP);
}
}
return true;
}
static bool twi_receive_byte(NRF_TWI_Type * p_twi,
twi_control_block_t * p_cb)
{
if (p_cb->bytes_transferred < p_cb->curr_length)
{
p_cb->p_curr_buf[p_cb->bytes_transferred] = nrf_twi_rxd_get(p_twi);
++(p_cb->bytes_transferred);
if ((p_cb->bytes_transferred == p_cb->curr_length - 1) && (!TWI_FLAG_SUSPEND(p_cb->flags)))
{
nrf_twi_shorts_set(p_twi, NRF_TWI_SHORT_BB_STOP_MASK);
}
else if (p_cb->bytes_transferred == p_cb->curr_length && (!TWI_FLAG_SUSPEND(p_cb->flags)))
{
return true;
}
else if (p_cb->bytes_transferred == p_cb->curr_length && TWI_FLAG_SUSPEND(p_cb->flags))
{
p_cb->prev_suspend = TWI_SUSPEND_RX;
return false;
}
nrf_twi_task_trigger(p_twi, NRF_TWI_TASK_RESUME);
}
return true;
}
static bool twi_transfer(NRF_TWI_Type * p_twi,
twi_control_block_t * p_cb)
{
bool stopped = false;
if (nrf_twi_event_check(p_twi, NRF_TWI_EVENT_STOPPED))
{
nrf_twi_event_clear(p_twi, NRF_TWI_EVENT_STOPPED);
NRFX_LOG_DEBUG("TWI: Event: %s.", EVT_TO_STR_TWI(NRF_TWI_EVENT_STOPPED));
// Delay handling of STOPPED event till the end of events processing procedure.
// If penultimate byte is received and function gets interrupted for long enough
// after enabling BB_STOP shortcut, RXDREADY for last byte as well as STOPPED
// may be active at the same time. Therefore RXREADY has to be processed before STOPPED to
// acquire last byte before finishing transmission by returning 'false'.
stopped = true;
}
if (p_cb->error)
{
nrf_twi_event_clear(p_twi, NRF_TWI_EVENT_ERROR);
nrf_twi_event_clear(p_twi, NRF_TWI_EVENT_TXDSENT);
nrf_twi_event_clear(p_twi, NRF_TWI_EVENT_RXDREADY);
}
else if (nrf_twi_event_check(p_twi, NRF_TWI_EVENT_ERROR))
{
nrf_twi_event_clear(p_twi, NRF_TWI_EVENT_ERROR);
NRFX_LOG_DEBUG("TWI: Event: %s.", EVT_TO_STR_TWI(NRF_TWI_EVENT_ERROR));
nrf_twi_task_trigger(p_twi, NRF_TWI_TASK_STOP);
p_cb->error = true;
}
else
{
if (nrf_twi_event_check(p_twi, NRF_TWI_EVENT_TXDSENT))
{
nrf_twi_event_clear(p_twi, NRF_TWI_EVENT_TXDSENT);
++(p_cb->bytes_transferred);
NRFX_LOG_DEBUG("TWI: Event: %s.", EVT_TO_STR_TWI(NRF_TWI_EVENT_TXDSENT));
if (nrf_twi_event_check(p_twi, NRF_TWI_EVENT_ERROR))
{
nrf_twi_event_clear(p_twi, NRF_TWI_EVENT_ERROR);
NRFX_LOG_DEBUG("TWI: Event: %s.", EVT_TO_STR_TWI(NRF_TWI_EVENT_ERROR));
nrf_twi_task_trigger(p_twi, NRF_TWI_TASK_STOP);
p_cb->error = true;
}
else
{
if (!twi_send_byte(p_twi, p_cb))
{
return false;
}
}
}
else if (nrf_twi_event_check(p_twi, NRF_TWI_EVENT_RXDREADY))
{
nrf_twi_event_clear(p_twi, NRF_TWI_EVENT_RXDREADY);
NRFX_LOG_DEBUG("TWI: Event: %s.", EVT_TO_STR_TWI(NRF_TWI_EVENT_RXDREADY));
if (nrf_twi_event_check(p_twi, NRF_TWI_EVENT_ERROR))
{
NRFX_LOG_DEBUG("TWI: Event: %s.", EVT_TO_STR_TWI(NRF_TWI_EVENT_ERROR));
nrf_twi_event_clear(p_twi, NRF_TWI_EVENT_ERROR);
nrf_twi_task_trigger(p_twi, NRF_TWI_TASK_STOP);
p_cb->error = true;
}
else
{
if (!twi_receive_byte(p_twi, p_cb))
{
return false;
}
}
}
}
if (stopped)
{
p_cb->prev_suspend = TWI_NO_SUSPEND;
if (!p_cb->error)
{
p_cb->error = !xfer_completeness_check(p_twi, p_cb);
}
return false;
}
return true;
}
static nrfx_err_t twi_tx_start_transfer(NRF_TWI_Type * p_twi,
twi_control_block_t * p_cb)
{
nrfx_err_t ret_code = NRFX_SUCCESS;
volatile int32_t hw_timeout;
hw_timeout = HW_TIMEOUT;
nrf_twi_event_clear(p_twi, NRF_TWI_EVENT_STOPPED);
nrf_twi_event_clear(p_twi, NRF_TWI_EVENT_ERROR);
nrf_twi_event_clear(p_twi, NRF_TWI_EVENT_TXDSENT);
nrf_twi_event_clear(p_twi, NRF_TWI_EVENT_RXDREADY);
nrf_twi_shorts_set(p_twi, 0);
p_cb->bytes_transferred = 0;
p_cb->error = false;
// In case TWI is suspended resume its operation.
nrf_twi_task_trigger(p_twi, NRF_TWI_TASK_RESUME);
if (p_cb->prev_suspend != TWI_SUSPEND_TX)
{
nrf_twi_task_trigger(p_twi, NRF_TWI_TASK_STARTTX);
}
(void)twi_send_byte(p_twi, p_cb);
if (p_cb->handler)
{
p_cb->int_mask = NRF_TWI_INT_STOPPED_MASK |
NRF_TWI_INT_ERROR_MASK |
NRF_TWI_INT_TXDSENT_MASK |
NRF_TWI_INT_RXDREADY_MASK;
nrf_twi_int_enable(p_twi, p_cb->int_mask);
}
else
{
while ((hw_timeout > 0) &&
twi_transfer(p_twi, p_cb))
{
hw_timeout--;
}
if (p_cb->error)
{
uint32_t errorsrc = nrf_twi_errorsrc_get_and_clear(p_twi);
if (errorsrc)
{
ret_code = twi_process_error(errorsrc);
}
else
{
ret_code = NRFX_ERROR_INTERNAL;
}
}
if (hw_timeout <= 0)
{
nrf_twi_disable(p_twi);
nrf_twi_enable(p_twi);
ret_code = NRFX_ERROR_INTERNAL;
}
}
return ret_code;
}
static nrfx_err_t twi_rx_start_transfer(NRF_TWI_Type * p_twi,
twi_control_block_t * p_cb)
{
nrfx_err_t ret_code = NRFX_SUCCESS;
volatile int32_t hw_timeout;
hw_timeout = HW_TIMEOUT;
nrf_twi_event_clear(p_twi, NRF_TWI_EVENT_STOPPED);
nrf_twi_event_clear(p_twi, NRF_TWI_EVENT_ERROR);
nrf_twi_event_clear(p_twi, NRF_TWI_EVENT_TXDSENT);
nrf_twi_event_clear(p_twi, NRF_TWI_EVENT_RXDREADY);
p_cb->bytes_transferred = 0;
p_cb->error = false;
if ((p_cb->curr_length == 1) && (!TWI_FLAG_SUSPEND(p_cb->flags)))
{
nrf_twi_shorts_set(p_twi, NRF_TWI_SHORT_BB_STOP_MASK);
}
else
{
nrf_twi_shorts_set(p_twi, NRF_TWI_SHORT_BB_SUSPEND_MASK);
}
// In case TWI is suspended resume its operation.
nrf_twi_task_trigger(p_twi, NRF_TWI_TASK_RESUME);
if (p_cb->prev_suspend != TWI_SUSPEND_RX)
{
nrf_twi_task_trigger(p_twi, NRF_TWI_TASK_STARTRX);
}
if (p_cb->handler)
{
p_cb->int_mask = NRF_TWI_INT_STOPPED_MASK |
NRF_TWI_INT_ERROR_MASK |
NRF_TWI_INT_TXDSENT_MASK |
NRF_TWI_INT_RXDREADY_MASK;
nrf_twi_int_enable(p_twi, p_cb->int_mask);
}
else
{
while ((hw_timeout > 0) &&
twi_transfer(p_twi, p_cb))
{
hw_timeout--;
}
if (p_cb->error)
{
uint32_t errorsrc = nrf_twi_errorsrc_get_and_clear(p_twi);
if (errorsrc)
{
ret_code = twi_process_error(errorsrc);
}
else
{
ret_code = NRFX_ERROR_INTERNAL;
}
}
if (hw_timeout <= 0)
{
nrf_twi_disable(p_twi);
nrf_twi_enable(p_twi);
ret_code = NRFX_ERROR_INTERNAL;
}
}
return ret_code;
}
__STATIC_INLINE nrfx_err_t twi_xfer(NRF_TWI_Type * p_twi,
twi_control_block_t * p_cb,
nrfx_twi_xfer_desc_t const * p_xfer_desc,
uint32_t flags)
{
nrfx_err_t err_code = NRFX_SUCCESS;
if ((p_cb->prev_suspend == TWI_SUSPEND_TX) && (p_xfer_desc->type == NRFX_TWI_XFER_RX))
{
/* RX is invalid after TX suspend */
return NRFX_ERROR_INVALID_STATE;
}
else if ((p_cb->prev_suspend == TWI_SUSPEND_RX) && (p_xfer_desc->type != NRFX_TWI_XFER_RX))
{
/* TX, TXRX and TXTX are invalid after RX suspend */
return NRFX_ERROR_INVALID_STATE;
}
/* Block TWI interrupts to ensure that function is not interrupted by TWI interrupt. */
nrf_twi_int_disable(p_twi, NRF_TWI_ALL_INTS_MASK);
if (p_cb->busy)
{
nrf_twi_int_enable(p_twi, p_cb->int_mask);
err_code = NRFX_ERROR_BUSY;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
else
{
p_cb->busy = (TWI_FLAG_NO_HANDLER_IN_USE(flags)) ? false : true;
}
p_cb->flags = flags;
p_cb->xfer_desc = *p_xfer_desc;
p_cb->curr_length = p_xfer_desc->primary_length;
p_cb->p_curr_buf = p_xfer_desc->p_primary_buf;
nrf_twi_address_set(p_twi, p_xfer_desc->address);
if (p_xfer_desc->type != NRFX_TWI_XFER_RX)
{
p_cb->curr_tx_no_stop = ((p_xfer_desc->type == NRFX_TWI_XFER_TX) &&
!(flags & NRFX_TWI_FLAG_TX_NO_STOP)) ? false : true;
err_code = twi_tx_start_transfer(p_twi, p_cb);
}
else
{
err_code = twi_rx_start_transfer(p_twi, p_cb);
}
if (p_cb->handler == NULL)
{
p_cb->busy = false;
}
return err_code;
}
bool nrfx_twi_is_busy(nrfx_twi_t const * p_instance)
{
twi_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
return p_cb->busy;
}
nrfx_err_t nrfx_twi_xfer(nrfx_twi_t const * p_instance,
nrfx_twi_xfer_desc_t const * p_xfer_desc,
uint32_t flags)
{
nrfx_err_t err_code = NRFX_SUCCESS;
twi_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
// TXRX and TXTX transfers are supported only in non-blocking mode.
NRFX_ASSERT( !((p_cb->handler == NULL) && (p_xfer_desc->type == NRFX_TWI_XFER_TXRX)));
NRFX_ASSERT( !((p_cb->handler == NULL) && (p_xfer_desc->type == NRFX_TWI_XFER_TXTX)));
NRFX_LOG_INFO("Transfer type: %s.", TRANSFER_TO_STR(p_xfer_desc->type));
NRFX_LOG_INFO("Transfer buffers length: primary: %d, secondary: %d.",
p_xfer_desc->primary_length,
p_xfer_desc->secondary_length);
NRFX_LOG_DEBUG("Primary buffer data:");
NRFX_LOG_HEXDUMP_DEBUG(p_xfer_desc->p_primary_buf,
p_xfer_desc->primary_length * sizeof(p_xfer_desc->p_primary_buf[0]));
NRFX_LOG_DEBUG("Secondary buffer data:");
NRFX_LOG_HEXDUMP_DEBUG(p_xfer_desc->p_secondary_buf,
p_xfer_desc->secondary_length * sizeof(p_xfer_desc->p_secondary_buf[0]));
err_code = twi_xfer((NRF_TWI_Type *)p_instance->p_twi, p_cb, p_xfer_desc, flags);
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
nrfx_err_t nrfx_twi_tx(nrfx_twi_t const * p_instance,
uint8_t address,
uint8_t const * p_data,
size_t length,
bool no_stop)
{
nrfx_twi_xfer_desc_t xfer = NRFX_TWI_XFER_DESC_TX(address, (uint8_t*)p_data, length);
return nrfx_twi_xfer(p_instance, &xfer, no_stop ? NRFX_TWI_FLAG_TX_NO_STOP : 0);
}
nrfx_err_t nrfx_twi_rx(nrfx_twi_t const * p_instance,
uint8_t address,
uint8_t * p_data,
size_t length)
{
nrfx_twi_xfer_desc_t xfer = NRFX_TWI_XFER_DESC_RX(address, p_data, length);
return nrfx_twi_xfer(p_instance, &xfer, 0);
}
size_t nrfx_twi_data_count_get(nrfx_twi_t const * const p_instance)
{
return m_cb[p_instance->drv_inst_idx].bytes_transferred;
}
uint32_t nrfx_twi_stopped_event_get(nrfx_twi_t const * p_instance)
{
return (uint32_t)nrf_twi_event_address_get(p_instance->p_twi, NRF_TWI_EVENT_STOPPED);
}
static void twi_irq_handler(NRF_TWI_Type * p_twi, twi_control_block_t * p_cb)
{
NRFX_ASSERT(p_cb->handler);
if (twi_transfer(p_twi, p_cb))
{
return;
}
if (!p_cb->error &&
((p_cb->xfer_desc.type == NRFX_TWI_XFER_TXRX) ||
(p_cb->xfer_desc.type == NRFX_TWI_XFER_TXTX)) &&
p_cb->p_curr_buf == p_cb->xfer_desc.p_primary_buf)
{
p_cb->p_curr_buf = p_cb->xfer_desc.p_secondary_buf;
p_cb->curr_length = p_cb->xfer_desc.secondary_length;
p_cb->curr_tx_no_stop = (p_cb->flags & NRFX_TWI_FLAG_TX_NO_STOP);
p_cb->prev_suspend = TWI_NO_SUSPEND;
if (p_cb->xfer_desc.type == NRFX_TWI_XFER_TXTX)
{
(void)twi_tx_start_transfer(p_twi, p_cb);
}
else
{
(void)twi_rx_start_transfer(p_twi, p_cb);
}
}
else
{
nrfx_twi_evt_t event;
event.xfer_desc = p_cb->xfer_desc;
if (p_cb->error)
{
uint32_t errorsrc = nrf_twi_errorsrc_get_and_clear(p_twi);
if (errorsrc & NRF_TWI_ERROR_ADDRESS_NACK)
{
event.type = NRFX_TWI_EVT_ADDRESS_NACK;
NRFX_LOG_DEBUG("Event: %s.", EVT_TO_STR(NRFX_TWI_EVT_ADDRESS_NACK));
}
else if (errorsrc & NRF_TWI_ERROR_DATA_NACK)
{
event.type = NRFX_TWI_EVT_DATA_NACK;
NRFX_LOG_DEBUG("Event: %s.", EVT_TO_STR(NRFX_TWI_EVT_DATA_NACK));
}
else if (errorsrc & NRF_TWI_ERROR_OVERRUN)
{
event.type = NRFX_TWI_EVT_OVERRUN;
NRFX_LOG_DEBUG("Event: %s.", EVT_TO_STR(NRFX_TWI_EVT_OVERRUN));
}
else
{
event.type = NRFX_TWI_EVT_BUS_ERROR;
NRFX_LOG_DEBUG("Event: %s.", EVT_TO_STR(NRFX_TWI_EVT_BUS_ERROR));
}
}
else
{
event.type = NRFX_TWI_EVT_DONE;
NRFX_LOG_DEBUG("Event: %s.", EVT_TO_STR(NRFX_TWI_EVT_DONE));
}
p_cb->busy = false;
if (!(TWI_FLAG_NO_HANDLER_IN_USE(p_cb->flags)) || p_cb->error)
{
p_cb->handler(&event, p_cb->p_context);
}
}
}
#if NRFX_CHECK(NRFX_TWI0_ENABLED)
void nrfx_twi_0_irq_handler(void)
{
twi_irq_handler(NRF_TWI0, &m_cb[NRFX_TWI0_INST_IDX]);
}
#endif
#if NRFX_CHECK(NRFX_TWI1_ENABLED)
void nrfx_twi_1_irq_handler(void)
{
twi_irq_handler(NRF_TWI1, &m_cb[NRFX_TWI1_INST_IDX]);
}
#endif
#endif // NRFX_CHECK(NRFX_TWI_ENABLED)

View File

@@ -0,0 +1,90 @@
/**
* Copyright (c) 2019 - 2021, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <nrfx.h>
#include <nrf_gpio.h>
#define TWI_TWIM_PIN_CONFIGURE(_pin) nrf_gpio_cfg((_pin), \
NRF_GPIO_PIN_DIR_OUTPUT, \
NRF_GPIO_PIN_INPUT_CONNECT, \
NRF_GPIO_PIN_PULLUP, \
NRF_GPIO_PIN_S0D1, \
NRF_GPIO_PIN_NOSENSE)
nrfx_err_t nrfx_twi_twim_bus_recover(uint32_t scl_pin, uint32_t sda_pin)
{
nrf_gpio_pin_set(scl_pin);
nrf_gpio_pin_set(sda_pin);
TWI_TWIM_PIN_CONFIGURE(scl_pin);
TWI_TWIM_PIN_CONFIGURE(sda_pin);
NRFX_DELAY_US(4);
for (uint8_t i = 0; i < 9; i++)
{
if (nrf_gpio_pin_read(sda_pin))
{
break;
}
else
{
// Pulse CLOCK signal
nrf_gpio_pin_clear(scl_pin);
NRFX_DELAY_US(4);
nrf_gpio_pin_set(scl_pin);
NRFX_DELAY_US(4);
}
}
// Generate a STOP condition on the bus
nrf_gpio_pin_clear(sda_pin);
NRFX_DELAY_US(4);
nrf_gpio_pin_set(sda_pin);
NRFX_DELAY_US(4);
if (nrf_gpio_pin_read(sda_pin))
{
return NRFX_SUCCESS;
}
else
{
return NRFX_ERROR_INTERNAL;
}
}

View File

@@ -0,0 +1,872 @@
/**
* Copyright (c) 2015 - 2021, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <nrfx.h>
#if NRFX_CHECK(NRFX_TWIM_ENABLED)
#if !(NRFX_CHECK(NRFX_TWIM0_ENABLED) || \
NRFX_CHECK(NRFX_TWIM1_ENABLED) || \
NRFX_CHECK(NRFX_TWIM2_ENABLED) || \
NRFX_CHECK(NRFX_TWIM3_ENABLED))
#error "No enabled TWIM instances. Check <nrfx_config.h>."
#endif
#include <nrfx_twim.h>
#include <hal/nrf_gpio.h>
#include "prs/nrfx_prs.h"
#define NRFX_LOG_MODULE TWIM
#include <nrfx_log.h>
#define EVT_TO_STR(event) \
(event == NRFX_TWIM_EVT_DONE ? "EVT_DONE" : \
(event == NRFX_TWIM_EVT_ADDRESS_NACK ? "EVT_ADDRESS_NACK" : \
(event == NRFX_TWIM_EVT_DATA_NACK ? "EVT_DATA_NACK" : \
(event == NRFX_TWIM_EVT_OVERRUN ? "EVT_OVERRUN" : \
(event == NRFX_TWIM_EVT_BUS_ERROR ? "EVT_BUS_ERROR" : \
"UNKNOWN ERROR")))))
#define EVT_TO_STR_TWIM(event) \
(event == NRF_TWIM_EVENT_STOPPED ? "NRF_TWIM_EVENT_STOPPED" : \
(event == NRF_TWIM_EVENT_ERROR ? "NRF_TWIM_EVENT_ERROR" : \
(event == NRF_TWIM_EVENT_SUSPENDED ? "NRF_TWIM_EVENT_SUSPENDED" : \
(event == NRF_TWIM_EVENT_RXSTARTED ? "NRF_TWIM_EVENT_RXSTARTED" : \
(event == NRF_TWIM_EVENT_TXSTARTED ? "NRF_TWIM_EVENT_TXSTARTED" : \
(event == NRF_TWIM_EVENT_LASTRX ? "NRF_TWIM_EVENT_LASTRX" : \
(event == NRF_TWIM_EVENT_LASTTX ? "NRF_TWIM_EVENT_LASTTX" : \
"UNKNOWN ERROR")))))))
#define TRANSFER_TO_STR(type) \
(type == NRFX_TWIM_XFER_TX ? "XFER_TX" : \
(type == NRFX_TWIM_XFER_RX ? "XFER_RX" : \
(type == NRFX_TWIM_XFER_TXRX ? "XFER_TXRX" : \
(type == NRFX_TWIM_XFER_TXTX ? "XFER_TXTX" : \
"UNKNOWN TRANSFER TYPE"))))
#define TWIM_PIN_INIT(_pin) nrf_gpio_cfg((_pin), \
NRF_GPIO_PIN_DIR_INPUT, \
NRF_GPIO_PIN_INPUT_CONNECT, \
NRF_GPIO_PIN_PULLUP, \
NRF_GPIO_PIN_S0D1, \
NRF_GPIO_PIN_NOSENSE)
#define TWIMX_LENGTH_VALIDATE(peripheral, drv_inst_idx, len1, len2) \
(((drv_inst_idx) == NRFX_CONCAT_3(NRFX_, peripheral, _INST_IDX)) && \
NRFX_EASYDMA_LENGTH_VALIDATE(peripheral, len1, len2))
#if NRFX_CHECK(NRFX_TWIM0_ENABLED)
#define TWIM0_LENGTH_VALIDATE(...) TWIMX_LENGTH_VALIDATE(TWIM0, __VA_ARGS__)
#else
#define TWIM0_LENGTH_VALIDATE(...) 0
#endif
#if NRFX_CHECK(NRFX_TWIM1_ENABLED)
#define TWIM1_LENGTH_VALIDATE(...) TWIMX_LENGTH_VALIDATE(TWIM1, __VA_ARGS__)
#else
#define TWIM1_LENGTH_VALIDATE(...) 0
#endif
#if NRFX_CHECK(NRFX_TWIM2_ENABLED)
#define TWIM2_LENGTH_VALIDATE(...) TWIMX_LENGTH_VALIDATE(TWIM2, __VA_ARGS__)
#else
#define TWIM2_LENGTH_VALIDATE(...) 0
#endif
#if NRFX_CHECK(NRFX_TWIM3_ENABLED)
#define TWIM3_LENGTH_VALIDATE(...) TWIMX_LENGTH_VALIDATE(TWIM3, __VA_ARGS__)
#else
#define TWIM3_LENGTH_VALIDATE(...) 0
#endif
#define TWIM_LENGTH_VALIDATE(drv_inst_idx, len1, len2) \
(TWIM0_LENGTH_VALIDATE(drv_inst_idx, len1, len2) || \
TWIM1_LENGTH_VALIDATE(drv_inst_idx, len1, len2) || \
TWIM2_LENGTH_VALIDATE(drv_inst_idx, len1, len2) || \
TWIM3_LENGTH_VALIDATE(drv_inst_idx, len1, len2))
// Control block - driver instance local data.
typedef struct
{
nrfx_twim_evt_handler_t handler;
void * p_context;
volatile uint32_t int_mask;
nrfx_twim_xfer_desc_t xfer_desc;
uint32_t flags;
uint8_t * p_curr_buf;
size_t curr_length;
bool curr_no_stop;
nrfx_drv_state_t state;
bool error;
volatile bool busy;
bool repeated;
uint8_t bytes_transferred;
bool hold_bus_uninit;
#if NRFX_CHECK(NRFX_TWIM_NRF52_ANOMALY_109_WORKAROUND_ENABLED)
nrf_twim_frequency_t bus_frequency;
#endif
} twim_control_block_t;
static twim_control_block_t m_cb[NRFX_TWIM_ENABLED_COUNT];
static nrfx_err_t twi_process_error(uint32_t errorsrc)
{
nrfx_err_t ret = NRFX_ERROR_INTERNAL;
if (errorsrc & NRF_TWIM_ERROR_OVERRUN)
{
ret = NRFX_ERROR_DRV_TWI_ERR_OVERRUN;
}
if (errorsrc & NRF_TWIM_ERROR_ADDRESS_NACK)
{
ret = NRFX_ERROR_DRV_TWI_ERR_ANACK;
}
if (errorsrc & NRF_TWIM_ERROR_DATA_NACK)
{
ret = NRFX_ERROR_DRV_TWI_ERR_DNACK;
}
return ret;
}
static bool xfer_completeness_check(NRF_TWIM_Type * p_twim, twim_control_block_t const * p_cb)
{
// If the actual number of transferred bytes is not equal to what was requested,
// but there was no error signaled by the peripheral, this means that something
// unexpected, like a premature STOP condition, was received on the bus.
// In such case the peripheral has to be disabled and re-enabled, so that its
// internal state machine is reinitialized.
bool transfer_complete = true;
switch (p_cb->xfer_desc.type)
{
case NRFX_TWIM_XFER_TXTX:
// int_mask variable is used to determine which length should be checked
// against number of bytes latched in EasyDMA.
// NRF_TWIM_INT_SUSPENDED_MASK is configured only in first TX of TXTX transfer.
if (((p_cb->int_mask & NRF_TWIM_INT_SUSPENDED_MASK) &&
(nrf_twim_txd_amount_get(p_twim) != p_cb->xfer_desc.primary_length)) ||
(!(p_cb->int_mask & NRF_TWIM_INT_SUSPENDED_MASK) &&
(nrf_twim_txd_amount_get(p_twim) != p_cb->xfer_desc.secondary_length)))
{
transfer_complete = false;
}
break;
case NRFX_TWIM_XFER_TXRX:
if ((nrf_twim_txd_amount_get(p_twim) != p_cb->xfer_desc.primary_length) ||
(nrf_twim_rxd_amount_get(p_twim) != p_cb->xfer_desc.secondary_length))
{
transfer_complete = false;
}
break;
case NRFX_TWIM_XFER_TX:
if (nrf_twim_txd_amount_get(p_twim) != p_cb->xfer_desc.primary_length)
{
transfer_complete = false;
}
break;
case NRFX_TWIM_XFER_RX:
if (nrf_twim_rxd_amount_get(p_twim) != p_cb->xfer_desc.primary_length)
{
transfer_complete = false;
}
break;
default:
break;
}
if (!transfer_complete)
{
nrf_twim_disable(p_twim);
nrf_twim_enable(p_twim);
}
return transfer_complete;
}
nrfx_err_t nrfx_twim_init(nrfx_twim_t const * p_instance,
nrfx_twim_config_t const * p_config,
nrfx_twim_evt_handler_t event_handler,
void * p_context)
{
NRFX_ASSERT(p_config);
NRFX_ASSERT(p_config->scl != p_config->sda);
twim_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
nrfx_err_t err_code;
if (p_cb->state != NRFX_DRV_STATE_UNINITIALIZED)
{
err_code = NRFX_ERROR_INVALID_STATE;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
#if NRFX_CHECK(NRFX_PRS_ENABLED)
static nrfx_irq_handler_t const irq_handlers[NRFX_TWIM_ENABLED_COUNT] = {
#if NRFX_CHECK(NRFX_TWIM0_ENABLED)
nrfx_twim_0_irq_handler,
#endif
#if NRFX_CHECK(NRFX_TWIM1_ENABLED)
nrfx_twim_1_irq_handler,
#endif
#if NRFX_CHECK(NRFX_TWIM2_ENABLED)
nrfx_twim_2_irq_handler,
#endif
#if NRFX_CHECK(NRFX_TWIM3_ENABLED)
nrfx_twim_3_irq_handler,
#endif
};
if (nrfx_prs_acquire(p_instance->p_twim,
irq_handlers[p_instance->drv_inst_idx]) != NRFX_SUCCESS)
{
err_code = NRFX_ERROR_BUSY;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
#endif // NRFX_CHECK(NRFX_PRS_ENABLED)
p_cb->handler = event_handler;
p_cb->p_context = p_context;
p_cb->int_mask = 0;
p_cb->repeated = false;
p_cb->busy = false;
p_cb->hold_bus_uninit = p_config->hold_bus_uninit;
#if NRFX_CHECK(NRFX_TWIM_NRF52_ANOMALY_109_WORKAROUND_ENABLED)
p_cb->bus_frequency = (nrf_twim_frequency_t)p_config->frequency;
#endif
/* To secure correct signal levels on the pins used by the TWI
master when the system is in OFF mode, and when the TWI master is
disabled, these pins must be configured in the GPIO peripheral.
*/
TWIM_PIN_INIT(p_config->scl);
TWIM_PIN_INIT(p_config->sda);
NRF_TWIM_Type * p_twim = p_instance->p_twim;
nrf_twim_pins_set(p_twim, p_config->scl, p_config->sda);
nrf_twim_frequency_set(p_twim,
(nrf_twim_frequency_t)p_config->frequency);
if (p_cb->handler)
{
NRFX_IRQ_PRIORITY_SET(nrfx_get_irq_number(p_instance->p_twim),
p_config->interrupt_priority);
NRFX_IRQ_ENABLE(nrfx_get_irq_number(p_instance->p_twim));
}
p_cb->state = NRFX_DRV_STATE_INITIALIZED;
err_code = NRFX_SUCCESS;
NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
void nrfx_twim_uninit(nrfx_twim_t const * p_instance)
{
twim_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
NRFX_ASSERT(p_cb->state != NRFX_DRV_STATE_UNINITIALIZED);
if (p_cb->handler)
{
NRFX_IRQ_DISABLE(nrfx_get_irq_number(p_instance->p_twim));
}
nrfx_twim_disable(p_instance);
#if NRFX_CHECK(NRFX_PRS_ENABLED)
nrfx_prs_release(p_instance->p_twim);
#endif
if (!p_cb->hold_bus_uninit)
{
nrf_gpio_cfg_default(p_instance->p_twim->PSEL.SCL);
nrf_gpio_cfg_default(p_instance->p_twim->PSEL.SDA);
}
p_cb->state = NRFX_DRV_STATE_UNINITIALIZED;
NRFX_LOG_INFO("Instance uninitialized: %d.", p_instance->drv_inst_idx);
}
void nrfx_twim_enable(nrfx_twim_t const * p_instance)
{
twim_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
NRFX_ASSERT(p_cb->state == NRFX_DRV_STATE_INITIALIZED);
nrf_twim_enable(p_instance->p_twim);
p_cb->state = NRFX_DRV_STATE_POWERED_ON;
NRFX_LOG_INFO("Instance enabled: %d.", p_instance->drv_inst_idx);
}
void nrfx_twim_disable(nrfx_twim_t const * p_instance)
{
twim_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
NRFX_ASSERT(p_cb->state != NRFX_DRV_STATE_UNINITIALIZED);
NRF_TWIM_Type * p_twim = p_instance->p_twim;
p_cb->int_mask = 0;
nrf_twim_int_disable(p_twim, NRF_TWIM_ALL_INTS_MASK);
nrf_twim_shorts_disable(p_twim, NRF_TWIM_ALL_SHORTS_MASK);
nrf_twim_disable(p_twim);
p_cb->state = NRFX_DRV_STATE_INITIALIZED;
NRFX_LOG_INFO("Instance disabled: %d.", p_instance->drv_inst_idx);
}
bool nrfx_twim_is_busy(nrfx_twim_t const * p_instance)
{
twim_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
return p_cb->busy;
}
__STATIC_INLINE void twim_list_enable_handle(NRF_TWIM_Type * p_twim, uint32_t flags)
{
if (NRFX_TWIM_FLAG_TX_POSTINC & flags)
{
nrf_twim_tx_list_enable(p_twim);
}
else
{
nrf_twim_tx_list_disable(p_twim);
}
if (NRFX_TWIM_FLAG_RX_POSTINC & flags)
{
nrf_twim_rx_list_enable(p_twim);
}
else
{
nrf_twim_rx_list_disable(p_twim);
}
}
__STATIC_INLINE nrfx_err_t twim_xfer(twim_control_block_t * p_cb,
NRF_TWIM_Type * p_twim,
nrfx_twim_xfer_desc_t const * p_xfer_desc,
uint32_t flags)
{
nrfx_err_t err_code = NRFX_SUCCESS;
nrf_twim_task_t start_task = NRF_TWIM_TASK_STARTTX;
p_cb->error = false;
if (!nrfx_is_in_ram(p_xfer_desc->p_primary_buf))
{
err_code = NRFX_ERROR_INVALID_ADDR;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
/* Block TWI interrupts to ensure that function is not interrupted by TWI interrupt. */
nrf_twim_int_disable(p_twim, NRF_TWIM_ALL_INTS_MASK);
if (p_cb->busy)
{
nrf_twim_int_enable(p_twim, p_cb->int_mask);
err_code = NRFX_ERROR_BUSY;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
else
{
p_cb->busy = ((NRFX_TWIM_FLAG_NO_XFER_EVT_HANDLER & flags) ||
(NRFX_TWIM_FLAG_REPEATED_XFER & flags)) ? false: true;
}
p_cb->xfer_desc = *p_xfer_desc;
p_cb->repeated = (flags & NRFX_TWIM_FLAG_REPEATED_XFER) ? true : false;
p_cb->flags = flags;
nrf_twim_address_set(p_twim, p_xfer_desc->address);
nrf_twim_event_clear(p_twim, NRF_TWIM_EVENT_STOPPED);
nrf_twim_event_clear(p_twim, NRF_TWIM_EVENT_ERROR);
nrf_twim_event_clear(p_twim, NRF_TWIM_EVENT_LASTTX);
nrf_twim_event_clear(p_twim, NRF_TWIM_EVENT_SUSPENDED);
twim_list_enable_handle(p_twim, flags);
switch (p_xfer_desc->type)
{
case NRFX_TWIM_XFER_TXTX:
NRFX_ASSERT(!(flags & NRFX_TWIM_FLAG_REPEATED_XFER));
NRFX_ASSERT(!(flags & NRFX_TWIM_FLAG_HOLD_XFER));
NRFX_ASSERT(!(flags & NRFX_TWIM_FLAG_NO_XFER_EVT_HANDLER));
if (!nrfx_is_in_ram(p_xfer_desc->p_secondary_buf))
{
err_code = NRFX_ERROR_INVALID_ADDR;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
nrf_twim_shorts_set(p_twim, NRF_TWIM_SHORT_LASTTX_SUSPEND_MASK);
nrf_twim_tx_buffer_set(p_twim, p_xfer_desc->p_primary_buf, p_xfer_desc->primary_length);
nrf_twim_event_clear(p_twim, NRF_TWIM_EVENT_TXSTARTED);
nrf_twim_task_trigger(p_twim, NRF_TWIM_TASK_RESUME);
nrf_twim_task_trigger(p_twim, NRF_TWIM_TASK_STARTTX);
while (!nrf_twim_event_check(p_twim, NRF_TWIM_EVENT_TXSTARTED))
{}
NRFX_LOG_DEBUG("TWIM: Event: %s.", EVT_TO_STR_TWIM(NRF_TWIM_EVENT_TXSTARTED));
nrf_twim_event_clear(p_twim, NRF_TWIM_EVENT_TXSTARTED);
nrf_twim_tx_buffer_set(p_twim, p_xfer_desc->p_secondary_buf, p_xfer_desc->secondary_length);
p_cb->int_mask = NRF_TWIM_INT_SUSPENDED_MASK;
break;
case NRFX_TWIM_XFER_TXRX:
nrf_twim_tx_buffer_set(p_twim, p_xfer_desc->p_primary_buf, p_xfer_desc->primary_length);
if (!nrfx_is_in_ram(p_xfer_desc->p_secondary_buf))
{
err_code = NRFX_ERROR_INVALID_ADDR;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
nrf_twim_rx_buffer_set(p_twim, p_xfer_desc->p_secondary_buf, p_xfer_desc->secondary_length);
nrf_twim_shorts_set(p_twim, NRF_TWIM_SHORT_LASTTX_STARTRX_MASK |
NRF_TWIM_SHORT_LASTRX_STOP_MASK);
p_cb->int_mask = NRF_TWIM_INT_STOPPED_MASK;
nrf_twim_task_trigger(p_twim, NRF_TWIM_TASK_RESUME);
break;
case NRFX_TWIM_XFER_TX:
nrf_twim_tx_buffer_set(p_twim, p_xfer_desc->p_primary_buf, p_xfer_desc->primary_length);
if (NRFX_TWIM_FLAG_TX_NO_STOP & flags)
{
nrf_twim_shorts_set(p_twim, NRF_TWIM_SHORT_LASTTX_SUSPEND_MASK);
p_cb->int_mask = NRF_TWIM_INT_SUSPENDED_MASK;
}
else
{
nrf_twim_shorts_set(p_twim, NRF_TWIM_SHORT_LASTTX_STOP_MASK);
p_cb->int_mask = NRF_TWIM_INT_STOPPED_MASK;
}
nrf_twim_task_trigger(p_twim, NRF_TWIM_TASK_RESUME);
break;
case NRFX_TWIM_XFER_RX:
nrf_twim_rx_buffer_set(p_twim, p_xfer_desc->p_primary_buf, p_xfer_desc->primary_length);
nrf_twim_shorts_set(p_twim, NRF_TWIM_SHORT_LASTRX_STOP_MASK);
p_cb->int_mask = NRF_TWIM_INT_STOPPED_MASK;
start_task = NRF_TWIM_TASK_STARTRX;
nrf_twim_task_trigger(p_twim, NRF_TWIM_TASK_RESUME);
break;
default:
err_code = NRFX_ERROR_INVALID_PARAM;
break;
}
if (!(flags & NRFX_TWIM_FLAG_HOLD_XFER) && (p_xfer_desc->type != NRFX_TWIM_XFER_TXTX))
{
nrf_twim_task_trigger(p_twim, start_task);
}
if (p_cb->handler)
{
if (flags & NRFX_TWIM_FLAG_NO_XFER_EVT_HANDLER)
{
p_cb->int_mask = 0;
}
if (!(flags & NRFX_TWIM_FLAG_NO_SPURIOUS_STOP_CHECK))
{
p_cb->int_mask |= NRF_TWIM_INT_STOPPED_MASK;
}
// Interrupts for ERROR are implicitly enabled, regardless of driver configuration.
p_cb->int_mask |= NRF_TWIM_INT_ERROR_MASK;
nrf_twim_int_enable(p_twim, p_cb->int_mask);
#if NRFX_CHECK(NRFX_TWIM_NRF52_ANOMALY_109_WORKAROUND_ENABLED)
if ((flags & NRFX_TWIM_FLAG_HOLD_XFER) && (p_xfer_desc->type != NRFX_TWIM_XFER_RX))
{
twim_list_enable_handle(p_twim, 0);
p_twim->FREQUENCY = 0;
nrf_twim_event_clear(p_twim, NRF_TWIM_EVENT_TXSTARTED);
nrf_twim_int_enable(p_twim, NRF_TWIM_INT_TXSTARTED_MASK);
}
else
{
nrf_twim_frequency_set(p_twim, p_cb->bus_frequency);
}
#endif
}
else
{
bool transmission_finished = false;
do {
if (nrf_twim_event_check(p_twim, NRF_TWIM_EVENT_SUSPENDED))
{
NRFX_LOG_DEBUG("TWIM: Event: %s.", EVT_TO_STR_TWIM(NRF_TWIM_EVENT_SUSPENDED));
transmission_finished = true;
}
if (nrf_twim_event_check(p_twim, NRF_TWIM_EVENT_STOPPED))
{
nrf_twim_event_clear(p_twim, NRF_TWIM_EVENT_STOPPED);
NRFX_LOG_DEBUG("TWIM: Event: %s.", EVT_TO_STR_TWIM(NRF_TWIM_EVENT_STOPPED));
transmission_finished = true;
}
if (nrf_twim_event_check(p_twim, NRF_TWIM_EVENT_ERROR))
{
nrf_twim_event_clear(p_twim, NRF_TWIM_EVENT_ERROR);
NRFX_LOG_DEBUG("TWIM: Event: %s.", EVT_TO_STR_TWIM(NRF_TWIM_EVENT_ERROR));
bool lasttx_triggered = nrf_twim_event_check(p_twim, NRF_TWIM_EVENT_LASTTX);
uint32_t shorts_mask = nrf_twim_shorts_get(p_twim);
if (!(lasttx_triggered && (shorts_mask & NRF_TWIM_SHORT_LASTTX_STOP_MASK)))
{
// Unless LASTTX event arrived and LASTTX_STOP shortcut is active,
// triggering of STOP task in case of error has to be done manually.
nrf_twim_task_trigger(p_twim, NRF_TWIM_TASK_RESUME);
nrf_twim_task_trigger(p_twim, NRF_TWIM_TASK_STOP);
// Mark transmission as not finished yet,
// as STOPPED event is expected to arrive.
// If LASTTX_SUSPENDED shortcut is active,
// NACK has been received on last byte sent
// and SUSPENDED event happened to be checked before ERROR,
// transmission will be marked as finished.
// In such case this flag has to be overwritten.
transmission_finished = false;
}
if (lasttx_triggered && (shorts_mask & NRF_TWIM_SHORT_LASTTX_SUSPEND_MASK))
{
// When STOP task was triggered just before SUSPEND task has taken effect,
// SUSPENDED event may not arrive.
// However if SUSPENDED arrives it always arrives after ERROR.
// Therefore SUSPENDED has to be cleared
// so it does not cause premature termination of busy loop
// waiting for STOPPED event to arrive.
nrf_twim_event_clear(p_twim, NRF_TWIM_EVENT_SUSPENDED);
// Mark transmission as not finished yet,
// for same reasons as above.
transmission_finished = false;
}
}
} while (!transmission_finished);
uint32_t errorsrc = nrf_twim_errorsrc_get_and_clear(p_twim);
p_cb->busy = false;
if (errorsrc)
{
err_code = twi_process_error(errorsrc);
}
else
{
if (!(flags & NRFX_TWIM_FLAG_NO_SPURIOUS_STOP_CHECK) &&
!xfer_completeness_check(p_twim, p_cb))
{
err_code = NRFX_ERROR_INTERNAL;
}
}
}
return err_code;
}
nrfx_err_t nrfx_twim_xfer(nrfx_twim_t const * p_instance,
nrfx_twim_xfer_desc_t const * p_xfer_desc,
uint32_t flags)
{
NRFX_ASSERT(TWIM_LENGTH_VALIDATE(p_instance->drv_inst_idx,
p_xfer_desc->primary_length,
p_xfer_desc->secondary_length));
nrfx_err_t err_code = NRFX_SUCCESS;
twim_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
// TXRX and TXTX transfers are supported only in non-blocking mode.
NRFX_ASSERT( !((p_cb->handler == NULL) && (p_xfer_desc->type == NRFX_TWIM_XFER_TXRX)));
NRFX_ASSERT( !((p_cb->handler == NULL) && (p_xfer_desc->type == NRFX_TWIM_XFER_TXTX)));
NRFX_LOG_INFO("Transfer type: %s.", TRANSFER_TO_STR(p_xfer_desc->type));
NRFX_LOG_INFO("Transfer buffers length: primary: %d, secondary: %d.",
p_xfer_desc->primary_length,
p_xfer_desc->secondary_length);
NRFX_LOG_DEBUG("Primary buffer data:");
NRFX_LOG_HEXDUMP_DEBUG(p_xfer_desc->p_primary_buf,
p_xfer_desc->primary_length * sizeof(p_xfer_desc->p_primary_buf[0]));
NRFX_LOG_DEBUG("Secondary buffer data:");
NRFX_LOG_HEXDUMP_DEBUG(p_xfer_desc->p_secondary_buf,
p_xfer_desc->secondary_length * sizeof(p_xfer_desc->p_secondary_buf[0]));
err_code = twim_xfer(p_cb, (NRF_TWIM_Type *)p_instance->p_twim, p_xfer_desc, flags);
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
nrfx_err_t nrfx_twim_tx(nrfx_twim_t const * p_instance,
uint8_t address,
uint8_t const * p_data,
size_t length,
bool no_stop)
{
nrfx_twim_xfer_desc_t xfer = NRFX_TWIM_XFER_DESC_TX(address, (uint8_t*)p_data, length);
return nrfx_twim_xfer(p_instance, &xfer, no_stop ? NRFX_TWIM_FLAG_TX_NO_STOP : 0);
}
nrfx_err_t nrfx_twim_rx(nrfx_twim_t const * p_instance,
uint8_t address,
uint8_t * p_data,
size_t length)
{
nrfx_twim_xfer_desc_t xfer = NRFX_TWIM_XFER_DESC_RX(address, p_data, length);
return nrfx_twim_xfer(p_instance, &xfer, 0);
}
uint32_t nrfx_twim_start_task_get(nrfx_twim_t const * p_instance,
nrfx_twim_xfer_type_t xfer_type)
{
return (uint32_t)nrf_twim_task_address_get(p_instance->p_twim,
(xfer_type != NRFX_TWIM_XFER_RX) ? NRF_TWIM_TASK_STARTTX : NRF_TWIM_TASK_STARTRX);
}
uint32_t nrfx_twim_stopped_event_get(nrfx_twim_t const * p_instance)
{
return (uint32_t)nrf_twim_event_address_get(p_instance->p_twim, NRF_TWIM_EVENT_STOPPED);
}
static void twim_irq_handler(NRF_TWIM_Type * p_twim, twim_control_block_t * p_cb)
{
#if NRFX_CHECK(NRFX_TWIM_NRF52_ANOMALY_109_WORKAROUND_ENABLED)
/* Handle only workaround case. Can be used without TWIM handler in IRQs. */
if (nrf_twim_event_check(p_twim, NRF_TWIM_EVENT_TXSTARTED))
{
nrf_twim_event_clear(p_twim, NRF_TWIM_EVENT_TXSTARTED);
nrf_twim_int_disable(p_twim, NRF_TWIM_INT_TXSTARTED_MASK);
if (p_twim->FREQUENCY == 0)
{
// Set enable to zero to reset TWIM internal state.
nrf_twim_disable(p_twim);
nrf_twim_enable(p_twim);
// Set proper frequency.
nrf_twim_frequency_set(p_twim, p_cb->bus_frequency);
twim_list_enable_handle(p_twim, p_cb->flags);
// Start proper transmission.
nrf_twim_task_trigger(p_twim, NRF_TWIM_TASK_STARTTX);
return;
}
}
#endif
NRFX_ASSERT(p_cb->handler);
if (nrf_twim_event_check(p_twim, NRF_TWIM_EVENT_ERROR))
{
nrf_twim_event_clear(p_twim, NRF_TWIM_EVENT_ERROR);
NRFX_LOG_DEBUG("TWIM: Event: %s.", EVT_TO_STR_TWIM(NRF_TWIM_EVENT_ERROR));
if (!nrf_twim_event_check(p_twim, NRF_TWIM_EVENT_STOPPED))
{
nrf_twim_int_disable(p_twim, p_cb->int_mask);
p_cb->int_mask = NRF_TWIM_INT_STOPPED_MASK;
nrf_twim_int_enable(p_twim, p_cb->int_mask);
if (!(nrf_twim_event_check(p_twim, NRF_TWIM_EVENT_LASTTX) &&
(nrf_twim_shorts_get(p_twim) & NRF_TWIM_SHORT_LASTTX_STOP_MASK)))
{
nrf_twim_task_trigger(p_twim, NRF_TWIM_TASK_RESUME);
nrf_twim_task_trigger(p_twim, NRF_TWIM_TASK_STOP);
}
p_cb->error = true;
return;
}
}
nrfx_twim_evt_t event;
if (nrf_twim_event_check(p_twim, NRF_TWIM_EVENT_STOPPED))
{
NRFX_LOG_DEBUG("TWIM: Event: %s.", EVT_TO_STR_TWIM(NRF_TWIM_EVENT_STOPPED));
nrf_twim_event_clear(p_twim, NRF_TWIM_EVENT_STOPPED);
if (!(p_cb->flags & NRFX_TWIM_FLAG_NO_SPURIOUS_STOP_CHECK) && !p_cb->error)
{
p_cb->error = !xfer_completeness_check(p_twim, p_cb);
}
// Further processing of STOPPED event is valid only if NO_XFER_EVT_HANDLER
// setting is not used.
if (!(p_cb->flags & NRFX_TWIM_FLAG_NO_XFER_EVT_HANDLER))
{
event.xfer_desc = p_cb->xfer_desc;
nrf_twim_event_clear(p_twim, NRF_TWIM_EVENT_LASTTX);
nrf_twim_event_clear(p_twim, NRF_TWIM_EVENT_LASTRX);
if (!p_cb->repeated || p_cb->error)
{
nrf_twim_shorts_set(p_twim, 0);
p_cb->int_mask = 0;
nrf_twim_int_disable(p_twim, NRF_TWIM_ALL_INTS_MASK);
// At this point interrupt handler should not be invoked again for current transfer.
// If STOPPED arrived during ERROR processing,
// its pending interrupt should be ignored.
// Otherwise spurious NRFX_TWIM_EVT_DONE or NRFX_TWIM_EVT_BUS_ERROR
// would be passed to user's handler.
NRFX_IRQ_PENDING_CLEAR(nrfx_get_irq_number(p_twim));
}
}
#if NRFX_CHECK(NRFX_TWIM_NRF52_ANOMALY_109_WORKAROUND_ENABLED)
else if (p_cb->xfer_desc.type != NRFX_TWIM_XFER_RX)
{
/* Add Anomaly 109 workaround for each potential repeated transfer starting from TX. */
twim_list_enable_handle(p_twim, 0);
p_twim->FREQUENCY = 0;
nrf_twim_int_enable(p_twim, NRF_TWIM_INT_TXSTARTED_MASK);
}
#endif
}
else
{
nrf_twim_event_clear(p_twim, NRF_TWIM_EVENT_SUSPENDED);
NRFX_LOG_DEBUG("TWIM: Event: %s.", EVT_TO_STR_TWIM(NRF_TWIM_EVENT_SUSPENDED));
if (p_cb->xfer_desc.type == NRFX_TWIM_XFER_TX)
{
event.xfer_desc = p_cb->xfer_desc;
if (!p_cb->repeated)
{
nrf_twim_shorts_set(p_twim, 0);
p_cb->int_mask = 0;
nrf_twim_int_disable(p_twim, NRF_TWIM_ALL_INTS_MASK);
// At this point interrupt handler should not be invoked again for current transfer.
// If STOPPED arrived during SUSPENDED processing,
// its pending interrupt should be ignored.
// Otherwise spurious NRFX_TWIM_EVT_DONE or NRFX_TWIM_EVT_BUS_ERROR
// would be passed to user's handler.
NRFX_IRQ_PENDING_CLEAR(nrfx_get_irq_number(p_twim));
}
}
else
{
nrf_twim_shorts_set(p_twim, NRF_TWIM_SHORT_LASTTX_STOP_MASK);
p_cb->int_mask = NRF_TWIM_INT_STOPPED_MASK | NRF_TWIM_INT_ERROR_MASK;
nrf_twim_int_disable(p_twim, NRF_TWIM_ALL_INTS_MASK);
nrf_twim_int_enable(p_twim, p_cb->int_mask);
nrf_twim_task_trigger(p_twim, NRF_TWIM_TASK_STARTTX);
nrf_twim_task_trigger(p_twim, NRF_TWIM_TASK_RESUME);
return;
}
}
uint32_t errorsrc = nrf_twim_errorsrc_get_and_clear(p_twim);
if (errorsrc & NRF_TWIM_ERROR_ADDRESS_NACK)
{
event.type = NRFX_TWIM_EVT_ADDRESS_NACK;
NRFX_LOG_DEBUG("Event: %s.", EVT_TO_STR(NRFX_TWIM_EVT_ADDRESS_NACK));
}
else if (errorsrc & NRF_TWIM_ERROR_DATA_NACK)
{
event.type = NRFX_TWIM_EVT_DATA_NACK;
NRFX_LOG_DEBUG("Event: %s.", EVT_TO_STR(NRFX_TWIM_EVT_DATA_NACK));
}
else if (errorsrc & NRF_TWIM_ERROR_OVERRUN)
{
event.type = NRFX_TWIM_EVT_OVERRUN;
NRFX_LOG_DEBUG("Event: %s.", EVT_TO_STR(NRFX_TWIM_EVT_OVERRUN));
}
else if (p_cb->error)
{
event.type = NRFX_TWIM_EVT_BUS_ERROR;
NRFX_LOG_DEBUG("Event: %s.", EVT_TO_STR(NRFX_TWIM_EVT_BUS_ERROR));
}
else
{
event.type = NRFX_TWIM_EVT_DONE;
NRFX_LOG_DEBUG("Event: %s.", EVT_TO_STR(NRFX_TWIM_EVT_DONE));
}
if (!p_cb->repeated)
{
p_cb->busy = false;
}
if (!(p_cb->flags & NRFX_TWIM_FLAG_NO_XFER_EVT_HANDLER) || p_cb->error)
{
p_cb->handler(&event, p_cb->p_context);
}
}
#if NRFX_CHECK(NRFX_TWIM0_ENABLED)
void nrfx_twim_0_irq_handler(void)
{
twim_irq_handler(NRF_TWIM0, &m_cb[NRFX_TWIM0_INST_IDX]);
}
#endif
#if NRFX_CHECK(NRFX_TWIM1_ENABLED)
void nrfx_twim_1_irq_handler(void)
{
twim_irq_handler(NRF_TWIM1, &m_cb[NRFX_TWIM1_INST_IDX]);
}
#endif
#if NRFX_CHECK(NRFX_TWIM2_ENABLED)
void nrfx_twim_2_irq_handler(void)
{
twim_irq_handler(NRF_TWIM2, &m_cb[NRFX_TWIM2_INST_IDX]);
}
#endif
#if NRFX_CHECK(NRFX_TWIM3_ENABLED)
void nrfx_twim_3_irq_handler(void)
{
twim_irq_handler(NRF_TWIM3, &m_cb[NRFX_TWIM3_INST_IDX]);
}
#endif
#endif // NRFX_CHECK(NRFX_TWIM_ENABLED)

View File

@@ -0,0 +1,857 @@
/**
* Copyright (c) 2015 - 2021, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <nrfx.h>
#if NRFX_CHECK(NRFX_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 - 2021, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <nrfx.h>
#if NRFX_CHECK(NRFX_UART_ENABLED)
#if !NRFX_CHECK(NRFX_UART0_ENABLED)
#error "No enabled UART instances. Check <nrfx_config.h>."
#endif
#include <nrfx_uart.h>
#include "prs/nrfx_prs.h"
#include <hal/nrf_gpio.h>
#define NRFX_LOG_MODULE UART
#include <nrfx_log.h>
#define EVT_TO_STR(event) \
(event == NRF_UART_EVENT_ERROR ? "NRF_UART_EVENT_ERROR" : \
"UNKNOWN EVENT")
#define TX_COUNTER_ABORT_REQ_VALUE UINT32_MAX
typedef struct
{
void * p_context;
nrfx_uart_event_handler_t handler;
uint8_t const * p_tx_buffer;
uint8_t * p_rx_buffer;
uint8_t * p_rx_secondary_buffer;
volatile size_t tx_buffer_length;
size_t rx_buffer_length;
size_t rx_secondary_buffer_length;
volatile size_t tx_counter;
volatile size_t rx_counter;
volatile bool tx_abort;
bool rx_enabled;
nrfx_drv_state_t state;
} uart_control_block_t;
static uart_control_block_t m_cb[NRFX_UART_ENABLED_COUNT];
static void apply_config(nrfx_uart_t const * p_instance,
nrfx_uart_config_t const * p_config)
{
if (p_config->pseltxd != NRF_UART_PSEL_DISCONNECTED)
{
nrf_gpio_pin_set(p_config->pseltxd);
nrf_gpio_cfg_output(p_config->pseltxd);
}
if (p_config->pselrxd != NRF_UART_PSEL_DISCONNECTED)
{
nrf_gpio_cfg_input(p_config->pselrxd, NRF_GPIO_PIN_NOPULL);
}
nrf_uart_baudrate_set(p_instance->p_reg, p_config->baudrate);
nrf_uart_configure(p_instance->p_reg, p_config->parity, p_config->hwfc);
nrf_uart_txrx_pins_set(p_instance->p_reg, p_config->pseltxd, p_config->pselrxd);
if (p_config->hwfc == NRF_UART_HWFC_ENABLED)
{
if (p_config->pselcts != NRF_UART_PSEL_DISCONNECTED)
{
nrf_gpio_cfg_input(p_config->pselcts, NRF_GPIO_PIN_NOPULL);
}
if (p_config->pselrts != NRF_UART_PSEL_DISCONNECTED)
{
nrf_gpio_pin_set(p_config->pselrts);
nrf_gpio_cfg_output(p_config->pselrts);
}
nrf_uart_hwfc_pins_set(p_instance->p_reg, p_config->pselrts, p_config->pselcts);
}
}
static void interrupts_enable(nrfx_uart_t const * p_instance,
uint8_t interrupt_priority)
{
nrf_uart_event_clear(p_instance->p_reg, NRF_UART_EVENT_TXDRDY);
nrf_uart_event_clear(p_instance->p_reg, NRF_UART_EVENT_RXTO);
nrf_uart_int_enable(p_instance->p_reg, NRF_UART_INT_MASK_TXDRDY |
NRF_UART_INT_MASK_RXTO);
NRFX_IRQ_PRIORITY_SET(nrfx_get_irq_number((void *)p_instance->p_reg),
interrupt_priority);
NRFX_IRQ_ENABLE(nrfx_get_irq_number((void *)p_instance->p_reg));
}
static void interrupts_disable(nrfx_uart_t const * p_instance)
{
nrf_uart_int_disable(p_instance->p_reg, NRF_UART_INT_MASK_RXDRDY |
NRF_UART_INT_MASK_TXDRDY |
NRF_UART_INT_MASK_ERROR |
NRF_UART_INT_MASK_RXTO);
NRFX_IRQ_DISABLE(nrfx_get_irq_number((void *)p_instance->p_reg));
}
static void pins_to_default(nrfx_uart_t const * p_instance)
{
/* Reset pins to default states */
uint32_t txd;
uint32_t rxd;
uint32_t rts;
uint32_t cts;
txd = nrf_uart_tx_pin_get(p_instance->p_reg);
rxd = nrf_uart_rx_pin_get(p_instance->p_reg);
rts = nrf_uart_rts_pin_get(p_instance->p_reg);
cts = nrf_uart_cts_pin_get(p_instance->p_reg);
nrf_uart_txrx_pins_disconnect(p_instance->p_reg);
nrf_uart_hwfc_pins_disconnect(p_instance->p_reg);
if (txd != NRF_UART_PSEL_DISCONNECTED)
{
nrf_gpio_cfg_default(txd);
}
if (rxd != NRF_UART_PSEL_DISCONNECTED)
{
nrf_gpio_cfg_default(rxd);
}
if (cts != NRF_UART_PSEL_DISCONNECTED)
{
nrf_gpio_cfg_default(cts);
}
if (rts != NRF_UART_PSEL_DISCONNECTED)
{
nrf_gpio_cfg_default(rts);
}
}
nrfx_err_t nrfx_uart_init(nrfx_uart_t const * p_instance,
nrfx_uart_config_t const * p_config,
nrfx_uart_event_handler_t event_handler)
{
NRFX_ASSERT(p_config);
uart_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
nrfx_err_t err_code = NRFX_SUCCESS;
if (p_cb->state != NRFX_DRV_STATE_UNINITIALIZED)
{
err_code = NRFX_ERROR_INVALID_STATE;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
#if NRFX_CHECK(NRFX_PRS_ENABLED)
static nrfx_irq_handler_t const irq_handlers[NRFX_UART_ENABLED_COUNT] = {
#if NRFX_CHECK(NRFX_UART0_ENABLED)
nrfx_uart_0_irq_handler,
#endif
};
if (nrfx_prs_acquire(p_instance->p_reg,
irq_handlers[p_instance->drv_inst_idx]) != NRFX_SUCCESS)
{
err_code = NRFX_ERROR_BUSY;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
#endif // NRFX_CHECK(NRFX_PRS_ENABLED)
apply_config(p_instance, p_config);
p_cb->handler = event_handler;
p_cb->p_context = p_config->p_context;
if (p_cb->handler)
{
interrupts_enable(p_instance, p_config->interrupt_priority);
}
nrf_uart_enable(p_instance->p_reg);
p_cb->rx_buffer_length = 0;
p_cb->rx_secondary_buffer_length = 0;
p_cb->rx_enabled = false;
p_cb->tx_buffer_length = 0;
p_cb->state = NRFX_DRV_STATE_INITIALIZED;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
void nrfx_uart_uninit(nrfx_uart_t const * p_instance)
{
uart_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
nrf_uart_disable(p_instance->p_reg);
if (p_cb->handler)
{
interrupts_disable(p_instance);
}
pins_to_default(p_instance);
#if NRFX_CHECK(NRFX_PRS_ENABLED)
nrfx_prs_release(p_instance->p_reg);
#endif
p_cb->state = NRFX_DRV_STATE_UNINITIALIZED;
p_cb->handler = NULL;
NRFX_LOG_INFO("Instance uninitialized: %d.", p_instance->drv_inst_idx);
}
static void tx_byte(NRF_UART_Type * p_uart, uart_control_block_t * p_cb)
{
nrf_uart_event_clear(p_uart, NRF_UART_EVENT_TXDRDY);
uint8_t txd = p_cb->p_tx_buffer[p_cb->tx_counter];
p_cb->tx_counter++;
nrf_uart_txd_set(p_uart, txd);
}
static bool tx_blocking(NRF_UART_Type * p_uart, uart_control_block_t * p_cb)
{
// Use a local variable to avoid undefined order of accessing two volatile variables
// in one statement.
size_t const tx_buffer_length = p_cb->tx_buffer_length;
while (p_cb->tx_counter < tx_buffer_length)
{
// Wait until the transmitter is ready to accept a new byte.
// Exit immediately if the transfer has been aborted.
while (!nrf_uart_event_check(p_uart, NRF_UART_EVENT_TXDRDY))
{
if (p_cb->tx_abort)
{
return false;
}
}
tx_byte(p_uart, p_cb);
}
return true;
}
nrfx_err_t nrfx_uart_tx(nrfx_uart_t const * p_instance,
uint8_t const * p_data,
size_t length)
{
uart_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
NRFX_ASSERT(p_cb->state == NRFX_DRV_STATE_INITIALIZED);
NRFX_ASSERT(p_data);
NRFX_ASSERT(length > 0);
nrfx_err_t err_code;
if (nrfx_uart_tx_in_progress(p_instance))
{
err_code = NRFX_ERROR_BUSY;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
p_cb->tx_buffer_length = length;
p_cb->p_tx_buffer = p_data;
p_cb->tx_counter = 0;
p_cb->tx_abort = false;
NRFX_LOG_INFO("Transfer tx_len: %d.", p_cb->tx_buffer_length);
NRFX_LOG_DEBUG("Tx data:");
NRFX_LOG_HEXDUMP_DEBUG(p_cb->p_tx_buffer,
p_cb->tx_buffer_length * sizeof(p_cb->p_tx_buffer[0]));
err_code = NRFX_SUCCESS;
nrf_uart_event_clear(p_instance->p_reg, NRF_UART_EVENT_TXDRDY);
nrf_uart_task_trigger(p_instance->p_reg, NRF_UART_TASK_STARTTX);
tx_byte(p_instance->p_reg, p_cb);
if (p_cb->handler == NULL)
{
if (!tx_blocking(p_instance->p_reg, p_cb))
{
// The transfer has been aborted.
err_code = NRFX_ERROR_FORBIDDEN;
}
else
{
// Wait until the last byte is completely transmitted.
while (!nrf_uart_event_check(p_instance->p_reg, NRF_UART_EVENT_TXDRDY))
{}
nrf_uart_task_trigger(p_instance->p_reg, NRF_UART_TASK_STOPTX);
}
p_cb->tx_buffer_length = 0;
}
NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
bool nrfx_uart_tx_in_progress(nrfx_uart_t const * p_instance)
{
return (m_cb[p_instance->drv_inst_idx].tx_buffer_length != 0);
}
static void rx_enable(nrfx_uart_t const * p_instance)
{
nrf_uart_event_clear(p_instance->p_reg, NRF_UART_EVENT_ERROR);
nrf_uart_event_clear(p_instance->p_reg, NRF_UART_EVENT_RXDRDY);
nrf_uart_task_trigger(p_instance->p_reg, NRF_UART_TASK_STARTRX);
}
static void rx_byte(NRF_UART_Type * p_uart, uart_control_block_t * p_cb)
{
if (!p_cb->rx_buffer_length)
{
nrf_uart_event_clear(p_uart, NRF_UART_EVENT_RXDRDY);
// Byte received when buffer is not set - data lost.
(void) nrf_uart_rxd_get(p_uart);
return;
}
nrf_uart_event_clear(p_uart, NRF_UART_EVENT_RXDRDY);
p_cb->p_rx_buffer[p_cb->rx_counter] = nrf_uart_rxd_get(p_uart);
p_cb->rx_counter++;
}
nrfx_err_t nrfx_uart_rx(nrfx_uart_t const * p_instance,
uint8_t * p_data,
size_t length)
{
uart_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
NRFX_ASSERT(m_cb[p_instance->drv_inst_idx].state == NRFX_DRV_STATE_INITIALIZED);
NRFX_ASSERT(p_data);
NRFX_ASSERT(length > 0);
nrfx_err_t err_code;
bool second_buffer = false;
if (p_cb->handler)
{
nrf_uart_int_disable(p_instance->p_reg, NRF_UART_INT_MASK_RXDRDY |
NRF_UART_INT_MASK_ERROR);
}
if (p_cb->rx_buffer_length != 0)
{
if (p_cb->rx_secondary_buffer_length != 0)
{
if (p_cb->handler)
{
nrf_uart_int_enable(p_instance->p_reg, NRF_UART_INT_MASK_RXDRDY |
NRF_UART_INT_MASK_ERROR);
}
err_code = NRFX_ERROR_BUSY;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
second_buffer = true;
}
if (!second_buffer)
{
p_cb->rx_buffer_length = length;
p_cb->p_rx_buffer = p_data;
p_cb->rx_counter = 0;
p_cb->rx_secondary_buffer_length = 0;
}
else
{
p_cb->p_rx_secondary_buffer = p_data;
p_cb->rx_secondary_buffer_length = length;
}
NRFX_LOG_INFO("Transfer rx_len: %d.", length);
if ((!p_cb->rx_enabled) && (!second_buffer))
{
rx_enable(p_instance);
}
if (p_cb->handler == NULL)
{
nrf_uart_event_clear(p_instance->p_reg, NRF_UART_EVENT_RXTO);
bool rxrdy;
bool rxto;
bool error;
do
{
do
{
error = nrf_uart_event_check(p_instance->p_reg, NRF_UART_EVENT_ERROR);
rxrdy = nrf_uart_event_check(p_instance->p_reg, NRF_UART_EVENT_RXDRDY);
rxto = nrf_uart_event_check(p_instance->p_reg, NRF_UART_EVENT_RXTO);
} while ((!rxrdy) && (!rxto) && (!error));
if (error || rxto)
{
break;
}
rx_byte(p_instance->p_reg, p_cb);
} while (p_cb->rx_buffer_length > p_cb->rx_counter);
p_cb->rx_buffer_length = 0;
if (error)
{
err_code = NRFX_ERROR_INTERNAL;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
if (rxto)
{
err_code = NRFX_ERROR_FORBIDDEN;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
if (p_cb->rx_enabled)
{
nrf_uart_task_trigger(p_instance->p_reg, NRF_UART_TASK_STARTRX);
}
else
{
// Skip stopping RX if driver is forced to be enabled.
nrf_uart_task_trigger(p_instance->p_reg, NRF_UART_TASK_STOPRX);
}
}
else
{
nrf_uart_int_enable(p_instance->p_reg, NRF_UART_INT_MASK_RXDRDY |
NRF_UART_INT_MASK_ERROR);
}
err_code = NRFX_SUCCESS;
NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
bool nrfx_uart_rx_ready(nrfx_uart_t const * p_instance)
{
return nrf_uart_event_check(p_instance->p_reg, NRF_UART_EVENT_RXDRDY);
}
void nrfx_uart_rx_enable(nrfx_uart_t const * p_instance)
{
if (!m_cb[p_instance->drv_inst_idx].rx_enabled)
{
rx_enable(p_instance);
m_cb[p_instance->drv_inst_idx].rx_enabled = true;
}
}
void nrfx_uart_rx_disable(nrfx_uart_t const * p_instance)
{
nrf_uart_task_trigger(p_instance->p_reg, NRF_UART_TASK_STOPRX);
m_cb[p_instance->drv_inst_idx].rx_enabled = false;
}
uint32_t nrfx_uart_errorsrc_get(nrfx_uart_t const * p_instance)
{
nrf_uart_event_clear(p_instance->p_reg, NRF_UART_EVENT_ERROR);
return nrf_uart_errorsrc_get_and_clear(p_instance->p_reg);
}
static void rx_done_event(uart_control_block_t * p_cb,
size_t bytes,
uint8_t * p_data)
{
nrfx_uart_event_t event;
event.type = NRFX_UART_EVT_RX_DONE;
event.data.rxtx.bytes = bytes;
event.data.rxtx.p_data = p_data;
p_cb->handler(&event, p_cb->p_context);
}
static void tx_done_event(uart_control_block_t * p_cb,
size_t bytes)
{
nrfx_uart_event_t event;
event.type = NRFX_UART_EVT_TX_DONE;
event.data.rxtx.bytes = bytes;
event.data.rxtx.p_data = (uint8_t *)p_cb->p_tx_buffer;
p_cb->tx_buffer_length = 0;
p_cb->handler(&event, p_cb->p_context);
}
void nrfx_uart_tx_abort(nrfx_uart_t const * p_instance)
{
uart_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
p_cb->tx_abort = true;
nrf_uart_task_trigger(p_instance->p_reg, NRF_UART_TASK_STOPTX);
if (p_cb->handler)
{
tx_done_event(p_cb, p_cb->tx_counter);
}
NRFX_LOG_INFO("TX transaction aborted.");
}
void nrfx_uart_rx_abort(nrfx_uart_t const * p_instance)
{
nrf_uart_int_disable(p_instance->p_reg, NRF_UART_INT_MASK_RXDRDY |
NRF_UART_INT_MASK_ERROR);
nrf_uart_task_trigger(p_instance->p_reg, NRF_UART_TASK_STOPRX);
NRFX_LOG_INFO("RX transaction aborted.");
}
static void uart_irq_handler(NRF_UART_Type * p_uart,
uart_control_block_t * p_cb)
{
if (nrf_uart_int_enable_check(p_uart, NRF_UART_INT_MASK_ERROR) &&
nrf_uart_event_check(p_uart, NRF_UART_EVENT_ERROR))
{
nrfx_uart_event_t event;
nrf_uart_event_clear(p_uart, NRF_UART_EVENT_ERROR);
NRFX_LOG_DEBUG("Event: %s.", EVT_TO_STR(NRF_UART_EVENT_ERROR));
nrf_uart_int_disable(p_uart, NRF_UART_INT_MASK_RXDRDY |
NRF_UART_INT_MASK_ERROR);
if (!p_cb->rx_enabled)
{
nrf_uart_task_trigger(p_uart, NRF_UART_TASK_STOPRX);
}
event.type = NRFX_UART_EVT_ERROR;
event.data.error.error_mask = nrf_uart_errorsrc_get_and_clear(p_uart);
event.data.error.rxtx.bytes = p_cb->rx_buffer_length;
event.data.error.rxtx.p_data = p_cb->p_rx_buffer;
// Abort transfer.
p_cb->rx_buffer_length = 0;
p_cb->rx_secondary_buffer_length = 0;
p_cb->handler(&event,p_cb->p_context);
}
else if (nrf_uart_int_enable_check(p_uart, NRF_UART_INT_MASK_RXDRDY) &&
nrf_uart_event_check(p_uart, NRF_UART_EVENT_RXDRDY))
{
rx_byte(p_uart, p_cb);
if (p_cb->rx_buffer_length == p_cb->rx_counter)
{
if (p_cb->rx_secondary_buffer_length)
{
uint8_t * p_data = p_cb->p_rx_buffer;
size_t rx_counter = p_cb->rx_counter;
// Switch to secondary buffer.
p_cb->rx_buffer_length = p_cb->rx_secondary_buffer_length;
p_cb->p_rx_buffer = p_cb->p_rx_secondary_buffer;
p_cb->rx_secondary_buffer_length = 0;
p_cb->rx_counter = 0;
rx_done_event(p_cb, rx_counter, p_data);
}
else
{
if (!p_cb->rx_enabled)
{
nrf_uart_task_trigger(p_uart, NRF_UART_TASK_STOPRX);
}
nrf_uart_int_disable(p_uart, NRF_UART_INT_MASK_RXDRDY |
NRF_UART_INT_MASK_ERROR);
p_cb->rx_buffer_length = 0;
rx_done_event(p_cb, p_cb->rx_counter, p_cb->p_rx_buffer);
}
}
}
if (nrf_uart_event_check(p_uart, NRF_UART_EVENT_TXDRDY))
{
// Use a local variable to avoid undefined order of accessing two volatile variables
// in one statement.
size_t const tx_buffer_length = p_cb->tx_buffer_length;
if (p_cb->tx_counter < tx_buffer_length && !p_cb->tx_abort)
{
tx_byte(p_uart, p_cb);
}
else
{
nrf_uart_event_clear(p_uart, NRF_UART_EVENT_TXDRDY);
if (p_cb->tx_buffer_length)
{
tx_done_event(p_cb, p_cb->tx_buffer_length);
}
}
}
if (nrf_uart_event_check(p_uart, NRF_UART_EVENT_RXTO))
{
nrf_uart_event_clear(p_uart, NRF_UART_EVENT_RXTO);
// RXTO event may be triggered as a result of abort call. In th
if (p_cb->rx_enabled)
{
nrf_uart_task_trigger(p_uart, NRF_UART_TASK_STARTRX);
}
if (p_cb->rx_buffer_length)
{
p_cb->rx_buffer_length = 0;
rx_done_event(p_cb, p_cb->rx_counter, p_cb->p_rx_buffer);
}
}
}
#if NRFX_CHECK(NRFX_UART0_ENABLED)
void nrfx_uart_0_irq_handler(void)
{
uart_irq_handler(NRF_UART0, &m_cb[NRFX_UART0_INST_IDX]);
}
#endif
#endif // NRFX_CHECK(NRFX_UART_ENABLED)

View File

@@ -0,0 +1,702 @@
/**
* Copyright (c) 2015 - 2021, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <nrfx.h>
#if NRFX_CHECK(NRFX_UARTE_ENABLED)
#if !(NRFX_CHECK(NRFX_UARTE0_ENABLED) || \
NRFX_CHECK(NRFX_UARTE1_ENABLED) || \
NRFX_CHECK(NRFX_UARTE2_ENABLED) || \
NRFX_CHECK(NRFX_UARTE3_ENABLED))
#error "No enabled UARTE instances. Check <nrfx_config.h>."
#endif
#include <nrfx_uarte.h>
#include "prs/nrfx_prs.h"
#include <hal/nrf_gpio.h>
#define NRFX_LOG_MODULE UARTE
#include <nrfx_log.h>
#define EVT_TO_STR(event) \
(event == NRF_UARTE_EVENT_ERROR ? "NRF_UARTE_EVENT_ERROR" : \
"UNKNOWN EVENT")
#define UARTEX_LENGTH_VALIDATE(peripheral, drv_inst_idx, len1, len2) \
(((drv_inst_idx) == NRFX_CONCAT_3(NRFX_, peripheral, _INST_IDX)) && \
NRFX_EASYDMA_LENGTH_VALIDATE(peripheral, len1, len2))
#if NRFX_CHECK(NRFX_UARTE0_ENABLED)
#define UARTE0_LENGTH_VALIDATE(...) UARTEX_LENGTH_VALIDATE(UARTE0, __VA_ARGS__)
#else
#define UARTE0_LENGTH_VALIDATE(...) 0
#endif
#if NRFX_CHECK(NRFX_UARTE1_ENABLED)
#define UARTE1_LENGTH_VALIDATE(...) UARTEX_LENGTH_VALIDATE(UARTE1, __VA_ARGS__)
#else
#define UARTE1_LENGTH_VALIDATE(...) 0
#endif
#if NRFX_CHECK(NRFX_UARTE2_ENABLED)
#define UARTE2_LENGTH_VALIDATE(...) UARTEX_LENGTH_VALIDATE(UARTE2, __VA_ARGS__)
#else
#define UARTE2_LENGTH_VALIDATE(...) 0
#endif
#if NRFX_CHECK(NRFX_UARTE3_ENABLED)
#define UARTE3_LENGTH_VALIDATE(...) UARTEX_LENGTH_VALIDATE(UARTE3, __VA_ARGS__)
#else
#define UARTE3_LENGTH_VALIDATE(...) 0
#endif
#define UARTE_LENGTH_VALIDATE(drv_inst_idx, length) \
(UARTE0_LENGTH_VALIDATE(drv_inst_idx, length, 0) || \
UARTE1_LENGTH_VALIDATE(drv_inst_idx, length, 0) || \
UARTE2_LENGTH_VALIDATE(drv_inst_idx, length, 0) || \
UARTE3_LENGTH_VALIDATE(drv_inst_idx, length, 0))
typedef struct
{
void * p_context;
nrfx_uarte_event_handler_t handler;
uint8_t const * p_tx_buffer;
uint8_t * p_rx_buffer;
uint8_t * p_rx_secondary_buffer;
volatile size_t tx_buffer_length;
size_t rx_buffer_length;
size_t rx_secondary_buffer_length;
nrfx_drv_state_t state;
} uarte_control_block_t;
static uarte_control_block_t m_cb[NRFX_UARTE_ENABLED_COUNT];
static void apply_config(nrfx_uarte_t const * p_instance,
nrfx_uarte_config_t const * p_config)
{
if (p_config->pseltxd != NRF_UARTE_PSEL_DISCONNECTED)
{
nrf_gpio_pin_set(p_config->pseltxd);
nrf_gpio_cfg_output(p_config->pseltxd);
}
if (p_config->pselrxd != NRF_UARTE_PSEL_DISCONNECTED)
{
nrf_gpio_cfg_input(p_config->pselrxd, NRF_GPIO_PIN_NOPULL);
}
nrf_uarte_baudrate_set(p_instance->p_reg, p_config->baudrate);
nrf_uarte_configure(p_instance->p_reg, p_config->parity, p_config->hwfc);
nrf_uarte_txrx_pins_set(p_instance->p_reg, p_config->pseltxd, p_config->pselrxd);
if (p_config->hwfc == NRF_UARTE_HWFC_ENABLED)
{
if (p_config->pselcts != NRF_UARTE_PSEL_DISCONNECTED)
{
nrf_gpio_cfg_input(p_config->pselcts, NRF_GPIO_PIN_NOPULL);
}
if (p_config->pselrts != NRF_UARTE_PSEL_DISCONNECTED)
{
nrf_gpio_pin_set(p_config->pselrts);
nrf_gpio_cfg_output(p_config->pselrts);
}
nrf_uarte_hwfc_pins_set(p_instance->p_reg, p_config->pselrts, p_config->pselcts);
}
}
static void interrupts_enable(nrfx_uarte_t const * p_instance,
uint8_t interrupt_priority)
{
nrf_uarte_event_clear(p_instance->p_reg, NRF_UARTE_EVENT_ENDRX);
nrf_uarte_event_clear(p_instance->p_reg, NRF_UARTE_EVENT_ENDTX);
nrf_uarte_event_clear(p_instance->p_reg, NRF_UARTE_EVENT_ERROR);
nrf_uarte_event_clear(p_instance->p_reg, NRF_UARTE_EVENT_RXTO);
nrf_uarte_event_clear(p_instance->p_reg, NRF_UARTE_EVENT_TXSTOPPED);
nrf_uarte_int_enable(p_instance->p_reg, NRF_UARTE_INT_ENDRX_MASK |
NRF_UARTE_INT_ENDTX_MASK |
NRF_UARTE_INT_ERROR_MASK |
NRF_UARTE_INT_RXTO_MASK |
NRF_UARTE_INT_TXSTOPPED_MASK);
NRFX_IRQ_PRIORITY_SET(nrfx_get_irq_number((void *)p_instance->p_reg),
interrupt_priority);
NRFX_IRQ_ENABLE(nrfx_get_irq_number((void *)p_instance->p_reg));
}
static void interrupts_disable(nrfx_uarte_t const * p_instance)
{
nrf_uarte_int_disable(p_instance->p_reg, NRF_UARTE_INT_ENDRX_MASK |
NRF_UARTE_INT_ENDTX_MASK |
NRF_UARTE_INT_ERROR_MASK |
NRF_UARTE_INT_RXTO_MASK |
NRF_UARTE_INT_TXSTOPPED_MASK);
NRFX_IRQ_DISABLE(nrfx_get_irq_number((void *)p_instance->p_reg));
}
static void pins_to_default(nrfx_uarte_t const * p_instance)
{
/* Reset pins to default states */
uint32_t txd;
uint32_t rxd;
uint32_t rts;
uint32_t cts;
txd = nrf_uarte_tx_pin_get(p_instance->p_reg);
rxd = nrf_uarte_rx_pin_get(p_instance->p_reg);
rts = nrf_uarte_rts_pin_get(p_instance->p_reg);
cts = nrf_uarte_cts_pin_get(p_instance->p_reg);
nrf_uarte_txrx_pins_disconnect(p_instance->p_reg);
nrf_uarte_hwfc_pins_disconnect(p_instance->p_reg);
if (txd != NRF_UARTE_PSEL_DISCONNECTED)
{
nrf_gpio_cfg_default(txd);
}
if (rxd != NRF_UARTE_PSEL_DISCONNECTED)
{
nrf_gpio_cfg_default(rxd);
}
if (cts != NRF_UARTE_PSEL_DISCONNECTED)
{
nrf_gpio_cfg_default(cts);
}
if (rts != NRF_UARTE_PSEL_DISCONNECTED)
{
nrf_gpio_cfg_default(rts);
}
}
nrfx_err_t nrfx_uarte_init(nrfx_uarte_t const * p_instance,
nrfx_uarte_config_t const * p_config,
nrfx_uarte_event_handler_t event_handler)
{
NRFX_ASSERT(p_config);
uarte_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
nrfx_err_t err_code = NRFX_SUCCESS;
if (p_cb->state != NRFX_DRV_STATE_UNINITIALIZED)
{
err_code = NRFX_ERROR_INVALID_STATE;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
#if NRFX_CHECK(NRFX_PRS_ENABLED)
static nrfx_irq_handler_t const irq_handlers[NRFX_UARTE_ENABLED_COUNT] = {
#if NRFX_CHECK(NRFX_UARTE0_ENABLED)
nrfx_uarte_0_irq_handler,
#endif
#if NRFX_CHECK(NRFX_UARTE1_ENABLED)
nrfx_uarte_1_irq_handler,
#endif
#if NRFX_CHECK(NRFX_UARTE2_ENABLED)
nrfx_uarte_2_irq_handler,
#endif
#if NRFX_CHECK(NRFX_UARTE3_ENABLED)
nrfx_uarte_3_irq_handler,
#endif
};
if (nrfx_prs_acquire(p_instance->p_reg,
irq_handlers[p_instance->drv_inst_idx]) != NRFX_SUCCESS)
{
err_code = NRFX_ERROR_BUSY;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
#endif // NRFX_CHECK(NRFX_PRS_ENABLED)
apply_config(p_instance, p_config);
#if defined(NRF5340_XXAA_APPLICATION) || defined(NRF5340_XXAA_NETWORK) || defined(NRF9160_XXAA)
// Apply workaround for anomalies:
// - nRF9160 - anomaly 23
// - nRF5340 - anomaly 44
volatile uint32_t const * rxenable_reg =
(volatile uint32_t *)(((uint32_t)p_instance->p_reg) + 0x564);
volatile uint32_t const * txenable_reg =
(volatile uint32_t *)(((uint32_t)p_instance->p_reg) + 0x568);
if (*txenable_reg == 1)
{
nrf_uarte_task_trigger(p_instance->p_reg, NRF_UARTE_TASK_STOPTX);
}
if (*rxenable_reg == 1)
{
nrf_uarte_enable(p_instance->p_reg);
nrf_uarte_task_trigger(p_instance->p_reg, NRF_UARTE_TASK_STOPRX);
while (*rxenable_reg)
{}
(void)nrf_uarte_errorsrc_get_and_clear(p_instance->p_reg);
nrf_uarte_disable(p_instance->p_reg);
}
#endif // defined(NRF5340_XXAA_APPLICATION) || defined(NRF5340_XXAA_NETWORK) || defined(NRF9160_XXAA)
p_cb->handler = event_handler;
p_cb->p_context = p_config->p_context;
if (p_cb->handler)
{
interrupts_enable(p_instance, p_config->interrupt_priority);
}
nrf_uarte_enable(p_instance->p_reg);
p_cb->rx_buffer_length = 0;
p_cb->rx_secondary_buffer_length = 0;
p_cb->tx_buffer_length = 0;
p_cb->state = NRFX_DRV_STATE_INITIALIZED;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
void nrfx_uarte_uninit(nrfx_uarte_t const * p_instance)
{
uarte_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
NRF_UARTE_Type * p_reg = p_instance->p_reg;
if (p_cb->handler)
{
interrupts_disable(p_instance);
}
// Make sure all transfers are finished before UARTE is disabled
// to achieve the lowest power consumption.
nrf_uarte_shorts_disable(p_reg, NRF_UARTE_SHORT_ENDRX_STARTRX);
// Check if there is any ongoing reception.
if (p_cb->rx_buffer_length)
{
nrf_uarte_event_clear(p_reg, NRF_UARTE_EVENT_RXTO);
nrf_uarte_task_trigger(p_reg, NRF_UARTE_TASK_STOPRX);
}
nrf_uarte_event_clear(p_reg, NRF_UARTE_EVENT_TXSTOPPED);
nrf_uarte_task_trigger(p_reg, NRF_UARTE_TASK_STOPTX);
// Wait for TXSTOPPED event and for RXTO event, provided that there was ongoing reception.
while (!nrf_uarte_event_check(p_reg, NRF_UARTE_EVENT_TXSTOPPED) ||
(p_cb->rx_buffer_length && !nrf_uarte_event_check(p_reg, NRF_UARTE_EVENT_RXTO)))
{}
nrf_uarte_disable(p_reg);
pins_to_default(p_instance);
#if NRFX_CHECK(NRFX_PRS_ENABLED)
nrfx_prs_release(p_reg);
#endif
p_cb->state = NRFX_DRV_STATE_UNINITIALIZED;
p_cb->handler = NULL;
NRFX_LOG_INFO("Instance uninitialized: %d.", p_instance->drv_inst_idx);
}
nrfx_err_t nrfx_uarte_tx(nrfx_uarte_t const * p_instance,
uint8_t const * p_data,
size_t length)
{
uarte_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
NRFX_ASSERT(p_cb->state == NRFX_DRV_STATE_INITIALIZED);
NRFX_ASSERT(p_data);
NRFX_ASSERT(length > 0);
NRFX_ASSERT(UARTE_LENGTH_VALIDATE(p_instance->drv_inst_idx, length));
nrfx_err_t err_code;
// EasyDMA requires that transfer buffers are placed in DataRAM,
// signal error if the are not.
if (!nrfx_is_in_ram(p_data))
{
err_code = NRFX_ERROR_INVALID_ADDR;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
if (nrfx_uarte_tx_in_progress(p_instance))
{
err_code = NRFX_ERROR_BUSY;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
p_cb->tx_buffer_length = length;
p_cb->p_tx_buffer = p_data;
NRFX_LOG_INFO("Transfer tx_len: %d.", p_cb->tx_buffer_length);
NRFX_LOG_DEBUG("Tx data:");
NRFX_LOG_HEXDUMP_DEBUG(p_cb->p_tx_buffer,
p_cb->tx_buffer_length * sizeof(p_cb->p_tx_buffer[0]));
err_code = NRFX_SUCCESS;
nrf_uarte_event_clear(p_instance->p_reg, NRF_UARTE_EVENT_ENDTX);
nrf_uarte_event_clear(p_instance->p_reg, NRF_UARTE_EVENT_TXSTOPPED);
nrf_uarte_tx_buffer_set(p_instance->p_reg, p_cb->p_tx_buffer, p_cb->tx_buffer_length);
nrf_uarte_task_trigger(p_instance->p_reg, NRF_UARTE_TASK_STARTTX);
if (p_cb->handler == NULL)
{
bool endtx;
bool txstopped;
do
{
endtx = nrf_uarte_event_check(p_instance->p_reg, NRF_UARTE_EVENT_ENDTX);
txstopped = nrf_uarte_event_check(p_instance->p_reg, NRF_UARTE_EVENT_TXSTOPPED);
}
while ((!endtx) && (!txstopped));
if (txstopped)
{
err_code = NRFX_ERROR_FORBIDDEN;
}
else
{
// Transmitter has to be stopped by triggering the STOPTX task to achieve
// the lowest possible level of the UARTE power consumption.
nrf_uarte_task_trigger(p_instance->p_reg, NRF_UARTE_TASK_STOPTX);
while (!nrf_uarte_event_check(p_instance->p_reg, NRF_UARTE_EVENT_TXSTOPPED))
{}
}
p_cb->tx_buffer_length = 0;
}
NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
bool nrfx_uarte_tx_in_progress(nrfx_uarte_t const * p_instance)
{
return (m_cb[p_instance->drv_inst_idx].tx_buffer_length != 0);
}
nrfx_err_t nrfx_uarte_rx(nrfx_uarte_t const * p_instance,
uint8_t * p_data,
size_t length)
{
uarte_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
NRFX_ASSERT(m_cb[p_instance->drv_inst_idx].state == NRFX_DRV_STATE_INITIALIZED);
NRFX_ASSERT(p_data);
NRFX_ASSERT(length > 0);
NRFX_ASSERT(UARTE_LENGTH_VALIDATE(p_instance->drv_inst_idx, length));
nrfx_err_t err_code;
// EasyDMA requires that transfer buffers are placed in DataRAM,
// signal error if the are not.
if (!nrfx_is_in_ram(p_data))
{
err_code = NRFX_ERROR_INVALID_ADDR;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
bool second_buffer = false;
if (p_cb->handler)
{
nrf_uarte_int_disable(p_instance->p_reg, NRF_UARTE_INT_ERROR_MASK |
NRF_UARTE_INT_ENDRX_MASK);
}
if (p_cb->rx_buffer_length != 0)
{
if (p_cb->rx_secondary_buffer_length != 0)
{
if (p_cb->handler)
{
nrf_uarte_int_enable(p_instance->p_reg, NRF_UARTE_INT_ERROR_MASK |
NRF_UARTE_INT_ENDRX_MASK);
}
err_code = NRFX_ERROR_BUSY;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
second_buffer = true;
}
if (!second_buffer)
{
p_cb->rx_buffer_length = length;
p_cb->p_rx_buffer = p_data;
p_cb->rx_secondary_buffer_length = 0;
}
else
{
p_cb->p_rx_secondary_buffer = p_data;
p_cb->rx_secondary_buffer_length = length;
}
NRFX_LOG_INFO("Transfer rx_len: %d.", length);
err_code = NRFX_SUCCESS;
nrf_uarte_event_clear(p_instance->p_reg, NRF_UARTE_EVENT_ENDRX);
nrf_uarte_event_clear(p_instance->p_reg, NRF_UARTE_EVENT_RXTO);
nrf_uarte_rx_buffer_set(p_instance->p_reg, p_data, length);
if (!second_buffer)
{
nrf_uarte_task_trigger(p_instance->p_reg, NRF_UARTE_TASK_STARTRX);
}
else
{
nrf_uarte_shorts_enable(p_instance->p_reg, NRF_UARTE_SHORT_ENDRX_STARTRX);
}
if (m_cb[p_instance->drv_inst_idx].handler == NULL)
{
bool endrx;
bool rxto;
bool error;
do {
endrx = nrf_uarte_event_check(p_instance->p_reg, NRF_UARTE_EVENT_ENDRX);
rxto = nrf_uarte_event_check(p_instance->p_reg, NRF_UARTE_EVENT_RXTO);
error = nrf_uarte_event_check(p_instance->p_reg, NRF_UARTE_EVENT_ERROR);
} while ((!endrx) && (!rxto) && (!error));
m_cb[p_instance->drv_inst_idx].rx_buffer_length = 0;
if (error)
{
err_code = NRFX_ERROR_INTERNAL;
}
if (rxto)
{
err_code = NRFX_ERROR_FORBIDDEN;
}
}
else
{
nrf_uarte_int_enable(p_instance->p_reg, NRF_UARTE_INT_ERROR_MASK |
NRF_UARTE_INT_ENDRX_MASK);
}
NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
bool nrfx_uarte_rx_ready(nrfx_uarte_t const * p_instance)
{
return nrf_uarte_event_check(p_instance->p_reg, NRF_UARTE_EVENT_ENDRX);
}
uint32_t nrfx_uarte_errorsrc_get(nrfx_uarte_t const * p_instance)
{
nrf_uarte_event_clear(p_instance->p_reg, NRF_UARTE_EVENT_ERROR);
return nrf_uarte_errorsrc_get_and_clear(p_instance->p_reg);
}
static void rx_done_event(uarte_control_block_t * p_cb,
size_t bytes,
uint8_t * p_data)
{
nrfx_uarte_event_t event;
event.type = NRFX_UARTE_EVT_RX_DONE;
event.data.rxtx.bytes = bytes;
event.data.rxtx.p_data = p_data;
p_cb->handler(&event, p_cb->p_context);
}
static void tx_done_event(uarte_control_block_t * p_cb,
size_t bytes)
{
nrfx_uarte_event_t event;
event.type = NRFX_UARTE_EVT_TX_DONE;
event.data.rxtx.bytes = bytes;
event.data.rxtx.p_data = (uint8_t *)p_cb->p_tx_buffer;
p_cb->tx_buffer_length = 0;
p_cb->handler(&event, p_cb->p_context);
}
void nrfx_uarte_tx_abort(nrfx_uarte_t const * p_instance)
{
uarte_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
nrf_uarte_event_clear(p_instance->p_reg, NRF_UARTE_EVENT_TXSTOPPED);
nrf_uarte_task_trigger(p_instance->p_reg, NRF_UARTE_TASK_STOPTX);
if (p_cb->handler == NULL)
{
while (!nrf_uarte_event_check(p_instance->p_reg, NRF_UARTE_EVENT_TXSTOPPED))
{}
}
NRFX_LOG_INFO("TX transaction aborted.");
}
void nrfx_uarte_rx_abort(nrfx_uarte_t const * p_instance)
{
uarte_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
// Short between ENDRX event and STARTRX task must be disabled before
// aborting transmission.
if (p_cb->rx_secondary_buffer_length != 0)
{
nrf_uarte_shorts_disable(p_instance->p_reg, NRF_UARTE_SHORT_ENDRX_STARTRX);
}
nrf_uarte_task_trigger(p_instance->p_reg, NRF_UARTE_TASK_STOPRX);
NRFX_LOG_INFO("RX transaction aborted.");
}
static void uarte_irq_handler(NRF_UARTE_Type * p_uarte,
uarte_control_block_t * p_cb)
{
if (nrf_uarte_event_check(p_uarte, NRF_UARTE_EVENT_ERROR))
{
nrfx_uarte_event_t event;
nrf_uarte_event_clear(p_uarte, NRF_UARTE_EVENT_ERROR);
event.type = NRFX_UARTE_EVT_ERROR;
event.data.error.error_mask = nrf_uarte_errorsrc_get_and_clear(p_uarte);
event.data.error.rxtx.bytes = nrf_uarte_rx_amount_get(p_uarte);
event.data.error.rxtx.p_data = p_cb->p_rx_buffer;
// Abort transfer.
p_cb->rx_buffer_length = 0;
p_cb->rx_secondary_buffer_length = 0;
p_cb->handler(&event, p_cb->p_context);
}
else if (nrf_uarte_event_check(p_uarte, NRF_UARTE_EVENT_ENDRX))
{
nrf_uarte_event_clear(p_uarte, NRF_UARTE_EVENT_ENDRX);
size_t amount = nrf_uarte_rx_amount_get(p_uarte);
// If the transfer was stopped before completion, amount of transfered bytes
// will not be equal to the buffer length. Interrupted transfer is ignored.
if (amount == p_cb->rx_buffer_length)
{
if (p_cb->rx_secondary_buffer_length != 0)
{
uint8_t * p_data = p_cb->p_rx_buffer;
nrf_uarte_shorts_disable(p_uarte, NRF_UARTE_SHORT_ENDRX_STARTRX);
p_cb->rx_buffer_length = p_cb->rx_secondary_buffer_length;
p_cb->p_rx_buffer = p_cb->p_rx_secondary_buffer;
p_cb->rx_secondary_buffer_length = 0;
rx_done_event(p_cb, amount, p_data);
}
else
{
p_cb->rx_buffer_length = 0;
rx_done_event(p_cb, amount, p_cb->p_rx_buffer);
}
}
}
if (nrf_uarte_event_check(p_uarte, NRF_UARTE_EVENT_RXTO))
{
nrf_uarte_event_clear(p_uarte, NRF_UARTE_EVENT_RXTO);
if (p_cb->rx_buffer_length != 0)
{
p_cb->rx_buffer_length = 0;
// In case of using double-buffered reception both variables storing buffer length
// have to be cleared to prevent incorrect behaviour of the driver.
p_cb->rx_secondary_buffer_length = 0;
rx_done_event(p_cb, nrf_uarte_rx_amount_get(p_uarte), p_cb->p_rx_buffer);
}
}
if (nrf_uarte_event_check(p_uarte, NRF_UARTE_EVENT_ENDTX))
{
nrf_uarte_event_clear(p_uarte, NRF_UARTE_EVENT_ENDTX);
// Transmitter has to be stopped by triggering STOPTX task to achieve
// the lowest possible level of the UARTE power consumption.
nrf_uarte_task_trigger(p_uarte, NRF_UARTE_TASK_STOPTX);
if (p_cb->tx_buffer_length != 0)
{
tx_done_event(p_cb, nrf_uarte_tx_amount_get(p_uarte));
}
}
if (nrf_uarte_event_check(p_uarte, NRF_UARTE_EVENT_TXSTOPPED))
{
nrf_uarte_event_clear(p_uarte, NRF_UARTE_EVENT_TXSTOPPED);
if (p_cb->tx_buffer_length != 0)
{
tx_done_event(p_cb, nrf_uarte_tx_amount_get(p_uarte));
}
}
}
#if NRFX_CHECK(NRFX_UARTE0_ENABLED)
void nrfx_uarte_0_irq_handler(void)
{
uarte_irq_handler(NRF_UARTE0, &m_cb[NRFX_UARTE0_INST_IDX]);
}
#endif
#if NRFX_CHECK(NRFX_UARTE1_ENABLED)
void nrfx_uarte_1_irq_handler(void)
{
uarte_irq_handler(NRF_UARTE1, &m_cb[NRFX_UARTE1_INST_IDX]);
}
#endif
#if NRFX_CHECK(NRFX_UARTE2_ENABLED)
void nrfx_uarte_2_irq_handler(void)
{
uarte_irq_handler(NRF_UARTE2, &m_cb[NRFX_UARTE2_INST_IDX]);
}
#endif
#if NRFX_CHECK(NRFX_UARTE3_ENABLED)
void nrfx_uarte_3_irq_handler(void)
{
uarte_irq_handler(NRF_UARTE3, &m_cb[NRFX_UARTE3_INST_IDX]);
}
#endif
#endif // NRFX_CHECK(NRFX_UARTE_ENABLED)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,93 @@
/**
* Copyright (c) 2016 - 2021, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef NRFX_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 - 2021, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <nrfx.h>
#if NRFX_CHECK(NRFX_WDT_ENABLED)
#include <nrfx_wdt.h>
#define NRFX_LOG_MODULE WDT
#include <nrfx_log.h>
/**@brief WDT state. */
static nrfx_drv_state_t m_state;
/**@brief WDT alloc table. */
static uint8_t m_alloc_index;
#if !NRFX_CHECK(NRFX_WDT_CONFIG_NO_IRQ)
/**@brief WDT event handler. */
static nrfx_wdt_event_handler_t m_wdt_event_handler;
/**@brief WDT interrupt handler. */
void nrfx_wdt_irq_handler(void)
{
if (nrf_wdt_event_check(NRF_WDT_EVENT_TIMEOUT))
{
m_wdt_event_handler();
nrf_wdt_event_clear(NRF_WDT_EVENT_TIMEOUT);
}
}
#endif
nrfx_err_t nrfx_wdt_init(nrfx_wdt_config_t const * p_config,
nrfx_wdt_event_handler_t wdt_event_handler)
{
NRFX_ASSERT(p_config);
nrfx_err_t err_code;
#if !NRFX_CHECK(NRFX_WDT_CONFIG_NO_IRQ)
NRFX_ASSERT(wdt_event_handler != NULL);
m_wdt_event_handler = wdt_event_handler;
#else
NRFX_ASSERT(wdt_event_handler == NULL);
(void)wdt_event_handler;
#endif
if (m_state == NRFX_DRV_STATE_UNINITIALIZED)
{
m_state = NRFX_DRV_STATE_INITIALIZED;
}
else
{
err_code = NRFX_ERROR_INVALID_STATE;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
nrf_wdt_behaviour_set(p_config->behaviour);
uint64_t ticks = (p_config->reload_value * 32768ULL) / 1000;
NRFX_ASSERT(ticks <= UINT32_MAX);
nrf_wdt_reload_value_set((uint32_t) ticks);
#if !NRFX_CHECK(NRFX_WDT_CONFIG_NO_IRQ)
NRFX_IRQ_PRIORITY_SET(WDT_IRQn, p_config->interrupt_priority);
NRFX_IRQ_ENABLE(WDT_IRQn);
#endif
err_code = NRFX_SUCCESS;
NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
void nrfx_wdt_enable(void)
{
NRFX_ASSERT(m_alloc_index != 0);
NRFX_ASSERT(m_state == NRFX_DRV_STATE_INITIALIZED);
#if !NRFX_CHECK(NRFX_WDT_CONFIG_NO_IRQ)
nrf_wdt_int_enable(NRF_WDT_INT_TIMEOUT_MASK);
#endif
nrf_wdt_task_trigger(NRF_WDT_TASK_START);
m_state = NRFX_DRV_STATE_POWERED_ON;
NRFX_LOG_INFO("Enabled.");
}
void nrfx_wdt_feed(void)
{
NRFX_ASSERT(m_state == NRFX_DRV_STATE_POWERED_ON);
for (uint8_t i = 0; i < m_alloc_index; i++)
{
nrf_wdt_reload_request_set((nrf_wdt_rr_register_t)(NRF_WDT_RR0 + i));
}
}
nrfx_err_t nrfx_wdt_channel_alloc(nrfx_wdt_channel_id * p_channel_id)
{
nrfx_err_t result;
NRFX_ASSERT(p_channel_id);
NRFX_ASSERT(m_state == NRFX_DRV_STATE_INITIALIZED);
NRFX_CRITICAL_SECTION_ENTER();
if (m_alloc_index < NRF_WDT_CHANNEL_NUMBER)
{
*p_channel_id = (nrfx_wdt_channel_id)(NRF_WDT_RR0 + m_alloc_index);
m_alloc_index++;
nrf_wdt_reload_request_enable(*p_channel_id);
result = NRFX_SUCCESS;
}
else
{
result = NRFX_ERROR_NO_MEM;
}
NRFX_CRITICAL_SECTION_EXIT();
NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(result));
return result;
}
void nrfx_wdt_channel_feed(nrfx_wdt_channel_id channel_id)
{
NRFX_ASSERT(m_state == NRFX_DRV_STATE_POWERED_ON);
nrf_wdt_reload_request_set(channel_id);
}
#endif // NRFX_CHECK(NRFX_WDT_ENABLED)

View File

@@ -0,0 +1,166 @@
/**
* Copyright (c) 2017 - 2021, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <nrfx.h>
#if NRFX_CHECK(NRFX_PRS_ENABLED)
#include "nrfx_prs.h"
#define NRFX_LOG_MODULE PRS
#include <nrfx_log.h>
#define LOG_FUNCTION_EXIT(level, ret_code) \
NRFX_LOG_##level("Function: %s, error code: %s.", \
__func__, \
NRFX_LOG_ERROR_STRING_GET(ret_code))
typedef struct {
nrfx_irq_handler_t handler;
bool acquired;
} prs_box_t;
#define PRS_BOX_DEFINE(n) \
static prs_box_t m_prs_box_##n = { .handler = NULL, .acquired = false }; \
void nrfx_prs_box_##n##_irq_handler(void) \
{ \
NRFX_ASSERT(m_prs_box_##n.handler); \
m_prs_box_##n.handler(); \
}
#if defined(NRFX_PRS_BOX_0_ADDR) && NRFX_CHECK(NRFX_PRS_BOX_0_ENABLED)
PRS_BOX_DEFINE(0)
#endif
#if defined(NRFX_PRS_BOX_1_ADDR) && NRFX_CHECK(NRFX_PRS_BOX_1_ENABLED)
PRS_BOX_DEFINE(1)
#endif
#if defined(NRFX_PRS_BOX_2_ADDR) && NRFX_CHECK(NRFX_PRS_BOX_2_ENABLED)
PRS_BOX_DEFINE(2)
#endif
#if defined(NRFX_PRS_BOX_3_ADDR) && NRFX_CHECK(NRFX_PRS_BOX_3_ENABLED)
PRS_BOX_DEFINE(3)
#endif
#if defined(NRFX_PRS_BOX_4_ADDR) && NRFX_CHECK(NRFX_PRS_BOX_4_ENABLED)
PRS_BOX_DEFINE(4)
#endif
static prs_box_t * prs_box_get(void const * p_base_addr)
{
#if !defined(IS_PRS_BOX)
#define IS_PRS_BOX(n, p_base_addr) ((p_base_addr) == NRFX_PRS_BOX_##n##_ADDR)
#endif
#if defined(NRFX_PRS_BOX_0_ADDR) && NRFX_CHECK(NRFX_PRS_BOX_0_ENABLED)
if (IS_PRS_BOX(0, p_base_addr)) { return &m_prs_box_0; }
else
#endif
#if defined(NRFX_PRS_BOX_1_ADDR) && NRFX_CHECK(NRFX_PRS_BOX_1_ENABLED)
if (IS_PRS_BOX(1, p_base_addr)) { return &m_prs_box_1; }
else
#endif
#if defined(NRFX_PRS_BOX_2_ADDR) && NRFX_CHECK(NRFX_PRS_BOX_2_ENABLED)
if (IS_PRS_BOX(2, p_base_addr)) { return &m_prs_box_2; }
else
#endif
#if defined(NRFX_PRS_BOX_3_ADDR) && NRFX_CHECK(NRFX_PRS_BOX_3_ENABLED)
if (IS_PRS_BOX(3, p_base_addr)) { return &m_prs_box_3; }
else
#endif
#if defined(NRFX_PRS_BOX_4_ADDR) && NRFX_CHECK(NRFX_PRS_BOX_4_ENABLED)
if (IS_PRS_BOX(4, p_base_addr)) { return &m_prs_box_4; }
else
#endif
{
return NULL;
}
}
nrfx_err_t nrfx_prs_acquire(void const * p_base_addr,
nrfx_irq_handler_t irq_handler)
{
NRFX_ASSERT(p_base_addr);
nrfx_err_t ret_code;
prs_box_t * p_box = prs_box_get(p_base_addr);
if (p_box != NULL)
{
bool busy = false;
NRFX_CRITICAL_SECTION_ENTER();
if (p_box->acquired)
{
busy = true;
}
else
{
p_box->handler = irq_handler;
p_box->acquired = true;
}
NRFX_CRITICAL_SECTION_EXIT();
if (busy)
{
ret_code = NRFX_ERROR_BUSY;
LOG_FUNCTION_EXIT(WARNING, ret_code);
return ret_code;
}
}
ret_code = NRFX_SUCCESS;
LOG_FUNCTION_EXIT(INFO, ret_code);
return ret_code;
}
void nrfx_prs_release(void const * p_base_addr)
{
NRFX_ASSERT(p_base_addr);
prs_box_t * p_box = prs_box_get(p_base_addr);
if (p_box != NULL)
{
p_box->handler = NULL;
p_box->acquired = false;
}
}
#endif // NRFX_CHECK(NRFX_PRS_ENABLED)

View File

@@ -0,0 +1,155 @@
/**
* Copyright (c) 2017 - 2021, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef NRFX_PRS_H__
#define NRFX_PRS_H__
#include <nrfx.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @defgroup nrfx_prs Peripheral Resource Sharing (PRS)
* @{
* @ingroup nrfx
*
* @brief Peripheral Resource Sharing interface (PRS).
*/
#if defined(NRF51)
// SPI0, TWI0
#define NRFX_PRS_BOX_0_ADDR NRF_SPI0
// SPI1, SPIS1, TWI1
#define NRFX_PRS_BOX_1_ADDR NRF_SPI1
#elif defined(NRF52810_XXAA)
// TWIM0, TWIS0, TWI0
#define NRFX_PRS_BOX_0_ADDR NRF_TWIM0
// SPIM0, SPIS0, SPI0
#define NRFX_PRS_BOX_1_ADDR NRF_SPIM0
// UARTE0, UART0
#define NRFX_PRS_BOX_2_ADDR NRF_UARTE0
#elif defined(NRF52811_XXAA)
// TWIM0, TWIS0, TWI0, SPIM1, SPIS1, SPI1
#define NRFX_PRS_BOX_0_ADDR NRF_TWIM0
// SPIM0, SPIS0, SPI0
#define NRFX_PRS_BOX_1_ADDR NRF_SPIM0
// UART0, UARTE0
#define NRFX_PRS_BOX_2_ADDR NRF_UART0
#elif defined(NRF52820_XXAA)
// SPIM0, SPIS0, TWIM0, TWIS0, SPI0, TWI0
#define NRFX_PRS_BOX_0_ADDR NRF_SPIM0
// SPIM1, SPIS1, TWIM1, TWIS1, SPI1, TWI1
#define NRFX_PRS_BOX_1_ADDR NRF_SPIM1
// UARTE0, UART0
#define NRFX_PRS_BOX_2_ADDR NRF_UARTE0
#elif defined(NRF52832_XXAA) || defined(NRF52832_XXAB) || \
defined(NRF52833_XXAA) || defined(NRF52840_XXAA)
// SPIM0, SPIS0, TWIM0, TWIS0, SPI0, TWI0
#define NRFX_PRS_BOX_0_ADDR NRF_SPIM0
// SPIM1, SPIS1, TWIM1, TWIS1, SPI1, TWI1
#define NRFX_PRS_BOX_1_ADDR NRF_SPIM1
// SPIM2, SPIS2, SPI2
#define NRFX_PRS_BOX_2_ADDR NRF_SPIM2
// COMP, LPCOMP
#define NRFX_PRS_BOX_3_ADDR NRF_COMP
// UARTE0, UART0
#define NRFX_PRS_BOX_4_ADDR NRF_UARTE0
#elif defined(NRF9160_XXAA)
// UARTE0, SPIM0, SPIS0, TWIM0, TWIS0
#define NRFX_PRS_BOX_0_ADDR NRF_UARTE0
// UARTE1, SPIM1, SPIS1, TWIM1, TWIS1
#define NRFX_PRS_BOX_1_ADDR NRF_UARTE1
// UARTE2, SPIM2, SPIS2, TWIM2, TWIS2
#define NRFX_PRS_BOX_2_ADDR NRF_UARTE2
// UARTE3, SPIM3, SPIS3, TWIM3, TWIS3
#define NRFX_PRS_BOX_3_ADDR NRF_UARTE3
#else
#error "Unknown device."
#endif
/**
* @brief Function for acquiring shared peripheral resources associated with
* the specified peripheral.
*
* Certain resources and registers are shared among peripherals that have
* the same ID (for example: SPI0, SPIM0, SPIS0, TWI0, TWIM0, and TWIS0 in
* nRF52832). Only one of them can be utilized at a given time. This function
* reserves proper resources to be used by the specified peripheral.
* If NRFX_PRS_ENABLED is set to a non-zero value, IRQ handlers for peripherals
* that are sharing resources with others are implemented by the @ref nrfx_prs
* module instead of individual drivers. The drivers must then specify their
* interrupt handling routines and register them by using this function.
*
* @param[in] p_base_addr Requested peripheral base pointer.
* @param[in] irq_handler Interrupt handler to register.
*
* @retval NRFX_SUCCESS If resources were acquired successfully or the
* specified peripheral is not handled by the PRS
* subsystem and there is no need to acquire resources
* for it.
* @retval NRFX_ERROR_BUSY If resources were already acquired.
*/
nrfx_err_t nrfx_prs_acquire(void const * p_base_addr,
nrfx_irq_handler_t irq_handler);
/**
* @brief Function for releasing shared resources reserved previously by
* @ref nrfx_prs_acquire() for the specified peripheral.
*
* @param[in] p_base_addr Released peripheral base pointer.
*/
void nrfx_prs_release(void const * p_base_addr);
/** @} */
void nrfx_prs_box_0_irq_handler(void);
void nrfx_prs_box_1_irq_handler(void);
void nrfx_prs_box_2_irq_handler(void);
void nrfx_prs_box_3_irq_handler(void);
void nrfx_prs_box_4_irq_handler(void);
#ifdef __cplusplus
}
#endif
#endif // NRFX_PRS_H__