Работа с OLED дисплей и ESP8266 с MicroPython

Работа с OLED дисплей и ESP8266 с MicroPython

Въведение:

В тази статия ще разгледаме как да използваме OLED SSD1306 дисплей с ESP8266 и MicroPython. OLED дисплеите са компактни и ефективни, като предоставят висока разделителна способност и ниско енергийно потребление. Ще обясним необходимите стъпки за свързване и настройка, както и основни функции за изчертаване на текст и графики върху дисплея.

За целта на тази статия ще използваме процесора ESP8266 с OLED дисплей който е закачен директно на платформата. Може да прочетете повече за него в нашето ревю: ESP8266 с OLED дисплей.

Ако все още не сте инсталирали MicroPython на вашия ESP8266, вижте нашето ръководство за инсталация в статията ни: Как да инсталирате MicroPython на ESP8266.

Използвана библиотека SSD1306:

OLED дисплеите използват библиотеката ssd1306 в MicroPython, която трябва да бъде качена в ESP8266. За да го направите, запазете библиотеката като ssd1306.py, след което я качете на контролера с помощта на инструмент като ampy или WebREPL. Веднъж качена, библиотеката може лесно да бъде използвана чрез импортиране в кода ви с import ssd1306. Може да свалите библиотеката от тук: SSD1306.

# MicroPython SSD1306 OLED driver, I2C and SPI interfaces

from micropython import const
import framebuf


# register definitions
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_IREF_SELECT = const(0xAD)
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)

# Subclassing FrameBuffer provides support for graphics primitives
# http://docs.micropython.org/en/latest/pyboard/library/framebuf.html
class SSD1306(framebuf.FrameBuffer):
    def __init__(self, width, height, external_vcc):
        self.width = width
        self.height = height
        self.external_vcc = external_vcc
        self.pages = self.height // 8
        self.buffer = bytearray(self.pages * self.width)
        super().__init__(self.buffer, self.width, self.height, framebuf.MONO_VLSB)
        self.init_display()

    def init_display(self):
        for cmd in (
            SET_DISP,  # display off
            # address setting
            SET_MEM_ADDR,
            0x00,  # horizontal
            # resolution and layout
            SET_DISP_START_LINE,  # start at line 0
            SET_SEG_REMAP | 0x01,  # column addr 127 mapped to SEG0
            SET_MUX_RATIO,
            self.height - 1,
            SET_COM_OUT_DIR | 0x08,  # scan from COM[N] to COM0
            SET_DISP_OFFSET,
            0x00,
            SET_COM_PIN_CFG,
            0x02 if self.width > 2 * self.height else 0x12,
            # timing and driving scheme
            SET_DISP_CLK_DIV,
            0x80,
            SET_PRECHARGE,
            0x22 if self.external_vcc else 0xF1,
            SET_VCOM_DESEL,
            0x30,  # 0.83*Vcc
            # display
            SET_CONTRAST,
            0xFF,  # maximum
            SET_ENTIRE_ON,  # output follows RAM contents
            SET_NORM_INV,  # not inverted
            SET_IREF_SELECT,
            0x30,  # enable internal IREF during display on
            # charge pump
            SET_CHARGE_PUMP,
            0x10 if self.external_vcc else 0x14,
            SET_DISP | 0x01,  # display on
        ):  # on
            self.write_cmd(cmd)
        self.fill(0)
        self.show()

    def poweroff(self):
        self.write_cmd(SET_DISP)

    def poweron(self):
        self.write_cmd(SET_DISP | 0x01)

    def contrast(self, contrast):
        self.write_cmd(SET_CONTRAST)
        self.write_cmd(contrast)

    def invert(self, invert):
        self.write_cmd(SET_NORM_INV | (invert & 1))

    def rotate(self, rotate):
        self.write_cmd(SET_COM_OUT_DIR | ((rotate & 1) << 3))
        self.write_cmd(SET_SEG_REMAP | (rotate & 1))

    def show(self):
        x0 = 0
        x1 = self.width - 1
        if self.width != 128:
            # narrow displays use centred columns
            col_offset = (128 - self.width) // 2
            x0 += col_offset
            x1 += col_offset
        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_data(self.buffer)


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.write_list = [b"\x40", None]  # Co=0, D/C#=1
        super().__init__(width, height, external_vcc)

    def write_cmd(self, cmd):
        self.temp[0] = 0x80  # Co=1, D/C#=0
        self.temp[1] = cmd
        self.i2c.writeto(self.addr, self.temp)

    def write_data(self, buf):
        self.write_list[1] = buf
        self.i2c.writevto(self.addr, self.write_list)


