random kludges

notes for my future self

STM32 IO Pins And I2C

I2C data is ‘open drain’.

I spent way too long on figuring out this one. And it feels like one of those issues that are certain to come back and haunt at some point. (Unless I now jinx it by writing all this down).

So. While trying to do some I2C communication using libOpenCM3 on a STM32 discovery board that libOpenCM3 did not have an I2C example for, I run into this: logic analyzer

Here the STM32 writes the address byte to I2C, and gets a NACK (at about 80us). (An ACK would be sent by the slave device after receiving a byte by it pulling down the data line for the clock cycle following the transmitted byte.)

Looking at the blue oscilloscope line it is clear that something tries to pull the data line down, but fails.

STM GPIOs

The trick is to RTFM. STM GPIO Schematic of a STM32 GPIO pin (image from STM, RM0383).

In order to have the I2C peripheral’s lines visible on the pin, one must:

The problem was that output control has push-pull as default (which I didn’t notice and override). In this mode both the P-MOS and N-MOS are used, i.e. the output is always connected to either Vdd or GND.

Whereas in open drain mode (quoting the fine RM) “A “0” in the Output register activates the N-MOS whereas a “1” in the Output register leaves the port in Hi-Z (the P-MOS is never activated)”.

For the ACK/NACK, the I2C peripheral leaves the data line into ‘1’, where only the pull-up resistor pulls the line up - when in open drain mode…

Or in libOpenCM3 terms:

rcc_periph_clock_enable(RCC_GPIOB);
gpio_set_af(GPIOB, GPIO_AF4, GPIO9);
gpio_set_output_options(GPIOB, GPIO_OTYPE_OD, GPIO_OSPEED_25MHZ, GPIO9);
gpio_mode_setup(GPIOB, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO9);
Home