DongshanPI-D1s开发板使用教程之使用U8g2驾驭SSD1306 OLED(I2C)液晶显示屏

经过 DongshanPI-D1s开发板使用教程之SHT30温湿度传感器(I2C)数据读取DongshanPI-D1s开发板使用教程之使用LCD1602(I2C)液晶显示屏 的学习研究,已经可以使用I2C设备了。

进过进一步的研究,又点亮了SSD1306 OLED(I2C)液晶显示屏,下面给大家分享。
SSD1306 OLED是一种非常常用的自发光的像素点液晶显示屏,通常为128x64和128x32,也有其他规格的。
接口通常为I2C和SPI,非常方便在嵌入式设备上使用。在Arduino的教程中,SSD1306 OLED也是常客。

驱动SSD1306 OLED的方法,也是八仙过海各显神通,有很多人写了很多种不同的操作库。
但最常用、最通用、最好用的,当属U8g2了。
image
U8g2绝对是一个好东西。它是用于嵌入式设备的单色图形库,支持单色 OLED 和 LCD,其中包括但不限于以下控制器:SSD1305、SSD1306、SSD1309、SSD1312、SSD1316、SSD1318、SSD1320、SSD1322、SSD1325、SSD1327、SSD1329、SSD1606、SSD1607、SH1106、SH1107、SH1108、SH1122、T6963、RA8835、LC7981、PCD8544、PCF1107、SH1108、SH1122、T6963、RA8835、LC7981、PCD8544、PCFf。 8812, HX1230, UC1601, UC1604, UC1608, UC1610, UC1611, UC1617, UC1638, UC1701, ST7511, ST7528, ST7565, ST7567, ST7571, ST7586, ST7588, ST75160, ST75256, ST75320, NT7534, ST7920, IST3020, IST3088, IST7920, LD7032, KS0108, KS0713, HD44102, T7932, SED1520, SBN1661, IL3820, MAX7219, GP1287, GP1247, GU800。。。
可以说,常见的嵌入式设备上用的单色OLED和LCD,基本上都支持上了。

那这篇分享中,就使用U8g2来驱动SSD1306 OLED(I2C)液晶显示屏。

一、硬件材料

  1. D1s开发板:DongshanPI-D1s开发板
  2. SSD1306 OLED:0.96寸,分辨率128x64,I2C接口
  3. 连接线:杜邦线若干,需要2.0转2.54线;手头没有转接线,直接用测试钩

SSD1306 OLED(I2C)液晶显示屏:

我使用的:



我手头的这款,带有支架,显得相当的高级。

二、原理图
通过查看DongshanPI-D1s开发板的原理图,了解到I2C接口使用和连接:

在上图核心板的左下角,可以看到:
PE12:SCL
PE13:SDA
需要注意的是,默认系统中,开启的是I2C2。

三、实物接线



需要注意的是,不同厂家的SSD1306 OLED,使用的电压不同。
有的只能使用3.3V,使用5V就会烧掉。我用的这款,是支持3.3V~5V的。
另外,不同厂家不同的版本,VCC、GND、SDA、SCL的顺序也可能不同,一定要注意,以免烧毁。

四、编译固件
参考 DongshanPI-D1s开发板使用教程之SHT30温湿度传感器(I2C)数据读取 进行。
如果已经编译烧录过,则不用重新编译烧录,直接使用即可。

五、I2C设备检测
DongshanPI-D1s开发板启动后,参考 DongshanPI-D1s开发板使用基础文档【编译、烧录、adb、gpio-led、c】 使用OTG接口通过adb连接,再进行下面的操作。

  1. 执行i2cdetect -l 查看当前的I2C控制器:

    可以看到,当前可用的i2c设备为i2c-2
  2. 执行i2cdetect -r -y 2,查看识别的I2C外部设备:

    可以看到,SSD1306 OLED(I2C)的地址0x3c被识别出来了。
    不同厂家的SSD1306 OLED(I2C),设备地址可能不同,请仔细查阅所用设备的说明。
    后续代码中,也要做对应的处理。

如果在列表中,没有看到SSD1306 OLED(I2C)的地址,请仔细检查OLED的I2C的接线是否正常。
如果因为程序错误,导致I2C设备打开后,进入busy状态,那就把开发板断电,然后重新上电启动,再次进行检查。

六、编写代码
U8g2的官方代码库为: olikraus/u8g2: U8glib library for monochrome displays, version 2 (github.com)

先将代码克隆下来:

git clone https://github.com/olikraus/u8g2.git

在U8g2的官方仓库中,有linux-i2c的演示,我们可以直接参考使用,位置如下:

不过,该演示,不能直接编译为DongshanPI-D1s的Tina系统的目标代码,需要做一些处理。

具体处理如下:
文件:sys/linux-i2c/common/linux-i2c.c

