前言:
前面我们已经成功移植了U8g2的图形库(0.96寸OLED):手把手移植U8g2图形库,这个文章主要熟悉u8g2图形库的常用C语言函数!需要移植的资料的可以关注一波评论区评论,我看到了就会给你发哦!这里主要是介绍一部分函数的使用说明和使用这个强大的库制作一个多功能智能手表!
手表的基本功能(部分外设传感器还没有到):自己成功移植好了库后,可以结合学习的32板子自己去做一点这种小玩具玩玩,也挺有趣的!
基于STMF103的多功能手表玩具(残缺版)
一、OLED的初始化
1.1、初始化与硬件相关的配置函数
void u8g2_Setup_ssd1306_i2c_128x64_noname_f(&u8g2, U8G2_R0, u8x8_byte_sw_i2c, u8g2_gpio_and_delay_stm32);
1.2、u8g2_InitDisplay——OLED显示初始化
void u8g2_InitDisplay(u8g2_t *u8g2);
作用:写入关键显示参数,对显示初始化
1.3、u8g2_setPowerSave——OLED打开与关闭
函数:
void u8g2_setPowerSave(u8g2_t *u8g2, uint8_t is_enable);
参数:
is_enable:
1:关闭OLED
0:打开OLED
⭐1.4、缓存区的使用
须知:在OLED上显示一个物体,不是直接给OLED发送数据的,它们中间其实有一个中介——缓存区,首先将数据存进缓存区里面,然后在使用函数发送给OLED的!
清除缓存区:
void u8g2_ClearBuffer(u8g2_t *u8g2);
发送缓存区的数据给OLED:
void u8g2_SendBuffer(u8g2_t *u8g2);
清屏函数:
void u8g2_ClearDisplay(u8g2_t *u8g2);
作用和一次性使用下面两个函数一致:
void u8g2_ClearBuffer(u8g2_t *u8g2);
void u8g2_SendBuffer(u8g2_t *u8g2);
1.5、使用以上函数对OLED进行初始化和函数配置
#define u8 unsigned char // ?unsigned char ????
#define MAX_LEN 128 //
#define OLED_ADDRESS 0x78 // oled
#define OLED_CMD 0x00 //
#define OLED_DATA 0x40 //
/* 填空初始化u8x8_byte_hw_i2c函数 */
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_I2C2_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(&hi2c2, OLED_ADDRESS, buffer, buf_idx, 1000) != HAL_OK)
return 0;
}
break;
case U8X8_MSG_BYTE_SET_DC:
break;
default:
return 0;
}
return 1;
}
/* 填空初始化u8x8_gpio_and_delay函数 */
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
HAL_Delay(1);
break;
case U8X8_MSG_DELAY_I2C: // arg_int is the I2C speed in 100KHz, e.g. 4 = 400 KHz
Tims_delay_us(5);
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;
}
void u8g2Init(u8g2_t *u8g2)
{
u8g2_Setup_ssd1306_i2c_128x64_noname_f(u8g2, U8G2_R0, u8x8_byte_hw_i2c, u8x8_gpio_and_delay); // 初始化u8g2 结构体
u8g2_InitDisplay(u8g2); //对缓存进行初始化 // 初始化u8x8_gpio_and_delay函数
u8g2_SetPowerSave(u8g2, 0); //wake up 屏幕 // 初始化u8x8_byte_hw_i2c函数
u8g2_ClearBuffer(u8g2); //清除缓存区 //对驱动进行初始化
}
二、入门函数显示各种东西
通过以上的初始化就可以正式学习函数的使用,去如何显示各种各样的东西了!
2.1、u8g2_DrawXBMP——显示一张图片
void u8g2_DrawXBMP(u8g2_t *u8g2, u8g2_uint_t x, u8g2_uint_t y, u8g2_uint_t w,
u8g2_uint_t h, const uint8_t *bitmap);
软件设置如下:(设置不对会显示很乱)
代码:
static const unsigned char mmap[] U8X8_PROGMEM=
{
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0xC0,0xC7,0x07,0x00,0x40,0x4C,0x0C,0x00,
0x40,0x48,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x00,0x00,
0x00,0x84,0x00,0x00,0x00,0x84,0x00,0x00,0x00,0x88,0x00,0x00,0x00,0x98,0x00,0x00,
0x00,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,/*"未命名文件",0*/
};
u8g2_DrawXBMP(&u8g2, 0, 0, 25, 25, mmap);
u8g2_SendBuffer(&u8g2);//发送缓冲区的数据给OLED
显示效果:
2.2、u8g2_DrawBox() —— 画实心矩形
/**
* 画实心矩形,左上角坐标为(x,y),宽度为w,高度为h
* @param x 左上角的x坐标
* @param y 左上角的y坐标
* @param w 方形的宽度
* @param h 方形的高度
*/
void u8g2_DrawBox(u8g2_t *u8g2, u8g2_uint_t x, u8g2_uint_t y, u8g2_uint_t w, u8g2_uint_t h)
u8g2_DrawBox(&u8g2,3,7,25,15);
2.3、u8g2_DrawCircle() —— 画空心圆
函数说明:
/**
* 画空心圆,圆心坐标为(x0,y0),半径为rad
* @param x0 圆点的x坐标
* @param y0 圆点的y坐标
* @param rad 圆形的半径
* @param opt 圆形选项
* U8G2_DRAW_ALL 整个圆
* U8G2_DRAW_UPPER_RIGHT 右上部分的圆弧
* U8G2_DRAW_UPPER_LEFT 左上部分的圆弧
* U8G2_DRAW_LOWER_LEFT 左下部分的圆弧
* U8G2_DRAW_LOWER_RIGHT 右下部分的圆弧
* 选项可以通过 | 操作符来组合
*/
void u8g2_DrawCircle(u8g2_t *u8g2, u8g2_uint_t x0, u8g2_uint_t y0, u8g2_uint_t rad, uint8_t option)
示例:
u8g2_DrawCircle(&u8g2,20,25,10,U8G2_DRAW_ALL);
2.4、u8g2_DrawDisc() —— 画实心圆
/**
* 画实心圆,圆心坐标为(x0,y0),半径为rad
* @param x0 圆点的x坐标
* @param y0 圆点的y坐标
* @param rad 圆形的半径
* @param opt 圆形选项
* U8G2_DRAW_ALL 整个圆
* U8G2_DRAW_UPPER_RIGHT 右上部分的圆弧
* U8G2_DRAW_UPPER_LEFT 左上部分的圆弧
* U8G2_DRAW_LOWER_LEFT 左下部分的圆弧
* U8G2_DRAW_LOWER_RIGHT 右下部分的圆弧
* 选项可以通过 | 操作符来组合
*/
void u8g2_DrawDisc(u8g2_t *u8g2, u8g2_uint_t x0, u8g2_uint_t y0, u8g2_uint_t rad, uint8_t option)
2.5、u8g2_DrawFrame() —— 画空心矩形
/**
* 画空心方形,左上角坐标为(x,y),宽度为w,高度为h
* @param x 左上角的x坐标
* @param y 左上角的y坐标
* @param w 方形的宽度
* @param h 方形的高度
*/
void u8g2_DrawFrame(u8g2_t *u8g2, u8g2_uint_t x, u8g2_uint_t y, u8g2_uint_t w, u8g2_uint_t h)
2.6、u8g2_DrawHLine() —— 绘制水平线
/**
* 绘制水平线
* @param x 左上角的x坐标
* @param y 左上角的y坐标
* @param w 水平线的长度
*/
void u8g2_DrawHLine(u8g2_t *u8g2, u8g2_uint_t x, u8g2_uint_t y, u8g2_uint_t len)
2.7、u8g2_DrawLine() —— 两点之间绘制线
/**
* 绘制线,从坐标(x0,y0) 到(x1,y1)
* @param x0 端点0的x坐标
* @param y0 端点0的y坐标
* @param x1 端点1的x坐标
* @param y1 端点1的y坐标
*/
void u8g2_DrawLine(u8g2_t *u8g2, u8g2_uint_t x1, u8g2_uint_t y1, u8g2_uint_t x2, u8g2_uint_t y2)
2.8、u8g2_DrawPixel() —— 绘制像素点
/**
* 绘制像素点,坐标(x,y)
* @param x 像素点的x坐标
* @param y 像素点的y坐标
* @Note 关联方法 setDrawColor
*/
void u8g2_DrawPixel(u8g2_t *u8g2, u8g2_uint_t x, u8g2_uint_t y)
2.9、u8g2_DrawRBox() —— 绘制圆角实心方形
/**
* 绘制圆角实心方形,左上角坐标为(x,y),宽度为w,高度为h,圆角半径为r
* @param x 左上角的x坐标
* @param y 左上角的y坐标
* @param w 方形的宽度
* @param h 方形的高度
* @param r 圆角半径
*/
void u8g2_DrawRBox(u8g2_t *u8g2, u8g2_uint_t x, u8g2_uint_t y, u8g2_uint_t w, u8g2_uint_t h, u8g2_uint_t r)
2.10、u8g2_DrawRFrame() —— 绘制圆角空心方形
/**
* 绘制圆角空心方形,左上角坐标为(x,y),宽度为w,高度为h,圆角半径为r
* @param x 左上角的x坐标
* @param y 左上角的y坐标
* @param w 方形的宽度
* @param h 方形的高度
* @param r 圆角半径
*/
void u8g2_DrawRFrame(u8g2_t *u8g2, u8g2_uint_t x, u8g2_uint_t y, u8g2_uint_t w, u8g2_uint_t h, u8g2_uint_t r)
2.11、u8g2_DrawTriangle() —— 绘制实心三角形
/**
* 绘制实心三角形,定点坐标分别为(x0,y0),(x1,y1),(x2,y2)
*/
void u8g2_DrawTriangle(u8g2_t *u8g2, int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2)
2.12、u8g2_DrawVLine() —— 绘制竖直线
/**
* 绘制竖直线
* @param x 左上角坐标x
* @param y 左上角坐标y
* @param h 高度
*/
void u8g2_DrawVLine(u8g2_t *u8g2, u8g2_uint_t x, u8g2_uint_t y, u8g2_uint_t len)
2.14、部分图形显示效果
三、动态显示
3.1、u8g2_FirstPage/u8g2_NextPage — 绘制命令
/**
* 绘制图像
*/
void u8g2_FirstPage(u8g2_t *u8g2)
uint8_t u8g2_NextPage(u8g2_t *u8g2)
- u8g2_FirstPage方法会把当前页码位置变成0;
- 修改内容处于u8g2_FirstPage和u8g2_NextPage之间,每次都是重新渲染所有内容;
- 该方法消耗的ram空间,比u8g2_SendBuffer消耗的RAM空间要少;
示例:(进度条)
u8g2_FirstPage(&u8g2);
do
{
u8g2_DrawBox(&u8g2,0,32,i++,15);
HAL_Delay(50);
}while (u8g2_NextPage(&u8g2));
四、绘制字体函数
4.1、u8g2_SetFont()——设置字库
写字之前肯定要自己决定写什么字体呀!
原作者制作了挺多的库的,里面其中甚至还有支持外国语言!这里我就举例我会用的和常用的库!
不同的库就代表着字体的大小或者样式不同,还有语言不同,这个库是非常强大的,还是有很多值得去探究的!
其他的库就需要自己去探究了!
4.2、u8g2_DrawStr()——绘制字符串
参数:
u8g2 : u8g2 结构体(C interface only). x,y: 左下角起点坐标
*s: 字符串文本
返回值: 字符串的长度
函数:
u8g2_uint_t u8g2_DrawStr(u8g2_t *u8g2, u8g2_uint_t x, u8g2_uint_t y, const char *s);
u8g2_uint_t u8g2_DrawStrX2(u8g2_t *u8g2, u8g2_uint_t x, u8g2_uint_t y, const char *s);
使用例子:
u8g2_SetFont(&u8g2,u8g2_font_ncenB14_tr);//设置字库
u8g2_DrawStr(&u8g2,0,15,"Hello World!");//绘制你好世界
u8g2_SendBuffer(&u8g2);//发送缓冲区的数据给OLED
效果:
4.3、u8g2_DrawUTF8() —— 绘制UTF8编码的字符
/**
* 绘制UTF8编码的字符串
* @param x 字符串在屏幕上的左下角x坐标
* @param y 字符串在屏幕上的左下角y坐标
* @param s 需要绘制的UTF-8编码字符串
* @return 返回字符串的长度
*/
u8g2_uint_t u8g2_DrawUTF8(u8g2_t *u8g2, u8g2_uint_t x, u8g2_uint_t y, const char *str)
u8g2_uint_t u8g2_DrawUTF8X2(u8g2_t *u8g2, u8g2_uint_t x, u8g2_uint_t y, const char *s);//双倍显示
使用该方法,有两个前提。首先是你的编译器需要支持UTF-8编码,对于绝大部分Arduino板子已经支持;其次,显示的字符串需要存为“UTF-8”编码,Arduino IDE上默认支持;
该方法需要依赖于fontMode(setFont)以及drawing Color,也就是说如果你传进来的字符串编码必须在font定义里面;
Keil v5 mdk 编译UTF8字符串报错的解决办法–no-multibyte-chars
使用例子:
u8g2_SetFont(&u8g2,u8g2_font_unifont_t_symbols);
u8g2_DrawUTF8(&u8g2,5, 20, "Snowman: ☃");
4.4、绘制数字
还是使用u8g2_DrawStr函数
具体实现:(记得包括头文件stdio.h)
void PrintVarFormat(u8g2_t *u8g2, u8g2_uint_t x, u8g2_uint_t y, const uint8_t *font, short var)
{
char var_buff[100]; //用来存ASCII码
u8g2_SetFont(u8g2, font); //设定字体
sprintf(var_buff, "%4d", var); //传递变量(十进制(占四格))
u8g2_DrawStr(u8g2, x, y, var_buff); //显示
}
for(uint8_t i=0;i<100;i++)
{
u8g2_ClearBuffer(&u8g2);
PrintVarFormat(&u8g2,63,31,u8g2_font_ncenB14_tr,i);
HAL_Delay(1000);
u8g2_SendBuffer(&u8g2);
}
效果:
4.5、u8g2_DrawGlyph()——绘制有趣的图标
函数:
函数:u8g2_uint_t u8g2_DrawGlyph(u8g2_t *u8g2, u8g2_uint_t x, u8g2_uint_t y, uint16_t encoding);
u8g2_uint_t u8g2_DrawGlyphX2(u8g2_t *u8g2, u8g2_uint_t x, u8g2_uint_t y, uint16_t encoding);
说明:画图形式字符,8g2_uint_t u8g2_DrawGlyphX2()图形放大显示
参数:
u8g2 : u8g2 结构体(C interface only). x,y: 左下角坐标
encoding: 字形索引
例子:
u8g2_SetFont(&u8g2,u8g2_font_unifont_t_symbols);//
u8g2_DrawGlyph(&u8g2,30, 20, 0x2603);
u8g2_DrawGlyphX2(&u8g2,70,45, 0x2603);
u8g2_SendBuffer(&u8g2);
效果:
上面的数字号码,是根据这个图:
五、使用U8g2库显示众多图形效果
这里的代码是一个优快云大佬博主的,我这里直接拿来使用了,原博主我找不到了,如果原博主看到了可以告诉我一下!
U8g2库常用函数驱动OLED显示效果