Skip to content

Commit

Permalink
rp2: Add new port to Raspberry Pi RP2 microcontroller.
Browse files Browse the repository at this point in the history
This commit adds a new port "rp2" which targets the new Raspberry Pi RP2040
microcontroller.

The build system uses pure cmake (with a small Makefile wrapper for
convenience).  The USB driver is TinyUSB, and there is a machine module
with most of the standard classes implemented.  Some examples are provided
in the examples/rp2/ directory.

Work done in collaboration with Graham Sanderson.

Signed-off-by: Damien George <damien@micropython.org>
  • Loading branch information
dpgeorge committed Jan 29, 2021
1 parent ef3ee7a commit 469345e
Show file tree
Hide file tree
Showing 44 changed files with 5,509 additions and 0 deletions.
35 changes: 35 additions & 0 deletions examples/rp2/pio_1hz.py
@@ -0,0 +1,35 @@
# Example using PIO to blink an LED and raise an IRQ at 1Hz.

import time
from machine import Pin
import rp2


@rp2.asm_pio(set_init=rp2.PIO.OUT_LOW)
def blink_1hz():
# fmt: off
# Cycles: 1 + 1 + 6 + 32 * (30 + 1) = 1000
irq(rel(0))
set(pins, 1)
set(x, 31) [5]
label("delay_high")
nop() [29]
jmp(x_dec, "delay_high")

# Cycles: 1 + 7 + 32 * (30 + 1) = 1000
set(pins, 0)
set(x, 31) [6]
label("delay_low")
nop() [29]
jmp(x_dec, "delay_low")
# fmt: on


# Create the StateMachine with the blink_1hz program, outputting on Pin(25).
sm = rp2.StateMachine(0, blink_1hz, freq=2000, set_base=Pin(25))

# Set the IRQ handler to print the millisecond timestamp.
sm.irq(lambda p: print(time.ticks_ms()))

# Start the StateMachine.
sm.active(1)
27 changes: 27 additions & 0 deletions examples/rp2/pio_exec.py
@@ -0,0 +1,27 @@
# Example using PIO to turn on an LED via an explicit exec.
#
# Demonstrates:
# - using set_init and set_base
# - using StateMachine.exec

import time
from machine import Pin
import rp2

# Define an empty program that uses a single set pin.
@rp2.asm_pio(set_init=rp2.PIO.OUT_LOW)
def prog():
pass


# Construct the StateMachine, binding Pin(25) to the set pin.
sm = rp2.StateMachine(0, prog, set_base=Pin(25))

# Turn on the set pin via an exec instruction.
sm.exec("set(pins, 1)")

# Sleep for 500ms.
time.sleep(0.5)

# Turn off the set pin via an exec instruction.
sm.exec("set(pins, 0)")
46 changes: 46 additions & 0 deletions examples/rp2/pio_pinchange.py
@@ -0,0 +1,46 @@
# Example using PIO to wait for a pin change and raise an IRQ.
#
# Demonstrates:
# - PIO wrapping
# - PIO wait instruction, waiting on an input pin
# - PIO irq instruction, in blocking mode with relative IRQ number
# - setting the in_base pin for a StateMachine
# - setting an irq handler for a StateMachine
# - instantiating 2x StateMachine's with the same program and different pins

import time
from machine import Pin
import rp2


@rp2.asm_pio()
def wait_pin_low():
wrap_target()

wait(0, pin, 0)
irq(block, rel(0))
wait(1, pin, 0)

wrap()


def handler(sm):
# Print a (wrapping) timestamp, and the state machine object.
print(time.ticks_ms(), sm)


# Instantiate StateMachine(0) with wait_pin_low program on Pin(16).
pin16 = Pin(16, Pin.IN, Pin.PULL_UP)
sm0 = rp2.StateMachine(0, wait_pin_low, in_base=pin16)
sm0.irq(handler)

# Instantiate StateMachine(1) with wait_pin_low program on Pin(17).
pin17 = Pin(17, Pin.IN, Pin.PULL_UP)
sm1 = rp2.StateMachine(1, wait_pin_low, in_base=pin17)
sm1.irq(handler)

# Start the StateMachine's running.
sm0.active(1)
sm1.active(1)

# Now, when Pin(16) or Pin(17) is pulled low a message will be printed to the REPL.
45 changes: 45 additions & 0 deletions examples/rp2/pio_pwm.py
@@ -0,0 +1,45 @@
# Example of using PIO for PWM, and fading the brightness of an LED

from machine import Pin
from rp2 import PIO, StateMachine, asm_pio
from time import sleep


@asm_pio(sideset_init=PIO.OUT_LOW)
def pwm_prog():
# fmt: off
pull(noblock) .side(0)
mov(x, osr) # Keep most recent pull data stashed in X, for recycling by noblock
mov(y, isr) # ISR must be preloaded with PWM count max
label("pwmloop")
jmp(x_not_y, "skip")
nop() .side(1)
label("skip")
jmp(y_dec, "pwmloop")
# fmt: on


