示例#1
0
class spiram:
  def __init__(self):
    self.led = Pin(5, Pin.OUT)
    self.led.off()
    self.rom="48.rom"
    #self.rom="opense.rom"
    #self.rom="/sd/zxspectrum/48.rom"
    self.spi_channel = const(2)
    self.init_pinout_sd()
    self.spi_freq = const(4000000)
    self.hwspi=SPI(self.spi_channel, baudrate=self.spi_freq, polarity=0, phase=0, bits=8, firstbit=SPI.MSB, sck=Pin(self.gpio_sck), mosi=Pin(self.gpio_mosi), miso=Pin(self.gpio_miso))

  @micropython.viper
  def init_pinout_sd(self):
    self.gpio_sck  = const(16)
    self.gpio_mosi = const(4)
    self.gpio_miso = const(12)

  # read from file -> write to SPI RAM
  def load_stream(self, filedata, addr=0, maxlen=0x10000, blocksize=1024):
    block = bytearray(blocksize)
    # Request load
    self.led.on()
    self.hwspi.write(bytearray([0,(addr >> 24) & 0xFF, (addr >> 16) & 0xFF, (addr >> 8) & 0xFF, addr & 0xFF]))
    bytes_loaded = 0
    while bytes_loaded < maxlen:
      if filedata.readinto(block):
        self.hwspi.write(block)
        bytes_loaded += blocksize
      else:
        break
    self.led.off()

  # read from SPI RAM -> write to file
  def save_stream(self, filedata, addr=0, length=1024, blocksize=1024):
    bytes_saved = 0
    block = bytearray(blocksize)
    # Request save
    self.led.on()
    self.hwspi.write(bytearray([1,(addr >> 24) & 0xFF, (addr >> 16) & 0xFF, (addr >> 8) & 0xFF, addr & 0xFF, 0]))
    while bytes_saved < length:
      self.hwspi.readinto(block)
      filedata.write(block)
      bytes_saved += len(block)
    self.led.off()

  def ctrl(self,i):
    self.led.on()
    self.hwspi.write(bytearray([0, 0xFF, 0xFF, 0xFF, 0xFF, i]))
    self.led.off()

  def cpu_halt(self):
    self.ctrl(2)

  def cpu_continue(self):
    self.ctrl(0)
示例#2
0
class spiram:
    def __init__(self):
        self.init_pinout_sd()
        self.cs = Pin(self.gpio_cs, Pin.OUT)
        self.cs.off()
        self.spi_channel = const(1)
        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))

    @micropython.viper
    def init_pinout_sd(self):
        self.gpio_cs = const(5)  # also the LED
        self.gpio_sck = const(25)  # gn[11]
        self.gpio_miso = const(26)  # gp[11]
        self.gpio_mosi = const(16)
        #self.gpio_led  = const(19)

    # read from file -> write to SPI RAM
    def load_stream(self, filedata, addr=0, blocksize=1024):
        block = bytearray(blocksize)
        self.cs.on()
        self.hwspi.write(bytearray([0x00, (addr >> 8) & 0xFF, addr & 0xFF]))
        while True:
            if filedata.readinto(block):
                self.hwspi.write(block)
            else:
                break
        self.cs.off()

    # read from SPI RAM -> write to file
    def save_stream(self, filedata, addr=0, length=1024, blocksize=1024):
        bytes_saved = 0
        block = bytearray(blocksize)
        self.cs.on()
        self.hwspi.write(
            bytearray([0x01, (addr >> 8) & 0xFF, addr & 0xFF, 0x00]))
        while bytes_saved < length:
            self.hwspi.readinto(block)
            filedata.write(block)
            bytes_saved += len(block)
        self.cs.off()
示例#3
0
class MAX31855:
    """
    Driver for the MAX31855 thermocouple amplifier.
    """
    def __init__(self, CLK, CS, DO):
        self.spi = SPI(0,
                       mode=SPI.MASTER,
                       baudrate=2000000,
                       polarity=0,
                       phase=0,
                       pins=(CLK, CS, DO))
        self.data = bytearray(4)
        self.cs = Pin(CS, mode=Pin.OUT)

    def _read(self, internal=False):

        self.cs.value(0)
        time.sleep(0.001)
        self.data = bytearray(4)
        self.spi.readinto(self.data)  #pylint: disable=no-member
        self.cs.value(1)

        if self.data[3] & 0x01:
            raise RuntimeError("thermocouple not connected")
        if self.data[3] & 0x02:
            raise RuntimeError("short circuit to ground")
        if self.data[3] & 0x04:
            raise RuntimeError("short circuit to power")
        if self.data[1] & 0x01:
            raise RuntimeError("faulty reading")
        temp, refer = struct.unpack('>hh', self.data)
        refer >>= 4
        temp >>= 2
        if internal:
            return refer
        return temp

    @property
    def temperature(self):
        """Thermocouple temperature in degrees Celsius."""
        return self._read() / 4.0

    @property
    def reference_temperature(self):
        """Internal reference temperature in degrees Celsius."""
        return self._read(True) * 0.625
示例#4
0
def f():
    cs_flash = Pin("B11", Pin.OUT) #V13: B11; V14: B12
    cs_flash.value(0)
    spi = SPI(1)
    buf = bytearray(8)
    #spi.send(b"\x9F")
    time.sleep_ms(10)
    #spi.write(b"\x9F")
    #spi.write(b"\x9F")
    spi.write(b"\x9F")
    #spi.write(b"\x05")

    #time.sleep_ms(10)
    spi.readinto(buf)
    cs_flash.value(1)
    print(buf)
    time.sleep_ms(200)
class MAX6675:
    def __init__(self, hwspi=2, cs=None, sck=None, miso=None, offset=0.0, cache_time=0):
        baudrate = 10**5
        self._offset = offset
        self._cs = Pin(cs, Pin.OUT)
        self.cache_time = cache_time
        self.last_read = 0
        self.last_read_time = 0

        if hwspi == 1 or hwspi == 2:
            # Hardware SPI Bus
            self._spi = SPI(hwspi, baudrate=baudrate, sck=Pin(sck), miso=Pin(miso))
        else:
            # Software SPI Bus
            self._spi = SPI(baudrate=baudrate, sck=Pin(sck), miso=Pin(miso))

    def get_offset(self):
        return self._offset

    def set_offset(self, offset):
        self._offset = offset

    def read_temp(self, internal=False):
        data = bytearray(2)
        self._cs.value(0)
        try:
            self._spi.readinto(data)
        finally:
            self._cs.value(1)

        if data[1] & 0x04:
            raise RuntimeError("NC") # not connected

        self.last_read_time = utime.ticks_ms()
        self.last_read = ((data[0]<<8 | data[1]) >> 3) * 0.25 + self._offset
        return self.last_read

    def get_temp(self):
        if utime.ticks_diff(utime.ticks_ms(), self.last_read_time) < self.cache_time:
            return self.last_read
        return self.read_temp()
示例#6
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
示例#7
0
class HSPIMaster:
    def __init__(self, baud=115200, noc=2):
        self.num_of_counters = noc
        self.hspi = SPI(1, baudrate=baud, polarity=0, phase=0)
        self.buf = bytearray(4 * self.num_of_counters)

        # battery level is received separately
        self.batt = bytearray(1)

    def receive_counters(self):
        print("Waiting for stm to wake up...")
        stm_halt_mark = 0xff
        self.hspi.readinto(self.batt)

        # First byte, not equal to 0xff, is battery level
        while self.batt[0] == stm_halt_mark:
            self.hspi.readinto(self.batt)

        print("STM is awake!")

        print("Reading data from STM...")

        self.hspi.readinto(self.buf, 0xff)

        print("Processing received data...")

        stm_data = []
        counter_value = 0
        i = 1
        j = 0
        while i <= self.num_of_counters:
            while j < 4 * i:
                counter_value <<= 8
                counter_value |= self.buf[j]
                j += 1
            stm_data.append(counter_value)
            counter_value = 0
            i += 1

        print("SPI data processed, info received successfully!")

        return stm_data
示例#8
0
class MAX31855:
    def __init__(self, hwspi=2, cs=None, sck=None, miso=None, offset=0.0, cache_time=0):
        """
        :param hwspi: Hardware SPI bus id
            HSPI(id=1): sck=14, mosi=13, miso=12
            VSPI(id=2): sck=18, mosi=23, miso=19
        :param cs: chip select pin
        :param sck: serial clock pin
        :param mosi: mosi pin
        :param miso: miso pin

        FOR ADAFRUIT MAX31855 BREAKOUT BOARD WIRING
        ESP32 3V3 => Sensor VDD
        ESP32 GND => Sensor GND
        ESP32 SCK => Sensor CLK
        ESP32 MISO => Sensor DO
        ESP32 any digital IO pin => Sensor CS
        """
        baudrate = 10**5
        self._offset = offset
        self._cs = Pin(cs, Pin.OUT)
        self._data = bytearray(4)
        self.cache_time = cache_time
        self.last_read = 0
        self.last_read_time = 0

        if hwspi == 1 or hwspi == 2:
            # Hardware SPI Bus
            self._spi = SPI(hwspi, baudrate=baudrate, sck=Pin(sck), miso=Pin(miso))
        else:
            # Software SPI Bus
            self._spi = SPI(baudrate=baudrate, sck=Pin(sck), miso=Pin(miso))

    def get_offset(self):
        return self._offset

    def set_offset(self, offset):
        self._offset = offset

    def read_temp(self, internal=False):
        self._cs.value(0)
        try:
            self._spi.readinto(self._data)
        finally:
            self._cs.value(1)

        if self._data[3] & 0x01:
            raise RuntimeError("NC") # not connected
        if self._data[3] & 0x02:
            raise RuntimeError("X_GND") # shortcut to GND
        if self._data[3] & 0x04:
            raise RuntimeError("X_PWR") # shortcut to power
        if self._data[1] & 0x01:
            raise RuntimeError("ERR") # faulty reading

        temp, refer = ustruct.unpack('>hh', self._data)
        refer >>= 4
        temp >>= 2
        self.last_read_time = utime.ticks_ms()
        self.last_read = refer * 0.0625 + self._offset if internal else temp * 0.25 + self._offset
        return self.last_read

    def get_temp(self):
        if utime.ticks_diff(utime.ticks_ms(), self.last_read_time) < self.cache_time:
            return self.last_read
        return self.read_temp()
