ULP I2C sensor readings.

All ESP32 boards running MicroPython.
Target audience: MicroPython users with an ESP32 board.
Post Reply
divekr
Posts: 6
Joined: Tue Oct 05, 2021 8:02 am

ULP I2C sensor readings.

Post by divekr » Fri Feb 04, 2022 2:14 am

Greetings!

Thanks for the great micropython, v1.18 is awesome for speed :)

I am trying to understand how to manage the obscure ULP code,
I found https://github.com/ThomasWaldmann/py-esp32-ulp, Thanks to Thomas, I've done a few things with that.
It has some limitations(exclude .macro etc), but almost part, It doesn't matter to work with,
and I found it "I2C_WR/RD" instructions available, so I am trying to communicate with I2C sensors.

From Espressif instruction set(https://docs.espressif.com/projects/esp ... n_set.html),
it says Slave address (in 7-bit format) has to be set in advance into SENS_I2C_SLAVE_ADDRx register field

Code: Select all

I2C_RD      0x10, 7, 0, 0      
// Read byte from sub-address 0x10 of slave with address set in SENS_I2C_SLAVE_ADDR0
I've tried to set SENS_I2C_SLAVE_ADDR0 within py-esp32-ulp,

Code: Select all

#define SENS_I2C_SLAVE_ADDR0  0x000007FF // Found in soc/sens_reg.h
#define RTC_I2C_SLAVE_ADDR_S  0 // Found in soc/rtc_i2c_reg.h
WRITE_RTC_REG(SENS_I2C_SLAVE_ADDR0,RTC_I2C_SLAVE_ADDR_S,8,0x68)

ValueError: invalid register base
I could not move forward from this,
so I've tried to follow the espressif's "Compiling the ULP Code" instruction,
I though it could be loaded into micropython.
But It generates a whole bunch of firmware file include ulp binary.

Could you help me to go further?
Using ThomasWaldmann/py-esp32-ulp will be the best way for me,
But the any way could be okay to get assembly bin files to load into micropython.

Thanks in advance,

Regards
Sam Lee

Christian Walther
Posts: 169
Joined: Fri Aug 19, 2016 11:55 am

Re: ULP I2C sensor readings.

Post by Christian Walther » Fri Feb 04, 2022 10:19 pm

I don’t think SENS_I2C_SLAVE_ADDR0 is the register’s address, that looks more like a mask.

Correlating esp32_technical_reference_manual_en.pdf section 30.8.1 to the stuff in the headers, try

Code: Select all

# from soc/soc.h
#define DR_REG_SENS_BASE                        0x3ff48800
# from soc/sens_reg.h
#define SENS_SAR_SLAVE_ADDR1_REG          (DR_REG_SENS_BASE + 0x003c)
#define SENS_I2C_SLAVE_ADDR0_S  11
WRITE_RTC_REG(SENS_SAR_SLAVE_ADDR1_REG, SENS_I2C_SLAVE_ADDR0_S, 11, 0x68)
I haven’t tested this and I have not used I2C on the ULP, only GPIOs.

KJM
Posts: 158
Joined: Sun Nov 18, 2018 10:53 pm
Location: Sydney AU

Re: ULP I2C sensor readings.

Post by KJM » Sat Feb 05, 2022 1:03 am

I wish I could get my head around accessing the ULP from micropython! I found https://github.com/ThomasWaldmann/py-es ... counter.py but it crashes because there is no esp32_ulp on my machine. I found a lib of the same name @ https://github.com/ThomasWaldmann/py-es ... /esp32_ulp but it has no src_to_binary in it. I think ULP stands for 'unlikely little project'.

divekr
Posts: 6
Joined: Tue Oct 05, 2021 8:02 am

Re: ULP I2C sensor readings.

Post by divekr » Sat Feb 05, 2022 3:46 am

Christian Walther wrote:
Fri Feb 04, 2022 10:19 pm
I don’t think SENS_I2C_SLAVE_ADDR0 is the register’s address, that looks more like a mask.

Correlating esp32_technical_reference_manual_en.pdf section 30.8.1 to the stuff in the headers, try

Code: Select all

# from soc/soc.h
#define DR_REG_SENS_BASE                        0x3ff48800
# from soc/sens_reg.h
#define SENS_SAR_SLAVE_ADDR1_REG          (DR_REG_SENS_BASE + 0x003c)
#define SENS_I2C_SLAVE_ADDR0_S  11
WRITE_RTC_REG(SENS_SAR_SLAVE_ADDR1_REG, SENS_I2C_SLAVE_ADDR0_S, 11, 0x68)
I haven’t tested this and I have not used I2C on the ULP, only GPIOs.
Dear Christian Walther,
I do really do thank you so much for your reply,
Your code is successfully converted to bin by py-esp32-ulp, so I could move forward.
I assuming that could register slave address, and I tried to initialize the I2C connection with below codes,

Code: Select all

// Select SDA/SCL pins to version 1, TOUCH2 and TOUCH3 (version 0 is TOUCH0 and TOUCH1)
WRITE_RTC_REG(RTC_IO_SAR_I2C_IO_REG, RTC_IO_SAR_I2C_SDA_SEL_S, RTC_IO_SAR_I2C_SDA_SEL_M, 1);
WRITE_RTC_REG(RTC_IO_SAR_I2C_IO_REG, RTC_IO_SAR_I2C_SCL_SEL_S, RTC_IO_SAR_I2C_SCL_SEL_M, 1);
I think RTC_IO_SAR_I2C_XXX_SEL_M values cause the error from header files, most of XXX_M has shift(<<) calcuration and some of them couldn't be converted. I manually put calcurated values, but there is "OverflowError: overflow converting long int to machine word"

I found

Code: Select all

WRITE_RTC_REG(SENS_SAR_SLAVE_ADDR1_REG, SENS_I2C_SLAVE_ADDR0_S, 11, 0x68)"
WRITE_RTC_REG(SENS_SAR_SLAVE_ADDR1_REG, SENS_I2C_SLAVE_ADDR0_S, SENS_I2C_SLAVE_ADDR0_M, 0x68);
SENS_I2C_SLAVE_ADDR0_M looks like 11, Can I ask you where can I find that value?

In espressif's header says it is,

Code: Select all

#define SENS_I2C_SLAVE_ADDR0_M  ((SENS_I2C_SLAVE_ADDR0_V)<<(SENS_I2C_SLAVE_ADDR0_S))
#define SENS_I2C_SLAVE_ADDR0_V  0x7FF
#define SENS_I2C_SLAVE_ADDR0_S  11
forgive my ignorances, I could not figure out 0x7FF << 11 = '0x3ff800' is "11"
This is becoming to decrypt a password for me :)


