/* ###################################################################
**     This component module is generated by Processor Expert. Do not modify it.
**     Filename    : McuEE24.h
**     Project     : FRDM-K64F_Generator
**     Processor   : MK64FN1M0VLL12
**     Component   : 24AA_EEPROM
**     Version     : Component 01.042, Driver 01.00, CPU db: 3.00.000
**     Compiler    : GNU C Compiler
**     Date/Time   : 2021-04-18, 20:37, # CodeGen: 733
**     Abstract    :
**         Driver for Microchip 24_AA/LC EEPROMs
**     Settings    :
**          Component name                                 : McuEE24
**          SDK                                            : McuLib
**          Device Type                                    : 8
**          Initial I2C Device Address Bits                : 0x0
**          Block buffer size                              : 32
**          Acknowledge Polling                            : Enabled
**            Page Write Time (ms)                         : 5
**            Wait                                         : McuWait
**            ACK Polling Time (us)                        : 100
**          Connection                                     : 
**            I2C                                          : McuGenericI2C
**            Write Protection Pin                         : Enabled
**              WP                                         : SDK_BitIO
**          Timeout                                        : Enabled
**            Timeout                                      : McuTimeout
**            Timeout Byte (ms)                            : 10
**            Timeout Block (ms)                           : 60
**          Shell                                          : Enabled
**            Shell                                        : McuShell
**            Utility                                      : McuUtility
**     Contents    :
**         ReadByte          - uint8_t McuEE24_ReadByte(McuEE24_Address addr, uint8_t *data);
**         WriteByte         - uint8_t McuEE24_WriteByte(McuEE24_Address addr, uint8_t data);
**         ReadBlock         - uint8_t McuEE24_ReadBlock(McuEE24_Address addr, uint8_t *data, uint16_t...
**         WriteBlock        - uint8_t McuEE24_WriteBlock(McuEE24_Address addr, uint8_t *data, uint16_t...
**         WriteProtect      - void McuEE24_WriteProtect(void);
**         WriteUnprotect    - void McuEE24_WriteUnprotect(void);
**         SelectDevice      - uint8_t McuEE24_SelectDevice(uint8_t addrI2C);
**         GetSelectedDevice - uint8_t McuEE24_GetSelectedDevice(void);
**         Test              - uint8_t McuEE24_Test(void);
**         ParseCommand      - uint8_t McuEE24_ParseCommand(const unsigned char *cmd, bool *handled, const...
**         Deinit            - void McuEE24_Deinit(void);
**         Init              - void McuEE24_Init(void);
**
** * Copyright (c) 2013-2021, Erich Styger
**  * Web:         https://mcuoneclipse.com
**  * SourceForge: https://sourceforge.net/projects/mcuoneclipse
**  * Git:         https://github.com/ErichStyger/McuOnEclipse_PEx
**  * All rights reserved.
**  *
**  * Redistribution and use in source and binary forms, with or without modification,
**  * are permitted provided that the following conditions are met:
**  *
**  * - Redistributions of source code must retain the above copyright notice, this list
**  *   of conditions and the following disclaimer.
**  *
**  * - 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.
**  *
**  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
**  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
**  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
**  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS 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.
** ###################################################################*/
/*!
** @file McuEE24.h
** @version 01.00
** @brief
**         Driver for Microchip 24_AA/LC EEPROMs
*/         
/*!
**  @addtogroup McuEE24_module McuEE24 module documentation
**  @{
*/         

/* MODULE McuEE24. */

#include "McuEE24.h"
#if McuEE24_CONFIG_HAS_WP_PIN
  #include "WPpin1.h"
#endif
#include "McuWait.h"
#if McuEE24_CONFIG_USE_SHELL
  #include "McuShell.h"
#endif
#if McuEE24_CONFIG_USE_UTILITY
  #include "McuUtility.h"
#endif
#include "McuGenericI2C.h"
#if McuEE24_CONFIG_USE_TIMEOUT
  #include "McuTimeout.h"
#endif

#if McuEE24_CONFIG_USE_TIMEOUT
  #define McuEE24_TIMEOUT_BYTE_TICKS     (McuEE24_CONFIG_TIMEOUT_BYTE_MS/(McuTimeout_TICK_PERIOD_MS))
  #define McuEE24_TIMEOUT_BLOCK_TICKS    (McuEE24_CONFIG_TIMEOUT_BLOCK_MS/(McuTimeout_TICK_PERIOD_MS))
#endif