示例#9
0
class RFM69:
    """Interface to a RFM69 series packet radio.  Allows simple sending and
    receiving of wireless data at supported frequencies of the radio
    (433/915mhz).

    :param busio.SPI spi: The SPI bus connected to the chip.  Ensure SCK, MOSI, and MISO are
        connected.
    :param ~digitalio.DigitalInOut cs: A DigitalInOut object connected to the chip's CS/chip select
        line.
    :param ~digitalio.DigitalInOut reset: A DigitalInOut object connected to the chip's RST/reset
        line.
    :param int frequency: The center frequency to configure for radio transmission and reception.
        Must be a frequency supported by your hardware (i.e. either 433 or 915mhz).
    :param bytes sync_word: A byte string up to 8 bytes long which represents the syncronization
        word used by received and transmitted packets. Read the datasheet for a full understanding
        of this value! However by default the library will set a value that matches the RadioHead
        Arduino library.
    :param int preamble_length: The number of bytes to pre-pend to a data packet as a preamble.
        This is by default 4 to match the RadioHead library.
    :param bytes encryption_key: A 16 byte long string that represents the AES encryption key to use
        when encrypting and decrypting packets.  Both the transmitter and receiver MUST have the
        same key value! By default no encryption key is set or used.
    :param bool high_power: Indicate if the chip is a high power variant that supports boosted
        transmission power.  The default is True as it supports the common RFM69HCW modules sold by
        Adafruit.

    .. note:: The D0/interrupt line is currently unused by this module and can remain unconnected.

    Remember this library makes a best effort at receiving packets with pure Python code.  Trying
    to receive packets too quickly will result in lost data so limit yourself to simple scenarios
    of sending and receiving single packets at a time.

    Also note this library tries to be compatible with raw RadioHead Arduino library communication.
    This means the library sets up the radio modulation to match RadioHead's default of GFSK
    encoding, 250kbit/s bitrate, and 250khz frequency deviation. To change this requires explicitly
    setting the radio's bitrate and encoding register bits. Read the datasheet and study the init
    function to see an example of this--advanced users only! Advanced RadioHead features like
    address/node specific packets or guaranteed delivery are not supported. Only simple broadcast
    of packets to all listening radios is supported. Features like addressing and guaranteed
    delivery need to be implemented at an application level.
    """

    # Global buffer to hold data sent and received with the chip.  This must be
    # at least as large as the FIFO on the chip (66 bytes)!  Keep this on the
    # class level to ensure only one copy ever exists (with the trade-off that
    # this is NOT re-entrant or thread safe code by design).
    _BUFFER = bytearray(66)

    class _RegisterBits:
        # Class to simplify access to the many configuration bits avaialable
        # on the chip's registers.  This is a subclass here instead of using
        # a higher level module to increase the efficiency of memory usage
        # (all of the instances of this bit class will share the same buffer
        # used by the parent RFM69 class instance vs. each having their own
        # buffer and taking too much memory).

        # Quirk of pylint that it requires public methods for a class.  This
        # is a decorator class in Python and by design it has no public methods.
        # Instead it uses dunder accessors like get and set below.  For some
        # reason pylint can't figure this out so disable the check.
        # pylint: disable=too-few-public-methods

        # Again pylint fails to see the true intent of this code and warns
        # against private access by calling the write and read functions below.
        # This is by design as this is an internally used class.  Disable the
        # check from pylint.
        # pylint: disable=protected-access

        def __init__(self, address, *, offset=0, bits=1):
            assert 0 <= offset <= 7
            assert 1 <= bits <= 8
            assert (offset + bits) <= 8
            self._address = address
            self._mask = 0
            for _ in range(bits):
                self._mask <<= 1
                self._mask |= 1
            self._mask <<= offset
            self._offset = offset

        def __get__(self, obj, objtype):
            reg_value = obj._read_u8(self._address)
            return (reg_value & self._mask) >> self._offset

        def __set__(self, obj, val):
            reg_value = obj._read_u8(self._address)
            reg_value &= ~self._mask
            reg_value |= (val & 0xFF) << self._offset
            obj._write_u8(self._address, reg_value)

    # Control bits from the registers of the chip:
    data_mode = _RegisterBits(_REG_DATA_MOD, offset=5, bits=2)

    modulation_type = _RegisterBits(_REG_DATA_MOD, offset=3, bits=2)

    modulation_shaping = _RegisterBits(_REG_DATA_MOD, offset=0, bits=2)

    temp_start = _RegisterBits(_REG_TEMP1, offset=3)

    temp_running = _RegisterBits(_REG_TEMP1, offset=2)

    sync_on = _RegisterBits(_REG_SYNC_CONFIG, offset=7)

    sync_size = _RegisterBits(_REG_SYNC_CONFIG, offset=3, bits=3)

    aes_on = _RegisterBits(_REG_PACKET_CONFIG2, offset=0)

    pa_0_on = _RegisterBits(_REG_PA_LEVEL, offset=7)

    pa_1_on = _RegisterBits(_REG_PA_LEVEL, offset=6)

    pa_2_on = _RegisterBits(_REG_PA_LEVEL, offset=5)

    output_power = _RegisterBits(_REG_PA_LEVEL, offset=0, bits=5)

    rx_bw_dcc_freq = _RegisterBits(_REG_RX_BW, offset=5, bits=3)

    rx_bw_mantissa = _RegisterBits(_REG_RX_BW, offset=3, bits=2)

    rx_bw_exponent = _RegisterBits(_REG_RX_BW, offset=0, bits=3)

    afc_bw_dcc_freq = _RegisterBits(_REG_AFC_BW, offset=5, bits=3)

    afc_bw_mantissa = _RegisterBits(_REG_AFC_BW, offset=3, bits=2)

    afc_bw_exponent = _RegisterBits(_REG_AFC_BW, offset=0, bits=3)

    packet_format = _RegisterBits(_REG_PACKET_CONFIG1, offset=7, bits=1)

    dc_free = _RegisterBits(_REG_PACKET_CONFIG1, offset=5, bits=2)

    crc_on = _RegisterBits(_REG_PACKET_CONFIG1, offset=4, bits=1)

    crc_auto_clear_off = _RegisterBits(_REG_PACKET_CONFIG1, offset=3, bits=1)

    address_filter = _RegisterBits(_REG_PACKET_CONFIG1, offset=1, bits=2)

    mode_ready = _RegisterBits(_REG_IRQ_FLAGS1, offset=7)

    rx_ready = _RegisterBits(_REG_IRQ_FLAGS1, offset=6)

    tx_ready = _RegisterBits(_REG_IRQ_FLAGS1, offset=5)

    dio_0_mapping = _RegisterBits(_REG_DIO_MAPPING1, offset=6, bits=2)

    packet_sent = _RegisterBits(_REG_IRQ_FLAGS2, offset=3)

    payload_ready = _RegisterBits(_REG_IRQ_FLAGS2, offset=2)

    def __init__(self, cs: int, reset: int, *, baudrate=100000):
        self._cselect = Pin(cs, Pin.OUT, value=0)
        # Device support SPI mode 0 (polarity & phase = 0) up to a max of 10mhz.
        self._device = SPI(1,
                           baudrate=baudrate,
                           polarity=0,
                           phase=0,
                           sck=Pin(5, Pin.OUT),
                           mosi=Pin(18, Pin.OUT),
                           miso=Pin(19, Pin.IN))
        # Setup reset as a digital output that's low.
        self._reset = Pin(reset, Pin.OUT, value=0)

    def init(self,
             frequency,
             *,
             sync_word=b'\x2D\xD4',
             preamble_length=4,
             encryption_key=None,
             high_power=True):
        self._tx_power = 13
        self.high_power = high_power
        # Reset the chip.
        self.reset()
        # Check the version of the chip.
        version = self._read_u8(_REG_VERSION)
        print("version: ", version)
        if version != 0x24:
            raise RuntimeError(
                'Failed to find RFM69 with expected version, check wiring!')
        # Enter idle state.
        self.idle()
        # Setup the chip in a similar way to the RadioHead RFM69 library.
        # Set FIFO TX condition to not empty and the default FIFO threshold
        # to 15.
        self._write_u8(_REG_FIFO_THRESH, 0b10001111)
        # Configure low beta off.
        self._write_u8(_REG_TEST_DAGC, 0x30)
        # Disable boost.
        self._write_u8(_REG_TEST_PA1, _TEST_PA1_NORMAL)
        self._write_u8(_REG_TEST_PA2, _TEST_PA2_NORMAL)
        # Set the syncronization word.
        self.sync_word = sync_word
        # Configure modulation for RadioHead library GFSK_Rb250Fd250 mode
        # by default.  Users with advanced knowledge can manually reconfigure
        # for any other mode (consulting the datasheet is absolutely
        # necessary!).
        self.data_mode = 0b00  # Packet mode
        self.modulation_type = 0b00  # FSK modulation
        self.modulation_shaping = 0b01  # Gaussian filter, BT=1.0
        self.bitrate = 250000  # 250kbs
        self.frequency_deviation = 250000  # 250khz
        self.rx_bw_dcc_freq = 0b111  # RxBw register = 0xE0
        self.rx_bw_mantissa = 0b00
        self.rx_bw_exponent = 0b000
        self.afc_bw_dcc_freq = 0b111  # AfcBw register = 0xE0
        self.afc_bw_mantissa = 0b00
        self.afc_bw_exponent = 0b000
        self.packet_format = 1  # Variable length.
        self.dc_free = 0b10  # Whitening
        self.crc_on = 1  # CRC enabled
        self.crc_auto_clear = 0  # Clear FIFO on CRC fail
        self.address_filtering = 0b00  # No address filtering
        # Set the preamble length.
        self.preamble_length = preamble_length
        # Set frequency.
        self.frequency_mhz = frequency
        # Set encryption key.
        self.encryption_key = encryption_key
        # Set transmit power to 13 dBm, a safe value any module supports.
        self.tx_power = 13

    def close(self):
        """ Close down SPI bus -- VERY important to run at end, otherwise will require soft reset between runs. """
        self._device.deinit()

    # pylint: disable=no-member
    # Reconsider this disable when it can be tested.
    def _read_into(self, address, buf, length=None):
        # Read a number of bytes from the specified address into the provided
        # buffer.  If length is not specified (the default) the entire buffer
        # will be filled.
        if length is None:
            length = len(buf)

        self._cselect.off()
        self._BUFFER[0] = address & 0x7F  # Strip out top bit to set 0
        # value (read).
        self._device.write(self._BUFFER[:1])  #end=1
        read_buf = bytearray(length)
        self._device.readinto(read_buf)  #end=length
        buf[:length] = read_buf
        self._cselect.on()

    def _read_u8(self, address):
        # Read a single byte from the provided address and return it.
        self._read_into(address, self._BUFFER, length=1)

        return self._BUFFER[0]

    def _write_from(self, address, buf, length=None):
        # Write a number of bytes to the provided address and taken from the
        # provided buffer.  If no length is specified (the default) the entire
        # buffer is written.
        if length is None:
            length = len(buf)

        self._cselect.off()
        self._BUFFER[0] = (address | 0x80) & 0xFF  # Set top bit to 1 to
        # indicate a write.
        self._device.write(self._BUFFER[:1])  #end=1
        self._device.write(buf[:length])  #end=length
        self._cselect.on()

    def _write_u8(self, address, val):
        # Write a byte register to the chip.  Specify the 7-bit address and the
        # 8-bit value to write to that address.
        self._cselect.off()
        self._BUFFER[0] = (address | 0x80) & 0xFF  # Set top bit to 1 to
        # indicate a write.
        self._BUFFER[1] = val & 0xFF
        self._device.write(self._BUFFER[:2])  #end=2
        self._cselect.on()

    def reset(self):
        """Perform a reset of the chip."""
        # See section 7.2.2 of the datasheet for reset description.
        self._reset.value(True)
        time.sleep(0.0001)  # 100 us
        self._reset.value(False)
        time.sleep(0.005)  # 5 ms

    def idle(self):
        """Enter idle standby mode (switching off high power amplifiers if necessary)."""
        # Like RadioHead library, turn off high power boost if enabled.
        if self._tx_power >= 18:
            self._write_u8(_REG_TEST_PA1, _TEST_PA1_NORMAL)
            self._write_u8(_REG_TEST_PA2, _TEST_PA2_NORMAL)
        self.operation_mode = STANDBY_MODE

    def sleep(self):
        """Enter sleep mode."""
        self.operation_mode = SLEEP_MODE

    def listen(self):
        """Listen for packets to be received by the chip.  Use :py:func:`receive` to listen, wait
           and retrieve packets as they're available.
        """
        # Like RadioHead library, turn off high power boost if enabled.
        if self._tx_power >= 18:
            self._write_u8(_REG_TEST_PA1, _TEST_PA1_NORMAL)
            self._write_u8(_REG_TEST_PA2, _TEST_PA2_NORMAL)
        # Enable payload ready interrupt for D0 line.
        self.dio_0_mapping = 0b01
        # Enter RX mode (will clear FIFO!).
        self.operation_mode = RX_MODE

    def transmit(self):
        """Transmit a packet which is queued in the FIFO.  This is a low level function for
           entering transmit mode and more.  For generating and transmitting a packet of data use
           :py:func:`send` instead.
        """
        # Like RadioHead library, turn on high power boost if enabled.
        if self._tx_power >= 18:
            self._write_u8(_REG_TEST_PA1, _TEST_PA1_BOOST)
            self._write_u8(_REG_TEST_PA2, _TEST_PA2_BOOST)
        # Enable packet sent interrupt for D0 line.
        self.dio_0_mapping = 0b00
        # Enter TX mode (will clear FIFO!).
        self.operation_mode = TX_MODE

    @property
    def temperature(self):
        """The internal temperature of the chip in degrees Celsius. Be warned this is not
           calibrated or very accurate.

           .. warning:: Reading this will STOP any receiving/sending that might be happening!
        """
        # Start a measurement then poll the measurement finished bit.
        self.temp_start = 1
        while self.temp_running > 0:
            pass
        # Grab the temperature value and convert it to Celsius.
        # This uses the same observed value formula from the Radiohead library.
        temp = self._read_u8(_REG_TEMP2)
        return 166.0 - temp

    @property
    def operation_mode(self):
        """The operation mode value.  Unless you're manually controlling the chip you shouldn't
           change the operation_mode with this property as other side-effects are required for
           changing logical modes--use :py:func:`idle`, :py:func:`sleep`, :py:func:`transmit`,
           :py:func:`listen` instead to signal intent for explicit logical modes.
        """
        op_mode = self._read_u8(_REG_OP_MODE)
        return (op_mode >> 2) & 0b111

    @operation_mode.setter
    def operation_mode(self, val):
        assert 0 <= val <= 4
        # Set the mode bits inside the operation mode register.
        op_mode = self._read_u8(_REG_OP_MODE)
        op_mode &= 0b11100011
        op_mode |= (val << 2)
        self._write_u8(_REG_OP_MODE, op_mode)
        # Wait for mode to change by polling interrupt bit.
        while not self.mode_ready:
            pass

    @property
    def sync_word(self):
        """The synchronization word value.  This is a byte string up to 8 bytes long (64 bits)
           which indicates the synchronization word for transmitted and received packets. Any
           received packet which does not include this sync word will be ignored. The default value
           is 0x2D, 0xD4 which matches the RadioHead RFM69 library. Setting a value of None will
           disable synchronization word matching entirely.
        """
        # Handle when sync word is disabled..
        if not self.sync_on:
            return None
        # Sync word is not disabled so read the current value.
        sync_word_length = self.sync_size + 1  # Sync word size is offset by 1
        # according to datasheet.
        sync_word = bytearray(sync_word_length)
        self._read_into(_REG_SYNC_VALUE1, sync_word)
        return sync_word

    @sync_word.setter
    def sync_word(self, val):
        # Handle disabling sync word when None value is set.
        if val is None:
            self.sync_on = 0
        else:
            # Check sync word is at most 8 bytes.
            assert 1 <= len(val) <= 8
            # Update the value, size and turn on the sync word.
            self._write_from(_REG_SYNC_VALUE1, val)
            self.sync_size = len(val) - 1  # Again sync word size is offset by
            # 1 according to datasheet.
            self.sync_on = 1

    @property
    def preamble_length(self):
        """The length of the preamble for sent and received packets, an unsigned 16-bit value.
           Received packets must match this length or they are ignored! Set to 4 to match the
           RadioHead RFM69 library.
        """
        msb = self._read_u8(_REG_PREAMBLE_MSB)
        lsb = self._read_u8(_REG_PREAMBLE_LSB)
        return ((msb << 8) | lsb) & 0xFFFF

    @preamble_length.setter
    def preamble_length(self, val):
        assert 0 <= val <= 65535
        self._write_u8(_REG_PREAMBLE_MSB, (val >> 8) & 0xFF)
        self._write_u8(_REG_PREAMBLE_LSB, val & 0xFF)

    @property
    def frequency_mhz(self):
        """The frequency of the radio in Megahertz. Only the allowed values for your radio must be
           specified (i.e. 433 vs. 915 mhz)!
        """
        # FRF register is computed from the frequency following the datasheet.
        # See section 6.2 and FRF register description.
        # Read bytes of FRF register and assemble into a 24-bit unsigned value.
        msb = self._read_u8(_REG_FRF_MSB)
        mid = self._read_u8(_REG_FRF_MID)
        lsb = self._read_u8(_REG_FRF_LSB)
        frf = ((msb << 16) | (mid << 8) | lsb) & 0xFFFFFF
        frequency = (frf * _FSTEP) / 1000000.0
        return frequency

    @frequency_mhz.setter
    def frequency_mhz(self, val):
        assert 290 <= val <= 1020
        # Calculate FRF register 24-bit value using section 6.2 of the datasheet.
        frf = int((val * 1000000.0) / _FSTEP) & 0xFFFFFF
        # Extract byte values and update registers.
        msb = frf >> 16
        mid = (frf >> 8) & 0xFF
        lsb = frf & 0xFF
        self._write_u8(_REG_FRF_MSB, msb)
        self._write_u8(_REG_FRF_MID, mid)
        self._write_u8(_REG_FRF_LSB, lsb)

    @property
    def encryption_key(self):
        """The AES encryption key used to encrypt and decrypt packets by the chip. This can be set
           to None to disable encryption (the default), otherwise it must be a 16 byte long byte
           string which defines the key (both the transmitter and receiver must use the same key
           value).
        """
        # Handle if encryption is disabled.
        if self.aes_on == 0:
            return None
        # Encryption is enabled so read the key and return it.
        key = bytearray(16)
        self._read_into(_REG_AES_KEY1, key)
        return key

    @encryption_key.setter
    def encryption_key(self, val):
        # Handle if unsetting the encryption key (None value).
        if val is None:
            self.aes_on = 0
        else:
            # Set the encryption key and enable encryption.
            assert len(val) == 16
            self._write_from(_REG_AES_KEY1, val)
            self.aes_on = 1

    @property
    def tx_power(self):
        """The transmit power in dBm. Can be set to a value from -2 to 20 for high power devices
           (RFM69HCW, high_power=True) or -18 to 13 for low power devices. Only integer power
           levels are actually set (i.e. 12.5 will result in a value of 12 dBm).
        """
        # Follow table 10 truth table from the datasheet for determining power
        # level from the individual PA level bits and output power register.
        pa0 = self.pa_0_on
        pa1 = self.pa_1_on
        pa2 = self.pa_2_on
        if pa0 and not pa1 and not pa2:
            # -18 to 13 dBm range
            return -18 + self.output_power
        if not pa0 and pa1 and not pa2:
            # -2 to 13 dBm range
            return -18 + self.output_power
        if not pa0 and pa1 and pa2 and not self.high_power:
            # 2 to 17 dBm range
            return -14 + self.output_power
        if not pa0 and pa1 and pa2 and self.high_power:
            # 5 to 20 dBm range
            return -11 + self.output_power
        raise RuntimeError('Power amplifiers in unknown state!')

    @tx_power.setter
    def tx_power(self, val):
        val = int(val)
        # Determine power amplifier and output power values depending on
        # high power state and requested power.
        pa_0_on = 0
        pa_1_on = 0
        pa_2_on = 0
        output_power = 0
        if self.high_power:
            # Handle high power mode.
            assert -2 <= val <= 20
            if val <= 13:
                pa_1_on = 1
                output_power = val + 18
            elif 13 < val <= 17:
                pa_1_on = 1
                pa_2_on = 1
                output_power = val + 14
            else:  # power >= 18 dBm
                # Note this also needs PA boost enabled separately!
                pa_1_on = 1
                pa_2_on = 1
                output_power = val + 11
        else:
            # Handle non-high power mode.
            assert -18 <= val <= 13
            # Enable only power amplifier 0 and set output power.
            pa_0_on = 1
            output_power = val + 18
        # Set power amplifiers and output power as computed above.
        self.pa_0_on = pa_0_on
        self.pa_1_on = pa_1_on
        self.pa_2_on = pa_2_on
        self.output_power = output_power
        self._tx_power = val

    @property
    def rssi(self):
        """The received strength indicator (in dBm) of the last received message."""
        # Read RSSI register and convert to value using formula in datasheet.
        return -self._read_u8(_REG_RSSI_VALUE) / 2.0

    @property
    def bitrate(self):
        """The modulation bitrate in bits/second (or chip rate if Manchester encoding is enabled).
           Can be a value from ~489 to 32mbit/s, but see the datasheet for the exact supported
           values.
        """
        msb = self._read_u8(_REG_BITRATE_MSB)
        lsb = self._read_u8(_REG_BITRATE_LSB)
        return _FXOSC / ((msb << 8) | lsb)

    @bitrate.setter
    def bitrate(self, val):
        assert (_FXOSC / 65535) <= val <= 32000000.0
        # Round up to the next closest bit-rate value with addition of 0.5.
        bitrate = int((_FXOSC / val) + 0.5) & 0xFFFF
        self._write_u8(_REG_BITRATE_MSB, bitrate >> 8)
        self._write_u8(_REG_BITRATE_LSB, bitrate & 0xFF)

    @property
    def frequency_deviation(self):
        """The frequency deviation in Hertz."""
        msb = self._read_u8(_REG_FDEV_MSB)
        lsb = self._read_u8(_REG_FDEV_LSB)
        return _FSTEP * ((msb << 8) | lsb)

    @frequency_deviation.setter
    def frequency_deviation(self, val):
        assert 0 <= val <= (_FSTEP * 16383)  # fdev is a 14-bit unsigned value
        # Round up to the next closest integer value with addition of 0.5.
        fdev = int((val / _FSTEP) + 0.5) & 0x3FFF
        self._write_u8(_REG_FDEV_MSB, fdev >> 8)
        self._write_u8(_REG_FDEV_LSB, fdev & 0xFF)

    def send(self,
             data,
             timeout=2.,
             tx_header=(_RH_BROADCAST_ADDRESS, _RH_BROADCAST_ADDRESS, 0, 0)):
        """Send a string of data using the transmitter.
           You can only send 60 bytes at a time
           (limited by chip's FIFO size and appended headers).
           This appends a 4 byte header to be compatible with the RadioHead library.
           The tx_header defaults to using the Broadcast addresses. It may be overidden
           by specifying a 4-tuple of bytes containing (To,From,ID,Flags)
           The timeout is just to prevent a hang (arbitrarily set to 2 seconds)
        """
        # Disable pylint warning to not use length as a check for zero.
        # This is a puzzling warning as the below code is clearly the most
        # efficient and proper way to ensure a precondition that the provided
        # buffer be within an expected range of bounds.  Disable this check.
        # pylint: disable=len-as-condition
        assert 0 < len(data) <= 60
        assert len(
            tx_header) == 4, "tx header must be 4-tuple (To,From,ID,Flags)"
        # pylint: enable=len-as-condition
        self.idle()  # Stop receiving to clear FIFO and keep it clear.
        # Fill the FIFO with a packet to send.
        self._cselect.off()
        self._BUFFER[0] = (_REG_FIFO | 0x80)  # Set top bit to 1 to
        # indicate a write.
        self._BUFFER[1] = (len(data) + 4) & 0xFF
        # Add 4 bytes of headers to match RadioHead library.
        # Just use the defaults for global broadcast to all receivers
        # for now.
        self._BUFFER[2] = tx_header[0]  # Header: To
        self._BUFFER[3] = tx_header[1]  # Header: From
        self._BUFFER[4] = tx_header[2]  # Header: Id
        self._BUFFER[5] = tx_header[3]  # Header: Flags
        self._device.write(self._BUFFER[:6])  #end=6
        # Now send the payload.
        self._device.write(data)
        self._cselect.on()
        # Turn on transmit mode to send out the packet.
        self.transmit()
        # Wait for packet sent interrupt with explicit polling (not ideal but
        # best that can be done right now without interrupts).
        start = time.ticks_ms()
        timed_out = False
        while not timed_out and not self.packet_sent:
            if (time.ticks_diff(time.ticks_ms(), start)) / 10.**3 >= timeout:
                timed_out = True
        # Go back to idle mode after transmit.
        self.idle()
        if timed_out:
            raise RuntimeError('Timeout during packet send')

    def receive(self,
                timeout=0.5,
                keep_listening=True,
                with_header=False,
                rx_filter=_RH_BROADCAST_ADDRESS):
        """Wait to receive a packet from the receiver. Will wait for up to timeout_s amount of
           seconds for a packet to be received and decoded. If a packet is found the payload bytes
           are returned, otherwise None is returned (which indicates the timeout elapsed with no
           reception).
           If keep_listening is True (the default) the chip will immediately enter listening mode
           after reception of a packet, otherwise it will fall back to idle mode and ignore any
           future reception.
           A 4-byte header must be prepended to the data for compatibilty with the
           RadioHead library.
           The header consists of a 4 bytes (To,From,ID,Flags). The default setting will accept
           any  incomming packet and strip the header before returning the packet to the caller.
           If with_header is True then the 4 byte header will be returned with the packet.
           The payload then begins at packet[4].
           rx_fliter may be set to reject any "non-broadcast" packets that do not contain the
           specfied "To" value in the header.
           if rx_filter is set to 0xff (_RH_BROADCAST_ADDRESS) or if the  "To" field (packet[[0])
           is equal to 0xff then the packet will be accepted and returned to the caller.
           If rx_filter is not 0xff and packet[0] does not match rx_filter then
           the packet is ignored and None is returned.
        """
        # Make sure we are listening for packets.
        self.listen()
        # Wait for the payload_ready interrupt.  This is not ideal and will
        # surely miss or overflow the FIFO when packets aren't read fast
        # enough, however it's the best that can be done from Python without
        # interrupt supports.
        start = time.ticks_ms()
        timed_out = False
        while not timed_out and not self.payload_ready:
            if (time.ticks_diff(time.ticks_ms(), start)) / 10.**3 >= timeout:
                timed_out = True
        # Payload ready is set, a packet is in the FIFO.
        packet = None
        # Enter idle mode to stop receiving other packets.
        self.idle()
        if timed_out:
            return None
        # Read the data from the FIFO.
        self._cselect.off()
        self._BUFFER[0] = _REG_FIFO & 0x7F  # Strip out top bit to set 0
        # value (read).
        self._device.write(self._BUFFER[:1])  #end=1
        # Read the length of the FIFO.
        read_buf = bytearray(1)
        self._device.readinto(read_buf)  # end=1
        self._BUFFER[:1] = read_buf
        fifo_length = self._BUFFER[0]
        # Handle if the received packet is too small to include the 4 byte
        # RadioHead header--reject this packet and ignore it.
        if fifo_length < 4:
            # Invalid packet, ignore it.  However finish reading the FIFO
            # to clear the packet.
            read_buf = bytearray(fifo_length)
            self._device.readinto(read_buf)  #end=fifo_length
            self._BUFFER[:fifo_length] = read_buf
            packet = None
        else:
            packet = bytearray(fifo_length)
            self._device.readinto(packet)
            if (rx_filter != _RH_BROADCAST_ADDRESS
                    and packet[0] != _RH_BROADCAST_ADDRESS
                    and packet[0] != rx_filter):
                packet = None
            elif not with_header:  # skip the header if not wanted
                packet = packet[4:]
        self._cselect.on()

        # Listen again if necessary and return the result packet.
        if keep_listening:
            self.listen()
        return packet
示例#10
0
from machine import SPI
from micropython import const

sck=Pin(18)
mosi=Pin(32)
miso=Pin(21)
cs = Pin(12, Pin.OUT)
reset=Pin(13)
cs.value(1)

address=const(0x01) #-- seems constant across widgets

spi=SPI(2,baudrate=12500000,sck=sck,mosi=mosi,miso=miso)

buf= bytearray(10)

length=1

buf[0]=address & 0x7F

cs.value(0)

spi.write(buf)
spi.readinto(buf)
cs.value(1)


print(buf[0])


