Adding implementation for I2C APIs - master mode, slave mode, and master async interrupt mode.

pull/1297/head
vimalrajr 2015-07-27 16:33:51 +05:30 committed by Karthik Purushothaman
parent 2110178848
commit 062ee461da
7 changed files with 1986 additions and 3 deletions

View File

@ -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

View File

@ -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 <a href="http://www.atmel.com/design-support/">Atmel Support</a>
*/
#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 */

View File

@ -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 <a href="http://www.atmel.com/design-support/">Atmel Support</a>
*/
#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 I<SUP>2</SUP>C packet to transfer
*
* \return Status of starting reading I<SUP>2</SUP>C packet.
* \retval STATUS_OK If reading was started successfully
* \retval STATUS_BUSY If module is currently busy with another transfer
*/
static enum status_code _i2c_master_read_packet(
struct i2c_master_module *const module,
struct i2c_master_packet *const packet)
{
/* Sanity check */
Assert(module);
Assert(module->hw);
SercomI2cm *const i2c_module = &(module->hw->I2CM);
enum status_code tmp_status;
/* Save packet to software module */
module->buffer = packet->data;
module->buffer_remaining = packet->data_length;
module->transfer_direction = I2C_TRANSFER_READ;
module->status = STATUS_BUSY;
/* Switch to high speed mode */
if (packet->high_speed) {
_i2c_master_send_hs_master_code(module, packet->hs_master_code);
}
/* Set action to ACK. */
i2c_module->CTRLB.reg &= ~SERCOM_I2CM_CTRLB_ACKACT;
if (packet->ten_bit_address) {
/*
* Write ADDR.ADDR[10:1] with the 10-bit address. ADDR.TENBITEN must
* be set and read/write bit (ADDR.ADDR[0]) equal to 0.
*/
i2c_module->ADDR.reg = (packet->address << 1) |
(packet->high_speed << SERCOM_I2CM_ADDR_HS_Pos) |
SERCOM_I2CM_ADDR_TENBITEN;
/* Wait for response on bus. */
tmp_status = _i2c_master_wait_for_bus(module);
/* Set action to ack. */
i2c_module->CTRLB.reg &= ~SERCOM_I2CM_CTRLB_ACKACT;
/* Check for address response error unless previous error is
* detected. */
if (tmp_status == STATUS_OK) {
tmp_status = _i2c_master_address_response(module);
}
if (tmp_status == STATUS_OK) {
/* Enable interrupts */
i2c_module->INTENSET.reg =
SERCOM_I2CM_INTENSET_MB | SERCOM_I2CM_INTENSET_SB;
/*
* Write ADDR[7:0] register to 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 I<SUP>2</SUP>C
* 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 I<SUP>2</SUP>C packet to transfer
*
* \return Status of starting reading I<SUP>2</SUP>C packet.
* \retval STATUS_OK If reading was started successfully
* \retval STATUS_BUSY If module is currently busy with another transfer
*/
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 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 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 I<SUP>2</SUP>C packet to transfer
*
* \return Status of starting reading I<SUP>2</SUP>C packet.
* \retval STATUS_OK If reading was started successfully
* \retval STATUS_BUSY If module is currently busy with another 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 I<SUP>2</SUP>C packet to transfer
*
* \return Status of starting writing I<SUP>2</SUP>C 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 I<SUP>2</SUP>C
* 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 I<SUP>2</SUP>C packet to transfer
*
* \return Status of starting writing I<SUP>2</SUP>C 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 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.
*
* 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 I<SUP>2</SUP>C packet to transfer
*
* \return Status of starting writing I<SUP>2</SUP>C 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 I<SUP>2</SUP>C 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);
}
}
}

View File

@ -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 I<SUP>2</SUP>C 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 I<SUP>2</SUP>C 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 <a href="http://www.atmel.com/design-support/">Atmel Support</a>
*/
#include <asf.h>
#include <conf_clocks.h>

View File

@ -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 <math.h>
#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

View File

@ -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;

View File

@ -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"