static uint8_t McuEE24_I2CAddress = (McuEE24_CONFIG_DEVICE_I2C_ADDRESS_BITS&McuEE24_MAX_I2C_ADDR_MASK); /* current I2C address used */

/* macros for the control byte: */
#define McuEE24_CTRL_NBL       (0x0A<<3) /* control byte high nibble. Typically this is 1010 (shifted by one to the right) */
#if (McuEE24_CONFIG_DEVICE_ID==8) || (McuEE24_CONFIG_DEVICE_ID==16)
  #define McuEE24_CTRL_ADDR      0      /* no additional address bits */
  /* define control byte as 1010|Bx|B1|B0 */
  #define McuEE24_BANK_0         (0<<2) /* B0 bit (0) inside the CTRL_BYTE: 1010|B0|A1|A0 */
  #define McuEE24_BANK_1         (1<<2) /* B0 bit (1) inside the CTRL_BYTE: 1010|B0|A1|A0 */
  #define McuEE24_CTRL_BYTE      (McuEE24_CTRL_NBL|McuEE24_CTRL_ADDR) /* 1010|B0|A1|A0 */
  #define McuEE24_DEVICE_ADDR(addr) \
    ( McuEE24_CTRL_BYTE|((addr>>8)&0x07) )
  #if 0 /* old style */
    (((addr)&0x400)? \
        (McuEE24_CTRL_BYTE|McuEE24_BANK_1) \
      : (McuEE24_CTRL_BYTE|McuEE24_BANK_0) ) /* 7bit address of device used to select device */
  #endif
#elif (McuEE24_CONFIG_DEVICE_ID==32) || (McuEE24_CONFIG_DEVICE_ID==256) || (McuEE24_CONFIG_DEVICE_ID==512)
  #define McuEE24_CTRL_ADDR      McuEE24_I2CAddress /* address inside control byte */
  /* define control byte as 1010|A2|A1|A0 */
  #define McuEE24_CTRL_BYTE         (McuEE24_CTRL_NBL|McuEE24_CTRL_ADDR) /* 1010|A2|A1|A0 */
  #define McuEE24_DEVICE_ADDR(addr) McuEE24_CTRL_BYTE /* 7bit address of device used to select device */
#elif McuEE24_CONFIG_DEVICE_ID==1025
  #define McuEE24_CTRL_ADDR      McuEE24_I2CAddress /* address inside control byte */
  /* define control byte as 1010|Bx|A1|A0 */
  #define McuEE24_BANK_0         (0<<2) /* B0 bit (0) inside the CTRL_BYTE: 1010|B0|A1|A0 */
  #define McuEE24_BANK_1         (1<<2) /* B0 bit (1) inside the CTRL_BYTE: 1010|B0|A1|A0 */
  #define McuEE24_CTRL_BYTE      (McuEE24_CTRL_NBL|McuEE24_CTRL_ADDR) /* 1010|B0|A1|A0 */
  #define McuEE24_DEVICE_ADDR(addr) \
    (((addr)&0x10000)? \
        (McuEE24_CTRL_BYTE|McuEE24_BANK_1) \
      : (McuEE24_CTRL_BYTE|McuEE24_BANK_0) ) /* 7bit address of device used to select device */
#endif

static uint8_t PrintStatus(const McuShell_StdIOType *io) {
  unsigned char buf[32];

  McuShell_SendStatusStr((unsigned char*)"McuEE24", (unsigned char*)"\r\n", io->stdOut);

  McuUtility_strcpy(buf, sizeof(buf), (unsigned char*)"0x");
  McuUtility_strcatNum8Hex(buf, sizeof(buf), (uint8_t)McuEE24_DEVICE_ADDR(0));
  McuUtility_strcat(buf, sizeof(buf), (unsigned char*)" (for memory @0x00)\r\n");
  McuShell_SendStatusStr((unsigned char*)"  I2C Addr", buf, io->stdOut);

  McuUtility_Num16uToStr(buf, sizeof(buf), (uint16_t)McuEE24_CONFIG_DEVICE_ID);
  McuUtility_strcat(buf, sizeof(buf), (unsigned char*)"\r\n");
  McuShell_SendStatusStr((unsigned char*)"  Type", buf, io->stdOut);

  return ERR_OK;
}

