Bipes-Blockly项目二次开发/Coze智能体(十)

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,然后拼凑成自己的代码,本期也告一段落,期待下期其它硬件

相关推荐
1 小时前
Vue 3 组件封装与使用:保姆级教程
前端
星辰1 小时前
深入浅出 Android AOA 协议:通信流程与设备切换附着机制解析
前端
恋猫de小郭1 小时前
Amper 正式转正 Kotlin Toolchain ,Gradle 未来何去何从
android·前端·flutter
敲代码的彭于晏2 小时前
Bean 生命周期完全图解:前端同学也能看懂的 Spring 核心机制
java·前端·后端
IT_陈寒2 小时前
Redis内存飙升的锅,原来是我没搞懂这个过期策略
前端·人工智能·后端
云浪2 小时前
前端二进制数组完全指南:ArrayBuffer、TypedArray、DataView 一次讲透
前端·javascript
张风捷特烈2 小时前
Flutter 类库大揭秘#02 | path_provider 各平台实现
前端·flutter
铁皮饭盒3 小时前
26年bunjs, elysia+pg一把梭, redis都省了
前端·javascript·后端