基于STM32F103硬件IIC驱动SH1106的U8G2移植

参考文章:

U8g2图形库与STM32移植(I2C,软件与硬件) - 冰封残烛 - 博客园

基于STM32移植U8g2图形库——OLED显示(HAL库)_stm32 u8g2-CSDN博客

Wiki - Gitee.com

stm32f103移植u8g2(硬件I2C) - njit-sam - 博客园

STM32移植U8g2图形库——玩转OLED显示 - 知乎

stm32f103移植u8g2(硬件I2C) - njit-sam - 博客园

STM32 U8g2 spi软件驱动,spi硬件驱动(优化u8g2的软件spi,速度翻好几倍)-CSDN博客

U8g2 OLED显示库使用详解-CSDN博客

正文

这次要使用STM32的硬件IIC驱动一个在网上买到的1.3寸oled液晶屏,驱动芯片为SH1106。

U8g2下载地址: https://github.com/olikraus/u8g2

STM32工程配置

栈保留大一些,因为我们会使用u8g2的全缓存接口进行测试,所以任务栈保留大一些。

U8G2移植

打开下载的u8g2源码,找到csrc文件夹,这里面存放的都是我们所需要的源码。

由于我们使用的是CUBEIDE进行编译,在编译时会自动移除未使用的代码,所以不需要精简源码,只需要直接将csrc文件夹改名为u8g2文件夹,放到工程下即可。

如果使用的是keil或其他编译器需要进行代码精简,当然使用cubeide也希望进行精简。

打开csrc文件夹,有许多U8x8_d_*****类型的c文件,这是针对不同的驱动芯片所写的驱动程序,我们只需要选择我们需要的就行了,其他的在可以删除。我们此次使用的是ssd1306,因为sh1106的驱动代码在这个文件中,这两个芯片的寄存器基本相同,所以作者把这两个芯片的驱动放到了一个文件中。其它的屏幕驱动和分辨率的文件可以删掉。

我使用的是硬件IIC接口,在u8g2_d_setup.c文件中只留一个本次要用到u8g2_Setup_sh1106_i2c_128x64_noname_f就好,其它的可以删掉或注释掉。

在 U8g2 库 中,u8g2_Setup_sh1106_128x64_noname_1、u8g2_Setup_sh1106_128x64_noname_2 和 u8g2_Setup_sh1106_128x64_noname_f 是用于初始化 SH1106 驱动的 128×64 OLED 屏幕 的不同配置函数。它们的区别主要体现在 内存缓存大小和刷新方式 上。以下是详细对比:

函数名称

缓存大小(128×64 屏幕)

内存占用(字节)

说明

u8g2_Setup_sh1106_128x64_noname_1

1/8 屏幕像素

128 字节

内存占用最小,但可能因缓存不足导致显示效果不佳(如字体模糊或图形撕裂)。

u8g2_Setup_sh1106_128x64_noname_2

1/4 屏幕像素

256 字节

内存占用适中,显示效果比

1

版本更好,但仍有轻微限制。

u8g2_Setup_sh1106_128x64_noname_f

全屏像素(1:1)

1024 字节

内存占用最大,但显示效果最佳(无撕裂、无模糊),适合高精度需求场景。

在u8g2_d_memory.c

由于用到的u8g2_Setup_sh1106_i2c_128x64_noname_f函数中,只调用了u8g2_m_16_8_f这个函数,所以留下这个函数,其它的函数一定要删掉或注释掉。

将csrc文件夹改名为u8g2文件夹,放到工程目录下。并在工程中添加头文件引用和源码目录。

编写引用文件

oled.h文件


#ifndef __OLED_H
#define __OLED_H
 
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "u8g2.h"
/* USER CODE BEGIN Includes */
 
/* USER CODE END Includes */
 
 extern u8g2_t u8g2;
 
/* USER CODE BEGIN Private defines */
 
/* USER CODE END Private defines */
#define OLED_ADDRESS  0x78 // oled
 
/* USER CODE BEGIN Prototypes */
void oledInit(void);
uint8_t u8x8_byte_hw_i2c(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr);
uint8_t u8x8_gpio_and_delay(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr);
void u8g2DrawTest(void);
 
#endif //__OLED_H

oled.c文件

#include "oled.h"

#include <stdio.h>

#include "cmsis_os.h"
#include "i2c.h"

u8g2_t u8g2;

uint8_t u8x8_byte_hw_i2c(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr)
{
    /* u8g2/u8x8 will never send more than 32 bytes between START_TRANSFER and END_TRANSFER */
    static uint8_t buffer[128];
    static uint8_t buf_idx;
    uint8_t *data;

    switch (msg) {
    case U8X8_MSG_BYTE_INIT: {
        /* add your custom code to init i2c subsystem */
        MX_I2C1_Init();  // I2C初始化
    } break;

    case U8X8_MSG_BYTE_START_TRANSFER: {
        buf_idx = 0;
    } break;

    case U8X8_MSG_BYTE_SEND: {
        data = (uint8_t *)arg_ptr;

        while (arg_int > 0) {
            buffer[buf_idx++] = *data;
            data++;
            arg_int--;
        }
    } break;

    case U8X8_MSG_BYTE_END_TRANSFER: {
        if (HAL_I2C_Master_Transmit(&hi2c1, OLED_ADDRESS, buffer, buf_idx, 1000) != HAL_OK) return 0;
    } break;

    case U8X8_MSG_BYTE_SET_DC:
        break;

    default:
        return 0;
    }

    return 1;
}