static uint8_t PrintHelp(const McuShell_StdIOType *io) {
  McuShell_SendHelpStr((unsigned char*)"McuEE24", (unsigned char*)"Group of McuEE24 commands\r\n", io->stdOut);
  McuShell_SendHelpStr((unsigned char*)"  help|status", (unsigned char*)"Print help or status information\r\n", io->stdOut);
  McuShell_SendHelpStr((unsigned char*)"  read 0x<addr>", (unsigned char*)"Read a byte from an address\r\n", io->stdOut);
  McuShell_SendHelpStr((unsigned char*)"  write 0x<addr> 0x<value>", (unsigned char*)"Write a byte to an address\r\n", io->stdOut);
  return ERR_OK;
}

/*
** ===================================================================
**     Method      :  WriteByte (component 24AA_EEPROM)
**
**     Description :
**         Writes a single byte to specified address
**     Parameters  :
**         NAME            - DESCRIPTION
**         addr            - The address inside the EEPROM
**         data            - The data value to write
**     Returns     :
**         ---             - Error code, possible values
**                           ERR_OK - OK
**                           otherwise it can return an error code of
**                           the underlying communication protocol.
** ===================================================================
*/
uint8_t McuEE24_WriteByte(McuEE24_Address addr, uint8_t data)
{
  uint8_t res, block[3];
#if McuEE24_CONFIG_USE_TIMEOUT
#if McuEE24_DO_ACKNOWLEDGE_POLLING && McuEE24_TIMEOUT_BYTE_TICKS>0
  McuTimeout_CounterHandle timeout;
  bool isTimeout;
#endif
#endif

  res = McuGenericI2C_SelectSlave(McuEE24_DEVICE_ADDR(addr));
  if (res != ERR_OK) {
    (void)McuGenericI2C_UnselectSlave();
    return res;
  }
  #if (McuEE24_CONFIG_DEVICE_ID==8) || (McuEE24_CONFIG_DEVICE_ID==16)
    block[0] = (uint8_t)(addr&0xff);    /* low byte of address */
    block[1] = data; /* switch to read mode */
    res = McuGenericI2C_WriteBlock(block, 2, McuGenericI2C_SEND_STOP); /* send address and data */
  #else
    block[0] = (uint8_t)(addr>>8);      /* high byte of address */
    block[1] = (uint8_t)(addr&0xff);    /* low byte of address */
    block[2] = data; /* switch to read mode */
    res = McuGenericI2C_WriteBlock(block, sizeof(block), McuGenericI2C_SEND_STOP); /* send address and data */
  #endif
  if (res != ERR_OK) {
    (void)McuGenericI2C_UnselectSlave();
    return res;
  }
#if McuEE24_DO_ACKNOWLEDGE_POLLING
  /* do acknowledge polling */
  block[0] = 0xff; /* dummy value */
#if McuEE24_CONFIG_USE_TIMEOUT
#if McuEE24_TIMEOUT_BYTE_TICKS>0
    timeout = McuTimeout_GetCounter(McuEE24_TIMEOUT_BYTE_TICKS); /* set up timeout counter */
    if (timeout==McuTimeout_OUT_OF_HANDLE) {
      (void)McuGenericI2C_UnselectSlave();
      return ERR_FAILED;
    }
#endif
#endif
  do {
#if McuEE24_CONFIG_USE_TIMEOUT
#if McuEE24_TIMEOUT_BYTE_TICKS>0
    isTimeout = McuTimeout_CounterExpired(timeout);
    if (isTimeout) {
      break; /* break while() */
    }
#endif
#endif
    McuWait_WaitOSms(McuEE24_CONFIG_PAGE_WRITE_TIME_MS);
    res = McuGenericI2C_ProbeACK(block, 1, McuGenericI2C_SEND_STOP, McuEE24_CONFIG_ACK_POLLING_TIME_US); /* send address and data */
  } while(res!=ERR_OK); /* wait until we get an ACK */
#if McuEE24_CONFIG_USE_TIMEOUT
#if McuEE24_TIMEOUT_BYTE_TICKS>0
    McuTimeout_LeaveCounter(timeout);
    if (isTimeout) {
      res = ERR_FAILED;
    }
#endif
#endif
#endif /* McuEE24_CONFIG_DO_ACKNOWLEDGE_POLLING */
  if (res != ERR_OK) {
    (void)McuGenericI2C_UnselectSlave();
    return res;
  }
  return McuGenericI2C_UnselectSlave();
}

