Merge pull request #11939 from blckmn/flash-fix
[betaflight.git] / src / main / drivers / bus_i2c_timing.c
blobed3b959cac40d46a831959b7d7b1e147808d5f43
1 /*
2 * This file is part of Cleanflight and Betaflight.
4 * Cleanflight and Betaflight are free software. You can redistribute
5 * this software and/or modify this software under the terms of the
6 * GNU General Public License as published by the Free Software
7 * Foundation, either version 3 of the License, or (at your option)
8 * any later version.
10 * Cleanflight and Betaflight are distributed in the hope that they
11 * will be useful, but WITHOUT ANY WARRANTY; without even the implied
12 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 * See the GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this software.
18 * If not, see <http://www.gnu.org/licenses/>.
21 #include <stdint.h>
23 #include "platform.h"
26 * Compute SCLDEL, SDADEL, SCLH and SCLL for TIMINGR register according to reference manuals.
28 static void i2cClockComputeRaw(uint32_t pclkFreq, int i2cFreqKhz, int presc, int dfcoeff,
29 uint8_t *scldel, uint8_t *sdadel, uint16_t *sclh, uint16_t *scll)
31 // Values from I2C-SMBus specification
32 uint16_t trmax; // Rise time (max)
33 uint16_t tfmax; // Fall time (max)
34 uint8_t tsuDATmin; // SDA setup time (min)
35 uint8_t thdDATmin; // SDA hold time (min)
36 uint16_t tHIGHmin; // High period of SCL clock (min)
37 uint16_t tLOWmin; // Low period of SCL clock (min)
39 // Silicon specific values, from datasheet
40 uint8_t tAFmin = 50; // Analog filter delay (min)
42 // Actual (estimated) values
43 uint8_t tr = 100; // Rise time
44 uint8_t tf = 10; // Fall time
46 if (i2cFreqKhz > 400) {
47 // Fm+ (Fast mode plus)
48 trmax = 120;
49 tfmax = 120;
50 tsuDATmin = 50;
51 thdDATmin = 0;
52 tHIGHmin = 260;
53 tLOWmin = 500;
54 } else {
55 // Fm (Fast mode)
56 trmax = 300;
57 tfmax = 300;
58 tsuDATmin = 100;
59 thdDATmin = 0;
60 tHIGHmin = 600;
61 tLOWmin = 1300;
64 // Convert pclkFreq into nsec
65 float tI2cclk = 1000000000.0f / pclkFreq;
67 // Convert target i2cFreq into cycle time (nsec)
68 float tSCL = 1000000.0f / i2cFreqKhz;
70 uint32_t SCLDELmin = (trmax + tsuDATmin) / ((presc + 1) * tI2cclk) - 1;
71 uint32_t SDADELmin = (tfmax + thdDATmin - tAFmin - ((dfcoeff + 3) * tI2cclk)) / ((presc + 1) * tI2cclk);
73 float tsync1 = tf + tAFmin + dfcoeff * tI2cclk + 2 * tI2cclk;
74 float tsync2 = tr + tAFmin + dfcoeff * tI2cclk + 2 * tI2cclk;
76 float tSCLH = tHIGHmin * tSCL / (tHIGHmin + tLOWmin) - tsync2;
77 float tSCLL = tSCL - tSCLH - tsync1 - tsync2;
79 uint32_t SCLH = tSCLH / ((presc + 1) * tI2cclk) - 1;
80 uint32_t SCLL = tSCLL / ((presc + 1) * tI2cclk) - 1;
82 while (tsync1 + tsync2 + ((SCLH + 1) + (SCLL + 1)) * ((presc + 1) * tI2cclk) < tSCL) {
83 SCLH++;
86 *scldel = SCLDELmin;
87 *sdadel = SDADELmin;
88 *sclh = SCLH;
89 *scll = SCLL;
92 uint32_t i2cClockTIMINGR(uint32_t pclkFreq, int i2cFreqKhz, int dfcoeff)
94 #define TIMINGR(presc, scldel, sdadel, sclh, scll) \
95 ((presc << 28)|(scldel << 20)|(sdadel << 16)|(sclh << 8)|(scll << 0))
97 uint8_t scldel;
98 uint8_t sdadel;
99 uint16_t sclh;
100 uint16_t scll;
102 for (int presc = 0; presc < 15; presc++) {
103 i2cClockComputeRaw(pclkFreq, i2cFreqKhz, presc, dfcoeff, &scldel, &sdadel, &sclh, &scll);
105 // If all fields are not overflowing, return TIMINGR.
106 // Otherwise, increase prescaler and try again.
107 if ((scldel < 16) && (sdadel < 16) && (sclh < 256) && (scll < 256)) {
108 return TIMINGR(presc, scldel, sdadel, sclh, scll);
111 return 0; // Shouldn't reach here