/** * \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; bool sclsm_flag = i2c_module->CTRLA.bit.SCLSM; /* Switch to high speed mode */ if (packet->high_speed) { _i2c_master_send_hs_master_code(module, packet->hs_master_code); } /* Set action to ACK. */ i2c_module->CTRLB.reg &= ~SERCOM_I2CM_CTRLB_ACKACT; /* Set address and direction bit. Will send start command on bus. */ if (packet->ten_bit_address) { /* * Write ADDR.ADDR[10:1] with the 10-bit address. ADDR.TENBITEN must * be set and read/write bit (ADDR.ADDR[0]) equal to 0. */ i2c_module->ADDR.reg = (packet->address << 1) | (packet->high_speed << SERCOM_I2CM_ADDR_HS_Pos) | SERCOM_I2CM_ADDR_TENBITEN; /* 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); } if (tmp_status == STATUS_OK) { /* * Write ADDR[7:0] register to 鈥10 address[9:8] 1鈥 * ADDR.TENBITEN must be cleared */ i2c_module->ADDR.reg = (((packet->address >> 8) | 0x78) << 1) | (packet->high_speed << SERCOM_I2CM_ADDR_HS_Pos) | I2C_TRANSFER_READ; } else {
/** * \internal * Starts a read packet 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 starting reading I<SUP>2</SUP>C packet. * \retval STATUS_OK If reading was started successfully * \retval STATUS_BUSY If module is currently busy with another transfer */ static enum status_code _i2c_master_read_packet( struct i2c_master_module *const module, struct i2c_master_packet *const packet) { /* Sanity check */ Assert(module); Assert(module->hw); SercomI2cm *const i2c_module = &(module->hw->I2CM); enum status_code tmp_status; /* Save packet to software module */ module->buffer = packet->data; module->buffer_remaining = packet->data_length; module->transfer_direction = I2C_TRANSFER_READ; module->status = STATUS_BUSY; /* Switch to high speed mode */ if (packet->high_speed) { _i2c_master_send_hs_master_code(module, packet->hs_master_code); } /* Set action to ACK. */ i2c_module->CTRLB.reg &= ~SERCOM_I2CM_CTRLB_ACKACT; if (packet->ten_bit_address) { /* * Write ADDR.ADDR[10:1] with the 10-bit address. ADDR.TENBITEN must * be set and read/write bit (ADDR.ADDR[0]) equal to 0. */ i2c_module->ADDR.reg = (packet->address << 1) | (packet->high_speed << SERCOM_I2CM_ADDR_HS_Pos) | SERCOM_I2CM_ADDR_TENBITEN; /* 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); } if (tmp_status == STATUS_OK) { /* Enable interrupts */ i2c_module->INTENSET.reg = SERCOM_I2CM_INTENSET_MB | SERCOM_I2CM_INTENSET_SB; /* * Write ADDR[7:0] register to "11110 address[9:8] 1" * ADDR.TENBITEN must be cleared */ i2c_module->ADDR.reg = (((packet->address >> 8) | 0x78) << 1) | (packet->high_speed << SERCOM_I2CM_ADDR_HS_Pos) | I2C_TRANSFER_READ; } else {
/** * \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; }