Skip to content

Commit debff30

Browse files
committed
Add examples for ESP32-S2 and ESP32-S3
There are now example files for the S2 and S3 (with ``_s2`` or ``_s3`` appended to their filenames). Note: The s2 examples also work unmodified on the ESP32-S3, except the readgpio example which needs different peripheral register addresses on the S3. The ``counter_s2.py`` example is unmodified compared to the original example, except that the assembler is told to generate esp32s2 output. The ``blink_s2.py``, ``readgpio_s2.py`` and ``readgpio_s3.py`` examples have their rtc_io base address updated, as well as the constants referring to the GPIO pins and channels and the peripheral register bits used to read/write the GPIO inputs/outputs. These addresses/bits have changed from the original ESP32. Otherwise the examples are identical to the examples for the original ESP32.
1 parent ed28d27 commit debff30

File tree

7 files changed

+327
-5
lines changed

7 files changed

+327
-5
lines changed

examples/blink.py

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
"""
2+
Example for: ESP32
3+
24
Simple example showing how to control a GPIO pin from the ULP coprocessor.
35
46
The GPIO port is configured to be attached to the RTC module, and then set
@@ -22,11 +24,11 @@
2224

2325
source = """\
2426
# constants from:
25-
# https://github.com/espressif/esp-idf/blob/1cb31e5/components/soc/esp32/include/soc/soc.h
27+
# https://github.com/espressif/esp-idf/blob/v5.0.2/components/soc/esp32/include/soc/reg_base.h
2628
#define DR_REG_RTCIO_BASE 0x3ff48400
2729
2830
# constants from:
29-
# https://github.com/espressif/esp-idf/blob/1cb31e5/components/soc/esp32/include/soc/rtc_io_reg.h
31+
# https://github.com/espressif/esp-idf/blob/v5.0.2/components/soc/esp32/include/soc/rtc_io_reg.h
3032
#define RTC_IO_TOUCH_PAD2_REG (DR_REG_RTCIO_BASE + 0x9c)
3133
#define RTC_IO_TOUCH_PAD2_MUX_SEL_M (BIT(19))
3234
#define RTC_GPIO_OUT_REG (DR_REG_RTCIO_BASE + 0x0)
@@ -35,7 +37,7 @@
3537
#define RTC_GPIO_OUT_DATA_S 14
3638
3739
# constants from:
38-
# https://github.com/espressif/esp-idf/blob/1cb31e5/components/soc/esp32/include/soc/rtc_io_channel.h
40+
# https://github.com/espressif/esp-idf/blob/v5.0.2/components/soc/esp32/include/soc/rtc_io_channel.h
3941
#define RTCIO_GPIO2_CHANNEL 12
4042
4143
# When accessed from the RTC module (ULP) GPIOs need to be addressed by their channel number

examples/blink_s2.py

