From 766f3d3efa8527352d3ca35142cf5ae749337ea7 Mon Sep 17 00:00:00 2001 From: peterhinch Date: Sun, 6 Nov 2022 09:02:25 +0000 Subject: [PATCH] Add ssd1327 driver (not yet documented). --- DISPLAYS.md | 3 + drivers/ssd1327/ssd1327.py | 192 +++++++++++++++++++++++++++++++++++++ 2 files changed, 195 insertions(+) create mode 100644 drivers/ssd1327/ssd1327.py diff --git a/DISPLAYS.md b/DISPLAYS.md index 3825ee1..a203014 100644 --- a/DISPLAYS.md +++ b/DISPLAYS.md @@ -21,6 +21,7 @@ Width and height are pixels. | 1.44C | 128 | 128 | TFT | [ST7735R][4d] | [Adafruit 2088][5m] | | | 1.5C | 160 | 128 | TFT | [ST7735R][4d] | [Adafruit 358][6m] | | | 1.3C | 240 | 240 | TFT | [ST7789][5d] | [Adafruit 4313][7m] | | +| 1.5GS | 128 | 128 | OLED | [SSD1327][11d]| [Waveshare 13992][20m] | User contributed | | 2.0C | 320 | 240 | TFT | [ST7789][5d] | [Waveshare Pico LCD 2][18m]| For Pi Pico | | 1.54C | 240 | 240 | TFT | [ST7789][5d] | [Adafruit 3787][8m] | | | 1.14C | 240 | 135 | TFT | [ST7789][5d] | [T-Display][9m] | ESP32 with attached display | @@ -86,6 +87,7 @@ simple. See [this doc](./DRIVERS.md#7-writing-device-drivers) for details. [8d]: https://github.com/peterhinch/micropython-nano-gui/blob/master/DRIVERS.md#52-waveshare-eink-display-hat [9d]: https://github.com/peterhinch/micropython-nano-gui/blob/master/DRIVERS.md#4-drivers-for-sharp-displays [10d]: https://github.com/peterhinch/micropython-nano-gui/blob/master/DRIVERS.md#53-waveshare-400x300-pi-pico-display +[11d]: [1m]: https://www.adafruit.com/product/684 [2m]: https://www.adafruit.com/product/1673 @@ -106,3 +108,4 @@ simple. See [this doc](./DRIVERS.md#7-writing-device-drivers) for details. [17m]: https://www.adafruit.com/product/3502 [18m]: https://www.waveshare.com/wiki/Pico-LCD-2 [19m]: https://thepihut.com/collections/epaper-displays-for-raspberry-pi/products/4-2-e-paper-display-module-for-raspberry-pi-pico-black-white-400x300 +[20m]: https://www.waveshare.com/product/ai/displays/oled/1.5inch-oled-module.htm?___SID=U diff --git a/drivers/ssd1327/ssd1327.py b/drivers/ssd1327/ssd1327.py new file mode 100644 index 0000000..cd475f3 --- /dev/null +++ b/drivers/ssd1327/ssd1327.py @@ -0,0 +1,192 @@ +""" +MicroPython SSD1327 OLED I2C driver + +Adapted for CWriter, nano-gui and micro-gui from +https://github.com/mcauser/micropython-ssd1327 +by @mcauser (Mike Causer) and @Treadbrook (Philip Adamson). + +MIT License +Copyright (c) 2022 Mike Causer + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +""" + +__version__ = '1.1.2' + +from micropython import const +from framebuf import FrameBuffer, GS4_HMSB +from boolpalette import BoolPalette + +# commands +SET_COL_ADDR = const(0x15) +SET_SCROLL_DEACTIVATE = const(0x2E) +SET_ROW_ADDR = const(0x75) +SET_CONTRAST = const(0x81) +SET_SEG_REMAP = const(0xA0) +SET_DISP_START_LINE = const(0xA1) +SET_DISP_OFFSET = const(0xA2) +SET_DISP_MODE = const(0xA4) # 0xA4 normal, 0xA5 all on, 0xA6 all off, 0xA7 when inverted +SET_MUX_RATIO = const(0xA8) +SET_FN_SELECT_A = const(0xAB) +SET_DISP = const(0xAE) # 0xAE power off, 0xAF power on +SET_PHASE_LEN = const(0xB1) +SET_DISP_CLK_DIV = const(0xB3) +SET_SECOND_PRECHARGE = const(0xB6) +SET_GRAYSCALE_TABLE = const(0xB8) +SET_GRAYSCALE_LINEAR = const(0xB9) +SET_PRECHARGE = const(0xBC) +SET_VCOM_DESEL = const(0xBE) +SET_FN_SELECT_B = const(0xD5) +SET_COMMAND_LOCK = const(0xFD) + +# registers +REG_CMD = const(0x80) +REG_DATA = const(0x40) + +class SSD1327(FrameBuffer): + lut = bytearray(32) + def __init__(self, width=128, height=128): + self.width = width + self.height = height + self.buffer = bytearray(self.width * self.height // 2) + mode = GS4_HMSB + self.palette = BoolPalette(mode) + super().__init__(self.buffer, 128, 128, GS4_HMSB) + + self.col_addr = ((128 - self.width) // 4, 63 - ((128 - self.width) // 4)) + # 96x96 (8, 55) + # 128x128 (0, 63) + + self.row_addr = (0, self.height - 1) + # 96x96 (0, 95) + # 128x128 (0, 127) + + self.offset = 128 - self.height + # 96x96 32 + # 128x128 0 + + self.poweron() + self.init_display() + + def init_display(self): + for cmd in ( + SET_COMMAND_LOCK, 0x12, # Unlock + SET_DISP, # Display off + # Resolution and layout + SET_DISP_START_LINE, 0x00, + SET_DISP_OFFSET, self.offset, # Set vertical offset by COM from 0~127 + # Set re-map + # Enable column address re-map + # Disable nibble re-map + # Horizontal address increment + # Enable COM re-map + # Enable COM split odd even + SET_SEG_REMAP, 0x51, + SET_MUX_RATIO, self.height - 1, + # Timing and driving scheme + SET_FN_SELECT_A, 0x01, # Enable internal VDD regulator + SET_PHASE_LEN, 0x51, # Phase 1: 1 DCLK, Phase 2: 5 DCLKs + SET_DISP_CLK_DIV, 0x01, # Divide ratio: 1, Oscillator Frequency: 0 + SET_PRECHARGE, 0x08, # Set pre-charge voltage level: VCOMH + SET_VCOM_DESEL, 0x07, # Set VCOMH COM deselect voltage level: 0.86*Vcc + SET_SECOND_PRECHARGE, 0x01, # Second Pre-charge period: 1 DCLK + SET_FN_SELECT_B, 0x62, # Enable enternal VSL, Enable second precharge + # Display + SET_GRAYSCALE_LINEAR, # Use linear greyscale lookup table + SET_CONTRAST, 0x7f, # Medium brightness + SET_DISP_MODE, # Normal, not inverted + SET_COL_ADDR, self.col_addr[0], self.col_addr[1], + SET_ROW_ADDR, self.row_addr[0], self.row_addr[1], + SET_SCROLL_DEACTIVATE, + SET_DISP | 0x01): # Display on + self.write_cmd(cmd) + self.fill(0) + self.write_data(self.buffer) + + def poweroff(self): + self.write_cmd(SET_FN_SELECT_A) + self.write_cmd(0x00) # Disable internal VDD regulator, to save power + self.write_cmd(SET_DISP) + + def poweron(self): + self.write_cmd(SET_FN_SELECT_A) + self.write_cmd(0x01) # Enable internal VDD regulator + self.write_cmd(SET_DISP | 0x01) + + def contrast(self, contrast): + self.write_cmd(SET_CONTRAST) + self.write_cmd(contrast) # 0-255 + + def rotate(self, rotate): + self.poweroff() + self.write_cmd(SET_DISP_OFFSET) + self.write_cmd(self.height if rotate else self.offset) + self.write_cmd(SET_SEG_REMAP) + self.write_cmd(0x42 if rotate else 0x51) + self.poweron() + + def invert(self, invert): + self.write_cmd(SET_DISP_MODE | (invert & 1) << 1 | (invert & 1)) # 0xA4=Normal, 0xA7=Inverted + + def show(self): + self.write_cmd(SET_COL_ADDR) + self.write_cmd(self.col_addr[0]) + self.write_cmd(self.col_addr[1]) + self.write_cmd(SET_ROW_ADDR) + self.write_cmd(self.row_addr[0]) + self.write_cmd(self.row_addr[1]) + self.write_data(self.buffer) + + def write_cmd(self): + raise NotImplementedError + + def write_data(self): + raise NotImplementedError + + +class SSD1327_I2C(SSD1327): + def __init__(self, width, height, i2c, addr=0x3c): + self.i2c = i2c + self.addr = addr + self.cmd_arr = bytearray([REG_CMD, 0]) # Co=1, D/C#=0 + self.data_list = [bytes((REG_DATA,)), None] + super().__init__(width, height) + + def write_cmd(self, cmd): + self.cmd_arr[1] = cmd + self.i2c.writeto(self.addr, self.cmd_arr) + + def write_data(self, data_buf): + self.data_list[1] = data_buf + self.i2c.writevto(self.addr, self.data_list) + + +class SEEED_OLED_96X96(SSD1327_I2C): + def __init__(self, i2c): + super().__init__(96, 96, i2c) + + def lookup(self, table): + # GS0 has no pre-charge and current drive + self.write_cmd(SET_GRAYSCALE_TABLE) + for i in range(0,15): + self.write_cmd(table[i]) + +class WS_OLED_128X128(SSD1327_I2C): + def __init__(self, i2c, addr=0x3c): + super().__init__(128, 128, i2c, addr)