/* * Copyright (c) 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 or object form under this license must not be reverse * engineered, decompiled, modified and/or disassembled. * * 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. * */ #if (defined(DEVICE_I2C) && defined(DEVICE_LPTICKER)) /* I2C * * This HAL implementation uses the nrf_drv_twi.h API primarily but switches to TWI for the * low-level HAL functions. These calls can't be implemented with the TWIM due to the * different API. * * Known limitations: * * The TWI/TWIM only supports 7-bit addresses. * * The TWI API doesn't allow reading 1 byte. At least 2 bytes will be read. */ #include "i2c_api.h" #include "lp_ticker_api.h" #include "object_owners.h" #include "pinmap_ex.h" #include "nrf_drv_twi.h" #include "nrf_drv_common.h" #include "app_util_platform.h" #if 0 #define DEBUG_PRINTF(...) printf(__VA_ARGS__) #else #define DEBUG_PRINTF(...) #endif #define DEFAULT_TIMEOUT_US (1000) // timeout for waiting for address NACK #define MAXIMUM_TIMEOUT_US (10000) // timeout for waiting for RX #define I2C_READ_BIT 0x01 // read bit /* Keep track of what mode the peripheral is in. On NRF52, Driver mode can use TWIM. */ typedef enum { NORDIC_I2C_MODE_NONE, NORDIC_I2C_MODE_TWI, NORDIC_I2C_MODE_DRIVER } nordic_nrf5_mode_t; /* In simple mode, the Start signal is sent on the first write call due to hardware limitations. */ typedef enum { NORDIC_TWI_STATE_IDLE, NORDIC_TWI_STATE_START, NORDIC_TWI_STATE_BUSY } nordic_nrf5_twi_state_t; /* Forward declaration. These functions are implemented in the driver but not * set up in the NVIC due to it being relocated. */ void SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0_IRQHandler(void); void SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1_IRQHandler(void); /** Initialize the I2C peripheral. It sets the default parameters for I2C * peripheral, and configures its specifieds pins. * * @param obj The I2C object * @param sda The sda pin * @param scl The scl pin */ void i2c_init(i2c_t *obj, PinName sda, PinName scl) { DEBUG_PRINTF("i2c_init: %p %d %d\r\n", obj, sda, scl); #if DEVICE_I2C_ASYNCH struct i2c_s *config = &obj->i2c; #else struct i2c_s *config = obj; #endif /* Get instance from pin configuration. */ int instance = pin_instance_i2c(sda, scl); MBED_ASSERT(instance < ENABLED_TWI_COUNT); /* Initialize i2c_t object */ config->instance = instance; config->sda = sda; config->scl = scl; config->frequency = NRF_TWI_FREQ_100K; config->state = NORDIC_TWI_STATE_IDLE; config->mode = NORDIC_I2C_MODE_NONE; #if DEVICE_I2C_ASYNCH config->handler = 0; config->event = 0; config->mask = 0; #endif /* Force reconfiguration */ config->update = true; static bool first_init = true; if (first_init) { first_init = false; /* Register interrupt handlers in driver with the NVIC. */ NVIC_SetVector(SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0_IRQn, (uint32_t) SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0_IRQHandler); NVIC_SetVector(SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1_IRQn, (uint32_t) SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1_IRQHandler); } } /** Configure the I2C frequency * * @param obj The I2C object * @param hz Frequency in Hz */ void i2c_frequency(i2c_t *obj, int hz) { DEBUG_PRINTF("i2c_frequency: %p %d\r\n", obj, hz); #if DEVICE_I2C_ASYNCH struct i2c_s *config = &obj->i2c; #else struct i2c_s *config = obj; #endif /* Round down to nearest valid frequency. */ nrf_twi_frequency_t new_frequency; if (hz < 250000) { new_frequency = NRF_TWI_FREQ_100K; } else if (hz < 400000) { new_frequency = NRF_TWI_FREQ_250K; } else { new_frequency = NRF_TWI_FREQ_400K; } /* Only store frequency in object. Configuration happens at the beginning of each transaction. */ config->frequency = new_frequency; config->update = true; } /*** * _____ _ _ _________ _______ * / ____(_) | | |__ __\ \ / /_ _| * | (___ _ _ __ ___ _ __ | | ___ | | \ \ /\ / / | | * \___ \| | '_ ` _ \| '_ \| |/ _ \ | | \ \/ \/ / | | * ____) | | | | | | | |_) | | __/ | | \ /\ / _| |_ * |_____/|_|_| |_| |_| .__/|_|\___| |_| \/ \/ |_____| * | | * |_| */ /*****************************************************************************/ /* Simple API implementation using TWI */ /*****************************************************************************/ /* Global array for easy register selection for each instance. */ static NRF_TWI_Type * nordic_nrf5_twi_register[2] = { NRF_TWI0, NRF_TWI1 }; /** * @brief Reconfigure TWI register. * * If the peripheral is enabled, it will be disabled first. All * registers are cleared to their default values unless replaced * by new configuration. * * If the object is the owner, the mode hasn't changed, and the * force change flag is false, all settings are kept unchanged. * * @param obj The object */ void i2c_configure_twi_instance(i2c_t *obj) { #if DEVICE_I2C_ASYNCH struct i2c_s *config = &obj->i2c; #else struct i2c_s *config = obj; #endif int instance = config->instance; /* Get pointer to object of the current owner of the peripheral. */ void *current_owner = object_owner_spi2c_get(instance); /* Check if reconfiguration is actually necessary. */ if ((obj != current_owner) || (config->mode != NORDIC_I2C_MODE_TWI) || config->update) { DEBUG_PRINTF("i2c_configure_twi_instance: %p %p\r\n", obj, current_owner); /* Claim ownership of peripheral. */ object_owner_spi2c_set(instance, obj); /* Set current mode. */ config->mode = NORDIC_I2C_MODE_TWI; /* Disable peripheral if it is currently enabled. */ if (nordic_nrf5_twi_register[instance]->ENABLE) { nrf_twi_disable(nordic_nrf5_twi_register[instance]); } /* Force resource release. This is necessary because mbed drivers don't * deinitialize on object destruction. */ nrf_drv_common_irq_disable(nrf_drv_get_IRQn((void *) nordic_nrf5_twi_register[instance])); nrf_drv_common_per_res_release(nordic_nrf5_twi_register[instance]); /* Reset shorts register. */ nrf_twi_shorts_set(nordic_nrf5_twi_register[instance], 0); /* Disable all TWI interrupts. */ nrf_twi_int_disable(nordic_nrf5_twi_register[instance], NRF_TWI_INT_STOPPED_MASK | NRF_TWI_INT_RXDREADY_MASK | NRF_TWI_INT_TXDSENT_MASK | NRF_TWI_INT_ERROR_MASK | NRF_TWI_INT_BB_MASK | NRF_TWI_INT_SUSPENDED_MASK); /* Clear error register. */ nrf_twi_errorsrc_get_and_clear(nordic_nrf5_twi_register[instance]); /* Clear all previous events. */ nrf_twi_event_clear(nordic_nrf5_twi_register[instance], NRF_TWI_EVENT_STOPPED | NRF_TWI_EVENT_RXDREADY | NRF_TWI_EVENT_TXDSENT | NRF_TWI_EVENT_ERROR | NRF_TWI_EVENT_BB | NRF_TWI_EVENT_SUSPENDED); /* Configure SDA and SCL pins. */ nrf_twi_pins_set(nordic_nrf5_twi_register[instance], config->scl, config->sda); /* Set frequency. */ nrf_twi_frequency_set(nordic_nrf5_twi_register[instance], config->frequency); /* Enable TWI peripheral with new settings. */ nrf_twi_enable(nordic_nrf5_twi_register[instance]); } } /** Send START command * * @param obj The I2C object */ int i2c_start(i2c_t *obj) { DEBUG_PRINTF("i2c_start: %p\r\n", obj); #if DEVICE_I2C_ASYNCH struct i2c_s *config = &obj->i2c; #else struct i2c_s *config = obj; #endif /* Change state but defer actual signaling until the first byte (the address) is transmitted. This is due to hardware limitations. */ config->state = NORDIC_TWI_STATE_START; return 0; } /** Write one byte * * @param obj The I2C object * @param data Byte to be written * @return 0 if NAK was received, 1 if ACK was received, 2 for timeout. */ int i2c_byte_write(i2c_t *obj, int data) { DEBUG_PRINTF("i2c_byte_write: %p %d\r\n", obj, data); #if DEVICE_I2C_ASYNCH struct i2c_s *config = &obj->i2c; #else struct i2c_s *config = obj; #endif int instance = config->instance; int result = 1; // default to ACK /* Check if this is the first byte to be transferred. If it is, then send start signal and address. */ if (config->state == NORDIC_TWI_STATE_START) { config->state = NORDIC_TWI_STATE_BUSY; /* Beginning of new transaction, configure peripheral if necessary. */ i2c_configure_twi_instance(obj); /* Set I2C device address. NOTE: due to hardware limitations only 7-bit addresses are supported. */ nrf_twi_address_set(nordic_nrf5_twi_register[instance], data >> 1); /* If read bit is set, trigger read task otherwise trigger write task. */ if (data & I2C_READ_BIT) { /* For timing reasons, reading bytes requires shorts to suspend peripheral after each byte. */ nrf_twi_shorts_set(nordic_nrf5_twi_register[instance], NRF_TWI_SHORT_BB_SUSPEND_MASK); nrf_twi_task_trigger(nordic_nrf5_twi_register[instance], NRF_TWI_TASK_STARTRX); } else { /* Reset shorts register. */ nrf_twi_shorts_set(nordic_nrf5_twi_register[instance], 0); nrf_twi_task_trigger(nordic_nrf5_twi_register[instance], NRF_TWI_TASK_STARTTX); } /* Setup stop watch for timeout. */ uint32_t start_us = lp_ticker_read(); uint32_t now_us = start_us; /* Block until timeout or an address error has been detected. */ while (((now_us - start_us) < DEFAULT_TIMEOUT_US) && !(nrf_twi_event_check(nordic_nrf5_twi_register[instance], NRF_TWI_EVENT_ERROR))) { now_us = lp_ticker_read(); } /* Check error register and update return value if an address NACK was detected. */ uint32_t error = nrf_twi_errorsrc_get_and_clear(nordic_nrf5_twi_register[instance]); if (error & NRF_TWI_ERROR_ADDRESS_NACK) { result = 0; // set NACK } } else { /* Normal write. Send next byte after clearing event flag. */ nrf_twi_event_clear(nordic_nrf5_twi_register[instance], NRF_TWI_EVENT_TXDSENT); nrf_twi_txd_set(nordic_nrf5_twi_register[instance], data); /* Setup stop watch for timeout. */ uint32_t start_us = lp_ticker_read(); uint32_t now_us = start_us; /* Block until timeout or the byte has been sent. */ while (((now_us - start_us) < MAXIMUM_TIMEOUT_US) && !(nrf_twi_event_check(nordic_nrf5_twi_register[instance], NRF_TWI_EVENT_TXDSENT))) { now_us = lp_ticker_read(); } /* Check the error code to see if the byte was acknowledged. */ uint32_t error = nrf_twi_errorsrc_get_and_clear(nordic_nrf5_twi_register[instance]); if (error & NRF_TWI_ERROR_DATA_NACK) { result = 0; // set NACK } else if (now_us - start_us >= MAXIMUM_TIMEOUT_US) { result = 2; // set timeout } } return result; } /** Read one byte * * @param obj The I2C object * @param last Acknoledge * @return The read byte */ int i2c_byte_read(i2c_t *obj, int last) { DEBUG_PRINTF("i2c_byte_read: %p %d\r\n", obj, last); #if DEVICE_I2C_ASYNCH struct i2c_s *config = &obj->i2c; #else struct i2c_s *config = obj; #endif int instance = config->instance; int retval = I2C_ERROR_NO_SLAVE; uint32_t start_us = 0; uint32_t now_us = 0; /* Due to hardware limitations, the stop condition must triggered through a short before * reading the last byte. */ if (last) { nrf_twi_shorts_set(nordic_nrf5_twi_register[instance], NRF_TWI_SHORT_BB_STOP_MASK); /* Transaction will be complete after this call, reset state. */ config->state = NORDIC_TWI_STATE_IDLE; } /* Due to the way events are generated, if a byte is available it should be read directly * but without resuming reading. Otherwise the TWI will read one byte too many. */ if (nrf_twi_event_check(nordic_nrf5_twi_register[instance], NRF_TWI_EVENT_RXDREADY)) { retval = nrf_twi_rxd_get(nordic_nrf5_twi_register[instance]); nrf_twi_event_clear(nordic_nrf5_twi_register[instance], NRF_TWI_EVENT_RXDREADY); } else { /* No data available, resume reception. */ nrf_twi_task_trigger(nordic_nrf5_twi_register[instance], NRF_TWI_TASK_RESUME); /* Setup timeout */ start_us = lp_ticker_read(); now_us = start_us; /* Block until timeout or data ready event has been signaled. */ while (((now_us - start_us) < MAXIMUM_TIMEOUT_US) && !(nrf_twi_event_check(nordic_nrf5_twi_register[instance], NRF_TWI_EVENT_RXDREADY))) { now_us = lp_ticker_read(); } /* Retrieve data from buffer. */ if ((now_us - start_us) < MAXIMUM_TIMEOUT_US) { retval = nrf_twi_rxd_get(nordic_nrf5_twi_register[instance]); nrf_twi_event_clear(nordic_nrf5_twi_register[instance], NRF_TWI_EVENT_RXDREADY); } } return retval; } /** Send STOP command * * @param obj The I2C object */ int i2c_stop(i2c_t *obj) { DEBUG_PRINTF("i2c_stop: %p\r\n", obj); #if DEVICE_I2C_ASYNCH struct i2c_s *config = &obj->i2c; #else struct i2c_s *config = obj; #endif int instance = config->instance; /* Set explicit stop signal. */ nrf_twi_event_clear(nordic_nrf5_twi_register[instance], NRF_TWI_EVENT_STOPPED); nrf_twi_task_trigger(nordic_nrf5_twi_register[instance], NRF_TWI_TASK_STOP); /* Block until stop signal has been generated. */ uint32_t start_us = lp_ticker_read(); uint32_t now_us = start_us; while (((now_us - start_us) < MAXIMUM_TIMEOUT_US) && !(nrf_twi_event_check(nordic_nrf5_twi_register[instance], NRF_TWI_EVENT_STOPPED))) { now_us = lp_ticker_read(); } /* Reset state. */ config->state = NORDIC_TWI_STATE_IDLE; return 0; } /** Reset I2C peripheral. TODO: The action here. Most of the implementation sends stop() * * @param obj The I2C object */ void i2c_reset(i2c_t *obj) { DEBUG_PRINTF("i2c_reset: %p\r\n", obj); #if DEVICE_I2C_ASYNCH struct i2c_s *config = &obj->i2c; #else struct i2c_s *config = obj; #endif /* Force reconfiguration to reset peripheral completely. */ config->update = true; i2c_configure_twi_instance(obj); } /*** * _____ _ _________ _______ * | __ \ (_) |__ __\ \ / /_ _| * | | | |_ __ ___ _____ _ __ | | \ \ /\ / / | | * | | | | '__| \ \ / / _ \ '__| | | \ \/ \/ / | | * | |__| | | | |\ V / __/ | | | \ /\ / _| |_ * |_____/|_| |_| \_/ \___|_| |_| \/ \/ |_____| * * */ /* Global array holding driver configuration for easy access. */ static const nrf_drv_twi_t nordic_nrf5_instance[2] = { NRF_DRV_TWI_INSTANCE(0), NRF_DRV_TWI_INSTANCE(1) }; /* Forward declare interrupt handler. */ #if DEVICE_I2C_ASYNCH static void nordic_nrf5_twi_event_handler(nrf_drv_twi_evt_t const *p_event, void *p_context); #endif /** * @brief Reconfigure driver. * * If the peripheral is enabled, it will be disabled first. All * registers are cleared to their default values unless replaced * by new configuration. * * If the object is the owner, the mode hasn't changed, and the * force change flag is false, all settings are kept unchanged. * * @param obj The object * @param[in] force_change Set to true to force a reconfiguration. */ static void i2c_configure_driver_instance(i2c_t *obj) { #if DEVICE_I2C_ASYNCH struct i2c_s *config = &obj->i2c; #else struct i2c_s *config = obj; #endif int instance = config->instance; /* Get pointer to object of the current owner of the peripheral. */ void *current_owner = object_owner_spi2c_get(instance); /* Check if reconfiguration is actually necessary. */ if ((obj != current_owner) || (config->mode != NORDIC_I2C_MODE_DRIVER) || config->update) { DEBUG_PRINTF("i2c_configure_driver_instance: %p %p\r\n", obj, current_owner); /* Claim ownership of peripheral. */ object_owner_spi2c_set(instance, obj); /* Set current mode. */ config->mode = NORDIC_I2C_MODE_DRIVER; /* If the peripheral is already running, then disable it and use the driver API to uninitialize it.*/ if (nordic_nrf5_instance[instance].reg.p_twi->ENABLE) { nrf_drv_twi_disable(&nordic_nrf5_instance[instance]); nrf_drv_twi_uninit(&nordic_nrf5_instance[instance]); } /* Force resource release. This is necessary because mbed drivers don't * deinitialize on object destruction. */ nrf_drv_common_irq_disable(nrf_drv_get_IRQn((void *) nordic_nrf5_twi_register[instance])); nrf_drv_common_per_res_release(nordic_nrf5_twi_register[instance]); /* Configure driver with new settings. */ nrf_drv_twi_config_t twi_config = { .scl = config->scl, .sda = config->sda, .frequency = config->frequency, .interrupt_priority = APP_IRQ_PRIORITY_LOWEST, .clear_bus_init = false, .hold_bus_uninit = false }; #if DEVICE_I2C_ASYNCH /* Set callback handler in asynchronous mode. */ if (config->handler) { /* Initialze driver in non-blocking mode. */ nrf_drv_twi_init(&nordic_nrf5_instance[instance], &twi_config, nordic_nrf5_twi_event_handler, obj); } else { /* Initialze driver in blocking mode. */ nrf_drv_twi_init(&nordic_nrf5_instance[instance], &twi_config, NULL, NULL); } #else /* Initialze driver in blocking mode. */ nrf_drv_twi_init(&nordic_nrf5_instance[instance], &twi_config, NULL, NULL); #endif /* Enable peripheral. */ nrf_drv_twi_enable(&nordic_nrf5_instance[instance]); } } /** Blocking reading data * * @param obj The I2C object * @param address 7-bit address (last bit is 1) * @param data The buffer for receiving * @param length Number of bytes to read * @param stop Stop to be generated after the transfer is done * @return Number of read bytes */ int i2c_read(i2c_t *obj, int address, char *data, int length, int stop) { DEBUG_PRINTF("i2c_read: %p %d %p %d %d\r\n", obj, address, data, length, stop); #if DEVICE_I2C_ASYNCH struct i2c_s *config = &obj->i2c; #else struct i2c_s *config = obj; #endif int instance = config->instance; int result = I2C_ERROR_NO_SLAVE; /* Force peripheral configuration to avoid timing errors. */ config->update = true; i2c_configure_driver_instance(obj); /* Initialize transaction. */ ret_code_t retval = nrf_drv_twi_rx(&nordic_nrf5_instance[instance], address >> 1, (uint8_t *) data, length); /* Set return value on success. */ if (retval == NRF_SUCCESS) { result = length; } DEBUG_PRINTF("result: %lu %d\r\n", retval, result); return result; } /** Blocking sending data * * @param obj The I2C object * @param address 7-bit address (last bit is 0) * @param data The buffer for sending * @param length Number of bytes to write * @param stop Stop to be generated after the transfer is done * @return * zero or non-zero - Number of written bytes * negative - I2C_ERROR_XXX status */ int i2c_write(i2c_t *obj, int address, const char *data, int length, int stop) { DEBUG_PRINTF("i2c_write: %p %d %p %d %d\r\n", obj, address, data, length, stop); #if DEVICE_I2C_ASYNCH struct i2c_s *config = &obj->i2c; #else struct i2c_s *config = obj; #endif int instance = config->instance; int result = I2C_ERROR_NO_SLAVE; /* Force peripheral configuration to avoid timing errors. */ config->update = true; i2c_configure_driver_instance(obj); /* Initialize transaction. */ ret_code_t retval = nrf_drv_twi_tx(&nordic_nrf5_instance[instance], address >> 1, (const uint8_t *) data, length, !stop); /* Set return value on success. */ if (retval == NRF_SUCCESS) { result = length; } DEBUG_PRINTF("result: %lu %d\r\n", retval, result); return result; } #if DEVICE_I2C_ASYNCH /*** * _____ _____ * /\ /\ | __ \_ _| * / \ ___ _ _ _ __ ___ / \ | |__) || | * / /\ \ / __| | | | '_ \ / __| / /\ \ | ___/ | | * / ____ \\__ \ |_| | | | | (__ / ____ \| | _| |_ * /_/ \_\___/\__, |_| |_|\___| /_/ \_\_| |_____| * __/ | * |___/ */ /* Callback function for driver calls. This is called from ISR context. */ static void nordic_nrf5_twi_event_handler(nrf_drv_twi_evt_t const *p_event, void *p_context) { // Only safe to use with mbed-printf. //DEBUG_PRINTF("nordic_nrf5_twi_event_handler: %d %p\r\n", p_event->type, p_context); i2c_t *obj = (i2c_t *) p_context; struct i2c_s *config = &obj->i2c; /* Translate event type from NRF driver values to mbed HAL values. */ switch (p_event->type) { /* Transfer completed event. */ case NRF_DRV_TWI_EVT_DONE: config->event = I2C_EVENT_TRANSFER_COMPLETE; break; /* Error event: NACK received after sending the address. */ case NRF_DRV_TWI_EVT_ADDRESS_NACK: config->event = I2C_EVENT_ERROR_NO_SLAVE; break; /* Error event: NACK received after sending a data byte. */ case NRF_DRV_TWI_EVT_DATA_NACK: config->event = I2C_EVENT_TRANSFER_EARLY_NACK; break; default: config->event = I2C_EVENT_ERROR; break; } /* If event matches event mask and event handler is set, signal event. */ if ((config->event & config->mask) && config->handler) { /* Cast handler to function pointer. */ void (*callback)(void) = (void (*)(void)) config->handler; /* Reset handler and force reconfiguration. */ config->handler = 0; config->update = true; /* Signal callback handler. */ callback(); } } /** Start I2C asynchronous transfer * * @param obj The I2C object * @param tx The transmit buffer * @param tx_length The number of bytes to transmit * @param rx The receive buffer * @param rx_length The number of bytes to receive * @param address The address to be set - 7bit or 9bit * @param stop If true, stop will be generated after the transfer is done * @param handler The I2C IRQ handler to be set * @param event Event mask for the transfer. See \ref hal_I2CEvents * @param hint DMA hint usage */ void i2c_transfer_asynch(i2c_t *obj, const void *tx, size_t tx_length, void *rx, size_t rx_length, uint32_t address, uint32_t stop, uint32_t handler, uint32_t mask, DMAUsage hint) { DEBUG_PRINTF("i2c_transfer_asynch\r\n"); /* TWI only supports 7 bit addresses. */ MBED_ASSERT(address < 0xFF); struct i2c_s *config = &obj->i2c; int instance = config->instance; /* Save event handler and event mask in global variables so they can be called from interrupt handler. */ config->handler = handler; config->mask = mask; /* Clear event flag. */ config->event = 0; /* Configure peripheral. */ config->update = true; i2c_configure_driver_instance(obj); /* Configure TWI transfer. */ const nrf_drv_twi_xfer_desc_t twi_config = NRF_DRV_TWI_XFER_DESC_TXRX(address >> 1, (uint8_t*) tx, tx_length, rx, rx_length); uint32_t flags = (stop) ? 0 : NRF_DRV_TWI_FLAG_TX_NO_STOP; /* Initiate TWI transfer using NRF driver. */ ret_code_t result = nrf_drv_twi_xfer(&nordic_nrf5_instance[instance], &twi_config, flags); /* Signal error if event mask matches and event handler is set. */ if ((result != NRF_SUCCESS) && (mask & I2C_EVENT_ERROR) && handler) { /* Store event value so it can be read back. */ config->event = I2C_EVENT_ERROR; /* Cast handler to function pointer. */ void (*callback)(void) = (void (*)(void)) handler; /* Reset handler and force reconfiguration. */ config->handler = 0; config->update = true; /* Signal callback handler. */ callback(); } } /** The asynchronous IRQ handler * * @param obj The I2C object which holds the transfer information * @return Event flags if a transfer termination condition was met, otherwise return 0. */ uint32_t i2c_irq_handler_asynch(i2c_t *obj) { DEBUG_PRINTF("i2c_irq_handler_asynch\r\n"); /* Return latest event. */ return obj->i2c.event; } /** Attempts to determine if the I2C peripheral is already in use * * @param obj The I2C object * @return Non-zero if the I2C module is active or zero if it is not */ uint8_t i2c_active(i2c_t *obj) { DEBUG_PRINTF("i2c_active\r\n"); /* Query NRF driver if transaction is in progress. */ return nrf_drv_twi_is_busy(&nordic_nrf5_instance[obj->i2c.instance]); } /** Abort asynchronous transfer * * This function does not perform any check - that should happen in upper layers. * @param obj The I2C object */ void i2c_abort_asynch(i2c_t *obj) { DEBUG_PRINTF("i2c_abort_asynch\r\n"); /* Reconfiguration will disable and enable the TWI module. */ obj->i2c.update = true; i2c_configure_driver_instance(obj); } #endif // DEVICE_I2C_ASYNCH #if DEVICE_I2CSLAVE #warning DEVICE_I2CSLAVE /*** * _____ ___ _____ _____ _ * |_ _|__ \ / ____| / ____| | * | | ) | | | (___ | | __ ___ _____ * | | / /| | \___ \| |/ _` \ \ / / _ \ * _| |_ / /_| |____ ____) | | (_| |\ V / __/ * |_____|____|\_____| |_____/|_|\__,_| \_/ \___| * * */ /** Configure I2C as slave or master. * @param obj The I2C object * @param enable_slave Enable i2c hardware so you can receive events with ::i2c_slave_receive * @return non-zero if a value is available */ void i2c_slave_mode(i2c_t *obj, int enable_slave) { DEBUG_PRINTF("i2c_slave_mode\r\n"); } /** Check to see if the I2C slave has been addressed. * @param obj The I2C object * @return The status - 1 - read addresses, 2 - write to all slaves, * 3 write addressed, 0 - the slave has not been addressed */ int i2c_slave_receive(i2c_t *obj) { DEBUG_PRINTF("i2c_slave_receive\r\n"); return 0; } /** Configure I2C as slave or master. * @param obj The I2C object * @param data The buffer for receiving * @param length Number of bytes to read * @return non-zero if a value is available */ int i2c_slave_read(i2c_t *obj, char *data, int length) { DEBUG_PRINTF("i2c_slave_read\r\n"); return 0; } /** Configure I2C as slave or master. * @param obj The I2C object * @param data The buffer for sending * @param length Number of bytes to write * @return non-zero if a value is available */ int i2c_slave_write(i2c_t *obj, const char *data, int length) { DEBUG_PRINTF("i2c_slave_write\r\n"); return 0; } /** Configure I2C address. * @param obj The I2C object * @param idx Currently not used * @param address The address to be set * @param mask Currently not used */ void i2c_slave_address(i2c_t *obj, int idx, uint32_t address, uint32_t mask) { DEBUG_PRINTF("i2c_slave_address\r\n"); } #endif // DEVICE_I2CSLAVE #endif // DEVICE_I2C