.. _psoc_edge_quickref: .. include:: links.rst Quick reference for the PSOC™ Edge =================================== .. image:: img/kit-pse84-ai.png :alt: KIT_PSE84_AI board :width: 540px The `PSOC™ Edge E84 AI Kit `_. Below is a quick reference for PSOC™ Edge E84 boards. If it is your first time working with this port it may be useful to get an overview of the microcontroller: .. toctree:: :maxdepth: 1 :includehidden: general.rst installation.rst mpy-usage.rst Pins and GPIO ------------- See :ref:`machine.Pin ` for the complete Pin API reference. This section focuses on the specific PSOC™ Edge port variations and particularities. The constructor ^^^^^^^^^^^^^^^ The controller pin naming follows the nomenclature ``P_``, where: - ```` is a numeric identifier for the port (e.g., 0-21 for the PSOC™ Edge E84) - ```` is the pin number within that port. Use the respective board pinout diagram to find the available pins and their locations. This is the ``id`` that needs to be passed to the constructor in one of the following formats: - As a **string label**, single or double quoted: ``'P_'`` or ``"P_"`` - A **pre-instantiated object** ``Pin.cpu.`` or ``Pin.board.``. :: from machine import Pin p_in = Pin('P0_0', Pin.IN) p_out = Pin("P7_0", Pin.OUT, value=False) p = Pin(Pin.cpu.P17_1, Pin.OPEN_DRAIN) The pre-instantiated object can be used directly without calling the constructor. Instead, you can use ``init()`` to configure it. :: from machine import Pin pin = Pin.cpu.P17_0 pin.init(mode=Pin.IN) .. tip:: Use the REPL interface to discover the available user pins, using tab for completion: >>> from machine import Pin >>> Pin.cpu.P P10_5 P10_7 P11_3 P12_3 P13_0 P13_1 P13_2 P13_3 P13_4 P13_5 P13_6 P13_7 P14_0 P14_1 P14_2 P14_3 P14_4 P14_5 P14_6 P14_7 P15_0 P15_1 P15_2 P15_3 P15_4 P15_5 P15_6 P15_7 P16_0 P16_1 P16_2 P16_3 P16_4 P16_5 P16_6 P16_7 P17_0 P17_1 P17_2 P17_3 P17_4 P17_5 P17_7 P20_3 P20_4 P20_5 P20_6 P20_7 P21_1 P21_2 P21_3 P21_4 P21_5 P21_6 P21_7 P3_0 P3_1 P6_4 P6_6 P7_0 P7_7 P8_0 P8_1 P8_5 P8_6 P9_0 P9_1 P9_2 P9_3 >>> from machine import Pin >>> Pin.board. AMIC1_CTB_INN AMIC1_CTB_INP AMIC1_CTB_OUT AMIC1_CTB_REF AMIC2_CTB_INN AMIC2_CTB_INP AMIC2_CTB_OUT AMIC2_CTB_REF I2C_SCL_1V8 I2C_SCL_3V3 I2C_SDA_1V8 I2C_SDA_3V3 I2S_TX_FYSYNC I2S_TX_MCK I2S_TX_SCK I2S_TX_SD I3C_SCL I3C_SDA IMU0_INT IMU1_INT MAG_INT PDM_CLK PDM_DATA PRESS_SENS_INT RADAR_INT RADAR_RESET RADAR_SPI_CLK RADAR_SPI_CS RADAR_SPI_MISO RADAR_SPI_MOSI SERIAL_INT0 SERIAL_INT1 SERIAL_INT2 SERIAL_INT3 USER_BUTTON USER_LED1 USER_LED2 USER_LED_B USER_LED_G USER_LED_R In addition to the supported ``pull`` configuration values, ``PULL_UP_DOWN`` is also available in this port. The ``drive`` parameter accepts up to 8 levels, which set the following drive strength for the pin: - ``DRIVE_0``: 1mA/2mA drive current (normal/high speed IO) - ``DRIVE_1``: 2mA/4mA drive current (normal/high speed IO) - ``DRIVE_2``: 3mA/6mA drive current (normal/high speed IO) - ``DRIVE_3``: 4mA/8mA drive current (normal/high speed IO) - ``DRIVE_4``: 5mA/10mA drive current (normal/high speed IO) - ``DRIVE_5``: 6mA/12mA drive current (normal/high speed IO) - ``DRIVE_6``: 7mA/14mA drive current (normal/high speed IO) - ``DRIVE_7``: 8mA/16mA drive current (normal/high speed IO) For more information about drive strength, check the PSOC™ Edge `Datasheet `_ and `Architecture Reference Manual `_. .. note:: The following constructor arguments and/or configuration values are NOT supported in this port: - ``alt``: Alternate functionality is not supported. - ``mode``: ``Pin.ALT``, ``Pin.ALT_OPEN_DRAIN``, and ``Pin.ANALOG`` modes are not supported. Methods ^^^^^^^ .. method:: Pin.irq(handler=None, trigger=(Pin.IRQ_FALLING | Pin.IRQ_RISING), priority=7) The following parameters have port-specific behavior: - ``priority``: Priority values range from 7 (lowest) to 0 (highest). Default is 7. .. note:: All pins on the same port share the same interrupt line. Therefore, only one priority can be set for all pins on the same port. If multiple pins configure interrupts for the same port, the highest priority will be used. If only one pin is configured for an interrupt, its priority can be reconfigured to any value. .. note:: The following ``irq()`` features are not supported in this port: - ``trigger``: The ``Pin.IRQ_LOW_LEVEL`` and ``Pin.IRQ_HIGH_LEVEL`` triggers are not supported. - ``wake``: The wake parameter is currently not supported. - ``hard``: This parameter is ignored. It can be passed but currently has no effect. .. note:: **None** of the non-core methods from the Pin API are currently implemented for this port. Real time clock (RTC) --------------------- See :ref:`machine.RTC `: :: from machine import RTC import time irq_counter = 0 def cback(event): global irq_counter irq_counter += 1 rtc = RTC() rtc.init((2023, 1, 1, 0, 0, 0, 0, 0)) # initialise rtc with specific date and time, # eg. 2023/1/1 00:00:00 rtc.datetime((2017, 8, 23, 2, 12, 48, 0, 0)) # set a specific date and # time, eg. 2017/8/23 1:12:48 rtc.datetime() # get date and time rtc.irq(trigger=RTC.ALARM0, handler=cback) rtc.alarm(1000, repeat=False) # set one-shot short alarm in ms rtc.alarm_left() # Read the time left for the alarm to expire time.sleep_ms(1008) # wait sufficient time print(irq_counter) # Check irq counter rtc.irq(trigger=RTC.ALARM0, handler=cback) rtc.alarm(3000, repeat=True) # set periodic short alarm in ms rtc.cancel() # cancel the alarm rtc.irq(trigger=RTC.ALARM0, handler=cback) rtc.alarm((2023, 1, 1, 0, 0, 1, 0, 0), repeat=False) # set one-shot longer duration alarm rtc.memory(b"hello") # write bytes into RTC user memory rtc.memory() # read bytes from RTC user memory .. note:: Setting a random week day in 'wday' field is not valid. The underlying library implements the logic to always calculate the right weekday based on the year, date and month passed. However, datetime() will not raise an error for this but rather re-write the field with the last calculated actual value. .. note:: RTC API behavior on this port has the following specifics: - ``RTC()`` is a singleton constructor with no ``id`` or additional constructor arguments. - ``rtc.irq()`` accepts alarm trigger ``0`` (``RTC.ALARM0``); ``wake`` is not implemented. - ``rtc.alarm()`` accepts ``time`` and optional ``repeat``; no positional alarm ``id`` argument is used. - Input ``weekday`` in datetime tuples is ignored and hardware computes the weekday from date fields. - The current ``rtc.memory([data])`` maximum payload on KIT_PSE84_AI is 28 bytes. .. warning:: RTC alarm timing on this port has second-level resolution. Millisecond alarm values are accepted, but are rounded up to whole seconds internally. Hardware I2C bus ---------------- See :ref:`machine.I2C ` and :ref:`machine.I2CTarget ` for the complete I2C API reference. Hardware I2C is available on the PSOC™ Edge E84 using the SCB (Serial Communication Block) peripheral. The port supports both controller (master) and target (slave) modes. .. note:: External pull-up resistors (typically 4.7kΩ) are required on both SCL and SDA lines. Only one I2C instance (controller or target) can be active at a time, as both modes share the same SCB peripheral and pins. Controller mode (Master) ^^^^^^^^^^^^^^^^^^^^^^^^ Use the ``I2C`` class for controller (master) operations:: from machine import I2C i2c = I2C(scl='P17_0', sda='P17_1', freq=100000) Constructor arguments: - ``id``: I2C bus number (currently only 0 is available). **This parameter is ignored**. - ``scl``: SCL pin (string 'P_' or Pin object). - ``sda``: SDA pin (string 'P_' or Pin object). - ``freq``: I2C clock frequency in Hz. Supported: 100000 (100kHz) or 400000 (400kHz). Default is 400000. - ``timeout``: Transfer timeout in microseconds. Must be > 0. Default is 50000 (50ms). The ``scl`` and ``sda`` pins are the only mandatory arguments. Methods ~~~~~~~ All the methods(functions) given in :ref:`machine.I2C ` class have been implemented in this port except: .. method:: I2C.init() Additionally, the following functions are enabled: .. method:: I2C.deinit() Target mode (Slave) ^^^^^^^^^^^^^^^^^^^ Use the ``I2CTarget`` class for target (slave) operations:: from machine import I2CTarget mem = bytearray([0xAA, 0xBB, 0xCC, 0xDD]) i2c_target = I2CTarget(scl="P17_0", sda="P17_1", addr=0x43, mem=mem) The I2CTarget implementation on PSoC Edge has the following port-specific details: **Memory Addressing:** - ``addrsize``: 7-bit and 10-bit addresses are accepted. - ``mem_addrsize``: Only ``0`` is supported. - EEPROM-like internal address phase (8/16/24/32-bit ``mem_addrsize``) is not implemented. **IRQ triggers:** The ``hard`` argument in ``i2c_target.irq(..., hard=True)`` is supported. When using hard IRQ callbacks, keep handlers short and allocation-free. Current implementation notes for IRQ data-phase events: - PSoC hardware events are limited and do not map 1:1 to MicroPython ``IRQ_READ_REQ``/ ``IRQ_WRITE_REQ`` semantics. - Treat these two flags as optional notifications, not required control points. - In the current PSOC Edge port implementation, data-path behavior is strongly tied to ``mem`` buffer configuration. After address match, hardware/port state handling performs most data movement automatically (when ``mem`` is configured). Practical guidance for this port: - For robust target-mode operation, configure ``mem`` and size it to cover expected master write payloads. - With ``mem`` configured, explicit ``I2CTarget.readinto()``/ ``I2CTarget.write()`` calls are usually optional, and mainly needed for custom protocol handling in IRQ callbacks. Hardware SPI bus ---------------- See :ref:`machine.SPI ` for the complete SPI master API reference. ``SPITarget`` is a PSOC™ Edge-specific class and has no separate library page. Hardware SPI on PSOC™ Edge uses the SCB peripheral and supports both controller (master) and target (slave) modes. Controller mode (Master) ^^^^^^^^^^^^^^^^^^^^^^^^ Use the ``SPI`` class for controller-mode transfers:: from machine import SPI, Pin spi = SPI( baudrate=1_000_000, polarity=0, phase=0, bits=8, firstbit=SPI.MSB, sck='P16_0', mosi='P16_1', miso='P16_2', ) cs = Pin('P16_3', Pin.OUT, value=1) tx = b'\x9f\x00\x00\x00' rx = bytearray(4) cs(0) spi.write_readinto(tx, rx) cs(1) print(rx) Constructor arguments: - ``id``: accepted for API compatibility, currently ignored. - ``baudrate``: SPI clock in Hz. Default is ``1_000_000``. - ``polarity`` / ``phase``: must be ``0`` or ``1``. - ``bits``: only ``8`` is supported. - ``firstbit``: ``SPI.MSB`` or ``SPI.LSB``. - ``sck``, ``mosi``, ``miso``: required SPI signal pins. .. note:: Chip select (CS/SS) is controlled by user code with ``machine.Pin``. It is not driven automatically by ``machine.SPI``. Target mode (Slave) ^^^^^^^^^^^^^^^^^^^ Use the ``SPITarget`` class for target-mode communication:: from machine import SPITarget spi_t = SPITarget( sck='P16_0', mosi='P16_1', miso='P16_2', ssel='P16_3', polarity=0, phase=0, bits=8, firstbit=SPITarget.MSB, ) tx = b'\x11\x22\x33\x44' rx = bytearray(4) spi_t.write_readinto(tx, rx) spi_t.deinit() Constructor arguments: - ``sck``: SPI clock pin. - ``mosi``: target TX pin. - ``miso``: target RX pin. - ``ssel``: chip-select input pin. - ``polarity`` / ``phase``: must be ``0`` or ``1``. - ``bits``: only ``8`` is supported. - ``firstbit``: ``SPITarget.MSB`` or ``SPITarget.LSB``. Constants: - ``SPITarget.MSB``: most-significant-bit first. - ``SPITarget.LSB``: least-significant-bit first. Methods: .. method:: SPITarget.readinto(buf) Read bytes from the SPI target RX FIFO into writable buffer ``buf``. Returns the number of bytes read. Raises ``OSError`` on timeout. .. method:: SPITarget.write(buf) Write bytes from buffer ``buf`` to the SPI target TX FIFO. Returns the number of bytes written. Raises ``OSError`` on timeout. .. method:: SPITarget.write_readinto(tx_buf, rx_buf) Full-duplex transfer in target mode. Writes bytes from ``tx_buf`` and reads bytes into ``rx_buf``. ``tx_buf`` and ``rx_buf`` must have the same length. Returns the number of bytes transferred. Raises ``ValueError`` if buffer lengths differ. Raises ``OSError`` on timeout. .. method:: SPITarget.deinit() Deinitialise the SPITarget instance and release its underlying SCB resource. The SPITarget implementation on PSoC Edge has the following port-specific details: - ``sck``, ``mosi``, ``miso``, and ``ssel`` are all required. - ``bits`` is fixed to 8. - ``readinto()``, ``write()``, and ``write_readinto()`` are blocking and use an internal timeout. - A single ``SPITarget`` instance is supported in this port configuration. Inter-Processor Communication (IPC) ------------------------------------- The ``IPC`` class provides message-passing between the two cores using the hardware IPC pipe peripheral. This is a PSOC™ Edge-specific module — it is not part of the standard MicroPython ``machine`` API. Currently, for this port, IPC is supported to enable communication from the CM33 core to the CM55 core. .. note:: IPC is only available on builds compiled with the ``MULTI_CORE`` flag. The module is not present on single-core firmware images. By default it is enabled for this port. .. note:: Only CM33 → CM55 communication is currently supported. The CM33 core always acts as the initiating side; the CM55 firmware must be built and deployed separately. The constructor ^^^^^^^^^^^^^^^ :: from machine import IPC ipc = IPC(src_core=IPC.CM33, target_core=IPC.CM55) Constructor arguments: - ``src_core``: Source core ID. Currently only ``IPC.CM33`` (``0``) is supported. Default is ``IPC.CM33``. - ``target_core``: Destination core ID. Currently only ``IPC.CM55`` (``1``) is supported. Default is ``IPC.CM55``. Up to 5 IPC object instances can be created and this is enforced by the constructor. Each instance can be configured with different source and target cores, but currently only CM33 → CM55 communication is supported regardless of the constructor arguments. Core ID and command constants ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The following class-level constants are available on every ``IPC`` object: - ``IPC.CM33``: Core ID for the CM33 core (``0``). - ``IPC.CM55``: Core ID for the CM55 core (``1``). - ``IPC.CMD_START``: Pre-defined start command (``0x82``). - ``IPC.CMD_STOP``: Pre-defined stop command (``0x83``). Methods ^^^^^^^ .. method:: IPC.init() Initialise the IPC pipe hardware. Must be called once after construction and before any other IPC operation:: ipc.init() .. method:: IPC.register_client(client_id, callback, endpoint_id, endpoint_addr) Register a client on the source endpoint so that incoming messages addressed to ``client_id`` invoke ``callback``. - ``client_id``: Unique client identifier (``0``–``7``). Each registered client must have a unique ID. - ``callback``: A callable invoked as ``callback(client)`` when a message arrives for this client. The callback receives a single ``IPCClient`` object with the following read-only attributes: - ``client.cmd``: Command byte received from the target core. - ``client.value``: 32-bit value received from the target core. - ``client.id``: The client ID this message was addressed to. The callback is deferred and runs in the MicroPython scheduler context (not in the ISR), so it is safe to allocate memory and call any MicroPython function. - ``endpoint_id``: Endpoint index for source core. - ``endpoint_addr``: Endpoint address for the source core. Returns ``True`` on success, ``False`` otherwise. Maximum of 8 clients can be registered per endpoint. This is a PSOC™ Edge-specific limit. Example — registering two independent services:: def svc1_cb(client): print("Service1 received cmd=0x{:02X}".format(client.cmd)) def svc2_cb(client): print("Service2 received cmd=0x{:02X}".format(client.cmd)) ipc.register_client(3, svc1_cb, 1, 1) # Service 1 ipc.register_client(4, svc2_cb, 1, 1) # Service 2 .. method:: IPC.enable_core(core_id=IPC.CM55) Boot the target core and wait for it to start. Currently only ``IPC.CM55`` is supported. - ``core_id``: The core to enable. Default is ``IPC.CM55``. Calling this when the CM55 is already running prints a notice and returns immediately. Allow a short delay after this call for CM55 initialisation to complete before sending messages:: ipc.enable_core(IPC.CM55) time.sleep(1) # Wait for CM55 firmware to initialise .. method:: IPC.send(cmd, value=0, client_id) Send a message to a client on the target core. - ``cmd``: Command byte. Use ``IPC.CMD_START``, ``IPC.CMD_STOP``, or any application-defined value. - ``value``: Optional 32-bit data payload. Default is ``0``. - ``client_id``: Client ID on the target core. Raises ``OSError`` if the send fails after the maximum number of retries ipc.send(IPC.CMD_START, 0, 5) # Send CMD_START to CM55 client 5 ipc.send(IPC.CMD_STOP, 0, 6) # Send CMD_STOP to CM55 client 6 .. method:: IPC.is_busy([core_id]) Check whether the IPC channel for the given core is currently locked. - ``core_id``: ``IPC.CM33`` or ``IPC.CM55`` Returns ``True`` if the channel is busy, ``False`` otherwise:: if not ipc.is_busy(): ipc.send(IPC.CMD_START, 0, 5) # Check a specific core explicitly if ipc.is_busy(IPC.CM55): print("CM33 channel is locked") Complete example ^^^^^^^^^^^^^^^^ The following example demonstrates two independent services sharing a single IPC endpoint, with each service using its own client IDs on both the CM33 and CM55 sides:: import time from machine import IPC ipc = IPC(src_core=IPC.CM33, target_core=IPC.CM55) ipc.init() # Per-service receive state svc1 = {"received": False, "cmd": None} svc2 = {"received": False, "cmd": None} def svc1_cb(client): svc1["received"] = True svc1["cmd"] = client.cmd def svc2_cb(client): svc2["received"] = True svc2["cmd"] = client.cmd # Register both services on the CM33 endpoint (endpoint_id=1, endpoint_addr=1) ipc.register_client(3, svc1_cb, 1, 1) # Service 1 -- CM33 client_id=3 ipc.register_client(4, svc2_cb, 1, 1) # Service 2 -- CM33 client_id=4 # Boot CM55 and wait for it to initialise ipc.enable_core(IPC.CM55) time.sleep(1) # Send CMD_START to CM55 Service 1 (client_id=5); echo comes back to CM33 client_id=3 ipc.send(IPC.CMD_START, 0, 5) # Send CMD_STOP to CM55 Service 2 (client_id=6); echo comes back to CM33 client_id=4 ipc.send(IPC.CMD_STOP, 0, 6) PDM - PCM bus -------------- PDM/PCM is a asynchronous operation used to connect digital audio devices. At the physical level, a bus consists of 2 lines: CLK, DATA. .. warning:: This is not part of the core MicroPython libraries. Therefore, not mapping any existing machine class API and neither supported by other ports. PDM-PCM objects can be created and initialized using:: from machine import PDM_PCM clk_pin = "P8_5" data_pin = "P8_6" pdm_pcm = PDM_PCM( sck=clk_pin, data=data_pin, sample_rate=16000, bits=PDM_PCM.BITS_16, format=PDM_PCM.MONO, gain=0, ) 2 modes of operation are supported: - blocking - non-blocking Constructor ^^^^^^^^^^^^ .. class:: PDM_PCM(clk, data, sample_rate, bits, format, gain, ibuf) Keyword-only parameters that are supported on this port: - ``clk`` is a pin object for the clock line - ``data`` is a pin object for the data line - ``sample_rate`` specifies audio sampling rate. **Currently only 16 KHz sample rate is supported**. - ``bits`` specifies word length - 16 and 32 being accepted values. - ``format`` specifies channel format - STEREO or MONO. - ``gain`` is the gain in dB. In case of STEREO format is applies to both channels. The range goes from -103 to +83 dB with 6 dB step. Values will be rounded to the closest step. - ``ibuf`` is the size of the internal ring buffer (in bytes) storing the incoming audio data stream. Default is 20000. Methods ^^^^^^^^ .. method:: PDM_PCM.init(clk, data, sample_rate, bits, format, gain, ibuf) Initializes the PDM_PCM hardware as per the specified parameters. See the constructor for details on the supported parameters. .. method:: PDM_PCM.deinit() Deinitializes the PDM_PCM object. .. method:: PDM_PCM.readinto(buf) Read audio samples into the buffer specified by ``buf``. ``buf`` must support the buffer protocol, such as bytearray or array. Sample size can be calculated as (PCM_bits/8) * (format_size); where format_size is 2(stereo mode) and 1(mono mode). Returns number of bytes read. .. method:: PDM_PCM.irq(handler) Set the callback.``handler`` is called when ``buf`` becomes full (``readinto`` method). Setting a callback changes the ``readinto`` method to non-blocking operation. ``handler`` is called in the context of the MicroPython scheduler. .. method:: PDM_PCM.gain([gain]) Set/get the gain for of all the channels. The gain is specified in dB. The range goes from -103 to +83 dB with 6 dB step. Values will be rounded to the closest step. Default is 0 dB. Constants ^^^^^^^^^^ .. data:: PDM_PCM.STEREO for initialising the PDM_PCM ``format`` to stereo .. data:: PDM_PCM.MONO for initialising the PDM_PCM ``format`` to mono .. data:: PDM_PCM.BITS_16 for initialising the PDM_PCM ``bits`` to 16 .. data:: PDM_PCM.BITS_32 for initialising the PDM_PCM ``bits`` to 32 UART ---- See :ref:`machine.UART `. The following specialization applies to this port: Constructor ^^^^^^^^^^^^ .. class:: UART(id) The following parameters are supported with limited configuration: - ``bits``. Only 8 bits. These are planned for future implementation, but yet unavailable: - ``rts`` - ``cts`` - ``flow`` .. Note:: These parameters are not implemented: - ``id`` - ``txbuf`` - ``invert`` Methods ^^^^^^^ .. method:: UART.init(baudrate=9600, bits=8, parity=None, stop=1, *, ...) The same parameters as the constructor are supported, with the same limitations. PWM ---- Pulse Width Modulation (PWM) signal output on the PSOC™ Edge. See :ref:`machine.PWM ` for the standard MicroPython PWM API. .. note:: On this port, PWM output is available exclusively on pins **P16_1** through **P16_7**. Each pin is backed by an independent TCPWM0 counter, so each instance can run at a different frequency simultaneously. A maximum of **7** PWM instances can be active at the same time. .. note:: The base clock driving all counters is **1 MHz** (derived from the 100 MHz PCLK via a shared 16-bit divider). This gives a frequency range of **1 Hz - 500 kHz** and a period resolution of 1 µs. A PWM object can be created and started using:: from machine import PWM pwm = PWM("P16_1", freq=1000, duty_u16=32767) # 1 kHz, 50 % duty cycle Constructor ^^^^^^^^^^^^ .. class:: PWM(pin, *, freq, duty_u16, duty_ns, invert=False) Construct and immediately start a PWM signal on *pin*. - ``pin`` — the output pin. Accepts a string label (``"P16_1"``), a ``Pin.cpu.`` object, or a ``Pin.board.`` object. The pin must be one of the PWM-capable pins P16_1 - P16_7. - ``freq`` — output frequency in Hz (**required**). Must be in the range 1 - 500 000. - ``duty_u16`` — duty cycle as a 16-bit unsigned integer 0-65535 (0 % - ~100 %). - ``duty_ns`` — duty cycle (high pulse width) in nanoseconds. Must not exceed the period (``1 000 000 000 / freq`` ns). - ``invert`` — if ``True``, inverts the output polarity. Default is ``False``. Raises ``ValueError`` if the pin does not support PWM, if a PWM instance for that pin already exists without calling ``deinit()`` first, or if the frequency or duty arguments are out of range. Complete example ^^^^^^^^^^^^^^^^ :: from machine import PWM # Start a 1 kHz signal at 50 % duty cycle on P16_1 pwm = PWM("P16_1", freq=1000, duty_u16=32767) print(pwm) # PWM(Pin.cpu.P16_1, freq=1000, duty=50.00%) # Read back the current settings print(pwm.freq()) # 1000 print(pwm.duty_u16()) # 32767 print(pwm.duty_ns()) # 500000 (0.5 ms duty cycle at 1 kHz) # Update frequency only — duty ratio preserved pwm.freq(2000) print(pwm.freq()) # 2000 # Switch to nanosecond duty control: 250 µs duty cycle at 2 kHz = 50 % pwm.duty_ns(250000) print(pwm.duty_ns()) # 250000 # Reconfigure completely pwm.init(freq=500, duty_u16=16383) # 500 Hz, 25 % # Stop and release the pin pwm.deinit() Timer ----- Hardware timer using the TCPWM0 peripheral on the PSOC™ Edge. See :ref:`machine.Timer ` for the standard MicroPython Timer API. .. note:: This port provides **4** independent hardware timer instances (IDs ``0``, ``1``, ``2``, ``3``), backed by TCPWM0 Group 0 counters 3, 5, 6, and 2 respectively. Only one instance per ID can exist at a time; constructing a second ``Timer(id)`` without calling ``deinit()`` first raises a ``ValueError``. .. note:: The timer clock is **1 MHz** (shared with the system tick). This means: - The minimum resolvable period is **1 µs**. - With ``period`` the minimum value is **1 ms** and the maximum is **4 294 967 ms** (~49.7 days). - With ``freq``, the minimum frequency is **1 Hz** and the maximum is **1 000 000 Hz** (1 MHz). - Computed period ticks must fit in a **32-bit** counter (1 - 4 294 967 295); values outside this range raise a ``ValueError``. A timer can be created and started using:: from machine import Timer def tick(timer): print("tick") tim = Timer(0, mode=Timer.PERIODIC, period=500, callback=tick) Constructor ^^^^^^^^^^^^ .. class:: Timer(id, *, mode=Timer.PERIODIC, period, freq, callback, hard=False) Construct a hardware timer. - ``id`` — timer instance identifier. Must be **0**, **1**, **2**, or **3**. - ``mode`` — ``Timer.ONE_SHOT`` or ``Timer.PERIODIC``. Default is ``Timer.PERIODIC``. - ``period`` — timer period in **milliseconds**. Must be ``> 0``. Either ``period`` or ``freq`` must be provided; supplying both raises a ``ValueError``. - ``freq`` — timer frequency in Hz. Must be ``> 0``. Takes precedence over ``period`` when both are supplied. - ``callback`` — a callable invoked on each timer expiry. Receives the ``Timer`` object as its only argument. Must be a callable; passing a non-callable raises a ``ValueError``. - ``hard`` — must be a ``bool``. If ``True``, the callback runs directly in the hardware IRQ context (no scheduler). Heap allocation inside the callback will raise ``MemoryError``; use ``micropython.alloc_emergency_exception_buf()`` before arming a hard timer. Default is ``False`` (soft callback, deferred via the MicroPython scheduler). Raises ``ValueError`` if: - ``id`` is outside ``0``-``3``. - A ``Timer`` with the given ``id`` already exists (call ``deinit()`` first). - Neither ``freq`` nor ``period`` is provided. - ``period`` is ``0`` or negative. - ``freq`` is ``0`` or negative. - The computed period exceeds the 32-bit counter range (e.g. ``freq=2_000_000``). - ``hard`` is not a ``bool``. - ``callback`` is not callable. Complete example ^^^^^^^^^^^^^^^^ :: from machine import Timer import micropython import time # --- Periodic soft timer (default) --- count = 0 def on_tick(timer): global count count += 1 print("tick", count) tim = Timer(0, mode=Timer.PERIODIC, period=500, callback=on_tick) time.sleep(3) # let it fire ~6 times tim.deinit() # --- One-shot timer --- def on_done(timer): print("one-shot fired") tim = Timer(1, mode=Timer.ONE_SHOT, period=1000, callback=on_done) time.sleep(2) tim.deinit() # --- Frequency-based timer --- def on_freq(timer): print("2 Hz tick") tim = Timer(2, mode=Timer.PERIODIC, freq=2, callback=on_freq) time.sleep(3) tim.deinit() # --- Hard IRQ timer (allocation-free callback required) --- micropython.alloc_emergency_exception_buf(100) fired = [False] def on_hard(timer): fired[0] = True # list index write is allocation-free tim = Timer(0, mode=Timer.ONE_SHOT, period=100, callback=on_hard, hard=True) time.sleep_ms(200) tim.deinit() print("hard fired:", fired[0]) Network Module -------------- The :mod:`network` module. See :ref:`network.WLAN `: This section documents only the PSOC™ Edge-specific WLAN behaviour. .. method:: WLAN.active([is_active]) Interface activation differs by interface type: * STA: ``WLAN.active()`` returns the current connection state. Setting the STA active state is not supported. * AP: ``WLAN.active()`` returns whether the access point is running, and ``WLAN.active(True/False)`` starts or stops it. .. method:: WLAN.scan(ssid=None, bssid=None) STA only. Accepts one optional filter per call: * ``ssid`` — return only networks matching this SSID. * ``bssid`` — return only networks matching this BSSID (bytes of length 6). Passing both filters together is not supported. .. method:: WLAN.connect(ssid, key=None, *, bssid=None) Connect the STA interface to an access point. * ``ssid`` is required. * ``key`` is optional and may be omitted for open networks. * ``bssid`` is optional and, if provided, must be 6 bytes. .. method:: WLAN.isconnected() Return the interface connection state: * STA: ``True`` when connected to an access point. * AP: ``True`` when the access point is running and at least one station is associated. .. method:: WLAN.status('param') .. warning:: This function does not provide link-up/down connection state. Use ``isconnected()`` to check connection status. The following query parameters are allowed: * ``rssi`` — received signal strength in dBm (STA only). * ``stations`` — list of connected station MAC addresses as bytes (AP only). .. method:: WLAN.config('param') WLAN.config(param=value, ...) Supported configuration parameters are: * AP query parameters: - ``channel`` - ``ssid`` - ``security`` - ``key`` / ``password`` — only queryable when the default AP password is set. - ``mac`` * AP set parameters: - ``channel`` - ``ssid`` - ``security`` - ``key`` / ``password`` * STA query parameters: - ``channel`` - ``ssid`` - ``security`` - ``mac`` .. note:: STA has no settable config parameters. Use ``WLAN.connect(ssid, key)`` to associate to a network. Constants ^^^^^^^^^ Security mode constants: .. data:: WLAN.OPEN WLAN.WEP WLAN.WPA WLAN.WPA2 WLAN.WPA3 WLAN.WPA2_WPA_PSK WLAN.SEC_UNKNOWN The following helper can be run manually (or placed in ``boot.py``) to connect to a Wi-Fi network: :: def network_connect(ssid, key, timeout_s=30): import network import time wlan = network.WLAN(network.STA_IF) if wlan.isconnected(): print("[Network] Already connected") print(wlan.ifconfig()) return wlan.connect(ssid, key) for _ in range(timeout_s): if wlan.isconnected(): print("[Network] Connected") print(wlan.ifconfig()) return time.sleep(1) print("[Network] Connection failed")