/*
** ===================================================================
**     Method      :  ReadByte (component 24AA_EEPROM)
**
**     Description :
**         Reads a single byte from the given memory address
**     Parameters  :
**         NAME            - DESCRIPTION
**         addr            - The address where to read from memory.
**       * data            - Pointer to a location where to store the
**                           data
**     Returns     :
**         ---             - Error code, possible values
**                           ERR_OK - OK
**                           otherwise it can return an error code of
**                           the underlying communication protocol.
** ===================================================================
*/
uint8_t McuEE24_ReadByte(McuEE24_Address addr, uint8_t *data)
{
  uint8_t res;
  #if (McuEE24_CONFIG_DEVICE_ID==8) || (McuEE24_CONFIG_DEVICE_ID==16)
    uint8_t addr8;
    addr8 = (uint8_t)(addr&0xff); /* low address byte */
  #else
    uint8_t addr16[2];                  /* big endian address on I2C bus needs to be 16bit */

    addr16[0] = (uint8_t)(addr>>8); /* 16 bit address must be in big endian format */
    addr16[1] = (uint8_t)(addr&0xff);
  #endif

  res = McuGenericI2C_SelectSlave(McuEE24_DEVICE_ADDR(addr));
  if (res != ERR_OK) {
    (void)McuGenericI2C_UnselectSlave();
    return res;
  }
  #if (McuEE24_CONFIG_DEVICE_ID==8) || (McuEE24_CONFIG_DEVICE_ID==16)
    res = McuGenericI2C_WriteBlock(&addr8, 1, McuGenericI2C_DO_NOT_SEND_STOP); /* send 8bit address */
  #else /* use 16bit address */
    res = McuGenericI2C_WriteBlock(addr16, 2, McuGenericI2C_DO_NOT_SEND_STOP); /* send 16bit address */
  #endif
  if (res != ERR_OK) {
    (void)McuGenericI2C_UnselectSlave();
    return res;
  }
  res = McuGenericI2C_ReadBlock(data, 1, McuGenericI2C_SEND_STOP); /* read data byte from bus */
  if (res != ERR_OK) {
    (void)McuGenericI2C_UnselectSlave();
    return res;
  }
  return McuGenericI2C_UnselectSlave();
}

/*
** ===================================================================
**     Method      :  ReadBlock (component 24AA_EEPROM)
**
**     Description :
**         Read a block of memory.
**     Parameters  :
**         NAME            - DESCRIPTION
**         addr            - Address where to read the memory
**       * data            - Pointer to a buffer where to store the
**                           data
**         dataSize        - Size of buffer the data pointer
**                           is pointing to
**     Returns     :
**         ---             - Error code, possible values
**                           ERR_OK - OK
**                           otherwise it can return an error code of
**                           the underlying communication protocol.
** ===================================================================
*/
uint8_t McuEE24_ReadBlock(McuEE24_Address addr, uint8_t *data, uint16_t dataSize)
{
  uint8_t res;
  #if (McuEE24_CONFIG_DEVICE_ID==8) || (McuEE24_CONFIG_DEVICE_ID==16)
    uint8_t addr8;
    addr8 = (uint8_t)(addr&0xff);
  #else
    uint8_t addr16[2];                  /* big endian address on I2C bus needs to be 16bit */
    addr16[0] = (uint8_t)(addr>>8); /* 16 bit address must be in big endian format */
    addr16[1] = (uint8_t)(addr&0xff);
  #endif

  res = McuGenericI2C_SelectSlave(McuEE24_DEVICE_ADDR(addr));
  if (res != ERR_OK) {
    (void)McuGenericI2C_UnselectSlave();
    return res;
  }
  #if (McuEE24_CONFIG_DEVICE_ID==8) || (McuEE24_CONFIG_DEVICE_ID==16)
    res = McuGenericI2C_WriteBlock(&addr8, 1, McuGenericI2C_DO_NOT_SEND_STOP); /* send 8bit address */
  #else
    res = McuGenericI2C_WriteBlock(addr16, 2, McuGenericI2C_DO_NOT_SEND_STOP); /* send 16bit address */
  #endif
  if (res != ERR_OK) {
    (void)McuGenericI2C_UnselectSlave();
    return res;
  }
  res = McuGenericI2C_ReadBlock(data, dataSize, McuGenericI2C_SEND_STOP);
  if (res != ERR_OK) {
    (void)McuGenericI2C_UnselectSlave();
    return res;
  }
  return McuGenericI2C_UnselectSlave();
}

