Compare commits

...

156 commits

Author SHA1 Message Date
ffea37149a ci: Enable format checking in many builds.
Signed-off-by: Jeff Epler <jepler@gmail.com>
2025-08-27 14:15:00 -05:00
b02f562bbf stm32: Don't list format plugin on linker commandline.
It causes an error, so filter it out, similar to other compile-only
flags.

Signed-off-by: Jeff Epler <jepler@gmail.com>
2025-08-27 14:12:57 -05:00
ed22c8b729 nrf: Can't list format plugin on linker commandline.
.. so filter it out, similar to stm32.

Signed-off-by: Jeff Epler <jepler@gmail.com>
2025-08-27 14:12:57 -05:00
9f53701ac0 micropython_checks: Add compiler plugin.
Signed-off-by: Jeff Epler <jepler@gmail.com>
2025-08-27 14:12:57 -05:00
David Schneider
8c47e446bf py/obj: Fix a comment regarding make_new slot.
This was missed as part of the transition to make_new a mp_obj_type_t slot.

See also: 94beeabd2e

Signed-off-by: David Schneider <schneidav81@gmail.com>
2025-08-26 23:16:01 +10:00
Alessandro Gatti
c33a02fe6d tests/run-tests.py: Enable Arm inlineasm FPU tests if possible.
This commits lifts the unconditional restriction on inline assembler FPU
tests for the Qemu platform, and makes said restriction conditional to
the lack of an available floating point unit on the running platform.

The Qemu platform supported only emulated machines that could target up
to a Cortex-M3, so an ArmV7-M target that had no support for floating
point.  With the addition of MPS2_AN500 to the list of emulated targets
the range was extended to cover up to Cortex-M7, so a floating point
unit may possibly be available and thus able to run the FPU inlineasm
tests.

For that, the test runner was changed to detect the running architecture
when checking the target capabilities; if the target reports its
MicroPython architecture to be either "armv7emsp" or "armv7emdp"
(providing single-precision and double-precision floating point unit
support respectively) then the FPU-only inline tests are not put into
the blocked tests list.

Signed-off-by: Alessandro Gatti <a.gatti@frob.it>
2025-08-26 12:27:21 +10:00
Alessandro Gatti
6c1d1f3ad4 qemu/mcu/arm/mps2.ld: Add .ARM.exidx section to the linkerscript.
This commit fixes a linking issue on certain Arm toolchains where
library code is compiled with exception support.

If a library with exception support is included in the MicroPython
build, the linker had no place to put the stack unwinding tables
necessary to perform exception handling at runtime.  This change adds a
new section to the linkerscript (and therefore the final ELF file) where
that data can be placed into.

Signed-off-by: Alessandro Gatti <a.gatti@frob.it>
2025-08-26 12:27:21 +10:00
Alessandro Gatti
6650506e06 tools/ci.sh: Extend Arm testing to include hardfp targets.
This commit lets CI extend the testing scope of the QEMU Arm target, by
letting it perform the usual battery of tests (interpreter and natmods)
also on hardfp targets.

The default board for Arm testing lacks hardware floating point support,
so natmods weren't tested in that specific configuration.  With the
introduction of the "MPS_AN500" QEMU target, now this is made possible
as said board emulates a Cortex-M7 machine with a single- and
double-precision floating point unit.

To reduce the impact on build times, the "ci_qemu_build_arm_thumb" CI
step was split in two: "ci_qemu_build_arm_thumb_softfp" and
"ci_qemu_build_arm_thumb_hardfp" - so hopefully those can run in
parallel whenever possible.

Signed-off-by: Alessandro Gatti <a.gatti@frob.it>
2025-08-26 12:27:21 +10:00
Alessandro Gatti
64cac4690f qemu/mcu/arm/errorhandler: Add ARMv7-M debug registers.
This commit extends the QEMU port's CPU error handler for the Arm target
by printing out in detail the ARMv7-M debug registers if the firmware is
compiled for such a target.

Signed-off-by: Alessandro Gatti <a.gatti@frob.it>
2025-08-26 12:27:21 +10:00
Alessandro Gatti
19be404ad8 qemu/arm: Add definition for the MPS2_AN500 machine.
This commit introduces a new target for the QEMU port called
"MPS2_AN500", an ARMv7-M machine with a Cortex-M7 CPU and
single-/double-precision floating point unit.

Signed-off-by: Alessandro Gatti <a.gatti@frob.it>
2025-08-26 12:27:21 +10:00
Alessandro Gatti
87099b5731 qemu/Makefile: Allow overriding floating point mode by boards.
This commit lets board use a different floating point mode rather than
the usual soft-float that was the original default for all QEMU-based
boards.

The configuration options are the same available in the "stm32" port.
Boards can set "MICROPY_FLOAT_IMPL" to either "float", "double", or
"none" to indicate which floating point mode they want, and optionally
"SUPPORTS_HARDWARE_FP_SINGLE" or "SUPPORTS_HARDWARE_FP_DOUBLE" can be
set to 1 to further indicate the hardware capabilities of the hardware
floating point unit, if present.

Signed-off-by: Alessandro Gatti <a.gatti@frob.it>
2025-08-26 12:27:21 +10:00
Alessandro Gatti
b0fd0079f4 tests/micropython: Make tests behave in low memory condition.
This commit changes the "viper_ptr*_store_boundary" tests to make them
fail more gracefully in low memory conditions.

The original version of the tests compiled viper code blocks on the fly
when it needed them, making them fail at runtime on some boards that do
not come with enough memory for this test.  This clashes with
"run-tests.py"'s ability to look for a particular signature to mark
tests as skipped due to not enough memory.

Now compiled code blocks are generated at the beginning of the test
inside an appropriate exception handler.  In case of a memory error when
pre-compiling a code block, the running test exits reporting a low
memory condition to the test runner.  This allows to have clean test
runs on all platforms when it comes to viper pointer tests.

Signed-off-by: Alessandro Gatti <a.gatti@frob.it>
2025-08-26 10:47:17 +10:00
Daniël van de Giessen
1df5ee12e8 esp32/network_ppp: Stop polling if PPP was disconnected.
When disconnecting from PPP the modem sends a confirmation. This message
is received, like all messages, through the poll() method. lwIP may then
immediately call our status callback with code PPPERR_USER to indicate
the connection was closed. Our callback then immediately proceeds to
free the PCB. Thus, during each new iteration of the loop in poll() we
must check if we haven't disconnected in the meantime to prevent calling
the pppos_input_tcpip with a PCB that is now NULL.

Signed-off-by: Daniël van de Giessen <daniel@dvdgiessen.nl>
2025-08-25 23:11:33 +10:00
Daniël van de Giessen
72147c02c7 esp32/network_ppp: Stop polling if stream becomes None.
If while a polling operation is active the stream is removed we should
stop polling data from that stream.

This was already the intended behaviour, but implemented incorrectly.

Signed-off-by: Daniël van de Giessen <daniel@dvdgiessen.nl>
2025-08-25 23:11:33 +10:00
Yilin Sun
053aade741 mimxrt/boards: Re-generate MIMXRT1052 clock config files.
These were regenerated by the NXP Config tool for v2.11.

The board_init update was needed to ensure CLOCK_SetMode() is run
at the appropriate time during startup.

Signed-off-by: Andrew Leech <andrew.leech@planetinnovation.com.au>
2025-08-25 10:48:17 +10:00
Yilin Sun
bd7342d9ec mimxrt: Restructure nxp_sdk to match official mcux-sdk.
The official mcux-sdk follows a slightly different structure to the
current nxp_sdk submodule, with many drivers moved to a common location.

To ease updating the newer versions of the SDK and/or add new families
the nxp_sdk submodule has been updated to follow the structure of
mcux-sdk, just trimmed down to families used here to considerably
reduce the size.

Signed-off-by: Andrew Leech <andrew.leech@planetinnovation.com.au>
2025-08-25 10:48:16 +10:00
Damien George
a3f9dec788 py/mpconfig: Enable the sys module at all feature levels by default.
This is a pretty fundamental module, and even minimal ports like unix and
zephyr minimal have it enabled.  So, enabled it by default at the lowest
feature level.

Most things in the `sys` module are configurable, and off by default, so it
shouldn't add too much to ports that don't already have it enabled (which
is just the minimal port).

Also note that `sys` is still disabled on the bare-arm port, to keep that
ultra minimal.  It means we now have bare-arm without `sys` and the minimal
port with `sys`.  That will allow different code size comparisons if/when
new `sys` features are added.

Signed-off-by: Damien George <damien@micropython.org>
2025-08-22 13:31:10 +10:00
Damien George
14e9c00cb9 py/builtinimport: Guard code needing sys.path with MICROPY_PY_SYS_PATH.
Signed-off-by: Damien George <damien@micropython.org>
2025-08-22 13:30:46 +10:00
Damien George
b5fcb33eaa py/mpconfig: Enable CRYPTOLIB, HASHLIB_MD5, HASHLIB_SHA1 if SSL enabled.
This commit unifies the configuration of MICROPY_PY_CRYPTOLIB,
MICROPY_PY_HASHLIB_MD5 and MICROPY_PY_HASHLIB_SHA1, so they are enabled by
default if MICROPY_PY_SSL is enabled.  This matches the existing
configuration of most of the ports.

With this change, all ports remain the same except:
- reneses-ra now enables MICROPY_PY_CRYPTOLIB, MICROPY_PY_HASHLIB_MD5 and
  MICROPY_PY_HASHLIB_SHA1.
- rp2 now enables MICROPY_PY_HASHLIB_MD5.

Signed-off-by: Damien George <damien@micropython.org>
2025-08-19 21:10:00 +10:00
Damien George
989abae12c py/mpconfig: Move MICROPY_MODULE___ALL__ option to other module options.
Signed-off-by: Damien George <damien@micropython.org>
2025-08-19 13:00:45 +10:00
Damien George
169d382248 py/mpconfig: Rename MICROPY_PY___FILE__ to MICROPY_MODULE___FILE__.
For consistency with other module-related configuration options.

Signed-off-by: Damien George <damien@micropython.org>
2025-08-19 13:00:45 +10:00
Damien George
3650196682 py/objtype: Use locals_ptr directly instead of getting it from the slot.
This is a very minor code simplification, which reduces code size by about
-8 bytes.  It should have no functional change.

Signed-off-by: Damien George <damien@micropython.org>
2025-08-19 12:58:02 +10:00
Alessandro Gatti
bd413d3d85 py/asmthumb: Fix T3 encoding of conditional branches.
This commit fixes the encoding of conditional branch opcodes emitted for
ARMv7-M targets, when the emitter decides to use the T3 encoding for
said operation.

Fields J1 and J2 are now present in the generated opcode word, along
with correcting some minor issues in bitmasks and shifts computation.

This fixes #17940.

Signed-off-by: Alessandro Gatti <a.gatti@frob.it>
2025-08-19 12:40:29 +10:00
Angus Gratton
22deeeb8db tests/stress/recursive_iternext: Rewrite to find its own limit.
Necessary on the unix port when running with sanitizers, as the newly
increased stack size can run all tests at N=5000 without raising
RuntimeError, and increasing N to fix this causes issues on other
configurations.

This way the test progressively builds a deeper data structure until it
fails with RuntimeError. This is theoretically slower, but not noticeably
so in reality.

Signed-off-by: Angus Gratton <angus@redyak.com.au>
2025-08-19 10:33:40 +10:00
Angus Gratton
d8672f4cde unix: Increase stack sizes if running with sanitizers.
The specific problem seems to be that the runtime "Python stack frame"
function call is several times more expensive in stack usage when running
with UBSan on older GCC (observed on gcc 11.4 as used in CI, would get
'RuntimeError: maximum recursion depth exceeded' when running some tests
with UBSan enabled.)

Other stack usage (i.e. from pushing things on the stack in Python) stays
the same. Whatever causes the usage seems to be mostly gone in later GCC
versions.

Includes a refactor to apply the same stack size multipliers
for the default thread stack size same as the main stack size.

This goes in a new port-specific header as it depends on macros
in misc.h, so can't be in mpconfigport.h.

A side effect of this is that the default thread stack size is
now doubled on ARM, same as the main stack size.

This work was funded through GitHub Sponsors.

Signed-off-by: Angus Gratton <angus@redyak.com.au>
2025-08-19 10:33:40 +10:00
Angus Gratton
3faf229853 py/misc: Add a way to detect sanitizer builds.
Clang and gcc>=14 can use __has_feature() to detect if a sanitizer
is enabled, but older GCC has no mechanism - need to set a macro
explicitly for this to be recognised.

Necessary for increasing some resource limits in sanitizer builds.
Important not to use to avoid real issues!

This work was funded through GitHub Sponsors.

Signed-off-by: Angus Gratton <angus@redyak.com.au>
2025-08-19 10:33:40 +10:00
Angus Gratton
d81d56cc4d tools/ci: Add UBSan to longlong CI build.
Also rewrite the sanitizer argument variables to not assume a variant.

longlong variant currently fails in this config, due to a bug fixed
in follow-up commit.

Signed-off-by: Angus Gratton <angus@redyak.com.au>
2025-08-19 10:33:40 +10:00
Tico06
020eeba412 stm32/eth_phy: Fix typo in header guard macro.
Typo on line 29 (PYH instead of PHY).

Compilation failing, here is the output:

    eth_phy.h:28: error: header guard 'MICROPY_INCLUDED_STM32_PHY_H'
    followed by '#define' of a different macro [Werror=header-guard]
        28 | #ifndef MICROPY_INCLUDED_STM32_PHY_H
    eth_phy.h:29: note: 'MICROPY_INCLUDED_STM32_PYH_H' is defined here;
    did you mean 'MICROPY_INCLUDED_STM32_PHY_H'?
        29 | #define MICROPY_INCLUDED_STM32_PYH_H

Signed-off-by: Tico06 <e.grammatico@gmail.com>
2025-08-18 14:07:02 +10:00
iabdalkader
c7ddf0c54f stm32/Makefile: Add .gc.blocks.table section to generated binary.
The generated binary file was missing this section, which caused a hard
fault when loading bin or dfu firmware (eg on ARDUINO_GIGA).

Signed-off-by: iabdalkader <i.abdalkader@gmail.com>
2025-08-18 13:57:30 +10:00
Yuuki NAGAO
365329cd54 stm32/dac: Fix DAC write for MCUs that have D-Cache.
To prevent wrong DAC output, clean D-cache before starting DMA.

For more details, please refer to the following document:
https://www.st.com/resource/en/application_note/DM00272913.pdf

Signed-off-by: Yuuki NAGAO <wf.yn386@gmail.com>
2025-08-18 13:20:50 +10:00
Yuuki NAGAO
152a0782e6 stm32/dac: Add support for DAC feature on STM32G0.
DAC.write() and DAC.write_timed() are now available on STM32G0.

Tested on NUCLEO_G0B1RE.

Signed-off-by: Yuuki NAGAO <wf.yn386@gmail.com>
2025-08-18 13:17:34 +10:00
Yuuki NAGAO
1b35116c92 stm32/dac: Fix 12-bit DAC issue on STM32H5.
For STM32H5, to use 12-bit DAC, the DMA parameter should set:
- Actual DMA source datawidth to CTR1.
- The length is the amount of data to be transferred from source to
  destination in bytes.

Also, this commit modifies the (dummy) definition of DMA_CIRCULAR for
STM32H5 to prevent conflict with data width specification.

Signed-off-by: Yuuki NAGAO <wf.yn386@gmail.com>
2025-08-18 13:11:01 +10:00
dependabot[bot]
1588c455c4 github/workflows: Bump actions/checkout from 4 to 5.
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-16 18:36:57 +10:00
Maureen Helm
afa7265ffa zephyr: Upgrade to Zephyr v4.2.0.
Updates the Zephyr port build instructions and CI to use the latest
Zephyr release tag.

Tested on max32690fthr and frdm_k64f.

Signed-off-by: Maureen Helm <maureen.helm@analog.com>
2025-08-16 15:10:30 +10:00
Thomas Watson
bba3542018 extmod/modlwip: Remove unused include and functions.
The lwIP includes are now port-specific.

The `sys_arch_{,un}protect` functions are not used according to line 34
of `extmod/lwip-include/lwipopts_common.h`.

Neither have been touched in nearly a decade.

Signed-off-by: Thomas Watson <twatson52@icloud.com>
2025-08-16 14:52:52 +10:00
Damien George
b7cfafc1ee alif/alif.mk: Add MPY_CROSS_FLAGS setting.
The HP and HE CPUs have double-precision hardware floating point, so can
use the armv7emdp architecture.

This allows frozen code to use native/viper/asm_thumb decorators.

Fixes issue #17896.

Signed-off-by: Damien George <damien@micropython.org>
2025-08-15 12:45:33 +10:00
Damien George
f0f5abb7a3 alif/mpconfigport: Enable cryptolib and hashlib.md5/sha1.
They are enabled when SSL/mbedTLS is included in the firmware.  These new
features cost around +1400 bytes of code size.

Signed-off-by: Damien George <damien@micropython.org>
2025-08-15 12:44:39 +10:00
Damien George
f1462448d0 alif/modtime: Implement the rest of the time module.
Adds: `time.time()`, `time.time_ns()`, `time.localtime()`, `time.mktime()`
and `time.gmtime()`.

Signed-off-by: Damien George <damien@micropython.org>
2025-08-15 12:35:24 +10:00
Damien George
326730d8b2 alif/mbedtls: Implement the mbedTLS time function.
Signed-off-by: Damien George <damien@micropython.org>
2025-08-15 12:35:23 +10:00
Damien George
46b366d7b2 alif/fatfs_port: Implement get_fattime.
Signed-off-by: Damien George <damien@micropython.org>
2025-08-15 12:35:21 +10:00
Damien George
0feb4f5ea4 alif/mphalport: Implement mp_hal_time_ns.
Signed-off-by: Damien George <damien@micropython.org>
2025-08-15 12:35:18 +10:00
Damien George
e58848a21e alif/machine_rtc: Implement RTC.datetime to get and set the RTC.
The LPRTC peripheral is a 32-bit counter with a 16-bit prescaler.  It's
configured here to count at 1Hz (to get maximum date range) and then the
prescaler value is used to get 30 microsecond resolution.  That's
essentially a 32+15=47-bit counter.

Signed-off-by: Damien George <damien@micropython.org>
2025-08-15 12:34:28 +10:00
Damien George
b3cd1a355e extmod/modtime: Move tuple creation to common localtime implementation.
This factors code out of the ports and into the common `time.localtime`
implementation in `extmod/modtime.c`.  That helps to reduce code
duplication, prevent errors in implementation, and reduce code size on
some ports (mimxrt and stm32 at least).

Signed-off-by: Damien George <damien@micropython.org>
2025-08-15 12:09:13 +10:00
Damien George
40cc4e4f74 py/objtype: Make mp_obj_new_type a static function.
It's only used once, in the same file it's defined, and making it static
reduces code size.

Along with this, the associated example code comment in `ports/unix/main.c`
has been removed.

Signed-off-by: Damien George <damien@micropython.org>
2025-08-15 11:45:11 +10:00
Damien George
3efbd726eb py/parse: Remove explicit checks for invalid folding operations.
They are instead checked by `binary_op_maybe()`, which catches exceptions
for invalid int/float operations.

This is a follow-up to 69ead7d98e

Signed-off-by: Damien George <damien@micropython.org>
2025-08-15 11:38:20 +10:00
Phil Howard
141f7d0c35 py/mkrules.cmake: Clean genhdr and frozen_mpy dirs.
CMake builds relied upon the parent Makefile removing the entire
build directory to successfully clean build artifacts.

py/mkrules.cmake: Add ADDITIONAL_CLEAN_FILES properties to ensure a
		  "make clean" from within the build directory removes
                  the genhdr and frozen_mpy directories.

Signed-off-by: Phil Howard <github@gadgetoid.com>
2025-08-15 01:24:53 +10:00
0615d13963 py/objringio: Detect incorrect constructor calls.
ringbuffer.size must be at least 2, and is a 16-bit quantity.

This fixes several cases including the one the fuzzer discovered, which
would lead to a fatal signal when accessing the object.

Fixes issue #17847.

Signed-off-by: Jeff Epler <jepler@gmail.com>
2025-08-15 01:21:25 +10:00
803da9645f extmod/modframebuf: Save code size in setpixel.
There's a slight code size increase paid for by using setpixel_checked for
the last pixel of a line, instead of repeating the checks inline.

Signed-off-by: Jeff Epler <jepler@gmail.com>
2025-08-15 01:17:00 +10:00
e15219800e extmod/modframebuf: Fix crash in scroll() for large inputs.
If mp_int_t is wider than int, then the tests such as `xend < 0` can fail
even when the amount of scrolling requested is out of range.  This resulted
in a segmentation fault when attempting an out-of-bounds access to the
framebuffer.

Signed-off-by: Jeff Epler <jepler@gmail.com>
2025-08-15 01:16:24 +10:00
Damien George
2bba507148 tests: Require SSL certificate file to be available for test to run.
Previously, any test needing an SSL certificate file would automatically
skip if the file could not be found.  But that makes it too easy to
accidentally skip tests.

Instead, change it so that the test fails if the certificate file doesn't
exist.  That matches, for example, the fact that the test fails if
networking (LAN, WiFi) is not active.

Signed-off-by: Damien George <damien@micropython.org>
2025-08-15 01:03:00 +10:00
Damien George
a279c64046 tests: Add .native.exp output files for tests that differ with native.
Signed-off-by: Damien George <damien@micropython.org>
2025-08-15 00:36:27 +10:00
Damien George
3c72c3a1e6 tests/micropython/opt_level_lineno.py: Force test func to use bytecode.
So that the test can run the same on all targets when used with the native
emitter.

Signed-off-by: Damien George <damien@micropython.org>
2025-08-15 00:36:27 +10:00
Damien George
95d1794afd tests/misc/print_exception.py: Use "raise e" instead of no-arg "raise".
This allows the test to run with the native emitter.

The test semantics remain the same.

Signed-off-by: Damien George <damien@micropython.org>
2025-08-15 00:36:27 +10:00
Damien George
0f5f6484a2 tests/run-tests.py: Add support for .native.exp expected output files.
There are currently a few tests that are excluded when using the native
emitter because they test printing of exception tracebacks, which includes
line numbers.  And the native emitter doesn't store line numbers, so gets
these tests wrong.

But we'd still like to run these tests using the native emitter, because
they test useful things even if the line number info is not in the
traceback (eg that threads which crash print out their exception).

This commit adds support for native-specific .exp files, which are of the
form `<test>.py.native.exp`.  If such an .exp file exists then it take
precedence over any normal `<test>.py.exp` file.

(Actually, the implementation here is general enough that it also supports
`<test>.py.bytecode.exp` as well, if bytecode ever needs a specific exp
file.)

Signed-off-by: Damien George <damien@micropython.org>
2025-08-15 00:36:27 +10:00
Damien George
0cb2c69b3f tests/misc/rge_sm.py: Remove unused code from the test.
This cleans up the test to remove all unused code, making it smaller,
a bit faster to deploy to a target to run, and also use less RAM on the
target (which may help it run on targets that are just slightly out of
memory running it).

Signed-off-by: Damien George <damien@micropython.org>
2025-08-15 00:30:41 +10:00
Damien George
c16fe5b5ed webassembly: Enable C-stack checking.
This gets the recursive stress-tests working on this port.

For relatively small Python functions the maximum recursive depth is about
150 nested calls.

Signed-off-by: Damien George <damien@micropython.org>
2025-08-15 00:23:16 +10:00
Damien George
f9b6d8e608 github/workflows: Run webassembly and zephyr workflows if tests/ change.
Because these ports run tests as part of CI, and need to be run if any of
the tests change, to check those changes.

Signed-off-by: Damien George <damien@micropython.org>
2025-08-15 00:23:16 +10:00
Damien George
54e6cfc6e3 tests/basics: Skip tests of io module individually using SKIP.
Instead of using a feature check.  This is more consistent with how other
optional modules are skipped.

Signed-off-by: Damien George <damien@micropython.org>
2025-08-15 00:23:16 +10:00
Damien George
e2744ce679 tests/run-tests.py: Autodetect if the target has unicode support.
The unicode tests are now run on all targets that enable unicode.  And
other unicode tests (namely `extmod/json_loads.py`) are now properly
skipped if the target doesn't have unicode support.

Signed-off-by: Damien George <damien@micropython.org>
2025-08-15 00:23:16 +10:00
Damien George
1db71f9e55 tests/run-tests.py: Generalise addition of port specific test directory.
Signed-off-by: Damien George <damien@micropython.org>
2025-08-15 00:23:16 +10:00
Damien George
6565827955 tests/run-tests.py: Always include stress/ tests directory in tests.
Ports that now run the stress tests, that didn't prior to this commit are:
cc3200, esp8266, minimal, nrf, renesas-ra, samd, qemu, webassembly.

Signed-off-by: Damien George <damien@micropython.org>
2025-08-15 00:23:16 +10:00
Damien George
f493075d88 tests/run-tests.py: Automatically include float tests when possible.
This simplifies the code by removing the explicit addition of the "float/"
test directory for certain targets.  It also means the tests won't be added
incorrectly, eg on a unix build without float.

Signed-off-by: Damien George <damien@micropython.org>
2025-08-15 00:23:16 +10:00
Angus Gratton
744270ac1b py/misc: Add explicit dependency on py/mpconfig.h.
Macros in misc.h depend on values defined by including mpconfig.h.

This work was funded through GitHub Sponsors.

Signed-off-by: Angus Gratton <angus@redyak.com.au>
2025-08-13 10:13:08 +10:00
4614ee9e68 py/binary: Add MICROPY_PY_STRUCT_UNSAFE_TYPECODES.
This adds a compile-time flag to disable some "unsafe" and non-standard
typecodes in struct, array and related modules.

This is useful to turn off when fuzzing, as improper use of these typecodes
can crash MicroPython.

Signed-off-by: Jeff Epler <jepler@gmail.com>
2025-08-12 14:34:19 +10:00
Alessandro Gatti
0ee3f99da2 py/asmrv32: Make lt/le comparisons emitter shorter.
This commit simplifies the emitter code in charge of generating opcodes
performing less-than and less-than-or-equal comparisons.

By rewriting the SLT/SLTU opcode generator (handling less-than
comparisons) and de-inlining the less-than comparison generator call in
the less-than-or-equal generator, the output binary is ~80 bytes smaller
(measurements taken from the QEMU port).

Signed-off-by: Alessandro Gatti <a.gatti@frob.it>
2025-08-11 22:31:13 +10:00
Alessandro Gatti
b1d5c656de tests/micropython: Remove big ints dependence for viper boundary tests.
This commit provides an implementation for viper boundary tests that can
work even without big int support.

Since it uses a fixed-size buffer to hold values to work with, this
should work on any platform as long as its integers are at least 32 bits
wide, regardless its configuration on how big integers can get.

Signed-off-by: Alessandro Gatti <a.gatti@frob.it>
2025-08-11 16:46:15 +10:00
Jared Hancock
f10707febb extmod/modlwip: Support family specification in getaddrinfo.
`socket.getaddrinfo()` supports the specification of an address family;
however, the LwIP implementation does not use it. This change allows the
application to specify the address family request in DNS resolution. If
no family is specified, it falls back to the default preference
configured with `network.ipconfig()`.

Signed-off-by: Jared Hancock <jared.hancock@centeredsolutions.com>
2025-08-11 16:22:49 +10:00
Jared Hancock
14ccdeb4d7 extmod/modre: Add support for start- and endpos.
Pattern objects have two additional parameters for the ::search and ::match
methods to define the starting and ending position of the subject within
the string to be searched.

This allows for searching a sub-string without creating a slice.  However,
one caveat of using the start-pos rather than a slice is that the start
anchor (`^`) remains anchored to the beginning of the text.

Signed-off-by: Jared Hancock <jared@greezybacon.me>
2025-08-11 14:11:56 +10:00
Daniël van de Giessen
485dac783b tools/codeformat.py: Print filename + linenumber when dedenting fails.
Signed-off-by: Daniël van de Giessen <daniel@dvdgiessen.nl>
2025-08-11 13:35:34 +10:00
Daniël van de Giessen
6e450dba7e tools/codeformat.py: Iterate lines instead of modifying list in-place.
Co-authored-by: David Lechner <david@pybricks.com>
Signed-off-by: Daniël van de Giessen <daniel@dvdgiessen.nl>
2025-08-11 13:35:27 +10:00
Jos Verlinde
20e1ae0733 tools/mpremote: Fix encoding error in PyboardCommand.
This is a fix for utf-8 decoding errors that are thrown when non-utf-8
content is received.  For instance during a reboot of an ESP8266 module.

