Skip to content

Commit

Permalink
[stm32] Improve stm32-extended i2c calculator efficiency
Browse files Browse the repository at this point in the history
  • Loading branch information
chris-durand committed Nov 2, 2018
1 parent f2db421 commit 48c92cd
Showing 1 changed file with 94 additions and 32 deletions.
126 changes: 94 additions & 32 deletions src/modm/platform/i2c/stm32-extended/i2c_timing_calculator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ struct I2cParameters
class I2cTimingCalculator
{
public:
constexpr I2cTimingCalculator(const I2cParameters& parameters) : params{parameters} {}
explicit constexpr I2cTimingCalculator(const I2cParameters& parameters)
: params{parameters} {}

constexpr std::optional<I2cMasterTimings>
calculateTimings() const
Expand All @@ -62,6 +63,11 @@ class I2cTimingCalculator
return std::nullopt;
}

if((params.fallTime > ModeConstants::tFallMax[modeIndex()])
|| (params.riseTime > ModeConstants::tRiseMax[modeIndex()])) {
return std::nullopt;
}

bool found = false;
float lastError = 1e10;
uint8_t bestSclLow = 0;
Expand All @@ -74,19 +80,28 @@ class I2cTimingCalculator
continue;
}

for(uint16_t sclLow = 0; sclLow <= 255; ++sclLow) {
for(uint16_t sclHigh = 0; sclHigh <= 255; ++sclHigh) {
std::optional<float> speed = calculateSpeed(prescaler, sclLow, sclHigh);
if(speed) {
auto error = std::fabs((speed.value() / params.targetSpeed) - 1.0f);
// modm::Tolerance value is in unit [1/1000]
if((error * 1000) < params.tolerance && error <= lastError && prescaler <= bestPrescaler) {
lastError = error;
bestPrescaler = prescaler;
bestSclLow = sclLow;
bestSclHigh = sclHigh;
found = true;
}
auto [valid, sclLowMin, sclHighMin] = minimumSclLowHigh(prescaler);
if(!valid) {
continue;
}

const uint16_t sclSumMax = maximumSclSum(prescaler);
const uint16_t sclLowMax = std::min<uint16_t>(sclSumMax, 255);

for(uint16_t sclLow = sclLowMin; sclLow <= sclLowMax; ++sclLow) {
auto sclHighMax = std::min(sclSumMax - sclLow, 255);

auto sclHigh = findBestSclHigh(prescaler, sclLow, sclHighMin, sclHighMax);
std::optional<float> speed = calculateSpeed(prescaler, sclLow, sclHigh);
if(speed) {
auto error = std::fabs((speed.value()/params.targetSpeed)-1.0f);
// modm::Tolerance value is in unit [1/1000]
if (((error*1000) < params.tolerance) && (error <= lastError) && (prescaler<=bestPrescaler)) {
lastError = error;
bestPrescaler = prescaler;
bestSclLow = sclLow;
bestSclHigh = sclHigh;
found = true;
}
}
}
Expand Down Expand Up @@ -123,6 +138,11 @@ class I2cTimingCalculator
private:
I2cParameters params;

const float FilterDelay = (params.enableAnalogFilter ? AnalogFilterDelayMin : 0)
+ float(params.digitalFilterLength) / params.peripheralClock;

const float SyncTime = FilterDelay + (2.0f / params.peripheralClock);

struct ModeConstants
{
// index 0: standard 100kHz
Expand Down Expand Up @@ -166,8 +186,8 @@ class I2cTimingCalculator
};
};

static constexpr auto FastThreshold = 125'000.f;
static constexpr auto FastPlusThreshold = 500'000.f;
static constexpr auto FastThreshold = 250'000.f;
static constexpr auto FastPlusThreshold = 700'000.f;

static constexpr auto AnalogFilterDelayMin = 50.0e-9f;
static constexpr auto AnalogFilterDelayMax = 260.0e-9f;
Expand Down Expand Up @@ -237,31 +257,73 @@ class I2cTimingCalculator
return prescalerMask;
}

constexpr std::optional<float>
calculateSpeed(uint8_t prescaler, uint16_t sclLow, uint16_t sclHigh) const
constexpr std::tuple<bool, uint8_t, uint8_t>
minimumSclLowHigh(uint8_t prescaler) const
{
auto clockPeriod = float(prescaler + 1) / params.peripheralClock;

auto filterDelay = (params.enableAnalogFilter ? AnalogFilterDelayMin : 0)
+ float(params.digitalFilterLength) / params.peripheralClock;
float lowMinFloat = std::max(
((ModeConstants::tLowMin[modeIndex()] - SyncTime) / clockPeriod) - 1,
((4.0f / params.peripheralClock) + FilterDelay - SyncTime) / clockPeriod - 1
);

float highMinFloat = std::max(
((ModeConstants::tHighMin[modeIndex()] - SyncTime) / clockPeriod) - 1,
((1.0f / params.peripheralClock) - SyncTime) / clockPeriod - 1
);

lowMinFloat = std::ceil(lowMinFloat);
highMinFloat = std::ceil(highMinFloat);

if(lowMinFloat > 255 || highMinFloat > 255) {
return {false, 255, 255};
}

auto syncTime = filterDelay + (2.0f / params.peripheralClock);
uint8_t lowMin = (uint8_t) std::max(lowMinFloat, 0.f);
uint8_t highMin = (uint8_t) std::max(highMinFloat, 0.f);
return {true, lowMin, highMin};
}

constexpr uint16_t
maximumSclSum(uint8_t prescaler) const
{
auto clockPeriod = float(prescaler + 1) / params.peripheralClock;

auto sclLowTime = (sclLow + 1) * clockPeriod + syncTime;
auto sclHighTime = (sclHigh + 1) * clockPeriod + syncTime;
auto targetSclTime = 1.f / params.targetSpeed;
auto sclTimeMax = targetSclTime * (1.f + params.tolerance / 1000.f);
auto maxSclSum = ((sclTimeMax - params.riseTime - params.fallTime - 2*SyncTime)
/ clockPeriod) - 2;

return (uint16_t) std::max(0.0f, std::min(maxSclSum, 255.0f * 2));
}

constexpr uint8_t
findBestSclHigh(uint8_t prescaler, uint8_t sclLow, uint8_t min, uint8_t max) const
{
auto clockPeriod = float(prescaler + 1) / params.peripheralClock;
auto sclLowTime = (sclLow + 1) * clockPeriod + SyncTime;

auto targetSclTime = 1.f / params.targetSpeed;
auto targetSclHighTime = targetSclTime - sclLowTime
- params.riseTime - params.fallTime;

auto targetSclHigh = std::round((targetSclHighTime - SyncTime) / clockPeriod - 1);

return (uint8_t) std::max<float>(min, std::min<float>(targetSclHigh, max));
}

constexpr std::optional<float>
calculateSpeed(uint8_t prescaler, uint16_t sclLow, uint16_t sclHigh) const
{
auto clockPeriod = float(prescaler + 1) / params.peripheralClock;

auto sclLowTime = (sclLow + 1) * clockPeriod + SyncTime;
auto sclHighTime = (sclHigh + 1) * clockPeriod + SyncTime;

auto sclTime = sclLowTime + sclHighTime + params.riseTime + params.fallTime;
auto speed = 1.0f / sclTime;

if(sclLowTime >= ModeConstants::tLowMin[modeIndex()]
&& sclHighTime >= ModeConstants::tHighMin[modeIndex()]
&& ((1.0f / params.peripheralClock) < ((sclLowTime - filterDelay) / 4))
&& ((1.0f / params.peripheralClock) < sclHighTime)) {

return speed;
} else {
return std::nullopt;
}
return speed;
}
};

Expand Down

0 comments on commit 48c92cd

Please sign in to comment.