uint8_t u8x8_gpio_and_delay(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr)
{
    switch (msg) {
    case U8X8_MSG_DELAY_100NANO:  // delay arg_int * 100 nano seconds
        __NOP();
        break;
    case U8X8_MSG_DELAY_10MICRO:  // delay arg_int * 10 micro seconds
        for (uint16_t n = 0; n < 320; n++) {
            __NOP();
        }
        break;
    case U8X8_MSG_DELAY_MILLI:  // delay arg_int * 1 milli second
        osDelay(1);
        break;
    case U8X8_MSG_DELAY_I2C:  // arg_int is the I2C speed in 100KHz, e.g. 4 = 400 KHz
        for (uint16_t n = 0; n < 160; n++) {
            __NOP();
        }
        break;                     // arg_int=1: delay by 5us, arg_int = 4: delay by 1.25us
    case U8X8_MSG_GPIO_I2C_CLOCK:  // arg_int=0: Output low at I2C clock pin
        break;                     // arg_int=1: Input dir with pullup high for I2C clock pin
    case U8X8_MSG_GPIO_I2C_DATA:   // arg_int=0: Output low at I2C data pin
        break;                     // arg_int=1: Input dir with pullup high for I2C data pin
    case U8X8_MSG_GPIO_MENU_SELECT:
        u8x8_SetGPIOResult(u8x8, /* get menu select pin state */ 0);
        break;
    case U8X8_MSG_GPIO_MENU_NEXT:
        u8x8_SetGPIOResult(u8x8, /* get menu next pin state */ 0);
        break;
    case U8X8_MSG_GPIO_MENU_PREV:
        u8x8_SetGPIOResult(u8x8, /* get menu prev pin state */ 0);
        break;
    case U8X8_MSG_GPIO_MENU_HOME:
        u8x8_SetGPIOResult(u8x8, /* get menu home pin state */ 0);
        break;
    default:
        u8x8_SetGPIOResult(u8x8, 1);  // default return value
        break;
    }
    return 1;
}

// U8g2的初始化,需要调用下面这个u8g2_Setup_ssd1306_128x64_noname_f函数,该函数的4个参数含义:
// u8g2:传入的U8g2结构体
// U8G2_R0:默认使用U8G2_R0即可(用于配置屏幕是否要旋转)
// u8x8_byte_sw_i2c:使用软件IIC驱动,该函数由U8g2源码提供
// u8x8_gpio_and_delay:就是上面我们写的配置函数

void u8g2Init(u8g2_t *u8g2)
{
    u8g2_Setup_sh1106_i2c_128x64_noname_f(u8g2, U8G2_R0, u8x8_byte_hw_i2c, u8x8_gpio_and_delay);  // 初始化u8g2 结构体
    u8g2_InitDisplay(u8g2);                                                                   //
    u8g2_SetPowerSave(u8g2, 0);                                                               //
    u8g2_ClearBuffer(u8g2);
}

void oledInit(void) { u8g2Init(&u8g2); }

void draw(u8g2_t *u8g2)
{
    u8g2_ClearBuffer(u8g2);

    u8g2_SetFontMode(u8g2, 1);              /*字体模式选择*/
    u8g2_SetFontDirection(u8g2, 0);         /*字体方向选择*/
    u8g2_SetFont(u8g2, u8g2_font_inb24_mf); /*字库选择*/
    u8g2_DrawStr(u8g2, 0, 20, "U");

    u8g2_SetFontDirection(u8g2, 1);
    u8g2_SetFont(u8g2, u8g2_font_inb30_mn);
    u8g2_DrawStr(u8g2, 21, 8, "8");

    u8g2_SetFontDirection(u8g2, 0);
    u8g2_SetFont(u8g2, u8g2_font_inb24_mf);
    u8g2_DrawStr(u8g2, 51, 30, "g");
    u8g2_DrawStr(u8g2, 67, 30, "\xb2");

    u8g2_DrawHLine(u8g2, 2, 35, 47);
    u8g2_DrawHLine(u8g2, 3, 36, 47);
    u8g2_DrawVLine(u8g2, 45, 32, 12);
    u8g2_DrawVLine(u8g2, 46, 33, 12);

    u8g2_SetFont(u8g2, u8g2_font_4x6_tr);
    u8g2_DrawStr(u8g2, 1, 54, "github.com/olikraus/u8g2");

    u8g2_SendBuffer(u8g2);
    osDelay(1000);
}

//---------------U8g2测试函数

#define SEND_BUFFER_DISPLAY_MS(u8g2, ms) \
    do {                                 \
        u8g2_SendBuffer(u8g2);           \
        osDelay(ms);                     \
    } while (0);

