mirror of https://github.com/ARMmbed/mbed-os.git
Adding implementation for I2C APIs - master mode, slave mode, and master async interrupt mode.
parent
2110178848
commit
062ee461da
|
@ -29,8 +29,9 @@
|
||||||
#define DEVICE_SERIAL_FC 1
|
#define DEVICE_SERIAL_FC 1
|
||||||
#define DEVICE_SERIAL_ASYNCH 1
|
#define DEVICE_SERIAL_ASYNCH 1
|
||||||
|
|
||||||
#define DEVICE_I2C 0
|
#define DEVICE_I2C 1
|
||||||
#define DEVICE_I2CSLAVE 0
|
#define DEVICE_I2CSLAVE 1
|
||||||
|
#define DEVICE_I2C_ASYNCH 1
|
||||||
|
|
||||||
#define DEVICE_SPI 1
|
#define DEVICE_SPI 1
|
||||||
#define DEVICE_SPISLAVE 1
|
#define DEVICE_SPISLAVE 1
|
||||||
|
|
|
@ -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 */
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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>
|
||||||
|
|
|
@ -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
|
|
@ -22,6 +22,9 @@
|
||||||
#include "gpio_object.h"
|
#include "gpio_object.h"
|
||||||
#include "adc.h"
|
#include "adc.h"
|
||||||
#include "extint.h"
|
#include "extint.h"
|
||||||
|
#include "i2c_master.h"
|
||||||
|
#include "i2c_slave.h"
|
||||||
|
#include "dma_api.h"
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
@ -71,6 +74,22 @@ struct analogin_s {
|
||||||
struct pwmout_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 {
|
struct spi_s {
|
||||||
Sercom *spi;
|
Sercom *spi;
|
||||||
uint8_t mode;
|
uint8_t mode;
|
||||||
|
|
|
@ -1531,7 +1531,7 @@ class SAMR21G18A(Target):
|
||||||
Target.__init__(self)
|
Target.__init__(self)
|
||||||
self.core = "Cortex-M0+"
|
self.core = "Cortex-M0+"
|
||||||
self.extra_labels = ['Atmel', 'SAM0', 'SAMR21']
|
self.extra_labels = ['Atmel', 'SAM0', 'SAMR21']
|
||||||
self.macros = ['__SAMR21G18A__']
|
self.macros = ['__SAMR21G18A__', 'I2C_MASTER_CALLBACK_MODE=true']
|
||||||
self.supported_toolchains = ["GCC_ARM"]
|
self.supported_toolchains = ["GCC_ARM"]
|
||||||
self.default_toolchain = "GCC_ARM"
|
self.default_toolchain = "GCC_ARM"
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue