├── .travis.yml ├── LICENSE ├── README.rst └── ssd1306.py /.travis.yml: -------------------------------------------------------------------------------- 1 | # Travis CI configuration for automated .mpy file generation. 2 | # Author: Tony DiCola 3 | # License: Public Domain 4 | # This configuration will work with Travis CI (travis-ci.org) to automacially 5 | # build .mpy files for MicroPython when a new tagged release is created. This 6 | # file is relatively generic and can be shared across multiple repositories by 7 | # following these steps: 8 | # 1. Copy this file into a .travis.yml file in the root of the repository. 9 | # 2. Change the deploy > file section below to list each of the .mpy files 10 | # that should be generated. The config will automatically look for 11 | # .py files with the same name as the source for generating the .mpy files. 12 | # Note that the .mpy extension should be lower case! 13 | # 3. Commit the .travis.yml file and push it to GitHub. 14 | # 4. Go to travis-ci.org and find the repository (it needs to be setup to access 15 | # your github account, and your github account needs access to write to the 16 | # repo). Flip the 'ON' switch on for Travis and the repo, see the Travis 17 | # docs for more details: https://docs.travis-ci.com/user/getting-started/ 18 | # 5. Get a GitHub 'personal access token' which has at least 'public_repo' or 19 | # 'repo' scope: https://help.github.com/articles/creating-an-access-token-for-command-line-use/ 20 | # Keep this token safe and secure! Anyone with the token will be able to 21 | # access and write to your GitHub repositories. Travis will use the token 22 | # to attach the .mpy files to the release. 23 | # 6. In the Travis CI settings for the repository that was enabled find the 24 | # environment variable editing page: https://docs.travis-ci.com/user/environment-variables/#Defining-Variables-in-Repository-Settings 25 | # Add an environment variable named GITHUB_TOKEN and set it to the value 26 | # of the GitHub personal access token above. Keep 'Display value in build 27 | # log' flipped off. 28 | # 7. That's it! Tag a release and Travis should go to work to add .mpy files 29 | # to the release. It takes about a 2-3 minutes for a worker to spin up, 30 | # build mpy-cross, and add the binaries to the release. 31 | language: generic 32 | 33 | sudo: true 34 | 35 | deploy: 36 | provider: releases 37 | api_key: $GITHUB_TOKEN 38 | file: 39 | - "ssd1306.mpy" 40 | skip_cleanup: true 41 | on: 42 | tags: true 43 | 44 | before_install: 45 | - sudo apt-get -yqq update 46 | - sudo apt-get install -y build-essential git python python-pip 47 | - git clone https://github.com/adafruit/micropython.git 48 | - make -C micropython/mpy-cross 49 | - export PATH=$PATH:$PWD/micropython/mpy-cross/ 50 | - sudo pip install shyaml 51 | 52 | before_deploy: 53 | - shyaml get-values deploy.file < .travis.yml | sed 's/.mpy/.py/' | xargs -L1 mpy-cross 54 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Adafruit Industries 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | DEPRECATED LIBRARY micropython-adafruit-ssd1306 2 | =============================================== 3 | 4 | This library has been deprecated! We are leaving this up for historical and research purposes but archiving the repository. 5 | 6 | We are now only supporting CircuitPython libraries. 7 | 8 | Check out this library if you're interested in using the SSD1306: https://github.com/adafruit/Adafruit_CircuitPython_SSD1306 9 | 10 | MicroPython driver for SSD1306 OLED displays. 11 | 12 | This driver is based on the SSD1306 driver in the MicroPython source but differs by supporting hardware I2C interfaces (like on the SAMD21 MicroPython port). 13 | -------------------------------------------------------------------------------- /ssd1306.py: -------------------------------------------------------------------------------- 1 | # MicroPython SSD1306 OLED driver, I2C and SPI interfaces 2 | 3 | import time 4 | import framebuf 5 | 6 | 7 | # register definitions 8 | SET_CONTRAST = const(0x81) 9 | SET_ENTIRE_ON = const(0xa4) 10 | SET_NORM_INV = const(0xa6) 11 | SET_DISP = const(0xae) 12 | SET_MEM_ADDR = const(0x20) 13 | SET_COL_ADDR = const(0x21) 14 | SET_PAGE_ADDR = const(0x22) 15 | SET_DISP_START_LINE = const(0x40) 16 | SET_SEG_REMAP = const(0xa0) 17 | SET_MUX_RATIO = const(0xa8) 18 | SET_COM_OUT_DIR = const(0xc0) 19 | SET_DISP_OFFSET = const(0xd3) 20 | SET_COM_PIN_CFG = const(0xda) 21 | SET_DISP_CLK_DIV = const(0xd5) 22 | SET_PRECHARGE = const(0xd9) 23 | SET_VCOM_DESEL = const(0xdb) 24 | SET_CHARGE_PUMP = const(0x8d) 25 | 26 | 27 | class SSD1306: 28 | def __init__(self, width, height, external_vcc): 29 | self.width = width 30 | self.height = height 31 | self.external_vcc = external_vcc 32 | self.pages = self.height // 8 33 | # Note the subclass must initialize self.framebuf to a framebuffer. 34 | # This is necessary because the underlying data buffer is different 35 | # between I2C and SPI implementations (I2C needs an extra byte). 36 | self.poweron() 37 | self.init_display() 38 | 39 | def init_display(self): 40 | for cmd in ( 41 | SET_DISP | 0x00, # off 42 | # address setting 43 | SET_MEM_ADDR, 0x00, # horizontal 44 | # resolution and layout 45 | SET_DISP_START_LINE | 0x00, 46 | SET_SEG_REMAP | 0x01, # column addr 127 mapped to SEG0 47 | SET_MUX_RATIO, self.height - 1, 48 | SET_COM_OUT_DIR | 0x08, # scan from COM[N] to COM0 49 | SET_DISP_OFFSET, 0x00, 50 | SET_COM_PIN_CFG, 0x02 if self.height == 32 else 0x12, 51 | # timing and driving scheme 52 | SET_DISP_CLK_DIV, 0x80, 53 | SET_PRECHARGE, 0x22 if self.external_vcc else 0xf1, 54 | SET_VCOM_DESEL, 0x30, # 0.83*Vcc 55 | # display 56 | SET_CONTRAST, 0xff, # maximum 57 | SET_ENTIRE_ON, # output follows RAM contents 58 | SET_NORM_INV, # not inverted 59 | # charge pump 60 | SET_CHARGE_PUMP, 0x10 if self.external_vcc else 0x14, 61 | SET_DISP | 0x01): # on 62 | self.write_cmd(cmd) 63 | self.fill(0) 64 | self.show() 65 | 66 | def poweroff(self): 67 | self.write_cmd(SET_DISP | 0x00) 68 | 69 | def contrast(self, contrast): 70 | self.write_cmd(SET_CONTRAST) 71 | self.write_cmd(contrast) 72 | 73 | def invert(self, invert): 74 | self.write_cmd(SET_NORM_INV | (invert & 1)) 75 | 76 | def show(self): 77 | x0 = 0 78 | x1 = self.width - 1 79 | if self.width == 64: 80 | # displays with width of 64 pixels are shifted by 32 81 | x0 += 32 82 | x1 += 32 83 | self.write_cmd(SET_COL_ADDR) 84 | self.write_cmd(x0) 85 | self.write_cmd(x1) 86 | self.write_cmd(SET_PAGE_ADDR) 87 | self.write_cmd(0) 88 | self.write_cmd(self.pages - 1) 89 | self.write_framebuf() 90 | 91 | def fill(self, col): 92 | self.framebuf.fill(col) 93 | 94 | def pixel(self, x, y, col): 95 | self.framebuf.pixel(x, y, col) 96 | 97 | def scroll(self, dx, dy): 98 | self.framebuf.scroll(dx, dy) 99 | 100 | def text(self, string, x, y, col=1): 101 | self.framebuf.text(string, x, y, col) 102 | 103 | 104 | class SSD1306_I2C(SSD1306): 105 | def __init__(self, width, height, i2c, addr=0x3c, external_vcc=False): 106 | self.i2c = i2c 107 | self.addr = addr 108 | self.temp = bytearray(2) 109 | # Add an extra byte to the data buffer to hold an I2C data/command byte 110 | # to use hardware-compatible I2C transactions. A memoryview of the 111 | # buffer is used to mask this byte from the framebuffer operations 112 | # (without a major memory hit as memoryview doesn't copy to a separate 113 | # buffer). 114 | self.buffer = bytearray(((height // 8) * width) + 1) 115 | self.buffer[0] = 0x40 # Set first byte of data buffer to Co=0, D/C=1 116 | self.framebuf = framebuf.FrameBuffer1(memoryview(self.buffer)[1:], width, height) 117 | super().__init__(width, height, external_vcc) 118 | 119 | def write_cmd(self, cmd): 120 | self.temp[0] = 0x80 # Co=1, D/C#=0 121 | self.temp[1] = cmd 122 | self.i2c.writeto(self.addr, self.temp) 123 | 124 | def write_framebuf(self): 125 | # Blast out the frame buffer using a single I2C transaction to support 126 | # hardware I2C interfaces. 127 | self.i2c.writeto(self.addr, self.buffer) 128 | 129 | def poweron(self): 130 | pass 131 | 132 | 133 | class SSD1306_SPI(SSD1306): 134 | def __init__(self, width, height, spi, dc, res, cs, external_vcc=False): 135 | self.rate = 10 * 1024 * 1024 136 | dc.init(dc.OUT, value=0) 137 | res.init(res.OUT, value=0) 138 | cs.init(cs.OUT, value=1) 139 | self.spi = spi 140 | self.dc = dc 141 | self.res = res 142 | self.cs = cs 143 | self.buffer = bytearray((height // 8) * width) 144 | self.framebuf = framebuf.FrameBuffer1(self.buffer, width, height) 145 | super().__init__(width, height, external_vcc) 146 | 147 | def write_cmd(self, cmd): 148 | self.spi.init(baudrate=self.rate, polarity=0, phase=0) 149 | self.cs.high() 150 | self.dc.low() 151 | self.cs.low() 152 | self.spi.write(bytearray([cmd])) 153 | self.cs.high() 154 | 155 | def write_framebuf(self): 156 | self.spi.init(baudrate=self.rate, polarity=0, phase=0) 157 | self.cs.high() 158 | self.dc.high() 159 | self.cs.low() 160 | self.spi.write(self.buffer) 161 | self.cs.high() 162 | 163 | def poweron(self): 164 | self.res.high() 165 | time.sleep_ms(1) 166 | self.res.low() 167 | time.sleep_ms(10) 168 | self.res.high() 169 | --------------------------------------------------------------------------------