Quick reference for the PSoC6™¶
The CY8CPROTO-062-4343W PSoC6™ Board.
Below is a quick reference for PSoC6™ boards. If it is your first time working with this port it may be useful to get an overview of the microcontroller:
Warning
The PSoC6™ port is still in an early stage of development. It is expected any MicroPython built-in library to be supported, but not all libraries, modules and features have been implemented yet. For those modules relying on platform and hardware dependencies, only the listed and documented in this quick reference are certainyly supported. Check here for a complete list of currently enabled or implemented modules as well as not yet implemented functionality.
Please, consider opening an issue or discussion on GitHub for any clarification required on available features or requests for missing ones.
General board control¶
The MicroPython REPL is accessed via the USB serial port. Paste mode (ctrl-E) is useful to paste a large slab of Python code into the REPL.
This port implements most of the methods described in the machine
module. Tab-completion is useful to
find out what methods an instantiated object has.
The machine
module:
import machine
machine.freq() # get the current frequency of the CPU
- ::
from machine import Bitstream timing = [1000, 7000, 5000, 2500] #timing (high_time_0, low_time_0, high_time_1, low_time_1)in ns buf = bytearray([0xAB]) #buffer data bitstream(‘P13_6’, 0, timing, buf) # bitstrem buffer data with timing through pin P13_6
All timings greater than 1500 ns works and the accuracy of the timing is +/- 400ns. Supported timing_ns ranges below 1500 ns is [500, 1125, 800, 750]
Delay and timing¶
Use the time
module:
import time
time.sleep(1) # sleep for 1 second
time.sleep_ms(500) # sleep for 500 milliseconds
time.sleep_us(10) # sleep for 10 microseconds
start = time.ticks_ms() # get millisecond counter
delta = time.ticks_diff(time.ticks_ms(), start) # compute time difference
start = time.ticks_us() # get microsecond counter
delta = time.ticks_diff(time.ticks_us(), start) # compute time difference
Pins and GPIO¶
Most of the methods (functions) and constants given in the machine.Pin class have been implemented in this port. Any functions in addition to those or function calls with ambiguous list of parameters have been documented here with suitable examples.
The constructor¶
The constructor can be called in different flavors and configurations based on the number of arguments (parameters) passed.
An instance of the machine.Pin
class can be created by invoking the constructor with all the necessary parameters to fully configure the Pin
.
from machine import Pin
p0 = Pin('P13_7', Pin.OUT, Pin.PULL_DOWN, value=0) # create output pin on pin P13_7,
# with pull-down resistor enabled,
# with initial value 0 (low)
Additionally, with any combination of parameters (except the Pin number or id
which should be passed mandatorily), a machine.Pin
object with various configuration levels can be instantiated. In these cases, the Pin.init()
function has to be called proactively to set the other necessary configurations, as needed.
Moreover, a pre-configured pin object can be repurposed by calling the Pin.init()
function.
from machine import Pin
p0 = Pin('P13_7') # create pin object for pin P13_7.
p0.init(Pin.OUT, Pin.PULL_DOWN) # set pin as output and enable pull-down resistor.
p0.low() # set value low.
Similar to CPython, the parameters can be passed in any order if keywords are used. On the other hand, in case of a non-keyword assignment if a parameter is not to be set, a None
is to be passed in its place.
from machine import Pin
p0 = Pin(id='P13_7', value=0, pull=Pin.PULL_DOWN, mode=Pin.OUT) # create output pin on pin P13_7,
# with pull-down resistor enabled,
# with initial value 0 (low)
p1 = Pin('P0_0', Pin.OUT, None, value=1) # create output pin on pin P0_0,
# with pull as NONE,
# with initial value 1 (high)
Note that the parameters such as value
can only be passed as keyword arguments.
Note
- The following constructor arguments are NOT supported in this port:
drive
. This configuration is automatically handled by the constructor and abstracted to the user.alt
. Alternative functionality is directly handled by the respective machine peripherals classes.
Methods¶
- Pin.irq(handler=None, trigger=Pin.IRQ_FALLING | Pin.IRQ_RISING)¶
Here two arguments should be passed mandatorily.
Trigger can be Pin.IRQ_FALLING
or Pin.IRQ_RISING
or PIN.IRQ_FALLING||PIN.IRQ_RISING
.
from machine import Pin
p0 = Pin('P0_4', value=1, pull=Pin.PULL_UP, mode=Pin.IN)
p1 = Pin('P13_7', value=0, pull=Pin.PULL_DOWN, mode=Pin.OUT)
p0.irq(handler=lambda t:p1.high(),trigger=Pin.IRQ_RISING) #configure an IRQ callback function P1.high() when there is a rising edge on pin object p0.
- Pin.toggle()¶
Set pin value to its complement.
Signal¶
There’s a higher-level abstraction machine.Signal
which can be used to invert a pin. Useful for illuminating active-low LEDs
using on()
or value(1)
.
Warning
The machine.Signal value() getter functionality is not supported in this port, and the returned value is undefined. This is the same behavior as the machine.Pin value() for Pin.OUT mode, which is the object supporting the Signal object.
Software I2C bus¶
Software I2C (using bit-banging) works on all output-capable pins, and is accessed via machine.SoftI2C
from machine import Pin, SoftI2C
i2c = SoftI2C(scl='P5_0', sda='P5_1', freq=100000)
i2c.scan() # scan for devices
i2c.readfrom(0x3a, 4) # read 4 bytes from device with address 0x3a
i2c.writeto(0x3a, '12') # write '12' to device with address 0x3a
buf = bytearray(10) # create a buffer with 10 bytes
i2c.writeto(0x3a, buf) # write the given buffer to the peripheral
Hardware I2C bus¶
Hardware I2C works on the following listed pair of I2C pins
Default |
||
---|---|---|
scl |
P6_0 |
P9_0 |
sda |
P6_1 |
P9_1 |
The driver is accessed via machine.I2C
The constructor¶
An instance of the machine.I2C
class can be created by invoking the constructor with all the
necessary parameters to fully configure the I2C
. By invoking the constructor, I2C peripheral is
initialized and configured to work in master mode. The maximum supported frequency is 1 MHz.
from machine import I2C
i2c = I2C(scl='P6_0', sda='P6_1', freq=400000)
The scl
and sda
pins are the only mandatory arguments. The frequency is optional, and if not passed will be set to 400KHz by default.
Note
The timeout
option is currently not implemented in this port.
from machine import I2C
i2c = I2C(scl='P6_0', sda='P6_1') #I2C is initialized & configured with default frequency
from machine import I2C
i2c = I2C(scl='P9_0', sda='P9_1', freq=400000) #I2C is initialised & configured with given scl, sda pins & frequency
Methods¶
All the methods(functions) given in machine.I2C class have been implemented in this port except:
- I2C.init()¶
All the initialization & configurations are handled by the constructor. Hence init()
is not required.
Hardware I2C bus slave¶
The PSoC6™ port offers an additional class to implement an I2C slave device. An I2C master node connected to the slave can exchange data over I2C for the configured slave address and frequency.
Warning
This is not part of the core MicroPython libraries. Therefore, not mapping any existing machine class API and neither supported by other ports.
Note
The constructor¶
- class I2CSlave(scl, sda, addr, freq)¶
Constructs and returns a new I2C slave object using the following parameters.
- Required arguments:
scl should be a pin name supporting the SCL functionality.
sda should be a pin name supporting the SDA functionality.
addr should be an 8 bits unsigned integer.
- Optional arguments:
freq should be an integer which sets the maximum frequency for SCL. If not passed, by default is set to 400KHz.
- Example:
from machine import I2CSlave i2c_slave = I2CSlave(scl="P6_4", sda="P6_5", addr=0x45)
Methods¶
- I2CSlave.deinit()¶
Deinitialises the I2C slave.
- I2CSlave.conf_receive_buffer(buf)¶
Configures the reception buf on an I2C slave. This is the buffer to which the master writes data to. The user needs to setup a new buffer every time the buffer has been used up.
- I2CSlave.conf_transmit_buffer(buf)¶
Configures the transmission buf on an I2C slave. This is the buffer from which the master reads data from. The user needs to setup a new buffer every time the buffer has been used up.
- I2CSlave.irq(callback, events, priority)¶
Enables interrupts and the function handlers to be called upon different I2C bus events.
- Required arguments:
callback should be a function handler that will be executed upon an interrupt event. The callback takes one argument which is the event causing the interrupt.
events is the I2C bus events that will be triggering the interrupt. Multiple ones can be configured by ORing the following constants:
- Optional arguments:
priority should be an unsigned integer with the priority of the interrupt.
- Example:
def cback(event): if event == (event & I2CSlave.WR_EVENT): print("i2c wr event") i2c_slave.irq(callback=cback, events=(I2CSlave.WR_EVENT | I2CSlave.WR_CMPLT_EVENT), priority=1)
- I2CSlave.irq_disable()¶
Disables the I2C slave interrupts.
Real time clock (RTC)¶
See machine.RTC
from machine import RTC
rtc = RTC()
rtc.init((2023, 1, 1, 0, 0, 0, 0, 0)) # initialize 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.now() # get current date and time
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 last calculated actual value.
Network Module¶
The network
module
See network.WLAN
For some methods and constants, the PSoC6 network port implements certain specialization and slightly different behavior. This is explained in this section.
Methods¶
- WLAN.scan(ssid=None, bssid=None)¶
The scan option accepts the following filters as keyword arguments, removing from scan results any network not matching these parameters values:
ssid
bssid
- WLAN.status('param')¶
Warning
The function does not provide status of the connection. Use the
active()
for that purpose. Any errors or failure are communicated when using the corresponding enable/disable or connect/disconnect functions.- The following query parameters are allowed:
rssi
. Only for STA.stations
. List of connected stations (only for AP).
- WLAN.config('param')¶
- WLAN.config(param=value, ...)
Among the suggested parameters of the general network WLAN API, for this port, only these are available:
AP & STA query parameters
channel
ssid
security`
key/password
. Only for default AP key.mac
AP set parameters
channel
ssid
security`
key/password
. Only for default AP key.
STA has no configurable parameter.
Constants¶
Security modes constants:
Note
Power modes configuration not implemented.
Here is a function you can run (or put in your boot.py file) to automatically connect to your WiFi network:
def network_connect() :
import network
from utime import sleep,sleep_ms
wlan = network.WLAN(network.STA_IF)
if wlan.isconnected():
print('[Network] already connected')
return
# enable and connect wlan
wlan.active(True)
wlan.connect('<ssid>','<key>')
# wait for connection to establish
sleep(5)
for i in range(0,100):
if not wlan.isconnected() and wlan.status() >= 0:
print("[Network] Waiting to connect..")
sleep(2)
# check connection
if not wlan.isconnected():
print("[Network] Connection failed!")
else:
print(wlan.ifconfig())
PWM (pulse width modulation)¶
PWM can be enabled on all output capable pins.The frequency can range from 1Hz to 100MHz. As the frequency increases the PWM resolution decreases. Refer PSoC 6 MCU: CY8C62x8, CY8C62xA Datasheet for additional details regarding board specific PWM.
Use the machine.PWM class:
The constructor can be called by passing the required arguments. All initialization and the configurations are handled by the constructor. Create PWM object using
pwm = PWM('P9_0', freq=50, duty_u16=8192) # PWM is initialised for the given pin with respective frequency & duty cycle given as raw value.
pwm1 = PWM('P9_1', freq=50, duty_ns=1000) # PWM is initialised for the given pin with respective frequency & duty cycle given in nanoseconds.
All four arguments has to be passed manadatorily to create PWM object. duty_u16 or duty_ns should be specified at a time.
from machine import PWM
pwm = PWM('P9_0', freq=50, duty_u16=8192)
print(pwm) # view PWM settings
pwm.freq() # get current frequency
pwm.freq(100) # set PWM frequency to 100 Hz
pwm.duty_u16() # get current duty cycle, range 0-65535
pwm.duty_u16(8192) # set duty cycle from 0 to 65535 as a ratio of duty_u16/65535, now 25%
pwm.duty_ns() # get current pulse width in ns
pwm.duty_ns(1000) # set the current pulse width in ns from 0 to 1000000000/freq
pwm.init(freq=90,duty_us=100) # Modify the settings of PWM object
pwm.deinit() # Deinitialisation of PWM pin
Note
invert functionality is not enabled in this port.
Software SPI bus¶
Software SPI (using bit-banging) works on all pins, and is accessed via the machine.SoftSPI class:
from machine import Pin, SoftSPI
# construct a SoftSPI bus on the given pins
# polarity is the idle state of SCK
# phase=0 means sample on the first edge of SCK, phase=1 means the second edge
spi = SoftSPI(baudrate=100_000, polarity=1, phase=0, sck='P0_2', mosi='P0_0', miso='P0_1')
spi.init(baudrate=200000) # set the baudrate
spi.read(10) # read 10 bytes on MISO
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
Warning
Currently all of sck
, mosi
and miso
must be specified when
initialising Software SPI.
Hardware SPI bus¶
Hardware SPI works on the following listed pair of SPI pins.
Default |
|||
---|---|---|---|
MOSI |
P9_0 |
P6_0 |
P10_0 |
MISO |
P9_1 |
P6_1 |
P10_1 |
SCK |
P9_2 |
P6_2 |
P10_2 |
Refer PSoC 6 MCU: CY8C62x8, CY8C62xA Datasheet
for additional details regarding all the SPI capable pins. The pins sck
, mosi
and miso
must be specified when
initialising Software SPI.
The driver is accessed via machine.SPI
The constructor¶
An instance of the machine.SPI
class can be created by invoking the constructor with all the
necessary parameters to fully configure and initialize the SPI
. By invoking the constructor with no additional parameters
SPI object is created with default settings or settings of previous initialization if any.
from machine import SPI
spi = SPI(0, sck='P11_2', mosi='P11_0', miso='P11_1') # Default assignment: id=0, SCK=P11_2 ,MOSI=P11_0, MISO=P11_1
spi.init()
Management of a CS signal should happen in user code (via machine.Pin class).
from machine import Pin
cs = Pin('P9_3', mode=Pin.OUT, value=1) # Create chip-select on pin P9_3
cs(0) # select the peripheral
Here, id=0
should be passed mandatorily which selects the master
mode operation.
If the constructor is called with any additional parameters then SPI object is created & initialised.
spi = SPI(0, sck='P11_2', mosi='P11_0', miso='P11_1', baudrate=2000000) #object is created & initialised with baudrate=2000000 & default parameters
spi = SPI(0, baudrate=1500000, polarity=1, phase=1, bits=8, firstbit=SPI.LSB, sck='P11_2', mosi='P11_0', miso='P11_1')
Methods¶
All the methods(functions) given in machine.SPI class have been implemented in this port.
Timers¶
Hardware timer is supported.
Use the machine.Timer
class:
from machine import Timer
import time
tim = Timer(0) #Default assignment: period=9999, frequency=10000
tim.init(period=2000, mode=Timer.ONE_SHOT, callback=lambda t:print(2)) #mode=Timer.PERIODIC in case of periodic timer
Here id=0 should be passed mandatorily.
ADC (analog to digital conversion)¶
On the PSoC6, a single ADC block with id - ‘0’ is available. The ADC functionality is available on the following pins : “P10_0” - “P10_5”.
Use the machine.ADC class:
from machine import ADC
adc = ADC("P10_0") # create an ADC object on ADC pin
val = adc.read_u16() # read a raw analog value in the range 0-65535
val = adc.read_uv() # read an analog value in micro volts
The PSoC6 port also supports machine.ADCBlock API to have control over the ADC configuration. Currently
PSoC6 supports only 1 12-bit SAR ADC with the following channel to pin mapping and the defaults are set accordingly:
Channel |
Pin |
---|---|
0 |
P10_0 |
1 |
P10_1 |
2 |
P10_2 |
3 |
P10_3 |
4 |
P10_4 |
5 |
P10_5 |
Note
Arbitrary connection of ADC channels to GPIO is not supported. Specifying a pin that is not connected to this block, or specifying a mismatched channel and pin, will raise an exception.
To use the APIs:
from machine import ADCBlock
adcBlock = ADCBlock(0, bits=11) # create an ADCBlock 0 object
adc = adcBlock.connect(0, "P10_0") # connect channel 0 to pin P10_0
val = adc.read_uv() # read an analog value in micro volts
Note
The ADC block supports only 11 bits resolution. If bits are not passed, by default 11 bits is considered.
Warning
When the input to ADC pin is connected to GND, it may not return value 0 as digitized output. This is a known issue and needs fix in low-level API’s.
I2S bus¶
See machine.I2S.
PSoC6 implements additional method which is explained below.
Methods¶
- I2S.stop()¶
This method is to stop either the transmission or reception. This function should be called by user once the readinto or write is done.
from machine import I2S, Pin
import array
buf=bytearray(10) #Initilase buffer with required values for transmission & as empty buffer for reception
audio_out = I2S(0, sck="P13_1", ws="P13_2", sd="P13_3", mode=I2S.TX, bits=16, format=I2S.STEREO, rate=22050, ibuf=20000) #create I2S object
num_written = audio_out.write(buf) # write buffer of audio samples to I2S device
audio_in = I2S(1, sck="P5_4", ws="P5_5", sd="P5_6", mode=I2S.RX, bits=16, format=I2S.STEREO, rate=22050, ibuf=20000) # create I2S object
num_read = audio_in.readinto(buf)# fill buffer with audio samples from I2S device
audio_out.stop() # stop transmission
audio_in.stop() # stop reception
PSoc6 supports two I2S Bus. So id could be either 0 or 1. Please note that if both instances are running together then the sample rate should be either 8/16/32/48 or 22.05/44.01. Both the set of frequencies can’t work together(For eg eg. 8 & 22.01 will cause error) Supported sample rates are 8KHz, 16KHz, 32KHz, 48KHz, 22.05KHz, 44.1KHz. PSoC6 supports only STEREO Mode.
Note
I2S Blocking mode is only supported