刚刚开学有一些空闲的时间,加上手头上刚好有一个吃灰派和一个SSD1315
显示屏,于是研究了一下
先放一个成品图:
这块屏幕是淘宝上买的SSD1315 0.96 OLED
屏,只提供了I2C
接口,店家宣称兼容SSD1306
(?)
以此为关键词在百度上可以搜索到许多直接使用I2C
驱动的样例。但是,我最近在更新eeprom
的时候发现boot
目录下的overlay
中提供了一个ssd1306
的设备驱动,于是我想通过驱动的形式加载这块显示屏,绕过手动通过I2C
发送初始化指令的过程
这一个驱动将提供一个framebuffer
设备,可以通过向/dev/fbX
写入二进制的图像数据来控制显示内容,也可以将设备终端映射到这块设备上
首先开启相关内核模块,修改/boot/config.txt
:
1 2 3 4
| dtoverlay=ssd1306,inverted
dtparam=i2c_baudrate=400000
|
保存后重启可以发现/dev
目录下多了一个fb1
设备(fb0
是我外接的显示器)
我们可以使用fbset
指令查看这个设备的详细信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| ➜ ~ fbset -i -fb /dev/fb1
mode "128x64" geometry 128 64 128 64 1 timings 0 0 0 0 0 0 0 rgba 1/0,1/0,1/0,0/0 endmode
Frame buffer device information: Name : Solomon SSD1307 Address : 0x4900f000 Size : 1024 Type : PACKED PIXELS Visual : MONO10 XPanStep : 0 YPanStep : 0 YWrapStep : 0 LineLength : 16 Accelerator : No
|
可以发现这里加载的是SSD1307
的驱动(SSD1315
-> SSD1306
-> SSD1307
:cry:)无论如何,能够互相兼容即可
首先尝试在屏幕中间输出一个Hello World
,这里使用ImageMagick
提供的convert
工具将字符转换为图像:
1
| convert -size 128x64 -depth 1 xc:white -fill black -font "Source-Code-Pro" -pointsize 14 -annotate +0+32 "Hello World" mono:- > /dev/fb1
|
(这里就不放截图了)
这个工具并不是特别的好用:首先是字体不便调整,其次不方便排版。所以不妨手写一个吧!
首先生成字库:
相关的工具有很多,我找到了一个在线工具:LCD/OLED字模提取软件,ASCII字符8*16点阵字库。需要注意”取模方式”参数:横向8点右高位(在配置驱动的时候使用了inverted
参数)
这个工具生成了8*16
的点阵ASCII
字符画,每个字符有16
个元素对应16
行,每行一个8
位0/1
数据表示点阵
其次编写操作/dev/fb1
的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| extern uint8_t fontData[]; extern std::string statInfo();
static volatile bool keepRunning = true;
void sig_handler(int sig) { if (sig == SIGINT) keepRunning = false; }
int main(int argc, char** argv) { signal(SIGINT, sig_handler);
auto fd = open("/dev/fb1", O_RDWR);
struct fb_var_screeninfo screenInfo{}; ioctl(fd, FBIOGET_VSCREENINFO, &screenInfo);
auto width = screenInfo.xres; auto widthChar = width / 8; auto height = screenInfo.yres;
std::cout << "Bits/Pixel = " << screenInfo.bits_per_pixel << std::endl; std::cout << "Width = " << width << std::endl; std::cout << "Height = " << height << std::endl;
auto fontChar = [=](char c, unsigned line) { if (c < ' ' || c > '~' || line >= 16) return static_cast<uint8_t>(0); else return fontData[(c - ' ') * 16 + line]; };
auto data = reinterpret_cast<uint8_t*>(mmap(nullptr, width * height, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)); while (keepRunning) { auto outputString = statInfo(); std::cout << "string to print: " << outputString << std::endl;
auto idx = 0; auto finish = [=](int idx) { return idx >= outputString.length(); };
for (auto row = 0; row < height; row += 16) for (auto col = 0; col < widthChar; col++, idx++) for (auto i = 0; i < 16; i++) data[(row + i) * widthChar + col] = fontChar(finish(idx) ? ' ' : outputString[idx], i);
std::this_thread::sleep_for(std::chrono::seconds(1)); }
munmap(data, width * height); close(fd); return 0; }
|
至于要显示的具体内容,我主要获取了三个参数:时间、CPU
占用、内存占用。我通过popen
来获取执行命令的结果来拼接参数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| void runCmd(const std::string& command, std::ostringstream& outputStream) { auto fd = popen(command.c_str(), "r"); if (fd == nullptr) return;
char buffer[128]; while (fgets(buffer, sizeof(buffer), fd)) { auto inputLine = buffer; outputStream << inputLine; }
pclose(fd); }
std::string statInfo() { std::ostringstream info; info << "< Rasp Monitor >";
std::string cmdGetDateTime; cmdGetDateTime = R"lit(date '+ %m-%d %H:%M:%S ' | awk '{printf(" %s %s ", $1, $2)}')lit"; runCmd(cmdGetDateTime, info);
std::string cmdGetCPU; cmdGetCPU = R"lit(top -bn1 | grep "Cpu(s)" | sed "s/.*, *\([0-9.]*\)%* id.*/\1/" | awk '{printf("CPU: %10.2f%%", 100-$1)}')lit"; runCmd(cmdGetCPU, info);
std::string cmdGetMem; cmdGetMem = R"lit(awk '/MemAvailable/{free=$2} /MemTotal/{total=$2} END {printf("MEM: %10.2f%%", 100-(free*100)/total)}' /proc/meminfo)lit"; runCmd(cmdGetMem, info);
return info.str(); }
|
编译后用nohup
丢到后台执行即可
最后,这里这个绘制图像的过程个人感觉过于简陋,如果有空的话可以研究一下专为Arduino
设计的字符模块:u8g2,其自带多种排版输出功能(图标/文字)
== 完 ==