|
| 1 | +""" |
| 2 | +Simple example showing how to control a GPIO pin from the ULP coprocessor. |
| 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/1cb31e5/components/soc/esp32/include/soc/soc.h |
| 26 | +#define DR_REG_RTCIO_BASE 0x3ff48400 |
| 27 | +
|
| 28 | +# constants from: |
| 29 | +# https://github.com/espressif/esp-idf/blob/1cb31e5/components/soc/esp32/include/soc/rtc_io_reg.h |
| 30 | +#define RTC_IO_TOUCH_PAD2_REG (DR_REG_RTCIO_BASE + 0x9c) |
| 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_W1TS_REG (DR_REG_RTCIO_BASE + 0x10) |
| 34 | +#define RTC_GPIO_ENABLE_W1TC_REG (DR_REG_RTCIO_BASE + 0x14) |
| 35 | +#define RTC_GPIO_ENABLE_W1TS_S 14 |
| 36 | +#define RTC_GPIO_ENABLE_W1TC_S 14 |
| 37 | +#define RTC_GPIO_OUT_DATA_S 14 |
| 38 | +
|
| 39 | +# constants from: |
| 40 | +# https://github.com/espressif/esp-idf/blob/1cb31e5/components/soc/esp32/include/soc/rtc_io_channel.h |
| 41 | +#define RTCIO_GPIO2_CHANNEL 12 |
| 42 | +
|
| 43 | +# When accessed from the RTC module (ULP) GPIOs need to be addressed by their channel number |
| 44 | +.set gpio, RTCIO_GPIO2_CHANNEL |
| 45 | +.set token, 0xcafe # magic token |
| 46 | +
|
| 47 | +.text |
| 48 | +magic: .long 0 |
| 49 | +state: .long 0 |
| 50 | +
|
| 51 | +.global entry |
| 52 | +entry: |
| 53 | + # load magic flag |
| 54 | + move r0, magic |
| 55 | + ld r1, r0, 0 |
| 56 | +
|
| 57 | + # test if we have initialised already |
| 58 | + sub r1, r1, token |
| 59 | + jump after_init, eq # jump if magic == token (note: "eq" means the last instruction (sub) resulted in 0) |
| 60 | +
|
| 61 | +init: |
| 62 | + # connect GPIO to ULP (0: GPIO connected to digital GPIO module, 1: GPIO connected to analog RTC module) |
| 63 | + WRITE_RTC_REG(RTC_IO_TOUCH_PAD2_REG, RTC_IO_TOUCH_PAD2_MUX_SEL_M, 1, 1); |
| 64 | +
|
| 65 | + # GPIO shall be output, not input |
| 66 | + WRITE_RTC_REG(RTC_GPIO_OUT_REG, RTC_GPIO_OUT_DATA_S + gpio, 1, 1); |
| 67 | +
|
| 68 | + # store that we're done with initialisation |
| 69 | + move r0, magic |
| 70 | + move r1, token |
| 71 | + st r1, r0, 0 |
| 72 | +
|
| 73 | +after_init: |
| 74 | + move r1, state |
| 75 | + ld r0, r1, 0 |
| 76 | +
|
| 77 | + move r2, 1 |
| 78 | + sub r0, r2, r0 # toggle state |
| 79 | + st r0, r1, 0 # store updated state |
| 80 | +
|
| 81 | + jumpr on, 0, gt # if r0 (state) > 0, jump to 'on' |
| 82 | + jump off # else jump to 'off' |
| 83 | +
|
| 84 | +on: |
| 85 | + # turn on led (set GPIO) |
| 86 | + WRITE_RTC_REG(RTC_GPIO_ENABLE_W1TS_REG, RTC_GPIO_ENABLE_W1TS_S + gpio, 1, 1) |
| 87 | + jump exit |
| 88 | +
|
| 89 | +off: |
| 90 | + # turn off led (clear GPIO) |
| 91 | + WRITE_RTC_REG(RTC_GPIO_ENABLE_W1TC_REG, RTC_GPIO_ENABLE_W1TC_S + gpio, 1, 1) |
| 92 | + jump exit |
| 93 | +
|
| 94 | +exit: |
| 95 | + halt # go back to sleep until next wakeup period |
| 96 | +""" |
| 97 | + |
| 98 | +binary = src_to_binary(source) |
| 99 | + |
| 100 | +load_addr, entry_addr = 0, 8 |
| 101 | + |
| 102 | +ULP_MEM_BASE = 0x50000000 |
| 103 | +ULP_DATA_MASK = 0xffff # ULP data is only in lower 16 bits |
| 104 | + |
| 105 | +ulp = ULP() |
| 106 | +ulp.set_wakeup_period(0, 500000) # use timer0, wakeup after 500000usec (0.5s) |
| 107 | +ulp.load_binary(load_addr, binary) |
| 108 | + |
| 109 | +ulp.run(entry_addr) |
| 110 | + |
| 111 | +while True: |
| 112 | + print(hex(mem32[ULP_MEM_BASE + load_addr] & ULP_DATA_MASK), # magic token |
| 113 | + hex(mem32[ULP_MEM_BASE + load_addr + 4] & ULP_DATA_MASK) # current state |
| 114 | + ) |
0 commit comments