RT thread使用u8g2点亮oled显示屏

本案例使用u8g2的软件包,stm32f407,0.96寸oled显示屏,at24c04。rtthread版本4.1.1

增加软件包u8g2,该软件包可以直接支持SSD1306的0.96寸的oled显示屏

修改配置:打开RT THread settings 配置u8g2软件包

配置项 开关 / 填写值 核心说明
U8G2 总开关 ✅ 开启 启用 u8g2 软件包
使用硬件 SPI ❌ 关闭 我们用 I2C 接口,不需要 SPI
使用硬件 I2C ✅ 必须开启(核心!) 名字有误导性,真实含义是「启用 RT-Thread 标准 I2C 设备接口」,哪怕你用的是软件模拟 I2C,也必须开这个
↳ i2c device name 填入 i2c1 和你board.h里注册的 I2C 设备名完全一致,和 AT24C04 共用同一个 I2C 总线
Use C++ ❌ 关闭 我们用 C 语言开发,不需要 C++ 支持
U8G2 Examples ❌ 里面所有示例全部关闭 避免示例代码和你自己写的main.c/oled.c编译冲突,我们自己写,不用它的示例
Version 保持latest 不用修改,用最新稳定版即可

为什么用软件 I2C,还要开使用硬件 I2c?

这个开关的名字有极强的误导性,本质:

  • 不是控制 "用不用硬件 I2C 外设" ,而是控制 u8g2 要不要对接RT-Thread 标准的 I2C 设备驱动框架

  • 只要你在 RT-Thread 里注册了名为i2c1的 I2C 设备(不管这个设备是硬件外设实现的,还是软件 GPIO 模拟的),就必须开这个开关。

  • 开启后,u8g2 才会生成你oled.c里用到的u8x8_byte_rtthread_hw_i2cu8x8_gpio_and_delay_rtthread适配函数,否则会报「函数未定义」的编译错误。

打开软件模拟iic

\

配置board.h,打开软件iic,填入pb8与9的对应引脚

右键main.c,选择属性,文本编码该成utf-8,用于显示中文

oled.c

cs 复制代码
#include "oled.h"
#include <u8g2.h>
#include <u8g2_port.h>
#include <stdio.h>

// 全局 u8g2 句柄,内部维护,外部无法直接调用
static u8g2_t u8g2;

/**
 * @brief  初始化 OLED 屏幕
 * @note   配置: SSD1306 128x64, 软件I2C, 设备名 i2c1
 */
void oled_init(void)
{
    // 1. 初始化 u8g2 核心
    // 关键:使用 u8g2 软件包提供的 RT-Thread 硬件I2C适配函数
    // 注意:这里的 "hw_i2c" 指的是 RT-Thread 标准 I2C 设备接口,不是 STM32 硬件外设
    u8g2_Setup_ssd1306_i2c_128x64_noname_f(
        &u8g2,
        U8G2_R0,                  // 屏幕旋转方向 (0/90/180/270)
        u8x8_byte_rtthread_hw_i2c, // RT-Thread I2C 字节传输函数
        u8x8_gpio_and_delay_rtthread // RT-Thread 延时函数
    );

    // 2. 初始化显示硬件
    u8g2_InitDisplay(&u8g2);

    // 3. 开启显示 (退出省电模式)
    u8g2_SetPowerSave(&u8g2, 0);

    // 4. 清空显存
    u8g2_ClearBuffer(&u8g2);

    // 5. 默认设置英文字体
    u8g2_SetFont(&u8g2, u8g2_font_ncenB10_tr);
}

/**
 * @brief  清空屏幕显存
 * @note   调用后需要调用 oled_update() 才会生效
 */
void oled_clear(void)
{
    u8g2_ClearBuffer(&u8g2);
}

/**
 * @brief  刷新屏幕,把显存内容一次性显示出来
 */
void oled_update(void)
{
    u8g2_SendBuffer(&u8g2);
}

/**
 * @brief  显示英文字符/字符串
 * @param  x: 像素横坐标 (0~127)
 * @param  y: 像素纵坐标 (0~63, 注意是像素,不是行号)
 * @param  str: 要显示的字符串
 */
void oled_show_str(uint8_t x, uint8_t y, const char *str)
{
    // 切换到英文字体
    u8g2_SetFont(&u8g2, u8g2_font_ncenB10_tr);
    // 绘制字符串
    u8g2_DrawStr(&u8g2, x, y, str);
}

/**
 * @brief  显示中文字符串 (UTF-8编码)
 * @param  x: 像素横坐标
 * @param  y: 像素纵坐标
 * @param  str: 要显示的中文字符串 (UTF-8)
 * @note   调用此函数的 .c 文件必须是 UTF-8 编码!
 */
void oled_show_chinese(uint8_t x, uint8_t y, const char *str)
{
    // 切换到完整版中文字体 (包含所有汉字,如:哈哈)
    u8g2_SetFont(&u8g2, u8g2_font_wqy12_t_chinese3);
    // 关键:必须用 DrawUTF8 显示中文
    u8g2_DrawUTF8(&u8g2, x, y, str);
}