/*
** ===================================================================
**     Method      :  McuEE24_WriteBlockPage (component 24AA_EEPROM)
**
**     Description :
**         This method is internal. It is used by Processor Expert only.
** ===================================================================
*/
#ifdef __HIWARE__
#pragma MESSAGE DISABLE C1855 /* recursive function call */
#endif
uint8_t McuEE24_WriteBlockPage(McuEE24_Address addr, uint8_t *data, uint16_t dataSize)
{
#if McuEE24_CONFIG_USE_TIMEOUT
#if McuEE24_DO_ACKNOWLEDGE_POLLING && McuEE24_TIMEOUT_BLOCK_TICKS>0
  McuTimeout_CounterHandle timeout;
  bool isTimeout;
#endif
#endif
  uint8_t res, i, *p, block[McuEE24_CONFIG_BLOCK_BUF_SIZE+2]; /* additional 2 bytes for the address */
  uint16_t eepromPage = (uint16_t)(addr/McuEE24_PAGE_SIZE);
  uint8_t offset = (uint8_t)(addr%McuEE24_PAGE_SIZE);

  if (dataSize==0 || dataSize>McuEE24_CONFIG_BLOCK_BUF_SIZE) {
    return ERR_OVERFLOW;                /* you may increase the buffer size in the properties? */
  }
  if (dataSize>McuEE24_PAGE_SIZE) {
    uint16_t size;

    size = (uint16_t)(McuEE24_PAGE_SIZE-offset);
    if (size!=0) {
      res = McuEE24_WriteBlock(addr, data, size); /* first page write */
      if (res != ERR_OK) {
        return res;
      }
      data += size; /* increment data pointer */
      addr += size; /* increment address */
      dataSize -= size; /* reduce size */
    }
    /* write multiple block of PAGE_SIZE */
    while (dataSize>McuEE24_PAGE_SIZE) {
      res = McuEE24_WriteBlock(addr, data, McuEE24_PAGE_SIZE);
      if (res != ERR_OK) {
        return res;
      }
      data += McuEE24_PAGE_SIZE; /* increment data pointer */
      addr += McuEE24_PAGE_SIZE; /* increment address */
      dataSize -= McuEE24_PAGE_SIZE; /* reduce size */
    }
    /* write remainder (if any) */
    if (dataSize>0) {
      return McuEE24_WriteBlock(addr, data, dataSize);
    }
    return ERR_OK;
  }
  if (offset+dataSize <= McuEE24_PAGE_SIZE) { /* no page boundary crossing */
    res = McuGenericI2C_SelectSlave(McuEE24_DEVICE_ADDR(addr));
    if (res != ERR_OK) {
      (void)McuGenericI2C_UnselectSlave();
      return res;
    }
    #if (McuEE24_CONFIG_DEVICE_ID==8) || (McuEE24_CONFIG_DEVICE_ID==16)
      /* 8 bit address byte, high byte of address have been place in SelectSlave(addr) */
      block[0] = (uint8_t)(addr&0xff);  /* low byte of address */
      p = &block[1]; i = (uint8_t)dataSize;
    #else /* 16 bit address byte */
      block[0] = (uint8_t)(addr>>8);    /* high byte of address */
      block[1] = (uint8_t)(addr&0xff);  /* low byte of address */
      p = &block[2]; i = (uint8_t)dataSize;
    #endif

    /* copy block */
    while(i>0) {
      *p++ = *data++;
      i--;
    }
    res = McuGenericI2C_WriteBlock(block,
        dataSize+((McuEE24_CONFIG_DEVICE_ID==8)||(McuEE24_CONFIG_DEVICE_ID==16)? 1:2), McuGenericI2C_SEND_STOP); /* send address and data */
    if (res != ERR_OK) {
      (void)McuGenericI2C_UnselectSlave();
      return res;
    }
#if McuEE24_DO_ACKNOWLEDGE_POLLING
    /* do acknowledge polling */
#if McuEE24_CONFIG_USE_TIMEOUT
#if McuEE24_TIMEOUT_BLOCK_TICKS>0
    timeout = McuTimeout_GetCounter(McuEE24_TIMEOUT_BLOCK_TICKS); /* set up timeout counter */
    if (timeout==McuTimeout_OUT_OF_HANDLE) {
      (void)McuGenericI2C_UnselectSlave();
      return ERR_OVERFLOW;
    }
#endif
#endif
    block[0] = 0xff; /* dummy value */
    do {
      McuWait_WaitOSms(McuEE24_CONFIG_PAGE_WRITE_TIME_MS);
      res = McuGenericI2C_ProbeACK(block, 1, McuGenericI2C_SEND_STOP, McuEE24_CONFIG_ACK_POLLING_TIME_US); /* send address and data */
#if McuEE24_CONFIG_USE_TIMEOUT
#if McuEE24_TIMEOUT_BLOCK_TICKS>0
      isTimeout = McuTimeout_CounterExpired(timeout);
      if (isTimeout) {
        res = ERR_FAULT;
        break;
      }
#endif
#endif
    } while(res!=ERR_OK); /* wait until we get an ACK */
#if McuEE24_CONFIG_USE_TIMEOUT
#if McuEE24_TIMEOUT_BLOCK_TICKS>0
    McuTimeout_LeaveCounter(timeout);
    if (isTimeout) {
      res = ERR_FAILED;
    }
#endif
#endif
    if (res != ERR_OK) {
      (void)McuGenericI2C_UnselectSlave();
      return res;
    }
#endif /* McuEE24_CONFIg_DO_ACKNOWLEDGE_POLLING */
    return McuGenericI2C_UnselectSlave();
  } else { /* crossing page boundaries: make two page writes */
    res = McuEE24_WriteBlock(addr, data, (uint16_t)(McuEE24_PAGE_SIZE-offset)); /* first page write */
    if (res != ERR_OK) {
      return res;
    }
    res = McuEE24_WriteBlock((McuEE24_Address)((eepromPage+1)*McuEE24_PAGE_SIZE),
       data+(McuEE24_PAGE_SIZE-offset),
       (uint16_t)(dataSize-(McuEE24_PAGE_SIZE-offset))); /* first page write */
    if (res != ERR_OK) {
      return res;
    }
  }
  return res;
}
#ifdef __HIWARE__
  #pragma MESSAGE DEFAULT C1855 /* recursive function call */
