Skip to content

Commit

Permalink
Add examples for ESP32-S2 and ESP32-S3
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
wnienhaus committed Aug 31, 2023
1 parent ed28d27 commit debff30
Show file tree
Hide file tree
Showing 7 changed files with 327 additions and 5 deletions.
8 changes: 5 additions & 3 deletions examples/blink.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
"""
Example for: ESP32
Simple example showing how to control a GPIO pin from the ULP coprocessor.
The GPIO port is configured to be attached to the RTC module, and then set
Expand All @@ -22,11 +24,11 @@

source = """\
# constants from:
# https://github.com/espressif/esp-idf/blob/1cb31e5/components/soc/esp32/include/soc/soc.h
# https://github.com/espressif/esp-idf/blob/v5.0.2/components/soc/esp32/include/soc/reg_base.h
#define DR_REG_RTCIO_BASE 0x3ff48400
# constants from:
# https://github.com/espressif/esp-idf/blob/1cb31e5/components/soc/esp32/include/soc/rtc_io_reg.h
# https://github.com/espressif/esp-idf/blob/v5.0.2/components/soc/esp32/include/soc/rtc_io_reg.h
#define RTC_IO_TOUCH_PAD2_REG (DR_REG_RTCIO_BASE + 0x9c)
#define RTC_IO_TOUCH_PAD2_MUX_SEL_M (BIT(19))
#define RTC_GPIO_OUT_REG (DR_REG_RTCIO_BASE + 0x0)
Expand All @@ -35,7 +37,7 @@
#define RTC_GPIO_OUT_DATA_S 14
# constants from:
# https://github.com/espressif/esp-idf/blob/1cb31e5/components/soc/esp32/include/soc/rtc_io_channel.h
# https://github.com/espressif/esp-idf/blob/v5.0.2/components/soc/esp32/include/soc/rtc_io_channel.h
#define RTCIO_GPIO2_CHANNEL 12
# When accessed from the RTC module (ULP) GPIOs need to be addressed by their channel number
Expand Down
112 changes: 112 additions & 0 deletions examples/blink_s2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
"""
Example for: ESP32-S2 and ESP32-S3
The GPIO port is configured to be attached to the RTC module, and then set
to OUTPUT mode. To avoid re-initializing the GPIO on every wakeup, a magic
token gets set in memory.
After every change of state, the ULP is put back to sleep again until the
next wakeup. The ULP wakes up every 500ms to change the state of the GPIO
pin. An LED attached to the GPIO pin would toggle on and off every 500ms.
The end of the python script has a loop to show the value of the magic token
and the current state, so you can confirm the magic token gets set and watch
the state value changing. If the loop is stopped (Ctrl-C), the LED attached
to the GPIO pin continues to blink, because the ULP runs independently from
the main processor.
"""

from esp32 import ULP
from machine import mem32
from esp32_ulp import src_to_binary