/**
 * @brief  显示整数数字
 * @param  x: 像素横坐标
 * @param  y: 像素纵坐标
 * @param  num: 要显示的整数 (支持负数)
 */
void oled_show_num(uint8_t x, uint8_t y, int32_t num)
{
    char buf[32] = {0};
    // 格式化数字为字符串
    rt_snprintf(buf, sizeof(buf), "%d", num);
    // 调用英文字符显示函数
    oled_show_str(x, y, buf);
}

/**
 * @brief  画一个点
 * @param  x: 横坐标  y: 纵坐标
 */
void oled_draw_pixel(uint8_t x, uint8_t y)
{
    u8g2_DrawPixel(&u8g2, x, y);
}

/**
 * @brief  画一条直线
 * @param  x1,y1: 起点坐标
 * @param  x2,y2: 终点坐标
 */
void oled_draw_line(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2)
{
    u8g2_DrawLine(&u8g2, x1, y1, x2, y2);
}

/**
 * @brief  画一个空心矩形框
 * @param  x: 左上角横坐标
 * @param  y: 左上角纵坐标
 * @param  w: 宽度
 * @param  h: 高度
 */
void oled_draw_rect(uint8_t x, uint8_t y, uint8_t w, uint8_t h)
{
    u8g2_DrawFrame(&u8g2, x, y, w, h);
}

/**
 * @brief  画一个实心填充矩形
 * @param  x: 左上角横坐标
 * @param  y: 左上角纵坐标
 * @param  w: 宽度
 * @param  h: 高度
 */
void oled_fill_rect(uint8_t x, uint8_t y, uint8_t w, uint8_t h)
{
    u8g2_DrawBox(&u8g2, x, y, w, h);
}

/**
 * @brief  画空心圆
 * @param  x,y: 圆心坐标
 * @param  r: 圆的半径
 */
void oled_draw_circle(uint8_t x, uint8_t y, uint8_t r)
{
    u8g2_DrawCircle(&u8g2, x, y, r, U8G2_DRAW_ALL); // U8G2_DRAW_ALL 表示画整个圆
}

/**
 * @brief  画空心三角形
 * @param  x1,y1/x2,y2/x3,y3: 三角形三个顶点的坐标
 */
void oled_draw_triangle(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint8_t x3, uint8_t y3)
{
    u8g2_DrawTriangle(&u8g2, x1, y1, x2, y2, x3, y3);
}

代码详细讲解:

cs 复制代码
u8g2_Setup_ssd1306_i2c_128x64_noname_f(
    &u8g2,                  // 把我们的"遥控器"传进去
    U8G2_R0,                // 屏幕不旋转
    u8x8_byte_rtthread_hw_i2c, // 【关键】告诉u8g2:"你通过RT-Thread的I2C设备接口去发数据"
    u8x8_gpio_and_delay_rtthread // 告诉u8g2:"你用RT-Thread的延时函数"
);

oled.h

cs 复制代码
#ifndef __OLED_H__
#define __OLED_H__

#include <rtthread.h>
#include <stdint.h>

// 滚动方向枚举定义
typedef enum {
    OLED_SCROLL_LEFT  = 0,   // 屏幕内容向左滚动
    OLED_SCROLL_RIGHT = 1    // 屏幕内容向右滚动
} oled_scroll_dir_t;

/**
 * @brief  OLED初始化函数(必须第一个调用)
 */
void oled_init(void);

/**
 * @brief  清空OLED显存(仅清空内存,不刷新屏幕)
 */
void oled_clear(void);

/**
 * @brief  刷新显存到OLED屏幕(所有绘制后必须调用)
 */
void oled_update(void);

/**
 * @brief  显示英文字符串
 * @param  x: 横坐标(0~127)  y: 纵坐标(0~63)  str: 英文文本
 */
void oled_show_str(uint8_t x, uint8_t y, const char *str);

/**
 * @brief  显示UTF-8编码中文字符串
 * @param  x: 横坐标  y: 纵坐标  str: 中文文本
 */
void oled_show_chinese(uint8_t x, uint8_t y, const char *str);

/**
 * @brief  显示整数数字
 * @param  x: 横坐标  y: 纵坐标  num: 整数值
 */
void oled_show_num(uint8_t x, uint8_t y, int32_t num);

/**
 * @brief  在指定坐标画一个点
 * @param  x: 横坐标  y: 纵坐标
 */
void oled_draw_pixel(uint8_t x, uint8_t y);

/**
 * @brief  画一条直线
 * @param  x1,y1: 起点坐标  x2,y2: 终点坐标
 */
void oled_draw_line(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2);

/**
 * @brief  画空心矩形
 * @param  x,y: 左上角坐标  w:宽度  h:高度
 */
void oled_draw_rect(uint8_t x, uint8_t y, uint8_t w, uint8_t h);

/**
 * @brief  画实心矩形
 * @param  x,y: 左上角坐标  w:宽度  h:高度
 */
