📄 gas_mon.c
字号:
//_____________________________________________________________________________
//_____________________________________________________________________________
// gas_mon
//_____________________________________________________________________________
//_____________________________________________________________________________
//_____________________________________________________________________________
// DS2438 battery monitor IC one-wire bus communications
//
// If there is no gascell present, or there is any other type of open circuit
// in the gascell circuit, this will make the battery monitor produce random results
// including nonsense temperature measurements.
//
// DS2438 SRAM/EEPROM Memory function commands
// You can't read and write directly to the SRAM/EEPROM memory map.
// You can only directly read and alter the "shadow" or "scratch" page
// registers.
// However, the two high level commands gas_mon_readRegister(page, byte) and
// gas_mon_writeRegister(page, byte, data) overcome this limitation by carrying
// out all the low level scratch pad copying automatically.
//
// Exception: The data in the scratchpad of the status and threshold
// register will determine the operation of the device.
//
//
// Writing to the SRAM/EEPROM memory map:-
// To modify a memory location you have to "write" to the approproate scratch
// page and then "copy" that into SRAM/EEPROM.
//
// Reading from the SRAM/EEPROM memory map:-
// To read a memory location you have to "recall" that page to the appropriate
// scratch page and then "read" that scratch page.
//
// Do not add reset() at the end of any functions, it seems to mess things up.
//
// to do: update to read full serial number and checksum from monitor which
// is longer than a word and would provide a guaranteed unique ID.
// to do: include checksum as part of serial number. Then ID_NOT_FOUND can be any
// any number with a false checksum.
// to do: add functions for enabling/disabling various features
// to do: CRC checks
// to do: think about removing the disabling on interrupts
// to do: Add hardware timer check into while (!readBit());
// statements do if communication gets stuck, it does not stop the
// whole application from responding.
//
//_____________________________________________________________________________
// Includes
//_________
#include ".\gas_mon.h" // Prototypes and defines for this file
// Private Variables
//___________________
static float full_gas_mon_ICA; // the value of the ICA in mA when full
// Private Prototypes
//___________________
static int resetDQ(void);
static void write1(void);
static void write0(void);
static BOOL readBit(void);
static void writebyte(byte Data);
static byte readbyte(void);
static void gas_mon_set_threshold(byte th2, byte th1);
static float gas_mon_readVoltage(void);
static void gas_mon_changeToVad(void);
static void gas_mon_changeToVdd(void);
// Functions
//__________
//=============================================================================
// public
//=============================================================================
//_____________________________________________________________________________
// gas_mon_power_down
//_____________________________________________________________________________
// place gascell monitor in power down mode
//_____________________________________________________________________________
void gas_mon_power_down(void)
{
DQ_PULL_UP = NO_PULL_UP; // pullup for DQ line
DQ = LOW;
DQ_DIRECTION = OUTPUT;
DQ_EXTRA_PULL_UP = LOW;
DQ_EXTRA_PULL_UP_DIRECTION = OUTPUT;
}
//_____________________________________________________________________________
// gas_mon_power_up
//_____________________________________________________________________________
// Enable gascell monitor DQ line which powers it up and starts it operating
//_____________________________________________________________________________
void gas_mon_power_up(void)
{
DQ_PULL_UP = PULL_UP; // pullup for DQ line
DQ_DIRECTION = INPUT;
DQ_EXTRA_PULL_UP_DIRECTION = INPUT; // used for the pull up only
// remains input only. does not get switched
// high or low
}
//_____________________________________________________________________________
// gas_mon_initialise
//_____________________________________________________________________________
// Sets up the gascell monitor so that all it resources are available for use
// Loads ICA with max value.
//_____________________________________________________________________________
void gas_mon_initialise(void)
{
asm("fclr I"); // disable interrupts
gas_mon_power_up();
// configure status/config register
//---------------------------------
resetDQ(); // modify to check for presence pulse was received OK
writebyte(SKIP_ROM); // Skip ROM
writebyte(WRITE_SP); // write to a scratch pad
writebyte(0x00); // scratch pad 0
writebyte(0x0F); // change byte 0 (status/config register) so that
// all resources are turned on
resetDQ(); // only wanted to write that first byte, so reset now instead
// of writing next 7 bytes
writebyte(SKIP_ROM); // Skip ROM
writebyte(COPY_SP); // copy scratch pad to NV memory
writebyte(0x00); // scratch pad 0
while (!readBit()); // the monitor outputs all zeros on the DQ line
// until the NV writing is complete, at which
// time it outputs all ones.
gas_mon_set_threshold((byte)1, (byte)0); // ignore current accumulation up to +/- 4 LSB
gas_mon_load_ICA(); // Loads ICA with max value
asm("fset I"); // enable interrupts
}
//_____________________________________________________________________________
// gas_mon_setFullICA
//_____________________________________________________________________________
// Determine the value of a full ICA in mA. This is partially determined by the
// exact value of the gascell sense resistor.
// DOES NOT actually change the value of the ICA, only tells you what the value of
// it is when full.
//
// set_cal must be called after gascell monitor comms to establish ID and calibration
// values. However, part of the natural Initialisation would be gas_mon_setFullICA() which uses
// calibration information from set_cal. This is a catch 22, so to get around this
// the monitor is initialised without gas_mon_setFullICA(), then set_cal happens
// and then gas_mon_setFullICA() can be called.
//_____________________________________________________________________________
void gas_mon_setFullICA(void)
{
full_gas_mon_ICA = ((float)0xFF/(2048.0f*getGascellSenseResistor())*1000.0f); // convert to mAhr
}
//_____________________________________________________________________________
// gas_mon_getFullICA
//_____________________________________________________________________________
// Get the value of a full ICA in mA.
//_____________________________________________________________________________
float gas_mon_getFullICA(void)
{
return full_gas_mon_ICA;
}
//_____________________________________________________________________________
// gas_mon_calibrate_current
//_____________________________________________________________________________
// Calibrates current ADC for zero offset by adjusting offset register.
// Comments are based on page 6 of the data sheet, but the data sheet does not
// tell the whole story.
//
// The following process can be used to calibrate the current ADC:
// 1. Ensure no current flows through the sense resistor by turning the load
// and charge current off.
// 2. Wait until the voltage on the sense resistor decays to zero if using
// a low pass filer.
// 3. Disable the current ADC by setting the IAD bit in the
// Status/Configuration Register to 0
// 4. Write all zeroes to the Offset Register.
// 5. Enable the current ADC by setting the IAD bit in the
// Status/Configuration Register to 1
// 6. Wait for the first current conversion to give a valid result in the
// current register. 60ms should be more than enough.
// 7. Read the Current Register value.
// 8. Disable the current ADC by setting the IAD bit in the
// Status/Configuration Register to 0
// 9. Change the sign of the previously-read Current Register value by
// performing the two抯 complement
// and write the result to the Offset Register.
// 10. Enable the current ADC by setting the IAD bit in the
// Status/Configuration Register to 1
// 11. Wait for the first current conversion to give a valid result
// in the current register before reading it. 60ms should be more than enough.
//_____________________________________________________________________________
void gas_mon_calibrate_current(void)
{
word_union raw_current_register;
bool previous_gascell_state;
asm("fclr I"); // disable interrupts
// Ensure it is awake
// ------------------
gas_mon_power_up();
previous_gascell_state = HR_Gascell_Switch_IN; // save gascell switch state so it can be restored
// Turn load off
//-------------
gascell_off(); // turn off gascell and wait a few ms for V on Rsense to zero
// shouldn't really be part of gas monitor code but better
// than forgetting to turn it off before calling this function.
// wait for voltage across sense resistor to fall to zero
delay_ms(80); // wait for voltage on Rsense to decay to zero - filter effect
// Write all zeroes to the offset register to remove old offset
//-------------------------------------------------------------
// When writing to the Offset Register, current measurement MUST be disabled
// (IAD bit set to 0).
gas_mon_clearIAD();
gas_mon_writeRegister(1, 5, 0);
gas_mon_writeRegister(1, 6, 0);
// Read the Current Register value
//----------------------------------
// IAD bit set to 1 so current can be measured
gas_mon_setIAD(); // must be set here so current can be measured
// wait for current A/D to do first automatic current measurement cycle
delay_ms(80);
raw_current_register.asword = gas_mon_readCurrentRegisterword();
// Negate offset error
//----------------------
// Change the sign of the previously-read Current Register value by performing the two抯 complement
raw_current_register.asSignedInt = - raw_current_register.asSignedInt;
// Update offset Register
//-----------------------
// offset register is in a different format to current register
raw_current_register.asword = raw_current_register.asword << 3;
// When writing to the Offset Register, current measurement MUST be disabled
// (IAD bit set to 0).
gas_mon_clearIAD();
// write the result to the Offset Register.
gas_mon_writeRegister(1, 5, raw_current_register.asbytes.lowbyte); // write LSB
gas_mon_writeRegister(1, 6, raw_current_register.asbytes.highbyte); // write MSB
// IAD bit set to 1 so current can be measured
gas_mon_setIAD();
delay_ms(80); // wait for current A/D to do first automatic current measurement cycle
// after resetting the IAD bit to one. Waiting here means
// that the current register is valid for reading immediately at
// the return of this function.
// restore gascell switch state
HR_Gascell_Switch_IN = (previous_gascell_state)? (bool)ON:OFF;
asm("fset I"); // enable interrupts
}
//_____________________________________________________________________________
// gas_mon_load_ICA
//_____________________________________________________________________________
// Loads ICA with max value
// Note: The ICA counts DOWN when the gascell is discharging
// Writes over elapsed time registers
// to do: avoid writing over elapsed time registers.
//_____________________________________________________________________________
void gas_mon_load_ICA(void)
{
asm("fclr I"); // disable interrupts
resetDQ(); // modify to check for presence pulse was received OK
writebyte(SKIP_ROM); // Skip ROM
writebyte(WRITE_SP); // write to a scratch pad
writebyte(0x01); // scratch pad 1
writebyte(0x00); // write over elapsed time registers
writebyte(0x00);
writebyte(0x00);
writebyte(0x00);
writebyte(0xFF); // write max value into ICA for it to decrement when
// gascell is discharging
resetDQ(); // only wanted to write those first bytes, so reset now instead
// of writing next bytes
writebyte(SKIP_ROM); // Skip ROM
writebyte(COPY_SP); // copy scratch pad to NV memory
writebyte(0x01); // scratch pad 1
while (!readBit()); // the monitor outputs all zeros on the DQ line
// until the NV writing is complete, at which
// time it outputs all ones.
asm("fset I"); // enable interrupts
}
//_____________________________________________________________________________
// gas_mon_readROMserial
//_____________________________________________________________________________
// read serial number in ROM .
// The SN is in ROM, not in the SRAM/EEPROM memory map.
//_____________________________________________________________________________
word gas_mon_readROMserial(void)
{
word_union monitor_id = 0;
// to do: the id is 6 bytes, this only reads 2 of them at the moment
asm("fclr I"); // disable interrupts
resetDQ(); // modify to check for presence pulse was received OK
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -