Ejemplo n.º 1
0
    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
Ejemplo n.º 2
0
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)
Ejemplo n.º 3
0
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)
Ejemplo n.º 4
0
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)
Ejemplo n.º 5
0
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
Ejemplo n.º 6
0
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
Ejemplo n.º 7
0
# 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)
Ejemplo n.º 8
0
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)
Ejemplo n.º 9
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)
Ejemplo n.º 10
0
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()
Ejemplo n.º 11
0
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)
Ejemplo n.º 12
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)
Ejemplo n.º 13
0
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
Ejemplo n.º 14
0
# 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)
Ejemplo n.º 15
0
          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
Ejemplo n.º 16
0
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()
Ejemplo n.º 17
0
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
Ejemplo n.º 18
0
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("")
Ejemplo n.º 19
0
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
Ejemplo n.º 20
0
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
Ejemplo n.º 22
0
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)
Ejemplo n.º 23
0
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)
'''
Ejemplo n.º 24
0
#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
Ejemplo n.º 25
0
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)
Ejemplo n.º 27
0
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)
Ejemplo n.º 28
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)))
Ejemplo n.º 29
0
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)
Ejemplo n.º 30
0
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()
Ejemplo n.º 31
0
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