diff --git a/.github/workflows/bundle_cron.yml b/.github/workflows/bundle_cron.yml new file mode 100644 index 0000000000000..606707d4102f1 --- /dev/null +++ b/.github/workflows/bundle_cron.yml @@ -0,0 +1,58 @@ +# SPDX-FileCopyrightText: 2019 Michael Schroeder +# +# SPDX-License-Identifier: MIT + +name: Update Bundles + +on: + schedule: + - cron: 0 5 * * * + workflow_dispatch: + +jobs: + check-repo-owner: + # This job is so the entire workflow will end successfully and give some + # output to explain why it hasn't run on a non-Adafruit fork. + runs-on: ubuntu-latest + steps: + - name: repository + env: + OWNER_IS_ADAFRUIT: ${{ startswith(github.repository, 'adafruit/') }} + run: | + echo "This workflow will only run if Adafruit is the repository owner." + echo "Repository owner is Adafruit: $OWNER_IS_ADAFRUIT" + update-bundles: + runs-on: ubuntu-latest + # Only run the build on Adafruit's repository. Forks won't have the secrets. + # It's necessary to do this here, since 'schedule' events cannot (currently) + # be limited (they run on all forks' default branches). + if: startswith(github.repository, 'adafruit/') + steps: + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: 3.12 + - name: Load contributor cache + uses: actions/cache@v4 + with: + key: "contributor-cache" + path: "contributors.json" + - name: Versions + run: | + python3 --version + - uses: actions/checkout@v4 + with: + repository: 'adafruit/adabot' + submodules: true + - name: Install deps + run: | + pip install -r requirements.txt + - name: Run adabot.circuitpython_bundle + env: + ADABOT_EMAIL: ${{ secrets.ADABOT_EMAIL }} + ADABOT_GITHUB_USER: ${{ secrets.ADABOT_GITHUB_USER }} + ADABOT_GITHUB_ACCESS_TOKEN: ${{ secrets.ADABOT_GITHUB_ACCESS_TOKEN }} + BIGQUERY_PRIVATE_KEY: ${{ secrets.BIGQUERY_PRIVATE_KEY }} + BIGQUERY_CLIENT_EMAIL: ${{ secrets.BIGQUERY_CLIENT_EMAIL }} + run: | + python3 -u -m adabot.circuitpython_bundle diff --git a/.github/workflows/learn_cron.yml b/.github/workflows/learn_cron.yml new file mode 100644 index 0000000000000..6100e6637c20e --- /dev/null +++ b/.github/workflows/learn_cron.yml @@ -0,0 +1,41 @@ +# SPDX-FileCopyrightText: 2021 Jeff Epler for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +name: Tag Learning System Guides + +on: + schedule: + - cron: 0 5 * * * + +jobs: + check-repo-owner: + # This job is so the entire workflow will end successfully and give some + # output to explain why it hasn't run on a non-Adafruit fork. + runs-on: ubuntu-latest + if: ${{ (github.repository_owner != 'adafruit') }} + steps: + - run: | + echo "This workflow is only intended to run in the adafruit fork" + + update-learn: + runs-on: ubuntu-latest + # Only run the build if the access token has been configured. This will be + # the case on Adafruit's repository. It's necessary to do this here, since + # 'schedule' events cannot (currently) be limited (they run on all forks' + # default branches). + if: ${{ (github.repository_owner == 'adafruit') }} + steps: + - uses: actions/checkout@v4 + with: + repository: ${{ github.repository_owner }}/Adafruit_Learning_System_Guides + token: ${{ secrets.ADABOT_GITHUB_ACCESS_TOKEN }} + - name: Tag a release + env: + ADABOT_EMAIL: ${{ secrets.ADABOT_EMAIL }} + run: | + git config --global user.name adabot + git config --global user.email "$ADABOT_EMAIL" + TAG_NAME=`date +%Y%m%d` + git tag $TAG_NAME + git push origin $TAG_NAME diff --git a/.github/workflows/reports_cron.yml b/.github/workflows/reports_cron.yml new file mode 100644 index 0000000000000..b4e9a43024e27 --- /dev/null +++ b/.github/workflows/reports_cron.yml @@ -0,0 +1,91 @@ +# SPDX-FileCopyrightText: 2019 Michael Schroeder +# +# SPDX-License-Identifier: MIT + +name: Run Daily Reports + +on: + schedule: + # The actor (github.actor) that runs the cron job may be the user who created the cron job + # initially. It does not appear to be settable via a secret or environment variable. + - cron: 15 5 * * * + workflow_dispatch: + + +jobs: + check-repo-owner: + # This job is so the entire workflow will end successfully and give some + # output to explain why it hasn't run on a non-Adafruit fork. + runs-on: ubuntu-latest + steps: + - name: repository + env: + OWNER_IS_ADAFRUIT: ${{ startswith(github.repository, 'adafruit/') }} + run: | + echo "This workflow will only run if Adafruit is the repository owner." + echo "Repository owner is Adafruit: $OWNER_IS_ADAFRUIT" + run-reports: + runs-on: ubuntu-latest + # Only run the build on Adafruit's repository. Forks won't have the secrets. + # It's necessary to do this here, since 'schedule' events cannot (currently) + # be limited (they run on all forks' default branches). + if: startswith(github.repository, 'adafruit/') + env: + ADABOT_GITHUB_USER: ${{ secrets.ADABOT_GITHUB_USER }} + ADABOT_GITHUB_ACCESS_TOKEN: ${{ secrets.ADABOT_GITHUB_ACCESS_TOKEN }} + RTD_TOKEN: ${{ secrets.RTD_TOKEN }} + BIGQUERY_PRIVATE_KEY: ${{ secrets.BIGQUERY_PRIVATE_KEY }} + BIGQUERY_CLIENT_EMAIL: ${{ secrets.BIGQUERY_CLIENT_EMAIL }} + steps: + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: 3.11 + - name: Versions + run: | + python3 --version + - uses: actions/checkout@v4 + with: + repository: 'adafruit/adabot' + submodules: true + - name: Install deps + run: | + pip install -r requirements.txt + - name: Make Directory For Report Files + run: mkdir -p bin/adabot + - name: Set Date Variable + id: today + run: | + echo date=$( + date +%Y%m%d + ) >> $GITHUB_OUTPUT + - name: Run adabot.circuitpython_libraries + env: + # LIB_CHECK_CP_FILE is for circuitpython_libraries.py output + LIB_CHECK_CP_FILE: bin/adabot/circuitpython_library_report_${{ steps.today.outputs.date }}.txt + run: | + python3 -u -m adabot.circuitpython_libraries -o $LIB_CHECK_CP_FILE + continue-on-error: true + - name: Run adabot.circuitpython_library_download_stats + env: + # LIB_DL_STATS_FILE is for future Bundle and PyPi download stats script + LIB_DL_STATS_FILE: bin/adabot/library_download_stats_${{ steps.today.outputs.date }}.txt + run: | + python3 -u -m adabot.circuitpython_library_download_stats -o $LIB_DL_STATS_FILE + continue-on-error: true + - name: Run adabot.arduino_libraries + env: + # LIB_CHECK_ARD_FILE is for arduino_libraries.py output + LIB_CHECK_ARD_FILE: bin/adabot/arduino_library_report_${{ steps.today.outputs.date }}.txt + run: | + python3 -u -m adabot.arduino_libraries -o $LIB_CHECK_ARD_FILE + continue-on-error: true + - name: Check For Files + run: | + ls bin/adabot + - name: Upload Reports To AWS S3 + if: ${{ github.event_name != 'workflow_dispatch' }} + env: + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + run: "[ -z \"$AWS_ACCESS_KEY_ID\" ] || aws s3 cp bin/adabot/ s3://adafruit-circuit-python/adabot/bin/reports/ --recursive --no-progress --region us-east-1" diff --git a/Makefile b/Makefile index f2fc070a6331d..c2aebc61f1c0b 100644 --- a/Makefile +++ b/Makefile @@ -299,6 +299,9 @@ update-frozen-libraries: one-of-each: samd21 litex mimxrt10xx nordic stm +analog: + $(MAKE) -C ports/analog/ BOARD=apard32690 + samd21: $(MAKE) -C ports/atmel-samd BOARD=trinket_m0 diff --git a/frozen/Adafruit_CircuitPython_DotStar b/frozen/Adafruit_CircuitPython_DotStar index 163f2f166aee1..4b0ba649e5abd 160000 --- a/frozen/Adafruit_CircuitPython_DotStar +++ b/frozen/Adafruit_CircuitPython_DotStar @@ -1 +1 @@ -Subproject commit 163f2f166aee11d82303492bb1e5af4937e57b62 +Subproject commit 4b0ba649e5abdebead5b9a47a6c695d67c2c25fa diff --git a/lib/littlefs/lfs1.c b/lib/littlefs/lfs1.c index 6a3fd670012cc..ec18dc470258c 100644 --- a/lib/littlefs/lfs1.c +++ b/lib/littlefs/lfs1.c @@ -2141,7 +2141,7 @@ int lfs1_format(lfs1_t *lfs1, const struct lfs1_config *cfg) { .d.elen = sizeof(superblock.d) - sizeof(superblock.d.magic) - 4, .d.nlen = sizeof(superblock.d.magic), .d.version = LFS1_DISK_VERSION, - .d.magic = {"littlefs"}, + .d.magic = {'l', 'i', 't', 't', 'l', 'e', 'f', 's'}, .d.block_size = lfs1->cfg->block_size, .d.block_count = lfs1->cfg->block_count, .d.root = {lfs1->root[0], lfs1->root[1]}, diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index 262b5de769c89..1e167c30353dd 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -130,6 +130,7 @@ msgstr "" msgid "%q indices must be integers, not %s" msgstr "" +#: ports/analog/common-hal/busio/SPI.c ports/analog/common-hal/busio/UART.c #: shared-module/bitbangio/SPI.c msgid "%q init failed" msgstr "" @@ -158,6 +159,10 @@ msgstr "" msgid "%q length must be >= %d" msgstr "" +#: py/runtime.c +msgid "%q moved from %q to %q" +msgstr "" + #: py/argcheck.c msgid "%q must be %d" msgstr "" @@ -237,6 +242,7 @@ msgstr "" msgid "%q out of bounds" msgstr "" +#: ports/analog/common-hal/busio/SPI.c #: ports/atmel-samd/common-hal/pulseio/PulseIn.c #: ports/cxd56/common-hal/pulseio/PulseIn.c #: ports/nordic/common-hal/pulseio/PulseIn.c @@ -498,7 +504,8 @@ msgstr "" msgid "All SPI peripherals are in use" msgstr "" -#: ports/espressif/common-hal/busio/UART.c ports/nordic/common-hal/busio/UART.c +#: ports/analog/common-hal/busio/UART.c ports/espressif/common-hal/busio/UART.c +#: ports/nordic/common-hal/busio/UART.c msgid "All UART peripherals are in use" msgstr "" @@ -649,6 +656,7 @@ msgstr "" msgid "Boot device must be first (interface #0)." msgstr "" +#: ports/analog/common-hal/busio/UART.c #: ports/mimxrt10xx/common-hal/busio/UART.c msgid "Both RX and TX required for flow control" msgstr "" @@ -844,6 +852,10 @@ msgstr "" msgid "Coordinate arrays types have different sizes" msgstr "" +#: shared-module/usb/core/Device.c +msgid "Could not allocate DMA capable buffer" +msgstr "" + #: ports/espressif/common-hal/rclcpy/Publisher.c msgid "Could not publish to ROS topic" msgstr "" @@ -987,7 +999,7 @@ msgid "" "Failed to add service TXT record; non-string or bytes found in txt_records" msgstr "" -#: shared-module/rgbmatrix/RGBMatrix.c +#: ports/analog/common-hal/busio/UART.c shared-module/rgbmatrix/RGBMatrix.c msgid "Failed to allocate %q buffer" msgstr "" @@ -1045,6 +1057,10 @@ msgstr "" msgid "Failed to release mutex, err 0x%04x" msgstr "" +#: ports/analog/common-hal/busio/SPI.c +msgid "Failed to set SPI Clock Mode" +msgstr "" + #: ports/zephyr-cp/common-hal/wifi/Radio.c msgid "Failed to set hostname" msgstr "" @@ -1247,6 +1263,10 @@ msgstr "" msgid "Interrupted by output function" msgstr "" +#: ports/analog/common-hal/busio/UART.c +#: ports/analog/peripherals/max32690/max32_i2c.c +#: ports/analog/peripherals/max32690/max32_spi.c +#: ports/analog/peripherals/max32690/max32_uart.c #: ports/espressif/common-hal/_bleio/Service.c #: ports/espressif/common-hal/espulp/ULP.c #: ports/espressif/common-hal/microcontroller/Processor.c @@ -1339,6 +1359,7 @@ msgstr "" msgid "Invalid socket for TLS" msgstr "" +#: ports/analog/common-hal/busio/SPI.c #: ports/espressif/common-hal/espidf/__init__.c #: ports/nordic/common-hal/_bleio/__init__.c msgid "Invalid state" @@ -1394,6 +1415,10 @@ msgstr "" msgid "Mapping must be a tuple" msgstr "" +#: py/persistentcode.c +msgid "MicroPython .mpy file; use CircuitPython mpy-cross" +msgstr "" + #: ports/raspberrypi/bindings/rp2pio/StateMachine.c #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c msgid "Mismatched data size" @@ -1894,6 +1919,7 @@ msgstr "" msgid "ROS topic failed to initialize" msgstr "" +#: ports/analog/common-hal/busio/UART.c #: ports/atmel-samd/common-hal/busio/UART.c ports/cxd56/common-hal/busio/UART.c #: ports/nordic/common-hal/busio/UART.c ports/stm/common-hal/busio/UART.c msgid "RS485" @@ -1986,6 +2012,10 @@ msgstr "" msgid "SPI init error" msgstr "" +#: ports/analog/common-hal/busio/SPI.c +msgid "SPI needs MOSI, MISO, and SCK" +msgstr "" + #: ports/raspberrypi/common-hal/busio/SPI.c msgid "SPI peripheral in use" msgstr "" @@ -2117,6 +2147,10 @@ msgstr "" msgid "Timeout is too long: Maximum timeout length is %d seconds" msgstr "" +#: ports/analog/common-hal/busio/UART.c +msgid "Timeout must be < 100 seconds" +msgstr "" + #: ports/atmel-samd/common-hal/audiobusio/I2SOut.c msgid "Too many channels in sample" msgstr "" @@ -2160,6 +2194,10 @@ msgstr "" msgid "UART init" msgstr "" +#: ports/analog/common-hal/busio/UART.c +msgid "UART needs TX & RX" +msgstr "" + #: ports/raspberrypi/common-hal/busio/UART.c msgid "UART peripheral in use" msgstr "" @@ -2168,6 +2206,14 @@ msgstr "" msgid "UART re-init" msgstr "" +#: ports/analog/common-hal/busio/UART.c +msgid "UART read error" +msgstr "" + +#: ports/analog/common-hal/busio/UART.c +msgid "UART transaction timeout" +msgstr "" + #: ports/stm/common-hal/busio/UART.c msgid "UART write" msgstr "" diff --git a/locale/cs.po b/locale/cs.po index 58785e0a5d75a..fa1cb2d40b738 100644 --- a/locale/cs.po +++ b/locale/cs.po @@ -141,6 +141,7 @@ msgstr "Index %q je mimo rozsah" msgid "%q indices must be integers, not %s" msgstr "Indexy %q musí být celá čísla, nikoli %s" +#: ports/analog/common-hal/busio/SPI.c ports/analog/common-hal/busio/UART.c #: shared-module/bitbangio/SPI.c msgid "%q init failed" msgstr "Inicializace %q selhala" @@ -169,6 +170,10 @@ msgstr "Délka %q musí být <= %d" msgid "%q length must be >= %d" msgstr "Délka %q musí být >= %d" +#: py/runtime.c +msgid "%q moved from %q to %q" +msgstr "" + #: py/argcheck.c msgid "%q must be %d" msgstr "%q musí být %d" @@ -248,6 +253,7 @@ msgstr "%q musí být mocnina 2" msgid "%q out of bounds" msgstr "%q je mimo hranice" +#: ports/analog/common-hal/busio/SPI.c #: ports/atmel-samd/common-hal/pulseio/PulseIn.c #: ports/cxd56/common-hal/pulseio/PulseIn.c #: ports/nordic/common-hal/pulseio/PulseIn.c @@ -509,7 +515,8 @@ msgstr "Všechny RX FIFO jsou používány" msgid "All SPI peripherals are in use" msgstr "Všechny SPI periferie jsou používány" -#: ports/espressif/common-hal/busio/UART.c ports/nordic/common-hal/busio/UART.c +#: ports/analog/common-hal/busio/UART.c ports/espressif/common-hal/busio/UART.c +#: ports/nordic/common-hal/busio/UART.c msgid "All UART peripherals are in use" msgstr "Všechny UART periferie jsou používány" @@ -662,6 +669,7 @@ msgstr "Velikost bitmapy a počet bitů na hodnotu se musí shodovat" msgid "Boot device must be first (interface #0)." msgstr "Bootovací zařízení musí být první (rozhraní #0)." +#: ports/analog/common-hal/busio/UART.c #: ports/mimxrt10xx/common-hal/busio/UART.c msgid "Both RX and TX required for flow control" msgstr "RX a TX jsou vyžadovány pro kontrolu toku" @@ -861,6 +869,10 @@ msgstr "Pole souřadnic mají různé délky" msgid "Coordinate arrays types have different sizes" msgstr "" +#: shared-module/usb/core/Device.c +msgid "Could not allocate DMA capable buffer" +msgstr "" + #: ports/espressif/common-hal/rclcpy/Publisher.c msgid "Could not publish to ROS topic" msgstr "" @@ -1005,7 +1017,7 @@ msgid "" "Failed to add service TXT record; non-string or bytes found in txt_records" msgstr "" -#: shared-module/rgbmatrix/RGBMatrix.c +#: ports/analog/common-hal/busio/UART.c shared-module/rgbmatrix/RGBMatrix.c msgid "Failed to allocate %q buffer" msgstr "Chyba alokace %q bufferu" @@ -1063,6 +1075,10 @@ msgstr "" msgid "Failed to release mutex, err 0x%04x" msgstr "Nepodařilo se uvolnit mutex, err 0x%04x" +#: ports/analog/common-hal/busio/SPI.c +msgid "Failed to set SPI Clock Mode" +msgstr "" + #: ports/zephyr-cp/common-hal/wifi/Radio.c msgid "Failed to set hostname" msgstr "" @@ -1267,6 +1283,10 @@ msgstr "Chyba přerušení." msgid "Interrupted by output function" msgstr "" +#: ports/analog/common-hal/busio/UART.c +#: ports/analog/peripherals/max32690/max32_i2c.c +#: ports/analog/peripherals/max32690/max32_spi.c +#: ports/analog/peripherals/max32690/max32_uart.c #: ports/espressif/common-hal/_bleio/Service.c #: ports/espressif/common-hal/espulp/ULP.c #: ports/espressif/common-hal/microcontroller/Processor.c @@ -1359,6 +1379,7 @@ msgstr "Chybná velikost" msgid "Invalid socket for TLS" msgstr "Chybný soket pro TLS" +#: ports/analog/common-hal/busio/SPI.c #: ports/espressif/common-hal/espidf/__init__.c #: ports/nordic/common-hal/_bleio/__init__.c msgid "Invalid state" @@ -1414,6 +1435,10 @@ msgstr "" msgid "Mapping must be a tuple" msgstr "" +#: py/persistentcode.c +msgid "MicroPython .mpy file; use CircuitPython mpy-cross" +msgstr "" + #: ports/raspberrypi/bindings/rp2pio/StateMachine.c #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c msgid "Mismatched data size" @@ -1917,6 +1942,7 @@ msgstr "" msgid "ROS topic failed to initialize" msgstr "" +#: ports/analog/common-hal/busio/UART.c #: ports/atmel-samd/common-hal/busio/UART.c ports/cxd56/common-hal/busio/UART.c #: ports/nordic/common-hal/busio/UART.c ports/stm/common-hal/busio/UART.c msgid "RS485" @@ -2009,6 +2035,10 @@ msgstr "Konfigurace SPI selhala" msgid "SPI init error" msgstr "Chyba inicializace SPI" +#: ports/analog/common-hal/busio/SPI.c +msgid "SPI needs MOSI, MISO, and SCK" +msgstr "" + #: ports/raspberrypi/common-hal/busio/SPI.c msgid "SPI peripheral in use" msgstr "SPI periferie je používána" @@ -2140,6 +2170,10 @@ msgstr "Čas je v minulosti." msgid "Timeout is too long: Maximum timeout length is %d seconds" msgstr "Časový limit je příliš dlouhý: maximální limit je %d vteřin" +#: ports/analog/common-hal/busio/UART.c +msgid "Timeout must be < 100 seconds" +msgstr "" + #: ports/atmel-samd/common-hal/audiobusio/I2SOut.c msgid "Too many channels in sample" msgstr "V samplu je příliš mnoho kanálů" @@ -2184,6 +2218,10 @@ msgstr "De-inicializace UART" msgid "UART init" msgstr "Inicializace UART" +#: ports/analog/common-hal/busio/UART.c +msgid "UART needs TX & RX" +msgstr "" + #: ports/raspberrypi/common-hal/busio/UART.c msgid "UART peripheral in use" msgstr "UART periférie je používána" @@ -2192,6 +2230,14 @@ msgstr "UART periférie je používána" msgid "UART re-init" msgstr "Opětovná inicializace UART" +#: ports/analog/common-hal/busio/UART.c +msgid "UART read error" +msgstr "" + +#: ports/analog/common-hal/busio/UART.c +msgid "UART transaction timeout" +msgstr "" + #: ports/stm/common-hal/busio/UART.c msgid "UART write" msgstr "Zápis na UART" diff --git a/locale/el.po b/locale/el.po index 082ae20351e3b..91624ceb08672 100644 --- a/locale/el.po +++ b/locale/el.po @@ -145,6 +145,7 @@ msgstr "%q δείκτης εκτός εμβέλειας" msgid "%q indices must be integers, not %s" msgstr "%q δείκτες πρέπει να είναι ακέραιοι, όχι %s" +#: ports/analog/common-hal/busio/SPI.c ports/analog/common-hal/busio/UART.c #: shared-module/bitbangio/SPI.c msgid "%q init failed" msgstr "%q εκκίνηση απέτυχε" @@ -173,6 +174,10 @@ msgstr "%q μήκος πρέπει να είναι <= %d" msgid "%q length must be >= %d" msgstr "%q μήκος πρέπει να είναι >= %d" +#: py/runtime.c +msgid "%q moved from %q to %q" +msgstr "%q μετακινήθηκε από το %q στο %q" + #: py/argcheck.c msgid "%q must be %d" msgstr "%q πρέπει να είναι %d" @@ -252,6 +257,7 @@ msgstr "%q πρέπει να είναι δύναμη του 2" msgid "%q out of bounds" msgstr "%q εκτός ορίων" +#: ports/analog/common-hal/busio/SPI.c #: ports/atmel-samd/common-hal/pulseio/PulseIn.c #: ports/cxd56/common-hal/pulseio/PulseIn.c #: ports/nordic/common-hal/pulseio/PulseIn.c @@ -513,7 +519,8 @@ msgstr "Όλα τα RX FIFOs είναι σε χρήση" msgid "All SPI peripherals are in use" msgstr "Όλα τα SPI περιφεριακά είναι σε χρήση" -#: ports/espressif/common-hal/busio/UART.c ports/nordic/common-hal/busio/UART.c +#: ports/analog/common-hal/busio/UART.c ports/espressif/common-hal/busio/UART.c +#: ports/nordic/common-hal/busio/UART.c msgid "All UART peripherals are in use" msgstr "Όλα τα UART περιφεριακά ειναι σε χρήση" @@ -666,6 +673,7 @@ msgstr "Το μέγεθος του bitmap και τα bits ανα τιμή πρ msgid "Boot device must be first (interface #0)." msgstr "Η συσκευή εκκίνησης πρέπει να επιλεχθεί πρώτα (διεπαφή #0)." +#: ports/analog/common-hal/busio/UART.c #: ports/mimxrt10xx/common-hal/busio/UART.c msgid "Both RX and TX required for flow control" msgstr "Και RX και TX απαιτούνται για έλεγχο flow" @@ -867,6 +875,10 @@ msgstr "" msgid "Coordinate arrays types have different sizes" msgstr "" +#: shared-module/usb/core/Device.c +msgid "Could not allocate DMA capable buffer" +msgstr "" + #: ports/espressif/common-hal/rclcpy/Publisher.c msgid "Could not publish to ROS topic" msgstr "" @@ -1013,7 +1025,7 @@ msgid "" "Failed to add service TXT record; non-string or bytes found in txt_records" msgstr "" -#: shared-module/rgbmatrix/RGBMatrix.c +#: ports/analog/common-hal/busio/UART.c shared-module/rgbmatrix/RGBMatrix.c msgid "Failed to allocate %q buffer" msgstr "" @@ -1071,6 +1083,10 @@ msgstr "" msgid "Failed to release mutex, err 0x%04x" msgstr "" +#: ports/analog/common-hal/busio/SPI.c +msgid "Failed to set SPI Clock Mode" +msgstr "" + #: ports/zephyr-cp/common-hal/wifi/Radio.c msgid "Failed to set hostname" msgstr "" @@ -1273,6 +1289,10 @@ msgstr "" msgid "Interrupted by output function" msgstr "" +#: ports/analog/common-hal/busio/UART.c +#: ports/analog/peripherals/max32690/max32_i2c.c +#: ports/analog/peripherals/max32690/max32_spi.c +#: ports/analog/peripherals/max32690/max32_uart.c #: ports/espressif/common-hal/_bleio/Service.c #: ports/espressif/common-hal/espulp/ULP.c #: ports/espressif/common-hal/microcontroller/Processor.c @@ -1365,6 +1385,7 @@ msgstr "" msgid "Invalid socket for TLS" msgstr "" +#: ports/analog/common-hal/busio/SPI.c #: ports/espressif/common-hal/espidf/__init__.c #: ports/nordic/common-hal/_bleio/__init__.c msgid "Invalid state" @@ -1420,6 +1441,10 @@ msgstr "" msgid "Mapping must be a tuple" msgstr "" +#: py/persistentcode.c +msgid "MicroPython .mpy file; use CircuitPython mpy-cross" +msgstr "" + #: ports/raspberrypi/bindings/rp2pio/StateMachine.c #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c msgid "Mismatched data size" @@ -1922,6 +1947,7 @@ msgstr "" msgid "ROS topic failed to initialize" msgstr "" +#: ports/analog/common-hal/busio/UART.c #: ports/atmel-samd/common-hal/busio/UART.c ports/cxd56/common-hal/busio/UART.c #: ports/nordic/common-hal/busio/UART.c ports/stm/common-hal/busio/UART.c msgid "RS485" @@ -2014,6 +2040,10 @@ msgstr "" msgid "SPI init error" msgstr "" +#: ports/analog/common-hal/busio/SPI.c +msgid "SPI needs MOSI, MISO, and SCK" +msgstr "" + #: ports/raspberrypi/common-hal/busio/SPI.c msgid "SPI peripheral in use" msgstr "" @@ -2145,6 +2175,10 @@ msgstr "" msgid "Timeout is too long: Maximum timeout length is %d seconds" msgstr "" +#: ports/analog/common-hal/busio/UART.c +msgid "Timeout must be < 100 seconds" +msgstr "" + #: ports/atmel-samd/common-hal/audiobusio/I2SOut.c msgid "Too many channels in sample" msgstr "" @@ -2188,6 +2222,10 @@ msgstr "" msgid "UART init" msgstr "" +#: ports/analog/common-hal/busio/UART.c +msgid "UART needs TX & RX" +msgstr "" + #: ports/raspberrypi/common-hal/busio/UART.c msgid "UART peripheral in use" msgstr "" @@ -2196,6 +2234,14 @@ msgstr "" msgid "UART re-init" msgstr "" +#: ports/analog/common-hal/busio/UART.c +msgid "UART read error" +msgstr "" + +#: ports/analog/common-hal/busio/UART.c +msgid "UART transaction timeout" +msgstr "" + #: ports/stm/common-hal/busio/UART.c msgid "UART write" msgstr "" @@ -4537,9 +4583,6 @@ msgstr "" msgid "zi must be of shape (n_section, 2)" msgstr "" -#~ msgid "%q moved from %q to %q" -#~ msgstr "%q μετακινήθηκε από το %q στο %q" - #, c-format #~ msgid "%%c requires int or char" #~ msgstr "%%c απαιτεί ακέραιο ή χαρακτήρα" diff --git a/locale/hi.po b/locale/hi.po index 8e4aa0990aee7..1ab48338ebaba 100644 --- a/locale/hi.po +++ b/locale/hi.po @@ -132,6 +132,7 @@ msgstr "" msgid "%q indices must be integers, not %s" msgstr "" +#: ports/analog/common-hal/busio/SPI.c ports/analog/common-hal/busio/UART.c #: shared-module/bitbangio/SPI.c msgid "%q init failed" msgstr "" @@ -160,6 +161,10 @@ msgstr "" msgid "%q length must be >= %d" msgstr "" +#: py/runtime.c +msgid "%q moved from %q to %q" +msgstr "" + #: py/argcheck.c msgid "%q must be %d" msgstr "" @@ -239,6 +244,7 @@ msgstr "" msgid "%q out of bounds" msgstr "" +#: ports/analog/common-hal/busio/SPI.c #: ports/atmel-samd/common-hal/pulseio/PulseIn.c #: ports/cxd56/common-hal/pulseio/PulseIn.c #: ports/nordic/common-hal/pulseio/PulseIn.c @@ -500,7 +506,8 @@ msgstr "" msgid "All SPI peripherals are in use" msgstr "" -#: ports/espressif/common-hal/busio/UART.c ports/nordic/common-hal/busio/UART.c +#: ports/analog/common-hal/busio/UART.c ports/espressif/common-hal/busio/UART.c +#: ports/nordic/common-hal/busio/UART.c msgid "All UART peripherals are in use" msgstr "" @@ -651,6 +658,7 @@ msgstr "" msgid "Boot device must be first (interface #0)." msgstr "" +#: ports/analog/common-hal/busio/UART.c #: ports/mimxrt10xx/common-hal/busio/UART.c msgid "Both RX and TX required for flow control" msgstr "" @@ -846,6 +854,10 @@ msgstr "" msgid "Coordinate arrays types have different sizes" msgstr "" +#: shared-module/usb/core/Device.c +msgid "Could not allocate DMA capable buffer" +msgstr "" + #: ports/espressif/common-hal/rclcpy/Publisher.c msgid "Could not publish to ROS topic" msgstr "" @@ -989,7 +1001,7 @@ msgid "" "Failed to add service TXT record; non-string or bytes found in txt_records" msgstr "" -#: shared-module/rgbmatrix/RGBMatrix.c +#: ports/analog/common-hal/busio/UART.c shared-module/rgbmatrix/RGBMatrix.c msgid "Failed to allocate %q buffer" msgstr "" @@ -1047,6 +1059,10 @@ msgstr "" msgid "Failed to release mutex, err 0x%04x" msgstr "" +#: ports/analog/common-hal/busio/SPI.c +msgid "Failed to set SPI Clock Mode" +msgstr "" + #: ports/zephyr-cp/common-hal/wifi/Radio.c msgid "Failed to set hostname" msgstr "" @@ -1249,6 +1265,10 @@ msgstr "" msgid "Interrupted by output function" msgstr "" +#: ports/analog/common-hal/busio/UART.c +#: ports/analog/peripherals/max32690/max32_i2c.c +#: ports/analog/peripherals/max32690/max32_spi.c +#: ports/analog/peripherals/max32690/max32_uart.c #: ports/espressif/common-hal/_bleio/Service.c #: ports/espressif/common-hal/espulp/ULP.c #: ports/espressif/common-hal/microcontroller/Processor.c @@ -1341,6 +1361,7 @@ msgstr "" msgid "Invalid socket for TLS" msgstr "" +#: ports/analog/common-hal/busio/SPI.c #: ports/espressif/common-hal/espidf/__init__.c #: ports/nordic/common-hal/_bleio/__init__.c msgid "Invalid state" @@ -1396,6 +1417,10 @@ msgstr "" msgid "Mapping must be a tuple" msgstr "" +#: py/persistentcode.c +msgid "MicroPython .mpy file; use CircuitPython mpy-cross" +msgstr "" + #: ports/raspberrypi/bindings/rp2pio/StateMachine.c #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c msgid "Mismatched data size" @@ -1896,6 +1921,7 @@ msgstr "" msgid "ROS topic failed to initialize" msgstr "" +#: ports/analog/common-hal/busio/UART.c #: ports/atmel-samd/common-hal/busio/UART.c ports/cxd56/common-hal/busio/UART.c #: ports/nordic/common-hal/busio/UART.c ports/stm/common-hal/busio/UART.c msgid "RS485" @@ -1988,6 +2014,10 @@ msgstr "" msgid "SPI init error" msgstr "" +#: ports/analog/common-hal/busio/SPI.c +msgid "SPI needs MOSI, MISO, and SCK" +msgstr "" + #: ports/raspberrypi/common-hal/busio/SPI.c msgid "SPI peripheral in use" msgstr "" @@ -2119,6 +2149,10 @@ msgstr "" msgid "Timeout is too long: Maximum timeout length is %d seconds" msgstr "" +#: ports/analog/common-hal/busio/UART.c +msgid "Timeout must be < 100 seconds" +msgstr "" + #: ports/atmel-samd/common-hal/audiobusio/I2SOut.c msgid "Too many channels in sample" msgstr "" @@ -2162,6 +2196,10 @@ msgstr "" msgid "UART init" msgstr "" +#: ports/analog/common-hal/busio/UART.c +msgid "UART needs TX & RX" +msgstr "" + #: ports/raspberrypi/common-hal/busio/UART.c msgid "UART peripheral in use" msgstr "" @@ -2170,6 +2208,14 @@ msgstr "" msgid "UART re-init" msgstr "" +#: ports/analog/common-hal/busio/UART.c +msgid "UART read error" +msgstr "" + +#: ports/analog/common-hal/busio/UART.c +msgid "UART transaction timeout" +msgstr "" + #: ports/stm/common-hal/busio/UART.c msgid "UART write" msgstr "" diff --git a/locale/ko.po b/locale/ko.po index 8bde494eeec78..35bfed4de08e9 100644 --- a/locale/ko.po +++ b/locale/ko.po @@ -143,6 +143,7 @@ msgstr "%q 인덱스 범위를 벗어났습니다" msgid "%q indices must be integers, not %s" msgstr "%q 인덱스는 %s 가 아닌 정수 여야합니다" +#: ports/analog/common-hal/busio/SPI.c ports/analog/common-hal/busio/UART.c #: shared-module/bitbangio/SPI.c msgid "%q init failed" msgstr "%q 초기화 실패" @@ -171,6 +172,11 @@ msgstr "%q 길이는 <= %d>여야 합니다" msgid "%q length must be >= %d" msgstr "%q 길이는 >= %d이어야 합니다" +#: py/runtime.c +#, fuzzy +msgid "%q moved from %q to %q" +msgstr "%q가 %q에서 %q로 이동했습니다" + #: py/argcheck.c msgid "%q must be %d" msgstr "%q는 %d이어야 합니다" @@ -256,6 +262,7 @@ msgstr "%q는 2의 거듭제곱이어야 합니다" msgid "%q out of bounds" msgstr "%q가 경계를 벗어남" +#: ports/analog/common-hal/busio/SPI.c #: ports/atmel-samd/common-hal/pulseio/PulseIn.c #: ports/cxd56/common-hal/pulseio/PulseIn.c #: ports/nordic/common-hal/pulseio/PulseIn.c @@ -531,7 +538,8 @@ msgstr "모든 RX FIFOs가 사용 중입니다" msgid "All SPI peripherals are in use" msgstr "사용중인 모든 SPI주변 기기" -#: ports/espressif/common-hal/busio/UART.c ports/nordic/common-hal/busio/UART.c +#: ports/analog/common-hal/busio/UART.c ports/espressif/common-hal/busio/UART.c +#: ports/nordic/common-hal/busio/UART.c msgid "All UART peripherals are in use" msgstr "사용중인 모든 UART주변 기기" @@ -692,6 +700,7 @@ msgstr "비트맵 크기와 값 당 비트가 일치해야 합니다" msgid "Boot device must be first (interface #0)." msgstr "부팅 장치는 첫 번째(인터페이스 #0)여야 합니다." +#: ports/analog/common-hal/busio/UART.c #: ports/mimxrt10xx/common-hal/busio/UART.c msgid "Both RX and TX required for flow control" msgstr "플로우 제어에 RX와 TX가 모두 필요합니다" @@ -890,6 +899,10 @@ msgstr "좌표 배열의 길이가 다릅니다" msgid "Coordinate arrays types have different sizes" msgstr "좌표 배열 유형은 크기가 다릅니다" +#: shared-module/usb/core/Device.c +msgid "Could not allocate DMA capable buffer" +msgstr "" + #: ports/espressif/common-hal/rclcpy/Publisher.c msgid "Could not publish to ROS topic" msgstr "" @@ -1037,7 +1050,7 @@ msgstr "" "서비스 TXT 레코드를 추가하는 것에 실패했습니다; txt_records에서 비문자열 또" "는 바이트가 발견되었습니다" -#: shared-module/rgbmatrix/RGBMatrix.c +#: ports/analog/common-hal/busio/UART.c shared-module/rgbmatrix/RGBMatrix.c msgid "Failed to allocate %q buffer" msgstr "%q 버퍼 할당에 실패했습니다" @@ -1095,6 +1108,10 @@ msgstr "" msgid "Failed to release mutex, err 0x%04x" msgstr "뮤텍스 해제에 실패했습니다, 오류 0x%04x" +#: ports/analog/common-hal/busio/SPI.c +msgid "Failed to set SPI Clock Mode" +msgstr "" + #: ports/zephyr-cp/common-hal/wifi/Radio.c msgid "Failed to set hostname" msgstr "" @@ -1300,6 +1317,10 @@ msgstr "인터럽트 오류." msgid "Interrupted by output function" msgstr "출력 함수로 인해 종료되었다" +#: ports/analog/common-hal/busio/UART.c +#: ports/analog/peripherals/max32690/max32_i2c.c +#: ports/analog/peripherals/max32690/max32_spi.c +#: ports/analog/peripherals/max32690/max32_uart.c #: ports/espressif/common-hal/_bleio/Service.c #: ports/espressif/common-hal/espulp/ULP.c #: ports/espressif/common-hal/microcontroller/Processor.c @@ -1392,6 +1413,7 @@ msgstr "잘못된 크기" msgid "Invalid socket for TLS" msgstr "TLS에 대한 잘못된 소켓" +#: ports/analog/common-hal/busio/SPI.c #: ports/espressif/common-hal/espidf/__init__.c #: ports/nordic/common-hal/_bleio/__init__.c msgid "Invalid state" @@ -1447,6 +1469,10 @@ msgstr "" msgid "Mapping must be a tuple" msgstr "매핑은 투플이어야 합니다" +#: py/persistentcode.c +msgid "MicroPython .mpy file; use CircuitPython mpy-cross" +msgstr "" + #: ports/raspberrypi/bindings/rp2pio/StateMachine.c #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c msgid "Mismatched data size" @@ -1970,6 +1996,7 @@ msgstr "" msgid "ROS topic failed to initialize" msgstr "" +#: ports/analog/common-hal/busio/UART.c #: ports/atmel-samd/common-hal/busio/UART.c ports/cxd56/common-hal/busio/UART.c #: ports/nordic/common-hal/busio/UART.c ports/stm/common-hal/busio/UART.c msgid "RS485" @@ -2062,6 +2089,10 @@ msgstr "" msgid "SPI init error" msgstr "" +#: ports/analog/common-hal/busio/SPI.c +msgid "SPI needs MOSI, MISO, and SCK" +msgstr "" + #: ports/raspberrypi/common-hal/busio/SPI.c msgid "SPI peripheral in use" msgstr "" @@ -2193,6 +2224,10 @@ msgstr "" msgid "Timeout is too long: Maximum timeout length is %d seconds" msgstr "" +#: ports/analog/common-hal/busio/UART.c +msgid "Timeout must be < 100 seconds" +msgstr "" + #: ports/atmel-samd/common-hal/audiobusio/I2SOut.c msgid "Too many channels in sample" msgstr "" @@ -2236,6 +2271,10 @@ msgstr "" msgid "UART init" msgstr "" +#: ports/analog/common-hal/busio/UART.c +msgid "UART needs TX & RX" +msgstr "" + #: ports/raspberrypi/common-hal/busio/UART.c msgid "UART peripheral in use" msgstr "" @@ -2244,6 +2283,14 @@ msgstr "" msgid "UART re-init" msgstr "" +#: ports/analog/common-hal/busio/UART.c +msgid "UART read error" +msgstr "" + +#: ports/analog/common-hal/busio/UART.c +msgid "UART transaction timeout" +msgstr "" + #: ports/stm/common-hal/busio/UART.c msgid "UART write" msgstr "" @@ -4586,10 +4633,6 @@ msgstr "" msgid "zi must be of shape (n_section, 2)" msgstr "" -#, fuzzy -#~ msgid "%q moved from %q to %q" -#~ msgstr "%q가 %q에서 %q로 이동했습니다" - #, c-format #~ msgid "%%c requires int or char" #~ msgstr "%%c 전수(int)또는 캐릭터(char)필요합니다" diff --git a/locale/ru.po b/locale/ru.po index d3d9415230455..af7c8bca194d2 100644 --- a/locale/ru.po +++ b/locale/ru.po @@ -145,6 +145,7 @@ msgstr "Индекс %q вне диапазона" msgid "%q indices must be integers, not %s" msgstr "Индексы %q должны быть целыми числами, а не %s" +#: ports/analog/common-hal/busio/SPI.c ports/analog/common-hal/busio/UART.c #: shared-module/bitbangio/SPI.c msgid "%q init failed" msgstr "Инициализация %q не удалась" @@ -173,6 +174,10 @@ msgstr "Длинна %q должна быть <= %d" msgid "%q length must be >= %d" msgstr "Длинна %q должна быть >= %d" +#: py/runtime.c +msgid "%q moved from %q to %q" +msgstr "%q переместился из %q в %q" + #: py/argcheck.c msgid "%q must be %d" msgstr "%q должно быть %d" @@ -252,6 +257,7 @@ msgstr "%q должен быть во 2-й степени" msgid "%q out of bounds" msgstr "%q за пределом" +#: ports/analog/common-hal/busio/SPI.c #: ports/atmel-samd/common-hal/pulseio/PulseIn.c #: ports/cxd56/common-hal/pulseio/PulseIn.c #: ports/nordic/common-hal/pulseio/PulseIn.c @@ -513,7 +519,8 @@ msgstr "Все RX FIFO уже используются" msgid "All SPI peripherals are in use" msgstr "Все периферийные устройства SPI уже используются" -#: ports/espressif/common-hal/busio/UART.c ports/nordic/common-hal/busio/UART.c +#: ports/analog/common-hal/busio/UART.c ports/espressif/common-hal/busio/UART.c +#: ports/nordic/common-hal/busio/UART.c msgid "All UART peripherals are in use" msgstr "Все периферийные устройства UART уже используются" @@ -667,6 +674,7 @@ msgstr "" msgid "Boot device must be first (interface #0)." msgstr "Загрузочное устройство должно быть первым (интерфейс #0)." +#: ports/analog/common-hal/busio/UART.c #: ports/mimxrt10xx/common-hal/busio/UART.c msgid "Both RX and TX required for flow control" msgstr "Для управления потоком требуется как RX так и TX" @@ -870,6 +878,10 @@ msgstr "Координатные массивы имеют разные длин msgid "Coordinate arrays types have different sizes" msgstr "Типы массивов координат имеют разные размеры" +#: shared-module/usb/core/Device.c +msgid "Could not allocate DMA capable buffer" +msgstr "" + #: ports/espressif/common-hal/rclcpy/Publisher.c msgid "Could not publish to ROS topic" msgstr "" @@ -1018,7 +1030,7 @@ msgstr "" "Не удалось добавить служебную TXT-запись; в txt_records обнаружена нестрока " "или байт" -#: shared-module/rgbmatrix/RGBMatrix.c +#: ports/analog/common-hal/busio/UART.c shared-module/rgbmatrix/RGBMatrix.c msgid "Failed to allocate %q buffer" msgstr "Не удалось выделить буфер %q" @@ -1076,6 +1088,10 @@ msgstr "" msgid "Failed to release mutex, err 0x%04x" msgstr "Не удалось освободить mutex, ошибка 0x%04x" +#: ports/analog/common-hal/busio/SPI.c +msgid "Failed to set SPI Clock Mode" +msgstr "" + #: ports/zephyr-cp/common-hal/wifi/Radio.c msgid "Failed to set hostname" msgstr "" @@ -1288,6 +1304,10 @@ msgstr "Прерванная ошибка." msgid "Interrupted by output function" msgstr "Прерывается функцией выхода" +#: ports/analog/common-hal/busio/UART.c +#: ports/analog/peripherals/max32690/max32_i2c.c +#: ports/analog/peripherals/max32690/max32_spi.c +#: ports/analog/peripherals/max32690/max32_uart.c #: ports/espressif/common-hal/_bleio/Service.c #: ports/espressif/common-hal/espulp/ULP.c #: ports/espressif/common-hal/microcontroller/Processor.c @@ -1380,6 +1400,7 @@ msgstr "Неверный размер" msgid "Invalid socket for TLS" msgstr "Неверный сокет для TLS" +#: ports/analog/common-hal/busio/SPI.c #: ports/espressif/common-hal/espidf/__init__.c #: ports/nordic/common-hal/_bleio/__init__.c msgid "Invalid state" @@ -1435,6 +1456,10 @@ msgstr "" msgid "Mapping must be a tuple" msgstr "Сопоставление должно быть кортежом" +#: py/persistentcode.c +msgid "MicroPython .mpy file; use CircuitPython mpy-cross" +msgstr "" + #: ports/raspberrypi/bindings/rp2pio/StateMachine.c #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c msgid "Mismatched data size" @@ -1947,6 +1972,7 @@ msgstr "" msgid "ROS topic failed to initialize" msgstr "" +#: ports/analog/common-hal/busio/UART.c #: ports/atmel-samd/common-hal/busio/UART.c ports/cxd56/common-hal/busio/UART.c #: ports/nordic/common-hal/busio/UART.c ports/stm/common-hal/busio/UART.c msgid "RS485" @@ -2039,6 +2065,10 @@ msgstr "Сбой конфигурации SPI" msgid "SPI init error" msgstr "Ошибка инициализации SPI" +#: ports/analog/common-hal/busio/SPI.c +msgid "SPI needs MOSI, MISO, and SCK" +msgstr "" + #: ports/raspberrypi/common-hal/busio/SPI.c msgid "SPI peripheral in use" msgstr "Используемое периферийное устройство SPI" @@ -2175,6 +2205,10 @@ msgstr "Время в прошлом." msgid "Timeout is too long: Maximum timeout length is %d seconds" msgstr "Таймаут слишком длинный: максимальная длина таймаута %d секунд" +#: ports/analog/common-hal/busio/UART.c +msgid "Timeout must be < 100 seconds" +msgstr "" + #: ports/atmel-samd/common-hal/audiobusio/I2SOut.c msgid "Too many channels in sample" msgstr "Слишком много каналов в выборке" @@ -2218,6 +2252,10 @@ msgstr "Деинициализация UART" msgid "UART init" msgstr "Инициализация UART" +#: ports/analog/common-hal/busio/UART.c +msgid "UART needs TX & RX" +msgstr "" + #: ports/raspberrypi/common-hal/busio/UART.c msgid "UART peripheral in use" msgstr "Используемое периферийное устройство UART" @@ -2226,6 +2264,14 @@ msgstr "Используемое периферийное устройство U msgid "UART re-init" msgstr "Повторная инициализация UART" +#: ports/analog/common-hal/busio/UART.c +msgid "UART read error" +msgstr "" + +#: ports/analog/common-hal/busio/UART.c +msgid "UART transaction timeout" +msgstr "" + #: ports/stm/common-hal/busio/UART.c msgid "UART write" msgstr "Запись UART" @@ -4604,9 +4650,6 @@ msgstr "zi должно быть типа float" msgid "zi must be of shape (n_section, 2)" msgstr "zi должен иметь форму (n_section, 2)" -#~ msgid "%q moved from %q to %q" -#~ msgstr "%q переместился из %q в %q" - #~ msgid "start/end indices" #~ msgstr "Начальные/конечные индексы" diff --git a/locale/tr.po b/locale/tr.po index 10a288553a698..758137eae21f8 100644 --- a/locale/tr.po +++ b/locale/tr.po @@ -143,6 +143,7 @@ msgstr "%q indeksi aralık dışında" msgid "%q indices must be integers, not %s" msgstr "%q indeksleri integer olmalı, %s değil" +#: ports/analog/common-hal/busio/SPI.c ports/analog/common-hal/busio/UART.c #: shared-module/bitbangio/SPI.c msgid "%q init failed" msgstr "%q init başarısız oldu" @@ -171,6 +172,10 @@ msgstr "%q boyutu <= %d olmalıdır" msgid "%q length must be >= %d" msgstr "%q boyutu >= %d olmalıdır" +#: py/runtime.c +msgid "%q moved from %q to %q" +msgstr "" + #: py/argcheck.c msgid "%q must be %d" msgstr "%q, %d olmalıdır" @@ -250,6 +255,7 @@ msgstr "%q, 2'nin kuvveti olmalıdır" msgid "%q out of bounds" msgstr "%q sınırların dışında" +#: ports/analog/common-hal/busio/SPI.c #: ports/atmel-samd/common-hal/pulseio/PulseIn.c #: ports/cxd56/common-hal/pulseio/PulseIn.c #: ports/nordic/common-hal/pulseio/PulseIn.c @@ -511,7 +517,8 @@ msgstr "Tüm RX FIFO'ları kullanımda" msgid "All SPI peripherals are in use" msgstr "Tüm SPI çevre birimleri kullanımda" -#: ports/espressif/common-hal/busio/UART.c ports/nordic/common-hal/busio/UART.c +#: ports/analog/common-hal/busio/UART.c ports/espressif/common-hal/busio/UART.c +#: ports/nordic/common-hal/busio/UART.c msgid "All UART peripherals are in use" msgstr "Tüm UART çevre birimleri kullanımda" @@ -664,6 +671,7 @@ msgstr "Bitmap boyutu ve bit başına değer uyuşmalı" msgid "Boot device must be first (interface #0)." msgstr "" +#: ports/analog/common-hal/busio/UART.c #: ports/mimxrt10xx/common-hal/busio/UART.c msgid "Both RX and TX required for flow control" msgstr "Hem RX hem de TX akış kontrolü için gerekli" @@ -860,6 +868,10 @@ msgstr "" msgid "Coordinate arrays types have different sizes" msgstr "" +#: shared-module/usb/core/Device.c +msgid "Could not allocate DMA capable buffer" +msgstr "" + #: ports/espressif/common-hal/rclcpy/Publisher.c msgid "Could not publish to ROS topic" msgstr "" @@ -1003,7 +1015,7 @@ msgid "" "Failed to add service TXT record; non-string or bytes found in txt_records" msgstr "" -#: shared-module/rgbmatrix/RGBMatrix.c +#: ports/analog/common-hal/busio/UART.c shared-module/rgbmatrix/RGBMatrix.c msgid "Failed to allocate %q buffer" msgstr "" @@ -1061,6 +1073,10 @@ msgstr "" msgid "Failed to release mutex, err 0x%04x" msgstr "Muteks serbest bırakılamadı, err 0x%04x" +#: ports/analog/common-hal/busio/SPI.c +msgid "Failed to set SPI Clock Mode" +msgstr "" + #: ports/zephyr-cp/common-hal/wifi/Radio.c msgid "Failed to set hostname" msgstr "" @@ -1267,6 +1283,10 @@ msgstr "" msgid "Interrupted by output function" msgstr "" +#: ports/analog/common-hal/busio/UART.c +#: ports/analog/peripherals/max32690/max32_i2c.c +#: ports/analog/peripherals/max32690/max32_spi.c +#: ports/analog/peripherals/max32690/max32_uart.c #: ports/espressif/common-hal/_bleio/Service.c #: ports/espressif/common-hal/espulp/ULP.c #: ports/espressif/common-hal/microcontroller/Processor.c @@ -1360,6 +1380,7 @@ msgstr "Geçersiz boyut" msgid "Invalid socket for TLS" msgstr "TLS için geçersiz soket" +#: ports/analog/common-hal/busio/SPI.c #: ports/espressif/common-hal/espidf/__init__.c #: ports/nordic/common-hal/_bleio/__init__.c msgid "Invalid state" @@ -1415,6 +1436,10 @@ msgstr "" msgid "Mapping must be a tuple" msgstr "Map tuple olmalıdır" +#: py/persistentcode.c +msgid "MicroPython .mpy file; use CircuitPython mpy-cross" +msgstr "" + #: ports/raspberrypi/bindings/rp2pio/StateMachine.c #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c msgid "Mismatched data size" @@ -1918,6 +1943,7 @@ msgstr "" msgid "ROS topic failed to initialize" msgstr "" +#: ports/analog/common-hal/busio/UART.c #: ports/atmel-samd/common-hal/busio/UART.c ports/cxd56/common-hal/busio/UART.c #: ports/nordic/common-hal/busio/UART.c ports/stm/common-hal/busio/UART.c msgid "RS485" @@ -2010,6 +2036,10 @@ msgstr "" msgid "SPI init error" msgstr "" +#: ports/analog/common-hal/busio/SPI.c +msgid "SPI needs MOSI, MISO, and SCK" +msgstr "" + #: ports/raspberrypi/common-hal/busio/SPI.c msgid "SPI peripheral in use" msgstr "" @@ -2141,6 +2171,10 @@ msgstr "" msgid "Timeout is too long: Maximum timeout length is %d seconds" msgstr "" +#: ports/analog/common-hal/busio/UART.c +msgid "Timeout must be < 100 seconds" +msgstr "" + #: ports/atmel-samd/common-hal/audiobusio/I2SOut.c msgid "Too many channels in sample" msgstr "" @@ -2184,6 +2218,10 @@ msgstr "" msgid "UART init" msgstr "" +#: ports/analog/common-hal/busio/UART.c +msgid "UART needs TX & RX" +msgstr "" + #: ports/raspberrypi/common-hal/busio/UART.c msgid "UART peripheral in use" msgstr "" @@ -2192,6 +2230,14 @@ msgstr "" msgid "UART re-init" msgstr "" +#: ports/analog/common-hal/busio/UART.c +msgid "UART read error" +msgstr "" + +#: ports/analog/common-hal/busio/UART.c +msgid "UART transaction timeout" +msgstr "" + #: ports/stm/common-hal/busio/UART.c msgid "UART write" msgstr "" diff --git a/main.c b/main.c index 1f387b7ca3ceb..6c8e9c8804906 100644 --- a/main.c +++ b/main.c @@ -1179,10 +1179,6 @@ void gc_collect(void) { gc_collect_end(); } -// Ports may provide an implementation of this function if it is needed -MP_WEAK void port_gc_collect(void) { -} - size_t gc_get_max_new_split(void) { return port_heap_get_largest_free_size(); } diff --git a/ports/analog/Makefile b/ports/analog/Makefile index ce9082c5e255a..0ae276985f7f4 100644 --- a/ports/analog/Makefile +++ b/ports/analog/Makefile @@ -6,12 +6,13 @@ # # SPDX-License-Identifier: MIT +BOARD ?= apard32690 +CROSS_COMPILE = arm-none-eabi- + # Includes mpconfigboard.mk & mpconfigport.mk, # along with numerous other shared environment makefiles. include ../../py/circuitpy_mkenv.mk -CROSS_COMPILE = arm-none-eabi- - # MCU_SERIES e.g. "max32" # MCU_VARIANT e.g. "max32690" # defined in mpconfigboard.mk @@ -20,6 +21,7 @@ MCU_SERIES_UPPER := $(shell echo $(MCU_SERIES) | tr '[:lower:]' '[:upper:]') MCU_VARIANT_LOWER := $(shell echo $(MCU_VARIANT) | tr '[:upper:]' '[:lower:]') MCU_VARIANT_UPPER := $(shell echo $(MCU_VARIANT) | tr '[:lower:]' '[:upper:]') + # ******************************************************************************* #### MSDK INCLUDES #### # Necessary for msdk makefiles @@ -59,6 +61,7 @@ DIE_TYPE=me18 endif PERIPH_SRC = $(ADI_PERIPH)/Source +PERIPH_INC = $(ADI_PERIPH)/Include/$(MCU_VARIANT_UPPER) INC += -I. INC += -I../.. @@ -75,7 +78,7 @@ INC += \ -I$(TOP)/lib/cmsis/inc \ -I$(CMSIS_ROOT)/Include \ -I$(CMSIS_ROOT)/Device/Maxim/$(MCU_VARIANT_UPPER)/Include \ - -I$(ADI_PERIPH)/Include/$(MCU_VARIANT_UPPER) \ + -I$(PERIPH_INC) \ -I$(PERIPH_SRC)/SYS \ -I$(PERIPH_SRC)/CTB \ -I$(PERIPH_SRC)/DMA \ @@ -85,7 +88,9 @@ INC += \ -I$(PERIPH_SRC)/TMR \ -I$(PERIPH_SRC)/RTC \ -I$(PERIPH_SRC)/UART \ - -I$(PERIPH_SRC)/TRNG + -I$(PERIPH_SRC)/TRNG \ + -I$(PERIPH_SRC)/I2C \ + -I$(PERIPH_SRC)/SPI INC += -I$(CMSIS_ROOT)/Device/Maxim/$(MCU_VARIANT_UPPER)/Source/GCC @@ -120,13 +125,20 @@ SRC_MAX32 += \ $(PERIPH_SRC)/UART/uart_$(DIE_TYPE).c \ $(PERIPH_SRC)/UART/uart_revb.c \ $(PERIPH_SRC)/TRNG/trng_revb.c \ - $(PERIPH_SRC)/TRNG/trng_$(DIE_TYPE).c + $(PERIPH_SRC)/TRNG/trng_$(DIE_TYPE).c \ + $(PERIPH_SRC)/I2C/i2c_$(DIE_TYPE).c \ + $(PERIPH_SRC)/I2C/i2c_reva.c \ + $(PERIPH_SRC)/SPI/spi_$(DIE_TYPE).c \ + $(PERIPH_SRC)/SPI/spi_reva1.c SRC_C += $(SRC_MAX32) \ boards/$(BOARD)/board.c \ boards/$(BOARD)/pins.c \ peripherals/$(MCU_VARIANT_LOWER)/pins.c \ - peripherals/$(MCU_VARIANT_LOWER)/gpios.c + peripherals/$(MCU_VARIANT_LOWER)/gpios.c \ + peripherals/$(MCU_VARIANT_LOWER)/max32_uart.c \ + peripherals/$(MCU_VARIANT_LOWER)/max32_i2c.c \ + peripherals/$(MCU_VARIANT_LOWER)/max32_spi.c # ******************************************************************************* ### Compiler & Linker Flags ### @@ -267,11 +279,16 @@ flash-msdk: -f interface/cmsis-dap.cfg -f target/$(MCU_VARIANT_LOWER).cfg \ -c "program $(BUILD)/firmware.elf verify; init; reset; exit" +flash-openocd-jlink: + $(OPENOCD) -s $(OPENOCD_SCRIPTS) \ + -f interface/jlink.cfg -f target/$(MCU_VARIANT_LOWER).cfg \ + -c "program $(BUILD)/firmware.elf verify; init; reset; exit" + # flash target using JLink JLINK_DEVICE = $(MCU_VARIANT_LOWER) JLINKEXE ?= JLink.exe -JLINKEXE += -if SWD -device ${JLINK_DEVICE} -speed 10000 +JLINKEXE += -if SWD -device ${JLINK_DEVICE} -speed 4000 COMMAND_FILE := tools/flash_max32.jlink flash-jlink: $(BUILD)/firmware.bin diff --git a/ports/analog/background.c b/ports/analog/background.c index ffad007ffa51c..ad840c6dcdabc 100644 --- a/ports/analog/background.c +++ b/ports/analog/background.c @@ -19,19 +19,13 @@ extern const mxc_gpio_cfg_t led_pin[]; extern const int num_leds; /** NOTE: ALL "ticks" refer to a 1/1024 s period */ -static int status_led_ticks = 0; +static int status_ticks = 0; // This function is where port-specific background // tasks should be performed // Execute port specific actions during background tick. Only if ticks are enabled. void port_background_tick(void) { - status_led_ticks++; - - // Set an LED approx. 1/s - if (status_led_ticks > 1024) { - MXC_GPIO_OutToggle(led_pin[2].port, led_pin[2].mask); - status_led_ticks = 0; - } + status_ticks++; } // Execute port specific actions during background tasks. This is before the diff --git a/ports/analog/common-hal/busio/I2C.c b/ports/analog/common-hal/busio/I2C.c new file mode 100644 index 0000000000000..336c04fe039f9 --- /dev/null +++ b/ports/analog/common-hal/busio/I2C.c @@ -0,0 +1,256 @@ +/* + * This file is part of Adafruit for EFR32 project + * + * The MIT License (MIT) + * + * Copyright 2023 Silicon Laboratories Inc. www.silabs.com + * + * 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/I2C.h" +#include "py/mperrno.h" +#include "py/runtime.h" +#include "shared-bindings/microcontroller/__init__.h" +#include "shared-bindings/microcontroller/Pin.h" + +#include "max32_port.h" + +#define I2C_PRIORITY 1 + +// Set each bit to indicate an active I2c +static uint8_t i2c_active = 0; +static volatile int i2c_err; + +// I2C struct for configuring GPIO pins +extern const mxc_gpio_cfg_t i2c_maps[NUM_I2C]; + +// Construct I2C protocol, this function init i2c peripheral +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) { + // Check for NULL Pointers && valid I2C settings + assert(self); + + /* NOTE: The validate_obj_is_free_pin() calls in shared-bindings/busio/I2C.c + will ensure that scl and sda are both pins and cannot be null + ref: https://github.com/adafruit/circuitpython/pull/10413 + */ + + // Assign I2C ID based on pins + int i2c_id = pinsToI2c(sda, scl); + if (i2c_id == -1) { + return; + } else { + self->i2c_id = i2c_id; + self->i2c_regs = MXC_I2C_GET_I2C(i2c_id); + } + + // Check for valid I2C controller + assert((self->i2c_id >= 0) && (self->i2c_id < NUM_I2C)); + assert(!(i2c_active & (1 << self->i2c_id))); + + // Attach I2C pins + self->sda = sda; + self->scl = scl; + common_hal_mcu_pin_claim(self->sda); + common_hal_mcu_pin_claim(self->scl); + + // Clear all flags + MXC_I2C_ClearFlags(self->i2c_regs, 0xFFFFFF, 0xFFFFFF); + + // Init as master, no slave address + MXC_I2C_Shutdown(self->i2c_regs); + MXC_I2C_Init(self->i2c_regs, 1, 0); + + // Set frequency arg (CircuitPython shared-bindings validate) + MXC_I2C_SetFrequency(self->i2c_regs, frequency); + + // Indicate to this module that the I2C is active + i2c_active |= (1 << self->i2c_id); + + // Set the timeout to a default value + if (timeout > 100) { + self->timeout = 1; + } else { + self->timeout = timeout; + } + + return; +} + +// Never reset I2C obj when reload +void common_hal_busio_i2c_never_reset(busio_i2c_obj_t *self) { + common_hal_never_reset_pin(self->sda); + common_hal_never_reset_pin(self->scl); +} + +// Check I2C status, deinited or not +bool common_hal_busio_i2c_deinited(busio_i2c_obj_t *self) { + return self->sda == NULL; +} + +// Deinit i2c obj, reset I2C pin +void common_hal_busio_i2c_deinit(busio_i2c_obj_t *self) { + MXC_I2C_Shutdown(self->i2c_regs); + + common_hal_reset_pin(self->sda); + common_hal_reset_pin(self->scl); + + self->sda = NULL; + self->scl = NULL; +} + +// Probe device in I2C bus +bool common_hal_busio_i2c_probe(busio_i2c_obj_t *self, uint8_t addr) { + uint32_t int_fl0; + bool ret = 0; + + // If not in Master mode, error out (can happen in some error conditions) + if (!(self->i2c_regs->ctrl & MXC_F_I2C_CTRL_MST_MODE)) { + return false; + } + + // Clear FIFOs & all interrupt flags + MXC_I2C_ClearRXFIFO(self->i2c_regs); + MXC_I2C_ClearTXFIFO(self->i2c_regs); + MXC_I2C_ClearFlags(self->i2c_regs, 0xFFFFFF, 0xFFFFFF); + + // Pre-load target address into transmit FIFO + addr = (addr << 1); + self->i2c_regs->fifo = addr; + + // Set start bit & wait for it to clear + MXC_I2C_Start(self->i2c_regs); + + // wait for ACK/NACK + while (!(self->i2c_regs->intfl0 & MXC_F_I2C_INTFL0_ADDR_ACK) && + !(self->i2c_regs->intfl0 & MXC_F_I2C_INTFL0_ADDR_NACK_ERR)) { + } + + // Save interrupt flags for ACK/NACK checking + int_fl0 = self->i2c_regs->intfl0; + + // Set / Wait for stop + MXC_I2C_Stop(self->i2c_regs); + + // Wait for controller not busy, then clear flags + while (self->i2c_regs->status & MXC_F_I2C_STATUS_BUSY) { + ; + } + MXC_I2C_ClearFlags(self->i2c_regs, 0xFFFFFF, 0xFFFFFF); + + if (int_fl0 & MXC_F_I2C_INTFL0_ADDR_ACK) { + ret = true; + } else { + ret = false; + } + return ret; +} + +// Lock I2C bus +bool common_hal_busio_i2c_try_lock(busio_i2c_obj_t *self) { + + if (self->i2c_regs->status & MXC_F_I2C_STATUS_BUSY) { + return false; + } else { + self->has_lock = true; + return true; + } +} + +// Check I2C lock status +bool common_hal_busio_i2c_has_lock(busio_i2c_obj_t *self) { + return self->has_lock; +} + +// Unlock I2C bus +void common_hal_busio_i2c_unlock(busio_i2c_obj_t *self) { + self->has_lock = false; +} + +// Write data to the device selected by address +uint8_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, + const uint8_t *data, size_t len) { + + int ret; + mxc_i2c_req_t wr_req = { + .addr = addr, + .i2c = self->i2c_regs, + .tx_buf = (uint8_t *)data, + .tx_len = len, + .rx_buf = NULL, + .rx_len = 0, + .callback = NULL + }; + ret = MXC_I2C_MasterTransaction(&wr_req); + if (ret) { + return MP_EIO; + } + + return 0; +} + +// Read into buffer from the device selected by address +uint8_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, + uint16_t addr, + uint8_t *data, size_t len) { + + int ret; + mxc_i2c_req_t rd_req = { + .addr = addr, + .i2c = self->i2c_regs, + .tx_buf = NULL, + .tx_len = 0, + .rx_buf = data, + .rx_len = len, + .callback = NULL + }; + ret = MXC_I2C_MasterTransaction(&rd_req); + if (ret) { + // Return I/O error + return MP_EIO; + } + + return 0; +} + +// Write the bytes from out_data to the device selected by address +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) { + + int ret; + mxc_i2c_req_t wr_rd_req = { + .addr = addr, + .i2c = self->i2c_regs, + .tx_buf = out_data, + .tx_len = out_len, + .rx_buf = in_data, + .rx_len = in_len, + .callback = NULL + }; + ret = MXC_I2C_MasterTransaction(&wr_rd_req); + if (ret) { + return MP_EIO; + } + + return 0; +} diff --git a/ports/analog/common-hal/busio/I2C.h b/ports/analog/common-hal/busio/I2C.h new file mode 100644 index 0000000000000..55c230ee2f97f --- /dev/null +++ b/ports/analog/common-hal/busio/I2C.h @@ -0,0 +1,28 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2024 Brandon Hurst, Analog Devices, Inc. +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "common-hal/microcontroller/Pin.h" +#include "py/obj.h" + +// HAL-specific +#include "i2c.h" +#include "gpio.h" + +// Define a struct for what BUSIO.I2C should carry +typedef struct { + mp_obj_base_t base; + + int i2c_id; + mxc_i2c_regs_t *i2c_regs; + const mcu_pin_obj_t *scl; + const mcu_pin_obj_t *sda; + const int frequency; + + uint32_t timeout; + bool has_lock; +} busio_i2c_obj_t; diff --git a/ports/analog/common-hal/busio/SPI.c b/ports/analog/common-hal/busio/SPI.c new file mode 100644 index 0000000000000..b4a519c37a72d --- /dev/null +++ b/ports/analog/common-hal/busio/SPI.c @@ -0,0 +1,316 @@ +/* + * This file is part of Adafruit for EFR32 project + * + * The MIT License (MIT) + * + * Copyright 2023 Silicon Laboratories Inc. www.silabs.com + * + * 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 "py/mperrno.h" +#include "py/runtime.h" +#include "shared-bindings/microcontroller/__init__.h" +#include "supervisor/board.h" +#include "shared-bindings/microcontroller/Pin.h" + +#include "max32_port.h" +#include "spi_reva1.h" + +// Note that any bugs introduced in this file can cause crashes +// at startupfor chips using external SPI flash. + +#define SPI_PRIORITY 1 + +typedef enum { + SPI_FREE = 0, + SPI_BUSY, + SPI_NEVER_RESET, +} spi_status_t; + +// Set each bit to indicate an active SPI +static uint8_t spi_active = 0; +static spi_status_t spi_status[NUM_SPI]; +static volatile int spi_err; + +// Construct SPI protocol, this function init SPI peripheral +void common_hal_busio_spi_construct(busio_spi_obj_t *self, + const mcu_pin_obj_t *sck, + const mcu_pin_obj_t *mosi, + const mcu_pin_obj_t *miso, + bool half_duplex) { + int err = 0; + + // Check for NULL Pointer + assert(self); + + // Assign SPI ID based on pins + int spi_id = pinsToSpi(mosi, miso, sck); + if (spi_id == -1) { + return; + } else { + self->spi_id = spi_id; + self->spi_regs = MXC_SPI_GET_SPI(spi_id); + } + + // Other pins default to true + mxc_spi_pins_t spi_pins = { + .clock = TRUE, + .mosi = TRUE, + .miso = TRUE, + .ss0 = FALSE, + .ss1 = FALSE, + .ss2 = FALSE, + .vddioh = true, + .drvstr = MXC_GPIO_DRVSTR_0 + }; + + assert((self->spi_id >= 0) && (self->spi_id < NUM_SPI)); + + // Init SPI controller + if ((mosi != NULL) && (miso != NULL) && (sck != NULL)) { + // spi, mastermode, quadModeUsed, numSubs, ssPolarity, frequency + err = MXC_SPI_Init(self->spi_regs, MXC_SPI_TYPE_CONTROLLER, MXC_SPI_INTERFACE_STANDARD, + 1, 0x01, 1000000, spi_pins); + MXC_GPIO_SetVSSEL(MXC_GPIO_GET_GPIO(sck->port), MXC_GPIO_VSSEL_VDDIOH, (sck->mask | miso->mask | mosi->mask | MXC_GPIO_PIN_0)); + if (err) { + // NOTE: Reuse existing messages from locales/circuitpython.pot to save space + mp_raise_RuntimeError_varg(MP_ERROR_TEXT("%q init failed"), MP_QSTR_SPI); + } + } else { + mp_raise_NotImplementedError(MP_ERROR_TEXT("SPI needs MOSI, MISO, and SCK")); + } + + // Attach SPI pins + self->mosi = mosi; + self->miso = miso; + self->sck = sck; + common_hal_mcu_pin_claim(self->mosi); + common_hal_mcu_pin_claim(self->miso); + common_hal_mcu_pin_claim(self->sck); + + // Indicate to this module that the SPI is active + spi_active |= (1 << self->spi_id); + + return; +} + +// Never reset SPI when reload +void common_hal_busio_spi_never_reset(busio_spi_obj_t *self) { + common_hal_never_reset_pin(self->mosi); + common_hal_never_reset_pin(self->miso); + common_hal_never_reset_pin(self->sck); + common_hal_never_reset_pin(self->nss); + + spi_status[self->spi_id] = SPI_NEVER_RESET; +} + +// Check SPI status, deinited or not +bool common_hal_busio_spi_deinited(busio_spi_obj_t *self) { + return self->sck == NULL; +} + +// Deinit SPI obj +void common_hal_busio_spi_deinit(busio_spi_obj_t *self) { + + MXC_SPI_Shutdown(self->spi_regs); + common_hal_reset_pin(self->mosi); + common_hal_reset_pin(self->miso); + common_hal_reset_pin(self->sck); + common_hal_reset_pin(self->nss); + + self->mosi = NULL; + self->miso = NULL; + self->sck = NULL; + self->nss = NULL; +} + +// Configures the SPI bus. The SPI object must be locked. +bool common_hal_busio_spi_configure(busio_spi_obj_t *self, + uint32_t baudrate, + uint8_t polarity, + uint8_t phase, + uint8_t bits) { + + mxc_spi_clkmode_t clk_mode; + int ret; + + self->baudrate = baudrate; + self->polarity = polarity; + self->phase = phase; + self->bits = bits; + + switch ((polarity << 1) | (phase)) { + case 0b00: + clk_mode = MXC_SPI_CLKMODE_0; + break; + case 0b01: + clk_mode = MXC_SPI_CLKMODE_1; + break; + case 0b10: + clk_mode = MXC_SPI_CLKMODE_2; + break; + case 0b11: + clk_mode = MXC_SPI_CLKMODE_3; + break; + default: + // should not be reachable; validated in shared-bindings/busio/SPI.c + return false; + } + + ret = MXC_SPI_SetFrequency(self->spi_regs, baudrate); + if (ret) { + mp_raise_ValueError_varg(MP_ERROR_TEXT("%q out of range"), MP_QSTR_baudrate); + return false; + } + ret = MXC_SPI_SetDataSize(self->spi_regs, bits); + if (ret == E_BAD_PARAM) { + mp_raise_ValueError_varg(MP_ERROR_TEXT("%q out of range"), MP_QSTR_bits); + return false; + } else if (ret == E_BAD_STATE) { + mp_raise_RuntimeError(MP_ERROR_TEXT("Invalid state")); + } + ret = MXC_SPI_SetMode(self->spi_regs, clk_mode); + if (ret) { + mp_raise_ValueError(MP_ERROR_TEXT("Failed to set SPI Clock Mode")); + return false; + } + return true; +} + +// Lock SPI bus +bool common_hal_busio_spi_try_lock(busio_spi_obj_t *self) { + if (spi_status[self->spi_id] != SPI_BUSY) { + self->has_lock = true; + return true; + } else { + return false; + } +} + +// Check SPI lock status +bool common_hal_busio_spi_has_lock(busio_spi_obj_t *self) { + return self->has_lock; +} + +// Unlock SPI bus +void common_hal_busio_spi_unlock(busio_spi_obj_t *self) { + self->has_lock = false; +} + +// Write the data contained in buffer +bool common_hal_busio_spi_write(busio_spi_obj_t *self, + const uint8_t *data, + size_t len) { + int ret = 0; + + mxc_spi_req_t wr_req = { + .spi = self->spi_regs, + .ssIdx = 0, + .txCnt = 0, + .rxCnt = 0, + .txData = (uint8_t *)data, + .txLen = len, + .rxData = NULL, + .rxLen = 0, + .ssDeassert = 1, + .completeCB = NULL, + .txDummyValue = 0xFF, + }; + ret = MXC_SPI_MasterTransaction(&wr_req); + if (ret) { + return false; + } else { + return true; + } +} + +// Read data into buffer +bool common_hal_busio_spi_read(busio_spi_obj_t *self, + uint8_t *data, size_t len, + uint8_t write_value) { + + int ret = 0; + + mxc_spi_req_t rd_req = { + .spi = self->spi_regs, + .ssIdx = 0, + .txCnt = 0, + .rxCnt = 0, + .txData = NULL, + .txLen = len, + .rxData = data, + .rxLen = len, + .ssDeassert = 1, + .completeCB = NULL, + .txDummyValue = write_value, + }; + ret = MXC_SPI_MasterTransaction(&rd_req); + if (ret) { + return false; + } else { + return true; + } +} + +// Write out the data in data_out +// while simultaneously reading data into data_in +bool common_hal_busio_spi_transfer(busio_spi_obj_t *self, + const uint8_t *data_out, + uint8_t *data_in, + size_t len) { + + int ret = 0; + + mxc_spi_req_t rd_req = { + .spi = self->spi_regs, + .ssIdx = 0, + .txCnt = 0, + .rxCnt = 0, + .txData = (uint8_t *)data_out, + .txLen = len, + .rxData = data_in, + .rxLen = len, + .ssDeassert = 1, + .completeCB = NULL, + .txDummyValue = 0xFF, + }; + ret = MXC_SPI_MasterTransaction(&rd_req); + if (ret) { + return false; + } else { + return true; + } +} + +// Get SPI baudrate +uint32_t common_hal_busio_spi_get_frequency(busio_spi_obj_t *self) { + return self->baudrate; +} + +// Get SPI phase +uint8_t common_hal_busio_spi_get_phase(busio_spi_obj_t *self) { + return self->phase; +} + +// Get SPI polarity +uint8_t common_hal_busio_spi_get_polarity(busio_spi_obj_t *self) { + return self->polarity; +} diff --git a/ports/analog/common-hal/busio/SPI.h b/ports/analog/common-hal/busio/SPI.h new file mode 100644 index 0000000000000..d10601876fb5e --- /dev/null +++ b/ports/analog/common-hal/busio/SPI.h @@ -0,0 +1,41 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2024 Brandon Hurst, Analog Devices, Inc. +// +// SPDX-License-Identifier: MIT + +#ifndef MICROPY_INCLUDED_MAX32_COMMON_HAL_BUSIO_SPI_H +#define MICROPY_INCLUDED_MAX32_COMMON_HAL_BUSIO_SPI_H + +#include "common-hal/microcontroller/Pin.h" +#include "py/obj.h" + +// HAL-specific +#include "spi.h" + +// Define a struct for what BUSIO.SPI should carry +typedef struct { + mp_obj_base_t base; + + // Spi regs & pins + int spi_id; + mxc_spi_regs_t *spi_regs; + const mcu_pin_obj_t *sck; + const mcu_pin_obj_t *mosi; + const mcu_pin_obj_t *miso; + const mcu_pin_obj_t *nss; + + // Configuration + uint32_t baudrate; + uint16_t prescaler; + uint8_t polarity; + uint8_t phase; + uint8_t bits; + + // Synch data + bool has_lock; +} busio_spi_obj_t; + +void spi_reset(void); + +#endif // MICROPY_INCLUDED_MAX32_COMMON_HAL_BUSIO_SPI_H diff --git a/ports/analog/common-hal/busio/UART.c b/ports/analog/common-hal/busio/UART.c new file mode 100644 index 0000000000000..be7851f52a21c --- /dev/null +++ b/ports/analog/common-hal/busio/UART.c @@ -0,0 +1,460 @@ +/* + * This file is part of Adafruit for EFR32 project + * + * The MIT License (MIT) + * + * Copyright 2023 Silicon Laboratories Inc. www.silabs.com + * + * 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. + */ + +#if CIRCUITPY_BUSIO_UART + +#include "mpconfigport.h" +#include "supervisor/shared/tick.h" + +#include "shared-bindings/microcontroller/__init__.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/busio/UART.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared/readline/readline.h" +#include "shared/runtime/interrupt_char.h" + +#include "py/gc.h" +#include "py/ringbuf.h" +#include "py/mperrno.h" +#include "py/mpprint.h" +#include "py/runtime.h" + +#include "max32_port.h" +#include "UART.h" +#include "nvic_table.h" + +// UART IRQ Priority +#define UART_PRIORITY 1 + +typedef enum { + UART_9600 = 9600, + UART_14400 = 14400, + UART_19200 = 19200, + UART_38400 = 38400, + UART_57600 = 57600, + UART_115200 = 115200, + UART_230400 = 230400, + UART_460800 = 460800, + UART_921600 = 921600, +} uart_valid_baudrates; + +typedef enum { + UART_FREE = 0, + UART_BUSY, + UART_NEVER_RESET, +} uart_status_t; + +static uint32_t timeout_ms = 0; + +// Set each bit to indicate an active UART +// will be checked by ISR Handler for which ones to call +static uint8_t uarts_active = 0; +static uart_status_t uart_status[NUM_UARTS]; +static volatile int uart_err; +static uint8_t uart_never_reset_mask = 0; +static busio_uart_obj_t *context; + +static bool isValidBaudrate(uint32_t baudrate) { + switch (baudrate) { + case UART_9600: + return true; + break; + case UART_14400: + return true; + break; + case UART_19200: + return true; + break; + case UART_38400: + return true; + break; + case UART_57600: + return true; + break; + case UART_115200: + return true; + break; + case UART_230400: + return true; + break; + case UART_460800: + return true; + break; + case UART_921600: + return true; + break; + default: + return false; + break; + } +} + +static mxc_uart_parity_t convertParity(busio_uart_parity_t busio_parity) { + switch (busio_parity) { + case BUSIO_UART_PARITY_NONE: + return MXC_UART_PARITY_DISABLE; + case BUSIO_UART_PARITY_EVEN: + return MXC_UART_PARITY_EVEN_0; + case BUSIO_UART_PARITY_ODD: + return MXC_UART_PARITY_ODD_0; + default: + // not reachable due to validation in shared-bindings/busio/SPI.c + return MXC_UART_PARITY_DISABLE; + } +} + +void uart_isr(void) { + for (int i = 0; i < NUM_UARTS; i++) { + if (uarts_active & (1 << i)) { + MXC_UART_AsyncHandler(MXC_UART_GET_UART(i)); + } + } +} + +// Callback gets called when AsyncRequest is COMPLETE +// (e.g. txLen == txCnt) +static volatile void uartCallback(mxc_uart_req_t *req, int error) { + uart_status[MXC_UART_GET_IDX(req->uart)] = UART_FREE; + uart_err = error; + + MXC_SYS_Crit_Enter(); + ringbuf_put_n(context->ringbuf, req->rxData, req->rxLen); + MXC_SYS_Crit_Exit(); +} + +// Construct an underlying UART object. +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) { + int err, temp; + + // Check for NULL Pointers && valid UART settings + assert(self); + + // Assign UART ID based on pins + temp = pinsToUart(tx, rx); + if (temp == -1) { + // Error will be indicated by pinsToUart(tx, rx) function + return; + } else { + self->uart_id = temp; + self->uart_regs = MXC_UART_GET_UART(temp); + } + assert((self->uart_id >= 0) && (self->uart_id < NUM_UARTS)); + + // Check for size of ringbuffer, desire powers of 2 + // At least use 1 byte if no size is given + assert((receiver_buffer_size & (receiver_buffer_size - 1)) == 0); + if (receiver_buffer_size == 0) { + receiver_buffer_size += 1; + } + + // Indicate RS485 not implemented + if ((rs485_dir != NULL) || (rs485_invert)) { + mp_raise_NotImplementedError(MP_ERROR_TEXT("RS485")); + } + + if ((rx != NULL) && (tx != NULL)) { + err = MXC_UART_Init(self->uart_regs, baudrate, MXC_UART_IBRO_CLK); + if (err != E_NO_ERROR) { + mp_raise_RuntimeError_varg(MP_ERROR_TEXT("%q init failed"), MP_QSTR_UART); + } + + // attach & configure pins + self->tx_pin = tx; + self->rx_pin = rx; + common_hal_mcu_pin_claim(self->tx_pin); + common_hal_mcu_pin_claim(self->rx_pin); + } else { + mp_raise_NotImplementedError(MP_ERROR_TEXT("UART needs TX & RX")); + } + + if ((cts) && (rts)) { + MXC_UART_SetFlowCtrl(self->uart_regs, MXC_UART_FLOW_EN, 8); + self->cts_pin = cts; + self->rts_pin = rts; + common_hal_mcu_pin_claim(self->cts_pin); + common_hal_mcu_pin_claim(self->rts_pin); + } else if (cts || rts) { + mp_raise_ValueError(MP_ERROR_TEXT("Both RX and TX required for flow control")); + } + + // Set stop bits & data size + assert((stop == 1) || (stop == 2)); + mp_arg_validate_int(bits, 8, MP_QSTR_bits); + MXC_UART_SetDataSize(self->uart_regs, bits); + MXC_UART_SetStopBits(self->uart_regs, stop); + + // Set parity + MXC_UART_SetParity(self->uart_regs, convertParity(parity)); + + // attach UART parameters + self->stop_bits = stop; // must be 1 or 2 + self->bits = bits; + self->parity = parity; + self->baudrate = baudrate; + self->error = E_NO_ERROR; + + // Indicate to this module that the UART is active + uarts_active |= (1 << self->uart_id); + + // Set the timeout to a default value + if (((timeout < 0.0) || (timeout > 100.0))) { + self->timeout = 1.0; + } else { + self->timeout = timeout; + } + + // Initialize ringbuffer + if (receiver_buffer == NULL) { + self->ringbuf = m_malloc_without_collect(receiver_buffer_size); + if (!ringbuf_alloc(self->ringbuf, receiver_buffer_size)) { + m_malloc_fail(receiver_buffer_size); + mp_raise_RuntimeError_varg(MP_ERROR_TEXT("Failed to allocate %q buffer"), + MP_QSTR_UART); + } + } else { + if (!(ringbuf_init(self->ringbuf, receiver_buffer, receiver_buffer_size))) { + mp_raise_RuntimeError_varg(MP_ERROR_TEXT("Failed to allocate %q buffer"), + MP_QSTR_UART); + } + } + + context = self; + + // Setup UART interrupt + NVIC_ClearPendingIRQ(MXC_UART_GET_IRQ(self->uart_id)); + NVIC_DisableIRQ(MXC_UART_GET_IRQ(self->uart_id)); + NVIC_SetPriority(MXC_UART_GET_IRQ(self->uart_id), UART_PRIORITY); + NVIC_SetVector(MXC_UART_GET_IRQ(self->uart_id), (uint32_t)uart_isr); + + NVIC_EnableIRQ(MXC_UART_GET_IRQ(self->uart_id)); + + return; +} + +void common_hal_busio_uart_deinit(busio_uart_obj_t *self) { + assert(self); + + if (!common_hal_busio_uart_deinited(self)) { + // First disable the ISR to avoid pre-emption + NVIC_DisableIRQ(MXC_UART_GET_IRQ(self->uart_id)); + + // Shutdown the UART Controller + MXC_UART_Shutdown(self->uart_regs); + self->error = E_UNINITIALIZED; + + assert(self->rx_pin && self->tx_pin); + reset_pin_number(self->rx_pin->port, self->rx_pin->mask); + reset_pin_number(self->tx_pin->port, self->tx_pin->mask); + + if (self->cts_pin && self->rts_pin) { + reset_pin_number(self->cts_pin->port, self->cts_pin->mask); + reset_pin_number(self->rts_pin->port, self->rts_pin->mask); + } + + self->tx_pin = NULL; + self->rx_pin = NULL; + self->cts_pin = NULL; + self->rts_pin = NULL; + + ringbuf_deinit(self->ringbuf); + + // Indicate to this module that the UART is not active + uarts_active &= ~(1 << self->uart_id); + } +} + +bool common_hal_busio_uart_deinited(busio_uart_obj_t *self) { + if (uarts_active & (1 << self->uart_id)) { + return false; + } else { + return true; + }; +} + +// Read characters. len is in characters. +size_t common_hal_busio_uart_read(busio_uart_obj_t *self, + uint8_t *data, size_t len, int *errcode) { + int err; + uint32_t start_time = 0; + static size_t bytes_remaining; + + // Setup globals & status tracking + uart_err = E_NO_ERROR; + uarts_active |= (1 << self->uart_id); + uart_status[self->uart_id] = UART_BUSY; + bytes_remaining = len; + + mxc_uart_req_t uart_rd_req; + uart_rd_req.rxCnt = 0; + uart_rd_req.txCnt = 0; + uart_rd_req.rxData = data; + uart_rd_req.txData = NULL; + uart_rd_req.rxLen = bytes_remaining; + uart_rd_req.txLen = 0; + uart_rd_req.uart = self->uart_regs; + uart_rd_req.callback = (void *)uartCallback; + + // Initiate the read transaction + start_time = supervisor_ticks_ms64(); + err = MXC_UART_TransactionAsync(&uart_rd_req); + if (err != E_NO_ERROR) { + *errcode = err; + MXC_UART_AbortAsync(self->uart_regs); + NVIC_DisableIRQ(MXC_UART_GET_IRQ(self->uart_id)); + mp_raise_RuntimeError_varg(MP_ERROR_TEXT("UART read error")); + } + + // Wait for transaction completion or timeout + while ((uart_status[self->uart_id] != UART_FREE) && + (supervisor_ticks_ms64() - start_time < (self->timeout * 1000))) { + } + + // If the timeout gets hit, abort and error out + if (uart_status[self->uart_id] != UART_FREE) { + MXC_UART_AbortAsync(self->uart_regs); + NVIC_DisableIRQ(MXC_UART_GET_IRQ(self->uart_id)); + mp_raise_RuntimeError(MP_ERROR_TEXT("UART transaction timeout")); + } + // Check for errors from the callback + else if (uart_err != E_NO_ERROR) { + mp_raise_RuntimeError(MP_ERROR_TEXT("UART read error")); + MXC_UART_AbortAsync(self->uart_regs); + } + + // Copy the data from the ringbuf (or return error) + MXC_SYS_Crit_Enter(); + err = ringbuf_get_n(context->ringbuf, data, len); + MXC_SYS_Crit_Exit(); + + return err; +} + +// Write characters. len is in characters NOT bytes! +// This function blocks until the timeout finishes +size_t common_hal_busio_uart_write(busio_uart_obj_t *self, + const uint8_t *data, size_t len, int *errcode) { + int err; + static size_t bytes_remaining; + + // Setup globals & status tracking + uart_err = E_NO_ERROR; + uarts_active |= (1 << self->uart_id); + uart_status[self->uart_id] = UART_BUSY; + bytes_remaining = len; + + mxc_uart_req_t uart_wr_req = {}; + + // Setup transaction + uart_wr_req.rxCnt = 0; + uart_wr_req.txCnt = 0; + uart_wr_req.rxData = NULL; + uart_wr_req.txData = data; + uart_wr_req.txLen = bytes_remaining; + uart_wr_req.rxLen = 0; + uart_wr_req.uart = self->uart_regs; + uart_wr_req.callback = (void *)uartCallback; + + // Start the transaction + err = MXC_UART_TransactionAsync(&uart_wr_req); + if (err != E_NO_ERROR) { + *errcode = err; + MXC_UART_AbortAsync(self->uart_regs); + NVIC_DisableIRQ(MXC_UART_GET_IRQ(self->uart_id)); + mp_raise_ValueError(MP_ERROR_TEXT("All UART peripherals are in use")); + } + + // Wait for transaction completion + while (uart_status[self->uart_id] != UART_FREE) { + // Call the handler and abort if errors + uart_err = MXC_UART_AsyncHandler(self->uart_regs); + if (uart_err != E_NO_ERROR) { + MXC_UART_AbortAsync(self->uart_regs); + } + } + // Check for errors from the callback + if (uart_err != E_NO_ERROR) { + MXC_UART_AbortAsync(self->uart_regs); + } + + return len; +} + +uint32_t common_hal_busio_uart_get_baudrate(busio_uart_obj_t *self) { + return self->baudrate; +} + +// Validate baudrate +void common_hal_busio_uart_set_baudrate(busio_uart_obj_t *self, uint32_t baudrate) { + if (isValidBaudrate(baudrate)) { + self->baudrate = baudrate; + } else { + mp_raise_ValueError_varg(MP_ERROR_TEXT("Invalid %q"), MP_QSTR_baudrate); + } +} + +mp_float_t common_hal_busio_uart_get_timeout(busio_uart_obj_t *self) { + return self->timeout; +} + +void common_hal_busio_uart_set_timeout(busio_uart_obj_t *self, mp_float_t timeout) { + if (timeout > 100.0) { + mp_raise_ValueError(MP_ERROR_TEXT("Timeout must be < 100 seconds")); + } + + timeout_ms = 1000 * (uint32_t)timeout; + self->timeout = (uint32_t)timeout; + + return; +} + +uint32_t common_hal_busio_uart_rx_characters_available(busio_uart_obj_t *self) { + return ringbuf_num_filled(self->ringbuf); +} + +void common_hal_busio_uart_clear_rx_buffer(busio_uart_obj_t *self) { + MXC_UART_ClearRXFIFO(self->uart_regs); + ringbuf_clear(self->ringbuf); +} + +bool common_hal_busio_uart_ready_to_tx(busio_uart_obj_t *self) { + return !(MXC_UART_GetStatus(self->uart_regs) & (MXC_F_UART_STATUS_TX_BUSY)); +} + +void common_hal_busio_uart_never_reset(busio_uart_obj_t *self) { + common_hal_never_reset_pin(self->tx_pin); + common_hal_never_reset_pin(self->rx_pin); + common_hal_never_reset_pin(self->cts_pin); + common_hal_never_reset_pin(self->rts_pin); + uart_never_reset_mask |= (1 << (self->uart_id)); +} + +#endif // CIRCUITPY_BUSIO_UART diff --git a/ports/analog/common-hal/busio/UART.h b/ports/analog/common-hal/busio/UART.h new file mode 100644 index 0000000000000..296c738772e68 --- /dev/null +++ b/ports/analog/common-hal/busio/UART.h @@ -0,0 +1,39 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2024 Brandon Hurst, Analog Devices, Inc. +// +// SPDX-License-Identifier: MIT + +#ifndef MICROPY_INCLUDED_MAX32_COMMON_HAL_BUSIO_UART_H +#define MICROPY_INCLUDED_MAX32_COMMON_HAL_BUSIO_UART_H + +#include "common-hal/microcontroller/Pin.h" +#include "py/obj.h" +#include "py/ringbuf.h" + +#include "max32_port.h" + +// Define a struct for what BUSIO.UART should contain +typedef struct { + mp_obj_base_t base; + int error; + float timeout; + + int uart_id; + int uart_map; + mxc_uart_regs_t *uart_regs; + ringbuf_t *ringbuf; + bool parity; + uint8_t bits; + uint8_t stop_bits; + uint32_t baudrate; + + const mcu_pin_obj_t *rx_pin; + const mcu_pin_obj_t *tx_pin; + const mcu_pin_obj_t *rts_pin; + const mcu_pin_obj_t *cts_pin; +} busio_uart_obj_t; + +void uart_reset(void); + +#endif // MICROPY_INCLUDED_MAX32_COMMON_HAL_BUSIO_UART_H diff --git a/ports/analog/common-hal/busio/__init__.c b/ports/analog/common-hal/busio/__init__.c new file mode 100644 index 0000000000000..ff05be051bb25 --- /dev/null +++ b/ports/analog/common-hal/busio/__init__.c @@ -0,0 +1,5 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2024 Brandon Hurst, Analog Devices, Inc. +// +// SPDX-License-Identifier: MIT diff --git a/ports/analog/common-hal/microcontroller/Pin.c b/ports/analog/common-hal/microcontroller/Pin.c index 4545aa039c2fa..83e2f3b9c3a76 100644 --- a/ports/analog/common-hal/microcontroller/Pin.c +++ b/ports/analog/common-hal/microcontroller/Pin.c @@ -37,7 +37,7 @@ void reset_all_pins(void) { } void reset_pin_number(uint8_t pin_port, uint8_t pin_pad) { - if (pin_port == INVALID_PIN || pin_port > NUM_GPIO_PORTS) { + if ((pin_port == INVALID_PIN) || (pin_port > NUM_GPIO_PORTS)) { return; } diff --git a/ports/analog/max32_port.h b/ports/analog/max32_port.h index 5d92fcfe6d3ee..89830e96b0a49 100644 --- a/ports/analog/max32_port.h +++ b/ports/analog/max32_port.h @@ -22,29 +22,34 @@ #include "system_max32690.h" #include "max32690.h" +// UART Ports & pins +#include "peripherals/max32690/max32_uart.h" +#include "peripherals/max32690/max32_i2c.h" +#include "peripherals/max32690/max32_spi.h" + /** START: GPIO4 Handling specific to MAX32690 */ -#define GPIO4_PIN_MASK 0x00000003 -#define GPIO4_RESET_MASK 0xFFFFFF77 -#define GPIO4_OUTEN_MASK(mask) \ + #define GPIO4_PIN_MASK 0x00000003 + #define GPIO4_RESET_MASK 0xFFFFFF77 + #define GPIO4_OUTEN_MASK(mask) \ (((mask & (1 << 0)) << MXC_F_MCR_GPIO4_CTRL_P40_OE_POS) | \ ((mask & (1 << 1)) << (MXC_F_MCR_GPIO4_CTRL_P41_OE_POS - 1))) -#define GPIO4_PULLDIS_MASK(mask) \ + #define GPIO4_PULLDIS_MASK(mask) \ (((mask & (1 << 0)) << MXC_F_MCR_GPIO4_CTRL_P40_PE_POS) | \ ((mask & (1 << 1)) << (MXC_F_MCR_GPIO4_CTRL_P41_PE_POS - 1))) -#define GPIO4_DATAOUT_MASK(mask) \ + #define GPIO4_DATAOUT_MASK(mask) \ (((mask & (1 << 0)) << MXC_F_MCR_GPIO4_CTRL_P40_DO_POS) | \ ((mask & (1 << 1)) << (MXC_F_MCR_GPIO4_CTRL_P41_DO_POS - 1))) -#define GPIO4_DATAOUT_GET_MASK(mask) \ + #define GPIO4_DATAOUT_GET_MASK(mask) \ ((((MXC_MCR->gpio4_ctrl & MXC_F_MCR_GPIO4_CTRL_P40_DO) >> MXC_F_MCR_GPIO4_CTRL_P40_DO_POS) | \ ((MXC_MCR->gpio4_ctrl & MXC_F_MCR_GPIO4_CTRL_P41_DO) >> \ (MXC_F_MCR_GPIO4_CTRL_P41_DO_POS - 1))) & \ mask) -#define GPIO4_DATAIN_MASK(mask) \ + #define GPIO4_DATAIN_MASK(mask) \ ((((MXC_MCR->gpio4_ctrl & MXC_F_MCR_GPIO4_CTRL_P40_IN) >> MXC_F_MCR_GPIO4_CTRL_P40_IN_POS) | \ ((MXC_MCR->gpio4_ctrl & MXC_F_MCR_GPIO4_CTRL_P41_IN) >> \ (MXC_F_MCR_GPIO4_CTRL_P41_IN_POS - 1))) & \ mask) -#define GPIO4_AFEN_MASK(mask) \ + #define GPIO4_AFEN_MASK(mask) \ (((mask & (1 << 0)) << MXC_F_MCR_OUTEN_PDOWN_OUT_EN_POS) | \ ((mask & (1 << 1)) >> (MXC_F_MCR_OUTEN_SQWOUT_EN_POS + 1))) /** END: GPIO4 Handling specific to MAX32690 */ diff --git a/ports/analog/mpconfigport.mk b/ports/analog/mpconfigport.mk index 97ead8efdfb0f..f1cd0bb2901e9 100644 --- a/ports/analog/mpconfigport.mk +++ b/ports/analog/mpconfigport.mk @@ -22,8 +22,7 @@ INTERNAL_FLASH_FILESYSTEM = 1 #################################################################################### # These modules are implemented in ports//common-hal: -# Plan to implement -CIRCUITPY_BUSIO ?= 0 +CIRCUITPY_BUSIO ?= 1 CIRCUITPY_RTC ?= 1 # Other modules (may or may not implement): diff --git a/ports/analog/peripherals/max32690/gpios.h b/ports/analog/peripherals/max32690/gpios.h index 4677bf8f33dbe..fe91227728291 100644 --- a/ports/analog/peripherals/max32690/gpios.h +++ b/ports/analog/peripherals/max32690/gpios.h @@ -4,6 +4,8 @@ // // SPDX-License-Identifier: MIT +#pragma once + #include "py/obj.h" #include "py/mphal.h" diff --git a/ports/analog/peripherals/max32690/max32_i2c.c b/ports/analog/peripherals/max32690/max32_i2c.c new file mode 100644 index 0000000000000..4b9ec8b4db264 --- /dev/null +++ b/ports/analog/peripherals/max32690/max32_i2c.c @@ -0,0 +1,75 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2024 Brandon Hurst, Analog Devices, Inc. +// +// SPDX-License-Identifier: MIT + +#include "peripherals/pins.h" + +#include "common-hal/busio/I2C.h" +#include "max32_i2c.h" +#include "max32690.h" + +#include "py/runtime.h" +#include "py/mperrno.h" + + +/* Note: The MAX32690 assigns the same alternate function to multiple sets + * of pins. The drivers will enable both sets so that either can be used. + * Users should ensure the unused set is left unconnected. + * + * See MAX32690 Rev A2 Errata #16: + * https://www.analog.com/media/en/technical-documentation/data-sheets/max32690_a2_errata_rev2.pdf + * + * Additionally, note that the TQFN package does not expose some of the duplicate pins. For this package, + * enabling the un-routed GPIOs has been shown to cause initialization issues with the I2C block. + * To work around this, "MAX32690GTK_PACKAGE_TQFN" can be defined by the build system. The recommend place + * to do it is in the "board.mk" file of the BSP. This will prevent the inaccessible pins from being configured. + */ + +const mxc_gpio_cfg_t i2c_maps[NUM_I2C] = { + // I2C0 + { MXC_GPIO2, (MXC_GPIO_PIN_7 | MXC_GPIO_PIN_8), MXC_GPIO_FUNC_ALT1, + MXC_GPIO_PAD_NONE, MXC_GPIO_VSSEL_VDDIO, MXC_GPIO_DRVSTR_0 }, + // I2C1 + { MXC_GPIO0, (MXC_GPIO_PIN_11 | MXC_GPIO_PIN_12), MXC_GPIO_FUNC_ALT1, + MXC_GPIO_PAD_NONE, MXC_GPIO_VSSEL_VDDIO, MXC_GPIO_DRVSTR_0 }, + // I2C2 + { MXC_GPIO1, (MXC_GPIO_PIN_7 | MXC_GPIO_PIN_8), MXC_GPIO_FUNC_ALT3, + MXC_GPIO_PAD_NONE, MXC_GPIO_VSSEL_VDDIOH, MXC_GPIO_DRVSTR_0 }, +}; +#ifndef MAX32690GTK_PACKAGE_TQFN +const mxc_gpio_cfg_t i2c_maps_extra[NUM_I2C] = { + // I2C0A + { MXC_GPIO0, (MXC_GPIO_PIN_30 | MXC_GPIO_PIN_31), MXC_GPIO_FUNC_ALT1, + MXC_GPIO_PAD_PULL_UP, MXC_GPIO_VSSEL_VDDIOH, MXC_GPIO_DRVSTR_0 }, + // I2C1A + { MXC_GPIO2, (MXC_GPIO_PIN_17 | MXC_GPIO_PIN_18), MXC_GPIO_FUNC_ALT1, + MXC_GPIO_PAD_PULL_UP, MXC_GPIO_VSSEL_VDDIOH, MXC_GPIO_DRVSTR_0 }, + // I2C2C + { MXC_GPIO0, (MXC_GPIO_PIN_13 | MXC_GPIO_PIN_14), MXC_GPIO_FUNC_ALT3, + MXC_GPIO_PAD_NONE, MXC_GPIO_VSSEL_VDDIO, MXC_GPIO_DRVSTR_0 }, +}; +#endif + +int pinsToI2c(const mcu_pin_obj_t *sda, const mcu_pin_obj_t *scl) { + for (int i = 0; i < NUM_I2C; i++) { + if ((i2c_maps[i].port == (MXC_GPIO_GET_GPIO(sda->port))) + && (i2c_maps[i].mask == ((sda->mask) | (scl->mask)))) { + return i; + } + } + + // Additional for loop to cover alternate potential I2C maps + #ifndef MAX32690GTK_PACKAGE_TQFN + for (int i = 0; i < NUM_I2C; i++) { + if ((i2c_maps_extra[i].port == (MXC_GPIO_GET_GPIO(sda->port))) + && (i2c_maps_extra[i].mask == ((sda->mask) | (scl->mask)))) { + return i; + } + } + #endif + + mp_raise_ValueError_varg(MP_ERROR_TEXT("Invalid %q"), MP_QSTR_pins); + return -1; +} diff --git a/ports/analog/peripherals/max32690/max32_i2c.h b/ports/analog/peripherals/max32690/max32_i2c.h new file mode 100644 index 0000000000000..b64cfd308dcf5 --- /dev/null +++ b/ports/analog/peripherals/max32690/max32_i2c.h @@ -0,0 +1,16 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2024 Brandon Hurst, Analog Devices, Inc. +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "i2c_regs.h" +#include "mxc_sys.h" +#include "i2c.h" +#include "peripherals/pins.h" + +#define NUM_I2C 3 + +int pinsToI2c(const mcu_pin_obj_t *sda, const mcu_pin_obj_t *scl); diff --git a/ports/analog/peripherals/max32690/max32_spi.c b/ports/analog/peripherals/max32690/max32_spi.c new file mode 100644 index 0000000000000..c78fd64dbd705 --- /dev/null +++ b/ports/analog/peripherals/max32690/max32_spi.c @@ -0,0 +1,44 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2024 Brandon Hurst, Analog Devices, Inc. +// +// SPDX-License-Identifier: MIT + +#include "peripherals/pins.h" + +#include "common-hal/busio/SPI.h" +#include "max32_spi.h" +#include "max32690.h" + +#include "py/runtime.h" +#include "py/mperrno.h" + +const mxc_gpio_cfg_t spi_maps[NUM_SPI] = { + // SPI0 + { MXC_GPIO2, (MXC_GPIO_PIN_27 | MXC_GPIO_PIN_28 | MXC_GPIO_PIN_29), + MXC_GPIO_FUNC_ALT2, MXC_GPIO_PAD_NONE, MXC_GPIO_VSSEL_VDDIO, MXC_GPIO_DRVSTR_0 }, + // SPI1 + { MXC_GPIO1, (MXC_GPIO_PIN_26 | MXC_GPIO_PIN_28 | MXC_GPIO_PIN_29), + MXC_GPIO_FUNC_ALT1, MXC_GPIO_PAD_NONE, MXC_GPIO_VSSEL_VDDIO, MXC_GPIO_DRVSTR_0 }, + // SPI2 + { MXC_GPIO2, (MXC_GPIO_PIN_2 | MXC_GPIO_PIN_3 | MXC_GPIO_PIN_4), + MXC_GPIO_FUNC_ALT1, MXC_GPIO_PAD_NONE, MXC_GPIO_VSSEL_VDDIO, MXC_GPIO_DRVSTR_0 }, + // SPI3 + { MXC_GPIO0, (MXC_GPIO_PIN_16 | MXC_GPIO_PIN_20 | MXC_GPIO_PIN_21), + MXC_GPIO_FUNC_ALT1, MXC_GPIO_PAD_NONE, MXC_GPIO_VSSEL_VDDIO, MXC_GPIO_DRVSTR_0 }, + // SPI4 + { MXC_GPIO1, (MXC_GPIO_PIN_1 | MXC_GPIO_PIN_2 | MXC_GPIO_PIN_3), + MXC_GPIO_FUNC_ALT1, MXC_GPIO_PAD_NONE, MXC_GPIO_VSSEL_VDDIO, MXC_GPIO_DRVSTR_0 }, +}; + +int pinsToSpi(const mcu_pin_obj_t *mosi, const mcu_pin_obj_t *miso, + const mcu_pin_obj_t *sck) { + for (int i = 0; i < NUM_SPI; i++) { + if ((spi_maps[i].port == (MXC_GPIO_GET_GPIO(mosi->port))) + && (spi_maps[i].mask == ((mosi->mask) | (miso->mask) | (sck->mask)))) { + return i; + } + } + mp_raise_ValueError_varg(MP_ERROR_TEXT("Invalid %q"), MP_QSTR_pins); + return -1; +} diff --git a/ports/analog/peripherals/max32690/max32_spi.h b/ports/analog/peripherals/max32690/max32_spi.h new file mode 100644 index 0000000000000..76bb48a59a7e5 --- /dev/null +++ b/ports/analog/peripherals/max32690/max32_spi.h @@ -0,0 +1,17 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2024 Brandon Hurst, Analog Devices, Inc. +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "spi_regs.h" +#include "mxc_sys.h" +#include "spi.h" +#include "peripherals/pins.h" + +#define NUM_SPI 5 + +int pinsToSpi(const mcu_pin_obj_t *mosi, const mcu_pin_obj_t *miso, + const mcu_pin_obj_t *sck); diff --git a/ports/analog/peripherals/max32690/max32_uart.c b/ports/analog/peripherals/max32690/max32_uart.c new file mode 100644 index 0000000000000..7d37708797415 --- /dev/null +++ b/ports/analog/peripherals/max32690/max32_uart.c @@ -0,0 +1,36 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2024 Brandon Hurst, Analog Devices, Inc. +// +// SPDX-License-Identifier: MIT + +#include "peripherals/pins.h" + +#include "common-hal/busio/UART.h" +#include "max32_uart.h" +#include "max32690.h" + +#include "py/runtime.h" +#include "py/mperrno.h" + +const mxc_gpio_cfg_t uart_maps[NUM_UARTS] = { + { MXC_GPIO2, (MXC_GPIO_PIN_11 | MXC_GPIO_PIN_12), MXC_GPIO_FUNC_ALT1, + MXC_GPIO_PAD_WEAK_PULL_UP, MXC_GPIO_VSSEL_VDDIO, MXC_GPIO_DRVSTR_0 }, + { MXC_GPIO2, (MXC_GPIO_PIN_14 | MXC_GPIO_PIN_16), MXC_GPIO_FUNC_ALT1, + MXC_GPIO_PAD_WEAK_PULL_UP, MXC_GPIO_VSSEL_VDDIO, MXC_GPIO_DRVSTR_0 }, + { MXC_GPIO1, (MXC_GPIO_PIN_9 | MXC_GPIO_PIN_10), MXC_GPIO_FUNC_ALT1, + MXC_GPIO_PAD_WEAK_PULL_UP, MXC_GPIO_VSSEL_VDDIO, MXC_GPIO_DRVSTR_0 }, + { MXC_GPIO3, (MXC_GPIO_PIN_0 | MXC_GPIO_PIN_1), MXC_GPIO_FUNC_ALT2, + MXC_GPIO_PAD_WEAK_PULL_UP, MXC_GPIO_VSSEL_VDDIO, MXC_GPIO_DRVSTR_0 } +}; + +int pinsToUart(const mcu_pin_obj_t *rx, const mcu_pin_obj_t *tx) { + for (int i = 0; i < NUM_UARTS; i++) { + if ((uart_maps[i].port == (MXC_GPIO_GET_GPIO(tx->port))) + && (uart_maps[i].mask == ((tx->mask) | (rx->mask)))) { + return i; + } + } + mp_raise_ValueError_varg(MP_ERROR_TEXT("Invalid %q"), MP_QSTR_pins); + return -1; +} diff --git a/ports/analog/peripherals/max32690/max32_uart.h b/ports/analog/peripherals/max32690/max32_uart.h new file mode 100644 index 0000000000000..f03b500a3c507 --- /dev/null +++ b/ports/analog/peripherals/max32690/max32_uart.h @@ -0,0 +1,16 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2024 Brandon Hurst, Analog Devices, Inc. +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "uart_regs.h" +#include "mxc_sys.h" +#include "uart.h" +#include "peripherals/pins.h" + +#define NUM_UARTS 4 + +int pinsToUart(const mcu_pin_obj_t *rx, const mcu_pin_obj_t *tx); diff --git a/ports/analog/supervisor/port.c b/ports/analog/supervisor/port.c index ff05ff2d28709..40e8e1d605038 100644 --- a/ports/analog/supervisor/port.c +++ b/ports/analog/supervisor/port.c @@ -39,6 +39,7 @@ // Sys includes #include "max32_port.h" +#include "nvic_table.h" // Timers #include "mxc_delay.h" @@ -72,12 +73,21 @@ volatile uint32_t system_ticks = 0; void SysTick_Handler(void) { system_ticks++; + + MXC_DelayHandler(); } safe_mode_t port_init(void) { int err = E_NO_ERROR; + // Set Vector Table to RAM & configure ARM core to use RAM-based ISRs + // This allows definition of ISRs with custom names + // + // Useful for mapping ISRs with names not related to a specific IRQn. + // Source: https://arm-software.github.io/CMSIS_5/Core/html/using_VTOR_pg.html + NVIC_SetRAM(); + // 1ms tick timer SysTick_Config(SystemCoreClock / 1000); NVIC_EnableIRQ(SysTick_IRQn); diff --git a/ports/analog/tools/debug-dap.gdb b/ports/analog/tools/debug-dap.gdb new file mode 100644 index 0000000000000..d0aa7e2b13fa0 --- /dev/null +++ b/ports/analog/tools/debug-dap.gdb @@ -0,0 +1,3 @@ +target remote :3333 +break main +continue diff --git a/ports/analog/tools/debug-dap.sh b/ports/analog/tools/debug-dap.sh new file mode 100644 index 0000000000000..df2964fcb122a --- /dev/null +++ b/ports/analog/tools/debug-dap.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +# Primer on taking cmd line args in Linux shell scripts +# $0 is the script itself +# $1 $2 $3 are arg1, arg2, arg3, etc + +# Export OCD Path +### USER ACTION: Replace with your path to OpenOCD ### +OCD_PATH="~/MaximSDK/Tools/OpenOCD" + +# Call openocd to setup the debug server, storing the PID for OpenOCD +sh -c "openocd -s $OCD_PATH/scripts -f interface/cmsis-dap.cfg -f target/$1.cfg -c \"init; reset halt\" " & + +# Allow enough time for OCD server to set up +# + wait for the sleep to finish +sleep 3 & +wait $! + +# spawn the gdb process and store the gdb_pid +gdb-multiarch build-apard32690/firmware.elf -x "tools/debug-dap.gdb" + +# when gdb exits, kill all openocd processes +killall openocd diff --git a/ports/analog/tools/flash_max32.jlink b/ports/analog/tools/flash_max32.jlink index 4c9cbacb96fee..0453f4cb077fd 100644 --- a/ports/analog/tools/flash_max32.jlink +++ b/ports/analog/tools/flash_max32.jlink @@ -1,6 +1,7 @@ -si 1 +selectinterface swd erase -loadbin build-APARD/firmware.bin 0x10000000 -r -g +loadbin build-apard32690/firmware.bin 0x10000000 +reset +halt +go exit diff --git a/ports/atmel-samd/boards/bradanlanestudio_coin_m0/mpconfigboard.mk b/ports/atmel-samd/boards/bradanlanestudio_coin_m0/mpconfigboard.mk index 33165c8cfc5d0..9a2cdcd668ed2 100644 --- a/ports/atmel-samd/boards/bradanlanestudio_coin_m0/mpconfigboard.mk +++ b/ports/atmel-samd/boards/bradanlanestudio_coin_m0/mpconfigboard.mk @@ -18,7 +18,7 @@ CHIP_FAMILY = samd21 SPI_FLASH_FILESYSTEM = 1 EXTERNAL_FLASH_DEVICES = "W25Q32JVxQ" -LONGINT_IMPL = NONE +LONGINT_IMPL = MPZ # the M0 Coin has limited functionality and many modules can be eliminated @@ -26,7 +26,7 @@ LONGINT_IMPL = NONE # Disable modules that are unusable on this special-purpose board. -CIRCUITPY_FULL_BUILD = 0 +CIRCUITPY_FULL_BUILD = 1 CIRCUITPY_AUDIOIO = 1 CIRCUITPY_DISPLAYIO = 0 @@ -38,7 +38,8 @@ CIRCUITPY_ROTARYIO = 0 CIRCUITPY_RTC = 0 CIRCUITPY_USB_HID = 1 CIRCUITPY_USB_MIDI = 0 - +CIRCUITPY_NEOPIXEL_WRITE = 1 +CIRCUITPY_PIXELBUF = 1 # Include these Python libraries in firmware. FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_HID diff --git a/ports/atmel-samd/common-hal/busio/I2C.c b/ports/atmel-samd/common-hal/busio/I2C.c index e13b5410ae32a..da108709feaff 100644 --- a/ports/atmel-samd/common-hal/busio/I2C.c +++ b/ports/atmel-samd/common-hal/busio/I2C.c @@ -135,6 +135,7 @@ void common_hal_busio_i2c_deinit(busio_i2c_obj_t *self) { if (common_hal_busio_i2c_deinited(self)) { return; } + allow_reset_sercom(self->i2c_desc.device.hw); i2c_m_sync_disable(&self->i2c_desc); i2c_m_sync_deinit(&self->i2c_desc); diff --git a/ports/atmel-samd/common-hal/rotaryio/IncrementalEncoder.c b/ports/atmel-samd/common-hal/rotaryio/IncrementalEncoder.c index c6cd4605784ab..a8c87d82ed268 100644 --- a/ports/atmel-samd/common-hal/rotaryio/IncrementalEncoder.c +++ b/ports/atmel-samd/common-hal/rotaryio/IncrementalEncoder.c @@ -17,6 +17,10 @@ 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) { + + // Ensure object starts in its deinit state. + common_hal_rotaryio_incrementalencoder_mark_deinit(self); + if (!pin_a->has_extint) { raise_ValueError_invalid_pin_name(MP_QSTR_pin_a); } @@ -83,10 +87,13 @@ void common_hal_rotaryio_incrementalencoder_deinit(rotaryio_incrementalencoder_o turn_off_eic_channel(self->eic_channel_b); reset_pin_number(self->pin_a); - self->pin_a = NO_PIN; - reset_pin_number(self->pin_b); - self->pin_b = NO_PIN; + + common_hal_rotaryio_incrementalencoder_mark_deinit(self); +} + +void common_hal_rotaryio_incrementalencoder_mark_deinit(rotaryio_incrementalencoder_obj_t *self) { + self->pin_a = NO_PIN; } void incrementalencoder_interrupt_handler(uint8_t channel) { diff --git a/ports/espressif/boards/espressif_esp32c3_lyra_v2/board.c b/ports/espressif/boards/espressif_esp32c3_lyra_v2/board.c new file mode 100644 index 0000000000000..b3b20cfe2d3f0 --- /dev/null +++ b/ports/espressif/boards/espressif_esp32c3_lyra_v2/board.c @@ -0,0 +1,9 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2021 microDev +// +// SPDX-License-Identifier: MIT + +#include "supervisor/board.h" + +// Use the MP_WEAK supervisor/shared/board.c versions of routines not defined here. diff --git a/ports/espressif/boards/espressif_esp32c3_lyra_v2/mpconfigboard.h b/ports/espressif/boards/espressif_esp32c3_lyra_v2/mpconfigboard.h new file mode 100644 index 0000000000000..d38d4f4d09fdf --- /dev/null +++ b/ports/espressif/boards/espressif_esp32c3_lyra_v2/mpconfigboard.h @@ -0,0 +1,23 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2021 microDev +// +// SPDX-License-Identifier: MIT + +#pragma once + +// Board setup +#define MICROPY_HW_BOARD_NAME "ESP32-C3-Lyra-V2" +#define MICROPY_HW_MCU_NAME "ESP32-C3N4" + +// Status LED +#define MICROPY_HW_NEOPIXEL (&pin_GPIO10) +#define MICROPY_HW_NEOPIXEL_COUNT (1) + +// Default bus pins +#define DEFAULT_UART_BUS_RX (&pin_GPIO20) +#define DEFAULT_UART_BUS_TX (&pin_GPIO21) + +// Serial over UART +#define CIRCUITPY_CONSOLE_UART_RX DEFAULT_UART_BUS_RX +#define CIRCUITPY_CONSOLE_UART_TX DEFAULT_UART_BUS_TX diff --git a/ports/espressif/boards/espressif_esp32c3_lyra_v2/mpconfigboard.mk b/ports/espressif/boards/espressif_esp32c3_lyra_v2/mpconfigboard.mk new file mode 100644 index 0000000000000..4b23a76ba6a15 --- /dev/null +++ b/ports/espressif/boards/espressif_esp32c3_lyra_v2/mpconfigboard.mk @@ -0,0 +1,10 @@ +CIRCUITPY_CREATOR_ID = 0x000C303A +CIRCUITPY_CREATION_ID = 0x00C3A000 + +IDF_TARGET = esp32c3 + +CIRCUITPY_ESP_FLASH_MODE = qio +CIRCUITPY_ESP_FLASH_FREQ = 80m +CIRCUITPY_ESP_FLASH_SIZE = 4MB + +CIRCUITPY_ESP_USB_SERIAL_JTAG = 0 diff --git a/ports/espressif/boards/espressif_esp32c3_lyra_v2/pins.c b/ports/espressif/boards/espressif_esp32c3_lyra_v2/pins.c new file mode 100644 index 0000000000000..5f7c3d084dd3f --- /dev/null +++ b/ports/espressif/boards/espressif_esp32c3_lyra_v2/pins.c @@ -0,0 +1,41 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2021 microDev +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/board/__init__.h" + +static const mp_rom_map_elem_t board_module_globals_table[] = { + CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS + + { MP_ROM_QSTR(MP_QSTR_IO0), MP_ROM_PTR(&pin_GPIO0) }, + { MP_ROM_QSTR(MP_QSTR_IO1), MP_ROM_PTR(&pin_GPIO1) }, + { MP_ROM_QSTR(MP_QSTR_IO2), MP_ROM_PTR(&pin_GPIO2) }, + { MP_ROM_QSTR(MP_QSTR_IO3), MP_ROM_PTR(&pin_GPIO3) }, + { MP_ROM_QSTR(MP_QSTR_IO4), MP_ROM_PTR(&pin_GPIO4) }, + { MP_ROM_QSTR(MP_QSTR_IO5), MP_ROM_PTR(&pin_GPIO5) }, + { MP_ROM_QSTR(MP_QSTR_IO6), MP_ROM_PTR(&pin_GPIO6) }, + { MP_ROM_QSTR(MP_QSTR_IO7), MP_ROM_PTR(&pin_GPIO7) }, + { MP_ROM_QSTR(MP_QSTR_IO8), MP_ROM_PTR(&pin_GPIO8) }, + { MP_ROM_QSTR(MP_QSTR_IO9), MP_ROM_PTR(&pin_GPIO9) }, + { MP_ROM_QSTR(MP_QSTR_IO10), MP_ROM_PTR(&pin_GPIO10) }, + { MP_ROM_QSTR(MP_QSTR_IO18), MP_ROM_PTR(&pin_GPIO18) }, + { MP_ROM_QSTR(MP_QSTR_IO19), MP_ROM_PTR(&pin_GPIO19) }, + { MP_ROM_QSTR(MP_QSTR_IO20), MP_ROM_PTR(&pin_GPIO20) }, + { MP_ROM_QSTR(MP_QSTR_IO21), MP_ROM_PTR(&pin_GPIO21) }, + + { MP_ROM_QSTR(MP_QSTR_RX), MP_ROM_PTR(&pin_GPIO20) }, + { MP_ROM_QSTR(MP_QSTR_TX), MP_ROM_PTR(&pin_GPIO21) }, + + { MP_ROM_QSTR(MP_QSTR_MTMS), MP_ROM_PTR(&pin_GPIO4) }, + { MP_ROM_QSTR(MP_QSTR_MTDI), MP_ROM_PTR(&pin_GPIO5) }, + { MP_ROM_QSTR(MP_QSTR_MTCK), MP_ROM_PTR(&pin_GPIO6) }, + { MP_ROM_QSTR(MP_QSTR_MTDO), MP_ROM_PTR(&pin_GPIO7) }, + + { MP_ROM_QSTR(MP_QSTR_BUTTON), MP_ROM_PTR(&pin_GPIO9) }, + { MP_ROM_QSTR(MP_QSTR_NEOPIXEL), MP_ROM_PTR(&pin_GPIO10) }, + + { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&board_uart_obj) }, +}; +MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table); diff --git a/ports/espressif/boards/espressif_esp32c3_lyra_v2/sdkconfig b/ports/espressif/boards/espressif_esp32c3_lyra_v2/sdkconfig new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/ports/espressif/boards/m5stack_cardputer/mpconfigboard.mk b/ports/espressif/boards/m5stack_cardputer/mpconfigboard.mk index cd18ccf8a5a81..bc30ae3afd5e9 100644 --- a/ports/espressif/boards/m5stack_cardputer/mpconfigboard.mk +++ b/ports/espressif/boards/m5stack_cardputer/mpconfigboard.mk @@ -13,4 +13,4 @@ CIRCUITPY_ESP_FLASH_SIZE = 8MB CIRCUITPY_ESPCAMERA = 0 CIRCUITPY_MAX3421E = 0 -SRC_C += boards/$(BOARD)/cardputer_keyboard.c +SRC_C += module/cardputer_keyboard.c diff --git a/ports/espressif/boards/m5stack_cardputer_ros/cardputer_keyboard.c b/ports/espressif/boards/m5stack_cardputer_ros/cardputer_keyboard.c deleted file mode 100644 index 73880f66e19d9..0000000000000 --- a/ports/espressif/boards/m5stack_cardputer_ros/cardputer_keyboard.c +++ /dev/null @@ -1,238 +0,0 @@ -// This file is part of the CircuitPython project: https://circuitpython.org -// -// SPDX-FileCopyrightText: Copyright (c) 2016 Scott Shawcroft -// -// SPDX-License-Identifier: MIT - -#include "py/obj.h" -#include "py/objstr.h" -#include "py/runtime.h" - -#include "supervisor/shared/serial.h" -#include "shared-bindings/keypad/EventQueue.h" -#include "shared-bindings/keypad_demux/DemuxKeyMatrix.h" -#include "shared-bindings/microcontroller/Pin.h" -#include "shared-module/keypad/EventQueue.h" -#include "shared-module/keypad_demux/DemuxKeyMatrix.h" -#include "supervisor/shared/reload.h" - -#include "keymap.h" - -//| """M5Stack Cardputer keyboard integration. -//| """ -//| -//| """The KEYBOARD object is an instance of DemuxKeyMatrix, configured with correct pins. -//| The pins cannot be used for any other purposes (even though exposed in the board module). -//| By default all keyboard events are consumed and routed to the standard input - there is -//| not much use of the KEYBOARD object in this configuration - just read the input via sys.stdin. -//| -//| If you need to manually process individual key up / key down events via KEYBOARD.events, -//| call `detach_serial()`. -//| """" -//| KEYBOARD: keypad_demux.DemuxKeymatrix -//| -keypad_demux_demuxkeymatrix_obj_t cardputer_keyboard_obj; -bool cardputer_keyboard_serial_attached = false; - -void cardputer_keyboard_init(void); -void keyboard_seq(const char *seq); -void update_keyboard(keypad_eventqueue_obj_t *queue); - -//| def detach_serial() -> None: -//| """Stops consuming keyboard events and routing them to sys.stdin.""" -//| ... -//| -static mp_obj_t detach_serial(void) { - cardputer_keyboard_serial_attached = false; - common_hal_keypad_eventqueue_set_event_handler(cardputer_keyboard_obj.events, NULL); - return mp_const_none; -} -static MP_DEFINE_CONST_FUN_OBJ_0(detach_serial_obj, detach_serial); - -//| def attach_serial() -> None: -//| """Starts consuming keyboard events and routing them to sys.stdin.""" -//| ... -//| -static mp_obj_t attach_serial(void) { - common_hal_keypad_eventqueue_set_event_handler(cardputer_keyboard_obj.events, update_keyboard); - cardputer_keyboard_serial_attached = true; - return mp_const_none; -} -static MP_DEFINE_CONST_FUN_OBJ_0(attach_serial_obj, attach_serial); - -//| def key_to_char(key: int, shifted: bool) -> str | None: -//| """Converts a key index to the respective key (with or without shift modifier). -//| Returns None for functional & modifier keys or whenever not 0 <= key < 56. -//| """ -//| ... -//| -static mp_obj_t key_to_char(mp_obj_t key_obj, mp_obj_t shifted_obj) { - mp_int_t key = mp_obj_get_int(key_obj); - if (key < 0 || key > (mp_int_t)(sizeof keymap / sizeof *keymap) || keymap[key] == 0) { - return mp_const_none; - } else if (shifted_obj == mp_const_true) { - return mp_obj_new_str(&keymap_shifted[key], 1); - } else { - return mp_obj_new_str(&keymap[key], 1); - } -} -static MP_DEFINE_CONST_FUN_OBJ_2(key_to_char_obj, key_to_char); - -// Ring buffer of characters consumed from keyboard events (when serial attached) -ringbuf_t keyqueue; -char keybuf[32]; - -keypad_event_obj_t event; -char keystate[56]; - -// Keyboard pins -const mcu_pin_obj_t *row_addr_pins[] = { - &pin_GPIO8, - &pin_GPIO9, - &pin_GPIO11, -}; - -const mcu_pin_obj_t *column_pins[] = { - &pin_GPIO13, - &pin_GPIO15, - &pin_GPIO3, - &pin_GPIO4, - &pin_GPIO5, - &pin_GPIO6, - &pin_GPIO7 -}; - -void cardputer_keyboard_init(void) { - cardputer_keyboard_obj.base.type = &keypad_demux_demuxkeymatrix_type; - common_hal_keypad_demux_demuxkeymatrix_construct( - &cardputer_keyboard_obj, // self - 3, // num_row_addr_pins - row_addr_pins, // row_addr_pins - 7, // num_column_pins - column_pins, // column_pins - true, // columns_to_anodes - false, // transpose - 0.01f, // interval - 20, // max_events - 2 // debounce_threshold - ); - demuxkeymatrix_never_reset(&cardputer_keyboard_obj); - - ringbuf_init(&keyqueue, (uint8_t *)keybuf, sizeof(keybuf)); - attach_serial(); -} - -// Overrides the weakly linked function from supervisor/shared/serial.c -void board_serial_init(void) { - cardputer_keyboard_init(); -} - -// Overrides the weakly linked function from supervisor/shared/serial.c -bool board_serial_connected(void) { - return cardputer_keyboard_serial_attached; -} - -// Overrides the weakly linked function from supervisor/shared/serial.c -uint32_t board_serial_bytes_available(void) { - if (cardputer_keyboard_serial_attached) { - return ringbuf_num_filled(&keyqueue); - } else { - return 0; - } -} - -// Overrides the weakly linked function from supervisor/shared/serial.c -char board_serial_read(void) { - if (cardputer_keyboard_serial_attached) { - return ringbuf_get(&keyqueue); - } else { - return 0; - } -} - -void keyboard_seq(const char *seq) { - while (*seq) { - ringbuf_put(&keyqueue, *seq++); - } -} - -void update_keyboard(keypad_eventqueue_obj_t *queue) { - uint8_t ascii = 0; - - if (common_hal_keypad_eventqueue_get_length(queue) == 0) { - return; - } - - while (common_hal_keypad_eventqueue_get_into(queue, &event)) { - if (event.pressed) { - keystate[event.key_number] = 1; - - if (keystate[KEY_CTRL]) { - if (keystate[KEY_ALT] && keystate[KEY_BACKSPACE]) { - reload_initiate(RUN_REASON_REPL_RELOAD); - } - ascii = keymap[event.key_number]; - if (ascii >= 'a' && ascii <= 'z') { - ascii -= 'a' - 1; - } - - if (ascii == mp_interrupt_char) { - mp_sched_keyboard_interrupt(); - } - } else if (keystate[KEY_SHIFT]) { - ascii = keymap_shifted[event.key_number]; - } else if (keystate[KEY_FN] && event.key_number != KEY_FN) { - switch (event.key_number | FN_MOD) - { - case KEY_DOWN: - keyboard_seq("\e[B"); - break; - case KEY_UP: - keyboard_seq("\e[A"); - break; - case KEY_DELETE: - keyboard_seq("\e[3~"); - break; - case KEY_LEFT: - keyboard_seq("\e[D"); - break; - case KEY_RIGHT: - keyboard_seq("\e[C"); - break; - case KEY_ESC: - ringbuf_put(&keyqueue, '\e'); - break; - } - } else { - ascii = keymap[event.key_number]; - } - - if (ascii > 0) { - if (keystate[KEY_ALT]) { - ringbuf_put(&keyqueue, '\e'); - } else if (keystate[KEY_OPT]) { - ringbuf_put(&keyqueue, '\x10'); - } - ringbuf_put(&keyqueue, ascii); - } - } else { - keystate[event.key_number] = 0; - } - } -} - -static const mp_rom_map_elem_t cardputer_keyboard_module_globals_table[] = { - {MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_cardputer_keyboard)}, - {MP_ROM_QSTR(MP_QSTR_KEYBOARD), MP_ROM_PTR(&cardputer_keyboard_obj)}, - {MP_ROM_QSTR(MP_QSTR_attach_serial), MP_ROM_PTR(&attach_serial_obj)}, - {MP_ROM_QSTR(MP_QSTR_detach_serial), MP_ROM_PTR(&detach_serial_obj)}, - {MP_ROM_QSTR(MP_QSTR_key_to_char), MP_ROM_PTR(&key_to_char_obj)}, -}; -MP_DEFINE_CONST_DICT(cardputer_keyboard_module_globals, cardputer_keyboard_module_globals_table); - -const mp_obj_module_t cardputer_keyboard_module = { - .base = {&mp_type_module}, - .globals = (mp_obj_dict_t *)&cardputer_keyboard_module_globals, -}; - -MP_REGISTER_MODULE(MP_QSTR_cardputer_keyboard, cardputer_keyboard_module); diff --git a/ports/espressif/boards/m5stack_cardputer_ros/keymap.h b/ports/espressif/boards/m5stack_cardputer_ros/keymap.h deleted file mode 100644 index 0256fafaa0f57..0000000000000 --- a/ports/espressif/boards/m5stack_cardputer_ros/keymap.h +++ /dev/null @@ -1,214 +0,0 @@ -// This file is part of the CircuitPython project: https://circuitpython.org -// -// SPDX-FileCopyrightText: Copyright (c) 2020 Scott Shawcroft for Adafruit Industries -// -// SPDX-License-Identifier: MIT - -#pragma once - -#define SHIFT_MOD 0x40 -#define FN_MOD 0x80 - -#define KEY_OPT 0 -#define KEY_Z 1 -#define KEY_C 2 -#define KEY_B 3 -#define KEY_M 4 -#define KEY_DOT 5 -#define KEY_SPACE 6 -#define KEY_SHIFT 7 -#define KEY_S 8 -#define KEY_F 9 -#define KEY_H 10 -#define KEY_K 11 -#define KEY_SEMICOLON 12 -#define KEY_ENTER 13 -#define KEY_Q 14 -#define KEY_E 15 -#define KEY_T 16 -#define KEY_U 17 -#define KEY_O 18 -#define KEY_LEFT_BRACKET 19 -#define KEY_BACKSLASH 20 -#define KEY_1 21 -#define KEY_3 22 -#define KEY_5 23 -#define KEY_7 24 -#define KEY_9 25 -#define KEY_UNDERSCORE 26 -#define KEY_BACKSPACE 27 -#define KEY_CTRL 28 -#define KEY_ALT 29 -#define KEY_X 30 -#define KEY_V 31 -#define KEY_N 32 -#define KEY_COMMA 33 -#define KEY_SLASH 34 -#define KEY_FN 35 -#define KEY_A 36 -#define KEY_D 37 -#define KEY_G 38 -#define KEY_J 39 -#define KEY_L 40 -#define KEY_APOSTROPHE 41 -#define KEY_TAB 42 -#define KEY_W 43 -#define KEY_R 44 -#define KEY_Y 45 -#define KEY_I 46 -#define KEY_P 47 -#define KEY_RIGHT_BRACKET 48 -#define KEY_GRAVE 49 -#define KEY_2 50 -#define KEY_4 51 -#define KEY_6 52 -#define KEY_8 53 -#define KEY_0 54 -#define KEY_EQUALS 55 - -#define KEY_GREATER (5 | SHIFT_MOD) -#define KEY_COLON (12 | SHIFT_MOD) -#define KEY_LEFT_CURLY_BRACKET (19 | SHIFT_MOD) -#define KEY_PIPE (20 | SHIFT_MOD) -#define KEY_EXCLAMATION (21 | SHIFT_MOD) -#define KEY_HASH (22 | SHIFT_MOD) -#define KEY_PERCENT (23 | SHIFT_MOD) -#define KEY_AMPERSAND (24 | SHIFT_MOD) -#define KEY_OPEN_PARENTHESIS (25 | SHIFT_MOD) -#define KEY_MINUS (26 | SHIFT_MOD) -#define KEY_LESS (33 | SHIFT_MOD) -#define KEY_QUESTION (34 | SHIFT_MOD) -#define KEY_DOUBLE_QUOTE (41 | SHIFT_MOD) -#define KEY_RIGHT_CURLY_BRACKET (48 | SHIFT_MOD) -#define KEY_TILDE (49 | SHIFT_MOD) -#define KEY_AT (50 | SHIFT_MOD) -#define KEY_DOLLAR (51 | SHIFT_MOD) -#define KEY_CARET (52 | SHIFT_MOD) -#define KEY_ASTERISK (53 | SHIFT_MOD) -#define KEY_CLOSE_PARENTHESIS (54 | SHIFT_MOD) -#define KEY_PLUS (55 | SHIFT_MOD) - -#define KEY_DOWN (5 | FN_MOD) -#define KEY_UP (12 | FN_MOD) -#define KEY_DELETE (27 | FN_MOD) -#define KEY_LEFT (33 | FN_MOD) -#define KEY_RIGHT (34 | FN_MOD) -#define KEY_ESC (49 | FN_MOD) - -const char keymap[56] = { - 0, // KEY_OPT - 'z', // KEY_Z - 'c', // KEY_C - 'b', // KEY_B - 'm', // KEY_M - '.', // KEY_DOT - ' ', // KEY_SPACE - 0, // KEY_SHIFT - 's', // KEY_S - 'f', // KEY_F - 'h', // KEY_H - 'k', // KEY_K - ';', // KEY_SEMICOLON - '\r',// KEY_ENTER - 'q', // KEY_Q - 'e', // KEY_E - 't', // KEY_T - 'u', // KEY_U - 'o', // KEY_O - '[', // KEY_LEFT_BRACKET - '\\',// KEY_BACKSLASH - '1', // KEY_1 - '3', // KEY_3 - '5', // KEY_5 - '7', // KEY_7 - '9', // KEY_9 - '_', // KEY_UNDERSCORE - '\b',// KEY_BACKSPACE - 0, // KEY_CTRL - 0, // KEY_ALT - 'x', // KEY_X - 'v', // KEY_V - 'n', // KEY_N - ',', // KEY_COMMA - '/', // KEY_SLASH - 0, // KEY_FN - 'a', // KEY_A - 'd', // KEY_D - 'g', // KEY_G - 'j', // KEY_J - 'l', // KEY_L - '\'',// KEY_APOSTROPHE - '\t',// KEY_TAB - 'w', // KEY_W - 'r', // KEY_R - 'y', // KEY_Y - 'i', // KEY_I - 'p', // KEY_P - ']', // KEY_RIGHT_BRACKET - '`', // KEY_GRAVE - '2', // KEY_2 - '4', // KEY_4 - '6', // KEY_6 - '8', // KEY_8 - '0', // KEY_0 - '=' // KEY_EQUALS -}; - -const char keymap_shifted[56] = { - 0, // KEY_OPT - 'Z', // KEY_Z - 'C', // KEY_C - 'B', // KEY_B - 'M', // KEY_M - '>', // KEY_DOT -> '>' - ' ', // KEY_SPACE - 0, // KEY_SHIFT - 'S', // KEY_S - 'F', // KEY_F - 'H', // KEY_H - 'K', // KEY_K - ':', // KEY_SEMICOLON -> ':' - '\r',// KEY_ENTER - 'Q', // KEY_Q - 'E', // KEY_E - 'T', // KEY_T - 'U', // KEY_U - 'O', // KEY_O - '{', // KEY_LEFT_BRACKET -> '{' - '|', // KEY_BACKSLASH -> '|' - '!', // KEY_1 -> '!' - '#', // KEY_3 -> '#' - '%', // KEY_5 -> '%' - '&', // KEY_7 -> '&' - '(', // KEY_9 -> '(' - '-', // KEY_UNDERSCORE -> '-' - '\b',// KEY_BACKSPACE - 0, // KEY_CTRL - 0, // KEY_ALT - 'X', // KEY_X - 'V', // KEY_V - 'N', // KEY_N - '<', // KEY_COMMA -> '<' - '?', // KEY_SLASH -> '?' - 0, // KEY_FN - 'A', // KEY_A - 'D', // KEY_D - 'G', // KEY_G - 'J', // KEY_J - 'L', // KEY_L - '"', // KEY_APOSTROPHE -> '"' - '\t',// KEY_TAB - 'W', // KEY_W - 'R', // KEY_R - 'Y', // KEY_Y - 'I', // KEY_I - 'P', // KEY_P - '}', // KEY_RIGHT_BRACKET -> '}' - '~', // KEY_GRAVE -> '~' - '@', // KEY_2 -> '@' - '$', // KEY_4 -> '$' - '^', // KEY_6 -> '^' - '*', // KEY_8 -> '*' - ')', // KEY_0 -> ')' - '+' // KEY_EQUALS -> '+' -}; diff --git a/ports/espressif/boards/m5stack_cardputer_ros/mpconfigboard.mk b/ports/espressif/boards/m5stack_cardputer_ros/mpconfigboard.mk index 573f947f0a8f5..ede48c2f01523 100644 --- a/ports/espressif/boards/m5stack_cardputer_ros/mpconfigboard.mk +++ b/ports/espressif/boards/m5stack_cardputer_ros/mpconfigboard.mk @@ -15,4 +15,4 @@ CIRCUITPY_RCLCPY = 1 CIRCUITPY_ESPCAMERA = 0 CIRCUITPY_MAX3421E = 0 -SRC_C += boards/$(BOARD)/cardputer_keyboard.c +SRC_C += module/cardputer_keyboard.c diff --git a/ports/espressif/boards/m5stack_cores3/pins.c b/ports/espressif/boards/m5stack_cores3/pins.c index 2ebb225277ded..8b40d9e84842a 100644 --- a/ports/espressif/boards/m5stack_cores3/pins.c +++ b/ports/espressif/boards/m5stack_cores3/pins.c @@ -28,42 +28,59 @@ CIRCUITPY_BOARD_BUS_SINGLETON(porta_i2c, i2c, 1) static const mp_rom_map_elem_t board_module_globals_table[] = { CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS - // M5 Bus (except I2S & PORT B) + // M5 Bus (except I2S) { MP_ROM_QSTR(MP_QSTR_MOSI), MP_ROM_PTR(&pin_GPIO37) }, { MP_ROM_QSTR(MP_QSTR_MISO), MP_ROM_PTR(&pin_GPIO35) }, { MP_ROM_QSTR(MP_QSTR_SCK), MP_ROM_PTR(&pin_GPIO36) }, + { MP_ROM_QSTR(MP_QSTR_RX), MP_ROM_PTR(&pin_GPIO44) }, { MP_ROM_QSTR(MP_QSTR_D44), MP_ROM_PTR(&pin_GPIO44) }, + { MP_ROM_QSTR(MP_QSTR_PORTC_RX), MP_ROM_PTR(&pin_GPIO18) }, { MP_ROM_QSTR(MP_QSTR_D18), MP_ROM_PTR(&pin_GPIO18) }, + { MP_ROM_QSTR(MP_QSTR_A18), MP_ROM_PTR(&pin_GPIO18) }, + { MP_ROM_QSTR(MP_QSTR_SDA), MP_ROM_PTR(&pin_GPIO12) }, - { MP_ROM_QSTR(MP_QSTR_D12), MP_ROM_PTR(&pin_GPIO12) }, + { MP_ROM_QSTR(MP_QSTR_PORTA_SDA), MP_ROM_PTR(&pin_GPIO2) }, { MP_ROM_QSTR(MP_QSTR_D2), MP_ROM_PTR(&pin_GPIO2) }, { MP_ROM_QSTR(MP_QSTR_A2), MP_ROM_PTR(&pin_GPIO2) }, + { MP_ROM_QSTR(MP_QSTR_D6), MP_ROM_PTR(&pin_GPIO6) }, { MP_ROM_QSTR(MP_QSTR_A6), MP_ROM_PTR(&pin_GPIO6) }, - { MP_ROM_QSTR(MP_QSTR_D5), MP_ROM_PTR(&pin_GPIO6) }, + + { MP_ROM_QSTR(MP_QSTR_PORTB_IN), MP_ROM_PTR(&pin_GPIO8) }, + { MP_ROM_QSTR(MP_QSTR_D8), MP_ROM_PTR(&pin_GPIO8) }, + { MP_ROM_QSTR(MP_QSTR_A8), MP_ROM_PTR(&pin_GPIO8) }, + + { MP_ROM_QSTR(MP_QSTR_D5), MP_ROM_PTR(&pin_GPIO5) }, + { MP_ROM_QSTR(MP_QSTR_A5), MP_ROM_PTR(&pin_GPIO5) }, + + { MP_ROM_QSTR(MP_QSTR_PORTB_OUT), MP_ROM_PTR(&pin_GPIO9) }, + { MP_ROM_QSTR(MP_QSTR_D9), MP_ROM_PTR(&pin_GPIO9) }, + { MP_ROM_QSTR(MP_QSTR_A9), MP_ROM_PTR(&pin_GPIO9) }, + { MP_ROM_QSTR(MP_QSTR_TX), MP_ROM_PTR(&pin_GPIO43) }, { MP_ROM_QSTR(MP_QSTR_D43), MP_ROM_PTR(&pin_GPIO43) }, + { MP_ROM_QSTR(MP_QSTR_PORTC_TX), MP_ROM_PTR(&pin_GPIO17) }, { MP_ROM_QSTR(MP_QSTR_D17), MP_ROM_PTR(&pin_GPIO17) }, + { MP_ROM_QSTR(MP_QSTR_A17), MP_ROM_PTR(&pin_GPIO17) }, + { MP_ROM_QSTR(MP_QSTR_SCL), MP_ROM_PTR(&pin_GPIO11) }, + { MP_ROM_QSTR(MP_QSTR_PORTA_SCL), MP_ROM_PTR(&pin_GPIO1) }, + { MP_ROM_QSTR(MP_QSTR_D1), MP_ROM_PTR(&pin_GPIO1) }, + { MP_ROM_QSTR(MP_QSTR_D7), MP_ROM_PTR(&pin_GPIO7) }, { MP_ROM_QSTR(MP_QSTR_A7), MP_ROM_PTR(&pin_GPIO7) }, - // Port B - { MP_ROM_QSTR(MP_QSTR_PORTB_IN), MP_ROM_PTR(&pin_GPIO8) }, - { MP_ROM_QSTR(MP_QSTR_D8), MP_ROM_PTR(&pin_GPIO8) }, - { MP_ROM_QSTR(MP_QSTR_PORTB_OUT), MP_ROM_PTR(&pin_GPIO9) }, - { MP_ROM_QSTR(MP_QSTR_D9), MP_ROM_PTR(&pin_GPIO9) }, - // I2S { MP_ROM_QSTR(MP_QSTR_I2S_BIT_CLOCK), MP_ROM_PTR(&pin_GPIO34) }, { MP_ROM_QSTR(MP_QSTR_I2S_WORD_SELECT), MP_ROM_PTR(&pin_GPIO33) }, - { MP_ROM_QSTR(MP_QSTR_IS2_DATA), MP_ROM_PTR(&pin_GPIO13) }, - { MP_ROM_QSTR(MP_QSTR_IS2_MASTER_CLOCK), MP_ROM_PTR(&pin_GPIO0) }, + { MP_ROM_QSTR(MP_QSTR_I2S_DATA_OUT), MP_ROM_PTR(&pin_GPIO13) }, + { MP_ROM_QSTR(MP_QSTR_I2S_DATA_IN), MP_ROM_PTR(&pin_GPIO14) }, + { MP_ROM_QSTR(MP_QSTR_I2S_MASTER_CLOCK), MP_ROM_PTR(&pin_GPIO0) }, // Camera { MP_ROM_QSTR(MP_QSTR_CAMERA_DATA), MP_ROM_PTR(&camera_data_tuple) }, diff --git a/ports/espressif/boards/prokyber_ai_on_the_edge_cam/board.c b/ports/espressif/boards/prokyber_ai_on_the_edge_cam/board.c new file mode 100644 index 0000000000000..a3a9eec047145 --- /dev/null +++ b/ports/espressif/boards/prokyber_ai_on_the_edge_cam/board.c @@ -0,0 +1,9 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2020 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "supervisor/board.h" + +// Use the MP_WEAK supervisor/shared/board.c versions of routines not defined here. diff --git a/ports/espressif/boards/prokyber_ai_on_the_edge_cam/mpconfigboard.h b/ports/espressif/boards/prokyber_ai_on_the_edge_cam/mpconfigboard.h new file mode 100644 index 0000000000000..2114fecf373a4 --- /dev/null +++ b/ports/espressif/boards/prokyber_ai_on_the_edge_cam/mpconfigboard.h @@ -0,0 +1,22 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2019 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +// Micropython setup + +#define MICROPY_HW_BOARD_NAME "Prokyber Ai-On-The-Edge-Cam" +#define MICROPY_HW_MCU_NAME "ESP32S3" + +#define MICROPY_HW_NEOPIXEL (&pin_GPIO12) + +#define MICROPY_HW_LED_STATUS (&pin_GPIO48) + +#define DEFAULT_UART_BUS_RX (&pin_GPIO44) +#define DEFAULT_UART_BUS_TX (&pin_GPIO43) + +#define CIRCUITPY_BOARD_I2C (1) +#define CIRCUITPY_BOARD_I2C_PIN {{.scl = &pin_GPIO5, .sda = &pin_GPIO4}} diff --git a/ports/espressif/boards/prokyber_ai_on_the_edge_cam/mpconfigboard.mk b/ports/espressif/boards/prokyber_ai_on_the_edge_cam/mpconfigboard.mk new file mode 100644 index 0000000000000..82419bb80745c --- /dev/null +++ b/ports/espressif/boards/prokyber_ai_on_the_edge_cam/mpconfigboard.mk @@ -0,0 +1,14 @@ +USB_VID = 0x1209 +USB_PID = 0xDABB +USB_PRODUCT = "AI-ON-THE-EDGE-CAM" +USB_MANUFACTURER = "Prokyber" + +IDF_TARGET = esp32s3 + +CIRCUITPY_ESP_FLASH_MODE = qio +CIRCUITPY_ESP_FLASH_FREQ = 80m +CIRCUITPY_ESP_FLASH_SIZE = 16MB + +CIRCUITPY_ESP_PSRAM_MODE = opi +CIRCUITPY_ESP_PSRAM_FREQ = 80m +CIRCUITPY_ESP_PSRAM_SIZE = 8MB diff --git a/ports/espressif/boards/prokyber_ai_on_the_edge_cam/pins.c b/ports/espressif/boards/prokyber_ai_on_the_edge_cam/pins.c new file mode 100644 index 0000000000000..68dc40781a480 --- /dev/null +++ b/ports/espressif/boards/prokyber_ai_on_the_edge_cam/pins.c @@ -0,0 +1,106 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2020 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "py/objtuple.h" +#include "shared-bindings/board/__init__.h" + +static const mp_rom_obj_tuple_t camera_data_tuple = { + {&mp_type_tuple}, + 8, + { + MP_ROM_PTR(&pin_GPIO11), + MP_ROM_PTR(&pin_GPIO9), + MP_ROM_PTR(&pin_GPIO8), + MP_ROM_PTR(&pin_GPIO10), + MP_ROM_PTR(&pin_GPIO47), + MP_ROM_PTR(&pin_GPIO18), + MP_ROM_PTR(&pin_GPIO17), + MP_ROM_PTR(&pin_GPIO16), + } +}; + +static const mp_rom_map_elem_t board_module_globals_table[] = { + CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS + + { MP_ROM_QSTR(MP_QSTR_IO0), MP_ROM_PTR(&pin_GPIO0) }, + { MP_ROM_QSTR(MP_QSTR_IO2), MP_ROM_PTR(&pin_GPIO2) }, + + { MP_ROM_QSTR(MP_QSTR_IO46), MP_ROM_PTR(&pin_GPIO46) }, + { MP_ROM_QSTR(MP_QSTR_PER_EN), MP_ROM_PTR(&pin_GPIO46) }, + + // SD + { MP_ROM_QSTR(MP_QSTR_SD_CS), MP_ROM_PTR(&pin_GPIO3) }, + { MP_ROM_QSTR(MP_QSTR_IO3), MP_ROM_PTR(&pin_GPIO3) }, + + { MP_ROM_QSTR(MP_QSTR_SD_MISO), MP_ROM_PTR(&pin_GPIO41) }, + { MP_ROM_QSTR(MP_QSTR_IO41), MP_ROM_PTR(&pin_GPIO41) }, + + { MP_ROM_QSTR(MP_QSTR_SD_MOSI), MP_ROM_PTR(&pin_GPIO42) }, + { MP_ROM_QSTR(MP_QSTR_IO42), MP_ROM_PTR(&pin_GPIO42) }, + + { MP_ROM_QSTR(MP_QSTR_SD_CLK), MP_ROM_PTR(&pin_GPIO40) }, + { MP_ROM_QSTR(MP_QSTR_IO40), MP_ROM_PTR(&pin_GPIO40) }, + + // Ethernet + { MP_ROM_QSTR(MP_QSTR_ETH_RST), MP_ROM_PTR(&pin_GPIO45) }, + { MP_ROM_QSTR(MP_QSTR_IO45), MP_ROM_PTR(&pin_GPIO45) }, + + { MP_ROM_QSTR(MP_QSTR_ETH_INT), MP_ROM_PTR(&pin_GPIO38) }, + { MP_ROM_QSTR(MP_QSTR_IO38), MP_ROM_PTR(&pin_GPIO38) }, + + { MP_ROM_QSTR(MP_QSTR_ETH_MOSI), MP_ROM_PTR(&pin_GPIO1) }, + { MP_ROM_QSTR(MP_QSTR_IO1), MP_ROM_PTR(&pin_GPIO1) }, + + { MP_ROM_QSTR(MP_QSTR_ETH_MISO), MP_ROM_PTR(&pin_GPIO14) }, + { MP_ROM_QSTR(MP_QSTR_IO14), MP_ROM_PTR(&pin_GPIO14) }, + + { MP_ROM_QSTR(MP_QSTR_ETH_CLK), MP_ROM_PTR(&pin_GPIO21) }, + { MP_ROM_QSTR(MP_QSTR_IO21), MP_ROM_PTR(&pin_GPIO21) }, + + { MP_ROM_QSTR(MP_QSTR_ETH_CS), MP_ROM_PTR(&pin_GPIO39) }, + { MP_ROM_QSTR(MP_QSTR_IO39), MP_ROM_PTR(&pin_GPIO39) }, + + // LEDs + { MP_ROM_QSTR(MP_QSTR_NEOPIXEL), MP_ROM_PTR(&pin_GPIO12) }, + { MP_ROM_QSTR(MP_QSTR_IO12), MP_ROM_PTR(&pin_GPIO12) }, + + { MP_ROM_QSTR(MP_QSTR_LED), MP_ROM_PTR(&pin_GPIO48) }, + { MP_ROM_QSTR(MP_QSTR_IO48), MP_ROM_PTR(&pin_GPIO48) }, + + // UART + { MP_ROM_QSTR(MP_QSTR_TX), MP_ROM_PTR(&pin_GPIO43) }, + { MP_ROM_QSTR(MP_QSTR_IO43), MP_ROM_PTR(&pin_GPIO43) }, + + { MP_ROM_QSTR(MP_QSTR_RX), MP_ROM_PTR(&pin_GPIO44) }, + { MP_ROM_QSTR(MP_QSTR_IO44), MP_ROM_PTR(&pin_GPIO44) }, + + // I2C + { MP_ROM_QSTR(MP_QSTR_SDA), MP_ROM_PTR(&pin_GPIO4) }, + { MP_ROM_QSTR(MP_QSTR_SCL), MP_ROM_PTR(&pin_GPIO5) }, + + { MP_ROM_QSTR(MP_QSTR_CAMERA_DATA), MP_ROM_PTR(&camera_data_tuple) }, + + { MP_ROM_QSTR(MP_QSTR_CAMERA_VSYNC), MP_ROM_PTR(&pin_GPIO6) }, + { MP_ROM_QSTR(MP_QSTR_CAMERA_HREF), MP_ROM_PTR(&pin_GPIO7) }, + { MP_ROM_QSTR(MP_QSTR_CAMERA_DATA9), MP_ROM_PTR(&pin_GPIO16) }, + { MP_ROM_QSTR(MP_QSTR_CAMERA_XCLK), MP_ROM_PTR(&pin_GPIO15) }, + { MP_ROM_QSTR(MP_QSTR_CAMERA_DATA8), MP_ROM_PTR(&pin_GPIO17) }, + { MP_ROM_QSTR(MP_QSTR_CAMERA_DATA7), MP_ROM_PTR(&pin_GPIO18) }, + { MP_ROM_QSTR(MP_QSTR_CAMERA_PCLK), MP_ROM_PTR(&pin_GPIO13) }, + { MP_ROM_QSTR(MP_QSTR_CAMERA_DATA6), MP_ROM_PTR(&pin_GPIO47) }, + { MP_ROM_QSTR(MP_QSTR_CAMERA_DATA2), MP_ROM_PTR(&pin_GPIO11) }, + { MP_ROM_QSTR(MP_QSTR_CAMERA_DATA5), MP_ROM_PTR(&pin_GPIO10) }, + { MP_ROM_QSTR(MP_QSTR_CAMERA_DATA3), MP_ROM_PTR(&pin_GPIO9) }, + { MP_ROM_QSTR(MP_QSTR_CAMERA_DATA4), MP_ROM_PTR(&pin_GPIO8) }, + { MP_ROM_QSTR(MP_QSTR_CAMERA_SIOD), MP_ROM_PTR(&pin_GPIO4) }, // SDA + { MP_ROM_QSTR(MP_QSTR_CAMERA_SIOC), MP_ROM_PTR(&pin_GPIO5) }, // SCL + + { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&board_i2c_obj) }, + { MP_ROM_QSTR(MP_QSTR_STEMMA_I2C), MP_ROM_PTR(&board_i2c_obj) }, + { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&board_uart_obj) }, + { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&board_spi_obj) }, +}; +MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table); diff --git a/ports/espressif/boards/prokyber_ai_on_the_edge_cam/sdkconfig b/ports/espressif/boards/prokyber_ai_on_the_edge_cam/sdkconfig new file mode 100644 index 0000000000000..ac1c535717672 --- /dev/null +++ b/ports/espressif/boards/prokyber_ai_on_the_edge_cam/sdkconfig @@ -0,0 +1,14 @@ +# +# Espressif IoT Development Framework Configuration +# +# +# Component config +# +# +# LWIP +# +# end of LWIP +CONFIG_OV2640_SUPPORT=y +# end of Component config + +# end of Espressif IoT Development Framework Configuration diff --git a/ports/espressif/common-hal/rotaryio/IncrementalEncoder.c b/ports/espressif/common-hal/rotaryio/IncrementalEncoder.c index ba8b70221b729..f65c7fdd9f110 100644 --- a/ports/espressif/common-hal/rotaryio/IncrementalEncoder.c +++ b/ports/espressif/common-hal/rotaryio/IncrementalEncoder.c @@ -21,6 +21,10 @@ void common_hal_rotaryio_incrementalencoder_construct(rotaryio_incrementalencode // // These routines also implicitly configure the weak internal pull-ups, as expected // in CircuitPython. + + // Ensure object starts in its deinit state. + common_hal_rotaryio_incrementalencoder_mark_deinit(self); + pcnt_unit_config_t unit_config = { // Set counter limit .low_limit = INT16_MIN, @@ -87,6 +91,10 @@ void common_hal_rotaryio_incrementalencoder_deinit(rotaryio_incrementalencoder_o pcnt_del_channel(self->channel_a); pcnt_del_channel(self->channel_b); pcnt_del_unit(self->unit); + common_hal_rotaryio_incrementalencoder_mark_deinit(self); +} + +void common_hal_rotaryio_incrementalencoder_mark_deinit(rotaryio_incrementalencoder_obj_t *self) { self->unit = NULL; } diff --git a/ports/espressif/common-hal/socketpool/Socket.c b/ports/espressif/common-hal/socketpool/Socket.c index 0a36eff95b454..5128e93c8589e 100644 --- a/ports/espressif/common-hal/socketpool/Socket.c +++ b/ports/espressif/common-hal/socketpool/Socket.c @@ -483,7 +483,7 @@ void common_hal_socketpool_socket_connect(socketpool_socket_obj_t *self, socklen_t socklen = sizeof(error_code); result = getsockopt(self->num, SOL_SOCKET, SO_ERROR, &error_code, &socklen); if (result < 0 || error_code != 0) { - mp_raise_OSError(errno); + mp_raise_OSError(error_code); } self->connected = true; return; diff --git a/ports/espressif/esp-idf-config/sdkconfig.defaults b/ports/espressif/esp-idf-config/sdkconfig.defaults index b54f599b2a5b7..dd766675136d9 100644 --- a/ports/espressif/esp-idf-config/sdkconfig.defaults +++ b/ports/espressif/esp-idf-config/sdkconfig.defaults @@ -71,16 +71,6 @@ CONFIG_LWIP_IPV6_AUTOCONFIG=y CONFIG_LWIP_IPV6_RDNSS_MAX_DNS_SERVERS=0 CONFIG_LWIP_IPV6_DHCP6=y # -# TCP -# -CONFIG_LWIP_MAX_ACTIVE_TCP=4 -CONFIG_LWIP_MAX_LISTENING_TCP=4 -CONFIG_LWIP_TCP_SYNMAXRTX=6 -CONFIG_LWIP_TCP_SND_BUF_DEFAULT=2880 -CONFIG_LWIP_TCP_WND_DEFAULT=2880 -CONFIG_LWIP_TCP_RTO_TIME=3000 -# end of TCP - # end of LWIP # diff --git a/ports/espressif/boards/m5stack_cardputer/cardputer_keyboard.c b/ports/espressif/module/cardputer_keyboard.c similarity index 83% rename from ports/espressif/boards/m5stack_cardputer/cardputer_keyboard.c rename to ports/espressif/module/cardputer_keyboard.c index 73880f66e19d9..548275001acc5 100644 --- a/ports/espressif/boards/m5stack_cardputer/cardputer_keyboard.c +++ b/ports/espressif/module/cardputer_keyboard.c @@ -16,7 +16,7 @@ #include "shared-module/keypad_demux/DemuxKeyMatrix.h" #include "supervisor/shared/reload.h" -#include "keymap.h" +#include "cardputer_keymap.h" //| """M5Stack Cardputer keyboard integration. //| """ @@ -31,12 +31,13 @@ //| """" //| KEYBOARD: keypad_demux.DemuxKeymatrix //| -keypad_demux_demuxkeymatrix_obj_t cardputer_keyboard_obj; -bool cardputer_keyboard_serial_attached = false; -void cardputer_keyboard_init(void); -void keyboard_seq(const char *seq); -void update_keyboard(keypad_eventqueue_obj_t *queue); +keypad_demux_demuxkeymatrix_obj_t cardputer_keyboard; +static bool cardputer_keyboard_serial_attached = false; + +// Forward declarations. +static void keyboard_seq(const char *seq); +static void update_keyboard(keypad_eventqueue_obj_t *queue); //| def detach_serial() -> None: //| """Stops consuming keyboard events and routing them to sys.stdin.""" @@ -44,7 +45,7 @@ void update_keyboard(keypad_eventqueue_obj_t *queue); //| static mp_obj_t detach_serial(void) { cardputer_keyboard_serial_attached = false; - common_hal_keypad_eventqueue_set_event_handler(cardputer_keyboard_obj.events, NULL); + common_hal_keypad_eventqueue_set_event_handler(cardputer_keyboard.events, NULL); return mp_const_none; } static MP_DEFINE_CONST_FUN_OBJ_0(detach_serial_obj, detach_serial); @@ -54,7 +55,7 @@ static MP_DEFINE_CONST_FUN_OBJ_0(detach_serial_obj, detach_serial); //| ... //| static mp_obj_t attach_serial(void) { - common_hal_keypad_eventqueue_set_event_handler(cardputer_keyboard_obj.events, update_keyboard); + common_hal_keypad_eventqueue_set_event_handler(cardputer_keyboard.events, update_keyboard); cardputer_keyboard_serial_attached = true; return mp_const_none; } @@ -79,20 +80,20 @@ static mp_obj_t key_to_char(mp_obj_t key_obj, mp_obj_t shifted_obj) { static MP_DEFINE_CONST_FUN_OBJ_2(key_to_char_obj, key_to_char); // Ring buffer of characters consumed from keyboard events (when serial attached) -ringbuf_t keyqueue; -char keybuf[32]; +static ringbuf_t keyqueue; +static char keybuf[32]; keypad_event_obj_t event; -char keystate[56]; +static char keystate[56] = {0}; // Keyboard pins -const mcu_pin_obj_t *row_addr_pins[] = { +static const mcu_pin_obj_t *row_addr_pins[] = { &pin_GPIO8, &pin_GPIO9, &pin_GPIO11, }; -const mcu_pin_obj_t *column_pins[] = { +static const mcu_pin_obj_t *column_pins[] = { &pin_GPIO13, &pin_GPIO15, &pin_GPIO3, @@ -102,22 +103,22 @@ const mcu_pin_obj_t *column_pins[] = { &pin_GPIO7 }; -void cardputer_keyboard_init(void) { - cardputer_keyboard_obj.base.type = &keypad_demux_demuxkeymatrix_type; +static void cardputer_keyboard_init(void) { common_hal_keypad_demux_demuxkeymatrix_construct( - &cardputer_keyboard_obj, // self - 3, // num_row_addr_pins - row_addr_pins, // row_addr_pins - 7, // num_column_pins - column_pins, // column_pins - true, // columns_to_anodes - false, // transpose - 0.01f, // interval - 20, // max_events - 2 // debounce_threshold + &cardputer_keyboard, // self + MP_ARRAY_SIZE(row_addr_pins), // num_row_addr_pins + row_addr_pins, // row_addr_pins + MP_ARRAY_SIZE(column_pins), // num_column_pins + column_pins, // column_pins + true, // columns_to_anodes + false, // transpose + 0.01f, // interval + 20, // max_events + 2, // debounce_threshold + false // use_gc_allocator ); - demuxkeymatrix_never_reset(&cardputer_keyboard_obj); + demuxkeymatrix_never_reset(&cardputer_keyboard); ringbuf_init(&keyqueue, (uint8_t *)keybuf, sizeof(keybuf)); attach_serial(); } @@ -150,13 +151,13 @@ char board_serial_read(void) { } } -void keyboard_seq(const char *seq) { +static void keyboard_seq(const char *seq) { while (*seq) { ringbuf_put(&keyqueue, *seq++); } } -void update_keyboard(keypad_eventqueue_obj_t *queue) { +static void update_keyboard(keypad_eventqueue_obj_t *queue) { uint8_t ascii = 0; if (common_hal_keypad_eventqueue_get_length(queue) == 0) { @@ -223,7 +224,7 @@ void update_keyboard(keypad_eventqueue_obj_t *queue) { static const mp_rom_map_elem_t cardputer_keyboard_module_globals_table[] = { {MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_cardputer_keyboard)}, - {MP_ROM_QSTR(MP_QSTR_KEYBOARD), MP_ROM_PTR(&cardputer_keyboard_obj)}, + {MP_ROM_QSTR(MP_QSTR_KEYBOARD), MP_ROM_PTR(&cardputer_keyboard)}, {MP_ROM_QSTR(MP_QSTR_attach_serial), MP_ROM_PTR(&attach_serial_obj)}, {MP_ROM_QSTR(MP_QSTR_detach_serial), MP_ROM_PTR(&detach_serial_obj)}, {MP_ROM_QSTR(MP_QSTR_key_to_char), MP_ROM_PTR(&key_to_char_obj)}, diff --git a/ports/espressif/boards/m5stack_cardputer/keymap.h b/ports/espressif/module/cardputer_keymap.h similarity index 100% rename from ports/espressif/boards/m5stack_cardputer/keymap.h rename to ports/espressif/module/cardputer_keymap.h diff --git a/ports/mimxrt10xx/common-hal/rotaryio/IncrementalEncoder.c b/ports/mimxrt10xx/common-hal/rotaryio/IncrementalEncoder.c index c71f8b212e183..211b5c186f3fa 100644 --- a/ports/mimxrt10xx/common-hal/rotaryio/IncrementalEncoder.c +++ b/ports/mimxrt10xx/common-hal/rotaryio/IncrementalEncoder.c @@ -26,6 +26,9 @@ static void encoder_change(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) { + // Ensure object starts in its deinit state. + common_hal_rotaryio_incrementalencoder_mark_deinit(self); + self->pin_a = pin_a; self->pin_b = pin_b; @@ -49,7 +52,7 @@ void common_hal_rotaryio_incrementalencoder_construct(rotaryio_incrementalencode } bool common_hal_rotaryio_incrementalencoder_deinited(rotaryio_incrementalencoder_obj_t *self) { - return !self->pin_a; + return self->pin_a == NULL; } void common_hal_rotaryio_incrementalencoder_deinit(rotaryio_incrementalencoder_obj_t *self) { @@ -62,6 +65,9 @@ void common_hal_rotaryio_incrementalencoder_deinit(rotaryio_incrementalencoder_o common_hal_reset_pin(self->pin_a); common_hal_reset_pin(self->pin_b); + common_hal_rotaryio_incrementalencoder_mark_deinit(self); +} + +void common_hal_rotaryio_incrementalencoder_mark_deinit(rotaryio_incrementalencoder_obj_t *self) { self->pin_a = NULL; - self->pin_b = NULL; } diff --git a/ports/nordic/common-hal/rotaryio/IncrementalEncoder.c b/ports/nordic/common-hal/rotaryio/IncrementalEncoder.c index b0617fae7e566..2e8b308ff64d9 100644 --- a/ports/nordic/common-hal/rotaryio/IncrementalEncoder.c +++ b/ports/nordic/common-hal/rotaryio/IncrementalEncoder.c @@ -32,6 +32,9 @@ static void _intr_handler(nrfx_gpiote_pin_t pin, nrf_gpiote_polarity_t action) { 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) { + // Ensure object starts in its deinit state. + common_hal_rotaryio_incrementalencoder_mark_deinit(self); + self->pin_a = pin_a->number; self->pin_b = pin_b->number; @@ -78,6 +81,10 @@ void common_hal_rotaryio_incrementalencoder_deinit(rotaryio_incrementalencoder_o nrfx_gpiote_in_uninit(self->pin_b); reset_pin_number(self->pin_a); reset_pin_number(self->pin_b); + + common_hal_rotaryio_incrementalencoder_mark_deinit(self); +} + +void common_hal_rotaryio_incrementalencoder_mark_deinit(rotaryio_incrementalencoder_obj_t *self) { self->pin_a = NO_PIN; - self->pin_b = NO_PIN; } diff --git a/ports/raspberrypi/bindings/rp2pio/StateMachine.h b/ports/raspberrypi/bindings/rp2pio/StateMachine.h index afdffd1eccd3b..16f884bcfca32 100644 --- a/ports/raspberrypi/bindings/rp2pio/StateMachine.h +++ b/ports/raspberrypi/bindings/rp2pio/StateMachine.h @@ -42,6 +42,7 @@ void common_hal_rp2pio_statemachine_construct(rp2pio_statemachine_obj_t *self, void common_hal_rp2pio_statemachine_deinit(rp2pio_statemachine_obj_t *self); bool common_hal_rp2pio_statemachine_deinited(rp2pio_statemachine_obj_t *self); +void common_hal_rp2pio_statemachine_mark_deinit(rp2pio_statemachine_obj_t *self); void common_hal_rp2pio_statemachine_never_reset(rp2pio_statemachine_obj_t *self); diff --git a/ports/raspberrypi/common-hal/busio/SPI.c b/ports/raspberrypi/common-hal/busio/SPI.c index d20bc4d7d10aa..4735d1284f98e 100644 --- a/ports/raspberrypi/common-hal/busio/SPI.c +++ b/ports/raspberrypi/common-hal/busio/SPI.c @@ -182,7 +182,10 @@ static bool _transfer(busio_spi_obj_t *self, chan_tx = dma_claim_unused_channel(false); chan_rx = dma_claim_unused_channel(false); } - bool use_dma = chan_rx >= 0 && chan_tx >= 0; + bool has_dma_channels = chan_rx >= 0 && chan_tx >= 0; + // Only use DMA if both data buffers are in SRAM. Otherwise, we'll stall the DMA with PSRAM or flash cache misses. + bool data_in_sram = data_in >= (uint8_t *)SRAM_BASE && data_out >= (uint8_t *)SRAM_BASE; + bool use_dma = has_dma_channels && data_in_sram; if (use_dma) { dma_channel_config c = dma_channel_get_default_config(chan_tx); channel_config_set_transfer_data_size(&c, DMA_SIZE_8); diff --git a/ports/raspberrypi/common-hal/rotaryio/IncrementalEncoder.c b/ports/raspberrypi/common-hal/rotaryio/IncrementalEncoder.c index 113bd1900dd88..d71ff60b2ebc3 100644 --- a/ports/raspberrypi/common-hal/rotaryio/IncrementalEncoder.c +++ b/ports/raspberrypi/common-hal/rotaryio/IncrementalEncoder.c @@ -43,6 +43,9 @@ void common_hal_rotaryio_incrementalencoder_construct(rotaryio_incrementalencode const mcu_pin_obj_t *pin_a, const mcu_pin_obj_t *pin_b) { const mcu_pin_obj_t *pins[] = { pin_a, pin_b }; + // Ensure object starts in its deinit state. + common_hal_rotaryio_incrementalencoder_mark_deinit(self); + // Start out with swapped to match behavior with other ports. self->swapped = true; if (!common_hal_rp2pio_pins_are_sequential(2, pins)) { @@ -89,6 +92,7 @@ void common_hal_rotaryio_incrementalencoder_construct(rotaryio_incrementalencode } bool common_hal_rotaryio_incrementalencoder_deinited(rotaryio_incrementalencoder_obj_t *self) { + // Use the deinit state of the PIO state machine. return common_hal_rp2pio_statemachine_deinited(&self->state_machine); } @@ -100,6 +104,11 @@ void common_hal_rotaryio_incrementalencoder_deinit(rotaryio_incrementalencoder_o common_hal_rp2pio_statemachine_deinit(&self->state_machine); } +void common_hal_rotaryio_incrementalencoder_mark_deinit(rotaryio_incrementalencoder_obj_t *self) { + // Use the deinit state of the PIO state machine. + common_hal_rp2pio_statemachine_mark_deinit(&self->state_machine); +} + static void incrementalencoder_interrupt_handler(void *self_in) { rotaryio_incrementalencoder_obj_t *self = self_in; diff --git a/ports/raspberrypi/common-hal/rp2pio/StateMachine.c b/ports/raspberrypi/common-hal/rp2pio/StateMachine.c index fefecf05ff715..71050677f225f 100644 --- a/ports/raspberrypi/common-hal/rp2pio/StateMachine.c +++ b/ports/raspberrypi/common-hal/rp2pio/StateMachine.c @@ -523,9 +523,10 @@ static void consider_instruction(introspect_t *state, uint16_t full_instruction, } } if (instruction == pio_instr_bits_wait) { - uint16_t wait_source = (full_instruction & 0x0060) >> 5; - uint16_t wait_index = (full_instruction & 0x001f) + state->inputs.pio_gpio_offset; - if (wait_source == 0 && !PIO_PINMASK_IS_SET(state->inputs.pins_we_use, wait_index)) { // GPIO + const uint16_t wait_source = (full_instruction & 0x0060) >> 5; + const uint16_t wait_index = full_instruction & 0x001f; + const uint16_t wait_pin = wait_index + state->inputs.pio_gpio_offset; + if (wait_source == 0 && !PIO_PINMASK_IS_SET(state->inputs.pins_we_use, wait_pin)) { // GPIO mp_raise_ValueError_varg(MP_ERROR_TEXT("%q[%u] uses extra pin"), what_program, i); } else if (wait_source == 1) { // Input pin if (!state->inputs.has_in_pin) { @@ -627,6 +628,9 @@ void common_hal_rp2pio_statemachine_construct(rp2pio_statemachine_obj_t *self, int mov_status_type, int mov_status_n) { + // Ensure object starts in its deinit state. + common_hal_rp2pio_statemachine_mark_deinit(self); + // First, check that all pins are free OR already in use by any PIO if exclusive_pin_use is false. pio_pinmask_t pins_we_use = wait_gpio_mask; PIO_PINMASK_MERGE(pins_we_use, _check_pins_free(first_out_pin, out_pin_count, exclusive_pin_use)); @@ -743,7 +747,7 @@ void common_hal_rp2pio_statemachine_construct(rp2pio_statemachine_obj_t *self, mov_status_type, mov_status_n); if (!ok) { // indicate state machine never inited - self->state_machine = NUM_PIO_STATE_MACHINES; + common_hal_rp2pio_statemachine_mark_deinit(self); mp_raise_RuntimeError(MP_ERROR_TEXT("All state machines in use")); } } @@ -826,6 +830,10 @@ void common_hal_rp2pio_statemachine_deinit(rp2pio_statemachine_obj_t *self) { rp2pio_statemachine_deinit(self, false); } +void common_hal_rp2pio_statemachine_mark_deinit(rp2pio_statemachine_obj_t *self) { + self->state_machine = NUM_PIO_STATE_MACHINES; +} + void common_hal_rp2pio_statemachine_never_reset(rp2pio_statemachine_obj_t *self) { rp2pio_statemachine_never_reset(self->pio, self->state_machine); // TODO: never reset all the pins @@ -853,25 +861,64 @@ static bool _transfer(rp2pio_statemachine_obj_t *self, // 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. - // Don't exceed FIFO size. - const size_t dma_min_size_threshold = self->fifo_depth; 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; + bool free_data_out = false; + bool free_data_in = false; + uint8_t *sram_data_out = (uint8_t *)data_out; + uint8_t *sram_data_in = data_in; + bool tx_fits_in_fifo = (out_len / out_stride_in_bytes) <= self->fifo_depth; + bool rx_fits_in_fifo = (in_len / in_stride_in_bytes) <= self->fifo_depth; + bool use_dma = !(tx_fits_in_fifo && rx_fits_in_fifo) || swap_out || swap_in; + if (use_dma) { - // Use DMA channels to service the two FIFOs + // We can only reliably use DMA for SRAM buffers. So, if we're given PSRAM buffers, + // then copy them to SRAM first. If we can't, then fail. + // Use DMA channels to service the two FIFOs. Fail if we can't allocate DMA channels. if (tx) { + if (data_out < (uint8_t *)SRAM_BASE) { + // Try to allocate a temporary buffer for DMA transfer + uint8_t *temp_buffer = (uint8_t *)port_malloc(len, true); + if (temp_buffer == NULL) { + mp_printf(&mp_plat_print, "Failed to allocate temporary buffer for DMA tx\n"); + return false; + } + memcpy(temp_buffer, data_out, len); + sram_data_out = temp_buffer; + free_data_out = true; + } chan_tx = dma_claim_unused_channel(false); // DMA allocation failed... if (chan_tx < 0) { + if (free_data_out) { + port_free(sram_data_out); + } + if (free_data_in) { + port_free(sram_data_in); + } return false; } } if (rx) { + if (data_in < (uint8_t *)SRAM_BASE) { + // Try to allocate a temporary buffer for DMA transfer + uint8_t *temp_buffer = (uint8_t *)port_malloc(len, true); + if (temp_buffer == NULL) { + mp_printf(&mp_plat_print, "Failed to allocate temporary buffer for DMA rx\n"); + if (chan_tx >= 0) { + dma_channel_unclaim(chan_tx); + } + if (free_data_out) { + port_free(sram_data_out); + } + return false; + } + sram_data_in = temp_buffer; + free_data_in = true; + } chan_rx = dma_claim_unused_channel(false); // DMA allocation failed... if (chan_rx < 0) { @@ -879,6 +926,12 @@ static bool _transfer(rp2pio_statemachine_obj_t *self, if (chan_tx >= 0) { dma_channel_unclaim(chan_tx); } + if (free_data_out) { + port_free(sram_data_out); + } + if (free_data_in) { + port_free(sram_data_in); + } return false; } } @@ -910,7 +963,7 @@ static bool _transfer(rp2pio_statemachine_obj_t *self, channel_config_set_bswap(&c, swap_out); dma_channel_configure(chan_tx, &c, tx_destination, - data_out, + sram_data_out, out_len / out_stride_in_bytes, false); channel_mask |= 1u << chan_tx; @@ -923,7 +976,7 @@ static bool _transfer(rp2pio_statemachine_obj_t *self, channel_config_set_write_increment(&c, true); channel_config_set_bswap(&c, swap_in); dma_channel_configure(chan_rx, &c, - data_in, + sram_data_in, rx_source, in_len / in_stride_in_bytes, false); @@ -950,8 +1003,7 @@ static bool _transfer(rp2pio_statemachine_obj_t *self, 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. + // Release the DMA channels after use_dma has been done. if (chan_rx >= 0) { dma_channel_unclaim(chan_rx); } @@ -960,31 +1012,31 @@ static bool _transfer(rp2pio_statemachine_obj_t *self, } if (!use_dma && !(self->user_interruptible && mp_hal_is_interrupted())) { - // Use software for small transfers, or if couldn't claim two DMA channels + // Use software for small transfers 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; + *tx_destination = *sram_data_out; } else if (out_stride_in_bytes == 2) { - *((uint16_t *)tx_destination) = *((uint16_t *)data_out); + *((uint16_t *)tx_destination) = *((uint16_t *)sram_data_out); } else if (out_stride_in_bytes == 4) { - *((uint32_t *)tx_destination) = *((uint32_t *)data_out); + *((uint32_t *)tx_destination) = *((uint32_t *)sram_data_out); } - data_out += out_stride_in_bytes; + sram_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; + *sram_data_in = (uint8_t)*rx_source; } else if (in_stride_in_bytes == 2) { - *((uint16_t *)data_in) = *((uint16_t *)rx_source); + *((uint16_t *)sram_data_in) = *((uint16_t *)rx_source); } else if (in_stride_in_bytes == 4) { - *((uint32_t *)data_in) = *((uint32_t *)rx_source); + *((uint32_t *)sram_data_in) = *((uint32_t *)rx_source); } - data_in += in_stride_in_bytes; + sram_data_in += in_stride_in_bytes; --rx_remaining; } RUN_BACKGROUND_TASKS; @@ -996,7 +1048,7 @@ static bool _transfer(rp2pio_statemachine_obj_t *self, self->pio->fdebug = stall_mask; } // Wait for the state machine to finish transmitting the data we've queued - // up. + // up (either from the CPU or via DMA.) if (tx) { while (!pio_sm_is_tx_fifo_empty(self->pio, self->state_machine) || (self->wait_for_txstall && (self->pio->fdebug & stall_mask) == 0)) { @@ -1006,6 +1058,14 @@ static bool _transfer(rp2pio_statemachine_obj_t *self, } } } + if (free_data_out) { + port_free(sram_data_out); + } + if (free_data_in) { + // Copy the data from the SRAM buffer to the user PSRAM buffer. + memcpy(data_in, sram_data_in, len); + port_free(sram_data_in); + } return true; } diff --git a/ports/raspberrypi/common-hal/socketpool/Socket.c b/ports/raspberrypi/common-hal/socketpool/Socket.c index 018365def8c75..aeab1498510a0 100644 --- a/ports/raspberrypi/common-hal/socketpool/Socket.c +++ b/ports/raspberrypi/common-hal/socketpool/Socket.c @@ -89,7 +89,7 @@ static mp_obj_t socketpool_ip_addr_and_port_to_tuple(const ip_addr_t *addr, int // socket API. // Extension to lwIP error codes -// Matches lwIP 2.0.3 +// Matches lwIP 2.2.1 #undef _ERR_BADF #define _ERR_BADF -17 static const int error_lookup_table[] = { @@ -473,7 +473,12 @@ static mp_uint_t lwip_tcp_send(socketpool_socket_obj_t *socket, const byte *buf, MICROPY_PY_LWIP_ENTER - u16_t available = tcp_sndbuf(socket->pcb.tcp); + // If the socket is still connecting then don't let data be written to it. + // Otherwise, get the number of available bytes in the output buffer. + u16_t available = 0; + if (socket->state != STATE_CONNECTING) { + available = tcp_sndbuf(socket->pcb.tcp); + } if (available == 0) { // Non-blocking socket @@ -490,7 +495,8 @@ static mp_uint_t lwip_tcp_send(socketpool_socket_obj_t *socket, const byte *buf, // If peer fully closed socket, we would have socket->state set to ERR_RST (connection // reset) by error callback. // Avoid sending too small packets, so wait until at least 16 bytes available - while (socket->state >= STATE_CONNECTED && (available = tcp_sndbuf(socket->pcb.tcp)) < 16) { + while (socket->state == STATE_CONNECTING + || (socket->state >= STATE_CONNECTED && (available = tcp_sndbuf(socket->pcb.tcp)) < 16)) { MICROPY_PY_LWIP_EXIT if (socket->timeout != (unsigned)-1 && mp_hal_ticks_ms() - start > socket->timeout) { *_errno = MP_ETIMEDOUT; @@ -531,9 +537,10 @@ static mp_uint_t lwip_tcp_send(socketpool_socket_obj_t *socket, const byte *buf, MICROPY_PY_LWIP_REENTER } - // If the output buffer is getting full then send the data to the lower layers - if (err == ERR_OK && tcp_sndbuf(socket->pcb.tcp) < TCP_SND_BUF / 4) { - err = tcp_output(socket->pcb.tcp); + // Use nagle algorithm to determine when to send segment buffer (can be + // disabled with TCP_NODELAY socket option) + if (err == ERR_OK) { + err = tcp_output_nagle(socket->pcb.tcp); } MICROPY_PY_LWIP_EXIT @@ -551,6 +558,12 @@ static mp_uint_t lwip_tcp_receive(socketpool_socket_obj_t *socket, byte *buf, mp // Check for any pending errors STREAM_ERROR_CHECK(socket); + if (socket->state == STATE_LISTENING) { + // original socket in listening state, not the accepted connection. + *_errno = MP_ENOTCONN; + return -1; + } + if (socket->incoming.pbuf == NULL) { // Non-blocking socket diff --git a/ports/raspberrypi/lwip_src/lwip_mem.c b/ports/raspberrypi/lwip_src/lwip_mem.c index 244f51e289bc9..23b185354b072 100644 --- a/ports/raspberrypi/lwip_src/lwip_mem.c +++ b/ports/raspberrypi/lwip_src/lwip_mem.c @@ -8,10 +8,12 @@ #include #include "lib/tlsf/tlsf.h" #include "lwip_mem.h" +#include "shared-bindings/microcontroller/__init__.h" #include "supervisor/port_heap.h" void *lwip_heap_malloc(size_t size) { - return port_malloc(size, true); + void *ptr = port_malloc(size, true); + return ptr; } void lwip_heap_free(void *ptr) { diff --git a/ports/raspberrypi/mpconfigport.mk b/ports/raspberrypi/mpconfigport.mk index 1404b2b06777c..bbce13d13811f 100644 --- a/ports/raspberrypi/mpconfigport.mk +++ b/ports/raspberrypi/mpconfigport.mk @@ -59,6 +59,9 @@ CIRCUITPY_CYW43_INIT_DELAY ?= 1000 endif ifeq ($(CHIP_VARIANT),RP2350) +# RP2350 has PSRAM that is not DMA-capable +CIRCUITPY_ALL_MEMORY_DMA_CAPABLE = 0 + # This needs to be implemented. CIRCUITPY_ALARM = 0 # Default PICODVI on because it doesn't require much code in RAM to talk to HSTX. diff --git a/ports/raspberrypi/supervisor/port.c b/ports/raspberrypi/supervisor/port.c index 4be42380491cb..66e63248c4810 100644 --- a/ports/raspberrypi/supervisor/port.c +++ b/ports/raspberrypi/supervisor/port.c @@ -264,33 +264,52 @@ void port_heap_init(void) { void *port_malloc(size_t size, bool dma_capable) { if (!dma_capable && _psram_size > 0) { + common_hal_mcu_disable_interrupts(); void *block = tlsf_malloc(_psram_heap, size); + common_hal_mcu_enable_interrupts(); if (block) { return block; } } + common_hal_mcu_disable_interrupts(); void *block = tlsf_malloc(_heap, size); + common_hal_mcu_enable_interrupts(); return block; } void port_free(void *ptr) { + common_hal_mcu_disable_interrupts(); if (((size_t)ptr) < SRAM_BASE) { tlsf_free(_psram_heap, ptr); } else { tlsf_free(_heap, ptr); } + common_hal_mcu_enable_interrupts(); } void *port_realloc(void *ptr, size_t size, bool dma_capable) { if (_psram_size > 0 && ((ptr != NULL && ((size_t)ptr) < SRAM_BASE) || (ptr == NULL && !dma_capable))) { + common_hal_mcu_disable_interrupts(); void *block = tlsf_realloc(_psram_heap, ptr, size); + common_hal_mcu_enable_interrupts(); if (block) { return block; } } - return tlsf_realloc(_heap, ptr, size); + common_hal_mcu_disable_interrupts(); + void *new_ptr = tlsf_realloc(_heap, ptr, size); + common_hal_mcu_enable_interrupts(); + return new_ptr; } +#if !CIRCUITPY_ALL_MEMORY_DMA_CAPABLE +bool port_buffer_is_dma_capable(const void *ptr) { + // For RP2350, DMA can only access SRAM, not PSRAM + // PSRAM addresses are below SRAM_BASE + return ptr != NULL && ((size_t)ptr) >= SRAM_BASE; +} +#endif + static bool max_size_walker(void *ptr, size_t size, int used, void *user) { size_t *max_size = (size_t *)user; if (!used && *max_size < size) { diff --git a/py/builtinimport.c b/py/builtinimport.c index 17b3f11c4a001..e4e06d8e2cc8b 100644 --- a/py/builtinimport.c +++ b/py/builtinimport.c @@ -451,7 +451,9 @@ static mp_obj_t process_import_at_level(qstr full_mod_name, qstr level_mod_name, if (stat == MP_IMPORT_STAT_NO_EXIST) { // Not found -- fail. - #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE + // CIRCUITPY-CHANGE: always the use more verbose error message that names missing module. + // Otherwise `import a` where `a` imports `b`, but `b` is missing will give a confusing error. + #if 0 && (MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE) mp_raise_msg(&mp_type_ImportError, MP_ERROR_TEXT("module not found")); #else mp_raise_msg_varg(&mp_type_ImportError, MP_ERROR_TEXT("no module named '%q'"), full_mod_name); diff --git a/py/circuitpy_mpconfig.h b/py/circuitpy_mpconfig.h index 3ca06f7a0fd3a..afa9c4b795c92 100644 --- a/py/circuitpy_mpconfig.h +++ b/py/circuitpy_mpconfig.h @@ -453,6 +453,7 @@ void background_callback_run_all(void); #define MICROPY_VM_HOOK_LOOP RUN_BACKGROUND_TASKS; #define MICROPY_VM_HOOK_RETURN RUN_BACKGROUND_TASKS; +#define MICROPY_INTERNAL_EVENT_HOOK (RUN_BACKGROUND_TASKS) // CIRCUITPY_AUTORELOAD_DELAY_MS = 0 will completely disable autoreload. #ifndef CIRCUITPY_AUTORELOAD_DELAY_MS diff --git a/py/circuitpy_mpconfig.mk b/py/circuitpy_mpconfig.mk index 2009c4e177da6..8c838a41218a3 100644 --- a/py/circuitpy_mpconfig.mk +++ b/py/circuitpy_mpconfig.mk @@ -98,6 +98,11 @@ CFLAGS += -DCIRCUITPY_ALARM=$(CIRCUITPY_ALARM) CIRCUITPY_ALARM_TOUCH ?= $(CIRCUITPY_ALARM) CFLAGS += -DCIRCUITPY_ALARM_TOUCH=$(CIRCUITPY_ALARM_TOUCH) +# Enable DMA buffer management for platforms where not all memory is DMA-capable +# Platforms with PSRAM or other non-DMA memory should set this to 0 +CIRCUITPY_ALL_MEMORY_DMA_CAPABLE ?= 1 +CFLAGS += -DCIRCUITPY_ALL_MEMORY_DMA_CAPABLE=$(CIRCUITPY_ALL_MEMORY_DMA_CAPABLE) + CIRCUITPY_ANALOGBUFIO ?= 0 CFLAGS += -DCIRCUITPY_ANALOGBUFIO=$(CIRCUITPY_ANALOGBUFIO) diff --git a/py/emitinlinethumb.c b/py/emitinlinethumb.c index 7818bb4f46da8..d6596337ae5a6 100644 --- a/py/emitinlinethumb.c +++ b/py/emitinlinethumb.c @@ -150,27 +150,27 @@ typedef struct _reg_name_t { byte reg; byte name[3]; } reg_name_t; static const reg_name_t reg_name_table[] = { - {0, "r0\0"}, - {1, "r1\0"}, - {2, "r2\0"}, - {3, "r3\0"}, - {4, "r4\0"}, - {5, "r5\0"}, - {6, "r6\0"}, - {7, "r7\0"}, - {8, "r8\0"}, - {9, "r9\0"}, - {10, "r10"}, - {11, "r11"}, - {12, "r12"}, - {13, "r13"}, - {14, "r14"}, - {15, "r15"}, - {10, "sl\0"}, - {11, "fp\0"}, - {13, "sp\0"}, - {14, "lr\0"}, - {15, "pc\0"}, + {0, {'r', '0' }}, + {1, {'r', '1' }}, + {2, {'r', '2' }}, + {3, {'r', '3' }}, + {4, {'r', '4' }}, + {5, {'r', '5' }}, + {6, {'r', '6' }}, + {7, {'r', '7' }}, + {8, {'r', '8' }}, + {9, {'r', '9' }}, + {10, {'r', '1', '0' }}, + {11, {'r', '1', '1' }}, + {12, {'r', '1', '2' }}, + {13, {'r', '1', '3' }}, + {14, {'r', '1', '4' }}, + {15, {'r', '1', '5' }}, + {10, {'s', 'l' }}, + {11, {'f', 'p' }}, + {13, {'s', 'p' }}, + {14, {'l', 'r' }}, + {15, {'p', 'c' }}, }; #define MAX_SPECIAL_REGISTER_NAME_LENGTH 7 @@ -368,20 +368,20 @@ typedef struct _cc_name_t { byte cc; byte name[2]; } cc_name_t; static const cc_name_t cc_name_table[] = { - { ASM_THUMB_CC_EQ, "eq" }, - { ASM_THUMB_CC_NE, "ne" }, - { ASM_THUMB_CC_CS, "cs" }, - { ASM_THUMB_CC_CC, "cc" }, - { ASM_THUMB_CC_MI, "mi" }, - { ASM_THUMB_CC_PL, "pl" }, - { ASM_THUMB_CC_VS, "vs" }, - { ASM_THUMB_CC_VC, "vc" }, - { ASM_THUMB_CC_HI, "hi" }, - { ASM_THUMB_CC_LS, "ls" }, - { ASM_THUMB_CC_GE, "ge" }, - { ASM_THUMB_CC_LT, "lt" }, - { ASM_THUMB_CC_GT, "gt" }, - { ASM_THUMB_CC_LE, "le" }, + { ASM_THUMB_CC_EQ, { 'e', 'q' }}, + { ASM_THUMB_CC_NE, { 'n', 'e' }}, + { ASM_THUMB_CC_CS, { 'c', 's' }}, + { ASM_THUMB_CC_CC, { 'c', 'c' }}, + { ASM_THUMB_CC_MI, { 'm', 'i' }}, + { ASM_THUMB_CC_PL, { 'p', 'l' }}, + { ASM_THUMB_CC_VS, { 'v', 's' }}, + { ASM_THUMB_CC_VC, { 'v', 'c' }}, + { ASM_THUMB_CC_HI, { 'h', 'i' }}, + { ASM_THUMB_CC_LS, { 'l', 's' }}, + { ASM_THUMB_CC_GE, { 'g', 'e' }}, + { ASM_THUMB_CC_LT, { 'l', 't' }}, + { ASM_THUMB_CC_GT, { 'g', 't' }}, + { ASM_THUMB_CC_LE, { 'l', 'e' }}, }; typedef struct _format_4_op_t { byte op; @@ -389,21 +389,21 @@ typedef struct _format_4_op_t { byte op; } format_4_op_t; #define X(x) (((x) >> 4) & 0xff) // only need 1 byte to distinguish these ops static const format_4_op_t format_4_op_table[] = { - { X(ASM_THUMB_FORMAT_4_EOR), "eor" }, - { X(ASM_THUMB_FORMAT_4_LSL), "lsl" }, - { X(ASM_THUMB_FORMAT_4_LSR), "lsr" }, - { X(ASM_THUMB_FORMAT_4_ASR), "asr" }, - { X(ASM_THUMB_FORMAT_4_ADC), "adc" }, - { X(ASM_THUMB_FORMAT_4_SBC), "sbc" }, - { X(ASM_THUMB_FORMAT_4_ROR), "ror" }, - { X(ASM_THUMB_FORMAT_4_TST), "tst" }, - { X(ASM_THUMB_FORMAT_4_NEG), "neg" }, - { X(ASM_THUMB_FORMAT_4_CMP), "cmp" }, - { X(ASM_THUMB_FORMAT_4_CMN), "cmn" }, - { X(ASM_THUMB_FORMAT_4_ORR), "orr" }, - { X(ASM_THUMB_FORMAT_4_MUL), "mul" }, - { X(ASM_THUMB_FORMAT_4_BIC), "bic" }, - { X(ASM_THUMB_FORMAT_4_MVN), "mvn" }, + { X(ASM_THUMB_FORMAT_4_EOR), {'e', 'o', 'r' }}, + { X(ASM_THUMB_FORMAT_4_LSL), {'l', 's', 'l' }}, + { X(ASM_THUMB_FORMAT_4_LSR), {'l', 's', 'r' }}, + { X(ASM_THUMB_FORMAT_4_ASR), {'a', 's', 'r' }}, + { X(ASM_THUMB_FORMAT_4_ADC), {'a', 'd', 'c' }}, + { X(ASM_THUMB_FORMAT_4_SBC), {'s', 'b', 'c' }}, + { X(ASM_THUMB_FORMAT_4_ROR), {'r', 'o', 'r' }}, + { X(ASM_THUMB_FORMAT_4_TST), {'t', 's', 't' }}, + { X(ASM_THUMB_FORMAT_4_NEG), {'n', 'e', 'g' }}, + { X(ASM_THUMB_FORMAT_4_CMP), {'c', 'm', 'p' }}, + { X(ASM_THUMB_FORMAT_4_CMN), {'c', 'm', 'n' }}, + { X(ASM_THUMB_FORMAT_4_ORR), {'o', 'r', 'r' }}, + { X(ASM_THUMB_FORMAT_4_MUL), {'m', 'u', 'l' }}, + { X(ASM_THUMB_FORMAT_4_BIC), {'b', 'i', 'c' }}, + { X(ASM_THUMB_FORMAT_4_MVN), {'m', 'v', 'n' }}, }; #undef X @@ -428,10 +428,10 @@ typedef struct _format_vfp_op_t { char name[3]; } format_vfp_op_t; static const format_vfp_op_t format_vfp_op_table[] = { - { 0x30, "add" }, - { 0x34, "sub" }, - { 0x20, "mul" }, - { 0x80, "div" }, + { 0x30, {'a', 'd', 'd' }}, + { 0x34, {'s', 'u', 'b' }}, + { 0x20, {'m', 'u', 'l' }}, + { 0x80, {'d', 'i', 'v' }}, }; // shorthand alias for whether we allow ARMv7-M instructions diff --git a/py/objtuple.c b/py/objtuple.c index 42e8d56a806cc..e0b31edaf2376 100644 --- a/py/objtuple.c +++ b/py/objtuple.c @@ -250,6 +250,9 @@ MP_DEFINE_CONST_OBJ_TYPE( // the zero-length tuple const mp_obj_tuple_t mp_const_empty_tuple_obj = {{&mp_type_tuple}, 0}; +// CIRCUITPY-CHANGE: No change here, but implementation was copied for +// mp_obj_new_port_tuple in supervisor/shared/port.c, which allocates using port_malloc(). +// Change that to match if this changes. mp_obj_t mp_obj_new_tuple(size_t n, const mp_obj_t *items) { if (n == 0) { return mp_const_empty_tuple; diff --git a/py/persistentcode.c b/py/persistentcode.c index 1976729f4d879..93f4c33deb4e2 100644 --- a/py/persistentcode.c +++ b/py/persistentcode.c @@ -471,6 +471,9 @@ void mp_raw_code_load(mp_reader_t *reader, mp_compiled_module_t *cm) { read_bytes(reader, header, sizeof(header)); byte arch = MPY_FEATURE_DECODE_ARCH(header[2]); // CIRCUITPY-CHANGE: 'C', not 'M' + if (header[0] == 'M') { + mp_raise_ValueError(MP_ERROR_TEXT("MicroPython .mpy file; use CircuitPython mpy-cross")); + } if (header[0] != 'C' || header[1] != MPY_VERSION || (arch != MP_NATIVE_ARCH_NONE && MPY_FEATURE_DECODE_SUB_VERSION(header[2]) != MPY_SUB_VERSION) diff --git a/py/scheduler.c b/py/scheduler.c index 5984346a6f68a..92e5af6b31b20 100644 --- a/py/scheduler.c +++ b/py/scheduler.c @@ -277,10 +277,6 @@ void mp_event_wait_indefinite(void) { MICROPY_EVENT_POLL_HOOK #else mp_event_handle_nowait(); - - // CIRCUITPY-CHANGE: don't starve CircuitPython background tasks - RUN_BACKGROUND_TASKS; - MICROPY_INTERNAL_WFE(-1); #endif } @@ -293,10 +289,6 @@ void mp_event_wait_ms(mp_uint_t timeout_ms) { MICROPY_EVENT_POLL_HOOK #else mp_event_handle_nowait(); - - // CIRCUITPY-CHANGE: don't starve CircuitPython background tasks - RUN_BACKGROUND_TASKS; - MICROPY_INTERNAL_WFE(timeout_ms); #endif } diff --git a/shared-bindings/audiodelays/Chorus.c b/shared-bindings/audiodelays/Chorus.c index c6bfa2ffa7eb1..87922ff110b1b 100644 --- a/shared-bindings/audiodelays/Chorus.c +++ b/shared-bindings/audiodelays/Chorus.c @@ -206,11 +206,15 @@ MP_DEFINE_CONST_FUN_OBJ_1(audiodelays_chorus_get_playing_obj, audiodelays_chorus MP_PROPERTY_GETTER(audiodelays_chorus_playing_obj, (mp_obj_t)&audiodelays_chorus_get_playing_obj); -//| def play(self, sample: circuitpython_typing.AudioSample, *, loop: bool = False) -> None: +//| def play(self, sample: circuitpython_typing.AudioSample, *, loop: bool = False) -> Chorus: //| """Plays the sample once when loop=False and continuously when loop=True. //| Does not block. Use `playing` to block. //| -//| The sample must match the encoding settings given in the constructor.""" +//| The sample must match the encoding settings given in the constructor. +//| +//| :return: The effect object itself. Can be used for chaining, ie: +//| ``audio.play(effect.play(sample))``. +//| :rtype: Chorus""" //| ... //| static mp_obj_t audiodelays_chorus_obj_play(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { @@ -228,7 +232,7 @@ static mp_obj_t audiodelays_chorus_obj_play(size_t n_args, const mp_obj_t *pos_a mp_obj_t sample = args[ARG_sample].u_obj; common_hal_audiodelays_chorus_play(self, sample, args[ARG_loop].u_bool); - return mp_const_none; + return MP_OBJ_FROM_PTR(self); } MP_DEFINE_CONST_FUN_OBJ_KW(audiodelays_chorus_play_obj, 1, audiodelays_chorus_obj_play); diff --git a/shared-bindings/audiodelays/Echo.c b/shared-bindings/audiodelays/Echo.c index 5ae849b19aa41..dbb496c61b975 100644 --- a/shared-bindings/audiodelays/Echo.c +++ b/shared-bindings/audiodelays/Echo.c @@ -230,11 +230,15 @@ MP_DEFINE_CONST_FUN_OBJ_1(audiodelays_echo_get_playing_obj, audiodelays_echo_obj MP_PROPERTY_GETTER(audiodelays_echo_playing_obj, (mp_obj_t)&audiodelays_echo_get_playing_obj); -//| def play(self, sample: circuitpython_typing.AudioSample, *, loop: bool = False) -> None: +//| def play(self, sample: circuitpython_typing.AudioSample, *, loop: bool = False) -> Echo: //| """Plays the sample once when loop=False and continuously when loop=True. //| Does not block. Use `playing` to block. //| -//| The sample must match the encoding settings given in the constructor.""" +//| The sample must match the encoding settings given in the constructor. +//| +//| :return: The effect object itself. Can be used for chaining, ie: +//| ``audio.play(effect.play(sample))``. +//| :rtype: Echo""" //| ... //| static mp_obj_t audiodelays_echo_obj_play(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { @@ -252,7 +256,7 @@ static mp_obj_t audiodelays_echo_obj_play(size_t n_args, const mp_obj_t *pos_arg mp_obj_t sample = args[ARG_sample].u_obj; common_hal_audiodelays_echo_play(self, sample, args[ARG_loop].u_bool); - return mp_const_none; + return MP_OBJ_FROM_PTR(self); } MP_DEFINE_CONST_FUN_OBJ_KW(audiodelays_echo_play_obj, 1, audiodelays_echo_obj_play); diff --git a/shared-bindings/audiodelays/MultiTapDelay.c b/shared-bindings/audiodelays/MultiTapDelay.c index 5b057eaf80d1b..2126dbf990435 100644 --- a/shared-bindings/audiodelays/MultiTapDelay.c +++ b/shared-bindings/audiodelays/MultiTapDelay.c @@ -233,11 +233,15 @@ MP_DEFINE_CONST_FUN_OBJ_1(audiodelays_multi_tap_delay_get_playing_obj, audiodela MP_PROPERTY_GETTER(audiodelays_multi_tap_delay_playing_obj, (mp_obj_t)&audiodelays_multi_tap_delay_get_playing_obj); -//| def play(self, sample: circuitpython_typing.AudioSample, *, loop: bool = False) -> None: +//| def play(self, sample: circuitpython_typing.AudioSample, *, loop: bool = False) -> MultiTapDelay: //| """Plays the sample once when loop=False and continuously when loop=True. //| Does not block. Use `playing` to block. //| -//| The sample must match the encoding settings given in the constructor.""" +//| The sample must match the encoding settings given in the constructor. +//| +//| :return: The effect object itself. Can be used for chaining, ie: +//| ``audio.play(effect.play(sample))``. +//| :rtype: MultiTapDelay""" //| ... //| static mp_obj_t audiodelays_multi_tap_delay_obj_play(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { @@ -255,7 +259,7 @@ static mp_obj_t audiodelays_multi_tap_delay_obj_play(size_t n_args, const mp_obj mp_obj_t sample = args[ARG_sample].u_obj; common_hal_audiodelays_multi_tap_delay_play(self, sample, args[ARG_loop].u_bool); - return mp_const_none; + return MP_OBJ_FROM_PTR(self); } MP_DEFINE_CONST_FUN_OBJ_KW(audiodelays_multi_tap_delay_play_obj, 1, audiodelays_multi_tap_delay_obj_play); diff --git a/shared-bindings/audiodelays/PitchShift.c b/shared-bindings/audiodelays/PitchShift.c index df2189945aa5d..b94df7d9edd08 100644 --- a/shared-bindings/audiodelays/PitchShift.c +++ b/shared-bindings/audiodelays/PitchShift.c @@ -190,11 +190,15 @@ MP_PROPERTY_GETTER(audiodelays_pitch_shift_playing_obj, (mp_obj_t)&audiodelays_pitch_shift_get_playing_obj); -//| def play(self, sample: circuitpython_typing.AudioSample, *, loop: bool = False) -> None: +//| def play(self, sample: circuitpython_typing.AudioSample, *, loop: bool = False) -> PitchShift: //| """Plays the sample once when loop=False and continuously when loop=True. //| Does not block. Use `playing` to block. //| -//| The sample must match the encoding settings given in the constructor.""" +//| The sample must match the encoding settings given in the constructor. +//| +//| :return: The effect object itself. Can be used for chaining, ie: +//| ``audio.play(effect.play(sample))``. +//| :rtype: PitchShift""" //| ... //| static mp_obj_t audiodelays_pitch_shift_obj_play(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { @@ -211,7 +215,7 @@ static mp_obj_t audiodelays_pitch_shift_obj_play(size_t n_args, const mp_obj_t * mp_obj_t sample = args[ARG_sample].u_obj; common_hal_audiodelays_pitch_shift_play(self, sample, args[ARG_loop].u_bool); - return mp_const_none; + return MP_OBJ_FROM_PTR(self); } MP_DEFINE_CONST_FUN_OBJ_KW(audiodelays_pitch_shift_play_obj, 1, audiodelays_pitch_shift_obj_play); diff --git a/shared-bindings/audiofilters/Distortion.c b/shared-bindings/audiofilters/Distortion.c index cb888d0c71b7b..8de4c1d1ea331 100644 --- a/shared-bindings/audiofilters/Distortion.c +++ b/shared-bindings/audiofilters/Distortion.c @@ -309,11 +309,15 @@ MP_DEFINE_CONST_FUN_OBJ_1(audiofilters_distortion_get_playing_obj, audiofilters_ MP_PROPERTY_GETTER(audiofilters_distortion_playing_obj, (mp_obj_t)&audiofilters_distortion_get_playing_obj); -//| def play(self, sample: circuitpython_typing.AudioSample, *, loop: bool = False) -> None: +//| def play(self, sample: circuitpython_typing.AudioSample, *, loop: bool = False) -> Distortion: //| """Plays the sample once when loop=False and continuously when loop=True. //| Does not block. Use `playing` to block. //| -//| The sample must match the encoding settings given in the constructor.""" +//| The sample must match the encoding settings given in the constructor. +//| +//| :return: The effect object itself. Can be used for chaining, ie: +//| ``audio.play(effect.play(sample))``. +//| :rtype: Distortion""" //| ... //| static mp_obj_t audiofilters_distortion_obj_play(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { @@ -331,7 +335,7 @@ static mp_obj_t audiofilters_distortion_obj_play(size_t n_args, const mp_obj_t * mp_obj_t sample = args[ARG_sample].u_obj; common_hal_audiofilters_distortion_play(self, sample, args[ARG_loop].u_bool); - return mp_const_none; + return MP_OBJ_FROM_PTR(self); } MP_DEFINE_CONST_FUN_OBJ_KW(audiofilters_distortion_play_obj, 1, audiofilters_distortion_obj_play); diff --git a/shared-bindings/audiofilters/Filter.c b/shared-bindings/audiofilters/Filter.c index 2a4887a4d42a2..426fff226168f 100644 --- a/shared-bindings/audiofilters/Filter.c +++ b/shared-bindings/audiofilters/Filter.c @@ -179,11 +179,15 @@ MP_DEFINE_CONST_FUN_OBJ_1(audiofilters_filter_get_playing_obj, audiofilters_filt MP_PROPERTY_GETTER(audiofilters_filter_playing_obj, (mp_obj_t)&audiofilters_filter_get_playing_obj); -//| def play(self, sample: circuitpython_typing.AudioSample, *, loop: bool = False) -> None: +//| def play(self, sample: circuitpython_typing.AudioSample, *, loop: bool = False) -> Filter: //| """Plays the sample once when loop=False and continuously when loop=True. //| Does not block. Use `playing` to block. //| -//| The sample must match the encoding settings given in the constructor.""" +//| The sample must match the encoding settings given in the constructor. +//| +//| :return: The effect object itself. Can be used for chaining, ie: +//| ``audio.play(effect.play(sample))``. +//| :rtype: Filter""" //| ... //| static mp_obj_t audiofilters_filter_obj_play(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { @@ -201,7 +205,7 @@ static mp_obj_t audiofilters_filter_obj_play(size_t n_args, const mp_obj_t *pos_ mp_obj_t sample = args[ARG_sample].u_obj; common_hal_audiofilters_filter_play(self, sample, args[ARG_loop].u_bool); - return mp_const_none; + return MP_OBJ_FROM_PTR(self); } MP_DEFINE_CONST_FUN_OBJ_KW(audiofilters_filter_play_obj, 1, audiofilters_filter_obj_play); diff --git a/shared-bindings/audiofilters/Phaser.c b/shared-bindings/audiofilters/Phaser.c index e7ddd986176b3..9f713afce5f72 100644 --- a/shared-bindings/audiofilters/Phaser.c +++ b/shared-bindings/audiofilters/Phaser.c @@ -214,11 +214,15 @@ MP_DEFINE_CONST_FUN_OBJ_1(audiofilters_phaser_get_playing_obj, audiofilters_phas MP_PROPERTY_GETTER(audiofilters_phaser_playing_obj, (mp_obj_t)&audiofilters_phaser_get_playing_obj); -//| def play(self, sample: circuitpython_typing.AudioSample, *, loop: bool = False) -> None: +//| def play(self, sample: circuitpython_typing.AudioSample, *, loop: bool = False) -> Phaser: //| """Plays the sample once when loop=False and continuously when loop=True. //| Does not block. Use `playing` to block. //| -//| The sample must match the encoding settings given in the constructor.""" +//| The sample must match the encoding settings given in the constructor. +//| +//| :return: The effect object itself. Can be used for chaining, ie: +//| ``audio.play(effect.play(sample))``. +//| :rtype: Phaser""" //| ... //| static mp_obj_t audiofilters_phaser_obj_play(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { @@ -236,7 +240,7 @@ static mp_obj_t audiofilters_phaser_obj_play(size_t n_args, const mp_obj_t *pos_ mp_obj_t sample = args[ARG_sample].u_obj; common_hal_audiofilters_phaser_play(self, sample, args[ARG_loop].u_bool); - return mp_const_none; + return MP_OBJ_FROM_PTR(self); } MP_DEFINE_CONST_FUN_OBJ_KW(audiofilters_phaser_play_obj, 1, audiofilters_phaser_obj_play); diff --git a/shared-bindings/audiofreeverb/Freeverb.c b/shared-bindings/audiofreeverb/Freeverb.c index 62c9237a0d271..12eb7ef70ab79 100644 --- a/shared-bindings/audiofreeverb/Freeverb.c +++ b/shared-bindings/audiofreeverb/Freeverb.c @@ -196,11 +196,15 @@ MP_DEFINE_CONST_FUN_OBJ_1(audiofreeverb_freeverb_get_playing_obj, audiofreeverb_ MP_PROPERTY_GETTER(audiofreeverb_freeverb_playing_obj, (mp_obj_t)&audiofreeverb_freeverb_get_playing_obj); -//| def play(self, sample: circuitpython_typing.AudioSample, *, loop: bool = False) -> None: +//| def play(self, sample: circuitpython_typing.AudioSample, *, loop: bool = False) -> Freeverb: //| """Plays the sample once when loop=False and continuously when loop=True. //| Does not block. Use `playing` to block. //| -//| The sample must match the encoding settings given in the constructor.""" +//| The sample must match the encoding settings given in the constructor. +//| +//| :return: The effect object itself. Can be used for chaining, ie: +//| ``audio.play(effect.play(sample))``. +//| :rtype: Freeverb""" //| ... //| static mp_obj_t audiofreeverb_freeverb_obj_play(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { @@ -218,7 +222,7 @@ static mp_obj_t audiofreeverb_freeverb_obj_play(size_t n_args, const mp_obj_t *p mp_obj_t sample = args[ARG_sample].u_obj; common_hal_audiofreeverb_freeverb_play(self, sample, args[ARG_loop].u_bool); - return mp_const_none; + return MP_OBJ_FROM_PTR(self); } MP_DEFINE_CONST_FUN_OBJ_KW(audiofreeverb_freeverb_play_obj, 1, audiofreeverb_freeverb_obj_play); diff --git a/shared-bindings/audiomixer/Mixer.c b/shared-bindings/audiomixer/Mixer.c index dda6d06bd500c..516d079cd2a09 100644 --- a/shared-bindings/audiomixer/Mixer.c +++ b/shared-bindings/audiomixer/Mixer.c @@ -160,13 +160,17 @@ MP_PROPERTY_GETTER(audiomixer_mixer_voice_obj, //| def play( //| self, sample: circuitpython_typing.AudioSample, *, voice: int = 0, loop: bool = False -//| ) -> None: +//| ) -> Mixer: //| """Plays the sample once when loop=False and continuously when loop=True. //| Does not block. Use `playing` to block. //| //| Sample must be an `audiocore.WaveFile`, `audiocore.RawSample`, `audiomixer.Mixer` or `audiomp3.MP3Decoder`. //| -//| The sample must match the Mixer's encoding settings given in the constructor.""" +//| The sample must match the Mixer's encoding settings given in the constructor. +//| +//| :return: The mixer object itself. Can be used for chaining, ie: +//| ``audio.play(mixer.play(sample))``. +//| :rtype: Chorus""" //| ... //| static mp_obj_t audiomixer_mixer_obj_play(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { @@ -189,7 +193,7 @@ static mp_obj_t audiomixer_mixer_obj_play(size_t n_args, const mp_obj_t *pos_arg mp_obj_t sample = args[ARG_sample].u_obj; common_hal_audiomixer_mixervoice_play(voice, sample, args[ARG_loop].u_bool); - return mp_const_none; + return MP_OBJ_FROM_PTR(self); } MP_DEFINE_CONST_FUN_OBJ_KW(audiomixer_mixer_play_obj, 1, audiomixer_mixer_obj_play); diff --git a/shared-bindings/keypad/EventQueue.h b/shared-bindings/keypad/EventQueue.h index 893165e3220f1..02c3414b0764c 100644 --- a/shared-bindings/keypad/EventQueue.h +++ b/shared-bindings/keypad/EventQueue.h @@ -11,7 +11,7 @@ extern const mp_obj_type_t keypad_eventqueue_type; -void common_hal_keypad_eventqueue_construct(keypad_eventqueue_obj_t *self, size_t max_events); +void common_hal_keypad_eventqueue_construct(keypad_eventqueue_obj_t *self, size_t max_events, bool use_gc_allocator); void common_hal_keypad_eventqueue_clear(keypad_eventqueue_obj_t *self); size_t common_hal_keypad_eventqueue_get_length(keypad_eventqueue_obj_t *self); diff --git a/shared-bindings/keypad_demux/DemuxKeyMatrix.c b/shared-bindings/keypad_demux/DemuxKeyMatrix.c index d76a20aec9d01..f1b5ecae9ceb6 100644 --- a/shared-bindings/keypad_demux/DemuxKeyMatrix.c +++ b/shared-bindings/keypad_demux/DemuxKeyMatrix.c @@ -123,7 +123,8 @@ static mp_obj_t keypad_demux_demuxkeymatrix_make_new(const mp_obj_type_t *type, column_pins_array[column] = pin; } - common_hal_keypad_demux_demuxkeymatrix_construct(self, num_row_addr_pins, row_addr_pins_array, num_column_pins, column_pins_array, args[ARG_columns_to_anodes].u_bool, args[ARG_transpose].u_bool, interval, max_events, debounce_threshold); + // Last arg is use_gc_allocator, true during VM use. + common_hal_keypad_demux_demuxkeymatrix_construct(self, num_row_addr_pins, row_addr_pins_array, num_column_pins, column_pins_array, args[ARG_columns_to_anodes].u_bool, args[ARG_transpose].u_bool, interval, max_events, debounce_threshold, true); return MP_OBJ_FROM_PTR(self); } diff --git a/shared-bindings/keypad_demux/DemuxKeyMatrix.h b/shared-bindings/keypad_demux/DemuxKeyMatrix.h index 8bdaa597dc035..25d36283e90fc 100644 --- a/shared-bindings/keypad_demux/DemuxKeyMatrix.h +++ b/shared-bindings/keypad_demux/DemuxKeyMatrix.h @@ -11,7 +11,7 @@ extern const mp_obj_type_t keypad_demux_demuxkeymatrix_type; -void common_hal_keypad_demux_demuxkeymatrix_construct(keypad_demux_demuxkeymatrix_obj_t *self, mp_uint_t num_row_addr_pins, const mcu_pin_obj_t *row_addr_pins[], mp_uint_t num_column_pins, const mcu_pin_obj_t *column_pins[], bool columns_to_anodes, bool transpose, mp_float_t interval, size_t max_events, uint8_t debounce_threshold); +void common_hal_keypad_demux_demuxkeymatrix_construct(keypad_demux_demuxkeymatrix_obj_t *self, mp_uint_t num_row_addr_pins, const mcu_pin_obj_t *row_addr_pins[], mp_uint_t num_column_pins, const mcu_pin_obj_t *column_pins[], bool columns_to_anodes, bool transpose, mp_float_t interval, size_t max_events, uint8_t debounce_threshold, bool use_gc_allocator); void common_hal_keypad_demux_demuxkeymatrix_deinit(keypad_demux_demuxkeymatrix_obj_t *self); diff --git a/shared-bindings/rotaryio/IncrementalEncoder.h b/shared-bindings/rotaryio/IncrementalEncoder.h index 1c60dd69a1937..b3ceb3eeb1513 100644 --- a/shared-bindings/rotaryio/IncrementalEncoder.h +++ b/shared-bindings/rotaryio/IncrementalEncoder.h @@ -13,11 +13,15 @@ extern const mp_obj_type_t rotaryio_incrementalencoder_type; extern 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); + extern void common_hal_rotaryio_incrementalencoder_deinit(rotaryio_incrementalencoder_obj_t *self); extern bool common_hal_rotaryio_incrementalencoder_deinited(rotaryio_incrementalencoder_obj_t *self); +extern void common_hal_rotaryio_incrementalencoder_mark_deinit(rotaryio_incrementalencoder_obj_t *self); + extern mp_int_t common_hal_rotaryio_incrementalencoder_get_position(rotaryio_incrementalencoder_obj_t *self); extern void common_hal_rotaryio_incrementalencoder_set_position(rotaryio_incrementalencoder_obj_t *self, mp_int_t new_position); + extern mp_int_t common_hal_rotaryio_incrementalencoder_get_divisor(rotaryio_incrementalencoder_obj_t *self); extern void common_hal_rotaryio_incrementalencoder_set_divisor(rotaryio_incrementalencoder_obj_t *self, mp_int_t new_divisor); diff --git a/shared-bindings/time/__init__.c b/shared-bindings/time/__init__.c index 504cd07d90111..8a591da368858 100644 --- a/shared-bindings/time/__init__.c +++ b/shared-bindings/time/__init__.c @@ -83,6 +83,16 @@ static mp_obj_t struct_time_make_new(const mp_obj_type_t *type, size_t n_args, s } //| class struct_time: +//| tm_year: int +//| tm_mon: int +//| tm_mday: int +//| tm_hour: int +//| tm_min: int +//| tm_sec: int +//| tm_wday: int +//| tm_yday: int +//| tm_isdst: int +//| //| def __init__(self, time_tuple: Sequence[int]) -> None: //| """Structure used to capture a date and time. Can be constructed from a `struct_time`, `tuple`, `list`, or `namedtuple` with 9 elements. //| @@ -210,7 +220,7 @@ static mp_obj_t time_monotonic_ns(void) { } MP_DEFINE_CONST_FUN_OBJ_0(time_monotonic_ns_obj, time_monotonic_ns); -//| def localtime(secs: int) -> struct_time: +//| def localtime(secs: Optional[int] = None) -> struct_time: //| """Convert a time expressed in seconds since Jan 1, 1970 to a struct_time in //| local time. If secs is not provided or None, the current time as returned //| by time() is used. diff --git a/shared-bindings/wifi/Radio.c b/shared-bindings/wifi/Radio.c index a267dea11c1ff..9a7d445fd0473 100644 --- a/shared-bindings/wifi/Radio.c +++ b/shared-bindings/wifi/Radio.c @@ -99,7 +99,7 @@ MP_PROPERTY_GETSET(wifi_radio_enabled_obj, (mp_obj_t)&wifi_radio_get_enabled_obj, (mp_obj_t)&wifi_radio_set_enabled_obj); -//| hostname: Union[str | ReadableBuffer] +//| hostname: Union[str, ReadableBuffer] //| """Hostname for wifi interface. When the hostname is altered after interface started/connected //| the changes would only be reflected once the interface restarts/reconnects.""" static mp_obj_t wifi_radio_get_hostname(mp_obj_t self_in) { @@ -325,8 +325,8 @@ MP_DEFINE_CONST_FUN_OBJ_1(wifi_radio_stop_station_obj, wifi_radio_stop_station); //| def start_ap( //| self, -//| ssid: Union[str | ReadableBuffer], -//| password: Union[str | ReadableBuffer] = b"", +//| ssid: Union[str, ReadableBuffer], +//| password: Union[str, ReadableBuffer] = b"", //| *, //| channel: int = 1, //| authmode: Iterable[AuthMode] = (), @@ -438,11 +438,11 @@ MP_PROPERTY_GETTER(wifi_radio_ap_active_obj, //| def connect( //| self, -//| ssid: Union[str | ReadableBuffer], -//| password: Union[str | ReadableBuffer] = b"", +//| ssid: Union[str, ReadableBuffer], +//| password: Union[str, ReadableBuffer] = b"", //| *, //| channel: int = 0, -//| bssid: Optional[Union[str | ReadableBuffer]] = None, +//| bssid: Optional[Union[str, ReadableBuffer]] = None, //| timeout: Optional[float] = None, //| ) -> None: //| """Connects to the given ssid and waits for an ip address. Reconnections are handled diff --git a/shared-module/keypad/EventQueue.c b/shared-module/keypad/EventQueue.c index e5b362a045ddf..3130d8b59a1d7 100644 --- a/shared-module/keypad/EventQueue.c +++ b/shared-module/keypad/EventQueue.c @@ -15,9 +15,14 @@ #define EVENT_SIZE_BYTES (sizeof(uint16_t) + sizeof(mp_obj_t)) -void common_hal_keypad_eventqueue_construct(keypad_eventqueue_obj_t *self, size_t max_events) { +void common_hal_keypad_eventqueue_construct(keypad_eventqueue_obj_t *self, size_t max_events, bool use_gc_allocator) { // Event queue is 16-bit values. - ringbuf_alloc(&self->encoded_events, max_events * EVENT_SIZE_BYTES); + const size_t size = max_events * EVENT_SIZE_BYTES; + if (use_gc_allocator) { + ringbuf_alloc(&self->encoded_events, size); + } else { + ringbuf_init(&self->encoded_events, port_malloc(size, false), size); + } self->overflowed = false; self->event_handler = NULL; } diff --git a/shared-module/keypad/KeyMatrix.c b/shared-module/keypad/KeyMatrix.c index 67a5546853552..28830f591b129 100644 --- a/shared-module/keypad/KeyMatrix.c +++ b/shared-module/keypad/KeyMatrix.c @@ -56,7 +56,7 @@ void common_hal_keypad_keymatrix_construct(keypad_keymatrix_obj_t *self, mp_uint self->columns_to_anodes = columns_to_anodes; self->funcs = &keymatrix_funcs; - keypad_construct_common((keypad_scanner_obj_t *)self, interval, max_events, debounce_threshold); + keypad_construct_common((keypad_scanner_obj_t *)self, interval, max_events, debounce_threshold, true); } void common_hal_keypad_keymatrix_deinit(keypad_keymatrix_obj_t *self) { diff --git a/shared-module/keypad/Keys.c b/shared-module/keypad/Keys.c index 39d50f95e3238..80dcf36e163ad 100644 --- a/shared-module/keypad/Keys.c +++ b/shared-module/keypad/Keys.c @@ -41,7 +41,7 @@ void common_hal_keypad_keys_construct(keypad_keys_obj_t *self, mp_uint_t num_pin self->value_when_pressed = value_when_pressed; self->funcs = &keys_funcs; - keypad_construct_common((keypad_scanner_obj_t *)self, interval, max_events, debounce_threshold); + keypad_construct_common((keypad_scanner_obj_t *)self, interval, max_events, debounce_threshold, true); } diff --git a/shared-module/keypad/ShiftRegisterKeys.c b/shared-module/keypad/ShiftRegisterKeys.c index 97917434ff81b..aa0c14dd36b50 100644 --- a/shared-module/keypad/ShiftRegisterKeys.c +++ b/shared-module/keypad/ShiftRegisterKeys.c @@ -71,7 +71,7 @@ void common_hal_keypad_shiftregisterkeys_construct(keypad_shiftregisterkeys_obj_ self->value_when_pressed = value_when_pressed; self->funcs = &shiftregisterkeys_funcs; - keypad_construct_common((keypad_scanner_obj_t *)self, interval, max_events, debounce_threshold); + keypad_construct_common((keypad_scanner_obj_t *)self, interval, max_events, debounce_threshold, true); } void common_hal_keypad_shiftregisterkeys_deinit(keypad_shiftregisterkeys_obj_t *self) { diff --git a/shared-module/keypad/__init__.c b/shared-module/keypad/__init__.c index 3ffa0433174cf..f1d5d395df789 100644 --- a/shared-module/keypad/__init__.c +++ b/shared-module/keypad/__init__.c @@ -85,14 +85,20 @@ void keypad_deregister_scanner(keypad_scanner_obj_t *scanner) { supervisor_release_lock(&keypad_scanners_linked_list_lock); } -void keypad_construct_common(keypad_scanner_obj_t *self, mp_float_t interval, size_t max_events, uint8_t debounce_threshold) { +void keypad_construct_common(keypad_scanner_obj_t *self, mp_float_t interval, size_t max_events, uint8_t debounce_threshold, bool use_gc_allocator) { size_t key_count = common_hal_keypad_generic_get_key_count(self); - self->debounce_counter = (int8_t *)m_malloc_without_collect(sizeof(int8_t) * key_count); + self->debounce_counter = + use_gc_allocator + ? (int8_t *)m_malloc_without_collect(sizeof(int8_t) * key_count) + : (int8_t *)port_malloc_zero(sizeof(int8_t) * key_count, false); self->interval_ticks = (mp_uint_t)(interval * 1024); // interval * 1000 * (1024/1000) - keypad_eventqueue_obj_t *events = mp_obj_malloc(keypad_eventqueue_obj_t, &keypad_eventqueue_type); - common_hal_keypad_eventqueue_construct(events, max_events); + keypad_eventqueue_obj_t *events = + use_gc_allocator + ? mp_obj_malloc(keypad_eventqueue_obj_t, &keypad_eventqueue_type) + : mp_obj_port_malloc(keypad_eventqueue_obj_t, &keypad_eventqueue_type); + common_hal_keypad_eventqueue_construct(events, max_events, use_gc_allocator); self->events = events; self->debounce_threshold = debounce_threshold; diff --git a/shared-module/keypad/__init__.h b/shared-module/keypad/__init__.h index a7d21753f6625..66fea4878543b 100644 --- a/shared-module/keypad/__init__.h +++ b/shared-module/keypad/__init__.h @@ -39,7 +39,7 @@ void keypad_reset(void); void keypad_register_scanner(keypad_scanner_obj_t *scanner); void keypad_deregister_scanner(keypad_scanner_obj_t *scanner); -void keypad_construct_common(keypad_scanner_obj_t *scanner, mp_float_t interval, size_t max_events, uint8_t debounce_cycles); +void keypad_construct_common(keypad_scanner_obj_t *scanner, mp_float_t interval, size_t max_events, uint8_t debounce_cycles, bool use_gc_allocator); bool keypad_debounce(keypad_scanner_obj_t *self, mp_uint_t key_number, bool current); void keypad_never_reset(keypad_scanner_obj_t *self); diff --git a/shared-module/keypad_demux/DemuxKeyMatrix.c b/shared-module/keypad_demux/DemuxKeyMatrix.c index b90669d772fcd..329f7679d3f76 100644 --- a/shared-module/keypad_demux/DemuxKeyMatrix.c +++ b/shared-module/keypad_demux/DemuxKeyMatrix.c @@ -31,38 +31,45 @@ static mp_uint_t row_column_to_key_number(keypad_demux_demuxkeymatrix_obj_t *sel return row * common_hal_keypad_demux_demuxkeymatrix_get_column_count(self) + column; } -void common_hal_keypad_demux_demuxkeymatrix_construct(keypad_demux_demuxkeymatrix_obj_t *self, mp_uint_t num_row_addr_pins, const mcu_pin_obj_t *row_addr_pins[], mp_uint_t num_column_pins, const mcu_pin_obj_t *column_pins[], bool columns_to_anodes, bool transpose, mp_float_t interval, size_t max_events, uint8_t debounce_threshold) { - +void common_hal_keypad_demux_demuxkeymatrix_construct(keypad_demux_demuxkeymatrix_obj_t *self, mp_uint_t num_row_addr_pins, const mcu_pin_obj_t *row_addr_pins[], mp_uint_t num_column_pins, const mcu_pin_obj_t *column_pins[], bool columns_to_anodes, bool transpose, mp_float_t interval, size_t max_events, uint8_t debounce_threshold, bool use_gc_allocator) { // the multiplexed pins are outputs so we can set the address for the target row // the sense of the address pins themselves doesn't change with columns_to_anodes // but the value output on the selected row line will be !columns_to_anodes mp_obj_t row_addr_dios[num_row_addr_pins]; for (size_t row = 0; row < num_row_addr_pins; row++) { digitalio_digitalinout_obj_t *dio = - mp_obj_malloc(digitalio_digitalinout_obj_t, &digitalio_digitalinout_type); + use_gc_allocator + ? mp_obj_malloc(digitalio_digitalinout_obj_t, &digitalio_digitalinout_type) + : mp_obj_port_malloc(digitalio_digitalinout_obj_t, &digitalio_digitalinout_type); common_hal_digitalio_digitalinout_construct(dio, row_addr_pins[row]); common_hal_digitalio_digitalinout_switch_to_output(dio, false, DRIVE_MODE_PUSH_PULL); row_addr_dios[row] = dio; } - self->row_addr_digitalinouts = mp_obj_new_tuple(num_row_addr_pins, row_addr_dios); + self->row_addr_digitalinouts = + use_gc_allocator + ? mp_obj_new_tuple(num_row_addr_pins, row_addr_dios) + : mp_obj_new_port_tuple(num_row_addr_pins, row_addr_dios); // the column pins are always inputs, with default state based on columns_to_anodes mp_obj_t column_dios[num_column_pins]; for (size_t column = 0; column < num_column_pins; column++) { digitalio_digitalinout_obj_t *dio = - mp_obj_malloc(digitalio_digitalinout_obj_t, &digitalio_digitalinout_type); - dio->base.type = &digitalio_digitalinout_type; + use_gc_allocator + ? mp_obj_malloc(digitalio_digitalinout_obj_t, &digitalio_digitalinout_type) + : mp_obj_port_malloc(digitalio_digitalinout_obj_t, &digitalio_digitalinout_type); common_hal_digitalio_digitalinout_construct(dio, column_pins[column]); common_hal_digitalio_digitalinout_switch_to_input(dio, columns_to_anodes ? PULL_UP : PULL_DOWN); column_dios[column] = dio; } - self->column_digitalinouts = mp_obj_new_tuple(num_column_pins, column_dios); - + self->column_digitalinouts = + use_gc_allocator + ? mp_obj_new_tuple(num_column_pins, column_dios) + : mp_obj_new_port_tuple(num_column_pins, column_dios); self->columns_to_anodes = columns_to_anodes; self->transpose = transpose; self->funcs = &keymatrix_funcs; - keypad_construct_common((keypad_scanner_obj_t *)self, interval, max_events, debounce_threshold); + keypad_construct_common((keypad_scanner_obj_t *)self, interval, max_events, debounce_threshold, use_gc_allocator); } void common_hal_keypad_demux_demuxkeymatrix_deinit(keypad_demux_demuxkeymatrix_obj_t *self) { diff --git a/shared-module/lvfontio/OnDiskFont.c b/shared-module/lvfontio/OnDiskFont.c index 0cf65301d2270..368bd381475b1 100644 --- a/shared-module/lvfontio/OnDiskFont.c +++ b/shared-module/lvfontio/OnDiskFont.c @@ -253,9 +253,10 @@ static bool load_font_header(lvfontio_ondiskfont_t *self, FIL *file, size_t *max *max_slots = advance_count[1] * 2 + advance_count[0]; } } + } else { + *max_slots = advance_count[0] + advance_count[1]; } - found_glyf = true; } diff --git a/shared-module/usb/core/Device.c b/shared-module/usb/core/Device.c index c9df0cf73805f..83def37de914e 100644 --- a/shared-module/usb/core/Device.c +++ b/shared-module/usb/core/Device.c @@ -1,12 +1,15 @@ // This file is part of the CircuitPython project: https://circuitpython.org // // SPDX-FileCopyrightText: Copyright (c) 2022 Scott Shawcroft for Adafruit Industries +// SPDX-FileCopyrightText: Copyright (c) 2025 Sam Blenny // // SPDX-License-Identifier: MIT #include "shared-bindings/usb/core/Device.h" #include "tusb_config.h" +#include "supervisor/port.h" +#include "supervisor/port_heap.h" #include "lib/tinyusb/src/host/hcd.h" #include "lib/tinyusb/src/host/usbh.h" @@ -32,6 +35,28 @@ void tuh_umount_cb(uint8_t dev_addr) { static xfer_result_t _xfer_result; static size_t _actual_len; + +#if !CIRCUITPY_ALL_MEMORY_DMA_CAPABLE +// Helper to ensure buffer is DMA-capable for transfer operations +static uint8_t *_ensure_dma_buffer(usb_core_device_obj_t *self, const uint8_t *buffer, size_t len, bool for_write) { + if (port_buffer_is_dma_capable(buffer)) { + return (uint8_t *)buffer; // Already DMA-capable, use directly + } + + uint8_t *dma_buffer = port_malloc(len, true); // true = DMA capable + if (dma_buffer == NULL) { + return NULL; // Allocation failed + } + + // Copy data to DMA buffer if writing + if (for_write && buffer != NULL) { + memcpy(dma_buffer, buffer, len); + } + + return dma_buffer; +} + +#endif bool common_hal_usb_core_device_construct(usb_core_device_obj_t *self, uint8_t device_address) { if (!tuh_inited()) { mp_raise_RuntimeError(MP_ERROR_TEXT("No usb host port initialized")); @@ -45,7 +70,7 @@ bool common_hal_usb_core_device_construct(usb_core_device_obj_t *self, uint8_t d } self->device_address = device_address; self->first_langid = 0; - _xfer_result = 0xff; + _xfer_result = XFER_RESULT_INVALID; return true; } @@ -70,14 +95,18 @@ void common_hal_usb_core_device_deinit(usb_core_device_obj_t *self) { uint16_t common_hal_usb_core_device_get_idVendor(usb_core_device_obj_t *self) { uint16_t vid; uint16_t pid; - tuh_vid_pid_get(self->device_address, &vid, &pid); + if (!tuh_vid_pid_get(self->device_address, &vid, &pid)) { + mp_raise_usb_core_USBError(NULL); + } return vid; } uint16_t common_hal_usb_core_device_get_idProduct(usb_core_device_obj_t *self) { uint16_t vid; uint16_t pid; - tuh_vid_pid_get(self->device_address, &vid, &pid); + if (!tuh_vid_pid_get(self->device_address, &vid, &pid)) { + mp_raise_usb_core_USBError(NULL); + } return pid; } @@ -91,14 +120,109 @@ static void _transfer_done_cb(tuh_xfer_t *xfer) { static bool _wait_for_callback(void) { while (!mp_hal_is_interrupted() && - _xfer_result == 0xff) { + _xfer_result == XFER_RESULT_INVALID) { + // The background tasks include TinyUSB which will call the function + // we provided above. In other words, the callback isn't in an interrupt. + RUN_BACKGROUND_TASKS; + } + if (mp_hal_is_interrupted()) { + // Handle case of VM being interrupted by Ctrl-C or autoreload + return false; + } + // Handle callback result code from TinyUSB + xfer_result_t result = _xfer_result; + _xfer_result = XFER_RESULT_INVALID; + switch (result) { + case XFER_RESULT_SUCCESS: + return true; + case XFER_RESULT_FAILED: + mp_raise_usb_core_USBError(NULL); + break; + case XFER_RESULT_STALLED: + mp_raise_usb_core_USBError(MP_ERROR_TEXT("Pipe error")); + break; + case XFER_RESULT_TIMEOUT: + case XFER_RESULT_INVALID: + mp_raise_usb_core_USBTimeoutError(); + break; + } + return false; +} + +static void _prepare_for_transfer(void) { + // Prepare for transfer. Unless there is a timeout, these static globals will + // get modified by the _transfer_done_cb() callback when tinyusb finishes the + // transfer or encounters an error condition. + _xfer_result = XFER_RESULT_INVALID; + _actual_len = 0; +} + +static void _abort_transfer(tuh_xfer_t *xfer) { + bool aborted = tuh_edpt_abort_xfer(xfer->daddr, xfer->ep_addr); + if (aborted) { + // If the transfer was aborted, then we can continue. + return; + } + uint32_t start_time = supervisor_ticks_ms32(); + // If not, we need to wait for it to finish, otherwise we may free memory out from under it. + // Limit the wait time to 10 milliseconds to avoid blocking indefinitely. + while (_xfer_result == XFER_RESULT_INVALID && (supervisor_ticks_ms32() - start_time < 10)) { + // The background tasks include TinyUSB which will call the function + // we provided above. In other words, the callback isn't in an interrupt. + RUN_BACKGROUND_TASKS; + } +} + +// Only frees the transfer buffer on error. +static size_t _handle_timed_transfer_callback(tuh_xfer_t *xfer, mp_int_t timeout, bool our_buffer) { + if (xfer == NULL) { + mp_raise_usb_core_USBError(NULL); + return 0; + } + uint32_t start_time = supervisor_ticks_ms32(); + while ((timeout == 0 || supervisor_ticks_ms32() - start_time < (uint32_t)timeout) && + !mp_hal_is_interrupted() && + _xfer_result == XFER_RESULT_INVALID) { // The background tasks include TinyUSB which will call the function // we provided above. In other words, the callback isn't in an interrupt. RUN_BACKGROUND_TASKS; } + if (mp_hal_is_interrupted()) { + // Handle case of VM being interrupted by Ctrl-C or autoreload + _abort_transfer(xfer); + return 0; + } + // Handle transfer result code from TinyUSB xfer_result_t result = _xfer_result; - _xfer_result = 0xff; - return result == XFER_RESULT_SUCCESS; + _xfer_result = XFER_RESULT_INVALID; + if (our_buffer && result != XFER_RESULT_SUCCESS && result != XFER_RESULT_INVALID) { + port_free(xfer->buffer); + } + switch (result) { + case XFER_RESULT_SUCCESS: + return _actual_len; + case XFER_RESULT_FAILED: + mp_raise_usb_core_USBError(NULL); + break; + case XFER_RESULT_STALLED: + mp_raise_usb_core_USBError(MP_ERROR_TEXT("Pipe error")); + break; + case XFER_RESULT_TIMEOUT: + // This timeout comes from TinyUSB, so assume that it has stopped the + // transfer (note: timeout logic may be unimplemented on TinyUSB side) + mp_raise_usb_core_USBTimeoutError(); + break; + case XFER_RESULT_INVALID: + // This timeout comes from CircuitPython, not TinyUSB, so tell TinyUSB + // to stop the transfer and then wait to free the buffer. + _abort_transfer(xfer); + if (our_buffer) { + port_free(xfer->buffer); + } + mp_raise_usb_core_USBTimeoutError(); + break; + } + return 0; } static mp_obj_t _get_string(const uint16_t *temp_buf) { @@ -115,41 +239,75 @@ static void _get_langid(usb_core_device_obj_t *self) { } // Two control bytes and one uint16_t language code. uint16_t temp_buf[2]; - if (!tuh_descriptor_get_string(self->device_address, 0, 0, temp_buf, sizeof(temp_buf), _transfer_done_cb, 0) || - !_wait_for_callback()) { - return; + _prepare_for_transfer(); + if (!tuh_descriptor_get_string(self->device_address, 0, 0, temp_buf, sizeof(temp_buf), _transfer_done_cb, 0)) { + mp_raise_usb_core_USBError(NULL); + } else if (_wait_for_callback()) { + self->first_langid = temp_buf[1]; } - self->first_langid = temp_buf[1]; } mp_obj_t common_hal_usb_core_device_get_serial_number(usb_core_device_obj_t *self) { uint16_t temp_buf[127]; - _get_langid(self); - if (!tuh_descriptor_get_serial_string(self->device_address, self->first_langid, temp_buf, sizeof(temp_buf), _transfer_done_cb, 0) || - !_wait_for_callback()) { + tusb_desc_device_t descriptor; + // First, be sure not to ask TinyUSB for a non-existent string (avoid error) + if (!tuh_descriptor_get_device_local(self->device_address, &descriptor)) { return mp_const_none; } - return _get_string(temp_buf); + if (descriptor.iSerialNumber == 0) { + return mp_const_none; + } + // Device does provide this string, so continue + _get_langid(self); + _prepare_for_transfer(); + if (!tuh_descriptor_get_serial_string(self->device_address, self->first_langid, temp_buf, sizeof(temp_buf), _transfer_done_cb, 0)) { + mp_raise_usb_core_USBError(NULL); + } else if (_wait_for_callback()) { + return _get_string(temp_buf); + } + return mp_const_none; } mp_obj_t common_hal_usb_core_device_get_product(usb_core_device_obj_t *self) { uint16_t temp_buf[127]; - _get_langid(self); - if (!tuh_descriptor_get_product_string(self->device_address, self->first_langid, temp_buf, sizeof(temp_buf), _transfer_done_cb, 0) || - !_wait_for_callback()) { + tusb_desc_device_t descriptor; + // First, be sure not to ask TinyUSB for a non-existent string (avoid error) + if (!tuh_descriptor_get_device_local(self->device_address, &descriptor)) { + return mp_const_none; + } + if (descriptor.iProduct == 0) { return mp_const_none; } - return _get_string(temp_buf); + // Device does provide this string, so continue + _get_langid(self); + _prepare_for_transfer(); + if (!tuh_descriptor_get_product_string(self->device_address, self->first_langid, temp_buf, sizeof(temp_buf), _transfer_done_cb, 0)) { + mp_raise_usb_core_USBError(NULL); + } else if (_wait_for_callback()) { + return _get_string(temp_buf); + } + return mp_const_none; } mp_obj_t common_hal_usb_core_device_get_manufacturer(usb_core_device_obj_t *self) { uint16_t temp_buf[127]; - _get_langid(self); - if (!tuh_descriptor_get_manufacturer_string(self->device_address, self->first_langid, temp_buf, sizeof(temp_buf), _transfer_done_cb, 0) || - !_wait_for_callback()) { + tusb_desc_device_t descriptor; + // First, be sure not to ask TinyUSB for a non-existent string (avoid error) + if (!tuh_descriptor_get_device_local(self->device_address, &descriptor)) { + return mp_const_none; + } + if (descriptor.iManufacturer == 0) { return mp_const_none; } - return _get_string(temp_buf); + // Device does provide this string, so continue + _get_langid(self); + _prepare_for_transfer(); + if (!tuh_descriptor_get_manufacturer_string(self->device_address, self->first_langid, temp_buf, sizeof(temp_buf), _transfer_done_cb, 0)) { + mp_raise_usb_core_USBError(NULL); + } else if (_wait_for_callback()) { + return _get_string(temp_buf); + } + return mp_const_none; } @@ -224,39 +382,18 @@ void common_hal_usb_core_device_set_configuration(usb_core_device_obj_t *self, m _wait_for_callback(); } -static size_t _xfer(tuh_xfer_t *xfer, mp_int_t timeout) { - _xfer_result = 0xff; +// Raises an exception on failure. Returns the number of bytes transferred (maybe zero) on success. +static size_t _xfer(tuh_xfer_t *xfer, mp_int_t timeout, bool our_buffer) { + _prepare_for_transfer(); xfer->complete_cb = _transfer_done_cb; if (!tuh_edpt_xfer(xfer)) { + if (our_buffer) { + port_free(xfer->buffer); + } mp_raise_usb_core_USBError(NULL); return 0; } - uint32_t start_time = supervisor_ticks_ms32(); - while ((timeout == 0 || supervisor_ticks_ms32() - start_time < (uint32_t)timeout) && - !mp_hal_is_interrupted() && - _xfer_result == 0xff) { - // The background tasks include TinyUSB which will call the function - // we provided above. In other words, the callback isn't in an interrupt. - RUN_BACKGROUND_TASKS; - } - if (mp_hal_is_interrupted()) { - tuh_edpt_abort_xfer(xfer->daddr, xfer->ep_addr); - return 0; - } - xfer_result_t result = _xfer_result; - _xfer_result = 0xff; - if (result == XFER_RESULT_STALLED) { - mp_raise_usb_core_USBError(MP_ERROR_TEXT("Pipe error")); - } - if (result == 0xff) { - tuh_edpt_abort_xfer(xfer->daddr, xfer->ep_addr); - mp_raise_usb_core_USBTimeoutError(); - } - if (result == XFER_RESULT_SUCCESS) { - return _actual_len; - } - - return 0; + return _handle_timed_transfer_callback(xfer, timeout, our_buffer); } static bool _open_endpoint(usb_core_device_obj_t *self, mp_int_t endpoint) { @@ -313,12 +450,30 @@ mp_int_t common_hal_usb_core_device_write(usb_core_device_obj_t *self, mp_int_t mp_raise_usb_core_USBError(NULL); return 0; } + + #if !CIRCUITPY_ALL_MEMORY_DMA_CAPABLE + // Ensure buffer is in DMA-capable memory + uint8_t *dma_buffer = _ensure_dma_buffer(self, buffer, len, true); // true = for write + if (dma_buffer == NULL) { + mp_raise_msg(&mp_type_MemoryError, MP_ERROR_TEXT("Could not allocate DMA capable buffer")); + return 0; + } + #else + uint8_t *dma_buffer = (uint8_t *)buffer; // All memory is DMA-capable + #endif + tuh_xfer_t xfer; xfer.daddr = self->device_address; xfer.ep_addr = endpoint; - xfer.buffer = (uint8_t *)buffer; + xfer.buffer = dma_buffer; xfer.buflen = len; - return _xfer(&xfer, timeout); + size_t result = _xfer(&xfer, timeout, dma_buffer != buffer); + #if !CIRCUITPY_ALL_MEMORY_DMA_CAPABLE + if (dma_buffer != buffer) { + port_free(dma_buffer); + } + #endif + return result; } mp_int_t common_hal_usb_core_device_read(usb_core_device_obj_t *self, mp_int_t endpoint, uint8_t *buffer, mp_int_t len, mp_int_t timeout) { @@ -326,12 +481,34 @@ mp_int_t common_hal_usb_core_device_read(usb_core_device_obj_t *self, mp_int_t e mp_raise_usb_core_USBError(NULL); return 0; } + + #if !CIRCUITPY_ALL_MEMORY_DMA_CAPABLE + // Ensure buffer is in DMA-capable memory + uint8_t *dma_buffer = _ensure_dma_buffer(self, buffer, len, false); // false = for read + if (dma_buffer == NULL) { + mp_raise_msg(&mp_type_MemoryError, MP_ERROR_TEXT("Could not allocate DMA capable buffer")); + return 0; + } + #else + uint8_t *dma_buffer = buffer; // All memory is DMA-capable + #endif + tuh_xfer_t xfer; xfer.daddr = self->device_address; xfer.ep_addr = endpoint; - xfer.buffer = buffer; + xfer.buffer = dma_buffer; xfer.buflen = len; - return _xfer(&xfer, timeout); + mp_int_t result = _xfer(&xfer, timeout, dma_buffer != buffer); + + #if !CIRCUITPY_ALL_MEMORY_DMA_CAPABLE + // Copy data back to original buffer if needed + if (dma_buffer != buffer) { + memcpy(buffer, dma_buffer, result); + port_free(dma_buffer); + } + #endif + + return result; } mp_int_t common_hal_usb_core_device_ctrl_transfer(usb_core_device_obj_t *self, @@ -340,6 +517,23 @@ mp_int_t common_hal_usb_core_device_ctrl_transfer(usb_core_device_obj_t *self, uint8_t *buffer, mp_int_t len, mp_int_t timeout) { // Timeout is in ms. + #if !CIRCUITPY_ALL_MEMORY_DMA_CAPABLE + // Determine if this is a write (host-to-device) or read (device-to-host) transfer + bool is_write = (bmRequestType & 0x80) == 0; // Bit 7: 0=host-to-device, 1=device-to-host + + // Ensure buffer is in DMA-capable memory + uint8_t *dma_buffer = NULL; + if (len > 0 && buffer != NULL) { + dma_buffer = _ensure_dma_buffer(self, buffer, len, is_write); + if (dma_buffer == NULL) { + mp_raise_msg(&mp_type_MemoryError, MP_ERROR_TEXT("Could not allocate DMA capable buffer")); + return 0; + } + } + #else + uint8_t *dma_buffer = buffer; // All memory is DMA-capable + #endif + tusb_control_request_t request = { .bmRequestType = bmRequestType, .bRequest = bRequest, @@ -351,42 +545,28 @@ mp_int_t common_hal_usb_core_device_ctrl_transfer(usb_core_device_obj_t *self, .daddr = self->device_address, .ep_addr = 0, .setup = &request, - .buffer = buffer, + .buffer = dma_buffer, .complete_cb = _transfer_done_cb, }; - _xfer_result = 0xff; - + _prepare_for_transfer(); if (!tuh_control_xfer(&xfer)) { mp_raise_usb_core_USBError(NULL); return 0; } - uint32_t start_time = supervisor_ticks_ms32(); - while ((timeout == 0 || supervisor_ticks_ms32() - start_time < (uint32_t)timeout) && - !mp_hal_is_interrupted() && - _xfer_result == 0xff) { - // The background tasks include TinyUSB which will call the function - // we provided above. In other words, the callback isn't in an interrupt. - RUN_BACKGROUND_TASKS; - } - if (mp_hal_is_interrupted()) { - tuh_edpt_abort_xfer(xfer.daddr, xfer.ep_addr); - return 0; - } - xfer_result_t result = _xfer_result; - _xfer_result = 0xff; - if (result == XFER_RESULT_STALLED) { - mp_raise_usb_core_USBError(MP_ERROR_TEXT("Pipe error")); - } - if (result == 0xff) { - tuh_edpt_abort_xfer(xfer.daddr, xfer.ep_addr); - mp_raise_usb_core_USBTimeoutError(); - } - if (result == XFER_RESULT_SUCCESS) { - return len; + mp_int_t result = (mp_int_t)_handle_timed_transfer_callback(&xfer, timeout, dma_buffer != buffer); + + #if !CIRCUITPY_ALL_MEMORY_DMA_CAPABLE + if (dma_buffer != buffer) { + // Copy data back to original buffer if this was a read transfer + if (buffer != NULL && !is_write) { + memcpy(buffer, dma_buffer, result); + } + port_free(dma_buffer); } + #endif - return 0; + return result; } bool common_hal_usb_core_device_is_kernel_driver_active(usb_core_device_obj_t *self, mp_int_t interface) { diff --git a/supervisor/port.h b/supervisor/port.h index cc49538218fac..0bf8fd8d01350 100644 --- a/supervisor/port.h +++ b/supervisor/port.h @@ -9,6 +9,7 @@ #include #include +#include "py/obj.h" #include "supervisor/shared/safe_mode.h" // Provided by the linker; @@ -107,3 +108,10 @@ void port_gc_collect(void); // this function to sense the button. Ports that need to can override this // function to provide their own implementation. bool port_boot_button_pressed(void); + +// Allocating objects on the port heap, not the VM heap. +#define mp_obj_port_malloc(struct_type, obj_type) ((struct_type *)mp_obj_port_malloc_helper(sizeof(struct_type), obj_type)) +#define mp_obj_port_malloc_var(struct_type, var_field, var_type, var_num, obj_type) ((struct_type *)mp_obj_port_malloc_helper(offsetof(struct_type, var_field) + sizeof(var_type) * (var_num), obj_type)) + +void *mp_obj_port_malloc_helper(size_t num_bytes, const mp_obj_type_t *type); +mp_obj_t mp_obj_new_port_tuple(size_t n, const mp_obj_t *items); diff --git a/supervisor/port_heap.h b/supervisor/port_heap.h index 07a3c884e241e..3e6b5a660fa79 100644 --- a/supervisor/port_heap.h +++ b/supervisor/port_heap.h @@ -21,9 +21,16 @@ void port_heap_init(void); void *port_malloc(size_t size, bool dma_capable); +void *port_malloc_zero(size_t size, bool dma_capable); void port_free(void *ptr); void *port_realloc(void *ptr, size_t size, bool dma_capable); +#if !CIRCUITPY_ALL_MEMORY_DMA_CAPABLE +// Check if a buffer pointer is in DMA-capable memory. DMA-capable memory is also accessible during +// flash operations. +bool port_buffer_is_dma_capable(const void *ptr); +#endif + size_t port_heap_get_largest_free_size(void); diff --git a/supervisor/shared/port.c b/supervisor/shared/port.c index 3b9f0c8718161..aabf003fb302d 100644 --- a/supervisor/shared/port.c +++ b/supervisor/shared/port.c @@ -43,6 +43,15 @@ MP_WEAK void *port_malloc(size_t size, bool dma_capable) { return block; } +// Ensure allocated memory is zero. +MP_WEAK void *port_malloc_zero(size_t size, bool dma_capable) { + void *ptr = port_malloc(size, dma_capable); + if (ptr) { + memset(ptr, 0, size); + } + return ptr; +} + MP_WEAK void port_free(void *ptr) { tlsf_free(heap, ptr); } @@ -80,3 +89,30 @@ MP_WEAK bool port_boot_button_pressed(void) { return false; #endif } + +// Ports may provide an implementation of this function if it is needed +MP_WEAK void port_gc_collect(void) { +} + +// Allocates an object in the port heap, not the VM heap, and also sets type, for mp_obj_malloc{,_var} macros. +MP_NOINLINE void *mp_obj_port_malloc_helper(size_t num_bytes, const mp_obj_type_t *type) { + mp_obj_base_t *base = (mp_obj_base_t *)port_malloc_zero(num_bytes, false); + base->type = type; + return base; +} + +// Creates a tuple on the port heap, not the VM heap. +// Implementation copied from py/objtuple.c. +mp_obj_t mp_obj_new_port_tuple(size_t n, const mp_obj_t *items) { + if (n == 0) { + return mp_const_empty_tuple; + } + mp_obj_tuple_t *o = mp_obj_port_malloc_var(mp_obj_tuple_t, items, mp_obj_t, n, &mp_type_tuple); + o->len = n; + if (items) { + for (size_t i = 0; i < n; i++) { + o->items[i] = items[i]; + } + } + return MP_OBJ_FROM_PTR(o); +}