示例#11
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)
示例#12
0
class Ads1299:
    def __init__(self, pinconfig=PINCONFIG, fs=250):
        self.virgin = True
        self.mode = EEGACQUIRE

        # test
        self.co = 0
        self.data_num = 0
        self.max_data_num = 500

        data_ready = Pin(pinconfig['drdy'], Pin.IN,
                         Pin.PULL_UP)  # 内部上拉,默认高电平,捕捉下降沿
        data_ready.irq(trigger=Pin.IRQ_FALLING,
                       handler=self._data_ready_callback)

        self.rst = Pin(pinconfig['reset'], Pin.OUT)
        self.cs = Pin(pinconfig['cs'], Pin.OUT)  #使能
        self.pown = Pin(pinconfig['pown'], Pin.OUT, value=0)  #默认低功耗设置

        # SPI init
        self.spi = SPI(1,
                       baudrate=4000000,
                       polarity=0,
                       phase=1,
                       firstbit=SPI.MSB,
                       sck=Pin(pinconfig['sclk']),
                       mosi=Pin(pinconfig['mosi']),
                       miso=Pin(pinconfig['miso']))

        self.buffer = bytearray(0)
        self.status = bytearray(3)

        if fs == 2000:
            self.samplingrate = SAMPLE_RATE_2K
        elif fs == 1000:
            self.samplingrate = SAMPLE_RATE_1K
        elif fs == 500:
            self.samplingrate = SAMPLE_RATE_500
        elif fs == 250:
            self.samplingrate = SAMPLE_RATE_250
        else:
            self.samplingrate = SAMPLE_RATE_250

    def _data_ready_callback(self, pin):
        # 数据格式
        # 24bits status + ch1(24bits) + ch2(24bits) + ...
        # status: 1100 + LOFF_STATP(8 bits) + LOFF_STATN(8 bits) + bits[4:7] of GPIO
        buf = bytearray(27)
        self.spi.readinto(buf)
        self.status = buf[:3]
        databuffer = buf[3:]
        self.buffer += databuffer

        # 超时未读取,自动清空
        self.data_num += 1
        if self.data_num > self.max_data_num:
            self.buffer = bytearray(0)
            self.data_num = 0

        self.co += 1

    def read_data(self):  # output interface
        # int24高位先行
        buf = self.buffer
        self.buffer = bytearray(0)
        self.data_num = 0
        return buf

    def write_cmd(self, data):
        self.spi.write(BYTESMAP[data])
        utime.sleep_us(20)

    def channel_setting(self,
                        power_down=False,
                        gain_set=ADS_GAIN24,
                        input_type=ADSINPUT_NORMAL,
                        SRB2_set=True):
        # 这里从N-side输入信号,将所有P-side连接至srb2
        setting = 0x00
        if power_down: setting |= 0x80
        setting |= gain_set
        setting |= input_type
        if SRB2_set: setting |= 0x08

        for i in range(8):  #对8个通道逐个进行增益和输入模式配置
            self.write_register(CH1SET + i, setting)

    def write_register(self, address, value):
        # 写寄存器步骤
        # stp1:写寄存器地址
        # stp2:写n,代表下面要写入n+1个寄存器值
        # stp3:连续写入n+1个寄存器值
        addr = address + 0x40
        self.spi.write(BYTESMAP[addr])
        self.spi.write(BYTESMAP[0])  #默认只写入一个值
        self.spi.write(BYTESMAP[value])
        utime.sleep_us(20)

    def start_loff_detect(self):
        self.mode = LOFFDETECT
        self.reset()

        # 进入配置模式
        self.write_cmd(SDATAC)

        # 配置寄存器1:菊花链、时钟、采样率等
        self.write_register(
            CONFIG1, ADS1299_CONFIG1_DAISY_NOT | self.samplingrate | CLOCK_EN)

        # 配置通道:增益、输入模式等
        self.channel_setting()

        # 配置寄存器LOFF
        # bit3:2 ILEAD_OFF: 0b00->6nA 0b01->24nA 0b10->6uA 0b11->24uA
        # bit1:0 FLEAD_OFF: 0b00->DC 0b01->AC 7.8Hz 0b10->AC 31.2Hz 0b11->AC fdr/4
        # 配置为6nA 31.2Hz激励
        self.write_register(LOFF, 0x02)

        # 配置N-side通道连接上测试电流源
        self.write_register(LOFF_SENSP, 0x00)
        self.write_register(LOFF_SENSN, 0xFF)

        # 激励电流的方向,翻转,将AVDD连接到N-side,从N-side灌入电流
        self.write_register(LOFF_FLIP, 0xFF)

        # config4 开启lead-off comparators
        self.write_register(CONFIG4, 0x02)

        # 完成配置,启动
        self.write_cmd(START)
        self.write_cmd(RDATAC)  #进入读数据模式
        utime.sleep_ms(25)

    def reset(self):
        self.cs.value(0)  #片选
        self.pown.value(1)  #唤醒
        self.rst.value(1)  #复位,所有寄存器的设置
        utime.sleep_us(50)
        self.rst.value(0)
        utime.sleep_us(100)
        self.rst.value(1)

        if self.virgin:  #初次,等待vcap1充电
            utime.sleep_ms(500)
            self.virgin = False
        else:
            utime.sleep_us(100)

    def start_data_acquire(self):
        self.mode = EEGACQUIRE
        self.reset()

        # 进入配置模式
        self.write_cmd(SDATAC)

        # 配置寄存器1:菊花链、时钟、采样率等
        # 一旦启用CLOCK_EN,将可以从clk观察到2.048Mhz波形输出,可用于调试
        #self.write_register(CONFIG1,ADS1299_CONFIG1_DAISY_NOT | SAMPLE_RATE_8K | CLOCK_EN)
        self.write_register(
            CONFIG1, ADS1299_CONFIG1_DAISY_NOT | self.samplingrate | CLOCK_EN)
        #self.write_register(CONFIG1,ADS1299_CONFIG1_DAISY_NOT | self.samplingrate)

        # 配置寄存器2:测试信号
        # 相当于内部产生一个信号,便于从后端观察系统运行情况

        # 配置通道:增益、输入模式等
        self.channel_setting()

        self.write_register(BIAS_SENSP, 0xff)
        self.write_register(BIAS_SENSN, 0xff)

        # 配置寄存器3:配置内部/外部参考以及bias等
        self.write_register(CONFIG3, 0b11101100)

        # 完成配置,启动
        self.write_cmd(START)
        self.write_cmd(RDATAC)  #进入读数据模式
        utime.sleep_ms(25)

    def stop(self):
        self.write_cmd(SDATAC)
        self.write_cmd(STOP)  #停止
        self.pown.value(0)  #进入省电模式
        self.buffer = bytearray(0)  #将可能残余的数据清除
示例#13
0
文件: slides.py 项目: emard/hdl4fpga
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)
示例#14
0
class CAN:
    """Class providing interface with the mcp2515 module.

    Must be initiated with a pin objects for
    miso, mosi, sck and select chip pin.
    """

    def __init__(self, miso, mosi, sck, cs):
        self.spi = SPI(1)
        self.miso = miso
        self.mosi = mosi
        self.sck = sck
        self.cs = cs
        self.msgbuf = bytearray(13)  # for loading and retrieving messages
        self.buf8 = bytearray(1)  # for reading adresses

    # SPI helper methods
    def spi_start(self):
        self.spi.init(
            baudrate=int(10E6),
            polarity=0,
            phase=0,
            bits=8,
            firstbit=SPI.MSB,
            miso=self.miso,
            mosi=self.mosi,
            sck=self.sck
        )

        self.cs.off()

    def spi_end(self):
        self.cs.on()
        self.spi.deinit()

    # SPI CONTOLS

    def write_register(self, address, data):
        self.spi_start()

        self.spi.write(pack("<B", mcp_spi.WRITE))
        self.spi.write(pack("<B", address))
        for byte in data:
            self.spi.write(pack("<B", byte))

        self.spi_end()

    def bitmodify_register(self, address, mask, data):
        self.spi_start()

        self.spi.write(pack("<B", mcp_spi.BIT_MODIFY))
        self.spi.write(pack("<B", address))
        self.spi.write(pack("<B", mask))
        self.spi.write(pack("<B", data))

        self.spi_end()

    def read_register(self, address):
        self.spi_start()

        self.spi.write(pack("<B", mcp_spi.READ))
        self.spi.write(pack("<B", address))
        self.spi.readinto(self.buf8)

        self.spi_end()

    def reset(self):
        self.spi_start()

        self.spi.write(pack("<B", mcp_spi.RESET))

        self.spi_end()

    def read_rx_buffer(self, buffer=0):
        """fetch all data from a recieve buffer.
        First 5 bytes are info, adn last 8 bytes are data

        buffer: rx buffer to read from: 0 or 1
        """

        self.spi_start()

        if buffer == 0:
            self.spi.write(pack("<B", mcp_spi.READ_RX_BUFFER_0))
        if buffer == 1:
            self.spi.write(pack("<B", mcp_spi.READ_RX_BUFFER_1))

        self.spi.readinto(self.msgbuf)
        self.spi_end()

    def load_tx_buffer(self, buffer):
        """Write provided load whatever is in msgbuf to selected TX buffer"""

        self.spi_start()

        if buffer == 0:
            self.spi.write(pack("<B", mcp_spi.WRITE_TX_BUFFER_0))

        if buffer == 1:
            self.spi.write(pack("<B", mcp_spi.WRITE_TX_BUFFER_1))

        if buffer == 2:
            self.spi.write(pack("<B", mcp_spi.WRITE_TX_BUFFER_2))

        self.spi.write(self.msgbuf)
        self.spi_end()

    def request_to_send(self, buffer=0):

        self.spi_start()

        if buffer == 0:
            self.spi.write(pack("<B", mcp_spi.RTS0))

        if buffer == 1:
            self.spi.write(pack("<B", mcp_spi.RTS1))

        if buffer == 2:
            self.spi.write(pack("<B", mcp_spi.RTS2))

        self.spi_end()

    def read_status(self):
        """Fetch recieve and transmit status and flags"""
        self.spi_start()

        self.spi.write(pack("<B", mcp_spi.READ_STATUS))
        self.spi.readinto(self.buf8)
        data = self.buf8[0]

        self.spi_end()

        status = {
            "RX0IF": data & 0x01,
            "RX1IF": data >> 1 & 0x01,
            "TX0REQ": data >> 2 & 0x01,
            "TX0IF": data >> 3 & 0x01,
            "TX1REQ": data >> 4 & 0x01,
            "TX1IF": data >> 5 & 0x01,
            "TX2REQ": data >> 6 & 0x01,
            "TX2IF": data >> 7 & 0x01,
        }

        return status

    def rx_status(self):

        self.spi_start()

        self.spi.write(pack("<B", mcp_spi.RX_STATUS))
        self.spi.readinto(self.buf8)
        data = self.buf8[0]

        self.spi_end()

        status = {
            "filter_match": data & 0x07,
            "RTR": data >> 3 & 0x01,
            "extendedID": data >> 4 & 0x01,
            "RXB0": data >> 6 & 0x01,
            "RXB1": data >> 7 & 0x01
        }

        return status
    # SET MODES ----------------------------

    def get_opmod(self):
        self.read_register(address.CANSTAT)
        opmod = self.buf8[0] >> 5

        return opmod

    # Set OPMOD on CANCTRL and check that the mode is sucessfuly set
    def set_opmod(self, mode):
        import time

        cur_mode = self.get_opmod()
        if cur_mode == mode >> 5:
            print("mode already set")
            return
        else:
            i = 0
            while i < 10:  # try to set mode 10 times
                self.bitmodify_register(address.CANCTRL, op_mode.op_mask, mode)
                time.sleep_ms(100)

                cur_mode = self.get_opmod()
                if cur_mode == mode >> 5:
                    print("mode sucessfully set")
                    return
                else:
                    print("retrying setting mode...")
                    i += 1
            print("Failed setting mode")
            return

    # initiate CAN communication
    def init(
            self,
            bit_rate=500E3,
            clock_freq=8E6,
            SJW=1,
            BTLMODE=1,
            SAM=0,
            PRESEG=0,
            PHSEG1=2,
            PHSEG2=2):
        """
        Initiate the can controller with selected baudrate.
        bit_rate: CAN bus bit rate
        clock_freq: Can module occilator frequency
        SJW: synchronization Jump Width: 1 to 4, default(1)
        BTLMODE: : PS2 Bit Time Length bit: 0 or 1, default(1)
        SAM: Sample Point Configuration bit: 0 or 1, default(0)
        PHSEG1: lenght of PS1, default(7)
        PHSEG2: lenght of PS2, default(2) (only valid when BTLMODE=1)
        PRESEG: lengh of propagation segment: default(2)

        """
        import time

        self.reset()
        time.sleep_ms(10)

        print("Entering configuration mode...")
        self.set_opmod(op_mode.configuration)

        print("calulating BRP and setting configuration registers 1, 2 and 3")
        self._set_timing(bit_rate, clock_freq, SJW, BTLMODE,
                         SAM, PRESEG, PHSEG1, PHSEG2)

        print("Entering normal mode...")
        self.set_opmod(op_mode.normal)

    def _set_timing(
            self,
            bit_rate,
            clock_freq,
            SJW,
            BTLMODE,
            SAM,
            PRESEG,
            PHSEG1,
            PHSEG2):
        """
        SJW: synchronization Jump Width: 1 to 4, default(1)
        BTLMODE: : PS2 Bit Time Length bit: 0 or 1, default(0)
        SAM: Sample Point Configuration bit: 0 or 1, default(1)
        PHSEG1: lenght of PS1, default(7)
        PHSEG2: lenght of PS2, default(6) (only valid when BTLMODE=1)
        PRESEG: lengh of propagation segment: default(2)
        """

        # Calculate bit time and time quantum
        bit_time = 1 / bit_rate
        tq = bit_time / 16

        # Calculate Baud rate prescaler
        BRP = int((tq * clock_freq) / 2 - 1)
        # assert BRP % 1 > 0, "warning, bit-rate and
        # clock-frequency is not compatible"

        # Set configuration register 1
        SJW = SJW - 1  # length of 1 is 0 etc.
        assert len(bin(SJW)) - 2 <= 2, "SJW must be 1 to 4"
        assert len(bin(BRP)) - 2 <= 5, "BRP must be under 31"

        self.bitmodify_register(address.CNF1, 0xc0, SJW << 6)
        self.bitmodify_register(address.CNF1, 0x3f, BRP)

        # Set configuration register 2
        assert len(bin(BTLMODE)) - 2 <= 1, "BTLMODE must be 0 or 1"
        assert len(bin(SAM)) - 2 <= 1, "SAM must be 0 or 1"
        assert len(bin(PHSEG1)) - 2 <= 3, "PHSEG1 must be 0 to 7"
        assert len(bin(PRESEG)) - 2 <= 3, "PRESEG must be 0 to 7"

        self.bitmodify_register(address.CNF2, 0x80, BTLMODE << 7)
        self.bitmodify_register(address.CNF2, 0x40, SAM << 6)
        self.bitmodify_register(address.CNF2, 0x38, PHSEG1 << 3)
        self.bitmodify_register(address.CNF2, 0x07, PRESEG)

        # Set configuration register 3
        assert len(bin(PHSEG2)) - 2 <= 3, "PHSEG2 must be 0 to 7"

        self.bitmodify_register(address.CNF3, 0x07, PHSEG2)

    # Filter and masks (currently not working)

    def set_filter(self, id=0x000, filter=0, extendedID=False, clear=False):
        self.set_opmod(op_mode.configuration)

        address = [0x00, 0x04, 0x08, 0x10, 0x14, 0x18]

        if filter <= 1:
            mask_address = 0x20
            ctrl_address = 0x60
        else:
            mask_address = 0x24
            ctrl_address = 0x70

        self._prepare_id(id, extendedID)

        self.write_register(address=address[filter], data=self.msgbuf[0:4])
        self.write_register(address=mask_address, data=[
                            0xff, 0xff, 0x00, 0x00])

        self.bitmodify_register(ctrl_address, 0x60, 0x00)  # activate filtering

        if clear:
            self.bitmodify_register(0x60, 0x60, 0xff)  # clear filtering
            self.bitmodify_register(0x70, 0x60, 0xff)  # clear filtering

        self.set_opmod(op_mode.normal)

    def _prepare_id(self, id, ext):
        info = [0, 0, 0, 0]
        if ext:
            id = id & 0x1fffffff
            info[3] = id & 0xff
            info[2] = id >> 8
            id = id >> 16
            info[1] = id & 0x03
            info[1] += (id & 0x1c) << 3
            info[1] |= 0x08
            info[0] = id >> 5
        else:
            id = id & 0x7ff
            info[0] = id >> 3
            info[1] = (id & 0x07) << 5
            info[2] = 0
            info[3] = 0

        pack_into("<4B", self.msgbuf, 0, *info)

    # read messages
    def read_message(self):
        """returns a dictionary containing message info
        id: message id
        rtr: remote transmit request
        extended: extended id
        data_lenght: number of bytes recieved
        data: list of bytes"""

        # fetch recieve status
        status = self.rx_status()
        if status["RXB0"] == 1:
            self.read_rx_buffer(buffer=0)
        elif status["RXB1"] == 1:
            self.read_rx_buffer(buffer=1)
        else:
            return False  # exit if no buffer is full

        id = self.msgbuf[0] << 3 | self.msgbuf[1] >> 5
        if status["extendedID"]:
            id = id << 8 | self.msgbuf[2]
            id = id << 8 | self.msgbuf[3]

        data_length = self.msgbuf[4] & 0x0f

        if data_length > 0:
            data = list(self.msgbuf[5:5+data_length])
        else:
            data = list()

        message = {
            "id": id,
            "RTR": status["RTR"],
            "extendedID": status["extendedID"],
            "data_length": data_length,
            "data": data
        }
        return message

    # Write message
    def write_message(self, id, data, rtr=0, extendedID=False):
        """Prepares a message to be sent on available trasmission buffer

        id: a standar or extended message id
        message: list of bytes max = 8
        rtr: remote transmission request, if 1, no data is sent, default(0)
        extendedID: Set to True if message ID is extended, else Fales
        """
        data_length = len(data)

        self._prepare_id(id, extendedID)

        if rtr:
            dlc = data_length | 0x40
        else:
            dlc = data_length

        self.msgbuf[4] = dlc
        self.msgbuf[5:5+data_length] = bytearray(data)

        status = self.read_status()

        if status["TX0REQ"] == 0:
            self.load_tx_buffer(buffer=0)
            self.request_to_send(buffer=0)
            TXIF = 0x04
            self.bitmodify_register(0x2c, TXIF, 0x00)  # clear interupt flag

        elif status["TX1REQ"] == 0:
            self.load_tx_buffer(buffer=1)
            self.request_to_send(buffer=1)
            TXIF = 0x10
            self.bitmodify_register(0x2c, TXIF, 0x00)  # clear interupt flag

        elif status["TX2REQ"] == 0:
            self.load_tx_buffer(buffer=2)
            self.request_to_send(buffer=2)
            TXIF = 0x20
            self.bitmodify_register(0x2c, TXIF, 0x00)  # clear interupt flag

        else:
            return  # no transmit buffers are available

    # quick function to check for incoming messages
    def message_available(self):
        status = self.rx_status()
        if status["RXB0"] == 1 or status["RXB1"] == 1:
            return True
        else:
            return False

    # enableing and clearing interrupts -----------------
    def enable_interrupts(
            self,
            message_error=False,
            wake_up=False,
            errors=False,
            tx0_empty=False,
            tx1_empty=False,
            tx2_empty=False,
            rx0_full=False,
            rx1_full=False):
        """Enables interrupt conditions that will activate the INT pin.

        Note: errors must be cleared by MCU.
        use clear_message_error(), clear_wake_up()
        and clear_errors() to clear these flags

        tx_empty and rx_full are automatically cleared
         when reading or writing messages

        """

        if message_error:
            self.bitmodify_register(0x2b, 0x80, 0xff)

        if wake_up:
            self.bitmodify_register(0x2b, 0x40, 0xff)

        if errors:
            self.bitmodify_register(0x2b, 0x20, 0xff)

        if tx0_empty:
            self.bitmodify_register(0x2b, 0x04, 0xff)
        if tx1_empty:
            self.bitmodify_register(0x2b, 0x08, 0xff)
        if tx2_empty:
            self.bitmodify_register(0x2b, 0x10, 0xff)

        if rx0_full:
            self.bitmodify_register(0x2b, 0x01, 0xff)
        if rx1_full:
            self.bitmodify_register(0x2b, 0x02, 0xff)

    def clear_message_error(self):
        self.bitmodify_register(0x2c, 0x80, 0x00)

    def clear_wake_up(self):
        """Clears the wake-up interrupt flag and wakes-up the device"""
        self.bitmodify_register(0x2c, 0x40, 0x00)

    def clear_error(self):
        self.bitmodify_register(0x2c, 0x20, 0x00)

    def which_interrupt(self):
        """returns information about which interrupt condition
         triggered the interrupt"""
        self.read_register(0x0e)
        i_code = self.buf8[0] >> 1 & 0x07

        codes = ["No interrupts", "Error", "Wake-up",
                 "TX0", "TX1", "TX2", "RX0", "RX1"]
        return codes[i_code]

    def abort_messages(self):
        """Request abort of all pending transmissions"""
        self.bitmodify_register(0x0f, 0x10, 0xff)
示例#15
0
print(spi)
spi = SPI(0, SPI.MASTER, baudrate=10000000, polarity=1, phase=1)
print(spi)
spi.init(baudrate=20000000, polarity=0, phase=0)
print(spi)
spi=SPI()
print(spi)
SPI(mode=SPI.MASTER)
SPI(mode=SPI.MASTER, pins=spi_pins)
SPI(id=0, mode=SPI.MASTER, polarity=0, phase=0, pins=('GP14', 'GP16', 'GP15'))
SPI(0, SPI.MASTER, polarity=0, phase=0, pins=('GP31', 'GP16', 'GP15'))

