[HN Gopher] I2C in a Nutshell ___________________________________________________________________ I2C in a Nutshell Author : fra Score : 185 points Date : 2020-01-22 17:28 UTC (5 hours ago) (HTM) web link (interrupt.memfault.com) (TXT) w3m dump (interrupt.memfault.com) | linker3000 wrote: | Here is a shameless plug for a build-it-yourself multi-function | FT232H-based (USB interface) board that can do I2C among other | things (JTAG/SPI/UART/GPIO) and has a few extras compared to | similar commercial boards from elsewhere (pullups and some | blinkenlights). | | The board works with various apps and frameworks, including | OpenOCD and CircuitPython. | | Full build details and some other resources are here: | | https://github.com/linker3000/shukran | bsder wrote: | I'm happy to see all the discussion against I2C in the comments | here. I thought I was the only one who loathed debugging I2C | stuff. | | The only things I take exception in the article to is: | | > When a single device is on the bus, UART may work just as well. | | The problem with UART is _clock drift_. It 's remarkably easy for | your two chips to get out of sync if they don't use crystals and | don't have autobaud (normally rare). That is one thing that I2C, | SPI, and CAN do better. They either don't care as they have a | single master clock (I2C and SPI) or they autobaud detect and | then adjust (CAN). | skybrian wrote: | I wonder what people think about i2c connectors? I see that | Sparkfun has Qwiic and Adafruit has Stemma, and there are others | like Grove. | | I'm designing my first circuit board and I'm wondering if I | should bother with JST connectors or just use header pins. | kaik wrote: | This. I would also love to know what connectors I should use | for my hobby projects. I'm also designing my first PCB board | and something as simple as choosing connectors is daunting... | nlfwhulsdhouv wrote: | If it's your first project, stick with common 0.1" headers | unless you need something very specific (small, high pin | count, etc). You can buy locking versions if needed. | skybrian wrote: | What are locking headers? | bsder wrote: | Through-hole .100 headers. Always. Unless you have a _REALLY_ | good reason otherwise. (weatherproofing, signal integrity, | compatibility with existing solution, etc.) | | First, if you have a small number of pins (up to about 4-6), | a 2x2 or 2x3 .100 header isn't that much larger than any | alternative. Compare this: https://www.tag- | connect.com/product/tc2030-fp-footprint to a 2x2 of .100 | headers. It's actually bigger, and now you need a special | cable instead of that bag of .100" jumper wires you have. | | If you have something like 20 genuinely used pins (not 6 | active and 14 unused), okay, you may need a different | connector. But are you really sure about this? 20 pins | communicating simultaneously has signal integrity needs and | small connectors have _WAY_ more coupling than .100 " | spacing. | | Second, through-hole is always way more stable than no- | through hole. Once you give your smaller pitch connector | through holes, is it really smaller than .100"? | | Third, manufacturers have no problems with .100" headers. | Smaller pitches may increase the cost of your board. Try | costing out a board that can mount and route a modern USB-C | connector which has both surface mount and through-hole at | small pitch. You're probably going to get a cost bump. | | Fourth, you can buy _really_ long .100 " headers which allow | you to conect to them _and_ put a scope probe underneath. | That 's really convenient for debugging. | | So, go through-hole .100" header until you've got a good | reason otherwise. | skybrian wrote: | I have a bunch of male and female header and wires, but now | I'm looking at whether to buy right-angle header because I | will have a couple of boards that are close together and | having straight-up pins on the bottom board gets awkward. | | Then I start looking into various kinds of connectors, and | wonder if I'm overthinking it. | fra wrote: | They're all roughly equivalent. I would choose a cost effective | one and move on. | | One thing to watch out for: i2c is not designed for long wires, | so you may have signal integrity issues beyond a 1-2 feet. | You'll want to switch to differential signaling beyond that | (e.g. with https://www.sparkfun.com/products/14589). | TOGoS wrote: | http://www.nuke24.net/docs/2019/WSTYPE-4106-I2C-over-GX16-4.... | | Or GX12-4, if you want something a little smaller. | AceJohnny2 wrote: | > _We're partial to Saleae devices, which come with an easy to | set up I2C decoder._ | | I can vouch for these. We have a couple for our team to debug HW | issues, and I was amused, once in a Chinese factory, to be handed | one there when I asked for a logic analyzer (I know Saleae had | issues with clones in the past, and had to implement | countermeasures some years ago...) | Ives wrote: | I really don't like I2C. Yes, in principle it's pretty simple, | but if you consider NACKS, slaves holding SCK low, what happens | if your master resets while the slave is trying to send a 0 bit | (hint: power cycle!), etc, it's so easy for the peripheral to get | stuck. | | SPI is much easier to write correctly, and pretty much only has | the extra wire (usually not a problem) and the phase polarity | issues as a negative point. | fpgaminer wrote: | I built a battery powered project designed to run continuously | for ten years. Of all the things in that design, the I2C bus | makes me the most nervous (1). Every time the MCU wakes up it | has to use I2C to read from the RTC. If at any point in that | ten year span the I2C bus gets jammed ... | | I mitigated the issues by doing I2C resets on every read (see | the Analog Devices I2C reset documentation linked in another | comment) and reading multiple times to filter out any spurious | bit errors. | | Other than that I just have my fingers crossed that a bit | doesn't accidentally flip and overwrite something in the RTC. | Or that the bus somehow gets stuck driven between sleeps and | drains the battery early. _sigh_ SPI would have been so much | nicer. | | (1) I mean, okay, the massive lithium battery exploding is | perhaps the most nerve racking component, but I2C is a close | second. | shortsightedsid wrote: | On the other hand you need a lot more pins for SPI. | | One more advantage of SPI that I see is higher data rates, | full-duplex communication etc.. | uep wrote: | For exactly that reason, I've encountered peripherals getting | stuck during i2c transactions far too many times. At least a | handful of times I've had to add boot (and sometimes runtime) | logic to clear stuck transactions, due to the lack of a | dedicated way to reset said peripheral. | | It's quite annoying because one stuck device can block all | other communication on the bus. Let's hope your reset pin (if | you have it), isn't on an i2c expander on the same i2c bus. | fra wrote: | For what it's worth, here is a good doc on I2C resets: | https://www.analog.com/media/en/technical- | documentation/appl... | pslam wrote: | Same. I really dislike I2C, but it's universal and it's been | around for decades, and it's hard to avoid designs without it. | I2C keeps causing these additional issues which the article | doesn't touch on: | | * No way to safely bring the bus back to idle from mid- | transaction. By "safely" I mean not accidentally transmit an | extra byte which could e.g overwrite EEPROM memory. There is no | combination of transitioning the 2-wire bus from an arbitrary | state back to idle which works in the general case. If it's | important, you end up adding a dedicated reset wire. | | * No safe, universal way to bring the bus from tristate, or | low, to pulled-up. There are designs where this ends up being | necessary. You end up with a spurious transaction, which may | wedge the bus, or having to add a reset wire or buffer. | | * The protocol is extremely hostile to devices with non-zero | latency response. It's designed as a simple "Address this | register and then immediately read out a byte in the next clock | cycle". Works great for trivial devices, but for anything more | complex it ends up needing a bank of register acting as a | "proxy" to access the higher latency side of the chip. At this | point I2C is an awesomely bad choice, but people keep doing | this, because it's so universal. | fra wrote: | > No way to safely bring the bus back to idle from mid- | transaction. [...] No safe, universal way to bring the bus | from tristate, or low, to pulled-up. | | These are great points, and I'll add a note about them in the | article. Thanks! | | > The protocol is extremely hostile to devices with non-zero | latency response. [...] | | Technically, this is what clock-stretching is for. In | practice, you're right that complex devices implemented proxy | registers. I've seen it on DP->MIPI bridges for example. | russdill wrote: | There is a safe, out of band way to do this that system | designers can utilize. Reset all the other peripherals on the | bus. | amelius wrote: | > No way to safely bring the bus back to idle from mid- | transaction. | | Why would you want to do that? Not having the ability to do | this is part of the contract. If you design your device such | that it always completes the transaction, then there should | be no problem, unless one of the devices on the bus doesn't | play fair but then you have a different problem. | ajross wrote: | > SPI is much easier to write correctly | | I'm not sure that I buy that. A daisy chained SPI bus is a rats | nest of configuration hassle and basically impossible to debug. | You're trading hardware robustness for software complexity, and | that's not always a win. | | And as far as hardware messes: SPI doesn't synchronize anything | beyond the bit level (modulo an out of band chip enable or | reset, of course, which would work to "fix" an I2C device too), | making shift/offset bugs a real danger. | | Board-level digital interfacing is just hard. That's why we do | it as little as possible and put everything on the SoC these | days. | fra wrote: | I think I2C and SPI have very different use cases. Over I2C, | you can interact with 127 devices with just 2 pins. To do the | same with SPI, you'd need 130 (4 + an additional CS for every | device on the bus). | | You may think of the extra pins as not a problem, but on every | product I've worked on we've been pin-limited on the MCU. | andyjpb wrote: | If you only want to select one device at a time (which is | often the case) then you only need log2(devices) pins on the | microcontroller because you can decode that binary number | into the appropriate chip select. | | Obviously that requires more hardware on the board tho'. | Ives wrote: | Agreed, but most I2C busses only have 2 or 3 devices on them. | There are some boards with 16 or so devices on the same bus, | but much more than that and you'd better hope you can either | program their addresses or order them with a specific | address, or you might end up with 2 chips with the same | address. | fra wrote: | I've worked on server hardware with dozens of devices on a | bus :-). Making sure addresses were programmable was a must | indeed. | nlfwhulsdhouv wrote: | I don't think I've ever run more than 5 or 6 devices on an | I2C bus without running into rise time, bus contention, or | address collision issues. Some devices scoop tons of | addresses, and most only allow re-assignment to a few | alternate addresses. | | I agree it's still lower pin count than SPI, but | realistically you don't get anywhere near 127 devices. | tropo wrote: | For that many devices on SPI, run 7 pins to an address | decoder that fans out to 128. You can do this in the spare | pins of an FPGA that you might have for some other purpose, | and the 7 pins are cut down to one or less if you've already | put much into the FPGA. For example, the FPGA provides 4096 | bytes of registers (12 address bits) to the MCU but only | needs 3700 registers, so use one of the spare bytes to | control which SPI CS is enabled. | | I've also seen JTAG as an alternative to I2C and SPI. JTAG | can be part of normal operation. | clarry wrote: | > Over I2C, you can interact with 127 devices with just 2 | pins. | | In practice, I don't see that many chips offering 7 bits of | address configuration. You buy a chip, it has a hardwired | address. Maybe a pin or two for selecting another address. | photojosh wrote: | There was the design I did quite a few years ago now. | Grabbed a old design, changed the board shape, put a third | I2C device on. Everything powered up beautifully first | go... and it was only then we worked out two of the devices | from different vendors had the same I2C address. <facepalm> | vvanders wrote: | Not always the case that you need the pin count, a good | number of SPI devices support daisy chaining[1]. | | [1] https://www.maximintegrated.com/en/design/technical- | document... | fra wrote: | Cool! I've never seen this before. | amelius wrote: | The downside is of course that you need a lot of clock | cycles before the data reaches the corresponding device, | which makes this too inefficient for certain | applications. | TickleSteve wrote: | Don't forget the reset lines to each device that you need to | release the bus when they get locked up! | fra wrote: | No reason why you need to reset them individually ;-). | [deleted] | whalesalad wrote: | I've been having a lot of fun learning how to work with I2C on a | Raspberry Pi with the Nerves framework. tl;dr it's an end-to-end | development framework for deploying Elixir to embedded devices. | | I2C was easy enough to understand, but understanding the obscure | ways to configure and speak to a device has been really | challenging. Spending a ton of time reading datasheets and | experimenting with assembling binary messages. | | The next time you are frustrated and unhappy with the state of | modern web development (REST and/or GQL) take a ublox GPS chip | for a spin and try to get it to give you high frequency location | data. You will think, hey gee this isn't so bad after all | compared to encoding and decoding a binary protocol by hand. | fpgaminer wrote: | > Spending a ton of time reading datasheets and experimenting | with assembling binary messages. | | That's embedded development in a nutshell. The only part you're | missing is wasting a week of your life tracking down a compiler | heisenbug, because embedded devices have niche, poorly | maintained compilers. | | Though to be honest it's a matter of taste; natural sadists | tend to "enjoy" embedded. | keithnz wrote: | not to mention bugs in the micros themselves. I lost a few | weeks of my life to one that would corrupt the stack in | certain situations in an interrupt. The only way to debug it | was with a logic anaylzer on all the memory / data lines / | interrupts and decode it into opcodes and see what it was | doing in the interrupts. Then when found, get bacck to the | manufacturer who eventually gets back and says they already | knew about it, but hadn't done and errata for it.... so | yeah.... these days I mainly do backend/front end stuff :) | whalesalad wrote: | The weekend project was recompiling the kernel and adding | missing wifi drivers. By the end of the huge yak shave (just | to use an external wifi antenna instead of onboard wifi of | pi4) I was so relieved to see the device boot and have the | wlan get an address. | jlangemeier wrote: | Take the sadomasochism one step further; do FPGA programming, | learn the joys of properly enumerated case statements (or the | hell of finding what one is blowing up your flip-flop/latch | diagram). | fpgaminer wrote: | You don't know true embedded BDSM until you've debugged | incorrect timing constraints on an FPGA ... with a customer | on the other side of the planet ... only to realize later | that they have no idea how to design an HDMI compliant | board and none of it was your own fault. | | Or spending three weeks trying to achieve timing closure on | a design, only to finally realize after much inspection of | the routed designs by myself and an IntelFPGA FAE that the | router was smoking digital crack the whole time and had no | clue how to route their own divider units? | | Or maybe the programming facility reversed bit ordering on | a batch of the FPGA's flash chips and you only learn of | that after a very, very long couple of nights of language | barrier back and forth with a flummoxed customer. | | The joys are endless. | neillyons wrote: | Sounds cool. Can you access the GPIO pins directly from Elixir? | whalesalad wrote: | Yes. You're running on Linux (buildroot) so you can basically | do anything Linux can do. | | Here is the library you would use to do this via Elixir, | https://github.com/elixir-circuits/circuits_gpio | neillyons wrote: | This article has appeared at the perfect time for me. I was just | trying to use i2c with a BMP180 temperature/pressure/altitude | sensor and a micro python board and was rather confused. Love | Hacker News | chasd00 wrote: | hah i'm doing the same thing, building an altimeter for my | son's model rocket. There are altimeters out there for sale but | i wanted to get into embedded programming as a hobby. | jhallenworld wrote: | Here's an I2C to RS-232 serial converter for long term monitoring | of an I2C bus. I needed this at one point, and made it with the | cheapest FPGA board available on eBay: | | https://github.com/jhallen/i2cmon | Zenst wrote: | Worth reading this afterwards: | https://hackaday.com/2019/04/18/all-you-need-to-know-about-i... | nlfwhulsdhouv wrote: | I2S is much closer to SPI than I2C. It really has nothing to do | with I2C other than it connects chips together on a board. | fra wrote: | Yes! I considered adding a bit about i2s, but since the article | is already clocking at ~2500 words I thought I'd leave it to | another time. | | I2S is everywhere in audio. | Zenst wrote: | Agreed, you can oversaturate the learning process and what | you have is elegant, laid out well and wonderful, also covers | the subject and I2S would be another subject. | fra wrote: | Thanks for the kind words! I was up late last night writing | this up, it's encouraging to see folks enjoy it. | vic20forever wrote: | Comparison of I2C and SPI: | https://news.ycombinator.com/item?id=9303405 | imagiko wrote: | I just want to take a moment to thanks folks over at memfault for | bringing us in depth content from the world of embdedded systems. | Be sure to check out their articles on ARM, RTOS etx. | fra wrote: | Thanks! We've been writing all the content we wish had existed | when we started out as embedded software engineers. It's | fantastic to hear from folks who enjoy reading it as much as we | do writing it. | Isamu wrote: | Very nice! I especially like that it starts with a discussion of | why you would choose to use I2C, as well as why you may not, | depending on your application: | | >I2C is not appropriate for all applications however: When higher | bandwidth is required, SPI may be the right choice and can be | found in many NOR-flash chips. MIPI can go even faster, and is | often used in displays and cameras. If reliability is a must, CAN | is the bus of choice. It is found in cars and other vehicles. | When a single device is on the bus, UART may work just as well. | _sbrk wrote: | Article misses two of the best features of CAN: Built-in, non- | corrupting collision resolution (lowest CAN ID wins) and CRC- | protected frames. The latter feature is usually done by | hardware, just as in Ethernet. | bsder wrote: | CAN also autobauds so if you have frequency drift it | compensates. That's why it forces bit transitions via bit | stuffing if it gets too many 1's or 0's in a row. | inamberclad wrote: | Probably one of the least painful digital buses. | | If anyone is wondering how to access an I2C bus from a Linux | computer, say, a raspberry pi: | | int fd = open("/dev/i2c-1", O_RDWR); | | ioctl(fd, I2C_SLAVE, [slave address here]); | | Then you can read() and write() to the device with the kernel | taking care of all the transmission details. Usually all that's | exposed is a few bytes for the registers. To set a register, | write two bytes: first the register address, and then the value. | To read a register, write the register address and then read a | byte. Most of the devices have linear address spaces, so reading | out multiple registers is as simple as reading multiple bytes. | | The i2c-tools package has some very handy CLI tools for exploring | an I2C bus. | | Electrically, the bus is an open-collector design on both ends, | so devices can only pull the lines to low, and they release them | to set them high. Don't forget pull-up resistors! | inamberclad wrote: | A quick note: buy one of the really cheap (~$10) logic | analyzers on ebay and use Sigrok/Pulseview to to watch the bits | get sent over the wire! It's an absolutely invaluable tool for | the price. | | The hardware inside those logic analyzers is fascinating in its | own right, too! | dws wrote: | To avoid clock-stretching problems, using one of the small | Arduinos that support USB serial to handle the I2C bus can help | considerably, at the expense of a bit more programming | complexity. | zeta0134 wrote: | I'm not at all familiar with this space, but I'm a bit | surprised the kernel isn't wrapping the I2C transmission and | helping to work around clock stretching issues anyway. Can | someone who's more familiar with this implementation weigh | in? It seems like that'd be the primary feature of writing to | the /dev/i2c* device instead of manually bit-banging the GPIO | pins from userland. | opencl wrote: | The Pi's hardware i2c has buggy silicon and does not handle | clock stretching properly. There's also a kernel driver to | bitbang i2c over the GPIO pins and clock stretching _does_ | work properly with that. | BubRoss wrote: | Does the PI 4 also have this problem? | TOGoS wrote: | I'd like to know why 1-wire isn't more common. It seems to me | like a more elegant protocol than I2C. Not least because every | device has a unique baked-in address so you don't need to worry | about address collisions or dip switches to alter them. | | (Also: needs one fewer wire) | londons_explore wrote: | Programming a globally unique ID per device is a large added | cost for <$0.01 devices. The device needs fuses or equivalent | so it can 'remember' it's 64 bit ID. it needs logic to both | program as well as read the fuses. | | Think of applications like LED controllers - previously with | I2C all they needed was an 8 bit shift register + comparator | for their address, and an 8 bit shift register for their | 'brightness', and an 8 bit comparator and +-50% RC oscilator | for their actual operation. Probably ~400 transistors. | | With onewire, they need a 64 bit address shift register, an | oscilator, a state machine for bus states, a counter to act as | a timer for long/short bits, multiple comparators on the timer | output. I don't think you could do it in less than 1000 | transistors. Doesn't sound like much, but when every LED in a | million pixel LED wall needs one of these circuits, it adds up! | AWildC182 wrote: | If I had to guess, using clock-less (serial) protocols like 1 | wire and UART requires some logic on each RX side to figure out | what the clock of the incoming signal is, usually a PLL of some | sort, and you'll need lots of crystal oscillators to ensure | that clocks are sufficiently stable and accurate as to ensure | reliable communication. | andyjpb wrote: | 1-wire is pretty slow (kbps max in normal mode) and very | tolerant of devices with a wide range of timing skew. | andyjpb wrote: | I have wondered this too. | | Maxim have documentation about the fact you can get device IDs | but I've never actually worked out how to get some. I guess | people didn't want to be reliant on Maxim as the numbers | authority? | | It's pretty popular for simple serial number / "Number In A | Can" applications tho'. | londons_explore wrote: | I was under the impression it used to be patent/licensing | encumbered? ___________________________________________________________________ (page generated 2020-01-22 23:00 UTC)