mirror of
https://github.com/letscontrolit/ESPEasy.git
synced 2026-07-28 04:07:47 +00:00
431 lines
32 KiB
C++
431 lines
32 KiB
C++
/*
|
|
Program to demonstrate using the interrupt pin of any INA2xx which supports that functionality in
|
|
order to trigger readings in the background and using a timer on the Arduino to trigger data
|
|
averaging and storing or displaying the computed values.
|
|
|
|
The INA226 is set up to pull the alert pin down when a measurement is ready. The program has set
|
|
the bus and shunt to the maximum conversion time of 8.244ms and then averaging to 8, so each
|
|
measurement will take about 64ms. The interrupt vector "PCINT0_vect" is called and the readings
|
|
from the INA226 are read and added to the averages.
|
|
|
|
A timer interrupt is defined in the setup() method that triggers a call to the vector
|
|
"TIMER1_COMPA_vect" once every second. The average values collected in the "PCINT0_vect" call are
|
|
then taken and stored in memory. As the amount of RAM is limited and the absolute readings are 2
|
|
Bytes long while the delta values to the previous measurement are usually quite small, a variable
|
|
length Huffmann coding has been implemented at a nibble (4 bit) level to provide a higher-density
|
|
method of storing data. Each array is declared at 600 Bytes (one array for voltage measurements and
|
|
one array for shunt voltage) and those 1200 Bytes total can store up to 18 minutes of per-second
|
|
data, which would otherwise occupy over 4Kb memory. The Huffmann encoding method is described in
|
|
more detail in the interrupt code below.
|
|
|
|
This example works on Atmel-Arduinos since it uses Atmel interrupts which are different on
|
|
processors such as the ESP32. The value of ARRAY_BYTES is set at 1200, which works on Arduinos with
|
|
2K or more of RAM, smaller processors would need to reduce this value in order to work correctly.
|
|
The example is also coded for the INA226, as a chip with an ALERT pin is required for the program
|
|
to work; additionally the hard-coded LSB values for the bus voltage and shunt voltage have been set
|
|
to those used in the INA226.
|
|
|
|
Detailed documentation can be found on the GitHub Wiki pages at
|
|
https://github.com/Zanduino/INA/wiki
|
|
|
|
This example is for a INA226 set up to measure a 5-Volt load with a 0.1Ω resistor in place, this is
|
|
the same setup that can be found in the Adafruit INA226 breakout board. The complex calibration
|
|
options are done at runtime using the 2 parameters specified in the "begin()" call and the library
|
|
has gone to great lengths to avoid the use of floating point to conserve space and minimize
|
|
runtime. This demo program uses floating point only to convert and display the data conveniently.
|
|
The INA226 uses 15 bits of precision, and even though the current and watt information is returned
|
|
using 32-bit integers the precision remains the same.
|
|
|
|
The INA226 is set up to measure using the maximum conversion length (and maximum accuracy) and then
|
|
average those readings 64 times. This results in readings taking 8.244ms x 64 = 527.616ms or just
|
|
less than 2 times a second. The pin-change interrupt handler is called when a reading is finished
|
|
and the INA226 pulls the pin down to ground, it resets the pin status and adds the readings to the
|
|
global variables. The main program will do whatever processing it has to and every 5 seconds it
|
|
will display the current averaged readings and reset them.
|
|
|
|
The datasheet for the INA226 can be found at http://www.ti.com/lit/ds/symlink/INA226.pdf and it
|
|
contains the information required in order to hook up the device. Unfortunately it comes as a VSSOP
|
|
package but it can be soldered onto a breakout board for breadboard use. The INA226 is quite
|
|
similar to the INA219 mentioned above, but it can take bus voltages of up to 36V (which I needed in
|
|
order to monitor a 24V battery system which goes above 28V while charging and which is above the
|
|
absolute limits of the INA219). It is also significantly more accurate than the INA219, plus has an
|
|
alert pin.
|
|
|
|
Interrupts on Arduinos can get a bit confusing, differentiating between external interrupts and pin
|
|
change interrupts. The external interrupts are limited and which pins are available are different
|
|
for each processor, see
|
|
https://www.arduino.cc/reference/en/language/functions/external-interrupts/attachinterrupt/ for
|
|
additional information. Pin Change interrupts, on the other hand, can be assigned to most pins, but
|
|
these interrupts are shared in groups of pins (call "ports") and when the interrupts are triggered
|
|
they call one of 3 possible ISRs. This program makes use of PCINT0_vect and the interrupt is set to
|
|
pin 8. The tests were done on an Arduino UNO and Arduino Micro using this pin
|
|
|
|
Sometimes the INA devices will do a soft/hard reset on voltage spikes (despite using decoupling
|
|
capacitors) and since the "PCINT0_vect" is called only when the ALERT pin is pulled low and the
|
|
default mode of the INA226 upon reset is "off, this would result in the program never collecting
|
|
statistics. For this reason the TIMER1 is used as a watchdog timer, triggering an interrupt every
|
|
second. If no measurements are detected then the INA226 is manually reset and processing continues.
|
|
|
|
GNU General Public License 3
|
|
============================
|
|
This program is free software: you can redistribute it and/or modify it under the terms of the GNU
|
|
General Public License as published by the Free Software Foundation, either version 3 of the
|
|
License, or (at your option) any later version. This program is distributed in the hope that it
|
|
will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should
|
|
have received a copy of the GNU General Public License along with this program (see
|
|
https://github.com/Zanduino/INA/blob/master/LICENSE). If not, see
|
|
<http://www.gnu.org/licenses/>.
|
|
|
|
Vers. Date Developer Comments
|
|
====== ========== ========== ==============================================================
|
|
1.0.1 2020-06-30 SV-Zanshin Issue #58 - clang-formatted document
|
|
1.0.0 2018-10-13 SV-Zanshin Ready for publishing
|
|
1.0.0 2018-10-03 SV-Zanshin Cloned and adapted example
|
|
*/
|
|
#include <INA.h> // INA Library
|
|
|
|
#include "MB85_FRAM.h" // I2C FRAM Library
|
|
/**************************************************************************************************
|
|
** Declare program Constants **
|
|
**************************************************************************************************/
|
|
const uint8_t INA_ALERT_PIN = 8; // Pin 8.
|
|
const uint8_t GREEN_LED_PIN = 13; // Green LED (standard location)
|
|
const uint32_t SERIAL_SPEED = 115200; // Use fast serial speed
|
|
const uint16_t ARRAY_BYTES = 1200; // Bytes in data array
|
|
/**************************************************************************************************
|
|
** Declare global variables, structures and instantiate classes **
|
|
**************************************************************************************************/
|
|
uint8_t deviceNumber = UINT8_MAX; // Device Number to use in example
|
|
volatile uint64_t sumBusRaw = 0; // Sum of bus raw values
|
|
volatile int64_t sumShuntRaw = 0; // Sum of shunt raw values
|
|
volatile uint8_t readings = 0; // Number of measurements taken
|
|
uint8_t chips_detected = 0; // Number of I2C FRAM chips detected
|
|
volatile uint32_t framIndex = 0; // Index to the next free position
|
|
INA_Class INA; // INA class instantiation
|
|
MB85_FRAM_Class FRAM; // FRAM Memory class instantiation
|
|
|
|
void writeNibble(uint8_t dataArray[], const uint16_t nibblePos, const uint8_t nibbleData) {
|
|
/************************************************************************************************
|
|
** Method "writeNibble()" will write the LSB 4 bits of "nibbleData" to the "dataArray" nibble **
|
|
** offset "nibblePos", each index position is 4 bits. **
|
|
************************************************************************************************/
|
|
uint8_t writeByte = *(dataArray + (nibblePos / 2)); // Read the correct byte and select
|
|
if (nibblePos & 1) { // whether the LSB or MSB is to be set
|
|
writeByte = (writeByte & 0xF0) | (nibbleData & 0xF); // Keep MSB & set the LSB to value
|
|
} else {
|
|
writeByte = (nibbleData << 4) | (writeByte & 0xF); // Keep LSB & set the MSB to value
|
|
} // of if-then-else nibblePos is odd
|
|
*(dataArray + (nibblePos / 2)) = writeByte; // Write the new value to array
|
|
} // of method "writeNibble()"
|
|
uint8_t readNibble(uint8_t dataArray[], const uint16_t nibblePos) {
|
|
/************************************************************************************************
|
|
** Method "readNibble()" will read the nibble addressed by "nibblePos" into the write the 4 **
|
|
** LSB bits of the return value. Each index position of the virtual array is 4 bits. **
|
|
************************************************************************************************/
|
|
uint8_t returnVal = *(dataArray + (nibblePos / 2)); // Read the correct byte and select
|
|
if (nibblePos & 1) { // whether the LSB or MSB is to be returned
|
|
returnVal = returnVal & 0xF; // Use the 4 LSB bits
|
|
} else {
|
|
returnVal = returnVal >> 4; // Use the 4 MSB bits
|
|
} // of if-then-else nibblePos is odd
|
|
return returnVal; // Return the computed nibble
|
|
} // of method "readNibble()"
|
|
ISR(PCINT0_vect) {
|
|
/************************************************************************************************
|
|
** Declare interrupt service routine for the pin-change interrupt on pin 8 which is set in the **
|
|
** setup() method **
|
|
************************************************************************************************/
|
|
static uint16_t tempsumBusRaw; // Declare as static to only init 1
|
|
static int16_t tempsumShuntRaw; // Declare as static to only init 1
|
|
*digitalPinToPCMSK(INA_ALERT_PIN) &= ~bit(digitalPinToPCMSKbit(INA_ALERT_PIN)); // Disable PCMSK
|
|
PCICR &= ~bit(digitalPinToPCICRbit(INA_ALERT_PIN)); // disable interrupt for the group
|
|
digitalWrite(GREEN_LED_PIN, !digitalRead(GREEN_LED_PIN)); // Toggle LED to show we are working
|
|
sei(); // Enable interrupts for I2C calls
|
|
tempsumBusRaw = INA.getBusRaw(deviceNumber); // Read the current value into temp
|
|
tempsumShuntRaw = INA.getShuntRaw(deviceNumber); // Read the current value into temp
|
|
INA.waitForConversion(deviceNumber); // Resets interrupt flag and start
|
|
cli(); // Disable interrupts
|
|
sumBusRaw += tempsumBusRaw; // copy value while ints disabled
|
|
sumShuntRaw += tempsumShuntRaw; // copy value while ints disabled
|
|
readings++; // Increment the number of readings
|
|
*digitalPinToPCMSK(INA_ALERT_PIN) |= bit(digitalPinToPCMSKbit(INA_ALERT_PIN)); // Enable PCMSK
|
|
PCIFR |= bit(digitalPinToPCICRbit(INA_ALERT_PIN)); // clear any outstanding interrupt
|
|
PCICR |= bit(digitalPinToPCICRbit(INA_ALERT_PIN)); // enable interrupt for the group
|
|
} // of ISR handler for INT0 group of pins
|
|
void writeDataToArray(uint8_t dataArray[], uint16_t &nibbleIndex, const int16_t deltaData) {
|
|
/************************************************************************************************
|
|
** Function "writeDataToArray()" writes 4LSB from "nibbleData" to the "nibbleIndex" nibble in **
|
|
*"dataArray". A Huffmann-like encoding with variable length is used to write the delta values **
|
|
** to the appropriate array. Each array "index" element is one nibble (4 bits) and the MSB **
|
|
** characters of the MSB nibble denote the record type. If the MSB is "B0" then it is a **
|
|
** 1-nibble long value, if the 2 MSB are "B10" then it is a 2 nibble value, if "B110" then 3 **
|
|
** nibbles, "B1110" denotes 4 and "B1111" denotes 5 nibbles. See the table below: **
|
|
** **
|
|
** 24-Bit representation Data Bits Value Range **
|
|
** ===================== ============ =============== **
|
|
** ----------------0xxx 3 bits data -4 to 3 **
|
|
** ------------10xxxxxx 6 bits data -16 to 15 **
|
|
** --------110xxxxxxxxx 9 bits data -256 to 255 **
|
|
** ----1110xxxxxxxxxxxx 12 bits data -2048 to 2047 **
|
|
** 1111xxxxxxxxxxxxxxxx 16 bits data -32768 to 32767 **
|
|
************************************************************************************************/
|
|
|
|
if (deltaData >= -4 && deltaData <= 3) // 1N, format 0xxx
|
|
{
|
|
writeNibble(dataArray, nibbleIndex++, deltaData & B111); // Write 1N to array
|
|
} else {
|
|
if (deltaData >= -16 && deltaData <= 15) // 2N, format 10xxxxxx
|
|
{
|
|
writeNibble(dataArray, nibbleIndex++, ((deltaData >> 4) & B11) | B1000); // write MSB
|
|
writeNibble(dataArray, nibbleIndex++, deltaData); // write LSB
|
|
} else {
|
|
if (deltaData >= -256 && deltaData <= 255) // 3N, format 110xxxxxxxxx
|
|
{
|
|
writeNibble(dataArray, nibbleIndex++, ((deltaData >> 8) & 1) | B1100); // Set 3MSB 9th bit
|
|
writeNibble(dataArray, nibbleIndex++, deltaData >> 4 & B1111); // write 4 MSB bits byte 1
|
|
writeNibble(dataArray, nibbleIndex++, deltaData); // write 4 LSB bits byte 1
|
|
} else {
|
|
if (deltaData >= -2048 && deltaData <= 2047) // 4N, format 1110xxxxxxxxxxxx
|
|
{
|
|
writeNibble(dataArray, nibbleIndex++, B1110); // Header nibble
|
|
writeNibble(dataArray, nibbleIndex++, deltaData >> 8); // next nibble
|
|
writeNibble(dataArray, nibbleIndex++, deltaData >> 4); // next nibble
|
|
writeNibble(dataArray, nibbleIndex++, deltaData); // LSB nibble
|
|
} else { // 5N, fmt 1111xxxxxxxxxxxxxxxx
|
|
writeNibble(dataArray, nibbleIndex++, B1111); // Header nibble
|
|
writeNibble(dataArray, nibbleIndex++, deltaData >> 12); // MSB nibble
|
|
writeNibble(dataArray, nibbleIndex++, deltaData >> 8); // next nibble
|
|
writeNibble(dataArray, nibbleIndex++, deltaData >> 4); // next nibble
|
|
writeNibble(dataArray, nibbleIndex++, deltaData); // LSB nibble
|
|
} // if-then-else value fits in 4 or 5 nibbles
|
|
} // if-then-else value fits in 3 nibbles
|
|
} // if-then-else value fits in 2 nibbles
|
|
} // if-then-else value fits in 1 nibble
|
|
} // of method "WriteDataToArray()"
|
|
int16_t readDataFromArray(uint8_t dataArray[], uint16_t &nibbleIndex) {
|
|
/************************************************************************************************
|
|
** Function "readDataToArray()" returns a 2-Byte signed integer from "dataArray" starting at **
|
|
** "nibbleIndex" and expanding the Array's internal Huffmann-encoding values. See the descrip- **
|
|
** tion of writeDataToArray() for details **
|
|
************************************************************************************************/
|
|
|
|
int16_t outValue = 0; // Declare return variable
|
|
uint8_t controlBits = readNibble(dataArray, nibbleIndex++); // Read the header nibble
|
|
if (controlBits >> 3 == 0) // ----------------0xxx 3 bits data - 4 to 3
|
|
{
|
|
outValue = controlBits & B111; // mask High Bit
|
|
if (outValue >> 2 & B1) { outValue |= 0xFFF8; } // If it is a negative number
|
|
} else {
|
|
if (controlBits >> 2 == B10) // ------------10xxxxxx 6 bits data - 16 to 15
|
|
{
|
|
outValue = (controlBits & B11) << 4; // mask 2 High Bits
|
|
outValue |= readNibble(dataArray, nibbleIndex++); // move in 4 LSB
|
|
if (outValue >> 5 & B1) { outValue |= 0xFFE0; } // If it is a negative number
|
|
} else {
|
|
if (controlBits >> 1 == B110) // --------110xxxxxxxxx 9 bits data - 256 to 255
|
|
{
|
|
outValue = (controlBits & B1) << 8; // mask 2 High Bits
|
|
outValue |= readNibble(dataArray, nibbleIndex++) << 4; // move in 4 middle bits
|
|
outValue |= readNibble(dataArray, nibbleIndex++); // move in 4 LSB
|
|
if (outValue >> 8 & B1) { outValue |= 0xFE00; } // If it is a negative number
|
|
} else {
|
|
if (controlBits == B1110) // ----1110xxxxxxxxxxxx 12 bits data - 2048 to 2047
|
|
{
|
|
outValue = readNibble(dataArray, nibbleIndex++) << 8; // move in 4 high bits
|
|
outValue |= readNibble(dataArray, nibbleIndex++) << 4; // move in 4 middle bits
|
|
outValue |= readNibble(dataArray, nibbleIndex++); // move in 4 low bits
|
|
if (outValue >> 11 & B1) { outValue |= 0xF000; } // If it is a negative number
|
|
} else {
|
|
if (controlBits == B1111) // 1111xxxxxxxxxxxxxxxx 16 bits data - 16384 to 16383
|
|
{
|
|
outValue = readNibble(dataArray, nibbleIndex++) << 12; // move in 4 high bits
|
|
outValue |= readNibble(dataArray, nibbleIndex++) << 8; // move in 4 middle bits
|
|
outValue |= readNibble(dataArray, nibbleIndex++) << 4; // move in 4 middle bits
|
|
outValue |= readNibble(dataArray, nibbleIndex++); // move in 4 low bits
|
|
} // if-then 5 nibbles
|
|
} // if-then-else 4 nibbles
|
|
} // if-then-else 3 nibbles
|
|
} // if-then-else 2 nibbles
|
|
} // if-then-else 1 nibble
|
|
return (outValue);
|
|
} // of method "readDataFromArray()"
|
|
|
|
ISR(TIMER1_COMPA_vect) {
|
|
/**********************************************************************************************
|
|
** Declare interrupt service routine for TIMER1, which is set to trigger once every second **
|
|
**********************************************************************************************/
|
|
static int16_t deltaBus, deltaShunt; // Difference value from last
|
|
static uint16_t arrayNibbleIndex = 0; // Array index in Nibbles
|
|
static int16_t lastBusRaw = 0; // Value from last reading
|
|
static int16_t lastShuntRaw = 0; // Value from last reading
|
|
static int16_t baseBusRaw = 0; // Base value for delta readings
|
|
static int16_t baseShuntRaw = 0; // Base value for delta readings
|
|
static uint16_t arrayReadings = 0; // Number of readings in array
|
|
static uint8_t dataArray[ARRAY_BYTES]; // Array for bus and shunt readings
|
|
if (arrayNibbleIndex == 0 && millis() < 3000) { // Skip first 3 seconds
|
|
baseBusRaw = (int16_t)(sumBusRaw / readings); // after startup to allow settings
|
|
lastBusRaw = baseBusRaw; // to settle
|
|
baseShuntRaw = (int16_t)(sumShuntRaw / readings);
|
|
lastShuntRaw = baseShuntRaw;
|
|
readings = 0; // then skip readings to let the
|
|
sumBusRaw = 0; // sensor settle down
|
|
sumShuntRaw = 0; // Reset values
|
|
return;
|
|
} // of if-then first second after startup
|
|
deltaBus = ((int16_t)(sumBusRaw / readings) - lastBusRaw); // Compute the delta bus
|
|
deltaShunt = ((int16_t)(sumShuntRaw / readings) - lastShuntRaw); // Compute the delta shunt
|
|
writeDataToArray(dataArray, arrayNibbleIndex, deltaBus); // Add bus reading to array
|
|
writeDataToArray(dataArray, arrayNibbleIndex, deltaShunt); // Add shunt reading to array
|
|
arrayReadings++; // increment the counter
|
|
lastBusRaw = sumBusRaw / readings; // Reset values
|
|
lastShuntRaw = sumShuntRaw / readings; // Reset values
|
|
readings = 0; // Reset values
|
|
sumBusRaw = 0; // Reset values
|
|
sumShuntRaw = 0; // Reset values
|
|
/*****************************************************************************************************************
|
|
** Once the array could fill up on the next reading (2x max reading of 5 nibbles) then it is
|
|
*time to flush the **
|
|
** the accumulated readings. **
|
|
*****************************************************************************************************************/
|
|
if ((arrayNibbleIndex + 10) / 2 >= ARRAY_BYTES) // //
|
|
{ // //
|
|
int16_t busValue = 0; // Contains current bus value //
|
|
int16_t shuntValue = 0; // Contains current shunt value //
|
|
uint16_t workNibbleIndex = 0; // Index into array for reading //
|
|
/***************************************************************************************************************
|
|
** If there is a FRAM memory board attached, then copy the array contents to it **
|
|
***************************************************************************************************************/
|
|
if (chips_detected > 0) // Only execute if there is memory //
|
|
{ // //
|
|
if ((framIndex + sizeof(dataArray) <
|
|
FRAM.totalBytes())) // Only write when space available //
|
|
{ // //
|
|
cli(); // Enable interrupts temporarily //
|
|
Serial.print(millis() / 1000 / 60); // //
|
|
Serial.print(" "); // //
|
|
Serial.print(F("Writing ")); // //
|
|
Serial.print(sizeof(dataArray)); // //
|
|
Serial.print(" Bytes to memory @"); // //
|
|
Serial.print(framIndex); // //
|
|
Serial.print(".\n"); // //
|
|
sei(); // Disable interrupts again //
|
|
FRAM.write(framIndex, dataArray); // Write the whole array to FRAM //
|
|
framIndex += sizeof(dataArray); // set index to new location //
|
|
} // of if-then there is space in the EEPROM // //
|
|
} // of if-then we have at least one EEPROM attached to the I2C bus // //
|
|
for (uint16_t readingNo = 1; readingNo <= arrayReadings;
|
|
readingNo++) // Process every reading in array //
|
|
{ // //
|
|
busValue = readDataFromArray(dataArray, workNibbleIndex); // Get next bus value from array //
|
|
baseBusRaw += busValue; // apply delta value to bus base //
|
|
shuntValue =
|
|
readDataFromArray(dataArray, workNibbleIndex); // Get shunt next value from array //
|
|
baseShuntRaw += shuntValue; // apply delta value to shunt base //
|
|
/*************************************************************************************************************
|
|
** Insert code here to save data to static RAM or to a SD-Card or elsewhere **
|
|
*************************************************************************************************************/
|
|
|
|
cli(); // Enable interrupts temporarily //
|
|
Serial.print(millis() / 1000);
|
|
Serial.print(" ");
|
|
Serial.print(readingNo);
|
|
Serial.print(" ");
|
|
Serial.print(baseBusRaw * 0.00125, 4);
|
|
Serial.print("V ");
|
|
Serial.print(0.0025 * baseShuntRaw);
|
|
Serial.println("mA");
|
|
sei(); // Disable interrupts again //
|
|
|
|
} // of for-next each array reading // //
|
|
arrayNibbleIndex = 0; // reset //
|
|
arrayReadings = 0; // reset //
|
|
} // of if-then the internal array is full // //
|
|
} // of ISR "TIMER1_COMPA_vect" // //
|
|
|
|
/*******************************************************************************************************************
|
|
** Method Setup(). This is an Arduino IDE method which is called first upon initial boot or
|
|
*restart. It is only **
|
|
** called one time and all of the variables and other initialization calls are done here prior to
|
|
*entering the **
|
|
** main loop for data measurement. **
|
|
*******************************************************************************************************************/
|
|
void setup() // //
|
|
{ // //
|
|
pinMode(GREEN_LED_PIN, OUTPUT); // Define the green LED as an output//
|
|
digitalWrite(GREEN_LED_PIN, true); // Turn on the LED //
|
|
pinMode(INA_ALERT_PIN, INPUT_PULLUP); // Declare pin with pull-up resistor//
|
|
*digitalPinToPCMSK(INA_ALERT_PIN) |=
|
|
bit(digitalPinToPCMSKbit(INA_ALERT_PIN)); // Enable PCMSK pin //
|
|
PCIFR |= bit(digitalPinToPCICRbit(INA_ALERT_PIN)); // clear any outstanding interrupt //
|
|
PCICR |= bit(digitalPinToPCICRbit(INA_ALERT_PIN)); // enable interrupt for the group //
|
|
Serial.begin(SERIAL_SPEED); // Start serial communications //
|
|
#ifdef __AVR_ATmega32U4__ // If this is a 32U4 processor, //
|
|
delay(2000); // wait 3 seconds for serial port //
|
|
#endif // interface to initialize //
|
|
Serial.print(
|
|
F("\n\nINA Data Logging with interrupts V1.0.3\n")); // Display program information //
|
|
uint8_t devicesFound = 0; // Number of INA2xx found on I2C //
|
|
while (deviceNumber == UINT8_MAX) // Loop until we find devices //
|
|
{ // //
|
|
devicesFound = INA.begin(1, 100000); // ±1Amps maximum for 0.1Ω resistor //
|
|
for (uint8_t i = 0; i < devicesFound; i++) // the first INA226 device found //
|
|
{ // Change "INA226" to "INA260" or //
|
|
// whichever INA2xx to measure //
|
|
if (strcmp(INA.getDeviceName(i), "INA226") == 0) // Set deviceNumber appropriately //
|
|
{ // //
|
|
deviceNumber = i; // //
|
|
INA.reset(deviceNumber); // Reset device to default settings //
|
|
break; // //
|
|
} // of if-then we have found an INA226 // //
|
|
} // of for-next loop through all devices found // //
|
|
if (deviceNumber == UINT8_MAX) // Try again if no device found //
|
|
{ // //
|
|
Serial.print(F("No INA226 found. Waiting 5s.\n")); // //
|
|
delay(5000); // //
|
|
} // of if-then no INA226 found // //
|
|
} // of if-then no device found // //
|
|
Serial.print(F("Found INA226 at device number ")); // //
|
|
Serial.println(deviceNumber); // //
|
|
Serial.println(); // //
|
|
INA.setAveraging(64, deviceNumber); // Average each reading 64 times //
|
|
INA.setAveraging(8, deviceNumber); // Average each reading 4 times //
|
|
INA.setBusConversion(82440, deviceNumber); // Maximum conversion time 8.244ms //
|
|
INA.setShuntConversion(82440, deviceNumber); // Maximum conversion time 8.244ms //
|
|
INA.setMode(INA_MODE_CONTINUOUS_BOTH, deviceNumber); // Bus/shunt measured continuously //
|
|
INA.AlertOnConversion(true, deviceNumber); // Make alert pin go low on finish //
|
|
chips_detected = FRAM.begin(); // return number of memories //
|
|
if (chips_detected > 0) { // //
|
|
Serial.print(F("Found ")); // //
|
|
Serial.print(chips_detected); // //
|
|
Serial.print(F(" FRAM with a total of ")); // //
|
|
uint32_t totalMemory = 0; // //
|
|
for (uint8_t i = 0; i < chips_detected; i++) { // //
|
|
totalMemory += FRAM.memSize(i); // Add memory of chip to total //
|
|
} // of for-next each memory // //
|
|
Serial.print(totalMemory / 1024); // //
|
|
Serial.println(F("KB memory.")); // //
|
|
} // if-then we have found a FRAM memory // //
|
|
cli(); // disable interrupts while setting //
|
|
TCCR1A = 0; // TCCR1A register reset //
|
|
TCCR1B = 0; // TCCR1B register reset //
|
|
TCNT1 = 0; // initialize counter //
|
|
OCR1A = 15624; // ((16*10^6) / (1*1024)) - 1 //
|
|
TCCR1B |= (1 << WGM12); // Enable CTC mode //
|
|
TCCR1B |= (1 << CS12) | (1 << CS10); // CS10 & CS12 for 1024 prescaler //
|
|
TIMSK1 |= (1 << OCIE1A); // Enable timer compare interrupt //
|
|
sei(); // re-enable interrupts //
|
|
} // of method setup() // //
|
|
|
|
/*******************************************************************************************************************
|
|
** This is the main program for the Arduino IDE, it is called in an infinite loop. The INA226
|
|
*measurements are **
|
|
** triggered by the interrupt handler each time a conversion is ready, and another interrupt is
|
|
*triggered every **
|
|
** second to store the collected readings. Thus the main program is free to do other tasks. **
|
|
*******************************************************************************************************************/
|
|
void loop() // //
|
|
{ // //
|
|
delay(10000);
|
|
} // of method loop //----------------------------------//
|