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)
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()
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
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()
class SCL3300: def __init__(self, spinr: int, baudrate: int, sck: Pin, mosi: Pin, miso: Pin, cs: CS) -> None: self.cs = cs self.spi = SPI(spinr, baudrate, polarity=0, phase=0, bits=32, firstbit=SPI.MSB, sck=sck, mosi=mosi, miso=miso) def write(self, data: bytes | int) -> None: if isinstance(data, int): data = data.to_bytes(4, "big") try: self.cs.enable() self.spi.write(data) finally: self.cs.disable() def exchange(self, data: bytes | int) -> bytes: if isinstance(data, int): data = data.to_bytes(4, "big") received = bytearray(len(data)) try: self.cs.enable() received = self.spi.write_readinto(data, received) finally: self.cs.disable() return received def read(self, n) -> bytes: received = bytearray(n) try: self.cs.enable() received = self.spi.readinto(received, 0x00) finally: return received
class 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
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()
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
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])
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)
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) #将可能残余的数据清除
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)
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)
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)
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
class osd: def __init__(self): self.screen_x = const(64) self.screen_y = const(20) self.cwd = "/" self.init_fb() self.exp_names = " KMGTE" self.mark = bytearray([32,16,42]) # space, right triangle, asterisk self.diskfile=False self.data_buf=bytearray(554) self.mdv_byte=bytearray(1) self.mdv_phase=bytearray([1]) self.read_dir() self.spi_read_irq = bytearray([1,0xF1,0,0,0,0,0]) self.spi_read_btn = bytearray([1,0xFB,0,0,0,0,0]) self.spi_read_blktyp = bytearray([1,0xD0,0,0,0,0,0]) self.spi_result = bytearray(7) self.spi_enable_osd = bytearray([0,0xFE,0,0,0,1]) self.spi_write_osd = bytearray([0,0xFD,0,0,0]) self.spi_send_mdv_bram = bytearray([0,0xD1,0,0,0]) self.spi_channel = const(2) self.spi_freq = const(3000000) self.init_pinout_sd() self.init_spi() alloc_emergency_exception_buf(100) self.enable = bytearray(1) self.timer = Timer(3) self.irq_handler(0) self.irq_handler_ref = self.irq_handler # allocation happens here self.spi_request = Pin(0, Pin.IN, Pin.PULL_UP) self.spi_request.irq(trigger=Pin.IRQ_FALLING, handler=self.irq_handler_ref) def init_spi(self): self.spi=SPI(self.spi_channel, baudrate=self.spi_freq, polarity=0, phase=0, bits=8, firstbit=SPI.MSB, sck=Pin(self.gpio_sck), mosi=Pin(self.gpio_mosi), miso=Pin(self.gpio_miso)) self.cs=Pin(self.gpio_cs,Pin.OUT) self.cs.off() self.led=Pin(self.gpio_led,Pin.OUT) self.led.off() # init file browser def init_fb(self): self.fb_topitem = 0 self.fb_cursor = 0 self.fb_selected = -1 @micropython.viper def init_pinout_sd(self): self.gpio_cs = const(17) self.gpio_sck = const(16) self.gpio_mosi = const(4) self.gpio_miso = const(12) self.gpio_led = const(5) @micropython.viper def irq_handler(self, pin): p8result = ptr8(addressof(self.spi_result)) self.cs.on() self.spi.write_readinto(self.spi_read_irq, self.spi_result) self.cs.off() btn_irq = p8result[6] if btn_irq&1: # microdrive 1 request #self.cs.on() #self.spi.write_readinto(self.spi_read_blktyp,self.spi_result) #self.cs.off() #blktyp=p8result[6] if self.diskfile: self.mdv_read() if btn_irq&0x80: # btn event IRQ flag self.cs.on() self.spi.write_readinto(self.spi_read_btn, self.spi_result) self.cs.off() btn = p8result[6] p8enable = ptr8(addressof(self.enable)) if p8enable[0]&2: # wait to release all BTNs if btn==1: p8enable[0]&=1 # clear bit that waits for all BTNs released else: # all BTNs released if (btn&0x78)==0x78: # all cursor BTNs pressed at the same time self.show_dir() # refresh directory p8enable[0]=(p8enable[0]^1)|2; self.osd_enable(p8enable[0]&1) if p8enable[0]==1: if btn==9: # btn3 cursor up self.start_autorepeat(-1) if btn==17: # btn4 cursor down self.start_autorepeat(1) if btn==1: self.timer.deinit() # stop autorepeat if btn==33: # btn5 cursor left self.updir() if btn==65: # btn6 cursor right self.select_entry() def start_autorepeat(self, i:int): self.autorepeat_direction=i self.move_dir_cursor(i) self.timer_slow=1 self.timer.init(mode=Timer.PERIODIC, period=500, callback=self.autorepeat) def autorepeat(self, timer): if self.timer_slow: self.timer_slow=0 self.timer.init(mode=Timer.PERIODIC, period=30, callback=self.autorepeat) self.move_dir_cursor(self.autorepeat_direction) self.irq_handler(0) # catch stale IRQ def select_entry(self): if self.direntries[self.fb_cursor][1]: # is it directory oldselected = self.fb_selected - self.fb_topitem self.fb_selected = self.fb_cursor try: self.cwd = self.fullpath(self.direntries[self.fb_cursor][0]) except: self.fb_selected = -1 self.show_dir_line(oldselected) self.show_dir_line(self.fb_cursor - self.fb_topitem) self.init_fb() self.read_dir() self.show_dir() else: self.change_file() def updir(self): if len(self.cwd) < 2: self.cwd = "/" else: s = self.cwd.split("/")[:-1] self.cwd = "" for name in s: if len(name) > 0: self.cwd += "/"+name self.init_fb() self.read_dir() self.show_dir() def fullpath(self,fname): if self.cwd.endswith("/"): return self.cwd+fname else: return self.cwd+"/"+fname def change_file(self): oldselected = self.fb_selected - self.fb_topitem self.fb_selected = self.fb_cursor try: filename = self.fullpath(self.direntries[self.fb_cursor][0]) except: filename = False self.fb_selected = -1 self.show_dir_line(oldselected) self.show_dir_line(self.fb_cursor - self.fb_topitem) if filename: if filename.endswith(".mdv") or filename.endswith(".MDV"): self.diskfile = open(filename,"rb") self.mdv_refill_buf() self.enable[0]=0 self.osd_enable(0) if filename.endswith(".bit"): self.spi_request.irq(handler=None) self.timer.deinit() self.enable[0]=0 self.osd_enable(0) self.spi.deinit() tap=ecp5.ecp5() tap.prog_stream(open(filename,"rb"),blocksize=1024) if filename.endswith("_sd.bit"): os.umount("/sd") for i in bytearray([2,4,12,13,14,15]): p=Pin(i,Pin.IN) a=p.value() del p,a result=tap.prog_close() del tap gc.collect() #os.mount(SDCard(slot=3),"/sd") # BUG, won't work self.init_spi() # because of ecp5.prog() spi.deinit() self.spi_request.irq(trigger=Pin.IRQ_FALLING, handler=self.irq_handler_ref) self.irq_handler(0) # handle stuck IRQ if filename.endswith(".nes") \ or filename.endswith(".snes") \ or filename.endswith(".smc") \ or filename.endswith(".sfc"): import ld_nes s=ld_nes.ld_nes(self.spi,self.cs) s.ctrl(1) s.ctrl(0) s.load_stream(open(filename,"rb")) del s gc.collect() self.enable[0]=0 self.osd_enable(0) if filename.startswith("/sd/ti99_4a/") and filename.endswith(".bin"): import ld_ti99_4a s=ld_ti99_4a.ld_ti99_4a(self.spi,self.cs) s.load_rom_auto(open(filename,"rb"),filename) del s gc.collect() self.enable[0]=0 self.osd_enable(0) if (filename.startswith("/sd/msx") and filename.endswith(".rom")) \ or filename.endswith(".mx1"): import ld_msx s=ld_msx.ld_msx(self.spi,self.cs) s.load_msx_rom(open(filename,"rb")) del s gc.collect() self.enable[0]=0 self.osd_enable(0) if filename.endswith(".z80"): self.enable[0]=0 self.osd_enable(0) import ld_zxspectrum s=ld_zxspectrum.ld_zxspectrum(self.spi,self.cs) s.loadz80(filename) del s gc.collect() if filename.endswith(".ora") or filename.endswith(".orao"): self.enable[0]=0 self.osd_enable(0) import ld_orao s=ld_orao.ld_orao(self.spi,self.cs) s.loadorao(filename) del s gc.collect() if filename.endswith(".vsf"): self.enable[0]=0 self.osd_enable(0) import ld_vic20 s=ld_vic20.ld_vic20(self.spi,self.cs) s.loadvsf(filename) del s gc.collect() if filename.endswith(".prg"): self.enable[0]=0 self.osd_enable(0) import ld_vic20 s=ld_vic20.ld_vic20(self.spi,self.cs) s.loadprg(filename) del s gc.collect() if filename.endswith(".cas"): self.enable[0]=0 self.osd_enable(0) import ld_trs80 s=ld_trs80.ld_trs80(self.spi,self.cs) s.loadcas(filename) del s gc.collect() if filename.endswith(".cmd"): self.enable[0]=0 self.osd_enable(0) import ld_trs80 s=ld_trs80.ld_trs80(self.spi,self.cs) s.loadcmd(filename) del s gc.collect() @micropython.viper def osd_enable(self, en:int): pena = ptr8(addressof(self.spi_enable_osd)) pena[5] = en&1 self.cs.on() self.spi.write(self.spi_enable_osd) self.cs.off() @micropython.viper def osd_print(self, x:int, y:int, i:int, text): p8msg=ptr8(addressof(self.spi_write_osd)) a=0xF000+(x&63)+((y&31)<<6) p8msg[2]=i p8msg[3]=a>>8 p8msg[4]=a self.cs.on() self.spi.write(self.spi_write_osd) self.spi.write(text) self.cs.off() @micropython.viper def osd_cls(self): p8msg=ptr8(addressof(self.spi_write_osd)) p8msg[3]=0xF0 p8msg[4]=0 self.cs.on() self.spi.write(self.spi_write_osd) self.spi.read(1280,32) self.cs.off() # y is actual line on the screen def show_dir_line(self, y): if y < 0 or y >= self.screen_y: return mark = 0 invert = 0 if y == self.fb_cursor - self.fb_topitem: mark = 1 invert = 1 if y == self.fb_selected - self.fb_topitem: mark = 2 i = y+self.fb_topitem if i >= len(self.direntries): self.osd_print(0,y,0,"%64s" % "") return if self.direntries[i][1]: # directory self.osd_print(0,y,invert,"%c%-57s D" % (self.mark[mark],self.direntries[i][0])) else: # file mantissa = self.direntries[i][2] exponent = 0 while mantissa >= 1024: mantissa >>= 10 exponent += 1 self.osd_print(0,y,invert,"%c%-57s %4d%c" % (self.mark[mark],self.direntries[i][0], mantissa, self.exp_names[exponent])) def show_dir(self): for i in range(self.screen_y): self.show_dir_line(i) def move_dir_cursor(self, step): oldcursor = self.fb_cursor if step == 1: if self.fb_cursor < len(self.direntries)-1: self.fb_cursor += 1 if step == -1: if self.fb_cursor > 0: self.fb_cursor -= 1 if oldcursor != self.fb_cursor: screen_line = self.fb_cursor - self.fb_topitem if screen_line >= 0 and screen_line < self.screen_y: # move cursor inside screen, no scroll self.show_dir_line(oldcursor - self.fb_topitem) # no highlight self.show_dir_line(screen_line) # highlight else: # scroll if screen_line < 0: # cursor going up screen_line = 0 if self.fb_topitem > 0: self.fb_topitem -= 1 self.show_dir() else: # cursor going down screen_line = self.screen_y-1 if self.fb_topitem+self.screen_y < len(self.direntries): self.fb_topitem += 1 self.show_dir() def read_dir(self): self.direntries = [] ls = sorted(os.listdir(self.cwd)) for fname in ls: stat = os.stat(self.fullpath(fname)) if stat[0] & 0o170000 == 0o040000: self.direntries.append([fname,1,0]) # directory for fname in ls: stat = os.stat(self.fullpath(fname)) if stat[0] & 0o170000 != 0o040000: self.direntries.append([fname,0,stat[6]]) # file def mdv_skip_preamble(self,n:int)->int: i=0 j=0 found=0 while j<n: if self.diskfile.readinto(self.mdv_byte): if self.mdv_byte[0]==0xFF: self.diskfile.readinto(self.mdv_byte) if self.mdv_byte[0]==0xFF and i>=10: found=1 i+=2 break else: i=0 else: if self.mdv_byte[0]==0: i+=1 else: i=0 j+=1 else: # EOF, make it circular self.diskfile.seek(0) print("MDV: wraparound",self.data_buf[2:12].decode("utf-8")) if found: return i return 0 def mdv_refill_buf(self): if self.mdv_skip_preamble(1000): self.diskfile.readinto(self.data_buf) # skip block if header doesn't start with 0xFF #i=0 #while self.data_buf[0]!=0xFF and i<254: # self.mdv_skip_preamble(1000) # self.diskfile.readinto(self.data_buf) # i+=1 #print(self.data_buf[1],self.data_buf[2:12]) # block number, volume name #print(self.data_buf[0:16],self.data_buf[28:32],self.data_buf[40:44]) # block number, volume name else: print("MDV: preamble not found") @micropython.viper def mdv_checksum(self,a:int,b:int)->int: p8b=ptr8(addressof(self.data_buf)) c=0xF0F i=a while i<b: c+=p8b[i] i+=1 return c&0xFFFF def mdv_read(self): if self.mdv_phase[0]: self.ctrl(8) # R_cpu_control[3]=1 self.cs.on() self.spi.write(self.spi_send_mdv_bram) self.spi.write(self.data_buf[0:16]) self.cs.off() self.ctrl(0) else: self.ctrl(8) self.cs.on() self.spi.write(self.spi_send_mdv_bram) self.spi.write(self.data_buf[28:32]) self.spi.write(self.data_buf[40:554]) self.cs.off() self.ctrl(0) self.led.on() self.mdv_refill_buf() # fix checksum in broken MDV images #c=self.mdv_checksum(0,14) #self.data_buf[14]=c #self.data_buf[15]=c>>8 c=self.mdv_checksum(28,30) self.data_buf[30]=c self.data_buf[31]=c>>8 #c=self.mdv_checksum(40,552) #self.data_buf[552]=c #self.data_buf[553]=c>>8 self.led.off() self.mdv_phase[0]^=1 # NOTE: this can be used for debugging #def osd(self, a): # if len(a) > 0: # enable = 1 # else: # enable = 0 # self.cs.on() # self.spi.write(bytearray([0,0xFE,0,0,0,enable])) # enable OSD # self.cs.off() # if enable: # self.cs.on() # self.spi.write(bytearray([0,0xFD,0,0,0])) # write content # self.spi.write(bytearray(a)) # write content # self.cs.off() def ctrl(self,i): self.cs.on() self.spi.write(bytearray([0, 0xFF, 0xFF, 0xFF, 0xFF, i])) self.cs.off() def peek(self,addr,length): self.ctrl(4) self.ctrl(6) self.cs.on() self.spi.write(bytearray([1,(addr >> 24) & 0xFF, (addr >> 16) & 0xFF, (addr >> 8) & 0xFF, addr & 0xFF, 0])) b=bytearray(length) self.spi.readinto(b) self.cs.off() self.ctrl(4) self.ctrl(0) return b def poke(self,addr,data): self.ctrl(4) self.ctrl(6) self.cs.on() self.spi.write(bytearray([0,(addr >> 24) & 0xFF, (addr >> 16) & 0xFF, (addr >> 8) & 0xFF, addr & 0xFF])) self.spi.write(data) self.cs.off() self.ctrl(4) self.ctrl(0)
class 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)
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
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()
class MeshNet(RadioDriver): def __init__(self, domain, address, **kwargs): super(MeshNet, self).__init__(domain, **kwargs) self.address = address self._meshlock = rlock() self._transmit_queue = queue() self._receive_queue = queue() self._hwmp_sequence_number = 0 self._hwmp_sequence_lock = lock() self._route_lock = rlock() self._promiscuous = False self._debug = False self._announce_thread = None # Defines routes to nodes self._routes = {} self._packet_errors_crc = 0 self._packet_received = 0 self._packet_transmitted = 0 self._packet_ignored = 0 self._packet_lock = rlock() self._gateway = kwargs['gateway'] if 'gateway' in kwargs else False if self._gateway: self._announce_interval = float( kwargs['interval'] ) if 'interval' in kwargs else _ANOUNCE_DEFAULT_INTERVAL else: self._announce_interval = 0 self._PROTOCOLS = { RouteAnnounce.PROTOCOL_ID: RouteAnnounce, RouteRequest.PROTOCOL_ID: RouteRequest, RouteError.PROTOCOL_ID: RouteError, None: DataPacket, # Data packet protocol id is a wildcard } # In promiscuous mode, all received packets are dropped into the receive queue, as well # as being processed. def set_promiscuous(self, mode=True): self._promiscuous = mode def set_debug(self, mode=True): self._debug = mode def start(self): self._spi = SPI(baudrate=10000000, polarity=0, phase=0, bits=8, firstbit=SPI.MSB, sck=Pin(_SX127x_SCK, Pin.OUT, Pin.PULL_DOWN), mosi=Pin(_SX127x_MOSI, Pin.OUT, Pin.PULL_UP), miso=Pin(_SX127x_MISO, Pin.IN, Pin.PULL_UP)) self._ss = Pin(_SX127x_SS, Pin.OUT) self._reset = Pin(_SX127x_RESET, Pin.OUT) self._dio_table = [ Pin(_SX127x_DIO0, Pin.IN), Pin(_SX127x_DIO1, Pin.IN), Pin(_SX127x_DIO2, Pin.IN) ] self._ping_count = 0 self._power = None # not True nor False # Perform base class init # super(MeshNet, self).start(_SX127x_WANTED_VERSION) super().start(_SX127x_WANTED_VERSION, activate=False) # Set power state self.set_power() # A timer than can be started to do retries; not started until needed self._retry_routerequests_thread = thread( run=self._retry_routerequests, stack=8192) self._retry_routerequests_thread.start(timeout=0.5) # Start announce if requested if self._gateway: self.announce_start(self._announce_interval / 1000.0) def announce_start(self, interval): print("Announce gateway every %.1f seconds" % interval) self._announce_thread = thread(run=self._announce, stack=8192) self._announce_thread.start(interval=interval) def _announce(self, t, interval): countdown = interval # Only sleep a second at a time so we can be killed fairly quickly while t.running: sleep(1) countdown -= 1 if countdown <= 0: countdown += interval packet = RouteAnnounce(target=BROADCAST_ADDRESS, nexthop=BROADCAST_ADDRESS, sequence=self._create_sequence_number(), gateway_flag=self._gateway) self.send_packet(packet) return 0 # Return the protocol wrapper or Data is not otherwise defined def get_protocol_wrapper(self, protocol): return self._PROTOCOLS[ protocol] if protocol in self._PROTOCOLS else self._PROTOCOLS[None] # Remove a root def remove_route(self, address): with self._route_lock: if address in self._routes: del (self_routes[address]) # Update a route. If route is not defined, create it. If defined but metric is better or sequence is different, update it. # Return True if new or updated route def update_route(self, target, nexthop, sequence, metric=_MAX_METRIC, gateway_flag=False, force=False): with self._route_lock: if force or target not in self._routes or self._routes[ target].is_expired(): # Create new route route = Route(target=target, nexthop=nexthop, sequence=sequence, metric=metric, gateway_flag=gateway_flag) self._routes[target] = route if self._debug: print("Created %s" % str(route)) # Else if we are forcing creation, or the sequence number is different or the metric is better, create a new route elif sequence != self._routes[target].sequence( ) or metric < self._routes[target].metric(): # Update route route = self._routes[target] route.nexthop(nexthop) route.metric(metric) route.sequence(sequence) route.update_lifetime() if self._debug: print("Updated %s" % str(route)) else: # No route to host route = None return route # Default to search routes table def find_route(self, address): with self._route_lock: return self._routes[ address] if address in self._routes and not self._routes[ address].is_expired() else None # Reset device def reset(self): self._reset.value(0) sleep(0.1) self._reset.value(1) # Read register from SPI port def read_register(self, address): value = int.from_bytes(self._spi_transfer(address & 0x7F), 'big') # print("%02x from %02x" % (value, address)) return value # Write register to SPI port def write_register(self, address, value): # print("write %02x to %02x" % (value, address)) self._spi_transfer(address | 0x80, value) def _spi_transfer(self, address, value=0): response = bytearray(1) self._ss.value(0) self._spi.write(bytes([address])) self._spi.write_readinto(bytes([value]), response) self._ss.value(1) return response # Read block of data from SPI port def read_buffer(self, address, length): try: response = bytearray(length) self._ss.value(0) self._spi.write(bytes([address & 0x7F])) self._spi.readinto(response) self._ss.value(1) except: # No room. gc now gc.collect() response = None return response # Write block of data to SPI port def write_buffer(self, address, buffer, size): self._ss.value(0) self._spi.write(bytes([address | 0x80])) self._spi.write(memoryview(buffer)[0:size]) self._ss.value(1) def attach_interrupt(self, dio, edge, callback): # if self._debug: # print("attach_interrupt dio %d rising %s with callback %s" % (dio, edge, callback)) if dio < 0 or dio >= len(self._dio_table): raise Exception("DIO %d out of range (0..%d)" % (dio, len(self._dio_table) - 1)) edge = Pin.IRQ_RISING if edge else Pin.IRQ_FALLING self._dio_table[dio].irq(handler=callback, trigger=edge if callback else 0) # Enwrap the packet with a class object for the particular message type def wrap_packet(self, data, rssi=None): return self.get_protocol_wrapper(data[_HEADER_PROTOCOL[0]])(load=data, rssi=rssi) # Duplicate packet with new private data def dup_packet(self, packet): return self.wrap_packet(bytearray(packet.data()), rssi=packet.rssi()) def onReceive(self, data, crc_ok, rssi): if crc_ok: packet = self.wrap_packet(data, rssi) nexthop = packet.nexthop() if self._debug: print("Received: %s" % (str(packet))) # In promiscuous, deliver to receiver so it can handle it (but not process it) if self._promiscuous: packet_copy = self.dup_packet(packet) packet_copy.promiscuous(True) self.put_receive_packet(packet_copy) if nexthop == BROADCAST_ADDRESS or nexthop == self.address: self._packet_received += 1 # To us or broadcasted packet.process(self) else: # It is non processed self._packet_ignored += 1 else: self._packet_errors_crc += 1 def receive_packet(self): gc.collect() return self._receive_queue.get() def put_receive_packet(self, packet): self._receive_queue.put(packet) gc.collect() # Finished transmitting - see if we can transmit another # If we have another packet, return it to caller. def onTransmit(self): # if self._debug: # print("onTransmit complete") self._packet_transmitted += 1 # Delete top packet in queue packet = self._transmit_queue.get(wait=0) del packet # Return head of queue if one exists packet = self._transmit_queue.head() gc.collect() return packet.data() if packet else None def _create_sequence_number(self): with self._hwmp_sequence_lock: self._hwmp_sequence_number += 1 return self._hwmp_sequence_number # A packet with a source and destination is ready to transmit. # Label the from address and if no to address, attempt to route # If ttl is true, decrease ttl and discard packet if 0 def send_packet(self, packet, ttl=False): if ttl and packet.ttl(packet.ttl() - 1) == 0: # Packet has expired if self._debug: print("Expired: %s" % str(packet)) else: # Label packets as coming from us packet.previous(self.address) # print("%s: set previous to %d" % (str(packet), self.address)) # Label as originating here if no previous assigned source address if packet.source() == NULL_ADDRESS: packet.source(self.address) # print("%s: set source to %d" % (str(packet), self.address)) # If the nexthop is NULL, then we compute next hop based on route table. # If no route table, create pending NULL route and cache packet for later retransmission. if packet.nexthop() == NULL_ADDRESS: with self._route_lock: # Look up the route to the destination route = self.find_route(packet.target()) # If no route, create a dummy route and queue the results if route == None: # Unknown route. Create a NULL route awaiting RouteAnnounce route = self.update_route( target=packet.target(), nexthop=NULL_ADDRESS, sequence=self._create_sequence_number(), force=True) # Save packet in route for later delivery route.put_pending_packet(packet) if self._debug: print("Routing %s" % str(packet)) request = RouteRequest(target=packet.target(), previous=self.address, source=self.address, sequence=route.sequence(), metric=1, gateway_flag=self._gateway) # This will queue repeats of this request until cancelled route.set_pending_routerequest(request) elif route.nexthop() == NULL_ADDRESS: # We still have a pending route, so append packet to queue only. request = None route.put_pending_packet(packet) else: # Label the destination for the packet packet.nexthop(route.nexthop()) request = packet # Transmit the request if we created one or else the actual packet packet = request if packet: # # TODO: we may need another method to restart transmit queue other than looking # and transmit queue length. A 'transmitting' flag (protected by meshlock) that # is True if transmitting of packet is in progress. Cleared on onTransmit when # queue has become empty. # # This may need to be implemented to allow stalling transmission for windows of # reception. A timer will then restart the queue if items remain within it. # # if self._debug: # print("sending: %s" % str(packet)) with self._meshlock: # print("Appending to queue: %s" % packet.decode()) self._transmit_queue.put(packet) if len(self._transmit_queue) == 1: self.transmit_packet(packet.data()) if self._debug: print("Transmitted: %s" % str(packet)) # A thread to check all routes and those with resend the packets for those with retry requests def _retry_routerequests(self, thread, timeout): while thread.running: sleep(timeout) with self._route_lock: # Go through all routes looking for those with pending requests. for target in list(self._routes): route = self._routes[target] # If route is expired, remove it if route.is_expired(): # Clean up route del (self._routes[target]) # Otherwise if it has a pending request, resend it else: packet = route.get_pending_routerequest() if packet: if self._debug: print("Retry route request %s" % str(packet)) self.send_packet(packet) def stop(self): # Stop announce if running if self._announce_thread: self._announce_thread.stop() self._announce_thread.wait() self._announce_thread = None if self._retry_routerequest_thread != None: self._retry_routerequest_thread.stop() self._retry_routerequest_thread.wait() self._retry_routerequest_thread = None super(MeshNet, self).stop() # Shut down power self.set_power(False) print("MeshNet handler close called") # Close DIO interrupts for dio in self._dio_table: dio.irq(handler=None, trigger=0) # Close SPI channel if opened if self._spi: self._spi.deinit() self._spi = None def set_power(self, power=True): # print("set_power %s" % power) if power != self._power: self._power = power # Call base class super(MeshNet, self).set_power(power) def __del__(self): self.stop() def dump(self): item = 0 for reg in range(0x43): print("%02x: %02x" % (reg, self.read_register(reg)), end=" " if item != 7 else "\n") item = (item + 1) % 8 print("")
class 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
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
#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
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)
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
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])
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)