Code: Select all
#include "STI2c.h"
#include "SMCommon.h"
#include "Wire.h"
#define TRACKER() printf("[I2C TRACKER] %u\n", __LINE__);
std::map<uint8_t, std::string> I2cErrorNameLookup =
{
{ i2c_err_t::I2C_ERROR_OK, "I2C_ERROR_OK" },
{ i2c_err_t::I2C_ERROR_DEV, "I2C_ERROR_DEV" },
{ i2c_err_t::I2C_ERROR_ADDR_NACK, "I2C_ERROR_ADDR_NACK" },
{ i2c_err_t::I2C_ERROR_DATA_NACK, "I2C_ERROR_DATA_NACK" },
{ i2c_err_t::I2C_ERROR_TIMEOUT, "I2C_ERROR_TIMEOUT" },
{ i2c_err_t::I2C_ERROR_BUS, "I2C_ERROR_BUS" },
{ i2c_err_t::I2C_ERROR_BUSY, "I2C_ERROR_BUSY" },
{ i2c_err_t::I2C_ERROR_MEMORY, "I2C_ERROR_MEMORY" },
{ i2c_err_t::I2C_ERROR_CONTINUE, "I2C_ERROR_CONTINUE" },
{ i2c_err_t::I2C_ERROR_NO_BEGIN, "I2C_ERROR_NO_BEGIN" }
};
namespace STI2c
{
SemaphoreHandle_t GuardedWire::_lock = xSemaphoreCreateMutex();
std::map<std::string, uint32_t> GuardedWire::_used = {};
std::map<std::string, uint32_t> GuardedWire::_failures = {};
std::map<std::string, TickType_t> GuardedWire::_maxTimes = {};
std::map<std::string, TickType_t> GuardedWire::_minTimes = {};
TickType_t GuardedWire::_reportStatusTimeout = xTaskGetTickCount() + pdMS_TO_TICKS(60000);
std::string GuardedWire::_holder = "unknown";
int GuardedWire::_holderLineNo = 0;
bool GuardedWire::_initialized = false;
GuardedWire::GuardedWire(std::string tag, int lineNo):
_tag(tag),
_lineNo(lineNo)
{
if (_lock == NULL)
{
THROW;
}
if (xSemaphoreTake(_lock, pdMS_TO_TICKS(2500)) != pdTRUE)
{
ERROR_PRINT("[I2C]%s raised exception\n", _tag.c_str());
ERROR_PRINT("[I2C]%s %d held for to long\n", _holder.c_str(), _holderLineNo);
THROW;
}
_holder = _tag;
_holderLineNo = _lineNo;
if (!_initialized)
{
_initialized = true;
Wire.begin(23, 19, 100000);
Wire.setTimeOut(20);
}
Wire.flush();
if (_maxTimes.find(_tag) == _maxTimes.end())
{
_used[_tag] = 0;
_failures[_tag] = 0;
_maxTimes[_tag] = 0;
_minTimes[_tag] = 10000000;
}
_start = xTaskGetTickCount();
}
GuardedWire::~GuardedWire()
{
Wire.flush();
uint32_t durationMs = (xTaskGetTickCount() - _start)/pdMS_TO_TICKS(1);
_maxTimes[_tag] = std::max(_maxTimes[_tag], durationMs);
_minTimes[_tag] = std::min(_minTimes[_tag], durationMs);
if (xTaskGetTickCount() > _reportStatusTimeout)
{
_reportStatusTimeout = xTaskGetTickCount() + pdMS_TO_TICKS(60000);
for (auto & each : _maxTimes)
{
std::string id = each.first;
SMCommon::CsvPrint(
"i2c_stats.csv",
"name, maxTime, minTime, count, fails",
"%s, %u, %u, %u, %u",
id.c_str(),
_maxTimes[id],
_minTimes[id],
_used[id],
_failures[id]
);
}
}
xSemaphoreGive(_lock);
}
uint16_t GuardedWire::GetTimeOut()
{
return Wire.getTimeOut();
}
void GuardedWire::SetTimeOut(uint16_t timeOutMillis)
{
Wire.setTimeOut(timeOutMillis);
}
bool GuardedWire::I2CWrite(I2cDetails details)
{
uint8_t error;
for(uint8_t j=0; j<5; j++)
{
_used[_tag]++;
Wire.beginTransmission(details.address);
Wire.write(details.reg);
for(uint8_t i=0; i<details.datalen; i++)
{
Wire.write(details.data.data[i]);
}
error = Wire.endTransmission(true);
if(!error)
{
return true;
}
_failures[_tag]++;
vTaskDelay(pdMS_TO_TICKS(1));
}
ERROR_PRINT("[I2C]%s %d write failed: %s\n", _tag.c_str(), _lineNo, SMCommon::safeLookup<uint8_t>(I2cErrorNameLookup, error));
return false;
}
bool GuardedWire::I2CRead(I2cDetails details, I2CData & results)
{
uint8_t *data = results.data;
for (uint8_t i=0; i<5; i++)
{
_used[_tag]++;
Wire.beginTransmission(details.address);
Wire.write(details.reg);
Wire.endTransmission(false);
if (Wire.requestFrom(details.address, details.datalen, true))
{
while(Wire.available())
{
*data++ = Wire.read();
}
return true;
}
_failures[_tag]++;
vTaskDelay(pdMS_TO_TICKS(30));
}
ERROR_PRINT("[I2C]%s %d read failed\n", _tag.c_str(), _lineNo);
return false;
}
}