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_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
|
||||
|
|
|
@ -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 "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;
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
Loading…
Reference in New Issue