#endif

/*
** ===================================================================
**     Method      :  WriteBlock (component 24AA_EEPROM)
**
**     Description :
**         Writes a block of data to the EEPROM
**     Parameters  :
**         NAME            - DESCRIPTION
**         addr            - Address of memory
**       * data            - Pointer to the data
**         dataSize        - Size of data
**     Returns     :
**         ---             - Error code, possible values
**                           ERR_OK - OK
**                           ERR_OVERFLOW - data block passed has either
**                           size of zero or exceeds internal buffer
**                           size
**                           otherwise it can return an error code of
**                           the underlying communication protocol.
** ===================================================================
*/
uint8_t McuEE24_WriteBlock(McuEE24_Address addr, uint8_t *data, uint16_t dataSize)
{
  int32_t size;

  if (dataSize<=McuEE24_CONFIG_BLOCK_BUF_SIZE) { /* fits into internal buffer */
    return McuEE24_WriteBlockPage(addr, data, dataSize);
  }
  size = dataSize;
  while(size>=McuEE24_CONFIG_BLOCK_BUF_SIZE) { /* write in chunks McuEE24_CONFIG_BLOCK_BUF_SIZE */
    if (McuEE24_WriteBlock(addr, data, McuEE24_CONFIG_BLOCK_BUF_SIZE)!=ERR_OK) {
      return ERR_FAILED;
    }
    addr += McuEE24_CONFIG_BLOCK_BUF_SIZE;
    data += McuEE24_CONFIG_BLOCK_BUF_SIZE;
    size -= McuEE24_CONFIG_BLOCK_BUF_SIZE;
  }
  if (size>0) { /* write remainder which is < EE241_BLOCK_BUF_SIZE  */
    if (McuEE24_WriteBlockPage(addr, data, size)!=ERR_OK) {
      return ERR_FAILED;
    }
  }
  return ERR_OK;
}

/*
** ===================================================================
**     Method      :  Test (component 24AA_EEPROM)
**
**     Description :
**         Test routine to test the driver. Note that this routine
**         writes to the EEPROM!
**     Parameters  : None
**     Returns     :
**         ---             - Error code, possible values
**                           ERR_OK - OK
**                           ERR_FAULT - Test failed
** ===================================================================
*/
#include <string.h> /* for strcmp() */
static void Err(void) {
  static uint8_t errCnt = 0;

  errCnt++;
}

