ulp: Added support for RTC I2C driver for ULP RISC-V on esp32s2 and esp32s3

This commit adds support for using the RTC I2C peripheral on the ULP
RISC-V core for esp32s2 and esp32s3. It also adds an example to demonstrate the
usage of the RTC I2C peripheral.

This commit also modifies the rtc_i2c register structure files to enable
the use of bitfields in the ULP RISC-V RTC I2C driver.
pull/9741/head
Sudeep Mohanty 2022-08-09 15:46:14 +02:00
rodzic 6193e4c8e8
commit 4fde033a5f
21 zmienionych plików z 1559 dodań i 302 usunięć

Wyświetl plik

@ -165,7 +165,11 @@ typedef volatile struct rtc_i2c_dev_s {
} fifo_data;
union {
struct {
uint32_t command0: 14; /*command0*/
uint32_t byte_num: 8;
uint32_t ack_en: 1;
uint32_t ack_exp: 1;
uint32_t ack_val: 1;
uint32_t op_code: 3;
uint32_t reserved14: 17;
uint32_t done: 1; /*command0_done*/
};

Wyświetl plik

@ -166,9 +166,13 @@ typedef volatile struct rtc_i2c_dev_s {
} fifo_data;
union {
struct {
uint32_t command0: 14; /*command0*/
uint32_t byte_num: 8;
uint32_t ack_en: 1;
uint32_t ack_exp: 1;
uint32_t ack_val: 1;
uint32_t op_code: 3;
uint32_t reserved14: 17;
uint32_t done: 1; /*command0_done*/
uint32_t done: 1;
};
uint32_t val;
} command[16];

Wyświetl plik

@ -11,6 +11,7 @@ PROVIDE ( SDM = 0x3f404f00 );
PROVIDE ( RTCCNTL = 0x3f408000 );
PROVIDE ( RTCIO = 0x3f408400 );
PROVIDE ( SENS = 0x3f408800 );
PROVIDE ( RTC_I2C = 0x3f408C00 );
PROVIDE ( HINF = 0x3f40B000 );
PROVIDE ( I2S0 = 0x3f40F000 );
PROVIDE ( UART1 = 0x3f410000 );

Wyświetl plik

@ -190,294 +190,27 @@ typedef union {
uint32_t val;
} rtc_i2c_data_reg_t;
/** Type of i2c_cmd0 register
* i2c commond0 register
/** Type of i2c_cmd register
* i2c command register
*/
typedef union {
struct {
/** i2c_command0 : R/W; bitpos: [13:0]; default: 2307;
* command0
/** i2c_command : R/W; bitpos: [13:0]; default: 2307;
* command
*/
uint32_t i2c_command0:14;
uint32_t reserved_14:17;
/** i2c_command0_done : RO; bitpos: [31]; default: 0;
uint32_t i2c_byte_num:8;
uint32_t i2c_ack_en:1;
uint32_t i2c_ack_exp:1;
uint32_t i2c_ack_val:1;
uint32_t i2c_op_code:3;
uint32_t reserved14:17;
/** i2c_command_done : RO; bitpos: [31]; default: 0;
* command0_done
*/
uint32_t i2c_command0_done:1;
uint32_t i2c_command_done:1;
};
uint32_t val;
} rtc_i2c_cmd0_reg_t;
/** Type of i2c_cmd1 register
* i2c commond1 register
*/
typedef union {
struct {
/** i2c_command1 : R/W; bitpos: [13:0]; default: 6401;
* command1
*/
uint32_t i2c_command1:14;
uint32_t reserved_14:17;
/** i2c_command1_done : RO; bitpos: [31]; default: 0;
* command1_done
*/
uint32_t i2c_command1_done:1;
};
uint32_t val;
} rtc_i2c_cmd1_reg_t;
/** Type of i2c_cmd2 register
* i2c commond2 register
*/
typedef union {
struct {
/** i2c_command2 : R/W; bitpos: [13:0]; default: 2306;
* command2
*/
uint32_t i2c_command2:14;
uint32_t reserved_14:17;
/** i2c_command2_done : RO; bitpos: [31]; default: 0;
* command2_done
*/
uint32_t i2c_command2_done:1;
};
uint32_t val;
} rtc_i2c_cmd2_reg_t;
/** Type of i2c_cmd3 register
* i2c commond3 register
*/
typedef union {
struct {
/** i2c_command3 : R/W; bitpos: [13:0]; default: 257;
* command3
*/
uint32_t i2c_command3:14;
uint32_t reserved_14:17;
/** i2c_command3_done : RO; bitpos: [31]; default: 0;
* command3_done
*/
uint32_t i2c_command3_done:1;
};
uint32_t val;
} rtc_i2c_cmd3_reg_t;
/** Type of i2c_cmd4 register
* i2c commond4 register
*/
typedef union {
struct {
/** i2c_command4 : R/W; bitpos: [13:0]; default: 2305;
* command4
*/
uint32_t i2c_command4:14;
uint32_t reserved_14:17;
/** i2c_command4_done : RO; bitpos: [31]; default: 0;
* command4_done
*/
uint32_t i2c_command4_done:1;
};
uint32_t val;
} rtc_i2c_cmd4_reg_t;
/** Type of i2c_cmd5 register
* i2c commond5_register
*/
typedef union {
struct {
/** i2c_command5 : R/W; bitpos: [13:0]; default: 5889;
* command5
*/
uint32_t i2c_command5:14;
uint32_t reserved_14:17;
/** i2c_command5_done : RO; bitpos: [31]; default: 0;
* command5_done
*/
uint32_t i2c_command5_done:1;
};
uint32_t val;
} rtc_i2c_cmd5_reg_t;
/** Type of i2c_cmd6 register
* i2c commond6 register
*/
typedef union {
struct {
/** i2c_command6 : R/W; bitpos: [13:0]; default: 6401;
* command6
*/
uint32_t i2c_command6:14;
uint32_t reserved_14:17;
/** i2c_command6_done : RO; bitpos: [31]; default: 0;
* command6_done
*/
uint32_t i2c_command6_done:1;
};
uint32_t val;
} rtc_i2c_cmd6_reg_t;
/** Type of i2c_cmd7 register
* i2c commond7 register
*/
typedef union {
struct {
/** i2c_command7 : R/W; bitpos: [13:0]; default: 2308;
* command7
*/
uint32_t i2c_command7:14;
uint32_t reserved_14:17;
/** i2c_command7_done : RO; bitpos: [31]; default: 0;
* command7_done
*/
uint32_t i2c_command7_done:1;
};
uint32_t val;
} rtc_i2c_cmd7_reg_t;
/** Type of i2c_cmd8 register
* i2c commond8 register
*/
typedef union {
struct {
/** i2c_command8 : R/W; bitpos: [13:0]; default: 6401;
* command8
*/
uint32_t i2c_command8:14;
uint32_t reserved_14:17;
/** i2c_command8_done : RO; bitpos: [31]; default: 0;
* command8_done
*/
uint32_t i2c_command8_done:1;
};
uint32_t val;
} rtc_i2c_cmd8_reg_t;
/** Type of i2c_cmd9 register
* i2c commond9 register
*/
typedef union {
struct {
/** i2c_command9 : R/W; bitpos: [13:0]; default: 2307;
* command9
*/
uint32_t i2c_command9:14;
uint32_t reserved_14:17;
/** i2c_command9_done : RO; bitpos: [31]; default: 0;
* command9_done
*/
uint32_t i2c_command9_done:1;
};
uint32_t val;
} rtc_i2c_cmd9_reg_t;
/** Type of i2c_cmd10 register
* i2c commond10 register
*/
typedef union {
struct {
/** i2c_command10 : R/W; bitpos: [13:0]; default: 257;
* command10
*/
uint32_t i2c_command10:14;
uint32_t reserved_14:17;
/** i2c_command10_done : RO; bitpos: [31]; default: 0;
* command10_done
*/
uint32_t i2c_command10_done:1;
};
uint32_t val;
} rtc_i2c_cmd10_reg_t;
/** Type of i2c_cmd11 register
* i2c commond11 register
*/
typedef union {
struct {
/** i2c_command11 : R/W; bitpos: [13:0]; default: 2305;
* command11
*/
uint32_t i2c_command11:14;
uint32_t reserved_14:17;
/** i2c_command11_done : RO; bitpos: [31]; default: 0;
* command11_done
*/
uint32_t i2c_command11_done:1;
};
uint32_t val;
} rtc_i2c_cmd11_reg_t;
/** Type of i2c_cmd12 register
* i2c commond12 register
*/
typedef union {
struct {
/** i2c_command12 : R/W; bitpos: [13:0]; default: 5889;
* command12
*/
uint32_t i2c_command12:14;
uint32_t reserved_14:17;
/** i2c_command12_done : RO; bitpos: [31]; default: 0;
* command12_done
*/
uint32_t i2c_command12_done:1;
};
uint32_t val;
} rtc_i2c_cmd12_reg_t;
/** Type of i2c_cmd13 register
* i2c commond13 register
*/
typedef union {
struct {
/** i2c_command13 : R/W; bitpos: [13:0]; default: 6401;
* command13
*/
uint32_t i2c_command13:14;
uint32_t reserved_14:17;
/** i2c_command13_done : RO; bitpos: [31]; default: 0;
* command13_done
*/
uint32_t i2c_command13_done:1;
};
uint32_t val;
} rtc_i2c_cmd13_reg_t;
/** Type of i2c_cmd14 register
* i2c commond14 register
*/
typedef union {
struct {
/** i2c_command14 : R/W; bitpos: [13:0]; default: 0;
* command14
*/
uint32_t i2c_command14:14;
uint32_t reserved_14:17;
/** i2c_command14_done : RO; bitpos: [31]; default: 0;
* command14_done
*/
uint32_t i2c_command14_done:1;
};
uint32_t val;
} rtc_i2c_cmd14_reg_t;
/** Type of i2c_cmd15 register
* i2c commond15 register
*/
typedef union {
struct {
/** i2c_command15 : R/W; bitpos: [13:0]; default: 0;
* command15
*/
uint32_t i2c_command15:14;
uint32_t reserved_14:17;
/** i2c_command15_done : RO; bitpos: [31]; default: 0;
* command15_done
*/
uint32_t i2c_command15_done:1;
};
uint32_t val;
} rtc_i2c_cmd15_reg_t;
} rtc_i2c_cmd_reg_t;
/** Group: status register */
/** Type of i2c_status register
@ -750,29 +483,15 @@ typedef struct {
volatile rtc_i2c_int_st_reg_t i2c_int_st;
volatile rtc_i2c_int_ena_reg_t i2c_int_ena;
volatile rtc_i2c_data_reg_t i2c_data;
volatile rtc_i2c_cmd0_reg_t i2c_cmd0;
volatile rtc_i2c_cmd1_reg_t i2c_cmd1;
volatile rtc_i2c_cmd2_reg_t i2c_cmd2;
volatile rtc_i2c_cmd3_reg_t i2c_cmd3;
volatile rtc_i2c_cmd4_reg_t i2c_cmd4;
volatile rtc_i2c_cmd5_reg_t i2c_cmd5;
volatile rtc_i2c_cmd6_reg_t i2c_cmd6;
volatile rtc_i2c_cmd7_reg_t i2c_cmd7;
volatile rtc_i2c_cmd8_reg_t i2c_cmd8;
volatile rtc_i2c_cmd9_reg_t i2c_cmd9;
volatile rtc_i2c_cmd10_reg_t i2c_cmd10;
volatile rtc_i2c_cmd11_reg_t i2c_cmd11;
volatile rtc_i2c_cmd12_reg_t i2c_cmd12;
volatile rtc_i2c_cmd13_reg_t i2c_cmd13;
volatile rtc_i2c_cmd14_reg_t i2c_cmd14;
volatile rtc_i2c_cmd15_reg_t i2c_cmd15;
volatile rtc_i2c_cmd_reg_t i2c_cmd[16];
uint32_t reserved_078[33];
volatile rtc_i2c_date_reg_t i2c_date;
} rtc_dev_t;
} rtc_i2c_dev_t;
extern rtc_i2c_dev_t RTC_I2C;
#ifndef __cplusplus
_Static_assert(sizeof(rtc_dev_t) == 0x100, "Invalid size of rtc_dev_t structure");
_Static_assert(sizeof(rtc_i2c_dev_t) == 0x100, "Invalid size of rtc_i2c_dev_t structure");
#endif
#ifdef __cplusplus

Wyświetl plik

@ -12,6 +12,7 @@ PROVIDE ( EFUSE = 0x60007000 );
PROVIDE ( RTCCNTL = 0x60008000 );
PROVIDE ( RTCIO = 0x60008400 );
PROVIDE ( SENS = 0x60008800 );
PROVIDE ( RTC_I2C = 0x60008C00 );
PROVIDE ( HINF = 0x6000B000 );
PROVIDE ( I2S0 = 0x6000F000 );
PROVIDE ( I2S1 = 0x6002D000 );

Wyświetl plik

@ -25,7 +25,8 @@ if(CONFIG_SOC_ULP_SUPPORTED OR CONFIG_SOC_RISCV_COPROC_SUPPORTED)
list(APPEND srcs
"ulp_riscv/ulp_riscv.c"
"ulp_riscv/ulp_riscv_lock.c"
"ulp_riscv/ulp_riscv_adc.c")
"ulp_riscv/ulp_riscv_adc.c"
"ulp_riscv/ulp_riscv_i2c.c")
list(APPEND includes
ulp_riscv/include

Wyświetl plik

@ -76,6 +76,7 @@ if(ULP_COCPU_IS_RISCV)
"${IDF_PATH}/components/ulp/ulp_riscv/ulp_core/ulp_riscv_lock.c"
"${IDF_PATH}/components/ulp/ulp_riscv/ulp_core/ulp_riscv_uart.c"
"${IDF_PATH}/components/ulp/ulp_riscv/ulp_core/ulp_riscv_print.c"
"${IDF_PATH}/components/ulp/ulp_riscv/ulp_core/ulp_riscv_i2c.c"
"${IDF_PATH}/components/ulp/ulp_riscv/ulp_core/ulp_riscv_utils.c")
target_link_options(${ULP_APP_NAME} PRIVATE "-nostartfiles")

Wyświetl plik

@ -7,3 +7,4 @@
PROVIDE ( RTCCNTL = 0x8000 );
PROVIDE ( RTCIO = 0xA400 );
PROVIDE ( SENS = 0xC800 );
PROVIDE ( RTC_I2C = 0x8C00 );

Wyświetl plik

@ -7,3 +7,4 @@
PROVIDE ( RTCCNTL = 0x8000 );
PROVIDE ( RTCIO = 0xA400 );
PROVIDE ( SENS = 0xC800 );
PROVIDE ( RTC_I2C = 0xEC00 );

Wyświetl plik

@ -0,0 +1,99 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include <stdbool.h>
#include "hal/gpio_types.h"
#include "esp_err.h"
typedef struct {
uint32_t sda_io_num; // GPIO pin for SDA signal. Only GPIO#1 or GPIO#3 can be used as the SDA pin.
uint32_t scl_io_num; // GPIO pin for SCL signal. Only GPIO#0 or GPIO#2 can be used as the SCL pin.
bool sda_pullup_en; // SDA line enable internal pullup. Can be configured if external pullup is not used.
bool scl_pullup_en; // SCL line enable internal pullup. Can be configured if external pullup is not used.
} ulp_riscv_i2c_pin_cfg_t;
typedef struct {
uint32_t scl_low_period; // SCL low period
uint32_t scl_high_period; // SCL high period
uint32_t sda_duty_period; // Period between the SDA switch and the falling edge of SCL
uint32_t scl_start_period; // Waiting time after the START condition
uint32_t scl_stop_period; // Waiting time before the END condition
uint32_t i2c_trans_timeout; // I2C transaction timeout
} ulp_riscv_i2c_timing_cfg_t;
typedef struct {
ulp_riscv_i2c_pin_cfg_t i2c_pin_cfg; // RTC I2C pin configuration
ulp_riscv_i2c_timing_cfg_t i2c_timing_cfg; // RTC I2C timing configuration
} ulp_riscv_i2c_cfg_t;
/* Nominal default GPIO settings and timing parametes */
#define ULP_RISCV_I2C_DEFAULT_CONFIG() \
{ \
.i2c_pin_cfg.sda_io_num = GPIO_NUM_3, \
.i2c_pin_cfg.scl_io_num = GPIO_NUM_2, \
.i2c_pin_cfg.sda_pullup_en = true, \
.i2c_pin_cfg.scl_pullup_en = true, \
.i2c_timing_cfg.scl_low_period = 5, \
.i2c_timing_cfg.scl_high_period = 5, \
.i2c_timing_cfg.sda_duty_period = 2, \
.i2c_timing_cfg.scl_start_period = 3, \
.i2c_timing_cfg.scl_stop_period = 6, \
.i2c_timing_cfg.i2c_trans_timeout = 20, \
}
/**
* @brief Set the I2C slave device address
*
* @param slave_addr I2C slave address (7 bit)
*/
void ulp_riscv_i2c_master_set_slave_addr(uint8_t slave_addr);
/**
* @brief Set the I2C slave device sub register address
*
* @param slave_reg_addr I2C slave sub register address
*/
void ulp_riscv_i2c_master_set_slave_reg_addr(uint8_t slave_reg_addr);
/**
* @brief Read from I2C slave device
*
* @note The I2C slave device address must be configured at least once before invoking this API.
*
* @param data_rd Buffer to hold data to be read
* @param size Size of data to be read in bytes
*/
void ulp_riscv_i2c_master_read_from_device(uint8_t *data_rd, size_t size);
/**
* @brief Write to I2C slave device
*
* @note The I2C slave device address must be configured at least once before invoking this API.
*
* @param data_wr Buffer which holds the data to be written
* @param size Size of data to be written in bytes
*/
void ulp_riscv_i2c_master_write_to_device(uint8_t *data_wr, size_t size);
/**
* @brief Initialize and configure the RTC I2C for use by ULP RISC-V
* Currently RTC I2C can only be used in master mode
*
* @param cfg Configuration parameters
* @return esp_err_t ESP_OK when successful
*/
esp_err_t ulp_riscv_i2c_master_init(const ulp_riscv_i2c_cfg_t *cfg);
#ifdef __cplusplus
}
#endif

