# I2C Communication Between STM32F3 and SSD1306

I am trying to write data to my 128x32 OLED display. I researched it online and found that it has the SSD1306 controller connected to the display. It uses the I2C protocol and it only has 4 pins (VCC, GND, SDA, SCL). I am using the SMT32F303RET as my main controller and i want to write some data on the display over the I2C connection. I am programming the controller using registers and I am not using any other external libraries other then the "stm32f3xx.h". I wish to stay this way because I am learning a lot more then when I'am using finished libraries.

I am connecting the SDA and SCL pins of the display directly to the SDA and SCL pins of my controller. I am setting the SDA and SCL pins as pull-up trough software.

simulate this circuit – Schematic created using CircuitLab

My main problem is that i can't get the I2C communication working properly. I have reed through all the I2C documentation in the reference maunual for the STM32F303RET and folow the steps carefully. Also I followed the rules and information given in the data sheet for the SSD1306 controller. But I had no luck...

This is the code I wrote for configuring the I2C and writing data trough it:

void i2c_init() {
//Enable I2C clock
RCC->APB1ENR |= RCC_APB1ENR_I2C2EN;

//Pin configuration
RCC->AHBENR |= RCC_AHBENR_GPIOFEN;

GPIOF->MODER |= GPIO_MODER_MODER0_1 | GPIO_MODER_MODER1_1;
GPIOF->MODER &= ~(GPIO_MODER_MODER0_0 | GPIO_MODER_MODER1_0);

GPIOF->OTYPER |= GPIO_OTYPER_OT_0 | GPIO_OTYPER_OT_1;

GPIOF->OSPEEDR &= ~(GPIO_OSPEEDER_OSPEEDR0 | GPIO_OSPEEDER_OSPEEDR1);

GPIOF->PUPDR |= GPIO_PUPDR_PUPDR0_0 | GPIO_PUPDR_PUPDR1_0;
GPIOF->PUPDR &= ~(GPIO_PUPDR_PUPDR0_1 | GPIO_PUPDR_PUPDR1_1);

GPIOF->AFR[0] |= 0x44;

//Timing configuration
I2C2->TIMINGR = 0x0000020C;

//CR2 configuration
I2C2->CR2 |= I2C_CR2_NACK | (0x3d << 1);

//CR1 configuration
I2C2->CR1 |= I2C_CR1_ANFOFF;

//Peripheral enable
I2C2->CR1 |= I2C_CR1_PE;
}

void i2c_write() {
I2C2->CR2 &= ~(I2C_CR2_RD_WRN);
I2C2->CR2 |= I2C_CR2_START | (1 << 16);

while(I2C2->CR2 & I2C_CR2_START);

I2C2->TXDR = (uint8_t)0xA5;

while(!(I2C2->ISR & I2C_ISR_TXE));

I2C2->CR2 = I2C_CR2_STOP;

while (I2C2->CR2 & I2C_CR2_STOP);
}


The main error I am getting is NACK (not acknowledged). I know this because the NACKF and STOPF bits in the ISR register turn on as soon as I enable the START bit in the CR2 register. Also before I change some timing settings I was getting the ARLO (arbitration lost) error.

• understand that some of these inexpensive displays do not ack, write your own code or change the library not to look for an ack. rarely do devices conform to i2c nor spi specs, always assume they dont, and if they do then great. – old_timer May 26 '19 at 18:46
• An I2C peripheral that "does not ACK" is simply not an I2C peripheral of any sort, as the ACK is fundamental to the very idea of the protocol. An SSD1306 most defintiely does ACK when correctly addressed. – Chris Stratton Jul 11 at 3:37

Not meaning to solve your specific problem with i2c, here is some advice :

SSD1306 also supports SPI and 8-bit parallel. You may want to switch to SPI. You will get faster communication while still using reasonnably few pins (3 pins). Communicating faster with your OLED display will provide a better refresh rate and avoid the sweeping effect when pushing pixels to the display controller.

I think it's worth considering it.

• Well if the OLED he has is configured in I²C mode, he can't change it - the decision is usually done on the glass under the blob of glue which protects the controller. I do agree that SPI is a better choice, but sometimes it's not an option. – Arsenal May 26 '19 at 9:48
• @Arsenal , ah, true, i missed that :( – FrProg34 May 26 '19 at 9:57

In general, internal pull-up\down resistor in ICs are usually what is called 'weak' pull up\down. This means that the resistor value doing the pulling is large (I would say typically >50kohm, and you can find the exact value in the data-sheet).
This can contribute to 2 factors:
1. current through the pull up which impacts the time it takes the board and chip capacitance to charge (signal rise time)
2. current carrying capacity of the relative pin. Smaller resistance values allows greater currents which might damage the IC

Additionally, you have to consider logic levels required between your STM32 and the display controller. If they are working in different logic levels (eg. 3.3V and 5V) it is possible that your signals don't cross the required thresholds.

Lastly, if you post your schematics it will be much easier to comment, rather then just general comments.

• Thank you for the quick response! I tried what you sugested. I found the value of the built in pull-up resistor and the value is around 40kohm. I then tried to connect my own pull-up resistor, setting the GPIO to no pull-up pull-down. I tried connecting it to 5V and 3.3V and changing the suply voltage to the ssd1306 controller. Also tried a lot of different resistor values ranging from 1kohm up to 50kohm. Al of these tests gave me the same result. There was no error this time, but the I2C was busy after setting the START bit. I guess the controller didn't receive anything. – jose2000 May 26 '19 at 12:06
• In that case I would suggest to get your hands on a nice and simple scope and start probing the lines to see whether there are signals actually coming out. – ZelmaB May 27 '19 at 14:15