...
-#include <i2c/smbus.h>
+// #include <i2c/smbus.h>
...
-int adapter_nr = 0; /* probably dynamically determined */
+int adapter_nr = 2; /* probably dynamically determined */
...
-               if (i2c_smbus_write_i2c_block_data(file, data[0], idx - 1, &data[1]) < 0) {
+               // if (i2c_smbus_write_i2c_block_data(file, data[0], idx - 1, &data[1]) < 0) {
+               if(write(file, data, idx) < 0) {
...

上述代码中的修改,是将i2c_smbus调用注释掉,换成通用的write调用即可。

然后,编写测试代码:
文件:sys/linux-i2c/128x32-oled/oled_demo.c

#include <linux-i2c.h>
#include <u8g2.h>
#include <stdio.h>

#define SSD1306_ADDR  0x3c

u8g2_t u8g2;

int main (void)
{
    u8g2_Setup_ssd1306_i2c_128x64_noname_f(&u8g2, U8G2_R0, u8x8_byte_linux_i2c, u8x8_linux_i2c_delay);

	u8g2_SetI2CAddress(&u8g2, SSD1306_ADDR);

	u8g2_InitDisplay(&u8g2);

	u8g2_SetPowerSave(&u8g2, 0);

	u8g2_ClearBuffer(&u8g2);

	u8g2_SetFont(&u8g2, u8g2_font_smart_patrol_nbp_tr);

	u8g2_SetFontRefHeightText(&u8g2);

	u8g2_SetFontPosTop(&u8g2);

	u8g2_DrawFrame(&u8g2, 0, 0, 128, 16);
	u8g2_DrawRFrame(&u8g2, 0, 16, 128, 48, 5);

	u8g2_SetFont(&u8g2, u8g2_font_unifont_t_chinese1);
	u8g2_SetFontMode(&u8g2, 0);
	u8g2_DrawStr(&u8g2, 3, 1, "u8g2@SSD1306");

	u8g2_SetFont(&u8g2, u8g2_font_siji_t_6x10);
	u8g2_SetFontMode(&u8g2, 1);
	u8g2_DrawGlyph(&u8g2, 128 - 16, 3, 0x0e200);

	// u8g2_DrawStr(&u8g2, 0, 20, "I am a test.");
	u8g2_SetFont(&u8g2, u8g2_font_unifont_t_chinese2);
	u8g2_SetFontMode(&u8g2, 0);
	u8g2_DrawUTF8(&u8g2, 3, 24, "我是东山派Pi-D1s");

	u8g2_DrawUTF8(&u8g2, 3, 48, "我很NB 天下无双");

	u8g2_SendBuffer(&u8g2);

}

简单起见,我直接在128x32-oled目录中编写了代码,你也可以把这个目录拷贝为128x64-oled,再进行编写和后续的操作

在上述代码中:

  • sys/linux-i2c/common/linux-i2c:负责I2C接口数据收发处理,使得底层硬件操作逻辑分离
  • sys/linux-i2c/128x32-oled/oled_demo.c:
    • 初始化I2C接口的ssd1306设备
    • 画框
    • 使用不同的字体,显示不同的内容

关于字体的使用,可以查看我的另外一篇文章: U8g2中的字体应用
这里进行简单说明:

  1. u8g2_font_unifont_t_chinese1、u8g2_font_unifont_t_chinese2为常用的中文字体,但只能显示几百个常用的中文字符,且为UTF-8格式,但都能显示英文字符,不过两者字号不同。
  2. u8g2_font_siji_t_6x10用于显示图标,这里显示的是插电的图标。U8g2中有不少可以显示特殊符号的字体。

U8g2提供了丰富的接口,用于输出字符以及绘制图形处理,具体可以查看: u8g2reference · olikraus/u8g2 Wiki (github.com)

然后要修改一下sys/linux-i2c/128x32-oled/Makefile:


CFLAGS = -g -Wall -I../../../csrc/. -I../common/. 

SRC = $(shell ls ../../../csrc/*.c) $(shell ls ../common/*.c) oled_demo.c 

OBJ = $(SRC:.c=.o) 

oled_demo: $(OBJ) 
	# $(CC) $(CFLAGS) $(LDFLAGS) $(OBJ) -li2c -o oled_demo
	$(CC) $(CFLAGS) $(LDFLAGS) $(OBJ) -o oled_demo

clean:	
	-rm -f $(OBJ) oled_demo


编写完测试代码,就可以准备编译执行了。

七、编译代码并执行
这里需要注意的是,编写、编译、上传、执行,是在不同的环境:

  • 编写:可以在编译固件的环境,也可以在主机环境编写好以后在编译环境编译
  • 编译:在编译固件的环境中编译
  • 上传:在主机环境,通过adb上传到开发板
  • 执行:在开发板上执行

那就先编译代码:

# 设置编译工具路径
export PATH=/sdk/tina-d1-h/prebuilt/gcc/linux-x86/riscv/toolchain-thead-glibc/riscv64-glibc-gcc-thead_20200702/bin/:$PATH

# 编译
cd ssd1306/u8g2/sys/linux-i2c/128x32-oled/
env CC=riscv64-unknown-linux-gnu-gcc make -f Makefile

# 查看结果
ls -lh oled_demo
  -rwxr-xr-x 1 root  13M Nov  6 16:06 oled_demo

编译完成后,在主机上,使用adb上传:

adb push oled_demo /tmp/

因为开发板上,如果没有插SD卡的话,/可用空间不足,所以上传到/tmp/空间使用。

最后,在开发板上执行:

/tmp/oled_demo

image

OLED液晶屏上实际输出如下:

SSD1306 OLED非常好用,在嵌入式设备上,也非常的适用,用途非常的广泛。学会使用,对于后续的开发设计,会有非常大的帮助。
例如,下面就是一个简单的互联网时钟:

1 个赞