source = """\
# constants from:
# https://github.com/espressif/esp-idf/blob/v5.0.2/components/soc/esp32s2/include/soc/reg_base.h
#define DR_REG_RTCIO_BASE 0x3f408400
# constants from:
# https://github.com/espressif/esp-idf/blob/v5.0.2/components/soc/esp32s2/include/soc/rtc_io_reg.h
#define RTC_IO_TOUCH_PAD2_REG (DR_REG_RTCIO_BASE + 0x8c)
#define RTC_IO_TOUCH_PAD2_MUX_SEL_M (BIT(19))
#define RTC_GPIO_OUT_REG (DR_REG_RTCIO_BASE + 0x0)
#define RTC_GPIO_ENABLE_REG (DR_REG_RTCIO_BASE + 0xc)
#define RTC_GPIO_ENABLE_S 10
#define RTC_GPIO_OUT_DATA_S 10
# constants from:
# https://github.com/espressif/esp-idf/blob/v5.0.2/components/soc/esp32s2/include/soc/rtc_io_channel.h
#define RTCIO_GPIO2_CHANNEL 2
# When accessed from the RTC module (ULP) GPIOs need to be addressed by their channel number
.set gpio, RTCIO_GPIO2_CHANNEL
.set token, 0xcafe # magic token
.text
magic: .long 0
state: .long 0
.global entry
entry:
# load magic flag
move r0, magic
ld r1, r0, 0
# test if we have initialised already
sub r1, r1, token
jump after_init, eq # jump if magic == token (note: "eq" means the last instruction (sub) resulted in 0)
init:
# connect GPIO to ULP (0: GPIO connected to digital GPIO module, 1: GPIO connected to analog RTC module)
WRITE_RTC_REG(RTC_IO_TOUCH_PAD2_REG, RTC_IO_TOUCH_PAD2_MUX_SEL_M, 1, 1);
# GPIO shall be output, not input (this also enables a pull-down by default)
WRITE_RTC_REG(RTC_GPIO_ENABLE_REG, RTC_GPIO_ENABLE_S + gpio, 1, 1)
# store that we're done with initialisation
move r0, magic
move r1, token
st r1, r0, 0
after_init:
move r1, state
ld r0, r1, 0
move r2, 1
sub r0, r2, r0 # toggle state
st r0, r1, 0 # store updated state
jumpr on, 0, gt # if r0 (state) > 0, jump to 'on'
jump off # else jump to 'off'
on:
# turn on led (set GPIO)
WRITE_RTC_REG(RTC_GPIO_OUT_REG, RTC_GPIO_OUT_DATA_S + gpio, 1, 1)
jump exit
off:
# turn off led (clear GPIO)
WRITE_RTC_REG(RTC_GPIO_OUT_REG, RTC_GPIO_OUT_DATA_S + gpio, 1, 0)
jump exit
exit:
halt # go back to sleep until next wakeup period
"""

binary = src_to_binary(source, cpu="esp32s2") # cpu is esp32 or esp32s2

load_addr, entry_addr = 0, 8

ULP_MEM_BASE = 0x50000000
ULP_DATA_MASK = 0xffff # ULP data is only in lower 16 bits

ulp = ULP()
ulp.set_wakeup_period(0, 500000) # use timer0, wakeup after 500000usec (0.5s)
ulp.load_binary(load_addr, binary)

ulp.run(entry_addr)

