package i2c.max7311;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.pi4j.io.i2c.I2CBus;
import com.pi4j.io.i2c.I2CDevice;
import i2c.Util;
public class Max7311 {
private static final Logger logger = LoggerFactory.getLogger(Max7311.class);
private static final long MAX_RETRY_TIME = 3000l;
interface ThrowingTask {
void run() throws Exception;
}
/*
* Max7311 registers.
*/
private static final int InL = 0x0;
private static final int InH = 0x1;
private static final int OutL = 0x2;
private static final int OutH = 0x3;
private static final int PolInvL = 0x4;
private static final int PolInvH = 0x5;
private static final int DDR_L = 0x6;
private static final int DDR_H = 0x7;
private static final int TO_Reg = 0x8;
private I2CDevice device;
private byte address = -1;
private List<OutPin> output = new ArrayList<OutPin>();
private List<InPin> input = new ArrayList<InPin>();
public void init(I2CBus bus, byte address) throws Exception {
this.address = address;
for (byte i = 0; i < 8; i++) {
output.add(new OutPin(address, i));
input.add(new InPin(address, i));
}
device = bus.getDevice(address);
// low byte as output
device.write(DDR_L, (byte) 0x00);
Thread.sleep(100);
// high byte as input
device.write(DDR_H, (byte) 0xFF);
// in order to init inputs correctly, first read operation is needed
write();
read();
logger.debug("device at address {} initialized", device.getAddress());
}
private boolean runWithRetries(ThrowingTask t) throws Exception {
long start = System.currentTimeMillis();
while (true) {
try {
t.run();
Thread.sleep(10);
return true;
} catch (Exception e) {
if (System.currentTimeMillis() - start > MAX_RETRY_TIME) {
throw e;
}
}
}
}
/**
* Reads input pins and returns Pins where values changed.
*
*
* @return
* @throws IOException
*/
public List<InPin> read() throws IOException {
List<InPin> changes = new ArrayList<InPin>();
int in = device.read((byte) InH);
logger.debug("read: {}", in);
if (in > 0) {
byte data = (byte) ~in;
for (InPin pin : input) {
if (pin.update(data)) {
changes.add(pin);
}
}
}
return changes;
}
/**
* Writes output pins
*
* @param data
* @throws IOException
*/
public void write() throws Exception {
byte data = 0;
for (OutPin pin : output) {
if (pin.isSet()) {
data |= pin.getMask();
logger.debug("pin {} is set, mask={} data is now {}", pin.index, Util.toHex(pin.mask),
Util.toHex(data));
}
}
write(data);
}
private void write(byte data) throws Exception {
logger.debug("writing {} to {}", Util.toHex(data), Util.toHex(address));
runWithRetries(() -> {
device.write(OutL, data);
});
}
public byte getAddress() {
return address;
}
/**
* tests outputs
*
* @throws IOException
*/
public void test() throws Exception {
for (int i = 0; i < 8; i++) {
write((byte) (1 << i % 8));
try {
Thread.sleep(500);
} catch (InterruptedException e) {
}
}
write((byte) 0);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Max7311) {
return ((Max7311) obj).address == address;
}
return false;
}
@Override
public String toString() {
return String.format("Max7311: %02X", address);
}
public InPin getInputPin(byte index) {
if (index >= 0 && index < input.size()) {
return input.get(index);
}
return null;
}
public OutPin getOutputPin(byte index) {
if (index >= 0 && index < output.size()) {
return output.get(index);
}
return null;
}
public List<OutPin> getOutputPins() {
return new ArrayList<>(output);
}
}