Wyświetl plik

@ -0,0 +1,52 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <stddef.h>
#include <stdint.h>
/**
* @brief Set the I2C slave device address
*
* @param slave_addr I2C slave address (7 bit)
*/
void ulp_riscv_i2c_master_set_slave_addr(uint8_t slave_addr);
/**
* @brief Set the I2C slave device sub register address
*
* @param slave_reg_addr I2C slave register address
*/
void ulp_riscv_i2c_master_set_slave_reg_addr(uint8_t slave_reg_addr);
/**
* @brief Read from I2C slave device
*
* @note The I2C slave device address must be configured at least once before invoking this API.
*
* @param data_rd Buffer to hold data to be read
* @param size Size of data to be read in bytes
*/
void ulp_riscv_i2c_master_read_from_device(uint8_t *data_rd, size_t size);
/**
* @brief Write to I2C slave device
*
* @note The I2C slave device address must be configured at least once before invoking this API.
*
* @param data_wr Buffer which holds the data to be written
* @param size Size of data to be written in bytes
*/
void ulp_riscv_i2c_master_write_to_device(uint8_t *data_wr, size_t size);
#ifdef __cplusplus
}
#endif

Wyświetl plik

@ -0,0 +1,225 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "ulp_riscv_i2c_ulp_core.h"
#include "ulp_riscv_utils.h"
#include "soc/rtc_i2c_reg.h"
#include "soc/rtc_i2c_struct.h"
#include "soc/rtc_io_reg.h"
#include "soc/sens_reg.h"
#include "hal/i2c_ll.h"
#define I2C_CTRL_SLAVE_ADDR_MASK (0xFF << 0)
#define I2C_CTRL_SLAVE_REG_ADDR_MASK (0xFF << 11)
#define I2C_CTRL_MASTER_TX_DATA_MASK (0xFF << 19)
#if CONFIG_IDF_TARGET_ESP32S3
#define ULP_I2C_CMD_RESTART 0 /*!<I2C restart command */
#define ULP_I2C_CMD_WRITE 1 /*!<I2C write command */
#define ULP_I2C_CMD_READ 2 /*!<I2C read command */
#define ULP_I2C_CMD_STOP 3 /*!<I2C stop command */
#define ULP_I2C_CMD_END 4 /*!<I2C end command */
#else
#define ULP_I2C_CMD_RESTART I2C_LL_CMD_RESTART /*!<I2C restart command */
#define ULP_I2C_CMD_WRITE I2C_LL_CMD_WRITE /*!<I2C write command */
#define ULP_I2C_CMD_READ I2C_LL_CMD_READ /*!<I2C read command */
#define ULP_I2C_CMD_STOP I2C_LL_CMD_STOP /*!<I2C stop command */
#define ULP_I2C_CMD_END I2C_LL_CMD_END /*!<I2C end command */
#endif // CONFIG_IDF_TARGET_ESP32S3
/*
* The RTC I2C controller follows the I2C command registers to perform read/write operations.
* The cmd registers have the following format:
*
* 31 30:14 13:11 10 9 8 7:0
* |----------|----------|---------|---------|----------|------------|---------|
* | CMD_DONE | Reserved | OPCODE |ACK Value|ACK Expect|ACK Check En|Byte Num |
* |----------|----------|---------|---------|----------|------------|---------|
*/
static void ulp_riscv_i2c_format_cmd(uint32_t cmd_idx, uint8_t op_code, uint8_t ack_val,
uint8_t ack_expected, uint8_t ack_check_en, uint8_t byte_num)
{
uint32_t reg_addr = RTC_I2C_CMD0_REG + 4*cmd_idx;
CLEAR_PERI_REG_MASK(reg_addr, 0xFFFFFFFF);
WRITE_PERI_REG(reg_addr,
(0 << 31) | // CMD Done
((op_code & 0x3) << 11) | // Opcode
((ack_val & 0x1) << 10) | // ACK bit sent by I2C controller during READ.
// Ignored during RSTART, STOP, END and WRITE cmds.
((ack_expected & 0x1) << 9) | // ACK bit expected by I2C controller during WRITE.
// Ignored during RSTART, STOP, END and READ cmds.
((ack_check_en & 0x1) << 8) | // I2C controller verifies that the ACK bit sent by the slave device matches
// the ACK expected bit during WRITE.
// Ignored during RSTART, STOP, END and READ cmds.
((byte_num & 0xFF) << 0)); // Byte Num
}
void ulp_riscv_i2c_master_set_slave_addr(uint8_t slave_addr)
{
CLEAR_PERI_REG_MASK(SENS_SAR_I2C_CTRL_REG, I2C_CTRL_SLAVE_ADDR_MASK);
SET_PERI_REG_BITS(SENS_SAR_I2C_CTRL_REG, 0xFF, slave_addr, 0);
}
void ulp_riscv_i2c_master_set_slave_reg_addr(uint8_t slave_reg_addr)
{
CLEAR_PERI_REG_MASK(SENS_SAR_I2C_CTRL_REG, I2C_CTRL_SLAVE_REG_ADDR_MASK);
SET_PERI_REG_BITS(SENS_SAR_I2C_CTRL_REG, 0xFF, slave_reg_addr, 11);
}
/*
* I2C transactions when master reads one byte of data from the slave device:
*
* |--------|--------|---------|--------|--------|--------|--------|---------|--------|--------|--------|--------|
* | Master | START | SAD + W | | SUB | | SR | SAD + R | | | NACK | STOP |
* |--------|--------|---------|--------|--------|--------|--------|---------|--------|--------|--------|--------|
* | Slave | | | ACK | | ACK | | | ACK | DATA | | |
* |--------|--------|---------|--------|--------|--------|--------|---------|--------|--------|--------|--------|
*
* I2C transactions when master reads multiple bytes of data from the slave device:
*
* |--------|--------|---------|--------|--------|--------|--------|---------|--------|--------|--------|--------|--------|--------|
* | Master | START | SAD + W | | SUB | | SR | SAD + R | | | ACK | | NACK | STOP |
* |--------|--------|---------|--------|--------|--------|--------|---------|--------|--------|--------|--------|--------|--------|
* | Slave | | | ACK | | ACK | | | ACK | DATA | | DATA | | |
* |--------|--------|---------|--------|--------|--------|--------|---------|--------|--------|--------|--------|--------|--------|
*/
void ulp_riscv_i2c_master_read_from_device(uint8_t *data_rd, size_t size)
{
uint32_t i = 0;
uint32_t cmd_idx = 0;
if (size == 0) {
// Quietly return
return;
}
/* By default, RTC I2C controller is hard wired to use CMD2 register onwards for read operations */
cmd_idx = 2;
/* Write slave addr */
ulp_riscv_i2c_format_cmd(cmd_idx++, ULP_I2C_CMD_WRITE, 0, 0, 1, 2);
/* Repeated START */
ulp_riscv_i2c_format_cmd(cmd_idx++, ULP_I2C_CMD_RESTART, 0, 0, 0, 0);
/* Write slave register addr */
ulp_riscv_i2c_format_cmd(cmd_idx++, ULP_I2C_CMD_WRITE, 0, 0, 1, 1);
if (size > 1) {
/* Read n - 1 bytes */
ulp_riscv_i2c_format_cmd(cmd_idx++, ULP_I2C_CMD_READ, 0, 0, 1, size - 1);
}
/* Read last byte + NACK */
ulp_riscv_i2c_format_cmd(cmd_idx++, ULP_I2C_CMD_READ, 1, 1, 1, 1);
/* STOP */
ulp_riscv_i2c_format_cmd(cmd_idx++, ULP_I2C_CMD_STOP, 0, 0, 0, 0);
/* Configure the RTC I2C controller in read mode */
SET_PERI_REG_BITS(SENS_SAR_I2C_CTRL_REG, 0x1, 0, 27);
/* Enable Rx data interrupt */
SET_PERI_REG_MASK(RTC_I2C_INT_ENA_REG, RTC_I2C_RX_DATA_INT_ENA);
/* Start RTC I2C transmission */
SET_PERI_REG_MASK(SENS_SAR_I2C_CTRL_REG, SENS_SAR_I2C_START_FORCE);
SET_PERI_REG_MASK(SENS_SAR_I2C_CTRL_REG, SENS_SAR_I2C_START);
for (i = 0; i < size; i++) {
/* Poll for RTC I2C Rx Data interrupt bit to be set */
while (!REG_GET_FIELD(RTC_I2C_INT_ST_REG, RTC_I2C_RX_DATA_INT_ST)) { }
/* Read the data
*
* Unfortunately, the RTC I2C has no fifo buffer to help us with reading and storing
* multiple bytes of data. Therefore, we need to read one byte at a time and clear the
* Rx interrupt to get ready for the next byte.
*/
#if CONFIG_IDF_TARGET_ESP32S2
data_rd[i] = REG_GET_FIELD(RTC_I2C_DATA_REG, RTC_I2C_RDATA);
#elif CONFIG_IDF_TARGET_ESP32S3
data_rd[i] = REG_GET_FIELD(RTC_I2C_DATA_REG, RTC_I2C_I2C_RDATA);
#endif // CONFIG_IDF_TARGET_ESP32S2
/* Clear the Rx data interrupt bit */
SET_PERI_REG_MASK(RTC_I2C_INT_CLR_REG, RTC_I2C_RX_DATA_INT_CLR);
}
/* Clear the RTC I2C transmission bits */
CLEAR_PERI_REG_MASK(SENS_SAR_I2C_CTRL_REG, SENS_SAR_I2C_START_FORCE);
CLEAR_PERI_REG_MASK(SENS_SAR_I2C_CTRL_REG, SENS_SAR_I2C_START);
}
/*
* I2C transactions when master writes one byte of data to the slave device:
*
* |--------|--------|---------|--------|--------|--------|--------|--------|--------|
* | Master | START | SAD + W | | SUB | | DATA | | STOP |
* |--------|--------|---------|--------|--------|--------|--------|--------|--------|
* | Slave | | | ACK | | ACK | | ACK | |
* |--------|--------|---------|--------|--------|--------|--------|--------|--------|
*
* I2C transactions when master writes multiple bytes of data to the slave device:
*
* |--------|--------|---------|--------|--------|--------|--------|--------|--------|--------|--------|
* | Master | START | SAD + W | | SUB | | DATA | | DATA | | STOP |
* |--------|--------|---------|--------|--------|--------|--------|--------|--------|--------|--------|
* | Slave | | | ACK | | ACK | | ACK | | ACK | |
* |--------|--------|---------|--------|--------|--------|--------|--------|--------|--------|--------|
*/
void ulp_riscv_i2c_master_write_to_device(uint8_t *data_wr, size_t size)
{
uint32_t i = 0;
uint32_t cmd_idx = 0;
if (size == 0) {
// Quietly return
return;
}
/* By default, RTC I2C controller is hard wired to use CMD0 and CMD1 registers for write operations */
cmd_idx = 0;
/* Write slave addr + reg addr + data */
ulp_riscv_i2c_format_cmd(cmd_idx++, ULP_I2C_CMD_WRITE, 0, 0, 1, 2 + size);
/* Stop */
ulp_riscv_i2c_format_cmd(cmd_idx++, ULP_I2C_CMD_STOP, 0, 0, 0, 0);
/* Configure the RTC I2C controller in write mode */
SET_PERI_REG_BITS(SENS_SAR_I2C_CTRL_REG, 0x1, 1, 27);
/* Enable Tx data interrupt */
SET_PERI_REG_MASK(RTC_I2C_INT_ENA_REG, RTC_I2C_TX_DATA_INT_ENA);
for (i = 0; i < size; i++) {
/* Write the data to be transmitted */
CLEAR_PERI_REG_MASK(SENS_SAR_I2C_CTRL_REG, I2C_CTRL_MASTER_TX_DATA_MASK);
SET_PERI_REG_BITS(SENS_SAR_I2C_CTRL_REG, 0xFF, data_wr[i], 19);
if (i == 0) {
/* Start RTC I2C transmission. (Needn't do it for every byte) */
SET_PERI_REG_MASK(SENS_SAR_I2C_CTRL_REG, SENS_SAR_I2C_START_FORCE);
SET_PERI_REG_MASK(SENS_SAR_I2C_CTRL_REG, SENS_SAR_I2C_START);
}
/* Poll for RTC I2C Tx Data interrupt bit to be set */
while (!REG_GET_FIELD(RTC_I2C_INT_ST_REG, RTC_I2C_TX_DATA_INT_ST)) { }
/* Clear the Tx data interrupt bit */
SET_PERI_REG_MASK(RTC_I2C_INT_CLR_REG, RTC_I2C_TX_DATA_INT_CLR);
}
/* Clear the RTC I2C transmission bits */
CLEAR_PERI_REG_MASK(SENS_SAR_I2C_CTRL_REG, SENS_SAR_I2C_START_FORCE);
CLEAR_PERI_REG_MASK(SENS_SAR_I2C_CTRL_REG, SENS_SAR_I2C_START);
}

