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, ®, 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 |