The fix is to handle conversion errors by replacing illegal characters.
Note that these illegal characters most often occur during an MCU reboot
sequence when the MCU is using baudrates different from 115200.

Signed-off-by: Jos Verlinde <Jos_Verlinde@hotmail.com>
2025-08-11 13:28:56 +10:00
Daniël van de Giessen
8c47ff7153 esp32/network_ppp: Correctly clean up PPP PCB after close.
If PPP is still connected, freeing the PCB will fail and thus instead we
should trigger a disconnect and wait for the lwIP callback to actually
free the PCB.

When PPP is not connected we should check if the freeing failed, warn
the user if so, and only mark the connection as inactive if not.

When all this happens during garbage collection the best case is that
the PPP connection is already dead, which means the callback will be
called immediately and cleanup will happen correctly. The worst case is
that the connection is still alive, thus we are unable to free the PCB
(lwIP won't let us) and it remains referenced in the netif_list, meaning
a use-after-free happens later when lwIP traverses that linked list.

This change does not fully prevent that, but it *does* improve how the
PPP.active(False) method on the ESP32 port behaves: It no longer
immediately tries to free (and fails), but instead triggers a disconnect
and lets the cleanup happen correctly through the status callback.

Signed-off-by: Daniël van de Giessen <daniel@dvdgiessen.nl>
2025-08-11 12:38:37 +10:00
Daniël van de Giessen
adcfdf625b esp32/network_ppp: Use non-thread-safe API inside status callback.
The status callback runs on the lwIP tcpip_thread, and thus must use the
non-thread-safe API because the thread-safe API would cause a deadlock.

Signed-off-by: Daniël van de Giessen <daniel@dvdgiessen.nl>
2025-08-11 12:38:37 +10:00
Daniël van de Giessen
361c615f3e esp32/network_ppp: Use thread-safe API for PPPoS input.
A small follow-up to 3b1e22c669, in which
the entire PPP implementation was reworked to more closely resemble the
extmod version. One of the differences between extmod and the ESP32 port
is that the ESP32 port uses the thread-safe API, but in that changeset
the PPP input function was accidentally changed to use the non-safe API.

Signed-off-by: Daniël van de Giessen <daniel@dvdgiessen.nl>
2025-08-11 12:38:37 +10:00
Daniël van de Giessen
1273751a3b esp32: Support building against IDFv5.5.
Signed-off-by: Daniël van de Giessen <daniel@dvdgiessen.nl>
2025-08-11 12:23:02 +10:00
Damien George
1401fdb8b3 tests/run-tests.py: Run tests-with-regex-output as normal tests.
Some tests (currently given by the `special_tests` list) have output which
must be mached via a regex, because it can change from run to run (eg the
address of an object is printed).  These tests are currently classified as
`is_special` in the test runner, which means they get special treatment.
In particular they don't set the emitter as specified by `args.emit`.  That
means these tests do not run via .mpy or using the native emitter, even if
those options are given on the command line.

This commit fixes that by considering `is_special` as different to
`tests_with_regex_output`.  The former is used for things like target
feature detection (which are not really tests) and when extra command line
options need to be passed to the unix micropython executable.  The latter
(now called `tests_with_regex_output`) are specifically for tests that have
output to be matched via regex.

The `thread_exc2.py` test now needs to be excluded when running using the
native emitter, because the native emitter doesn't print traceback info.
And the `sys_settrace_cov.py` test needs to be excluded because set-trace
output is different with the native emitter.

Signed-off-by: Damien George <damien@micropython.org>
2025-08-10 11:40:54 +10:00
Damien George
c35427cbdb tests/run-tests.py: Move tests to skip with native emitter to a list.
This makes `run-tests.py` a little more organised, by putting all the
tests-to-skip-when-using-the-native-emitter in a dedicated list.

This should make it easier to maintain the list, and understand why a test
is there.

Signed-off-by: Damien George <damien@micropython.org>
2025-08-10 11:25:38 +10:00
Damien George
7e8705fe2b all: Bump version to 1.27.0-preview.
Signed-off-by: Damien George <damien@micropython.org>
2025-08-10 08:42:11 +10:00
Damien George
4ce2dd2cda all: Bump version to 1.26.0.
Signed-off-by: Damien George <damien@micropython.org>
2025-08-09 23:31:31 +10:00
Angus Gratton
593ae04eeb esp32/machine_timer: Fix machine.Timer() tick frequency on ESP32C2,C6.
Also future-proofs this code for other chips. Apart form C6 and C2, all
currently supported chips use APB clock for GPTIMER_CLK_SRC_DEFAULT.

ESP32-C2 uses 40MHz PLL but APB_CLK_FREQ was 26MHz.
ESP32-C6 uses 80MHz PLL but APB_CLK_FREQ was 40MHz.

Implementation now gets the correct frequency from ESP-IDF.

This work was funded through GitHub Sponsors.

Signed-off-by: Angus Gratton <angus@redyak.com.au>
2025-08-07 16:55:46 +10:00
Angus Gratton
ce109af712 esp32/machine_timer: Enable timer clock source for ESP32C6.
Otherwise the PLL is not enabled.  These changes are adapted from
`timer_legacy.h` in ESP-IDF.

This work was funded through GitHub Sponsors.

Signed-off-by: Angus Gratton <angus@redyak.com.au>
2025-08-07 16:55:23 +10:00
Damien George
d5ecda05eb ports: Allow MICROPY_PY_MACHINE_I2C_TARGET to be disabled by board cfg.
Signed-off-by: Damien George <damien@micropython.org>
2025-08-07 10:33:26 +10:00
Damien George
255d74b5a8 renesas-ra/mpconfigport: Enable MICROPY_TIME_SUPPORT_Y1969_AND_BEFORE.
This setting was missed in df05caea6c.  It's
needed for this port to pass its `tests/ports/renesas-ra/modtime.py` test.

Signed-off-by: Damien George <damien@micropython.org>
2025-08-04 10:41:06 +10:00
Damien George
7c8ae78a03 lib/micropython-lib: Update submodule to latest.
This brings in:
- lora: fix SNR value in SX126x received packets
- utop: add initial implementation for ESP32
- utop: print MicroPython memory info
- utop: print IDF heap details
- urllib.urequest: add support for headers to urequest.urlopen
- aiorepl: use blocking reads for raw REPL and raw paste
- errno: add ENOTCONN constant
- logging: allow logging.exception helper to handle tracebacks
- aioble-l2cap: raise correct error if l2cap disconnects during send
- abc: add ABC base class
- aiohttp: fix partial reads by using readexactly
- aiorepl: handle stream shutdown

Signed-off-by: Damien George <damien@micropython.org>
2025-08-04 01:50:32 +10:00
c0252d73c6 py/parse: Fix missing nlr_pop call in complex path of binary_op_maybe.
Reproducer (needs to be run as one compilation unit):

    ans = (-1) ** 2.3
    aa

Fixes issue #17815.

Signed-off-by: Jeff Epler <jepler@gmail.com>
2025-08-03 08:29:28 +10:00
Damien George
658a2e3dbd github/workflows: Add a CI job to build ESP32-C2 and ESP32-C6 boards.
So that all six supported SoCs are built by CI.

Signed-off-by: Damien George <damien@micropython.org>
2025-08-02 08:53:36 +10:00
Damien George
3c9546ea09 esp32/mpconfigport: Disable I2CTarget on ESP32-C6 to reduce code size.
I2CTarget costs about 8k of flash size on ESP32-S2, and about 11k on
ESP32-C6.  The ESP32-C6 only has about 8k remaining, so disable I2CTarget
on that SoC until more flash can be made available.

Signed-off-by: Damien George <damien@micropython.org>
2025-08-02 08:53:36 +10:00
Phil Howard
e6739fc87e rp2/rp2_flash: Add binary info for ROMFS.
This describes the ROMFS location and size in Pico SDK's binary declaration
format, so it can be read from a .uf2 file for use with various tools.

Signed-off-by: Phil Howard <github@gadgetoid.com>
2025-08-02 00:36:50 +10:00
Jos Verlinde
a9dd741e66 docs/reference/mpremote: Document location of config file.
Signed-off-by: Jos Verlinde <Jos_Verlinde@hotmail.com>
2025-08-02 00:23:21 +10:00
Jos Verlinde
64b3944b01 tools/mpremote: Locate config.py location across different host OSes.
Use `platformdirs.user_config_dir()` (see
https://platformdirs.readthedocs.io/en/latest/api.html#user-config-directory)
to provide portability across many different OSes and configuration styles.

Signed-off-by: Jos Verlinde <Jos_Verlinde@hotmail.com>
2025-08-02 00:22:32 +10:00
Jos Verlinde
026a20da3e tools/mpremote: Add platformdirs dependency to requirements.txt.
Needed to easily find the user configuration file.

Signed-off-by: Jos Verlinde <Jos_Verlinde@hotmail.com>
2025-08-02 00:20:05 +10:00
Angus Gratton
907c5e9976 tests/extmod_hardware: Add basic tests for machine.Counter and Encoder.
These don't test any advanced features, just the basics.

This work was funded through GitHub Sponsors.

Signed-off-by: Angus Gratton <angus@redyak.com.au>
2025-08-02 00:03:03 +10:00
Jonathan Hogg
641ca2eb06 docs/library/machine: Add docs for Counter and Encoder.
Add documentation for `machine.Counter` and `machine.Encoder` as currently
implemented by the esp32 port, but intended to be implemented by other
ports.

Originally authored by: Ihor Nehrutsa <Ihor.Nehrutsa@gmail.com> and
Jonathan Hogg <me@jonathanhogg.com>.

Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
2025-08-02 00:00:03 +10:00
Jonathan Hogg
327655905e esp32/modules/machine.py: Add Counter and Encoder classes.
Adds a Python override of the `machine` module, which delegates to the
built-in module and adds an implementation of `Counter` and `Encoder`,
based on the `esp32.PCNT` class.

Original implementation by: Jonathan Hogg <me@jonathanhogg.com>

Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
2025-08-01 23:45:18 +10:00
Jonathan Hogg
e54553c496 docs/esp32: Add documentation for esp32.PCNT.
Document the new `esp32.PCNT` class for hardware pulse counting.

Originally authored by: Jonathan Hogg <me@jonathanhogg.com>

Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
2025-08-01 23:45:18 +10:00
Jonathan Hogg
c3f3339c87 esp32/modesp32: Add esp32.PCNT class.
Add a new `esp32.PCNT` class that provides complete, low-level support to
the ESP32 PCNT pulse counting hardware units.

This can be used as a building block to implement the higher-level
`machine.Counter` and `machine.Encoder` classes.

This is enabled by default on all OG, S2, S3, C6 boards, but not on C3 (as
the PCNT peripheral is not supported).

Original implementation by: Jonathan Hogg <me@jonathanhogg.com>

Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
Signed-off-by: Angus Gratton <angus@redyak.com.au>
2025-08-01 23:45:18 +10:00
Damien George
bf6f229cf3 docs/library: Document the new machine.I2CTarget class.
With some working examples that show how to use all the features.

Signed-off-by: Damien George <damien@micropython.org>
2025-08-01 23:03:17 +10:00
Damien George
277b615f26 tests/multi_extmod: Add I2CTarget multi tests.
These require two boards wired together, SCL-SCL and SDA-SDA.

Signed-off-by: Damien George <damien@micropython.org>
2025-08-01 23:03:17 +10:00
Damien George
6558d519a2 tests/extmod_hardware: Add self unittest for I2CTarget.
This test uses a SoftI2C controller wired to an I2CTarget on the one board,
and tests all functionality of the I2CTarget class.

Signed-off-by: Damien George <damien@micropython.org>
2025-08-01 23:03:17 +10:00
Damien George
7bc83afee2 esp32/machine_i2c_target: Implement I2CTarget class.
Only soft IRQs are supported.

Signed-off-by: Damien George <damien@micropython.org>
2025-08-01 23:03:17 +10:00
Damien George
ac5b1bce99 esp32/machine_i2c: Factor default pin macros to header file.
So the implementation of I2CTarget can use them.

Signed-off-by: Damien George <damien@micropython.org>
2025-08-01 23:03:17 +10:00
robert-hh
79d182deb2 samd/machine_i2c_target: Support I2C target mode.
Supporting readfrom_mem*(). writeto_mem() and a set of IRQs.  Enabled by
default for SAMD51 devices and SAMD21 devices with external flash.

Tested with ItsyBitsy M4 and ItsyBitsy M0 with both on-board SoftI2C and a
RP2 Pico as controller.

Signed-off-by: Damien George <damien@micropython.org>
Signed-off-by: robert-hh <robert@hammelrath.com>
2025-08-01 23:03:17 +10:00
robert-hh
5c78762c16 mimxrt/machine_i2c_target: Support I2C target mode.
The functionality is similar to the RP2 implementation.  The supported
address size is 7 bit.  In order to achieve a sufficient response, the
target I2C IRQ handler has to run from RAM, causing much more code moved to
RAM than required.

Tested with Teensy 4.1, MIMXRT1021EVK, MIMXRT1011EVK and MIMXRT1170, using
both a On-Board SoftI2C as controller and a RP2 Pico as external
controller.

Signed-off-by: Damien George <damien@micropython.org>
Signed-off-by: robert-hh <robert@hammelrath.com>
2025-08-01 23:03:17 +10:00
Damien George
67a442d8fa alif/machine_i2c: Allow changing I2C SCL/SDA pins.
Signed-off-by: Damien George <damien@micropython.org>
2025-08-01 23:03:17 +10:00
Damien George
6e72cae619 alif/machine_i2c_target: Implement I2CTarget class.
Signed-off-by: Damien George <damien@micropython.org>
2025-08-01 23:03:17 +10:00
Damien George
0c50343145 zephyr/machine_i2c_target: Implement I2CTarget class.
Tested and working on rpi_pico and nucleo_wb55rg.

Signed-off-by: Damien George <damien@micropython.org>
2025-08-01 23:03:17 +10:00
Damien George
1839340dda rp2/machine_i2c_target: Implement I2CTarget class.
Signed-off-by: Damien George <damien@micropython.org>
2025-08-01 23:03:17 +10:00
Damien George
56d2b47370 rp2/machine_i2c: Factor default pin macros to header file.
So they can be reused by the I2CTarget implementation.

Signed-off-by: Damien George <damien@micropython.org>
2025-08-01 23:03:17 +10:00
Damien George
01e570a347 stm32/machine_i2c_target: Implement I2CTarget class.
Works, tested on PYBV10, PYBD_SF2 and PYBD_SF6:

    buf = bytearray(16)
    machine.I2CTargetMemory("X", addr=67, mem=buf)

Signed-off-by: Damien George <damien@micropython.org>
2025-08-01 23:03:17 +10:00
Damien George
78d16672e1 stm32/i2cslave: Account for slow addr_match callback.
Signed-off-by: Damien George <damien@micropython.org>
2025-08-01 23:03:17 +10:00
Damien George
2443878bd9 stm32/i2cslave: Support i2c_slave_process_tx_end callback on F4.
The rounds out the F4 implementation to match the other supported MCUs.

Signed-off-by: Damien George <damien@micropython.org>
2025-08-01 23:03:17 +10:00
Damien George
17d0449ac8 stm32/i2cslave: Add functions to read/write I2C data.
Instead of requiring the callback to consume/provide the data.  This allows
the data to be consumed/provided later on, which will stretch the I2C clock
until that occurs.

Signed-off-by: Damien George <damien@micropython.org>
2025-08-01 23:03:17 +10:00
Damien George
a4ca42f094 stm32/i2cslave: Change irq handler name to i2c_slave_irq_handler.
Remove the "ev" part, so this handler can be generalised to also handle
error IRQs.

Signed-off-by: Damien George <damien@micropython.org>
2025-08-01 23:03:17 +10:00
Damien George
9b1778fc77 stm32/i2c: Move I2C IRQ handlers from stm32_it.c to i2c.c.
And add MP_STATIC_ASSERT to statically check that the IRQ names are correct
on the MCU that it's compiled for.

Signed-off-by: Damien George <damien@micropython.org>
2025-08-01 23:03:17 +10:00
Damien George
1b578fe2c0 extmod/machine_i2c_target: Add new machine.I2CTarget class.
This commit implements a generic I2C target/peripheral/"slave" device,
called `machine.I2CTarget`.  It can work in two separate modes:

- A general device with interrupts/events/callbacks for low-level I2C
  operations like address match, read request and stop.

- A memory device that allows reading/writing a specific region of memory
  (or "registers") on the target I2C device.

To make a memory device is very simple:

    from machine import I2CTarget

    mem = bytearray(8)
    i2c = I2CTarget(addr=67, mem=mem)

That's all that's needed to start the I2C target.  From then on it will
respond to any I2C controller on the bus, allowing reads and writes to the
mem bytearray.

It's also possible to register to receive events.  For example to be
notified when the memory is read/written:

    from machine import I2CTarget

    def irq_handler(i2c_target):
        flags = i2c_target.irq().flags()
        if flags & I2CTarget.IRQ_END_READ:
            print("controller read target at addr", i2c_target.memaddr)
        if flags & I2CTarget.IRQ_END_WRITE:
            print("controller wrote target at addr", i2c_target.memaddr)

    mem = bytearray(8)
    i2c = I2CTarget(addr=67, mem=mem)
    i2c.irq(irq_handler)

Instead of a memory device, an arbitrary I2C device can be implemented
using all the events (see docs).

This is based on the discussion in #3935.

Signed-off-by: Damien George <damien@micropython.org>
2025-08-01 23:03:17 +10:00
Koudai Aono
ab7c5a1733 docs/library/btree: Fix method links to explicitly specify class.
So they don't clash with other potential references.

Signed-off-by: Koudai Aono <koxudaxi@gmail.com>
2025-08-01 22:58:26 +10:00
root
769453c750 rp2/rp2_pio: Fix use of PIO2 in prog data structure.
The RP2350 PIO2 State Machines (8, 9, 10, 11) did not work.  The data
structure used to pass the PIO arguments was missing an entry for PIO2,
thus causing the PIO2 instances to write wrong data to wrong locations.

Fixes issue #17509.

Signed-off-by: Matt Westveld <github@intergalacticmicro.com>
2025-08-01 22:38:00 +10:00
Dryw Wade
c9b52b2b7f rp2/CMakeLists.txt: Fix flash size check logic.
Follow up to 6bfb83e30a, if the variable
`PICO_FLASH_SIZE_BYTES` is not a numeric constant, eg "(2 * 1024 * 1024)",
then it won't pass the GREATER check.  So change the if logic to just test
if it's defined.

Signed-off-by: Dryw Wade <dryw.wade@sparkfun.com>
2025-08-01 16:20:49 +10:00
Damien George
a9a606bf5d docs/library/rp2.StateMachine: Add a note about PIO in and jmp pins.
Signed-off-by: Damien George <damien@micropython.org>
2025-08-01 15:10:15 +10:00
Damien George
41987c6cf4 rp2/rp2_pio: Configure jmp_pin for PIO use if it's isolation is set.
Signed-off-by: Damien George <damien@micropython.org>
2025-08-01 15:10:15 +10:00
Jos Verlinde
dea949e860 tools/mpremote: Update ESPxxx detection for USB-CDC ports.
Detection of ESP-XX devices was based on just the names of the USB driver
name, and did not account for the switch of the newer ESP-xx devices to
USB-CDC.  On Windows this caused unwanted/unneeded resets as the DTR/RTS
signals are also used for automatic device reset over USB-CDC.  See
https://github.com/micropython/micropython/issues/9659#issuecomment-3124704572

This commit uses the Espressif registered VID 0x303A to detect USB-CDC
ports, to enable the same DTR/RTS settings as used on the UART-USB
connection.

Also improved the robustness of the code using `getattr()`.

Signed-off-by: Jos Verlinde <Jos_Verlinde@hotmail.com>
2025-08-01 14:58:20 +10:00
Jos Verlinde
4ba626ab5a tools/mpremote: Fix errno.ENOTBLK attribute error on Windows.
Not all errors defined in stdlib errno are available on Windows.
Specifically, errno.ENOTBLK is not.

Fixes issue #17773.

Signed-off-by: Jos Verlinde <Jos_Verlinde@hotmail.com>
2025-08-01 14:52:59 +10:00
Chris Webb
953da2080e tests/micropython: Test that viper offset stores don't clobber base reg.
When running the viper boundary tests, assert that the offset stores don't
clobber the base register, which is saved and temporarily modified on some
architectures.

Signed-off-by: Chris Webb <chris@arachsys.com>
2025-08-01 14:17:49 +10:00
Chris Webb
f39434e9fb py/asmthumb: Don't corrupt base register in large offset store.
asm_thumb_store_reg_reg_offset() modifies the base register when storing
with a large offset which triggers the generic path. If a variable lives
in that register, this corrupts it. Fix this by saving the base register
on the stack before modifying it.

Signed-off-by: Chris Webb <chris@arachsys.com>
2025-08-01 14:15:39 +10:00
Yoctopuce dev
69ead7d98e py/parse: Add support for math module constants and float folding.
Add a new MICROPY_COMP_CONST_FLOAT feature, enabled by in mpy-cross and
when compiling with MICROPY_CONFIG_ROM_LEVEL_CORE_FEATURES.  The new
feature leverages the code of MICROPY_COMP_CONST_FOLDING to support folding
of floating point constants.

If MICROPY_COMP_MODULE_CONST is defined as well, math module constants are
made available at compile time. For example:

    _DEG_TO_GRADIANT = const(math.pi / 180)
    _INVALID_VALUE = const(math.nan)

A few corner cases had to be handled:
- The float const folding code should not fold expressions resulting into
  complex results, as the mpy parser for complex immediates has
  limitations.
- The constant generation code must distinguish between -0.0 and 0.0, which
  are different even if C consider them as ==.

This change removes previous limitations on the use of `const()`
expressions that would result in floating point number, so the test cases
of micropython/const_error have to be updated.

Additional test cases have been added to cover the new repr() code (from a
previous commit).  A few other simple test cases have been added to handle
the use of floats in `const()` expressions, but the float folding code
itself is also tested when running general float test cases, as float
expressions often get resolved at compile-time (with this change).

Signed-off-by: Yoctopuce dev <dev@yoctopuce.com>
2025-08-01 13:35:44 +10:00
SiZiOUS
f67a370311 embed/port: Fix alloca include for Windows platforms.
When building the embedded port on MinGW-w64, I receive the following
error:

    fatal error: alloca.h: No such file or directory

MinGW-w64 (used on MSYS2) doesn't include `alloca.h`, but `alloca()` is
provided via `malloc.h` instead.  And this fix is also needed for other
Windows build systems.

Signed-off-by: SiZiOUS <sizious@gmail.com>
2025-08-01 12:01:37 +10:00
Damien George
f8f6d71940 nrf/drivers/bluetooth: Change soft-device download URL to self hosted.
The existing URLs have started to return a HTTP 403.  The simplest way
around this is to host the files at micropython.org, and point to them from
the download script.

The soft-device files have been retrieved from:
- https://www.nordicsemi.com/Products/Development-software/s110/download
- https://www.nordicsemi.com/Products/Development-software/s132/download
- https://www.nordicsemi.com/Products/Development-software/s140/download

Signed-off-by: Damien George <damien@micropython.org>
2025-08-01 11:34:42 +10:00
Damien George
947d5448b4 tests/cpydiff: Remove passing types_float_rounding test.
Since commit dbbaa959c8, this test now
produces the same output on MicroPython as CPython does, namely -1e+01.

Signed-off-by: Damien George <damien@micropython.org>
2025-08-01 11:13:12 +10:00
Damien George
97d56527a0 github/workflows: Build unix port for docs and run workflow more often.
The unix port is needed to build the docs, due to the cpydiff tests which
run both CPython and MicroPython (unix port).  That was previously not
failing the CI because the output from MicroPython was:

    /bin/sh: 1: ../ports/unix/build-standard/micropython: not found

which doesn't match the CPython output for any of the cpydiff tests, and so
it was considered a "pass" in terms of the output differing.

Also, run the docs workflow when py/ or tests/cpydiff/ changes, because the
cpydiff results may change when the core code changes.

Signed-off-by: Damien George <damien@micropython.org>
2025-08-01 11:04:01 +10:00
Yoctopuce dev
dbbaa959c8 py/formatfloat: Improve accuracy of float formatting code.
Following discussions in PR #16666, this commit updates the float
formatting code to improve the `repr` reversibility, i.e. the percentage of
valid floating point numbers that do parse back to the same number when
formatted by `repr` (in CPython it's 100%).

This new code offers a choice of 3 float conversion methods, depending on
the desired tradeoff between code size and conversion precision:

- BASIC method is the smallest code footprint

- APPROX method uses an iterative method to approximate the exact
  representation, which is a bit slower but but does not have a big impact
  on code size.  It provides `repr` reversibility on >99.8% of the cases in
  double precision, and on >98.5% in single precision (except with REPR_C,
  where reversibility is 100% as the last two bits are not taken into
  account).

- EXACT method uses higher-precision floats during conversion, which
  provides perfect results but has a higher impact on code size.  It is
  faster than APPROX method, and faster than the CPython equivalent
  implementation.  It is however not available on all compilers when using
  FLOAT_IMPL_DOUBLE.

Here is the table comparing the impact of the three conversion methods on
code footprint on PYBV10 (using single-precision floats) and reversibility
rate for both single-precision and double-precision floats.  The table
includes current situation as a baseline for the comparison:

              PYBV10  REPR_C   FLOAT  DOUBLE
    current = 364688   12.9%   27.6%   37.9%
    basic   = 364812   85.6%   60.5%   85.7%
    approx  = 365080  100.0%   98.5%   99.8%
    exact   = 366408  100.0%  100.0%  100.0%

Signed-off-by: Yoctopuce dev <dev@yoctopuce.com>
2025-08-01 00:47:33 +10:00
Yoctopuce dev
e4e1c9f413 py/parsenum: Refactor float parsing code.
This commit extracts from the current float parsing code two functions
which could be reused elsewhere in MicroPython.

The code used to multiply a float x by a power of 10 is also simplified by
applying the binary exponent separately from the power of 5.  This avoids
the risk of overflow in the intermediate stage, before multiplying by x.

Signed-off-by: Yoctopuce dev <dev@yoctopuce.com>
2025-08-01 00:47:33 +10:00
Damien George
ffa98cb014 webassembly/proxy_js: Reuse JsProxy ref if object matches.
This reduces memory use by reusing objects, and improves identity/equality
relationships of JavaScript objects on the Python side.

In 77bd8fe5b8 PyProxy's were reused when the
same Python object was proxied across to JavaScript.  This commit does the
same thing but for JsProxy's going from JS to Python.  If an existing
JsProxy reference exists for the JS object about to be proxied across, then
it's reused.

This helps reduce the number of alive objects (memory use), and, more
importantly, improves equality relationships of JavaScript objects on the
Python side.  Eg we now get, on the Python side:

    import js

    print(js.Object == js.Object)

that prints True.  Previously it was False.

Note that this change does not make identity work with `is`, for example
`js.Object is js.Object` is actually False.  With more work that could be
made True but for now we leave that as-is.

The behaviour with this commit matches Pyodide semantics.

Signed-off-by: Damien George <damien@micropython.org>
2025-07-31 11:40:50 +10:00
Damien George
813f0c1cb9 webassembly/objjsproxy: Implement equality for JsProxy objects.
Signed-off-by: Damien George <damien@micropython.org>
2025-07-31 11:40:03 +10:00
Damien George
241ee163c0 py/objboundmeth: Add option to use mp_is_equal instead of == comparison.
This option is needed for ports such as webassembly where objects are
proxied and can be identical without being the same C pointer.

Signed-off-by: Damien George <damien@micropython.org>
2025-07-31 11:38:35 +10:00
Angus Gratton
fdbd23268d tests/run-multitests.py: Escape encoding errors instead of crashing.
It's possible for a test to output non-ASCII characters (for example, due
to a hard fault or serial noise or memory corruption). Rather than crashing
the test runner, backslash escape those characters and treat them as
program output.

Refactors the string encoding step to a single helper to avoid copy-paste.

This work was funded through GitHub Sponsors.

Signed-off-by: Angus Gratton <angus@redyak.com.au>
2025-07-31 11:17:13 +10:00
Angus Gratton
4bdf2a2dc0 tests/multi_bluetooth: Extend the deep sleep test timeout.
As per comment, if a boot.py is present that connects to Wi-Fi then waking
can take a little longer.

This work was funded through GitHub Sponsors.

Signed-off-by: Angus Gratton <angus@redyak.com.au>
2025-07-31 11:15:03 +10:00
Damien George
ff6491ded0 tests/run-natmodtests.py: Automatically skip tests that are too large.
This follows a similar change made for `run-tests.py` in commit
229104558f.  The change here uses the same
logic to detect if a natmod test is too big for the target (eg overflows
(I)RAM loading the native .mpy), by printing "START TEST" at the start of
the test.

Typical output is now something like this:

    ...
    pass  extmod/random_basic.py
    pass  extmod/random_extra_float.py
    pass  extmod/random_extra.py
    SKIP  extmod/random_seed_default.py
    LRGE  extmod/re1.py
    SKIP  extmod/re_debug.py
    pass  extmod/re_error.py
    pass  extmod/re_group.py
    pass  extmod/re_groups.py
    ...

and the tests that are too large are reported at the end, and written to
the `_result.json` file.

Signed-off-by: Damien George <damien@micropython.org>
2025-07-31 11:07:50 +10:00
Damien George
a9b038a57e examples/bluetooth/ble_advertising.py: Fix decoding UUIDs.
The UUID32 case was incorrect: first, the "<d" should have been "<I", and
second, the UUID constructor treats integer arguments as UUID16.  So this
UUID32 case needs to pass in the actual data bytes.

And then it's simpler to just make all cases pass in the data bytes.

Signed-off-by: Damien George <damien@micropython.org>
2025-07-31 11:02:41 +10:00
Damien George
6a8c45b6c4 docs/library/bluetooth: Document all allowed args to UUID constructor.
Signed-off-by: Damien George <damien@micropython.org>
2025-07-31 11:02:41 +10:00
Damien George
4360da1684 zephyr/mpconfigport: Use MICROPY_CONFIG_ROM_LEVEL_BASIC_FEATURES.
This commit adjusts the configuration of the standard zephyr build to use
MICROPY_CONFIG_ROM_LEVEL_BASIC_FEATURES.  That's a lot cleaner than
explicitly enabling/disabling options, and allows boards to more easily
fine-tune the settings, eg select a different feature level.

Features that are now enabled are:
- async/await keyword support
- `filter`, `property` and `reversed` builtins
- `range` attributes
- `str.count()` method
- `array` module with `array.array` object
- `collections` module with `collections.namedtuple` object
- `struct` module with everything
- `id = const()` and constant folding in the compiler

Bulding qemu_cortex_m3, the code size was originally:

    Memory region         Used Size  Region Size  %age Used
               FLASH:      193864 B       256 KB     73.95%
                 RAM:       61992 B        64 KB     94.59%

and with this commit it is now:

    Memory region         Used Size  Region Size  %age Used
               FLASH:      200698 B       256 KB     76.56%
                 RAM:       61992 B        64 KB     94.59%

That's a mild increase of +6834 bytes flash usage for a good selection of
new features.

Signed-off-by: Damien George <damien@micropython.org>
2025-07-31 10:48:56 +10:00
Damien George
68434b4be7 zephyr/mpconfigport_minimal: Use MICROPY_CONFIG_ROM_LEVEL_MINIMUM.
This commit adjusts the configuration of the minimal zephyr build to use
MICROPY_CONFIG_ROM_LEVEL_MINIMUM.  That's a lot cleaner than explicitly
enabling/disabling options.

Prior to this change the minimal build for qemu_cortex_m3 had size:

    Memory region         Used Size  Region Size  %age Used
               FLASH:      114436 B       256 KB     43.65%
                 RAM:       26320 B        64 KB     40.16%

and had the following test results (running using the CI settings, ie
`-d basics float --exclude inf_nan_arith`):

    352 tests performed (7092 individual testcases)
    352 tests passed
    254 tests skipped: ...

With the changes here the qemu_cortex_m3 size is now:

    Memory region         Used Size  Region Size  %age Used
               FLASH:       99428 B       256 KB     37.93%
                 RAM:       26312 B        64 KB     40.15%

That's a good decrease of about 15k firmware size.  And the test suite
still passes with:

    342 tests performed (6776 individual testcases)
    341 tests passed
    265 tests skipped: ...

Signed-off-by: Damien George <damien@micropython.org>
2025-07-31 10:48:56 +10:00
Damien George
135c1cc7cd extmod/modtls_mbedtls: Do gc_collect and retry ssl_init on any error.
Contrary to the docs, mbedtls can return more than just
MBEDTLS_ERR_SSL_ALLOC_FAILED when `mbedtls_ssl_setup()` fails.  At least
MBEDTLS_ERR_MD_ALLOC_FAILED was also seen on ESP32_GENERIC, but there
could possibly be other error codes.

To cover all these codes, just check if `ret` is non-0, and in that case
do a `gc_collect()` and retry the init.

Signed-off-by: Damien George <damien@micropython.org>
2025-07-31 10:46:47 +10:00
Yanfeng Liu
ab4af2c1a6 py/mphal: Add stddef.h header for size_t.
This includes "stddef.h" for `size_t` to resolve NuttX integration build
issues.

Signed-off-by: Yanfeng Liu <yfliu2008@qq.com>
2025-07-30 11:47:43 +10:00
Angus Gratton
d4399b3230 esp32: Fix first line ESP32-C2 serial output after reset or deepsleep.
ESP32-C2 ROM prints at 74880bps (same as ESP8266), so need a newline
before first MicroPython output to avoid it being appended on end of
a line of noise.

This work was funded through GitHub Sponsors.

Signed-off-by: Angus Gratton <angus@redyak.com.au>
2025-07-30 10:51:29 +10:00
Angus Gratton
77c9eb7795 esp32: Add "Free RAM" optimisation config flags.
This is necessary for ESP32-C2 Wi-Fi & BT to work reliably (and for TLS to
work at all). On IDF 5.4.2 the free static RAM goes from 60KB to 100KB, and
there will also be a reduction in lwIP & Wi-Fi memory use at runtime.

The performance trade-off seems low for most use cases, although it will
probably be significant for certain combinations of load (i.e. heavy
TCP/IP, heavy BT throughput, and some peripheral driver functions).

Added as a set of config flags because this is potentially useful on other
SoCs where the goal is to maximise RAM available for MicroPython.

This work was funded through GitHub Sponsors.

Signed-off-by: Angus Gratton <angus@redyak.com.au>
2025-07-30 10:51:29 +10:00
TianShuang Ke
ca9916968c esp32: Add support for ESP32-C2 (aka ESP8684).
Includes:
esp32/esp32c2: Adapt to target chip ESP32C2.
esp32/esp32c2: Fix heap size is too small to enable Bluetooth.

Signed-off-by: TianShuangKe <qinyun575@gmail.com>
Signed-off-by: Angus Gratton <angus@redyak.com.au>
2025-07-30 10:51:29 +10:00
Anson Mansfield
88cb6bc818 tests/run-internalbench.py: Allow running internalbench on hardware.
Signed-off-by: Anson Mansfield <amansfield@mantaro.com>
2025-07-29 11:49:10 +10:00
Anson Mansfield
b9d6d6af4b tests/internal_bench/var: Benchmark descriptor access.
Signed-off-by: Anson Mansfield <amansfield@mantaro.com>
2025-07-29 10:08:53 +10:00
Anson Mansfield
c3e77ad6db tests/internal_bench/class_create: Benchmark class creation.
Signed-off-by: Anson Mansfield <amansfield@mantaro.com>
2025-07-29 10:08:53 +10:00
Anson Mansfield
d5dc554742 docs: Document PEP487 __set_name__ implementation.
Signed-off-by: Anson Mansfield <amansfield@mantaro.com>
2025-07-29 09:44:26 +10:00
Anson Mansfield
4412753f0c py/objtype: Add support for PEP487 __set_name__.
This commit adds support for the `__set_name__` data model method specified
by PEP487 - Simpler customisation of class creation.

This includes support for methods that mutate the owner class, and avoids
the naive modify-while-iterating hazard possible in a naive implementation
like micropython/micropython#15503.

Note that based on the benchmarks in micropython/micropython#16825, this is
also as fast or faster than the naive implementation, thanks to clever data
layout in `setname_list_t`, and the way this allows the capture step to run
during an existing loop through the class dict.

Other rejected approaches for dealing with the hazard include:

- python/cpython#72983
During the implementation of this feature for MicroPython, it was
discovered that some versions of CPython also have this naive hazard.
CPython resolved this bug in BPO-28797 and now makes a complete flat copy
of the class's dict to iterate.  This design decision doesn't make much
sense for a microcontroller though, even if it's perfectly reasonable in
the desktop world where memcpy might actually be cheaper than a
hard-to-branch-predict conditional; and it's also motivated in their case
by error-tracing considerations.

- micropython/micropython#16816
This is an equivalent implementation to CPython's approach that places this
copy directly on the stack; however it is both slower and has larger code
size than the approach taken here.

- micropython/micropython#15503
The simplest implementation is to just not worry about it and let the user
face the consequences if they mutate the owner class.  That's not a very
friendly behavior, though, and it's not actually much more performant than
this implementation on either time or code size.

- micropython/micropython#17693
Another alternative is to do the same as #15503 but leverage MicroPython's
existing `is_fixed` field in its dict type to convert attempted mutations
of the owner dict into `AttributeError`s.  This is safer than just leaving
the open hazard, but there's still important use-cases for owner-mutating
descriptors, and the performance gain is small enough that it isn't worth
missing support for those cases.

- combined micropython/micropython#17693 with this
Another version of this feature used a new feature define,
`MICROPY_PY_METACLASSES_LITE`, to control whether this algorithm or the
naive version is used.  This was rejected in favor of simplicity, based on
the very limited performance margin the naive version has (which in some
cases even goes _against_ it).

Signed-off-by: Anson Mansfield <amansfield@mantaro.com>
2025-07-29 09:41:24 +10:00
Anson Mansfield
82db5c81e0 tests/basics: Add tests for PEP487 __set_name__.
Including the stochastic tests needed to guarantee sensitivity to the
potential iterate-while-modifying hazard a naive implementation might have.

Signed-off-by: Anson Mansfield <amansfield@mantaro.com>
2025-07-29 09:41:10 +10:00
Yoctopuce dev
3a72f95919 py/objint_longlong: Fix longlong interoperability with floats.
Current longlong implementation does not allow a float as RHS of mathematic
operators, as it lacks the delegation code present in mpz.

Signed-off-by: Yoctopuce dev <dev@yoctopuce.com>
2025-07-29 01:14:35 +10:00
Yoctopuce dev
3c69277ba9 py/objint_longlong: Fix overflow check in mp_obj_int_get_checked.
This is to fix an outstanding TODO.  The test cases is using a range as
this will exist in all builds, but `mp_obj_get_int` is used in many
different parts of code where an overflow is more likely to occur.

Signed-off-by: Yoctopuce dev <dev@yoctopuce.com>
2025-07-29 00:27:01 +10:00
062e82a7cd py/objint_mpz: Fix pow3 where third argument is zero.
This finding is based on fuzzing MicroPython.  I manually minimized the
test case it provided.

Signed-off-by: Jeff Epler <jepler@gmail.com>
2025-07-28 23:58:46 +10:00
Christian Lang
ebc9525c95 rp2/modmachine: Do not use deprecated XOSC_MHZ and XOSC_KHZ.
XOSC_MHZ and XOSC_KHZ may not be defined if we use a custom XIN clock
by defining PLL_SYS_REFDIV etc. calculated by vcocalc.py.

Signed-off-by: Christian Lang <lang.chr86@gmail.com>
2025-07-25 11:25:24 +10:00
362 changed files with 9174 additions and 2244 deletions

View file

@ -7,7 +7,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Setup Biome
uses: biomejs/setup-biome@v2
with:

View file

@ -10,7 +10,7 @@ jobs:
code-formatting:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- uses: actions/setup-python@v5
- name: Install packages
run: source tools/ci.sh && ci_c_code_formatting_setup

View file

@ -25,7 +25,7 @@ jobs:
build:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
fetch-depth: 100
- name: Install packages

View file

@ -6,7 +6,7 @@ jobs:
codespell:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
# codespell version should be kept in sync with .pre-commit-config.yml
- run: pip install --user codespell==2.4.1 tomli
- run: codespell

View file

@ -10,7 +10,7 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
fetch-depth: 100
- uses: actions/setup-python@v5

View file

@ -5,6 +5,8 @@ on:
pull_request:
paths:
- docs/**
- py/**
- tests/cpydiff/**
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
@ -15,9 +17,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- uses: actions/setup-python@v5
- name: Install Python packages
run: pip install -r docs/requirements.txt
- name: Build unix port
run: source tools/ci.sh && ci_unix_build_helper
- name: Build docs
run: make -C docs/ html

View file

@ -18,7 +18,7 @@ jobs:
embedding:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Build
run: make -C examples/embedding -f micropython_embed.mk && make -C examples/embedding
- name: Run

View file

@ -11,7 +11,7 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
# Setting this to zero means fetch all history and tags,
# which hatch-vcs can use to discover the version tag.

View file

@ -17,7 +17,7 @@ jobs:
test:
runs-on: ubuntu-22.04 # use 22.04 to get python2
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Install packages
run: source tools/ci.sh && ci_mpy_format_setup
- name: Test mpy-tool.py

View file

@ -17,6 +17,6 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Build ports download metadata
run: mkdir boards && ./tools/autobuild/build-downloads.py . ./boards

View file

@ -26,7 +26,7 @@ jobs:
- alif_ae3_build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Install packages
run: source tools/ci.sh && ci_alif_setup
- name: Build ci_${{matrix.ci_func }}

View file

@ -21,7 +21,7 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Install packages
run: source tools/ci.sh && ci_cc3200_setup
- name: Build

View file

@ -25,9 +25,10 @@ jobs:
ci_func: # names are functions in ci.sh
- esp32_build_cmod_spiram_s2
- esp32_build_s3_c3
- esp32_build_c2_c6
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- id: idf_ver
name: Read the ESP-IDF version (including Python version)

View file

@ -21,7 +21,7 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Install packages
run: source tools/ci.sh && ci_esp8266_setup && ci_esp8266_path >> $GITHUB_PATH
- name: Build

View file

@ -24,7 +24,7 @@ jobs:
run:
working-directory: 'micropython repo' # test build with space in path
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
path: 'micropython repo'
- name: Install packages

View file

@ -21,7 +21,7 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Install packages
run: source tools/ci.sh && ci_nrf_setup
- name: Build

View file

@ -21,7 +21,7 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Install packages
run: source tools/ci.sh && ci_powerpc_setup
- name: Build

View file

@ -26,10 +26,11 @@ jobs:
ci_func: # names are functions in ci.sh
- bigendian
- sabrelite
- thumb
- thumb_softfp
- thumb_hardfp
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Install packages
run: source tools/ci.sh && ci_qemu_setup_arm
- name: Build and run test suite ci_qemu_build_arm_${{ matrix.ci_func }}
@ -41,7 +42,7 @@ jobs:
build_and_test_rv32:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Install packages
run: source tools/ci.sh && ci_qemu_setup_rv32
- name: Build and run test suite

View file

@ -21,7 +21,7 @@ jobs:
build_renesas_ra_board:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Install packages
run: source tools/ci.sh && ci_renesas_ra_setup
- name: Build

View file

@ -24,7 +24,7 @@ jobs:
run:
working-directory: 'micropython repo' # test build with space in path
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
path: 'micropython repo'
- name: Install packages

View file

@ -21,7 +21,7 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Install packages
run: source tools/ci.sh && ci_samd_setup
- name: Build

View file

@ -28,7 +28,7 @@ jobs:
- stm32_misc_build
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Install packages
run: source tools/ci.sh && ci_stm32_setup
- name: Build ci_${{matrix.ci_func }}

View file

@ -23,7 +23,7 @@ jobs:
minimal:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Build
run: source tools/ci.sh && ci_unix_minimal_build
- name: Run main test suite
@ -35,7 +35,7 @@ jobs:
reproducible:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Build with reproducible date
run: source tools/ci.sh && ci_unix_minimal_build
env:
@ -46,7 +46,7 @@ jobs:
standard:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Build
run: source tools/ci.sh && ci_unix_standard_build
- name: Run main test suite
@ -58,7 +58,7 @@ jobs:
standard_v2:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Build
run: source tools/ci.sh && ci_unix_standard_v2_build
- name: Run main test suite
@ -70,7 +70,7 @@ jobs:
coverage:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- uses: actions/setup-python@v5
# Python 3.12 is the default for ubuntu-24.04, but that has compatibility issues with settrace tests.
# Can remove this step when ubuntu-latest uses a more recent Python 3.x as the default.
@ -105,7 +105,7 @@ jobs:
coverage_32bit:
runs-on: ubuntu-22.04 # use 22.04 to get libffi-dev:i386
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Install packages
run: source tools/ci.sh && ci_unix_32bit_setup
- name: Build
@ -123,7 +123,7 @@ jobs:
nanbox:
runs-on: ubuntu-22.04 # use 22.04 to get python2, and libffi-dev:i386
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Install packages
run: source tools/ci.sh && ci_unix_32bit_setup
- name: Build
@ -137,7 +137,7 @@ jobs:
longlong:
runs-on: ubuntu-22.04 # use 22.04 to get python2, and libffi-dev:i386
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Install packages
run: source tools/ci.sh && ci_unix_32bit_setup
- name: Build
@ -151,7 +151,10 @@ jobs:
float:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v4
- name: Install packages
run: source tools/ci.sh && ci_unix_float_setup
- name: Build
run: source tools/ci.sh && ci_unix_float_build
- name: Run main test suite
@ -163,7 +166,7 @@ jobs:
gil_enabled:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Build
run: source tools/ci.sh && ci_unix_gil_enabled_build
- name: Run main test suite
@ -175,7 +178,7 @@ jobs:
stackless_clang:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Install packages
run: source tools/ci.sh && ci_unix_clang_setup
- name: Build
@ -189,7 +192,7 @@ jobs:
float_clang:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Install packages
run: source tools/ci.sh && ci_unix_clang_setup
- name: Build
@ -203,7 +206,7 @@ jobs:
settrace_stackless:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- uses: actions/setup-python@v5
# Python 3.12 is the default for ubuntu-24.04, but that has compatibility issues with settrace tests.
# Can remove this step when ubuntu-latest uses a more recent Python 3.x as the default.
@ -220,7 +223,7 @@ jobs:
macos:
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- uses: actions/setup-python@v5
with:
python-version: '3.8'
@ -236,7 +239,7 @@ jobs:
# ubuntu-22.04 is needed for older libffi.
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Install packages
run: source tools/ci.sh && ci_unix_qemu_mips_setup
- name: Build
@ -251,7 +254,7 @@ jobs:
# ubuntu-22.04 is needed for older libffi.
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Install packages
run: source tools/ci.sh && ci_unix_qemu_arm_setup
- name: Build
@ -266,7 +269,7 @@ jobs:
# ubuntu-22.04 is needed for older libffi.
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Install packages
run: source tools/ci.sh && ci_unix_qemu_riscv64_setup
- name: Build
@ -280,7 +283,7 @@ jobs:
sanitize_address:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- uses: actions/setup-python@v5
# Python 3.12 is the default for ubuntu-24.04, but that has compatibility issues with settrace tests.
# Can remove this step when ubuntu-latest uses a more recent Python 3.x as the default.
@ -305,7 +308,7 @@ jobs:
sanitize_undefined:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- uses: actions/setup-python@v5
# Python 3.12 is the default for ubuntu-24.04, but that has compatibility issues with settrace tests.
# Can remove this step when ubuntu-latest uses a more recent Python 3.x as the default.

View file

@ -11,6 +11,7 @@ on:
- 'shared/**'
- 'lib/**'
- 'ports/webassembly/**'
- 'tests/**'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
@ -20,7 +21,7 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Install packages
run: source tools/ci.sh && ci_webassembly_setup
- name: Build

View file

@ -58,7 +58,7 @@ jobs:
- uses: microsoft/setup-msbuild@v2
with:
vs-version: ${{ matrix.vs_version }}
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Build mpy-cross.exe
run: msbuild mpy-cross\mpy-cross.vcxproj -maxcpucount -property:Configuration=${{ matrix.configuration }} -property:Platform=${{ matrix.platform }}
- name: Update submodules
@ -125,7 +125,7 @@ jobs:
git
diffutils
path-type: inherit # Remove when setup-python is removed
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Build mpy-cross.exe
run: make -C mpy-cross -j2
- name: Update submodules
@ -141,10 +141,14 @@ jobs:
run: python run-tests.py --print-failures
cross-build-on-linux:
strategy:
fail-fast: false
matrix:
sys: [i686, x86_64]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Install packages
run: source tools/ci.sh && ci_windows_setup
- name: Build
run: source tools/ci.sh && ci_windows_build
run: source tools/ci.sh && ci_windows_build ${{ matrix.sys }}

View file

@ -11,6 +11,7 @@ on:
- 'shared/**'
- 'lib/**'
- 'ports/zephyr/**'
- 'tests/**'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
@ -29,7 +30,7 @@ jobs:
large-packages: false
docker-images: false
swap-storage: false
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- id: versions
name: Read Zephyr version
run: source tools/ci.sh && echo "ZEPHYR=$ZEPHYR_VERSION" | tee "$GITHUB_OUTPUT"

View file

@ -6,7 +6,7 @@ jobs:
ruff:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
# ruff version should be kept in sync with .pre-commit-config.yaml & also micropython-lib
- run: pipx install ruff==0.11.6
- run: ruff check --output-format=github .

2
.gitmodules vendored
View file

@ -35,7 +35,7 @@
url = https://github.com/bluekitchen/btstack.git
[submodule "lib/nxp_driver"]
path = lib/nxp_driver
url = https://github.com/hathach/nxp_driver.git
url = https://github.com/micropython/nxp_driver.git
[submodule "lib/libhydrogen"]
path = lib/libhydrogen
url = https://github.com/jedisct1/libhydrogen.git

View file

@ -234,6 +234,24 @@ You can also specify which board to use:
See `ports/stm32/boards <https://github.com/micropython/micropython/tree/master/ports/stm32/boards>`_
for the available boards. e.g. "PYBV11" or "NUCLEO_WB55".
Compile-time format string checking
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
When gcc is used to build MicroPython, a plugin can be used for compile-time
checking of ``mp_printf`` format strings. The plugin is enabled by setting the
Makefile variable ``MICROPY_USE_COMPILER_PLUGIN=gcc`` before including
``py/mkrules.mk``.
The plugin doesn't work:
* With non-gcc compilers (including clang, which is sometimes installed on
Macs with the name "gcc")
* On Windows systems, where the steps for building a plugin are more complicated
* With MicroPython builds that use cmake rather than traditional make.
* If the necessary files for plugin development are not installed. In Debian
bookworm, for instance, the files to build plugins for ``gcc`` are in
``gcc-12-plugin-dev``, and the files to build plugins for
``riscv64-unknown-elf-gcc`` are in ``gcc-12-plugin-dev-riscv64-linux-gnu``.
Building the documentation
--------------------------

View file

@ -25,7 +25,8 @@ Python 3.6 beta 1 was released on 12 Sep 2016, and a summary of the new features
+--------------------------------------------------------+--------------------------------------------------+-----------------+
| `PEP 468 <https://www.python.org/dev/peps/pep-0468/>`_ | Preserving the order of *kwargs* in a function | |
+--------------------------------------------------------+--------------------------------------------------+-----------------+
| `PEP 487 <https://www.python.org/dev/peps/pep-0487/>`_ | Simpler customization of class creation | |
| `PEP 487 <https://www.python.org/dev/peps/pep-0487/>`_ | Simpler customization of class creation | Partial |
| | | [#setname]_ |
+--------------------------------------------------------+--------------------------------------------------+-----------------+
| `PEP 520 <https://www.python.org/dev/peps/pep-0520/>`_ | Preserving Class Attribute Definition Order | |
+--------------------------------------------------------+--------------------------------------------------+-----------------+
@ -198,3 +199,7 @@ Changes to built-in modules:
+--------------------------------------------------------------------------------------------------------------+----------------+
| The *compress()* and *decompress()* functions now accept keyword arguments | |
+--------------------------------------------------------------------------------------------------------------+----------------+
.. rubric:: Notes
.. [#setname] Currently, only :func:`__set_name__` is implemented.

View file

@ -566,6 +566,42 @@ ESP32 S2:
Provided to deinit the adc driver.
Pulse Counter (pin pulse/edge counting)
---------------------------------------
The ESP32 provides up to 8 pulse counter peripherals depending on the hardware,
with id 0..7. These can be configured to count rising and/or falling edges on
any input pin.
Use the :ref:`esp32.PCNT <esp32.PCNT>` class::
from machine import Pin
from esp32 import PCNT
counter = PCNT(0, pin=Pin(2), rising=PCNT.INCREMENT) # create counter
counter.start() # start counter
count = counter.value() # read count, -32768..32767
counter.value(0) # reset counter
count = counter.value(0) # read and reset
The PCNT hardware supports monitoring multiple pins in a single unit to
implement quadrature decoding or up/down signal counters.
See the :ref:`machine.Counter <machine.Counter>` and
:ref:`machine.Encoder <machine.Encoder>` classes for simpler abstractions of
common pulse counting applications::
from machine import Pin, Counter
counter = Counter(0, Pin(2)) # create a counter as above and start it
count = counter.value() # read the count as an arbitrary precision signed integer
encoder = Encoder(0, Pin(12), Pin(14)) # create an encoder and begin counting
count = encoder.value() # read the count as an arbitrary precision signed integer
Note that the id passed to these ``Counter()`` and ``Encoder()`` objects must be
a PCNT id.
Software SPI bus
----------------
@ -802,6 +838,9 @@ The RMT is ESP32-specific and allows generation of accurate digital pulses with
# The channel resolution is 100ns (1/(source_freq/clock_div)).
r.write_pulses((1, 20, 2, 40), 0) # Send 0 for 100ns, 1 for 2000ns, 0 for 200ns, 1 for 4000ns
The ESP32-C2 family does not include any RMT peripheral, so this class is
unavailable on those SoCs.
OneWire driver
--------------

View file

@ -764,4 +764,5 @@ Constructor
The **value** can be either:
- A 16-bit integer. e.g. ``0x2908``.
- An object with the buffer protocol and that is 2, 4 or 16 bytes long, e.g. ``b'\x08\x29'``.
- A 128-bit UUID string. e.g. ``'6E400001-B5A3-F393-E0A9-E50E24DCCA9E'``.

View file

@ -151,10 +151,10 @@ Constants
.. data:: INCL
A flag for `keys()`, `values()`, `items()` methods to specify that
A flag for :meth:`btree.keys`, :meth:`btree.values`, :meth:`btree.items` methods to specify that
scanning should be inclusive of the end key.
.. data:: DESC
A flag for `keys()`, `values()`, `items()` methods to specify that
A flag for :meth:`btree.keys`, :meth:`btree.values`, :meth:`btree.items` methods to specify that
scanning should be in descending direction of keys.

View file

@ -195,6 +195,151 @@ Constants
Used in `idf_heap_info`.
.. _esp32.PCNT:
PCNT
----
This class provides access to the ESP32 hardware support for pulse counting.
There are 8 pulse counter units, with id 0..7.
See the :ref:`machine.Counter <machine.Counter>` and
:ref:`machine.Encoder <machine.Encoder>` classes for simpler and portable
abstractions of common pulse counting applications. These classes are
implemented as thin Python shims around :class:`PCNT`.
.. class:: PCNT(id, *, ...)
Returns the singleton PCNT instance for the given unit ``id``.
Keyword arguments are passed to the ``init()`` method as described
below.
.. method:: PCNT.init(*, ...)
(Re-)initialise a pulse counter unit. Supported keyword arguments are:
- ``channel``: see description below
- ``pin``: the input Pin to monitor for pulses
- ``rising``: an action to take on a rising edge - one of
``PCNT.INCREMENT``, ``PCNT.DECREMENT`` or ``PCNT.IGNORE`` (the default)
- ``falling``: an action to take on a falling edge (takes the save values
as the ``rising`` argument).
- ``mode_pin``: ESP32 pulse counters support monitoring a second pin and
altering the behaviour of the counter based on its level - set this
keyword to any input Pin
- ``mode_low``: set to either ``PCNT.HOLD`` or ``PCNT.REVERSE`` to
either suspend counting or reverse the direction of the counter (i.e.,
``PCNT.INCREMENT`` behaves as ``PCNT.DECREMENT`` and vice versa)
when ``mode_pin`` is low
- ``mode_high``: as ``mode_low`` but for the behaviour when ``mode_pin``
is high
- ``filter``: set to a value 1..1023, in ticks of the 80MHz clock, to
enable the pulse width filter
- ``min``: set to the minimum level of the counter value when
decrementing (-32768..-1) or 0 to disable
- ``max``: set to the maximum level of the counter value when
incrementing (1..32767) or 0 to disable
- ``threshold0``: sets the counter value for the
``PCNT.IRQ_THRESHOLD0`` event (see ``irq`` method)
- ``threshold1``: sets the counter value for the
``PCNT.IRQ_THRESHOLD1`` event (see ``irq`` method)
- ``value``: can be set to ``0`` to reset the counter value
The hardware initialisation is done in stages and so some of the keyword
arguments can be used in groups or in isolation to partially reconfigure a
unit:
- the ``pin`` keyword (optionally combined with ``mode_pin``) can be used
to change just the bound pin(s)
- ``rising``, ``falling``, ``mode_low`` and ``mode_high`` can be used
(singly or together) to change the counting logic - omitted keywords
use their default (``PCNT.IGNORE`` or ``PCNT.NORMAL``)
- ``filter`` can be used to change only the pulse width filter (with 0
disabling it)
- each of ``min``, ``max``, ``threshold0`` and ``threshold1`` can
be used to change these limit/event values individually; however,
setting any will reset the counter to zero (i.e., they imply
``value=0``)
Each pulse counter unit supports two channels, 0 and 1, each able to
monitor different pins with different counting logic but updating the same
counter value. Use ``channel=1`` with the ``pin``, ``rising``, ``falling``,
``mode_pin``, ``mode_low`` and ``mode_high`` keywords to configure the
second channel.
The second channel can be used to configure 4X quadrature decoding with a
single counter unit::
pin_a = Pin(2, Pin.INPUT, pull=Pin.PULL_UP)
pin_b = Pin(3, Pin.INPUT, pull=Pin.PULL_UP)
rotary = PCNT(0, min=-32000, max=32000)
rotary.init(channel=0, pin=pin_a, falling=PCNT.INCREMENT, rising=PCNT.DECREMENT, mode_pin=pin_b, mode_low=PCNT.REVERSE)
rotary.init(channel=1, pin=pin_b, falling=PCNT.DECREMENT, rising=PCNT.INCREMENT, mode_pin=pin_a, mode_low=PCNT.REVERSE)
rotary.start()
.. method:: PCNT.value([value])
Call this method with no arguments to return the current counter value.
If the optional *value* argument is set to ``0`` then the counter is
reset (but the previous value is returned). Read and reset is not atomic and
so it is possible for a pulse to be missed. Any value other than ``0`` will
raise an error.
.. method:: PCNT.irq(handler=None, trigger=PCNT.IRQ_ZERO)
ESP32 pulse counters support interrupts on these counter events:
- ``PCNT.IRQ_ZERO``: the counter has reset to zero
- ``PCNT.IRQ_MIN``: the counter has hit the ``min`` value
- ``PCNT.IRQ_MAX``: the counter has hit the ``max`` value
- ``PCNT.IRQ_THRESHOLD0``: the counter has hit the ``threshold0`` value
- ``PCNT.IRQ_THRESHOLD1``: the counter has hit the ``threshold1`` value
``trigger`` should be a bit-mask of the desired events OR'ed together. The
``handler`` function should take a single argument which is the
:class:`PCNT` instance that raised the event.
This method returns a callback object. The callback object can be used to
access the bit-mask of events that are outstanding on the PCNT unit.::
def pcnt_irq(pcnt):
flags = pcnt.irq().flags()
if flags & PCNT.IRQ_ZERO:
# reset
if flags & PCNT.IRQ_MAX:
# overflow...
... etc
pcnt.irq(handler=pcnt_irq, trigger=PCNT.IRQ_ZERO | PCNT.IRQ_MAX | ...)
**Note:** Accessing ``irq.flags()`` will clear the flags, so only call it
once per invocation of the handler.
The handler is called with the MicroPython scheduler and so will run at a
point after the interrupt. If another interrupt occurs before the handler
has been called then the events will be coalesced together into a single
call and the bit mask will indicate all events that have occurred.
To avoid race conditions between a handler being called and retrieving the
current counter value, the ``value()`` method will force execution of any
pending events before returning the current counter value (and potentially
resetting the value).
Only one handler can be in place per-unit. Set ``handler`` to ``None`` to
disable the event interrupt.
.. Note::
ESP32 pulse counters reset to *zero* when reaching the minimum or maximum
value. Thus the ``IRQ_ZERO`` event will also trigger when either of these
events occurs.
See the :ref:`machine.Counter <machine.Counter>` and
:ref:`machine.Encoder <machine.Encoder>` classes for simpler abstractions of
common pulse counting applications.
.. _esp32.RMT:
RMT

View file

@ -0,0 +1,93 @@
.. currentmodule:: machine
.. _machine.Counter:
class Counter -- pulse counter
==============================
Counter implements pulse counting by monitoring an input signal and counting
rising or falling edges.
Minimal example usage::
from machine import Pin, Counter
counter = Counter(0, Pin(0, Pin.IN)) # create Counter for pin 0 and begin counting
value = counter.value() # retrieve current pulse count
Availability: **ESP32**
Constructors
------------
.. class:: Counter(id, ...)
Returns the singleton Counter object for the the given *id*. Values of *id*
depend on a particular port and its hardware. Values 0, 1, etc. are commonly
used to select hardware block #0, #1, etc.
Additional arguments are passed to the :meth:`init` method described below,
and will cause the Counter instance to be re-initialised and reset.
On ESP32, the *id* corresponds to a :ref:`PCNT unit <esp32.PCNT>`.
Methods
-------
.. method:: Counter.init(src, *, ...)
Initialise and reset the Counter with the given parameters:
- *src* specifies the input pin as a :ref:`machine.Pin <machine.Pin>` object.
May be omitted on ports that have a predefined pin for a given hardware
block.
Additional keyword-only parameters that may be supported by a port are:
- *edge* specifies the edge to count. Either ``Counter.RISING`` (the default)
or ``Counter.FALLING``. *(Supported on ESP32)*
- *direction* specifies the direction to count. Either ``Counter.UP`` (the
default) or ``Counter.DOWN``. *(Supported on ESP32)*
- *filter_ns* specifies a minimum period of time in nanoseconds that the
source signal needs to be stable for a pulse to be counted. Implementations
should use the longest filter supported by the hardware that is less than
or equal to this value. The default is 0 (no filter). *(Supported on ESP32)*
.. method:: Counter.deinit()
Stops the Counter, disabling any interrupts and releasing hardware resources.
A Soft Reset should deinitialize all Counter objects.
.. method:: Counter.value([value])
Get, and optionally set, the counter value as a signed integer.
Implementations must aim to do the get and set atomically (i.e. without
leading to skipped counts).
This counter value could exceed the range of a :term:`small integer`, which
means that calling :meth:`Counter.value` could cause a heap allocation, but
implementations should aim to ensure that internal state only uses small
integers and therefore will not allocate until the user calls
:meth:`Counter.value`.
For example, on ESP32, the internal state counts overflows of the hardware
counter (every 32000 counts), which means that it will not exceed the small
integer range until ``2**30 * 32000`` counts (slightly over 1 year at 1MHz).
In general, it is recommended that you should use ``Counter.value(0)`` to reset
the counter (i.e. to measure the counts since the last call), and this will
avoid this problem.
Constants
---------
.. data:: Counter.RISING
Counter.FALLING
Select the pulse edge.
.. data:: Counter.UP
Counter.DOWN
Select the counting direction.

View file

@ -0,0 +1,72 @@
.. currentmodule:: machine
.. _machine.Encoder:
class Encoder -- quadrature decoding
====================================
Encoder implements decoding of quadrature signals as commonly output from
rotary encoders, by counting either up or down depending on the order of two
input pulses.
Minimal example usage::
from machine import Pin, Encoder
counter = Counter(0, Pin(0, Pin.IN), Pin(1, Pin.IN)) # create Encoder for pins 0, 1 and begin counting
value = counter.value() # retrieve current count
Availability: **ESP32**
Constructors
------------
.. class:: Encoder(id, ...)
Returns the singleton Encoder object for the the given *id*. Values of *id*
depend on a particular port and its hardware. Values 0, 1, etc. are commonly
used to select hardware block #0, #1, etc.
Additional arguments are passed to the :meth:`init` method described below,
and will cause the Encoder instance to be re-initialised and reset.
On ESP32, the *id* corresponds to a :ref:`PCNT unit <esp32.PCNT>`.
Methods
-------
.. method:: Encoder.init(phase_a, phase_b, *, ...)
Initialise and reset the Encoder with the given parameters:
- *phase_a* specifies the first input pin as a
:ref:`machine.Pin <machine.Pin>` object.
- *phase_b* specifies the second input pin as a
:ref:`machine.Pin <machine.Pin>` object.
These pins may be omitted on ports that have predefined pins for a given
hardware block.
Additional keyword-only parameters that may be supported by a port are:
- *filter_ns* specifies a minimum period of time in nanoseconds that the
source signal needs to be stable for a pulse to be counted. Implementations
should use the longest filter supported by the hardware that is less than
or equal to this value. The default is 0 (no filter). *(Supported on ESP32)*
- *phases* specifies the number of signal edges to count and thus the
granularity of the decoding. e.g. 4 phases corresponds to "4x quadrature
decoding", and will result in four counts per pulse. Ports may support
either 1, 2, or 4 phases and the default is 1 phase. *(Supported on ESP32)*
.. method:: Encoder.deinit()
Stops the Encoder, disabling any interrupts and releasing hardware resources.
A Soft Reset should deinitialize all Encoder objects.
.. method:: Encoder.value([value])
Get, and optionally set, the encoder value as a signed integer.
Implementations should aim to do the get and set atomically.
See :meth:`machine.Counter.value` for details about overflow of this value.

View file

@ -0,0 +1,174 @@
.. currentmodule:: machine
.. _machine.I2CTarget:
class I2CTarget -- an I2C target device
=======================================
An I2C target is a device which connects to an I2C bus and is controlled by an
I2C controller. I2C targets can take many forms. The :class:`machine.I2CTarget`
class implements an I2C target that can be configured as a memory/register device,
or as an arbitrary I2C device by using callbacks (if supported by the port).
Example usage for the case of a memory device::
from machine import I2CTarget
# Create the backing memory for the I2C target.
mem = bytearray(8)
# Create an I2C target. Depending on the port, extra parameters
# may be required to select the peripheral and/or pins to use.
i2c = I2CTarget(addr=67, mem=mem)
# At this point an I2C controller can read and write `mem`.
...
# Deinitialise the I2C target.
i2c.deinit()
Note that some ports require an ``id``, and maybe ``scl`` and ``sda`` pins, to be
passed to the `I2CTarget` constructor, to select the hardware I2C instance and
pins that it connects to.
When configured as a memory device, it's also possible to register to receive events.
For example to be notified when the memory is read/written::
from machine import I2CTarget
# Define an IRQ handler, for I2C events.
def irq_handler(i2c_target):
flags = i2c_target.irq().flags()
if flags & I2CTarget.IRQ_END_READ:
print("controller read target at addr", i2c_target.memaddr)
if flags & I2CTarget.IRQ_END_WRITE:
print("controller wrote target at addr", i2c_target.memaddr)
# Create the I2C target and register to receive default events.
mem = bytearray(8)
i2c = I2CTarget(addr=67, mem=mem)
i2c.irq(irq_handler)
More complicated I2C devices can be implemented using the full set of events. For
example, to see the raw events as they are triggered::
from machine import I2CTarget
# Define an IRQ handler that prints the event id and responds to reads/writes.
def irq_handler(i2c_target, buf=bytearray(1)):
flags = i2c_target.irq().flags()
print(flags)
if flags & I2CTarget.IRQ_READ_REQ:
i2c_target.write(buf)
if flags & I2CTarget.IRQ_WRITE_REQ:
i2c_target.readinto(buf)
# Create the I2C target and register to receive all events.
i2c = I2CTarget(addr=67)
all_triggers = (
I2CTarget.IRQ_ADDR_MATCH_READ
| I2CTarget.IRQ_ADDR_MATCH_WRITE
| I2CTarget.IRQ_READ_REQ
| I2CTarget.IRQ_WRITE_REQ
| I2CTarget.IRQ_END_READ
| I2CTarget.IRQ_END_WRITE
)
i2c.irq(irq_handler, trigger=all_triggers, hard=True)
Constructors
------------
.. class:: I2CTarget(id, addr, *, addrsize=7, mem=None, mem_addrsize=8, scl=None, sda=None)
Construct and return a new I2CTarget object using the following parameters:
- *id* identifies a particular I2C peripheral. Allowed values depend on the
particular port/board. Some ports have a default in which case this parameter
can be omitted.
- *addr* is the I2C address of the target.
- *addrsize* is the number of bits in the I2C target address. Valid values
are 7 and 10.
- *mem* is an object with the buffer protocol that is writable. If not
specified then there is no backing memory and data must be read/written
using the :meth:`I2CTarget.readinto` and :meth:`I2CTarget.write` methods.
- *mem_addrsize* is the number of bits in the memory address. Valid values
are 0, 8, 16, 24 and 32.
- *scl* is a pin object specifying the pin to use for SCL.
- *sda* is a pin object specifying the pin to use for SDA.
Note that some ports/boards will have default values of *scl* and *sda*
that can be changed in this constructor. Others will have fixed values
of *scl* and *sda* that cannot be changed.
General Methods
---------------
.. method:: I2CTarget.deinit()
Deinitialise the I2C target. After this method is called the hardware will no
longer respond to requests on the I2C bus, and no other methods can be called.
.. method:: I2CTarget.readinto(buf)
Read into the given buffer any pending bytes written by the I2C controller.
Returns the number of bytes read.
.. method:: I2CTarget.write(buf)
Write out the bytes from the given buffer, to be passed to the I2C controller
after it sends a read request. Returns the number of bytes written. Most ports
only accept one byte at a time to this method.
.. method:: I2CTarget.irq(handler=None, trigger=IRQ_END_READ|IRQ_END_WRITE, hard=False)
Configure an IRQ *handler* to be called when an event occurs. The possible events are
given by the following constants, which can be or'd together and passed to the *trigger*
argument:
- ``IRQ_ADDR_MATCH_READ`` indicates that the target was addressed by a
controller for a read transaction.
- ``IRQ_ADDR_MATCH_READ`` indicates that the target was addressed by a
controller for a write transaction.
- ``IRQ_READ_REQ`` indicates that the controller is requesting data, and this
request must be satisfied by calling `I2CTarget.write` with the data to be
passed back to the controller.
- ``IRQ_WRITE_REQ`` indicates that the controller has written data, and the
data must be read by calling `I2CTarget.readinto`.
- ``IRQ_END_READ`` indicates that the controller has finished a read transaction.
- ``IRQ_END_WRITE`` indicates that the controller has finished a write transaction.
Not all triggers are available on all ports. If a port has the constant then that
event is available.
Note the following restrictions:
- ``IRQ_ADDR_MATCH_READ``, ``IRQ_ADDR_MATCH_READ``, ``IRQ_READ_REQ`` and
``IRQ_WRITE_REQ`` must be handled by a hard IRQ callback (with the *hard* argument
set to ``True``). This is because these events have very strict timing requirements
and must usually be satisfied synchronously with the hardware event.
- ``IRQ_END_READ`` and ``IRQ_END_WRITE`` may be handled by either a soft or hard
IRQ callback (although note that all events must be registered with the same handler,
so if any events need a hard callback then all events must be hard).
- If a memory buffer has been supplied in the constructor then ``IRQ_END_WRITE``
is not emitted for the transaction that writes the memory address. This is to
allow ``IRQ_END_READ`` and ``IRQ_END_WRITE`` to function correctly as soft IRQ
callbacks, where the IRQ handler may be called quite some time after the actual
hardware event.
.. attribute:: I2CTarget.memaddr
The integer value of the most recent memory address that was selected by the I2C
controller (only valid if ``mem`` was specified in the constructor).
Constants
---------
.. data:: I2CTarget.IRQ_ADDR_MATCH_READ
I2CTarget.IRQ_ADDR_MATCH_WRITE
I2CTarget.IRQ_READ_REQ
I2CTarget.IRQ_WRITE_REQ
I2CTarget.IRQ_END_READ
I2CTarget.IRQ_END_WRITE
IRQ trigger sources.

View file

@ -264,9 +264,12 @@ Classes
machine.UART.rst
machine.SPI.rst
machine.I2C.rst
machine.I2CTarget.rst
machine.I2S.rst
machine.RTC.rst
machine.Timer.rst
machine.Counter.rst
machine.Encoder.rst
machine.WDT.rst
machine.SD.rst
machine.SDCard.rst

View file

@ -154,8 +154,8 @@ Regex objects
Compiled regular expression. Instances of this class are created using
`re.compile()`.
.. method:: regex.match(string)
regex.search(string)
.. method:: regex.match(string, [pos, [endpos]])
regex.search(string, [pos, [endpos]])
regex.sub(replace, string, count=0, flags=0, /)
Similar to the module-level functions :meth:`match`, :meth:`search`
@ -163,6 +163,16 @@ Compiled regular expression. Instances of this class are created using
Using methods is (much) more efficient if the same regex is applied to
multiple strings.
The optional second parameter *pos* gives an index in the string where the
search is to start; it defaults to ``0``. This is not completely equivalent
to slicing the string; the ``'^'`` pattern character matches at the real
beginning of the string and at positions just after a newline, but not
necessarily at the index where the search is to start.
The optional parameter *endpos* limits how far the string will be searched;
it will be as if the string is *endpos* characters long, so only the
characters from *pos* to ``endpos - 1`` will be searched for a match.
.. method:: regex.split(string, max_split=-1, /)
Split a *string* using regex. If *max_split* is given, it specifies

View file

@ -58,6 +58,11 @@ Methods
- *pull_thresh* is the threshold in bits before auto-pull or conditional
re-pulling is triggered.
Note: pins used for *in_base* need to be configured manually for input (or
otherwise) so that the PIO can see the desired signal (they could be input
pins, output pins, or connected to a different peripheral). The *jmp_pin*
can also be configured manually, but by default will be an input pin.
.. method:: StateMachine.active([value])
Gets or sets whether the state machine is currently running.

View file

@ -188,6 +188,13 @@ Glossary
Most MicroPython boards make a REPL available over a UART, and this is
typically accessible on a host PC via USB.
small integer
MicroPython optimises the internal representation of integers such that
"small" values do not take up space on the heap, and calculations with
them do not require heap allocation. On most 32-bit ports, this
corresponds to values in the interval ``-2**30 <= x < 2**30``, but this
should be considered an implementation detail and not relied upon.
stream
Also known as a "file-like object". A Python object which provides
sequential read-write access to the underlying data. A stream object

View file

@ -108,7 +108,7 @@ The full list of supported commands are:
**Note:** Instead of using the ``connect`` command, there are several
:ref:`pre-defined shortcuts <mpremote_shortcuts>` for common device paths. For
example the ``a0`` shortcut command is equivalent to
``connect /dev/ttyACM0`` (Linux), or ``c0`` for ``COM0`` (Windows).
``connect /dev/ttyACM0`` (Linux), or ``c1`` for ``COM1`` (Windows).
**Note:** The ``auto`` option will only detect USB serial ports, i.e. a serial
port that has an associated USB VID/PID (i.e. CDC/ACM or FTDI-style
@ -487,9 +487,16 @@ Shortcuts can be defined using the macro system. Built-in shortcuts are:
- ``cat``, ``edit``, ``ls``, ``cp``, ``rm``, ``mkdir``, ``rmdir``, ``touch``: Aliases for ``fs <sub-command>``
Additional shortcuts can be defined by in user-configuration files, which is
located at ``.config/mpremote/config.py``. This file should define a
dictionary named ``commands``. The keys of this dictionary are the shortcuts
Additional shortcuts can be defined in the user configuration file ``mpremote/config.py``,
located in the User Configuration Directory.
The correct location for each OS is determined using the ``platformdirs`` module.
This is typically:
- ``$XDG_CONFIG_HOME/mpremote/config.py``
- ``$HOME/.config/mpremote/config.py``
- ``$env:LOCALAPPDATA/mpremote/config.py``
The ``config.py``` file should define a dictionary named ``commands``. The keys of this dictionary are the shortcuts
and the values are either a string or a list-of-strings:
.. code-block:: python3

View file

@ -31,8 +31,8 @@ With your serial program open (PuTTY, screen, picocom, etc) you may see a
blank screen with a flashing cursor. Press Enter (or reset the board) and
you should be presented with the following text::
*** Booting Zephyr OS build v4.0.0 ***
MicroPython v1.24.0-preview.179.g5b85b24bd on 2024-08-05; zephyr-frdm_k64f with mk64f12
*** Booting Zephyr OS build v4.2.0 ***
MicroPython v1.26.0-preview.451.gebc9525c9 on 2025-07-25; zephyr-frdm_k64f with mk64f12
Type "help()" for more information.
>>>

View file

@ -79,12 +79,9 @@ def decode_name(payload):
def decode_services(payload):
services = []
for u in decode_field(payload, _ADV_TYPE_UUID16_COMPLETE):
services.append(bluetooth.UUID(struct.unpack("<h", u)[0]))
for u in decode_field(payload, _ADV_TYPE_UUID32_COMPLETE):
services.append(bluetooth.UUID(struct.unpack("<d", u)[0]))
for u in decode_field(payload, _ADV_TYPE_UUID128_COMPLETE):
services.append(bluetooth.UUID(u))
for code in (_ADV_TYPE_UUID16_COMPLETE, _ADV_TYPE_UUID32_COMPLETE, _ADV_TYPE_UUID128_COMPLETE):
for u in decode_field(payload, code):
services.append(bluetooth.UUID(u))
return services

View file

@ -13,3 +13,4 @@
#define MICROPY_ENABLE_COMPILER (1)
#define MICROPY_ENABLE_GC (1)
#define MICROPY_PY_GC (1)
#define MICROPY_PY_SYS (0)

View file

@ -11,6 +11,7 @@ set(MICROPY_SOURCE_EXTMOD
${MICROPY_EXTMOD_DIR}/machine_adc_block.c
${MICROPY_EXTMOD_DIR}/machine_bitstream.c
${MICROPY_EXTMOD_DIR}/machine_i2c.c
${MICROPY_EXTMOD_DIR}/machine_i2c_target.c
${MICROPY_EXTMOD_DIR}/machine_i2s.c
${MICROPY_EXTMOD_DIR}/machine_mem.c
${MICROPY_EXTMOD_DIR}/machine_pulse.c

View file

@ -6,6 +6,7 @@ SRC_EXTMOD_C += \
extmod/machine_adc_block.c \
extmod/machine_bitstream.c \
extmod/machine_i2c.c \
extmod/machine_i2c_target.c \
extmod/machine_i2s.c \
extmod/machine_mem.c \
extmod/machine_pinbase.c \

View file

@ -1,41 +0,0 @@
#ifndef MICROPY_INCLUDED_EXTMOD_LWIP_INCLUDE_ARCH_CC_H
#define MICROPY_INCLUDED_EXTMOD_LWIP_INCLUDE_ARCH_CC_H
#include <stdint.h>
// Generate lwip's internal types from stdint
typedef uint8_t u8_t;
typedef int8_t s8_t;
typedef uint16_t u16_t;
typedef int16_t s16_t;
typedef uint32_t u32_t;
typedef int32_t s32_t;
typedef u32_t mem_ptr_t;
#define U16_F "hu"
#define S16_F "hd"
#define X16_F "hx"
#define U32_F "u"
#define S32_F "d"
#define X32_F "x"
#define X8_F "02x"
#define SZT_F "u"
#define BYTE_ORDER LITTLE_ENDIAN
#define LWIP_CHKSUM_ALGORITHM 2
#include <assert.h>
#define LWIP_PLATFORM_DIAG(x)
#define LWIP_PLATFORM_ASSERT(x) { assert(1); }
//#define PACK_STRUCT_FIELD(x) x __attribute__((packed))
#define PACK_STRUCT_FIELD(x) x
#define PACK_STRUCT_STRUCT __attribute__((packed))
#define PACK_STRUCT_BEGIN
#define PACK_STRUCT_END
#endif // MICROPY_INCLUDED_EXTMOD_LWIP_INCLUDE_ARCH_CC_H

View file

@ -1,7 +0,0 @@
#ifndef MICROPY_INCLUDED_EXTMOD_LWIP_INCLUDE_ARCH_PERF_H
#define MICROPY_INCLUDED_EXTMOD_LWIP_INCLUDE_ARCH_PERF_H
#define PERF_START /* null definition */
#define PERF_STOP(x) /* null definition */
#endif // MICROPY_INCLUDED_EXTMOD_LWIP_INCLUDE_ARCH_PERF_H

View file

@ -1,35 +0,0 @@
#ifndef MICROPY_INCLUDED_EXTMOD_LWIP_INCLUDE_LWIPOPTS_H
#define MICROPY_INCLUDED_EXTMOD_LWIP_INCLUDE_LWIPOPTS_H
#include <py/mpconfig.h>
#include <py/misc.h>
#include <py/mphal.h>
// We're running without an OS for this port. We don't provide any services except light protection.
#define NO_SYS 1
#define SYS_LIGHTWEIGHT_PROT 1
#include <stdint.h>
typedef uint32_t sys_prot_t;
#define TCP_LISTEN_BACKLOG 1
// We'll put these into a proper ifdef once somebody implements an ethernet driver
#define LWIP_ARP 0
#define LWIP_ETHERNET 0
#define LWIP_DNS 1
#define LWIP_NETCONN 0
#define LWIP_SOCKET 0
#ifdef MICROPY_PY_LWIP_SLIP
#define LWIP_HAVE_SLIPIF 1
#endif
// For now, we can simply define this as a macro for the timer code. But this function isn't
// universal and other ports will need to do something else. It may be necessary to move
// things like this into a port-provided header file.
#define sys_now mp_hal_ticks_ms
#endif // MICROPY_INCLUDED_EXTMOD_LWIP_INCLUDE_LWIPOPTS_H

424
extmod/machine_i2c_target.c Normal file
View file

@ -0,0 +1,424 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2025 Damien P. George
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "py/runtime.h"
#if MICROPY_PY_MACHINE_I2C_TARGET
#include "extmod/modmachine.h"
#include "shared/runtime/mpirq.h"
enum {
// Events exposed to Python.
I2C_TARGET_IRQ_ADDR_MATCH_READ = 1 << 0,
I2C_TARGET_IRQ_ADDR_MATCH_WRITE = 1 << 1,
I2C_TARGET_IRQ_READ_REQ = 1 << 2,
I2C_TARGET_IRQ_WRITE_REQ = 1 << 3,
I2C_TARGET_IRQ_END_READ = 1 << 4,
I2C_TARGET_IRQ_END_WRITE = 1 << 5,
// Internal event, not exposed to Python.
I2C_TARGET_IRQ_MEM_ADDR_MATCH = 1 << 6,
};
// Define the IRQs that require a hard interrupt.
#define I2C_TARGET_IRQ_ALL_HARD ( \
I2C_TARGET_IRQ_ADDR_MATCH_READ \
| I2C_TARGET_IRQ_ADDR_MATCH_WRITE \
| I2C_TARGET_IRQ_READ_REQ \
| I2C_TARGET_IRQ_WRITE_REQ \
)
enum {
STATE_INACTIVE,
STATE_IDLE,
STATE_ADDR_MATCH_READ,
STATE_ADDR_MATCH_WRITE,
STATE_MEM_ADDR_SELECT,
STATE_READING,
STATE_WRITING,
};
typedef struct _machine_i2c_target_data_t {
uint8_t state;
uint8_t mem_addr_count;
uint8_t mem_addrsize;
uint32_t mem_addr_last;
uint32_t mem_addr;
uint32_t mem_len;
uint8_t *mem_buf;
} machine_i2c_target_data_t;
typedef struct _machine_i2c_target_irq_obj_t {
mp_irq_obj_t base;
uint32_t flags;
uint32_t trigger;
} machine_i2c_target_irq_obj_t;
// The port must provide implementations of these low-level I2C target functions.
static void mp_machine_i2c_target_event_callback(machine_i2c_target_irq_obj_t *irq);
// Read up to N bytes, and return the number of bytes read.
static size_t mp_machine_i2c_target_read_bytes(machine_i2c_target_obj_t *self, size_t len, uint8_t *buf);
// Write (or buffer) N bytes, and return the number of bytes written/buffered.
static size_t mp_machine_i2c_target_write_bytes(machine_i2c_target_obj_t *self, size_t len, const uint8_t *buf);
// Configure the given events to trigger an interrupt.
static void mp_machine_i2c_target_irq_config(machine_i2c_target_obj_t *self, unsigned int trigger);
static mp_obj_t mp_machine_i2c_target_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args);
static void mp_machine_i2c_target_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind);
static void mp_machine_i2c_target_deinit(machine_i2c_target_obj_t *self);
static const mp_irq_methods_t machine_i2c_target_irq_methods;
static machine_i2c_target_data_t machine_i2c_target_data[MICROPY_PY_MACHINE_I2C_TARGET_MAX];
// Needed to retain a root pointer to the memory object.
MP_REGISTER_ROOT_POINTER(mp_obj_t machine_i2c_target_mem_obj[MICROPY_PY_MACHINE_I2C_TARGET_MAX]);
// Needed to retain a root pointer to the IRQ object.
MP_REGISTER_ROOT_POINTER(void *machine_i2c_target_irq_obj[MICROPY_PY_MACHINE_I2C_TARGET_MAX]);
static bool handle_event(machine_i2c_target_data_t *data, unsigned int trigger) {
unsigned int id = data - &machine_i2c_target_data[0];
if (trigger & I2C_TARGET_IRQ_MEM_ADDR_MATCH) {
data->mem_addr_last = data->mem_addr;
}
machine_i2c_target_irq_obj_t *irq = MP_STATE_PORT(machine_i2c_target_irq_obj[id]);
if (irq != NULL && (trigger & irq->trigger)) {
irq->flags = trigger & irq->trigger;
mp_machine_i2c_target_event_callback(irq);
return true; // irq handled
}
return false; // irq not handled
}
static void machine_i2c_target_data_init(machine_i2c_target_data_t *data, mp_obj_t mem_obj, mp_int_t mem_addrsize) {
data->state = STATE_IDLE;
data->mem_addr_count = 0;
data->mem_addrsize = 0;
data->mem_addr_last = 0;
data->mem_addr = 0;
data->mem_len = 0;
data->mem_buf = NULL;
if (mem_obj != mp_const_none) {
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(mem_obj, &bufinfo, MP_BUFFER_RW);
if (mem_addrsize < 0 || mem_addrsize > 32 || mem_addrsize % 8 != 0) {
mp_raise_ValueError(MP_ERROR_TEXT("mem_addrsize must be 0, 8, 16, 24 or 32"));
}
data->mem_addrsize = mem_addrsize / 8;
data->mem_len = bufinfo.len;
data->mem_buf = bufinfo.buf;
}
}
static void machine_i2c_target_data_reset_helper(machine_i2c_target_data_t *data) {
if (data->state == STATE_READING) {
handle_event(data, I2C_TARGET_IRQ_END_READ);
} else if (data->state == STATE_ADDR_MATCH_WRITE || data->state == STATE_WRITING) {
handle_event(data, I2C_TARGET_IRQ_END_WRITE);
}
data->state = STATE_IDLE;
}
static void machine_i2c_target_data_addr_match(machine_i2c_target_data_t *data, bool read) {
machine_i2c_target_data_reset_helper(data);
if (read) {
handle_event(data, I2C_TARGET_IRQ_ADDR_MATCH_READ);
data->state = STATE_ADDR_MATCH_READ;
} else {
handle_event(data, I2C_TARGET_IRQ_ADDR_MATCH_WRITE);
data->state = STATE_ADDR_MATCH_WRITE;
}
}
static void machine_i2c_target_data_read_request(machine_i2c_target_obj_t *self, machine_i2c_target_data_t *data) {
// Let the user handle the read request.
bool event_handled = handle_event(data, I2C_TARGET_IRQ_READ_REQ);
if (data->mem_buf == NULL) {
data->state = STATE_READING;
if (!event_handled) {
// No data source, just write out a zero.
uint8_t val = 0;
mp_machine_i2c_target_write_bytes(self, 1, &val);
}
} else {
// Have a buffer.
if (data->state == STATE_MEM_ADDR_SELECT) {
// Got a short memory address.
data->mem_addr %= data->mem_len;
handle_event(data, I2C_TARGET_IRQ_MEM_ADDR_MATCH);
}
if (data->state != STATE_READING) {
data->state = STATE_READING;
}
uint8_t val = data->mem_buf[data->mem_addr++];
if (data->mem_addr >= data->mem_len) {
data->mem_addr = 0;
}
mp_machine_i2c_target_write_bytes(self, 1, &val);
}
}
static void machine_i2c_target_data_write_request(machine_i2c_target_obj_t *self, machine_i2c_target_data_t *data) {
// Let the user handle the write request.
bool event_handled = handle_event(data, I2C_TARGET_IRQ_WRITE_REQ);
if (data->mem_buf == NULL) {
data->state = STATE_WRITING;
if (!event_handled) {
// No data sink, just read and discard the incoming byte.
uint8_t buf = 0;
mp_machine_i2c_target_read_bytes(self, 1, &buf);
}
} else {
// Have a buffer.
uint8_t buf[4] = {0};
size_t n = mp_machine_i2c_target_read_bytes(self, sizeof(buf), &buf[0]);
for (size_t i = 0; i < n; ++i) {
uint8_t val = buf[i];
if (data->state == STATE_ADDR_MATCH_WRITE) {
data->state = STATE_MEM_ADDR_SELECT;
data->mem_addr = 0;
data->mem_addr_count = data->mem_addrsize;
}
if (data->state == STATE_MEM_ADDR_SELECT && data->mem_addr_count > 0) {
data->mem_addr = data->mem_addr << 8 | val;
--data->mem_addr_count;
if (data->mem_addr_count == 0) {
data->mem_addr %= data->mem_len;
handle_event(data, I2C_TARGET_IRQ_MEM_ADDR_MATCH);
}
} else {
if (data->state == STATE_MEM_ADDR_SELECT) {
data->state = STATE_WRITING;
}
data->mem_buf[data->mem_addr++] = val;
if (data->mem_addr >= data->mem_len) {
data->mem_addr = 0;
}
}
}
}
}
static inline void machine_i2c_target_data_restart_or_stop(machine_i2c_target_data_t *data) {
machine_i2c_target_data_reset_helper(data);
}
static inline void machine_i2c_target_data_stop(machine_i2c_target_data_t *data) {
machine_i2c_target_data_reset_helper(data);
}
// The port provides implementations of its bindings in this file.
#include MICROPY_PY_MACHINE_I2C_TARGET_INCLUDEFILE
static void machine_i2c_target_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
machine_i2c_target_obj_t *self = MP_OBJ_TO_PTR(self_in);
size_t index = mp_machine_i2c_target_get_index(self);
machine_i2c_target_data_t *data = &machine_i2c_target_data[index];
if (dest[0] == MP_OBJ_NULL) {
// Load attribute.
if (attr == MP_QSTR_memaddr) {
dest[0] = mp_obj_new_int(data->mem_addr_last);
} else {
// Continue lookup in locals_dict.
dest[1] = MP_OBJ_SENTINEL;
}
}
}
// I2CTarget.deinit()
static mp_obj_t machine_i2c_target_deinit(mp_obj_t self_in) {
machine_i2c_target_obj_t *self = MP_OBJ_TO_PTR(self_in);
size_t index = mp_machine_i2c_target_get_index(self);
if (machine_i2c_target_data[index].state != STATE_INACTIVE) {
machine_i2c_target_data[index].state = STATE_INACTIVE;
mp_machine_i2c_target_deinit(self);
MP_STATE_PORT(machine_i2c_target_mem_obj[index]) = MP_OBJ_NULL;
MP_STATE_PORT(machine_i2c_target_irq_obj[index]) = NULL;
}
return mp_const_none;
}
static MP_DEFINE_CONST_FUN_OBJ_1(machine_i2c_target_deinit_obj, machine_i2c_target_deinit);
// I2CTarget.readinto(buf)
static mp_obj_t machine_i2c_target_readinto(mp_obj_t self_in, mp_obj_t buf_in) {
machine_i2c_target_obj_t *self = MP_OBJ_TO_PTR(self_in);
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(buf_in, &bufinfo, MP_BUFFER_READ);
return MP_OBJ_NEW_SMALL_INT(mp_machine_i2c_target_read_bytes(self, bufinfo.len, bufinfo.buf));
}
static MP_DEFINE_CONST_FUN_OBJ_2(machine_i2c_target_readinto_obj, machine_i2c_target_readinto);
// I2CTarget.write(data)
static mp_obj_t machine_i2c_target_write(mp_obj_t self_in, mp_obj_t data_in) {
machine_i2c_target_obj_t *self = MP_OBJ_TO_PTR(self_in);
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(data_in, &bufinfo, MP_BUFFER_READ);
return MP_OBJ_NEW_SMALL_INT(mp_machine_i2c_target_write_bytes(self, bufinfo.len, bufinfo.buf));
}
static MP_DEFINE_CONST_FUN_OBJ_2(machine_i2c_target_write_obj, machine_i2c_target_write);
static machine_i2c_target_irq_obj_t *machine_i2c_target_get_irq(machine_i2c_target_obj_t *self) {
// Get the IRQ object.
size_t index = mp_machine_i2c_target_get_index(self);
machine_i2c_target_irq_obj_t *irq = MP_STATE_PORT(machine_i2c_target_irq_obj[index]);
// Allocate the IRQ object if it doesn't already exist.
if (irq == NULL) {
irq = m_new_obj(machine_i2c_target_irq_obj_t);
irq->base.base.type = &mp_irq_type;
irq->base.methods = (mp_irq_methods_t *)&machine_i2c_target_irq_methods;
irq->base.parent = MP_OBJ_FROM_PTR(self);
irq->base.handler = mp_const_none;
irq->base.ishard = false;
MP_STATE_PORT(machine_i2c_target_irq_obj[index]) = irq;
}
return irq;
}
// I2CTarget.irq(handler=None, trigger=IRQ_END_READ|IRQ_END_WRITE, hard=False)
static mp_obj_t machine_i2c_target_irq(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_handler, ARG_trigger, ARG_hard };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_handler, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
{ MP_QSTR_trigger, MP_ARG_INT, {.u_int = I2C_TARGET_IRQ_END_READ | I2C_TARGET_IRQ_END_WRITE} },
{ MP_QSTR_hard, MP_ARG_BOOL, {.u_bool = false} },
};
machine_i2c_target_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
machine_i2c_target_irq_obj_t *irq = machine_i2c_target_get_irq(self);
if (n_args > 1 || kw_args->used != 0) {
// Update IRQ data.
mp_obj_t handler = args[ARG_handler].u_obj;
mp_uint_t trigger = args[ARG_trigger].u_int;
bool hard = args[ARG_hard].u_bool;
#if MICROPY_PY_MACHINE_I2C_TARGET_HARD_IRQ
if ((trigger & I2C_TARGET_IRQ_ALL_HARD) && !hard) {
mp_raise_ValueError(MP_ERROR_TEXT("hard IRQ required"));
}
#else
if (hard) {
mp_raise_ValueError(MP_ERROR_TEXT("hard IRQ unsupported"));
}
#endif
// Disable all IRQs while data is updated.
mp_machine_i2c_target_irq_config(self, 0);
// Update IRQ data.
irq->base.handler = handler;
irq->base.ishard = hard;
irq->flags = 0;
irq->trigger = trigger;
// Enable IRQ if a handler is given.
if (handler != mp_const_none && trigger != 0) {
mp_machine_i2c_target_irq_config(self, trigger);
}
}
return MP_OBJ_FROM_PTR(irq);
}
static MP_DEFINE_CONST_FUN_OBJ_KW(machine_i2c_target_irq_obj, 1, machine_i2c_target_irq);
static const mp_rom_map_elem_t machine_i2c_target_locals_dict_table[] = {
#if MICROPY_PY_MACHINE_I2C_TARGET_FINALISER
{ MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&machine_i2c_target_deinit_obj) },
#endif
{ MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&machine_i2c_target_deinit_obj) },
{ MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&machine_i2c_target_readinto_obj) },
{ MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&machine_i2c_target_write_obj) },
{ MP_ROM_QSTR(MP_QSTR_irq), MP_ROM_PTR(&machine_i2c_target_irq_obj) },
#if MICROPY_PY_MACHINE_I2C_TARGET_HARD_IRQ
{ MP_ROM_QSTR(MP_QSTR_IRQ_ADDR_MATCH_READ), MP_ROM_INT(I2C_TARGET_IRQ_ADDR_MATCH_READ) },
{ MP_ROM_QSTR(MP_QSTR_IRQ_ADDR_MATCH_WRITE), MP_ROM_INT(I2C_TARGET_IRQ_ADDR_MATCH_WRITE) },
{ MP_ROM_QSTR(MP_QSTR_IRQ_READ_REQ), MP_ROM_INT(I2C_TARGET_IRQ_READ_REQ) },
{ MP_ROM_QSTR(MP_QSTR_IRQ_WRITE_REQ), MP_ROM_INT(I2C_TARGET_IRQ_WRITE_REQ) },
#endif
{ MP_ROM_QSTR(MP_QSTR_IRQ_END_READ), MP_ROM_INT(I2C_TARGET_IRQ_END_READ) },
{ MP_ROM_QSTR(MP_QSTR_IRQ_END_WRITE), MP_ROM_INT(I2C_TARGET_IRQ_END_WRITE) },
};
static MP_DEFINE_CONST_DICT(machine_i2c_target_locals_dict, machine_i2c_target_locals_dict_table);
MP_DEFINE_CONST_OBJ_TYPE(
machine_i2c_target_type,
MP_QSTR_I2CTarget,
MP_TYPE_FLAG_NONE,
make_new, mp_machine_i2c_target_make_new,
print, mp_machine_i2c_target_print,
attr, &machine_i2c_target_attr,
locals_dict, &machine_i2c_target_locals_dict
);
static mp_uint_t machine_i2c_target_irq_trigger(mp_obj_t self_in, mp_uint_t new_trigger) {
machine_i2c_target_obj_t *self = MP_OBJ_TO_PTR(self_in);
size_t index = mp_machine_i2c_target_get_index(self);
machine_i2c_target_irq_obj_t *irq = MP_STATE_PORT(machine_i2c_target_irq_obj[index]);
mp_machine_i2c_target_irq_config(self, 0);
irq->flags = 0;
irq->trigger = new_trigger;
mp_machine_i2c_target_irq_config(self, new_trigger);
return 0;
}
static mp_uint_t machine_i2c_target_irq_info(mp_obj_t self_in, mp_uint_t info_type) {
machine_i2c_target_obj_t *self = MP_OBJ_TO_PTR(self_in);
size_t index = mp_machine_i2c_target_get_index(self);
machine_i2c_target_irq_obj_t *irq = MP_STATE_PORT(machine_i2c_target_irq_obj[index]);
if (info_type == MP_IRQ_INFO_FLAGS) {
return irq->flags;
} else if (info_type == MP_IRQ_INFO_TRIGGERS) {
return irq->trigger;
}
return 0;
}
static const mp_irq_methods_t machine_i2c_target_irq_methods = {
.trigger = machine_i2c_target_irq_trigger,
.info = machine_i2c_target_irq_info,
};
#if !MICROPY_PY_MACHINE_I2C_TARGET_FINALISER
void mp_machine_i2c_target_deinit_all(void) {
for (size_t i = 0; i < MICROPY_PY_MACHINE_I2C_TARGET_MAX; ++i) {
if (machine_i2c_target_data[i].state != STATE_INACTIVE) {
machine_i2c_target_deinit(MP_OBJ_FROM_PTR(&machine_i2c_target_obj[i]));
}
}
}
#endif
#endif // MICROPY_PY_MACHINE_I2C_TARGET

