python 显示模块_用Python控制硬件19-OLED128x64显示模块

OLED显示屏在消费类产品中非常流行,使用有机发光二极管技术,每个像素只在需要时自发光,而非传统的背光方式。下面演示用MCUSH/Python控制128x64显示模块。

这款显示器由SSD1306芯片驱动,有传统的I2C和兼容SPI两种控制方式。I2C方式仅需要将SDA/SCL连接至PA0/PA1。

SPI方式需要MOSI/SCK/CS连接至PA1/PA2/PA3,连接RST(模块复位)至PA4,连接DC(数据命令模式切换)至PA5。

和上次控制MAX7219点阵模块流程类似:先要初始化OLED控制对象,然后用该对象创建虚拟画布,这样导入了画点、画线、贴图和文字等接口。

根据数据手册,整屏64行按8行一组分成8个Pages,纵向8个像素作为一个字节整体控制,于是后续的缓存策略都是如此:8行,每行128字节。

先将两种控制方式共用的API提取出来,准备一个基础类:

class Ssd1306_Common():

current_page = None

current_x = None

def cmd( self, b ):

raise NotImplementedError

def dat( self, b ):

raise NotImplementedError

def fill( self, pattern ):

# 初始化时整屏填充固定模式

for p in range((self.height-1)/8+1):

self.cmd(0xb0+p)

self.cmd(0x01)

self.cmd(0x10)

for x in range(self.width):

self.dat(pattern)

def write_mem( self, page, x, mem ):

# 运行中写入指定位置缓存数据

# 加了点优化:当顺序位置写入时,不用重复发送位置坐标,提高效率

if page != self.current_page:

self.cmd(0xb0+page)

self.current_page = page

if x != self.current_x:

self.cmd(((x&0xf0)>>4)|0x10)

self.cmd((x&0x0f)|0x00)

self.current_x = x

self.dat( mem )

self.current_x += 1

def init(self):

# 寄存器初始化,参考数据手册

self.cmd(0xae) # turn off oled panel

self.cmd(0x00) # set low column address

self.cmd(0x10) # set high column address

self.cmd(0x40) # set start line address Set Mapping RAM Display Start Line (0x00~0x3F)

self.cmd(0x81) # set contrast control register

self.cmd(0xcf) # Set SEG Output Current Brightness

self.cmd(0xa1) # Set SEG/Column Mapping

self.cmd(0xc8) # Set COM/Row Scan Direction

self.cmd(0xa6) # set normal display

self.cmd(0xa8) # set multiplex ratio(1 to 64)

self.cmd(0x3f) # 1/64 duty

self.cmd(0xd3) # set display offset Shift Mapping RAM Counter (0x00~0x3F)

self.cmd(0x00) # not offset

self.cmd(0xd5) # set display clock divide ratio/oscillator frequency

self.cmd(0x80) # set divide ratio, Set Clock as 100 Frames/Sec

self.cmd(0xd9) # set pre-charge period

self.cmd(0xf1) # Set Pre-Charge as 15 Clocks & Discharge as 1 Clock

self.cmd(0xda) # set com pins hardware configuration

self.cmd(0x12) #

self.cmd(0xdb) # set vcomh

self.cmd(0x40) # Set VCOM Deselect Level

self.cmd(0x20) # Set Page Addressing Mode (0x00/0x01/0x02)

self.cmd(0x02) #

self.cmd(0x8d) # set Charge Pump enable/disable

self.cmd(0x14) # set(0x10) disable

self.cmd(0xa4) # Disable Entire Display On (0xa4/0xa5)

self.cmd(0xa6) # Disable Inverse Display On (0xa6/a7)

self.cmd(0xaf) # turn on oled panel

其中两个函数cmd/dat分别用来发送命令指令和数据指令,根据SPI/I2C两种方式来实现。

I2C模式下的继承方式:

class Ssd1306_I2C(Ssd1306_Common):

def __init__( self, controller, scl=None, sda=None ):

self.controller = controller

self.controller.i2c_init( 0x3C, scl, sda, delay=5 )

self.init()

def cmd( self, b ):

self.controller.i2c( [0x00, b] ) # 引导符0x00表示后续是命令

def dat( self, b ):

self.controller.i2c( [0x40, b] ) # 引导符0x40表示后续是数据

class OLED128x64_I2C(Ssd1306_I2C):

width, height = 128, 64

SPI模式下的继承方式:

class Ssd1306_SPI(Ssd1306_Common):

def __init__( self, controller, rst=None, dc=None, sdo=None, sdi=None, sck=None, cs=None ):

self.controller = controller

self.controller.spi_init( sdo=sdo, sdi=sdi, sck=sck, cs=cs, delay=5 )

self.rst_pin = rst

self.dc_pin = dc

