LTC2947 - 30A Power/Energy Monitor with Integrated Sense Resistor

Features

  • Measures Current, Voltage, Power, Charge, Energy
  • ±30A Current Range with Low 9mA Offset
  • Integrated 300µΩ Sense Resistor
  • 0V to 15V Input Range Independent of Supply Voltage
  • Instantaneous Multiplication of Voltage and Current
  • 0.5% Voltage Accuracy
  • 1% Current and Charge Accuracy
  • 1.2% Power and Energy Accuracy
  • Alerts When Thresholds Exceeded
  • Stores Maximum and Minimum Values
  • Shutdown Mode with I< 10μA
  • I2C/SPI Compatible Interface
  • Available in 32-Lead 4mm × 6mm QFN Package

Typical Application

LTC2947 Typical Application
LTC2947 Typical Application

Description

The LTC2947 is a high-precision power and energy monitor with an internal sense resistor supporting up to ±30A. Three internal No Latency Δ∑™ ADCs ensure accurate measurement of voltage and current, while highbandwidth analog multiplication of voltage and current provides accurate power measurement in a wide range of applications. Internal or external clocking options enable precise charge and energy measurements.

An internal 300µΩ, temperature-compensated sense resistor minimizes efficiency loss and external components, simplifying energy measurement applications while enabling high accuracy current measurement over the full temperature range.

All measured quantities are stored in internal registers accessible via the selectable I2C/SPI interface. The LTC2947 features programmable high and low thresholds for all

Packaging

CAD Symbols and Footprints: The downloadable Zip file below contains the schematic symbol and PCB footprints.

For complete and up to date package information and drawings, please refer to our packaging page

Part Number Package Code Temp Package
Drawing
RoHS
LTC2947IUFE#PBF 4x6 QFN-32 UFEMA I 05-08-1938 Yes
LTC2947IUFE#TRPBF 4x6 QFN-32 UFEMA I 05-08-1938 Yes


LTC2947 Package Drawing

Order Info

  • Part numbers ending in PBF are lead free. Solder plated terminal finish (SnPb) versions are non-standard and special terms and conditions and pricing applies if available. Please contact LTC marketing for information.
  • Part numbers containing TR or TRM are shipped in tape and reel or 500 unit mini tape and reel, respectively
  • Please refer to our general ordering information or the product datasheet for more details

Package Variations and Pricing

Part Number Package Temp Price
(1-99)
Price
(1k)*
RoHS
LTC2947IUFE#PBF 4x6 QFN-32 I $8.50 $5.95 Yes
LTC2947IUFE#TRPBF 4x6 QFN-32 I $8.56 $6.01 Yes
Buy NowRequest Samples
* The USA list pricing shown is for BUDGETARY USE ONLY, shown in United States dollars (FOB USA per unit for the stated volume), and is subject to change. International prices may differ due to local duties, taxes, fees and exchange rates. For volume-specific price or delivery quotes, please contact your local Linear Technology sales office or authorized distributor.

Demo Boards

Linear Technology offers many demo boards free of charge to qualified customers. Contact your local sales office or distributor to inquire about a demo board. Certain demo boards are also available for sale via credit card on this website. Demo boards are for evaluation purposes only. It remains the customer’s responsibility to verify proper and reliable operation in the actual end application.

Part Number Description Price Documentation
DC2334A LTC2947 Demo Board | 30A Energy Monitor with Integrated RSENSE (req. DC590 or DC2026) $100.00
DC2574A-KIT LTC2947 Standalone Demo Kit | LTC2947 Demo Board (DC2334) + Linduino (DC2026) + LCD Keypad Shield $200.00
Buy Now

Companion Boards

Part Number Description Price Documentation
DC2026C Linduino One Isolated USB Demo Board: An Arduino- and QuikEval-Compatible Code Development Platform $75.00
DC590B Isolated USB Serial Controller for Linear Technology QuikEval-Compatible Demo Boards $50.00
Buy Now
Click here to view our complete list of demo boards

Applications

  • Servers
  • Telecom Infrastructure
  • Industrial
  • Electric Vehicles
  • Photovoltaics

Product Notifications

Please login to your MyLinear account for notifications of datasheet updates, new document releases and LTspice model announcements for your favorite products. If you do not have a MyLinear account you may Sign Up Now.

Forgot your password? Click here.
Need help? Email mylinear@linear.com with questions and comments.

Design Tools

Linduino

Linduino is an Arduino compatible platform for developing and distributing firmware libraries and code for SPI and I²C-compatible integrated circuits. The Linduino One board interfaces to more than 300 QuikEval demonstration cards, supporting a variety of product types including analog-to-digital converters (ADCs)digital-to-analog converters (DACs)power monitors, and more. Firmware libraries for individual devices are written in C and designed to be portable to a wide variety of processors and microcontrollers. Each library has a demonstration program that can be uploaded to the Linduino One platform to allow the circuit and software to be quickly and easily verified.

Click here for more information on Linduino

Code

Linduino is Linear Technology's Arduino compatible system for developing and distributing firmware libraries and example code for Linear Technology’s integrated circuits. The code below can be downloaded or copied and pasted into your project. Please visit the Linduino Home Page for demo board, manual and setup information.

This part is Code Supported: There is example code available for this part. The code below may rely on other drivers available in the full library.

Download LTC2947 - DC2334A.ino File

/*!
@verbatim

Linear Technology DC2334A Demonstration Board.
LTC2947: LTC2947 a high-precision power and energy monitor
with an internal sense resistor supporting up to 30A.

The LTC2947 is a high-precision power and energy
monitor with an internal sense resistor supporting up
to 30A. Three internal No Latency delta sigma ADCs ensure
accurate measurement of voltage and current, while high-
bandwidth analog multiplication of voltage and current
provides accurate power measurement in a wide range of
applications. Internal or external clocking options enable
precise charge and energy measurements.
An internal 300 micro ohms, temperature-compensated sense
resistor minimizes efficiency loss and external compo-
nents, simplifying energy measurement applications while
enabling high accuracy current measurement over the full
temperature range. For more details see following URLs:

http://www.linear.com/product/LTC2947
http://www.linear.com/product/LTC2947#demoboards

NOTES
Setup:
Set the terminal baud rate to 115200 and select the newline terminator.

USER INPUT DATA FORMAT:
decimal : 1024
hex     : 0x400
octal   : 02000  (leading 0 "zero")
binary  : B10000000000
float   : 1024.0

See the DC2334 demo manual and LTC2947 datasheet  for  more  details on
operation.

@endverbatim


REVISION HISTORY
$Revision: 5954 $
$Date: 2016-10-20 12:05:26 -0700 (Thu, 20 Oct 2016) $

Copyright (c) 2013, Linear Technology Corp.(LTC)
All rights reserved.

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.

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 OWNER 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.

The views and conclusions contained in the software and documentation are those
of the authors and should not be interpreted as representing official policies,
either expressed or implied, of Linear Technology Corp.

The Linear Technology Linduino is not affiliated with the official Arduino team.
However, the Linduino is only possible because of the Arduino team's commitment
to the open-source community.  Please, visit http://www.arduino.cc and
http://store.arduino.cc , and consider a purchase that will help fund their
ongoing work.
*/

/*! @file
@ingroup LTC2947
*/

#include <Arduino.h>
#include <stdint.h>
#include "Linduino.h"
#include "LT_SPI.h"
#include "LT_I2C.h"
#include "UserInterface.h"
#include "QuikEval_EEPROM.h"
#include "LTC2947.h"
#include <SPI.h>
#include <LiquidCrystal.h>

//! Global variables
static uint8_t demo_board_connected;                //!< Set to 1 if the board is connected

//! Initialize Linduino
void setup()
{
  char demo_name[] = "DC2334";  //! Demo Board Name stored in QuikEval EEPROM
  spi_enable(SPI_CLOCK_DIV16);  //! Configure the spi port for 1MHz SCK
  quikeval_SPI_connect();       //! Connect SPI to main data port (in case the demo board is
  //! configured to SPI mode this avoids pin conflicts when
  //! reading the EEPROM with discover_demo_board)
  quikeval_I2C_init();          //! Configure the EEPROM I2C port for 100kHz
  Serial.begin(115200);         //! Initialize the serial port to the PC
  print_title();          //! print title
  demo_board_connected = discover_demo_board(demo_name); //! read demo board's EEPROM
  if (demo_board_connected)
  {
    Serial.print(F("Found correct demo board"));
  }
  else
  {
    Serial.println(F("Will attempt to proceed"));
    demo_board_connected = 1;
  }
  setup_ser_mode();   //! setup LTC2947's serial interface mode
  go_idle_cfg_tbctl();  //! go to idle and configure TBCTL
  print_main_menu();    //! show main menu
}

//! Repeats Linduino loop
void loop()
{
  if (Serial.available()) //! Do nothing if serial is not available
  {
    int32_t command = read_int(); //! get user input
    Serial.println(command);      //! echo selection
    Serial.println();
    switch (command)
    {
      case 1:
        mm1_operation_control();
        break;
      case 2:
        mm2_read_status_alert_registers();
        break;
      case 3:
        mm3_measurements();
        break;
      case 4:
        mm4_accu_measurements();
        break;
      case 5:
        mm5_accu_init();
        break;
      case 6:
        mm6_min_max();
        break;
      case 7:
        mm7_gpio_ctrl();
        break;
      case 8:
        mm8_ara();
        break;
#ifdef LTC2947_DEBUG
      case 99:
        LTC2947_DoubleToBytes_Test();
        break;
#endif
      default:
        Serial.println(F("Unknown command!"));
        break;
    }
    print_main_menu();
  }
}

//! Print the title block
void print_title()
{
  Serial.println();
  Serial.println(F("*****************************************************************"));
  Serial.println(F("* DC2334A Demonstration Program                                 *"));
  Serial.println(F("*                                                               *"));
  Serial.println(F("* This program demonstrates the basic features of the LTC2947   *"));
  Serial.println(F("* a high-precision power and energy monitor with an internal    *"));
  Serial.println(F("* sense resistor supporting up to 30A.                          *"));
  Serial.println(F("* Set the baud rate to 115200 and select the newline terminator.*"));
  Serial.println(F("*****************************************************************"));
}

//! Print the main menu
void print_main_menu()
{
  Serial.println(F("\n\nMain Menu"));
  Serial.println(F("  1. Operation control menu"));
  Serial.println(F("  2. Read Status and Alert Registers"));
  Serial.println(F("  3. Read measurements (I,P,V,TEMP,VCC)"));
  Serial.println(F("  4. Read accumulated measurements (C,E,TB)"));
  Serial.println(F("  5. Init accumulated measurements (C,E,TB)"));
  Serial.println(F("  6. Read min/max values of I,V,P,TEMP"));
  Serial.println(F("  7. GPIO control"));
  if (!LTC2947_SPI_Mode_Enabled)
    Serial.println(F("  8. Send ARA"));

#ifdef LTC2947_DEBUG
  Serial.println(F("  99. LTC2947_DoubleToBytes_Test"));
#endif
  Serial.print(F("Enter: "));
}

//! LTC2947's operation control menu
void mm1_operation_control()
{
  Serial.println(F("\n\nOperation control"));
  Serial.println(F(" 1. Continuous measurement"));
  Serial.println(F(" 2. Single measurement"));
  Serial.println(F(" 3. Clear measurement results"));
  Serial.println(F(" 4. Stop measurement"));
  Serial.println(F(" 5. Shutdown"));
  Serial.println(F(" 6. Wakeup"));
  Serial.println(F(" 7. Read current operation mode"));
  Serial.println(F(" 8. RESET"));
  Serial.println(F(" Any other number to cancel"));
  Serial.print(F("Enter: "));

  while (!Serial.available()); //! wait for user input
  int32_t opt = read_int();    //! Read user input command
  Serial.println(opt);         //! echo user input
  switch (opt)
  {
    case 1: //! Continuous measurement
      LTC2947_WR_BYTE(LTC2947_REG_OPCTL, LTC2947_BM_OPCTL_CONT);
      break;
    case 2: //! Single measurement
      LTC2947_WR_BYTE(LTC2947_REG_OPCTL, LTC2947_BM_OPCTL_SSHOT);
      break;
    case 3: //! Clear measurement results
      {
        byte data[1];
        LTC2947_RD_BYTE(LTC2947_REG_OPCTL, data); //! store current operation mode
        LTC2947_WR_BYTE(LTC2947_REG_OPCTL,
                        LTC2947_BM_OPCTL_CLR |        //! set clear bit
                        (data[0] & LTC2947_BM_OPCTL_CONT) //! restore last operation mode (idle or cont. meas.)
                       );
      }
      break;
    case 4: //! Stop measurement
      LTC2947_WR_BYTE(LTC2947_REG_OPCTL, 0);
      break;
    case 5: //! Shutdown
      LTC2947_WR_BYTE(LTC2947_REG_OPCTL, LTC2947_BM_OPCTL_SHDN);
      break;
    case 6: //! Wakeup
      {
        int16_t wakeupTime = LTC2947_wake_up();
        if (wakeupTime < 0)
          Serial.println(F("Failed to wakeup LTC2947 due to timeout"));
        else
        {
          Serial.print(F("LTC2947 woke up after "));
          Serial.print(wakeupTime);
          Serial.println(F(" milliseconds"));
        }
      }
      break;
    case 7: //! Read current operation mode
      {
        byte data[1];
        LTC2947_RD_BYTE(LTC2947_REG_OPCTL, data);
        if (bitMaskSetChk(data[0], LTC2947_BM_OPCTL_CONT))
          Serial.println(F("Continuous measurement enabled"));
        else if (bitMaskSetChk(data[0], LTC2947_BM_OPCTL_SHDN))
          Serial.println(F("LTC2947 was in shutdown mode and should\nhave been woken up by this read operation"));
        else
          Serial.println(F("Idle mode"));
      }
      break;
    case 8: //! make a reset of LTC2947
      LTC2947_WR_BYTE(LTC2947_REG_OPCTL, LTC2947_BM_OPCTL_RST);
      delay(120); //! give device some time to restart
      go_idle_cfg_tbctl(); //! re-configure TBCTL
      break;
    default:
      Serial.println(F("Unsupported command, canceled!"));
      break;
  }
}

//! Read and print all status / alert registers
void mm2_read_status_alert_registers()
{
  Serial.println(F("\nSTATUS / ALERT registers"));
  uint8_t values[8];
  LTC2947_RD_BYTES(LTC2947_REG_STATUS, 8, values);
  for (uint8_t i = 0; i < 8; i++)
    parse_status_alert_regs(values, i);
}

//! Read measurement results
void mm3_measurements()
{
  Serial.println(F("\nMeasurements"));
  float current, power, voltage, temp, vcc;

  LTC2947_SetPageSelect(false); //! make sure we are on page 0
  LTC2947_Read_I_P_V_TEMP_VCC(&current, &power, &voltage, &temp, &vcc);

  Serial.print(F("Current(A): "));
  Serial.println(current, 3);

  Serial.print(F("Power(W)  : "));
  Serial.println(power, 2);

  Serial.print(F("Voltage(V): "));
  Serial.println(voltage, 3);

  Serial.print(F("Temp.(C)  : "));
  Serial.println(temp, 1);

  Serial.print(F("Supply(V) : "));
  Serial.println(vcc, 2);

  Serial.println();
}

//! Read accumulated measurement results
void mm4_accu_measurements()
{
  Serial.println(F("\nAccumulated measurements"));
  double C, E, TB;
  boolean accuSet1 = true; //! start with accu set 1
  LTC2947_SetPageSelect(false); //! make sure we are on page 0
  do
  {
    LTC2947_Read_C_E_TB(accuSet1, &C, &E, &TB); //! read values from device
    Serial.print(F("\nAccu set "));
    Serial.println(accuSet1 ? 1 : 2);

    Serial.print(F("Charge(As): "));
    Serial.println(C, 4); // smallest increment is roughly 3 mA * 100 ms = 0.0003 As

    Serial.print(F("Energy(Ws): "));
    Serial.println(E, 3); // smallest increment is roughly 50 mW * 100 ms = 0.005 Ws

    Serial.print(F("Time(s)   : "));
    Serial.println(TB, 1); // smallest increment is roughly 100 ms = 0.1 s

    if (!accuSet1)
      break; //! DONE!
    accuSet1 = false;
  }
  while (true);
}

//! Initialize accumulators with user defined values
void mm5_accu_init()
{
  boolean accuSet1;
  byte bytes[16];   //! 16 bytes to store charge, energy and time
  uint8_t sel;
  boolean lsbs;
  double cetb;
  uint8_t accu_base_addr;

  //! Which ACCUs? (C1,E1,TB1 or C2,E2,TB2)
  Serial.println(F("\nInit Accumulator set 1 or 2?"));
  Serial.print(F("Enter: "));
  while (!Serial.available());
  sel = read_int();
  accuSet1 = (sel == 1);
  if (accuSet1)
    Serial.println(F("1 (C1,E1,TB1)"));
  else if (sel == 2)
    Serial.println(F("2 (C2,E2,TB2)"));
  else
    Serial.println(F("Unknown selection, will use C2,E2,TB2"));

  //! assign base register address of the accu set
  accu_base_addr = accuSet1 ? LTC2947_VAL_C1 : LTC2947_VAL_C2;

  //! Set accus in LSBs or in real values?
  Serial.println(F("\nInput LSBs or real values in As/Ws/s?"));
  Serial.println(F(" 1. LSBs"));
  Serial.println(F(" 2. Real Values in As/Ws/s"));
  Serial.print(F("Enter: "));
  while (!Serial.available());
  sel = read_int();
  lsbs = (sel == 1);
  if (lsbs)
    Serial.println(F("1 (LSBs)"));
  else if (sel == 2)
    Serial.println(F("2 (As/Ws/s)"));
  else
    Serial.println(F("Unknown selection, will use 1 (LSBs)"));

  //! Input new charge value
  Serial.println();
  Serial.print(accuSet1 ? F("C1: ") : F("C2: "));
  while (!Serial.available());
  if (lsbs)
    //! convert integer input to floating point value
    //! Note: actually this is not necessary as we have to
    //!       write integer values to the device anyway. So
    //!       we could convert this int value directly to
    //!       a bytes array and write it to the device instead of first going to float
    //!       and then going back to an integer representation
    //!       in form of a byte array. Still the focus of the code below
    //!       is to show the usage of the function LTC2947_DoubleToBytes
    cetb = read_int() * LTC2947_LSB_C1;
  else
    cetb = read_float();
  Serial.print(cetb, 8);
  Serial.println(F(" As"));
  LTC2947_DoubleToBytes(cetb, LTC2947_LSB_C1, bytes, 6); //! convert to bytes

  //! Input new energy value
  Serial.println();
  Serial.print(accuSet1 ? F("E1: ") : F("E2: "));
  while (!Serial.available());
  if (lsbs)
    cetb = read_int() * LTC2947_LSB_E1;
  else
    cetb = read_float();
  Serial.print(cetb, 8);
  Serial.println(F(" Ws"));
  LTC2947_DoubleToBytes(cetb, LTC2947_LSB_E1, bytes + 6, 6); //! convert to bytes

  //! Input new time value
  Serial.println();
  Serial.print(accuSet1 ? F("TB1: ") : F("TB2: "));
  while (!Serial.available());
  if (lsbs)
    cetb = read_int() * LTC2947_LSB_TB1;
  else
    cetb = read_float();
  Serial.print(cetb, 8);
  Serial.println(F(" s"));
  LTC2947_DoubleToBytes(cetb, LTC2947_LSB_TB1, bytes + 6 + 6, 4); //! convert to bytes

  Serial.print(F("\nC,E,TB will be written with following values starting at address 0x"));
  LTC2947_SerialPrint8hex(accu_base_addr);
  Serial.println();

  //! report the whole byte array
  for (int8_t i = 0; i < 16; i++)
  {
    Serial.print(i);
    Serial.print(F(":0x"));
    LTC2947_SerialPrint8hex(bytes[i]);
    if (i == 0)
      Serial.print(F(" (MSB)"));
    else if (i == 15)
      Serial.print(F(" (LSB)"));
    Serial.println();
  }

  LTC2947_SetPageSelect(false); //! make sure we are on page 0
  LTC2947_WR_BYTES(accu_base_addr, 16, bytes); //! write new values to the device

  //! readback values from the device
  Serial.println(F("\nRead back of both accu sets from device:"));
  mm4_accu_measurements();
}