spi = SPI(0, SPI.MASTER, baudrate=10000000, polarity=0, phase=0, pins=spi_pins)
print(spi.write('123456') == 6)
buffer_r = bytearray(10)
print(spi.readinto(buffer_r) == 10)
print(spi.readinto(buffer_r, write=0x55) == 10)
read = spi.read(10)
print(len(read) == 10)
read = spi.read(10, write=0xFF)
print(len(read) == 10)
buffer_w = bytearray([1, 2, 3, 4, 5, 6, 7, 8, 9, 0])
print(spi.write_readinto(buffer_w, buffer_r) == 10)
print(buffer_w == buffer_r)

# test all polaritiy and phase combinations
spi.init(polarity=1, phase=0, pins=None)
buffer_r = bytearray(10)
spi.write_readinto(buffer_w, buffer_r)
print(buffer_w == buffer_r)
示例#16
0
文件: ecp5.py 项目: nanoant/ulx3s-bin
class ecp5:
    def init_pinout_jtag(self):
        self.gpio_tms = const(21)
        self.gpio_tck = const(18)
        self.gpio_tdi = const(23)
        self.gpio_tdo = const(19)

    # if JTAG is directed to SD card pins
    # then bus traffic can be monitored using
    # JTAG slave OLED HEX decoder:
    # https://github.com/emard/ulx3s-misc/tree/master/examples/jtag_slave/proj/ulx3s_jtag_hex_passthru_v
    #def init_pinout_sd(self):
    #  self.gpio_tms = 15
    #  self.gpio_tck = 14
    #  self.gpio_tdi = 13
    #  self.gpio_tdo = 12

    def bitbang_jtag_on(self):
        self.led = Pin(self.gpio_led, Pin.OUT)
        self.tms = Pin(self.gpio_tms, Pin.OUT)
        self.tck = Pin(self.gpio_tck, Pin.OUT)
        self.tdi = Pin(self.gpio_tdi, Pin.OUT)
        self.tdo = Pin(self.gpio_tdo, Pin.IN)

    def bitbang_jtag_off(self):
        self.led = Pin(self.gpio_led, Pin.IN)
        self.tms = Pin(self.gpio_tms, Pin.IN)
        self.tck = Pin(self.gpio_tck, Pin.IN)
        self.tdi = Pin(self.gpio_tdi, Pin.IN)
        self.tdo = Pin(self.gpio_tdo, Pin.IN)
        a = self.led.value()
        a = self.tms.value()
        a = self.tck.value()
        a = self.tdo.value()
        a = self.tdi.value()
        del self.led
        del self.tms
        del self.tck
        del self.tdi
        del self.tdo

    # initialize both hardware accelerated SPI
    # software SPI on the same pins
    def spi_jtag_on(self):
        self.hwspi = SPI(self.spi_channel,
                         baudrate=self.spi_freq,
                         polarity=1,
                         phase=1,
                         bits=8,
                         firstbit=SPI.MSB,
                         sck=Pin(self.gpio_tck),
                         mosi=Pin(self.gpio_tdi),
                         miso=Pin(self.gpio_tdo))
        self.swspi = SPI(-1,
                         baudrate=self.spi_freq,
                         polarity=1,
                         phase=1,
                         bits=8,
                         firstbit=SPI.MSB,
                         sck=Pin(self.gpio_tck),
                         mosi=Pin(self.gpio_tdi),
                         miso=Pin(self.gpio_tdo))

    def spi_jtag_off(self):
        self.hwspi.deinit()
        del self.hwspi
        self.swspi.deinit()
        del self.swspi

    def __init__(self):
        self.spi_freq = const(30000000)  # Hz JTAG clk frequency
        # -1 for JTAG over SOFT SPI slow, compatibility
        #  1 or 2 for JTAG over HARD SPI fast
        #  2 is preferred as it has default pinout wired
        self.flash_write_size = const(256)
        self.flash_erase_size = const(
            4096)  # no ESP32 memory for more at flash_loop_clever()
        flash_erase_cmd = {
            4096: 0x20,
            32768: 0x52,
            65536: 0xD8
        }  # erase commands from FLASH PDF
        self.flash_erase_cmd = flash_erase_cmd[self.flash_erase_size]
        self.spi_channel = const(2)  # -1 soft, 1:sd, 2:jtag
        self.gpio_led = const(5)
        self.gpio_dummy = const(17)
        self.progress = False
        self.init_pinout_jtag()
        #self.init_pinout_sd()

    # print bytes reverse - appears the same as in SVF file
    def print_hex_reverse(self, block, head="", tail="\n"):
        print(head, end="")
        for n in range(len(block)):
            print("%02X" % block[len(block) - n - 1], end="")
        print(tail, end="")

    @micropython.viper
    def bitreverse(self, x: int) -> int:
        y = 0
        for i in range(8):
            if (x >> (7 - i)) & 1:
                y |= (1 << i)
        return y

    @micropython.viper
    def send_tms(self, tms: int):
        if tms:
            self.tms.on()
        else:
            self.tms.off()
        self.tck.off()
        self.tck.on()

    @micropython.viper
    def send_read_data_buf(self, buf, last: int, w: ptr8):
        p = ptr8(addressof(buf))
        l = int(len(buf))
        val = 0
        self.tms.off()
        for i in range(l - 1):
            byte = 0
            val = p[i]
            for nf in range(8):
                if (val >> nf) & 1:
                    self.tdi.on()
                else:
                    self.tdi.off()
                self.tck.off()
                self.tck.on()
                if self.tdo.value():
                    byte |= 1 << nf
            if int(w):
                w[i] = byte  # write byte
        byte = 0
        val = p[l - 1]  # read last byte
        for nf in range(7):  # first 7 bits
            if (val >> nf) & 1:
                self.tdi.on()
            else:
                self.tdi.off()
            self.tck.off()
            self.tck.on()
            if self.tdo.value():
                byte |= 1 << nf
        # last bit
        if last:
            self.tms.on()
        if (val >> 7) & 1:
            self.tdi.on()
        else:
            self.tdi.off()
        self.tck.off()
        self.tck.on()
        if self.tdo.value():
            byte |= 1 << 7
        if int(w):
            w[l - 1] = byte  # write last byte

    @micropython.viper
    def send_data_byte_reverse(self, val: int, last: int, bits: int):
        self.tms.off()
        for nf in range(bits - 1):
            if (val >> (7 - nf)) & 1:
                self.tdi.on()
            else:
                self.tdi.off()
            self.tck.off()
            self.tck.on()
        if last:
            self.tms.on()
        if val & 1:
            self.tdi.on()
        else:
            self.tdi.off()
        self.tck.off()
        self.tck.on()

    # TAP to "reset" state
    @micropython.viper
    def reset_tap(self):
        for n in range(6):
            self.send_tms(1)  # -> Test Logic Reset

    # TAP should be in "idle" state
    # TAP returns to "select DR scan" state
    @micropython.viper
    def runtest_idle(self, count: int, duration_ms: int):
        leave = int(ticks_ms()) + duration_ms
        for n in range(count):
            self.send_tms(0)  # -> idle
        while int(ticks_ms()) < leave:
            self.send_tms(0)  # -> idle
        self.send_tms(1)  # -> select DR scan

    # send SIR command (bytes)
    # TAP should be in "select DR scan" state
    # TAP returns to "select DR scan" state
    @micropython.viper
    def sir(self, sir):
        self.send_tms(1)  # -> select IR scan
        self.send_tms(0)  # -> capture IR
        self.send_tms(0)  # -> shift IR
        self.send_read_data_buf(sir, 1, 0)  # -> exit 1 IR
        self.send_tms(0)  # -> pause IR
        self.send_tms(1)  # -> exit 2 IR
        self.send_tms(1)  # -> update IR
        self.send_tms(1)  # -> select DR scan

    # send SIR command (bytes)
    # TAP should be in "select DR scan" state
    # TAP returns to "select DR scan" state
    # finish with n idle cycles during minimum of ms time
    @micropython.viper
    def sir_idle(self, sir, n: int, ms: int):
        self.send_tms(1)  # -> select IR scan
        self.send_tms(0)  # -> capture IR
        self.send_tms(0)  # -> shift IR
        self.send_read_data_buf(sir, 1, 0)  # -> exit 1 IR
        self.send_tms(0)  # -> pause IR
        self.send_tms(1)  # -> exit 2 IR
        self.send_tms(1)  # -> update IR
        self.runtest_idle(n + 1, ms)  # -> select DR scan

    @micropython.viper
    def sdr(self, sdr):
        self.send_tms(0)  # -> capture DR
        self.send_tms(0)  # -> shift DR
        self.send_read_data_buf(sdr, 1, 0)
        self.send_tms(0)  # -> pause DR
        self.send_tms(1)  # -> exit 2 DR
        self.send_tms(1)  # -> update DR
        self.send_tms(1)  # -> select DR scan

    @micropython.viper
    def sdr_idle(self, sdr, n: int, ms: int):
        self.send_tms(0)  # -> capture DR
        self.send_tms(0)  # -> shift DR
        self.send_read_data_buf(sdr, 1, 0)
        self.send_tms(0)  # -> pause DR
        self.send_tms(1)  # -> exit 2 DR
        self.send_tms(1)  # -> update DR
        self.runtest_idle(n + 1, ms)  # -> select DR scan

    # sdr buffer will be overwritten with response
    @micropython.viper
    def sdr_response(self, sdr):
        self.send_tms(0)  # -> capture DR
        self.send_tms(0)  # -> shift DR
        self.send_read_data_buf(sdr, 1, addressof(sdr))
        self.send_tms(0)  # -> pause DR
        self.send_tms(1)  # -> exit 2 DR
        self.send_tms(1)  # -> update DR
        self.send_tms(1)  # -> select DR scan

    def check_response(self, response, expected, mask=0xFFFFFFFF, message=""):
        if (response & mask) != expected:
            print("0x%08X & 0x%08X != 0x%08X %s" %
                  (response, mask, expected, message))

    def idcode(self):
        self.bitbang_jtag_on()
        self.led.on()
        self.reset_tap()
        self.runtest_idle(1, 0)
        self.sir(b"\xE0")
        id_bytes = bytearray(4)
        self.sdr_response(id_bytes)
        self.led.off()
        self.bitbang_jtag_off()
        return unpack("<I", id_bytes)[0]

    # common JTAG open for both program and flash
    def common_open(self):
        self.spi_jtag_on()
        self.hwspi.init(sck=Pin(self.gpio_dummy))  # avoid TCK-glitch
        self.bitbang_jtag_on()
        self.led.on()
        self.reset_tap()
        self.runtest_idle(1, 0)
        #self.sir(b"\xE0") # read IDCODE
        #self.sdr(pack("<I",0), expected=pack("<I",0), message="IDCODE")
        self.sir(b"\x1C")  # LSC_PRELOAD: program Bscan register
        self.sdr(bytearray([0xFF for i in range(64)]))
        self.sir(b"\xC6")  # ISC ENABLE: Enable SRAM programming mode
        self.sdr_idle(b"\x00", 2, 10)
        self.sir_idle(b"\x3C", 2, 1)  # LSC_READ_STATUS
        status = bytearray(4)
        self.sdr_response(status)
        self.check_response(unpack("<I", status)[0],
                            mask=0x24040,
                            expected=0,
                            message="FAIL status")
        self.sir(b"\x0E")  # ISC_ERASE: Erase the SRAM
        self.sdr_idle(b"\x01", 2, 10)
        self.sir_idle(b"\x3C", 2, 1)  # LSC_READ_STATUS
        status = bytearray(4)
        self.sdr_response(status)
        self.check_response(unpack("<I", status)[0],
                            mask=0xB000,
                            expected=0,
                            message="FAIL status")

    # call this before sending the bitstram
    # FPGA will enter programming mode
    # after this TAP will be in "shift DR" state
    def prog_open(self):
        self.common_open()
        self.sir(b"\x46")  # LSC_INIT_ADDRESS
        self.sdr_idle(b"\x01", 2, 10)
        self.sir(b"\x7A")  # LSC_BITSTREAM_BURST
        # ---------- bitstream begin -----------
        # manually walk the TAP
        # we will be sending one long DR command
        self.send_tms(0)  # -> capture DR
        self.send_tms(0)  # -> shift DR
        # switch from bitbanging to SPI mode
        self.hwspi.init(sck=Pin(self.gpio_tck))  # 1 TCK-glitch TDI=0
        # we are lucky that format of the bitstream tolerates
        # any leading and trailing junk bits. If it weren't so,
        # HW SPI JTAG acceleration wouldn't work.
        # to upload the bitstream:
        # FAST SPI mode
        #self.hwspi.write(block)
        # SLOW bitbanging mode
        #for byte in block:
        #  self.send_data_byte_reverse(byte,0)

    # call this after uploading all of the bitstream blocks,
    # this will exit FPGA programming mode and start the bitstream
    def prog_close(self):
        # switch from hardware SPI to bitbanging
        self.hwspi.init(sck=Pin(self.gpio_dummy))  # avoid TCK-glitch
        self.bitbang_jtag_on()  # 1 TCK-glitch
        self.send_tms(1)  # -> exit 1 DR
        self.send_tms(0)  # -> pause DR
        self.send_tms(1)  # -> exit 2 DR
        self.send_tms(1)  # -> update DR
        #self.send_tms(0) # -> idle, disabled here as runtest_idle does the same
        self.runtest_idle(100, 10)
        # ---------- bitstream end -----------
        self.sir_idle(b"\xC0", 2, 1)  # read usercode
        usercode = bytearray(4)
        self.sdr_response(usercode)
        self.check_response(unpack("<I", usercode)[0],
                            expected=0,
                            message="FAIL usercode")
        self.sir_idle(b"\x26", 2, 200)  # ISC DISABLE
        self.sir_idle(b"\xFF", 2, 1)  # BYPASS
        self.sir(b"\x3C")  # LSC_READ_STATUS
        status = bytearray(4)
        self.sdr_response(status)
        status = unpack("<I", status)[0]
        self.check_response(status,
                            mask=0x2100,
                            expected=0x100,
                            message="FAIL status")
        done = True
        if (status & 0x2100) != 0x100:
            done = False
        self.spi_jtag_off()
        self.reset_tap()
        self.led.off()
        self.bitbang_jtag_off()
        return done

    # call this before sending the flash image
    # FPGA will enter flashing mode
    # TAP should be in "select DR scan" state
    @micropython.viper
    def flash_open(self):
        self.common_open()
        self.reset_tap()
        self.runtest_idle(1, 0)
        self.sir_idle(b"\xFF", 32, 0)  # BYPASS
        self.sir(b"\x3A")  # LSC_PROG_SPI
        self.sdr_idle(pack("<H", 0x68FE), 32, 0)
        # ---------- flashing begin -----------
        # 0x60 and other SPI flash commands here are bitreverse() values
        # of flash commands found in SPI FLASH datasheet.
        # e.g. 0x1B here is actually 0xD8 in datasheet, 0x60 is is 0x06 etc.

    @micropython.viper
    def flash_wait_status(self):
        retry = 50
        # read_status_register = pack("<H",0x00A0) # READ STATUS REGISTER
        status_register = bytearray(2)
        while retry > 0:
            # always refresh status_register[0], overwitten by response
            status_register[0] = 0xA0  # 0xA0 READ STATUS REGISTER
            self.sdr_response(status_register)
            if (int(status_register[1]) & 0xC1) == 0:
                break
            sleep_ms(1)
            retry -= 1
        if retry <= 0:
            print("error flash status %04X & 0xC1 != 0" %
                  (unpack("<H", status_register))[0])
        #  self.sdr(pack("<H",0x00A0), mask=pack("<H",0xC100), expected=pack("<H",0)) # READ STATUS REGISTER

    def flash_erase_block(self, addr=0):
        self.sdr(b"\x60")  # SPI WRITE ENABLE
        # some chips won't clear WIP without this:
        status = pack("<H", 0x00A0)  # READ STATUS REGISTER
        self.sdr_response(status)
        self.check_response(unpack("<H", status)[0],
                            mask=0xC100,
                            expected=0x4000)
        sdr = pack(">I", (self.flash_erase_cmd << 24) | (addr & 0xFFFFFF))
        self.send_tms(0)  # -> capture DR
        self.send_tms(0)  # -> shift DR
        self.swspi.write(sdr[:-1])
        self.send_data_byte_reverse(sdr[-1], 1, 8)  # last byte -> exit 1 DR
        self.send_tms(0)  # -> pause DR
        self.send_tms(1)  # -> exit 2 DR
        self.send_tms(1)  # -> update DR
        self.send_tms(1)  # -> select DR scan
        self.flash_wait_status()

    def flash_write_block(self, block, addr=0):
        self.sdr(b"\x60")  # SPI WRITE ENABLE
        self.send_tms(0)  # -> capture DR
        self.send_tms(0)  # -> shift DR
        # self.bitreverse(0x40) = 0x02 -> 0x02000000
        self.swspi.write(pack(">I", 0x02000000 | (addr & 0xFFFFFF)))
        self.swspi.write(block[:-1])  # whole block except last byte
        self.send_data_byte_reverse(block[-1], 1, 8)  # last byte -> exit 1 DR
        self.send_tms(0)  # -> pause DR
        self.send_tms(1)  # -> exit 2 DR
        self.send_tms(1)  # -> update DR
        self.send_tms(1)  # -> select DR scan
        self.flash_wait_status()

    # 256-byte write block is too short for hardware SPI to accelerate
    # flash_fast_write_block() is actually slower than flash_write_block()
#  def flash_fast_write_block(self, block, addr=0):
#    self.sdr(b"\x60") # SPI WRITE ENABLE
#    self.send_tms(0) # -> capture DR
#    self.send_tms(0) # -> shift DR
#    # self.bitreverse(0x40) = 0x02 -> 0x02000000
#    # send bits of 0x02 before the TCK glitch
#    self.send_data_byte_reverse(0x02,0,7) # LSB bit 0 not sent now
#    a = pack(">I", addr)
#    self.hwspi.init(sck=Pin(self.gpio_tck)) # 1 TCK-glitch TDO=0 as LSB bit
#    self.hwspi.write(a[1:4]) # send 3-byte address
#    self.hwspi.write(block[:-1]) # whole block except last byte
#    # switch from SPI to bitbanging mode
#    self.hwspi.init(sck=Pin(self.gpio_dummy)) # avoid TCK-glitch
#    self.bitbang_jtag_on()
#    self.send_data_byte_reverse(block[-1],1,8) # last byte -> exit 1 DR
#    self.send_tms(0) # -> pause DR
#    self.send_tms(1) # -> exit 2 DR
#    self.send_tms(1) # -> update DR
#    self.send_tms(1) # -> select DR scan
#    self.flash_wait_status()

# data is bytearray of to-be-read length

    def flash_fast_read_block(self, data, addr=0):
        # 0x0B is SPI flash fast read command
        sdr = pack(">I", 0x0B000000 | (addr & 0xFFFFFF))
        self.send_tms(0)  # -> capture DR
        self.send_tms(0)  # -> shift DR
        self.swspi.write(sdr)  # send SPI FLASH read command and address
        # fast read after address, should read 8 dummy cycles
        # this is a chance for TCK glitch workaround:
        # first 7 cycles will be done in bitbang mode
        # then switch to hardware SPI mode
        # will add 1 more TCK-glitch cycle
        for i in range(7):
            self.tck.off()
            self.tck.on()
        # switch from bitbanging to SPI mode
        self.hwspi.init(sck=Pin(self.gpio_tck))  # 1 TCK-glitch TDI=0
        self.hwspi.readinto(data)  # retrieve whole block
        # switch from SPI to bitbanging mode
        self.hwspi.init(sck=Pin(self.gpio_dummy))  # avoid TCK-glitch
        self.bitbang_jtag_on()
        self.send_data_byte_reverse(0, 1, 8)  # dummy read byte -> exit 1 DR
        self.send_tms(0)  # -> pause DR
        self.send_tms(1)  # -> exit 2 DR
        self.send_tms(1)  # -> update DR
        self.send_tms(1)  # -> select DR scan

    # call this after uploading all of the flash blocks,
    # this will exit FPGA flashing mode and start the bitstream
    @micropython.viper
    def flash_close(self):
        # switch from SPI to bitbanging
        # ---------- flashing end -----------
        self.sdr(b"\x20")  # SPI WRITE DISABLE
        self.sir_idle(b"\xFF", 100, 1)  # BYPASS
        self.sir_idle(b"\x26", 2, 200)  # ISC DISABLE
        self.sir_idle(b"\xFF", 2, 1)  # BYPASS
        self.sir(b"\x79")  # LSC_REFRESH reload the bitstream from flash
        self.sdr_idle(b"\x00\x00\x00", 2, 100)
        self.spi_jtag_off()
        self.reset_tap()
        self.led.off()
        self.bitbang_jtag_off()

    def stopwatch_start(self):
        self.stopwatch_ms = ticks_ms()

    def stopwatch_stop(self, bytes_uploaded):
        elapsed_ms = ticks_ms() - self.stopwatch_ms
        transfer_rate_MBps = 0
        if elapsed_ms > 0:
            transfer_rate_kBps = bytes_uploaded // elapsed_ms
        print("%d bytes uploaded in %d ms (%d kB/s)" %
              (bytes_uploaded, elapsed_ms, transfer_rate_kBps))

    def program_loop(self, filedata, blocksize=16384):
        self.prog_open()
        bytes_uploaded = 0
        self.stopwatch_start()
        block = bytearray(blocksize)
        while True:
            if filedata.readinto(block):
                self.hwspi.write(block)
                if self.progress:
                    print(".", end="")
                bytes_uploaded += len(block)
            else:
                if self.progress:
                    print("*")
                break
        self.stopwatch_stop(bytes_uploaded)
        return self.prog_close()

    def open_file(self, filename, gz=False):
        filedata = open(filename, "rb")
        if gz:
            import uzlib
            return uzlib.DecompIO(filedata, 31)
        return filedata

    def open_web(self, url, gz=False):
        import socket
        _, _, host, path = url.split('/', 3)
        addr = socket.getaddrinfo(host, 80)[0][-1]
        s = socket.socket()
        s.connect(addr)
        s.send(
            bytes(
                'GET /%s HTTP/1.0\r\nHost: %s\r\nAccept:  image/*\r\n\r\n' %
                (path, host), 'utf8'))
        for i in range(100):  # read first 100 lines searching for
            if len(s.readline()) < 3:  # first empty line (contains "\r\n")
                break
        if gz:
            import uzlib
            return uzlib.DecompIO(s, 31)
        return s

    # data is bytearray of to-be-read length
    def flash_read(self, data, addr=0):
        self.flash_open()
        self.flash_fast_read_block(data, addr)
        self.flash_close()

    # accelerated compare flash and file block
    # return value
    # 0-must nothing, 1-must erase, 2-must write, 3-must erase and write
    @micropython.viper
    def compare_flash_file_buf(self, flash_b, file_b) -> int:
        flash_block = ptr8(addressof(flash_b))
        file_block = ptr8(addressof(file_b))
        l = int(len(file_b))
        must = 0
        for i in range(l):
            if (flash_block[i] & file_block[i]) != file_block[i]:
                must = 1
        if must:  # erase will reset all bytes to 0xFF
            for i in range(l):
                if file_block[i] != 0xFF:
                    must = 3
        else:  # no erase
            for i in range(l):
                if flash_block[i] != file_block[i]:
                    must = 2
        return must

    # clever = read-compare-erase-write
    # prevents flash wear when overwriting the same data
    # needs more buffers: 4K erase block is max that fits on ESP32
    # TODO reduce buffer usage
    def flash_loop_clever(self, filedata, addr=0):
        addr_mask = self.flash_erase_size - 1
        if addr & addr_mask:
            print(
                "addr must be rounded to flash_erase_size = %d bytes (& 0x%06X)"
                % (self.flash_erase_size, 0xFFFFFF & ~addr_mask))
            return
        addr = addr & 0xFFFFFF & ~addr_mask  # rounded to even 64K (erase block)
        self.flash_open()
        bytes_uploaded = 0
        self.stopwatch_start()
        count_total = 0
        count_erase = 0
        count_write = 0
        file_block = bytearray(self.flash_erase_size)
        flash_block = bytearray(self.flash_erase_size)
        progress_char = "."
        while filedata.readinto(file_block):
            retry = 3
            while retry >= 0:
                self.flash_fast_read_block(flash_block,
                                           addr=addr + bytes_uploaded)
                must = self.compare_flash_file_buf(flash_block, file_block)
                write_addr = addr + bytes_uploaded
                if must == 0:
                    if (write_addr & 0xFFFF) == 0:
                        print("\r0x%06X %dK " %
                              (write_addr, self.flash_erase_size >> 10),
                              end="")
                    else:
                        print(progress_char, end="")
                    progress_char = "."
                    count_total += 1
                    bytes_uploaded += len(file_block)
                    break
                retry -= 1
                if must & 1:  # must_erase:
                    #print("from 0x%06X erase %dK" % (write_addr, self.flash_erase_size>>10),end="\r")
                    self.flash_erase_block(write_addr)
                    count_erase += 1
                    progress_char = "e"
                if must & 2:  # must_write:
                    #print("from 0x%06X write %dK" % (write_addr, self.flash_erase_size>>10),end="\r")
                    block_addr = 0
                    next_block_addr = 0
                    while next_block_addr < len(file_block):
                        next_block_addr = block_addr + self.flash_write_size
                        self.flash_write_block(
                            file_block[block_addr:next_block_addr],
                            addr=write_addr)
                        write_addr += self.flash_write_size
                        block_addr = next_block_addr
                    count_write += 1
                    progress_char = "w"
                #if not verify:
                #  count_total += 1
                #  bytes_uploaded += len(file_block)
                #  break
            if retry < 0:
                break
        print("\r", end="")
        self.stopwatch_stop(bytes_uploaded)
        print("%dK blocks: %d total, %d erased, %d written." %
              (self.flash_erase_size >> 10, count_total, count_erase,
               count_write))
        self.flash_close()
        return retry >= 0  # True if successful
示例#17
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)
示例#18
0
class ATM90e32:
    ##############################################################################

    def __init__(self, linefreq, pgagain, ugain, igainA, igainB, igainC):
        self._linefreq = linefreq
        self._pgagain = pgagain
        self._ugain = ugain
        self._igainA = igainA
        self._igainB = igainB
        self._igainC = igainC
        # HSPI Pins
        mosi_pin = Pin(13)
        miso_pin = Pin(12)
        sck_pin = Pin(14)
        cs_pin = 15
        # Chip select pin
        self.cs = Pin(cs_pin, Pin.OUT)
        # not using SPI, set to HIGH
        self.cs.on()

        # Setting SPI for what works for SAMD....
        # Doc on esp8266 SPI hw init: http://bit.ly/2ZhqeRB

        self.device = SPI(1,
                          baudrate=200000,
                          sck=sck_pin,
                          mosi=mosi_pin,
                          miso=miso_pin,
                          polarity=1,
                          phase=1)
        self.num_write_failed = 0
        self._init_config()
        if self.num_write_failed > 0:  # Can't write to registers..probably not connected.
            print(self.num_write_failed)
            raise OSError(NoMonitor().number, NoMonitor().explanation)

    def _init_config(self):
        # CurrentGainCT2 = 25498  #25498 - SCT-013-000 100A/50mA
        if (self._linefreq == 4485 or self._linefreq == 5231):
            # North America power frequency
            FreqHiThresh = 61 * 100
            FreqLoThresh = 59 * 100
            sagV = 90
        else:
            FreqHiThresh = 51 * 100
            FreqLoThresh = 49 * 100
            sagV = 190

        # calculation for voltage sag threshold - assumes we do not want to go under 90v for split phase and 190v otherwise
        # sqrt(2) = 1.41421356
        fvSagTh = (sagV * 100 * 1.41421356) / (2 * self._ugain / 32768)
        # convert to int for sending to the atm90e32.
        vSagTh = self._round_number(fvSagTh)

        self._spi_rw(SPI_WRITE, SoftReset, 0x789A)  # Perform soft reset
        # enable register config access
        self._spi_rw(SPI_WRITE, CfgRegAccEn, 0x55AA)
        self._spi_rw(SPI_WRITE, MeterEn, 0x0001)  # Enable Metering

        self._spi_rw(SPI_WRITE, SagTh, vSagTh)  # Voltage sag threshold
        # High frequency threshold - 61.00Hz
        self._spi_rw(SPI_WRITE, FreqHiTh, FreqHiThresh)
        # Lo frequency threshold - 59.00Hz
        self._spi_rw(SPI_WRITE, FreqLoTh, FreqLoThresh)
        self._spi_rw(SPI_WRITE, EMMIntEn0, 0xB76F)  # Enable interrupts
        self._spi_rw(SPI_WRITE, EMMIntEn1, 0xDDFD)  # Enable interrupts
        self._spi_rw(SPI_WRITE, EMMIntState0, 0x0001)  # Clear interrupt flags
        self._spi_rw(SPI_WRITE, EMMIntState1, 0x0001)  # Clear interrupt flags
        # ZX2, ZX1, ZX0 pin config
        self._spi_rw(SPI_WRITE, ZXConfig, 0x0A55)

        # Set metering config values (CONFIG)
        # PL Constant MSB (default) - Meter Constant = 3200 - PL Constant = 140625000
        self._spi_rw(SPI_WRITE, PLconstH, 0x0861)
        # PL Constant LSB (default) - this is 4C68 in the application note, which is incorrect
        self._spi_rw(SPI_WRITE, PLconstL, 0xC468)
        # Mode Config (frequency set in main program)
        self._spi_rw(SPI_WRITE, MMode0, self._linefreq)
        # PGA Gain Configuration for Current Channels - 0x002A (x4) # 0x0015 (x2) # 0x0000 (1x)
        self._spi_rw(SPI_WRITE, MMode1, self._pgagain)
        # Active Startup Power Threshold - 50% of startup current = 0.9/0.00032 = 2812.5
        # self._spi_rw(SPI_WRITE, PStartTh, 0x0AFC)
        # Testing a little lower setting...because readings aren't hapenng if power is off then
        # turned on....
        # Startup Power Threshold = .4/.00032 = 1250 = 0x04E2
        # Just checking with 0.
        self._spi_rw(SPI_WRITE, PStartTh, 0x0000)
        # Reactive Startup Power Threshold
        #  self._spi_rw(SPI_WRITE, QStartTh, 0x0AEC)
        self._spi_rw(SPI_WRITE, QStartTh, 0x0000)
        # Apparent Startup Power Threshold
        self._spi_rw(SPI_WRITE, SStartTh, 0x0000)
        # Active Phase Threshold = 10% of startup current = 0.06/0.00032 = 187.5
        # self._spi_rw(SPI_WRITE, PPhaseTh, 0x00BC)
        self._spi_rw(SPI_WRITE, PPhaseTh, 0x0000)
        self._spi_rw(SPI_WRITE, QPhaseTh, 0x0000)  # Reactive Phase Threshold
        # Apparent  Phase Threshold
        self._spi_rw(SPI_WRITE, SPhaseTh, 0x0000)

        # Set metering calibration values (CALIBRATION)
        self._spi_rw(SPI_WRITE, PQGainA, 0x0000)  # Line calibration gain
        self._spi_rw(SPI_WRITE, PhiA, 0x0000)  # Line calibration angle
        self._spi_rw(SPI_WRITE, PQGainB, 0x0000)  # Line calibration gain
        self._spi_rw(SPI_WRITE, PhiB, 0x0000)  # Line calibration angle
        self._spi_rw(SPI_WRITE, PQGainC, 0x0000)  # Line calibration gain
        self._spi_rw(SPI_WRITE, PhiC, 0x0000)  # Line calibration angle
        # A line active power offset
        self._spi_rw(SPI_WRITE, PoffsetA, 0x0000)
        # A line reactive power offset
        self._spi_rw(SPI_WRITE, QoffsetA, 0x0000)
        # B line active power offset
        self._spi_rw(SPI_WRITE, PoffsetB, 0x0000)
        # B line reactive power offset
        self._spi_rw(SPI_WRITE, QoffsetB, 0x0000)
        # C line active power offset
        self._spi_rw(SPI_WRITE, PoffsetC, 0x0000)
        # C line reactive power offset
        self._spi_rw(SPI_WRITE, QoffsetC, 0x0000)

        # Set metering calibration values (HARMONIC)
        # A Fund. active power offset
        self._spi_rw(SPI_WRITE, POffsetAF, 0x0000)
        # B Fund. active power offset
        self._spi_rw(SPI_WRITE, POffsetBF, 0x0000)
        # C Fund. active power offset
        self._spi_rw(SPI_WRITE, POffsetCF, 0x0000)
        # A Fund. active power gain
        self._spi_rw(SPI_WRITE, PGainAF, 0x0000)
        # B Fund. active power gain
        self._spi_rw(SPI_WRITE, PGainBF, 0x0000)
        # C Fund. active power gain
        self._spi_rw(SPI_WRITE, PGainCF, 0x0000)

        # Set measurement calibration values (ADJUST)
        self._spi_rw(SPI_WRITE, UgainA, self._ugain)  # A Voltage rms gain
        # A line current gain
        self._spi_rw(SPI_WRITE, IgainA, self._igainA)
        self._spi_rw(SPI_WRITE, UoffsetA, 0x0000)  # A Voltage offset
        self._spi_rw(SPI_WRITE, IoffsetA, 0x0000)  # A line current offset
        self._spi_rw(SPI_WRITE, UgainB, self._ugain)  # B Voltage rms gain
        # B line current gain
        self._spi_rw(SPI_WRITE, IgainB, self._igainB)
        self._spi_rw(SPI_WRITE, UoffsetB, 0x0000)  # B Voltage offset
        self._spi_rw(SPI_WRITE, IoffsetB, 0x0000)  # B line current offset
        self._spi_rw(SPI_WRITE, UgainC, self._ugain)  # C Voltage rms gain
        # C line current gain
        self._spi_rw(SPI_WRITE, IgainC, self._igainC)
        self._spi_rw(SPI_WRITE, UoffsetC, 0x0000)  # C Voltage offset
        self._spi_rw(SPI_WRITE, IoffsetC, 0x0000)  # C line current offset

        self._spi_rw(SPI_WRITE, CfgRegAccEn, 0x0000)  # end configuration
        # In order to get correct results, I needed to insert a 'significant' delay.
        time.sleep(1)

    #####################################################################################
    @property
    def lastSpiData(self):
        reading = self._spi_rw(SPI_READ, LastSPIData, 0xFFFF)
        return reading

    #####################################################################################
    @property
    def sys_status0(self):
        reading = self._spi_rw(SPI_READ, EMMIntState0, 0xFFFF)
        return reading

    #####################################################################################
    @property
    def sys_status1(self):
        reading = self._spi_rw(SPI_READ, EMMIntState1, 0xFFFF)
        return reading

    #####################################################################################

    @property
    def meter_status0(self):
        reading = self._spi_rw(SPI_READ, EMMState0, 0xFFFF)
        return reading

    #####################################################################################
    @property
    def en_status0(self):
        reading = self._spi_rw(SPI_READ, ENStatus0, 0xFFFF)
        return reading

    #####################################################################################
    @property
    def meter_status1(self):
        reading = self._spi_rw(SPI_READ, EMMState1, 0xFFFF)
        return reading

    #####################################################################################
    @property
    def line_voltageA(self):
        reading = self._spi_rw(SPI_READ, UrmsA, 0xFFFF)
        return reading / 100.0

    #####################################################################################
    @property
    def line_voltageB(self):
        reading = self._spi_rw(SPI_READ, UrmsB, 0xFFFF)
        return reading / 100.0

    #####################################################################################
    @property
    def line_voltageC(self):
        reading = self._spi_rw(SPI_READ, UrmsC, 0xFFFF)
        return reading / 100.0

    #####################################################################################
    @property
    def line_currentA(self):
        reading = self._spi_rw(SPI_READ, IrmsA, 0xFFFF)
        return reading / 1000.0

    #####################################################################################
    @property
    def line_currentC(self):
        reading = self._spi_rw(SPI_READ, IrmsC, 0xFFFF)
        return reading / 1000.0

    #####################################################################################
    @property
    def frequency(self):
        reading = self._spi_rw(SPI_READ, Freq, 0xFFFF)
        return reading / 100.0

    #####################################################################################
    @property
    def total_active_power(self):
        reading = self._read32Register(PmeanT, PmeanTLSB)
        return reading * 0.00032

    #####################################################################################

    @property
    def active_power_A(self):
        reading = self._read32Register(PmeanA, PmeanALSB)
        return reading * 0.00032
        #####################################################################################

    @property
    def active_power_C(self):
        reading = self._read32Register(PmeanC, PmeanCLSB)
        return reading * 0.00032
        #####################################################################################

    @property
    def total_reactive_power(self):
        reading = self._read32Register(QmeanT, PmeanTLSB)
        return reading * 0.00032

    #####################################################################################

    @property
    def reactive_power_A(self):
        reading = self._read32Register(QmeanA, PmeanALSB)
        return reading * 0.00032
        #####################################################################################

    @property
    def reactive_power_C(self):
        reading = self._read32Register(QmeanC, PmeanCLSB)
        return reading * 0.00032

    ######################################################
    # Support reading a register
    ######################################################

    def read(self, address):
        reading = self._spi_rw(SPI_READ, address, 0xFFFF)
        return reading

    ######################################################
    # Support writing to a register
    ######################################################
    def write(self, address, val):
        print('in write')
        return self._spi_rw(SPI_WRITE, address, val)

    #####################################################################################
    # do the SPI read or write request.
    #####################################################################################
    def _spi_rw(self, rw, address, val):

        address |= rw << 15

        if (rw):  # read
            return self._readSPI(address)

        else:
            for i in range(3):  # for robustness try a few times.
                bWriteSuccess = self._writeSPI(address, val)
                if (bWriteSuccess or address == SoftReset):
                    # time.sleep_us(3000)
                    # start = time.ticks_us()
                    # print(
                    #     'reading = value. Write successful.  It took {} tries.'.format(i))
                    # print('us ticks: {}'.format(
                    #     time.ticks_diff(time.ticks_us(), start)))
                    return True
            # Write to register didn't work.
            self.num_write_failed += 1
            # print(
            #     'Calibration write to address {:#04x} not successful.'.format(address))
            return False

    ###################################################################################

    def _readSPI(self, address):
        self.cs.off()
        time.sleep_us(4)
        # pack the address into a the bytearray.  It is an unsigned short(H) that needs to be in MSB(>)
        two_byte_buf = bytearray(2)
        results_buf = bytearray(2)
        struct.pack_into('>H', two_byte_buf, 0, address)
        self.device.write(two_byte_buf)
        time.sleep_us(4)
        self.device.readinto(results_buf)
        result = struct.unpack('>H', results_buf)[0]
        self.cs.on()
        return result

    ##################################################################################

    def _writeSPI(self, address, val):
        self.cs.off()
        time.sleep_us(4)
        four_byte_buf = bytearray(4)
        # pack the address into a the bytearray.  It is an unsigned short(H) that needs to be in MSB(>)

        struct.pack_into('>H', four_byte_buf, 0, address)
        struct.pack_into('>H', four_byte_buf, 2, val)

        self.device.write(four_byte_buf)
        self.cs.on()
        time.sleep_us(4)
        reading = self._spi_rw(SPI_READ, LastSPIData, 0xFFFF)
        if (reading == val):
            return True

        return False

    ##################################################################################
    def _round_number(self, f_num):
        if f_num - math.floor(f_num) < 0.5:
            return math.floor(f_num)
        return math.ceil(f_num)

    ###################################################################################
    def _read32Register(self, regh_addr, regl_addr):
        val_h = self._spi_rw(SPI_READ, regh_addr, 0xFFFF)
        val_l = self._spi_rw(SPI_READ, regl_addr, 0xFFFF)
        val = val_h << 16
        val |= val_l  # concatenate the 2 registers to make 1 32 bit number
        if ((val & 0x80000000) != 0):  # if val is negative,
            val = (0xFFFFFFFF - val) + 1  # 2s compliment + 1
        return (val)
