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:
Damien George
2021-01-21 00:34:08 +11:00
parent ef3ee7aa10
commit 469345e728
44 changed files with 5509 additions and 0 deletions
+35
View File
@@ -0,0 +1,35 @@
# Example using PIO to blink an LED and raise an IRQ at 1Hz.
import time
from machine import Pin
import rp2
@rp2.asm_pio(set_init=rp2.PIO.OUT_LOW)
def blink_1hz():
# fmt: off
# Cycles: 1 + 1 + 6 + 32 * (30 + 1) = 1000
irq(rel(0))
set(pins, 1)
set(x, 31) [5]
label("delay_high")
nop() [29]
jmp(x_dec, "delay_high")
# Cycles: 1 + 7 + 32 * (30 + 1) = 1000
set(pins, 0)
set(x, 31) [6]
label("delay_low")
nop() [29]
jmp(x_dec, "delay_low")
# fmt: on
# Create the StateMachine with the blink_1hz program, outputting on Pin(25).
sm = rp2.StateMachine(0, blink_1hz, freq=2000, set_base=Pin(25))
# Set the IRQ handler to print the millisecond timestamp.
sm.irq(lambda p: print(time.ticks_ms()))
# Start the StateMachine.
sm.active(1)
+27
View File
@@ -0,0 +1,27 @@
# Example using PIO to turn on an LED via an explicit exec.
#
# Demonstrates:
# - using set_init and set_base
# - using StateMachine.exec
import time
from machine import Pin
import rp2
# Define an empty program that uses a single set pin.
@rp2.asm_pio(set_init=rp2.PIO.OUT_LOW)
def prog():
pass
# Construct the StateMachine, binding Pin(25) to the set pin.
sm = rp2.StateMachine(0, prog, set_base=Pin(25))
# Turn on the set pin via an exec instruction.
sm.exec("set(pins, 1)")
# Sleep for 500ms.
time.sleep(0.5)
# Turn off the set pin via an exec instruction.
sm.exec("set(pins, 0)")
+46
View File
@@ -0,0 +1,46 @@
# Example using PIO to wait for a pin change and raise an IRQ.
#
# Demonstrates:
# - PIO wrapping
# - PIO wait instruction, waiting on an input pin
# - PIO irq instruction, in blocking mode with relative IRQ number
# - setting the in_base pin for a StateMachine
# - setting an irq handler for a StateMachine
# - instantiating 2x StateMachine's with the same program and different pins
import time
from machine import Pin
import rp2
@rp2.asm_pio()
def wait_pin_low():
wrap_target()
wait(0, pin, 0)
irq(block, rel(0))
wait(1, pin, 0)
wrap()
def handler(sm):
# Print a (wrapping) timestamp, and the state machine object.
print(time.ticks_ms(), sm)
# Instantiate StateMachine(0) with wait_pin_low program on Pin(16).
pin16 = Pin(16, Pin.IN, Pin.PULL_UP)
sm0 = rp2.StateMachine(0, wait_pin_low, in_base=pin16)
sm0.irq(handler)
# Instantiate StateMachine(1) with wait_pin_low program on Pin(17).
pin17 = Pin(17, Pin.IN, Pin.PULL_UP)
sm1 = rp2.StateMachine(1, wait_pin_low, in_base=pin17)
sm1.irq(handler)
# Start the StateMachine's running.
sm0.active(1)
sm1.active(1)
# Now, when Pin(16) or Pin(17) is pulled low a message will be printed to the REPL.
+45
View File
@@ -0,0 +1,45 @@
# Example of using PIO for PWM, and fading the brightness of an LED
from machine import Pin
from rp2 import PIO, StateMachine, asm_pio
from time import sleep
@asm_pio(sideset_init=PIO.OUT_LOW)
def pwm_prog():
# fmt: off
pull(noblock) .side(0)
mov(x, osr) # Keep most recent pull data stashed in X, for recycling by noblock
mov(y, isr) # ISR must be preloaded with PWM count max
label("pwmloop")
jmp(x_not_y, "skip")
nop() .side(1)
label("skip")
jmp(y_dec, "pwmloop")
# fmt: on
class PIOPWM:
def __init__(self, sm_id, pin, max_count, count_freq):
self._sm = StateMachine(sm_id, pwm_prog, freq=2 * count_freq, sideset_base=Pin(pin))
# Use exec() to load max count into ISR
self._sm.put(max_count)
self._sm.exec("pull()")
self._sm.exec("mov(isr, osr)")
self._sm.active(1)
self._max_count = max_count
def set(self, value):
# Minimum value is -1 (completely turn off), 0 actually still produces narrow pulse
value = max(value, -1)
value = min(value, self._max_count)
self._sm.put(value)
# Pin 25 is LED on Pico boards
pwm = PIOPWM(0, 25, max_count=(1 << 16) - 1, count_freq=10_000_000)
while True:
for i in range(256):
pwm.set(i ** 2)
sleep(0.01)
+44
View File
@@ -0,0 +1,44 @@
# Example using PIO to create a UART TX interface
from machine import Pin
from rp2 import PIO, StateMachine, asm_pio
UART_BAUD = 115200
PIN_BASE = 10
NUM_UARTS = 8
@asm_pio(sideset_init=PIO.OUT_HIGH, out_init=PIO.OUT_HIGH, out_shiftdir=PIO.SHIFT_RIGHT)
def uart_tx():
# fmt: off
# Block with TX deasserted until data available
pull()
# Initialise bit counter, assert start bit for 8 cycles
set(x, 7) .side(0) [7]
# Shift out 8 data bits, 8 execution cycles per bit
label("bitloop")
out(pins, 1) [6]
jmp(x_dec, "bitloop")
# Assert stop bit for 8 cycles total (incl 1 for pull())
nop() .side(1) [6]
# fmt: on
# Now we add 8 UART TXs, on pins 10 to 17. Use the same baud rate for all of them.
uarts = []
for i in range(NUM_UARTS):
sm = StateMachine(
i, uart_tx, freq=8 * UART_BAUD, sideset_base=Pin(PIN_BASE + i), out_base=Pin(PIN_BASE + i)
)
sm.active(1)
uarts.append(sm)
# We can print characters from each UART by pushing them to the TX FIFO
def pio_uart_print(sm, s):
for c in s:
sm.put(ord(c))
# Print a different message from each UART
for i, u in enumerate(uarts):
pio_uart_print(u, "Hello from UART {}!\n".format(i))
+59
View File
@@ -0,0 +1,59 @@
# Example using PIO to drive a set of WS2812 LEDs.
import array, time
from machine import Pin
import rp2
# Configure the number of WS2812 LEDs.
NUM_LEDS = 8
@rp2.asm_pio(
sideset_init=rp2.PIO.OUT_LOW,
out_shiftdir=rp2.PIO.SHIFT_LEFT,
autopull=True,
pull_thresh=24,
)
def ws2812():
# fmt: off
T1 = 2
T2 = 5
T3 = 3
wrap_target()
label("bitloop")
out(x, 1) .side(0) [T3 - 1]
jmp(not_x, "do_zero") .side(1) [T1 - 1]
jmp("bitloop") .side(1) [T2 - 1]
label("do_zero")
nop() .side(0) [T2 - 1]
wrap()
# fmt: on
# Create the StateMachine with the ws2812 program, outputting on Pin(22).
sm = rp2.StateMachine(0, ws2812, freq=8_000_000, sideset_base=Pin(22))
# Start the StateMachine, it will wait for data on its FIFO.
sm.active(1)
# Display a pattern on the LEDs via an array of LED RGB values.
ar = array.array("I", [0 for _ in range(NUM_LEDS)])
# Cycle colours.
for i in range(4 * NUM_LEDS):
for j in range(NUM_LEDS):
r = j * 100 // (NUM_LEDS - 1)
b = 100 - j * 100 // (NUM_LEDS - 1)
if j != i % NUM_LEDS:
r >>= 3
b >>= 3
ar[j] = r << 16 | b
sm.put(ar, 8)
time.sleep_ms(50)
# Fade out.
for i in range(24):
for j in range(NUM_LEDS):
ar[j] = ar[j] >> 1 & 0x7F7F7F
sm.put(ar, 8)
time.sleep_ms(50)
+25
View File
@@ -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)
+162
View File
@@ -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
)
+14
View File
@@ -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)
+100
View File
@@ -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.
+120
View File
@@ -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,
};
+161
View File
@@ -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,
};
+449
View File
@@ -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;
}
+197
View File
@@ -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,
};
+278
View File
@@ -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,
};
+165
View File
@@ -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,
};
+246
View File
@@ -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,
};
+78
View File
@@ -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,
};
+208
View File
@@ -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"
;
+3
View File
@@ -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