//! Read tracking (min/max) measurement values
void mm6_min_max()
{
  float min, max;
  byte bytes[8];
  Serial.println(F("\nMIN/MAX measurements"));

  //! read current/power max/min from device
  LTC2947_RD_BYTES(LTC2947_VAL_IMAX, 8, bytes);

  //! current max/min
  //! LSB is given in mA, *1e-3 gives A. Note also the different LSB size of
  //! LTC2947_LSB_IMAX (or LTC2947_LSB_IMIN which is the same) compared to LTC2947_LSB_I!
  Serial.print(F("Current(A) "));
  max = LTC2947_2BytesToInt16(bytes + 0) * LTC2947_LSB_IMAX * 1e-3;
  min = LTC2947_2BytesToInt16(bytes + 2) * LTC2947_LSB_IMIN * 1e-3;
  print_min_max(min, max, 3, bytes + 2, bytes + 0);

  Serial.print(F("Power(W)   "));
  max = LTC2947_2BytesToInt16(bytes + 4) * LTC2947_LSB_PMAX * 1e-3;
  min = LTC2947_2BytesToInt16(bytes + 6) * LTC2947_LSB_PMIN * 1e-3;
  print_min_max(min, max, 3, bytes + 6, bytes + 4);

  //! read voltage/temperature max/min from device
  LTC2947_RD_BYTES(LTC2947_VAL_VMAX, 8, bytes);

  Serial.print(F("Voltage(V) "));
  max = LTC2947_2BytesToInt16(bytes + 0) * LTC2947_LSB_VMAX * 1e-3;
  min = LTC2947_2BytesToInt16(bytes + 2) * LTC2947_LSB_VMIN * 1e-3;
  print_min_max(min, max, 3, bytes + 2, bytes + 0);

  Serial.print(F("Temp.(C)   "));
  max = LTC2947_2BytesToInt16(bytes + 4) * LTC2947_LSB_TEMPMAX + LTC2947_OFFS_TEMPMAX;
  min = LTC2947_2BytesToInt16(bytes + 6) * LTC2947_LSB_TEMPMIN + LTC2947_OFFS_TEMPMIN;
  print_min_max(min, max, 2, bytes + 6, bytes + 4);
}

//! LTC2947's GPIO control menu
void mm7_gpio_ctrl()
{
  Serial.println(F("\n\nGPIO control"));
  Serial.println(F(" 1. Disable output driver"));
  Serial.println(F(" 2. Enable output driver"));
  Serial.println(F(" 3. Set GPO"));
  Serial.println(F(" 4. Clr GPO"));
  Serial.println(F(" 5. Read GPIO pin state"));
  Serial.print(F("Enter: "));

  while (!Serial.available()); //! wait for user input
  int32_t opt = read_int();    //! Read user input command
  Serial.println(opt);         //! echo user input
  switch (opt)
  {
    case 1:
    case 2:
      Serial.print(F("GPIO output driver "));
      LTC2947_GPIO_PinMode((opt == 1)
                           ? INPUT
                           : OUTPUT);
      Serial.println((opt == 1)
                     ? F("disabled")
                     : F("enabled")
                    );
      break;
    case 3:
    case 4:
      Serial.print(F("GPIO pin state set "));
      LTC2947_GPIO_SetPinState((opt == 3)
                               ? HIGH
                               : LOW);
      Serial.println((opt == 3)
                     ? F("high")
                     : F("low")
                    );
      break;
    case 5:
      Serial.print(F("GPIO pin "));
      Serial.println(LTC2947_GPIO_Read()
                     ? F("high")
                     : F("low")
                    );
      break;
    default:
      Serial.println(F("Unsupported command, canceled!"));
      break;
  }
}

//! SMBus Alert Response Protocol
void mm8_ara()
{
  uint8_t slvAddr;
  uint8_t result;
  Serial.print(F("\nSend ARA, slave response:0x"));
  result = LTC2947_Ara(&slvAddr);
  LTC2947_SerialPrint8hex(slvAddr);
  switch (result)
  {
    case LTC2947_ARA_ERROR:
      Serial.println(F(" (ERROR: I2C communication failed)"));
      break;
    case LTC2947_ARA_LTC2947_RESPONSE:
      Serial.println(F(" (LTC2947 response)"));
      break;
    case LTC2947_ARA_OTHER_RESPONSE:
      Serial.println(F(" (other slave response)"));
      break;
    case LTC2947_ARA_NO_RESPONSE:
      Serial.println(F(" (no slave response)"));
      break;
    case LTC2947_ARA_RESPONSE_WO_WR:
      if (slvAddr == LTC2947_I2C_Slave_Addr)
        Serial.println(F(" (ERROR: LTC2947 response without WR bit)"));
      else
        Serial.println(F(" (ERROR: other slave response without WR bit)"));
      break;
  }
}

//! prints two (min/max) values with label including the 16 bit raw values
void print_min_max(float min, float max, uint8_t digits, byte *minBytes, byte *maxBytes)
{
  Serial.print(F("MIN: "));
  if (min >= 0)
    Serial.print('+');
  Serial.print(min, digits); //! print float value
  Serial.print(F(" (0x"));
  //! print integer value
  LTC2947_SerialPrint16hex(LTC2947_2BytesToInt16(minBytes));
  Serial.print(F(") / MAX: "));
  if (max >= 0)
    Serial.print('+');
  Serial.print(max, digits); //! print float value
  Serial.print(F(" (0x"));
  //! print integer value
  LTC2947_SerialPrint16hex(LTC2947_2BytesToInt16(maxBytes));
  Serial.println(')');
}

//! put LTC2947 into idle mode and configure TBCTL
void go_idle_cfg_tbctl()
{
  byte data[1];
  byte tbctl;
  LTC2947_RD_BYTE(LTC2947_REG_OPCTL, data); //! check if we are in idle mode
  if (data[0] != 0) //! not in idle mode
  {
    Serial.println(F("Will set LTC2947 to IDLE mode now!"));
    LTC2947_WR_BYTE(LTC2947_REG_OPCTL, 0);//! go to idle mode
    delay(200); //! after 2 system tickes (2x100ms) LTC2947 must be in idle mode
    LTC2947_RD_BYTE(LTC2947_REG_OPCTL, data);
  }
  if (data[0] == 0)
  {
    Serial.println(F("LTC2947 in IDLE mode now!"));
  }
  else
  {
    Serial.println(F("Unable to set LTC2947 to IDLE mode!"));
  }

  LTC2947_SetPageSelect(false); //! make sure we are on page 0

  tbctl = //! generate TBCTL configuration value
    LTC2947_PRE << BM2BITPOS(LTC2947_BM_TBCTL_PRE_0) |
    LTC2947_DIV << BM2BITPOS(LTC2947_BM_TBCTL_DIV_0);

  LTC2947_WR_BYTE(LTC2947_REG_TBCTL, tbctl); //! write PRE and DIV setting

  LTC2947_RD_BYTE(LTC2947_REG_TBCTL, data); //! read back configuration

  if (tbctl == data[0]) //! check if everything was written correctly
    Serial.println(F("Successfully configured LTC2947's TBCTL"));
  else
    Serial.println(F("Failed to configured LTC2947's TBCTL"));
}

//! parse status and alert registers. register_num (0..7) selects one of the 8 status/alert registers
//! statusRegs is a 8-byte array that holds the content of the status/alert registers read from the device
void parse_status_alert_regs(uint8_t statusRegs[8], uint8_t register_num)
{
  switch (register_num)
  {
    case 0:
      Serial.println(F("STATUS:"));
      Serial.print(F(" UPDATE:  "));
      Serial.println(bitMaskSetChk(statusRegs[0], LTC2947_BM_STATUS_UPDATE/*  */) ? 1 : 0);
      Serial.print(F(" ADCERR:  "));
      Serial.println(bitMaskSetChk(statusRegs[0], LTC2947_BM_STATUS_ADCERR/*  */) ? 1 : 0);
      Serial.print(F(" TBERR:   "));
      Serial.println(bitMaskSetChk(statusRegs[0], LTC2947_BM_STATUS_TBERR/*   */) ? 1 : 0);
      Serial.print(F(" PORA:    "));
      Serial.println(bitMaskSetChk(statusRegs[0], LTC2947_BM_STATUS_PORA/*    */) ? 1 : 0);
      Serial.print(F(" UVLOA:   "));
      Serial.println(bitMaskSetChk(statusRegs[0], LTC2947_BM_STATUS_UVLOA/*   */) ? 1 : 0);
      Serial.print(F(" UVLOD:   "));
      Serial.println(bitMaskSetChk(statusRegs[0], LTC2947_BM_STATUS_UVLOD/*   */) ? 1 : 0);
      Serial.print(F(" UVLOSTBY:"));
      Serial.println(bitMaskSetChk(statusRegs[0], LTC2947_BM_STATUS_UVLOSTBY/**/) ? 1 : 0);
      break;
    case 1:
      Serial.println(F("STATVT:"));
      Serial.print(F(" FANH:    "));
      Serial.println(bitMaskSetChk(statusRegs[1], LTC2947_BM_STATVT_FANH/*   */) ? 1 : 0);
      Serial.print(F(" FANL:    "));
      Serial.println(bitMaskSetChk(statusRegs[1], LTC2947_BM_STATVT_FANL/*   */) ? 1 : 0);
      Serial.print(F(" TEMPH:   "));
      Serial.println(bitMaskSetChk(statusRegs[1], LTC2947_BM_STATVT_TEMPH/*  */) ? 1 : 0);
      Serial.print(F(" TEMPL:   "));
      Serial.println(bitMaskSetChk(statusRegs[1], LTC2947_BM_STATVT_TEMPL/*  */) ? 1 : 0);
      Serial.print(F(" VH:      "));
      Serial.println(bitMaskSetChk(statusRegs[1], LTC2947_BM_STATVT_VH/*     */) ? 1 : 0);
      Serial.print(F(" VL:      "));
      Serial.println(bitMaskSetChk(statusRegs[1], LTC2947_BM_STATVT_VL/*     */) ? 1 : 0);
      break;
    case 2:
      Serial.println(F("STATIP:"));
      Serial.print(F(" IH:      "));
      Serial.println(bitMaskSetChk(statusRegs[2], LTC2947_BM_STATIP_IH) ? 1 : 0);
      Serial.print(F(" IL:      "));
      Serial.println(bitMaskSetChk(statusRegs[2], LTC2947_BM_STATIP_IL) ? 1 : 0);
      Serial.print(F(" PH:      "));
      Serial.println(bitMaskSetChk(statusRegs[2], LTC2947_BM_STATIP_PH) ? 1 : 0);
      Serial.print(F(" PL:      "));
      Serial.println(bitMaskSetChk(statusRegs[2], LTC2947_BM_STATIP_PL) ? 1 : 0);
      break;
    case 3:
      Serial.println(F("STATC:"));
      Serial.print(F(" C1H      "));
      Serial.println(bitMaskSetChk(statusRegs[3], LTC2947_BM_STATC_C1H) ? 1 : 0);
      Serial.print(F(" C1L      "));
      Serial.println(bitMaskSetChk(statusRegs[3], LTC2947_BM_STATC_C1L) ? 1 : 0);
      Serial.print(F(" C2H      "));
      Serial.println(bitMaskSetChk(statusRegs[3], LTC2947_BM_STATC_C2H) ? 1 : 0);
      Serial.print(F(" C2L      "));
      Serial.println(bitMaskSetChk(statusRegs[3], LTC2947_BM_STATC_C2L) ? 1 : 0);
      break;
    case 4:
      Serial.println(F("STATE:"));
      Serial.print(F(" E1H      "));
      Serial.println(bitMaskSetChk(statusRegs[4], LTC2947_BM_STATE_E1H) ? 1 : 0);
      Serial.print(F(" E1L      "));
      Serial.println(bitMaskSetChk(statusRegs[4], LTC2947_BM_STATE_E1L) ? 1 : 0);
      Serial.print(F(" E2H      "));
      Serial.println(bitMaskSetChk(statusRegs[4], LTC2947_BM_STATE_E2H) ? 1 : 0);
      Serial.print(F(" E2L      "));
      Serial.println(bitMaskSetChk(statusRegs[4], LTC2947_BM_STATE_E2L) ? 1 : 0);
      break;
    case 5:
      Serial.println(F("STATCEOF:"));
      Serial.print(F(" C1OF     "));
      Serial.println(bitMaskSetChk(statusRegs[5], LTC2947_BM_STATCEOF_C1OF) ? 1 : 0);
      Serial.print(F(" C2OF     "));
      Serial.println(bitMaskSetChk(statusRegs[5], LTC2947_BM_STATCEOF_C2OF) ? 1 : 0);
      Serial.print(F(" E1OF     "));
      Serial.println(bitMaskSetChk(statusRegs[5], LTC2947_BM_STATCEOF_E1OF) ? 1 : 0);
      Serial.print(F(" E2OF     "));
      Serial.println(bitMaskSetChk(statusRegs[5], LTC2947_BM_STATCEOF_E2OF) ? 1 : 0);
      break;
    case 6:
      Serial.println(F("STATTB:"));
      Serial.print(F(" TB1OF    "));
      Serial.println(bitMaskSetChk(statusRegs[6], LTC2947_BM_STATTB_TB1OF) ? 1 : 0);
      Serial.print(F(" TB2OF    "));
      Serial.println(bitMaskSetChk(statusRegs[6], LTC2947_BM_STATTB_TB2OF) ? 1 : 0);
      Serial.print(F(" TB1TH    "));
      Serial.println(bitMaskSetChk(statusRegs[6], LTC2947_BM_STATTB_TB1TH) ? 1 : 0);
      Serial.print(F(" TB2TH    "));
      Serial.println(bitMaskSetChk(statusRegs[6], LTC2947_BM_STATTB_TB2TH) ? 1 : 0);
      break;
    case 7:
      Serial.println(F("STATVDVCC:"));
      Serial.print(F(" VDVCCH   "));
      Serial.println(bitMaskSetChk(statusRegs[7], LTC2947_BM_STATVDVCC_VDVCCH) ? 1 : 0);
      Serial.print(F(" VDVCCL   "));
      Serial.println(bitMaskSetChk(statusRegs[7], LTC2947_BM_STATVDVCC_VDVCCL) ? 1 : 0);
      break;
    default:
      Serial.print(register_num);
      Serial.println(F(" is not available!"));
      break;
  }
}

//! Setup LTC2947's serial interface mode
void setup_ser_mode()
{
  do
  {
    Serial.println(F("\n\nSelect serial mode for LTC2947"));
    Serial.println(F("  1. I2C (DEFAULT)"));
    Serial.println(F("  2. SPI"));
    Serial.print(F("Enter: "));
    while (Serial.available())
      ;
    int32_t i = read_int();
    Serial.println(i); //! echo selection
    if (i == 0)
    {
      Serial.println(F("Will use DEFAULT (I2C)"));
      i = 1;
    }
    if (i == 1)
    {
      do
      {
        Serial.println(F("\n\nEnter 7-bit I2C slave address for LTC2947 (DEFAULT:0x5C)"));
        while (Serial.available())
          ;
        uint8_t slv = read_int();
        if (slv == 0)
        {
          Serial.println(F("Will use DEFAULT (0x5C)"));
          slv = 0x5C;
        }
        Serial.print(F("Slave address: 0x"));
        Serial.println(slv, HEX);
        switch (slv)
        {
          case LTC2947_I2C_ADDR_LL:
          case LTC2947_I2C_ADDR_LH:
          case LTC2947_I2C_ADDR_LR:
          case LTC2947_I2C_ADDR_RL:
          case LTC2947_I2C_ADDR_RH:
          case LTC2947_I2C_ADDR_RR:
            LTC2947_InitI2C(slv);   //! set LTC2947 library to I2C mode
            quikeval_I2C_init();    //! Configure I2C port for 100kHz
            quikeval_I2C_connect(); //! Connect I2C to main data port
            return;
          default:
            Serial.println(F("Unsupported slave address, please try again!"));
            break;
        }

      }
      while (true);
    }
    else if (i == 2)
    {
      LTC2947_InitSPI(); //! set LTC2947 library to SPI mode
      spi_enable(SPI_CLOCK_DIV16);  //! Configure the spi port for 1MHz SCK (16MHz / 16)
      quikeval_SPI_connect();       //! Connect SPI to main data port
      return;
    }
    else
    {
      Serial.println(F("Please enter 1 or 2!"));
    }
  }
  while (true);
}

Download LTC2947 - DC2574A_Kit.ino File

/**
\date 6/17/2016

\brief
Linduino.INO File for DC2574A_KIT: Standalone power/energy monitor for evaluation of the LTC2947 monitor without a PC
Linear Technology DC2574A_KIT Demonstration Board: Standalone power/energy monitor for evaluation of the LTC2947 monitor without a PC.
LTC2947: high-precision power and energy monitor with an internal sense resistor supporting up to 30A.

NOTE:
The DC2574A-KIT includes the following items:
- DC2334A: LTC2947 Demo Circuit
- DC2026: Linduino with Pre-Programmed "LTC2947 - DC2574A_KIT Linduino.INO File" (THIS FILE!)
- LCD Keypad Shield
- USB Cable: Power Supply And Optional Communication Interface for Operating with the GUI
- 14-pin Ribbon Cable.

The DC2574A_KIT Linduino sketch contains a lot of code that is only necessary to operate with the
LCD, the serial ports and to allow compatibility with DC590 commands (see usage of GUI together with
the DC2574A-KIT). This code might not be of interest by the customer that wants to focuse on functionality
of the LTC2947. The focuse on LTC2947 relevant code only, just search for LTC2947_ within this source file.

Setup:
Set the terminal baud rate to 115200 and de-select the newline terminator. (That means
e.g. within the serial monitor of the Arduino IDE select "No line ending")
Refer to Demo Manual DC2574A_KIT.
Ensure all jumpers are installed in the factory default positions.
Especially make sure the board is configured to I2C mode with slave address set
to 0x5C (LL), see also below the define LTC2947_I2C_ADDRESS

Command Description:
After start-up the DC2574A_KIT Linduino sketch will send the following lines
on the connected serial com port:

Hello LTC2947
30A PowerMonitor
LCD found! Enter l or m for options.

- or in case no LCD Keypad Shield is plugged on top of the DC2334A demoboard -

No LCD found! Enter l or m for options.

In any case commands l or m can be send:
l-command: Status/Alert regsierts, the following submenu will be shown:
0:STATUS
1:STATVT
2:STATIP
3:STATC
4:STATE
5:STATCEOF
6:STATTB
7:STATVDVCC
8:CYCLE ALL
enter 0-8, any other to cancel

Select one out of 0-7 to report a single status/alert register or 8 to cycle
through all status/alert registers. In both cases new values will be reported
every second. E.g. for option 0 (STATUS) you will get the following output:
STAT:UPD,

STAT:UPD,
...

The bits of the status/alert registers that are set to 1 will be reported, e.g. UPD
is the UPDATE bit of the STATUS register that tells a new conversion cycle
was performed.

Option 8 (CYCLE ALL) will show the following output:

STAT:UPD,

STATVT:

STATIP:

STATC:

STATE:

STATCEOF:

STATTB:

STATVDVCC:

In the example output above only the UPDATE bit was set

m-command: Measurement values, the following submenu will be shown:
0: I V
P TEMP
1:C1 VDVCC
E1 TB1
2:C2 VDVCC
E2 TB2
3:IMIN IMAX
PMIN PMAX
4:VMIN VMAX
TMIN TMAX
5:TMIN TMAX
VDMIN VDMAX
6:CYCLE ALL
measurements
enter 0-6, any other to cancel

Select one out of 0-5 to report a single set of measurement values or 8 to cycle
through all measurements. In both cases new values will be reported
every second. E.g. for option 0 (I V P TEMP) you will get the following output:

I V
P TEMP
0.012A -0.342V
0.000W 30.2°C
0.006A -0.342V
0.000W 30.2°C
0.012A -0.342V
0.000W 30.2°C
I V
P TEMP
0.012A -0.342V
0.000W 30.2°C
....

Current, Voltage, Power and die temperature are reported every second.
Option 6 (CYCLE ALL) will show the following output:

I V
P TEMP
0.006A -0.342V
0.000W 30.2°C
0.012A -0.342V
0.000W 30.2°C
0.012A -0.342V
0.000W 30.2°C
C1 VDVCC
E1 TB1
4.83mAh 4.50V
0.00mWh 34m35s
4.83mAh 4.50V
0.00mWh 34m36s
4.83mAh 4.50V
0.00mWh 34m37s
C2 VDVCC
E2 TB2
4.84mAh 4.64V
0.00mWh 34m40s
4.84mAh 4.50V
0.00mWh 34m41s
4.85mAh 4.50V
0.00mWh 34m42s
....

In case a LCD Keypad Shield is plugged on top of the DC2334A demoboard, all
output will also be shown on the LCD.

DC590B compatibility:
Appart from commands m and l the DC2574A_KIT sketch also supports a subset of the DC590B commands.
This way the LTC2947 GUI is also able to communicate with the DC2574A_KIT sketch which enables the
user to use the GUI to change LTC2947 configuration e.g. to set alarm thresholds etc.
The main limitations of this compatibility mode are:
- EEPROM read from the DC2334A demo board is faked (it will always report the right EEPROM string)
- SPI operation is not supported, due to incompatibility with the optional LCD Keypad Shield

See tinyDC590.cpp for details

http://www.linear.com/product/LTC2947

http://www.linear.com/product/LTC2947#demoboards

REVISION HISTORY
$Revision: 5954 $
$Date: 2016-10-20 12:05:26 -0700 (Thu, 20 Oct 2016) $

Copyright (c) 2016, Linear Technology Corp.(LTC)
All rights reserved.

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.

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 OWNER 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.

The views and conclusions contained in the software and documentation are those
of the authors and should not be interpreted as representing official policies,
either expressed or implied, of Linear Technology Corp.

The Linear Technology Linduino is not affiliated with the official Arduino team.
However, the Linduino is only possible because of the Arduino team's commitment
to the open-source community.  Please, visit http://www.arduino.cc and
http://store.arduino.cc , and consider a purchase that will help fund their
ongoing work.
*/

/*! @file
@ingroup LTC2947
Linduino .INO file for LTC2947 a high-precision power and energy monitor with an internal sense resistor supporting up to 30A.
*/

#include <SPI.h>
#include <LT_SPI.h>
#include <LT_I2C.h>
#include <Linduino.h>
#include <LiquidCrystal.h>
#include "LCDKeypad.h"
#include "DC590_subset.ino"

//! DC590 busy check. Declared and controlled by DC590_subset.ino
//! true if DC590 is busy processing commands, false if not busy
extern boolean dc590Busy;

/** \brief Edit LTC2947.h to adjust the following settings:
  Internal / External clock operation and frequency setting
  I2C / SPI mode operation
  In case of I2C: Slave address configuration
*/
#include "LTC2947.h"

/** \brief
Define either LTC2947_I2C_MODE_ENABLE or LTC2947_SPI_MODE_ENABLE to set the communication interface to I2C or SPI
NOTE: It is not possible to operate in SPI mode together with the LCD due to pin conflicts. If you want to
operate in SPI mode you have to remove the LCD and connect DC2334A via the 14-pin ribbon cable to J1 of Linduino
instead of connecting DC2334A as a shield on top of Linduino.
*/
#define LTC2947_I2C_MODE_ENABLE
//#define LTC2947_SPI_MODE_ENABLE

/** \brief  Only one mode is possible at a time. If I2C and SPI are both enabled, I2C mode will be used! */
#ifdef LTC2947_I2C_MODE_ENABLE
#undef LTC2947_SPI_MODE_ENABLE
#endif

/** \brief  update interval time in milli seconds */
#define SCREEN_UPDATE_TIME    1000UL
#define LABEL_DELAY           2000UL
#define SPLASH_SCREEN_DELAY   3000UL
/** \brief Menu Group 1 count: STATUS STATVT STATIP STATC STATE STATCEOF STATTB STATVDVCC "CYCLE stats" */
#define USER_SEL_GRP1_COUNT 9
/** \brief  Menu Group 1 cycle count: STATUS STATVT STATIP STATC STATE STATCEOF STATTB STATVDVCC */
#define USER_SEL_GRP1_CYCLE_COUNT 8
/** \brief  Menu group 2 count: RAW, ACCU1, ACCU2, Track1, Track2, Track3, "Cycle meas." */
#define USER_SEL_GRP2_COUNT 7
/** \brief  Menu Group 2 cycle count:  RAW, ACCU1, ACCU2, Track1, Track2, Track3 */
#define USER_SEL_GRP2_CYCLE_COUNT 6
/** \brief  defines how many cycles a single measurement group will be displayed on the LCD in case of "cycle all" (2 means each measurement is shown 3 times) */
#define USERGROUPCYCLETIMERRELOAD 3

#define MEAS_LABEL00 F("I V")
#define MEAS_LABEL01 F("P TEMP")
#define MEAS_LABEL10 F("C1 VDVCC")
#define MEAS_LABEL11 F("E1 TB1")
#define MEAS_LABEL20 F("C2 VDVCC")
#define MEAS_LABEL21 F("E2 TB2")
#define MEAS_LABEL30 F("IMIN IMAX")
#define MEAS_LABEL31 F("PMIN PMAX")
#define MEAS_LABEL40 F("VMIN VMAX")
#define MEAS_LABEL41 F("TMIN TMAX")
#define MEAS_LABEL50 F("TMIN TMAX")
#define MEAS_LABEL51 F("VDMIN VDMAX")
#define MEAS_LABEL60 F("CYCLE ALL")
#define MEAS_LABEL61 F("measurements")

/** \brief  The next update time. Stores the next time stamp (used for timing of update task) */
unsigned long nextUpdateTime = 0;

/** \brief  pointer to the LCD keypad object */
LCDKeypad *lcd;
/** \brief  The previous key. Stores previously pressed key*/
int8_t previousKey = KEYPAD_NONE;
/** \brief  flag to store actions from user input. */
boolean flagClear = false;
/** \brief  User selection of the first group. */
uint8_t userSelGroup1 = 0;
/** \brief  group 1 cycle enable */
boolean userGroup1Cycle = false;
/** \brief  User selection of the second group. */
uint8_t userSelGroup2 = 0;
/** \brief  group 2 cycle enable */
boolean userGroup2Cycle = false;
/** \brief selected group 1: false, 2: true */
boolean userGroupSelect = true;
/** \brief  flag is asserted once after all status registers were read. */
boolean statusRegsRead = false;
/** \brief  array to store all status / alert registers */
byte statusRegs[8];
/** \brief  The user group cycle timer. */
uint8_t userGroupCycleTimer = 0;

/**  \fn  void setup()
 \brief general initialization */
void setup()
{
  // init serial port
  Serial.begin(115200);
  // connect to LCD
  lcd = new LCDKeypad(10);
#ifdef LTC2947_SPI_MODE_ENABLE
  lcd->Disable4BitLCD();
  LTC2947_InitSPI();
  spi_enable(SPI_CLOCK_DIV16);  // Configure the spi port for 1MHz SCK (16MHz / 16)
  quikeval_SPI_connect();
#else
  lcd->searchForLCD();
  // set LTC2947 library to I2C mode
  LTC2947_InitI2C(LTC2947_I2C_ADDR_LL);
  // Initializes Linduino I2C port.
  quikeval_I2C_init();
  // Connects I2C to QuikEval port
  quikeval_I2C_connect();
#endif
  // make a reset of LTC2947
  LTC2947_WR_BYTE(LTC2947_REG_OPCTL, LTC2947_BM_OPCTL_RST);
  // give device some time to restart
  DelayAndCheckSerialData(120);
  // write PRE and DIV setting
  LTC2947_WR_BYTE(LTC2947_REG_TBCTL,
                  LTC2947_PRE << BM2BITPOS(LTC2947_BM_TBCTL_PRE_0) |
                  LTC2947_DIV << BM2BITPOS(LTC2947_BM_TBCTL_DIV_0));
  // enable continuous measurement on LTC2947
  LTC2947_WR_BYTE(LTC2947_REG_OPCTL, LTC2947_BM_OPCTL_CONT);
  CLEAR_SCREEN();
  lcd->EnableSerialOutput = true;
  PRINTSCREEN(F("Hello LTC2947"));
  NEWLINE();
  PRINTSCREEN(F("30A PowerMonitor"));
  lcd->EnableSerialOutput = false;
  Serial.println();
  if (!lcd->foundLCD)
    Serial.print(F("No "));
  Serial.println(F("LCD found! Enter l or m for options."));
  Serial.flush();
  DelayAndProcessInputs(SPLASH_SCREEN_DELAY); //DelayAndCheckSerialData(SPLASH_SCREEN_DELAY);
}

/**  \fn  void update()
\brief update LTC2947 measurement results */
void update()
{
  // general buffer to hold data from 2947
  byte data[8];
  // stores the last value or PGCTL
  boolean lastPageSelect;
  // 1st check if we will actually read values
  // from the device or just show labels of
  // measured values on the LCD
  if (!flagClear && userGroupSelect)
  {
    // labels might only be shown if no clear is requested and measurement values will be shown
    boolean showHeader = false;
    if (userGroupCycleTimer == 0)
    {
      if (userGroup2Cycle)
        userSelGroup2 = (userSelGroup2 + 1) % USER_SEL_GRP2_CYCLE_COUNT;
      userGroupCycleTimer = USERGROUPCYCLETIMERRELOAD;
      showHeader = true;
    }
    else
      userGroupCycleTimer--;
    if (showHeader)
    {
      CLEAR_SCREEN();
      switch (userSelGroup2)
      {
        case 0:
          PRINTSCREEN(MEAS_LABEL00);
          NEWLINE();
          PRINTSCREEN(MEAS_LABEL01);
          DelayAndProcessInputs(LABEL_DELAY);
          return;
        case 1:
          PRINTSCREEN(MEAS_LABEL10);
          NEWLINE();
          PRINTSCREEN(MEAS_LABEL11);
          DelayAndProcessInputs(LABEL_DELAY);
          return;
        case 2:
          PRINTSCREEN(MEAS_LABEL20);
          NEWLINE();
          PRINTSCREEN(MEAS_LABEL21);
          DelayAndProcessInputs(LABEL_DELAY);
          return;
        case 3:
          PRINTSCREEN(MEAS_LABEL30);
          NEWLINE();
          PRINTSCREEN(MEAS_LABEL31);
          DelayAndProcessInputs(LABEL_DELAY);
          return;
        case 4:
          PRINTSCREEN(MEAS_LABEL40);
          NEWLINE();
          PRINTSCREEN(MEAS_LABEL41);
          DelayAndProcessInputs(LABEL_DELAY);
          return;
        default:
          PRINTSCREEN(MEAS_LABEL50);
          NEWLINE();
          PRINTSCREEN(MEAS_LABEL51);
          DelayAndProcessInputs(LABEL_DELAY);
          return;
      }
    }
  }
  // check if we are in cont mode
  LTC2947_RD_BYTE(LTC2947_REG_OPCTL, data);
  if (bitMaskClrChk(data[0], LTC2947_BM_OPCTL_CONT))
  {
    // enable continuous measurement on LTC2947
    LTC2947_WR_BYTE(LTC2947_REG_OPCTL, LTC2947_BM_OPCTL_CONT);
    return; // we were in IDLE, so nothing todo here
  }
  // store page select
  // this is needed to restore last selected page to avoid page missmatch
  // if the LTC2947 GUI is also communicating with Linduino
  lastPageSelect = LTC2947_GetCurrentPageSelect();
  if (lastPageSelect)
    LTC2947_SetPageSelect(false); // go to page 0 where the measuremnt results are stored
#ifdef LTC2947_AUTO_READ_STATUS
  // reading the STATUS register would also affect the GUI as status register bits are CoR!!!
  // for this reason the following code is not enabled!
  // still KEYPAD_RIGHT allows read of STATUS on demand and print result on LCD
  // read all status and alert (if alerts are used it is important to read all in a single burst of 8 bytes!)
  LTC2947_RD_BYTES(LTC2947_REG_STATUS, 8, data);
  // if UPDATE bit is set we have a new conversion result
  if (bitMaskSetChk(data[0], LTC2947_BM_STATUS_UPDATE))
    return; // UPDATE bit not set, so no new data
#endif
  if (flagClear)
  {
    // clear everything
    flagClear = false;
    NEWLINE();
    PRINTSCREEN(F("****CLEARED*****"));
    LTC2947_WR_BYTE(LTC2947_REG_OPCTL, 0); // leave continuous mode and go to IDLE
    DelayAndCheckSerialData(100);
    LTC2947_WR_BYTE(LTC2947_REG_OPCTL, LTC2947_BM_OPCTL_CLR); // clear tracking registers and accumulators
    DelayAndCheckSerialData(50);
    LTC2947_WR_BYTE(LTC2947_REG_OPCTL, LTC2947_BM_OPCTL_CONT); // enable continuous
  }
  else
  {
    if (userGroupSelect)
    {
      switch (userSelGroup2)
      {
        case 0://PRINTSCREEN(F("I V P TEMP"));
          {
            // show RAW quantities like I,P,V,TEMP
            float I, P, V, TEMP, VCC;
            LTC2947_Read_I_P_V_TEMP_VCC(&I, &P, &V, &TEMP, &VCC);
            CheckSerialData();// we execute CheckSerialData() from time to time here to make sure the uart rx buffer never overflows
            // print current and limit decimal points for values -30.00 ... -10.00 (but -9.999 ... 30.000)
            CLEAR_SCREEN();
            PRINTSCREEN2(I, I > -10.0 ? 3 : 2);
            PRINTSCREEN('A');
            SCREENSETCURSOR(8, 0);
            PRINTSCREEN2(V, 3);
            PRINTSCREEN('V');
            CheckSerialData();
            NEWLINE();
            PRINTSCREEN2(P, 3); // max characters: -450.000W = 9: 0..8
            PRINTSCREEN('W');
            boolean limitTempCharacters = P <= -100.0;
            SCREENSETCURSOR(10, 1);
            PRINTSCREEN2(TEMP, limitTempCharacters ? 0 : 1);
            WRITE_DEGREE_C();
          }
          break;
        case 1://PRINTSCREEN(F("C1 VDVCC E1 TB1"));
        case 2://PRINTSCREEN(F("C2 VDVCC E2 TB2"));
          {
            double C, E, TB;
            boolean signC = true, signE = true;
#ifndef LTC2947_READ_SIGNED_ACCUS
            LTC2947_Read_Abs_C_E_TB(userSelGroup2 == 1, &C, &signC, &E, &signE, &TB); //Read either C1/2,E1/2,TB1/2
#else
            // instead of using LTC2947_Read_Abs_C_E_TB we could also call LTC2947_Read_C_E_TB to
            // get signed charge and energy values. But as the following code makes usage of
            // LTC2947_Read_Abs_C_E_TB's separation in absolute values and sign, we won't use
            // LTC2947_Read_C_E_TB here!
            LTC2947_Read_C_E_TB(userSelGroup2 == 1, &C, &E, &TB); //Read either C1/2,E1/2,TB1/2
            if (C < 0) C = -C;
            else signC = false;
            if (E < 0) E = -E;
            else signE = false;
#endif
            C = C * LTC2947_LSB_FACTOR_MILLI_HOURS; //! convert As to mAh
            E = E * LTC2947_LSB_FACTOR_MILLI_HOURS; //! convert Ws to mWh
            CLEAR_SCREEN();
            // convert to reasonable units
            if (C < 1e3)
            {
              // restore sign and print value in mAh
              PRINTSCREEN2(signC ? -C : C, 2); // e.g. -999.99mAh: 10 chars
              PRINTSCREEN(F("mAh"));
            }
            else if (C < 1e6)
            {
              // restore sign and print value in Ah
              C = C * 1e-3;
              PRINTSCREEN2(signC ? -C : C, 3); // e.g. -999.999Ah: 10 chars
              PRINTSCREEN(F("Ah"));
            }
            else if (C < 1e9)
            {
              // restore sign and print value in kAh
              C = C * 1e-6;
              PRINTSCREEN2(signC ? -C : C, 2); // e.g. -999.99kAh: 10 chars
              PRINTSCREEN(F("kAh"));
            }
            CheckSerialData();
            SCREENSETCURSOR(11, 0);
            LTC2947_RD_BYTES(LTC2947_VAL_VDVCC, 2, data); // read VDVCC[15:0]
            double vcc = LTC2947_BytesToDouble(data, 2, true, LTC2947_LSB_VDVCC * 1e-3); // convert to real volts
            PRINTSCREEN2(vcc, 2);
            PRINTSCREEN('V'); // e.g. 4.65V: 5 chars
            CheckSerialData();
            NEWLINE();
            // convert to reasonable units
            if (E < 1e3)
            {
              // restore sign and print value in mWh
              PRINTSCREEN2(signE ? -E : E, signE && (E >= 1e2) ? 1 : 2);
              PRINTSCREEN(F("mWh"));
            }
            else if (E < 1e6)
            {
              // restore sign and print value in Wh
              E = E * 1e-3;
              PRINTSCREEN2(signE ? -E : E, signE && (E >= 1e5) ? 2 : 3);
              PRINTSCREEN(F("Wh"));
            }
            else if (E < 1e9)
            {
              // restore sign and print value in kWh
              E = E * 1e-6;
              PRINTSCREEN2(signE ? -E : E, signE && (E >= 1e8) ? 1 : 2);
              PRINTSCREEN(F("kWh"));
            }
            CheckSerialData();
            SCREENSETCURSOR(10, 1);
            // convert to reasonable units
            if (TB < 60.0) // less than one minute
            {
              //print time in seconds
              PRINTSCREEN2(TB, 2);
              PRINTSCREEN('s');//e.g. 59.9s
            }
            else if (TB < 60.0 * 60.0) // less than one hour
            {
              //print time in minutes and seconds
              uint16_t intSec = TB;
              PRINTSCREEN(intSec / 60);
              PRINTSCREEN('m');
              PRINTSCREEN(intSec % 60);
              PRINTSCREEN('s');//e.g. 59m59s
            }
            else if (TB < 60.0 * 60.0 * 24.0)
            {
              //print time in hours and minutes
              uint16_t intMin = TB / 60.0;
              PRINTSCREEN(intMin / 60);
              PRINTSCREEN('h');
              PRINTSCREEN(intMin % 60);
              PRINTSCREEN('m');//e.g. 23h59m
            }
            else if (TB < 60.0 * 60.0 * 24.0 * 99.0)
            {
              //print time in days and hours
              uint16_t inthour = TB / (60.0 * 60.0);
              PRINTSCREEN(inthour / 24);
              PRINTSCREEN('d');
              PRINTSCREEN(inthour % 24);
              PRINTSCREEN('h');//e.g. 99d23h
            }
          }
          break;
        case 3://IMIN IMAX / PMIN PMAX
          {
            float floatHelp;
            LTC2947_RD_BYTES(LTC2947_VAL_IMAX, 8, data); // IMAX[15:0] IMIN[15:0] PMAX[15:0] PMIN[15:0] starting at data[0]
            CheckSerialData();
            floatHelp = LTC2947_2BytesToInt16(data + 2) * LTC2947_LSB_IMIN * 1e-3;// calc min current in amps
            CLEAR_SCREEN();
            PRINTSCREEN2(floatHelp, 2); //e.g. -30.00...30.00A..-30.00A
            PRINTSCREEN(F("..."));
            floatHelp = LTC2947_2BytesToInt16(data) * LTC2947_LSB_IMAX * 1e-3;
            PRINTSCREEN2(floatHelp, 2);
            PRINTSCREEN('A');
            CheckSerialData();
            floatHelp = LTC2947_2BytesToInt16(data + 6) * LTC2947_LSB_PMIN * 1e-3;// calc power in watts
            NEWLINE();
            PRINTSCREEN2(floatHelp, 1); // e.g. -450.0W
            PRINTSCREEN(F("..."));
            floatHelp = LTC2947_2BytesToInt16(data + 4) * LTC2947_LSB_PMAX * 1e-3;
            PRINTSCREEN2(floatHelp, 1);
            PRINTSCREEN('W');
          }
          break;
        case 4://VMIN VMAX / TMIN TMAX
          {
            float floatHelp;
            LTC2947_RD_BYTES(LTC2947_VAL_VMAX, 8, data); // VMAX[15:0] VMIN[15:0] TEMPMAX[15:0] TEMPMIN[15:0] starting at data[0]
            CheckSerialData();
            floatHelp = LTC2947_2BytesToInt16(data + 2) * LTC2947_LSB_VMIN * 1e-3;
            CLEAR_SCREEN();
            PRINTSCREEN2(floatHelp, 2);
            PRINTSCREEN(F("..."));
            floatHelp = LTC2947_2BytesToInt16(data) * LTC2947_LSB_VMAX * 1e-3;
            PRINTSCREEN2(floatHelp, 2);
            PRINTSCREEN('V');
            CheckSerialData();
            floatHelp = LTC2947_2BytesToInt16(data + 6) * LTC2947_LSB_TEMPMIN + LTC2947_OFFS_TEMPMIN;
            boolean limitTempCharacters = floatHelp <= -100.0;
            NEWLINE();
            PRINTSCREEN2(floatHelp, limitTempCharacters ? 0 : 1);
            PRINTSCREEN(F("..."));
            floatHelp = LTC2947_2BytesToInt16(data + 4) * LTC2947_LSB_TEMPMAX + LTC2947_OFFS_TEMPMAX;
            limitTempCharacters = floatHelp <= -100.0;
            PRINTSCREEN2(floatHelp, limitTempCharacters ? 0 : 1);
            WRITE_DEGREE_C();
          }
          break;
        default://TMIN TMAX / VDMIN VDMAX
          {
            float floatHelp;
            LTC2947_RD_BYTES(LTC2947_VAL_TEMPMAX, 8, data); // TEMPMAX[15:0] TEMPMIN[15:0] VDVCCMAX[15:0] VDVCCMIN[15:0] starting at data[0]
            CheckSerialData();
            floatHelp = LTC2947_2BytesToInt16(data + 2) * LTC2947_LSB_TEMPMIN + LTC2947_OFFS_TEMPMIN;
            boolean limitTempCharacters = floatHelp <= -100.0;
            CLEAR_SCREEN();
            PRINTSCREEN2(floatHelp, limitTempCharacters ? 0 : 1);
            PRINTSCREEN(F("..."));
            floatHelp = LTC2947_2BytesToInt16(data) * LTC2947_LSB_TEMPMAX + LTC2947_OFFS_TEMPMAX;
            limitTempCharacters = floatHelp <= -100.0;
            PRINTSCREEN2(floatHelp, limitTempCharacters ? 0 : 1);
            WRITE_DEGREE_C();
            CheckSerialData();
            floatHelp = LTC2947_2BytesToInt16(data + 6) * LTC2947_LSB_VDVCCMIN * 1e-3;
            NEWLINE();
            PRINTSCREEN2(floatHelp, 0);
            PRINTSCREEN(F("..."));
            floatHelp = LTC2947_2BytesToInt16(data + 4) * LTC2947_LSB_VDVCCMAX * 1e-3;
            PRINTSCREEN2(floatHelp, 0);
            PRINTSCREEN('V');
          }
          break;
      }
    }
    else
    {
      if (userGroup1Cycle)
        userSelGroup1 = (userSelGroup1 + 1) % USER_SEL_GRP1_CYCLE_COUNT;
      else
        statusRegsRead = false;// in case we don't cycle the status regs are read always
      if (!statusRegsRead)
      {
        // read all status and alert (if alerts are used it is important to read all in a single burst of 8 bytes!)
        LTC2947_RD_BYTES(LTC2947_REG_STATUS, 8, statusRegs);
        // all status & alert regs are cleared-on-read. To make sure we report all bits correctly we
        // only re-read them once we cycled through all of them (see last case STATVDVCC in which we clear
        // statusRegsRead again.
        statusRegsRead = true;
      }
      CLEAR_SCREEN();
      switch (userSelGroup1)
      {
        case 0:
          PRINTSCREEN(F("STAT:"));
          if (bitMaskSetChk(statusRegs[0], LTC2947_BM_STATUS_UPDATE))
            PRINTSCREEN(F("UPD,"));
          if (bitMaskSetChk(statusRegs[0], LTC2947_BM_STATUS_ADCERR))
            PRINTSCREEN(F("AER,"));
          if (bitMaskSetChk(statusRegs[0], LTC2947_BM_STATUS_TBERR))
            PRINTSCREEN(F("TER"));
          NEWLINE();
          if (bitMaskSetChk(statusRegs[0], LTC2947_BM_STATUS_UVLOA))
            PRINTSCREEN(F("UVA,"));
          if (bitMaskSetChk(statusRegs[0], LTC2947_BM_STATUS_PORA))
            PRINTSCREEN(F("POR,"));
          if (bitMaskSetChk(statusRegs[0], LTC2947_BM_STATUS_UVLOSTBY))
            PRINTSCREEN(F("UVS,"));
          if (bitMaskSetChk(statusRegs[0], LTC2947_BM_STATUS_UVLOD))
            PRINTSCREEN(F("UVD"));
          break;
        case 1:
          PRINTSCREEN(F("STATVT:"));
          if (bitMaskSetChk(statusRegs[LTC2947_REG_STATVT - LTC2947_REG_STATUS], LTC2947_BM_STATVT_VH))
            PRINTSCREEN(F("VH,"));
          if (bitMaskSetChk(statusRegs[LTC2947_REG_STATVT - LTC2947_REG_STATUS], LTC2947_BM_STATVT_VL))
            PRINTSCREEN(F("VL"));
          NEWLINE();
          if (bitMaskSetChk(statusRegs[LTC2947_REG_STATVT - LTC2947_REG_STATUS], LTC2947_BM_STATVT_TEMPH))
            PRINTSCREEN(F("TEH,"));
          if (bitMaskSetChk(statusRegs[LTC2947_REG_STATVT - LTC2947_REG_STATUS], LTC2947_BM_STATVT_TEMPL))
            PRINTSCREEN(F("TEL,"));
          if (bitMaskSetChk(statusRegs[LTC2947_REG_STATVT - LTC2947_REG_STATUS], LTC2947_BM_STATVT_FANH))
            PRINTSCREEN(F("FH,"));
          if (bitMaskSetChk(statusRegs[LTC2947_REG_STATVT - LTC2947_REG_STATUS], LTC2947_BM_STATVT_FANL))
            PRINTSCREEN(F("FL"));
          break;
        case 2:
          PRINTSCREEN(F("STATIP:"));
          NEWLINE();

          if (bitMaskSetChk(statusRegs[LTC2947_REG_STATIP - LTC2947_REG_STATUS], LTC2947_BM_STATIP_IH))
            PRINTSCREEN(F("IH,"));
          if (bitMaskSetChk(statusRegs[LTC2947_REG_STATIP - LTC2947_REG_STATUS], LTC2947_BM_STATIP_IL))
            PRINTSCREEN(F("IL,"));
          if (bitMaskSetChk(statusRegs[LTC2947_REG_STATIP - LTC2947_REG_STATUS], LTC2947_BM_STATIP_PH))
            PRINTSCREEN(F("PH,"));
          if (bitMaskSetChk(statusRegs[LTC2947_REG_STATIP - LTC2947_REG_STATUS], LTC2947_BM_STATIP_PL))
            PRINTSCREEN(F("PL"));
          break;
        case 3:
          PRINTSCREEN(F("STATC:"));
          NEWLINE();
          if (bitMaskSetChk(statusRegs[LTC2947_REG_STATC - LTC2947_REG_STATUS], LTC2947_BM_STATC_C1H))
            PRINTSCREEN(F("C1H,"));
          if (bitMaskSetChk(statusRegs[LTC2947_REG_STATC - LTC2947_REG_STATUS], LTC2947_BM_STATC_C1L))
            PRINTSCREEN(F("C1L,"));
          if (bitMaskSetChk(statusRegs[LTC2947_REG_STATC - LTC2947_REG_STATUS], LTC2947_BM_STATC_C2H))
            PRINTSCREEN(F("C2H,"));
          if (bitMaskSetChk(statusRegs[LTC2947_REG_STATC - LTC2947_REG_STATUS], LTC2947_BM_STATC_C2L))
            PRINTSCREEN(F("C2L"));
          break;
        case 4:
          PRINTSCREEN(F("STATE:"));
          NEWLINE();
          if (bitMaskSetChk(statusRegs[LTC2947_REG_STATE - LTC2947_REG_STATUS], LTC2947_BM_STATE_E1H))
            PRINTSCREEN(F("E1H,"));
          if (bitMaskSetChk(statusRegs[LTC2947_REG_STATE - LTC2947_REG_STATUS], LTC2947_BM_STATE_E1L))
            PRINTSCREEN(F("E1L,"));
          if (bitMaskSetChk(statusRegs[LTC2947_REG_STATE - LTC2947_REG_STATUS], LTC2947_BM_STATE_E2H))
            PRINTSCREEN(F("E2H,"));
          if (bitMaskSetChk(statusRegs[LTC2947_REG_STATE - LTC2947_REG_STATUS], LTC2947_BM_STATE_E2L))
            PRINTSCREEN(F("E2L"));
          break;
        case 5:
          PRINTSCREEN(F("STATCEOF:"));
          if (bitMaskSetChk(statusRegs[LTC2947_REG_STATCEOF - LTC2947_REG_STATUS], LTC2947_BM_STATCEOF_C1OF))
            PRINTSCREEN(F("C1OF"));
          NEWLINE();
          if (bitMaskSetChk(statusRegs[LTC2947_REG_STATCEOF - LTC2947_REG_STATUS], LTC2947_BM_STATCEOF_C2OF))
            PRINTSCREEN(F("C2OF,"));
          if (bitMaskSetChk(statusRegs[LTC2947_REG_STATCEOF - LTC2947_REG_STATUS], LTC2947_BM_STATCEOF_E1OF))
            PRINTSCREEN(F("E1OF,"));
          if (bitMaskSetChk(statusRegs[LTC2947_REG_STATCEOF - LTC2947_REG_STATUS], LTC2947_BM_STATCEOF_E2OF))
            PRINTSCREEN(F("E2OF"));
          break;
        case 6:
          PRINTSCREEN(F("STATTB:"));
          NEWLINE();
          if (bitMaskSetChk(statusRegs[LTC2947_REG_STATTB - LTC2947_REG_STATUS], LTC2947_BM_STATTB_TB1OF))
            PRINTSCREEN(F("1OF,"));
          if (bitMaskSetChk(statusRegs[LTC2947_REG_STATTB - LTC2947_REG_STATUS], LTC2947_BM_STATTB_TB2OF))
            PRINTSCREEN(F("2OF,"));
          if (bitMaskSetChk(statusRegs[LTC2947_REG_STATTB - LTC2947_REG_STATUS], LTC2947_BM_STATTB_TB1TH))
            PRINTSCREEN(F("1TH,"));
          if (bitMaskSetChk(statusRegs[LTC2947_REG_STATTB - LTC2947_REG_STATUS], LTC2947_BM_STATTB_TB2TH))
            PRINTSCREEN(F("2TH"));
          break;
        default:
          PRINTSCREEN(F("STATVDVCC:"));
          NEWLINE();
          if (bitMaskSetChk(statusRegs[LTC2947_REG_STATVDVCC - LTC2947_REG_STATUS], LTC2947_BM_STATVDVCC_VDVCCH))
            PRINTSCREEN(F("VDVCCH,"));
          if (bitMaskSetChk(statusRegs[LTC2947_REG_STATVDVCC - LTC2947_REG_STATUS], LTC2947_BM_STATVDVCC_VDVCCL))
            PRINTSCREEN(F("VDVCCL"));
          statusRegsRead = false;// this will trigger a re-read of all status register in the next cycle
          break;
      }
    }
  }
  // go back to page 1 if we initially had to switch to page 0
  // this is only neccessary for compatibility with the GUI
  if (lastPageSelect)
    LTC2947_SetPageSelect(true); // go back to page 1
}

