# OLED：安装一次，以后只换内容

**更换文字、字体、尺寸或图片，只需更新 OLED_Content.h。** 驱动、OLED_User.h、main.c 和 Keil 工程配置都不需要跟着修改。

## 1. 第一次安装：只做一次

网页准备好初始内容，展开「首次安装」，选择 SSD1306 或 CH1116。默认配置 I²C1、128×64；SSD1306 使用 0x78，CH1116 使用 0x7A（均为 8 位写地址）；不同时再展开连接设置修改。点击 **「下载首次安装包」**。

### 先选屏幕、调整显示位置

在「点阵预览」选择屏幕：0.96 寸或 1.3 寸为 128×64，0.91 寸为 128×32。这里列的是常见规格，请以模块实际分辨率为准，控制器另行选择。CH1116 当前驱动只支持 128×64。

在预览旁输入 X、Y，或拖动坐标条，文字和图片会立即移动。左上角是 (0, 0)，X 向右、Y 向下；「回到左上角」可复位。文字会从所设 X 位置换行，超出屏幕的部分裁切。下载 OLED_Content.h 后覆盖 BSP 中的同名文件，再编译烧录，位置会随内容一起更新。首次安装包也会包含当前坐标。

第一次安装时，屏幕分辨率会同步写入驱动配置。如果已安装后更换了不同分辨率的硬件，还需在 BSP/OLED_STM32.h 中把 OLED_SCREEN_HEIGHT 改为 32 或 64，与预览一致。只移动位置不需要修改驱动。

### 第一步：在资源管理器中新建 BSP 文件夹

先在 Keil 外，用 Windows 资源管理器打开工程所在目录，新建文件夹，命名为 **BSP**。以 OLED 工程为例，BSP 与 Core、Drivers、MDK-ARM 同级：

```text
OLED/
├─ BSP/
│  ├─ OLED.c
│  ├─ OLED.h
│  ├─ OLED_STM32.c
│  ├─ OLED_STM32.h
│  ├─ OLED_User.h
│  └─ OLED_Content.h
├─ Core/
├─ Drivers/
└─ MDK-ARM/
   └─ OLED.uvprojx
```

解压首次安装包，将包内 BSP 文件夹中的 **全部 .c 和 .h 文件**复制到你创建的 BSP 文件夹。两个 .c 是驱动实现，四个 .h 是接口、配置和显示内容。只使用与你屏幕对应的一套驱动。

### 第二步：在 Keil 中新建 BSP 组，添加 .c 文件

1. 用 Keil 打开工程的 .uvprojx 文件。
2. 在左侧 Project 工程树中，右键工程目标（例如 Target 1 或 OLED），选择 **Add Group**，将新组命名为 **BSP**。
3. 右键刚创建的 BSP 组，选择 **Add Existing Files to Group 'BSP'**。
4. 打开磁盘上的 BSP 文件夹，选中 **OLED.c 和 OLED_STM32.c**，点击 Add，再点 Close。

Keil 的 BSP 组是工程中的文件分组，资源管理器里的 BSP 是实际存放文件的文件夹，两处都要创建。**组里只添加 .c 文件；.h 文件通过下一步的包含路径使用。** 同一个 .c 文件只添加一次。

### 第三步：设置 BSP 头文件包含路径

1. 点击 **Project → Options for Target**（也可右键工程目标，选择 Options for Target）。
2. 打开 **C/C++** 选项卡，在 **Include Paths** 右侧点击 **…**。
3. 新增一条路径，通过浏览按钮选择刚才的 **BSP 文件夹**，点击 OK 保存。

按上面的目录结构，.uvprojx 位于 MDK-ARM 内，所以包含路径通常显示为 **..\BSP**。目录不同就通过浏览选择实际 BSP 文件夹，不要照抄路径。选择文件夹即可，不要选择某个 .h 文件；保留 CubeMX 已有的其他包含路径。

### 第四步：添加启动调用，编译烧录

main.c 的 USER CODE Includes 区域加：

```c
#include "OLED_User.h"
```

在 CubeMX 的 MX_I2Cx_Init() 之后、USER CODE 2 区域加：

```c
OLED_Start();
```

编译、烧录即可。保留 CubeMX 自动生成的时钟、GPIO 和 I²C 初始化。OLED_Start 已包含初始化与显示，不要在 while(1) 中反复调用。

## 2. 已经安装：以后只做这三步

1. 在网页输入新文字、调整字体，或选择新图片。
2. 点击 **「下载 OLED_Content.h」**。
3. 用下载的文件覆盖工程的 **BSP/OLED_Content.h**，然后在 Keil 编译、烧录。