+112
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
"""
2+
Example for: ESP32-S2 and ESP32-S3
3+
4+
The GPIO port is configured to be attached to the RTC module, and then set
5+
to OUTPUT mode. To avoid re-initializing the GPIO on every wakeup, a magic
6+
token gets set in memory.
7+
8+
After every change of state, the ULP is put back to sleep again until the
9+
next wakeup. The ULP wakes up every 500ms to change the state of the GPIO
10+
pin. An LED attached to the GPIO pin would toggle on and off every 500ms.
11+
12+
The end of the python script has a loop to show the value of the magic token
13+
and the current state, so you can confirm the magic token gets set and watch
14+
the state value changing. If the loop is stopped (Ctrl-C), the LED attached
15+
to the GPIO pin continues to blink, because the ULP runs independently from
16+
the main processor.
17+
"""
18+
19+
from esp32 import ULP
20+
from machine import mem32
21+
from esp32_ulp import src_to_binary
22+
23+
source = """\
24+
# constants from:
25+
# https://github.com/espressif/esp-idf/blob/v5.0.2/components/soc/esp32s2/include/soc/reg_base.h
26+
#define DR_REG_RTCIO_BASE 0x3f408400
27+
28+
# constants from:
29+
# https://github.com/espressif/esp-idf/blob/v5.0.2/components/soc/esp32s2/include/soc/rtc_io_reg.h
30+
#define RTC_IO_TOUCH_PAD2_REG (DR_REG_RTCIO_BASE + 0x8c)
31+
#define RTC_IO_TOUCH_PAD2_MUX_SEL_M (BIT(19))
32+
#define RTC_GPIO_OUT_REG (DR_REG_RTCIO_BASE + 0x0)
33+
#define RTC_GPIO_ENABLE_REG (DR_REG_RTCIO_BASE + 0xc)
34+
#define RTC_GPIO_ENABLE_S 10
35+
#define RTC_GPIO_OUT_DATA_S 10
36+
37+
# constants from:
38+
# https://github.com/espressif/esp-idf/blob/v5.0.2/components/soc/esp32s2/include/soc/rtc_io_channel.h
39+
#define RTCIO_GPIO2_CHANNEL 2
40+
41+
# When accessed from the RTC module (ULP) GPIOs need to be addressed by their channel number
42+
.set gpio, RTCIO_GPIO2_CHANNEL
43+
.set token, 0xcafe # magic token
44+
45+
.text
46+
magic: .long 0
47+
state: .long 0
48+
49+
.global entry
50+
entry:
51+
# load magic flag
52+
move r0, magic
53+
ld r1, r0, 0
54+
55+
# test if we have initialised already
56+
sub r1, r1, token
57+
jump after_init, eq # jump if magic == token (note: "eq" means the last instruction (sub) resulted in 0)
58+
59+
init:
60+
# connect GPIO to ULP (0: GPIO connected to digital GPIO module, 1: GPIO connected to analog RTC module)
61+
WRITE_RTC_REG(RTC_IO_TOUCH_PAD2_REG, RTC_IO_TOUCH_PAD2_MUX_SEL_M, 1, 1);
62+
63+
# GPIO shall be output, not input (this also enables a pull-down by default)
64+
WRITE_RTC_REG(RTC_GPIO_ENABLE_REG, RTC_GPIO_ENABLE_S + gpio, 1, 1)
65+
66+
# store that we're done with initialisation
67+
move r0, magic
68+
move r1, token
69+
st r1, r0, 0
70+
71+
after_init:
72+
move r1, state
73+
ld r0, r1, 0
74+
75+
move r2, 1
76+
sub r0, r2, r0 # toggle state
77+
st r0, r1, 0 # store updated state
78+
79+
jumpr on, 0, gt # if r0 (state) > 0, jump to 'on'
80+
jump off # else jump to 'off'
81+
82+
on:
83+
# turn on led (set GPIO)
84+
WRITE_RTC_REG(RTC_GPIO_OUT_REG, RTC_GPIO_OUT_DATA_S + gpio, 1, 1)
85+
jump exit
86+
87+
off:
88+
# turn off led (clear GPIO)
89+
WRITE_RTC_REG(RTC_GPIO_OUT_REG, RTC_GPIO_OUT_DATA_S + gpio, 1, 0)
90+
jump exit
91+
92+
exit:
93+
halt # go back to sleep until next wakeup period
94+
"""
95+
96+
binary = src_to_binary(source, cpu="esp32s2") # cpu is esp32 or esp32s2
97+
98+
load_addr, entry_addr = 0, 8
99+
100+
ULP_MEM_BASE = 0x50000000
101+
ULP_DATA_MASK = 0xffff # ULP data is only in lower 16 bits
102+
103+
ulp = ULP()
104+
ulp.set_wakeup_period(0, 500000) # use timer0, wakeup after 500000usec (0.5s)
105+
ulp.load_binary(load_addr, binary)
106+
107+
ulp.run(entry_addr)
108+
109+
while True:
110+
print(hex(mem32[ULP_MEM_BASE + load_addr] & ULP_DATA_MASK), # magic token
111+
hex(mem32[ULP_MEM_BASE + load_addr + 4] & ULP_DATA_MASK) # current state
112+
)

