12864 OLED屏幕因其体积小巧、功耗低、显示清晰等特点,常被用于树莓派等嵌入式设备的可视化项目中。其中IIC接口的0.96寸SSD1306协议屏幕尤为常见,本文将详细介绍如何在树莓派上通过Python驱动该屏幕,并实现GIF动画的显示功能。
1. 硬件准备 1.1 所需硬件
树莓派(所有型号应该都是一样的)
IIC接口的0.96寸12864 OLED屏幕(SSD1306协议)
杜邦线若干
1.2 硬件连接 12864 OLED屏幕通过IIC协议与树莓派通信,需连接4根线:
VCC:接树莓派3.3V或引脚
GND:接树莓派GND引脚
SDA:接树莓派GPIO2(SDA1)引脚
SCL:接树莓派GPIO3(SCL1)引脚
1.3 启用IIC接口 打开终端,输入以下命令进入配置界面:
依次选择 Interface Options → I2C → Yes 启用 IIC 接口。
2. 关键代码实现 2.1 安装依赖库 驱动12864屏幕显示需用到luma.oled(驱动 OLED 屏幕)。此外,使用Pillow库实现图像处理,安装命令如下:
1 2 3 4 5 pip3 install luma.oled pip3 install pillow
2.2 代码实现 2.2.1 初始化硬件 通过i2c函数初始化 I2C 通信接口,指定通信端口为port=1(树莓派常用 I2C 端口),设备地址为0x3C(OLED 屏幕的 I2C 地址,需与实际硬件匹配)。基于初始化的 I2C 接口,使用ssd1306驱动创建 OLED 设备实例(注释提示可根据屏幕型号替换为ssd1325、ssd1331、sh1106等其他协议驱动)。通过sys.argv[1]从命令行参数中读取需要显示的 GIF 文件路径,代码如下:
1 2 3 4 serial = i2c(port=1 , address=0x3C ) device = ssd1306(serial) imagepath = str (sys.argv[1 ])
2.2.2 图像处理 使用Pillow库用于对图像进行缩放和布局调整,以适配128x64分辨率的显示区域(如 12864 OLED 屏幕)。首先计算图像的宽高比,为选择合适的缩放方式;之后根据目标宽度或高度,按原图像宽高比等比例计算新宽度和高度缩放,使图像高度符合目标值;最后创建一个 128x64 的透明画布,将缩放后的图像居中粘贴到画布上,确保图像在目标显示区域中处于居中位置,最终输出可直接用于显示的图像。相关代码如下:
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 WIDTH = 128 HEIGHT = 64 def resize_image_by_height (image, target_height ): width, height = image.size new_width = int (width * target_height / height) resized_image = image.resize((new_width, target_height)) return resized_image def resize_image_by_width (image, target_width ): width, height = image.size new_height = int (target_width * height / width) resized_image = image.resize((target_width, new_height)) return resized_image def AspectRatio (image ): width, height = image.size return width / height def resize_and_paste (middle_img ): transparent_image = Image.new('RGBA' , (128 , 64 ), color = (0 , 0 , 0 , 0 )) middle_size = middle_img.size x = (transparent_image.width - middle_size[0 ]) // 2 y = (transparent_image.height - middle_size[1 ]) // 2 transparent_image.paste(middle_img, (x, y)) return transparent_image
2.2.3 显示动画 首先打开目标 GIF 文件,通过ImageSequence.Iterator遍历 GIF 的所有帧,并将帧复制到frames列表中。之后调用上面的函数将gif当前帧转换为设备支持的显示模式后,即可通过device.display显示在 OLED 屏幕上。最后使用死循环,可确保GIF动画持续播放。代码如下:
1 2 3 4 5 6 7 8 9 while True : with Image.open (imagepath) as gif: frames = [frame.copy() for frame in ImageSequence.Iterator(gif)] for frame in frames: if AspectRatio(frame) > 2 : resized_image = resize_image_by_width(frame, WIDTH) else : resized_image = resize_image_by_height(frame, HEIGHT) device.display(resize_and_paste(resized_image).convert(device.mode))
2.3 完整代码 完整代码如下:
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 from luma.core.interface.serial import i2c from luma.oled.device import ssd1306 from PIL import Image, ImageSequenceimport sysWIDTH = 128 HEIGHT = 64 def resize_image_by_height (image, target_height ): width, height = image.size new_width = int (width * target_height / height) resized_image = image.resize((new_width, target_height)) return resized_image def resize_image_by_width (image, target_width ): width, height = image.size new_height = int (target_width * height / width) resized_image = image.resize((target_width, new_height)) return resized_image def AspectRatio (image ): width, height = image.size return width / height def resize_and_paste (middle_img ): transparent_image = Image.new('RGBA' , (128 , 64 ), color = (0 , 0 , 0 , 0 )) middle_size = middle_img.size x = (transparent_image.width - middle_size[0 ]) // 2 y = (transparent_image.height - middle_size[1 ]) // 2 transparent_image.paste(middle_img, (x, y)) return transparent_image def main (): __version__ = 1.0 print ("current version:" , __version__) serial = i2c(port=1 , address=0x3C ) device = ssd1306(serial) imagepath = str (sys.argv[1 ]) while True : with Image.open (imagepath) as gif: frames = [frame.copy() for frame in ImageSequence.Iterator(gif)] for frame in frames: if AspectRatio(frame) > 2 : resized_image = resize_image_by_width(frame, WIDTH) else : resized_image = resize_image_by_height(frame, HEIGHT) device.display(resize_and_paste(resized_image).convert(device.mode)) print ("next frame" ) print ("replay" ) if __name__ == "__main__" : try : main() except KeyboardInterrupt: pass
在终端中执行以下命令运行程序,屏幕会循环显示 GIF 动画,按Ctrl+C可停止程序:
1 python3 oled_gif.py test.gif
最后给一个使用12864显示摄像头画面的完整代码:
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 from luma.core.interface.serial import i2c from luma.oled.device import ssd1306 import cv2from PIL import Imagecam = cv2.VideoCapture(0 ) cam.set (cv2.CAP_PROP_FRAME_WIDTH, 320 ) cam.set (cv2.CAP_PROP_FRAME_HEIGHT, 240 ) serial = i2c(port=1 , address=0x3C ) device = ssd1306(serial) while True : ret, image = cam.read() cv2.imshow(("video" ), image)知识产权师 print (image.shape) image = cv2.resize(image, (128 , 96 )) gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) h, w = gray_image.shape[:2 ] start_y = h // 6 end_y = (h * 5 ) // 6 gray_image = gray_image[start_y:end_y, :] gray_image = cv2.flip(gray_image, 1 ) cv2.imshow(("gray" ),gray_image) pil_image = Image.fromarray(cv2.cvtColor(gray_image, cv2.COLOR_BGR2RGB)) print (pil_image.size) device.display(pil_image.convert(device.mode)) k = cv2.waitKey(1 ) if k != -1 : break cam.release() cv2.destroyAllWindows()