Bipes-Blockly项目二次开发/硬件功能-0.96寸OLED(十)
bash
距离上次更新已经过去好几个月了,不知道要写什么内容,灵感缺失。忽然想到之前找我开发的老板给我了一套硬件,一直放着也是太浪费,所以我打算开始写硬件相关的内容,依旧废话不多说,请看VCR,以及效果图。
申明:本人不是嵌入式工程师,有些讲解有误区可以在评论区纠正下,谢谢。
本期:带来的是0.96寸OLED的使用教程,目的完成在显示屏上显示:中,英,数,表情包(未实现)。
广告:需要二开Bipes,Scratch,blockly可以找我。
项目地址:https://maxuecan.github.io/Bipes/index.html
VCR
video(video-SDoLFcZ0-1782732489743)(type-csdn)(url-[live.csdn.net/v/embed/533...](https://link.juejin.cn?target=https%3A%2F%2Flive.csdn.net%2Fv%2Fembed%2F533006)(image-https%3A%2F%2Fi-blog.csdnimg.cn%2Fimg_convert%2F15964aa9414ddc732d88d293367ae2b1.jpeg)(title-0.96%25E5%25AF%25B8oled%25E4%25BD%25BF%25E7%2594%25A8%25E6%2595%2599%25E7%25A8%258B "https://live.csdn.net/v/embed/533006)(image-https://i-blog.csdnimg.cn/img_convert/15964aa9414ddc732d88d293367ae2b1.jpeg)(title-0.96%E5%AF%B8oled%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B"))
效果图

Python代码解析
代码是参考别人的项目,以及一些博客的讲解,最终将这些整合成我想要的,一共有4个文件,案例在micorpython/oled。 后面我发现Bipes原项目有给出oled的案例,有兴趣的童鞋可以去观看下,案例在ui/pylibs。 ssd1306.py:控制的OLED 显示屏 oled.py:oled类库 font.py:16x16字体库 main.py:主程序 工具:树莓派开发板,0.96寸oled
ssd1306.py
python
import framebuf
from micropython import const
# 寄存器定义
SET_CONTRAST = const(0x81)
SET_ENTIRE_ON = const(0xA4)
SET_NORM_INV = const(0xA6)
SET_DISP = const(0xAE)
SET_MEM_ADDR = const(0x20)
SET_COL_ADDR = const(0x21)
SET_PAGE_ADDR = const(0x22)
SET_DISP_START_LINE = const(0x40)
SET_SEG_REMAP = const(0xA0)
SET_MUX_RATIO = const(0xA8)
SET_COM_OUT_DIR = const(0xC0)
SET_DISP_OFFSET = const(0xD3)
SET_COM_PIN_CFG = const(0xDA)
SET_DISP_CLK_DIV = const(0xD5)
SET_PRECHARGE = const(0xD9)
SET_VCOM_DESEL = const(0xDB)
SET_CHARGE_PUMP = const(0x8D)
class SSD1306:
def __init__(self, width, height, external_vcc):
self.width = width
self.height = height
self.external_vcc = external_vcc
self.pages = height // 8
self.poweron()
self.init_display()
def init_display(self):
for cmd in (
SET_DISP | 0x00, # 关闭显示
# 地址设置
SET_MEM_ADDR, 0x00, # 水平寻址模式
# 分辨率和布局
SET_DISP_START_LINE | 0x00,
SET_SEG_REMAP | 0x01, # 段重映射 (列addr127 映射到 SEG0)
SET_MUX_RATIO, self.height - 1,
SET_COM_OUT_DIR | 0x08, # COM输出方向 (从COM[N]扫描到COM0)
SET_DISP_OFFSET, 0x00,
SET_COM_PIN_CFG, 0x02 if self.height == 32 else 0x12,
# 时间 和 行使 方案
SET_DISP_CLK_DIV, 0x80,
SET_PRECHARGE, 0x22 if self.external_vcc else 0xf1,
SET_VCOM_DESEL, 0x30, # 0.83*Vcc
# 显示
SET_CONTRAST, 0xFF, # 最大限度
SET_ENTIRE_ON, # 输出遵循RAM内容
SET_NORM_INV, # 不进行反向
# 电荷泵
SET_CHARGE_PUMP, 0x10 if self.external_vcc else 0x14,
SET_DISP | 0x01
): # 开始
self.write_cmd(cmd)
self.fill(0)
self.show()
def fill(self, col):
# FrameBuffer.fill(c)
# 用指定的颜色填充整个 FrameBuffer
self.framebuf.fill(col)
def show(self):
x0 = 0
x1 = self.width - 1
if self.width == 64:
# 宽度为64像素的显示器偏移了32
x0 += 32
x1 += 32
self.write_cmd(SET_COL_ADDR)
self.write_cmd(x0)
self.write_cmd(x1)
self.write_cmd(SET_PAGE_ADDR)
self.write_cmd(0)
self.write_cmd(self.pages - 1)
self.write_framebuf()
def pixel(self, x, y, col):
self.framebuf.pixel(x, y, col)
def text(self, string, x, y, col=1):
self.framebuf.text(string, x, y, col)
class SSD1306_I2C(SSD1306):
def __init__(self, width, height, i2c, addr=0x3c, external_vcc=False):
self.i2c = i2c
self.addr = addr
self.temp = bytearray(2)
self.buffer = bytearray((height // 8) * width + 1)
self.buffer[0] = 0x40
# framebuf.FrameBuffer(buffer, width, height, format, stride=width, /)
# buffer:是一个具有缓冲协议的对象,该协议必须足够大以包含由 FrameBuffer 的宽度、高度和格式定义的每个像素
# width:是FrameBuffer的宽度
# height:是FrameBuffer的高度
# format:指定了FrameBuffer中使用的像素类型;允许的值列在下面的常量下。这些设置用于编码颜色值的位数以及这些位在缓冲区中的布局。在将颜色值 c 传递给方法的情况下,c 是一个小整数,其编码取决于 FrameBuffer 的格式
# stride:是FrameBuffer中每条水平像素线之间的像素数。这默认为宽度,但在另一个更大的 FrameBuffer 或屏幕中实现 FrameBuffer 时可能需要调整。所述缓冲器的大小必须容纳增加的步长大小
self.framebuf = framebuf.FrameBuffer1(memoryview(self.buffer)[1:], width, height)
super().__init__(width, height, external_vcc)
def poweron(self):
pass
def write_cmd(self, cmd):
self.temp[0] = 0x80 # Co=1, D/C#=0
self.temp[1] = cmd
# I2C.writeto(addr, buf, stop=True, /)
# 将buf 中的字节写入addr指定的从站。如果在从buf写入一个字节后收到 NACK,则不会发送剩余的字节。如果stop为真,则在传输结束时生成 STOP 条件,即使收到 NACK 也是如此。该函数返回接收到的 ACK 数
self.i2c.writeto(self.addr, self.temp)
# I2C.writeto_mem(addr, memaddr, buf, *, addrsize=8)
# 从memaddr指定的内存地址开始,将buf写入addr指定的从站。参数addrsize以位为单位指定地址大小
# self.i2c.writeto_mem(self.addr, 0x00, bytes([cmd]))
def write_framebuf(self):
# 使用单个I2C事务来释放帧缓冲区以支持
# 硬件I2C接口。
self.i2c.writeto(self.addr, self.buffer)
oled.py
bash
oled.py 我对代码进行了封装,通过text,text_cn,text_scroll显示中,英,数。
text:只能显示8x8的中,英,数。
text_cn和text_scroll:用于显示16x16中,英,数(这些都需要通过取模生成https://led.baud-dance.com/,将生成的数据复制到font.py文件里面)
from machine import Pin, I2C
from ssd1306 import SSD1306_I2C
from font import fonts
import time
# 0.96寸 OLED 128x64 I2C驱动
# I2C配置
prot = 0
scl = 1
sda = 0
freq = 400000
# OLED配置
width = 128
height = 64
# 配置参数
char_size = 16
scroll_speed = 1
class OLED:
def __init__(self):
self.i2c = self.init_i2c(prot, scl, sda, freq)
self.oled = self.init_oled()
self.is_scroll = True
self.scroll_offset = 128
def init_i2c(self, id, scl, sda, freq=400000):
# 初始化i2c接口
# I2C(id, *, scl, sda, freq=400000) 是一种用于设备间通信的两线协议
# id: 标识特定的 I2C 外设。允许的值取决于特定的端口/板
# scl: 时钟线,是一个 pin 对象,指定用于 SCL 的 pin
# sda: 数据线,是一个 pin 对象,指定用于 SDA 的 pin
# freq:是一个整数,是 SCL 时钟频率
_i2c = I2C(id, scl=Pin(scl), sda=Pin(sda), freq=freq)
return _i2c
def init_oled(self):
# 初始oled
_oled = SSD1306_I2C(width, height, self.i2c)
return _oled
def clear(self):
# 清屏
self.oled.fill(0)
def show(self):
# 显示
self.oled.show()
def text(self, string, x, y, font_size=8):
# 显示8x8字符串,英文、数字
if font_size != 8 and font_size != 16 and font_size != 24 and font_size != 32:
return
if font_size == 8:
self.oled.text(string, x, y)
return
def text_cn(self, string, x, y, font_size=16):
# 显示16x16字符串,英文、数字
count = 0
for str in string:
if str in fonts:
x = count * font_size
count += 1
char_data = fonts[str]
for col in range(16):
byte1 = char_data[col * 2]
byte2 = char_data[col * 2 + 1]
for row in range(8):
if byte1 & (1 << row):
self.oled.pixel(x + col, y + row, 1)
for row in range(8):
if byte2 & (1 << row):
self.oled.pixel(x + col, y + 8 + row, 1)
def draw_text(self, string, scroll_offset, y):
self.oled.fill(0)
x = scroll_offset
y = 16
for str in string:
if x + char_size > 0 and x < 128:
if str in fonts:
char_data = fonts[str]
for col in range(16):
byte1 = char_data[col * 2]
byte2 = char_data[col * 2 + 1]
for row in range(8):
if byte1 & (1 << row):
self.oled.pixel(x + col, y + row, 1)
for row in range(8):
if byte2 & (1 << row):
self.oled.pixel(x + col, y + 8 + row, 1)
x += char_size + 1
self.oled.show()
# 更新滚动偏移量
scroll_offset -= scroll_speed
total_width = 14 * (char_size + 1) - 1
if (scroll_offset < -total_width):
scroll_offset = 128
return scroll_offset
def text_scroll(self, string, y):
# 显示滚动的16x16字符串,英文、数字
try:
while self.is_scroll:
self.scroll_offset = self.draw_text(string, self.scroll_offset, y)
time.sleep(0.05)
except Exception as e:
# self.oled.fill(0)
# self.oled("Err", 55, 30)
# self.oled.show()
# self.is_scroll = False
print('错误:', e)
def stop_scroll(self):
# 停止滚动
self.is_scroll = False
大致啥就是这样子,可惜没弄懂图像是怎么显示,不过我还是会继续查阅其它资料,后续也会补上。
前端
页面这次改动比较少,基本都是之前封装好的,兼容了硬件的使用。看下具体的使用流程
第一,设置页面开发模式为硬件编程

第二:扩展选OLED硬件,并确认

第三,拉积木组装

第四,连接硬件,点击右上角三角叉或者连接按钮,并选择要连接的串口设备

第五,连接完成,点击运行

总结
硬件功能-0.96寸OLED功能,难点在于我不是一名嵌入式工程师,基本没怎么讲解,相信各位大佬都是实力顶呱呱,一定能理解到位。好,在这个开发工程中,我更多还是阅读mircopython文档,了解每个api的使用,一步一步问ai,然后拼凑成自己的代码,本期也告一段落,期待下期其它硬件