Пример #1
0
int i2c_byte_read(i2c_t *obj, int last)
{
    NRF_TWI_Type *twi = m_twi_instances[TWI_IDX(obj)];
    uint32_t t0;

    if (last) {
        nrf_twi_shorts_set(twi, NRF_TWI_SHORT_BB_STOP_MASK);
    }
    nrf_twi_task_trigger(twi, NRF_TWI_TASK_RESUME);

    t0 = ticker_read(get_us_ticker_data());

    do {
        if (nrf_twi_event_check(twi, NRF_TWI_EVENT_RXDREADY)) {
            nrf_twi_event_clear(twi, NRF_TWI_EVENT_RXDREADY);
            return nrf_twi_rxd_get(twi);
        }
        if (nrf_twi_event_check(twi, NRF_TWI_EVENT_ERROR)) {
            nrf_twi_event_clear(twi, NRF_TWI_EVENT_ERROR);
            return I2C_ERROR_NO_SLAVE;
        }
    } while (((uint32_t)ticker_read(get_us_ticker_data()) - t0) < I2C_TIMEOUT_VALUE_US);

    return I2C_ERROR_BUS_BUSY;
}
Пример #2
0
/**@brief Generic function for handling TWI interrupt
 *
 * @param[in]  p_reg         Pointer to instance register structure.
 * @param[in]  instance_id   Index of instance.
 */