// 进度条显示
void testDrawProcess(u8g2_t *u8g2)
{
    for (int i = 10; i <= 80; i = i + 2) {
        u8g2_ClearBuffer(u8g2);

        char buff[20];
        sprintf(buff, "%d%%", (int)(i / 80.0 * 100));

        u8g2_SetFont(u8g2, u8g2_font_ncenB12_tf);
        u8g2_DrawStr(u8g2, 16, 32, "STM32 U8g2");  // 字符显示

        u8g2_SetFont(u8g2, u8g2_font_ncenB08_tf);
        u8g2_DrawStr(u8g2, 100, 49, buff);  // 当前进度显示

        u8g2_DrawRBox(u8g2, 16, 40, i, 10, 4);     // 圆角填充框矩形框
        u8g2_DrawRFrame(u8g2, 16, 40, 80, 10, 4);  // 圆角矩形

        u8g2_SendBuffer(u8g2);
    }
    osDelay(500);
}

// 字体测试 数字英文可选用 u8g2_font_ncenB..(粗) 系列字体
// u8g2_font_unifont_t_symbols/u8g2_font_unifont_h_symbols(细 圆润)
void testShowFont(u8g2_t *u8g2)
{
    int t = 1000;
    char testStr[14] = "STM32F103C8T6";

    u8g2_ClearBuffer(u8g2);

    u8g2_SetFont(u8g2, u8g2_font_u8glib_4_tf);
    u8g2_DrawStr(u8g2, 0, 5, testStr);
    SEND_BUFFER_DISPLAY_MS(u8g2, t);

    u8g2_SetFont(u8g2, u8g2_font_ncenB08_tf);
    u8g2_DrawStr(u8g2, 0, 30, testStr);
    SEND_BUFFER_DISPLAY_MS(u8g2, t);

    u8g2_SetFont(u8g2, u8g2_font_ncenB10_tr);
    u8g2_DrawStr(u8g2, 0, 60, testStr);
    SEND_BUFFER_DISPLAY_MS(u8g2, t);
}

// 画空心矩形
void testDrawFrame(u8g2_t *u8g2)
{
    int t = 1000;
    int x = 16;
    int y = 32;
    int w = 50;
    int h = 20;
    u8g2_ClearBuffer(u8g2);
    u8g2_DrawStr(u8g2, 0, 15, "DrawFrame");

    u8g2_DrawFrame(u8g2, x, y, w, h);
    SEND_BUFFER_DISPLAY_MS(u8g2, t);
    u8g2_DrawFrame(u8g2, x + w + 5, y - 10, w - 20, h + 20);
    SEND_BUFFER_DISPLAY_MS(u8g2, t);
}

// 画实心圆角矩形
void testDrawRBox(u8g2_t *u8g2)
{
    int t = 1000;
    int x = 16;
    int y = 32;
    int w = 50;
    int h = 20;
    int r = 3;
    u8g2_ClearBuffer(u8g2);
    u8g2_DrawStr(u8g2, 0, 15, "DrawRBox");

    u8g2_DrawRBox(u8g2, x, y, w, h, r);
    SEND_BUFFER_DISPLAY_MS(u8g2, t);
    u8g2_DrawRBox(u8g2, x + w + 5, y - 10, w - 20, h + 20, r);
    SEND_BUFFER_DISPLAY_MS(u8g2, t);
}

// 画空心圆
void testDrawCircle(u8g2_t *u8g2)
{
    int t = 600;
    int stx = 0;    // 画图起始x
    int sty = 16;   // 画图起始y
    int with = 16;  // 一个图块的间隔
    int r = 15;     // 圆的半径
    u8g2_ClearBuffer(u8g2);
    u8g2_DrawStr(u8g2, 0, 15, "DrawCircle");

    u8g2_DrawCircle(u8g2, stx, sty - 1 + with, r, U8G2_DRAW_UPPER_RIGHT);  // 右上
    SEND_BUFFER_DISPLAY_MS(u8g2, t);
    u8g2_DrawCircle(u8g2, stx + with, sty, r, U8G2_DRAW_LOWER_RIGHT);  // 右下
    SEND_BUFFER_DISPLAY_MS(u8g2, t);
    u8g2_DrawCircle(u8g2, stx - 1 + with * 3, sty - 1 + with, r, U8G2_DRAW_UPPER_LEFT);  // 左上
    SEND_BUFFER_DISPLAY_MS(u8g2, t);
    u8g2_DrawCircle(u8g2, stx - 1 + with * 4, sty, r, U8G2_DRAW_LOWER_LEFT);  // 左下
    SEND_BUFFER_DISPLAY_MS(u8g2, t);
    u8g2_DrawCircle(u8g2, stx - 1 + with * 2, sty - 1 + with * 2, r, U8G2_DRAW_ALL);  // 整个圆
    SEND_BUFFER_DISPLAY_MS(u8g2, t);

    u8g2_DrawCircle(u8g2, 32 * 3, 32, 31, U8G2_DRAW_ALL);  // 右侧整个圆
    SEND_BUFFER_DISPLAY_MS(u8g2, t);
}

