rusEFI
The most advanced open source ECU
Loading...
Searching...
No Matches
i2c_bb.cpp
Go to the documentation of this file.
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
14#if EFI_PROD_CODE
15 palSetPad(m_sdaPort, m_sdaPin);
16#endif
17}
18
20#if EFI_PROD_CODE
21 palClearPad(m_sdaPort, m_sdaPin);
22#endif
23}
24
26#if EFI_PROD_CODE
27 palSetPad(m_sclPort, m_sclPin);
28#endif
29}
30
32#if EFI_PROD_CODE
33 palClearPad(m_sclPort, m_sclPin);
34#endif
35}
36
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 scl_high();
59 sda_high();
60
61 return true;
62}
63
65#if EFI_PROD_CODE
66 if (m_sclPort) {
68 m_sclPort = NULL;
69 }
70 if (m_sdaPort) {
72 m_sdaPort = NULL;
73 }
74#endif
75}
76
78 // Start with both lines high (bus idle)
79 sda_high();
81 scl_high();
83
84 // SDA goes low while SCL is high
85 sda_low();
87 scl_low();
89}
90
92 scl_low();
94 sda_low();
96 scl_high();
98 // SDA goes high while SCL is high
99 sda_high();
100}
101
102void BitbangI2c::sendBit(bool val) {
104
105 // Write the bit (write while SCL is low)
106 if (val) {
107 sda_high();
108 } else {
109 sda_low();
110 }
111
112 // Data setup time (~100ns min)
114
115 // Strobe the clock
116 scl_high();
118 scl_low();
120}
121
124
125 scl_high();
126
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 bool val = false;
135#endif
136
137 scl_low();
139
140 return val;
141}
142
143bool BitbangI2c::writeByte(uint8_t data) {
144 // write out 8 data bits
145 for (size_t i = 0; i < 8; i++) {
146 // Send the MSB
147 sendBit((data & 0x80) != 0);
148
149 data = data << 1;
150 }
151
152 // Force a release of the data line so the slave can ACK
153 sda_high();
154
155 // Read the ack bit
156 bool ackBit = readBit();
157
158 // 0 -> ack
159 // 1 -> nack
160 return !ackBit;
161}
162
163uint8_t BitbangI2c::readByte(bool ack) {
164 uint8_t result = 0;
165
166 // Read in 8 data bits
167 for (size_t i = 0; i < 8; i++) {
168 result = result << 1;
169
170 result |= readBit() ? 1 : 0;
171 }
172
173 // 0 -> ack
174 // 1 -> nack
175 sendBit(!ack);
176
177 // release SDA
178 sda_high();
179
180 return result;
181}
182
184 // This yields a bitrate of about 320khz on a 168MHz F4
185 for (size_t i = 0; i < 30; i++) {
186 __asm__ volatile ("nop");
187 }
188}
189
190void BitbangI2c::write(uint8_t addr, const uint8_t* writeData, size_t writeSize) {
191 start();
192
193 // Address + write
194 writeByte(addr << 1 | 0);
195
196 // Write outbound bytes
197 for (size_t i = 0; i < writeSize; i++) {
198 writeByte(writeData[i]);
199 }
200
201 stop();
202}
203
204void BitbangI2c::writeRead(uint8_t addr, const uint8_t* writeData, size_t writeSize, uint8_t* readData, size_t readSize) {
205 write(addr, writeData, writeSize);
206
207 read(addr, readData, readSize);
208}
209
210void BitbangI2c::read(uint8_t addr, uint8_t* readData, size_t readSize) {
211 start();
212
213 // Address + read
214 writeByte(addr << 1 | 1);
215
216 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 readData[readSize - 1] = readByte(false);
223
224 stop();
225}
226
227uint8_t BitbangI2c::readRegister(uint8_t addr, uint8_t reg) {
228 uint8_t retval;
229
230 writeRead(addr, &reg, 1, &retval, 1);
231
232 return retval;
233}
234
235void 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}
constexpr uint8_t addr
Definition ads1015.cpp:14
void efiSetPadMode(const char *msg, brain_pin_e brainPin, iomode_t mode)
void read(uint8_t addr, uint8_t *data, size_t size)
Definition i2c_bb.cpp:210
void waitQuarterBit()
Definition i2c_bb.cpp:183
ioportid_t m_sclPort
Definition i2c_bb.h:54
ioportmask_t m_sdaPin
Definition i2c_bb.h:57
void write(uint8_t addr, const uint8_t *data, size_t size)
Definition i2c_bb.cpp:190
ioportid_t m_sdaPort
Definition i2c_bb.h:56
bool writeByte(uint8_t data)
Definition i2c_bb.cpp:143
void writeRegister(uint8_t addr, uint8_t reg, uint8_t val)
Definition i2c_bb.cpp:235
void start()
Definition i2c_bb.cpp:77
bool readBit()
Definition i2c_bb.cpp:122
bool init(brain_pin_e scl, brain_pin_e sda)
Definition i2c_bb.cpp:37
void scl_low()
Definition i2c_bb.cpp:31
void sda_low()
Definition i2c_bb.cpp:19
void sda_high()
Definition i2c_bb.cpp:13
void stop()
Definition i2c_bb.cpp:91
uint8_t readByte(bool ack)
Definition i2c_bb.cpp:163
uint8_t readRegister(uint8_t addr, uint8_t reg)
Definition i2c_bb.cpp:227
void scl_high()
Definition i2c_bb.cpp:25
void writeRead(uint8_t addr, const uint8_t *writeData, size_t writeSize, uint8_t *readData, size_t readSize)
Definition i2c_bb.cpp:204
ioportmask_t m_sclPin
Definition i2c_bb.h:55
void sendBit(bool val)
Definition i2c_bb.cpp:102
void deinit()
Definition i2c_bb.cpp:64
ioportid_t getHwPort(const char *msg, brain_pin_e brainPin)
ioportmask_t getHwPin(const char *msg, brain_pin_e brainPin)
Bit-banged I2C driver.
void gpio_pin_markUnused(ioportid_t port, ioportmask_t pin)
bool isBrainPinValid(brain_pin_e brainPin)