You've already forked Core2forAWS-MicroPython
mirror of
https://github.com/m5stack/Core2forAWS-MicroPython.git
synced 2026-05-20 10:30:31 -07:00
rp2: Add new port to Raspberry Pi RP2 microcontroller.
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>
This commit is contained in:
@@ -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)
|
||||
@@ -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)")
|
||||
@@ -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.
|
||||
@@ -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)
|
||||
@@ -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))
|
||||
@@ -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)
|
||||
@@ -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,0 +1,162 @@
|
||||
cmake_minimum_required(VERSION 3.12)
|
||||
|
||||
# Set build type to reduce firmware size
|
||||
if(NOT CMAKE_BUILD_TYPE)
|
||||
set(CMAKE_BUILD_TYPE MinSizeRel)
|
||||
endif()
|
||||
|
||||
# Set main target and component locations
|
||||
set(MICROPYTHON_TARGET firmware)
|
||||
get_filename_component(MPY_DIR "../.." ABSOLUTE)
|
||||
if (PICO_SDK_PATH_OVERRIDE)
|
||||
set(PICO_SDK_PATH ${PICO_SDK_PATH_OVERRIDE})
|
||||
else()
|
||||
set(PICO_SDK_PATH ../../lib/pico-sdk)
|
||||
endif()
|
||||
|
||||
# Include component cmake fragments
|
||||
include(micropy_py.cmake)
|
||||
include(micropy_extmod.cmake)
|
||||
include(${PICO_SDK_PATH}/pico_sdk_init.cmake)
|
||||
|
||||
# Define the top-level project
|
||||
project(${MICROPYTHON_TARGET})
|
||||
|
||||
pico_sdk_init()
|
||||
|
||||
add_executable(${MICROPYTHON_TARGET})
|
||||
|
||||
set(SOURCE_LIB
|
||||
${MPY_DIR}/lib/littlefs/lfs1.c
|
||||
${MPY_DIR}/lib/littlefs/lfs1_util.c
|
||||
${MPY_DIR}/lib/littlefs/lfs2.c
|
||||
${MPY_DIR}/lib/littlefs/lfs2_util.c
|
||||
${MPY_DIR}/lib/mp-readline/readline.c
|
||||
${MPY_DIR}/lib/oofatfs/ff.c
|
||||
${MPY_DIR}/lib/oofatfs/ffunicode.c
|
||||
${MPY_DIR}/lib/timeutils/timeutils.c
|
||||
${MPY_DIR}/lib/utils/gchelper_m0.s
|
||||
${MPY_DIR}/lib/utils/gchelper_native.c
|
||||
${MPY_DIR}/lib/utils/mpirq.c
|
||||
${MPY_DIR}/lib/utils/stdout_helpers.c
|
||||
${MPY_DIR}/lib/utils/sys_stdio_mphal.c
|
||||
${MPY_DIR}/lib/utils/pyexec.c
|
||||
)
|
||||
|
||||
set(SOURCE_DRIVERS
|
||||
${MPY_DIR}/drivers/bus/softspi.c
|
||||
)
|
||||
|
||||
set(SOURCE_PORT
|
||||
machine_adc.c
|
||||
machine_i2c.c
|
||||
machine_pin.c
|
||||
machine_pwm.c
|
||||
machine_spi.c
|
||||
machine_timer.c
|
||||
machine_uart.c
|
||||
machine_wdt.c
|
||||
main.c
|
||||
modmachine.c
|
||||
modrp2.c
|
||||
moduos.c
|
||||
modutime.c
|
||||
mphalport.c
|
||||
mpthreadport.c
|
||||
rp2_flash.c
|
||||
rp2_pio.c
|
||||
tusb_port.c
|
||||
uart.c
|
||||
)
|
||||
|
||||
set(SOURCE_QSTR
|
||||
${SOURCE_PY}
|
||||
${SOURCE_EXTMOD}
|
||||
${MPY_DIR}/lib/utils/mpirq.c
|
||||
${MPY_DIR}/lib/utils/sys_stdio_mphal.c
|
||||
${PROJECT_SOURCE_DIR}/machine_adc.c
|
||||
${PROJECT_SOURCE_DIR}/machine_i2c.c
|
||||
${PROJECT_SOURCE_DIR}/machine_pin.c
|
||||
${PROJECT_SOURCE_DIR}/machine_pwm.c
|
||||
${PROJECT_SOURCE_DIR}/machine_spi.c
|
||||
${PROJECT_SOURCE_DIR}/machine_timer.c
|
||||
${PROJECT_SOURCE_DIR}/machine_uart.c
|
||||
${PROJECT_SOURCE_DIR}/machine_wdt.c
|
||||
${PROJECT_SOURCE_DIR}/modmachine.c
|
||||
${PROJECT_SOURCE_DIR}/modrp2.c
|
||||
${PROJECT_SOURCE_DIR}/moduos.c
|
||||
${PROJECT_SOURCE_DIR}/modutime.c
|
||||
${PROJECT_SOURCE_DIR}/rp2_flash.c
|
||||
${PROJECT_SOURCE_DIR}/rp2_pio.c
|
||||
)
|
||||
|
||||
set(MPY_QSTR_DEFS ${PROJECT_SOURCE_DIR}/qstrdefsport.h)
|
||||
|
||||
# Define mpy-cross flags and frozen manifest
|
||||
set(MPY_CROSS_FLAGS -march=armv7m)
|
||||
set(FROZEN_MANIFEST ${PROJECT_SOURCE_DIR}/manifest.py)
|
||||
|
||||
include(micropy_rules.cmake)
|
||||
|
||||
target_sources(${MICROPYTHON_TARGET} PRIVATE
|
||||
${SOURCE_PY}
|
||||
${SOURCE_EXTMOD}
|
||||
${SOURCE_LIB}
|
||||
${SOURCE_DRIVERS}
|
||||
${SOURCE_PORT}
|
||||
)
|
||||
|
||||
target_include_directories(${MICROPYTHON_TARGET} PRIVATE
|
||||
"${PROJECT_SOURCE_DIR}"
|
||||
"${MPY_DIR}"
|
||||
"${CMAKE_BINARY_DIR}"
|
||||
)
|
||||
|
||||
target_compile_options(${MICROPYTHON_TARGET} PRIVATE
|
||||
-Wall
|
||||
#-Werror
|
||||
-DFFCONF_H=\"${MPY_DIR}/lib/oofatfs/ffconf.h\"
|
||||
-DLFS1_NO_MALLOC -DLFS1_NO_DEBUG -DLFS1_NO_WARN -DLFS1_NO_ERROR -DLFS1_NO_ASSERT
|
||||
-DLFS2_NO_MALLOC -DLFS2_NO_DEBUG -DLFS2_NO_WARN -DLFS2_NO_ERROR -DLFS2_NO_ASSERT
|
||||
)
|
||||
|
||||
target_compile_definitions(${MICROPYTHON_TARGET} PRIVATE
|
||||
PICO_FLOAT_PROPAGATE_NANS=1
|
||||
PICO_STACK_SIZE=0x2000
|
||||
PICO_CORE1_STACK_SIZE=0
|
||||
PICO_PROGRAM_NAME="MicroPython"
|
||||
PICO_NO_PROGRAM_VERSION_STRING=1 # do it ourselves in main.c
|
||||
MICROPY_BUILD_TYPE="${CMAKE_C_COMPILER_ID} ${CMAKE_C_COMPILER_VERSION} ${CMAKE_BUILD_TYPE}"
|
||||
PICO_NO_BI_STDIO_UART=1 # we call it UART REPL
|
||||
)
|
||||
|
||||
target_link_libraries(${MICROPYTHON_TARGET}
|
||||
hardware_adc
|
||||
hardware_dma
|
||||
hardware_flash
|
||||
hardware_i2c
|
||||
hardware_pio
|
||||
hardware_pwm
|
||||
hardware_rtc
|
||||
hardware_spi
|
||||
hardware_sync
|
||||
pico_multicore
|
||||
pico_stdlib_headers
|
||||
pico_stdlib
|
||||
tinyusb_device
|
||||
)
|
||||
|
||||
# todo this is a bit brittle, but we want to move a few source files into RAM (which requires
|
||||
# a linker script modification) until we explicitly add macro calls around the function
|
||||
# defs to move them into RAM.
|
||||
if (PICO_ON_DEVICE AND NOT PICO_NO_FLASH AND NOT PICO_COPY_TO_RAM)
|
||||
pico_set_linker_script(${MICROPYTHON_TARGET} ${CMAKE_CURRENT_LIST_DIR}/memmap_mp.ld)
|
||||
endif()
|
||||
|
||||
pico_add_extra_outputs(${MICROPYTHON_TARGET})
|
||||
|
||||
add_custom_command(TARGET ${MICROPYTHON_TARGET}
|
||||
POST_BUILD
|
||||
COMMAND arm-none-eabi-size --format=berkeley ${PROJECT_BINARY_DIR}/${MICROPYTHON_TARGET}.elf
|
||||
VERBATIM
|
||||
)
|
||||
@@ -0,0 +1,14 @@
|
||||
# Makefile for micropython on Raspberry Pi RP2
|
||||
#
|
||||
# This is a simple wrapper around cmake
|
||||
|
||||
BUILD = build
|
||||
|
||||
$(VERBOSE)MAKESILENT = -s
|
||||
|
||||
all:
|
||||
[ -d $(BUILD) ] || cmake -S . -B $(BUILD) -DPICO_BUILD_DOCS=0
|
||||
$(MAKE) $(MAKESILENT) -C $(BUILD)
|
||||
|
||||
clean:
|
||||
$(RM) -rf $(BUILD)
|
||||
@@ -0,0 +1,100 @@
|
||||
# The RP2 port
|
||||
|
||||
This is a port of MicroPython to the Raspberry Pi RP2 series of microcontrollers.
|
||||
Currently supported features are:
|
||||
|
||||
- REPL over USB VCP, and optionally over UART (on GP0/GP1).
|
||||
- Filesystem on the internal flash, using littlefs2.
|
||||
- Support for native code generation and inline assembler.
|
||||
- `utime` module with sleep, time and ticks functions.
|
||||
- `uos` module with VFS support.
|
||||
- `machine` module with the following classes: `Pin`, `ADC`, `PWM`, `I2C`, `SPI`,
|
||||
`SoftI2C`, `SoftSPI`, `Timer`, `UART`, `WDT`.
|
||||
- `rp2` module with programmable IO (PIO) support.
|
||||
|
||||
See the `examples/rp2/` directory for some example code.
|
||||
|
||||
## Building
|
||||
|
||||
The MicroPython cross-compiler must be built first, which will be used to
|
||||
pre-compile (freeze) built-in Python code. This cross-compiler is built and
|
||||
run on the host machine using:
|
||||
|
||||
$ make -C mpy-cross
|
||||
|
||||
This command should be executed from the root directory of this repository.
|
||||
All other commands below should be executed from the ports/rp2/ directory.
|
||||
|
||||
Building of the RP2 firmware is done entirely using CMake, although a simple
|
||||
Makefile is also provided as a convenience. To build the firmware run (from
|
||||
this directory):
|
||||
|
||||
$ make clean
|
||||
$ make
|
||||
|
||||
You can also build the standard CMake way. The final firmware is found in
|
||||
the top-level of the CMake build directory (`build` by default) and is
|
||||
called `firmware.uf2`.
|
||||
|
||||
## Deploying firmware to the device
|
||||
|
||||
Firmware can be deployed to the device by putting it into bootloader mode
|
||||
(hold down BOOTSEL while powering on or resetting) and then copying
|
||||
`firmware.uf2` to the USB mass storage device that appears.
|
||||
|
||||
If MicroPython is already installed then the bootloader can be entered by
|
||||
executing `import machine; machine.bootloader()` at the REPL.
|
||||
|
||||
## Sample code
|
||||
|
||||
The following samples can be easily run on the board by entering paste mode
|
||||
with Ctrl-E at the REPL, then cut-and-pasting the sample code to the REPL, then
|
||||
executing the code with Ctrl-D.
|
||||
|
||||
### Blinky
|
||||
|
||||
This blinks the on-board LED on the Pico board at 1.25Hz, using a Timer object
|
||||
with a callback.
|
||||
|
||||
```python
|
||||
from machine import Pin, Timer
|
||||
led = Pin(25, Pin.OUT)
|
||||
tim = Timer()
|
||||
def tick(timer):
|
||||
global led
|
||||
led.toggle()
|
||||
|
||||
tim.init(freq=2.5, mode=Timer.PERIODIC, callback=tick)
|
||||
```
|
||||
|
||||
### PIO blinky
|
||||
|
||||
This blinks the on-board LED on the Pico board at 1Hz, using a PIO peripheral and
|
||||
PIO assembler to directly toggle the LED at the required rate.
|
||||
|
||||
```python
|
||||
from machine import Pin
|
||||
import rp2
|
||||
|
||||
@rp2.asm_pio(set_init=rp2.PIO.OUT_LOW)
|
||||
def blink_1hz():
|
||||
# Turn on the LED and delay, taking 1000 cycles.
|
||||
set(pins, 1)
|
||||
set(x, 31) [6]
|
||||
label("delay_high")
|
||||
nop() [29]
|
||||
jmp(x_dec, "delay_high")
|
||||
|
||||
# Turn off the LED and delay, taking 1000 cycles.
|
||||
set(pins, 0)
|
||||
set(x, 31) [6]
|
||||
label("delay_low")
|
||||
nop() [29]
|
||||
jmp(x_dec, "delay_low")
|
||||
|
||||
# Create StateMachine(0) with the blink_1hz program, outputting on Pin(25).
|
||||
sm = rp2.StateMachine(0, blink_1hz, freq=2000, set_base=Pin(25))
|
||||
sm.active(1)
|
||||
```
|
||||
|
||||
See the `examples/rp2/` directory for further example code.
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2020-2021 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "py/runtime.h"
|
||||
#include "py/mphal.h"
|
||||
#include "hardware/adc.h"
|
||||
|
||||
#define ADC_IS_VALID_GPIO(gpio) ((gpio) >= 26 && (gpio) <= 29)
|
||||
#define ADC_CHANNEL_FROM_GPIO(gpio) ((gpio) - 26)
|
||||
#define ADC_CHANNEL_TEMPSENSOR (4)
|
||||
|
||||
STATIC uint16_t adc_config_and_read_u16(uint32_t channel) {
|
||||
adc_select_input(channel);
|
||||
uint32_t raw = adc_read();
|
||||
const uint32_t bits = 12;
|
||||
// Scale raw reading to 16 bit value using a Taylor expansion (for 8 <= bits <= 16)
|
||||
return raw << (16 - bits) | raw >> (2 * bits - 16);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
// MicroPython bindings for machine.ADC
|
||||
|
||||
const mp_obj_type_t machine_adc_type;
|
||||
|
||||
typedef struct _machine_adc_obj_t {
|
||||
mp_obj_base_t base;
|
||||
uint32_t channel;
|
||||
} machine_adc_obj_t;
|
||||
|
||||
STATIC void machine_adc_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
|
||||
machine_adc_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
mp_printf(print, "<ADC channel=%u>", self->channel);
|
||||
}
|
||||
|
||||
// ADC(id)
|
||||
STATIC mp_obj_t machine_adc_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
|
||||
// Check number of arguments
|
||||
mp_arg_check_num(n_args, n_kw, 1, 1, false);
|
||||
|
||||
mp_obj_t source = all_args[0];
|
||||
|
||||
uint32_t channel;
|
||||
if (mp_obj_is_int(source)) {
|
||||
// Get and validate channel number.
|
||||
channel = mp_obj_get_int(source);
|
||||
if (!((channel >= 0 && channel <= ADC_CHANNEL_TEMPSENSOR) || ADC_IS_VALID_GPIO(channel))) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("invalid channel"));
|
||||
}
|
||||
|
||||
} else {
|
||||
// Get GPIO and check it has ADC capabilities.
|
||||
channel = mp_hal_get_pin_obj(source);
|
||||
if (!ADC_IS_VALID_GPIO(channel)) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("Pin doesn't have ADC capabilities"));
|
||||
}
|
||||
}
|
||||
|
||||
adc_init();
|
||||
|
||||
if (ADC_IS_VALID_GPIO(channel)) {
|
||||
// Configure the GPIO pin in ADC mode.
|
||||
adc_gpio_init(channel);
|
||||
channel = ADC_CHANNEL_FROM_GPIO(channel);
|
||||
} else if (channel == ADC_CHANNEL_TEMPSENSOR) {
|
||||
// Enable temperature sensor.
|
||||
adc_set_temp_sensor_enabled(1);
|
||||
}
|
||||
|
||||
// Create ADC object.
|
||||
machine_adc_obj_t *o = m_new_obj(machine_adc_obj_t);
|
||||
o->base.type = &machine_adc_type;
|
||||
o->channel = channel;
|
||||
|
||||
return MP_OBJ_FROM_PTR(o);
|
||||
}
|
||||
|
||||
// read_u16()
|
||||
STATIC mp_obj_t machine_adc_read_u16(mp_obj_t self_in) {
|
||||
machine_adc_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
return MP_OBJ_NEW_SMALL_INT(adc_config_and_read_u16(self->channel));
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(machine_adc_read_u16_obj, machine_adc_read_u16);
|
||||
|
||||
STATIC const mp_rom_map_elem_t machine_adc_locals_dict_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR_read_u16), MP_ROM_PTR(&machine_adc_read_u16_obj) },
|
||||
|
||||
{ MP_ROM_QSTR(MP_QSTR_CORE_TEMP), MP_ROM_INT(ADC_CHANNEL_TEMPSENSOR) },
|
||||
};
|
||||
STATIC MP_DEFINE_CONST_DICT(machine_adc_locals_dict, machine_adc_locals_dict_table);
|
||||
|
||||
const mp_obj_type_t machine_adc_type = {
|
||||
{ &mp_type_type },
|
||||
.name = MP_QSTR_ADC,
|
||||
.print = machine_adc_print,
|
||||
.make_new = machine_adc_make_new,
|
||||
.locals_dict = (mp_obj_dict_t *)&machine_adc_locals_dict,
|
||||
};
|
||||
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2020-2021 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "py/runtime.h"
|
||||
#include "py/mphal.h"
|
||||
#include "py/mperrno.h"
|
||||
#include "extmod/machine_i2c.h"
|
||||
#include "modmachine.h"
|
||||
|
||||
#include "hardware/i2c.h"
|
||||
|
||||
#define DEFAULT_I2C_FREQ (400000)
|
||||
#define DEFAULT_I2C0_SCL (9)
|
||||
#define DEFAULT_I2C0_SDA (8)
|
||||
#define DEFAULT_I2C1_SCL (7)
|
||||
#define DEFAULT_I2C1_SDA (6)
|
||||
|
||||
// SDA/SCL on even/odd pins, I2C0/I2C1 on even/odd pairs of pins.
|
||||
#define IS_VALID_SCL(i2c, pin) (((pin) & 1) == 1 && (((pin) & 2) >> 1) == (i2c))
|
||||
#define IS_VALID_SDA(i2c, pin) (((pin) & 1) == 0 && (((pin) & 2) >> 1) == (i2c))
|
||||
|
||||
typedef struct _machine_i2c_obj_t {
|
||||
mp_obj_base_t base;
|
||||
i2c_inst_t *const i2c_inst;
|
||||
uint8_t i2c_id;
|
||||
uint8_t scl;
|
||||
uint8_t sda;
|
||||
uint32_t freq;
|
||||
} machine_i2c_obj_t;
|
||||
|
||||
STATIC machine_i2c_obj_t machine_i2c_obj[] = {
|
||||
{{&machine_hw_i2c_type}, i2c0, 0, DEFAULT_I2C0_SCL, DEFAULT_I2C0_SDA, 0},
|
||||
{{&machine_hw_i2c_type}, i2c1, 1, DEFAULT_I2C1_SCL, DEFAULT_I2C1_SDA, 0},
|
||||
};
|
||||
|
||||
STATIC void machine_i2c_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
|
||||
machine_i2c_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
mp_printf(print, "I2C(%u, freq=%u, scl=%u, sda=%u)",
|
||||
self->i2c_id, self->freq, self->scl, self->sda);
|
||||
}
|
||||
|
||||
mp_obj_t machine_i2c_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
|
||||
enum { ARG_id, ARG_freq, ARG_scl, ARG_sda };
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_id, MP_ARG_REQUIRED | MP_ARG_OBJ },
|
||||
{ MP_QSTR_freq, MP_ARG_INT, {.u_int = DEFAULT_I2C_FREQ} },
|
||||
{ MP_QSTR_scl, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
|
||||
{ MP_QSTR_sda, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
|
||||
};
|
||||
|
||||
// Parse args.
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
// Get I2C bus.
|
||||
int i2c_id = mp_obj_get_int(args[ARG_id].u_obj);
|
||||
if (i2c_id < 0 || i2c_id >= MP_ARRAY_SIZE(machine_i2c_obj)) {
|
||||
mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("I2C(%d) doesn't exist"), i2c_id);
|
||||
}
|
||||
|
||||
// Get static peripheral object.
|
||||
machine_i2c_obj_t *self = (machine_i2c_obj_t *)&machine_i2c_obj[i2c_id];
|
||||
|
||||
// Set SCL/SDA pins if configured.
|
||||
if (args[ARG_scl].u_obj != mp_const_none) {
|
||||
int scl = mp_hal_get_pin_obj(args[ARG_scl].u_obj);
|
||||
if (!IS_VALID_SCL(self->i2c_id, scl)) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("bad SCL pin"));
|
||||
}
|
||||
self->scl = scl;
|
||||
}
|
||||
if (args[ARG_sda].u_obj != mp_const_none) {
|
||||
int sda = mp_hal_get_pin_obj(args[ARG_sda].u_obj);
|
||||
if (!IS_VALID_SDA(self->i2c_id, sda)) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("bad SDA pin"));
|
||||
}
|
||||
self->sda = sda;
|
||||
}
|
||||
|
||||
// Initialise the I2C peripheral if any arguments given, or it was not initialised previously.
|
||||
if (n_args > 1 || n_kw > 0 || self->freq == 0) {
|
||||
self->freq = args[ARG_freq].u_int;
|
||||
i2c_init(self->i2c_inst, self->freq);
|
||||
self->freq = i2c_set_baudrate(self->i2c_inst, self->freq);
|
||||
gpio_set_function(self->scl, GPIO_FUNC_I2C);
|
||||
gpio_set_function(self->sda, GPIO_FUNC_I2C);
|
||||
gpio_set_pulls(self->scl, true, 0);
|
||||
gpio_set_pulls(self->sda, true, 0);
|
||||
}
|
||||
|
||||
return MP_OBJ_FROM_PTR(self);
|
||||
}
|
||||
|
||||
STATIC int machine_i2c_transfer_single(mp_obj_base_t *self_in, uint16_t addr, size_t len, uint8_t *buf, unsigned int flags) {
|
||||
machine_i2c_obj_t *self = (machine_i2c_obj_t *)self_in;
|
||||
int ret;
|
||||
bool nostop = !(flags & MP_MACHINE_I2C_FLAG_STOP);
|
||||
if (flags & MP_MACHINE_I2C_FLAG_READ) {
|
||||
ret = i2c_read_blocking(self->i2c_inst, addr, buf, len, nostop);
|
||||
} else {
|
||||
if (len <= 2) {
|
||||
// Workaround issue with hardware I2C not accepting short writes.
|
||||
mp_machine_soft_i2c_obj_t soft_i2c = {
|
||||
.base = { &mp_machine_soft_i2c_type },
|
||||
.us_delay = 500000 / self->freq + 1,
|
||||
.us_timeout = 255,
|
||||
.scl = self->scl,
|
||||
.sda = self->sda,
|
||||
};
|
||||
mp_machine_i2c_buf_t bufs = {
|
||||
.len = len,
|
||||
.buf = buf,
|
||||
};
|
||||
mp_hal_pin_open_drain(self->scl);
|
||||
mp_hal_pin_open_drain(self->sda);
|
||||
ret = mp_machine_soft_i2c_transfer(&soft_i2c.base, addr, 1, &bufs, flags);
|
||||
gpio_set_function(self->scl, GPIO_FUNC_I2C);
|
||||
gpio_set_function(self->sda, GPIO_FUNC_I2C);
|
||||
} else {
|
||||
ret = i2c_write_blocking(self->i2c_inst, addr, buf, len, nostop);
|
||||
}
|
||||
}
|
||||
return (ret < 0) ? -MP_EIO : ret;
|
||||
}
|
||||
|
||||
STATIC const mp_machine_i2c_p_t machine_i2c_p = {
|
||||
.transfer = mp_machine_i2c_transfer_adaptor,
|
||||
.transfer_single = machine_i2c_transfer_single,
|
||||
};
|
||||
|
||||
const mp_obj_type_t machine_hw_i2c_type = {
|
||||
{ &mp_type_type },
|
||||
.name = MP_QSTR_I2C,
|
||||
.print = machine_i2c_print,
|
||||
.make_new = machine_i2c_make_new,
|
||||
.protocol = &machine_i2c_p,
|
||||
.locals_dict = (mp_obj_dict_t *)&mp_machine_i2c_locals_dict,
|
||||
};
|
||||
@@ -0,0 +1,449 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2016-2021 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "py/runtime.h"
|
||||
#include "py/mphal.h"
|
||||
#include "lib/utils/mpirq.h"
|
||||
#include "modmachine.h"
|
||||
#include "extmod/virtpin.h"
|
||||
|
||||
#include "hardware/irq.h"
|
||||
#include "hardware/regs/intctrl.h"
|
||||
#include "hardware/structs/iobank0.h"
|
||||
#include "hardware/structs/padsbank0.h"
|
||||
|
||||
#define GPIO_MODE_IN (0)
|
||||
#define GPIO_MODE_OUT (1)
|
||||
#define GPIO_MODE_OPEN_DRAIN (2)
|
||||
#define GPIO_MODE_ALT (3)
|
||||
|
||||
// These can be or'd together.
|
||||
#define GPIO_PULL_UP (1)
|
||||
#define GPIO_PULL_DOWN (2)
|
||||
|
||||
#define GPIO_IRQ_ALL (0xf)
|
||||
|
||||
// Macros to access the state of the hardware.
|
||||
#define GPIO_GET_FUNCSEL(id) ((iobank0_hw->io[(id)].ctrl & IO_BANK0_GPIO0_CTRL_FUNCSEL_BITS) >> IO_BANK0_GPIO0_CTRL_FUNCSEL_LSB)
|
||||
#define GPIO_IS_OUT(id) (sio_hw->gpio_oe & (1 << (id)))
|
||||
#define GPIO_IS_PULL_UP(id) (padsbank0_hw->io[(id)] & PADS_BANK0_GPIO0_PUE_BITS)
|
||||
#define GPIO_IS_PULL_DOWN(id) (padsbank0_hw->io[(id)] & PADS_BANK0_GPIO0_PDE_BITS)
|
||||
|
||||
// Open drain behaviour is simulated.
|
||||
#define GPIO_IS_OPEN_DRAIN(id) (machine_pin_open_drain_mask & (1 << (id)))
|
||||
|
||||
typedef struct _machine_pin_obj_t {
|
||||
mp_obj_base_t base;
|
||||
uint32_t id;
|
||||
} machine_pin_obj_t;
|
||||
|
||||
typedef struct _machine_pin_irq_obj_t {
|
||||
mp_irq_obj_t base;
|
||||
uint32_t flags;
|
||||
uint32_t trigger;
|
||||
} machine_pin_irq_obj_t;
|
||||
|
||||
STATIC const mp_irq_methods_t machine_pin_irq_methods;
|
||||
|
||||
STATIC const machine_pin_obj_t machine_pin_obj[N_GPIOS] = {
|
||||
{{&machine_pin_type}, 0},
|
||||
{{&machine_pin_type}, 1},
|
||||
{{&machine_pin_type}, 2},
|
||||
{{&machine_pin_type}, 3},
|
||||
{{&machine_pin_type}, 4},
|
||||
{{&machine_pin_type}, 5},
|
||||
{{&machine_pin_type}, 6},
|
||||
{{&machine_pin_type}, 7},
|
||||
{{&machine_pin_type}, 8},
|
||||
{{&machine_pin_type}, 9},
|
||||
{{&machine_pin_type}, 10},
|
||||
{{&machine_pin_type}, 11},
|
||||
{{&machine_pin_type}, 12},
|
||||
{{&machine_pin_type}, 13},
|
||||
{{&machine_pin_type}, 14},
|
||||
{{&machine_pin_type}, 15},
|
||||
{{&machine_pin_type}, 16},
|
||||
{{&machine_pin_type}, 17},
|
||||
{{&machine_pin_type}, 18},
|
||||
{{&machine_pin_type}, 19},
|
||||
{{&machine_pin_type}, 20},
|
||||
{{&machine_pin_type}, 21},
|
||||
{{&machine_pin_type}, 22},
|
||||
{{&machine_pin_type}, 23},
|
||||
{{&machine_pin_type}, 24},
|
||||
{{&machine_pin_type}, 25},
|
||||
{{&machine_pin_type}, 26},
|
||||
{{&machine_pin_type}, 27},
|
||||
{{&machine_pin_type}, 28},
|
||||
{{&machine_pin_type}, 29},
|
||||
};
|
||||
|
||||
// Mask with "1" indicating that the corresponding pin is in simulated open-drain mode.
|
||||
uint32_t machine_pin_open_drain_mask;
|
||||
|
||||
STATIC void gpio_irq(void) {
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
uint32_t intr = iobank0_hw->intr[i];
|
||||
if (intr) {
|
||||
for (int j = 0; j < 8; ++j) {
|
||||
if (intr & 0xf) {
|
||||
uint32_t gpio = 8 * i + j;
|
||||
gpio_acknowledge_irq(gpio, intr & 0xf);
|
||||
machine_pin_irq_obj_t *irq = MP_STATE_PORT(machine_pin_irq_obj[gpio]);
|
||||
if (irq != NULL && (intr & irq->trigger)) {
|
||||
irq->flags = intr & irq->trigger;
|
||||
mp_irq_handler(&irq->base);
|
||||
}
|
||||
}
|
||||
intr >>= 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void machine_pin_init(void) {
|
||||
memset(MP_STATE_PORT(machine_pin_irq_obj), 0, sizeof(MP_STATE_PORT(machine_pin_irq_obj)));
|
||||
irq_set_exclusive_handler(IO_IRQ_BANK0, gpio_irq);
|
||||
irq_set_enabled(IO_IRQ_BANK0, true);
|
||||
}
|
||||
|
||||
void machine_pin_deinit(void) {
|
||||
for (int i = 0; i < N_GPIOS; ++i) {
|
||||
gpio_set_irq_enabled(i, GPIO_IRQ_ALL, false);
|
||||
}
|
||||
irq_set_enabled(IO_IRQ_BANK0, false);
|
||||
irq_remove_handler(IO_IRQ_BANK0, gpio_irq);
|
||||
}
|
||||
|
||||
STATIC void machine_pin_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
|
||||
machine_pin_obj_t *self = self_in;
|
||||
uint funcsel = GPIO_GET_FUNCSEL(self->id);
|
||||
qstr mode_qst;
|
||||
if (funcsel == GPIO_FUNC_SIO) {
|
||||
if (GPIO_IS_OPEN_DRAIN(self->id)) {
|
||||
mode_qst = MP_QSTR_OPEN_DRAIN;
|
||||
} else if (GPIO_IS_OUT(self->id)) {
|
||||
mode_qst = MP_QSTR_OUT;
|
||||
} else {
|
||||
mode_qst = MP_QSTR_IN;
|
||||
}
|
||||
} else {
|
||||
mode_qst = MP_QSTR_ALT;
|
||||
}
|
||||
mp_printf(print, "Pin(%u, mode=%q", self->id, mode_qst);
|
||||
bool pull_up = false;
|
||||
if (GPIO_IS_PULL_UP(self->id)) {
|
||||
mp_printf(print, ", pull=%q", MP_QSTR_PULL_UP);
|
||||
pull_up = true;
|
||||
}
|
||||
if (GPIO_IS_PULL_DOWN(self->id)) {
|
||||
if (pull_up) {
|
||||
mp_printf(print, "|%q", MP_QSTR_PULL_DOWN);
|
||||
} else {
|
||||
mp_printf(print, ", pull=%q", MP_QSTR_PULL_DOWN);
|
||||
}
|
||||
}
|
||||
if (funcsel != GPIO_FUNC_SIO) {
|
||||
mp_printf(print, ", alt=%u", funcsel);
|
||||
}
|
||||
mp_printf(print, ")");
|
||||
}
|
||||
|
||||
// pin.init(mode, pull=None, *, value=None, alt=FUNC_SIO)
|
||||
STATIC mp_obj_t machine_pin_obj_init_helper(const machine_pin_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
enum { ARG_mode, ARG_pull, ARG_value, ARG_alt };
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_mode, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE}},
|
||||
{ MP_QSTR_pull, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE}},
|
||||
{ MP_QSTR_value, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE}},
|
||||
{ MP_QSTR_alt, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = GPIO_FUNC_SIO}},
|
||||
};
|
||||
|
||||
// parse args
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
// set initial value (do this before configuring mode/pull)
|
||||
if (args[ARG_value].u_obj != mp_const_none) {
|
||||
gpio_put(self->id, mp_obj_is_true(args[ARG_value].u_obj));
|
||||
}
|
||||
|
||||
// configure mode
|
||||
if (args[ARG_mode].u_obj != mp_const_none) {
|
||||
mp_int_t mode = mp_obj_get_int(args[ARG_mode].u_obj);
|
||||
if (mode == GPIO_MODE_IN) {
|
||||
mp_hal_pin_input(self->id);
|
||||
} else if (mode == GPIO_MODE_OUT) {
|
||||
mp_hal_pin_output(self->id);
|
||||
} else if (mode == GPIO_MODE_OPEN_DRAIN) {
|
||||
mp_hal_pin_open_drain(self->id);
|
||||
} else {
|
||||
// Alternate function.
|
||||
gpio_set_function(self->id, args[ARG_alt].u_int);
|
||||
machine_pin_open_drain_mask &= ~(1 << self->id);
|
||||
}
|
||||
}
|
||||
|
||||
// configure pull (unconditionally because None means no-pull)
|
||||
uint32_t pull = 0;
|
||||
if (args[ARG_pull].u_obj != mp_const_none) {
|
||||
pull = mp_obj_get_int(args[ARG_pull].u_obj);
|
||||
}
|
||||
gpio_set_pulls(self->id, pull & GPIO_PULL_UP, pull & GPIO_PULL_DOWN);
|
||||
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
// constructor(id, ...)
|
||||
mp_obj_t mp_pin_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
|
||||
mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true);
|
||||
|
||||
// get the wanted pin object
|
||||
int wanted_pin = mp_obj_get_int(args[0]);
|
||||
if (!(0 <= wanted_pin && wanted_pin < MP_ARRAY_SIZE(machine_pin_obj))) {
|
||||
mp_raise_ValueError("invalid pin");
|
||||
}
|
||||
const machine_pin_obj_t *self = &machine_pin_obj[wanted_pin];
|
||||
|
||||
if (n_args > 1 || n_kw > 0) {
|
||||
// pin mode given, so configure this GPIO
|
||||
mp_map_t kw_args;
|
||||
mp_map_init_fixed_table(&kw_args, n_kw, args + n_args);
|
||||
machine_pin_obj_init_helper(self, n_args - 1, args + 1, &kw_args);
|
||||
}
|
||||
|
||||
return MP_OBJ_FROM_PTR(self);
|
||||
}
|
||||
|
||||
// fast method for getting/setting pin value
|
||||
STATIC mp_obj_t machine_pin_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) {
|
||||
mp_arg_check_num(n_args, n_kw, 0, 1, false);
|
||||
machine_pin_obj_t *self = self_in;
|
||||
if (n_args == 0) {
|
||||
// get pin
|
||||
return MP_OBJ_NEW_SMALL_INT(gpio_get(self->id));
|
||||
} else {
|
||||
// set pin
|
||||
bool value = mp_obj_is_true(args[0]);
|
||||
if (GPIO_IS_OPEN_DRAIN(self->id)) {
|
||||
MP_STATIC_ASSERT(GPIO_IN == 0 && GPIO_OUT == 1);
|
||||
gpio_set_dir(self->id, 1 - value);
|
||||
} else {
|
||||
gpio_put(self->id, value);
|
||||
}
|
||||
return mp_const_none;
|
||||
}
|
||||
}
|
||||
|
||||
// pin.init(mode, pull)
|
||||
STATIC mp_obj_t machine_pin_obj_init(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) {
|
||||
return machine_pin_obj_init_helper(args[0], n_args - 1, args + 1, kw_args);
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(machine_pin_init_obj, 1, machine_pin_obj_init);
|
||||
|
||||
// pin.value([value])
|
||||
STATIC mp_obj_t machine_pin_value(size_t n_args, const mp_obj_t *args) {
|
||||
return machine_pin_call(args[0], n_args - 1, 0, args + 1);
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_pin_value_obj, 1, 2, machine_pin_value);
|
||||
|
||||
// pin.low()
|
||||
STATIC mp_obj_t machine_pin_low(mp_obj_t self_in) {
|
||||
machine_pin_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
if (GPIO_IS_OPEN_DRAIN(self->id)) {
|
||||
gpio_set_dir(self->id, GPIO_OUT);
|
||||
} else {
|
||||
gpio_clr_mask(1u << self->id);
|
||||
}
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_pin_low_obj, machine_pin_low);
|
||||
|
||||
// pin.high()
|
||||
STATIC mp_obj_t machine_pin_high(mp_obj_t self_in) {
|
||||
machine_pin_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
if (GPIO_IS_OPEN_DRAIN(self->id)) {
|
||||
gpio_set_dir(self->id, GPIO_IN);
|
||||
} else {
|
||||
gpio_set_mask(1u << self->id);
|
||||
}
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_pin_high_obj, machine_pin_high);
|
||||
|
||||
// pin.toggle()
|
||||
STATIC mp_obj_t machine_pin_toggle(mp_obj_t self_in) {
|
||||
machine_pin_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
if (GPIO_IS_OPEN_DRAIN(self->id)) {
|
||||
if (GPIO_IS_OUT(self->id)) {
|
||||
gpio_set_dir(self->id, GPIO_IN);
|
||||
} else {
|
||||
gpio_set_dir(self->id, GPIO_OUT);
|
||||
}
|
||||
} else {
|
||||
gpio_xor_mask(1u << self->id);
|
||||
}
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_pin_toggle_obj, machine_pin_toggle);
|
||||
|
||||
// pin.irq(handler=None, trigger=IRQ_FALLING|IRQ_RISING, hard=False)
|
||||
STATIC mp_obj_t machine_pin_irq(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
enum { ARG_handler, ARG_trigger, ARG_hard };
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_handler, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
|
||||
{ MP_QSTR_trigger, MP_ARG_INT, {.u_int = GPIO_IRQ_EDGE_FALL | GPIO_IRQ_EDGE_RISE} },
|
||||
{ MP_QSTR_hard, MP_ARG_BOOL, {.u_bool = false} },
|
||||
};
|
||||
machine_pin_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
// Get the IRQ object.
|
||||
machine_pin_irq_obj_t *irq = MP_STATE_PORT(machine_pin_irq_obj[self->id]);
|
||||
|
||||
// Allocate the IRQ object if it doesn't already exist.
|
||||
if (irq == NULL) {
|
||||
irq = m_new_obj(machine_pin_irq_obj_t);
|
||||
irq->base.base.type = &mp_irq_type;
|
||||
irq->base.methods = (mp_irq_methods_t *)&machine_pin_irq_methods;
|
||||
irq->base.parent = MP_OBJ_FROM_PTR(self);
|
||||
irq->base.handler = mp_const_none;
|
||||
irq->base.ishard = false;
|
||||
MP_STATE_PORT(machine_pin_irq_obj[self->id]) = irq;
|
||||
}
|
||||
|
||||
if (n_args > 1 || kw_args->used != 0) {
|
||||
// Configure IRQ.
|
||||
|
||||
// Disable all IRQs while data is updated.
|
||||
gpio_set_irq_enabled(self->id, GPIO_IRQ_ALL, false);
|
||||
|
||||
// Update IRQ data.
|
||||
irq->base.handler = args[ARG_handler].u_obj;
|
||||
irq->base.ishard = args[ARG_hard].u_bool;
|
||||
irq->flags = 0;
|
||||
irq->trigger = args[ARG_trigger].u_int;
|
||||
|
||||
// Enable IRQ if a handler is given.
|
||||
if (args[ARG_handler].u_obj != mp_const_none) {
|
||||
gpio_set_irq_enabled(self->id, args[ARG_trigger].u_int, true);
|
||||
}
|
||||
}
|
||||
|
||||
return MP_OBJ_FROM_PTR(irq);
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(machine_pin_irq_obj, 1, machine_pin_irq);
|
||||
|
||||
STATIC const mp_rom_map_elem_t machine_pin_locals_dict_table[] = {
|
||||
// instance methods
|
||||
{ MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&machine_pin_init_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_value), MP_ROM_PTR(&machine_pin_value_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_low), MP_ROM_PTR(&machine_pin_low_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_high), MP_ROM_PTR(&machine_pin_high_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_off), MP_ROM_PTR(&machine_pin_low_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_on), MP_ROM_PTR(&machine_pin_high_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_toggle), MP_ROM_PTR(&machine_pin_toggle_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_irq), MP_ROM_PTR(&machine_pin_irq_obj) },
|
||||
|
||||
// class constants
|
||||
{ MP_ROM_QSTR(MP_QSTR_IN), MP_ROM_INT(GPIO_MODE_IN) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_OUT), MP_ROM_INT(GPIO_MODE_OUT) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_OPEN_DRAIN), MP_ROM_INT(GPIO_MODE_OPEN_DRAIN) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_ALT), MP_ROM_INT(GPIO_MODE_ALT) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_PULL_UP), MP_ROM_INT(GPIO_PULL_UP) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_PULL_DOWN), MP_ROM_INT(GPIO_PULL_DOWN) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_IRQ_RISING), MP_ROM_INT(GPIO_IRQ_EDGE_RISE) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_IRQ_FALLING), MP_ROM_INT(GPIO_IRQ_EDGE_FALL) },
|
||||
};
|
||||
STATIC MP_DEFINE_CONST_DICT(machine_pin_locals_dict, machine_pin_locals_dict_table);
|
||||
|
||||
STATIC mp_uint_t pin_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_t arg, int *errcode) {
|
||||
(void)errcode;
|
||||
machine_pin_obj_t *self = self_in;
|
||||
|
||||
switch (request) {
|
||||
case MP_PIN_READ: {
|
||||
return gpio_get(self->id);
|
||||
}
|
||||
case MP_PIN_WRITE: {
|
||||
gpio_put(self->id, arg);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
STATIC const mp_pin_p_t pin_pin_p = {
|
||||
.ioctl = pin_ioctl,
|
||||
};
|
||||
|
||||
const mp_obj_type_t machine_pin_type = {
|
||||
{ &mp_type_type },
|
||||
.name = MP_QSTR_Pin,
|
||||
.print = machine_pin_print,
|
||||
.make_new = mp_pin_make_new,
|
||||
.call = machine_pin_call,
|
||||
.protocol = &pin_pin_p,
|
||||
.locals_dict = (mp_obj_t)&machine_pin_locals_dict,
|
||||
};
|
||||
|
||||
STATIC mp_uint_t machine_pin_irq_trigger(mp_obj_t self_in, mp_uint_t new_trigger) {
|
||||
machine_pin_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
machine_pin_irq_obj_t *irq = MP_STATE_PORT(machine_pin_irq_obj[self->id]);
|
||||
gpio_set_irq_enabled(self->id, GPIO_IRQ_ALL, false);
|
||||
irq->flags = 0;
|
||||
irq->trigger = new_trigger;
|
||||
gpio_set_irq_enabled(self->id, new_trigger, true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
STATIC mp_uint_t machine_pin_irq_info(mp_obj_t self_in, mp_uint_t info_type) {
|
||||
machine_pin_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
machine_pin_irq_obj_t *irq = MP_STATE_PORT(machine_pin_irq_obj[self->id]);
|
||||
if (info_type == MP_IRQ_INFO_FLAGS) {
|
||||
return irq->flags;
|
||||
} else if (info_type == MP_IRQ_INFO_TRIGGERS) {
|
||||
return irq->trigger;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
STATIC const mp_irq_methods_t machine_pin_irq_methods = {
|
||||
.trigger = machine_pin_irq_trigger,
|
||||
.info = machine_pin_irq_info,
|
||||
};
|
||||
|
||||
mp_hal_pin_obj_t mp_hal_get_pin_obj(mp_obj_t obj) {
|
||||
if (!mp_obj_is_type(obj, &machine_pin_type)) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("expecting a Pin"));
|
||||
}
|
||||
machine_pin_obj_t *pin = MP_OBJ_TO_PTR(obj);
|
||||
return pin->id;
|
||||
}
|
||||
@@ -0,0 +1,197 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2020-2021 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "py/runtime.h"
|
||||
#include "py/mphal.h"
|
||||
#include "modmachine.h"
|
||||
|
||||
#include "hardware/clocks.h"
|
||||
#include "hardware/pwm.h"
|
||||
|
||||
/******************************************************************************/
|
||||
// MicroPython bindings for machine.PWM
|
||||
|
||||
const mp_obj_type_t machine_pwm_type;
|
||||
|
||||
typedef struct _machine_pwm_obj_t {
|
||||
mp_obj_base_t base;
|
||||
uint8_t slice;
|
||||
uint8_t channel;
|
||||
} machine_pwm_obj_t;
|
||||
|
||||
STATIC machine_pwm_obj_t machine_pwm_obj[] = {
|
||||
{{&machine_pwm_type}, 0, PWM_CHAN_A},
|
||||
{{&machine_pwm_type}, 0, PWM_CHAN_B},
|
||||
{{&machine_pwm_type}, 1, PWM_CHAN_A},
|
||||
{{&machine_pwm_type}, 1, PWM_CHAN_B},
|
||||
{{&machine_pwm_type}, 2, PWM_CHAN_A},
|
||||
{{&machine_pwm_type}, 2, PWM_CHAN_B},
|
||||
{{&machine_pwm_type}, 3, PWM_CHAN_A},
|
||||
{{&machine_pwm_type}, 3, PWM_CHAN_B},
|
||||
{{&machine_pwm_type}, 4, PWM_CHAN_A},
|
||||
{{&machine_pwm_type}, 4, PWM_CHAN_B},
|
||||
{{&machine_pwm_type}, 5, PWM_CHAN_A},
|
||||
{{&machine_pwm_type}, 5, PWM_CHAN_B},
|
||||
{{&machine_pwm_type}, 6, PWM_CHAN_A},
|
||||
{{&machine_pwm_type}, 6, PWM_CHAN_B},
|
||||
{{&machine_pwm_type}, 7, PWM_CHAN_A},
|
||||
{{&machine_pwm_type}, 7, PWM_CHAN_B},
|
||||
};
|
||||
|
||||
STATIC void machine_pwm_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
|
||||
machine_pwm_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
mp_printf(print, "<PWM slice=%u channel=%u>", self->slice, self->channel);
|
||||
}
|
||||
|
||||
// PWM(pin)
|
||||
STATIC mp_obj_t machine_pwm_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
|
||||
// Check number of arguments
|
||||
mp_arg_check_num(n_args, n_kw, 1, 1, false);
|
||||
|
||||
// Get GPIO to connect to PWM.
|
||||
uint32_t gpio = mp_hal_get_pin_obj(all_args[0]);
|
||||
|
||||
// Get static peripheral object.
|
||||
uint slice = pwm_gpio_to_slice_num(gpio);
|
||||
uint8_t channel = pwm_gpio_to_channel(gpio);
|
||||
const machine_pwm_obj_t *self = &machine_pwm_obj[slice * 2 + channel];
|
||||
|
||||
// Select PWM function for given GPIO.
|
||||
gpio_set_function(gpio, GPIO_FUNC_PWM);
|
||||
|
||||
return MP_OBJ_FROM_PTR(self);
|
||||
}
|
||||
|
||||
STATIC mp_obj_t machine_pwm_deinit(mp_obj_t self_in) {
|
||||
machine_pwm_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
pwm_set_enabled(self->slice, false);
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_pwm_deinit_obj, machine_pwm_deinit);
|
||||
|
||||
// PWM.freq([value])
|
||||
STATIC mp_obj_t machine_pwm_freq(size_t n_args, const mp_obj_t *args) {
|
||||
machine_pwm_obj_t *self = MP_OBJ_TO_PTR(args[0]);
|
||||
uint32_t source_hz = clock_get_hz(clk_sys);
|
||||
if (n_args == 1) {
|
||||
// Get frequency.
|
||||
uint32_t div16 = pwm_hw->slice[self->slice].div;
|
||||
uint32_t top = pwm_hw->slice[self->slice].top;
|
||||
uint32_t pwm_freq = 16 * source_hz / div16 / top;
|
||||
return MP_OBJ_NEW_SMALL_INT(pwm_freq);
|
||||
} else {
|
||||
// Set the frequency, making "top" as large as possible for maximum resolution.
|
||||
// Maximum "top" is set at 65534 to be able to achieve 100% duty with 65535.
|
||||
#define TOP_MAX 65534
|
||||
mp_int_t freq = mp_obj_get_int(args[1]);
|
||||
uint32_t div16_top = 16 * source_hz / freq;
|
||||
uint32_t top = 1;
|
||||
for (;;) {
|
||||
// Try a few small prime factors to get close to the desired frequency.
|
||||
if (div16_top >= 16 * 5 && div16_top % 5 == 0 && top * 5 <= TOP_MAX) {
|
||||
div16_top /= 5;
|
||||
top *= 5;
|
||||
} else if (div16_top >= 16 * 3 && div16_top % 3 == 0 && top * 3 <= TOP_MAX) {
|
||||
div16_top /= 3;
|
||||
top *= 3;
|
||||
} else if (div16_top >= 16 * 2 && top * 2 <= TOP_MAX) {
|
||||
div16_top /= 2;
|
||||
top *= 2;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (div16_top < 16) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("freq too large"));
|
||||
} else if (div16_top >= 256 * 16) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("freq too small"));
|
||||
}
|
||||
pwm_hw->slice[self->slice].div = div16_top;
|
||||
pwm_hw->slice[self->slice].top = top;
|
||||
return mp_const_none;
|
||||
}
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_pwm_freq_obj, 1, 2, machine_pwm_freq);
|
||||
|
||||
// PWM.duty_u16([value])
|
||||
STATIC mp_obj_t machine_pwm_duty_u16(size_t n_args, const mp_obj_t *args) {
|
||||
machine_pwm_obj_t *self = MP_OBJ_TO_PTR(args[0]);
|
||||
uint32_t top = pwm_hw->slice[self->slice].top;
|
||||
if (n_args == 1) {
|
||||
// Get duty cycle.
|
||||
uint32_t cc = pwm_hw->slice[self->slice].cc;
|
||||
cc = (cc >> (self->channel ? PWM_CH0_CC_B_LSB : PWM_CH0_CC_A_LSB)) & 0xffff;
|
||||
return MP_OBJ_NEW_SMALL_INT(cc * 65535 / (top + 1));
|
||||
} else {
|
||||
// Set duty cycle.
|
||||
mp_int_t duty_u16 = mp_obj_get_int(args[1]);
|
||||
uint32_t cc = duty_u16 * (top + 1) / 65535;
|
||||
pwm_set_chan_level(self->slice, self->channel, cc);
|
||||
pwm_set_enabled(self->slice, true);
|
||||
return mp_const_none;
|
||||
}
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_pwm_duty_u16_obj, 1, 2, machine_pwm_duty_u16);
|
||||
|
||||
// PWM.duty_ns([value])
|
||||
STATIC mp_obj_t machine_pwm_duty_ns(size_t n_args, const mp_obj_t *args) {
|
||||
machine_pwm_obj_t *self = MP_OBJ_TO_PTR(args[0]);
|
||||
uint32_t source_hz = clock_get_hz(clk_sys);
|
||||
uint32_t slice_hz = 16 * source_hz / pwm_hw->slice[self->slice].div;
|
||||
if (n_args == 1) {
|
||||
// Get duty cycle.
|
||||
uint32_t cc = pwm_hw->slice[self->slice].cc;
|
||||
cc = (cc >> (self->channel ? PWM_CH0_CC_B_LSB : PWM_CH0_CC_A_LSB)) & 0xffff;
|
||||
return MP_OBJ_NEW_SMALL_INT((uint64_t)cc * 1000000000ULL / slice_hz);
|
||||
} else {
|
||||
// Set duty cycle.
|
||||
mp_int_t duty_ns = mp_obj_get_int(args[1]);
|
||||
uint32_t cc = (uint64_t)duty_ns * slice_hz / 1000000000ULL;
|
||||
if (cc > 65535) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("duty larger than period"));
|
||||
}
|
||||
pwm_set_chan_level(self->slice, self->channel, cc);
|
||||
pwm_set_enabled(self->slice, true);
|
||||
return mp_const_none;
|
||||
}
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_pwm_duty_ns_obj, 1, 2, machine_pwm_duty_ns);
|
||||
|
||||
STATIC const mp_rom_map_elem_t machine_pwm_locals_dict_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&machine_pwm_deinit_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_freq), MP_ROM_PTR(&machine_pwm_freq_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_duty_u16), MP_ROM_PTR(&machine_pwm_duty_u16_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_duty_ns), MP_ROM_PTR(&machine_pwm_duty_ns_obj) },
|
||||
};
|
||||
STATIC MP_DEFINE_CONST_DICT(machine_pwm_locals_dict, machine_pwm_locals_dict_table);
|
||||
|
||||
const mp_obj_type_t machine_pwm_type = {
|
||||
{ &mp_type_type },
|
||||
.name = MP_QSTR_PWM,
|
||||
.print = machine_pwm_print,
|
||||
.make_new = machine_pwm_make_new,
|
||||
.locals_dict = (mp_obj_dict_t *)&machine_pwm_locals_dict,
|
||||
};
|
||||
@@ -0,0 +1,278 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2020-2021 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "py/runtime.h"
|
||||
#include "py/mphal.h"
|
||||
#include "py/mperrno.h"
|
||||
#include "extmod/machine_spi.h"
|
||||
#include "modmachine.h"
|
||||
|
||||
#include "hardware/spi.h"
|
||||
#include "hardware/dma.h"
|
||||
|
||||
#define DEFAULT_SPI_BAUDRATE (1000000)
|
||||
#define DEFAULT_SPI_POLARITY (0)
|
||||
#define DEFAULT_SPI_PHASE (0)
|
||||
#define DEFAULT_SPI_BITS (8)
|
||||
#define DEFAULT_SPI_FIRSTBIT (SPI_MSB_FIRST)
|
||||
#define DEFAULT_SPI0_SCK (6)
|
||||
#define DEFAULT_SPI0_MOSI (7)
|
||||
#define DEFAULT_SPI0_MISO (4)
|
||||
#define DEFAULT_SPI1_SCK (10)
|
||||
#define DEFAULT_SPI1_MOSI (11)
|
||||
#define DEFAULT_SPI1_MISO (8)
|
||||
|
||||
#define IS_VALID_PERIPH(spi, pin) ((((pin) & 8) >> 3) == (spi))
|
||||
#define IS_VALID_SCK(spi, pin) (((pin) & 3) == 2 && IS_VALID_PERIPH(spi, pin))
|
||||
#define IS_VALID_MOSI(spi, pin) (((pin) & 3) == 3 && IS_VALID_PERIPH(spi, pin))
|
||||
#define IS_VALID_MISO(spi, pin) (((pin) & 3) == 0 && IS_VALID_PERIPH(spi, pin))
|
||||
|
||||
typedef struct _machine_spi_obj_t {
|
||||
mp_obj_base_t base;
|
||||
spi_inst_t *const spi_inst;
|
||||
uint8_t spi_id;
|
||||
uint8_t polarity;
|
||||
uint8_t phase;
|
||||
uint8_t bits;
|
||||
uint8_t firstbit;
|
||||
uint8_t sck;
|
||||
uint8_t mosi;
|
||||
uint8_t miso;
|
||||
uint32_t baudrate;
|
||||
} machine_spi_obj_t;
|
||||
|
||||
STATIC machine_spi_obj_t machine_spi_obj[] = {
|
||||
{
|
||||
{&machine_spi_type}, spi0, 0,
|
||||
DEFAULT_SPI_POLARITY, DEFAULT_SPI_PHASE, DEFAULT_SPI_BITS, DEFAULT_SPI_FIRSTBIT,
|
||||
DEFAULT_SPI0_SCK, DEFAULT_SPI0_MOSI, DEFAULT_SPI0_MISO,
|
||||
0,
|
||||
},
|
||||
{
|
||||
{&machine_spi_type}, spi1, 1,
|
||||
DEFAULT_SPI_POLARITY, DEFAULT_SPI_PHASE, DEFAULT_SPI_BITS, DEFAULT_SPI_FIRSTBIT,
|
||||
DEFAULT_SPI1_SCK, DEFAULT_SPI1_MOSI, DEFAULT_SPI1_MISO,
|
||||
0,
|
||||
},
|
||||
};
|
||||
|
||||
STATIC void machine_spi_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
|
||||
machine_spi_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
mp_printf(print, "SPI(%u, baudrate=%u, polarity=%u, phase=%u, bits=%u, sck=%u, mosi=%u, miso=%u)",
|
||||
self->spi_id, self->baudrate, self->polarity, self->phase, self->bits,
|
||||
self->sck, self->mosi, self->miso);
|
||||
}
|
||||
|
||||
mp_obj_t machine_spi_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
|
||||
enum { ARG_id, ARG_baudrate, ARG_polarity, ARG_phase, ARG_bits, ARG_firstbit, ARG_sck, ARG_mosi, ARG_miso };
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_id, MP_ARG_REQUIRED | MP_ARG_OBJ },
|
||||
{ MP_QSTR_baudrate, MP_ARG_INT, {.u_int = DEFAULT_SPI_BAUDRATE} },
|
||||
{ MP_QSTR_polarity, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = DEFAULT_SPI_POLARITY} },
|
||||
{ MP_QSTR_phase, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = DEFAULT_SPI_PHASE} },
|
||||
{ MP_QSTR_bits, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = DEFAULT_SPI_BITS} },
|
||||
{ MP_QSTR_firstbit, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = DEFAULT_SPI_FIRSTBIT} },
|
||||
{ MP_QSTR_sck, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
|
||||
{ MP_QSTR_mosi, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
|
||||
{ MP_QSTR_miso, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
|
||||
};
|
||||
|
||||
// Parse the arguments.
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
// Get the SPI bus id.
|
||||
int spi_id = mp_obj_get_int(args[ARG_id].u_obj);
|
||||
if (spi_id < 0 || spi_id >= MP_ARRAY_SIZE(machine_spi_obj)) {
|
||||
mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("SPI(%d) doesn't exist"), spi_id);
|
||||
}
|
||||
|
||||
// Get static peripheral object.
|
||||
machine_spi_obj_t *self = (machine_spi_obj_t *)&machine_spi_obj[spi_id];
|
||||
|
||||
// Set SCK/MOSI/MISO pins if configured.
|
||||
if (args[ARG_sck].u_obj != mp_const_none) {
|
||||
int sck = mp_hal_get_pin_obj(args[ARG_sck].u_obj);
|
||||
if (!IS_VALID_SCK(self->spi_id, sck)) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("bad SCK pin"));
|
||||
}
|
||||
self->sck = sck;
|
||||
}
|
||||
if (args[ARG_mosi].u_obj != mp_const_none) {
|
||||
int mosi = mp_hal_get_pin_obj(args[ARG_mosi].u_obj);
|
||||
if (!IS_VALID_MOSI(self->spi_id, mosi)) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("bad MOSI pin"));
|
||||
}
|
||||
self->mosi = mosi;
|
||||
}
|
||||
if (args[ARG_miso].u_obj != mp_const_none) {
|
||||
int miso = mp_hal_get_pin_obj(args[ARG_miso].u_obj);
|
||||
if (!IS_VALID_MISO(self->spi_id, miso)) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("bad MISO pin"));
|
||||
}
|
||||
self->miso = miso;
|
||||
}
|
||||
|
||||
// Initialise the SPI peripheral if any arguments given, or it was not initialised previously.
|
||||
if (n_args > 1 || n_kw > 0 || self->baudrate == 0) {
|
||||
self->baudrate = args[ARG_baudrate].u_int;
|
||||
self->polarity = args[ARG_polarity].u_int;
|
||||
self->phase = args[ARG_phase].u_int;
|
||||
self->bits = args[ARG_bits].u_int;
|
||||
self->firstbit = args[ARG_firstbit].u_int;
|
||||
if (self->firstbit == SPI_LSB_FIRST) {
|
||||
mp_raise_NotImplementedError(MP_ERROR_TEXT("LSB"));
|
||||
}
|
||||
|
||||
spi_init(self->spi_inst, self->baudrate);
|
||||
self->baudrate = spi_set_baudrate(self->spi_inst, self->baudrate);
|
||||
spi_set_format(self->spi_inst, self->bits, self->polarity, self->phase, self->firstbit);
|
||||
gpio_set_function(self->sck, GPIO_FUNC_SPI);
|
||||
gpio_set_function(self->miso, GPIO_FUNC_SPI);
|
||||
gpio_set_function(self->mosi, GPIO_FUNC_SPI);
|
||||
}
|
||||
|
||||
return MP_OBJ_FROM_PTR(self);
|
||||
}
|
||||
|
||||
STATIC void machine_spi_init(mp_obj_base_t *self_in, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
enum { ARG_baudrate, ARG_polarity, ARG_phase, ARG_bits, ARG_firstbit };
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_baudrate, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} },
|
||||
{ MP_QSTR_polarity, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} },
|
||||
{ MP_QSTR_phase, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} },
|
||||
{ MP_QSTR_bits, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} },
|
||||
{ MP_QSTR_firstbit, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} },
|
||||
};
|
||||
|
||||
// Parse the arguments.
|
||||
machine_spi_obj_t *self = (machine_spi_obj_t *)self_in;
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
// Reconfigure the baudrate if requested.
|
||||
if (args[ARG_baudrate].u_int != -1) {
|
||||
self->baudrate = spi_set_baudrate(self->spi_inst, args[ARG_baudrate].u_int);
|
||||
}
|
||||
|
||||
// Reconfigure the format if requested.
|
||||
bool set_format = false;
|
||||
if (args[ARG_polarity].u_int != -1) {
|
||||
self->polarity = args[ARG_polarity].u_int;
|
||||
set_format = true;
|
||||
}
|
||||
if (args[ARG_phase].u_int != -1) {
|
||||
self->phase = args[ARG_phase].u_int;
|
||||
set_format = true;
|
||||
}
|
||||
if (args[ARG_bits].u_int != -1) {
|
||||
self->bits = args[ARG_bits].u_int;
|
||||
set_format = true;
|
||||
}
|
||||
if (args[ARG_firstbit].u_int != -1) {
|
||||
self->firstbit = args[ARG_firstbit].u_int;
|
||||
if (self->firstbit == SPI_LSB_FIRST) {
|
||||
mp_raise_NotImplementedError(MP_ERROR_TEXT("LSB"));
|
||||
}
|
||||
}
|
||||
if (set_format) {
|
||||
spi_set_format(self->spi_inst, self->bits, self->polarity, self->phase, self->firstbit);
|
||||
}
|
||||
}
|
||||
|
||||
STATIC void machine_spi_transfer(mp_obj_base_t *self_in, size_t len, const uint8_t *src, uint8_t *dest) {
|
||||
machine_spi_obj_t *self = (machine_spi_obj_t *)self_in;
|
||||
// Use DMA for large transfers if channels are available
|
||||
const size_t dma_min_size_threshold = 32;
|
||||
int chan_tx = -1;
|
||||
int chan_rx = -1;
|
||||
if (len >= dma_min_size_threshold) {
|
||||
// Use two DMA channels to service the two FIFOs
|
||||
chan_tx = dma_claim_unused_channel(false);
|
||||
chan_rx = dma_claim_unused_channel(false);
|
||||
}
|
||||
bool use_dma = chan_rx >= 0 && chan_tx >= 0;
|
||||
// note src is guaranteed to be non-NULL
|
||||
bool write_only = dest == NULL;
|
||||
|
||||
if (use_dma) {
|
||||
uint8_t dev_null;
|
||||
dma_channel_config c = dma_channel_get_default_config(chan_tx);
|
||||
channel_config_set_transfer_data_size(&c, DMA_SIZE_8);
|
||||
channel_config_set_dreq(&c, spi_get_index(self->spi_inst) ? DREQ_SPI1_TX : DREQ_SPI0_TX);
|
||||
dma_channel_configure(chan_tx, &c,
|
||||
&spi_get_hw(self->spi_inst)->dr,
|
||||
src,
|
||||
len,
|
||||
false);
|
||||
|
||||
c = dma_channel_get_default_config(chan_rx);
|
||||
channel_config_set_transfer_data_size(&c, DMA_SIZE_8);
|
||||
channel_config_set_dreq(&c, spi_get_index(self->spi_inst) ? DREQ_SPI1_RX : DREQ_SPI0_RX);
|
||||
channel_config_set_read_increment(&c, false);
|
||||
channel_config_set_write_increment(&c, !write_only);
|
||||
dma_channel_configure(chan_rx, &c,
|
||||
write_only ? &dev_null : dest,
|
||||
&spi_get_hw(self->spi_inst)->dr,
|
||||
len,
|
||||
false);
|
||||
|
||||
dma_start_channel_mask((1u << chan_rx) | (1u << chan_tx));
|
||||
dma_channel_wait_for_finish_blocking(chan_rx);
|
||||
dma_channel_wait_for_finish_blocking(chan_tx);
|
||||
}
|
||||
|
||||
// If we have claimed only one channel successfully, we should release immediately
|
||||
if (chan_rx >= 0) {
|
||||
dma_channel_unclaim(chan_rx);
|
||||
}
|
||||
if (chan_tx >= 0) {
|
||||
dma_channel_unclaim(chan_tx);
|
||||
}
|
||||
|
||||
if (!use_dma) {
|
||||
// Use software for small transfers, or if couldn't claim two DMA channels
|
||||
if (write_only) {
|
||||
spi_write_blocking(self->spi_inst, src, len);
|
||||
} else {
|
||||
spi_write_read_blocking(self->spi_inst, src, dest, len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
STATIC const mp_machine_spi_p_t machine_spi_p = {
|
||||
.init = machine_spi_init,
|
||||
.transfer = machine_spi_transfer,
|
||||
};
|
||||
|
||||
const mp_obj_type_t machine_spi_type = {
|
||||
{ &mp_type_type },
|
||||
.name = MP_QSTR_SPI,
|
||||
.print = machine_spi_print,
|
||||
.make_new = machine_spi_make_new,
|
||||
.protocol = &machine_spi_p,
|
||||
.locals_dict = (mp_obj_dict_t *)&mp_machine_spi_locals_dict,
|
||||
};
|
||||
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2020-2021 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "py/runtime.h"
|
||||
#include "py/mperrno.h"
|
||||
#include "py/mphal.h"
|
||||
#include "pico/time.h"
|
||||
|
||||
#define ALARM_ID_INVALID (-1)
|
||||
#define TIMER_MODE_ONE_SHOT (0)
|
||||
#define TIMER_MODE_PERIODIC (1)
|
||||
|
||||
typedef struct _machine_timer_obj_t {
|
||||
mp_obj_base_t base;
|
||||
struct alarm_pool *pool;
|
||||
alarm_id_t alarm_id;
|
||||
uint32_t mode;
|
||||
uint64_t delta_us; // for periodic mode
|
||||
mp_obj_t callback;
|
||||
} machine_timer_obj_t;
|
||||
|
||||
const mp_obj_type_t machine_timer_type;
|
||||
|
||||
STATIC int64_t alarm_callback(alarm_id_t id, void *user_data) {
|
||||
machine_timer_obj_t *self = user_data;
|
||||
mp_sched_schedule(self->callback, MP_OBJ_FROM_PTR(self));
|
||||
if (self->mode == TIMER_MODE_ONE_SHOT) {
|
||||
return 0;
|
||||
} else {
|
||||
return -self->delta_us;
|
||||
}
|
||||
}
|
||||
|
||||
STATIC void machine_timer_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
|
||||
machine_timer_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
qstr mode = self->mode == TIMER_MODE_ONE_SHOT ? MP_QSTR_ONE_SHOT : MP_QSTR_PERIODIC;
|
||||
mp_printf(print, "Timer(mode=%q, period=%u, tick_hz=1000000)", mode, self->delta_us);
|
||||
}
|
||||
|
||||
STATIC mp_obj_t machine_timer_init_helper(machine_timer_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
enum { ARG_mode, ARG_callback, ARG_period, ARG_tick_hz, ARG_freq, };
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_mode, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = TIMER_MODE_PERIODIC} },
|
||||
{ MP_QSTR_callback, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
|
||||
{ MP_QSTR_period, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0xffffffff} },
|
||||
{ MP_QSTR_tick_hz, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 1000} },
|
||||
{ MP_QSTR_freq, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
|
||||
};
|
||||
|
||||
// Parse args
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
self->mode = args[ARG_mode].u_int;
|
||||
if (args[ARG_freq].u_obj != mp_const_none) {
|
||||
// Frequency specified in Hz
|
||||
#if MICROPY_PY_BUILTINS_FLOAT
|
||||
self->delta_us = (uint64_t)(MICROPY_FLOAT_CONST(1000000.0) / mp_obj_get_float(args[ARG_freq].u_obj));
|
||||
#else
|
||||
self->delta_us = 1000000 / mp_obj_get_int(args[ARG_freq].u_obj);
|
||||
#endif
|
||||
} else {
|
||||
// Period specified
|
||||
self->delta_us = (uint64_t)args[ARG_period].u_int * 1000000 / args[ARG_tick_hz].u_int;
|
||||
}
|
||||
if (self->delta_us < 1) {
|
||||
self->delta_us = 1;
|
||||
}
|
||||
|
||||
self->callback = args[ARG_callback].u_obj;
|
||||
self->alarm_id = alarm_pool_add_alarm_in_us(self->pool, self->delta_us, alarm_callback, self, true);
|
||||
if (self->alarm_id == -1) {
|
||||
mp_raise_OSError(MP_ENOMEM);
|
||||
}
|
||||
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
STATIC mp_obj_t machine_timer_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
|
||||
machine_timer_obj_t *self = m_new_obj_with_finaliser(machine_timer_obj_t);
|
||||
self->base.type = &machine_timer_type;
|
||||
self->pool = alarm_pool_get_default();
|
||||
self->alarm_id = ALARM_ID_INVALID;
|
||||
|
||||
// Get timer id (only soft timer (-1) supported at the moment)
|
||||
mp_int_t id = -1;
|
||||
if (n_args > 0) {
|
||||
id = mp_obj_get_int(args[0]);
|
||||
--n_args;
|
||||
++args;
|
||||
}
|
||||
if (id != -1) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("Timer doesn't exist"));
|
||||
}
|
||||
|
||||
if (n_args > 0 || n_kw > 0) {
|
||||
// Start the timer
|
||||
mp_map_t kw_args;
|
||||
mp_map_init_fixed_table(&kw_args, n_kw, args + n_args);
|
||||
machine_timer_init_helper(self, n_args, args, &kw_args);
|
||||
}
|
||||
|
||||
return MP_OBJ_FROM_PTR(self);
|
||||
}
|
||||
|
||||
STATIC mp_obj_t machine_timer_init(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) {
|
||||
machine_timer_obj_t *self = MP_OBJ_TO_PTR(args[0]);
|
||||
if (self->alarm_id != ALARM_ID_INVALID) {
|
||||
alarm_pool_cancel_alarm(self->pool, self->alarm_id);
|
||||
self->alarm_id = ALARM_ID_INVALID;
|
||||
}
|
||||
return machine_timer_init_helper(self, n_args - 1, args + 1, kw_args);
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(machine_timer_init_obj, 1, machine_timer_init);
|
||||
|
||||
STATIC mp_obj_t machine_timer_deinit(mp_obj_t self_in) {
|
||||
machine_timer_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
if (self->alarm_id != ALARM_ID_INVALID) {
|
||||
alarm_pool_cancel_alarm(self->pool, self->alarm_id);
|
||||
self->alarm_id = ALARM_ID_INVALID;
|
||||
}
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_timer_deinit_obj, machine_timer_deinit);
|
||||
|
||||
STATIC const mp_rom_map_elem_t machine_timer_locals_dict_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&machine_timer_deinit_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&machine_timer_init_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&machine_timer_deinit_obj) },
|
||||
|
||||
{ MP_ROM_QSTR(MP_QSTR_ONE_SHOT), MP_ROM_INT(TIMER_MODE_ONE_SHOT) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_PERIODIC), MP_ROM_INT(TIMER_MODE_PERIODIC) },
|
||||
};
|
||||
STATIC MP_DEFINE_CONST_DICT(machine_timer_locals_dict, machine_timer_locals_dict_table);
|
||||
|
||||
const mp_obj_type_t machine_timer_type = {
|
||||
{ &mp_type_type },
|
||||
.name = MP_QSTR_Timer,
|
||||
.print = machine_timer_print,
|
||||
.make_new = machine_timer_make_new,
|
||||
.locals_dict = (mp_obj_dict_t *)&machine_timer_locals_dict,
|
||||
};
|
||||
@@ -0,0 +1,246 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2020-2021 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "py/runtime.h"
|
||||
#include "py/stream.h"
|
||||
#include "py/mphal.h"
|
||||
#include "py/mperrno.h"
|
||||
#include "modmachine.h"
|
||||
|
||||
#include "hardware/uart.h"
|
||||
|
||||
#define DEFAULT_UART_BAUDRATE (115200)
|
||||
#define DEFAULT_UART_BITS (8)
|
||||
#define DEFAULT_UART_STOP (1)
|
||||
#define DEFAULT_UART0_TX (0)
|
||||
#define DEFAULT_UART0_RX (1)
|
||||
#define DEFAULT_UART1_TX (4)
|
||||
#define DEFAULT_UART1_RX (5)
|
||||
|
||||
#define IS_VALID_PERIPH(uart, pin) (((((pin) + 4) & 8) >> 3) == (uart))
|
||||
#define IS_VALID_TX(uart, pin) (((pin) & 3) == 0 && IS_VALID_PERIPH(uart, pin))
|
||||
#define IS_VALID_RX(uart, pin) (((pin) & 3) == 1 && IS_VALID_PERIPH(uart, pin))
|
||||
|
||||
typedef struct _machine_uart_obj_t {
|
||||
mp_obj_base_t base;
|
||||
uart_inst_t *const uart;
|
||||
uint8_t uart_id;
|
||||
uint32_t baudrate;
|
||||
uint8_t bits;
|
||||
uart_parity_t parity;
|
||||
uint8_t stop;
|
||||
uint8_t tx;
|
||||
uint8_t rx;
|
||||
} machine_uart_obj_t;
|
||||
|
||||
STATIC machine_uart_obj_t machine_uart_obj[] = {
|
||||
{{&machine_uart_type}, uart0, 0, 0, DEFAULT_UART_BITS, UART_PARITY_NONE, DEFAULT_UART_STOP, DEFAULT_UART0_TX, DEFAULT_UART0_RX},
|
||||
{{&machine_uart_type}, uart1, 1, 0, DEFAULT_UART_BITS, UART_PARITY_NONE, DEFAULT_UART_STOP, DEFAULT_UART1_TX, DEFAULT_UART1_RX},
|
||||
};
|
||||
|
||||
STATIC const char *_parity_name[] = {"None", "0", "1"};
|
||||
|
||||
/******************************************************************************/
|
||||
// MicroPython bindings for UART
|
||||
|
||||
STATIC void machine_uart_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
|
||||
machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
mp_printf(print, "UART(%u, baudrate=%u, bits=%u, parity=%s, stop=%u, tx=%d, rx=%d)",
|
||||
self->uart_id, self->baudrate, self->bits, _parity_name[self->parity],
|
||||
self->stop, self->tx, self->rx);
|
||||
}
|
||||
|
||||
STATIC mp_obj_t machine_uart_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
|
||||
enum { ARG_id, ARG_baudrate, ARG_bits, ARG_parity, ARG_stop, ARG_tx, ARG_rx };
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_id, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
|
||||
{ MP_QSTR_baudrate, MP_ARG_INT, {.u_int = -1} },
|
||||
{ MP_QSTR_bits, MP_ARG_INT, {.u_int = -1} },
|
||||
{ MP_QSTR_parity, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_INT(-1)} },
|
||||
{ MP_QSTR_stop, MP_ARG_INT, {.u_int = -1} },
|
||||
{ MP_QSTR_tx, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
|
||||
{ MP_QSTR_rx, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
|
||||
};
|
||||
|
||||
// Parse args.
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
// Get UART bus.
|
||||
int uart_id = mp_obj_get_int(args[ARG_id].u_obj);
|
||||
if (uart_id < 0 || uart_id >= MP_ARRAY_SIZE(machine_uart_obj)) {
|
||||
mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("UART(%d) doesn't exist"), uart_id);
|
||||
}
|
||||
|
||||
// Get static peripheral object.
|
||||
machine_uart_obj_t *self = (machine_uart_obj_t *)&machine_uart_obj[uart_id];
|
||||
|
||||
// Set baudrate if configured.
|
||||
if (args[ARG_baudrate].u_int > 0) {
|
||||
self->baudrate = args[ARG_baudrate].u_int;
|
||||
}
|
||||
|
||||
// Set bits if configured.
|
||||
if (args[ARG_bits].u_int > 0) {
|
||||
self->bits = args[ARG_bits].u_int;
|
||||
}
|
||||
|
||||
// Set parity if configured.
|
||||
if (args[ARG_parity].u_obj != MP_OBJ_NEW_SMALL_INT(-1)) {
|
||||
if (args[ARG_parity].u_obj == mp_const_none) {
|
||||
self->parity = UART_PARITY_NONE;
|
||||
} else if (mp_obj_get_int(args[ARG_parity].u_obj) & 1) {
|
||||
self->parity = UART_PARITY_ODD;
|
||||
} else {
|
||||
self->parity = UART_PARITY_EVEN;
|
||||
}
|
||||
}
|
||||
|
||||
// Set stop bits if configured.
|
||||
if (args[ARG_stop].u_int > 0) {
|
||||
self->stop = args[ARG_stop].u_int;
|
||||
}
|
||||
|
||||
// Set TX/RX pins if configured.
|
||||
if (args[ARG_tx].u_obj != mp_const_none) {
|
||||
int tx = mp_hal_get_pin_obj(args[ARG_tx].u_obj);
|
||||
if (!IS_VALID_TX(self->uart_id, tx)) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("bad TX pin"));
|
||||
}
|
||||
self->tx = tx;
|
||||
}
|
||||
if (args[ARG_rx].u_obj != mp_const_none) {
|
||||
int rx = mp_hal_get_pin_obj(args[ARG_rx].u_obj);
|
||||
if (!IS_VALID_RX(self->uart_id, rx)) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("bad RX pin"));
|
||||
}
|
||||
self->rx = rx;
|
||||
}
|
||||
|
||||
// Initialise the UART peripheral if any arguments given, or it was not initialised previously.
|
||||
if (n_args > 1 || n_kw > 0 || self->baudrate == 0) {
|
||||
if (self->baudrate == 0) {
|
||||
self->baudrate = DEFAULT_UART_BAUDRATE;
|
||||
}
|
||||
uart_init(self->uart, self->baudrate);
|
||||
uart_set_format(self->uart, self->bits, self->stop, self->parity);
|
||||
uart_set_fifo_enabled(self->uart, true);
|
||||
gpio_set_function(self->tx, GPIO_FUNC_UART);
|
||||
gpio_set_function(self->rx, GPIO_FUNC_UART);
|
||||
}
|
||||
|
||||
return MP_OBJ_FROM_PTR(self);
|
||||
}
|
||||
|
||||
STATIC mp_obj_t machine_uart_any(mp_obj_t self_in) {
|
||||
machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
return MP_OBJ_NEW_SMALL_INT(uart_is_readable(self->uart));
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_uart_any_obj, machine_uart_any);
|
||||
|
||||
STATIC mp_obj_t machine_uart_sendbreak(mp_obj_t self_in) {
|
||||
machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
uart_set_break(self->uart, true);
|
||||
mp_hal_delay_us(13000000 / self->baudrate + 1);
|
||||
uart_set_break(self->uart, false);
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_uart_sendbreak_obj, machine_uart_sendbreak);
|
||||
|
||||
STATIC const mp_rom_map_elem_t machine_uart_locals_dict_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR_any), MP_ROM_PTR(&machine_uart_any_obj) },
|
||||
|
||||
{ MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) },
|
||||
|
||||
{ MP_ROM_QSTR(MP_QSTR_sendbreak), MP_ROM_PTR(&machine_uart_sendbreak_obj) },
|
||||
};
|
||||
STATIC MP_DEFINE_CONST_DICT(machine_uart_locals_dict, machine_uart_locals_dict_table);
|
||||
|
||||
STATIC mp_uint_t machine_uart_read(mp_obj_t self_in, void *buf_in, mp_uint_t size, int *errcode) {
|
||||
machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
// TODO support timeout
|
||||
uint8_t *dest = buf_in;
|
||||
for (size_t i = 0; i < size; ++i) {
|
||||
while (!uart_is_readable(self->uart)) {
|
||||
MICROPY_EVENT_POLL_HOOK
|
||||
}
|
||||
*dest++ = uart_get_hw(self->uart)->dr;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
STATIC mp_uint_t machine_uart_write(mp_obj_t self_in, const void *buf_in, mp_uint_t size, int *errcode) {
|
||||
machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
// TODO support timeout
|
||||
const uint8_t *src = buf_in;
|
||||
for (size_t i = 0; i < size; ++i) {
|
||||
while (!uart_is_writable(self->uart)) {
|
||||
MICROPY_EVENT_POLL_HOOK
|
||||
}
|
||||
uart_get_hw(self->uart)->dr = *src++;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
STATIC mp_uint_t machine_uart_ioctl(mp_obj_t self_in, mp_uint_t request, mp_uint_t arg, int *errcode) {
|
||||
machine_uart_obj_t *self = self_in;
|
||||
mp_uint_t ret;
|
||||
if (request == MP_STREAM_POLL) {
|
||||
uintptr_t flags = arg;
|
||||
ret = 0;
|
||||
if ((flags & MP_STREAM_POLL_RD) && uart_is_readable(self->uart)) {
|
||||
ret |= MP_STREAM_POLL_RD;
|
||||
}
|
||||
if ((flags & MP_STREAM_POLL_WR) && uart_is_writable(self->uart)) {
|
||||
ret |= MP_STREAM_POLL_WR;
|
||||
}
|
||||
} else {
|
||||
*errcode = MP_EINVAL;
|
||||
ret = MP_STREAM_ERROR;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
STATIC const mp_stream_p_t uart_stream_p = {
|
||||
.read = machine_uart_read,
|
||||
.write = machine_uart_write,
|
||||
.ioctl = machine_uart_ioctl,
|
||||
.is_text = false,
|
||||
};
|
||||
|
||||
const mp_obj_type_t machine_uart_type = {
|
||||
{ &mp_type_type },
|
||||
.name = MP_QSTR_UART,
|
||||
.print = machine_uart_print,
|
||||
.make_new = machine_uart_make_new,
|
||||
.getiter = mp_identity_getiter,
|
||||
.iternext = mp_stream_unbuffered_iter,
|
||||
.protocol = &uart_stream_p,
|
||||
.locals_dict = (mp_obj_dict_t *)&machine_uart_locals_dict,
|
||||
};
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2020-2021 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "py/runtime.h"
|
||||
#include "modmachine.h"
|
||||
|
||||
#include "hardware/watchdog.h"
|
||||
|
||||
typedef struct _machine_wdt_obj_t {
|
||||
mp_obj_base_t base;
|
||||
} machine_wdt_obj_t;
|
||||
|
||||
STATIC const machine_wdt_obj_t machine_wdt = {{&machine_wdt_type}};
|
||||
|
||||
STATIC mp_obj_t machine_wdt_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
|
||||
enum { ARG_id, ARG_timeout };
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_id, MP_ARG_INT, {.u_int = 0} },
|
||||
{ MP_QSTR_timeout, MP_ARG_INT, {.u_int = 5000} },
|
||||
};
|
||||
|
||||
// Parse the arguments.
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
// Verify the WDT id.
|
||||
mp_int_t id = args[ARG_id].u_int;
|
||||
if (id != 0) {
|
||||
mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("WDT(%d) doesn't exist"), id);
|
||||
}
|
||||
|
||||
// Start the watchdog (timeout is in milliseconds).
|
||||
watchdog_enable(args[ARG_timeout].u_int, false);
|
||||
|
||||
return MP_OBJ_FROM_PTR(&machine_wdt);
|
||||
}
|
||||
|
||||
STATIC mp_obj_t machine_wdt_feed(mp_obj_t self_in) {
|
||||
(void)self_in;
|
||||
watchdog_update();
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_wdt_feed_obj, machine_wdt_feed);
|
||||
|
||||
STATIC const mp_rom_map_elem_t machine_wdt_locals_dict_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR_feed), MP_ROM_PTR(&machine_wdt_feed_obj) },
|
||||
};
|
||||
STATIC MP_DEFINE_CONST_DICT(machine_wdt_locals_dict, machine_wdt_locals_dict_table);
|
||||
|
||||
const mp_obj_type_t machine_wdt_type = {
|
||||
{ &mp_type_type },
|
||||
.name = MP_QSTR_WDT,
|
||||
.make_new = machine_wdt_make_new,
|
||||
.locals_dict = (mp_obj_dict_t *)&machine_wdt_locals_dict,
|
||||
};
|
||||
@@ -0,0 +1,208 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2020-2021 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "py/compile.h"
|
||||
#include "py/runtime.h"
|
||||
#include "py/gc.h"
|
||||
#include "py/mperrno.h"
|
||||
#include "py/stackctrl.h"
|
||||
#include "lib/mp-readline/readline.h"
|
||||
#include "lib/utils/gchelper.h"
|
||||
#include "lib/utils/pyexec.h"
|
||||
#include "tusb.h"
|
||||
#include "uart.h"
|
||||
#include "modmachine.h"
|
||||
#include "modrp2.h"
|
||||
#include "genhdr/mpversion.h"
|
||||
|
||||
#include "pico/stdlib.h"
|
||||
#include "pico/binary_info.h"
|
||||
#include "hardware/rtc.h"
|
||||
#include "hardware/structs/rosc.h"
|
||||
|
||||
extern uint8_t __StackTop, __StackBottom;
|
||||
static char gc_heap[192 * 1024];
|
||||
|
||||
// Embed version info in the binary in machine readable form
|
||||
bi_decl(bi_program_version_string(MICROPY_GIT_TAG));
|
||||
|
||||
// Add a section to the picotool output similar to program features, but for frozen modules
|
||||
// (it will aggregate BINARY_INFO_ID_MP_FROZEN binary info)
|
||||
bi_decl(bi_program_feature_group_with_flags(BINARY_INFO_TAG_MICROPYTHON,
|
||||
BINARY_INFO_ID_MP_FROZEN, "frozen modules",
|
||||
BI_NAMED_GROUP_SEPARATE_COMMAS | BI_NAMED_GROUP_SORT_ALPHA));
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
#if MICROPY_HW_ENABLE_UART_REPL
|
||||
bi_decl(bi_program_feature("UART REPL"))
|
||||
setup_default_uart();
|
||||
mp_uart_init();
|
||||
#endif
|
||||
|
||||
#if MICROPY_HW_ENABLE_USBDEV
|
||||
bi_decl(bi_program_feature("USB REPL"))
|
||||
tusb_init();
|
||||
#endif
|
||||
|
||||
#if MICROPY_PY_THREAD
|
||||
bi_decl(bi_program_feature("thread support"))
|
||||
mp_thread_init();
|
||||
#endif
|
||||
|
||||
// Start and initialise the RTC
|
||||
datetime_t t = {
|
||||
.year = 2021,
|
||||
.month = 1,
|
||||
.day = 1,
|
||||
.dotw = 5, // 0 is Sunday, so 5 is Friday
|
||||
.hour = 0,
|
||||
.min = 0,
|
||||
.sec = 0,
|
||||
};
|
||||
rtc_init();
|
||||
rtc_set_datetime(&t);
|
||||
|
||||
// Initialise stack extents and GC heap.
|
||||
mp_stack_set_top(&__StackTop);
|
||||
mp_stack_set_limit(&__StackTop - &__StackBottom - 256);
|
||||
gc_init(&gc_heap[0], &gc_heap[MP_ARRAY_SIZE(gc_heap)]);
|
||||
|
||||
for (;;) {
|
||||
|
||||
// Initialise MicroPython runtime.
|
||||
mp_init();
|
||||
mp_obj_list_init(MP_OBJ_TO_PTR(mp_sys_path), 0);
|
||||
mp_obj_list_append(mp_sys_path, MP_OBJ_NEW_QSTR(MP_QSTR_));
|
||||
mp_obj_list_append(mp_sys_path, MP_OBJ_NEW_QSTR(MP_QSTR__slash_lib));
|
||||
mp_obj_list_init(MP_OBJ_TO_PTR(mp_sys_argv), 0);
|
||||
|
||||
// Initialise sub-systems.
|
||||
readline_init0();
|
||||
machine_pin_init();
|
||||
rp2_pio_init();
|
||||
|
||||
// Execute _boot.py to set up the filesystem.
|
||||
pyexec_frozen_module("_boot.py");
|
||||
|
||||
// Execute user scripts.
|
||||
pyexec_file_if_exists("boot.py");
|
||||
if (pyexec_mode_kind == PYEXEC_MODE_FRIENDLY_REPL) {
|
||||
pyexec_file_if_exists("main.py");
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
if (pyexec_mode_kind == PYEXEC_MODE_RAW_REPL) {
|
||||
if (pyexec_raw_repl() != 0) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (pyexec_friendly_repl() != 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mp_printf(MP_PYTHON_PRINTER, "MPY: soft reboot\n");
|
||||
rp2_pio_deinit();
|
||||
machine_pin_deinit();
|
||||
gc_sweep_all();
|
||||
mp_deinit();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void gc_collect(void) {
|
||||
gc_collect_start();
|
||||
gc_helper_collect_regs_and_stack();
|
||||
#if MICROPY_PY_THREAD
|
||||
mp_thread_gc_others();
|
||||
#endif
|
||||
gc_collect_end();
|
||||
}
|
||||
|
||||
void nlr_jump_fail(void *val) {
|
||||
printf("FATAL: uncaught exception %p\n", val);
|
||||
mp_obj_print_exception(&mp_plat_print, MP_OBJ_FROM_PTR(val));
|
||||
for (;;) {
|
||||
__breakpoint();
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
void MP_WEAK __assert_func(const char *file, int line, const char *func, const char *expr) {
|
||||
printf("Assertion '%s' failed, at file %s:%d\n", expr, file, line);
|
||||
panic("Assertion failed");
|
||||
}
|
||||
#endif
|
||||
|
||||
uint32_t rosc_random_u32(void) {
|
||||
uint32_t value = 0;
|
||||
for (size_t i = 0; i < 32; ++i) {
|
||||
value = value << 1 | rosc_hw->randombit;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
const char rp2_help_text[] =
|
||||
"Welcome to MicroPython!\n"
|
||||
"\n"
|
||||
"For online help please visit https://micropython.org/help/.\n"
|
||||
"\n"
|
||||
"For access to the hardware use the 'machine' module. RP2 specific commands\n"
|
||||
"are in the 'rp2' module.\n"
|
||||
"\n"
|
||||
"Quick overview of some objects:\n"
|
||||
" machine.Pin(pin) -- get a pin, eg machine.Pin(0)\n"
|
||||
" machine.Pin(pin, m, [p]) -- get a pin and configure it for IO mode m, pull mode p\n"
|
||||
" methods: init(..), value([v]), high(), low(), irq(handler)\n"
|
||||
" machine.ADC(pin) -- make an analog object from a pin\n"
|
||||
" methods: read_u16()\n"
|
||||
" machine.PWM(pin) -- make a PWM object from a pin\n"
|
||||
" methods: deinit(), freq([f]), duty_u16([d]), duty_ns([d])\n"
|
||||
" machine.I2C(id) -- create an I2C object (id=0,1)\n"
|
||||
" methods: readfrom(addr, buf, stop=True), writeto(addr, buf, stop=True)\n"
|
||||
" readfrom_mem(addr, memaddr, arg), writeto_mem(addr, memaddr, arg)\n"
|
||||
" machine.SPI(id, baudrate=1000000) -- create an SPI object (id=0,1)\n"
|
||||
" methods: read(nbytes, write=0x00), write(buf), write_readinto(wr_buf, rd_buf)\n"
|
||||
" machine.Timer(freq, callback) -- create a software timer object\n"
|
||||
" eg: machine.Timer(freq=1, callback=lambda t:print(t))\n"
|
||||
"\n"
|
||||
"Pins are numbered 0-29, and 26-29 have ADC capabilities\n"
|
||||
"Pin IO modes are: Pin.IN, Pin.OUT, Pin.ALT\n"
|
||||
"Pin pull modes are: Pin.PULL_UP, Pin.PULL_DOWN\n"
|
||||
"\n"
|
||||
"Useful control commands:\n"
|
||||
" CTRL-C -- interrupt a running program\n"
|
||||
" CTRL-D -- on a blank line, do a soft reset of the board\n"
|
||||
" CTRL-E -- on a blank line, enter paste mode\n"
|
||||
"\n"
|
||||
"For further help on a specific object, type help(obj)\n"
|
||||
"For a list of available modules, type help('modules')\n"
|
||||
;
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
freeze("modules")
|
||||
freeze("$(MPY_DIR)/drivers/onewire")
|
||||
include("$(MPY_DIR)/extmod/uasyncio/manifest.py")
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user