mirror of
https://github.com/openmv/openmv.git
synced 2025-11-04 14:49:50 +08:00
710 lines
23 KiB
C
710 lines
23 KiB
C
/*
|
|
* Copyright (c) 2015 - 2019, 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_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" : \
|
|
"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 10000
|
|
|
|
/* 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;
|
|
}
|
|
|
|
|
|
|
|
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]);
|
|
++(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 do_stop_check = ((p_cb->error) || ((p_cb->bytes_transferred) == p_cb->curr_length));
|
|
|
|
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);
|
|
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 (do_stop_check && nrf_twi_event_check(p_twi, NRF_TWI_EVENT_STOPPED))
|
|
{
|
|
nrf_twi_event_clear(p_twi, NRF_TWI_EVENT_STOPPED);
|
|
p_cb->prev_suspend = TWI_NO_SUSPEND;
|
|
NRFX_LOG_DEBUG("TWI: Event: %s.", EVT_TO_STR_TWI(NRF_TWI_EVENT_STOPPED));
|
|
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);
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
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_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;
|
|
}
|
|
|
|
size_t nrfx_twi_data_count_get(nrfx_twi_t 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 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_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->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)
|