示例#19
0
class spiram:
  def __init__(self):
    self.led = Pin(5, Pin.OUT)
    self.led.off()
    self.rom="48.rom"
    #self.rom="opense.rom"
    #self.rom="/sd/zxspectrum/48.rom"
    self.spi_channel = const(2)
    self.init_pinout_sd()
    self.spi_freq = const(4000000)
    self.hwspi=SPI(self.spi_channel, baudrate=self.spi_freq, polarity=0, phase=0, bits=8, firstbit=SPI.MSB, sck=Pin(self.gpio_sck), mosi=Pin(self.gpio_mosi), miso=Pin(self.gpio_miso))

  @micropython.viper
  def init_pinout_sd(self):
    self.gpio_sck  = const(16)
    self.gpio_mosi = const(4)
    self.gpio_miso = const(12)

  # read from file -> write to SPI RAM
  def load_stream(self, filedata, addr=0, maxlen=0x10000, blocksize=1024):
    block = bytearray(blocksize)
    # Request load
    self.led.on()
    self.hwspi.write(bytearray([0,(addr >> 24) & 0xFF, (addr >> 16) & 0xFF, (addr >> 8) & 0xFF, addr & 0xFF]))
    bytes_loaded = 0
    while bytes_loaded < maxlen:
      if filedata.readinto(block):
        self.hwspi.write(block)
        bytes_loaded += blocksize
      else:
        break
    self.led.off()

  # read from SPI RAM -> write to file
  def save_stream(self, filedata, addr=0, length=1024, blocksize=1024):
    bytes_saved = 0
    block = bytearray(blocksize)
    # Request save
    self.led.on()
    self.hwspi.write(bytearray([1,(addr >> 24) & 0xFF, (addr >> 16) & 0xFF, (addr >> 8) & 0xFF, addr & 0xFF, 0]))
    while bytes_saved < length:
      self.hwspi.readinto(block)
      filedata.write(block)
      bytes_saved += len(block)
    self.led.off()

  def ctrl(self,i):
    self.led.on()
    self.hwspi.write(bytearray([0, 0xFF, 0xFF, 0xFF, 0xFF, i]))
    self.led.off()

  def cpu_halt(self):
    self.ctrl(2)

  def cpu_continue(self):
    self.ctrl(0)

  def load_z80_compressed_stream(self, filedata, length=0xFFFF):
    b=bytearray(1)
    escbyte=bytearray([0xED])
    s=0
    repeat=0
    bytes_loaded=0
    while bytes_loaded < length:
      if filedata.readinto(b):
        nexts=s
        if s==0:
          if b[0]==escbyte[0]:
            nexts=1
          else:
            self.hwspi.write(b)
        if s==1:
          if b[0]==escbyte[0]:
            nexts=2
          else:
            self.hwspi.write(escbyte)
            self.hwspi.write(b)
            nexts=0
        if s==2:
          repeat=b[0]
          if repeat==0:
            print("end")
            break
          nexts=3
        if s==3:
          self.hwspi.read(repeat,b[0])
          nexts=0
        s=nexts
        bytes_loaded += 1
      else:
        break
    print("bytes loaded %d" % bytes_loaded)

  def load_z80_v1_compressed_block(self, filedata):
    self.led.on()
    self.hwspi.write(bytearray([0,0,0,0x40,0])) # from 0x4000
    self.load_z80_compressed_stream(filedata)
    self.led.off()

  def load_z80_v23_block(self, filedata):
    header = bytearray(3)
    if filedata.readinto(header):
      length,page = unpack("<HB",header)
      print("load z80 block: length=%d, page=%d" % (length,page))
    else:
      return False
    addr = -1
    if page==4:
      addr=0x8000
    if page==5:
      addr=0xC000
    if page==8:
      addr=0x4000
    if addr < 0:
      print("unsupported page ignored")
      filedata.seek(length,1)
      return True
    if length==0xFFFF:
      compress=0
      length=0x4000
    else:
      compress=1
    #print("addr=%04X compress=%d" % (addr,compress))
    if compress:
      # Request load
      self.led.on()
      self.hwspi.write(bytearray([0,(addr >> 24) & 0xFF, (addr >> 16) & 0xFF, (addr >> 8) & 0xFF, addr & 0xFF]))
      self.load_z80_compressed_stream(filedata,length)
      self.led.off()
    else:
      print("uncompressed v2/v3 may need FIXME")
      self.load_stream(filedata,addr,16384)
    return True
  
  def patch_rom(self,pc,header):
    # overwrite tape saving code in original ROM
    # with restore code and data from header
    code_addr = 0x4C2
    header_addr = 0x500
    self.led.on()
    self.hwspi.write(bytearray([0, 0,0,0,0, 0xF3, 0xAF, 0x11, 0xFF, 0xFF, 0xC3, code_addr&0xFF, (code_addr>>8)&0xFF])) # overwrite start of ROM to JP 0x04C2
    self.led.off()
    self.led.on()
    self.hwspi.write(bytearray([0, 0,0,(code_addr>>8)&0xFF,code_addr&0xFF])) # overwrite 0x04C2
    # Z80 code that POPs REGs from header as stack data at 0x500
    # z80asm restore.z80asm; hexdump -v -e '/1 "0x%02X,"' a.bin
    # restores border color, registers I, AFBCDEHL' and AFBCDEHL
    self.hwspi.write(bytearray([0x31,(header_addr+9)&0xFF,((header_addr+9)>>8)&0xFF,0xF1,0xED,0x47,0xF1,0x1F,0xD3,0xFE,0xD1,0xD9,0xC1,0xD1,0xE1,0xD9,0xF1,0x08,0xFD,0xE1,0xDD,0xE1,0x21,0xE5,0xFF,0x39,0xF9,0xF1,0xC1,0xE1]));
    self.hwspi.write(bytearray([0x31])) # LD SP, ...
    self.hwspi.write(header[8:10])
    self.hwspi.write(bytearray([0xED])) # IM ...
    imarg = bytearray([0x46,0x56,0x5E,0x5E])
    self.hwspi.write(bytearray([imarg[header[29]&3]])) # IM mode
    if header[27]:
      self.hwspi.write(bytearray([0xFB])) # EI
    header[6]=pc&0xFF
    header[7]=(pc>>8)&0xFF
    self.hwspi.write(bytearray([0xC3])) # JP ...
    self.hwspi.write(header[6:8]) # PC address of final JP
    self.led.off()
    self.led.on()
    self.hwspi.write(bytearray([0, 0,0,(header_addr>>8)&0xFF,header_addr&0xFF])) # overwrite 0x0500 with header
    # header fix: exchange A and F, A' and F' to become POPable
    x=header[0]
    header[0]=header[1]
    header[1]=x
    x=header[21]
    header[21]=header[22]
    header[22]=x
    if header[12]==255:
      header[12]=1
    #header[12] ^= 7<<1 # FIXME border color
    self.hwspi.write(header) # AF and AF' now POPable
    self.led.off()

  def loadz80(self,filename):
    z=open(filename,"rb")
    header1 = bytearray(30)
    z.readinto(header1)
    pc=unpack("<H",header1[6:8])[0]
    self.cpu_halt()
    self.load_stream(open(self.rom, "rb"), addr=0)
    if pc: # V1 format
      print("Z80 v1")
      self.patch_rom(pc,header1)
      if header1[12] & 32:
        self.load_z80_v1_compressed_block(z)
      else:
        self.load_stream(z,0x4000)
    else: # V2 or V3 format
      word = bytearray(2)
      z.readinto(word)
      length2 = unpack("<H", word)[0]
      if length2 == 23:
        print("Z80 v2")
      else:
        if length2 == 54 or length2 == 55:
          print("Z80 v3")
        else:
          print("unsupported header2 length %d" % length2)
          return
      header2 = bytearray(length2)
      z.readinto(header2)
      pc=unpack("<H",header2[0:2])[0]
      self.patch_rom(pc,header1)
      while self.load_z80_v23_block(z):
        pass
    self.ctrl(3) # reset and halt
    self.ctrl(1) # only reset
    self.cpu_continue()
    # restore original ROM after image starts
    self.cpu_halt()
    self.load_stream(open(self.rom, "rb"), addr=0)
    self.cpu_continue() # release reset
示例#20
0
class SpmodTest():

    test_conut = 0

    def __init__(self, mosi=8, miso=15, cs=20, clk=21):
        self.is_load = False
        self.spi = SPI(SPI.SPI_SOFT,
                       mode=SPI.MODE_MASTER,
                       baudrate=400 * 1000,
                       polarity=0,
                       phase=0,
                       bits=8,
                       firstbit=SPI.MSB,
                       sck=clk,
                       mosi=mosi,
                       miso=miso)
        fm.register(cs, fm.fpioa.GPIO6, force=True)
        self.cs = GPIO(GPIO.GPIO6, GPIO.OUT)

    def test_event(self):
        if self.work_data != None and self.work_data == b'\x0b\x17':
            Report.Spmod_Test = True
        sample_page.next()

    def load(self):
        if Report.Spmod_Test:
            sample_page.next()
        if self.is_load == False:
            # i2c init()
            sample_page.btn.enable = False
            self.isError = None
            self.work_info = []
            self.work_data = None
            self.agent = agent()
            self.agent.event(250, self.check)
            self.agent.event(1500, self.test_event)
            self.is_load = True

    def free(self):
        if self.is_load:
            # i2c deinit()
            sample_page.btn.enable = True
            self.is_load = False

    def check(self):
        try:
            tmp = []
            self.cs.value(0)
            write_data = bytearray([0x90, 0x00, 0x00, 0x00])
            self.spi.write(write_data)
            id_buf = bytearray(2)
            self.spi.readinto(id_buf, write=0xff)
            self.work_data = id_buf
            self.cs.value(1)
            tmp.append("Flash ReadID\n\n" + str(self.work_data))
            self.work_info = tmp
        except Exception as e:
            Report.Spmod_Test = False
            Report.isError = str(e)
            print(e)

    def work(self):
        self.agent.parallel_cycle()

        ui.canvas.draw_string(10, 10, "4 Spmod Test", (0, 255, 127), scale=2)
        if self.work_data:
            for i in range(len(self.work_info)):
                ui.canvas.draw_string(20,
                                      20 * i + 90,
                                      "{0}".format(str(self.work_info[i])),
                                      scale=2)
        if self.isError != None:
            ui.canvas.draw_string(40,
                                  80,
                                  self.isError, (255, 255, 255),
                                  scale=2)
            sample_page.next()
示例#21
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("")
示例#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)
if c==0:
     
    pw2.deinit()
    p2.value(1)

    deadline = utime.ticks_add(utime.ticks_ms(), TIMEOUT)
    #/CS asserted
    cs.value(0)
    
    while (utime.ticks_diff(deadline, utime.ticks_ms()) > 0):
        if irq_flag:
            #p1=utime.ticks_us()
            
            #get the segments through SPI
            vspi.readinto(buff_video)
            #delta_t=utime.ticks_diff(utime.ticks_us(),p1)
            # exception , ENOMEM bug 
            if flag_ex:
                if utime.ticks_diff(utime.ticks_ms(),t1_ex)>50:
                    flag_ex=False
            #do not send the first frames and do not send during 50 ms if an exception has been raised
            if indice>=27*4*2 and not flag_ex:
                try:
                    #p1=utime.ticks_us()
                    s.sendto(buff_video,('192.168.4.2',7674))
                    #delta_t=utime.ticks_diff(utime.ticks_us(),p1)
                except OSError as e:
                    #ENOMEM exception , bug
                    if e.args[0]==12:
                        flag_ex=True