examples/counter.py

+2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
"""
2+
Example for: ESP32
3+
24
Very basic example showing data exchange main CPU <--> ULP coprocessor.
35
46
To show that the ULP is doing something, it just increments the value <data>.

examples/counter_s2.py

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
"""
2+
Example for: ESP32-S2 and ESP32-S3
3+
4+
Very basic example showing data exchange main CPU <--> ULP coprocessor.
5+
6+
To show that the ULP is doing something, it just increments the value <data>.
7+
It does that once per ulp timer wakeup (and then the ULP halts until it gets
8+
waked up via the timer again).
9+
10+
The timer is set to a rather long period, so you can watch the data value
11+
incrementing (see loop at the end).
12+
"""
13+
14+
from esp32 import ULP
15+
from machine import mem32
16+
17+
from esp32_ulp import src_to_binary
18+
19+
source = """\
20+
data: .long 0
21+
22+
entry: move r3, data # load address of data into r3
23+
ld r2, r3, 0 # load data contents ([r3+0]) into r2
24+
add r2, r2, 1 # increment r2
25+
st r2, r3, 0 # store r2 contents into data ([r3+0])
26+
27+
halt # halt ULP co-prozessor (until it gets waked up again)
28+
"""
29+
30+
binary = src_to_binary(source, cpu="esp32s2") # cpu is esp32 or esp32s2
31+
32+
load_addr, entry_addr = 0, 4
33+
34+
ULP_MEM_BASE = 0x50000000
35+
ULP_DATA_MASK = 0xffff # ULP data is only in lower 16 bits
36+
37+
ulp = ULP()
38+
ulp.set_wakeup_period(0, 50000) # use timer0, wakeup after 50.000 cycles
39+
ulp.load_binary(load_addr, binary)
40+
41+
mem32[ULP_MEM_BASE + load_addr] = 0x1000
42+
ulp.run(entry_addr)
43+
44+
while True:
45+
print(hex(mem32[ULP_MEM_BASE + load_addr] & ULP_DATA_MASK))
46+

examples/readgpio.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
11
"""
2+
Example for: ESP32
3+
24
Very basic example showing how to read a GPIO pin from the ULP and access
35
that data from the main CPU.
46
57
In this case GPIO4 is being read. Note that the ULP needs to refer to GPIOs
68
via their RTC channel number. You can see the mapping in this file:
7-
https://github.com/espressif/esp-idf/blob/v4.4.1/components/soc/esp32/include/soc/rtc_io_channel.h#L51
9+
https://github.com/espressif/esp-idf/blob/v5.0.2/components/soc/esp32/include/soc/rtc_io_channel.h#L51
810
911
If you change to a different GPIO number, make sure to modify both the channel
1012
number and also the RTC_IO_TOUCH_PAD0_* references appropriately. The best place
1113
to see the mappings might be this table here (notice the "real GPIO numbers" as
1214
comments to each line):
13-
https://github.com/espressif/esp-idf/blob/v4.4.1/components/soc/esp32/rtc_io_periph.c#L61
15+
https://github.com/espressif/esp-idf/blob/v5.0.2/components/soc/esp32/rtc_io_periph.c#L53
1416
1517
The timer is set to a rather long period, so you can watch the data value
1618
change as you change the GPIO input (see loop at the end).

