From e3c2e7cbbc308d4305f084d77dbc72251e66e46e Mon Sep 17 00:00:00 2001 From: Andreas Goetz Date: Fri, 14 Dec 2012 19:38:25 +0100 Subject: [PATCH] Added I2C support --- adafruit/adafruitgfx.py | 37 +++-- gaugette/ssd1306.py | 318 ++++++++++++++++++++++----------------- setup.py | 2 +- test/test_adafruitgfx.py | 29 ++-- 4 files changed, 217 insertions(+), 169 deletions(-) diff --git a/adafruit/adafruitgfx.py b/adafruit/adafruitgfx.py index 0e2e69f..8b4cfe7 100644 --- a/adafruit/adafruitgfx.py +++ b/adafruit/adafruitgfx.py @@ -1,6 +1,6 @@ #---------------------------------------------------------------------- -# adafruitgfx.py from https://github.com/andig/AdaPi -# ported by Andreas Goetz, http://www.cpuidle.de +# adafruitgfx.py ported by Andreas Goetz, http://www.cpuidle.de +# https://github.com/andig/AdaPi # # This library is a pure-python port of the Adafruit GFX C library: # @@ -56,13 +56,13 @@ def draw_circle(self, x0, y0, r, color): while (x= 0): - y-=1 + y -=1 ddF_y += 2 - f += ddF_y + f += ddF_y - x+=1 + x +=1 ddF_x += 2 - f += ddF_x + f += ddF_x self.draw_pixel(x0 + x, y0 + y, color) self.draw_pixel(x0 - x, y0 + y, color) @@ -83,13 +83,13 @@ def draw_circle_helper(self, x0, y0, r, cornername, color): while (x= 0): - y-=1 + y -=1 ddF_y += 2 f += ddF_y - x +=1 + x +=1 ddF_x += 2 - f += ddF_x + f += ddF_x if (cornername & 0x4): self.draw_pixel(x0 + x, y0 + y, color) @@ -341,8 +341,8 @@ def fill_triangle (self, x0, y0, x1, y1, x2, y2, color): # def draw_char(self, x, y, unsigned char c, color, ubg, size): # if ((x >= self._width) || # Clip right # (y >= self._height) || # Clip bottom -# ((x + 5 * size - 1) < 0) || # Clip left -# ((y + 8 * size - 1) < 0)): # Clip top +# ((x + 5 * size - 1) < 0) || # Clip left +# ((y + 8 * size - 1) < 0)): # Clip top # return # # for (i=0; i<6; i+=1) @@ -393,14 +393,11 @@ def get_rotation(self): def set_rotation(self, x): - x %= 4; # cant be higher than 3 - self.rotation = x - if x in [0,2]: - self._width = WIDTH - self._height = HEIGHT + self.rotation = x % 4; # cant be higher than 3 + if self.rotation % 2 == 0: + self._width, self._height = WIDTH, HEIGHT else: - self._width = HEIGHT - self._height = WIDTH + self._width, self._height = HEIGHT, WIDTH def invert_display(self, i): @@ -430,8 +427,8 @@ def draw_text(self, x, y, string, size=1, space=1): p = ord(c) * font_cols for col in range(0,font_cols): mask = font_bytes[p] - p+=1 - py = y + p += 1 + py = y for row in range(0,8): for sy in range(0,size): px = x diff --git a/gaugette/ssd1306.py b/gaugette/ssd1306.py index 87c7ec1..bc519c5 100644 --- a/gaugette/ssd1306.py +++ b/gaugette/ssd1306.py @@ -2,6 +2,9 @@ # ssd1306.py from https://github.com/guyc/py-gaugette # ported by Guy Carpenter, Clearwater Software # +# Rebased to use the adafruit GFX library by Andreas Goetz +# https://github.com/andig/AdaPi +# # This library is for the Adafruit 128x32 SPI monochrome OLED # based on the SSD1306 driver. # http://www.adafruit.com/products/661 @@ -47,77 +50,20 @@ # GND -> GND #---------------------------------------------------------------------- -#import spidev -#import wiringpi -#import time -#import sys +import spidev +import wiringpi +import Adafruit_I2C import font5x8 import adafruit.adafruitgfx as adafruitgfx -class SSD1306(adafruitgfx.AdafruitGFX): - - # Class constants are externally accessible as gaugette.ssd1306.SSD1306.CONST - # or my_instance.CONST - - # TODO - insert underscores to rationalize constant names - - EXTERNAL_VCC = 0x1 - SWITCH_CAP_VCC = 0x2 - - SET_LOW_COLUMN = 0x00 - SET_HIGH_COLUMN = 0x10 - SET_MEMORY_MODE = 0x20 - SET_COL_ADDRESS = 0x21 - SET_PAGE_ADDRESS = 0x22 - RIGHT_HORIZ_SCROLL = 0x26 - LEFT_HORIZ_SCROLL = 0x27 - VERT_AND_RIGHT_HORIZ_SCROLL = 0x29 - VERT_AND_LEFT_HORIZ_SCROLL = 0x2A - DEACTIVATE_SCROLL = 0x2E - ACTIVATE_SCROLL = 0x2F - SET_START_LINE = 0x40 - SET_CONTRAST = 0x81 - CHARGE_PUMP = 0x8D - SEG_REMAP = 0xA0 - SET_VERT_SCROLL_AREA = 0xA3 - DISPLAY_ALL_ON_RESUME = 0xA4 - DISPLAY_ALL_ON = 0xA5 - NORMAL_DISPLAY = 0xA6 - INVERT_DISPLAY = 0xA7 - DISPLAY_OFF = 0xAE - DISPLAY_ON = 0xAF - COM_SCAN_INC = 0xC0 - COM_SCAN_DEC = 0xC8 - SET_DISPLAY_OFFSET = 0xD3 - SET_COM_PINS = 0xDA - SET_VCOM_DETECT = 0xDB - SET_DISPLAY_CLOCK_DIV = 0xD5 - SET_PRECHARGE = 0xD9 - SET_MULTIPLEX = 0xA8 - - MEMORY_MODE_HORIZ = 0x00 - MEMORY_MODE_VERT = 0x01 - MEMORY_MODE_PAGE = 0x02 - - # Device name will be /dev/spidev-{bus}.{device} - # dc_pin is the data/commmand pin. This line is HIGH for data, LOW for command. - # We will keep d/c low and bump it high only for commands with data - # reset is normally HIGH, and pulled LOW to reset the display - - def __init__(self, bus=0, device=0, dc_pin=1, reset_pin=2, buffer_rows=64, buffer_cols=128): +# +# text functions were moved to adafruitgfx +# +class SSD1306Virtual(adafruitgfx.AdafruitGFX): + def __init__(self, buffer_rows=64, buffer_cols=128): self.cols = 128 self.rows = 32 - self.dc_pin = dc_pin - self.reset_pin = reset_pin -# self.spi = spidev.SpiDev() -# self.spi.open(bus, device) -# self.spi.max_speed_hz = 500000 -# self.gpio = wiringpi.GPIO(wiringpi.GPIO.WPI_MODE_PINS) -# self.gpio.pinMode(self.reset_pin, self.gpio.OUTPUT) -# self.gpio.digitalWrite(self.reset_pin, self.gpio.HIGH) -# self.gpio.pinMode(self.dc_pin, self.gpio.OUTPUT) -# self.gpio.digitalWrite(self.dc_pin, self.gpio.LOW) self.font = font5x8.Font5x8 self.buffer_rows = buffer_rows self.buffer_cols = buffer_cols @@ -126,74 +72,12 @@ def __init__(self, bus=0, device=0, dc_pin=1, reset_pin=2, buffer_rows=64, buffe self.mem_bytes = self.rows * self.cols * 2 / 8 # total bytes in SSD1306 display ram self.buffer = [0] * (self.buffer_cols * self.bytes_per_col) - super(SSD1306, self).__init__(self.cols, self.rows) + super(SSD1306Virtual, self).__init__(self.cols, self.rows) -# def reset(self): -# self.gpio.digitalWrite(self.reset_pin, self.gpio.LOW) -# self.gpio.delay(10) # 10ms -# self.gpio.digitalWrite(self.reset_pin, self.gpio.HIGH) -# -# def command(self, *bytes): -# # already low -# # self.gpio.digitalWrite(self.dc_pin, self.gpio.LOW) -# self.spi.writebytes(list(bytes)) -# -# def data(self, bytes): -# self.gpio.digitalWrite(self.dc_pin, self.gpio.HIGH) -# self.spi.writebytes(bytes) -# self.gpio.digitalWrite(self.dc_pin, self.gpio.LOW) -# -# def begin(self, vcc_state = SWITCH_CAP_VCC): -# self.gpio.delay(1) # 1ms -# self.reset() -# self.command(self.DISPLAY_OFF) -# self.command(self.SET_DISPLAY_CLOCK_DIV, 0x80) -# self.command(self.SET_MULTIPLEX, 0x1F) -# self.command(self.SET_DISPLAY_OFFSET, 0x00) -# self.command(self.SET_START_LINE | 0x00) -# if (vcc_state == self.EXTERNAL_VCC): -# self.command(self.CHARGE_PUMP, 0x10) -# else: -# self.command(self.CHARGE_PUMP, 0x14) -# self.command(self.SET_MEMORY_MODE, 0x00) -# self.command(self.SEG_REMAP | 0x01) -# self.command(self.COM_SCAN_DEC) -# self.command(self.SET_COM_PINS, 0x02) -# self.command(self.SET_CONTRAST, 0x8f) -# if (vcc_state == self.EXTERNAL_VCC): -# self.command(self.SET_PRECHARGE, 0x22) -# else: -# self.command(self.SET_PRECHARGE, 0xF1) -# self.command(self.SET_VCOM_DETECT, 0x40) -# self.command(self.DISPLAY_ALL_ON_RESUME) -# self.command(self.NORMAL_DISPLAY) -# self.command(self.DISPLAY_ON) - def clear_display(self): for i in range(0,len(self.buffer)): self.buffer[i] = 0 - def invert_display(self): - self.command(self.INVERT_DISPLAY) - - def normal_display(self): - self.command(self.NORMAL_DISPLAY) - - def display(self): - self.command(self.SET_MEMORY_MODE, self.MEMORY_MODE_VERT) - self.command(self.SET_COL_ADDRESS, 0, 127) - start = self.col_offset * self.bytes_per_col - length = self.mem_bytes # automatically trucated if few bytes available in self.buffer - self.data(self.buffer[start:start+length]) - - def display_cols(self, start_col, count): - self.command(self.SET_MEMORY_MODE, self.MEMORY_MODE_VERT) - self.command(self.SET_COL_ADDRESS, start_col, (start_col + count) % self.cols) - start = (self.col_offset + start_col) * self.bytes_per_col - length = count * self.bytes_per_col - self.data(self.buffer[start:start+length]) - - # Diagnostic print of the memory buffer to stdout def dump_buffer(self): for y in range(0, self.buffer_rows): @@ -227,18 +111,18 @@ def dump_buffer(self): # 7 15 # - def draw_pixel(self, x, y, on=True): + def draw_pixel(self, x, y, color=1): if (x<0 or x>=self.buffer_cols or y<0 or y>=self.buffer_rows): return mem_col = x mem_row = y / 8 bit_mask = 1 << (y % 8) offset = mem_row + self.buffer_rows/8 * mem_col - self.draw_fast_helper(offset, bit_mask, on) + self.draw_fast_helper(offset, bit_mask, color) # helper function for switching bits on/off - def draw_fast_helper(self, offset, bit_mask, color): + def draw_fast_helper(self, offset, bit_mask, color=1): if color: self.buffer[offset] |= bit_mask else: @@ -255,22 +139,21 @@ def draw_fast_vline(self, x, y, h, color=1): mem_row_start = y / 8 mem_row_end = (y+h) / 8 x *= self.buffer_rows/8 - # initial offset + # line start settings offset = mem_row_start + x + bit_mask = (0xff << (y % 8)) & 0xff # special intra-byte cases mini_line = 8 - (y % 8) - h if mini_line > 0: - bit_mask = (0xff << (y % 8)) & 0xff bit_mask = ((bit_mask << mini_line) & 0xFF) >> mini_line self.draw_fast_helper(offset, bit_mask, color) return - + # line start if (y % 8) != 0: - bit_mask = (0xff << (y % 8)) & 0xff - mem_row_start += 1 self.draw_fast_helper(offset, bit_mask, color) + mem_row_start += 1 # line end if (y+h) % 8 != 0: @@ -289,7 +172,6 @@ def draw_fast_vline(self, x, y, h, color=1): # overwritten from adafruitgfx def draw_fast_hline(self, x, y, w, color=1): - # stupidest version - update in subclasses if desired! if (x<0 or x+w>self.buffer_cols or y<0 or y>=self.buffer_rows): return mem_row = y / 8 @@ -302,3 +184,165 @@ def draw_fast_hline(self, x, y, w, color=1): # use fillrect instead def clear_block(self, x0,y0,dx,dy): fill_rect(x0,y0,dx,dy,0) + + +class SSD1306Physical(SSD1306Virtual): + + # Class constants are externally accessible as gaugette.ssd1306.SSD1306.CONST + # or my_instance.CONST + + # TODO - insert underscores to rationalize constant names + + EXTERNAL_VCC = 0x1 + SWITCH_CAP_VCC = 0x2 + + SET_LOW_COLUMN = 0x00 + SET_HIGH_COLUMN = 0x10 + SET_MEMORY_MODE = 0x20 + SET_COL_ADDRESS = 0x21 + SET_PAGE_ADDRESS = 0x22 + RIGHT_HORIZ_SCROLL = 0x26 + LEFT_HORIZ_SCROLL = 0x27 + VERT_AND_RIGHT_HORIZ_SCROLL = 0x29 + VERT_AND_LEFT_HORIZ_SCROLL = 0x2A + DEACTIVATE_SCROLL = 0x2E + ACTIVATE_SCROLL = 0x2F + SET_START_LINE = 0x40 + SET_CONTRAST = 0x81 + CHARGE_PUMP = 0x8D + SEG_REMAP = 0xA0 + SET_VERT_SCROLL_AREA = 0xA3 + DISPLAY_ALL_ON_RESUME = 0xA4 + DISPLAY_ALL_ON = 0xA5 + NORMAL_DISPLAY = 0xA6 + INVERT_DISPLAY = 0xA7 + DISPLAY_OFF = 0xAE + DISPLAY_ON = 0xAF + COM_SCAN_INC = 0xC0 + COM_SCAN_DEC = 0xC8 + SET_DISPLAY_OFFSET = 0xD3 + SET_COM_PINS = 0xDA + SET_VCOM_DETECT = 0xDB + SET_DISPLAY_CLOCK_DIV = 0xD5 + SET_PRECHARGE = 0xD9 + SET_MULTIPLEX = 0xA8 + + MEMORY_MODE_HORIZ = 0x00 + MEMORY_MODE_VERT = 0x01 + MEMORY_MODE_PAGE = 0x02 + + # Reset is normally HIGH, and pulled LOW to reset the display. + + def __init__(self, bus=0, device=0, reset_pin=2, buffer_rows=64, buffer_cols=128): + self.reset_pin = reset_pin + self.gpio = wiringpi.GPIO(wiringpi.GPIO.WPI_MODE_PINS) + self.gpio.pinMode(self.reset_pin, self.gpio.OUTPUT) + self.gpio.digitalWrite(self.reset_pin, self.gpio.HIGH) + super(SSD1306Physical, self).__init__(buffer_rows, buffer_cols) + + def reset(self): + self.gpio.digitalWrite(self.reset_pin, self.gpio.LOW) + self.gpio.delay(10) # 10ms + self.gpio.digitalWrite(self.reset_pin, self.gpio.HIGH) + + def command(self, *bytes): + pass + + def data(self, bytes): + pass + + def begin(self, vcc_state = SWITCH_CAP_VCC): + self.gpio.delay(1) # 1ms + self.reset() + self.command(self.DISPLAY_OFF) + self.command(self.SET_DISPLAY_CLOCK_DIV, 0x80) + self.command(self.SET_MULTIPLEX, 0x1F) + self.command(self.SET_DISPLAY_OFFSET, 0x00) + self.command(self.SET_START_LINE | 0x00) + if (vcc_state == self.EXTERNAL_VCC): + self.command(self.CHARGE_PUMP, 0x10) + else: + self.command(self.CHARGE_PUMP, 0x14) + self.command(self.SET_MEMORY_MODE, 0x00) + self.command(self.SEG_REMAP | 0x01) + self.command(self.COM_SCAN_DEC) + self.command(self.SET_COM_PINS, 0x02) + self.command(self.SET_CONTRAST, 0x8f) + if (vcc_state == self.EXTERNAL_VCC): + self.command(self.SET_PRECHARGE, 0x22) + else: + self.command(self.SET_PRECHARGE, 0xF1) + self.command(self.SET_VCOM_DETECT, 0x40) + self.command(self.DISPLAY_ALL_ON_RESUME) + self.command(self.NORMAL_DISPLAY) + self.command(self.DISPLAY_ON) + + def invert_display(self): + self.command(self.INVERT_DISPLAY) + + def normal_display(self): + self.command(self.NORMAL_DISPLAY) + + def display(self): + self.command(self.SET_MEMORY_MODE, self.MEMORY_MODE_VERT) + self.command(self.SET_COL_ADDRESS, 0, 127) + start = self.col_offset * self.bytes_per_col + length = self.mem_bytes # automatically truncated if few bytes available in self.buffer + self.data(self.buffer[start:start+length]) + + def display_cols(self, start_col, count): + self.command(self.SET_MEMORY_MODE, self.MEMORY_MODE_VERT) + self.command(self.SET_COL_ADDRESS, start_col, (start_col + count) % self.cols) + start = (self.col_offset + start_col) * self.bytes_per_col + length = count * self.bytes_per_col + self.data(self.buffer[start:start+length]) + +class SSD1306_SPI(SSD1306Physical): + + # Device name will be /dev/spidev-{bus}.{device} + # dc_pin is the data/commmand pin. This line is HIGH for data, LOW for command. + # We will keep d/c low and bump it high only for commands with data. + + def __init__(self, bus=0, device=0, dc_pin=1, reset_pin=2, buffer_rows=64, buffer_cols=128): + self.dc_pin = dc_pin + self.spi = spidev.SpiDev() + self.spi.open(bus, device) + self.spi.max_speed_hz = 500000 + self.gpio.pinMode(self.dc_pin, self.gpio.OUTPUT) + self.gpio.digitalWrite(self.dc_pin, self.gpio.LOW) + super(SSD1306_SPI, self).__init__(bus, device, reset_pin, buffer_rows, buffer_cols) + + def reset(self): + self.gpio.digitalWrite(self.reset_pin, self.gpio.LOW) + self.gpio.delay(10) # 10ms + self.gpio.digitalWrite(self.reset_pin, self.gpio.HIGH) + + def command(self, *bytes): + # already low + # self.gpio.digitalWrite(self.dc_pin, self.gpio.LOW) + self.spi.writebytes(list(bytes)) + + def data(self, bytes): + self.gpio.digitalWrite(self.dc_pin, self.gpio.HIGH) + self.spi.writebytes(bytes) + self.gpio.digitalWrite(self.dc_pin, self.gpio.LOW) + +class SSD1306_I2C(SSD1306Physical): + + # Device name will be /dev/i2c-{bus}.{device} + # Reset is normally HIGH, and pulled LOW to reset the display. + + I2C_CONTROL = 0x00 + I2C_DATA = 0x40 + + def __init__(self, bus=0, device=0, reset_pin=2, buffer_rows=64, buffer_cols=128): + self.i2c = Adafruit_I2C.Adafruit_I2C(device) + super(SSD1306_I2C, self).__init__(bus, device, reset_pin, buffer_rows, buffer_cols) + + def command(self, *bytes): + # already low + # self.gpio.digitalWrite(self.dc_pin, self.gpio.LOW) + self.i2c.writeList(self.I2C_CONTROL, list(bytes)) + + def data(self, bytes): + self.i2c.writeList(self.I2C_DATA, list(bytes)) diff --git a/setup.py b/setup.py index 905bdf6..52322a9 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ from distutils.core import setup -setup(name='gaugette', +setup(name='adapi', version='0.1', description='Python port of various libraries for use with the Raspberry Pi', author='Andreas Goetz', diff --git a/test/test_adafruitgfx.py b/test/test_adafruitgfx.py index 8e63d80..6346d97 100644 --- a/test/test_adafruitgfx.py +++ b/test/test_adafruitgfx.py @@ -6,21 +6,28 @@ #ssd1306.draw_line(0,0,ssd1306.cols,ssd1306.rows,1) #ssd1306.draw_circle(16,18,12,1) -for i in range(0,16): - ssd1306.draw_fast_vline(i, i, ssd1306.buffer_rows-2*i) - ssd1306.draw_fast_vline(i+16, i, i+1) - ssd1306.draw_fast_vline(i+32, 0, i+1) - -#for i in range(0,16): -# ssd1306.draw_fast_hline(i, i, ssd1306.buffer_cols-2*i) +ssd1306.draw_fast_vline(64, 0, 15) + +# border-line cases +y=3 +dh=0 +for i in range(0,8): + ssd1306.draw_fast_vline(i, y+0, i+1+dh) + ssd1306.draw_fast_vline(8+i, y+i, 8-i+dh) + ssd1306.draw_fast_vline(16+i, i, 1) + ssd1306.draw_fast_vline(24+i, i, 2) + ssd1306.draw_fast_vline(32+i, i, 3) + ssd1306.draw_fast_vline(48+i, i, 15-2*i) + pass + #ssd1306.draw_fast_vline(1, 1, ssd1306.buffer_rows-2) #ssd1306.draw_fast_vline(0, 0, ssd1306.buffer_rows) -ssd1306.fill_rect(110, 8, 3, 3, 1) +#ssd1306.fill_rect(110, 8, 3, 3, 1) -for i in range(0,4): - ssd1306.draw_fast_hline(110, i, 10) - ssd1306.draw_fast_vline(120-i, 0, ssd1306.rows) +#for i in range(0,4): +# ssd1306.draw_fast_hline(110, i, 10) +# ssd1306.draw_fast_vline(120-i, 0, ssd1306.rows) #x = ssd1306.draw_text(30,0,"Test") #ssd1306.draw_text3(x,0,"Test", fonts.arial_regular_10)