I just need to write and read a single 8bit to determine the sensor status like below,

Code: Select all

I2C_WR  0x40 ,0x0, 7, 0, 0
WAIT      2000
I2C_RD  0x00 ,7, 0, 0
I didn't think, that 3 lines to be this hard :), I found micropython ULP references is more less than other parts.

Please help me to get there.......:)


regards,
Sam lee
Last edited by divekr on Sat Feb 05, 2022 6:38 am, edited 1 time in total.

divekr
Posts: 6
Joined: Tue Oct 05, 2021 8:02 am

Re: ULP I2C sensor readings.

Post by divekr » Sat Feb 05, 2022 6:33 am

KJM wrote:
Sat Feb 05, 2022 1:03 am
I wish I could get my head around accessing the ULP from micropython! I found https://github.com/ThomasWaldmann/py-es ... counter.py but it crashes because there is no esp32_ulp on my machine. I found a lib of the same name @ https://github.com/ThomasWaldmann/py-es ... /esp32_ulp but it has no src_to_binary in it. I think ULP stands for 'unlikely little project'.
Dear KJM,
src_to_binary function is stated in __init__.py under esp32_ulp folder.
I found ThomasWaldmann/py-esp32-ulp is just library which converts some *.S(original ESP ULP code) into binary code.
src_to_binary makes it happen, once you convert binary code, You can run the ULP code without py-esp32-ulp library.
like,

Code: Select all

source = """
  move r2, 1
  sub r0, r2, r0 
  .....extra code.....
"""
binary = src_to_binary(source)
# you just print(binary) and copy into a variables, You can use it without the library anymore.

