class WS2812: # values to put inside the SPI register for each bit of color bits = (0xE0, 0xFC) def __init__(self, nleds=1): # params: * nleds = number of LEDs self.buf = bytearray(nleds * 3 * 8) # 1 byte per bit # spi init # bus 0, 8MHz => 125 ns by bit, each transfer is 10 cycles long due to the # CC3200 spi dead cycles, therefore => 125*10=1.25 us as required by the # WS2812. Don't do the automatic pin assigment since we only need MOSI self.spi = SPI(0, SPI.MASTER, baudrate=8000000, pins=None) Pin('GP16', mode=Pin.ALT, pull=Pin.PULL_DOWN, alt=7) # turn all the LEDs off self.show([]) def _send(self): # send the buffer over SPI. disable_irq() self.spi.write(self.buf) enable_irq() def show(self, data): # show RGB data on the LEDs. Expected data = [(R, G, B), ...] where # R, G and B are the intensities of the colors in the range 0 to 255. # the number of tuples may be less than the number of connected LEDs. self.fill(data) self._send() def update(self, data, start=0): # fill a part of the buffer with RGB data. # Returns the index of the first unfilled LED. buf = self.buf bits = self.bits idx = start * 24 for colors in data: for c in (1, 0, 2): # the WS2812 requires GRB color = colors[c] for bit in range (0, 8): buf[idx + bit] = bits[color >> (7 - bit) & 0x01] idx += 8 return idx // 24 def fill(self, data): # fill the buffer with RGB data. # all the LEDs after the data are turned off. end = self.update(data) buf = self.buf off = self.bits[0] for idx in range(end * 24, len(self.buf)): buf[idx] = off
class LedStrip: """ Driver for APA102C ledstripes (Adafruit Dotstars) on the Wipy Connect CLK <---> GP14 (yellow cable) DI <---> GP16 (green cable) """ def __init__(self, leds): """ :param int leds: number of LEDs on strio """ self.ledcount = leds self.buffersize = self.ledcount * 4 self.buffer = bytearray(self.ledcount * 4) self.emptybuffer = bytearray(self.ledcount * 4) for i in range(0, self.buffersize, 4): self.emptybuffer[i] = 0xFF self.emptybuffer[i + 1] = 0x0 self.emptybuffer[i + 2] = 0x0 self.emptybuffer[i + 3] = 0x0 self.startframe = bytes([0x00, 0x00, 0x00, 0x00]) self.endframe = bytes([0xFF, 0xFF, 0xFF, 0xFF]) self.spi = SPI(0, mode=SPI.MASTER, baudrate=4000000, polarity=0, phase=0, bits=8, firstbit=SPI.MSB) self.clear() def clear(self): self.buffer = self.emptybuffer[:] def set(self, led, red=0, green=0, blue=0, bri=0x1F): """ :param int led: Index of LED :param int red, green, blue: 0-255 :param int bri: Brightness 0-31 """ if led > self.ledcount: led %= self.ledcount if led < 0: led += self.ledcount frameheader = (0x07 << 5) | bri offset = led * 4 self.buffer[offset] = frameheader self.buffer[offset + 1] = blue self.buffer[offset + 2] = green self.buffer[offset + 3] = red def send(self): self.spi.write(self.startframe + self.buffer)
class DotStars: def __init__(self, leds): self.ledcount = leds self.buffersize = self.ledcount * 4 self.buffer = bytearray(self.ledcount * 4) self.emptybuffer = bytearray(self.ledcount * 4) for i in range(0, self.buffersize, 4): self.emptybuffer[i] = 0xff self.emptybuffer[i + 1] = 0x0 self.emptybuffer[i + 2] = 0x0 self.emptybuffer[i + 3] = 0x0 self.startframe = bytes([0x00, 0x00, 0x00, 0x00]) self.endframe = bytes([0xff, 0xff, 0xff, 0xff]) self.spi = SPI(0, mode=SPI.MASTER, baudrate=8000000, polarity=0, phase=0,bits=8, firstbit=SPI.MSB) self.clearleds() #init empty self.buffer def clearleds(self): self.buffer = self.emptybuffer[:] def setled(self, led, red=0, green=0, blue=0, bri=0x1f): if (led > self.ledcount): led=led % self.ledcount if (led < 0): led = self.ledcount + led frameheader = (0x07 << 5) | bri offset = led * 4 self.buffer[offset] = frameheader self.buffer[offset + 1] = blue self.buffer[offset + 2] = green self.buffer[offset + 3] = red def send(self): #self.spi.write(self.startframe + self.buffer + self.endframe) self.spi.write(self.startframe + self.buffer)
class EPD(object): MAX_READ = 45 SW_NORMAL_PROCESSING = 0x9000 EP_FRAMEBUFFER_SLOT_OVERRUN = 0x6a84 # too much data fed in EP_SW_INVALID_LE = 0x6c00 # Wrong expected length EP_SW_INSTRUCTION_NOT_SUPPORTED = 0x6d00 # bad instr EP_SW_WRONG_PARAMETERS_P1P2 = 0x6a00 EP_SW_WRONG_LENGTH = 0x6700 DEFAULT_SLOT=0 # always the *oldest*, should wear-level then I think def __init__(self, debug=False, baud=100000): # From datasheet # Bit rate – up to 12 MHz1 # ▪ Polarity – CPOL = 1; clock transition high-to-low on the leading edge and low-to-high on the # trailing edge # ▪ Phase – CPHA = 1; setup on the leading edge and sample on the trailing edge # ▪ Bit order – MSB first # ▪ Chip select polarity – active low self.spi = SPI(0) try: self.spi.init(mode=SPI.MASTER, baudrate=baud, bits=8, polarity=1, phase=1, firstbit=SPI.MSB, pins=('GP31', 'GP16', 'GP30')) # CLK, MOSI, MISO except AttributeError: self.spi.init(baudrate=baud, bits=8, polarity=1, phase=1, firstbit=SPI.MSB, pins=('GP31', 'GP16', 'GP30')) # CLK, MOSI, MISO # These are all active low! self.tc_en_bar = Pin('GP4', mode=Pin.OUT) self.disable() self.tc_busy_bar = Pin('GP5', mode=Pin.IN) self.tc_busy_bar.irq(trigger=Pin.IRQ_RISING) # Wake up when it changes self.tc_cs_bar = Pin('GP17', mode=Pin.ALT, alt=7) self.debug = debug def enable(self): self.tc_en_bar.value(0) # Power up time.sleep_ms(5) while self.tc_busy_bar() == 0: machine.idle() # will it wake up here? # /tc_busy goes high during startup, low during init, then high when not busy def disable(self): self.tc_en_bar.value(1) # Off def send_command(self, ins, p1, p2, data=None, expected=None): # These command variables are always sent cmd = struct.pack('3B', ins, p1, p2) # Looks like data is only sent with the length (Lc) if data: assert len(data) <= 251 # Thus speaks the datasheet cmd += struct.pack('B', len(data)) cmd += data # Expected data is either not present at all, 0 for null-terminated, or a number for fixed if expected is not None: cmd += struct.pack('B', expected) if self.debug: print("Sending: " + hexlify(cmd).decode()) self.spi.write(cmd) # Wait for a little while time.sleep_us(15) # This should take at most 14.5us while self.tc_busy_bar() == 0: machine.idle() # Request a response if expected is not None: if expected > 0: result_bytes = self.spi.read(2 + expected) else: result_bytes = self.spi.read(EPD.MAX_READ) strlen = result_bytes.find(b'\x00') result_bytes = result_bytes[:strlen] + result_bytes[strlen+1:strlen+3] else: result_bytes = self.spi.read(2) if self.debug: print("Received: " + hexlify(result_bytes).decode()) (result,) = struct.unpack_from('>H', result_bytes[-2:]) if result != EPD.SW_NORMAL_PROCESSING: raise ValueError("Bad result code: 0x%x" % result) return result_bytes[:-2] @staticmethod def calculate_checksum(data, skip=16): """ Initial checksum value is 0x6363 :param data: :param skip: Skip some data as slices are expensive :return: """ acc = 0x6363 for byte in data: if skip > 0: skip -= 1 else: acc ^= byte acc = ((acc >> 8) | (acc << 8)) & 0xffff acc ^= ((acc & 0xff00) << 4) & 0xffff acc ^= (acc >> 8) >> 4 acc ^= (acc & 0xff00) >> 5 return acc def get_sensor_data(self): # GetSensorData val = self.send_command(0xe5, 1, 0, expected=2) (temp,) = struct.unpack(">H", val) return temp def get_device_id(self): return self.send_command(0x30, 2, 1, expected=0x14) def get_system_info(self): return self.send_command(0x31, 1, 1, expected=0) def get_system_version_code(self): return self.send_command(0x31, 2, 1, expected=0x10) def display_update(self, slot=0, flash=True): cmd = 0x86 if flash: cmd = 0x24 self.send_command(cmd, 1, slot) def reset_data_pointer(self): self.send_command(0x20, 0xd, 0) def image_erase_frame_buffer(self, slot=0): self.send_command(0x20, 0xe, slot) def get_checksum(self, slot): cksum_val = self.send_command(0x2e, 1, slot, expected=2) (cksum,) = struct.unpack(">H", cksum_val) return cksum def upload_image_data(self, data, slot=0, delay_us=1000): self.send_command(0x20, 1, slot, data) time.sleep_us(delay_us) def upload_whole_image(self, img, slot=0): """ Chop up chunks and send it :param img: Image to send in EPD format :param slot: Slot framebuffer number to use :param delay_us: Delay between packets? 450us and the Tbusy line never comes back :return: """ total = len(img) idx = 0 try: while idx < total - 250: chunk = img[idx:idx+250] self.upload_image_data(chunk, slot) del chunk idx += 250 self.upload_image_data(img[idx:], slot) except KeyboardInterrupt: print("Stopped at user request at position: %d (%d)" % (idx, (idx // 250))) except ValueError as e: print("Stopped at position: %d (%d) - %s" % (idx, (idx // 250), e))
from machine import Pin, SPI import time import ubinascii hspi = SPI(1, baudrate=1000000, polarity=0, phase=0) # hspi.init() while 1: hspi.write(b'\x05') # write 5 bytes on MOSI time.sleep(1)
class MFRC522: DEBUG = False OK = 0 NOTAGERR = 1 ERR = 2 REQIDL = 0x26 REQALL = 0x52 AUTHENT1A = 0x60 AUTHENT1B = 0x61 PICC_ANTICOLL1 = 0x93 PICC_ANTICOLL2 = 0x95 PICC_ANTICOLL3 = 0x97 def __init__(self, sck, mosi, miso, rst, cs, baudrate=1000000, spi_id=0): self.sck = Pin(sck, Pin.OUT) self.mosi = Pin(mosi, Pin.OUT) self.miso = Pin(miso) self.rst = Pin(rst, Pin.OUT) self.cs = Pin(cs, Pin.OUT) self.rst.value(0) self.cs.value(1) board = uname()[0] if board == 'WiPy' or board == 'LoPy' or board == 'FiPy': self.spi = SPI(0) self.spi.init(SPI.MASTER, baudrate=1000000, pins=(self.sck, self.mosi, self.miso)) elif (board == 'esp8266') or (board == 'esp32'): self.spi = SPI(baudrate=100000, polarity=0, phase=0, sck=self.sck, mosi=self.mosi, miso=self.miso) self.spi.init() elif board == 'rp2': self.spi = SPI(spi_id, baudrate=baudrate, sck=self.sck, mosi=self.mosi, miso=self.miso) else: raise RuntimeError("Unsupported platform") self.rst.value(1) self.init() def _wreg(self, reg, val): self.cs.value(0) self.spi.write(b'%c' % int(0xff & ((reg << 1) & 0x7e))) self.spi.write(b'%c' % int(0xff & val)) self.cs.value(1) def _rreg(self, reg): self.cs.value(0) self.spi.write(b'%c' % int(0xff & (((reg << 1) & 0x7e) | 0x80))) val = self.spi.read(1) self.cs.value(1) return val[0] def _sflags(self, reg, mask): self._wreg(reg, self._rreg(reg) | mask) def _cflags(self, reg, mask): self._wreg(reg, self._rreg(reg) & (~mask)) def _tocard(self, cmd, send): recv = [] bits = irq_en = wait_irq = n = 0 stat = self.ERR if cmd == 0x0E: irq_en = 0x12 wait_irq = 0x10 elif cmd == 0x0C: irq_en = 0x77 wait_irq = 0x30 self._wreg(0x02, irq_en | 0x80) self._cflags(0x04, 0x80) self._sflags(0x0A, 0x80) self._wreg(0x01, 0x00) for c in send: self._wreg(0x09, c) self._wreg(0x01, cmd) if cmd == 0x0C: self._sflags(0x0D, 0x80) i = 2000 while True: n = self._rreg(0x04) i -= 1 if ~((i != 0) and ~(n & 0x01) and ~(n & wait_irq)): break self._cflags(0x0D, 0x80) if i: if (self._rreg(0x06) & 0x1B) == 0x00: stat = self.OK if n & irq_en & 0x01: stat = self.NOTAGERR elif cmd == 0x0C: n = self._rreg(0x0A) lbits = self._rreg(0x0C) & 0x07 if lbits != 0: bits = (n - 1) * 8 + lbits else: bits = n * 8 if n == 0: n = 1 elif n > 16: n = 16 for _ in range(n): recv.append(self._rreg(0x09)) else: stat = self.ERR return stat, recv, bits def _crc(self, data): self._cflags(0x05, 0x04) self._sflags(0x0A, 0x80) for c in data: self._wreg(0x09, c) self._wreg(0x01, 0x03) i = 0xFF while True: n = self._rreg(0x05) i -= 1 if not ((i != 0) and not (n & 0x04)): break return [self._rreg(0x22), self._rreg(0x21)] def init(self): self.reset() self._wreg(0x2A, 0x8D) self._wreg(0x2B, 0x3E) self._wreg(0x2D, 30) self._wreg(0x2C, 0) self._wreg(0x15, 0x40) self._wreg(0x11, 0x3D) self.antenna_on() def reset(self): self._wreg(0x01, 0x0F) def antenna_on(self, on=True): if on and ~(self._rreg(0x14) & 0x03): self._sflags(0x14, 0x03) else: self._cflags(0x14, 0x03) def request(self, mode): self._wreg(0x0D, 0x07) (stat, recv, bits) = self._tocard(0x0C, [mode]) if (stat != self.OK) | (bits != 0x10): stat = self.ERR return stat, bits def anticoll(self, anticolN): ser_chk = 0 ser = [anticolN, 0x20] self._wreg(0x0D, 0x00) (stat, recv, bits) = self._tocard(0x0C, ser) if stat == self.OK: if len(recv) == 5: for i in range(4): ser_chk = ser_chk ^ recv[i] if ser_chk != recv[4]: stat = self.ERR else: stat = self.ERR return stat, recv def PcdSelect(self, serNum, anticolN): backData = [] buf = [] buf.append(anticolN) buf.append(0x70) #i = 0 ###xorsum=0; for i in serNum: buf.append(i) #while i<5: # buf.append(serNum[i]) # i = i + 1 pOut = self._crc(buf) buf.append(pOut[0]) buf.append(pOut[1]) (status, backData, backLen) = self._tocard(0x0C, buf) if (status == self.OK) and (backLen == 0x18): return 1 else: return 0 def SelectTag(self, uid): byte5 = 0 #(status,puid)= self.anticoll(self.PICC_ANTICOLL1) #print("uid",uid,"puid",puid) for i in uid: byte5 = byte5 ^ i puid = uid + [byte5] if self.PcdSelect(puid, self.PICC_ANTICOLL1) == 0: return (self.ERR, []) return (self.OK, uid) def tohexstring(self, v): s = "[" for i in v: if i != v[0]: s = s + ", " s = s + "0x{:02X}".format(i) s = s + "]" return s def SelectTagSN(self): valid_uid = [] (status, uid) = self.anticoll(self.PICC_ANTICOLL1) #print("Select Tag 1:",self.tohexstring(uid)) if status != self.OK: return (self.ERR, []) if self.DEBUG: print("anticol(1) {}".format(uid)) if self.PcdSelect(uid, self.PICC_ANTICOLL1) == 0: return (self.ERR, []) if self.DEBUG: print("pcdSelect(1) {}".format(uid)) #check if first byte is 0x88 if uid[0] == 0x88: #ok we have another type of card valid_uid.extend(uid[1:4]) (status, uid) = self.anticoll(self.PICC_ANTICOLL2) #print("Select Tag 2:",self.tohexstring(uid)) if status != self.OK: return (self.ERR, []) if self.DEBUG: print("Anticol(2) {}".format(uid)) rtn = self.PcdSelect(uid, self.PICC_ANTICOLL2) if self.DEBUG: print("pcdSelect(2) return={} uid={}".format(rtn, uid)) if rtn == 0: return (self.ERR, []) if self.DEBUG: print("PcdSelect2() {}".format(uid)) #now check again if uid[0] is 0x88 if uid[0] == 0x88: valid_uid.extend(uid[1:4]) (status, uid) = self.anticoll(self.PICC_ANTICOLL3) #print("Select Tag 3:",self.tohexstring(uid)) if status != self.OK: return (self.ERR, []) if self.DEBUG: print("Anticol(3) {}".format(uid)) if self.MFRC522_PcdSelect(uid, self.PICC_ANTICOLL3) == 0: return (self.ERR, []) if self.DEBUG: print("PcdSelect(3) {}".format(uid)) valid_uid.extend(uid[0:5]) # if we are here than the uid is ok # let's remove the last BYTE whic is the XOR sum return (self.OK, valid_uid[:len(valid_uid) - 1]) #return (self.OK , valid_uid) def auth(self, mode, addr, sect, ser): return self._tocard(0x0E, [mode, addr] + sect + ser[:4])[0] def authKeys(self, uid, addr, keyA=None, keyB=None): status = self.ERR if keyA is not None: status = self.auth(self.AUTHENT1A, addr, keyA, uid) elif keyB is not None: status = self.auth(self.AUTHENT1B, addr, keyB, uid) return status def stop_crypto1(self): self._cflags(0x08, 0x08) def read(self, addr): data = [0x30, addr] data += self._crc(data) (stat, recv, _) = self._tocard(0x0C, data) return stat, recv def write(self, addr, data): buf = [0xA0, addr] buf += self._crc(buf) (stat, recv, bits) = self._tocard(0x0C, buf) if not (stat == self.OK) or not (bits == 4) or not ( (recv[0] & 0x0F) == 0x0A): stat = self.ERR else: buf = [] for i in range(16): buf.append(data[i]) buf += self._crc(buf) (stat, recv, bits) = self._tocard(0x0C, buf) if not (stat == self.OK) or not (bits == 4) or not ( (recv[0] & 0x0F) == 0x0A): stat = self.ERR return stat def writeSectorBlock(self, uid, sector, block, data, keyA=None, keyB=None): absoluteBlock = sector * 4 + (block % 4) if absoluteBlock > 63: return self.ERR if len(data) != 16: return self.ERR if self.authKeys(uid, absoluteBlock, keyA, keyB) != self.ERR: return self.write(absoluteBlock, data) return self.ERR def readSectorBlock(self, uid, sector, block, keyA=None, keyB=None): absoluteBlock = sector * 4 + (block % 4) if absoluteBlock > 63: return self.ERR, None if self.authKeys(uid, absoluteBlock, keyA, keyB) != self.ERR: return self.read(absoluteBlock) return self.ERR, None def MFRC522_DumpClassic1K(self, uid, Start=0, End=64, keyA=None, keyB=None): for absoluteBlock in range(Start, End): status = self.authKeys(uid, absoluteBlock, keyA, keyB) # Check if authenticated print("{:02d} S{:02d} B{:1d}: ".format(absoluteBlock, absoluteBlock // 4, absoluteBlock % 4), end="") if status == self.OK: status, block = self.read(absoluteBlock) if status == self.ERR: break else: for value in block: print("{:02X} ".format(value), end="") print(" ", end="") for value in block: if (value > 0x20) and (value < 0x7f): print(chr(value), end="") else: print('.', end="") print("") else: break if status == self.ERR: print("Authentication error") return self.ERR return self.OK
print(spi) spi = SPI(0, SPI.MASTER, baudrate=5000000, bits=32, polarity=1, phase=0) print(spi) spi = SPI(0, SPI.MASTER, baudrate=10000000, polarity=1, phase=1) print(spi) spi.init(baudrate=20000000, polarity=0, phase=0) print(spi) spi=SPI() print(spi) SPI(mode=SPI.MASTER) SPI(mode=SPI.MASTER, pins=spi_pins) SPI(id=0, mode=SPI.MASTER, polarity=0, phase=0, pins=('GP14', 'GP16', 'GP15')) SPI(0, SPI.MASTER, polarity=0, phase=0, pins=('GP31', 'GP16', 'GP15')) spi = SPI(0, SPI.MASTER, baudrate=10000000, polarity=0, phase=0, pins=spi_pins) print(spi.write('123456') == 6) buffer_r = bytearray(10) print(spi.readinto(buffer_r) == 10) print(spi.readinto(buffer_r, write=0x55) == 10) read = spi.read(10) print(len(read) == 10) read = spi.read(10, write=0xFF) print(len(read) == 10) buffer_w = bytearray([1, 2, 3, 4, 5, 6, 7, 8, 9, 0]) print(spi.write_readinto(buffer_w, buffer_r) == 10) print(buffer_w == buffer_r) # test all polaritiy and phase combinations spi.init(polarity=1, phase=0, pins=None) buffer_r = bytearray(10) spi.write_readinto(buffer_w, buffer_r)
from machine import Pin, SPI import time import ubinascii vref = 3.3 cs = Pin(15, Pin.OUT) cs.high() hspi = SPI(1, baudrate=1600000, polarity=0, phase=0) value = bytearray(2) while True: data = bytearray(2) wget = b'\xA0\x00' cs.low() hspi.write(b'\x01') hspi.write(b'\xA0') data = hspi.read(1) # data = hspi.write_readinto(wget, data) cs.high() print(str(int(ubinascii.hexlify(data & b'\x0fff'))*vref/4096)) time.sleep(1) # data = data*vref/4096 # time.sleep(1) # print(str(data & b'\x0fff'))
from fpioa_manager import fm mosi = 8 miso = 15 cs = 20 clk = 21 spi = SPI(SPI.SPI_SOFT, mode=SPI.MODE_MASTER, baudrate=400 * 1000, polarity=0, phase=0, bits=8, firstbit=SPI.MSB, sck=clk, mosi=mosi, miso=miso) fm.register(cs, fm.fpioa.GPIO6, force=True) cs = GPIO(GPIO.GPIO6, GPIO.OUT) # read spi flash id while True: cs.value(0) write_data = bytearray([0x90, 0x00, 0x00, 0x00]) spi.write(write_data) id_buf = bytearray(2) spi.readinto(id_buf, write=0xff) work_data = id_buf cs.value(1) print(work_data)
class spiram: def __init__(self): self.led = Pin(5, Pin.OUT) self.led.off() self.spi_channel = const(2) self.init_pinout_sd() self.spi_freq = const(4000000) self.hwspi = SPI(self.spi_channel, baudrate=self.spi_freq, polarity=0, phase=0, bits=8, firstbit=SPI.MSB, sck=Pin(self.gpio_sck), mosi=Pin(self.gpio_mosi), miso=Pin(self.gpio_miso)) @micropython.viper def init_pinout_sd(self): self.gpio_sck = const(16) self.gpio_mosi = const(4) self.gpio_miso = const(12) # read from file -> write to SPI RAM def load_stream(self, filedata, addr=0, maxlen=0x10000, blocksize=1024): block = bytearray(blocksize) # Request load self.led.on() self.hwspi.write( bytearray([ 0, (addr >> 24) & 0xFF, (addr >> 16) & 0xFF, (addr >> 8) & 0xFF, addr & 0xFF ])) bytes_loaded = 0 while bytes_loaded < maxlen: if filedata.readinto(block): self.hwspi.write(block) bytes_loaded += blocksize else: break self.led.off() # read from SPI RAM -> write to file def save_stream(self, filedata, addr=0, length=1024, blocksize=1024): bytes_saved = 0 block = bytearray(blocksize) # Request save self.led.on() self.hwspi.write( bytearray([ 1, (addr >> 24) & 0xFF, (addr >> 16) & 0xFF, (addr >> 8) & 0xFF, addr & 0xFF, 0 ])) while bytes_saved < length: self.hwspi.readinto(block) filedata.write(block) bytes_saved += len(block) self.led.off() def ctrl(self, i): self.led.on() self.hwspi.write(bytearray([0, 0xFF, 0xFF, 0xFF, 0xFF, i])) self.led.off() def cpu_halt(self): self.ctrl(2) def cpu_continue(self): self.ctrl(0) def store_rom(self, length=32): self.stored_code = bytearray(length) self.led.on() self.hwspi.write( bytearray([ 1, 0, 0, (self.code_addr >> 8) & 0xFF, self.code_addr & 0xFF, 0 ])) self.hwspi.readinto(self.stored_code) self.led.off() self.stored_vector = bytearray(2) self.led.on() self.hwspi.write( bytearray([ 1, 0, 0, (self.vector_addr >> 8) & 0xFF, self.vector_addr & 0xFF, 0 ])) self.hwspi.readinto(self.stored_vector) self.led.off() def restore_rom(self): self.led.on() self.hwspi.write( bytearray( [0, 0, 0, (self.code_addr >> 8) & 0xFF, self.code_addr & 0xFF])) self.hwspi.write(self.stored_code) self.led.off() self.led.on() self.hwspi.write( bytearray([ 0, 0, 0, (self.vector_addr >> 8) & 0xFF, self.vector_addr & 0xFF ])) self.hwspi.write(self.stored_vector) self.led.off() def patch_rom(self, regs): # regs = 0:A 1:X 2:Y 3:P 4:S 5-6:PC # overwrite with register restore code self.led.on() self.hwspi.write( bytearray([ 0, 0, 0, (self.vector_addr >> 8) & 0xFF, self.vector_addr & 0xFF, self.code_addr & 0xFF, (self.code_addr >> 8) & 0xFF ])) # overwrite reset vector at 0xFFFC self.led.off() self.led.on() self.hwspi.write( bytearray( [0, 0, 0, (self.code_addr >> 8) & 0xFF, self.code_addr & 0xFF])) # overwrite code self.hwspi.write( bytearray([ 0x78, 0xA2, regs[4], 0x9A, 0xA2, regs[1], 0xA0, regs[2], 0xA9, regs[3], 0x48, 0x28, 0xA9, regs[0], 0x4C, regs[5], regs[6] ])) self.led.off() self.led.on()
class WS2812: """ Driver for WS2812 RGB LEDs. May be used for controlling single LED or chain of LEDs. Example of use: chain = WS2812(ledNumber=4) data = [ (255, 0, 0), # red (0, 255, 0), # green (0, 0, 255), # blue (85, 85, 85), # white ] chain.show(data) Version: 1.0 """ # Values to put inside SPi register for each color's bit buf_bytes = (0xE0E0, 0xFCE0, 0xE0FC, 0xFCFC) def __init__(self, ledNumber=1, brightness=100, dataPin='P22'): """ Params: * ledNumber = count of LEDs * brightness = light brightness (integer : 0 to 100%) * dataPin = pin to connect data channel (LoPy only) """ self.ledNumber = ledNumber self.brightness = brightness # Prepare SPI data buffer (8 bytes for each color) self.buf_length = self.ledNumber * 3 * 8 self.buf = bytearray(self.buf_length) # SPI init # Bus 0, 8MHz => 125 ns by bit, 8 clock cycle when bit transfert+2 clock cycle between each transfert # => 125*10=1.25 us required by WS2812 if uname().sysname == 'LoPy': self.spi = SPI(0, SPI.MASTER, baudrate=8000000, polarity=0, phase=1, pins=(None, dataPin, None)) # Enable pull down Pin(dataPin, mode=Pin.OUT, pull=Pin.PULL_DOWN) else: #WiPy self.spi = SPI(0, SPI.MASTER, baudrate=8000000, polarity=0, phase=1) # Enable pull down Pin('GP16', mode=Pin.ALT, pull=Pin.PULL_DOWN) # Turn LEDs off self.show([]) def show(self, data): """ Show RGB data on LEDs. Expected data = [(R, G, B), ...] where R, G and B are intensities of colors in range from 0 to 255. One RGB tuple for each LED. Count of tuples may be less than count of connected LEDs. """ self.fill_buf(data) self.send_buf() def send_buf(self): """ Send buffer over SPI. """ disable_irq() self.spi.write(self.buf) enable_irq() gc.collect() def update_buf(self, data, start=0): """ Fill a part of the buffer with RGB data. Order of colors in buffer is changed from RGB to GRB because WS2812 LED has GRB order of colors. Each color is represented by 4 bytes in buffer (1 byte for each 2 bits). Returns the index of the first unfilled LED Note: If you find this function ugly, it's because speed optimisations beated purity of code. """ buf = self.buf buf_bytes = self.buf_bytes brightness = self.brightness index = start * 24 for red, green, blue in data: red = int(red * brightness // 100) green = int(green * brightness // 100) blue = int(blue * brightness // 100) buf[index] = buf_bytes[green >> 6 & 0x03] buf[index + 2] = buf_bytes[green >> 4 & 0x03] buf[index + 4] = buf_bytes[green >> 2 & 0x03] buf[index + 6] = buf_bytes[green & 0x03] buf[index + 8] = buf_bytes[red >> 6 & 0x03] buf[index + 10] = buf_bytes[red >> 4 & 0x03] buf[index + 12] = buf_bytes[red >> 2 & 0x03] buf[index + 14] = buf_bytes[red & 0x03] buf[index + 16] = buf_bytes[blue >> 6 & 0x03] buf[index + 18] = buf_bytes[blue >> 4 & 0x03] buf[index + 20] = buf_bytes[blue >> 2 & 0x03] buf[index + 22] = buf_bytes[blue & 0x03] index += 24 return index // 24 def fill_buf(self, data): """ Fill buffer with RGB data. All LEDs after the data are turned off. """ end = self.update_buf(data) # Turn off the rest of the LEDs buf = self.buf off = self.buf_bytes[0] for index in range(end * 24, self.buf_length): buf[index] = off index += 2 def set_brightness(self, brightness): """ Set brighness of all leds """ self.brightness = brightness
class Accelerometer: def __init__(self, cs_pin=5, scl_pin=18, sda_pin=23, sdo_pin=19, spi_freq=5000000): """ Class for fast SPI comunications between an ESP32 flashed with MicroPython and an Analog Devices ADXL345 accelerometer :param cs_pin: MCU pin number at which accelerometer's CS wire is connected :param scl_pin: MCU pin number at which accelerometer's SCL wire is connected (SCK) :param sda_pin: MCU pin number at which accelerometer's SDA wire is connected (MOSI) :param sdo_pin: MCU pin number at which accelerometer's SDO wire is connected (MISO) :param spi_freq: frequency of SPI comunications """ # valid inputs if spi_freq > 5000000: spi_freq = 5000000 print('max spi clock frequency for adxl355 is 5Mhz') # constants self.standard_g = 9.80665 # m/s2 self.read_mask = const(0x80) self.multibyte_mask = const(0x40) self.nmaxvalues_infifo = 32 self.bytes_per_3axes = 6 # 2 bytes * 3 axes self.device_id = 0xE5 # register addresses self.addr_device = const(0x53) self.regaddr_devid = const(0x00) self.regaddr_acc = const(0x32) self.regaddr_freq = const(0x2C) self.regaddr_pwr = const(0x2D) self.regaddr_intsource = const(0x30) self.regaddr_grange = const(0x31) self.regaddr_fifoctl = const(0x38) self.regaddr_fifostatus = const(0x39) # SPI pins self.cs_pin = cs_pin self.scl_pin = scl_pin self.sdo_pin = sdo_pin self.sda_pin = sda_pin self.spi_freq = spi_freq # allowed values self.power_modes = {'standby': 0x00, 'measure': 0x08} self.g_ranges = {2: 0x00, 4: 0x01, 8: 0x10, 16: 0x11} self.device_sampling_rates = { 1.56: 0x04, 3.13: 0x05, 6.25: 0x06, 12.5: 0x07, 25: 0x08, 50: 0x09, 100: 0x0a, 200: 0x0b, 400: 0x0c, 800: 0x0d, 1600: 0x0e, 3200: 0x0f } def __del__(self): self.spi.deinit() # == general purpose == def init_spi(self): self.spi = SPI(2, sck=Pin(self.scl_pin, Pin.OUT), mosi=Pin(self.sda_pin, Pin.OUT), miso=Pin(self.sdo_pin), baudrate=self.spi_freq, polarity=1, phase=1, bits=8, firstbit=SPI.MSB) time.sleep(0.2) self.cs = Pin(self.cs_pin, Pin.OUT, value=1) time.sleep(0.2) if not self.is_spi_communcation_working(): print('SPI communication is not working: ' '\n\t* wrong wiring?' '\n\t* reinitialised SPI?' '\n\t* broken sensor (test I2C to be sure)') return self def deinit_spi(self): self.spi.deinit() return self def write(self, regaddr: int, the_byte: int): """ write byte into register address :param regaddr: register address to write :param bt: byte to write """ self.cs.value(0) self.spi.write(bytearray((regaddr, the_byte))) self.cs.value(1) return self @micropython.native def read(self, regaddr: int, nbytes: int) -> bytearray or int: """ read bytes from register :param regaddr: register address to read :param nbytes: number of bytes to read :return: byte or bytes read """ wbyte = regaddr | self.read_mask if nbytes > 1: wbyte = wbyte | self.multibyte_mask self.cs.value(0) value = self.spi.read(nbytes + 1, wbyte)[1:] self.cs.value(1) return value @micropython.native def read_into(self, buf: bytearray, regaddr: int) -> bytearray: """ read bytes from register into an existing bytearray, generally faster than normal read :param rbuf: bytearray where read values will be assigned to :param regaddr: register address to read :return: modified input bytearray """ wbyte = regaddr | self.read_mask | self.multibyte_mask self.cs.value(0) self.spi.readinto(buf, wbyte) self.cs.value(1) return buf @micropython.native def remove_first_bytes_from_bytearray_of_many_transactions( self, buf: bytearray) -> bytearray: """ remove first byte of SPI transaction (which is irrelevant) from a buffer read through spi.readinto :param buf: bytearray of size multiple of (self.bytes_per_3axes + 1) :return: bytearray of size multiple of self.bytes_per_3axes """ bytes_per_3axes = self.bytes_per_3axes return bytearray( [b for i, b in enumerate(buf) if i % (bytes_per_3axes + 1) != 0]) # == settings == def set_power_mode(self, mode: str): """ set the power mode of the accelerometer :param mode: {'measure', 'standby'} """ print('set power mode to %s' % (mode)) self.write(self.regaddr_pwr, self.power_modes[mode]) self.power_mode = mode return self def set_g_range(self, grange: int): """ set the scale of output acceleration data :param grange: {2, 4, 8, 16} """ print('set range to pm %s' % (grange)) self.write(self.regaddr_grange, self.g_ranges[grange]) self.g_range = grange return self def set_sampling_rate(self, sr: int): """ :param sr: sampling rate of the accelerometer can be {1.56, 3.13, 6.25, 12.5, 25, 50, 100, 200, 400, 800, 1600, 3200} """ print('set sampling rate to %s' % (sr)) self.write(self.regaddr_freq, self.device_sampling_rates[sr]) self.sampling_rate = sr return self def set_fifo_mode(self, mode: str, watermark_level: int = 16): """ :param mode: in 'stream' mode the fifo is on, in 'bypass' mode the fifo is off :param watermark_level: see set_watermark_level method """ self.fifo_mode = mode self.watermark_level = watermark_level if mode == 'bypass': b = 0x00 print("set fifo in bypass mode") else: # stream mode stream_bstr = '100' wm_bstr = bin(watermark_level).split('b')[1] bstr = '0b' + stream_bstr + '{:>5}'.format(wm_bstr).replace( ' ', '0') b = int(bstr, 2) print("set fifo in stream mode") self.write(self.regaddr_fifoctl, b) return self def set_watermark_level(self, nrows: int = 16): """ set the number of new measures (xyz counts 1) after which the watermark is triggered """ print('set watermark to %s rows' % (nrows)) self.fifo_mode = 'stream' self.watermark_level = nrows stream_bstr = '100' wm_bstr = bin(nrows).split('b')[1] bstr = '0b' + stream_bstr + '{:>5}'.format(wm_bstr).replace(' ', '0') b = int(bstr, 2) self.write(self.regaddr_fifoctl, b) return self # == readings == def is_spi_communcation_working(self) -> bool: if self.read(self.regaddr_devid, 1)[0] == self.device_id: return True else: print(self.read(self.regaddr_devid, 1)) return False def clear_fifo(self): """ Clears all values in fifo: usefull to start reading FIFO when expected, otherwise the first values were recorded before actually starting the measure """ self.set_fifo_mode('bypass') self.set_fifo_mode('stream') def clear_isdataready(self): _ = self.read(self.regaddr_acc, 6) @micropython.native def is_watermark_reached(self) -> bool: """ :return: 1 if watermark level of measures was reached since last reading, 0 otherwise """ return self.read(self.regaddr_intsource, 1)[0] >> 1 & 1 # second bit @micropython.native def is_data_ready(self) -> bool: """ :return: 1 if a new measure has arrived since last reading, 0 otherwise """ return self.read(self.regaddr_intsource, 1)[0] >> 7 & 1 # eighth bit @micropython.native def get_nvalues_in_fifo(self) -> int: """ :return: number of measures (xyz counts 1) in the fifo since last reading """ return self.read(self.regaddr_fifostatus, 1)[0] & 0x3f # first six bits to int # == continuos readings able to reach 3.2 kHz == @micropython.native def read_many_xyz(self, n: int) -> tuple: """ :param n: number of xyz accelerations to read from the accelerometer return: ( bytearray containing 2 bytes for each of the 3 axes multiplied by the fractions of the sampling rate contained in the acquisition time, array of times at which each sample was recorded in microseconds ) """ print("Measuring %s samples at %s Hz, range %sg" % (n, self.sampling_rate, self.g_range)) # local variables and functions are MUCH faster regaddr_acc = self.regaddr_acc | self.read_mask | self.multibyte_mask regaddr_intsource = self.regaddr_intsource | self.read_mask spi_readinto = self.spi.readinto cs = self.cs ticks_us = time.ticks_us bytes_per_3axes = self.bytes_per_3axes read = self.spi.read # definitions n_exp_meas = n n_exp_bytes = (self.bytes_per_3axes + 1) * n_exp_meas T = [0] * (int(n_exp_meas * 1.5)) buf = bytearray(int(n_exp_bytes * 1.5)) m = memoryview(buf) # set up device self.set_fifo_mode('bypass') gc.collect() # measure n_act_meas = 0 self.set_power_mode('measure') while n_act_meas < n_exp_meas: start_index = n_act_meas * (bytes_per_3axes + 1) stop_index = n_act_meas * (bytes_per_3axes + 1) + ( bytes_per_3axes + 1) cs.value(0) is_data_ready = read(2, regaddr_intsource)[1] >> 7 & 1 cs.value(1) if not is_data_ready: continue cs.value(0) spi_readinto(m[start_index:stop_index], regaddr_acc) cs.value(1) T[n_act_meas] = ticks_us() n_act_meas += 1 self.set_power_mode('standby') # final corrections buf = self.remove_first_bytes_from_bytearray_of_many_transactions(buf) buf = buf[:n_exp_meas * bytes_per_3axes] # remove exceeding values T = T[:n_exp_meas] # remove exceeding values # debug actual_acq_time = (T[-1] - T[0]) / 1000000 print('measured for %s seconds, expected %s seconds' % (actual_acq_time, n_exp_meas / self.sampling_rate)) print('avg sampling rate = ' + str(n_act_meas / actual_acq_time) + ' Hz') # TODO: send error to webapp when actual acquisition time is different from expected gc.collect() return buf, T @micropython.native def read_many_xyz_fromfifo(self, n: int) -> tuple: """ read many measures of accaleration on the 3 axes from the fifo register :param n: number of measures to read (xyz counts 1) return: ( bytearray containing 2 bytes for each of the 3 axes multiplied by the fractions of the sampling rate contained in the acquisition time, array of times at which each sample was recorded in microseconds ) """ print("Measuring %s samples at %s Hz, range %sg" % (n, self.sampling_rate, self.g_range)) # local variables and functions are MUCH faster regaddr_acc = self.regaddr_acc | self.read_mask | self.multibyte_mask spi_readinto = self.spi.readinto cs = self.cs get_nvalues_in_fifo = self.get_nvalues_in_fifo bytes_per_3axes = self.bytes_per_3axes # definitions n_exp_meas = n n_exp_bytes = (bytes_per_3axes + 1) * n_exp_meas buf = bytearray(int(n_exp_bytes * 1.5)) m = memoryview(buf) # set up device self.set_fifo_mode('stream') gc.collect() # measure n_act_meas = 0 self.set_power_mode('measure') self.clear_fifo() t_start = time.ticks_us() while n_act_meas < n_exp_meas: nvalues_infifo = get_nvalues_in_fifo() for _ in range( nvalues_infifo ): # it is impossible to read a block of measures from fifo cs.value(0) spi_readinto( m[n_act_meas * (bytes_per_3axes + 1):n_act_meas * (bytes_per_3axes + 1) + (bytes_per_3axes + 1)], regaddr_acc) cs.value(1) n_act_meas += 1 t_stop = time.ticks_us() self.set_power_mode('standby') # final corrections buf = self.remove_first_bytes_from_bytearray_of_many_transactions(buf) buf = buf[:n_exp_meas * bytes_per_3axes] # remove exceeding values actual_acq_time = (t_stop - t_start) / 1000000 actual_sampling_rate = n_act_meas / actual_acq_time T = [(i + 1) / actual_sampling_rate for i in range(n_exp_meas)] # debug print('measured for %s seconds, expected %s seconds' % (actual_acq_time, n / self.sampling_rate)) print('actual sampling rate = ' + str(n_act_meas / actual_acq_time) + ' Hz') # TODO: send error to webapp when actual acquisition time is different from expected gc.collect() return buf, T @micropython.native def read_continuos_xyz(self, acquisition_time: int) -> tuple: """ read for the provided amount of time from the acceleration register, saving the value only if a new measure is available since last reading :param acquisition_time: seconds the acquisition should last :return: ( bytearray containing 2 bytes for each of the 3 axes multiplied by the fractions of the sampling rate contained in the acquisition time, array of times at which each sample was recorded in microseconds ) """ n_exp_meas = int(acquisition_time * self.sampling_rate) buf, T = self.read_many_xyz(n_exp_meas) return buf, T @micropython.native def read_continuos_xyz_fromfifo(self, acquisition_time: int) -> tuple: """ read for the provided amount of time all the values contained in the fifo register (if any) :param acquisition_time: :return: ( bytearray containing 2 bytes for each of the 3 axes multiplied by the fractions of the sampling rate contained in the acquisition time, array of times at which each sample was recorded in microseconds ) """ n_exp_meas = int(acquisition_time * self.sampling_rate) buf, T = self.read_many_xyz_fromfifo(n_exp_meas) return buf, T # == conversions == def xyzbytes2g(self, buf: bytearray) -> tuple: """ convert a bytearray of measures on the three axes xyz in three lists where the acceleration is in units of gravity on the sealevel (g) :param buf: bytearray of 2 bytes * 3 axes * nvalues :return: 3 lists of ints corresponding to x, y, z values of acceleration in units of g """ gc.collect() n_act_meas = int(len(buf) / self.bytes_per_3axes) acc_x, acc_y, acc_z = zip(*[ ustruct.unpack('<HHH', buf[i:i + self.bytes_per_3axes]) for i in range(n_act_meas) if i % self.bytes_per_3axes == 0 ]) # negative values rule acc_x = [x if x <= 32767 else x - 65536 for x in acc_x] acc_y = [y if y <= 32767 else y - 65536 for y in acc_y] acc_z = [z if z <= 32767 else z - 65536 for z in acc_z] gc.collect() return acc_x, acc_y, acc_z
class Display: """ This class creates the interface between the main code and the EPaper display on the ESPaper from Thingpulse. Display model: GooDisplay 2.9 inch e-paper display GDEH029A1 The pins on this module are fixed as shown below, since the display is attached to the ESP8266 through the ESPaper board. Pinout: | NAME | PIN | DESCRIPTION | | DC | 5 | LOW will write COMMANDS, HIGH will write DATA | | RESET | 2 | LOW will enable the RESET | | CS | 14 | LOW will enable comminucation to the Display | | BUSY | 4 | is HIGH when Display is busy, LOW when IDLE | | SPI | default | 4-wire communication using the SPI1 from ESP8266 | Args: portrait (bool): Set the (0, 0) position on the top-right corner. fast (bool): Set the display for partial / fast refresh. """ def __init__(self, portrait=False, fast=False): # Screen size (x, y) self.portrait = portrait if self.portrait: self.size = (128, 296) else: self.size = (296, 128) # Image size, pre-alocation and lookup table self.n_bytes = self.size[0] * self.size[1] // 8 self.blank_image() if fast: self.lut = (b'\x10\x18\x18\x08\x18\x18' b'\x08\x00\x00\x00\x00\x00' b'\x00\x00\x00\x00\x00\x00' b'\x00\x00\x13\x14\x44\x12' b'\x00\x00\x00\x00\x00\x00') else: self.lut = (b'\x50\xaa\x55\xaa\x11\x00' b'\x00\x00\x00\x00\x00\x00' b'\x00\x00\x00\x00\x00\x00' b'\x00\x00\xff\xff\x1f\x00' b'\x00\x00\x00\x00\x00\x00') # Pins definition self.dc = Pin(5, Pin.OUT) self.rst = Pin(2, Pin.OUT) self.cs = Pin(15, Pin.OUT) self.busy = Pin(4, Pin.IN) # SPI definition self.spi = SPI(1, baudrate=4000000, polarity=0, phase=0) # Display first cycle self.dc.on() self.cs.on() self.rst.on() self.reset() self.init() def reset(self): """Display reset cycle.""" self.rst.off() sleep_ms(10) self.rst.on() sleep_ms(10) def blank_image(self, black=False): """ Set a blank image with the desired color. args: - black (bool): black image when True, white image when False """ self.image = bytearray() if black: self.image = bytearray(b'\x00' * self.n_bytes) else: self.image = bytearray(b'\xff' * self.n_bytes) def wait_until_idle(self): """Display idle check to avoid sending commands when busy.""" while self.busy.value() == 1: sleep_ms(10) def write_cmd(self, cmd): """ Prepare the display then send the command to it. args: - cmd (bytearray): command to be sent to the display. """ self.wait_until_idle() self.cs.off() self.dc.off() self.spi.write(cmd) self.cs.on() def write_data(self, data): """ Prepare the display then send the data to it. args: - data (bytearray): data to be sent to the display. """ self.wait_until_idle() self.cs.off() self.dc.on() self.spi.write(data) self.cs.on() def set_memory_area(self): """ Set the position to write in the RAM. """ # Set RAM-X Address Start-End Position self.write_cmd(b'\x44') if self.portrait: self.write_data(b'\x00\x0f') else: self.write_data(b'\x0f\x00') # Set RAM-Y Address Start-End Position self.write_cmd(b'\x45') if self.portrait: self.write_data(b'\x00\x00\x27\x01') else: self.write_data(b'\x27\x01\x00\x00') def set_memory_pointer(self): """ Set the memory pointer position to write to the RAM. """ # Set RAM-X Address count self.write_cmd(b'\x4e') if self.portrait: self.write_data(b'\x00') else: self.write_data(b'\x0f') # Set RAM-Y Address count self.write_cmd(b'\x4f') if self.portrait: self.write_data(b'\x00\x00') else: self.write_data(b'\x27\x01') def init(self): """ Prepare the display for normal operation. The basic sequence of commands is found on the datasheet of the display. """ # Driver Output Control self.write_cmd(b'\x01') self.write_data(b'\x27\x01\x00') # Booster soft start self.write_cmd(b'\x0C') self.write_data(b'\xd7\xd6\x9d') # VCOM Voltage self.write_cmd(b'\x2c') self.write_data(b'\xa8') # Dummy Line self.write_cmd(b'\x3a') self.write_data(b'\x1a') # Gate Time self.write_cmd('\x3b') self.write_data('\x08') # Data Entry Mode self.write_cmd(b'\x11') if self.portrait: self.write_data(b'\x03') else: self.write_data(b'\x04') # Write LUT (LookUpTable) self.write_cmd(b'\x32') self.write_data(self.lut) # Border Wave Form # self.write_cmd(b'\x3c') # self.write_data(b'\x33') def write_image(self): """ Write the image to the Display RAM. Send the command to write data to RAM then send the IMAGE to it. """ self.set_memory_area() self.set_memory_pointer() self.write_cmd(b'\x24') self.write_data(self.image) def update(self): """ Show the image from RAM on the display. Send the command to update the display then send the command to activate it. """ self.write_cmd(b'\x22') self.write_data(b'\xc4') self.write_cmd(b'\x20') self.write_cmd(b'\xff') def full_refresh(self): """ Implementation of a full refresh to be used in fast mode. Will set the image to a black screen, wait for 300ms, then set it again to white screen, clearing the ghost effect. """ self.blank_image(True) self.write_image() self.update() sleep_ms(300) self.blank_image() self.write_image() self.update()
class Screen(Canvas): def __init__(self, sck=None, mosi=None, miso=None, spi=None, resetDisplayPin=None, slaveSelectPin=None, baudrate=1800000): self.cmdbuf = bytearray( 33 ) # enough for 1 header byte plus 16 graphic bytes encoded as two bytes each self.cmdmv = memoryview(self.cmdbuf) if spi is not None: self.spi = spi else: polarity = 0 phase = 0 if sck or mosi or miso: # any pins are identified - wire up as software SPI if not (sck and mosi and miso): raise AssertionError( "All SPI pins sck, mosi and miso need to be specified") self.spi = SPI(-1, baudrate=baudrate, polarity=polarity, phase=phase, sck=sck, mosi=mosi, miso=miso) else: self.spi = SPI(1, baudrate=baudrate, polarity=polarity, phase=phase) # allocate frame buffer just once, use memoryview-wrapped bytearrays for rows self.fbuff = [ memoryview(bytearray(colBound)) for rowPos in range(rowBound) ] self.resetDisplayPin = resetDisplayPin if self.resetDisplayPin is not None: self.resetDisplayPin.init(mode=Pin.OUT) self.slaveSelectPin = slaveSelectPin if self.slaveSelectPin is not None: self.slaveSelectPin.init(mode=Pin.OUT) self.set_rotation(0) # rotate to 0 degrees self.config() def config(self): self.reset() self.select(True) self.send_flag(0x30) # basic instruction set self.send_flag(0x30) # repeated self.send_flag(0x0C) # display on self.send_flag(0x34) # enable RE mode self.send_flag(0x34) self.send_flag(0x36) # enable graphics display self.select(False) # slave select surprisingly? is active high +V means active def select(self, selected): if self.slaveSelectPin: self.slaveSelectPin.value(1 if selected else 0) # reset logic untested def reset(self): if self.resetDisplayPin is not None: # pulse active low to reset screen self.resetDisplayPin.value(0) sleep(0.1) self.resetDisplayPin.value(1) def set_rotation(self, rot): if rot == 0 or rot == 2: self.width = 128 self.height = 64 elif rot == 1 or rot == 3: self.width = 64 self.height = 128 self.rot = rot def clear_bytes(self, count): for pos in range(count): self.cmdbuf[pos] = 0 def send_flag(self, b): count = 3 pos = 0 while pos < count: self.cmdbuf[pos] = 0 pos += 1 self.cmdbuf[0] = 0b11111000 # rs = 0 self.cmdbuf[1] = b & 0xF0 self.cmdbuf[2] = (b & 0x0F) << 4 submv = self.cmdmv[:count] self.spi.write(submv) del submv def send_address(self, b1, b2): count = 5 pos = 0 while pos < count: self.cmdbuf[pos] = 0 pos += 1 self.cmdbuf[0] = 0b11111000 # rs = 0 self.cmdbuf[1] = b1 & 0xF0 self.cmdbuf[2] = (b1 & 0x0F) << 4 self.cmdbuf[3] = b2 & 0xF0 self.cmdbuf[4] = (b2 & 0x0F) << 4 submv = self.cmdmv[:count] self.spi.write(submv) del submv def send_data(self, arr): arrlen = len(arr) count = 1 + (arrlen << 1) pos = 0 while pos < count: self.cmdbuf[pos] = 0 pos += 1 self.cmdbuf[0] = 0b11111000 | 0x02 # rs = 1 pos = 0 while pos < arrlen: # inlined code from marshal_byte self.cmdbuf[1 + (pos << 1)] = arr[pos] & 0xF0 self.cmdbuf[2 + (pos << 1)] = (arr[pos] & 0x0F) << 4 pos += 1 submv = self.cmdmv[:count] self.spi.write(submv) del submv def clear(self): rowPos = 0 while rowPos < rowBound: row = self.fbuff[rowPos] colPos = 0 while colPos < colBound: row[colPos] = 0 colPos += 1 rowPos += 1 def create_plotter(self, write=True): if write: if self.rot == 0: def plot(x, y): self.fbuff[y][x // 8] |= 1 << (7 - (x % 8)) elif self.rot == 1: def plot(x, y): self.fbuff[x][15 - (y // 8)] |= 1 << (y % 8) elif self.rot == 2: def plot(x, y): self.fbuff[63 - y][15 - (x // 8)] |= 1 << (x % 8) elif self.rot == 3: def plot(x, y): self.fbuff[63 - x][y // 8] |= 1 << (7 - (y % 8)) else: if self.rot == 0: def plot(x, y): self.fbuff[y][x // 8] &= ~(1 << (7 - (x % 8))) elif self.rot == 1: def plot(x, y): self.fbuff[x][15 - (y // 8)] &= ~(1 << (y % 8)) elif self.rot == 2: def plot(x, y): self.fbuff[63 - y][15 - (x // 8)] &= ~(1 << (x % 8)) elif self.rot == 3: def plot(x, y): self.fbuff[63 - x][y // 8] &= ~(1 << (7 - (y % 8))) return plot def redraw(self, dx1=None, dy1=None, dx2=None, dy2=None): """ # TODO CH bug here? (inherited from https://github.com/JMW95/pyST7920 ) buffer address ranges calculated incorrect for (bottom-right?) rectangles # TODO CH HACK uncomment 4 lines below for redraw rectangle to be ignored dx1 = 0 dy1 = 0 dx2 = 127 dy2 = 63 """ # TODO CH consider more efficient bounds checking if dx1 is None: dx1 = 0 else: dx1 = max(0, dx1) dx1 = min(127, dx1) if dx2 is None: dx2 = 127 else: dx2 = max(0, dx2) dx2 = min(127, dx2) if dy1 is None: dy1 = 0 else: dy1 = max(0, dy1) dy1 = min(63, dy1) if dy2 is None: dy2 = 63 else: dy2 = max(0, dy2) dy2 = min(63, dy2) try: self.select(True) i = dy1 while i < dy2 + 1: self.send_address(0x80 + i % 32, 0x80 + ((dx1 // 16) + (8 if i >= 32 else 0))) self.send_data(self.fbuff[i][dx1 // 16:(dx2 // 8) + 1]) i += 1 finally: self.select(False)
class WS2812: """ Driver for WS2812 RGB LEDs. May be used for controlling single LED or chain of LEDs. Example of use: chain = WS2812(ledNumber=4) data = [ (255, 0, 0), # red (0, 255, 0), # green (0, 0, 255), # blue (85, 85, 85), # white ] chain.show(data) Version: 1.0 """ # Values to put inside SPi register for each color's bit buf_bytes = (0xE0E0, 0xFCE0, 0xE0FC, 0xFCFC) def __init__(self, ledNumber=1, brightness=100, dataPin='P22'): """ Params: * ledNumber = count of LEDs * brightness = light brightness (integer : 0 to 100%) * dataPin = pin to connect data channel (LoPy only) """ self.ledNumber = ledNumber self.brightness = brightness # Prepare SPI data buffer (8 bytes for each color) self.buf_length = self.ledNumber * 3 * 8 self.buf = bytearray(self.buf_length) # SPI init # Bus 0, 8MHz => 125 ns by bit, 8 clock cycle when bit transfert+2 clock cycle between each transfert # => 125*10=1.25 us required by WS2812 if uname().sysname == 'LoPy': self.spi = SPI(0, SPI.MASTER, baudrate=8000000, polarity=0, phase=1, pins=(None, dataPin, None)) # Enable pull down Pin(dataPin, mode=Pin.OUT, pull=Pin.PULL_DOWN) else: #WiPy self.spi = SPI(0, SPI.MASTER, baudrate=8000000, polarity=0, phase=1) # Enable pull down Pin('GP16', mode=Pin.ALT, pull=Pin.PULL_DOWN) # Turn LEDs off self.show([]) def show(self, data): """ Show RGB data on LEDs. Expected data = [(R, G, B), ...] where R, G and B are intensities of colors in range from 0 to 255. One RGB tuple for each LED. Count of tuples may be less than count of connected LEDs. """ self.fill_buf(data) self.send_buf() def send_buf(self): """ Send buffer over SPI. """ disable_irq() self.spi.write(self.buf) enable_irq() gc.collect() def update_buf(self, data, start=0): """ Fill a part of the buffer with RGB data. Order of colors in buffer is changed from RGB to GRB because WS2812 LED has GRB order of colors. Each color is represented by 4 bytes in buffer (1 byte for each 2 bits). Returns the index of the first unfilled LED Note: If you find this function ugly, it's because speed optimisations beated purity of code. """ buf = self.buf buf_bytes = self.buf_bytes brightness = self.brightness index = start * 24 for red, green, blue in data: red = int(red * brightness // 100) green = int(green * brightness // 100) blue = int(blue * brightness // 100) buf[index] = buf_bytes[green >> 6 & 0x03] buf[index+2] = buf_bytes[green >> 4 & 0x03] buf[index+4] = buf_bytes[green >> 2 & 0x03] buf[index+6] = buf_bytes[green & 0x03] buf[index+8] = buf_bytes[red >> 6 & 0x03] buf[index+10] = buf_bytes[red >> 4 & 0x03] buf[index+12] = buf_bytes[red >> 2 & 0x03] buf[index+14] = buf_bytes[red & 0x03] buf[index+16] = buf_bytes[blue >> 6 & 0x03] buf[index+18] = buf_bytes[blue >> 4 & 0x03] buf[index+20] = buf_bytes[blue >> 2 & 0x03] buf[index+22] = buf_bytes[blue & 0x03] index += 24 return index // 24 def fill_buf(self, data): """ Fill buffer with RGB data. All LEDs after the data are turned off. """ end = self.update_buf(data) # Turn off the rest of the LEDs buf = self.buf off = self.buf_bytes[0] for index in range(end * 24, self.buf_length): buf[index] = off index += 2 def set_brightness(self, brightness): """ Set brighness of all leds """ self.brightness = brightness
class MFRC522: RESET = 'GP22' CLK = 'GP14' MISO = 'GP15' MOSI = 'GP16' CS = 'GP17' MAX_LEN = 16 PCD_IDLE = 0x00 PCD_AUTHENT = 0x0E PCD_TRANSCEIVE = 0x0C PCD_RESETPHASE = 0x0F PCD_CALCCRC = 0x03 PICC_REQIDL = 0x26 PICC_REQALL = 0x52 PICC_ANTICOLL = 0x93 PICC_SElECTTAG = 0x93 PICC_AUTHENT1A = 0x60 PICC_READ = 0x30 PICC_WRITE = 0xA0 MI_OK = 0 MI_NOTAGERR = 1 MI_ERR = 2 MI_AUTH_ERROR_STATUS2REG = 3 CommandReg = 0x01 CommIEnReg = 0x02 CommIrqReg = 0x04 DivIrqReg = 0x05 ErrorReg = 0x06 Status2Reg = 0x08 FIFODataReg = 0x09 FIFOLevelReg = 0x0A WaterLevelReg = 0x0B ControlReg = 0x0C BitFramingReg = 0x0D ModeReg = 0x11 TxControlReg = 0x14 TxAutoReg = 0x15 CRCResultRegM = 0x21 CRCResultRegL = 0x22 TModeReg = 0x2A TPrescalerReg = 0x2B TReloadRegH = 0x2C TReloadRegL = 0x2D serNum = [] def __init__(self, spd=1000000): # first assign CLK, MISO, MOSI, CS to the correct pins self.pin_clk = Pin(self.CLK, mode=Pin.OUT) # CLK self.pin_miso = Pin(self.MISO) # MISO self.pin_mosi = Pin(self.MOSI, mode=Pin.OUT) # MOSI self.pin_cs = Pin(self.CS, mode=Pin.OUT) # NSS/CS self.pin_reset = Pin(self.RESET, mode=Pin.OUT) self.pin_reset.value(0) self.pin_cs.value(1) self.spi = SPI(0) self.spi.init(mode=SPI.MASTER, baudrate=spd, pins=(self.CLK, self.MOSI, self.MISO)) self.pin_reset.value(1) self.MFRC522_Init() def MFRC522_Reset(self): self.Write_MFRC522(self.CommandReg, self.PCD_RESETPHASE) def Write_MFRC522(self, addr, val): self.pin_cs.value(0) # 0(MSB = Write) ADDR1 ADDR2 ADDR3 ADDR4 ADDR5 ADDR6 0(LSB = 0) DATA spiBytes = bytearray(2) spiBytes[0] = ((addr<<1)&0x7E) spiBytes[1] = val self.spi.write(spiBytes) self.pin_cs.value(1) def Read_MFRC522(self, addr): self.pin_cs.value(0) # 1(MSB = Read) ADDR1 ADDR2 ADDR3 ADDR4 ADDR5 ADDR6 0(LSB = 0) self.spi.write((addr<<1)&0x7E|0x80) data = self.spi.read(1) self.pin_cs.value(1) return data[0] def SetBitMask(self, reg, mask): tmp = self.Read_MFRC522(reg) self.Write_MFRC522(reg, tmp | mask) def ClearBitMask(self, reg, mask): tmp = self.Read_MFRC522(reg); self.Write_MFRC522(reg, tmp & (~mask)) def AntennaOn(self): temp = self.Read_MFRC522(self.TxControlReg) if(~(temp & 0x03)): self.SetBitMask(self.TxControlReg, 0x03) def AntennaOff(self): self.ClearBitMask(self.TxControlReg, 0x03) def MFRC522_ToCard(self,command,sendData): backData = [] backLen = 0 status = self.MI_ERR irqEn = 0x00 waitIRq = 0x00 lastBits = None n = 0 i = 0 if command == self.PCD_AUTHENT: irqEn = 0x12 waitIRq = 0x10 if command == self.PCD_TRANSCEIVE: irqEn = 0x77 waitIRq = 0x30 self.Write_MFRC522(self.CommIEnReg, irqEn|0x80) self.ClearBitMask(self.CommIrqReg, 0x80) self.SetBitMask(self.FIFOLevelReg, 0x80) self.Write_MFRC522(self.CommandReg, self.PCD_IDLE); while(i<len(sendData)): self.Write_MFRC522(self.FIFODataReg, sendData[i]) i = i+1 self.Write_MFRC522(self.CommandReg, command) if command == self.PCD_TRANSCEIVE: self.SetBitMask(self.BitFramingReg, 0x80) i = 2000 while True: n = self.Read_MFRC522(self.CommIrqReg) i = i - 1 if ~((i!=0) and ~(n&0x01) and ~(n&waitIRq)): break self.ClearBitMask(self.BitFramingReg, 0x80) if i != 0: st = self.Read_MFRC522(self.ErrorReg) if (st & 0x1B)==0x00: status = self.MI_OK if n & irqEn & 0x01: status = self.MI_NOTAGERR elif command == self.PCD_TRANSCEIVE: n = self.Read_MFRC522(self.FIFOLevelReg) lastBits = self.Read_MFRC522(self.ControlReg) & 0x07 if lastBits != 0: backLen = (n-1)*8 + lastBits else: backLen = n*8 if n == 0: n = 1 if n > self.MAX_LEN: n = self.MAX_LEN i = 0 while i<n: backData.append(self.Read_MFRC522(self.FIFODataReg)) i = i + 1; else: status = self.MI_ERR return (status,backData,backLen) def MFRC522_Request(self, reqMode): status = None backBits = None TagType = [reqMode] self.Write_MFRC522(self.BitFramingReg, 0x07) (status,backData,backBits) = self.MFRC522_ToCard(self.PCD_TRANSCEIVE, TagType) if ((status != self.MI_OK) | (backBits != 0x10)): status = self.MI_ERR return (status,backBits) def MFRC522_Anticoll(self): backData = [] serNumCheck = 0 serNum = [self.PICC_ANTICOLL, 0x20] self.Write_MFRC522(self.BitFramingReg, 0x00) (status,backData,backBits) = self.MFRC522_ToCard(self.PCD_TRANSCEIVE,serNum) if(status == self.MI_OK): i = 0 if len(backData)==5: while i<4: serNumCheck = serNumCheck ^ backData[i] i = i + 1 if serNumCheck != backData[i]: status = self.MI_ERR else: status = self.MI_ERR return (status,backData) def CalulateCRC(self, pIndata): self.ClearBitMask(self.DivIrqReg, 0x04) self.SetBitMask(self.FIFOLevelReg, 0x80); i = 0 while i<len(pIndata): self.Write_MFRC522(self.FIFODataReg, pIndata[i]) i = i + 1 self.Write_MFRC522(self.CommandReg, self.PCD_CALCCRC) i = 0xFF while True: n = self.Read_MFRC522(self.DivIrqReg) i = i - 1 if not ((i != 0) and not (n&0x04)): break pOutData = [] pOutData.append(self.Read_MFRC522(self.CRCResultRegL)) pOutData.append(self.Read_MFRC522(self.CRCResultRegM)) return pOutData def MFRC522_SelectTag(self, serNum): backData = [] buf = [self.PICC_SElECTTAG, 0x70] + serNum[:5] buf += self.CalulateCRC(buf) (status, backData, backLen) = self.MFRC522_ToCard(self.PCD_TRANSCEIVE, buf) if (status == self.MI_OK) and (backLen == 0x18): return self.MI_OK else: return self.MI_ERR def MFRC522_Auth(self, authMode, BlockAddr, Sectorkey, serNum): #First byte should be the authMode (A or B) #Second byte is the trailerBlock (usually 7) #Now we need to append the authKey which usually is 6 bytes of 0xFF #Next we append the first 4 bytes of the UID buff = [authMode, BlockAddr] + Sectorkey + serNum[:4] # Now we start the authentication itself (status, backData, backLen) = self.MFRC522_ToCard(self.PCD_AUTHENT,buff) # Check if an error occurred # Return the status return status def MFRC522_StopCrypto1(self): self.ClearBitMask(self.Status2Reg, 0x08) def MFRC522_Read(self, blockAddr): recvData = [self.PICC_READ, blockAddr] recvData += self.CalulateCRC(recvData) (status, backData, backLen) = self.MFRC522_ToCard(self.PCD_TRANSCEIVE, recvData) return (status, backData) def MFRC522_Write(self, blockAddr, writeData): buff = [self.PICC_WRITE, blockAddr] buff += self.CalulateCRC(buff) (status, backData, backLen) = self.MFRC522_ToCard(self.PCD_TRANSCEIVE, buff) if not(status == self.MI_OK) or not(backLen == 4) or not((backData[0] & 0x0F) == 0x0A): status = self.MI_ERR if status == self.MI_OK: i = 0 buf = [] while i < 16: buf.append(writeData[i]) i = i + 1 buf += self.CalulateCRC(buf) (status, backData, backLen) = self.MFRC522_ToCard(self.PCD_TRANSCEIVE,buf) if not(status == self.MI_OK) or not(backLen == 4) or not((backData[0] & 0x0F) == 0x0A): status = self.MI_ERR return status def MFRC522_Init(self): self.MFRC522_Reset(); self.Write_MFRC522(self.TModeReg, 0x8D) self.Write_MFRC522(self.TPrescalerReg, 0x3E) self.Write_MFRC522(self.TReloadRegL, 30) self.Write_MFRC522(self.TReloadRegH, 0) self.Write_MFRC522(self.TxAutoReg, 0x40) self.Write_MFRC522(self.ModeReg, 0x3D) self.AntennaOn()
class LCD_1inch14(framebuf.FrameBuffer): def __init__(self): self.width = 240 self.height = 135 self.cs = Pin(CS,Pin.OUT) self.rst = Pin(RST,Pin.OUT) self.cs(1) self.spi = SPI(1) self.spi = SPI(1,1000_000) self.spi = SPI(1,10000_000,polarity=0, phase=0,sck=Pin(SCK),mosi=Pin(MOSI),miso=None) self.dc = Pin(DC,Pin.OUT) self.dc(1) self.buffer = bytearray(self.height * self.width * 2) super().__init__(self.buffer, self.width, self.height, framebuf.RGB565) self.init_display() self.red = 0x07E0 self.green = 0x001f self.blue = 0xf800 self.white = 0xffff self.black = 0x0000 def write_cmd(self, cmd): self.cs(1) self.dc(0) self.cs(0) self.spi.write(bytearray([cmd])) self.cs(1) def write_data(self, buf): self.cs(1) self.dc(1) self.cs(0) self.spi.write(bytearray([buf])) self.cs(1) def init_display(self): """Initialize dispaly""" self.rst(1) self.rst(0) self.rst(1) self.write_cmd(0x36) self.write_data(0x70) self.write_cmd(0x3A) self.write_data(0x05) self.write_cmd(0xB2) self.write_data(0x0C) self.write_data(0x0C) self.write_data(0x00) self.write_data(0x33) self.write_data(0x33) self.write_cmd(0xB7) self.write_data(0x35) self.write_cmd(0xBB) self.write_data(0x19) self.write_cmd(0xC0) self.write_data(0x2C) self.write_cmd(0xC2) self.write_data(0x01) self.write_cmd(0xC3) self.write_data(0x12) self.write_cmd(0xC4) self.write_data(0x20) self.write_cmd(0xC6) self.write_data(0x0F) self.write_cmd(0xD0) self.write_data(0xA4) self.write_data(0xA1) self.write_cmd(0xE0) self.write_data(0xD0) self.write_data(0x04) self.write_data(0x0D) self.write_data(0x11) self.write_data(0x13) self.write_data(0x2B) self.write_data(0x3F) self.write_data(0x54) self.write_data(0x4C) self.write_data(0x18) self.write_data(0x0D) self.write_data(0x0B) self.write_data(0x1F) self.write_data(0x23) self.write_cmd(0xE1) self.write_data(0xD0) self.write_data(0x04) self.write_data(0x0C) self.write_data(0x11) self.write_data(0x13) self.write_data(0x2C) self.write_data(0x3F) self.write_data(0x44) self.write_data(0x51) self.write_data(0x2F) self.write_data(0x1F) self.write_data(0x1F) self.write_data(0x20) self.write_data(0x23) self.write_cmd(0x21) self.write_cmd(0x11) self.write_cmd(0x29) def show(self): self.write_cmd(0x2A) self.write_data(0x00) self.write_data(0x28) self.write_data(0x01) self.write_data(0x17) self.write_cmd(0x2B) self.write_data(0x00) self.write_data(0x35) self.write_data(0x00) self.write_data(0xBB) self.write_cmd(0x2C) self.cs(1) self.dc(1) self.cs(0) self.spi.write(self.buffer) self.cs(1)
class ILI9341: def __init__(self, width, height): self.width = width self.height = height self.pages = self.height // 8 self.buffer = bytearray(self.pages * self.width) self.framebuf = framebuf.FrameBuffer(self.buffer, self.width, self.height, framebuf.MONO_VLSB) self.spi = SPI(0) # chip select self.cs = Pin("P16", mode=Pin.OUT, pull=Pin.PULL_UP) # command self.dc = Pin("P17", mode=Pin.OUT, pull=Pin.PULL_UP) # initialize all pins high self.cs.high() self.dc.high() self.spi.init(baudrate=8000000, phase=0, polarity=0) self.init_display() def init_display(self): time.sleep_ms(500) self.write_cmd(0x01) time.sleep_ms(200) self.write_cmd(0xCF) self.write_data(bytearray([0x00, 0x8B, 0x30])) self.write_cmd(0xED) self.write_data(bytearray([0x67, 0x03, 0x12, 0x81])) self.write_cmd(0xE8) self.write_data(bytearray([0x85, 0x10, 0x7A])) self.write_cmd(0xCB) self.write_data(bytearray([0x39, 0x2C, 0x00, 0x34, 0x02])) self.write_cmd(0xF7) self.write_data(bytearray([0x20])) self.write_cmd(0xEA) self.write_data(bytearray([0x00, 0x00])) # Power control self.write_cmd(0xC0) # VRH[5:0] self.write_data(bytearray([0x1B])) # Power control self.write_cmd(0xC1) # SAP[2:0];BT[3:0] self.write_data(bytearray([0x10])) # VCM control self.write_cmd(0xC5) self.write_data(bytearray([0x3F, 0x3C])) # VCM control2 self.write_cmd(0xC7) self.write_data(bytearray([0xB7])) # Memory Access Control self.write_cmd(0x36) self.write_data(bytearray([0x08])) self.write_cmd(0x3A) self.write_data(bytearray([0x55])) self.write_cmd(0xB1) self.write_data(bytearray([0x00, 0x1B])) # Display Function Control self.write_cmd(0xB6) self.write_data(bytearray([0x0A, 0xA2])) # 3Gamma Function Disable self.write_cmd(0xF2) self.write_data(bytearray([0x00])) # Gamma curve selected self.write_cmd(0x26) self.write_data(bytearray([0x01])) # Set Gamma self.write_cmd(0xE0) self.write_data(bytearray([0x0F, 0x2A, 0x28, 0x08, 0x0E, 0x08, 0x54, 0XA9, 0x43, 0x0A, 0x0F, 0x00, 0x00, 0x00, 0x00])) # Set Gamma self.write_cmd(0XE1) self.write_data(bytearray([0x00, 0x15, 0x17, 0x07, 0x11, 0x06, 0x2B, 0x56, 0x3C, 0x05, 0x10, 0x0F, 0x3F, 0x3F, 0x0F])) # Exit Sleep self.write_cmd(0x11) time.sleep_ms(120) # Display on self.write_cmd(0x29) time.sleep_ms(500) self.fill(0) def show(self): # set col self.write_cmd(0x2A) self.write_data(bytearray([0x00, 0x00])) self.write_data(bytearray([0x00, 0xef])) # set page self.write_cmd(0x2B) self.write_data(bytearray([0x00, 0x00])) self.write_data(bytearray([0x01, 0x3f])) self.write_cmd(0x2c); num_of_pixels = self.height * self.width for row in range(0, self.pages): for pixel_pos in range(0, 8): for col in range(0, self.width): compressed_pixel = self.buffer[row * 240 + col] if ((compressed_pixel >> pixel_pos) & 0x1) == 0: self.write_data(bytearray([0x00, 0x00])) else: self.write_data(bytearray([0xFF, 0xFF])) def fill(self, col): self.framebuf.fill(col) def pixel(self, x, y, col): self.framebuf.pixel(x, y, col) def scroll(self, dx, dy): self.framebuf.scroll(dx, dy) def text(self, string, x, y, col=1): self.framebuf.text(string, x, y, col) def write_cmd(self, cmd): self.dc.low() self.cs.low() self.spi.write(bytearray([cmd])) self.cs.high() def write_data(self, buf): self.dc.high() self.cs.low() self.spi.write(buf) self.cs.high()
class EPD(framebuf.FrameBuffer): def __init__(self, width, height): self.spi = SPI(1, baudrate=20000000, polarity=0, phase=0, sck=Pin(18), mosi=Pin(23), miso=Pin(19)) #self.spi = SPI(1, 10000000, sck=Pin(14), mosi=Pin(13), miso=Pin(12)) self.spi.init() dc = Pin(27) cs = Pin(5) rst = Pin(14) busy = Pin(4) self.cs = cs self.dc = dc self.rst = rst self.busy = busy self.cs.init(self.cs.OUT, value=1) self.dc.init(self.dc.OUT, value=0) self.rst.init(self.rst.OUT, value=0) self.busy.init(self.busy.IN) self.width = width self.height = height self.pages = self.height // 8 self.buffer = bytearray(self.pages * self.width) super().__init__(self.buffer, self.width, self.height, framebuf.MONO_VLSB) self.init_display() def init_display(self): #Reset EPD Driver IC self.reset() #Booster soft start self._command(BOOSTER_SOFT_START_CONTROL, b'\x17\x17\x17') #Power setting self._command(POWER_SETTING, b'\x03\x00\x2B\x2B\x09') #Power on self._command(POWER_ON) #Check busy pin and proceed if idle self.wait_until_idle() #Panel setting - B&W and full resolution self._command(PANEL_SETTING, b'\x1F') #PLL control - Set to 100Hz, default is 50Hz self._command(PLL_CONTROL, b'\x3A') #Resolution setting self._command(RES_SETTING, b'\x01\x90\x01\x2C') #VCM_DC setting - Currently set to -1V, default is -0.1V i.e. b'\x00' self._command(VCM_DC_SETTING, b'\x12') #VCOM and Data Interval setting self._command(VCOM_DATA_INT_SETTING, b'\x87') #Finish initialisation with refresh of a blank screen self.fill(0) self.show() def poweroff(self): self._command(POWER_OFF) def poweron(self): self._command(POWER_ON) def show(self): self._command(START_TRANSMISSION_2) self._data(self.buffer) self._command(DATA_STOP) self._command(DISP_REFRESH) self.wait_until_idle() # To display your own buffer, instead of the framebuf buffer def show_buffer(self, buf): self.wait_until_idle() self._command(START_TRANSMISSION_2) for i in range(0, len(buf)): self._data(bytearray([buf[i]])) self._command(DATA_STOP) self._command(DISP_REFRESH) self.wait_until_idle() def _command(self, command, data=None): self.cs(1) # according to LOLIN_EPD self.dc(0) self.cs(0) self.spi.write(bytearray([command])) self.cs(1) if data is not None: self._data(data) def _data(self, data): self.cs(1) # according to LOLIN_EPD self.dc(1) self.cs(0) self.spi.write(data) self.cs(1) def wait_until_idle(self): while self.busy == 0: pass return def reset(self): self.rst(1) sleep_ms(1) self.rst(0) sleep_ms(10) self.rst(1) # to wake call reset() or init() def sleep(self): self._command(bytearray([DEEP_SLEEP_MODE])) self.wait_until_idle()
class MFRC522: OK = 0 NOTAGERR = 1 ERR = 2 REQIDL = 0x26 REQALL = 0x52 AUTHENT1A = 0x60 AUTHENT1B = 0x61 def __init__(self, sck, mosi, miso, rst, cs): self.sck = Pin(sck, Pin.OUT) self.mosi = Pin(mosi, Pin.OUT) self.miso = Pin(miso) self.rst = Pin(rst, Pin.OUT) self.cs = Pin(cs, Pin.OUT) self.rst.value(0) self.cs.value(1) if uname()[0] == 'WiPy': self.spi = SPI(0) self.spi.init(SPI.MASTER, baudrate=1000000, pins=(self.sck, self.mosi, self.miso)) elif uname()[0] == 'esp8266': self.spi = SPI(baudrate=100000, polarity=0, phase=0, sck=self.sck, mosi=self.mosi, miso=self.miso) self.spi.init() else: raise RuntimeError("Unsupported platform") self.rst.value(1) self.init() def _wreg(self, reg, val): self.cs.value(0) self.spi.write(b'%c' % int(0xff & ((reg << 1) & 0x7e))) self.spi.write(b'%c' % int(0xff & val)) self.cs.value(1) def _rreg(self, reg): self.cs.value(0) self.spi.write(b'%c' % int(0xff & (((reg << 1) & 0x7e) | 0x80))) val = self.spi.read(1) self.cs.value(1) return val[0] def _sflags(self, reg, mask): self._wreg(reg, self._rreg(reg) | mask) def _cflags(self, reg, mask): self._wreg(reg, self._rreg(reg) & (~mask)) def _tocard(self, cmd, send): recv = [] bits = irq_en = wait_irq = n = 0 stat = self.ERR if cmd == 0x0E: irq_en = 0x12 wait_irq = 0x10 elif cmd == 0x0C: irq_en = 0x77 wait_irq = 0x30 self._wreg(0x02, irq_en | 0x80) self._cflags(0x04, 0x80) self._sflags(0x0A, 0x80) self._wreg(0x01, 0x00) for c in send: self._wreg(0x09, c) self._wreg(0x01, cmd) if cmd == 0x0C: self._sflags(0x0D, 0x80) i = 2000 while True: n = self._rreg(0x04) i -= 1 if ~((i != 0) and ~(n & 0x01) and ~(n & wait_irq)): break self._cflags(0x0D, 0x80) if i: if (self._rreg(0x06) & 0x1B) == 0x00: stat = self.OK if n & irq_en & 0x01: stat = self.NOTAGERR elif cmd == 0x0C: n = self._rreg(0x0A) lbits = self._rreg(0x0C) & 0x07 if lbits != 0: bits = (n - 1) * 8 + lbits else: bits = n * 8 if n == 0: n = 1 elif n > 16: n = 16 for _ in range(n): recv.append(self._rreg(0x09)) else: stat = self.ERR return stat, recv, bits def _crc(self, data): self._cflags(0x05, 0x04) self._sflags(0x0A, 0x80) for c in data: self._wreg(0x09, c) self._wreg(0x01, 0x03) i = 0xFF while True: n = self._rreg(0x05) i -= 1 if not ((i != 0) and not (n & 0x04)): break return [self._rreg(0x22), self._rreg(0x21)] def init(self): self.reset() self._wreg(0x2A, 0x8D) self._wreg(0x2B, 0x3E) self._wreg(0x2D, 30) self._wreg(0x2C, 0) self._wreg(0x15, 0x40) self._wreg(0x11, 0x3D) self.antenna_on() def reset(self): self._wreg(0x01, 0x0F) def antenna_on(self, on=True): if on and ~(self._rreg(0x14) & 0x03): self._sflags(0x14, 0x03) else: self._cflags(0x14, 0x03) def request(self, mode): self._wreg(0x0D, 0x07) (stat, recv, bits) = self._tocard(0x0C, [mode]) if (stat != self.OK) | (bits != 0x10): stat = self.ERR return stat, bits def anticoll(self): ser_chk = 0 ser = [0x93, 0x20] self._wreg(0x0D, 0x00) (stat, recv, bits) = self._tocard(0x0C, ser) if stat == self.OK: if len(recv) == 5: for i in range(4): ser_chk = ser_chk ^ recv[i] if ser_chk != recv[4]: stat = self.ERR else: stat = self.ERR return stat, recv def select_tag(self, ser): buf = [0x93, 0x70] + ser[:5] buf += self._crc(buf) (stat, recv, bits) = self._tocard(0x0C, buf) return self.OK if (stat == self.OK) and (bits == 0x18) else self.ERR def auth(self, mode, addr, sect, ser): return self._tocard(0x0E, [mode, addr] + sect + ser[:4])[0] def stop_crypto1(self): self._cflags(0x08, 0x08) def read(self, addr): data = [0x30, addr] data += self._crc(data) (stat, recv, _) = self._tocard(0x0C, data) return recv if stat == self.OK else None def write(self, addr, data): buf = [0xA0, addr] buf += self._crc(buf) (stat, recv, bits) = self._tocard(0x0C, buf) if not (stat == self.OK) or not (bits == 4) or not ((recv[0] & 0x0F) == 0x0A): stat = self.ERR else: buf = [] for i in range(16): buf.append(data[i]) buf += self._crc(buf) (stat, recv, bits) = self._tocard(0x0C, buf) if not (stat == self.OK) or not (bits == 4) or not ((recv[0] & 0x0F) == 0x0A): stat = self.ERR return stat
class SevenSegment: def __init__(self, digits=8, scan_digits=MAX7219_DIGITS, baudrate=SPI_BAUDRATE, cs=SPI_CS): """ Constructor: `digits` should be the total number of individual digits being displayed `cs` is the GPIO port to use for the chip select line of the SPI bus - defaults to GPIO 0 / D3 `scan_digits` is the number of digits each individual max7219 displays `baudrate` defaults to 100KHz, note that excessive rates may result in instability (and is probably unnecessary) """ self.digits = digits self.devices = -(-digits // scan_digits) # ceiling integer division self.scan_digits = scan_digits self._buffer = [0] * digits self._spi = SPI(SPI_BUS, baudrate=baudrate, polarity=0, phase=0) self._cs = Pin(cs, Pin.OUT, value=1) self.command(MAX7219_REG_SCANLIMIT, scan_digits - 1) # digits to display on each device 0-7 self.command(MAX7219_REG_DECODEMODE, 0) # use segments (not digits) self.command(MAX7219_REG_DISPLAYTEST, 0) # no display test self.command(MAX7219_REG_SHUTDOWN, 1) # not blanking mode self.brightness(7) # intensity: range: 0..15 self.clear() def command(self, register, data): """Sets a specific register some data, replicated for all cascaded devices.""" self._write([register, data] * self.devices) def _write(self, data): """Send the bytes (which should comprise of alternating command, data values) over the SPI device.""" self._cs.off() self._spi.write(bytes(data)) self._cs.on() def clear(self, flush=True): """Clears the buffer and if specified, flushes the display.""" self._buffer = [0] * self.digits if flush: self.flush() def flush(self): """For each digit, cascade out the contents of the buffer cells to the SPI device.""" for dev in range(self.devices): for pos in range(self.scan_digits): self._write([ pos + MAX7219_REG_DIGIT0, self._buffer[pos + (dev * self.scan_digits)] ] + ([MAX7219_REG_NOOP, 0] * dev)) def brightness(self, intensity): """Sets the brightness level of all cascaded devices to the same intensity level, ranging from 0..15.""" self.command(MAX7219_REG_INTENSITY, intensity) def letter(self, position, char, dot=False, flush=True): """Looks up the appropriate character representation for char and updates the buffer, flushes by default.""" value = get_char2(char) | (dot << 7) self._buffer[position] = value if flush: self.flush() def text(self, text): """Outputs the text (as near as possible) on the specific device.""" self.clear(False) text = text[:self.digits] # make sure we don't overrun the buffer for pos, char in enumerate(text): self.letter(pos, char, flush=False) self.flush() def number(self, val): """Formats the value according to the parameters supplied, and displays it.""" self.clear(False) strval = '' if isinstance(val, (int, float)): strval = str(val) elif isinstance(val, str): if val.replace('.', '', 1).strip().isdigit(): strval = val if '.' in strval: strval = strval[:self.digits + 1] else: strval = strval[:self.digits] pos = 0 for char in strval: dot = False if char == '.': continue else: if pos < len(strval) - 1: if strval[pos + 1] == '.': dot = True self.letter(pos, char, dot, False) pos += 1 self.flush() def scroll(self, rotate=True, reverse=False, flush=True): """Shifts buffer contents left or right (reverse), with option to wrap around (rotate).""" if reverse: tmp = self._buffer.pop() if rotate: self._buffer.insert(0, tmp) else: self._buffer.insert(0, 0x00) else: tmp = self._buffer.pop(0) if rotate: self._buffer.append(tmp) else: self._buffer.append(0x00) if flush: self.flush() def message(self, text, delay=0.4): """Transitions the text message across the devices from left-to-right.""" self.clear(False) for char in text: time.sleep(delay) self.scroll(rotate=False, flush=False) self.letter(self.digits - 1, char)