self.dc = None # 缓存当前DC脚状态,在后续发送cmd/dat时不用发送无效指令,提高效率

self.controller.pinOutputHigh( [rst, dc] )

self.reset()

self.init()

def reset( self ):

self.controller.pinClr( self.rst_pin )

time.sleep(0.1)

self.controller.pinSet( self.rst_pin )

def cmd( self, d ):

if self.dc != 0:

self.controller.pinClr( self.dc_pin )

self.dc = 0

self.controller.spi( [d] )

def dat( self, d ):

if self.dc != 1:

self.controller.pinSet( self.dc_pin )

self.dc = 1

self.controller.spi( [d] )

class OLED128x64_SPI(Ssd1306_SPI):

width, height = 128, 64

至此可以创建OLED128x64_XXX对象,调用写缓存write_mem函数来控制显示内容了,但这还不够,需要和Canvas画布类结合起来,调用更高阶的接口。

看看MCUSH库提供的Canvas类(位于misc/Canvas.py)的接口:

class Canvas():

def __init__( self, display ):

self.display = display

self.width = display.width

self.height = display.height

def flush( self, force=False ):

# flush screen

raise NotImplementedError

def setPixel( self, x, y, color=None, flush=True ):

# set pixel

raise NotImplementedError

def clrScr( self, flush=True ):

...

def drawVLine( self, x, y0, y1, color=None, flush=True ):

...

def drawHLine( self, y, x0, x1, color=None, flush=True ):

...

def drawRectangle( self, x0, y0, x1, y1, color=None, fill=True, flush=True ):

...

def drawLine( self, x0, y0, x1, y1, color=None, flush=True ):

...

def drawBitmap( self, x, y, bitmap, mode='normal', flush=True ):

...

def drawString( self, x, y, string, mode='normal', font=None, flush=True ):

...

必须实现flush和setPixel两个基础函数,才能调用Canvas提供的其它2D图像接口:

import Canvas as _Canvas

class Canvas(_Canvas.Canvas):

def __init__( self, display ):

self.display = display

self.width = display.width

self.height = display.height

self.pages = (display.height-1)/8+1

self.buffer = [ [0] * self.width for i in range(self.pages) ] # 缓存区

self.display.fill( 0 )

self.dirty = [0] * self.pages # 标注各个Page/X的8个像素是否“脏”,需要重绘

def flush( self, force=False ):

for p in range(self.pages):

for x in range(self.width):

if force or (self.dirty[p] & (1<

self.display.write_mem( p, x, self.buffer[p][x] )

self.dirty[p] &= ~(1<

def setPixel( self, x, y, color=None, flush=True ):

# check if it's out of range

if x < 0 or x >= self.width:

return

if y < 0 or y >= self.height:

return

page = y/8

byte_mask = 1 << (y%8)

byte_index = x

if color is None:

color = 1

if color:

self.buffer[page][byte_index] |= byte_mask

else:

self.buffer[page][byte_index] &= ~byte_mask

self.dirty[page] |= 1<

if flush:

self.flush()

所有基础环境准备就绪,下面编写调用脚本:

import time

from mcush import *

s = Mcush.Mcush('/dev/ttyACM0')

# Ssd1306模块已经集成了上文的基础环境

oled = Ssd1306.OLED128x64_SPI(s, rst='0.4', dc='0.5') # SPI接口的OLED

#oled = Ssd1306.OLED128x64_I2C(s) # I2C接口的OLED

cv = Ssd1306.Canvas(oled)

# 绘制边框

cv.drawRectangle( 0, 0, cv.width-1, cv.height-1, color=1, fill=False )

# 居中显示两行文字

line1 = 'Hardware control'

line2 = 'with Python'

w1, h = cv.getStringRenderSize(line1)

w2, h = cv.getStringRenderSize(line2)

cv.drawString( (cv.width-w1)/2, (cv.height-h)/2-2, line1 )

cv.drawString( (cv.width-w2)/2, (cv.height+h)/2+2, line2 )

# 循环显示转动图标

while True:

for i in range(7):

x, y = 10, 10 # 图标左上角位置

cv.drawRectangle( x, y, x+7, y+7, color=0, fill=True, flush=False )

cv.drawLine( x+i, y, x+7-i, y+7, flush=False )

cv.drawLine( x+i+1, y, x+7-i-1, y+7, flush=False )

cv.drawLine( x+7, y+i, x+0, y+7-i, flush=False )

cv.drawLine( x+7, y+i+1, x+0, y+7-i-1, flush=False )

cv.flush()

#time.sleep(0.1)

运行效果如下:v2-b3600cd900a1e472f2abf0dfa5f3ab03.jpeg用SPI/I2C控制OLED12864显示模块https://www.zhihu.com/video/1167188182536949760

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值