示例#24
0
class ecp5:

  def init_pinout_jtag(self):
    # FJC-ESP32-V0r2 pluggable
    #self.gpio_tms = const(4)
    #self.gpio_tck = const(16)
    #self.gpio_tdi = const(15)
    #self.gpio_tdo = const(2)
    #self.gpio_tcknc = const(21)
    #self.gpio_led = const(19)
    # ULX3S v3.0.x
    #self.gpio_tms = const(21)
    #self.gpio_tck = const(18)
    #self.gpio_tdi = const(23)
    #self.gpio_tdo = const(19)
    #self.gpio_tcknc = const(17) # free pin for SPI workaround
    #self.gpio_led = const(5)
    # ULX3S v3.1.x
    self.gpio_tms = const(5)   # BLUE LED - 549ohm - 3.3V
    self.gpio_tck = const(18)
    self.gpio_tdi = const(23)
    self.gpio_tdo = const(34)
    self.gpio_tcknc = const(21) # 1,2,3,19,21 free pin for SPI workaround
    self.gpio_led = const(19)

  def bitbang_jtag_on(self):
    #self.led=Pin(self.gpio_led,Pin.OUT)
    self.tms=Pin(self.gpio_tms,Pin.OUT)
    self.tck=Pin(self.gpio_tck,Pin.OUT)
    self.tdi=Pin(self.gpio_tdi,Pin.OUT)
    self.tdo=Pin(self.gpio_tdo,Pin.IN)

  def bitbang_jtag_off(self):
    #self.led=Pin(self.gpio_led,Pin.IN)
    self.tms=Pin(self.gpio_tms,Pin.IN)
    self.tck=Pin(self.gpio_tck,Pin.IN)
    self.tdi=Pin(self.gpio_tdi,Pin.IN)
    self.tdo=Pin(self.gpio_tdo,Pin.IN)
    #a = self.led.value()
    a = self.tms.value()
    a = self.tck.value()
    a = self.tdo.value()
    a = self.tdi.value()
    #del self.led
    del self.tms
    del self.tck
    del self.tdi
    del self.tdo

  # initialize both hardware accelerated SPI
  # software SPI on the same pins
  def spi_jtag_on(self):
    self.hwspi=SPI(self.spi_channel, baudrate=self.spi_freq, polarity=1, phase=1, bits=8, firstbit=SPI.MSB, sck=Pin(self.gpio_tck), mosi=Pin(self.gpio_tdi), miso=Pin(self.gpio_tdo))
    self.swspi=SPI(-1, baudrate=self.spi_freq, polarity=1, phase=1, bits=8, firstbit=SPI.MSB, sck=Pin(self.gpio_tck), mosi=Pin(self.gpio_tdi), miso=Pin(self.gpio_tdo))

  def spi_jtag_off(self):
    self.hwspi.deinit()
    del self.hwspi
    self.swspi.deinit()
    del self.swspi

  def __init__(self):
    self.spi_freq = const(25000000) # Hz JTAG clk frequency
    # -1 for JTAG over SOFT SPI slow, compatibility
    #  1 or 2 for JTAG over HARD SPI fast
    #  2 is preferred as it has default pinout wired
    self.flash_read_size = const(2048)
    self.flash_write_size = const(256)
    self.flash_erase_size = const(65536)
    flash_erase_cmd = { 4096:0x20, 32768:0x52, 65536:0xD8, 262144:0xD8 } # erase commands from FLASH PDF
    self.flash_era = bytearray([flash_erase_cmd[self.flash_erase_size],0,0])
    #self.rb=bytearray(256) # reverse bits
    #self.init_reverse_bits()
    self.spi_channel = const(2) # -1 soft, 1:sd, 2:jtag
    self.init_pinout_jtag()
    self.flash_req=bytearray(4)
    self.read_status=bytearray([5])
    self.status=bytearray(1)

  # print bytes reverse - appears the same as in SVF file
  #def print_hex_reverse(self, block, head="", tail="\n"):
  #  print(head, end="")
  #  for n in range(len(block)):
  #    print("%02X" % block[len(block)-n-1], end="")
  #  print(tail, end="")

  #@micropython.viper
  #def init_reverse_bits(self):
  #  #p8rb=ptr8(addressof(self.rb))
  #  p8rb=memoryview(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

  @micropython.viper
  def send_tms(self, tms:int):
    if tms:
      self.tms.on()
    else:
      self.tms.off()
    self.tck.off()
    self.tck.on()

  # LSB 1st
  @micropython.viper
  def send_read_data_lsb1st(self, buf, last:int, w:ptr8):
    p = ptr8(addressof(buf))
    l = int(len(buf))
    val = 0
    self.tms.off()
    for i in range(l-1):
      byte = 0
      val = p[i]
      for nf in range(8):
        if (val >> nf) & 1:
          self.tdi.on()
        else:
          self.tdi.off()
        self.tck.off()
        self.tck.on()
        if self.tdo.value():
          byte |= 1 << nf
      if int(w):
        w[i] = byte # write byte
    byte = 0
    val = p[l-1] # read last byte
    for nf in range(7): # first 7 bits
      if (val >> nf) & 1:
        self.tdi.on()
      else:
        self.tdi.off()
      self.tck.off()
      self.tck.on()
      if self.tdo.value():
        byte |= 1 << nf
    # last bit
    if last:
      self.tms.on()
    if (val >> 7) & 1:
      self.tdi.on()
    else:
      self.tdi.off()
    self.tck.off()
    self.tck.on()
    if self.tdo.value():
      byte |= 1 << 7
    if int(w):
      w[l-1] = byte # write last byte

  @micropython.viper
  def send_data_msb1st(self, val:int, last:int, bits:int):
    self.tms.off()
    for nf in range(bits-1):
      if (val >> (7-nf)) & 1:
        self.tdi.on()
      else:
        self.tdi.off()
      self.tck.off()
      self.tck.on()
    if last:
      self.tms.on()
    if val & 1:
      self.tdi.on()
    else:
      self.tdi.off()
    self.tck.off()
    self.tck.on()

  # TAP to "reset" state
  @micropython.viper
  def reset_tap(self):
    for n in range(6):
      self.send_tms(1) # -> Test Logic Reset

  # TAP should be in "idle" state
  # TAP returns to "select DR scan" state
  @micropython.viper
  def runtest_idle(self, count:int, duration_ms:int):
    leave=int(ticks_ms()) + duration_ms
    for n in range(count):
      self.send_tms(0) # -> idle
    while int(ticks_ms())-leave < 0:
      self.send_tms(0) # -> idle
    self.send_tms(1) # -> select DR scan

  # send SIR command (bytes)
  # TAP should be in "select DR scan" state
  # TAP returns to "select DR scan" state
  @micropython.viper
  def sir(self, sir):
    self.send_tms(1) # -> select IR scan
    self.send_tms(0) # -> capture IR
    self.send_tms(0) # -> shift IR
    self.send_read_data_lsb1st(sir,1,0) # -> exit 1 IR
    self.send_tms(0) # -> pause IR
    self.send_tms(1) # -> exit 2 IR
    self.send_tms(1) # -> update IR
    self.send_tms(1) # -> select DR scan

  # send SIR command (bytes)
  # TAP should be in "select DR scan" state
  # TAP returns to "select DR scan" state
  # finish with n idle cycles during minimum of ms time
  @micropython.viper
  def sir_idle(self, sir, n:int, ms:int):
    self.send_tms(1) # -> select IR scan
    self.send_tms(0) # -> capture IR
    self.send_tms(0) # -> shift IR
    self.send_read_data_lsb1st(sir,1,0) # -> exit 1 IR
    self.send_tms(0) # -> pause IR
    self.send_tms(1) # -> exit 2 IR
    self.send_tms(1) # -> update IR
    self.runtest_idle(n+1,ms) # -> select DR scan

  @micropython.viper
  def sdr(self, sdr):
    self.send_tms(0) # -> capture DR
    self.send_tms(0) # -> shift DR
    self.send_read_data_lsb1st(sdr,1,0)
    self.send_tms(0) # -> pause DR
    self.send_tms(1) # -> exit 2 DR
    self.send_tms(1) # -> update DR
    self.send_tms(1) # -> select DR scan

  @micropython.viper
  def sdr_idle(self, sdr, n:int, ms:int):
    self.send_tms(0) # -> capture DR
    self.send_tms(0) # -> shift DR
    self.send_read_data_lsb1st(sdr,1,0)
    self.send_tms(0) # -> pause DR
    self.send_tms(1) # -> exit 2 DR
    self.send_tms(1) # -> update DR
    self.runtest_idle(n+1, ms) # -> select DR scan

  # sdr buffer will be overwritten with response
  @micropython.viper
  def sdr_response(self, sdr):
    self.send_tms(0) # -> capture DR
    self.send_tms(0) # -> shift DR
    self.send_read_data_lsb1st(sdr,1,addressof(sdr))
    self.send_tms(0) # -> pause DR
    self.send_tms(1) # -> exit 2 DR
    self.send_tms(1) # -> update DR
    self.send_tms(1) # -> select DR scan

  def check_response(self, response, expected, mask=0xFFFFFFFF, message=""):
    if (response & mask) != expected:
      print("0x%08X & 0x%08X != 0x%08X %s" % (response,mask,expected,message))

  def idcode(self):
    self.bitbang_jtag_on()
    #self.led.on()
    self.reset_tap()
    self.runtest_idle(1,0)
    self.sir(b"\xE0")
    id_bytes = bytearray(4)
    self.sdr_response(id_bytes)
    #self.led.off()
    self.bitbang_jtag_off()
    return unpack("<I", id_bytes)[0]

  # common JTAG open for both program and flash
  def common_open(self):
    self.spi_jtag_on()
    self.hwspi.init(sck=Pin(self.gpio_tcknc)) # avoid TCK-glitch
    self.bitbang_jtag_on()
    #self.led.on()
    self.reset_tap()
    self.runtest_idle(1,0)
    #self.sir(b"\xE0") # read IDCODE
    #self.sdr(pack("<I",0), expected=pack("<I",0), message="IDCODE")
    self.sir(b"\x1C") # LSC_PRELOAD: program Bscan register
    self.sdr(bytearray([0xFF for i in range(64)]))
    self.sir(b"\xC6") # ISC ENABLE: Enable SRAM programming mode
    self.sdr_idle(b"\x00",2,10)
    self.sir_idle(b"\x3C",2,1) # LSC_READ_STATUS
    status = bytearray(4)
    self.sdr_response(status)
    self.check_response(unpack("<I",status)[0], mask=0x24040, expected=0, message="FAIL status")
    self.sir(b"\x0E") # ISC_ERASE: Erase the SRAM
    self.sdr_idle(b"\x01",2,10)
    self.sir_idle(b"\x3C",2,1) # LSC_READ_STATUS
    status = bytearray(4)
    self.sdr_response(status)
    self.check_response(unpack("<I",status)[0], mask=0xB000, expected=0, message="FAIL status")

  # call this before sending the bitstram
  # FPGA will enter programming mode
  # after this TAP will be in "shift DR" state
  def prog_open(self):
    self.common_open()
    self.sir(b"\x46") # LSC_INIT_ADDRESS
    self.sdr_idle(b"\x01",2,10)
    self.sir(b"\x7A") # LSC_BITSTREAM_BURST
    # ---------- bitstream begin -----------
    # manually walk the TAP
    # we will be sending one long DR command
    self.send_tms(0) # -> capture DR
    self.send_tms(0) # -> shift DR
    # switch from bitbanging to SPI mode
    self.hwspi.init(sck=Pin(self.gpio_tck)) # 1 TCK-glitch? TDI=0
    # we are lucky that format of the bitstream tolerates
    # any leading and trailing junk bits. If it weren't so,
    # HW SPI JTAG acceleration wouldn't work.
    # to upload the bitstream:
    # FAST SPI mode
    #self.hwspi.write(block)
    # SLOW bitbanging mode
    #for byte in block:
    #  self.send_data_msb1st(byte,0)

  def prog_stream_done(self):
    # switch from hardware SPI to bitbanging done after prog_stream()
    self.hwspi.init(sck=Pin(self.gpio_tcknc)) # avoid TCK-glitch
    self.spi_jtag_off()

  # call this after uploading all of the bitstream blocks,
  # this will exit FPGA programming mode and start the bitstream
  # returns status True-OK False-Fail
  def prog_close(self):
    self.bitbang_jtag_on()
    self.send_tms(1) # -> exit 1 DR
    self.send_tms(0) # -> pause DR
    self.send_tms(1) # -> exit 2 DR
    self.send_tms(1) # -> update DR
    #self.send_tms(0) # -> idle, disabled here as runtest_idle does the same
    self.runtest_idle(100,10)
    # ---------- bitstream end -----------
    self.sir_idle(b"\xC0",2,1) # read usercode
    usercode = bytearray(4)
    self.sdr_response(usercode)
    self.check_response(unpack("<I",usercode)[0],expected=0,message="FAIL usercode")
    self.sir_idle(b"\x26",2,200) # ISC DISABLE
    self.sir_idle(b"\xFF",2,1) # BYPASS
    self.sir(b"\x3C") # LSC_READ_STATUS
    status = bytearray(4)
    self.sdr_response(status)
    status = unpack("<I",status)[0]
    self.check_response(status,mask=0x2100,expected=0x100,message="FAIL status")
    done = True
    if (status & 0x2100) != 0x100:
      done = False
    self.reset_tap()
    #self.led.off()
    self.bitbang_jtag_off()
    return done

  # call this before sending the flash image
  # FPGA will enter flashing mode
  # TAP should be in "select DR scan" state
  @micropython.viper
  def flash_open(self):
    self.common_open()
    self.reset_tap()
    self.runtest_idle(1,0)
    self.sir_idle(b"\xFF",32,0) # BYPASS
    self.sir(b"\x3A") # LSC_PROG_SPI
    self.sdr_idle(pack("<H",0x68FE),32,0)
    # ---------- flashing begin -----------
    # 0x60 and other SPI flash commands here are bitreverse() values
    # of flash commands found in SPI FLASH datasheet.
    # e.g. 0x1B here is actually 0xD8 in datasheet, 0x60 is is 0x06 etc.

  @micropython.viper
  def flash_wait_status(self,n:int):
    retry=n
    mask=1 # WIP bit (work-in-progress)
    self.send_tms(0) # -> capture DR
    self.send_tms(0) # -> shift DR
    self.swspi.write(self.read_status) # READ STATUS REGISTER
    self.swspi.readinto(self.status)
    while retry > 0:
      self.swspi.readinto(self.status)
      if (int(self.status[0]) & mask) == 0:
        break
      sleep_ms(1)
      retry -= 1
    self.send_tms(1) # -> exit 1 DR # exit at byte incomplete
    #self.send_data_msb1st(0,1,8) # exit at byte complete
    self.send_tms(0) # -> pause DR
    self.send_tms(1) # -> exit 2 DR
    self.send_tms(1) # -> update DR
    self.send_tms(1) # -> select DR scan
    if retry <= 0:
      print("error %d flash status 0x%02X & 0x%02X != 0" % (n,self.status[0],mask))

  @micropython.viper
  def flash_erase_block(self, addr:int):
    self.sdr(b"\x60") # SPI WRITE ENABLE
    self.flash_wait_status(1001)
    p8=ptr8(addressof(self.flash_era))
    p8[1]=addr>>16
    p8[2]=addr>>8
    self.send_tms(0) # -> capture DR
    self.send_tms(0) # -> shift DR
    self.swspi.write(self.flash_era) # except LSB
    self.send_data_msb1st(addr,1,8) # last LSB byte -> exit 1 DR
    self.send_tms(0) # -> pause DR
    self.send_tms(1) # -> exit 2 DR
    self.send_tms(1) # -> update DR
    self.send_tms(1) # -> select DR scan
    self.flash_wait_status(2002)

  @micropython.viper
  def flash_write_block(self, block, last:int, addr:int):
    self.sdr(b"\x60") # SPI WRITE ENABLE
    self.flash_wait_status(1003)
    p8=ptr8(addressof(self.flash_req))
    p8[0]=2
    p8[1]=addr>>16
    p8[2]=addr>>8
    p8[3]=addr
    self.send_tms(0) # -> capture DR
    self.send_tms(0) # -> shift DR
    self.swspi.write(self.flash_req)
    self.swspi.write(block) # whole block
    self.send_data_msb1st(last,1,8) # last byte -> exit 1 DR
    self.send_tms(0) # -> pause DR
    self.send_tms(1) # -> exit 2 DR
    self.send_tms(1) # -> update DR
    self.send_tms(1) # -> select DR scan
    self.flash_wait_status(1004)

  # data is bytearray of to-be-read length
  @micropython.viper
  def flash_read_block(self, data, addr:int):
    p8=ptr8(addressof(self.flash_req))
    p8[0]=3
    p8[1]=addr>>16
    p8[2]=addr>>8
    p8[3]=addr
    self.send_tms(0) # -> capture DR
    self.send_tms(0) # -> shift DR
    self.swspi.write(self.flash_req) # send SPI FLASH read command and address and dummy byte
    self.swspi.readinto(data) # retrieve whole block
    self.send_data_msb1st(0,1,8) # dummy read byte -> exit 1 DR
    self.send_tms(0) # -> pause DR
    self.send_tms(1) # -> exit 2 DR
    self.send_tms(1) # -> update DR
    self.send_tms(1) # -> select DR scan

  # call this after uploading all of the flash blocks,
  # this will exit FPGA flashing mode and start the bitstream
  @micropython.viper
  def flash_close(self):
    # switch from SPI to bitbanging
    # ---------- flashing end -----------
    self.sdr(b"\x20") # SPI WRITE DISABLE
    self.sir_idle(b"\xFF",100,1) # BYPASS
    self.sir_idle(b"\x26",2,200) # ISC DISABLE
    self.sir_idle(b"\xFF",2,1) # BYPASS
    self.sir(b"\x79") # LSC_REFRESH reload the bitstream from flash
    self.sdr_idle(b"\x00\x00\x00",2,100)
    self.spi_jtag_off()
    self.reset_tap()
    #self.led.off()
    self.bitbang_jtag_off()

  def stopwatch_start(self):
    self.stopwatch_ms = ticks_ms()

  def stopwatch_stop(self, bytes_uploaded):
    elapsed_ms = ticks_ms() - self.stopwatch_ms
    transfer_rate_MBps = 0
    if elapsed_ms > 0:
      transfer_rate_kBps = bytes_uploaded // elapsed_ms
    print("%d bytes uploaded in %d ms (%d kB/s)" % (bytes_uploaded, elapsed_ms, transfer_rate_kBps))

  def prog_stream(self, filedata, blocksize=16384):
    self.prog_open()
    bytes_uploaded = 0
    self.stopwatch_start()
    block = bytearray(blocksize)
    while True:
      if filedata.readinto(block):
        self.hwspi.write(block)
        bytes_uploaded += len(block)
      else:
        break
    self.stopwatch_stop(bytes_uploaded)
    self.prog_stream_done()

  def open_file(self, filename, gz=False):
    filedata = open(filename, "rb")
    if gz:
      import uzlib
      return uzlib.DecompIO(filedata,31)
    return filedata

  def open_web(self, url, gz=False):
    import socket
    _, _, host, path = url.split('/', 3)
    port = 80
    if ( len(host.split(':')) == 2 ):
      host, port = host.split(':', 2)
    print("host = %s, port = %d, path = %s" % (host, port, path))
    addr = socket.getaddrinfo(host, port)[0][-1]
    s = socket.socket()
    s.connect(addr)
    s.send(bytes('GET /%s HTTP/1.0\r\nHost: %s\r\nAccept:  image/*\r\n\r\n' % (path, host), 'utf8'))
    for i in range(100): # read first 100 lines searching for
      if len(s.readline()) < 3: # first empty line (contains "\r\n")
        break
    if gz:
      import uzlib
      return uzlib.DecompIO(s,31)
    return s

  # data is bytearray of to-be-read length
  def flash_read(self, data, addr=0):
    self.flash_open()
    self.flash_read_block(data, addr)
    self.flash_close()

  # accelerated compare flash and file block
  # return value
  # 0-must nothing, 1-must erase, 2-must write, 3-must erase and write
  @micropython.viper
  def compare_flash_file_buf(self, flash_b, file_b, must:int)->int:
    flash_block = ptr8(addressof(flash_b))
    file_block = ptr8(addressof(file_b))
    l = int(len(file_b))
    for i in range(l):
      if (flash_block[i] & file_block[i]) != file_block[i]:
        must = 1
    if must: # erase will reset all bytes to 0xFF
      for i in range(l):
        if file_block[i] != 0xFF:
          must = 3
    else: # no erase
      for i in range(l):
        if flash_block[i] != file_block[i]:
          must = 2
    return must

  # clever = read-compare-erase-write
  # prevents flash wear when overwriting the same data
  # needs more buffers: 4K erase block is max that fits on ESP32
  # TODO reduce buffer usage
  # returns status True-OK False-Fail
  def flash_stream(self, filedata, addr=0):
    self.flash_open()
    addr_mask = self.flash_erase_size-1
    if addr & addr_mask:
      print("addr must be rounded to flash_erase_size = %d bytes (& 0x%06X)" % (self.flash_erase_size, 0xFFFFFF & ~addr_mask))
      return False
    addr = addr & 0xFFFFFF & ~addr_mask # rounded to even 64K (erase block)
    bytes_uploaded = 0
    self.stopwatch_start()
    #if 1:
    #  print("erase whole FLASH (max 90s)")
    #  self.sdr(b"\x60") # SPI WRITE ENABLE
    #  self.flash_wait_status(105)
    #  self.sdr(b"\xE3") # BULK ERASE (whole chip) self.rb[0x60]=0x06 or self.rb[0xC7]=0xE3
    #  self.flash_wait_status(90000)
    count_total = 0
    count_erase = 0
    count_write = 0
    file_block = bytearray(self.flash_erase_size)
    flash_block = bytearray(self.flash_read_size)
    fbmv=memoryview(file_block)
    progress_char="."
    while filedata.readinto(file_block):
      #self.led.value((bytes_uploaded >> 12)&1)
      retry = 3
      while retry > 0:
        must = 0
        flash_rd = 0
        while flash_rd<self.flash_erase_size:
          self.flash_read_block(flash_block,addr+bytes_uploaded+flash_rd)
          must = self.compare_flash_file_buf(flash_block,file_block[flash_rd:flash_rd+self.flash_read_size],must)
          flash_rd+=self.flash_read_size
        write_addr = addr+bytes_uploaded
        if must == 0:
          if (write_addr & 0xFFFF) == 0:
            print("\r0x%06X %dK %c" % (write_addr, self.flash_erase_size>>10, progress_char),end="")
          else:
            print(progress_char,end="")
          progress_char="."
          count_total += 1
          bytes_uploaded += len(file_block)
          break
        retry -= 1
        if must & 1: # must_erase:
          #print("from 0x%06X erase %dK" % (write_addr, self.flash_erase_size>>10),end="\r")
          self.flash_erase_block(write_addr)
          count_erase += 1
          progress_char = "e"
        if must & 2: # must_write:
          #print("from 0x%06X write %dK" % (write_addr, self.flash_erase_size>>10),end="\r")
          block_addr = 0
          next_block_addr = 0
          while next_block_addr < len(file_block):
            next_block_addr = block_addr+self.flash_write_size
            self.flash_write_block(fbmv[block_addr:next_block_addr-1], fbmv[next_block_addr-1], write_addr)
            write_addr += self.flash_write_size
            block_addr = next_block_addr
          count_write += 1
          progress_char = "w"
        #if not verify:
        #  count_total += 1
        #  bytes_uploaded += len(file_block)
        #  break
      if retry <= 0:
        break
    print("\r",end="")
    self.stopwatch_stop(bytes_uploaded)
    print("%dK blocks: %d total, %d erased, %d written." % (self.flash_erase_size>>10, count_total, count_erase, count_write))
    return retry > 0 # True if successful

  def filedata_gz(self, filepath):
    gz = filepath.endswith(".gz")
    if filepath.startswith("http://") or filepath.startswith("/http:/"):
      filedata = self.open_web(filepath, gz)
    else:
      filedata = self.open_file(filepath, gz)
    return filedata, gz
