move components to SDK dir

This commit is contained in:
Shuanglei Tao
2025-03-03 09:06:26 +08:00
parent 20d1297e57
commit f4f4c9e60d
1021 changed files with 58 additions and 35059 deletions

View File

@@ -0,0 +1,809 @@
/**
* Copyright (c) 2015 - 2017, 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 "sdk_common.h"
#if NRF_MODULE_ENABLED(GPIOTE)
#include "nrf_drv_gpiote.h"
#include "nrf_drv_common.h"
#include "app_util_platform.h"
#include "nrf_assert.h"
#include "nrf_bitmask.h"
#include <string.h>
#define NRF_LOG_MODULE_NAME "GPIOTE"
#if GPIOTE_CONFIG_LOG_ENABLED
#define NRF_LOG_LEVEL GPIOTE_CONFIG_LOG_LEVEL
#define NRF_LOG_INFO_COLOR GPIOTE_CONFIG_INFO_COLOR
#define NRF_LOG_DEBUG_COLOR GPIOTE_CONFIG_DEBUG_COLOR
#else // GPIOTE_CONFIG_LOG_ENABLED
#define NRF_LOG_LEVEL 0
#endif // GPIOTE_CONFIG_LOG_ENABLED
#include "nrf_log.h"
#include "nrf_log_ctrl.h"
/* Validate configuration */
INTERRUPT_PRIORITY_VALIDATION(GPIOTE_CONFIG_IRQ_PRIORITY);
#define FORBIDDEN_HANDLER_ADDRESS ((nrf_drv_gpiote_evt_handler_t)UINT32_MAX)
#define PIN_NOT_USED (-1)
#define PIN_USED (-2)
#define NO_CHANNELS (-1)
#define SENSE_FIELD_POS (6)
#define SENSE_FIELD_MASK (0xC0)
/**
* @brief Macro for 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(__SDK_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(__SDK_DOXYGEN__)
#if defined(GPIOTE_FEATURE_CLR_PRESENT) || defined(__SDK_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(__SDK_DOXYGEN__)
/*lint -save -e661*/
typedef struct
{
nrf_drv_gpiote_evt_handler_t handlers[GPIOTE_CH_NUM + GPIOTE_CONFIG_NUM_OF_LOW_POWER_EVENTS];
int8_t pin_assignments[NUMBER_OF_PINS];
int8_t port_handlers_pins[GPIOTE_CONFIG_NUM_OF_LOW_POWER_EVENTS];
nrf_drv_state_t state;
} gpiote_control_block_t;
static gpiote_control_block_t m_cb;
__STATIC_INLINE bool pin_in_use(uint32_t pin)
{
return (m_cb.pin_assignments[pin] != PIN_NOT_USED);
}
__STATIC_INLINE bool pin_in_use_as_non_task_out(uint32_t pin)
{
return (m_cb.pin_assignments[pin] == PIN_USED);
}
__STATIC_INLINE bool pin_in_use_by_te(uint32_t pin)
{
return (m_cb.pin_assignments[pin] >= 0 && m_cb.pin_assignments[pin] <
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,
nrf_drv_gpiote_evt_handler_t handler,
bool is_channel)
{
m_cb.pin_assignments[pin] = channel_id;
m_cb.handlers[channel_id] = handler;
if (!is_channel)
{
m_cb.port_handlers_pins[channel_id - 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 int8_t channel_port_get(uint32_t pin)
{
return m_cb.pin_assignments[pin];
}
__STATIC_INLINE nrf_drv_gpiote_evt_handler_t channel_handler_get(uint32_t channel)
{
return m_cb.handlers[channel];
}
static int8_t channel_port_alloc(uint32_t pin, nrf_drv_gpiote_evt_handler_t handler, bool channel)
{
int8_t channel_id = NO_CHANNELS;
uint32_t i;
uint32_t start_idx = channel ? 0 : GPIOTE_CH_NUM;
uint32_t end_idx =
channel ? GPIOTE_CH_NUM : (GPIOTE_CH_NUM + 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;
}
}
ret_code_t nrf_drv_gpiote_init(void)
{
ret_code_t err_code;
if (m_cb.state != NRF_DRV_STATE_UNINITIALIZED)
{
err_code = NRF_ERROR_INVALID_STATE;
NRF_LOG_WARNING("Function: %s, error code: %s.\r\n",
(uint32_t)__func__,
(uint32_t)ERR_TO_STR(err_code));
return err_code;
}
uint8_t i;
for (i = 0; i < NUMBER_OF_PINS; i++)
{
pin_in_use_clear(i);
}
for (i = 0; i < (GPIOTE_CH_NUM + GPIOTE_CONFIG_NUM_OF_LOW_POWER_EVENTS); i++)
{
channel_free(i);
}
nrf_drv_common_irq_enable(GPIOTE_IRQn, GPIOTE_CONFIG_IRQ_PRIORITY);
nrf_gpiote_event_clear(NRF_GPIOTE_EVENTS_PORT);
nrf_gpiote_int_enable(GPIOTE_INTENSET_PORT_Msk);
m_cb.state = NRF_DRV_STATE_INITIALIZED;
err_code = NRF_SUCCESS;
NRF_LOG_INFO("Function: %s, error code: %s.\r\n", (uint32_t)__func__,
(uint32_t)ERR_TO_STR(err_code));
return err_code;
}
bool nrf_drv_gpiote_is_init(void)
{
return (m_cb.state != NRF_DRV_STATE_UNINITIALIZED) ? true : false;
}
void nrf_drv_gpiote_uninit(void)
{
ASSERT(m_cb.state != NRF_DRV_STATE_UNINITIALIZED);
uint32_t i;
for (i = 0; i < NUMBER_OF_PINS; i++)
{
if (pin_in_use_as_non_task_out(i))
{
nrf_drv_gpiote_out_uninit(i);
}
else if ( pin_in_use_by_gpiote(i))
{
/* Disable gpiote_in is having the same effect on out pin as gpiote_out_uninit on
* so it can be called on all pins used by GPIOTE.
*/
nrf_drv_gpiote_in_uninit(i);
}
}
m_cb.state = NRF_DRV_STATE_UNINITIALIZED;
NRF_LOG_INFO("Uninitialized.\r\n");
}
ret_code_t nrf_drv_gpiote_out_init(nrf_drv_gpiote_pin_t pin,
nrf_drv_gpiote_out_config_t const * p_config)
{
ASSERT(pin < NUMBER_OF_PINS);
ASSERT(m_cb.state == NRF_DRV_STATE_INITIALIZED);
ASSERT(p_config);
ret_code_t err_code = NRF_SUCCESS;
if (pin_in_use(pin))
{
err_code = NRF_ERROR_INVALID_STATE;
}
else
{
if (p_config->task_pin)
{
int8_t channel = channel_port_alloc(pin, NULL, true);
if (channel != NO_CHANNELS)
{
nrf_gpiote_task_configure(channel, pin, p_config->action, p_config->init_state);
}
else
{
err_code = NRF_ERROR_NO_MEM;
}
}
else
{
pin_in_use_set(pin);
}
if (err_code == NRF_SUCCESS)
{
if (p_config->init_state == NRF_GPIOTE_INITIAL_VALUE_HIGH)
{
nrf_gpio_pin_set(pin);
}
else
{
nrf_gpio_pin_clear(pin);
}
nrf_gpio_cfg_output(pin);
}
}
NRF_LOG_INFO("Function: %s, error code: %s.\r\n", (uint32_t)__func__,
(uint32_t)ERR_TO_STR(err_code));
return err_code;
}
void nrf_drv_gpiote_out_uninit(nrf_drv_gpiote_pin_t pin)
{
ASSERT(pin < NUMBER_OF_PINS);
ASSERT(pin_in_use(pin));
if (pin_in_use_by_te(pin))
{
channel_free((uint8_t)channel_port_get(pin));
nrf_gpiote_te_default(channel_port_get(pin));
}
pin_in_use_clear(pin);
nrf_gpio_cfg_default(pin);
}
void nrf_drv_gpiote_out_set(nrf_drv_gpiote_pin_t pin)
{
ASSERT(pin < NUMBER_OF_PINS);
ASSERT(pin_in_use(pin));
ASSERT(!pin_in_use_by_te(pin))
nrf_gpio_pin_set(pin);
}
void nrf_drv_gpiote_out_clear(nrf_drv_gpiote_pin_t pin)
{
ASSERT(pin < NUMBER_OF_PINS);
ASSERT(pin_in_use(pin));
ASSERT(!pin_in_use_by_te(pin))
nrf_gpio_pin_clear(pin);
}
void nrf_drv_gpiote_out_toggle(nrf_drv_gpiote_pin_t pin)
{
ASSERT(pin < NUMBER_OF_PINS);
ASSERT(pin_in_use(pin));
ASSERT(!pin_in_use_by_te(pin))
nrf_gpio_pin_toggle(pin);
}
void nrf_drv_gpiote_out_task_enable(nrf_drv_gpiote_pin_t pin)
{
ASSERT(pin < NUMBER_OF_PINS);
ASSERT(pin_in_use(pin));
ASSERT(pin_in_use_by_te(pin))
nrf_gpiote_task_enable(m_cb.pin_assignments[pin]);
}
void nrf_drv_gpiote_out_task_disable(nrf_drv_gpiote_pin_t pin)
{
ASSERT(pin < NUMBER_OF_PINS);
ASSERT(pin_in_use(pin));
ASSERT(pin_in_use_by_te(pin))
nrf_gpiote_task_disable(m_cb.pin_assignments[pin]);
}
uint32_t nrf_drv_gpiote_out_task_addr_get(nrf_drv_gpiote_pin_t pin)
{
ASSERT(pin < NUMBER_OF_PINS);
ASSERT(pin_in_use_by_te(pin));
nrf_gpiote_tasks_t task = TE_OUT_IDX_TO_TASK_ADDR(channel_port_get(pin));
return nrf_gpiote_task_addr_get(task);
}
#if defined(GPIOTE_FEATURE_SET_PRESENT)
uint32_t nrf_drv_gpiote_set_task_addr_get(nrf_drv_gpiote_pin_t pin)
{
ASSERT(pin < NUMBER_OF_PINS);
ASSERT(pin_in_use_by_te(pin));
nrf_gpiote_tasks_t task = TE_SET_IDX_TO_TASK_ADDR(channel_port_get(pin));
return nrf_gpiote_task_addr_get(task);
}
#endif // defined(GPIOTE_FEATURE_SET_PRESENT)
#if defined(GPIOTE_FEATURE_CLR_PRESENT)
uint32_t nrf_drv_gpiote_clr_task_addr_get(nrf_drv_gpiote_pin_t pin)
{
ASSERT(pin < NUMBER_OF_PINS);
ASSERT(pin_in_use_by_te(pin));
nrf_gpiote_tasks_t task = TE_CLR_IDX_TO_TASK_ADDR(channel_port_get(pin));
return nrf_gpiote_task_addr_get(task);
}
#endif // defined(GPIOTE_FEATURE_CLR_PRESENT)
void nrf_drv_gpiote_out_task_force(nrf_drv_gpiote_pin_t pin, uint8_t state)
{
ASSERT(pin < NUMBER_OF_PINS);
ASSERT(pin_in_use(pin));
ASSERT(pin_in_use_by_te(pin));
nrf_gpiote_outinit_t init_val =
state ? NRF_GPIOTE_INITIAL_VALUE_HIGH : NRF_GPIOTE_INITIAL_VALUE_LOW;
nrf_gpiote_task_force(m_cb.pin_assignments[pin], init_val);
}
void nrf_drv_gpiote_out_task_trigger(nrf_drv_gpiote_pin_t pin)
{
ASSERT(pin < NUMBER_OF_PINS);
ASSERT(pin_in_use(pin));
ASSERT(pin_in_use_by_te(pin));
nrf_gpiote_tasks_t task = TE_OUT_IDX_TO_TASK_ADDR(channel_port_get(pin));
nrf_gpiote_task_set(task);
}
#if defined(GPIOTE_FEATURE_SET_PRESENT)
void nrf_drv_gpiote_set_task_trigger(nrf_drv_gpiote_pin_t pin)
{
ASSERT(pin < NUMBER_OF_PINS);
ASSERT(pin_in_use(pin));
ASSERT(pin_in_use_by_te(pin));
nrf_gpiote_tasks_t task = TE_SET_IDX_TO_TASK_ADDR(channel_port_get(pin));
nrf_gpiote_task_set(task);
}
#endif // defined(GPIOTE_FEATURE_SET_PRESENT)
#if defined(GPIOTE_FEATURE_CLR_PRESENT)
void nrf_drv_gpiote_clr_task_trigger(nrf_drv_gpiote_pin_t pin)
{
ASSERT(pin < NUMBER_OF_PINS);
ASSERT(pin_in_use(pin));
ASSERT(pin_in_use_by_te(pin));
nrf_gpiote_tasks_t task = TE_CLR_IDX_TO_TASK_ADDR(channel_port_get(pin));
nrf_gpiote_task_set(task);
}
#endif // defined(GPIOTE_FEATURE_CLR_PRESENT)
ret_code_t nrf_drv_gpiote_in_init(nrf_drv_gpiote_pin_t pin,
nrf_drv_gpiote_in_config_t const * p_config,
nrf_drv_gpiote_evt_handler_t evt_handler)
{
ASSERT(pin < NUMBER_OF_PINS);
ret_code_t err_code = NRF_SUCCESS;
/* Only one GPIOTE channel can be assigned to one physical pin. */
if (pin_in_use_by_gpiote(pin))
{
err_code = NRF_ERROR_INVALID_STATE;
}
else
{
int8_t channel = channel_port_alloc(pin, evt_handler, p_config->hi_accuracy);
if (channel != NO_CHANNELS)
{
if (p_config->is_watcher)
{
nrf_gpio_cfg_watcher(pin);
}
else
{
nrf_gpio_cfg_input(pin, p_config->pull);
}
if (p_config->hi_accuracy)
{
nrf_gpiote_event_configure(channel, pin, p_config->sense);
}
else
{
m_cb.port_handlers_pins[channel -
GPIOTE_CH_NUM] |= (p_config->sense) << SENSE_FIELD_POS;
}
}
else
{
err_code = NRF_ERROR_NO_MEM;
}
}
NRF_LOG_INFO("Function: %s, error code: %s.\r\n", (uint32_t)__func__,
(uint32_t)ERR_TO_STR(err_code));
return err_code;
}
void nrf_drv_gpiote_in_event_enable(nrf_drv_gpiote_pin_t pin, bool int_enable)
{
ASSERT(pin < NUMBER_OF_PINS);
ASSERT(pin_in_use_by_gpiote(pin));
if (pin_in_use_by_port(pin))
{
uint8_t pin_and_sense =
m_cb.port_handlers_pins[channel_port_get(pin) - GPIOTE_CH_NUM];
nrf_gpiote_polarity_t polarity =
(nrf_gpiote_polarity_t)(pin_and_sense >> SENSE_FIELD_POS);
nrf_gpio_pin_sense_t sense;
if (polarity == NRF_GPIOTE_POLARITY_TOGGLE)
{
/* read current pin state and set for next sense to oposit */
sense = (nrf_gpio_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(channel);
nrf_gpiote_event_enable(channel);
nrf_gpiote_event_clear(event);
if (int_enable)
{
nrf_drv_gpiote_evt_handler_t handler = channel_handler_get(channel_port_get(pin));
// Enable the interrupt only if event handler was provided.
if (handler)
{
nrf_gpiote_int_enable(1 << channel);
}
}
}
}
void nrf_drv_gpiote_in_event_disable(nrf_drv_gpiote_pin_t pin)
{
ASSERT(pin < NUMBER_OF_PINS);
ASSERT(pin_in_use_by_gpiote(pin));
if (pin_in_use_by_port(pin))
{
nrf_gpio_cfg_sense_set(pin, NRF_GPIO_PIN_NOSENSE);
}
else if (pin_in_use_by_te(pin))
{
int32_t channel = (int32_t)channel_port_get(pin);
nrf_gpiote_event_disable(channel);
nrf_gpiote_int_disable(1 << channel);
}
}
void nrf_drv_gpiote_in_uninit(nrf_drv_gpiote_pin_t pin)
{
ASSERT(pin < NUMBER_OF_PINS);
ASSERT(pin_in_use_by_gpiote(pin));
nrf_drv_gpiote_in_event_disable(pin);
if (pin_in_use_by_te(pin))
{
nrf_gpiote_te_default(channel_port_get(pin));
}
nrf_gpio_cfg_default(pin);
channel_free((uint8_t)channel_port_get(pin));
pin_in_use_clear(pin);
}
bool nrf_drv_gpiote_in_is_set(nrf_drv_gpiote_pin_t pin)
{
ASSERT(pin < NUMBER_OF_PINS);
return nrf_gpio_pin_read(pin) ? true : false;
}
uint32_t nrf_drv_gpiote_in_event_addr_get(nrf_drv_gpiote_pin_t pin)
{
ASSERT(pin < NUMBER_OF_PINS);
ASSERT(pin_in_use_by_port(pin) || pin_in_use_by_te(pin));
nrf_gpiote_events_t event = NRF_GPIOTE_EVENTS_PORT;
if (pin_in_use_by_te(pin))
{
event = TE_IDX_TO_EVENT_ADDR(channel_port_get(pin));
}
return nrf_gpiote_event_addr_get(event);
}
void GPIOTE_IRQHandler(void)
{
uint32_t status = 0;
uint32_t input[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;
nrf_gpio_ports_read(0, GPIO_COUNT, input);
}
/* 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)
{
nrf_drv_gpiote_pin_t pin = nrf_gpiote_event_pin_get(i);
NRF_LOG_DEBUG("Event in number: %d.\r\n", i);
nrf_gpiote_polarity_t polarity = nrf_gpiote_event_polarity_get(i);
nrf_drv_gpiote_evt_handler_t handler = channel_handler_get(i);
NRF_LOG_DEBUG("Pin: %d, polarity: %d.\r\n", pin, polarity);
if (handler)
{
handler(pin, polarity);
}
}
mask <<= 1;
}
}
if (status & (uint32_t)NRF_GPIOTE_INT_PORT_MASK)
{
/* Process port event. */
uint32_t port_idx;
uint8_t repeat = 0;
uint32_t toggle_mask[GPIO_COUNT] = {0};
uint32_t pins_to_check[GPIO_COUNT];
// Faster way of doing memset because in interrupt context.
for (port_idx = 0; port_idx < GPIO_COUNT; port_idx++)
{
pins_to_check[port_idx] = 0xFFFFFFFF;
}
do
{
repeat = 0;
for (i = 0; i < GPIOTE_CONFIG_NUM_OF_LOW_POWER_EVENTS; i++)
{
uint8_t pin_and_sense = m_cb.port_handlers_pins[i];
nrf_drv_gpiote_pin_t pin = (pin_and_sense & ~SENSE_FIELD_MASK);
if ((m_cb.port_handlers_pins[i] != PIN_NOT_USED)
&& nrf_bitmask_bit_is_set(pin, pins_to_check))
{
nrf_gpiote_polarity_t polarity =
(nrf_gpiote_polarity_t)((pin_and_sense &
SENSE_FIELD_MASK) >> SENSE_FIELD_POS);
nrf_drv_gpiote_evt_handler_t handler =
channel_handler_get(channel_port_get(pin));
if (handler || (polarity == NRF_GPIOTE_POLARITY_TOGGLE))
{
if (polarity == NRF_GPIOTE_POLARITY_TOGGLE)
{
nrf_bitmask_bit_set(pin, toggle_mask);
}
nrf_gpio_pin_sense_t sense = nrf_gpio_pin_sense_get(pin);
uint32_t pin_state = nrf_bitmask_bit_is_set(pin, input);
if ((pin_state && (sense == NRF_GPIO_PIN_SENSE_HIGH)) ||
(!pin_state && (sense == NRF_GPIO_PIN_SENSE_LOW)) )
{
NRF_LOG_DEBUG("PORT event for pin: %d, polarity: %d.\r\n", pin,
polarity);
if (polarity == NRF_GPIOTE_POLARITY_TOGGLE)
{
nrf_gpio_pin_sense_t next_sense =
(sense == NRF_GPIO_PIN_SENSE_HIGH) ?
NRF_GPIO_PIN_SENSE_LOW :
NRF_GPIO_PIN_SENSE_HIGH;
nrf_gpio_cfg_sense_set(pin, next_sense);
++repeat;
}
if (handler)
{
handler(pin, polarity);
}
}
}
}
}
if (repeat)
{
// When one of the pins in low-accuracy and toggle mode becomes active,
// it's sense mode is inverted to clear the internal SENSE signal.
// State of any other enabled low-accuracy input in toggle mode must be checked
// explicitly, because it does not trigger the interrput when SENSE signal is active.
// For more information about SENSE functionality, refer to Product Specification.
uint32_t new_input[GPIO_COUNT];
bool input_unchanged = true;
nrf_gpio_ports_read(0, GPIO_COUNT, new_input);
// Faster way of doing memcmp because in interrupt context.
for (port_idx = 0; port_idx < GPIO_COUNT; port_idx++)
{
if (new_input[port_idx] != input[port_idx])
{
input_unchanged = false;
break;
}
}
if (input_unchanged)
{
// No change.
repeat = 0;
}
else
{
// Faster way of doing memcpy because in interrupt context.
for (port_idx = 0; port_idx < GPIO_COUNT; port_idx++)
{
input[port_idx] = new_input[port_idx];
pins_to_check[port_idx] = toggle_mask[port_idx];
}
}
}
}
while (repeat);
}
}
/*lint -restore*/
#endif // NRF_MODULE_ENABLED(GPIOTE)

View File

@@ -0,0 +1,386 @@
/**
* Copyright (c) 2015 - 2017, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef NRF_DRV_GPIOTE__
#define NRF_DRV_GPIOTE__
/**
* @addtogroup nrf_gpiote GPIOTE abstraction and driver
* @ingroup nrf_drivers
* @brief GPIOTE APIs.
* @defgroup nrf_drv_gpiote GPIOTE driver
* @{
* @ingroup nrf_gpiote
* @brief GPIOTE driver for managing input and output pins.
*/
#include "nrf_gpiote.h"
#include "nrf_gpio.h"
#include "sdk_errors.h"
#include <stdint.h>
#include <stdbool.h>
#include "sdk_config.h"
#ifdef __cplusplus
extern "C" {
#endif
/**@brief Input pin configuration. */
typedef struct
{
nrf_gpiote_polarity_t sense; /**< Transition that triggers interrupt. */
nrf_gpio_pin_pull_t pull; /**< Pulling mode. */
bool is_watcher; /**< True when the input pin is tracking an output pin. */
bool hi_accuracy;/**< True when high accuracy (IN_EVENT) is used. */
} nrf_drv_gpiote_in_config_t;
/**@brief Macro for configuring a pin to use a GPIO IN or PORT EVENT to detect low-to-high transition.
* @details Set hi_accu to true to use IN_EVENT. */
#define GPIOTE_CONFIG_IN_SENSE_LOTOHI(hi_accu) \
{ \
.is_watcher = false, \
.hi_accuracy = hi_accu, \
.pull = NRF_GPIO_PIN_NOPULL, \
.sense = NRF_GPIOTE_POLARITY_LOTOHI, \
}
/**@brief Macro for configuring a pin to use a GPIO IN or PORT EVENT to detect high-to-low transition.
* @details Set hi_accu to true to use IN_EVENT. */
#define GPIOTE_CONFIG_IN_SENSE_HITOLO(hi_accu) \
{ \
.is_watcher = false, \
.hi_accuracy = hi_accu, \
.pull = NRF_GPIO_PIN_NOPULL, \
.sense = NRF_GPIOTE_POLARITY_HITOLO, \
}
/**@brief Macro for configuring a pin to use a GPIO IN or PORT EVENT to detect any change on the pin.
* @details Set hi_accu to true to use IN_EVENT.*/
#define GPIOTE_CONFIG_IN_SENSE_TOGGLE(hi_accu) \
{ \
.is_watcher = false, \
.hi_accuracy = hi_accu, \
.pull = NRF_GPIO_PIN_NOPULL, \
.sense = NRF_GPIOTE_POLARITY_TOGGLE, \
}
/**@brief Output pin configuration. */
typedef struct
{
nrf_gpiote_polarity_t action; /**< Configuration of the pin task. */
nrf_gpiote_outinit_t init_state; /**< Initial state of the output pin. */
bool task_pin; /**< True if the pin is controlled by a GPIOTE task. */
} nrf_drv_gpiote_out_config_t;
/**@brief Macro for configuring a pin to use as output. GPIOTE is not used for the pin. */
#define GPIOTE_CONFIG_OUT_SIMPLE(init_high) \
{ \
.init_state = init_high ? NRF_GPIOTE_INITIAL_VALUE_HIGH : NRF_GPIOTE_INITIAL_VALUE_LOW, \
.task_pin = false, \
}
/**@brief Macro for configuring a pin to use the GPIO OUT TASK to change the state from high to low.
* @details The task will clear the pin. Therefore, the pin is set initially. */
#define GPIOTE_CONFIG_OUT_TASK_LOW \
{ \
.init_state = NRF_GPIOTE_INITIAL_VALUE_HIGH, \
.task_pin = true, \
.action = NRF_GPIOTE_POLARITY_HITOLO, \
}
/**@brief Macro for configuring a pin to use the GPIO OUT TASK to change the state from low to high.
* @details The task will set the pin. Therefore, the pin is cleared initially. */
#define GPIOTE_CONFIG_OUT_TASK_HIGH \
{ \
.init_state = NRF_GPIOTE_INITIAL_VALUE_LOW, \
.task_pin = true, \
.action = NRF_GPIOTE_POLARITY_LOTOHI, \
}
/**@brief Macro for configuring a pin to use the GPIO OUT TASK to toggle the pin state.
* @details The initial pin state must be provided. */
#define GPIOTE_CONFIG_OUT_TASK_TOGGLE(init_high) \
{ \
.init_state = init_high ? NRF_GPIOTE_INITIAL_VALUE_HIGH : NRF_GPIOTE_INITIAL_VALUE_LOW, \
.task_pin = true, \
.action = NRF_GPIOTE_POLARITY_TOGGLE, \
}
/** @brief Pin. */
typedef uint32_t nrf_drv_gpiote_pin_t;
/**
* @brief Pin event handler prototype.
* @param pin Pin that triggered this event.
* @param action Action that lead to triggering this event.
*/
typedef void (*nrf_drv_gpiote_evt_handler_t)(nrf_drv_gpiote_pin_t pin, nrf_gpiote_polarity_t action);
/**
* @brief Function for initializing the GPIOTE module.
*
* @details Only static configuration is supported to prevent the shared
* resource being customized by the initiator.
*
* @retval NRF_SUCCESS If initialization was successful.
* @retval NRF_ERROR_INVALID_STATE If the driver was already initialized.
*/
ret_code_t nrf_drv_gpiote_init(void);
/**
* @brief Function for checking if the GPIOTE module is initialized.
*
* @details The GPIOTE module is a shared module. Therefore, you should check if
* the module is already initialized and skip initialization if it is.
*
* @retval true If the module is already initialized.
* @retval false If the module is not initialized.
*/
bool nrf_drv_gpiote_is_init(void);
/**
* @brief Function for uninitializing the GPIOTE module.
*/
void nrf_drv_gpiote_uninit(void);
/**
* @brief Function for initializing a GPIOTE output pin.
* @details The output pin can be controlled by the CPU or by PPI. The initial
* configuration specifies which mode is used. If PPI mode is used, the driver
* attempts to allocate one of the available GPIOTE channels. If no channel is
* available, an error is returned.
*
* @param[in] pin Pin.
* @param[in] p_config Initial configuration.
*
* @retval NRF_SUCCESS If initialization was successful.
* @retval NRF_ERROR_INVALID_STATE If the driver is not initialized or the pin is already used.
* @retval NRF_ERROR_NO_MEM If no GPIOTE channel is available.
*/
ret_code_t nrf_drv_gpiote_out_init(nrf_drv_gpiote_pin_t pin,
nrf_drv_gpiote_out_config_t const * p_config);
/**
* @brief Function for uninitializing a GPIOTE output pin.
* @details The driver frees the GPIOTE channel if the output pin was using one.
*
* @param[in] pin Pin.
*/
void nrf_drv_gpiote_out_uninit(nrf_drv_gpiote_pin_t pin);
/**
* @brief Function for setting a GPIOTE output pin.
*
* @param[in] pin Pin.
*/
void nrf_drv_gpiote_out_set(nrf_drv_gpiote_pin_t pin);
/**
* @brief Function for clearing a GPIOTE output pin.
*
* @param[in] pin Pin.
*/
void nrf_drv_gpiote_out_clear(nrf_drv_gpiote_pin_t pin);
/**
* @brief Function for toggling a GPIOTE output pin.
*
* @param[in] pin Pin.
*/
void nrf_drv_gpiote_out_toggle(nrf_drv_gpiote_pin_t pin);
/**
* @brief Function for enabling a GPIOTE output pin task.
*
* @param[in] pin Pin.
*/
void nrf_drv_gpiote_out_task_enable(nrf_drv_gpiote_pin_t pin);
/**
* @brief Function for disabling a GPIOTE output pin task.
*
* @param[in] pin Pin.
*/
void nrf_drv_gpiote_out_task_disable(nrf_drv_gpiote_pin_t pin);
/**
* @brief Function for getting the address of a configurable GPIOTE task.
*
* @param[in] pin Pin.
*
* @return Address of OUT task.
*/
uint32_t nrf_drv_gpiote_out_task_addr_get(nrf_drv_gpiote_pin_t pin);
#if defined(GPIOTE_FEATURE_SET_PRESENT)
/**
* @brief Function for getting the address of a configurable GPIOTE task.
*
* @param[in] pin Pin.
*
* @return Address of SET task.
*/
uint32_t nrf_drv_gpiote_set_task_addr_get(nrf_drv_gpiote_pin_t pin);
#endif // defined(GPIOTE_FEATURE_SET_PRESENT)
#if defined(GPIOTE_FEATURE_CLR_PRESENT)
/**
* @brief Function for getting the address of a configurable GPIOTE task.
*
* @param[in] pin Pin.
*
* @return Address of CLR task.
*/
uint32_t nrf_drv_gpiote_clr_task_addr_get(nrf_drv_gpiote_pin_t pin);
#endif // defined(GPIOTE_FEATURE_CLR_PRESENT)
/**
* @brief Function for initializing a GPIOTE input pin.
* @details The input pin can act in two ways:
* - lower accuracy but low power (high frequency clock not needed)
* - higher accuracy (high frequency clock required)
*
* The initial configuration specifies which mode is used.
* If high-accuracy mode is used, the driver attempts to allocate one
* of the available GPIOTE channels. If no channel is
* available, an error is returned.
* In low accuracy mode SENSE feature is used. In this case only one active pin
* can be detected at a time. It can be worked around by setting all of the used
* low accuracy pins to toggle mode.
* For more information about SENSE functionality, refer to Product Specification.
*
* @param[in] pin Pin.
* @param[in] p_config Initial configuration.
* @param[in] evt_handler User function to be called when the configured transition occurs.
*
* @retval NRF_SUCCESS If initialization was successful.
* @retval NRF_ERROR_INVALID_STATE If the driver is not initialized or the pin is already used.
* @retval NRF_ERROR_NO_MEM If no GPIOTE channel is available.
*/
ret_code_t nrf_drv_gpiote_in_init(nrf_drv_gpiote_pin_t pin,
nrf_drv_gpiote_in_config_t const * p_config,
nrf_drv_gpiote_evt_handler_t evt_handler);
/**
* @brief Function for uninitializing a GPIOTE input pin.
* @details The driver frees the GPIOTE channel if the input pin was using one.
*
* @param[in] pin Pin.
*/
void nrf_drv_gpiote_in_uninit(nrf_drv_gpiote_pin_t pin);
/**
* @brief Function for enabling sensing of a GPIOTE input pin.
*
* @details If the input pin is configured as high-accuracy pin, the function
* enables an IN_EVENT. Otherwise, the function enables the GPIO sense mechanism.
* Note that a PORT event is shared between multiple pins, therefore the
* interrupt is always enabled.
*
* @param[in] pin Pin.
* @param[in] int_enable True to enable the interrupt. Always valid for a high-accuracy pin.
*/
void nrf_drv_gpiote_in_event_enable(nrf_drv_gpiote_pin_t pin, bool int_enable);
/**
* @brief Function for disabling a GPIOTE input pin.
*
* @param[in] pin Pin.
*/
void nrf_drv_gpiote_in_event_disable(nrf_drv_gpiote_pin_t pin);
/**
* @brief Function for checking if a GPIOTE input pin is set.
*
* @param[in] pin Pin.
* @retval true If the input pin is set.
* @retval false If the input pin is not set.
*/
bool nrf_drv_gpiote_in_is_set(nrf_drv_gpiote_pin_t pin);
/**
* @brief Function for getting the address of a GPIOTE input pin event.
* @details If the pin is configured to use low-accuracy mode, the address of the PORT event is returned.
*
* @param[in] pin Pin.
*/
uint32_t nrf_drv_gpiote_in_event_addr_get(nrf_drv_gpiote_pin_t pin);
/**
* @brief Function for forcing a specific state on the pin configured as task.
*
* @param[in] pin Pin.
* @param[in] state Pin state.
*/
void nrf_drv_gpiote_out_task_force(nrf_drv_gpiote_pin_t pin, uint8_t state);
/**
* @brief Function for triggering the task OUT manually.
*
* @param[in] pin Pin.
*/
void nrf_drv_gpiote_out_task_trigger(nrf_drv_gpiote_pin_t pin);
#ifdef NRF52_SERIES
/**
* @brief Function for triggering the task SET manually.
*
* @param[in] pin Pin.
*/
void nrf_drv_gpiote_set_task_trigger(nrf_drv_gpiote_pin_t pin);
/**
* @brief Function for triggering the task CLR manually.
*
* @param[in] pin Pin.
*/
void nrf_drv_gpiote_clr_task_trigger(nrf_drv_gpiote_pin_t pin);
#endif
/**
*@}
**/
#ifdef __cplusplus
}
#endif
#endif //NRF_DRV_GPIOTE__