examples/readgpio_s2.py

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
"""
2+
Example for: ESP32-S2
3+
4+
Very basic example showing how to read a GPIO pin from the ULP and access
5+
that data from the main CPU.
6+
7+
In this case GPIO4 is being read. Note that the ULP needs to refer to GPIOs
8+
via their RTC channel number. You can see the mapping in this file:
9+
https://github.com/espressif/esp-idf/blob/v5.0.2/components/soc/esp32s2/include/soc/rtc_io_channel.h#L33
10+
11+
If you change to a different GPIO number, make sure to modify both the channel
12+
number and also the RTC_IO_TOUCH_PAD4_* references appropriately. The best place
13+
to see the mappings might be this table here (notice the "real GPIO numbers" as
14+
comments to each line):
15+
https://github.com/espressif/esp-idf/blob/v5.0.2/components/soc/esp32s2/rtc_io_periph.c#L60
16+
17+
The timer is set to a rather long period, so you can watch the data value
18+
change as you change the GPIO input (see loop at the end).
19+
"""
20+
21+
from esp32 import ULP
22+
from machine import mem32
23+
24+
from esp32_ulp import src_to_binary
25+
26+
source = """\
27+
#define DR_REG_RTCIO_BASE 0x3f408400
28+
#define RTC_IO_TOUCH_PAD4_REG (DR_REG_RTCIO_BASE + 0x94)
29+
#define RTC_IO_TOUCH_PAD4_MUX_SEL_M (BIT(19))
30+
#define RTC_IO_TOUCH_PAD4_FUN_IE_M (BIT(13))
31+
#define RTC_GPIO_IN_REG (DR_REG_RTCIO_BASE + 0x24)
32+
#define RTC_GPIO_IN_NEXT_S 10
33+
#define DR_REG_SENS_BASE 0x3f408800
34+
#define SENS_SAR_IO_MUX_CONF_REG (DR_REG_SENS_BASE + 0x0144)
35+
#define SENS_IOMUX_CLK_GATE_EN (BIT(31))
36+
.set channel, 4
37+
38+
state: .long 0
39+
40+
entry:
41+
# enable IOMUX clock
42+
WRITE_RTC_FIELD(SENS_SAR_IO_MUX_CONF_REG, SENS_IOMUX_CLK_GATE_EN, 1)
43+
44+
# connect GPIO to the RTC subsystem so the ULP can read it
45+
WRITE_RTC_REG(RTC_IO_TOUCH_PAD4_REG, RTC_IO_TOUCH_PAD4_MUX_SEL_M, 1, 1)
46+
47+
# switch the GPIO into input mode
48+
WRITE_RTC_REG(RTC_IO_TOUCH_PAD4_REG, RTC_IO_TOUCH_PAD4_FUN_IE_M, 1, 1)
49+
50+
# read the GPIO's current state into r0
51+
READ_RTC_REG(RTC_GPIO_IN_REG, RTC_GPIO_IN_NEXT_S + channel, 1)
52+
53+
# set r3 to the memory address of "state"
54+
move r3, state
55+
56+
# store what was read into r0 into the "state" variable
57+
st r0, r3, 0
58+
59+
# halt ULP co-processor (until it gets woken up again)
60+
halt
61+
"""
62+
63+
binary = src_to_binary(source, cpu="esp32s2") # cpu is esp32 or esp32s2
64+
65+
load_addr, entry_addr = 0, 4
66+
67+
ULP_MEM_BASE = 0x50000000
68+
ULP_DATA_MASK = 0xffff # ULP data is only in lower 16 bits
69+
70+
ulp = ULP()
71+
ulp.set_wakeup_period(0, 50000) # use timer0, wakeup after 50.000 cycles
72+
ulp.load_binary(load_addr, binary)
73+
74+
mem32[ULP_MEM_BASE + load_addr] = 0x0 # initialise state to 0
75+
ulp.run(entry_addr)
76+
77+
while True:
78+
print(hex(mem32[ULP_MEM_BASE + load_addr] & ULP_DATA_MASK))
79+