Wyświetl plik

@ -0,0 +1,441 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "ulp_riscv_i2c.h"
#include "esp_check.h"
#include "soc/rtc_i2c_reg.h"
#include "soc/rtc_i2c_struct.h"
#include "soc/rtc_io_struct.h"
#include "soc/sens_reg.h"
#include "soc/clk_tree_defs.h"
#include "hal/i2c_ll.h"
#include "driver/rtc_io.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
static const char *RTCI2C_TAG = "ulp_riscv_i2c";
#define I2C_CTRL_SLAVE_ADDR_MASK (0xFF << 0)
#define I2C_CTRL_SLAVE_REG_ADDR_MASK (0xFF << 11)
#define I2C_CTRL_MASTER_TX_DATA_MASK (0xFF << 19)
#if CONFIG_IDF_TARGET_ESP32S3
#define ULP_I2C_CMD_RESTART 0 /*!<I2C restart command */
#define ULP_I2C_CMD_WRITE 1 /*!<I2C write command */
#define ULP_I2C_CMD_READ 2 /*!<I2C read command */
#define ULP_I2C_CMD_STOP 3 /*!<I2C stop command */
#define ULP_I2C_CMD_END 4 /*!<I2C end command */
#else
#define ULP_I2C_CMD_RESTART I2C_LL_CMD_RESTART /*!<I2C restart command */
#define ULP_I2C_CMD_WRITE I2C_LL_CMD_WRITE /*!<I2C write command */
#define ULP_I2C_CMD_READ I2C_LL_CMD_READ /*!<I2C read command */
#define ULP_I2C_CMD_STOP I2C_LL_CMD_STOP /*!<I2C stop command */
#define ULP_I2C_CMD_END I2C_LL_CMD_END /*!<I2C end command */
#endif // CONFIG_IDF_TARGET_ESP32S3
/* Use the register structure to access RTC_I2C and RTCIO module registers */
rtc_i2c_dev_t *i2c_dev = &RTC_I2C;
rtc_io_dev_t *rtc_io_dev = &RTCIO;
#define MICROSEC_TO_RTC_FAST_CLK(period) (period) * ((SOC_CLK_RC_FAST_FREQ_APPROX) / (1000000))
static esp_err_t i2c_gpio_is_cfg_valid(gpio_num_t sda_io_num, gpio_num_t scl_io_num)
{
/* Verify that the SDA and SCL GPIOs are valid RTC I2C io pins */
ESP_RETURN_ON_ERROR(!rtc_gpio_is_valid_gpio(sda_io_num), RTCI2C_TAG, "RTC I2C SDA GPIO invalid");
ESP_RETURN_ON_ERROR(!rtc_gpio_is_valid_gpio(scl_io_num), RTCI2C_TAG, "RTC I2C SCL GPIO invalid");
/* Verify that the SDA and SCL line belong to the RTC IO I2C function group */
if ((sda_io_num != GPIO_NUM_1) && (sda_io_num != GPIO_NUM_3)) {
ESP_LOGE(RTCI2C_TAG, "SDA pin can only be configured as GPIO#1 or GPIO#3");
return ESP_ERR_INVALID_ARG;
}
if ((scl_io_num != GPIO_NUM_0) && (scl_io_num != GPIO_NUM_2)) {
ESP_LOGE(RTCI2C_TAG, "SCL pin can only be configured as GPIO#0 or GPIO#2");
return ESP_ERR_INVALID_ARG;
}
return ESP_OK;
}
static esp_err_t i2c_configure_io(gpio_num_t io_num, bool pullup_en)
{
/* Initialize IO Pin */
ESP_RETURN_ON_ERROR(rtc_gpio_init(io_num), RTCI2C_TAG, "RTC GPIO Init failed for GPIO %d", io_num);
/* Set direction to input+output */
ESP_RETURN_ON_ERROR(rtc_gpio_set_direction(io_num, RTC_GPIO_MODE_INPUT_OUTPUT), RTCI2C_TAG, "RTC GPIO Set direction failed for %d", io_num);
/* Disable pulldown on the io pin */
ESP_RETURN_ON_ERROR(rtc_gpio_pulldown_dis(io_num), RTCI2C_TAG, "RTC GPIO pulldown disable failed for %d", io_num);
/* Enable pullup based on pullup_en flag */
if (pullup_en) {
ESP_RETURN_ON_ERROR(rtc_gpio_pullup_en(io_num), RTCI2C_TAG, "RTC GPIO pullup enable failed for %d", io_num);
} else {
ESP_RETURN_ON_ERROR(rtc_gpio_pullup_dis(io_num), RTCI2C_TAG, "RTC GPIO pullup disable failed for %d", io_num);
}
return ESP_OK;
}
static esp_err_t i2c_set_pin(const ulp_riscv_i2c_cfg_t *cfg)
{
gpio_num_t sda_io_num = cfg->i2c_pin_cfg.sda_io_num;
gpio_num_t scl_io_num = cfg->i2c_pin_cfg.scl_io_num;
bool sda_pullup_en = cfg->i2c_pin_cfg.sda_pullup_en;
bool scl_pullup_en = cfg->i2c_pin_cfg.scl_pullup_en;
/* Verify that the I2C GPIOs are valid */
ESP_RETURN_ON_ERROR(i2c_gpio_is_cfg_valid(sda_io_num, scl_io_num), RTCI2C_TAG, "RTC I2C GPIO config invalid");
/* Initialize SDA Pin */
ESP_RETURN_ON_ERROR(i2c_configure_io(sda_io_num, sda_pullup_en), RTCI2C_TAG, "RTC I2C SDA pin config failed");
/* Initialize SCL Pin */
ESP_RETURN_ON_ERROR(i2c_configure_io(scl_io_num, scl_pullup_en), RTCI2C_TAG, "RTC I2C SCL pin config failed");
/* Route SDA IO signal to the RTC subsystem */
rtc_io_dev->touch_pad[sda_io_num].mux_sel = 1;
/* Route SCL IO signal to the RTC subsystem */
rtc_io_dev->touch_pad[scl_io_num].mux_sel = 1;
/* Select RTC I2C function for SDA pin */
rtc_io_dev->touch_pad[sda_io_num].fun_sel = 3;
/* Select RTC I2C function for SCL pin */
rtc_io_dev->touch_pad[scl_io_num].fun_sel = 3;
/* Map the SDA and SCL signals to the RTC I2C controller */
if (sda_io_num == GPIO_NUM_1) {
rtc_io_dev->sar_i2c_io.sda_sel = 0;
} else {
rtc_io_dev->sar_i2c_io.sda_sel = 1;
}
if (scl_io_num == GPIO_NUM_0) {
rtc_io_dev->sar_i2c_io.scl_sel = 0;
} else {
rtc_io_dev->sar_i2c_io.scl_sel = 1;
}
return ESP_OK;
}
static esp_err_t i2c_set_timing(const ulp_riscv_i2c_cfg_t *cfg)
{
/* Convert all timing parameters from micro-seconds to period in RTC_FAST_CLK cycles.
* RTC_FAST_CLK = 8.5 MHz for esp32s2 and 17.5 MHz for esp32s3.
* The following calculations approximate the period for each parameter.
*/
uint32_t scl_low_period = MICROSEC_TO_RTC_FAST_CLK(cfg->i2c_timing_cfg.scl_low_period);
uint32_t scl_high_period = MICROSEC_TO_RTC_FAST_CLK(cfg->i2c_timing_cfg.scl_high_period);
uint32_t sda_duty_period = MICROSEC_TO_RTC_FAST_CLK(cfg->i2c_timing_cfg.sda_duty_period);
uint32_t scl_start_period = MICROSEC_TO_RTC_FAST_CLK(cfg->i2c_timing_cfg.scl_start_period);
uint32_t scl_stop_period = MICROSEC_TO_RTC_FAST_CLK(cfg->i2c_timing_cfg.scl_stop_period);
uint32_t i2c_trans_timeout = MICROSEC_TO_RTC_FAST_CLK(cfg->i2c_timing_cfg.i2c_trans_timeout);
uint32_t setup_time_start = (cfg->i2c_timing_cfg.scl_high_period + cfg->i2c_timing_cfg.sda_duty_period);
uint32_t hold_time_start = (cfg->i2c_timing_cfg.scl_start_period - cfg->i2c_timing_cfg.sda_duty_period);
uint32_t setup_time_data = (cfg->i2c_timing_cfg.scl_low_period - cfg->i2c_timing_cfg.sda_duty_period);
/* Verify timing constraints */
ESP_RETURN_ON_FALSE((float)cfg->i2c_timing_cfg.scl_low_period > 1.3, ESP_ERR_INVALID_ARG, RTCI2C_TAG, "SCL low period cannot be less than 1.3 micro seconds");
ESP_RETURN_ON_FALSE((float)cfg->i2c_timing_cfg.scl_high_period > 0.6, ESP_ERR_INVALID_ARG, RTCI2C_TAG, "SCL high period cannot be less than 0.6 micro seconds");
ESP_RETURN_ON_FALSE((float)setup_time_start > 0.6, ESP_ERR_INVALID_ARG, RTCI2C_TAG, "Setup time cannot be less than 0.6 micro seconds");
ESP_RETURN_ON_FALSE((float)hold_time_start > 0.6, ESP_ERR_INVALID_ARG, RTCI2C_TAG, "Data hold time cannot be less than 0.6 micro seconds");
ESP_RETURN_ON_FALSE((float)cfg->i2c_timing_cfg.scl_stop_period > 0.6, ESP_ERR_INVALID_ARG, RTCI2C_TAG, "Setup time cannot be less than 0.6 micro seconds");
ESP_RETURN_ON_FALSE((float)cfg->i2c_timing_cfg.sda_duty_period < 3.45, ESP_ERR_INVALID_ARG, RTCI2C_TAG, "Data hold time cannot be greater than 3.45 micro seconds");
ESP_RETURN_ON_FALSE((float)(setup_time_data * 1000) > 250, ESP_ERR_INVALID_ARG, RTCI2C_TAG, "Data setup time cannot be less than 250 nano seconds");
/* Verify filtering constrains
*
* I2C may have glitches on the transition edge, so the edge will be filtered in the design,
* which will also affect the value of the timing parameter register.
* Therefore, the following filtering constraints must be followed:
*/
ESP_RETURN_ON_FALSE(scl_stop_period > scl_high_period, ESP_ERR_INVALID_ARG, RTCI2C_TAG, "SCL Stop period cannot be greater than SCL high period");
ESP_RETURN_ON_FALSE(sda_duty_period < scl_low_period, ESP_ERR_INVALID_ARG, RTCI2C_TAG, "SDA duty period cannot be less than the SCL low period");
ESP_RETURN_ON_FALSE(scl_start_period > 8, ESP_ERR_INVALID_ARG, RTCI2C_TAG, "SCL start period must be greater than 8 RTC_FAST_CLK cycles");
ESP_RETURN_ON_FALSE((scl_low_period + scl_high_period - sda_duty_period) > 8, ESP_ERR_INVALID_ARG, RTCI2C_TAG, "SCL low + SCL high - SDA duty must be greater than 8 RTC_FAST_CLK cycles");
/* Verify SDA duty num constraints */
ESP_RETURN_ON_FALSE(sda_duty_period > 14, ESP_ERR_INVALID_ARG, RTCI2C_TAG, "SDA duty period must be greater than 14 RTC_FAST_CLK cycles");
/* Set the RTC I2C timing parameters */
#if CONFIG_IDF_TARGET_ESP32S2
i2c_dev->scl_low.val = scl_low_period; // SCL low period
i2c_dev->scl_high.val = scl_high_period; // SCL high period
i2c_dev->sda_duty.val = sda_duty_period; // SDA duty cycle
i2c_dev->scl_start_period.val = scl_start_period; // Wait time after START condition
i2c_dev->scl_stop_period.val = scl_stop_period; // Wait time before END condition
i2c_dev->timeout.val = i2c_trans_timeout; // I2C transaction timeout
#elif CONFIG_IDF_TARGET_ESP32S3
i2c_dev->i2c_scl_low.val = scl_low_period; // SCL low period
i2c_dev->i2c_scl_high.val = scl_high_period; // SCL high period
i2c_dev->i2c_sda_duty.val = sda_duty_period; // SDA duty cycle
i2c_dev->i2c_scl_start_period.val = scl_start_period; // Wait time after START condition
i2c_dev->i2c_scl_stop_period.val = scl_stop_period; // Wait time before END condition
i2c_dev->i2c_to.val = i2c_trans_timeout; // I2C transaction timeout
#endif // CONFIG_IDF_TARGET_ESP32S2
return ESP_OK;
}
/*
* The RTC I2C controller follows the I2C command registers to perform read/write operations.
* The cmd registers have the following format:
*
* 31 30:14 13:11 10 9 8 7:0
* |----------|----------|---------|---------|----------|------------|---------|
* | CMD_DONE | Reserved | OPCODE |ACK Value|ACK Expect|ACK Check En|Byte Num |
* |----------|----------|---------|---------|----------|------------|---------|
*/
static void ulp_riscv_i2c_format_cmd(uint32_t cmd_idx, uint8_t op_code, uint8_t ack_val,
uint8_t ack_expected, uint8_t ack_check_en, uint8_t byte_num)
{
#if CONFIG_IDF_TARGET_ESP32S2
/* Reset cmd register */
i2c_dev->command[cmd_idx].val = 0;
/* Write new command to cmd register */
i2c_dev->command[cmd_idx].done = 0; // CMD Done
i2c_dev->command[cmd_idx].op_code = op_code; // Opcode
i2c_dev->command[cmd_idx].ack_val = ack_val; // ACK bit sent by I2C controller during READ.
// Ignored during RSTART, STOP, END and WRITE cmds.
i2c_dev->command[cmd_idx].ack_exp = ack_expected; // ACK bit expected by I2C controller during WRITE.
// Ignored during RSTART, STOP, END and READ cmds.
i2c_dev->command[cmd_idx].ack_en = ack_check_en; // I2C controller verifies that the ACK bit sent by the
// slave device matches the ACK expected bit during WRITE.
// Ignored during RSTART, STOP, END and READ cmds.
i2c_dev->command[cmd_idx].byte_num = byte_num; // Byte Num
#elif CONFIG_IDF_TARGET_ESP32S3
/* Reset cmd register */
i2c_dev->i2c_cmd[cmd_idx].val = 0;
/* Write new command to cmd register */
i2c_dev->i2c_cmd[cmd_idx].i2c_command_done = 0; // CMD Done
i2c_dev->i2c_cmd[cmd_idx].i2c_op_code = op_code; // Opcode
i2c_dev->i2c_cmd[cmd_idx].i2c_ack_val = ack_val; // ACK bit sent by I2C controller during READ.
// Ignored during RSTART, STOP, END and WRITE cmds.
i2c_dev->i2c_cmd[cmd_idx].i2c_ack_exp = ack_expected; // ACK bit expected by I2C controller during WRITE.
// Ignored during RSTART, STOP, END and READ cmds.
i2c_dev->i2c_cmd[cmd_idx].i2c_ack_en = ack_check_en; // I2C controller verifies that the ACK bit sent by the
// slave device matches the ACK expected bit during WRITE.
// Ignored during RSTART, STOP, END and READ cmds.
i2c_dev->i2c_cmd[cmd_idx].i2c_byte_num = byte_num; // Byte Num
#endif // CONFIG_IDF_TARGET_ESP32S2
}
void ulp_riscv_i2c_master_set_slave_addr(uint8_t slave_addr)
{
CLEAR_PERI_REG_MASK(SENS_SAR_I2C_CTRL_REG, I2C_CTRL_SLAVE_ADDR_MASK);
SET_PERI_REG_BITS(SENS_SAR_I2C_CTRL_REG, 0xFF, slave_addr, 0);
}
void ulp_riscv_i2c_master_set_slave_reg_addr(uint8_t slave_reg_addr)
{
CLEAR_PERI_REG_MASK(SENS_SAR_I2C_CTRL_REG, I2C_CTRL_SLAVE_REG_ADDR_MASK);
SET_PERI_REG_BITS(SENS_SAR_I2C_CTRL_REG, 0xFF, slave_reg_addr, 11);
}
/*
* I2C transactions when master reads one byte of data from the slave device:
*
* |--------|--------|---------|--------|--------|--------|--------|---------|--------|--------|--------|--------|
* | Master | START | SAD + W | | SUB | | SR | SAD + R | | | NACK | STOP |
* |--------|--------|---------|--------|--------|--------|--------|---------|--------|--------|--------|--------|
* | Slave | | | ACK | | ACK | | | ACK | DATA | | |
* |--------|--------|---------|--------|--------|--------|--------|---------|--------|--------|--------|--------|
*
* I2C transactions when master reads multiple bytes of data from the slave device:
*
* |--------|--------|---------|--------|--------|--------|--------|---------|--------|--------|--------|--------|--------|--------|
* | Master | START | SAD + W | | SUB | | SR | SAD + R | | | ACK | | NACK | STOP |
* |--------|--------|---------|--------|--------|--------|--------|---------|--------|--------|--------|--------|--------|--------|
* | Slave | | | ACK | | ACK | | | ACK | DATA | | DATA | | |
* |--------|--------|---------|--------|--------|--------|--------|---------|--------|--------|--------|--------|--------|--------|
*/
void ulp_riscv_i2c_master_read_from_device(uint8_t *data_rd, size_t size)
{
uint32_t i = 0;
uint32_t cmd_idx = 0;
if (size == 0) {
// Quietly return
return;
}
/* By default, RTC I2C controller is hard wired to use CMD2 register onwards for read operations */
cmd_idx = 2;
/* Write slave addr */
ulp_riscv_i2c_format_cmd(cmd_idx++, ULP_I2C_CMD_WRITE, 0, 0, 1, 2);
/* Repeated START */
ulp_riscv_i2c_format_cmd(cmd_idx++, ULP_I2C_CMD_RESTART, 0, 0, 0, 0);
/* Write slave register addr */
ulp_riscv_i2c_format_cmd(cmd_idx++, ULP_I2C_CMD_WRITE, 0, 0, 1, 1);
if (size > 1) {
/* Read n - 1 bytes */
ulp_riscv_i2c_format_cmd(cmd_idx++, ULP_I2C_CMD_READ, 0, 0, 1, size - 1);
}
/* Read last byte + NACK */
ulp_riscv_i2c_format_cmd(cmd_idx++, ULP_I2C_CMD_READ, 1, 1, 1, 1);
/* STOP */
ulp_riscv_i2c_format_cmd(cmd_idx++, ULP_I2C_CMD_STOP, 0, 0, 0, 0);
/* Configure the RTC I2C controller in read mode */
SET_PERI_REG_BITS(SENS_SAR_I2C_CTRL_REG, 0x1, 0, 27);
/* Enable Rx data interrupt */
SET_PERI_REG_MASK(RTC_I2C_INT_ENA_REG, RTC_I2C_RX_DATA_INT_ENA);
/* Start RTC I2C transmission */
SET_PERI_REG_MASK(SENS_SAR_I2C_CTRL_REG, SENS_SAR_I2C_START_FORCE);
SET_PERI_REG_MASK(SENS_SAR_I2C_CTRL_REG, SENS_SAR_I2C_START);
for (i = 0; i < size; i++) {
/* Poll for RTC I2C Rx Data interrupt bit to be set */
while (!REG_GET_FIELD(RTC_I2C_INT_ST_REG, RTC_I2C_RX_DATA_INT_ST)) {
/* Minimal delay to avoid hogging the CPU */
vTaskDelay(1);
}
/* Read the data
*
* Unfortunately, the RTC I2C has no fifo buffer to help us with reading and storing
* multiple bytes of data. Therefore, we need to read one byte at a time and clear the
* Rx interrupt to get ready for the next byte.
*/
#if CONFIG_IDF_TARGET_ESP32S2
data_rd[i] = REG_GET_FIELD(RTC_I2C_DATA_REG, RTC_I2C_RDATA);
#elif CONFIG_IDF_TARGET_ESP32S3
data_rd[i] = REG_GET_FIELD(RTC_I2C_DATA_REG, RTC_I2C_I2C_RDATA);
#endif // CONFIG_IDF_TARGET_ESP32S2
/* Clear the Rx data interrupt bit */
SET_PERI_REG_MASK(RTC_I2C_INT_CLR_REG, RTC_I2C_RX_DATA_INT_CLR);
}
/* Clear the RTC I2C transmission bits */
CLEAR_PERI_REG_MASK(SENS_SAR_I2C_CTRL_REG, SENS_SAR_I2C_START_FORCE);
CLEAR_PERI_REG_MASK(SENS_SAR_I2C_CTRL_REG, SENS_SAR_I2C_START);
}
/*
* I2C transactions when master writes one byte of data to the slave device:
*
* |--------|--------|---------|--------|--------|--------|--------|--------|--------|
* | Master | START | SAD + W | | SUB | | DATA | | STOP |
* |--------|--------|---------|--------|--------|--------|--------|--------|--------|
* | Slave | | | ACK | | ACK | | ACK | |
* |--------|--------|---------|--------|--------|--------|--------|--------|--------|
*
* I2C transactions when master writes multiple bytes of data to the slave device:
*
* |--------|--------|---------|--------|--------|--------|--------|--------|--------|--------|--------|
* | Master | START | SAD + W | | SUB | | DATA | | DATA | | STOP |
* |--------|--------|---------|--------|--------|--------|--------|--------|--------|--------|--------|
* | Slave | | | ACK | | ACK | | ACK | | ACK | |
* |--------|--------|---------|--------|--------|--------|--------|--------|--------|--------|--------|
*/
void ulp_riscv_i2c_master_write_to_device(uint8_t *data_wr, size_t size)
{
uint32_t i = 0;
uint32_t cmd_idx = 0;
if (size == 0) {
// Quietly return
return;
}
/* By default, RTC I2C controller is hard wired to use CMD0 and CMD1 registers for write operations */
cmd_idx = 0;
/* Write slave addr + reg addr + data */
ulp_riscv_i2c_format_cmd(cmd_idx++, ULP_I2C_CMD_WRITE, 0, 0, 1, 2 + size);
/* Stop */
ulp_riscv_i2c_format_cmd(cmd_idx++, ULP_I2C_CMD_STOP, 0, 0, 0, 0);
/* Configure the RTC I2C controller in write mode */
SET_PERI_REG_BITS(SENS_SAR_I2C_CTRL_REG, 0x1, 1, 27);
/* Enable Tx data interrupt */
SET_PERI_REG_MASK(RTC_I2C_INT_ENA_REG, RTC_I2C_TX_DATA_INT_ENA);
for (i = 0; i < size; i++) {
/* Write the data to be transmitted */
CLEAR_PERI_REG_MASK(SENS_SAR_I2C_CTRL_REG, I2C_CTRL_MASTER_TX_DATA_MASK);
SET_PERI_REG_BITS(SENS_SAR_I2C_CTRL_REG, 0xFF, data_wr[i], 19);
if (i == 0) {
/* Start RTC I2C transmission. (Needn't do it for every byte) */
SET_PERI_REG_MASK(SENS_SAR_I2C_CTRL_REG, SENS_SAR_I2C_START_FORCE);
SET_PERI_REG_MASK(SENS_SAR_I2C_CTRL_REG, SENS_SAR_I2C_START);
}
/* Poll for RTC I2C Tx Data interrupt bit to be set */
while (!REG_GET_FIELD(RTC_I2C_INT_ST_REG, RTC_I2C_TX_DATA_INT_ST)) {
/* Minimal delay to avoid hogging the CPU */
vTaskDelay(1);
}
/* Clear the Tx data interrupt bit */
SET_PERI_REG_MASK(RTC_I2C_INT_CLR_REG, RTC_I2C_TX_DATA_INT_CLR);
}
/* Clear the RTC I2C transmission bits */
CLEAR_PERI_REG_MASK(SENS_SAR_I2C_CTRL_REG, SENS_SAR_I2C_START_FORCE);
CLEAR_PERI_REG_MASK(SENS_SAR_I2C_CTRL_REG, SENS_SAR_I2C_START);
}
esp_err_t ulp_riscv_i2c_master_init(const ulp_riscv_i2c_cfg_t *cfg)
{
/* Reset RTC I2C */
#if CONFIG_IDF_TARGET_ESP32S2
i2c_dev->ctrl.i2c_reset = 1;
esp_rom_delay_us(20);
i2c_dev->ctrl.i2c_reset = 0;
#elif CONFIG_IDF_TARGET_ESP32S3
SET_PERI_REG_MASK(SENS_SAR_PERI_RESET_CONF_REG, SENS_RTC_I2C_RESET);
i2c_dev->i2c_ctrl.i2c_i2c_reset = 1;
esp_rom_delay_us(20);
i2c_dev->i2c_ctrl.i2c_i2c_reset = 0;
CLEAR_PERI_REG_MASK(SENS_SAR_PERI_RESET_CONF_REG, SENS_RTC_I2C_RESET);
#endif // CONFIG_IDF_TARGET_ESP32S2
/* Verify that the input cfg param is valid */
ESP_RETURN_ON_FALSE(cfg, ESP_ERR_INVALID_ARG, RTCI2C_TAG, "RTC I2C configuration is NULL");
/* Configure RTC I2C GPIOs */
ESP_RETURN_ON_ERROR(i2c_set_pin(cfg), RTCI2C_TAG, "Failed to configure RTC I2C GPIOs");
#if CONFIG_IDF_TARGET_ESP32S2
/* Configure the RTC I2C controller in master mode */
i2c_dev->ctrl.ms_mode = 1;
/* Enable RTC I2C Clock gate */
i2c_dev->ctrl.i2c_ctrl_clk_gate_en = 1;
#elif CONFIG_IDF_TARGET_ESP32S3
/* For esp32s3, we need to enable the rtc_i2c clock gate before accessing rtc i2c registers */
SET_PERI_REG_MASK(SENS_SAR_PERI_CLK_GATE_CONF_REG, SENS_RTC_I2C_CLK_EN);
/* Configure the RTC I2C controller in master mode */
i2c_dev->i2c_ctrl.i2c_ms_mode = 1;
/* Enable RTC I2C Clock gate */
i2c_dev->i2c_ctrl.i2c_i2c_ctrl_clk_gate_en = 1;
#endif // CONFIG_IDF_TARGET_ESP32S2
/* Configure RTC I2C timing paramters */
ESP_RETURN_ON_ERROR(i2c_set_timing(cfg), RTCI2C_TAG, "Failed to configure RTC I2C timing");
return ESP_OK;
}