class SSD1306_SPI(SSD1306):
    def __init__(self, width, height, spi, dc, res, cs, external_vcc=False):
        self.rate = 10 * 1024 * 1024
        dc.init(dc.OUT, value=0)
        res.init(res.OUT, value=0)
        cs.init(cs.OUT, value=1)
        self.spi = spi
        self.dc = dc
        self.res = res
        self.cs = cs
        import time

        self.res(1)
        time.sleep_ms(1)
        self.res(0)
        time.sleep_ms(10)
        self.res(1)
        super().__init__(width, height, external_vcc)

    def write_cmd(self, cmd):
        self.spi.init(baudrate=self.rate, polarity=0, phase=0)
        self.cs(1)
        self.dc(0)
        self.cs(0)
        self.spi.write(bytearray([cmd]))
        self.cs(1)

    def write_data(self, buf):
        self.spi.init(baudrate=self.rate, polarity=0, phase=0)
        self.cs(1)
        self.dc(1)
        self.cs(0)
        self.spi.write(buf)
        self.cs(1)

Намиране на I2C адреса на OLED дисплея:

За да се свърже правилно OLED дисплеят, е важно първо да се установи неговият I2C адрес. Ето код за сканиране на I2C устройството:

import machine

# Change this if you are using other ports
I2C_SDA_PIN = 5
I2C_SCL_PIN = 4
i2c=machine.I2C(sda=machine.Pin(I2C_SDA_PIN), scl=machine.Pin(I2C_SCL_PIN), freq=400000)

print('Scanning I2C bus.')
devices = i2c.scan() # this returns a list of devices

device_count = len(devices)

if device_count == 0:
    print('No i2c device found.')
else:
    print(device_count, 'devices found.')

for device in devices:
    print('Decimal address:', device, ", Hex address: ", hex(device))

Обикновено I2C адресът на дисплея е 0x3c, но проверката е важна, особено при различни модели.

Основен код работа с OLED дисплей и ESP8266 с MicroPython

from machine import Pin,I2C
import ssd1306
from time import sleep
#import framebuf

i2c=I2C(scl=Pin(4),sda=Pin(5), freq=400000)
oled=ssd1306.SSD1306_I2C(128,64,i2c,0x3c)
print("oled address = " , i2c.scan())                  # print OLED I2C address

oled.fill(0)                        # clear LCD
oled.show()                         # send data to oled

oled.rect(0, 0, 128, 64, 1)         # drow frame on Oled dislpay
oled.show()                         # send data to oled
sleep(1)
oled.fill(0)

oled.pixel(10 , 10 , 1)              # flash only one pixel
oled.show()                          # send data to oled
print(str(oled.pixel(10 , 10)))      # check pixel is flash or not
sleep(1)
oled.fill(0)

oled.hline(5, 32, 50, 1)             # draw horizontal line x=5px, y=32px, width=50px, colour=1
oled.show()
sleep(1)
oled.fill(0)

oled.vline(64, 10, 25, 1)            # draw vertical line x=64px, y=10px, height=25px, colour=1
oled.show()
sleep(1)
oled.fill(0)

oled.line(0, 0, 127, 63, 1)          # draw a line from 0,0 to 127,63
oled.show()
sleep(1)
oled.fill(0)

oled.rect(10, 10, 107, 43, 1)         # draw a rectangle outline 10,10 to 117,53, colour=1
oled.show()
sleep(1)
oled.fill(0)

oled.fill_rect(10, 10, 107, 43, 1)    # draw a solid rectangle 10,10 to 117,53, colour=1 fill_rectangle
oled.show()
sleep(1)
oled.fill(0)

oled.text('Hello World', 10, 30, 1)    # draw some text at x=10, y=30, colour=1
oled.show()
sleep(1)
oled.fill(0)


oled.fill_rect(0, 0, 32, 32, 1)
oled.fill_rect(2, 2, 28, 28, 0)
oled.vline(9, 8, 22, 1)
oled.vline(16, 2, 22, 1)
oled.vline(23, 8, 22, 1)
oled.fill_rect(26, 24, 2, 4, 1)
oled.text('MicroPython', 40, 0, 1)
oled.text('SSD1306', 40, 12, 1)
oled.text('OLED 128x64', 40, 24, 1)
oled.show()


