i2c-oled.c

STM32F103 "Blue Pill" (STM32F103C8T6) · File : i2c-oled.c · Language : C · Use case : Écrire sur un écran OLED SSD1306 en I2C avec la HAL

The SSD1306 I2C protocol boils down to two control bytes: 0x00 for a command, 0x40 for data. This program sends the init sequence then a test image, with no library — the best way to understand master I2C.

Hardware

A Blue Pill STM32F103C8T6 (Cortex-M3 @ 72 MHz, 64 KB flash) powered by micro-USB or external 3.3 V; programmed over SWD or the serial bootloader. 1 SSD1306 I2C OLED module (0x3C), 3.3 V supply.

Wiring

SDA to PB7, SCL to PB6 (typical I2C1 pins on Nucleo boards, confirm in the .ioc), VCC 3.3 V, GND.

How it works

Each I2C frame starts with address byte 0x78 then the control byte. The sequence sets the charge pump and scan; 0xAF turns the panel on, then the 1024-byte frame fills the GDDRAM.

Running it

STM32CubeIDE project (STM32F103C8) or Arduino (STM32 core): program over SWD (ST-LINK, DAPLink adapter) or the serial bootloader (BOOT0 to 1). Note: PC13 LED is active LOW.

Variants

Add a 5x7 font to print strings (printf → bitmap), or switch to I2C DMA to free the CPU during refresh.

i2c-oled.c

// OLED SSD1306 (0x3C) : envoi de commandes/data en I2C HAL.
#include "main.h"
#include <string.h>

extern I2C_HandleTypeDef hi2c1;

#define SSD1306_ADDR 0x78 // 0x3C << 1 (format HAL)

static void cmd(uint8_t c) {
  uint8_t d[2] = { 0x00, c };        // 0x00 = byte de commande
  HAL_I2C_Master_Transmit(&hi2c1, SSD1306_ADDR, d, 2, 10);
}
static void data(const uint8_t *p, uint16_t n) {
  uint8_t buf[129];
  buf[0] = 0x40;                       // 0x40 = byte de donnees
  memcpy(buf + 1, p, n);
  HAL_I2C_Master_Transmit(&hi2c1, SSD1306_ADDR, buf, n + 1, 100);
}
static void init(void) {
  const uint8_t seq[] = { 0xAE, 0xD5, 0x80, 0xA8, 0x3F, 0xD3, 0x00,
                          0x40, 0x8D, 0x14, 0x20, 0x00, 0xA1, 0xC8,
                          0xDA, 0x12, 0x81, 0xFF, 0xD9, 0xF1, 0xDB,
                          0x40, 0xA4, 0xA6, 0xAF };                 // AF = on
  for (unsigned i = 0; i < sizeof(seq); i++) cmd(seq[i]);
}

int main(void) {
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_I2C1_Init();   // SCL/PB6, SDA/PB7 dans le .ioc (400 kHz)
  init();
  uint8_t trame[1024];
  memset(trame, 0xAA, sizeof(trame)); // damier de test 128x64
  data(trame, sizeof(trame));
  for (;;);
}

← Back to the board