Skip to content

Commit cfe5624

Browse files
authored
Merge pull request adafruit#11649 from dhalbert/zephyr-cp-default-modules
zephyr-cp: enable keypad, keypad_demux, touchio, atexit, codeop, locale
2 parents fb98f21 + f8cf5e4 commit cfe5624

3 files changed

Lines changed: 229 additions & 20 deletions

File tree

‎ports/zephyr-cp/cptools/build_circuitpython.py‎

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -42,36 +42,43 @@
4242
compiler = cpbuild.Compiler(srcdir, builddir, cmake_args)
4343

4444
ALWAYS_ON_MODULES = ["sys", "collections"]
45+
# Keep in alphabetical order.
4546
DEFAULT_MODULES = [
4647
"__future__",
47-
"time",
48-
"os",
49-
"microcontroller",
50-
"struct",
48+
"adafruit_bus_device",
49+
"aesio",
5150
"array",
52-
"json",
53-
"random",
54-
"digitalio",
51+
"asyncio",
52+
"atexit",
53+
"binascii",
5554
"bitbangio",
56-
"rotaryio",
57-
"rainbowio",
58-
"traceback",
59-
"warnings",
60-
"supervisor",
55+
"codeop",
56+
"digitalio",
6157
"errno",
58+
"getpass",
59+
"hashlib",
6260
"io",
61+
"json",
62+
"keypad",
63+
"keypad_demux",
64+
"locale",
6365
"math",
66+
"microcontroller",
6467
"msgpack",
65-
"aesio",
66-
"hashlib",
67-
"zlib",
68-
"adafruit_bus_device",
69-
"getpass",
70-
"storage",
71-
"binascii",
68+
"os",
69+
"rainbowio",
70+
"random",
7271
"re",
73-
"asyncio",
72+
"rotaryio",
7473
"select",
74+
"storage",
75+
"struct",
76+
"supervisor",
77+
"time",
78+
"touchio",
79+
"traceback",
80+
"warnings",
81+
"zlib",
7582
]
7683
# Flags that don't match with with a *bindings module. Some used by adafruit_requests
7784
MPCONFIG_FLAGS = ["array", "errno", "io", "json", "math", "binascii"]
@@ -117,8 +124,10 @@
117124
EXTRA_FLAGS = {
118125
"audiobusio": {"AUDIOBUSIO_I2SOUT": 1, "AUDIOBUSIO_PDMIN": 0},
119126
"busio": {"BUSIO_SPI": 1, "BUSIO_I2C": 1, "BUSIO_UART": 1},
127+
"keypad": {"KEYPAD_KEYMATRIX": 1, "KEYPAD_KEYS": 1, "KEYPAD_SHIFTREGISTERKEYS": 1},
120128
"rotaryio": {"ROTARYIO_SOFTENCODER": 1},
121129
"synthio": {"SYNTHIO_MAX_CHANNELS": 12},
130+
"touchio": {"TOUCHIO_USE_NATIVE": 0},
122131
}
123132

