using System;
using System.Threading;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using SWModules;
namespace HWModules
{
public sealed class LheBoard
{
private static LheBoard instance;
private static LhePio pio1, pio2;
private static SMBusController smbus;
///
/// Battery information array.
///
/// > 1, clock_rate);
smbus = new SMBusController(batt_i2c, 6);
BatteryBank = new BatteryState[6];
Thread.Sleep(200);
InitPIO();
UpdateBatteryBank();
}
///
/// Does a round robin collection of data from each battery in the
/// pack, collecting critical information and stores it in the object's
/// BatteryBank field.
///
/// true on success
///
public bool UpdateBatteryBank()
{
//Check Sequencer
var time_sleep = 0; //ms
var embus = EnergyMonitorHotel.Instance;
EnergyMonitorLTC2946.EM_VandI vandi;
for (var n = 0; n < 6; n++)
{
//POLL INPUTS FOR CHARGER STATUS
Thread.Sleep(time_sleep);
var charging_batt = CurrentBatteryUnderCharge;
if (charging_batt == -1)
{
EngrLogger.Comment("[ERROR] LHEBoard::UpdateBatteryPack Couldn't Poll PIO 0x20 for Charge Sequence");
vandi = embus.getVoltsAndMilliAmps();
return false;
}
if (charging_batt == n + 1)
{
//Debug.Print("[ERROR] LHEBoard::UpdateBatteryPack Can't check battery, it's charging");
BatteryBank[n].Charging = true;
continue;
}
Thread.Sleep(time_sleep);
//Debug.Print("[INFO] Selecting Battery: "+n);
//Switch on I2C to Battery
if (!SelectBatteryI2C(n + 1))
{
EngrLogger.Comment("[ERROR] LHEBoard::UpdateBatteryPack Couldn't Enable PIO 0x21 for Battery Update");
vandi = embus.getVoltsAndMilliAmps();
return false;
}
Thread.Sleep(time_sleep);
smbus.SetModeDefaults();
Thread.Sleep(time_sleep);
BatteryBank[n].Voltage = smbus.ReadVoltage();
Thread.Sleep(time_sleep);
BatteryBank[n].Current = (short) smbus.ReadCurrent();
Thread.Sleep(time_sleep);
BatteryBank[n].SerialNumber = smbus.ReadSerialNumber();
Thread.Sleep(time_sleep);
BatteryBank[n].RemainingCapacity = smbus.ReadRemainingCapacity();
Thread.Sleep(time_sleep);
BatteryBank[n].Temperature = smbus.ReadTemperature();
Thread.Sleep(time_sleep);
BatteryBank[n].BatteryStatus = smbus.ReadBatteryStatus();
Thread.Sleep(time_sleep);
BatteryBank[n].BatteryMode = smbus.ReadBatteryMode();
Thread.Sleep(time_sleep);
BatteryBank[n].Charging = false;
//Disable Battery Communication
if (!DisableBatteryI2C())
{
EngrLogger.Comment("[ERROR] LHEBoard::UpdateBatteryPack Couldn't Poll PIO 0x21 for Switch Disable");
vandi = embus.getVoltsAndMilliAmps();
//Debug.Print(vandi.ToString());
return false;
}
}
return true;
}
///
/// BatteryHealth returns an uint16 with bit flags irregularites
/// in Mode or Status flags:
/// bits [0..5]: 1 if battery's mode value is not 0x4000
/// bits [6..11]: 1 if battery's status is not 0x00C0 or 0x00E0
///
public ushort BatteryHealth
{
get
{
ushort health = 0;
for (var n = 0; n < 6; n++)
{
if (BatteryBank[n].BatteryMode != 0x4000)
health |= (ushort)(0x01 << n);
if (BatteryBank[n].BatteryStatus != 0x00E0 || BatteryBank[n].BatteryMode != 0x00C0)
health |= (ushort) (0x01 << (n + 6));
}
return health;
}
}
///
/// Print all battery data to the EngrLogger.
///
///
public void PrintBatteryData()
{
//Debug.Print("--------------------------------------------------------------");
for (var n = 0; n < 6; n++)
{
EngrLogger.Comment(BatteryBank[n].ToString());
}
}
///
/// Interrogate which battery is charging
///
/// 0-6 value of which battery is charging, 0 is no battery.
public int CurrentBatteryUnderCharge
{
get {
byte ret = 0;
if (!pio2.readRegister(LhePio.Command.InputPort, ref ret))
return -1;
return 0x07 & ret;
}
}
///
/// Interrogate if the Pump power is is not faulted
///
/// 1 for NOT FAULTED, 0 for FAULTED, and -1 for a bit read error.
public int isPumpPowerNotFaulted
{
get
{
byte ret = 0;
if (!pio2.readRegister(LhePio.Command.InputPort, ref ret))
return -1;
return ((0x08 & ret) == 0x08)?1:0;
}
}
///
/// This sets up the parallel IO chips on the LHE for the designed
/// IO on the LHE board. It also resets the sleep circuit, and important
/// step to make sure the system doesn't accidentally put itself to sleep
/// after returning from a sleep cycle.
///
private void InitPIO()
{
_currentPio1Output = DefaultPio1Output;
_currentPio2Output = DefaultPio2Output;
//RESETTING SLEEP FUNCTIONS ASSERTS CONFIGURATION MODES AS WELL
ResetSleepFunctions();
}
private const LhePio.RegOutput DefaultPio1Output = LhePio.RegOutput.O0 |
LhePio.RegOutput.O1 |
LhePio.RegOutput.O2 |
LhePio.RegOutput.O3 |
LhePio.RegOutput.O4 |
LhePio.RegOutput.O5;
//0b 0011 1111
private const LhePio.RegConfiguration DefaultPio1Config = (LhePio.RegConfiguration) 0x80; // P7 Set to input
private const LhePio.RegConfiguration DefaultPio2Config = (LhePio.RegConfiguration) 0xFF; // All inputs
private const LhePio.RegConfiguration SleepTimerPio1Config = 0; //All outputs
private const LhePio.RegConfiguration SleepTimerPio2Config = (LhePio.RegConfiguration) 0x1F; //P5-7 outputs
private const LhePio.RegOutput DefaultPio2Output = 0;
private LhePio.RegOutput _currentPio1Output;
private LhePio.RegOutput _currentPio2Output;
public static LheBoard Instance
{
get { return instance ?? (instance = new LheBoard()); }
}
///
/// Disable all analog switches that facilitate SMBus
/// communication with individual battery packs.
///
///
public bool DisableBatteryI2C()
{
var tempOutput = _currentPio1Output;
tempOutput |= LhePio.RegOutput.O0;
tempOutput |= LhePio.RegOutput.O1;
tempOutput |= LhePio.RegOutput.O2;
tempOutput |= LhePio.RegOutput.O3;
tempOutput |= LhePio.RegOutput.O4;
tempOutput |= LhePio.RegOutput.O5;
if (!pio1.setOutputs(tempOutput))
return false;
_currentPio1Output = tempOutput;
return true;
}
///
/// Use the PIO1 chip to single device communication.
///
///
/// true on success
public bool SelectBatteryI2C(int p_n)
{
//if (!DisableBatteryI2C())
//{
//return false;
//}
var tempOutput = _currentPio1Output;
//reset all outputs for battery select
tempOutput |= (LhePio.RegOutput) 0x3F;
switch (p_n)
{
case 1: //P1
tempOutput &= ~LhePio.RegOutput.O0;
break;
case 2:
tempOutput &= ~LhePio.RegOutput.O1;
break;
case 3:
tempOutput &= ~LhePio.RegOutput.O2;
break;
case 4:
tempOutput &= ~LhePio.RegOutput.O3;
break;
case 5:
tempOutput &= ~LhePio.RegOutput.O4;
break;
case 6:
tempOutput &= ~LhePio.RegOutput.O5;
break;
default:
return false;
}
// standard: tempOutput || all on 0x80 || all off 0xBF
if (!pio1.setOutputs(tempOutput))
return false;
_currentPio1Output = tempOutput;
return true;
}
///
/// Enables/Disables the Battery power supply to the Elmo
/// Twitter
///
/// true enables, false disables
/// true on success
public bool EnablePumpPower(bool enable)
{
var tempOutput = _currentPio1Output;
if (enable)
tempOutput |= LhePio.RegOutput.O6;
else
tempOutput &= ~LhePio.RegOutput.O6;
if (!pio1.setOutputs(tempOutput)) return false;
_currentPio1Output = tempOutput;
return true;
}
///
/// Loads the register held by the flip-flop with value
/// 0x0 to 0x7. This will sleep for 2^n-1 minutes.
///
/// Valid range, 0x0-0x7
/// true on success
public bool SetupSleep(byte n)
{
//Test n
if (n > 0x7) return false;
var tempPio1Output = _currentPio1Output;
var tempPio2Output = _currentPio2Output;
//Set SleepTrig to 0
tempPio1Output &= ~LhePio.RegOutput.O7;
if (!pio1.setOutputs(tempPio1Output)) return false;
_currentPio1Output = tempPio1Output;
//Set ABC to 0
tempPio2Output = DefaultPio2Output;
if (!pio2.setOutputs(tempPio2Output)) return false;
_currentPio2Output = tempPio2Output;
//Configure SleepTrig Outputs
if (!pio1.setPortDirections(SleepTimerPio1Config)) return false;
if (!pio2.setPortDirections(SleepTimerPio2Config)) return false;
//Set ABC
if ((n & 0x1) > 0x00)
tempPio2Output |= LhePio.RegOutput.O5;
if ((n & 0x2) > 0x00)
tempPio2Output |= LhePio.RegOutput.O6;
if ((n & 0x4) > 0x00)
tempPio2Output |= LhePio.RegOutput.O7;
if (!pio2.setOutputs(tempPio2Output)) return false;
_currentPio2Output = tempPio2Output;
return true;
}
///
/// Engages the sleep function of the LHE board for minutes, calls .
///
/// Valid range, 0x0-0x7
/// true on success
public bool Sleep(byte n)
{
if (!SetupSleep(n)) return false;
//Enable the sleep trigger
//Latch ABC via SleepTrig
var tempPio1Output = _currentPio1Output;
tempPio1Output |= LhePio.RegOutput.O7;
if (!pio1.setOutputs(tempPio1Output)) return false;
_currentPio1Output = tempPio1Output;
return true;
}
///
/// Conducts an output sequence of the PIO chips that resets the sleep functions
/// circuits on the LHE board.
///
///
public bool ResetSleepFunctions()
{
if (!Sleep(0)) return false;
// reset line configs
if (!pio1.setPortDirections(DefaultPio1Config)) return false;
return pio2.setPortDirections(DefaultPio2Config);
}
}
///
/// Command interface for the TI TCA6408APWR 8-bit I2C I/O expander
///
///
public class LhePio : I2CPortBase
{
public enum Command : byte
{
InputPort = 0x00,
OutputPort = 0x01,
PolarityInversion = 0x02,
Configuration = 0x03
}
public enum Direction
{
Input,
Output
}
[Flags]
public enum RegConfiguration : byte
{
C0 = 0x01,
C1 = 0x02,
C2 = 0x04,
C3 = 0x08,
C4 = 0x10,
C5 = 0x20,
C6 = 0x40,
C7 = 0x80,
}
[Flags]
public enum RegInput : byte
{
I0 = 0x01,
I1 = 0x02,
I2 = 0x04,
I3 = 0x08,
I4 = 0x10,
I5 = 0x20,
I6 = 0x40,
I7 = 0x80
}
[Flags]
public enum RegInversion : byte
{
N0 = 0x01,
N1 = 0x02,
N2 = 0x04,
N3 = 0x08,
N4 = 0x10,
N5 = 0x20,
N6 = 0x40,
N7 = 0x80,
}
[Flags]
public enum RegOutput : byte
{
O0 = 0x01,
O1 = 0x02,
O2 = 0x04,
O3 = 0x08,
O4 = 0x10,
O5 = 0x20,
O6 = 0x40,
O7 = 0x80,
}
private byte _configurationReg;
private byte _inputReg;
private byte _outputReg;
private byte _polarityReg;
private byte[] _readBytes;
private const int I2CTimeout = 500;
private I2CDevice.I2CTransaction[] _transactions;
private I2CDevice.I2CTransaction[] _write_transaction;
private byte[] _writeByte;
private byte[] _writeBytes;
public LhePio(I2CDevice.Configuration config, int slotNum) : base(config, slotNum)
{
_transactions = new I2CDevice.I2CTransaction[2];
_write_transaction = new I2CDevice.I2CTransaction[1];
_writeByte = new byte[1];
_writeBytes = new byte[2];
_readBytes = new byte[1];
}
public bool readRegister(Command command, ref byte value)
{
_writeByte[0] = (byte) command;
_transactions[0] = CreateWriteTransaction(_writeByte);
_transactions[1] = CreateReadTransaction(_readBytes);
if (Execute(_transactions, I2CTimeout) == 0)
{
//Debug.Print("[ERROR] Reading");
return false;
}
switch (command)
{
case Command.InputPort:
_inputReg = _readBytes[0];
break;
case Command.OutputPort:
_outputReg = _readBytes[0];
break;
case Command.PolarityInversion:
_polarityReg = _readBytes[0];
break;
case Command.Configuration:
_configurationReg = _readBytes[0];
break;
default:
throw new ArgumentOutOfRangeException("command");
}
value = _readBytes[0];
return true;
}
public bool setPortDirections(RegConfiguration configuration)
{
_writeBytes[0] = (byte) Command.Configuration;
_writeBytes[1] = (byte) configuration;
_write_transaction[0] = CreateWriteTransaction(_writeBytes);
if (Execute(_write_transaction, I2CTimeout) > 0) return true;
EngrLogger.Comment("[Error] LHEBoard::setPortDirections Writing Port Direction.");
return false;
}
public bool setOutputs(RegOutput outputs)
{
_writeBytes[0] = (byte) Command.OutputPort;
_writeBytes[1] = (byte) outputs;
_write_transaction[0] = CreateWriteTransaction(_writeBytes);
if (Execute(_write_transaction, I2CTimeout) > 0) return true;
EngrLogger.Comment("[ERROR] LHEBoard::setOutPuts Writing Outputs: "+_writeBytes[1].ToString("X"));
return false;
}
}
}