Форум сайта python.su
1
Привествую
Вопрос такой возник.
Пытался поизучать протокол работы oled дисплея ssd1306 128x64, по шине i2c. В MX линукс подключил переходник который,
появился как i2c-10, ну и через библиотеку smbus2 передаю команды. Из кода видно что хочу вывести текст в первой и последней строке дисплея. Выводится последняя строка на месте первой. Первая строка вообще не выводится.
К тому же строка обтекается белым фоном, которым залит дисплей.
Что я делаю не так, может кто разбирался ранее с такими вещами? Практического применения не вижу (просто мне так удобно изучить тему), на контроллерах этот дисплей с их библиотеками прекрасно работает.
from smbus2 import SMBus # Из C библиотеки SSD1306_I2C_ADDRESS = 0x3C # 011110+SA0+RW - 0x3C or 0x3D SSD1306_EXTERNALVCC = 0x1 SSD1306_SETLOWCOLUMN = 0x00 SSD1306_SETHIGHCOLUMN = 0x10 SSD1306_MEMORYMODE = 0x20 SSD1306_COLUMNADDR = 0x21 SSD1306_PAGEADDR = 0x22 SSD1306_SETSTARTLINE = 0x40 SSD1306_DEFAULT_ADDRESS = 0x78 SSD1306_SETCONTRAST = 0x81 SSD1306_CHARGEPUMP = 0x8D SSD1306_SEGREMAP = 0xA0 SSD1306_DISPLAYALLON_RESUME = 0xA4 SSD1306_DISPLAYALLON = 0xA5 SSD1306_NORMALDISPLAY = 0xA6 SSD1306_INVERTDISPLAY = 0xA7 SSD1306_SETMULTIPLEX = 0xA8 SSD1306_DISPLAYOFF = 0xAE SSD1306_DISPLAYON = 0xAF SSD1306_SETPAGE = 0xB0 SSD1306_COMSCANINC = 0xC0 SSD1306_COMSCANDEC = 0xC8 SSD1306_SETDISPLAYOFFSET = 0xD3 SSD1306_SETDISPLAYCLOCKDIV = 0xD5 SSD1306_SETPRECHARGE = 0xD9 SSD1306_SETCOMPINS = 0xDA SSD1306_SETVCOMDETECT = 0xDB SSD1306_SWITCHCAPVCC = 0x02 SSD1306_NOP = 0xE3 HORIZONTAL_ADDRESSING_MODE = 0x00 VERTICAL_ADDRESSING_MODE = 0x01 PAGE_ADDRESSING_MODE = 0x02 class SSD1306Base(object): def __init__(self, width, height): self.i2c = SMBus(10) self.width = width self.height = height self._pages = height//8 self._buffer = [0]*(width*self._pages) def _initialize(self): raise NotImplementedError def command(self, c): control = 0x00 # Co = 0, DC = 0 self.i2c.write_i2c_block_data(SSD1306_I2C_ADDRESS, control,[c]) def begin(self, vccstate=SSD1306_SWITCHCAPVCC): """Initialize display.""" # Save vcc state. self._vccstate = vccstate # initialize display. self._initialize() # Turn on the display. self.command(SSD1306_DISPLAYON) def display(self): self.command(0x00) self.command(SSD1306_COLUMNADDR) self.command(0) # Column start address. (0 = reset) self.command(self.width-1) # Column end address. self.command(SSD1306_PAGEADDR) self.command(0) # Page start address. (0 = reset) self.command(self._pages-1) # Page end address. # Write buffer data. for i in range(0, len(self._buffer), 16): control = 0x40 # Co = 0, DC = 0 self.i2c.write_i2c_block_data(SSD1306_I2C_ADDRESS, control,self._buffer[i:i+16]) def image(self, image): """Set buffer to value of Python Imaging Library image. The image should be in 1 bit mode and a size equal to the display size. """ imwidth, imheight = image.size if imwidth != self.width or imheight != self.height: raise ValueError('Image must be same dimensions as display ({0}x{1}).'.format(self.width, self.height)) pix = image.load() # Iterate through the memory pages index = 0 for page in range(self._pages): for x in range(self.width):#128 # Set the bits for the column of pixels at the current position. bits = 0 # Don't use range here as it's a bit slow for bit in [0, 1, 2, 3, 4, 5, 6, 7]: bits = bits << 1 bits |= 0 if pix[(x, page*8+7-bit)] == 0 else 1 # Update buffer byte and increment to next byte. self._buffer[index] = bits index += 1 self.save(image) def save(self, image): """save contents of buffer for further analysis""" with open("out.bin", "wb") as f: f.write(bytes(self._buffer)) image.save("out_raw.bmp") def clear(self): """Clear contents of image buffer.""" self._buffer = [0]*(self.width*self._pages) def set_contrast(self, contrast): """Sets the contrast of the display. Contrast should be a value between 0 and 255.""" if contrast < 0 or contrast > 255: raise ValueError('Contrast must be a value from 0 to 255 (inclusive).') self.command(SSD1306_SETCONTRAST) self.command(contrast) def dim(self, dim): """Adjusts contrast to dim the display if dim is True, otherwise sets the contrast to normal brightness if dim is False. """ # Assume dim display. contrast = 0 # Adjust contrast based on VCC if not dimming. if not dim: if self._vccstate == SSD1306_EXTERNALVCC: contrast = 0x9F else: contrast = 0xCF class SSD1306_128_64(SSD1306Base): def __init__(self): # Call base class constructor. super(SSD1306_128_64, self).__init__(128,64) def _initialize(self): initial = [SSD1306_DISPLAYOFF, SSD1306_MEMORYMODE, HORIZONTAL_ADDRESSING_MODE, SSD1306_COMSCANDEC, SSD1306_SETSTARTLINE | 0x00, SSD1306_SETCONTRAST, 0x7F, SSD1306_SEGREMAP | 0x01, SSD1306_NORMALDISPLAY, SSD1306_SETMULTIPLEX, 63, SSD1306_SETDISPLAYOFFSET, 0x00, SSD1306_SETDISPLAYCLOCKDIV, 0x80, SSD1306_SETPRECHARGE, 0x22, SSD1306_SETCOMPINS, 0x12, SSD1306_SETVCOMDETECT, 0x20, SSD1306_CHARGEPUMP, 0x14, SSD1306_DISPLAYALLON_RESUME, SSD1306_DISPLAYON] for i in initial: self.command(i) from PIL import Image from PIL import ImageFont from PIL import ImageDraw disp = SSD1306_128_64() disp.begin() disp.clear() disp.display() image = Image.new('1', (128, 64)) draw = ImageDraw.Draw(image) font = ImageFont.truetype('arial.ttf', 18) draw.text((0, 50),'Hello', font=font, fill=255) draw.text((0, 10),'Hello10', font=font, fill=255) disp.image(image) disp.display()
Отредактировано ingfa_1981 (Апрель 21, 2023 08:11:21)
Офлайн
124
from PIL import Image
from PIL import ImageFont
from PIL import ImageDraw
image = Image.new('1', (128, 64))
draw = ImageDraw.Draw(image)
font = ImageFont.truetype('arial.ttf', 18)
draw.text((0, 0),'Hello', font=font, fill=255)
draw.text((0, 10),'Hello10', font=font, fill=255)
image.show()
Офлайн
1
xam1816на картинке все вравильно на дисплее
from PIL import Image
from PIL import ImageFont
from PIL import ImageDraw
image = Image.new('1', (128, 64))
draw = ImageDraw.Draw(image)
font = ImageFont.truetype('arial.ttf', 18)
draw.text((0, 0),'Hello', font=font, fill=255)
draw.text((0, 10),'Hello10', font=font, fill=255)
image.show()
Офлайн
124
ingfa_1981а если 2ю строку считать как 0, 60, отобразится? приложи фото через https://imgur.com/ например, там есть ссылка для вставки на форуме, чтобы сразу картинка в сообщении отображалась
Если выполнить
draw.text((0,50),'Hello10', font=font, fill=255
то отобразится как первая строка
Офлайн
1
import time import smbus2 as smbus from PIL import Image,ImageFont,ImageDraw _COMMAND_MODE = 0x80 _DATA_MODE = 0x40 _NORMAL_DISPLAY = 0xA6 _DISPLAY_OFF = 0xAE _DISPLAY_ON = 0xAF _INVERSE_DISPLAY = 0xA7 _SET_BRIGHTNESS = 0x81 BasicFont = [[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], [0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, 0x00, 0x00], [0x00, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x00], [0x00, 0x14, 0x7F, 0x14, 0x7F, 0x14, 0x00, 0x00], [0x00, 0x24, 0x2A, 0x7F, 0x2A, 0x12, 0x00, 0x00], [0x00, 0x23, 0x13, 0x08, 0x64, 0x62, 0x00, 0x00], [0x00, 0x36, 0x49, 0x55, 0x22, 0x50, 0x00, 0x00], [0x00, 0x00, 0x05, 0x03, 0x00, 0x00, 0x00, 0x00], [0x00, 0x1C, 0x22, 0x41, 0x00, 0x00, 0x00, 0x00], [0x00, 0x41, 0x22, 0x1C, 0x00, 0x00, 0x00, 0x00], [0x00, 0x08, 0x2A, 0x1C, 0x2A, 0x08, 0x00, 0x00], [0x00, 0x08, 0x08, 0x3E, 0x08, 0x08, 0x00, 0x00], [0x00, 0xA0, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00], [0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00], [0x00, 0x60, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00], [0x00, 0x20, 0x10, 0x08, 0x04, 0x02, 0x00, 0x00], [0x00, 0x3E, 0x51, 0x49, 0x45, 0x3E, 0x00, 0x00], [0x00, 0x00, 0x42, 0x7F, 0x40, 0x00, 0x00, 0x00], [0x00, 0x62, 0x51, 0x49, 0x49, 0x46, 0x00, 0x00], [0x00, 0x22, 0x41, 0x49, 0x49, 0x36, 0x00, 0x00], [0x00, 0x18, 0x14, 0x12, 0x7F, 0x10, 0x00, 0x00], [0x00, 0x27, 0x45, 0x45, 0x45, 0x39, 0x00, 0x00], [0x00, 0x3C, 0x4A, 0x49, 0x49, 0x30, 0x00, 0x00], [0x00, 0x01, 0x71, 0x09, 0x05, 0x03, 0x00, 0x00], [0x00, 0x36, 0x49, 0x49, 0x49, 0x36, 0x00, 0x00], [0x00, 0x06, 0x49, 0x49, 0x29, 0x1E, 0x00, 0x00], [0x00, 0x00, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00], [0x00, 0x00, 0xAC, 0x6C, 0x00, 0x00, 0x00, 0x00], [0x00, 0x08, 0x14, 0x22, 0x41, 0x00, 0x00, 0x00], [0x00, 0x14, 0x14, 0x14, 0x14, 0x14, 0x00, 0x00], [0x00, 0x41, 0x22, 0x14, 0x08, 0x00, 0x00, 0x00], [0x00, 0x02, 0x01, 0x51, 0x09, 0x06, 0x00, 0x00], [0x00, 0x32, 0x49, 0x79, 0x41, 0x3E, 0x00, 0x00], [0x00, 0x7E, 0x09, 0x09, 0x09, 0x7E, 0x00, 0x00], [0x00, 0x7F, 0x49, 0x49, 0x49, 0x36, 0x00, 0x00], [0x00, 0x3E, 0x41, 0x41, 0x41, 0x22, 0x00, 0x00], [0x00, 0x7F, 0x41, 0x41, 0x22, 0x1C, 0x00, 0x00], [0x00, 0x7F, 0x49, 0x49, 0x49, 0x41, 0x00, 0x00], [0x00, 0x7F, 0x09, 0x09, 0x09, 0x01, 0x00, 0x00], [0x00, 0x3E, 0x41, 0x41, 0x51, 0x72, 0x00, 0x00], [0x00, 0x7F, 0x08, 0x08, 0x08, 0x7F, 0x00, 0x00], [0x00, 0x41, 0x7F, 0x41, 0x00, 0x00, 0x00, 0x00], [0x00, 0x20, 0x40, 0x41, 0x3F, 0x01, 0x00, 0x00], [0x00, 0x7F, 0x08, 0x14, 0x22, 0x41, 0x00, 0x00], [0x00, 0x7F, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00], [0x00, 0x7F, 0x02, 0x0C, 0x02, 0x7F, 0x00, 0x00], [0x00, 0x7F, 0x04, 0x08, 0x10, 0x7F, 0x00, 0x00], [0x00, 0x3E, 0x41, 0x41, 0x41, 0x3E, 0x00, 0x00], [0x00, 0x7F, 0x09, 0x09, 0x09, 0x06, 0x00, 0x00], [0x00, 0x3E, 0x41, 0x51, 0x21, 0x5E, 0x00, 0x00], [0x00, 0x7F, 0x09, 0x19, 0x29, 0x46, 0x00, 0x00], [0x00, 0x26, 0x49, 0x49, 0x49, 0x32, 0x00, 0x00], [0x00, 0x01, 0x01, 0x7F, 0x01, 0x01, 0x00, 0x00], [0x00, 0x3F, 0x40, 0x40, 0x40, 0x3F, 0x00, 0x00], [0x00, 0x1F, 0x20, 0x40, 0x20, 0x1F, 0x00, 0x00], [0x00, 0x3F, 0x40, 0x38, 0x40, 0x3F, 0x00, 0x00], [0x00, 0x63, 0x14, 0x08, 0x14, 0x63, 0x00, 0x00], [0x00, 0x03, 0x04, 0x78, 0x04, 0x03, 0x00, 0x00], [0x00, 0x61, 0x51, 0x49, 0x45, 0x43, 0x00, 0x00], [0x00, 0x7F, 0x41, 0x41, 0x00, 0x00, 0x00, 0x00], [0x00, 0x02, 0x04, 0x08, 0x10, 0x20, 0x00, 0x00], [0x00, 0x41, 0x41, 0x7F, 0x00, 0x00, 0x00, 0x00], [0x00, 0x04, 0x02, 0x01, 0x02, 0x04, 0x00, 0x00], [0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00], [0x00, 0x01, 0x02, 0x04, 0x00, 0x00, 0x00, 0x00], [0x00, 0x20, 0x54, 0x54, 0x54, 0x78, 0x00, 0x00], [0x00, 0x7F, 0x48, 0x44, 0x44, 0x38, 0x00, 0x00], [0x00, 0x38, 0x44, 0x44, 0x28, 0x00, 0x00, 0x00], [0x00, 0x38, 0x44, 0x44, 0x48, 0x7F, 0x00, 0x00], [0x00, 0x38, 0x54, 0x54, 0x54, 0x18, 0x00, 0x00], [0x00, 0x08, 0x7E, 0x09, 0x02, 0x00, 0x00, 0x00], [0x00, 0x18, 0xA4, 0xA4, 0xA4, 0x7C, 0x00, 0x00], [0x00, 0x7F, 0x08, 0x04, 0x04, 0x78, 0x00, 0x00], [0x00, 0x00, 0x7D, 0x00, 0x00, 0x00, 0x00, 0x00], [0x00, 0x80, 0x84, 0x7D, 0x00, 0x00, 0x00, 0x00], [0x00, 0x7F, 0x10, 0x28, 0x44, 0x00, 0x00, 0x00], [0x00, 0x41, 0x7F, 0x40, 0x00, 0x00, 0x00, 0x00], [0x00, 0x7C, 0x04, 0x18, 0x04, 0x78, 0x00, 0x00], [0x00, 0x7C, 0x08, 0x04, 0x7C, 0x00, 0x00, 0x00], [0x00, 0x38, 0x44, 0x44, 0x38, 0x00, 0x00, 0x00], [0x00, 0xFC, 0x24, 0x24, 0x18, 0x00, 0x00, 0x00], [0x00, 0x18, 0x24, 0x24, 0xFC, 0x00, 0x00, 0x00], [0x00, 0x00, 0x7C, 0x08, 0x04, 0x00, 0x00, 0x00], [0x00, 0x48, 0x54, 0x54, 0x24, 0x00, 0x00, 0x00], [0x00, 0x04, 0x7F, 0x44, 0x00, 0x00, 0x00, 0x00], [0x00, 0x3C, 0x40, 0x40, 0x7C, 0x00, 0x00, 0x00], [0x00, 0x1C, 0x20, 0x40, 0x20, 0x1C, 0x00, 0x00], [0x00, 0x3C, 0x40, 0x30, 0x40, 0x3C, 0x00, 0x00], [0x00, 0x44, 0x28, 0x10, 0x28, 0x44, 0x00, 0x00], [0x00, 0x1C, 0xA0, 0xA0, 0x7C, 0x00, 0x00, 0x00], [0x00, 0x44, 0x64, 0x54, 0x4C, 0x44, 0x00, 0x00], [0x00, 0x08, 0x36, 0x41, 0x00, 0x00, 0x00, 0x00], [0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00], [0x00, 0x41, 0x36, 0x08, 0x00, 0x00, 0x00, 0x00], [0x00, 0x02, 0x01, 0x01, 0x02, 0x01, 0x00, 0x00], [0x00, 0x02, 0x05, 0x05, 0x02, 0x00, 0x00, 0x00]] class GroveOledDisplay128x64(object): HORIZONTAL = 0x00 VERTICAL = 0x01 PAGE = 0x02 def __init__(self, bus=10, address=0x3C): self.bus = smbus.SMBus(bus) self.address = address self.off() self.inverse = False self.mode = self.HORIZONTAL self.clear() self.on() def on(self): self.send_command(_DISPLAY_ON) def off(self): self.send_command(_DISPLAY_OFF) def send_command(self, command): self.bus.write_byte_data(self.address, _COMMAND_MODE, command) def send_data(self, data): self.bus.write_byte_data(self.address, _DATA_MODE, data) def send_commands(self, commands): for c in commands: self.send_command(c) def clear(self): self.off() for i in range(8): self.set_cursor(i, 0) self.puts(' ' * 16) self.on() self.set_cursor(0, 0) @property def inverse(self): return self._inverse @inverse.setter def inverse(self, enable): self.send_command(_INVERSE_DISPLAY if enable else _NORMAL_DISPLAY) self._inverse = enable @property def mode(self): return self._mode @mode.setter def mode(self, mode): self.send_command(0x20) self.send_command(mode) self._mode = mode def set_cursor(self, row, column): self.send_command(0xB0 + row) self.send_command(0x00 + (8 * column & 0x0F)) self.send_command(0x10 + ((8 * column >> 4) & 0x0F)) def putc(self, c): C_add = ord(c) if C_add < 32 or C_add > 127: # Ignore non-printable ASCII characters c = ' ' C_add = ord(c) for i in range(0, 8): self.send_data(BasicFont[C_add - 32][i]) def puts(self, text): for c in text: self.putc(c) def show_image(self, image): from PIL import Image import numpy as np image.save("ssd1306.bmp") #im = Image.open(image) #bw = im.convert('1') #pixels = np.array(bw.getdata()) pixels = np.array(image.getdata()) page_size = 128 * 8 self.set_cursor(0, 0) for page in range(8): start = page_size * page end = start + page_size for i in range(start, start + 128): data = np.packbits(pixels[i:end:128][::-1])[0] self.send_data(data) display = GroveOledDisplay128x64(bus=10) image = Image.new('1', (128, 64)) draw = ImageDraw.Draw(image) font = ImageFont.truetype('/home/konsyst/arial.ttf', 15) draw.text((0,50),'Hello', font=font, fill=255) display.show_image(image) display.set_cursor(0, 2) display.puts('hello') display.set_cursor(1, 4) display.puts('world') display.set_cursor(2, 2) display.puts('privet') image.show()
display = GroveOledDisplay128x64(bus=10) image = Image.new('1', (128, 64)) draw = ImageDraw.Draw(image) font = ImageFont.truetype('/home/konsyst/arial.ttf', 15) draw.text((0,50),'Hello', font=font, fill=255) display.show_image(image)
display.set_cursor(0, 2) display.puts('hello') display.set_cursor(1, 4) display.puts('world') display.set_cursor(2, 2) display.puts('privet')
Офлайн