124133
# Library sources. Will be globbed from the top level directory
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
# SPDX-FileCopyrightText: 2026 Dan Halbert for Adafruit Industries LLC
2+
# SPDX-License-Identifier: MIT
3+
4+
"""Test keypad functionality on native_sim."""
5+
6+
import re
7+
8+
import pytest
9+
10+
11+
KEYS_CODE = """\
12+
import time
13+
import keypad
14+
import microcontroller
15+
16+
keys = keypad.Keys(
17+
(microcontroller.pin.P_01, microcontroller.pin.P_02),
18+
value_when_pressed={value_when_pressed},
19+
pull=True,
20+
)
21+
event = keypad.Event()
22+
print(f"t_abs={{time.monotonic():.3f}} start")
23+
24+
while time.monotonic() < 7.0:
25+
if keys.events.get_into(event):
26+
print(
27+
f"t_abs={{time.monotonic():.3f}} key={{event.key_number}} "
28+
f"pressed={{event.pressed}} ts={{event.timestamp}}"
29+
)
30+
time.sleep(0.01)
31+
32+
print(f"t_abs={{time.monotonic():.3f}} done")
33+
"""
34+
35+
36+
def keys_trace(pressed: int) -> dict:
37+
"""Press and release key 0 at 4.0-4.5 s, then key 1 at 5.0-5.5 s."""
38+
released = 1 - pressed
39+
return {
40+
"gpio_emul.01": [
41+
(4_000_000_000, pressed),
42+
(4_500_000_000, released),
43+
],
44+
"gpio_emul.02": [
45+
(5_000_000_000, pressed),
46+
(5_500_000_000, released),
47+
],
48+
}
49+
50+
51+
# (key_number, pressed, trace time in seconds)
52+
EXPECTED_EVENTS = [
53+
(0, True, 4.0),
54+
(0, False, 4.5),
55+
(1, True, 5.0),
56+
(1, False, 5.5),
57+
]
58+
59+
EVENT_RE = re.compile(r"t_abs=([0-9.]+) key=(\d+) pressed=(True|False) ts=(\d+)")
60+
61+
62+
def check_keys_events(output: str) -> None:
63+
start_match = re.search(r"t_abs=([0-9.]+) start", output)
64+
assert start_match is not None
65+
# The trace only reaches pins that are already inputs.
66+
assert float(start_match.group(1)) < 4.0
67+
68+
events = [
69+
(float(t_abs), int(key), pressed == "True", int(ts))
70+
for t_abs, key, pressed, ts in EVENT_RE.findall(output)
71+
]
72+
assert [(key, pressed) for _, key, pressed, _ in events] == [
73+
(key, pressed) for key, pressed, _ in EXPECTED_EVENTS
74+
]
75+
76+
# Seen within one scan interval plus one poll of the trace edge.
77+
for (t_abs, _, _, _), (_, _, trace_time) in zip(events, EXPECTED_EVENTS):
78+
assert trace_time <= t_abs <= trace_time + 0.1
79+
80+
# Event timestamps are in ms; each key is held for 500 ms.
81+
for press, release in ((events[0], events[1]), (events[2], events[3])):
82+
assert abs((release[3] - press[3]) - 500) <= 50
83+
84+
assert "done" in output
85+
86+
87+
@pytest.mark.circuitpy_drive({"code.py": KEYS_CODE.format(value_when_pressed=False)})
88+
@pytest.mark.input_trace(keys_trace(pressed=0))
89+
def test_keypad_keys_active_low(circuitpython):
90+
"""Keys with pull-ups reports presses when a trace pulls the pins low."""
91+
circuitpython.wait_until_done()
92+
check_keys_events(circuitpython.serial.all_output)
93+
94+
95+
@pytest.mark.circuitpy_drive({"code.py": KEYS_CODE.format(value_when_pressed=True)})
96+
@pytest.mark.input_trace(keys_trace(pressed=1))
97+
def test_keypad_keys_active_high(circuitpython):
98+
"""Keys with pull-downs reports presses when a trace drives the pins high."""
99+
circuitpython.wait_until_done()
100+
check_keys_events(circuitpython.serial.all_output)
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
# SPDX-FileCopyrightText: 2026 Dan Halbert for Adafruit Industries LLC
2+
# SPDX-License-Identifier: MIT
3+
4+
"""Test touchio functionality on native_sim.
5+
6+
touchio charges the pin, switches it to an input, and counts until it reads
7+
back discharged. gpio_emul has no RC model: an input reads whatever value was
8+
last set, so the count is either 0 or the 10000-tick timeout. A pin held at its
9+
charge level by an input trace stands in for a touch.
10+
"""
11+
12+
import re
13+
14+
import pytest
15+
16+
17+
TOUCH_CODE = """\
18+
import time
19+
import microcontroller
20+
import touchio
21+
22+
touch = touchio.TouchIn(microcontroller.pin.P_01)
23+
print(f"t_abs={time.monotonic():.3f} threshold={touch.threshold}")
24+
25+
26+
def report():
27+
print(f"t_abs={time.monotonic():.3f} raw={touch.raw_value} value={touch.value}")
28+
29+
30+
report()
31+
# Read only between trace edges, so each edge lands while the pin is an input.
32+
for until in (4.5, 5.5):
33+
while time.monotonic() < until:
34+
time.sleep(0.05)
35+
report()
36+
37+
print("done")
38+
"""
39+
40+
TOUCH_TRACE = {
41+
"gpio_emul.01": [
42+
(4_000_000_000, 1), # 4.0s: touched
43+
(5_000_000_000, 0), # 5.0s: released
44+
],
45+
}
46+
47+
REPORT_RE = re.compile(r"t_abs=([0-9.]+) raw=(\d+) value=(True|False)")
48+
49+
50+
@pytest.mark.circuitpy_drive({"code.py": TOUCH_CODE})
51+
@pytest.mark.input_trace(TOUCH_TRACE)
52+
def test_touchio_touch_and_release(circuitpython):
53+
"""TouchIn reads touched while a trace holds the pin high."""
54+
circuitpython.wait_until_done()
55+
56+
output = circuitpython.serial.all_output
57+
threshold_match = re.search(r"t_abs=([0-9.]+) threshold=(\d+)", output)
58+
assert threshold_match is not None
59+
# The trace only reaches pins that are already inputs.
60+
assert float(threshold_match.group(1)) < 4.0
61+
# Untouched raw reading of 0 gives a threshold of 0 * 1.05 + 100.
62+
assert int(threshold_match.group(2)) == 100
63+
64+
reports = [(int(raw), value == "True") for _, raw, value in REPORT_RE.findall(output)]
65+
assert reports == [(0, False), (10000, True), (0, False)]
66+
assert "done" in output
67+
68+
69+
NO_PULL_CODE = """\
70+
import digitalio
71+
import microcontroller
72+
import touchio
73+
74+
# Nothing pulls P_01 up, so it never charges low.
75+
try:
76+
touchio.TouchIn(microcontroller.pin.P_01, pull=digitalio.Pull.UP)
77+
except ValueError as e:
78+
print(f"pull up: {e}")
79+
80+
# Leave P_02 high, as if nothing pulled it down.
81+
with digitalio.DigitalInOut(microcontroller.pin.P_02) as pin:
82+
pin.switch_to_input(pull=digitalio.Pull.UP)
83+
try:
84+
touchio.TouchIn(microcontroller.pin.P_02)
85+
except ValueError as e:
86+
print(f"pull down: {e}")
87+
88+
print("done")
89+
"""
90+
91+
92+
@pytest.mark.circuitpy_drive({"code.py": NO_PULL_CODE})
93+
def test_touchio_missing_pull_resistor(circuitpython):
94+
"""TouchIn raises ValueError when the pin never discharges."""
95+
circuitpython.wait_until_done()
96+
97+
output = circuitpython.serial.all_output
98+
assert "pull up: No pullup on pin; 1Mohm recommended" in output
99+
assert "pull down: No pulldown on pin; 1Mohm recommended" in output
100+
assert "done" in output

0 commit comments

Comments
 (0)