Ejemplo n.º 1
0
/**
 * \brief Writes a packet to the master
 *
 * Writes a packet to the master. This will wait for the master to issue
 * a request.
 *
 * \param[in]  module  Pointer to software module structure
 * \param[in]  packet  Packet to write to master
 *
 * \return Status of packet write.
 * \retval STATUS_OK                Packet was written successfully
 * \retval STATUS_ERR_DENIED        Start condition not received, another
 *                                  interrupt flag is set
 * \retval STATUS_ERR_IO            There was an error in the previous transfer
 * \retval STATUS_ERR_BAD_FORMAT    Master wants to write data
 * \retval STATUS_ERR_INVALID_ARG   Invalid argument(s) was provided
 * \retval STATUS_ERR_BUSY          The I<SUP>2</SUP>C module is busy with a job
 * \retval STATUS_ERR_ERR_OVERFLOW  Master NACKed before entire packet was
 *                                  transferred
 * \retval STATUS_ERR_TIMEOUT       No response was given within the timeout
 *                                  period
 */
enum status_code i2c_slave_write_packet_wait(
		struct i2c_slave_module *const module,
		struct i2c_slave_packet *const packet)
{
	/* Sanity check arguments */
	Assert(module);
	Assert(module->hw);
	Assert(packet);

	SercomI2cs *const i2c_hw = &(module->hw->I2CS);

	uint16_t length = packet->data_length;

	if (length == 0) {
		return STATUS_ERR_INVALID_ARG;
	}

#if I2C_SLAVE_CALLBACK_MODE == true
	/* Check if the module is busy with a job or AMATCH is enabled */
	if (module->buffer_remaining > 0 ||
			(i2c_hw->INTENSET.reg & SERCOM_I2CS_INTFLAG_AMATCH)) {
		return STATUS_BUSY;
	}
#endif

	enum status_code status;
	/* Wait for master to send address packet */
	status = _i2c_slave_wait_for_bus(module);

	if (status != STATUS_OK) {
			/* Timeout, return */
			return status;
	}
	if (!(i2c_hw->INTFLAG.reg & SERCOM_I2CS_INTFLAG_AMATCH)) {
		/* Not address interrupt, something is wrong */
		return STATUS_ERR_DENIED;
	}

	if (module->ten_bit_address) {
		/* ACK the first address */
		i2c_hw->CTRLB.reg &= ~SERCOM_I2CS_CTRLB_ACKACT;
		i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_CMD(0x3);

		/* Wait for address interrupt */
		status = _i2c_slave_wait_for_bus(module);

		if (status != STATUS_OK) {
			/* Timeout, return */
			return STATUS_ERR_TIMEOUT;
		}

		if (!(i2c_hw->INTFLAG.reg & SERCOM_I2CS_INTFLAG_AMATCH)) {
			/* Not address interrupt, something is wrong */
			return STATUS_ERR_DENIED;
		}
	}

	/* Check if there was an error in last transfer */
	if (i2c_hw->STATUS.reg & (SERCOM_I2CS_STATUS_BUSERR |
			SERCOM_I2CS_STATUS_COLL | SERCOM_I2CS_STATUS_LOWTOUT)) {
		return STATUS_ERR_IO;
	}

	/* Check direction */
	if (!(i2c_hw->STATUS.reg & SERCOM_I2CS_STATUS_DIR)) {
		/* Write request from master, send NACK and return */
		i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_ACKACT;
		i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_CMD(0x3);
		return STATUS_ERR_BAD_FORMAT;
	}

	/* Read request from master, ACK address */
	i2c_hw->CTRLB.reg &= ~SERCOM_I2CS_CTRLB_ACKACT;
	i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_CMD(0x3);

	uint16_t i = 0;

	/* Wait for data interrupt */
	status = _i2c_slave_wait_for_bus(module);
	if (status != STATUS_OK) {
			/* Timeout, return */
			return status;
	}