class PIOPWM:
def __init__(self, sm_id, pin, max_count, count_freq):
self._sm = StateMachine(sm_id, pwm_prog, freq=2 * count_freq, sideset_base=Pin(pin))
# Use exec() to load max count into ISR
self._sm.put(max_count)
self._sm.exec("pull()")
self._sm.exec("mov(isr, osr)")
self._sm.active(1)
self._max_count = max_count

def set(self, value):
# Minimum value is -1 (completely turn off), 0 actually still produces narrow pulse
value = max(value, -1)
value = min(value, self._max_count)
self._sm.put(value)


# Pin 25 is LED on Pico boards
pwm = PIOPWM(0, 25, max_count=(1 << 16) - 1, count_freq=10_000_000)

while True:
for i in range(256):
pwm.set(i ** 2)
sleep(0.01)
44 changes: 44 additions & 0 deletions examples/rp2/pio_uart_tx.py
@@ -0,0 +1,44 @@
# Example using PIO to create a UART TX interface

from machine import Pin
from rp2 import PIO, StateMachine, asm_pio

UART_BAUD = 115200
PIN_BASE = 10
NUM_UARTS = 8


@asm_pio(sideset_init=PIO.OUT_HIGH, out_init=PIO.OUT_HIGH, out_shiftdir=PIO.SHIFT_RIGHT)
def uart_tx():
# fmt: off
# Block with TX deasserted until data available
pull()
# Initialise bit counter, assert start bit for 8 cycles
set(x, 7) .side(0) [7]
# Shift out 8 data bits, 8 execution cycles per bit
label("bitloop")
out(pins, 1) [6]
jmp(x_dec, "bitloop")
# Assert stop bit for 8 cycles total (incl 1 for pull())
nop() .side(1) [6]
# fmt: on


# Now we add 8 UART TXs, on pins 10 to 17. Use the same baud rate for all of them.
uarts = []
for i in range(NUM_UARTS):
sm = StateMachine(
i, uart_tx, freq=8 * UART_BAUD, sideset_base=Pin(PIN_BASE + i), out_base=Pin(PIN_BASE + i)
)
sm.active(1)
uarts.append(sm)

# We can print characters from each UART by pushing them to the TX FIFO
def pio_uart_print(sm, s):
for c in s:
sm.put(ord(c))


# Print a different message from each UART
for i, u in enumerate(uarts):
pio_uart_print(u, "Hello from UART {}!\n".format(i))
59 changes: 59 additions & 0 deletions examples/rp2/pio_ws2812.py
@@ -0,0 +1,59 @@
# Example using PIO to drive a set of WS2812 LEDs.

import array, time
from machine import Pin
import rp2

# Configure the number of WS2812 LEDs.
NUM_LEDS = 8


@rp2.asm_pio(
sideset_init=rp2.PIO.OUT_LOW,
out_shiftdir=rp2.PIO.SHIFT_LEFT,
autopull=True,
pull_thresh=24,
)
def ws2812():
# fmt: off
T1 = 2
T2 = 5
T3 = 3
wrap_target()
label("bitloop")
out(x, 1) .side(0) [T3 - 1]
jmp(not_x, "do_zero") .side(1) [T1 - 1]
jmp("bitloop") .side(1) [T2 - 1]
label("do_zero")
nop() .side(0) [T2 - 1]
wrap()
# fmt: on


# Create the StateMachine with the ws2812 program, outputting on Pin(22).
sm = rp2.StateMachine(0, ws2812, freq=8_000_000, sideset_base=Pin(22))

# Start the StateMachine, it will wait for data on its FIFO.
sm.active(1)

# Display a pattern on the LEDs via an array of LED RGB values.
ar = array.array("I", [0 for _ in range(NUM_LEDS)])

# Cycle colours.
for i in range(4 * NUM_LEDS):
for j in range(NUM_LEDS):
r = j * 100 // (NUM_LEDS - 1)
b = 100 - j * 100 // (NUM_LEDS - 1)
if j != i % NUM_LEDS:
r >>= 3
b >>= 3
ar[j] = r << 16 | b
sm.put(ar, 8)
time.sleep_ms(50)

# Fade out.
for i in range(24):
for j in range(NUM_LEDS):
ar[j] = ar[j] >> 1 & 0x7F7F7F
sm.put(ar, 8)
time.sleep_ms(50)
25 changes: 25 additions & 0 deletions examples/rp2/pwm_fade.py
@@ -0,0 +1,25 @@
# Example using PWM to fade an LED.

import time
from machine import Pin, PWM


# Construct PWM object, with LED on Pin(25).
pwm = PWM(Pin(25))

# Set the PWM frequency.
pwm.freq(1000)

# Fade the LED in and out a few times.
duty = 0
direction = 1
for _ in range(8 * 256):
duty += direction
if duty > 255:
duty = 255
direction = -1
elif duty < 0:
duty = 0
direction = 1
pwm.duty_u16(duty * duty)
time.sleep(0.001)

0 comments on commit 469345e

Please sign in to comment.