#oled.contrast(255)                    # dimm display

#oled.poweron()                        # oled power on
#oled.poweroff()                       # oled power off

#oled.invert(1)                        # invert black text to white fon
#oled.invert(0)                        # invert white text to black fon

#oled.rotate(True)                     # rotate 180 degrees
#oled.rotate(False)                    # rotate 0 degrees

Подробно обяснение на кода стъпка по стъпка:

Импортиране на модули:

from machine import Pin, I2C
import ssd1306
from time import sleep

Импортираме нужните библиотеки – Pin и I2C за контрол на пиновете, ssd1306 за работа с дисплея и sleep за забавяния между операциите.

Инициализация на I2C и OLED дисплея:

i2c = I2C(scl=Pin(4), sda=Pin(5), freq=400000)
oled = ssd1306.SSD1306_I2C(128, 64, i2c, 0x3c)

Създаваме I2C обект с пинове 4 и 5 за връзка с OLED и настройваме честотата на 400 kHz. Дефинираме OLED дисплея с резолюция 128×64 пиксела и задаваме адреса му 0x3c.

Проверка на I2C устройството:

print("oled address = ", i2c.scan())

Извежда адреса на свързаното I2C устройство, за да потвърди, че OLED дисплеят е правилно свързан.

Изчистване на дисплея:

oled.fill(0)
oled.show()

oled.fill(0) нулира екрана, а oled.show() прилага промените, изчиствайки OLED дисплея.

Рисуване на рамка:

oled.rect(0, 0, 128, 64, 1)
oled.show()

Създава рамка с координати от (0, 0) до (128, 64), след което я показва.

Активиране на пиксел:

oled.pixel(10, 10, 1)
oled.show()

Активира единичен пиксел в позиция (10, 10) и го показва на дисплея. Това може да се използва за проверка на отделни пиксели.

Рисуване на хоризонтална линия:

oled.hline(5, 32, 50, 1)
oled.show()

Извежда хоризонтална линия с дължина 50 пиксела на позиция (5, 32).

Рисуване на вертикална линия:

oled.vline(64, 10, 25, 1)
oled.show()

Създава вертикална линия на позиция (64, 10) с височина 25 пиксела.

Рисуване на диагонална линия:

oled.line(0, 0, 127, 63, 1)
oled.show()

Начертава диагонална линия от (0, 0) до (127, 63).

Очертание на правоъгълник:

oled.rect(10, 10, 107, 43, 1)
oled.show()

Създава правоъгълник с очертания от позиция (10, 10) до (117, 53).

Запълнен правоъгълник:

oled.fill_rect(10, 10, 107, 43, 1)
oled.show()

Рисува запълнен правоъгълник в същия регион.

Текстово съобщение:

oled.text('Hello World', 10, 30, 1)
oled.show()

Показва текст в позиция (10, 30).

Демо фигура с текст:

oled.fill_rect(0, 0, 32, 32, 1)
oled.fill_rect(2, 2, 28, 28, 0)
oled.vline(9, 8, 22, 1)
oled.vline(16, 2, 22, 1)
oled.vline(23, 8, 22, 1)
oled.fill_rect(26, 24, 2, 4, 1)
oled.text('MicroPython', 40, 0, 1)
oled.text('SSD1306', 40, 12, 1)
oled.text('OLED 128x64', 40, 24, 1)
oled.show()

Това е сложна комбинация от форми и линии, последвана от текст. Демонстрира детайлна графика с MicroPython.

(По избор) Функции за управление на контраста и екрана:

# oled.contrast(255)
# oled.poweron()
# oled.poweroff()
# oled.invert(1)
# oled.invert(0)
# oled.rotate(True)
# oled.rotate(False)

Тези функции предоставят опции за управление на контраста, включване/изключване на дисплея, инверсия на цветовете и завъртане на екрана. Може да ги пробвате един по един за да разберете какво правят те.

Заключение:

Използването на OLED дисплей с ESP8266 и MicroPython осигурява лесен и гъвкав начин за визуализиране на данни. С библиотеката ssd1306 можете лесно да добавяте графики и текст, което прави този дисплей идеален за IoT проекти и прототипи. Офицялен наръчник тук.

Translate »