In my never-ending quest to figure out why my I2C connection to an MPU6050 dies intermittently, I decided to try and record the I2C bus conversation to see if I can determine if it is the MPU6050 or the microcontroller goes tits-up on me.
Of course, this adventure turned out to be a LOT more complicated than I thought – I mean, how hard could it be to find and run one of the many (or so I thought) I2C sniffer setups out there in the i-verse? Well, after a fair bit of Googling and forum searches, I found that there just aren’t any good I2C sniffer programs out there, or at least nothing that I could find.
I did run across one promising program; it’s a Teensy 3.2 sniffer program written by ‘Kito’ and posted on the PJRC Teensy forum in this post. I also found this program written for the Arduino Mega. So, I created a small Arduino Mega test program connected to a MPU6050 using Jeff Rowberg’s I2CDev library.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 |
/* Name: I2C_SNIFFER_TEST_PGM.ino Created: 12/20/2019 9:56:20 PM Author: FRANKNEWXPS15\Frank */ #include <elapsedMillis.h> #include <PrintEx.h> //allows printf-style printout syntax #include "MPU6050_6Axis_MotionApps_V6_12.h" //changed to this version 10/05/19 #include "I2Cdev.h" //02/19/19: this includes SBWire.h #include <NewPing.h> //added 01/15/15 #pragma endregion INCLUDES StreamEx mySerial = Serial; //added 03/18/18 for printf-style printing elapsedMillis sinceLastNavUpdateMsec; //added 10/15/18 to replace lastmillisec //MPU6050 mpu(0x69); //06/23/18 chg to AD0 high addr on 2-motor robot MPU6050 mpu(0x68); //06/23/18 chg to AD0 high addr on 2-motor robot #pragma region MPU6050_SUPPORT uint8_t mpuIntStatus; // holds actual interrupt status byte from MPU. Used in Homer's Overflow routine uint8_t devStatus; // return status after each device operation (0 = success, !0 = error) uint16_t packetSize; // expected DMP packet size (default is 42 bytes) uint16_t fifoCount; // count of all bytes currently in FIFO uint8_t fifoBuffer[64]; // FIFO storage buffer // orientation/motion vars Quaternion q; // [w, x, y, z] quaternion container VectorInt16 aa; // [x, y, z] accel sensor measurements VectorInt16 aaReal; // [x, y, z] gravity-free accel sensor measurements VectorInt16 aaWorld; // [x, y, z] world-frame accel sensor measurements VectorFloat gravity; // [x, y, z] gravity vector float euler[3]; // [psi, theta, phi] Euler angle container float ypr[3]; // [yaw, pitch, roll] yaw/pitch/roll container and gravity vector int GetPacketLoopCount = 0; int OuterGetPacketLoopCount = 0; //RTC/FRAM/MPU6050 status flags bool bMPU6050Ready = true; bool dmpReady = false; // set true if DMP init was successful volatile float global_yawval = 0; //updated by GetIMUHeadingDeg() const uint16_t MAX_GETPACKET_LOOPS = 100; //10/30/19 added for backup loop exit condition in GetCurrentFIFOPacket() uint8_t GetCurrentFIFOPacket(uint8_t* data, uint8_t length, uint16_t max_loops = MAX_GETPACKET_LOOPS); //prototype here so can define a default param bool bFirstTime = true; #pragma endregion MPU6050 Support const int NAV_UPDATE_INTERVAL_MSEC = 200; void setup() { Serial.begin(115200); Wire.setClock(100000); // initialize MPU6050 added 09/03/18 Serial.println(F("Initializing MPU6050 ...")); mpu.initialize(); // verify connection Serial.println(F("Testing device connections...")); Serial.println(mpu.testConnection() ? F("MPU6050 connection successful") : F("MPU6050 connection failed")); Serial.println(F("Initializing DMP...")); devStatus = mpu.dmpInitialize(); // make sure it worked (returns 0 if successful) if (devStatus == 0) { //12/06/19 no longer needed; cal vals already acquired // Calibration Time: generate offsets and calibrate our MPU6050 //mpu.CalibrateAccel(6); //mpu.CalibrateGyro(6); //Serial.println(); //mpu.PrintActiveOffsets(); // turn on the DMP, now that it's ready Serial.println(F("Enabling DMP...")); mpu.setDMPEnabled(true); // set our DMP Ready flag so the main loop() function knows it's okay to use it Serial.println(F("DMP ready! Waiting for MPU6050 drift rate to settle...")); dmpReady = true; // get expected DMP packet size for later comparison packetSize = mpu.dmpGetFIFOPacketSize(); bMPU6050Ready = true; mySerial.printf("\nMPU6050 Ready at %2.2f Sec\n", millis() / 1000.f); } else { // ERROR! // 1 = initial memory load failed // 2 = DMP configuration updates failed // (if it's going to break, usually the code will be 1) Serial.print(F("DMP Initialization failed (code ")); Serial.print(devStatus); Serial.println(F(")")); bMPU6050Ready = false; } sinceLastNavUpdateMsec = 0; } void loop() { if (sinceLastNavUpdateMsec >= NAV_UPDATE_INTERVAL_MSEC) { sinceLastNavUpdateMsec -= NAV_UPDATE_INTERVAL_MSEC; mpu.testConnection(); mpu.resetFIFO(); for (size_t i = 0; i < 10; i++) { int count = mpu.getFIFOCount(); delay(10); } } } |
This program sets up the connection to the MPU6050 and then once every 200 mSec tests the I2C connection, resets the FIFO, and then repeatedly checks the FIFO count to verify that the MPU6050 is actually doing something.
When I ran Kito’s I2C sniffer program on a Teensy 3.2 (taking care to switch the SCL & SDA lines as Kito’s code has it backwards), I get the following output
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
Opening port Port open ----------------- Legend: S=Start Sr=Start repeat P=stoP R=Read W=Write A=Ack N=Nak ----------------- Addr=0x68,W,N,P S,Addr=0x68,R,N,P S,Addr=0x68,W,N,P S,Addr=0x68,R,N,P S,Addr=0x68,W,N,P S,Addr=0x68,R,N,P S,Addr=0x68,W,N,P S,Addr=0x68,R,N,P S,Addr=0x68,W,N,P S,Addr=0x68,R,N,P S,Addr=0x68,W,N,P |
which isn’t very useful, when compared to the debug output from Jeff Rowberg’s I2CDev program, as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
I2C (0x68) writing 1 bytes to 0x6A...C4. Done. I2C (0x68) reading 2 bytes from 0x72...0 0. Done (2 read). I2C (0x68) reading 2 bytes from 0x72...0 38. Done (2 read). I2C (0x68) reading 2 bytes from 0x72...0 54. Done (2 read). I2C (0x68) reading 2 bytes from 0x72...0 70. Done (2 read). I2C (0x68) reading 2 bytes from 0x72...0 8C. Done (2 read). I2C (0x68) reading 2 bytes from 0x72...0 A8. Done (2 read). I2C (0x68) reading 2 bytes from 0x72...0 E0. Done (2 read). I2C (0x68) reading 2 bytes from 0x72...0 FC. Done (2 read). I2C (0x68) reading 2 bytes from 0x72...1 18. Done (2 read). I2C (0x68) reading 2 bytes from 0x72...1 34. Done (2 read). I2C (0x68) reading 1 bytes from 0x75...68. Done (1 read). I2C (0x68) reading 1 bytes from 0x6A...C0. Done (1 read). I2C (0x68) writing 1 bytes to 0x6A...C4. Done. I2C (0x68) reading 2 bytes from 0x72...0 1C. Done (2 read). I2C (0x68) reading 2 bytes from 0x72...0 38. Done (2 read). I2C (0x68) reading 2 bytes from 0x72...0 54. Done (2 read). I2C (0x68) reading 2 bytes from 0x72...0 70. Done (2 read). I2C (0x68) reading 2 bytes from 0x72...0 8C. Done (2 read). I2C (0x68) reading 2 bytes from 0x72...0 A8. Done (2 read). I2C (0x68) reading 2 bytes from 0x72...0 E0. Done (2 read). I2C (0x68) reading 2 bytes from 0x72...0 FC. Done (2 read). |
As can be seen from Jeff’s output, there is a LOT of data being missed by Kito’s program. It gets the initial sequence right (S,Addr=0x68,W,N,P), but skips the 8-bit data sequence after the ‘W’, and mis-detects the following RESTART as a STOP. The next sequence (S,Addr=0x68,R,N,P) is correct as far as the initial address is concerned, but again omits the 8-bit data value after the ‘R’ direction modifier.
Notwithstanding its problems, Kito’s program, along with this I2C bus specifications document did teach me a LOT about the I2C protocol and how to parse it effectively. In addition, Kito’s program showed me how to use direct port bus reads to bypass the overhead associated with ‘digitalRead()’ calls – nice!
I got lost pretty quickly trying to understand Kito’s programming logic, so I decided I would do what any good researcher does when trying to understand a complex situation – CHEAT!! I modified Kito’s program to simply capture the I2C bus transitions associated with my little test program into a 1024 byte buffer, then stop and print the contents of the buffer out to the serial port. Then I copy/pasted this output into an Excel spreadsheet and wrote a VBA script to parse through the output, line-by-line. By doing it this way, I could easily examine the result of each script change, and step through the script one line at a time, watching the parsing machinery run.
Here’s a partial output from the data capture program:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
Buffer full - Printing 1024 byte Contents! 0xc 0x4 0x0 0x8 0xc 0x8 0xc 0x0 0x4 0x8 0xc 0x0 0x4 0x0 0x4 0x0 0x4 0x0 0x4 0x0 0x4 0x8 0x0 0x4 0x0 0x8 0xc 0x8 0xc 0x8 0xc 0x8 0x0 0x4 0x0 0x8 0xc |
So then I copy/pasted this into Excel and wrote the following VBA script to parse the data:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 |
Sub Test() 'Purpose: Parse recorded bytes containing combined I2C SCL/SDA states 'Inputs: recorded bytes containing combined SCL & SDA states 'Outputs: Decoded I2C data 'Notes: ' Each I2C communication proceeds as follows: ' A START condition, denoted by SDA & SCL HIGH, followed by SDA LOW, SCL HIGH ' A 7-bit device address ' A R/W bit ' An ACK bit ' Data read/write, each data byte consisting of: ' 8 data bits ' ACK/NACK ' A Repeated START or STOP ' Other than the START/Repeated Start and the STOP conditions, all bit definitions ' start with the SCL line LOW, and finish with the SCL line HIGH, as follows: ' SCL LOW, SDA LOW, followed by SCL HIGH, SDA LOW = "0" or "WRITE" or "ACK" ' "WRITE" if it is the bit immediately following the 7 device ' address bits ' "ACK" if it is the bit immediately following 8 data bits or the ' bit immediately following the R/W bit ' Otherise it is a "0" ' SCL LOW, SDA HIGH, followed by SCL HIGH, SDA HIGH = "1" or "READ" or "NAK" ' "READ" if it is the bit immediately following the 7 device ' address bits ' "NAK" if it is the bit immediately following 8 data bits or the ' bit immediately following the R/W bit ' Otherise it is a "1" ' A special case occurs for a transition from SDA = HIGH/LOW to "0X0" (both LOW). ' This means the slave isn't ready for the next bit and has pulled the SCL ' line LOW. Processing has to skip bits until the next 0X0->0X4 or 0X08->0XC ' transition occurs to continue Dim startrange As Range Dim searchrange As Range Dim findrange As Range Dim datarange As Range With Worksheets("Sheet1") Set startrange = .Range("RawDataColStart") Set searchrange = .Range("A:A") 'Find first row with no text in the 'CA Cat' column (column B) command can fail - trap error On Error Resume Next Set findrange = searchrange.Find("", startrange, xlValues, , xlByRows, xlNext) Set datarange = Range(startrange.Address, findrange.Address) 'OK, now findrange.row is the row number of the first row with "" in the "CA Cat" column (column B) Dim datacell As Range Dim bitcount As Integer Dim startrow As Integer Dim startcount As Integer 'needed for repeated start detection Dim lastbitrow As Integer 'needed to put the byte value on correct row Dim Is7BitAddress As Boolean 'addresses are 7-bit, data is 8-bit Dim row As Integer 'needed for byRef sub/function calls ' Dim IsSTOP As Boolean 'added 12/25/19 for better code readability Dim i As Integer 'backup iteration counter If datarange.Rows.Count > 1 Then ' For row = 1 To datarange.Rows.Count row = 1 While row < datarange.Rows.Count If UCase(datarange.Cells(row, 1)) = "0XC" And UCase(datarange.Cells(row + 1, 1)) = "0X4" Then If startcount = 0 Then datarange.Cells(row + 1, 2) = "START" startcount = startcount + 1 'needed for later repeated start treatment Else datarange.Cells(row + 1, 2) = "RE-START" End If bitcount = 0 startrow = row 'need this to tell if bit 9 is 'ACK/NACK' or 'R/W' row = row + 2 'point to next line pair 'skip any NOP rows ' row = SkipNOPs(row, datarange) Call SkipNOPs(row, datarange) 'Get 7-bit address ' row = Build7BitAddress(row + 1, datarange) 'point to line following "START" or "RESTART bitcount = Build7BitAddress(row, datarange) 'point to first address line pair If bitcount = 7 Then 'success 'Read/Write Call GetReadWrite(row, datarange) 'point to line following last address bit 'ACK/NAK Call GetACKNAK(row, datarange) 'skip any NOP rows ' row = SkipNOPs(row, datarange) 'points to row after "START/RE-START" Call SkipNOPs(row, datarange) 'points to row after "START/RE-START" '8-bit Data Byte bitcount = Build8BitValue(row, datarange) ' 7-bit address always followed by an 8-bit value/addr If bitcount = 8 Then 'success 'ACK/NAK Call GetACKNAK(row, datarange) 'skip any NOP rows Call SkipNOPs(row, datarange) 'points to row after "START/RE-START" End If 'can have any number of 8-bit-ACK/NAK-SkipNOP cycles While (bitcount = 8 And IsSTOP(row, datarange) = 0 And IsSTART(row, datarange) = 0) '8-bit Data Byte bitcount = Build8BitValue(row, datarange) ' 7-bit address always followed by an 8-bit value/addr If bitcount = 8 Then 'success 'ACK/NAK Call GetACKNAK(row, datarange) 'skip any NOP rows Call SkipNOPs(row, datarange) 'points to row after "START/RE-START" End If Wend End If ElseIf UCase(datarange.Cells(row, 1)) = "0X4" And UCase(datarange.Cells(row + 1, 1)) = "0XC" Then datarange.Cells(row + 1, 2) = "STOP" bitcount = 0 startcount = 0 'a STOP resets repeated start detection counter ' row = row + 1 'needed to skip the "0X0" line after STOP detection End If row = row + 2 'move focus to next line pair 'skip any NOP rows Call SkipNOPs(row, datarange) Wend End If End With End Sub Function Build8BitDataValueStr(ByVal rownum As Integer) As String 'Inputs: ' rownum = integer denoting row containing LSB 'Outputs: ' string containing hexidecimal representation of preceding 8 bit strings 'Plan: ' start on this row and then every other row going backwards, ' adding up bit positions to obtain a decimal represntation of the 7-bit value, ' then convert to a hexidecimal number then convert the number to a string. Dim startrow As Integer, bitrow As Integer Dim bytedecval As Integer startrow = rownum For bitpos = 0 To 7 bitrow = startrow - bitpos * 2 ' Debug.Print ("row " & bitrow) If Cells(bitrow, 2) = "1" Then bytedecval = bytedecval + 2 ^ (bitpos) End If Next bitpos ' Debug.Print ("hex value = " & Hex(bytedecval)) Build8BitDataValueStr = Hex(bytedecval) End Function Function Build7BitAddress(ByRef row As Integer, datarange As Range) As Integer 'Purpose: build and display 7-bit address 'Inputs: ' row = line number of first address pair ' dr = datarange (column containing transition data) 'Outputs: ' displayed bit & bitnumber values ' displayed 7-bit address ' line number of next line pair For bitcount = 1 To 7 ' SCL LOW, SDA LOW, followed by SCL HIGH, SDA LOW = "0" If UCase(datarange.Cells(row, 1)) = "0X0" And UCase(datarange.Cells(row + 1, 1)) = "0X4" Then datarange.Cells(row + 1, 2) = "0" datarange.Cells(row + 1, 3) = bitcount ' SCL LOW, SDA HIGH, followed by SCL HIGH, SDA HIGH = "1" ElseIf UCase(datarange.Cells(row, 1)) = "0X8" And UCase(datarange.Cells(row + 1, 1)) = "0XC" Then datarange.Cells(row + 1, 2) = "1" datarange.Cells(row + 1, 3) = bitcount Else: Exit For End If row = row + 2 'skip to next bit pair Next bitcount If bitcount = 8 Then 'Now build and display the address string Dim AddrStr As String AddrStr = Build7BitAddressStr(row - 1) datarange.Cells(row - 1, 4) = AddrStr bitcount = bitcount - 1 End If Build7BitAddress = bitcount 'on success, should be 7 End Function Function Build7BitAddressStr(ByVal rownum As Integer) As String 'Inputs: ' rownum = integer denoting row containing LSB 'Outputs: ' string containing hexidecimal representation of preceding 7 bit strings 'Plan: ' start on this row and then every other row going backwards, ' adding up bit positions to obtain a decimal represntation of the 7-bit value, ' then convert to a hexidecimal number then convert the number to a string. Dim startrow As Integer, bitrow As Integer Dim bytedecval As Integer ' startrow = rownum - 2 'rownum arg actually points to W/R row startrow = rownum For bitpos = 0 To 7 bitrow = startrow - bitpos * 2 ' Debug.Print ("row " & bitrow) If Cells(bitrow, 2) = "1" Then If bitpos = 0 Then bytedecval = 1 Else bytedecval = bytedecval + 2 ^ (bitpos) End If End If Next bitpos ' Debug.Print ("hex value = " & Hex(bytedecval)) Build7BitAddressStr = Hex(bytedecval) End Function Function Build8BitValue(ByRef row As Integer, datarange As Range) As Integer 'Purpose: build & display 8-bit data bits 'Inputs: ' row = row containing 2nd transition of ACK/NAK pair (points at the ACK/NAK display line) ' dr = datarange (column containing transition data) 'Outputs: ' displayed bit & bitnumber values ' displayed 8-bit value ' displayed ACK/NAK value ' row number of second transition value for ACK/NAK pair (points at the ACK/NAK display line) 'Notes: ' there may be one or more 'NOP' transitions before the first data bit transition pair For bitcount = 1 To 8 ' SCL LOW, SDA LOW, followed by SCL HIGH, SDA LOW = "0" If UCase(datarange.Cells(row, 1)) = "0X0" And UCase(datarange.Cells(row + 1, 1)) = "0X4" Then datarange.Cells(row + 1, 2) = "0" datarange.Cells(row + 1, 3) = bitcount ' SCL LOW, SDA HIGH, followed by SCL HIGH, SDA HIGH = "1" ElseIf UCase(datarange.Cells(row, 1)) = "0X8" And UCase(datarange.Cells(row + 1, 1)) = "0XC" Then datarange.Cells(row + 1, 2) = "1" datarange.Cells(row + 1, 3) = bitcount Else 'oops - something went wrong row = row - 2 'back up row counter to start of last good pair Exit For End If row = row + 2 'skip to next bit pair Next bitcount If bitcount = 9 Then '9 here means a full 8-bit byte has been processed 'Now build and display the address string Dim DataValStr As String DataValStr = Build8BitDataValueStr(row - 1) datarange.Cells(row - 1, 4) = DataValStr End If Build8BitValue = bitcount - 1 End Function 'Function SkipNOPs(ByVal row As Integer, datarange As Range) As Integer Sub SkipNOPs(ByRef row As Integer, datarange As Range) 'check for NOP transitions While ((UCase(datarange.Cells(row, 1)) = "0X8" And UCase(datarange.Cells(row + 1, 1)) = "0X0") _ Or (UCase(datarange.Cells(row, 1)) = "0X8" And UCase(datarange.Cells(row + 1, 1)) = "0X4") _ Or (UCase(datarange.Cells(row, 1)) = "0X0" And UCase(datarange.Cells(row + 1, 1)) = "0X8") _ ) row = row + 1 Wend ' SkipNOPs = row End Sub Sub GetReadWrite(ByRef row As Integer, datarange As Range) 'Next line pair is W/R ' SCL LOW, SDA LOW, followed by SCL HIGH, SDA LOW = "WRITE" If UCase(datarange.Cells(row, 1)) = "0X0" And UCase(datarange.Cells(row + 1, 1)) = "0X4" Then datarange.Cells(row + 1, 2) = "WRITE" ' SCL LOW, SDA HIGH, followed by SCL HIGH, SDA HIGH = "READ" ElseIf UCase(datarange.Cells(row, 1)) = "0X8" And UCase(datarange.Cells(row + 1, 1)) = "0XC" Then datarange.Cells(row + 1, 2) = "READ" End If row = row + 2 End Sub Sub GetACKNAK(ByRef row As Integer, datarange As Range) 'Next line pair is ACK/NAK ' SCL LOW, SDA LOW, followed by SCL HIGH, SDA LOW = "ACK" If UCase(datarange.Cells(row, 1)) = "0X0" And UCase(datarange.Cells(row + 1, 1)) = "0X4" Then datarange.Cells(row + 1, 2) = "ACK" ' SCL LOW, SDA HIGH, followed by SCL HIGH, SDA HIGH = "NAK" ElseIf UCase(datarange.Cells(row, 1)) = "0X8" And UCase(datarange.Cells(row + 1, 1)) = "0XC" Then datarange.Cells(row + 1, 2) = "NAK" End If row = row + 2 End Sub Function IsSTOP(ByRef row As Integer, datarange As Range) As Boolean If UCase(datarange.Cells(row, 1)) = "0X4" And UCase(datarange.Cells(row + 1, 1)) = "0XC" Then IsSTOP = True Else IsSTOP = False End If End Function Function IsSTART(ByRef row As Integer, datarange As Range) As Boolean If UCase(datarange.Cells(row, 1)) = "0XC" And UCase(datarange.Cells(row + 1, 1)) = "0X4" Then IsSTART = True Else IsSTART = False End If End Function |
The above script assumes the data is in column A, starting at A1. A partial output from the program is shown below, showing the first few sequences
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 |
0xc 0x4 START 0x0 0x8 0xc 1 1 0x8 0xc 1 2 0x0 0x4 0 3 0x8 0xc 1 4 0x0 0x4 0 5 0x0 0x4 0 6 0x0 0x4 0 7 68 0x0 0x4 WRITE 0x0 0x4 ACK 0x8 0x0 0x4 0 1 0x0 0x8 0xc 0x8 0xc 0x8 0xc 0x8 0x0 0x4 0x0 0x8 0xc 0x8 0x0 0x4 0x0 0x8 0xc 0x8 0x0 0x4 0x0 0x4 0xc 0x4 RE-START 0x0 0x8 0xc 1 1 0x8 0xc 1 2 0x8 0x0 0x4 0x0 0x8 0xc 0x8 0x0 0x4 0x0 0x4 0x0 0x4 0x0 0x8 0xc 0x8 0x0 0x4 0x0 0x4 0x8 0xc 0x8 0xc 0x0 0x4 0x8 0xc 0x0 0x4 0x0 0x4 0x0 0x4 0x8 0xc 0x8 0x0 0x4 0xc 0x4 RE-START 0x0 0x8 0xc 1 1 0x8 0xc 1 2 0x0 0x4 0 3 0x8 0xc 1 4 0x0 0x4 0 5 0x0 0x4 0 6 0x0 0x4 0 7 68 0x0 0x4 WRITE 0x0 0x4 ACK 0x8 0x0 0x4 0 1 0x8 0xc 1 2 0x8 0xc 1 3 0x0 0x4 0 4 0x8 0xc 1 5 0x0 0x4 0 6 0x8 0xc 1 7 0x0 0x4 0 8 6A 0x0 0x4 ACK 0x0 0x4 0 1 0xc 0x4 RE-START 0x0 0x8 0xc 1 1 0x8 0xc 1 2 0x0 0x4 0 3 0x8 0xc 1 4 0x0 0x4 0 5 0x0 0x4 0 6 0x0 0x4 0 7 68 0x8 0xc READ 0x0 0x4 ACK 0x8 0xc 1 1 0x8 0xc 1 2 0x0 0x4 0 3 0x0 0x4 0 4 0x0 0x4 0 5 0x0 0x4 0 6 0x0 0x4 0 7 0x0 0x4 0 8 C0 |
The above output corresponds to this line in the debug output from Jeff Rowberg’s I2Cdev code:
1 |
I2C (0x68) reading 1 bytes from 0x6A...C0. Done (1 read). |
So, the VBA program is parsing OK-ish, but is missing big chunks, and there are some weird 1 and 2 bit sequences floating around too.
After some more research, I finally figured out that part of the problem is that the I2C protocol allows a slave device to pull the SCL line low unilaterally to temporarily suspend transmissions until the slave device catches up. This causes ‘NOP’ sequences to appear more or less randomly in the data stream. So, I again modified Kito’s program to first capture a 1024 byte data sample, and then parse through the sample, eliminating any NOP sequences. The result is a ‘clean’ data sample. Here’s the modified Kito program
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 |
/* Name: MyLA3.ino Created: 5/28/2015 11:53:48 AM Author: kito Sniffs i2c bus by polling SCL0(pin19)and SDA0(pin18)sda pins. Displays i2c commands in easy to read text output. //12/20/19 edited by gfp to correct SCL/SDA pin switch //See https://i2c.info/ for protocol details //12/27/19 re-written to capture a short sample and then edit the //sample to remove any NOP codes */ #include <TimerOne.h> #define SDA_PIN 18 #define SCL_PIN 19 void setup(void) { interrupts(); pinMode(2, OUTPUT); digitalWriteFast(2, LOW); pinMode(LED_BUILTIN, OUTPUT); pinMode(SDA_PIN, INPUT); pinMode(SCL_PIN, INPUT); Timer1.initialize(1); // run every mico second Timer1.attachInterrupt(capture_data); Serial.begin(115200); while (!Serial); digitalWriteFast(2, LOW); } #define RAWSIZE 1024 uint8_t raw_read_data[RAWSIZE]; // buffer to capture io pin changes uint8_t valid_read_data[RAWSIZE]; // buffer to capture valid sequences uint16_t write_index = 0; uint16_t read_index = 0; uint8_t current_portb = 0xFF; uint8_t last_portb = 0xFF; bool bBufferFull = false; //12/21/19 modified to just capture one buffer load & then stop void capture_data(void) { last_portb = current_portb; current_portb = GPIOB_PDIR & 12; //reads state of SDA (18) & SCL (19) at same time if (last_portb != current_portb && !bBufferFull) { raw_read_data[write_index++] = current_portb; digitalWriteFast(2, !digitalReadFast(2)); } if (write_index >= RAWSIZE) bBufferFull = true; } uint32_t loop_count = 0; uint8_t data_current = 0; uint8_t data_previous; int i2c_start_count = 0; int i2c_index = 0; uint8_t byte_build = 0; uint8_t byte_index = 0; void loop(void) { if (bBufferFull) { Serial.printf("Buffer full - Printing %d byte Contents!\n", write_index); for (size_t i = 0; i < RAWSIZE; i++) { Serial.printf("0x%x\n", raw_read_data[i]); } //OK, now go back through the array, excising invalid sequences uint rawidx = 0; data_previous = raw_read_data[rawidx]; uint valididx = 0; for (rawidx = 1; rawidx < RAWSIZE;) { data_current = raw_read_data[rawidx]; bool validpair = ( (data_previous == 0xC && data_current == 0x4) //START or RESTART || (data_previous == 0x4 && data_current == 0xC) //STOP || (data_previous == 0x0 && data_current == 0x4) //0 OR ACK || (data_previous == 0x8 && data_current == 0xC) //1 or NAK ); Serial.printf("idx %d: Considering dp = %x, dc = %x: validity = %d\n", rawidx, data_previous, data_current, validpair); if (validpair) { valid_read_data[valididx] = data_previous; Serial.printf("Wrote %x into %d, ", valid_read_data[valididx], valididx); valididx++; //bump counter to next pair valid_read_data[valididx] = data_current; Serial.printf("Wrote %x into %d\n", valid_read_data[valididx], valididx); valididx++; //pont to next blank entry rawidx++; data_previous = raw_read_data[rawidx]; rawidx++; } else { data_previous = data_current; rawidx++; //only bump 1 } } delay(1000); Serial.printf("Buffer processed - Printing %d byte Contents of valid_read_data array\n", valididx); for (size_t i = 0; i < valididx; i++) { Serial.printf("0x%x\n", valid_read_data[i]); } Serial.printf("\n\nDone - Stopping Program!\n"); while (1) { } } } |
and a partial output from the run:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
Buffer full - Printing 1024 byte Contents! 0xc 0x4 0x0 0x8 0xc 0x8 ........ ........ idx 1: Considering dp = c, dc = 4: validity = 1 Wrote c into 0, Wrote 4 into 1 idx 3: Considering dp = 0, dc = 8: validity = 0 idx 4: Considering dp = 8, dc = c: validity = 1 Wrote 8 into 2, Wrote c into 3 idx 6: Considering dp = 8, dc = c: validity = 1 Wrote 8 into 4, Wrote c into 5 idx 8: Considering dp = 0, dc = 4: validity = 1 Wrote 0 into 6, Wrote 4 into 7 ...... ...... ...... Buffer processed - Printing 928 byte Contents of valid_read_data array 0xc 0x4 0x8 0xc 0x8 0xc 0x0 0x4 0x8 0xc 0x0 0x4 0x0 0x4 0x0 |
After processing all 1024 transition codes, 96 invalid transitions were removed, resulting in 928 valid I2C transitions.
When this data was copy/pasted into my Excel VBA program, it was able to correctly parse the entire sample correctly, as shown below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 |
0xc 0x4 START 0x8 0xc 1 1 0x8 0xc 1 2 0x0 0x4 0 3 0x8 0xc 1 4 0x0 0x4 0 5 0x0 0x4 0 6 0x0 0x4 0 7 68 0x0 0x4 WRITE 0x0 0x4 ACK 0x0 0x4 0 1 0x8 0xc 1 2 0x8 0xc 1 3 0x8 0xc 1 4 0x0 0x4 0 5 0x8 0xc 1 6 0x0 0x4 0 7 0x8 0xc 1 8 75 0x0 0x4 ACK 0x0 0x4 0 1 0xc 0x4 RE-START 0x8 0xc 1 1 0x8 0xc 1 2 0x0 0x4 0 3 0x8 0xc 1 4 0x0 0x4 0 5 0x0 0x4 0 6 0x0 0x4 0 7 68 0x8 0xc READ 0x0 0x4 ACK 0x0 0x4 0 1 0x8 0xc 1 2 0x8 0xc 1 3 0x0 0x4 0 4 0x8 0xc 1 5 0x0 0x4 0 6 0x0 0x4 0 7 0x0 0x4 0 8 68 0x8 0xc NAK 0x0 0x4 0 1 0xc 0x4 RE-START 0x8 0xc 1 1 0x8 0xc 1 2 0x0 0x4 0 3 0x8 0xc 1 4 0x0 0x4 0 5 0x0 0x4 0 6 0x0 0x4 0 7 68 0x0 0x4 WRITE 0x0 0x4 ACK 0x0 0x4 0 1 0x8 0xc 1 2 0x8 0xc 1 3 0x0 0x4 0 4 0x8 0xc 1 5 0x0 0x4 0 6 0x8 0xc 1 7 0x0 0x4 0 8 6A 0x0 0x4 ACK 0x0 0x4 0 1 0xc 0x4 RE-START 0x8 0xc 1 1 0x8 0xc 1 2 0x0 0x4 0 3 0x8 0xc 1 4 0x0 0x4 0 5 0x0 0x4 0 6 0x0 0x4 0 7 68 0x8 0xc READ 0x0 0x4 ACK 0x8 0xc 1 1 0x8 0xc 1 2 0x0 0x4 0 3 0x0 0x4 0 4 0x0 0x4 0 5 0x0 0x4 0 6 0x0 0x4 0 7 0x0 0x4 0 8 C0 0x8 0xc NAK 0x0 0x4 0 1 0xc 0x4 RE-START 0x8 0xc 1 1 0x8 0xc 1 2 0x0 0x4 0 3 0x8 0xc 1 4 0x0 0x4 0 5 0x0 0x4 0 6 0x0 0x4 0 7 68 0x0 0x4 WRITE 0x0 0x4 ACK 0x0 0x4 0 1 0x8 0xc 1 2 0x8 0xc 1 3 0x0 0x4 0 4 0x8 0xc 1 5 0x0 0x4 0 6 0x8 0xc 1 7 0x0 0x4 0 8 6A 0x0 0x4 ACK 0x8 0xc 1 1 0x8 0xc 1 2 0x0 0x4 0 3 0x0 0x4 0 4 0x0 0x4 0 5 0x8 0xc 1 6 0x0 0x4 0 7 0x0 0x4 0 8 C4 0x0 0x4 ACK 0x0 0x4 0 1 0xc 0x4 RE-START 0x8 0xc 1 1 0x8 0xc 1 2 0x0 0x4 0 3 0x8 0xc 1 4 0x0 0x4 0 5 0x0 0x4 0 6 0x0 0x4 0 7 68 0x0 0x4 WRITE 0x0 0x4 ACK 0x0 0x4 0 1 0x8 0xc 1 2 0x8 0xc 1 3 0x8 0xc 1 4 0x0 0x4 0 5 0x0 0x4 0 6 0x8 0xc 1 7 0x0 0x4 0 8 72 0x0 0x4 ACK 0x0 0x4 0 1 0xc 0x4 RE-START 0x8 0xc 1 1 0x8 0xc 1 2 0x0 0x4 0 3 0x8 0xc 1 4 0x0 0x4 0 5 0x0 0x4 0 6 0x0 0x4 0 7 68 0x8 0xc READ 0x0 0x4 ACK 0x0 0x4 0 1 0x0 0x4 0 2 0x0 0x4 0 3 0x0 0x4 0 4 0x0 0x4 0 5 0x0 0x4 0 6 0x0 0x4 0 7 0x0 0x4 0 8 0 0x0 0x4 ACK 0x0 0x4 0 1 0x0 0x4 0 2 0x0 0x4 0 3 0x8 0xc 1 4 0x8 0xc 1 5 0x8 0xc 1 6 0x0 0x4 0 7 0x0 0x4 0 8 1C 0x8 0xc NAK 0x0 0x4 0 1 0xc 0x4 RE-START 0x8 0xc 1 1 0x8 0xc 1 2 0x0 0x4 0 3 0x8 0xc 1 4 0x0 0x4 0 5 0x0 0x4 0 6 0x0 0x4 0 7 68 0x0 0x4 WRITE 0x0 0x4 ACK 0x0 0x4 0 1 0x8 0xc 1 2 0x8 0xc 1 3 0x8 0xc 1 4 0x0 0x4 0 5 0x0 0x4 0 6 0x8 0xc 1 7 0x0 0x4 0 8 72 0x0 0x4 ACK 0x0 0x4 0 1 0xc 0x4 RE-START 0x8 0xc 1 1 0x8 0xc 1 2 0x0 0x4 0 3 0x8 0xc 1 4 0x0 0x4 0 5 0x0 0x4 0 6 0x0 0x4 0 7 68 0x8 0xc READ 0x0 0x4 ACK 0x0 0x4 0 1 0x0 0x4 0 2 0x0 0x4 0 3 0x0 0x4 0 4 0x0 0x4 0 5 0x0 0x4 0 6 0x0 0x4 0 7 0x0 0x4 0 8 0 0x0 0x4 ACK 0x0 0x4 0 1 0x0 0x4 0 2 0x8 0xc 1 3 0x8 0xc 1 4 0x8 0xc 1 5 0x0 0x4 0 6 0x0 0x4 0 7 0x0 0x4 0 8 38 0x8 0xc NAK 0x0 0x4 0 1 0xc 0x4 RE-START 0x8 0xc 1 1 0x8 0xc 1 2 0x0 0x4 0 3 0x8 0xc 1 4 0x0 0x4 0 5 0x0 0x4 0 6 0x0 0x4 0 7 68 0x0 0x4 WRITE 0x0 0x4 ACK 0x0 0x4 0 1 0x8 0xc 1 2 0x8 0xc 1 3 0x8 0xc 1 4 0x0 0x4 0 5 0x0 0x4 0 6 0x8 0xc 1 7 0x0 0x4 0 8 72 0x0 0x4 ACK 0x0 0x4 0 1 0xc 0x4 RE-START 0x8 0xc 1 1 0x8 0xc 1 2 0x0 0x4 0 3 0x8 0xc 1 4 0x0 0x4 0 5 0x0 0x4 0 6 0x0 0x4 0 7 68 0x8 0xc READ 0x0 0x4 ACK 0x0 0x4 0 1 0x0 0x4 0 2 0x0 0x4 0 3 0x0 0x4 0 4 0x0 0x4 0 5 0x0 0x4 0 6 0x0 0x4 0 7 0x0 0x4 0 8 0 0x0 0x4 ACK 0x0 0x4 0 1 0x8 0xc 1 2 0x0 0x4 0 3 0x8 0xc 1 4 0x0 0x4 0 5 0x8 0xc 1 6 0x0 0x4 0 7 0x0 0x4 0 8 54 0x8 0xc NAK 0x0 0x4 0 1 0xc 0x4 RE-START 0x8 0xc 1 1 0x8 0xc 1 2 0x0 0x4 0 3 0x8 0xc 1 4 0x0 0x4 0 5 0x0 0x4 0 6 0x0 0x4 0 7 68 0x0 0x4 WRITE 0x0 0x4 ACK 0x0 0x4 0 1 0x8 0xc 1 2 0x8 0xc 1 3 0x8 0xc 1 4 0x0 0x4 0 5 0x0 0x4 0 6 0x8 0xc 1 7 0x0 0x4 0 8 72 0x0 0x4 ACK 0x0 0x4 0 1 0xc 0x4 RE-START 0x8 0xc 1 1 0x8 0xc 1 2 0x0 0x4 0 3 0x8 0xc 1 4 0x0 0x4 0 5 0x0 0x4 0 6 0x0 0x4 0 7 68 0x8 0xc READ 0x0 0x4 ACK 0x0 0x4 0 1 0x0 0x4 0 2 0x0 0x4 0 3 0x0 0x4 0 4 0x0 0x4 0 5 0x0 0x4 0 6 0x0 0x4 0 7 0x0 0x4 0 8 0 0x0 0x4 ACK 0x0 0x4 0 1 0x8 0xc 1 2 0x8 0xc 1 3 0x8 0xc 1 4 0x0 0x4 0 5 0x0 0x4 0 6 0x0 0x4 0 7 0x0 0x4 0 8 70 0x8 0xc NAK 0x0 0x4 0 1 0xc 0x4 RE-START 0x8 0xc 1 1 0x8 0xc 1 2 0x0 0x4 0 3 0x8 0xc 1 4 0x0 0x4 0 5 0x0 0x4 0 6 0x0 0x4 0 7 68 0x0 0x4 WRITE 0x0 0x4 ACK 0x0 0x4 0 1 0x8 0xc 1 2 0x8 0xc 1 3 0x8 0xc 1 4 0x0 0x4 0 5 0x0 0x4 0 6 0x8 0xc 1 7 0x0 0x4 0 8 72 0x0 0x4 ACK 0x0 0x4 0 1 0xc 0x4 RE-START 0x8 0xc 1 1 0x8 0xc 1 2 0x0 0x4 0 3 0x8 0xc 1 4 0x0 0x4 0 5 0x0 0x4 0 6 0x0 0x4 0 7 68 0x8 0xc READ 0x0 0x4 ACK 0x0 0x4 0 1 0x0 0x4 0 2 0x0 0x4 0 3 0x0 0x4 0 4 0x0 0x4 0 5 0x0 0x4 0 6 0x0 0x4 0 7 0x0 0x4 0 8 0 0x0 0x4 ACK 0x8 0xc 1 1 0x0 0x4 0 2 0x0 0x4 0 3 0x0 0x4 0 4 0x8 0xc 1 5 0x8 0xc 1 6 0x0 0x4 0 7 0x0 0x4 0 8 8C 0x8 0xc NAK 0x0 0x4 0 1 0xc 0x4 RE-START 0x8 0xc 1 1 0x8 0xc 1 2 0x0 0x4 0 3 0x8 0xc 1 4 0x0 0x4 0 5 0x0 0x4 0 6 0x0 0x4 0 7 68 0x0 0x4 WRITE 0x0 0x4 ACK 0x0 0x4 0 1 0x8 0xc 1 2 0x8 0xc 1 3 0x8 0xc 1 4 0x0 0x4 0 5 0x0 0x4 0 6 0x8 0xc 1 7 0x0 0x4 0 8 72 0x0 0x4 ACK 0x0 0x4 0 1 0xc 0x4 RE-START 0x8 0xc 1 1 0x8 0xc 1 2 0x0 0x4 0 3 0x8 0xc 1 4 0x0 0x4 0 5 0x0 0x4 0 6 0x0 0x4 0 7 68 0x8 0xc READ 0x0 0x4 ACK 0x0 0x4 0 1 0x0 0x4 0 2 0x0 0x4 0 3 0x0 0x4 0 4 0x0 0x4 0 5 0x0 0x4 0 6 0x0 0x4 0 7 0x0 0x4 0 8 0 0x0 0x4 ACK 0x8 0xc 1 1 0x8 0xc 1 2 0x0 0x4 0 3 0x0 0x4 0 4 0x0 0x4 0 5 0x8 0xc 1 6 0x0 0x4 0 7 0x0 0x4 0 8 C4 0x8 0xc NAK 0x0 0x4 0 1 0xc 0x4 RE-START 0x8 0xc 1 1 0x8 0xc 1 2 0x0 0x4 0 3 0x8 0xc 1 4 0x0 0x4 0 5 0x0 0x4 0 6 0x0 0x4 0 7 68 0x0 0x4 WRITE 0x0 0x4 ACK 0x0 0x4 0 1 0x8 0xc 1 2 0x8 0xc 1 3 0x8 0xc 1 4 0x0 0x4 0 5 0x0 0x4 0 6 0x8 0xc 1 7 0x0 0x4 0 8 72 0x0 0x4 ACK 0x0 0x4 0 1 0xc 0x4 RE-START 0x8 0xc 1 1 0x8 0xc 1 2 0x0 0x4 0 3 0x8 0xc 1 4 0x0 0x4 0 5 0x0 0x4 0 6 0x0 0x4 0 7 68 0x8 0xc READ 0x0 0x4 ACK 0x0 0x4 0 1 0x0 0x4 0 2 0x0 0x4 0 3 0x0 0x4 0 4 0x0 0x4 0 5 0x0 0x4 0 6 0x0 0x4 0 7 0x0 0x4 0 8 0 0x0 0x4 ACK 0x8 0xc 1 1 0x8 0xc 1 2 0x8 0xc 1 3 0x0 0x4 0 4 0x0 0x4 0 5 0x0 0x4 0 6 0x0 0x4 0 7 0x0 0x4 0 8 E0 0x8 0xc NAK 0x0 0x4 0 1 0xc 0x4 RE-START 0x8 0xc 1 1 0x8 0xc 1 2 0x0 0x4 0 3 0x8 0xc 1 4 0x0 0x4 0 5 0x0 0x4 0 6 0x0 0x4 0 7 68 0x0 0x4 WRITE 0x0 0x4 ACK 0x0 0x4 0 1 0x8 0xc 1 2 |
This corresponds to the following lines from Jeff’s program:
1 2 3 4 5 6 7 8 9 10 |
I2C (0x68) reading 1 bytes from 0x75...68. Done (1 read). I2C (0x68) reading 1 bytes from 0x6A...C0. Done (1 read). I2C (0x68) writing 1 bytes to 0x6A...C4. Done. I2C (0x68) reading 2 bytes from 0x72...0 1C. Done (2 read). I2C (0x68) reading 2 bytes from 0x72...0 38. Done (2 read). I2C (0x68) reading 2 bytes from 0x72...0 54. Done (2 read). I2C (0x68) reading 2 bytes from 0x72...0 70. Done (2 read). I2C (0x68) reading 2 bytes from 0x72...0 8C. Done (2 read). I2C (0x68) reading 2 bytes from 0x72...0 C4. Done (2 read). I2C (0x68) reading 2 bytes from 0x72...0 E0. Done (2 read). |
Although the VBA code correctly parsed all the data and missed nothing, there is still a small ‘fly in the ointment’; there is still an extra ‘0’ bit after every transmission sequence. Instead of
1 |
START - Addr - R/W - ACK/NAK - Data - ACK/NAK - RESTART - Addr - R/W - ACK/NAK - Data - ACK/NAK |
we have
1 |
START - Addr - R/W - ACK/NAK - Data - ACK/NAK - 0 - RESTART - Addr - R/W - ACK/NAK - Data - ACK/NAK |
with an extra ‘0’ between the ACK/NAK and the RESTART. This appears in every transmission sequence, so it must be a real part of the I2C protocol, but I haven’t yet found an explanation for it.
In any case, it is clear that the Excel VBA program is correctly parsing the captured sequence, so I should now be able to port it into C++ code for my Teensy I2C sniffer.
Stay tuned!
Frank