/**  \fn  void loop()
 \brief main loop */
void loop()
{
  DelayAndProcessInputs(0);
}

/**  \fn  void DelayAndProcessInputs(unsigned long ms)
\brief Delay and process input commands either from serial port or from keypad
\param  ms  delay in milliseconds
*/
void DelayAndProcessInputs(unsigned long ms)
{
  unsigned long currentMillis = millis();
  ms += currentMillis; // calculate delay end time
  do
  {
    if (dc590Busy || CheckSerialData())
    {
      // DC590 is still busy or new data available from serial port
      char cmd = get_char();
      if (!tinyDC590B(cmd))
        processCommands(cmd); // no DC590B command so check for other commands
      else if (cmd == 'i' || cmd == 'I') // check if one of the id commands was received (quikeval and GUI will send this command)
        lcd->EnableSerialOutput = false; // disable any serial output
      return;
    }
    if (currentMillis < ms)
    {
      currentMillis = millis();
      continue;
    }
    if (currentMillis >= nextUpdateTime)
    {
      // make the periodic update of LTC2947 measurement results
      nextUpdateTime = currentMillis + SCREEN_UPDATE_TIME;// calc next update time
      if (!userInput())
        update(); // update data from LTC2947
    }
    break;
  }
  while (true);
}

/**  \fn  boolean userInput()
 \brief  processes the user input from LCD keypad shield
 \return  true if any key was pressed on the LDC keypad shield */
boolean userInput()
{
  if (!lcd->foundLCD)
    return false;
  int8_t key = lcd->button();// read pressed key
  if (key != KEYPAD_NONE)
  {
    CLEAR_SCREEN();
    // desable cyclic operation
    userGroup1Cycle = false;
    userGroup2Cycle = false;
    userGroupCycleTimer = 0;
  }
  switch (key)
  {
    case KEYPAD_NONE:
      break;
    case KEYPAD_LEFT:
    case KEYPAD_RIGHT:
      if (previousKey == KEYPAD_LEFT)
      {
        if (userSelGroup2 == 0)
          userSelGroup2 = USER_SEL_GRP2_COUNT - 1;
        else
          userSelGroup2--;
      }
      else if (previousKey == KEYPAD_RIGHT)
      {
        if (userSelGroup2 == USER_SEL_GRP2_COUNT - 1)
          userSelGroup2 = 0;
        else
          userSelGroup2++;
      }
      userGroupSelect = true;
      userGroupCycleTimer = 0;
      switch (userSelGroup2)
      {
        case 0:
          PRINTSCREEN(F("I V"));
          NEWLINE();
          PRINTSCREEN(F("P TEMP"));
          break;
        case 1:
          PRINTSCREEN(F("C1 VDVCC"));
          NEWLINE();
          PRINTSCREEN(F("E1 TB1"));
          break;
        case 2:
          PRINTSCREEN(F("C2 VDVCC"));
          NEWLINE();
          PRINTSCREEN(F("E2 TB2"));
          break;
        case 3:
          PRINTSCREEN(F("IMIN IMAX"));
          NEWLINE();
          PRINTSCREEN(F("PMIN PMAX"));
          break;
        case 4:
          PRINTSCREEN(F("VMIN VMAX"));
          NEWLINE();
          PRINTSCREEN(F("TMIN TMAX"));
          break;
        case 5:
          PRINTSCREEN(F("TMIN TMAX"));
          NEWLINE();
          PRINTSCREEN(F("VDMIN VDMAX"));
          break;
        default:
          PRINTSCREEN(F("CYCLE ALL"));
          NEWLINE();
          PRINTSCREEN(F("measurements"));
          userGroup2Cycle = true;
          break;
      }
      break;
    case KEYPAD_UP:
    case KEYPAD_DOWN:
      if (previousKey == KEYPAD_DOWN)
      {
        if (userSelGroup1 == 0)
          userSelGroup1 = USER_SEL_GRP1_COUNT - 1;
        else
          userSelGroup1--;
      }
      else if (previousKey == KEYPAD_UP)
      {
        if (userSelGroup1 == USER_SEL_GRP1_COUNT - 1)
          userSelGroup1 = 0;
        else
          userSelGroup1++;
      }
      userGroupSelect = false;
      userGroupCycleTimer = 0;
      PRINTSCREEN(F("STATUS,ALERTS:"));
      NEWLINE();
      switch (userSelGroup1)
      {
        case 0:
          PRINTSCREEN(F("STATUS"));
          break;
        case 1:
          PRINTSCREEN(F("STATVT"));
          break;
        case 2:
          PRINTSCREEN(F("STATIP"));
          break;
        case 3:
          PRINTSCREEN(F("STATC"));
          break;
        case 4:
          PRINTSCREEN(F("STATE"));
          break;
        case 5:
          PRINTSCREEN(F("STATCEOF"));
          break;
        case 6:
          PRINTSCREEN(F("STATTB"));
          break;
        case 7:
          PRINTSCREEN(F("STATVDVCC"));
          break;
        default:
          PRINTSCREEN(F("CYCLE ALL"));
          userGroup1Cycle = true;
          statusRegsRead = false; // this will make sure we read all STATUS regs once we enter the cycle the first time
          userSelGroup1 = USER_SEL_GRP1_CYCLE_COUNT - 1; // this will make sure we start with STATUS
          break;
      }
      break;
    case KEYPAD_SELECT:
      PRINTSCREEN(F("CLEAR"));
      flagClear = true;
      break;
  }
  previousKey = key;
  return (key != KEYPAD_NONE);
}

/** \fn  void processCommands()
\brief  process commands received via serial interface. Those are the m,l commands and the basic DC590B commands
used for DC590B compatibility mode that allows the GUI to communicate with this sketch.
(basic set of DC590 commands to allow I2C communication to the LTC2947)
\param  command  the command to be decoded */
void processCommands(char command)
{
  switch (command)
  {
    case 'm':
      {
        // show measurement menu
        Serial.println();
        Serial.print(F("0:"));
        Serial.println(MEAS_LABEL00);
        Serial.print(F("  "));
        Serial.println(MEAS_LABEL01);
        Serial.print(F("1:"));
        Serial.println(MEAS_LABEL10);
        Serial.print(F("  "));
        Serial.println(MEAS_LABEL11);
        Serial.print(F("2:"));
        Serial.println(MEAS_LABEL20);
        Serial.print(F("  "));
        Serial.println(MEAS_LABEL21);
        Serial.print(F("3:"));
        Serial.println(MEAS_LABEL30);
        Serial.print(F("  "));
        Serial.println(MEAS_LABEL31);
        Serial.print(F("4:"));
        Serial.println(MEAS_LABEL40);
        Serial.print(F("  "));
        Serial.println(MEAS_LABEL41);
        Serial.print(F("5:"));
        Serial.println(MEAS_LABEL50);
        Serial.print(F("  "));
        Serial.println(MEAS_LABEL51);
        Serial.print(F("6:"));
        Serial.println(MEAS_LABEL60);
        Serial.print(F("  "));
        Serial.println(MEAS_LABEL61);
        Serial.println(F("enter 0-6, any other to cancel"));
        char sel = get_char() - 0x30;
        userGroupCycleTimer = 0;
        switch (sel)
        {
          case 0:
          case 1:
          case 2:
          case 3:
          case 4:
          case 5:
            lcd->EnableSerialOutput = true;
            userGroup1Cycle = false;
            userGroup2Cycle = false;
            userSelGroup2 = sel;
            userGroupSelect = true;
            break;
          case 6:
            lcd->EnableSerialOutput = true;
            userGroup1Cycle = false;
            userGroup2Cycle = true;
            userGroupSelect = true;
            break;
          default:
            lcd->EnableSerialOutput = false;
            Serial.println(F("canceled"));
            break;
        }
      }
      break;
    case 'l':
      {
        // show status / alerts menu
        Serial.println();
        Serial.println(F("0:STATUS"));
        Serial.println(F("1:STATVT"));
        Serial.println(F("2:STATIP"));
        Serial.println(F("3:STATC"));
        Serial.println(F("4:STATE"));
        Serial.println(F("5:STATCEOF"));
        Serial.println(F("6:STATTB"));
        Serial.println(F("7:STATVDVCC"));
        Serial.println(F("8:CYCLE ALL"));
        Serial.println(F("enter 0-8, any other to cancel"));
        char sel = get_char() - 0x30;
        userGroupCycleTimer = 0;
        switch (sel)
        {
          case 0:
          case 1:
          case 2:
          case 3:
          case 4:
          case 5:
          case 6:
          case 7:
            lcd->EnableSerialOutput = true;
            userGroup1Cycle = false;
            userGroup2Cycle = false;
            userSelGroup1 = sel;
            userGroupSelect = false;
            break;
          case 8:
            lcd->EnableSerialOutput = true;
            userGroup2Cycle = false;
            userGroupSelect = false;
            userGroup1Cycle = true;
            statusRegsRead = false; // this will make sure we read all STATUS regs once we enter the cycle the first time
            userSelGroup1 = USER_SEL_GRP1_CYCLE_COUNT - 1; // this will make sure we start with STATUS
            break;
          default:
            lcd->EnableSerialOutput = false;
            Serial.println(F("canceled"));
            break;
        }
      }
      break;
  }
}


Download LTC2947 Linduino CPP File

/*!
LTC2947: LTC2947 a high-precision power and energy monitor with an internal sense resistor supporting up to 30A

@verbatim

The LTC2947 is a high-precision power and energy
monitor with an internal sense resistor supporting up
to 30A. Three internal No Latency delta sigma ADCs ensure
accurate measurement of voltage and current, while high-
bandwidth analog multiplication of voltage and current
provides accurate power measurement in a wide range of
applications. Internal or external clocking options enable
precise charge and energy measurements.
An internal 300 micro ohms, temperature-compensated sense
resistor minimizes efficiency loss and external compo-
nents, simplifying energy measurement applications while
enabling high accuracy current measurement over the full
temperature range. For more details see following URLs:

@endverbatim

http://www.linear.com/product/LTC2947

http://www.linear.com/product/LTC2947#demoboards

REVISION HISTORY
$Revision: 5903 $
$Date: 2016-10-07 15:15:24 -0700 (Fri, 07 Oct 2016) $

Copyright (c) 2016, Linear Technology Corp.(LTC)
All rights reserved.

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.

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 OWNER 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.

The views and conclusions contained in the software and documentation are those
of the authors and should not be interpreted as representing official policies,
either expressed or implied, of Linear Technology Corp.

The Linear Technology Linduino is not affiliated with the official Arduino team.
However, the Linduino is only possible because of the Arduino team's commitment
to the open-source community.  Please, visit http://www.arduino.cc and
http://store.arduino.cc , and consider a purchase that will help fund their
ongoing work.
*/

//! @defgroup LTC2947 LTC2947 a high-precision power and energy monitor with an internal sense resistor supporting up to 30A.
/*! @file
  @ingroup LTC2947
  Library for LTC2947: A high-precision power and energy monitor with an internal sense resistor supporting up to 30A.
*/

#include <Arduino.h>
#include <Linduino.h>
#include <LT_I2C.h>
#include <LT_SPI.h>
#include "LTC2947.h"
#include <SPI.h>

#ifdef LTC2947_DEBUG
#include "UserInterface.h"
#endif

boolean LTC2947_SPI_Mode_Enabled = false;
uint8_t LTC2947_I2C_Slave_Addr = LTC2947_I2C_ADDR_LL;

void LTC2947_InitI2C(uint8_t slvAddr)
{
  LTC2947_SPI_Mode_Enabled = false;
  LTC2947_I2C_Slave_Addr = slvAddr;
}

void LTC2947_InitSPI()
{
  LTC2947_SPI_Mode_Enabled = true;
}

boolean LTC2947_Abs(uint8_t *bytes, uint8_t length)
{
  if (bitMaskClrChk(*bytes, 0x80))
    return false;// value is already positive

  // two's complement is generated by inverting all bits and add 1

  length--;
  bytes += length; // seek to LSB
  uint16_t cHelp = (~(*bytes)) & 0xFF; // invert LSB
  cHelp++; // add 1
  *bytes = cHelp; // store back to buffer

  while (length != 0)
  {
    // seek next byte (towards MSB)
    length--;
    bytes--;
    cHelp = cHelp >> 8; // restore carry from previous sum
    cHelp += (~(*bytes)) & 0xFF; // add inverted byte
    *bytes = cHelp; // store back
  }
  return true;// value inverted
}

