This is the documentation for the latest development branch of MicroPython and may refer to features that are not available in released versions.

If you are looking for the documentation for a specific release, use the drop-down menu on the left and select the desired version.

Quick reference for the PSOC™ Edge

KIT_PSE84_AI board

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:

Pins and GPIO

See 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<port>_<pin>, where:

  • <port> is a numeric identifier for the port (e.g., 0-21 for the PSOC™ Edge E84)

  • <pin> 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<port>_<pin>' or "P<port>_<pin>"

  • A pre-instantiated object Pin.cpu.<pin> or Pin.board.<pin>.

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

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 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 machine.I2C and 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.

Warning

The KIT_PSE84_AI board has only one hardware I2C peripheral with fixed pins: P17_0 (SCL) and P17_1 (SDA). These pins cannot be changed. If you specify custom pins in the constructor, they will be ignored and a warning message will be printed.

Controller mode (Master)

Use the I2C class for controller (master) operations:

from machine import I2C

# Create I2C - uses fixed pins P17_0 (SCL) and P17_1 (SDA)
i2c = I2C(freq=400000)

# With custom timeout (default is 50000us = 50ms)
i2c = I2C(freq=100000, timeout=100000)  # 100ms timeout

# Pin parameters are optional but ignored on KIT_PSE84_AI
i2c = I2C(scl='P17_0', sda='P17_1', freq=100000)  # You can only use P17_0/P17_1

Constructor arguments:

  • id: I2C bus number (currently only 0 is available). This parameter is ignored.

  • 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).

  • scl: SCL pin (string ‘P<port>_<pin>’ or Pin object). Ignored on KIT_PSE84_AI - always uses P17_0. Prints warning if specified.

  • sda: SDA pin (string ‘P<port>_<pin>’ or Pin object). Ignored on KIT_PSE84_AI - always uses P17_1. Prints warning if specified.

Target mode (Slave)

The I2CTarget implementation on PSoC Edge has the following port-specific details:

Fixed pins (KIT_PSE84_AI):
  • SCL: P17_0

  • SDA: P17_1

  • Custom pin parameters are ignored with a warning message

Memory addressing:
  • mem_addrsize: Only 0 is supported (no memory addressing)

  • EEPROM-like addressing (8/16/24/32 bit) is not yet implemented

Supported IRQ triggers:

Only transaction-level events are supported (soft IRQ only):

  • I2CTarget.IRQ_END_READ: Triggered when master completes reading from slave

  • I2CTarget.IRQ_END_WRITE: Triggered when master completes writing to slave

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

IPC.init()

Initialise the IPC pipe hardware. Must be called once after construction and before any other IPC operation:

ipc.init()
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 (07). Each registered client must have a unique ID.

  • callback: A callable invoked as callback(cmd, value, client_id) when a message arrives for this client. The callback parameters are:

  1. cmd: Command byte received from the target core.

  2. value: 32-bit value received from the target core.

  3. client_id: The client ID that the message was sent to (same as the client_id argument passed to this method).

  • 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(cmd, val, cid):
    print("Service1 received cmd=0x{:02X}".format(cmd))

def svc2_cb(cmd, val, cid):
    print("Service2 received cmd=0x{:02X}".format(cmd))

ipc.register_client(3, svc1_cb, 1, 1)   # Service 1
ipc.register_client(4, svc2_cb, 1, 1)   # Service 2
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
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

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(cmd, val, cid):
    svc1["received"] = True
    svc1["cmd"] = cmd

def svc2_cb(cmd, val, cid):
    svc2["received"] = True
    svc2["cmd"] = 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)