GCC Code Coverage Report


Directory: ./
File: firmware/hw_layer/drivers/i2c/i2c_bb.cpp
Date: 2025-10-03 00:57:22
Coverage Exec Excl Total
Lines: 91.1% 92 0 101
Functions: 88.9% 16 0 18
Branches: 81.2% 13 0 16
Decisions: 91.7% 11 - 12

Line Branch Decision Exec Source
1 /**
2 * @file i2c_bb.cpp
3 * @brief Bit-banged I2C driver
4 *
5 * @date February 6, 2020
6 * @author Matthew Kennedy, (c) 2020
7 */
8
9 #include "pch.h"
10
11 #include "i2c_bb.h"
12
13 46 void BitbangI2c::sda_high() {
14 #if EFI_PROD_CODE
15 palSetPad(m_sdaPort, m_sdaPin);
16 #endif
17 46 }
18
19 30 void BitbangI2c::sda_low() {
20 #if EFI_PROD_CODE
21 palClearPad(m_sdaPort, m_sdaPin);
22 #endif
23 30 }
24
25 82 void BitbangI2c::scl_high() {
26 #if EFI_PROD_CODE
27 palSetPad(m_sclPort, m_sclPin);
28 #endif
29 82 }
30
31 80 void BitbangI2c::scl_low() {
32 #if EFI_PROD_CODE
33 palClearPad(m_sclPort, m_sclPin);
34 #endif
35 80 }
36
37 2 bool BitbangI2c::init(brain_pin_e scl, brain_pin_e sda) {
38 #if EFI_PROD_CODE
39 if (m_sdaPort) {
40 return false;
41 }
42
43 if (!isBrainPinValid(scl) || !isBrainPinValid(sda)) {
44 return false;
45 }
46
47 efiSetPadMode("i2c", scl, PAL_MODE_OUTPUT_OPENDRAIN); //PAL_STM32_OTYPE_OPENDRAIN
48 efiSetPadMode("i2c", sda, PAL_MODE_OUTPUT_OPENDRAIN);
49
50 m_sclPort = getHwPort("i2c", scl);
51 m_sclPin = getHwPin("i2c", scl);
52
53 m_sdaPort = getHwPort("i2c", sda);
54 m_sdaPin = getHwPin("i2c", sda);
55 #endif
56
57 // Both lines idle high
58 2 scl_high();
59 2 sda_high();
60
61 2 return true;
62 }
63
64 void BitbangI2c::deinit() {
65 #if EFI_PROD_CODE
66 if (m_sclPort) {
67 gpio_pin_markUnused(m_sclPort, m_sclPin);
68 m_sclPort = NULL;
69 }
70 if (m_sdaPort) {
71 gpio_pin_markUnused(m_sdaPort, m_sdaPin);
72 m_sdaPort = NULL;
73 }
74 #endif
75 }
76
77 4 void BitbangI2c::start() {
78 // Start with both lines high (bus idle)
79 4 sda_high();
80 4 waitQuarterBit();
81 4 scl_high();
82 4 waitQuarterBit();
83
84 // SDA goes low while SCL is high
85 4 sda_low();
86 4 waitQuarterBit();
87 4 scl_low();
88 4 waitQuarterBit();
89 4 }
90
91 4 void BitbangI2c::stop() {
92 4 scl_low();
93 4 waitQuarterBit();
94 4 sda_low();
95 4 waitQuarterBit();
96 4 scl_high();
97 4 waitQuarterBit();
98 // SDA goes high while SCL is high
99 4 sda_high();
100 4 }
101
102 50 void BitbangI2c::sendBit(bool val) {
103 50 waitQuarterBit();
104
105 // Write the bit (write while SCL is low)
106
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 22 times.
2/2
✓ Decision 'true' taken 28 times.
✓ Decision 'false' taken 22 times.
50 if (val) {
107 28 sda_high();
108 } else {
109 22 sda_low();
110 }
111
112 // Data setup time (~100ns min)
113 50 waitQuarterBit();
114
115 // Strobe the clock
116 50 scl_high();
117 50 waitQuarterBit();
118 50 scl_low();
119 50 waitQuarterBit();
120 50 }
121
122 22 bool BitbangI2c::readBit() {
123 22 waitQuarterBit();
124
125 22 scl_high();
126
127 22 waitQuarterBit();
128 22 waitQuarterBit();
129
130 #if EFI_PROD_CODE
131 // Read just before we set the clock low (ie, as late as possible)
132 bool val = palReadPad(m_sdaPort, m_sdaPin);
133 #else
134 22 bool val = false;
135 #endif
136
137 22 scl_low();
138 22 waitQuarterBit();
139
140 22 return val;
141 }
142
143 6 bool BitbangI2c::writeByte(uint8_t data) {
144 // write out 8 data bits
145
2/2
✓ Branch 0 taken 48 times.
✓ Branch 1 taken 6 times.
2/2
✓ Decision 'true' taken 48 times.
✓ Decision 'false' taken 6 times.
54 for (size_t i = 0; i < 8; i++) {
146 // Send the MSB
147 48 sendBit((data & 0x80) != 0);
148
149 48 data = data << 1;
150 }
151
152 // Force a release of the data line so the slave can ACK
153 6 sda_high();
154
155 // Read the ack bit
156 6 bool ackBit = readBit();
157
158 // 0 -> ack
159 // 1 -> nack
160 6 return !ackBit;
161 }
162
163 2 uint8_t BitbangI2c::readByte(bool ack) {
164 2 uint8_t result = 0;
165
166 // Read in 8 data bits
167
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 2 times.
2/2
✓ Decision 'true' taken 16 times.
✓ Decision 'false' taken 2 times.
18 for (size_t i = 0; i < 8; i++) {
168 16 result = result << 1;
169
170
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 16 times.
16 result |= readBit() ? 1 : 0;
171 }
172
173 // 0 -> ack
174 // 1 -> nack
175 2 sendBit(!ack);
176
177 // release SDA
178 2 sda_high();
179
180 2 return result;
181 }
182
183 316 void BitbangI2c::waitQuarterBit() {
184 // This yields a bitrate of about 320khz on a 168MHz F4
185
2/2
✓ Branch 0 taken 9480 times.
✓ Branch 1 taken 316 times.
2/2
✓ Decision 'true' taken 9480 times.
✓ Decision 'false' taken 316 times.
9796 for (size_t i = 0; i < 30; i++) {
186 9480 __asm__ volatile ("nop");
187 }
188 316 }
189
190 2 void BitbangI2c::write(uint8_t addr, const uint8_t* writeData, size_t writeSize) {
191 2 start();
192
193 // Address + write
194 2 writeByte(addr << 1 | 0);
195
196 // Write outbound bytes
197
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
2/2
✓ Decision 'true' taken 2 times.
✓ Decision 'false' taken 2 times.
4 for (size_t i = 0; i < writeSize; i++) {
198 2 writeByte(writeData[i]);
199 }
200
201 2 stop();
202 2 }
203
204 2 void BitbangI2c::writeRead(uint8_t addr, const uint8_t* writeData, size_t writeSize, uint8_t* readData, size_t readSize) {
205 2 write(addr, writeData, writeSize);
206
207 2 read(addr, readData, readSize);
208 2 }
209
210 2 void BitbangI2c::read(uint8_t addr, uint8_t* readData, size_t readSize) {
211 2 start();
212
213 // Address + read
214 2 writeByte(addr << 1 | 1);
215
216
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 2 times.
2 for (size_t i = 0; i < readSize - 1; i++) {
217 // All but the last byte send ACK to indicate we're still reading
218 readData[i] = readByte(true);
219 }
220
221 // last byte sends NAK to indicate we're done reading
222 2 readData[readSize - 1] = readByte(false);
223
224 2 stop();
225 2 }
226
227 2 uint8_t BitbangI2c::readRegister(uint8_t addr, uint8_t reg) {
228 2 uint8_t retval;
229
230
1/1
✓ Branch 1 taken 2 times.
2 writeRead(addr, &reg, 1, &retval, 1);
231
232 2 return retval;
233 }
234
235 void BitbangI2c::writeRegister(uint8_t addr, uint8_t reg, uint8_t val) {
236 uint8_t buf[2];
237 buf[0] = reg;
238 buf[1] = val;
239
240 write(addr, buf, 2);
241 }
242