aboutsummaryrefslogtreecommitdiff
path: root/circuitpython/ports/raspberrypi/common-hal
diff options
context:
space:
mode:
authorRaghuram Subramani <raghus2247@gmail.com>2022-06-19 19:47:51 +0530
committerRaghuram Subramani <raghus2247@gmail.com>2022-06-19 19:47:51 +0530
commit4fd287655a72b9aea14cdac715ad5b90ed082ed2 (patch)
tree65d393bc0e699dd12d05b29ba568e04cea666207 /circuitpython/ports/raspberrypi/common-hal
parent0150f70ce9c39e9e6dd878766c0620c85e47bed0 (diff)
add circuitpython code
Diffstat (limited to 'circuitpython/ports/raspberrypi/common-hal')
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/alarm/SleepMemory.c48
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/alarm/SleepMemory.h38
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/alarm/__init__.c258
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/alarm/__init__.h42
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/alarm/pin/PinAlarm.c164
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/alarm/pin/PinAlarm.h45
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/alarm/time/TimeAlarm.c130
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/alarm/time/TimeAlarm.h41
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/alarm/touch/TouchAlarm.c32
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/alarm/touch/TouchAlarm.h38
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/analogio/AnalogIn.c74
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/analogio/AnalogIn.h41
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/analogio/AnalogOut.c48
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/analogio/AnalogOut.h36
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/analogio/__init__.c1
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/audiobusio/I2SOut.c242
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/audiobusio/I2SOut.h48
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/audiobusio/PDMIn.c175
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/audiobusio/PDMIn.h50
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/audiobusio/__init__.c1
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c287
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.h48
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/audiopwmio/__init__.c0
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/board/__init__.c34
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/busio/I2C.c242
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/busio/I2C.h49
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/busio/SPI.c290
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/busio/SPI.h52
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/busio/UART.c339
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/busio/UART.h54
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/busio/__init__.c1
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/countio/Counter.c115
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/countio/Counter.h21
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/countio/__init__.c1
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/digitalio/DigitalInOut.c191
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/digitalio/DigitalInOut.h40
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/digitalio/__init__.c1
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/floppyio/__init__.h37
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/imagecapture/ParallelImageCapture.c159
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/imagecapture/ParallelImageCapture.h35
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/imagecapture/__init__.c0
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/imagecapture/__init__.h0
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/microcontroller/Pin.c105
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/microcontroller/Pin.h45
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/microcontroller/Processor.c99
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/microcontroller/Processor.h41
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/microcontroller/__init__.c184
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/microcontroller/__init__.h36
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/neopixel_write/__init__.c104
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/nvm/ByteArray.c121
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/nvm/ByteArray.h38
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/nvm/__init__.c1
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/os/__init__.c127
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/paralleldisplay/ParallelBus.c170
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/paralleldisplay/ParallelBus.h45
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/pulseio/PulseIn.c244
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/pulseio/PulseIn.h53
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/pulseio/PulseOut.c134
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/pulseio/PulseOut.h52
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/pulseio/__init__.c1
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/pwmio/PWMOut.c307
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/pwmio/PWMOut.h58
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/pwmio/__init__.c1
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/rgbmatrix/RGBMatrix.c75
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/rgbmatrix/RGBMatrix.h37
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/rgbmatrix/__init__.c0
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/rotaryio/IncrementalEncoder.c135
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/rotaryio/IncrementalEncoder.h42
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/rotaryio/__init__.c1
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/rotaryio/__init__.h0
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/rp2pio/StateMachine.c1034
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/rp2pio/StateMachine.h100
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/rp2pio/__init__.c43
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/rtc/RTC.c96
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/rtc/RTC.h32
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/rtc/__init__.c1
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/supervisor/Runtime.c37
-rwxr-xr-xcircuitpython/ports/raspberrypi/common-hal/supervisor/Runtime.h37
-rwxr-xr-xcircuitpython/ports/raspberrypi/common-hal/supervisor/__init__.c40
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/watchdog/WatchDogMode.c1
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/watchdog/WatchDogTimer.c83
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/watchdog/WatchDogTimer.h43
-rw-r--r--circuitpython/ports/raspberrypi/common-hal/watchdog/__init__.c1
83 files changed, 7352 insertions, 0 deletions
diff --git a/circuitpython/ports/raspberrypi/common-hal/alarm/SleepMemory.c b/circuitpython/ports/raspberrypi/common-hal/alarm/SleepMemory.c
new file mode 100644
index 0000000..c176529
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/alarm/SleepMemory.c
@@ -0,0 +1,48 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 Lucian Copeland for Adafruit Industries
+ *
+ * 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 <string.h>
+
+#include "py/runtime.h"
+#include "common-hal/alarm/SleepMemory.h"
+#include "shared-bindings/alarm/SleepMemory.h"
+
+void alarm_sleep_memory_reset(void) {
+}
+
+uint32_t common_hal_alarm_sleep_memory_get_length(alarm_sleep_memory_obj_t *self) {
+ mp_raise_NotImplementedError(translate("Sleep Memory not available"));
+ return 0;
+}
+
+bool common_hal_alarm_sleep_memory_set_bytes(alarm_sleep_memory_obj_t *self, uint32_t start_index, const uint8_t *values, uint32_t len) {
+ mp_raise_NotImplementedError(translate("Sleep Memory not available"));
+ return false;
+}
+
+void common_hal_alarm_sleep_memory_get_bytes(alarm_sleep_memory_obj_t *self, uint32_t start_index, uint8_t *values, uint32_t len) {
+ mp_raise_NotImplementedError(translate("Sleep Memory not available"));
+}
diff --git a/circuitpython/ports/raspberrypi/common-hal/alarm/SleepMemory.h b/circuitpython/ports/raspberrypi/common-hal/alarm/SleepMemory.h
new file mode 100644
index 0000000..59d0429
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/alarm/SleepMemory.h
@@ -0,0 +1,38 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 Lucian Copeland for Adafruit Industries
+ *
+ * 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.
+ */
+
+#ifndef MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_ALARM_SLEEPMEMORY_H
+#define MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_ALARM_SLEEPMEMORY_H
+
+#include "py/obj.h"
+
+typedef struct {
+ mp_obj_base_t base;
+} alarm_sleep_memory_obj_t;
+
+extern void alarm_sleep_memory_reset(void);
+
+#endif // MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_ALARM_SLEEPMEMORY_H
diff --git a/circuitpython/ports/raspberrypi/common-hal/alarm/__init__.c b/circuitpython/ports/raspberrypi/common-hal/alarm/__init__.c
new file mode 100644
index 0000000..0d67345
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/alarm/__init__.c
@@ -0,0 +1,258 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 Lucian Copeland for Adafruit Industries
+ *
+ * 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/gc.h"
+#include "py/obj.h"
+#include "py/objtuple.h"
+#include "py/runtime.h"
+#include "shared/runtime/interrupt_char.h"
+
+#include "shared-bindings/alarm/__init__.h"
+#include "shared-bindings/alarm/SleepMemory.h"
+#include "shared-bindings/alarm/pin/PinAlarm.h"
+#include "shared-bindings/alarm/time/TimeAlarm.h"
+#include "shared-bindings/alarm/touch/TouchAlarm.h"
+
+#include "shared-bindings/microcontroller/__init__.h"
+
+#include "supervisor/port.h"
+#include "supervisor/shared/workflow.h"
+
+#include "pico/stdlib.h"
+#include "hardware/sync.h"
+#include "hardware/clocks.h"
+#include "hardware/xosc.h"
+#include "hardware/structs/scb.h"
+#include "hardware/watchdog.h"
+#include "hardware/structs/watchdog.h"
+
+// XOSC shutdown
+#include "hardware/rtc.h"
+#include "hardware/pll.h"
+#include "hardware/regs/io_bank0.h"
+
+// Watchdog scratch register
+// Not used elsewhere in the SDK for now, keep an eye on it
+#define RP_WKUP_SCRATCH_REG 0
+
+// Light sleep turns off nonvolatile Busio and other wake-only peripherals
+// TODO: this only saves about 2mA right now, expand with other non-essentials
+const uint32_t RP_LIGHTSLEEP_EN0_MASK = ~(
+ CLOCKS_SLEEP_EN0_CLK_SYS_SPI1_BITS |
+ CLOCKS_SLEEP_EN0_CLK_PERI_SPI1_BITS |
+ CLOCKS_SLEEP_EN0_CLK_SYS_SPI0_BITS |
+ CLOCKS_SLEEP_EN0_CLK_PERI_SPI0_BITS |
+ CLOCKS_SLEEP_EN0_CLK_SYS_PWM_BITS |
+ CLOCKS_SLEEP_EN0_CLK_SYS_PIO1_BITS |
+ CLOCKS_SLEEP_EN0_CLK_SYS_PIO0_BITS |
+ CLOCKS_SLEEP_EN0_CLK_SYS_I2C1_BITS |
+ CLOCKS_SLEEP_EN0_CLK_SYS_I2C0_BITS |
+ CLOCKS_SLEEP_EN0_CLK_SYS_ADC_BITS |
+ CLOCKS_SLEEP_EN0_CLK_ADC_ADC_BITS
+ );
+// This bank has the USB clocks in it, leave it for now
+const uint32_t RP_LIGHTSLEEP_EN1_MASK = CLOCKS_SLEEP_EN1_RESET;
+
+// Light sleeps used for TimeAlarm deep sleep turn off almost everything
+const uint32_t RP_LIGHTSLEEP_EN0_MASK_HARSH = (
+ CLOCKS_SLEEP_EN0_CLK_RTC_RTC_BITS |
+ CLOCKS_SLEEP_EN0_CLK_SYS_PADS_BITS
+ );
+const uint32_t RP_LIGHTSLEEP_EN1_MASK_HARSH = 0x0;
+
+STATIC void prepare_for_dormant_xosc(void);
+
+// Singleton instance of SleepMemory.
+const alarm_sleep_memory_obj_t alarm_sleep_memory_obj = {
+ .base = {
+ .type = &alarm_sleep_memory_type,
+ },
+};
+
+void alarm_reset(void) {
+ alarm_sleep_memory_reset();
+ alarm_pin_pinalarm_reset();
+ alarm_time_timealarm_reset();
+
+ // Reset the scratch source
+ watchdog_hw->scratch[RP_WKUP_SCRATCH_REG] = RP_SLEEP_WAKEUP_UNDEF;
+}
+
+STATIC uint8_t _get_wakeup_cause(void) {
+ // First check if the modules remember what last woke up
+ if (alarm_pin_pinalarm_woke_this_cycle()) {
+ return RP_SLEEP_WAKEUP_GPIO;
+ }
+ if (alarm_time_timealarm_woke_this_cycle()) {
+ return RP_SLEEP_WAKEUP_RTC;
+ }
+ // If waking from true deep sleep, modules will have lost their state,
+ // so check the deep wakeup cause manually
+ if (watchdog_hw->scratch[RP_WKUP_SCRATCH_REG] != RP_SLEEP_WAKEUP_UNDEF) {
+ return watchdog_hw->scratch[RP_WKUP_SCRATCH_REG];
+ }
+ return RP_SLEEP_WAKEUP_UNDEF;
+}
+
+// Set up light sleep or deep sleep alarms.
+STATIC void _setup_sleep_alarms(bool deep_sleep, size_t n_alarms, const mp_obj_t *alarms) {
+ alarm_pin_pinalarm_set_alarms(deep_sleep, n_alarms, alarms);
+ alarm_time_timealarm_set_alarms(deep_sleep, n_alarms, alarms);
+}
+
+bool common_hal_alarm_woken_from_sleep(void) {
+ return _get_wakeup_cause() != RP_SLEEP_WAKEUP_UNDEF;
+}
+
+mp_obj_t common_hal_alarm_create_wake_alarm(void) {
+ // If woken from deep sleep, create a copy alarm similar to what would have
+ // been passed in originally. Otherwise, just return none
+ uint8_t cause = _get_wakeup_cause();
+ switch (cause) {
+ case RP_SLEEP_WAKEUP_RTC: {
+ return alarm_time_timealarm_create_wakeup_alarm();
+ }
+
+ case RP_SLEEP_WAKEUP_GPIO: {
+ return alarm_pin_pinalarm_create_wakeup_alarm();
+ }
+
+ case RP_SLEEP_WAKEUP_UNDEF:
+ default:
+ // Not a deep sleep reset.
+ break;
+ }
+ return mp_const_none;
+}
+
+mp_obj_t common_hal_alarm_light_sleep_until_alarms(size_t n_alarms, const mp_obj_t *alarms) {
+ _setup_sleep_alarms(false, n_alarms, alarms);
+
+ mp_obj_t wake_alarm = mp_const_none;
+
+ while (!mp_hal_is_interrupted()) {
+ RUN_BACKGROUND_TASKS;
+ // Detect if interrupt was alarm or ctrl-C interrupt.
+ if (common_hal_alarm_woken_from_sleep()) {
+ uint8_t cause = _get_wakeup_cause();
+ switch (cause) {
+ case RP_SLEEP_WAKEUP_RTC: {
+ wake_alarm = alarm_time_timealarm_find_triggered_alarm(n_alarms,alarms);
+ break;
+ }
+ case RP_SLEEP_WAKEUP_GPIO: {
+ wake_alarm = alarm_pin_pinalarm_find_triggered_alarm(n_alarms,alarms);
+ break;
+ }
+ default:
+ // Should not reach this, if all light sleep types are covered correctly
+ break;
+ }
+ shared_alarm_save_wake_alarm(wake_alarm);
+ break;
+ }
+
+ // Prune the clock for sleep
+ clocks_hw->sleep_en0 &= RP_LIGHTSLEEP_EN0_MASK;
+ clocks_hw->sleep_en1 = RP_LIGHTSLEEP_EN1_MASK;
+
+ // Enable System Control Block (SCB) deep sleep
+ uint save = scb_hw->scr;
+ scb_hw->scr = save | M0PLUS_SCR_SLEEPDEEP_BITS;
+
+ __wfi();
+ }
+
+ if (mp_hal_is_interrupted()) {
+ return mp_const_none; // Shouldn't be given to python code because exception handling should kick in.
+ }
+
+ alarm_reset();
+ return wake_alarm;
+}
+
+void common_hal_alarm_set_deep_sleep_alarms(size_t n_alarms, const mp_obj_t *alarms) {
+ _setup_sleep_alarms(true, n_alarms, alarms);
+}
+
+void NORETURN common_hal_alarm_enter_deep_sleep(void) {
+ bool timealarm_set = alarm_time_timealarm_is_set();
+
+ // If there's a timealarm, just enter a very deep light sleep
+ if (timealarm_set) {
+ // Prune the clock for sleep
+ clocks_hw->sleep_en0 &= RP_LIGHTSLEEP_EN0_MASK_HARSH;
+ clocks_hw->sleep_en1 = RP_LIGHTSLEEP_EN1_MASK_HARSH;
+ // Enable System Control Block (SCB) deep sleep
+ uint save = scb_hw->scr;
+ scb_hw->scr = save | M0PLUS_SCR_SLEEPDEEP_BITS;
+ __wfi();
+ } else {
+ prepare_for_dormant_xosc();
+ xosc_dormant();
+ }
+ // // TODO: support ROSC when available in SDK
+ // rosc_set_dormant();
+
+ // Reset uses the watchdog. Use scratch registers to store wake reason
+ watchdog_hw->scratch[RP_WKUP_SCRATCH_REG] = _get_wakeup_cause();
+ reset_cpu();
+}
+
+void common_hal_alarm_gc_collect(void) {
+ gc_collect_ptr(shared_alarm_get_wake_alarm());
+}
+
+STATIC void prepare_for_dormant_xosc(void) {
+ // TODO: add ROSC support with sleep_run_from_dormant_source when it's added to SDK
+ uint src_hz = XOSC_MHZ * MHZ;
+ uint clk_ref_src = CLOCKS_CLK_REF_CTRL_SRC_VALUE_XOSC_CLKSRC;
+ clock_configure(clk_ref,
+ clk_ref_src,
+ 0, // No aux mux
+ src_hz,
+ src_hz);
+ clock_configure(clk_sys,
+ CLOCKS_CLK_SYS_CTRL_SRC_VALUE_CLK_REF,
+ 0, // Using glitchless mux
+ src_hz,
+ src_hz);
+ clock_stop(clk_usb);
+ clock_stop(clk_adc);
+ uint clk_rtc_src = CLOCKS_CLK_RTC_CTRL_AUXSRC_VALUE_XOSC_CLKSRC;
+ clock_configure(clk_rtc,
+ 0, // No GLMUX
+ clk_rtc_src,
+ src_hz,
+ 46875);
+ clock_configure(clk_peri,
+ 0,
+ CLOCKS_CLK_PERI_CTRL_AUXSRC_VALUE_CLK_SYS,
+ src_hz,
+ src_hz);
+ pll_deinit(pll_sys);
+ pll_deinit(pll_usb);
+}
diff --git a/circuitpython/ports/raspberrypi/common-hal/alarm/__init__.h b/circuitpython/ports/raspberrypi/common-hal/alarm/__init__.h
new file mode 100644
index 0000000..3d5d86f
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/alarm/__init__.h
@@ -0,0 +1,42 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 Lucian Copeland for Adafruit Industries
+ *
+ * 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.
+ */
+
+#ifndef MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_ALARM__INIT__H
+#define MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_ALARM__INIT__H
+
+#include "common-hal/alarm/SleepMemory.h"
+
+#include "hardware/regs/clocks.h"
+
+#define RP_SLEEP_WAKEUP_UNDEF 0
+#define RP_SLEEP_WAKEUP_GPIO 1
+#define RP_SLEEP_WAKEUP_RTC 2
+
+extern const alarm_sleep_memory_obj_t alarm_sleep_memory_obj;
+
+extern void alarm_reset(void);
+
+#endif // MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_ALARM__INIT__H
diff --git a/circuitpython/ports/raspberrypi/common-hal/alarm/pin/PinAlarm.c b/circuitpython/ports/raspberrypi/common-hal/alarm/pin/PinAlarm.c
new file mode 100644
index 0000000..53cbfca
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/alarm/pin/PinAlarm.c
@@ -0,0 +1,164 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 Lucian Copeland for Adafruit Industries
+ *
+ * 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 "shared-bindings/alarm/pin/PinAlarm.h"
+#include "shared-bindings/microcontroller/__init__.h"
+#include "common-hal/microcontroller/__init__.h"
+#include "shared-bindings/microcontroller/Pin.h"
+
+#include "pico/stdlib.h"
+#include "hardware/gpio.h"
+#include "hardware/structs/iobank0.h"
+
+STATIC bool woke_up;
+STATIC uint64_t alarm_triggered_pins; // 36 actual pins
+STATIC uint64_t alarm_reserved_pins; // 36 actual pins
+STATIC bool _pinalarm_set = false;
+
+#define GPIO_IRQ_ALL_EVENTS 0x15u
+
+STATIC void gpio_callback(uint gpio, uint32_t events) {
+ alarm_triggered_pins |= (1 << gpio);
+ woke_up = true;
+
+ // does this need to be called, to prevent IRQ from constantly going off?
+ gpio_acknowledge_irq(gpio, events);
+
+ // Disable IRQ automatically
+ gpio_set_irq_enabled(gpio, events, false);
+ gpio_set_dormant_irq_enabled(gpio, events, false);
+}
+
+void common_hal_alarm_pin_pinalarm_construct(alarm_pin_pinalarm_obj_t *self, const mcu_pin_obj_t *pin, bool value, bool edge, bool pull) {
+ self->pin = pin;
+ self->value = value;
+ self->edge = edge;
+ self->pull = pull;
+}
+
+const mcu_pin_obj_t *common_hal_alarm_pin_pinalarm_get_pin(alarm_pin_pinalarm_obj_t *self) {
+ return self->pin;
+}
+
+bool common_hal_alarm_pin_pinalarm_get_value(alarm_pin_pinalarm_obj_t *self) {
+ return self->value;
+}
+
+bool common_hal_alarm_pin_pinalarm_get_edge(alarm_pin_pinalarm_obj_t *self) {
+ return self->edge;
+}
+
+bool common_hal_alarm_pin_pinalarm_get_pull(alarm_pin_pinalarm_obj_t *self) {
+ return self->pull;
+}
+
+bool alarm_pin_pinalarm_woke_this_cycle(void) {
+ return woke_up;
+}
+
+mp_obj_t alarm_pin_pinalarm_find_triggered_alarm(size_t n_alarms, const mp_obj_t *alarms) {
+ for (size_t i = 0; i < n_alarms; i++) {
+ if (!mp_obj_is_type(alarms[i], &alarm_pin_pinalarm_type)) {
+ continue;
+ }
+ alarm_pin_pinalarm_obj_t *alarm = MP_OBJ_TO_PTR(alarms[i]);
+ if (alarm_triggered_pins & (1 << alarm->pin->number)) {
+ return alarms[i];
+ }
+ }
+ return mp_const_none;
+}
+
+mp_obj_t alarm_pin_pinalarm_create_wakeup_alarm(void) {
+ alarm_pin_pinalarm_obj_t *alarm = m_new_obj(alarm_pin_pinalarm_obj_t);
+ alarm->base.type = &alarm_pin_pinalarm_type;
+ // TODO: how to obtain the correct pin from memory?
+ return alarm;
+}
+
+void alarm_pin_pinalarm_reset(void) {
+ alarm_triggered_pins = 0;
+ woke_up = false;
+
+ // Clear all GPIO interrupts
+ for (uint8_t i = 0; i < 4; i++) {
+ iobank0_hw->intr[i] = 0;
+ }
+
+ // Reset pins and pin IRQs
+ for (size_t i = 0; i < TOTAL_GPIO_COUNT; i++) {
+ if (alarm_reserved_pins & (1 << i)) {
+ gpio_set_irq_enabled(i, GPIO_IRQ_ALL_EVENTS, false);
+ reset_pin_number(i);
+ }
+ }
+ alarm_reserved_pins = 0;
+}
+
+void alarm_pin_pinalarm_set_alarms(bool deep_sleep, size_t n_alarms, const mp_obj_t *alarms) {
+ for (size_t i = 0; i < n_alarms; i++) {
+ if (mp_obj_is_type(alarms[i], &alarm_pin_pinalarm_type)) {
+ alarm_pin_pinalarm_obj_t *alarm = MP_OBJ_TO_PTR(alarms[i]);
+
+ gpio_init(alarm->pin->number);
+ if (alarm->pull) {
+ // If value is high, the pullup should be off, and vice versa
+ gpio_set_pulls(alarm->pin->number, !alarm->value, alarm->value);
+ } else {
+ // Clear in case the pulls are already on
+ gpio_set_pulls(alarm->pin->number, false, false);
+ }
+ gpio_set_dir(alarm->pin->number, GPIO_IN);
+ // Don't reset at end of VM (instead, pinalarm_reset will reset before next VM)
+ common_hal_never_reset_pin(alarm->pin);
+ alarm_reserved_pins |= (1 << alarm->pin->number);
+
+ uint32_t event;
+ if (alarm->value == true && alarm->edge == true) {
+ event = GPIO_IRQ_EDGE_RISE;
+ } else if (alarm->value == false && alarm->edge == true) {
+ event = GPIO_IRQ_EDGE_FALL;
+ } else if (alarm->value == true && alarm->edge == false) {
+ event = GPIO_IRQ_LEVEL_HIGH;
+ } else { // both false
+ event = GPIO_IRQ_LEVEL_LOW;
+ }
+
+ gpio_set_irq_enabled_with_callback((uint)alarm->pin->number, event, true, &gpio_callback);
+ if (deep_sleep) {
+ gpio_set_dormant_irq_enabled((uint)alarm->pin->number, event, true);
+ }
+
+ _pinalarm_set = true;
+ }
+ }
+}
+
+bool alarm_pin_pinalarm_is_set(void) {
+ return _pinalarm_set;
+}
diff --git a/circuitpython/ports/raspberrypi/common-hal/alarm/pin/PinAlarm.h b/circuitpython/ports/raspberrypi/common-hal/alarm/pin/PinAlarm.h
new file mode 100644
index 0000000..d31ac6c
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/alarm/pin/PinAlarm.h
@@ -0,0 +1,45 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 Lucian Copeland for Adafruit Industries
+ *
+ * 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/obj.h"
+#include "py/objtuple.h"
+
+typedef struct {
+ mp_obj_base_t base;
+ const mcu_pin_obj_t *pin;
+ bool value;
+ bool pull;
+ bool edge;
+} alarm_pin_pinalarm_obj_t;
+
+mp_obj_t alarm_pin_pinalarm_find_triggered_alarm(size_t n_alarms, const mp_obj_t *alarms);
+mp_obj_t alarm_pin_pinalarm_create_wakeup_alarm(void);
+
+void alarm_pin_pinalarm_reset(void);
+void alarm_pin_pinalarm_light_reset(void);
+void alarm_pin_pinalarm_set_alarms(bool deep_sleep, size_t n_alarms, const mp_obj_t *alarms);
+bool alarm_pin_pinalarm_woke_this_cycle(void);
+bool alarm_pin_pinalarm_is_set(void);
diff --git a/circuitpython/ports/raspberrypi/common-hal/alarm/time/TimeAlarm.c b/circuitpython/ports/raspberrypi/common-hal/alarm/time/TimeAlarm.c
new file mode 100644
index 0000000..16ac8fc
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/alarm/time/TimeAlarm.c
@@ -0,0 +1,130 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 Lucian Copeland for Adafruit Industries
+ *
+ * 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 "shared-bindings/alarm/time/TimeAlarm.h"
+#include "shared-bindings/time/__init__.h"
+
+#include "shared/timeutils/timeutils.h"
+
+#include "hardware/gpio.h"
+#include "hardware/rtc.h"
+
+STATIC bool woke_up = false;
+STATIC bool _timealarm_set = false;
+
+STATIC void timer_callback(void) {
+ woke_up = true;
+}
+
+void common_hal_alarm_time_timealarm_construct(alarm_time_timealarm_obj_t *self, mp_float_t monotonic_time) {
+ self->monotonic_time = monotonic_time;
+}
+
+mp_float_t common_hal_alarm_time_timealarm_get_monotonic_time(alarm_time_timealarm_obj_t *self) {
+ return self->monotonic_time;
+}
+
+mp_obj_t alarm_time_timealarm_find_triggered_alarm(size_t n_alarms, const mp_obj_t *alarms) {
+ for (size_t i = 0; i < n_alarms; i++) {
+ if (mp_obj_is_type(alarms[i], &alarm_time_timealarm_type)) {
+ return alarms[i];
+ }
+ }
+ return mp_const_none;
+}
+
+mp_obj_t alarm_time_timealarm_create_wakeup_alarm(void) {
+ alarm_time_timealarm_obj_t *timer = m_new_obj(alarm_time_timealarm_obj_t);
+ timer->base.type = &alarm_time_timealarm_type;
+ // TODO: Set monotonic_time based on the RTC state.
+ timer->monotonic_time = 0.0f;
+ return timer;
+}
+
+bool alarm_time_timealarm_woke_this_cycle(void) {
+ return woke_up;
+}
+
+void alarm_time_timealarm_reset(void) {
+ rtc_disable_alarm();
+ woke_up = false;
+}
+
+void alarm_time_timealarm_set_alarms(bool deep_sleep, size_t n_alarms, const mp_obj_t *alarms) {
+ bool timealarm_set = false;
+ alarm_time_timealarm_obj_t *timealarm = MP_OBJ_NULL;
+
+ for (size_t i = 0; i < n_alarms; i++) {
+ if (!mp_obj_is_type(alarms[i], &alarm_time_timealarm_type)) {
+ continue;
+ }
+ if (timealarm_set) {
+ mp_raise_ValueError(translate("Only one alarm.time alarm can be set."));
+ }
+ timealarm = MP_OBJ_TO_PTR(alarms[i]);
+ timealarm_set = true;
+ }
+ if (!timealarm_set) {
+ return;
+ }
+ if (deep_sleep) {
+ _timealarm_set = true;
+ }
+
+ // Compute how long to actually sleep, considering the time now.
+ mp_float_t mono_seconds_to_date = uint64_to_float(common_hal_time_monotonic_ms()) / 1000.0f;
+ mp_float_t wakeup_in_secs = MAX(0.0f, timealarm->monotonic_time - mono_seconds_to_date);
+ datetime_t t;
+
+ rtc_get_datetime(&t);
+
+ uint32_t rtc_seconds_to_date = timeutils_seconds_since_2000(t.year, t.month,
+ t.day, t.hour, t.min, t.sec);
+
+ // The float value is always slightly under, so add 1 to compensate
+ uint32_t alarm_seconds = rtc_seconds_to_date + (uint32_t)wakeup_in_secs + 1;
+ timeutils_struct_time_t tm;
+ timeutils_seconds_since_2000_to_struct_time(alarm_seconds, &tm);
+
+ // reuse t
+ t.hour = tm.tm_hour;
+ t.min = tm.tm_min;
+ t.sec = tm.tm_sec;
+ t.day = tm.tm_mday;
+ t.month = tm.tm_mon;
+ t.year = tm.tm_year;
+ t.dotw = (tm.tm_wday + 1) % 7;
+
+ rtc_set_alarm(&t, &timer_callback);
+
+ woke_up = false;
+}
+
+bool alarm_time_timealarm_is_set(void) {
+ return _timealarm_set;
+}
diff --git a/circuitpython/ports/raspberrypi/common-hal/alarm/time/TimeAlarm.h b/circuitpython/ports/raspberrypi/common-hal/alarm/time/TimeAlarm.h
new file mode 100644
index 0000000..d524855
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/alarm/time/TimeAlarm.h
@@ -0,0 +1,41 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 Lucian Copeland for Adafruit Industries
+ *
+ * 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/obj.h"
+
+typedef struct {
+ mp_obj_base_t base;
+ mp_float_t monotonic_time;
+} alarm_time_timealarm_obj_t;
+
+mp_obj_t alarm_time_timealarm_find_triggered_alarm(size_t n_alarms, const mp_obj_t *alarms);
+mp_obj_t alarm_time_timealarm_create_wakeup_alarm(void);
+
+void alarm_time_timealarm_reset(void);
+void alarm_time_timealarm_set_alarms(bool deep_sleep, size_t n_alarms, const mp_obj_t *alarms);
+bool alarm_time_timealarm_woke_this_cycle(void);
+bool alarm_time_timealarm_is_set(void);
diff --git a/circuitpython/ports/raspberrypi/common-hal/alarm/touch/TouchAlarm.c b/circuitpython/ports/raspberrypi/common-hal/alarm/touch/TouchAlarm.c
new file mode 100644
index 0000000..88c7372
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/alarm/touch/TouchAlarm.c
@@ -0,0 +1,32 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 Lucian Copeland for Adafruit Industries
+ *
+ * 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 "shared-bindings/alarm/touch/TouchAlarm.h"
+#include "shared-bindings/microcontroller/__init__.h"
+
+void common_hal_alarm_touch_touchalarm_construct(alarm_touch_touchalarm_obj_t *self, const mcu_pin_obj_t *pin) {
+ mp_raise_NotImplementedError(translate("Touch alarms not available"));
+}
diff --git a/circuitpython/ports/raspberrypi/common-hal/alarm/touch/TouchAlarm.h b/circuitpython/ports/raspberrypi/common-hal/alarm/touch/TouchAlarm.h
new file mode 100644
index 0000000..f631293
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/alarm/touch/TouchAlarm.h
@@ -0,0 +1,38 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 Lucian Copeland for Adafruit Industries
+ *
+ * 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.
+ */
+
+#ifndef MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_ALARM_TOUCH_TOUCHALARM_H
+#define MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_ALARM_TOUCH_TOUCHALARM_H
+
+#include "py/obj.h"
+#include "common-hal/microcontroller/Pin.h"
+
+typedef struct {
+ mp_obj_base_t base;
+ const mcu_pin_obj_t *pin;
+} alarm_touch_touchalarm_obj_t;
+
+#endif // MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_ALARM_TOUCH_TOUCHALARM_H
diff --git a/circuitpython/ports/raspberrypi/common-hal/analogio/AnalogIn.c b/circuitpython/ports/raspberrypi/common-hal/analogio/AnalogIn.c
new file mode 100644
index 0000000..1d77630
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/analogio/AnalogIn.c
@@ -0,0 +1,74 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 Scott Shawcroft for Adafruit Industries
+ *
+ * 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 "common-hal/analogio/AnalogIn.h"
+#include "shared-bindings/analogio/AnalogIn.h"
+#include "py/runtime.h"
+#include "supervisor/shared/translate.h"
+
+#include "src/rp2_common/hardware_adc/include/hardware/adc.h"
+
+#define ADC_FIRST_PIN_NUMBER 26
+#define ADC_PIN_COUNT 4
+
+void common_hal_analogio_analogin_construct(analogio_analogin_obj_t *self, const mcu_pin_obj_t *pin) {
+ if (pin->number < ADC_FIRST_PIN_NUMBER || pin->number > ADC_FIRST_PIN_NUMBER + ADC_PIN_COUNT) {
+ mp_raise_ValueError(translate("Pin does not have ADC capabilities"));
+ }
+
+ adc_init();
+
+ adc_gpio_init(pin->number);
+
+ claim_pin(pin);
+ self->pin = pin;
+}
+
+bool common_hal_analogio_analogin_deinited(analogio_analogin_obj_t *self) {
+ return self->pin == NULL;
+}
+
+void common_hal_analogio_analogin_deinit(analogio_analogin_obj_t *self) {
+ if (common_hal_analogio_analogin_deinited(self)) {
+ return;
+ }
+
+ reset_pin_number(self->pin->number);
+ self->pin = NULL;
+}
+
+uint16_t common_hal_analogio_analogin_get_value(analogio_analogin_obj_t *self) {
+ adc_select_input(self->pin->number - ADC_FIRST_PIN_NUMBER);
+ uint16_t value = adc_read();
+
+ // Map value to from 12 to 16 bits
+ return value << 4;
+}
+
+float common_hal_analogio_analogin_get_reference_voltage(analogio_analogin_obj_t *self) {
+ // The nominal VCC voltage
+ return 3.3f;
+}
diff --git a/circuitpython/ports/raspberrypi/common-hal/analogio/AnalogIn.h b/circuitpython/ports/raspberrypi/common-hal/analogio/AnalogIn.h
new file mode 100644
index 0000000..c322a67
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/analogio/AnalogIn.h
@@ -0,0 +1,41 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 Scott Shawcroft for Adafruit Industries
+ *
+ * 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.
+ */
+
+#ifndef MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_ANALOGIO_ANALOGIN_H
+#define MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_ANALOGIO_ANALOGIN_H
+
+#include "common-hal/microcontroller/Pin.h"
+
+#include "py/obj.h"
+
+typedef struct {
+ mp_obj_base_t base;
+ const mcu_pin_obj_t *pin;
+} analogio_analogin_obj_t;
+
+void analogin_init(void);
+
+#endif // MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_ANALOGIO_ANALOGIN_H
diff --git a/circuitpython/ports/raspberrypi/common-hal/analogio/AnalogOut.c b/circuitpython/ports/raspberrypi/common-hal/analogio/AnalogOut.c
new file mode 100644
index 0000000..7afa773
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/analogio/AnalogOut.c
@@ -0,0 +1,48 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Dan Halbert for Adafruit Industries
+ *
+ * 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 "shared-bindings/analogio/AnalogOut.h"
+
+#include <stdint.h>
+#include <string.h>
+
+#include "py/mperrno.h"
+#include "py/runtime.h"
+#include "supervisor/shared/translate.h"
+
+void common_hal_analogio_analogout_construct(analogio_analogout_obj_t *self, const mcu_pin_obj_t *pin) {
+ mp_raise_RuntimeError(translate("AnalogOut functionality not supported"));
+}
+
+bool common_hal_analogio_analogout_deinited(analogio_analogout_obj_t *self) {
+ return true;
+}
+
+void common_hal_analogio_analogout_deinit(analogio_analogout_obj_t *self) {
+}
+
+void common_hal_analogio_analogout_set_value(analogio_analogout_obj_t *self, uint16_t value) {
+}
diff --git a/circuitpython/ports/raspberrypi/common-hal/analogio/AnalogOut.h b/circuitpython/ports/raspberrypi/common-hal/analogio/AnalogOut.h
new file mode 100644
index 0000000..7c7a61a
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/analogio/AnalogOut.h
@@ -0,0 +1,36 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Scott Shawcroft
+ *
+ * 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.
+ */
+
+#ifndef MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_ANALOGIO_ANALOGOUT_H
+#define MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_ANALOGIO_ANALOGOUT_H
+
+#include "py/obj.h"
+
+typedef struct {
+ mp_obj_base_t base;
+} analogio_analogout_obj_t;
+
+#endif // MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_ANALOGIO_ANALOGOUT_H
diff --git a/circuitpython/ports/raspberrypi/common-hal/analogio/__init__.c b/circuitpython/ports/raspberrypi/common-hal/analogio/__init__.c
new file mode 100644
index 0000000..eea58c7
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/analogio/__init__.c
@@ -0,0 +1 @@
+// No analogio module functions.
diff --git a/circuitpython/ports/raspberrypi/common-hal/audiobusio/I2SOut.c b/circuitpython/ports/raspberrypi/common-hal/audiobusio/I2SOut.c
new file mode 100644
index 0000000..0371634
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/audiobusio/I2SOut.c
@@ -0,0 +1,242 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 Scott Shawcroft for Adafruit Industries
+ *
+ * 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 <stdint.h>
+#include <string.h>
+
+#include "mpconfigport.h"
+
+#include "py/gc.h"
+#include "py/mperrno.h"
+#include "py/runtime.h"
+#include "common-hal/audiobusio/I2SOut.h"
+#include "shared-bindings/audiobusio/I2SOut.h"
+#include "shared-bindings/microcontroller/Pin.h"
+#include "shared-module/audiocore/__init__.h"
+#include "bindings/rp2pio/StateMachine.h"
+#include "supervisor/shared/translate.h"
+
+const uint16_t i2s_program[] = {
+// ; Load the next set of samples
+// ; /--- LRCLK
+// ; |/-- BCLK
+// ; ||
+// pull noblock side 0b01 ; Loads OSR with the next FIFO value or X
+ 0x8880,
+// mov x osr side 0b01 ; Save the new value in case we need it again
+ 0xa827,
+// set y 14 side 0b01
+ 0xe84e,
+// bitloop1:
+// out pins 1 side 0b00 [2]
+ 0x6201,
+// jmp y-- bitloop1 side 0b01 [2]
+ 0x0a83,
+// out pins 1 side 0b10 [2]
+ 0x7201,
+// set y 14 side 0b11 [2]
+ 0xfa4e,
+// bitloop0:
+// out pins 1 side 0b10 [2]
+ 0x7201,
+// jmp y-- bitloop0 side 0b11 [2]
+ 0x1a87,
+// out pins 1 side 0b00 [2]
+ 0x6201
+};
+
+const uint16_t i2s_program_left_justified[] = {
+// ; Load the next set of samples
+// ; /--- LRCLK
+// ; |/-- BCLK
+// ; ||
+// pull noblock side 0b11 ; Loads OSR with the next FIFO value or X
+ 0x9880,
+// mov x osr side 0b11 ; Save the new value in case we need it again
+ 0xb827,
+// set y 14 side 0b11
+ 0xf84e,
+// bitloop1:
+// out pins 1 side 0b00 [2]
+ 0x6201,
+// jmp y-- bitloop1 side 0b01 [2]
+ 0x0a83,
+// out pins 1 side 0b10 [2]
+ 0x6201,
+// set y 14 side 0b01 [2]
+ 0xea4e,
+// bitloop0:
+// out pins 1 side 0b10 [2]
+ 0x7201,
+// jmp y-- bitloop0 side 0b11 [2]
+ 0x1a87,
+// out pins 1 side 0b10 [2]
+ 0x7201
+};
+
+void i2sout_reset(void) {
+}
+
+// Caller validates that pins are free.
+void common_hal_audiobusio_i2sout_construct(audiobusio_i2sout_obj_t *self,
+ const mcu_pin_obj_t *bit_clock, const mcu_pin_obj_t *word_select,
+ const mcu_pin_obj_t *data, bool left_justified) {
+ if (bit_clock->number != word_select->number - 1) {
+ mp_raise_ValueError(translate("Bit clock and word select must be sequential pins"));
+ }
+
+ const uint16_t *program = i2s_program;
+ size_t program_len = sizeof(i2s_program) / sizeof(i2s_program[0]);
+ if (left_justified) {
+ program = i2s_program_left_justified;
+ program_len = sizeof(i2s_program_left_justified) / sizeof(i2s_program_left_justified[0]);
+ ;
+ }
+
+ // Use the state machine to manage pins.
+ common_hal_rp2pio_statemachine_construct(
+ &self->state_machine,
+ program, program_len,
+ 44100 * 32 * 6, // Clock at 44.1 khz to warm the DAC up.
+ NULL, 0,
+ data, 1, 0, 0xffffffff, // out pin
+ NULL, 0, // in pins
+ 0, 0, // in pulls
+ NULL, 0, 0, 0x1f, // set pins
+ bit_clock, 2, 0, 0x1f, // sideset pins
+ false, // No sideset enable
+ NULL, PULL_NONE, // jump pin
+ 0, // wait gpio pins
+ true, // exclusive pin use
+ false, 32, false, // shift out left to start with MSB
+ false, // Wait for txstall
+ false, 32, false, // in settings
+ false, // Not user-interruptible.
+ 0, -1); // wrap settings
+
+ self->playing = false;
+ audio_dma_init(&self->dma);
+}
+
+bool common_hal_audiobusio_i2sout_deinited(audiobusio_i2sout_obj_t *self) {
+ return common_hal_rp2pio_statemachine_deinited(&self->state_machine);
+}
+
+void common_hal_audiobusio_i2sout_deinit(audiobusio_i2sout_obj_t *self) {
+ if (common_hal_audiobusio_i2sout_deinited(self)) {
+ return;
+ }
+
+ if (common_hal_audiobusio_i2sout_get_playing(self)) {
+ common_hal_audiobusio_i2sout_stop(self);
+ }
+
+ common_hal_rp2pio_statemachine_deinit(&self->state_machine);
+
+ audio_dma_deinit(&self->dma);
+}
+
+void common_hal_audiobusio_i2sout_play(audiobusio_i2sout_obj_t *self,
+ mp_obj_t sample, bool loop) {
+ if (common_hal_audiobusio_i2sout_get_playing(self)) {
+ common_hal_audiobusio_i2sout_stop(self);
+ }
+
+ uint8_t bits_per_sample = audiosample_bits_per_sample(sample);
+ // Make sure we transmit a minimum of 16 bits.
+ // TODO: Maybe we need an intermediate object to upsample instead. This is
+ // only needed for some I2S devices that expect at least 8.
+ if (bits_per_sample < 16) {
+ bits_per_sample = 16;
+ }
+ // We always output stereo so output twice as many bits.
+ uint16_t bits_per_sample_output = bits_per_sample * 2;
+ size_t clocks_per_bit = 6;
+ uint32_t frequency = bits_per_sample_output * audiosample_sample_rate(sample);
+ uint8_t channel_count = audiosample_channel_count(sample);
+ if (channel_count > 2) {
+ mp_raise_ValueError(translate("Too many channels in sample."));
+ }
+
+ common_hal_rp2pio_statemachine_set_frequency(&self->state_machine, clocks_per_bit * frequency);
+ common_hal_rp2pio_statemachine_restart(&self->state_machine);
+
+ // On the RP2040, output registers are always written with a 32-bit write.
+ // If the write is 8 or 16 bits wide, the data will be replicated in upper bytes.
+ // See section 2.1.4 Narrow IO Register Writes in the RP2040 datasheet.
+ // This means that identical 16-bit audio data will be written in both halves of the incoming PIO
+ // FIFO register. Thus we get mono-to-stereo conversion for the I2S output for free.
+ audio_dma_result result = audio_dma_setup_playback(
+ &self->dma,
+ sample,
+ loop,
+ false, // single channel
+ 0, // audio channel
+ true, // output signed
+ bits_per_sample,
+ (uint32_t)&self->state_machine.pio->txf[self->state_machine.state_machine], // output register
+ self->state_machine.tx_dreq); // data request line
+
+ if (result == AUDIO_DMA_DMA_BUSY) {
+ common_hal_audiobusio_i2sout_stop(self);
+ mp_raise_RuntimeError(translate("No DMA channel found"));
+ } else if (result == AUDIO_DMA_MEMORY_ERROR) {
+ common_hal_audiobusio_i2sout_stop(self);
+ mp_raise_RuntimeError(translate("Unable to allocate buffers for signed conversion"));
+ }
+
+ self->playing = true;
+}
+
+void common_hal_audiobusio_i2sout_pause(audiobusio_i2sout_obj_t *self) {
+ audio_dma_pause(&self->dma);
+}
+
+void common_hal_audiobusio_i2sout_resume(audiobusio_i2sout_obj_t *self) {
+ // Maybe: Clear any overrun/underrun errors
+
+ audio_dma_resume(&self->dma);
+}
+
+bool common_hal_audiobusio_i2sout_get_paused(audiobusio_i2sout_obj_t *self) {
+ return audio_dma_get_paused(&self->dma);
+}
+
+void common_hal_audiobusio_i2sout_stop(audiobusio_i2sout_obj_t *self) {
+ audio_dma_stop(&self->dma);
+
+ common_hal_rp2pio_statemachine_stop(&self->state_machine);
+
+ self->playing = false;
+}
+
+bool common_hal_audiobusio_i2sout_get_playing(audiobusio_i2sout_obj_t *self) {
+ bool playing = audio_dma_get_playing(&self->dma);
+ if (!playing && self->playing) {
+ common_hal_audiobusio_i2sout_stop(self);
+ }
+ return playing;
+}
diff --git a/circuitpython/ports/raspberrypi/common-hal/audiobusio/I2SOut.h b/circuitpython/ports/raspberrypi/common-hal/audiobusio/I2SOut.h
new file mode 100644
index 0000000..52226ae
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/audiobusio/I2SOut.h
@@ -0,0 +1,48 @@
+
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 Scott Shawcroft for Adafruit Industries
+ *
+ * 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.
+ */
+
+#ifndef MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_AUDIOBUSIO_I2SOUT_H
+#define MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_AUDIOBUSIO_I2SOUT_H
+
+#include "common-hal/microcontroller/Pin.h"
+#include "common-hal/rp2pio/StateMachine.h"
+
+#include "audio_dma.h"
+#include "py/obj.h"
+
+// We don't bit pack because we'll only have two at most. Its better to save code size instead.
+typedef struct {
+ mp_obj_base_t base;
+ rp2pio_statemachine_obj_t state_machine;
+ audio_dma_t dma;
+ bool left_justified;
+ bool playing;
+} audiobusio_i2sout_obj_t;
+
+void i2sout_reset(void);
+
+#endif // MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_AUDIOBUSIO_I2SOUT_H
diff --git a/circuitpython/ports/raspberrypi/common-hal/audiobusio/PDMIn.c b/circuitpython/ports/raspberrypi/common-hal/audiobusio/PDMIn.c
new file mode 100644
index 0000000..8301f9f
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/audiobusio/PDMIn.c
@@ -0,0 +1,175 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 Scott Shawcroft for Adafruit Industries
+ *
+ * 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 <stdint.h>
+#include <string.h>
+#include <math.h>
+
+#include "py/mperrno.h"
+#include "py/runtime.h"
+#include "shared-bindings/audiobusio/PDMIn.h"
+#include "shared-bindings/microcontroller/Pin.h"
+#include "supervisor/shared/translate.h"
+
+#include "audio_dma.h"
+
+#define OVERSAMPLING 64
+#define SAMPLES_PER_BUFFER 32
+
+// MEMS microphones must be clocked at at least 1MHz.
+#define MIN_MIC_CLOCK 1000000
+
+const uint16_t pdmin[] = {
+ // in pins 1 side 0b1
+ 0x5001,
+ // push iffull side 0b0
+ 0x8040
+};
+
+// Caller validates that pins are free.
+void common_hal_audiobusio_pdmin_construct(audiobusio_pdmin_obj_t *self,
+ const mcu_pin_obj_t *clock_pin,
+ const mcu_pin_obj_t *data_pin,
+ uint32_t sample_rate,
+ uint8_t bit_depth,
+ bool mono,
+ uint8_t oversample) {
+ if (!(bit_depth == 16 || bit_depth == 8) || !mono || oversample != OVERSAMPLING) {
+ mp_raise_NotImplementedError(translate("Only 8 or 16 bit mono with " MP_STRINGIFY(OVERSAMPLING) "x oversampling is supported."));
+ }
+
+ // Use the state machine to manage pins.
+ common_hal_rp2pio_statemachine_construct(&self->state_machine,
+ pdmin, MP_ARRAY_SIZE(pdmin),
+ sample_rate * 32 * 2, // Frequency based on sample rate
+ NULL, 0,
+ NULL, 1, 0, 0xffffffff, // out pin
+ data_pin, 1, // in pins
+ 0, 0, // in pulls
+ NULL, 0, 0, 0x1f, // set pins
+ clock_pin, 1, 0, 0x1f, // sideset pins
+ false, // No sideset enable
+ NULL, PULL_NONE, // jump pin
+ 0, // wait gpio pins
+ true, // exclusive pin use
+ false, 32, false, // out settings
+ false, // Wait for txstall
+ false, 32, true, // in settings
+ false, // Not user-interruptible.
+ 0, -1); // wrap settings
+
+ uint32_t actual_frequency = common_hal_rp2pio_statemachine_get_frequency(&self->state_machine);
+ if (actual_frequency < MIN_MIC_CLOCK) {
+ mp_raise_ValueError(translate("sampling rate out of range"));
+ }
+
+ self->sample_rate = actual_frequency / oversample;
+ self->bit_depth = bit_depth;
+}
+
+bool common_hal_audiobusio_pdmin_deinited(audiobusio_pdmin_obj_t *self) {
+ return common_hal_rp2pio_statemachine_deinited(&self->state_machine);
+}
+
+void common_hal_audiobusio_pdmin_deinit(audiobusio_pdmin_obj_t *self) {
+ if (common_hal_audiobusio_pdmin_deinited(self)) {
+ return;
+ }
+ return common_hal_rp2pio_statemachine_deinit(&self->state_machine);
+}
+
+uint8_t common_hal_audiobusio_pdmin_get_bit_depth(audiobusio_pdmin_obj_t *self) {
+ return self->bit_depth;
+}
+
+uint32_t common_hal_audiobusio_pdmin_get_sample_rate(audiobusio_pdmin_obj_t *self) {
+ return self->sample_rate;
+}
+
+// a windowed sinc filter for 44 khz, 64 samples
+//
+// This filter is good enough to use for lower sample rates as
+// well. It does not increase the noise enough to be a problem.
+//
+// In the long run we could use a fast filter like this to do the
+// decimation and initial filtering in real time, filtering to a
+// higher sample rate than specified. Then after the audio is
+// recorded, a more expensive filter non-real-time filter could be
+// used to down-sample and low-pass.
+const uint16_t sinc_filter[OVERSAMPLING] = {
+ 0, 2, 9, 21, 39, 63, 94, 132,
+ 179, 236, 302, 379, 467, 565, 674, 792,
+ 920, 1055, 1196, 1341, 1487, 1633, 1776, 1913,
+ 2042, 2159, 2263, 2352, 2422, 2474, 2506, 2516,
+ 2506, 2474, 2422, 2352, 2263, 2159, 2042, 1913,
+ 1776, 1633, 1487, 1341, 1196, 1055, 920, 792,
+ 674, 565, 467, 379, 302, 236, 179, 132,
+ 94, 63, 39, 21, 9, 2, 0, 0
+};
+
+#define REPEAT_32_TIMES(X) do { X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X } while (0)
+
+static uint16_t filter_sample(uint32_t pdm_samples[2]) {
+ uint16_t running_sum = 0;
+ const uint16_t *filter_ptr = sinc_filter;
+ for (uint8_t i = 0; i < 2; i++) {
+ uint32_t pdm_sample = pdm_samples[i];
+ REPEAT_32_TIMES({
+ if (pdm_sample & 0x1) {
+ running_sum += *filter_ptr;
+ }
+ filter_ptr++;
+ pdm_sample >>= 1;
+ }
+ );
+ }
+ return running_sum;
+}
+
+// output_buffer may be a byte buffer or a halfword buffer.
+// output_buffer_length is the number of slots, not the number of bytes.
+uint32_t common_hal_audiobusio_pdmin_record_to_buffer(audiobusio_pdmin_obj_t *self,
+ uint16_t *output_buffer, uint32_t output_buffer_length) {
+ uint32_t samples[2];
+ size_t output_count = 0;
+ common_hal_rp2pio_statemachine_clear_rxfifo(&self->state_machine);
+ // Do one read to get the mic going and throw it away.
+ common_hal_rp2pio_statemachine_readinto(&self->state_machine, (uint8_t *)samples, 2 * sizeof(uint32_t), sizeof(uint32_t), false);
+ while (output_count < output_buffer_length && !common_hal_rp2pio_statemachine_get_rxstall(&self->state_machine)) {
+ common_hal_rp2pio_statemachine_readinto(&self->state_machine, (uint8_t *)samples, 2 * sizeof(uint32_t), sizeof(uint32_t), false);
+ // Call filter_sample just one place so it can be inlined.
+ uint16_t value = filter_sample(samples);
+ if (self->bit_depth == 8) {
+ // Truncate to 8 bits.
+ ((uint8_t *)output_buffer)[output_count] = value >> 8;
+ } else {
+ output_buffer[output_count] = value;
+ }
+ output_count++;
+ }
+
+ return output_count;
+}
diff --git a/circuitpython/ports/raspberrypi/common-hal/audiobusio/PDMIn.h b/circuitpython/ports/raspberrypi/common-hal/audiobusio/PDMIn.h
new file mode 100644
index 0000000..995ffb5
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/audiobusio/PDMIn.h
@@ -0,0 +1,50 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 Scott Shawcroft for Adafruit Industries
+ *
+ * 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.
+ */
+
+#ifndef MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_AUDIOBUSIO_AUDIOOUT_H
+#define MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_AUDIOBUSIO_AUDIOOUT_H
+
+#include "common-hal/microcontroller/Pin.h"
+#include "bindings/rp2pio/StateMachine.h"
+
+#include "extmod/vfs_fat.h"
+#include "py/obj.h"
+
+typedef struct {
+ mp_obj_base_t base;
+ uint32_t sample_rate;
+ uint8_t serializer;
+ uint8_t clock_unit;
+ uint8_t bytes_per_sample;
+ uint8_t bit_depth;
+ rp2pio_statemachine_obj_t state_machine;
+} audiobusio_pdmin_obj_t;
+
+void pdmin_reset(void);
+
+void pdmin_background(void);
+
+#endif // MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_AUDIOBUSIO_AUDIOOUT_H
diff --git a/circuitpython/ports/raspberrypi/common-hal/audiobusio/__init__.c b/circuitpython/ports/raspberrypi/common-hal/audiobusio/__init__.c
new file mode 100644
index 0000000..87db404
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/audiobusio/__init__.c
@@ -0,0 +1 @@
+// No audiobusio module functions.
diff --git a/circuitpython/ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c b/circuitpython/ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c
new file mode 100644
index 0000000..2f09124
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c
@@ -0,0 +1,287 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Jeff Epler for Adafruit Industries
+ *
+ * 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 "common-hal/audiopwmio/PWMAudioOut.h"
+
+#include <math.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "extmod/vfs_fat.h"
+#include "py/gc.h"
+#include "py/mperrno.h"
+#include "py/runtime.h"
+#include "shared-bindings/pwmio/PWMOut.h"
+#include "shared-bindings/audiopwmio/PWMAudioOut.h"
+#include "shared-bindings/microcontroller/__init__.h"
+#include "shared-bindings/microcontroller/Pin.h"
+#include "shared-bindings/microcontroller/Processor.h"
+#include "supervisor/shared/translate.h"
+
+#include "src/rp2040/hardware_structs/include/hardware/structs/dma.h"
+#include "src/rp2_common/hardware_pwm/include/hardware/pwm.h"
+
+// The PWM clock frequency is base_clock_rate / PWM_TOP, typically 125_000_000 / PWM_TOP.
+// We pick BITS_PER_SAMPLE so we get a clock frequency that is above what would cause aliasing.
+#define BITS_PER_SAMPLE 10
+#define SAMPLE_BITS_TO_DISCARD (16 - BITS_PER_SAMPLE)
+#define PWM_TOP ((1 << BITS_PER_SAMPLE) - 1)
+
+
+static uint32_t gcd(uint32_t a, uint32_t b) {
+ while (b) {
+ uint32_t tmp = a % b;
+ a = b;
+ b = tmp;
+ }
+ return a;
+}
+
+static uint32_t limit_denominator(uint32_t max_denominator, uint32_t num_in, uint32_t den_in, uint32_t *den_out) {
+// Algorithm based on Python's limit_denominator
+ uint32_t p0 = 0, q0 = 1, p1 = 1, q1 = 0;
+ uint32_t d = den_in, n = num_in;
+ uint32_t g = gcd(n, d);
+ d /= g;
+ n /= g;
+ if (d < max_denominator) {
+ *den_out = d;
+ return n;
+ }
+ while (1) {
+ uint32_t a = n / d;
+ uint32_t q2 = q0 + a * q1;
+ if (q2 > max_denominator) {
+ break;
+ }
+
+ uint32_t p_tmp = p0 + a * p1;
+ p0 = p1;
+ q0 = q1;
+ p1 = p_tmp;
+ q1 = q2;
+
+ uint32_t d_tmp = n - a * d;
+ n = d;
+ d = d_tmp;
+ }
+ uint32_t k = (max_denominator - q0) / q1;
+ uint32_t bound1_num = p0 + k * p1, bound1_den = q0 + k * q1;
+ uint32_t bound2_num = p1, bound2_den = q1;
+
+ if (fabsf((float)bound1_num / bound1_den - (float)num_in / den_in) <=
+ fabsf((float)bound2_num / bound2_den - (float)num_in / den_in)) {
+ *den_out = bound2_den;
+ return bound2_num;
+ }
+
+ *den_out = bound1_den;
+ return bound1_num;
+}
+
+void audiopwmout_reset() {
+ for (size_t i = 0; i < NUM_DMA_TIMERS; i++) {
+ dma_hw->timer[i] = 0;
+ }
+}
+
+// Caller validates that pins are free.
+void common_hal_audiopwmio_pwmaudioout_construct(audiopwmio_pwmaudioout_obj_t *self,
+ const mcu_pin_obj_t *left_channel, const mcu_pin_obj_t *right_channel, uint16_t quiescent_value) {
+
+ self->stereo = right_channel != NULL;
+
+ if (self->stereo) {
+ if (pwm_gpio_to_slice_num(left_channel->number) != pwm_gpio_to_slice_num(right_channel->number)) {
+ mp_raise_ValueError(translate("Pins must share PWM slice"));
+ }
+ if (pwm_gpio_to_channel(left_channel->number) != 0) {
+ mp_raise_ValueError(translate("Stereo left must be on PWM channel A"));
+ }
+ if (pwm_gpio_to_channel(right_channel->number) != 1) {
+ mp_raise_ValueError(translate("Stereo right must be on PWM channel B"));
+ }
+ }
+
+ // Typically pwmout doesn't let us change frequency with two objects on the
+ // same PWM slice. However, we have private access to it so we can do what
+ // we want. ;-) We mark ourselves variable only if we're a mono output to
+ // prevent other PWM use on the other channel. If stereo, we say fixed
+ // frequency so we can allocate with ourselves.
+
+ // We don't actually know our frequency yet. It is set when
+ // pwmio_pwmout_set_top() is called. This value is unimportant; it just needs to be valid.
+ const uint32_t frequency = 12000000;
+
+ // Make sure the PWMOut's are "deinited" by default.
+ self->left_pwm.pin = NULL;
+ self->right_pwm.pin = NULL;
+
+ pwmout_result_t result =
+ common_hal_pwmio_pwmout_construct(&self->left_pwm, left_channel, 0, frequency, !self->stereo);
+ if (result == PWMOUT_OK && right_channel != NULL) {
+ result =
+ common_hal_pwmio_pwmout_construct(&self->right_pwm, right_channel, 0, frequency, false);
+ if (result != PWMOUT_OK) {
+ common_hal_pwmio_pwmout_deinit(&self->left_pwm);
+ }
+ }
+ if (result != PWMOUT_OK) {
+ mp_raise_RuntimeError(translate("All timers in use"));
+ }
+
+ self->quiescent_value = quiescent_value >> SAMPLE_BITS_TO_DISCARD;
+ common_hal_pwmio_pwmout_set_duty_cycle(&self->left_pwm, self->quiescent_value);
+ pwmio_pwmout_set_top(&self->left_pwm, PWM_TOP);
+ if (self->stereo) {
+ common_hal_pwmio_pwmout_set_duty_cycle(&self->right_pwm, self->quiescent_value);
+ pwmio_pwmout_set_top(&self->right_pwm, PWM_TOP);
+ }
+
+ audio_dma_init(&self->dma);
+ self->pacing_timer = NUM_DMA_TIMERS;
+}
+
+bool common_hal_audiopwmio_pwmaudioout_deinited(audiopwmio_pwmaudioout_obj_t *self) {
+ return common_hal_pwmio_pwmout_deinited(&self->left_pwm);
+}
+
+void common_hal_audiopwmio_pwmaudioout_deinit(audiopwmio_pwmaudioout_obj_t *self) {
+ if (common_hal_audiopwmio_pwmaudioout_deinited(self)) {
+ return;
+ }
+
+ if (common_hal_audiopwmio_pwmaudioout_get_playing(self)) {
+ common_hal_audiopwmio_pwmaudioout_stop(self);
+ }
+
+ // TODO: ramp the pwm down from quiescent value to 0
+ common_hal_pwmio_pwmout_deinit(&self->left_pwm);
+ common_hal_pwmio_pwmout_deinit(&self->right_pwm);
+
+ audio_dma_deinit(&self->dma);
+}
+
+void common_hal_audiopwmio_pwmaudioout_play(audiopwmio_pwmaudioout_obj_t *self, mp_obj_t sample, bool loop) {
+
+ if (common_hal_audiopwmio_pwmaudioout_get_playing(self)) {
+ common_hal_audiopwmio_pwmaudioout_stop(self);
+ }
+
+ // TODO: Share pacing timers based on frequency.
+ size_t pacing_timer = NUM_DMA_TIMERS;
+ for (size_t i = 0; i < NUM_DMA_TIMERS; i++) {
+ if (dma_hw->timer[i] == 0) {
+ pacing_timer = i;
+ break;
+ }
+ }
+ if (pacing_timer == NUM_DMA_TIMERS) {
+ mp_raise_RuntimeError(translate("No DMA pacing timer found"));
+ }
+ uint32_t tx_register = (uint32_t)&pwm_hw->slice[self->left_pwm.slice].cc;
+ if (self->stereo) {
+ // Shift the destination if we are outputting to both PWM channels.
+ tx_register += self->left_pwm.ab_channel * sizeof(uint16_t);
+ }
+
+ self->pacing_timer = pacing_timer;
+
+ // Playback with two independent clocks. One is the sample rate which
+ // determines when we push a new sample to the PWM slice. The second is the
+ // PWM frequency itself.
+
+ // Determine the DMA divisor. The RP2040 has four pacing timers we can use
+ // to trigger the DMA. Each has a 16 bit fractional divisor system clock * X / Y where X and Y
+ // are 16-bit.
+
+ uint32_t sample_rate = audiosample_sample_rate(sample);
+
+ uint32_t system_clock = common_hal_mcu_processor_get_frequency();
+ uint32_t best_denominator;
+ uint32_t best_numerator = limit_denominator(0xffff, sample_rate, system_clock, &best_denominator);
+
+ dma_hw->timer[pacing_timer] = best_numerator << 16 | best_denominator;
+ audio_dma_result result = audio_dma_setup_playback(
+ &self->dma,
+ sample,
+ loop,
+ false, // single channel
+ 0, // audio channel
+ false, // output signed
+ BITS_PER_SAMPLE,
+ (uint32_t)tx_register, // output register: PWM cc register
+ 0x3b + pacing_timer); // data request line
+
+ if (result == AUDIO_DMA_DMA_BUSY) {
+ common_hal_audiopwmio_pwmaudioout_stop(self);
+ mp_raise_RuntimeError(translate("No DMA channel found"));
+ }
+ if (result == AUDIO_DMA_MEMORY_ERROR) {
+ common_hal_audiopwmio_pwmaudioout_stop(self);
+ mp_raise_RuntimeError(translate("Unable to allocate buffers for signed conversion"));
+ }
+ // OK! We got all of the resources we need and dma is ready.
+}
+
+void common_hal_audiopwmio_pwmaudioout_stop(audiopwmio_pwmaudioout_obj_t *self) {
+ if (self->pacing_timer < NUM_DMA_TIMERS) {
+ dma_hw->timer[self->pacing_timer] = 0;
+ self->pacing_timer = NUM_DMA_TIMERS;
+ }
+
+ audio_dma_stop(&self->dma);
+
+ // Set to quiescent level.
+ common_hal_pwmio_pwmout_set_duty_cycle(&self->left_pwm, self->quiescent_value);
+ pwmio_pwmout_set_top(&self->left_pwm, PWM_TOP);
+ if (self->stereo) {
+ common_hal_pwmio_pwmout_set_duty_cycle(&self->right_pwm, self->quiescent_value);
+ pwmio_pwmout_set_top(&self->right_pwm, PWM_TOP);
+ }
+}
+
+bool common_hal_audiopwmio_pwmaudioout_get_playing(audiopwmio_pwmaudioout_obj_t *self) {
+ bool playing = audio_dma_get_playing(&self->dma);
+
+ if (!playing && self->pacing_timer < NUM_DMA_TIMERS) {
+ common_hal_audiopwmio_pwmaudioout_stop(self);
+ }
+
+ return playing;
+}
+
+void common_hal_audiopwmio_pwmaudioout_pause(audiopwmio_pwmaudioout_obj_t *self) {
+ audio_dma_pause(&self->dma);
+}
+
+void common_hal_audiopwmio_pwmaudioout_resume(audiopwmio_pwmaudioout_obj_t *self) {
+ audio_dma_resume(&self->dma);
+}
+
+bool common_hal_audiopwmio_pwmaudioout_get_paused(audiopwmio_pwmaudioout_obj_t *self) {
+ return audio_dma_get_paused(&self->dma);
+}
diff --git a/circuitpython/ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.h b/circuitpython/ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.h
new file mode 100644
index 0000000..e9f0f68
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.h
@@ -0,0 +1,48 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Jeff Epler for Adafruit Industries
+ *
+ * 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.
+ */
+
+#ifndef MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_AUDIOPWMIO_PWMAUDIOOUT_H
+#define MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_AUDIOPWMIO_PWMAUDIOOUT_H
+
+#include "common-hal/pwmio/PWMOut.h"
+
+#include "audio_dma.h"
+
+typedef struct {
+ mp_obj_base_t base;
+ pwmio_pwmout_obj_t left_pwm;
+ pwmio_pwmout_obj_t right_pwm;
+ audio_dma_t dma;
+ uint16_t quiescent_value;
+ uint8_t pacing_timer;
+ bool stereo; // if false, only using left_pwm.
+} audiopwmio_pwmaudioout_obj_t;
+
+void audiopwmout_reset(void);
+
+void audiopwmout_background(void);
+
+#endif // MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_AUDIOPWMIO_PWMAUDIOOUT_H
diff --git a/circuitpython/ports/raspberrypi/common-hal/audiopwmio/__init__.c b/circuitpython/ports/raspberrypi/common-hal/audiopwmio/__init__.c
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/audiopwmio/__init__.c
diff --git a/circuitpython/ports/raspberrypi/common-hal/board/__init__.c b/circuitpython/ports/raspberrypi/common-hal/board/__init__.c
new file mode 100644
index 0000000..3c7f30d
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/board/__init__.c
@@ -0,0 +1,34 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 Scott Shawcroft for Adafruit Industries
+ *
+ * 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 <string.h>
+
+#include "py/runtime.h"
+#include "py/mphal.h"
+#include "common-hal/microcontroller/Pin.h"
+
+// Pins aren't actually defined here. They are in the board specific directory
+// such as boards/arduino_zero/pins.c.
diff --git a/circuitpython/ports/raspberrypi/common-hal/busio/I2C.c b/circuitpython/ports/raspberrypi/common-hal/busio/I2C.c
new file mode 100644
index 0000000..516cee2
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/busio/I2C.c
@@ -0,0 +1,242 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 Scott Shawcroft for Adafruit Industries
+ *
+ * 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/mperrno.h"
+#include "py/mphal.h"
+#include "shared-bindings/busio/I2C.h"
+#include "py/runtime.h"
+
+#include "shared-bindings/microcontroller/__init__.h"
+#include "shared-bindings/bitbangio/I2C.h"
+
+#include "src/rp2_common/hardware_gpio/include/hardware/gpio.h"
+
+// Synopsys DW_apb_i2c (v2.01) IP
+
+#define NO_PIN 0xff
+
+// One second
+#define BUS_TIMEOUT_US 1000000
+
+STATIC bool never_reset_i2c[2];
+STATIC i2c_inst_t *i2c[2] = {i2c0, i2c1};
+
+void reset_i2c(void) {
+ for (size_t i = 0; i < 2; i++) {
+ if (never_reset_i2c[i]) {
+ continue;
+ }
+
+ i2c_deinit(i2c[i]);
+ }
+}
+
+void common_hal_busio_i2c_construct(busio_i2c_obj_t *self,
+ const mcu_pin_obj_t *scl, const mcu_pin_obj_t *sda, uint32_t frequency, uint32_t timeout) {
+ self->peripheral = NULL;
+ // I2C pins have a regular pattern. SCL is always odd and SDA is even. They match up in pairs
+ // so we can divide by two to get the instance. This pattern repeats.
+ size_t scl_instance = (scl->number / 2) % 2;
+ size_t sda_instance = (sda->number / 2) % 2;
+ if (scl->number % 2 == 1 && sda->number % 2 == 0 && scl_instance == sda_instance) {
+ self->peripheral = i2c[sda_instance];
+ }
+ if (self->peripheral == NULL) {
+ mp_raise_ValueError(translate("Invalid pins"));
+ }
+ if ((i2c_get_hw(self->peripheral)->enable & I2C_IC_ENABLE_ENABLE_BITS) != 0) {
+ mp_raise_ValueError(translate("I2C peripheral in use"));
+ }
+ if (frequency > 1000000) {
+ mp_raise_ValueError(translate("Unsupported baudrate"));
+ }
+
+ #if CIRCUITPY_REQUIRE_I2C_PULLUPS
+ // Test that the pins are in a high state. (Hopefully indicating they are pulled up.)
+ gpio_set_function(sda->number, GPIO_FUNC_SIO);
+ gpio_set_function(scl->number, GPIO_FUNC_SIO);
+ gpio_set_dir(sda->number, GPIO_IN);
+ gpio_set_dir(scl->number, GPIO_IN);
+
+ gpio_set_pulls(sda->number, false, true);
+ gpio_set_pulls(scl->number, false, true);
+
+ common_hal_mcu_delay_us(10);
+
+ gpio_set_pulls(sda->number, false, false);
+ gpio_set_pulls(scl->number, false, false);
+
+ // We must pull up within 3us to achieve 400khz.
+ common_hal_mcu_delay_us(3);
+
+ if (!gpio_get(sda->number) || !gpio_get(scl->number)) {
+ reset_pin_number(sda->number);
+ reset_pin_number(scl->number);
+ mp_raise_RuntimeError(translate("No pull up found on SDA or SCL; check your wiring"));
+ }
+ #endif
+
+ // Create a bitbangio.I2C object to do 0 byte writes.
+ //
+ // These are used to non-invasively detect I2C devices by sending
+ // the address and confirming an ACK.
+ // They are not supported by the RP2040 hardware.
+ //
+ // Must be done before setting up the I2C pins, since they will be
+ // set up as GPIO by the bitbangio.I2C object.
+ //
+ // Sets pins to open drain, high, and input.
+ //
+ // Do not use the default supplied clock stretching timeout here.
+ // It is too short for some devices. Use the busio timeout instead.
+ shared_module_bitbangio_i2c_construct(&self->bitbangio_i2c, scl, sda,
+ frequency, BUS_TIMEOUT_US);
+
+ self->baudrate = i2c_init(self->peripheral, frequency);
+
+ self->scl_pin = scl->number;
+ self->sda_pin = sda->number;
+ claim_pin(scl);
+ claim_pin(sda);
+
+ gpio_set_function(self->scl_pin, GPIO_FUNC_I2C);
+ gpio_set_function(self->sda_pin, GPIO_FUNC_I2C);
+}
+
+bool common_hal_busio_i2c_deinited(busio_i2c_obj_t *self) {
+ return self->sda_pin == NO_PIN;
+}
+
+void common_hal_busio_i2c_deinit(busio_i2c_obj_t *self) {
+ if (common_hal_busio_i2c_deinited(self)) {
+ return;
+ }
+ never_reset_i2c[i2c_hw_index(self->peripheral)] = false;
+
+ i2c_deinit(self->peripheral);
+
+ reset_pin_number(self->sda_pin);
+ reset_pin_number(self->scl_pin);
+ self->sda_pin = NO_PIN;
+ self->scl_pin = NO_PIN;
+}
+
+bool common_hal_busio_i2c_probe(busio_i2c_obj_t *self, uint8_t addr) {
+ return common_hal_busio_i2c_write(self, addr, NULL, 0) == 0;
+}
+
+bool common_hal_busio_i2c_try_lock(busio_i2c_obj_t *self) {
+ bool grabbed_lock = false;
+ if (!self->has_lock) {
+ grabbed_lock = true;
+ self->has_lock = true;
+ }
+ return grabbed_lock;
+}
+
+bool common_hal_busio_i2c_has_lock(busio_i2c_obj_t *self) {
+ return self->has_lock;
+}
+
+void common_hal_busio_i2c_unlock(busio_i2c_obj_t *self) {
+ self->has_lock = false;
+}
+
+STATIC uint8_t _common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr,
+ const uint8_t *data, size_t len, bool transmit_stop_bit) {
+ if (len == 0) {
+ // The RP2040 I2C peripheral will not perform 0 byte writes.
+ // So use bitbangio.I2C to do the write.
+
+ gpio_set_function(self->scl_pin, GPIO_FUNC_SIO);
+ gpio_set_function(self->sda_pin, GPIO_FUNC_SIO);
+ gpio_set_dir(self->scl_pin, GPIO_IN);
+ gpio_set_dir(self->sda_pin, GPIO_IN);
+ gpio_put(self->scl_pin, false);
+ gpio_put(self->sda_pin, false);
+
+ uint8_t status = shared_module_bitbangio_i2c_write(&self->bitbangio_i2c,
+ addr, data, len, transmit_stop_bit);
+
+ // The pins must be set back to GPIO_FUNC_I2C in the order given here,
+ // SCL first, otherwise reads will hang.
+ gpio_set_function(self->scl_pin, GPIO_FUNC_I2C);
+ gpio_set_function(self->sda_pin, GPIO_FUNC_I2C);
+
+ return status;
+ }
+
+ int result = i2c_write_timeout_us(self->peripheral, addr, data, len, !transmit_stop_bit, BUS_TIMEOUT_US);
+ if (result == len) {
+ return 0;
+ }
+ switch (result) {
+ case PICO_ERROR_GENERIC:
+ return MP_ENODEV;
+ case PICO_ERROR_TIMEOUT:
+ return MP_ETIMEDOUT;
+ default:
+ return MP_EIO;
+ }
+}
+
+uint8_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr,
+ const uint8_t *data, size_t len) {
+ return _common_hal_busio_i2c_write(self, addr, data, len, true);
+}
+
+uint8_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr,
+ uint8_t *data, size_t len) {
+ int result = i2c_read_timeout_us(self->peripheral, addr, data, len, false, BUS_TIMEOUT_US);
+ if (result == len) {
+ return 0;
+ }
+ switch (result) {
+ case PICO_ERROR_GENERIC:
+ return MP_ENODEV;
+ case PICO_ERROR_TIMEOUT:
+ return MP_ETIMEDOUT;
+ default:
+ return MP_EIO;
+ }
+}
+
+uint8_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr,
+ uint8_t *out_data, size_t out_len, uint8_t *in_data, size_t in_len) {
+ uint8_t result = _common_hal_busio_i2c_write(self, addr, out_data, out_len, false);
+ if (result != 0) {
+ return result;
+ }
+
+ return common_hal_busio_i2c_read(self, addr, in_data, in_len);
+}
+
+void common_hal_busio_i2c_never_reset(busio_i2c_obj_t *self) {
+ never_reset_i2c[i2c_hw_index(self->peripheral)] = true;
+
+ never_reset_pin_number(self->scl_pin);
+ never_reset_pin_number(self->sda_pin);
+}
diff --git a/circuitpython/ports/raspberrypi/common-hal/busio/I2C.h b/circuitpython/ports/raspberrypi/common-hal/busio/I2C.h
new file mode 100644
index 0000000..dbaede1
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/busio/I2C.h
@@ -0,0 +1,49 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 Scott Shawcroft for Adafruit Industries
+ *
+ * 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.
+ */
+
+#ifndef MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_BUSIO_I2C_H
+#define MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_BUSIO_I2C_H
+
+#include "common-hal/microcontroller/Pin.h"
+#include "shared-module/bitbangio/I2C.h"
+
+#include "py/obj.h"
+
+#include "src/rp2_common/hardware_i2c/include/hardware/i2c.h"
+
+typedef struct {
+ mp_obj_base_t base;
+ i2c_inst_t *peripheral;
+ bitbangio_i2c_obj_t bitbangio_i2c;
+ bool has_lock;
+ uint baudrate;
+ uint8_t scl_pin;
+ uint8_t sda_pin;
+} busio_i2c_obj_t;
+
+void reset_i2c(void);
+
+#endif // MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_BUSIO_I2C_H
diff --git a/circuitpython/ports/raspberrypi/common-hal/busio/SPI.c b/circuitpython/ports/raspberrypi/common-hal/busio/SPI.c
new file mode 100644
index 0000000..4a18d62
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/busio/SPI.c
@@ -0,0 +1,290 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 Scott Shawcroft for Adafruit Industries
+ *
+ * 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 "shared-bindings/busio/SPI.h"
+
+#include "shared/runtime/interrupt_char.h"
+#include "py/mperrno.h"
+#include "py/runtime.h"
+
+#include "supervisor/board.h"
+#include "common-hal/microcontroller/Pin.h"
+#include "shared-bindings/microcontroller/Pin.h"
+
+#include "src/rp2_common/hardware_dma/include/hardware/dma.h"
+#include "src/rp2_common/hardware_gpio/include/hardware/gpio.h"
+
+#define NO_INSTANCE 0xff
+
+STATIC bool never_reset_spi[2];
+STATIC spi_inst_t *spi[2] = {spi0, spi1};
+
+void reset_spi(void) {
+ for (size_t i = 0; i < 2; i++) {
+ if (never_reset_spi[i]) {
+ continue;
+ }
+
+ spi_deinit(spi[i]);
+ }
+}
+
+void common_hal_busio_spi_construct(busio_spi_obj_t *self,
+ const mcu_pin_obj_t *clock, const mcu_pin_obj_t *mosi,
+ const mcu_pin_obj_t *miso, bool half_duplex) {
+ size_t instance_index = NO_INSTANCE;
+
+ if (half_duplex) {
+ mp_raise_NotImplementedError(translate("Half duplex SPI is not implemented"));
+ }
+
+ if (clock->number % 4 == 2) {
+ instance_index = (clock->number / 8) % 2;
+ }
+ if (mosi != NULL) {
+ // Make sure the set MOSI matches the clock settings.
+ if (mosi->number % 4 != 3 ||
+ (mosi->number / 8) % 2 != instance_index) {
+ instance_index = NO_INSTANCE;
+ }
+ }
+ if (miso != NULL) {
+ // Make sure the set MOSI matches the clock settings.
+ if (miso->number % 4 != 0 ||
+ (miso->number / 8) % 2 != instance_index) {
+ instance_index = NO_INSTANCE;
+ }
+ }
+
+ // TODO: Check to see if we're sharing the SPI with a native APA102.
+
+ if (instance_index > 1) {
+ mp_raise_ValueError(translate("Invalid pins"));
+ }
+
+ if (instance_index == 0) {
+ self->peripheral = spi0;
+ } else if (instance_index == 1) {
+ self->peripheral = spi1;
+ }
+
+ if ((spi_get_hw(self->peripheral)->cr1 & SPI_SSPCR1_SSE_BITS) != 0) {
+ mp_raise_ValueError(translate("SPI peripheral in use"));
+ }
+
+ self->target_frequency = 250000;
+ self->real_frequency = spi_init(self->peripheral, self->target_frequency);
+
+ gpio_set_function(clock->number, GPIO_FUNC_SPI);
+ claim_pin(clock);
+ self->clock = clock;
+
+ self->MOSI = mosi;
+ if (mosi != NULL) {
+ gpio_set_function(mosi->number, GPIO_FUNC_SPI);
+ claim_pin(mosi);
+ }
+
+ self->MISO = miso;
+ if (miso != NULL) {
+ gpio_set_function(miso->number, GPIO_FUNC_SPI);
+ claim_pin(miso);
+ }
+}
+
+void common_hal_busio_spi_never_reset(busio_spi_obj_t *self) {
+ never_reset_spi[spi_get_index(self->peripheral)] = true;
+
+ common_hal_never_reset_pin(self->clock);
+ common_hal_never_reset_pin(self->MOSI);
+ common_hal_never_reset_pin(self->MISO);
+}
+
+bool common_hal_busio_spi_deinited(busio_spi_obj_t *self) {
+ return self->clock == NULL;
+}
+
+void common_hal_busio_spi_deinit(busio_spi_obj_t *self) {
+ if (common_hal_busio_spi_deinited(self)) {
+ return;
+ }
+ never_reset_spi[spi_get_index(self->peripheral)] = false;
+ spi_deinit(self->peripheral);
+
+ common_hal_reset_pin(self->clock);
+ common_hal_reset_pin(self->MOSI);
+ common_hal_reset_pin(self->MISO);
+ self->clock = NULL;
+}
+
+bool common_hal_busio_spi_configure(busio_spi_obj_t *self,
+ uint32_t baudrate, uint8_t polarity, uint8_t phase, uint8_t bits) {
+ if (baudrate == self->target_frequency &&
+ polarity == self->polarity &&
+ phase == self->phase &&
+ bits == self->bits) {
+ return true;
+ }
+
+ spi_set_format(self->peripheral, bits, polarity, phase, SPI_MSB_FIRST);
+
+ self->polarity = polarity;
+ self->phase = phase;
+ self->bits = bits;
+ self->target_frequency = baudrate;
+ self->real_frequency = spi_set_baudrate(self->peripheral, baudrate);
+
+ return true;
+}
+
+bool common_hal_busio_spi_try_lock(busio_spi_obj_t *self) {
+ bool grabbed_lock = false;
+ if (!self->has_lock) {
+ grabbed_lock = true;
+ self->has_lock = true;
+ }
+ return grabbed_lock;
+}
+
+bool common_hal_busio_spi_has_lock(busio_spi_obj_t *self) {
+ return self->has_lock;
+}
+
+void common_hal_busio_spi_unlock(busio_spi_obj_t *self) {
+ self->has_lock = false;
+}
+
+static bool _transfer(busio_spi_obj_t *self,
+ const uint8_t *data_out, size_t out_len,
+ uint8_t *data_in, size_t in_len) {
+ // 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;
+ size_t len = MAX(out_len, in_len);
+ 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;
+ if (use_dma) {
+ 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->peripheral) ? DREQ_SPI1_TX : DREQ_SPI0_TX);
+ channel_config_set_read_increment(&c, out_len == len);
+ channel_config_set_write_increment(&c, false);
+ dma_channel_configure(chan_tx, &c,
+ &spi_get_hw(self->peripheral)->dr,
+ data_out,
+ 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->peripheral) ? DREQ_SPI1_RX : DREQ_SPI0_RX);
+ channel_config_set_read_increment(&c, false);
+ channel_config_set_write_increment(&c, in_len == len);
+ dma_channel_configure(chan_rx, &c,
+ data_in,
+ &spi_get_hw(self->peripheral)->dr,
+ len,
+ false);
+
+ dma_start_channel_mask((1u << chan_rx) | (1u << chan_tx));
+ while (dma_channel_is_busy(chan_rx) || dma_channel_is_busy(chan_tx)) {
+ // TODO: We should idle here until we get a DMA interrupt or something else.
+ RUN_BACKGROUND_TASKS;
+ }
+ }
+
+ // If we have claimed only one channel successfully, we should release immediately. This also
+ // releases the DMA after use_dma has been done.
+ 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
+ // Never have more transfers in flight than will fit into the RX FIFO,
+ // else FIFO will overflow if this code is heavily interrupted.
+ const size_t fifo_depth = 8;
+ size_t rx_remaining = len;
+ size_t tx_remaining = len;
+
+ while (rx_remaining || tx_remaining) {
+ if (tx_remaining && spi_is_writable(self->peripheral) && rx_remaining - tx_remaining < fifo_depth) {
+ spi_get_hw(self->peripheral)->dr = (uint32_t)*data_out;
+ // Increment only if the buffer is the transfer length. It's 1 otherwise.
+ if (out_len == len) {
+ data_out++;
+ }
+ --tx_remaining;
+ }
+ if (rx_remaining && spi_is_readable(self->peripheral)) {
+ *data_in = (uint8_t)spi_get_hw(self->peripheral)->dr;
+ // Increment only if the buffer is the transfer length. It's 1 otherwise.
+ if (in_len == len) {
+ data_in++;
+ }
+ --rx_remaining;
+ }
+ RUN_BACKGROUND_TASKS;
+ }
+ }
+ return true;
+}
+
+bool common_hal_busio_spi_write(busio_spi_obj_t *self,
+ const uint8_t *data, size_t len) {
+ uint32_t data_in;
+ return _transfer(self, data, len, (uint8_t *)&data_in, MIN(len, 4));
+}
+
+bool common_hal_busio_spi_read(busio_spi_obj_t *self,
+ uint8_t *data, size_t len, uint8_t write_value) {
+ uint32_t data_out = write_value << 24 | write_value << 16 | write_value << 8 | write_value;
+ return _transfer(self, (const uint8_t *)&data_out, MIN(4, len), data, len);
+}
+
+bool common_hal_busio_spi_transfer(busio_spi_obj_t *self, const uint8_t *data_out, uint8_t *data_in, size_t len) {
+ return _transfer(self, data_out, len, data_in, len);
+}
+
+uint32_t common_hal_busio_spi_get_frequency(busio_spi_obj_t *self) {
+ return self->real_frequency;
+}
+
+uint8_t common_hal_busio_spi_get_phase(busio_spi_obj_t *self) {
+ return self->phase;
+}
+
+uint8_t common_hal_busio_spi_get_polarity(busio_spi_obj_t *self) {
+ return self->polarity;
+}
diff --git a/circuitpython/ports/raspberrypi/common-hal/busio/SPI.h b/circuitpython/ports/raspberrypi/common-hal/busio/SPI.h
new file mode 100644
index 0000000..b76dbbf
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/busio/SPI.h
@@ -0,0 +1,52 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 Scott Shawcroft for Adafruit Industries
+ *
+ * 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.
+ */
+
+#ifndef MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_BUSIO_SPI_H
+#define MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_BUSIO_SPI_H
+
+#include "common-hal/microcontroller/Pin.h"
+
+#include "py/obj.h"
+
+#include "src/rp2_common/hardware_spi/include/hardware/spi.h"
+
+typedef struct {
+ mp_obj_base_t base;
+ spi_inst_t *peripheral;
+ bool has_lock;
+ const mcu_pin_obj_t *clock;
+ const mcu_pin_obj_t *MOSI;
+ const mcu_pin_obj_t *MISO;
+ uint32_t target_frequency;
+ int32_t real_frequency;
+ uint8_t polarity;
+ uint8_t phase;
+ uint8_t bits;
+} busio_spi_obj_t;
+
+void reset_spi(void);
+
+#endif // MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_BUSIO_SPI_H
diff --git a/circuitpython/ports/raspberrypi/common-hal/busio/UART.c b/circuitpython/ports/raspberrypi/common-hal/busio/UART.c
new file mode 100644
index 0000000..97af41d
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/busio/UART.c
@@ -0,0 +1,339 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 microDev
+ *
+ * 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 "shared-bindings/busio/UART.h"
+
+#include "py/stream.h"
+#include "py/mperrno.h"
+#include "py/runtime.h"
+#include "supervisor/shared/tick.h"
+#include "shared/runtime/interrupt_char.h"
+#include "common-hal/microcontroller/Pin.h"
+
+#include "src/rp2_common/hardware_irq/include/hardware/irq.h"
+#include "src/rp2_common/hardware_gpio/include/hardware/gpio.h"
+
+#define NO_PIN 0xff
+
+#define UART_INST(uart) (((uart) ? uart1 : uart0))
+
+typedef enum {
+ STATUS_FREE = 0,
+ STATUS_BUSY,
+ STATUS_NEVER_RESET
+} uart_status_t;
+
+static uart_status_t uart_status[NUM_UARTS];
+
+void reset_uart(void) {
+ for (uint8_t num = 0; num < NUM_UARTS; num++) {
+ if (uart_status[num] == STATUS_BUSY) {
+ uart_status[num] = STATUS_FREE;
+ uart_deinit(UART_INST(num));
+ }
+ }
+}
+
+void never_reset_uart(uint8_t num) {
+ uart_status[num] = STATUS_NEVER_RESET;
+}
+
+static uint8_t pin_init(const uint8_t uart, const mcu_pin_obj_t *pin, const uint8_t pin_type) {
+ if (pin == NULL) {
+ return NO_PIN;
+ }
+ if (!(((pin->number % 4) == pin_type) && ((((pin->number + 4) / 8) % NUM_UARTS) == uart))) {
+ mp_raise_ValueError(translate("Invalid pins"));
+ }
+ claim_pin(pin);
+ gpio_set_function(pin->number, GPIO_FUNC_UART);
+ return pin->number;
+}
+
+static busio_uart_obj_t *active_uarts[NUM_UARTS];
+
+static void _copy_into_ringbuf(ringbuf_t *r, uart_inst_t *uart) {
+ while (uart_is_readable(uart) && ringbuf_num_empty(r) > 0) {
+ ringbuf_put(r, (uint8_t)uart_get_hw(uart)->dr);
+ }
+}
+
+static void shared_callback(busio_uart_obj_t *self) {
+ _copy_into_ringbuf(&self->ringbuf, self->uart);
+ // We always clear the interrupt so it doesn't continue to fire because we
+ // may not have read everything available.
+ uart_get_hw(self->uart)->icr = UART_UARTICR_RXIC_BITS | UART_UARTICR_RTIC_BITS;
+}
+
+static void uart0_callback(void) {
+ shared_callback(active_uarts[0]);
+}
+
+static void uart1_callback(void) {
+ shared_callback(active_uarts[1]);
+}
+
+void common_hal_busio_uart_construct(busio_uart_obj_t *self,
+ const mcu_pin_obj_t *tx, const mcu_pin_obj_t *rx,
+ const mcu_pin_obj_t *rts, const mcu_pin_obj_t *cts,
+ const mcu_pin_obj_t *rs485_dir, bool rs485_invert,
+ uint32_t baudrate, uint8_t bits, busio_uart_parity_t parity, uint8_t stop,
+ mp_float_t timeout, uint16_t receiver_buffer_size, byte *receiver_buffer,
+ bool sigint_enabled) {
+
+ if (bits > 8) {
+ mp_raise_ValueError(translate("Invalid word/bit length"));
+ }
+
+ if (receiver_buffer_size == 0) {
+ mp_raise_ValueError(translate("Invalid buffer size"));
+ }
+
+ uint8_t uart_id = ((((tx != NULL) ? tx->number : rx->number) + 4) / 8) % NUM_UARTS;
+
+ if (uart_status[uart_id] != STATUS_FREE) {
+ mp_raise_RuntimeError(translate("All UART peripherals are in use"));
+ }
+ // These may raise exceptions if pins are already in use.
+ self->tx_pin = pin_init(uart_id, tx, 0);
+ self->rx_pin = pin_init(uart_id, rx, 1);
+ self->cts_pin = pin_init(uart_id, cts, 2);
+ self->rts_pin = pin_init(uart_id, rts, 3);
+
+ if (rs485_dir != NULL) {
+ uint8_t pin = rs485_dir->number;
+ self->rs485_dir_pin = pin;
+ self->rs485_invert = rs485_invert;
+
+ gpio_init(pin);
+
+ claim_pin(rs485_dir);
+
+ gpio_disable_pulls(pin);
+
+ // Turn on "strong" pin driving (more current available).
+ hw_write_masked(&padsbank0_hw->io[pin],
+ PADS_BANK0_GPIO0_DRIVE_VALUE_12MA << PADS_BANK0_GPIO0_DRIVE_LSB,
+ PADS_BANK0_GPIO0_DRIVE_BITS);
+
+ gpio_put(self->rs485_dir_pin, rs485_invert);
+ gpio_set_dir(self->rs485_dir_pin, GPIO_OUT);
+ } else {
+ self->rs485_dir_pin = NO_PIN;
+ }
+
+ uart_status[uart_id] = STATUS_BUSY;
+
+
+ self->uart = UART_INST(uart_id);
+ self->uart_id = uart_id;
+ self->baudrate = baudrate;
+ self->timeout_ms = timeout * 1000;
+
+ uart_init(self->uart, self->baudrate);
+ uart_set_fifo_enabled(self->uart, true);
+ uart_set_format(self->uart, bits, stop, parity);
+ uart_set_hw_flow(self->uart, (cts != NULL), (rts != NULL));
+
+ if (rx != NULL) {
+ // Initially allocate the UART's buffer in the long-lived part of the
+ // heap. UARTs are generally long-lived objects, but the "make long-
+ // lived" machinery is incapable of moving internal pointers like
+ // self->buffer, so do it manually. (However, as long as internal
+ // pointers like this are NOT moved, allocating the buffer
+ // in the long-lived pool is not strictly necessary)
+ // (This is a macro.)
+ if (!ringbuf_alloc(&self->ringbuf, receiver_buffer_size, true)) {
+ mp_raise_msg(&mp_type_MemoryError, translate("Failed to allocate RX buffer"));
+ }
+ active_uarts[uart_id] = self;
+ if (uart_id == 1) {
+ self->uart_irq_id = UART1_IRQ;
+ irq_set_exclusive_handler(self->uart_irq_id, uart1_callback);
+ } else {
+ self->uart_irq_id = UART0_IRQ;
+ irq_set_exclusive_handler(self->uart_irq_id, uart0_callback);
+ }
+ irq_set_enabled(self->uart_irq_id, true);
+ uart_set_irq_enables(self->uart, true /* rx has data */, false /* tx needs data */);
+ }
+}
+
+bool common_hal_busio_uart_deinited(busio_uart_obj_t *self) {
+ return self->tx_pin == NO_PIN && self->rx_pin == NO_PIN;
+}
+
+void common_hal_busio_uart_deinit(busio_uart_obj_t *self) {
+ if (common_hal_busio_uart_deinited(self)) {
+ return;
+ }
+ uart_deinit(self->uart);
+ ringbuf_free(&self->ringbuf);
+ active_uarts[self->uart_id] = NULL;
+ uart_status[self->uart_id] = STATUS_FREE;
+ reset_pin_number(self->tx_pin);
+ reset_pin_number(self->rx_pin);
+ reset_pin_number(self->cts_pin);
+ reset_pin_number(self->rts_pin);
+ reset_pin_number(self->rs485_dir_pin);
+ self->tx_pin = NO_PIN;
+ self->rx_pin = NO_PIN;
+ self->cts_pin = NO_PIN;
+ self->rts_pin = NO_PIN;
+ self->rs485_dir_pin = NO_PIN;
+}
+
+// Write characters.
+size_t common_hal_busio_uart_write(busio_uart_obj_t *self, const uint8_t *data, size_t len, int *errcode) {
+ if (self->tx_pin == NO_PIN) {
+ mp_raise_ValueError(translate("No TX pin"));
+ }
+
+ if (self->rs485_dir_pin != NO_PIN) {
+ uart_tx_wait_blocking(self->uart);
+ gpio_put(self->rs485_dir_pin, !self->rs485_invert);
+ }
+
+ size_t left_to_write = len;
+ while (left_to_write > 0) {
+ while (uart_is_writable(self->uart) && left_to_write > 0) {
+ // Write and advance.
+ uart_get_hw(self->uart)->dr = *data++;
+ // Decrease how many chars left to write.
+ left_to_write--;
+ }
+ RUN_BACKGROUND_TASKS;
+ }
+ if (self->rs485_dir_pin != NO_PIN) {
+ uart_tx_wait_blocking(self->uart);
+ gpio_put(self->rs485_dir_pin, self->rs485_invert);
+ }
+ return len;
+}
+
+// Read characters.
+size_t common_hal_busio_uart_read(busio_uart_obj_t *self, uint8_t *data, size_t len, int *errcode) {
+ if (self->rx_pin == NO_PIN) {
+ mp_raise_ValueError(translate("No RX pin"));
+ }
+
+ if (len == 0) {
+ // Nothing to read.
+ return 0;
+ }
+
+ // Prevent conflict with uart irq.
+ irq_set_enabled(self->uart_irq_id, false);
+
+ // Copy as much received data as available, up to len bytes.
+ size_t total_read = ringbuf_get_n(&self->ringbuf, data, len);
+
+ // Check if we still need to read more data.
+ if (len > total_read) {
+ len -= total_read;
+ uint64_t start_ticks = supervisor_ticks_ms64();
+ // Busy-wait until timeout or until we've read enough chars.
+ while (len > 0 && (supervisor_ticks_ms64() - start_ticks < self->timeout_ms)) {
+ if (uart_is_readable(self->uart)) {
+ // Read and advance.
+ data[total_read] = uart_get_hw(self->uart)->dr;
+
+ // Adjust the counters.
+ len--;
+ total_read++;
+
+ // Reset the timeout on every character read.
+ start_ticks = supervisor_ticks_ms64();
+ }
+ RUN_BACKGROUND_TASKS;
+ // Allow user to break out of a timeout with a KeyboardInterrupt.
+ if (mp_hal_is_interrupted()) {
+ break;
+ }
+ }
+ }
+
+ // Now that we've emptied the ringbuf some, fill it up with anything in the
+ // FIFO. This ensures that we'll empty the FIFO as much as possible and
+ // reset the interrupt when we catch up.
+ _copy_into_ringbuf(&self->ringbuf, self->uart);
+
+ // Re-enable irq.
+ irq_set_enabled(self->uart_irq_id, true);
+
+ if (total_read == 0) {
+ *errcode = EAGAIN;
+ return MP_STREAM_ERROR;
+ }
+
+ return total_read;
+}
+
+uint32_t common_hal_busio_uart_get_baudrate(busio_uart_obj_t *self) {
+ return self->baudrate;
+}
+
+void common_hal_busio_uart_set_baudrate(busio_uart_obj_t *self, uint32_t baudrate) {
+ self->baudrate = baudrate;
+ uart_set_baudrate(self->uart, baudrate);
+}
+
+mp_float_t common_hal_busio_uart_get_timeout(busio_uart_obj_t *self) {
+ return (mp_float_t)(self->timeout_ms / 1000.0f);
+}
+
+void common_hal_busio_uart_set_timeout(busio_uart_obj_t *self, mp_float_t timeout) {
+ self->timeout_ms = timeout * 1000;
+}
+
+uint32_t common_hal_busio_uart_rx_characters_available(busio_uart_obj_t *self) {
+ // Prevent conflict with uart irq.
+ irq_set_enabled(self->uart_irq_id, false);
+ // The UART only interrupts after a threshold so make sure to copy anything
+ // out of its FIFO before measuring how many bytes we've received.
+ _copy_into_ringbuf(&self->ringbuf, self->uart);
+ irq_set_enabled(self->uart_irq_id, false);
+ return ringbuf_num_filled(&self->ringbuf);
+}
+
+void common_hal_busio_uart_clear_rx_buffer(busio_uart_obj_t *self) {
+ // Prevent conflict with uart irq.
+ irq_set_enabled(self->uart_irq_id, false);
+ ringbuf_clear(&self->ringbuf);
+
+ // Throw away the FIFO contents too.
+ while (uart_is_readable(self->uart)) {
+ (void)uart_get_hw(self->uart)->dr;
+ }
+ irq_set_enabled(self->uart_irq_id, true);
+}
+
+bool common_hal_busio_uart_ready_to_tx(busio_uart_obj_t *self) {
+ if (self->tx_pin == NO_PIN) {
+ return false;
+ }
+ return uart_is_writable(self->uart);
+}
diff --git a/circuitpython/ports/raspberrypi/common-hal/busio/UART.h b/circuitpython/ports/raspberrypi/common-hal/busio/UART.h
new file mode 100644
index 0000000..db416b7
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/busio/UART.h
@@ -0,0 +1,54 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 microDev
+ *
+ * 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.
+ */
+
+#ifndef MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_BUSIO_UART_H
+#define MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_BUSIO_UART_H
+
+#include "py/obj.h"
+#include "py/ringbuf.h"
+
+#include "src/rp2_common/hardware_uart/include/hardware/uart.h"
+
+typedef struct {
+ mp_obj_base_t base;
+ uint8_t tx_pin;
+ uint8_t rx_pin;
+ uint8_t cts_pin;
+ uint8_t rs485_dir_pin;
+ bool rs485_invert;
+ uint8_t rts_pin;
+ uint8_t uart_id;
+ uint8_t uart_irq_id;
+ uint32_t baudrate;
+ uint32_t timeout_ms;
+ uart_inst_t *uart;
+ ringbuf_t ringbuf;
+} busio_uart_obj_t;
+
+extern void reset_uart(void);
+extern void never_reset_uart(uint8_t num);
+
+#endif // MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_BUSIO_UART_H
diff --git a/circuitpython/ports/raspberrypi/common-hal/busio/__init__.c b/circuitpython/ports/raspberrypi/common-hal/busio/__init__.c
new file mode 100644
index 0000000..41761b6
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/busio/__init__.c
@@ -0,0 +1 @@
+// No busio module functions.
diff --git a/circuitpython/ports/raspberrypi/common-hal/countio/Counter.c b/circuitpython/ports/raspberrypi/common-hal/countio/Counter.c
new file mode 100644
index 0000000..0ae773b
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/countio/Counter.c
@@ -0,0 +1,115 @@
+#include "shared-bindings/countio/Counter.h"
+
+#include "py/runtime.h"
+#include "py/mpstate.h"
+#include "supervisor/shared/translate.h"
+
+#include "shared-bindings/countio/Edge.h"
+#include "shared-bindings/digitalio/Pull.h"
+#include "common-hal/pwmio/PWMOut.h"
+
+#include "src/rp2_common/hardware_gpio/include/hardware/gpio.h"
+#include "src/rp2_common/hardware_pwm/include/hardware/pwm.h"
+#include "src/rp2_common/hardware_irq/include/hardware/irq.h"
+
+
+void common_hal_countio_counter_construct(countio_counter_obj_t *self,
+ const mcu_pin_obj_t *pin, countio_edge_t edge, digitalio_pull_t pull) {
+
+ if (pwm_gpio_to_channel(pin->number) != PWM_CHAN_B) {
+ mp_raise_RuntimeError(translate("Pin must be on PWM Channel B"));
+ }
+
+ if (edge == EDGE_RISE_AND_FALL) {
+ mp_raise_NotImplementedError(translate("RISE_AND_FALL not available on this chip"));
+ }
+
+ self->pin = pin->number;
+ self->slice_num = pwm_gpio_to_slice_num(self->pin);
+
+ if (MP_STATE_PORT(counting)[self->slice_num] != NULL) {
+ mp_raise_RuntimeError(translate("PWM slice already in use"));
+ }
+
+ uint8_t ab_channel = pwm_gpio_to_channel(self->pin);
+ if (!pwmio_claim_slice_ab_channels(self->slice_num)) {
+ mp_raise_RuntimeError(translate("PWM slice channel A already in use"));
+ }
+
+ pwm_clear_irq(self->slice_num);
+ pwm_set_irq_enabled(self->slice_num, true);
+ irq_set_exclusive_handler(PWM_IRQ_WRAP, counter_interrupt_handler);
+ irq_set_enabled(PWM_IRQ_WRAP, true);
+
+ pwm_config cfg = pwm_get_default_config();
+ pwm_config_set_clkdiv_mode(&cfg, edge == EDGE_RISE ? PWM_DIV_B_RISING : PWM_DIV_B_FALLING);
+ pwm_init(self->slice_num, &cfg, false);
+ gpio_set_function(self->pin, GPIO_FUNC_PWM);
+ gpio_set_pulls(self->pin, pull == PULL_UP, pull == PULL_DOWN);
+
+ claim_pin(pin);
+
+ MP_STATE_PORT(counting)[self->slice_num] = self;
+
+ self->count = 0;
+ pwm_set_enabled(self->slice_num, true);
+}
+
+
+void reset_countio(void) {
+ for (size_t i = 0; i < NUM_PWM_SLICES; i++) {
+ if (MP_STATE_PORT(counting)[i] != NULL) {
+ common_hal_countio_counter_deinit(MP_STATE_PORT(counting)[i]);
+ }
+ }
+}
+
+bool common_hal_countio_counter_deinited(countio_counter_obj_t *self) {
+ return self->pin == 0;
+}
+
+void common_hal_countio_counter_deinit(countio_counter_obj_t *self) {
+ if (common_hal_countio_counter_deinited(self)) {
+ return;
+ }
+
+ pwm_set_enabled(self->slice_num, false);
+ pwm_set_irq_enabled(self->slice_num, false);
+
+ pwmio_release_slice_ab_channels(self->slice_num);
+
+ reset_pin_number(self->pin);
+
+ MP_STATE_PORT(counting)[self->slice_num] = NULL;
+ self->pin = 0;
+ self->slice_num = 0;
+}
+
+mp_int_t common_hal_countio_counter_get_count(countio_counter_obj_t *self) {
+ self->count += pwm_get_counter(self->slice_num);
+ pwm_set_counter(self->slice_num, 0);
+ return self->count;
+}
+
+void common_hal_countio_counter_set_count(countio_counter_obj_t *self,
+ mp_int_t new_count) {
+ pwm_set_counter(self->slice_num, 0);
+ self->count = new_count;
+}
+
+void counter_interrupt_handler(void) {
+ uint32_t mask = pwm_get_irq_status_mask();
+
+ uint8_t i = 1;
+ uint8_t pos = 0;
+ while (!(i & mask)) {
+ i = i << 1;
+ ++pos;
+ }
+
+ countio_counter_obj_t *self = MP_STATE_PORT(counting)[pos];
+ if (self != NULL) {
+ pwm_clear_irq(self->slice_num);
+ self->count += 65536;
+ }
+}
diff --git a/circuitpython/ports/raspberrypi/common-hal/countio/Counter.h b/circuitpython/ports/raspberrypi/common-hal/countio/Counter.h
new file mode 100644
index 0000000..3355036
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/countio/Counter.h
@@ -0,0 +1,21 @@
+
+#ifndef MICROPY_INCLUDED_RASPBERRRYPI_COMMON_HAL_COUNTIO_COUNTER_H
+#define MICROPY_INCLUDED_RASPBERRRYPI_COMMON_HAL_COUNTIO_COUNTER_H
+
+#include "common-hal/microcontroller/Pin.h"
+
+#include "py/obj.h"
+
+typedef struct {
+ mp_obj_base_t base;
+ uint8_t pin;
+ uint8_t slice_num;
+ mp_int_t count;
+} countio_counter_obj_t;
+
+
+void counter_interrupt_handler(void);
+
+void reset_countio(void);
+
+#endif // MICROPY_INCLUDED_RASPBERRRYPI_COMMON_HAL_COUNTIO_COUNTER_H
diff --git a/circuitpython/ports/raspberrypi/common-hal/countio/__init__.c b/circuitpython/ports/raspberrypi/common-hal/countio/__init__.c
new file mode 100644
index 0000000..d47de33
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/countio/__init__.c
@@ -0,0 +1 @@
+// No countio module functions
diff --git a/circuitpython/ports/raspberrypi/common-hal/digitalio/DigitalInOut.c b/circuitpython/ports/raspberrypi/common-hal/digitalio/DigitalInOut.c
new file mode 100644
index 0000000..a8a5a6b
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/digitalio/DigitalInOut.c
@@ -0,0 +1,191 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Scott Shawcroft for Adafruit Industries
+ *
+ * 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 <stdint.h>
+#include <string.h>
+
+#include "py/runtime.h"
+#include "py/mphal.h"
+
+#include "common-hal/microcontroller/Pin.h"
+#include "shared-bindings/digitalio/DigitalInOut.h"
+#include "supervisor/shared/translate.h"
+
+#include "src/rp2_common/hardware_gpio/include/hardware/gpio.h"
+
+digitalinout_result_t common_hal_digitalio_digitalinout_construct(
+ digitalio_digitalinout_obj_t *self, const mcu_pin_obj_t *pin) {
+ claim_pin(pin);
+ self->pin = pin;
+ self->output = false;
+ self->open_drain = false;
+
+ // Set to input. No output value.
+ gpio_init(pin->number);
+ return DIGITALINOUT_OK;
+}
+
+void common_hal_digitalio_digitalinout_never_reset(
+ digitalio_digitalinout_obj_t *self) {
+ never_reset_pin_number(self->pin->number);
+}
+
+bool common_hal_digitalio_digitalinout_deinited(digitalio_digitalinout_obj_t *self) {
+ return self->pin == NULL;
+}
+
+void common_hal_digitalio_digitalinout_deinit(digitalio_digitalinout_obj_t *self) {
+ if (common_hal_digitalio_digitalinout_deinited(self)) {
+ return;
+ }
+ reset_pin_number(self->pin->number);
+ self->pin = NULL;
+}
+
+void common_hal_digitalio_digitalinout_switch_to_input(
+ digitalio_digitalinout_obj_t *self, digitalio_pull_t pull) {
+ self->output = false;
+ // This also sets direction to input.
+ common_hal_digitalio_digitalinout_set_pull(self, pull);
+}
+
+digitalinout_result_t common_hal_digitalio_digitalinout_switch_to_output(
+ digitalio_digitalinout_obj_t *self, bool value,
+ digitalio_drive_mode_t drive_mode) {
+ const uint8_t pin = self->pin->number;
+ gpio_disable_pulls(pin);
+
+ // Turn on "strong" pin driving (more current available).
+ hw_write_masked(&padsbank0_hw->io[pin],
+ PADS_BANK0_GPIO0_DRIVE_VALUE_12MA << PADS_BANK0_GPIO0_DRIVE_LSB,
+ PADS_BANK0_GPIO0_DRIVE_BITS);
+
+ self->output = true;
+ self->open_drain = drive_mode == DRIVE_MODE_OPEN_DRAIN;
+
+ // Pin direction is ultimately set in set_value. We don't need to do it here.
+ common_hal_digitalio_digitalinout_set_value(self, value);
+ return DIGITALINOUT_OK;
+}
+
+digitalio_direction_t common_hal_digitalio_digitalinout_get_direction(
+ digitalio_digitalinout_obj_t *self) {
+ return self->output ? DIRECTION_OUTPUT : DIRECTION_INPUT;
+}
+
+void common_hal_digitalio_digitalinout_set_value(
+ digitalio_digitalinout_obj_t *self, bool value) {
+ const uint8_t pin = self->pin->number;
+ if (self->open_drain && value) {
+ // If true and open-drain, set the direction -before- setting
+ // the pin value, to to avoid a high glitch on the pin before
+ // switching from output to input for open-drain.
+ gpio_set_dir(pin, GPIO_IN);
+ gpio_put(pin, value);
+ } else {
+ // Otherwise set the direction -after- setting the pin value,
+ // to avoid a glitch which might occur if the old value was
+ // different and the pin was previously set to input.
+ gpio_put(pin, value);
+ gpio_set_dir(pin, GPIO_OUT);
+ }
+}
+
+bool common_hal_digitalio_digitalinout_get_value(
+ digitalio_digitalinout_obj_t *self) {
+ return gpio_get(self->pin->number);
+}
+
+digitalinout_result_t common_hal_digitalio_digitalinout_set_drive_mode(
+ digitalio_digitalinout_obj_t *self,
+ digitalio_drive_mode_t drive_mode) {
+ const uint8_t pin = self->pin->number;
+ bool value = common_hal_digitalio_digitalinout_get_value(self);
+ self->open_drain = drive_mode == DRIVE_MODE_OPEN_DRAIN;
+ // True is implemented differently between modes so reset the value to make
+ // sure it's correct for the new mode.
+ if (value) {
+ common_hal_digitalio_digitalinout_set_value(self, value);
+ }
+ return DIGITALINOUT_OK;
+}
+
+digitalio_drive_mode_t common_hal_digitalio_digitalinout_get_drive_mode(
+ digitalio_digitalinout_obj_t *self) {
+ if (self->open_drain) {
+ return DRIVE_MODE_OPEN_DRAIN;
+ } else {
+ return DRIVE_MODE_PUSH_PULL;
+ }
+}
+
+void common_hal_digitalio_digitalinout_set_pull(
+ digitalio_digitalinout_obj_t *self, digitalio_pull_t pull) {
+ const uint8_t pin = self->pin->number;
+ gpio_set_pulls(pin, pull == PULL_UP, pull == PULL_DOWN);
+ gpio_set_dir(pin, GPIO_IN);
+}
+
+digitalio_pull_t common_hal_digitalio_digitalinout_get_pull(
+ digitalio_digitalinout_obj_t *self) {
+ uint32_t pin = self->pin->number;
+ if (self->output) {
+ mp_raise_AttributeError(translate("Cannot get pull while in output mode"));
+ return PULL_NONE;
+ } else {
+ if (gpio_is_pulled_up(pin)) {
+ return PULL_UP;
+ } else if (gpio_is_pulled_down(pin)) {
+ return PULL_DOWN;
+ }
+ }
+ return PULL_NONE;
+}
+
+bool common_hal_digitalio_has_reg_op(digitalinout_reg_op_t op) {
+ return true;
+}
+
+volatile uint32_t *common_hal_digitalio_digitalinout_get_reg(digitalio_digitalinout_obj_t *self, digitalinout_reg_op_t op, uint32_t *mask) {
+ const uint8_t pin = self->pin->number;
+
+ *mask = 1u << pin;
+
+ switch (op) {
+ case DIGITALINOUT_REG_READ:
+ return (volatile uint32_t *)&sio_hw->gpio_in;
+ case DIGITALINOUT_REG_WRITE:
+ return &sio_hw->gpio_out;
+ case DIGITALINOUT_REG_SET:
+ return &sio_hw->gpio_set;
+ case DIGITALINOUT_REG_RESET:
+ return &sio_hw->gpio_clr;
+ case DIGITALINOUT_REG_TOGGLE:
+ return &sio_hw->gpio_togl;
+ default:
+ return NULL;
+ }
+}
diff --git a/circuitpython/ports/raspberrypi/common-hal/digitalio/DigitalInOut.h b/circuitpython/ports/raspberrypi/common-hal/digitalio/DigitalInOut.h
new file mode 100644
index 0000000..d656f60
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/digitalio/DigitalInOut.h
@@ -0,0 +1,40 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Scott Shawcroft for Adafruit Industries
+ *
+ * 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.
+ */
+
+#ifndef MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_DIGITALIO_DIGITALINOUT_H
+#define MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_DIGITALIO_DIGITALINOUT_H
+
+#include "common-hal/microcontroller/Pin.h"
+#include "py/obj.h"
+
+typedef struct {
+ mp_obj_base_t base;
+ const mcu_pin_obj_t *pin;
+ bool output;
+ bool open_drain;
+} digitalio_digitalinout_obj_t;
+
+#endif // MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_DIGITALIO_DIGITALINOUT_H
diff --git a/circuitpython/ports/raspberrypi/common-hal/digitalio/__init__.c b/circuitpython/ports/raspberrypi/common-hal/digitalio/__init__.c
new file mode 100644
index 0000000..20fad45
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/digitalio/__init__.c
@@ -0,0 +1 @@
+// No digitalio module functions.
diff --git a/circuitpython/ports/raspberrypi/common-hal/floppyio/__init__.h b/circuitpython/ports/raspberrypi/common-hal/floppyio/__init__.h
new file mode 100644
index 0000000..7dbf8a7
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/floppyio/__init__.h
@@ -0,0 +1,37 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2022 Jeff Epler for Adafruit Industries
+ *
+ * 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.
+ */
+
+#pragma once
+
+// empirical-ish from RP2040 @ 125MHz for floppy_flux_readinto
+#define FLOPPYIO_SAMPLERATE (24000000)
+// empirical-ish from RP2040 @ 125MHz for floppy_mfm_readinto
+// my guess is these are slower because the more complex routine falls out of cache, but it's just
+// speculation because the loops are very similar. When looking at raw bins with a modified
+// version of adafruit_floppy, it can be seen that there are _two_ peaks for T2 and T3, rather
+// than a single one, around 36 (mostly) and 43 (rarer), compared to a single peak around 48.
+#define T2_5 (54)
+#define T3_5 (75)
diff --git a/circuitpython/ports/raspberrypi/common-hal/imagecapture/ParallelImageCapture.c b/circuitpython/ports/raspberrypi/common-hal/imagecapture/ParallelImageCapture.c
new file mode 100644
index 0000000..3c5c57e
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/imagecapture/ParallelImageCapture.c
@@ -0,0 +1,159 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 Jeff Epler for Adafruit Industries
+ *
+ * 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/obj.h"
+#include "py/runtime.h"
+
+#include "shared/runtime/context_manager_helpers.h"
+#include "shared/runtime/interrupt_char.h"
+
+#include "bindings/rp2pio/StateMachine.h"
+#include "bindings/rp2pio/__init__.h"
+#include "common-hal/imagecapture/ParallelImageCapture.h"
+#include "shared-bindings/imagecapture/ParallelImageCapture.h"
+#include "shared-bindings/microcontroller/Pin.h"
+#include "shared-bindings/microcontroller/Processor.h"
+#include "shared-bindings/microcontroller/__init__.h"
+
+#include "src/rp2_common/hardware_pio/include/hardware/pio.h"
+#include "src/rp2_common/hardware_pio/include/hardware/pio_instructions.h"
+
+// Define this to (1), and you can scope the instruction-pointer of the state machine on D26..28 (note the weird encoding though!)
+#define DEBUG_STATE_MACHINE (0)
+#if DEBUG_STATE_MACHINE
+#define SIDE(x) ((x) << 8)
+#else
+#define SIDE(x) (0)
+#endif
+
+#define _0 SIDE(0b11100)
+#define _1 SIDE(0b00000)
+#define _2 SIDE(0b10000)
+#define _3 SIDE(0b10100)
+#define _4 SIDE(0b11000)
+#define _5 SIDE(0b10100)
+
+#define IMAGECAPTURE_CODE(width, pclk, vsync, href) \
+ { \
+/* 0 */ pio_encode_wait_gpio(0, vsync) | _0, \
+/* 1 */ pio_encode_wait_gpio(1, vsync) | _1, \
+ /* .wrap_target */ \
+/* 2 */ pio_encode_wait_gpio(1, href) | _2, \
+/* 3 */ pio_encode_wait_gpio(1, pclk) | _3, \
+/* 4 */ pio_encode_in(pio_pins, width) | _4, \
+/* 5 */ pio_encode_wait_gpio(0, pclk) | _5, \
+ /* .wrap */ \
+ }
+
+STATIC mcu_pin_obj_t *pin_from_number(uint8_t number) {
+ const mp_map_t *mcu_map = &mcu_pin_globals.map;
+ for (uint8_t i = 0; i < mcu_map->alloc; i++) {
+ mp_obj_t val = mcu_map->table[i].value;
+ if (!mp_obj_is_type(val, &mcu_pin_type)) {
+ continue;
+ }
+ mcu_pin_obj_t *pin = MP_OBJ_TO_PTR(val);
+ if (pin->number == number) {
+ return pin;
+ }
+ }
+ return NULL;
+}
+
+void common_hal_imagecapture_parallelimagecapture_construct(imagecapture_parallelimagecapture_obj_t *self,
+ const uint8_t data_pins[],
+ uint8_t data_count,
+ const mcu_pin_obj_t *data_clock,
+ const mcu_pin_obj_t *vertical_sync,
+ const mcu_pin_obj_t *horizontal_reference) {
+
+ for (int i = 1; i < data_count; i++) {
+ if (data_pins[i] - data_pins[0] != i) {
+ mp_raise_RuntimeError(translate("Pins must be sequential"));
+ }
+ }
+
+ uint16_t imagecapture_code[] = IMAGECAPTURE_CODE(data_count, data_clock->number, vertical_sync->number, horizontal_reference->number);
+
+ common_hal_rp2pio_statemachine_construct(&self->state_machine,
+ imagecapture_code, MP_ARRAY_SIZE(imagecapture_code),
+ common_hal_mcu_processor_get_frequency(), // full speed (4 instructions per loop -> max pclk 30MHz @ 120MHz)
+ 0, 0, // init
+ NULL, 0, 0, 0, // out pins
+ pin_from_number(data_pins[0]), data_count, // in pins
+ 0, 0, // in pulls
+ NULL, 0, 0, 0, // set pins
+ #if DEBUG_STATE_MACHINE
+ &pin_GPIO26, 3, 7, 7, // sideset pins
+ #else
+ NULL, 0, 0, 0, // sideset pins
+ #endif
+ false, // No sideset enable
+ NULL, PULL_NONE, // jump pin
+ (1 << vertical_sync->number) | (1 << horizontal_reference->number) | (1 << data_clock->number), // wait gpio pins
+ true, // exclusive pin use
+ false, 32, false, // out settings
+ false, // wait for txstall
+ true, 32, true, // in settings
+ false, // Not user-interruptible.
+ 2, 5); // wrap settings
+
+
+ PIO pio = self->state_machine.pio;
+ uint8_t pio_index = pio_get_index(pio);
+ uint sm = self->state_machine.state_machine;
+}
+
+void common_hal_imagecapture_parallelimagecapture_deinit(imagecapture_parallelimagecapture_obj_t *self) {
+ if (common_hal_imagecapture_parallelimagecapture_deinited(self)) {
+ return;
+ }
+ return common_hal_rp2pio_statemachine_deinit(&self->state_machine);
+}
+
+bool common_hal_imagecapture_parallelimagecapture_deinited(imagecapture_parallelimagecapture_obj_t *self) {
+ return common_hal_rp2pio_statemachine_deinited(&self->state_machine);
+}
+
+void common_hal_imagecapture_parallelimagecapture_singleshot_capture(imagecapture_parallelimagecapture_obj_t *self, mp_obj_t buffer) {
+ mp_buffer_info_t bufinfo;
+ mp_get_buffer_raise(buffer, &bufinfo, MP_BUFFER_RW);
+
+ PIO pio = self->state_machine.pio;
+ uint sm = self->state_machine.state_machine;
+ uint8_t offset = rp2pio_statemachine_program_offset(&self->state_machine);
+
+ pio_sm_set_enabled(pio, sm, false);
+ pio_sm_clear_fifos(pio, sm);
+
+ pio_sm_restart(pio, sm);
+ pio_sm_exec(pio, sm, pio_encode_jmp(offset));
+ pio_sm_set_enabled(pio, sm, true);
+
+ common_hal_rp2pio_statemachine_readinto(&self->state_machine, bufinfo.buf, bufinfo.len, 4, false);
+
+ pio_sm_set_enabled(pio, sm, false);
+}
diff --git a/circuitpython/ports/raspberrypi/common-hal/imagecapture/ParallelImageCapture.h b/circuitpython/ports/raspberrypi/common-hal/imagecapture/ParallelImageCapture.h
new file mode 100644
index 0000000..9f5d1ab
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/imagecapture/ParallelImageCapture.h
@@ -0,0 +1,35 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 Jeff Epler for Adafruit Industries
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include "common-hal/rp2pio/StateMachine.h"
+#include "shared-bindings/imagecapture/ParallelImageCapture.h"
+
+struct imagecapture_parallelimagecapture_obj {
+ mp_obj_base_t base;
+ rp2pio_statemachine_obj_t state_machine;
+};
diff --git a/circuitpython/ports/raspberrypi/common-hal/imagecapture/__init__.c b/circuitpython/ports/raspberrypi/common-hal/imagecapture/__init__.c
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/imagecapture/__init__.c
diff --git a/circuitpython/ports/raspberrypi/common-hal/imagecapture/__init__.h b/circuitpython/ports/raspberrypi/common-hal/imagecapture/__init__.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/imagecapture/__init__.h
diff --git a/circuitpython/ports/raspberrypi/common-hal/microcontroller/Pin.c b/circuitpython/ports/raspberrypi/common-hal/microcontroller/Pin.c
new file mode 100644
index 0000000..d40c1f4
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/microcontroller/Pin.c
@@ -0,0 +1,105 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 Scott Shawcroft for Adafruit Industries
+ *
+ * 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 "common-hal/microcontroller/__init__.h"
+#include "shared-bindings/microcontroller/Pin.h"
+
+#include "src/rp2_common/hardware_gpio/include/hardware/gpio.h"
+
+STATIC uint32_t never_reset_pins;
+
+void reset_all_pins(void) {
+ for (size_t i = 0; i < TOTAL_GPIO_COUNT; i++) {
+ if ((never_reset_pins & (1 << i)) != 0) {
+ continue;
+ }
+ reset_pin_number(i);
+ }
+}
+
+void never_reset_pin_number(uint8_t pin_number) {
+ if (pin_number >= TOTAL_GPIO_COUNT) {
+ return;
+ }
+
+ never_reset_pins |= 1 << pin_number;
+}
+
+void reset_pin_number(uint8_t pin_number) {
+ if (pin_number >= TOTAL_GPIO_COUNT) {
+ return;
+ }
+
+ never_reset_pins &= ~(1 << pin_number);
+
+ // We are very aggressive in shutting down the pad fully. Both pulls are
+ // disabled and both buffers are as well.
+ gpio_init(pin_number);
+ hw_clear_bits(&padsbank0_hw->io[pin_number], PADS_BANK0_GPIO0_IE_BITS |
+ PADS_BANK0_GPIO0_PUE_BITS |
+ PADS_BANK0_GPIO0_PDE_BITS);
+ hw_set_bits(&padsbank0_hw->io[pin_number], PADS_BANK0_GPIO0_OD_BITS);
+}
+
+void common_hal_never_reset_pin(const mcu_pin_obj_t *pin) {
+ never_reset_pin_number(pin->number);
+}
+
+void common_hal_reset_pin(const mcu_pin_obj_t *pin) {
+ reset_pin_number(pin->number);
+}
+
+void claim_pin(const mcu_pin_obj_t *pin) {
+ // Nothing to do because all changes will set the GPIO settings.
+}
+
+bool pin_number_is_free(uint8_t pin_number) {
+ if (pin_number >= TOTAL_GPIO_COUNT) {
+ return false;
+ }
+
+ uint32_t pad_state = padsbank0_hw->io[pin_number];
+ return (pad_state & PADS_BANK0_GPIO0_IE_BITS) == 0 &&
+ (pad_state & PADS_BANK0_GPIO0_OD_BITS) != 0;
+}
+
+bool common_hal_mcu_pin_is_free(const mcu_pin_obj_t *pin) {
+ return pin_number_is_free(pin->number);
+}
+
+uint8_t common_hal_mcu_pin_number(const mcu_pin_obj_t *pin) {
+ return pin->number;
+}
+
+void common_hal_mcu_pin_claim(const mcu_pin_obj_t *pin) {
+ return claim_pin(pin);
+}
+
+void common_hal_mcu_pin_reset_number(uint8_t pin_no) {
+ reset_pin_number(pin_no);
+}
diff --git a/circuitpython/ports/raspberrypi/common-hal/microcontroller/Pin.h b/circuitpython/ports/raspberrypi/common-hal/microcontroller/Pin.h
new file mode 100644
index 0000000..3e2287d
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/microcontroller/Pin.h
@@ -0,0 +1,45 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 Scott Shawcroft for Adafruit Industries
+ *
+ * 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.
+ */
+
+#ifndef MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_MICROCONTROLLER_PIN_H
+#define MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_MICROCONTROLLER_PIN_H
+
+#include <assert.h>
+#include <stdint.h>
+
+#include <py/obj.h>
+
+#include "peripherals/pins.h"
+
+void reset_all_pins(void);
+// reset_pin_number takes the pin number instead of the pointer so that objects don't
+// need to store a full pointer.
+void reset_pin_number(uint8_t pin_number);
+void never_reset_pin_number(uint8_t pin_number);
+void claim_pin(const mcu_pin_obj_t *pin);
+bool pin_number_is_free(uint8_t pin_number);
+
+#endif // MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_MICROCONTROLLER_PIN_H
diff --git a/circuitpython/ports/raspberrypi/common-hal/microcontroller/Processor.c b/circuitpython/ports/raspberrypi/common-hal/microcontroller/Processor.c
new file mode 100644
index 0000000..8c5bc7b
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/microcontroller/Processor.c
@@ -0,0 +1,99 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 Scott Shawcroft for Adafruit Industries
+ *
+ * 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 <math.h>
+#include <string.h>
+
+#include "py/mphal.h"
+#include "common-hal/microcontroller/Processor.h"
+#include "shared-bindings/microcontroller/Processor.h"
+#include "shared-bindings/microcontroller/ResetReason.h"
+
+#include "src/rp2_common/hardware_adc/include/hardware/adc.h"
+#include "src/rp2_common/hardware_clocks/include/hardware/clocks.h"
+
+#include "src/rp2040/hardware_regs/include/hardware/regs/vreg_and_chip_reset.h"
+#include "src/rp2040/hardware_regs/include/hardware/regs/watchdog.h"
+#include "src/rp2040/hardware_structs/include/hardware/structs/vreg_and_chip_reset.h"
+#include "src/rp2040/hardware_structs/include/hardware/structs/watchdog.h"
+
+float common_hal_mcu_processor_get_temperature(void) {
+ adc_init();
+ adc_set_temp_sensor_enabled(true);
+ adc_select_input(4);
+ uint16_t value = adc_read();
+ adc_set_temp_sensor_enabled(false);
+ float voltage = value * 3.3 / (1 << 12);
+ // TODO: turn the ADC back off
+ return 27 - (voltage - 0.706) / 0.001721;
+}
+
+float common_hal_mcu_processor_get_voltage(void) {
+ return 3.3f;
+}
+
+uint32_t common_hal_mcu_processor_get_frequency(void) {
+ return clock_get_hz(clk_sys);
+}
+
+void common_hal_mcu_processor_get_uid(uint8_t raw_id[]) {
+ pico_unique_board_id_t retrieved_id;
+ pico_get_unique_board_id(&retrieved_id);
+ memcpy(raw_id, retrieved_id.id, COMMON_HAL_MCU_PROCESSOR_UID_LENGTH);
+}
+
+mcu_reset_reason_t common_hal_mcu_processor_get_reset_reason(void) {
+ mcu_reset_reason_t reason = RESET_REASON_UNKNOWN;
+
+ uint32_t watchdog_reset_reg = watchdog_hw->reason;
+ uint32_t chip_reset_reg = vreg_and_chip_reset_hw->chip_reset;
+
+ if (chip_reset_reg & VREG_AND_CHIP_RESET_CHIP_RESET_HAD_PSM_RESTART_BITS) {
+ reason = RESET_REASON_RESCUE_DEBUG;
+ }
+
+ if (chip_reset_reg & VREG_AND_CHIP_RESET_CHIP_RESET_HAD_RUN_BITS) {
+ reason = RESET_REASON_RESET_PIN;
+ }
+
+ if (chip_reset_reg & VREG_AND_CHIP_RESET_CHIP_RESET_HAD_POR_BITS) {
+ // NOTE: This register is also used for brownout, but there is no way to differentiate between power on and brown out
+ reason = RESET_REASON_POWER_ON;
+ }
+
+ // Check watchdog after chip reset since watchdog doesn't clear chip_reset, while chip_reset clears the watchdog
+
+ if (watchdog_reset_reg & WATCHDOG_REASON_TIMER_BITS) {
+ // This bit can also be set during a software reset because the pico-sdk performs a software reset by setting an extremely low timeout on the watchdog, rather than triggering a watchdog reset manually
+ reason = RESET_REASON_WATCHDOG;
+ }
+
+ if (watchdog_reset_reg & WATCHDOG_REASON_FORCE_BITS) {
+ reason = RESET_REASON_SOFTWARE;
+ }
+
+ return reason;
+}
diff --git a/circuitpython/ports/raspberrypi/common-hal/microcontroller/Processor.h b/circuitpython/ports/raspberrypi/common-hal/microcontroller/Processor.h
new file mode 100644
index 0000000..afb43f9
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/microcontroller/Processor.h
@@ -0,0 +1,41 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 Scott Shawcroft for Adafruit Industries
+ *
+ * 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.
+ */
+
+#ifndef MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_MICROCONTROLLER_PROCESSOR_H
+#define MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_MICROCONTROLLER_PROCESSOR_H
+
+#include "src/rp2_common/pico_unique_id/include/pico/unique_id.h"
+
+#define COMMON_HAL_MCU_PROCESSOR_UID_LENGTH PICO_UNIQUE_BOARD_ID_SIZE_BYTES
+
+#include "py/obj.h"
+
+typedef struct {
+ mp_obj_base_t base;
+ // Stores no state currently.
+} mcu_processor_obj_t;
+
+#endif // MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_MICROCONTROLLER_PROCESSOR_H
diff --git a/circuitpython/ports/raspberrypi/common-hal/microcontroller/__init__.c b/circuitpython/ports/raspberrypi/common-hal/microcontroller/__init__.c
new file mode 100644
index 0000000..ed68835
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/microcontroller/__init__.c
@@ -0,0 +1,184 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 Scott Shawcroft for Adafruit Industries
+ *
+ * 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/mphal.h"
+#include "py/obj.h"
+#include "py/runtime.h"
+
+#include "common-hal/microcontroller/__init__.h"
+#if CIRCUITPY_NVM
+#include "shared-bindings/nvm/ByteArray.h"
+#endif
+#include "shared-bindings/microcontroller/__init__.h"
+#include "shared-bindings/microcontroller/Pin.h"
+#include "shared-bindings/microcontroller/Processor.h"
+#include "supervisor/filesystem.h"
+#include "supervisor/port.h"
+#include "supervisor/shared/safe_mode.h"
+#include "supervisor/shared/translate.h"
+
+#include "src/rp2040/hardware_structs/include/hardware/structs/sio.h"
+#include "src/rp2_common/hardware_sync/include/hardware/sync.h"
+
+#include "hardware/watchdog.h"
+
+void common_hal_mcu_delay_us(uint32_t delay) {
+ mp_hal_delay_us(delay);
+}
+
+volatile uint32_t nesting_count = 0;
+void common_hal_mcu_disable_interrupts(void) {
+ // We don't use save_and_disable_interrupts() from the sdk because we don't want to worry about PRIMASK.
+ // This is what we do on the SAMD21 via CMSIS.
+ asm volatile ("cpsid i" : : : "memory");
+ __dmb();
+ nesting_count++;
+}
+
+void common_hal_mcu_enable_interrupts(void) {
+ if (nesting_count == 0) {
+ // reset_into_safe_mode(LOCKING_ERROR);
+ }
+ nesting_count--;
+ if (nesting_count > 0) {
+ return;
+ }
+ __dmb();
+ asm volatile ("cpsie i" : : : "memory");
+}
+
+static bool next_reset_to_bootloader = false;
+
+void common_hal_mcu_on_next_reset(mcu_runmode_t runmode) {
+ switch (runmode) {
+ case RUNMODE_UF2:
+ case RUNMODE_BOOTLOADER:
+ next_reset_to_bootloader = true;
+ break;
+ case RUNMODE_SAFE_MODE:
+ safe_mode_on_next_reset(PROGRAMMATIC_SAFE_MODE);
+ break;
+ default:
+ break;
+ }
+}
+
+void common_hal_mcu_reset(void) {
+ filesystem_flush();
+ if (next_reset_to_bootloader) {
+ reset_to_bootloader();
+ } else {
+ reset_cpu();
+ }
+}
+
+// The singleton microcontroller.Processor object, bound to microcontroller.cpu
+// It currently only has properties, and no state.
+#if CIRCUITPY_PROCESSOR_COUNT > 1
+static const mcu_processor_obj_t processor0 = {
+ .base = {
+ .type = &mcu_processor_type,
+ },
+};
+
+static const mcu_processor_obj_t processor1 = {
+ .base = {
+ .type = &mcu_processor_type,
+ },
+};
+
+const mp_rom_obj_tuple_t common_hal_multi_processor_obj = {
+ {&mp_type_tuple},
+ CIRCUITPY_PROCESSOR_COUNT,
+ {
+ MP_ROM_PTR(&processor0),
+ MP_ROM_PTR(&processor1)
+ }
+};
+#endif
+
+const mcu_processor_obj_t common_hal_mcu_processor_obj = {
+ .base = {
+ .type = &mcu_processor_type,
+ },
+};
+
+#if CIRCUITPY_NVM && CIRCUITPY_INTERNAL_NVM_SIZE > 0
+// The singleton nvm.ByteArray object.
+const nvm_bytearray_obj_t common_hal_mcu_nvm_obj = {
+ .base = {
+ .type = &nvm_bytearray_type,
+ },
+ .len = CIRCUITPY_INTERNAL_NVM_SIZE,
+ .start_address = (uint8_t *)(CIRCUITPY_INTERNAL_NVM_START_ADDR)
+};
+#endif
+
+#if CIRCUITPY_WATCHDOG
+// The singleton watchdog.WatchDogTimer object.
+watchdog_watchdogtimer_obj_t common_hal_mcu_watchdogtimer_obj = {
+ .base = {
+ .type = &watchdog_watchdogtimer_type,
+ },
+ .timeout = 0.0f,
+ .mode = WATCHDOGMODE_NONE,
+};
+#endif
+
+// This maps MCU pin names to pin objects.
+const mp_rom_map_elem_t mcu_pin_global_dict_table[TOTAL_GPIO_COUNT] = {
+ { MP_ROM_QSTR(MP_QSTR_GPIO0), MP_ROM_PTR(&pin_GPIO0) },
+ { MP_ROM_QSTR(MP_QSTR_GPIO1), MP_ROM_PTR(&pin_GPIO1) },
+ { MP_ROM_QSTR(MP_QSTR_GPIO2), MP_ROM_PTR(&pin_GPIO2) },
+ { MP_ROM_QSTR(MP_QSTR_GPIO3), MP_ROM_PTR(&pin_GPIO3) },
+ { MP_ROM_QSTR(MP_QSTR_GPIO4), MP_ROM_PTR(&pin_GPIO4) },
+ { MP_ROM_QSTR(MP_QSTR_GPIO5), MP_ROM_PTR(&pin_GPIO5) },
+ { MP_ROM_QSTR(MP_QSTR_GPIO6), MP_ROM_PTR(&pin_GPIO6) },
+ { MP_ROM_QSTR(MP_QSTR_GPIO7), MP_ROM_PTR(&pin_GPIO7) },
+ { MP_ROM_QSTR(MP_QSTR_GPIO8), MP_ROM_PTR(&pin_GPIO8) },
+ { MP_ROM_QSTR(MP_QSTR_GPIO9), MP_ROM_PTR(&pin_GPIO9) },
+ { MP_ROM_QSTR(MP_QSTR_GPIO10), MP_ROM_PTR(&pin_GPIO10) },
+ { MP_ROM_QSTR(MP_QSTR_GPIO11), MP_ROM_PTR(&pin_GPIO11) },
+ { MP_ROM_QSTR(MP_QSTR_GPIO12), MP_ROM_PTR(&pin_GPIO12) },
+ { MP_ROM_QSTR(MP_QSTR_GPIO13), MP_ROM_PTR(&pin_GPIO13) },
+ { MP_ROM_QSTR(MP_QSTR_GPIO14), MP_ROM_PTR(&pin_GPIO14) },
+ { MP_ROM_QSTR(MP_QSTR_GPIO15), MP_ROM_PTR(&pin_GPIO15) },
+ { MP_ROM_QSTR(MP_QSTR_GPIO16), MP_ROM_PTR(&pin_GPIO16) },
+ { MP_ROM_QSTR(MP_QSTR_GPIO17), MP_ROM_PTR(&pin_GPIO17) },
+ { MP_ROM_QSTR(MP_QSTR_GPIO18), MP_ROM_PTR(&pin_GPIO18) },
+ { MP_ROM_QSTR(MP_QSTR_GPIO19), MP_ROM_PTR(&pin_GPIO19) },
+ { MP_ROM_QSTR(MP_QSTR_GPIO20), MP_ROM_PTR(&pin_GPIO20) },
+ { MP_ROM_QSTR(MP_QSTR_GPIO21), MP_ROM_PTR(&pin_GPIO21) },
+ { MP_ROM_QSTR(MP_QSTR_GPIO22), MP_ROM_PTR(&pin_GPIO22) },
+ { MP_ROM_QSTR(MP_QSTR_GPIO23), MP_ROM_PTR(&pin_GPIO23) },
+ { MP_ROM_QSTR(MP_QSTR_GPIO24), MP_ROM_PTR(&pin_GPIO24) },
+ { MP_ROM_QSTR(MP_QSTR_GPIO25), MP_ROM_PTR(&pin_GPIO25) },
+ { MP_ROM_QSTR(MP_QSTR_GPIO26), MP_ROM_PTR(&pin_GPIO26) },
+ { MP_ROM_QSTR(MP_QSTR_GPIO27), MP_ROM_PTR(&pin_GPIO27) },
+ { MP_ROM_QSTR(MP_QSTR_GPIO28), MP_ROM_PTR(&pin_GPIO28) },
+ { MP_ROM_QSTR(MP_QSTR_GPIO29), MP_ROM_PTR(&pin_GPIO29) },
+};
+MP_DEFINE_CONST_DICT(mcu_pin_globals, mcu_pin_global_dict_table);
diff --git a/circuitpython/ports/raspberrypi/common-hal/microcontroller/__init__.h b/circuitpython/ports/raspberrypi/common-hal/microcontroller/__init__.h
new file mode 100644
index 0000000..cc509b6
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/microcontroller/__init__.h
@@ -0,0 +1,36 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 Scott Shawcroft for Adafruit Industries
+ *
+ * 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.
+ */
+
+#ifndef MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_MICROCONTROLLER___INIT___H
+#define MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_MICROCONTROLLER___INIT___H
+
+#include "src/rp2040/hardware_regs/include/hardware/platform_defs.h"
+
+#define TOTAL_GPIO_COUNT NUM_BANK0_GPIOS
+
+extern const mp_rom_map_elem_t mcu_pin_global_dict_table[TOTAL_GPIO_COUNT];
+
+#endif // MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_MICROCONTROLLER___INIT___H
diff --git a/circuitpython/ports/raspberrypi/common-hal/neopixel_write/__init__.c b/circuitpython/ports/raspberrypi/common-hal/neopixel_write/__init__.c
new file mode 100644
index 0000000..d473285
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/neopixel_write/__init__.c
@@ -0,0 +1,104 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 Scott Shawcroft for Adafruit Industries
+ *
+ * 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 "shared-bindings/neopixel_write/__init__.h"
+
+#include "bindings/rp2pio/StateMachine.h"
+#include "common-hal/rp2pio/StateMachine.h"
+#include "shared-bindings/microcontroller/__init__.h"
+#include "shared-bindings/digitalio/DigitalInOut.h"
+
+#include "supervisor/port.h"
+
+uint64_t next_start_raw_ticks = 0;
+
+// NeoPixels are 800khz bit streams. We are choosing zeros as <312ns hi, 936 lo> and ones
+// and ones as <700 ns hi, 556 ns lo>.
+// cycle. The first two instructions always run while only one of the two final
+// instructions run per bit. We start with the low period because it can be
+// longer while waiting for more data.
+const uint16_t neopixel_program[] = {
+// bitloop:
+// out x 1 side 0 [6]; Drive low. Side-set still takes place before instruction stalls.
+ 0x6621,
+// jmp !x do_zero side 1 [3]; Branch on the bit we shifted out previous delay. Drive high.
+ 0x1323,
+// do_one:
+// jmp bitloop side 1 [4]; Continue driving high, for a one (long pulse)
+ 0x1400,
+// do_zero:
+// nop side 0 [4]; Or drive low, for a zero (short pulse)
+ 0xa442
+};
+
+void common_hal_neopixel_write(const digitalio_digitalinout_obj_t *digitalinout, uint8_t *pixels, uint32_t num_bytes) {
+ // Set everything up.
+ rp2pio_statemachine_obj_t state_machine;
+
+ // TODO: Cache the state machine after we create it once. We'll need a way to
+ // change the pins then though.
+ uint32_t pins_we_use = 1 << digitalinout->pin->number;
+ bool ok = rp2pio_statemachine_construct(&state_machine,
+ neopixel_program, sizeof(neopixel_program) / sizeof(neopixel_program[0]),
+ 12800000, // 12.8MHz, to get appropriate sub-bit times in PIO program.
+ NULL, 0, // init program
+ NULL, 1, // out
+ NULL, 1, // in
+ 0, 0, // in pulls
+ NULL, 1, // set
+ digitalinout->pin, 1, // sideset
+ 0, pins_we_use, // initial pin state
+ NULL, // jump pin
+ pins_we_use, true, false,
+ true, 8, false, // TX, auto pull every 8 bits. shift left to output msb first
+ true, // Wait for txstall. If we don't, then we'll deinit too quickly.
+ false, 32, true, // RX setting we don't use
+ false, // claim pins
+ false, // Not user-interruptible.
+ false, // No sideset enable
+ 0, -1); // wrap
+ if (!ok) {
+ // Do nothing. Maybe bitbang?
+ return;
+ }
+
+ // Wait to make sure we don't append onto the last transmission. This should only be a tick or
+ // two.
+ while (port_get_raw_ticks(NULL) < next_start_raw_ticks) {
+ }
+
+ common_hal_rp2pio_statemachine_write(&state_machine, pixels, num_bytes, 1 /* stride in bytes */, false);
+
+ // Use a private deinit of the state machine that doesn't reset the pin.
+ rp2pio_statemachine_deinit(&state_machine, true);
+
+ // Reset the pin and release it from the PIO
+ gpio_init(digitalinout->pin->number);
+ common_hal_digitalio_digitalinout_switch_to_output((digitalio_digitalinout_obj_t *)digitalinout, false, DRIVE_MODE_PUSH_PULL);
+
+ // Update the next start to +2 ticks. This ensures we give it at least 300us.
+ next_start_raw_ticks = port_get_raw_ticks(NULL) + 2;
+}
diff --git a/circuitpython/ports/raspberrypi/common-hal/nvm/ByteArray.c b/circuitpython/ports/raspberrypi/common-hal/nvm/ByteArray.c
new file mode 100644
index 0000000..cc52c88
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/nvm/ByteArray.c
@@ -0,0 +1,121 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 microDev
+ *
+ * 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 "common-hal/nvm/ByteArray.h"
+#include "shared-bindings/nvm/ByteArray.h"
+
+#include <string.h>
+
+#include "py/runtime.h"
+#include "src/rp2_common/hardware_flash/include/hardware/flash.h"
+#include "shared-bindings/microcontroller/__init__.h"
+
+extern uint32_t __flash_binary_start;
+static const uint32_t flash_binary_start = (uint32_t)&__flash_binary_start;
+
+#define RMV_OFFSET(addr) addr - flash_binary_start
+
+uint32_t common_hal_nvm_bytearray_get_length(const nvm_bytearray_obj_t *self) {
+ return self->len;
+}
+
+static void write_page(uint32_t page_addr, uint32_t offset, uint32_t len, uint8_t *bytes) {
+ // Write a whole page to flash, buffering it first and then erasing and rewriting it
+ // since we can only write a whole page at a time.
+ if (offset == 0 && len == FLASH_PAGE_SIZE) {
+ // disable interrupts to prevent core hang on rp2040
+ common_hal_mcu_disable_interrupts();
+ flash_range_program(RMV_OFFSET(page_addr), bytes, FLASH_PAGE_SIZE);
+ common_hal_mcu_enable_interrupts();
+ } else {
+ uint8_t buffer[FLASH_PAGE_SIZE];
+ memcpy(buffer, (uint8_t *)page_addr, FLASH_PAGE_SIZE);
+ memcpy(buffer + offset, bytes, len);
+ common_hal_mcu_disable_interrupts();
+ flash_range_program(RMV_OFFSET(page_addr), buffer, FLASH_PAGE_SIZE);
+ common_hal_mcu_enable_interrupts();
+ }
+
+}
+
+static void erase_and_write_sector(uint32_t address, uint32_t len, uint8_t *bytes) {
+ // Write a whole sector to flash, buffering it first and then erasing and rewriting it
+ // since we can only erase a whole sector at a time.
+ uint8_t buffer[FLASH_SECTOR_SIZE];
+ #pragma GCC diagnostic push
+ #if __GNUC__ >= 11
+ // TODO: Update this to a better workaround for GCC 11 when one is provided.
+ // See: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=99578#c20
+ #pragma GCC diagnostic ignored "-Warray-bounds"
+ #pragma GCC diagnostic ignored "-Wstringop-overread"
+ #endif
+ memcpy(buffer, (uint8_t *)CIRCUITPY_INTERNAL_NVM_START_ADDR, FLASH_SECTOR_SIZE);
+ #pragma GCC diagnostic pop
+ memcpy(buffer + address, bytes, len);
+ // disable interrupts to prevent core hang on rp2040
+ common_hal_mcu_disable_interrupts();
+ flash_range_erase(RMV_OFFSET(CIRCUITPY_INTERNAL_NVM_START_ADDR), FLASH_SECTOR_SIZE);
+ flash_range_program(RMV_OFFSET(CIRCUITPY_INTERNAL_NVM_START_ADDR), buffer, FLASH_SECTOR_SIZE);
+ common_hal_mcu_enable_interrupts();
+}
+
+void common_hal_nvm_bytearray_get_bytes(const nvm_bytearray_obj_t *self,
+ uint32_t start_index, uint32_t len, uint8_t *values) {
+ memcpy(values, self->start_address + start_index, len);
+}
+
+bool common_hal_nvm_bytearray_set_bytes(const nvm_bytearray_obj_t *self,
+ uint32_t start_index, uint8_t *values, uint32_t len) {
+ uint8_t values_in[len];
+ common_hal_nvm_bytearray_get_bytes(self, start_index, len, values_in);
+
+ bool all_ones = true;
+ for (uint32_t i = 0; i < len; i++) {
+ if (values_in[i] != UINT8_MAX) {
+ all_ones = false;
+ break;
+ }
+ }
+
+ if (all_ones) {
+ uint32_t address = (uint32_t)self->start_address + start_index;
+ uint32_t offset = address % FLASH_PAGE_SIZE;
+ uint32_t page_addr = address - offset;
+
+ while (len) {
+ uint32_t write_len = MIN(len, FLASH_PAGE_SIZE - offset);
+ write_page(page_addr, offset, write_len, values);
+ len -= write_len;
+ values += write_len;
+ page_addr += FLASH_PAGE_SIZE;
+ offset = 0;
+ }
+ } else {
+ erase_and_write_sector(start_index, len, values);
+ }
+
+ return true;
+}
diff --git a/circuitpython/ports/raspberrypi/common-hal/nvm/ByteArray.h b/circuitpython/ports/raspberrypi/common-hal/nvm/ByteArray.h
new file mode 100644
index 0000000..4667e6b
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/nvm/ByteArray.h
@@ -0,0 +1,38 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 microDev
+ *
+ * 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.
+ */
+
+#ifndef MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_NVM_BYTEARRAY_H
+#define MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_NVM_BYTEARRAY_H
+
+#include "py/obj.h"
+
+typedef struct {
+ mp_obj_base_t base;
+ uint8_t *start_address;
+ uint32_t len;
+} nvm_bytearray_obj_t;
+
+#endif // MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_NVM_BYTEARRAY_H
diff --git a/circuitpython/ports/raspberrypi/common-hal/nvm/__init__.c b/circuitpython/ports/raspberrypi/common-hal/nvm/__init__.c
new file mode 100644
index 0000000..1b702a1
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/nvm/__init__.c
@@ -0,0 +1 @@
+// No nvm module functions.
diff --git a/circuitpython/ports/raspberrypi/common-hal/os/__init__.c b/circuitpython/ports/raspberrypi/common-hal/os/__init__.c
new file mode 100644
index 0000000..8054182
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/os/__init__.c
@@ -0,0 +1,127 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 Scott Shawcroft for Adafruit Industries
+ *
+ * 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 "genhdr/mpversion.h"
+#include "py/mpconfig.h"
+#include "py/objstr.h"
+#include "py/objtuple.h"
+#include "py/qstr.h"
+
+#include "shared-bindings/os/__init__.h"
+
+#include "lib/crypto-algorithms/sha256.h"
+
+#include "hardware/structs/rosc.h"
+
+#include <string.h>
+
+STATIC const qstr os_uname_info_fields[] = {
+ MP_QSTR_sysname, MP_QSTR_nodename,
+ MP_QSTR_release, MP_QSTR_version, MP_QSTR_machine
+};
+STATIC const MP_DEFINE_STR_OBJ(os_uname_info_sysname_obj, "rp2040");
+STATIC const MP_DEFINE_STR_OBJ(os_uname_info_nodename_obj, "rp2040");
+STATIC const MP_DEFINE_STR_OBJ(os_uname_info_release_obj, MICROPY_VERSION_STRING);
+STATIC const MP_DEFINE_STR_OBJ(os_uname_info_version_obj, MICROPY_GIT_TAG " on " MICROPY_BUILD_DATE);
+STATIC const MP_DEFINE_STR_OBJ(os_uname_info_machine_obj, MICROPY_HW_BOARD_NAME " with " MICROPY_HW_MCU_NAME);
+
+
+STATIC MP_DEFINE_ATTRTUPLE(
+ os_uname_info_obj,
+ os_uname_info_fields,
+ 5,
+ (mp_obj_t)&os_uname_info_sysname_obj,
+ (mp_obj_t)&os_uname_info_nodename_obj,
+ (mp_obj_t)&os_uname_info_release_obj,
+ (mp_obj_t)&os_uname_info_version_obj,
+ (mp_obj_t)&os_uname_info_machine_obj
+ );
+
+mp_obj_t common_hal_os_uname(void) {
+ return (mp_obj_t)&os_uname_info_obj;
+}
+
+// NIST Special Publication 800-90B (draft) recommends several extractors,
+// including the SHA hash family and states that if the amount of entropy input
+// is twice the number of bits output from them, that output can be considered
+// essentially fully random. If every RANDOM_SAFETY_MARGIN bits from
+// `rosc_hw->randombit` have at least 1 bit of entropy, then this criterion is met.
+//
+// This works by seeding the `random_state` with plenty of random bits (SHA256
+// as entropy harvesting function), then using that state it as a counter input
+// (SHA256 as a CSPRNG), re-seeding at least every 256 blocks (8kB).
+//
+// In practice, `PractRand` doesn't detect any gross problems with the output
+// random numbers on samples of 1 to 8 megabytes, no matter the setting of
+// RANDOM_SAFETY_MARGIN. (it does detect "unusual" results from time to time,
+// as it will with any RNG)
+#define RANDOM_SAFETY_MARGIN (4)
+
+static BYTE random_state[SHA256_BLOCK_SIZE];
+static void seed_random_bits(BYTE out[SHA256_BLOCK_SIZE]) {
+ CRYAL_SHA256_CTX context;
+ sha256_init(&context);
+ for (int i = 0; i < 2 * RANDOM_SAFETY_MARGIN; i++) {
+ for (int j = 0; j < SHA256_BLOCK_SIZE; j++) {
+ out[j] = rosc_hw->randombit & 1;
+ for (int k = 0; k < 8; k++) {
+ out[j] = (out[j] << 1) ^ (rosc_hw->randombit & 1);
+ }
+ }
+ sha256_update(&context, out, SHA256_BLOCK_SIZE);
+ }
+ sha256_final(&context, out);
+}
+
+static void get_random_bits(BYTE out[SHA256_BLOCK_SIZE]) {
+ if (!random_state[0]++) {
+ seed_random_bits(random_state);
+ }
+ CRYAL_SHA256_CTX context;
+ sha256_init(&context);
+ sha256_update(&context, random_state, SHA256_BLOCK_SIZE);
+ sha256_final(&context, out);
+}
+
+bool common_hal_os_urandom(uint8_t *buffer, mp_uint_t length) {
+#define ROSC_POWER_SAVE (1) // assume ROSC is not necessarily active all the time
+ #if ROSC_POWER_SAVE
+ uint32_t old_rosc_ctrl = rosc_hw->ctrl;
+ rosc_hw->ctrl = (old_rosc_ctrl & ~ROSC_CTRL_ENABLE_BITS) | (ROSC_CTRL_ENABLE_VALUE_ENABLE << 12);
+ #endif
+ while (length) {
+ size_t n = MIN(length, SHA256_BLOCK_SIZE);
+ BYTE sha_buf[SHA256_BLOCK_SIZE];
+ get_random_bits(sha_buf);
+ memcpy(buffer, sha_buf, n);
+ buffer += n;
+ length -= n;
+ }
+ #if ROSC_POWER_SAVE
+ rosc_hw->ctrl = old_rosc_ctrl;
+ #endif
+ return true;
+}
diff --git a/circuitpython/ports/raspberrypi/common-hal/paralleldisplay/ParallelBus.c b/circuitpython/ports/raspberrypi/common-hal/paralleldisplay/ParallelBus.c
new file mode 100644
index 0000000..96c89ad
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/paralleldisplay/ParallelBus.c
@@ -0,0 +1,170 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 Scott Shawcroft for Adafruit Industries
+ *
+ * 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 "shared-bindings/paralleldisplay/ParallelBus.h"
+
+#include <stdint.h>
+
+#include "common-hal/microcontroller/Pin.h"
+#include "py/runtime.h"
+#include "shared-bindings/digitalio/DigitalInOut.h"
+#include "shared-bindings/microcontroller/__init__.h"
+#include "bindings/rp2pio/StateMachine.h"
+#include "common-hal/rp2pio/StateMachine.h"
+
+static const uint16_t parallel_program[] = {
+// .side_set 1
+// .wrap_target
+ 0x6008, // out pins, 8 side 0
+ 0xB042 // nop side 1
+// .wrap
+};
+
+void common_hal_paralleldisplay_parallelbus_construct(paralleldisplay_parallelbus_obj_t *self,
+ const mcu_pin_obj_t *data0, const mcu_pin_obj_t *command, const mcu_pin_obj_t *chip_select,
+ const mcu_pin_obj_t *write, const mcu_pin_obj_t *read, const mcu_pin_obj_t *reset, uint32_t frequency) {
+
+ uint8_t data_pin = data0->number;
+ for (uint8_t i = 0; i < 8; i++) {
+ if (!pin_number_is_free(data_pin + i)) {
+ mp_raise_ValueError_varg(translate("Bus pin %d is already in use"), i);
+ }
+ }
+
+ uint8_t write_pin = write->number;
+ if (!pin_number_is_free(write_pin)) {
+ mp_raise_ValueError_varg(translate("Bus pin %d is already in use"), write_pin);
+ }
+
+ self->command.base.type = &digitalio_digitalinout_type;
+ common_hal_digitalio_digitalinout_construct(&self->command, command);
+ common_hal_digitalio_digitalinout_switch_to_output(&self->command, true, DRIVE_MODE_PUSH_PULL);
+
+ self->chip_select.base.type = &digitalio_digitalinout_type;
+ common_hal_digitalio_digitalinout_construct(&self->chip_select, chip_select);
+ common_hal_digitalio_digitalinout_switch_to_output(&self->chip_select, true, DRIVE_MODE_PUSH_PULL);
+
+ self->read.base.type = &mp_type_NoneType;
+ if (read != NULL) {
+ self->read.base.type = &digitalio_digitalinout_type;
+ common_hal_digitalio_digitalinout_construct(&self->read, read);
+ common_hal_digitalio_digitalinout_switch_to_output(&self->read, true, DRIVE_MODE_PUSH_PULL);
+ never_reset_pin_number(read->number);
+ }
+
+ self->data0_pin = data_pin;
+ self->write = write_pin;
+
+ self->reset.base.type = &mp_type_NoneType;
+ if (reset != NULL) {
+ self->reset.base.type = &digitalio_digitalinout_type;
+ common_hal_digitalio_digitalinout_construct(&self->reset, reset);
+ common_hal_digitalio_digitalinout_switch_to_output(&self->reset, true, DRIVE_MODE_PUSH_PULL);
+ never_reset_pin_number(reset->number);
+ common_hal_paralleldisplay_parallelbus_reset(self);
+ }
+
+ never_reset_pin_number(command->number);
+ never_reset_pin_number(chip_select->number);
+ never_reset_pin_number(write_pin);
+ for (uint8_t i = 0; i < 8; i++) {
+ never_reset_pin_number(data_pin + i);
+ }
+
+ common_hal_rp2pio_statemachine_construct(&self->state_machine,
+ parallel_program, MP_ARRAY_SIZE(parallel_program),
+ frequency * 2, // frequency multiplied by 2 as 2 PIO instructions
+ NULL, 0, // init
+ data0, 8, 0, 255, // first out pin, # out pins
+ NULL, 0, 0, 0, // first in pin, # in pins
+ NULL, 0, 0, 0, // first set pin
+ write, 1, 0, 1, // first sideset pin
+ false, // No sideset enable
+ NULL, PULL_NONE, // jump pin
+ 0, // wait gpio pins
+ true, // exclusive pin usage
+ true, 8, true, // TX, auto pull every 8 bits. shift left to output msb first
+ false, // wait for TX stall
+ false, 32, true, // RX setting we don't use
+ false, // Not user-interruptible.
+ 0, -1); // wrap settings
+
+ common_hal_rp2pio_statemachine_never_reset(&self->state_machine);
+}
+
+void common_hal_paralleldisplay_parallelbus_deinit(paralleldisplay_parallelbus_obj_t *self) {
+ common_hal_rp2pio_statemachine_deinit(&self->state_machine);
+
+ for (uint8_t i = 0; i < 8; i++) {
+ reset_pin_number(self->data0_pin + i);
+ }
+
+ reset_pin_number(self->command.pin->number);
+ reset_pin_number(self->chip_select.pin->number);
+ reset_pin_number(self->write);
+ if (self->read.base.type != &mp_type_NoneType) {
+ reset_pin_number(self->read.pin->number);
+ }
+ if (self->reset.base.type != &mp_type_NoneType) {
+ reset_pin_number(self->reset.pin->number);
+ }
+}
+
+bool common_hal_paralleldisplay_parallelbus_reset(mp_obj_t obj) {
+ paralleldisplay_parallelbus_obj_t *self = MP_OBJ_TO_PTR(obj);
+ if (self->reset.base.type == &mp_type_NoneType) {
+ return false;
+ }
+
+ common_hal_digitalio_digitalinout_set_value(&self->reset, false);
+ common_hal_mcu_delay_us(4);
+ common_hal_digitalio_digitalinout_set_value(&self->reset, true);
+ return true;
+}
+
+bool common_hal_paralleldisplay_parallelbus_bus_free(mp_obj_t obj) {
+ return true;
+}
+
+bool common_hal_paralleldisplay_parallelbus_begin_transaction(mp_obj_t obj) {
+ paralleldisplay_parallelbus_obj_t *self = MP_OBJ_TO_PTR(obj);
+ common_hal_digitalio_digitalinout_set_value(&self->chip_select, false);
+ return true;
+}
+
+void common_hal_paralleldisplay_parallelbus_send(mp_obj_t obj, display_byte_type_t byte_type,
+ display_chip_select_behavior_t chip_select, const uint8_t *data, uint32_t data_length) {
+
+ paralleldisplay_parallelbus_obj_t *self = MP_OBJ_TO_PTR(obj);
+
+ common_hal_digitalio_digitalinout_set_value(&self->command, byte_type == DISPLAY_DATA);
+ common_hal_rp2pio_statemachine_write(&self->state_machine, data, data_length, 1, false);
+}
+
+void common_hal_paralleldisplay_parallelbus_end_transaction(mp_obj_t obj) {
+ paralleldisplay_parallelbus_obj_t *self = MP_OBJ_TO_PTR(obj);
+ common_hal_digitalio_digitalinout_set_value(&self->chip_select, true);
+}
diff --git a/circuitpython/ports/raspberrypi/common-hal/paralleldisplay/ParallelBus.h b/circuitpython/ports/raspberrypi/common-hal/paralleldisplay/ParallelBus.h
new file mode 100644
index 0000000..87f149b
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/paralleldisplay/ParallelBus.h
@@ -0,0 +1,45 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 Scott Shawcroft for Adafruit Industries
+ *
+ * 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.
+ */
+
+#ifndef MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_PARALLELDISPLAY_PARALLELBUS_H
+#define MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_PARALLELDISPLAY_PARALLELBUS_H
+
+#include "common-hal/digitalio/DigitalInOut.h"
+#include "bindings/rp2pio/StateMachine.h"
+#include "common-hal/rp2pio/StateMachine.h"
+
+typedef struct {
+ mp_obj_base_t base;
+ digitalio_digitalinout_obj_t command;
+ digitalio_digitalinout_obj_t chip_select;
+ digitalio_digitalinout_obj_t reset;
+ digitalio_digitalinout_obj_t read;
+ uint8_t write;
+ uint8_t data0_pin;
+ rp2pio_statemachine_obj_t state_machine;
+} paralleldisplay_parallelbus_obj_t;
+
+#endif // MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_PARALLELDISPLAY_PARALLELBUS_H
diff --git a/circuitpython/ports/raspberrypi/common-hal/pulseio/PulseIn.c b/circuitpython/ports/raspberrypi/common-hal/pulseio/PulseIn.c
new file mode 100644
index 0000000..45d3162
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/pulseio/PulseIn.c
@@ -0,0 +1,244 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 Dave Putz for Adafruit Industries
+ *
+ * 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 "src/rp2_common/hardware_gpio/include/hardware/gpio.h"
+
+#include <stdint.h>
+
+#include "py/runtime.h"
+#include "shared-bindings/microcontroller/__init__.h"
+#include "shared-bindings/pulseio/PulseIn.h"
+#include "shared-bindings/microcontroller/Pin.h"
+#include "supervisor/shared/translate.h"
+#include "bindings/rp2pio/StateMachine.h"
+#include "common-hal/pulseio/PulseIn.h"
+
+#define NO_PIN 0xff
+#define MAX_PULSE 65535
+#define MIN_PULSE 10
+
+uint16_t pulsein_program[] = {
+ 0x4001, // 1: in pins, 1
+};
+
+void common_hal_pulseio_pulsein_construct(pulseio_pulsein_obj_t *self,
+ const mcu_pin_obj_t *pin, uint16_t maxlen, bool idle_state) {
+
+ self->buffer = (uint16_t *)m_malloc(maxlen * sizeof(uint16_t), false);
+ if (self->buffer == NULL) {
+ mp_raise_msg_varg(&mp_type_MemoryError, translate("Failed to allocate RX buffer of %d bytes"), maxlen * sizeof(uint16_t));
+ }
+ self->pin = pin->number;
+ self->maxlen = maxlen;
+ self->idle_state = idle_state;
+ self->start = 0;
+ self->len = 0;
+
+ bool ok = rp2pio_statemachine_construct(&self->state_machine,
+ pulsein_program, sizeof(pulsein_program) / sizeof(pulsein_program[0]),
+ 1000000,
+ NULL, 0,
+ NULL, 0,
+ pin, 1,
+ 0,0,
+ NULL, 0,
+ NULL, 0,
+ 1, 0,
+ NULL, // jump pin
+ 1 << self->pin, false, true,
+ false, 8, false, // TX, unused
+ false,
+ true, 32, true, // RX auto-push every 32 bits
+ false, // claim pins
+ false, // Not user-interruptible.
+ false, // No sideset enable
+ 0, -1); // wrap settings
+
+ if (!ok) {
+ mp_raise_RuntimeError(translate("All state machines in use"));
+ }
+
+ pio_sm_set_enabled(self->state_machine.pio,self->state_machine.state_machine, false);
+ pio_sm_clear_fifos(self->state_machine.pio,self->state_machine.state_machine);
+ self->last_level = self->idle_state;
+ self->level_count = 0;
+ self->buf_index = 0;
+
+ pio_sm_set_in_pins(self->state_machine.pio,self->state_machine.state_machine,pin->number);
+ common_hal_rp2pio_statemachine_set_interrupt_handler(&(self->state_machine),&common_hal_pulseio_pulsein_interrupt,self,PIO_IRQ0_INTE_SM0_RXNEMPTY_BITS);
+
+ // exec a set pindirs to 0 for input
+ pio_sm_exec(self->state_machine.pio,self->state_machine.state_machine,0xe080);
+ // exec the appropriate wait for pin
+ if (self->idle_state == true) {
+ pio_sm_exec(self->state_machine.pio,self->state_machine.state_machine,0x2020);
+ } else {
+ pio_sm_exec(self->state_machine.pio,self->state_machine.state_machine,0x20a0);
+ }
+ pio_sm_set_enabled(self->state_machine.pio, self->state_machine.state_machine, true);
+}
+
+bool common_hal_pulseio_pulsein_deinited(pulseio_pulsein_obj_t *self) {
+ return self->pin == NO_PIN;
+}
+
+void common_hal_pulseio_pulsein_deinit(pulseio_pulsein_obj_t *self) {
+ if (common_hal_pulseio_pulsein_deinited(self)) {
+ return;
+ }
+ pio_sm_set_enabled(self->state_machine.pio, self->state_machine.state_machine, false);
+ common_hal_rp2pio_statemachine_deinit(&self->state_machine);
+ m_free(self->buffer);
+ reset_pin_number(self->pin);
+ self->pin = NO_PIN;
+}
+
+void common_hal_pulseio_pulsein_pause(pulseio_pulsein_obj_t *self) {
+ pio_sm_restart(self->state_machine.pio, self->state_machine.state_machine);
+ pio_sm_set_enabled(self->state_machine.pio, self->state_machine.state_machine, false);
+ self->last_level = self->idle_state;
+ self->level_count = 0;
+ self->buf_index = 0;
+}
+void common_hal_pulseio_pulsein_interrupt(void *self_in) {
+ pulseio_pulsein_obj_t *self = self_in;
+
+ uint32_t rxfifo = 0;
+
+ rxfifo = pio_sm_get_blocking(self->state_machine.pio, self->state_machine.state_machine);
+ // translate from fifo to buffer
+ if ((rxfifo == 0 && self->last_level == false) || (rxfifo == 0xffffffff && self->last_level == true)) {
+ self->level_count = self->level_count + 32;
+ } else {
+ for (uint i = 0; i < 32; i++) {
+ bool level = (rxfifo & (1 << i)) >> i;
+ if (level == self->last_level) {
+ self->level_count++;
+ } else {
+ uint32_t result = self->level_count;
+ self->last_level = level;
+ self->level_count = 0;
+ // Pulses that are longer than MAX_PULSE will return MAX_PULSE
+ if (result > MAX_PULSE) {
+ result = MAX_PULSE;
+ }
+ // return pulses that are not too short
+ if (result > MIN_PULSE) {
+ size_t buf_index = (self->start + self->len) % self->maxlen;
+ self->buffer[buf_index] = (uint16_t)result;
+ if (self->len < self->maxlen) {
+ self->len++;
+ } else {
+ self->start = (self->start + 1) % self->maxlen;
+ }
+ if (self->buf_index < self->maxlen) {
+ self->buf_index++;
+ } else {
+ self->start = 0;
+ self->buf_index = 0;
+ }
+ }
+ }
+ }
+ }
+
+// check for a pulse thats too long (MAX_PULSE us) or maxlen reached, and reset
+ if ((self->level_count > MAX_PULSE) || (self->buf_index >= self->maxlen)) {
+ pio_sm_set_enabled(self->state_machine.pio, self->state_machine.state_machine, false);
+ pio_sm_init(self->state_machine.pio, self->state_machine.state_machine, self->state_machine.offset, &self->state_machine.sm_config);
+ pio_sm_restart(self->state_machine.pio,self->state_machine.state_machine);
+ pio_sm_set_enabled(self->state_machine.pio, self->state_machine.state_machine, true);
+ self->buf_index = 0;
+ }
+}
+void common_hal_pulseio_pulsein_resume(pulseio_pulsein_obj_t *self,
+ uint16_t trigger_duration) {
+ // Send the trigger pulse.
+ if (trigger_duration > 0) {
+ gpio_set_function(self->pin,GPIO_FUNC_SIO);
+ gpio_set_dir(self->pin,true);
+ gpio_put(self->pin, !self->idle_state);
+ common_hal_mcu_delay_us((uint32_t)trigger_duration);
+ gpio_set_function(self->pin,GPIO_FUNC_PIO0);
+ common_hal_mcu_delay_us(125);
+ }
+
+ // Reconfigure the pin for PIO
+ gpio_set_function(self->pin, GPIO_FUNC_PIO0);
+ // exec a wait for the selected pin to change state
+ if (self->idle_state == true) {
+ pio_sm_exec(self->state_machine.pio,self->state_machine.state_machine,0x2020);
+ } else {
+ pio_sm_exec(self->state_machine.pio,self->state_machine.state_machine,0x20a0);
+ }
+ pio_sm_set_enabled(self->state_machine.pio, self->state_machine.state_machine, true);
+}
+
+void common_hal_pulseio_pulsein_clear(pulseio_pulsein_obj_t *self) {
+ self->start = 0;
+ self->len = 0;
+ self->buf_index = 0;
+}
+
+uint16_t common_hal_pulseio_pulsein_popleft(pulseio_pulsein_obj_t *self) {
+ if (self->len == 0) {
+ mp_raise_IndexError_varg(translate("pop from empty %q"), MP_QSTR_PulseIn);
+ }
+ uint16_t value = self->buffer[self->start];
+ self->start = (self->start + 1) % self->maxlen;
+ self->len--;
+ // if we are empty reset buffer pointer and counters
+ if (self->len == 0) {
+ self->start = 0;
+ self->buf_index = 0;
+ self->level_count = 0;
+ }
+ return value;
+}
+
+uint16_t common_hal_pulseio_pulsein_get_maxlen(pulseio_pulsein_obj_t *self) {
+ return self->maxlen;
+}
+
+uint16_t common_hal_pulseio_pulsein_get_len(pulseio_pulsein_obj_t *self) {
+ return self->len;
+}
+
+bool common_hal_pulseio_pulsein_get_paused(pulseio_pulsein_obj_t *self) {
+ return true;
+}
+
+uint16_t common_hal_pulseio_pulsein_get_item(pulseio_pulsein_obj_t *self,
+ int16_t index) {
+ if (index < 0) {
+ index += self->len;
+ }
+ if (index < 0 || index >= self->len) {
+ mp_raise_IndexError_varg(translate("%q index out of range"), MP_QSTR_PulseIn);
+ }
+ uint16_t value = self->buffer[(self->start + index) % self->maxlen];
+ return value;
+}
diff --git a/circuitpython/ports/raspberrypi/common-hal/pulseio/PulseIn.h b/circuitpython/ports/raspberrypi/common-hal/pulseio/PulseIn.h
new file mode 100644
index 0000000..f2c4fc0
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/pulseio/PulseIn.h
@@ -0,0 +1,53 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 Dave Putz for Adafruit Industries
+ *
+ * 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.
+ */
+
+#ifndef MICROPY_INCLUDED_ATMEL_SAMD_COMMON_HAL_PULSEIO_PULSEIN_H
+#define MICROPY_INCLUDED_ATMEL_SAMD_COMMON_HAL_PULSEIO_PULSEIN_H
+
+#include "common-hal/microcontroller/Pin.h"
+#include "src/rp2_common/hardware_pio/include/hardware/pio.h"
+#include "common-hal/rp2pio/StateMachine.h"
+
+#include "py/obj.h"
+
+typedef struct {
+ mp_obj_base_t base;
+ uint8_t pin;
+ bool idle_state;
+ uint16_t maxlen;
+ uint16_t *buffer;
+ volatile bool last_level;
+ volatile uint32_t level_count;
+ volatile uint16_t len;
+ volatile uint16_t start;
+ volatile uint16_t buf_index;
+ rp2pio_statemachine_obj_t state_machine;
+} pulseio_pulsein_obj_t;
+
+void pulsein_reset(void);
+void common_hal_pulseio_pulsein_interrupt(void *);
+
+#endif // MICROPY_INCLUDED_ATMEL_SAMD_COMMON_HAL_PULSEIO_PULSEIN_H
diff --git a/circuitpython/ports/raspberrypi/common-hal/pulseio/PulseOut.c b/circuitpython/ports/raspberrypi/common-hal/pulseio/PulseOut.c
new file mode 100644
index 0000000..7704fdf
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/pulseio/PulseOut.c
@@ -0,0 +1,134 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 Dave Putz for Adafruit Industries
+ *
+ * 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 "common-hal/pulseio/PulseOut.h"
+
+#include <stdint.h>
+#include "mpconfigport.h"
+#include "py/runtime.h"
+#include "shared-bindings/pulseio/PulseOut.h"
+#include "shared-bindings/pwmio/PWMOut.h"
+#include "shared-bindings/microcontroller/__init__.h"
+#include "common-hal/pwmio/PWMOut.h"
+#include "supervisor/shared/translate.h"
+#include "src/rp2040/hardware_structs/include/hardware/structs/pwm.h"
+#include "src/rp2_common/hardware_gpio/include/hardware/gpio.h"
+#include "src/rp2_common/hardware_pwm/include/hardware/pwm.h"
+#include "src/common/pico_time/include/pico/time.h"
+
+volatile alarm_id_t cur_alarm = 0;
+
+static void pulse_finish(pulseio_pulseout_obj_t *self) {
+ self->pulse_index++;
+ // Turn pwm pin off by switching the GPIO mux to SIO (the cpu manual
+ // control).
+ if (self->pulse_index >= self->pulse_length) {
+ gpio_set_function(self->pin, GPIO_FUNC_SIO);
+ return;
+ }
+ if (self->pulse_index % 2 == 0) {
+ gpio_set_function(self->pin, GPIO_FUNC_PWM);
+ } else {
+ gpio_set_function(self->pin, GPIO_FUNC_SIO);
+ }
+ uint64_t delay = self->pulse_buffer[self->pulse_index];
+ if (delay < self->min_pulse) {
+ delay = self->min_pulse;
+ }
+ cur_alarm = 0;
+ // if the alarm cannot be set, try again with a longer delay
+ while (cur_alarm == 0) {
+ cur_alarm = add_alarm_in_us(delay, pulseout_interrupt_handler, self, false);
+ delay = delay + 1;
+ }
+}
+
+int64_t pulseout_interrupt_handler(alarm_id_t id, void *user_data) {
+ pulse_finish(user_data);
+ return 0;
+}
+
+void pulseout_reset() {
+}
+
+void common_hal_pulseio_pulseout_construct(pulseio_pulseout_obj_t *self,
+ const mcu_pin_obj_t *pin,
+ uint32_t frequency,
+ uint16_t duty_cycle) {
+
+ pwmout_result_t result = common_hal_pwmio_pwmout_construct(
+ &self->carrier, pin, 0, frequency, false);
+ // This will raise an exception and not return if needed.
+ common_hal_pwmio_pwmout_raise_error(result);
+
+ // Disable gpio output before we set the duty cycle.
+ gpio_put(pin->number, false);
+ gpio_set_dir(pin->number, GPIO_OUT);
+ gpio_set_function(pin->number, GPIO_FUNC_SIO);
+ common_hal_pwmio_pwmout_set_duty_cycle(&self->carrier, duty_cycle);
+
+ self->pin = pin->number;
+ self->slice = self->carrier.slice;
+ self->min_pulse = (1000000 / self->carrier.actual_frequency);
+}
+
+bool common_hal_pulseio_pulseout_deinited(pulseio_pulseout_obj_t *self) {
+ return self->pin == NO_PIN;
+}
+
+void common_hal_pulseio_pulseout_deinit(pulseio_pulseout_obj_t *self) {
+ if (common_hal_pulseio_pulseout_deinited(self)) {
+ return;
+ }
+ gpio_set_dir(self->pin, GPIO_IN);
+ common_hal_pwmio_pwmout_deinit(&self->carrier);
+ self->pin = NO_PIN;
+}
+
+void common_hal_pulseio_pulseout_send(pulseio_pulseout_obj_t *self, uint16_t *pulses, uint16_t length) {
+ self->pulse_buffer = pulses;
+ self->pulse_index = 0;
+ self->pulse_length = length;
+
+ // Turn on the signal by connecting the PWM to the outside pin.
+ gpio_set_function(self->pin, GPIO_FUNC_PWM);
+ uint64_t delay = self->pulse_buffer[0];
+ if (delay < self->min_pulse) {
+ delay = self->min_pulse;
+ }
+ cur_alarm = 0;
+ // if the alarm cannot be set, try again with a longer delay
+ while (cur_alarm == 0) {
+ cur_alarm = add_alarm_in_us(delay, pulseout_interrupt_handler, self, false);
+ delay = delay + 1;
+ }
+
+ while (self->pulse_index < length) {
+ // Do other things while we wait. The interrupts will handle sending the
+ // signal.
+ RUN_BACKGROUND_TASKS;
+ }
+}
diff --git a/circuitpython/ports/raspberrypi/common-hal/pulseio/PulseOut.h b/circuitpython/ports/raspberrypi/common-hal/pulseio/PulseOut.h
new file mode 100644
index 0000000..3366946
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/pulseio/PulseOut.h
@@ -0,0 +1,52 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 Dave Putz for Adafruit Industries
+ *
+ * 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.
+ */
+
+#ifndef MICROPY_INCLUDED_ATMEL_SAMD_COMMON_HAL_PULSEIO_PULSEOUT_H
+#define MICROPY_INCLUDED_ATMEL_SAMD_COMMON_HAL_PULSEIO_PULSEOUT_H
+
+#include "common-hal/microcontroller/Pin.h"
+#include "common-hal/pwmio/PWMOut.h"
+#include "src/common/pico_time/include/pico/time.h"
+
+#include "py/obj.h"
+
+#define NO_PIN 0xff
+
+typedef struct {
+ mp_obj_base_t base;
+ uint8_t pin;
+ uint8_t slice;
+ pwmio_pwmout_obj_t carrier;
+ uint16_t *pulse_buffer;
+ uint16_t pulse_length;
+ uint32_t min_pulse;
+ volatile uint16_t pulse_index;
+} pulseio_pulseout_obj_t;
+
+void pulseout_reset(void);
+int64_t pulseout_interrupt_handler(alarm_id_t id, void *user_data);
+
+#endif // MICROPY_INCLUDED_ATMEL SAMD_COMMON_HAL_PULSEIO_PULSEOUT_H
diff --git a/circuitpython/ports/raspberrypi/common-hal/pulseio/__init__.c b/circuitpython/ports/raspberrypi/common-hal/pulseio/__init__.c
new file mode 100644
index 0000000..2bee925
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/pulseio/__init__.c
@@ -0,0 +1 @@
+// No pulseio module functions.
diff --git a/circuitpython/ports/raspberrypi/common-hal/pwmio/PWMOut.c b/circuitpython/ports/raspberrypi/common-hal/pwmio/PWMOut.c
new file mode 100644
index 0000000..3ef4fb5
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/pwmio/PWMOut.c
@@ -0,0 +1,307 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 Scott Shawcroft for Adafruit Industries
+ *
+ * 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 <stdint.h>
+
+#include "shared/runtime/interrupt_char.h"
+#include "py/runtime.h"
+#include "common-hal/pwmio/PWMOut.h"
+#include "shared-bindings/pwmio/PWMOut.h"
+#include "shared-bindings/microcontroller/Processor.h"
+
+#include "supervisor/shared/translate.h"
+
+#include "src/rp2040/hardware_regs/include/hardware/platform_defs.h"
+#include "src/rp2_common/hardware_clocks/include/hardware/clocks.h"
+#include "src/rp2_common/hardware_gpio/include/hardware/gpio.h"
+#include "src/rp2_common/hardware_pwm/include/hardware/pwm.h"
+
+uint32_t target_slice_frequencies[NUM_PWM_SLICES];
+uint32_t slice_variable_frequency;
+
+#define AB_CHANNELS_PER_SLICE 2
+static uint32_t channel_use;
+static uint32_t never_reset_channel;
+
+// Per the RP2040 datasheet:
+//
+// "A CC value of 0 will produce a 0% output, i.e. the output signal
+// is always low. A CC value of TOP + 1 (i.e. equal to the period, in
+// non-phase-correct mode) will produce a 100% output. For example, if
+// TOP is programmed to 254, the counter will have a period of 255
+// cycles, and CC values in the range of 0 to 255 inclusive will
+// produce duty cycles in the range 0% to 100% inclusive."
+//
+// So 65534 should be the maximum top value, and we'll set CC to be TOP+1 as appropriate.
+#define MAX_TOP 65534
+
+static uint32_t _mask(uint8_t slice, uint8_t ab_channel) {
+ return 1 << (slice * AB_CHANNELS_PER_SLICE + ab_channel);
+}
+
+bool pwmio_claim_slice_ab_channels(uint8_t slice) {
+ uint32_t channel_use_mask_a = _mask(slice, 0);
+ uint32_t channel_use_mask_b = _mask(slice, 1);
+
+ if ((channel_use & channel_use_mask_a) != 0) {
+ return false;
+ }
+ if ((channel_use & channel_use_mask_b) != 0) {
+ return false;
+ }
+
+ channel_use |= channel_use_mask_a;
+ channel_use |= channel_use_mask_b;
+ return true;
+}
+
+void pwmio_release_slice_ab_channels(uint8_t slice) {
+ uint32_t channel_mask = _mask(slice, 0);
+ channel_use &= ~channel_mask;
+ channel_mask = _mask(slice, 1);
+ channel_use &= ~channel_mask;
+}
+
+void pwmout_never_reset(uint8_t slice, uint8_t ab_channel) {
+ never_reset_channel |= _mask(slice, ab_channel);
+}
+
+void pwmout_reset_ok(uint8_t slice, uint8_t ab_channel) {
+ never_reset_channel &= ~_mask(slice, ab_channel);
+}
+
+void common_hal_pwmio_pwmout_never_reset(pwmio_pwmout_obj_t *self) {
+ pwmout_never_reset(self->slice, self->ab_channel);
+
+ never_reset_pin_number(self->pin->number);
+}
+
+void common_hal_pwmio_pwmout_reset_ok(pwmio_pwmout_obj_t *self) {
+ pwmout_reset_ok(self->slice, self->ab_channel);
+}
+
+void pwmout_reset(void) {
+ // Reset all slices
+ for (size_t slice = 0; slice < NUM_PWM_SLICES; slice++) {
+ bool reset = true;
+ for (size_t ab_channel = 0; ab_channel < AB_CHANNELS_PER_SLICE; ab_channel++) {
+ uint32_t channel_use_mask = _mask(slice, ab_channel);
+ if ((never_reset_channel & channel_use_mask) != 0) {
+ reset = false;
+ continue;
+ }
+ channel_use &= ~channel_use_mask;
+ }
+ if (!reset) {
+ continue;
+ }
+ pwm_set_enabled(slice, false);
+ target_slice_frequencies[slice] = 0;
+ slice_variable_frequency &= ~(1 << slice);
+ }
+}
+
+pwmout_result_t pwmout_allocate(uint8_t slice, uint8_t ab_channel, bool variable_frequency, uint32_t frequency) {
+ uint32_t channel_use_mask = _mask(slice, ab_channel);
+
+ // Check the channel first.
+ if ((channel_use & channel_use_mask) != 0) {
+ return PWMOUT_ALL_TIMERS_ON_PIN_IN_USE;
+ }
+ // Now check if the slice is in use and if we can share with it.
+ if (target_slice_frequencies[slice] > 0) {
+ // If we want to change frequency then we can't share.
+ if (variable_frequency) {
+ return PWMOUT_ALL_TIMERS_ON_PIN_IN_USE;
+ }
+ // If the other user wants a variable frequency then we can't share either.
+ if ((slice_variable_frequency & (1 << slice)) != 0) {
+ return PWMOUT_ALL_TIMERS_ON_PIN_IN_USE;
+ }
+ // If we're both fixed frequency but we don't match target frequencies then we can't share.
+ if (target_slice_frequencies[slice] != frequency) {
+ return PWMOUT_ALL_TIMERS_ON_PIN_IN_USE;
+ }
+ }
+
+ channel_use |= channel_use_mask;
+ if (variable_frequency) {
+ slice_variable_frequency |= 1 << slice;
+ }
+
+ return PWMOUT_OK;
+}
+
+pwmout_result_t common_hal_pwmio_pwmout_construct(pwmio_pwmout_obj_t *self,
+ const mcu_pin_obj_t *pin,
+ uint16_t duty,
+ uint32_t frequency,
+ bool variable_frequency) {
+ self->pin = pin;
+ self->variable_frequency = variable_frequency;
+ self->duty_cycle = duty;
+
+ claim_pin(pin);
+
+ if (frequency == 0 || frequency > (common_hal_mcu_processor_get_frequency() / 2)) {
+ return PWMOUT_INVALID_FREQUENCY;
+ }
+
+ uint8_t slice = pwm_gpio_to_slice_num(pin->number);
+ uint8_t ab_channel = pwm_gpio_to_channel(pin->number);
+
+ int r = pwmout_allocate(slice, ab_channel, variable_frequency, frequency);
+ if (r != PWMOUT_OK) {
+ return r;
+ }
+
+ self->slice = slice;
+ self->ab_channel = ab_channel;
+
+ if (target_slice_frequencies[slice] != frequency) {
+ // Reset the counter and compare values.
+ pwm_hw->slice[slice].ctr = PWM_CH0_CTR_RESET;
+ common_hal_pwmio_pwmout_set_duty_cycle(self, duty);
+ common_hal_pwmio_pwmout_set_frequency(self, frequency);
+ pwm_set_enabled(slice, true);
+ } else {
+ common_hal_pwmio_pwmout_set_frequency(self, frequency);
+ common_hal_pwmio_pwmout_set_duty_cycle(self, duty);
+ }
+
+ // Connect to the pad last to avoid any glitches from changing settings.
+ gpio_set_function(pin->number, GPIO_FUNC_PWM);
+
+ return PWMOUT_OK;
+}
+
+bool common_hal_pwmio_pwmout_deinited(pwmio_pwmout_obj_t *self) {
+ return self->pin == NULL;
+}
+
+void pwmout_free(uint8_t slice, uint8_t ab_channel) {
+ uint32_t channel_mask = _mask(slice, ab_channel);
+ channel_use &= ~channel_mask;
+ never_reset_channel &= ~channel_mask;
+ uint32_t slice_mask = ((1 << AB_CHANNELS_PER_SLICE) - 1) << (slice * AB_CHANNELS_PER_SLICE);
+ if ((channel_use & slice_mask) == 0) {
+ target_slice_frequencies[slice] = 0;
+ slice_variable_frequency &= ~(1 << slice);
+ pwm_set_enabled(slice, false);
+ }
+}
+
+void common_hal_pwmio_pwmout_deinit(pwmio_pwmout_obj_t *self) {
+ if (common_hal_pwmio_pwmout_deinited(self)) {
+ return;
+ }
+ pwmout_free(self->slice, self->ab_channel);
+ reset_pin_number(self->pin->number);
+ self->pin = NULL;
+}
+
+extern void common_hal_pwmio_pwmout_set_duty_cycle(pwmio_pwmout_obj_t *self, uint16_t duty) {
+ self->duty_cycle = duty;
+ // Do arithmetic in 32 bits to prevent overflow.
+ uint16_t compare_count;
+ if (duty == 65535) {
+ // Ensure that 100% duty cycle is 100% full on and not rounded down,
+ // but do MIN() to keep value in range, just in case.
+ compare_count = MIN(UINT16_MAX, (uint32_t)self->top + 1);
+ } else {
+ compare_count = ((uint32_t)duty * self->top + MAX_TOP / 2) / MAX_TOP;
+ }
+ // compare_count is the CC register value, which should be TOP+1 for 100% duty cycle.
+ pwm_set_chan_level(self->slice, self->ab_channel, compare_count);
+ // Wait for wrap so that we know our new cc value has been applied. Clear
+ // the internal interrupt and then wait for it to be set. Worst case, we
+ // wait a full cycle.
+ pwm_hw->intr = 1 << self->slice;
+ while ((pwm_hw->en & (1 << self->slice)) != 0 &&
+ (pwm_hw->intr & (1 << self->slice)) == 0 &&
+ !mp_hal_is_interrupted()) {
+ }
+}
+
+uint16_t common_hal_pwmio_pwmout_get_duty_cycle(pwmio_pwmout_obj_t *self) {
+ return self->duty_cycle;
+}
+
+void pwmio_pwmout_set_top(pwmio_pwmout_obj_t *self, uint16_t top) {
+ self->actual_frequency = common_hal_mcu_processor_get_frequency() / top;
+ self->top = top;
+ pwm_set_clkdiv_int_frac(self->slice, 1, 0);
+ pwm_set_wrap(self->slice, self->top);
+}
+
+void common_hal_pwmio_pwmout_set_frequency(pwmio_pwmout_obj_t *self, uint32_t frequency) {
+ if (frequency == 0 || frequency > (common_hal_mcu_processor_get_frequency() / 2)) {
+ mp_raise_ValueError(translate("Invalid PWM frequency"));
+ }
+
+ target_slice_frequencies[self->slice] = frequency;
+
+ // For low frequencies use the divider to give us full resolution duty_cycle.
+ if (frequency <= (common_hal_mcu_processor_get_frequency() / (1 << 16))) {
+ // Compute the divisor. It's an 8 bit integer and 4 bit fraction. Therefore,
+ // we compute everything * 16 for the fractional part.
+ // This is 1 << 12 because 4 bits are the * 16.
+ uint64_t frequency16 = ((uint64_t)clock_get_hz(clk_sys)) / (1 << 12);
+ uint64_t div16 = frequency16 / frequency;
+ // Round the divisor to try and get closest to the target frequency. We could
+ // also always round up and use TOP to get us closer. We may not need that though.
+ if (frequency16 % frequency >= frequency / 2) {
+ div16 += 1;
+ }
+ if (div16 >= (1 << 12)) {
+ div16 = (1 << 12) - 1;
+ }
+ self->actual_frequency = (frequency16 + (div16 / 2)) / div16;
+ self->top = MAX_TOP;
+ pwm_set_clkdiv_int_frac(self->slice, div16 / 16, div16 % 16);
+ pwm_set_wrap(self->slice, self->top);
+ } else {
+ uint32_t top = common_hal_mcu_processor_get_frequency() / frequency;
+ self->actual_frequency = common_hal_mcu_processor_get_frequency() / top;
+ self->top = MIN(MAX_TOP, top);
+ pwm_set_clkdiv_int_frac(self->slice, 1, 0);
+ // Set TOP register. For 100% duty cycle, CC must be set to TOP+1.
+ pwm_set_wrap(self->slice, self->top);
+ }
+ common_hal_pwmio_pwmout_set_duty_cycle(self, self->duty_cycle);
+}
+
+uint32_t common_hal_pwmio_pwmout_get_frequency(pwmio_pwmout_obj_t *self) {
+ return self->actual_frequency;
+}
+
+bool common_hal_pwmio_pwmout_get_variable_frequency(pwmio_pwmout_obj_t *self) {
+ return self->variable_frequency;
+}
+
+const mcu_pin_obj_t *common_hal_pwmio_pwmout_get_pin(pwmio_pwmout_obj_t *self) {
+ return self->pin;
+}
diff --git a/circuitpython/ports/raspberrypi/common-hal/pwmio/PWMOut.h b/circuitpython/ports/raspberrypi/common-hal/pwmio/PWMOut.h
new file mode 100644
index 0000000..0d17993
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/pwmio/PWMOut.h
@@ -0,0 +1,58 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 Scott Shawcroft for Adafruit Industries
+ *
+ * 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.
+ */
+
+#ifndef MICROPY_INCLUDED_RASPBERRY_PI_COMMON_HAL_PWMIO_PWMOUT_H
+#define MICROPY_INCLUDED_RASPBERRY_PI_COMMON_HAL_PWMIO_PWMOUT_H
+
+#include "common-hal/microcontroller/Pin.h"
+
+#include "py/obj.h"
+
+typedef struct {
+ mp_obj_base_t base;
+ const mcu_pin_obj_t *pin;
+ uint8_t slice; // 0-7
+ uint8_t ab_channel; // 0-1: A or B slice channel
+ bool variable_frequency;
+ uint16_t duty_cycle;
+ uint32_t actual_frequency;
+ uint16_t top;
+} pwmio_pwmout_obj_t;
+
+void pwmout_reset(void);
+// Private API for AudioPWMOut.
+void pwmio_pwmout_set_top(pwmio_pwmout_obj_t *self, uint16_t top);
+// Private APIs for RGBMatrix
+enum pwmout_result_t pwmout_allocate(uint8_t slice, uint8_t ab_channel, bool variable_frequency, uint32_t frequency);
+void pwmout_free(uint8_t slice, uint8_t ab_channel);
+void pwmout_never_reset(uint8_t slice, uint8_t ab_channel);
+void pwmout_reset_ok(uint8_t slice, uint8_t ab_channel);
+
+// Private API for countio to claim both ab_channels on a slice
+bool pwmio_claim_slice_ab_channels(uint8_t slice);
+void pwmio_release_slice_ab_channels(uint8_t slice);
+
+#endif // MICROPY_INCLUDED_RASPBERRY_PI_COMMON_HAL_PWMIO_PWMOUT_H
diff --git a/circuitpython/ports/raspberrypi/common-hal/pwmio/__init__.c b/circuitpython/ports/raspberrypi/common-hal/pwmio/__init__.c
new file mode 100644
index 0000000..9e551a1
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/pwmio/__init__.c
@@ -0,0 +1 @@
+// No pwmio module functions.
diff --git a/circuitpython/ports/raspberrypi/common-hal/rgbmatrix/RGBMatrix.c b/circuitpython/ports/raspberrypi/common-hal/rgbmatrix/RGBMatrix.c
new file mode 100644
index 0000000..d580b4c
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/rgbmatrix/RGBMatrix.c
@@ -0,0 +1,75 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Jeff Epler for Adafruit Industries
+ *
+ * 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 <stddef.h>
+
+#include "py/mphal.h"
+
+#include "common-hal/rgbmatrix/RGBMatrix.h"
+#include "shared-bindings/pwmio/PWMOut.h"
+#include "shared-module/rgbmatrix/RGBMatrix.h"
+
+#include "src/rp2_common/hardware_pwm/include/hardware/pwm.h"
+#include "src/rp2_common/hardware_irq/include/hardware/irq.h"
+
+void *common_hal_rgbmatrix_timer_allocate(rgbmatrix_rgbmatrix_obj_t *self) {
+ // Choose a PWM channel based on the first RGB pin
+ uint8_t slice = pwm_gpio_to_slice_num(self->rgb_pins[0]);
+ uint8_t channel = pwm_gpio_to_channel(self->rgb_pins[0]);
+ int result = pwmout_allocate(slice, channel, true, 125000000 / 3);
+ if (result == PWMOUT_OK) {
+ // return value must be nonzero (but slice and channel can both be
+ // zero), so set bit 16...
+ pwmout_never_reset(slice, channel);
+ return (void *)(intptr_t)(slice | (channel << 8) | 0x10000);
+ }
+ return NULL;
+}
+
+void common_hal_rgbmatrix_timer_enable(void *ptr) {
+ int8_t slice = ((intptr_t)ptr) & 0xff;
+ pwm_set_enabled(slice, false);
+ pwm_clear_irq(slice);
+ pwm_set_enabled(slice, true);
+}
+
+void common_hal_rgbmatrix_timer_disable(void *ptr) {
+ int8_t slice = ((intptr_t)ptr) & 0xff;
+ pwm_set_enabled(slice, false);
+ irq_set_enabled(PWM_IRQ_WRAP, false);
+ pwm_clear_irq(slice);
+}
+
+void common_hal_rgbmatrix_timer_free(void *ptr) {
+ intptr_t value = (intptr_t)ptr;
+ uint8_t slice = value & 0xff;
+ uint8_t channel = value >> 8;
+ pwm_set_enabled(slice, false);
+ irq_set_enabled(PWM_IRQ_WRAP, false);
+ pwm_clear_irq(slice);
+ pwmout_free(slice, channel);
+ return;
+}
diff --git a/circuitpython/ports/raspberrypi/common-hal/rgbmatrix/RGBMatrix.h b/circuitpython/ports/raspberrypi/common-hal/rgbmatrix/RGBMatrix.h
new file mode 100644
index 0000000..d14cd9b
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/rgbmatrix/RGBMatrix.h
@@ -0,0 +1,37 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Jeff Epler for Adafruit Industries
+ *
+ * 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.
+ */
+
+#ifndef MICROPY_INCLUDED_ATMEL_SAMD_COMMON_HAL_RGBMATRIX_RGBMATRIX_H
+#define MICROPY_INCLUDED_ATMEL_SAMD_COMMON_HAL_RGBMATRIX_RGBMATRIX_H
+
+#include "shared-module/rgbmatrix/RGBMatrix.h"
+
+void *common_hal_rgbmatrix_timer_allocate(rgbmatrix_rgbmatrix_obj_t *self);
+void common_hal_rgbmatrix_timer_enable(void *);
+void common_hal_rgbmatrix_timer_disable(void *);
+void common_hal_rgbmatrix_timer_free(void *);
+
+#endif
diff --git a/circuitpython/ports/raspberrypi/common-hal/rgbmatrix/__init__.c b/circuitpython/ports/raspberrypi/common-hal/rgbmatrix/__init__.c
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/rgbmatrix/__init__.c
diff --git a/circuitpython/ports/raspberrypi/common-hal/rotaryio/IncrementalEncoder.c b/circuitpython/ports/raspberrypi/common-hal/rotaryio/IncrementalEncoder.c
new file mode 100644
index 0000000..0da12e5
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/rotaryio/IncrementalEncoder.c
@@ -0,0 +1,135 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Scott Shawcroft for Adafruit Industries
+ *
+ * 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 <hardware/regs/pio.h>
+#include "common-hal/rotaryio/IncrementalEncoder.h"
+#include "shared-bindings/rotaryio/IncrementalEncoder.h"
+#include "shared-module/rotaryio/IncrementalEncoder.h"
+#include "bindings/rp2pio/__init__.h"
+#include "bindings/rp2pio/StateMachine.h"
+
+STATIC const uint16_t encoder[] = {
+ // again:
+ // in pins, 2
+ 0x4002,
+ // mov x, isr
+ 0xa026,
+ // jmp x!=y, push_data
+ 0x00a5,
+ // mov isr, null
+ 0xa0c3,
+ // jmp again
+ 0x0000,
+ // push_data:
+ // push
+ 0x8020,
+ // mov y, x
+ 0xa041,
+};
+
+STATIC const uint16_t encoder_init[] = {
+ // set y, 31
+ 0xe05f,
+};
+
+STATIC void incrementalencoder_interrupt_handler(void *self_in);
+
+void common_hal_rotaryio_incrementalencoder_construct(rotaryio_incrementalencoder_obj_t *self,
+ const mcu_pin_obj_t *pin_a, const mcu_pin_obj_t *pin_b) {
+ mp_obj_t pins[] = {MP_OBJ_FROM_PTR(pin_a), MP_OBJ_FROM_PTR(pin_b)};
+ // Start out with swapped to match behavior with other ports.
+ self->swapped = true;
+ if (!common_hal_rp2pio_pins_are_sequential(2, pins)) {
+ pins[0] = MP_OBJ_FROM_PTR(pin_b);
+ pins[1] = MP_OBJ_FROM_PTR(pin_a);
+ self->swapped = false;
+ if (!common_hal_rp2pio_pins_are_sequential(2, pins)) {
+ mp_raise_RuntimeError(translate("Pins must be sequential GPIO pins"));
+ }
+ }
+
+ self->position = 0;
+ self->sub_count = 0;
+
+ common_hal_rp2pio_statemachine_construct(&self->state_machine,
+ encoder, MP_ARRAY_SIZE(encoder),
+ 1000000,
+ encoder_init, MP_ARRAY_SIZE(encoder_init), // init
+ NULL, 0, 0, 0, // out pin
+ pins[0], 2, // in pins
+ 3, 0, // in pulls
+ NULL, 0, 0, 0x1f, // set pins
+ NULL, 0, 0, 0x1f, // sideset pins
+ false, // No sideset enable
+ NULL, PULL_NONE, // jump pin
+ 0, // wait gpio pins
+ true, // exclusive pin use
+ false, 32, false, // out settings
+ false, // Wait for txstall
+ false, 32, false, // in settings
+ false, // Not user-interruptible.
+ 0, MP_ARRAY_SIZE(encoder) - 1 // wrap settings
+ );
+
+ // We're guaranteed by the init code that some output will be available promptly
+ uint8_t quiescent_state;
+ common_hal_rp2pio_statemachine_readinto(&self->state_machine, &quiescent_state, 1, 1, false);
+
+ shared_module_softencoder_state_init(self, quiescent_state & 3);
+ common_hal_rp2pio_statemachine_set_interrupt_handler(&self->state_machine, incrementalencoder_interrupt_handler, self, PIO_IRQ0_INTF_SM0_RXNEMPTY_BITS);
+}
+
+bool common_hal_rotaryio_incrementalencoder_deinited(rotaryio_incrementalencoder_obj_t *self) {
+ return common_hal_rp2pio_statemachine_deinited(&self->state_machine);
+}
+
+void common_hal_rotaryio_incrementalencoder_deinit(rotaryio_incrementalencoder_obj_t *self) {
+ if (common_hal_rotaryio_incrementalencoder_deinited(self)) {
+ return;
+ }
+ common_hal_rp2pio_statemachine_set_interrupt_handler(&self->state_machine, NULL, NULL, 0);
+ common_hal_rp2pio_statemachine_deinit(&self->state_machine);
+}
+
+STATIC void incrementalencoder_interrupt_handler(void *self_in) {
+ rotaryio_incrementalencoder_obj_t *self = self_in;
+
+ while (common_hal_rp2pio_statemachine_get_in_waiting(&self->state_machine)) {
+ // Bypass all the logic of StateMachine.c:_transfer, we need something
+ // very simple and fast for an interrupt!
+ uint8_t new_state = self->state_machine.pio->rxf[self->state_machine.state_machine];
+ if (self->swapped) {
+ if (new_state == 0x1) {
+ new_state = 0x2;
+ } else if (new_state == 0x2) {
+ new_state = 0x1;
+ }
+ }
+ shared_module_softencoder_state_update(self, new_state);
+ }
+}
diff --git a/circuitpython/ports/raspberrypi/common-hal/rotaryio/IncrementalEncoder.h b/circuitpython/ports/raspberrypi/common-hal/rotaryio/IncrementalEncoder.h
new file mode 100644
index 0000000..812ba09
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/rotaryio/IncrementalEncoder.h
@@ -0,0 +1,42 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Scott Shawcroft for Adafruit Industries
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include "common-hal/rp2pio/StateMachine.h"
+#include "common-hal/microcontroller/Pin.h"
+
+#include "py/obj.h"
+
+typedef struct {
+ mp_obj_base_t base;
+ rp2pio_statemachine_obj_t state_machine;
+ uint8_t state; // <old A><old B>
+ int8_t sub_count; // count intermediate transitions between detents
+ int8_t divisor; // Number of quadrature edges required per count
+ bool swapped; // Did the pins need to be swapped to be sequential?
+ mp_int_t position;
+} rotaryio_incrementalencoder_obj_t;
diff --git a/circuitpython/ports/raspberrypi/common-hal/rotaryio/__init__.c b/circuitpython/ports/raspberrypi/common-hal/rotaryio/__init__.c
new file mode 100644
index 0000000..0aae79c
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/rotaryio/__init__.c
@@ -0,0 +1 @@
+// No rotaryio module functions.
diff --git a/circuitpython/ports/raspberrypi/common-hal/rotaryio/__init__.h b/circuitpython/ports/raspberrypi/common-hal/rotaryio/__init__.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/rotaryio/__init__.h
diff --git a/circuitpython/ports/raspberrypi/common-hal/rp2pio/StateMachine.c b/circuitpython/ports/raspberrypi/common-hal/rp2pio/StateMachine.c
new file mode 100644
index 0000000..bee7723
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/rp2pio/StateMachine.c
@@ -0,0 +1,1034 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 Scott Shawcroft for Adafruit Industries
+ *
+ * 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 <string.h>
+
+#include "bindings/rp2pio/StateMachine.h"
+
+#include "common-hal/microcontroller/__init__.h"
+#include "shared-bindings/digitalio/Pull.h"
+#include "shared-bindings/microcontroller/__init__.h"
+#include "shared-bindings/microcontroller/Pin.h"
+
+#include "src/rp2040/hardware_regs/include/hardware/platform_defs.h"
+#include "src/rp2_common/hardware_clocks/include/hardware/clocks.h"
+#include "src/rp2_common/hardware_dma/include/hardware/dma.h"
+#include "src/rp2_common/hardware_pio/include/hardware/pio_instructions.h"
+#include "src/rp2040/hardware_structs/include/hardware/structs/iobank0.h"
+#include "src/rp2_common/hardware_irq/include/hardware/irq.h"
+
+#include "shared/runtime/interrupt_char.h"
+#include "py/obj.h"
+#include "py/objproperty.h"
+#include "py/runtime.h"
+
+#define NO_DMA_CHANNEL (-1)
+
+// Count how many state machines are using each pin.
+STATIC uint8_t _pin_reference_count[TOTAL_GPIO_COUNT];
+STATIC uint32_t _current_program_id[NUM_PIOS][NUM_PIO_STATE_MACHINES];
+STATIC uint8_t _current_program_offset[NUM_PIOS][NUM_PIO_STATE_MACHINES];
+STATIC uint8_t _current_program_len[NUM_PIOS][NUM_PIO_STATE_MACHINES];
+STATIC bool _never_reset[NUM_PIOS][NUM_PIO_STATE_MACHINES];
+
+STATIC uint32_t _current_pins[NUM_PIOS];
+STATIC uint32_t _current_sm_pins[NUM_PIOS][NUM_PIO_STATE_MACHINES];
+STATIC int8_t _sm_dma_plus_one[NUM_PIOS][NUM_PIO_STATE_MACHINES];
+
+#define SM_DMA_ALLOCATED(pio_index, sm) (_sm_dma_plus_one[(pio_index)][(sm)] != 0)
+#define SM_DMA_GET_CHANNEL(pio_index, sm) (_sm_dma_plus_one[(pio_index)][(sm)] - 1)
+#define SM_DMA_CLEAR_CHANNEL(pio_index, sm) (_sm_dma_plus_one[(pio_index)][(sm)] = 0)
+#define SM_DMA_SET_CHANNEL(pio_isntance, sm, channel) (_sm_dma_plus_one[(pio_index)][(sm)] = (channel) + 1)
+
+STATIC PIO pio_instances[2] = {pio0, pio1};
+typedef void (*interrupt_handler_type)(void *);
+STATIC interrupt_handler_type _interrupt_handler[NUM_PIOS][NUM_PIO_STATE_MACHINES];
+STATIC void *_interrupt_arg[NUM_PIOS][NUM_PIO_STATE_MACHINES];
+
+STATIC void rp2pio_statemachine_interrupt_handler(void);
+
+static void rp2pio_statemachine_set_pull(uint32_t pull_pin_up, uint32_t pull_pin_down, uint32_t pins_we_use) {
+ for (int i = 0; i < TOTAL_GPIO_COUNT; i++) {
+ bool used = pins_we_use & (1 << i);
+ if (used) {
+ bool pull_up = pull_pin_up & (1 << i);
+ bool pull_down = pull_pin_down & (1 << i);
+ gpio_set_pulls(i, pull_up, pull_down);
+ }
+ }
+}
+
+STATIC void rp2pio_statemachine_clear_dma(int pio_index, int sm) {
+ if (SM_DMA_ALLOCATED(pio_index, sm)) {
+ int channel = SM_DMA_GET_CHANNEL(pio_index, sm);
+ uint32_t channel_mask = 1u << channel;
+ dma_hw->inte0 &= ~channel_mask;
+ if (!dma_hw->inte0) {
+ irq_set_mask_enabled(1 << DMA_IRQ_0, false);
+ }
+ MP_STATE_PORT(background_pio)[channel] = NULL;
+ dma_channel_abort(channel);
+ dma_channel_unclaim(channel);
+ }
+ SM_DMA_CLEAR_CHANNEL(pio_index, sm);
+}
+
+STATIC void _reset_statemachine(PIO pio, uint8_t sm, bool leave_pins) {
+ uint8_t pio_index = pio_get_index(pio);
+ rp2pio_statemachine_clear_dma(pio_index, sm);
+ uint32_t program_id = _current_program_id[pio_index][sm];
+ if (program_id == 0) {
+ return;
+ }
+ _current_program_id[pio_index][sm] = 0;
+ bool program_in_use = false;
+ for (size_t i = 0; i < NUM_PIO_STATE_MACHINES; i++) {
+ if (_current_program_id[pio_index][i] == program_id) {
+ program_in_use = true;
+ break;
+ }
+ }
+ if (!program_in_use) {
+ uint8_t offset = _current_program_offset[pio_index][sm];
+ pio_program_t program_struct = {
+ .length = _current_program_len[pio_index][sm]
+ };
+ pio_remove_program(pio, &program_struct, offset);
+ }
+
+ uint32_t pins = _current_sm_pins[pio_index][sm];
+ for (size_t pin_number = 0; pin_number < TOTAL_GPIO_COUNT; pin_number++) {
+ if ((pins & (1 << pin_number)) == 0) {
+ continue;
+ }
+ _pin_reference_count[pin_number]--;
+ if (_pin_reference_count[pin_number] == 0) {
+ if (!leave_pins) {
+ reset_pin_number(pin_number);
+ }
+ _current_pins[pio_index] &= ~(1 << pin_number);
+ }
+ }
+ _current_sm_pins[pio_index][sm] = 0;
+ pio->inte0 &= ~((PIO_IRQ0_INTF_SM0_RXNEMPTY_BITS | PIO_IRQ0_INTF_SM0_TXNFULL_BITS | PIO_IRQ0_INTF_SM0_BITS) << sm);
+ pio_sm_unclaim(pio, sm);
+}
+
+void reset_rp2pio_statemachine(void) {
+ for (size_t i = 0; i < NUM_PIOS; i++) {
+ PIO pio = pio_instances[i];
+ for (size_t j = 0; j < NUM_PIO_STATE_MACHINES; j++) {
+ if (_never_reset[i][j]) {
+ continue;
+ }
+ _reset_statemachine(pio, j, false);
+ }
+ }
+ for (uint8_t irq = PIO0_IRQ_0; irq <= PIO1_IRQ_1; irq++) {
+ irq_handler_t int_handler = irq_get_exclusive_handler(irq);
+ if (int_handler > 0) {
+ irq_set_enabled(irq, false);
+ irq_remove_handler(irq,int_handler);
+ }
+ }
+}
+
+STATIC uint32_t _check_pins_free(const mcu_pin_obj_t *first_pin, uint8_t pin_count, bool exclusive_pin_use) {
+ uint32_t pins_we_use = 0;
+ if (first_pin != NULL) {
+ for (size_t i = 0; i < pin_count; i++) {
+ uint8_t pin_number = first_pin->number + i;
+ if (pin_number >= TOTAL_GPIO_COUNT) {
+ mp_raise_ValueError(translate("Pin count too large"));
+ }
+ const mcu_pin_obj_t *pin = mcu_pin_global_dict_table[pin_number].value;
+ if (exclusive_pin_use || _pin_reference_count[pin_number] == 0) {
+ assert_pin_free(pin);
+ }
+ pins_we_use |= 1 << pin_number;
+ }
+ }
+ return pins_we_use;
+}
+
+
+bool rp2pio_statemachine_construct(rp2pio_statemachine_obj_t *self,
+ const uint16_t *program, size_t program_len,
+ size_t frequency,
+ const uint16_t *init, size_t init_len,
+ const mcu_pin_obj_t *first_out_pin, uint8_t out_pin_count,
+ const mcu_pin_obj_t *first_in_pin, uint8_t in_pin_count,
+ uint32_t pull_pin_up, uint32_t pull_pin_down,
+ const mcu_pin_obj_t *first_set_pin, uint8_t set_pin_count,
+ const mcu_pin_obj_t *first_sideset_pin, uint8_t sideset_pin_count,
+ uint32_t initial_pin_state, uint32_t initial_pin_direction,
+ const mcu_pin_obj_t *jmp_pin,
+ uint32_t pins_we_use, bool tx_fifo, bool rx_fifo,
+ bool auto_pull, uint8_t pull_threshold, bool out_shift_right,
+ bool wait_for_txstall,
+ bool auto_push, uint8_t push_threshold, bool in_shift_right,
+ bool claim_pins,
+ bool user_interruptible,
+ bool sideset_enable,
+ int wrap_target, int wrap
+ ) {
+ // Create a program id that isn't the pointer so we can store it without storing the original object.
+ uint32_t program_id = ~((uint32_t)program);
+
+ // Next, find a PIO and state machine to use.
+ size_t pio_index = NUM_PIOS;
+ uint8_t program_offset = 32;
+ pio_program_t program_struct = {
+ .instructions = (uint16_t *)program,
+ .length = program_len,
+ .origin = -1
+ };
+ for (size_t i = 0; i < NUM_PIOS; i++) {
+ PIO pio = pio_instances[i];
+ uint8_t free_count = 0;
+ for (size_t j = 0; j < NUM_PIO_STATE_MACHINES; j++) {
+ if (_current_program_id[i][j] == program_id &&
+ _current_program_len[i][j] == program_len) {
+ program_offset = _current_program_offset[i][j];
+ }
+ int temp_claim = pio_claim_unused_sm(pio, false);
+ if (temp_claim >= 0) {
+ pio_sm_unclaim(pio, temp_claim);
+ free_count++;
+ }
+ }
+ if (free_count > 0 && (program_offset < 32 || pio_can_add_program(pio, &program_struct))) {
+ pio_index = i;
+ if (program_offset < 32) {
+ break;
+ }
+ }
+ // Reset program offset if we weren't able to find a free state machine
+ // on that PIO. (We would have broken the loop otherwise.)
+ program_offset = 32;
+ }
+
+ int state_machine = -1;
+ if (pio_index < NUM_PIOS) {
+ PIO pio = pio_instances[pio_index];
+ for (size_t i = 0; i < NUM_PIOS; i++) {
+ if (i == pio_index) {
+ continue;
+ }
+ if ((_current_pins[i] & pins_we_use) != 0) {
+ // Pin in use by another PIO already.
+ return false;
+ }
+ }
+ state_machine = pio_claim_unused_sm(pio, false);
+ }
+ if (pio_index == NUM_PIOS || state_machine < 0 || state_machine >= NUM_PIO_STATE_MACHINES) {
+ return false;
+ }
+
+ self->pio = pio_instances[pio_index];
+ self->state_machine = state_machine;
+ if (program_offset == 32) {
+ program_offset = pio_add_program(self->pio, &program_struct);
+ }
+ self->offset = program_offset;
+ _current_program_id[pio_index][state_machine] = program_id;
+ _current_program_len[pio_index][state_machine] = program_len;
+ _current_program_offset[pio_index][state_machine] = program_offset;
+ _current_sm_pins[pio_index][state_machine] = pins_we_use;
+ _current_pins[pio_index] |= pins_we_use;
+
+ pio_sm_set_pins_with_mask(self->pio, state_machine, initial_pin_state, pins_we_use);
+ pio_sm_set_pindirs_with_mask(self->pio, state_machine, initial_pin_direction, pins_we_use);
+ rp2pio_statemachine_set_pull(pull_pin_up, pull_pin_down, pins_we_use);
+ self->initial_pin_state = initial_pin_state;
+ self->initial_pin_direction = initial_pin_direction;
+ self->pull_pin_up = pull_pin_up;
+ self->pull_pin_down = pull_pin_down;
+
+ for (size_t pin_number = 0; pin_number < TOTAL_GPIO_COUNT; pin_number++) {
+ if ((pins_we_use & (1 << pin_number)) == 0) {
+ continue;
+ }
+ _pin_reference_count[pin_number]++;
+ const mcu_pin_obj_t *pin = mcu_pin_global_dict_table[pin_number].value;
+ // Also claim the pin at the top level when we're the first to grab it.
+ if (_pin_reference_count[pin_number] == 1) {
+ if (claim_pins) {
+ claim_pin(pin);
+ }
+ pio_gpio_init(self->pio, pin_number);
+ }
+ }
+
+ pio_sm_config c = {0, 0, 0};
+
+ if (frequency == 0) {
+ frequency = clock_get_hz(clk_sys);
+ }
+ uint64_t frequency256 = ((uint64_t)clock_get_hz(clk_sys)) * 256;
+ uint64_t div256 = frequency256 / frequency;
+ if (frequency256 % div256 > 0) {
+ div256 += 1;
+ }
+ self->actual_frequency = frequency256 / div256;
+ sm_config_set_clkdiv_int_frac(&c, div256 / 256, div256 % 256);
+
+ if (first_out_pin != NULL) {
+ sm_config_set_out_pins(&c, first_out_pin->number, out_pin_count);
+ }
+ if (first_in_pin != NULL) {
+ sm_config_set_in_pins(&c, first_in_pin->number);
+ }
+ if (first_set_pin != NULL) {
+ sm_config_set_set_pins(&c, first_set_pin->number, set_pin_count);
+ }
+ if (first_sideset_pin != NULL) {
+ size_t total_sideset_bits = sideset_pin_count;
+ if (sideset_enable) {
+ total_sideset_bits += 1;
+ }
+ sm_config_set_sideset(&c, total_sideset_bits, sideset_enable, false /* pin direction */);
+ sm_config_set_sideset_pins(&c, first_sideset_pin->number);
+ }
+ if (jmp_pin != NULL) {
+ sm_config_set_jmp_pin(&c, jmp_pin->number);
+ }
+
+ mp_arg_validate_int_range(wrap, -1, program_len - 1, MP_QSTR_wrap);
+ if (wrap == -1) {
+ wrap = program_len - 1;
+ }
+
+ mp_arg_validate_int_range(wrap_target, 0, program_len - 1, MP_QSTR_wrap_target);
+
+ wrap += program_offset;
+ wrap_target += program_offset;
+
+ sm_config_set_wrap(&c, wrap_target, wrap);
+ sm_config_set_in_shift(&c, in_shift_right, auto_push, push_threshold);
+ sm_config_set_out_shift(&c, out_shift_right, auto_pull, pull_threshold);
+
+ enum pio_fifo_join join = PIO_FIFO_JOIN_NONE;
+ if (!rx_fifo) {
+ join = PIO_FIFO_JOIN_TX;
+ } else if (!tx_fifo) {
+ join = PIO_FIFO_JOIN_RX;
+ }
+ if (rx_fifo) {
+ self->rx_dreq = pio_get_dreq(self->pio, self->state_machine, false);
+ }
+ if (tx_fifo) {
+ self->tx_dreq = pio_get_dreq(self->pio, self->state_machine, true);
+ }
+ self->in = rx_fifo;
+ self->out = tx_fifo;
+ self->out_shift_right = out_shift_right;
+ self->in_shift_right = in_shift_right;
+ self->wait_for_txstall = wait_for_txstall;
+ self->user_interruptible = user_interruptible;
+
+ self->init = init;
+ self->init_len = init_len;
+
+ sm_config_set_fifo_join(&c, join);
+ self->sm_config = c;
+
+ // no DMA allocated
+ SM_DMA_CLEAR_CHANNEL(pio_index, state_machine);
+
+ pio_sm_init(self->pio, self->state_machine, program_offset, &c);
+ common_hal_rp2pio_statemachine_run(self, init, init_len);
+
+ common_hal_rp2pio_statemachine_set_frequency(self, frequency);
+ pio_sm_set_enabled(self->pio, self->state_machine, true);
+ return true;
+}
+
+static uint32_t mask_and_rotate(const mcu_pin_obj_t *first_pin, uint32_t bit_count, uint32_t value) {
+ value = value & ((1 << bit_count) - 1);
+ uint32_t shift = first_pin->number;
+ return value << shift | value >> (32 - shift);
+}
+
+void common_hal_rp2pio_statemachine_construct(rp2pio_statemachine_obj_t *self,
+ const uint16_t *program, size_t program_len,
+ size_t frequency,
+ const uint16_t *init, size_t init_len,
+ const mcu_pin_obj_t *first_out_pin, uint8_t out_pin_count, uint32_t initial_out_pin_state, uint32_t initial_out_pin_direction,
+ const mcu_pin_obj_t *first_in_pin, uint8_t in_pin_count,
+ uint32_t pull_pin_up, uint32_t pull_pin_down,
+ const mcu_pin_obj_t *first_set_pin, uint8_t set_pin_count, uint32_t initial_set_pin_state, uint32_t initial_set_pin_direction,
+ const mcu_pin_obj_t *first_sideset_pin, uint8_t sideset_pin_count, uint32_t initial_sideset_pin_state, uint32_t initial_sideset_pin_direction,
+ bool sideset_enable,
+ const mcu_pin_obj_t *jmp_pin, digitalio_pull_t jmp_pull,
+ uint32_t wait_gpio_mask,
+ bool exclusive_pin_use,
+ bool auto_pull, uint8_t pull_threshold, bool out_shift_right,
+ bool wait_for_txstall,
+ bool auto_push, uint8_t push_threshold, bool in_shift_right,
+ bool user_interruptible,
+ int wrap_target, int wrap) {
+
+ // First, check that all pins are free OR already in use by any PIO if exclusive_pin_use is false.
+ uint32_t pins_we_use = wait_gpio_mask;
+ pins_we_use |= _check_pins_free(first_out_pin, out_pin_count, exclusive_pin_use);
+ pins_we_use |= _check_pins_free(first_in_pin, in_pin_count, exclusive_pin_use);
+ pins_we_use |= _check_pins_free(first_set_pin, set_pin_count, exclusive_pin_use);
+ pins_we_use |= _check_pins_free(first_sideset_pin, sideset_pin_count, exclusive_pin_use);
+ pins_we_use |= _check_pins_free(jmp_pin, 1, exclusive_pin_use);
+
+ // Look through the program to see what we reference and make sure it was provided.
+ bool tx_fifo = false;
+ bool rx_fifo = false;
+ bool in_loaded = false; // can be loaded in other ways besides the fifo
+ bool out_loaded = false;
+ bool in_used = false;
+ bool out_used = false;
+ for (size_t i = 0; i < program_len; i++) {
+ uint16_t full_instruction = program[i];
+ uint16_t instruction = full_instruction & 0xe000;
+ if (instruction == 0x8000) {
+ if ((full_instruction & 0xe080) == pio_instr_bits_push) {
+ rx_fifo = true;
+ in_loaded = true;
+ } else { // pull otherwise.
+ tx_fifo = true;
+ out_loaded = true;
+ }
+ }
+ if (instruction == pio_instr_bits_jmp) {
+ uint16_t condition = (full_instruction & 0x00e0) >> 5;
+ if ((condition == 0x6) && (jmp_pin == NULL)) {
+ mp_raise_ValueError_varg(translate("Missing jmp_pin. Instruction %d jumps on pin"), i);
+ }
+ }
+ if (instruction == pio_instr_bits_wait) {
+ uint16_t wait_source = (full_instruction & 0x0060) >> 5;
+ uint16_t wait_index = full_instruction & 0x001f;
+ if (wait_source == 0 && (pins_we_use & (1 << wait_index)) == 0) { // GPIO
+ mp_raise_ValueError_varg(translate("Instruction %d uses extra pin"), i);
+ }
+ if (wait_source == 1) { // Input pin
+ if (first_in_pin == NULL) {
+ mp_raise_ValueError_varg(translate("Missing first_in_pin. Instruction %d waits based on pin"), i);
+ }
+ if (wait_index > in_pin_count) {
+ mp_raise_ValueError_varg(translate("Instruction %d waits on input outside of count"), i);
+ }
+ }
+ }
+ if (instruction == pio_instr_bits_in) {
+ uint16_t source = (full_instruction & 0x00e0) >> 5;
+ uint16_t bit_count = full_instruction & 0x001f;
+ if (source == 0) {
+ if (first_in_pin == NULL) {
+ mp_raise_ValueError_varg(translate("Missing first_in_pin. Instruction %d shifts in from pin(s)"), i);
+ }
+ if (bit_count > in_pin_count) {
+ mp_raise_ValueError_varg(translate("Instruction %d shifts in more bits than pin count"), i);
+ }
+ }
+ if (auto_push) {
+ in_loaded = true;
+ rx_fifo = true;
+ }
+ in_used = true;
+ }
+ if (instruction == pio_instr_bits_out) {
+ uint16_t bit_count = full_instruction & 0x001f;
+ uint16_t destination = (full_instruction & 0x00e0) >> 5;
+ // Check for pins or pindirs destination.
+ if (destination == 0x0 || destination == 0x4) {
+ if (first_out_pin == NULL) {
+ mp_raise_ValueError_varg(translate("Missing first_out_pin. Instruction %d shifts out to pin(s)"), i);
+ }
+ if (bit_count > out_pin_count) {
+ mp_raise_ValueError_varg(translate("Instruction %d shifts out more bits than pin count"), i);
+ }
+ }
+ if (auto_pull) {
+ out_loaded = true;
+ tx_fifo = true;
+ }
+ out_used = true;
+ }
+ if (instruction == pio_instr_bits_set) {
+ uint16_t destination = (full_instruction & 0x00e0) >> 5;
+ // Check for pins or pindirs destination.
+ if ((destination == 0x00 || destination == 0x4) && first_set_pin == NULL) {
+ mp_raise_ValueError_varg(translate("Missing first_set_pin. Instruction %d sets pin(s)"), i);
+ }
+ }
+ if (instruction == pio_instr_bits_mov) {
+ uint16_t source = full_instruction & 0x0007;
+ uint16_t destination = (full_instruction & 0x00e0) >> 5;
+ // Check for pins or pindirs destination.
+ if (destination == 0x0 && first_out_pin == NULL) {
+ mp_raise_ValueError_varg(translate("Missing first_out_pin. Instruction %d writes pin(s)"), i);
+ }
+ if (source == 0x0 && first_in_pin == NULL) {
+ mp_raise_ValueError_varg(translate("Missing first_in_pin. Instruction %d reads pin(s)"), i);
+ }
+ if (destination == 0x6) {
+ in_loaded = true;
+ } else if (destination == 0x7) {
+ out_loaded = true;
+ }
+ }
+ }
+
+ if (!in_loaded && in_used) {
+ mp_raise_ValueError_varg(translate("Program does IN without loading ISR"));
+ }
+ if (!out_loaded && out_used) {
+ mp_raise_ValueError_varg(translate("Program does OUT without loading OSR"));
+ }
+
+ uint32_t initial_pin_state = mask_and_rotate(first_out_pin, out_pin_count, initial_out_pin_state);
+ uint32_t initial_pin_direction = mask_and_rotate(first_out_pin, out_pin_count, initial_out_pin_direction);
+ initial_set_pin_state = mask_and_rotate(first_set_pin, set_pin_count, initial_set_pin_state);
+ initial_set_pin_direction = mask_and_rotate(first_set_pin, set_pin_count, initial_set_pin_direction);
+ uint32_t set_out_overlap = mask_and_rotate(first_out_pin, out_pin_count, 0xffffffff) &
+ mask_and_rotate(first_set_pin, set_pin_count, 0xffffffff);
+ // Check that OUT and SET settings agree because we don't have a way of picking one over the other.
+ if ((initial_pin_state & set_out_overlap) != (initial_set_pin_state & set_out_overlap)) {
+ mp_raise_ValueError(translate("Initial set pin state conflicts with initial out pin state"));
+ }
+ if ((initial_pin_direction & set_out_overlap) != (initial_set_pin_direction & set_out_overlap)) {
+ mp_raise_ValueError(translate("Initial set pin direction conflicts with initial out pin direction"));
+ }
+ initial_pin_state |= initial_set_pin_state;
+ initial_pin_direction |= initial_set_pin_direction;
+
+ // Sideset overrides OUT or SET so we always use its values.
+ uint32_t sideset_mask = mask_and_rotate(first_sideset_pin, sideset_pin_count, 0x1f);
+ initial_pin_state = (initial_pin_state & ~sideset_mask) | mask_and_rotate(first_sideset_pin, sideset_pin_count, initial_sideset_pin_state);
+ initial_pin_direction = (initial_pin_direction & ~sideset_mask) | mask_and_rotate(first_sideset_pin, sideset_pin_count, initial_sideset_pin_direction);
+
+ // Deal with pull up/downs
+ uint32_t pull_up = mask_and_rotate(first_in_pin, in_pin_count, pull_pin_up);
+ uint32_t pull_down = mask_and_rotate(first_in_pin, in_pin_count, pull_pin_down);
+
+ if (jmp_pin) {
+ uint32_t jmp_mask = mask_and_rotate(jmp_pin, 1, 0x1f);
+ if (jmp_pull == PULL_UP) {
+ pull_up |= jmp_mask;
+ }
+ if (jmp_pull == PULL_DOWN) {
+ pull_up |= jmp_mask;
+ }
+ }
+ if (initial_pin_direction & (pull_up | pull_down)) {
+ mp_raise_ValueError(translate("pull masks conflict with direction masks"));
+ }
+ bool ok = rp2pio_statemachine_construct(
+ self,
+ program, program_len,
+ frequency,
+ init, init_len,
+ first_out_pin, out_pin_count,
+ first_in_pin, in_pin_count,
+ pull_up, pull_down,
+ first_set_pin, set_pin_count,
+ first_sideset_pin, sideset_pin_count,
+ initial_pin_state, initial_pin_direction,
+ jmp_pin,
+ pins_we_use, tx_fifo, rx_fifo,
+ auto_pull, pull_threshold, out_shift_right,
+ wait_for_txstall,
+ auto_push, push_threshold, in_shift_right,
+ true /* claim pins */,
+ user_interruptible,
+ sideset_enable,
+ wrap_target, wrap);
+ if (!ok) {
+ mp_raise_RuntimeError(translate("All state machines in use"));
+ }
+}
+
+void common_hal_rp2pio_statemachine_restart(rp2pio_statemachine_obj_t *self) {
+ common_hal_rp2pio_statemachine_stop(self);
+ // Reset program counter to the original offset. A JMP is 0x0000 plus
+ // the desired offset, so we can just use self->offset.
+ pio_sm_exec(self->pio, self->state_machine, self->offset);
+ pio_sm_restart(self->pio, self->state_machine);
+ uint8_t pio_index = pio_get_index(self->pio);
+ uint32_t pins_we_use = _current_sm_pins[pio_index][self->state_machine];
+ pio_sm_set_pins_with_mask(self->pio, self->state_machine, self->initial_pin_state, pins_we_use);
+ pio_sm_set_pindirs_with_mask(self->pio, self->state_machine, self->initial_pin_direction, pins_we_use);
+ rp2pio_statemachine_set_pull(self->pull_pin_up, self->pull_pin_down, pins_we_use);
+ common_hal_rp2pio_statemachine_run(self, self->init, self->init_len);
+ pio_sm_set_enabled(self->pio, self->state_machine, true);
+}
+
+void common_hal_rp2pio_statemachine_stop(rp2pio_statemachine_obj_t *self) {
+ pio_sm_set_enabled(self->pio, self->state_machine, false);
+}
+
+void common_hal_rp2pio_statemachine_run(rp2pio_statemachine_obj_t *self, const uint16_t *instructions, size_t len) {
+ for (size_t i = 0; i < len; i++) {
+ pio_sm_exec(self->pio, self->state_machine, instructions[i]);
+ }
+}
+
+uint32_t common_hal_rp2pio_statemachine_get_frequency(rp2pio_statemachine_obj_t *self) {
+ return self->actual_frequency;
+}
+
+void common_hal_rp2pio_statemachine_set_frequency(rp2pio_statemachine_obj_t *self, uint32_t frequency) {
+ if (frequency == 0) {
+ frequency = clock_get_hz(clk_sys);
+ }
+ uint64_t frequency256 = ((uint64_t)clock_get_hz(clk_sys)) * 256;
+ uint64_t div256 = frequency256 / frequency;
+ if (frequency256 % div256 > 0) {
+ div256 += 1;
+ }
+ // 0 is interpreted as 0x10000 so it's valid.
+ if (div256 / 256 > 0x10000 || frequency > clock_get_hz(clk_sys)) {
+ mp_raise_ValueError_varg(translate("%q out of range"), MP_QSTR_frequency);
+ }
+ self->actual_frequency = frequency256 / div256;
+
+ pio_sm_set_clkdiv_int_frac(self->pio, self->state_machine, div256 / 256, div256 % 256);
+ // Reset the clkdiv counter in case our new TOP is lower.
+ pio_sm_clkdiv_restart(self->pio, self->state_machine);
+}
+
+void rp2pio_statemachine_deinit(rp2pio_statemachine_obj_t *self, bool leave_pins) {
+ common_hal_rp2pio_statemachine_stop(self);
+ (void)common_hal_rp2pio_statemachine_stop_background_write(self);
+
+ uint8_t sm = self->state_machine;
+ uint8_t pio_index = pio_get_index(self->pio);
+ common_hal_mcu_disable_interrupts();
+ _interrupt_arg[pio_index][sm] = NULL;
+ _interrupt_handler[pio_index][sm] = NULL;
+ common_hal_mcu_enable_interrupts();
+ _never_reset[pio_index][sm] = false;
+ _reset_statemachine(self->pio, sm, leave_pins);
+ self->state_machine = NUM_PIO_STATE_MACHINES;
+}
+
+void common_hal_rp2pio_statemachine_deinit(rp2pio_statemachine_obj_t *self) {
+ rp2pio_statemachine_deinit(self, false);
+}
+
+void common_hal_rp2pio_statemachine_never_reset(rp2pio_statemachine_obj_t *self) {
+ uint8_t sm = self->state_machine;
+ uint8_t pio_index = pio_get_index(self->pio);
+ _never_reset[pio_index][sm] = true;
+ // TODO: never reset all the pins
+}
+
+bool common_hal_rp2pio_statemachine_deinited(rp2pio_statemachine_obj_t *self) {
+ return self->state_machine == NUM_PIO_STATE_MACHINES;
+}
+
+STATIC enum dma_channel_transfer_size _stride_to_dma_size(uint8_t stride) {
+ switch (stride) {
+ case 4:
+ return DMA_SIZE_32;
+ case 2:
+ return DMA_SIZE_16;
+ case 1:
+ default:
+ return DMA_SIZE_8;
+ }
+}
+
+static bool _transfer(rp2pio_statemachine_obj_t *self,
+ const uint8_t *data_out, size_t out_len, uint8_t out_stride_in_bytes,
+ uint8_t *data_in, size_t in_len, uint8_t in_stride_in_bytes, bool swap_out, bool swap_in) {
+ // This implementation is based on SPI but varies because the tx and rx buffers
+ // may be different lengths and occur at different times or speeds.
+
+ // 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;
+ size_t len = MAX(out_len, in_len);
+ bool tx = data_out != NULL;
+ bool rx = data_in != NULL;
+ bool use_dma = len >= dma_min_size_threshold || swap_out || swap_in;
+ if (use_dma) {
+ // Use DMA channels to service the two FIFOs
+ if (tx) {
+ chan_tx = dma_claim_unused_channel(false);
+ // DMA allocation failed...
+ if (chan_tx < 0) {
+ return false;
+ }
+ }
+ if (rx) {
+ chan_rx = dma_claim_unused_channel(false);
+ // DMA allocation failed...
+ if (chan_rx < 0) {
+ // may need to free tx channel
+ if (chan_tx >= 0) {
+ dma_channel_unclaim(chan_tx);
+ }
+ return false;
+ }
+ }
+ }
+ volatile uint8_t *tx_destination = NULL;
+ const volatile uint8_t *rx_source = NULL;
+ if (tx) {
+ tx_destination = (volatile uint8_t *)&self->pio->txf[self->state_machine];
+ if (!self->out_shift_right) {
+ tx_destination += 4 - out_stride_in_bytes;
+ }
+ }
+ if (rx) {
+ rx_source = (const volatile uint8_t *)&self->pio->rxf[self->state_machine];
+ if (self->in_shift_right) {
+ rx_source += 4 - in_stride_in_bytes;
+ }
+ }
+ uint32_t stall_mask = 1 << (PIO_FDEBUG_TXSTALL_LSB + self->state_machine);
+ if (use_dma) {
+ dma_channel_config c;
+ uint32_t channel_mask = 0;
+ if (tx) {
+ c = dma_channel_get_default_config(chan_tx);
+ channel_config_set_transfer_data_size(&c, _stride_to_dma_size(out_stride_in_bytes));
+ channel_config_set_dreq(&c, self->tx_dreq);
+ channel_config_set_read_increment(&c, true);
+ channel_config_set_write_increment(&c, false);
+ channel_config_set_bswap(&c, swap_out);
+ dma_channel_configure(chan_tx, &c,
+ tx_destination,
+ data_out,
+ out_len / out_stride_in_bytes,
+ false);
+ channel_mask |= 1u << chan_tx;
+ }
+ if (rx) {
+ c = dma_channel_get_default_config(chan_rx);
+ channel_config_set_transfer_data_size(&c, _stride_to_dma_size(in_stride_in_bytes));
+ channel_config_set_dreq(&c, self->rx_dreq);
+ channel_config_set_read_increment(&c, false);
+ channel_config_set_write_increment(&c, true);
+ channel_config_set_bswap(&c, swap_in);
+ dma_channel_configure(chan_rx, &c,
+ data_in,
+ rx_source,
+ in_len / in_stride_in_bytes,
+ false);
+ channel_mask |= 1u << chan_rx;
+ }
+
+ dma_start_channel_mask(channel_mask);
+ while ((rx && dma_channel_is_busy(chan_rx)) ||
+ (tx && dma_channel_is_busy(chan_tx))) {
+ // TODO: We should idle here until we get a DMA interrupt or something else.
+ RUN_BACKGROUND_TASKS;
+ if (self->user_interruptible && mp_hal_is_interrupted()) {
+ if (rx && dma_channel_is_busy(chan_rx)) {
+ dma_channel_abort(chan_rx);
+ }
+ if (tx && dma_channel_is_busy(chan_tx)) {
+ dma_channel_abort(chan_tx);
+ }
+ break;
+ }
+
+ }
+ // Clear the stall bit so we can detect when the state machine is done transmitting.
+ self->pio->fdebug = stall_mask;
+ }
+
+ // If we have claimed only one channel successfully, we should release immediately. This also
+ // releases the DMA after use_dma has been done.
+ if (chan_rx >= 0) {
+ dma_channel_unclaim(chan_rx);
+ }
+ if (chan_tx >= 0) {
+ dma_channel_unclaim(chan_tx);
+ }
+
+ if (!use_dma && !(self->user_interruptible && mp_hal_is_interrupted())) {
+ // Use software for small transfers, or if couldn't claim two DMA channels
+ size_t rx_remaining = in_len / in_stride_in_bytes;
+ size_t tx_remaining = out_len / out_stride_in_bytes;
+
+ while (rx_remaining || tx_remaining) {
+ while (tx_remaining && !pio_sm_is_tx_fifo_full(self->pio, self->state_machine)) {
+ if (out_stride_in_bytes == 1) {
+ *tx_destination = *data_out;
+ } else if (out_stride_in_bytes == 2) {
+ *((uint16_t *)tx_destination) = *((uint16_t *)data_out);
+ } else if (out_stride_in_bytes == 4) {
+ *((uint32_t *)tx_destination) = *((uint32_t *)data_out);
+ }
+ data_out += out_stride_in_bytes;
+ --tx_remaining;
+ }
+ while (rx_remaining && !pio_sm_is_rx_fifo_empty(self->pio, self->state_machine)) {
+ if (in_stride_in_bytes == 1) {
+ *data_in = (uint8_t)*rx_source;
+ } else if (in_stride_in_bytes == 2) {
+ *((uint16_t *)data_in) = *((uint16_t *)rx_source);
+ } else if (in_stride_in_bytes == 4) {
+ *((uint32_t *)data_in) = *((uint32_t *)rx_source);
+ }
+ data_in += in_stride_in_bytes;
+ --rx_remaining;
+ }
+ RUN_BACKGROUND_TASKS;
+ if (self->user_interruptible && mp_hal_is_interrupted()) {
+ break;
+ }
+ }
+ // Clear the stall bit so we can detect when the state machine is done transmitting.
+ self->pio->fdebug = stall_mask;
+ }
+ // Wait for the state machine to finish transmitting the data we've queued
+ // up.
+ if (tx) {
+ while (!pio_sm_is_tx_fifo_empty(self->pio, self->state_machine) ||
+ (self->wait_for_txstall && (self->pio->fdebug & stall_mask) == 0)) {
+ RUN_BACKGROUND_TASKS;
+ if (self->user_interruptible && mp_hal_is_interrupted()) {
+ break;
+ }
+ }
+ }
+ return true;
+}
+
+// TODO: Provide a way around these checks in case someone wants to use the FIFO
+// with manually run code.
+
+bool common_hal_rp2pio_statemachine_write(rp2pio_statemachine_obj_t *self, const uint8_t *data, size_t len, uint8_t stride_in_bytes, bool swap) {
+ if (!self->out) {
+ mp_raise_RuntimeError(translate("No out in program"));
+ }
+ return _transfer(self, data, len, stride_in_bytes, NULL, 0, 0, swap, false);
+}
+
+bool common_hal_rp2pio_statemachine_readinto(rp2pio_statemachine_obj_t *self, uint8_t *data, size_t len, uint8_t stride_in_bytes, bool swap) {
+ if (!self->in) {
+ mp_raise_RuntimeError(translate("No in in program"));
+ }
+ return _transfer(self, NULL, 0, 0, data, len, stride_in_bytes, false, swap);
+}
+
+bool common_hal_rp2pio_statemachine_write_readinto(rp2pio_statemachine_obj_t *self,
+ const uint8_t *data_out, size_t out_len, uint8_t out_stride_in_bytes,
+ uint8_t *data_in, size_t in_len, uint8_t in_stride_in_bytes, bool swap_out, bool swap_in) {
+ if (!self->in || !self->out) {
+ mp_raise_RuntimeError(translate("No in or out in program"));
+ }
+ return _transfer(self, data_out, out_len, out_stride_in_bytes, data_in, in_len, in_stride_in_bytes, swap_out, swap_in);
+}
+
+bool common_hal_rp2pio_statemachine_get_rxstall(rp2pio_statemachine_obj_t *self) {
+ uint32_t stall_mask = 1 << (PIO_FDEBUG_RXSTALL_LSB + self->state_machine);
+ return (self->pio->fdebug & stall_mask) != 0;
+}
+
+void common_hal_rp2pio_statemachine_clear_rxfifo(rp2pio_statemachine_obj_t *self) {
+ uint8_t level = pio_sm_get_rx_fifo_level(self->pio, self->state_machine);
+ uint32_t stall_mask = 1 << (PIO_FDEBUG_RXSTALL_LSB + self->state_machine);
+ for (size_t i = 0; i < level; i++) {
+ (void)self->pio->rxf[self->state_machine];
+ }
+ self->pio->fdebug = stall_mask;
+}
+
+bool common_hal_rp2pio_statemachine_get_txstall(rp2pio_statemachine_obj_t *self) {
+ uint32_t stall_mask = 1 << (PIO_FDEBUG_TXSTALL_LSB + self->state_machine);
+ return (self->pio->fdebug & stall_mask) != 0;
+}
+
+void common_hal_rp2pio_statemachine_clear_txstall(rp2pio_statemachine_obj_t *self) {
+ uint8_t level = pio_sm_get_rx_fifo_level(self->pio, self->state_machine);
+ uint32_t stall_mask = 1 << (PIO_FDEBUG_TXSTALL_LSB + self->state_machine);
+ self->pio->fdebug = stall_mask;
+}
+
+
+size_t common_hal_rp2pio_statemachine_get_in_waiting(rp2pio_statemachine_obj_t *self) {
+ uint8_t level = pio_sm_get_rx_fifo_level(self->pio, self->state_machine);
+ return level;
+}
+
+void common_hal_rp2pio_statemachine_set_interrupt_handler(rp2pio_statemachine_obj_t *self, void (*handler)(void *), void *arg, int mask) {
+ uint8_t pio_index = pio_get_index(self->pio);
+ uint8_t sm = self->state_machine;
+
+ common_hal_mcu_disable_interrupts();
+ uint32_t inte = self->pio->inte0;
+ inte &= ~((PIO_IRQ0_INTF_SM0_RXNEMPTY_BITS | PIO_IRQ0_INTF_SM0_TXNFULL_BITS | PIO_IRQ0_INTF_SM0_BITS) << sm);
+ inte |= (mask << sm);
+ self->pio->inte0 = inte;
+ _interrupt_arg[pio_index][sm] = arg;
+ _interrupt_handler[pio_index][sm] = handler;
+ irq_set_exclusive_handler(PIO0_IRQ_0 + 2 * pio_index, rp2pio_statemachine_interrupt_handler);
+ irq_set_enabled(PIO0_IRQ_0 + 2 * pio_index, true);
+ common_hal_mcu_enable_interrupts();
+}
+
+STATIC void rp2pio_statemachine_interrupt_handler(void) {
+ for (size_t pio_index = 0; pio_index < NUM_PIOS; pio_index++) {
+ PIO pio = pio_instances[pio_index];
+ for (size_t sm = 0; sm < NUM_PIO_STATE_MACHINES; sm++) {
+ if (!_interrupt_handler[pio_index][sm]) {
+ continue;
+ }
+ uint32_t intf = (PIO_IRQ0_INTF_SM0_RXNEMPTY_BITS | PIO_IRQ0_INTF_SM0_TXNFULL_BITS | PIO_IRQ0_INTF_SM0_BITS) << sm;
+ if (pio->ints0 & intf) {
+ _interrupt_handler[pio_index][sm](_interrupt_arg[pio_index][sm]);
+ }
+ }
+ }
+}
+
+uint8_t rp2pio_statemachine_program_offset(rp2pio_statemachine_obj_t *self) {
+ uint8_t pio_index = pio_get_index(self->pio);
+ uint8_t sm = self->state_machine;
+ return _current_program_offset[pio_index][sm];
+}
+
+bool common_hal_rp2pio_statemachine_background_write(rp2pio_statemachine_obj_t *self, const sm_buf_info *once, const sm_buf_info *loop, uint8_t stride_in_bytes, bool swap) {
+ uint8_t pio_index = pio_get_index(self->pio);
+ uint8_t sm = self->state_machine;
+
+ int pending_buffers = (once->info.len != 0) + (loop->info.len != 0);
+ if (!once->info.len) {
+ once = loop;
+ }
+
+ if (SM_DMA_ALLOCATED(pio_index, sm)) {
+ if (stride_in_bytes != self->background_stride_in_bytes) {
+ mp_raise_ValueError(translate("Mismatched data size"));
+ }
+ if (swap != self->byteswap) {
+ mp_raise_ValueError(translate("Mismatched swap flag"));
+ }
+
+ while (self->pending_buffers) {
+ RUN_BACKGROUND_TASKS;
+ if (self->user_interruptible && mp_hal_is_interrupted()) {
+ return false;
+ }
+ }
+
+ common_hal_mcu_disable_interrupts();
+ self->once = *once;
+ self->loop = *loop;
+ self->pending_buffers = pending_buffers;
+
+ if (self->dma_completed && self->once.info.len) {
+ rp2pio_statemachine_dma_complete(self, SM_DMA_GET_CHANNEL(pio_index, sm));
+ self->dma_completed = false;
+ }
+
+ common_hal_mcu_enable_interrupts();
+
+ return true;
+ }
+
+ int channel = dma_claim_unused_channel(false);
+ if (channel == -1) {
+ return false;
+ }
+
+ SM_DMA_SET_CHANNEL(pio_index, sm, channel);
+
+ volatile uint8_t *tx_destination = (volatile uint8_t *)&self->pio->txf[self->state_machine];
+
+ self->tx_dreq = pio_get_dreq(self->pio, self->state_machine, true);
+
+ dma_channel_config c;
+
+ self->current = *once;
+ self->once = *loop;
+ self->loop = *loop;
+ self->pending_buffers = pending_buffers;
+ self->dma_completed = false;
+ self->background_stride_in_bytes = stride_in_bytes;
+ self->byteswap = swap;
+
+ c = dma_channel_get_default_config(channel);
+ channel_config_set_transfer_data_size(&c, _stride_to_dma_size(stride_in_bytes));
+ channel_config_set_dreq(&c, self->tx_dreq);
+ channel_config_set_read_increment(&c, true);
+ channel_config_set_write_increment(&c, false);
+ channel_config_set_bswap(&c, swap);
+ dma_channel_configure(channel, &c,
+ tx_destination,
+ once->info.buf,
+ once->info.len / stride_in_bytes,
+ false);
+
+ common_hal_mcu_disable_interrupts();
+ MP_STATE_PORT(background_pio)[channel] = self;
+ dma_hw->inte0 |= 1u << channel;
+ irq_set_mask_enabled(1 << DMA_IRQ_0, true);
+ dma_start_channel_mask(1u << channel);
+ common_hal_mcu_enable_interrupts();
+
+ return true;
+}
+
+void rp2pio_statemachine_dma_complete(rp2pio_statemachine_obj_t *self, int channel) {
+ self->current = self->once;
+ self->once = self->loop;
+
+ if (self->current.info.buf) {
+ if (self->pending_buffers > 0) {
+ self->pending_buffers--;
+ }
+ dma_channel_set_read_addr(channel, self->current.info.buf, false);
+ dma_channel_set_trans_count(channel, self->current.info.len / self->background_stride_in_bytes, true);
+ } else {
+ self->dma_completed = true;
+ self->pending_buffers = 0; // should be a no-op
+ }
+}
+
+bool common_hal_rp2pio_statemachine_stop_background_write(rp2pio_statemachine_obj_t *self) {
+ uint8_t pio_index = pio_get_index(self->pio);
+ uint8_t sm = self->state_machine;
+ rp2pio_statemachine_clear_dma(pio_index, sm);
+ memset(&self->current, 0, sizeof(self->current));
+ memset(&self->once, 0, sizeof(self->once));
+ memset(&self->loop, 0, sizeof(self->loop));
+ self->pending_buffers = 0;
+ return true;
+}
+
+bool common_hal_rp2pio_statemachine_get_writing(rp2pio_statemachine_obj_t *self) {
+ return !self->dma_completed;
+}
+
+int common_hal_rp2pio_statemachine_get_pending(rp2pio_statemachine_obj_t *self) {
+ return self->pending_buffers;
+}
diff --git a/circuitpython/ports/raspberrypi/common-hal/rp2pio/StateMachine.h b/circuitpython/ports/raspberrypi/common-hal/rp2pio/StateMachine.h
new file mode 100644
index 0000000..03dadc5
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/rp2pio/StateMachine.h
@@ -0,0 +1,100 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 Scott Shawcroft for Adafruit Industries
+ *
+ * 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.
+ */
+
+#ifndef MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_RP2PIO_STATEMACHINE_H
+#define MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_RP2PIO_STATEMACHINE_H
+
+#include "py/obj.h"
+
+#include "common-hal/microcontroller/Pin.h"
+#include "src/rp2_common/hardware_pio/include/hardware/pio.h"
+
+typedef struct sm_buf_info {
+ mp_obj_t obj;
+ mp_buffer_info_t info;
+} sm_buf_info;
+
+typedef struct {
+ mp_obj_base_t base;
+ uint32_t pins; // Bitmask of what pins this state machine uses.
+ int state_machine;
+ PIO pio;
+ const uint16_t *init;
+ size_t init_len;
+ uint32_t initial_pin_state;
+ uint32_t initial_pin_direction;
+ uint32_t pull_pin_up;
+ uint32_t pull_pin_down;
+ uint tx_dreq;
+ uint rx_dreq;
+ uint32_t actual_frequency;
+ pio_sm_config sm_config;
+ bool in;
+ bool out;
+ bool wait_for_txstall;
+ bool out_shift_right;
+ bool in_shift_right;
+ bool user_interruptible;
+ uint8_t offset;
+
+ // dma-related items
+ volatile int pending_buffers;
+ sm_buf_info current, once, loop;
+ int background_stride_in_bytes;
+ bool dma_completed, byteswap;
+} rp2pio_statemachine_obj_t;
+
+void reset_rp2pio_statemachine(void);
+
+// Minimal internal version that only fails on pin error (not in use) or full PIO.
+bool rp2pio_statemachine_construct(rp2pio_statemachine_obj_t *self,
+ const uint16_t *program, size_t program_len,
+ size_t frequency,
+ const uint16_t *init, size_t init_len,
+ const mcu_pin_obj_t *first_out_pin, uint8_t out_pin_count,
+ const mcu_pin_obj_t *first_in_pin, uint8_t in_pin_count,
+ uint32_t pull_pin_up, uint32_t pull_pin_down,
+ const mcu_pin_obj_t *first_set_pin, uint8_t set_pin_count,
+ const mcu_pin_obj_t *first_sideset_pin, uint8_t sideset_pin_count,
+ uint32_t initial_pin_state, uint32_t initial_pin_direction,
+ const mcu_pin_obj_t *jmp_pin,
+ uint32_t pins_we_use, bool tx_fifo, bool rx_fifo,
+ bool auto_pull, uint8_t pull_threshold, bool out_shift_right,
+ bool wait_for_txstall,
+ bool auto_push, uint8_t push_threshold, bool in_shift_right,
+ bool claim_pins,
+ bool interruptible,
+ bool sideset_enable,
+ int wrap_target, int wrap);
+
+uint8_t rp2pio_statemachine_program_offset(rp2pio_statemachine_obj_t *self);
+
+void rp2pio_statemachine_deinit(rp2pio_statemachine_obj_t *self, bool leave_pins);
+void rp2pio_statemachine_dma_complete(rp2pio_statemachine_obj_t *self, int channel);
+
+extern const mp_obj_type_t rp2pio_statemachine_type;
+
+#endif // MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_RP2PIO_STATEMACHINE_H
diff --git a/circuitpython/ports/raspberrypi/common-hal/rp2pio/__init__.c b/circuitpython/ports/raspberrypi/common-hal/rp2pio/__init__.c
new file mode 100644
index 0000000..8b8101f
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/rp2pio/__init__.c
@@ -0,0 +1,43 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 Jeff Epler for Adafruit Industries
+ *
+ * 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/obj.h"
+#include "shared-bindings/microcontroller/Pin.h"
+#include "bindings/rp2pio/__init__.h"
+
+bool common_hal_rp2pio_pins_are_sequential(size_t len, mp_obj_t *items) {
+ if (len == 0) {
+ return true;
+ }
+ const mcu_pin_obj_t *last_pin = validate_obj_is_pin(items[0]);
+ for (int i = 1; i < len; i++) {
+ const mcu_pin_obj_t *pin = validate_obj_is_pin(items[i]);
+ if (pin->number != last_pin->number + 1) {
+ return false;
+ }
+ }
+ return true;
+}
diff --git a/circuitpython/ports/raspberrypi/common-hal/rtc/RTC.c b/circuitpython/ports/raspberrypi/common-hal/rtc/RTC.c
new file mode 100644
index 0000000..9bd10ab
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/rtc/RTC.c
@@ -0,0 +1,96 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright 2020 microDev
+ *
+ * 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 "shared-bindings/rtc/RTC.h"
+#include "common-hal/rtc/RTC.h"
+
+#include <sys/time.h>
+
+#include "py/runtime.h"
+#include "src/rp2_common/hardware_rtc/include/hardware/rtc.h"
+#include "src/rp2_common/hardware_clocks/include/hardware/clocks.h"
+
+void common_hal_rtc_init(void) {
+ datetime_t t = {
+ .year = 2020,
+ .month = 1,
+ .day = 1,
+ .dotw = 3, // 0 is Sunday, so 3 is Wednesday
+ .hour = 0,
+ .min = 0,
+ .sec = 0
+ };
+
+ // Start the RTC
+ rtc_init();
+ rtc_set_datetime(&t);
+
+}
+
+void common_hal_rtc_get_time(timeutils_struct_time_t *tm) {
+ datetime_t t;
+ rtc_get_datetime(&t);
+
+ tm->tm_year = t.year;
+ tm->tm_mon = t.month;
+ tm->tm_mday = t.day;
+ tm->tm_wday = t.dotw;
+ tm->tm_hour = t.hour;
+ tm->tm_min = t.min;
+ tm->tm_sec = t.sec;
+
+ if (tm->tm_wday == 0) {
+ tm->tm_wday = 6;
+ } else {
+ tm->tm_wday -= 1;
+ }
+}
+
+void common_hal_rtc_set_time(timeutils_struct_time_t *tm) {
+ if (tm->tm_wday == 6) {
+ tm->tm_wday = 0;
+ } else {
+ tm->tm_wday += 1;
+ }
+
+ datetime_t t = {
+ .year = tm->tm_year,
+ .month = tm->tm_mon,
+ .day = tm->tm_mday,
+ .dotw = tm->tm_wday,
+ .hour = tm->tm_hour,
+ .min = tm->tm_min,
+ .sec = tm->tm_sec
+ };
+ rtc_set_datetime(&t);
+}
+
+int common_hal_rtc_get_calibration(void) {
+ return 0;
+}
+
+void common_hal_rtc_set_calibration(int calibration) {
+ mp_raise_NotImplementedError(translate("RTC calibration is not supported on this board"));
+}
diff --git a/circuitpython/ports/raspberrypi/common-hal/rtc/RTC.h b/circuitpython/ports/raspberrypi/common-hal/rtc/RTC.h
new file mode 100644
index 0000000..426a4ec
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/rtc/RTC.h
@@ -0,0 +1,32 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 microDev
+ *
+ * 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.
+ */
+
+#ifndef MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_RTC_RTC_H
+#define MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_RTC_RTC_H
+
+extern void common_hal_rtc_init(void);
+
+#endif // MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_RTC_RTC_H
diff --git a/circuitpython/ports/raspberrypi/common-hal/rtc/__init__.c b/circuitpython/ports/raspberrypi/common-hal/rtc/__init__.c
new file mode 100644
index 0000000..f5e6b6b
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/rtc/__init__.c
@@ -0,0 +1 @@
+// No RTC module functions
diff --git a/circuitpython/ports/raspberrypi/common-hal/supervisor/Runtime.c b/circuitpython/ports/raspberrypi/common-hal/supervisor/Runtime.c
new file mode 100644
index 0000000..f827651
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/supervisor/Runtime.c
@@ -0,0 +1,37 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Michael Schroeder
+ *
+ * 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 <stdbool.h>
+#include "shared-bindings/supervisor/Runtime.h"
+#include "supervisor/serial.h"
+
+bool common_hal_supervisor_runtime_get_serial_connected(void) {
+ return (bool)serial_connected();
+}
+
+bool common_hal_supervisor_runtime_get_serial_bytes_available(void) {
+ return (bool)serial_bytes_available();
+}
diff --git a/circuitpython/ports/raspberrypi/common-hal/supervisor/Runtime.h b/circuitpython/ports/raspberrypi/common-hal/supervisor/Runtime.h
new file mode 100755
index 0000000..45db489
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/supervisor/Runtime.h
@@ -0,0 +1,37 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Michael Schroeder
+ *
+ * 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.
+ */
+
+#ifndef MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_SUPERVISOR_RUNTIME_H
+#define MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_SUPERVISOR_RUNTIME_H
+
+#include "py/obj.h"
+
+typedef struct {
+ mp_obj_base_t base;
+ // Stores no state currently.
+} super_runtime_obj_t;
+
+#endif // MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_SUPERVISOR_RUNTIME_H
diff --git a/circuitpython/ports/raspberrypi/common-hal/supervisor/__init__.c b/circuitpython/ports/raspberrypi/common-hal/supervisor/__init__.c
new file mode 100755
index 0000000..6dca35f
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/supervisor/__init__.c
@@ -0,0 +1,40 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Michael Schroeder
+ *
+ * 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/obj.h"
+
+#include "shared-bindings/supervisor/__init__.h"
+#include "shared-bindings/supervisor/Runtime.h"
+
+
+// The singleton supervisor.Runtime object, bound to supervisor.runtime
+// It currently only has properties, and no state.
+const super_runtime_obj_t common_hal_supervisor_runtime_obj = {
+ .base = {
+ .type = &supervisor_runtime_type,
+ },
+};
diff --git a/circuitpython/ports/raspberrypi/common-hal/watchdog/WatchDogMode.c b/circuitpython/ports/raspberrypi/common-hal/watchdog/WatchDogMode.c
new file mode 100644
index 0000000..fc4e0e0
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/watchdog/WatchDogMode.c
@@ -0,0 +1 @@
+// No watchdog module functions.
diff --git a/circuitpython/ports/raspberrypi/common-hal/watchdog/WatchDogTimer.c b/circuitpython/ports/raspberrypi/common-hal/watchdog/WatchDogTimer.c
new file mode 100644
index 0000000..f23ccda
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/watchdog/WatchDogTimer.c
@@ -0,0 +1,83 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 microDev
+ *
+ * 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 "common-hal/watchdog/WatchDogTimer.h"
+
+#include "shared-bindings/watchdog/__init__.h"
+#include "shared-bindings/microcontroller/__init__.h"
+
+#include "src/rp2_common/hardware_watchdog/include/hardware/watchdog.h"
+
+void common_hal_watchdog_feed(watchdog_watchdogtimer_obj_t *self) {
+ watchdog_update();
+}
+
+void common_hal_watchdog_deinit(watchdog_watchdogtimer_obj_t *self) {
+ if (self->mode == WATCHDOGMODE_RESET) {
+ mp_raise_RuntimeError(translate("WatchDogTimer cannot be deinitialized once mode is set to RESET"));
+ } else {
+ self->mode = WATCHDOGMODE_NONE;
+ }
+}
+
+/*
+void watchdog_reset(void) {
+ common_hal_watchdog_deinit(&common_hal_mcu_watchdogtimer_obj);
+}
+*/
+
+mp_float_t common_hal_watchdog_get_timeout(watchdog_watchdogtimer_obj_t *self) {
+ return self->timeout;
+}
+
+void common_hal_watchdog_set_timeout(watchdog_watchdogtimer_obj_t *self, mp_float_t new_timeout) {
+ // max timeout is 8.388607 sec
+ // this is rounded down to 8.388 sec
+ uint64_t timeout = new_timeout * 1000;
+ if (timeout > 8388) {
+ mp_raise_ValueError(translate("timeout duration exceeded the maximum supported value"));
+ }
+ if ((uint16_t)self->timeout != timeout) {
+ watchdog_enable(timeout, false);
+ self->timeout = new_timeout;
+ }
+}
+
+watchdog_watchdogmode_t common_hal_watchdog_get_mode(watchdog_watchdogtimer_obj_t *self) {
+ return self->mode;
+}
+
+void common_hal_watchdog_set_mode(watchdog_watchdogtimer_obj_t *self, watchdog_watchdogmode_t new_mode) {
+ if (self->mode != new_mode) {
+ if (new_mode == WATCHDOGMODE_RAISE) {
+ mp_raise_NotImplementedError(translate("RAISE mode is not implemented"));
+ } else if (new_mode == WATCHDOGMODE_NONE) {
+ common_hal_watchdog_deinit(self);
+ }
+ self->mode = new_mode;
+ }
+}
diff --git a/circuitpython/ports/raspberrypi/common-hal/watchdog/WatchDogTimer.h b/circuitpython/ports/raspberrypi/common-hal/watchdog/WatchDogTimer.h
new file mode 100644
index 0000000..ce34f0b
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/watchdog/WatchDogTimer.h
@@ -0,0 +1,43 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 microDev
+ *
+ * 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.
+ */
+
+#ifndef MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_WATCHDOG_WATCHDOGTIMER_H
+#define MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_WATCHDOG_WATCHDOGTIMER_H
+
+#include "py/obj.h"
+#include "shared-bindings/watchdog/WatchDogMode.h"
+#include "shared-bindings/watchdog/WatchDogTimer.h"
+
+struct _watchdog_watchdogtimer_obj_t {
+ mp_obj_base_t base;
+ mp_float_t timeout;
+ watchdog_watchdogmode_t mode;
+};
+
+// This needs to be called in order to disable the watchdog
+// void watchdog_reset(void);
+
+#endif // MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_WATCHDOG_WATCHDOGTIMER_H
diff --git a/circuitpython/ports/raspberrypi/common-hal/watchdog/__init__.c b/circuitpython/ports/raspberrypi/common-hal/watchdog/__init__.c
new file mode 100644
index 0000000..fc4e0e0
--- /dev/null
+++ b/circuitpython/ports/raspberrypi/common-hal/watchdog/__init__.c
@@ -0,0 +1 @@
+// No watchdog module functions.