How to design and debug a custom I2C master-slave system?
This tutorial I gave at the Embedded Linux Conference tries to answer the questions, providing links to more detailed description of the topics addressed and using the practical example of driving a 4WD drone, where an Arduino Mini Pro acts as slave and controls the 4 independent wheels.
The original document can be found here.

Note: This answer is currently work in progress, as I adapt the highlights from the link.



Typical Applications of the I2C bus


Interfacing with relatively slow peripherals. Ex: sensors, mechanical actuators.
Controlling “fast” peripherals, that use other channels for exchanging data. Ex: codecs.

In a PC, the Operating System usually interacts over I2C with:


temperature and battery voltage meters;
fan speed controllers;
audio codecs.



In case multiple bus controllers are available, peripherals are grouped by speed, so that fast ones are not penalized by slower ones.



A quick introduction to the I2C bus - key features


Serial bus.  
Only 2 lines: Serial CLock and Serial DAta (plus ground).
4 speeds: 100kHz, 400kHz, 1MHz, 3.2MHz.
Typically, 1 master device and 1 or more slaves.
Communications are always initiated by a master device.
Multiple masters can co-exist on the same bus (multi-master).
Open-Drain: both SDA and SCL need pull-up resistors.
“Clock Stretching”


The master controls SCL, but a slave can hold it down (because open drain), if it needs to adjust the speed.
The master must check for this scenario.
A slave can get stuck and jam the bus: need for reset lines from the master to the slave.

Typically 7-bit addressing, but also 10 bit is supported.
Logical protocol: actual voltage levels are not specified and depend on individual implementations. Ex: 1.8V / 3.3V / 5.0V


Reference URLs: 


http://www.i2c-bus.org
http://www.robot-electronics.co.uk/i2c-tutorial


Example of Bus Configuration





Characteristics of the Protocol (simplified)


2 message types: read and write
Start / Stop bit - represented as “[“ and “]” in the rest of the answer
Address: 7 or 10 bits
R/W bit: R = 1 / W = 0 Used to discriminate the type of message sent.
Data on the bus: (Address &lt;&lt; 1 | R/W)
Registers as information handlers, within the selected device.


Example of Bus traffic








Custom Slaves

Why create a custom I2C slave?


Desired sensor/actuator unavailable with I2C interface.
Less unique addresses available than slaves needed.
Desired custom functionality on the slave:


Semi-autonomous reactions to stimuli.
Filtering/preprocessing input data.

Power optimization: custom “sensor hub” does the housekeeping while the main processor is idle.
Realtime response to inputs.
[your imagination here]


How to design a custom I2C slave?


Define requirements (see previous slide).
Choose microcontroller or microprocessor.
Choose Scheduler or Operating System (if any).
Define communication sub-protocol:


Define parameters and commands to be exchanged.
Organize them into “registers” and choose a free address.





Design of the I2C Master

Key design criteria:


Weight/Dimensions.
Required computational power and average latency.
PC-like device


Embedded device, typically headless.
Preferred programming language: interpreted vs compiled.

Availability of busses/gpios for driving the slave(s):


GPIOs only: bitbang the protocol
I2C: user-space application vs kernel driver.
No GPIOs/I2C interfaces available: USB to I2C adapter.





Debugging: Divide and Conquer

Take direct control of the bus with an ad-hoc device. 
Examples:


Bus Pirate (useful also for other busses)
USB to I2C Master adapter, also based on the FTDI FT232R chip.
Custom device (could be a separate project).
Snoop the bus with a logic analyzer or a scope/advanced meter.
Examples:


sigrok/pulseview with compatible logic analyzer
2-channels standalone scope/meter
Use slave-specific In Circuit Debugger/In Circuit Emulator.

Example:
AVR Dragon for AVR chips (Arduino UNO, Nano, Mini, MiniPro)





BUS Pirate




