I2C at 400kHz works out of the box. At 1MHz Fast-mode Plus, signal integrity becomes critical. Let’s analyze the electrical requirements and optimize for reliable high-speed communication.
Rise Time Requirements
I2C rise time must meet spec for the target frequency:
// I2C timing requirements (I2C specification)
// Standard Mode (100kHz): tr_max = 1000ns
// Fast Mode (400kHz): tr_max = 300ns
// Fast Mode Plus (1MHz): tr_max = 120ns
#define VDD 3.3f // Supply voltage
#define VOL 0.4f // Low output voltage
#define VIH 0.7f * VDD // Input high threshold (2.31V)
#define VIL 0.3f * VDD // Input low threshold (0.99V)
float calculate_rise_time(float r_pullup, float c_bus) {
// Rise time from VOL to VIH (10% to 70% of VDD)
// t_r = -RC * ln((VDD - VIH) / (VDD - VOL))
return -r_pullup * c_bus * logf((VDD - VIH) / (VDD - VOL));
}
float calculate_optimal_pullup(float c_bus, float tr_target) {
// Solve for R given target rise time
return -tr_target / (c_bus * logf((VDD - VIH) / (VDD - VOL)));
}
Pull-up Resistor Selection for Different Bus Capacitances
| Bus Capacitance | 100kHz R_pu | 400kHz R_pu | 1MHz R_pu |
|---|---|---|---|
| 50pF | 10kΩ | 4.7kΩ | 1.5kΩ |
| 100pF | 6.8kΩ | 2.7kΩ | 1kΩ |
| 200pF | 4.7kΩ | 1.8kΩ | 680Ω |
| 400pF (max) | 2.4kΩ | 1kΩ | N/A |
Lower pull-up resistance increases IOL sink current. I2C spec limits IOL to 3mA (Standard) or 20mA (Fast Mode Plus). With 680Ω at 3.3V: I = 3.3V/680Ω = 4.85mA—verify your device supports this.
Bus Capacitance Measurement
// Measure bus capacitance using RC time constant
void measure_bus_capacitance(void) {
// Configure GPIO as output, drive low
gpio_set_direction(SDA_PIN, GPIO_MODE_OUTPUT);
gpio_set_level(SDA_PIN, 0);
delay_us(10); // Discharge bus
// Switch to input (high-Z), measure rise time
uint32_t start = get_timer_us();
gpio_set_direction(SDA_PIN, GPIO_MODE_INPUT);
// Wait for line to reach VIH
while (!gpio_get_level(SDA_PIN) && (get_timer_us() - start) < 1000);
uint32_t rise_time_us = get_timer_us() - start;
// C = -t_r / (R * ln((VDD - VIH) / VDD))
// With known pull-up R, calculate C
float c_bus_pf = (-rise_time_us * 1e-6f) /
(KNOWN_PULLUP * logf((3.3f - 2.31f) / 3.3f)) * 1e12f;
printf("Measured rise time: %lu us\n", rise_time_us);
printf("Estimated bus capacitance: %.0f pF\n", c_bus_pf);
}
Noise Immunity Techniques
1. Schmitt Trigger Inputs
Most modern MCUs have Schmitt trigger inputs on I2C pins. Verify hysteresis:
// Check I2C pin configuration for Schmitt trigger
void configure_i2c_gpio(void) {
// STM32 example: Enable Schmitt trigger
GPIOB->MODER |= (GPIO_MODER_MODE6_1 | GPIO_MODER_MODE7_1); // AF mode
GPIOB->OTYPER |= (GPIO_OTYPER_OT6 | GPIO_OTYPER_OT7); // Open-drain
// Schmitt trigger is default on STM32 I2C pins
// Typical hysteresis: 200-400mV
}
2. Digital Filtering
Enable peripheral-level digital filters:
// STM32 I2C analog and digital filters
void configure_i2c_filters(I2C_TypeDef* i2c) {
// Enable analog filter (default on)
i2c->CR1 &= ~I2C_CR1_ANFOFF;
// Configure digital filter (0-15 clock cycles)
// Higher values = more noise immunity, but added latency
i2c->CR1 &= ~I2C_CR1_DNF_Msk;
i2c->CR1 |= (4 << I2C_CR1_DNF_Pos); // 4 clock cycles
// Digital filter adds latency: t_filter = DNF * t_I2CCLK
// At 48MHz I2CCLK, DNF=4: 83ns filter delay
}
3. Ground Plane Design
PCB Layout Checklist for I2C:
✓ Continuous ground plane under I2C traces
✓ Pull-ups close to master device
✓ Minimize trace length between devices
✓ Keep SDA/SCL traces parallel, same length
✓ Avoid crossing high-speed digital signals
✓ Add 100nF decoupling at each device
Clock Stretching Handling
Slaves may stretch the clock. Handle robustly:
// I2C clock stretching timeout
#define CLOCK_STRETCH_TIMEOUT_US 1000
bool wait_for_clock_release(void) {
uint32_t start = get_timer_us();
while (!gpio_get_level(SCL_PIN)) {
if (get_timer_us() - start > CLOCK_STRETCH_TIMEOUT_US) {
// Clock stuck low - bus error
i2c_bus_reset();
return false;
}
}
return true;
}
void i2c_bus_reset(void) {
// Generate 9 clock pulses to release stuck slave
gpio_set_direction(SDA_PIN, GPIO_MODE_INPUT); // Release SDA
for (int i = 0; i < 9; i++) {
gpio_set_direction(SCL_PIN, GPIO_MODE_OUTPUT);
gpio_set_level(SCL_PIN, 0);
delay_us(5);
gpio_set_direction(SCL_PIN, GPIO_MODE_INPUT); // Release high
delay_us(5);
if (gpio_get_level(SDA_PIN)) {
break; // SDA released
}
}
// Generate STOP condition
generate_stop_condition();
}
I2C Throughput vs Speed Mode
(KB/s)Practical Test Setup
// I2C signal quality validation
void validate_i2c_signals(void) {
// Test 1: Rise time measurement
float tr = measure_rise_time();
printf("Rise time: %.0f ns (max 120ns for FM+)\n", tr * 1e9);
// Test 2: Data transfer test
uint8_t test_data[256];
for (int i = 0; i < 256; i++) test_data[i] = i;
uint32_t errors = 0;
for (int iter = 0; iter < 1000; iter++) {
i2c_write(SLAVE_ADDR, test_data, 256);
uint8_t read_back[256];
i2c_read(SLAVE_ADDR, read_back, 256);
if (memcmp(test_data, read_back, 256) != 0) {
errors++;
}
}
printf("Transfer test: %lu errors in 1000 iterations\n", errors);
printf("Error rate: %.2f%%\n", errors / 10.0f);
}
Conclusion
Reliable 1MHz I2C requires:
- Correct pull-up sizing: Calculate for your bus capacitance
- Signal integrity: Good ground plane, short traces
- Filtering: Enable analog and digital filters
- Robust error handling: Timeout and bus reset logic
Start at 400kHz, validate signals with oscilloscope, then increase to 1MHz if margins allow.