Esempio n. 1
0
/**
 * \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 {