	while (length--) {
		/* Write data */
		_i2c_slave_wait_for_sync(module);
		i2c_hw->DATA.reg = packet->data[i++];

		/* Wait for response from master */
		status = _i2c_slave_wait_for_bus(module);

		if (status != STATUS_OK) {
			/* Timeout, return */
			return status;
		}

		if (i2c_hw->STATUS.reg & SERCOM_I2CS_STATUS_RXNACK &&
				length !=0) {
			/* NACK from master, abort */
			/* Release line */
			i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_CMD(0x02);

			return STATUS_ERR_OVERFLOW;
		}
		/* ACK from master, continue writing */
	}

	/* Release line */
	i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_CMD(0x02);

	return STATUS_OK;
}
Ejemplo n.º 2
0
/**
 * \brief Reads a packet from the master
 *
 * Reads a packet from the master. This will wait for the master to issue a
 * request.
 *
 * \param[in]  module  Pointer to software module structure
 * \param[out] packet  Packet to read from master
 *
 * \return Status of packet read.
 * \retval STATUS_OK                Packet was read successfully
 * \retval STATUS_ABORTED           Master sent stop condition or repeated
 *                                  start before specified length of bytes
 *                                  was received
 * \retval STATUS_ERR_IO            There was an error in the previous transfer
 * \retval STATUS_ERR_DENIED        Start condition not received, another
 *                                  interrupt flag is set
 * \retval STATUS_ERR_INVALID_ARG   Invalid argument(s) was provided
 * \retval STATUS_ERR_BUSY          The I<SUP>2</SUP>C module is busy with a job
 * \retval STATUS_ERR_BAD_FORMAT    Master wants to read data
 * \retval STATUS_ERR_ERR_OVERFLOW  Last byte received overflows buffer
 */
enum status_code i2c_slave_read_packet_wait(
		struct i2c_slave_module *const module,
		struct i2c_slave_packet *const packet)
{
	/* Sanity check arguments */
	Assert(module);
	Assert(module->hw);
	Assert(packet);

	SercomI2cs *const i2c_hw = &(module->hw->I2CS);

	uint16_t length = packet->data_length;

	if (length == 0) {
		return STATUS_ERR_INVALID_ARG;
	}

#if I2C_SLAVE_CALLBACK_MODE == true
	/* Check if the module is busy with a job or AMATCH is enabled */
	if (module->buffer_remaining > 0 ||
			(i2c_hw->INTENSET.reg & SERCOM_I2CS_INTFLAG_AMATCH)) {
		return STATUS_BUSY;
	}
#endif

	enum status_code status;

	/* Wait for master to send address packet */
	status = _i2c_slave_wait_for_bus(module);
	if (status != STATUS_OK) {
		/* Timeout, return */
		return status;
	}

	if (!(i2c_hw->INTFLAG.reg & SERCOM_I2CS_INTFLAG_AMATCH)) {
		/* Not address interrupt, something is wrong */
		return STATUS_ERR_DENIED;
	}

	/* Check if there was an error in the last transfer */
	if (i2c_hw->STATUS.reg & (SERCOM_I2CS_STATUS_BUSERR |
			SERCOM_I2CS_STATUS_COLL | SERCOM_I2CS_STATUS_LOWTOUT)) {
		return STATUS_ERR_IO;
	}
	/* Check direction */
	if ((i2c_hw->STATUS.reg & SERCOM_I2CS_STATUS_DIR)) {
		/* Read request from master, send NACK and return */
		i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_ACKACT;
		i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_CMD(0x3);
		return STATUS_ERR_BAD_FORMAT;
	}

	/* Write request from master, ACK address */
	i2c_hw->CTRLB.reg &= ~SERCOM_I2CS_CTRLB_ACKACT;
	i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_CMD(0x3);

	uint16_t i = 0;
	while (length--) {

		/* Wait for next byte or stop condition */
		status = _i2c_slave_wait_for_bus(module);
		if (status != STATUS_OK) {
			/* Timeout, return */
			return status;
		}

		if ((i2c_hw->INTFLAG.reg & SERCOM_I2CS_INTFLAG_PREC) ||
				i2c_hw->INTFLAG.reg & SERCOM_I2CS_INTFLAG_AMATCH) {
			/* Master sent stop condition, or repeated start, read done */
			/* Clear stop flag */
			i2c_hw->INTFLAG.reg = SERCOM_I2CS_INTFLAG_PREC;
			return STATUS_ABORTED;
		}

		/* Read data */
		_i2c_slave_wait_for_sync(module);
		packet->data[i++] = i2c_hw->DATA.reg;

	}

