/** * \internal * Acts on slave address response. Checks for errors concerning master->slave * handshake. * * \param[in,out] module Pointer to software module structure */ static void _i2c_master_async_address_response( struct i2c_master_module *const module) { /* Sanity check arguments. */ Assert(module); Assert(module->hw); SercomI2cm *const i2c_module = &(module->hw->I2CM); /* Check for error. Ignore bus-error; workaround for bus state stuck in * BUSY. */ if (i2c_module->INTFLAG.reg & SERCOM_I2CM_INTFLAG_MB) { /* Clear write interrupt flag */ i2c_module->INTFLAG.reg = SERCOM_I2CM_INTENCLR_MB; /* Check arbitration */ if (i2c_module->STATUS.reg & SERCOM_I2CM_STATUS_ARBLOST) { /* Return busy */ module->status = STATUS_ERR_PACKET_COLLISION; } } if (i2c_module->STATUS.reg & SERCOM_I2CM_STATUS_RXNACK) { /* Return bad address value */ module->status = STATUS_ERR_BAD_ADDRESS; module->buffer_remaining = 0; if (module->send_stop) { /* Send stop condition */ _i2c_master_wait_for_sync(module); i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3); } } module->buffer_length = module->buffer_remaining; /* Check for status OK. */ if (module->status == STATUS_BUSY) { /* Call function based on transfer direction. */ if (module->transfer_direction == I2C_TRANSFER_WRITE) { _i2c_master_write(module); } else { _i2c_master_read(module); } } }
/** * \brief Reads data packet from slave without sending a stop condition when done * * Reads a data packet from the specified slave address on the I<SUP>2</SUP>C * bus without sending a stop condition when done, thus retaining ownership of * the bus when done. To end the transaction, a * \ref i2c_master_read_packet_wait "read" or * \ref i2c_master_write_packet_wait "write" with stop condition must be * performed. * * \note This will stall the device from any other operation. For * interrupt-driven operation, see \ref i2c_master_read_packet_job. * * \param[in,out] module Pointer to software module struct * \param[in,out] packet Pointer to I<SUP>2</SUP>C packet to transfer * * \return Status of reading packet. * \retval STATUS_OK The packet was read successfully * \retval STATUS_ERR_TIMEOUT If no response was given within * specified timeout period * \retval STATUS_ERR_DENIED If error on bus * \retval STATUS_ERR_PACKET_COLLISION If arbitration is lost * \retval STATUS_ERR_BAD_ADDRESS If slave is busy, or no slave * acknowledged the address */ enum status_code i2c_master_read_packet_wait_no_stop( struct i2c_master_module *const module, struct i2c_packet *const packet) { /* Sanity check */ Assert(module); Assert(module->hw); Assert(packet); #if I2C_MASTER_CALLBACK_MODE == true /* Check if the I2C module is busy with a job. */ if (module->buffer_remaining > 0) { return STATUS_BUSY; } #endif module->send_stop = false; return _i2c_master_read(module, packet); }
/** * \internal * Interrupt handler for I<SUP>2</SUP>C master. * * \param[in] instance SERCOM instance that triggered the interrupt */ void _i2c_master_interrupt_handler( uint8_t instance) { /* Get software module for callback handling */ struct i2c_master_module *module = (struct i2c_master_module*)_sercom_instances[instance]; Assert(module); SercomI2cm *const i2c_module = &(module->hw->I2CM); /* Combine callback registered and enabled masks */ uint8_t callback_mask = module->enabled_callback & module->registered_callback; /* Check if the module should respond to address ack */ if ((module->buffer_length <= 0) && (module->buffer_remaining > 0)) { /* Call function for address response */ _i2c_master_async_address_response(module); /* Check if buffer write is done */ } else if ((module->buffer_length > 0) && (module->buffer_remaining <= 0) && (module->status == STATUS_BUSY) && (module->transfer_direction == I2C_TRANSFER_WRITE)) { /* Stop packet operation */ i2c_module->INTENCLR.reg = SERCOM_I2CM_INTENCLR_MB | SERCOM_I2CM_INTENCLR_SB; module->buffer_length = 0; module->status = STATUS_OK; if (module->send_stop) { /* Send stop condition */ _i2c_master_wait_for_sync(module); i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3); } else { /* Clear write interrupt flag */ i2c_module->INTFLAG.reg = SERCOM_I2CM_INTENCLR_MB; } if (callback_mask & (1 << I2C_MASTER_CALLBACK_WRITE_COMPLETE)) { module->callbacks[I2C_MASTER_CALLBACK_WRITE_COMPLETE](module); } /* Continue buffer write/read */ } else if ((module->buffer_length > 0) && (module->buffer_remaining > 0)){ /* Check that bus ownership is not lost */ if (!(i2c_module->STATUS.reg & SERCOM_I2CM_STATUS_BUSSTATE(2))) { module->status = STATUS_ERR_PACKET_COLLISION; } else if (module->transfer_direction == I2C_TRANSFER_WRITE) { _i2c_master_write(module); } else { _i2c_master_read(module); } } /* Check if read buffer transfer is complete */ if ((module->buffer_length > 0) && (module->buffer_remaining <= 0) && (module->status == STATUS_BUSY) && (module->transfer_direction == I2C_TRANSFER_READ)) { /* Stop packet operation */ i2c_module->INTENCLR.reg = SERCOM_I2CM_INTENCLR_MB | SERCOM_I2CM_INTENCLR_SB; module->buffer_length = 0; module->status = STATUS_OK; /* Call appropriate callback if enabled and registered */ if ((callback_mask & (1 << I2C_MASTER_CALLBACK_READ_COMPLETE)) && (module->transfer_direction == I2C_TRANSFER_READ)) { module->callbacks[I2C_MASTER_CALLBACK_READ_COMPLETE](module); } else if ((callback_mask & (1 << I2C_MASTER_CALLBACK_WRITE_COMPLETE)) && (module->transfer_direction == I2C_TRANSFER_WRITE)) { module->callbacks[I2C_MASTER_CALLBACK_WRITE_COMPLETE](module); } } /* Check for error */ if ((module->status != STATUS_BUSY) && (module->status != STATUS_OK)) { /* Stop packet operation */ i2c_module->INTENCLR.reg = SERCOM_I2CM_INTENCLR_MB | SERCOM_I2CM_INTENCLR_SB; module->buffer_length = 0; module->buffer_remaining = 0; /* Send nack and stop command unless arbitration is lost */ if ((module->status != STATUS_ERR_PACKET_COLLISION) && module->send_stop) { _i2c_master_wait_for_sync(module); i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_ACKACT | SERCOM_I2CM_CTRLB_CMD(3); } /* Call error callback if enabled and registered */ if (callback_mask & (1 << I2C_MASTER_CALLBACK_ERROR)) { module->callbacks[I2C_MASTER_CALLBACK_ERROR](module); } } }