Wyświetl plik

@ -194,6 +194,10 @@ examples/system/ulp_riscv/gpio_interrupt:
temporary: true
reason: the other targets are not tested yet
examples/system/ulp_riscv/i2c:
enable:
- if: SOC_RISCV_COPROC_SUPPORTED == 1
examples/system/ulp_riscv/uart_print:
enable:
- if: SOC_RISCV_COPROC_SUPPORTED == 1

Wyświetl plik

@ -0,0 +1,6 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.16)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(ulp-riscv-rtc-i2c-example)

Wyświetl plik

@ -0,0 +1,76 @@
| Supported Targets | ESP32-S2 | ESP32-S3 |
| ----------------- | -------- | -------- |
# ULP RISC-V I2C Example
This example demonstrates how to use the RTC I2C peripheral from the ULP RISC-V coprocessor in deep sleep.
The ULP program is based on the BMP180 Temperature and Pressure sensor (https://cz.mouser.com/datasheet/2/783/BST-BMP180-DS000-1509579.pdf) which has an I2C interface. The main CPU initializes the RTC I2C peripheral, the BMP180 sensor and loads the ULP program. It then goes into deep sleep.
The ULP program periodically measures the temperature and pressure values from the BMP180 sensor and wakesup the main CPU when the values are above a certain thershold.
### Hardware Required
* A development board with a SOC which has a RISC-V ULP coprocessor (e.g., ESP32-S2 Saola)
* A BMP180 sensor module
* A USB cable for power supply and programming
## Example output
Below is the output from this example.
```
Not a ULP-RISC V wakeup (cause = 0)
Initializing RTC I2C ...
RTC_I2C_STATUS_REG = 0x00000000
Reading calibration data from BMP180 ...
ac1 = 7819
ac2 = -1152
ac3 = -14317
ac4 = 34252
ac5 = 25122
ac6 = 14289
b1 = 6515
b2 = 44
mb = -32768
mc = -11786
md = 2746
Reading initial uncompensated temperature and pressure data ...
Uncompensated Temperature = 22865
Uncompensated Pressure = 41768
Real Temperature = 24.900000 deg celcius
Real Pressure = 990.640000 hPa
Entering in deep sleep
ESP-ROM:esp32s2-rc4-20191025
Build:Oct 25 2019
rst:0x5 (DSLEEP),boot:0x9 (SPI_FAST_FLASH_BOOT)
SPIWP:0xee
mode:DIO, clock div:1
load:0x3ffe6108,len:0x1298
load:0x4004c000,len:0x92c
load:0x40050000,len:0x2f04
entry 0x4004c154
W (76) spi_flash: Detected size(4096k) larger than the size in the binary image header(2048k). Using the size in the binary image header.
ULP RISC-V woke up the main CPU
Uncompensated Temperature = 22865
Uncompensated Pressure = 41765
Reading calibration data from BMP180 ...
ac1 = 7819
ac2 = -1152
ac3 = -14317
ac4 = 34252
ac5 = 25122
ac6 = 14289
b1 = 6515
b2 = 44
mb = -32768
mc = -11786
md = 2746
New Real Temperature = 24.900000 deg celcius
New Real Pressure = 990.550000 hPa
Entering in deep sleep
```

Wyświetl plik

@ -0,0 +1,25 @@
idf_component_register(SRCS "ulp_riscv_rtc_i2c_example_main.c"
INCLUDE_DIRS ""
REQUIRES soc ulp)
target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format")
#
# ULP support additions to component CMakeLists.txt.
#
# 1. The ULP app name must be unique (if multiple components use ULP).
set(ulp_app_name ulp_${COMPONENT_NAME})
#
# 2. Specify all C and Assembly source files.
# Files should be placed into a separate directory (in this case, ulp/),
# which should not be added to COMPONENT_SRCS.
set(ulp_riscv_sources "ulp/main.c")
#
# 3. List all the component source files which include automatically
# generated ULP export file, ${ulp_app_name}.h:
set(ulp_exp_dep_srcs "ulp_riscv_rtc_i2c_example_main.c")
#
# 4. Call function to build ULP binary and embed in project using the argument
# values above.
ulp_embed_binary(${ulp_app_name} "${ulp_riscv_sources}" "${ulp_exp_dep_srcs}")

Wyświetl plik

@ -0,0 +1,110 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#pragma once
/***************************************************
* BMP180 Register Addresses
***************************************************/
#define BMP180_SENSOR_I2C_ADDR 0x77
#define BMP180_SENSOR_REG_ADDR_WHO_AM_I 0xD0
#define BMP180_SENSOR_REG_ADDR_SOFT_RESET 0xE0
#define BMP180_SENSOR_REG_ADDR_AC1_MSB 0xAA
#define BMP180_SENSOR_REG_ADDR_AC1_LSB 0xAB
#define BMP180_SENSOR_REG_ADDR_AC2_MSB 0xAC
#define BMP180_SENSOR_REG_ADDR_AC2_LSB 0xAD
#define BMP180_SENSOR_REG_ADDR_AC3_MSB 0xAE
#define BMP180_SENSOR_REG_ADDR_AC3_LSB 0xAF
#define BMP180_SENSOR_REG_ADDR_AC4_MSB 0xB0
#define BMP180_SENSOR_REG_ADDR_AC4_LSB 0xB1
#define BMP180_SENSOR_REG_ADDR_AC5_MSB 0xB2
#define BMP180_SENSOR_REG_ADDR_AC5_LSB 0xB3
#define BMP180_SENSOR_REG_ADDR_AC6_MSB 0xB4
#define BMP180_SENSOR_REG_ADDR_AC6_LSB 0xB5
#define BMP180_SENSOR_REG_ADDR_B1_MSB 0xB6
#define BMP180_SENSOR_REG_ADDR_B1_LSB 0xB7
#define BMP180_SENSOR_REG_ADDR_B2_MSB 0xB8
#define BMP180_SENSOR_REG_ADDR_B2_LSB 0xB9
#define BMP180_SENSOR_REG_ADDR_MB_MSB 0xBA
#define BMP180_SENSOR_REG_ADDR_MB_LSB 0xBB
#define BMP180_SENSOR_REG_ADDR_MC_MSB 0xBC
#define BMP180_SENSOR_REG_ADDR_MC_LSB 0xBD
#define BMP180_SENSOR_REG_ADDR_MD_MSB 0xBE
#define BMP180_SENSOR_REG_ADDR_MD_LSB 0xBF
#define BMP180_SENSOR_REG_ADDR_CTRL_REG 0xF4
#define BMP180_SENSOR_REG_ADDR_SENSOR_DATA_MSB 0xF6
#define BMP180_SENSOR_REG_ADDR_SENSOR_DATA_LSB 0xF7
#define BMP180_SENSOR_REG_ADDR_SENSOR_DATA_XLSB 0xF8
/***************************************************
* BMP180 Control Commands
***************************************************/
#define BMP180_SENSOR_CMD_READ_TEMPERATURE 0x2E
#define BMP180_SENSOR_CMD_READ_PRESSURE_OSS_0 0x34
#define BMP180_SENSOR_CMD_READ_PRESSURE_OSS_1 0x74
#define BMP180_SENSOR_CMD_READ_PRESSURE_OSS_2 0xB4
#define BMP180_SENSOR_CMD_READ_PRESSURE_OSS_3 0xF4
#define BMP180_SENSOR_CMD_SOFT_RESET 0xB6
/***************************************************
* BMP180 Chip ID
***************************************************/
#define BMP180_SENSOR_CHIP_ID 0x55
/***************************************************
* BMP180 Calibration Data
***************************************************/
typedef struct {
int16_t ac1;
int16_t ac2;
int16_t ac3;
uint16_t ac4;
uint16_t ac5;
uint16_t ac6;
int16_t b1;
int16_t b2;
int16_t mb;
int16_t mc;
int16_t md;
} bmp180_cal_data_t;
bmp180_cal_data_t bmp180_cal_data;
/***************************************************
* BMP180 Oversampling setting to measure pressure
***************************************************/
typedef enum {
OSS_0 = 0, // Ultra low power
OSS_1 = 1, // Standard
OSS_2 = 2, // High resolution
OSS_3 = 3 // Ultra high resolution
} oss_mode_t;
/***************************************************
* BMP180 Interaction APIs
***************************************************/
static void bmp180_read_cal_data(void); // Read cal data
static void bmp180_read_ut_data(int16_t *ut_data); // Read uncompensated temperature
static void bmp180_read_up_data(int32_t *up_data, oss_mode_t oss_mode); // Read uncompensated pressure
/************************************************
* BMP180 Utility APIs
************************************************/
static void bmp180_read16(uint16_t *data_out, uint32_t reg_msb, uint32_t reg_lsb);
static int32_t bmp180_calculate_real_temp(int32_t ut_data);
static int32_t bmp180_calculate_real_pressure(int32_t up_data, int32_t ut_data, oss_mode_t oss_mode);
/************************************************
* Pressure measurement mode
************************************************/
#define EXAMPLE_OSS_MODE OSS_0
/************************************************
* Temperature and Pressure thresholds (uncompensated) to wake up Main CPU
* The threshold values have been selected for demp purposes and may not
* represent real world use case.
************************************************/
#define EXAMPLE_UT_THRESHOLD 20000
#define EXAMPLE_UP_THRESHOLD 40000

Wyświetl plik

@ -0,0 +1,126 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
/* ULP RISC-V RTC I2C example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
This code runs on ULP RISC-V coprocessor
*/
#include <stdint.h>
#include "ulp_riscv.h"
#include "ulp_riscv_utils.h"
#include "ulp_riscv_i2c_ulp_core.h"
#include "../bmp180_defs.h"
/************************************************
* Shared data between main CPU and ULP
************************************************/
int16_t ut_data = 0;
int32_t up_data = 0;
int32_t ut_threshold = EXAMPLE_UT_THRESHOLD;
int32_t up_threshold = EXAMPLE_UP_THRESHOLD;
oss_mode_t oss_mode = EXAMPLE_OSS_MODE;
int main (void)
{
/* Read uncompensated temperature */
bmp180_read_ut_data(&ut_data);
/* Read uncompensated pressure */
bmp180_read_up_data(&up_data, oss_mode);
/* Wakeup the main CPU if either the uncompensated temperature or uncompensated pressure values
* are more than their respective threshold values.
*/
if ((ut_data > ut_threshold) || (up_data > up_threshold)) {
ulp_riscv_wakeup_main_processor();
}
return 0;
}
static void bmp180_read16(uint16_t *data_out, uint32_t reg_msb, uint32_t reg_lsb)
{
uint8_t data_rd = 0;
*data_out = 0;
ulp_riscv_i2c_master_set_slave_reg_addr(reg_msb);
ulp_riscv_i2c_master_read_from_device(&data_rd, 1);
*data_out |= (uint16_t)(data_rd << 8);
ulp_riscv_i2c_master_set_slave_reg_addr(reg_lsb);
data_rd = 0;
ulp_riscv_i2c_master_read_from_device(&data_rd, 1);
*data_out |= (uint16_t)(data_rd);
}
static void bmp180_read_ut_data(int16_t *ut_data)
{
/* Set slave register address to the control register */
ulp_riscv_i2c_master_set_slave_reg_addr(BMP180_SENSOR_REG_ADDR_CTRL_REG);
/* Setup control register to read temperature */
uint8_t cmd = BMP180_SENSOR_CMD_READ_TEMPERATURE;
ulp_riscv_i2c_master_write_to_device(&cmd, 1);
/* Wait at least 4.5 milliseconds for the sensor to complete the reading */
ulp_riscv_delay_cycles(5 * ULP_RISCV_CYCLES_PER_US * 1000);
/* Read uncompensated temperature data */
bmp180_read16((uint16_t *)ut_data, BMP180_SENSOR_REG_ADDR_SENSOR_DATA_MSB, BMP180_SENSOR_REG_ADDR_SENSOR_DATA_LSB);
}
static void bmp180_read_up_data(int32_t *up_data, oss_mode_t oss_mode)
{
uint16_t press_high;
uint8_t press_low;
/* Set slave register address to the control register */
ulp_riscv_i2c_master_set_slave_reg_addr(BMP180_SENSOR_REG_ADDR_CTRL_REG);
/* Setup control register to read pressure */
uint8_t cmd = 0;
uint8_t wait = 0;
switch(oss_mode)
{
case OSS_0:
cmd = BMP180_SENSOR_CMD_READ_PRESSURE_OSS_0;
wait = 5; // Wait atleast 4.5 msec
break;
case OSS_1:
cmd = BMP180_SENSOR_CMD_READ_PRESSURE_OSS_1;
wait = 8; // Wait atleast 7.5 msec
break;
case OSS_2:
cmd = BMP180_SENSOR_CMD_READ_PRESSURE_OSS_2;
wait = 14; // Wait atleast 13.5 msec
break;
case OSS_3:
cmd = BMP180_SENSOR_CMD_READ_PRESSURE_OSS_3;
wait = 26; // Wait atleast 25.5 msec
break;
}
ulp_riscv_i2c_master_write_to_device(&cmd, 1);
/* Wait for the required amount of time for the sensor to complete the reading */
ulp_riscv_delay_cycles(wait * ULP_RISCV_CYCLES_PER_US * 1000);
/* Read uncompensated temperature data */
/* Read MSB + LSB */
bmp180_read16(&press_high, BMP180_SENSOR_REG_ADDR_SENSOR_DATA_MSB, BMP180_SENSOR_REG_ADDR_SENSOR_DATA_LSB);
/* Read XLSB */
ulp_riscv_i2c_master_set_slave_reg_addr(BMP180_SENSOR_REG_ADDR_SENSOR_DATA_XLSB);
ulp_riscv_i2c_master_read_from_device(&press_low, 1);
*up_data = (((uint32_t)press_high << 8) + (uint32_t)press_low) >> (8 - oss_mode);
}

Wyświetl plik

@ -0,0 +1,351 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
/* ULP RISC-V RTC I2C example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include <math.h>
#include "esp_sleep.h"
#include "ulp_riscv.h"
#include "ulp_riscv_i2c.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "ulp_main.h"
#include "bmp180_defs.h"
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");
/************************************************
* ULP utility APIs
************************************************/
static void init_ulp_program(void);
/************************************************
* RTC I2C utility APIs
************************************************/
static void init_i2c(void);
void app_main(void)
{
uint8_t data_rd = 0;
int16_t ut_data = 0;
int32_t up_data = 0;
int32_t temperature = 0;
int32_t pressure = 0;
oss_mode_t oss_mode;
esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause();
/* Not a wakeup from ULP
* Initialize RTC I2C
* Setup BMP180 sensor
* Store current temperature and pressure values
* Load the ULP firmware
* Go to deep sleep
*/
if (cause != ESP_SLEEP_WAKEUP_ULP) {
printf("Not a ULP-RISC V wakeup (cause = %d)\n", cause);
/* Initialize RTC I2C */
init_i2c();
/* Configure I2C slave address */
ulp_riscv_i2c_master_set_slave_addr(BMP180_SENSOR_I2C_ADDR);
/* Reset the BMP180 sensor*/
ulp_riscv_i2c_master_set_slave_reg_addr(BMP180_SENSOR_REG_ADDR_SOFT_RESET);
uint8_t data_wr = BMP180_SENSOR_CMD_SOFT_RESET;
ulp_riscv_i2c_master_write_to_device(&data_wr, 1);
/* Confirm that the sensor is alive
* The BMP180 returns the chip id 0x55 on quering reg addr 0xD0
*/
ulp_riscv_i2c_master_set_slave_reg_addr(BMP180_SENSOR_REG_ADDR_WHO_AM_I);
ulp_riscv_i2c_master_read_from_device(&data_rd, 1);
if (data_rd != BMP180_SENSOR_CHIP_ID) {
printf("ERROR: Cannot communicate with I2C sensor\n");
abort();
}
/* Read the calibration data */
printf("Reading calibration data from BMP180 ...\n");
bmp180_read_cal_data();
printf("\n");
/* Read uncompensated temperature and pressure */
printf("Reading initial uncompensated temperature and pressure data ...\n");
ut_data = 0;
up_data = 0;
oss_mode = EXAMPLE_OSS_MODE;
bmp180_read_ut_data(&ut_data);
bmp180_read_up_data(&up_data, oss_mode);
printf("Uncompensated Temperature = %d\n", ut_data);
printf("Uncompensated Pressure = %d\n", up_data);
printf("\n");
/* Calculate real temperature value */
temperature = bmp180_calculate_real_temp((int32_t)ut_data);
printf("Real Temperature = %f deg celcius\n", (float)(temperature/10.0));
/* Calculate real pressure value */
pressure = bmp180_calculate_real_pressure(up_data, (int32_t)ut_data, oss_mode);
printf("Real Pressure = %f hPa\n", pressure / 100.0);
printf("\n");
/* Load ULP firmware
*
* The ULP is responsible of monitoring the temperature and pressure values
* periodically. It will wakeup the main CPU if the temperature and pressure
* values are above a certain threshold.
*/
init_ulp_program();
}
/* ULP RISC-V read and detected a temperature or pressure above the limit */
if (cause == ESP_SLEEP_WAKEUP_ULP) {
printf("ULP RISC-V woke up the main CPU\n");
/* Pause ULP while we are using the RTC I2C from the main CPU */
ulp_timer_stop();
ulp_riscv_halt();
printf("Uncompensated Temperature = %d\n", ulp_ut_data);
printf("Uncompensated Pressure = %d\n", ulp_up_data);
/* Read the calibration data again */
printf("Reading calibration data from BMP180 ...\n");
bmp180_read_cal_data();
printf("\n");
/* Calculate real temperature and pressure again */
temperature = 0;
temperature = bmp180_calculate_real_temp((int32_t)ulp_ut_data);
printf("New Real Temperature = %f deg celcius\n", (float)(temperature/10.0));
/* Calculate real pressure value */
pressure = 0;
pressure = bmp180_calculate_real_pressure(ulp_up_data, (int32_t)ulp_ut_data, oss_mode);
printf("New Real Pressure = %f hPa\n", pressure / 100.0);
/* Resume ULP and go to deep sleep again */
ulp_timer_resume();
}
/* Add a delay for everything to the printed before heading in to light sleep */
vTaskDelay(100);
/* Go back to sleep, only the ULP RISC-V will run */
printf("Entering deep sleep\n\n");
/* RTC peripheral power domain needs to be kept on to keep RTC I2C related configs during sleep */
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
ESP_ERROR_CHECK(esp_sleep_enable_ulp_wakeup());
esp_deep_sleep_start();
}
static void init_i2c(void)
{
/* Configure RTC I2C */
printf("Initializing RTC I2C ...\n");
ulp_riscv_i2c_cfg_t i2c_cfg = ULP_RISCV_I2C_DEFAULT_CONFIG();
ulp_riscv_i2c_master_init(&i2c_cfg);
}
static void bmp180_read16(uint16_t *data_out, uint32_t reg_msb, uint32_t reg_lsb)
{
uint8_t data_rd = 0;
*data_out = 0;
ulp_riscv_i2c_master_set_slave_reg_addr(reg_msb);
ulp_riscv_i2c_master_read_from_device(&data_rd, 1);
*data_out |= (uint16_t)(data_rd << 8);
ulp_riscv_i2c_master_set_slave_reg_addr(reg_lsb);
data_rd = 0;
ulp_riscv_i2c_master_read_from_device(&data_rd, 1);
*data_out |= (uint16_t)(data_rd);
}
static void bmp180_read_cal_data(void)
{
/* AC1 */
bmp180_read16((uint16_t *)&bmp180_cal_data.ac1, BMP180_SENSOR_REG_ADDR_AC1_MSB, BMP180_SENSOR_REG_ADDR_AC1_LSB);
printf("ac1 = %d\n", bmp180_cal_data.ac1);
/* AC2 */
bmp180_read16((uint16_t *)&bmp180_cal_data.ac2, BMP180_SENSOR_REG_ADDR_AC2_MSB, BMP180_SENSOR_REG_ADDR_AC2_LSB);
printf("ac2 = %d\n", bmp180_cal_data.ac2);
/* AC3 */
bmp180_read16((uint16_t *)&bmp180_cal_data.ac3, BMP180_SENSOR_REG_ADDR_AC3_MSB, BMP180_SENSOR_REG_ADDR_AC3_LSB);
printf("ac3 = %d\n", bmp180_cal_data.ac3);
/* AC4 */
bmp180_read16(&bmp180_cal_data.ac4, BMP180_SENSOR_REG_ADDR_AC4_MSB, BMP180_SENSOR_REG_ADDR_AC4_LSB);
printf("ac4 = %u\n", bmp180_cal_data.ac4);
/* AC5 */
bmp180_read16(&bmp180_cal_data.ac5, BMP180_SENSOR_REG_ADDR_AC5_MSB, BMP180_SENSOR_REG_ADDR_AC5_LSB);
printf("ac5 = %u\n", bmp180_cal_data.ac5);
/* AC6 */
bmp180_read16(&bmp180_cal_data.ac6, BMP180_SENSOR_REG_ADDR_AC6_MSB, BMP180_SENSOR_REG_ADDR_AC6_LSB);
printf("ac6 = %u\n", bmp180_cal_data.ac6);
/* B1 */
bmp180_read16((uint16_t *)&bmp180_cal_data.b1, BMP180_SENSOR_REG_ADDR_B1_MSB, BMP180_SENSOR_REG_ADDR_B1_LSB);
printf("b1 = %d\n", bmp180_cal_data.b1);
/* B2 */
bmp180_read16((uint16_t *)&bmp180_cal_data.b2, BMP180_SENSOR_REG_ADDR_B2_MSB, BMP180_SENSOR_REG_ADDR_B2_LSB);
printf("b2 = %d\n", bmp180_cal_data.b2);
/* MB */
bmp180_read16((uint16_t *)&bmp180_cal_data.mb, BMP180_SENSOR_REG_ADDR_MB_MSB, BMP180_SENSOR_REG_ADDR_MB_LSB);
printf("mb = %d\n", bmp180_cal_data.mb);
/* MC */
bmp180_read16((uint16_t *)&bmp180_cal_data.mc, BMP180_SENSOR_REG_ADDR_MC_MSB, BMP180_SENSOR_REG_ADDR_MC_LSB);
printf("mc = %d\n", bmp180_cal_data.mc);
/* MD */
bmp180_read16((uint16_t *)&bmp180_cal_data.md, BMP180_SENSOR_REG_ADDR_MD_MSB, BMP180_SENSOR_REG_ADDR_MD_LSB);
printf("md = %d\n", bmp180_cal_data.md);
}
static void bmp180_read_ut_data(int16_t *ut_data)
{
/* Set slave register address to the control register */
ulp_riscv_i2c_master_set_slave_reg_addr(BMP180_SENSOR_REG_ADDR_CTRL_REG);
/* Setup control register to read temperature */
uint8_t cmd = BMP180_SENSOR_CMD_READ_TEMPERATURE;
ulp_riscv_i2c_master_write_to_device(&cmd, 1);
/* Wait at least 4.5 milliseconds for the sensor to complete the reading */
vTaskDelay(pdMS_TO_TICKS(5));
/* Read uncompensated temperature data */
bmp180_read16((uint16_t *)ut_data, BMP180_SENSOR_REG_ADDR_SENSOR_DATA_MSB, BMP180_SENSOR_REG_ADDR_SENSOR_DATA_LSB);
}
static int32_t computeb5(int32_t ut_data)
{
int32_t x1 = (ut_data - (int32_t)bmp180_cal_data.ac6) * ((int32_t)bmp180_cal_data.ac5) >> 15;
int32_t x2 = ((int32_t)bmp180_cal_data.mc << 11) / (x1 + (int32_t)bmp180_cal_data.md);
return x1 + x2;
}
static int32_t bmp180_calculate_real_temp(int32_t ut_data)
{
int32_t b5 = computeb5(ut_data);
int32_t t = (b5 + 8) >> 4;
return t;
}
static void bmp180_read_up_data(int32_t *up_data, oss_mode_t oss_mode)
{
uint16_t press_high;
uint8_t press_low;
/* Set slave register address to the control register */
ulp_riscv_i2c_master_set_slave_reg_addr(BMP180_SENSOR_REG_ADDR_CTRL_REG);
/* Setup control register to read pressure */
uint8_t cmd = 0;
uint8_t wait = 0;
switch(oss_mode)
{
case OSS_0:
cmd = BMP180_SENSOR_CMD_READ_PRESSURE_OSS_0;
wait = 5; // Wait atleast 4.5 msec
break;
case OSS_1:
cmd = BMP180_SENSOR_CMD_READ_PRESSURE_OSS_1;
wait = 8; // Wait atleast 7.5 msec
break;
case OSS_2:
cmd = BMP180_SENSOR_CMD_READ_PRESSURE_OSS_2;
wait = 14; // Wait atleast 13.5 msec
break;
case OSS_3:
cmd = BMP180_SENSOR_CMD_READ_PRESSURE_OSS_3;
wait = 26; // Wait atleast 25.5 msec
break;
}
ulp_riscv_i2c_master_write_to_device(&cmd, 1);
/* Wait for the required amount of time for the sensor to complete the reading */
vTaskDelay(pdMS_TO_TICKS(wait));
/* Read uncompensated temperature data */
/* Read MSB + LSB */
bmp180_read16(&press_high, BMP180_SENSOR_REG_ADDR_SENSOR_DATA_MSB, BMP180_SENSOR_REG_ADDR_SENSOR_DATA_LSB);
/* Read XLSB */
ulp_riscv_i2c_master_set_slave_reg_addr(BMP180_SENSOR_REG_ADDR_SENSOR_DATA_XLSB);
ulp_riscv_i2c_master_read_from_device(&press_low, 1);
*up_data = (((uint32_t)press_high << 8) + (uint32_t)press_low) >> (8 - oss_mode);
}
static int32_t bmp180_calculate_real_pressure(int32_t up_data, int32_t ut_data, oss_mode_t oss_mode)
{
int32_t p, x1, x2, x3, b3, b5, b6;
uint32_t b4, b7;
b5 = computeb5(ut_data);
b6 = b5 - 4000;
x1 = (bmp180_cal_data.b2 * ((b6 * b6) >> 12)) >> 11;
x2 = (bmp180_cal_data.ac2 * b6) >> 11;
x3 = x1 + x2;
b3 = (((((int32_t) bmp180_cal_data.ac1) * 4 + x3) << oss_mode) + 2) >> 2;
x1 = (bmp180_cal_data.ac3 * b6) >> 13;
x2 = (bmp180_cal_data.b1 * ((b6 * b6) >> 12)) >> 16;
x3 = ((x1 + x2) + 2) >> 2;
b4 = (bmp180_cal_data.ac4 * (uint32_t) (x3 + 32768)) >> 15;
b7 = ((uint32_t) (up_data - b3) * (50000 >> oss_mode));
if (b7 < 0x80000000) {
p = (b7 << 1) / b4;
} else {
p = (b7 / b4) << 1;
}
x1 = (p >> 8) * (p >> 8);
x1 = (x1 * 3038) >> 16;
x2 = (-7357 * p) >> 16;
p = p + ((x1 + x2 + 3791) >> 4);
return p;
}
static void init_ulp_program(void)
{
esp_err_t err = ulp_riscv_load_binary(ulp_main_bin_start, (ulp_main_bin_end - ulp_main_bin_start));
ESP_ERROR_CHECK(err);
/* The first argument is the period index, which is not used by the ULP-RISC-V timer
* The second argument is the period in microseconds, which gives a wakeup time period of: 20ms
*/
ulp_set_wakeup_period(0, 20000);
/* Start the program */
err = ulp_riscv_run();
ESP_ERROR_CHECK(err);
}

Wyświetl plik

@ -0,0 +1,9 @@
# Enable ULP
CONFIG_ULP_COPROC_ENABLED=y
CONFIG_ULP_COPROC_RISCV=y
CONFIG_ULP_COPROC_RESERVE_MEM=4096
# Set log level to Warning to produce clean output
CONFIG_BOOTLOADER_LOG_LEVEL_WARN=y
CONFIG_BOOTLOADER_LOG_LEVEL=2
CONFIG_LOG_DEFAULT_LEVEL_WARN=y
CONFIG_LOG_DEFAULT_LEVEL=2