while True:
print(hex(mem32[ULP_MEM_BASE + load_addr] & ULP_DATA_MASK), # magic token
hex(mem32[ULP_MEM_BASE + load_addr + 4] & ULP_DATA_MASK) # current state
)
2 changes: 2 additions & 0 deletions examples/counter.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
"""
Example for: ESP32
Very basic example showing data exchange main CPU <--> ULP coprocessor.
To show that the ULP is doing something, it just increments the value <data>.
Expand Down
46 changes: 46 additions & 0 deletions examples/counter_s2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
"""
Example for: ESP32-S2 and ESP32-S3
Very basic example showing data exchange main CPU <--> ULP coprocessor.
To show that the ULP is doing something, it just increments the value <data>.
It does that once per ulp timer wakeup (and then the ULP halts until it gets
waked up via the timer again).
The timer is set to a rather long period, so you can watch the data value
incrementing (see loop at the end).
"""

from esp32 import ULP
from machine import mem32

from esp32_ulp import src_to_binary

source = """\
data: .long 0
entry: move r3, data # load address of data into r3
ld r2, r3, 0 # load data contents ([r3+0]) into r2
add r2, r2, 1 # increment r2
st r2, r3, 0 # store r2 contents into data ([r3+0])
halt # halt ULP co-prozessor (until it gets waked up again)
"""

binary = src_to_binary(source, cpu="esp32s2") # cpu is esp32 or esp32s2

load_addr, entry_addr = 0, 4

ULP_MEM_BASE = 0x50000000
ULP_DATA_MASK = 0xffff # ULP data is only in lower 16 bits

ulp = ULP()
ulp.set_wakeup_period(0, 50000) # use timer0, wakeup after 50.000 cycles
ulp.load_binary(load_addr, binary)

mem32[ULP_MEM_BASE + load_addr] = 0x1000
ulp.run(entry_addr)

while True:
print(hex(mem32[ULP_MEM_BASE + load_addr] & ULP_DATA_MASK))

6 changes: 4 additions & 2 deletions examples/readgpio.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
"""
Example for: ESP32
Very basic example showing how to read a GPIO pin from the ULP and access
that data from the main CPU.
In this case GPIO4 is being read. Note that the ULP needs to refer to GPIOs
via their RTC channel number. You can see the mapping in this file:
https://github.com/espressif/esp-idf/blob/v4.4.1/components/soc/esp32/include/soc/rtc_io_channel.h#L51
https://github.com/espressif/esp-idf/blob/v5.0.2/components/soc/esp32/include/soc/rtc_io_channel.h#L51
If you change to a different GPIO number, make sure to modify both the channel
number and also the RTC_IO_TOUCH_PAD0_* references appropriately. The best place
to see the mappings might be this table here (notice the "real GPIO numbers" as
comments to each line):
https://github.com/espressif/esp-idf/blob/v4.4.1/components/soc/esp32/rtc_io_periph.c#L61
https://github.com/espressif/esp-idf/blob/v5.0.2/components/soc/esp32/rtc_io_periph.c#L53
The timer is set to a rather long period, so you can watch the data value
change as you change the GPIO input (see loop at the end).
Expand Down
79 changes: 79 additions & 0 deletions examples/readgpio_s2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
"""
Example for: ESP32-S2
Very basic example showing how to read a GPIO pin from the ULP and access
that data from the main CPU.
In this case GPIO4 is being read. Note that the ULP needs to refer to GPIOs
via their RTC channel number. You can see the mapping in this file:
https://github.com/espressif/esp-idf/blob/v5.0.2/components/soc/esp32s2/include/soc/rtc_io_channel.h#L33
If you change to a different GPIO number, make sure to modify both the channel
number and also the RTC_IO_TOUCH_PAD4_* references appropriately. The best place
to see the mappings might be this table here (notice the "real GPIO numbers" as
comments to each line):
https://github.com/espressif/esp-idf/blob/v5.0.2/components/soc/esp32s2/rtc_io_periph.c#L60
The timer is set to a rather long period, so you can watch the data value
change as you change the GPIO input (see loop at the end).
"""

from esp32 import ULP
from machine import mem32

from esp32_ulp import src_to_binary

source = """\
#define DR_REG_RTCIO_BASE 0x3f408400
#define RTC_IO_TOUCH_PAD4_REG (DR_REG_RTCIO_BASE + 0x94)
#define RTC_IO_TOUCH_PAD4_MUX_SEL_M (BIT(19))
#define RTC_IO_TOUCH_PAD4_FUN_IE_M (BIT(13))
#define RTC_GPIO_IN_REG (DR_REG_RTCIO_BASE + 0x24)
#define RTC_GPIO_IN_NEXT_S 10
#define DR_REG_SENS_BASE 0x3f408800
#define SENS_SAR_IO_MUX_CONF_REG (DR_REG_SENS_BASE + 0x0144)
#define SENS_IOMUX_CLK_GATE_EN (BIT(31))
.set channel, 4
state: .long 0
entry:
# enable IOMUX clock
WRITE_RTC_FIELD(SENS_SAR_IO_MUX_CONF_REG, SENS_IOMUX_CLK_GATE_EN, 1)
# connect GPIO to the RTC subsystem so the ULP can read it
WRITE_RTC_REG(RTC_IO_TOUCH_PAD4_REG, RTC_IO_TOUCH_PAD4_MUX_SEL_M, 1, 1)
# switch the GPIO into input mode
WRITE_RTC_REG(RTC_IO_TOUCH_PAD4_REG, RTC_IO_TOUCH_PAD4_FUN_IE_M, 1, 1)
# read the GPIO's current state into r0
READ_RTC_REG(RTC_GPIO_IN_REG, RTC_GPIO_IN_NEXT_S + channel, 1)
# set r3 to the memory address of "state"
move r3, state
# store what was read into r0 into the "state" variable
st r0, r3, 0
# halt ULP co-processor (until it gets woken up again)
halt
"""

binary = src_to_binary(source, cpu="esp32s2") # cpu is esp32 or esp32s2