uint8_t McuEE24_Test(void)
{
  uint8_t res, val, data[16];

  res = McuEE24_WriteByte(0x0000, 0);
  if (res != ERR_OK) {
    Err();
    return res;
  }
  res = McuEE24_WriteByte(0x0001, 1);
  if (res != ERR_OK) {
    Err();
    return res;
  }
  res = McuEE24_WriteByte(0x0002, 2);
  if (res != ERR_OK) {
    Err();
    return res;
  }
  res = McuEE24_WriteByte(0x0003, 3);
  if (res != ERR_OK) {
    Err();
    return res;
  }
  res = McuEE24_WriteByte(0x0004, 4);
  if (res != ERR_OK) {
    Err();
    return res;
  }
  res = McuEE24_WriteByte(0x0010, 5);
  if (res != ERR_OK) {
    Err();
    return res;
  }

  res = McuEE24_ReadByte(0x0000, &val);
  if (res != ERR_OK) {
    Err();
    return res;
  }
  if (val != 0) {
    Err();
    return ERR_FAULT;
  }
  res = McuEE24_ReadByte(0x0001, &val);
  if (res != ERR_OK) {
    Err();
    return res;
  }
  if (val != 1) {
    Err();
    return ERR_FAULT;
  }
  res = McuEE24_ReadByte(0x0002, &val);
  if (res != ERR_OK) {
    Err();
    return res;
  }
  if (val != 2) {
    Err();
    return ERR_FAULT;
  }
  res = McuEE24_ReadByte(0x0003, &val);
  if (res != ERR_OK) {
    Err();
    return res;
  }
  if (val != 3) {
    Err();
    return ERR_FAULT;
  }
  res = McuEE24_ReadByte(0x0004, &val);
  if (res != ERR_OK) {
    Err();
    return res;
  }
  if (val != 4) {
    Err();
    return ERR_FAULT;
  }
  res = McuEE24_ReadByte(0x0010, &val);
  if (res != ERR_OK) {
    Err();
    return res;
  }
  if (val != 5) {
    Err();
    return ERR_FAULT;
  }
  res = McuEE24_WriteBlock(0x10, (uint8_t*)"Hello", sizeof("Hello"));
  if (res != ERR_OK) {
    Err();
    return res;
  }
  res = McuEE24_ReadBlock(0x10, data, sizeof(data));
  if (res != ERR_OK) {
    Err();
    return res;
  }
  if (strcmp((char*)"Hello", (char*)data) != 0) {
    Err();
    return ERR_FAULT;
  }
  /* testing crossing page boundary */
  res = McuEE24_WriteBlock(McuEE24_PAGE_SIZE-5, (uint8_t*)"Hello World!", sizeof("Hello World!"));
  if (res != ERR_OK) {
    Err();
    return res;
  }
  res = McuEE24_ReadBlock(McuEE24_PAGE_SIZE-5, data, sizeof(data));
  if (res != ERR_OK) {
    Err();
    return res;
  }
  if (strcmp((char*)"Hello World!", (char*)data) != 0) return ERR_FAULT;
#if McuEE24_CONFIG_DEVICE_ID==1025
  /* testing writing to second bank */
  res = McuEE24_WriteBlock(0x10005, (uint8_t*)"Hello bank 1!", sizeof("Hello bank 1!"));
  if (res != ERR_OK) {
    Err();
    return res;
  }
  res = McuEE24_ReadBlock(0x10005, data,(uint8_t*) sizeof(data));
  if (res != ERR_OK) {
    Err();
    return res;
  }
  if (strcmp((char*)"Hello bank 1!", (char*)data) != 0) {
    Err();
    return ERR_FAULT;
  }
#endif
  return ERR_OK;
}

/*
** ===================================================================
**     Method      :  WriteProtect (component 24AA_EEPROM)
**
**     Description :
**         Prevents writing to the memory. Read operations are not
**         affected.
**     Parameters  : None
**     Returns     : Nothing
** ===================================================================
*/
void McuEE24_WriteProtect(void)
{
#if McuEE24_CONFIG_HAS_WP_PIN
  WPpin1_SetVal();                      /* Tie pin to Vcc/High level to protect the memory */
#endif
}

/*
** ===================================================================
**     Method      :  WriteUnprotect (component 24AA_EEPROM)
**
**     Description :
**         Allows writing to the memory
**     Parameters  : None
**     Returns     : Nothing
** ===================================================================
*/
void McuEE24_WriteUnprotect(void)
{
#if McuEE24_CONFIG_HAS_WP_PIN
  WPpin1_ClrVal();                      /* Tie pin to Vss/Low level to allow writing to the memory */
#endif
}

/*
** ===================================================================
**     Method      :  SelectDevice (component 24AA_EEPROM)
**
**     Description :
**         Configures the I2C address to be used for the memory. With
**         this multiple devices attached to the bus can be addressed.
**     Parameters  :
**         NAME            - DESCRIPTION
**         addrI2C         - I2C Address of the memory device,
**                           formed by the address pins.
**     Returns     :
**         ---             - Error code, possible codes:
**                           ERR_OK - OK
**                           ERR_VALUE - address exceeds address pins of
**                           device type used
** ===================================================================
*/
uint8_t McuEE24_SelectDevice(uint8_t addrI2C)
{
  if (addrI2C>McuEE24_MAX_I2C_ADDR_MASK) {
    return ERR_VALUE;                   /* Device address too large for device address pins available. */
  }
  McuEE24_I2CAddress = addrI2C;
  return ERR_OK;
}