__STATIC_INLINE void nrf_drv_twi_int_handler(NRF_TWI_Type * p_reg, uint32_t instance_id)
{
    volatile transfer_t * p_transfer = &(m_cb[instance_id].transfer);
    sm_evt_t sm_event;

    bool error_occured   = nrf_twi_event_check(p_reg, NRF_TWI_EVENTS_ERROR);
    bool end_evt_occured = nrf_twi_event_check(p_reg, p_transfer->end_event);

    nrf_twi_event_clear(p_reg, NRF_TWI_EVENTS_ERROR);
    nrf_twi_event_clear(p_reg, NRF_TWI_EVENTS_TXDSENT);
    nrf_twi_event_clear(p_reg, NRF_TWI_EVENTS_RXDREADY);
    nrf_twi_event_clear(p_reg, NRF_TWI_EVENTS_STOPPED);

    if (error_occured || end_evt_occured)
    {
        if (error_occured)
        {
            sm_event = ON_ERROR;
        }
        else
        {
            sm_event = p_transfer->is_tx ? TX_DONE : RX_DONE;
        }
        state_machine(m_instances[instance_id], sm_event);

        if (p_transfer->error_condition)
        {
            p_transfer->transfer_in_progress = false;
            nrf_drv_twi_evt_t evt =
            {
                .type               = NRF_DRV_TWI_ERROR,
                .p_data    = p_transfer->p_data,
                .length    = p_transfer->count,
                // Driver uses shortcuts, so NRF_TWI_ERROR_OVERRUN_NACK will not take place.
                .error_src = (nrf_twi_error_source_get(p_reg) &
                             NRF_TWI_ERROR_ADDRESS_NACK) ? NRF_TWI_ERROR_ADDRESS_NACK
                             : NRF_TWI_ERROR_DATA_NACK,
            };
            m_handlers[instance_id](&evt);
        }
        else if (p_transfer->count >= p_transfer->length)
        {
            p_transfer->transfer_in_progress = false;
            nrf_drv_twi_evt_t evt =
            {
                .type   = p_transfer->is_tx ? NRF_DRV_TWI_TX_DONE : NRF_DRV_TWI_RX_DONE,
                .p_data = p_transfer->p_data,
                .length = p_transfer->count,
            };
            m_handlers[instance_id](&evt);
        }
    }
Пример #3
0
/**
 * @brief Function for blocking the module until desired event occurs.
 *
 * @param[in] p_instance      TWI.
 *
 * @return    False if any error has occurred.
 */
static bool twi_action_wait(nrf_drv_twi_t const * const p_instance)
{
    bool     error;
    bool     done;
    uint32_t timeout = 0;
    volatile transfer_t * p_transfer = &(m_cb[p_instance->instance_id].transfer);

    do
    {
        done  = nrf_twi_event_check(p_instance->p_reg, p_transfer->end_event);
        error = nrf_twi_event_check(p_instance->p_reg, NRF_TWI_EVENTS_ERROR);
        error |= (++timeout < BUSY_LOOP_TIMEOUT) ? false : true;
    } while (!(error | done));
    return !error;
}
Пример #4
0
static uint8_t twi_byte_write(NRF_TWI_Type *twi, uint8_t data)
{
    uint32_t t0;

    nrf_twi_event_clear(twi, NRF_TWI_EVENT_TXDSENT);
    nrf_twi_event_clear(twi, NRF_TWI_EVENT_ERROR);

    nrf_twi_txd_set(twi, data);

    t0 = ticker_read(get_us_ticker_data());

    do {
        if (nrf_twi_event_check(twi, NRF_TWI_EVENT_TXDSENT)) {
            nrf_twi_event_clear(twi, NRF_TWI_EVENT_TXDSENT);
            return 1; // ACK received
        }
        if (nrf_twi_event_check(twi, NRF_TWI_EVENT_ERROR)) {
            nrf_twi_event_clear(twi, NRF_TWI_EVENT_ERROR);
            return 0; // some error occurred
        }
    } while (((uint32_t)ticker_read(get_us_ticker_data()) - t0) < I2C_TIMEOUT_VALUE_US);

    return 2; // timeout;
}
Пример #5
0
int i2c_stop(i2c_t *obj)
{
    NRF_TWI_Type *twi = m_twi_instances[TWI_IDX(obj)];
    uint32_t t0;

    // The current transfer may be suspended (if it is RX), so it must be
    // resumed before the STOP task is triggered.
    nrf_twi_task_trigger(twi, NRF_TWI_TASK_RESUME);
    nrf_twi_task_trigger(twi, NRF_TWI_TASK_STOP);

    t0 = ticker_read(get_us_ticker_data());

    do {
        if (nrf_twi_event_check(twi, NRF_TWI_EVENT_STOPPED)) {
            return 0;
        }
    } while (((uint32_t)ticker_read(get_us_ticker_data()) - t0) < I2C_TIMEOUT_VALUE_US);

    return 1;
}
Пример #6
0
int i2c_write(i2c_t *obj, int address, const char *data, int length, int stop)
{
    twi_info_t *twi_info = TWI_INFO(obj);
    bool timeout = false;
    uint32_t t0, t1;

#if DEVICE_I2C_ASYNCH
    if (twi_info->active) {
        return I2C_ERROR_BUS_BUSY;
    }
#endif
    twi_info->start_twi = false;

    NRF_TWI_Type *twi = m_twi_instances[TWI_IDX(obj)];
    start_twi_write(twi, address);

    // Special case - transaction with no data.
    // It can be used to check if a slave acknowledges the address.
    if (length == 0) {
        nrf_twi_event_t event;
        if (stop) {
            event = NRF_TWI_EVENT_STOPPED;
            nrf_twi_task_trigger(twi, NRF_TWI_TASK_STOP);
        } else {
            event = NRF_TWI_EVENT_SUSPENDED;
            nrf_twi_event_clear(twi, event);
            nrf_twi_task_trigger(twi, NRF_TWI_TASK_SUSPEND);
        }

        t0 = ticker_read(get_us_ticker_data());

        do {
            if (nrf_twi_event_check(twi, event)) {
                break;
            }
            t1 = ticker_read(get_us_ticker_data());
            timeout = (t1 - t0) >= I2C_TIMEOUT_VALUE_US;
        } while (!timeout);

        uint32_t errorsrc = nrf_twi_errorsrc_get_and_clear(twi);
        if (errorsrc & NRF_TWI_ERROR_ADDRESS_NACK) {
            if (!stop) {
                i2c_stop(obj);
            }
            return I2C_ERROR_NO_SLAVE;
        }

        return (timeout ? I2C_ERROR_BUS_BUSY : 0);
    }

    int result = length;
    do {
        uint8_t byte_write_result = twi_byte_write(twi, (uint8_t)*data++);
        if (byte_write_result != 1) {
            if (byte_write_result == 0) {
                // Check what kind of error has been signaled by TWI.
                uint32_t errorsrc = nrf_twi_errorsrc_get_and_clear(twi);
                if (errorsrc & NRF_TWI_ERROR_ADDRESS_NACK) {
                    result = I2C_ERROR_NO_SLAVE;
                } else {
                    // Some other error - return the number of bytes that
                    // have been sent successfully.
                    result -= length;
                }
            } else {
                result = I2C_ERROR_BUS_BUSY;
            }
            // Force STOP condition.
            stop = 1;
            break;
        }
        --length;
    } while (length > 0);

    if (stop) {
        (void)i2c_stop(obj);
    }

    return result;
}
Пример #7
0
static void twi_irq_handler(uint8_t instance_idx)
{
    twi_info_t *twi_info = &m_twi_info[instance_idx];

    NRF_TWI_Type *twi = m_twi_instances[instance_idx];
    if (nrf_twi_event_check(twi, NRF_TWI_EVENT_ERROR)) {
        nrf_twi_event_clear(twi, NRF_TWI_EVENT_ERROR);

        // In case of an error, force STOP.
        // The current transfer may be suspended (if it is RX), so it must be
        // resumed before the STOP task is triggered.
        nrf_twi_task_trigger(twi, NRF_TWI_TASK_RESUME);
        nrf_twi_task_trigger(twi, NRF_TWI_TASK_STOP);

        uint32_t errorsrc = nrf_twi_errorsrc_get_and_clear(twi);
        twi_info->events |= I2C_EVENT_ERROR;
        if (errorsrc & NRF_TWI_ERROR_ADDRESS_NACK) {
            twi_info->events |= I2C_EVENT_ERROR_NO_SLAVE;
        }
        if (errorsrc & NRF_TWI_ERROR_DATA_NACK) {
            twi_info->events |= I2C_EVENT_TRANSFER_EARLY_NACK;
        }
    }

    bool finished = false;

    if (nrf_twi_event_check(twi, NRF_TWI_EVENT_TXDSENT)) {
        nrf_twi_event_clear(twi, NRF_TWI_EVENT_TXDSENT);

        MBED_ASSERT(twi_info->tx_length > 0);
        --(twi_info->tx_length);
        // Send next byte if there is still something to be sent.
        if (twi_info->tx_length > 0) {
            nrf_twi_txd_set(twi, *(twi_info->tx));
            ++(twi_info->tx);
        // It TX is done, start RX if requested.
        } else if (twi_info->rx_length > 0) {
            start_asynch_rx(twi_info, twi);
        // If there is nothing more to do, finalize the transfer.
        } else {
            if (twi_info->stop) {
                nrf_twi_task_trigger(twi, NRF_TWI_TASK_STOP);
            } else {
                nrf_twi_task_trigger(twi, NRF_TWI_TASK_SUSPEND);
                finished = true;
            }
            twi_info->events |= I2C_EVENT_TRANSFER_COMPLETE;
        }
    }

    if (nrf_twi_event_check(twi, NRF_TWI_EVENT_RXDREADY)) {
        nrf_twi_event_clear(twi, NRF_TWI_EVENT_RXDREADY);

        MBED_ASSERT(twi_info->rx_length > 0);
        *(twi_info->rx) = nrf_twi_rxd_get(twi);
        ++(twi_info->rx);
        --(twi_info->rx_length);

        if (twi_info->rx_length > 0) {
            // If more bytes should be received, resume the transfer
            // (in case the stop condition should be generated after the next
            // byte, change the shortcuts configuration first).
            if (twi_info->rx_length == 1 && twi_info->stop) {
                nrf_twi_shorts_set(twi, NRF_TWI_SHORT_BB_STOP_MASK);
            }
            nrf_twi_task_trigger(twi, NRF_TWI_TASK_RESUME);
        } else {
            // If all requested bytes were received, finalize the transfer.
            finished = true;
            twi_info->events |= I2C_EVENT_TRANSFER_COMPLETE;
        }
    }

    if (finished ||
        nrf_twi_event_check(twi, NRF_TWI_EVENT_STOPPED) ||
        (nrf_twi_int_enable_check(twi, NRF_TWI_INT_SUSPENDED_MASK) &&
         nrf_twi_event_check(twi, NRF_TWI_EVENT_SUSPENDED))) {
        // There is no need to clear the STOPPED and SUSPENDED events here,
        // they will no longer generate the interrupt - see below.

        nrf_twi_shorts_set(twi, 0);
        // Disable all interrupt sources.
        nrf_twi_int_disable(twi, UINT32_MAX);
        twi_info->active = false;

        if (twi_info->handler) {
            twi_info->handler();
        }
    }
}