I2C是一种串行总线,用来连接多个集成电路设备或芯片,它的连接方式比较简单2条线连接即可,分别由SDA(串行数据线)和SCL(串行时钟线)及上拉电阻组成。
通信原理是通过对SCL和SDA线高低电平时序
的控制,来产生信号传递。空闲时上拉电阻拉高,保持着高电平。
想更多了解I2C相关的原理的话自行骚扰度娘。
之所以先介绍I2C总线,是因为咱们要用到的OLED显示屏幕,它就是使用的I2C总线协议来与单片机通信的。
我这里手上使用的是0.96寸的oled屏幕,是当时买第一块esp8266开发板europa时附带买的,单色屏价格便宜,能达到测试使用的目的就行。
这块屏的分辨率是128*64,上面分布了这么多矩阵排列的发光二极管,我们要在上面绘制线条、图案、字符甚至中文,其实就是点亮相应的像素点二极管。
至于绘制的算法,需要查看显示屏官方资料,分为横向扫描法和纵向扫描法,这里也不展开了。
前面咱们在学习温湿度计的时候,当时已经使用过I2C总线。
Micropython官方 提供的I2C总线的基本通信代码:
from machine import Pin, I2C
# construct an I2C bus
i2c = I2C(scl=Pin(5), sda=Pin(4), freq=100000)
i2c.readfrom(0x3a, 4) # read 4 bytes from slave device with address 0x3a
i2c.writeto(0x3a, '12') # write '12' to slave device with address 0x3a
buf = bytearray(10) # create a buffer with 10 bytes
i2c.writeto(0x3a, buf) # write the given buffer to the slave
此处是把两个GPIO管(5和4)脚当作SCL和SDA线来使用,I2C的通信由这两个管脚完成。I2C接线是4条,另外两条是电源正负。
0.96寸的这款oled屏幕,官方提供了ssd1306
库作为驱动,这个库底层是有多种总线通信的实现方式的,我们使用的I2C总线,所以需要引入下面的库:
from ssd1306 import SSD1306_I2C
如果编译的mpy固件没有包含该库时,可以从mcropython官网上下载ssd1306库
from machine import I2C, Pin
i2c = I2C(scl=Pin(5), sda=Pin(4)) # C1接口对应的SCL=IO5, SDA=IO4
print(i2c.scan())
通过REPL看到日志打印出[60] 或 [61]之类,证明已找到屏幕的从机地址。
from machine import I2C, Pin
from ssd1306 import SSD1306_I2C
i2c = I2C(scl=Pin(5), sda=Pin(4))
oled = SSD1306_I2C(128, 64, i2c, addr=61) # 创建OLED对象,addr地址需要参考上面scan输出的地址
oled.text('Hello World', 0, 20) # 在屏幕(0, 20)坐标上显示文字“Hello World”
oled.show() # 刷新屏幕
这里需要注意的是在初始化ssd1306对象时,要准确地指定分辨率,这样才好在后面指定不同字符的显示位置。
ssd1306继承自framebuffer类,绘制内容时是将定义好的缓存对象刷新(show)到屏幕上。
附:ssd1306的功能函数列表:
import time
from machine import I2C, Pin
from ssd1306 import SSD1306_I2C
oled = None
def check():
global oled
while True:
i2c = I2C(scl=Pin(5), sda=Pin(4))
addr = i2c.scan()
if len(addr):
oled = SSD1306_I2C(128, 64, i2c, addr=addr[0])
break
time.sleep(1)
print('connect oled module to esp8266')
while True:
try:
oled.fill(1)
oled.show()
time.sleep(1)
oled.fill(0)
oled.show()
time.sleep(1)
oled.rect(0, 0, 128, 64, 1)
oled.show()
time.sleep(1)
oled.rect(32, 16, 64, 32, 1)
oled.show()
time.sleep(1)
oled.fill(0) # 清空内容后再单独绘制字符
oled.text("This is a test.", 12, 12)
oled.show()
time.sleep(1)
except:
check()
time.sleep(0.1)
实测效果:
(oled屏幕刷新率有限,手机拍出来有闪烁很正常)
前面提到在屏幕上绘制内容,就是点亮对应的像素点,ssd1306封装的text是不支持显示中文的。
那要是想显示汉字屏幕上面,目前我查到的资料有两种办法
一、刷大佬们提供的带中文字库和驱动的增强型固件
如创客大神提供的方案. 但这个是针对esp32芯片的,我不知esp8266是否也能完美支持。
这个有点考验动手能力了,目前还不敢尝试,等有多余空闲的板子再试。
二、手动取模,根据取模的结果来刷新屏幕显示对应的像素点。
手动取模式使用PCtoLCD2002软件来自行创建需要的汉字的模组字典,显示汉字时,按字典取key刷新到屏幕中
取模软件我就不提供,自行查找就是。
一般别人使用这块屏显示中文时,汉字都是用1616点阵,手动取模时我把字体缩小到1212的点阵,再小中文就识别不到了,要注意配置屏幕的行列刷新方式,这里给个我配置的参考。
配置中取模演示那里多观察下,可以很容易理解各种取模方式的绘制原理,那么代码里面就相应在对应像素点在标记颜色就可以了。
from machine import I2C, Pin
from ssd1306 import SSD1306_I2C
import utime
oled = None
def check():
global oled
while True:
i2c = I2C(scl=Pin(5), sda=Pin(4))
addr = i2c.scan()
if len(addr):
oled = SSD1306_I2C(128, 64, i2c, addr=addr[0])
break
utime.sleep(1)
print('connect oled module to esp8266')
fonts= {
"气": [0x20,0x3F,0x40,0xBF,0x00,0x7F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0x00,0xC0,0x00,0x80,0x80,0x80,0x80,0xA0,0x60,0x20],
"温": [0x0F,0x88,0x4F,0x08,0x0F,0x80,0x5F,0x15,0x35,0x55,0x95,0x3F,0x80,0x80,0x80,0x80,0x80,0x00,0xC0,0x40,0x40,0x40,0x40,0xE0],
"湿": [0x80,0x5F,0x10,0x1F,0x90,0x5F,0x05,0x25,0x15,0x45,0x85,0x3F,0x00,0xC0,0x40,0xC0,0x40,0xC0,0x00,0x20,0x40,0x00,0x00,0xE0],
"度": [0x02,0x7F,0x48,0x7F,0x48,0x4F,0x40,0x5F,0x48,0x44,0x43,0x9C,0x00,0xE0,0x80,0xE0,0x80,0x80,0x00,0xC0,0x40,0x80,0x00,0xE0],
"日": [0x00,0x7F,0x40,0x40,0x40,0x7F,0x40,0x40,0x40,0x40,0x7F,0x40,0x00,0xC0,0x40,0x40,0x40,0xC0,0x40,0x40,0x40,0x40,0xC0,0x40],
"期": [0x48,0x49,0xFD,0x49,0x79,0x49,0x79,0x49,0xFD,0x01,0x49,0x86,0x00,0xE0,0x20,0x20,0xE0,0x20,0x20,0xE0,0x20,0x20,0x20,0x60],
}
def chinese(ch_str, x_axis, y_axis, ch_size=12):
''' 刷单字到屏幕像素点
Args:
ch_str 单字或连接汉字
x_axis,y_axis 定位点
ch_size 单字大小,默认12,最大16
'''
global oled
offset_ = 0
for k in ch_str:
byte_data = fonts[k]
print(fonts[k], "offset=", offset_)
for y in range(0, ch_size):
# 进制转换、补全
a_ = '{:0>8b}'.format(byte_data[y])
b_ = '{:0>8b}'.format(byte_data[y+ch_size])
# 绘制像素点 (按取模软件的行列式方式)
for x in range(0, 8):
oled.pixel(x_axis + offset_ + x, y + y_axis, int(a_[x]))
oled.pixel(x_axis + offset_ + x + 8, y + y_axis, int(b_[x]))
offset_ += ch_size
check()
chinese('气温',0,0)
chinese('温度',0,20)
chinese('日期',0,40)
oled.show()
这里打错一个字,中间计划放“湿度”的,为下一步引入温湿度数据做准备。