double LTC2947_BytesToDouble(uint8_t *bytes, uint8_t length, boolean sig, double lsb)
{
  if (length == 0)
    return 0.0;
  else if (length == 1)
    return sig ? (int8_t)(bytes[0])*lsb : bytes[0] * lsb;
  else if (length == 2)
    return sig ? LTC2947_2BytesToInt16(bytes)*lsb : LTC2947_2BytesToUInt16(bytes)*lsb;
  else if (length == 3)
    return sig ? LTC2947_3BytesToInt32(bytes)*lsb : LTC2947_3BytesToUInt32(bytes)*lsb;
  else if (length == 4)
    return sig ? LTC2947_4BytesToInt32(bytes)*lsb : LTC2947_4BytesToUInt32(bytes)*lsb;
  else
    return sig ? LTC2947_SignedBytesToDouble(bytes, length, lsb) : LTC2947_UnsignedBytesToDouble(bytes, length, lsb);
}

double LTC2947_UnsignedBytesToDouble(uint8_t *unsignedBytes, uint8_t length, double lsb)
{
  // NOTE: On Arduino double is 32-bit and NOT 64-bit, thus the returned value
  // will not reflect the full precission of e.g. C1, C2... which are 48-bit values
  double ret = (*unsignedBytes); // MSB!

  while (length > 1)
  {
    unsignedBytes++; // go to next byte
    length--;
    ret = ret * 256.0 + (*unsignedBytes);
  }
  return ret*lsb;
}

void LTC2947_SerialPrint8hex(uint8_t val)
{
  if (val < 0x10) Serial.print("0");
  Serial.print(val, HEX);
}
void LTC2947_SerialPrint16hex(uint16_t val)
{
  for (uint16_t i = 0x1000L; i >= 0x10L; i = i >> 4)
    if (val < i) Serial.print("0");
  Serial.print(val, HEX);
}
void LTC2947_SerialPrint32hex(uint32_t val)
{
  for (uint32_t i = 0x10000000L; i >= 0x10L; i = i >> 4)
    if (val < i) Serial.print("0");
  Serial.print(val, HEX);
}
void LTC2947_SerialPrint64hex(uint64_t uint64Val)
{
  LTC2947_SerialPrint32hex((uint32_t)(uint64Val >> 32));
  LTC2947_SerialPrint32hex((uint32_t)(uint64Val));
}

void LTC2947_DoubleToBytes(double value, double lsb, uint8_t *bytes, uint8_t length)
{
  //! on Arduino Uno / Linduino the maximum is 8 bytes
  if (length > sizeof(int64_t)) //! sizeof(int64_t) = 8
    return;

  //! revert the scaling with LSB and convert to integer value
  int64_t int64Val = int64_t(value / lsb);

#ifdef LTC2947_DEBUG
  Serial.print(F("int64Val=0x"));
  LTC2947_SerialPrint64hex((uint64_t)(int64Val));
  Serial.println();
  Serial.println(F("bytes:"));
#endif

  //! convert the integer value to byte array
  for (int8_t i = length - 1; i >= 0; i--)
  {
    bytes[i] = int64Val & 0xFF;
    int64Val >>= 8;
#ifdef LTC2947_DEBUG
    Serial.print(i);
    Serial.print(F(":"));
    LTC2947_SerialPrint8hex(bytes[i]);
    if (i == 0)
      Serial.print(F(" (MSB)"));
    else if (i == length - 1)
      Serial.print(F(" (LSB)"));
    Serial.println();
#endif
  }
}

#ifdef LTC2947_DEBUG
//! conversion function test
void LTC2947_DoubleToBytes_Test()
{
  Serial.print(F("LSB:"));
  while (!Serial.available());
  double lsb = read_float();
  Serial.println(lsb, 8);

  Serial.print(F("LSBrshift:"));
  while (!Serial.available());
  int32_t shift = read_int();
  Serial.println(shift);
  while (shift-- > 0)
    lsb = lsb * 0.5;

  Serial.println(lsb, 16);

  Serial.print(F("VAL:"));
  while (!Serial.available());
  double val = read_float();
  Serial.println(val, 8);

  Serial.print(F("VALlshift:"));
  while (!Serial.available());
  shift = read_int();
  Serial.println(shift);
  while (shift-- > 0)
    val = val * 2.0;

  Serial.println(val, 16);

  byte bytes[8];
  LTC2947_DoubleToBytes(val, lsb, bytes, 8);
}
#endif


double LTC2947_SignedBytesToDouble(uint8_t *signedBytes, uint8_t length, double lsb)
{
  // reserve memory for unsigned bytes
  uint8_t *unsignedBytes = (uint8_t *)malloc(length);
  // copy signed bytes to unsigned bytes
  memcpy(unsignedBytes, signedBytes, length);
  // calculate absolute value of the signed bytes and store sign
  // this function will change the unsigned bytes, for this reason
  // we copied the original unsigned bytes to a new array
  boolean sign = LTC2947_Abs(unsignedBytes, length);
  // convert the unsigned bytes to a double value
  double absDouble = LTC2947_UnsignedBytesToDouble(unsignedBytes, length, lsb);
  // free the allocated memory of the copied array
  free(unsignedBytes);
  // recover the previously stored sign to return a signed value
  return sign ? -absDouble : absDouble;
}

int32_t LTC2947_4BytesToInt32(byte *bytes)
{
  int32_t ret;

  ret = *bytes;
  bytes++;
  ret = ret << 8;
  ret |= *bytes;
  bytes++;
  ret = ret << 8;
  ret |= *bytes;
  bytes++;
  ret = ret << 8;
  ret |= *bytes;
  return ret;
}

int32_t LTC2947_3BytesToInt32(byte *bytes)
{
  int32_t ret;
  // sign extension
  if (*bytes & 0x80)
    ret = 0xFF00;
  else
    ret = 0;

  ret |= *bytes;
  bytes++;
  ret = ret << 8;
  ret |= *bytes;
  bytes++;
  ret = ret << 8;
  ret |= *bytes;
  return ret;
}

int16_t LTC2947_2BytesToInt16(byte *bytes)
{
  int16_t ret;
  ret = *bytes;
  bytes++;
  ret = ret << 8;
  ret |= *bytes;
  return ret;
}

uint32_t LTC2947_4BytesToUInt32(byte *bytes)
{
  uint32_t ret;

  ret = *bytes;
  bytes++;
  ret = ret << 8;
  ret |= *bytes;
  bytes++;
  ret = ret << 8;
  ret |= *bytes;
  bytes++;
  ret = ret << 8;
  ret |= *bytes;
  return ret;
}

uint32_t LTC2947_3BytesToUInt32(byte *bytes)
{
  uint32_t ret;

  ret = *bytes;
  bytes++;
  ret = ret << 8;
  ret |= *bytes;
  bytes++;
  ret = ret << 8;
  ret |= *bytes;
  return ret;
}

uint16_t LTC2947_2BytesToUInt16(byte *bytes)
{
  uint16_t ret;
  ret = *bytes;
  bytes++;
  ret = ret << 8;
  ret |= *bytes;
  return ret;
}

/*
* Note on I2C/SPI functions:
* i2c_write / i2c_read / spi_write / spi_read block methods from LT_I2C / LT_SPI write / read byte arrays from last to first element
*  which is not compatible with LTC2947 library that expects the opposite order
*/

int8_t LTC2947_SpiWrBlock(uint8_t address, uint8_t length, uint8_t *values)
{
  int8_t i;

  output_low(LTC2947_CS);                 //! 1) Pull CS low

  SPI.transfer(LTC2947_SPI_WRITE_CMD); // write
  SPI.transfer(address); // reg addr

  for (i = 0; i < length; i++)
    SPI.transfer(values[i]);    //! 2) send byte array

  output_high(LTC2947_CS);                //! 3) Pull CS high
  return 0;
}

int8_t LTC2947_SpiRdBlock(uint8_t address, uint8_t length, uint8_t *values)
{
  int8_t i;

  output_low(LTC2947_CS);                 //! 1) Pull CS low

  SPI.transfer(LTC2947_SPI_READ_CMD); // read
  SPI.transfer(address); // reg addr

  for (i = 0; i < length; i++)
    values[i] = SPI.transfer(0x00);

  output_high(LTC2947_CS);                //! 3) Pull CS high
  return 0;
}

int8_t LTC2947_SpiWrByte(uint8_t address, uint8_t value)
{
  output_low(LTC2947_CS);                 //! 1) Pull CS low

  SPI.transfer(LTC2947_SPI_WRITE_CMD); // write
  SPI.transfer(address); // reg addr
  SPI.transfer(value);    //! 2) send byte

  output_high(LTC2947_CS);                //! 3) Pull CS high
  return 0;
}

int8_t LTC2947_SpiRdByte(uint8_t address, uint8_t *value)
{
  output_low(LTC2947_CS);                 //! 1) Pull CS low

  SPI.transfer(LTC2947_SPI_READ_CMD); // read
  SPI.transfer(address); // reg addr
  value[0] = SPI.transfer(0x00);    //! 2) read byte

  output_high(LTC2947_CS);                //! 3) Pull CS high
  return 0;
}

int8_t LTC2947_I2CWrBlock(uint8_t slvAddr, uint8_t regAddr, uint8_t length, uint8_t *values)
{
  int8_t ret = 0;

  if (i2c_start() != 0) //I2C START
    return 1;         //Stop and return 0 if START fail

  ret |= i2c_write((slvAddr << 1) | I2C_WRITE_BIT); // Write 7 bit address with W bit
  ret |= i2c_write(regAddr);                        // Set register address

  while (length > 0)
  {
    ret |= i2c_write(*values);     //Write Value
    length--;
    values++;
  }

  i2c_stop();                        // I2C STOP

  return ret != 0 ? 1 : 0;
}

int8_t LTC2947_I2CRdBlock(uint8_t slvAddr, uint8_t regAddr, uint8_t length, uint8_t *values)
{
  int8_t ret = 0;

  if (length == 0 || i2c_start() != 0) //I2C START
    return 1; //Stop and return 0 if START fail

  ret |= i2c_write((slvAddr << 1) | I2C_WRITE_BIT); // Write 7 bit address with W bit
  ret |= i2c_write(regAddr);                        // Set register address
  ret |= i2c_repeated_start();
  ret |= i2c_write((slvAddr << 1) | I2C_READ_BIT); // Write 7 bit address with R bit

  if (ret != 0)   //If NACK return 1
  {
    i2c_stop();                         //I2C STOP
    return 1;
  }

  length--;
  while (length > 0)
  {
    *values = i2c_read(WITH_ACK); //Read from bus with ACK
    values++;
    length--;
  }

  *values = i2c_read(WITH_NACK); //Read from bus with NACK for the last one;

  i2c_stop(); //I2C STOP

  return 0; // Success!
}

int8_t LTC2947_I2CWrByte(uint8_t slvAddr, uint8_t regAddr, uint8_t value)
{
  int8_t ret = 0;

  if (i2c_start() != 0) //I2C START
    return 1;        //Stop and return 0 if START fail

  ret |= i2c_write((slvAddr << 1) | I2C_WRITE_BIT); // Write 7 bit address with W bit
  ret |= i2c_write(regAddr);                        // Set register address
  ret |= i2c_write(value);     //Write Value

  i2c_stop();                  // I2C STOP

  return ret != 0 ? 1 : 0;
}

int8_t LTC2947_I2CRdByte(uint8_t slvAddr, uint8_t regAddr, uint8_t *value)
{
  int8_t ret = 0;

  if (i2c_start() != 0) //I2C START
    return 1; //Stop and return 0 if START fail

  ret |= i2c_write((slvAddr << 1) | I2C_WRITE_BIT); // Write 7 bit address with W bit
  ret |= i2c_write(regAddr);                        // Set register address
  ret |= i2c_repeated_start();
  ret |= i2c_write((slvAddr << 1) | I2C_READ_BIT); // Write 7 bit address with R bit

  if (ret != 0)   //If NACK return 1
  {
    i2c_stop();                         //I2C STOP
    return 1;
  }

  *value = i2c_read(WITH_NACK); //Read from bus with NACK for the last one;

  i2c_stop(); //I2C STOP

  return 0; // Success!
}

void LTC2947_GPIO_PinMode(uint8_t mode)
{
  uint8_t gpiostatcl;
  LTC2947_RD_BYTE(LTC2947_REG_GPIOSTATCL, &gpiostatcl);
  bitMaskSetClr(gpiostatcl, LTC2947_BM_GPIOSTATCL_GPOEN, mode != INPUT);
  LTC2947_WR_BYTE(LTC2947_REG_GPIOSTATCL, gpiostatcl);
}

void LTC2947_GPIO_SetPinState(uint8_t val)
{
  uint8_t gpiostatcl;
  LTC2947_RD_BYTE(LTC2947_REG_GPIOSTATCL, &gpiostatcl);
  bitMaskSetClr(gpiostatcl, LTC2947_BM_GPIOSTATCL_GPO, val != LOW);
  LTC2947_WR_BYTE(LTC2947_REG_GPIOSTATCL, gpiostatcl);
}

boolean LTC2947_GPIO_Read()
{
  uint8_t gpiostatcl;
  LTC2947_RD_BYTE(LTC2947_REG_GPIOSTATCL, &gpiostatcl);
  return bitMaskSetChk(gpiostatcl, LTC2947_BM_GPIOSTATCL_GPI);
}

uint8_t LTC2947_Ara(uint8_t *svlAddr)
{
  *svlAddr = 0;
  //! Send I2C start bit
  if (i2c_start() != 0)
    return LTC2947_ARA_ERROR;

  //! send the ALERT RESPONSE ADDRESS with read bit
  if (i2c_write((LTC2947_ALERT_RESP_ADDR << 1) | I2C_READ_BIT))
  {
    //! NACK means no device response!
    return LTC2947_ARA_NO_RESPONSE;
  }

  //! Read device address from the responding device
  *svlAddr = i2c_read(WITH_NACK); //! read with NACK
  i2c_stop(); //! I2C STOP
  //! check for the expected write bit of the response
  boolean response_wr_bit = bitMaskClrChk(*svlAddr, 0x1);
  //! right shift to get 7-bit slave address
  *svlAddr = ((*svlAddr) >> 1) & 0x7F;
  if (response_wr_bit)
  {
    //! got expected write bit, compare slave address
    //! with LTC2947's address
    return ((*svlAddr) == LTC2947_I2C_Slave_Addr)
           ? LTC2947_ARA_LTC2947_RESPONSE
           : LTC2947_ARA_OTHER_RESPONSE;
  }

  //! missing write bit within response!
  return LTC2947_ARA_RESPONSE_WO_WR;
}

int16_t LTC2947_wake_up()
{
  byte data[1];
  unsigned long wakeupStart = millis(), wakeupTime;
  LTC2947_WR_BYTE(LTC2947_REG_OPCTL, 0);//! any serial transaction will wakeup LTC2947
  do
  {
    delay(1);
    LTC2947_RD_BYTE(LTC2947_REG_OPCTL, data); //! wake up polling by reading OPCTL
    wakeupTime = millis() - wakeupStart;
    if (data[0] == 0) //! check if we are in idle mode
    {
      //! wake up successful, return wakeup time in milliseconds
      return wakeupTime;
    }
    if (wakeupTime > 200)
    {
      //! failed to wake up due to timeout, return -1
      return -1;
    }
  }
  while (true);
}

boolean LTC2947_GetCurrentPageSelect()
{
  uint8_t currentPageCtrl;
  LTC2947_RD_BYTE(LTC2947_REG_PGCTL, &currentPageCtrl);
  return bitMaskSetChk(currentPageCtrl, LTC2947_BM_PGCTL_PAGE);
}

void LTC2947_SetPageSelect(boolean page)
{
  LTC2947_WR_BYTE(LTC2947_REG_PGCTL, page ? LTC2947_BM_PGCTL_PAGE : 0); // switch page
}

void LTC2947_Read_I_P_V_TEMP_VCC(float *I, float *P, float *V, float *TEMP, float *VCC)
{
  // byte array to store register values
  byte bytes[12];

  // read measurement results from device
  LTC2947_RD_BYTES(LTC2947_VAL_I, 6, bytes);      // I[23:0] P[23:0] starting at data[0]
  LTC2947_RD_BYTES(LTC2947_VAL_V, 6, bytes + 6);  // V[15:0] TEMP[15:0] VDVCC[15:0] starting at data[6]

  // convert to floating point values
  // Note: definitions in LTC2947.h are given in mA, mW, mV
  // so they have to be mutliplied by 1e-3 to get A, W, V respectively
  *I = LTC2947_3BytesToInt32(bytes) * LTC2947_LSB_I * 1e-3;                            // calc current in amps
  *P = LTC2947_3BytesToInt32(bytes + 3) * LTC2947_LSB_P * 1e-3;                        // calc power in watts
  *V = LTC2947_2BytesToInt16(bytes + 6) * LTC2947_LSB_V * 1e-3;                        // calc voltage in volts
  *TEMP = LTC2947_2BytesToInt16(bytes + 6 + 2) * LTC2947_LSB_TEMP + LTC2947_OFFS_TEMP; // calc temperature in degree celcius
  *VCC = LTC2947_2BytesToInt16(bytes + 6 + 4) * LTC2947_LSB_VDVCC * 1e-3;              // calc supply voltage in volts
}

void LTC2947_Read_Abs_C_E_TB(boolean accuSet1, double *C, boolean *signC, double *E, boolean *signE, double *TB)
{
  // byte array to store register values
  byte bytes[16];

  // read measurement results from device
  if (accuSet1)
    // read accumulated quantities set 1: C1[47:0] E1[47:0] TB1[31:0]
    LTC2947_RD_BYTES(LTC2947_VAL_C1, 16, bytes);
  else
    // read accumulated quantities set 2: C2[47:0] E2[47:0] TB2[31:0]
    LTC2947_RD_BYTES(LTC2947_VAL_C2, 16, bytes);

  // calculate absolute value of Cx and store sign
  *signC = LTC2947_Abs(bytes, 6);
  // convert unsigned bytes to double value in As
  *C = LTC2947_UnsignedBytesToDouble(bytes, 6, LTC2947_LSB_C1);

  // calculate absolute value of Ex and store sign
  *signE = LTC2947_Abs(bytes + 6, 6);
  // convert unsigned bytes to double value in Ws
  *E = LTC2947_UnsignedBytesToDouble(bytes + 6, 6, LTC2947_LSB_E1);

  // calc time in seconds
  *TB = LTC2947_4BytesToUInt32(bytes + 12) * LTC2947_LSB_TB1;
}

void LTC2947_Read_C_E_TB(boolean accuSet1, double *C, double *E, double *TB)
{
  // byte array to store register values
  byte bytes[16];

  // read measurement results from device
  if (accuSet1)
    // read accumulated quantities set 1: C1[47:0] E1[47:0] TB1[31:0]
    LTC2947_RD_BYTES(LTC2947_VAL_C1, 16, bytes);
  else
    // read accumulated quantities set 2: C2[47:0] E2[47:0] TB2[31:0]
    LTC2947_RD_BYTES(LTC2947_VAL_C2, 16, bytes);

  // convert signed bytes to double value in As
  *C = LTC2947_SignedBytesToDouble(bytes, 6, LTC2947_LSB_C1);

  // convert signed bytes to double value in Ws
  *E = LTC2947_SignedBytesToDouble(bytes + 6, 6, LTC2947_LSB_E1);

  // calc time in seconds
  *TB = LTC2947_4BytesToUInt32(bytes + 6 + 6) * LTC2947_LSB_TB1;
}

Download LTC2947 Linduino Header File

/*!
LTC2947: LTC2947 a high-precision power and energy monitor with an internal sense resistor supporting up to 30A.

@verbatim

The LTC2947 is a high-precision power and energy
monitor with an internal sense resistor supporting up
to 30A. Three internal No Latency delta sigma ADCs ensure
accurate measurement of voltage and current, while high-
bandwidth analog multiplication of voltage and current
provides accurate power measurement in a wide range of
applications. Internal or external clocking options enable
precise charge and energy measurements.
An internal 300 micro ohms, temperature-compensated sense
resistor minimizes efficiency loss and external compo-
nents, simplifying energy measurement applications while
enabling high accuracy current measurement over the full
temperature range. For more details see following URLs:

@endverbatim


http://www.linear.com/product/LTC2947

http://www.linear.com/product/LTC2947#demoboards

REVISION HISTORY
$Revision: 5903 $
$Date: 2016-10-07 15:15:24 -0700 (Fri, 07 Oct 2016) $

Copyright (c) 2013, Linear Technology Corp.(LTC)
All rights reserved.

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.

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 OWNER 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.

The views and conclusions contained in the software and documentation are those
of the authors and should not be interpreted as representing official policies,
either expressed or implied, of Linear Technology Corp.

The Linear Technology Linduino is not affiliated with the official Arduino team.
However, the Linduino is only possible because of the Arduino team's commitment
to the open-source community.  Please, visit http://www.arduino.cc and
http://store.arduino.cc , and consider a purchase that will help fund their
ongoing work.
*/


/*! @file
  @ingroup LTC2947
  Header for LTC2947 a high-precision power and energy monitor with an internal sense resistor supporting up to 30A.
*/

#ifndef LTC2947_H
#define LTC2947_H

#undef LTC2947_DEBUG

#include "arduino.h"

