基于小智派-LZ3863星闪开发版:OpenHarmony I2C 外设开发 — OLED 屏幕显示
1、实验简介
参考网址:https://gitee.com/Lockzhiner-Electronics/lz3863/tree/master/apps/b4_oled_i2c
1.1、实验目的
本实验旨在帮助学习者掌握 OpenHarmony 轻量系统中 I2C(Inter-Integrated Circuit,集成电路总线) 外设的基本使用方法。通过本实验,你将学会:
- 理解 I2C 总线的工作原理及主从通信机制;
- 使用 IoT Hardware 接口初始化 I2C 总线并配置引脚复用;
- 理解 OLED 显示屏 的显存结构与刷新机制;
- 掌握 OLED 驱动库的基本用法,实现中文字符显示;
- 完成案例代码的编译、烧录与屏幕现象观察。
1.2、实验内容
本案例在 LZ3863-星闪开发板上,通过 I2C1 总线驱动板载 OLED 屏幕,在屏幕上显示 “星闪” 两个中文字符。
| 项目 | 说明 |
|---|---|
| I2C 控制器 | I2C1(OLED_I2C_IDX = 1) |
| SDA 引脚 | GPIO15(IOT_IO_FUNC_GPIO_15_I2C1_SDA) |
| SCL 引脚 | GPIO16(IOT_IO_FUNC_GPIO_16_I2C1_SCL) |
| 通信速率 | 400 kHz(IoTI2cInit(1, 400000)) |
| OLED 地址 | 0x3D(7-bit 地址) |
| 屏幕分辨率 | 128 × 64 像素 |
| 显示内容 | 32 号字体汉字 “星”、“闪” |
| 任务名称 | oled_i2c_main |
程序将 OLED 显示逻辑封装在独立的 RTOS 线程中:上电后完成 I2C 与 OLED 初始化,清屏后在屏幕中央区域显示两个预置汉字,并刷新到屏幕。
1.3、实验环境
| 项目 | 说明 |
|---|---|
| 硬件 | LZ3863-星闪开发板(含板载 OLED 屏幕)、USB 数据线 |
| 软件 | OpenHarmony v5.1.0 源码、hb 编译工具 |
| 调试工具 | 串口助手(波特率 115200,可选,用于查看错误日志) |
| 案例路径 | applications/sample/wifi-iot/app/b4_oled_i2c/ |
2、基础知识
2.1、什么是 I2C
I2C(Inter-Integrated Circuit) 是一种常用的 两线制串行总线,仅需 SDA(数据线) 和 SCL(时钟线) 两根信号线即可实现主设备与多个从设备之间的通信。
I2C 总线的核心特点:
| 概念 | 说明 |
|---|---|
| 主从架构 | 通常由 MCU 作为主机(Master),外设作为从机(Slave) |
| 地址寻址 | 每个从设备拥有唯一的 7 位或 10 位 I2C 地址 |
| 半双工通信 | 同一时刻数据沿 SDA 单向传输,由 SCL 同步 |
| 开漏输出 | SDA、SCL 需外接上拉电阻,支持多设备挂载 |
| 通信速率 | 标准模式 100 kHz,快速模式 400 kHz,本案例使用 400 kHz |
本实验 MCU 作为 I2C 主机,OLED 控制器作为从机,通过 IoTI2cWrite() 向 OLED 发送命令和数据。
2.2、OLED 显示屏原理
本案例使用的 OLED 模块基于 SSD1306 类控制器,采用 128 列 × 64 行 的单色点阵显示。屏幕内部没有直接可写的显存接口,需要通过 I2C 发送 命令字节 和 数据字节 来控制。
OLED 显示的关键概念:
| 概念 | 说明 |
|---|---|
| 页寻址模式 | 屏幕纵向分为 8 页(Page 0~7),每页 8 行,共 64 行 |
| 显存(GRAM) | 程序维护 OLED_GRAM[128][8] 软件缓冲区,先写入显存再刷新到屏幕 |
| 命令/数据区分 | I2C 传输时首字节为控制字:0x00 表示命令,0x40 表示数据 |
| 刷新机制 | 调用 OLED_Refresh() 将显存内容按页写入 OLED |
显示流程可概括为:
绘图/文字函数 → 修改 OLED_GRAM 显存 → OLED_Refresh() → I2C 写入 OLED 硬件
2.3、I2C 与 OLED 驱动层次
本案例的软件调用层次如下:
应用层(oled_i2c_example.c)
├── IoTGpioInit() / IoSetFunc() ← 引脚复用为 I2C
├── IoTI2cInit() ← 初始化 I2C 总线
└── OLED_Init() / OLED_ShowChinese() / OLED_Refresh()
└── OLED 驱动层(oled.c)
└── IoTI2cWrite() ← 通过 I2C 发送命令/数据
└── HAL 层(iot_i2c.h)
应用层负责线程创建与业务逻辑;oled.c 封装了 SSD1306 初始化序列、显存管理及绘图接口;底层通过 IoT Hardware 的 I2C API 完成硬件通信。
2.4、核心 API 介绍
2.4.1、头文件
#include <stdio.h>
#include "ohos_init.h"
#include "cmsis_os2.h"
#include "iot_gpio.h"
#include "iot_gpio_ex.h"
#include "iot_i2c.h"
#include "oled.h"
2.4.2、I2C 常用 API
| API 名称 | 功能说明 |
|---|---|
IoTI2cInit |
以指定波特率初始化 I2C 控制器 |
IoTI2cDeinit |
解除 I2C 控制器初始化 |
IoTI2cWrite |
向 I2C 从设备写入数据 |
IoTI2cRead |
从 I2C 从设备读取数据 |
IoTI2cSetBaudrate |
动态设置 I2C 波特率 |
2.4.3、IoTI2cInit — 初始化 I2C
unsigned int IoTI2cInit(unsigned int id, unsigned int baudrate);
| 参数 | 说明 |
|---|---|
id |
I2C 控制器编号,本案例为 1(I2C1) |
baudrate |
通信波特率,本案例为 400000(400 kHz) |
| 返回值 | IOT_SUCCESS 表示成功,否则为失败 |
2.4.4、IoTI2cWrite — 写入 I2C 数据
unsigned int IoTI2cWrite(unsigned int id, unsigned short deviceAddr,
const unsigned char *data, unsigned int dataLen);
| 参数 | 说明 |
|---|---|
id |
I2C 控制器编号 |
deviceAddr |
从设备 7-bit 地址,本案例 OLED 为 0x3D |
data |
待发送数据缓冲区指针 |
dataLen |
待发送数据长度(字节数) |
| 返回值 | IOT_SUCCESS 表示成功 |
2.4.5、OLED 驱动库常用 API
| API 名称 | 功能说明 |
|---|---|
OLED_Init |
发送 SSD1306 初始化命令序列,开启显示 |
OLED_Clear |
清空显存并刷新屏幕 |
OLED_Refresh |
将显存数据刷新到 OLED 屏幕 |
OLED_ColorTurn |
反显控制(0:正常,1:反相) |
OLED_DisplayTurn |
屏幕旋转 180° 控制 |
OLED_ShowChar |
显示 ASCII 字符 |
OLED_ShowString |
显示 ASCII 字符串 |
OLED_ShowNum |
显示数字 |
OLED_ShowChinese |
显示预置汉字(按字库索引) |
OLED_DrawPoint |
画点 |
OLED_DrawLine |
画线 |
OLED_DrawCircle |
画圆 |
3、程序设计
3.1、工程目录结构
applications/sample/wifi-iot/app/b4_oled_i2c/
├── oled_i2c_example.c # 应用入口,创建 OLED 显示任务
├── oled.c # OLED 驱动实现(初始化、显存、绘图)
├── oled.h # OLED 驱动头文件
├── oledfont.h # 字库数据(ASCII 及汉字点阵)
├── BUILD.gn # 编译配置
├── README_zh.md # 案例简要说明
└── 实验手册.md # 本实验手册
3.2、关键宏定义
OLED 驱动中的核心配置(oled.c):
#define OLED_I2C_IDX 1 // 使用 I2C1
#define OLED_I2C_ADDR 0x3D // OLED 7-bit I2C 地址
显存大小为 128 列 × 8 页,对应 128×64 像素:
u8 OLED_GRAM[128][8];
字库 Hzk3 中预置了本案例使用的汉字(32 号字体):
| 索引(num) | 汉字 |
|---|---|
| 1 | 星 |
| 2 | 闪 |
3.3、主要代码分析
(1)I2C 底层写字节 — OLED_WR_Byte
OLED_WR_Byte 是驱动层与硬件交互的基础函数,通过 I2C 向 OLED 发送一个字节的命令或数据:
void OLED_WR_Byte(u8 dat, u8 mode)
{
uint8_t buf[2];
buf[0] = (mode == OLED_DATA) ? 0x40 : 0x00; // 0x00: 命令, 0x40: 数据
buf[1] = dat;
uint32_t ret = IoTI2cWrite(OLED_I2C_IDX, OLED_I2C_ADDR, buf, 2);
if (ret != IOT_SUCCESS)
{
printf("OLED I2C Write failed: %08X\n", ret);
}
}
首字节 buf[0] 为 SSD1306 I2C 协议的控制字,次字节 buf[1] 为实际命令或显示数据。
(2)OLED 初始化 — OLED_Init
OLED_Init 发送一系列 SSD1306 配置命令:关闭显示 → 设置列/页地址 → 对比度 → 电荷泵 → 扫描方向等,最后清屏并开启显示:
void OLED_Init(void)
{
osDelay(10); // 上电稳定等待
OLED_WR_Byte(0xAE, OLED_CMD); // Display OFF
// ... 省略若干配置命令 ...
OLED_Clear();
OLED_WR_Byte(0xAF, OLED_CMD); // Display ON
}
(3)显存刷新 — OLED_Refresh
OLED_Refresh 逐页将 OLED_GRAM 中的数据通过 I2C 写入 OLED。每一页先发送页地址命令,再批量发送 128 列像素数据:
void OLED_Refresh(void)
{
for (u8 i = 0; i < 8; i++)
{
OLED_WR_Byte(0xB0 + i, OLED_CMD); // 设置页地址
OLED_WR_Byte(0x02, OLED_CMD); // 列低地址
OLED_WR_Byte(0x10, OLED_CMD); // 列高地址
uint8_t buf[129];
buf[0] = 0x40; // 数据模式
for (u8 n = 0; n < 128; n++)
{
buf[n + 1] = OLED_GRAM[n][i];
}
IoTI2cWrite(OLED_I2C_IDX, OLED_I2C_ADDR, buf, 129);
}
}
(4)汉字显示 — OLED_ShowChinese
OLED_ShowChinese 从字库 Hzk3 中取出点阵数据,逐位解析后调用 OLED_DrawPoint 写入显存:
OLED_ShowChinese(32, 16, 1, 32, 1); // 在 (32,16) 显示 "星",32 号字体
OLED_ShowChinese(64, 16, 2, 32, 1); // 在 (64,16) 显示 "闪",32 号字体
参数说明:
| 参数 | 说明 |
|---|---|
x, y |
显示起始坐标(像素) |
num |
字库中的汉字索引 |
size1 |
字体大小(16/24/32/64) |
mode |
1:正常显示,0:反显 |
(5)OLED 显示任务 — oled_i2c_main
oled_i2c_main 是核心工作线程,完成引脚复用、I2C 初始化、OLED 初始化及汉字显示:
void oled_i2c_main(void *arg)
{
IoTGpioInit(1);
IoSetFunc(IOT_IO_NAME_GPIO_15, IOT_IO_FUNC_GPIO_15_I2C1_SDA);
IoSetFunc(IOT_IO_NAME_GPIO_16, IOT_IO_FUNC_GPIO_16_I2C1_SCL);
IoTI2cInit(1, 400000);
OLED_Init();
OLED_ColorTurn(0);
OLED_DisplayTurn(0);
OLED_Clear();
OLED_ShowChinese(32, 16, 1, 32, 1);
OLED_ShowChinese(64, 16, 2, 32, 1);
OLED_Refresh();
}
(6)应用启动入口 — oled_i2c_example
系统启动后,通过 APP_FEATURE_INIT 宏自动调用此函数,创建 OLED 显示线程:
static void oled_i2c_example(void)
{
osThreadAttr_t attr = {0};
attr.name = "oled_i2c_main";
attr.stack_size = 4096;
attr.priority = osPriorityNormal;
if (osThreadNew(oled_i2c_main, NULL, &attr) == NULL)
{
printf("[OledTest] Failed to create oled_i2c_main thread!\n");
}
}
APP_FEATURE_INIT(oled_i2c_example);
3.4、程序执行流程
4、编译步骤
4.1、确认案例目录
确认案例已位于 OpenHarmony 源码目录下:
applications/sample/wifi-iot/app/b4_oled_i2c/
├── oled_i2c_example.c
├── oled.c
├── oled.h
├── oledfont.h
├── BUILD.gn
└── 实验手册.md
若从外部复制,请将 b4_oled_i2c 目录放到上述 app/ 路径下。
4.2、修改 BUILD.gn(注册编译组件)
编辑 applications/sample/wifi-iot/app/BUILD.gn,在 features 列表中添加本案例:
lite_component("app") {
features = [
"startup",
"b4_oled_i2c:oled_i2c_example", // 添加此行
]
}
4.3、修改 SDK 配置文件
步骤 1:编辑 device/soc/hisilicon/ws63v100/sdk/build/config/target_config/ws63/config.py
找到 'ws63-liteos-app' 配置段,在其 'ram_component' 列表中添加:
"oled_i2c_example"
步骤 2:编辑 device/soc/hisilicon/ws63v100/sdk/libs_url/ws63/cmake/ohos.cmake
找到 "ws63-liteos-app" 对应的 set(COMPONENT_LIST 部分,添加:
"oled_i2c_example"
4.4、执行编译
在 OpenHarmony 源码根目录下执行:
rm -rf out
hb set -root .
# 通过上下方向键选择 ws63 对应的编译分支(如 nearlink_dk_3863 / ws63-liteos-app)
hb build -f
编译成功后,固件输出路径通常在 out/ws63/ 目录下。
4.5、烧录固件
使用开发板配套的烧录工具,将编译生成的固件烧写到 LZ3863-星闪开发板。具体烧录步骤请参考开发板用户手册。
5、运行结果
5.1、串口配置(可选)
如需查看调试日志,可打开串口助手,配置参数如下:
| 参数 | 值 |
|---|---|
| 波特率 | 115200 |
| 数据位 | 8 |
| 停止位 | 1 |
| 校验位 | 无 |
5.2、预期现象
烧录并复位开发板后,可观察到:
- 板载 OLED 屏幕点亮,背景为黑色;
- 屏幕中央区域显示 “星闪” 两个白色汉字(32 号字体);
- 显示内容在上电后保持静态,不会闪烁或消失;
- 若 I2C 通信异常,串口可能输出
OLED I2C Write failed: xxxxxxxx。
5.3、显示效果说明
| 显示项 | 坐标 | 字库索引 | 字体大小 |
|---|---|---|---|
| 星 | (32, 16) | 1 | 32 |
| 闪 | (64, 16) | 2 | 32 |
两个汉字水平排列,每个汉字宽 32 像素、高 32 像素,整体居中偏上显示在 128×64 的屏幕上。
5.4、结果分析
| 现象 | 说明 |
|---|---|
| OLED 正常点亮 | OLED_Init() 初始化成功,电荷泵和显示均已开启 |
| 显示 “星闪” 汉字 | OLED_ShowChinese 从 Hzk3 字库取点阵并写入显存 |
| 内容稳定不闪烁 | 显存刷新一次后任务结束,无循环覆盖操作 |
| 屏幕全黑无显示 | 可能 I2C 通信失败或地址/引脚配置错误 |
若 I2C 写入失败,串口可能输出:
OLED I2C Write failed: xxxxxxxx
若任务创建失败,串口可能输出:
[OledTest] Failed to create oled_i2c_main thread!
5.5、常见问题排查
| 问题 | 可能原因 | 解决方法 |
|---|---|---|
| OLED 完全无显示 | 案例未加入编译或烧录了错误固件 | 核对 4.2、4.3 节三处配置,重新编译烧录 |
| 屏幕全亮或全乱码 | I2C 地址错误或初始化序列不匹配 | 确认 OLED_I2C_ADDR 为 0x3D,检查硬件原理图 |
| 串口报 I2C Write failed | 引脚复用或 I2C 端口配置错误 | 确认 GPIO15/GPIO16 已复用为 I2C1_SDA/SCL |
| 显示内容颠倒或镜像 | 扫描方向配置不符 | 调用 OLED_DisplayTurn(1) 尝试旋转 180° |
| 汉字显示为方块或空白 | 字库索引或字号参数错误 | 核对 Hzk3 字库索引与 size1 参数是否匹配 |
编译报错找不到 oled_i2c_example |
BUILD.gn 或 config.py 未正确修改 | 逐步核对 4.2、4.3 节的配置项 |
6、实验扩展
完成基本实验后,可尝试以下扩展练习:
- 显示英文字符串:使用
OLED_ShowString(0, 0, "Hello", 16, 1)在屏幕顶部显示英文; - 显示动态数字:在循环中调用
OLED_ShowNum()显示递增计数器,配合osDelay()实现秒表效果; - 绘制图形:使用
OLED_DrawLine()、OLED_DrawCircle()绘制几何图案; - 屏幕旋转:调用
OLED_DisplayTurn(1)将显示内容旋转 180°,观察效果变化; - 反显模式:调用
OLED_ColorTurn(1)切换黑底白字 / 白底黑字显示; - 结合按键实验:配合
b3_adc_key案例,按下不同按键在 OLED 上显示不同内容; - 自定义字库:在
oledfont.h中添加新汉字的点阵数据,扩展显示内容。
更多推荐



所有评论(0)