| /** |
| * 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) |