Primarily for development purposes.
Can both sniff the bus and drive it.
Console interface over serial (ttyACM) port, including macros, or programmatic access for several programming languages.
Built-in pullup resistors and voltage sources (5V / 3.3V)
Supports many other protocols.
References: Wikipedia, main page




USB to I2C Adapter




Small footprint.
Suitable for permanent installations.
No need for special connections on the host: it can be used to interface with a typical PC.
Variant available that is also SPI-capable.
No console interface, only serial binary protocol.
Requires protocol wrapper.
Reference: protocol




sigrok and pulseview

sigrok(bakend component) logo



pulseview (visualizer) example



Example of low end logic Analyzer




De-facto standard for PC-driven measurements on linux (but available on other OSes too).
Support for vast range of logic analyzers, scopes and meters.
Various protocol decoders, including I2C.
Useful for visualizing the logical signals and debugging protocol errors.
Even very low end, inexpensive HW can provide a whole new dimension to debugging.
References: sigrok, pulseview, supported hardware




Example: steering a 4WD drone

Prototype built using 2 Arduino Mini Pro.




What does the slave do in the example?

The I2C slave:


Controls the amount of torque applied to each wheel.
Controls the direction each wheel spins.
Measures  the rotation speed of each wheel through an optical encoder (Odometer).
Exposes the parameters above to the I2C Master.




High level block diagram of the I2C Slave.



Selecting the Slave: Arduino Mini Pro




Enough pins/functions to provide for each wheel:


1 PWM output with independent configuration of the duty-cycle.
1 GPIO for registering odometer input as IRQ.
2 GPIOs for selecting:


Forward
Reverse
Idle
Lock


I2C HW block for interrupt-driven i2c exchanges.
Dedicated pins for SPI-based programming.
Small footprint.
Low Cost.
The board layout of the clone represented in the picture is optimized for mounting on a DIL socket.




Slave-specific ICD: AVR Dragon




Supports various programming modes, included SPI programming, through AVRDude.
Doesn’t interfere with normal AVR operations, so it can be left plugged into the system.
After enabling debugWire interface, it allows configuring HW/SW breakpoints, by a dedicated backend for gdb/ddd.




Selecting the OS: ChibiOS




RTOS: preemption, tasks, semaphores, dynamic system tic, etc.
Small footprint: link only used code/data.
Distinction between RTOS and BSP through HAL.
GPLv3 for non-commercial use.
Actively developed, but already mature.
Supports 8bit AVR.


However it had limited BSP support for AVR, lack of:
 - interrupts driver for AVR GPIOs (added).
 - I2C support for AVR slave mode (custom).
Which had to be developed separately as part of the Drone SW for the AVR.



Defining the Communication Parameters

For each wheel:


Duty Cycle of the PWM signal used to drive it - 1 byte.
0xFF = max torque / 0x00 = no torque.
Direction of rotation - 1 byte.


0x00 = idle
0x01 = reverse
0x02 = forward
0x03 = locked

Average period in between slots of the optical encoder - 2 bytes.


Writing anything resets the measurement.

Parameter Index - 1 nibble:


0 = Duty Cycle
1 = Direction
2 = Average Period

Wheel indexes - 1 nibble:


0 = Left Rear
1 = Right Rear
2 = Right Front
3 = Left Front
4 = All





Sub Protocol: Defining the Registers

Register format:   0xαβ
 - α = Parameter Index
 - β = Wheel Index

Address (chosen arbitrarily): 0x10

Bus Pirate format:
 - [ = start bit
 - ] = end bit
 - r = read byte
 - address times 2 (left shift 1), for R/W bit



Example - in Bus Pirate Format

[   i2c_addr   reg_addr=(parm,wheel)   reg_value  ]

[0x20 0x20 0x02]  Left Rear Forward
[0x20 0x21 0x01]  Right Rear Backward
[0x20 0x22 0x01]  Right Front Backward
[0x20 0x23 0x02]  Left Front Forward
[0x20 0x14 0xFF]  Wheels set to max torque


The car spins clockwise.