| /** |
| * 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_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" : \ |
| "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 HW_TIMEOUT 10000 |
| |
| // 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; |
| uint8_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; |
| } 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->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(p_instance->p_twi->PSELSCL); |
| nrf_gpio_cfg_default(p_instance->p_twi->PSELSDA); |
| } |
| |
| 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, |
| uint8_t const * p_data, |
| uint8_t length, |
| uint8_t * p_bytes_transferred, |
| bool no_stop) |
| { |
| if (*p_bytes_transferred < length) |
| { |
| nrf_twi_txd_set(p_twi, p_data[*p_bytes_transferred]); |
| ++(*p_bytes_transferred); |
| } |
| else |
| { |
| if (no_stop) |
| { |
| nrf_twi_task_trigger(p_twi, NRF_TWI_TASK_SUSPEND); |
| return false; |
| } |
| else |
| { |
| nrf_twi_task_trigger(p_twi, NRF_TWI_TASK_STOP); |
| } |
| } |
| return true; |
| } |
| |
| static void twi_receive_byte(NRF_TWI_Type * p_twi, |
| uint8_t * p_data, |
| uint8_t length, |
| uint8_t * p_bytes_transferred) |
| { |
| if (*p_bytes_transferred < length) |
| { |
| p_data[*p_bytes_transferred] = nrf_twi_rxd_get(p_twi); |
| |
| ++(*p_bytes_transferred); |
| |
| if (*p_bytes_transferred == length - 1) |
| { |
| nrf_twi_shorts_set(p_twi, NRF_TWI_SHORT_BB_STOP_MASK); |
| } |
| else if (*p_bytes_transferred == length) |
| { |
| return; |
| } |
| |
| nrf_twi_task_trigger(p_twi, NRF_TWI_TASK_RESUME); |
| } |
| } |
| |
| static bool twi_transfer(NRF_TWI_Type * p_twi, |
| bool * p_error, |
| uint8_t * p_bytes_transferred, |
| uint8_t * p_data, |
| uint8_t length, |
| bool no_stop) |
| { |
| bool do_stop_check = ((*p_error) || ((*p_bytes_transferred) == length)); |
| |
| if (*p_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_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_error = true; |
| } |
| else |
| { |
| if (!twi_send_byte(p_twi, p_data, length, p_bytes_transferred, no_stop)) |
| { |
| 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_error = true; |
| } |
| else |
| { |
| twi_receive_byte(p_twi, p_data, length, p_bytes_transferred); |
| } |
| } |
| } |
| |
| if (do_stop_check && 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)); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| static nrfx_err_t twi_tx_start_transfer(twi_control_block_t * p_cb, |
| NRF_TWI_Type * p_twi, |
| uint8_t const * p_data, |
| uint8_t length, |
| bool no_stop) |
| { |
| 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); |
| nrf_twi_task_trigger(p_twi, NRF_TWI_TASK_STARTTX); |
| |
| (void)twi_send_byte(p_twi, p_data, length, &p_cb->bytes_transferred, no_stop); |
| |
| 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->error, |
| &p_cb->bytes_transferred, |
| (uint8_t *)p_data, |
| length, |
| no_stop)) |
| { |
| 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(twi_control_block_t * p_cb, |
| NRF_TWI_Type * p_twi, |
| uint8_t const * p_data, |
| uint8_t length) |
| { |
| 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 (length == 1) |
| { |
| 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); |
| 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->error, |
| &p_cb->bytes_transferred, |
| (uint8_t*)p_data, |
| length, |
| false)) |
| { |
| 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_INLINE nrfx_err_t twi_xfer(twi_control_block_t * p_cb, |
| NRF_TWI_Type * p_twi, |
| nrfx_twi_xfer_desc_t const * p_xfer_desc, |
| uint32_t flags) |
| { |
| |
| nrfx_err_t err_code = NRFX_SUCCESS; |
| |
| /* 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 = (NRFX_TWI_FLAG_NO_XFER_EVT_HANDLER & 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_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_cb, |
| p_twi, |
| p_xfer_desc->p_primary_buf, |
| p_xfer_desc->primary_length, |
| p_cb->curr_no_stop); |
| } |
| else |
| { |
| p_cb->curr_no_stop = false; |
| |
| err_code = twi_rx_start_transfer(p_cb, |
| p_twi, |
| p_xfer_desc->p_primary_buf, |
| p_xfer_desc->primary_length); |
| } |
| 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(p_cb, (NRF_TWI_Type *)p_instance->p_twi, 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->error, |
| &p_cb->bytes_transferred, |
| p_cb->p_curr_buf, |
| p_cb->curr_length, |
| p_cb->curr_no_stop )) |
| { |
| 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_no_stop = (p_cb->flags & NRFX_TWI_FLAG_TX_NO_STOP); |
| |
| if (p_cb->xfer_desc.type == NRFX_TWI_XFER_TXTX) |
| { |
| (void)twi_tx_start_transfer(p_cb, |
| p_twi, |
| p_cb->p_curr_buf, |
| p_cb->curr_length, |
| p_cb->curr_no_stop); |
| } |
| else |
| { |
| (void)twi_rx_start_transfer(p_cb, p_twi, p_cb->p_curr_buf, p_cb->curr_length); |
| } |
| } |
| 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 |
| { |
| event.type = NRFX_TWI_EVT_DONE; |
| NRFX_LOG_DEBUG("Event: %s.", EVT_TO_STR(NRFX_TWI_EVT_DONE)); |
| } |
| |
| p_cb->busy = false; |
| |
| if (!(NRFX_TWI_FLAG_NO_XFER_EVT_HANDLER & 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) |