View file

@ -483,9 +483,7 @@ static void line(const mp_obj_framebuf_t *fb, mp_int_t x1, mp_int_t y1, mp_int_t
e += 2 * dy;
}
if (0 <= x2 && x2 < fb->width && 0 <= y2 && y2 < fb->height) {
setpixel(fb, x2, y2, col);
}
setpixel_checked(fb, x2, y2, col, 1);
}
static mp_obj_t framebuf_line(size_t n_args, const mp_obj_t *args_in) {
@ -787,39 +785,40 @@ static mp_obj_t framebuf_scroll(mp_obj_t self_in, mp_obj_t xstep_in, mp_obj_t ys
mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(self_in);
mp_int_t xstep = mp_obj_get_int(xstep_in);
mp_int_t ystep = mp_obj_get_int(ystep_in);
int sx, y, xend, yend, dx, dy;
unsigned int sx, y, xend, yend;
int dx, dy;
if (xstep < 0) {
sx = 0;
xend = self->width + xstep;
if (xend <= 0) {
if (-xstep >= self->width) {
return mp_const_none;
}
sx = 0;
xend = self->width + (int)xstep;
dx = 1;
} else {
sx = self->width - 1;
xend = xstep - 1;
if (xend >= sx) {
if (xstep >= self->width) {
return mp_const_none;
}
sx = self->width - 1;
xend = (int)xstep - 1;
dx = -1;
}
if (ystep < 0) {
y = 0;
yend = self->height + ystep;
if (yend <= 0) {
if (-ystep >= self->height) {
return mp_const_none;
}
y = 0;
yend = self->height + (int)ystep;
dy = 1;
} else {
y = self->height - 1;
yend = ystep - 1;
if (yend >= y) {
if (ystep >= self->height) {
return mp_const_none;
}
y = self->height - 1;
yend = (int)ystep - 1;
dy = -1;
}
for (; y != yend; y += dy) {
for (int x = sx; x != xend; x += dx) {
for (unsigned x = sx; x != xend; x += dx) {
setpixel(self, x, y, getpixel(self, x - xstep, y - ystep));
}
}

View file

@ -1724,18 +1724,6 @@ static MP_DEFINE_CONST_OBJ_TYPE(
locals_dict, &lwip_socket_locals_dict
);
/******************************************************************************/
// Support functions for memory protection. lwIP has its own memory management
// routines for its internal structures, and since they might be called in
// interrupt handlers, they need some protection.
sys_prot_t sys_arch_protect() {
return (sys_prot_t)MICROPY_BEGIN_ATOMIC_SECTION();
}
void sys_arch_unprotect(sys_prot_t state) {
MICROPY_END_ATOMIC_SECTION((mp_uint_t)state);
}
/******************************************************************************/
// Polling callbacks for the interfaces connected to lwIP. Right now it calls
// itself a "list" but isn't; we only support a single interface.
@ -1802,10 +1790,11 @@ static mp_obj_t lwip_getaddrinfo(size_t n_args, const mp_obj_t *args) {
mp_obj_t host_in = args[0], port_in = args[1];
const char *host = mp_obj_str_get_str(host_in);
mp_int_t port = mp_obj_get_int(port_in);
mp_int_t family = 0;
// If constraints were passed then check they are compatible with the supported params
if (n_args > 2) {
mp_int_t family = mp_obj_get_int(args[2]);
family = mp_obj_get_int(args[2]);
mp_int_t type = 0;
mp_int_t proto = 0;
mp_int_t flags = 0;
@ -1818,7 +1807,7 @@ static mp_obj_t lwip_getaddrinfo(size_t n_args, const mp_obj_t *args) {
}
}
}
if (!((family == 0 || family == MOD_NETWORK_AF_INET)
if (!((family == 0 || family == MOD_NETWORK_AF_INET || family == MOD_NETWORK_AF_INET6)
&& (type == 0 || type == MOD_NETWORK_SOCK_STREAM)
&& proto == 0
&& flags == 0)) {
@ -1829,11 +1818,23 @@ static mp_obj_t lwip_getaddrinfo(size_t n_args, const mp_obj_t *args) {
getaddrinfo_state_t state;
state.status = 0;
#if LWIP_VERSION_MAJOR >= 2
// If family was specified, then try and resolve the address type as
// requested. Otherwise, use the default from network configuration.
if (family == MOD_NETWORK_AF_INET) {
family = LWIP_DNS_ADDRTYPE_IPV4;
} else if (family == MOD_NETWORK_AF_INET6) {
family = LWIP_DNS_ADDRTYPE_IPV6;
} else {
family = mp_mod_network_prefer_dns_use_ip_version == 4 ? LWIP_DNS_ADDRTYPE_IPV4_IPV6 : LWIP_DNS_ADDRTYPE_IPV6_IPV4;
}
#endif
MICROPY_PY_LWIP_ENTER
#if LWIP_VERSION_MAJOR < 2
err_t ret = dns_gethostbyname(host, (ip_addr_t *)&state.ipaddr, lwip_getaddrinfo_cb, &state);
#else
err_t ret = dns_gethostbyname_addrtype(host, (ip_addr_t *)&state.ipaddr, lwip_getaddrinfo_cb, &state, mp_mod_network_prefer_dns_use_ip_version == 4 ? LWIP_DNS_ADDRTYPE_IPV4_IPV6 : LWIP_DNS_ADDRTYPE_IPV6_IPV4);
err_t ret = dns_gethostbyname_addrtype(host, (ip_addr_t *)&state.ipaddr, lwip_getaddrinfo_cb, &state, family);
#endif
MICROPY_PY_LWIP_EXIT

View file

@ -219,6 +219,9 @@ static const mp_rom_map_elem_t machine_module_globals_table[] = {
#if MICROPY_PY_MACHINE_I2C
{ MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&machine_i2c_type) },
#endif
#if MICROPY_PY_MACHINE_I2C_TARGET
{ MP_ROM_QSTR(MP_QSTR_I2CTarget), MP_ROM_PTR(&machine_i2c_target_type) },
#endif
#if MICROPY_PY_MACHINE_I2S
{ MP_ROM_QSTR(MP_QSTR_I2S), MP_ROM_PTR(&machine_i2s_type) },
#endif

View file

@ -129,6 +129,7 @@
// A port must provide these types, but they are otherwise opaque.
typedef struct _machine_adc_obj_t machine_adc_obj_t;
typedef struct _machine_adc_block_obj_t machine_adc_block_obj_t;
typedef struct _machine_i2c_target_obj_t machine_i2c_target_obj_t;
typedef struct _machine_i2s_obj_t machine_i2s_obj_t;
typedef struct _machine_pwm_obj_t machine_pwm_obj_t;
typedef struct _machine_uart_obj_t machine_uart_obj_t;
@ -203,6 +204,7 @@ extern const machine_mem_obj_t machine_mem32_obj;
extern const mp_obj_type_t machine_adc_type;
extern const mp_obj_type_t machine_adc_block_type;
extern const mp_obj_type_t machine_i2c_type;
extern const mp_obj_type_t machine_i2c_target_type;
extern const mp_obj_type_t machine_i2s_type;
extern const mp_obj_type_t machine_mem_type;
extern const mp_obj_type_t machine_pin_type;
@ -261,6 +263,10 @@ int mp_machine_i2c_transfer_adaptor(mp_obj_base_t *self, uint16_t addr, size_t n
int mp_machine_soft_i2c_transfer(mp_obj_base_t *self, uint16_t addr, size_t n, mp_machine_i2c_buf_t *bufs, unsigned int flags);
#endif
#if MICROPY_PY_MACHINE_I2C_TARGET
void mp_machine_i2c_target_deinit_all(void);
#endif
#if MICROPY_PY_MACHINE_SPI
mp_obj_t mp_machine_spi_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args);
MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_machine_spi_read_obj);

View file

@ -196,10 +196,11 @@ static void re_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t
// Note: this function can't be named re_exec because it may clash with system headers, eg on FreeBSD
static mp_obj_t re_exec_helper(bool is_anchored, uint n_args, const mp_obj_t *args) {
(void)n_args;
mp_obj_re_t *self;
bool was_compiled = false;
if (mp_obj_is_type(args[0], (mp_obj_type_t *)&re_type)) {
self = MP_OBJ_TO_PTR(args[0]);
was_compiled = true;
} else {
self = MP_OBJ_TO_PTR(mod_re_compile(1, args));
}
@ -207,6 +208,28 @@ static mp_obj_t re_exec_helper(bool is_anchored, uint n_args, const mp_obj_t *ar
size_t len;
subj.begin_line = subj.begin = mp_obj_str_get_data(args[1], &len);
subj.end = subj.begin + len;
if (was_compiled && n_args > 2) {
// Arg #2 is starting-pos
mp_int_t startpos = mp_obj_get_int(args[2]);
if (startpos > (mp_int_t)len) {
startpos = len;
} else if (startpos < 0) {
startpos = 0;
}
subj.begin += startpos;
if (n_args > 3) {
// Arg #3 is ending-pos
mp_int_t endpos = mp_obj_get_int(args[3]);
if (endpos > (mp_int_t)len) {
endpos = len;
} else if (endpos < startpos) {
endpos = startpos;
}
subj.end = subj.begin_line + endpos;
}
}
int caps_num = (self->re.sub + 1) * 2;
mp_obj_match_t *match = m_new_obj_var(mp_obj_match_t, caps, char *, caps_num);
// cast is a workaround for a bug in msvc: it treats const char** as a const pointer instead of a pointer to pointer to const char

View file

@ -53,26 +53,26 @@
// - weekday is 0-6 for Mon-Sun
// - yearday is 1-366
static mp_obj_t time_localtime(size_t n_args, const mp_obj_t *args) {
timeutils_struct_time_t tm;
if (n_args == 0 || args[0] == mp_const_none) {
// Get current date and time.
return mp_time_localtime_get();
mp_time_localtime_get(&tm);
} else {
// Convert given seconds to tuple.
mp_timestamp_t seconds = timeutils_obj_get_timestamp(args[0]);
timeutils_struct_time_t tm;
timeutils_seconds_since_epoch_to_struct_time(seconds, &tm);
mp_obj_t tuple[8] = {
tuple[0] = mp_obj_new_int(tm.tm_year),
tuple[1] = mp_obj_new_int(tm.tm_mon),
tuple[2] = mp_obj_new_int(tm.tm_mday),
tuple[3] = mp_obj_new_int(tm.tm_hour),
tuple[4] = mp_obj_new_int(tm.tm_min),
tuple[5] = mp_obj_new_int(tm.tm_sec),
tuple[6] = mp_obj_new_int(tm.tm_wday),
tuple[7] = mp_obj_new_int(tm.tm_yday),
};
return mp_obj_new_tuple(8, tuple);
}
mp_obj_t tuple[8] = {
mp_obj_new_int(tm.tm_year),
mp_obj_new_int(tm.tm_mon),
mp_obj_new_int(tm.tm_mday),
mp_obj_new_int(tm.tm_hour),
mp_obj_new_int(tm.tm_min),
mp_obj_new_int(tm.tm_sec),
mp_obj_new_int(tm.tm_wday),
mp_obj_new_int(tm.tm_yday),
};
return mp_obj_new_tuple(8, tuple);
}
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_time_localtime_obj, 0, 1, time_localtime);

View file

@ -639,7 +639,7 @@ static mp_obj_t ssl_socket_make_new(mp_obj_ssl_context_t *ssl_context, mp_obj_t
ret = mbedtls_ssl_setup(&o->ssl, &ssl_context->conf);
#if !MICROPY_MBEDTLS_CONFIG_BARE_METAL
if (ret == MBEDTLS_ERR_SSL_ALLOC_FAILED) {
if (ret != 0) {
// If mbedTLS relies on platform libc heap for buffers (i.e. esp32
// port), then run a GC pass and then try again. This is useful because
// it may free a Python object (like an old SSL socket) whose finaliser

View file

@ -62,8 +62,6 @@ typedef struct _network_ppp_obj_t {
const mp_obj_type_t mp_network_ppp_lwip_type;
static mp_obj_t network_ppp___del__(mp_obj_t self_in);
static void network_ppp_stream_uart_irq_disable(network_ppp_obj_t *self) {
if (self->stream == mp_const_none) {
return;
@ -88,8 +86,12 @@ static void network_ppp_status_cb(ppp_pcb *pcb, int err_code, void *ctx) {
// only need to free the PPP PCB, not close it.
self->state = STATE_ACTIVE;
}
network_ppp_stream_uart_irq_disable(self);
// Clean up the PPP PCB.
network_ppp___del__(MP_OBJ_FROM_PTR(self));
if (ppp_free(pcb) == ERR_OK) {
self->state = STATE_INACTIVE;
self->pcb = NULL;
}
break;
default:
self->state = STATE_ERROR;
@ -117,17 +119,18 @@ static mp_obj_t network_ppp_make_new(const mp_obj_type_t *type, size_t n_args, s
static mp_obj_t network_ppp___del__(mp_obj_t self_in) {
network_ppp_obj_t *self = MP_OBJ_TO_PTR(self_in);
if (self->state >= STATE_ACTIVE) {
if (self->state >= STATE_ERROR) {
// Still connected over the stream.
// Force the connection to close, with nocarrier=1.
self->state = STATE_INACTIVE;
ppp_close(self->pcb, 1);
}
network_ppp_stream_uart_irq_disable(self);
network_ppp_stream_uart_irq_disable(self);
if (self->state >= STATE_ERROR) {
// Still connected over the stream.
// Force the connection to close, with nocarrier=1.
ppp_close(self->pcb, 1);
} else if (self->state >= STATE_ACTIVE) {
// Free PPP PCB and reset state.
if (ppp_free(self->pcb) != ERR_OK) {
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("ppp_free failed"));
}
self->state = STATE_INACTIVE;
ppp_free(self->pcb);
self->pcb = NULL;
}
return mp_const_none;
@ -142,8 +145,8 @@ static mp_obj_t network_ppp_poll(size_t n_args, const mp_obj_t *args) {
}
mp_int_t total_len = 0;
mp_obj_t stream = self->stream;
while (stream != mp_const_none) {
mp_obj_t stream;
while (self->state >= STATE_ACTIVE && (stream = self->stream) != mp_const_none) {
uint8_t buf[256];
int err;
mp_uint_t len = mp_stream_rw(stream, buf, sizeof(buf), &err, 0);

@ -1 +1 @@
Subproject commit 5b496e944ec045177afa1620920a168410b7f60b
Subproject commit 34c4ee1647ac4b177ae40adf0ec514660e433dc0

@ -1 +1 @@
Subproject commit fa5a554c7944d2a196626f8d3631e44943f9abcc
Subproject commit 91b04b34a59f6d81661cec6f84611afe6330ce92

View file

@ -81,7 +81,7 @@ static int compile_and_save(const char *file, const char *output_file, const cha
source_name = qstr_from_str(source_file);
}
#if MICROPY_PY___FILE__
#if MICROPY_MODULE___FILE__
mp_store_global(MP_QSTR___file__, MP_OBJ_NEW_QSTR(source_name));
#endif

View file

@ -55,6 +55,7 @@
#define MICROPY_COMP_CONST_FOLDING (1)
#define MICROPY_COMP_MODULE_CONST (1)
#define MICROPY_COMP_CONST (1)
#define MICROPY_COMP_CONST_FLOAT (1)
#define MICROPY_COMP_DOUBLE_TUPLE_ASSIGN (1)
#define MICROPY_COMP_TRIPLE_TUPLE_ASSIGN (1)
#define MICROPY_COMP_RETURN_IF_EXPR (1)
@ -84,11 +85,12 @@
#define MICROPY_GCREGS_SETJMP (1)
#endif
#define MICROPY_PY___FILE__ (0)
#define MICROPY_MODULE___FILE__ (0)
#define MICROPY_PY_ARRAY (0)
#define MICROPY_PY_ATTRTUPLE (0)
#define MICROPY_PY_COLLECTIONS (0)
#define MICROPY_PY_MATH (0)
#define MICROPY_PY_MATH (MICROPY_COMP_CONST_FLOAT)
#define MICROPY_PY_MATH_CONSTANTS (MICROPY_COMP_CONST_FLOAT)
#define MICROPY_PY_CMATH (0)
#define MICROPY_PY_GC (0)
#define MICROPY_PY_IO (0)

View file

@ -22,6 +22,8 @@ include $(TOP)/extmod/extmod.mk
################################################################################
# Project specific settings and compiler/linker flags
MPY_CROSS_FLAGS += -march=armv7emdp
CROSS_COMPILE ?= arm-none-eabi-
ALIF_DFP_REL_TOP ?= lib/alif_ensemble-cmsis-dfp
ALIF_DFP_REL_HERE ?= $(TOP)/lib/alif_ensemble-cmsis-dfp

View file

@ -24,15 +24,12 @@
* THE SOFTWARE.
*/
#include "py/mphal.h"
#include "shared/timeutils/timeutils.h"
#include "lib/oofatfs/ff.h"
DWORD get_fattime(void) {
// TODO
int year = 2024;
int month = 1;
int day = 1;
int hour = 0;
int min = 0;
int sec = 0;
return ((year - 1980) << 25) | (month << 21) | (day << 16) | (hour << 11) | (min << 5) | (sec / 2);
timeutils_struct_time_t tm;
timeutils_seconds_since_epoch_to_struct_time(mp_hal_time_get(NULL), &tm);
return ((tm.tm_year - 1980) << 25) | ((tm.tm_mon) << 21) | ((tm.tm_mday) << 16) | ((tm.tm_hour) << 11) | ((tm.tm_min) << 5) | (tm.tm_sec / 2);
}

View file

@ -49,6 +49,7 @@
#define IRQ_PRI_HWSEM NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 8, 0)
#define IRQ_PRI_GPU NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 10, 0)
#define IRQ_PRI_GPIO NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 50, 0)
#define IRQ_PRI_I2C NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 60, 0)
#define IRQ_PRI_RTC NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 100, 0)
#define IRQ_PRI_CYW43 NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 126, 0)
#define IRQ_PRI_PENDSV NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 127, 0)

View file

@ -125,9 +125,12 @@ mp_obj_t machine_i2c_make_new(const mp_obj_type_t *type, size_t n_args, size_t n
self->freq = args[ARG_freq].u_int;
self->timeout = args[ARG_timeout].u_int;
// here we would check the scl/sda pins and configure them, but it's not implemented
if (args[ARG_scl].u_obj != mp_const_none || args[ARG_sda].u_obj != mp_const_none) {
mp_raise_ValueError(MP_ERROR_TEXT("explicit choice of scl/sda is not implemented"));
// Set SCL/SDA pins if given.
if (args[ARG_scl].u_obj != mp_const_none) {
self->scl = mp_hal_get_pin_obj(args[ARG_scl].u_obj);
}
if (args[ARG_sda].u_obj != mp_const_none) {
self->sda = mp_hal_get_pin_obj(args[ARG_sda].u_obj);
}
// Disable I2C controller.

View file

@ -0,0 +1,325 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2025 Damien P. George
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
// This file is never compiled standalone, it's included directly from
// extmod/machine_i2c_target.c via MICROPY_PY_MACHINE_I2C_TARGET_INCLUDEFILE.
#include "i2c.h"
#define I2C_IC_CON_RX_FIFO_FULL_HLD_CTRL (1 << 9)
#define I2C_IC_CON_TX_EMPTY_CTRL (1 << 8)
#define I2C_IC_CON_STOP_DET_IFADDRESSED (1 << 7)
typedef struct _machine_i2c_target_obj_t {
mp_obj_base_t base;
I2C_Type *i2c;
mp_hal_pin_obj_t scl;
mp_hal_pin_obj_t sda;
uint8_t state;
bool stop_pending;
bool irq_active;
} machine_i2c_target_obj_t;
static machine_i2c_target_obj_t machine_i2c_target_obj[] = {
#if defined(MICROPY_HW_I2C0_SCL)
[0] = {{&machine_i2c_target_type}, (I2C_Type *)I2C0_BASE, MICROPY_HW_I2C0_SCL, MICROPY_HW_I2C0_SDA},
#endif
#if defined(MICROPY_HW_I2C1_SCL)
[1] = {{&machine_i2c_target_type}, (I2C_Type *)I2C1_BASE, MICROPY_HW_I2C1_SCL, MICROPY_HW_I2C1_SDA},
#endif
#if defined(MICROPY_HW_I2C2_SCL)
[2] = {{&machine_i2c_target_type}, (I2C_Type *)I2C2_BASE, MICROPY_HW_I2C2_SCL, MICROPY_HW_I2C2_SDA},
#endif
#if defined(MICROPY_HW_I2C3_SCL)
[3] = {{&machine_i2c_target_type}, (I2C_Type *)I2C3_BASE, MICROPY_HW_I2C3_SCL, MICROPY_HW_I2C3_SDA},
#endif
};
/******************************************************************************/
// Alif I2C hardware bindings
//
// The hardware triggers the following IRQs for the given scenarios:
// - scan (0-byte write) of another target: START_DET
// - scan (0-byte write) of us: START_DET STOP_DET
// - write of n bytes: START_DET RX_FULL*n STOP_DET
// - write of n bytes then read of m bytes: START_DET RX_FULL*n START_DET RD_REQ*m RX_DONE STOP_DET
static inline unsigned int i2c_reg_base_to_index(I2C_Type *i2c) {
return ((uintptr_t)i2c - I2C0_BASE) / (I2C1_BASE - I2C0_BASE);
}
static const uint32_t i2c_irq_num[] = { I2C0_IRQ_IRQn, I2C1_IRQ_IRQn, I2C2_IRQ_IRQn, I2C3_IRQ_IRQn };
static void check_stop_pending(machine_i2c_target_obj_t *self) {
if (self->irq_active) {
return;
}
if (self->stop_pending && !(self->i2c->I2C_STATUS & I2C_IC_STATUS_RECEIVE_FIFO_NOT_EMPTY)) {
unsigned int i2c_id = self - &machine_i2c_target_obj[0];
machine_i2c_target_data_t *data = &machine_i2c_target_data[i2c_id];
self->stop_pending = false;
self->state = STATE_IDLE;
machine_i2c_target_data_restart_or_stop(data);
}
}
static void i2c_target_irq_handler(machine_i2c_target_obj_t *self) {
unsigned int i2c_id = self - &machine_i2c_target_obj[0];
machine_i2c_target_data_t *data = &machine_i2c_target_data[i2c_id];
I2C_Type *i2c = self->i2c;
self->irq_active = true;
// Get the interrupt status.
uint32_t intr_stat = i2c->I2C_RAW_INTR_STAT;
if (intr_stat & I2C_IC_INTR_STAT_TX_ABRT) {
// Clear the TX_ABRT condition.
(void)i2c->I2C_CLR_TX_ABRT;
}
if (intr_stat & I2C_IC_INTR_STAT_START_DET) {
// Controller sent a start condition.
// Reset all state machines in case something went wrong.
(void)i2c->I2C_CLR_START_DET;
if (self->state != STATE_IDLE) {
machine_i2c_target_data_reset_helper(data);
self->state = STATE_IDLE;
}
}
if (intr_stat & I2C_IC_INTR_STAT_RX_FULL) {
// Data from controller is available for reading.
// Mask interrupt until I2C_DATA_CMD is read from.
i2c->I2C_INTR_MASK &= ~I2C_IC_INTR_STAT_RX_FULL;
if (self->state != STATE_WRITING) {
machine_i2c_target_data_addr_match(data, false);
}
machine_i2c_target_data_write_request(self, data);
self->state = STATE_WRITING;
}
if (intr_stat & (I2C_IC_INTR_STAT_RD_REQ | I2C_IC_INTR_STAT_RX_DONE)) {
// Controller is requesting data.
// Note: for RX_DONE interrupt, no data needs to be written but this event is
// needed to match the expected I2CTarget event behaviour. A TX_ABTR interrupt
// will be fired after the unused byte is written to I2C_DATA_CMD, and clearing
// that abort is required to reset the hardware I2C state machine.
(void)i2c->I2C_CLR_RX_DONE;
(void)i2c->I2C_CLR_RD_REQ;
i2c->I2C_INTR_MASK &= ~I2C_IC_INTR_STAT_RD_REQ;
if (self->state != STATE_READING) {
machine_i2c_target_data_addr_match(data, true);
}
machine_i2c_target_data_read_request(self, data);
self->state = STATE_READING;
}
if (intr_stat & I2C_IC_INTR_STAT_STOP_DET) {
// Controller has generated a stop condition.
(void)i2c->I2C_CLR_STOP_DET;
if (self->state == STATE_IDLE) {
machine_i2c_target_data_addr_match(data, false);
}
if (i2c->I2C_STATUS & I2C_IC_STATUS_RECEIVE_FIFO_NOT_EMPTY) {
self->stop_pending = true;
} else {
machine_i2c_target_data_restart_or_stop(data);
self->state = STATE_IDLE;
}
}
self->irq_active = false;
check_stop_pending(self);
}
void I2C0_IRQHandler(void) {
i2c_target_irq_handler(&machine_i2c_target_obj[0]);
}
void I2C1_IRQHandler(void) {
i2c_target_irq_handler(&machine_i2c_target_obj[1]);
}
void I2C2_IRQHandler(void) {
i2c_target_irq_handler(&machine_i2c_target_obj[2]);
}
void I2C3_IRQHandler(void) {
i2c_target_irq_handler(&machine_i2c_target_obj[3]);
}
static void i2c_target_init(I2C_Type *i2c, uint16_t addr, bool addr_10bit) {
i2c_disable(i2c);
uint32_t ic_con_reg = 0;
ic_con_reg |= I2C_IC_CON_RX_FIFO_FULL_HLD_CTRL;
ic_con_reg |= I2C_IC_CON_STOP_DET_IFADDRESSED;
if (addr_10bit) {
ic_con_reg |= I2C_SLAVE_10BIT_ADDR_MODE;
}
i2c->I2C_CON = ic_con_reg;
i2c->I2C_SAR = addr & I2C_IC_SAR_10BIT_ADDR_MASK;
i2c->I2C_TX_TL = 1;
i2c->I2C_RX_TL = 0; // interrupt when at least 1 byte is available
i2c_clear_all_interrupt(i2c);
// Enable interrupts.
i2c->I2C_INTR_MASK =
I2C_IC_INTR_STAT_STOP_DET
| I2C_IC_INTR_STAT_RX_DONE
| I2C_IC_INTR_STAT_TX_ABRT
| I2C_IC_INTR_STAT_RD_REQ
| I2C_IC_INTR_STAT_RX_FULL
;
i2c_enable(i2c);
// Enable I2C interrupts.
uint32_t irq_num = i2c_irq_num[i2c_reg_base_to_index(i2c)];
NVIC_ClearPendingIRQ(irq_num);
NVIC_SetPriority(irq_num, IRQ_PRI_I2C);
NVIC_EnableIRQ(irq_num);
}
static void i2c_target_deinit(I2C_Type *i2c) {
uint32_t irq_num = i2c_irq_num[i2c_reg_base_to_index(i2c)];
NVIC_DisableIRQ(irq_num);
i2c_disable(i2c);
}
/******************************************************************************/
// I2CTarget port implementation
static inline size_t mp_machine_i2c_target_get_index(machine_i2c_target_obj_t *self) {
return self - &machine_i2c_target_obj[0];
}
static inline void mp_machine_i2c_target_event_callback(machine_i2c_target_irq_obj_t *irq) {
mp_irq_handler(&irq->base);
}
static size_t mp_machine_i2c_target_read_bytes(machine_i2c_target_obj_t *self, size_t len, uint8_t *buf) {
I2C_Type *i2c = self->i2c;
// Read from the RX FIFO.
size_t i = 0;
while (i < len && (i2c->I2C_STATUS & I2C_IC_STATUS_RECEIVE_FIFO_NOT_EMPTY)) {
buf[i++] = i2c->I2C_DATA_CMD;
}
// Re-enable RX_FULL interrupt.
i2c->I2C_INTR_MASK |= I2C_IC_INTR_STAT_RX_FULL;
check_stop_pending(self);
return i;
}
static size_t mp_machine_i2c_target_write_bytes(machine_i2c_target_obj_t *self, size_t len, const uint8_t *buf) {
// Write to the TX FIFO.
size_t i = 0;
while (i < len && (self->i2c->I2C_STATUS & I2C_IC_STATUS_TRANSMIT_FIFO_NOT_FULL)) {
self->i2c->I2C_DATA_CMD = buf[i++];
}
// Re-enable RD_REQ interrupt.
self->i2c->I2C_INTR_MASK |= I2C_IC_INTR_STAT_RD_REQ;
return 1;
}
static inline void mp_machine_i2c_target_irq_config(machine_i2c_target_obj_t *self, unsigned int trigger) {
(void)self;
(void)trigger;
}
static mp_obj_t mp_machine_i2c_target_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
enum { ARG_id, ARG_addr, ARG_addrsize, ARG_mem, ARG_mem_addrsize, ARG_scl, ARG_sda };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_id, MP_ARG_INT | MP_ARG_REQUIRED },
{ MP_QSTR_addr, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_addrsize, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 7} },
{ MP_QSTR_mem, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
{ MP_QSTR_mem_addrsize, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 8} },
{ MP_QSTR_scl, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
{ MP_QSTR_sda, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
};
// Parse arguments.
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
int i2c_id = args[ARG_id].u_int;
// Check if the I2C bus is valid
if (i2c_id < 0 || i2c_id >= MP_ARRAY_SIZE(machine_i2c_target_obj)) {
mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("I2CTarget(%d) doesn't exist"), i2c_id);
}
// Get static peripheral object.
machine_i2c_target_obj_t *self = &machine_i2c_target_obj[i2c_id];
// Disable I2C controller.
i2c_disable(self->i2c);
// Initialise data.
self->state = STATE_IDLE;
self->stop_pending = false;
self->irq_active = false;
MP_STATE_PORT(machine_i2c_target_mem_obj)[i2c_id] = args[ARG_mem].u_obj;
machine_i2c_target_data_t *data = &machine_i2c_target_data[i2c_id];
machine_i2c_target_data_init(data, args[ARG_mem].u_obj, args[ARG_mem_addrsize].u_int);
// Set SCL/SDA pins if given.
if (args[ARG_scl].u_obj != mp_const_none) {
self->scl = mp_hal_get_pin_obj(args[ARG_scl].u_obj);
}
if (args[ARG_sda].u_obj != mp_const_none) {
self->sda = mp_hal_get_pin_obj(args[ARG_sda].u_obj);
}
// Configure I2C pins.
mp_hal_pin_config(self->scl, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_UP,
MP_HAL_PIN_SPEED_LOW, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT(I2C_SCL, i2c_id), true);
mp_hal_pin_config(self->sda, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_UP,
MP_HAL_PIN_SPEED_LOW, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT(I2C_SDA, i2c_id), true);
// Initialise the I2C target.
i2c_target_init(self->i2c, args[ARG_addr].u_int, args[ARG_addrsize].u_int == 10);
return MP_OBJ_FROM_PTR(self);
}
static void mp_machine_i2c_target_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
machine_i2c_target_obj_t *self = MP_OBJ_TO_PTR(self_in);
mp_printf(print, "I2CTarget(%u, addr=%u, scl=" MP_HAL_PIN_FMT ", sda=" MP_HAL_PIN_FMT ")",
self - &machine_i2c_target_obj[0], self->i2c->I2C_SAR, mp_hal_pin_name(self->scl), mp_hal_pin_name(self->sda));
}
static void mp_machine_i2c_target_deinit(machine_i2c_target_obj_t *self) {
i2c_target_deinit(self->i2c);
}

View file

@ -28,9 +28,20 @@
#include "py/mphal.h"
#include "py/mperrno.h"
#include "extmod/modmachine.h"
#include "shared/timeutils/timeutils.h"
#include "rtc.h"
#include "sys_ctrl_rtc.h"
// The LPRTC (low-power real-time counter) is a 32-bit counter with a 16-bit prescaler,
// and usually clocked by a 32768Hz clock source. To get a large date range of around
// 136 years, the prescaler is set to 32768 and so the counter clocks at 1Hz. Then the
// counter counts the number of seconds since the 1970 Epoch. The prescaler is used to
// get the subseconds which are then converted to microseconds.
//
// The combined counter+prescaler counts starting at 0 from the year 1970 up to the year
// 2106, with a resolution of 30.52 microseconds.
#define LPRTC_PRESCALER_SETTING (32768)
typedef struct _machine_rtc_obj_t {
mp_obj_base_t base;
LPRTC_Type *rtc;
@ -44,24 +55,97 @@ void LPRTC_IRQHandler(void) {
lprtc_interrupt_disable(machine_rtc.rtc);
}
// Returns the number of seconds and microseconds since the Epoch.
uint32_t mp_hal_time_get(uint32_t *microseconds) {
uint32_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION();
uint32_t count = lprtc_get_count(machine_rtc.rtc);
if (microseconds == NULL) {
MICROPY_END_ATOMIC_SECTION(atomic_state);
return count;
}
uint32_t prescaler = machine_rtc.rtc->LPRTC_CPCVR;
uint32_t count2 = lprtc_get_count(machine_rtc.rtc);
if (count != count2) {
// The counter incremented during sampling of the prescaler, so resample the prescaler.
prescaler = machine_rtc.rtc->LPRTC_CPCVR;
}
MICROPY_END_ATOMIC_SECTION(atomic_state);
// Compute the microseconds from the up-counting prescaler value.
MP_STATIC_ASSERT(LPRTC_PRESCALER_SETTING == 32768);
*microseconds = 15625UL * prescaler / 512UL;
return count2;
}
static mp_obj_t machine_rtc_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
const machine_rtc_obj_t *self = &machine_rtc;
// Check arguments.
mp_arg_check_num(n_args, n_kw, 0, 0, false);
enable_lprtc_clk();
lprtc_prescaler_disable(self->rtc);
lprtc_counter_wrap_disable(self->rtc);
lprtc_interrupt_disable(self->rtc);
lprtc_interrupt_unmask(self->rtc);
// Initialise the LPRTC if it's not already enabled.
if (!((VBAT->RTC_CLK_EN & RTC_CLK_ENABLE)
&& (self->rtc->LPRTC_CCR & CCR_LPRTC_EN)
&& (self->rtc->LPRTC_CPSR == LPRTC_PRESCALER_SETTING))) {
enable_lprtc_clk();
self->rtc->LPRTC_CCR = 0;
lprtc_load_prescaler(self->rtc, LPRTC_PRESCALER_SETTING);
lprtc_load_count(self->rtc, 0);
self->rtc->LPRTC_CCR = CCR_LPRTC_PSCLR_EN | CCR_LPRTC_EN;
}
NVIC_SetPriority(LPRTC_IRQ_IRQn, IRQ_PRI_RTC);
NVIC_ClearPendingIRQ(LPRTC_IRQ_IRQn);
NVIC_EnableIRQ(LPRTC_IRQ_IRQn);
return MP_OBJ_FROM_PTR(self);
}
static mp_obj_t machine_rtc_datetime(mp_uint_t n_args, const mp_obj_t *args) {
if (n_args == 1) {
// Get datetime.
uint32_t microseconds;
mp_timestamp_t s = mp_hal_time_get(&microseconds);
timeutils_struct_time_t tm;
timeutils_seconds_since_epoch_to_struct_time(s, &tm);
mp_obj_t tuple[8] = {
mp_obj_new_int(tm.tm_year),
mp_obj_new_int(tm.tm_mon),
mp_obj_new_int(tm.tm_mday),
mp_obj_new_int(tm.tm_wday),
mp_obj_new_int(tm.tm_hour),
mp_obj_new_int(tm.tm_min),
mp_obj_new_int(tm.tm_sec),
mp_obj_new_int(microseconds),
};
return mp_obj_new_tuple(8, tuple);
} else {
// Set datetime.
mp_obj_t *items;
mp_obj_get_array_fixed_n(args[1], 8, &items);
timeutils_struct_time_t tm = {
.tm_year = mp_obj_get_int(items[0]),
.tm_mon = mp_obj_get_int(items[1]),
.tm_mday = mp_obj_get_int(items[2]),
.tm_hour = mp_obj_get_int(items[4]),
.tm_min = mp_obj_get_int(items[5]),
.tm_sec = mp_obj_get_int(items[6]),
};
mp_timestamp_t s = timeutils_seconds_since_epoch(tm.tm_year, tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
// Disable then re-enable the LPRTC so that the prescaler counter resets to 0.
machine_rtc.rtc->LPRTC_CCR = 0;
lprtc_load_count(machine_rtc.rtc, s);
machine_rtc.rtc->LPRTC_CCR = CCR_LPRTC_PSCLR_EN | CCR_LPRTC_EN;
}
return mp_const_none;
}
static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_rtc_datetime_obj, 1, 2, machine_rtc_datetime);
static mp_obj_t machine_rtc_alarm(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_id, ARG_time, ARG_repeat };
@ -80,13 +164,18 @@ static mp_obj_t machine_rtc_alarm(size_t n_args, const mp_obj_t *pos_args, mp_ma
if (mp_obj_is_int(args[ARG_time].u_obj)) {
uint32_t seconds = mp_obj_get_int(args[1].u_obj) / 1000;
lprtc_counter_disable(self->rtc);
lprtc_load_count(self->rtc, 1);
lprtc_load_counter_match_register(self->rtc, seconds * 32768);
// Make sure we are guaranteed an interrupt:
// - if seconds = 0 it won't fire
// - if seconds = 1 it may miss if the counter rolls over just after it's read
// - if seconds >= 2 then it will always fire (when read/written close enough)
seconds = MAX(2, seconds);
// Configure the counter match as atomically as possible.
uint32_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION();
lprtc_interrupt_ack(self->rtc);
lprtc_load_counter_match_register(self->rtc, lprtc_get_count(self->rtc) + seconds);
lprtc_interrupt_enable(self->rtc);
lprtc_counter_enable(self->rtc);
MICROPY_END_ATOMIC_SECTION(atomic_state);
} else {
mp_raise_ValueError(MP_ERROR_TEXT("invalid argument(s)"));
}
@ -96,6 +185,7 @@ static mp_obj_t machine_rtc_alarm(size_t n_args, const mp_obj_t *pos_args, mp_ma
static MP_DEFINE_CONST_FUN_OBJ_KW(machine_rtc_alarm_obj, 1, machine_rtc_alarm);
static const mp_rom_map_elem_t machine_rtc_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_datetime), MP_ROM_PTR(&machine_rtc_datetime_obj) },
{ MP_ROM_QSTR(MP_QSTR_alarm), MP_ROM_PTR(&machine_rtc_alarm_obj) },
};
static MP_DEFINE_CONST_DICT(machine_rtc_locals_dict, machine_rtc_locals_dict_table);

View file

@ -31,6 +31,7 @@
#include "py/mphal.h"
#include "py/stackctrl.h"
#include "extmod/modbluetooth.h"
#include "extmod/modmachine.h"
#include "extmod/modnetwork.h"
#include "shared/readline/readline.h"
#include "shared/runtime/gchelper.h"
@ -164,6 +165,9 @@ int main(void) {
#if MICROPY_PY_BLUETOOTH
mp_bluetooth_deinit();
#endif
#if MICROPY_PY_MACHINE_I2C_TARGET
mp_machine_i2c_target_deinit_all();
#endif
soft_timer_deinit();
machine_pin_irq_deinit();
gc_sweep_all();

View file

@ -24,15 +24,10 @@
* THE SOFTWARE.
*/
#include "py/obj.h"
#include "py/mphal.h"
#include "se_services.h"
#include "mbedtls_config_port.h"
#if defined(MBEDTLS_HAVE_TIME)
#include "shared/timeutils/timeutils.h"
#include "mbedtls/platform_time.h"
#endif
int mbedtls_hardware_poll(void *data, unsigned char *output, size_t len, size_t *olen) {
uint32_t val = 0;
int n = 0;
@ -52,14 +47,7 @@ int mbedtls_hardware_poll(void *data, unsigned char *output, size_t len, size_t
#if defined(MBEDTLS_HAVE_TIME)
time_t alif_mbedtls_time(time_t *timer) {
// TODO implement proper RTC time
unsigned int year = 2025;
unsigned int month = 1;
unsigned int date = 1;
unsigned int hours = 12;
unsigned int minutes = 0;
unsigned int seconds = 0;
return timeutils_seconds_since_epoch(year, month, date, hours, minutes, seconds);
return mp_hal_time_get(NULL);
}
#endif

39
ports/alif/modtime.c Normal file
View file

@ -0,0 +1,39 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2025 Damien P. George
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "py/mphal.h"
#include "shared/timeutils/timeutils.h"
// Get the localtime.
static void mp_time_localtime_get(timeutils_struct_time_t *tm) {
mp_timestamp_t s = mp_hal_time_get(NULL);
timeutils_seconds_since_epoch_to_struct_time(s, tm);
}
// Return the number of seconds since the Epoch.
static mp_obj_t mp_time_time_get(void) {
return mp_obj_new_int_from_uint(mp_hal_time_get(NULL));
}

View file

@ -119,7 +119,9 @@
#define MICROPY_PY_OS_UNAME (1)
#define MICROPY_PY_OS_URANDOM (1)
#define MICROPY_PY_RANDOM_SEED_INIT_FUNC (se_services_rand64())
#define MICROPY_PY_TIME (1)
#define MICROPY_PY_TIME_GMTIME_LOCALTIME_MKTIME (1)
#define MICROPY_PY_TIME_TIME_TIME_NS (1)
#define MICROPY_PY_TIME_INCLUDEFILE "ports/alif/modtime.c"
#define MICROPY_PY_MACHINE (1)
#define MICROPY_PY_MACHINE_INCLUDEFILE "ports/alif/modmachine.c"
#define MICROPY_PY_MACHINE_RESET (1)
@ -132,6 +134,12 @@
#define MICROPY_PY_MACHINE_PULSE (1)
#define MICROPY_PY_MACHINE_I2C (MICROPY_HW_ENABLE_HW_I2C)
#define MICROPY_PY_MACHINE_I2C_TRANSFER_WRITE1 (1)
#ifndef MICROPY_PY_MACHINE_I2C_TARGET
#define MICROPY_PY_MACHINE_I2C_TARGET (MICROPY_HW_ENABLE_HW_I2C)
#define MICROPY_PY_MACHINE_I2C_TARGET_INCLUDEFILE "ports/alif/machine_i2c_target.c"
#define MICROPY_PY_MACHINE_I2C_TARGET_MAX (4)
#define MICROPY_PY_MACHINE_I2C_TARGET_HARD_IRQ (1)
#endif
#define MICROPY_PY_MACHINE_SOFTI2C (1)
#define MICROPY_PY_MACHINE_SPI (1)
#define MICROPY_PY_MACHINE_SOFTSPI (1)

View file

@ -167,7 +167,9 @@ void mp_hal_delay_ms(mp_uint_t ms) {
}
uint64_t mp_hal_time_ns(void) {
return 0;
uint32_t microseconds;
uint32_t s = mp_hal_time_get(&microseconds);
return (uint64_t)s * 1000000000ULL + (uint64_t)microseconds * 1000ULL;
}
void mp_hal_pin_config(const machine_pin_obj_t *pin, uint32_t mode,

View file

@ -373,3 +373,5 @@ enum {
void mp_hal_generate_laa_mac(int idx, uint8_t buf[6]);
void mp_hal_get_mac(int idx, uint8_t buf[6]);
void mp_hal_get_mac_ascii(int idx, size_t chr_off, size_t chr_len, char *dest);
uint32_t mp_hal_time_get(uint32_t *microseconds);

View file

@ -37,6 +37,9 @@
// Python internal features
#define MICROPY_ERROR_REPORTING (MICROPY_ERROR_REPORTING_NONE)
// Fine control over Python builtins, classes, modules, etc.
#define MICROPY_PY_SYS (0)
// Type definitions for the specific machine
typedef int32_t mp_int_t; // must be pointer size

View file

@ -29,23 +29,10 @@
#include "shared/timeutils/timeutils.h"
#include "pybrtc.h"
// Return the localtime as an 8-tuple.
static mp_obj_t mp_time_localtime_get(void) {
timeutils_struct_time_t tm;
// Get the localtime.
static void mp_time_localtime_get(timeutils_struct_time_t *tm) {
// get the seconds from the RTC
timeutils_seconds_since_2000_to_struct_time(pyb_rtc_get_seconds(), &tm);
mp_obj_t tuple[8] = {
mp_obj_new_int(tm.tm_year),
mp_obj_new_int(tm.tm_mon),
mp_obj_new_int(tm.tm_mday),
mp_obj_new_int(tm.tm_hour),
mp_obj_new_int(tm.tm_min),
mp_obj_new_int(tm.tm_sec),
mp_obj_new_int(tm.tm_wday),
mp_obj_new_int(tm.tm_yday)
};
return mp_obj_new_tuple(8, tuple);
timeutils_seconds_since_2000_to_struct_time(pyb_rtc_get_seconds(), tm);
}
// Returns the number of seconds, as an integer, since the Epoch.

View file

@ -34,8 +34,13 @@ typedef long mp_off_t;
// Need to provide a declaration/definition of alloca()
#if defined(__FreeBSD__) || defined(__NetBSD__)
// BSD
#include <stdlib.h>
#elif defined(_WIN32)
// Windows
#include <malloc.h>
#else
// Other OS
#include <alloca.h>
#endif

View file

@ -5,8 +5,8 @@ This is a port of MicroPython to the Espressif ESP32 series of
microcontrollers. It uses the ESP-IDF framework and MicroPython runs as
a task under FreeRTOS.
Currently supports ESP32, ESP32-C3, ESP32-C6, ESP32-S2 and ESP32-S3
(ESP8266 is supported by a separate MicroPython port).
Currently supports ESP32, ESP32-C2 (aka ESP8684), ESP32-C3, ESP32-C6, ESP32-S2
and ESP32-S3 (ESP8266 is supported by a separate MicroPython port).
Supported features include:
- REPL (Python prompt) over UART0.

View file

@ -0,0 +1,22 @@
{
"deploy": [
"../deploy.md"
],
"deploy_options": {
"flash_offset": "0"
},
"docs": "",
"features": [
"BLE",
"External Flash",
"WiFi"
],
"images": [
"esp32c2_devkitmini.jpg"
],
"mcu": "esp32c2",
"product": "ESP32-C2",
"thumbnail": "",
"url": "https://www.espressif.com/en/products/modules",
"vendor": "Espressif"
}

View file

@ -0,0 +1,3 @@
The following files are firmware images that should work on most ESP32-C2-based
boards with at least 4MiB of flash and 26MHz crystal frequency. This includes
ESP8684-WROOM and ESP8684-MINI modules.

View file

@ -0,0 +1,35 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2025 Angus Gratton
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "py/mpconfig.h"
void GENERIC_C2_board_startup(void) {
// With a 26MHz crystal the ESP32-C2 ROM prints output at 74880 which is
// interpreted mostly as noise, then boot.py output and/or the REPL banner
// prints at the end of a line of noise unless we inject a newline here
printf("\n");
boardctrl_startup();
}

View file

@ -0,0 +1,14 @@
set(IDF_TARGET esp32c2)
set(SDKCONFIG_DEFAULTS
boards/sdkconfig.base
boards/sdkconfig.ble
boards/sdkconfig.c2
# C2 has unusably low free RAM without these optimisations
boards/sdkconfig.free_ram
)
set(MICROPY_SOURCE_BOARD
${MICROPY_BOARD_DIR}/board_init.c
)

View file

@ -0,0 +1,10 @@
// This configuration is for a generic ESP32C2 board with 4MiB (or more) of flash.
#define MICROPY_HW_BOARD_NAME "ESP32C2 module"
#define MICROPY_HW_MCU_NAME "ESP32C2"
#define MICROPY_HW_ENABLE_SDCARD (0)
#define MICROPY_PY_MACHINE_I2S (0)
#define MICROPY_BOARD_STARTUP GENERIC_C2_board_startup
void GENERIC_C2_board_startup(void);

View file

@ -117,7 +117,9 @@ CONFIG_ADC_CAL_LUT_ENABLE=y
CONFIG_UART_ISR_IN_IRAM=y
# IDF 5 deprecated
CONFIG_PCNT_SUPPRESS_DEPRECATE_WARN=y
CONFIG_RMT_SUPPRESS_DEPRECATE_WARN=y
CONFIG_TOUCH_SUPPRESS_DEPRECATE_WARN=y
CONFIG_ETH_USE_SPI_ETHERNET=y
CONFIG_ETH_SPI_ETHERNET_W5500=y
@ -125,9 +127,9 @@ CONFIG_ETH_SPI_ETHERNET_KSZ8851SNL=y
CONFIG_ETH_SPI_ETHERNET_DM9051=y
# Using newlib "nano" formatting saves size on SoCs where "nano" formatting
# functions are in ROM. Note some newer chips (c2,c6) have "full" newlib
# formatting in ROM instead and should override this, check
# ESP_ROM_HAS_NEWLIB_NANO_FORMAT.
# functions are in ROM. ESP32-C6 (and possibly other new chips) have "full"
# newlib formatting in ROM instead and should override this, check
# ESP_ROM_HAS_NEWLIB_NANO_FORMAT in ESP-IDF.
CONFIG_NEWLIB_NANO_FORMAT=y
# IRAM/DRAM split protection is a memory protection feature on some parts
@ -139,3 +141,7 @@ CONFIG_ESP_SYSTEM_PMP_IDRAM_SPLIT=n
# Further limit total sockets in TIME-WAIT when there are many short-lived
# connections.
CONFIG_LWIP_MAX_ACTIVE_TCP=12
# Enable new I2C slave API, and disable conflict check.
CONFIG_I2C_SKIP_LEGACY_CONFLICT_CHECK=y
CONFIG_I2C_ENABLE_SLAVE_DRIVER_VERSION_2=y

View file

@ -0,0 +1,17 @@
#
# Main XTAL Config
#
CONFIG_XTAL_FREQ_26=y
# CONFIG_XTAL_FREQ_40 is not set
CONFIG_XTAL_FREQ=26
# When using 26MHz crystal the baud rate defaults to 74880,
# same as ESP8266 - MicroPython uses 115200, so switch early
CONFIG_ESP_CONSOLE_UART_CUSTOM=y
CONFIG_ESP_CONSOLE_UART_BAUDRATE=115200
# Increase NimBLE stack size for functional BT
CONFIG_BT_NIMBLE_TASK_STACK_SIZE=5120
# Decrease mDNS stack size to save RAM
CONFIG_MDNS_TASK_STACK_SIZE=3072

View file

@ -0,0 +1,44 @@
# This is a collection of sdkconfig settings that frees RAM at runtime,
# at the expense of performance.
#
# Not all options will work on all SoC families, but adding this sdkconfig
# set to a board should increase the free memory.
#
# - Many options free IRAM, which on most ESP32 families leads to
# free DRAM at runtime (original ESP32 and S2 may not).
# - The other options reduce runtime DRAM usage from the heap.
#
# IMPORTANT: If you enable these config settings on a custom build then you may
# encounter bugs or crashes. If you choose to open a MicroPython bug report then
# please mention these config settings!
# Place functions in flash whenever possible to free IRAM
CONFIG_RINGBUF_PLACE_FUNCTIONS_INTO_FLASH=y
CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH=y
CONFIG_HEAP_PLACE_FUNCTION_INTO_FLASH=y
# Use the SPI flash functions in ROM (when available). This may limit flash chip
# support and cause issues with some flash chips. Each SoC family has different
# set of chip support baked into ROM.
CONFIG_SPI_FLASH_ROM_IMPL=y
# Run the Bluetooth controller from flash not IRAM
CONFIG_BT_CTRL_RUN_IN_FLASH_ONLY=y
# lwIP adjustments to limit runtime memory usage (at expense of performance, and/or
# a reduction in number of active connections).
CONFIG_LWIP_TCPIP_RECVMBOX_SIZE=16
CONFIG_LWIP_MAX_SOCKETS=6
CONFIG_LWIP_MAX_ACTIVE_TCP=8
# These lwIP values are recommended to scale relative to the Wi-Fi buffer numbers
CONFIG_LWIP_TCP_WND_DEFAULT=3072
CONFIG_LWIP_TCP_SND_BUF_DEFAULT=3072
# Wi-Fi adjustments to reduce peak runtime memory usage, at expense of peak
# performance
CONFIG_ESP_WIFI_STATIC_RX_BUFFER_NUM=8
CONFIG_ESP_WIFI_DYNAMIC_RX_BUFFER_NUM=12
CONFIG_ESP_WIFI_DYNAMIC_TX_BUFFER_NUM=12
CONFIG_ESP_WIFI_RX_MGMT_BUF_NUM_DEF=2
CONFIG_ESP_WIFI_RX_BA_WIN=12

View file

@ -127,6 +127,7 @@ list(APPEND MICROPY_SOURCE_PORT
modesp.c
esp32_nvs.c
esp32_partition.c
esp32_pcnt.c
esp32_rmt.c
esp32_ulp.c
modesp32.c
@ -264,7 +265,7 @@ target_include_directories(${MICROPY_TARGET} PUBLIC
# Add additional extmod and usermod components.
if (MICROPY_PY_BTREE)
target_link_libraries(${MICROPY_TARGET} micropy_extmod_btree)
target_link_libraries(${MICROPY_TARGET} $<TARGET_OBJECTS:micropy_extmod_btree>)
endif()
target_link_libraries(${MICROPY_TARGET} usermod)

513
ports/esp32/esp32_pcnt.c Normal file
View file

@ -0,0 +1,513 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2021-22 Jonathan Hogg
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "py/runtime.h"
#include "py/mphal.h"
#include "py/obj.h"
#if MICROPY_PY_ESP32_PCNT
#include "shared/runtime/mpirq.h"
#include "modesp32.h"
#include "driver/pcnt.h"
#if !MICROPY_ENABLE_FINALISER
#error "esp32.PCNT requires MICROPY_ENABLE_FINALISER."
#endif
typedef struct _esp32_pcnt_irq_obj_t {
mp_irq_obj_t base;
uint32_t flags;
uint32_t trigger;
} esp32_pcnt_irq_obj_t;
typedef struct _esp32_pcnt_obj_t {
mp_obj_base_t base;
pcnt_unit_t unit;
esp32_pcnt_irq_obj_t *irq;
struct _esp32_pcnt_obj_t *next;
} esp32_pcnt_obj_t;
// Linked list of PCNT units.
MP_REGISTER_ROOT_POINTER(struct _esp32_pcnt_obj_t *esp32_pcnt_obj_head);
// Once off installation of the PCNT ISR service (using the default service).
// Persists across soft reset.
static bool pcnt_isr_service_installed = false;
static mp_obj_t esp32_pcnt_deinit(mp_obj_t self_in);
void esp32_pcnt_deinit_all(void) {
esp32_pcnt_obj_t **pcnt = &MP_STATE_PORT(esp32_pcnt_obj_head);
while (*pcnt != NULL) {
esp32_pcnt_deinit(MP_OBJ_FROM_PTR(*pcnt));
*pcnt = (*pcnt)->next;
}
}
static void esp32_pcnt_init_helper(esp32_pcnt_obj_t *self, size_t n_pos_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum {
ARG_channel,
ARG_pin,
ARG_rising,
ARG_falling,
ARG_mode_pin,
ARG_mode_low,
ARG_mode_high,
ARG_min,
ARG_max,
ARG_filter,
ARG_threshold0,
ARG_threshold1,
ARG_value,
};
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_channel, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
// Applies to the channel.
{ MP_QSTR_pin, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
{ MP_QSTR_rising, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
{ MP_QSTR_falling, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
{ MP_QSTR_mode_pin, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
{ MP_QSTR_mode_low, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
{ MP_QSTR_mode_high, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
// Applies to the whole unit.
{ MP_QSTR_min, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
{ MP_QSTR_max, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
{ MP_QSTR_filter, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
{ MP_QSTR_threshold0, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
{ MP_QSTR_threshold1, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
// Implicitly zero if min, max, threshold0/1 are set.
{ MP_QSTR_value, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_pos_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
// The pin/mode_pin, rising, falling, mode_low, mode_high args all apply
// to the channel (defaults to channel zero).
mp_uint_t channel = args[ARG_channel].u_int;
if (channel >= PCNT_CHANNEL_MAX) {
mp_raise_ValueError(MP_ERROR_TEXT("channel"));
}
if (args[ARG_pin].u_obj != MP_OBJ_NULL || args[ARG_mode_pin].u_obj != MP_OBJ_NULL) {
// If you set mode_pin, you must also set pin.
if (args[ARG_pin].u_obj == MP_OBJ_NULL) {
mp_raise_ValueError(MP_ERROR_TEXT("pin"));
}
mp_hal_pin_obj_t pin = PCNT_PIN_NOT_USED;
mp_hal_pin_obj_t mode_pin = PCNT_PIN_NOT_USED;
// Set to None to disable pin/mode_pin.
if (args[ARG_pin].u_obj != mp_const_none) {
pin = mp_hal_get_pin_obj(args[ARG_pin].u_obj);
}
if (args[ARG_mode_pin].u_obj != MP_OBJ_NULL && args[ARG_mode_pin].u_obj != mp_const_none) {
mode_pin = mp_hal_get_pin_obj(args[ARG_mode_pin].u_obj);
}
pcnt_set_pin(self->unit, channel, pin, mode_pin);
}
if (
args[ARG_rising].u_obj != MP_OBJ_NULL || args[ARG_falling].u_obj != MP_OBJ_NULL ||
args[ARG_mode_low].u_obj != MP_OBJ_NULL || args[ARG_mode_high].u_obj != MP_OBJ_NULL
) {
mp_uint_t rising = args[ARG_rising].u_obj == MP_OBJ_NULL ? PCNT_COUNT_DIS : mp_obj_get_int(args[ARG_rising].u_obj);
mp_uint_t falling = args[ARG_falling].u_obj == MP_OBJ_NULL ? PCNT_COUNT_DIS : mp_obj_get_int(args[ARG_falling].u_obj);
mp_uint_t mode_low = args[ARG_mode_low].u_obj == MP_OBJ_NULL ? PCNT_MODE_KEEP : mp_obj_get_int(args[ARG_mode_low].u_obj);
mp_uint_t mode_high = args[ARG_mode_high].u_obj == MP_OBJ_NULL ? PCNT_MODE_KEEP : mp_obj_get_int(args[ARG_mode_high].u_obj);
if (rising >= PCNT_COUNT_MAX) {
mp_raise_ValueError(MP_ERROR_TEXT("rising"));
}
if (falling >= PCNT_COUNT_MAX) {
mp_raise_ValueError(MP_ERROR_TEXT("falling"));
}
if (mode_low >= PCNT_MODE_MAX) {
mp_raise_ValueError(MP_ERROR_TEXT("mode_low"));
}
if (mode_high >= PCNT_MODE_MAX) {
mp_raise_ValueError(MP_ERROR_TEXT("mode_high"));
}
pcnt_set_mode(self->unit, channel, rising, falling, mode_high, mode_low);
}
// The rest of the arguments apply to the whole unit.
if (args[ARG_filter].u_obj != MP_OBJ_NULL) {
mp_uint_t filter = mp_obj_get_int(args[ARG_filter].u_obj);
if (filter > 1023) {
mp_raise_ValueError(MP_ERROR_TEXT("filter"));
}
if (filter) {
check_esp_err(pcnt_set_filter_value(self->unit, filter));
check_esp_err(pcnt_filter_enable(self->unit));
} else {
check_esp_err(pcnt_filter_disable(self->unit));
}
}
bool clear = false;
if (args[ARG_value].u_obj != MP_OBJ_NULL) {
mp_int_t value = mp_obj_get_int(args[ARG_value].u_obj);
if (value != 0) {
mp_raise_ValueError(MP_ERROR_TEXT("value"));
}
clear = true;
}
if (args[ARG_min].u_obj != MP_OBJ_NULL) {
mp_int_t minimum = mp_obj_get_int(args[ARG_min].u_obj);
if (minimum < -32768 || minimum > 0) {
mp_raise_ValueError(MP_ERROR_TEXT("minimum"));
}
check_esp_err(pcnt_set_event_value(self->unit, PCNT_EVT_L_LIM, minimum));
clear = true;
}
if (args[ARG_max].u_obj != MP_OBJ_NULL) {
mp_int_t maximum = mp_obj_get_int(args[ARG_max].u_obj);
if (maximum < 0 || maximum > 32767) {
mp_raise_ValueError(MP_ERROR_TEXT("maximum"));
}
check_esp_err(pcnt_set_event_value(self->unit, PCNT_EVT_H_LIM, maximum));
clear = true;
}
if (args[ARG_threshold0].u_obj != MP_OBJ_NULL) {
mp_int_t threshold0 = mp_obj_get_int(args[ARG_threshold0].u_obj);
if (threshold0 < -32768 || threshold0 > 32767) {
mp_raise_ValueError(MP_ERROR_TEXT("threshold0"));
}
check_esp_err(pcnt_set_event_value(self->unit, PCNT_EVT_THRES_0, threshold0));
clear = true;
}
if (args[ARG_threshold1].u_obj != MP_OBJ_NULL) {
mp_int_t threshold1 = mp_obj_get_int(args[ARG_threshold1].u_obj);
if (threshold1 < -32768 || threshold1 > 32767) {
mp_raise_ValueError(MP_ERROR_TEXT("threshold1"));
}
check_esp_err(pcnt_set_event_value(self->unit, PCNT_EVT_THRES_1, threshold1));
clear = true;
}
if (clear) {
check_esp_err(pcnt_counter_clear(self->unit));
}
}
// Disable any events, and remove the ISR handler for this unit.
static void esp32_pcnt_disable_events_for_unit(esp32_pcnt_obj_t *self) {
if (!self->irq) {
return;
}
// Disable all possible events and remove the ISR.
for (pcnt_evt_type_t evt_type = PCNT_EVT_THRES_1; evt_type <= PCNT_EVT_ZERO; evt_type <<= 1) {
check_esp_err(pcnt_event_disable(self->unit, evt_type));
}
check_esp_err(pcnt_isr_handler_remove(self->unit));
// Clear IRQ object state.
self->irq->base.handler = mp_const_none;
self->irq->trigger = 0;
}
static mp_obj_t esp32_pcnt_make_new(const mp_obj_type_t *type, size_t n_pos_args, size_t n_kw_args, const mp_obj_t *args) {
if (n_pos_args < 1) {
mp_raise_TypeError(MP_ERROR_TEXT("id"));
}
pcnt_unit_t unit = mp_obj_get_int(args[0]);
if (unit < 0 || unit >= PCNT_UNIT_MAX) {
mp_raise_ValueError(MP_ERROR_TEXT("invalid id"));
}
// Try and find an existing instance for this unit.
esp32_pcnt_obj_t *self = MP_STATE_PORT(esp32_pcnt_obj_head);
while (self) {
if (self->unit == unit) {
break;
}
self = self->next;
}
if (!self) {
// Unused unit, create a new esp32_pcnt_obj_t instance and put it at
// the head of the list.
self = mp_obj_malloc(esp32_pcnt_obj_t, &esp32_pcnt_type);
self->unit = unit;
self->irq = NULL;
self->next = MP_STATE_PORT(esp32_pcnt_obj_head);
MP_STATE_PORT(esp32_pcnt_obj_head) = self;
// Ensure the unit is in a known (deactivated) state.
esp32_pcnt_deinit(MP_OBJ_FROM_PTR(self));
}
mp_map_t kw_args;
mp_map_init_fixed_table(&kw_args, n_kw_args, args + n_pos_args);
esp32_pcnt_init_helper(self, 0, args + n_pos_args, &kw_args);
// Ensure the global PCNT ISR service is installed.
if (!pcnt_isr_service_installed) {
check_esp_err(pcnt_isr_service_install(ESP_INTR_FLAG_IRAM));
pcnt_isr_service_installed = true;
}
// And enable for this unit.
check_esp_err(pcnt_intr_enable(self->unit));
return MP_OBJ_FROM_PTR(self);
}
static void esp32_pcnt_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
esp32_pcnt_obj_t *self = MP_OBJ_TO_PTR(self_in);
mp_printf(print, "PCNT(%u)", self->unit);
}
static mp_obj_t esp32_pcnt_init(size_t n_pos_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
esp32_pcnt_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
esp32_pcnt_init_helper(self, n_pos_args - 1, pos_args + 1, kw_args);
return mp_const_none;
}
static MP_DEFINE_CONST_FUN_OBJ_KW(esp32_pcnt_init_obj, 1, esp32_pcnt_init);
static mp_obj_t esp32_pcnt_deinit(mp_obj_t self_in) {
esp32_pcnt_obj_t *self = MP_OBJ_TO_PTR(self_in);
// Remove IRQ and events.
esp32_pcnt_disable_events_for_unit(self);
// Deactivate both channels.
pcnt_config_t channel_config = {
.unit = self->unit,
.pulse_gpio_num = PCNT_PIN_NOT_USED,
.pos_mode = PCNT_COUNT_DIS,
.neg_mode = PCNT_COUNT_DIS,
.ctrl_gpio_num = PCNT_PIN_NOT_USED,
.lctrl_mode = PCNT_MODE_KEEP,
.hctrl_mode = PCNT_MODE_KEEP,
.counter_l_lim = 0,
.counter_h_lim = 0,
};
for (pcnt_channel_t channel = 0; channel <= 1; ++channel) {
channel_config.channel = channel;
check_esp_err(pcnt_unit_config(&channel_config));
}
// Disable filters & thresholds, pause & clear.
check_esp_err(pcnt_filter_disable(self->unit));
check_esp_err(pcnt_set_event_value(self->unit, PCNT_EVT_THRES_0, 0));
check_esp_err(pcnt_set_event_value(self->unit, PCNT_EVT_THRES_1, 0));
check_esp_err(pcnt_counter_pause(self->unit));
check_esp_err(pcnt_counter_clear(self->unit));
return mp_const_none;
}
static MP_DEFINE_CONST_FUN_OBJ_1(esp32_pcnt_deinit_obj, esp32_pcnt_deinit);
static mp_obj_t esp32_pcnt_value(size_t n_args, const mp_obj_t *pos_args) {
esp32_pcnt_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
// Optionally use pcnt.value(True) to clear the counter but only support a
// value of zero. Note: This can lead to skipped counts.
if (n_args == 2) {
if (mp_obj_get_int(pos_args[1]) != 0) {
mp_raise_ValueError(MP_ERROR_TEXT("value"));
}
}
// This loop ensures that the caller's state (as inferred from IRQs, e.g.
// under/overflow) corresponds to the returned value, by synchronously
// flushing all pending IRQs.
int16_t value;
while (true) {
check_esp_err(pcnt_get_counter_value(self->unit, &value));
if (self->irq && self->irq->flags && self->irq->base.handler != mp_const_none) {
// The handler must call irq.flags() to clear self->irq->base.flags,
// otherwise this will be an infinite loop.
mp_call_function_1(self->irq->base.handler, self->irq->base.parent);
} else {
break;
}
}
if (n_args == 2) {
// Value was given, and we've already checked it was zero, so clear
// the counter.
check_esp_err(pcnt_counter_clear(self->unit));
}
return MP_OBJ_NEW_SMALL_INT(value);
}
static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(esp32_pcnt_value_obj, 1, 2, esp32_pcnt_value);
static mp_uint_t esp32_pcnt_irq_trigger(mp_obj_t self_in, mp_uint_t new_trigger) {
esp32_pcnt_obj_t *self = MP_OBJ_TO_PTR(self_in);
self->irq->trigger = new_trigger;
for (pcnt_evt_type_t evt_type = PCNT_EVT_THRES_1; evt_type <= PCNT_EVT_ZERO; evt_type <<= 1) {
if (new_trigger & evt_type) {
pcnt_event_enable(self->unit, evt_type);
} else {
pcnt_event_disable(self->unit, evt_type);
}
}
return 0;
}
static mp_uint_t esp32_pcnt_irq_info(mp_obj_t self_in, mp_uint_t info_type) {
esp32_pcnt_obj_t *self = MP_OBJ_TO_PTR(self_in);
if (info_type == MP_IRQ_INFO_FLAGS) {
// Atomically get-and-clear the flags.
mp_uint_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION();
mp_uint_t flags = self->irq->flags;
self->irq->flags = 0;
MICROPY_END_ATOMIC_SECTION(atomic_state);
return flags;
} else if (info_type == MP_IRQ_INFO_TRIGGERS) {
return self->irq->trigger;
}
return 0;
}
static const mp_irq_methods_t esp32_pcnt_irq_methods = {
.trigger = esp32_pcnt_irq_trigger,
.info = esp32_pcnt_irq_info,
};
static IRAM_ATTR void esp32_pcnt_intr_handler(void *arg) {
esp32_pcnt_obj_t *self = (esp32_pcnt_obj_t *)arg;
pcnt_unit_t unit = self->unit;
uint32_t status;
pcnt_get_event_status(unit, &status);
mp_uint_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION();
self->irq->flags |= status;
MICROPY_END_ATOMIC_SECTION(atomic_state);
mp_irq_handler(&self->irq->base);
}
static mp_obj_t esp32_pcnt_irq(size_t n_pos_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_handler, ARG_trigger };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_handler, MP_ARG_OBJ, {.u_obj = mp_const_none} },
{ MP_QSTR_trigger, MP_ARG_INT, {.u_int = PCNT_EVT_ZERO} },
};
esp32_pcnt_obj_t *self = pos_args[0];
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_pos_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
if (!self->irq) {
// Create IRQ object if necessary. This instance persists across a
// de-init.
self->irq = mp_obj_malloc(esp32_pcnt_irq_obj_t, &mp_irq_type);
self->irq->base.methods = (mp_irq_methods_t *)&esp32_pcnt_irq_methods;
self->irq->base.parent = MP_OBJ_FROM_PTR(self);
self->irq->base.ishard = false;
self->irq->base.handler = mp_const_none;
self->irq->trigger = 0;
}
if (n_pos_args > 1 || kw_args->used != 0) {
// Update IRQ data.
mp_obj_t handler = args[ARG_handler].u_obj;
mp_uint_t trigger = args[ARG_trigger].u_int;
if (trigger < PCNT_EVT_THRES_1 || trigger >= (PCNT_EVT_ZERO << 1)) {
mp_raise_ValueError(MP_ERROR_TEXT("trigger"));
}
if (handler != mp_const_none) {
self->irq->base.handler = handler;
self->irq->trigger = trigger;
pcnt_isr_handler_add(self->unit, esp32_pcnt_intr_handler, (void *)self);
esp32_pcnt_irq_trigger(MP_OBJ_FROM_PTR(self), trigger);
} else {
// Remove the ISR, disable all events, clear the IRQ object state.
esp32_pcnt_disable_events_for_unit(self);
}
}
return MP_OBJ_FROM_PTR(self->irq);
}
static MP_DEFINE_CONST_FUN_OBJ_KW(esp32_pcnt_irq_obj, 1, esp32_pcnt_irq);
static mp_obj_t esp32_pcnt_start(mp_obj_t self_in) {
esp32_pcnt_obj_t *self = MP_OBJ_TO_PTR(self_in);
check_esp_err(pcnt_counter_resume(self->unit));
return mp_const_none;
}
static MP_DEFINE_CONST_FUN_OBJ_1(esp32_pcnt_start_obj, esp32_pcnt_start);
static mp_obj_t esp32_pcnt_stop(mp_obj_t self_in) {
esp32_pcnt_obj_t *self = MP_OBJ_TO_PTR(self_in);
check_esp_err(pcnt_counter_pause(self->unit));
return mp_const_none;
}
static MP_DEFINE_CONST_FUN_OBJ_1(esp32_pcnt_stop_obj, esp32_pcnt_stop);
static const mp_rom_map_elem_t esp32_pcnt_locals_dict_table[] = {
// Methods
{ MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&esp32_pcnt_init_obj) },
{ MP_ROM_QSTR(MP_QSTR_value), MP_ROM_PTR(&esp32_pcnt_value_obj) },
{ MP_ROM_QSTR(MP_QSTR_irq), MP_ROM_PTR(&esp32_pcnt_irq_obj) },
{ MP_ROM_QSTR(MP_QSTR_start), MP_ROM_PTR(&esp32_pcnt_start_obj) },
{ MP_ROM_QSTR(MP_QSTR_stop), MP_ROM_PTR(&esp32_pcnt_stop_obj) },
{ MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&esp32_pcnt_deinit_obj) },
{ MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&esp32_pcnt_deinit_obj) },
// Constants
{ MP_ROM_QSTR(MP_QSTR_IGNORE), MP_ROM_INT(PCNT_COUNT_DIS) },
{ MP_ROM_QSTR(MP_QSTR_INCREMENT), MP_ROM_INT(PCNT_COUNT_INC) },
{ MP_ROM_QSTR(MP_QSTR_DECREMENT), MP_ROM_INT(PCNT_COUNT_DEC) },
{ MP_ROM_QSTR(MP_QSTR_NORMAL), MP_ROM_INT(PCNT_MODE_KEEP) },
{ MP_ROM_QSTR(MP_QSTR_REVERSE), MP_ROM_INT(PCNT_MODE_REVERSE) },
{ MP_ROM_QSTR(MP_QSTR_HOLD), MP_ROM_INT(PCNT_MODE_DISABLE) },
{ MP_ROM_QSTR(MP_QSTR_IRQ_ZERO), MP_ROM_INT(PCNT_EVT_ZERO) },
{ MP_ROM_QSTR(MP_QSTR_IRQ_THRESHOLD0), MP_ROM_INT(PCNT_EVT_THRES_0) },
{ MP_ROM_QSTR(MP_QSTR_IRQ_THRESHOLD1), MP_ROM_INT(PCNT_EVT_THRES_1) },
{ MP_ROM_QSTR(MP_QSTR_IRQ_MIN), MP_ROM_INT(PCNT_EVT_L_LIM) },
{ MP_ROM_QSTR(MP_QSTR_IRQ_MAX), MP_ROM_INT(PCNT_EVT_H_LIM) },
};
static MP_DEFINE_CONST_DICT(esp32_pcnt_locals_dict, esp32_pcnt_locals_dict_table);
MP_DEFINE_CONST_OBJ_TYPE(
esp32_pcnt_type,
MP_QSTR_PCNT,
MP_TYPE_FLAG_NONE,
make_new, esp32_pcnt_make_new,
print, esp32_pcnt_print,
locals_dict, &esp32_pcnt_locals_dict
);
#endif // MICROPY_PY_ESP32_PCNT

View file

@ -30,6 +30,8 @@
#include "modesp32.h"
#include "esp_task.h"
#if SOC_RMT_SUPPORTED
#include "driver/rmt.h"
// This exposes the ESP32's RMT module to MicroPython. RMT is provided by the Espressif ESP-IDF:
@ -105,7 +107,7 @@ esp_err_t rmt_driver_install_core1(uint8_t channel_id) {
return rmt_driver_install(channel_id, 0, 0);
}
#endif
#endif // MP_TASK_COREID==0
static mp_obj_t esp32_rmt_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
static const mp_arg_t allowed_args[] = {
@ -387,3 +389,5 @@ MP_DEFINE_CONST_OBJ_TYPE(
print, esp32_rmt_print,
locals_dict, &esp32_rmt_locals_dict
);
#endif // SOC_RMT_SUPPORTED

View file

@ -87,6 +87,12 @@ static const machine_adc_obj_t madc_obj[] = {
{{&machine_adc_type}, ADCBLOCK2, ADC_CHANNEL_7, GPIO_NUM_27},
{{&machine_adc_type}, ADCBLOCK2, ADC_CHANNEL_8, GPIO_NUM_25},
{{&machine_adc_type}, ADCBLOCK2, ADC_CHANNEL_9, GPIO_NUM_26},
#elif CONFIG_IDF_TARGET_ESP32C2
{{&machine_adc_type}, ADCBLOCK1, ADC_CHANNEL_0, GPIO_NUM_0},
{{&machine_adc_type}, ADCBLOCK1, ADC_CHANNEL_1, GPIO_NUM_1},
{{&machine_adc_type}, ADCBLOCK1, ADC_CHANNEL_2, GPIO_NUM_2},
{{&machine_adc_type}, ADCBLOCK1, ADC_CHANNEL_3, GPIO_NUM_3},
{{&machine_adc_type}, ADCBLOCK1, ADC_CHANNEL_4, GPIO_NUM_4},
#elif CONFIG_IDF_TARGET_ESP32C3
{{&machine_adc_type}, ADCBLOCK1, ADC_CHANNEL_0, GPIO_NUM_0},
{{&machine_adc_type}, ADCBLOCK1, ADC_CHANNEL_1, GPIO_NUM_1},

View file

@ -90,6 +90,7 @@ static void IRAM_ATTR machine_bitstream_high_low_bitbang(mp_hal_pin_obj_t pin, u
mp_hal_quiet_timing_exit(irq_state);
}
#if SOC_RMT_SUPPORTED
/******************************************************************************/
// RMT implementation
@ -172,16 +173,18 @@ static void machine_bitstream_high_low_rmt(mp_hal_pin_obj_t pin, uint32_t *timin
// Cancel RMT output to GPIO pin.
esp_rom_gpio_connect_out_signal(pin, SIG_GPIO_OUT_IDX, false, false);
}
#endif
/******************************************************************************/
// Interface to machine.bitstream
void machine_bitstream_high_low(mp_hal_pin_obj_t pin, uint32_t *timing_ns, const uint8_t *buf, size_t len) {
if (esp32_rmt_bitstream_channel_id < 0) {
machine_bitstream_high_low_bitbang(pin, timing_ns, buf, len);
} else {
#if SOC_RMT_SUPPORTED
if (esp32_rmt_bitstream_channel_id >= 0) {
machine_bitstream_high_low_rmt(pin, timing_ns, buf, len, esp32_rmt_bitstream_channel_id);
return;
}
#endif
machine_bitstream_high_low_bitbang(pin, timing_ns, buf, len);
}
#endif // MICROPY_PY_MACHINE_BITSTREAM

View file

@ -28,34 +28,19 @@
#include "py/mphal.h"
#include "py/mperrno.h"
#include "extmod/modmachine.h"
#include "machine_i2c.h"
#include "driver/i2c.h"
#include "hal/i2c_ll.h"
#if MICROPY_PY_MACHINE_I2C || MICROPY_PY_MACHINE_SOFTI2C
#ifndef MICROPY_HW_I2C0_SCL
#if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3
#define MICROPY_HW_I2C0_SCL (GPIO_NUM_9)
#define MICROPY_HW_I2C0_SDA (GPIO_NUM_8)
#else
#define MICROPY_HW_I2C0_SCL (GPIO_NUM_18)
#define MICROPY_HW_I2C0_SDA (GPIO_NUM_19)
#endif
#endif
#ifndef MICROPY_HW_I2C1_SCL
#if CONFIG_IDF_TARGET_ESP32
#define MICROPY_HW_I2C1_SCL (GPIO_NUM_25)
#define MICROPY_HW_I2C1_SDA (GPIO_NUM_26)
#else
#define MICROPY_HW_I2C1_SCL (GPIO_NUM_9)
#define MICROPY_HW_I2C1_SDA (GPIO_NUM_8)
#endif
#endif
#if SOC_I2C_SUPPORT_XTAL
#define I2C_SCLK_FREQ XTAL_CLK_FREQ
#if CONFIG_XTAL_FREQ > 0
#define I2C_SCLK_FREQ (CONFIG_XTAL_FREQ * 1000000)
#else
#error "I2C uses XTAL but no configured freq"
#endif // CONFIG_XTAL_FREQ
#elif SOC_I2C_SUPPORT_APB
#define I2C_SCLK_FREQ APB_CLK_FREQ
#else

52
ports/esp32/machine_i2c.h Normal file
View file

@ -0,0 +1,52 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2019-2025 Damien P. George
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef MICROPY_INCLUDED_ESP32_MACHINE_I2C_H
#define MICROPY_INCLUDED_ESP32_MACHINE_I2C_H
// Configure default I2C0 pins.
#ifndef MICROPY_HW_I2C0_SCL
#if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3
#define MICROPY_HW_I2C0_SCL (GPIO_NUM_9)
#define MICROPY_HW_I2C0_SDA (GPIO_NUM_8)
#else
#define MICROPY_HW_I2C0_SCL (GPIO_NUM_18)
#define MICROPY_HW_I2C0_SDA (GPIO_NUM_19)
#endif
#endif
// Configure default I2C1 pins.
#ifndef MICROPY_HW_I2C1_SCL
#if CONFIG_IDF_TARGET_ESP32
#define MICROPY_HW_I2C1_SCL (GPIO_NUM_25)
#define MICROPY_HW_I2C1_SDA (GPIO_NUM_26)
#else
#define MICROPY_HW_I2C1_SCL (GPIO_NUM_9)
#define MICROPY_HW_I2C1_SDA (GPIO_NUM_8)
#endif
#endif
#endif // MICROPY_INCLUDED_ESP32_MACHINE_I2C_H

View file

@ -0,0 +1,225 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2025 Damien P. George
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
// This file is never compiled standalone, it's included directly from
// extmod/machine_i2c_target.c via MICROPY_PY_MACHINE_I2C_TARGET_INCLUDEFILE.
#include "machine_i2c.h"
#include "driver/i2c_slave.h"
// These headers are needed to call i2c_ll_txfifo_rst().
#include "hal/i2c_ll.h"
#include "../i2c_private.h"
typedef struct _machine_i2c_target_obj_t {
mp_obj_base_t base;
i2c_slave_dev_handle_t handle;
i2c_slave_config_t config;
uint8_t state;
bool stop_pending;
bool irq_active;
int index;
const i2c_slave_rx_done_event_data_t *rx_done_event_data;
} machine_i2c_target_obj_t;
static machine_i2c_target_obj_t machine_i2c_target_obj[I2C_NUM_MAX];
/******************************************************************************/
// ESP-IDF hardware bindings
// Called when the controller is about to read from the TX/send buffer.
static bool i2c_slave_request_cb(i2c_slave_dev_handle_t i2c_slave, const i2c_slave_request_event_data_t *evt_data, void *arg) {
machine_i2c_target_obj_t *self = arg;
machine_i2c_target_data_t *data = &machine_i2c_target_data[self->config.i2c_port];
// Flush hardware TX FIFO to get rid of any data from a previous read.
i2c_ll_txfifo_rst(self->handle->base->hal.dev);
// Perform an entire read transaction, including start, read and stop events.
// We don't know how much data the controller will read, so write the entire
// memory buffer to the TX FIFO.
machine_i2c_target_data_addr_match(data, true);
for (uint32_t i = 0; i < data->mem_len; ++i) {
machine_i2c_target_data_read_request(self, data);
}
machine_i2c_target_data_restart_or_stop(data);
// A higher priority task was not woken up.
return false;
}
// Called when the controller has written into the RX/receive buffer.
static bool i2c_slave_receive_cb(i2c_slave_dev_handle_t i2c_slave, const i2c_slave_rx_done_event_data_t *evt_data, void *arg) {
machine_i2c_target_obj_t *self = arg;
machine_i2c_target_data_t *data = &machine_i2c_target_data[self->config.i2c_port];
// Perform an entire write transaction, including start, read and stop events.
machine_i2c_target_data_addr_match(data, false);
self->index = 0;
self->rx_done_event_data = evt_data;
while (self->index < self->rx_done_event_data->length) {
machine_i2c_target_data_write_request(self, data);
}
machine_i2c_target_data_restart_or_stop(data);
// A higher priority task was not woken up.
return false;
}
static void i2c_target_init(machine_i2c_target_obj_t *self, machine_i2c_target_data_t *data, uint32_t addr, uint32_t addrsize, bool first_init) {
if (!first_init && self->handle != NULL) {
i2c_del_slave_device(self->handle);
self->handle = NULL;
}
self->config.clk_source = I2C_CLK_SRC_DEFAULT;
self->config.slave_addr = addr;
self->config.send_buf_depth = data->mem_len;
self->config.receive_buf_depth = data->mem_len;
if (addrsize == 7) {
self->config.addr_bit_len = I2C_ADDR_BIT_LEN_7;
} else {
#if SOC_I2C_SUPPORT_10BIT_ADDR
self->config.addr_bit_len = I2C_ADDR_BIT_LEN_10;
#else
mp_raise_ValueError(MP_ERROR_TEXT("10-bit address unsupported"));
#endif
}
self->config.intr_priority = 0; // 0 selects the default
self->config.flags.allow_pd = 0;
self->config.flags.enable_internal_pullup = 1;
ESP_ERROR_CHECK(i2c_new_slave_device(&self->config, &self->handle));
i2c_slave_event_callbacks_t cbs = {
.on_receive = i2c_slave_receive_cb,
.on_request = i2c_slave_request_cb,
};
ESP_ERROR_CHECK(i2c_slave_register_event_callbacks(self->handle, &cbs, self));
}
/******************************************************************************/
// I2CTarget port implementation
static inline size_t mp_machine_i2c_target_get_index(machine_i2c_target_obj_t *self) {
return self->config.i2c_port;
}
static void mp_machine_i2c_target_event_callback(machine_i2c_target_irq_obj_t *irq) {
mp_irq_handler(&irq->base);
}
static size_t mp_machine_i2c_target_read_bytes(machine_i2c_target_obj_t *self, size_t len, uint8_t *buf) {
size_t i = 0;
while (i < len && self->index < self->rx_done_event_data->length) {
buf[i++] = self->rx_done_event_data->buffer[self->index++];
}
return i;
}
static size_t mp_machine_i2c_target_write_bytes(machine_i2c_target_obj_t *self, size_t len, const uint8_t *buf) {
uint32_t write_len;
i2c_slave_write(self->handle, buf, len, &write_len, 1000);
return write_len;
}
static void mp_machine_i2c_target_irq_config(machine_i2c_target_obj_t *self, unsigned int trigger) {
}
static mp_obj_t mp_machine_i2c_target_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
enum { ARG_id, ARG_addr, ARG_addrsize, ARG_mem, ARG_mem_addrsize, ARG_scl, ARG_sda };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_id, MP_ARG_INT, {.u_int = 0} },
{ MP_QSTR_addr, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_addrsize, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 7} },
{ MP_QSTR_mem, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
{ MP_QSTR_mem_addrsize, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 8} },
{ MP_QSTR_scl, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
{ MP_QSTR_sda, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
};
// Parse args.
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
int i2c_id = args[ARG_id].u_int;
// Check if the I2C bus is valid
if (i2c_id < 0 || i2c_id >= MP_ARRAY_SIZE(machine_i2c_target_obj)) {
mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("I2CTarget(%d) doesn't exist"), i2c_id);
}
// Get static peripheral object.
machine_i2c_target_obj_t *self = &machine_i2c_target_obj[i2c_id];
bool first_init = false;
if (self->base.type == NULL) {
// Created for the first time, set default pins
self->base.type = &machine_i2c_target_type;
self->config.i2c_port = i2c_id;
if (self->config.i2c_port == 0) {
self->config.scl_io_num = MICROPY_HW_I2C0_SCL;
self->config.sda_io_num = MICROPY_HW_I2C0_SDA;
} else {
self->config.scl_io_num = MICROPY_HW_I2C1_SCL;
self->config.sda_io_num = MICROPY_HW_I2C1_SDA;
}
first_init = true;
}
// Initialise data.
self->state = STATE_IDLE;
self->stop_pending = false;
self->irq_active = false;
MP_STATE_PORT(machine_i2c_target_mem_obj)[i2c_id] = args[ARG_mem].u_obj;
machine_i2c_target_data_t *data = &machine_i2c_target_data[i2c_id];
machine_i2c_target_data_init(data, args[ARG_mem].u_obj, args[ARG_mem_addrsize].u_int);
// Set SCL/SDA pins if configured.
if (args[ARG_scl].u_obj != mp_const_none) {
self->config.scl_io_num = mp_hal_get_pin_obj(args[ARG_scl].u_obj);
}
if (args[ARG_sda].u_obj != mp_const_none) {
self->config.sda_io_num = mp_hal_get_pin_obj(args[ARG_sda].u_obj);
}
// Initialise the I2C target.
i2c_target_init(self, data, args[ARG_addr].u_int, args[ARG_addrsize].u_int, first_init);
return MP_OBJ_FROM_PTR(self);
}
static void mp_machine_i2c_target_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
machine_i2c_target_obj_t *self = MP_OBJ_TO_PTR(self_in);
mp_printf(print, "I2CTarget(%u, addr=%u, scl=%u, sda=%u)",
self->config.i2c_port, self->config.slave_addr, self->config.scl_io_num, self->config.sda_io_num);
}
static void mp_machine_i2c_target_deinit(machine_i2c_target_obj_t *self) {
if (self->handle != NULL) {
i2c_del_slave_device(self->handle);
self->handle = NULL;
}
}

View file

@ -152,7 +152,7 @@ static mp_obj_t machine_pin_obj_init_helper(const machine_pin_obj_t *self, size_
// reset the pin to digital if this is a mode-setting init (grab it back from ADC)
if (args[ARG_mode].u_obj != mp_const_none) {
if (rtc_gpio_is_valid_gpio(index)) {
#if !(CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32C6)
#if !(CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32C6)
rtc_gpio_deinit(index);
#endif
}

View file

@ -71,6 +71,23 @@
#define MICROPY_HW_ENABLE_GPIO38 (1)
#define MICROPY_HW_ENABLE_GPIO39 (1)
#elif CONFIG_IDF_TARGET_ESP32C2
#define MICROPY_HW_ENABLE_GPIO0 (1)
#define MICROPY_HW_ENABLE_GPIO1 (1)
#define MICROPY_HW_ENABLE_GPIO2 (1)
#define MICROPY_HW_ENABLE_GPIO3 (1)
#define MICROPY_HW_ENABLE_GPIO4 (1)
#define MICROPY_HW_ENABLE_GPIO5 (1)
#define MICROPY_HW_ENABLE_GPIO6 (1)
#define MICROPY_HW_ENABLE_GPIO7 (1)
#define MICROPY_HW_ENABLE_GPIO8 (1)
#define MICROPY_HW_ENABLE_GPIO9 (1)
#define MICROPY_HW_ENABLE_GPIO10 (1)
#define MICROPY_HW_ENABLE_GPIO18 (1)
#define MICROPY_HW_ENABLE_GPIO19 (1) // UART0_RXD
#define MICROPY_HW_ENABLE_GPIO20 (1) // UART0_TXD
#elif CONFIG_IDF_TARGET_ESP32C3
#define MICROPY_HW_ENABLE_GPIO0 (1)

View file

@ -38,13 +38,13 @@
#include "hal/timer_hal.h"
#include "hal/timer_ll.h"
#include "soc/timer_periph.h"
#include "esp_private/esp_clk_tree_common.h"
#include "esp_private/periph_ctrl.h"
#include "machine_timer.h"
#define TIMER_CLK_SRC GPTIMER_CLK_SRC_DEFAULT
#define TIMER_DIVIDER 8
// TIMER_BASE_CLK is normally 80MHz. TIMER_DIVIDER ought to divide this exactly
#define TIMER_SCALE (APB_CLK_FREQ / TIMER_DIVIDER)
#define TIMER_FLAGS 0
const mp_obj_type_t machine_timer_type;
@ -52,6 +52,14 @@ const mp_obj_type_t machine_timer_type;
static mp_obj_t machine_timer_init_helper(machine_timer_obj_t *self, mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
static mp_obj_t machine_timer_deinit(mp_obj_t self_in);
uint32_t machine_timer_freq_hz(void) {
// The timer source clock is APB or a fixed PLL (depending on chip), both constant frequency.
uint32_t freq;
check_esp_err(esp_clk_tree_src_get_freq_hz(TIMER_CLK_SRC, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &freq));
assert(freq % TIMER_DIVIDER == 0); // Source clock should divide evenly into TIMER_DIVIDER
return freq / TIMER_DIVIDER;
}
void machine_timer_deinit_all(void) {
// Disable, deallocate and remove all timers from list
machine_timer_obj_t **t = &MP_STATE_PORT(machine_timer_obj_head);
@ -66,7 +74,7 @@ void machine_timer_deinit_all(void) {
static void machine_timer_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
machine_timer_obj_t *self = self_in;
qstr mode = self->repeat ? MP_QSTR_PERIODIC : MP_QSTR_ONE_SHOT;
uint64_t period = self->period / (TIMER_SCALE / 1000); // convert to ms
uint64_t period = self->period / (machine_timer_freq_hz() / 1000); // convert to ms
#if SOC_TIMER_GROUP_TIMERS_PER_GROUP == 1
mp_printf(print, "Timer(%u, mode=%q, period=%lu)", self->group, mode, period);
#else
@ -163,8 +171,23 @@ static void machine_timer_isr_handler(machine_timer_obj_t *self) {
void machine_timer_enable(machine_timer_obj_t *self) {
// Initialise the timer.
timer_hal_init(&self->hal_context, self->group, self->index);
PERIPH_RCC_ACQUIRE_ATOMIC(timer_group_periph_signals.groups[self->index].module, ref_count) {
if (ref_count == 0) {
timer_ll_enable_bus_clock(self->index, true);
timer_ll_reset_register(self->index);
}
}
timer_ll_enable_counter(self->hal_context.dev, self->index, false);
timer_ll_set_clock_source(self->hal_context.dev, self->index, GPTIMER_CLK_SRC_DEFAULT);
esp_clk_tree_enable_src(TIMER_CLK_SRC, true);
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 5, 0)
timer_ll_set_clock_source(self->hal_context.dev, self->index, TIMER_CLK_SRC);
timer_ll_enable_clock(self->hal_context.dev, self->index, true);
#else
timer_ll_set_clock_source(self->group, self->index, TIMER_CLK_SRC);
timer_ll_enable_clock(self->group, self->index, true);
#endif
timer_ll_set_clock_prescale(self->hal_context.dev, self->index, TIMER_DIVIDER);
timer_hal_set_counter_value(&self->hal_context, 0);
timer_ll_set_count_direction(self->hal_context.dev, self->index, GPTIMER_COUNT_UP);
@ -224,7 +247,7 @@ static mp_obj_t machine_timer_init_helper(machine_timer_obj_t *self, mp_uint_t n
#if MICROPY_PY_BUILTINS_FLOAT
if (args[ARG_freq].u_obj != mp_const_none) {
self->period = (uint64_t)(TIMER_SCALE / mp_obj_get_float(args[ARG_freq].u_obj));
self->period = (uint64_t)(machine_timer_freq_hz() / mp_obj_get_float(args[ARG_freq].u_obj));
}
#else
if (args[ARG_freq].u_int != 0xffffffff) {
@ -232,7 +255,7 @@ static mp_obj_t machine_timer_init_helper(machine_timer_obj_t *self, mp_uint_t n
}
#endif
else {
self->period = (((uint64_t)args[ARG_period].u_int) * TIMER_SCALE) / args[ARG_tick_hz].u_int;
self->period = (((uint64_t)args[ARG_period].u_int) * machine_timer_freq_hz()) / args[ARG_tick_hz].u_int;
}
self->repeat = args[ARG_mode].u_int;
@ -268,7 +291,7 @@ static mp_obj_t machine_timer_value(mp_obj_t self_in) {
mp_raise_ValueError(MP_ERROR_TEXT("timer not set"));
}
uint64_t result = timer_ll_get_counter_value(self->hal_context.dev, self->index);
return MP_OBJ_NEW_SMALL_INT((mp_uint_t)(result / (TIMER_SCALE / 1000))); // value in ms
return MP_OBJ_NEW_SMALL_INT((mp_uint_t)(result / (machine_timer_freq_hz() / 1000))); // value in ms
}
static MP_DEFINE_CONST_FUN_OBJ_1(machine_timer_value_obj, machine_timer_value);

View file

@ -34,13 +34,6 @@
#include "hal/timer_ll.h"
#include "soc/timer_periph.h"
#define TIMER_DIVIDER 8
// TIMER_BASE_CLK is normally 80MHz. TIMER_DIVIDER ought to divide this exactly
#define TIMER_SCALE (APB_CLK_FREQ / TIMER_DIVIDER)
#define TIMER_FLAGS 0
typedef struct _machine_timer_obj_t {
mp_obj_base_t base;
@ -64,4 +57,6 @@ machine_timer_obj_t *machine_timer_create(mp_uint_t timer);
void machine_timer_enable(machine_timer_obj_t *self);
void machine_timer_disable(machine_timer_obj_t *self);
uint32_t machine_timer_freq_hz(void);
#endif // MICROPY_INCLUDED_ESP32_MACHINE_TIMER_H

View file

@ -58,7 +58,7 @@
#define UART_IRQ_RXIDLE (0x1000)
#define UART_IRQ_BREAK (1 << UART_BREAK)
#define MP_UART_ALLOWED_FLAGS (UART_IRQ_RX | UART_IRQ_RXIDLE | UART_IRQ_BREAK)
#define RXIDLE_TIMER_MIN (5000) // 500 us
#define RXIDLE_TIMER_MIN (machine_timer_freq_hz() * 5 / 10000) // 500us minimum rxidle time
#define UART_QUEUE_SIZE (3)
enum {
@ -535,7 +535,7 @@ static void uart_irq_configure_timer(machine_uart_obj_t *self, mp_uint_t trigger
self->mp_irq_obj->ishard = false;
uint32_t baudrate;
uart_get_baudrate(self->uart_num, &baudrate);
mp_int_t period = TIMER_SCALE * 20 / baudrate + 1;
mp_int_t period = machine_timer_freq_hz() * 20 / baudrate + 1;
if (period < RXIDLE_TIMER_MIN) {
period = RXIDLE_TIMER_MIN;
}

Some files were not shown because too many files have changed in this diff Show more