Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 11 of 11

Thread: I2C Sniffer transition parsing problem

  1. #1

    I2C Sniffer transition parsing problem

    I have been playing with Kito's I2C sniffer code with a Teensy 3.2 connected to a MPU6050. In order to understand what is going on, I wrote a small test program to hold a VERY simple conversation with the MPU6050 (repeatedly asking it for it's ID code), and then recorded the I2C bus transitions using Kito's program (modified to just write 1024 transition values into an array and then print them all back out again after the array is filled).

    Then I wrote an Excel VBA program to parse through all the transition pairs to make sure I understood what the various transition pairs meant (i.e. a 0xC followed by 0x4 ==> START, 0x0 followed by 0x4 ==> 0 or ACK, etc).

    In doing this, I discovered some transitions pairs that don't make sense, but must be treated properly (I call them 'NOP' sequences, like the following:

    HTML Code:
    0xc		
    0x4	RE-START	
    0x0		
    0x8		
    0xc	1	1
    The 0xC/0x4 transition is properly parsed as 'RESTART', but the next transition pair (0x0/0x8) isn't a valid transition. However, if the 0x0 is skipped, then the 0x8/0xc pair is a valid '1' code. From the I2C docs at https://i2c.info/i2c-bus-specification I gathered that this may be due to the MPU6050 holding the SCL line low while it does some processing. These 'NOP' sequences only happen after a 7-bit ADDR - R/W - ACK/NAK sequence or a 8-bit DATABYTE - ACK/NAK sequence and I revised my program to handle this case

    However, I ran into a case where a 'NOP' sequence occurred in the middle of an 8-bit DATABYTE sequence, which I thought was a real NO-NO. So I'm not sure whether this was just an error in the data collection program (seems unlikely, but...) or something in the I2C specs that I don't fully understand (could easily be the case...) or something else entirely.

    So here's the section of the data that's driving me crazy. It starts with a normal RESTART - 7-bit ADDR - R/W - ACK - 8-bit DATABYTE sequence, and then repeats, except the second iteration doesn't complete because of a 'NOP' sequence after the 1st (MSB) bit of the 8-bit DATABYTE section

    HTML Code:
    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		
    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		
    0x8			
    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
    0x0			
    0x4	WRITE		
    0x0			
    0x4	ACK		
    0x8			
    0x0			
    0x4	0	1	
    0x0			
    0x8			
    0xc			
    0x8			
    0xc			
    0x8			
    0xc			
    0x8			
    0x0			
    0x4			
    0x8			
    0xc			
    0x8			
    0x0			
    0x4			
    0x0

    Zeroing in on the problem section, you see the following:

    HTML Code:
    0x4	WRITE	
    0x0		
    0x4	ACK	
    0x8		
    0x0		
    0x4	0	1
    0x0		
    0x8		
    0xc		
    0x8
    The WRITE pair and the ACK pair are followed by one 'NOP' transition (expected and parsed correctly), then a 0x0/0x4 pair parsed as a '0' bit. However, the next transition pair is 0x0/0x8, which is an invalid transition pair in the middle of an 8-bit DATABYTE section.

    Is this sort of behavior allowed in the I2C specs? Do I have to worry about NOP sequences anywhere in a conversation, even inside ADDR/DATABYTE sequences?

    TIA,

    Frank

  2. #2
    Well, I finally figured out how to remove the NOP bytes from the data stream, and as a result my Excel VBA program will correctly parse the entire capture buffer (minus the NOPs, of course). When I parsed a repetitive sequence of 'mpu.testConnection();' calls, I get the following results

    Code:
    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		
    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
    which is great, but I didn't expect the odd occurrence of 1 byte of data after each second address/data section, as shown below:

    Code:
    0x4	0	8	68	
    0x8				
    0xc	NAK			
    0x0				
    0x4	0	1		
    0xc				
    0x4	RE-START
    Any idea what this is all about? It doesn't seem to affect anything, but it is a bit disconcerting to start cranking out an 8-bit data byte only to have it terminate after the first bit!

    TIA,

    Frank

  3. #3
    It’s been years since I had to dig deep into I2C, and then it was work, so I used a Beagle protocol analyzer - very handy.

    But I wonder, have you monitored the same transaction using kito’s original program, to see how it decodes the sequences in question?

  4. #4
    tele_player,

    I was hoping to avoid the cost of a Beagle, as my per-hour labor cost these days is zero $/0 ;-). I have tried to use Kito's original program (and still use his transition capture routine), but his original code has some significant problems that make it difficult to use.

    Frank

  5. #5
    Understood - but I still wonder if the original program behaves differently on these sequences.

  6. #6
    tele_player,

    Here's some output from the original un-modified Kito code using the same test program. Judge for yourself:

    Code:
    Opening port
    Port open
    -----------------
    Legend:
     S=Start
     Sr=Start repeat
     P=stoP
     R=Read
     W=Write
     A=Ack
     N=Nak
    -----------------
    P
    S,P
    P
    P
    P
    S,P
    P
    P
    S,P
    S,P
    P
    P
    S,P
    S,P
    S,P
    S,P
    P
    S,P
    S,P
    S,P
    P
    S,P
    P
    As you can see, there's not a whole lot of information there, and it entirely misses all the actual data in the I2C stream.

    Frank

  7. #7
    Oops, forgot that Kito's program reverses the SCL & SDA lines: When the lines are switched to conform to the code, I get

    Code:
    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
    Still not very helpful, as it omits the actual data

  8. #8
    I've created an Excel VBA program that correctly parses an arbitrary I2C transmission stream. See my Paynter's Palace post here for all the gory details.

    As noted in the post, I'm still not sure what is causing the extra '0' to appear in the transmission sequence, but it is clearly a consistent part of the sequence. I have looked around in the protocol documents, but haven't found anything relevant. Anyone else have a clue?

    TIA,

    Frank

  9. #9
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    10,633
    That looks very cool Frank. Did I read right that the VB parsing in moving to c++ will be done on the Teensy?

    Is there any value in tracking time - and flag where bits don't parse right? Time is driven by the sample rate to the buffer so when all goes right the only skips would be the slave delays and time between transactions. But when things fail it might point to an issue.

  10. #10
    defragster,

    Yes, the plan is to port the VBA code to a Teensy 3.2 I have lying around, (and maybe later to a Teensy 4!).

    Yes, I think time-tagging has merit, especially, as you said, when things go wrong. But first I need to convince myself that my sniffer-to-be will actually work over long periods (up to 12 hours as it sometimes takes that long for my MPU6050 setup to fail) so I can use it to figure out who is the culprit in these failures.

    In the long run I envision a sniffer with a 'look-back buffer' and some capability to detect I2C bus anomalies. The idea would be that a anomaly detection would freeze the sniffer some short time after detection so that the event that caused the anomaly would be captured in the middle of the look-back buffer. At the moment though, I plan to use the VS2019 serial logging feature as the look-back buffer (AFAIK, the VS2019 serial port log length is limited only by my 1TB SSD).

    I plan to post the Teensy code here, in the hopes that the real experts on this forum can help me make it better/faster. My planned application is for a wall-following robot that acquires a single yaw value from the MPU6050 every 200 mSec, so a 'burst capture then analyze/print' method should be OK. However, a real-time 100KHz I2C capture/display system would be pretty cool, too!

    Frank

  11. #11
    Here's the Teensy 3.2 code for parsing a small sample of I2C communications with a MPU6050 IMU. The data used here are the same as the ones used for my previous Excel VBA project, but it turned out that the algorithm was much simpler than I previously thought.

    This code uses a fixed array of captured data, but of course that won't work for a real I2C sniffer. My plan is to marry this algorithm to a FIFO implementation (perhaps tonton81's circular buffer library?) so the extracted/decoded sentences can be logged for later analysis in the event of a failure like the intermittent hangups I'm experiencing.

    When the code below is run against the sample data (with all debug printouts disabled), the output is:

    HTML Code:
    Opening port
    Port open
    I2C(68) reading 1 bytes from 75... 68 . Done
    I2C(68) reading 1 bytes from 6a... c0 . Done
    I2C(68) writing 1 bytes to 6a... c4 . Done
    I2C(68) reading 2 bytes from 72... 0 1c . Done
    I2C(68) reading 2 bytes from 72... 0 38 . Done
    I2C(68) reading 2 bytes from 72... 0 54 . Done
    I2C(68) reading 2 bytes from 72... 0 70 . Done
    I2C(68) reading 2 bytes from 72... 0 8c . Done
    I2C(68) reading 2 bytes from 72... 0 c4 . Done
    I2C(68) reading 2 bytes from 72... 0 e0 . Done
    Parsing 928 bytes took 1081 uSec
    For comparison, the output from Jeff Rowberg's I2Cdev code for the same sample set is:
    HTML Code:
    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).
    Decoding and printing out the results took a little over 1 mSec using a 500Kb serial transfer rate on my XPS15 7590 laptop, so with a sufficiently large FIFO I should be able to keep up with my proposed application which only communicates with the MPU6050 every 200 mSec.

    Any and all comments/suggestions are welcome, especially with respect to interfacing this code with tonton81's circular buffer library.




    Code:
    /*
        Name:       Teensy_I2C_Sniffer_V5.ino
        Created:	12/31/2019 11:36:00 PM
        Author:     FRANKNEWXPS15\Frank
    
        This is a port of my Excel VBA code into Arduino/Teensy C++ language.
        It parses the exact same data as the Excel program, so it should produce
        the exact same I2C sequences.
    
        The idea here is to not only perform the port, but to figure out how to
        produce a useful human-readable output format, and to determine how much
        time is required to parse the data and output the data to the serial port
    
        The captured data to be parsed is held in the 'simdata' array.
    
        The original VBA code is saved in 'ExcelVBACode.txt'
    
    */
    
    /* 'Notes:
    
        A typical I2C sentence when communicating with a MPU6050 IMU module goes like:
            "I2C(68) wrote 1 byte to 75 - C0 Done."
            "I2C(68) wrote 3 bytes to 72 - C0 0C 10 Done."
            "I2C(68) read 5 bytes from 6A - C0 0C 10 14 03 Done."
    
        To form a sentence, we need:
            Device addr: 68 in the above examples
            Read/Write direction
            To/From register address:  75, 72 and 6A in the above examples
            Data:  C0, C0 0C 10, and C0 0C 10 14 03 in the above examples
            number of bytes written/read:  1,3 & 5 in the above examples
    
         Each I2C communication proceeds as follows (assuming a START from an IDLE condition):
             A START or RESTART condition, denoted by SDA & SCL HIGH, followed by SDA LOW, SCL HIGH
             A 7-bit device address, MSB first (0x8/0xC = 1, 0x0/0x4 = 0)
             A R/W bit (0x8/0xC = read, 0x0/0x4 = write)
             An ACK bit (0x8/0xC = NAK, 0x0/0x4 = ACK)
             If the bus direction is WRITE, then
                 A register address for read/write
                 zero or more additional data bytes
             Else (the bus direction is READ)
                One or more additional data bytes
             Endif
    */
    const int SIM_DATA_ARRAY_SIZE = 928;
    byte simdata[SIM_DATA_ARRAY_SIZE] =
    {
        0xc, 0x4, 0x8, 0xc, 0x8, 0xc, 0x0, 0x4, 0x8, 0xc, 0x0,
        0x4, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0x8, 0xc, 0x8, 0xc,
        0x8, 0xc, 0x0, 0x4, 0x8, 0xc, 0x0, 0x4, 0x8, 0xc, 0x0, 0x4, 0x0, 0x4, 0xc,
        0x4, 0x8, 0xc, 0x8, 0xc, 0x0, 0x4, 0x8, 0xc, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4,
        0x8, 0xc, 0x0, 0x4, 0x0, 0x4, 0x8, 0xc, 0x8, 0xc, 0x0, 0x4, 0x8, 0xc, 0x0,
        0x4, 0x0, 0x4, 0x0, 0x4, 0x8, 0xc, 0x0, 0x4, 0xc, 0x4, 0x8, 0xc, 0x8, 0xc,
        0x0, 0x4, 0x8, 0xc, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0x0,
        0x4, 0x8, 0xc, 0x8, 0xc, 0x0, 0x4, 0x8, 0xc, 0x0, 0x4, 0x8, 0xc, 0x0, 0x4,
        0x0, 0x4, 0x0, 0x4, 0xc, 0x4, 0x8, 0xc, 0x8, 0xc, 0x0, 0x4, 0x8, 0xc, 0x0,
        0x4, 0x0, 0x4, 0x0, 0x4, 0x8, 0xc, 0x0, 0x4, 0x8, 0xc, 0x8, 0xc, 0x0, 0x4,
        0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0x8, 0xc, 0x0, 0x4, 0xc,
        0x4, 0x8, 0xc, 0x8, 0xc, 0x0, 0x4, 0x8, 0xc, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4,
        0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0x8, 0xc, 0x8, 0xc, 0x0, 0x4, 0x8, 0xc, 0x0,
        0x4, 0x8, 0xc, 0x0, 0x4, 0x0, 0x4, 0x8, 0xc, 0x8, 0xc, 0x0, 0x4, 0x0, 0x4,
        0x0, 0x4, 0x8, 0xc, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0xc, 0x4, 0x8,
        0xc, 0x8, 0xc, 0x0, 0x4, 0x8, 0xc, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4,
        0x0, 0x4, 0x0, 0x4, 0x8, 0xc, 0x8, 0xc, 0x8, 0xc, 0x0, 0x4, 0x0, 0x4, 0x8,
        0xc, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0xc, 0x4, 0x8, 0xc, 0x8, 0xc, 0x0, 0x4,
        0x8, 0xc, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0x8, 0xc, 0x0, 0x4, 0x0, 0x4, 0x0,
        0x4, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4,
        0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0x8, 0xc, 0x8, 0xc, 0x8, 0xc, 0x0, 0x4, 0x0,
        0x4, 0x8, 0xc, 0x0, 0x4, 0xc, 0x4, 0x8, 0xc, 0x8, 0xc, 0x0, 0x4, 0x8, 0xc,
        0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0x8, 0xc, 0x8,
        0xc, 0x8, 0xc, 0x0, 0x4, 0x0, 0x4, 0x8, 0xc, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4,
        0xc, 0x4, 0x8, 0xc, 0x8, 0xc, 0x0, 0x4, 0x8, 0xc, 0x0, 0x4, 0x0, 0x4, 0x0,
        0x4, 0x8, 0xc, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4,
        0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0x8, 0xc, 0x8,
        0xc, 0x8, 0xc, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0x8, 0xc, 0x0, 0x4, 0xc, 0x4,
        0x8, 0xc, 0x8, 0xc, 0x0, 0x4, 0x8, 0xc, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0x0,
        0x4, 0x0, 0x4, 0x0, 0x4, 0x8, 0xc, 0x8, 0xc, 0x8, 0xc, 0x0, 0x4, 0x0, 0x4,
        0x8, 0xc, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0xc, 0x4, 0x8, 0xc, 0x8, 0xc, 0x0,
        0x4, 0x8, 0xc, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0x8, 0xc, 0x0, 0x4, 0x0, 0x4,
        0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0x0,
        0x4, 0x0, 0x4, 0x8, 0xc, 0x0, 0x4, 0x8, 0xc, 0x0, 0x4, 0x8, 0xc, 0x0, 0x4,
        0x0, 0x4, 0x8, 0xc, 0x0, 0x4, 0xc, 0x4, 0x8, 0xc, 0x8, 0xc, 0x0, 0x4, 0x8,
        0xc, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0x8, 0xc,
        0x8, 0xc, 0x8, 0xc, 0x0, 0x4, 0x0, 0x4, 0x8, 0xc, 0x0, 0x4, 0x0, 0x4, 0x0,
        0x4, 0xc, 0x4, 0x8, 0xc, 0x8, 0xc, 0x0, 0x4, 0x8, 0xc, 0x0, 0x4, 0x0, 0x4,
        0x0, 0x4, 0x8, 0xc, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0x0,
        0x4, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0x8, 0xc, 0x8, 0xc,
        0x8, 0xc, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0x8, 0xc, 0x0, 0x4, 0xc,
        0x4, 0x8, 0xc, 0x8, 0xc, 0x0, 0x4, 0x8, 0xc, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4,
        0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0x8, 0xc, 0x8, 0xc, 0x8, 0xc, 0x0, 0x4, 0x0,
        0x4, 0x8, 0xc, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0xc, 0x4, 0x8, 0xc, 0x8, 0xc,
        0x0, 0x4, 0x8, 0xc, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0x8, 0xc, 0x0, 0x4, 0x0,
        0x4, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4,
        0x0, 0x4, 0x8, 0xc, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0x8, 0xc, 0x8, 0xc, 0x0,
        0x4, 0x0, 0x4, 0x8, 0xc, 0x0, 0x4, 0xc, 0x4, 0x8, 0xc, 0x8, 0xc, 0x0, 0x4,
        0x8, 0xc, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0x8,
        0xc, 0x8, 0xc, 0x8, 0xc, 0x0, 0x4, 0x0, 0x4, 0x8, 0xc, 0x0, 0x4, 0x0, 0x4,
        0x0, 0x4, 0xc, 0x4, 0x8, 0xc, 0x8, 0xc, 0x0, 0x4, 0x8, 0xc, 0x0, 0x4, 0x0,
        0x4, 0x0, 0x4, 0x8, 0xc, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4,
        0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0x8, 0xc, 0x8, 0xc, 0x0,
        0x4, 0x0, 0x4, 0x0, 0x4, 0x8, 0xc, 0x0, 0x4, 0x0, 0x4, 0x8, 0xc, 0x0, 0x4,
        0xc, 0x4, 0x8, 0xc, 0x8, 0xc, 0x0, 0x4, 0x8, 0xc, 0x0, 0x4, 0x0, 0x4, 0x0,
        0x4, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0x8, 0xc, 0x8, 0xc, 0x8, 0xc, 0x0, 0x4,
        0x0, 0x4, 0x8, 0xc, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0xc, 0x4, 0x8, 0xc, 0x8,
        0xc, 0x0, 0x4, 0x8, 0xc, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0x8, 0xc, 0x0, 0x4,
        0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0x0,
        0x4, 0x0, 0x4, 0x8, 0xc, 0x8, 0xc, 0x8, 0xc, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4,
        0x0, 0x4, 0x0, 0x4, 0x8, 0xc, 0x0, 0x4, 0xc, 0x4, 0x8, 0xc, 0x8, 0xc, 0x0,
        0x4, 0x8, 0xc, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4, 0x0, 0x4,
        0x8, 0xc
    };
    
    //#define PARSE_LOOP_DEBUG
    //#define GET_8BIT_DATABYTE_DEBUG
    
    #pragma region VARIABLES
    uint8_t devAddr;
    uint8_t regAddr;
    
    //added to handle bus direction changes
    enum BUSDIR
    {
        WRITE,
        READ,
        UNKNOWN = -1
    } RWDir;
    BUSDIR BusDir = BUSDIR::UNKNOWN;
    
    int ACKNAKFlag; //can be negative
    uint8_t databyte_array[2048]; //holds multiple databytes for later output sentence construction
    uint16_t databyte_idx = 0; //index into databyte_array FIFO
    uint16_t numbytes = 0; //number of data bytes extracted from data stream
    #pragma endregion Variables
    
    
    void setup()
    {
        Serial.begin(500000); //trying higher speed with teensy
        unsigned long now = millis();
        int idx = 0;
        while (!Serial && (millis() - now) < 3000)
        {
            delay(500);
            idx++;
        }
    
        uint16_t dataidx = 0;
        unsigned long startUsec = micros();
    
        while (dataidx < SIM_DATA_ARRAY_SIZE) //outer loop only necessary for simulation runs
        {
    #ifdef PARSE_LOOP_DEBUG
            Serial.printf("looptop: dataidx = %d\n", dataidx);
    #endif
    
            //Find a START sequence (0xC followed by 0x4)
            while (!IsStart(simdata, dataidx) && dataidx < SIM_DATA_ARRAY_SIZE)
            {
                dataidx++;
            }
    #ifdef PARSE_LOOP_DEBUG
            Serial.printf("Start sequence found at dataidx = %d\n", dataidx);
    #endif
    
            if (dataidx < SIM_DATA_ARRAY_SIZE - 14)//14 entries required for 7-bit address
            {
                //Get 7-bit device address
                devAddr = Get7BitDeviceAddr(simdata, dataidx);
    #ifdef PARSE_LOOP_DEBUG
                Serial.printf("devAddr = %x, dataidx = %d\n", devAddr, dataidx);
    #endif
    
                //get read/write flag  1 = Read, 0 = Write, -1 = error
                BusDir = (BUSDIR)GetReadWriteFlag(simdata, dataidx);
    
    #ifdef PARSE_LOOP_DEBUG
                Serial.printf("BusDir = %s\n", ((BusDir == BUSDIR::WRITE) ? "WRITE" : "READ"));
    #endif
    
                //get ACK/NAK flag
                ACKNAKFlag = GetACKNAKFlag(simdata, dataidx);
    
                numbytes = GetDataBytes(simdata, dataidx, databyte_array);
    #ifdef PARSE_LOOP_DEBUG
                Serial.printf("Got %d bytes from GetDataBytes()\n", numbytes);
    #endif
    
                //If the bus direction is WRITE, then extract
                //    A register address for read / write
                //    zero or more additional data bytes
                    if (BusDir == BUSDIR::WRITE)
                {
                    regAddr = databyte_array[0];
    #ifdef PARSE_LOOP_DEBUG
                    Serial.printf("regAddr = %x, dataidx = %d\n", regAddr, dataidx);
    #endif
    
                    //check for additional data
                    if (numbytes > 1)
                    {
    #ifdef PARSE_LOOP_DEBUG
                        Serial.printf("Additional data found!\n");
                        for (size_t i = 0; i < numbytes; i++)
                        {
                            Serial.printf("data[%d] = %x\n", i, databyte_array[i]);
                        }
    #endif
    
                        //1st byte is register addr, subsequent bytes are data
                        OutputFormattedSentence(BusDir, devAddr, regAddr, numbytes, databyte_array, 1 );
                    }
                }
                else  //all bytes are data
                {
    #ifdef PARSE_LOOP_DEBUG
                    Serial.printf("In data block:  got %d bytes of data\n",numbytes);
                    for (size_t i = 0; i < numbytes; i++)
                    {
                        Serial.printf("data[%d] = %x\n", i, databyte_array[i]);
                    }
    #endif
                    OutputFormattedSentence(BusDir, devAddr, regAddr, numbytes, databyte_array, 0);
                }
    
            }//if (dataidx < SIM_DATA_ARRAY_SIZE - 14)//14 entries required for 7-bit address
    
        }//while (dataidx < SIM_DATA_ARRAY_SIZE) //outer loop only necessary for simulation runs
    
        Serial.printf("Parsing %d bytes took %lu uSec\n", SIM_DATA_ARRAY_SIZE, micros() - startUsec);
    
    }//Setup()
    
    void loop()
    {
    
    
    }
    bool IsStart(byte* data, uint16_t& readidx)
    {
        bool result = false;
    
        //Serial.printf("IsStart[%d] = %x, IsStart[%d] = %x\n",
        //    readidx, data[readidx], readidx + 1, data[readidx + 1]);
    
        if (data[readidx] == 0xC && data[readidx + 1] == 0x4)
        {
            result = true;
            readidx += 2; //bump to next byte pair
        }
        return result;
    }
    
    bool IsStop(byte* data, uint16_t& readidx)
    {
        bool result = false;
    
        //Serial.printf("IsStop[%d] = %x, IsStop[%d] = %x\n",
            //readidx, data[readidx], readidx + 1, data[readidx + 1]);
    
        if (data[readidx] == 0x4 && data[readidx + 1] == 0xC)
        {
            result = true;
            readidx += 2; //bump to next byte pair
        }
        return result;
    }
    
    uint8_t Get7BitDeviceAddr(byte* simdata, uint16_t& readidx)
    {
        //Purpose: Construct a 7-bit address starting from dataidx
        //Inputs:
        //  simdata = pointer to simulated data array
        //  readidx = starting index of 7-bit address sequence (MSB first)
        //Outputs:
        //  returns the address as an 8-bit value with the MSB = 0, or 0x0 if unsuccessful
        //  dataidx = pointer to next simdata entry
        //Plan:
        //  Step1: Convert a pair of simdata entries into a 0 or 1
        //  Step2: Add the appropriate value to an ongoing sum
        //  Step3: return the total.
        //Notes:
        //  A '0' is coded as a 0x0 followed by a 0x4
        //  A '1' is coded as a 0x8 followed by a 0xC
    
        uint8_t devAddr = 0x0; //failure return value
    
        //Serial.printf("Get7BitDeviceAddr: readidx = %d\n",readidx);
    
        //devAddr is exactly 7 bits long, so 8 bits with MSB = 0
        for (size_t i = 0; i < 7; i++)
        {
            if (simdata[readidx] == 0x0 && simdata[readidx + 1] == 0x4)
            {
                readidx += 2; //advance the pointer, but don't add to sum
            }
    
            else if (simdata[readidx] == 0x8 && simdata[readidx + 1] == 0xC)
            {
                //Serial.printf("Get7BitDeviceAddr: '1' found at i = %d, adding %x to devAddr to get %x\n",
                //    i, 1 << (7 - i), devAddr + (1 << (7-i)));
    
                readidx += 2; //advance the pointer
                devAddr += (1 << (7 - i)); //add 2^(7-i) to sum
            }
        }
    
        devAddr = devAddr >> 1; //divide result by 2 to get 7-bit addr from 8 bits
        return devAddr;
    }
    
    int Get8BitDataByte(byte* simdata, uint16_t& readidx)
    {
        //Purpose: Construct a 8-bit data byte starting from dataidx
        //Inputs:
        //  simdata = pointer to simulated data array
        //  readidx = starting index of 8-bit data byte (MSB first)
        //Outputs:
        //  returns the address as an 8-bit value, or 0x0 if unsuccessful
        //  dataidx = pointer to next simdata entry
        //Plan:
        //  Step1: Convert a pair of simdata entries into a 0 or 1
        //  Step2: Add the appropriate value to an ongoing sum
        //  Step3: return the total.
        //Notes:
        //  A '0' is coded as a 0x0 followed by a 0x4
        //  A '1' is coded as a 0x8 followed by a 0xC
        //  12/29/19 - changed return val to int, so can return -1 when a 'short byte' is detected
    
        int dataval = 0x0; //failure return value
    
    #ifdef GET_8BIT_DATABYTE_DEBUG
        Serial.printf("Get8BitDataByte: simdata[%d] = %x, simdata[%d] = %x\n",
            readidx, simdata[readidx], readidx + 1, simdata[readidx + 1]);
    #endif
    
        //8 bits with MSB = 0
        int numbytes = 0;
        for (size_t i = 0; i < 8; i++)
        {
            if (simdata[readidx] == 0x0 && simdata[readidx + 1] == 0x4)
            {
                readidx += 2; //advance the pointer, but don't add to sum
                numbytes++;
            }
    
            else if (simdata[readidx] == 0x8 && simdata[readidx + 1] == 0xC)
            {
    #ifdef GET_8BIT_DATABYTE_DEBUG
                Serial.printf("Get8BitDataByte: '1' found at i = %d, adding %x to devAddr to get %x\n",
                    i, 1 << (7 - i), dataval + (1 << (7 - i)));
    #endif
                readidx += 2; //advance the pointer
                dataval += (1 << (7 - i)); //add 2^(8-i) to sum
                numbytes++;
            }
        }
    
    #ifdef GET_8BIT_DATABYTE_DEBUG
        Serial.printf("Get8BitDataByte: numbytes = %d\n", numbytes);
    #endif
        if (numbytes != 8)
        {
            dataval = -1; //error return value
        }
    
        return dataval;
    }
    
    int GetReadWriteFlag(byte* simdata, uint16_t& readidx)
    {
        //Purpose: decode R/W byte pair
        //Inputs:
        //  simdata = pointer to byte capture array
        //  readidx = index into simdata to start of R/W byte pair
        //Outputs:
        //  readidx = if successful, points to next byte pair in simdata
        //  returns 1 for Read (0x8/0xC), 0 for Write (0x0/0x4), -1 for failure
        //Notes:
        //  
    
        //Serial.printf("GetReadWriteFlag: readidx = %d, simdata[readidx] = %x, simdata[readidx+1]= %x\n",
        //    readidx, simdata[readidx], simdata[readidx + 1]);
        int result = 0;
        if (simdata[readidx] == 0x8 && simdata[readidx + 1] == 0xC)
        {
            result = 1; //read detected
            readidx += 2; //point to next byte pair
        }
    
        else if (simdata[readidx] == 0x0 && simdata[readidx + 1] == 0x4)
        {
            result = 0; //write detected
            readidx += 2; //point to next byte pair
        }
        else
        {
            result = -1; //failed to detect read or write
        }
    
        return result;
    }
    
    int GetACKNAKFlag(byte* simdata, uint16_t& readidx)
    {
        //Purpose: decode ACK/NAK byte pair
        //Inputs:
        //  simdata = pointer to byte capture array
        //  readidx = index into simdata to start of ACK/NAK byte pair
        //Outputs:
        //  readidx = if successful, points to next byte pair in simdata
        //  returns 1 for NAK (0x8/0xC), 0 for ACK (0x0/0x4), -1 for failure
        //Notes:
        //  
    
        //Serial.printf("GetACKNAKFlag: readidx = %d, simdata[readidx] = %x, simdata[readidx+1]= %x\n",
        //    readidx, simdata[readidx], simdata[readidx + 1]);
        int result = 0;
        if (simdata[readidx] == 0x8 && simdata[readidx + 1] == 0xC)
        {
            result = 1; //NAK detected
            readidx += 2; //point to next byte pair
        }
    
        else if (simdata[readidx] == 0x0 && simdata[readidx + 1] == 0x4)
        {
            result = 0; //ACK detected
            readidx += 2; //point to next byte pair
        }
        else
        {
            result = -1; //failed to detect ACK or NAK
        }
    
        return result;
    }
    
    int GetDataBytes(uint8_t* data, uint16_t& readidx, uint8_t* databytes)
    {
        //Notes:
        //  01/01/2020: removed databyteidx from sig - always starts at zero
    
        uint16_t numbytes = 0;
        uint16_t databyte_idx = 0;
    
        bool StartFlag = false;
        bool StopFlag = false;
    
        do
        {
            int dataval = Get8BitDataByte(data, readidx);
    
            //watch out for 'short byte' reads
            if (dataval >= 0)
            {
                uint8_t databyte = (uint8_t)dataval;
                databyte_array[databyte_idx] = databyte;
                databyte_idx++;
                numbytes++;
            }
    
            ACKNAKFlag = GetACKNAKFlag(data, readidx);
            StartFlag = IsStart(data, readidx);
            StopFlag = IsStop(data, readidx);
    
    #ifdef PARSE_LOOP_DEBUG
            Serial.printf("IsStart returned %d, IsStop returned %d, dataidx = %d\n",
                StartFlag, StopFlag, readidx);
    #endif
    
        } while (!StartFlag && !StopFlag && readidx < SIM_DATA_ARRAY_SIZE);
    
        
        readidx -= 2;//back readidx up so loop top is positioned correctly.
    
        return numbytes;
    }
    
    void OutputFormattedSentence(int RW, uint8_t dev, uint8_t reg, uint8_t numbytes, uint8_t* bytearray,uint16_t startidx)
    {
        Serial.printf("I2C(%x) %s %d bytes %s %x... ",
            dev, (RW == 0 ? "writing" : "reading"), numbytes-startidx, (RW == 0 ? "to" : "from"), reg);
        for (size_t i = startidx; i < numbytes; i++)
        {
            Serial.printf("%x ", bytearray[i]);
        }
    
        Serial.printf(". Done\n");
    
    }

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •