def distance_mm(self): spi = SPI(0, SPI.MASTER, baudrate=1250000) pre = 0 post = 0 k = -1 length = 500 resp = bytearray(length) resp[0] = 0xFF spi.write_readinto(resp, resp) # find first non zero value try: i, value = next((ind, v) for ind, v in enumerate(resp) if v) except StopIteration: i = -1 if i > 0: pre = bin(value).count("1") # find first non full high value afterwards try: k, value = next((ind, v) for ind, v in enumerate(resp[i:length - 2]) if resp[i + ind + 1] == 0) post = bin(value).count("1") if k else 0 k = k + i except StopIteration: i = -1 dist = -1 if i < 0 else round( ((pre + (k - i) * 8. + post) * 8 * 0.172) / 2) return dist
class SpiControl: "Simple higher-level SPI stuff" def __init__(self, pin_id_sck=PIN_ID_SCK, pin_id_miso=PIN_ID_MISO, pin_id_mosi=PIN_ID_MOSI, pin_id_lora_dio0=PIN_ID_LORA_DIO0, pin_id_lora_ss=PIN_ID_LORA_SS, pin_id_lora_reset=PIN_ID_LORA_RESET, baudrate=5000000, **kwargs): self.spi = SPI(1, baudrate=baudrate, polarity=0, phase=0, bits=8, firstbit=SPI.MSB, sck=Pin(pin_id_sck, Pin.OUT), mosi=Pin(pin_id_mosi, Pin.OUT), miso=Pin(pin_id_miso, Pin.IN)) self.pinss = Pin(pin_id_lora_ss, Pin.OUT) self.pinrst = Pin(pin_id_lora_reset, Pin.OUT) self.pin_id_lora_dio0 = pin_id_lora_dio0 # sx127x transfer is always write 2 bytes while reading the second byte # a read doesn't write the second byte. a write returns the prior value # write register # = 0x80 | read register # def transfer(self, address, value=0x00): response = bytearray(1) self.pinss.value(0) # hold chip select low self.spi.write(bytearray([address])) # write register address # write or read register walue self.spi.write_readinto(bytearray([value]), response) self.pinss.value(1) return response def get_irq_pin(self): "Get handle on a machine.Pin() for the LoRa's DIO0." irq_pin = Pin(self.pin_id_lora_dio0, Pin.IN) return irq_pin def init_lora_pins(self): "Initialize the pins for the LoRa device after instantiation of SX127x." self.pinss.value(1) # initialize CS to high (off) self.pinrst.value(1) # do a reset pulse sleep_ms(10) self.pinrst.value(0) sleep_ms(10) self.pinrst.value(1)
class SpiControl: ''' simple higher level spi stuff ''' def __init__(self): self.spi = SPI(1, baudrate=5000000, polarity=0, phase=0, bits=8, firstbit=SPI.MSB, sck=Pin(PIN_ID_SCK, Pin.OUT), mosi=Pin(PIN_ID_MOSI, Pin.OUT), miso=Pin(PIN_ID_MISO, Pin.IN)) self.pinss = Pin(PIN_ID_LORA_SS, Pin.OUT) self.pinrst = Pin(PIN_ID_LORA_RESET, Pin.OUT) # sx127x transfer is always write two bytes while reading the second byte # a read doesn't write the second byte. a write returns the prior value. # write register # = 0x80 | read register # def transfer(self, address, value=0x00): response = bytearray(1) self.pinss.value(0) # hold chip select low self.spi.write(bytearray([address])) # write register address self.spi.write_readinto(bytearray([value]), response) # write or read register walue self.pinss.value(1) return response # this doesn't belong here but it doesn't really belong anywhere, so put # it with the other loraconfig-ed stuff def getIrqPin(self): irqPin = Pin(PIN_ID_LORA_DIO0, Pin.IN) return irqPin # this doesn't belong here but it doesn't really belong anywhere, so put # it with the other loraconfig-ed stuff def initLoraPins(self): ''' initialize the pins for the LoRa device. ''' self.pinss.value(1) # initialize CS to high (off) self.pinrst.value(1) # do a reset pulse sleep(.01) self.pinrst.value(0) sleep(.01) self.pinrst.value(1)
class SPIBus(object): """SPI bus access.""" def __init__(self, freq, sc, mo, mi=None, spidev=2): self._spi = SPI(spidev) if mi == None: self._spi.init(baudrate=freq, sck=Pin(sc), mosi=Pin(mo)) else: self._spi.init(baudrate=freq, sck=Pin(sc), mosi=Pin(mo), miso=Pin(mi)) def deinit(self): self._spi.deinit() @property def bus(self): return self._spi def write_readinto(self, wbuf, rbuf): self._spi.write_readinto(wbuf, rbuf) def write(self, wbuf): self._spi.write(wbuf)
class SCL3300: def __init__(self, spinr: int, baudrate: int, sck: Pin, mosi: Pin, miso: Pin, cs: CS) -> None: self.cs = cs self.spi = SPI(spinr, baudrate, polarity=0, phase=0, bits=32, firstbit=SPI.MSB, sck=sck, mosi=mosi, miso=miso) def write(self, data: bytes | int) -> None: if isinstance(data, int): data = data.to_bytes(4, "big") try: self.cs.enable() self.spi.write(data) finally: self.cs.disable() def exchange(self, data: bytes | int) -> bytes: if isinstance(data, int): data = data.to_bytes(4, "big") received = bytearray(len(data)) try: self.cs.enable() received = self.spi.write_readinto(data, received) finally: self.cs.disable() return received def read(self, n) -> bytes: received = bytearray(n) try: self.cs.enable() received = self.spi.readinto(received, 0x00) finally: return received
class MFRC522(object): """ class for interacting with the 'RC522' NFC reader chip """ MI_OK = 0 MI_NOTAGERR = 1 MI_ERR = 2 PCD_IDLE = 0x00 PCD_AUTHENT = 0x0E PCD_RECEIVE = 0x08 PCD_TRANSMIT = 0x04 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_AUTHENT1B = 0x61 PICC_READ = 0x30 PICC_WRITE = 0xA0 PICC_DECREMENT = 0xC0 PICC_INCREMENT = 0xC1 PICC_RESTORE = 0xC2 PICC_TRANSFER = 0xB0 PICC_HALT = 0x50 Reserved00 = 0x00 CommandReg = 0x01 CommIEnReg = 0x02 DivlEnReg = 0x03 CommIrqReg = 0x04 DivIrqReg = 0x05 ErrorReg = 0x06 Status1Reg = 0x07 Status2Reg = 0x08 FIFODataReg = 0x09 FIFOLevelReg = 0x0A WaterLevelReg = 0x0B ControlReg = 0x0C BitFramingReg = 0x0D CollReg = 0x0E Reserved01 = 0x0F Reserved10 = 0x10 ModeReg = 0x11 TxModeReg = 0x12 RxModeReg = 0x13 TxControlReg = 0x14 TxAutoReg = 0x15 TxSelReg = 0x16 RxSelReg = 0x17 RxThresholdReg = 0x18 DemodReg = 0x19 Reserved11 = 0x1A Reserved12 = 0x1B MifareReg = 0x1C Reserved13 = 0x1D Reserved14 = 0x1E SerialSpeedReg = 0x1F Reserved20 = 0x20 CRCResultRegM = 0x21 CRCResultRegL = 0x22 Reserved21 = 0x23 ModWidthReg = 0x24 Reserved22 = 0x25 RFCfgReg = 0x26 GsNReg = 0x27 CWGsPReg = 0x28 ModGsPReg = 0x29 TModeReg = 0x2A TPrescalerReg = 0x2B TReloadRegH = 0x2C TReloadRegL = 0x2D TCounterValueRegH = 0x2E TCounterValueRegL = 0x2F Reserved30 = 0x30 TestSel1Reg = 0x31 TestSel2Reg = 0x32 TestPinEnReg = 0x33 TestPinValueReg = 0x34 TestBusReg = 0x35 AutoTestReg = 0x36 VersionReg = 0x37 AnalogTestReg = 0x38 TestDAC1Reg = 0x39 TestDAC2Reg = 0x3A TestADCReg = 0x3B Reserved31 = 0x3C Reserved32 = 0x3D Reserved33 = 0x3E Reserved34 = 0x3F MAX_LEN = 16 serNum = [] def __init__(self): ''' init the interface ''' if not MPY_ESP8266: # PYB specific use self.nrstpd = pyb.Pin(pyb.Pin.cpu.B10, pyb.Pin.OUT) # sets the pin to output self.nenbrc522 = pyb.Pin(pyb.Pin.cpu.A4, pyb.Pin.OUT) # sets the pin to output self.nenbrc522.high() # deselect device self.nrstpd.low() # put it in power down mode # reader has stable data on rising edge of signal (phase 0), clock is high active (polarity 0) self.spi = SPI(1, SPI.MASTER, baudrate=1000000, polarity=0, phase=0, firstbit=SPI.MSB) # default pins for SPI1 are selected else: # ESP8266 specific use self.nrstpd = machine.Pin(0, machine.Pin.OUT) # sets the pin to output self.nenbrc522 = machine.Pin(2, machine.Pin.OUT) # sets the pin to output self.nenbrc522.high() # deselect device self.nrstpd.low() # put it in power down mode # reader has stable data on rising edge of signal (phase 0), clock is high active (polarity 0) self.spi = SPI(SPI.MASTER, baudrate=1000000, polarity=0, phase=0, firstbit=SPI.MSB) # default pins for SPI are selected # go configure yourself self.MFRC522_Init() def Write_MFRC522(self, addr, val): data = bytearray(2) data[0] = (addr<<1) & 0x7E data[1] = val self.nenbrc522.low() # start the transaction if not MPY_ESP8266: self.spi.send(data) # transfer two bytes to the chip else: self.spi.write(data) # transfer buffer self.nenbrc522.high() # finished it def Read_MFRC522(self, addr): data = bytearray(2) buf = bytearray(2) # could reuse data instead of using buf data[0] = ((addr<<1)&0x7E) | 0x80 data[1] = 0x00 self.nenbrc522.low() # start transaction if not MPY_ESP8266: self.spi.send_recv(data,buf) # send data and read two bytes back else: self.spi.write_readinto(data,buf) # send data and read two bytes back self.nenbrc522.high() # transaction ended return buf[1] def MFRC522_Reset(self): self.Write_MFRC522(self.CommandReg, self.PCD_RESETPHASE) 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: if (self.Read_MFRC522(self.ErrorReg) & 0x1B)==0x00: status = self.MI_OK if n & irqEn & 0x01: status = self.MI_NOTAGERR if 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 = [] self.Write_MFRC522(self.BitFramingReg, 0x07) TagType.append(reqMode); (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.Write_MFRC522(self.BitFramingReg, 0x00) serNum.append(self.PICC_ANTICOLL) serNum.append(0x20) (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 = [] buf.append(self.PICC_SElECTTAG) buf.append(0x70) i = 0 while i<5: buf.append(serNum[i]) i = i + 1 pOut = self.CalulateCRC(buf) buf.append(pOut[0]) buf.append(pOut[1]) (status, backData, backLen) = self.MFRC522_ToCard(self.PCD_TRANSCEIVE, buf) if (status == self.MI_OK) and (backLen == 0x18): print ("Size: " + str(backData[0])) return backData[0] else: return 0 def MFRC522_Auth(self, authMode, BlockAddr, Sectorkey, serNum): buff = [] # First byte should be the authMode (A or B) buff.append(authMode) # Second byte is the trailerBlock (usually 7) buff.append(BlockAddr) # Now we need to append the authKey which usually is 6 bytes of 0xFF i = 0 while(i < len(Sectorkey)): buff.append(Sectorkey[i]) i = i + 1 i = 0 # Next we append the first 4 bytes of the UID while(i < 4): buff.append(serNum[i]) i = i +1 # Now we start the authentication itself (status, backData, backLen) = self.MFRC522_ToCard(self.PCD_AUTHENT,buff) # Check if an error occurred if not(status == self.MI_OK): print ("AUTH ERROR!!") if not (self.Read_MFRC522(self.Status2Reg) & 0x08) != 0: print ("AUTH ERROR(status2reg & 0x08) != 0" ) # Return the status return status def MFRC522_StopCrypto1(self): self.ClearBitMask(self.Status2Reg, 0x08) def MFRC522_Read(self, blockAddr): recvData = [] recvData.append(self.PICC_READ) recvData.append(blockAddr) pOut = self.CalulateCRC(recvData) recvData.append(pOut[0]) recvData.append(pOut[1]) (status, backData, backLen) = self.MFRC522_ToCard(self.PCD_TRANSCEIVE, recvData) if not(status == self.MI_OK): print ("Error while reading!") i = 0 if len(backData) == 16: print ("Sector "+str(blockAddr)+" "+str(backData)) def MFRC522_Write(self, blockAddr, writeData): buff = [] buff.append(self.PICC_WRITE) buff.append(blockAddr) crc = self.CalulateCRC(buff) buff.append(crc[0]) buff.append(crc[1]) (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 print (str(backLen)+" backdata &0x0F == 0x0A "+str(backData[0]&0x0F)) if status == self.MI_OK: i = 0 buf = [] while i < 16: buf.append(writeData[i]) i = i + 1 crc = self.CalulateCRC(buf) buf.append(crc[0]) buf.append(crc[1]) (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): print ("Error while writing") if status == self.MI_OK: print ("Data written") def MFRC522_DumpClassic1K(self, key, uid): ''' dump the entire 1K of data ''' i = 0 while i < 64: status = self.MFRC522_Auth(self.PICC_AUTHENT1A, i, key, uid) # Check if authenticated if status == self.MI_OK: self.MFRC522_Read(i) else: print ("Authentication error") i = i+1 def MFRC522_Version(self): ''' return version of silicon used reader returned 0x11 expected 0x91 (so we missed one bit?) ''' val = self.Read_MFRC522(self.VersionReg) & 0x0F return val def MFRC522_ReceiverGain(self): ''' the receiver gain is variable from 18 to 48dB ''' #self.Write_MFRC522(self.RFCfgReg, 0x4<<4) val = (self.Read_MFRC522(self.RFCfgReg) & 0x7F) >> 4 if (val==0) or (val==2): val = 18 elif (val==1) or (val==3): val = 23 elif val==4: val = 33 elif val==5: val=38 elif val==6: val=43 else: val=48 return val def MFRC522_Init(self): ''' init the device registers ''' self.nrstpd.high() # get it out of reset self.MFRC522_Reset(); if not MPY_ESP8266: pyb.delay(500) else: time.sleep_ms(500) print("MFRC522 version is " + str(self.MFRC522_Version() ) ) print("Receiver gain is " + str(self.MFRC522_ReceiverGain()) + "dB") 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() def MFRC522_DeInit(self): ''' turn it off and sleep ''' self.AntennaOff() self.nenbrc522.high() self.nrstpd.low() # put the chip into power down self.spi.deinit() # turn off SPI bus
# br = 10_000 # 10 K buffer_w = bytearray([0x1, 0x2, 0x3, 0x1, 0x2, 0x3, 0x1, 0x2, 0x3, 0x66]) # buffer_w = bytearray([0xa,0xa,0xa, 0xa,0xa,0xa, 0xa,0xa,0xa, 0]) #buffer_w = bytearray([0xa,0xe,0xa, 0xe,0xa,0xe, 0xa,0xe,0xa, 0]) buffer_r = bytearray(len(buffer_w)) while True: for s in range(len(speeds)): spi = SPI(0, SPI.MASTER, baudrate=speeds[s], polarity=0, phase=0, pins=spi_pins) for i in range(10): # cs(0) l = spi.write_readinto(buffer_w, buffer_r) # cs(1) # print(buffer_r) # print(buffer_r == buffer_w) if buffer_r == buffer_w: ok[s] += 1 else: fail[s] += 1 time.sleep(0.01) for s in range(len(speeds)): print("{}:{}/{}".format(speeds[s], ok[s], fail[s]), end=' ') print() sys.exit() log(miso)
class osd: def __init__(self): self.screen_x = const(64) self.screen_y = const(20) self.cwd = "/" self.init_fb() self.exp_names = " KMGTE" self.mark = bytearray([32, 16, 42]) # space, right triangle, asterisk self.diskfile = [open("main.py", "rb"), open("main.py", "rb")] # any dummy file that can open self.imgtype = bytearray( 2) # [0]=0:.mac/.bin 1638400 bytes, [0]=1:.dsk 819200 bytes self.drive = bytearray(1) # [0]=0:1st drive, [0]=1:2nd drive self.conv_dataIn12K = bytearray(12 * 1024) # memoryview for each track//16 datainmv = memoryview(self.conv_dataIn12K) self.conv_dataIn = [] for i in range(5): self.conv_dataIn.append(memoryview(datainmv[0:(12 - i) * 1024])) self.conv_nibsOut = bytearray(1024) dsk2mac.init_nibsOut(self.conv_nibsOut) self.track2sector = bytearray(81 * 2) self.init_track2sector() self.read_dir() self.spi_read_irq = bytearray([1, 0xF1, 0, 0, 0, 0, 0]) self.spi_read_btn = bytearray([1, 0xFB, 0, 0, 0, 0, 0]) self.spi_read_trackno = bytearray([1, 0xD0, 0, 0, 0, 0, 0]) self.spi_result = bytearray(7) self.spi_enable_osd = bytearray([0, 0xFE, 0, 0, 0, 1]) self.spi_write_osd = bytearray([0, 0xFD, 0, 0, 0]) self.spi_write_track = [ bytearray([0, 0xD1, 0, 0, 0]), bytearray([0, 0xD1, 0, 0x60, 0]) ] self.spi_channel = const(2) self.spi_freq = const(3000000) self.init_pinout_sd() self.init_spi() alloc_emergency_exception_buf(100) self.enable = bytearray(1) self.timer = Timer(3) self.irq_handler(0) self.irq_handler_ref = self.irq_handler # allocation happens here self.spi_request = Pin(0, Pin.IN, Pin.PULL_UP) self.spi_request.irq(trigger=Pin.IRQ_FALLING, handler=self.irq_handler_ref) def init_spi(self): self.spi = 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)) self.cs = Pin(self.gpio_cs, Pin.OUT) self.cs.off() self.led = Pin(self.gpio_led, Pin.OUT) self.led.off() # init file browser def init_fb(self): self.fb_topitem = 0 self.fb_cursor = 0 self.fb_selected = -1 @micropython.viper def init_track2sector(self): p16t2s = ptr16(addressof(self.track2sector)) offset = 0 for i in range(0, 81): p16t2s[i] = offset offset += 12 - i // 16 @micropython.viper def init_pinout_sd(self): self.gpio_cs = const(17) self.gpio_sck = const(16) #self.gpio_mosi = const(4) #self.gpio_miso = const(12) self.gpio_mosi = const(25) self.gpio_miso = const(26) self.gpio_led = const(5) @micropython.viper def update_track(self): p8result = ptr8(addressof(self.spi_result)) p16t2s = ptr16(addressof(self.track2sector)) p8it = ptr8(addressof(self.imgtype)) # ask FPGA for current track number self.ctrl(4) # stop cpu self.cs.on() self.spi.write_readinto(self.spi_read_trackno, self.spi_result) self.cs.off() drive = 0 if p8result[6] & 0x80: drive = 1 track = p8result[6] & 0x7F if track > 79: track = 79 trackdiv16 = track // 16 #print("Fetching drive " + str(drive) + " track " + str(track)) sectors = 12 - trackdiv16 self.diskfile[drive].seek((2 - p8it[drive]) * p16t2s[track] * 1024) # upload data self.cs.on() self.spi.write(self.spi_write_track[drive]) if p8it[drive]: # .dsk self.diskfile[drive].readinto(self.conv_dataIn[trackdiv16]) offset = 0 for side in range(2): for sector in range(sectors): dsk2mac.convert_sector(self.conv_dataIn12K, offset, self.conv_nibsOut, track, side, sector) offset += 512 self.spi.write(self.conv_nibsOut) else: # .mac for side in range(2): self.diskfile[drive].readinto(self.conv_dataIn[trackdiv16]) self.spi.write(self.conv_dataIn[trackdiv16]) self.cs.off() self.ctrl(0) # resume cpu @micropython.viper def irq_handler(self, pin): p8result = ptr8(addressof(self.spi_result)) p8drive = ptr8(addressof(self.drive)) self.cs.on() self.spi.write_readinto(self.spi_read_irq, self.spi_result) self.cs.off() btn_irq = p8result[6] if btn_irq & 1: # drive 1 request self.update_track() if btn_irq & 0x80: # btn event IRQ flag self.cs.on() self.spi.write_readinto(self.spi_read_btn, self.spi_result) self.cs.off() btn = p8result[6] p8enable = ptr8(addressof(self.enable)) if p8enable[0] & 2: # wait to release all BTNs if btn == 1: p8enable[ 0] &= 1 # clear bit that waits for all BTNs released else: # all BTNs released if (btn & 0x78 ) == 0x78: # all cursor BTNs pressed at the same time self.show_dir() # refresh directory p8enable[0] = (p8enable[0] ^ 1) | 2 self.osd_enable(p8enable[0] & 1) if p8enable[0] == 1: if btn == 9: # btn3 cursor up self.start_autorepeat(-1) if btn == 17: # btn4 cursor down self.start_autorepeat(1) if btn == 1: self.timer.deinit() # stop autorepeat if btn == 33: # btn5 cursor left self.updir() if btn == 65: # btn6 cursor right 1st drive p8drive[0] = 0 self.select_entry() if btn == 3: # btn1 2nd drive p8drive[0] = 1 self.select_entry() def start_autorepeat(self, i: int): self.autorepeat_direction = i self.move_dir_cursor(i) self.timer_slow = 1 self.timer.init(mode=Timer.PERIODIC, period=500, callback=self.autorepeat) def autorepeat(self, timer): if self.timer_slow: self.timer_slow = 0 self.timer.init(mode=Timer.PERIODIC, period=30, callback=self.autorepeat) self.move_dir_cursor(self.autorepeat_direction) self.irq_handler(0) # catch stale IRQ def select_entry(self): if self.direntries[self.fb_cursor][1]: # is it directory oldselected = self.fb_selected - self.fb_topitem self.fb_selected = self.fb_cursor try: self.cwd = self.fullpath(self.direntries[self.fb_cursor][0]) except: self.fb_selected = -1 self.show_dir_line(oldselected) self.show_dir_line(self.fb_cursor - self.fb_topitem) self.init_fb() self.read_dir() self.show_dir() else: self.change_file() def updir(self): if len(self.cwd) < 2: self.cwd = "/" else: s = self.cwd.split("/")[:-1] self.cwd = "" for name in s: if len(name) > 0: self.cwd += "/" + name self.init_fb() self.read_dir() self.show_dir() def fullpath(self, fname): if self.cwd.endswith("/"): return self.cwd + fname else: return self.cwd + "/" + fname def change_file(self): oldselected = self.fb_selected - self.fb_topitem self.fb_selected = self.fb_cursor try: filename = self.fullpath(self.direntries[self.fb_cursor][0]) except: filename = False self.fb_selected = -1 self.show_dir_line(oldselected) self.show_dir_line(self.fb_cursor - self.fb_topitem) if filename: if filename.endswith(".mac") or filename.endswith( ".MAC") or filename.endswith(".dsk") or filename.endswith( ".DSK"): self.diskfile[self.drive[0]] = open(filename, "rb") self.imgtype[self.drive[0]] = 0 if filename.endswith(".dsk") or filename.endswith(".DSK"): self.imgtype[self.drive[0]] = 1 #self.update_track() self.ctrl(16 << self.drive[0]) # set insert_disk self.enable[0] = 0 self.osd_enable(0) if filename.endswith(".bit"): self.spi_request.irq(handler=None) self.timer.deinit() self.enable[0] = 0 self.osd_enable(0) self.spi.deinit() import ecp5 ecp5.prog_stream(open(filename, "rb"), blocksize=1024) if filename.endswith("_sd.bit"): os.umount("/sd") for i in bytearray([2, 4, 12, 13, 14, 15]): p = Pin(i, Pin.IN) a = p.value() del p, a result = ecp5.prog_close() del tap gc.collect() #os.mount(SDCard(slot=3),"/sd") # BUG, won't work self.init_spi() # because of ecp5.prog() spi.deinit() self.spi_request.irq(trigger=Pin.IRQ_FALLING, handler=self.irq_handler_ref) self.irq_handler(0) # handle stuck IRQ if filename.endswith(".nes") \ or filename.endswith(".snes") \ or filename.endswith(".smc") \ or filename.endswith(".sfc"): import ld_nes s = ld_nes.ld_nes(self.spi, self.cs) s.ctrl(1) s.ctrl(0) s.load_stream(open(filename, "rb")) del s gc.collect() self.enable[0] = 0 self.osd_enable(0) if filename.startswith("/sd/ti99_4a/") and filename.endswith( ".bin"): import ld_ti99_4a s = ld_ti99_4a.ld_ti99_4a(self.spi, self.cs) s.load_rom_auto(open(filename, "rb"), filename) del s gc.collect() self.enable[0] = 0 self.osd_enable(0) if (filename.startswith("/sd/msx") and filename.endswith(".rom")) \ or filename.endswith(".mx1"): import ld_msx s = ld_msx.ld_msx(self.spi, self.cs) s.load_msx_rom(open(filename, "rb")) del s gc.collect() self.enable[0] = 0 self.osd_enable(0) if filename.endswith(".z80"): self.enable[0] = 0 self.osd_enable(0) import ld_zxspectrum s = ld_zxspectrum.ld_zxspectrum(self.spi, self.cs) s.loadz80(filename) del s gc.collect() if filename.endswith(".ora") or filename.endswith(".orao"): self.enable[0] = 0 self.osd_enable(0) import ld_orao s = ld_orao.ld_orao(self.spi, self.cs) s.loadorao(filename) del s gc.collect() if filename.endswith(".vsf"): self.enable[0] = 0 self.osd_enable(0) import ld_vic20 s = ld_vic20.ld_vic20(self.spi, self.cs) s.loadvsf(filename) del s gc.collect() if filename.endswith(".prg"): self.enable[0] = 0 self.osd_enable(0) import ld_vic20 s = ld_vic20.ld_vic20(self.spi, self.cs) s.loadprg(filename) del s gc.collect() if filename.endswith(".cas"): self.enable[0] = 0 self.osd_enable(0) import ld_trs80 s = ld_trs80.ld_trs80(self.spi, self.cs) s.loadcas(filename) del s gc.collect() if filename.endswith(".cmd"): self.enable[0] = 0 self.osd_enable(0) import ld_trs80 s = ld_trs80.ld_trs80(self.spi, self.cs) s.loadcmd(filename) del s gc.collect() @micropython.viper def osd_enable(self, en: int): pena = ptr8(addressof(self.spi_enable_osd)) pena[5] = en & 1 self.cs.on() self.spi.write(self.spi_enable_osd) self.cs.off() @micropython.viper def osd_print(self, x: int, y: int, i: int, text): p8msg = ptr8(addressof(self.spi_write_osd)) a = 0xF000 + (x & 63) + ((y & 31) << 6) p8msg[2] = i p8msg[3] = a >> 8 p8msg[4] = a self.cs.on() self.spi.write(self.spi_write_osd) self.spi.write(text) self.cs.off() @micropython.viper def osd_cls(self): p8msg = ptr8(addressof(self.spi_write_osd)) p8msg[3] = 0xF0 p8msg[4] = 0 self.cs.on() self.spi.write(self.spi_write_osd) self.spi.read(1280, 32) self.cs.off() # y is actual line on the screen def show_dir_line(self, y): if y < 0 or y >= self.screen_y: return mark = 0 invert = 0 if y == self.fb_cursor - self.fb_topitem: mark = 1 invert = 1 if y == self.fb_selected - self.fb_topitem: mark = 2 i = y + self.fb_topitem if i >= len(self.direntries): self.osd_print(0, y, 0, "%64s" % "") return if self.direntries[i][1]: # directory self.osd_print( 0, y, invert, "%c%-57s D" % (self.mark[mark], self.direntries[i][0])) else: # file mantissa = self.direntries[i][2] exponent = 0 while mantissa >= 1024: mantissa >>= 10 exponent += 1 self.osd_print( 0, y, invert, "%c%-57s %4d%c" % (self.mark[mark], self.direntries[i][0], mantissa, self.exp_names[exponent])) def show_dir(self): for i in range(self.screen_y): self.show_dir_line(i) def move_dir_cursor(self, step): oldcursor = self.fb_cursor if step == 1: if self.fb_cursor < len(self.direntries) - 1: self.fb_cursor += 1 if step == -1: if self.fb_cursor > 0: self.fb_cursor -= 1 if oldcursor != self.fb_cursor: screen_line = self.fb_cursor - self.fb_topitem if screen_line >= 0 and screen_line < self.screen_y: # move cursor inside screen, no scroll self.show_dir_line(oldcursor - self.fb_topitem) # no highlight self.show_dir_line(screen_line) # highlight else: # scroll if screen_line < 0: # cursor going up screen_line = 0 if self.fb_topitem > 0: self.fb_topitem -= 1 self.show_dir() else: # cursor going down screen_line = self.screen_y - 1 if self.fb_topitem + self.screen_y < len(self.direntries): self.fb_topitem += 1 self.show_dir() def read_dir(self): self.direntries = [] ls = sorted(os.listdir(self.cwd)) for fname in ls: stat = os.stat(self.fullpath(fname)) if stat[0] & 0o170000 == 0o040000: self.direntries.append([fname, 1, 0]) # directory for fname in ls: stat = os.stat(self.fullpath(fname)) if stat[0] & 0o170000 != 0o040000: self.direntries.append([fname, 0, stat[6]]) # file # NOTE: this can be used for debugging #def osd(self, a): # if len(a) > 0: # enable = 1 # else: # enable = 0 # self.cs.on() # self.spi.write(bytearray([0,0xFE,0,0,0,enable])) # enable OSD # self.cs.off() # if enable: # self.cs.on() # self.spi.write(bytearray([0,0xFD,0,0,0])) # write content # self.spi.write(bytearray(a)) # write content # self.cs.off() def ctrl(self, i): self.cs.on() self.spi.write(bytearray([0, 0xFF, 0xFF, 0xFF, 0xFF, i])) self.cs.off() def peek(self, addr, length): self.ctrl(4) self.ctrl(6) self.cs.on() self.spi.write( bytearray([ 1, (addr >> 24) & 0xFF, (addr >> 16) & 0xFF, (addr >> 8) & 0xFF, addr & 0xFF, 0 ])) b = bytearray(length) self.spi.readinto(b) self.cs.off() self.ctrl(4) self.ctrl(0) return b def poke(self, addr, data): self.ctrl(4) self.ctrl(6) self.cs.on() self.spi.write( bytearray([ 0, (addr >> 24) & 0xFF, (addr >> 16) & 0xFF, (addr >> 8) & 0xFF, addr & 0xFF ])) self.spi.write(data) self.cs.off() self.ctrl(4) self.ctrl(0)
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) print(buffer_w == buffer_r) spi.init(polarity=1, phase=1, pins=None) buffer_r = bytearray(10) spi.write_readinto(buffer_w, buffer_r) print(buffer_w == buffer_r) spi.init(polarity=0, phase=1, pins=None) buffer_r = bytearray(10)
class disk2: def __init__(self, file_nib): self.diskfilename = file_nib print("DISK ][ %s" % self.diskfilename) self.screen_x = const(64) self.screen_y = const(20) self.cwd = "/" self.init_fb() self.exp_names = " KMGTE" self.highlight = bytearray([32, 16, 42]) # space, right triangle, asterisk self.read_dir() self.trackbuf = bytearray(6656) self.spi_read_track_irq = bytearray([1, 0, 0, 0, 0]) self.spi_result = bytearray(5) self.spi_write_track = bytearray([0, 0, 0]) self.spi_enable_osd = bytearray([0, 0xFE, 0, 1]) self.spi_write_osd = bytearray([0, 0xF0, 0]) self.spi_read_btn = bytearray([1, 0xFE, 0, 0, 0]) self.led = Pin(5, Pin.OUT) self.led.off() self.diskfile = open(self.diskfilename, "rb") self.spi_channel = const(2) self.init_pinout_sd() self.spi_freq = const(2000000) 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)) self.count = 0 self.count_prev = 0 self.track_change = Pin(0, Pin.IN, Pin.PULL_UP) alloc_emergency_exception_buf(100) self.irq_handler(0) self.irq_handler_ref = self.irq_handler # allocation happens here self.track_change.irq(trigger=Pin.IRQ_FALLING, handler=self.irq_handler_ref) # init file browser def init_fb(self): self.fb_topitem = 0 self.fb_cursor = 0 self.fb_selected = -1 @micropython.viper def init_pinout_sd(self): self.gpio_sck = const(16) self.gpio_mosi = const(4) self.gpio_miso = const(12) @micropython.viper def irq_handler(self, pin): p8result = ptr8(addressof(self.spi_result)) self.led.on() self.hwspi.write_readinto(self.spi_read_track_irq, self.spi_result) self.led.off() track_irq = p8result[4] if track_irq & 0x40: # track change event if self.diskfile: track = track_irq & 0x3F self.diskfile.seek(6656 * track) self.diskfile.readinto(self.trackbuf) self.led.on() self.hwspi.write(self.spi_write_track) self.hwspi.write(self.trackbuf) self.led.off() if track_irq & 0x80: # btn event self.led.on() self.hwspi.write_readinto(self.spi_read_btn, self.spi_result) self.led.off() btn = p8result[4] self.osd_enable((btn & 2) >> 1) # hold btn1 to enable OSD if btn & 4: # btn2 refresh directory self.show_dir() if btn & 8: # btn3 cursor up self.move_dir_cursor(-1) if btn & 16: # btn4 cursor down self.move_dir_cursor(1) if btn & 32: # btn6 cursor left self.updir() if btn & 64: # btn6 cursor right self.select_entry() def select_entry(self): if self.direntries[self.fb_cursor][1]: # is it directory self.cwd = self.fullpath(self.direntries[self.fb_cursor][0]) self.init_fb() self.read_dir() self.show_dir() else: self.change_file() def updir(self): if len(self.cwd) < 1: self.cwd = "/" else: s = self.cwd.split("/")[:-1] self.cwd = "" for name in s: if len(name) > 0: self.cwd += "/" + name self.init_fb() self.read_dir() self.show_dir() def fullpath(self, fname): if self.cwd.endswith("/"): return self.cwd + fname else: return self.cwd + "/" + fname def change_file(self): oldselected = self.fb_selected - self.fb_topitem self.fb_selected = self.fb_cursor try: self.diskfile = open( self.fullpath(self.direntries[self.fb_cursor][0]), "rb") except: self.diskfile = False self.fb_selected = -1 self.show_dir_line(oldselected) self.show_dir_line(self.fb_cursor - self.fb_topitem) @micropython.viper def osd_enable(self, en: int): pena = ptr8(addressof(self.spi_enable_osd)) pena[3] = en & 1 self.led.on() self.hwspi.write(self.spi_enable_osd) self.led.off() @micropython.viper def osd_print(self, x: int, y: int, text): p8msg = ptr8(addressof(self.spi_write_osd)) a = 0xF000 + (x & 63) + ((y & 31) << 6) p8msg[1] = a >> 8 p8msg[2] = a self.led.on() self.hwspi.write(self.spi_write_osd) self.hwspi.write(text) self.led.off() @micropython.viper def osd_cls(self): p8msg = ptr8(addressof(self.spi_write_osd)) p8msg[1] = 0xF0 p8msg[2] = 0 self.led.on() self.hwspi.write(self.spi_write_osd) self.hwspi.read(1280, 32) self.led.off() # y is actual line on the screen def show_dir_line(self, y): if y < 0 or y >= self.screen_y: return highlight = 0 if y == self.fb_cursor - self.fb_topitem: highlight = 1 if y == self.fb_selected - self.fb_topitem: highlight = 2 i = y + self.fb_topitem if i >= len(self.direntries): self.osd_print(0, y, "%64s" % "") return if self.direntries[i][1]: # directory self.osd_print( 0, y, "%c%-57s D" % (self.highlight[highlight], self.direntries[i][0])) else: # file mantissa = self.direntries[i][2] exponent = 0 while mantissa >= 1024: mantissa >>= 10 exponent += 1 self.osd_print( 0, y, "%c%-57s %4d%c" % (self.highlight[highlight], self.direntries[i][0], mantissa, self.exp_names[exponent])) def show_dir(self): for i in range(self.screen_y): self.show_dir_line(i) def move_dir_cursor(self, step): oldcursor = self.fb_cursor if step == 1: if self.fb_cursor < len(self.direntries) - 1: self.fb_cursor += 1 if step == -1: if self.fb_cursor > 0: self.fb_cursor -= 1 if oldcursor != self.fb_cursor: screen_line = self.fb_cursor - self.fb_topitem if screen_line >= 0 and screen_line < self.screen_y: # move cursor inside screen, no scroll self.show_dir_line(oldcursor - self.fb_topitem) # no highlight self.show_dir_line(screen_line) # highlight else: # scroll if screen_line < 0: # cursor going up screen_line = 0 if self.fb_topitem > 0: self.fb_topitem -= 1 self.show_dir() else: # cursor going down screen_line = self.screen_y - 1 if self.fb_topitem + self.screen_y < len(self.direntries): self.fb_topitem += 1 self.show_dir() def read_dir(self): self.direntries = [] ls = sorted(os.listdir(self.cwd)) for fname in ls: stat = os.stat(self.fullpath(fname)) if stat[0] & 0o170000 == 0o040000: self.direntries.append([fname, 1, 0]) # directory else: self.direntries.append([fname, 0, stat[6]]) # file def osd(self, a): if len(a) > 0: enable = 1 else: enable = 0 self.led.on() self.hwspi.write(bytearray([0, 0xFE, 0, enable])) # enable OSD self.led.off() if enable: self.led.on() self.hwspi.write(bytearray([0, 0xF0, 0])) # write content self.hwspi.write(bytearray(a)) # write content self.led.off()
class osd: def __init__(self): alloc_emergency_exception_buf(100) self.screen_x = const(64) self.screen_y = const(20) self.cwd = "/sd/slides" # shown on startup self.init_fb() self.exp_names = " KMGTE" self.mark = bytearray([32, 16, 42]) # space, right triangle, asterisk self.read_dir() self.spi_read_irq = bytearray([1, 0xF1, 0, 0, 0, 0, 0]) self.spi_read_btn = bytearray([1, 0xFB, 0, 0, 0, 0, 0]) self.spi_result = bytearray(7) self.spi_enable_osd = bytearray([0, 0xFE, 0, 0, 0, 1]) self.spi_write_osd = bytearray([0, 0xFD, 0, 0, 0]) self.spi_channel = const(2) self.spi_freq = const(3000000) self.init_pinout_sd() #self.spi=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)) self.init_spi() self.enable = bytearray(1) self.timer = Timer(3) self.init_slides() self.files2slides() self.start_bgreader() self.irq_handler(0) self.irq_handler_ref = self.irq_handler # allocation happens here self.spi_request = Pin(0, Pin.IN, Pin.PULL_UP) self.spi_request.irq(trigger=Pin.IRQ_FALLING, handler=self.irq_handler_ref) def init_spi(self): self.spi = 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)) self.cs = Pin(self.gpio_cs, Pin.OUT) self.cs.off() # init file browser def init_fb(self): self.fb_topitem = 0 self.fb_cursor = 0 self.fb_selected = -1 @micropython.viper def init_pinout_sd(self): self.gpio_cs = const(5) self.gpio_sck = const(16) self.gpio_mosi = const(4) self.gpio_miso = const(12) @micropython.viper def irq_handler(self, pin): p8result = ptr8(addressof(self.spi_result)) self.cs.on() self.spi.write_readinto(self.spi_read_irq, self.spi_result) self.cs.off() btn_irq = p8result[6] if btn_irq & 0x80: # btn event IRQ flag self.cs.on() self.spi.write_readinto(self.spi_read_btn, self.spi_result) self.cs.off() btn = p8result[6] p8enable = ptr8(addressof(self.enable)) if p8enable[0] & 2: # wait to release all BTNs if btn == 1: p8enable[ 0] &= 1 # clear bit that waits for all BTNs released else: # all BTNs released if (btn & 0x78 ) == 0x78: # all cursor BTNs pressed at the same time self.show_dir() # refresh directory p8enable[0] = (p8enable[0] ^ 1) | 2 self.osd_enable(p8enable[0] & 1) if p8enable[0] == 1: if btn == 9: # btn3 cursor up self.start_autorepeat(-1) if btn == 17: # btn4 cursor down self.start_autorepeat(1) if btn == 1: self.timer.deinit() # stop autorepeat if btn == 33: # btn5 cursor left self.updir() if btn == 65: # btn6 cursor right self.select_entry() else: if btn == 33: # btn5 cursor left self.change_slide(-1) if btn == 65: # btn6 cursor right self.change_slide(1) self.show_slide() @micropython.viper def show_slide(self): addr = int(self.xres) * int( self.yres) * (int(self.vi) % int(self.ncache)) self.cs.on() self.rgtr(0x19, self.i24(addr)) self.cs.off() def start_autorepeat(self, i: int): self.autorepeat_direction = i self.move_dir_cursor(i) self.timer_slow = 1 self.timer.init(mode=Timer.PERIODIC, period=500, callback=self.autorepeat) def autorepeat(self, timer): if self.timer_slow: self.timer_slow = 0 self.timer.init(mode=Timer.PERIODIC, period=30, callback=self.autorepeat) self.move_dir_cursor(self.autorepeat_direction) self.irq_handler(0) # catch stale IRQ def select_entry(self): if self.direntries[self.fb_cursor][1]: # is it directory oldselected = self.fb_selected - self.fb_topitem self.fb_selected = self.fb_cursor try: self.cwd = self.fullpath(self.direntries[self.fb_cursor][0]) except: self.fb_selected = -1 self.show_dir_line(oldselected) self.show_dir_line(self.fb_cursor - self.fb_topitem) self.init_fb() self.read_dir() self.show_dir() else: self.change_file() def updir(self): if len(self.cwd) < 2: self.cwd = "/" else: s = self.cwd.split("/")[:-1] self.cwd = "" for name in s: if len(name) > 0: self.cwd += "/" + name self.init_fb() self.read_dir() self.show_dir() def fullpath(self, fname): if self.cwd.endswith("/"): return self.cwd + fname else: return self.cwd + "/" + fname def change_file(self): oldselected = self.fb_selected - self.fb_topitem self.fb_selected = self.fb_cursor try: filename = self.fullpath(self.direntries[self.fb_cursor][0]) except: filename = False self.fb_selected = -1 self.show_dir_line(oldselected) self.show_dir_line(self.fb_cursor - self.fb_topitem) if filename: if filename.endswith(".bit"): self.spi_request.irq(handler=None) self.timer.deinit() self.enable[0] = 0 self.osd_enable(0) self.spi.deinit() tap = ecp5.ecp5() tap.prog_stream(open(filename, "rb"), blocksize=1024) if filename.endswith("_sd.bit"): os.umount("/sd") for i in bytearray([2, 4, 12, 13, 14, 15]): p = Pin(i, Pin.IN) a = p.value() del p, a result = tap.prog_close() del tap gc.collect() #os.mount(SDCard(slot=3),"/sd") # BUG, won't work self.init_spi() # because of ecp5.prog() spi.deinit() self.spi_request.irq(trigger=Pin.IRQ_FALLING, handler=self.irq_handler_ref) self.irq_handler(0) # handle stuck IRQ if filename.endswith(".ppm"): self.files2slides() self.start_bgreader() self.enable[0] = 0 self.osd_enable(0) @micropython.viper def osd_enable(self, en: int): pena = ptr8(addressof(self.spi_enable_osd)) pena[5] = en & 1 self.cs.on() self.spi.write(self.spi_enable_osd) self.cs.off() @micropython.viper def osd_print(self, x: int, y: int, i: int, text): p8msg = ptr8(addressof(self.spi_write_osd)) a = 0xF000 + (x & 63) + ((y & 31) << 6) p8msg[2] = i p8msg[3] = a >> 8 p8msg[4] = a self.cs.on() self.spi.write(self.spi_write_osd) self.spi.write(text) self.cs.off() @micropython.viper def osd_cls(self): p8msg = ptr8(addressof(self.spi_write_osd)) p8msg[3] = 0xF0 p8msg[4] = 0 self.cs.on() self.spi.write(self.spi_write_osd) self.spi.read(1280, 32) self.cs.off() # y is actual line on the screen def show_dir_line(self, y): if y < 0 or y >= self.screen_y: return mark = 0 invert = 0 if y == self.fb_cursor - self.fb_topitem: mark = 1 invert = 1 if y == self.fb_selected - self.fb_topitem: mark = 2 i = y + self.fb_topitem if i >= len(self.direntries): self.osd_print(0, y, 0, "%64s" % "") return if self.direntries[i][1]: # directory self.osd_print( 0, y, invert, "%c%-57s D" % (self.mark[mark], self.direntries[i][0])) else: # file mantissa = self.direntries[i][2] exponent = 0 while mantissa >= 1024: mantissa >>= 10 exponent += 1 self.osd_print( 0, y, invert, "%c%-57s %4d%c" % (self.mark[mark], self.direntries[i][0], mantissa, self.exp_names[exponent])) def show_dir(self): for i in range(self.screen_y): self.show_dir_line(i) def move_dir_cursor(self, step): oldcursor = self.fb_cursor if step == 1: if self.fb_cursor < len(self.direntries) - 1: self.fb_cursor += 1 if step == -1: if self.fb_cursor > 0: self.fb_cursor -= 1 if oldcursor != self.fb_cursor: screen_line = self.fb_cursor - self.fb_topitem if screen_line >= 0 and screen_line < self.screen_y: # move cursor inside screen, no scroll self.show_dir_line(oldcursor - self.fb_topitem) # no highlight self.show_dir_line(screen_line) # highlight else: # scroll if screen_line < 0: # cursor going up screen_line = 0 if self.fb_topitem > 0: self.fb_topitem -= 1 self.show_dir() else: # cursor going down screen_line = self.screen_y - 1 if self.fb_topitem + self.screen_y < len(self.direntries): self.fb_topitem += 1 self.show_dir() def read_dir(self): self.direntries = [] ls = sorted(os.listdir(self.cwd)) for fname in ls: stat = os.stat(self.fullpath(fname)) if stat[0] & 0o170000 == 0o040000: self.direntries.append([fname, 1, 0]) # directory self.file0 = len(self.direntries) for fname in ls: stat = os.stat(self.fullpath(fname)) if stat[0] & 0o170000 != 0o040000: self.direntries.append([fname, 0, stat[6]]) # file self.nfiles = len(self.direntries) - self.file0 gc.collect() # *** SLIDESHOW *** # self.direntries, self.file0 def init_slides(self): self.xres = 800 self.yres = 600 self.bpp = 16 membytes = 32 * 1024 * 1024 self.slide_pixels = self.xres * self.yres self.ncache = membytes // (self.slide_pixels * self.bpp // 8) #self.ncache=7 # NOTE debug self.priority_forward = 2 self.priority_backward = 1 self.nbackward = self.ncache * self.priority_backward // ( self.priority_forward + self.priority_backward) self.nforward = self.ncache - self.nbackward #print(self.nforward, self.nbackward, self.nbackward+self.nforward) self.rdi = 0 # currently reading image self.prev_rdi = -1 self.vi = 0 # currently viewed image self.cache_li = [] # image to be loaded self.cache_ti = [] # top image self.cache_ty = [] # top good lines 0..(y-1) self.cache_tyend = [] # top y load max self.cache_bi = [] # bot image self.cache_by = [] # bot good lines y..yres for i in range(self.ncache): self.cache_li.append(i) self.cache_ti.append(-1) self.cache_ty.append(0) self.cache_tyend.append(self.yres) self.cache_bi.append(-1) self.cache_by.append(0) self.bg_file = None self.PPM_line_buf = bytearray(3 * self.xres) self.rb = bytearray(256) # reverse bits self.init_reverse_bits() self.finished = 1 def files2slides(self): self.finished = 1 self.bg_file = None self.rdi = 0 self.prev_rdi = -1 self.vi = 0 self.show_slide() self.nslides = 0 self.slide_fi = [] # file index in direntries self.slide_xres = [] self.slide_yres = [] self.slide_pos = [] for i in range(self.nfiles): filename = self.fullpath(self.direntries[self.file0 + i][0]) f = open(filename, "rb") line = f.readline(1000) if line[0:2] == b"P6": # PPM header line = b"#" while line[0] == 35: # skip commented lines line = f.readline(1000) xystr = line.split(b" ") xres = int(xystr[0]) yres = int(xystr[1]) line = f.readline(1000) if int(line) == 255: # 255 levels supported only self.slide_fi.append(self.file0 + i) self.slide_xres.append(xres) self.slide_yres.append(yres) self.slide_pos.append(f.tell()) self.nslides += 1 #if self.nslides>=256: # break # discard cache for i in range(self.ncache): ci = i % self.nslides if i < self.nslides: self.cache_li[ci] = i else: self.cache_li[ci] = -1 self.cache_ti[ci] = -1 self.cache_ty[ci] = 0 self.cache_tyend[ci] = self.yres self.cache_bi[ci] = -1 self.cache_by[ci] = 0 # choose next, ordered by priority def next_to_read(self): before_first = self.nbackward - self.vi if before_first < 0: before_first = 0 after_last = self.vi + self.nforward - self.nslides if after_last < 0: after_last = 0 #print("before_first=%d after_last=%d" % (before_first,after_last)) next_forward_slide = -1 i = self.vi n = i + self.nforward + before_first - after_last if n < 0: n = 0 if n > self.nslides: n = self.nslides while i < n: ic = i % self.ncache if self.cache_ti[ic]!=self.cache_li[ic] \ or self.cache_ty[ic]<self.yres: next_forward_slide = i break i += 1 next_backward_slide = -1 i = self.vi - 1 n = i - self.nbackward - after_last + before_first if n < 0: n = 0 if n > self.nslides: n = self.nslides while i >= n: ic = i % self.ncache if self.cache_ti[ic]!=self.cache_li[ic] \ or self.cache_ty[ic]<self.yres: next_backward_slide = i break i -= 1 next_reading_slide = -1 if next_forward_slide >= 0 and next_backward_slide >= 0: if (next_forward_slide-self.vi)*self.priority_backward < \ (self.vi-next_backward_slide)*self.priority_forward: next_reading_slide = next_forward_slide else: next_reading_slide = next_backward_slide else: if next_forward_slide >= 0: next_reading_slide = next_forward_slide else: if next_backward_slide >= 0: next_reading_slide = next_backward_slide return next_reading_slide # image to be discarded at changed view def next_to_discard(self) -> int: return (self.vi + self.nforward - 1) % self.ncache # which image to replace after changing slide def replace(self, mv): dc_replace = -1 if mv > 0: dc_replace = self.vi + self.nforward - 1 if dc_replace >= self.nslides: dc_replace -= self.ncache if mv < 0: dc_replace = (self.vi - self.nbackward - 1) if dc_replace < 0: dc_replace += self.ncache return dc_replace # change currently viewed slide # discard images in cache def change_slide(self, mv): vi = self.vi + mv if vi < 0 or vi >= self.nslides or mv == 0: return self.cache_li[self.next_to_discard()] = self.replace(mv) self.vi = vi self.cache_li[self.next_to_discard()] = self.replace(mv) self.rdi = self.next_to_read() if self.rdi >= 0: self.start_bgreader() @micropython.viper def init_reverse_bits(self): p8rb = ptr8(addressof(self.rb)) for i in range(256): v = i r = 0 for j in range(8): r <<= 1 r |= v & 1 v >>= 1 p8rb[i] = r # convert PPM line RGB888 to RGB565, bits reversed @micropython.viper def ppm2pixel(self): p8 = ptr8(addressof(self.PPM_line_buf)) p8rb = ptr8(addressof(self.rb)) xi = 0 yi = 0 yn = 2 * int(self.xres) while yi < yn: r = p8[xi] g = p8[xi + 1] b = p8[xi + 2] p8[yi] = p8rb[(r & 0xF8) | ((g & 0xE0) >> 5)] p8[yi + 1] = p8rb[((g & 0x1C) << 3) | ((b & 0xF8) >> 3)] xi += 3 yi += 2 def read_scanline(self): bytpp = self.bpp // 8 # on screen rdi = self.rdi % self.ncache self.bg_file.readinto(self.PPM_line_buf) if self.slide_xres[self.rdi] != self.xres: self.bg_file.seek(self.slide_pos[self.rdi] + 3 * self.slide_xres[self.rdi] * (self.cache_ty[rdi] + 1)) self.ppm2pixel() # write PPM_line_buf to screen addr = self.xres * (rdi * self.yres + self.cache_ty[rdi]) # DMA transfer <= 2048 bytes each # DMA transfer must be divided in N buffer uploads # buffer upload <= 256 bytes each nbuf = 8 astep = 200 abuf = 0 self.cs.on() self.rgtr(0x16, self.i24(addr)) for j in range(nbuf): self.rgtr(0x18, self.PPM_line_buf[abuf:abuf + astep]) abuf += astep self.rgtr(0x17, self.i24(nbuf * astep // bytpp - 1)) self.cs.off() # file should be already closed when calling this def next_file(self): #print("next_file") filename = self.fullpath(self.direntries[self.slide_fi[self.rdi]][0]) self.bg_file = open(filename, "rb") rdi = self.rdi % self.ncache # Y->seek to first position to read from self.bg_file.seek(self.slide_pos[self.rdi] + 3 * self.slide_xres[self.rdi] * self.cache_ty[rdi]) print("%d RD %s" % (self.rdi, filename)) # background read, call it periodically def bgreader(self, timer): if self.rdi < 0: self.finished = 1 self.timer.deinit() return rdi = self.rdi % self.ncache if self.cache_ti[rdi] != self.cache_li[rdi]: # cache contains different image than the one to be loaded # y begin from top self.cache_ti[rdi] = self.cache_li[rdi] self.cache_ty[rdi] = 0 # y end depends on cache content # if bottom part is already in cache, reduce tyend if self.cache_bi[rdi] == self.cache_ti[rdi]: self.cache_tyend[rdi] = self.cache_by[rdi] else: self.cache_tyend[rdi] = self.yres # update self.rdi after cache_ti[rdi] has changed self.rdi = self.cache_ti[rdi] rdi = self.rdi % self.ncache # after self.rdi and cache_ty has been updated # call next file if self.prev_rdi != self.rdi or self.bg_file == None: # file changed, close and reopen if self.bg_file: # maybe not needed, python will auto-close? self.bg_file.close() self.bg_file = None self.next_file() self.prev_rdi = self.rdi if self.cache_ty[rdi] < self.cache_tyend[rdi]: # file read self.read_scanline() self.cache_ty[rdi] += 1 if self.cache_ty[rdi] > self.cache_by[rdi]: self.cache_by[rdi] = self.cache_ty[rdi] if self.cache_ty[rdi] >= self.cache_tyend[rdi]: # slide complete, close file, find next self.cache_ty[rdi] = self.yres self.cache_bi[rdi] = self.cache_li[rdi] self.cache_by[rdi] = 0 self.bg_file = None self.rdi = self.next_to_read() if self.rdi < 0: self.finished = 1 return self.timer.init(mode=Timer.ONE_SHOT, period=0, callback=self.bgreader) def start_bgreader(self): if self.finished: self.finished = 0 self.timer.init(mode=Timer.ONE_SHOT, period=1, callback=self.bgreader) # convert integer to 24-bit RGTR parameter def i24(self, i: int): return bytearray([ self.rb[(i >> 16) & 0xFF], self.rb[(i >> 8) & 0xFF], self.rb[i & 0xFF] ]) # x is bytearray, length 1-256 def rgtr(self, cmd, x): self.spi.write(bytearray([self.rb[cmd], self.rb[len(x) - 1]])) self.spi.write(x) def ctrl(self, i): self.cs.on() self.spi.write(bytearray([0, 0xFF, 0xFF, 0xFF, 0xFF, i])) self.cs.off() def peek(self, addr, length): self.ctrl(2) self.cs.on() self.spi.write( bytearray([ 1, (addr >> 24) & 0xFF, (addr >> 16) & 0xFF, (addr >> 8) & 0xFF, addr & 0xFF, 0 ])) b = bytearray(length) self.spi.readinto(b) self.cs.off() self.ctrl(0) return b def poke(self, addr, data): self.ctrl(2) self.cs.on() self.spi.write( bytearray([ 0, (addr >> 24) & 0xFF, (addr >> 16) & 0xFF, (addr >> 8) & 0xFF, addr & 0xFF ])) self.spi.write(data) self.cs.off() self.ctrl(0)
from fpioa_manager import fm, board_info import utime fm.register(25, fm.fpioa.SPI1_SS0) #cs fm.register(28, fm.fpioa.SPI1_D0) #mosi fm.register(26, fm.fpioa.SPI1_D1) #miso fm.register(27, fm.fpioa.SPI1_SCLK) #sclk spi01 = SPI(SPI.SPI1, mode=SPI.MODE_MASTER, baudrate=10000000, polarity=0, phase=0, bits=8, firstbit=SPI.MSB, mosi=fm.fpioa.SPI1_D0, miso=fm.fpioa.SPI1_D1, sck=fm.fpioa.SPI1_SCLK, cs0=fm.fpioa.SPI1_SS0) buff = bytearray([0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]) r_buff = bytearray(8) spi01.write(buff, cs=SPI.CS0) print(buff) utime.sleep_ms(100) spi01.write_readinto(buff, r_buff) print(buff, r_buff) utime.sleep_ms(100)
from machine import SPI, Pin spi = SPI( spihost=SPI.HSPI, baudrate=2600000 sck=Pin(18), mosi=Pin(23), miso=Pin(19), cs=Pin(4) ) spi.write(buf) #NOHEAP spi.read(nbytes, *, write=0x00) #write is the byte to ?output on MOSI for each byte read in spi.readinto(buf, *, write=0x00) #NOHEAP spi.write_readinto(write_buf, read_buf) #NOHEAP; write_buf and read_buf can be the same
# MISO D7 (13) DOUT # CS D5 (14) CS # polarity=1 -> idle state of SCK # phase=0 -> data op stijgende flank SCK # phase=1 -> data op dalende flank SCK SCK = Pin(15, Pin.OUT) MOSI = Pin(12, Pin.OUT) MISO = Pin(13, Pin.OUT) CS = Pin(14, Pin.OUT) spi = SPI(-1, baudrate=100000, polarity=1, phase=0, sck=SCK, mosi=MOSI, miso=MISO) # zet baudrate spi.init(baudrate=200000) # oneindige lus while True: # zet Chip Select voor communicatie CS.low() time.sleep_us(5) # zend commando en lees antwoord spi.write_readinto(DIN, DOUT) # reset Chip Select CS.high() time.sleep_us(5) # converteer 10 LSB uit antwoord en druk af in REPL waarde = ((((3 & DOUT[1]) << 8) + DOUT[2]) / 1023) * 3.3 print("Spanning over potentiometer: {:.1f} V".format(waarde)) # even wachten time.sleep_ms(500)
phase=1, bits=8, firstbit=SPI.MSB, sck=fm.fpioa.SPI1_SCLK, mosi=fm.fpioa.SPI1_D0, miso=fm.fpioa.SPI1_D1, cs0=fm.fpioa.SPI1_SS0) data_len = 10 wbuff = bytearray([0x75 | 0x80 for x in range(data_len)]) rbuff = bytearray([0x00 for x in range(data_len)]) spi.write(bytearray([0x6B, 0x80]), cs=SPI.CS0) print(wbuff, rbuff) spi.write_readinto(wbuff, rbuff, cs=SPI.CS0) utime.sleep_ms(100) print(wbuff, rbuff) for i in range(data_len): print(hex(rbuff[i])) utime.sleep_ms(300) MPU6886_ADDRESS = 0x68 MPU6886_WHOAMI = 0x75 MPU6886_ACCEL_INTEL_CTRL = 0x69 MPU6886_SMPLRT_DIV = 0x19 MPU6886_INT_PIN_CFG = 0x37 MPU6886_INT_ENABLE = 0x38 MPU6886_ACCEL_XOUT_H = 0x3B MPU6886_TEMP_OUT_H = 0x41
class ILI(object): _curwidth = 240 # Current TFT width _curheight = 320 # Current TFT height def __init__(self, cs='22', dc='21', rst=None, bl=None, port=VSPI, baud=DEFAULT_BAUDRATE, portrait=True): """ Initialize ILI class. """ if cs is None or dc is None: raise RuntimeError('cs and dc pins must not be None') if port not in [HSPI, VSPI]: raise RuntimeError('port must be HSPI or VSPI') self.csPin = Pin(cs, Pin.OUT) self.dcPin = Pin(dc, Pin.OUT) self.rstPin = None if rst is not None: self.rstPin = Pin(rst, Pin.OUT) self.blPin = None if bl is not None: self.blPin = Pin(bl, Pin.OUT) # Backlight On self.blPin.on() self.spi = SPI(port, baudrate=rate) self._portrait = portrait self.curHeight = TFTHEIGHT self.curWidth = TFTWIDTH self.reset() self.initDisplay() self._gcCollect() @micropython.viper def reset(self): """ Reset the Screen. """ if self.rstPin is not None: # Reset Pin is Connected to ESP32 self.rstPin.off() sleep(0.01) self.rstPin.on() return @micropython.viper def _gcCollect(self): collect() @micropython.viper def setDimensions(self): if ILI.portrait: self.curHeight = TFTHEIGHT self.curWidth = TFTWIDTH else: self.curHeight = TFTWIDTH self.curWidth = TFTHEIGHT self._graph_orientation() @micropython.viper def _initILI(self): self._write_cmd(LCDOFF) # Display OFF sleep(0.01) self._write_cmd(SWRESET) # Software Reset sleep(0.05) self._graph_orientation() self._write_cmd(PTLON) # Partial Mode ON self._write_cmd(PIXFMT) # Pixel Format Set #self._write_data(0x66) # 18-Bit/Pixel self._write_data(0x55) # 16-Bit/Pixel self._write_cmd(GAMMASET) self._write_data(0x01) self._write_cmd(ETMOD) # Entry Mode Set self._write_data(0x07) self._write_cmd(SLPOUT) # Exit Sleep Mode sleep(0.01) self._write_cmd(LCDON) # Display ON sleep(0.01) self._write_cmd(RAMWR) def _write(self, word, command=True, read=False): self.csPin.off() self.dcPin.value(0 if command else 1) if read: fmt = '>BI' data = bytearray(5) self.spi.write_readinto(pack(fmt, word), data) self.csPin.on() return data self.spi.write(word) slef.csPin.on() def _decode_spi_data(self, data): # For example: # 1. recieving sets 5 bytes # 2. first 2 of them are useless (or null bytes) # 3. and just 3 last of them having a useful data: # - those 3 bytes are RGB bytes (if we are reading from memory) # - those 3 bytes have a 7 useful bits (and doesn't matter which color is) # - we must get from them: # * just 5 largest bits for R and B colors # * just 6 largest bits for G color # next 2 lines sorting useful data # getting 4 last bytes data = unpack('<BI', data)[1] # reversing them data = pack('>I', data) # getting just 3 bytes from bytearray as BGR data = unpack('>3B', data) # with those 3 assignmentations we sorting useful bits for each color red = (((data[2] >> 2) & 31) << 11) green = (((data[1] >> 1) & 63) << 5) blue = ((data[0] >> 2) & 31) # setting them to 16 bit color data = red | green | blue data = pack('>H', data) return data def _write_cmd(self, word, read=False): data = self._write(word, read=read) return data def _write_data(self, word): self._write(word, command=False) def _write_words(self, words): wordL = len(words) wordL = wordL if wordL > 1 else "" fmt = '>{0}B'.format(wordL) words = pack(fmt, *words) self._write_data(words) @micropython.viper def _graph_orientation(self): self._write_cmd(MADCTL) # Memory Access Control # Portrait: # | MY=0 | MX=1 | MV=0 | ML=0 | BGR=1 | MH=0 | 0 | 0 | # OR Landscape: # | MY=0 | MX=0 | MV=1 | ML=0 | BGR=1 | MH=0 | 0 | 0 | data = 0x48 if self._portrait else 0x28 self._write_data(data) @micropython.viper def _char_orientation(self): self._write_cmd(MADCTL) # Memory Access Control # Portrait: # | MY=1 | MX=1 | MV=1 | ML=0 | BGR=1 | MH=0 | 0 | 0 | # OR Landscape: # | MY=0 | MX=1 | MV=1 | ML=0 | BGR=1 | MH=0 | 0 | 0 | data = 0xE8 if self._portrait else 0x58 self._write_data(data) @micropython.viper def _image_orientation(self): self._write_cmd(MADCTL) # Memory Access Control # Portrait: # | MY=0 | MX=1 | MV=0 | ML=0 | BGR=1 | MH=0 | 0 | 0 | # OR Landscape: # | MY=0 | MX=1 | MV=0 | ML=1 | BGR=1 | MH=0 | 0 | 0 | data = 0xC8 if self._portrait else 0x68 self._write_data(data) def _set_window(self, x0, y0, x1, y1): # Column Address Set self._write_cmd(CASET) self._write_words( ((x0 >> 8) & 0xFF, x0 & 0xFF, (y0 >> 8) & 0xFF, y0 & 0xFF)) # Page Address Set self._write_cmd(PASET) self._write_words( ((x1 >> 8) & 0xFF, x1 & 0xFF, (y1 >> 8) & 0xFF, y1 & 0xFF)) # Memory Write self._write_cmd(RAMWR) def _get_Npix_monoword(self, color): if color == WHITE: word = 0xFFFF elif color == BLACK: word = 0 else: R, G, B = color word = (R << 11) | (G << 5) | B word = pack('>H', word) return word def _return_chpos(self, chrwidth, scale): if chrwidth == 1: chpos = scale + 1 if scale > 2 else scale - 1 else: chpos = scale return chpos # Method written by MCHobby https://github.com/mchobby # Transform a RGB888 color to RGB565 color tuple. def rgbTo565(self, r, g, b): return (r >> 3, g >> 2, b >> 3) @property def portrait(self): return self._portrait @portrait.setter def portrait(self, portr): if not isinstance(portr, bool): from exceptions import PortraitBoolError raise PortraitBoolError self._portrait = portr self.setDimensions()
class machx02_spi(object): # bit12:busy, bit13:failed, bit9:enabled status = {"busy": 4096, "failed": 8192, "enabled": 512, "done": 256} mask = 0x00000000FFFFFFFF def __init__(self, baudrate, deviceid): self.cs = Pin(15, Pin.OUT) self.cs.on() self.hspi = SPI(1, baudrate=400000, sck=Pin(14), mosi=Pin(13), miso=Pin(12)) self.deviceid = deviceid # transfert spi def spiTrans(self, cmd, size): resp_machx02_spi = bytearray(size) self.hspi.init(baudrate=400000, sck=Pin(14), mosi=Pin(13), miso=Pin(12), firstbit=SPI.MSB) # set the baudrate* self.cs.off() self.hspi.write_readinto(cmd, resp_machx02_spi) self.cs.on() return resp_machx02_spi # check idcode def check_idcode(self): cmd = bytearray([ MACHXO2_CMD_READ_DEVICEID, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]) resp_machx02_spi = self.spiTrans(cmd, 8) resp_machx02_spi_int = self.from_bytes_big(bytes(resp_machx02_spi)) result = resp_machx02_spi_int & self.mask return (self.deviceid == result) # disable def disable(self): #disable configuration cmd = bytearray([MACHXO2_CMD_DISABLE, 0x00, 0x00]) resp_machx02_spi = self.spiTrans(cmd, 3) # nop cmd = bytearray([MACHXO2_CMD_NOP, 0xFF, 0xFF, 0xFF]) resp_machx02_spi = self.spiTrans(cmd, 4) # refresh cmd = bytearray([MACHXO2_CMD_REFRESH, 0x00, 0x00]) resp_machx02_spi = self.spiTrans(cmd, 3) utime.sleep_ms(10000) def wakeup_device(self): # refresh cmd = bytearray([MACHXO2_CMD_REFRESH, 0x00, 0x00]) resp_machx02_spi = self.spiTrans(cmd, 3) utime.sleep_ms(10000) # enable offline configuration def enable(self): cmd = bytearray([MACHXO2_CMD_ENABLE_OFFLINE, 0x08, 0x00, 0x00]) resp_machx02_spi = self.spiTrans(cmd, 4) self.waitidle() # erase SRAM cmd = bytearray([MACHXO2_CMD_ERASE, 0x01, 0x00, 0x00]) resp_machx02_spi = self.spiTrans(cmd, 4) self.waitidle() # bit 13 = fail # bit 9 = enabled resp = self.readstatus() print("status enable", resp) status_failed = self.from_bytes_big(resp) & self.status["failed"] status_enabled = self.from_bytes_big(resp) & self.status["enabled"] #print(status_failed,status_enabled) return (status_failed != self.status["failed"]) and ( status_enabled == self.status["enabled"]) # busy = bit12 def waitidle(self): while True: resp = self.readstatus() # print("status=",resp) status_busy = self.from_bytes_big(resp) & self.status["busy"] #print("busy ",status_busy,type(status_busy)) if status_busy == self.status["busy"]: #print("busy") utime.sleep_ms(1) # datasheet = 200 us else: break def waitdone(self): while True: resp = self.readstatus() status_done = self.from_bytes_big(resp) & self.status["done"] print("wait done status=", status_done) if status_done != self.status["done"]: print("wait done") utime.sleep_ms(1) # datasheet = 200 us else: break def waitrefresh(self): while True: resp = self.readstatus() print("wait refresh=", resp) status_done = self.from_bytes_big(resp) & self.status["done"] print("status_done=", status_done) status_busy = self.from_bytes_big(resp) & self.status["busy"] print("status_busy=", status_busy) if status_busy == self.status["busy"]: cmd = bytearray([MACHXO2_CMD_REFRESH, 0x00, 0x00]) resp_machx02_spi = self.spiTrans(cmd, 3) print("wait refresh") utime.sleep_ms(1) # datasheet = 200 us else: break # read status def readstatus(self): cmd = bytearray([ MACHXO2_CMD_READ_STATUS, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]) resp_machx02_spi = self.spiTrans(cmd, 8) return resp_machx02_spi # read features def readfeatures(self): cmd = bytearray([ MACHXO2_CMD_READ_FEATURE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]) feature_row = self.spiTrans(cmd, 12) # read FEABITS cmd = bytearray( [MACHXO2_CMD_READ_FEABITS, 0x00, 0x00, 0x00, 0x00, 0x00]) feabits = self.spiTrans(cmd, 6) return self.from_bytes_big(feature_row), self.from_bytes_big(feabits) # programm jedec file def program(self, fusetable): # erase flash cmd = bytearray([MACHXO2_CMD_ERASE, 0x04, 0x00, 0x00]) resp_machx02_spi = self.spiTrans(cmd, 4) print("erase flash ...") self.waitidle() resp = self.readstatus() status_failed = self.from_bytes_big(resp) & self.status["failed"] if status_failed == self.status["failed"]: return False # set address to zero cmd = bytearray([MACHXO2_CMD_INIT_ADDRESS, 0x00, 0x00, 0x00]) resp_machx02_spi = self.spiTrans(cmd, 4) #print("set adress to 0") self.waitidle() # program pages sizefile = len(fusetable) nb_bit = sizefile * 128 # not implemented, because ufm not programmed # if nb_bit != int(MACHXO2_SIZE_FUSETABLE_7000): # print("error size fusetable",nb_bit) # sys.exit() # else: # print("size fusetable",nb_bit) #lines = [i.strip() for i in fusetable] numbit = 0 crc = 0 nbdata = 16 countline = 0 req = bytearray(20) for line in fusetable: #before = gc.mem_free() line_strip = line.strip() size_line = len(line_strip) # print("count",countline,size_line) countline = countline + 1 for countbit_128 in range(size_line): valbit = line_strip[countbit_128] if valbit == "1": val = 1 else: val = 0 crc += val << (numbit % 8) numbit = numbit + 1 line_bytes = [line_strip[i:i + 8] for i in range(0, size_line, 8)] # transmit data with format : cmd,0,0,1 <16* 8 bytes data> req[0] = MACHXO2_CMD_PROG_INCR_NV req[1] = 0x00 req[2] = 0x00 req[3] = 0x01 # transmit Y*16 bytes for i in range(nbdata): req[4 + i] = self.str_to_byte(line_bytes[i]) resp_machx02_spi = self.spiTrans(req, nbdata + 4) #after = gc.mem_free() #print("mem =",before - after ,"bytes") self.waitidle() print("numbit programmed = ", numbit) # program DONE bit cmd = bytearray([MACHXO2_CMD_PROGRAM_DONE, 0x00, 0x00, 0x00]) resp_machx02_spi = self.spiTrans(cmd, 4) self.waitdone() status = self.readstatus() status_failed = self.from_bytes_big(status) & self.status["failed"] error = (status_failed != self.status["failed"]) return error, crc # read flash def checkflash(self): countpage = 0x0100 # 14 bits counter page cmd = bytearray([MACHXO2_CMD_READ_INCR_NV, 0x01, 0x01, 0x00]) self.cs.off() self.hspi.write(cmd) self.cs.on() utime.sleep_us(500) self.cs.off() resp_machx02_spi = self.hspi.read(int(countpage)) self.cs.on() return resp_machx02_spi # read ufm def readufm(self): cmd = bytearray([MACHXO2_CMD_READ_UFM, 0x01, 0x00, 0x00]) resp_machx02_spi = self.spiTrans(cmd, 4) return resp_machx02_spi def from_bytes_big(self, b): n = 0 for x in b: n <<= 8 n |= x return n def str_to_byte(self, s): b = 0 for i in range(len(s)): b |= int(s[i]) << 7 - i return b
class MeshNet(RadioDriver): def __init__(self, domain, address, **kwargs): super(MeshNet, self).__init__(domain, **kwargs) self.address = address self._meshlock = rlock() self._transmit_queue = queue() self._receive_queue = queue() self._hwmp_sequence_number = 0 self._hwmp_sequence_lock = lock() self._route_lock = rlock() self._promiscuous = False self._debug = False self._announce_thread = None # Defines routes to nodes self._routes = {} self._packet_errors_crc = 0 self._packet_received = 0 self._packet_transmitted = 0 self._packet_ignored = 0 self._packet_lock = rlock() self._gateway = kwargs['gateway'] if 'gateway' in kwargs else False if self._gateway: self._announce_interval = float( kwargs['interval'] ) if 'interval' in kwargs else _ANOUNCE_DEFAULT_INTERVAL else: self._announce_interval = 0 self._PROTOCOLS = { RouteAnnounce.PROTOCOL_ID: RouteAnnounce, RouteRequest.PROTOCOL_ID: RouteRequest, RouteError.PROTOCOL_ID: RouteError, None: DataPacket, # Data packet protocol id is a wildcard } # In promiscuous mode, all received packets are dropped into the receive queue, as well # as being processed. def set_promiscuous(self, mode=True): self._promiscuous = mode def set_debug(self, mode=True): self._debug = mode def start(self): self._spi = SPI(baudrate=10000000, polarity=0, phase=0, bits=8, firstbit=SPI.MSB, sck=Pin(_SX127x_SCK, Pin.OUT, Pin.PULL_DOWN), mosi=Pin(_SX127x_MOSI, Pin.OUT, Pin.PULL_UP), miso=Pin(_SX127x_MISO, Pin.IN, Pin.PULL_UP)) self._ss = Pin(_SX127x_SS, Pin.OUT) self._reset = Pin(_SX127x_RESET, Pin.OUT) self._dio_table = [ Pin(_SX127x_DIO0, Pin.IN), Pin(_SX127x_DIO1, Pin.IN), Pin(_SX127x_DIO2, Pin.IN) ] self._ping_count = 0 self._power = None # not True nor False # Perform base class init # super(MeshNet, self).start(_SX127x_WANTED_VERSION) super().start(_SX127x_WANTED_VERSION, activate=False) # Set power state self.set_power() # A timer than can be started to do retries; not started until needed self._retry_routerequests_thread = thread( run=self._retry_routerequests, stack=8192) self._retry_routerequests_thread.start(timeout=0.5) # Start announce if requested if self._gateway: self.announce_start(self._announce_interval / 1000.0) def announce_start(self, interval): print("Announce gateway every %.1f seconds" % interval) self._announce_thread = thread(run=self._announce, stack=8192) self._announce_thread.start(interval=interval) def _announce(self, t, interval): countdown = interval # Only sleep a second at a time so we can be killed fairly quickly while t.running: sleep(1) countdown -= 1 if countdown <= 0: countdown += interval packet = RouteAnnounce(target=BROADCAST_ADDRESS, nexthop=BROADCAST_ADDRESS, sequence=self._create_sequence_number(), gateway_flag=self._gateway) self.send_packet(packet) return 0 # Return the protocol wrapper or Data is not otherwise defined def get_protocol_wrapper(self, protocol): return self._PROTOCOLS[ protocol] if protocol in self._PROTOCOLS else self._PROTOCOLS[None] # Remove a root def remove_route(self, address): with self._route_lock: if address in self._routes: del (self_routes[address]) # Update a route. If route is not defined, create it. If defined but metric is better or sequence is different, update it. # Return True if new or updated route def update_route(self, target, nexthop, sequence, metric=_MAX_METRIC, gateway_flag=False, force=False): with self._route_lock: if force or target not in self._routes or self._routes[ target].is_expired(): # Create new route route = Route(target=target, nexthop=nexthop, sequence=sequence, metric=metric, gateway_flag=gateway_flag) self._routes[target] = route if self._debug: print("Created %s" % str(route)) # Else if we are forcing creation, or the sequence number is different or the metric is better, create a new route elif sequence != self._routes[target].sequence( ) or metric < self._routes[target].metric(): # Update route route = self._routes[target] route.nexthop(nexthop) route.metric(metric) route.sequence(sequence) route.update_lifetime() if self._debug: print("Updated %s" % str(route)) else: # No route to host route = None return route # Default to search routes table def find_route(self, address): with self._route_lock: return self._routes[ address] if address in self._routes and not self._routes[ address].is_expired() else None # Reset device def reset(self): self._reset.value(0) sleep(0.1) self._reset.value(1) # Read register from SPI port def read_register(self, address): value = int.from_bytes(self._spi_transfer(address & 0x7F), 'big') # print("%02x from %02x" % (value, address)) return value # Write register to SPI port def write_register(self, address, value): # print("write %02x to %02x" % (value, address)) self._spi_transfer(address | 0x80, value) def _spi_transfer(self, address, value=0): response = bytearray(1) self._ss.value(0) self._spi.write(bytes([address])) self._spi.write_readinto(bytes([value]), response) self._ss.value(1) return response # Read block of data from SPI port def read_buffer(self, address, length): try: response = bytearray(length) self._ss.value(0) self._spi.write(bytes([address & 0x7F])) self._spi.readinto(response) self._ss.value(1) except: # No room. gc now gc.collect() response = None return response # Write block of data to SPI port def write_buffer(self, address, buffer, size): self._ss.value(0) self._spi.write(bytes([address | 0x80])) self._spi.write(memoryview(buffer)[0:size]) self._ss.value(1) def attach_interrupt(self, dio, edge, callback): # if self._debug: # print("attach_interrupt dio %d rising %s with callback %s" % (dio, edge, callback)) if dio < 0 or dio >= len(self._dio_table): raise Exception("DIO %d out of range (0..%d)" % (dio, len(self._dio_table) - 1)) edge = Pin.IRQ_RISING if edge else Pin.IRQ_FALLING self._dio_table[dio].irq(handler=callback, trigger=edge if callback else 0) # Enwrap the packet with a class object for the particular message type def wrap_packet(self, data, rssi=None): return self.get_protocol_wrapper(data[_HEADER_PROTOCOL[0]])(load=data, rssi=rssi) # Duplicate packet with new private data def dup_packet(self, packet): return self.wrap_packet(bytearray(packet.data()), rssi=packet.rssi()) def onReceive(self, data, crc_ok, rssi): if crc_ok: packet = self.wrap_packet(data, rssi) nexthop = packet.nexthop() if self._debug: print("Received: %s" % (str(packet))) # In promiscuous, deliver to receiver so it can handle it (but not process it) if self._promiscuous: packet_copy = self.dup_packet(packet) packet_copy.promiscuous(True) self.put_receive_packet(packet_copy) if nexthop == BROADCAST_ADDRESS or nexthop == self.address: self._packet_received += 1 # To us or broadcasted packet.process(self) else: # It is non processed self._packet_ignored += 1 else: self._packet_errors_crc += 1 def receive_packet(self): gc.collect() return self._receive_queue.get() def put_receive_packet(self, packet): self._receive_queue.put(packet) gc.collect() # Finished transmitting - see if we can transmit another # If we have another packet, return it to caller. def onTransmit(self): # if self._debug: # print("onTransmit complete") self._packet_transmitted += 1 # Delete top packet in queue packet = self._transmit_queue.get(wait=0) del packet # Return head of queue if one exists packet = self._transmit_queue.head() gc.collect() return packet.data() if packet else None def _create_sequence_number(self): with self._hwmp_sequence_lock: self._hwmp_sequence_number += 1 return self._hwmp_sequence_number # A packet with a source and destination is ready to transmit. # Label the from address and if no to address, attempt to route # If ttl is true, decrease ttl and discard packet if 0 def send_packet(self, packet, ttl=False): if ttl and packet.ttl(packet.ttl() - 1) == 0: # Packet has expired if self._debug: print("Expired: %s" % str(packet)) else: # Label packets as coming from us packet.previous(self.address) # print("%s: set previous to %d" % (str(packet), self.address)) # Label as originating here if no previous assigned source address if packet.source() == NULL_ADDRESS: packet.source(self.address) # print("%s: set source to %d" % (str(packet), self.address)) # If the nexthop is NULL, then we compute next hop based on route table. # If no route table, create pending NULL route and cache packet for later retransmission. if packet.nexthop() == NULL_ADDRESS: with self._route_lock: # Look up the route to the destination route = self.find_route(packet.target()) # If no route, create a dummy route and queue the results if route == None: # Unknown route. Create a NULL route awaiting RouteAnnounce route = self.update_route( target=packet.target(), nexthop=NULL_ADDRESS, sequence=self._create_sequence_number(), force=True) # Save packet in route for later delivery route.put_pending_packet(packet) if self._debug: print("Routing %s" % str(packet)) request = RouteRequest(target=packet.target(), previous=self.address, source=self.address, sequence=route.sequence(), metric=1, gateway_flag=self._gateway) # This will queue repeats of this request until cancelled route.set_pending_routerequest(request) elif route.nexthop() == NULL_ADDRESS: # We still have a pending route, so append packet to queue only. request = None route.put_pending_packet(packet) else: # Label the destination for the packet packet.nexthop(route.nexthop()) request = packet # Transmit the request if we created one or else the actual packet packet = request if packet: # # TODO: we may need another method to restart transmit queue other than looking # and transmit queue length. A 'transmitting' flag (protected by meshlock) that # is True if transmitting of packet is in progress. Cleared on onTransmit when # queue has become empty. # # This may need to be implemented to allow stalling transmission for windows of # reception. A timer will then restart the queue if items remain within it. # # if self._debug: # print("sending: %s" % str(packet)) with self._meshlock: # print("Appending to queue: %s" % packet.decode()) self._transmit_queue.put(packet) if len(self._transmit_queue) == 1: self.transmit_packet(packet.data()) if self._debug: print("Transmitted: %s" % str(packet)) # A thread to check all routes and those with resend the packets for those with retry requests def _retry_routerequests(self, thread, timeout): while thread.running: sleep(timeout) with self._route_lock: # Go through all routes looking for those with pending requests. for target in list(self._routes): route = self._routes[target] # If route is expired, remove it if route.is_expired(): # Clean up route del (self._routes[target]) # Otherwise if it has a pending request, resend it else: packet = route.get_pending_routerequest() if packet: if self._debug: print("Retry route request %s" % str(packet)) self.send_packet(packet) def stop(self): # Stop announce if running if self._announce_thread: self._announce_thread.stop() self._announce_thread.wait() self._announce_thread = None if self._retry_routerequest_thread != None: self._retry_routerequest_thread.stop() self._retry_routerequest_thread.wait() self._retry_routerequest_thread = None super(MeshNet, self).stop() # Shut down power self.set_power(False) print("MeshNet handler close called") # Close DIO interrupts for dio in self._dio_table: dio.irq(handler=None, trigger=0) # Close SPI channel if opened if self._spi: self._spi.deinit() self._spi = None def set_power(self, power=True): # print("set_power %s" % power) if power != self._power: self._power = power # Call base class super(MeshNet, self).set_power(power) def __del__(self): self.stop() def dump(self): item = 0 for reg in range(0x43): print("%02x: %02x" % (reg, self.read_register(reg)), end=" " if item != 7 else "\n") item = (item + 1) % 8 print("")
class osdzx: def __init__(self): self.screen_x = const(64) self.screen_y = const(20) self.cwd = "/" self.init_fb() self.exp_names = " KMGTE" self.mark = bytearray([32, 16, 42]) # space, right triangle, asterisk self.read_dir() self.spi_read_irq = bytearray([1, 0xF1, 0, 0, 0, 0, 0]) self.spi_read_btn = bytearray([1, 0xFB, 0, 0, 0, 0, 0]) self.spi_result = bytearray(7) self.spi_enable_osd = bytearray([0, 0xFE, 0, 0, 0, 1]) self.spi_write_osd = bytearray([0, 0xFD, 0, 0, 0]) self.spi_channel = const(2) self.spi_freq = const(4000000) self.init_pinout_sd() #self.spi=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)) self.init_spi() alloc_emergency_exception_buf(100) self.enable = bytearray(1) self.timer = Timer(3) self.irq_handler(0) self.irq_handler_ref = self.irq_handler # allocation happens here self.spi_request = Pin(0, Pin.IN, Pin.PULL_UP) self.spi_request.irq(trigger=Pin.IRQ_FALLING, handler=self.irq_handler_ref) def init_spi(self): self.spi = 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)) self.cs = Pin(self.gpio_cs, Pin.OUT) self.cs.off() # init file browser def init_fb(self): self.fb_topitem = 0 self.fb_cursor = 0 self.fb_selected = -1 @micropython.viper def init_pinout_sd(self): self.gpio_cs = const(5) self.gpio_sck = const(16) self.gpio_mosi = const(4) self.gpio_miso = const(12) @micropython.viper def irq_handler(self, pin): p8result = ptr8(addressof(self.spi_result)) self.cs.on() self.spi.write_readinto(self.spi_read_irq, self.spi_result) self.cs.off() btn_irq = p8result[6] if btn_irq & 0x80: # btn event IRQ flag self.cs.on() self.spi.write_readinto(self.spi_read_btn, self.spi_result) self.cs.off() btn = p8result[6] p8enable = ptr8(addressof(self.enable)) if p8enable[0] & 2: # wait to release all BTNs if btn == 1: p8enable[ 0] &= 1 # clear bit that waits for all BTNs released else: # all BTNs released if (btn & 0x78 ) == 0x78: # all cursor BTNs pressed at the same time self.show_dir() # refresh directory p8enable[0] = (p8enable[0] ^ 1) | 2 self.osd_enable(p8enable[0] & 1) if p8enable[0] == 1: if btn == 9: # btn3 cursor up self.start_autorepeat(-1) if btn == 17: # btn4 cursor down self.start_autorepeat(1) if btn == 1: self.timer.deinit() # stop autorepeat if btn == 33: # btn6 cursor left self.updir() if btn == 65: # btn6 cursor right self.select_entry() def start_autorepeat(self, i: int): self.autorepeat_direction = i self.move_dir_cursor(i) self.timer_slow = 1 self.timer.init(mode=Timer.PERIODIC, period=500, callback=self.autorepeat) def autorepeat(self, timer): if self.timer_slow: self.timer_slow = 0 self.timer.init(mode=Timer.PERIODIC, period=30, callback=self.autorepeat) self.move_dir_cursor(self.autorepeat_direction) self.irq_handler(0) # catch stale IRQ def select_entry(self): if self.direntries[self.fb_cursor][1]: # is it directory self.cwd = self.fullpath(self.direntries[self.fb_cursor][0]) self.init_fb() self.read_dir() self.show_dir() else: self.change_file() def updir(self): if len(self.cwd) < 2: self.cwd = "/" else: s = self.cwd.split("/")[:-1] self.cwd = "" for name in s: if len(name) > 0: self.cwd += "/" + name self.init_fb() self.read_dir() self.show_dir() def fullpath(self, fname): if self.cwd.endswith("/"): return self.cwd + fname else: return self.cwd + "/" + fname def change_file(self): oldselected = self.fb_selected - self.fb_topitem self.fb_selected = self.fb_cursor try: filename = self.fullpath(self.direntries[self.fb_cursor][0]) except: filename = False self.fb_selected = -1 self.show_dir_line(oldselected) self.show_dir_line(self.fb_cursor - self.fb_topitem) if filename: if filename.endswith(".bit"): self.spi_request.irq(handler=None) self.timer.deinit() self.enable[0] = 0 self.osd_enable(0) self.spi.deinit() tap = ecp5.ecp5() tap.prog_stream(open(filename, "rb"), blocksize=1024) if filename.endswith("_sd.bit"): os.umount("/sd") for i in bytearray([2, 4, 12, 13, 14, 15]): p = Pin(i, Pin.IN) a = p.value() del p, a result = tap.prog_close() del tap gc.collect() #os.mount(SDCard(slot=3),"/sd") # BUG, won't work self.init_spi() # because of ecp5.prog() spi.deinit() self.spi_request.irq(trigger=Pin.IRQ_FALLING, handler=self.irq_handler_ref) self.irq_handler(0) # handle stuck IRQ if filename.endswith(".z80"): self.enable[0] = 0 self.osd_enable(0) import ld_zxspectrum s = ld_zxspectrum.ld_zxspectrum(self.spi, self.cs) s.loadz80(filename) del s gc.collect() if filename.endswith(".nes"): import ld_zxspectrum s = ld_zxspectrum.ld_zxspectrum(self.spi, self.cs) s.ctrl(1) s.ctrl(0) s.load_stream(open(filename, "rb"), addr=0, maxlen=0x101000) del s gc.collect() self.enable[0] = 0 self.osd_enable(0) @micropython.viper def osd_enable(self, en: int): pena = ptr8(addressof(self.spi_enable_osd)) pena[5] = en & 1 self.cs.on() self.spi.write(self.spi_enable_osd) self.cs.off() @micropython.viper def osd_print(self, x: int, y: int, i: int, text): p8msg = ptr8(addressof(self.spi_write_osd)) a = 0xF000 + (x & 63) + ((y & 31) << 6) p8msg[2] = i p8msg[3] = a >> 8 p8msg[4] = a self.cs.on() self.spi.write(self.spi_write_osd) self.spi.write(text) self.cs.off() @micropython.viper def osd_cls(self): p8msg = ptr8(addressof(self.spi_write_osd)) p8msg[3] = 0xF0 p8msg[4] = 0 self.cs.on() self.spi.write(self.spi_write_osd) self.spi.read(1280, 32) self.cs.off() # y is actual line on the screen def show_dir_line(self, y): if y < 0 or y >= self.screen_y: return mark = 0 invert = 0 if y == self.fb_cursor - self.fb_topitem: mark = 1 invert = 1 if y == self.fb_selected - self.fb_topitem: mark = 2 i = y + self.fb_topitem if i >= len(self.direntries): self.osd_print(0, y, 0, "%64s" % "") return if self.direntries[i][1]: # directory self.osd_print( 0, y, invert, "%c%-57s D" % (self.mark[mark], self.direntries[i][0])) else: # file mantissa = self.direntries[i][2] exponent = 0 while mantissa >= 1024: mantissa >>= 10 exponent += 1 self.osd_print( 0, y, invert, "%c%-57s %4d%c" % (self.mark[mark], self.direntries[i][0], mantissa, self.exp_names[exponent])) def show_dir(self): for i in range(self.screen_y): self.show_dir_line(i) def move_dir_cursor(self, step): oldcursor = self.fb_cursor if step == 1: if self.fb_cursor < len(self.direntries) - 1: self.fb_cursor += 1 if step == -1: if self.fb_cursor > 0: self.fb_cursor -= 1 if oldcursor != self.fb_cursor: screen_line = self.fb_cursor - self.fb_topitem if screen_line >= 0 and screen_line < self.screen_y: # move cursor inside screen, no scroll self.show_dir_line(oldcursor - self.fb_topitem) # no highlight self.show_dir_line(screen_line) # highlight else: # scroll if screen_line < 0: # cursor going up screen_line = 0 if self.fb_topitem > 0: self.fb_topitem -= 1 self.show_dir() else: # cursor going down screen_line = self.screen_y - 1 if self.fb_topitem + self.screen_y < len(self.direntries): self.fb_topitem += 1 self.show_dir() def read_dir(self): self.direntries = [] ls = sorted(os.listdir(self.cwd)) for fname in ls: stat = os.stat(self.fullpath(fname)) if stat[0] & 0o170000 == 0o040000: self.direntries.append([fname, 1, 0]) # directory else: self.direntries.append([fname, 0, stat[6]]) # file
from machine import SPI spi1 = SPI(SPI.SPI1, mode=SPI.MODE_MASTER, baudrate=10000000, polarity=0, phase=0, bits=8, firstbit=SPI.MSB, sck=28, mosi=29, miso=30, cs0=27) w = b'1234' r = bytearray(4) spi1.write(w) spi1.write(w, cs=SPI.CS0) spi1.write_readinto(w, r) spi1.read(5, write=0x00) spi1.readinto(r, write=0x00)
# file: spi_loopback.py # Date: 2020-04-22 from machine import Pin, SPI import utime SCK = const(14) MOSI = const(13) MISO = const(12) hspi = SPI(1, baudrate=10000000, polarity=0, phase=0, bits=8, firstbit=SPI.MSB, sck=Pin(SCK), mosi=Pin(MOSI), miso=Pin(MISO)) for i in range(256): rbuf = bytearray(5 * [0]) # read buffer filled with zero wbuf = bytearray([0xaa, 0xbb, 0xcc, 0xdd, i]) hspi.write_readinto(wbuf, rbuf) for b in rbuf: print(hex(b), end=' ') print('') hspi.deinit() # turn off the SPI bus
class SPI_IDE: def __init__(self): self.seln = Pin(17,Pin.OUT) self.seln(1) self.spi = SPI(2,sck=Pin(16),mosi=Pin(4),miso=Pin(12),baudrate=3000000) self.buf = bytearray(8) self.get = bytearray([1,0,0,0,0,0,0,0]) self.data = bytearray(512) self.dsk = open("/sd/cortex/unix.dsk","ab+") self.wr = bytearray([4]) self.rd = bytearray([3]) self.mode = 0 self.rec = 0 self.dbg = 0 self.bsy = Pin(5,Pin.IN) self.bsy.irq(handler=self.action, trigger=Pin.IRQ_RISING) self.seln(0) def action(self, pin): if self.mode==0x00: self.spi.write_readinto(self.get, self.buf) if self.buf[7]==0xef: if self.dbg: print("-- set feature") self.buf[0] = 2 self.buf[1] = 0 self.buf[7] = 0 self.spi.write(self.buf) elif self.buf[7]==0x20: self.sec = self.buf[3] + 256*self.buf[4] if self.dbg: print("-- read sector 0x%04x" % self.sec) self.dsk.seek(512*self.sec) self.dsk.readinto(self.data) self.spi.write(self.wr) self.spi.write(self.data) self.mode = 0x20 self.buf[0] = 2 self.buf[1] = 0 self.buf[7] = 0x08 self.spi.write(self.buf) elif self.buf[7]==0x30: self.sec = self.buf[3] + 256*self.buf[4] if self.dbg: print("-- write sector 0x%04x" % self.sec) self.mode = 0x30 self.buf[0] = 2 self.buf[1] = 0 self.buf[7] = 0x08 self.spi.write(self.buf) else: print("Unsupported IDE command") print("cmd=0x%02x" % self.buf[7]) elif self.mode==0x20: self.mode = 0x00 self.buf[0] = 2 self.buf[1] = 0 self.buf[7] = 0 self.spi.write(self.buf) elif self.mode==0x30: self.spi.write(self.rd) self.spi.readinto(self.data) self.dsk.seek(512*self.sec) self.dsk.write(self.data) self.mode = 0x00 self.buf[0] = 2 self.buf[1] = 0 self.buf[7] = 0 self.spi.write(self.buf)
fm.register(25,fm.fpioa.GPIOHS10, force=True)#cs from Maix import GPIO cs = GPIO(GPIO.GPIOHS10, GPIO.OUT) fm.register(28,fm.fpioa.SPI1_D0, force=True)#mosi fm.register(26,fm.fpioa.SPI1_D1, force=True)#miso fm.register(27,fm.fpioa.SPI1_SCLK, force=True)#sclk spi1 = SPI(SPI.SPI1, mode=SPI.MODE_MASTER, baudrate=10000000, polarity=0, phase=0, bits=8, firstbit=SPI.MSB) while True: w = b'\xFF' r = bytearray(1) cs.value(0) print(spi1.write_readinto(w, r)) cs.value(1) print(w, r) time.sleep(0.1) ''' from machine import SPI spi1 = SPI(SPI.SPI1, mode=SPI.MODE_MASTER, baudrate=10000000, polarity=0, phase=0, bits=8, firstbit=SPI.MSB, sck=28, mosi=29, miso=30) w = b'1234' r = bytearray(4) spi1.write(w) spi1.write_readinto(w, r) spi1.read(5, write=0x00) spi1.readinto(r, write=0x00) '''
#importing machine and spi from machine import Pin, SPI #SPI functions# #Constructing the SPI bus using the following pins # polarity is the idle state of SCK # phase=0 means sample on the first edge of SCK, phase=1 means the second spi = SPI(baudrate=100000, polarity=1, phase=0, sck=Pin(0), mosi=Pin(2), miso=Pin(4)) spi.init(baudrate=200000) #Setting the baudrate spi.read(10) #Reading 10 bytes from the baudrate spi.read(10, 0xff) #read 10 bytes while outputting 0xff on MOSI buf = bytearray(50) # create a buffer spi.readinto(buf) # read into the given buffer (reads 50 bytes in this case) spi.readinto(buf, 0xff) # read into the given buffer and output 0xff on MOSI spi.write(b'12345') # write 5 bytes on MOSI buf = bytearray(4) # create a buffer spi.write_readinto(b'1234', buf) # write to MOSI and read from MISO into the buffer spi.write_readinto(buf, buf) # write buf to MOSI and read MISO back into buf while True: #Input your code that runs continually
from machine import SPI,SoftSPI,Pin # configure the SPI master @ 2MHz # this uses the SPI non-default pins for CLK, MOSI and MISO (``P19``, ``P20`` and ``P21``) spi = SPI(2, baudrate=80000000, polarity=0, phase=0, bits=8, firstbit=SPI.MSB, sck=Pin(18), mosi=Pin(23), miso=Pin(19)) spi.write(bytes([0x01, 0x02, 0x03, 0x04, 0x05])) # send 5 bytes on the bus spi.read(5) # receive 5 bytes on the bus rbuf = bytearray(5) spi.write_readinto(bytes([0x01, 0x02, 0x03, 0x04, 0x05]), rbuf) # send a receive 5 bytes int_val = int.from_bytes(spi.read(5), "big") print(int_val) print(spi.read(5))
class TOUCH: # # Init just sets the PIN's to In / out as required # async: set True if asynchronous operation intended # confidence: confidence level - number of consecutive touches with a margin smaller than the given level # which the function will sample until it accepts it as a valid touch # margin: Distance from mean centre at which touches are considered at the same position # delay: Delay between samples in ms. (n/a if asynchronous) # DEFAULT_CAL = (-3917, -0.127, -3923, -0.1267, -3799, -0.07572, -3738, -0.07814) def __init__(self, controller="XPT2046", asyn=False, *, confidence=5, margin=50, delay=10, calibration=None, spi = None): if spi is None: self.spi = SPI(-1, baudrate=1000000, sck=Pin("X12"), mosi=Pin("X11"), miso=Pin("Y2")) else: self.spi = spi self.recv = bytearray(3) self.xmit = bytearray(3) # set default values self.ready = False self.touched = False self.x = 0 self.y = 0 self.buf_length = 0 cal = TOUCH.DEFAULT_CAL if calibration is None else calibration self.asynchronous = False self.touch_parameter(confidence, margin, delay, cal) if asyn: self.asynchronous = True import uasyncio as asyncio loop = asyncio.get_event_loop() loop.create_task(self._main_thread()) # set parameters for get_touch() # res: Resolution in bits of the returned values, default = 10 # confidence: confidence level - number of consecutive touches with a margin smaller than the given level # which the function will sample until it accepts it as a valid touch # margin: Difference from mean centre at which touches are considered at the same position # delay: Delay between samples in ms. # def touch_parameter(self, confidence=5, margin=50, delay=10, calibration=None): if not self.asynchronous: # Ignore attempts to change on the fly. confidence = max(min(confidence, 25), 5) if confidence != self.buf_length: self.buff = [[0,0] for x in range(confidence)] self.buf_length = confidence self.delay = max(min(delay, 100), 5) margin = max(min(margin, 100), 1) self.margin = margin * margin # store the square value if calibration: self.calibration = calibration # get_touch(): Synchronous use. get a touch value; Parameters: # # initital: Wait for a non-touch state before getting a sample. # True = Initial wait for a non-touch state # False = Do not wait for a release # wait: Wait for a touch or not? # False: Do not wait for a touch and return immediately # True: Wait until a touch is pressed. # raw: Setting whether raw touch coordinates (True) or normalized ones (False) are returned # setting the calibration vector to (0, 1, 0, 1, 0, 1, 0, 1) result in a identity mapping # timeout: Longest time (ms, or None = 1 hr) to wait for a touch or release # # Return (x,y) or None # def get_touch(self, initial=True, wait=True, raw=False, timeout=None): if self.asynchronous: return None # Should only be called in synhronous mode if timeout is None: timeout = 3600000 # set timeout to 1 hour # if initial: ## wait for a non-touch state sample = True while sample and timeout > 0: sample = self.raw_touch() pyb.delay(self.delay) timeout -= self.delay if timeout <= 0: # after timeout, return None return None # buff = self.buff buf_length = self.buf_length buffptr = 0 nsamples = 0 while timeout > 0: if nsamples == buf_length: meanx = sum([c[0] for c in buff]) // buf_length meany = sum([c[1] for c in buff]) // buf_length dev = sum([(c[0] - meanx)**2 + (c[1] - meany)**2 for c in buff]) / buf_length if dev <= self.margin: # got one; compare against the square value if raw: return (meanx, meany) else: return self.do_normalize((meanx, meany)) # get a new value sample = self.raw_touch() # get a touch if sample is None: if not wait: return None nsamples = 0 # Invalidate buff else: buff[buffptr] = sample # put in buff buffptr = (buffptr + 1) % buf_length nsamples = min(nsamples + 1, buf_length) pyb.delay(self.delay) timeout -= self.delay return None # Asynchronous use: this thread maintains self.x and self.y async def _main_thread(self): import uasyncio as asyncio buff = self.buff buf_length = self.buf_length buffptr = 0 nsamples = 0 await asyncio.sleep(0) while True: if nsamples == buf_length: meanx = sum([c[0] for c in buff]) // buf_length meany = sum([c[1] for c in buff]) // buf_length dev = sum([(c[0] - meanx)**2 + (c[1] - meany)**2 for c in buff]) / buf_length if dev <= self.margin: # got one; compare against the square value self.ready = True self.x, self.y = self.do_normalize((meanx, meany)) sample = self.raw_touch() # get a touch if sample is None: self.touched = False self.ready = False nsamples = 0 # Invalidate buff else: self.touched = True buff[buffptr] = sample # put in buff buffptr = (buffptr + 1) % buf_length nsamples = min(nsamples + 1, buf_length) await asyncio.sleep(0) # Asynchronous get_touch def get_touch_async(self): if self.ready: self.ready = False return self.x, self.y return None # # do_normalize(touch) # calculate the screen coordinates from the touch values, using the calibration values # touch must be the tuple return by get_touch # def do_normalize(self, touch): xmul = self.calibration[3] + (self.calibration[1] - self.calibration[3]) * (touch[1] / 4096) xadd = self.calibration[2] + (self.calibration[0] - self.calibration[2]) * (touch[1] / 4096) ymul = self.calibration[7] + (self.calibration[5] - self.calibration[7]) * (touch[0] / 4096) yadd = self.calibration[6] + (self.calibration[4] - self.calibration[6]) * (touch[0] / 4096) x = int((touch[0] + xadd) * xmul) y = int((touch[1] + yadd) * ymul) return (x, y) # # raw_touch(tuple) # raw read touch. Returns (x,y) or None # def raw_touch(self): global CONTROL_PORT x = self.touch_talk(T_GETX, 12) y = self.touch_talk(T_GETY, 12) if x > X_LOW and y < Y_HIGH: # touch pressed? return (x, y) else: return None # # Send a command to the touch controller and wait for the response # cmd: command byte # bits: expected data size. Reasonable values are 8 and 12 # def touch_talk(self, cmd, bits): self.xmit[0] = cmd self.spi.write_readinto(self.xmit, self.recv) return (self.recv[1] * 256 + self.recv[2]) >> (15 - bits)
class osd: def __init__(self): self.screen_x = const(64) self.screen_y = const(20) self.cwd = "/" self.init_fb() self.exp_names = " KMGTE" self.mark = bytearray([32,16,42]) # space, right triangle, asterisk self.diskfile=False self.data_buf=bytearray(554) self.mdv_byte=bytearray(1) self.mdv_phase=bytearray([1]) self.read_dir() self.spi_read_irq = bytearray([1,0xF1,0,0,0,0,0]) self.spi_read_btn = bytearray([1,0xFB,0,0,0,0,0]) self.spi_read_blktyp = bytearray([1,0xD0,0,0,0,0,0]) self.spi_result = bytearray(7) self.spi_enable_osd = bytearray([0,0xFE,0,0,0,1]) self.spi_write_osd = bytearray([0,0xFD,0,0,0]) self.spi_send_mdv_bram = bytearray([0,0xD1,0,0,0]) self.spi_channel = const(2) self.spi_freq = const(3000000) self.init_pinout_sd() self.init_spi() alloc_emergency_exception_buf(100) self.enable = bytearray(1) self.timer = Timer(3) self.irq_handler(0) self.irq_handler_ref = self.irq_handler # allocation happens here self.spi_request = Pin(0, Pin.IN, Pin.PULL_UP) self.spi_request.irq(trigger=Pin.IRQ_FALLING, handler=self.irq_handler_ref) def init_spi(self): self.spi=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)) self.cs=Pin(self.gpio_cs,Pin.OUT) self.cs.off() self.led=Pin(self.gpio_led,Pin.OUT) self.led.off() # init file browser def init_fb(self): self.fb_topitem = 0 self.fb_cursor = 0 self.fb_selected = -1 @micropython.viper def init_pinout_sd(self): self.gpio_cs = const(17) self.gpio_sck = const(16) self.gpio_mosi = const(4) self.gpio_miso = const(12) self.gpio_led = const(5) @micropython.viper def irq_handler(self, pin): p8result = ptr8(addressof(self.spi_result)) self.cs.on() self.spi.write_readinto(self.spi_read_irq, self.spi_result) self.cs.off() btn_irq = p8result[6] if btn_irq&1: # microdrive 1 request #self.cs.on() #self.spi.write_readinto(self.spi_read_blktyp,self.spi_result) #self.cs.off() #blktyp=p8result[6] if self.diskfile: self.mdv_read() if btn_irq&0x80: # btn event IRQ flag self.cs.on() self.spi.write_readinto(self.spi_read_btn, self.spi_result) self.cs.off() btn = p8result[6] p8enable = ptr8(addressof(self.enable)) if p8enable[0]&2: # wait to release all BTNs if btn==1: p8enable[0]&=1 # clear bit that waits for all BTNs released else: # all BTNs released if (btn&0x78)==0x78: # all cursor BTNs pressed at the same time self.show_dir() # refresh directory p8enable[0]=(p8enable[0]^1)|2; self.osd_enable(p8enable[0]&1) if p8enable[0]==1: if btn==9: # btn3 cursor up self.start_autorepeat(-1) if btn==17: # btn4 cursor down self.start_autorepeat(1) if btn==1: self.timer.deinit() # stop autorepeat if btn==33: # btn5 cursor left self.updir() if btn==65: # btn6 cursor right self.select_entry() def start_autorepeat(self, i:int): self.autorepeat_direction=i self.move_dir_cursor(i) self.timer_slow=1 self.timer.init(mode=Timer.PERIODIC, period=500, callback=self.autorepeat) def autorepeat(self, timer): if self.timer_slow: self.timer_slow=0 self.timer.init(mode=Timer.PERIODIC, period=30, callback=self.autorepeat) self.move_dir_cursor(self.autorepeat_direction) self.irq_handler(0) # catch stale IRQ def select_entry(self): if self.direntries[self.fb_cursor][1]: # is it directory oldselected = self.fb_selected - self.fb_topitem self.fb_selected = self.fb_cursor try: self.cwd = self.fullpath(self.direntries[self.fb_cursor][0]) except: self.fb_selected = -1 self.show_dir_line(oldselected) self.show_dir_line(self.fb_cursor - self.fb_topitem) self.init_fb() self.read_dir() self.show_dir() else: self.change_file() def updir(self): if len(self.cwd) < 2: self.cwd = "/" else: s = self.cwd.split("/")[:-1] self.cwd = "" for name in s: if len(name) > 0: self.cwd += "/"+name self.init_fb() self.read_dir() self.show_dir() def fullpath(self,fname): if self.cwd.endswith("/"): return self.cwd+fname else: return self.cwd+"/"+fname def change_file(self): oldselected = self.fb_selected - self.fb_topitem self.fb_selected = self.fb_cursor try: filename = self.fullpath(self.direntries[self.fb_cursor][0]) except: filename = False self.fb_selected = -1 self.show_dir_line(oldselected) self.show_dir_line(self.fb_cursor - self.fb_topitem) if filename: if filename.endswith(".mdv") or filename.endswith(".MDV"): self.diskfile = open(filename,"rb") self.mdv_refill_buf() self.enable[0]=0 self.osd_enable(0) if filename.endswith(".bit"): self.spi_request.irq(handler=None) self.timer.deinit() self.enable[0]=0 self.osd_enable(0) self.spi.deinit() tap=ecp5.ecp5() tap.prog_stream(open(filename,"rb"),blocksize=1024) if filename.endswith("_sd.bit"): os.umount("/sd") for i in bytearray([2,4,12,13,14,15]): p=Pin(i,Pin.IN) a=p.value() del p,a result=tap.prog_close() del tap gc.collect() #os.mount(SDCard(slot=3),"/sd") # BUG, won't work self.init_spi() # because of ecp5.prog() spi.deinit() self.spi_request.irq(trigger=Pin.IRQ_FALLING, handler=self.irq_handler_ref) self.irq_handler(0) # handle stuck IRQ if filename.endswith(".nes") \ or filename.endswith(".snes") \ or filename.endswith(".smc") \ or filename.endswith(".sfc"): import ld_nes s=ld_nes.ld_nes(self.spi,self.cs) s.ctrl(1) s.ctrl(0) s.load_stream(open(filename,"rb")) del s gc.collect() self.enable[0]=0 self.osd_enable(0) if filename.startswith("/sd/ti99_4a/") and filename.endswith(".bin"): import ld_ti99_4a s=ld_ti99_4a.ld_ti99_4a(self.spi,self.cs) s.load_rom_auto(open(filename,"rb"),filename) del s gc.collect() self.enable[0]=0 self.osd_enable(0) if (filename.startswith("/sd/msx") and filename.endswith(".rom")) \ or filename.endswith(".mx1"): import ld_msx s=ld_msx.ld_msx(self.spi,self.cs) s.load_msx_rom(open(filename,"rb")) del s gc.collect() self.enable[0]=0 self.osd_enable(0) if filename.endswith(".z80"): self.enable[0]=0 self.osd_enable(0) import ld_zxspectrum s=ld_zxspectrum.ld_zxspectrum(self.spi,self.cs) s.loadz80(filename) del s gc.collect() if filename.endswith(".ora") or filename.endswith(".orao"): self.enable[0]=0 self.osd_enable(0) import ld_orao s=ld_orao.ld_orao(self.spi,self.cs) s.loadorao(filename) del s gc.collect() if filename.endswith(".vsf"): self.enable[0]=0 self.osd_enable(0) import ld_vic20 s=ld_vic20.ld_vic20(self.spi,self.cs) s.loadvsf(filename) del s gc.collect() if filename.endswith(".prg"): self.enable[0]=0 self.osd_enable(0) import ld_vic20 s=ld_vic20.ld_vic20(self.spi,self.cs) s.loadprg(filename) del s gc.collect() if filename.endswith(".cas"): self.enable[0]=0 self.osd_enable(0) import ld_trs80 s=ld_trs80.ld_trs80(self.spi,self.cs) s.loadcas(filename) del s gc.collect() if filename.endswith(".cmd"): self.enable[0]=0 self.osd_enable(0) import ld_trs80 s=ld_trs80.ld_trs80(self.spi,self.cs) s.loadcmd(filename) del s gc.collect() @micropython.viper def osd_enable(self, en:int): pena = ptr8(addressof(self.spi_enable_osd)) pena[5] = en&1 self.cs.on() self.spi.write(self.spi_enable_osd) self.cs.off() @micropython.viper def osd_print(self, x:int, y:int, i:int, text): p8msg=ptr8(addressof(self.spi_write_osd)) a=0xF000+(x&63)+((y&31)<<6) p8msg[2]=i p8msg[3]=a>>8 p8msg[4]=a self.cs.on() self.spi.write(self.spi_write_osd) self.spi.write(text) self.cs.off() @micropython.viper def osd_cls(self): p8msg=ptr8(addressof(self.spi_write_osd)) p8msg[3]=0xF0 p8msg[4]=0 self.cs.on() self.spi.write(self.spi_write_osd) self.spi.read(1280,32) self.cs.off() # y is actual line on the screen def show_dir_line(self, y): if y < 0 or y >= self.screen_y: return mark = 0 invert = 0 if y == self.fb_cursor - self.fb_topitem: mark = 1 invert = 1 if y == self.fb_selected - self.fb_topitem: mark = 2 i = y+self.fb_topitem if i >= len(self.direntries): self.osd_print(0,y,0,"%64s" % "") return if self.direntries[i][1]: # directory self.osd_print(0,y,invert,"%c%-57s D" % (self.mark[mark],self.direntries[i][0])) else: # file mantissa = self.direntries[i][2] exponent = 0 while mantissa >= 1024: mantissa >>= 10 exponent += 1 self.osd_print(0,y,invert,"%c%-57s %4d%c" % (self.mark[mark],self.direntries[i][0], mantissa, self.exp_names[exponent])) def show_dir(self): for i in range(self.screen_y): self.show_dir_line(i) def move_dir_cursor(self, step): oldcursor = self.fb_cursor if step == 1: if self.fb_cursor < len(self.direntries)-1: self.fb_cursor += 1 if step == -1: if self.fb_cursor > 0: self.fb_cursor -= 1 if oldcursor != self.fb_cursor: screen_line = self.fb_cursor - self.fb_topitem if screen_line >= 0 and screen_line < self.screen_y: # move cursor inside screen, no scroll self.show_dir_line(oldcursor - self.fb_topitem) # no highlight self.show_dir_line(screen_line) # highlight else: # scroll if screen_line < 0: # cursor going up screen_line = 0 if self.fb_topitem > 0: self.fb_topitem -= 1 self.show_dir() else: # cursor going down screen_line = self.screen_y-1 if self.fb_topitem+self.screen_y < len(self.direntries): self.fb_topitem += 1 self.show_dir() def read_dir(self): self.direntries = [] ls = sorted(os.listdir(self.cwd)) for fname in ls: stat = os.stat(self.fullpath(fname)) if stat[0] & 0o170000 == 0o040000: self.direntries.append([fname,1,0]) # directory for fname in ls: stat = os.stat(self.fullpath(fname)) if stat[0] & 0o170000 != 0o040000: self.direntries.append([fname,0,stat[6]]) # file def mdv_skip_preamble(self,n:int)->int: i=0 j=0 found=0 while j<n: if self.diskfile.readinto(self.mdv_byte): if self.mdv_byte[0]==0xFF: self.diskfile.readinto(self.mdv_byte) if self.mdv_byte[0]==0xFF and i>=10: found=1 i+=2 break else: i=0 else: if self.mdv_byte[0]==0: i+=1 else: i=0 j+=1 else: # EOF, make it circular self.diskfile.seek(0) print("MDV: wraparound",self.data_buf[2:12].decode("utf-8")) if found: return i return 0 def mdv_refill_buf(self): if self.mdv_skip_preamble(1000): self.diskfile.readinto(self.data_buf) # skip block if header doesn't start with 0xFF #i=0 #while self.data_buf[0]!=0xFF and i<254: # self.mdv_skip_preamble(1000) # self.diskfile.readinto(self.data_buf) # i+=1 #print(self.data_buf[1],self.data_buf[2:12]) # block number, volume name #print(self.data_buf[0:16],self.data_buf[28:32],self.data_buf[40:44]) # block number, volume name else: print("MDV: preamble not found") @micropython.viper def mdv_checksum(self,a:int,b:int)->int: p8b=ptr8(addressof(self.data_buf)) c=0xF0F i=a while i<b: c+=p8b[i] i+=1 return c&0xFFFF def mdv_read(self): if self.mdv_phase[0]: self.ctrl(8) # R_cpu_control[3]=1 self.cs.on() self.spi.write(self.spi_send_mdv_bram) self.spi.write(self.data_buf[0:16]) self.cs.off() self.ctrl(0) else: self.ctrl(8) self.cs.on() self.spi.write(self.spi_send_mdv_bram) self.spi.write(self.data_buf[28:32]) self.spi.write(self.data_buf[40:554]) self.cs.off() self.ctrl(0) self.led.on() self.mdv_refill_buf() # fix checksum in broken MDV images #c=self.mdv_checksum(0,14) #self.data_buf[14]=c #self.data_buf[15]=c>>8 c=self.mdv_checksum(28,30) self.data_buf[30]=c self.data_buf[31]=c>>8 #c=self.mdv_checksum(40,552) #self.data_buf[552]=c #self.data_buf[553]=c>>8 self.led.off() self.mdv_phase[0]^=1 # NOTE: this can be used for debugging #def osd(self, a): # if len(a) > 0: # enable = 1 # else: # enable = 0 # self.cs.on() # self.spi.write(bytearray([0,0xFE,0,0,0,enable])) # enable OSD # self.cs.off() # if enable: # self.cs.on() # self.spi.write(bytearray([0,0xFD,0,0,0])) # write content # self.spi.write(bytearray(a)) # write content # self.cs.off() def ctrl(self,i): self.cs.on() self.spi.write(bytearray([0, 0xFF, 0xFF, 0xFF, 0xFF, i])) self.cs.off() def peek(self,addr,length): self.ctrl(4) self.ctrl(6) self.cs.on() self.spi.write(bytearray([1,(addr >> 24) & 0xFF, (addr >> 16) & 0xFF, (addr >> 8) & 0xFF, addr & 0xFF, 0])) b=bytearray(length) self.spi.readinto(b) self.cs.off() self.ctrl(4) self.ctrl(0) return b def poke(self,addr,data): self.ctrl(4) self.ctrl(6) self.cs.on() self.spi.write(bytearray([0,(addr >> 24) & 0xFF, (addr >> 16) & 0xFF, (addr >> 8) & 0xFF, addr & 0xFF])) self.spi.write(data) self.cs.off() self.ctrl(4) self.ctrl(0)
class RADIO: def __init__( self, mode=LORA, # 0 - LoRa, 1 - FSK, 2 - OOK pars={ 'freq_kHz': 434000, # kHz 'freq_Hz': 0, # Hz 'power': 10, # 2...17 dBm 'crc': True, # CRC on/off # LoRa mode: 'bw': 125, # BW: 7.8...500 kHz 'sf': 10, # SF: 6..12 'cr': 5, # CR: 5...8 'ldro': None, # Low Data Rate Optimize (None - automatic) 'sw': 0x12, # Sync Word (allways 0x12) 'preamble': 8, # 6...65535 'implicit_header': False, # FSK/OOK mode: 'bitrate': 4800., # bit/s 'fdev': 5000., # frequency deviation [Hz] 'rx_bw': 10.4, # 2.6...250 kHz 'afc_bw': 2.6, # 2.6...250 kHz 'afc': False, # AFC on/off 'fixed': False, # fixed packet size or variable 'dcfree': 0 }, # 0=None, 1=Manchester or 2=Whitening gpio={ 'led': 2, # blue LED GPIO number on board 'reset': 5, # reset pin from GPIO5 (or may be None) 'dio0': 4, # DIO0 line to GPIO4 'cs': 15, # SPI CS 'sck': 14, # SPI SCK 'mosi': 13, # SPI MOSI 'miso': 12 }, # SPI MISO spi_hardware=True, spi_baudrate=None, onReceive=None): # receive callback # init GPIO self.pin_led = Pin(gpio['led'], Pin.OUT) self.led(0) # LED off if gpio['reset'] != None: self.pin_reset = Pin(gpio['reset'], Pin.OUT, Pin.PULL_UP) self.pin_reset.value(1) else: self.pin_reset = None self.pin_dio0 = Pin(gpio['dio0'], Pin.IN, Pin.PULL_UP) self.pin_cs = Pin(gpio['cs'], Pin.OUT, Pin.PULL_UP) self.pin_cs.value(1) # init SPI if spi_hardware: if spi_baudrate == None: spi_baudrate = 5000000 # 5MHz if ESP32: self.spi = SPI(1, baudrate=spi_baudrate, polarity=0, phase=0, sck=Pin(14), mosi=Pin(13), miso=Pin(12)) else: self.spi = SPI(1, baudrate=spi_baudrate, polarity=0, phase=0) else: if spi_baudrate == None: spi_baudrate = 500000 # 500kHz self.spi = SPI(-1, baudrate=spi_baudrate, polarity=0, phase=0, sck=Pin(gpio['sck']), mosi=Pin(gpio['mosi']), miso=Pin(gpio['miso'])) #bits=8, firstbit=SPI.MSB, # FIXME #sck=Pin(gpio['sck'], Pin.OUT, Pin.PULL_DOWN), #mosi=Pin(gpio['mosi'], Pin.OUT, Pin.PULL_UP), #miso=Pin(gpio['miso'], Pin.IN, Pin.PULL_UP)) self.spi.init() self.onReceive(onReceive) #self._lock = False self.reset() self._mode = 0 # LoRa mode by default self.init(mode, pars) def __exit__(self): self.pin_dio0.irq(trigger=0, handler=None) self.spi.close() def spiTransfer(self, address, value=0x00): response = bytearray(1) self.pin_cs.value(0) self.spi.write(bytes([address])) self.spi.write_readinto(bytes([value]), response) self.pin_cs.value(1) return response def readReg(self, address, byteorder='big', signed=False): """read 8-bit register by SPI""" response = self.spiTransfer(address & 0x7F) return int.from_bytes(response, byteorder) def writeReg(self, address, value): """write 8-bit register by SPI""" self.spiTransfer(address | 0x80, value) def led(self, on=True): """on/off LED on GPIO pin""" self.pin_led.value(not LED_ON ^ on) def blink(self, times=1, on_ms=100, off_ms=20): """short blink LED on GPIO pin""" for i in range(times): self.led(1) sleep_ms(on_ms) self.led(0) sleep_ms(off_ms) def reset(self, low_ms=100, high_ms=100, times=1): """hard reset SX127x chip""" if self.pin_reset: for i in range(times): self.pin_reset.value(1) sleep_ms(high_ms) self.pin_reset.value(0) sleep_ms(low_ms) self.pin_reset.value(1) sleep_ms(high_ms) def version(self): """get SX127x crystal revision""" return self.readReg(REG_VERSION) def setMode(self, mode): """set mode""" self.writeReg(REG_OP_MODE, (self.readReg(REG_OP_MODE) & ~MODES_MASK) | mode) def getMode(self): """get mode""" return self.readReg(REG_OP_MODE) & MODES_MASK def lora(self, lora=True): """switch to LoRa mode""" mode = self.readReg(REG_OP_MODE) # read mode sleep = (mode & ~MODES_MASK) | MODE_SLEEP self.writeReg(REG_OP_MODE, sleep) # go to sleep if lora: sleep |= MODE_LONG_RANGE mode |= MODE_LONG_RANGE else: sleep &= ~MODE_LONG_RANGE mode &= ~MODE_LONG_RANGE self.writeReg(REG_OP_MODE, sleep) # write "long range" bit self.writeReg(REG_OP_MODE, mode) # restore old mode def isLora(self): """check LoRa (or FSK/OOK) mode""" mode = self.readReg(REG_OP_MODE) # read mode return True if (mode & MODE_LONG_RANGE) else False def fsk(self, fsk=True): """switch to FSK mode""" self.lora(not fsk) if fsk: self.writeReg(REG_OP_MODE, (self.readReg(REG_OP_MODE) & ~MODES_MASK2) | MODE_FSK) def ook(self, ook=True): """switch to OOK mode""" self.lora(not ook) if ook: self.writeReg(REG_OP_MODE, (self.readReg(REG_OP_MODE) & ~MODES_MASK2) | MODE_OOK) def sleep(self): """switch to Sleep Mode:""" self.setMode(MODE_SLEEP) def standby(self): """switch ro Standby mode""" self.setMode(MODE_STDBY) def tx(self, on=True): """on/off TX mode (off = standby)""" if on: self.setMode(MODE_TX) else: self.setMode(MODE_STDBY) def rx(self, on=True): """on/off RX (continuous) mode (off = standby)""" if on: self.setMode(MODE_RX_CONTINUOUS) else: self.setMode(MODE_STDBY) def cad(self, on=True): """on/off CAD (LoRa) mode (off = standby)""" if self._mode == 0: # LoRa mode if on: self.setMode(MODE_CAD) else: self.setMode(MODE_STDBY) def init(self, mode=None, pars=None): """init chip""" if mode is not None: self._mode = mode if pars: self._pars = pars # check version version = self.version() print("SX127x selicon revision = 0x%02X" % version) if version != 0x12: raise Exception('Invalid SX127x selicon revision') # switch mode if self._mode == 1: self.fsk() # FSK elif self._mode == 2: self.ook() # OOK else: self.lora() # LoRa # set RF frequency self.setFrequency(self._pars['freq_kHz'], self._pars['freq_Hz']) # set LNA boost: `LnaBoostHf`->3 (Boost on, 150% LNA current) self.setLnaBoost(True) # set output power level self.setPower(self._pars['power']) # enable/disable CRC self.enableCRC(self._pars["crc"]) if self._mode == 0: # set LoRaTM options self.setBW(self._pars['bw']) self._implicitHeaderMode = None self.setImplicitHeaderMode(self._pars['implicit_header']) sf = self._pars['sf'] self.setSF(sf) ldro = self._pars['ldro'] if ldro == None: ldro = True if sf >= 10 else False # FIXME self.setLDRO(ldro) self.setCR(self._pars['cr']) self.setPreamble(self._pars['preamble']) self.setSW(self._pars['sw']) # set AGC auto on (internal AGC loop) self.writeReg(REG_MODEM_CONFIG_3, self.readReg(REG_MODEM_CONFIG_3) | 0x04) # `AgcAutoOn` # set base addresses self.writeReg(REG_FIFO_TX_BASE_ADDR, FIFO_TX_BASE_ADDR) self.writeReg(REG_FIFO_RX_BASE_ADDR, FIFO_RX_BASE_ADDR) # set DIO0 mapping (`RxDone`) self.writeReg(REG_DIO_MAPPING_1, 0x00) else: # set FSK/OOK options self.continuous(False) # packet mode by default self.setBitrate(self._pars["bitrate"]) self.setFdev(self._pars["fdev"]) self.setRxBW(self._pars["rx_bw"]) self.setAfcBW(self._pars["afc_bw"]) self._fixedLen = None self.setFixedLen(self._pars["fixed"]) self.enableAFC(self._pars["afc"]) self.setDcFree(self._pars["dcfree"]) self.writeReg(REG_RSSI_TRESH, 0xFF) # default self.writeReg(REG_PREAMBLE_LSB, 8) # 3 by default self.writeReg(REG_SYNC_VALUE_1, 0x69) # 0x01 by default self.writeReg(REG_SYNC_VALUE_2, 0x81) # 0x01 by default self.writeReg(REG_SYNC_VALUE_3, 0x7E) # 0x01 by default self.writeReg(REG_SYNC_VALUE_4, 0x96) # 0x01 by default # set `DataMode` to Packet (and reset PayloadLength(10:8) to 0) self.writeReg(REG_PACKET_CONFIG_2, 0x40) # set TX start FIFO condition self.writeReg(REG_FIFO_THRESH, TX_START_FIFO_NOEMPTY) # set DIO0 mapping (by default): # in RxContin - `SyncAddres` # in TxContin - `TxReady` # in RxPacket - `PayloadReady` <- used signal # in TxPacket - `PacketSent` self.writeReg(REG_DIO_MAPPING_1, 0x00) # RSSI and IQ callibrate self.rxCalibrate() self.standby() def setFrequency(self, freq_kHz, freq_Hz=0): """set RF frequency [kHz * 1000 + Hz]""" self._freq = int(freq_kHz) * 1000 + freq_Hz # kHz + Hz -> Hz freq_code = int(round(self._freq / FSTEP)) self.writeReg(REG_FRF_MSB, (freq_code >> 16) & 0xFF) self.writeReg(REG_FRF_MID, (freq_code >> 8) & 0xFF) self.writeReg(REG_FRF_LSB, freq_code & 0xFF) mode = self.readReg(REG_OP_MODE) if self._freq < 600000000: # LF <= 525 < _600_ < 779 <= HF [MHz] mode |= MODE_LOW_FREQ_MODE_ON # LF else: mode &= ~MODE_LOW_FREQ_MODE_ON # HF self.writeReg(REG_OP_MODE, mode) def setPower(self, level, PA_BOOST=True, MaxPower=7): """set TX Power level 2...17 dBm, select PA_BOOST pin""" MaxPower = min(max(MaxPower, 0), 7) if PA_BOOST: # Select PA_BOOST pin: Pout is limited to ~17..20 dBm # Pout = 17 - (15 - OutputPower) dBm OutputPower = min(max(level - 2, 0), 15) self.writeReg(REG_PA_CONFIG, PA_SELECT | OutputPower) else: # Select RFO pin: Pout is limited to ~14..15 dBm # Pmax = 10.8 + 0.6 * MaxPower [dBm] # Pout = Pmax - (15 - OutputPower)) = 0...15 dBm if MaxPower=7 OutputPower = min(max(level, 0), 15) self.writeReg(REG_PA_CONFIG, (MaxPower << 4) | OutputPower) def setHighPower(self, on=True): """set high power on PA_BOOST up to +20 dBm""" if on: # +3dB self.writeReg(REG_PA_DAC, 0x87) # power on PA_BOOST pin up to +20 dBm else: self.writeReg(REG_PA_DAC, 0x84) # default mode def setOCP(self, trim_mA=100., on=True): """set trimming of OCP current (45...240 mA)""" if trim_mA <= 120.: OcpTrim = round((trim_mA - 45.) / 5.) else: OcpTrim = round((trim_mA + 30.) / 10.) OcpTrim = min(max(OcpTrim, 0), 27) if on: OcpTrim |= 0x20 # `OcpOn` self.writeReg(REG_OCP, OcpTrim) def setLnaBoost(self, LnaBoost=True): """set LNA boost on/off (only for high frequency band)""" reg = self.readReg(REG_LNA) if LnaBoost: reg |= 0x03 # set `LnaBoostHf` to 3 (boost on, 150% LNA current) else: reg &= ~0x03 # set `LnaBoostHf` to 0 (default LNA current) self.writeReg(REG_LNA, reg) def setRamp(self, shaping=0, ramp=0x09): """set modulation shaping code 0..3 (FSK/OOK) and PA rise/fall time code 0..15 (FSK/Lora)""" shaping = min(max(shaping, 0), 3) ramp = min(max(ramp, 0), 15) reg = self.readReg(REG_PA_RAMP) reg = (reg & 0x90) | (shaping << 5) | ramp self.writeReg(REG_PA_RAMP, reg) def enableCRC(self, crc=True, crcAutoClearOff=True): """enable/disable CRC (and set CrcAutoClearOff in FSK/OOK mode)""" self._crc = crc if self._mode == 0: # LoRa mode reg = self.readReg(REG_MODEM_CONFIG_2) reg = (reg | 0x04) if crc else (reg & ~0x04) # `RxPayloadCrcOn` self.writeReg(REG_MODEM_CONFIG_2, reg) else: # FSK/OOK mode reg = self.readReg(REG_PACKET_CONFIG_1) & ~0x18 if crc: reg |= 0x10 # `CrcOn` if crcAutoClearOff: reg |= 0x08 # `CrcAutoClearOff` self.writeReg(REG_PACKET_CONFIG_1, reg) def getRxGain(self): """get current RX gain code [1..6] from `RegLna` (1 - maximum gain)""" return (self.readReg(REG_LNA) >> 5) & 0x07 # `LnaGain` def getPktRSSI(self): """get Packet RSSI [dB] (LoRa)""" if self._mode == 0: # LoRa mode return self.readReg(REG_PKT_RSSI_VALUE) - \ (164. if self._freq < 600000000 else 157.) else: # FSK/OOK mode return -0.5 * self.readReg(REG_RSSI_VALUE) def getRSSI(self): """get RSSI [dB]""" if self._mode == 0: # LoRa mode return self.readReg(REG_LR_RSSI_VALUE) - \ (164. if self._freq < 600000000 else 157.) else: # FSK/OOK mode return -0.5 * self.readReg(REG_RSSI_VALUE) def getSNR(self): """get SNR [dB] (LoRa)""" if self._mode == 0: # LoRa mode snr = self.readReg(REG_PKT_SNR_VALUE) if snr & 0x80: # sign bit is 1 snr -= 256 return snr * 0.25 else: # FSK/OOK mode return 0. def getIrqFlags(self): """get IRQ flags for debug""" if self._mode == 0: # LoRa mode irqFlags = self.readReg(REG_IRQ_FLAGS) self.writeReg(REG_IRQ_FLAGS, irqFlags) return irqFlags else: # FSK/OOK mode irqFlags1 = self.readReg(REG_IRQ_FLAGS_1) irqFlags2 = self.readReg(REG_IRQ_FLAGS_2) return (irqFlags2 << 8) | irqFlags1 def enableRxIrq(self, enable=True): """enable/disable interrupt by RX done for debug (LoRa)""" if self._mode == 0: # LoRa mode reg = self.readReg(REG_IRQ_FLAGS_MASK) if enable: reg &= ~IRQ_RX_DONE_MASK else: reg |= IRQ_RX_DONE_MASK self.writeReg(REG_IRQ_FLAGS_MASK, reg) def invertIQ(self, invert=True): """invert IQ channels (LoRa)""" if self._mode == 0: reg = self.readReg(REG_INVERT_IQ) if invert: reg |= 0x40 # `InvertIq` = 1 else: reg &= ~0x40 # `InvertIq` = 0 self.writeReg(REG_INVERT_IQ, reg) def setSF(self, sf=10): """set Spreading Factor 6...12 (LoRa)""" if self._mode == 0: sf = min(max(sf, 6), 12) self.writeReg(REG_DETECT_OPTIMIZE, 0xC5 if sf == 6 else 0xC3) self.writeReg(REG_DETECTION_THRESHOLD, 0x0C if sf == 6 else 0x0A) self.writeReg(REG_MODEM_CONFIG_2, (self.readReg(REG_MODEM_CONFIG_2) & 0x0F) | ((sf << 4) & 0xF0)) def setLDRO(self, ldro): """set Low Data Rate Optimisation (LoRa)""" if self._mode == 0: self.writeReg( REG_MODEM_CONFIG_3, # `LowDataRateOptimize` (self.readReg(REG_MODEM_CONFIG_3) & ~0x08) | 0x08 if ldro else 0) def setBW(self, sbw): """set signal Band Width 7.8-500 kHz (LoRa)""" if self._mode == 0: bw = len(BW_TABLE) - 1 for i in range(bw + 1): if sbw <= BW_TABLE[i]: bw = i break self.writeReg(REG_MODEM_CONFIG_1, \ (self.readReg(REG_MODEM_CONFIG_1) & 0x0F) | (bw << 4)) def setCR(self, denominator): """set Coding Rate [5..8] (LoRa)""" if self._mode == 0: denominator = min(max(denominator, 5), 8) cr = denominator - 4 self.writeReg(REG_MODEM_CONFIG_1, (self.readReg(REG_MODEM_CONFIG_1) & 0xF1) | (cr << 1)) def setPreamble(self, length): """set preamble length [6...65535] (LoRa)""" if self._mode == 0: self.writeReg(REG_PREAMBLE_MSB, (length >> 8) & 0xFF) self.writeReg(REG_PREAMBLE_LSB, (length) & 0xFF) def setSW(self, sw): # LoRa mode only """set Sync Word (LoRa)""" if self._mode == 0: self.writeReg(REG_SYNC_WORD, sw) def setImplicitHeaderMode(self, implicitHeaderMode=True): """set ImplicitHeaderModeOn (LoRa)""" if self._mode == 0: if self._implicitHeaderMode != implicitHeaderMode: # set value only if different self._implicitHeaderMode = implicitHeaderMode modem_config_1 = self.readReg(REG_MODEM_CONFIG_1) config = modem_config_1 | 0x01 if implicitHeaderMode else \ modem_config_1 & 0xFE self.writeReg(REG_MODEM_CONFIG_1, config) def setBitrate(self, bitrate=4800.): """set bitrate [bit/s] (FSK/OOK)""" if self._mode == 1: # FSK code = int(round((FXOSC * 16.) / bitrate)) # bit/s -> code/frac self.writeReg(REG_BITRATE_MSB, (code >> 12) & 0xFF) self.writeReg(REG_BITRATE_LSB, (code >> 4) & 0xFF) self.writeReg(REG_BITRATE_FRAC, code & 0x0F) elif self._mode == 2: # OOK code = int(round(FXOSC / bitrate)) # bit/s -> code self.writeReg(REG_BITRATE_MSB, (code >> 8) & 0xFF) self.writeReg(REG_BITRATE_LSB, code & 0xFF) self.writeReg(REG_BITRATE_FRAC, 0) def setFdev(self, fdev=5000.): """set frequency deviation (FSK)""" if self._mode: code = int(round(fdev / FSTEP)) # Hz -> code code = min(max(code, 0), 0x3FFF) self.writeReg(REG_FDEV_MSB, (code >> 8) & 0xFF) self.writeReg(REG_FDEV_LSB, code & 0xFF) def setRxBW(self, bw=10.4): """set RX BW [kHz] (FSK/OOK)""" if self._mode: m, e = getRxBw(bw) self.writeReg(REG_RX_BW, (m << 3) | e) def setAfcBW(self, bw=2.6): """set AFC BW [kHz] (FSK/OOK)""" if self._mode: m, e = getRxBw(bw) self.writeReg(REG_AFC_BW, (m << 3) | e) def enableAFC(self, afc=True): """enable/disable AFC (FSK/OOK)""" if self._mode: reg = self.readReg(REG_RX_CONFIG) if afc: reg |= 0x10 # bit 4: AfcAutoOn -> 1 else: reg &= ~0x10 # bit 4: AfcAutoOn -> 0 self.writeReg(REG_RX_CONFIG, reg) def setFixedLen(self, fixed=True): """set Fixed or Variable packet mode (FSK/OOK)""" if self._mode: if self._fixedLen != fixed: # set value only if different self._fixedLen = fixed reg = self.readReg(REG_PACKET_CONFIG_1) if fixed: reg &= ~0x80 # bit 7: PacketFormat -> 0 (fixed size) else: reg |= 0x80 # bit 7: PacketFormat -> 1 (variable size) self.writeReg(REG_PACKET_CONFIG_1, reg) def setDcFree(self, mode=0): """set DcFree mode: 0=Off, 1=Manchester, 2=Whitening (FSK/OOK)""" if self._mode: reg = self.readReg(REG_PACKET_CONFIG_1) reg = (reg & 0x9F) | ((mode & 3) << 5) # bit 6-5 `DcFree` self.writeReg(REG_PACKET_CONFIG_1, reg) def continuous(self, on=True): """select Continuous mode, must use DIO2->DATA, DIO1->DCLK (FSK/OOK)""" if self._mode: reg = self.readReg(REG_PACKET_CONFIG_2) if on: reg &= ~0x40 # bit 6: `DataMode` 0 -> Continuous mode else: reg |= 0x40 # bit 6: `DataMode` 1 -> Packet mode self.writeReg(REG_PACKET_CONFIG_2, reg) def rxCalibrate(self): """RSSI and IQ callibration (FSK/OOK)""" if self._mode: reg = self.readReg(REG_IMAGE_CAL) reg |= 0x40 # `ImageCalStart` bit self.writeReg(REG_IMAGE_CAL, reg) while (self.readReg(REG_IMAGE_CAL) & 0x20): # `ImageCalRunning` pass # FIXME: check timeout def setPllBW(self, bw=3): """set PLL bandwidth 0=75, 1=150, 2=225, 3=300 kHz (LoRa/FSK/OOK)""" bw = min(max(bw, 0), 3) reg = self.readReg(REG_PLL) reg = (reg & 0x3F) | (bw << 6) self.writeReg(REG_PLL, reg) def setFastHop(self, on=True): """on/off fast frequency PLL hopping (FSK/OOK)""" if self._mode: reg = self.readReg(REG_PLL_HOP) reg = reg | 0x80 if on else reg & 0x7F # `FastHopOn` self.writeReg(REG_PLL_HOP, reg) #def aquire_lock(self, lock=False): # if not MICROPYTHON: # MicroPython is single threaded, doesn't need lock. # if lock: # while self._lock: pass # self._lock = True # else: # self._lock = False def send(self, string, fixed=False): """send packet (LoRa/FSK/OOK)""" #self.aquire_lock(True) # wait until RX_Done, lock and begin writing. self.setMode(MODE_STDBY) buf = string.encode() size = len(buf) if self._mode == 0: # LoRa mode self.setImplicitHeaderMode(fixed) # set FIFO TX base address self.writeReg(REG_FIFO_ADDR_PTR, FIFO_TX_BASE_ADDR) # check size size = min(size, MAX_PKT_LENGTH) # write data for i in range(size): self.writeReg(REG_FIFO, buf[i]) # set length self.writeReg(REG_PAYLOAD_LENGTH, size) # start TX packet self.setMode(MODE_TX) # put in TX mode # wait for TX done, standby automatically on TX_DONE while (self.readReg(REG_IRQ_FLAGS) & IRQ_TX_DONE) == 0: pass # FIXME: check timeout # clear IRQ's self.writeReg(REG_IRQ_FLAGS, IRQ_TX_DONE) else: # FSK/OOK mode self.setFixedLen(fixed) size = min(size, MAX_PKT_LENGTH) # limit size # set TX start FIFO condition #self.writeReg(REG_FIFO_THRESH, TX_START_FIFO_NOEMPTY) # wait while FIFO is no empty while ((self.readReg(REG_IRQ_FLAGS_2) & IRQ2_FIFO_EMPTY) == 0): pass # FIXME: check timeout if self._fixedLen: self.writeReg(REG_PAYLOAD_LEN, size) # fixed length #add = 0 else: self.writeReg(REG_FIFO, size) # variable length #add = 1 # set TX start FIFO condition #self.writeReg(REG_FIFO_THRESH, TX_START_FIFO_LEVEL | (size + add)) # write data to FIFO for i in range(size): self.writeReg(REG_FIFO, buf[i]) # start TX packet self.setMode(MODE_TX) # wait `TxRaedy` (bit 5 in `RegIrqFlags1`) #while ((self.readReg(REG_IRQ_FLAGS_1) & IRQ1_TX_READY) == 0): # pass # FIXME: check timeout # wait `PacketSent` (bit 3 in `RegIrqFlags2`) while ((self.readReg(REG_IRQ_FLAGS_2) & IRQ2_PACKET_SENT) == 0): pass # FIXME: check timeout # switch to standby mode self.setMode(MODE_STDBY) self.collect() #self.aquire_lock(False) # unlock when done writing def onReceive(self, callback): """set callback on receive packet (Lora/FSK/OOK)""" self._onReceive = callback if callback: self.pin_dio0.irq(trigger=Pin.IRQ_RISING, handler=self._handleOnReceive) else: self.pin_dio0.irq(trigger=0, handler=None) def receive(self, size=0): """go to RX mode; wait callback by interrupt (LoRa/FSK/OOK)""" size = min(size, MAX_PKT_LENGTH) if self._mode == 0: # LoRa mode self.setImplicitHeaderMode(size > 0) if size > 0: self.writeReg(REG_PAYLOAD_LENGTH, size) # implicit header else: # FSK/OOK mode self.setFixedLen(size > 0) if size > 0: self.writeReg(REG_PAYLOAD_LEN, size) # fixed length else: self.writeReg(REG_PAYLOAD_LEN, MAX_PKT_LENGTH) # variable length self.setMode(MODE_RX_CONTINUOUS) def collect(self): """garbage collection""" gc.collect() #if MICROPYTHON: # print('[Memory - free: {} allocated: {}]'.format(gc.mem_free(), gc.mem_alloc())) def _handleOnReceive(self, event_source): #self.aquire_lock(True) if self._mode == 0: # LoRa mode irqFlags = self.readReg(REG_IRQ_FLAGS) # should be 0x50 self.writeReg(REG_IRQ_FLAGS, irqFlags) if (irqFlags & IRQ_RX_DONE) == 0: # check `RxDone` #self.aquire_lock(False) return # `RxDone` is not set print( "DIO0 interrupt in LoRa mode by `RxDone` (RegIrqFlags=0x%02X)" % irqFlags) # check `PayloadCrcError` bit crcOk = not bool(irqFlags & IRQ_PAYLOAD_CRC_ERROR) # set FIFO address to current RX address self.writeReg(REG_FIFO_ADDR_PTR, self.readReg(REG_FIFO_RX_CURRENT_ADDR)) # read packet length packetLen = self.readReg(REG_PAYLOAD_LENGTH) if self._implicitHeaderMode else \ self.readReg(REG_RX_NB_BYTES) else: # FSK/OOK mode irqFlags = self.readReg(REG_IRQ_FLAGS_2) # should be 0x26/0x24 if (irqFlags & IRQ2_PAYLOAD_READY) == 0: #self.aquire_lock(False) return # `PayloadReady` is not set print( "DIO0 interrupt in FSK/OOK mode by `PayloadReady` (RegIrqFlags2=0x%02X)" % irqFlags) # check `CrcOk` bit crcOk = bool(irqFlags & IRQ2_CRC_OK) # read packet length if self.readReg(REG_PACKET_CONFIG_1) & 0x80: # `PacketFormat` packetLen = self.readReg(REG_FIFO) # variable length else: packetLen = self.readReg(REG_PAYLOAD_LEN) # fixed length # read FIFO payload = bytearray(packetLen) for i in range(packetLen): payload[i] = self.readReg(REG_FIFO) payload = bytes(payload) self.collect() # run callback if self._onReceive: self._onReceive(self, payload, crcOk if self._crc else None) self.collect() #self.aquire_lock(False) def dump(self): for i in range(128): print("Reg[0x%02X] = 0x%02X" % (i, self.readReg(i)))
class HX711: # Class for controlling HX711 loadcell amplifier from Micropython pyboard. # Adapted from https://github.com/robert-hh/hx711-lopy def __init__(self, data_pin, clock_pin, SPI_clk_pin, gain=128): # data_pin must be SPI MISO # clock pin must be SPI MOSI # SPI_clk_pin is not connected but must be SPI CLK. self.pSCK = Pin(clock_pin, mode=Pin.OUT) self.pOUT = Pin(data_pin, mode=Pin.IN, pull=Pin.PULL_DOWN) self.spi = SPI(baudrate=1000000, polarity=0, phase=0, sck=SPI_clk_pin, mosi=clock_pin, miso=data_pin) self.pSCK(0) self.clock_25 = b'\xaa\xaa\xaa\xaa\xaa\xaa\x80' self.clock_26 = b'\xaa\xaa\xaa\xaa\xaa\xaa\xa0' self.clock_27 = b'\xaa\xaa\xaa\xaa\xaa\xaa\xa8' self.clock = self.clock_25 self.lookup = (b'\x00\x01\x00\x00\x02\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' b'\x04\x05\x00\x00\x06\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' b'\x08\x09\x00\x00\x0a\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' b'\x0c\x0d\x00\x00\x0e\x0f') self.in_data = bytearray(7) self.OFFSET = 0 self.SCALE = 3500 self.time_constant = 0.1 self.filtered = 0 self.set_gain(gain) def set_gain(self, gain): if gain == 128: self.clock = self.clock_25 elif gain == 64: self.clock = self.clock_27 elif gain == 32: self.clock = self.clock_26 self.read() self.filtered = self.read() def read(self): # wait for the device to get ready while self.pOUT() != 0: idle() # get the data and set channel and gain self.spi.write_readinto(self.clock, self.in_data) # pack the data into a single value result = 0 for _ in range(6): result = (result << 4) + self.lookup[self.in_data[_] & 0x55] # return sign corrected result return result - ((result & 0x800000) << 1) def read_average(self, times=3): sum = 0 for i in range(times): sum += self.read() return sum / times def weigh(self, times=3): return (self.read_average(times) - self.OFFSET) / self.SCALE def tare(self, times=15): # Set the 0 value. self.OFFSET = self.read_average(times) def calibrate(self, weight=1, times=15): # Calibrate the scale using a known weight, must be done after scale has beenn tared. self.SCALE = (self.read_average(times) - self.OFFSET) / weight def power_down(self): self.pSCK.value(False) self.pSCK.value(True) def power_up(self): self.pSCK.value(False)
class osd: def __init__(self): self.screen_x = const(64) self.screen_y = const(20) self.cwd = "/" self.init_fb() self.exp_names = " KMGTE" self.mark = bytearray([32, 16, 42]) # space, right triangle, asterisk self.read_dir() self.spi_read_irq = bytearray([1, 0xF1, 0, 0, 0, 0, 0]) self.spi_read_btn = bytearray([1, 0xFB, 0, 0, 0, 0, 0]) self.spi_result = bytearray(7) self.spi_enable_osd = bytearray([0, 0xFE, 0, 0, 0, 1]) self.spi_write_osd = bytearray([0, 0xFD, 0, 0, 0]) self.spi_channel = const(2) self.spi_freq = const(3000000) self.init_pinout_sd() #self.spi=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)) self.init_spi() alloc_emergency_exception_buf(100) self.enable = bytearray(1) self.timer = Timer(3) self.irq_handler(0) self.irq_handler_ref = self.irq_handler # allocation happens here self.spi_request = Pin(0, Pin.IN, Pin.PULL_UP) self.spi_request.irq(trigger=Pin.IRQ_FALLING, handler=self.irq_handler_ref) def init_spi(self): self.spi = 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)) self.cs = Pin(self.gpio_cs, Pin.OUT) self.cs.off() def get_spi_result(self): return self.spi_result def get_spi_result2(self): self.cs.on() self.spi.write_readinto(self.spi_read_btn, self.spi_result) self.cs.off() return self.spi_result # A couple of shortcut functions to change directories, useful for TI-99/4A core development. # After running "import osd", these can be accessed with osd.run.ti_cart() for example. def change_dir(self, dir): os.chdir(dir) print("Current directory changed to {}".format(dir)) def ti_cart(self): self.change_dir("/sd/ti99_4a/cart") def ti_grom(self): self.change_dir("/sd/ti99_4a/grom") def ti_bit(self): self.change_dir("/sd/ti99_4a/bitstreams") # init file browser def init_fb(self): self.fb_topitem = 0 self.fb_cursor = 0 self.fb_selected = -1 @micropython.viper def init_pinout_sd(self): self.gpio_cs = const(5) self.gpio_sck = const(16) self.gpio_mosi = const(4) self.gpio_miso = const(12) @micropython.viper def irq_handler(self, pin): p8result = ptr8(addressof(self.spi_result)) self.cs.on() self.spi.write_readinto(self.spi_read_irq, self.spi_result) self.cs.off() btn_irq = p8result[6] if btn_irq & 0x80: # btn event IRQ flag self.cs.on() self.spi.write_readinto(self.spi_read_btn, self.spi_result) self.cs.off() btn = p8result[6] p8enable = ptr8(addressof(self.enable)) if p8enable[0] & 2: # wait to release all BTNs if btn == 1: p8enable[ 0] &= 1 # clear bit that waits for all BTNs released else: # all BTNs released if (btn & 0x78 ) == 0x78: # all cursor BTNs pressed at the same time self.show_dir() # refresh directory p8enable[0] = (p8enable[0] ^ 1) | 2 self.osd_enable(p8enable[0] & 1) if p8enable[0] == 1: if btn == 9: # btn3 cursor up self.start_autorepeat(-1) if btn == 17: # btn4 cursor down self.start_autorepeat(1) if btn == 1: self.timer.deinit() # stop autorepeat if btn == 33: # btn6 cursor left self.updir() if btn == 65: # btn6 cursor right self.select_entry() def start_autorepeat(self, i: int): self.autorepeat_direction = i self.move_dir_cursor(i) self.timer_slow = 1 self.timer.init(mode=Timer.PERIODIC, period=500, callback=self.autorepeat) def autorepeat(self, timer): if self.timer_slow: self.timer_slow = 0 self.timer.init(mode=Timer.PERIODIC, period=30, callback=self.autorepeat) self.move_dir_cursor(self.autorepeat_direction) self.irq_handler(0) # catch stale IRQ def select_entry(self): if self.direntries[self.fb_cursor][1]: # is it directory oldselected = self.fb_selected - self.fb_topitem self.fb_selected = self.fb_cursor try: self.cwd = self.fullpath(self.direntries[self.fb_cursor][0]) except: self.fb_selected = -1 self.show_dir_line(oldselected) self.show_dir_line(self.fb_cursor - self.fb_topitem) self.init_fb() self.read_dir() self.show_dir() else: self.change_file() def updir(self): if len(self.cwd) < 2: self.cwd = "/" else: s = self.cwd.split("/")[:-1] self.cwd = "" for name in s: if len(name) > 0: self.cwd += "/" + name self.init_fb() self.read_dir() self.show_dir() def fullpath(self, fname): if self.cwd.endswith("/"): return self.cwd + fname else: return self.cwd + "/" + fname def change_file(self): oldselected = self.fb_selected - self.fb_topitem self.fb_selected = self.fb_cursor try: filename = self.fullpath(self.direntries[self.fb_cursor][0]) except: filename = False self.fb_selected = -1 self.show_dir_line(oldselected) self.show_dir_line(self.fb_cursor - self.fb_topitem) if filename: if filename.endswith(".bit"): self.spi_request.irq(handler=None) self.timer.deinit() self.enable[0] = 0 self.osd_enable(0) self.spi.deinit() tap = ecp5.ecp5() tap.prog_stream(open(filename, "rb"), blocksize=1024) if filename.endswith("_sd.bit"): os.umount("/sd") for i in bytearray([2, 4, 12, 13, 14, 15]): p = Pin(i, Pin.IN) a = p.value() del p, a result = tap.prog_close() del tap gc.collect() #os.mount(SDCard(slot=3),"/sd") # BUG, won't work self.init_spi() # because of ecp5.prog() spi.deinit() self.spi_request.irq(trigger=Pin.IRQ_FALLING, handler=self.irq_handler_ref) self.irq_handler(0) # handle stuck IRQ if filename.endswith(".nes") \ or filename.endswith(".snes") \ or filename.endswith(".smc") \ or filename.endswith(".sfc"): import ld_nes s = ld_nes.ld_nes(self.spi, self.cs) s.ctrl(1) s.ctrl(0) s.load_stream(open(filename, "rb"), addr=0, maxlen=0x101000) del s gc.collect() self.enable[0] = 0 self.osd_enable(0) if filename.startswith( "/sd/ti99_4a/") and filename.lower().endswith(".bin"): import ld_ti99_4a s = ld_ti99_4a.ld_ti99_4a(self.spi, self.cs) s.load_rom_auto(open(filename, "rb"), filename) del s gc.collect() self.enable[0] = 0 self.osd_enable(0) if (filename.startswith("/sd/msx") and filename.endswith(".rom")) \ or filename.endswith(".mx1"): import ld_msx s = ld_msx.ld_msx(self.spi, self.cs) s.load_msx_rom(open(filename, "rb")) del s gc.collect() self.enable[0] = 0 self.osd_enable(0) if filename.endswith(".z80"): self.enable[0] = 0 self.osd_enable(0) import ld_zxspectrum s = ld_zxspectrum.ld_zxspectrum(self.spi, self.cs) s.loadz80(filename) del s gc.collect() if filename.endswith(".ora") or filename.endswith(".orao"): self.enable[0] = 0 self.osd_enable(0) import ld_orao s = ld_orao.ld_orao(self.spi, self.cs) s.loadorao(filename) del s gc.collect() if filename.endswith(".vsf"): self.enable[0] = 0 self.osd_enable(0) import ld_vic20 s = ld_vic20.ld_vic20(self.spi, self.cs) s.loadvsf(filename) del s gc.collect() if filename.endswith(".prg"): self.enable[0] = 0 self.osd_enable(0) import ld_vic20 s = ld_vic20.ld_vic20(self.spi, self.cs) s.loadprg(filename) del s gc.collect() if filename.endswith(".cas"): self.enable[0] = 0 self.osd_enable(0) import ld_trs80 s = ld_trs80.ld_trs80(self.spi, self.cs) s.loadcas(filename) del s gc.collect() if filename.endswith(".cmd"): self.enable[0] = 0 self.osd_enable(0) import ld_trs80 s = ld_trs80.ld_trs80(self.spi, self.cs) s.loadcmd(filename) del s gc.collect() @micropython.viper def osd_enable(self, en: int): pena = ptr8(addressof(self.spi_enable_osd)) pena[5] = en & 1 self.cs.on() self.spi.write(self.spi_enable_osd) self.cs.off() @micropython.viper def osd_print(self, x: int, y: int, i: int, text): p8msg = ptr8(addressof(self.spi_write_osd)) a = 0xF000 + (x & 63) + ((y & 31) << 6) p8msg[2] = i p8msg[3] = a >> 8 p8msg[4] = a self.cs.on() self.spi.write(self.spi_write_osd) self.spi.write(text) self.cs.off() @micropython.viper def osd_cls(self): p8msg = ptr8(addressof(self.spi_write_osd)) p8msg[3] = 0xF0 p8msg[4] = 0 self.cs.on() self.spi.write(self.spi_write_osd) self.spi.read(1280, 32) self.cs.off() # y is actual line on the screen def show_dir_line(self, y): if y < 0 or y >= self.screen_y: return mark = 0 invert = 0 if y == self.fb_cursor - self.fb_topitem: mark = 1 invert = 1 if y == self.fb_selected - self.fb_topitem: mark = 2 i = y + self.fb_topitem if i >= len(self.direntries): self.osd_print(0, y, 0, "%64s" % "") return if self.direntries[i][1]: # directory self.osd_print( 0, y, invert, "%c%-57s D" % (self.mark[mark], self.direntries[i][0])) else: # file mantissa = self.direntries[i][2] exponent = 0 while mantissa >= 1024: mantissa >>= 10 exponent += 1 self.osd_print( 0, y, invert, "%c%-57s %4d%c" % (self.mark[mark], self.direntries[i][0], mantissa, self.exp_names[exponent])) def show_dir(self): for i in range(self.screen_y): self.show_dir_line(i) def move_dir_cursor(self, step): oldcursor = self.fb_cursor if step == 1: if self.fb_cursor < len(self.direntries) - 1: self.fb_cursor += 1 if step == -1: if self.fb_cursor > 0: self.fb_cursor -= 1 if oldcursor != self.fb_cursor: screen_line = self.fb_cursor - self.fb_topitem if screen_line >= 0 and screen_line < self.screen_y: # move cursor inside screen, no scroll self.show_dir_line(oldcursor - self.fb_topitem) # no highlight self.show_dir_line(screen_line) # highlight else: # scroll if screen_line < 0: # cursor going up screen_line = 0 if self.fb_topitem > 0: self.fb_topitem -= 1 self.show_dir() else: # cursor going down screen_line = self.screen_y - 1 if self.fb_topitem + self.screen_y < len(self.direntries): self.fb_topitem += 1 self.show_dir() def read_dir(self): self.direntries = [] ls = sorted(os.listdir(self.cwd)) for fname in ls: stat = os.stat(self.fullpath(fname)) if stat[0] & 0o170000 == 0o040000: self.direntries.append([fname, 1, 0]) # directory else: self.direntries.append([fname, 0, stat[6]]) # file gc.collect() # NOTE: this can be used for debugging #def osd(self, a): # if len(a) > 0: # enable = 1 # else: # enable = 0 # self.cs.on() # self.spi.write(bytearray([0,0xFE,0,0,0,enable])) # enable OSD # self.cs.off() # if enable: # self.cs.on() # self.spi.write(bytearray([0,0xFD,0,0,0])) # write content # self.spi.write(bytearray(a)) # write content # self.cs.off() def load_console_grom(self): gromfile = "/sd/ti99_4a/grom/994AGROM.Bin" import ld_ti99_4a s = ld_ti99_4a.ld_ti99_4a(self.spi, self.cs) s.reset_on() print("GROM file: {}".format(gromfile)) bytes = s.load_stream(open(gromfile, "rb"), 0x10000) print("Loaded {} bytes".format(bytes)) s.reset_off() del s gc.collect() def load_console_rom(self): romfile = "/sd/ti99_4a/rom/994AROM.Bin" import ld_ti99_4a s = ld_ti99_4a.ld_ti99_4a(self.spi, self.cs) s.reset_on() print("System ROM file: {}".format(romfile)) bytes = s.load_stream(open(romfile, "rb"), 0) print("Loaded {} bytes".format(bytes)) s.reset_off() del s gc.collect() def load_roms(self): romfile = "/sd/ti99_4a/rom/994AROM.Bin" gromfile = "/sd/ti99_4a/grom/994AGROM.Bin" import ld_ti99_4a s = ld_ti99_4a.ld_ti99_4a(self.spi, self.cs) s.reset_on() # Load ROM print("System ROM file: {}".format(romfile)) bytes = s.load_stream(open(romfile, "rb"), 0) print("Loaded {} bytes".format(bytes)) # Load GROM print("GROM file: {}".format(gromfile)) bytes = s.load_stream(open(gromfile, "rb"), 0x10000) print("Loaded {} bytes".format(bytes)) # remove reset s.reset_off() del s gc.collect() def save_mem(self, filename, addr, length): import ld_ti99_4a old_freq = self.spi_freq self.spi_freq = const(100000) self.init_spi() s = ld_ti99_4a.ld_ti99_4a(self.spi, self.cs) print("Saving memory from {} length {} file: {}".format( addr, length, filename)) s.save_stream(open(filename, "wb"), addr, length) del s self.spi_freq = old_freq self.init_spi() gc.collect()
class Stm32Spi: def __init__(self): # define the existing commands for the communication with STM32 # b'command name': [command byte to send, size in bytes of data structure in STM32, dictionary with data received/to send] self.default_commands = { b'get_general_config': [bytearray(b'\xAA'), 20], b'get_area_config': [bytearray(b'\xBA'), 48], b'get_adc_data': [bytearray(b'\xCA'), 38], b'get_act_data': [bytearray(b'\xDA'), 12], b'set_general_config': [bytearray(b'\xAB'), 20], b'set_area_config': [bytearray(b'\xBB'), 48], b'clear_log': [bytearray(b'\xEA'), 0], b'clear_conf': [bytearray(b'\xEB'), 0] } self.c_recv = None self.status = bytearray(b'\x00') self.dummyByteArr = bytearray(b'\x00') self.nElements = 0 self.nElementsByte = bytearray(b'\x00\x00\x00\x00') self.nElementsTuple = () # configure HW spi max baudrate=40000000 self.spi = SPI(1, baudrate=1000000, polarity=0, phase=0) self.reset = Pin(5, Pin.OUT) # Pin 5 or D1 on node mcu self.reset.on() self.reset.off() self.CSN = Pin(15, Pin.OUT) self.CSN.on() # auxiliary variables self.ackMaster = bytearray( b'\xE3\xE3\xE3\xE3') # ack from master to slave self.ackSlave = bytearray(b'\xCE') # ack byte from slave # variables for spi get data method self.byteArr = bytearray() self.tempArr = bytearray() self.byteArr_txt = '' # for debugging self.numBytes = 0 self.numParts = 0 def reset(self): #self.__init__() self.nElements = 0 def send_command(self, command): self.c_recv = command self.CSN.off() self.spi.write_readinto(self.default_commands[command][0], self.status) time.sleep_ms(5) if self.status == self.ackSlave: if self.c_recv == b'get_general_config': self.nElements = 1 elif self.c_recv == b'get_area_config' or self.c_recv == b'get_adc_data' or self.c_recv == b'get_act_data': self.spi.write_readinto( self.ackMaster, self.nElementsByte) # get number of elements time.sleep_ms(5) self.nElementsTuple = unpack('<HH', self.nElementsByte) self.nElements = self.nElementsTuple[ 0] | self.nElementsTuple[1] << 16 # debug #print("".join("0x%02x " % i for i in self.nElementsByte)) else: print("Slave did not ack. STM32 spi will be reset....") # restart stm32 spi self.reset.on() self.reset.off() self.reset() time.sleep_ms(2000) def get_data(self): self.CSN.off() if self.nElements > 0: # data size is not zero -> continue self.get_spi_bytes() self.nElements -= 1 else: print("Error: Num of elements is {} !".format(self.nElements)) self.CSN.on() def send_data(self, data, command): self.c_recv = command self.byteArr = data if command == b'set_general_config': if len(data) == self.default_commands[self.c_recv][1]: if self.send_spi_bytes() == 1: return 1 else: print("Invalid data size!") return 1 return 0 elif command == b'set_area_config': self.numBytes = self.default_commands[self.c_recv][1] self.numParts = int(len(self.byteArr) / self.numBytes) self.tempArr = self.byteArr for i in range(self.numParts): self.byteArr = self.tempArr[i * self.numBytes:i * self.numBytes + self.numBytes] if self.send_spi_bytes() == 1: return 1 return 0 print("Unknown command while trying to send data to mcu...") return 1 def send_spi_bytes(self): self.dummyByteArr = bytearray( b'\x21' * self.default_commands[self.c_recv][1]) # dummy bytes self.spi.write_readinto(self.byteArr, self.dummyByteArr) time.sleep_ms(1) # sleep msec def get_spi_bytes(self): self.byteArr = bytearray( b'\x00' * self.default_commands[self.c_recv][1]) # initialize to zero self.dummyByteArr = bytearray( b'\x21' * self.default_commands[self.c_recv][1]) # dummy bytes self.spi.write_readinto(self.dummyByteArr, self.byteArr) time.sleep_ms(1) # sleep msec