关于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 time
import Adafruit_GPIO.SPI as SPI
import Adafruit_SSD1306
from PIL import Image
from PIL import ImageDraw
from PIL import ImageFont
def init():
# Raspberry Pi pin configuration:
RST = 24
# Note the following are only used with SPI:
DC = 23
SPI_PORT = 0
SPI_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 = 23
SPI_PORT = 0
SPI_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')
# 调整图片大小到 128x64
img_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__H
void 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实时显示视频流的功能!