From a1a7198dbafb366e22bf596d5364c5bee4af08d5 Mon Sep 17 00:00:00 2001 From: Guy Carver Date: Sat, 4 Nov 2017 12:46:53 -0400 Subject: [PATCH] Added OLED and sevenseg display libraries. 128x64 i2c OLED and tm1637 7 segment display. --- Lib/OLED.py | 347 ++++++++++++++++++++++++++++++++++++++++++++++++ Lib/sevenseg.py | 170 ++++++++++++++++++++++++ main.py | 77 ++++++++--- 3 files changed, 576 insertions(+), 18 deletions(-) create mode 100644 Lib/OLED.py create mode 100644 Lib/sevenseg.py diff --git a/Lib/OLED.py b/Lib/OLED.py new file mode 100644 index 0000000..2e47c1a --- /dev/null +++ b/Lib/OLED.py @@ -0,0 +1,347 @@ +#driver for Sainsmart 1.8" TFT display ST7735 +#Translated by Guy Carver from the ST7735 sample code. + +#NOTE: This current code will set the pixel at 0,0 but the scrolling will not scroll it. Don't know if it's software causing it or not. + +import pyb + +_I2C_ADDRESS = const(0x3C) # 011110+SA0+RW - 0x3C or 0x3D + +_SETCONTRAST = const(0x81) +_DISPLAYALLON_RESUME = const(0xA4) +_DISPLAYALLON = const(0xA5) +_NORMALDISPLAY = const(0xA6) +_INVERTDISPLAY = const(0xA7) +_DISPLAYOFF = const(0xAE) +_DISPLAYON = const(0xAF) + +_SETDISPLAYOFFSET = const(0xD3) +_SETCOMPINS = const(0xDA) + +_SETVCOMDETECT = const(0xDB) + +_SETDISPLAYCLOCKDIV = const(0xD5) +_SETPRECHARGE = const(0xD9) + +_SETMULTIPLEX = const(0xA8) + +_SETLOWCOLUMN = const(0x00) +_SETHIGHCOLUMN = const(0x10) + +_SETSTARTLINE = const(0x40) + +_MEMORYMODE = const(0x20) + +_COMSCANINC = const(0xC0) +_COMSCANDEC = const(0xC8) + +_SEGREMAP = const(0xA0) + +_CHARGEPUMP = const(0x8D) + +_EXTRNALVCC = const(0x1) +_SWITCHAPVCC = const(0x2) + +_ACTIVATE_SCROLL = const(0x2F) +_DEACTIVATE_SCROLL = const(0x2E) +_SET_VERTICAL_SCROLL_AREA = const(0xA3) +_RIGHT_HORIZONTAL_SCROLL = const(0x26) +_LEFT_HORIZONTAL_SCROLL = const(0x27) +_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL = const(0x29) +_VERTICAL_AND_LEFT_HORIZONTAL_SCROLL = const(0x2A) + +#Buffer layout in bits. 128 columns by 64 rows. +#Each byte represents 8 pixels in a row. +# Column +# R 0 8 10 ... 3F8 +# O 1 9 11 ... 3F9 +# W 2 A 12 ... 3FA +# 3 B 13 ... 3FB +# 4 C 14 ... 3FC +# 5 D 15 ... 3FD +# 6 E 16 ... 3FE +# 7 F 17 ... 3FF +# 400 408 +# 401 409 +# 402 40A +# 403 40B +# 404 40C +# 405 40D +# 406 40E +# 407 40F + +class OLED(object) : + """diyMall OLED 9.6 128x64 pixel display driver.""" + + def __init__( self, aLoc ) : + """aLoc I2C pin location is either 1 for 'X' or 2 for 'Y'.""" + self._size = (128, 64) + self._rotation = 0 + self._inverted = False + self._on = False + self.i2c = pyb.I2C(aLoc, pyb.I2C.MASTER, baudrate = 200000) + self.bytes = self.size[0] * self.size[1] // 8 + self.buffer = bytearray(self.bytes + 1) + self.buffer[0] = 0x40 #data write start command at very start of buffer. + + self.data = bytearray(2) + self.data[0] = 0 + + self.command = _DISPLAYOFF + self.command = _SETDISPLAYCLOCKDIV + self.command = 0x80 #suggested ratio. + self.command = _SETMULTIPLEX + self.command = 0x3F + self.command = _SETDISPLAYOFFSET + self.command = 0x0 + self.command = _SETSTARTLINE #| 0x0 + self.command = _CHARGEPUMP + self.command = 0x14 #No external power. + self.command = _MEMORYMODE + self.command = 0x00 #Act like ks0108 + self.command = _SEGREMAP + 0x01 + self.command = _COMSCANDEC + self.command = _SETCOMPINS + self.command = 0x12 + self.command = _SETCONTRAST + self.command = 0xCF + self.command = _SETPRECHARGE + self.command = 0xF1 + self.command = _SETVCOMDETECT + self.command = 0x40 + self.command = _DISPLAYALLON_RESUME + self.command = _NORMALDISPLAY + self.command = 0XB0 + self.command = 0x10 + self.command = 0x01 #Set original position to 0,0. + + self.on = True + + self.display() + + @property + def size( self ) : return self._size + + @property + def rotation( self ) : return self._rotation + + @rotation.setter + def rotation( self, aValue ) : + self._rotation = aValue & 3 + + def write( self, aValue ) : + self.i2c.send(aValue, _I2C_ADDRESS) + + @property + def command( self ) : return 0 + + @command.setter + def command( self, aValue ) : + self.data[1] = aValue + self.write(self.data) + + @property + def on( self ) : return self._on + + @on.setter + def on( self, aTF ) : + if aTF != self._on : + self._on = aTF + '''Turn display on or off.''' + self.command = _DISPLAYON if aTF else _DISPLAYOFF + + @property + def invert( self ) : return self._inverted + + @invert.setter + def invert( self, aTF ) : + if aTF != self._inverted : + self._inverted = aTF + self.command = _INVERTDISPLAY if aTF else _NORMALDISPLAY + + @micropython.native + def fill( self, aValue ) : + for x in range(1, self.bytes + 1): + self.buffer[x] = aValue; + + def clear( self ) : + self.fill(0) + + @micropython.native + def pixel( self, aPos, aOn ) : + '''Draw a pixel at the given position''' + x, y = aPos + w, h = self.size + if 0 <= x < w and 0 <= y < h: + if self._rotation == 1: + aPos = (w - y - 1, x) + elif self._rotation == 2: + aPos = (w - x - 1, h - y - 1) + elif self._rotation == 3: + aPos = (y, h - x - 1) + + bit = 1 << (aPos[1] % 8) + index = (aPos[0] + (aPos[1] // 8) * w) + 1 + + if aOn : + self.buffer[index] |= bit + else : + self.buffer[index] &= not bit + + @micropython.native + def line( self, aStart, aEnd, aOn ) : + '''Draws a line from aStart to aEnd in the given color. Vertical or horizontal + lines are forwarded to vline and hline.''' + px, py = aStart + ex, ey = aEnd + dx = ex - px + dy = ey - py + inx = 1 if dx > 0 else -1 + iny = 1 if dy > 0 else -1 + + dx = abs(dx) + dy = abs(dy) + if (dx >= dy): + dy <<= 1 + e = dy - dx + dx <<= 1 + while (px != ex): + self.pixel((px, py), aOn) + if (e >= 0): + py += iny + e -= dx + e += dy + px += inx + else: + dx <<= 1 + e = dx - dy + dy <<= 1 + while (py != ey): + self.pixel((px, py), aOn) + if (e >= 0): + px += inx + e -= dy + e += dx + py += iny + +# @micropython.native + def fillrect( self, aStart, aSize, aOn ) : + '''Draw a filled rectangle. aStart is the smallest coordinate corner + and aSize is a tuple indicating width, height.''' + x, y = aStart + w, h = aSize + ex = x + w + for i in range(y, y + h): + self.line((x, i), (ex, i), aOn) + + @micropython.native + def text( self, aPos, aString, aColor, aFont, aSize = 1 ) : + '''Draw a text at the given position. If the string reaches the end of the + display it is wrapped to aPos[0] on the next line. aSize may be an integer + which will size the font uniformly on w,h or a or any type that may be + indexed with [0] or [1].''' + + if aFont == None: + return + + #Make a size either from single value or 2 elements. + if (type(aSize) == int) or (type(aSize) == float): + wh = (aSize, aSize) + else: + wh = aSize + + px, py = aPos + width = wh[0] * aFont["Width"] + 1 + for c in aString: + self.char((px, py), c, aColor, aFont, wh) + px += width + #We check > rather than >= to let the right (blank) edge of the + # character print off the right of the screen. + if px + width > self._size[0]: + py += aFont["Height"] * wh[1] + 1 + px = aPos[0] + + @micropython.native + def char( self, aPos, aChar, aOn, aFont, aSizes ) : + '''Draw a character at the given position using the given font and color. + aSizes is a tuple with x, y as integer scales indicating the + # of pixels to draw for each pixel in the character.''' + + if aFont == None: + return + + startchar = aFont['Start'] + endchar = aFont['End'] + + ci = ord(aChar) + if (startchar <= ci <= endchar): + fontw = aFont['Width'] + fonth = aFont['Height'] + ci = (ci - startchar) * fontw + + charA = aFont["Data"][ci:ci + fontw] + px = aPos[0] + if aSizes[0] <= 1 and aSizes[1] <= 1 : + for c in charA : + py = aPos[1] + for r in range(fonth) : + if c & 0x01 : + self.pixel((px, py), aOn) + py += 1 + c >>= 1 + px += 1 + else: + for c in charA : + py = aPos[1] + for r in range(fonth) : + if c & 0x01 : + self.fillrect((px, py), aSizes, aOn) + py += aSizes[1] + c >>= 1 + px += aSizes[0] + + def doscrollLR( self, start, stop, aDir ) : + self.command = aDir + self.command = 0x00 + self.command = start + self.command = 0x00 + self.command = stop + self.command = 0x01 + self.command = 0xFF + self.command = _ACTIVATE_SCROLL + + def startscrollright( self, start, stop ) : + self.doscrollLR(start, stop, _RIGHT_HORIZONTAL_SCROLL) + + def startscrollleft( self, start, stop ) : + self.doscrollLR(start, stop, _LEFT_HORIZONTAL_SCROLL) + + def doscrollDiag( self, start, stop, aDir ) : + self.command = _SET_VERTICAL_SCROLL_AREA + self.command = 0x00 + self.command = self.size()[1] + self.command = aDir + self.command = 0x00 + self.command = start + self.command = 0x00 + self.command = stop + self.command = 0x01 + self.command = _ACTIVATE_SCROLL + + def startscrolldiagright( self, start, stop ) : + self.doscrollDiag(start, stop, _VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL) + + def startscrolldiagleft( self, start, stop ) : + self.doscrollDiag(start, stop, _VERTICAL_AND_LEFT_HORIZONTAL_SCROLL) + + def stopscroll( self ) : + self.command = _DEACTIVATE_SCROLL + + def display( self ) : + self.command = _SETLOWCOLUMN #| 0x00 + self.command = _SETHIGHCOLUMN #| 0x00 + self.command = _SETSTARTLINE #| 0x00 + #buffer starts with 0x40 in 1st byte which is the command to start the buffer write. + self.write(self.buffer) + + diff --git a/Lib/sevenseg.py b/Lib/sevenseg.py new file mode 100644 index 0000000..df3c190 --- /dev/null +++ b/Lib/sevenseg.py @@ -0,0 +1,170 @@ + +from machine import Pin +from time import sleep_us, time, localtime +import pyb + + +class sevenseg(object): + """docstring for 7seg""" + + _CMD1 = const(0x40) + _CMD2 = const(0xC0) + _CMD3 = const(0x80) + + # A + # --- + # F | | B + # -G- + # E | | C + # --- + # D + # XGFEDCBA + _numbertable = [ + 0b00111111, #0 + 0b00000110, #1 + 0b01011011, #2 + 0b01001111, #3 + 0b01100110, #4 + 0b01101101, #5 + 0b01111101, #6 + 0b00000111, #7 + 0b01111111, #8 + 0b01101111, #9 + 0b01110111, #A + 0b01111100, #B + 0b00111001, #C + 0b01011110, #D + 0b01111001, #E + 0b01110001, #F + 0b00000000, #space + 0b01000000 #dash + ] + + def __init__(self, clk, dio ): + self._clk = pyb.Pin(clk, pyb.Pin.IN) + self._dio = pyb.Pin(dio, pyb.Pin.IN) + self._brightness = 0 + self._colon = False + self._buffer = bytearray(4) + + self._clk.init(Pin.IN) + self._dio.init(Pin.IN) + self._clk(0) + self._dio(0) + self.brightness = 7 + + @property + def colon( self ) : return self._colon + + @colon.setter + def colon( self, aValue ) : + self._colon = aValue + + @property + def brightness( self ) : return self._brightness + + @brightness.setter + def brightness( self, aValue ) : + self._brightness = aValue & 0xF + self._write_comm1() + self._write_comm3() + + def _start(self): + self._dio.init(Pin.OUT) + sleep_us(50) + + def _stop(self): + self._dio.init(Pin.OUT) + sleep_us(50) + self._clk.init(Pin.IN) + sleep_us(50) + self._dio.init(Pin.IN) + sleep_us(50) + + def _write_comm1(self): + self._start() + self._write_byte(_CMD1) + self._stop() + + def _write_comm3(self): + self._start() + self._write_byte(_CMD3 + self._brightness + 7) + self._stop() + + def _write_byte(self, b): + # send each bit + for i in range(8): + self._clk.init(Pin.OUT) + sleep_us(50) + self._dio.init(Pin.IN if b & 1 else Pin.OUT) + sleep_us(50) + self._clk.init(Pin.IN) + sleep_us(50) + b >>= 1 + self._clk.init(Pin.OUT) + sleep_us(50) + self._clk.init(Pin.IN) + sleep_us(50) + self._clk.init(Pin.OUT) + sleep_us(50) + + def clear( self ) : + for v in self._buffer: + v = 0 + + def display( self ) : + if self.colon : + self._buffer[1] |= 0x80 + else: + self._buffer[1] &= ~0x80 + + self._write_comm1() + self._start() + self._write_byte(_CMD2) + for b in self._buffer: + self._write_byte(b) + self._stop() + self._write_comm3() + + def data( self, aIndex, aValue ) : + self._buffer[aIndex] = aValue + + def digit( self, aIndex, aValue ) : + if 0 <= aValue < len(self._numbertable) : + self.data(aIndex, self._numbertable[aValue]) + + def char(self, aIndex, char): + """Display a character 0-9, a-f, space or dash.""" + o = ord(char) + c = -1 + # space + if o == 32: + c = 16 + # dash + if o == 45: + c = 17 + # uppercase A-F + if 65 <= o <= 70: + c = o - 55 + # lowercase a-f + if 97 <= o <= 102: + c = o - 87 + # 0-9 + if 48 <= o <= 57: + c = o - 48 + + self.digit(aIndex, c) + +def clock(tm) : + rtc = pyb.RTC() + while True: + t = rtc.datetime() + tm.colon = t[6] & 1 + tm.digit(0, t[4] // 10) + tm.digit(1, t[4] % 10) + tm.digit(2, t[5] // 10) + tm.digit(3, t[5] % 10) + tm.display() + +# sleep(1 - time() % 1) + diff --git a/main.py b/main.py index 0914077..d0173a8 100644 --- a/main.py +++ b/main.py @@ -1,27 +1,34 @@ # main.py -- put your code here! # from seriffont import * -# from sysfont import * +#from sysfont import * # from terminalfont import * -pyt = 0 -if pyt : - from ST7735 import makeg - t = makeg() -else: - t = pyb.TFT("x", "X1", "X2") - t.initg() +import gc -# t.fill(0) +print(gc.mem_free()) + +def reboot( ) : pyb.hard_reset() +#pyt = 0 +#if pyt : +# from ST7735 import makeg +# t = makeg() +#else: +# t = pyb.TFT("x", "X1", "X2") +# t.initg() +# +#t.fill(0) +# +#print(gc.mem_free()) # import TFT # TFT.run(t) # Display animated circle on TFT ------------------------- -# from bombs import bomber -# t.rotation(2) -# b = bomber(t) -# b.run() +#from bombs import bomber +#t.rotation(2) +#b = bomber(t) +#b.run() # Accelerometer display ---------------------------------- @@ -84,10 +91,44 @@ else: # f = ExtInt("Y2", ExtInt.IRQ_RISING, pyb.Pin.PULL_UP, cb) -from ESP8266 import WIFI +# WIFI board ---------------------------------------- + +# from ESP8266 import WIFI + +# v = WIFI(6) + +# from pyb import Pin +# p = Pin("X11", Pin.IN, Pin.PULL_DOWN) +# def v(): return p.value() + +# Accelerometer board ---------------------------------------- + +#from GY521 import Accel +# +print(gc.mem_free()) +# +#a = Accel(1) +#def d(): return a.acceltemprot +# +#print(gc.mem_free()) + +#import gyreport +# +#print(gc.mem_free()) +# +#m = gyreport.motion(1, t) +# +#print(gc.mem_free()) +# +#m.run() +# +#print(gc.mem_free()) + +def do( ) : + return pyb.MPU6050('x', 0) + +#def prnt( dev, font ) : +# dev.text((10, 10), "Hello!", 1, font, 1) +# dev.text((10, 30), "Hi Again?", 1, font, 3) -v = WIFI(6) -from pyb import Pin -p = Pin("X11", Pin.IN, Pin.PULL_DOWN) -def v(): return p.value()