wardjm / esp32-ulp-i2c Goto Github PK
View Code? Open in Web Editor NEWThis is an example of how to use the hardware i2c controller on the ESP32 ultra low power (ulp) coprocessor.
This is an example of how to use the hardware i2c controller on the ESP32 ultra low power (ulp) coprocessor.
Hi,
I am trying your code with a BME280 connected to GPIO04 and GPIO00....unluckily the ulp code hangs foreover on the I2C_RD call (if I comment it ulp wakes up correctly the ESP32). Here follows my changes:
ulp.s (switched to version 0 and to RTC_IO_TOUCH_PAD0/1_RE regs, changed I2C_ADDR and I2C_RD register)
#include "soc/rtc_cntl_reg.h"
#include "soc/rtc_io_reg.h"
#include "soc/soc_ulp.h"
#include "soc/sens_reg.h"
#include "soc/rtc_i2c_reg.h"
/* Define variables, which go into .bss section (zero-initialized data) */
.bss
.global dummy
dummy: .long 0
/* Code goes into .text section */
.text
.global entry
entry:
// Release hold on pins, so that we can manipulate them
WRITE_RTC_REG(RTC_IO_TOUCH_PAD1_REG, RTC_IO_TOUCH_PAD1_HOLD_S, RTC_IO_TOUCH_PAD1_HOLD_M, 0);
WRITE_RTC_REG(RTC_IO_TOUCH_PAD0_REG, RTC_IO_TOUCH_PAD0_HOLD_S, RTC_IO_TOUCH_PAD0_HOLD_M, 0);
// 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, 0);
WRITE_RTC_REG(RTC_IO_SAR_I2C_IO_REG, RTC_IO_SAR_I2C_SCL_SEL_S, RTC_IO_SAR_I2C_SCL_SEL_M, 0);
// Set slave address for i2c device we are talking to (ADDR1_REG contains both ADDR0 and ADDR1)
// 0x5a is for CCS811
WRITE_RTC_REG(SENS_SAR_SLAVE_ADDR1_REG, SENS_I2C_SLAVE_ADDR0_S, SENS_I2C_SLAVE_ADDR0_M, 0x76);
WRITE_RTC_REG(SENS_SAR_SLAVE_ADDR1_REG, SENS_I2C_SLAVE_ADDR1_S, SENS_I2C_SLAVE_ADDR1_M, 0x76);
// Set SCL speed to 100khz
WRITE_RTC_REG(RTC_I2C_SCL_LOW_PERIOD_REG, RTC_I2C_SCL_LOW_PERIOD_S, RTC_I2C_SCL_LOW_PERIOD_M, 40);
WRITE_RTC_REG(RTC_I2C_SCL_HIGH_PERIOD_REG, RTC_I2C_SCL_HIGH_PERIOD_S, RTC_I2C_SCL_HIGH_PERIOD_M, 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, RTC_I2C_SDA_DUTY_M, 16);
// Number of cycles after start/stop condition
WRITE_RTC_REG(RTC_I2C_SCL_START_PERIOD_REG, RTC_I2C_SCL_START_PERIOD_S, RTC_I2C_SCL_START_PERIOD_M, 30);
WRITE_RTC_REG(RTC_I2C_SCL_STOP_PERIOD_REG, RTC_I2C_SCL_STOP_PERIOD_S, RTC_I2C_SCL_STOP_PERIOD_M, 44);
// cycles before timeout
WRITE_RTC_REG(RTC_I2C_TIMEOUT_REG, RTC_I2C_TIMEOUT_S, RTC_I2C_TIMEOUT_M, 10000);
// Set mode to master
WRITE_RTC_REG(RTC_I2C_CTRL_REG, RTC_I2C_MS_MODE_S, RTC_I2C_MS_MODE_M, 1);
// Main code, store the i2c read (0x20 = get hardware id) into dummy and return the value to main process
move r3, dummy
i2c_rd 0xd0, 7, 0, 1
st r0, r3, 0
jump wake_up
jump entry
/* end the program */
.global exit
exit:
/* Set the GPIO13 output LOW (clear output) to signal that ULP is now going down (the 14 is because GPIO13 = RTC_GPIO_14) */
WRITE_RTC_REG(RTC_GPIO_OUT_W1TC_REG, RTC_GPIO_OUT_DATA_W1TC_S + 14, 1, 1)
/* Enable hold on GPIO13 output */
WRITE_RTC_REG(RTC_IO_TOUCH_PAD4_REG, RTC_IO_TOUCH_PAD4_HOLD_S, 1, 1)
halt
.global wake_up
wake_up:
/* Check if the system can be woken up */
READ_RTC_FIELD(RTC_CNTL_LOW_POWER_ST_REG, RTC_CNTL_RDY_FOR_WAKEUP)
and r0, r0, 1
jump exit, eq
/* Wake up the SoC, end program */
wake
WRITE_RTC_FIELD(RTC_CNTL_STATE0_REG, RTC_CNTL_ULP_CP_SLP_TIMER_EN, 0)
jump exit
ulp_test.ino (changed to version 0 and i2c_address)
#include "esp32/ulp.h"
#include "esp_sleep.h"
#include "driver/rtc_io.h"
#include "soc/sens_reg.h"
#include "soc/rtc_i2c_reg.h"
#include "esp_deep_sleep.h"
// Our header
#include "ulp_main.h"
// Unlike the esp-idf always use these binary blob names
extern const uint8_t ulp_main_bin_start[] asm("_binary_ulp_main_bin_start");
extern const uint8_t ulp_main_bin_end[] asm("_binary_ulp_main_bin_end");
static void init_run_ulp(uint32_t usec);
void setup() {
esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause();
if (cause != ESP_SLEEP_WAKEUP_ULP) {
init_run_ulp(5000 * 1000); // 100 msec
}
else {
// ***** HERE YOUR SKETCH *****
printf("Deep sleep wakeup\n");
printf("ULP dummy i2c read value: %08x\n", ulp_dummy & UINT16_MAX);
}
// Re-entering deep sleep
delay(1000); // Otherwise we are just spinning reading the same thing like crazy
start_ulp_program();
ESP_ERROR_CHECK( esp_sleep_enable_ulp_wakeup() );
printf("Before sleep\n");
esp_deep_sleep_start();
}
void loop() {
// Nothing goes here
}
// Use this function for all your first time init like setup() used to be
static void init_run_ulp(uint32_t usec) {
// initialize ulp variables
printf("initing...\n");
ulp_set_wakeup_period(0, usec);
// You MUST load the binary before setting shared variables!
esp_err_t err = ulp_load_binary(0, ulp_main_bin_start, (ulp_main_bin_end - ulp_main_bin_start) / sizeof(uint32_t));
ESP_ERROR_CHECK(err);
// Setup RTC I2C controller, selection either 0 or 1, see below for exact description and wiring (you can mix and match and comment out unused gpios)
SET_PERI_REG_BITS(RTC_IO_SAR_I2C_IO_REG, RTC_IO_SAR_I2C_SDA_SEL, 0, RTC_IO_SAR_I2C_SDA_SEL_S);
SET_PERI_REG_BITS(RTC_IO_SAR_I2C_IO_REG, RTC_IO_SAR_I2C_SCL_SEL, 0, RTC_IO_SAR_I2C_SCL_SEL_S);
// SDA/SCL selection 0 is for
// TOUCH0 = GPIO4 = SCL
// TOUCH1 = GPIO0 = SDA
rtc_gpio_init(GPIO_NUM_0);
rtc_gpio_init(GPIO_NUM_4);
// SDA/SCL selection 1 is for
// TOUCH2 = GPIO2 = SCL
// TOUCH3 = GPIO15 = SDA
//rtc_gpio_init(GPIO_NUM_2);
//rtc_gpio_init(GPIO_NUM_15);
// We will be reading and writing on these gpio
//rtc_gpio_set_direction(GPIO_NUM_15, RTC_GPIO_MODE_INPUT_OUTPUT);
rtc_gpio_set_direction(GPIO_NUM_4, RTC_GPIO_MODE_INPUT_OUTPUT);
//rtc_gpio_set_direction(GPIO_NUM_2, RTC_GPIO_MODE_INPUT_OUTPUT);
rtc_gpio_set_direction(GPIO_NUM_0, RTC_GPIO_MODE_INPUT_OUTPUT);
printf("rtc io...\n");
// Look up your device for these standard values needed to do i2c:
WRITE_PERI_REG(RTC_I2C_SCL_LOW_PERIOD_REG, 40); // SCL low/high period = 40, which result driving SCL with 100kHz.
WRITE_PERI_REG(RTC_I2C_SCL_HIGH_PERIOD_REG, 40);
WRITE_PERI_REG(RTC_I2C_SDA_DUTY_REG, 16); // SDA duty (delay) cycles from falling edge of SCL when SDA changes.
WRITE_PERI_REG(RTC_I2C_SCL_START_PERIOD_REG, 30); // Number of cycles to wait after START condition.
WRITE_PERI_REG(RTC_I2C_SCL_STOP_PERIOD_REG, 44); // Number of cycles to wait after STOP condition.
WRITE_PERI_REG(RTC_I2C_TIMEOUT_REG, 10000); // Number of cycles before timeout.
SET_PERI_REG_BITS(RTC_I2C_CTRL_REG, RTC_I2C_MS_MODE, 1, RTC_I2C_MS_MODE_S); // Set i2c mode to master for the ulp controller
// Both aren't necessary, but for example of how to get ADDR0 when there is no ADDR0_REG
// Also note this is SENS_SAR regs but everything else uses RTC_*
SET_PERI_REG_BITS(SENS_SAR_SLAVE_ADDR1_REG, SENS_I2C_SLAVE_ADDR0, 0x76, SENS_I2C_SLAVE_ADDR0_S); // Set I2C device address.
SET_PERI_REG_BITS(SENS_SAR_SLAVE_ADDR1_REG, SENS_I2C_SLAVE_ADDR1, 0x76, SENS_I2C_SLAVE_ADDR1_S); // Set I2C device address.
esp_deep_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
// Any other ulp variables can be assigned here
printf("end of init\n");
delay(100);
}
static void start_ulp_program() {
/* Start the program */
printf("Going to sleep...\n");
delay(100);
esp_err_t err = ulp_run((&ulp_entry - RTC_SLOW_MEM) / sizeof(uint32_t));
ESP_ERROR_CHECK(err);
}
Hardware wiring is correct because by I tested it with ESP32 and it can access the BME280 through I2C. Do you have an idea of what it could be ?
On stock Arduino 1.8.12 (Windows) it gives these errors, it seems that the .S file is not compiled. Shall I have to install some extensions ? I attach a verbose compilation log
Arduino: 1.8.12 (Windows 10), Board: "ESP32 Dev Module, Disabled, Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS), 240MHz (WiFi/BT), QIO, 80MHz, 4MB (32Mb), 230400, None"
sketch\ulp_test.ino.cpp.o:(.literal._Z5setupv+0x8): undefined reference to `_binary_ulp_main_bin_end'
sketch\ulp_test.ino.cpp.o:(.literal._Z5setupv+0xc): undefined reference to `_binary_ulp_main_bin_start'
sketch\ulp_test.ino.cpp.o:(.literal._Z5setupv+0x64): undefined reference to `ulp_dummy'
sketch\ulp_test.ino.cpp.o:(.literal._Z5setupv+0x70): undefined reference to `ulp_entry'
collect2.exe: error: ld returned 1 exit status
exit status 1
Error compiling for board ESP32 Dev Module.
This report would have more information with
"Show verbose output during compilation"
option enabled in File -> Preferences.
hi, i don't believe your method can be used to read 16-bit values; some sensors provide this. see https://github.com/tomtor/ulp-i2c for a bit-banged approach to the problem.
how do you instal for using de ULP, because i compile the file and somethings is messing. Thanks
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.