libxr
Embedded C++ framework for bare-metal, RTOS and Linux
Loading...
Searching...
No Matches
stm32_i2c_timing.hpp
1#pragma once
2
3#include <cstdint>
4
5namespace LibXR::STM32I2CTiming
6{
22inline bool Compute(uint32_t clock_hz, uint32_t speed_hz, bool analog_filter,
23 uint32_t digital_filter, uint32_t& timing)
24{
25 if (clock_hz == 0 || speed_hz == 0 || speed_hz > 1000000 || digital_filter > 15)
26 {
27 return false;
28 }
29
30 struct Limits
31 {
32 uint32_t rise, fall, setup, valid, low, high;
33 };
34 const Limits limits = speed_hz <= 100000 ? Limits{1000, 300, 250, 3450, 4700, 4000}
35 : speed_hz <= 400000 ? Limits{300, 300, 100, 900, 1300, 600}
36 : Limits{120, 120, 50, 450, 500, 260};
37
38 // 时间乘以内核频率计算,避免高频时钟的纳秒取整误差。
39 // Scale time by the kernel frequency to avoid rounding a clock period to ns.
40 constexpr uint64_t CYCLE = 1000000000ULL;
41 const auto ns = [clock_hz](uint32_t value) { return uint64_t(value) * clock_hz; };
42 const auto ceil_div = [](uint64_t value, uint64_t divisor)
43 { return value / divisor + (value % divisor != 0); };
44 const uint64_t af_min = analog_filter ? ns(50) : 0;
45 const uint64_t af_max = analog_filter ? ns(260) : 0;
46 const uint64_t sync = af_min + (digital_filter + 2ULL) * CYCLE;
47 const uint64_t setup_min = ns(limits.rise + limits.setup);
48 const int64_t hold_lower = int64_t(ns(limits.fall)) - int64_t(af_min) -
49 int64_t((digital_filter + 3ULL) * CYCLE);
50 const int64_t hold_upper = int64_t(ns(limits.valid)) - int64_t(ns(limits.rise)) -
51 int64_t(af_max) - int64_t((digital_filter + 4ULL) * CYCLE);
52 if (hold_upper < 0 || hold_lower > hold_upper)
53 {
54 return false;
55 }
56 const uint64_t hold_min = hold_lower > 0 ? uint64_t(hold_lower) : 0;
57 const uint64_t target = ceil_div(CYCLE * clock_hz, speed_hz);
58 uint64_t best_period = UINT64_MAX;
59 uint64_t best_balance = UINT64_MAX;
60 uint32_t best_timing = 0;
61
62 for (uint32_t presc = 0; presc < 16; ++presc)
63 {
64 const uint64_t step = (presc + 1ULL) * CYCLE;
65 const uint64_t setup_steps = ceil_div(setup_min, step);
66 const uint64_t hold_first = ceil_div(hold_min, step);
67 const uint64_t hold_last =
68 uint64_t(hold_upper) / step < 15 ? uint64_t(hold_upper) / step : 15;
69 if (setup_steps == 0 || setup_steps > 16 || hold_first > hold_last)
70 {
71 continue;
72 }
73 const uint32_t sdadel = static_cast<uint32_t>((hold_first + hold_last) / 2);
74 for (uint32_t scll = 0; scll < 256; ++scll)
75 {
76 const uint64_t low_raw = (scll + 1ULL) * step;
77 const uint64_t low = low_raw + sync;
78 // AN4235: tI2CCLK < (tLOW - tAF - tDNF) / 4.
79 if (low < ns(limits.low) || low_raw <= 2 * CYCLE)
80 {
81 continue;
82 }
83 uint64_t high_min = ns(limits.high);
84 if (high_min <= CYCLE)
85 {
86 high_min = CYCLE + 1;
87 }
88 if (target > low && target - low > high_min)
89 {
90 high_min = target - low;
91 }
92 const uint64_t high_steps = high_min > sync ? ceil_div(high_min - sync, step) : 1;
93 if (high_steps > 256)
94 {
95 continue;
96 }
97 const uint64_t high = high_steps * step + sync;
98 const uint64_t period = low + high;
99 const uint64_t balance = low > high ? low - high : high - low;
100 if (period < best_period || (period == best_period && balance < best_balance))
101 {
102 best_period = period;
103 best_balance = balance;
104 best_timing = (presc << 28) | (uint32_t(setup_steps - 1) << 20) | (sdadel << 16) |
105 (uint32_t(high_steps - 1) << 8) | scll;
106 }
107 }
108 }
109 if (best_period == UINT64_MAX || best_period - target > target / 5)
110 {
111 return false;
112 }
113 timing = best_timing;
114 return true;
115}
116} // namespace LibXR::STM32I2CTiming