#define bitMaskSetChk(value, bitMask) (((value) & (bitMask)) == (bitMask))
#define bitMaskClrChk(value, bitMask) (((value) & (bitMask)) == 0)

#define bitMaskSet(value, bitMask) (value |=  (bitMask))
#define bitMaskClr(value, bitMask) (value &= ~(bitMask))

#define bitMaskSetClr(value, bitMask, setNotClr) ((setNotClr) ? bitMaskSet(value,bitMask) : bitMaskClr(value,bitMask))

#define LOG_1(n) (((n) >= 2) ? 1 : 0)
#define LOG_2(n) (((n) >= 1<<2) ? (2 + LOG_1((n)>>2)) : LOG_1(n))
#define LOG_4(n) (((n) >= 1<<4) ? (4 + LOG_2((n)>>4)) : LOG_2(n))
#define LOG_8(n) (((n) >= 1<<8) ? (8 + LOG_4((n)>>8)) : LOG_4(n))
#define LOG(n)   (((n) >= 1<<16) ? (16 + LOG_8((n)>>16)) : LOG_8(n))


// used to convert any bitmask like LTC2947_BM_TBCTL_DIV_0
// to its bit position index to be used for shift operations
// BM2BITPOS(LTC2947_BM_TBCTL_DIV_0) = BM2BITPOS(0x8) = BM2BITPOS(1<<3) = 3
#define BM2BITPOS(bm) ((bm)&1<<7 ? 7 : \
             (bm)&1<<6 ? 6 : \
             (bm)&1<<5 ? 5 : \
             (bm)&1<<4 ? 4 : \
             (bm)&1<<3 ? 3 : \
             (bm)&1<<2 ? 2 : \
             (bm)&1<<1 ? 1 : 0)


#define LTC2947_I2C_ADDR_LL  0x5C  // AD0=L, AD1=L (default)
#define LTC2947_I2C_ADDR_LH  0x5D  // AD0=L, AD1=H
#define LTC2947_I2C_ADDR_LR  0x5E  // AD0=L, AD1=R
#define LTC2947_I2C_ADDR_RL  0x64  // AD0=R, AD1=L
#define LTC2947_I2C_ADDR_RH  0x65  // AD0=R, AD1=H
#define LTC2947_I2C_ADDR_RR  0x66  // AD0=R, AD1=R


#define LTC2947_SPI_READ_CMD  0x01
#define LTC2947_SPI_WRITE_CMD 0x00

#define LTC2947_LSB_FACTOR_MILLI_HOURS (1.0 / 60.0 / 60.0 / 1.0e-3)
#define LTC2947_LSB_FACTOR_HOURS       (1.0 / 60.0 / 60.0)

#ifndef LTC2947_CS
#define LTC2947_CS QUIKEVAL_CS
#endif

/**
\brief  defines ltc 2947 extclk.
PRE, DIV, ext. oscillator setting:
Before include of LTC2947.h you may
1. define LTC2947_EXTCLK only to specify your external oscillator / crystal
frequency

2. not define anything. In this case the internal oscillator will be used (PRE = 7, DIV = 0)
#undef  LTC2947_EXTCLK // internal oscillator, so no external clock frequency
#undef  LTC2947_PRE    // will be defined for internal oscillator by LTC2947.h
#undef  LTC2947_DIV    // will be defined for internal oscillator by LTC2947.h

3. define full set of PRE, DIV and EXTCLK
#define LTC2947_PRE 2     // LTC2947.h will use those definitions to calc LSB calues
#define LTC2947_DIV 30       // LTC2947.h will use those definitions to calc LSB calues
#define LTC2947_EXTCLK 4e6  // LTC2947.h will use those definitions to calc LSB calues

In all cases after loading the header file you can
1. Use LTC2947_PRE, LTC2947_DIV to write to LTC2947_REG_TBCTL see below.
2. Use LSB definitions like LTC2947_LSB_TB1 to calculate real values out of integers

Note: All LSBs are calculated as a function of PRE, DIV and EXTCLK by
pre-processor macros within LTC2947.h

\param  must be 4MHz in case of crystal  operation,
or 0.2 to 25 MHz in case of external oscillator
*/
#define LTC2947_EXTCLK 4e6 // external frequency on CLKI pin (must be 4MHz in case of crystal)
#undef  LTC2947_PRE   // will be calculated and defined by LTC2947.h
#undef  LTC2947_DIV   // will be calculated and defined by LTC2947.h


/** @name LTC2947 auto generated code
*  This code is automatically generated by the LTC2947's GUI, see Tools menu of the GUI
*  @{
*/

/////////////////////////////////////////////////////////
// Definitions of available registers without those    //
// that are part of multiple byte values               //
// (e.g. accumulators).                                //
//                                                     //
// Note: The address constants are 9 bit values where  //
// the lower 8 bit define the address and the 9th bit  //
// defines the page!                                   //
/////////////////////////////////////////////////////////

// GPIO status and control (register on page 0)
#define LTC2947_REG_GPIOSTATCL 0x067

// Status register (active high) (register on page 0)
#define LTC2947_REG_STATUS 0x080

// Voltage, Temperature Threshold Alerts (register on page 0)
#define LTC2947_REG_STATVT 0x081

// Current Power Threshold Alerts (register on page 0)
#define LTC2947_REG_STATIP 0x082

// Charge Threshold Alerts (register on page 0)
#define LTC2947_REG_STATC 0x083

// Energy Threshold Alerts (register on page 0)
#define LTC2947_REG_STATE 0x084

// Charge, Energy Overflow Alerts (register on page 0)
#define LTC2947_REG_STATCEOF 0x085

// Time Base Alerts (register on page 0)
#define LTC2947_REG_STATTB 0x086

// DVCC threshold alerts (register on page 0)
#define LTC2947_REG_STATVDVCC 0x087

// STATUS mask register (1: alert from STATUS is disabled) (register on page 0)
#define LTC2947_REG_STATUSM 0x088

// STATVT mask register (1: alert from STATVT is disabled) (register on page 0)
#define LTC2947_REG_STATVTM 0x089

// STATIP mask register (1: alert from STATIP is disabled) (register on page 0)
#define LTC2947_REG_STATIPM 0x08A

// STATC mask register (1: alert from STATC is disabled) (register on page 0)
#define LTC2947_REG_STATCM 0x08B

// STATE mask register (1: alert from STATE is disabled) (register on page 0)
#define LTC2947_REG_STATEM 0x08C

// STATCEOF mask register (1: alert from STATCEOF is disabled) (register on page 0)
#define LTC2947_REG_STATCEOFM 0x08D

// STATTB mask register (1: alert from STATTB is disabled) (register on page 0)
#define LTC2947_REG_STATTBM 0x08E

// STATVDVCC mask register (1: alert from STATVDVCC is disabled) (register on page 0)
#define LTC2947_REG_STATVDVCCM 0x08F

// Accumulation control of Charge1/2 and Energy1/2 by current polarity.
// 00: Accumulation takes place always,
// 01: only if the current is positive,
// 10: only if the current is negative,
// 11: No accumulation takes place. (register on page 0)
#define LTC2947_REG_ACCICTL 0x0E1

// Accumulation control of Charge1/2, Energy1/2 and TB1/2 by pin GPIO.
// 00: Accumulation takes place always,
// 01: only if pin GPIO is 1,
// 10: only if pin GPIO is 0,
// 11: RESERVED (register on page 0)
#define LTC2947_REG_ACCGPCTL 0x0E3

// Deadband current for accumulation
// If the absolute current value is higher than or equal this value, accumulation of Charge1/Charge2 and Energy1/Energy2 and comparison to their respective threshold takes place. If lower, Charge1/Charge2 and Energy1/Energy2 values are not accumulated and there is no comparison against thresholds. (register on page 0)
#define LTC2947_REG_ACCIDB 0x0E4

// Alert Master Control Enable (register on page 0)
#define LTC2947_REG_ALERTBCTL 0x0E8

// Time Base Control (register on page 0)
#define LTC2947_REG_TBCTL 0x0E9

// Operation Control (register on page 0)
#define LTC2947_REG_OPCTL 0x0F0

// Page Control Register (register on page 0)
#define LTC2947_REG_PGCTL 0x0FF

/////////////////////////////////////////////////////////
// Definitions of all byte addresses of all multiple   //
// byte values (e.g. accumulators).                    //
//                                                     //
// Note: The address constants are 9 bit values where  //
// the lower 8 bit define the address and the 9th bit  //
// defines the page!                                   //
/////////////////////////////////////////////////////////

// Charge1 (register on page 0)
#define LTC2947_REG_C1_47_40 0x000

// (register on page 0)
#define LTC2947_REG_C1_39_32 0x001

// (register on page 0)
#define LTC2947_REG_C1_31_24 0x002

// (register on page 0)
#define LTC2947_REG_C1_23_16 0x003

// (register on page 0)
#define LTC2947_REG_C1_15_8 0x004

// (register on page 0)
#define LTC2947_REG_C1_7_0 0x005

// Energy1 (register on page 0)
#define LTC2947_REG_E1_47_40 0x006

// (register on page 0)
#define LTC2947_REG_E1_39_32 0x007

// (register on page 0)
#define LTC2947_REG_E1_31_24 0x008

// (register on page 0)
#define LTC2947_REG_E1_23_16 0x009

// (register on page 0)
#define LTC2947_REG_E1_15_8 0x00A

// (register on page 0)
#define LTC2947_REG_E1_7_0 0x00B

// Time1 (register on page 0)
#define LTC2947_REG_TB1_31_24 0x00C

// (register on page 0)
#define LTC2947_REG_TB1_23_16 0x00D

// (register on page 0)
#define LTC2947_REG_TB1_15_8 0x00E

// (register on page 0)
#define LTC2947_REG_TB1_7_0 0x00F

// Charge2 (register on page 0)
#define LTC2947_REG_C2_47_40 0x010

// (register on page 0)
#define LTC2947_REG_C2_39_32 0x011

// (register on page 0)
#define LTC2947_REG_C2_31_24 0x012

// (register on page 0)
#define LTC2947_REG_C2_23_16 0x013

// (register on page 0)
#define LTC2947_REG_C2_15_8 0x014

// (register on page 0)
#define LTC2947_REG_C2_7_0 0x015

// Energy2 (register on page 0)
#define LTC2947_REG_E2_47_40 0x016

// (register on page 0)
#define LTC2947_REG_E2_39_32 0x017

// (register on page 0)
#define LTC2947_REG_E2_31_24 0x018

// (register on page 0)
#define LTC2947_REG_E2_23_16 0x019

// (register on page 0)
#define LTC2947_REG_E2_15_8 0x01A

// (register on page 0)
#define LTC2947_REG_E2_7_0 0x01B

// Time2 (register on page 0)
#define LTC2947_REG_TB2_31_24 0x01C

// (register on page 0)
#define LTC2947_REG_TB2_23_16 0x01D

// (register on page 0)
#define LTC2947_REG_TB2_15_8 0x01E

// (register on page 0)
#define LTC2947_REG_TB2_7_0 0x01F

// Maximum current tracking (register on page 0)
#define LTC2947_REG_IMAX_15_8 0x040

// (register on page 0)
#define LTC2947_REG_IMAX_7_0 0x041

// Minimum Current tracking (register on page 0)
#define LTC2947_REG_IMIN_15_8 0x042

// (register on page 0)
#define LTC2947_REG_IMIN_7_0 0x043

// Maximum Power tracking (register on page 0)
#define LTC2947_REG_PMAX_15_8 0x044

// (register on page 0)
#define LTC2947_REG_PMAX_7_0 0x045

// Minimum Power tracking (register on page 0)
#define LTC2947_REG_PMIN_15_8 0x046

// (register on page 0)
#define LTC2947_REG_PMIN_7_0 0x047

// Maximum Voltage (VD = VP - VM) tracking (register on page 0)
#define LTC2947_REG_VMAX_15_8 0x050

// (register on page 0)
#define LTC2947_REG_VMAX_7_0 0x051

// Minimum Voltage (VD = VP - VM) tracking (register on page 0)
#define LTC2947_REG_VMIN_15_8 0x052

// (register on page 0)
#define LTC2947_REG_VMIN_7_0 0x053

// Maximum Temperature tracking (register on page 0)
#define LTC2947_REG_TEMPMAX_15_8 0x054

// (register on page 0)
#define LTC2947_REG_TEMPMAX_7_0 0x055

// Minimum Temperature tracking (register on page 0)
#define LTC2947_REG_TEMPMIN_15_8 0x056

// (register on page 0)
#define LTC2947_REG_TEMPMIN_7_0 0x057

// Maximum Voltage at DVCC tracking (register on page 0)
#define LTC2947_REG_VDVCCMAX_15_8 0x058

// (register on page 0)
#define LTC2947_REG_VDVCCMAX_7_0 0x059

// Minimum Voltage at DVCC tracking (register on page 0)
#define LTC2947_REG_VDVCCMIN_15_8 0x05A

// (register on page 0)
#define LTC2947_REG_VDVCCMIN_7_0 0x05B

// Current (register on page 0)
#define LTC2947_REG_I_23_16 0x090

// (register on page 0)
#define LTC2947_REG_I_15_8 0x091

// (register on page 0)
#define LTC2947_REG_I_7_0 0x092

// Power (register on page 0)
#define LTC2947_REG_P_23_16 0x093

// (register on page 0)
#define LTC2947_REG_P_15_8 0x094

// (register on page 0)
#define LTC2947_REG_P_7_0 0x095

// Voltage (VD = VP - VM) (register on page 0)
#define LTC2947_REG_V_15_8 0x0A0

// (register on page 0)
#define LTC2947_REG_V_7_0 0x0A1

// Temperature (register on page 0)
#define LTC2947_REG_TEMP_15_8 0x0A2

// (register on page 0)
#define LTC2947_REG_TEMP_7_0 0x0A3

// Voltage at DVCC (register on page 0)
#define LTC2947_REG_VDVCC_15_8 0x0A4

// (register on page 0)
#define LTC2947_REG_VDVCC_7_0 0x0A5

// Current History 1 (prev. result) (register on page 0)
#define LTC2947_REG_IH1_23_16 0x0B0

// (register on page 0)
#define LTC2947_REG_IH1_15_8 0x0B1

// (register on page 0)
#define LTC2947_REG_IH1_7_0 0x0B2

// Current History 2 (prev. result '-1') (register on page 0)
#define LTC2947_REG_IH2_23_16 0x0B3

// (register on page 0)
#define LTC2947_REG_IH2_15_8 0x0B4

// (register on page 0)
#define LTC2947_REG_IH2_7_0 0x0B5

// Current History 3 (prev. result '-2') (register on page 0)
#define LTC2947_REG_IH3_23_16 0x0B6

// (register on page 0)
#define LTC2947_REG_IH3_15_8 0x0B7

// (register on page 0)
#define LTC2947_REG_IH3_7_0 0x0B8

// Current History 4 (prev. result '-3') (register on page 0)
#define LTC2947_REG_IH4_23_16 0x0B9

// (register on page 0)
#define LTC2947_REG_IH4_15_8 0x0BA

// (register on page 0)
#define LTC2947_REG_IH4_7_0 0x0BB

// Current History 5 (prev. result '-4') (register on page 0)
#define LTC2947_REG_IH5_23_16 0x0BC

// (register on page 0)
#define LTC2947_REG_IH5_15_8 0x0BD

// (register on page 0)
#define LTC2947_REG_IH5_7_0 0x0BE

// Charge 1 threshold high (register on page 1)
#define LTC2947_REG_C1TH_47_40 0x100

// (register on page 1)
#define LTC2947_REG_C1TH_39_32 0x101

// (register on page 1)
#define LTC2947_REG_C1TH_31_24 0x102

// (register on page 1)
#define LTC2947_REG_C1TH_23_16 0x103

// (register on page 1)
#define LTC2947_REG_C1TH_15_8 0x104

// (register on page 1)
#define LTC2947_REG_C1TH_7_0 0x105

// Charge 1 threshold low (register on page 1)
#define LTC2947_REG_C1TL_47_40 0x106

// (register on page 1)
#define LTC2947_REG_C1TL_39_32 0x107

// (register on page 1)
#define LTC2947_REG_C1TL_31_24 0x108

// (register on page 1)
#define LTC2947_REG_C1TL_23_16 0x109

// (register on page 1)
#define LTC2947_REG_C1TL_15_8 0x10A

// (register on page 1)
#define LTC2947_REG_C1TL_7_0 0x10B

// Time Base 1 threshold high (register on page 1)
#define LTC2947_REG_TB1TH_31_24 0x10C

// (register on page 1)
#define LTC2947_REG_TB1TH_23_16 0x10D

// (register on page 1)
#define LTC2947_REG_TB1TH_15_8 0x10E

// (register on page 1)
#define LTC2947_REG_TB1TH_7_0 0x10F

// Energy 1 threshold high (register on page 1)
#define LTC2947_REG_E1TH_47_40 0x110

// (register on page 1)
#define LTC2947_REG_E1TH_39_32 0x111

// (register on page 1)
#define LTC2947_REG_E1TH_31_24 0x112

// (register on page 1)
#define LTC2947_REG_E1TH_23_16 0x113

// (register on page 1)
#define LTC2947_REG_E1TH_15_8 0x114

// (register on page 1)
#define LTC2947_REG_E1TH_7_0 0x115

// Energy 1 threshold low (register on page 1)
#define LTC2947_REG_E1TL_47_40 0x116

// (register on page 1)
#define LTC2947_REG_E1TL_39_32 0x117

// (register on page 1)
#define LTC2947_REG_E1TL_31_24 0x118

// (register on page 1)
#define LTC2947_REG_E1TL_23_16 0x119

// (register on page 1)
#define LTC2947_REG_E1TL_15_8 0x11A

// (register on page 1)
#define LTC2947_REG_E1TL_7_0 0x11B

// Charge 2 threshold high (register on page 1)
#define LTC2947_REG_C2TH_47_40 0x120

// (register on page 1)
#define LTC2947_REG_C2TH_39_32 0x121

// (register on page 1)
#define LTC2947_REG_C2TH_31_24 0x122

// (register on page 1)
#define LTC2947_REG_C2TH_23_16 0x123

// (register on page 1)
#define LTC2947_REG_C2TH_15_8 0x124

// (register on page 1)
#define LTC2947_REG_C2TH_7_0 0x125

// Charge 2 threshold low (register on page 1)
#define LTC2947_REG_C2TL_47_40 0x126

// (register on page 1)
#define LTC2947_REG_C2TL_39_32 0x127

// (register on page 1)
#define LTC2947_REG_C2TL_31_24 0x128

// (register on page 1)
#define LTC2947_REG_C2TL_23_16 0x129

// (register on page 1)
#define LTC2947_REG_C2TL_15_8 0x12A

// (register on page 1)
#define LTC2947_REG_C2TL_7_0 0x12B

// Time Base 2 threshold high (register on page 1)
#define LTC2947_REG_TB2TH_31_24 0x12C

// (register on page 1)
#define LTC2947_REG_TB2TH_23_16 0x12D

// (register on page 1)
#define LTC2947_REG_TB2TH_15_8 0x12E

// (register on page 1)
#define LTC2947_REG_TB2TH_7_0 0x12F

// Energy 2 threshold high (register on page 1)
#define LTC2947_REG_E2TH_47_40 0x130

// (register on page 1)
#define LTC2947_REG_E2TH_39_32 0x131

// (register on page 1)
#define LTC2947_REG_E2TH_31_24 0x132

// (register on page 1)
#define LTC2947_REG_E2TH_23_16 0x133

// (register on page 1)
#define LTC2947_REG_E2TH_15_8 0x134

// (register on page 1)
#define LTC2947_REG_E2TH_7_0 0x135

// Energy 2 threshold low (register on page 1)
#define LTC2947_REG_E2TL_47_40 0x136

// (register on page 1)
#define LTC2947_REG_E2TL_39_32 0x137

// (register on page 1)
#define LTC2947_REG_E2TL_31_24 0x138

// (register on page 1)
#define LTC2947_REG_E2TL_23_16 0x139

// (register on page 1)
#define LTC2947_REG_E2TL_15_8 0x13A

// (register on page 1)
#define LTC2947_REG_E2TL_7_0 0x13B

// Current threshold high (register on page 1)
#define LTC2947_REG_ITH_15_8 0x180

// (register on page 1)
#define LTC2947_REG_ITH_7_0 0x181

// Current threshold low (register on page 1)
#define LTC2947_REG_ITL_15_8 0x182

// (register on page 1)
#define LTC2947_REG_ITL_7_0 0x183

// Power threshold high (register on page 1)
#define LTC2947_REG_PTH_15_8 0x184

// (register on page 1)
#define LTC2947_REG_PTH_7_0 0x185

// Power threshold low (register on page 1)
#define LTC2947_REG_PTL_15_8 0x186

// (register on page 1)
#define LTC2947_REG_PTL_7_0 0x187

// Voltage (VD = VP - VM) threshold high (register on page 1)
#define LTC2947_REG_VTH_15_8 0x190

// (register on page 1)
#define LTC2947_REG_VTH_7_0 0x191

// Voltage (VD = VP - VM) threshold low (register on page 1)
#define LTC2947_REG_VTL_15_8 0x192

// (register on page 1)
#define LTC2947_REG_VTL_7_0 0x193