examples/readgpio_s3.py

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
"""
2+
Example for: ESP32-S3
3+
4+
Very basic example showing how to read a GPIO pin from the ULP and access
5+
that data from the main CPU.
6+
7+
In this case GPIO4 is being read. Note that the ULP needs to refer to GPIOs
8+
via their RTC channel number. You can see the mapping in this file:
9+
https://github.com/espressif/esp-idf/blob/v5.0.2/components/soc/esp32s3/include/soc/rtc_io_channel.h#L33
10+
11+
If you change to a different GPIO number, make sure to modify both the channel
12+
number and also the RTC_IO_TOUCH_PAD2_* references appropriately. The best place
13+
to see the mappings might be this table here (notice the "real GPIO numbers" as
14+
comments to each line):
15+
https://github.com/espressif/esp-idf/blob/v5.0.2/components/soc/esp32s3/rtc_io_periph.c#L60
16+
17+
The timer is set to a rather long period, so you can watch the data value
18+
change as you change the GPIO input (see loop at the end).
19+
"""
20+
21+
from esp32 import ULP
22+
from machine import mem32
23+
24+
from esp32_ulp import src_to_binary
25+
26+
source = """\
27+
#define DR_REG_RTCIO_BASE 0x60008400
28+
#define RTC_IO_TOUCH_PAD2_REG (DR_REG_RTCIO_BASE + 0x8c)
29+
#define RTC_IO_TOUCH_PAD2_MUX_SEL_M (BIT(19))
30+
#define RTC_IO_TOUCH_PAD2_FUN_IE_M (BIT(13))
31+
#define RTC_GPIO_IN_REG (DR_REG_RTCIO_BASE + 0x24)
32+
#define RTC_GPIO_IN_NEXT_S 10
33+
#define DR_REG_SENS_BASE 0x60008800
34+
#define SENS_SAR_PERI_CLK_GATE_CONF_REG (DR_REG_SENS_BASE + 0x104)
35+
#define SENS_IOMUX_CLK_EN (BIT(31))
36+
.set channel, 2
37+
38+
state: .long 0
39+
40+
entry:
41+
# enable IOMUX clock
42+
WRITE_RTC_FIELD(SENS_SAR_PERI_CLK_GATE_CONF_REG, SENS_IOMUX_CLK_EN, 1)
43+
44+
# connect GPIO to the RTC subsystem so the ULP can read it
45+
WRITE_RTC_REG(RTC_IO_TOUCH_PAD2_REG, RTC_IO_TOUCH_PAD2_MUX_SEL_M, 1, 1)
46+
47+
# switch the GPIO into input mode
48+
WRITE_RTC_REG(RTC_IO_TOUCH_PAD2_REG, RTC_IO_TOUCH_PAD2_FUN_IE_M, 1, 1)
49+
50+
# read the GPIO's current state into r0
51+
READ_RTC_REG(RTC_GPIO_IN_REG, RTC_GPIO_IN_NEXT_S + channel, 1)
52+
53+
# set r3 to the memory address of "state"
54+
move r3, state
55+
56+
# store what was read into r0 into the "state" variable
57+
st r0, r3, 0
58+
59+
# halt ULP co-processor (until it gets woken up again)
60+
halt
61+
"""
62+
63+
binary = src_to_binary(source, cpu="esp32s2") # cpu is esp32 or esp32s2
64+
65+
load_addr, entry_addr = 0, 4
66+
67+
ULP_MEM_BASE = 0x50000000
68+
ULP_DATA_MASK = 0xffff # ULP data is only in lower 16 bits
69+
70+
ulp = ULP()
71+
ulp.set_wakeup_period(0, 50000) # use timer0, wakeup after 50.000 cycles
72+
ulp.load_binary(load_addr, binary)
73+
74+
mem32[ULP_MEM_BASE + load_addr] = 0x0 # initialise state to 0
75+
ulp.run(entry_addr)
76+
77+
while True:
78+
print(hex(mem32[ULP_MEM_BASE + load_addr] & ULP_DATA_MASK))
79+

0 commit comments

Comments
 (0)