Programming for Robotics 1, Chapter 2: Challenge 1 solution (bus times and pull-up resistors) Formulas: I2C: bits = 9 x (2 + register bytes + data bytes) t = bits / f (9 bit times per byte: 8 data bits and the ACK; START and STOP ignored) SPI: t = 8 x bytes / f UART (8N1): t = 10 x bytes / baud pull-up: Rp(min) = (Vcc - VOL) / IOL Rp(max) = t(rise) / (0.8473 x C) t(rise) = 0.8473 x Rp x C (a) A register-pointer byte and 6 data bytes: bits = 9 x (2 + 1 + 6) = 81. 100 kHz: t = 81 / 100 000 = 0.810 ms 400 kHz: t = 81 / 400 000 = 202.5 us three sensors at 400 kHz: 3 x 202.5 = 607.5 us, which is 607.5 / 10 000 = 6.075 % of the 10 ms period of a 100 Hz loop. (At 100 kHz the same three reads would take 2.43 ms, 24.3 % of the period: the faster clock is worth having.) (b) SPI at 2 MHz, one command byte and 6 data bytes: 7 bytes x 8 bits = 56 bits, t = 56 / 2 000 000 = 28.0 us. UART at 57 600 baud, 6 bytes: 60 bits, t = 60 / 57 600 = 1.0417 ms. So for the same 6 bytes: SPI 28 us, I2C at 400 kHz 202.5 us, UART 1.04 ms. SPI is the fastest here because it has no address byte, no ACK bits and a clock that is not limited to 400 kHz. (c) 3.3 V, IOL = 3 mA, VOL = 0.4 V, C = 150 pF, fast mode (rise time at most 300 ns). Rp(min) = (3.3 - 0.4) / 0.003 = 966.7 ohm Rp(max) = 300 ns / (0.8473 x 150 pF) = 2360 ohm The E12 values inside the window are 1.0 k, 1.2 k, 1.5 k, 1.8 k and 2.2 k (2.7 k is too big: its rise time would be 343 ns). The geometric middle of the window is sqrt(966.7 x 2360) = 1510 ohm, so 1.5 k keeps the most margin on both sides: t(rise) = 0.8473 x 1500 x 150 pF = 190.6 ns (limit 300 ns) current when pulled low = 2.9 V / 1500 = 1.93 mA (limit 3 mA) With C = 400 pF at 400 kHz: Rp(max) = 300 ns / (0.8473 x 400 pF) = 885 ohm, which is BELOW Rp(min) = 967 ohm. No resistor satisfies both limits. The fixes are to shorten the wires or remove devices (less capacitance), or to slow the bus: at 100 kHz the rise time may be 1000 ns, Rp(max) = 1000 ns / (0.8473 x 400 pF) = 2950 ohm, and 2.2 k works: t(rise) = 0.8473 x 2200 x 400 pF = 745.6 ns. The same numbers recomputed in Python (the code was run, and the output below is what it printed): CODE: def i2c_time_s(n_read, f_hz, n_reg_bytes=1): return 9 * (2 + n_reg_bytes + n_read) / f_hz print("(a) I2C, 6 bytes: %d bits; 100 kHz %.3f ms; 400 kHz %.1f us; three sensors at 400 kHz %.1f us = %.3f %% of 10 ms" % ( 9 * (3 + 6), 1000 * i2c_time_s(6, 100000), 1e6 * i2c_time_s(6, 400000), 3e6 * i2c_time_s(6, 400000), 100 * 3 * i2c_time_s(6, 400000) / 0.010)) print("(b) SPI 2 MHz, 1 + 6 bytes: %.1f us; UART 57600 baud, 6 bytes: %.4f ms" % (1e6 * 8 * 7 / 2e6, 1000 * 10 * 6 / 57600)) vcc, vol, iol, c, tr = 3.3, 0.4, 0.003, 150e-12, 300e-9 r_min = (vcc - vol) / iol r_max = tr / (0.8473 * c) print("(c) Rp(min) = %.1f ohm, Rp(max) = %.1f ohm" % (r_min, r_max)) for rp in (1000, 1200, 1500, 1800, 2200, 2700): ok = r_min <= rp <= r_max print(" %4d ohm: tr = %.1f ns, I(low) = %.2f mA %s" % (rp, 1e9 * 0.8473 * rp * c, 1000 * (vcc - vol) / rp, "inside" if ok else "outside")) c2 = 400e-12 print(" C = 400 pF at 400 kHz: Rp(max) = %.1f ohm, below Rp(min) = %.1f: no value works" % (tr / (0.8473 * c2), r_min)) print(" C = 400 pF at 100 kHz (tr 1000 ns): Rp(max) = %.1f ohm; 2200 ohm gives tr = %.1f ns" % (1000e-9 / (0.8473 * c2), 1e9 * 0.8473 * 2200 * c2)) END CODE OUTPUT: (a) I2C, 6 bytes: 81 bits; 100 kHz 0.810 ms; 400 kHz 202.5 us; three sensors at 400 kHz 607.5 us = 6.075 % of 10 ms (b) SPI 2 MHz, 1 + 6 bytes: 28.0 us; UART 57600 baud, 6 bytes: 1.0417 ms (c) Rp(min) = 966.7 ohm, Rp(max) = 2360.4 ohm 1000 ohm: tr = 127.1 ns, I(low) = 2.90 mA inside 1200 ohm: tr = 152.5 ns, I(low) = 2.42 mA inside 1500 ohm: tr = 190.6 ns, I(low) = 1.93 mA inside 1800 ohm: tr = 228.8 ns, I(low) = 1.61 mA inside 2200 ohm: tr = 279.6 ns, I(low) = 1.32 mA inside 2700 ohm: tr = 343.2 ns, I(low) = 1.07 mA outside C = 400 pF at 400 kHz: Rp(max) = 885.2 ohm, below Rp(min) = 966.7: no value works C = 400 pF at 100 kHz (tr 1000 ns): Rp(max) = 2950.5 ohm; 2200 ohm gives tr = 745.6 ns END OUTPUT Check on method: count the address bytes and the register byte as well as the data bytes (the 2 + 1 in the bit count), and use 9 bit times per I2C byte but 10 per UART byte and 8 per SPI byte. And a pull-up has two limits, not one: too small and the chip cannot pull the wire low, too large and the wire rises too slowly. When the window closes, the answer is a slower bus, not a cleverer resistor.