mbed library sources. Supersedes mbed-src.

Fork of mbed-dev by mbed official

Revision:
15:a81a8d6c1dfe
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/targets/hal/TARGET_Atmel/TARGET_SAM_CortexM0P/drivers/sercom/i2c/i2c_samd21_r21_d10_d11_l21/i2c_master.c	Wed Nov 04 16:30:11 2015 +0000
@@ -0,0 +1,1040 @@
+/**
+ * \file
+ *
+ * \brief SAM I2C Master Driver
+ *
+ * Copyright (C) 2012-2015 Atmel Corporation. All rights reserved.
+ *
+ * \asf_license_start
+ *
+ * \page License
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * 3. The name of Atmel may not be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * 4. This software may only be redistributed and used in connection with an
+ *    Atmel microcontroller product.
+ *
+ * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
+ * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * \asf_license_stop
+ *
+ */
+/*
+ * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a>
+ */
+
+#include "i2c_master.h"
+
+#if I2C_MASTER_CALLBACK_MODE == true
+# include "i2c_master_interrupt.h"
+#endif
+
+/* Forward declaration */
+enum status_code _i2c_master_wait_for_bus(
+    struct i2c_master_module *const module);
+
+enum status_code _i2c_master_address_response(
+    struct i2c_master_module *const module);
+
+enum status_code _i2c_master_send_hs_master_code(
+    struct i2c_master_module *const module,
+    uint8_t hs_master_code);
+
+#if !defined(__DOXYGEN__)
+
+/**
+ * \internal Sets configurations to module
+ *
+ * \param[out] module  Pointer to software module structure
+ * \param[in]  config  Configuration structure with configurations to set
+ *
+ * \return Status of setting configuration.
+ * \retval STATUS_OK                        If module was configured correctly
+ * \retval STATUS_ERR_ALREADY_INITIALIZED   If setting other GCLK generator than
+ *                                          previously set
+ * \retval STATUS_ERR_BAUDRATE_UNAVAILABLE  If given baudrate is not compatible
+ *                                          with set GCLK frequency
+ */
+static enum status_code _i2c_master_set_config(
+    struct i2c_master_module *const module,
+    const struct i2c_master_config *const config)
+{
+    /* Sanity check arguments. */
+    Assert(module);
+    Assert(module->hw);
+    Assert(config);
+
+    /* Temporary variables. */
+    uint32_t tmp_ctrla;
+    int32_t tmp_baud;
+    int32_t tmp_baud_hs;
+    enum status_code tmp_status_code = STATUS_OK;
+
+    SercomI2cm *const i2c_module = &(module->hw->I2CM);
+    Sercom *const sercom_hw = module->hw;
+
+    uint8_t sercom_index = _sercom_get_sercom_inst_index(sercom_hw);
+
+    /* Pin configuration */
+    struct system_pinmux_config pin_conf;
+    system_pinmux_get_config_defaults(&pin_conf);
+
+    uint32_t pad0 = config->pinmux_pad0;
+    uint32_t pad1 = config->pinmux_pad1;
+
+    /* SERCOM PAD0 - SDA */
+    if (pad0 == PINMUX_DEFAULT) {
+        pad0 = _sercom_get_default_pad(sercom_hw, 0);
+    }
+    pin_conf.mux_position = pad0 & 0xFFFF;
+    pin_conf.direction    = SYSTEM_PINMUX_PIN_DIR_OUTPUT_WITH_READBACK;
+    system_pinmux_pin_set_config(pad0 >> 16, &pin_conf);
+
+    /* SERCOM PAD1 - SCL */
+    if (pad1 == PINMUX_DEFAULT) {
+        pad1 = _sercom_get_default_pad(sercom_hw, 1);
+    }
+    pin_conf.mux_position = pad1 & 0xFFFF;
+    pin_conf.direction    = SYSTEM_PINMUX_PIN_DIR_OUTPUT_WITH_READBACK;
+    system_pinmux_pin_set_config(pad1 >> 16, &pin_conf);
+
+    /* Save timeout on unknown bus state in software module. */
+    module->unknown_bus_state_timeout = config->unknown_bus_state_timeout;
+
+    /* Save timeout on buffer write. */
+    module->buffer_timeout = config->buffer_timeout;
+
+    /* Set whether module should run in standby. */
+    if (config->run_in_standby || system_is_debugger_present()) {
+        tmp_ctrla = SERCOM_I2CM_CTRLA_RUNSTDBY;
+    } else {
+        tmp_ctrla = 0;
+    }
+
+    /* Check and set start data hold timeout. */
+    if (config->start_hold_time != I2C_MASTER_START_HOLD_TIME_DISABLED) {
+        tmp_ctrla |= config->start_hold_time;
+    }
+
+    /* Check and set transfer speed */
+    tmp_ctrla |= config->transfer_speed;
+
+    /* Check and set SCL low timeout. */
+    if (config->scl_low_timeout) {
+        tmp_ctrla |= SERCOM_I2CM_CTRLA_LOWTOUTEN;
+    }
+
+    /* Check and set inactive bus timeout. */
+    if (config->inactive_timeout != I2C_MASTER_INACTIVE_TIMEOUT_DISABLED) {
+        tmp_ctrla |= config->inactive_timeout;
+    }
+
+    /* Check and set SCL clock stretch mode. */
+    if (config->scl_stretch_only_after_ack_bit) {
+        tmp_ctrla |= SERCOM_I2CM_CTRLA_SCLSM;
+    }
+
+    /* Check and set slave SCL low extend timeout. */
+    if (config->slave_scl_low_extend_timeout) {
+        tmp_ctrla |= SERCOM_I2CM_CTRLA_SEXTTOEN;
+    }
+
+    /* Check and set master SCL low extend timeout. */
+    if (config->master_scl_low_extend_timeout) {
+        tmp_ctrla |= SERCOM_I2CM_CTRLA_MEXTTOEN;
+    }
+
+    /* Write config to register CTRLA. */
+    i2c_module->CTRLA.reg |= tmp_ctrla;
+
+    /* Set configurations in CTRLB. */
+    i2c_module->CTRLB.reg = SERCOM_I2CM_CTRLB_SMEN;
+
+    /* Find and set baudrate, considering sda/scl rise time */
+    uint32_t fgclk       = system_gclk_chan_get_hz(SERCOM0_GCLK_ID_CORE + sercom_index);
+    uint32_t fscl        = 1000*config->baud_rate;
+    uint32_t trise       = config->sda_scl_rise_time_ns;
+    int32_t  numerator   = fgclk - fscl*(10 + fgclk*trise/1000000000);
+    int32_t  denominator = 2*fscl;
+    /* For more accurate result, can use round div. */
+    tmp_baud = (int32_t)(div_ceil(numerator, denominator));
+
+    /* Check that baudrate is supported at current speed. */
+    if (tmp_baud > 255 || tmp_baud < 0) {
+        /* Baud rate not supported. */
+        tmp_status_code = STATUS_ERR_BAUDRATE_UNAVAILABLE;
+    } else {
+        /* Find baudrate for high speed */
+        tmp_baud_hs = (int32_t)(div_ceil(
+                                    system_gclk_chan_get_hz(SERCOM0_GCLK_ID_CORE + sercom_index),
+                                    (2000*(config->baud_rate_high_speed))) - 1);
+
+        /* Check that baudrate is supported at current speed. */
+        if (tmp_baud_hs > 255 || tmp_baud_hs < 0) {
+            /* Baud rate not supported. */
+            tmp_status_code = STATUS_ERR_BAUDRATE_UNAVAILABLE;
+        }
+    }
+    if (tmp_status_code != STATUS_ERR_BAUDRATE_UNAVAILABLE) {
+        /* Baud rate acceptable. */
+        i2c_module->BAUD.reg = SERCOM_I2CM_BAUD_BAUD(tmp_baud) |
+                               SERCOM_I2CM_BAUD_HSBAUD(tmp_baud_hs);
+    }
+
+    return tmp_status_code;
+}
+#endif /* __DOXYGEN__ */
+
+/**
+ * \brief Initializes the requested I<SUP>2</SUP>C hardware module
+ *
+ * Initializes the SERCOM I<SUP>2</SUP>C master device requested and sets the provided
+ * software module struct. Run this function before any further use of
+ * the driver.
+ *
+ * \param[out] module  Pointer to software module struct
+ * \param[in]  hw      Pointer to the hardware instance
+ * \param[in]  config  Pointer to the configuration struct
+ *
+ * \return Status of initialization.
+ * \retval STATUS_OK                        Module initiated correctly
+ * \retval STATUS_ERR_DENIED                If module is enabled
+ * \retval STATUS_BUSY                      If module is busy resetting
+ * \retval STATUS_ERR_ALREADY_INITIALIZED   If setting other GCLK generator than
+ *                                          previously set
+ * \retval STATUS_ERR_BAUDRATE_UNAVAILABLE  If given baudrate is not compatible
+ *                                          with set GCLK frequency
+ *
+ */
+enum status_code i2c_master_init(
+    struct i2c_master_module *const module,
+    Sercom *const hw,
+    const struct i2c_master_config *const config)
+{
+    /* Sanity check arguments. */
+    Assert(module);
+    Assert(hw);
+    Assert(config);
+
+    /* Initialize software module */
+    module->hw = hw;
+
+    SercomI2cm *const i2c_module = &(module->hw->I2CM);
+
+    uint32_t sercom_index = _sercom_get_sercom_inst_index(module->hw);
+    uint32_t pm_index, gclk_index;
+#if (SAML21) || (SAMC20) || (SAMC21)
+#if (SAML21)
+    if (sercom_index == 5) {
+        pm_index     = MCLK_APBDMASK_SERCOM5_Pos;
+        gclk_index   = SERCOM5_GCLK_ID_CORE;
+    } else {
+        pm_index     = sercom_index + MCLK_APBCMASK_SERCOM0_Pos;
+        gclk_index   = sercom_index + SERCOM0_GCLK_ID_CORE;
+    }
+#else
+    pm_index     = sercom_index + MCLK_APBCMASK_SERCOM0_Pos;
+    gclk_index   = sercom_index + SERCOM0_GCLK_ID_CORE;
+#endif
+#else
+    pm_index     = sercom_index + PM_APBCMASK_SERCOM0_Pos;
+    gclk_index   = sercom_index + SERCOM0_GCLK_ID_CORE;
+#endif
+
+    /* Turn on module in PM */
+#if (SAML21)
+    if (sercom_index == 5) {
+        system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBD, 1 << pm_index);
+    } else {
+        system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBC, 1 << pm_index);
+    }
+#else
+    system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBC, 1 << pm_index);
+#endif
+
+    /* Set up the GCLK for the module */
+    struct system_gclk_chan_config gclk_chan_conf;
+    system_gclk_chan_get_config_defaults(&gclk_chan_conf);
+    gclk_chan_conf.source_generator = config->generator_source;
+    system_gclk_chan_set_config(gclk_index, &gclk_chan_conf);
+    system_gclk_chan_enable(gclk_index);
+    sercom_set_gclk_generator(config->generator_source, false);
+
+    /* Check if module is enabled. */
+    if (i2c_module->CTRLA.reg & SERCOM_I2CM_CTRLA_ENABLE) {
+        return STATUS_ERR_DENIED;
+    }
+
+    /* Check if reset is in progress. */
+    if (i2c_module->CTRLA.reg & SERCOM_I2CM_CTRLA_SWRST) {
+        return STATUS_BUSY;
+    }
+
+#if I2C_MASTER_CALLBACK_MODE == true
+    /* Get sercom instance index and register callback. */
+    uint8_t instance_index = _sercom_get_sercom_inst_index(module->hw);
+    _sercom_set_handler(instance_index, _i2c_master_interrupt_handler);
+    _sercom_instances[instance_index] = module;
+
+    /* Initialize values in module. */
+    module->registered_callback = 0;
+    module->enabled_callback = 0;
+    module->buffer_length = 0;
+    module->buffer_remaining = 0;
+
+    module->status = STATUS_OK;
+    module->buffer = NULL;
+#endif
+
+    /* Set sercom module to operate in I2C master mode. */
+    i2c_module->CTRLA.reg = SERCOM_I2CM_CTRLA_MODE(0x5);
+
+    /* Set config and return status. */
+    return _i2c_master_set_config(module, config);
+}
+
+/**
+ * \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;
+}
+
+#if !defined(__DOXYGEN__)
+/**
+ * \internal
+ * Address response. Called when address is answered or timed out.
+ *
+ * \param[in,out] module  Pointer to software module structure
+ *
+ * \return Status of address response.
+ * \retval STATUS_OK                    No error has occurred
+ * \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
+ */
+enum status_code _i2c_master_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 and ignore bus-error; workaround for BUSSTATE stuck in
+     * BUSY */
+    if (i2c_module->INTFLAG.reg & SERCOM_I2CM_INTFLAG_SB) {
+
+        /* Clear write interrupt flag */
+        i2c_module->INTFLAG.reg = SERCOM_I2CM_INTFLAG_SB;
+
+        /* Check arbitration. */
+        if (i2c_module->STATUS.reg & SERCOM_I2CM_STATUS_ARBLOST) {
+            /* Return packet collision. */
+            return STATUS_ERR_PACKET_COLLISION;
+        }
+        /* Check that slave responded with ack. */
+    } else if (i2c_module->STATUS.reg & SERCOM_I2CM_STATUS_RXNACK) {
+        /* Slave busy. Issue ack and stop command. */
+        i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3);
+
+        /* Return bad address value. */
+        return STATUS_ERR_BAD_ADDRESS;
+    }
+
+    return STATUS_OK;
+}
+
+/**
+ * \internal
+ * Waits for answer on bus.
+ *
+ * \param[in,out] module  Pointer to software module structure
+ *
+ * \return Status of bus.
+ * \retval STATUS_OK           If given response from slave device
+ * \retval STATUS_ERR_TIMEOUT  If no response was given within specified timeout
+ *                             period
+ */
+enum status_code _i2c_master_wait_for_bus(
+    struct i2c_master_module *const module)
+{
+    /* Sanity check arguments */
+    Assert(module);
+    Assert(module->hw);
+
+    SercomI2cm *const i2c_module = &(module->hw->I2CM);
+
+    /* Wait for reply. */
+    uint16_t timeout_counter = 0;
+    while (!(i2c_module->INTFLAG.reg & SERCOM_I2CM_INTFLAG_MB) &&
+            !(i2c_module->INTFLAG.reg & SERCOM_I2CM_INTFLAG_SB)) {
+
+        /* Check timeout condition. */
+        if (++timeout_counter >= module->buffer_timeout) {
+            return STATUS_ERR_TIMEOUT;
+        }
+    }
+    return STATUS_OK;
+}
+#endif /* __DOXYGEN__ */
+
+/**
+ * \internal
+ * Send master code for high speed transfer.
+ *
+ * \param[in,out] module  Pointer to software module structure
+ * \param[in]     hs_master_code 8-bit master code (0000 1XXX)
+ *
+ * \return Status of bus.
+ * \retval STATUS_OK           No error happen
+ */
+enum status_code _i2c_master_send_hs_master_code(
+    struct i2c_master_module *const module,
+    uint8_t hs_master_code)
+{
+    SercomI2cm *const i2c_module = &(module->hw->I2CM);
+    /* Return value. */
+    enum status_code tmp_status;
+
+    /* Set NACK for high speed code */
+    i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_ACKACT;
+    /* Send high speed code */
+    i2c_module->ADDR.reg = hs_master_code;
+    /* Wait for response on bus. */
+    tmp_status = _i2c_master_wait_for_bus(module);
+    /* Clear write interrupt flag */
+    i2c_module->INTFLAG.reg = SERCOM_I2CM_INTENCLR_MB;
+
+    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;
+
+    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 "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 {
+            return tmp_status;
+        }
+    } else {
+        i2c_module->ADDR.reg = (packet->address << 1) | I2C_TRANSFER_READ |
+                               (packet->high_speed << SERCOM_I2CM_ADDR_HS_Pos);
+    }
+
+    /* 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 (module->send_nack && (((!sclsm_flag) && (tmp_data_length == 0)) ||
+                                      ((sclsm_flag) && (tmp_data_length == 1)))) {
+                /* 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;
+}
+
+/**
+ * \brief Reads data packet from slave
+ *
+ * Reads a data packet from the specified slave address on the I<SUP>2</SUP>C
+ * bus and sends a stop condition when finished.
+ *
+ * \note This will stall the device from any other operation. For
+ *       interrupt-driven operation, see \ref i2c_master_read_packet_job.
+ *
+ * \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
+ */
+enum status_code i2c_master_read_packet_wait(
+    struct i2c_master_module *const module,
+    struct i2c_master_packet *const packet)
+{
+    /* Sanity check */
+    Assert(module);
+    Assert(module->hw);
+    Assert(packet);
+
+#if I2C_MASTER_CALLBACK_MODE == true
+    /* Check if the I2C module is busy with a job. */
+    if (module->buffer_remaining > 0) {
+        return STATUS_BUSY;
+    }
+#endif
+
+    module->send_stop = true;
+    module->send_nack = true;
+
+    return _i2c_master_read_packet(module, packet);
+}
+
+/**
+ * \brief Reads data packet from slave without sending a stop condition when done
+ *
+ * Reads a data packet from the specified slave address on the I<SUP>2</SUP>C
+ * bus without sending a stop condition when done, thus retaining ownership of
+ * the bus when done. To end the transaction, a
+ * \ref i2c_master_read_packet_wait "read" or
+ * \ref i2c_master_write_packet_wait "write" with stop condition must be
+ * performed.
+ *
+ * \note This will stall the device from any other operation. For
+ *       interrupt-driven operation, see \ref i2c_master_read_packet_job.
+ *
+ * \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
+ */
+enum status_code i2c_master_read_packet_wait_no_stop(
+    struct i2c_master_module *const module,
+    struct i2c_master_packet *const packet)
+{
+    /* Sanity check */
+    Assert(module);
+    Assert(module->hw);
+    Assert(packet);
+
+#if I2C_MASTER_CALLBACK_MODE == true
+    /* Check if the I2C module is busy with a job. */
+    if (module->buffer_remaining > 0) {
+        return STATUS_BUSY;
+    }
+#endif
+
+    module->send_stop = false;
+    module->send_nack = true;
+
+    return _i2c_master_read_packet(module, packet);
+}
+
+/**
+ * \internal
+ * Starts blocking read operation.
+ * \brief Reads data packet from slave without sending a nack signal and a stop
+ * condition when done
+ *
+ * Reads a data packet from the specified slave address on the I<SUP>2</SUP>C
+ * bus without sending a nack signal and a stop condition when done,
+ * thus retaining ownership of the bus when done. To end the transaction, a
+ * \ref i2c_master_read_packet_wait "read" or
+ * \ref i2c_master_write_packet_wait "write" with stop condition must be
+ * performed.
+ *
+ * \note This will stall the device from any other operation. For
+ *       interrupt-driven operation, see \ref i2c_master_read_packet_job.
+ *
+ * \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
+ */
+enum status_code i2c_master_read_packet_wait_no_nack(
+    struct i2c_master_module *const module,
+    struct i2c_master_packet *const packet)
+{
+    /* Sanity check */
+    Assert(module);
+    Assert(module->hw);
+    Assert(packet);
+
+#if I2C_MASTER_CALLBACK_MODE == true
+    /* Check if the I2C module is busy with a job. */
+    if (module->buffer_remaining > 0) {
+        return STATUS_BUSY;
+    }
+#endif
+
+    module->send_stop = false;
+    module->send_nack = false;
+
+    return _i2c_master_read_packet(module, packet);
+}
+
+/**
+ * \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 write packet.
+ * \retval STATUS_OK                    The packet was write 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);
+
+    /* 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) {
+        i2c_module->ADDR.reg = (packet->address << 1) | I2C_TRANSFER_WRITE |
+                               (packet->high_speed << SERCOM_I2CM_ADDR_HS_Pos) |
+                               SERCOM_I2CM_ADDR_TENBITEN;
+    } else {
+        i2c_module->ADDR.reg = (packet->address << 1) | I2C_TRANSFER_WRITE |
+                               (packet->high_speed << SERCOM_I2CM_ADDR_HS_Pos);
+    }
+    /* 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;
+}
+
+/**
+ * \brief Writes data packet to slave
+ *
+ * Writes a data packet to the specified slave address on the I<SUP>2</SUP>C bus
+ * and sends a stop condition when finished.
+ *
+ * \note This will stall the device from any other operation. For
+ *       interrupt-driven operation, see \ref i2c_master_read_packet_job.
+ *
+ * \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 write packet.
+ * \retval STATUS_OK                    If packet was write successfully
+ * \retval STATUS_BUSY                  If master module is busy with a job
+ * \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
+ * \retval STATUS_ERR_TIMEOUT           If timeout occurred
+ * \retval STATUS_ERR_OVERFLOW          If slave did not acknowledge last sent
+ *                                      data, indicating that slave does not
+ *                                      want more data and was not able to read
+ *                                      last data sent
+ */
+enum status_code i2c_master_write_packet_wait(
+    struct i2c_master_module *const module,
+    struct i2c_master_packet *const packet)
+{
+    /* Sanity check */
+    Assert(module);
+    Assert(module->hw);
+    Assert(packet);
+
+#if I2C_MASTER_CALLBACK_MODE == true
+    /* Check if the I2C module is busy with a job */
+    if (module->buffer_remaining > 0) {
+        return STATUS_BUSY;
+    }
+#endif
+
+    module->send_stop = true;
+    module->send_nack = true;
+
+    return _i2c_master_write_packet(module, packet);
+}
+
+/**
+ * \brief Writes data packet to slave without sending a stop condition when done
+ *
+ * Writes a data packet to the specified slave address on the I<SUP>2</SUP>C bus
+ * without sending a stop condition, thus retaining ownership of the bus when
+ * done. To end the transaction, a \ref i2c_master_read_packet_wait "read" or
+ * \ref i2c_master_write_packet_wait "write" with stop condition or sending a
+ * stop with the \ref i2c_master_send_stop function must be performed.
+ *
+ * \note This will stall the device from any other operation. For
+ *       interrupt-driven operation, see \ref i2c_master_read_packet_job.
+ *
+ * \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 write packet.
+ * \retval STATUS_OK                    If packet was write successfully
+ * \retval STATUS_BUSY                  If master module is busy
+ * \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
+ * \retval STATUS_ERR_TIMEOUT           If timeout occurred
+ * \retval STATUS_ERR_OVERFLOW          If slave did not acknowledge last sent
+ *                                      data, indicating that slave do not want
+ *                                      more data
+ */
+enum status_code i2c_master_write_packet_wait_no_stop(
+    struct i2c_master_module *const module,
+    struct i2c_master_packet *const packet)
+{
+    /* Sanity check */
+    Assert(module);
+    Assert(module->hw);
+    Assert(packet);
+
+#if I2C_MASTER_CALLBACK_MODE == true
+    /* Check if the I2C module is busy with a job */
+    if (module->buffer_remaining > 0) {
+        return STATUS_BUSY;
+    }
+#endif
+
+    module->send_stop = false;
+    module->send_nack = true;
+
+    return _i2c_master_write_packet(module, packet);
+}
+
+/**
+ * \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,out] 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);
+}
+
+/**
+ * \brief Sends nack signal on bus
+ *
+ * Sends a nack signal on bus.
+ *
+ * \note This function can only be used after the
+ *       \ref i2c_master_write_packet_wait_no_nack function,
+ *        or \ref i2c_master_read_byte function.
+ * \param[in,out] module  Pointer to the software instance struct
+ */
+void i2c_master_send_nack(struct i2c_master_module *const module)
+{
+    /* Sanity check */
+    Assert(module);
+    Assert(module->hw);
+
+    SercomI2cm *const i2c_module = &(module->hw->I2CM);
+
+    /* Send nack signal */
+    _i2c_master_wait_for_sync(module);
+    i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_ACKACT;
+}
+
+/**
+ * \brief Reads one byte data from slave
+ *
+ * \param[in,out]  module  Pointer to software module struct
+ * \param[out]     byte    Read one byte data to slave
+ *
+ * \return Status of reading byte.
+ * \retval STATUS_OK                    One byte 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
+ */
+enum status_code i2c_master_read_byte(
+    struct i2c_master_module *const module,
+    uint8_t *byte)
+{
+    enum status_code tmp_status;
+    SercomI2cm *const i2c_module = &(module->hw->I2CM);
+
+    i2c_module->CTRLB.reg &= ~SERCOM_I2CM_CTRLB_ACKACT;
+    /* Write byte to slave. */
+    _i2c_master_wait_for_sync(module);
+    *byte = i2c_module->DATA.reg;
+    /* Wait for response. */
+    tmp_status = _i2c_master_wait_for_bus(module);
+
+    return tmp_status;
+}
+
+/**
+ * \brief Write one byte data to slave
+ *
+ * \param[in,out]  module  Pointer to software module struct
+ * \param[in]      byte    Send one byte data to slave
+ *
+ * \return Status of writing byte.
+ * \retval STATUS_OK                    One byte was write 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
+ */
+enum status_code i2c_master_write_byte(
+    struct i2c_master_module *const module,
+    uint8_t byte)
+{
+    enum status_code tmp_status;
+    SercomI2cm *const i2c_module = &(module->hw->I2CM);
+
+    /* Write byte to slave. */
+    _i2c_master_wait_for_sync(module);
+    i2c_module->DATA.reg = byte;
+    /* Wait for response. */
+    tmp_status = _i2c_master_wait_for_bus(module);
+    return tmp_status;
+}