	/* Packet read done, wait for packet to NACK, Stop or repeated start */
	status = _i2c_slave_wait_for_bus(module);

	if (i2c_hw->INTFLAG.reg & SERCOM_I2CS_INTFLAG_DRDY) {
		/* Buffer is full, send NACK */
		i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_ACKACT;
		i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_CMD(0x2);
	}
	if (i2c_hw->INTFLAG.reg & SERCOM_I2CS_INTFLAG_PREC) {
		/* Clear stop flag */
		i2c_hw->INTFLAG.reg = SERCOM_I2CS_INTFLAG_PREC;
	}
	return STATUS_OK;
}
Ejemplo n.º 3
0
/**
 * \internal Interrupt handler for I<SUP>2</SUP>C slave
 *
 * \param[in] instance Sercom instance that triggered the interrupt
 */
void _i2c_slave_interrupt_handler(
		uint8_t instance)
{
	/* Get software module for callback handling. */
	struct i2c_slave_module *module =
			(struct i2c_slave_module*)_sercom_instances[instance];

	Assert(module);

	SercomI2cs *const i2c_hw = &(module->hw->I2CS);

	/* Combine callback registered and enabled masks. */
	uint8_t callback_mask =
			module->enabled_callback & module->registered_callback;


	if (i2c_hw->INTFLAG.reg & SERCOM_I2CS_INTFLAG_AMATCH) {
	/* Address match */
		/* Check if last transfer is done - repeated start */
		if (module->buffer_length != module->buffer_remaining &&
				module->transfer_direction == I2C_TRANSFER_WRITE) {

			module->status = STATUS_OK;
			module->buffer_length = 0;
			module->buffer_remaining = 0;

			if ((callback_mask & (1 << I2C_SLAVE_CALLBACK_READ_COMPLETE))) {
				module->callbacks[I2C_SLAVE_CALLBACK_READ_COMPLETE](module);
			}
		} else if (module->buffer_length != module->buffer_remaining &&
				module->transfer_direction == I2C_TRANSFER_READ) {
			module->status = STATUS_OK;
			module->buffer_length = 0;
			module->buffer_remaining = 0;

			if ((callback_mask & (1 << I2C_SLAVE_CALLBACK_WRITE_COMPLETE))) {
				module->callbacks[I2C_SLAVE_CALLBACK_WRITE_COMPLETE](module);
			}
		}

		if (i2c_hw->STATUS.reg & (SERCOM_I2CS_STATUS_BUSERR ||
				SERCOM_I2CS_STATUS_COLL || SERCOM_I2CS_STATUS_LOWTOUT)) {
			/* An error occurred in last packet transfer */
			module->status = STATUS_ERR_IO;

			if ((callback_mask & (1 << I2C_SLAVE_CALLBACK_ERROR_LAST_TRANSFER))) {
				module->callbacks[I2C_SLAVE_CALLBACK_ERROR_LAST_TRANSFER](module);
			}
		}
		if (module->nack_on_address) {
			/* NACK address */
			i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_ACKACT;
		} else if (i2c_hw->STATUS.reg & SERCOM_I2CS_STATUS_DIR) {
			/* Set transfer direction in module instance */
			module->transfer_direction = I2C_TRANSFER_READ;

			/* Read request from master */
			if (callback_mask & (1 << I2C_SLAVE_CALLBACK_READ_REQUEST)) {
				module->callbacks[I2C_SLAVE_CALLBACK_READ_REQUEST](module);
			}

			if (module->buffer_length == 0) {
				/* Data buffer not set up, NACK address */
				i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_ACKACT;
			} else {
				/* ACK address */
				i2c_hw->CTRLB.reg &= ~SERCOM_I2CS_CTRLB_ACKACT;
			}
		} else {
			/* Set transfer direction in dev inst */
			module->transfer_direction = I2C_TRANSFER_WRITE;

			/* Write request from master */
			if (callback_mask & (1 << I2C_SLAVE_CALLBACK_WRITE_REQUEST)) {
				module->callbacks[I2C_SLAVE_CALLBACK_WRITE_REQUEST](module);
			}

			if (module->buffer_length == 0) {
				/* Data buffer not set up, NACK address */
				i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_ACKACT;
			} else {
				/* ACK address */
				i2c_hw->CTRLB.reg &= ~SERCOM_I2CS_CTRLB_ACKACT;
			}
		}

		/* ACK or NACK address */
		i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_CMD(0x3);

		/* ACK next incoming packet */
		i2c_hw->CTRLB.reg &= ~SERCOM_I2CS_CTRLB_ACKACT;

	} else if (i2c_hw->INTFLAG.reg & SERCOM_I2CS_INTFLAG_PREC) {
		/* Stop condition on bus - current transfer done */

		/* Clear Stop interrupt */
		i2c_hw->INTFLAG.reg = SERCOM_I2CS_INTFLAG_PREC;

		/* Disable interrupts */
		i2c_hw->INTENCLR.reg = SERCOM_I2CS_INTFLAG_PREC | SERCOM_I2CS_INTFLAG_DRDY;

		if (!((module->enabled_callback & (1 << I2C_SLAVE_CALLBACK_READ_REQUEST))
				|| (module->enabled_callback == (1 << I2C_SLAVE_CALLBACK_WRITE_REQUEST)))) {
			/* Disable address match if read/write request is not enabled */
			i2c_hw->INTENCLR.reg = SERCOM_I2CS_INTFLAG_AMATCH;
		}

		if (!(module->status == STATUS_ERR_OVERFLOW || module->status == STATUS_ERR_IO)) {
			module->status = STATUS_OK;
			module->buffer_length = 0;
			module->buffer_remaining = 0;

			/* Call appropriate callback if enabled and registered */
			if ((callback_mask & (1 << I2C_SLAVE_CALLBACK_READ_COMPLETE))
					&& (module->transfer_direction == I2C_TRANSFER_WRITE)) {
				/* Read from master complete */
				module->callbacks[I2C_SLAVE_CALLBACK_READ_COMPLETE](module);
			} else if ((callback_mask & (1 << I2C_SLAVE_CALLBACK_WRITE_COMPLETE))
					&& (module->transfer_direction == I2C_TRANSFER_READ)) {
				/* Write to master complete */
				module->callbacks[I2C_SLAVE_CALLBACK_WRITE_COMPLETE](module);
			}
					}
	} else if (i2c_hw->INTFLAG.reg & SERCOM_I2CS_INTFLAG_DRDY) {
		/* Check if buffer is full, or NACK from master */
		if (module->buffer_remaining <= 0 ||
				(module->transfer_direction == I2C_TRANSFER_READ &&
				(module->buffer_length > module->buffer_remaining) &&
				(i2c_hw->STATUS.reg & SERCOM_I2CS_STATUS_RXNACK))) {

			module->buffer_remaining = 0;
			module->buffer_length = 0;

			if (module->transfer_direction == I2C_TRANSFER_WRITE) {
				/* Buffer is full, send NACK */
				i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_ACKACT;
				i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_CMD(0x2);

				/* Set status, new character in DATA register will overflow
				 * buffer */
				module->status = STATUS_ERR_OVERFLOW;

				if (callback_mask & (1 << I2C_SLAVE_CALLBACK_ERROR)) {
					/* Read complete */
					module->callbacks[I2C_SLAVE_CALLBACK_ERROR](module);
				}
			} else {
				/* Release SCL and wait for new start condition */
				i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_ACKACT;
				i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_CMD(0x2);

				/* Transfer successful */
				module->status = STATUS_OK;

				/* Disable interrupts */
				i2c_hw->INTENCLR.reg = SERCOM_I2CS_INTFLAG_DRDY;
			}

		/* Continue buffer write/read */
		} else if (module->buffer_length > 0 && module->buffer_remaining > 0) {
			/* Call function based on transfer direction */
			if (module->transfer_direction == I2C_TRANSFER_WRITE) {
				_i2c_slave_read(module);
			} else {
				_i2c_slave_write(module);
			}
		}
	}
}