1、实验简介

参考网址:https://gitee.com/Lockzhiner-Electronics/lz3863/tree/master/apps/b4_oled_i2c

1.1、实验目的

本实验旨在帮助学习者掌握 OpenHarmony 轻量系统中 I2C(Inter-Integrated Circuit,集成电路总线) 外设的基本使用方法。通过本实验,你将学会:

  1. 理解 I2C 总线的工作原理及主从通信机制;
  2. 使用 IoT Hardware 接口初始化 I2C 总线并配置引脚复用;
  3. 理解 OLED 显示屏 的显存结构与刷新机制;
  4. 掌握 OLED 驱动库的基本用法,实现中文字符显示;
  5. 完成案例代码的编译、烧录与屏幕现象观察。

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、程序执行流程

OLED 屏幕 I2C1 总线 oled_i2c_main oled_i2c_example 系统启动 OLED 屏幕 I2C1 总线 oled_i2c_main oled_i2c_example 系统启动 APP_FEATURE_INIT 触发 osThreadNew(oled_i2c_main) IoTGpioInit + IoSetFunc (GPIO15/16) IoTI2cInit(1, 400000) OLED_Init() OLED_Clear() OLED_ShowChinese("星") OLED_ShowChinese("闪") OLED_Refresh() 屏幕显示 "星闪"

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_ShowChineseHzk3 字库取点阵并写入显存
内容稳定不闪烁 显存刷新一次后任务结束,无循环覆盖操作
屏幕全黑无显示 可能 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_ADDR0x3D,检查硬件原理图
串口报 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、实验扩展

完成基本实验后,可尝试以下扩展练习:

  1. 显示英文字符串:使用 OLED_ShowString(0, 0, "Hello", 16, 1) 在屏幕顶部显示英文;
  2. 显示动态数字:在循环中调用 OLED_ShowNum() 显示递增计数器,配合 osDelay() 实现秒表效果;
  3. 绘制图形:使用 OLED_DrawLine()OLED_DrawCircle() 绘制几何图案;
  4. 屏幕旋转:调用 OLED_DisplayTurn(1) 将显示内容旋转 180°,观察效果变化;
  5. 反显模式:调用 OLED_ColorTurn(1) 切换黑底白字 / 白底黑字显示;
  6. 结合按键实验:配合 b3_adc_key 案例,按下不同按键在 OLED 上显示不同内容;
  7. 自定义字库:在 oledfont.h 中添加新汉字的点阵数据,扩展显示内容。
Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