load_addr, entry_addr = 0, 4

ULP_MEM_BASE = 0x50000000
ULP_DATA_MASK = 0xffff # ULP data is only in lower 16 bits

ulp = ULP()
ulp.set_wakeup_period(0, 50000) # use timer0, wakeup after 50.000 cycles
ulp.load_binary(load_addr, binary)

mem32[ULP_MEM_BASE + load_addr] = 0x0 # initialise state to 0
ulp.run(entry_addr)

while True:
print(hex(mem32[ULP_MEM_BASE + load_addr] & ULP_DATA_MASK))

79 changes: 79 additions & 0 deletions examples/readgpio_s3.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
"""
Example for: ESP32-S3
Very basic example showing how to read a GPIO pin from the ULP and access
that data from the main CPU.
In this case GPIO4 is being read. Note that the ULP needs to refer to GPIOs
via their RTC channel number. You can see the mapping in this file:
https://github.com/espressif/esp-idf/blob/v5.0.2/components/soc/esp32s3/include/soc/rtc_io_channel.h#L33
If you change to a different GPIO number, make sure to modify both the channel
number and also the RTC_IO_TOUCH_PAD2_* references appropriately. The best place
to see the mappings might be this table here (notice the "real GPIO numbers" as
comments to each line):
https://github.com/espressif/esp-idf/blob/v5.0.2/components/soc/esp32s3/rtc_io_periph.c#L60
The timer is set to a rather long period, so you can watch the data value
change as you change the GPIO input (see loop at the end).
"""

from esp32 import ULP
from machine import mem32

from esp32_ulp import src_to_binary

source = """\
#define DR_REG_RTCIO_BASE 0x60008400
#define RTC_IO_TOUCH_PAD2_REG (DR_REG_RTCIO_BASE + 0x8c)
#define RTC_IO_TOUCH_PAD2_MUX_SEL_M (BIT(19))
#define RTC_IO_TOUCH_PAD2_FUN_IE_M (BIT(13))
#define RTC_GPIO_IN_REG (DR_REG_RTCIO_BASE + 0x24)
#define RTC_GPIO_IN_NEXT_S 10
#define DR_REG_SENS_BASE 0x60008800
#define SENS_SAR_PERI_CLK_GATE_CONF_REG (DR_REG_SENS_BASE + 0x104)
#define SENS_IOMUX_CLK_EN (BIT(31))
.set channel, 2
state: .long 0
entry:
# enable IOMUX clock
WRITE_RTC_FIELD(SENS_SAR_PERI_CLK_GATE_CONF_REG, SENS_IOMUX_CLK_EN, 1)
# connect GPIO to the RTC subsystem so the ULP can read it
WRITE_RTC_REG(RTC_IO_TOUCH_PAD2_REG, RTC_IO_TOUCH_PAD2_MUX_SEL_M, 1, 1)
# switch the GPIO into input mode
WRITE_RTC_REG(RTC_IO_TOUCH_PAD2_REG, RTC_IO_TOUCH_PAD2_FUN_IE_M, 1, 1)
# read the GPIO's current state into r0
READ_RTC_REG(RTC_GPIO_IN_REG, RTC_GPIO_IN_NEXT_S + channel, 1)
# set r3 to the memory address of "state"
move r3, state
# store what was read into r0 into the "state" variable
st r0, r3, 0
# halt ULP co-processor (until it gets woken up again)
halt
"""

binary = src_to_binary(source, cpu="esp32s2") # cpu is esp32 or esp32s2

load_addr, entry_addr = 0, 4

ULP_MEM_BASE = 0x50000000
ULP_DATA_MASK = 0xffff # ULP data is only in lower 16 bits

ulp = ULP()
ulp.set_wakeup_period(0, 50000) # use timer0, wakeup after 50.000 cycles
ulp.load_binary(load_addr, binary)

mem32[ULP_MEM_BASE + load_addr] = 0x0 # initialise state to 0
ulp.run(entry_addr)

while True:
print(hex(mem32[ULP_MEM_BASE + load_addr] & ULP_DATA_MASK))

0 comments on commit debff30

Please sign in to comment.