/*
** ===================================================================
**     Method      :  GetSelectedDevice (component 24AA_EEPROM)
**
**     Description :
**         Returns the currently used I2C address (e.g. set with
**         SelectDevice()).
**     Parameters  : None
**     Returns     :
**         ---             - I2C device address
** ===================================================================
*/
uint8_t McuEE24_GetSelectedDevice(void)
{
 return McuEE24_I2CAddress;
}

/*
** ===================================================================
**     Method      :  ParseCommand (component 24AA_EEPROM)
**
**     Description :
**         Shell Command Line parser. This method is enabled/disabled
**         depending on if you have the Shell enabled/disabled in the
**         properties.
**     Parameters  :
**         NAME            - DESCRIPTION
**       * cmd             - Pointer to command string
**       * handled         - Pointer to variable which tells if
**                           the command has been handled or not
**       * io              - Pointer to I/O structure
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
uint8_t McuEE24_ParseCommand(const unsigned char *cmd, bool *handled, const McuShell_StdIOType *io)
{
  const unsigned char *p;
  uint16_t addr16;
  uint8_t val8, buf[8];

  if (McuUtility_strcmp((char*)cmd, McuShell_CMD_HELP)==0 || McuUtility_strcmp((char*)cmd, "McuEE24 help")==0) {
    *handled = TRUE;
    return PrintHelp(io);
  } else if ((McuUtility_strcmp((char*)cmd, McuShell_CMD_STATUS)==0) || (McuUtility_strcmp((char*)cmd, "McuEE24 status")==0)) {
    *handled = TRUE;
    return PrintStatus(io);
  } else if (McuUtility_strncmp((char*)cmd, (char*)"McuEE24 read ", sizeof("McuEE24 read ")-1)==0) {
    p = cmd+sizeof("McuEE24 read ")-1;
    if (McuUtility_ScanHex16uNumber(&p, &addr16)==ERR_OK) {
      if (McuEE24_ReadByte(addr16, &val8)==ERR_OK) {
        McuUtility_strcpy(buf, sizeof(buf), (unsigned char*)"0x");
        McuUtility_strcatNum8Hex(buf, sizeof(buf), val8);
        McuUtility_strcat(buf, sizeof(buf), (unsigned char*)"\r\n");
        McuShell_SendStr(buf, io->stdOut);
      } else {
        McuShell_SendStr((unsigned char*)"**** read failed!\r\n", io->stdErr);
      }
    } else {
      McuShell_SendStr((unsigned char*)"**** wrong address\r\n", io->stdErr);
    }
    *handled = TRUE;
  } else if (McuUtility_strncmp((char*)cmd, (char*)"McuEE24 write ", sizeof("McuEE24 write ")-1)==0) {
    p = cmd+sizeof("McuEE24 write ")-1;
    if (McuUtility_ScanHex16uNumber(&p, &addr16)==ERR_OK) {
      if (McuUtility_ScanHex8uNumber(&p, &val8)==ERR_OK) {
        if (McuEE24_WriteByte(addr16, val8)!=ERR_OK) {
          McuShell_SendStr((unsigned char*)"**** write failed!\r\n", io->stdErr);
        }
      } else {
        McuShell_SendStr((unsigned char*)"**** wrong value\r\n", io->stdErr);
      }
    } else {
      McuShell_SendStr((unsigned char*)"**** wrong address\r\n", io->stdErr);
    }
    *handled = TRUE;
  }
  return ERR_OK;
}

/*
** ===================================================================
**     Method      :  Deinit (component 24AA_EEPROM)
**
**     Description :
**         Driver de-initialization
**     Parameters  : None
**     Returns     : Nothing
** ===================================================================
*/
void McuEE24_Deinit(void)
{
  /* nothing needed */
}

/*
** ===================================================================
**     Method      :  Init (component 24AA_EEPROM)
**
**     Description :
**         Driver initialization
**     Parameters  : None
**     Returns     : Nothing
** ===================================================================
*/
void McuEE24_Init(void)
{
  /* nothing needed */
}

/* END McuEE24. */

/*!
** @}
*/
