/** * \brief Resets the hardware module * * Reset the module to hardware defaults. * * \param[in,out] module Pointer to software module structure */ void i2c_master_reset(struct i2c_master_module *const module) { /* Sanity check arguments */ Assert(module); Assert(module->hw); SercomI2cm *const i2c_module = &(module->hw->I2CM); /* Wait for sync */ _i2c_master_wait_for_sync(module); /* Disable module */ i2c_master_disable(module); #if I2C_MASTER_CALLBACK_MODE == true /* Clear all pending interrupts */ system_interrupt_enter_critical_section(); system_interrupt_clear_pending(_sercom_get_interrupt_vector(module->hw)); system_interrupt_leave_critical_section(); #endif /* Wait for sync */ _i2c_master_wait_for_sync(module); /* Reset module */ i2c_module->CTRLA.reg = SERCOM_I2CM_CTRLA_SWRST; }
/** * \internal * Read next data. Used by interrupt handler to get next data byte from slave. * * \param[in,out] module Pointer to software module structure */ static void _i2c_master_read( struct i2c_master_module *const module) { /* Sanity check arguments. */ Assert(module); Assert(module->hw); SercomI2cm *const i2c_module = &(module->hw->I2CM); /* Find index to save next value in buffer */ uint16_t buffer_index = module->buffer_length - module->buffer_remaining; module->buffer_remaining--; if (!module->buffer_remaining) { /* Send nack */ i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_ACKACT; if (module->send_stop) { /* Send stop condition */ _i2c_master_wait_for_sync(module); i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3); } } else { i2c_module->CTRLB.reg &= ~SERCOM_I2CM_CTRLB_ACKACT; } /* Read byte from slave and put in buffer */ _i2c_master_wait_for_sync(module); module->buffer[buffer_index] = i2c_module->DATA.reg; }
/** * \internal * * Write next data. Used by interrupt handler to send next data byte to slave. * * \param[in,out] module Pointer to software module structure */ static void _i2c_master_write(struct i2c_master_module *const module) { /* Sanity check arguments. */ Assert(module); Assert(module->hw); SercomI2cm *const i2c_module = &(module->hw->I2CM); /* Check for ack from slave */ if (i2c_module->STATUS.reg & SERCOM_I2CM_STATUS_RXNACK) { /* Set status */ module->status = STATUS_ERR_OVERFLOW; /* Do not write more data */ return; } /* Find index to get next byte in buffer */ uint16_t buffer_index = module->buffer_length - module->buffer_remaining; module->buffer_remaining--; /* Write byte from buffer to slave */ _i2c_master_wait_for_sync(module); i2c_module->DATA.reg = module->buffer[buffer_index]; }
/** * \brief Sends stop condition on bus * * Sends a stop condition on bus. * * \note This function can only be used after the * \ref i2c_master_write_packet_wait_no_stop function. If a stop condition * is to be sent after a read, the \ref i2c_master_read_packet_wait * function must be used. * * \param[in] module Pointer to the software instance struct */ void i2c_master_send_stop(struct i2c_master_module *const module) { /* Sanity check */ Assert(module); Assert(module->hw); SercomI2cm *const i2c_module = &(module->hw->I2CM); /* Send stop command */ _i2c_master_wait_for_sync(module); i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3); }
/** * \internal * Read next data. Used by interrupt handler to get next data byte from slave. * * \param[in,out] module Pointer to software module structure */ static void _i2c_master_read( struct i2c_master_module *const module) { /* Sanity check arguments. */ Assert(module); Assert(module->hw); SercomI2cm *const i2c_module = &(module->hw->I2CM); bool sclsm_flag = i2c_module->CTRLA.bit.SCLSM; /* Find index to save next value in buffer */ uint16_t buffer_index = module->buffer_length; buffer_index -= module->buffer_remaining; module->buffer_remaining--; if (sclsm_flag) { if (module->send_nack && module->buffer_remaining == 1) { /* Set action to NACK. */ i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_ACKACT; } } else { if (module->send_nack && module->buffer_remaining == 0) { /* Set action to NACK. */ i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_ACKACT; } } if (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); } } /* Read byte from slave and put in buffer */ _i2c_master_wait_for_sync(module); module->buffer[buffer_index] = i2c_module->DATA.reg; }
/** * \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); } } }
/** * \internal * Starts blocking write operation. * * \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 */ static enum status_code _i2c_master_write_packet( struct i2c_master_module *const module, struct i2c_master_packet *const packet) { SercomI2cm *const i2c_module = &(module->hw->I2CM); /* Return value. */ enum status_code tmp_status; uint16_t tmp_data_length = packet->data_length; _i2c_master_wait_for_sync(module); /* Set address and direction bit. Will send start command on bus. */ i2c_module->ADDR.reg = (packet->address << 1) | I2C_TRANSFER_WRITE; /* Wait for response on bus. */ tmp_status = _i2c_master_wait_for_bus(module); /* Check for address response error unless previous error is * detected. */ if (tmp_status == STATUS_OK) { tmp_status = _i2c_master_address_response(module); } /* Check that no error has occurred. */ if (tmp_status == STATUS_OK) { /* Buffer counter. */ uint16_t buffer_counter = 0; /* Write data buffer. */ while (tmp_data_length--) { /* Check that bus ownership is not lost. */ if (!(i2c_module->STATUS.reg & SERCOM_I2CM_STATUS_BUSSTATE(2))) { return STATUS_ERR_PACKET_COLLISION; } /* Write byte to slave. */ _i2c_master_wait_for_sync(module); i2c_module->DATA.reg = packet->data[buffer_counter++]; /* Wait for response. */ tmp_status = _i2c_master_wait_for_bus(module); /* Check for error. */ if (tmp_status != STATUS_OK) { break; } /* Check for NACK from slave. */ if (i2c_module->STATUS.reg & SERCOM_I2CM_STATUS_RXNACK) { /* Return bad data value. */ tmp_status = STATUS_ERR_OVERFLOW; break; } } if (module->send_stop) { /* Stop command */ _i2c_master_wait_for_sync(module); i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3); } } return tmp_status; }
/** * \internal * Starts blocking read operation. * * \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 * */ static enum status_code _i2c_master_read_packet( struct i2c_master_module *const module, struct i2c_master_packet *const packet) { /* Sanity check arguments */ Assert(module); Assert(module->hw); Assert(packet); SercomI2cm *const i2c_module = &(module->hw->I2CM); /* Return value. */ enum status_code tmp_status; uint16_t tmp_data_length = packet->data_length; /* Written buffer counter. */ uint16_t counter = 0; /* Set address and direction bit. Will send start command on bus. */ i2c_module->ADDR.reg = (packet->address << 1) | I2C_TRANSFER_READ; /* Wait for response on bus. */ tmp_status = _i2c_master_wait_for_bus(module); /* Set action to ack. */ i2c_module->CTRLB.reg &= ~SERCOM_I2CM_CTRLB_ACKACT; /* Check for address response error unless previous error is * detected. */ if (tmp_status == STATUS_OK) { tmp_status = _i2c_master_address_response(module); } /* Check that no error has occurred. */ if (tmp_status == STATUS_OK) { /* Read data buffer. */ while (tmp_data_length--) { /* Check that bus ownership is not lost. */ if (!(i2c_module->STATUS.reg & SERCOM_I2CM_STATUS_BUSSTATE(2))) { return STATUS_ERR_PACKET_COLLISION; } if (tmp_data_length == 0) { /* Set action to NACK */ i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_ACKACT; } else { /* Save data to buffer. */ _i2c_master_wait_for_sync(module); packet->data[counter++] = i2c_module->DATA.reg; /* Wait for response. */ tmp_status = _i2c_master_wait_for_bus(module); } /* Check for error. */ if (tmp_status != STATUS_OK) { break; } } if (module->send_stop) { /* Send stop command unless arbitration is lost. */ _i2c_master_wait_for_sync(module); i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3); } /* Save last data to buffer. */ _i2c_master_wait_for_sync(module); packet->data[counter] = i2c_module->DATA.reg; } return tmp_status; }
/** * \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); } } }