// 画实心椭圆
void testDrawFilledEllipse(u8g2_t *u8g2)
{
    int t = 800;
    int with = 16;  // 一个图块的间隔
    int rx = 27;    // 椭圆x方向的半径
    int ry = 22;    // 椭圆y方向的半径
    u8g2_ClearBuffer(u8g2);
    u8g2_DrawStr(u8g2, 0, 14, "DrawFilledEllipse");

    SEND_BUFFER_DISPLAY_MS(u8g2, t);
    u8g2_DrawFilledEllipse(u8g2, 0, with, rx, ry, U8G2_DRAW_LOWER_RIGHT);  // 右下
    SEND_BUFFER_DISPLAY_MS(u8g2, t);
    u8g2_DrawFilledEllipse(u8g2, with * 4 - 1, with, rx, ry, U8G2_DRAW_LOWER_LEFT);  // 左下
    SEND_BUFFER_DISPLAY_MS(u8g2, t);
    u8g2_DrawFilledEllipse(u8g2, 0, with * 4 - 1, rx, ry, U8G2_DRAW_UPPER_RIGHT);  // 右上
    SEND_BUFFER_DISPLAY_MS(u8g2, t);
    u8g2_DrawFilledEllipse(u8g2, with * 4 - 1, with * 4 - 1, rx, ry, U8G2_DRAW_UPPER_LEFT);  // 左上
    SEND_BUFFER_DISPLAY_MS(u8g2, t);
    u8g2_DrawFilledEllipse(u8g2, with * 6, with * 2.5, rx, ry, U8G2_DRAW_ALL);  // 整个椭圆
    SEND_BUFFER_DISPLAY_MS(u8g2, t);
}

// 环形测试
void testDrawMulti(u8g2_t *u8g2)
{
    u8g2_ClearBuffer(u8g2);
    for (int j = 0; j < 64; j += 16) {
        for (int i = 0; i < 128; i += 16) {
            u8g2_DrawPixel(u8g2, i, j);
            u8g2_SendBuffer(u8g2);
        }
    }

    // 实心矩形逐渐变大
    u8g2_ClearBuffer(u8g2);
    for (int i = 30; i > 0; i -= 2) {
        u8g2_DrawBox(u8g2, i * 2, i, 128 - i * 4, 64 - 2 * i);
        u8g2_SendBuffer(u8g2);
    }
    // 空心矩形逐渐变小
    u8g2_ClearBuffer(u8g2);
    for (int i = 0; i < 32; i += 2) {
        u8g2_DrawFrame(u8g2, i * 2, i, 128 - i * 4, 64 - 2 * i);
        u8g2_SendBuffer(u8g2);
    }

    // 实心圆角矩形逐渐变大
    u8g2_ClearBuffer(u8g2);
    for (int i = 30; i > 0; i -= 2) {
        u8g2_DrawRBox(u8g2, i * 2, i, 128 - i * 4, 64 - 2 * i, 10 - i / 3);
        u8g2_SendBuffer(u8g2);
    }
    // 空心圆角矩形逐渐变小
    u8g2_ClearBuffer(u8g2);
    for (int i = 0; i < 32; i += 2) {
        u8g2_DrawRFrame(u8g2, i * 2, i, 128 - i * 4, 64 - 2 * i, 10 - i / 3);
        u8g2_SendBuffer(u8g2);
    }

    // 实心圆逐渐变大
    u8g2_ClearBuffer(u8g2);
    for (int i = 2; i < 64; i += 3) {
        u8g2_DrawDisc(u8g2, 64, 32, i, U8G2_DRAW_ALL);
        u8g2_SendBuffer(u8g2);
    }
    // 空心圆逐渐变小
    u8g2_ClearBuffer(u8g2);
    for (int i = 64; i > 0; i -= 3) {
        u8g2_DrawCircle(u8g2, 64, 32, i, U8G2_DRAW_ALL);
        u8g2_SendBuffer(u8g2);
    }

    // 实心椭圆逐渐变大
    u8g2_ClearBuffer(u8g2);
    for (int i = 2; i < 32; i += 3) {
        u8g2_DrawFilledEllipse(u8g2, 64, 32, i * 2, i, U8G2_DRAW_ALL);
        u8g2_SendBuffer(u8g2);
    }
    // 空心椭圆逐渐变小
    u8g2_ClearBuffer(u8g2);
    for (int i = 32; i > 0; i -= 3) {
        u8g2_DrawEllipse(u8g2, 64, 32, i * 2, i, U8G2_DRAW_ALL);
        u8g2_SendBuffer(u8g2);
    }
}

