/**
 * \file
 * \brief  definitions for bit-banged I2C
 *
 * \copyright (c) 2015-2020 Microchip Technology Inc. and its subsidiaries.
 *
 * \page License
 *
 * Subject to your compliance with these terms, you may use Microchip software
 * and any derivatives exclusively with Microchip products. It is your
 * responsibility to comply with third party license terms applicable to your
 * use of third party software (including open source software) that may
 * accompany Microchip software.
 *
 * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, WHETHER
 * EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY IMPLIED
 * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A
 * PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT,
 * SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE
 * OF ANY KIND WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF
 * MICROCHIP HAS BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE
 * FORESEEABLE. TO THE FULLEST EXTENT ALLOWED BY LAW, MICROCHIP'S TOTAL
 * LIABILITY ON ALL CLAIMS IN ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED
 * THE AMOUNT OF FEES, IF ANY, THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR
 * THIS SOFTWARE.
 */

#ifndef I2C_BITBANG_SAMD21_H_
#define I2C_BITBANG_SAMD21_H_

#include "atca_status.h"
#include <delay.h>


#define MAX_I2C_BUSES   18       //The MAX_I2C_BUSES is the number of free pins in samd21 xplained pro


typedef struct
{
    uint8_t pin_sda[MAX_I2C_BUSES];
    uint8_t pin_scl[MAX_I2C_BUSES];
} I2CBuses;

extern I2CBuses i2c_buses_default;

extern uint8_t pin_sda;
extern uint8_t pin_scl;

#   define I2C_ENABLE()         {  struct port_config pin_conf; \
                                   port_get_config_defaults(&pin_conf); \
                                   pin_conf.direction  = PORT_PIN_DIR_OUTPUT_WTH_READBACK; \
                                   port_pin_set_config(pin_sda, &pin_conf); \
                                   pin_conf.direction  = PORT_PIN_DIR_OUTPUT; \
                                   port_pin_set_config(pin_scl, &pin_conf); }
#   define I2C_DISABLE()         {  struct port_config pin_conf; \
                                    port_get_config_defaults(&pin_conf); \
                                    pin_conf.direction  = PORT_PIN_DIR_INPUT; \
                                    pin_conf.input_pull = PORT_PIN_PULL_UP; \
                                    port_pin_set_config(pin_sda, &pin_conf); \
                                    port_pin_set_config(pin_scl, &pin_conf); }
#   define I2C_CLOCK_LOW()       port_pin_set_output_level(pin_scl, false)
#   define I2C_CLOCK_HIGH()      port_pin_set_output_level(pin_scl, true)
#   define I2C_DATA_LOW()        port_pin_set_output_level(pin_sda, false)
#   define I2C_DATA_HIGH()       port_pin_set_output_level(pin_sda, true)
#   define I2C_DATA_IN()         port_pin_get_input_level(pin_sda)
#   define I2C_SET_OUTPUT()      {  struct port_config pin_conf; \
                                    port_get_config_defaults(&pin_conf); \
                                    pin_conf.direction  = PORT_PIN_DIR_OUTPUT_WTH_READBACK; \
                                    port_pin_set_config(pin_sda, &pin_conf); }
#   define I2C_SET_OUTPUT_HIGH() { I2C_SET_OUTPUT(); I2C_DATA_HIGH(); }
#   define I2C_SET_OUTPUT_LOW()  { I2C_SET_OUTPUT(); I2C_DATA_LOW(); }
#   define I2C_SET_INPUT()       {  struct port_config pin_conf; \
                                    port_get_config_defaults(&pin_conf); \
                                    pin_conf.direction  = PORT_PIN_DIR_INPUT; \
                                    port_pin_set_config(pin_sda, &pin_conf); }
#   define DISABLE_INTERRUPT()   cpu_irq_disable()
#   define ENABLE_INTERRUPT()    cpu_irq_enable()


#define I2C_CLOCK_DELAY_WRITE_LOW()  delay_us(1)
#define I2C_CLOCK_DELAY_WRITE_HIGH() delay_us(1)
#define I2C_CLOCK_DELAY_READ_LOW()   delay_us(1)
#define I2C_CLOCK_DELAY_READ_HIGH()  delay_us(1)
#define I2C_CLOCK_DELAY_SEND_ACK()   delay_us(1)
//! This delay is inserted to make the Start and Stop hold time at least 250 ns.
#define I2C_HOLD_DELAY()    delay_us(1)




//! loop count when waiting for an acknowledgment
#define I2C_ACK_TIMEOUT                 (4)


/**
 * \brief Set I2C data and clock pin.
 *        Other functions will use these pins.
 *
 * \param[in] sda  definition of GPIO pin to be used as data pin
 * \param[in] scl  definition of GPIO pin to be used as clock pin
 */
void i2c_set_pin(uint8_t sda, uint8_t scl);


/**
 * \brief  Assigns the logical bus number for discovering the devices
 *
 *
 * \param[in]  i2c_bitbang_buses         The logical bus numbers are assigned to the variables.
 * \param[in]  max_buses                 Maximum number of bus used for discovering.
 */

void i2c_discover_buses(int i2c_bitbang_buses[], int max_buses);

/**
 * \brief Configure GPIO pins for I2C clock and data as output.
 */
void i2c_enable(void);

/**
 * \brief Configure GPIO pins for I2C clock and data as input.
 */
void i2c_disable(void);


/**
 * \brief Send a START condition.
 */
void i2c_send_start(void);

/**
 * \brief Send an ACK or NACK (after receive).
 *
 * \param[in] ack  0: NACK, else: ACK
 */
void i2c_send_ack(uint8_t ack);

/**
 * \brief Send a STOP condition.
 */
void i2c_send_stop(void);

/**
 * \brief Send a Wake Token.
 */
void i2c_send_wake_token(void);

/**
 * \brief Send one byte.
 *
 * \param[in] i2c_byte  byte to write
 *
 * \return ATCA_STATUS
 */
ATCA_STATUS i2c_send_byte(uint8_t i2c_byte);

/**
 * \brief Send a number of bytes.
 *
 * \param[in] count  number of bytes to send
 * \param[in] data   pointer to buffer containing bytes to send
 *
 * \return ATCA_STATUS
 */
ATCA_STATUS i2c_send_bytes(uint8_t count, uint8_t *data);

/**
 * \brief Receive one byte (MSB first).
 *
 * \param[in] ack  0:NACK, else:ACK
 *
 * \return Number of bytes received
 */
uint8_t i2c_receive_one_byte(uint8_t ack);

/**
 * \brief Receive one byte and send ACK.
 *
 * \param[out] data  pointer to received byte
 */
void i2c_receive_byte(uint8_t *data);

/**
 * \brief Receive a number of bytes.
 *
 * \param[out] data   pointer to receive buffer
 * \param[in]  count  number of bytes to receive
 */
void i2c_receive_bytes(uint8_t count, uint8_t *data);

#endif /* I2C_BITBANG_SAMD21_H_ */