Compare commits
156 commits
cc58f72456
...
ffea37149a
| Author | SHA1 | Date | |
|---|---|---|---|
| ffea37149a | |||
| b02f562bbf | |||
| ed22c8b729 | |||
| 9f53701ac0 | |||
|
|
8c47e446bf | ||
|
|
c33a02fe6d | ||
|
|
6c1d1f3ad4 | ||
|
|
6650506e06 | ||
|
|
64cac4690f | ||
|
|
19be404ad8 | ||
|
|
87099b5731 | ||
|
|
b0fd0079f4 | ||
|
|
1df5ee12e8 | ||
|
|
72147c02c7 | ||
|
|
053aade741 | ||
|
|
bd7342d9ec | ||
|
|
a3f9dec788 | ||
|
|
14e9c00cb9 | ||
|
|
b5fcb33eaa | ||
|
|
989abae12c | ||
|
|
169d382248 | ||
|
|
3650196682 | ||
|
|
bd413d3d85 | ||
|
|
22deeeb8db | ||
|
|
d8672f4cde | ||
|
|
3faf229853 | ||
|
|
d81d56cc4d | ||
|
|
020eeba412 | ||
|
|
c7ddf0c54f | ||
|
|
365329cd54 | ||
|
|
152a0782e6 | ||
|
|
1b35116c92 | ||
|
|
1588c455c4 | ||
|
|
afa7265ffa | ||
|
|
bba3542018 | ||
|
|
b7cfafc1ee | ||
|
|
f0f5abb7a3 | ||
|
|
f1462448d0 | ||
|
|
326730d8b2 | ||
|
|
46b366d7b2 | ||
|
|
0feb4f5ea4 | ||
|
|
e58848a21e | ||
|
|
b3cd1a355e | ||
|
|
40cc4e4f74 | ||
|
|
3efbd726eb | ||
|
|
141f7d0c35 | ||
| 0615d13963 | |||
| 803da9645f | |||
| e15219800e | |||
|
|
2bba507148 | ||
|
|
a279c64046 | ||
|
|
3c72c3a1e6 | ||
|
|
95d1794afd | ||
|
|
0f5f6484a2 | ||
|
|
0cb2c69b3f | ||
|
|
c16fe5b5ed | ||
|
|
f9b6d8e608 | ||
|
|
54e6cfc6e3 | ||
|
|
e2744ce679 | ||
|
|
1db71f9e55 | ||
|
|
6565827955 | ||
|
|
f493075d88 | ||
|
|
744270ac1b | ||
| 4614ee9e68 | |||
|
|
0ee3f99da2 | ||
|
|
b1d5c656de | ||
|
|
f10707febb | ||
|
|
14ccdeb4d7 | ||
|
|
485dac783b | ||
|
|
6e450dba7e | ||
|
|
20e1ae0733 | ||
|
|
8c47ff7153 | ||
|
|
adcfdf625b | ||
|
|
361c615f3e | ||
|
|
1273751a3b | ||
|
|
1401fdb8b3 | ||
|
|
c35427cbdb | ||
|
|
7e8705fe2b | ||
|
|
4ce2dd2cda | ||
|
|
593ae04eeb | ||
|
|
ce109af712 | ||
|
|
d5ecda05eb | ||
|
|
255d74b5a8 | ||
|
|
7c8ae78a03 | ||
| c0252d73c6 | |||
|
|
658a2e3dbd | ||
|
|
3c9546ea09 | ||
|
|
e6739fc87e | ||
|
|
a9dd741e66 | ||
|
|
64b3944b01 | ||
|
|
026a20da3e | ||
|
|
907c5e9976 | ||
|
|
641ca2eb06 | ||
|
|
327655905e | ||
|
|
e54553c496 | ||
|
|
c3f3339c87 | ||
|
|
bf6f229cf3 | ||
|
|
277b615f26 | ||
|
|
6558d519a2 | ||
|
|
7bc83afee2 | ||
|
|
ac5b1bce99 | ||
|
|
79d182deb2 | ||
|
|
5c78762c16 | ||
|
|
67a442d8fa | ||
|
|
6e72cae619 | ||
|
|
0c50343145 | ||
|
|
1839340dda | ||
|
|
56d2b47370 | ||
|
|
01e570a347 | ||
|
|
78d16672e1 | ||
|
|
2443878bd9 | ||
|
|
17d0449ac8 | ||
|
|
a4ca42f094 | ||
|
|
9b1778fc77 | ||
|
|
1b578fe2c0 | ||
|
|
ab7c5a1733 | ||
|
|
769453c750 | ||
|
|
c9b52b2b7f | ||
|
|
a9a606bf5d | ||
|
|
41987c6cf4 | ||
|
|
dea949e860 | ||
|
|
4ba626ab5a | ||
|
|
953da2080e | ||
|
|
f39434e9fb | ||
|
|
69ead7d98e | ||
|
|
f67a370311 | ||
|
|
f8f6d71940 | ||
|
|
947d5448b4 | ||
|
|
97d56527a0 | ||
|
|
dbbaa959c8 | ||
|
|
e4e1c9f413 | ||
|
|
ffa98cb014 | ||
|
|
813f0c1cb9 | ||
|
|
241ee163c0 | ||
|
|
fdbd23268d | ||
|
|
4bdf2a2dc0 | ||
|
|
ff6491ded0 | ||
|
|
a9b038a57e | ||
|
|
6a8c45b6c4 | ||
|
|
4360da1684 | ||
|
|
68434b4be7 | ||
|
|
135c1cc7cd | ||
|
|
ab4af2c1a6 | ||
|
|
d4399b3230 | ||
|
|
77c9eb7795 | ||
|
|
ca9916968c | ||
|
|
88cb6bc818 | ||
|
|
b9d6d6af4b | ||
|
|
c3e77ad6db | ||
|
|
d5dc554742 | ||
|
|
4412753f0c | ||
|
|
82db5c81e0 | ||
|
|
3a72f95919 | ||
|
|
3c69277ba9 | ||
| 062e82a7cd | |||
|
|
ebc9525c95 |
362 changed files with 9174 additions and 2244 deletions
2
.github/workflows/biome.yml
vendored
2
.github/workflows/biome.yml
vendored
|
|
@ -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:
|
||||
|
|
|
|||
2
.github/workflows/code_formatting.yml
vendored
2
.github/workflows/code_formatting.yml
vendored
|
|
@ -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
|
||||
|
|
|
|||
2
.github/workflows/code_size.yml
vendored
2
.github/workflows/code_size.yml
vendored
|
|
@ -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
|
||||
|
|
|
|||
2
.github/workflows/codespell.yml
vendored
2
.github/workflows/codespell.yml
vendored
|
|
@ -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
|
||||
|
|
|
|||
2
.github/workflows/commit_formatting.yml
vendored
2
.github/workflows/commit_formatting.yml
vendored
|
|
@ -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
|
||||
|
|
|
|||
6
.github/workflows/docs.yml
vendored
6
.github/workflows/docs.yml
vendored
|
|
@ -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
|
||||
|
|
|
|||
2
.github/workflows/examples.yml
vendored
2
.github/workflows/examples.yml
vendored
|
|
@ -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
|
||||
|
|
|
|||
2
.github/workflows/mpremote.yml
vendored
2
.github/workflows/mpremote.yml
vendored
|
|
@ -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.
|
||||
|
|
|
|||
2
.github/workflows/mpy_format.yml
vendored
2
.github/workflows/mpy_format.yml
vendored
|
|
@ -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
|
||||
|
|
|
|||
2
.github/workflows/ports.yml
vendored
2
.github/workflows/ports.yml
vendored
|
|
@ -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
|
||||
|
|
|
|||
2
.github/workflows/ports_alif.yml
vendored
2
.github/workflows/ports_alif.yml
vendored
|
|
@ -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 }}
|
||||
|
|
|
|||
2
.github/workflows/ports_cc3200.yml
vendored
2
.github/workflows/ports_cc3200.yml
vendored
|
|
@ -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
|
||||
|
|
|
|||
3
.github/workflows/ports_esp32.yml
vendored
3
.github/workflows/ports_esp32.yml
vendored
|
|
@ -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)
|
||||
|
|
|
|||
2
.github/workflows/ports_esp8266.yml
vendored
2
.github/workflows/ports_esp8266.yml
vendored
|
|
@ -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
|
||||
|
|
|
|||
2
.github/workflows/ports_mimxrt.yml
vendored
2
.github/workflows/ports_mimxrt.yml
vendored
|
|
@ -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
|
||||
|
|
|
|||
2
.github/workflows/ports_nrf.yml
vendored
2
.github/workflows/ports_nrf.yml
vendored
|
|
@ -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
|
||||
|
|
|
|||
2
.github/workflows/ports_powerpc.yml
vendored
2
.github/workflows/ports_powerpc.yml
vendored
|
|
@ -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
|
||||
|
|
|
|||
7
.github/workflows/ports_qemu.yml
vendored
7
.github/workflows/ports_qemu.yml
vendored
|
|
@ -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
|
||||
|
|
|
|||
2
.github/workflows/ports_renesas-ra.yml
vendored
2
.github/workflows/ports_renesas-ra.yml
vendored
|
|
@ -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
|
||||
|
|
|
|||
2
.github/workflows/ports_rp2.yml
vendored
2
.github/workflows/ports_rp2.yml
vendored
|
|
@ -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
|
||||
|
|
|
|||
2
.github/workflows/ports_samd.yml
vendored
2
.github/workflows/ports_samd.yml
vendored
|
|
@ -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
|
||||
|
|
|
|||
2
.github/workflows/ports_stm32.yml
vendored
2
.github/workflows/ports_stm32.yml
vendored
|
|
@ -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 }}
|
||||
|
|
|
|||
39
.github/workflows/ports_unix.yml
vendored
39
.github/workflows/ports_unix.yml
vendored
|
|
@ -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.
|
||||
|
|
|
|||
3
.github/workflows/ports_webassembly.yml
vendored
3
.github/workflows/ports_webassembly.yml
vendored
|
|
@ -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
|
||||
|
|
|
|||
12
.github/workflows/ports_windows.yml
vendored
12
.github/workflows/ports_windows.yml
vendored
|
|
@ -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 }}
|
||||
|
|
|
|||
3
.github/workflows/ports_zephyr.yml
vendored
3
.github/workflows/ports_zephyr.yml
vendored
|
|
@ -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"
|
||||
|
|
|
|||
2
.github/workflows/ruff.yml
vendored
2
.github/workflows/ruff.yml
vendored
|
|
@ -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
2
.gitmodules
vendored
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
--------------------------
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
--------------
|
||||
|
||||
|
|
|
|||
|
|
@ -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'``.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
93
docs/library/machine.Counter.rst
Normal file
93
docs/library/machine.Counter.rst
Normal 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.
|
||||
72
docs/library/machine.Encoder.rst
Normal file
72
docs/library/machine.Encoder.rst
Normal 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.
|
||||
174
docs/library/machine.I2CTarget.rst
Normal file
174
docs/library/machine.I2CTarget.rst
Normal 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.
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
>>>
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -13,3 +13,4 @@
|
|||
#define MICROPY_ENABLE_COMPILER (1)
|
||||
#define MICROPY_ENABLE_GC (1)
|
||||
#define MICROPY_PY_GC (1)
|
||||
#define MICROPY_PY_SYS (0)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 \
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
424
extmod/machine_i2c_target.c
Normal 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
|
||||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
325
ports/alif/machine_i2c_target.c
Normal file
325
ports/alif/machine_i2c_target.c
Normal 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);
|
||||
}
|
||||
|
|
@ -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(µseconds);
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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
39
ports/alif/modtime.c
Normal 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));
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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(µseconds);
|
||||
return (uint64_t)s * 1000000000ULL + (uint64_t)microseconds * 1000ULL;
|
||||
}
|
||||
|
||||
void mp_hal_pin_config(const machine_pin_obj_t *pin, uint32_t mode,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
22
ports/esp32/boards/ESP32_GENERIC_C2/board.json
Normal file
22
ports/esp32/boards/ESP32_GENERIC_C2/board.json
Normal 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"
|
||||
}
|
||||
3
ports/esp32/boards/ESP32_GENERIC_C2/board.md
Normal file
3
ports/esp32/boards/ESP32_GENERIC_C2/board.md
Normal 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.
|
||||
35
ports/esp32/boards/ESP32_GENERIC_C2/board_init.c
Normal file
35
ports/esp32/boards/ESP32_GENERIC_C2/board_init.c
Normal 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();
|
||||
}
|
||||
14
ports/esp32/boards/ESP32_GENERIC_C2/mpconfigboard.cmake
Normal file
14
ports/esp32/boards/ESP32_GENERIC_C2/mpconfigboard.cmake
Normal 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
|
||||
)
|
||||
|
||||
10
ports/esp32/boards/ESP32_GENERIC_C2/mpconfigboard.h
Normal file
10
ports/esp32/boards/ESP32_GENERIC_C2/mpconfigboard.h
Normal 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);
|
||||
|
|
@ -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
|
||||
|
|
|
|||
17
ports/esp32/boards/sdkconfig.c2
Normal file
17
ports/esp32/boards/sdkconfig.c2
Normal 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
|
||||
44
ports/esp32/boards/sdkconfig.free_ram
Normal file
44
ports/esp32/boards/sdkconfig.free_ram
Normal 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
|
||||
|
|
@ -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
513
ports/esp32/esp32_pcnt.c
Normal 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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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},
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
52
ports/esp32/machine_i2c.h
Normal 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
|
||||
225
ports/esp32/machine_i2c_target.c
Normal file
225
ports/esp32/machine_i2c_target.c
Normal 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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
Loading…
Reference in a new issue