From 062ee461dab0a55f0e6fbcc91af42c76fed037e3 Mon Sep 17 00:00:00 2001 From: vimalrajr Date: Mon, 27 Jul 2015 16:33:51 +0530 Subject: [PATCH] Adding implementation for I2C APIs - master mode, slave mode, and master async interrupt mode. --- .../TARGET_SAM0/TARGET_SAMR21G18A/device.h | 5 +- .../drivers/sercom/i2c/i2c_master_interrupt.h | 202 ++++ .../i2c_master_interrupt.c | 633 +++++++++++ .../qs_i2c_master_callback.h | 131 +++ .../hal/TARGET_Atmel/TARGET_SAM0/i2c_api.c | 997 ++++++++++++++++++ .../hal/TARGET_Atmel/TARGET_SAM0/objects.h | 19 + workspace_tools/targets.py | 2 +- 7 files changed, 1986 insertions(+), 3 deletions(-) create mode 100644 libraries/mbed/targets/hal/TARGET_Atmel/TARGET_SAM0/drivers/sercom/i2c/i2c_master_interrupt.h create mode 100644 libraries/mbed/targets/hal/TARGET_Atmel/TARGET_SAM0/drivers/sercom/i2c/i2c_samd21_r21_d10_d11_l21/i2c_master_interrupt.c create mode 100644 libraries/mbed/targets/hal/TARGET_Atmel/TARGET_SAM0/drivers/sercom/i2c/quick_start_master_callback/qs_i2c_master_callback.h create mode 100644 libraries/mbed/targets/hal/TARGET_Atmel/TARGET_SAM0/i2c_api.c diff --git a/libraries/mbed/targets/hal/TARGET_Atmel/TARGET_SAM0/TARGET_SAMR21G18A/device.h b/libraries/mbed/targets/hal/TARGET_Atmel/TARGET_SAM0/TARGET_SAMR21G18A/device.h index 2f8646162c..3fb36eb1eb 100644 --- a/libraries/mbed/targets/hal/TARGET_Atmel/TARGET_SAM0/TARGET_SAMR21G18A/device.h +++ b/libraries/mbed/targets/hal/TARGET_Atmel/TARGET_SAM0/TARGET_SAMR21G18A/device.h @@ -29,8 +29,9 @@ #define DEVICE_SERIAL_FC 1 #define DEVICE_SERIAL_ASYNCH 1 -#define DEVICE_I2C 0 -#define DEVICE_I2CSLAVE 0 +#define DEVICE_I2C 1 +#define DEVICE_I2CSLAVE 1 +#define DEVICE_I2C_ASYNCH 1 #define DEVICE_SPI 1 #define DEVICE_SPISLAVE 1 diff --git a/libraries/mbed/targets/hal/TARGET_Atmel/TARGET_SAM0/drivers/sercom/i2c/i2c_master_interrupt.h b/libraries/mbed/targets/hal/TARGET_Atmel/TARGET_SAM0/drivers/sercom/i2c/i2c_master_interrupt.h new file mode 100644 index 0000000000..ca5580515d --- /dev/null +++ b/libraries/mbed/targets/hal/TARGET_Atmel/TARGET_SAM0/drivers/sercom/i2c/i2c_master_interrupt.h @@ -0,0 +1,202 @@ +/** + * \file + * + * \brief SAM SERCOM I2C Master Interrupt Driver + * + * Copyright (C) 2012-2014 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 Atmel Support +*/ + +#ifndef I2C_MASTER_INTERRUPT_H_INCLUDED +#define I2C_MASTER_INTERRUPT_H_INCLUDED + +#include "i2c_master.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup asfdoc_sam0_sercom_i2c_group + * @{ + * + */ + +/** + * \name Callbacks + * @{ + */ +#if !defined(__DOXYGEN__) +void _i2c_master_interrupt_handler( + uint8_t instance); +#endif + +void i2c_master_register_callback( + struct i2c_master_module *const module, + i2c_master_callback_t callback, + enum i2c_master_callback callback_type); + +void i2c_master_unregister_callback( + struct i2c_master_module *const module, + enum i2c_master_callback callback_type); + +/** + * \brief Enables callback + * + * Enables the callback specified by the callback_type. + * + * \param[in,out] module Pointer to the software module struct + * \param[in] callback_type Callback type to enable + */ +static inline void i2c_master_enable_callback( + struct i2c_master_module *const module, + enum i2c_master_callback callback_type) +{ + /* Sanity check. */ + Assert(module); + Assert(module->hw); + + /* Mark callback as enabled. */ + module->enabled_callback |= (1 << callback_type); +} + +/** + * \brief Disables callback + * + * Disables the callback specified by the callback_type. + * + * \param[in,out] module Pointer to the software module struct + * \param[in] callback_type Callback type to disable + */ +static inline void i2c_master_disable_callback( + struct i2c_master_module *const module, + enum i2c_master_callback callback_type) +{ + /* Sanity check. */ + Assert(module); + Assert(module->hw); + + /* Mark callback as disabled. */ + module->enabled_callback &= ~(1 << callback_type); +} + +/** @} */ + +/** + * \name Read and Write, Interrupt-Driven + * @{ + */ + +enum status_code i2c_master_read_packet_job( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet); + +enum status_code i2c_master_read_packet_job_no_stop( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet); + +enum status_code i2c_master_write_packet_job( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet); + +enum status_code i2c_master_write_packet_job_no_stop( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet); + +/** + * \brief Cancel any currently ongoing operation + * + * Terminates the running transfer operation. + * + * \param[in,out] module Pointer to software module structure + */ +static inline void i2c_master_cancel_job( + struct i2c_master_module *const module) +{ + /* Sanity check. */ + Assert(module); + Assert(module->hw); + + /* Set buffer to 0. */ + module->buffer_remaining = 0; + /* Update status*/ + module->status = STATUS_ABORTED; +} + +/** + * \brief Get status from ongoing job + * + * Will return the status of a transfer operation. + * + * \param[in] module Pointer to software module structure + * + * \return Last status code from transfer operation. + * \retval STATUS_OK No error has occurred + * \retval STATUS_BUSY If transfer is in progress + * \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 does not + * want more data and was not able to read + */ +static inline enum status_code i2c_master_get_job_status( + struct i2c_master_module *const module) +{ + /* Check sanity. */ + Assert(module); + Assert(module->hw); + + /* Return current status code. */ + return module->status; +} + +/** @} */ + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* I2C_MASTER_INTERRUPT_H_INCLUDED */ diff --git a/libraries/mbed/targets/hal/TARGET_Atmel/TARGET_SAM0/drivers/sercom/i2c/i2c_samd21_r21_d10_d11_l21/i2c_master_interrupt.c b/libraries/mbed/targets/hal/TARGET_Atmel/TARGET_SAM0/drivers/sercom/i2c/i2c_samd21_r21_d10_d11_l21/i2c_master_interrupt.c new file mode 100644 index 0000000000..166dc8921f --- /dev/null +++ b/libraries/mbed/targets/hal/TARGET_Atmel/TARGET_SAM0/drivers/sercom/i2c/i2c_samd21_r21_d10_d11_l21/i2c_master_interrupt.c @@ -0,0 +1,633 @@ +/** + * \file + * + * \brief SAM I2C Master Interrupt Driver + * + * Copyright (C) 2012-2014 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 Atmel Support +*/ + +#include "i2c_master_interrupt.h" + +extern enum status_code _i2c_master_wait_for_bus( + struct i2c_master_module *const module); + +extern enum status_code _i2c_master_address_response( + struct i2c_master_module *const module); + +extern enum status_code _i2c_master_send_hs_master_code( + struct i2c_master_module *const module, + uint8_t hs_master_code);; + +/** + * \internal + * Read next data. Used by interrupt handler to get next data byte from slave. + * + * \param[in,out] module Pointer to software module structure + */ +static void _i2c_master_read( + struct i2c_master_module *const module) +{ + /* Sanity check arguments. */ + Assert(module); + Assert(module->hw); + + SercomI2cm *const i2c_module = &(module->hw->I2CM); + bool sclsm_flag = i2c_module->CTRLA.bit.SCLSM; + + /* Find index to save next value in buffer */ + uint16_t buffer_index = module->buffer_length; + buffer_index -= module->buffer_remaining; + + module->buffer_remaining--; + + if (sclsm_flag) { + if (module->buffer_remaining == 1) { + /* Set action to NACK. */ + i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_ACKACT; + } + } else { + if (module->buffer_remaining == 0) { + /* Set action to NACK. */ + i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_ACKACT; + } + } + + if (module->buffer_remaining == 0) { + if (module->send_stop) { + /* Send stop condition */ + _i2c_master_wait_for_sync(module); + i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3); + } + } + + /* Read byte from slave and put in buffer */ + _i2c_master_wait_for_sync(module); + module->buffer[buffer_index] = i2c_module->DATA.reg; +} + +/** + * \internal + * + * Write next data. Used by interrupt handler to send next data byte to slave. + * + * \param[in,out] module Pointer to software module structure + */ +static void _i2c_master_write(struct i2c_master_module *const module) +{ + /* Sanity check arguments. */ + Assert(module); + Assert(module->hw); + + SercomI2cm *const i2c_module = &(module->hw->I2CM); + + /* Check for ack from slave */ + if (i2c_module->STATUS.reg & SERCOM_I2CM_STATUS_RXNACK) { + /* Set status */ + module->status = STATUS_ERR_OVERFLOW; + /* Do not write more data */ + return; + } + + /* Find index to get next byte in buffer */ + uint16_t buffer_index = module->buffer_length; + buffer_index -= module->buffer_remaining; + + module->buffer_remaining--; + + /* Write byte from buffer to slave */ + _i2c_master_wait_for_sync(module); + i2c_module->DATA.reg = module->buffer[buffer_index]; +} + +/** + * \internal + * Acts on slave address response. Checks for errors concerning master->slave + * handshake. + * + * \param[in,out] module Pointer to software module structure + */ +static void _i2c_master_async_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. Ignore bus-error; workaround for bus state stuck in + * BUSY. + */ + if (i2c_module->INTFLAG.reg & SERCOM_I2CM_INTFLAG_MB) { + /* Clear write interrupt flag */ + i2c_module->INTFLAG.reg = SERCOM_I2CM_INTENCLR_MB; + + /* Check arbitration */ + if (i2c_module->STATUS.reg & SERCOM_I2CM_STATUS_ARBLOST) { + /* Return busy */ + module->status = STATUS_ERR_PACKET_COLLISION; + } + } else if (i2c_module->STATUS.reg & SERCOM_I2CM_STATUS_RXNACK) { + /* Return bad address value */ + module->status = STATUS_ERR_BAD_ADDRESS; + module->buffer_remaining = 0; + + if (module->send_stop) { + /* Send stop condition */ + _i2c_master_wait_for_sync(module); + i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3); + } + } + + module->buffer_length = module->buffer_remaining; + + /* Check for status OK. */ + if (module->status == STATUS_BUSY) { + /* Call function based on transfer direction. */ + if (module->transfer_direction == I2C_TRANSFER_WRITE) { + _i2c_master_write(module); + } else { + _i2c_master_read(module); + } + } +} + +/** + * \brief Registers callback for the specified callback type + * + * Associates the given callback function with the + * specified callback type. + * + * To enable the callback, the \ref i2c_master_enable_callback function + * must be used. + * + * \param[in,out] module Pointer to the software module struct + * \param[in] callback Pointer to the function desired for the + * specified callback + * \param[in] callback_type Callback type to register + */ +void i2c_master_register_callback( + struct i2c_master_module *const module, + const i2c_master_callback_t callback, + enum i2c_master_callback callback_type) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + Assert(callback); + + /* Register callback */ + module->callbacks[callback_type] = callback; + + /* Set corresponding bit to set callback as registered */ + module->registered_callback |= (1 << callback_type); +} + +/** + * \brief Unregisters callback for the specified callback type + * + * When called, the currently registered callback for the given callback type + * will be removed. + * + * \param[in,out] module Pointer to the software module struct + * \param[in] callback_type Specifies the callback type to unregister + */ +void i2c_master_unregister_callback( + struct i2c_master_module *const module, + enum i2c_master_callback callback_type) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + + /* Register callback */ + module->callbacks[callback_type] = NULL; + + /* Clear corresponding bit to set callback as unregistered */ + module->registered_callback &= ~(1 << callback_type); +} + +/** + * \internal + * Starts a read packet operation. + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I2C packet to transfer + * + * \return Status of starting reading I2C 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 鈥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 { + return tmp_status; + } + } else { + /* Enable interrupts */ + i2c_module->INTENSET.reg = + SERCOM_I2CM_INTENSET_MB | SERCOM_I2CM_INTENSET_SB; + + /* Set address and direction bit. Will send start command on bus */ + i2c_module->ADDR.reg = (packet->address << 1) | I2C_TRANSFER_READ | + (packet->high_speed << SERCOM_I2CM_ADDR_HS_Pos); + } + + return STATUS_OK; +} + +/** + * \brief Initiates a read packet operation + * + * Reads a data packet from the specified slave address on the I2C + * bus. This is the non-blocking equivalent of \ref i2c_master_read_packet_wait. + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I2C packet to transfer + * + * \return Status of starting reading I2C packet. + * \retval STATUS_OK If reading was started successfully + * \retval STATUS_BUSY If module is currently busy with another transfer + */ +enum status_code i2c_master_read_packet_job( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + Assert(packet); + + /* Check if the I2C module is busy with a job */ + if (module->buffer_remaining > 0) { + return STATUS_BUSY; + } + + /* Make sure we send STOP */ + module->send_stop = true; + + /* Start reading */ + return _i2c_master_read_packet(module, packet); +} + +/** + * \brief Initiates a read packet operation without sending a STOP condition when done + * + * Reads a data packet from the specified slave address on the I2C 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 must be + * performed. + * + * This is the non-blocking equivalent of \ref i2c_master_read_packet_wait_no_stop. + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I2C packet to transfer + * + * \return Status of starting reading I2C packet. + * \retval STATUS_OK If reading was started successfully + * \retval STATUS_BUSY If module is currently busy with another operation + */ +enum status_code i2c_master_read_packet_job_no_stop( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + Assert(packet); + + /* Check if the I2C module is busy with a job */ + if (module->buffer_remaining > 0) { + return STATUS_BUSY; + } + + /* Make sure we don't send STOP */ + module->send_stop = false; + + /* Start reading */ + return _i2c_master_read_packet(module, packet); +} + +/** + * \internal Initiates a write packet operation + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I2C packet to transfer + * + * \return Status of starting writing I2C packet job. + * \retval STATUS_OK If writing was started successfully + * \retval STATUS_BUSY If module is currently busy with another transfer + */ +static enum status_code _i2c_master_write_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); + + /* 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; + + /* Save packet to software module */ + module->buffer = packet->data; + module->buffer_remaining = packet->data_length; + module->transfer_direction = I2C_TRANSFER_WRITE; + module->status = STATUS_BUSY; + + /* Enable interrupts */ + i2c_module->INTENSET.reg = + SERCOM_I2CM_INTENSET_MB | SERCOM_I2CM_INTENSET_SB; + + /* 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); + } + + return STATUS_OK; +} + +/** + * \brief Initiates a write packet operation + * + * Writes a data packet to the specified slave address on the I2C + * bus. This is the non-blocking equivalent of \ref i2c_master_write_packet_wait. + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I2C packet to transfer + * + * \return Status of starting writing I2C packet job. + * \retval STATUS_OK If writing was started successfully + * \retval STATUS_BUSY If module is currently busy with another transfer + */ +enum status_code i2c_master_write_packet_job( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + Assert(packet); + + /* Check if the I2C module is busy with another job. */ + if (module->buffer_remaining > 0) { + return STATUS_BUSY; + } + + /* Make sure we send STOP at end*/ + module->send_stop = true; + + /* Start write operation */ + return _i2c_master_write_packet(module, packet); +} + +/** + * \brief Initiates a write packet operation without sending a STOP condition when done + * + * Writes a data packet to the specified slave address on the I2C 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. + * + * This is the non-blocking equivalent of \ref i2c_master_write_packet_wait_no_stop. + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I2C packet to transfer + * + * \return Status of starting writing I2C packet job. + * \retval STATUS_OK If writing was started successfully + * \retval STATUS_BUSY If module is currently busy with another + */ +enum status_code i2c_master_write_packet_job_no_stop( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + Assert(packet); + + /* Check if the I2C module is busy with another job. */ + if (module->buffer_remaining > 0) { + return STATUS_BUSY; + } + + /* Do not send stop condition when done */ + module->send_stop = false; + + /* Start write operation */ + return _i2c_master_write_packet(module, packet); +} + +/** + * \internal + * Interrupt handler for I2C master. + * + * \param[in] instance SERCOM instance that triggered the interrupt + */ +void _i2c_master_interrupt_handler( + uint8_t instance) +{ + /* Get software module for callback handling */ + struct i2c_master_module *module = + (struct i2c_master_module*)_sercom_instances[instance]; + + Assert(module); + + SercomI2cm *const i2c_module = &(module->hw->I2CM); + bool sclsm_flag = i2c_module->CTRLA.bit.SCLSM; + + /* Combine callback registered and enabled masks */ + uint8_t callback_mask = module->enabled_callback; + callback_mask &= module->registered_callback; + + /* Check if the module should respond to address ack */ + if ((module->buffer_length <= 0) && (module->buffer_remaining > 0)) { + /* Call function for address response */ + _i2c_master_async_address_response(module); + + /* Check if buffer write is done */ + } else if ((module->buffer_length > 0) && (module->buffer_remaining <= 0) && + (module->status == STATUS_BUSY) && + (module->transfer_direction == I2C_TRANSFER_WRITE)) { + /* Stop packet operation */ + i2c_module->INTENCLR.reg = + SERCOM_I2CM_INTENCLR_MB | SERCOM_I2CM_INTENCLR_SB; + + module->buffer_length = 0; + module->status = STATUS_OK; + + if (module->send_stop) { + /* Send stop condition */ + _i2c_master_wait_for_sync(module); + i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3); + } + if (callback_mask & (1 << I2C_MASTER_CALLBACK_WRITE_COMPLETE)) { + module->callbacks[I2C_MASTER_CALLBACK_WRITE_COMPLETE](module); + } + + /* Continue buffer write/read */ + } else if ((module->buffer_length > 0) && (module->buffer_remaining > 0)) { + /* Check that bus ownership is not lost */ + if ((!(i2c_module->STATUS.reg & SERCOM_I2CM_STATUS_BUSSTATE(2))) && + (!(sclsm_flag && (module->buffer_remaining == 1)))) { + module->status = STATUS_ERR_PACKET_COLLISION; + } else if (module->transfer_direction == I2C_TRANSFER_WRITE) { + _i2c_master_write(module); + } else { + _i2c_master_read(module); + } + } + + /* Check if read buffer transfer is complete */ + if ((module->buffer_length > 0) && (module->buffer_remaining <= 0) && + (module->status == STATUS_BUSY) && + (module->transfer_direction == I2C_TRANSFER_READ)) { + + /* Stop packet operation */ + i2c_module->INTENCLR.reg = + SERCOM_I2CM_INTENCLR_MB | SERCOM_I2CM_INTENCLR_SB; + module->buffer_length = 0; + module->status = STATUS_OK; + + /* Call appropriate callback if enabled and registered */ + if ((callback_mask & (1 << I2C_MASTER_CALLBACK_READ_COMPLETE)) + && (module->transfer_direction == I2C_TRANSFER_READ)) { + module->callbacks[I2C_MASTER_CALLBACK_READ_COMPLETE](module); + } else if ((callback_mask & (1 << I2C_MASTER_CALLBACK_WRITE_COMPLETE)) + && (module->transfer_direction == I2C_TRANSFER_WRITE)) { + module->callbacks[I2C_MASTER_CALLBACK_WRITE_COMPLETE](module); + } + } + + /* Check for error */ + if ((module->status != STATUS_BUSY) && (module->status != STATUS_OK)) { + /* Stop packet operation */ + i2c_module->INTENCLR.reg = SERCOM_I2CM_INTENCLR_MB | + SERCOM_I2CM_INTENCLR_SB; + + module->buffer_length = 0; + module->buffer_remaining = 0; + + /* Send nack and stop command unless arbitration is lost */ + if ((module->status != STATUS_ERR_PACKET_COLLISION) && + module->send_stop) { + _i2c_master_wait_for_sync(module); + i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_ACKACT | + SERCOM_I2CM_CTRLB_CMD(3); + } + + /* Call error callback if enabled and registered */ + if (callback_mask & (1 << I2C_MASTER_CALLBACK_ERROR)) { + module->callbacks[I2C_MASTER_CALLBACK_ERROR](module); + } + } +} diff --git a/libraries/mbed/targets/hal/TARGET_Atmel/TARGET_SAM0/drivers/sercom/i2c/quick_start_master_callback/qs_i2c_master_callback.h b/libraries/mbed/targets/hal/TARGET_Atmel/TARGET_SAM0/drivers/sercom/i2c/quick_start_master_callback/qs_i2c_master_callback.h new file mode 100644 index 0000000000..e07fab28c2 --- /dev/null +++ b/libraries/mbed/targets/hal/TARGET_Atmel/TARGET_SAM0/drivers/sercom/i2c/quick_start_master_callback/qs_i2c_master_callback.h @@ -0,0 +1,131 @@ +/** + * \file + * + * \brief SAM SERCOM I2C Master Interface Driver + * + * Copyright (C) 2012-2014 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * 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 + * + */ + +/** + * \page asfdoc_sam0_sercom_i2c_master_callback_use_case Quick Start Guide for SERCOM I2C Master - Callback + * + * In this use case, the I2C will used and set up as follows: + * - Master mode + * - 100KHz operation speed + * - Not operational in standby + * - 65535 unknown bus state timeout value + * + * \section asfdoc_sam0_sercom_i2c_master_callback_use_case_prereq Prerequisites + * The device must be connected to an I2C slave. + * + * \section asfdoc_sam0_sercom_i2c_master_callback_use_case_setup Setup + * + * \subsection asfdoc_sam0_sercom_i2c_master_callback_use_case_setup_code Code + * The following must be added to the user application: + * + * A sample buffer to write from, a reversed buffer to write from and length of + * buffers. + * \snippet qs_i2c_master_callback.c packet_data + * + * Address of slave: + * \snippet qs_i2c_master_callback.c address + * + * Globally accessible module structure: + * \snippet qs_i2c_master_callback.c dev_inst + * + * Globally accessible packet: + * \snippet qs_i2c_master_callback.c packet_glob + * + * Function for setting up module: + * \snippet qs_i2c_master_callback.c initialize_i2c + * + * Callback function for write complete: + * \snippet qs_i2c_master_callback.c callback_func + * + * Function for setting up the callback functionality of the driver: + * \snippet qs_i2c_master_callback.c setup_callback + * + * Add to user application \c main(): + * \snippet qs_i2c_master_callback.c run_initialize_i2c + * + * \subsection asfdoc_sam0_sercom_i2c_master_callback_use_case_setup_workflow Workflow + * -# Configure and enable module. + * \snippet qs_i2c_master_callback.c config + * -# Create and initialize configuration structure. + * \snippet qs_i2c_master_callback.c init_conf + * -# Change settings in the configuration. + * \snippet qs_i2c_master_callback.c conf_change + * -# Initialize the module with the set configurations. + * \snippet qs_i2c_master_callback.c init_module + * -# Enable the module. + * \snippet qs_i2c_master_callback.c enable_module + * -# Configure callback functionality. + * \snippet qs_i2c_master_callback.c config_callback + * -# Register write complete callback. + * \snippet qs_i2c_master_callback.c callback_reg + * -# Enable write complete callback. + * \snippet qs_i2c_master_callback.c callback_en + * -# Create a packet to send to slave. + * \snippet qs_i2c_master_callback.c write_packet + * + * \section asfdoc_sam0_sercom_i2c_master_callback_use_case_implementation Implementation + * \subsection asfdoc_sam0_sercom_i2c_master_callback_use_case_code Code + * Add to user application \c main(): + * \snippet qs_i2c_master_callback.c while + * \subsection asfdoc_sam0_sercom_i2c_master_callback_use_case_implementation_workflow Workflow + * -# Write packet to slave. + * \snippet qs_i2c_master_callback.c write_packet + * -# Infinite while loop, while waiting for interaction with slave. + * \snippet qs_i2c_master_callback.c while + * + * \section asfdoc_sam0_sercom_i2c_master_callback_use_case_callback Callback + * Each time a packet is sent, the callback function will be called. + * + * \subsection asfdoc_sam0_sercom_i2c_master_callback_use_case_callback_workflow Workflow + * - Write complete callback: + * -# Send every other packet in reversed order. + * \snippet qs_i2c_master_callback.c revert_order + * -# Write new packet to slave. + * \snippet qs_i2c_master_callback.c write_packet + * + */ +/** +* Support and FAQ: visit Atmel Support +*/ + +#include +#include + diff --git a/libraries/mbed/targets/hal/TARGET_Atmel/TARGET_SAM0/i2c_api.c b/libraries/mbed/targets/hal/TARGET_Atmel/TARGET_SAM0/i2c_api.c new file mode 100644 index 0000000000..a063ebc14e --- /dev/null +++ b/libraries/mbed/targets/hal/TARGET_Atmel/TARGET_SAM0/i2c_api.c @@ -0,0 +1,997 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2013 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "mbed_assert.h" +#include "dma_api.h" +#include "i2c_api.h" + +#include + +#include "cmsis.h" +#include "pinmap.h" +#include "sercom.h" +#include "i2c_master.h" +#include "i2c_slave.h" + +#include "pinmap_function.h" + +#if DEVICE_I2C_ASYNCH +#include "i2c_master_interrupt.h" +#endif + + +#if DEVICE_I2C_ASYNCH +#define pI2C_S(obj) (&obj->i2c) +#else +#define pI2C_S(obj) obj +#endif + +#define I2C_MASTER_DEFAULT_BAUD 100000 + +/** + * \brief I2C modes enum + * + * I2C mode selection. + */ +enum i2c_mode { + /** Master mode. */ + I2C_MODE_MASTER = 0x5, + /** Slave mode. */ + I2C_MODE_SLAVE = 0x4, +}; + +/* Extern Variables */ +extern uint8_t g_sys_init; + +typedef void (*I2CHandler)(void); + +#if DEVICE_I2C_ASYNCH +#define _SERCOM_INTERRUPT_HANDLERS(n, unused) (uint32_t)SERCOM##n##_Handler, + +/* To save the i2c objects */ +static uint32_t i2c_instances[SERCOM_INST_NUM] = {0}; +const uint32_t sercom_irq_handlers[SERCOM_INST_NUM] = { + MREPEAT(SERCOM_INST_NUM, _SERCOM_INTERRUPT_HANDLERS, ~) +}; +#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); + +/* Adding function from ASF for compatibility */ +static enum status_code _i2c_slave_wait_for_bus( + struct i2c_slave_module *const module) +{ + /* Sanity check arguments. */ + MBED_ASSERT(module); + MBED_ASSERT(module->hw); + + SercomI2cs *const i2c_hw = &(module->hw->I2CS); + + /* Wait for reply. */ + uint16_t timeout_counter = 0; + while ((!(i2c_hw->INTFLAG.reg & SERCOM_I2CS_INTFLAG_DRDY)) && + (!(i2c_hw->INTFLAG.reg & SERCOM_I2CS_INTFLAG_PREC)) && + (!(i2c_hw->INTFLAG.reg & SERCOM_I2CS_INTFLAG_AMATCH))) { + + /* Check timeout condition. */ + if (++timeout_counter >= module->buffer_timeout) { + return STATUS_ERR_TIMEOUT; + } + } + return STATUS_OK; +} + +/** Initialize the I2C peripheral + * + * Configures the pins used by I2C, sets a default format and frequency, and enables the peripheral + * @param[out] obj The I2C object to initialize + * @param[in] sda The pin to use for SDA + * @param[in] scl The pin to use for SCL + * @return void + */ +void i2c_init(i2c_t *obj, PinName sda, PinName scl) +{ + Sercom* hw; + uint32_t mux_func; + struct i2c_master_config config_i2c_master; + + /* Sanity check arguments */ + MBED_ASSERT(obj); + MBED_ASSERT(sda != NC); + MBED_ASSERT(scl != NC); + + if (g_sys_init == 0) { + system_init(); + g_sys_init = 1; + } + + pI2C_S(obj)->pins[0] = sda; + pI2C_S(obj)->pins[1] = scl; + + /* Calculate SERCOM instance from pins */ + uint32_t sercom_index = pinmap_merge_sercom(sda, scl); + if (sercom_index == (uint32_t)NC) { + return; + } + hw = (Sercom*)pinmap_peripheral_sercom(NC, sercom_index); + + i2c_master_get_config_defaults(&config_i2c_master); + + /* SERCOM PAD0 - SDA */ + mux_func = pinmap_function_sercom(sda, sercom_index); + if (mux_func == (uint32_t)NC) return; + config_i2c_master.pinmux_pad0 = (sda << 16) | (mux_func & 0xFFFF); + + /* SERCOM PAD1 - SCL */ + mux_func = pinmap_function_sercom(scl, sercom_index); + if (mux_func == (uint32_t)NC) return; + config_i2c_master.pinmux_pad1 = (scl << 16) | (mux_func & 0xFFFF); + + config_i2c_master.start_hold_time = I2C_MASTER_START_HOLD_TIME_DISABLED; + + /* Default baud rate is set to 100kHz */ + pI2C_S(obj)->baud_rate = I2C_MASTER_DEFAULT_BAUD; + config_i2c_master.baud_rate = pI2C_S(obj)->baud_rate / 1000; + + while(i2c_master_init(&pI2C_S(obj)->master, hw, &config_i2c_master) != STATUS_OK); + pI2C_S(obj)->mode = I2C_MODE_MASTER; + +#if DEVICE_I2C_ASYNCH + /* Save the i2c object */ + i2c_instances[sercom_index] = (uint32_t)obj; +#endif + + i2c_master_enable(&pI2C_S(obj)->master); +} + +/** Configure the I2C frequency. + * @param obj The i2c object + * @param hz Frequency in Hz + */ +void i2c_frequency(i2c_t *obj, int hz) +{ + /* Temporary variables. */ + int32_t baud_rate; + int32_t tmp_baud; + int32_t tmp_baud_hs; + enum status_code tmp_status_code; + + /* Sanity check arguments */ + MBED_ASSERT(obj); + MBED_ASSERT(pI2C_S(obj)->master.hw); + + /* Return if in Slave mode, slave do not have any baud to set */ + if (pI2C_S(obj)->mode != I2C_MODE_MASTER) return; + + SercomI2cm *const i2c_module = &(pI2C_S(obj)->master.hw->I2CM); + + /* Disable I2C Module */ + i2c_master_disable(&pI2C_S(obj)->master); + + baud_rate = hz / 1000; /* To kHz */ + + /* Give a dummy supported value */ + pI2C_S(obj)->baud_rate_high_speed = I2C_MASTER_BAUD_RATE_3400KHZ; + + uint32_t sercom_index = _sercom_get_sercom_inst_index(pI2C_S(obj)->master.hw); + + /* Find and set baudrate. */ + tmp_baud = (int32_t)(div_ceil( + system_gclk_chan_get_hz(SERCOM0_GCLK_ID_CORE + sercom_index), (2000*(baud_rate))) - 5); + + /* 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*(pI2C_S(obj)->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); + pI2C_S(obj)->baud_rate = hz; + } + + /* Enable back the I2C Module */ + i2c_master_enable(&pI2C_S(obj)->master); +} + +/** Send START command. + * @param obj The i2c object + */ +int i2c_start(i2c_t *obj) +{ + /* Sanity check arguments */ + MBED_ASSERT(obj); + + if (pI2C_S(obj)->mode == I2C_MODE_MASTER) { + pI2C_S(obj)->start_pending = 1; + } + return 0; +} + +/** Send STOP command. + * @param obj The i2c object + */ +int i2c_stop(i2c_t *obj) +{ + /* Sanity check arguments */ + MBED_ASSERT(obj); + + if (pI2C_S(obj)->mode == I2C_MODE_MASTER) { + /* Send STOP command */ + i2c_master_send_stop(&pI2C_S(obj)->master); + } else { + SercomI2cs *const i2c_hw = &(pI2C_S(obj)->slave.hw->I2CS); + /* Release line and wait for next start condition */ + i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_CMD(0x2); + } + + pI2C_S(obj)->start_pending = 0; + + /* TODO: Wait till STOP is send */ + return 0; +} + +/** Blocking reading data. + * @param obj The i2c object + * @param address 7-bit address (last bit is 1) + * @param data The buffer for receiving + * @param length Number of bytes to read + * @param stop Stop to be generated after the transfer is done + * @return Number of read bytes + */ +int i2c_read(i2c_t *obj, int address, char *data, int length, int stop) +{ + /* Sanity check arguments */ + MBED_ASSERT(obj); + MBED_ASSERT(pI2C_S(obj)->master.hw); + + enum status_code tmp_status; + +#if DEVICE_I2C_ASYNCH + if (i2c_active(obj)) { + /* I2C is busy with a job */ + return 0; + } +#endif + + struct i2c_master_packet packet; + packet.address = address >> 1; + packet.data_length = length; + packet.data = (uint8_t*)data; + packet.ten_bit_address = false; + packet.high_speed = false; + + if (stop) { + tmp_status = i2c_master_read_packet_wait(&pI2C_S(obj)->master, &packet); + } else { + tmp_status = i2c_master_read_packet_wait_no_stop(&pI2C_S(obj)->master, &packet); + } + + if (tmp_status == STATUS_OK) { + return length; + } else { + /* Currently, no way to track no of bytes received, so return 0 if fail */ + return 0; + } +} + +/** Blocking sending data. + * @param obj The i2c object + * @param address 7-bit address (last bit is 0) + * @param data The buffer for sending + * @param length Number of bytes to write + * @param stop Stop to be generated after the transfer is done + * @return Number of written bytes + */ +int i2c_write(i2c_t *obj, int address, const char *data, int length, int stop) +{ + /* Sanity check arguments */ + MBED_ASSERT(obj); + MBED_ASSERT(pI2C_S(obj)->master.hw); + + enum status_code tmp_status; + +#if DEVICE_I2C_ASYNCH + if (i2c_active(obj)) { + /* I2C is busy with a job */ + return 0; + } +#endif + + struct i2c_master_packet packet; + packet.address = address >> 1; + packet.data_length = length; + packet.data = data; + packet.ten_bit_address = false; + packet.high_speed = false; + + if (stop) { + tmp_status = i2c_master_write_packet_wait(&pI2C_S(obj)->master, &packet); + } else { + tmp_status = i2c_master_write_packet_wait_no_stop(&pI2C_S(obj)->master, &packet); + } + + if (tmp_status == STATUS_OK) { + return length; + } else { + /* Currently, no way to track no of bytes transmitted, so return 0 if fail */ + return 0; + } +} + +/** Reset I2C peripheral. TODO: The action here. Most of the implementation sends stop(). + * @param obj The i2c object + */ +void i2c_reset(i2c_t *obj) +{ + /* Sanity check arguments */ + MBED_ASSERT(obj); + + /* Send STOP */ + i2c_stop(obj); + + if (pI2C_S(obj)->mode == I2C_MODE_MASTER) { + pI2C_S(obj)->start_pending = 0; + /* Reset the master module */ + i2c_master_reset(&pI2C_S(obj)->master); + } else { + /* Reset the slave module */ + i2c_slave_reset(&pI2C_S(obj)->slave); + } +} + +/** Write address preceded by START condition. + * @param obj The i2c object + * @param address Address to be placed + * @param rw_flag read or write flag + * @return 1 if NAK was received, 0 if ACK was received, 2 for timeout. + */ +int i2c_write_address(i2c_t *obj, int address, int rw_flag) +{ + /* Sanity check arguments */ + MBED_ASSERT(obj); + MBED_ASSERT(pI2C_S(obj)->master.hw); + + enum status_code tmp_status; + SercomI2cm *const i2c_module = &(pI2C_S(obj)->master.hw->I2CM); + + _i2c_master_wait_for_sync(&pI2C_S(obj)->master); + + /* Set action to ACK. */ + i2c_module->CTRLB.reg &= ~SERCOM_I2CM_CTRLB_ACKACT; + + i2c_module->ADDR.reg = (address << 1) | rw_flag | (0 << SERCOM_I2CM_ADDR_HS_Pos); + + /* Wait for response on bus. */ + tmp_status = _i2c_master_wait_for_bus(&pI2C_S(obj)->master); + /* Check for error. */ + if (tmp_status != STATUS_OK) { + return I2C_ERROR_BUS_BUSY; + } + /* Check for address response error unless previous error is detected. */ + tmp_status = _i2c_master_address_response(&pI2C_S(obj)->master); + if (tmp_status != STATUS_OK) { + return I2C_ERROR_NO_SLAVE; + } + + return 0; +} + +/** Read one byte. + * @param obj The i2c object + * @param last Acknowledge + * @return The read byte + */ +int i2c_byte_read(i2c_t *obj, int last) +{ + int data = -1; + /* Sanity check arguments */ + MBED_ASSERT(obj); + MBED_ASSERT(pI2C_S(obj)->master.hw); + + enum status_code tmp_status; + + if (pI2C_S(obj)->mode == I2C_MODE_MASTER) { + SercomI2cm *const i2c_module = &(pI2C_S(obj)->master.hw->I2CM); + + if (last) { + /* Set action to nack. */ + i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_ACKACT; + } else { + /* Set action to ack. */ + i2c_module->CTRLB.reg &= ~SERCOM_I2CM_CTRLB_ACKACT; + } + + /* Check that bus ownership is not lost. */ + if (!(i2c_module->STATUS.reg & SERCOM_I2CM_STATUS_BUSSTATE(2))) { + return -1; /* Return invalid data*/ + } + + /* Save data to buffer. */ + _i2c_master_wait_for_sync(&pI2C_S(obj)->master); + data = i2c_module->DATA.reg; + /* Wait for response. */ + tmp_status = _i2c_master_wait_for_bus(&pI2C_S(obj)->master); + + /* Check for error. */ + if (tmp_status != STATUS_OK) { + return -1; /* Return invalid data*/ + } + + } else { +#if DEVICE_I2CSLAVE + SercomI2cs *const i2c_hw = &(pI2C_S(obj)->slave.hw->I2CS); + + /* 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 -1; /* Return invalid data*/ + } + + if (i2c_hw->INTFLAG.reg & SERCOM_I2CS_INTFLAG_AMATCH) { + /* Request from master, Address not yet acknowledged */ + i2c_hw->CTRLB.reg &= ~SERCOM_I2CS_CTRLB_ACKACT; + i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_CMD(0x3); + i2c_hw->INTFLAG.reg = SERCOM_I2CS_INTFLAG_AMATCH; + } + if (last) { + /* Set action to nack. */ + i2c_hw->CTRLB.reg |= SERCOM_I2CM_CTRLB_ACKACT; + } else { + /* Set action to ack. */ + i2c_hw->CTRLB.reg &= ~SERCOM_I2CM_CTRLB_ACKACT; + } + + /* Wait for next byte or stop condition */ + tmp_status = _i2c_slave_wait_for_bus(&pI2C_S(obj)->slave); + if (tmp_status != STATUS_OK) { + /* Timeout, return */ + return -1; + } + + if (i2c_hw->INTFLAG.reg & SERCOM_I2CS_INTFLAG_PREC) { + /* Master sent stop condition, or repeated start, read done */ + /* Clear stop flag */ + i2c_hw->INTFLAG.reg = SERCOM_I2CS_INTFLAG_PREC; + return -1; + } + + /* Read data */ + _i2c_slave_wait_for_sync(&pI2C_S(obj)->slave); + data = i2c_hw->DATA.reg; +#endif + } + + return data; +} + +/** Write one byte. + * @param obj The i2c object + * @param data Byte to be written + * @return 1 if NAK was received, 0 if ACK was received, 2 for timeout. + */ +int i2c_byte_write(i2c_t *obj, int data) +{ + /* Sanity check arguments */ + MBED_ASSERT(obj); + MBED_ASSERT(pI2C_S(obj)->master.hw); + + enum status_code tmp_status; + + if (pI2C_S(obj)->mode == I2C_MODE_MASTER) { + SercomI2cm *const i2c_module = &(pI2C_S(obj)->master.hw->I2CM); + + if (pI2C_S(obj)->start_pending) { + pI2C_S(obj)->start_pending = 0; + /* Write address */ + return i2c_write_address(obj, (data >> 1), (data & 0x01)); + } else { + /* Write data */ + /* Check that bus ownership is not lost. */ + if (!(i2c_module->STATUS.reg & SERCOM_I2CM_STATUS_BUSSTATE(2))) { + return I2C_ERROR_NO_SLAVE; + } + + /* Write byte to slave. */ + _i2c_master_wait_for_sync(&pI2C_S(obj)->master); + i2c_module->DATA.reg = data; + + /* Wait for response. */ + tmp_status = _i2c_master_wait_for_bus(&pI2C_S(obj)->master); + /* Check for error. */ + if (tmp_status != STATUS_OK) { + return I2C_ERROR_BUS_BUSY; + } + + /* Check for NACK from slave. */ + if (i2c_module->STATUS.reg & SERCOM_I2CM_STATUS_RXNACK) { + /* Return bad data value. */ + return I2C_ERROR_NO_SLAVE; + } + } + } else { +#if DEVICE_I2CSLAVE + SercomI2cs *const i2c_hw = &(pI2C_S(obj)->slave.hw->I2CS); + + if (i2c_hw->INTFLAG.reg & SERCOM_I2CS_INTFLAG_AMATCH) { + /* Read request from master, Address not yet acknowledged */ + i2c_hw->CTRLB.reg &= ~SERCOM_I2CS_CTRLB_ACKACT; + i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_CMD(0x3); + i2c_hw->INTFLAG.reg = SERCOM_I2CS_INTFLAG_AMATCH; + } + + /* Write data */ + _i2c_slave_wait_for_sync(&pI2C_S(obj)->slave); + i2c_hw->DATA.reg = data; + + /* Wait for response from master */ + tmp_status = _i2c_slave_wait_for_bus(&pI2C_S(obj)->slave); + + if (tmp_status != STATUS_OK) { + /* Timeout, return */ + return I2C_ERROR_BUS_BUSY; + } + + if (i2c_hw->STATUS.reg & SERCOM_I2CS_STATUS_RXNACK) { + /* NACK from master, abort */ + /* Release line */ + i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_CMD(0x02); + + return I2C_ERROR_NO_SLAVE; + } +#endif + } + + return 0; +} + + +#if DEVICE_I2CSLAVE + +/** + * \defgroup SynchI2C Synchronous I2C Hardware Abstraction Layer for slave + * @{ + */ + +/** Configure I2C as slave or master. + * @param obj The I2C object + * @param enable_slave configure I2C in slave mode or not + * @return void + */ +void i2c_slave_mode(i2c_t *obj, int enable_slave) +{ + int i; + /* Sanity check arguments */ + MBED_ASSERT(obj); + MBED_ASSERT(pI2C_S(obj)->master.hw); + + uint32_t mux_func[2]; + uint32_t sercom_index = _sercom_get_sercom_inst_index(pI2C_S(obj)->master.hw); + for (i=0; i<2; i++) { + mux_func[i] = pinmap_function_sercom(pI2C_S(obj)->pins[0], sercom_index); + if (mux_func[i] == NC) return; + } + + if (enable_slave) { + /* Disable I2C Master module if active */ + i2c_master_disable(&pI2C_S(obj)->master); + + struct i2c_slave_config config_i2c_slave; + i2c_slave_get_config_defaults(&config_i2c_slave); + + /* SERCOM PAD0 - SDA */ + config_i2c_slave.pinmux_pad0 = (pI2C_S(obj)->pins[0] << 16) | (mux_func[0] & 0xFFFF); + /* SERCOM PAD1 - SCL */ + config_i2c_slave.pinmux_pad1 = (pI2C_S(obj)->pins[1] << 16) | (mux_func[1] & 0xFFFF); + + config_i2c_slave.sda_hold_time = I2C_SLAVE_SDA_HOLD_TIME_DISABLED; + + i2c_slave_init(&pI2C_S(obj)->slave, pI2C_S(obj)->master.hw, &config_i2c_slave); + + pI2C_S(obj)->mode = I2C_MODE_SLAVE; + + i2c_slave_enable(&pI2C_S(obj)->slave); + } else { + if ((pI2C_S(obj)->master.hw) && (pI2C_S(obj)->mode == I2C_MODE_MASTER)) { + /* Already configured, enable and return */ + i2c_master_enable(&pI2C_S(obj)->master); + return; + } else if ((pI2C_S(obj)->slave.hw) && (pI2C_S(obj)->mode == I2C_MODE_SLAVE)) { + /* Disable slave */ + i2c_slave_disable(&pI2C_S(obj)->slave); + } + + struct i2c_master_config config_i2c_master; + /* SERCOM PAD0 - SDA */ + config_i2c_master.pinmux_pad0 = (pI2C_S(obj)->pins[0] << 16) | (mux_func[0] & 0xFFFF); + /* SERCOM PAD1 - SCL */ + config_i2c_master.pinmux_pad1 = (pI2C_S(obj)->pins[1] << 16) | (mux_func[1] & 0xFFFF); + /* Baud rate */ + config_i2c_master.baud_rate = pI2C_S(obj)->baud_rate / 1000; + + while(i2c_master_init(&pI2C_S(obj)->master, pI2C_S(obj)->master.hw, &config_i2c_master) != STATUS_OK); + pI2C_S(obj)->mode = I2C_MODE_MASTER; + + i2c_master_enable(&pI2C_S(obj)->master); + } +} + +/** Check to see if the I2C slave has been addressed. + * @param obj The I2C object + * @return The status - 1 - read addresses, 2 - write to all slaves, + * 3 write addressed, 0 - the slave has not been addressed + */ +int i2c_slave_receive(i2c_t *obj) +{ + /* Sanity check arguments */ + MBED_ASSERT(obj); + MBED_ASSERT(pI2C_S(obj)->slave.hw); + + SercomI2cs *const i2c_module = &(pI2C_S(obj)->slave.hw->I2CS); + + /* TODO: Currently not checking "write to all" condition */ + if (i2c_module->INTFLAG.reg & SERCOM_I2CS_INTFLAG_AMATCH) { + if (i2c_module->STATUS.reg & SERCOM_I2CS_STATUS_DIR) { + /* Slave is read addressed */ + return 1; + } else { + /* Slave is write addressed */ + return 3; + } + } + + return 0; +} + +/** Blocking reading data. + * @param obj The i2c slave object + * @param data The buffer for receiving + * @param length Number of bytes to read + * @return Number of read bytes + */ +int i2c_slave_read(i2c_t *obj, char *data, int length) +{ + /* Sanity check arguments */ + MBED_ASSERT(obj); + MBED_ASSERT(pI2C_S(obj)->slave.hw); + + if (!data || !length) return 0; + + enum status_code tmp_status; + + struct i2c_slave_packet packet; + packet.data_length = length; + packet.data = (uint8_t*)data; + + tmp_status = i2c_slave_read_packet_wait(&pI2C_S(obj)->slave, &packet); + + if (tmp_status == STATUS_OK) { + return length; + } else { + /* Currently, no way to track no of bytes transmitted, so return 0 */ + return 0; + } + +} + +/** Blocking writing data. + * @param obj The i2c slave object + * @param data The buffer for transmitting + * @param length Number of bytes to write + * @return Number of bytes written + */ +int i2c_slave_write(i2c_t *obj, const char *data, int length) +{ + /* Sanity check arguments */ + MBED_ASSERT(obj); + MBED_ASSERT(pI2C_S(obj)->slave.hw); + + if (!data || !length) return 0; + + enum status_code tmp_status; + + struct i2c_slave_packet packet; + packet.data_length = length; + packet.data = data; + + tmp_status = i2c_slave_write_packet_wait(&pI2C_S(obj)->slave, &packet); + + if (tmp_status == STATUS_OK) { + return length; + } else { + /* Currently, no way to track no of bytes transmitted, so return 0 */ + return 0; + } +} + +/** Configure I2C slave address. + * @param obj The I2C object + * @param idx Currently not used + * @param address The address to be set + * @param mask Currently not used + */ +void i2c_slave_address(i2c_t *obj, int idx, uint32_t address, uint32_t mask) +{ + /* Sanity check arguments */ + MBED_ASSERT(obj); + MBED_ASSERT(pI2C_S(obj)->slave.hw); + + /* Disable I2C Module */ + i2c_slave_disable(&pI2C_S(obj)->slave); + + SercomI2cs *const i2c_hw = &(pI2C_S(obj)->slave.hw->I2CS); + + address = address >> 1; + if (!mask) { + mask = (0xFE >> 1); + } + /* Set the address and address mask */ + i2c_hw->ADDR.reg = (address << SERCOM_I2CS_ADDR_ADDR_Pos) | + (mask << SERCOM_I2CS_ADDR_ADDRMASK_Pos) | + (0 << SERCOM_I2CS_ADDR_TENBITEN_Pos) | + (1 << SERCOM_I2CS_ADDR_GENCEN_Pos); + + /* Enable I2C Module */ + i2c_slave_enable(&pI2C_S(obj)->slave); +} + +#endif /* DEVICE_I2CSLAVE */ + +/**@}*/ + +#if DEVICE_I2C_ASYNCH + +/** + * \defgroup AsynchI2C Asynchronous I2C Hardware Abstraction Layer + * @{ + */ + +/** +* \internal +* Callback for transfer finish. +* +* \param[in,out] module Pointer to SPI software instance struct +*/ +void i2c_transfer_complete_callback(struct i2c_master_module *const module) +{ + uint32_t sercom_index = _sercom_get_sercom_inst_index(module->hw); + + if (sercom_index >= SERCOM_INST_NUM) { + /* TODO: Abort operation */ + return; + } + + i2c_t *obj = (i2c_t*)i2c_instances[sercom_index]; + + i2c_master_disable_callback(&pI2C_S(obj)->master, I2C_MASTER_CALLBACK_WRITE_COMPLETE); + i2c_master_disable_callback(&pI2C_S(obj)->master, I2C_MASTER_CALLBACK_READ_COMPLETE); + i2c_master_disable_callback(&pI2C_S(obj)->master, I2C_MASTER_CALLBACK_ERROR); + + /* Call the handler function */ + if (pI2C_S(obj)->handler) { + ((I2CHandler)pI2C_S(obj)->handler)(); + } +} + +/** +* \internal +* Callback for write complete. Initiate read from here. +* +* \param[in,out] module Pointer to SPI software instance struct +*/ +void i2c_write_complete_callback(struct i2c_master_module *const module) +{ + uint32_t sercom_index = _sercom_get_sercom_inst_index(module->hw); + + if (sercom_index >= SERCOM_INST_NUM) { + /* TODO: Abort operation */ + return; + } + + i2c_t *obj = (i2c_t*)i2c_instances[sercom_index]; + + if (!(pI2C_S(obj)->rd_packet.data) || (pI2C_S(obj)->rd_packet.data_length == 0)) { + /* Call the handler function */ + if (pI2C_S(obj)->handler) { + ((I2CHandler)pI2C_S(obj)->handler)(); + } + } else { + i2c_master_disable_callback(&pI2C_S(obj)->master, I2C_MASTER_CALLBACK_WRITE_COMPLETE); + + /* TODO: Register read complete callback */ + i2c_master_register_callback(&pI2C_S(obj)->master, i2c_transfer_complete_callback, I2C_MASTER_CALLBACK_READ_COMPLETE); + i2c_master_enable_callback(&pI2C_S(obj)->master, I2C_MASTER_CALLBACK_READ_COMPLETE); + + /* Initiate read operation */ + if (pI2C_S(obj)->master.send_stop) { + i2c_master_read_packet_job(&pI2C_S(obj)->master,&pI2C_S(obj)->rd_packet); + } else { + i2c_master_read_packet_job_no_stop(&pI2C_S(obj)->master, &pI2C_S(obj)->rd_packet); + } + } +} + +/** Start i2c asynchronous transfer. + * @param obj The I2C object + * @param tx The buffer to send + * @param tx_length The number of words to transmit + * @param rx The buffer to receive + * @param rx_length The number of words to receive + * @param address The address to be set - 7bit or 9 bit + * @param stop If true, stop will be generated after the transfer is done + * @param handler The I2C IRQ handler to be set + * @param hint DMA hint usage + */ +void i2c_transfer_asynch(i2c_t *obj, const void *tx, size_t tx_length, void *rx, size_t rx_length, uint32_t address, uint32_t stop, uint32_t handler, uint32_t event, DMAUsage hint) +{ + /* Sanity check arguments */ + MBED_ASSERT(obj); + MBED_ASSERT(pI2C_S(obj)->master.hw); + + /* Return if in Slave mode */ + if (pI2C_S(obj)->mode != I2C_MODE_MASTER) return; + + uint32_t sercom_index = _sercom_get_sercom_inst_index(pI2C_S(obj)->master.hw); + + /* Init i2c packet. */ + pI2C_S(obj)->wr_packet.address = address >> 1; + pI2C_S(obj)->wr_packet.data_length = tx_length; + pI2C_S(obj)->wr_packet.data = tx; + + pI2C_S(obj)->rd_packet.address = address >> 1; + pI2C_S(obj)->rd_packet.data_length = rx_length; + pI2C_S(obj)->rd_packet.data = rx; + + /* Save event mask and handler function pointer */ + pI2C_S(obj)->events = event; + pI2C_S(obj)->handler = handler; + + /* TODO: Current implementation is interrupt based only */ + + /* TODO: Set interrupt handler to default handler of ASF */ + /* Enable interrupt */ + NVIC_SetVector((SERCOM0_IRQn + sercom_index), sercom_irq_handlers[sercom_index]); + NVIC_EnableIRQ(SERCOM0_IRQn + sercom_index); + + /* Register callbacks */ + i2c_master_register_callback(&pI2C_S(obj)->master, i2c_transfer_complete_callback, I2C_MASTER_CALLBACK_ERROR); + i2c_master_enable_callback(&pI2C_S(obj)->master, I2C_MASTER_CALLBACK_ERROR); + if (tx && tx_length) { + i2c_master_register_callback(&pI2C_S(obj)->master, i2c_write_complete_callback, I2C_MASTER_CALLBACK_WRITE_COMPLETE); + i2c_master_enable_callback(&pI2C_S(obj)->master, I2C_MASTER_CALLBACK_WRITE_COMPLETE); + + /* Start I2C write */ + if (stop) { + i2c_master_write_packet_job(&pI2C_S(obj)->master, &pI2C_S(obj)->wr_packet); + } else { + i2c_master_write_packet_job_no_stop(&pI2C_S(obj)->master, &pI2C_S(obj)->wr_packet); + } + } else if (rx && rx_length) { + i2c_master_register_callback(&pI2C_S(obj)->master, i2c_transfer_complete_callback, I2C_MASTER_CALLBACK_READ_COMPLETE); + i2c_master_enable_callback(&pI2C_S(obj)->master, I2C_MASTER_CALLBACK_READ_COMPLETE); + + /* Start I2C read */ + if (stop) { + i2c_master_read_packet_job(&pI2C_S(obj)->master,&pI2C_S(obj)->rd_packet); + } else { + i2c_master_read_packet_job_no_stop(&pI2C_S(obj)->master, &pI2C_S(obj)->rd_packet); + } + } +} + +/** The asynchronous IRQ handler + * @param obj The I2C object which holds the transfer information + * @return event flags if a transfer termination condition was met or 0 otherwise. + */ +uint32_t i2c_irq_handler_asynch(i2c_t *obj) +{ + /* Sanity check arguments */ + MBED_ASSERT(obj); + MBED_ASSERT(pI2C_S(obj)->master.hw); + + uint32_t event_mask = pI2C_S(obj)->events; + + /* TODO: Current implementation is interrupt based only */ + + switch (pI2C_S(obj)->master.status) { + case STATUS_OK: + /* Transfer is complete */ + return (I2C_EVENT_TRANSFER_COMPLETE & event_mask); + break; + case STATUS_ERR_BAD_ADDRESS: + /* Received a NACK */ + return (I2C_EVENT_ERROR_NO_SLAVE & event_mask); + break; + case STATUS_ERR_PACKET_COLLISION: + /* An error occurred in between transfer */ + return (I2C_EVENT_ERROR & event_mask); + break; + default: + return 0; + } + + return 0; +} + +/** Attempts to determine if I2C peripheral is already in use. + * @param obj The I2C object + * @return non-zero if the I2C module is active or zero if it is not + */ +uint8_t i2c_active(i2c_t *obj) +{ + /* Sanity check arguments */ + MBED_ASSERT(obj); + MBED_ASSERT(pI2C_S(obj)->master.hw); + + return (pI2C_S(obj)->master.status == STATUS_BUSY); +} + +/** Abort ongoing asynchronous transaction. + * @param obj The I2C object + */ +void i2c_abort_asynch(i2c_t *obj) +{ + /* Sanity check arguments */ + MBED_ASSERT(obj); + MBED_ASSERT(pI2C_S(obj)->master.hw); + + /* Pointer to the hardware module instance */ + SercomI2cm *const i2c_module = &(pI2C_S(obj)->master.hw->I2CM); + + /* Abort ongoing job */ + + /* Stop packet operation */ + i2c_module->INTENCLR.reg = SERCOM_I2CM_INTENCLR_MB | SERCOM_I2CM_INTENCLR_SB; + + pI2C_S(obj)->master.buffer_length = 0; + pI2C_S(obj)->master.buffer_remaining = 0; + + /* Send nack and stop command unless arbitration is lost */ + if ((pI2C_S(obj)->master.status != STATUS_ERR_PACKET_COLLISION) && pI2C_S(obj)->master.send_stop) { + _i2c_master_wait_for_sync(&pI2C_S(obj)->master); + i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_ACKACT | SERCOM_I2CM_CTRLB_CMD(3); + } + + /* Disable any registered callback */ + i2c_master_disable_callback(&pI2C_S(obj)->master, I2C_MASTER_CALLBACK_WRITE_COMPLETE); + i2c_master_disable_callback(&pI2C_S(obj)->master, I2C_MASTER_CALLBACK_READ_COMPLETE); + i2c_master_disable_callback(&pI2C_S(obj)->master, I2C_MASTER_CALLBACK_ERROR); + + pI2C_S(obj)->master.status = STATUS_ABORTED; +} + +#endif diff --git a/libraries/mbed/targets/hal/TARGET_Atmel/TARGET_SAM0/objects.h b/libraries/mbed/targets/hal/TARGET_Atmel/TARGET_SAM0/objects.h index b64497c7aa..119dc9b630 100644 --- a/libraries/mbed/targets/hal/TARGET_Atmel/TARGET_SAM0/objects.h +++ b/libraries/mbed/targets/hal/TARGET_Atmel/TARGET_SAM0/objects.h @@ -22,6 +22,9 @@ #include "gpio_object.h" #include "adc.h" #include "extint.h" +#include "i2c_master.h" +#include "i2c_slave.h" +#include "dma_api.h" #ifdef __cplusplus extern "C" { @@ -71,6 +74,22 @@ struct analogin_s { struct pwmout_s { }; +struct i2c_s { + struct i2c_master_module master; + struct i2c_slave_module slave; + uint8_t mode; + uint32_t baud_rate; + uint32_t baud_rate_high_speed; + uint8_t start_pending; + PinName pins[2]; +#if DEVICE_I2C_ASYNCH + uint32_t events; + uint32_t handler; + struct i2c_master_packet wr_packet; + struct i2c_master_packet rd_packet; +#endif +}; + struct spi_s { Sercom *spi; uint8_t mode; diff --git a/workspace_tools/targets.py b/workspace_tools/targets.py index 4c40b26f1b..9881ce09ce 100755 --- a/workspace_tools/targets.py +++ b/workspace_tools/targets.py @@ -1531,7 +1531,7 @@ class SAMR21G18A(Target): Target.__init__(self) self.core = "Cortex-M0+" self.extra_labels = ['Atmel', 'SAM0', 'SAMR21'] - self.macros = ['__SAMR21G18A__'] + self.macros = ['__SAMR21G18A__', 'I2C_MASTER_CALLBACK_MODE=true'] self.supported_toolchains = ["GCC_ARM"] self.default_toolchain = "GCC_ARM"