void oled_fill_rect(uint8_t x, uint8_t y, uint8_t w, uint8_t h);

/**
 * @brief  画空心圆
 * @param  x,y: 圆心坐标  r: 半径
 */
void oled_draw_circle(uint8_t x, uint8_t y, uint8_t r);

/**
 * @brief  画空心三角形
 * @param  x1,y1/x2,y2/x3,y3: 三个顶点坐标
 */
void oled_draw_triangle(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint8_t x3, uint8_t y3);


#endif
cs 复制代码
u8g2_InitDisplay(&u8g2);

作用:u8g2 通过 I2C 给 SSD1306 发送一串初始化命令(比如设置分辨率、开启电荷泵等),让屏幕从 "休眠" 状态醒过来。

cs 复制代码
u8g2_SetPowerSave(&u8g2, 0);

作用:退出省电模式,让屏幕亮起来。

cs 复制代码
u8g2_ClearBuffer(&u8g2);

核心概念:显存(Buffer)

  • u8g2 不会画一个点就立刻刷新屏幕,那样太慢了。

  • 它先在内存里开辟一块 "草稿纸"(显存),你所有的绘制操作(写字、画线)都是先画在这张草稿纸上。

  • 等你全部画完了,再一次性把草稿纸的内容发到屏幕上。

cs 复制代码
u8g2_SetFont(&u8g2, u8g2_font_ncenB10_tr);

作用:选一支笔,默认选一支写英文字母的笔。

cs 复制代码
void oled_clear(void)
{
    u8g2_ClearBuffer(&u8g2); // 只是擦干净了内存里的草稿纸
}

注意 :调用这个函数后,屏幕不会立刻变干净,因为你只擦了草稿纸,还没贴到屏幕上。

cs 复制代码
void oled_update(void)
{
    u8g2_SendBuffer(&u8g2); // 【关键】把草稿纸的内容一次性通过I2C发送到屏幕
}

作用:把内存里的 "草稿纸"(显存)通过 I2C 全部发送给 SSD1306,屏幕才会真正显示内容。

新手学习u8g2,只需要知道4个步骤

cs 复制代码
1. oled_init()       → 初始化屏幕,拿到"遥控器"
2. oled_clear()      → 擦干净"草稿纸"
3. oled_show_xxx()   → 在"草稿纸"上画内容(所有 Draw 函数都是画在内存里)
4. oled_update()     → 【关键】把"草稿纸"一次性贴到屏幕上

main.c

cs 复制代码
#include <rtthread.h>
#include "oled.h"

int main(void)
{
    rt_kprintf("=== OLED 测试程序 ===\n");

    // 1. 初始化 OLED
    oled_init();

    // 2. 清屏
    oled_clear();

    // 3. 测试显示英文
    oled_show_str(0, 15, "Hello RT-Thread!");

    // 4. 测试显示中文
    // 注意:这个 main.c 必须是 UTF-8 编码!
    oled_show_chinese(0, 35, "单片机");

    // 5. 测试显示数字
    oled_show_str(0, 55, "Year: ");
    oled_show_num(40, 55, 2026);

    // 6. 画个框装饰
    oled_draw_rect(0, 0, 128, 64);

    // 7. 【关键】刷新屏幕,所有内容才会显示出来
    oled_update();

    rt_kprintf("1测试显示完成!\n");
    rt_thread_mdelay(2000);
    oled_clear();
    oled_draw_circle(70, 32, 20); // 在屏幕中心画一个半径20的圆
    oled_draw_triangle(10, 50, 30, 10, 50, 50); // 画一个三角形
    oled_update();
    while (1)
    {
        rt_thread_mdelay(1000);
    }

    return RT_EOK;
}

相关推荐
航Hang*2 小时前
第2章:进阶Linux系统——第8节:配置与管理MariaDB服务器
linux·运维·服务器·数据库·笔记·学习·mariadb
wqww_12 小时前
Linux查看磁盘IO问题
linux·运维·服务器
senijusene2 小时前
IMX6ULL ADC 驱动开发解析:
驱动开发·嵌入式硬件
2023自学中2 小时前
正点原子 Linux 驱动开发:多点电容触摸屏实验,gt9147 触摸芯片
linux·驱动开发·嵌入式
航Hang*2 小时前
第2章:进阶Linux系统——第10节:Linux 系统编程与 Shell 脚本全解笔记(GCC+Make+Vim+Shell Script)
linux·运维·服务器·学习·vim·apache·vmware
UTP协同自动化测试2 小时前
智能家居中控屏测试:触摸屏操作 + I2C 读取传感器 + UART 与子设备通信 + GPIO 控制
功能测试·单片机·嵌入式硬件·测试工具·智能家居
【云轩】2 小时前
【拆解系列 一 】拆解手持式泡泡机
嵌入式硬件
孙同学_2 小时前
【Linux篇】应用层协议HTTP
linux·运维·http
DeadPool loves Star2 小时前
新版VSCode登录Old Linux
linux·ide·vscode