/** * \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; }
/** * \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; }
/** * \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); } } } }