Initial commit
This commit is contained in:
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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__
|
||||
Reference in New Issue
Block a user