// width: 128, height: 48
const unsigned char bilibili[] U8X8_PROGMEM = {
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x07, 0x80, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x0f, 0xc0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0xe0, 0x03,
    0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0xf0, 0x00, 0x00, 0xf8, 0x00,
    0x00, 0x00, 0x80, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xff, 0xff, 0xff, 0x01, 0xfc, 0x00, 0x00, 0x00, 0xc0,
    0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x03, 0xfc, 0x00, 0x00, 0x3c, 0xc0, 0x0f, 0x00, 0x80,
    0x03, 0x00, 0x80, 0xff, 0xff, 0xff, 0xff, 0x07, 0xfc, 0x00, 0x00, 0x3c, 0xc0, 0x0f, 0x00, 0xc0, 0x07, 0x00, 0x80,
    0xff, 0xff, 0xff, 0xff, 0x0f, 0xfc, 0x00, 0x00, 0x3c, 0x80, 0x0f, 0x00, 0xc0, 0x07, 0x00, 0xc0, 0x0f, 0x00, 0x00,
    0x80, 0x0f, 0xf8, 0x00, 0x00, 0x3c, 0x80, 0x0f, 0x00, 0x80, 0x07, 0x00, 0xc0, 0x07, 0x00, 0x00, 0x00, 0x1f, 0xf8,
    0x00, 0x00, 0x78, 0x80, 0x0f, 0x00, 0x80, 0x07, 0x00, 0xc0, 0x07, 0x00, 0x00, 0x00, 0x1f, 0xf8, 0x00, 0x00, 0x78,
    0x80, 0x0f, 0x00, 0x80, 0x07, 0x00, 0xc0, 0x07, 0x00, 0x00, 0x00, 0x1f, 0xf8, 0x00, 0x80, 0x79, 0x80, 0x0f, 0x00,
    0x98, 0x07, 0x00, 0xc0, 0x07, 0x00, 0x00, 0x00, 0x1f, 0xf8, 0x00, 0xe0, 0x79, 0x9f, 0x0f, 0x00, 0xbe, 0xe7, 0x01,
    0xc0, 0x07, 0x10, 0x40, 0x00, 0x1f, 0xf8, 0x00, 0xe0, 0x7b, 0x1f, 0x0f, 0x00, 0xbe, 0xe7, 0x01, 0xc0, 0x87, 0x1f,
    0xe0, 0x0f, 0x1f, 0xf8, 0x00, 0xe0, 0x7b, 0x1e, 0x0f, 0x00, 0x3e, 0xe7, 0x01, 0xc0, 0xe7, 0x3f, 0xe0, 0x3f, 0x1f,
    0xf0, 0x00, 0xe0, 0x7b, 0x1e, 0x0f, 0x00, 0x3e, 0xe7, 0x01, 0xc0, 0xe7, 0x3f, 0xe0, 0x3f, 0x1f, 0xf0, 0x00, 0x60,
    0x71, 0x1e, 0x0f, 0x00, 0x34, 0xe7, 0x01, 0xc0, 0xe7, 0x07, 0x00, 0x3f, 0x1f, 0xf0, 0x00, 0x00, 0x70, 0x00, 0x1f,
    0x00, 0x00, 0x07, 0x00, 0xc0, 0x07, 0x00, 0x00, 0x00, 0x1f, 0xf0, 0x00, 0xc0, 0x73, 0x1e, 0x1f, 0x00, 0x3c, 0xc7,
    0x01, 0xc0, 0x07, 0x00, 0x00, 0x00, 0x1f, 0xf0, 0x00, 0xc0, 0x73, 0x1e, 0x1f, 0x00, 0x7c, 0xe7, 0x01, 0xc0, 0x07,
    0x00, 0x00, 0x00, 0x1f, 0xf0, 0x00, 0xc0, 0x73, 0x1e, 0x1f, 0x00, 0x7c, 0xef, 0x01, 0xc0, 0x07, 0x00, 0x00, 0x00,
    0x1f, 0xf0, 0x01, 0xc0, 0x77, 0x1e, 0x1e, 0x00, 0x7c, 0xef, 0x01, 0xc0, 0x07, 0x00, 0x03, 0x00, 0x1f, 0xf0, 0xff,
    0xc1, 0xf7, 0x1e, 0xfe, 0x1f, 0x78, 0xef, 0x01, 0xc0, 0x07, 0x70, 0x37, 0x00, 0x1f, 0xe0, 0xff, 0x87, 0xf7, 0x1e,
    0xfe, 0xff, 0x78, 0xee, 0x01, 0xc0, 0x07, 0xe0, 0x3f, 0x00, 0x1f, 0xe0, 0xff, 0x9f, 0xf7, 0x1e, 0xfe, 0xff, 0x79,
    0xce, 0x01, 0xc0, 0x07, 0xc0, 0x18, 0x00, 0x1f, 0xe0, 0xff, 0xbf, 0xe7, 0x1e, 0xfe, 0xff, 0x7b, 0xce, 0x01, 0xc0,
    0x07, 0x00, 0x00, 0x00, 0x1f, 0xe0, 0xc7, 0xbf, 0xe7, 0x1e, 0xfe, 0xf8, 0x77, 0xce, 0x01, 0xc0, 0x07, 0x00, 0x00,
    0x00, 0x1f, 0xe0, 0x0f, 0x3f, 0xe7, 0x1c, 0xfe, 0xf0, 0x77, 0xce, 0x03, 0xc0, 0x07, 0x00, 0x00, 0x00, 0x1f, 0xe0,
    0xcf, 0x3f, 0xe7, 0x1c, 0xfe, 0xf8, 0xf3, 0xce, 0x03, 0xc0, 0xff, 0xff, 0xff, 0xff, 0x0f, 0xe0, 0xef, 0x1f, 0xe7,
    0x1c, 0xfe, 0xfe, 0xf1, 0xce, 0x03, 0x80, 0xff, 0xff, 0xff, 0xff, 0x0f, 0xe0, 0xff, 0x0f, 0xcf, 0x1c, 0xfc, 0xff,
    0xf0, 0xc0, 0x03, 0x00, 0xff, 0xff, 0xff, 0xff, 0x07, 0xe0, 0xff, 0x03, 0x06, 0x1c, 0xfc, 0x7f, 0x60, 0xc0, 0x01,
    0x00, 0xfe, 0xff, 0xff, 0xff, 0x03, 0xe0, 0xff, 0x00, 0x00, 0x00, 0xfc, 0x0f, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xff,
    0xff, 0xff, 0x00, 0xc0, 0x0f, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, 0x00, 0x1e, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
// width: 128, height: 48
const unsigned char three_support[] U8X8_PROGMEM = {
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x01,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0x00,
    0xe0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x03, 0x00, 0x00, 0x00, 0xfc, 0x1f, 0x00,
    0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x03, 0x00, 0x00, 0x00, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x80,
    0x0f, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x03, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x80, 0x1f, 0x00, 0x00,
    0x00, 0x00, 0xf0, 0x03, 0x00, 0x00, 0x80, 0x0f, 0xf0, 0x00, 0x00, 0x00, 0xc0, 0x3f, 0x00, 0x00, 0x00, 0x00, 0xf8,
    0x03, 0x00, 0x00, 0x80, 0x0f, 0xf0, 0x01, 0x00, 0x00, 0xfc, 0xff, 0x01, 0x00, 0x00, 0xc0, 0xfd, 0xff, 0x00, 0x00,
    0xc0, 0x7f, 0xfe, 0x01, 0x00, 0x00, 0xff, 0xff, 0x0f, 0x00, 0x00, 0xe0, 0xfd, 0xff, 0x01, 0x00, 0xc0, 0x1f, 0xf8,
    0x03, 0x00, 0x00, 0xff, 0xff, 0x0f, 0x00, 0x00, 0xe0, 0xfd, 0xff, 0x01, 0x00, 0xc0, 0x0f, 0xf0, 0x03, 0x00, 0x00,
    0xfe, 0xff, 0x07, 0x00, 0x00, 0xe0, 0xfd, 0xff, 0x01, 0x00, 0xc0, 0x67, 0xe6, 0x03, 0x00, 0x00, 0xfc, 0xff, 0x03,
    0x00, 0x00, 0xe0, 0xfd, 0xff, 0x01, 0x00, 0xc0, 0x67, 0xe6, 0x03, 0x00, 0x00, 0xf8, 0xff, 0x01, 0x00, 0x00, 0xe0,
    0xfd, 0xff, 0x00, 0x00, 0xc0, 0x67, 0xe6, 0x03, 0x00, 0x00, 0xf0, 0x7f, 0x00, 0x00, 0x00, 0xe0, 0xfd, 0xff, 0x00,
    0x00, 0xc0, 0x67, 0xee, 0x03, 0x00, 0x00, 0xe0, 0x7f, 0x00, 0x00, 0x00, 0xe0, 0xfd, 0xff, 0x00, 0x00, 0x80, 0x7f,
    0xfe, 0x01, 0x00, 0x00, 0xe0, 0xff, 0x00, 0x00, 0x00, 0xe0, 0xfd, 0xff, 0x00, 0x00, 0x80, 0x7f, 0xfe, 0x01, 0x00,
    0x00, 0xf0, 0xff, 0x00, 0x00, 0x00, 0xe0, 0xfd, 0x7f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xf0, 0xff,
    0x00, 0x00, 0x00, 0xe0, 0xfd, 0x7f, 0x00, 0x00, 0x00, 0xfe, 0xff, 0x00, 0x00, 0x00, 0xf8, 0xf9, 0x01, 0x00, 0x00,
    0xe0, 0xfd, 0x7f, 0x00, 0x00, 0x00, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0xf8, 0xf0, 0x00, 0x00, 0x00, 0xe0, 0xfd, 0x1f,
    0x00, 0x00, 0x00, 0xf8, 0x1f, 0x00, 0x00, 0x00, 0x30, 0xc0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0xc0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

void testDrawXBM(u8g2_t *u8g2)
{
    int t = 1000;
    u8g2_ClearBuffer(u8g2);
    u8g2_DrawStr(u8g2, 0, 14, "DrawXBM");

    u8g2_DrawXBM(u8g2, 0, 16, 128, 48, bilibili);
    SEND_BUFFER_DISPLAY_MS(u8g2, t);

    u8g2_ClearBuffer(u8g2);
    u8g2_DrawStr(u8g2, 0, 14, "bilibili");
    u8g2_DrawXBM(u8g2, 0, 16, 128, 48, three_support);
    SEND_BUFFER_DISPLAY_MS(u8g2, t);
}

void DrawTest(u8g2_t *u8g2)
{
    testDrawProcess(u8g2);
    testDrawMulti(u8g2);
    // testDrawFrame(u8g2);
    // testDrawRBox(u8g2);
    // testDrawCircle(u8g2);
    // testDrawFilledEllipse(u8g2);
    testShowFont(u8g2);
    testDrawXBM(u8g2);
}

void u8g2DrawTest(void)
{
    u8g2_FirstPage(&u8g2);
    do {
        draw(&u8g2);
        DrawTest(&u8g2);
    } while (u8g2_NextPage(&u8g2));
}

编写测试程序

freertos.c文件

#include "oled.h" //在USER CODE BEGIN Includes中添加头文件

void StartDefaultTask(void *argument)
{
  /* USER CODE BEGIN StartDefaultTask */
  oledInit();
  /* Infinite loop */
  for(;;)
  {
    osDelay(1000);
    u8g2DrawTest();
  }
  /* USER CODE END StartDefaultTask */
}

上面代码中主要有两个函数很关键,u8x8_byte_hw_i2c函数是通讯使用的,其中U8X8_MSG_BYTE_INIT中需要提供iic的硬件初始化,会在初始化u8g2屏幕时调用一次,U8X8_MSG_BYTE_START_TRANSFER,U8X8_MSG_BYTE_SEND,U8X8_MSG_BYTE_END_TRANSFER分别是对应iic的传输过程。

u8x8_gpio_and_delay函数主要实现延时函数,需要实现U8X8_MSG_DELAY_100NANO,U8X8_MSG_DELAY_10MICRO,U8X8_MSG_DELAY_MILLI,U8X8_MSG_DELAY_I2C。个人感觉不比太准确,能尽量准确更好,如果调试中出现奇怪的问题可以尝试这个地方的修改。

U8X8_MSG_GPIO_MENU_***这个的功能主要与菜单操作相关。如果没有使用可以直接填写u8x8_SetGPIOResult(u8x8, 0);

至此已经完成了U8G2的移植和主要功能,在此之后开始讲解U8G2提供的菜单功能。

菜单功能

菜单需要4个按钮,我的硬件如下图

工程配置

内部上拉,抬起时高电平,按下时低电平。

然后我们需要把上面提到的u8x8_gpio_and_delay函数修改。

    case U8X8_MSG_GPIO_MENU_SELECT:
        u8x8_SetGPIOResult(u8x8, HAL_GPIO_ReadPin(OLED_ENTER_GPIO_Port, OLED_ENTER_Pin) == GPIO_PIN_SET ? 1 : 0);
        break;
    case U8X8_MSG_GPIO_MENU_NEXT:
        u8x8_SetGPIOResult(u8x8, HAL_GPIO_ReadPin(OLED_DOWN_GPIO_Port, OLED_DOWN_Pin) == GPIO_PIN_SET ? 1 : 0);
        break;
    case U8X8_MSG_GPIO_MENU_PREV:
        u8x8_SetGPIOResult(u8x8, HAL_GPIO_ReadPin(OLED_UP_GPIO_Port, OLED_UP_Pin) == GPIO_PIN_SET ? 1 : 0);
        break;
    case U8X8_MSG_GPIO_MENU_HOME:
        u8x8_SetGPIOResult(u8x8, HAL_GPIO_ReadPin(OLED_BACK_GPIO_Port, OLED_BACK_Pin) == GPIO_PIN_SET ? 1 : 0);
        break;

然后讲一下三个函数

uint8_t u8g2_UserInterfaceSelectionList(u8g2_t *u8g2, const char *title, uint8_t start_pos, const char *sl);
uint8_t u8g2_UserInterfaceMessage(u8g2_t *u8g2, const char *title1, const char *title2, const char *title3, const char *buttons);
uint8_t u8g2_UserInterfaceInputValue(u8g2_t *u8g2, const char *title, const char *pre, uint8_t *value, uint8_t lo, uint8_t hi, uint8_t digits, const char *post);

然后分别讲一下这三个函数的用法

u8g2_UserInterfaceInputValue

请求输入一个 8 位值。

所有显示输出和按键处理都在此功能中完成。

参数:

u8g2:指向 u8g2 结构的指针。

title:值的多行描述(行必须用 \n 分隔)。

pre:值前的文本。

value:指向将用用户输入填充的变量的指针。

lo:最低值,可由用户选择。

hi:最高值,可由用户选择。

digits:位数(1 到 3)。

post:值后的文本。

返回:1,如果用户按下了选择按钮。0 如果用户按下了主页 / 取消按钮。

仅当用户按下选择键时,所选值才会存储在 value 中。

例子代码:

void StartDefaultTask(void *argument)
{
  /* USER CODE BEGIN StartDefaultTask */
  oledInit();
  uint8_t value = 4;
  uint8_t r;
  /* Infinite loop */
  for(;;)
  {
      osDelay(3000);
      // u8g2DrawTest();
      r = u8g2_UserInterfaceInputValue(&u8g2, "Title\n-----\n", "X=", &value, 0, 19, 2, "m");

      u8g2_ClearBuffer(&u8g2);  // 清空缓冲区
      // 设置光标位置 (x, y)
      u8g2_SetFont(&u8g2, u8g2_font_6x10_tf);  // 设置字体
      u8g2_DrawStr(&u8g2, 0, 10, "Value:");    // 显示 "Value:"
      char buffer[4];                       // 用于存储转换后的字符串
      sprintf(buffer, "%d", value);         // 将 uint8_t 转换为字符串
      u8g2_DrawStr(&u8g2, 50, 10, buffer);  // 显示 value 的值
      u8g2_DrawStr(&u8g2, 0, 25, "R:");     // 显示 "R:"
      sprintf(buffer, "%d", r);             // 将 r 转换为字符串
      u8g2_DrawStr(&u8g2, 30, 25, buffer);  // 显示 r 的值
      u8g2_SendBuffer(&u8g2);  // 将缓冲区内容发送到 OLED
  }
  /* USER CODE END StartDefaultTask */
}

然后是u8g2_UserInterfaceSelectionList函数

描述:显示可滚动和可选选项的列表。用户可以选择其中一个选项。

参数:

u8g2:指向 u8g2 结构的指针。

start_pos:首先突出显示的元素(以 1 开头)。

sl:选项列表,每行一个(行必须用 n 分隔)。

返回:如果已选择其中一个按钮,则为 1 到 n。如果用户按下了主页/取消按钮,则为 0。

u8g2_UserInterfaceMessage函数

说明:显示消息文本并等待用户输入。用户可以按一个按钮或选择

在两个或多个按钮之间。

参数:

u8g2:指向 u8g2 结构的指针。

title1:第一个多行描述(行必须用 n 分隔)。

title2:第二行描述(画一条线直到第一条 n 或 0)。

title3:第三行描述(行必须用 n 分隔)。

button:一个或多个按钮,以 n 分隔。

返回:如果已选择其中一个按钮,则为 1 到 n。如果用户按下了主页/取消按钮,则为 0。

例子代码:

void StartDefaultTask(void *argument)
{
  /* USER CODE BEGIN StartDefaultTask */
  oledInit();
  u8g2_SetFont(&u8g2, u8g2_font_6x12_tr);
  const char *string_list =
      "Altocumulus\n"
      "Altostratus\n"
      "Cirrocumulus\n"
      "Cirrostratus\n"
      "Cirrus\n"
      "Cumulonimbus\n"
      "Cumulus\n"
      "Nimbostratus\n"
      "Stratocumulus\n"
      "Stratus";

  uint8_t current_selection = 2;
  /* Infinite loop */
  for (;;) {
      osDelay(3000);
      current_selection = u8g2_UserInterfaceSelectionList(&u8g2, "Cloud Types", current_selection, string_list);

      if (current_selection == 0) {
          u8g2_UserInterfaceMessage(&u8g2, "Nothing selected.", "", "", " ok ");
      } else {
          u8g2_UserInterfaceMessage(&u8g2, "Selection:", u8x8_GetStringLineStart(current_selection - 1, string_list), "",
                                    " ok \n cancel ");
      }
  }
  /* USER CODE END StartDefaultTask */
}

u8g2_UserInterfaceMessage返回较慢

我们分析一下三个函数是如何工作的

u8g2_UserInterfaceMessage

==》u8x8_GetMenuEvent

====》u8x8_read_pin_state

======》u8x8_read_pin_state

========》u8x8_gpio_call

==========》gpio_and_delay_cb

最终会调用到我们之前写的u8x8_gpio_and_delay函数中,也就是会调用u8x8_SetGPIOResult函数,这里有一个问题就是他会一直调用这个,如果不是freertos程序会卡在这里,并且cpu占用率为100%,只有中断能打断这个过程。如果是freertos可以被时间片轮训打断,但是如果调用这个函数的任务优先级是最高的那就玩蛋了。所以如果我们确定在freertos中使用可以这样修改代码,让任务主动释放给其他任务。

    case U8X8_MSG_GPIO_MENU_SELECT:
        osDelay(10);
        u8x8_SetGPIOResult(u8x8, HAL_GPIO_ReadPin(OLED_ENTER_GPIO_Port, OLED_ENTER_Pin) == GPIO_PIN_SET ? 1 : 0);
        break;
    case U8X8_MSG_GPIO_MENU_NEXT:
        osDelay(10);
        u8x8_SetGPIOResult(u8x8, HAL_GPIO_ReadPin(OLED_DOWN_GPIO_Port, OLED_DOWN_Pin) == GPIO_PIN_SET ? 1 : 0);
        break;
    case U8X8_MSG_GPIO_MENU_PREV:
        osDelay(10);
        u8x8_SetGPIOResult(u8x8, HAL_GPIO_ReadPin(OLED_UP_GPIO_Port, OLED_UP_Pin) == GPIO_PIN_SET ? 1 : 0);
        break;
    case U8X8_MSG_GPIO_MENU_HOME:
        osDelay(10);
        u8x8_SetGPIOResult(u8x8, HAL_GPIO_ReadPin(OLED_BACK_GPIO_Port, OLED_BACK_Pin) == GPIO_PIN_SET ? 1 : 0);
        break;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值