// Temperature threshold high (register on page 1)
#define LTC2947_REG_TEMPTH_15_8 0x194

// (register on page 1)
#define LTC2947_REG_TEMPTH_7_0 0x195

// Temperature threshold low (register on page 1)
#define LTC2947_REG_TEMPTL_15_8 0x196

// (register on page 1)
#define LTC2947_REG_TEMPTL_7_0 0x197

// DVCC Voltage threshold high (register on page 1)
#define LTC2947_REG_VDVCCTH_15_8 0x198

// (register on page 1)
#define LTC2947_REG_VDVCCTH_7_0 0x199

// DVCC Voltage threshold low (register on page 1)
#define LTC2947_REG_VDVCCTL_15_8 0x19A

// (register on page 1)
#define LTC2947_REG_VDVCCTL_7_0 0x19B

// Fan temperature threshold high (if this temperature is exceeded the fan is enabled, see GPIOSTATCL) (register on page 1)
#define LTC2947_REG_TEMPTFANH_15_8 0x19C

// (register on page 1)
#define LTC2947_REG_TEMPTFANH_7_0 0x19D

// Fan temperature threshold low (if temperature falls below this value the fan is disabled, see GPIOSTATCL) (register on page 1)
#define LTC2947_REG_TEMPTFANL_15_8 0x19E

// (register on page 1)
#define LTC2947_REG_TEMPTFANL_7_0 0x19F

/////////////////////////////////////////////////////////
// Definitions of all available multiple byte values   //
// (e.g. accumulators).                                //
//                                                     //
// Note: The address constants are 9 bit values where  //
// the lower 8 bit define the address and the 9th bit  //
// defines the page!                                   //
/////////////////////////////////////////////////////////

// Charge1 (register on page 0)
#define LTC2947_VAL_C1 0x000

// Energy1 (register on page 0)
#define LTC2947_VAL_E1 0x006

// Time1 (register on page 0)
#define LTC2947_VAL_TB1 0x00C

// Charge2 (register on page 0)
#define LTC2947_VAL_C2 0x010

// Energy2 (register on page 0)
#define LTC2947_VAL_E2 0x016

// Time2 (register on page 0)
#define LTC2947_VAL_TB2 0x01C

// Maximum current tracking (register on page 0)
#define LTC2947_VAL_IMAX 0x040

// Minimum Current tracking (register on page 0)
#define LTC2947_VAL_IMIN 0x042

// Maximum Power tracking (register on page 0)
#define LTC2947_VAL_PMAX 0x044

// Minimum Power tracking (register on page 0)
#define LTC2947_VAL_PMIN 0x046

// Maximum Voltage (VD = VP - VM) tracking (register on page 0)
#define LTC2947_VAL_VMAX 0x050

// Minimum Voltage (VD = VP - VM) tracking (register on page 0)
#define LTC2947_VAL_VMIN 0x052

// Maximum Temperature tracking (register on page 0)
#define LTC2947_VAL_TEMPMAX 0x054

// Minimum Temperature tracking (register on page 0)
#define LTC2947_VAL_TEMPMIN 0x056

// Maximum Voltage at DVCC tracking (register on page 0)
#define LTC2947_VAL_VDVCCMAX 0x058

// Minimum Voltage at DVCC tracking (register on page 0)
#define LTC2947_VAL_VDVCCMIN 0x05A

// Current (register on page 0)
#define LTC2947_VAL_I 0x090

// Power (register on page 0)
#define LTC2947_VAL_P 0x093

// Voltage (VD = VP - VM) (register on page 0)
#define LTC2947_VAL_V 0x0A0

// Temperature (register on page 0)
#define LTC2947_VAL_TEMP 0x0A2

// Voltage at DVCC (register on page 0)
#define LTC2947_VAL_VDVCC 0x0A4

// Current History 1 (prev. result) (register on page 0)
#define LTC2947_VAL_IH1 0x0B0

// Current History 2 (prev. result '-1') (register on page 0)
#define LTC2947_VAL_IH2 0x0B3

// Current History 3 (prev. result '-2') (register on page 0)
#define LTC2947_VAL_IH3 0x0B6

// Current History 4 (prev. result '-3') (register on page 0)
#define LTC2947_VAL_IH4 0x0B9

// Current History 5 (prev. result '-4') (register on page 0)
#define LTC2947_VAL_IH5 0x0BC

// Charge 1 threshold high (register on page 1)
#define LTC2947_VAL_C1TH 0x100

// Charge 1 threshold low (register on page 1)
#define LTC2947_VAL_C1TL 0x106

// Time Base 1 threshold high (register on page 1)
#define LTC2947_VAL_TB1TH 0x10C

// Energy 1 threshold high (register on page 1)
#define LTC2947_VAL_E1TH 0x110

// Energy 1 threshold low (register on page 1)
#define LTC2947_VAL_E1TL 0x116

// Charge 2 threshold high (register on page 1)
#define LTC2947_VAL_C2TH 0x120

// Charge 2 threshold low (register on page 1)
#define LTC2947_VAL_C2TL 0x126

// Time Base 2 threshold high (register on page 1)
#define LTC2947_VAL_TB2TH 0x12C

// Energy 2 threshold high (register on page 1)
#define LTC2947_VAL_E2TH 0x130

// Energy 2 threshold low (register on page 1)
#define LTC2947_VAL_E2TL 0x136

// Current threshold high (register on page 1)
#define LTC2947_VAL_ITH 0x180

// Current threshold low (register on page 1)
#define LTC2947_VAL_ITL 0x182

// Power threshold high (register on page 1)
#define LTC2947_VAL_PTH 0x184

// Power threshold low (register on page 1)
#define LTC2947_VAL_PTL 0x186

// Voltage (VD = VP - VM) threshold high (register on page 1)
#define LTC2947_VAL_VTH 0x190

// Voltage (VD = VP - VM) threshold low (register on page 1)
#define LTC2947_VAL_VTL 0x192

// Temperature threshold high (register on page 1)
#define LTC2947_VAL_TEMPTH 0x194

// Temperature threshold low (register on page 1)
#define LTC2947_VAL_TEMPTL 0x196

// DVCC Voltage threshold high (register on page 1)
#define LTC2947_VAL_VDVCCTH 0x198

// DVCC Voltage threshold low (register on page 1)
#define LTC2947_VAL_VDVCCTL 0x19A

// Fan temperature threshold high (if this temperature is exceeded the fan is enabled, see GPIOSTATCL) (register on page 1)
#define LTC2947_VAL_TEMPTFANH 0x19C

// Fan temperature threshold low (if temperature falls below this value the fan is disabled, see GPIOSTATCL) (register on page 1)
#define LTC2947_VAL_TEMPTFANL 0x19E

/////////////////////////////////////////////////////////
// Definition of LSB constants.                        //
/////////////////////////////////////////////////////////

#ifndef LTC2947_PRE
#ifndef LTC2947_EXTCLK
// internal oscillator
#define LTC2947_PRE 7
#define LTC2947_DIV 0
#define LTC2947_EXTCLK 4e6
#else
#define LTC2947_EXTCLK_UNSUPPORTED 5
// user provided external oscillator frequency
#define LTC2947_PRE ( \
      LTC2947_EXTCLK <=  1e6 ? 0 : \
      LTC2947_EXTCLK <=  2e6 ? 1 : \
      LTC2947_EXTCLK <=  4e6 ? 2 : \
      LTC2947_EXTCLK <=  8e6 ? 3 : \
      LTC2947_EXTCLK <= 16e6 ? 4 : \
      LTC2947_EXTCLK <= 25e6 ? 5 : LTC2947_EXTCLK_UNSUPPORTED)
#define LTC2947_DIV ((uint8_t)(LTC2947_EXTCLK/32768.0/(1 << LTC2947_PRE)))
#endif
#endif

#define LTC2947_EXTPER (1.0/LTC2947_EXTCLK)

#define LTC2947_INTC 31e-6

// LSB of C1 in As
#define LTC2947_LSB_C1           (0.0384946 * (LTC2947_PRE == 7 ? LTC2947_INTC : LTC2947_EXTPER * (1 << LTC2947_PRE) * (LTC2947_DIV + 1)))
// LSB of E1 in Ws
#define LTC2947_LSB_E1           (0.641576 * (LTC2947_PRE == 7 ? LTC2947_INTC : LTC2947_EXTPER * (1 << LTC2947_PRE) * (LTC2947_DIV + 1)))
// LSB of TB1 in s
#define LTC2947_LSB_TB1          (12.8315 * (LTC2947_PRE == 7 ? LTC2947_INTC : LTC2947_EXTPER * (1 << LTC2947_PRE) * (LTC2947_DIV + 1)))
// LSB of C2 in As
#define LTC2947_LSB_C2           (0.0384946 * (LTC2947_PRE == 7 ? LTC2947_INTC : LTC2947_EXTPER * (1 << LTC2947_PRE) * (LTC2947_DIV + 1)))
// LSB of E2 in Ws
#define LTC2947_LSB_E2           (0.641576 * (LTC2947_PRE == 7 ? LTC2947_INTC : LTC2947_EXTPER * (1 << LTC2947_PRE) * (LTC2947_DIV + 1)))
// LSB of TB2 in s
#define LTC2947_LSB_TB2          (12.8315 * (LTC2947_PRE == 7 ? LTC2947_INTC : LTC2947_EXTPER * (1 << LTC2947_PRE) * (LTC2947_DIV + 1)))
// LSB of IMAX in mA
#define LTC2947_LSB_IMAX         12.0
// LSB of IMIN in mA
#define LTC2947_LSB_IMIN         12.0
// LSB of PMAX in mW
#define LTC2947_LSB_PMAX         200.0
// LSB of PMIN in mW
#define LTC2947_LSB_PMIN         200.0
// LSB of VMAX in mV
#define LTC2947_LSB_VMAX         2.0
// LSB of VMIN in mV
#define LTC2947_LSB_VMIN         2.0
// LSB of TEMPMAX in °C (Note: Temperature has additionally an offset of 5.5 °C)
#define LTC2947_LSB_TEMPMAX      0.204
// Offset of TEMPMAX in °C
#define LTC2947_OFFS_TEMPMAX     5.5
// LSB of TEMPMIN in °C (Note: Temperature has additionally an offset of 5.5 °C)
#define LTC2947_LSB_TEMPMIN      0.204
// Offset of TEMPMIN in °C
#define LTC2947_OFFS_TEMPMIN     5.5
// LSB of VDVCCMAX in mV
#define LTC2947_LSB_VDVCCMAX     145.0
// LSB of VDVCCMIN in mV
#define LTC2947_LSB_VDVCCMIN     145.0
// LSB of I in mA
#define LTC2947_LSB_I            3.0
// LSB of P in mW
#define LTC2947_LSB_P            50.0
// LSB of V in mV
#define LTC2947_LSB_V            2.0
// LSB of TEMP in °C (Note: Temperature has additionally an offset of 5.5 °C)
#define LTC2947_LSB_TEMP         0.204
// Offset of TEMP in °C
#define LTC2947_OFFS_TEMP        5.5
// LSB of VDVCC in mV
#define LTC2947_LSB_VDVCC        145.0
// LSB of IH1 in mA
#define LTC2947_LSB_IH1          3.0
// LSB of IH2 in mA
#define LTC2947_LSB_IH2          3.0
// LSB of IH3 in mA
#define LTC2947_LSB_IH3          3.0
// LSB of IH4 in mA
#define LTC2947_LSB_IH4          3.0
// LSB of IH5 in mA
#define LTC2947_LSB_IH5          3.0
// LSB of C1TH in As
#define LTC2947_LSB_C1TH         (0.0384946 * (LTC2947_PRE == 7 ? LTC2947_INTC : LTC2947_EXTPER * (1 << LTC2947_PRE) * (LTC2947_DIV + 1)))
// LSB of C1TL in As
#define LTC2947_LSB_C1TL         (0.0384946 * (LTC2947_PRE == 7 ? LTC2947_INTC : LTC2947_EXTPER * (1 << LTC2947_PRE) * (LTC2947_DIV + 1)))
// LSB of TB1TH in s
#define LTC2947_LSB_TB1TH        (12.8315 * (LTC2947_PRE == 7 ? LTC2947_INTC : LTC2947_EXTPER * (1 << LTC2947_PRE) * (LTC2947_DIV + 1)))
// LSB of E1TH in Ws
#define LTC2947_LSB_E1TH         (0.641576 * (LTC2947_PRE == 7 ? LTC2947_INTC : LTC2947_EXTPER * (1 << LTC2947_PRE) * (LTC2947_DIV + 1)))
// LSB of E1TL in Ws
#define LTC2947_LSB_E1TL         (0.641576 * (LTC2947_PRE == 7 ? LTC2947_INTC : LTC2947_EXTPER * (1 << LTC2947_PRE) * (LTC2947_DIV + 1)))
// LSB of C2TH in As
#define LTC2947_LSB_C2TH         (0.0384946 * (LTC2947_PRE == 7 ? LTC2947_INTC : LTC2947_EXTPER * (1 << LTC2947_PRE) * (LTC2947_DIV + 1)))
// LSB of C2TL in As
#define LTC2947_LSB_C2TL         (0.0384946 * (LTC2947_PRE == 7 ? LTC2947_INTC : LTC2947_EXTPER * (1 << LTC2947_PRE) * (LTC2947_DIV + 1)))
// LSB of TB2TH in s
#define LTC2947_LSB_TB2TH        (12.8315 * (LTC2947_PRE == 7 ? LTC2947_INTC : LTC2947_EXTPER * (1 << LTC2947_PRE) * (LTC2947_DIV + 1)))
// LSB of E2TH in Ws
#define LTC2947_LSB_E2TH         (0.641576 * (LTC2947_PRE == 7 ? LTC2947_INTC : LTC2947_EXTPER * (1 << LTC2947_PRE) * (LTC2947_DIV + 1)))
// LSB of E2TL in Ws
#define LTC2947_LSB_E2TL         (0.641576 * (LTC2947_PRE == 7 ? LTC2947_INTC : LTC2947_EXTPER * (1 << LTC2947_PRE) * (LTC2947_DIV + 1)))
// LSB of ITH in mA
#define LTC2947_LSB_ITH          12.0
// LSB of ITL in mA
#define LTC2947_LSB_ITL          12.0
// LSB of PTH in mW
#define LTC2947_LSB_PTH          200.0
// LSB of PTL in mW
#define LTC2947_LSB_PTL          200.0
// LSB of VTH in mV
#define LTC2947_LSB_VTH          2.0
// LSB of VTL in mV
#define LTC2947_LSB_VTL          2.0
// LSB of TEMPTH in °C (Note: Temperature has additionally an offset of 5.5 °C)
#define LTC2947_LSB_TEMPTH       0.204
// Offset of TEMPTH in °C
#define LTC2947_OFFS_TEMPTH      5.5
// LSB of TEMPTL in °C (Note: Temperature has additionally an offset of 5.5 °C)
#define LTC2947_LSB_TEMPTL       0.204
// Offset of TEMPTL in °C
#define LTC2947_OFFS_TEMPTL      5.5
// LSB of VDVCCTH in mV
#define LTC2947_LSB_VDVCCTH      145.0
// LSB of VDVCCTL in mV
#define LTC2947_LSB_VDVCCTL      145.0
// LSB of TEMPTFANH in °C (Note: Temperature has additionally an offset of 5.5 °C)
#define LTC2947_LSB_TEMPTFANH    0.204
// Offset of TEMPTFANH in °C
#define LTC2947_OFFS_TEMPTFANH   5.5
// LSB of TEMPTFANL in °C (Note: Temperature has additionally an offset of 5.5 °C)
#define LTC2947_LSB_TEMPTFANL    0.204
// Offset of TEMPTFANL in °C
#define LTC2947_OFFS_TEMPTFANL   5.5

/////////////////////////////////////////////////////////
// Definition of all bit masks.                        //
/////////////////////////////////////////////////////////

// GPIOSTATCL bit masks
// GPIO output enable (0: input, 1: output)
#define LTC2947_BM_GPIOSTATCL_GPOEN 0x1
// This register shows the applied level at pin GPIO
// 0: Logical level 0 at pin GPIO
// 1: Logical level 1 at pin GPIO
#define LTC2947_BM_GPIOSTATCL_GPI 0x10
// This register sets the level at GPIO if set as output provided there is a pull-up resistor at GPIO
// 0: Pin GPIO is set to 0 if set as output
// 1: Pin GPIO is set to 1 if set as output
#define LTC2947_BM_GPIOSTATCL_GPO 0x20
// GPIO fan control enable
// 0:GPIO controlled by GPO
// 1: GPIO controlled by temperature measurement against thresholds
#define LTC2947_BM_GPIOSTATCL_FANEN 0x40
// Polarity of GPIO if FANEN = 1
// 0: low active
// 1: high active
#define LTC2947_BM_GPIOSTATCL_FANPOL 0x80

// STATUS bit masks
// 1: Undervoltage in the analog domain including ADCs during a conversion
#define LTC2947_BM_STATUS_UVLOA 0x1
// 1: Power On Reset has occurred due to undervoltage in the analog domain.
#define LTC2947_BM_STATUS_PORA 0x2
// 1: Undervoltage in the standby domain
#define LTC2947_BM_STATUS_UVLOSTBY 0x4
// 1: Undervoltage in the digital domain
#define LTC2947_BM_STATUS_UVLOD 0x8
// 1: Result registers have been updated
#define LTC2947_BM_STATUS_UPDATE 0x10
// 1: The ADC conversion is not valid due to undervoltage during a conversion
#define LTC2947_BM_STATUS_ADCERR 0x20
// 1: External clock provided does not fit to TBC settings. Also LSB values of accumulated registers are wrong.
#define LTC2947_BM_STATUS_TBERR 0x40

// STATVT bit masks
// 1: Voltage (VD = VP - VM) high threshold exceeded
#define LTC2947_BM_STATVT_VH 0x1
// 1: Voltage (VD = VP - VM) low threshold exceeded
#define LTC2947_BM_STATVT_VL 0x2
// 1: Temperature high threshold exceeded
#define LTC2947_BM_STATVT_TEMPH 0x4
// 1: Temperature low threshold exceeded
#define LTC2947_BM_STATVT_TEMPL 0x8
// 1: Fan high temperature threshold exceeded
#define LTC2947_BM_STATVT_FANH 0x10
// 1: Fan low temperature threshold exceeded
#define LTC2947_BM_STATVT_FANL 0x20

// STATIP bit masks
// 1: Current high threshold exceeded
#define LTC2947_BM_STATIP_IH 0x1
// 1: Current low threshold exceeded
#define LTC2947_BM_STATIP_IL 0x2
// 1: Power high threshold exceeded
#define LTC2947_BM_STATIP_PH 0x4
// 1: Power low threshold exceeded
#define LTC2947_BM_STATIP_PL 0x8

// STATC bit masks
// 1: Charge1 high threshold exceeded
#define LTC2947_BM_STATC_C1H 0x1
// 1: Charge1 low threshold exceeded
#define LTC2947_BM_STATC_C1L 0x2
// 1: Charge2 high threshold exceeded
#define LTC2947_BM_STATC_C2H 0x4
// 1: Charge2 low threshold exceeded
#define LTC2947_BM_STATC_C2L 0x8

// STATE bit masks
// 1: Energy1 high threshold exceeded
#define LTC2947_BM_STATE_E1H 0x1
// 1: Energy1 low threshold exceeded
#define LTC2947_BM_STATE_E1L 0x2
// 1: Energy2 high threshold exceeded
#define LTC2947_BM_STATE_E2H 0x4
// 1: Energy2 low threshold exceeded
#define LTC2947_BM_STATE_E2L 0x8

// STATCEOF bit masks
// 1: Charge1 overflow alert
#define LTC2947_BM_STATCEOF_C1OF 0x1
// 1: Charge2 overflow alert
#define LTC2947_BM_STATCEOF_C2OF 0x2
// 1: Energy1 overflow alert
#define LTC2947_BM_STATCEOF_E1OF 0x10
// 1: Energy2 overflow alert
#define LTC2947_BM_STATCEOF_E2OF 0x20

// STATTB bit masks
// 1: Time1 threshold exceeded
#define LTC2947_BM_STATTB_TB1TH 0x1
// 1: Time2 threshold exceeded
#define LTC2947_BM_STATTB_TB2TH 0x2
// 1: Time1 overflow
#define LTC2947_BM_STATTB_TB1OF 0x10
// 1: Time 2 overflow
#define LTC2947_BM_STATTB_TB2OF 0x20

// STATVDVCC bit masks
// 1: DVCC high threshold exceeded
#define LTC2947_BM_STATVDVCC_VDVCCH 0x1
// 1: DVCC low threshold exceeded
#define LTC2947_BM_STATVDVCC_VDVCCL 0x2

// STATUSM bit masks
// UVLOA mask
#define LTC2947_BM_STATUSM_UVLOAM 0x1
// UVLOD mask
#define LTC2947_BM_STATUSM_UVLODM 0x8
// UPDATE mask
#define LTC2947_BM_STATUSM_UPDATEM 0x10
// ADCERR mask
#define LTC2947_BM_STATUSM_ADCERRM 0x20
// TBERR mask
#define LTC2947_BM_STATUSM_TBERRM 0x40

// STATVTM bit masks
// VH mask
#define LTC2947_BM_STATVTM_VHM 0x1
// VL mask
#define LTC2947_BM_STATVTM_VLM 0x2
// TEMPH mask
#define LTC2947_BM_STATVTM_TEMPHM 0x4
// TEMPL mask
#define LTC2947_BM_STATVTM_TEMPLM 0x8
// FANH mask
#define LTC2947_BM_STATVTM_FANHM 0x10
// FANL mask
#define LTC2947_BM_STATVTM_FANLM 0x20

// STATIPM bit masks
// IH mask
#define LTC2947_BM_STATIPM_IHM 0x1
// IL mask
#define LTC2947_BM_STATIPM_ILM 0x2
// PH mask
#define LTC2947_BM_STATIPM_PHM 0x4
// PL mask
#define LTC2947_BM_STATIPM_PLM 0x8

// STATCM bit masks
// C1H mask
#define LTC2947_BM_STATCM_C1HM 0x1
// C1L mask
#define LTC2947_BM_STATCM_C1LM 0x2
// C2H mask
#define LTC2947_BM_STATCM_C2HM 0x4
// C2L mask
#define LTC2947_BM_STATCM_C2LM 0x8

// STATEM bit masks
// E1H mask
#define LTC2947_BM_STATEM_E1HM 0x1
// E1L mask
#define LTC2947_BM_STATEM_E1LM 0x2
// E2H mask
#define LTC2947_BM_STATEM_E2HM 0x4
// E2L mask
#define LTC2947_BM_STATEM_E2LM 0x8

// STATCEOFM bit masks
// C1OF mask
#define LTC2947_BM_STATCEOFM_C1OFM 0x1
// C2OF mask
#define LTC2947_BM_STATCEOFM_C2OFM 0x2
// E1OF mask
#define LTC2947_BM_STATCEOFM_E1OFM 0x10
// E2OF mask
#define LTC2947_BM_STATCEOFM_E2OFM 0x20

// STATTBM bit masks
// TB1TH mask
#define LTC2947_BM_STATTBM_TB1THM 0x1
// TB2TH mask
#define LTC2947_BM_STATTBM_TB2THM 0x2
// TB1OF mask
#define LTC2947_BM_STATTBM_TB1OFM 0x10
// TB2OF mask
#define LTC2947_BM_STATTBM_TB2OFM 0x20

// STATVDVCCM bit masks
// VDVCCH mask
#define LTC2947_BM_STATVDVCCM_VDVCCHM 0x1
// VDVCCL mask
#define LTC2947_BM_STATVDVCCM_VDVCCLM 0x2

// ACCICTL bit masks
// accumulator 1 current control (bit 0)
#define LTC2947_BM_ACCICTL_ACC1I0 0x1
// accumulator 1 current control (bit 1)
#define LTC2947_BM_ACCICTL_ACC1I1 0x2
// accumulator 2 current control (bit 0)
#define LTC2947_BM_ACCICTL_ACC2I0 0x4
// accumulator 2 current control (bit 1)
#define LTC2947_BM_ACCICTL_ACC2I1 0x8

// ACCGPCTL bit masks
// accumulator 1 GPIO control (bit 0)
#define LTC2947_BM_ACCGPCTL_ACC1GP0 0x1
// accumulator 1 GPIO control (bit 1)
#define LTC2947_BM_ACCGPCTL_ACC1GP1 0x2
// accumulator 2 GPIO control (bit 0)
#define LTC2947_BM_ACCGPCTL_ACC2GP0 0x4
// accumulator 2 GPIO control (bit 1)
#define LTC2947_BM_ACCGPCTL_ACC2GP1 0x8

// ALERTBCTL bit masks
// 0: Unmasked alerts (see MASK registers) are not forwarded to ALERT pin
// 1: Unmasked alerts (see MASK registers) are forwarded to ALERT pin
#define LTC2947_BM_ALERTBCTL_ALERTBEN 0x1

// TBCTL bit masks
// Prescaler value bit 0, binary coded.
#define LTC2947_BM_TBCTL_PRE_0 0x1
// Prescaler value bit 1, binary coded.
#define LTC2947_BM_TBCTL_PRE_1 0x2
// Prescaler value bit 2, binary coded.
#define LTC2947_BM_TBCTL_PRE_2 0x4
// Divider value bit 0, binary coded.
#define LTC2947_BM_TBCTL_DIV_0 0x8
// Divider value bit 1, binary coded.
#define LTC2947_BM_TBCTL_DIV_1 0x10
// Divider value bit 2, binary coded.
#define LTC2947_BM_TBCTL_DIV_2 0x20
// Divider value bit 3, binary coded.
#define LTC2947_BM_TBCTL_DIV_3 0x40
// Divider value bit 4, binary coded.
#define LTC2947_BM_TBCTL_DIV_4 0x80

// OPCTL bit masks
// 0: Normal operation
// 1: Shutdown. The LTC2947 will exit shutdown in SPI mode if the pin AD1/CS is pulled lowand in I2C mode if it receives the correct I2C address (programmed at the ADx pins).
#define LTC2947_BM_OPCTL_SHDN 0x1
// 1: Clear. The accumulation and tracking (max/min) registers are cleared: C1, E1, TB1, C2, E2, TB2, IMAX, IMIN, PMAX, PMIN, VMAX, VMIN, TEMPMAX, TEMPMIN, VDVCCMAX,VDVCCMIN.
#define LTC2947_BM_OPCTL_CLR 0x2
// 1: Single Shot Measurement. A single set of measurements of current, voltage, power, temperature and VDVCC are performed and the result registers updated. If CONT is set, it is cleared after completion of any conversion cycle in progress and the single shot measurement is executed. SSHOT is cleared after the single measurement cycle is complete.
#define LTC2947_BM_OPCTL_SSHOT 0x4
// 0: Idle mode (no measurement)
// 1: Continuous measurement is enabled. Measurement cycles run continuously. Charge and energy measurements are only active in continuous mode.
#define LTC2947_BM_OPCTL_CONT 0x8
// Global Reset. When set, the 2947 is reset and all registers are set to their default values.
#define LTC2947_BM_OPCTL_RST 0x80

// PGCTL bit masks
// Memory Map Page Select.
// 0: PAGE 0 of memory map is selected.
// 1: PAGE 1 of memory map is selected.
#define LTC2947_BM_PGCTL_PAGE 0x1

/** @} */ // end of LTC2947 auto generated code

/** @name function prototypes
*  @{
*/

//! Initializes the LTC2947 library for I2C mode operation and configures the slave address
//! see defines LTC2947_I2C_ADDR_LL to LTC2947_I2C_ADDR_RR for possible slave addresses
void LTC2947_InitI2C(
  uint8_t slvAddr //!< 7-bit I2C slave address of the LTC2947 (e.g. LTC2947_I2C_ADDR_LL)
);

//! Initializes the LTC2947 library for SPI mode operation
void LTC2947_InitSPI();

//! Converts an array of 2 bytes to 16-bit unsigned integer
//! @return 16-bit unsigned integer
uint16_t LTC2947_2BytesToUInt16(
  byte *bytes //!< 2 byte array (MSB first)
);

//! converts an array of 3 bytes to 32-bit unsigned integer
//! @return 32-bit unsigned integer
uint32_t LTC2947_3BytesToUInt32(
  byte *bytes //!< 3 byte array (MSB first)
);

//! Converts an array of 4 bytes to 32-bit unsigned integer
//! @return 32-bit unsigned integer
uint32_t LTC2947_4BytesToUInt32(
  byte *bytes //!< 4 byte array (MSB first)
);

//! converts an array of 2 bytes to 16-bit signed integer
//! @return 16-bit signed integer
int16_t LTC2947_2BytesToInt16(
  byte *bytes //!< 2 byte array (MSB first)
);

//! converts an array of 3 bytes to 32-bit signed integer
//! @return 32-bit signed integer
int32_t LTC2947_3BytesToInt32(
  byte *bytes //!< 3 byte array (MSB first)
);

//! Converts an array of 4 bytes to 32-bit signed integer
//! @return 32-bit signed integer
int32_t LTC2947_4BytesToInt32(
  byte *bytes //!< 4 byte array (MSB first)
);

//! Converts an unsigned value of arbitrary number of bytes to a floating point value with the scaling factor lsb
//! The input value must be usigned, use LTC2947_Abs to convert the bytes to an absolute (positive) value or use
//! LTC2947_SignedBytesToDouble instead.
//! For input values with up to 4 bytes use LTC2947_<X>BytesToUInt<N> (e.g. LTC2947_4BytesToUInt32, LTC2947_2BytesToUInt16)
//! and multiply the result by lsb for reduced calculation time, e.g. "LTC2947_4BytesToUInt32(bytes)*lsb".
//! @return result in floating point number format (absolute value, so always positive!)
double LTC2947_UnsignedBytesToDouble(
  uint8_t *unsignedBytes, //!< unsigned input value as byte array (MSB first)
  uint8_t length,         //!< number of bytes of the unsigned value
  double lsb              //!< lsb value (scaling factor used to scale the unsigned value)
);

//! Converts a signed value of arbitrary number of bytes to a floating point value with the scaling factor lsb
//! The input value must be a signed format. For unsigned values use LTC2947_UnsignedBytesToDouble.
//! For input values with up to 4 bytes use LTC2947_<X>BytesToInt<N> (e.g. LTC2947_4BytesToInt32, LTC2947_2BytesToInt16)
//! and multiply the result by lsb for reduced calculation time, e.g. "LTC2947_4BytesToInt32(bytes)*lsb".
//! @return result in floating point number format.
double LTC2947_SignedBytesToDouble(
  uint8_t *signedBytes, //!< signed input value as byte array (MSB first)
  uint8_t length,     //!< number of bytes of the signed value
  double lsb        //!< lsb value (scaling factor used to scale the signed value)
);

//! Converts a signed or unsigned value of arbitrary number of bytes to a floating point number
//! @return result in floating point number format.
double LTC2947_BytesToDouble(
  uint8_t *bytes, //!< input value as byte array (MSB first)
  uint8_t length, //!< number of bytes of the input value
  boolean sig,    //!< true for signed value, false for unsigned
  double lsb      //!< lsb value (scaling factor used to scale the input value)
);

//! Converts a floating point number that was scaled with a given LSB to an integer representation
//! that will be stored in the given byte array.
void LTC2947_DoubleToBytes(
  double value, //!< floating point value
  double lsb,   //!< lsb of the floating point value (integer = value / lsb)
  uint8_t *bytes, //!< byte array of the integer representation
  uint8_t length  //!< length (<=8) of the byte array. Note: The function does not check for overflow of the integer representation
);

//! Prints a 8-bit value in 2-character hexadecimal format with left padded zeros
void LTC2947_SerialPrint8hex(
  uint8_t val //!< 8-bit input value
);

//! Prints a 16-bit value in 4-character hexadecimal format with left padded zeros
void LTC2947_SerialPrint16hex(
  uint16_t val  //!< 16-bit input value
);

//! Prints a 32-bit value in 8-character hexadecimal format with left padded zeros
void LTC2947_SerialPrint32hex(
  uint32_t val  //!< 32-bit input value
);

//! Prints a 64-bit value in 16-character hexadecimal format with left padded zeros
void LTC2947_SerialPrint64hex(
  uint64_t uint64Val  //!< 64-bit input value
);

#ifdef LTC2947_DEBUG
//! conversion function test
void LTC2947_DoubleToBytes_Test();
#endif

//! Calculates the absolute value of a signed value with arbitrary number of bytes
//! @return true: value was inverted, false: value was already positive
boolean LTC2947_Abs(
  uint8_t *bytes,  //!< bytes of the signed value (MSB first)
  uint8_t length   //!< number of bytes
);

//! Reads current (I), power (P), voltage (V), temperature (TEMP)
//! and supply voltage (VCC) from the device
//! Make sure LTC2947's page 0 is selected before calling this function.
//! Use LTC2947_SetPageSelect to change page if necessary
void LTC2947_Read_I_P_V_TEMP_VCC(
  float *I,  //!< Current im amps
  float *P,  //!< Power in watts
  float *V,  //!< Voltage in volts
  float *TEMP, //!< Temperature in degree celcius
  float *VCC   //!< Supply voltage in volts
);

//! Reads charge (C), energy (E) and time (TB) from the device.
//! Charge and Energy are converted to absulte
//! values (always positive!) and a separate sign bit.
//! If the separation in absolute value and sign is not desired use
//! LTC2947_Read_C_E_TB instead.
//! Make sure LTC2947's page 0 is selected before calling this function.
//! Use LTC2947_SetPageSelect to change page if necessary
void LTC2947_Read_Abs_C_E_TB(
  boolean accuSet1, //!< True: Read C1, E1, TB1. False: Read C2, E2, TB2.
  double *C,      //!< Absolute value of charge in As
  boolean *signC,   //!< Sign of charge (True: negative, False: positive)
  double *E,      //!< Absolute value of energy in Ws
  boolean *signE,   //!< Sign of energy (True: negative, False: positive)
  double *TB      //!< Time in s
);

//! Reads charge (C), energy (E) and time (TB) from the device.
//! Make sure LTC2947's page 0 is selected before calling this function.
//! Use LTC2947_SetPageSelect to change page if necessary
void LTC2947_Read_C_E_TB(
  boolean accuSet1, //!< True: Read C1, E1, TB1. False: Read C2, E2, TB2.
  double *C,      //!< Signed charge in As
  double *E,      //!< Signed energy in Ws
  double *TB      //!< Time in s
);

//! read single byte from SPI interface
//! @return always 0
int8_t LTC2947_SpiRdByte(
  uint8_t address,   //!< register address
  uint8_t *value     //!< Byte pointer to store read byte
);

//! write single byte to SPI interface
//! @return always 0
int8_t LTC2947_SpiWrByte(
  uint8_t address,   //!< register address
  uint8_t value      //!< Byte to be written
);

//! read array of bytes from the SPI interface
//! @return always 0
int8_t LTC2947_SpiRdBlock(
  uint8_t address,  //!< register address
  uint8_t length,   //!< Length of array
  uint8_t *values   //!< Byte array to store read bytes
);

//! writes block (array) of bytes to the SPI interface
//! @return always 0
int8_t LTC2947_SpiWrBlock(
  uint8_t address,  //!< register address
  uint8_t length,   //!< Length of array
  uint8_t *values   //!< Byte array to be written
);

//! read single byte via I2C
//! @return 0 if successful, 1 if not successful
int8_t LTC2947_I2CRdByte(
  uint8_t slvAddr, //!< The slv address.
  uint8_t regAddr, //!< The register address.
  uint8_t *value   //!< byte that was read
);

//! read multiple bytes via I2C
//! @return 0 if successful, 1 if not successful
int8_t LTC2947_I2CRdBlock(
  uint8_t slvAddr, //!< The slv address.
  uint8_t regAddr, //!< The register address.
  uint8_t length,  //!< number of bytes.
  uint8_t *values  //!< read bytes
);

//! write single byte via I2C
//! @return 0 if successful, 1 if not successful
int8_t LTC2947_I2CWrByte(
  uint8_t slvAddr, //!< The slv address.
  uint8_t regAddr, //!< The register address.
  uint8_t value    //!< byte to be written
);

//! write byte array via I2C interface
//! @return 0 if successful, 1 if not successful
int8_t LTC2947_I2CWrBlock(
  uint8_t slvAddr, //!< The slv address.
  uint8_t regAddr, //!< The register address.
  uint8_t length,  //!< number of bytes.
  uint8_t *values  //!< bytes to be written
);

//! general I2C communication error
#define LTC2947_ARA_ERROR             0xFF
//! got ARA response from LTC2947
#define LTC2947_ARA_LTC2947_RESPONSE  1
//! got ARA response from some other I2C slave
#define LTC2947_ARA_OTHER_RESPONSE    2
//! got NO ARA response
#define LTC2947_ARA_NO_RESPONSE       3
//! got ARA response from any slave but wihtout the expected WR bit
#define LTC2947_ARA_RESPONSE_WO_WR      4
//! The general alert response address
#define LTC2947_ALERT_RESP_ADDR 0x0C


//! Sends the Alert Response address to the I2C bus and reads the response
//! If two or more devices on the same bus are generating
//! alerts when the ARA is broadcasted, standard I2C arbitra-
//! tion causes the device with the highest priority (lowest
//! address) to reply first and the device with the lowest pri-
//! ority (highest address) to reply last.The bus master will
//! repeat the alert response protocol until the ALERT line is
//! released.
//! @return LTC2947_ARA_ERROR            : general I2C communication error
//!         LTC2947_ARA_LTC2947_RESPONSE : got ARA response from LTC2947
//!         LTC2947_ARA_OTHER_RESPONSE   : got ARA response from some other I2C slave
//!         LTC2947_ARA_NO_RESPONSE      : got NO ARA response
//!         LTC2947_ARA_RESPONSE_WO_WR   : got ARA response from any slave but wihtout the expected WR bit
uint8_t LTC2947_Ara(
  uint8_t *svlAddr //!< 7-bit address of the responding slave
);

//! write LTC2947's page control register to selected one of two memory pages
void LTC2947_SetPageSelect(
  boolean page //!< false: select page 0, true: select page 1
);

//! Wake up LTC2947 from shutdown mode and measure the wakeup time
//! @return -1 in case of timeout or milliseconds it took to wakeup LTC2947
int16_t LTC2947_wake_up();

//! reads LTC2947's page control register to determine the currently selected memory page
//! @return true: page 1 is selected, false: page 0
boolean LTC2947_GetCurrentPageSelect();

//! reads the current GPIO pin state
//! Make sure LTC2947's page 0 is selected before calling this function.
//! Use LTC2947_SetPageSelect to change page if necessary
//! @return true: pin high, false: pin low
boolean LTC2947_GPIO_Read();

//! Enables/disables the output driver on the GPIO pin
//! Make sure LTC2947's page 0 is selected before calling this function.
//! Use LTC2947_SetPageSelect to change page if necessary
void LTC2947_GPIO_PinMode(
  uint8_t mode //!< OUTPUT (1): output driver enabled, INPUT (0): output driver disabled
);

//! Sets the level of the output driver on the GPIO pin
//! This has only an effect if the output driver is enabled, see LTC2947_GPIO_PinMode
//! Make sure LTC2947's page 0 is selected before calling this function.
//! Use LTC2947_SetPageSelect to change page if necessary
void LTC2947_GPIO_SetPinState(
  uint8_t val //!< LOW (0): pin low, HIGH (1): pin high
);

/** @} */ // end of function prototypes

//! controlled by LTC2947_InitI2C / LTC2947_InitSPI to switch between I2C / SPI mode
extern boolean LTC2947_SPI_Mode_Enabled;
//! set by LTC2947_InitI2C to set slave address for I2C operation
extern uint8_t LTC2947_I2C_Slave_Addr;

/** @name serial communication wrapper macros
*  LTC2947's I2C / Spi functions are wrapped to common serial communication functions
*  The user sets the active communication interface by calling LTC2947_InitI2C or LTC2947_InitSPI
*  once at boot-up.
*  @{
*/

//! read multiple bytes via I2C/SPI
//! @return 0 if successful, 1 if not successful
static inline int8_t LTC2947_RD_BYTES(
  uint8_t REG_ADDR, //!< The register address.
  uint8_t LENGTH,   //!< number of bytes.
  uint8_t *BYTES    //!< read bytes
)
{
  return LTC2947_SPI_Mode_Enabled
         ? LTC2947_SpiRdBlock(REG_ADDR, LENGTH, BYTES)
         : LTC2947_I2CRdBlock(LTC2947_I2C_Slave_Addr, REG_ADDR, LENGTH, BYTES);
}

//! write byte array via I2C/SPI interface
//! @return 0 if successful, 1 if not successful
static inline int8_t LTC2947_WR_BYTES(
  uint8_t REG_ADDR, //!< The register address.
  uint8_t LENGTH,   //!< number of bytes.
  uint8_t *BYTES    //!< bytes to be written
)
{
  return LTC2947_SPI_Mode_Enabled
         ? LTC2947_SpiWrBlock(REG_ADDR, LENGTH, BYTES)
         : LTC2947_I2CWrBlock(LTC2947_I2C_Slave_Addr, REG_ADDR, LENGTH, BYTES);
}

//! read single byte via I2C/SPI
//! @return 0 if successful, 1 if not successful
static inline int8_t LTC2947_RD_BYTE(
  uint8_t REG_ADDR, //!< The register address.
  uint8_t *RESULT   //!< byte that was read
)
{
  return LTC2947_SPI_Mode_Enabled
         ? LTC2947_SpiRdByte(REG_ADDR, RESULT)
         : LTC2947_I2CRdByte(LTC2947_I2C_Slave_Addr, REG_ADDR, RESULT);
}

//! write single byte via I2C
//! @return 0 if successful, 1 if not successful
static inline int8_t LTC2947_WR_BYTE(
  uint8_t REG_ADDR, //!< The register address.
  uint8_t VALUE   //!< byte to be written
)
{
  return LTC2947_SPI_Mode_Enabled
         ? LTC2947_SpiWrByte(REG_ADDR, VALUE)
         : LTC2947_I2CWrByte(LTC2947_I2C_Slave_Addr, REG_ADDR, VALUE);
}
/** @} */ // end of serial communication wrapper macros

#endif  // LTC2947_H

Technical Support