mirror of
https://github.com/RobTillaart/Arduino.git
synced 2024-10-03 18:09:02 -04:00
437 lines
8.5 KiB
C++
437 lines
8.5 KiB
C++
//
|
|
// FILE: DS2438.cpp
|
|
// AUTHOR: Rob Tillaart
|
|
// VERSION: 0.1.1
|
|
// DATE: 2023-07-28
|
|
// PURPOSE: Arduino Library for DS2438 battery monitor
|
|
// URL: https://github.com/RobTillaart/DS2438
|
|
|
|
|
|
#include "DS2438.h"
|
|
|
|
|
|
// OneWire commands
|
|
#define DS2438_READ_TEMPERATURE 0x44
|
|
#define DS2438_READ_VOLTAGE 0xB4
|
|
|
|
#define DS2438_RECALL_SCRATCH 0xB8
|
|
#define DS2438_READ_SCRATCH 0xBE
|
|
#define DS2438_WRITE_SCRATCH 0x4E
|
|
#define DS2438_COPY_SCRATCH 0x48
|
|
|
|
|
|
#define DS2438_CONVERSION_DELAY 10
|
|
|
|
// bits configuration register
|
|
#define DS2438_CFG_IAD 0
|
|
#define DS2438_CFG_CA 1
|
|
#define DS2438_CFG_EE 2
|
|
#define DS2438_CFG_AD 3
|
|
|
|
|
|
DS2438::DS2438(OneWire * ow)
|
|
{
|
|
_oneWire = ow;
|
|
// no sensor found yet
|
|
_addressFound = false;
|
|
|
|
// no data read yet
|
|
_temperature = DS2438_INVALID;
|
|
_vad = DS2438_INVALID;
|
|
_vdd = DS2438_INVALID;
|
|
_current = DS2438_INVALID;
|
|
_inverseR = DS2438_INVALID;
|
|
}
|
|
|
|
|
|
bool DS2438::begin(uint8_t retries)
|
|
{
|
|
return isConnected(retries);
|
|
}
|
|
|
|
|
|
bool DS2438::isConnected(uint8_t retries)
|
|
{
|
|
if (_oneWire == NULL) return false;
|
|
_addressFound = false;
|
|
for (uint8_t rtr = retries; (rtr > 0) && (_addressFound == false); rtr--)
|
|
{
|
|
_oneWire->reset();
|
|
_oneWire->reset_search();
|
|
_address[0] = 0x00;
|
|
_oneWire->search(_address);
|
|
_addressFound = (_address[0] != 0x00) && (_oneWire->crc8(_address, 7) == _address[7]);
|
|
}
|
|
return _addressFound;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////
|
|
//
|
|
// TEMPERATURE
|
|
//
|
|
float DS2438::readTemperature()
|
|
{
|
|
// requestTemperature()
|
|
_oneWire->reset();
|
|
_oneWire->select(_address);
|
|
_oneWire->write(DS2438_READ_TEMPERATURE, 0);
|
|
|
|
// async version should return here
|
|
delay(DS2438_CONVERSION_DELAY);
|
|
readScratchPad(0);
|
|
|
|
// sign MSB LSB step >> 3
|
|
_temperature = (int(_scratchPad[2]) * 256 + _scratchPad[1]) * 0.03125 * 0.125;
|
|
|
|
return _temperature;
|
|
}
|
|
|
|
|
|
float DS2438::getTemperature()
|
|
{
|
|
return _temperature;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////
|
|
//
|
|
// VOLTAGE
|
|
//
|
|
float DS2438::readVDD()
|
|
{
|
|
setConfigBit(3);
|
|
|
|
// requestVoltage
|
|
_oneWire->reset();
|
|
_oneWire->select(_address);
|
|
_oneWire->write(DS2438_READ_VOLTAGE, 0);
|
|
delay(DS2438_CONVERSION_DELAY);
|
|
readScratchPad(0);
|
|
|
|
// 10 mV resolution
|
|
_vdd = ((_scratchPad[4] & 0x03) * 256 + _scratchPad[3]) * 0.01;
|
|
|
|
return _vdd;
|
|
}
|
|
|
|
|
|
float DS2438::getVDD()
|
|
{
|
|
return _vdd;
|
|
}
|
|
|
|
|
|
float DS2438::readVAD()
|
|
{
|
|
clearConfigBit(3);
|
|
|
|
// requestVoltage
|
|
_oneWire->reset();
|
|
_oneWire->select(_address);
|
|
_oneWire->write(DS2438_READ_VOLTAGE, 0);
|
|
delay(DS2438_CONVERSION_DELAY);
|
|
readScratchPad(0);
|
|
|
|
// 10 mV resolution
|
|
_vad = ((_scratchPad[4] & 0x03) * 256 + _scratchPad[3]) * 0.01;
|
|
|
|
return _vad;
|
|
}
|
|
|
|
|
|
float DS2438::getVAD()
|
|
{
|
|
return _vad;
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////
|
|
//
|
|
// CURRENT
|
|
//
|
|
void DS2438::setResistor(float resistor)
|
|
{
|
|
_inverseR = 1.0 / (4096.0 * resistor);
|
|
}
|
|
|
|
|
|
void DS2438::enableCurrentMeasurement()
|
|
{
|
|
// The DS2438 will only perform current A/D measurements
|
|
// if the IAD bit is set to “1” in the status/Configuration Register.
|
|
// The current A/D measures at a rate of 36.41 times per second, or once every 27.46 ms.
|
|
readScratchPad(0);
|
|
if ((_scratchPad[0] & 0x01) == 0x01) return; // already 1
|
|
_scratchPad[0] |= 0x01;
|
|
writeScratchPad(0);
|
|
}
|
|
|
|
|
|
void DS2438::disableCurrentMeasurement()
|
|
{
|
|
readScratchPad(0);
|
|
if ((_scratchPad[0] & 0x01) == 0x00) return; // already 0
|
|
_scratchPad[0] &= ~0x01;
|
|
writeScratchPad(0);
|
|
}
|
|
|
|
|
|
float DS2438::readCurrent()
|
|
{
|
|
readScratchPad(0);
|
|
int voltageSense = (int(_scratchPad[6]) * 256 + _scratchPad[5]);
|
|
_current = voltageSense * _inverseR; // I = V / (4096 * R)
|
|
return _current;
|
|
}
|
|
|
|
|
|
float DS2438::getCurrent()
|
|
{
|
|
return _current;
|
|
}
|
|
|
|
|
|
void DS2438::writeCurrentOffset(int value)
|
|
{
|
|
value *= 8;
|
|
readScratchPad(1);
|
|
_scratchPad[6] = value / 8;
|
|
_scratchPad[5] = value % 8;
|
|
writeScratchPad(1);
|
|
}
|
|
|
|
|
|
int DS2438::readCurrentOffset()
|
|
{
|
|
readScratchPad(1);
|
|
int offset = (int(_scratchPad[6]) * 256 + _scratchPad[5]);
|
|
return offset / 8;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////
|
|
//
|
|
// ICA + THRESHOLD
|
|
//
|
|
float DS2438::readRemaining()
|
|
{
|
|
readScratchPad(1);
|
|
// factor 2 from optimization
|
|
float remaining = _scratchPad[4] * _inverseR * (2 * 0.4882); // mVhr
|
|
return remaining;
|
|
}
|
|
|
|
|
|
void DS2438::writeThreshold(uint8_t value)
|
|
{
|
|
clearConfigBit(0);
|
|
readScratchPad(0);
|
|
_scratchPad[7] = value & 0xC0; // zero lower 6 bits.
|
|
writeScratchPad(0);
|
|
setConfigBit(0);
|
|
}
|
|
|
|
|
|
uint8_t DS2438::readThreshold()
|
|
{
|
|
readScratchPad(0);
|
|
return _scratchPad[7];
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////
|
|
//
|
|
// TIME
|
|
//
|
|
void DS2438::writeElapsedTimeMeter(uint32_t seconds)
|
|
{
|
|
readScratchPad(1);
|
|
_scratchPad[0] = seconds & 0xFF;
|
|
seconds >>= 8;
|
|
_scratchPad[1] = seconds & 0xFF;
|
|
seconds >>= 8;
|
|
_scratchPad[2] = seconds & 0xFF;
|
|
seconds >>= 8;
|
|
_scratchPad[3] = seconds & 0xFF;
|
|
writeScratchPad(1);
|
|
}
|
|
|
|
|
|
uint32_t DS2438::readElapsedTimeMeter()
|
|
{
|
|
readScratchPad(1);
|
|
uint32_t seconds = _scratchPad[3];
|
|
seconds <<= 8;
|
|
seconds += _scratchPad[2];
|
|
seconds <<= 8;
|
|
seconds += _scratchPad[1];
|
|
seconds <<= 8;
|
|
seconds += _scratchPad[0];
|
|
return seconds;
|
|
}
|
|
|
|
uint32_t DS2438::readDisconnectTime()
|
|
{
|
|
readScratchPad(2);
|
|
uint32_t seconds = _scratchPad[3];
|
|
seconds <<= 8;
|
|
seconds += _scratchPad[2];
|
|
seconds <<= 8;
|
|
seconds += _scratchPad[1];
|
|
seconds <<= 8;
|
|
seconds += _scratchPad[0];
|
|
return seconds;
|
|
}
|
|
|
|
|
|
uint32_t DS2438::readEndOfChargeTime()
|
|
{
|
|
readScratchPad(2);
|
|
uint32_t seconds = _scratchPad[7];
|
|
seconds <<= 8;
|
|
seconds += _scratchPad[6];
|
|
seconds <<= 8;
|
|
seconds += _scratchPad[5];
|
|
seconds <<= 8;
|
|
seconds += _scratchPad[4];
|
|
return seconds;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////
|
|
//
|
|
// EEPROM
|
|
//
|
|
void DS2438::writeEEPROM(uint8_t address, uint8_t value)
|
|
{
|
|
if (address > 39) return; // 0..39
|
|
uint8_t page = address / 8;
|
|
uint8_t index = address % 8;
|
|
|
|
readScratchPad(page);
|
|
if (_scratchPad[index] == value) return; // no need to write.
|
|
_scratchPad[index] = value;
|
|
writeScratchPad(page);
|
|
}
|
|
|
|
|
|
uint8_t DS2438::readEEPROM(uint8_t address)
|
|
{
|
|
if (address > 39) return 0; // 0..39
|
|
uint8_t page = address / 8;
|
|
uint8_t index = address % 8;
|
|
|
|
readScratchPad(page);
|
|
return _scratchPad[index];
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////
|
|
//
|
|
// Charging + Discharging Current Accumulator
|
|
//
|
|
void DS2438::enableCCA()
|
|
{
|
|
readScratchPad(0);
|
|
if ((_scratchPad[0] & 0x02) == 0x02) return; // already 1
|
|
_scratchPad[0] |= 0x02;
|
|
writeScratchPad(0);
|
|
}
|
|
|
|
|
|
void DS2438::disableCCA()
|
|
{
|
|
readScratchPad(0);
|
|
if ((_scratchPad[0] & 0x02) != 0x02) return; // already 0
|
|
_scratchPad[0] &= ~0x02;
|
|
writeScratchPad(0);
|
|
}
|
|
|
|
|
|
float DS2438::readCCA()
|
|
{
|
|
readScratchPad(7);
|
|
uint16_t raw = (_scratchPad[5] * 256 + _scratchPad[4]);
|
|
return raw * 15.625;
|
|
}
|
|
|
|
|
|
float DS2438::readDCA()
|
|
{
|
|
readScratchPad(7);
|
|
uint16_t raw = (_scratchPad[7] * 256 + _scratchPad[6]);
|
|
return raw * 15.625;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////
|
|
//
|
|
// CONFIG REGISTER
|
|
//
|
|
void DS2438::setConfigBit(uint8_t bit)
|
|
{
|
|
uint8_t mask = (0x01 << bit);
|
|
readScratchPad(0);
|
|
if ((_scratchPad[0] & mask) == mask) return; // already 1
|
|
_scratchPad[0] |= mask;
|
|
writeScratchPad(0);
|
|
}
|
|
|
|
|
|
void DS2438::clearConfigBit(uint8_t bit)
|
|
{
|
|
uint8_t mask = (0x01 << bit);
|
|
readScratchPad(0);
|
|
if ((_scratchPad[0] & mask) == 0x00) return; // already 0
|
|
_scratchPad[0] &= ~mask;
|
|
writeScratchPad(0);
|
|
}
|
|
|
|
|
|
uint8_t DS2438::getConfigRegister()
|
|
{
|
|
readScratchPad(0);
|
|
return _scratchPad[0];
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////
|
|
//
|
|
// PRIVATE
|
|
//
|
|
void DS2438::readScratchPad(uint8_t page)
|
|
{
|
|
if (page > 7) return;
|
|
_oneWire->reset();
|
|
_oneWire->select(_address);
|
|
_oneWire->write(DS2438_RECALL_SCRATCH, 0);
|
|
_oneWire->write(page, 0);
|
|
_oneWire->reset();
|
|
_oneWire->select(_address);
|
|
_oneWire->write(DS2438_READ_SCRATCH, 0);
|
|
_oneWire->write(page, 0);
|
|
for (uint8_t i = 0; i < 8; i++) _scratchPad[i] = _oneWire->read();
|
|
// skip crc for now.
|
|
}
|
|
|
|
|
|
void DS2438::writeScratchPad(uint8_t page)
|
|
{
|
|
if (page > 7) return;
|
|
_oneWire->reset();
|
|
_oneWire->select(_address);
|
|
_oneWire->write(DS2438_WRITE_SCRATCH, 0);
|
|
_oneWire->write(page, 0);
|
|
for (uint8_t i = 0; i < 8; i++) _oneWire->write(_scratchPad[i], 0);
|
|
_oneWire->reset();
|
|
_oneWire->select(_address);
|
|
_oneWire->write(DS2438_COPY_SCRATCH, 0);
|
|
_oneWire->write(page, 0);
|
|
}
|
|
|
|
|
|
// -- END OF FILE --
|
|
|