关于OLED屏幕的驱动,在之前我已经写过很多篇博文:
IIC 协议 和 OLED_oled iic-CSDN博客
香橙派配合IIC驱动OLED & 使用SourceInsight解读源码_香橙派5 驱动屏幕-CSDN博客
这两篇博文都是通过模拟或调用IIC协议来使用C语言驱动OLED屏幕,现在在树莓派中我尝试了和香橙派类似的方法,我发现了一些问题:香橙派中使用了wiringPi库提供的Oled测试代码并进行修改,其中包含了“font.h”和“oled.h”这两个头文件,但是我已经忘记了我当时这两个头文件是从哪里来的= =,如果不使用这两个库,在查阅了一些别人的实现后发现,如果纯用C语言结合wiringPi库来驱动OLED本质还是要模拟IIC协议,比较麻烦。
综上,经过一些比较和考量,我决定使用Python Adafruit驱动OLED屏幕。
树莓派的IIC开启和OLED屏幕识别
虽说要使用Python来驱动,但是OLED屏幕底层依然是IIC协议,所以对于树莓派需要先手动开启IIC协议:
IIC开启
- 运行以下代码进入设置界面:
sudo raspi-config
- 选择“Interface Options”:
- 选择“I2c”然后打开:
此时,就可以正常使用IIC了,通过“gpio-readall” 可知:
现在,物理引脚3和5对应的就是SDA.1和SCL.1,其中“.1”代表的就是驱动文件是“/dev/i2c-1”
此时,就可以按照引脚连接OLED屏幕了!
OLED屏幕识别
屏幕连接完成后,进行设备的识别:
- 运行以下命令安装i2c-tools
sudo apt-get install -y i2c-tools //安装IIC工具
- 运行以下命令识别OLED屏幕
sudo i2cdetect -y 1
//“1”代表“i2c-1”
可见,树莓派已经成功识别到了OLED屏幕!
Adafruit_Python_SSD1306 库的下载和使用
根据淘宝的搜索,可以了解到我的OLED屏幕驱动芯片为SSD1306(这也是市面上绝大部分单片机小OLED屏幕的芯片,不过现在最新的好像换代了)。
驱动芯片为SSD1306对应的python库就是“Adafruit_Python_SSD1306"。
以下是安装的代码:
1. pip install Adafruit-GPIO
2. pip install Adafruit-SSD1306
3. git clone https://github.com/adafruit/Adafruit_Python_SSD1306.git
安装完成后,运行例程查看效果:
cd Adafruit_Python_SSD1306/examples/
python3 shapes.py
将shapes.py中的“disp = Adafruit_SSD1306_128_32(rst=RST)”修改为“disp = Adafruit_SSD1306_128_64(rst=RST)”,可以显示得更清晰,具体根据不同的OLED屏幕尺寸来修改
成功显示!
修改例程显示图片
首先,将刚刚测试成功的例程“shapes.py”复制到我的代码路径中:
cp shapes.py /home/pi/mjm_code/oled_camera.py
由于之后我还是想用C语言来调用这个python文件,所以和我之前博文的思路一样,先将不需要的代码删去,再将python程序定义为函数进行测试:
oled_camera.py:
由于我的最终目的是显示实时的视频流,所以我先将这个python代码修改,显示一张图片,后续C语言进行while循环不断来显示当前拍照图片就可以有视频的效果。
# Copyright (c) 2014 Adafruit Industries
# Author: Tony DiCola
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
import timeimport Adafruit_GPIO.SPI as SPI
import Adafruit_SSD1306from PIL import Image
from PIL import ImageDraw
from PIL import ImageFontdef init():# Raspberry Pi pin configuration:RST = 24# Note the following are only used with SPI:DC = 23SPI_PORT = 0SPI_DEVICE = 0# Beaglebone Black pin configuration:
# RST = 'P9_12'
# Note the following are only used with SPI:
# DC = 'P9_15'
# SPI_PORT = 1
# SPI_DEVICE = 0# 128x32 display with hardware I2C:disp = Adafruit_SSD1306.SSD1306_128_64(rst=RST)# 128x64 display with hardware I2C:
# disp = Adafruit_SSD1306.SSD1306_128_64(rst=RST)# Note you can change the I2C address by passing an i2c_address parameter like:
# disp = Adafruit_SSD1306.SSD1306_128_64(rst=RST, i2c_address=0x3C)# Alternatively you can specify an explicit I2C bus number, for example
# with the 128x32 display you would use:
# disp = Adafruit_SSD1306.SSD1306_128_32(rst=RST, i2c_bus=2)# 128x32 display with hardware SPI:
# disp = Adafruit_SSD1306.SSD1306_128_32(rst=RST, dc=DC, spi=SPI.SpiDev(SPI_PORT, SPI_DEVICE, max_speed_hz=8000000))# 128x64 display with hardware SPI:
# disp = Adafruit_SSD1306.SSD1306_128_64(rst=RST, dc=DC, spi=SPI.SpiDev(SPI_PORT, SPI_DEVICE, max_speed_hz=8000000))# Alternatively you can specify a software SPI implementation by providing
# digital GPIO pin numbers for all the required display pins. For example
# on a Raspberry Pi with the 128x32 display you might use:
# disp = Adafruit_SSD1306.SSD1306_128_32(rst=RST, dc=DC, sclk=18, din=25, cs=22)# Initialize library.disp.begin()# Clear display.disp.clear()disp.display()# Create blank image for drawing.
# Make sure to create image with mode '1' for 1-bit color.
# width = disp.width
# height = disp.height
# image = Image.new('1', (width, height))# Get drawing object to draw on image.
# draw = ImageDraw.Draw(image)
# Draw a black filled box to clear the image.
# draw.rectangle((0,0,width,height), outline=0, fill=0)# Load default font.
# font = ImageFont.load_default()def display():# Raspberry Pi pin configuration:RST = 24# Note the following are only used with SPI:DC = 23SPI_PORT = 0SPI_DEVICE = 0# 128x32 display with hardware I2C:disp = Adafruit_SSD1306.SSD1306_128_64(rst=RST)# Initialize library.disp.begin()#打开本地的照片#当前路径测试用,需按照实际情况修改img = Image.open('/home/pi/mjm_code/mjm.png')# 调整图片大小到 128x64img_resized = img.resize((128, 64),Image.LANCZOS)image = img_resized.convert('1')# Display image.disp.image(image)disp.display()if __name__ == '__main__': #写一个main调用face_detect函数来测试init()display()
显示效果
运行“python3 oled_camera.py”:
可见,虽然画面很粗糙,但是成功显示图片!而且由于使用了现成的python库,不需要在麻烦的使用IIC协议写命令,初始化,取模了。
OLED显示实时视频流
现在已经能在OLED上显示一张图片了,再次重复刚刚的思路:
“由于我的最终目的是显示实时的视频流,所以我先将这个python代码修改,显示一张图片,后续C语言进行while循环不断来显示当前拍照图片就可以有视频的效果。”
关于如何拍照并保存本地,和如何使用C语言调用Python,可以看我之前的博文:
树莓派接入USB摄像头并使用fswebcam和mjpg-streamer进行测试_树莓派mjpg-streamer-CSDN博客
基于阿里云平台 通过树莓派实现 1:1人脸识别-CSDN博客
编写C语言调用oled_camera.py
回顾“C语言调用Python的步骤”:
oled_show.c:
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <Python.h>#include "oled_show.h"void oled_init(void)
{Py_Initialize();PyObject *sys = PyImport_ImportModule("sys");PyObject *path = PyObject_GetAttrString(sys, "path");PyList_Append(path, PyUnicode_FromString("."));
}void oled_final(void)
{Py_Finalize();
}void oled_show_init(void) //由于没有返回参数,所以可以直接定义void型
{PyObject *pModule = PyImport_ImportModule("oled_camera"); //加载python文件if (!pModule){PyErr_Print();printf("Error: failed to load module\n");goto FAILED_MODULE; //goto的意思就是如果运行到这里就直接跳转到FAILED_MODULE}PyObject *pFunc = PyObject_GetAttrString(pModule, "init"); //加载python文件中的对应函数if (!pFunc){PyErr_Print();printf("Error: failed to load function\n");goto FAILED_FUNC;}PyObject *pValue = PyObject_CallObject(pFunc, NULL);if (!pValue){PyErr_Print();printf("Error: function call failed\n");goto FAILED_VALUE;}/* 没有返回值,无需调用int result = 0;if (!PyArg_Parse(pValue, "i", &result)) //ace_detect函数返回的是已经经过提取和取证过的置信度score,是个int型,用‘i’表示{PyErr_Print();printf("Error: parse failed");goto FAILED_RESULT;}*/FAILED_RESULT:Py_DECREF(pValue);
FAILED_VALUE:Py_DECREF(pFunc);
FAILED_FUNC:Py_DECREF(pModule);
FAILED_MODULE://无需return
}void oled_show(void) //由于没有返回参数,所以可以直接定义void型
{PyObject *pModule = PyImport_ImportModule("oled_camera"); //加载python文件if (!pModule){PyErr_Print();printf("Error: failed to load module\n");goto FAILED_MODULE; //goto的意思就是如果运行到这里就直接跳转到FAILED_MODULE}PyObject *pFunc = PyObject_GetAttrString(pModule, "display"); //加载python文件中的对应函数if (!pFunc){PyErr_Print();printf("Error: failed to load function\n");goto FAILED_FUNC;}PyObject *pValue = PyObject_CallObject(pFunc, NULL);if (!pValue){PyErr_Print();printf("Error: function call failed\n");goto FAILED_VALUE;}/* 没有返回值,无需调用int result = 0;if (!PyArg_Parse(pValue, "i", &result)) //ace_detect函数返回的是已经经过提取和取证过的置信度score,是个int型,用‘i’表示{PyErr_Print();printf("Error: parse failed");goto FAILED_RESULT;}*/FAILED_RESULT:Py_DECREF(pValue);
FAILED_VALUE:Py_DECREF(pFunc);
FAILED_FUNC:Py_DECREF(pModule);
FAILED_MODULE://无需return
}
oled_show.h:
#ifndef __oled__H
#define __oled__Hvoid oled_init(void);
void oled_final(void);
void oled_show_init(void);
void oled_show(void);#endif
编写C程序调用刚刚的C函数实现实时视频流显示
test_oled_camera.c:
注意,由于此处涉及了拍照,拍照保存的路径和照片的名字需要和oled_camera.py中保持一样,所以此处需要按照需求修改python代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <wiringPi.h>#include "oled_show.h"int main()
{oled_init();oled_show_init();while(1){system("wget http://192.168.2.56:8080/?action=snapshot -O /home/pi/mjm_code/oled_pic.png"); //拍照delay(10);//给一点时间让照片拍出来if(0 == access("/home/pi/mjm_code/oled_pic.png", F_OK)){ //如果照片成功拍到了oled_show();}else{printf("pic not exist!\n");}remove("/home/pi/mjm_code/oled_pic.png");delay(50); //此处决定帧率,delay时间越短帧率越高}oled_final();return 0;
}
编译运行
- 输入以下指令编译:
gcc test_oled_camera.c oled_show.c -I /usr/include/python3.11/ -l python3.11 -lwiringPi
- 输入以下指令运行程序:
./a.out
- 输入以下指令结束运行:
ps -ef|grep 程序名称
kill 进程编号
最终效果
视频中我出现在画框中并朝着屏幕挥手:
可见,虽然帧率和分辨率低的可怕,但是的确实现了使用OLED实时显示视频流的功能!