Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Still Struggling #70

Closed
kjm1102 opened this issue Apr 25, 2022 · 37 comments
Closed

Still Struggling #70

kjm1102 opened this issue Apr 25, 2022 · 37 comments

Comments

@kjm1102
Copy link

kjm1102 commented Apr 25, 2022

The https://github.com/micropython/micropython-esp32-ulp/blob/master/examples/counter.py I think shows how to get the count from the ulp into the .py program. However to setup a ULP counter that counts pulses from a gpio pin (eg the ulp assembler code at https://esp32.com/viewtopic.php?t=12454 it looks like the .py program has to send some values (like gpio pin number & debounce info) to the ULP? Could we maybe have another example for us novices on how to do that?

@wnienhaus
Copy link
Collaborator

For 99% of things you don't need any setup in the main program, because the ULP can also do that. Most of the rtc_* C functions actually translate down to macros which set peripheral registers. If you can trace the C code of those functions to where the peripheral registers get set, you can use that code in the ULP.

The main thing you would want to do is to set the GPIO pin into RTC mode (so it can be read from the ULP) and to set it into input mode. The blink.py example shows how to do the former: https://github.com/micropython/micropython-esp32-ulp/blob/master/examples/blink.py#L63 and to set a GPIO to input mode use something like this:

WRITE_RTC_REG(RTC_IO_TOUCH_PAD0_REG, RTC_IO_TOUCH_PAD0_FUN_IE_M, 1, 1); //set gpio4 to input mode

With regards to "variable passing", you can hard code the GPIO pin number, debounce info, etc into the ULP assembler code. In the example you referenced, they simply reference memory locations from C that are in the RTC memory as set up by the assembler code (e.g. debounce_counter: .long 0), but there is no need to actually reference most of those memory addresses from the main program. The only one you are likely interested in from the MicroPython code is the counter value and the counter.py example shows how to read that. Note - from MicroPython we need to read by offset into the memory, rather than "variable names", because MicroPython has no idea of what was declared in the assembly code (there is an old issue #34 that had an idea of how we could implement such a thing, but so far it's not possible).

@kjm1102
Copy link
Author

kjm1102 commented May 18, 2022

I had a red hot go at counting lows on gpio4

from esp32 import ULP
from machine import mem32, deepsleep, reset_cause, RTC; rtc=RTC()
from esp32_ulp import src_to_binary
from time import time
import json


load_addr, entry_addr = 0, 4
ULP_MEM_BASE = 0x50000000
ULP_DATA_MASK = 0xffff                           # ULP data in lower 16 bits

causes={2:'reset', 1:'hardreset', 3:'wdt', 4:'wakeup', 5:'pwrup'}; cause=causes[reset_cause()]; print(), print('pwrup cause', cause)
if cause!='wakeup':
  dic={'To':time(), 'Co':0}; rtc.memory(json.dumps(dic))
  source = """\
  # constants from:
  # https://github.com/espressif/esp-idf/blob/1cb31e5/components/soc/esp32/include/soc/soc.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
  #define RTC_IO_TOUCH_PAD0_REG        (DR_REG_RTCIO_BASE + 0x94)
  #define RTC_IO_TOUCH_PAD0_MUX_SEL_M  (BIT(19))
  #define RTC_IO_TOUCH_PAD0_FUN_IE_M    (BIT(13))
  #define RTC_GPIO_IN_REG              (DR_REG_RTCIO_BASE + 0x24)
  #define RTC_GPIO_IN_NEXT_S           14
  # constants from:
  # https://github.com/espressif/esp-idf/blob/1cb31e5/components/soc/esp32/include/soc/rtc_io_channel.h
  #define RTCIO_GPIO4_CHANNEL          10
  # When accessed from the RTC module (ULP) GPIOs need to be addressed by their channel number                                                        gpio4 is ch 10
  .set gpio, RTCIO_GPIO4_CHANNEL
  # connect GPIO to ULP (0:GPIO connected to digital GPIO module, 1:GPIO connected to analog RTC module)
  WRITE_RTC_REG(RTC_IO_TOUCH_PAD0_REG, RTC_IO_TOUCH_PAD0_FUN_IE_M, 1, 1); //set gpio4 to input mode
  data: .long 0
  entry:
    move r3, gpio                                            # r3 = gpio addr
    ld r3, r3, 0                                             # r3 = gpio + 0
    READ_RTC_REG(RTC_GPIO_IN_REG, RTC_GPIO_IN_NEXT_S, 16)
    rsh r0, r0, r3                                           # r0 = r0 >> r3
    jumpr inc, 0, eq                                         # jump to incrementer if input=r0=0
    halt
  inc:
    move r3, data                                            # r3=data addr
    ld r2, r3, 0                                             # r2=data=r3+0
    add r2, r2, 1                                            # r2=r2+1
    st r2, r3, 0                                             # data(addr[r3+0])=r2
    halt                                                     # halt ULP co-prozessor for wakeup_period us
  """
  binary = src_to_binary(source)
  ulp = ULP()
  ulp.set_wakeup_period(0, 50000)                # use timer0, wakeup after 50ms
  ulp.load_binary(load_addr, binary)
  mem32[ULP_MEM_BASE + load_addr] = 0x1000
  ulp.run(entry_addr)
print(hex(mem32[ULP_MEM_BASE + load_addr] & ULP_DATA_MASK))
deepsleep(1000)

but the counter isn't incrementing with the gpio4 pin connected to gnd, just stuck on 0x1000

all I've got to show for my day with ulp assembler is my brain hurts. Also how did you know that gpio4 is RTC_IO_TOUCH_PAD0_REG ?

@kjm1102
Copy link
Author

kjm1102 commented May 19, 2022

Had another go at incrementing a counter each time I find a lo on gpio4(rtc ch10)

from esp32 import ULP
from machine import mem32, deepsleep, reset_cause, RTC; rtc=RTC()
from esp32_ulp import src_to_binary
from time import time
import json


load_addr, entry_addr = 0, 4
ULP_MEM_BASE = 0x50000000
ULP_DATA_MASK = 0xffff                                                    # ULP data in lower 16 bits

causes={2:'reset', 1:'hardreset', 3:'wdt', 4:'wakeup', 5:'pwrup'}; cause=causes[reset_cause()]; print(), print('pwrup cause', cause)
if cause!='wakeup':
  dic={'To':time(), 'Co':0}; rtc.memory(json.dumps(dic))
  source = """\
  #define DR_REG_RTCIO_BASE            0x3ff48400                         # from https://github.com/espressif/esp-idf/blob/1cb31e5/components/soc/esp32/include/soc/soc.h
  #define RTC_IO_TOUCH_PAD0_REG        (DR_REG_RTCIO_BASE + 0x94)         # from https://github.com/espressif/esp-idf/blob/1cb31e5/components/soc/esp32/include/soc/rtc_io_reg.h
  #define RTC_IO_TOUCH_PAD0_MUX_SEL_M  (BIT(19))
  #define RTC_IO_TOUCH_PAD0_FUN_IE_M   (BIT(13))
  #define RTC_GPIO_IN_REG              (DR_REG_RTCIO_BASE + 0x24)
  #define RTC_GPIO_IN_NEXT_S           14
  .set channel, 10                                                        # from https://github.com/espressif/esp-idf/blob/1cb31e5/components/soc/esp32/include/soc/rtc_io_channel.h
  WRITE_RTC_REG(RTC_IO_TOUCH_PAD0_REG, RTC_IO_TOUCH_PAD0_FUN_IE_M, 1, 1); # set gpio4 to input mode, apparently gpio4(ie ch10)=TOUCH_PAD0 (connected to digital GPIO:0, analog RTC:1)
  data:   .long 0
  entry:  move r3, channel                                                # r3 = ch addr
          ld r3, r3, 0                                                    # r3 = ch + 0
          READ_RTC_REG(RTC_GPIO_IN_REG, RTC_GPIO_IN_NEXT_S, 16)           # use READ_RTC_REG(RTC_GPIO_IN_REG, RTC_GPIO_IN_NEXT_S + 16, 2) for RTC IOs 16 & 17
          rsh r0, r0, r3                                                  # r0 = r0 >> r3 = analog input
          and r0, r0, 1                                                   # r0 = analog input msd
          jumpr incr, 0, eq                                               # jump to incrementer if input msd=0
          halt                                                            # halt ULP for wakeup_period us
  incr:   move r3, data                                                   # r3=data addr
          ld r2, r3, 0                                                    # r2=MEM[data addr + 0]=data
          add r2, r2, 1                                                   # r2=r2+1
          st r2, r3, 0                                                    # data(addr[r3+0])=r2
          halt
  """
  binary = src_to_binary(source)
  ulp = ULP()
  ulp.set_wakeup_period(0, 50000)                                         # use timer0, wakeup after 50ms
  ulp.load_binary(load_addr, binary)
  mem32[ULP_MEM_BASE + load_addr] = 0x1000
  ulp.run(entry_addr)
print(hex(mem32[ULP_MEM_BASE + load_addr] & ULP_DATA_MASK))
deepsleep(1000)

Still not incrementing sadly

@kjm1102
Copy link
Author

kjm1102 commented May 20, 2022

Why does it always have to be a feast or a famine in this life?

from esp32 import ULP
from machine import mem32, deepsleep, reset_cause, RTC; rtc=RTC()
from esp32_ulp import src_to_binary
from time import time
import json

load_addr, entry_addr = 0, 4
ULP_MEM_BASE = 0x50000000
ULP_DATA_MASK = 0xffff                                                    # ULP data in lower 16 bits

causes={2:'reset', 1:'hardreset', 3:'wdt', 4:'wakeup', 5:'pwrup'}; cause=causes[reset_cause()]; print(), print('pwrup cause', cause)
if cause!='wakeup':
  dic={'To':time(), 'Co':0}; rtc.memory(json.dumps(dic))
  source = """\
  #define DR_REG_RTCIO_BASE            0x3ff48400                         # from https://github.com/espressif/esp-idf/blob/1cb31e5/components/soc/esp32/include/soc/soc.h
  #define RTC_IO_TOUCH_PAD0_REG        (DR_REG_RTCIO_BASE + 0x94)         # from https://github.com/espressif/esp-idf/blob/1cb31e5/components/soc/esp32/include/soc/rtc_io_reg.h
  #define RTC_IO_TOUCH_PAD0_MUX_SEL_M  (BIT(19))
  #define RTC_IO_TOUCH_PAD0_FUN_IE_M   (BIT(13))
  #define RTC_GPIO_IN_REG              (DR_REG_RTCIO_BASE + 0x24)
  #define RTC_GPIO_IN_NEXT_S           14
  .set channel, 10                                                        # gpio4=ch10 https://github.com/espressif/esp-idf/blob/1cb31e5/components/soc/esp32/include/soc/rtc_io_channel.h
  #WRITE_RTC_REG(RTC_IO_TOUCH_PAD0_REG, RTC_IO_TOUCH_PAD0_FUN_IE_M, 1, 1); # set gpio4 to input mode, apparently gpio4(ie ch10)=TOUCH_PAD0 (connected to digital GPIO:0, analog RTC:1)
  data:   .long 0
  entry:  move r3, channel                                                # r3 = ch addr
          ld r3, r3, 0                                                    # r3 = ch + 0
          READ_RTC_REG(RTC_GPIO_IN_REG, RTC_GPIO_IN_NEXT_S, 16)           # use READ_RTC_REG(RTC_GPIO_IN_REG, RTC_GPIO_IN_NEXT_S + 16, 2) for RTC IOs 16 & 17
          rsh r0, r0, r3                                                  # r0 = r0 >> r3 = analog input
          and r0, r0, 1                                                   # r0 = analog input msd
          jumpr incr, 0, eq                                               # jump to incrementer if analog input msd=0
          halt                                                            # halt ULP for wakeup_period us
  incr:   move r3, data                                                   # r3 = data addr
          ld r2, r3, 0                                                    # r2 = MEM[data addr + 0]=data
          add r2, r2, 1                                                   # r2 = r2+1
          st r2, r3, 0                                                    # data = MEM(addr[r3+0]) = r2
          halt
  """
  binary = src_to_binary(source)
  ulp = ULP()
  ulp.set_wakeup_period(0, 50000)                                         # use timer0, wakeup after 50ms
  ulp.load_binary(load_addr, binary)
  mem32[ULP_MEM_BASE + load_addr] = 0x1000
  ulp.run(entry_addr)
print(mem32[ULP_MEM_BASE + load_addr] & ULP_DATA_MASK)
deepsleep(1000)

Got it counting again by knobbling line22 (line 22 inhibits any counting whatsoever). Unfortunately it counts continuously without reference to the state of gpio4(channel10)

@kjm1102
Copy link
Author

kjm1102 commented May 21, 2022

Today I gave up on counting & tried my luck reading the staus of gpio4

from esp32 import ULP
from machine import mem32, deepsleep, reset_cause, RTC; rtc=RTC()
from esp32_ulp import src_to_binary
from time import time
import json


load_addr, entry_addr = 0, 4
ULP_MEM_BASE = 0x50000000
ULP_DATA_MASK = 0xffff                                                    # ULP data in lower 16 bits

causes={2:'reset', 1:'hardreset', 3:'wdt', 4:'wakeup', 5:'pwrup'}; cause=causes[reset_cause()]; print(), print('pwrup cause', cause)
if cause!='wakeup':
  dic={'To':time(), 'Co':0}; rtc.memory(json.dumps(dic))
  source = """\
  #define DR_REG_RTCIO_BASE            0x3ff48400
  #define RTC_IO_TOUCH_PAD0_REG        (DR_REG_RTCIO_BASE + 0x94)
  #define RTC_IO_TOUCH_PAD0_MUX_SEL_M  (BIT(19))
  #define RTC_IO_TOUCH_PAD0_FUN_IE_M   (BIT(13))
  #define RTC_GPIO_IN_REG              (DR_REG_RTCIO_BASE + 0x24)
  #define RTC_GPIO_IN_NEXT_S           14
  .set channel, 10
  WRITE_RTC_REG(RTC_IO_TOUCH_PAD0_REG, RTC_IO_TOUCH_PAD0_FUN_IE_M, 1, 1)
  data:   .long 0
  entry:  move r3, channel                                                # r3 = ch addr
          ld r3, r3, 0                                                    # r3 = MEM[channel addr + 0] = channel
          READ_RTC_REG(RTC_GPIO_IN_REG, RTC_GPIO_IN_NEXT_S, 16)           # use READ_RTC_REG(RTC_GPIO_IN_REG, RTC_GPIO_IN_NEXT_S + 16, 2) for RTC channels 16 & 17
          rsh r0, r0, r3                                                  # r0 = r0 >> r3 = analog input
          and r2, r0, 1                                                   # r2 = ro>>1 = analog input msd
          st r2, r3, 0                                                    # data = MEM(addr[r3+0]) = r2
    """
  binary = src_to_binary(source)
  ulp = ULP()
  ulp.set_wakeup_period(0, 50000)                                         # use timer0, wakeup after 50ms
  ulp.load_binary(load_addr, binary)
  mem32[ULP_MEM_BASE + load_addr] = 0x1000
  ulp.run(entry_addr)

print('gpio4', mem32[ULP_MEM_BASE+load_addr] & ULP_DATA_MASK)
deepsleep(1000)                                                          # wakeup+run takes ~4s, so 30s between prints

No joy, I can toggle gpio4 input hi/lo as much as I like, the register is always 4096. If it was easy everyone would be doing it, right?

@kjm1102
Copy link
Author

kjm1102 commented May 21, 2022

Trial & error, story of my programming life.

from esp32 import ULP
from machine import mem32, mem8, deepsleep, reset_cause, RTC; rtc=RTC()
from esp32_ulp import src_to_binary
from time import time
import json


load_addr, entry_addr = 0, 4
ULP_MEM_BASE = 0x50000000
ULP_DATA_MASK = 0xffff                                                     # ULP data in lower 16 bits

causes={2:'reset', 1:'hardreset', 3:'wdt', 4:'wakeup', 5:'pwrup'}; cause=causes[reset_cause()]; print(), print('pwrup cause', cause)
if cause!='wakeup':
  dic={'To':time(), 'Co':0}; rtc.memory(json.dumps(dic))
  source = """\
  #define DR_REG_RTCIO_BASE            0x3ff48400
  #define RTC_IO_TOUCH_PAD0_REG        (DR_REG_RTCIO_BASE + 0x94)
  #define RTC_IO_TOUCH_PAD0_MUX_SEL_M  (BIT(19))
  #define RTC_IO_TOUCH_PAD0_FUN_IE_M   (BIT(13))
  #define RTC_GPIO_IN_REG              (DR_REG_RTCIO_BASE + 0x24)
  #define RTC_GPIO_IN_NEXT_S           14
  .set channel, 10
  WRITE_RTC_REG(RTC_IO_TOUCH_PAD0_REG, RTC_IO_TOUCH_PAD0_MUX_SEL_M, 1, 1) # connect GPIO to ULP
  WRITE_RTC_REG(RTC_IO_TOUCH_PAD0_REG, RTC_IO_TOUCH_PAD0_FUN_IE_M, 1, 1)  # gpio input mode

  data:   .long 0
  entry:  move r3, channel                                                # r3 = ch addr
          ld r3, r3, 0                                                    # r3 = MEM(addr[channel+0]) = channel
          READ_RTC_REG(RTC_GPIO_IN_REG, RTC_GPIO_IN_NEXT_S, 16)           # use READ_RTC_REG(RTC_GPIO_IN_REG, RTC_GPIO_IN_NEXT_S + 16, 2) for RTC channels 16 & 17
          rsh r0, r0, r3                                                  # r0 = r0 >> r3 = analog input
          and r2, r0, 1                                                   # r2 = ro>>1 = analog input msd
          st r2, r3, 0                                                    # data = MEM(addr[r3+0]) = r2
    """
  binary = src_to_binary(source)
  ulp = ULP()
  ulp.set_wakeup_period(0, 50000)                                         # use timer0, wakeup after 50ms
  ulp.load_binary(load_addr, binary)
  mem32[ULP_MEM_BASE + load_addr] = 0x1000
  ulp.run(entry_addr)

print('gpio4', mem32[ULP_MEM_BASE+load_addr] & ULP_DATA_MASK)
print([chr(mem8[0x50000000 + i]) for i in range(0,32)])
deepsleep(1000)                                                           # wakeup+run takes ~4s, so 30s between prints

pwrup cause wakeup
gpio4 4096
['\x00', '\x10', '\x00', '\x00', '%', '\x05', '\xb4', '\x16', '\x00', '\x00', '\x00', '\x00', '\xa3', '\x00', '\x80', 'r', '\x0f', '\x00', '\x00', '\xd0', '\t', '\x01', '\xb8', '.', '0', '\x00', '\xc0', 'p', '\x12', '\x00', '@', 'r']

Still not seeing the gpio4 input level in data

@wnienhaus
Copy link
Collaborator

It looks like you have been inching closer and closer with each attempt. Great persistence there!

What I think was missing in your earlier attempts was this line:

WRITE_RTC_REG(RTC_IO_TOUCH_PAD0_REG, RTC_IO_TOUCH_PAD0_MUX_SEL_M, 1, 1) # connect GPIO to ULP

This connects the GPIO to the RTC subsystem and is needed for the ULP to read the GPIO.

Your latest code has this line now, so now it should work!

But

But... your code is likely crashing the ULP.

To explain, If we remove all "non-code" lines (such as #define and .set), then the first 4 things in your assembly code are:

WRITE_RTC_REG(RTC_IO_TOUCH_PAD0_REG, RTC_IO_TOUCH_PAD0_MUX_SEL_M, 1, 1) # connect GPIO to ULP
WRITE_RTC_REG(RTC_IO_TOUCH_PAD0_REG, RTC_IO_TOUCH_PAD0_FUN_IE_M, 1, 1)  # gpio input mode
data:   .long 0
entry:  move r3, channel

The ULP runs instructions sequentially, but line 3 is not an instruction, and should crash the ULP.

Also ulp.run() specifies where the ULP should start, which in your example is 4 bytes (entry_addr) from the start, and basically means "start at the second instruction" in my snippet above (each machine instruction of the ULP is 4 bytes long)

So, you need to move all instructions after the variable and ensure the entry_addr given to ulp.run() is correct. If you stick with only the one .long variable, then 4 is the correct entry_addr... but if you add one more .long variable, entry_addr should be 8, and so on.

Easier GPIO reading

I see you can simplify reading the GPIO value. I notice you are reading 16 bits with this instruction:

READ_RTC_REG(RTC_GPIO_IN_REG, RTC_GPIO_IN_NEXT_S, 16)

and then doing bit operations afterwards the get one bit that you want.

But the READ_RTC_REG function takes a "starting bit" and a "number-of-bits-to-read" as arg 2 and 3 respectively, so you can read just 1 bit like this:

READ_RTC_REG(RTC_GPIO_IN_REG, RTC_GPIO_IN_NEXT_S + channel, 1)

That will get you exactly GPIO4's value into r0.

Putting it together

So putting that all together, I think this should work:

source = """\
  #define DR_REG_RTCIO_BASE            0x3ff48400
  #define RTC_IO_TOUCH_PAD0_REG        (DR_REG_RTCIO_BASE + 0x94)
  #define RTC_IO_TOUCH_PAD0_MUX_SEL_M  (BIT(19))
  #define RTC_IO_TOUCH_PAD0_FUN_IE_M   (BIT(13))
  #define RTC_GPIO_IN_REG              (DR_REG_RTCIO_BASE + 0x24)
  #define RTC_GPIO_IN_NEXT_S           14
  .set channel, 10

  data:   .long 0
  entry:
      WRITE_RTC_REG(RTC_IO_TOUCH_PAD0_REG, RTC_IO_TOUCH_PAD0_MUX_SEL_M, 1, 1) # connect GPIO to ULP
      WRITE_RTC_REG(RTC_IO_TOUCH_PAD0_REG, RTC_IO_TOUCH_PAD0_FUN_IE_M, 1, 1)  # gpio input mode
      READ_RTC_REG(RTC_GPIO_IN_REG, RTC_GPIO_IN_NEXT_S + channel, 1) # read gpio value into r0
      move r3, data              # set r3 to the address of data
      st r0, r3, 0               # store r0 into the data variable (to be read with mem32 in micropython)
"""

@kjm1102
Copy link
Author

kjm1102 commented May 22, 2022

Could you try

from esp32 import ULP
from machine import mem32, mem8, deepsleep, reset_cause, Pin
from esp32_ulp import src_to_binary

load_addr, entry_addr = 0, 4
ULP_MEM_BASE = 0x50000000
ULP_DATA_MASK = 0xffff                           # ULP data in lower 16 bits

causes={2:'reset', 1:'hardreset', 3:'wdt', 4:'wakeup', 5:'pwrup'}; cause=causes[reset_cause()]; print(), print('pwrup cause', cause)
if cause!='wakeup':
  source = """\
  #define DR_REG_RTCIO_BASE            0x3ff48400
  #define RTC_IO_TOUCH_PAD0_REG        (DR_REG_RTCIO_BASE + 0x94)
  #define RTC_IO_TOUCH_PAD0_MUX_SEL_M  (BIT(19))
  #define RTC_IO_TOUCH_PAD0_FUN_IE_M   (BIT(13))
  #define RTC_GPIO_IN_REG              (DR_REG_RTCIO_BASE + 0x24)
  #define RTC_GPIO_IN_NEXT_S           14
  .set channel, 10

  data:   .long 0
  entry:
      WRITE_RTC_REG(RTC_IO_TOUCH_PAD0_REG, RTC_IO_TOUCH_PAD0_MUX_SEL_M, 1, 1) # connect GPIO to ULP
      WRITE_RTC_REG(RTC_IO_TOUCH_PAD0_REG, RTC_IO_TOUCH_PAD0_FUN_IE_M, 1, 1)  # gpio input mode
      READ_RTC_REG(RTC_GPIO_IN_REG, RTC_GPIO_IN_NEXT_S + channel, 1) # read gpio value into r0
      move r3, data              # set r3 to the address of data
      st r0, r3, 0               # store r0 into the data variable (to be read with mem32 in micropython)
  """ 
  
  
  binary = src_to_binary(source)
  ulp = ULP()
  ulp.set_wakeup_period(0, 50000)                # use timer0, wakeup after 50ms
  ulp.load_binary(load_addr, binary)
  mem32[ULP_MEM_BASE + load_addr] = 0x1000
  ulp.run(entry_addr)

print('gpio4', mem32[ULP_MEM_BASE+load_addr] & ULP_DATA_MASK)
print([chr(mem8[0x50000000 + i]) for i in range(0,32)])
deepsleep(1000)  

on your esp32 please? Mine is showing

pwrup cause wakeup
gpio4 0
['\x00', '\x00', '\xa3', '\x00', '%', '\x05', '\xcc', '\x19', '%', '\x05', '\xb4', '\x16', '\t', '\x01', '`', ',', '\x03', '\x00', '\x80', 'r', '\x0c', '\x00', '\x00', 'h', 'V', '\xd6', '\xc7', '\x04', '\x1d', 'a', '\xbe', '.']

regardless of the voltage present on the gpio4 input.

@wnienhaus
Copy link
Collaborator

It works correctly (as expected) on my ESP32. When I have GPIO4 connected to VCC (3V3) I see gpio4 1 in the output and when i have GPIO4 not connected to anything, I see gpio4 0.

I wonder if there might be something wrong with your GPIO pin? Perhaps try read GPIO4 from MicroPython normally to test that the input works as expected:

from machine import Pin
p=Pin(4, Pin.IN)
while True:
    print(p.value())

This should print lines with 0 or 1 depending on the current state of GPIO4.

@kjm1102
Copy link
Author

kjm1102 commented May 22, 2022

Yeah I did that already Wilko, that's why I was keen to see your results. On my ESP32 the python works but the ULP always reports 0. What a bugger!

@kjm1102
Copy link
Author

kjm1102 commented May 22, 2022

I've discovered that the ulp will read gpio4 correctly if cause!=wakeup but not otherwise. Also if I remove the deepsleep (so that the cause is always hardreset) then the ulp reports gpio4 correctly on the first cycle then 4096 subsequently. I'm sure the universe is shouting at me but I'm not getting the message. Do these results give you any clues as to what's going on?

@wnienhaus
Copy link
Collaborator

The 4096 will be the 0x1000 you are setting just before ulp.run(..). So when you see that it should mean the ULP has not yet managed to store (overwrite) anything into the data variable.

Or that something overwrote the data variable with 0x1000 again, but in your last code the setting of 0x1000 always happens before the ulp.run() and only once on hardreset so I'll rule out this option.

So I wonder if this is a timing issue. Maybe the main processor manages to read the memory already before the ULP started properly and ran its code. What if you add a sleep of a few milliseconds (>50ms to be sure) after the ulp.run() to give the ULP time to start?

This is only a guess and I'm not very sure of it because a by the time the first deepsleep ends the ULP must have definitely started, but might be worth trying anyway.

@wnienhaus
Copy link
Collaborator

Btw when you say

if I remove the deepsleep (so that the cause is always hardreset) then the ulp reports gpio4 correctly on the first cycle then 4096 subsequently

what is a cycle? Do you let it start (which assembles and loads the ULP code) and see the output of the print statements and then you manually press reset each time to start the next cycle?

If so, I fid it very surprising that you get the correct GPIO value on the first cycle and not afterwards. Because all hard resets should be the same.

Or is the first cycle actually a power up? It would also surprise me that the power up and hard reset behave differently in this case - but something must be different between your first cycle and subsequent ones.

Or did you add some kind of loop around part of the code and cycles refer to the iterations of the loop?

@kjm1102
Copy link
Author

kjm1102 commented May 22, 2022

The delay didn't help, I didn't think it would since it's in the code that runs when cause!=wakeup & the failure to update the gpio4 input value is when the cause==wakeup.

Rather than download the code into the esp32 after each change I just run it from the repl, so a cycle is each time I run it again. I've checked & the behaviour is the same as if I'd downloaded & let it run repeatedly. The only difference is a download runs automatically but with the repl the next cycle occurs when I get a new idea to try out & click 'run' again.

My first cycle is always after a powerup (which is reported as a hardreset). I've been removing/reapplying power because pressing the reset button causes the board to behave the same as pressing the boot button (ie it goes into flashing mode).

Which upython are you running & on what board? I'm running release='1.17.0' on a devkit1 (110k ram, 2m flash). Maybe if I try the same upython version & board as you I can get your results?

@wnienhaus
Copy link
Collaborator

I am also using MicroPython 1.17.0. I am using a Lolin32 clone with the following specs (as per esptool.py flash_id):

Detecting chip type... ESP32
Chip is ESP32-D0WDQ6 (revision 1)
Features: WiFi, BT, Dual Core, 240MHz, VRef calibration in efuse, Coding Scheme None
Crystal is 40MHz
MAC: xx:xx:xx:xx:xx:xx
Uploading stub...
Running stub...
Stub running...
Manufacturer: 5e
Device: 4016
Detected flash size: 4MB

But I have previously run this type of code (read GPIO from ULP during deepsleep) on 2 other of my ESP32's (this one and this one), without any problems. All of them use the "original ESP32", not the newer (and different) ESP32 s2, s3, c3, etc models (note that our assembler does not support the S and C models - the ULP machine code is different).

So if you are using a "normal" ESP32 (perhaps you can also run esptool.py flash_id to compare with mine), then I can only see our approaches to running the code differing: You are using the IDE/REPL, and I put your code into boot.py to let the device run it on startup (actually, it feels like an unlikely reason for our different results).

@kjm1102
Copy link
Author

kjm1102 commented May 22, 2022

Despair creeping up on me now squire. Ran

from esp32 import ULP
from machine import mem32, deepsleep, reset_cause, Pin
from esp32_ulp import src_to_binary
import time

load_addr, entry_addr = 0, 4
ULP_MEM_BASE = 0x50000000
ULP_DATA_MASK = 0xffff                           # ULP data in lower 16 bits

causes={2:'reset', 1:'hardreset', 3:'wdt', 4:'wakeup', 5:'pwrup'}; cause=causes[reset_cause()]; print(), print('pwrup cause', cause)
if cause!='wakeup':
  source = """\
  #define DR_REG_RTCIO_BASE            0x3ff48400
  #define RTC_IO_TOUCH_PAD0_REG        (DR_REG_RTCIO_BASE + 0x94)
  #define RTC_IO_TOUCH_PAD0_MUX_SEL_M  (BIT(19))
  #define RTC_IO_TOUCH_PAD0_FUN_IE_M   (BIT(13))
  #define RTC_GPIO_IN_REG              (DR_REG_RTCIO_BASE + 0x24)
  #define RTC_GPIO_IN_NEXT_S           14
  .set channel, 10

  data:   .long 0
  entry:
      WRITE_RTC_REG(RTC_IO_TOUCH_PAD0_REG, RTC_IO_TOUCH_PAD0_MUX_SEL_M, 1, 1) # connect GPIO to ULP
      WRITE_RTC_REG(RTC_IO_TOUCH_PAD0_REG, RTC_IO_TOUCH_PAD0_FUN_IE_M, 1, 1)  # gpio input mode
      READ_RTC_REG(RTC_GPIO_IN_REG, RTC_GPIO_IN_NEXT_S + channel, 1) # read gpio value into r0
      move r3, data              # set r3 to the address of data
      st r0, r3, 0               # store r0 into the data variable (to be read with mem32 in micropython)
  """


  binary = src_to_binary(source)
  ulp = ULP()
  ulp.set_wakeup_period(0, 50000)                # use timer0, wakeup after 50ms
  ulp.load_binary(load_addr, binary)
  mem32[ULP_MEM_BASE + load_addr] = 0x1000
  ulp.run(entry_addr)

print('gpio4', mem32[ULP_MEM_BASE+load_addr] & ULP_DATA_MASK)
deepsleep(1000)
'''
as boot.py on the same Lolin32 clone as in your link &, like my devkit1 board, it will only read a new gpio4 value on the first run after a powerup, subsequent wakeup runs stay on that value. 

@wnienhaus
Copy link
Collaborator

Just a quick random idea: can you try add a HALT instruction as the last instruction in the assembly (i.e. after the st r0, r3, 0)?

@kjm1102
Copy link
Author

kjm1102 commented May 22, 2022

Hallelujah!
'''
from esp32 import ULP
from machine import mem32, mem8, deepsleep, reset_cause, Pin
from esp32_ulp import src_to_binary

load_addr, entry_addr = 0, 4
ULP_MEM_BASE = 0x50000000
ULP_DATA_MASK = 0xffff # ULP data in lower 16 bits

causes={2:'reset', 1:'hardreset', 3:'wdt', 4:'wakeup', 5:'pwrup'}; cause=causes[reset_cause()]; print(), print('pwrup cause', cause)
if cause!='wakeup':
source = """
#define DR_REG_RTCIO_BASE 0x3ff48400
#define RTC_IO_TOUCH_PAD0_REG (DR_REG_RTCIO_BASE + 0x94)
#define RTC_IO_TOUCH_PAD0_MUX_SEL_M (BIT(19))
#define RTC_IO_TOUCH_PAD0_FUN_IE_M (BIT(13))
#define RTC_GPIO_IN_REG (DR_REG_RTCIO_BASE + 0x24)
#define RTC_GPIO_IN_NEXT_S 14
.set channel, 10

data: .long 0
entry:
WRITE_RTC_REG(RTC_IO_TOUCH_PAD0_REG, RTC_IO_TOUCH_PAD0_MUX_SEL_M, 1, 1) # connect GPIO to ULP
WRITE_RTC_REG(RTC_IO_TOUCH_PAD0_REG, RTC_IO_TOUCH_PAD0_FUN_IE_M, 1, 1) # gpio input mode
READ_RTC_REG(RTC_GPIO_IN_REG, RTC_GPIO_IN_NEXT_S + channel, 1) # read gpio value into r0
move r3, data # set r3 to the address of data
st r0, r3, 0 # store r0 into the data variable (to be read with mem32 in micropython)
halt
"""

binary = src_to_binary(source)
ulp = ULP()
ulp.set_wakeup_period(0, 50000) # use timer0, wakeup after 50ms
ulp.load_binary(load_addr, binary)
mem32[ULP_MEM_BASE + load_addr] = 0x1000
ulp.run(entry_addr)

print('gpio4', mem32[ULP_MEM_BASE+load_addr] & ULP_DATA_MASK)
deepsleep(1000)
'''
works. I'd lost the halt somewhere along the way. I don't understand why failing to halt the micro stops it reading the gpio but it's working now that I've put it back in.

Hey, quick question for ya. I've been wondering about this for ages. Why does the gpio have so many different names? When we're setting up we refer to it by it's touch alias then later we read it by referencing it's channel alias. Just wondering why so many AKAs?

@wnienhaus
Copy link
Collaborator

Great! Well done.

That HALT makes sense because the ULP doesn't know what in the RAM is code and what isn't, or even how big the code in RAM is. It simply and obediently executes whatever next instruction comes. And since what is in RAM by default (without anything being written to it) is undefined, what state the ULP ends up in when it executes those "next instructions" (which are just randomness) is also undefined. That should explain the different behaviour on my side and yours - i.e. pure chance that it worked for me.

Regarding the naming of GPIOs - I don't know why they have so many different names. The GPIO numbers vs RTC channels somewhat makes sense because the RTC subsystem has it's own set of "channels" to which the real GPIOs are routed - it would have been quite convenient if the GPIO and channel numbers had been the same, but I guess convenience is not an embedded device designer's first priority (especially for a device that's designed to be super cheap).

Regarding the TOUCH naming, I admit being somewhat frustrated by that at times, because there is no logic in the mapping, e.g. GPIO4 maps to PAD0 but GPIO2 maps to PAD2, while GPIO0 maps to PAD1. I assume they designated some GPIOs to have touch capability and then named those sequentially relative to something (maybe some location in the IC design? where touch capable GPIOs are located near each other? ... pure speculation of course).

The best place I found to rely on is the mapping table at the bottom of this file: https://github.com/espressif/esp-idf/blob/4bf67afd0a5a4183f225e0678a38296cbd194291/components/soc/esp32/rtc_io_periph.c - the "real" GPIO numbers for each line are on the far right of each line as comments. The ESP32 Technical Reference manual also has a good table (table 19 under section 4.11) but I find the code file to be faster/easier to open than the PDF.

@wnienhaus
Copy link
Collaborator

wnienhaus commented May 22, 2022

I just added an example for reading a GPIO to the repo: #73 - so there's an easier way to get started for others down the line.

Could I ask you to please test it on your ESP32(s)? Just upload the file, run import readgpio and see if it shows the current GPIO value as you toggle the input. The example uses GPIO4 as you did so far.

@kjm1102
Copy link
Author

kjm1102 commented May 22, 2022

I haven't been able to figure out how to download code from github, I find the platform beginner unfriendly. I normally copy & paste. Sadly, copy & paste doesn't work with code that has + signs at the front of each line like a1241c9 does. Could you explain how to download to save me typing it in manually?

Also I have some more questions.

  1. The 1,1 at the end of WRITE_RTC_REG(RTC_IO_TOUCH_PAD0_REG, RTC_IO_TOUCH_PAD0_FUN_IE_M, 1, 1) means we set it up as an rtc analog rather than a digital input. Why then do we get only 0 or 1 when we read it? For a 12 bit wide A to D shouldn't we get a result that is 12 bits wide?

  2. Reagrding using gpio4 as a touch0 input. I'd like to disable the pull down on gpio4. Can I do that via upython when I'm also configuring & reading the input from the ulp? If not do you know how to disable a pull down via the ulp?

  3. I read somewhere that ADC2 is not available in upython when wifi is in use. Yet the only TOUCH AKAs with a MUX_SEL are TOUCH 0-7 which are all ADC2. Does this mean ADC2 is available to the ulp with wifi active?

  4. Curious why we don't need a function select(column 3) at the end of https://github.com/espressif/esp-idf/blob/4bf67afd0a5a4183f225e0678a38296cbd194291/components/soc/esp32/rtc_io_periph.c

@wnienhaus
Copy link
Collaborator

Let me start by answering the first question, I'll answer the "some more questions" a bit later.

To download the PR page #73, then choose the "Files changes" tab at the top, and then for the "readgpio.py" file, choose the "the dots icon" in the top right of the "header" of that file. That opens a menu from which you can choose "View file" and then you have the file itself (not the patch). That should work for copying, but if you want you can also then click the "Raw" button in the "header" for that file, to get the raw text.

@wnienhaus
Copy link
Collaborator

Answering the other 4 questions:

  1. The 1,1 means that we're setting 1 bit and the value we're writing (setting) is 1. I assume the "analog" part here refers to the touch-capability or that one can map ADCs to the RTC channels. The RTC_IO_TOUCH_PAD0_FUN_IE_M sets the GPIO into input mode. Maybe you meant to refer to RTC_IO_TOUCH_PAD0_MUX_SEL_M, but here 1 we're setting chooses the digital mode (ref)
  2. You could do this via MicroPython, but it would (potentially) not persist a deepsleep/wakeup cycle (you could try it). To disable the pull-down via the ULP, use this:
WRITE_RTC_REG(RTC_IO_TOUCH_PAD0_REG, RTC_IO_TOUCH_PAD0_RUE, 1, 0)
  1. The Hardware Design Guidelines doc confirms this (section 2.6). I doubt that this changes when using the ULP. It will likely work fine during deep-sleep as Wifi is off then, but during normal operation with Wifi, I would expect not.
  2. I assume we don't need a function select, because the default (0) is already what we want. See the ESP32 Technical Reference Manual table 19 and the default mentioned here: https://github.com/espressif/esp-idf/blob/1cb31e50943bb757966ca91ed7f4852692a5b0ed/components/soc/esp32/include/soc/rtc_io_reg.h#L589 (the default is given as 2'd0 with the the 2' referring to the register being 2 bits wide and d0 meaning "0 in decimal")

@kjm1102
Copy link
Author

kjm1102 commented May 24, 2022

The #73 demo program works great, displays 0x0 (gpio4 not connected to anything) or 0x1(gpio4 bridged to 3v3).

Re your answer to my Q1 about connecting a gpio to digital or analog rtc and that 1,1 means digital. https://github.com/micropython/micropython-esp32-ulp/blob/master/examples/blink.py#L63 says (0: GPIO connected to digital GPIO module, 1: GPIO connected to analog RTC module) but the ref you gave says description: Ò1Ó select the digital function Ó0Óslection the rtc function. Aren't we trying to conect it to the rtc domain for it to be accessible to the ulp?

You mention in #73 you have a TBRG counter for ULP already? That's exactly what I'm working towards. Any chance a glimpse of your code?

@wnienhaus
Copy link
Collaborator

Thank you for testing #73!

About Q1 earlier - I believe I answered wrong. The 1 that we're setting with MUX_SEL is actually connecting the GPIO to the RTC module (which seems to often be referred to as "analog RTC module"). Digital does mean the "normal GPIO". Internally it basically means whether the pin is connected to the IO_MUX or the RTC_MUX. The confusing thing is that the place I referenced is actually wrong. the 0 and 1 are the wrong way around. 0 is digital and 1 is RTC

I confirmed this two two ways:

a) Using example code in the ESP-IDF (here) then looking at what the rtc_gpio_init function does (here), seeing that it calls rtcio_hal_function_select (which is really rtcio_ll_function_select as mapped here) with an argument RTCIO_FUNC_RTC. That constant is defined here along with the other mode RTCIO_FUNC_DIGITAL and there you can see which one is 0 and which one is 1. Looking at what rtcio_ll_function_select actually does, we can see for RTCIO_FUNC_RTC is "sets" bits while for anything else (i.e. RTCIO_FUNC_DIGITAL) it clears bits in the configuration register. This again corresponds to 1 and 0 respectively. Even the comment in that function states this once more (and it looks awfully familiar when comparing to our blink.py example. I guess that's where our comment comes from)

b) Looking at the ESP32 Technical Reference Manual and looking at the example on page 80, where the register for configuring the ADC pads if explained, and RTCIO_ADC_ADCn_MUX_SEL takes 0 for "route to IO_MUX" and 1 for "route to RTC block". Compare that with the description of the same in the setting here, to see that the two places dont agree. Searching that rtc_io_reg.h file for all other references of _MUX_SEL shows the description and format are the same everywhere, including the *_PADn_MUX_SEL cases, which leads me to believe they are all implemented the same, and the ADC-related example on page 80 of the Technical Reference Manual is representative of all the others.

So the Reference Manual and the "real" code agree, while the comments in the code file(s) disagree. 2 against 1 :)

--
Here is my TBRG ULP code: https://gist.github.com/wnienhaus/56c534079823ed036a8f13a43ab7ddf8. It doesn't actually pulse count, but rather checks for changes from HIGH to LOW or vice versa. Every time it sees a difference to the last state seen, it adds 1 to the counter. It also pulses an LED briefly to signal that it recognised and counted the event.

@kjm1102
Copy link
Author

kjm1102 commented May 25, 2022

Nice edge counter Wilko. Curious about the add r0, r0, 0 instruction after you read the gpio state into r0, I can't work out how it changes anything?

@wnienhaus
Copy link
Collaborator

I think you are right - it doesnt do anything. It would be a leftover code from earlier iterations of the code - before using a reed switch via the GPIO, I actually used the ESP32's built-in hall effect sensor ... but it was just too unstable - it was heavily influenced by Wifi transmitting.

And because the hall sensor returns a range of values, there was a lot more code to test whether we're in the desired range, oversampling and smoothing the signal over time, etc. And this "no-op" add instruction would have been for a subsequent JUMP somewhere, eq, which is dependent on whether the last ALU operation resulted in 0.

So it's essentially a test for whether r0 is 0 or not.

@wnienhaus
Copy link
Collaborator

Can we consider this issue closed now?

@kjm1102
Copy link
Author

kjm1102 commented May 25, 2022

One last question. Given Count is 16 bit (presumably signed) does that mean it will roll over at 32768 edges?

@wnienhaus
Copy link
Collaborator

Yes. Adding to the counter will be the same whether you treat the number as signed or unsigned, and it will be up to your program reading the value to interpret it as signed or unsigned. Since you most likely only count upwards, interpreting it as unsigned will mean you get possible values from 0-32767 (0x0 - 0xffff) and after that it will roll over to 0x0 again.

If you wanted to represent a larger range, I guess you could use one more variable and increment that every time the ADD resulted in an overflow (which the JUMP instruction can test for).

@kjm1102
Copy link
Author

kjm1102 commented May 26, 2022

To my delight the counter still works from a .mpy file so I'm a very happy camper. Thnx for all the hand holding, good to close.

@wnienhaus
Copy link
Collaborator

Glad to have helped. Thanks for your questions and challenging my answers where they didn't make sense. Also as a result of this issue we now have one more example in the repo. Thanks for testing it.

@kjm1102
Copy link
Author

kjm1102 commented May 27, 2022

I ran the TBRG counter on gpio35 where I get the count first thing before I connect the wifi. I don't know what wifi uses ADC2 for but whatever it is it doesn't stop gpio35 from continuing to count edges through the rest of the program & the subsequent deepsleep..

@wnienhaus
Copy link
Collaborator

From what I can see GPIO35 connects to ADC1 not ADC2. Also I assume, when you read it as a digital pin, the ADC is not actually triggered. Also, for reading the ADC from ULP code there is a dedicated adc instruction (see).

@kjm1102
Copy link
Author

kjm1102 commented May 28, 2022

I got confused by it's input enable name (RTC_IO_ADC2_FUN_IE_M), but you're right gpio35 belongs to ADC1 group. So the wifi ADC2 conflict is only an issue if I'm trying to use an ADC2 pin as an analog input?

@wnienhaus
Copy link
Collaborator

I have never tried to use ADC2 together with Wifi. Looking at the Technical Ref Manual page 623 (Figure 146), I have a hunch it is used for "power and peak detection" (see also the text and diagram before Figure 146) as part of the Wifi implementation. The description above Figure 145 shows that there are different ADC controllers, and since the Power/Peak detection controller is separate from the others, I assume the ADC2 is only ever connected to one of the controllers, and thus: if used for Wifi, the others get no reading. It's all guessing on my part though.

@kjm1102
Copy link
Author

kjm1102 commented May 30, 2022

Welcome to my world.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants