/**
* \file
*
* \brief I2C Master Interrupt Driver for SAMB
*
* Copyright (c) 2015-2016 Atmel Corporation. All rights reserved.
*
* \asf_license_start
*
* \page License
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. The name of Atmel may not be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* 4. This software may only be redistributed and used in connection with an
* Atmel microcontroller product.
*
* THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* \asf_license_stop
*
*/
/*
* Support and FAQ: visit Atmel Support
*/
#include "i2c_slave_interrupt.h"
void *_i2c_instances;
/**
* \internal
* Reads next data. Used by interrupt handler to get next data byte from master.
*
* \param[in,out] module Pointer to software module structure
*/
static void _i2c_slave_read(
struct i2c_slave_module *const module)
{
/* Sanity check */
Assert(module);
Assert(module->hw);
I2c *const i2c_module = module->hw;
/* Read byte from master and put in buffer. */
*(module->buffer++) = i2c_module->RECEIVE_DATA.reg;
/*Decrement remaining buffer length */
module->buffer_remaining--;
}
/**
* \internal
* Writes next data. Used by interrupt handler to send next data byte to master.
*
* \param[in,out] module Pointer to software module structure
*/
static void _i2c_slave_write(
struct i2c_slave_module *const module)
{
/* Sanity check */
Assert(module);
Assert(module->hw);
I2c *const i2c_module = module->hw;
/* Write byte from buffer to master */
i2c_module->TRANSMIT_DATA.reg = *(module->buffer++);
/*Decrement remaining buffer length */
module->buffer_remaining--;
}
/**
* \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_slave_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_slave_register_callback(
struct i2c_slave_module *const module,
i2c_slave_callback_t callback,
enum i2c_slave_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 initiated. */
module->registered_callback |= (1 << callback_type);
}
/**
* \brief Unregisters callback for the specified callback type
*
* Removes the currently registered callback for the given callback
* type.
*
* \param[in,out] module Pointer to the software module struct
* \param[in] callback_type Callback type to unregister
*/
void i2c_slave_unregister_callback(
struct i2c_slave_module *const module,
enum i2c_slave_callback callback_type)
{
/* Sanity check. */
Assert(module);
Assert(module->hw);
/* Register callback. */
module->callbacks[callback_type] = NULL;
/* Set corresponding bit to set callback as initiated. */
module->registered_callback &= ~(1 << callback_type);
}
/**
* \brief Initiates a reads packet operation
*
* Reads a data packet from the master. A write request must be initiated by
* the master before the packet can be read.
*
* The \ref I2C_SLAVE_CALLBACK_WRITE_REQUEST callback can be used to call this
* function.
*
* \param[in,out] module Pointer to software module struct
* \param[in,out] packet Pointer to I2C packet to transfer
*
* \return Status of starting asynchronously reading I2C packet.
* \retval STATUS_OK If reading was started successfully
* \retval STATUS_BUSY If module is currently busy with another transfer
*/
enum status_code i2c_slave_read_packet_job(
struct i2c_slave_module *const module,
struct i2c_slave_packet *const packet)
{
/* Sanity check */
Assert(module);
Assert(module->hw);
Assert(packet);
I2c *const i2c_module = module->hw;
/* Check if the I2C module is busy doing async operation. */
if (module->buffer_remaining > 0) {
return STATUS_BUSY;
}
/* Save packet to software module. */
module->buffer = packet->data;
module->buffer_remaining = packet->data_length;
module->buffer_length = packet->data_length;
module->status = STATUS_BUSY;
/* Enable interrupts */
i2c_slave_rx_interrupt(i2c_module, true);
return STATUS_OK;
}
/**
* \brief Initiates a write packet operation
*
* Writes a data packet to the master. A read request must be initiated by
* the master before the packet can be written.
*
* The \ref I2C_SLAVE_CALLBACK_READ_REQUEST callback can be used to call this
* function.
*
* \param[in,out] module Pointer to software module struct
* \param[in,out] packet Pointer to I2C packet to transfer
*
* \return Status of starting writing I2C packet.
* \retval STATUS_OK If writing was started successfully
* \retval STATUS_BUSY If module is currently busy with another transfer
*/
enum status_code i2c_slave_write_packet_job(
struct i2c_slave_module *const module,
struct i2c_slave_packet *const packet)
{
/* Sanity check */
Assert(module);
Assert(module->hw);
Assert(packet);
I2c *const i2c_module = module->hw;
if (module->buffer_remaining > 0) {
return STATUS_BUSY;
}
/* Save packet to software module. */
module->buffer = packet->data;
module->buffer_remaining = packet->data_length;
module->buffer_length = packet->data_length;
module->status = STATUS_BUSY;
/* Enable interrupts */
i2c_slave_tx_interrupt(i2c_module, true);
return STATUS_OK;
}
/**
* \internal Interrupt handler for I2C slave
*
* \param[in] instance I2C instance that triggered the interrupt
*/
void _i2c_slave_rx_isr_handler(void)
{
/* Get software module for callback handling. */
struct i2c_slave_module *module =
(struct i2c_slave_module*)_i2c_instances;
Assert(module);
I2c *const i2c_module = module->hw;
/* Combine callback registered and enabled masks. */
uint8_t callback_mask =
module->enabled_callback & module->registered_callback;
if (i2c_module->RECEIVE_STATUS.reg & I2C_RECEIVE_STATUS_RX_FIFO_NOT_EMPTY) {
if (!module->buffer_length && (module->buffer_length == module->buffer_remaining)) {
module->transfer_direction = I2C_TRANSFER_WRITE;
if (callback_mask & (1 << I2C_SLAVE_CALLBACK_WRITE_REQUEST)) {
/* Write to master complete */
module->callbacks[I2C_SLAVE_CALLBACK_WRITE_REQUEST](module);
}
}
/* Continue buffer write/read */
if (module->buffer_length > 0 && module->buffer_remaining > 0) {
_i2c_slave_read(module);
}
if (!module->buffer_remaining) {
module->status = STATUS_OK;
module->buffer_length = 0;
if (callback_mask & (1 << I2C_SLAVE_CALLBACK_WRITE_COMPLETE)) {
/* Write to master complete */
module->callbacks[I2C_SLAVE_CALLBACK_WRITE_COMPLETE](module);
}
}
}
if ((i2c_module->RECEIVE_STATUS.reg & I2C_RECEIVE_STATUS_NAK)) { //&&
//module->transfer_direction == I2C_TRANSFER_READ) {
/* Received NAK, master received completed. */
i2c_module->RX_INTERRUPT_MASK.bit.NAK_MASK = 0;
if (callback_mask & (1 << I2C_SLAVE_CALLBACK_READ_COMPLETE)) {
module->callbacks[I2C_SLAVE_CALLBACK_READ_COMPLETE](module);
}
}
if (module->hw == I2C0) {
NVIC_ClearPendingIRQ(I2C0_RX_IRQn);
} else if (module->hw == I2C1) {
NVIC_ClearPendingIRQ(I2C1_RX_IRQn);
}
}
void _i2c_slave_tx_isr_handler(void)
{
/* Get software module for callback handling. */
struct i2c_slave_module *module =
(struct i2c_slave_module*)_i2c_instances;
Assert(module);
I2c *const i2c_module = module->hw;
/* Combine callback registered and enabled masks. */
uint8_t callback_mask =
module->enabled_callback & module->registered_callback;
if (!module->buffer_length && (module->buffer_length == module->buffer_remaining)) {
/* First timer interrupt */
module->transfer_direction = I2C_TRANSFER_READ;
if (callback_mask & (1 << I2C_SLAVE_CALLBACK_READ_REQUEST)) {
/* Write to master complete */
module->callbacks[I2C_SLAVE_CALLBACK_READ_REQUEST](module);
}
}
if (module->buffer_length > 0 && module->buffer_remaining > 0) {
_i2c_slave_write(module);
}
if (!module->buffer_remaining) {
module->status = STATUS_OK;
module->buffer_length = 0;
i2c_module->RX_INTERRUPT_MASK.bit.NAK_MASK = 0;
if (callback_mask & (1 << I2C_SLAVE_CALLBACK_READ_COMPLETE)) {
module->callbacks[I2C_SLAVE_CALLBACK_READ_COMPLETE](module);
}
}
if (module->hw == I2C0) {
NVIC_ClearPendingIRQ(I2C0_TX_IRQn);
} else if (module->hw == I2C1) {
NVIC_ClearPendingIRQ(I2C1_TX_IRQn);
}
}