完成。不用再下载驱动，不用再次添加 .c 文件，不用修改 OLED_Start()。文字切换为图片、图片切回文字，也都只换同一个 OLED_Content.h。更新内容不需要重新选择控制器，同一份内容可供 SSD1306 和 CH1116 两种驱动使用。

浏览器如果把文件保存为 OLED_Content (1).h，请恢复成 **OLED_Content.h** 后再覆盖。不要把新文件加到另一个头文件目录，避免工程仍使用旧文件。

内容编译进单片机固件，所以替换头文件后仍需重新编译和烧录；这不是重新移植驱动。一次更新包含当前预览中的全部文字和图片，从所设坐标显示，超出屏幕的部分会裁切。

### 文字、图片：看着预览调整即可

输入文字后会自动生成，不用再点生成按钮。导入图片后，图片与文字一起显示；切换「文字」「图片」只是在选择要编辑哪一项，X、Y 和尺寸分别保存。重叠时文字覆盖图片。

只想显示图片：点击「清空文字」。只想显示文字：点击「移除图片」。当前支持一张图片与一段可换行文字；图片可以事先拼成多个图标。图片与像素编辑不跨网页刷新保存，刷新后请重新导入图片。

所有内容文件都自动包含常用数字、英文字母、标点和空格。显示 25.6 变成 26.1 不需要重新取模。汉字按需生成：例如要显示「温度」「湿度」，输入包含「温湿度」的文字即可。选择 16×16 时，汉字是 16×16，数字和字母自动是 8×16，数值与汉字垂直居中对齐。行间距可在网页直接调节，0 表示不额外留空，数值越小越紧凑。

### 每行字号与行间距

在文字框按回车换行，下方会自动出现「第 1 行」「第 2 行」等字号设置。每行分别填写字号，例如第 1 行 20、第 2 行 12；字号单位是像素，汉字使用该尺寸，数字和字母自动使用较窄宽度。相同汉字在不同行也可以使用不同字号。

「行间距」是两行字框之间额外留出的像素数，可设为 0～32；建议从 0、1、2 试起，预览会立即变化。第二行起点 = 第一行起点 + 第一行字号高度 + 行间距。设置适用于所有换行，包含自动折行。

字号按输入框中回车分隔的行号保存；自动折行沿用当前行字号，空行也占用该行高度及间距。新增行使用下方统一尺寸；点击下方尺寸按钮或修改统一宽高，会将所有行统一设置。字号和间距在本机浏览器保存，刷新后保留。

改好后仍只下载 OLED_Content.h，覆盖 BSP 中同名文件并编译烧录。OLED_UpdateText 和温湿度显示函数会自动使用这些行设置，无需修改调用代码。实时文本超过已设置的行数时，额外行沿用最后一行字号。每次单独调用 OLED_DrawText 都从第 1 行的字号开始。

### 每行单独设置 X、Y

「每行字号与位置」中，每一行都有字号、X、Y。X 向右增大，Y 向下增大，左上角为 (0,0)。例如第 1 行 X=0、Y=0，第 2 行 X=40、Y=30，两行就分别从这两个位置开始显示。

坐标留空表示自动：X 跟随预览区的文字起点，Y 按上一行的实际结束位置、字号和行间距向下排列。只填写 X 或只填写 Y 也可以；已填写的坐标按屏幕绝对位置固定，调整整体文字起点或行间距不会改变该轴。自动折行仍从该行自己的 X 开始，沿用该行字号和行间距。

「恢复自动排列」清除所有行的独立坐标，字号保留。换成 128×32 屏幕时，已填写的 Y 超过 31 会自动收回到 31，仍需检查文字是否裁切。重叠时后面的文字覆盖前面的文字；建议在预览中错开。坐标跟随行号保存，新增或删除行后检查排版。

改好后重新下载 OLED_Content.h，覆盖 BSP 中同名文件并编译烧录即可。OLED_Start、OLED_SetNumber 和 OLED_Refresh 的调用都不变。OLED_ShowText / OLED_DrawText 手动指定起点时，已配置坐标相对网页原文字起点一起偏移；普通网页模板更新使用网页中的坐标。

### 固定文字与实时数值分开维护

固定的标题、姓名、说明只在网页输入。数值变化的位置点击「插入温度数值」「插入湿度数值」或「插入其他数值」，按钮会在光标处插入一个变量标记。例如：

```text
智能环境监测终端
姓名:张三 学号:12345678
温度阈值:30C
温度:{{temperature}} 湿度:{{humidity}}%
```

