1
0
Fork 0
mirror of https://git.rwth-aachen.de/acs/public/villas/node/ synced 2025-03-09 00:00:00 +01:00

fix several issues with i2c implementation and add support for

interacting with i2c switches

Signed-off-by: Niklas Eiling <niklas.eiling@eonerc.rwth-aachen.de>
This commit is contained in:
Niklas Eiling 2024-01-03 11:45:56 +01:00 committed by Niklas Eiling
parent 2d51deea0c
commit 57177c074e
2 changed files with 71 additions and 7 deletions

View file

@ -33,6 +33,32 @@ public:
int receiveIntrs;
int statusIntrs;
class Switch {
public:
Switch(I2c *i2c, uint8_t address)
: i2c(i2c), address(address), channel(0), readOnce(false){};
Switch(const Switch &other) = delete;
Switch &operator=(const Switch &other) = delete;
void setChannel(uint8_t channel);
uint8_t getChannel();
void setAddress(uint8_t address) { this->address = address; }
uint8_t getAddress() { return address; }
private:
I2c *i2c;
uint8_t address;
uint8_t channel;
bool readOnce;
};
Switch &getSwitch(uint8_t address = 0x70) {
if (switchInstance == nullptr) {
switchInstance = std::make_unique<Switch>(this, address);
} else {
switchInstance->setAddress(address);
}
return *switchInstance;
}
private:
static constexpr char registerMemory[] = "Reg";
static constexpr char i2cInterrupt[] = "iic2intc_irpt";
@ -45,6 +71,7 @@ private:
bool configDone;
bool initDone;
bool polling;
std::unique_ptr<Switch> switchInstance;
std::list<MemoryBlockName> getMemoryBlocks() const {
return {registerMemory};

View file

@ -17,9 +17,10 @@ using namespace villas::fpga::ip;
I2c::I2c()
: Node(), transmitIntrs(0), receiveIntrs(0), statusIntrs(0), xIic(),
xConfig(), hwLock(), configDone(false), initDone(false), polling(false) {}
xConfig(), hwLock(), configDone(false), initDone(false), polling(false),
switchInstance(nullptr) {}
I2c::~I2c() {}
I2c::~I2c() { reset(); }
static void SendHandler(I2c *i2c, __attribute__((unused)) int bytesSend) {
i2c->transmitIntrs++;
@ -53,10 +54,16 @@ bool I2c::init() {
irqs[i2cInterrupt].irqController->enableInterrupt(irqs[i2cInterrupt],
polling);
initDone = true;
return true;
}
bool I2c::reset() { return true; }
bool I2c::reset() {
XIic_Reset(&xIic);
irqs[i2cInterrupt].irqController->disableInterrupt(irqs[i2cInterrupt]);
initDone = false;
return true;
}
bool I2c::write(u8 address, std::vector<u8> &data) {
int ret;
@ -75,7 +82,7 @@ bool I2c::write(u8 address, std::vector<u8> &data) {
xIic.Stats.TxErrors = 0;
int retries = 10;
while (xIic.Stats.TxErrors != 0 && retries > 0) {
while (transmitIntrs == 0 && xIic.Stats.TxErrors == 0 && retries > 0) {
ret = XIic_Start(&xIic);
if (ret != XST_SUCCESS) {
throw RuntimeError("Failed to start I2C");
@ -99,8 +106,9 @@ bool I2c::write(u8 address, std::vector<u8> &data) {
if (ret != XST_SUCCESS) {
throw RuntimeError("Failed to stop I2C");
}
if (retries == 0) {
throw RuntimeError("Failed to send I2C data");
if (retries == 0 || xIic.Stats.TxErrors != 0) {
throw RuntimeError("Failed to send I2C data: {} retries, {} errors",
retries, xIic.Stats.TxErrors);
}
return true;
}
@ -129,7 +137,7 @@ bool I2c::read(u8 address, std::vector<u8> &data, size_t max_read) {
}
int retries = 10;
while (xIic.Stats.TxErrors != 0 && retries > 0) {
while (receiveIntrs == 0 && retries > 0) {
ret = XIic_Start(&xIic);
if (ret != XST_SUCCESS) {
throw RuntimeError("Failed to start I2C");
@ -160,6 +168,35 @@ bool I2c::read(u8 address, std::vector<u8> &data, size_t max_read) {
return XST_SUCCESS;
}
static const uint8_t CHANNEL_MAP[] = {0x20, 0x80, 0x02, 0x08,
0x10, 0x40, 0x01, 0x04};
void I2c::Switch::setChannel(uint8_t channel) {
if (channel > sizeof(CHANNEL_MAP) / sizeof(CHANNEL_MAP[0])) {
throw RuntimeError("Invalid channel number {}", channel);
}
this->channel = channel;
std::vector<u8> data = {CHANNEL_MAP[channel]};
i2c->write(address, data);
}
uint8_t I2c::Switch::getChannel() {
std::vector<u8> data(1);
i2c->read(address, data, 1);
if (readOnce && data[0] != CHANNEL_MAP[channel]) {
throw RuntimeError("Invalid channel readback: {:x} != {:x}", data[0],
CHANNEL_MAP[channel]);
} else {
for (size_t i = 0; i < sizeof(CHANNEL_MAP) / sizeof(CHANNEL_MAP[0]); ++i) {
if (data[0] == CHANNEL_MAP[i]) {
channel = i;
break;
}
}
readOnce = true;
}
return channel;
}
void I2cFactory::parse(Core &ip, json_t *cfg) {
NodeFactory::parse(ip, cfg);