参考文章:
U8g2图形库与STM32移植(I2C,软件与硬件) - 冰封残烛 - 博客园
基于STM32移植U8g2图形库——OLED显示(HAL库)_stm32 u8g2-CSDN博客
stm32f103移植u8g2(硬件I2C) - njit-sam - 博客园
stm32f103移植u8g2(硬件I2C) - njit-sam - 博客园
STM32 U8g2 spi软件驱动,spi硬件驱动(优化u8g2的软件spi,速度翻好几倍)-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;