一、主要技术参数:
| 1. 显示模式 | 被动矩阵 (Passive Matrix) |
| 2. 显示颜色 | 单色(白色) |
| 3. 驱动占空比 | 1/64 Duty |
| 4. 像素数量 | 128 × 64 |
| 5. PCB尺寸 | 35.4 × 33.5 × 2.6 (mm) |
| 6. 有效显示区域 | 29.42 × 14.7 (mm) |
| 7. 像素间距 | 0.23 × 0.23 (mm) |
| 8. 像素尺寸 | 0.21 × 0.21 (mm) |
二、外形尺寸:

三、引脚定义:
| 1 | GND | 逻辑电路地线 这是接地引脚,必须连接到地。 |
| 2 | VCC | OLED电源输入 这是电压供应引脚,必须连接到电源。 |
| 3 | SCL | 串行时钟输入 SCL |
| 4 | SDA | 串行数据输入 SDA |
| 5 | DC | 数据/命令控制 该引脚用于控制输入数据为显示数据或命令。当引脚为高电平时,SDA 输入被视为显示数据;当引脚为低电平时,SDA 输入将被传送到命令寄存器。 |
| 6 | CS1 | OLED芯片选择引脚;低电平使能,高电平禁用。 |
| 7 | FS0 | 字库芯片数据输出引脚。 |
| 8 | CS2 | 字库芯片选择引脚;低电平使能,高电平禁用。 |
四、指令集:
| 0 | 81 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 设置对比度控制 | 双字节命令,从256级对比度中选择一级。对比度随数值增加而增加。(复位值 = 7Fh) |
| 0 | A[7:0] | A7 | A6 | A5 | A4 | A3 | A2 | A1 | A0 | ||
| 0 | DB | 1 | 1 | 0 | 1 | 1 | 0 | 1 | 1 | 设置VCOMH取消选择电平 | A[6:4]: 十六进制码 |
| A[7:0] | 0 | A6 | A5 | A4 | 0 | 0 | 0 | 0 | 000b 00h ~ 0.65 × V<sub>CC</sub> 010b 20h ~ 0.77 × V<sub>CC</sub>(复位值) 011b 30h ~ 0.83 × V<sub>CC</sub> |
||
| 0 | A6/A7 | 1 | 0 | 1 | 0 | 0 | 1 | 1 | X0 | 设置正常/反相显示 | A6h, X[0]=0b: 正常显示(复位值) 0 在RAM中:显示面板 OFF 1 在RAM中:显示面板 ON A7h, X[0]=1b: 反相显示 0 在RAM中:显示面板 ON 1 在RAM中:显示面板 OFF |
| 0 | AE/AF | 1 | 0 | 1 | 0 | 1 | 1 | 1 | X0 | 设置显示 ON/OFF | AEh, X[0]=0b: 显示 OFF(睡眠模式)(复位值) AFh, X[0]=1b: 显示 ON(正常模式) |
| 0 | A0/A1 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | X0 | 设置段重映射 | A0h, X[0]=0b: 列地址0映射到SEG0(复位值) A1h, X[0]=1b: 列地址127映射到SEG0 |
| 0 | C0/C8 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | X3 | 设置COM输出扫描方向 | C0h, X[3]=0b: 正常模式(复位值) 从COM0扫描至COM[N-1] C8h, X[3]=1b: 重映射模式 从COM[N-1]扫描至COM0 其中N为复用比 |
五、头文件:
#ifndef __SH1106_H
#define __SH1106_H
#include "stm32f1xx_hal.h"
#include "stdint.h"
#include "stdlib.h"
#include "string.h"
#include "fonts.h"
/* SH1106 OLED驱动芯片相关定义 */
#define SH1106_WIDTH 128
#define SH1106_HEIGHT 64
#define SH1106_BUFFER_SIZE (SH1106_WIDTH * SH1106_HEIGHT / 8)
/* SH1106命令定义 */
#define SH1106_SET_CONTRAST 0x81
#define SH1106_SET_NORM_INV 0xA6
#define SH1106_SET_DISP 0xAE
#define SH1106_SET_SCAN_DIR 0xC0
#define SH1106_SET_DISP_OFFSET 0xD3
#define SH1106_SET_COM_PIN_CFG 0xDA
#define SH1106_SET_VCOM_DESEL 0xDB
#define SH1106_SET_DISP_CLK_DIV 0xD5
#define SH1106_SET_PRECHARGE 0xD9
#define SH1106_SET_MUX_RATIO 0xA8
#define SH1106_SET_CHARGE_PUMP 0x8D
#define SH1106_SET_DISP_START_LINE 0x40
#define SH1106_SET_SEG_REMAP 0xA0
#define SH1106_SET_MEM_ADDR_MODE 0x20
#define SH1106_SET_COL_ADDR 0x21
#define SH1106_SET_PAGE_ADDR 0x22
/* 通信模式选择 */
#define SH1106_I2C_ADDRESS 0x78 // I2C地址
// #define SH1106_SPI_MODE // 如果使用SPI,取消注释
/* 颜色定义 */
#define BLACK 0
#define WHITE 1
#define INVERSE 2
/* 字体结构体 */
typedef struct {
const uint8_t *data;
uint16_t width;
uint16_t height;
uint8_t first_char;
uint8_t last_char;
const uint16_t *offset;
const uint8_t *width_table;
} FontDef;
/* SH1106句柄结构体 */
typedef struct {
uint16_t CurrentX;
uint16_t CurrentY;
uint8_t Inverted;
uint8_t Initialized;
uint8_t Buffer[SH1106_BUFFER_SIZE];
I2C_HandleTypeDef *hi2c; // I2C句柄
SPI_HandleTypeDef *hspi; // SPI句柄
GPIO_TypeDef *CS_Port; // 片选引脚
uint16_t CS_Pin;
GPIO_TypeDef *DC_Port; // 数据/命令引脚
uint16_t DC_Pin;
GPIO_TypeDef *RES_Port; // 复位引脚
uint16_t RES_Pin;
} SH1106_t;
/* 函数声明 */
void SH1106_Init(SH1106_t *dev, I2C_HandleTypeDef *hi2c);
void SH1106_DrawPixel(SH1106_t *dev, uint8_t x, uint8_t y, uint8_t color);
void SH1106_UpdateScreen(SH1106_t *dev);
void SH1106_Clear(SH1106_t *dev);
void SH1106_Fill(SH1106_t *dev, uint8_t color);
void SH1106_DrawLine(SH1106_t *dev, uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint8_t color);
void SH1106_DrawRectangle(SH1106_t *dev, uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t color);
void SH1106_DrawFilledRectangle(SH1106_t *dev, uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t color);
void SH1106_DrawCircle(SH1106_t *dev, uint8_t x0, uint8_t y0, uint8_t r, uint8_t color);
void SH1106_DrawFilledCircle(SH1106_t *dev, uint8_t x0, uint8_t y0, uint8_t r, uint8_t color);
void SH1106_DrawTriangle(SH1106_t *dev, uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint8_t x3, uint8_t y3, uint8_t color);
void SH1106_DrawFilledTriangle(SH1106_t *dev, uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint8_t x3, uint8_t y3, uint8_t color);
void SH1106_DrawBitmap(SH1106_t *dev, uint8_t x, uint8_t y, const uint8_t *bitmap, uint8_t w, uint8_t h, uint8_t color);
void SH1106_GotoXY(SH1106_t *dev, uint8_t x, uint8_t y);
char SH1106_Putc(SH1106_t *dev, char ch, FontDef *font, uint8_t color);
char SH1106_Puts(SH1106_t *dev, char *str, FontDef *font, uint8_t color);
void SH1106_InvertColors(SH1106_t *dev);
void SH1106_NormalColors(SH1106_t *dev);
#endif /* __SH1106_H */
六、源文件:
#include "sh1106.h"
/* 私有函数声明 */
static void SH1106_WriteCommand(SH1106_t *dev, uint8_t cmd);
static void SH1106_WriteData(SH1106_t *dev, uint8_t *data, uint16_t size);
static void SH1106_Reset(SH1106_t *dev);
static void SH1106_SetDisplayOn(SH1106_t *dev, uint8_t on);
static void SH1106_SetContrast(SH1106_t *dev, uint8_t contrast);
/* I2C通信函数 */
static void SH1106_I2C_Write(SH1106_t *dev, uint8_t reg, uint8_t *data, uint16_t size) {
uint8_t buffer[size + 1];
buffer[0] = reg;
memcpy(&buffer[1], data, size);
HAL_I2C_Master_Transmit(dev->hi2c, SH1106_I2C_ADDRESS, buffer, size + 1, HAL_MAX_DELAY);
}
/* 写命令函数 */
static void SH1106_WriteCommand(SH1106_t *dev, uint8_t cmd) {
SH1106_I2C_Write(dev, 0x00, &cmd, 1);
}
/* 写数据函数 */
static void SH1106_WriteData(SH1106_t *dev, uint8_t *data, uint16_t size) {
SH1106_I2C_Write(dev, 0x40, data, size);
}
/* 复位函数 */
static void SH1106_Reset(SH1106_t *dev) {
if (dev->RES_Port) {
HAL_GPIO_WritePin(dev->RES_Port, dev->RES_Pin, GPIO_PIN_RESET);
HAL_Delay(10);
HAL_GPIO_WritePin(dev->RES_Port, dev->RES_Pin, GPIO_PIN_SET);
HAL_Delay(10);
}
}
/* 初始化函数 */
void SH1106_Init(SH1106_t *dev, I2C_HandleTypeDef *hi2c) {
dev->hi2c = hi2c;
dev->CurrentX = 0;
dev->CurrentY = 0;
dev->Inverted = 0;
dev->Initialized = 0;
// 硬件复位
SH1106_Reset(dev);
// 初始化序列
SH1106_WriteCommand(dev, SH1106_SET_DISP | 0x00); // 显示关闭
SH1106_WriteCommand(dev, SH1106_SET_MUX_RATIO);
SH1106_WriteCommand(dev, 0x3F); // 多路复用比例
SH1106_WriteCommand(dev, SH1106_SET_DISP_OFFSET);
SH1106_WriteCommand(dev, 0x00); // 显示偏移
SH1106_WriteCommand(dev, SH1106_SET_DISP_START_LINE | 0x00);
SH1106_WriteCommand(dev, SH1106_SET_SEG_REMAP | 0x01); // 段重映射
SH1106_WriteCommand(dev, SH1106_SET_SCAN_DIR | 0x08); // 扫描方向
SH1106_WriteCommand(dev, SH1106_SET_COM_PIN_CFG);
SH1106_WriteCommand(dev, 0x12); // COM引脚配置
SH1106_SetContrast(dev, 0x7F); // 对比度设置
SH1106_WriteCommand(dev, SH1106_SET_NORM_INV); // 正常显示
SH1106_WriteCommand(dev, SH1106_SET_DISP_CLK_DIV);
SH1106_WriteCommand(dev, 0x80); // 显示时钟分频
SH1106_WriteCommand(dev, SH1106_SET_PRECHARGE);
SH1106_WriteCommand(dev, 0x22); // 预充电周期
SH1106_WriteCommand(dev, SH1106_SET_VCOM_DESEL);
SH1106_WriteCommand(dev, 0x30); // VCOMH取消选择级别
SH1106_WriteCommand(dev, SH1106_SET_CHARGE_PUMP);
SH1106_WriteCommand(dev, 0x14); // 电荷泵设置
SH1106_WriteCommand(dev, SH1106_SET_DISP | 0x01); // 显示开启
HAL_Delay(100);
// 清屏
SH1106_Clear(dev);
SH1106_UpdateScreen(dev);
dev->Initialized = 1;
}
/* 设置对比度 */
static void SH1106_SetContrast(SH1106_t *dev, uint8_t contrast) {
SH1106_WriteCommand(dev, SH1106_SET_CONTRAST);
SH1106_WriteCommand(dev, contrast);
}
/* 画点函数 */
void SH1106_DrawPixel(SH1106_t *dev, uint8_t x, uint8_t y, uint8_t color) {
if (x >= SH1106_WIDTH || y >= SH1106_HEIGHT) {
return;
}
if (dev->Inverted) {
color = !color;
}
switch(color) {
case WHITE:
dev->Buffer[x + (y / 8) * SH1106_WIDTH] |= (1 << (y % 8));
break;
case BLACK:
dev->Buffer[x + (y / 8) * SH1106_WIDTH] &= ~(1 << (y % 8));
break;
case INVERSE:
dev->Buffer[x + (y / 8) * SH1106_WIDTH] ^= (1 << (y % 8));
break;
}
}
/* 更新屏幕显示 */
void SH1106_UpdateScreen(SH1106_t *dev) {
uint8_t page;
for (page = 0; page < 8; page++) {
SH1106_WriteCommand(dev, 0xB0 + page); // 设置页地址
SH1106_WriteCommand(dev, 0x02); // 设置列地址低4位
SH1106_WriteCommand(dev, 0x10); // 设置列地址高4位
SH1106_WriteData(dev, &dev->Buffer[SH1106_WIDTH * page], SH1106_WIDTH);
}
}
/* 清屏函数 */
void SH1106_Clear(SH1106_t *dev) {
memset(dev->Buffer, 0x00, SH1106_BUFFER_SIZE);
}
/* 填充屏幕 */
void SH1106_Fill(SH1106_t *dev, uint8_t color) {
uint8_t fill_value = (color == WHITE) ? 0xFF : 0x00;
memset(dev->Buffer, fill_value, SH1106_BUFFER_SIZE);
}
/* 画线函数 */
void SH1106_DrawLine(SH1106_t *dev, uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint8_t color) {
int16_t dx = abs(x2 – x1);
int16_t dy = abs(y2 – y1);
int16_t sx = (x1 < x2) ? 1 : -1;
int16_t sy = (y1 < y2) ? 1 : -1;
int16_t err = dx – dy;
int16_t err2;
while (1) {
SH1106_DrawPixel(dev, x1, y1, color);
if (x1 == x2 && y1 == y2) break;
err2 = 2 * err;
if (err2 > -dy) {
err -= dy;
x1 += sx;
}
if (err2 < dx) {
err += dx;
y1 += sy;
}
}
}
/* 画矩形 */
void SH1106_DrawRectangle(SH1106_t *dev, uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t color) {
SH1106_DrawLine(dev, x, y, x + w, y, color);
SH1106_DrawLine(dev, x + w, y, x + w, y + h, color);
SH1106_DrawLine(dev, x + w, y + h, x, y + h, color);
SH1106_DrawLine(dev, x, y + h, x, y, color);
}
/* 画填充矩形 */
void SH1106_DrawFilledRectangle(SH1106_t *dev, uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t color) {
for (uint8_t i = 0; i <= h; i++) {
SH1106_DrawLine(dev, x, y + i, x + w, y + i, color);
}
}
/* 画圆 */
void SH1106_DrawCircle(SH1106_t *dev, uint8_t x0, uint8_t y0, uint8_t r, uint8_t color) {
int16_t f = 1 – r;
int16_t ddF_x = 1;
int16_t ddF_y = -2 * r;
int16_t x = 0;
int16_t y = r;
SH1106_DrawPixel(dev, x0, y0 + r, color);
SH1106_DrawPixel(dev, x0, y0 – r, color);
SH1106_DrawPixel(dev, x0 + r, y0, color);
SH1106_DrawPixel(dev, x0 – r, y0, color);
while (x < y) {
if (f >= 0) {
y–;
ddF_y += 2;
f += ddF_y;
}
x++;
ddF_x += 2;
f += ddF_x;
SH1106_DrawPixel(dev, x0 + x, y0 + y, color);
SH1106_DrawPixel(dev, x0 – x, y0 + y, color);
SH1106_DrawPixel(dev, x0 + x, y0 – y, color);
SH1106_DrawPixel(dev, x0 – x, y0 – y, color);
SH1106_DrawPixel(dev, x0 + y, y0 + x, color);
SH1106_DrawPixel(dev, x0 – y, y0 + x, color);
SH1106_DrawPixel(dev, x0 + y, y0 – x, color);
SH1106_DrawPixel(dev, x0 – y, y0 – x, color);
}
}
/* 画填充圆 */
void SH1106_DrawFilledCircle(SH1106_t *dev, uint8_t x0, uint8_t y0, uint8_t r, uint8_t color) {
int16_t f = 1 – r;
int16_t ddF_x = 1;
int16_t ddF_y = -2 * r;
int16_t x = 0;
int16_t y = r;
SH1106_DrawLine(dev, x0 – r, y0, x0 + r, y0, color);
while (x < y) {
if (f >= 0) {
y–;
ddF_y += 2;
f += ddF_y;
}
x++;
ddF_x += 2;
f += ddF_x;
SH1106_DrawLine(dev, x0 – x, y0 + y, x0 + x, y0 + y, color);
SH1106_DrawLine(dev, x0 – x, y0 – y, x0 + x, y0 – y, color);
SH1106_DrawLine(dev, x0 – y, y0 + x, x0 + y, y0 + x, color);
SH1106_DrawLine(dev, x0 – y, y0 – x, x0 + y, y0 – x, color);
}
}
/* 设置光标位置 */
void SH1106_GotoXY(SH1106_t *dev, uint8_t x, uint8_t y) {
dev->CurrentX = x;
dev->CurrentY = y;
}
/* 显示单个字符 */
char SH1106_Putc(SH1106_t *dev, char ch, FontDef *font, uint8_t color) {
uint32_t i, b, j;
if (ch < font->first_char || ch > font->last_char) {
ch = '?';
}
// 获取字符在字体数据中的偏移
uint32_t char_offset = (ch – font->first_char) * font->height * (font->width / 8 + ((font->width % 8) ? 1 : 0));
for (i = 0; i < font->height; i++) {
for (j = 0; j < font->width; j++) {
b = font->data[char_offset + i * ((font->width + 7) / 8) + j / 8];
b &= (1 << (7 – (j % 8)));
if (b) {
SH1106_DrawPixel(dev, dev->CurrentX + j, dev->CurrentY + i, color);
} else {
SH1106_DrawPixel(dev, dev->CurrentX + j, dev->CurrentY + i, !color);
}
}
}
dev->CurrentX += font->width;
return ch;
}
/* 显示字符串 */
char SH1106_Puts(SH1106_t *dev, char *str, FontDef *font, uint8_t color) {
while (*str) {
if (SH1106_Putc(dev, *str, font, color) != *str) {
return *str;
}
str++;
}
return *str;
}
/* 反色显示 */
void SH1106_InvertColors(SH1106_t *dev) {
dev->Inverted = !dev->Inverted;
}
/* 正常显示 */
void SH1106_NormalColors(SH1106_t *dev) {
dev->Inverted = 0;
}
七、字体头文件:
#ifndef __FONTS_H
#define __FONTS_H
#include <stdint.h>
/* 字体结构体 */
typedef struct {
const uint8_t *data;
uint16_t width;
uint16_t height;
uint8_t first_char;
uint8_t last_char;
const uint16_t *offset;
const uint8_t *width_table;
} FontDef;
/* 字体定义 */
extern FontDef Font_7x10;
extern FontDef Font_11x18;
extern FontDef Font_16x26;
/* 8×16 ASCII字体数据 */
extern const uint8_t Font8x16[];
/* 6×8 ASCII字体数据 */
extern const uint8_t Font6x8[];
#endif /* __FONTS_H */
八、应用示例:
#include "main.h"
#include "sh1106.h"
#include "fonts.h"
/* 全局变量 */
SH1106_t oled;
I2C_HandleTypeDef hi2c1;
/* 系统初始化 */
void System_Init(void) {
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_I2C1_Init();
}
void OLED_Test(void) {
// 初始化OLED
SH1106_Init(&oled, &hi2c1);
// 测试1: 清屏并显示文字
SH1106_Clear(&oled);
SH1106_GotoXY(&oled, 10, 10);
SH1106_Puts(&oled, "Hello World!", &Font_7x10, WHITE);
SH1106_UpdateScreen(&oled);
HAL_Delay(2000);
// 测试2: 画图形
SH1106_Clear(&oled);
SH1106_DrawRectangle(&oled, 10, 10, 100, 40, WHITE);
SH1106_DrawFilledCircle(&oled, 64, 32, 15, WHITE);
SH1106_UpdateScreen(&oled);
HAL_Delay(2000);
// 测试3: 显示数字和变量
SH1106_Clear(&oled);
SH1106_GotoXY(&oled, 20, 5);
SH1106_Puts(&oled, "Temp:", &Font_7x10, WHITE);
char temp_str[20];
float temperature = 25.5f;
sprintf(temp_str, "%.1f C", temperature);
SH1106_GotoXY(&oled, 60, 5);
SH1106_Puts(&oled, temp_str, &Font_7x10, WHITE);
// 画进度条
uint8_t progress = 75; // 75%
SH1106_DrawRectangle(&oled, 20, 30, 84, 10, WHITE);
SH1106_DrawFilledRectangle(&oled, 22, 32, progress * 80 / 100, 6, WHITE);
SH1106_GotoXY(&oled, 20, 45);
SH1106_Puts(&oled, "Progress:", &Font_7x10, WHITE);
char prog_str[10];
sprintf(prog_str, "%d%%", progress);
SH1106_GotoXY(&oled, 80, 45);
SH1106_Puts(&oled, prog_str, &Font_7x10, WHITE);
SH1106_UpdateScreen(&oled);
}
/* 主函数 */
int main(void) {
System_Init();
while (1) {
OLED_Test();
HAL_Delay(5000);
}
}



![[开源]基于STM32单片机WIFI智能窗户系统 Onenet云平台设计HK-003-171主机测评](https://www.171host.com/wp-content/uploads/2026/09/20260903070616-6a991c6823886-220x150.jpg)

