Skip to content

Commit

Permalink
zephyr: Allow using devicetree node labels to construct machine objects.
Browse files Browse the repository at this point in the history
Zephyr v3.7.0 added a new feature to allow getting devices by their
devicetree node labels. Use this feature in the MicroPython Zephyr port
to simplify constructing machine module objects, including Pin, SPI,
I2C, and UART. It's still possible to use the more verbose device names
(e.g., gpio@400ff040, i2c@40066000, spi@4002c000), but now we can also
use their devicetree node labels (e.g., gpiob, i2c0, spi0).

Node labels aren't standardized across all SoC families because they
generally try to follow their respective SoC hardware user manual naming
convention, however many boards define common labels for devices routed
to Arduino headers (e.g., arduino_i2c, arduino_serial, and arduino_spi).
That means I2C("arduino_i2c") will work on quite a few boards (>100 in
the main Zephyr tree).

Signed-off-by: Maureen Helm <[email protected]>
  • Loading branch information
MaureenHelm committed Oct 2, 2024
1 parent f33df71 commit a9803f4
Show file tree
Hide file tree
Showing 4 changed files with 28 additions and 23 deletions.
23 changes: 10 additions & 13 deletions docs/zephyr/quickref.rst
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,7 @@ Use the :ref:`machine.Pin <machine.Pin>` class::

from machine import Pin

gpio1 = "gpio@400ff040" # GPIO1 device name
gpio2 = "gpio@400ff080" # GPIO2 device name

pin = Pin((gpio1, 21), Pin.IN) # create input pin on GPIO1
pin = Pin(("gpiob", 21), Pin.IN) # create input pin on GPIO port B
print(pin) # print pin port and number

pin.init(Pin.OUT, Pin.PULL_UP, value=1) # reinitialize pin
Expand All @@ -50,14 +47,14 @@ Use the :ref:`machine.Pin <machine.Pin>` class::
pin.on() # set pin to high
pin.off() # set pin to low

pin = Pin((gpio1, 21), Pin.IN) # create input pin on GPIO1
pin = Pin(("gpiob", 21), Pin.IN) # create input pin on GPIO port B

pin = Pin((gpio1, 21), Pin.OUT, value=1) # set pin high on creation
pin = Pin(("gpiob", 21), Pin.OUT, value=1) # set pin high on creation

pin = Pin((gpio1, 21), Pin.IN, Pin.PULL_UP) # enable internal pull-up resistor
pin = Pin(("gpiob", 21), Pin.IN, Pin.PULL_UP) # enable internal pull-up resistor

switch = Pin((gpio2, 6), Pin.IN) # create input pin for a switch
switch.irq(lambda t: print("SW2 changed")) # enable an interrupt when switch state is changed
switch = Pin(("gpioc", 6), Pin.IN) # create input pin for a switch
switch.irq(lambda t: print("SW2 changed")) # enable an interrupt when switch state is changed

Hardware I2C bus
----------------
Expand All @@ -66,7 +63,7 @@ Hardware I2C is accessed via the :ref:`machine.I2C <machine.I2C>` class::

from machine import I2C

i2c = I2C("i2c@40066000") # construct an i2c bus
i2c = I2C("i2c0") # construct an i2c bus
print(i2c) # print device name

i2c.scan() # scan the device for available I2C slaves
Expand All @@ -87,11 +84,11 @@ Hardware SPI is accessed via the :ref:`machine.SPI <machine.SPI>` class::

from machine import SPI

spi = SPI("spi@4002c000") # construct a spi bus with default configuration
spi = SPI("spi0") # construct a spi bus with default configuration
spi.init(baudrate=100000, polarity=0, phase=0, bits=8, firstbit=SPI.MSB) # set configuration

# equivalently, construct spi bus and set configuration at the same time
spi = SPI("spi@4002c000", baudrate=100000, polarity=0, phase=0, bits=8, firstbit=SPI.MSB)
spi = SPI("spi0", baudrate=100000, polarity=0, phase=0, bits=8, firstbit=SPI.MSB)
print(spi) # print device name and bus configuration

spi.read(4) # read 4 bytes on MISO
Expand Down Expand Up @@ -149,7 +146,7 @@ Use the :ref:`zsensor.Sensor <zsensor.Sensor>` class to access sensor data::
import zsensor
from zsensor import Sensor

accel = Sensor("fxos8700@1d") # create sensor object for the accelerometer
accel = Sensor("fxos8700") # create sensor object for the accelerometer

accel.measure() # obtain a measurement reading from the accelerometer

Expand Down
20 changes: 10 additions & 10 deletions ports/zephyr/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,26 +107,26 @@ To blink an LED:
import time
from machine import Pin

LED = Pin(("gpio@400ff040", 21), Pin.OUT)
LED = Pin(("gpiob", 21), Pin.OUT)
while True:
LED.value(1)
time.sleep(0.5)
LED.value(0)
time.sleep(0.5)

The above code uses an LED location for a FRDM-K64F board (port B, pin 21;
following Zephyr conventions port are identified by "GPIO_x", where *x*
starts from 0). You will need to adjust it for another board (using board's
reference materials). To execute the above sample, copy it to clipboard, in
MicroPython REPL enter "paste mode" using Ctrl+E, paste clipboard, press
Ctrl+D to finish paste mode and start execution.
following Zephyr conventions port are identified by their devicetree node
label. You will need to adjust it for another board (using board's reference
materials). To execute the above sample, copy it to clipboard, in MicroPython
REPL enter "paste mode" using Ctrl+E, paste clipboard, press Ctrl+D to finish
paste mode and start execution.

To respond to Pin change IRQs, on a FRDM-K64F board run:

from machine import Pin

SW2 = Pin(("gpio@400ff080", 6), Pin.IN)
SW3 = Pin(("gpio@400ff000", 4), Pin.IN)
SW2 = Pin(("gpioc", 6), Pin.IN)
SW3 = Pin(("gpioa", 4), Pin.IN)

SW2.irq(lambda t: print("SW2 changed"))
SW3.irq(lambda t: print("SW3 changed"))
Expand All @@ -138,14 +138,14 @@ Example of using I2C to scan for I2C slaves:

from machine import I2C

i2c = I2C("i2c@40066000")
i2c = I2C("i2c0")
i2c.scan()

Example of using SPI to write a buffer to the MOSI pin:

from machine import SPI

spi = SPI("spi@4002c000")
spi = SPI("spi0")
spi.init(baudrate=500000, polarity=1, phase=1, bits=8, firstbit=SPI.MSB)
spi.write(b'abcd')

Expand Down
2 changes: 2 additions & 0 deletions ports/zephyr/prj.conf
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ CONFIG_FPU=y
CONFIG_MAIN_STACK_SIZE=4736
CONFIG_POLL=y

CONFIG_DEVICE_DT_METADATA=y

# Enable sensor subsystem (doesn't add code if not used).
# Specific sensors should be enabled per-board.
CONFIG_SENSOR=y
Expand Down
6 changes: 6 additions & 0 deletions ports/zephyr/zephyr_device.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ const struct device *zephyr_device_find(mp_obj_t name) {
const char *dev_name = mp_obj_str_get_str(name);
const struct device *dev = device_get_binding(dev_name);

#ifdef CONFIG_DEVICE_DT_METADATA
if (dev == NULL) {
dev = device_get_by_dt_nodelabel(dev_name);
}
#endif

if (dev == NULL) {
#if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE
mp_raise_ValueError(MP_ERROR_TEXT("device not found"));
Expand Down

0 comments on commit a9803f4

Please sign in to comment.