示例#25
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
示例#26
0
文件: osd.py 项目: emard/ulx3s_mac128
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)
示例#27
0
class artix7:
    def init_pinout_jtag(self):
        # FJC-ESP32-V0r2 pluggable
        #self.gpio_tms = const(4)
        #self.gpio_tck = const(16)
        #self.gpio_tdi = const(15)
        #self.gpio_tdo = const(2)
        #self.gpio_tcknc = const(21)
        #self.gpio_led = const(19)
        # ESP32-WROVER-E FROGO wired
        self.gpio_tms = const(5)  # BLUE LED - 549ohm - 3.3V
        self.gpio_tck = const(18)
        self.gpio_tdi = const(23)
        self.gpio_tdo = const(34)
        self.gpio_tcknc = const(21)  # 1,2,3,19,21 for SPI workaround
        self.gpio_led = const(19)

    def bitbang_jtag_on(self):
        self.led = Pin(self.gpio_led, Pin.OUT)
        self.tms = Pin(self.gpio_tms, Pin.OUT)
        self.tck = Pin(self.gpio_tck, Pin.OUT)
        self.tdi = Pin(self.gpio_tdi, Pin.OUT)
        self.tdo = Pin(self.gpio_tdo, Pin.IN)

    def bitbang_jtag_off(self):
        self.led = Pin(self.gpio_led, Pin.IN)
        self.tms = Pin(self.gpio_tms, Pin.IN)
        self.tck = Pin(self.gpio_tck, Pin.IN)
        self.tdi = Pin(self.gpio_tdi, Pin.IN)
        self.tdo = Pin(self.gpio_tdo, Pin.IN)
        a = self.led.value()
        a = self.tms.value()
        a = self.tck.value()
        a = self.tdo.value()
        a = self.tdi.value()
        del self.led
        del self.tms
        del self.tck
        del self.tdi
        del self.tdo

    # initialize both hardware accelerated SPI
    # software SPI on the same pins
    def spi_jtag_on(self):
        self.hwspi = SPI(self.spi_channel,
                         baudrate=self.spi_freq,
                         polarity=1,
                         phase=1,
                         bits=8,
                         firstbit=SPI.MSB,
                         sck=Pin(self.gpio_tck),
                         mosi=Pin(self.gpio_tdi),
                         miso=Pin(self.gpio_tdo))
        self.swspi = SPI(-1,
                         baudrate=self.spi_freq,
                         polarity=1,
                         phase=1,
                         bits=8,
                         firstbit=SPI.MSB,
                         sck=Pin(self.gpio_tck),
                         mosi=Pin(self.gpio_tdi),
                         miso=Pin(self.gpio_tdo))

    def spi_jtag_off(self):
        self.hwspi.deinit()
        del self.hwspi
        self.swspi.deinit()
        del self.swspi

    def __init__(self):
        self.spi_freq = const(25000000)  # Hz JTAG clk frequency
        # -1 for JTAG over SOFT SPI slow, compatibility
        #  1 or 2 for JTAG over HARD SPI fast
        #  2 is preferred as it has default pinout wired
        self.flash_read_size = const(2048)
        self.flash_write_size = const(256)
        #self.flash_erase_size = const(4096) # WROOM
        self.flash_erase_size = const(65536)  # WROVER
        flash_erase_cmd = {
            4096: 0x20,
            32768: 0x52,
            65536: 0xD8
        }  # erase commands from FLASH PDF
        self.flash_erase_cmd = flash_erase_cmd[self.flash_erase_size]
        #self.rb=bytearray(256) # reverse bits
        #self.init_reverse_bits()
        self.spi_channel = const(2)  # -1 soft, 1:sd, 2:jtag
        self.init_pinout_jtag()
        self.magic = bytearray([0x59, 0xA6, 0x59, 0xA6])
        self.wrenable = self.magic + bytearray([0, 8, 6])
        self.wrdisable = self.magic + bytearray([0, 8, 4])
        self.read_status = self.magic + bytearray([0, 16, 5, 0])
        self.status = bytearray(2)
        self.dummy4 = bytearray(4)
        self.none = bytearray(0)

    # print bytes reverse - appears the same as in SVF file
    #def print_hex_reverse(self, block, head="", tail="\n"):
    #  print(head, end="")
    #  for n in range(len(block)):
    #    print("%02X" % block[len(block)-n-1], end="")
    #  print(tail, end="")

    #@micropython.viper
    #def init_reverse_bits(self):
    #  p8rb=ptr8(addressof(self.rb))
    #  #p8rb=memoryview(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

    @micropython.viper
    def send_tms(self, tms: int):
        if tms:
            self.tms.on()
        else:
            self.tms.off()
        self.tck.off()
        self.tck.on()

    @micropython.viper
    def send_read_buf_lsb1st(self, buf, last: int, w: ptr8):
        p = ptr8(addressof(buf))
        l = int(len(buf))
        val = 0
        self.tms.off()
        for i in range(l - 1):
            byte = 0
            val = p[i]
            for nf in range(8):
                if (val >> nf) & 1:
                    self.tdi.on()
                else:
                    self.tdi.off()
                self.tck.off()
                self.tck.on()
                if self.tdo.value():
                    byte |= 1 << nf
            if int(w):
                w[i] = byte  # write byte
        byte = 0
        val = p[l - 1]  # read last byte
        for nf in range(7):  # first 7 bits
            if (val >> nf) & 1:
                self.tdi.on()
            else:
                self.tdi.off()
            self.tck.off()
            self.tck.on()
            if self.tdo.value():
                byte |= 1 << nf
        # last bit
        if last:
            self.tms.on()
        if (val >> 7) & 1:
            self.tdi.on()
        else:
            self.tdi.off()
        self.tck.off()
        self.tck.on()
        if self.tdo.value():
            byte |= 1 << 7
        if int(w):
            w[l - 1] = byte  # write last byte

    @micropython.viper
    def send_read_int_msb1st(self, val: int, last: int, bits: int) -> int:
        self.tms.off()
        byte = 0
        for nf in range(bits - 1):
            if (val >> nf) & 1:
                self.tdi.on()
            else:
                self.tdi.off()
            self.tck.off()
            self.tck.on()
            if self.tdo.value():
                byte |= 1 << nf
        if last:
            self.tms.on()
        if (val >> (bits - 1)) & 1:
            self.tdi.on()
        else:
            self.tdi.off()
        self.tck.off()
        self.tck.on()
        if self.tdo.value():
            byte |= 1 << (bits - 1)
        return byte

    @micropython.viper
    def send_int_msb1st(self, val: int, last: int, bits: int):
        self.tms.off()
        for nf in range(bits - 1):
            if (val >> (7 - nf)) & 1:
                self.tdi.on()
            else:
                self.tdi.off()
            self.tck.off()
            self.tck.on()
        if last:
            self.tms.on()
        if val & 1:
            self.tdi.on()
        else:
            self.tdi.off()
        self.tck.off()
        self.tck.on()

    # TAP to "reset" state
    @micropython.viper
    def reset_tap(self):
        for n in range(6):
            self.send_tms(1)  # -> Test Logic Reset

    # TAP should be in "idle" state
    # TAP returns to "select DR scan" state
    @micropython.viper
    def runtest_idle(self, count: int, duration_ms: int):
        leave = int(ticks_ms()) + duration_ms
        for n in range(count):
            self.send_tms(0)  # -> idle
        while int(ticks_ms()) - leave < 0:
            self.send_tms(0)  # -> idle
        self.send_tms(1)  # -> select DR scan

    # send SIR command (bytes)
    # TAP should be in "select DR scan" state
    # TAP returns to "select DR scan" state
    # LSB first
    @micropython.viper
    def sir(self, sir: int) -> int:
        self.send_tms(1)  # -> select IR scan
        self.send_tms(0)  # -> capture IR
        self.send_tms(0)  # -> shift IR
        r = int(self.send_read_int_msb1st(sir, 1, 6))  # -> exit 1 IR
        self.send_tms(0)  # -> pause IR
        self.send_tms(1)  # -> exit 2 IR
        self.send_tms(1)  # -> update IR
        self.send_tms(1)  # -> select DR scan
        return r

    # send SIR command (bytes)
    # TAP should be in "select DR scan" state
    # TAP returns to "select DR scan" state
    # finish with n idle cycles during minimum of ms time
    #@micropython.viper
    #def sir_idle(self, sir:int, n:int, ms:int):
    #  self.send_tms(1) # -> select IR scan
    #  self.send_tms(0) # -> capture IR
    #  self.send_tms(0) # -> shift IR
    #  self.send_data_byte(sir,1,6) # -> exit 1 IR
    #  self.send_tms(0) # -> pause IR
    #  self.send_tms(1) # -> exit 2 IR
    #  self.send_tms(1) # -> update IR
    #  self.runtest_idle(n+1, ms) # -> select DR scan

    # LSB first
    @micropython.viper
    def sdr(self, sdr):
        self.send_tms(0)  # -> capture DR
        self.send_tms(0)  # -> shift DR
        self.send_read_buf_lsb1st(sdr, 1, 0)
        self.send_tms(0)  # -> pause DR
        self.send_tms(1)  # -> exit 2 DR
        self.send_tms(1)  # -> update DR
        self.send_tms(1)  # -> select DR scan

    # LSB first
    @micropython.viper
    def sdr_idle(self, sdr, n: int, ms: int):
        self.send_tms(0)  # -> capture DR
        self.send_tms(0)  # -> shift DR
        self.send_read_buf_lsb1st(sdr, 1, 0)
        self.send_tms(0)  # -> pause DR
        self.send_tms(1)  # -> exit 2 DR
        self.send_tms(1)  # -> update DR
        self.runtest_idle(n + 1, ms)  # -> select DR scan

    # sdr buffer will be overwritten with response LSB first
    @micropython.viper
    def sdr_response(self, sdr):
        self.send_tms(0)  # -> capture DR
        self.send_tms(0)  # -> shift DR
        self.send_read_buf_lsb1st(sdr, 1, addressof(sdr))
        self.send_tms(0)  # -> pause DR
        self.send_tms(1)  # -> exit 2 DR
        self.send_tms(1)  # -> update DR
        self.send_tms(1)  # -> select DR scan

    # USER1 send a+b MSB first
    # a can be 0-size
    def user1_send(self, a, b):
        self.sir(2)  # USER1
        self.send_tms(0)  # -> capture DR
        self.send_tms(0)  # -> shift DR
        self.swspi.write(a)
        self.swspi.write(b[:-1])
        self.send_int_msb1st(b[-1], 1, 8)  # last byte -> exit 1 DR
        self.send_tms(0)  # -> pause DR
        self.send_tms(1)  # -> exit 2 DR
        self.send_tms(1)  # -> update DR
        self.send_tms(1)  # -> select DR scan

    # USER1 send a, recv b
    # a can be 0-size
    # after b, it reads one dummy bit
    @micropython.viper
    def user1_send_recv(self, a, b):
        self.sir(2)  # USER1
        self.send_tms(0)  # -> capture DR
        self.send_tms(0)  # -> shift DR
        self.swspi.write(a)
        self.swspi.readinto(b)
        self.send_tms(1)  # -> exit 1 DR, dummy bit
        self.send_tms(0)  # -> pause DR
        self.send_tms(1)  # -> exit 2 DR
        self.send_tms(1)  # -> update DR
        self.send_tms(1)  # -> select DR scan

    def check_response(self, response, expected, mask=0xFFFFFFFF, message=""):
        if (response & mask) != expected:
            print("0x%08X & 0x%08X != 0x%08X %s" %
                  (response, mask, expected, message))

    def idcode(self):
        self.bitbang_jtag_on()
        self.led.on()
        self.reset_tap()
        self.runtest_idle(1, 0)
        #self.sir(9)
        id_bytes = bytearray(4)
        self.sdr_response(id_bytes)
        self.led.off()
        self.bitbang_jtag_off()
        return unpack("<I", id_bytes)[0]

    # common JTAG open for both program and flash
    def common_open(self):
        self.spi_jtag_on()
        self.hwspi.init(sck=Pin(self.gpio_tcknc))  # avoid TCK-glitch
        self.bitbang_jtag_on()
        self.led.on()
        self.reset_tap()
        self.runtest_idle(1, 0)

    # call this before sending the bitstram
    # FPGA will enter programming mode
    # after this TAP will be in "shift DR" state
    def prog_open(self):
        self.common_open()
        self.sir(0x3F)  # BYPASS
        self.sir(0xB)  # JPROGRAM
        self.runtest_idle(1, 20)
        self.check_response(self.sir(0x14),
                            mask=0x10,
                            expected=0x10,
                            message="FAIL ISC_NOOP")
        self.sir(5)  # CFG_IN
        # ---------- bitstream begin -----------
        # manually walk the TAP
        # we will be sending one long DR command
        self.send_tms(0)  # -> capture DR
        self.send_tms(0)  # -> shift DR # NOTE sent with 1 TCK glitch
        # switch from bitbanging to SPI mode
        self.hwspi.init(sck=Pin(self.gpio_tck))  # 1 TCK-glitch TDI=0
        # we are lucky that format of the bitstream tolerates
        # any leading and trailing junk bits. If it weren't so,
        # HW SPI JTAG acceleration wouldn't work.
        # to upload the bitstream:
        # FAST SPI mode
        #self.hwspi.write(block)
        # SLOW bitbanging mode
        #for byte in block:
        #  self.send_int_msb1st(byte,0)

    def prog_stream_done(self):
        # switch from hardware SPI to bitbanging done after prog_stream()
        self.hwspi.init(sck=Pin(self.gpio_tcknc))  # avoid TCK-glitch
        self.spi_jtag_off()

    # call this after uploading all of the bitstream blocks,
    # this will exit FPGA programming mode and start the bitstream
    # returns status True-OK False-Fail
    def prog_close(self):
        self.bitbang_jtag_on()
        self.send_tms(1)  # -> exit 1 DR
        self.send_tms(0)  # -> pause DR
        self.send_tms(1)  # -> exit 2 DR
        self.send_tms(1)  # -> update DR
        #self.send_tms(0) # -> idle, disabled here as runtest_idle does the same
        self.runtest_idle(1, 10)
        # ---------- bitstream end -----------
        self.sir(0xC)  # JSTART
        self.runtest_idle(2000, 0)
        self.reset_tap()
        self.led.off()
        self.bitbang_jtag_off()
        return True

    # call this before sending the flash image
    # FPGA will enter flashing mode
    # TAP should be in "select DR scan" state
    @micropython.viper
    def flash_open(self):
        file = "jtagspi%08x.bit.gz" % self.idcode()
        self.prog_stream(self.open_file(file, True))
        if not self.prog_close():
            print("%s failed" % file)
        self.common_open()
        self.reset_tap()
        self.runtest_idle(1, 0)
        # ---------- flashing begin -----------
        # 0x60 and other SPI flash commands here are bitreverse() values
        # of flash commands found in SPI FLASH datasheet.
        # e.g. 0x1B here is actually 0xD8 in datasheet, 0x60 is is 0x06 etc.

    @micropython.viper
    def flash_wait_status(self, n: int):
        retry = n
        while retry > 0:
            self.user1_send(self.none, self.read_status)
            self.user1_send_recv(self.none, self.status)
            if (int(self.status[1]) & 1) == 0:
                break
            sleep_ms(1)
            retry -= 1
        if retry <= 0:
            print("error %d flash status 0x%02X & 1 != 0" %
                  (n, self.status[1]))

    def flash_erase_block(self, addr=0):
        self.user1_send(self.none, self.wrenable)
        self.flash_wait_status(1001)
        req = self.magic + bytearray([
            0, 32, self.flash_erase_cmd, addr >> 16, addr >> 8, addr
        ])  # 6=SPI WRITE ENABLE
        self.user1_send(self.none, req)
        self.flash_wait_status(2002)

    def flash_write_block(self, block, addr=0):
        self.user1_send(self.none, self.wrenable)
        self.flash_wait_status(114)
        # 6 = SPI WRITE ENABLE, 2 = WRITE BLOCK followed by 3-byte address and 256-byte data block
        bits = (4 + len(block)) * 8
        req = self.magic + bytearray(
            [bits >> 8, bits, 2, addr >> 16, addr >> 8, addr])
        self.user1_send(req, block)
        self.flash_wait_status(1004)

    # data is bytearray of to-be-read length
    # max 2048 bytes
    def flash_read_block(self, data, addr=0):
        # first is the request 3=READ BLOCK, 3-byte address, 256-byte data
        bits = (len(data) + 4) * 8
        req = self.magic + bytearray(
            [bits >> 8, bits, 3, addr >> 16, addr >> 8, addr])
        self.user1_send(req, data)
        # collects response from previous command
        self.user1_send_recv(self.dummy4, data)

    # call this after uploading all of the flash blocks,
    # this will exit FPGA flashing mode and start the bitstream
    @micropython.viper
    def flash_close(self):
        # switch from SPI to bitbanging
        # ---------- flashing end -----------
        self.user1_send(self.none, self.wrdisable)
        self.sir(0xD)  # JSHUTDOWN
        self.sir(0xB)  # JPROGRAM
        self.runtest_idle(2000, 20)
        self.sir(0x3F)  # BYPASS
        self.runtest_idle(2000, 0)
        self.spi_jtag_off()
        self.reset_tap()
        self.led.off()
        self.bitbang_jtag_off()

    def stopwatch_start(self):
        self.stopwatch_ms = ticks_ms()

    def stopwatch_stop(self, bytes_uploaded):
        elapsed_ms = ticks_ms() - self.stopwatch_ms
        transfer_rate_MBps = 0
        if elapsed_ms > 0:
            transfer_rate_kBps = bytes_uploaded // elapsed_ms
        print("%d bytes uploaded in %d ms (%d kB/s)" %
              (bytes_uploaded, elapsed_ms, transfer_rate_kBps))

    def prog_stream(self, filedata, blocksize=16384):
        self.prog_open()
        bytes_uploaded = 0
        self.stopwatch_start()
        block = bytearray(blocksize)
        while True:
            if filedata.readinto(block):
                self.hwspi.write(block)
                bytes_uploaded += len(block)
            else:
                break
        self.stopwatch_stop(bytes_uploaded)
        self.prog_stream_done()

    def open_file(self, filename, gz=False):
        filedata = open(filename, "rb")
        if gz:
            import uzlib
            return uzlib.DecompIO(filedata, 31)
        return filedata

    def open_web(self, url, gz=False):
        import socket
        _, _, host, path = url.split('/', 3)
        port = 80
        if (len(host.split(':')) == 2):
            host, port = host.split(':', 2)
        print("host = %s, port = %d, path = %s" % (host, port, path))
        addr = socket.getaddrinfo(host, port)[0][-1]
        s = socket.socket()
        s.connect(addr)
        s.send(
            bytes(
                'GET /%s HTTP/1.0\r\nHost: %s\r\nAccept:  image/*\r\n\r\n' %
                (path, host), 'utf8'))
        for i in range(100):  # read first 100 lines searching for
            if len(s.readline()) < 3:  # first empty line (contains "\r\n")
                break
        if gz:
            import uzlib
            return uzlib.DecompIO(s, 31)
        return s

    # data is bytearray of to-be-read length
    def flash_readinto(self, data, addr=0):
        self.flash_open()
        self.flash_read_block(data, addr)
        self.flash_close()

    # accelerated compare flash and file block
    # return value
    # 0-must nothing, 1-must erase, 2-must write, 3-must erase and write
    @micropython.viper
    def compare_flash_file_buf(self, flash_b, file_b, must: int) -> int:
        flash_block = ptr8(addressof(flash_b))
        file_block = ptr8(addressof(file_b))
        l = int(len(file_b))
        for i in range(l):
            if (flash_block[i] & file_block[i]) != file_block[i]:
                must = 1
        if must:  # erase will reset all bytes to 0xFF
            for i in range(l):
                if file_block[i] != 0xFF:
                    must = 3
        else:  # no erase
            for i in range(l):
                if flash_block[i] != file_block[i]:
                    must = 2
        return must

    # clever = read-compare-erase-write
    # prevents flash wear when overwriting the same data
    # needs more buffers: 4K erase block is max that fits on ESP32
    # TODO reduce buffer usage
    # returns status True-OK False-Fail
    def flash_stream(self, filedata, addr=0):
        addr_mask = self.flash_erase_size - 1
        if addr & addr_mask:
            print(
                "addr must be rounded to flash_erase_size = %d bytes (& 0x%06X)"
                % (self.flash_erase_size, 0xFFFFFF & ~addr_mask))
            return
        addr = addr & 0xFFFFFF & ~addr_mask  # rounded to even 64K (erase block)
        self.flash_open()
        bytes_uploaded = 0
        self.stopwatch_start()
        count_total = 0
        count_erase = 0
        count_write = 0
        file_block = bytearray(self.flash_erase_size)
        flash_block = bytearray(self.flash_read_size)
        fbmv = memoryview(file_block)
        progress_char = "."
        while filedata.readinto(file_block):
            self.led.value((bytes_uploaded >> 12) & 1)
            retry = 3
            while retry >= 0:
                must = 0
                flash_rd = 0
                while flash_rd < self.flash_erase_size:
                    self.flash_read_block(flash_block,
                                          addr + bytes_uploaded + flash_rd)
                    must = self.compare_flash_file_buf(
                        flash_block,
                        fbmv[flash_rd:flash_rd + self.flash_read_size], must)
                    flash_rd += self.flash_read_size
                write_addr = addr + bytes_uploaded
                if must == 0:
                    if (write_addr & 0xFFFF) == 0:
                        print("\r0x%06X %dK %c" %
                              (write_addr, self.flash_erase_size >> 10,
                               progress_char),
                              end="")
                    else:
                        print(progress_char, end="")
                    progress_char = "."
                    count_total += 1
                    bytes_uploaded += len(file_block)
                    break
                retry -= 1
                if must & 1:  # must_erase:
                    #print("from 0x%06X erase %dK" % (write_addr, self.flash_erase_size>>10),end="\r")
                    self.flash_erase_block(write_addr)
                    count_erase += 1
                    progress_char = "e"
                if must & 2:  # must_write:
                    #print("from 0x%06X write %dK" % (write_addr, self.flash_erase_size>>10),end="\r")
                    block_addr = 0
                    next_block_addr = 0
                    while next_block_addr < len(file_block):
                        next_block_addr = block_addr + self.flash_write_size
                        self.flash_write_block(
                            fbmv[block_addr:next_block_addr], addr=write_addr)
                        write_addr += self.flash_write_size
                        block_addr = next_block_addr
                    count_write += 1
                    progress_char = "w"
                #if not verify:
                #  count_total += 1
                #  bytes_uploaded += len(file_block)
                #  break
            if retry < 0:
                break
        print("\r", end="")
        self.stopwatch_stop(bytes_uploaded)
        print("%dK blocks: %d total, %d erased, %d written." %
              (self.flash_erase_size >> 10, count_total, count_erase,
               count_write))
        return retry >= 0  # True if successful

    def filedata_gz(self, filepath):
        gz = filepath.endswith(".gz")
        if filepath.startswith("http://") or filepath.startswith("/http:/"):
            filedata = self.open_web(filepath, gz)
        else:
            filedata = self.open_file(filepath, gz)
        return filedata, gz
示例#28
0
from machine import SPI

spi_id = 1

spi = SPI(spi_id)
print(spi)
spi = SPI(spi_id, 2000000)
print(spi)
spi = SPI(spi_id, 2000000, polarity=0)
print(spi)
spi = SPI(spi_id, 2000000, polarity=0, phase=1)
print(spi)
spi = SPI(spi_id, bits=16, firstbit=SPI.MSB)
print(spi)

spi.init(4000000, polarity=1, phase=0, bits=8, firstbit=SPI.LSB)
print(spi)

buf = bytearray(32)
spi.readinto(buf)
spi.readinto(buf, 0xff)

data = b'01234567'
spi.write(data)

buf = bytearray(8)
spi.write_readinto(data, buf)

def read_into_simple(address, buf, length=None):
    print("---simple---")
    if length is None:
        length = len(buf)
    buffa[0] = address & 0x7F
    print("before:\n", buffa)
    writebuf = bytearray([buffa[0]])
    spi.write(writebuf)
    print("middle:\n", buffa)
    #newbuf=bytearray([buf[0]])
    #device.write(newbuf)
    #device.readinto(buf[0:length])
    newbuf = buf[0:length]
    spi.readinto(newbuf)
    buf[0:len(newbuf)] = newbuf
    print("after:\n", buffa)


cs.value(0)
spi.write(buffa)
spi.readinto(buffa)
cs.value(1)
print(buffa[0])

#cs.value(0)
#read_into_simple(address,buffa,length=1)
#cs.value(1)
#print(buffa[0])
示例#30
0
from machine import SPI
from fpioa_manager import fm

mosi = 8
miso = 15
cs = 20
clk = 21

spi = SPI(SPI.SPI_SOFT,
          mode=SPI.MODE_MASTER,
          baudrate=400 * 1000,
          polarity=0,
          phase=0,
          bits=8,
          firstbit=SPI.MSB,
          sck=clk,
          mosi=mosi,
          miso=miso)
fm.register(cs, fm.fpioa.GPIO6, force=True)
cs = GPIO(GPIO.GPIO6, GPIO.OUT)

# read spi flash id
while True:
    cs.value(0)
    write_data = bytearray([0x90, 0x00, 0x00, 0x00])
    spi.write(write_data)
    id_buf = bytearray(2)
    spi.readinto(id_buf, write=0xff)
    work_data = id_buf
    cs.value(1)
    print(work_data)