import esp32
binary = b"1\x00\x00\xd0\xe5\xaf,r,\x00@\x80+\x05\xcc........."
ulp = esp32.ULP()
load_addr, entry_addr = 0, 8
ulp.load_binary(load_addr, binary)
ulp.run(entry_addr)
PS: I am still looking for communicating with I2C sensor,
Please help me out :)

Christian Walther
Posts: 169
Joined: Fri Aug 19, 2016 11:55 am

Re: ULP I2C sensor readings.

Post by Christian Walther » Sat Feb 05, 2022 1:44 pm

divekr wrote:
Sat Feb 05, 2022 3:46 am
I think RTC_IO_SAR_I2C_XXX_SEL_M values cause the error from header files, most of XXX_M has shift(<<) calcuration and some of them couldn't be converted. I manually put calcurated values, but there is "OverflowError: overflow converting long int to machine word"

I found

Code: Select all

WRITE_RTC_REG(SENS_SAR_SLAVE_ADDR1_REG, SENS_I2C_SLAVE_ADDR0_S, 11, 0x68)"
WRITE_RTC_REG(SENS_SAR_SLAVE_ADDR1_REG, SENS_I2C_SLAVE_ADDR0_S, SENS_I2C_SLAVE_ADDR0_M, 0x68);
SENS_I2C_SLAVE_ADDR0_M looks like 11, Can I ask you where can I find that value?

In espressif's header says it is,

Code: Select all

#define SENS_I2C_SLAVE_ADDR0_M  ((SENS_I2C_SLAVE_ADDR0_V)<<(SENS_I2C_SLAVE_ADDR0_S))
#define SENS_I2C_SLAVE_ADDR0_V  0x7FF
#define SENS_I2C_SLAVE_ADDR0_S  11
I don’t really understand the constants from the headers either, I assume they are mostly meant for use from C (which I haven’t done). I find it more helpful to look at the actual definitions of the registers in https://www.espressif.com/sites/default ... ual_en.pdf.

There (section 30.8.1, register 30.3) you can see that the ADDR0 field in the register at 0x003c from (RTC base address + 0x0800) starts at bit 11 and is 11 bits wide, which are the two numbers that you need to give to WRITE_RTC_REG(). One of them has a constant defined, SENS_I2C_SLAVE_ADDR0_S, so I used it, the other doesn’t, so I wrote a literal “11”.

I’m not sure what the purpose of SENS_I2C_SLAVE_ADDR0 is, seeing as it has the same value as SENS_I2C_SLAVE_ADDR0_V.
SENS_I2C_SLAVE_ADDR0_S seems to be the shift or start bit number.
SENS_I2C_SLAVE_ADDR0_V seems to be the mask you apply to the value before shifting it into place, which I see little use for.
SENS_I2C_SLAVE_ADDR0_M seems to be the mask indicating where in the register the field lives, which is useless in ULP assembly, because WRITE_RTC_REG() doesn’t take a mask, but start and width bit numbers.

In other words, you can use the …_S constant for the low_bit argument to WRITE_RTC_REG(), but there is no constant that I can see for the bit_width argument. It is not the …_M constant. (The value can be derived from it – but not in a way that is straightforward to write in source code).
KJM wrote:
Sat Feb 05, 2022 1:03 am
I wish I could get my head around accessing the ULP from micropython! I found https://github.com/ThomasWaldmann/py-es ... counter.py but it crashes because there is no esp32_ulp on my machine. I found a lib of the same name @ https://github.com/ThomasWaldmann/py-es ... /esp32_ulp but it has no src_to_binary in it. I think ULP stands for 'unlikely little project'.
The way to install esp32_ulp is

Code: Select all

import upip; upip.install('micropython-py-esp32-ulp')
did you do that?

divekr
Posts: 6
Joined: Tue Oct 05, 2021 8:02 am

Re: ULP I2C sensor readings.

Post by divekr » Sun Feb 06, 2022 12:58 pm

Dear Christian Walther,
It is so much grateful for me to have your reply again.
I think I am understanding "WRITE_RTC_REG", thanks to Christian.

As you said the ESP32 technical reference manual is so helpful for to understand ULP.
I am using ESP32-PICO-V3-02, so I've download "https://www.espressif.com/sites/default ... ual_en.pdf" it and looked up all the values.

I followed the chapter 30.6.1 Configuring RTC_I2C,I've come to this code,

Code: Select all

source = """
#define DR_REG_RTCIO_BASE            0x3ff48400
#define DR_REG_RTC_I2C_BASE          0x3ff48C00
#define DR_REG_SENS_BASE             0x3ff48800                  
#define SENS_SAR_SLAVE_ADDR1_REG     0x3FF4883C 
#define SENS_I2C_SLAVE_ADDR0         0x000007FF                  
#define SENS_I2C_SLAVE_ADDR0_S       11
#define SENS_I2C_SLAVE_ADDR1_S       0
#define RTC_IO_SAR_I2C_IO_REG        0x3FF484C4
#define RTC_IO_SAR_I2C_SDA_SEL_S     30
#define RTC_IO_SAR_I2C_SCL_SEL_S     28
#define RTC_I2C_SCL_LOW_PERIOD_REG   0x3FF48C00 
#define RTC_I2C_SCL_HIGH_PERIOD_REG  0x3FF48C38
#define RTC_I2C_SCL_LOW_PERIOD_S     0
#define RTC_I2C_SCL_HIGH_PERIOD_S    0
#define RTC_I2C_SDA_DUTY_REG         0x3FF48C30
#define RTC_I2C_SDA_DUTY_S           0
#define RTC_I2C_SCL_START_PERIOD_REG 0x3FF48C40
#define RTC_I2C_SCL_START_PERIOD_S   0
#define RTC_I2C_SCL_STOP_PERIOD_REG  0x3FF48C44
#define RTC_I2C_SCL_STOP_PERIOD_S    0
#define RTC_I2C_TIMEOUT_REG          0x3FF48C0C
#define RTC_I2C_TIMEOUT_S            0
#define RTC_I2C_CTRL_REG             0x3FF48C04
#define RTC_I2C_MS_MODE_S            4 
#define DR_REG_RTCIO_BASE            0x3ff48400
#define RTC_IO_TOUCH_PAD0_REG        0x3ff48494 
#define RTC_IO_TOUCH_PAD1_REG        0x3ff48498 
#define RTC_IO_TOUCH_PAD2_REG        0x3ff4849c 
#define RTC_IO_TOUCH_PAD3_REG        0x3ff484a0 
#define RTC_IO_TOUCH_PAD0_FUN_SEL_S  17 
#define RTC_IO_TOUCH_PAD1_FUN_SEL_S  17 

data:        .long 0
value:       .long 0

.global entry
entry:   
# Select SDA/SCL pins to version 1, TOUCH2 and TOUCH3 (version 0 is TOUCH0 and TOUCH1)
WRITE_RTC_REG(RTC_IO_SAR_I2C_IO_REG, RTC_IO_SAR_I2C_SDA_SEL_S, 2, 0)
WRITE_RTC_REG(RTC_IO_SAR_I2C_IO_REG, RTC_IO_SAR_I2C_SCL_SEL_S, 2, 0)
WRITE_RTC_REG(RTC_IO_TOUCH_PAD0_REG, RTC_IO_TOUCH_PAD0_FUN_SEL_S, 2, 3) 
WRITE_RTC_REG(RTC_IO_TOUCH_PAD1_REG, RTC_IO_TOUCH_PAD1_FUN_SEL_S, 2, 3) 

# WRITE_RTC_REG(rtc_reg, low_bit, bit_width, value)
WRITE_RTC_REG(SENS_SAR_SLAVE_ADDR1_REG, SENS_I2C_SLAVE_ADDR0_S, 11, 0x76)

# Set SCL speed to 100khz
WRITE_RTC_REG(RTC_I2C_SCL_LOW_PERIOD_REG, RTC_I2C_SCL_LOW_PERIOD_S, 19, 40)
WRITE_RTC_REG(RTC_I2C_SCL_HIGH_PERIOD_REG, RTC_I2C_SCL_HIGH_PERIOD_S, 20, 40)

# SDA duty (delay) cycles from falling edge of SCL when SDA changes.
WRITE_RTC_REG(RTC_I2C_SDA_DUTY_REG, RTC_I2C_SDA_DUTY_S, 20, 16)

# Number of cycles after start/stop condition
WRITE_RTC_REG(RTC_I2C_SCL_START_PERIOD_REG, RTC_I2C_SCL_START_PERIOD_S, 20, 30)
WRITE_RTC_REG(RTC_I2C_SCL_STOP_PERIOD_REG, RTC_I2C_SCL_STOP_PERIOD_S, 20, 44)

# cycles before timeout
WRITE_RTC_REG(RTC_I2C_TIMEOUT_REG, RTC_I2C_TIMEOUT_S, 20, 200)

# Set mode to master
WRITE_RTC_REG(RTC_I2C_CTRL_REG, RTC_I2C_MS_MODE_S, 1, 1)

JUMP running


reads:
ADD r0, r0, 1     # r0 += 1  
I2C_RD  0xa2, 7, 0, 0    # r0 = 0x171 //sensor returns 0x171
ADD r0, r0, 1	  # r0 += 1 
ST r0, r1, 4     # mem32[r1+4] = r0 // 0x172
JUMP exit

running:
MOVE r3, data   # r3 = 0  //(data)
LD r2, r3, 0      # [r3+0]) = r2
ADD r2, r2, 1    #  r2 += 1
ST r2, r3, 0     # [r3+0] = r2
JUMP reads

exit:
HALT
"""
from esp32_ulp import src_to_binary
from esp32 import ULP
from machine import mem32

binary = src_to_binary(source)
load_addr, entry_addr = 0, 8
ULP_MEM_BASE = 0x50000000
ULP_DATA_MASK = 0xFFFF  
ulp = ULP()
ulp.set_wakeup_period(0, 1000000)  
ulp.load_binary(load_addr, binary)
mem32[ULP_MEM_BASE + load_addr] = 0x1000
ulp.run(entry_addr)
while True:
	res1 = hex(mem32[ULP_MEM_BASE + load_addr] & ULP_DATA_MASK)
 	res2 = hex(mem32[ULP_MEM_BASE + load_addr+4] & ULP_DATA_MASK)
	print(res1,res2)
I am expecting,
0x1001 , 0x172
0x1002 , 0x172
0x1003 , 0x172
......

result is,
0x1001 , 0x0
0x1001 , 0x0
0x1001 , 0x0
......
Just for getting one bit data from I2C, generates that too long codes :)
it freezed after I2C_RD, it shows keep count up++ without I2C_RD line.
(it is hard to debug ULP, I've added a counter, but after read I2C value it is not counting up..)

I've read the references carefully, refered another c++ examples, tried different GPIO(TOUCH2,3)
,but It did not work. MCU doesn't lie to me, there is obviously something wrong,
but I don't know which I am missing ...

I think I might give up using ThomasWaldmann/py-esp32-ulp ... :cry:

Christian Walther
Posts: 169
Joined: Fri Aug 19, 2016 11:55 am

Re: ULP I2C sensor readings.

Post by Christian Walther » Sun Feb 06, 2022 5:42 pm

In can’t help you with I2C, but at a quick glance it looks like you’re not initializing r1.

KJM
Posts: 158
Joined: Sun Nov 18, 2018 10:53 pm
Location: Sydney AU

Re: ULP I2C sensor readings.

Post by KJM » Sun Feb 06, 2022 10:26 pm

Tnx for the src-to-binary shortcut suggestion I'll try that when I'm feeling braver. Sorry for intruding on your thread, I2C via ULP sounds like a great idea to me, hope you can get it working. I agree with Thomas https://github.com/ThomasWaldmann/py-es ... 1030615730 that we need a central resource where efforts ULP related might live, save people reinventing the wheel.

Christian, you mention you've done GPIO with ULP. I'd like to have a water meter pulse counter with the ULP. There are some implementations in C but I can't figure out how to use them from python. Do you know of a ULP counter program that counts pulses on a GPIO that I could substitute into https://github.com/ThomasWaldmann/py-es ... counter.py now that I have that working?

Post Reply