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