上篇文章(《OrangePi ZERO 2 外设应用程序开发之 I²C 配置及驱动 OLED 屏幕》)我们用 wiringOP 库的 demo 代码,成功地驱动了 OLED 屏幕,本篇就基于 wiringOP 库所提供的 API 进行二次开发,便于实现更多的显示效果。
一、基于oled_putstr
函数的文本显示
先模仿 demo 代码写一段程序,显示内容部分在oledShow
函数里完成。
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <stdint.h>
#include <wiringPi.h>
#include "oled.h"
#include "font.h"
void oledShow(struct display_info *disp)
{
}
int main(int argc, char **argv)
{
int err;
char channal[32];
struct display_info disp;
if (argc < 2) {
printf("\nUsage:\n%s <I2C bus device node >\n", argv[0]);
return -1;
}
memset(&disp, 0, sizeof(disp));
sprintf(channal, "%s", argv[1]);
disp.address = OLED_I2C_ADDR;
disp.font = font2;
err = oled_open(&disp, channal);
if (err < 0) {
printf("\nERROR: %i, Failed(oled_open)\n\n", err);
} else {
err = oled_init(&disp);
if (err < 0) {
printf("\nERROR: %i, Failed(oled_init)\n\n", err);
} else {
printf("---------start--------\n");
oled_clear(&disp);
oledShow(&disp);
delay(3000);
oled_clear(&disp);
printf("----------end---------\n");
}
}
return 0;
}
显示的字体,我个人比较喜欢font2
,这个字体每行可以显示 21 个字符,字体间隔没那么紧凑,看着页舒服。font1
和font3
每行都可以显示 25 个字符,都是比较紧凑。另外就是font3
的字体太小,也不方便阅读。
oled_putstr
函数是按页寻址模式填充 OLED 的,不过只能填充一页,第三个参数是需要显示在 OLED 上的字符串首地址,如果字符串个数超出每页可显示的字符个数,只会显示最前面部分,超出部分不显示。
如下代码,是在第三页的位置显示buf
数组的内容。
void oledShow(struct display_info *disp)
{
uint8_t buf[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
oled_putstr(disp, 3, buf);
oled_send_buffer(disp);
}
实际效果如下(font2
字体最多每页显示 21 个字符):
如果每页显示相同的字符,代码如下:
void oledShow(struct display_info *disp)
{
uint8_t buf[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
for (int i = 0; i < 8; i++)
oled_putstr(disp, i, buf);
oled_send_buffer(disp);
}
效果如下:
当然,如果显示一段英文短句,也是可以的,只要对数组进行偏移即可。
void oledShow(struct display_info *disp)
{
uint8_t buf[] = " Life is like a rainbow, "\
"each color represents a different experience, "\
"and together they make up the beautiful sky.";
for (int i = 0; i < 6; i++)
oled_putstr(disp, i + 1, buf + i * 21);
oled_send_buffer(disp);
}
效果如下:
二、基于oled_putpixel
函数的图形显示
oled_putpixel
函数可以在 OLED 任意位置点亮或熄灭一个点,可以利用这个特点,在 OLED 上绘制一些图案。类似这种通过点亮某些点的方式来绘制图案的方法,通常都会用 Bresenham 算法来实现,下面是一些通过 Bresenham 算法实现的图形绘制函数。
1. 绘制直线
使用相同的显示函数 oled_putpixel
来绘制直线,可以借助直线的数学定义和简单的直线算法(Bresenham 算法)来实现。下面是一个示例代码,用于在 128 × 64 的 OLED 上绘制一条直线:
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <stdint.h>
#include <wiringPi.h>
#include <math.h>
#include "oled.h"
#include "font.h"
void drawLine(struct display_info *disp, int x0, int y0, int x1, int y1)
{
int dx = abs(x1 - x0);
int dy = abs(y1 - y0);
int sx = (x0 < x1) ? 1 : -1;
int sy = (y0 < y1) ? 1 : -1;
int err = dx - dy;
while (x0 != x1 || y0 != y1) {
oled_putpixel(disp, x0, y0, 1);
oled_send_buffer(disp);
int e2 = 2 * err;
if (e2 > -dy) {
err -= dy;
x0 += sx;
}
if (e2 < dx) {
err += dx;
y0 += sy;
}
}
}
int main(int argc, char **argv)
{
int err;
char channal[32];
struct display_info disp;
if (argc < 2) {
printf("\nUsage:\n%s <I2C bus device node >\n", argv[0]);
return -1;
}
memset(&disp, 0, sizeof(disp));
sprintf(channal, "%s", argv[1]);
disp.address = OLED_I2C_ADDR;
disp.font = font2;
err = oled_open(&disp, channal);
if (err < 0) {
printf("\nERROR: %i, Failed(oled_open)\n\n", err);
} else {
err = oled_init(&disp);
if (err < 0) {
printf("\nERROR: %i, Failed(oled_init)\n\n", err);
} else {
printf("---------start--------\n");
oled_clear(&disp);
drawLine(&disp, 0, 50, 100, 0);
delay(3000);
oled_clear(&disp);
printf("----------end---------\n");
}
}
return 0;
}
这里封装了一个绘制直线的函数drawLine
,后面四个参数分别是直线起点的坐标和终点坐标,效果如下:
2. 绘制圆形
同样可以借助圆的数学定义和 Bresenham 算法来实现在 OLED 上面画圆,下面是一个简单的示例代码:
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <stdint.h>
#include <wiringPi.h>
//#include <math.h>
#include "oled.h"
#include "font.h"
void drawCircle(struct display_info *disp, int xc, int yc, int radius)
{
int x = radius;
int y = 0;
int err = 0;
while (x >= y) {
oled_putpixel(disp, xc + x, yc + y, 1);
oled_putpixel(disp, xc + y, yc + x, 1);
oled_putpixel(disp, xc - y, yc + x, 1);
oled_putpixel(disp, xc - x, yc + y, 1);
oled_putpixel(disp, xc - x, yc - y, 1);
oled_putpixel(disp, xc - y, yc - x, 1);
oled_putpixel(disp, xc + y, yc - x, 1);
oled_putpixel(disp, xc + x, yc - y, 1);
oled_send_buffer(disp);
if (err <= 0) {
y += 1;
err += 2*y + 1;
}
if (err > 0) {
x -= 1;
err -= 2*x + 1;
}
}
}
int main(int argc, char **argv)
{
int err;
char channal[32];
struct display_info disp;
if (argc < 2) {
printf("\nUsage:\n%s <I2C bus device node >\n", argv[0]);
return -1;
}
memset(&disp, 0, sizeof(disp));
sprintf(channal, "%s", argv[1]);
disp.address = OLED_I2C_ADDR;
disp.font = font2;
err = oled_open(&disp, channal);
if (err < 0) {
printf("\nERROR: %i, Failed(oled_open)\n\n", err);
} else {
err = oled_init(&disp);
if (err < 0) {
printf("\nERROR: %i, Failed(oled_init)\n\n", err);
} else {
printf("---------start--------\n");
oled_clear(&disp);
drawCircle(&disp, 64, 32, 30);
delay(3000);
oled_clear(&disp);
printf("----------end---------\n");
}
}
return 0;
}
主函数中调用了绘制圆形的函数,圆心是(64, 32)
,半径为 30,效果如下:
想玩花一些,可以画一个奥运五环,代码如下:
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <stdint.h>
#include <wiringPi.h>
//#include <math.h>
#include "oled.h"
#include "font.h"
void drawCircle(struct display_info *disp, int xc, int yc, int radius)
{
int x = radius;
int y = 0;
int err = 0;
while (x >= y) {
oled_putpixel(disp, xc + x, yc + y, 1);
oled_putpixel(disp, xc + y, yc + x, 1);
oled_putpixel(disp, xc - y, yc + x, 1);
oled_putpixel(disp, xc - x, yc + y, 1);
oled_putpixel(disp, xc - x, yc - y, 1);
oled_putpixel(disp, xc - y, yc - x, 1);
oled_putpixel(disp, xc + y, yc - x, 1);
oled_putpixel(disp, xc + x, yc - y, 1);
oled_send_buffer(disp);
if (err <= 0) {
y += 1;
err += 2*y + 1;
}
if (err > 0) {
x -= 1;
err -= 2*x + 1;
}
}
}
int main(int argc, char **argv)
{
int err;
char channal[32];
struct display_info disp;
if (argc < 2) {
printf("\nUsage:\n%s <I2C bus device node >\n", argv[0]);
return -1;
}
memset(&disp, 0, sizeof(disp));
sprintf(channal, "%s", argv[1]);
disp.address = OLED_I2C_ADDR;
disp.font = font2;
err = oled_open(&disp, channal);
if (err < 0) {
printf("\nERROR: %i, Failed(oled_open)\n\n", err);
} else {
err = oled_init(&disp);
if (err < 0) {
printf("\nERROR: %i, Failed(oled_init)\n\n", err);
} else {
printf("---------start--------\n");
oled_clear(&disp);
drawCircle(&disp, 64, 25, 18);
drawCircle(&disp, 24, 25, 18);
drawCircle(&disp, 64+40, 25, 18);
drawCircle(&disp, 64+20, 40, 18);
drawCircle(&disp, 64-20, 40, 18);
delay(3000);
oled_clear(&disp);
printf("----------end---------\n");
}
}
return 0;
}
效果如下:
3. 显示图片(全屏)
这里特别强调一下,此处所提的图片分辨率必须为 128 × 64 像素的位图,通过取模软件获得图片对应的字符类型一维数组。我用的取模软件是“字模提取 V2.1”,取模方式为 C51 纵向取模。
具体实现显示图片的代码如下:
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <stdint.h>
#include <wiringPi.h>
#include "oled.h"
#include "font.h"
const char bmp_data[] = {
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0xFC,0xFC,0xFC,0xFC,0xFC,0x7C,0x7C,0x7C,0x7C,0x7C,0x7C,0x7C,0x7C,0x7C,0x7C,
0x7C,0x7C,0x7C,0x7C,0x7C,0x7C,0x7C,0x7C,0x7C,0x7C,0xFC,0xFC,0xF8,0xF0,0xE0,0x80,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x8F,0x8F,0x8F,0x8F,0x87,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0xC0,0xFF,0xFF,0xFF,0xFF,0x7F,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,
0x0F,0x0F,0x0F,0x0F,0x8F,0xCF,0xCF,0xCF,0xCF,0xCF,0xC7,0xC7,0xC7,0xC3,0xC1,0xC0,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x3F,0xFF,0xFF,0xFF,0xFF,0xE0,0xE0,0xE0,0xE0,0xE0,0xE0,0xE0,0xE0,0xE0,0xE0,
0xE0,0xE0,0xE6,0xE7,0xE7,0xE7,0xE7,0xE7,0xE7,0xE7,0xE7,0xFF,0xFF,0xFF,0xFF,0xFF,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x01,0x01,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,
0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,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,0xFE,0xFE,0xC6,0xC6,0xC6,0xC6,0xC6,0xE6,0xFE,0x7C,
0x38,0x00,0x00,0xF8,0xFC,0xFE,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x42,0x00,0x00,0xFE,
0xFE,0xFE,0xC6,0xC6,0xC6,0xC6,0xC6,0xFE,0x7E,0x3C,0x00,0x00,0x00,0xFC,0xFE,0xEE,
0xE6,0xE6,0xE6,0xE6,0xE6,0xE6,0x42,0x00,0x00,0xF8,0xFC,0xFE,0xC6,0xC6,0xC6,0xC6,
0xC6,0xC6,0x42,0x00,0x00,0xF8,0xFC,0xFE,0x0E,0x06,0x06,0x06,0x06,0x06,0x06,0x00,
0x02,0x06,0x06,0x06,0xFE,0xFE,0xFE,0x06,0x06,0x06,0x06,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0x1F,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x07,0x0F,0x1F,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x08,0x00,0x00,0x1F,
0x1F,0x1F,0x00,0x00,0x00,0x00,0x01,0x0F,0x1F,0x1E,0x00,0x00,0x00,0x1F,0x1F,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x0F,0x1F,0x1C,0x1C,0x1C,0x1C,
0x1C,0x1C,0x08,0x00,0x00,0x03,0x07,0x0F,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x18,0x00,
0x00,0x00,0x00,0x00,0x1F,0x1F,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x1E,0x0A,0x06,0x00,0x00,0x00,0x00,0x00,0x1E,0x0A,
0x0A,0x14,0x00,0x00,0x00,0x00,0x00,0x0C,0x12,0x12,0x0C,0x00,0x00,0x00,0x00,0x00,
0x0C,0x12,0x12,0x18,0x00,0x00,0x00,0x00,0x00,0x1E,0x0A,0x0A,0x14,0x00,0x00,0x00,
0x00,0x00,0x1C,0x0A,0x0A,0x1C,0x00,0x00,0x00,0x00,0x00,0x1E,0x04,0x18,0x04,0x1E,
0x00,0x00,0x00,0x00,0x00,0x1E,0x04,0x08,0x1E,0x00,0x00,0x00,0x00,0x00,0x00,0x1E,
0x16,0x12,0x00,0x00,0x00,0x00,0x00,0x1E,0x0A,0x0A,0x14,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
};
void displayBmp(struct display_info *disp, const char *bmp)
{
uint8_t x, y;
uint16_t index = 0;
uint8_t pixel_value;
for (y = 0; y < 64; y += 8) {
for (x = 0; x < 128; x++) {
for (int i = 0; i < 8; i++) {
pixel_value = (bmp[index] >> i) & 0x01;
oled_putpixel(disp, x, y + i, pixel_value);
}
index++;
//oled_send_buffer(disp);
}
//oled_send_buffer(disp);
}
oled_send_buffer(disp);
}
int main(int argc, char **argv)
{
int err;
char channal[32];
struct display_info disp;
if (argc < 2) {
printf("\nUsage:\n%s <I2C bus device node >\n", argv[0]);
return -1;
}
memset(&disp, 0, sizeof(disp));
sprintf(channal, "%s", argv[1]);
disp.address = OLED_I2C_ADDR;
disp.font = font2;
err = oled_open(&disp, channal);
if (err < 0) {
printf("\nERROR: %i, Failed(oled_open)\n\n", err);
} else {
err = oled_init(&disp);
if (err < 0) {
printf("\nERROR: %i, Failed(oled_init)\n\n", err);
} else {
printf("---------start--------\n");
oled_clear(&disp);
displayBmp(&disp, bmp_data);
delay(5000);
oled_clear(&disp);
printf("----------end---------\n");
}
}
return 0;
}
显示效果如下:
如果有多图片循环切换,可以到达一些动画效果,就是刷新率比较感人。
1000032190