|
| 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 | + ) |
0 commit comments