双大括号中的英文是变量名，不会显示到屏幕。可以通过按钮插入，不用手写。预览区显示示例数值，可修改预览值检查长数字能否放下；真正的启动画面先显示 --，等待传感器数据，避免把示例数值当成真实测量。

调整各行字号与行间距后，下载 OLED_Content.h 并替换 BSP 内同名文件。网页「复制到传感器读取成功后的代码」会按变量名生成调用，复制到 while 循环中传感器读取成功的位置，例如：

```c
OLED_SetNumber("temperature", temperature, 1);
OLED_SetNumber("humidity", humidity, 1);
OLED_Refresh();
```

引号内的英文对应网页变量名；第二个参数换成你的实际变量，最后的 1 表示保留一位小数，可填 0～3。SetNumber 只保存数值，最后 Refresh 一起刷新。中文、标题、图片、各行字号和位置会自动保留，无需自己拼接 snprintf，无需在 Keil 输入中文或开启浮点 printf。

OLED_Start() 仍在 I²C 初始化之后、while 循环之前调用一次。以后新增或修改固定文字，只改网页并替换 OLED_Content.h，再编译烧录；代码中的数值更新调用不变。新增实时变量时才需要增加一行 SetNumber；删除或改名实时变量时，同步删除或修改相应赋值调用。不要在刷新模板之后再调用 OLED_UpdateText(text)，否则会用整段字符串覆盖画面。

### 温湿度：也可以只调用一行

点击「温湿度示例」会自动插入 temperature 和 humidity 两个变量。你可以在它们前后添加标题、姓名等任意固定文字，再调整排版。每次读取成功后，只调用：

```c
OLED_ShowTemperatureHumidity(temperature, humidity);
```

它会给网页中的 temperature、humidity 赋值并刷新整幅画面，固定文字与图片保留。它与上面的两次 SetNumber 加一次 Refresh 二选一即可，不要重复调用。

这个便捷函数接收摄氏温度 -40～85、相对湿度 0～100，显示一位小数；超出范围返回 -1 并保留画面。显示其他量或其他范围时，用通用的 OLED_SetNumber。温湿度示例会重置为两行 16 像素字号；64 高屏幕示例间距为 2，32 高屏幕为 0。

### 更多数据：电压、光照、阈值也一样

例如网页输入「电压:{{voltage}}V」，程序调用：

```c
OLED_SetNumber("voltage", voltage, 2);
OLED_Refresh();
```

需要更新英文状态文字时可调用 OLED_SetTextValue("status", "OK")，并在网页放入 {{status}}，最后调用 OLED_Refresh()。网页预览值仅用于检查排版；更长数值可能折行或裁切，请按最大可能长度检查预览。

变量名使用英文字母或下划线开头，其后可用字母、数字、下划线，最长 24 字符；最多 16 个不同变量。同名变量可放在多个位置，一次赋值一起更新。数值范围为 -1000000～1000000，小数位 0～3。文本值最多 47 个 UTF-8 字节，不允许换行；中文文本值仍需预先取模。整屏拼接内容最多 1023 个 UTF-8 字节。参数、名称或长度错误返回 -1；刷新 I²C 错误返回 -2。未赋值变量保持 --。

### 完全自定义整段文字（按需使用）

OLED_UpdateText(text) 仍可使用，但会直接替换整段文字，包含所有固定行。通常使用网页变量配合 SetNumber / Refresh 更省事。自己在代码字符串里写中文时仍要保持 UTF-8；使用网页变量则不用在 Keil 写中文。

## 3. 接线与黑屏排查

STM32F103C8T6 未重映射的 I²C1：SCL→PB6、SDA→PB7，句柄 hi2c1。I²C2：SCL→PB10、SDA→PB11，句柄 hi2c2。必须与 CubeMX、实际接线和驱动配置一致。模块有 RESET 引脚时，按模块要求先复位。

SSD1306 支持 128×64 / 128×32；CH1116 支持 128×64，默认列偏移 2。控制器和地址根据模块资料选择，不能只看尺寸判断。更换硬件控制器或 I²C 接线时，才需要更换驱动或配置。

黑屏时在 Keil Watch 查看 **OLED_InitResult** 和 **OLED_ShowResult**：0 为调用成功，-1 为参数错误，-2 为 I²C 发送失败，-3 为未初始化，-99 为尚未执行。网页生成的内容已处理中文编码，无需手写 UTF-8 转义。

两个状态都为 0 仍黑屏时，要继续核对控制器型号、模块供电和复位。两套驱动均通过模拟测试与 Keil 编译，尚未在你的实物屏幕上验证。
