经过 DongshanPI-D1s开发板使用教程之SHT30温湿度传感器(I2C)数据读取 及 DongshanPI-D1s开发板使用教程之使用LCD1602(I2C)液晶显示屏 的学习研究,已经可以使用I2C设备了。
进过进一步的研究,又点亮了SSD1306 OLED(I2C)液晶显示屏,下面给大家分享。
SSD1306 OLED是一种非常常用的自发光的像素点液晶显示屏,通常为128x64和128x32,也有其他规格的。
接口通常为I2C和SPI,非常方便在嵌入式设备上使用。在Arduino的教程中,SSD1306 OLED也是常客。
驱动SSD1306 OLED的方法,也是八仙过海各显神通,有很多人写了很多种不同的操作库。
但最常用、最通用、最好用的,当属U8g2了。
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)液晶显示屏。
一、硬件材料
- D1s开发板:DongshanPI-D1s开发板
- SSD1306 OLED:0.96寸,分辨率128x64,I2C接口
- 连接线:杜邦线若干,需要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连接,再进行下面的操作。
- 执行
i2cdetect -l
查看当前的I2C控制器:
可以看到,当前可用的i2c设备为i2c-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中的字体应用
这里进行简单说明:
- u8g2_font_unifont_t_chinese1、u8g2_font_unifont_t_chinese2为常用的中文字体,但只能显示几百个常用的中文字符,且为UTF-8格式,但都能显示英文字符,不过两者字号不同。
- 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
OLED液晶屏上实际输出如下:
SSD1306 OLED非常好用,在嵌入式设备上,也非常的适用,用途非常的广泛。学会使用,对于后续的开发设计,会有非常大的帮助。
例如,下面就是一个简单的互联网时钟: